From f4955f5478e5e9e50c3909a4b3cf82bcfea9216f Mon Sep 17 00:00:00 2001 From: Christian Pauly Date: Mon, 30 May 2022 13:44:05 +0200 Subject: [PATCH] refactor: Switch to Hive Collections DB --- lib/pages/chat/chat.dart | 6 +- lib/pages/chat/chat_app_bar_title.dart | 5 +- lib/pages/chat/chat_input_row.dart | 4 +- lib/pages/chat/chat_view.dart | 4 +- lib/pages/chat/event_info_dialog.dart | 8 +- lib/pages/chat/events/message.dart | 41 +++++--- lib/pages/chat/events/message_content.dart | 97 ++++++++++++------- lib/pages/chat/events/message_reactions.dart | 2 +- lib/pages/chat/events/reply_content.dart | 33 ++++--- lib/pages/chat/events/state_message.dart | 28 ++++-- lib/pages/chat/pinned_events.dart | 63 ++++++------ lib/pages/chat/reply_display.dart | 35 ++++--- .../chat_encryption_settings_view.dart | 9 +- lib/pages/chat_list/chat_list_item.dart | 67 ++++++++----- .../chat_list/client_chooser_button.dart | 4 +- .../invitation_selection.dart | 2 +- .../key_verification_dialog.dart | 2 +- lib/pages/search/search_view.dart | 6 +- lib/pages/story/story_page.dart | 7 +- lib/utils/client_manager.dart | 6 +- .../client_stories_extension.dart | 3 +- .../fluffybox_database.dart | 3 + ...=> flutter_hive_collections_database.dart} | 92 ++++++++++++------ lib/utils/push_helper.dart | 2 +- .../local_notifications_extension.dart | 9 +- lib/widgets/public_room_bottom_sheet.dart | 4 +- pubspec.lock | 15 ++- pubspec.yaml | 2 +- test/utils/test_client.dart | 4 +- 29 files changed, 347 insertions(+), 216 deletions(-) rename lib/utils/matrix_sdk_extensions.dart/{flutter_matrix_hive_database.dart => flutter_hive_collections_database.dart} (50%) diff --git a/lib/pages/chat/chat.dart b/lib/pages/chat/chat.dart index 4b3d3cfd..c58272cd 100644 --- a/lib/pages/chat/chat.dart +++ b/lib/pages/chat/chat.dart @@ -461,11 +461,11 @@ class ChatController extends State { if (selectedEvents.length == 1) { return selectedEvents.first .getDisplayEvent(timeline!) - .getLocalizedBody(MatrixLocals(L10n.of(context)!)); + .calcLocalizedBodyFallback(MatrixLocals(L10n.of(context)!)); } for (final event in selectedEvents) { if (copyString.isNotEmpty) copyString += '\n\n'; - copyString += event.getDisplayEvent(timeline!).getLocalizedBody( + copyString += event.getDisplayEvent(timeline!).calcLocalizedBodyFallback( MatrixLocals(L10n.of(context)!), withSenderNamePrefix: true); } @@ -773,7 +773,7 @@ class ChatController extends State { editEvent = selectedEvents.first; inputText = sendController.text = editEvent! .getDisplayEvent(timeline!) - .getLocalizedBody(MatrixLocals(L10n.of(context)!), + .calcLocalizedBodyFallback(MatrixLocals(L10n.of(context)!), withSenderNamePrefix: false, hideReply: true); selectedEvents.clear(); }); diff --git a/lib/pages/chat/chat_app_bar_title.dart b/lib/pages/chat/chat_app_bar_title.dart index a112ff8c..f4a369d7 100644 --- a/lib/pages/chat/chat_app_bar_title.dart +++ b/lib/pages/chat/chat_app_bar_title.dart @@ -29,10 +29,11 @@ class ChatAppBarTitle extends StatelessWidget { ? () => showModalBottomSheet( context: context, builder: (c) => UserBottomSheet( - user: room.getUserByMXIDSync(directChatMatrixID), + user: room + .unsafeGetUserFromMemoryOrFallback(directChatMatrixID), outerContext: context, onMention: () => controller.sendController.text += - '${room.getUserByMXIDSync(directChatMatrixID).mention} ', + '${room.unsafeGetUserFromMemoryOrFallback(directChatMatrixID).mention} ', ), ) : () => VRouter.of(context).toSegments(['rooms', room.id, 'details']), diff --git a/lib/pages/chat/chat_input_row.dart b/lib/pages/chat/chat_input_row.dart index 19599f78..62f1db76 100644 --- a/lib/pages/chat/chat_input_row.dart +++ b/lib/pages/chat/chat_input_row.dart @@ -293,14 +293,14 @@ class _ChatAccountPicker extends StatelessWidget { return Padding( padding: const EdgeInsets.all(8.0), child: FutureBuilder( - future: controller.sendingClient!.ownProfile, + future: controller.sendingClient!.fetchOwnProfile(), builder: (context, snapshot) => PopupMenuButton( onSelected: _popupMenuButtonSelected, itemBuilder: (BuildContext context) => clients .map((client) => PopupMenuItem( value: client!.userID, child: FutureBuilder( - future: client.ownProfile, + future: client.fetchOwnProfile(), builder: (context, snapshot) => ListTile( leading: Avatar( mxContent: snapshot.data?.avatarUrl, diff --git a/lib/pages/chat/chat_view.dart b/lib/pages/chat/chat_view.dart index ee32862b..d628737b 100644 --- a/lib/pages/chat/chat_view.dart +++ b/lib/pages/chat/chat_view.dart @@ -350,12 +350,12 @@ class ChatView extends StatelessWidget { builder: (c) => UserBottomSheet( user: event - .sender, + .senderFromMemoryOrFallback, outerContext: context, onMention: () => controller .sendController - .text += '${event.sender.mention} ', + .text += '${event.senderFromMemoryOrFallback.mention} ', ), ), unfold: controller diff --git a/lib/pages/chat/event_info_dialog.dart b/lib/pages/chat/event_info_dialog.dart index ce9be4d8..16862bf0 100644 --- a/lib/pages/chat/event_info_dialog.dart +++ b/lib/pages/chat/event_info_dialog.dart @@ -48,12 +48,12 @@ class EventInfoDialog extends StatelessWidget { children: [ ListTile( leading: Avatar( - mxContent: event.sender.avatarUrl, - name: event.sender.calcDisplayname(), + mxContent: event.senderFromMemoryOrFallback.avatarUrl, + name: event.senderFromMemoryOrFallback.calcDisplayname(), ), title: Text(L10n.of(context)!.sender), - subtitle: - Text('${event.sender.calcDisplayname()} [${event.senderId}]'), + subtitle: Text( + '${event.senderFromMemoryOrFallback.calcDisplayname()} [${event.senderId}]'), ), ListTile( title: Text(L10n.of(context)!.time), diff --git a/lib/pages/chat/events/message.dart b/lib/pages/chat/events/message.dart index 37f5dd94..32f0873a 100644 --- a/lib/pages/chat/events/message.dart +++ b/lib/pages/chat/events/message.dart @@ -75,7 +75,7 @@ class Message extends StatelessWidget { EventTypes.Sticker, EventTypes.Encrypted, ].contains(nextEvent!.type) - ? nextEvent!.sender.id == event.sender.id && !displayTime + ? nextEvent!.senderId == event.senderId && !displayTime : false; final textColor = ownMessage ? Theme.of(context).colorScheme.onPrimary @@ -125,11 +125,16 @@ class Message extends StatelessWidget { ), ), )) - : Avatar( - mxContent: event.sender.avatarUrl, - name: event.sender.calcDisplayname(), - onTap: () => onAvatarTab!(event), - ), + : FutureBuilder( + future: event.fetchSenderUser(), + builder: (context, snapshot) { + final user = snapshot.data ?? event.senderFromMemoryOrFallback; + return Avatar( + mxContent: user.avatarUrl, + name: user.calcDisplayname(), + onTap: () => onAvatarTab!(event), + ); + }), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, @@ -140,14 +145,22 @@ class Message extends StatelessWidget { padding: const EdgeInsets.only(left: 8.0, bottom: 4), child: ownMessage || event.room.isDirectChat ? const SizedBox(height: 12) - : Text( - event.sender.calcDisplayname(), - style: TextStyle( - fontSize: 12, - fontWeight: FontWeight.bold, - color: event.sender.calcDisplayname().color, - ), - ), + : FutureBuilder( + future: event.fetchSenderUser(), + builder: (context, snapshot) { + final displayname = + snapshot.data?.calcDisplayname() ?? + event.senderFromMemoryOrFallback + .calcDisplayname(); + return Text( + displayname, + style: TextStyle( + fontSize: 12, + fontWeight: FontWeight.bold, + color: displayname.color, + ), + ); + }), ), Container( alignment: alignment, diff --git a/lib/pages/chat/events/message_content.dart b/lib/pages/chat/events/message_content.dart index 2c6597ba..59e4d649 100644 --- a/lib/pages/chat/events/message_content.dart +++ b/lib/pages/chat/events/message_content.dart @@ -34,7 +34,7 @@ class MessageContent extends StatelessWidget { content: Text( event.type == EventTypes.Encrypted ? L10n.of(context)!.needPantalaimonWarning - : event.getLocalizedBody( + : event.calcLocalizedBodyFallback( MatrixLocals(L10n.of(context)!), ), ))); @@ -172,48 +172,73 @@ class MessageContent extends StatelessWidget { textmessage: default: if (event.redacted) { - return _ButtonContent( - label: L10n.of(context)! - .redactedAnEvent(event.sender.calcDisplayname()), - icon: const Icon(Icons.delete_outlined), - textColor: buttonTextColor, - onPressed: () => onInfoTab!(event), - ); + return FutureBuilder( + future: event.fetchSenderUser(), + builder: (context, snapshot) { + return _ButtonContent( + label: L10n.of(context)!.redactedAnEvent(snapshot.data + ?.calcDisplayname() ?? + event.senderFromMemoryOrFallback.calcDisplayname()), + icon: const Icon(Icons.delete_outlined), + textColor: buttonTextColor, + onPressed: () => onInfoTab!(event), + ); + }); } final bigEmotes = event.onlyEmotes && event.numberEmotes > 0 && event.numberEmotes <= 10; - return LinkText( - text: event.getLocalizedBody(MatrixLocals(L10n.of(context)!), - hideReply: true), - textStyle: TextStyle( - color: textColor, - fontSize: bigEmotes ? fontSize * 3 : fontSize, - decoration: event.redacted ? TextDecoration.lineThrough : null, - ), - linkStyle: TextStyle( - color: textColor.withAlpha(150), - fontSize: bigEmotes ? fontSize * 3 : fontSize, - decoration: TextDecoration.underline, - ), - onLinkTap: (url) => UrlLauncher(context, url).launchUrl(), - ); + return FutureBuilder( + future: event.calcLocalizedBody(MatrixLocals(L10n.of(context)!), + hideReply: true), + builder: (context, snapshot) { + return LinkText( + text: snapshot.data ?? + event.calcLocalizedBodyFallback( + MatrixLocals(L10n.of(context)!), + hideReply: true), + textStyle: TextStyle( + color: textColor, + fontSize: bigEmotes ? fontSize * 3 : fontSize, + decoration: + event.redacted ? TextDecoration.lineThrough : null, + ), + linkStyle: TextStyle( + color: textColor.withAlpha(150), + fontSize: bigEmotes ? fontSize * 3 : fontSize, + decoration: TextDecoration.underline, + ), + onLinkTap: (url) => UrlLauncher(context, url).launchUrl(), + ); + }); } case EventTypes.CallInvite: - return _ButtonContent( - label: L10n.of(context)!.startedACall(event.sender.calcDisplayname()), - icon: const Icon(Icons.phone_outlined), - textColor: buttonTextColor, - onPressed: () => onInfoTab!(event), - ); + return FutureBuilder( + future: event.fetchSenderUser(), + builder: (context, snapshot) { + return _ButtonContent( + label: L10n.of(context)!.startedACall( + snapshot.data?.calcDisplayname() ?? + event.senderFromMemoryOrFallback.calcDisplayname()), + icon: const Icon(Icons.phone_outlined), + textColor: buttonTextColor, + onPressed: () => onInfoTab!(event), + ); + }); default: - return _ButtonContent( - label: L10n.of(context)! - .userSentUnknownEvent(event.sender.calcDisplayname(), event.type), - icon: const Icon(Icons.info_outlined), - textColor: buttonTextColor, - onPressed: () => onInfoTab!(event), - ); + return FutureBuilder( + future: event.fetchSenderUser(), + builder: (context, snapshot) { + return _ButtonContent( + label: L10n.of(context)!.userSentUnknownEvent( + snapshot.data?.calcDisplayname() ?? + event.senderFromMemoryOrFallback.calcDisplayname(), + event.type), + icon: const Icon(Icons.info_outlined), + textColor: buttonTextColor, + onPressed: () => onInfoTab!(event), + ); + }); } } } diff --git a/lib/pages/chat/events/message_reactions.dart b/lib/pages/chat/events/message_reactions.dart index 8e4b784c..023f757d 100644 --- a/lib/pages/chat/events/message_reactions.dart +++ b/lib/pages/chat/events/message_reactions.dart @@ -39,7 +39,7 @@ class MessageReactions extends StatelessWidget { ); } reactionMap[key]!.count++; - reactionMap[key]!.reactors!.add(e.sender); + reactionMap[key]!.reactors!.add(e.senderFromMemoryOrFallback); reactionMap[key]!.reacted |= e.senderId == e.room.client.userID; } } diff --git a/lib/pages/chat/events/reply_content.dart b/lib/pages/chat/events/reply_content.dart index 65cf3e97..cde75eb1 100644 --- a/lib/pages/chat/events/reply_content.dart +++ b/lib/pages/chat/events/reply_content.dart @@ -52,7 +52,7 @@ class ReplyContent extends StatelessWidget { ); } else { replyBody = Text( - displayEvent.getLocalizedBody( + displayEvent.calcLocalizedBodyFallback( MatrixLocals(L10n.of(context)!), withSenderNamePrefix: false, hideReply: true, @@ -83,18 +83,25 @@ class ReplyContent extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.center, children: [ - Text( - displayEvent.sender.calcDisplayname() + ':', - maxLines: 1, - overflow: TextOverflow.ellipsis, - style: TextStyle( - fontWeight: FontWeight.bold, - color: ownMessage - ? Theme.of(context).colorScheme.onPrimary - : Theme.of(context).colorScheme.onBackground, - fontSize: fontSize, - ), - ), + FutureBuilder( + future: displayEvent.fetchSenderUser(), + builder: (context, snapshot) { + return Text( + (snapshot.data?.calcDisplayname() ?? + displayEvent.senderFromMemoryOrFallback + .calcDisplayname()) + + ':', + maxLines: 1, + overflow: TextOverflow.ellipsis, + style: TextStyle( + fontWeight: FontWeight.bold, + color: ownMessage + ? Theme.of(context).colorScheme.onPrimary + : Theme.of(context).colorScheme.onBackground, + fontSize: fontSize, + ), + ); + }), replyBody, ], ), diff --git a/lib/pages/chat/events/state_message.dart b/lib/pages/chat/events/state_message.dart index c10537ad..6e270ae6 100644 --- a/lib/pages/chat/events/state_message.dart +++ b/lib/pages/chat/events/state_message.dart @@ -39,16 +39,24 @@ class StateMessage extends StatelessWidget { child: Column( mainAxisSize: MainAxisSize.min, children: [ - Text( - event.getLocalizedBody(MatrixLocals(L10n.of(context)!)), - textAlign: TextAlign.center, - style: TextStyle( - fontSize: 14 * AppConfig.fontSizeFactor, - color: Theme.of(context).textTheme.bodyText2!.color, - decoration: - event.redacted ? TextDecoration.lineThrough : null, - ), - ), + FutureBuilder( + future: event + .calcLocalizedBody(MatrixLocals(L10n.of(context)!)), + builder: (context, snapshot) { + return Text( + snapshot.data ?? + event.calcLocalizedBodyFallback( + MatrixLocals(L10n.of(context)!)), + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 14 * AppConfig.fontSizeFactor, + color: Theme.of(context).textTheme.bodyText2!.color, + decoration: event.redacted + ? TextDecoration.lineThrough + : null, + ), + ); + }), if (counter != 0) Text( L10n.of(context)!.moreEvents(counter), diff --git a/lib/pages/chat/pinned_events.dart b/lib/pages/chat/pinned_events.dart index c72ff1cc..856d95ac 100644 --- a/lib/pages/chat/pinned_events.dart +++ b/lib/pages/chat/pinned_events.dart @@ -26,7 +26,7 @@ class PinnedEvents extends StatelessWidget { actions: events .map((event) => SheetAction( key: event?.eventId ?? '', - label: event?.getLocalizedBody( + label: event?.calcLocalizedBodyFallback( MatrixLocals(L10n.of(context)!), withSenderNamePrefix: true, hideReply: true, @@ -90,32 +90,41 @@ class PinnedEvents extends StatelessWidget { Expanded( child: Padding( padding: const EdgeInsets.symmetric(horizontal: 4.0), - child: LinkText( - text: event.getLocalizedBody( - MatrixLocals(L10n.of(context)!), - withSenderNamePrefix: true, - hideReply: true, - ), - maxLines: 2, - textStyle: TextStyle( - overflow: TextOverflow.ellipsis, - fontSize: fontSize, - decoration: event.redacted - ? TextDecoration.lineThrough - : null, - ), - linkStyle: TextStyle( - color: Theme.of(context) - .textTheme - .bodyText1 - ?.color - ?.withAlpha(150), - fontSize: fontSize, - decoration: TextDecoration.underline, - ), - onLinkTap: (url) => - UrlLauncher(context, url).launchUrl(), - ), + child: FutureBuilder( + future: event.calcLocalizedBody( + MatrixLocals(L10n.of(context)!), + withSenderNamePrefix: true, + hideReply: true, + ), + builder: (context, snapshot) { + return LinkText( + text: snapshot.data ?? + event.calcLocalizedBodyFallback( + MatrixLocals(L10n.of(context)!), + withSenderNamePrefix: true, + hideReply: true, + ), + maxLines: 2, + textStyle: TextStyle( + overflow: TextOverflow.ellipsis, + fontSize: fontSize, + decoration: event.redacted + ? TextDecoration.lineThrough + : null, + ), + linkStyle: TextStyle( + color: Theme.of(context) + .textTheme + .bodyText1 + ?.color + ?.withAlpha(150), + fontSize: fontSize, + decoration: TextDecoration.underline, + ), + onLinkTap: (url) => + UrlLauncher(context, url).launchUrl(), + ); + }), ), ), ], diff --git a/lib/pages/chat/reply_display.dart b/lib/pages/chat/reply_display.dart index 5185d1db..ccb6e6a7 100644 --- a/lib/pages/chat/reply_display.dart +++ b/lib/pages/chat/reply_display.dart @@ -50,6 +50,7 @@ class _EditContent extends StatelessWidget { @override Widget build(BuildContext context) { + final event = this.event; if (event == null) { return Container(); } @@ -60,19 +61,27 @@ class _EditContent extends StatelessWidget { color: Theme.of(context).primaryColor, ), Container(width: 15.0), - Text( - event?.getLocalizedBody( - MatrixLocals(L10n.of(context)!), - withSenderNamePrefix: false, - hideReply: true, - ) ?? - '', - overflow: TextOverflow.ellipsis, - maxLines: 1, - style: TextStyle( - color: Theme.of(context).textTheme.bodyText2!.color, - ), - ), + FutureBuilder( + future: event.calcLocalizedBody( + MatrixLocals(L10n.of(context)!), + withSenderNamePrefix: false, + hideReply: true, + ), + builder: (context, snapshot) { + return Text( + snapshot.data ?? + event.calcLocalizedBodyFallback( + MatrixLocals(L10n.of(context)!), + withSenderNamePrefix: false, + hideReply: true, + ), + overflow: TextOverflow.ellipsis, + maxLines: 1, + style: TextStyle( + color: Theme.of(context).textTheme.bodyText2!.color, + ), + ); + }), ], ); } diff --git a/lib/pages/chat_encryption_settings/chat_encryption_settings_view.dart b/lib/pages/chat_encryption_settings/chat_encryption_settings_view.dart index 8da5698a..581bff85 100644 --- a/lib/pages/chat_encryption_settings/chat_encryption_settings_view.dart +++ b/lib/pages/chat_encryption_settings/chat_encryption_settings_view.dart @@ -94,15 +94,18 @@ class ChatEncryptionSettingsView extends StatelessWidget { child: ListTile( leading: Avatar( mxContent: room - .getUserByMXIDSync(deviceKeys[i].userId) + .unsafeGetUserFromMemoryOrFallback( + deviceKeys[i].userId) .avatarUrl, name: room - .getUserByMXIDSync(deviceKeys[i].userId) + .unsafeGetUserFromMemoryOrFallback( + deviceKeys[i].userId) .calcDisplayname(), ), title: Text( room - .getUserByMXIDSync(deviceKeys[i].userId) + .unsafeGetUserFromMemoryOrFallback( + deviceKeys[i].userId) .calcDisplayname(), ), subtitle: Text( diff --git a/lib/pages/chat_list/chat_list_item.dart b/lib/pages/chat_list/chat_list_item.dart index ebf6eb89..9affa6dc 100644 --- a/lib/pages/chat_list/chat_list_item.dart +++ b/lib/pages/chat_list/chat_list_item.dart @@ -262,32 +262,47 @@ class ChatListItem extends StatelessWidget { ), softWrap: false, ) - : Text( - room.membership == Membership.invite - ? L10n.of(context)!.youAreInvitedToThisChat - : room.lastEvent?.getLocalizedBody( - MatrixLocals(L10n.of(context)!), - hideReply: true, - hideEdit: true, - plaintextBody: true, - removeMarkdown: true, - withSenderNamePrefix: !room.isDirectChat || - room.directChatMatrixID != - room.lastEvent?.senderId, - ) ?? - L10n.of(context)!.emptyChat, - softWrap: false, - maxLines: 1, - overflow: TextOverflow.ellipsis, - style: TextStyle( - color: unread - ? Theme.of(context).colorScheme.secondary - : Theme.of(context).textTheme.bodyText2!.color, - decoration: room.lastEvent?.redacted == true - ? TextDecoration.lineThrough - : null, - ), - ), + : FutureBuilder( + future: room.lastEvent?.calcLocalizedBody( + MatrixLocals(L10n.of(context)!), + hideReply: true, + hideEdit: true, + plaintextBody: true, + removeMarkdown: true, + withSenderNamePrefix: !room.isDirectChat || + room.directChatMatrixID != + room.lastEvent?.senderId, + ) ?? + Future.value(L10n.of(context)!.emptyChat), + builder: (context, snapshot) { + return Text( + room.membership == Membership.invite + ? L10n.of(context)!.youAreInvitedToThisChat + : snapshot.data ?? + room.lastEvent?.calcLocalizedBodyFallback( + MatrixLocals(L10n.of(context)!), + hideReply: true, + hideEdit: true, + plaintextBody: true, + removeMarkdown: true, + withSenderNamePrefix: !room.isDirectChat || + room.directChatMatrixID != + room.lastEvent?.senderId, + ) ?? + L10n.of(context)!.emptyChat, + softWrap: false, + maxLines: 1, + overflow: TextOverflow.ellipsis, + style: TextStyle( + color: unread + ? Theme.of(context).colorScheme.secondary + : Theme.of(context).textTheme.bodyText2!.color, + decoration: room.lastEvent?.redacted == true + ? TextDecoration.lineThrough + : null, + ), + ); + }), ), const SizedBox(width: 8), AnimatedContainer( diff --git a/lib/pages/chat_list/client_chooser_button.dart b/lib/pages/chat_list/client_chooser_button.dart index cdd844a6..3c9a4b2f 100644 --- a/lib/pages/chat_list/client_chooser_button.dart +++ b/lib/pages/chat_list/client_chooser_button.dart @@ -48,7 +48,7 @@ class ClientChooserButton extends StatelessWidget { (client) => PopupMenuItem( value: client, child: FutureBuilder( - future: client!.ownProfile, + future: client!.fetchOwnProfile(), builder: (context, snapshot) => Row( children: [ Avatar( @@ -90,7 +90,7 @@ class ClientChooserButton extends StatelessWidget { matrix.accountBundles.forEach((key, value) => clientCount += value.length); return Center( child: FutureBuilder( - future: matrix.client.ownProfile, + future: matrix.client.fetchOwnProfile(), builder: (context, snapshot) => Stack( alignment: Alignment.center, children: [ diff --git a/lib/pages/invitation_selection/invitation_selection.dart b/lib/pages/invitation_selection/invitation_selection.dart index 3b11c79a..78972589 100644 --- a/lib/pages/invitation_selection/invitation_selection.dart +++ b/lib/pages/invitation_selection/invitation_selection.dart @@ -38,7 +38,7 @@ class InvitationSelectionController extends State { final participantsIds = participants.map((p) => p.stateKey).toList(); final contacts = client.rooms .where((r) => r.isDirectChat) - .map((r) => r.getUserByMXIDSync(r.directChatMatrixID!)) + .map((r) => r.unsafeGetUserFromMemoryOrFallback(r.directChatMatrixID!)) .toList() ..removeWhere((u) => participantsIds.contains(u.stateKey)); contacts.sort( diff --git a/lib/pages/key_verification/key_verification_dialog.dart b/lib/pages/key_verification/key_verification_dialog.dart index 53f20b7e..d9213178 100644 --- a/lib/pages/key_verification/key_verification_dialog.dart +++ b/lib/pages/key_verification/key_verification_dialog.dart @@ -111,7 +111,7 @@ class _KeyVerificationPageState extends State { if (directChatId != null) { user = widget.request.client .getRoomById(directChatId)! - .getUserByMXIDSync(widget.request.userId); + .unsafeGetUserFromMemoryOrFallback(widget.request.userId); } final displayName = user?.calcDisplayname() ?? widget.request.userId.localpart!; diff --git a/lib/pages/search/search_view.dart b/lib/pages/search/search_view.dart index c75c8fff..0bf6f95a 100644 --- a/lib/pages/search/search_view.dart +++ b/lib/pages/search/search_view.dart @@ -46,13 +46,11 @@ class SearchView extends StatelessWidget { }).then((QueryPublicRoomsResponse res) { final genericSearchTerm = controller.genericSearchTerm; if (genericSearchTerm != null && - !res.chunk.any((room) => - (room.aliases?.contains(controller.genericSearchTerm) ?? false) || - room.canonicalAlias == controller.genericSearchTerm)) { + !res.chunk.any( + (room) => room.canonicalAlias == controller.genericSearchTerm)) { // we have to tack on the original alias res.chunk.add( PublicRoomsChunk( - aliases: [genericSearchTerm], name: genericSearchTerm, numJoinedMembers: 0, roomId: '!unknown', diff --git a/lib/pages/story/story_page.dart b/lib/pages/story/story_page.dart index f784eef5..f6d200c3 100644 --- a/lib/pages/story/story_page.dart +++ b/lib/pages/story/story_page.dart @@ -370,7 +370,7 @@ class StoryPageController extends State { .client .getRoomById(roomId) ?.getState(EventTypes.RoomCreate) - ?.sender + ?.senderFromMemoryOrFallback .avatarUrl; String get title => @@ -378,7 +378,7 @@ class StoryPageController extends State { .client .getRoomById(roomId) ?.getState(EventTypes.RoomCreate) - ?.sender + ?.senderFromMemoryOrFallback .calcDisplayname() ?? 'Story not found'; @@ -485,7 +485,8 @@ class StoryPageController extends State { case PopupStoryAction.message: final roomIdResult = await showFutureLoadingDialog( context: context, - future: () => currentEvent!.sender.startDirectChat(), + future: () => + currentEvent!.senderFromMemoryOrFallback.startDirectChat(), ); if (roomIdResult.error != null) return; VRouter.of(context).toSegments(['rooms', roomIdResult.result!]); diff --git a/lib/utils/client_manager.dart b/lib/utils/client_manager.dart index 60a966da..44047d9a 100644 --- a/lib/utils/client_manager.dart +++ b/lib/utils/client_manager.dart @@ -8,10 +8,10 @@ import 'package:matrix/matrix.dart'; import 'package:path_provider/path_provider.dart'; import 'package:fluffychat/utils/custom_image_resizer.dart'; +import 'package:fluffychat/utils/matrix_sdk_extensions.dart/flutter_hive_collections_database.dart'; import 'package:fluffychat/utils/platform_infos.dart'; import 'famedlysdk_store.dart'; import 'matrix_sdk_extensions.dart/fluffybox_database.dart'; -import 'matrix_sdk_extensions.dart/flutter_matrix_hive_database.dart'; abstract class ClientManager { static const String clientNamespace = 'im.fluffychat.store.clients'; @@ -95,8 +95,8 @@ abstract class ClientManager { // To check which story room we can post in EventTypes.RoomPowerLevels, }, - databaseBuilder: FlutterFluffyBoxDatabase.databaseBuilder, - legacyDatabaseBuilder: FlutterMatrixHiveStore.hiveDatabaseBuilder, + databaseBuilder: FlutterHiveCollectionsDatabase.databaseBuilder, + legacyDatabaseBuilder: FlutterFluffyBoxDatabase.databaseBuilder, supportedLoginTypes: { AuthenticationTypes.password, if (PlatformInfos.isMobile || diff --git a/lib/utils/matrix_sdk_extensions.dart/client_stories_extension.dart b/lib/utils/matrix_sdk_extensions.dart/client_stories_extension.dart index f2e1627a..269de73c 100644 --- a/lib/utils/matrix_sdk_extensions.dart/client_stories_extension.dart +++ b/lib/utils/matrix_sdk_extensions.dart/client_stories_extension.dart @@ -12,7 +12,8 @@ extension ClientStoriesExtension on Client { List get contacts => rooms .where((room) => room.isDirectChat) - .map((room) => room.getUserByMXIDSync(room.directChatMatrixID!)) + .map((room) => + room.unsafeGetUserFromMemoryOrFallback(room.directChatMatrixID!)) .toList(); List get storiesRooms => rooms diff --git a/lib/utils/matrix_sdk_extensions.dart/fluffybox_database.dart b/lib/utils/matrix_sdk_extensions.dart/fluffybox_database.dart index ef7bd00a..c2e938fd 100644 --- a/lib/utils/matrix_sdk_extensions.dart/fluffybox_database.dart +++ b/lib/utils/matrix_sdk_extensions.dart/fluffybox_database.dart @@ -14,6 +14,7 @@ import 'package:path_provider/path_provider.dart'; import '../client_manager.dart'; import '../famedlysdk_store.dart'; +// ignore: deprecated_member_use class FlutterFluffyBoxDatabase extends FluffyBoxDatabase { FlutterFluffyBoxDatabase( String name, @@ -27,6 +28,7 @@ class FlutterFluffyBoxDatabase extends FluffyBoxDatabase { static const String _cipherStorageKey = 'database_encryption_key'; + // ignore: deprecated_member_use static Future databaseBuilder(Client client) async { Logs().d('Open FluffyBox...'); fluffybox.HiveAesCipher? hiverCipher; @@ -59,6 +61,7 @@ class FlutterFluffyBoxDatabase extends FluffyBoxDatabase { rethrow; } + // ignore: deprecated_member_use final db = FluffyBoxDatabase( 'fluffybox_${client.clientName.replaceAll(' ', '_').toLowerCase()}', await _findDatabasePath(client), diff --git a/lib/utils/matrix_sdk_extensions.dart/flutter_matrix_hive_database.dart b/lib/utils/matrix_sdk_extensions.dart/flutter_hive_collections_database.dart similarity index 50% rename from lib/utils/matrix_sdk_extensions.dart/flutter_matrix_hive_database.dart rename to lib/utils/matrix_sdk_extensions.dart/flutter_hive_collections_database.dart index 9879ff9c..0f775672 100644 --- a/lib/utils/matrix_sdk_extensions.dart/flutter_matrix_hive_database.dart +++ b/lib/utils/matrix_sdk_extensions.dart/flutter_hive_collections_database.dart @@ -2,78 +2,108 @@ import 'dart:convert'; import 'dart:io'; import 'dart:typed_data'; -import 'package:flutter/foundation.dart'; +import 'package:flutter/foundation.dart' hide Key; import 'package:flutter/services.dart'; import 'package:flutter_secure_storage/flutter_secure_storage.dart'; import 'package:hive/hive.dart'; -import 'package:hive_flutter/hive_flutter.dart'; import 'package:matrix/matrix.dart'; import 'package:path_provider/path_provider.dart'; -import '../platform_infos.dart'; - -class FlutterMatrixHiveStore extends FamedlySdkHiveDatabase { - FlutterMatrixHiveStore(String name, {HiveCipher? encryptionCipher}) - : super( +class FlutterHiveCollectionsDatabase extends HiveCollectionsDatabase { + FlutterHiveCollectionsDatabase( + String name, + String path, { + HiveCipher? key, + }) : super( name, - encryptionCipher: encryptionCipher, + path, + key: key, ); - static bool _hiveInitialized = false; - static const String _hiveCipherStorageKey = 'hive_encryption_key'; + static const String _cipherStorageKey = 'database_encryption_key'; - static Future hiveDatabaseBuilder( + static Future databaseBuilder( Client client) async { - if (!kIsWeb && !_hiveInitialized) { - _hiveInitialized = true; - } - HiveCipher? hiverCipher; + Logs().d('Open Hive...'); + HiveAesCipher? hiverCipher; try { // Workaround for secure storage is calling Platform.operatingSystem on web - if (kIsWeb || Platform.isLinux) throw MissingPluginException(); + if (kIsWeb) throw MissingPluginException(); const secureStorage = FlutterSecureStorage(); final containsEncryptionKey = - await secureStorage.containsKey(key: _hiveCipherStorageKey); + await secureStorage.containsKey(key: _cipherStorageKey); if (!containsEncryptionKey) { + // do not try to create a buggy secure storage for new Linux users + if (Platform.isLinux) throw MissingPluginException(); final key = Hive.generateSecureKey(); await secureStorage.write( - key: _hiveCipherStorageKey, + key: _cipherStorageKey, value: base64UrlEncode(key), ); } // workaround for if we just wrote to the key and it still doesn't exist - final rawEncryptionKey = - await secureStorage.read(key: _hiveCipherStorageKey); + final rawEncryptionKey = await secureStorage.read(key: _cipherStorageKey); if (rawEncryptionKey == null) throw MissingPluginException(); - final encryptionKey = base64Url.decode(rawEncryptionKey); - hiverCipher = HiveAesCipher(encryptionKey); + hiverCipher = HiveAesCipher(base64Url.decode(rawEncryptionKey)); } on MissingPluginException catch (_) { Logs().i('Hive encryption is not supported on this platform'); + } catch (_) { + const FlutterSecureStorage().delete(key: _cipherStorageKey); + rethrow; } - final db = FlutterMatrixHiveStore( - client.clientName, - encryptionCipher: hiverCipher, + + final db = FlutterHiveCollectionsDatabase( + 'hive_collections_${client.clientName.replaceAll(' ', '_').toLowerCase()}', + await _findDatabasePath(client), + key: hiverCipher, ); try { await db.open(); - } catch (e, s) { - Logs().e('Unable to open Hive. Delete and try again...', e, s); + } catch (_) { + Logs().w('Unable to open Hive. Delete database and storage key...'); + const FlutterSecureStorage().delete(key: _cipherStorageKey); await db.clear(); - await db.open(); + rethrow; } + Logs().d('Hive is ready'); return db; } + static Future _findDatabasePath(Client client) async { + String path = client.clientName; + if (!kIsWeb) { + Directory directory; + try { + if (Platform.isLinux) { + directory = await getApplicationSupportDirectory(); + } else { + directory = await getApplicationDocumentsDirectory(); + } + } catch (_) { + try { + directory = await getLibraryDirectory(); + } catch (_) { + directory = Directory.current; + } + } + // do not destroy your stable FluffyChat in debug mode + if (kDebugMode) { + directory = Directory(directory.uri.resolve('debug').toFilePath()); + directory.create(recursive: true); + } + path = directory.path; + } + return path; + } + @override int get maxFileSize => supportsFileStoring ? 100 * 1024 * 1024 : 0; @override - bool get supportsFileStoring => (PlatformInfos.isIOS || - PlatformInfos.isAndroid || - PlatformInfos.isDesktop); + bool get supportsFileStoring => !kIsWeb; Future _getFileStoreDirectory() async { try { diff --git a/lib/utils/push_helper.dart b/lib/utils/push_helper.dart index 335cee8c..f21544fe 100644 --- a/lib/utils/push_helper.dart +++ b/lib/utils/push_helper.dart @@ -62,7 +62,7 @@ Future pushHelper( final matrixLocals = MatrixLocals(l10n); // Calculate the body - final body = event.getLocalizedBody( + final body = await event.calcLocalizedBody( matrixLocals, plaintextBody: true, withSenderNamePrefix: !event.room.isDirectChat, diff --git a/lib/widgets/local_notifications_extension.dart b/lib/widgets/local_notifications_extension.dart index 81bc5fc4..37e8fbcf 100644 --- a/lib/widgets/local_notifications_extension.dart +++ b/lib/widgets/local_notifications_extension.dart @@ -31,7 +31,7 @@ extension LocalNotificationsExtension on MatrixState { final event = Event.fromJson(eventUpdate.content, room); final title = room.getLocalizedDisplayname(MatrixLocals(L10n.of(widget.context)!)); - final body = event.getLocalizedBody( + final body = await event.calcLocalizedBody( MatrixLocals(L10n.of(widget.context)!), withSenderNamePrefix: !room.isDirectChat || room.lastEvent?.senderId == client.userID, @@ -40,8 +40,11 @@ extension LocalNotificationsExtension on MatrixState { hideEdit: true, removeMarkdown: true, ); - final icon = event.sender.avatarUrl?.getThumbnail(client, - width: 64, height: 64, method: ThumbnailMethod.crop) ?? + final icon = event.senderFromMemoryOrFallback.avatarUrl?.getThumbnail( + client, + width: 64, + height: 64, + method: ThumbnailMethod.crop) ?? room.avatar?.getThumbnail(client, width: 64, height: 64, method: ThumbnailMethod.crop); if (kIsWeb) { diff --git a/lib/widgets/public_room_bottom_sheet.dart b/lib/widgets/public_room_bottom_sheet.dart index 1aedfe26..414702dc 100644 --- a/lib/widgets/public_room_bottom_sheet.dart +++ b/lib/widgets/public_room_bottom_sheet.dart @@ -41,9 +41,7 @@ class PublicRoomBottomSheet extends StatelessWidget { } } - bool _testRoom(PublicRoomsChunk r) => - r.canonicalAlias == roomAlias || - (r.aliases?.contains(roomAlias) ?? false); + bool _testRoom(PublicRoomsChunk r) => r.canonicalAlias == roomAlias; Future _search(BuildContext context) async { final chunk = this.chunk; diff --git a/pubspec.lock b/pubspec.lock index 6b409dfc..66fdbbbe 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -395,6 +395,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "5.0.1" + enhanced_enum: + dependency: transitive + description: + name: enhanced_enum + url: "https://pub.dartlang.org" + source: hosted + version: "0.1.2" fake_async: dependency: transitive description: @@ -788,7 +795,7 @@ packages: name: hive url: "https://pub.dartlang.org" source: hosted - version: "2.1.0" + version: "2.2.1" hive_flutter: dependency: "direct main" description: @@ -1012,21 +1019,21 @@ packages: name: matrix url: "https://pub.dartlang.org" source: hosted - version: "0.9.6" + version: "0.9.12" matrix_api_lite: dependency: transitive description: name: matrix_api_lite url: "https://pub.dartlang.org" source: hosted - version: "0.5.3" + version: "1.1.1" matrix_homeserver_recommendations: dependency: "direct main" description: name: matrix_homeserver_recommendations url: "https://pub.dartlang.org" source: hosted - version: "0.2.0" + version: "0.2.1" matrix_link_text: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index e2f8c479..4dc1542d 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -57,7 +57,7 @@ dependencies: keyboard_shortcuts: ^0.1.4 localstorage: ^4.0.0+1 lottie: ^1.2.2 - matrix: ^0.9.4 + matrix: ^0.9.12 matrix_homeserver_recommendations: ^0.2.0 matrix_link_text: ^1.0.2 native_imaging: diff --git a/test/utils/test_client.dart b/test/utils/test_client.dart index 7cbf2339..ac9bacf4 100644 --- a/test/utils/test_client.dart +++ b/test/utils/test_client.dart @@ -2,7 +2,7 @@ import 'package:matrix/encryption/utils/key_verification.dart'; import 'package:matrix/matrix.dart'; import 'package:matrix_api_lite/fake_matrix_api.dart'; -import 'package:fluffychat/utils/matrix_sdk_extensions.dart/flutter_matrix_hive_database.dart'; +import 'package:fluffychat/utils/matrix_sdk_extensions.dart/flutter_hive_collections_database.dart'; Future prepareTestClient({ bool loggedIn = false, @@ -20,7 +20,7 @@ Future prepareTestClient({ importantStateEvents: { 'im.ponies.room_emotes', // we want emotes to work properly }, - databaseBuilder: FlutterMatrixHiveStore.hiveDatabaseBuilder, + databaseBuilder: FlutterHiveCollectionsDatabase.databaseBuilder, supportedLoginTypes: { AuthenticationTypes.password, AuthenticationTypes.sso