mirror of
https://codeberg.org/shroff/phylum.git
synced 2026-05-11 14:49:32 -05:00
[client] Cut/paste within app
This commit is contained in:
@@ -26,12 +26,9 @@ class SelectAllIntent extends Intent {
|
||||
const SelectAllIntent();
|
||||
}
|
||||
|
||||
class CutToClipboardIntent extends Intent {
|
||||
const CutToClipboardIntent();
|
||||
}
|
||||
|
||||
class CopyToClipboardIntent extends Intent {
|
||||
const CopyToClipboardIntent();
|
||||
final bool cut;
|
||||
const CopyToClipboardIntent(this.cut);
|
||||
}
|
||||
|
||||
class PasteFromClipboardIntent extends Intent {
|
||||
@@ -82,8 +79,8 @@ Map<ShortcutActivator, Intent> getAppShortcuts() {
|
||||
SingleActivator(LogicalKeyboardKey.keyN, control: true): NewFolderIntent(),
|
||||
SingleActivator(LogicalKeyboardKey.keyU, control: true): UploadFilesIntent(),
|
||||
SingleActivator(LogicalKeyboardKey.keyU, control: true, shift: true): UploadFolderIntent(),
|
||||
SingleActivator(LogicalKeyboardKey.keyC, control: true): CopyToClipboardIntent(),
|
||||
SingleActivator(LogicalKeyboardKey.keyX, control: true): CutToClipboardIntent(),
|
||||
SingleActivator(LogicalKeyboardKey.keyC, control: true): CopyToClipboardIntent(false),
|
||||
SingleActivator(LogicalKeyboardKey.keyX, control: true): CopyToClipboardIntent(true),
|
||||
SingleActivator(LogicalKeyboardKey.keyV, control: true): PasteFromClipboardIntent(),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:phylum/app_shortcuts.dart';
|
||||
import 'package:phylum/libphylum/actions/action_resource_delete.dart';
|
||||
import 'package:phylum/libphylum/actions/action_resource_move.dart';
|
||||
import 'package:phylum/libphylum/db/db.dart';
|
||||
import 'package:phylum/libphylum/phylum_account.dart';
|
||||
import 'package:phylum/ui/folder/folder_navigator_stack.dart';
|
||||
@@ -16,9 +17,10 @@ import 'package:provider/provider.dart';
|
||||
import 'package:super_clipboard/super_clipboard.dart';
|
||||
|
||||
class FolderContentsView extends StatefulWidget {
|
||||
final String folderId;
|
||||
final List<Resource> resources;
|
||||
|
||||
const FolderContentsView({super.key, required this.resources});
|
||||
FolderContentsView({required this.folderId, required this.resources}) : super(key: ValueKey(folderId));
|
||||
|
||||
@override
|
||||
State<FolderContentsView> createState() => _FolderContentsViewState();
|
||||
@@ -114,26 +116,70 @@ class _FolderContentsViewState extends State<FolderContentsView> {
|
||||
deleteSelected();
|
||||
return null;
|
||||
}),
|
||||
CopyToClipboardIntent: CallbackAction<CopyToClipboardIntent>(onInvoke: (i) async {
|
||||
final account = context.read<PhylumAccount>();
|
||||
final selectedIds = context.read<FolderSelectionState>().selected;
|
||||
if (selectedIds.isEmpty) return;
|
||||
final selected = resources.where((r) => selectedIds.contains(r.id)).toList(growable: false);
|
||||
final items = selected.map((r) {
|
||||
final uri = account.api.createUriBuilder('/open');
|
||||
uri.queryParameters['id'] = r.id;
|
||||
if (i.cut) {
|
||||
uri.queryParameters['cut'] = 'y';
|
||||
}
|
||||
final uriData = Formats.uri(NamedUri(uri.build(), name: r.name));
|
||||
return DataWriterItem(suggestedName: r.name)..add(uriData);
|
||||
});
|
||||
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text('${items.length} items ${i.cut ? 'cut' : 'copied'} to clipboard')));
|
||||
await SystemClipboard.instance?.write(items);
|
||||
return null;
|
||||
}),
|
||||
PasteFromClipboardIntent: CallbackAction<PasteFromClipboardIntent>(onInvoke: (i) async {
|
||||
final account = context.read<PhylumAccount>();
|
||||
final openUri = account.api.createUri('/open');
|
||||
final clipboard = SystemClipboard.instance;
|
||||
if (clipboard == null) {
|
||||
return;
|
||||
}
|
||||
final reader = await clipboard.read();
|
||||
final files = reader.items.where((item) => item.canProvide(Formats.fileUri));
|
||||
final paths = <String>[];
|
||||
if (files.isNotEmpty) {
|
||||
for (final item in reader.items) {
|
||||
final c = Completer<String?>();
|
||||
item.getValue(Formats.fileUri, (value) => c.complete(value?.toFilePath()), onError: (value) => c.complete(null));
|
||||
final path = await c.future;
|
||||
if (path != null) {
|
||||
paths.add(path);
|
||||
final cutResources = <Resource>[];
|
||||
final copyResources = <Resource>[];
|
||||
for (final item in reader.items) {
|
||||
final c = Completer<String?>();
|
||||
item.getValue(Formats.fileUri, (value) => c.complete(value?.toFilePath()), onError: (value) => c.complete(null));
|
||||
final filePath = await c.future;
|
||||
if (filePath != null) {
|
||||
paths.add(filePath);
|
||||
} else {
|
||||
final c = Completer<Uri?>();
|
||||
item.getValue(Formats.uri, (value) {
|
||||
c.complete(value?.uri);
|
||||
}, onError: (value) => c.complete(null));
|
||||
final uri = await c.future;
|
||||
if (uri != null) {
|
||||
if (uri.authority == openUri.authority && uri.path == openUri.path && uri.queryParameters.containsKey('id')) {
|
||||
final resourceId = uri.queryParameters['id']!;
|
||||
final cut = uri.queryParameters.containsKey('cut');
|
||||
final resource = await account.datastore.db.managers.resources.filter((f) => f.id.equals(resourceId)).getSingleOrNull();
|
||||
if (resource != null) {
|
||||
(cut ? cutResources : copyResources).add(resource);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!context.mounted) return;
|
||||
uploadRecursive(context, context.read<FolderNavigatorState>().folderId, paths);
|
||||
}
|
||||
if (!context.mounted) return;
|
||||
uploadRecursive(context, widget.folderId, paths);
|
||||
final parent = await account.datastore.db.managers.resources.filter((f) => f.id.equals(widget.folderId)).getSingleOrNull();
|
||||
if (parent != null) {
|
||||
for (final r in cutResources) {
|
||||
account.addAction(ResourceMoveAction(r: r, parent: parent));
|
||||
}
|
||||
}
|
||||
|
||||
await SystemClipboard.instance?.write([]);
|
||||
|
||||
return null;
|
||||
}),
|
||||
},
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_state_notifier/flutter_state_notifier.dart';
|
||||
import 'package:phylum/libphylum/phylum_account.dart';
|
||||
import 'package:phylum/libphylum/requests/resource_detail_request.dart';
|
||||
@@ -50,7 +49,7 @@ class _FolderViewState extends State<FolderView> {
|
||||
if (!snapshot.hasData) {
|
||||
return Container();
|
||||
}
|
||||
return FolderContentsView(resources: snapshot.data!);
|
||||
return FolderContentsView(folderId: widget.id, resources: snapshot.data!);
|
||||
}),
|
||||
),
|
||||
),
|
||||
|
||||
@@ -42,8 +42,10 @@ Future<void> uploadRecursive(BuildContext context, String folderId, Iterable<Str
|
||||
tree = tree?.mergeWith(stat) ?? stat;
|
||||
}
|
||||
|
||||
if (tree == null || !context.mounted) return;
|
||||
if (!context.mounted) return;
|
||||
Navigator.of(context).pop();
|
||||
|
||||
if (tree == null) return;
|
||||
final confirm = await showAlertDialog(
|
||||
context,
|
||||
title: 'Upload Files?',
|
||||
|
||||
Reference in New Issue
Block a user