simplified code across the app and consolidated duplicated helpers
This commit is contained in:
@@ -5,6 +5,7 @@ import '../../../../api/marianumcloud/webdav/queries/list_files/list_files_cache
|
||||
import '../../../../api/marianumcloud/webdav/webdav_api.dart';
|
||||
import '../../../../utils/file_clipboard.dart';
|
||||
import '../../../../widget/info_dialog.dart';
|
||||
import '../data/webdav_path.dart';
|
||||
|
||||
/// Banner that appears at the top of a Files folder while there is something
|
||||
/// in the file clipboard. Shows the cut/copy state and offers a "Hier
|
||||
@@ -26,16 +27,6 @@ class ClipboardBanner extends StatefulWidget {
|
||||
class _ClipboardBannerState extends State<ClipboardBanner> {
|
||||
bool _busy = false;
|
||||
|
||||
// All paths here are relative to the WebDAV root (matching `CacheableFile.path`).
|
||||
// Root is the empty string ''. Folders end with '/'.
|
||||
String _normalised(String path) {
|
||||
final stripped = path.replaceAll(RegExp(r'^/+|/+$'), '');
|
||||
return stripped.isEmpty ? '' : '$stripped/';
|
||||
}
|
||||
|
||||
String _joinPath(String folder, String name, {required bool isDirectory}) =>
|
||||
isDirectory ? '$folder$name/' : '$folder$name';
|
||||
|
||||
// Disabled when:
|
||||
// - clipboard is empty
|
||||
// - we'd be pasting a folder into itself or one of its descendants
|
||||
@@ -43,14 +34,14 @@ class _ClipboardBannerState extends State<ClipboardBanner> {
|
||||
bool get _canPaste {
|
||||
final cb = FileClipboard.instance;
|
||||
if (cb.isEmpty) return false;
|
||||
final dst = _normalised(widget.currentFolder);
|
||||
final dst = webdavFolderPath(webdavSegmentsOf(widget.currentFolder));
|
||||
var atLeastOneActionable = false;
|
||||
for (final f in cb.files) {
|
||||
if (f.isDirectory) {
|
||||
final src = _normalised(f.path);
|
||||
final src = webdavFolderPath(webdavSegmentsOf(f.path));
|
||||
if (dst == src || dst.startsWith(src)) return false;
|
||||
}
|
||||
final destination = _joinPath(
|
||||
final destination = webdavJoin(
|
||||
widget.currentFolder,
|
||||
f.name,
|
||||
isDirectory: f.isDirectory,
|
||||
@@ -60,15 +51,6 @@ class _ClipboardBannerState extends State<ClipboardBanner> {
|
||||
return atLeastOneActionable;
|
||||
}
|
||||
|
||||
// Cache key format used by ListFilesCache (matches FilesBloc's pathString:
|
||||
// relative, no leading or trailing slash; root is '/').
|
||||
String _parentCacheKey(String relativePath) {
|
||||
final stripped = relativePath.replaceAll(RegExp(r'^/+|/+$'), '');
|
||||
if (!stripped.contains('/')) return '/';
|
||||
final parts = stripped.split('/')..removeLast();
|
||||
return parts.isEmpty ? '/' : parts.join('/');
|
||||
}
|
||||
|
||||
Future<void> _paste() async {
|
||||
final cb = FileClipboard.instance;
|
||||
if (_busy || !_canPaste) return;
|
||||
@@ -79,7 +61,7 @@ class _ClipboardBannerState extends State<ClipboardBanner> {
|
||||
try {
|
||||
final webdav = await WebdavApi.webdav;
|
||||
for (final file in cb.files) {
|
||||
final destination = _joinPath(
|
||||
final destination = webdavJoin(
|
||||
widget.currentFolder,
|
||||
file.name,
|
||||
isDirectory: file.isDirectory,
|
||||
@@ -91,7 +73,9 @@ class _ClipboardBannerState extends State<ClipboardBanner> {
|
||||
PathUri.parse(file.path),
|
||||
PathUri.parse(destination),
|
||||
);
|
||||
invalidatedSourceFolders.add(_parentCacheKey(file.path));
|
||||
invalidatedSourceFolders.add(
|
||||
listFilesCacheKey(webdavParentSegmentsOf(file.path)),
|
||||
);
|
||||
} else {
|
||||
await webdav.copy(
|
||||
PathUri.parse(file.path),
|
||||
@@ -105,9 +89,9 @@ class _ClipboardBannerState extends State<ClipboardBanner> {
|
||||
// After cut, the source folders no longer contain the moved files. Drop
|
||||
// their cached listings so the next visit fetches fresh data instead of
|
||||
// briefly showing the moved file as still present.
|
||||
for (final folder in invalidatedSourceFolders) {
|
||||
await ListFilesCache.invalidate(folder);
|
||||
}
|
||||
await Future.wait(
|
||||
invalidatedSourceFolders.map(ListFilesCache.invalidate),
|
||||
);
|
||||
if (operation == FileClipboardOperation.cut) cb.clear();
|
||||
widget.onPasteDone();
|
||||
} finally {
|
||||
|
||||
@@ -19,6 +19,7 @@ import '../../../../widget/details_bottom_sheet.dart';
|
||||
import '../../../../widget/downloads/download_trigger.dart';
|
||||
import '../../../../widget/prompt_dialog.dart';
|
||||
import '../../talk/widgets/highlighted_linkify.dart';
|
||||
import '../data/webdav_path.dart';
|
||||
import '../sharing/share_sheet.dart';
|
||||
import 'file_details_sheet.dart';
|
||||
import 'file_leading.dart';
|
||||
@@ -26,7 +27,7 @@ import 'file_leading.dart';
|
||||
class FileElement extends StatefulWidget {
|
||||
final CacheableFile file;
|
||||
final List<String> path;
|
||||
final void Function() refetch;
|
||||
final VoidCallback refetch;
|
||||
|
||||
/// When non-null, occurrences of this string in the file name are visually
|
||||
/// highlighted in the tile title. Used by the Files search delegate.
|
||||
@@ -101,7 +102,7 @@ class _FileElementState extends State<FileElement>
|
||||
if (widget.file.isDirectory) {
|
||||
AppRoutes.openFolder(
|
||||
context,
|
||||
widget.path.toList()..add(widget.file.name),
|
||||
[...widget.path, widget.file.name],
|
||||
);
|
||||
return;
|
||||
}
|
||||
@@ -116,18 +117,6 @@ class _FileElementState extends State<FileElement>
|
||||
);
|
||||
}
|
||||
|
||||
// All paths here are relative to the WebDAV root (matching CacheableFile.path).
|
||||
// Root parent is the empty string ''. Folders end with '/'.
|
||||
String _parentPathOf(String path) {
|
||||
final stripped = path.replaceAll(RegExp(r'^/+|/+$'), '');
|
||||
if (!stripped.contains('/')) return '';
|
||||
final parts = stripped.split('/')..removeLast();
|
||||
return parts.isEmpty ? '' : '${parts.join('/')}/';
|
||||
}
|
||||
|
||||
String _joinPath(String folder, String name, {required bool isDirectory}) =>
|
||||
isDirectory ? '$folder$name/' : '$folder$name';
|
||||
|
||||
void _rename() {
|
||||
if (guardDemoAction(context)) return;
|
||||
showPromptDialog(
|
||||
@@ -138,9 +127,8 @@ class _FileElementState extends State<FileElement>
|
||||
initialValue: widget.file.name,
|
||||
onConfirm: (newName) async {
|
||||
if (newName.isEmpty || newName == widget.file.name) return;
|
||||
final parent = _parentPathOf(widget.file.path);
|
||||
final destination = _joinPath(
|
||||
parent,
|
||||
final destination = webdavJoin(
|
||||
webdavFolderPath(webdavParentSegmentsOf(widget.file.path)),
|
||||
newName,
|
||||
isDirectory: widget.file.isDirectory,
|
||||
);
|
||||
|
||||
@@ -1,24 +1,22 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
import '../../../../state/app/modules/settings/bloc/settings_cubit.dart';
|
||||
import '../data/sort_options.dart';
|
||||
|
||||
/// Sort direction and sort key pickers, read from and persisted to the file
|
||||
/// settings.
|
||||
class FilesSortActions extends StatelessWidget {
|
||||
final SortOption currentSort;
|
||||
final bool ascending;
|
||||
final ValueChanged<bool> onDirectionChanged;
|
||||
final ValueChanged<SortOption> onSortChanged;
|
||||
|
||||
const FilesSortActions({
|
||||
required this.currentSort,
|
||||
required this.ascending,
|
||||
required this.onDirectionChanged,
|
||||
required this.onSortChanged,
|
||||
super.key,
|
||||
});
|
||||
const FilesSortActions({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
final settings = context.read<SettingsCubit>();
|
||||
final (currentSort, ascending) = context.select(
|
||||
(SettingsCubit c) =>
|
||||
(c.state.fileSettings.sortBy, c.state.fileSettings.ascending),
|
||||
);
|
||||
return Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
@@ -46,7 +44,7 @@ class FilesSortActions extends StatelessWidget {
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
onSelected: onDirectionChanged,
|
||||
onSelected: (e) => settings.val(write: true).fileSettings.ascending = e,
|
||||
),
|
||||
PopupMenuButton<SortOption>(
|
||||
tooltip:
|
||||
@@ -70,7 +68,7 @@ class FilesSortActions extends StatelessWidget {
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
onSelected: onSortChanged,
|
||||
onSelected: (e) => settings.val(write: true).fileSettings.sortBy = e,
|
||||
),
|
||||
],
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user