From 1d20ba02dbc255cc59abe131f9f2091d3f225516 Mon Sep 17 00:00:00 2001 From: ggurdin Date: Wed, 25 Jun 2025 15:05:38 -0400 Subject: [PATCH 1/8] chore: don't expand delete space dialog if empty --- lib/pangea/chat_settings/widgets/delete_space_dialog.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pangea/chat_settings/widgets/delete_space_dialog.dart b/lib/pangea/chat_settings/widgets/delete_space_dialog.dart index 6afb8ab26..4adce8e62 100644 --- a/lib/pangea/chat_settings/widgets/delete_space_dialog.dart +++ b/lib/pangea/chat_settings/widgets/delete_space_dialog.dart @@ -146,7 +146,7 @@ class DeleteSpaceDialogState extends State { style: TextStyle(color: Theme.of(context).colorScheme.error), ), ), - Expanded( + Flexible( child: SingleChildScrollView( child: Builder( builder: (context) { From bb99cd9d6124681abcdba71889ee0df7516be2c1 Mon Sep 17 00:00:00 2001 From: ggurdin Date: Wed, 25 Jun 2025 15:26:00 -0400 Subject: [PATCH 2/8] chore: fix duplicate key error --- lib/pangea/toolbar/widgets/overlay_center_content.dart | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/pangea/toolbar/widgets/overlay_center_content.dart b/lib/pangea/toolbar/widgets/overlay_center_content.dart index c4d2810d9..6b5396b65 100644 --- a/lib/pangea/toolbar/widgets/overlay_center_content.dart +++ b/lib/pangea/toolbar/widgets/overlay_center_content.dart @@ -70,9 +70,11 @@ class OverlayCenterContent extends StatelessWidget { MeasureRenderBox( onChange: onChangeMessageSize, child: OverlayMessage( - key: MatrixState.pAnyState - .layerLinkAndKey('overlay_message_${event.eventId}') - .key, + key: isTransitionAnimation + ? MatrixState.pAnyState + .layerLinkAndKey('overlay_message_${event.eventId}') + .key + : null, event, pangeaMessageEvent: pangeaMessageEvent, immersionMode: chatController.choreographer.immersionMode, From f1387eb662a543dbf85566f2a4a0d74a73ae59f6 Mon Sep 17 00:00:00 2001 From: ggurdin Date: Wed, 25 Jun 2025 16:01:17 -0400 Subject: [PATCH 3/8] chore: remove wait for sync when updating learning settings --- lib/pangea/learning_settings/pages/settings_learning.dart | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/pangea/learning_settings/pages/settings_learning.dart b/lib/pangea/learning_settings/pages/settings_learning.dart index 76e4fd764..340b79597 100644 --- a/lib/pangea/learning_settings/pages/settings_learning.dart +++ b/lib/pangea/learning_settings/pages/settings_learning.dart @@ -127,7 +127,6 @@ class SettingsLearningController extends State { context: context, future: () async => pangeaController.userController.updateProfile( (_) => _profile, - waitForDataInSync: true, ), ); Navigator.of(context).pop(); From 0f2a02447f57617dfd6067e0d1f8a8278240baa0 Mon Sep 17 00:00:00 2001 From: ggurdin Date: Wed, 25 Jun 2025 16:42:05 -0400 Subject: [PATCH 4/8] chore: if user is in trial window, always treat them as subscribed --- .../subscription/controllers/subscription_controller.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pangea/subscription/controllers/subscription_controller.dart b/lib/pangea/subscription/controllers/subscription_controller.dart index b3fb0e991..91027f88c 100644 --- a/lib/pangea/subscription/controllers/subscription_controller.dart +++ b/lib/pangea/subscription/controllers/subscription_controller.dart @@ -58,7 +58,7 @@ class SubscriptionController extends BaseController { final bool hasSubscription = currentSubscriptionInfo?.currentSubscriptionId != null; - return hasSubscription; + return hasSubscription || _userController.inTrialWindow(); } bool _isInitializing = false; From c545ddf02c10d14579157a284977e0b3ddf84a44 Mon Sep 17 00:00:00 2001 From: ggurdin Date: Wed, 25 Jun 2025 17:30:06 -0400 Subject: [PATCH 5/8] chore: on click space in nav rail, if invited, show invite popup --- lib/pages/chat_list/chat_list.dart | 1 - .../utils/chat_list_handle_space_tap.dart | 20 +++++++------------ lib/widgets/navigation_rail.dart | 16 ++++++++++++++- 3 files changed, 22 insertions(+), 15 deletions(-) diff --git a/lib/pages/chat_list/chat_list.dart b/lib/pages/chat_list/chat_list.dart index eccc19960..19006d83b 100644 --- a/lib/pages/chat_list/chat_list.dart +++ b/lib/pages/chat_list/chat_list.dart @@ -587,7 +587,6 @@ class ChatListController extends State if (space != null) { chatListHandleSpaceTap( context, - this, space, ); } diff --git a/lib/pangea/chat_list/utils/chat_list_handle_space_tap.dart b/lib/pangea/chat_list/utils/chat_list_handle_space_tap.dart index 4950c40c3..549d39440 100644 --- a/lib/pangea/chat_list/utils/chat_list_handle_space_tap.dart +++ b/lib/pangea/chat_list/utils/chat_list_handle_space_tap.dart @@ -3,9 +3,7 @@ import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; import 'package:matrix/matrix.dart'; -import 'package:fluffychat/config/themes.dart'; import 'package:fluffychat/l10n/l10n.dart'; -import 'package:fluffychat/pages/chat_list/chat_list.dart'; import 'package:fluffychat/pangea/extensions/pangea_room_extension.dart'; import 'package:fluffychat/widgets/adaptive_dialogs/show_ok_cancel_alert_dialog.dart'; import 'package:fluffychat/widgets/future_loading_dialog.dart'; @@ -23,7 +21,7 @@ Future _showInviteDialog(Room room, BuildContext context) async { cancelLabel: L10n.of(context).decline, ); - await showFutureLoadingDialog( + final resp = await showFutureLoadingDialog( context: context, future: () async { if (acceptInvite == OkCancelResult.ok) { @@ -31,28 +29,24 @@ Future _showInviteDialog(Room room, BuildContext context) async { context.go( room.isSpace ? "/rooms?spaceId=${room.id}" : "/rooms/${room.id}", ); - return; + return room.id; } await room.leave(); }, ); + + if (!resp.isError && resp.result is String) { + context.go("/rooms?spaceId=${resp.result}"); + } } // ignore: curly_braces_in_flow_control_structures void chatListHandleSpaceTap( BuildContext context, - ChatListController controller, Room space, ) { void setActiveSpaceAndCloseChat() { - controller.setActiveSpace(space.id); - - if (FluffyThemes.isColumnMode(context)) { - context.go('/rooms/${space.id}'); - } else if (controller.activeChat != null && - !space.isFirstOrSecondChild(controller.activeChat!)) { - context.go("/rooms"); - } + context.go("/rooms?spaceId=${space.id}"); } void autoJoin(Room space) { diff --git a/lib/widgets/navigation_rail.dart b/lib/widgets/navigation_rail.dart index ab0983345..ac14abdb9 100644 --- a/lib/widgets/navigation_rail.dart +++ b/lib/widgets/navigation_rail.dart @@ -7,6 +7,7 @@ import 'package:fluffychat/config/app_config.dart'; import 'package:fluffychat/config/themes.dart'; import 'package:fluffychat/l10n/l10n.dart'; import 'package:fluffychat/pages/chat_list/navi_rail_item.dart'; +import 'package:fluffychat/pangea/chat_list/utils/chat_list_handle_space_tap.dart'; import 'package:fluffychat/utils/matrix_sdk_extensions/matrix_locals.dart'; import 'package:fluffychat/utils/stream_extension.dart'; import 'package:fluffychat/widgets/avatar.dart'; @@ -175,7 +176,20 @@ class SpacesNavigationRail extends StatelessWidget { return NaviRailItem( toolTip: displayname, isSelected: activeSpaceId == space.id, - onTap: () => onGoToSpaceId(rootSpaces[i].id), + // #Pangea + // onTap: () => onGoToSpaceId(rootSpaces[i].id), + onTap: () { + final room = client.getRoomById(rootSpaces[i].id); + if (room != null) { + chatListHandleSpaceTap( + context, + room, + ); + } else { + onGoToSpaceId(rootSpaces[i].id); + } + }, + // Pangea# unreadBadgeFilter: (room) => spaceChildrenIds.contains(room.id), icon: Avatar( From 39b9aac80ea6b7ff8386a3ed7146ab5d6c582235 Mon Sep 17 00:00:00 2001 From: ggurdin Date: Thu, 26 Jun 2025 09:29:00 -0400 Subject: [PATCH 6/8] chore: replace call to startRoom with call to startDirectChat for bot chat --- lib/pangea/onboarding/onboarding.dart | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/lib/pangea/onboarding/onboarding.dart b/lib/pangea/onboarding/onboarding.dart index bf2f0979c..53d45bd87 100644 --- a/lib/pangea/onboarding/onboarding.dart +++ b/lib/pangea/onboarding/onboarding.dart @@ -80,17 +80,16 @@ class OnboardingController extends State { Future startChatWithBot() async { final resp = await showFutureLoadingDialog( context: context, - future: () => Matrix.of(context).client.createRoom( - invite: [BotName.byEnvironment], - isDirect: true, - preset: CreateRoomPreset.trustedPrivateChat, - initialState: [ - BotOptionsModel(mode: BotMode.directChat).toStateEvent, - RoomDefaults.defaultPowerLevels( - Matrix.of(context).client.userID!, - ), - ], + future: () => Matrix.of(context).client.startDirectChat( + BotName.byEnvironment, + preset: CreateRoomPreset.trustedPrivateChat, + initialState: [ + BotOptionsModel(mode: BotMode.directChat).toStateEvent, + RoomDefaults.defaultPowerLevels( + Matrix.of(context).client.userID!, ), + ], + ), ); if (resp.isError) return; context.go("/rooms/${resp.result}"); From f3281d87d2b9a341a2977c48e077ce90605a6688 Mon Sep 17 00:00:00 2001 From: ggurdin Date: Thu, 26 Jun 2025 11:25:38 -0400 Subject: [PATCH 7/8] chore: if originalSent doesn't have tokens, don't include it in representations list because related token events cannot be sent to it --- .../event_wrappers/pangea_message_event.dart | 10 ++++++- .../widgets/message_selection_overlay.dart | 26 ++++--------------- 2 files changed, 14 insertions(+), 22 deletions(-) diff --git a/lib/pangea/events/event_wrappers/pangea_message_event.dart b/lib/pangea/events/event_wrappers/pangea_message_event.dart index 0f01f2655..d0fff2281 100644 --- a/lib/pangea/events/event_wrappers/pangea_message_event.dart +++ b/lib/pangea/events/event_wrappers/pangea_message_event.dart @@ -396,7 +396,15 @@ class PangeaMessageEvent { ), ); } - _representations!.add(sent); + + // If originalSent has no tokens, there is not way to generate a tokens event + // and send it as a related event, since original sent has not eventID to set + // as parentEventId. In this case, it's better to generate a new representation + // with an eventID and send the related tokens event to that representation. + // This is a rare situation, and has only been seen with some bot messages. + if (sent.tokens != null) { + _representations!.add(sent); + } } catch (err, s) { ErrorHandler.logError( m: "error parsing originalSent", diff --git a/lib/pangea/toolbar/widgets/message_selection_overlay.dart b/lib/pangea/toolbar/widgets/message_selection_overlay.dart index 14da26467..d95fee56a 100644 --- a/lib/pangea/toolbar/widgets/message_selection_overlay.dart +++ b/lib/pangea/toolbar/widgets/message_selection_overlay.dart @@ -18,8 +18,6 @@ import 'package:fluffychat/pangea/events/event_wrappers/pangea_message_event.dar import 'package:fluffychat/pangea/events/event_wrappers/pangea_representation_event.dart'; import 'package:fluffychat/pangea/events/models/pangea_token_model.dart'; import 'package:fluffychat/pangea/events/models/pangea_token_text_model.dart'; -import 'package:fluffychat/pangea/events/models/tokens_event_content_model.dart'; -import 'package:fluffychat/pangea/extensions/pangea_room_extension.dart'; import 'package:fluffychat/pangea/lemmas/lemma_info_response.dart'; import 'package:fluffychat/pangea/practice_activities/activity_type_enum.dart'; import 'package:fluffychat/pangea/practice_activities/practice_activity_model.dart'; @@ -135,7 +133,11 @@ class MessageOverlayController extends State RepresentationEvent? repEvent = pangeaMessageEvent?.messageDisplayRepresentation; - repEvent ??= await _fetchNewRepEvent(); + + if (repEvent == null || + (repEvent.event == null && repEvent.tokens == null)) { + repEvent = await _fetchNewRepEvent(); + } if (repEvent?.event != null) { await repEvent!.sendTokensEvent( @@ -145,24 +147,6 @@ class MessageOverlayController extends State MatrixState.pangeaController.languageController.userL2!.langCode, ); } - // If repEvent is originalSent but it's missing tokens, then fetch tokens. - // An edge case, but has happened with some bot message. - else if (repEvent != null && - repEvent.tokens == null && - repEvent.content.originalSent) { - final tokens = await repEvent.tokensGlobal( - pangeaMessageEvent!.senderId, - pangeaMessageEvent!.event.originServerTs, - ); - await pangeaMessageEvent!.room.pangeaSendTextEvent( - pangeaMessageEvent!.messageDisplayText, - editEventId: pangeaMessageEvent!.eventId, - originalSent: pangeaMessageEvent!.originalSent?.content, - originalWritten: pangeaMessageEvent!.originalWritten?.content, - tokensSent: PangeaMessageTokens(tokens: tokens), - choreo: pangeaMessageEvent!.originalSent?.choreo, - ); - } // Get all the lemma infos final messageVocabConstructIds = pangeaMessageEvent! From e2cd920668d72e40e18042ea7da5d180fdba36b7 Mon Sep 17 00:00:00 2001 From: ggurdin Date: Thu, 26 Jun 2025 11:50:08 -0400 Subject: [PATCH 8/8] chore: fix audio player ID discrepancy --- lib/pangea/toolbar/widgets/select_mode_buttons.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pangea/toolbar/widgets/select_mode_buttons.dart b/lib/pangea/toolbar/widgets/select_mode_buttons.dart index 43759a1a1..238fec5f9 100644 --- a/lib/pangea/toolbar/widgets/select_mode_buttons.dart +++ b/lib/pangea/toolbar/widgets/select_mode_buttons.dart @@ -240,7 +240,7 @@ class SelectModeButtonsState extends State { matrix?.audioPlayer?.dispose(); matrix?.audioPlayer = AudioPlayer(); matrix?.voiceMessageEventId.value = - widget.overlayController.pangeaMessageEvent?.eventId; + "${widget.overlayController.pangeaMessageEvent?.eventId}_button"; _onPlayerStateChanged = matrix?.audioPlayer?.playerStateStream.listen((state) {