diff --git a/commet/lib/client/matrix/matrix_client.dart b/commet/lib/client/matrix/matrix_client.dart index fba2ee803..c6c9fa950 100644 --- a/commet/lib/client/matrix/matrix_client.dart +++ b/commet/lib/client/matrix/matrix_client.dart @@ -701,16 +701,18 @@ class MatrixClient extends Client { @override Future leaveRoom(Room room) async { - _rooms.remove(room); + await _matrixClient.leaveRoom(room.identifier); + await _matrixClient.waitForRoomInSync(room.identifier); await room.close(); - return _matrixClient.leaveRoom(room.identifier); + _rooms.remove(room); } @override Future leaveSpace(Space space) async { + await _matrixClient.leaveRoom(space.identifier); + await _matrixClient.waitForRoomInSync(space.identifier); + await space.close(); _spaces.remove(space); - space.close(); - return _matrixClient.leaveRoom(space.identifier); } void onSyncStatusChanged(matrix.SyncStatusUpdate event) { diff --git a/commet/lib/client/matrix/matrix_space.dart b/commet/lib/client/matrix/matrix_space.dart index 0978853ac..ab961ff05 100644 --- a/commet/lib/client/matrix/matrix_space.dart +++ b/commet/lib/client/matrix/matrix_space.dart @@ -220,6 +220,31 @@ class MatrixSpace extends Space { var leftRoom = client.rooms[index]; if (containsRoom(leftRoom.identifier)) { _rooms.remove(leftRoom); + _onUpdate.add(null); + + // Update preview list + _matrixClient.getSpaceHierarchy(identifier, maxDepth: 1).then((value) { + var chunk = value.rooms + .where((element) => element.roomId == leftRoom.identifier) + .where((element) => + _matrixClient.getRoomById(element.roomId)?.membership != + matrix.Membership.join) + .firstOrNull; + if (chunk == null) return; + + var viaContent = _matrixRoom + .getState(matrix.EventTypes.SpaceChild, chunk.roomId) + ?.content["via"]; + + List via = const []; + + if (viaContent is List) { + via = List.from(viaContent); + } + _previews + .add(MatrixSpaceRoomChunkPreview(chunk, _matrixClient, via: via)); + }); + _fullyLoaded = true; } } diff --git a/commet/lib/client/room_preview.dart b/commet/lib/client/room_preview.dart index a58d72cab..e96a719e2 100644 --- a/commet/lib/client/room_preview.dart +++ b/commet/lib/client/room_preview.dart @@ -1,5 +1,4 @@ import 'package:commet/client/client.dart'; -import 'package:commet/client/room.dart'; import 'package:flutter/material.dart'; enum RoomPreviewType { room, space, inaccessible } diff --git a/commet/lib/ui/atoms/rich_text_field.dart b/commet/lib/ui/atoms/rich_text_field.dart index 22f637e42..306ef1a3a 100644 --- a/commet/lib/ui/atoms/rich_text_field.dart +++ b/commet/lib/ui/atoms/rich_text_field.dart @@ -6,7 +6,6 @@ import 'package:commet/client/matrix/components/emoticon/matrix_emoticon.dart'; import 'package:commet/client/matrix/components/emoticon/matrix_emoticon_component.dart'; import 'package:commet/client/matrix/components/emoticon/matrix_room_emoticon_component.dart'; import 'package:commet/client/matrix/matrix_client.dart'; -import 'package:commet/client/room.dart'; import 'package:commet/main.dart'; import 'package:commet/ui/atoms/emoji_widget.dart'; import 'package:commet/ui/atoms/mention.dart'; diff --git a/commet/lib/ui/atoms/room_preview.dart b/commet/lib/ui/atoms/room_preview.dart index 839d3326f..e3c690cfb 100644 --- a/commet/lib/ui/atoms/room_preview.dart +++ b/commet/lib/ui/atoms/room_preview.dart @@ -1,5 +1,4 @@ import 'package:commet/client/client.dart'; -import 'package:commet/client/room.dart'; import 'package:commet/client/room_preview.dart'; import 'package:flutter/material.dart'; import 'package:tiamat/tiamat.dart'; diff --git a/commet/lib/ui/atoms/room_preview_text_button.dart b/commet/lib/ui/atoms/room_preview_text_button.dart new file mode 100644 index 000000000..001a27260 --- /dev/null +++ b/commet/lib/ui/atoms/room_preview_text_button.dart @@ -0,0 +1,86 @@ +import 'package:commet/client/client.dart'; +import 'package:commet/client/room_preview.dart'; +import 'package:commet/main.dart'; +import 'package:commet/utils/common_strings.dart'; +import 'package:commet/utils/text_utils.dart'; +import 'package:flutter/material.dart'; +import 'package:tiamat/tiamat.dart' as tiamat; + +class RoomPreviewTextButton extends StatefulWidget { + const RoomPreviewTextButton( + this.room, { + this.onTap, + super.key, + }); + final RoomPreview room; + final Function(RoomPreview roomPreview)? onTap; + + @override + State createState() => _RoomPreviewTextButtonState(); +} + +class _RoomPreviewTextButtonState extends State { + @override + void initState() { + super.initState(); + } + + static const double height = 37; + + @override + Widget build(BuildContext context) { + var color = Theme.of(context).colorScheme.secondary; + + bool showRoomIcons = preferences.showRoomAvatars; + bool useGenericIcons = preferences.usePlaceholderRoomAvatars; + + String displayName = widget.room.displayName; + + Color? avatarPlaceholderColor = + (showRoomIcons && useGenericIcons && widget.room.avatar == null) || + (!showRoomIcons && useGenericIcons) + ? widget.room.color + : null; + + String? avatarPlaceholderText = + (showRoomIcons && useGenericIcons && widget.room.avatar == null) || + (!showRoomIcons && useGenericIcons) + ? widget.room.displayName + : null; + + bool startsWithEmoji = + TextUtils.isEmoji(widget.room.displayName.characters.first); + + if (startsWithEmoji && widget.room.avatar == null) { + var emoji = displayName.characters.first; + displayName = displayName.characters.skip(1).string.trim(); + avatarPlaceholderColor = Colors.transparent; + avatarPlaceholderText = emoji; + } + var customBuilder = null; + + IconData? icon = widget.room.type?.icon; + + Widget result = SizedBox( + height: customBuilder == null ? height : null, + child: tiamat.TextButton( + displayName, + customBuilder: customBuilder, + icon: icon, + avatar: showRoomIcons && widget.room.avatar != null + ? widget.room.avatar + : null, + avatarRadius: 12, + avatarPlaceholderColor: avatarPlaceholderColor, + avatarPlaceholderText: avatarPlaceholderText, + iconColor: color, + textColor: color, + softwrap: false, + onTap: () => widget.onTap?.call(widget.room), + footer: tiamat.Text.labelLow(CommonStrings.promptJoin, + color: Theme.of(context).colorScheme.primary), + )); + + return result; + } +} diff --git a/commet/lib/ui/atoms/space_list.dart b/commet/lib/ui/atoms/space_list.dart index d23bdf465..d6a0038db 100644 --- a/commet/lib/ui/atoms/space_list.dart +++ b/commet/lib/ui/atoms/space_list.dart @@ -1,9 +1,12 @@ import 'dart:async'; import 'package:commet/client/client.dart'; +import 'package:commet/client/room_preview.dart'; import 'package:commet/client/space_child.dart'; import 'package:commet/main.dart'; +import 'package:commet/ui/atoms/room_preview_text_button.dart'; import 'package:commet/ui/atoms/room_text_button.dart'; +import 'package:commet/ui/navigation/adaptive_dialog.dart'; import 'package:commet/utils/event_bus.dart'; import 'package:flutter/material.dart'; import 'package:implicitly_animated_list/implicitly_animated_list.dart'; @@ -24,6 +27,8 @@ class SpaceList extends StatefulWidget { } class _SpaceListState extends State { + late List previews; + late List children; late List subs; @@ -35,8 +40,15 @@ class _SpaceListState extends State { @override void initState() { children = widget.space.children; + previews = widget.space.childPreviews; subs = [ + widget.space.onChildRoomPreviewAdded + .listen((_) => onPreviewListChanged()), + widget.space.onChildRoomPreviewsUpdated + .listen((_) => onPreviewListChanged()), + widget.space.onChildRoomPreviewRemoved + .listen((_) => onPreviewListChanged()), EventBus.onSelectedRoomChanged.stream.listen(onRoomSelected), widget.space.onUpdate.listen(onSpaceUpdated), widget.space.onChildSpaceAdded.listen(onSpaceUpdated), @@ -50,9 +62,16 @@ class _SpaceListState extends State { super.initState(); } + void onPreviewListChanged() { + setState(() { + previews = widget.space.childPreviews; + }); + } + void onSpaceUpdated(void event) { setState(() { children = widget.space.children; + previews = widget.space.childPreviews; }); } @@ -82,6 +101,7 @@ class _SpaceListState extends State { return Column( children: [ for (var child in children) buildChild(child), + for (var preview in previews) buildPreviewChild(preview), ], ); } @@ -116,6 +136,24 @@ class _SpaceListState extends State { ); } + Widget buildPreviewChild(RoomPreview preview) { + return RoomPreviewTextButton( + preview, + onTap: joinRoomWithConfirmation, + ); + } + + Future joinRoomWithConfirmation(RoomPreview preview) async { + if (await AdaptiveDialog.confirmation(context, + prompt: + "Are you sure you want to join the room '${preview.displayName}' ?") == + true) { + Room room = await widget.space.client.joinRoomFromPreview(preview); + await widget.onRoomSelected?.call(room); + subs.add(room.onUpdate.listen(onRoomUpdated)); + } + } + Widget buildChild(SpaceChild child) { if (child case SpaceChildSpace _) return tiamat.TextButtonExpander(child.child.displayName, diff --git a/commet/lib/ui/organisms/side_navigation_bar/side_navigation_bar_direct_messages.dart b/commet/lib/ui/organisms/side_navigation_bar/side_navigation_bar_direct_messages.dart index 540ba67c4..123004e39 100644 --- a/commet/lib/ui/organisms/side_navigation_bar/side_navigation_bar_direct_messages.dart +++ b/commet/lib/ui/organisms/side_navigation_bar/side_navigation_bar_direct_messages.dart @@ -2,7 +2,6 @@ import 'dart:async'; import 'package:commet/client/client.dart'; import 'package:commet/client/components/direct_messages/direct_message_component.dart'; -import 'package:commet/client/room.dart'; import 'package:commet/ui/atoms/space_icon.dart'; import 'package:commet/utils/event_bus.dart'; import 'package:flutter/material.dart'; diff --git a/commet/lib/ui/organisms/space_summary/space_summary.dart b/commet/lib/ui/organisms/space_summary/space_summary.dart index 3cf09925f..492987257 100644 --- a/commet/lib/ui/organisms/space_summary/space_summary.dart +++ b/commet/lib/ui/organisms/space_summary/space_summary.dart @@ -17,10 +17,15 @@ import '../../navigation/navigation_utils.dart'; class SpaceSummary extends StatefulWidget { const SpaceSummary( - {super.key, required this.space, this.onRoomTap, this.onSpaceTap}); + {super.key, + required this.space, + this.onRoomTap, + this.onSpaceTap, + this.onLeaveRoom}); final Space space; final Function(Room room)? onRoomTap; final Function(Space space)? onSpaceTap; + final Function()? onLeaveRoom; @override State createState() => _SpaceSummaryState(); } @@ -86,7 +91,8 @@ class _SpaceSummaryState extends State { } void openRoomSettings(Room room) { - NavigationUtils.navigateTo(context, RoomSettingsPage(room: room)); + NavigationUtils.navigateTo( + context, RoomSettingsPage(room: room, onLeaveRoom: widget.onLeaveRoom)); } void onAddRoomButtonTap() async { diff --git a/commet/lib/ui/pages/main/main_page.dart b/commet/lib/ui/pages/main/main_page.dart index 17bb5bae6..6856caaad 100644 --- a/commet/lib/ui/pages/main/main_page.dart +++ b/commet/lib/ui/pages/main/main_page.dart @@ -317,6 +317,7 @@ class MainPageState extends State { context, RoomSettingsPage( room: currentRoom!, + onLeaveRoom: clearRoomSelection, )); } } diff --git a/commet/lib/ui/pages/main/main_page_view_desktop.dart b/commet/lib/ui/pages/main/main_page_view_desktop.dart index c3b4546a1..6f2e347b0 100644 --- a/commet/lib/ui/pages/main/main_page_view_desktop.dart +++ b/commet/lib/ui/pages/main/main_page_view_desktop.dart @@ -420,6 +420,7 @@ class MainPageViewDesktop extends StatelessWidget { space: state.currentSpace!, onRoomTap: (room) => state.selectRoom(room), onSpaceTap: (space) => state.selectSpace(space), + onLeaveRoom: state.clearRoomSelection, ), ], ), diff --git a/commet/lib/ui/pages/main/main_page_view_mobile.dart b/commet/lib/ui/pages/main/main_page_view_mobile.dart index c33d4a84d..a3195b1e9 100644 --- a/commet/lib/ui/pages/main/main_page_view_mobile.dart +++ b/commet/lib/ui/pages/main/main_page_view_mobile.dart @@ -236,6 +236,7 @@ class _MainPageViewMobileState extends State { widget.state.selectRoom(room); }, onSpaceTap: (space) => widget.state.selectSpace(space), + onLeaveRoom: widget.state.clearRoomSelection, ), ), ), diff --git a/commet/lib/ui/pages/settings/room_settings_page.dart b/commet/lib/ui/pages/settings/room_settings_page.dart index 28c874bdf..a330f8ed1 100644 --- a/commet/lib/ui/pages/settings/room_settings_page.dart +++ b/commet/lib/ui/pages/settings/room_settings_page.dart @@ -7,8 +7,9 @@ import 'package:flutter/material.dart'; import 'package:intl/intl.dart'; class RoomSettingsPage extends StatefulWidget { - const RoomSettingsPage({super.key, required this.room}); + const RoomSettingsPage({super.key, required this.room, this.onLeaveRoom}); final Room room; + final Function()? onLeaveRoom; @override State createState() => _RoomSettingsPageState(); @@ -50,6 +51,7 @@ class _RoomSettingsPageState extends State { true) { if (context.mounted) Navigator.pop(context); widget.room.client.leaveRoom(widget.room); + widget.onLeaveRoom?.call(); } } } diff --git a/commet/lib/utils/event_bus.dart b/commet/lib/utils/event_bus.dart index 13727d899..0b17c8f76 100644 --- a/commet/lib/utils/event_bus.dart +++ b/commet/lib/utils/event_bus.dart @@ -1,8 +1,6 @@ import 'dart:async'; import 'package:commet/client/client.dart'; import 'package:commet/client/components/message_effects/message_effect_particles.dart'; -import 'package:commet/client/room.dart'; -import 'package:commet/client/space.dart'; import 'package:commet/ui/molecules/overlapping_panels.dart'; import 'package:desktop_drop/desktop_drop.dart'; import 'package:flutter/material.dart';