LCOV - code coverage report
Current view: top level - lib\ui\chatwoot_chat_page.dart - chatwoot_chat_page.dart (source / functions) Hit Total Coverage
Test: lcov.info Lines: 0 148 0.0 %
Date: Thu Jul 15 17:16:16 2021 Functions: 0 0 -

          Line data    Source code
       1             : import 'package:chatwoot_client_sdk/chatwoot_callbacks.dart';
       2             : import 'package:chatwoot_client_sdk/chatwoot_client.dart';
       3             : import 'package:chatwoot_client_sdk/data/local/entity/chatwoot_message.dart';
       4             : import 'package:chatwoot_client_sdk/data/local/entity/chatwoot_user.dart';
       5             : import 'package:chatwoot_client_sdk/data/remote/chatwoot_client_exception.dart';
       6             : import 'package:chatwoot_client_sdk/ui/chatwoot_chat_theme.dart';
       7             : import 'package:flutter/material.dart';
       8             : import 'package:flutter/widgets.dart';
       9             : import 'package:flutter_chat_types/flutter_chat_types.dart' as types;
      10             : import 'package:flutter_chat_ui/flutter_chat_ui.dart';
      11             : import 'package:intl/intl.dart';
      12             : import 'package:uuid/uuid.dart';
      13             : 
      14             : ///Chatwoot chat widget
      15             : /// {@category FlutterClientSdk}
      16             : class ChatwootChat extends StatefulWidget {
      17             :   /// Specifies a custom app bar for chatwoot page widget
      18             :   final PreferredSizeWidget? appBar;
      19             : 
      20             :   ///Installation url for chatwoot
      21             :   final String baseUrl;
      22             : 
      23             :   ///Identifier for target chatwoot inbox.
      24             :   ///
      25             :   /// For more details see https://www.chatwoot.com/docs/product/channels/api/client-apis
      26             :   final String inboxIdentifier;
      27             : 
      28             :   /// Enables persistence of chatwoot client instance's contact, conversation and messages to disk
      29             :   /// for convenience.
      30             :   ///
      31             :   /// Setting [enablePersistence] to false holds chatwoot client instance's data in memory and is cleared as
      32             :   /// soon as chatwoot client instance is disposed
      33             :   final bool enablePersistence;
      34             : 
      35             :   /// Custom user details to be attached to chatwoot contact
      36             :   final ChatwootUser? user;
      37             : 
      38             :   /// See [ChatList.onEndReached]
      39             :   final Future<void> Function()? onEndReached;
      40             : 
      41             :   /// See [ChatList.onEndReachedThreshold]
      42             :   final double? onEndReachedThreshold;
      43             : 
      44             :   /// See [Message.onMessageLongPress]
      45             :   final void Function(types.Message)? onMessageLongPress;
      46             : 
      47             :   /// See [Message.onMessageTap]
      48             :   final void Function(types.Message)? onMessageTap;
      49             : 
      50             :   /// See [Input.onSendPressed]
      51             :   final void Function(types.PartialText)? onSendPressed;
      52             : 
      53             :   /// See [Input.onTextChanged]
      54             :   final void Function(String)? onTextChanged;
      55             : 
      56             :   /// See [Message.showUserAvatars]
      57             :   final bool showUserAvatars;
      58             : 
      59             :   /// Show user names for received messages. Useful for a group chat. Will be
      60             :   /// shown only on text messages.
      61             :   final bool showUserNames;
      62             : 
      63             :   final ChatwootChatTheme? theme;
      64             : 
      65             :   /// See [ChatL10n]
      66             :   final ChatL10n? l10n;
      67             : 
      68             :   /// See [Chat.timeFormat]
      69             :   final DateFormat? timeFormat;
      70             : 
      71             :   /// See [Chat.dateFormat]
      72             :   final DateFormat? dateFormat;
      73             : 
      74             :   ///See [ChatwootCallbacks.onWelcome]
      75             :   final void Function()? onWelcome;
      76             : 
      77             :   ///See [ChatwootCallbacks.onPing]
      78             :   final void Function()? onPing;
      79             : 
      80             :   ///See [ChatwootCallbacks.onConfirmedSubscription]
      81             :   final void Function()? onConfirmedSubscription;
      82             : 
      83             :   ///See [ChatwootCallbacks.onConversationStartedTyping]
      84             :   final void Function()? onConversationStartedTyping;
      85             : 
      86             :   ///See [ChatwootCallbacks.onConversationIsOnline]
      87             :   final void Function()? onConversationIsOnline;
      88             : 
      89             :   ///See [ChatwootCallbacks.onConversationIsOffline]
      90             :   final void Function()? onConversationIsOffline;
      91             : 
      92             :   ///See [ChatwootCallbacks.onConversationStoppedTyping]
      93             :   final void Function()? onConversationStoppedTyping;
      94             : 
      95             :   ///See [ChatwootCallbacks.onMessageReceived]
      96             :   final void Function(ChatwootMessage)? onMessageReceived;
      97             : 
      98             :   ///See [ChatwootCallbacks.onMessageSent]
      99             :   final void Function(ChatwootMessage)? onMessageSent;
     100             : 
     101             :   ///See [ChatwootCallbacks.onMessageDelivered]
     102             :   final void Function(ChatwootMessage)? onMessageDelivered;
     103             : 
     104             :   ///See [ChatwootCallbacks.onPersistedMessagesRetrieved]
     105             :   final void Function(List<ChatwootMessage>)? onPersistedMessagesRetrieved;
     106             : 
     107             :   ///See [ChatwootCallbacks.onMessagesRetrieved]
     108             :   final void Function(List<ChatwootMessage>)? onMessagesRetrieved;
     109             : 
     110             :   ///See [ChatwootCallbacks.onError]
     111             :   final void Function(ChatwootClientException)? onError;
     112             : 
     113           0 :   const ChatwootChat({
     114             :     Key? key,
     115             :     required this.baseUrl,
     116             :     required this.inboxIdentifier,
     117             :     this.enablePersistence = true,
     118             :     this.user,
     119             :     this.appBar,
     120             :     this.onEndReached,
     121             :     this.onEndReachedThreshold,
     122             :     this.onMessageLongPress,
     123             :     this.onMessageTap,
     124             :     this.onSendPressed,
     125             :     this.onTextChanged,
     126             :     this.showUserAvatars = true,
     127             :     this.showUserNames = true,
     128             :     this.theme,
     129             :     this.l10n,
     130             :     this.timeFormat,
     131             :     this.dateFormat,
     132             :     this.onWelcome,
     133             :     this.onPing,
     134             :     this.onConfirmedSubscription,
     135             :     this.onMessageReceived,
     136             :     this.onMessageSent,
     137             :     this.onMessageDelivered,
     138             :     this.onPersistedMessagesRetrieved,
     139             :     this.onMessagesRetrieved,
     140             :     this.onConversationStartedTyping,
     141             :     this.onConversationStoppedTyping,
     142             :     this.onConversationIsOnline,
     143             :     this.onConversationIsOffline,
     144             :     this.onError,
     145           0 :   }) : super(key: key);
     146             : 
     147           0 :   @override
     148           0 :   _ChatwootChatState createState() => _ChatwootChatState();
     149             : }
     150             : 
     151             : class _ChatwootChatState extends State<ChatwootChat> {
     152             :   ///
     153             :   List<types.Message> _messages = [];
     154             : 
     155             :   late String status;
     156             : 
     157             :   final idGen = Uuid();
     158             :   late final _user;
     159             :   late final ChatwootClient chatwootClient;
     160             : 
     161             :   late final chatwootCallbacks;
     162             : 
     163           0 :   @override
     164             :   void initState() {
     165           0 :     super.initState();
     166             : 
     167           0 :     if (widget.user == null) {
     168           0 :       _user = types.User(id: idGen.v4());
     169             :     } else {
     170           0 :       _user = types.User(
     171           0 :         id: widget.user?.identifier ?? idGen.v4(),
     172           0 :         firstName: widget.user?.name,
     173           0 :         imageUrl: widget.user?.avatarUrl,
     174             :       );
     175             :     }
     176             : 
     177           0 :     chatwootCallbacks = ChatwootCallbacks(
     178           0 :       onWelcome: () {
     179           0 :         widget.onWelcome?.call();
     180             :       },
     181           0 :       onPing: () {
     182           0 :         widget.onPing?.call();
     183             :       },
     184           0 :       onConfirmedSubscription: () {
     185           0 :         widget.onConfirmedSubscription?.call();
     186             :       },
     187           0 :       onConversationStartedTyping: () {
     188           0 :         widget.onConversationStoppedTyping?.call();
     189             :       },
     190           0 :       onConversationStoppedTyping: () {
     191           0 :         widget.onConversationStartedTyping?.call();
     192             :       },
     193           0 :       onPersistedMessagesRetrieved: (persistedMessages) {
     194           0 :         if (widget.enablePersistence) {
     195           0 :           setState(() {
     196           0 :             _messages = persistedMessages
     197           0 :                 .map((message) => _chatwootMessageToTextMessage(message))
     198           0 :                 .toList();
     199             :           });
     200             :         }
     201           0 :         widget.onPersistedMessagesRetrieved?.call(persistedMessages);
     202             :       },
     203           0 :       onMessagesRetrieved: (messages) {
     204           0 :         if (messages.isEmpty) {
     205             :           return;
     206             :         }
     207           0 :         setState(() {
     208             :           final chatMessages = messages
     209           0 :               .map((message) => _chatwootMessageToTextMessage(message))
     210           0 :               .toList();
     211             :           final mergedMessages =
     212           0 :               <types.Message>[..._messages, ...chatMessages].toSet().toList();
     213           0 :           final now = DateTime.now().millisecondsSinceEpoch;
     214           0 :           mergedMessages.sort((a, b) {
     215           0 :             return (b.createdAt ?? now).compareTo(a.createdAt ?? now);
     216             :           });
     217           0 :           _messages = mergedMessages;
     218             :         });
     219           0 :         widget.onMessagesRetrieved?.call(messages);
     220             :       },
     221           0 :       onMessageReceived: (chatwootMessage) {
     222           0 :         _addMessage(_chatwootMessageToTextMessage(chatwootMessage));
     223           0 :         widget.onMessageReceived?.call(chatwootMessage);
     224             :       },
     225           0 :       onMessageDelivered: (chatwootMessage, echoId) {
     226           0 :         _handleMessageSent(
     227           0 :             _chatwootMessageToTextMessage(chatwootMessage, echoId: echoId));
     228           0 :         widget.onMessageDelivered?.call(chatwootMessage);
     229             :       },
     230           0 :       onMessageSent: (chatwootMessage, echoId) {
     231           0 :         final textMessage = types.TextMessage(
     232             :             id: echoId,
     233           0 :             author: _user,
     234           0 :             text: chatwootMessage.content ?? "",
     235             :             status: types.Status.delivered);
     236           0 :         _handleMessageSent(textMessage);
     237           0 :         widget.onMessageSent?.call(chatwootMessage);
     238             :       },
     239           0 :       onError: (error) {
     240           0 :         if (error.type == ChatwootClientExceptionType.SEND_MESSAGE_FAILED) {
     241           0 :           _handleSendMessageFailed(error.data);
     242             :         }
     243           0 :         print("Ooops! Something went wrong. Error Cause: ${error.cause}");
     244           0 :         widget.onError?.call(error);
     245             :       },
     246             :     );
     247             : 
     248           0 :     ChatwootClient.create(
     249           0 :             baseUrl: widget.baseUrl,
     250           0 :             inboxIdentifier: widget.inboxIdentifier,
     251           0 :             user: widget.user,
     252           0 :             enablePersistence: widget.enablePersistence,
     253           0 :             callbacks: chatwootCallbacks)
     254           0 :         .then((client) {
     255           0 :       setState(() {
     256           0 :         chatwootClient = client;
     257           0 :         chatwootClient.loadMessages();
     258             :       });
     259           0 :     }).onError((error, stackTrace) {
     260           0 :       widget.onError?.call(ChatwootClientException(
     261           0 :           error.toString(), ChatwootClientExceptionType.CREATE_CLIENT_FAILED));
     262           0 :       print("chatwoot client failed with error $error: $stackTrace");
     263             :     });
     264             :   }
     265             : 
     266           0 :   types.TextMessage _chatwootMessageToTextMessage(ChatwootMessage message,
     267             :       {String? echoId}) {
     268           0 :     String? avatarUrl = message.sender?.avatarUrl ?? message.sender?.thumbnail;
     269             : 
     270             :     //Sets avatar url to null if its a gravatar not found url
     271             :     //This enables placeholder for avatar to show
     272           0 :     if (avatarUrl?.contains("?d=404") ?? false) {
     273             :       avatarUrl = null;
     274             :     }
     275           0 :     return types.TextMessage(
     276           0 :         id: echoId ?? message.id.toString(),
     277           0 :         author: message.isMine
     278           0 :             ? _user
     279           0 :             : types.User(
     280           0 :                 id: message.sender?.id.toString() ?? idGen.v4(),
     281           0 :                 firstName: message.sender?.name,
     282             :                 imageUrl: avatarUrl,
     283             :               ),
     284           0 :         text: message.content ?? "",
     285             :         status: types.Status.seen,
     286           0 :         createdAt: DateTime.parse(message.createdAt).millisecondsSinceEpoch);
     287             :   }
     288             : 
     289           0 :   void _addMessage(types.Message message) {
     290           0 :     setState(() {
     291           0 :       _messages.insert(0, message);
     292             :     });
     293             :   }
     294             : 
     295           0 :   void _handleSendMessageFailed(String echoId) async {
     296           0 :     final index = _messages.indexWhere((element) => element.id == echoId);
     297           0 :     setState(() {
     298           0 :       _messages[index] = _messages[index].copyWith(status: types.Status.error);
     299             :     });
     300             :   }
     301             : 
     302           0 :   void _handleResendMessage(types.TextMessage message) async {
     303           0 :     chatwootClient.sendMessage(content: message.text, echoId: message.id);
     304           0 :     final index = _messages.indexWhere((element) => element.id == message.id);
     305           0 :     setState(() {
     306           0 :       _messages[index] = message.copyWith(status: types.Status.sending);
     307             :     });
     308             :   }
     309             : 
     310           0 :   void _handleMessageTap(types.Message message) async {
     311           0 :     if (message.status == types.Status.error && message is types.TextMessage) {
     312           0 :       _handleResendMessage(message);
     313             :     }
     314           0 :     widget.onMessageTap?.call(message);
     315             :   }
     316             : 
     317           0 :   void _handlePreviewDataFetched(
     318             :     types.TextMessage message,
     319             :     types.PreviewData previewData,
     320             :   ) {
     321           0 :     final index = _messages.indexWhere((element) => element.id == message.id);
     322           0 :     final updatedMessage = _messages[index].copyWith(previewData: previewData);
     323             : 
     324           0 :     WidgetsBinding.instance?.addPostFrameCallback((_) {
     325           0 :       setState(() {
     326           0 :         _messages[index] = updatedMessage;
     327             :       });
     328             :     });
     329             :   }
     330             : 
     331           0 :   void _handleMessageSent(
     332             :     types.Message message,
     333             :   ) {
     334           0 :     final index = _messages.indexWhere((element) => element.id == message.id);
     335             : 
     336           0 :     if (_messages[index].status == types.Status.seen) {
     337             :       return;
     338             :     }
     339             : 
     340           0 :     WidgetsBinding.instance?.addPostFrameCallback((_) {
     341           0 :       setState(() {
     342           0 :         _messages[index] = message;
     343             :       });
     344             :     });
     345             :   }
     346             : 
     347           0 :   void _handleSendPressed(types.PartialText message) {
     348           0 :     final textMessage = types.TextMessage(
     349           0 :         author: _user,
     350           0 :         createdAt: DateTime.now().millisecondsSinceEpoch,
     351           0 :         id: const Uuid().v4(),
     352           0 :         text: message.text,
     353             :         status: types.Status.sending);
     354             : 
     355           0 :     _addMessage(textMessage);
     356             : 
     357           0 :     chatwootClient.sendMessage(
     358           0 :         content: textMessage.text, echoId: textMessage.id);
     359           0 :     widget.onSendPressed?.call(message);
     360             :   }
     361             : 
     362           0 :   @override
     363             :   Widget build(BuildContext context) {
     364           0 :     return Scaffold(
     365           0 :       appBar: widget.appBar,
     366           0 :       backgroundColor: widget.theme?.backgroundColor ?? CHATWOOT_BG_COLOR,
     367           0 :       body: Padding(
     368             :         padding: const EdgeInsets.only(bottom: 16.0, left: 16.0, right: 16.0),
     369           0 :         child: Chat(
     370           0 :           messages: _messages,
     371           0 :           onMessageTap: _handleMessageTap,
     372           0 :           onPreviewDataFetched: _handlePreviewDataFetched,
     373           0 :           onSendPressed: _handleSendPressed,
     374           0 :           user: _user,
     375           0 :           onEndReached: widget.onEndReached,
     376           0 :           onEndReachedThreshold: widget.onEndReachedThreshold,
     377           0 :           onMessageLongPress: widget.onMessageLongPress,
     378           0 :           onTextChanged: widget.onTextChanged,
     379           0 :           showUserAvatars: widget.showUserAvatars,
     380           0 :           showUserNames: widget.showUserNames,
     381           0 :           timeFormat: widget.timeFormat ?? DateFormat.Hm(),
     382           0 :           dateFormat: widget.timeFormat ?? DateFormat("EEEE MMMM d"),
     383           0 :           theme: widget.theme ?? ChatwootChatTheme(),
     384           0 :           l10n: widget.l10n ??
     385           0 :               ChatL10nEn(
     386             :                   emptyChatPlaceholder: "",
     387             :                   inputPlaceholder: "Type your message"),
     388             :         ),
     389             :       ),
     390             :     );
     391             :   }
     392             : 
     393           0 :   @override
     394             :   void dispose() {
     395           0 :     super.dispose();
     396           0 :     chatwootClient.dispose();
     397             :   }
     398             : }

Generated by: LCOV version 1.15.alpha0w