consolidated downloads dir, nextcloud origin, upload route and chat timestamps; added webdav path tests

This commit is contained in:
2026-09-27 21:19:50 +02:00
parent aca86c506a
commit c455bb1cae
40 changed files with 464 additions and 282 deletions
+5 -9
View File
@@ -2,10 +2,10 @@ import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:persistent_bottom_nav_bar_v2/persistent_bottom_nav_bar_v2.dart';
import '../../../api/marianumcloud/webdav/queries/list_files/cacheable_file.dart';
import '../../../api/marianumcloud/webdav/queries/list_files/list_files_response.dart';
import '../../../routing/app_routes.dart';
import '../../../state/app/infrastructure/loadable_state/loadable_state.dart';
import '../../../state/app/infrastructure/loadable_state/view/loadable_state_consumer.dart';
import '../../../state/app/infrastructure/utility_widgets/bloc_module.dart';
@@ -15,7 +15,6 @@ import '../../../utils/cache_invalidation_bus.dart';
import '../../../widget/placeholder_view.dart';
import 'data/file_sort.dart';
import 'data/webdav_path.dart';
import 'files_upload_dialog.dart';
import 'search/files_search_delegate.dart';
import 'widgets/add_file_menu.dart';
import 'widgets/clipboard_banner.dart';
@@ -82,14 +81,11 @@ class _FilesViewState extends State<_FilesView> {
if (paths == null) return;
final bloc = context.read<FilesBloc>();
unawaited(
pushScreen(
AppRoutes.openFilesUpload(
context,
withNavBar: false,
screen: FilesUploadDialog(
filePaths: paths,
remotePath: widget.path.join('/'),
onUploadFinished: (_) => bloc.refresh(),
),
filePaths: paths,
remotePath: widget.path.join('/'),
onUploadFinished: (_) => bloc.refresh(),
),
);
}
@@ -32,7 +32,7 @@ class FileLeading extends StatelessWidget {
borderRadius: BorderRadius.circular(4),
child: CachedNetworkImage(
imageUrl:
'https://${EndpointData().nextcloud().full()}'
'${EndpointData().nextcloud().origin()}'
'/index.php/core/preview'
'?fileId=$fileId&x=128&y=128&a=0',
httpHeaders: SessionManager().requireNextcloud().authHeaders,
@@ -94,7 +94,7 @@ class _DiagnosticsPageState extends State<DiagnosticsPage> {
rows: await rows({
'Marianum Connect': () async => MarianumConnectEndpoint.current(),
'Nextcloud': () async =>
'https://${EndpointData().nextcloud().full()}',
EndpointData().nextcloud().origin(),
'Notfall-Nachricht': () async =>
settings.devToolsSettings.resolveEmergencyNoticeUrl(),
}),
@@ -121,8 +121,7 @@ class DevToolsSection extends StatelessWidget {
),
),
trailing: const Icon(Icons.arrow_right),
onTap: () =>
MarianumConnectEndpointPicker.show(context, settings),
onTap: () => MarianumConnectEndpointPicker.show(context, settings),
),
BlocBuilder<SettingsCubit, model.Settings>(
bloc: settings,
@@ -137,8 +136,7 @@ class DevToolsSection extends StatelessWidget {
title: const Text('Notfall-Nachrichtenquelle'),
subtitle: Text(override.isEmpty ? 'Standardquelle' : override),
trailing: const Icon(Icons.arrow_right),
onTap: () =>
_EmergencyNoticeUrlEditor.show(context, settings),
onTap: () => _EmergencyNoticeUrlEditor.show(context, settings),
);
},
),
@@ -146,9 +144,8 @@ class DevToolsSection extends StatelessWidget {
_StorageTile(
icon: Icons.settings_applications_outlined,
title: 'Einstellungen',
describe: () async => filesize(
utf8.encode(jsonEncode(settings.val().toJson())).length,
),
describe: () async =>
filesize(utf8.encode(jsonEncode(settings.val().toJson())).length),
onOpen: (_) async => AppRoutes.openJsonViewer(
context,
settings.val().toJson(),
@@ -235,15 +232,16 @@ class DevToolsSection extends StatelessWidget {
_StorageTile(
icon: Icons.download_outlined,
title: 'Downloads',
describe: () async =>
filesize(await directorySize(await _downloadsDir())),
describe: () async => filesize(
await directorySize(await DownloadManager.downloadsDirectory()),
),
clearTitle: 'Downloads löschen',
clearContent:
'Laufende Downloads werden abgebrochen und heruntergeladene '
'Dateien gelöscht.',
onClear: () async {
DownloadManager.instance.clearAll();
final dir = await _downloadsDir();
final dir = await DownloadManager.downloadsDirectory();
if (!dir.existsSync()) return;
// Cancelled tasks can still be flushing into the folder, so deleting
// it as a whole races them; whatever survives shows up in the size.
@@ -258,9 +256,6 @@ class DevToolsSection extends StatelessWidget {
),
],
);
static Future<Directory> _downloadsDir() async =>
Directory('${(await getTemporaryDirectory()).path}/downloads');
}
/// Storage row: tap opens a detail view ([onOpen]), the trailing button clears
@@ -2,7 +2,6 @@ import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:persistent_bottom_nav_bar_v2/persistent_bottom_nav_bar_v2.dart';
import '../../../api/errors/error_mapper.dart';
import '../../../api/marianumcloud/talk/room/get_room_response.dart';
@@ -20,7 +19,6 @@ import '../../../state/app/modules/settings/bloc/settings_cubit.dart';
import '../../../widget/app_progress_indicator.dart';
import '../../../widget/info_dialog.dart';
import '../../../widget/placeholder_view.dart';
import '../files/files_upload_dialog.dart';
import '../talk/search_chat.dart';
import '../talk/widgets/chat_tile.dart';
@@ -144,16 +142,13 @@ Future<void> _externalShareFlow(
try {
await ensureTalkShareFolder();
if (!context.mounted) return;
await pushScreen(
await AppRoutes.openFilesUpload(
context,
withNavBar: false,
screen: FilesUploadDialog(
filePaths: share.filePaths,
remotePath: talkShareFolder,
uniqueNames: true,
onUploadFinished: (uploaded) =>
_afterExternalFilesUploaded(context, room, uploaded, share),
),
filePaths: share.filePaths,
remotePath: talkShareFolder,
uniqueNames: true,
onUploadFinished: (uploaded) =>
_afterExternalFilesUploaded(context, room, uploaded, share),
);
} finally {
ShareIntentListener.instance.endFlow();
@@ -1,6 +1,5 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:persistent_bottom_nav_bar_v2/persistent_bottom_nav_bar_v2.dart';
import '../../../api/errors/error_mapper.dart';
import '../../../routing/app_routes.dart';
@@ -17,7 +16,6 @@ import '../../../widget/centered_leading.dart';
import '../../../widget/info_dialog.dart';
import '../../../widget/placeholder_view.dart';
import '../files/data/file_sort.dart';
import '../files/files_upload_dialog.dart';
import '../files/widgets/add_file_menu.dart';
import '../files/widgets/file_leading.dart';
import '../files/widgets/files_sort_actions.dart';
@@ -173,15 +171,12 @@ Future<void> _externalUploadFlow(
) async {
ShareIntentListener.instance.beginFlow();
try {
await pushScreen(
await AppRoutes.openFilesUpload(
context,
withNavBar: false,
screen: FilesUploadDialog(
filePaths: share.filePaths,
remotePath: targetPath.join('/'),
onUploadFinished: (_) =>
_afterExternalUploaded(context, targetPath, share),
),
filePaths: share.filePaths,
remotePath: targetPath.join('/'),
onUploadFinished: (_) =>
_afterExternalUploaded(context, targetPath, share),
);
} finally {
ShareIntentListener.instance.endFlow();
+1 -3
View File
@@ -304,9 +304,7 @@ class _ChatViewState extends State<ChatView> with RouteAware {
for (final element in response.sortByTimestamp()) {
if (ChatSearchController.isHiddenSystemMessage(element)) continue;
final elementDate = DateTime.fromMillisecondsSinceEpoch(
element.timestamp * 1000,
);
final elementDate = element.sentAt;
if (!elementDate.isSameDay(lastDate)) {
lastDate = elementDate;
+1 -1
View File
@@ -106,7 +106,7 @@ class ChatMessage {
errorListener: (value) {},
httpHeaders: SessionManager().requireNextcloud().authHeaders,
imageUrl:
'https://${EndpointData().nextcloud().full()}/index.php/core/preview?fileId=${file.id}&x=130&y=-1&a=1',
'${EndpointData().nextcloud().origin()}/index.php/core/preview?fileId=${file.id}&x=130&y=-1&a=1',
),
if (originalMessage != '{file}') ...[
SizedBox(height: 5),
@@ -511,7 +511,7 @@ class _SharedItemTileState extends State<_SharedItemTile>
}
String get _previewUrl =>
'https://${EndpointData().nextcloud().full()}'
'${EndpointData().nextcloud().origin()}'
'/index.php/core/preview?fileId=${_file.id}&x=300&y=300&a=1';
bool get _isVideo => _isVideoFile(_file.name);
@@ -586,7 +586,7 @@ class _SharedItemTileState extends State<_SharedItemTile>
title: Text(_file.name, maxLines: 2, overflow: TextOverflow.ellipsis),
subtitle: Text(
'${widget.item.actorDisplayName} · '
'${DateTime.fromMillisecondsSinceEpoch(widget.item.timestamp * 1000).formatDateShort()}',
'${widget.item.sentAt.formatDateShort()}',
),
trailing: _isDownloading
? SizedBox(
+1 -3
View File
@@ -257,9 +257,7 @@ class _ChatBubbleState extends State<ChatBubble>
: actorText;
final timeText = Text(
DateTime.fromMillisecondsSinceEpoch(
widget.bubbleData.timestamp * 1000,
).formatHm(),
widget.bubbleData.sentAt.formatHm(),
textAlign: TextAlign.end,
style: const TextStyle(color: _kTimeIconColor, fontSize: _kTimeIconSize),
);
@@ -67,9 +67,7 @@ void showChatMessageOptionsDialog(
final hasReactions = bubbleData.reactions?.isNotEmpty ?? false;
final canDelete =
isSender &&
DateTime.fromMillisecondsSinceEpoch(
bubbleData.timestamp * 1000,
).add(const Duration(hours: 6)).isAfter(DateTime.now());
bubbleData.sentAt.add(const Duration(hours: 6)).isAfter(DateTime.now());
final attachedFile = _attachedFile(bubbleData);
showDetailsBottomSheet(
@@ -2,12 +2,12 @@ import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:persistent_bottom_nav_bar_v2/persistent_bottom_nav_bar_v2.dart';
import '../../../../api/marianumcloud/talk/room/get_room_response.dart';
import '../../../../api/marianumcloud/talk/send_message/send_message.dart';
import '../../../../api/marianumcloud/talk/send_message/send_message_params.dart';
import '../../../../api/marianumcloud/talk/share_files_to_chat.dart';
import '../../../../routing/app_routes.dart';
import '../../../../state/app/modules/chat/bloc/chat_bloc.dart';
import '../../../../state/app/modules/chat/bloc/chat_state.dart';
import '../../../../state/app/modules/settings/bloc/settings_cubit.dart';
@@ -18,7 +18,6 @@ import '../../../../widget/downloads/download_tray.dart';
import '../../../../widget/emoji_picker_dialog.dart';
import '../../../../widget/file_pick.dart';
import '../../../../widget/focus_behaviour.dart';
import '../../files/files_upload_dialog.dart';
import 'answer_reference.dart';
import 'poll_create_sheet.dart';
@@ -71,15 +70,12 @@ class _ChatTextfieldState extends State<ChatTextfield> {
if (!mounted) return;
unawaited(
pushScreen(
AppRoutes.openFilesUpload(
context,
withNavBar: false,
screen: FilesUploadDialog(
filePaths: paths,
remotePath: talkShareFolder,
onUploadFinished: share,
uniqueNames: true,
),
filePaths: paths,
remotePath: talkShareFolder,
onUploadFinished: share,
uniqueNames: true,
),
);
}
+1 -1
View File
@@ -174,7 +174,7 @@ class _ChatTileState extends State<ChatTile> {
],
),
subtitle: Text(
'${DateTime.fromMillisecondsSinceEpoch(widget.data.lastMessage.timestamp * 1000).formatRelative()}: '
'${widget.data.lastMessage.sentAt.formatRelative()}: '
'${_lastMessagePreview()}',
overflow: TextOverflow.ellipsis,
),