feat: Room Previews

This commit is contained in:
Krille 2025-03-02 15:18:19 +01:00
parent e94368108a
commit d48da53134
No known key found for this signature in database
GPG key ID: E067ECD60F1A0652
8 changed files with 76 additions and 67 deletions

View file

@ -3,6 +3,7 @@ import 'dart:async';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart'; import 'package:go_router/go_router.dart';
import 'package:matrix/matrix_api_lite.dart';
import 'package:fluffychat/config/themes.dart'; import 'package:fluffychat/config/themes.dart';
import 'package:fluffychat/pages/archive/archive.dart'; import 'package:fluffychat/pages/archive/archive.dart';
@ -37,7 +38,6 @@ import 'package:fluffychat/widgets/log_view.dart';
import 'package:fluffychat/widgets/matrix.dart'; import 'package:fluffychat/widgets/matrix.dart';
import 'package:fluffychat/widgets/room_loader.dart'; import 'package:fluffychat/widgets/room_loader.dart';
import 'package:fluffychat/widgets/share_scaffold_dialog.dart'; import 'package:fluffychat/widgets/share_scaffold_dialog.dart';
import 'package:matrix/matrix_api_lite.dart';
abstract class AppRoutes { abstract class AppRoutes {
static FutureOr<String?> loggedInRedirect( static FutureOr<String?> loggedInRedirect(
@ -390,8 +390,11 @@ abstract class AppRoutes {
pageBuilder: (context, state) => defaultPageBuilder( pageBuilder: (context, state) => defaultPageBuilder(
context, context,
state, state,
ChatDetails( RoomLoader(
roomId: state.pathParameters['roomid']!, roomId: state.pathParameters['roomid']!,
builder: (context, room) => ChatDetails(
room: room,
),
), ),
), ),
routes: [ routes: [

View file

@ -1295,7 +1295,7 @@ class ChatController extends State<ChatPage> with WidgetsBindingObserver {
), ),
), ),
child: ChatDetails( child: ChatDetails(
roomId: roomId, room: room,
embeddedCloseButton: IconButton( embeddedCloseButton: IconButton(
icon: const Icon(Icons.close), icon: const Icon(Icons.close),
onPressed: toggleDisplayChatDetailsColumn, onPressed: toggleDisplayChatDetailsColumn,

View file

@ -31,11 +31,9 @@ class ChatAppBarTitle extends StatelessWidget {
hoverColor: Colors.transparent, hoverColor: Colors.transparent,
splashColor: Colors.transparent, splashColor: Colors.transparent,
highlightColor: Colors.transparent, highlightColor: Colors.transparent,
onTap: controller.isArchived onTap: () => FluffyThemes.isThreeColumnMode(context)
? null ? controller.toggleDisplayChatDetailsColumn()
: () => FluffyThemes.isThreeColumnMode(context) : context.go('/rooms/${room.id}/details'),
? controller.toggleDisplayChatDetailsColumn()
: context.go('/rooms/${room.id}/details'),
child: Row( child: Row(
children: [ children: [
Hero( Hero(

View file

@ -19,6 +19,7 @@ import 'package:fluffychat/pages/chat/reactions_picker.dart';
import 'package:fluffychat/pages/chat/reply_display.dart'; import 'package:fluffychat/pages/chat/reply_display.dart';
import 'package:fluffychat/utils/account_config.dart'; import 'package:fluffychat/utils/account_config.dart';
import 'package:fluffychat/widgets/chat_settings_popup_menu.dart'; import 'package:fluffychat/widgets/chat_settings_popup_menu.dart';
import 'package:fluffychat/widgets/future_loading_dialog.dart';
import 'package:fluffychat/widgets/matrix.dart'; import 'package:fluffychat/widgets/matrix.dart';
import 'package:fluffychat/widgets/mxc_image.dart'; import 'package:fluffychat/widgets/mxc_image.dart';
import 'package:fluffychat/widgets/unread_rooms_badge.dart'; import 'package:fluffychat/widgets/unread_rooms_badge.dart';
@ -125,7 +126,9 @@ class ChatView extends StatelessWidget {
ChatSettingsPopupMenu(controller.room, true), ChatSettingsPopupMenu(controller.room, true),
]; ];
} }
return []; return [
ChatSettingsPopupMenu(controller.room, true),
];
} }
@override @override
@ -292,6 +295,22 @@ class ChatView extends StatelessWidget {
child: ChatEventList(controller: controller), child: ChatEventList(controller: controller),
), ),
), ),
if (controller.room.membership != Membership.join &&
(controller.room.membership ==
Membership.invite ||
controller.room.joinRules ==
JoinRules.public))
Container(
padding: EdgeInsets.all(bottomSheetPadding),
width: double.infinity,
child: ElevatedButton(
onPressed: () => showFutureLoadingDialog(
context: context,
future: controller.room.join,
),
child: Text(L10n.of(context).joinRoom),
),
),
if (controller.room.canSendDefaultMessages && if (controller.room.canSendDefaultMessages &&
controller.room.membership == Membership.join) controller.room.membership == Membership.join)
Container( Container(

View file

@ -14,17 +14,16 @@ import 'package:fluffychat/utils/platform_infos.dart';
import 'package:fluffychat/widgets/adaptive_dialogs/show_modal_action_popup.dart'; import 'package:fluffychat/widgets/adaptive_dialogs/show_modal_action_popup.dart';
import 'package:fluffychat/widgets/adaptive_dialogs/show_text_input_dialog.dart'; import 'package:fluffychat/widgets/adaptive_dialogs/show_text_input_dialog.dart';
import 'package:fluffychat/widgets/future_loading_dialog.dart'; import 'package:fluffychat/widgets/future_loading_dialog.dart';
import 'package:fluffychat/widgets/matrix.dart';
enum AliasActions { copy, delete, setCanonical } enum AliasActions { copy, delete, setCanonical }
class ChatDetails extends StatefulWidget { class ChatDetails extends StatefulWidget {
final String roomId; final Room room;
final Widget? embeddedCloseButton; final Widget? embeddedCloseButton;
const ChatDetails({ const ChatDetails({
super.key, super.key,
required this.roomId, required this.room,
this.embeddedCloseButton, this.embeddedCloseButton,
}); });
@ -38,10 +37,8 @@ class ChatDetailsController extends State<ChatDetails> {
void toggleDisplaySettings() => void toggleDisplaySettings() =>
setState(() => displaySettings = !displaySettings); setState(() => displaySettings = !displaySettings);
String? get roomId => widget.roomId;
void setDisplaynameAction() async { void setDisplaynameAction() async {
final room = Matrix.of(context).client.getRoomById(roomId!)!; final room = widget.room;
final input = await showTextInputDialog( final input = await showTextInputDialog(
context: context, context: context,
title: L10n.of(context).changeTheNameOfTheGroup, title: L10n.of(context).changeTheNameOfTheGroup,
@ -66,7 +63,7 @@ class ChatDetailsController extends State<ChatDetails> {
} }
void setTopicAction() async { void setTopicAction() async {
final room = Matrix.of(context).client.getRoomById(roomId!)!; final room = widget.room;
final input = await showTextInputDialog( final input = await showTextInputDialog(
context: context, context: context,
title: L10n.of(context).setChatDescription, title: L10n.of(context).setChatDescription,
@ -92,7 +89,7 @@ class ChatDetailsController extends State<ChatDetails> {
} }
void goToEmoteSettings() async { void goToEmoteSettings() async {
final room = Matrix.of(context).client.getRoomById(roomId!)!; final room = widget.room;
// okay, we need to test if there are any emote state events other than the default one // okay, we need to test if there are any emote state events other than the default one
// if so, we need to be directed to a selection screen for which pack we want to look at // if so, we need to be directed to a selection screen for which pack we want to look at
// otherwise, we just open the normal one. // otherwise, we just open the normal one.
@ -106,7 +103,7 @@ class ChatDetailsController extends State<ChatDetails> {
} }
void setAvatarAction() async { void setAvatarAction() async {
final room = Matrix.of(context).client.getRoomById(roomId!); final room = widget.room;
final actions = [ final actions = [
if (PlatformInfos.isMobile) if (PlatformInfos.isMobile)
AdaptiveModalAction( AdaptiveModalAction(
@ -120,7 +117,7 @@ class ChatDetailsController extends State<ChatDetails> {
label: L10n.of(context).openGallery, label: L10n.of(context).openGallery,
icon: const Icon(Icons.photo_outlined), icon: const Icon(Icons.photo_outlined),
), ),
if (room?.avatar != null) if (room.avatar != null)
AdaptiveModalAction( AdaptiveModalAction(
value: AvatarAction.remove, value: AvatarAction.remove,
label: L10n.of(context).delete, label: L10n.of(context).delete,
@ -140,7 +137,7 @@ class ChatDetailsController extends State<ChatDetails> {
if (action == AvatarAction.remove) { if (action == AvatarAction.remove) {
await showFutureLoadingDialog( await showFutureLoadingDialog(
context: context, context: context,
future: () => room!.setAvatar(null), future: () => room.setAvatar(null),
); );
return; return;
} }
@ -172,7 +169,7 @@ class ChatDetailsController extends State<ChatDetails> {
} }
await showFutureLoadingDialog( await showFutureLoadingDialog(
context: context, context: context,
future: () => room!.setAvatar(file), future: () => room.setAvatar(file),
); );
} }

View file

@ -12,7 +12,6 @@ import 'package:fluffychat/utils/matrix_sdk_extensions/matrix_locals.dart';
import 'package:fluffychat/widgets/avatar.dart'; import 'package:fluffychat/widgets/avatar.dart';
import 'package:fluffychat/widgets/chat_settings_popup_menu.dart'; import 'package:fluffychat/widgets/chat_settings_popup_menu.dart';
import 'package:fluffychat/widgets/layouts/max_width_body.dart'; import 'package:fluffychat/widgets/layouts/max_width_body.dart';
import 'package:fluffychat/widgets/matrix.dart';
import '../../utils/url_launcher.dart'; import '../../utils/url_launcher.dart';
import '../../widgets/qr_code_viewer.dart'; import '../../widgets/qr_code_viewer.dart';
@ -25,17 +24,7 @@ class ChatDetailsView extends StatelessWidget {
Widget build(BuildContext context) { Widget build(BuildContext context) {
final theme = Theme.of(context); final theme = Theme.of(context);
final room = Matrix.of(context).client.getRoomById(controller.roomId!); final room = controller.widget.room;
if (room == null) {
return Scaffold(
appBar: AppBar(
title: Text(L10n.of(context).oopsSomethingWentWrong),
),
body: Center(
child: Text(L10n.of(context).youAreNoLongerParticipatingInThisChat),
),
);
}
return StreamBuilder( return StreamBuilder(
stream: room.client.onRoomState.stream stream: room.client.onRoomState.stream
@ -163,7 +152,7 @@ class ChatDetailsView extends StatelessWidget {
onPressed: () => room.isDirectChat onPressed: () => room.isDirectChat
? null ? null
: context.push( : context.push(
'/rooms/${controller.roomId}/details/members', '/rooms/${controller.widget.room.id}/details/members',
), ),
icon: const Icon( icon: const Icon(
Icons.group_outlined, Icons.group_outlined,
@ -335,7 +324,7 @@ class ChatDetailsView extends StatelessWidget {
), ),
), ),
onTap: () => context.push( onTap: () => context.push(
'/rooms/${controller.roomId!}/details/members', '/rooms/${controller.widget.room.id}/details/members',
), ),
trailing: const Icon(Icons.chevron_right_outlined), trailing: const Icon(Icons.chevron_right_outlined),
), ),

View file

@ -107,28 +107,30 @@ class ChatSettingsPopupMenuState extends State<ChatSettingsPopupMenu> {
], ],
), ),
), ),
if (widget.room.pushRuleState == PushRuleState.notify) if (widget.room.membership == Membership.join) ...[
PopupMenuItem<ChatPopupMenuActions>( if (widget.room.pushRuleState == PushRuleState.notify)
value: ChatPopupMenuActions.mute, PopupMenuItem<ChatPopupMenuActions>(
child: Row( value: ChatPopupMenuActions.mute,
children: [ child: Row(
const Icon(Icons.notifications_off_outlined), children: [
const SizedBox(width: 12), const Icon(Icons.notifications_off_outlined),
Text(L10n.of(context).muteChat), const SizedBox(width: 12),
], Text(L10n.of(context).muteChat),
],
),
)
else
PopupMenuItem<ChatPopupMenuActions>(
value: ChatPopupMenuActions.unmute,
child: Row(
children: [
const Icon(Icons.notifications_on_outlined),
const SizedBox(width: 12),
Text(L10n.of(context).unmuteChat),
],
),
), ),
) ],
else
PopupMenuItem<ChatPopupMenuActions>(
value: ChatPopupMenuActions.unmute,
child: Row(
children: [
const Icon(Icons.notifications_on_outlined),
const SizedBox(width: 12),
Text(L10n.of(context).unmuteChat),
],
),
),
PopupMenuItem<ChatPopupMenuActions>( PopupMenuItem<ChatPopupMenuActions>(
value: ChatPopupMenuActions.search, value: ChatPopupMenuActions.search,
child: Row( child: Row(
@ -139,16 +141,17 @@ class ChatSettingsPopupMenuState extends State<ChatSettingsPopupMenu> {
], ],
), ),
), ),
PopupMenuItem<ChatPopupMenuActions>( if (widget.room.membership == Membership.join)
value: ChatPopupMenuActions.leave, PopupMenuItem<ChatPopupMenuActions>(
child: Row( value: ChatPopupMenuActions.leave,
children: [ child: Row(
const Icon(Icons.delete_outlined), children: [
const SizedBox(width: 12), const Icon(Icons.delete_outlined),
Text(L10n.of(context).leave), const SizedBox(width: 12),
], Text(L10n.of(context).leave),
],
),
), ),
),
], ],
), ),
], ],

View file

@ -1,9 +1,9 @@
import 'package:fluffychat/utils/localized_exception_extension.dart';
import 'package:fluffychat/utils/room_from_public_rooms_chunk.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:matrix/matrix.dart'; import 'package:matrix/matrix.dart';
import 'package:fluffychat/utils/localized_exception_extension.dart';
import 'package:fluffychat/utils/room_from_public_rooms_chunk.dart';
import 'package:fluffychat/widgets/matrix.dart'; import 'package:fluffychat/widgets/matrix.dart';
class RoomLoader extends StatelessWidget { class RoomLoader extends StatelessWidget {