mirror of
https://codeberg.org/shroff/phylum.git
synced 2026-01-05 19:21:23 -06:00
[client] Paste files from clipboard
This commit is contained in:
@@ -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));
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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';
|
||||
|
||||
|
||||
@@ -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;
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"))
|
||||
}
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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"));
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user