Compare commits

...

2 Commits

Author SHA1 Message Date
d7746d14e4 🚀 Launch 3.5.0+151 2025-12-06 21:52:30 +08:00
648d5225f6 🐛 Ensure mobile site management request permission 2025-12-06 21:48:16 +08:00
7 changed files with 146 additions and 90 deletions

View File

@@ -12,6 +12,8 @@
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" android:maxSdkVersion="30" /> <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" android:maxSdkVersion="30" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
android:maxSdkVersion="29" /> android:maxSdkVersion="29" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" /> <uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" /> <uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />

View File

@@ -257,6 +257,8 @@ PODS:
- path_provider_foundation (0.0.1): - path_provider_foundation (0.0.1):
- Flutter - Flutter
- FlutterMacOS - FlutterMacOS
- permission_handler_apple (9.3.0):
- Flutter
- pointer_interceptor_ios (0.0.1): - pointer_interceptor_ios (0.0.1):
- Flutter - Flutter
- PromisesObjC (2.4.0) - PromisesObjC (2.4.0)
@@ -351,6 +353,7 @@ DEPENDENCIES:
- package_info_plus (from `.symlinks/plugins/package_info_plus/ios`) - package_info_plus (from `.symlinks/plugins/package_info_plus/ios`)
- pasteboard (from `.symlinks/plugins/pasteboard/ios`) - pasteboard (from `.symlinks/plugins/pasteboard/ios`)
- path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`) - path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`)
- permission_handler_apple (from `.symlinks/plugins/permission_handler_apple/ios`)
- pointer_interceptor_ios (from `.symlinks/plugins/pointer_interceptor_ios/ios`) - pointer_interceptor_ios (from `.symlinks/plugins/pointer_interceptor_ios/ios`)
- protocol_handler_ios (from `.symlinks/plugins/protocol_handler_ios/ios`) - protocol_handler_ios (from `.symlinks/plugins/protocol_handler_ios/ios`)
- receive_sharing_intent (from `.symlinks/plugins/receive_sharing_intent/ios`) - receive_sharing_intent (from `.symlinks/plugins/receive_sharing_intent/ios`)
@@ -458,6 +461,8 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/pasteboard/ios" :path: ".symlinks/plugins/pasteboard/ios"
path_provider_foundation: path_provider_foundation:
:path: ".symlinks/plugins/path_provider_foundation/darwin" :path: ".symlinks/plugins/path_provider_foundation/darwin"
permission_handler_apple:
:path: ".symlinks/plugins/permission_handler_apple/ios"
pointer_interceptor_ios: pointer_interceptor_ios:
:path: ".symlinks/plugins/pointer_interceptor_ios/ios" :path: ".symlinks/plugins/pointer_interceptor_ios/ios"
protocol_handler_ios: protocol_handler_ios:
@@ -539,6 +544,7 @@ SPEC CHECKSUMS:
package_info_plus: af8e2ca6888548050f16fa2f1938db7b5a5df499 package_info_plus: af8e2ca6888548050f16fa2f1938db7b5a5df499
pasteboard: 49088aeb6119d51f976a421db60d8e1ab079b63c pasteboard: 49088aeb6119d51f976a421db60d8e1ab079b63c
path_provider_foundation: bb55f6dbba17d0dccd6737fe6f7f34fbd0376880 path_provider_foundation: bb55f6dbba17d0dccd6737fe6f7f34fbd0376880
permission_handler_apple: 4ed2196e43d0651e8ff7ca3483a069d469701f2d
pointer_interceptor_ios: da06a662d5bfd329602b45b2ab41bc0fb5fdb0f0 pointer_interceptor_ios: da06a662d5bfd329602b45b2ab41bc0fb5fdb0f0
PromisesObjC: f5707f49cb48b9636751c5b2e7d227e43fba9f47 PromisesObjC: f5707f49cb48b9636751c5b2e7d227e43fba9f47
PromisesSwift: 9d77319bbe72ebf6d872900551f7eeba9bce2851 PromisesSwift: 9d77319bbe72ebf6d872900551f7eeba9bce2851

