[client] Cut/paste within app

This commit is contained in:
Abhishek Shroff
2024-09-06 22:11:13 +05:30
parent 1a638f739e
commit d64e068d0b
4 changed files with 65 additions and 21 deletions
+4 -7
View File
@@ -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(),
};
}
+57 -11
View File
@@ -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 -2
View File
@@ -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!);
}),
),
),
+3 -1
View File
@@ -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?',