Line data Source code
1 : 2 : import 'dart:async'; 3 : import 'dart:core'; 4 : 5 : import 'package:chatwoot_client_sdk/chatwoot_callbacks.dart'; 6 : import 'package:chatwoot_client_sdk/data/local/entity/chatwoot_user.dart'; 7 : import 'package:chatwoot_client_sdk/data/local/local_storage.dart'; 8 : import 'package:chatwoot_client_sdk/data/remote/chatwoot_client_exception.dart'; 9 : import 'package:chatwoot_client_sdk/data/remote/requests/chatwoot_action_data.dart'; 10 : import 'package:chatwoot_client_sdk/data/remote/requests/chatwoot_new_message_request.dart'; 11 : import 'package:chatwoot_client_sdk/data/remote/responses/chatwoot_event.dart'; 12 : import 'package:chatwoot_client_sdk/data/remote/service/chatwoot_client_service.dart'; 13 : import 'package:flutter/material.dart'; 14 : 15 : /// Handles interactions between chatwoot client api service[clientService] and 16 : /// [localStorage] if persistence is enabled. 17 : /// 18 : /// Results from repository operations are passed through [callbacks] to be handled 19 : /// appropriately 20 : abstract class ChatwootRepository{ 21 : @protected final ChatwootClientService clientService; 22 : @protected final LocalStorage localStorage; 23 : @protected ChatwootCallbacks callbacks; 24 : List<StreamSubscription> _subscriptions = []; 25 : 26 1 : ChatwootRepository( 27 : this.clientService, 28 : this.localStorage, 29 : this.callbacks 30 : ); 31 : 32 : /// Initializes client contact 33 : Future<void> initialize(ChatwootUser? user); 34 : 35 : /// Fetches persisted messages. 36 : /// 37 : /// Calls [callbacks.onPersistedMessagesRetrieved] if persisted messages are found 38 : void getPersistedMessages(); 39 : 40 : 41 : /// Fetches persisted messages. 42 : /// 43 : /// Calls [callbacks.onMessagesRetrieved] when [clientService.getAllMessages] is successful 44 : /// Calls [callbacks.onError] when [clientService.getAllMessages] fails 45 : Future<void> getMessages(); 46 : 47 : /// Connects to chatwoot websocket and starts listening for updates 48 : /// 49 : /// Calls [callbacks.onWelcome] when websocket welcome event is received 50 : /// Calls [callbacks.onPing] when websocket ping event is received 51 : /// Calls [callbacks.onConfirmedSubscription] when websocket subscription confirmation event is received 52 : /// Calls [callbacks.onMessageCreated] when websocket message created event is received, and 53 : /// message doesn't belong to current user 54 : /// Calls [callbacks.onMyMessageSent] when websocket message created event is received, and message belongs 55 : /// to current user 56 : void listenForEvents(); 57 : 58 : ///Save user object to local storage 59 : Future<void> sendMessage(ChatwootNewMessageRequest request); 60 : 61 : ///Send actions like user started typing 62 : void sendAction(ChatwootActionType action); 63 : 64 : /// Clears all data related to current chatwoot client instance 65 : Future<void> clear(); 66 : 67 : 68 : /// Cancels websocket stream subscriptions and disposes [localStorage] 69 : void dispose(); 70 : 71 : } 72 : 73 : 74 : class ChatwootRepositoryImpl extends ChatwootRepository{ 75 : 76 : 77 1 : ChatwootRepositoryImpl({ 78 : required ChatwootClientService clientService, 79 : required LocalStorage localStorage, 80 : required ChatwootCallbacks streamCallbacks 81 1 : }):super( 82 : clientService, 83 : localStorage, 84 : streamCallbacks 85 : ); 86 : 87 : @override 88 1 : Future<void> getMessages() async{ 89 : try{ 90 3 : final messages = await clientService.getAllMessages(); 91 4 : await localStorage.messagesDao.saveAllMessages(messages); 92 3 : callbacks.onMessagesRetrieved?.call(messages); 93 1 : }on ChatwootClientException catch(e){ 94 3 : callbacks.onError?.call(e); 95 : } 96 : } 97 : 98 1 : @override 99 : void getPersistedMessages() { 100 3 : final persistedMessages = localStorage.messagesDao.getMessages(); 101 1 : if(persistedMessages.isNotEmpty){ 102 3 : callbacks.onPersistedMessagesRetrieved?.call(persistedMessages); 103 : } 104 : } 105 : 106 1 : Future<void> initialize(ChatwootUser? user) async{ 107 : 108 : if(user != null){ 109 4 : await localStorage.userDao.saveUser(user); 110 : //refresh contact 111 4 : final contact = await clientService.updateContact(user.toJson()); 112 3 : localStorage.contactDao.saveContact(contact); 113 : }else{ 114 : //refresh contact 115 3 : final contact = await clientService.getContact(); 116 3 : localStorage.contactDao.saveContact(contact); 117 : } 118 : 119 : //refresh conversation 120 3 : final conversations = await clientService.getConversations(); 121 3 : final persistedConversation = localStorage.conversationDao.getConversation()!; 122 1 : final refreshedConversation = conversations.firstWhere( 123 4 : (element) => element.id == persistedConversation.id, 124 0 : orElse: ()=>persistedConversation //highly unlikely orElse will be called but still added it just in case 125 : ); 126 3 : localStorage.conversationDao.saveConversation(refreshedConversation); 127 : 128 1 : listenForEvents(); 129 : } 130 : 131 : 132 1 : Future<void> sendMessage(ChatwootNewMessageRequest request) async{ 133 : try{ 134 3 : final createdMessage = await clientService.createMessage(request); 135 4 : await localStorage.messagesDao.saveMessage(createdMessage); 136 4 : callbacks.onMessageSent?.call(createdMessage, request.echoId); 137 1 : }on ChatwootClientException catch(e){ 138 3 : callbacks.onError?.call(e); 139 : } 140 : } 141 : 142 1 : @override 143 : void listenForEvents() { 144 6 : clientService.startWebSocketConnection(localStorage.contactDao.getContact()!.pubsubToken); 145 5 : final newSubscription = clientService.connection!.stream.listen((event) { 146 1 : ChatwootEvent chatwootEvent = ChatwootEvent.fromJson(event); 147 2 : if(chatwootEvent.type == ChatwootEventType.welcome){ 148 3 : callbacks.onWelcome?.call(); 149 2 : }else if(chatwootEvent.type == ChatwootEventType.ping){ 150 3 : callbacks.onPing?.call(); 151 2 : }else if(chatwootEvent.type == ChatwootEventType.confirm_subscription){ 152 3 : callbacks.onConfirmedSubscription?.call(); 153 3 : }else if(chatwootEvent.message?.event == ChatwootEventMessageType.message_created){ 154 2 : print("here comes message: $event"); 155 3 : final message = chatwootEvent.message!.data!.getMessage(); 156 1 : if(message.isMine){ 157 6 : callbacks.onMessageDelivered?.call(message, chatwootEvent.message!.data!.echoId!); 158 : }else{ 159 3 : callbacks.onMessageReceived?.call(message); 160 : } 161 3 : }else if(chatwootEvent.message?.event == ChatwootEventMessageType.conversation_typing_off){ 162 3 : callbacks.onConversationStoppedTyping?.call(); 163 3 : }else if(chatwootEvent.message?.event == ChatwootEventMessageType.conversation_typing_on){ 164 3 : callbacks.onConversationStartedTyping?.call(); 165 3 : }else if(chatwootEvent.message?.event == ChatwootEventMessageType.presence_update){ 166 4 : final presenceStatuses = (chatwootEvent.message!.data!.users as Map<dynamic, dynamic>).values; 167 1 : final isOnline = presenceStatuses.contains("online"); 168 : if(isOnline){ 169 3 : callbacks.onConversationIsOnline?.call(); 170 : }else{ 171 3 : callbacks.onConversationIsOffline?.call(); 172 : } 173 : }else{ 174 2 : print("chatwoot unknown event: $event"); 175 : } 176 : }); 177 2 : _subscriptions.add(newSubscription); 178 : } 179 : 180 : @override 181 1 : Future<void> clear() async { 182 3 : await localStorage.clear(); 183 : } 184 : 185 1 : @override 186 : void dispose() { 187 2 : localStorage.dispose(); 188 2 : callbacks = ChatwootCallbacks(); 189 4 : _subscriptions.forEach((subs) { subs.cancel();}); 190 : } 191 : 192 1 : @override 193 : void sendAction(ChatwootActionType action) { 194 6 : clientService.sendAction(localStorage.contactDao.getContact()!.pubsubToken, action); 195 : } 196 : 197 : }