implemented internal file sharing and saving, added server-side file references, refactored share pickers for unified flows, and updated UI branding labels

This commit is contained in:
2026-05-09 20:18:52 +02:00
parent cb2c38aaa1
commit 151678f0fe
15 changed files with 437 additions and 128 deletions
+3 -3
View File
@@ -153,10 +153,10 @@ class _ChatListViewState extends State<_ChatListView> {
) {
if (username == null || !context.mounted) return;
ConfirmDialog(
title: 'Chat starten',
title: 'Talk-Chat starten',
content:
"Möchtest du einen Chat mit Nutzer '$username' starten?",
confirmButton: 'Chat starten',
"Möchtest du einen Talk-Chat mit Nutzer '$username' starten?",
confirmButton: 'Talk-Chat starten',
onConfirmAsync: () => bloc.createDirectChat(username),
).asDialog(context);
});
+9 -1
View File
@@ -6,6 +6,7 @@ import '../../../../api/marianumcloud/talk/room/get_room_response.dart';
import '../../../../extensions/date_time.dart';
import '../../../../extensions/text.dart';
import '../../../../routing/app_routes.dart';
import '../../../../share_intent/remote_file_ref.dart';
import '../../../../state/app/modules/chat/bloc/chat_bloc.dart';
import '../../../../utils/download_manager.dart';
import '../../../../widget/confirm_dialog.dart';
@@ -90,7 +91,14 @@ class _ChatBubbleState extends State<ChatBubble>
if (status is DownloadDone) {
DownloadManager.instance.clear(job.remotePath);
_detachJob();
AppRoutes.openFileViewer(context, status.localPath);
final talkFile = message.file;
AppRoutes.openFileViewer(
context,
status.localPath,
remoteFile: talkFile != null
? RemoteFileRef.fromTalk(talkFile)
: null,
);
setState(() {});
} else if (status is DownloadFailed) {
final message = status.message;
@@ -9,6 +9,7 @@ import '../../../../api/marianumcloud/talk/react_message/react_message.dart';
import '../../../../api/marianumcloud/talk/react_message/react_message_params.dart';
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 '../../../../utils/clipboard_helper.dart';
import '../../../../widget/app_progress_indicator.dart';
@@ -18,6 +19,16 @@ import '../../../../widget/details_bottom_sheet.dart';
const _commonReactions = <String>['👍', '👎', '😆', '❤️', '👀'];
RichObjectString? _attachedFile(GetChatResponseObject bubbleData) {
final file = bubbleData.messageParameters?['file'];
if (file == null ||
file.path == null ||
file.type != RichObjectStringObjectType.file) {
return null;
}
return file;
}
/// Long-press / double-tap options dialog for a single chat message bubble.
/// The hosting [ChatBubble] keeps responsibility for rendering the bubble;
/// this file owns the modal interactions (react, reply, copy, delete, ...).
@@ -36,6 +47,7 @@ void showChatMessageOptionsDialog(
DateTime.fromMillisecondsSinceEpoch(
bubbleData.timestamp * 1000,
).add(const Duration(hours: 6)).isAfter(DateTime.now());
final attachedFile = _attachedFile(bubbleData);
showDetailsBottomSheet(
context,
@@ -79,6 +91,19 @@ void showChatMessageOptionsDialog(
Navigator.of(sheetCtx).pop();
},
),
if (attachedFile != null)
ListTile(
leading: const Icon(Icons.cloud_outlined),
title: const Text('In Cloud speichern'),
onTap: () {
Navigator.of(sheetCtx).pop();
if (!parentContext.mounted) return;
AppRoutes.openInternalSaveToFolder(
parentContext,
RemoteFileRef.fromTalk(attachedFile),
);
},
),
if (!kReleaseMode &&
!isSender &&
chatData.type != GetRoomResponseObjectConversationType.oneToOne)
+2 -2
View File
@@ -207,11 +207,11 @@ class _ChatTileState extends State<ChatTile> {
),
ListTile(
leading: const Icon(Icons.delete_outline),
title: const Text('Konversation verlassen'),
title: const Text('Talk-Chat verlassen'),
onTap: () {
Navigator.of(sheetCtx).pop();
ConfirmDialog(
title: 'Chat verlassen',
title: 'Talk-Chat verlassen',
content:
'Du benötigst ggf. eine Einladung um erneut beizutreten.',
confirmButton: 'Verlassen',