View File

@@ -1,6 +1,7 @@
import 'dart:io'; import 'dart:io';
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:file_picker/file_picker.dart'; import 'package:file_picker/file_picker.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:gap/gap.dart'; import 'package:gap/gap.dart';
@@ -10,6 +11,7 @@ import 'package:island/pods/site_files.dart';
import 'package:island/widgets/alert.dart'; import 'package:island/widgets/alert.dart';
import 'package:island/widgets/sites/file_upload_dialog.dart'; import 'package:island/widgets/sites/file_upload_dialog.dart';
import 'package:island/widgets/sites/file_item.dart'; import 'package:island/widgets/sites/file_item.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:material_symbols_icons/symbols.dart'; import 'package:material_symbols_icons/symbols.dart';
import 'package:path/path.dart' as p; import 'package:path/path.dart' as p;
@@ -53,6 +55,9 @@ class FileManagementSection extends HookConsumerWidget {
PopupMenuButton<String>( PopupMenuButton<String>(
icon: const Icon(Symbols.upload), icon: const Icon(Symbols.upload),
onSelected: (String choice) async { onSelected: (String choice) async {
if (!kIsWeb) {
await Permission.storage.request();
}
List<File> files = []; List<File> files = [];
List<Map<String, dynamic>>? results; List<Map<String, dynamic>>? results;
if (choice == 'files') { if (choice == 'files') {
@@ -65,17 +70,17 @@ class FileManagementSection extends HookConsumerWidget {
selectedFiles.files.isEmpty) { selectedFiles.files.isEmpty) {
return; // User canceled return; // User canceled
} }
files = files = selectedFiles.files
selectedFiles.files .map((f) => File(f.path!))
.map((f) => File(f.path!)) .toList();
.toList();
} else if (choice == 'folder') { } else if (choice == 'folder') {
final dirPath = final dirPath = await FilePicker.platform
await FilePicker.platform.getDirectoryPath(); .getDirectoryPath();
if (dirPath == null) return; if (dirPath == null) return;
results = await _getFilesRecursive(dirPath); results = await _getFilesRecursive(dirPath);
files = files = results
results.map((m) => m['file'] as File).toList(); .map((m) => m['file'] as File)
.toList();
if (files.isEmpty) { if (files.isEmpty) {
showSnackBar('noFilesFoundInFolder'.tr()); showSnackBar('noFilesFoundInFolder'.tr());
return; return;
@@ -88,51 +93,46 @@ class FileManagementSection extends HookConsumerWidget {
showModalBottomSheet( showModalBottomSheet(
context: context, context: context,
isScrollControlled: true, isScrollControlled: true,
builder: builder: (context) => FileUploadDialog(
(context) => FileUploadDialog( selectedFiles: files,
selectedFiles: files, site: site,
site: site, relativePaths: results
relativePaths: ?.map((m) => m['relativePath'] as String)
results .toList(),
?.map( onUploadComplete: () {
(m) => m['relativePath'] as String, // Refresh file list
) ref.invalidate(
.toList(), siteFilesProvider(
onUploadComplete: () { siteId: site.id,
// Refresh file list path: currentPath.value,
ref.invalidate( ),
siteFilesProvider( );
siteId: site.id, },
path: currentPath.value, ),
),
);
},
),
); );
}, },
itemBuilder: itemBuilder: (BuildContext context) => [
(BuildContext context) => [ PopupMenuItem<String>(
PopupMenuItem<String>( value: 'files',
value: 'files', child: Row(
child: Row( children: [
children: [ Icon(Symbols.file_copy),
Icon(Symbols.file_copy), Gap(12),
Gap(12), Text('siteFiles'.tr()),
Text('siteFiles'.tr()), ],
], ),
), ),
), PopupMenuItem<String>(
PopupMenuItem<String>( value: 'folder',
value: 'folder', child: Row(
child: Row( children: [
children: [ Icon(Symbols.folder),
Icon(Symbols.folder), Gap(12),
Gap(12), Text('siteFolder'.tr()),
Text('siteFolder'.tr()), ],
], ),
), ),
), ],
],
style: ButtonStyle( style: ButtonStyle(
visualDensity: const VisualDensity( visualDensity: const VisualDensity(
horizontal: -4, horizontal: -4,
@@ -156,19 +156,17 @@ class FileManagementSection extends HookConsumerWidget {
IconButton( IconButton(
icon: Icon(Symbols.arrow_back), icon: Icon(Symbols.arrow_back),
onPressed: () { onPressed: () {
final pathParts = final pathParts = currentPath.value!
currentPath.value! .split('/')
.split('/') .where((part) => part.isNotEmpty)
.where((part) => part.isNotEmpty) .toList();
.toList();
if (pathParts.isEmpty) { if (pathParts.isEmpty) {
currentPath.value = null; currentPath.value = null;
} else { } else {
pathParts.removeLast(); pathParts.removeLast();
currentPath.value = currentPath.value = pathParts.isEmpty
pathParts.isEmpty ? null
? null : pathParts.join('/');
: pathParts.join('/');
} }
}, },
visualDensity: const VisualDensity( visualDensity: const VisualDensity(
@@ -185,11 +183,10 @@ class FileManagementSection extends HookConsumerWidget {
child: Text('siteRoot'.tr()), child: Text('siteRoot'.tr()),
), ),
...() { ...() {
final parts = final parts = currentPath.value!
currentPath.value! .split('/')
.split('/') .where((part) => part.isNotEmpty)
.where((part) => part.isNotEmpty) .toList();
.toList();
final widgets = <Widget>[]; final widgets = <Widget>[];
String currentBuilder = ''; String currentBuilder = '';
for (final part in parts) { for (final part in parts) {
@@ -200,8 +197,8 @@ class FileManagementSection extends HookConsumerWidget {
widgets.addAll([ widgets.addAll([
const Text(' / '), const Text(' / '),
InkWell( InkWell(
onTap: onTap: () =>
() => currentPath.value = pathToSet, currentPath.value = pathToSet,
child: Text(part), child: Text(part),
), ),
]); ]);
@@ -253,33 +250,31 @@ class FileManagementSection extends HookConsumerWidget {
return FileItem( return FileItem(
file: file, file: file,
site: site, site: site,
onNavigateDirectory: onNavigateDirectory: (path) =>
(path) => currentPath.value = path, currentPath.value = path,
); );
}, },
); );
}, },
loading: loading: () =>
() => const Center(child: CircularProgressIndicator()), const Center(child: CircularProgressIndicator()),
error: error: (error, stack) => Center(
(error, stack) => Center( child: Column(
child: Column( children: [
children: [ Text('failedToLoadFiles'.tr()),
Text('failedToLoadFiles'.tr()), const Gap(8),
const Gap(8), ElevatedButton(
ElevatedButton( onPressed: () => ref.invalidate(
onPressed: siteFilesProvider(
() => ref.invalidate( siteId: site.id,
siteFilesProvider( path: currentPath.value,
siteId: site.id,
path: currentPath.value,
),
),
child: Text('retry'.tr()),
), ),
], ),
child: Text('retry'.tr()),
), ),
), ],
),
),
), ),
], ],
), ),

View File

@@ -1917,6 +1917,54 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.1.0+3" version: "3.1.0+3"
permission_handler:
dependency: "direct main"
description:
name: permission_handler
sha256: bc917da36261b00137bbc8896bf1482169cd76f866282368948f032c8c1caae1
url: "https://pub.dev"
source: hosted
version: "12.0.1"
permission_handler_android:
dependency: transitive
description:
name: permission_handler_android
sha256: "1e3bc410ca1bf84662104b100eb126e066cb55791b7451307f9708d4007350e6"
url: "https://pub.dev"
source: hosted
version: "13.0.1"
permission_handler_apple:
dependency: transitive
description:
name: permission_handler_apple
sha256: f000131e755c54cf4d84a5d8bd6e4149e262cc31c5a8b1d698de1ac85fa41023
url: "https://pub.dev"
source: hosted
version: "9.4.7"
permission_handler_html:
dependency: transitive
description:
name: permission_handler_html
sha256: "38f000e83355abb3392140f6bc3030660cfaef189e1f87824facb76300b4ff24"
url: "https://pub.dev"
source: hosted
version: "0.1.3+5"
permission_handler_platform_interface:
dependency: transitive
description:
name: permission_handler_platform_interface
sha256: eb99b295153abce5d683cac8c02e22faab63e50679b937fa1bf67d58bb282878
url: "https://pub.dev"
source: hosted
version: "4.3.0"
permission_handler_windows:
dependency: transitive
description:
name: permission_handler_windows
sha256: "1a790728016f79a41216d88672dbc5df30e686e811ad4e698bfc51f76ad91f1e"
url: "https://pub.dev"
source: hosted
version: "0.2.1"
petitparser: petitparser:
dependency: transitive dependency: transitive
description: description:

View File

@@ -16,7 +16,7 @@ publish_to: "none" # Remove this line if you wish to publish to pub.dev
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
# In Windows, build-name is used as the major, minor, and patch parts # In Windows, build-name is used as the major, minor, and patch parts
# of the product and file versions while build-number is used as the build suffix. # of the product and file versions while build-number is used as the build suffix.
version: 3.5.0+150 version: 3.5.0+151
environment: environment:
sdk: ^3.8.0 sdk: ^3.8.0
@@ -171,6 +171,7 @@ dependencies:
http_parser: ^4.1.2 http_parser: ^4.1.2
flutter_code_editor: ^0.3.5 flutter_code_editor: ^0.3.5
skeletonizer: ^2.1.1 skeletonizer: ^2.1.1
permission_handler: ^12.0.1
dev_dependencies: dev_dependencies:
flutter_test: flutter_test:

View File

@@ -24,6 +24,7 @@
#include <media_kit_libs_windows_video/media_kit_libs_windows_video_plugin_c_api.h> #include <media_kit_libs_windows_video/media_kit_libs_windows_video_plugin_c_api.h>
#include <media_kit_video/media_kit_video_plugin_c_api.h> #include <media_kit_video/media_kit_video_plugin_c_api.h>
#include <pasteboard/pasteboard_plugin.h> #include <pasteboard/pasteboard_plugin.h>
#include <permission_handler_windows/permission_handler_windows_plugin.h>
#include <protocol_handler_windows/protocol_handler_windows_plugin_c_api.h> #include <protocol_handler_windows/protocol_handler_windows_plugin_c_api.h>
#include <record_windows/record_windows_plugin_c_api.h> #include <record_windows/record_windows_plugin_c_api.h>
#include <screen_retriever_windows/screen_retriever_windows_plugin_c_api.h> #include <screen_retriever_windows/screen_retriever_windows_plugin_c_api.h>
@@ -73,6 +74,8 @@ void RegisterPlugins(flutter::PluginRegistry* registry) {
registry->GetRegistrarForPlugin("MediaKitVideoPluginCApi")); registry->GetRegistrarForPlugin("MediaKitVideoPluginCApi"));
PasteboardPluginRegisterWithRegistrar( PasteboardPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("PasteboardPlugin")); registry->GetRegistrarForPlugin("PasteboardPlugin"));
PermissionHandlerWindowsPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("PermissionHandlerWindowsPlugin"));
ProtocolHandlerWindowsPluginCApiRegisterWithRegistrar( ProtocolHandlerWindowsPluginCApiRegisterWithRegistrar(
registry->GetRegistrarForPlugin("ProtocolHandlerWindowsPluginCApi")); registry->GetRegistrarForPlugin("ProtocolHandlerWindowsPluginCApi"));
RecordWindowsPluginCApiRegisterWithRegistrar( RecordWindowsPluginCApiRegisterWithRegistrar(

View File

@@ -21,6 +21,7 @@ list(APPEND FLUTTER_PLUGIN_LIST
media_kit_libs_windows_video media_kit_libs_windows_video
media_kit_video media_kit_video
pasteboard pasteboard
permission_handler_windows
protocol_handler_windows protocol_handler_windows
record_windows record_windows
screen_retriever_windows screen_retriever_windows