refactored direct chat logic into a shared utility, implemented direct message shortcuts in the participant list and message reactions, and added reaction visibility checks in the message options dialog

This commit is contained in:
2026-05-13 18:46:34 +02:00
parent a09817a975
commit d0ba7c0fd6
5 changed files with 121 additions and 77 deletions
@@ -10,13 +10,13 @@ import '../../../../api/marianumcloud/talk/room/get_room_response.dart';
import '../../../../routing/app_routes.dart';
import '../../../../share_intent/remote_file_ref.dart';
import '../../../../state/app/modules/chat/bloc/chat_bloc.dart';
import '../../../../state/app/modules/chat_list/bloc/chat_list_bloc.dart';
import '../../../../utils/clipboard_helper.dart';
import '../../../../widget/app_progress_indicator.dart';
import '../../../../widget/async_action_button.dart';
import '../../../../widget/confirm_dialog.dart';
import '../../../../widget/debug/debug_tile.dart';
import '../../../../widget/details_bottom_sheet.dart';
import '../data/open_direct_chat.dart';
const _commonReactions = <String>['👍', '👎', '😆', '❤️', '👀'];
@@ -40,6 +40,7 @@ void showChatMessageOptionsDialog(
final parentContext = context;
final canReact =
bubbleData.messageType == GetRoomResponseObjectMessageType.comment;
final hasReactions = bubbleData.reactions?.isNotEmpty ?? false;
final canDelete =
isSender &&
DateTime.fromMillisecondsSinceEpoch(
@@ -66,7 +67,7 @@ void showChatMessageOptionsDialog(
Navigator.of(sheetCtx).pop();
},
),
if (canReact)
if (canReact && hasReactions)
ListTile(
leading: const Icon(Icons.emoji_emotions_outlined),
title: const Text('Reaktionen'),
@@ -129,7 +130,7 @@ void showChatMessageOptionsDialog(
onTap: () {
Navigator.of(sheetCtx).pop();
if (!parentContext.mounted) return;
_openOrCreateDirectChat(
openOrCreateDirectChat(
parentContext,
actorId: bubbleData.actorId,
actorDisplayName: bubbleData.actorDisplayName,
@@ -160,62 +161,6 @@ void showChatMessageOptionsDialog(
);
}
void _openOrCreateDirectChat(
BuildContext context, {
required String actorId,
required String actorDisplayName,
}) {
final chatListBloc = context.read<ChatListBloc>();
GetRoomResponseObject? findExisting() {
final rooms = chatListBloc.state.data?.rooms;
if (rooms == null) return null;
for (final room in rooms.data) {
if (room.type == GetRoomResponseObjectConversationType.oneToOne &&
room.name == actorId) {
return room;
}
}
return null;
}
void switchToChat(GetRoomResponseObject room) {
// Pop the previous ChatView first — otherwise it stays in the
// back-stack with a now-mismatched currentToken and renders empty
// on back-swipe. Stop at popups so an open dialog stays alive.
Navigator.of(
context,
).popUntil((route) => route.isFirst || route is PopupRoute);
AppRoutes.openChatByToken(context, room.token);
}
final existing = findExisting();
if (existing != null) {
switchToChat(existing);
return;
}
ConfirmDialog(
title: 'Privatchat starten?',
content:
'Es existiert noch kein Privatchat mit $actorDisplayName. '
'Soll einer erstellt werden?',
confirmButton: 'Erstellen',
onConfirmAsync: () async {
await chatListBloc.createDirectChat(actorId);
final created = findExisting();
if (created == null) {
throw Exception(
'Privatchat konnte nach dem Erstellen nicht gefunden werden.',
);
}
if (context.mounted) {
switchToChat(created);
}
},
).asDialog(context);
}
class _ReactionsRow extends StatefulWidget {
final String chatToken;
final int messageId;