diff --git a/lib/pages/chat_list/chat_list.dart b/lib/pages/chat_list/chat_list.dart index eccc19960..091c6e8b0 100644 --- a/lib/pages/chat_list/chat_list.dart +++ b/lib/pages/chat_list/chat_list.dart @@ -46,6 +46,7 @@ import '../../widgets/matrix.dart'; import 'package:fluffychat/utils/tor_stub.dart' if (dart.library.html) 'package:tor_detector_web/tor_detector_web.dart'; + enum PopupMenuAction { settings, invite, @@ -587,7 +588,6 @@ class ChatListController extends State if (space != null) { chatListHandleSpaceTap( context, - this, space, ); } @@ -669,6 +669,10 @@ class ChatListController extends State _activeSpaceId = widget.activeSpaceId == 'clear' ? null : widget.activeSpaceId; + + WidgetsBinding.instance.addPostFrameCallback((_) { + _joinInvitedSpaces(); + }); // Pangea# super.initState(); @@ -685,6 +689,16 @@ class ChatListController extends State : setActiveSpace(widget.activeSpaceId!); } } + + Future _joinInvitedSpaces() async { + final invitedSpaces = Matrix.of(context).client.rooms.where( + (r) => r.isSpace && r.membership == Membership.invite, + ); + + for (final space in invitedSpaces) { + await showInviteDialog(space, context); + } + } // Pangea# @override diff --git a/lib/pages/settings/settings_view.dart b/lib/pages/settings/settings_view.dart index 81f488f2e..c273109c9 100644 --- a/lib/pages/settings/settings_view.dart +++ b/lib/pages/settings/settings_view.dart @@ -87,7 +87,14 @@ class SettingsView extends StatelessWidget { return Row( children: [ Padding( - padding: const EdgeInsets.all(32.0), + // #Pangea + // padding: const EdgeInsets.all(32.0), + padding: const EdgeInsets.only( + top: 32.0, + bottom: 32.0, + left: 12.0, + ), + // Pangea# child: Stack( children: [ Avatar( 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..428567a27 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,16 +3,15 @@ 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'; import 'package:fluffychat/widgets/matrix.dart'; import '../../common/utils/error_handler.dart'; -Future _showInviteDialog(Room room, BuildContext context) async { +Future showInviteDialog(Room room, BuildContext context) async { + if (room.membership != Membership.invite) return; final acceptInvite = await showOkCancelAlertDialog( context: context, title: L10n.of(context).youreInvited, @@ -23,7 +22,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 +30,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) { @@ -85,7 +80,7 @@ void chatListHandleSpaceTap( justInputtedCode == space.classCode) { // do nothing } else { - _showInviteDialog(space, context); + showInviteDialog(space, context); } break; case Membership.leave: 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) { 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/extensions/pangea_rooms_chunk_extension.dart b/lib/pangea/extensions/pangea_rooms_chunk_extension.dart new file mode 100644 index 000000000..78785948f --- /dev/null +++ b/lib/pangea/extensions/pangea_rooms_chunk_extension.dart @@ -0,0 +1,16 @@ +import 'dart:math'; + +import 'package:matrix/matrix_api_lite/generated/model.dart'; + +import 'package:fluffychat/pangea/spaces/constants/space_constants.dart'; + +extension PangeaRoomsChunk on PublicRoomsChunk { + /// Use Random with a seed to get the default + /// avatar associated with this space + String defaultAvatar() { + final int seed = roomId.hashCode; + return SpaceConstants.publicSpaceIcons[Random(seed).nextInt( + SpaceConstants.publicSpaceIcons.length, + )]; + } +} diff --git a/lib/pangea/find_your_people/public_space_tile.dart b/lib/pangea/find_your_people/public_space_tile.dart index 7880937a0..5dd19f68f 100644 --- a/lib/pangea/find_your_people/public_space_tile.dart +++ b/lib/pangea/find_your_people/public_space_tile.dart @@ -1,10 +1,12 @@ import 'package:flutter/material.dart'; +import 'package:cached_network_image/cached_network_image.dart'; import 'package:matrix/matrix.dart'; import 'package:fluffychat/config/themes.dart'; import 'package:fluffychat/l10n/l10n.dart'; import 'package:fluffychat/pangea/analytics_summary/learning_progress_indicator_button.dart'; +import 'package:fluffychat/pangea/extensions/pangea_rooms_chunk_extension.dart'; import 'package:fluffychat/pangea/public_spaces/public_room_bottom_sheet.dart'; import 'package:fluffychat/widgets/avatar.dart'; @@ -35,14 +37,26 @@ class PublicSpaceTile extends StatelessWidget { height: isColumnMode ? 80.0 : 58.0, child: Row( children: [ - Avatar( - mxContent: space.avatarUrl, - name: space.name, - size: isColumnMode ? 80.0 : 58.0, - borderRadius: BorderRadius.circular( - 10, - ), - ), + (space.avatarUrl != null) + ? Avatar( + mxContent: space.avatarUrl, + name: space.name, + size: isColumnMode ? 80.0 : 58.0, + borderRadius: BorderRadius.circular( + 10, + ), + ) + : ClipRRect( + borderRadius: BorderRadius.circular( + 10, + ), + child: CachedNetworkImage( + imageUrl: space.defaultAvatar(), + width: isColumnMode ? 80.0 : 58.0, + height: isColumnMode ? 80.0 : 58.0, + fit: BoxFit.cover, + ), + ), Flexible( child: Padding( padding: const EdgeInsets.all(8.0), 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(); 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}"); diff --git a/lib/pangea/public_spaces/public_room_bottom_sheet.dart b/lib/pangea/public_spaces/public_room_bottom_sheet.dart index 89ed09700..a634dc39b 100644 --- a/lib/pangea/public_spaces/public_room_bottom_sheet.dart +++ b/lib/pangea/public_spaces/public_room_bottom_sheet.dart @@ -1,5 +1,3 @@ -import 'dart:math'; - import 'package:flutter/material.dart'; import 'package:cached_network_image/cached_network_image.dart'; @@ -9,7 +7,7 @@ import 'package:matrix/matrix.dart'; import 'package:fluffychat/l10n/l10n.dart'; import 'package:fluffychat/pangea/common/config/environment.dart'; -import 'package:fluffychat/pangea/spaces/constants/space_constants.dart'; +import 'package:fluffychat/pangea/extensions/pangea_rooms_chunk_extension.dart'; import 'package:fluffychat/utils/adaptive_bottom_sheet.dart'; import 'package:fluffychat/utils/fluffy_share.dart'; import 'package:fluffychat/widgets/avatar.dart'; @@ -212,10 +210,7 @@ class PublicRoomBottomSheetState extends State { : ClipRRect( borderRadius: BorderRadius.circular(24.0), child: CachedNetworkImage( - imageUrl: SpaceConstants - .publicSpaceIcons[Random().nextInt( - SpaceConstants.publicSpaceIcons.length, - )], + imageUrl: chunk!.defaultAvatar(), width: 160.0, height: 160.0, fit: BoxFit.cover, diff --git a/lib/pangea/public_spaces/public_space_card.dart b/lib/pangea/public_spaces/public_space_card.dart index 5b8063225..29521f67b 100644 --- a/lib/pangea/public_spaces/public_space_card.dart +++ b/lib/pangea/public_spaces/public_space_card.dart @@ -1,5 +1,3 @@ -import 'dart:math'; - import 'package:flutter/material.dart'; import 'package:cached_network_image/cached_network_image.dart'; @@ -7,8 +5,8 @@ import 'package:matrix/matrix.dart'; import 'package:fluffychat/l10n/l10n.dart'; import 'package:fluffychat/pangea/common/widgets/pressable_button.dart'; +import 'package:fluffychat/pangea/extensions/pangea_rooms_chunk_extension.dart'; import 'package:fluffychat/pangea/public_spaces/public_room_bottom_sheet.dart'; -import 'package:fluffychat/pangea/spaces/constants/space_constants.dart'; import 'package:fluffychat/widgets/mxc_image.dart'; class PublicSpaceCard extends StatelessWidget { @@ -71,10 +69,7 @@ class PublicSpaceCard extends StatelessWidget { fit: BoxFit.cover, ) : CachedNetworkImage( - imageUrl: SpaceConstants - .publicSpaceIcons[Random().nextInt( - SpaceConstants.publicSpaceIcons.length, - )], + imageUrl: space.defaultAvatar(), width: width, height: width, fit: BoxFit.cover, 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; 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! 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, 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) { 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(