diff --git a/app/assets/CHANGELOG.md b/app/assets/CHANGELOG.md index 33382f88..e54c55a3 100644 --- a/app/assets/CHANGELOG.md +++ b/app/assets/CHANGELOG.md @@ -1,5 +1,6 @@ ## 1.16.1 (unreleased) +- feat(desktop): highlight file when tapping "Show in folder" (@Tienisto) - fix(android): properly close app on back gesture (@Tienisto) ## 1.16.0 (2024-11-03) diff --git a/app/lib/pages/progress_page.dart b/app/lib/pages/progress_page.dart index f0eedcf6..d8919908 100644 --- a/app/lib/pages/progress_page.dart +++ b/app/lib/pages/progress_page.dart @@ -257,7 +257,7 @@ class _ProgressPageState extends State with Refena { ? null : (TapGestureRecognizer() ..onTap = () async { - await openFolder(receiveSession.destinationDirectory); + await openFolder(folderPath: receiveSession.destinationDirectory); }), ), ], diff --git a/app/lib/pages/receive_history_page.dart b/app/lib/pages/receive_history_page.dart index 34f60090..6ac0b0aa 100644 --- a/app/lib/pages/receive_history_page.dart +++ b/app/lib/pages/receive_history_page.dart @@ -17,6 +17,7 @@ import 'package:localsend_app/widget/dialogs/file_info_dialog.dart'; import 'package:localsend_app/widget/dialogs/history_clear_dialog.dart'; import 'package:localsend_app/widget/file_thumbnail.dart'; import 'package:localsend_app/widget/responsive_list_view.dart'; +import 'package:path/path.dart' as path; import 'package:refena_flutter/refena_flutter.dart'; import 'package:routerino/routerino.dart'; @@ -83,7 +84,7 @@ class ReceiveHistoryPage extends StatelessWidget { : () async { // ignore: use_build_context_synchronously final destination = context.read(settingsProvider).destination ?? await getDefaultDestinationDirectory(); - await openFolder(destination); + await openFolder(folderPath: destination); }, icon: const Icon(Icons.folder), label: Text(t.receiveHistoryPage.openFolder), @@ -178,7 +179,10 @@ class ReceiveHistoryPage extends StatelessWidget { break; case _EntryOption.showInFolder: if (entry.path != null) { - await openFolder(File(entry.path!).parent.path); + await openFolder( + folderPath: File(entry.path!).parent.path, + fileName: path.basename(entry.path!), + ); } break; case _EntryOption.info: diff --git a/app/lib/util/native/open_folder.dart b/app/lib/util/native/open_folder.dart index e66b6e58..1ecff588 100644 --- a/app/lib/util/native/open_folder.dart +++ b/app/lib/util/native/open_folder.dart @@ -1,19 +1,39 @@ +import 'package:flutter/foundation.dart'; import 'package:localsend_app/util/native/android_saf.dart'; +import 'package:localsend_app/util/native/platform_check.dart'; import 'package:logging/logging.dart'; +import 'package:open_dir/open_dir.dart'; import 'package:open_filex/open_filex.dart'; final _logger = Logger('OpenFolder'); -/// Opens the selected file which is stored on the device. -Future openFolder(String path) async { - if (path.startsWith('content://')) { - await openContentUri(uri: path); +/// Opens the folder and optionally selects the file in the folder. +Future openFolder({ + required String folderPath, + String? fileName, +}) async { + if (folderPath.startsWith('content://')) { + await openContentUri(uri: folderPath); return; } - if (!path.endsWith('/')) { - path = '$path/'; + if (fileName != null && checkPlatform([TargetPlatform.windows, TargetPlatform.linux, TargetPlatform.macOS])) { + // open folder and select file + + if (defaultTargetPlatform == TargetPlatform.windows) { + folderPath = folderPath.replaceAll('/', '\\'); + } + + final result = await OpenDir().openNativeDir(path: folderPath, highlightedFileName: fileName); + _logger.info('Open folder result: $result, path: $folderPath, file: $fileName'); + } else { + // only open folder + + if (!folderPath.endsWith('/')) { + folderPath = '$folderPath/'; + } + + final result = await OpenFilex.open(folderPath); + _logger.info('Open folder result: ${result.message}, path: $folderPath'); } - final result = await OpenFilex.open(path); - _logger.info('Open folder result: ${result.message}, path: $path'); } diff --git a/app/lib/widget/dialogs/open_file_dialog.dart b/app/lib/widget/dialogs/open_file_dialog.dart index 9abde2dc..7210b9e2 100644 --- a/app/lib/widget/dialogs/open_file_dialog.dart +++ b/app/lib/widget/dialogs/open_file_dialog.dart @@ -6,6 +6,7 @@ import 'package:flutter/material.dart'; import 'package:localsend_app/gen/strings.g.dart'; import 'package:localsend_app/util/native/open_file.dart'; import 'package:localsend_app/util/native/open_folder.dart'; +import 'package:path/path.dart' as path; import 'package:routerino/routerino.dart'; class OpenFileDialog extends StatefulWidget { @@ -74,7 +75,10 @@ class _OpenFileDialogState extends State { child: Text(t.general.open), ), TextButton( - onPressed: () async => await openFolder(File(widget.filePath).parent.path), + onPressed: () async => await openFolder( + folderPath: File(widget.filePath).parent.path, + fileName: path.basename(widget.filePath), + ), child: Text(t.receiveHistoryPage.entryActions.showInFolder), ), TextButton( diff --git a/app/linux/flutter/generated_plugin_registrant.cc b/app/linux/flutter/generated_plugin_registrant.cc index 05a23180..3e0b782e 100644 --- a/app/linux/flutter/generated_plugin_registrant.cc +++ b/app/linux/flutter/generated_plugin_registrant.cc @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -31,6 +32,9 @@ void fl_register_plugins(FlPluginRegistry* registry) { g_autoptr(FlPluginRegistrar) gtk_registrar = fl_plugin_registry_get_registrar_for_plugin(registry, "GtkPlugin"); gtk_plugin_register_with_registrar(gtk_registrar); + g_autoptr(FlPluginRegistrar) open_dir_linux_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "OpenDirLinuxPlugin"); + open_dir_linux_plugin_register_with_registrar(open_dir_linux_registrar); g_autoptr(FlPluginRegistrar) pasteboard_registrar = fl_plugin_registry_get_registrar_for_plugin(registry, "PasteboardPlugin"); pasteboard_plugin_register_with_registrar(pasteboard_registrar); diff --git a/app/linux/flutter/generated_plugins.cmake b/app/linux/flutter/generated_plugins.cmake index b6470b4b..bee52301 100644 --- a/app/linux/flutter/generated_plugins.cmake +++ b/app/linux/flutter/generated_plugins.cmake @@ -7,6 +7,7 @@ list(APPEND FLUTTER_PLUGIN_LIST dynamic_color file_selector_linux gtk + open_dir_linux pasteboard screen_retriever tray_manager diff --git a/app/macos/Flutter/GeneratedPluginRegistrant.swift b/app/macos/Flutter/GeneratedPluginRegistrant.swift index f7fd37ef..5ce465f4 100644 --- a/app/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/app/macos/Flutter/GeneratedPluginRegistrant.swift @@ -13,6 +13,7 @@ import file_selector_macos import gal import in_app_purchase_storekit import network_info_plus +import open_dir_macos import package_info_plus import pasteboard import path_provider_foundation @@ -35,6 +36,7 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { GalPlugin.register(with: registry.registrar(forPlugin: "GalPlugin")) InAppPurchasePlugin.register(with: registry.registrar(forPlugin: "InAppPurchasePlugin")) NetworkInfoPlusPlugin.register(with: registry.registrar(forPlugin: "NetworkInfoPlusPlugin")) + OpenDirMacosPlugin.register(with: registry.registrar(forPlugin: "OpenDirMacosPlugin")) FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FPPPackageInfoPlusPlugin")) PasteboardPlugin.register(with: registry.registrar(forPlugin: "PasteboardPlugin")) PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) diff --git a/app/pubspec.lock b/app/pubspec.lock index 221e5afb..7dae5ae6 100644 --- a/app/pubspec.lock +++ b/app/pubspec.lock @@ -985,6 +985,46 @@ packages: url: "https://pub.dev" source: hosted version: "2.0.2" + open_dir: + dependency: "direct main" + description: + name: open_dir + sha256: a4884b00e5e5795a9b4b3d582ac6a66e9196795ed760dbc3c63b4837c70c5901 + url: "https://pub.dev" + source: hosted + version: "0.0.2+1" + open_dir_linux: + dependency: transitive + description: + name: open_dir_linux + sha256: "566cd9e02403971be06af35e1abc8057a4f3f98888c1226042e96a2af333b8bc" + url: "https://pub.dev" + source: hosted + version: "0.0.2+1" + open_dir_macos: + dependency: transitive + description: + name: open_dir_macos + sha256: "51fdc8c3a06c9d571b599b5901045ada23d1440b24c3052c0a66cf3ee4ac901b" + url: "https://pub.dev" + source: hosted + version: "0.0.2" + open_dir_platform_interface: + dependency: transitive + description: + name: open_dir_platform_interface + sha256: ca189abb02d8e3320f9b2493b6d58e3a33f393d5eb4ccbbef02e0bc0fd393872 + url: "https://pub.dev" + source: hosted + version: "0.0.2" + open_dir_windows: + dependency: transitive + description: + name: open_dir_windows + sha256: ec48df32ce61adb6f6cede0330d13b0d89714d2ee2df198f32ecd520e3a5d250 + url: "https://pub.dev" + source: hosted + version: "0.0.2+1" open_filex: dependency: "direct main" description: diff --git a/app/pubspec.yaml b/app/pubspec.yaml index 1be124b9..5747d1e7 100644 --- a/app/pubspec.yaml +++ b/app/pubspec.yaml @@ -40,6 +40,7 @@ dependencies: mime: 1.0.6 nanoid2: 2.0.1 network_info_plus: 6.0.1 + open_dir: 0.0.2+1 open_filex: 4.5.0 package_info_plus: 8.0.2 pasteboard: 0.3.0 diff --git a/app/windows/flutter/generated_plugin_registrant.cc b/app/windows/flutter/generated_plugin_registrant.cc index 2bdb6d5c..afccbe9d 100644 --- a/app/windows/flutter/generated_plugin_registrant.cc +++ b/app/windows/flutter/generated_plugin_registrant.cc @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -31,6 +32,8 @@ void RegisterPlugins(flutter::PluginRegistry* registry) { registry->GetRegistrarForPlugin("FileSelectorWindows")); GalPluginCApiRegisterWithRegistrar( registry->GetRegistrarForPlugin("GalPluginCApi")); + OpenDirWindowsPluginCApiRegisterWithRegistrar( + registry->GetRegistrarForPlugin("OpenDirWindowsPluginCApi")); PasteboardPluginRegisterWithRegistrar( registry->GetRegistrarForPlugin("PasteboardPlugin")); PermissionHandlerWindowsPluginRegisterWithRegistrar( diff --git a/app/windows/flutter/generated_plugins.cmake b/app/windows/flutter/generated_plugins.cmake index ca04435f..ef63f221 100644 --- a/app/windows/flutter/generated_plugins.cmake +++ b/app/windows/flutter/generated_plugins.cmake @@ -8,6 +8,7 @@ list(APPEND FLUTTER_PLUGIN_LIST dynamic_color file_selector_windows gal + open_dir_windows pasteboard permission_handler_windows screen_retriever