[client] Paste files from clipboard

This commit is contained in:
Abhishek Shroff
2024-09-06 12:06:40 +05:30
parent 4a1b7c29d1
commit fd28fad15d
12 changed files with 159 additions and 21 deletions

View File

@@ -3,6 +3,7 @@ import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter_state_notifier/flutter_state_notifier.dart';
import 'package:hive/hive.dart';
import 'package:logger/logger.dart';
import 'package:offtheline/offtheline.dart';
import 'package:path/path.dart' as path;
import 'package:path_provider/path_provider.dart';
@@ -21,6 +22,8 @@ void main() async {
? Directory(path.join((await getApplicationSupportDirectory()).path, storageDir.isNotEmpty ? storageDir : 'data'))
: await getApplicationDocumentsDirectory();
OTL.logger = Logger();
await appDir.create();
Hive.init(appDir.path);
Hive.registerAdapter(ApiActionTypeAdapter(actionDeserializers));

View File

@@ -1,3 +1,4 @@
import 'dart:async';
import 'dart:math';
import 'package:flutter/material.dart';
@@ -9,8 +10,10 @@ import 'package:phylum/libphylum/phylum_account.dart';
import 'package:phylum/ui/folder/folder_navigator_stack.dart';
import 'package:phylum/ui/folder/folder_selection_manager.dart';
import 'package:phylum/ui/folder/resource_details_row.dart';
import 'package:phylum/util/upload_utils.dart';
import 'package:phylum/util/dialogs.dart';
import 'package:provider/provider.dart';
import 'package:super_clipboard/super_clipboard.dart';
class FolderContentsView extends StatefulWidget {
final List<Resource> resources;
@@ -111,6 +114,28 @@ class _FolderContentsViewState extends State<FolderContentsView> {
deleteSelected();
return null;
}),
PasteFromClipboardIntent: CallbackAction<PasteFromClipboardIntent>(onInvoke: (i) async {
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);
}
}
if (!context.mounted) return;
uploadFilesAndDirs(context, context.read<FolderNavigatorState>().folderId, paths);
}
return null;
}),
},
child: Focus(
autofocus: true,

View File

@@ -5,7 +5,7 @@ import 'package:phylum/ui/common/expandable_fab.dart';
import 'package:phylum/ui/folder/folder_heriarchy_view.dart';
import 'package:phylum/ui/folder/nav_list.dart';
import 'package:phylum/ui/folder/folder_view.dart';
import 'package:phylum/ui/folder/upload_utils.dart';
import 'package:phylum/util/upload_utils.dart';
import 'package:provider/provider.dart';
import 'folder_navigator_stack.dart';

View File

@@ -1,6 +1,6 @@
import 'package:flutter/material.dart';
import 'package:offtheline/offtheline.dart';
import 'package:phylum/ui/folder/upload_utils.dart';
import 'package:phylum/util/upload_utils.dart';
import 'package:phylum/util/dialogs.dart';
import 'package:provider/provider.dart';

View File

@@ -22,27 +22,31 @@ void createDirectory(BuildContext context, String folderId) async {
}
void pickAndUploadFiles(BuildContext context, String folderId) async {
final account = context.read<PhylumAccount>();
final files = await openFiles();
for (final file in files) {
final action = await ResourceUploadAction.fromPath(parent: folderId, resourceName: file.name, path: file.path);
await account.addAction(action);
}
if (!context.mounted) return;
uploadFilesAndDirs(context, folderId, files.map((f) => f.path));
}
void pickAndUploadDirectory(BuildContext context, String folderId) async {
final account = context.read<PhylumAccount>();
final path = await getDirectoryPath();
if (path == null) return;
if (!context.mounted) return;
showProgressDialog(context, message: 'Counting Files');
final tree = await DirTree.stat(path);
if (path == null || !context.mounted) return;
uploadFilesAndDirs(context, folderId, [path]);
}
if (!context.mounted) return;
Future<void> uploadFilesAndDirs(BuildContext context, String folderId, Iterable<String> paths) async {
final account = context.read<PhylumAccount>();
showProgressDialog(context, message: 'Counting Files');
DirTree? tree;
for (final path in paths) {
final stat = await DirTree.stat(path);
tree = tree?.mergeWith(stat) ?? stat;
}
if (tree == null || !context.mounted) return;
Navigator.of(context).pop();
final confirm = await showAlertDialog(
context,
title: 'Upload Folder?',
title: 'Upload Files?',
message: 'Found ${tree.files.length} files and ${tree.dirs.length} directories.\n\nTotal Upload Size: ${tree.totalSize.getHumanString()}',
positiveText: 'YES',
negativeText: 'NO',
@@ -50,12 +54,10 @@ void pickAndUploadDirectory(BuildContext context, String folderId) async {
false;
if (!confirm || !context.mounted) return;
final dirIds = <String, String>{};
dirIds[path] = uuid.v4();
account.addAction(ResourceMkdirAction(id: dirIds[path]!, parent: folderId, resourceName: p.basename(path)));
for (final dir in tree.dirs) {
dirIds[dir] = uuid.v4();
final parent = p.dirname(dir);
account.addAction(ResourceMkdirAction(id: dirIds[dir]!, parent: dirIds[parent]!, resourceName: p.basename(dir)));
account.addAction(ResourceMkdirAction(id: dirIds[dir]!, parent: dirIds[parent] ?? folderId, resourceName: p.basename(dir)));
}
for (final file in tree.files) {
final parent = p.dirname(file);
@@ -79,11 +81,21 @@ class DirTree {
required this.dirs,
});
DirTree mergeWith(DirTree other) {
return DirTree(
numFiles: numFiles + other.numFiles,
numDirs: numDirs + other.numDirs,
totalSize: totalSize + other.totalSize,
files: files..addAll(other.files),
dirs: dirs..addAll(other.dirs),
);
}
static Future<DirTree> stat(String path) {
final dir = Directory(path);
final completer = Completer<DirTree>();
final files = <String>[];
final dirs = <String>[];
final dirs = <String>[path];
int numFiles = 0;
int numDirs = 0;
int totalSize = 0;

View File

@@ -7,13 +7,21 @@
#include "generated_plugin_registrant.h"
#include <file_selector_linux/file_selector_plugin.h>
#include <irondash_engine_context/irondash_engine_context_plugin.h>
#include <sqlite3_flutter_libs/sqlite3_flutter_libs_plugin.h>
#include <super_native_extensions/super_native_extensions_plugin.h>
void fl_register_plugins(FlPluginRegistry* registry) {
g_autoptr(FlPluginRegistrar) file_selector_linux_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "FileSelectorPlugin");
file_selector_plugin_register_with_registrar(file_selector_linux_registrar);
g_autoptr(FlPluginRegistrar) irondash_engine_context_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "IrondashEngineContextPlugin");
irondash_engine_context_plugin_register_with_registrar(irondash_engine_context_registrar);
g_autoptr(FlPluginRegistrar) sqlite3_flutter_libs_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "Sqlite3FlutterLibsPlugin");
sqlite3_flutter_libs_plugin_register_with_registrar(sqlite3_flutter_libs_registrar);
g_autoptr(FlPluginRegistrar) super_native_extensions_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "SuperNativeExtensionsPlugin");
super_native_extensions_plugin_register_with_registrar(super_native_extensions_registrar);
}

View File

@@ -4,7 +4,9 @@
list(APPEND FLUTTER_PLUGIN_LIST
file_selector_linux
irondash_engine_context
sqlite3_flutter_libs
super_native_extensions
)
list(APPEND FLUTTER_FFI_PLUGIN_LIST

View File

@@ -5,12 +5,18 @@
import FlutterMacOS
import Foundation
import device_info_plus
import file_selector_macos
import irondash_engine_context
import path_provider_foundation
import sqlite3_flutter_libs
import super_native_extensions
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
DeviceInfoPlusMacosPlugin.register(with: registry.registrar(forPlugin: "DeviceInfoPlusMacosPlugin"))
FileSelectorPlugin.register(with: registry.registrar(forPlugin: "FileSelectorPlugin"))
IrondashEngineContextPlugin.register(with: registry.registrar(forPlugin: "IrondashEngineContextPlugin"))
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
Sqlite3FlutterLibsPlugin.register(with: registry.registrar(forPlugin: "Sqlite3FlutterLibsPlugin"))
SuperNativeExtensionsPlugin.register(with: registry.registrar(forPlugin: "SuperNativeExtensionsPlugin"))
}

View File

@@ -206,6 +206,22 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.3.6"
device_info_plus:
dependency: transitive
description:
name: device_info_plus
sha256: a7fd703482b391a87d60b6061d04dfdeab07826b96f9abd8f5ed98068acc0074
url: "https://pub.dev"
source: hosted
version: "10.1.2"
device_info_plus_platform_interface:
dependency: transitive
description:
name: device_info_plus_platform_interface
sha256: "282d3cf731045a2feb66abfe61bbc40870ae50a3ed10a4d3d217556c35c8c2ba"
url: "https://pub.dev"
source: hosted
version: "7.0.1"
drift:
dependency: "direct main"
description:
@@ -429,6 +445,22 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.0.4"
irondash_engine_context:
dependency: transitive
description:
name: irondash_engine_context
sha256: cd7b769db11a2b5243b037c8a9b1ecaef02e1ae27a2d909ffa78c1dad747bb10
url: "https://pub.dev"
source: hosted
version: "0.5.4"
irondash_message_channel:
dependency: transitive
description:
name: irondash_message_channel
sha256: b4101669776509c76133b8917ab8cfc704d3ad92a8c450b92934dd8884a2f060
url: "https://pub.dev"
source: hosted
version: "0.7.0"
js:
dependency: transitive
description:
@@ -478,7 +510,7 @@ packages:
source: hosted
version: "4.0.0"
logger:
dependency: transitive
dependency: "direct main"
description:
name: logger
sha256: "697d067c60c20999686a0add96cf6aba723b3aa1f83ecf806a8097231529ec32"
@@ -612,6 +644,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.3.0"
pixel_snap:
dependency: transitive
description:
name: pixel_snap
sha256: "677410ea37b07cd37ecb6d5e6c0d8d7615a7cf3bd92ba406fd1ac57e937d1fb0"
url: "https://pub.dev"
source: hosted
version: "0.1.5"
platform:
dependency: transitive
description:
@@ -785,6 +825,22 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.2.0"
super_clipboard:
dependency: "direct main"
description:
name: super_clipboard
sha256: "74098001413e075cc53dee72b68c32eaffc10709df41806800393abaa6dac9d5"
url: "https://pub.dev"
source: hosted
version: "0.8.19"
super_native_extensions:
dependency: transitive
description:
name: super_native_extensions
sha256: c24676825c9f3ae844676a843d45ad186f2270539ffe72be4277753e46d14e29
url: "https://pub.dev"
source: hosted
version: "0.8.19"
term_glyph:
dependency: transitive
description:
@@ -881,6 +937,22 @@ packages:
url: "https://pub.dev"
source: hosted
version: "3.0.1"
win32:
dependency: transitive
description:
name: win32
sha256: "68d1e89a91ed61ad9c370f9f8b6effed9ae5e0ede22a270bdfa6daf79fc2290a"
url: "https://pub.dev"
source: hosted
version: "5.5.4"
win32_registry:
dependency: transitive
description:
name: win32_registry
sha256: "723b7f851e5724c55409bb3d5a32b203b3afe8587eaf5dafb93a5fed8ecda0d6"
url: "https://pub.dev"
source: hosted
version: "1.1.4"
xdg_directories:
dependency: transitive
description:

View File

@@ -1,5 +1,5 @@
name: phylum
description: "Self-hosted file storage"
description: Self-hosted file storage
publish_to: 'none'
version: 0.0.1+1
@@ -11,19 +11,21 @@ dependencies:
sdk: flutter
drift: ^2.19.1+1
drift_flutter: ^0.1.0
file_selector: ^1.0.3
flutter_state_notifier:
go_router:
hive:
http:
logger:
offtheline:
path: ../../offtheline
state_notifier:
path:
path_provider:
provider:
super_clipboard:
uri:
uuid:
file_selector: ^1.0.3
dev_dependencies:
flutter_test:

View File

@@ -7,11 +7,17 @@
#include "generated_plugin_registrant.h"
#include <file_selector_windows/file_selector_windows.h>
#include <irondash_engine_context/irondash_engine_context_plugin_c_api.h>
#include <sqlite3_flutter_libs/sqlite3_flutter_libs_plugin.h>
#include <super_native_extensions/super_native_extensions_plugin_c_api.h>
void RegisterPlugins(flutter::PluginRegistry* registry) {
FileSelectorWindowsRegisterWithRegistrar(
registry->GetRegistrarForPlugin("FileSelectorWindows"));
IrondashEngineContextPluginCApiRegisterWithRegistrar(
registry->GetRegistrarForPlugin("IrondashEngineContextPluginCApi"));
Sqlite3FlutterLibsPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("Sqlite3FlutterLibsPlugin"));
SuperNativeExtensionsPluginCApiRegisterWithRegistrar(
registry->GetRegistrarForPlugin("SuperNativeExtensionsPluginCApi"));
}

View File

@@ -4,7 +4,9 @@
list(APPEND FLUTTER_PLUGIN_LIST
file_selector_windows
irondash_engine_context
sqlite3_flutter_libs
super_native_extensions
)
list(APPEND FLUTTER_FFI_PLUGIN_LIST