xmpp: RECEIVE MESSAGES!
This commit is contained in:
		
							parent
							
								
									f22d042255
								
							
						
					
					
						commit
						1acc2630b4
					
				| @ -3,7 +3,7 @@ import "package:moxxyv2/isar.g.dart"; | ||||
| 
 | ||||
| @Collection() | ||||
| @Name("Conversation") | ||||
| class Conversation { | ||||
| class DBConversation { | ||||
|   int? id; | ||||
| 
 | ||||
|   @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. | ||||
|  */ | ||||
| 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 { | ||||
|     return list.firstWhere(test) != null; | ||||
|     return list.firstWhere(test); | ||||
|   } catch(e) { | ||||
|     return false; | ||||
|     return null; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -3,15 +3,17 @@ class Message { | ||||
|   final int timestamp; // NOTE: Milliseconds since Epoch | ||||
|   final String from; | ||||
|   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 }) { | ||||
|     return Message( | ||||
|       from: from ?? this.from, | ||||
|       body: body ?? this.body, | ||||
|       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 { | ||||
|   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 }); | ||||
| } | ||||
| 
 | ||||
| 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 { | ||||
|   final String jid; | ||||
|   final int id; | ||||
|  | ||||
| @ -5,24 +5,14 @@ import "package:moxxyv2/redux/conversation/state.dart"; | ||||
| import "package:moxxyv2/redux/conversation/actions.dart"; | ||||
| 
 | ||||
| HashMap<String, List<Message>> messageReducer(HashMap<String, List<Message>> state, dynamic action) { | ||||
|   if (action is SendMessageAction) { | ||||
|     HashMap<String, List<Message>> map = HashMap<String, List<Message>>()..addAll(state); | ||||
| 
 | ||||
|     Message msg = Message( | ||||
|       from: action.from, | ||||
|       body: action.body, | ||||
|       timestamp: action.timestamp, | ||||
|       sent: true | ||||
|     ); | ||||
|      | ||||
|     String jid = action.jid; | ||||
|     if (!map.containsKey(jid)) { | ||||
|       map[jid] = [ msg ]; | ||||
|       return map; | ||||
|   if (action is AddMessageAction) { | ||||
|     if (!state.containsKey(action.message.from)) { | ||||
|       state[action.message.from] = List.from([ action.message ]); | ||||
|     } else { | ||||
|       state[action.message.from] = state[action.message.from]!..add(action.message); | ||||
|     } | ||||
| 
 | ||||
|     map[jid]!.add(msg); | ||||
|     return map; | ||||
|     return state; | ||||
|   } | ||||
| 
 | ||||
|   return state; | ||||
|  | ||||
| @ -1,16 +1,13 @@ | ||||
| import "dart:collection"; | ||||
| import "package:moxxyv2/models/conversation.dart"; | ||||
| 
 | ||||
| class AddConversationAction { | ||||
|   final String title; | ||||
|   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; | ||||
|   Conversation conversation; | ||||
| 
 | ||||
|   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/conversation/actions.dart"; | ||||
| import "package:moxxyv2/repositories/conversation.dart"; | ||||
| import "package:moxxyv2/models/conversation.dart"; | ||||
| 
 | ||||
| import "package:redux/redux.dart"; | ||||
| import "package:flutter_redux_navigation/flutter_redux_navigation.dart"; | ||||
| import "package:get_it/get_it.dart"; | ||||
| 
 | ||||
| void conversationsMiddleware(Store<MoxxyState> store, action, NextDispatcher next) { | ||||
|   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")); | ||||
|   } | ||||
| 
 | ||||
| void conversationsMiddleware(Store<MoxxyState> store, action, NextDispatcher next) async { | ||||
|    | ||||
|   next(action); | ||||
| } | ||||
|  | ||||
| @ -5,30 +5,16 @@ import "package:moxxyv2/redux/conversation/actions.dart"; | ||||
| 
 | ||||
| List<Conversation> conversationReducer(List<Conversation> state, dynamic action) { | ||||
|   if (action is AddConversationAction) { | ||||
|     state.add(Conversation( | ||||
|         title: action.title, | ||||
|         lastMessageBody: action.lastMessageBody, | ||||
|         avatarUrl: action.avatarUrl, | ||||
|         jid: action.jid, | ||||
|         // 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 state..add(action.conversation); | ||||
|   } else if (action is UpdateConversationAction) { | ||||
|     return state.map((c) { | ||||
|         if (c.id == action.conversation.id) { | ||||
|           return action.conversation; | ||||
|         } | ||||
| 
 | ||||
|         return element; | ||||
|         return c; | ||||
|     }).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; | ||||
| } | ||||
|  | ||||
							
								
								
									
										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 "package:moxxyv2/db/conversation.dart" as db; | ||||
| import "package:moxxyv2/models/conversation.dart" as model; | ||||
| import "package:moxxyv2/db/conversation.dart"; | ||||
| 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/conversations/actions.dart"; | ||||
| import "package:moxxyv2/redux/conversation/actions.dart"; | ||||
| 
 | ||||
| import "package:isar/isar.dart"; | ||||
| import "package:redux/redux.dart"; | ||||
| @ -16,16 +19,18 @@ class DatabaseRepository { | ||||
|   final Isar isar; | ||||
|   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 }); | ||||
| 
 | ||||
|   | ||||
|   Future<void> loadConversations() async { | ||||
|     var conversations = await this.isar.conversations.where().findAll(); | ||||
| 
 | ||||
|     var conversations = await this.isar.dBConversations.where().findAll(); | ||||
|      | ||||
|     conversations.forEach((c) { | ||||
|         this._cache[c.id!] = c; | ||||
|         this.store.dispatch(AddConversationAction( | ||||
|             conversation: Conversation( | ||||
|             id: c.id!, | ||||
|             title: c.title, | ||||
|             jid: c.jid, | ||||
| @ -34,19 +39,32 @@ class DatabaseRepository { | ||||
|             unreadCounter: c.unreadCounter, | ||||
|             lastChangeTimestamp: c.lastChangeTimestamp, | ||||
|             sharedMediaPaths: [], | ||||
|             open: c.open, | ||||
|             triggeredByDatabase: true | ||||
|             open: c.open | ||||
|           ) | ||||
|         )); | ||||
|       } | ||||
|     ); | ||||
|   } | ||||
| 
 | ||||
|   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 | ||||
|   bool hasConversation(int 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"); | ||||
| 
 | ||||
|     final c = this._cache[id]!; | ||||
| @ -59,40 +77,64 @@ class DatabaseRepository { | ||||
|     if (open != null) { | ||||
|       c.open = open; | ||||
|     } | ||||
|     if (unreadCounter != null) { | ||||
|       c.unreadCounter = unreadCounter; | ||||
|     } | ||||
| 
 | ||||
|     await this.isar.writeTxn((isar) async { | ||||
|         await isar.conversations.put(c); | ||||
|         await isar.dBConversations.put(c); | ||||
|         print("DONE"); | ||||
|     }); | ||||
|   } | ||||
|    | ||||
|   Future<void> addConversationFromAction(AddConversationAction action) async { | ||||
|     print("addConversationFromACtion"); | ||||
|     final c = db.Conversation() | ||||
|       ..jid = action.jid | ||||
|       ..title = action.title | ||||
|       ..avatarUrl = action.avatarUrl | ||||
|       ..lastChangeTimestamp = action.lastChangeTimestamp | ||||
|       ..unreadCounter = action.unreadCounter | ||||
|       ..lastMessageBody = action.lastMessageBody | ||||
|       ..open = action.open; | ||||
| 
 | ||||
|   Future<Conversation> addConversationFromData(String title, String lastMessageBody, String avatarUrl, String jid, int unreadCounter, int lastChangeTimestamp, List<String> sharedMediaPaths, bool open) async { | ||||
|     print("addConversationFromAction"); | ||||
|     final c = DBConversation() | ||||
|       ..jid = jid | ||||
|       ..title = title | ||||
|       ..avatarUrl = avatarUrl | ||||
|       ..lastChangeTimestamp = lastChangeTimestamp | ||||
|       ..unreadCounter = unreadCounter | ||||
|       ..lastMessageBody = lastMessageBody | ||||
|       ..open = open; | ||||
|     await this.isar.writeTxn((isar) async { | ||||
|         await isar.conversations.put(c); | ||||
|         await isar.dBConversations.put(c); | ||||
|         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 { | ||||
|     final c = db.Conversation() | ||||
|       ..jid = conversation.jid | ||||
|       ..title = conversation.title | ||||
|       ..avatarUrl = conversation.avatarUrl | ||||
|       ..lastChangeTimestamp = conversation.lastChangeTimestamp | ||||
|       ..unreadCounter = conversation.unreadCounter | ||||
|       ..lastMessageBody = conversation.lastMessageBody | ||||
|       ..open = conversation.open; | ||||
| 
 | ||||
|   Future<Message> addMessageFromData(String body, int timestamp, String from, bool sent) async { | ||||
|     print("addMessageFromData"); | ||||
|     final m = DBMessage() | ||||
|       ..from = from | ||||
|       ..timestamp = timestamp | ||||
|       ..body = body | ||||
|       ..sent = sent; | ||||
|        | ||||
|     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/constants.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_redux/flutter_redux.dart"; | ||||
| import "package:redux/redux.dart"; | ||||
| import "package:get_it/get_it.dart"; | ||||
| 
 | ||||
| typedef SendMessageFunction = void Function(String body); | ||||
| 
 | ||||
| @ -50,8 +53,9 @@ class _MessageListViewModel { | ||||
|   final void Function(bool scrollToEndButton) setShowScrollToEndButton; | ||||
|   final bool showScrollToEndButton; | ||||
|   final void Function() closeChat; | ||||
|    | ||||
|   _MessageListViewModel({ required this.conversation, required this.showSendButton, required this.sendMessage, required this.setShowSendButton, required this.showScrollToEndButton, required this.setShowScrollToEndButton, required this.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, required this.loadMessages }); | ||||
| } | ||||
| 
 | ||||
| class _ListViewWrapperViewModel { | ||||
| @ -182,6 +186,7 @@ class ConversationPage extends StatelessWidget { | ||||
|               jid: jid, | ||||
|               id: conversation.id | ||||
|           )), | ||||
|           loadMessages: (jid) => store.dispatch(LoadMessagesAction(jid: jid)), | ||||
|           sendMessage: (body) => store.dispatch( | ||||
|             // TODO | ||||
|             SendMessageAction( | ||||
| @ -195,6 +200,11 @@ class ConversationPage extends StatelessWidget { | ||||
|         ); | ||||
|       }, | ||||
|       builder: (context, viewModel) { | ||||
|         // TODO: Handle this in a middleware | ||||
|         if (GetIt.I.get<DatabaseRepository>().loadedConversations.indexOf(jid) == -1) { | ||||
|           viewModel.loadMessages(jid); | ||||
|         } | ||||
| 
 | ||||
|         return Scaffold( | ||||
|           appBar: BorderlessTopbar.avatarAndName( | ||||
|             avatar: AvatarWrapper( | ||||
|  | ||||
| @ -1,8 +1,10 @@ | ||||
| import "package:moxxyv2/xmpp/jid.dart"; | ||||
| 
 | ||||
| abstract class XmppEvent {} | ||||
| 
 | ||||
| class MessageEvent extends XmppEvent { | ||||
|   final String body; | ||||
|   final String fromJid; | ||||
|   final FullJID fromJid; | ||||
|   final String sid; | ||||
| 
 | ||||
|   MessageEvent({ required this.body, required this.fromJid, required this.sid }); | ||||
|  | ||||
| @ -30,7 +30,17 @@ class BareJID extends JID { | ||||
| 
 | ||||
| class FullJID extends JID { | ||||
|   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() { | ||||
|     return "${this.local}@${this.domain}/${this.resource}"; | ||||
|   } | ||||
|  | ||||
| @ -1,6 +1,7 @@ | ||||
| import "package:moxxyv2/xmpp/stanzas/stanza.dart"; | ||||
| import "package:moxxyv2/xmpp/connection.dart"; | ||||
| import "package:moxxyv2/xmpp/events.dart"; | ||||
| import "package:moxxyv2/xmpp/jid.dart"; | ||||
| 
 | ||||
| bool handleMessageStanza(XmppConnection conn, Stanza stanza) { | ||||
|   final body = stanza.firstTag("body"); | ||||
| @ -8,7 +9,7 @@ bool handleMessageStanza(XmppConnection conn, Stanza stanza) { | ||||
|    | ||||
|   conn.sendEvent(MessageEvent( | ||||
|       body: body.innerText(), | ||||
|       fromJid: stanza.attributes["from"]!, | ||||
|       fromJid: FullJID.fromString(stanza.attributes["from"]!), | ||||
|       sid: stanza.attributes["id"]! | ||||
|   )); | ||||
| 
 | ||||
|  | ||||
| @ -18,12 +18,12 @@ void main() { | ||||
|       }); | ||||
|   }); | ||||
| 
 | ||||
|   group("listContains", () { | ||||
|   group("firstWhereOrNull", () { | ||||
|       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", () { | ||||
|           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