From 0cbefc8e7d50e60cf4fbdf8b737af926f9ec5419 Mon Sep 17 00:00:00 2001 From: poppingmoon <63451158+poppingmoon@users.noreply.github.com> Date: Tue, 24 Oct 2023 06:08:14 +0900 Subject: [PATCH] =?UTF-8?q?=E3=83=95=E3=82=A9=E3=83=AB=E3=83=80=E3=81=A8?= =?UTF-8?q?=E3=83=95=E3=82=A1=E3=82=A4=E3=83=AB=E3=82=92=E7=A7=BB=E5=8B=95?= =?UTF-8?q?=E3=81=A7=E3=81=8D=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/router/app_router.gr.dart | 20 +++++++++- .../drive_page/drive_files_notifier.dart | 30 ++++++++++++++ .../drive_page/drive_folders_notifier.dart | 30 ++++++++++++++ .../drive_page/drive_file_modal_sheet.dart | 27 +++++++++++++ .../drive_page/drive_folder_modal_sheet.dart | 27 +++++++++++++ .../drive_folder_select_dialog.dart | 39 +++++++++++++++++++ lib/view/drive_page/drive_page.dart | 33 +++++++++++----- 7 files changed, 195 insertions(+), 11 deletions(-) create mode 100644 lib/view/drive_page/drive_folder_select_dialog.dart diff --git a/lib/router/app_router.gr.dart b/lib/router/app_router.gr.dart index b777560d8..2685d864e 100644 --- a/lib/router/app_router.gr.dart +++ b/lib/router/app_router.gr.dart @@ -95,6 +95,8 @@ abstract class _$AppRouter extends RootStackRouter { child: DrivePage( key: args.key, account: args.account, + title: args.title, + floatingActionButtonBuilder: args.floatingActionButtonBuilder, ), ); }, @@ -732,12 +734,19 @@ class DriveRoute extends PageRouteInfo { DriveRoute({ Key? key, required Account account, + Widget? title, + Widget Function( + BuildContext, + DriveFolder?, + )? floatingActionButtonBuilder, List? children, }) : super( DriveRoute.name, args: DriveRouteArgs( key: key, account: account, + title: title, + floatingActionButtonBuilder: floatingActionButtonBuilder, ), initialChildren: children, ); @@ -751,15 +760,24 @@ class DriveRouteArgs { const DriveRouteArgs({ this.key, required this.account, + this.title, + this.floatingActionButtonBuilder, }); final Key? key; final Account account; + final Widget? title; + + final Widget Function( + BuildContext, + DriveFolder?, + )? floatingActionButtonBuilder; + @override String toString() { - return 'DriveRouteArgs{key: $key, account: $account}'; + return 'DriveRouteArgs{key: $key, account: $account, title: $title, floatingActionButtonBuilder: $floatingActionButtonBuilder}'; } } diff --git a/lib/state_notifier/drive_page/drive_files_notifier.dart b/lib/state_notifier/drive_page/drive_files_notifier.dart index 9e7c24486..51cc10054 100644 --- a/lib/state_notifier/drive_page/drive_files_notifier.dart +++ b/lib/state_notifier/drive_page/drive_files_notifier.dart @@ -4,6 +4,7 @@ import 'dart:typed_data'; import 'package:file/file.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:miria/model/pagination_state.dart'; +import 'package:miria/providers.dart'; import 'package:misskey_dart/misskey_dart.dart'; class DriveFilesNotifier extends AutoDisposeFamilyNotifier< @@ -110,4 +111,33 @@ class DriveFilesNotifier extends AutoDisposeFamilyNotifier< items: state.map((file) => file.id == fileId ? response : file).toList(), ); } + + Future move({ + required String fileId, + required String? folderId, + }) async { + if (folderId == _folderId) { + return; + } + // folderIdがnullのときキーが削除されるのを回避 + final response = await _misskey.apiService.post>( + "drive/files/update", + { + "fileId": fileId, + "folderId": folderId, + }, + excludeRemoveNullPredicate: (key, _) => key == "folderId", + ); + final file = DriveFile.fromJson(response); + state = state.copyWith( + items: state.where((file) => file.id != fileId).toList(), + ); + ref + .read(driveFilesNotifierProvider((_misskey, folderId)).notifier) + .add(file); + } + + void add(DriveFile file) { + state = state.copyWith(items: [file, ...state]); + } } diff --git a/lib/state_notifier/drive_page/drive_folders_notifier.dart b/lib/state_notifier/drive_page/drive_folders_notifier.dart index ce43dc860..a20a5015c 100644 --- a/lib/state_notifier/drive_page/drive_folders_notifier.dart +++ b/lib/state_notifier/drive_page/drive_folders_notifier.dart @@ -2,6 +2,7 @@ import 'dart:async'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:miria/model/pagination_state.dart'; +import 'package:miria/providers.dart'; import 'package:misskey_dart/misskey_dart.dart'; class DriveFoldersNotifier extends AutoDisposeFamilyNotifier< @@ -85,4 +86,33 @@ class DriveFoldersNotifier extends AutoDisposeFamilyNotifier< .toList(), ); } + + Future move({ + required String folderId, + required String? parentId, + }) async { + if (parentId == _folderId) { + return; + } + // parentIdがnullのときキーが削除されるのを回避 + final response = await _misskey.apiService.post>( + "drive/folders/update", + { + "folderId": folderId, + "parentId": parentId, + }, + excludeRemoveNullPredicate: (key, _) => key == "parentId", + ); + final folder = DriveFolder.fromJson(response); + state = state.copyWith( + items: state.where((file) => file.id != folderId).toList(), + ); + ref + .read(driveFoldersNotifierProvider((_misskey, parentId)).notifier) + .add(folder); + } + + void add(DriveFolder folder) { + state = state.copyWith(items: [folder, ...state]); + } } diff --git a/lib/view/drive_page/drive_file_modal_sheet.dart b/lib/view/drive_page/drive_file_modal_sheet.dart index 6bfe99ff6..d84f712a1 100644 --- a/lib/view/drive_page/drive_file_modal_sheet.dart +++ b/lib/view/drive_page/drive_file_modal_sheet.dart @@ -11,6 +11,7 @@ import 'package:miria/providers.dart'; import 'package:miria/router/app_router.dart'; import 'package:miria/view/common/error_dialog_handler.dart'; import 'package:miria/view/dialogs/simple_confirm_dialog.dart'; +import 'package:miria/view/drive_page/drive_folder_select_dialog.dart'; import 'package:miria/view/note_create_page/file_settings_dialog.dart'; import 'package:misskey_dart/misskey_dart.dart'; @@ -97,6 +98,27 @@ class DriveFileModalSheet extends ConsumerWidget { Navigator.of(context).pop(); } + Future move(WidgetRef ref) async { + final context = ref.context; + final misskey = ref.read(misskeyProvider(account)); + final result = await showDialog<(DriveFolder?,)>( + context: context, + builder: (context) => DriveFolderSelectDialog(account: account), + ); + if (result == null) return; + await ref + .read(driveFilesNotifierProvider((misskey, file.folderId)).notifier) + .move( + fileId: file.id, + folderId: result.$1?.id, + ); + if (!context.mounted) return; + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar(content: Text("移動しました")), + ); + Navigator.of(context).pop(); + } + Future delete(WidgetRef ref) async { final context = ref.context; final misskey = ref.read(misskeyProvider(account)); @@ -163,6 +185,11 @@ class DriveFileModalSheet extends ConsumerWidget { title: const Text("ダウンロード"), onTap: () => download(ref).expectFailure(context), ), + ListTile( + leading: const Icon(Icons.drive_file_move), + title: const Text("移動"), + onTap: () => move(ref).expectFailure(context), + ), ListTile( leading: const Icon(Icons.delete), title: const Text("削除"), diff --git a/lib/view/drive_page/drive_folder_modal_sheet.dart b/lib/view/drive_page/drive_folder_modal_sheet.dart index a7df2252d..f31401ac5 100644 --- a/lib/view/drive_page/drive_folder_modal_sheet.dart +++ b/lib/view/drive_page/drive_folder_modal_sheet.dart @@ -5,6 +5,7 @@ import 'package:miria/providers.dart'; import 'package:miria/view/common/error_dialog_handler.dart'; import 'package:miria/view/common/text_form_field_dialog.dart'; import 'package:miria/view/dialogs/simple_confirm_dialog.dart'; +import 'package:miria/view/drive_page/drive_folder_select_dialog.dart'; import 'package:misskey_dart/misskey_dart.dart'; class DriveFolderModalSheet extends ConsumerWidget { @@ -39,6 +40,27 @@ class DriveFolderModalSheet extends ConsumerWidget { } } + Future move(WidgetRef ref) async { + final context = ref.context; + final misskey = ref.read(misskeyProvider(account)); + final result = await showDialog<(DriveFolder?,)>( + context: context, + builder: (context) => DriveFolderSelectDialog(account: account), + ); + if (result == null) return; + await ref + .read(driveFoldersNotifierProvider((misskey, folder.parentId)).notifier) + .move( + folderId: folder.id, + parentId: result.$1?.id, + ); + if (!context.mounted) return; + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar(content: Text("移動しました")), + ); + Navigator.of(context).pop(); + } + Future delete(WidgetRef ref) async { final context = ref.context; final misskey = ref.read(misskeyProvider(account)); @@ -72,6 +94,11 @@ class DriveFolderModalSheet extends ConsumerWidget { title: const Text("名前を変更"), onTap: () => changeName(ref).expectFailure(context), ), + ListTile( + leading: const Icon(Icons.drive_file_move), + title: const Text("移動"), + onTap: () => move(ref).expectFailure(context), + ), ListTile( leading: const Icon(Icons.delete), title: const Text("削除"), diff --git a/lib/view/drive_page/drive_folder_select_dialog.dart b/lib/view/drive_page/drive_folder_select_dialog.dart new file mode 100644 index 000000000..77c976c37 --- /dev/null +++ b/lib/view/drive_page/drive_folder_select_dialog.dart @@ -0,0 +1,39 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:miria/model/account.dart'; +import 'package:miria/providers.dart'; +import 'package:miria/state_notifier/drive_page/breadcrumbs_notifier.dart'; +import 'package:miria/view/drive_page/drive_page.dart'; + +class DriveFolderSelectDialog extends StatelessWidget { + const DriveFolderSelectDialog({ + super.key, + required this.account, + }); + + final Account account; + + @override + Widget build(BuildContext context) { + return ProviderScope( + overrides: [ + breadcrumbsNotifierProvider.overrideWith(BreadcrumbsNotifier.new), + ], + child: Dialog( + child: DrivePage( + account: account, + title: const Text("フォルダを選択"), + floatingActionButtonBuilder: (context, folder) { + return FloatingActionButton.extended( + onPressed: () { + Navigator.of(context).pop((folder,)); + }, + label: const Text("選択"), + icon: const Icon(Icons.check), + ); + }, + ), + ), + ); + } +} diff --git a/lib/view/drive_page/drive_page.dart b/lib/view/drive_page/drive_page.dart index 15cfdd8f5..d69f8239a 100644 --- a/lib/view/drive_page/drive_page.dart +++ b/lib/view/drive_page/drive_page.dart @@ -11,12 +11,21 @@ import 'package:miria/view/drive_page/breadcrumbs.dart'; import 'package:miria/view/drive_page/drive_create_modal_sheet.dart'; import 'package:miria/view/drive_page/drive_file_grid_item.dart'; import 'package:miria/view/drive_page/drive_folder_grid_item.dart'; +import 'package:misskey_dart/misskey_dart.dart'; @RoutePage() class DrivePage extends ConsumerWidget { - const DrivePage({super.key, required this.account}); + const DrivePage({ + super.key, + required this.account, + this.title, + this.floatingActionButtonBuilder, + }); final Account account; + final Widget? title; + final Widget Function(BuildContext context, DriveFolder? currentFolder)? + floatingActionButtonBuilder; static const itemMaxCrossAxisExtent = 400.0; @@ -171,16 +180,20 @@ class DrivePage extends ConsumerWidget { ], ), ), - floatingActionButton: FloatingActionButton( - onPressed: () => showModalBottomSheet( - context: context, - builder: (context) => DriveCreateModalSheet( - account: account, - folder: currentFolder, + floatingActionButton: floatingActionButtonBuilder?.call( + context, + currentFolder, + ) ?? + FloatingActionButton( + onPressed: () => showModalBottomSheet( + context: context, + builder: (context) => DriveCreateModalSheet( + account: account, + folder: currentFolder, + ), + ), + child: const Icon(Icons.add), ), - ), - child: const Icon(Icons.add), - ), ), ); }