Skip to content

Commit

Permalink
フォルダとファイルを移動できるように
Browse files Browse the repository at this point in the history
  • Loading branch information
poppingmoon committed Jan 28, 2024
1 parent 33bf2b5 commit 8ec3ed6
Show file tree
Hide file tree
Showing 8 changed files with 209 additions and 12 deletions.
2 changes: 2 additions & 0 deletions lib/l10n/app_ja.arb
Original file line number Diff line number Diff line change
Expand Up @@ -973,12 +973,14 @@
"createFolder": "フォルダを作成",
"folderName": "フォルダ名",
"fileDownloaded": "ファイルを保存しました",
"moved": "移動しました",
"confirmDeleteFile": "このファイルを削除しますか?",
"deleted": "削除しました",
"editFile": "ファイルを編集",
"editImage": "画像を編集",
"createNoteFromThisFile": "このファイルからノートを作成",
"download": "ダウンロード",
"move": "移動",
"changeFolderName": "フォルダ名を変更",
"confirmDeleteFolder": "このフォルダを削除しますか?",
"noFiles": "ファイルがありません",
Expand Down
14 changes: 13 additions & 1 deletion lib/router/app_router.gr.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

30 changes: 30 additions & 0 deletions lib/state_notifier/drive_page/drive_files_notifier.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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 AutoDisposeFamilyAsyncNotifier<
Expand Down Expand Up @@ -120,4 +121,33 @@ class DriveFilesNotifier extends AutoDisposeFamilyAsyncNotifier<
items: _state.map((file) => file.id == fileId ? response : file).toList(),
);
}

Future<void> move({
required String fileId,
required String? folderId,
}) async {
if (folderId == _folderId) {
return;
}
// folderIdがnullのときキーが削除されるのを回避
final response = await _misskey.apiService.post<Map<String, dynamic>>(
"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]);
}
}
30 changes: 30 additions & 0 deletions lib/state_notifier/drive_page/drive_folders_notifier.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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 AutoDisposeFamilyAsyncNotifier<
Expand Down Expand Up @@ -95,4 +96,33 @@ class DriveFoldersNotifier extends AutoDisposeFamilyAsyncNotifier<
.toList(),
);
}

Future<void> move({
required String folderId,
required String? parentId,
}) async {
if (parentId == _folderId) {
return;
}
// parentIdがnullのときキーが削除されるのを回避
final response = await _misskey.apiService.post<Map<String, dynamic>>(
"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]);
}
}
27 changes: 27 additions & 0 deletions lib/view/drive_page/drive_file_modal_sheet.dart
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,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:miria/view/note_create_page/thumbnail.dart';
import 'package:misskey_dart/misskey_dart.dart';
Expand Down Expand Up @@ -100,6 +101,27 @@ class DriveFileModalSheet extends ConsumerWidget {
Navigator.of(context).pop();
}

Future<void> 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(
SnackBar(content: Text(S.of(context).moved)),
);
Navigator.of(context).pop();
}

Future<void> delete(WidgetRef ref) async {
final context = ref.context;
final misskey = ref.read(misskeyProvider(account));
Expand Down Expand Up @@ -175,6 +197,11 @@ class DriveFileModalSheet extends ConsumerWidget {
title: Text(S.of(context).download),
onTap: () => download(ref).expectFailure(context),
),
ListTile(
leading: const Icon(Icons.drive_file_move),
title: Text(S.of(context).move),
onTap: () => move(ref).expectFailure(context),
),
ListTile(
leading: const Icon(Icons.delete),
title: Text(S.of(context).delete),
Expand Down
27 changes: 27 additions & 0 deletions lib/view/drive_page/drive_folder_modal_sheet.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,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 {
Expand Down Expand Up @@ -41,6 +42,27 @@ class DriveFolderModalSheet extends ConsumerWidget {
}
}

Future<void> 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(
SnackBar(content: Text(S.of(context).moved)),
);
Navigator.of(context).pop();
}

Future<void> delete(WidgetRef ref) async {
final context = ref.context;
final misskey = ref.read(misskeyProvider(account));
Expand Down Expand Up @@ -82,6 +104,11 @@ class DriveFolderModalSheet extends ConsumerWidget {
title: Text(S.of(context).changeFolderName),
onTap: () => changeName(ref).expectFailure(context),
),
ListTile(
leading: const Icon(Icons.drive_file_move),
title: Text(S.of(context).move),
onTap: () => move(ref).expectFailure(context),
),
ListTile(
leading: const Icon(Icons.delete),
title: Text(S.of(context).delete),
Expand Down
50 changes: 50 additions & 0 deletions lib/view/drive_page/drive_folder_select_dialog.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.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/drive_page_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: [
drivePageNotifierProvider.overrideWith(DrivePageNotifier.new),
],
child: Dialog(
child: DrivePage(
account: account,
title: Text(S.of(context).selectFolder),
floatingActionButtonBuilder: (context) =>
const DriveFolderSelectDialogFloatingActionButton(),
),
),
);
}
}

class DriveFolderSelectDialogFloatingActionButton extends ConsumerWidget {
const DriveFolderSelectDialogFloatingActionButton({super.key});

@override
Widget build(BuildContext context, WidgetRef ref) {
return FloatingActionButton.extended(
onPressed: () {
final folder =
ref.read(drivePageNotifierProvider).breadcrumbs.lastOrNull;
Navigator.of(context).pop((folder,));
},
label: Text(S.of(context).select),
icon: const Icon(Icons.check),
);
}
}
41 changes: 30 additions & 11 deletions lib/view/drive_page/drive_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,16 @@ import 'package:miria/view/drive_page/drive_folder_modal_sheet.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)? floatingActionButtonBuilder;

static const itemMaxCrossAxisExtent = 400.0;

Expand Down Expand Up @@ -57,8 +64,19 @@ class DrivePage extends ConsumerWidget {
},
child: Scaffold(
appBar: AppBar(
title: Text(S.of(context).drive),
title: title ?? Text(S.of(context).drive),
actions: [
if (floatingActionButtonBuilder != null)
IconButton(
onPressed: () => showModalBottomSheet(
context: context,
builder: (context) => DriveCreateModalSheet(
account: account,
folder: currentFolder,
),
),
icon: const Icon(Icons.add),
),
if (currentFolder != null)
IconButton(
onPressed: () async {
Expand Down Expand Up @@ -249,16 +267,17 @@ class DrivePage extends ConsumerWidget {
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: () => showModalBottomSheet<void>(
context: context,
builder: (context) => DriveCreateModalSheet(
account: account,
folder: currentFolder,
floatingActionButton: floatingActionButtonBuilder?.call(context) ??
FloatingActionButton(
onPressed: () => showModalBottomSheet<void>(
context: context,
builder: (context) => DriveCreateModalSheet(
account: account,
folder: currentFolder,
),
),
child: const Icon(Icons.add),
),
),
child: const Icon(Icons.add),
),
),
);
}
Expand Down

0 comments on commit 8ec3ed6

Please sign in to comment.