xmpp: RECEIVE MESSAGES!
This commit is contained in:
		
							parent
							
								
									f22d042255
								
							
						
					
					
						commit
						1acc2630b4
					
				| @ -3,7 +3,7 @@ import "package:moxxyv2/isar.g.dart"; | |||||||
| 
 | 
 | ||||||
| @Collection() | @Collection() | ||||||
| @Name("Conversation") | @Name("Conversation") | ||||||
| class Conversation { | class DBConversation { | ||||||
|   int? id; |   int? id; | ||||||
| 
 | 
 | ||||||
|   @Index(caseSensitive: false) |   @Index(caseSensitive: false) | ||||||
|  | |||||||
							
								
								
									
										17
									
								
								lib/db/message.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								lib/db/message.dart
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,17 @@ | |||||||
|  | import "package:isar/isar.dart"; | ||||||
|  | import "package:moxxyv2/isar.g.dart"; | ||||||
|  | 
 | ||||||
|  | @Collection() | ||||||
|  | @Name("Message") | ||||||
|  | class DBMessage { | ||||||
|  |   int? id; | ||||||
|  | 
 | ||||||
|  |   @Index(caseSensitive: false) | ||||||
|  |   late String from; | ||||||
|  | 
 | ||||||
|  |   late int timestamp; | ||||||
|  | 
 | ||||||
|  |   late String body; | ||||||
|  | 
 | ||||||
|  |   late bool sent; | ||||||
|  | } | ||||||
| @ -21,10 +21,18 @@ String padInt(int i) { | |||||||
|  * returned true. |  * returned true. | ||||||
|  */ |  */ | ||||||
| bool listContains<T>(List<T> list, bool Function(T element) test) { | bool listContains<T>(List<T> list, bool Function(T element) test) { | ||||||
|  |   return firstWhereOrNull<T>(list, test) != null; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /* | ||||||
|  |  * A wrapper around List<T>.firstWhere that does not throw but instead just | ||||||
|  |  * return null if test never returned true | ||||||
|  |  */ | ||||||
|  | T? firstWhereOrNull<T>(List<T> list, bool Function(T element) test) { | ||||||
|   try { |   try { | ||||||
|     return list.firstWhere(test) != null; |     return list.firstWhere(test); | ||||||
|   } catch(e) { |   } catch(e) { | ||||||
|     return false; |     return null; | ||||||
|   } |   } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -3,15 +3,17 @@ class Message { | |||||||
|   final int timestamp; // NOTE: Milliseconds since Epoch |   final int timestamp; // NOTE: Milliseconds since Epoch | ||||||
|   final String from; |   final String from; | ||||||
|   final bool sent; |   final bool sent; | ||||||
|  |   final int id; // Database ID | ||||||
| 
 | 
 | ||||||
|   const Message({ required this.from, required this.body, required this.timestamp, required this.sent }); |   const Message({ required this.from, required this.body, required this.timestamp, required this.sent, required this.id }); | ||||||
| 
 | 
 | ||||||
|   Message copyWith({ String? from, String? body, int? timestamp }) { |   Message copyWith({ String? from, String? body, int? timestamp }) { | ||||||
|     return Message( |     return Message( | ||||||
|       from: from ?? this.from, |       from: from ?? this.from, | ||||||
|       body: body ?? this.body, |       body: body ?? this.body, | ||||||
|       timestamp: timestamp ?? this.timestamp, |       timestamp: timestamp ?? this.timestamp, | ||||||
|       sent: this.sent |       sent: this.sent, | ||||||
|  |       id: this.id | ||||||
|     ); |     ); | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  | |||||||
| @ -1,3 +1,6 @@ | |||||||
|  | import "package:moxxyv2/models/message.dart"; | ||||||
|  | import "package:moxxyv2/xmpp/jid.dart"; | ||||||
|  | 
 | ||||||
| class SetShowSendButtonAction { | class SetShowSendButtonAction { | ||||||
|   final bool show; |   final bool show; | ||||||
| 
 | 
 | ||||||
| @ -20,6 +23,21 @@ class SendMessageAction { | |||||||
|   SendMessageAction({ required this.from, required this.body, required this.timestamp, required this.jid, required this.cid }); |   SendMessageAction({ required this.from, required this.body, required this.timestamp, required this.jid, required this.cid }); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | class ReceiveMessageAction { | ||||||
|  |   final String body; | ||||||
|  |   final int timestamp; | ||||||
|  |   final FullJID from; | ||||||
|  |   final String jid; | ||||||
|  | 
 | ||||||
|  |   ReceiveMessageAction({ required this.from, required this.body, required this.timestamp, required this.jid }); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | class AddMessageAction { | ||||||
|  |   final Message message; | ||||||
|  | 
 | ||||||
|  |   AddMessageAction({ required this.message }); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| class CloseConversationAction { | class CloseConversationAction { | ||||||
|   final String jid; |   final String jid; | ||||||
|   final int id; |   final int id; | ||||||
|  | |||||||
| @ -5,24 +5,14 @@ import "package:moxxyv2/redux/conversation/state.dart"; | |||||||
| import "package:moxxyv2/redux/conversation/actions.dart"; | import "package:moxxyv2/redux/conversation/actions.dart"; | ||||||
| 
 | 
 | ||||||
| HashMap<String, List<Message>> messageReducer(HashMap<String, List<Message>> state, dynamic action) { | HashMap<String, List<Message>> messageReducer(HashMap<String, List<Message>> state, dynamic action) { | ||||||
|   if (action is SendMessageAction) { |   if (action is AddMessageAction) { | ||||||
|     HashMap<String, List<Message>> map = HashMap<String, List<Message>>()..addAll(state); |     if (!state.containsKey(action.message.from)) { | ||||||
| 
 |       state[action.message.from] = List.from([ action.message ]); | ||||||
|     Message msg = Message( |     } else { | ||||||
|       from: action.from, |       state[action.message.from] = state[action.message.from]!..add(action.message); | ||||||
|       body: action.body, |  | ||||||
|       timestamp: action.timestamp, |  | ||||||
|       sent: true |  | ||||||
|     ); |  | ||||||
|      |  | ||||||
|     String jid = action.jid; |  | ||||||
|     if (!map.containsKey(jid)) { |  | ||||||
|       map[jid] = [ msg ]; |  | ||||||
|       return map; |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     map[jid]!.add(msg); |     return state; | ||||||
|     return map; |  | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   return state; |   return state; | ||||||
|  | |||||||
| @ -1,16 +1,13 @@ | |||||||
| import "dart:collection"; | import "package:moxxyv2/models/conversation.dart"; | ||||||
| 
 | 
 | ||||||
| class AddConversationAction { | class AddConversationAction { | ||||||
|   final String title; |   Conversation conversation; | ||||||
|   final String lastMessageBody; |  | ||||||
|   final String avatarUrl; |  | ||||||
|   final String jid; |  | ||||||
|   final int id; |  | ||||||
|   final int unreadCounter; |  | ||||||
|   final List<String> sharedMediaPaths; |  | ||||||
|   final int lastChangeTimestamp; |  | ||||||
|   final bool triggeredByDatabase; |  | ||||||
|   final bool open; |  | ||||||
| 
 | 
 | ||||||
|   AddConversationAction({ required this.title, required this.lastMessageBody, required this.avatarUrl, required this.jid, required this.sharedMediaPaths, required this.lastChangeTimestamp, required this.id, this.unreadCounter = 0, this.triggeredByDatabase = false, required this.open }); |   AddConversationAction({ required this.conversation }); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | class UpdateConversationAction { | ||||||
|  |   Conversation conversation; | ||||||
|  | 
 | ||||||
|  |   UpdateConversationAction({ required this.conversation }); | ||||||
| } | } | ||||||
|  | |||||||
| @ -2,29 +2,13 @@ import "package:moxxyv2/redux/state.dart"; | |||||||
| import "package:moxxyv2/redux/conversations/actions.dart"; | import "package:moxxyv2/redux/conversations/actions.dart"; | ||||||
| import "package:moxxyv2/redux/conversation/actions.dart"; | import "package:moxxyv2/redux/conversation/actions.dart"; | ||||||
| import "package:moxxyv2/repositories/conversation.dart"; | import "package:moxxyv2/repositories/conversation.dart"; | ||||||
|  | import "package:moxxyv2/models/conversation.dart"; | ||||||
| 
 | 
 | ||||||
| import "package:redux/redux.dart"; | import "package:redux/redux.dart"; | ||||||
| import "package:flutter_redux_navigation/flutter_redux_navigation.dart"; | import "package:flutter_redux_navigation/flutter_redux_navigation.dart"; | ||||||
| import "package:get_it/get_it.dart"; | import "package:get_it/get_it.dart"; | ||||||
| 
 | 
 | ||||||
| void conversationsMiddleware(Store<MoxxyState> store, action, NextDispatcher next) { | void conversationsMiddleware(Store<MoxxyState> store, action, NextDispatcher next) async { | ||||||
|   var repo = GetIt.I.get<DatabaseRepository>(); |    | ||||||
| 
 |  | ||||||
|   if (action is AddConversationAction && !action.triggeredByDatabase) { |  | ||||||
|     if (repo.hasConversation(action.id)) { |  | ||||||
|       // TODO |  | ||||||
|     } else { |  | ||||||
|       repo.addConversationFromAction(action); |  | ||||||
|     } |  | ||||||
|   } else if (action is SendMessageAction) { |  | ||||||
|     if (repo.hasConversation(action.cid)) { |  | ||||||
|       repo.updateConversation(id: action.cid, lastMessageBody: action.body, lastChangeTimestamp: action.timestamp); |  | ||||||
|     } else { |  | ||||||
|       // TODO |  | ||||||
|     } |  | ||||||
|   } else if (action is CloseConversationAction) { |  | ||||||
|     store.dispatch(NavigateToAction.replace("/conversations")); |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   next(action); |   next(action); | ||||||
| } | } | ||||||
|  | |||||||
| @ -5,30 +5,16 @@ import "package:moxxyv2/redux/conversation/actions.dart"; | |||||||
| 
 | 
 | ||||||
| List<Conversation> conversationReducer(List<Conversation> state, dynamic action) { | List<Conversation> conversationReducer(List<Conversation> state, dynamic action) { | ||||||
|   if (action is AddConversationAction) { |   if (action is AddConversationAction) { | ||||||
|     state.add(Conversation( |     return state..add(action.conversation); | ||||||
|         title: action.title, |   } else if (action is UpdateConversationAction) { | ||||||
|         lastMessageBody: action.lastMessageBody, |     return state.map((c) { | ||||||
|         avatarUrl: action.avatarUrl, |         if (c.id == action.conversation.id) { | ||||||
|         jid: action.jid, |           return action.conversation; | ||||||
|         // TODO: Correct? |  | ||||||
|         unreadCounter: 0, |  | ||||||
|         sharedMediaPaths: action.sharedMediaPaths, |  | ||||||
|         lastChangeTimestamp: action.lastChangeTimestamp, |  | ||||||
|         open: action.open, |  | ||||||
|         id: action.id |  | ||||||
|     )); |  | ||||||
|   } else if (action is SendMessageAction) { |  | ||||||
|     return state.map((element) { |  | ||||||
|         if (element.jid == action.jid) { |  | ||||||
|           return element.copyWith(lastMessageBody: action.body, lastChangeTimestamp: action.timestamp); |  | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         return element; |         return c; | ||||||
|     }).toList(); |     }).toList(); | ||||||
|   } else if (action is CloseConversationAction) { |  | ||||||
|     // TODO: Yikes. |  | ||||||
|     return state.map((element) => element.jid == action.jid ? element.copyWith(open: false) : element).toList().where((element) => element.open).toList(); |  | ||||||
|   } |   } | ||||||
| 
 |    | ||||||
|   return state; |   return state; | ||||||
| } | } | ||||||
|  | |||||||
							
								
								
									
										5
									
								
								lib/redux/messages/actions.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								lib/redux/messages/actions.dart
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,5 @@ | |||||||
|  | class LoadMessagesAction { | ||||||
|  |   final String jid; | ||||||
|  | 
 | ||||||
|  |   LoadMessagesAction({ required this.jid }); | ||||||
|  | } | ||||||
							
								
								
									
										65
									
								
								lib/redux/messages/middleware.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										65
									
								
								lib/redux/messages/middleware.dart
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,65 @@ | |||||||
|  | import "package:moxxyv2/redux/state.dart"; | ||||||
|  | import "package:moxxyv2/redux/conversations/actions.dart"; | ||||||
|  | import "package:moxxyv2/redux/conversation/actions.dart"; | ||||||
|  | import "package:moxxyv2/repositories/conversation.dart"; | ||||||
|  | import "package:moxxyv2/models/conversation.dart"; | ||||||
|  | import "package:moxxyv2/models/message.dart"; | ||||||
|  | import "package:moxxyv2/redux/messages/actions.dart"; | ||||||
|  | import "package:moxxyv2/helpers.dart"; | ||||||
|  | 
 | ||||||
|  | import "package:redux/redux.dart"; | ||||||
|  | import "package:flutter_redux_navigation/flutter_redux_navigation.dart"; | ||||||
|  | import "package:get_it/get_it.dart"; | ||||||
|  | 
 | ||||||
|  | void messageMiddleware(Store<MoxxyState> store, action, NextDispatcher next) async { | ||||||
|  |   if (action is ReceiveMessageAction) { | ||||||
|  |     // TODO: Check if the conversation already exists | ||||||
|  |     final repo = GetIt.I.get<DatabaseRepository>(); | ||||||
|  |     final now = DateTime.now().millisecondsSinceEpoch; | ||||||
|  |     final bareJidString = action.from.toBare().toString(); | ||||||
|  |      | ||||||
|  |     final message = await repo.addMessageFromData( | ||||||
|  |       action.body, | ||||||
|  |       now, | ||||||
|  |       bareJidString, | ||||||
|  |       false | ||||||
|  |     ); | ||||||
|  | 
 | ||||||
|  |     final existantConversation = firstWhereOrNull(store.state.conversations, (Conversation c) => c.jid == bareJidString); | ||||||
|  |     if (existantConversation == null) { | ||||||
|  |       final conversation = await repo.addConversationFromData( | ||||||
|  |         action.from.local, | ||||||
|  |         action.body, | ||||||
|  |         "", | ||||||
|  |         bareJidString, | ||||||
|  |         1, | ||||||
|  |         now, | ||||||
|  |         [], | ||||||
|  |         true | ||||||
|  |       ); | ||||||
|  | 
 | ||||||
|  |       repo.loadedConversations.add(bareJidString); | ||||||
|  |       store.dispatch(AddConversationAction(conversation: conversation)); | ||||||
|  |     } else { | ||||||
|  |       await repo.updateConversation( | ||||||
|  |         id: existantConversation.id, | ||||||
|  |         lastMessageBody: action.body, | ||||||
|  |         lastChangeTimestamp: now, | ||||||
|  |         unreadCounter: existantConversation.unreadCounter + 1 | ||||||
|  |       ); | ||||||
|  |       store.dispatch(UpdateConversationAction( | ||||||
|  |           conversation: existantConversation.copyWith( | ||||||
|  |             lastMessageBody: action.body, | ||||||
|  |             lastChangeTimestamp: now, | ||||||
|  |             unreadCounter: existantConversation.unreadCounter + 1 | ||||||
|  |           ) | ||||||
|  |       )); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     store.dispatch(AddMessageAction(message: message)); | ||||||
|  |   } else if (action is LoadMessagesAction) { | ||||||
|  |     GetIt.I.get<DatabaseRepository>().loadMessagesForJid(action.jid); | ||||||
|  |   } | ||||||
|  |    | ||||||
|  |   next(action); | ||||||
|  | } | ||||||
| @ -1,9 +1,12 @@ | |||||||
| import "dart:collection"; | import "dart:collection"; | ||||||
| 
 | 
 | ||||||
| import "package:moxxyv2/db/conversation.dart" as db; | import "package:moxxyv2/db/conversation.dart"; | ||||||
| import "package:moxxyv2/models/conversation.dart" as model; | import "package:moxxyv2/db/message.dart"; | ||||||
|  | import "package:moxxyv2/models/conversation.dart"; | ||||||
|  | import "package:moxxyv2/models/message.dart"; | ||||||
| import "package:moxxyv2/redux/state.dart"; | import "package:moxxyv2/redux/state.dart"; | ||||||
| import "package:moxxyv2/redux/conversations/actions.dart"; | import "package:moxxyv2/redux/conversations/actions.dart"; | ||||||
|  | import "package:moxxyv2/redux/conversation/actions.dart"; | ||||||
| 
 | 
 | ||||||
| import "package:isar/isar.dart"; | import "package:isar/isar.dart"; | ||||||
| import "package:redux/redux.dart"; | import "package:redux/redux.dart"; | ||||||
| @ -16,16 +19,18 @@ class DatabaseRepository { | |||||||
|   final Isar isar; |   final Isar isar; | ||||||
|   final Store<MoxxyState> store; |   final Store<MoxxyState> store; | ||||||
| 
 | 
 | ||||||
|   final HashMap<int, db.Conversation> _cache = HashMap(); |   final HashMap<int, DBConversation> _cache = HashMap(); | ||||||
|  |   final List<String> loadedConversations = List.empty(growable: true); | ||||||
|    |    | ||||||
|   DatabaseRepository({ required this.isar, required this.store }); |   DatabaseRepository({ required this.isar, required this.store }); | ||||||
| 
 |   | ||||||
|   Future<void> loadConversations() async { |   Future<void> loadConversations() async { | ||||||
|     var conversations = await this.isar.conversations.where().findAll(); |     var conversations = await this.isar.dBConversations.where().findAll(); | ||||||
| 
 |      | ||||||
|     conversations.forEach((c) { |     conversations.forEach((c) { | ||||||
|         this._cache[c.id!] = c; |         this._cache[c.id!] = c; | ||||||
|         this.store.dispatch(AddConversationAction( |         this.store.dispatch(AddConversationAction( | ||||||
|  |             conversation: Conversation( | ||||||
|             id: c.id!, |             id: c.id!, | ||||||
|             title: c.title, |             title: c.title, | ||||||
|             jid: c.jid, |             jid: c.jid, | ||||||
| @ -34,19 +39,32 @@ class DatabaseRepository { | |||||||
|             unreadCounter: c.unreadCounter, |             unreadCounter: c.unreadCounter, | ||||||
|             lastChangeTimestamp: c.lastChangeTimestamp, |             lastChangeTimestamp: c.lastChangeTimestamp, | ||||||
|             sharedMediaPaths: [], |             sharedMediaPaths: [], | ||||||
|             open: c.open, |             open: c.open | ||||||
|             triggeredByDatabase: true |           ) | ||||||
|         )); |         )); | ||||||
|       } |       } | ||||||
|     ); |     ); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  |   Future<void> loadMessagesForJid(String jid) async { | ||||||
|  |     final messages = await this.isar.dBMessages.where().fromEqualTo(jid).findAll(); | ||||||
|  |     this.loadedConversations.add(jid); | ||||||
|  | 
 | ||||||
|  |     messages.forEach((m) => this.store.dispatch(AddMessageAction(message: Message( | ||||||
|  |             from: m.from, | ||||||
|  |             body: m.body, | ||||||
|  |             timestamp: m.timestamp, | ||||||
|  |             sent: m.sent, | ||||||
|  |             id: m.id! | ||||||
|  |     )))); | ||||||
|  |   } | ||||||
|  |    | ||||||
|   // TODO |   // TODO | ||||||
|   bool hasConversation(int id) { |   bool hasConversation(int id) { | ||||||
|     return this._cache.containsKey(id); |     return this._cache.containsKey(id); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   Future<void> updateConversation({ required int id, String? lastMessageBody, int? lastChangeTimestamp, bool? open }) async { |   Future<void> updateConversation({ required int id, String? lastMessageBody, int? lastChangeTimestamp, bool? open, int? unreadCounter }) async { | ||||||
|     print("updateConversation"); |     print("updateConversation"); | ||||||
| 
 | 
 | ||||||
|     final c = this._cache[id]!; |     final c = this._cache[id]!; | ||||||
| @ -59,40 +77,64 @@ class DatabaseRepository { | |||||||
|     if (open != null) { |     if (open != null) { | ||||||
|       c.open = open; |       c.open = open; | ||||||
|     } |     } | ||||||
|  |     if (unreadCounter != null) { | ||||||
|  |       c.unreadCounter = unreadCounter; | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
|     await this.isar.writeTxn((isar) async { |     await this.isar.writeTxn((isar) async { | ||||||
|         await isar.conversations.put(c); |         await isar.dBConversations.put(c); | ||||||
|         print("DONE"); |         print("DONE"); | ||||||
|     }); |     }); | ||||||
|   } |   } | ||||||
|    | 
 | ||||||
|   Future<void> addConversationFromAction(AddConversationAction action) async { |   Future<Conversation> addConversationFromData(String title, String lastMessageBody, String avatarUrl, String jid, int unreadCounter, int lastChangeTimestamp, List<String> sharedMediaPaths, bool open) async { | ||||||
|     print("addConversationFromACtion"); |     print("addConversationFromAction"); | ||||||
|     final c = db.Conversation() |     final c = DBConversation() | ||||||
|       ..jid = action.jid |       ..jid = jid | ||||||
|       ..title = action.title |       ..title = title | ||||||
|       ..avatarUrl = action.avatarUrl |       ..avatarUrl = avatarUrl | ||||||
|       ..lastChangeTimestamp = action.lastChangeTimestamp |       ..lastChangeTimestamp = lastChangeTimestamp | ||||||
|       ..unreadCounter = action.unreadCounter |       ..unreadCounter = unreadCounter | ||||||
|       ..lastMessageBody = action.lastMessageBody |       ..lastMessageBody = lastMessageBody | ||||||
|       ..open = action.open; |       ..open = open; | ||||||
|     await this.isar.writeTxn((isar) async { |     await this.isar.writeTxn((isar) async { | ||||||
|         await isar.conversations.put(c); |         await isar.dBConversations.put(c); | ||||||
|         print("DONE"); |         print("DONE"); | ||||||
|     }); |     }); | ||||||
|  |     this._cache[c.id!] = c; | ||||||
|  | 
 | ||||||
|  |     return Conversation( | ||||||
|  |       title: title, | ||||||
|  |       lastMessageBody: lastMessageBody, | ||||||
|  |       avatarUrl: avatarUrl, | ||||||
|  |       jid: jid, | ||||||
|  |       id: c.id!, | ||||||
|  |       unreadCounter: unreadCounter, | ||||||
|  |       lastChangeTimestamp: lastChangeTimestamp, | ||||||
|  |       sharedMediaPaths: sharedMediaPaths, | ||||||
|  |       open: open | ||||||
|  |     ); | ||||||
|   } |   } | ||||||
|    | 
 | ||||||
|   Future<void> addConversation(model.Conversation conversation) async { |   Future<Message> addMessageFromData(String body, int timestamp, String from, bool sent) async { | ||||||
|     final c = db.Conversation() |     print("addMessageFromData"); | ||||||
|       ..jid = conversation.jid |     final m = DBMessage() | ||||||
|       ..title = conversation.title |       ..from = from | ||||||
|       ..avatarUrl = conversation.avatarUrl |       ..timestamp = timestamp | ||||||
|       ..lastChangeTimestamp = conversation.lastChangeTimestamp |       ..body = body | ||||||
|       ..unreadCounter = conversation.unreadCounter |       ..sent = sent; | ||||||
|       ..lastMessageBody = conversation.lastMessageBody |        | ||||||
|       ..open = conversation.open; |  | ||||||
|     await this.isar.writeTxn((isar) async { |     await this.isar.writeTxn((isar) async { | ||||||
|         await isar.conversations.put(c); |         await isar.dBMessages.put(m); | ||||||
|  |         print("DONE"); | ||||||
|     }); |     }); | ||||||
|  | 
 | ||||||
|  |     return Message( | ||||||
|  |       body: body, | ||||||
|  |       from: from, | ||||||
|  |       timestamp: timestamp, | ||||||
|  |       sent: sent, | ||||||
|  |       id: m.id! | ||||||
|  |     ); | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  | |||||||
| @ -13,10 +13,13 @@ import "package:moxxyv2/ui/pages/profile/profile.dart"; | |||||||
| import "package:moxxyv2/ui/pages/conversation/arguments.dart"; | import "package:moxxyv2/ui/pages/conversation/arguments.dart"; | ||||||
| import "package:moxxyv2/ui/constants.dart"; | import "package:moxxyv2/ui/constants.dart"; | ||||||
| import "package:moxxyv2/ui/helpers.dart"; | import "package:moxxyv2/ui/helpers.dart"; | ||||||
|  | import "package:moxxyv2/repositories/conversation.dart"; | ||||||
|  | import "package:moxxyv2/redux/messages/actions.dart"; | ||||||
| 
 | 
 | ||||||
| import "package:flutter_speed_dial/flutter_speed_dial.dart"; | import "package:flutter_speed_dial/flutter_speed_dial.dart"; | ||||||
| import "package:flutter_redux/flutter_redux.dart"; | import "package:flutter_redux/flutter_redux.dart"; | ||||||
| import "package:redux/redux.dart"; | import "package:redux/redux.dart"; | ||||||
|  | import "package:get_it/get_it.dart"; | ||||||
| 
 | 
 | ||||||
| typedef SendMessageFunction = void Function(String body); | typedef SendMessageFunction = void Function(String body); | ||||||
| 
 | 
 | ||||||
| @ -50,8 +53,9 @@ class _MessageListViewModel { | |||||||
|   final void Function(bool scrollToEndButton) setShowScrollToEndButton; |   final void Function(bool scrollToEndButton) setShowScrollToEndButton; | ||||||
|   final bool showScrollToEndButton; |   final bool showScrollToEndButton; | ||||||
|   final void Function() closeChat; |   final void Function() closeChat; | ||||||
|    |   final void Function(String) loadMessages; | ||||||
|   _MessageListViewModel({ required this.conversation, required this.showSendButton, required this.sendMessage, required this.setShowSendButton, required this.showScrollToEndButton, required this.setShowScrollToEndButton, required this.closeChat }); |   | ||||||
|  |   _MessageListViewModel({ required this.conversation, required this.showSendButton, required this.sendMessage, required this.setShowSendButton, required this.showScrollToEndButton, required this.setShowScrollToEndButton, required this.closeChat, required this.loadMessages }); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| class _ListViewWrapperViewModel { | class _ListViewWrapperViewModel { | ||||||
| @ -182,6 +186,7 @@ class ConversationPage extends StatelessWidget { | |||||||
|               jid: jid, |               jid: jid, | ||||||
|               id: conversation.id |               id: conversation.id | ||||||
|           )), |           )), | ||||||
|  |           loadMessages: (jid) => store.dispatch(LoadMessagesAction(jid: jid)), | ||||||
|           sendMessage: (body) => store.dispatch( |           sendMessage: (body) => store.dispatch( | ||||||
|             // TODO |             // TODO | ||||||
|             SendMessageAction( |             SendMessageAction( | ||||||
| @ -195,6 +200,11 @@ class ConversationPage extends StatelessWidget { | |||||||
|         ); |         ); | ||||||
|       }, |       }, | ||||||
|       builder: (context, viewModel) { |       builder: (context, viewModel) { | ||||||
|  |         // TODO: Handle this in a middleware | ||||||
|  |         if (GetIt.I.get<DatabaseRepository>().loadedConversations.indexOf(jid) == -1) { | ||||||
|  |           viewModel.loadMessages(jid); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|         return Scaffold( |         return Scaffold( | ||||||
|           appBar: BorderlessTopbar.avatarAndName( |           appBar: BorderlessTopbar.avatarAndName( | ||||||
|             avatar: AvatarWrapper( |             avatar: AvatarWrapper( | ||||||
|  | |||||||
| @ -1,8 +1,10 @@ | |||||||
|  | import "package:moxxyv2/xmpp/jid.dart"; | ||||||
|  | 
 | ||||||
| abstract class XmppEvent {} | abstract class XmppEvent {} | ||||||
| 
 | 
 | ||||||
| class MessageEvent extends XmppEvent { | class MessageEvent extends XmppEvent { | ||||||
|   final String body; |   final String body; | ||||||
|   final String fromJid; |   final FullJID fromJid; | ||||||
|   final String sid; |   final String sid; | ||||||
| 
 | 
 | ||||||
|   MessageEvent({ required this.body, required this.fromJid, required this.sid }); |   MessageEvent({ required this.body, required this.fromJid, required this.sid }); | ||||||
|  | |||||||
| @ -30,7 +30,17 @@ class BareJID extends JID { | |||||||
| 
 | 
 | ||||||
| class FullJID extends JID { | class FullJID extends JID { | ||||||
|   FullJID({ required String local, required String domain, required String resource }) : super(local: local, domain: domain, resource: resource); |   FullJID({ required String local, required String domain, required String resource }) : super(local: local, domain: domain, resource: resource); | ||||||
|  |    | ||||||
|  |   BareJID toBare() { | ||||||
|  |     return BareJID(local: this.local, domain: this.domain); | ||||||
|  |   } | ||||||
| 
 | 
 | ||||||
|  |   static FullJID fromString(String fullJid) { | ||||||
|  |     final jidParts = fullJid.split("@"); | ||||||
|  |     final other = jidParts[1].split("/"); | ||||||
|  |     return FullJID(local: jidParts[0], domain: other[0], resource: other[1]); | ||||||
|  |   } | ||||||
|  |    | ||||||
|   String toString() { |   String toString() { | ||||||
|     return "${this.local}@${this.domain}/${this.resource}"; |     return "${this.local}@${this.domain}/${this.resource}"; | ||||||
|   } |   } | ||||||
|  | |||||||
| @ -1,6 +1,7 @@ | |||||||
| import "package:moxxyv2/xmpp/stanzas/stanza.dart"; | import "package:moxxyv2/xmpp/stanzas/stanza.dart"; | ||||||
| import "package:moxxyv2/xmpp/connection.dart"; | import "package:moxxyv2/xmpp/connection.dart"; | ||||||
| import "package:moxxyv2/xmpp/events.dart"; | import "package:moxxyv2/xmpp/events.dart"; | ||||||
|  | import "package:moxxyv2/xmpp/jid.dart"; | ||||||
| 
 | 
 | ||||||
| bool handleMessageStanza(XmppConnection conn, Stanza stanza) { | bool handleMessageStanza(XmppConnection conn, Stanza stanza) { | ||||||
|   final body = stanza.firstTag("body"); |   final body = stanza.firstTag("body"); | ||||||
| @ -8,7 +9,7 @@ bool handleMessageStanza(XmppConnection conn, Stanza stanza) { | |||||||
|    |    | ||||||
|   conn.sendEvent(MessageEvent( |   conn.sendEvent(MessageEvent( | ||||||
|       body: body.innerText(), |       body: body.innerText(), | ||||||
|       fromJid: stanza.attributes["from"]!, |       fromJid: FullJID.fromString(stanza.attributes["from"]!), | ||||||
|       sid: stanza.attributes["id"]! |       sid: stanza.attributes["id"]! | ||||||
|   )); |   )); | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -18,12 +18,12 @@ void main() { | |||||||
|       }); |       }); | ||||||
|   }); |   }); | ||||||
| 
 | 
 | ||||||
|   group("listContains", () { |   group("firstWhereOrNull", () { | ||||||
|       test("[] should not contain 1", () { |       test("[] should not contain 1", () { | ||||||
|           expect(listContains<int>([], (int element) => element == 1), false); |           expect(firstWhereOrNull<int>([], (int element) => element == 1), null); | ||||||
|       }); |       }); | ||||||
|       test("[1, 2, 3] should contain 2", () { |       test("[1, 2, 3] should contain 2", () { | ||||||
|           expect(listContains([ 1, 2, 3 ], (int element) => element == 2), true); |           expect(firstWhereOrNull([ 1, 2, 3 ], (int element) => element == 2), 2); | ||||||
|       }); |       }); | ||||||
|   }); |   }); | ||||||
| 
 | 
 | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user