diff --git a/lib/repository/import_export_repository.dart b/lib/repository/import_export_repository.dart index dc7d80c39..b2891e688 100644 --- a/lib/repository/import_export_repository.dart +++ b/lib/repository/import_export_repository.dart @@ -24,28 +24,36 @@ class ImportExportRepository extends ChangeNotifier { ImportExportRepository(this.reader); Future import(BuildContext context, Account account) async { - final folder = await showDialog( - barrierDismissible: false, + final result = await showDialog( context: context, - builder: (context2) => WillPopScope( - onWillPop: () async => false, - child: FolderSelectDialog( - account: account, - fileShowTarget: const ["miria.json", "miria.json.unknown"], - ), + builder: (context2) => FolderSelectDialog( + account: account, + fileShowTarget: const ["miria.json", "miria.json.unknown"], + confirmationText: "このフォルダーからインポートする", ), ); + if (result == null) return; - var alreadyExists = await reader(misskeyProvider(account)) - .drive - .files - .find(DriveFilesFindRequest(name: "miria.json", folderId: folder?.id)); + final folder = result.folder; + Iterable alreadyExists = + await reader(misskeyProvider(account)).drive.files.find( + DriveFilesFindRequest( + name: "miria.json", + folderId: folder?.id, + ), + ); + + if (!context.mounted) return; if (alreadyExists.isEmpty) { alreadyExists = await reader(misskeyProvider(account)).drive.files.find( - DriveFilesFindRequest( - name: "miria.json.unknown", folderId: folder?.id)); + DriveFilesFindRequest( + name: "miria.json.unknown", + folderId: folder?.id, + ), + ); + if (!context.mounted) return; if (alreadyExists.isEmpty) { await SimpleMessageDialog.show(context, "ここにMiriaの設定ファイルあれへんかったわ"); return; @@ -82,9 +90,11 @@ class ImportExportRepository extends ChangeNotifier { final tabSettings = []; for (final tabSetting in json["tabSettings"]) { - final account = accounts.firstWhereOrNull((element) => - tabSetting["account"]["host"] == element.host && - tabSetting["account"]["userId"] == element.userId); + final account = accounts.firstWhereOrNull( + (element) => + tabSetting["account"]["host"] == element.host && + tabSetting["account"]["userId"] == element.userId, + ); if (account == null) { continue; @@ -95,43 +105,51 @@ class ImportExportRepository extends ChangeNotifier { (tabSetting as Map) ..remove("account") ..addEntries( - [MapEntry("account", jsonDecode(jsonEncode(account.toJson())))]); + [MapEntry("account", jsonDecode(jsonEncode(account.toJson())))], + ); tabSettings.add(TabSetting.fromJson(tabSetting)); } reader(tabSettingsRepositoryProvider).save(tabSettings); + if (!context.mounted) return; await SimpleMessageDialog.show(context, "インポート終わったで。"); + + if (!context.mounted) return; context.router ..removeWhere((route) => true) ..push(const SplashRoute()); } Future export(BuildContext context, Account account) async { - final folder = await showDialog( - barrierDismissible: false, + final result = await showDialog( context: context, - builder: (context2) => WillPopScope( - onWillPop: () async => false, - child: FolderSelectDialog( - account: account, - fileShowTarget: const ["miria.json", "miria.json.unknown"], - ), + builder: (context2) => FolderSelectDialog( + account: account, + fileShowTarget: const ["miria.json", "miria.json.unknown"], + confirmationText: "このフォルダーに保存する", ), ); + if (result == null) return; + + final folder = result.folder; - final alreadyExists = await reader(misskeyProvider(account)) - .drive - .files - .find(DriveFilesFindRequest( - name: "miria.json.unknown", folderId: folder?.id)); + final alreadyExists = + await reader(misskeyProvider(account)).drive.files.find( + DriveFilesFindRequest( + name: "miria.json.unknown", + folderId: folder?.id, + ), + ); + if (!context.mounted) return; if (alreadyExists.isNotEmpty) { final alreadyConfirm = await SimpleConfirmDialog.show( - context: context, - message: "ここにもうあるけど上書きするか?", - primary: "上書きする", - secondary: "やっぱやめた"); + context: context, + message: "ここにもうあるけど上書きするか?", + primary: "上書きする", + secondary: "やっぱやめた", + ); if (alreadyConfirm != true) return; for (final element in alreadyExists) { @@ -143,13 +161,11 @@ class ImportExportRepository extends ChangeNotifier { } final data = ExportedSetting( - generalSettings: reader(generalSettingsRepositoryProvider).settings, - tabSettings: - reader(tabSettingsRepositoryProvider).tabSettings.toList(), - accountSettings: reader(accountSettingsRepositoryProvider) - .accountSettings - .toList()) - .toJson(); + generalSettings: reader(generalSettingsRepositoryProvider).settings, + tabSettings: reader(tabSettingsRepositoryProvider).tabSettings.toList(), + accountSettings: + reader(accountSettingsRepositoryProvider).accountSettings.toList(), + ).toJson(); // 外に漏れると困るので for (final element in data["tabSettings"] as List) { @@ -159,13 +175,16 @@ class ImportExportRepository extends ChangeNotifier { } await reader(misskeyProvider(account)).drive.files.createAsBinary( - DriveFilesCreateRequest( + DriveFilesCreateRequest( folderId: folder?.id, name: "miria.json", comment: "Miria設定ファイル", - force: true), - Uint8List.fromList(utf8.encode(jsonEncode(data)))); + force: true, + ), + Uint8List.fromList(utf8.encode(jsonEncode(data))), + ); + if (!context.mounted) return; await SimpleMessageDialog.show(context, "エクスポート終わったで"); } } diff --git a/lib/view/settings_page/import_export_page/folder_select_dialog.dart b/lib/view/settings_page/import_export_page/folder_select_dialog.dart index ddf7184cd..39871bc9d 100644 --- a/lib/view/settings_page/import_export_page/folder_select_dialog.dart +++ b/lib/view/settings_page/import_export_page/folder_select_dialog.dart @@ -6,14 +6,22 @@ import 'package:miria/view/common/futable_list_builder.dart'; import 'package:miria/view/common/pushable_listview.dart'; import 'package:misskey_dart/misskey_dart.dart'; +class FolderResult { + const FolderResult(this.folder); + + final DriveFolder? folder; +} + class FolderSelectDialog extends ConsumerStatefulWidget { final Account account; final List? fileShowTarget; + final String confirmationText; const FolderSelectDialog({ super.key, required this.account, required this.fileShowTarget, + required this.confirmationText, }); @override @@ -29,17 +37,18 @@ class FolderSelectDialogState extends ConsumerState { return AlertDialog( title: Column( children: [ - const Text("フォルダ選択"), + const Text("フォルダー選択"), Row( children: [ if (path.isNotEmpty) IconButton( - onPressed: () { - setState(() { - path.removeLast(); - }); - }, - icon: const Icon(Icons.arrow_back)), + onPressed: () { + setState(() { + path.removeLast(); + }); + }, + icon: const Icon(Icons.arrow_back), + ), Expanded(child: Text(path.map((e) => e.name).join("/"))), ], ) @@ -52,35 +61,40 @@ class FolderSelectDialogState extends ConsumerState { child: Column( children: [ PushableListView( - shrinkWrap: true, - physics: const NeverScrollableScrollPhysics(), - initializeFuture: () async { - final misskey = ref.read(misskeyProvider(widget.account)); - final response = await misskey.drive.folders.folders( - DriveFoldersRequest( - folderId: path.isEmpty ? null : path.last.id)); - return response.toList(); - }, - nextFuture: (lastItem, _) async { - final misskey = ref.read(misskeyProvider(widget.account)); - final response = await misskey.drive.folders.folders( - DriveFoldersRequest( - untilId: lastItem.id, - folderId: path.isEmpty ? null : path.last.id)); - return response.toList(); - }, - listKey: path.map((e) => e.id).join("/"), - itemBuilder: (context, item) { - return ListTile( - leading: const Icon(Icons.folder), - title: Text(item.name ?? ""), - onTap: () { - setState(() { - path.add(item); - }); - }, - ); - }), + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + initializeFuture: () async { + final misskey = ref.read(misskeyProvider(widget.account)); + final response = await misskey.drive.folders.folders( + DriveFoldersRequest( + folderId: path.isEmpty ? null : path.last.id, + ), + ); + return response.toList(); + }, + nextFuture: (lastItem, _) async { + final misskey = ref.read(misskeyProvider(widget.account)); + final response = await misskey.drive.folders.folders( + DriveFoldersRequest( + untilId: lastItem.id, + folderId: path.isEmpty ? null : path.last.id, + ), + ); + return response.toList(); + }, + listKey: path.map((e) => e.id).join("/"), + itemBuilder: (context, item) { + return ListTile( + leading: const Icon(Icons.folder), + title: Text(item.name ?? ""), + onTap: () { + setState(() { + path.add(item); + }); + }, + ); + }, + ), if (widget.fileShowTarget != null) FutureListView( shrinkWrap: true, @@ -88,18 +102,24 @@ class FolderSelectDialogState extends ConsumerState { future: () async { final list = []; for (final element in widget.fileShowTarget!) { - list.addAll(await ref - .read(misskeyProvider(widget.account)) - .drive - .files - .find(DriveFilesFindRequest( - folderId: path.lastOrNull?.id, name: element))); + list.addAll( + await ref + .read(misskeyProvider(widget.account)) + .drive + .files + .find( + DriveFilesFindRequest( + folderId: path.lastOrNull?.id, + name: element, + ), + ), + ); } return list.toList(); }(), builder: (context, item) => Row( children: [ - Icon(Icons.description), + const Icon(Icons.description), Expanded(child: Text(item.name)), ], ), @@ -110,10 +130,11 @@ class FolderSelectDialogState extends ConsumerState { ), actions: [ ElevatedButton( - onPressed: () { - Navigator.of(context).pop(path.lastOrNull); - }, - child: const Text("このフォルダーに保存する")) + onPressed: () { + Navigator.of(context).pop(FolderResult(path.lastOrNull)); + }, + child: Text(widget.confirmationText), + ) ], ); } diff --git a/lib/view/settings_page/import_export_page/import_export_page.dart b/lib/view/settings_page/import_export_page/import_export_page.dart index d20114f08..c3b9e9755 100644 --- a/lib/view/settings_page/import_export_page/import_export_page.dart +++ b/lib/view/settings_page/import_export_page/import_export_page.dart @@ -25,95 +25,98 @@ class ImportExportPageState extends ConsumerState { body: Padding( padding: const EdgeInsets.only(left: 10, right: 10), child: Column( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - "設定のインポート", - style: Theme.of(context).textTheme.titleLarge, - ), - const Text( - "設定ファイルをドライブから読み込みます。設定ファイルには保存されたときのすべてのアカウントの設定情報が記録されていますが、そのうちこの端末でログインしているアカウントの情報のみを読み込みます。"), - Row( - children: [ - Expanded( - child: DropdownButton( - isExpanded: true, - items: [ - for (final account - in ref.read(accountRepository).account) - DropdownMenuItem( - value: account, - child: Text("@${account.userId}@${account.host}"), - ), - ], - value: selectedImportAccount, - onChanged: (Account? value) { - setState(() { - selectedImportAccount = value; - }); - }, - ), + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + "設定のインポート", + style: Theme.of(context).textTheme.titleLarge, + ), + const Text( + "設定ファイルをドライブから読み込みます。設定ファイルには保存されたときのすべてのアカウントの設定情報が記録されていますが、そのうちこの端末でログインしているアカウントの情報のみを読み込みます。", + ), + Row( + children: [ + Expanded( + child: DropdownButton( + isExpanded: true, + items: [ + for (final account in ref.read(accountRepository).account) + DropdownMenuItem( + value: account, + child: Text("@${account.userId}@${account.host}"), + ), + ], + value: selectedImportAccount, + onChanged: (Account? value) { + setState(() { + selectedImportAccount = value; + }); + }, ), - ElevatedButton( - onPressed: () async { - final account = selectedImportAccount; - if (account == null) { - await SimpleMessageDialog.show(context, "アカウントを選んでや"); - return; - } - await ref - .read(importExportRepository) - .import(context, account); - }, - child: const Text("設定ファイル選択")), - ], - ), - const Padding(padding: EdgeInsets.only(top: 30)), - Text( - "設定のエクスポート", - style: Theme.of(context).textTheme.titleLarge, - ), - const Text( - "設定ファイルをドライブに保存します。設定ファイルにはこの端末でログインしているすべてのアカウントの、ログイン情報以外の情報が記録されます。"), - const Text("設定ファイルは1回のエクスポートにつき1つのアカウントに対して保存します。"), - Row( - children: [ - Expanded( - child: DropdownButton( - isExpanded: true, - items: [ - for (final account - in ref.read(accountRepository).account) - DropdownMenuItem( - value: account, - child: Text("@${account.userId}@${account.host}"), - ), - ], - value: selectedExportAccount, - onChanged: (Account? value) { - setState(() { - selectedExportAccount = value; - }); - }, - ), + ), + ElevatedButton( + onPressed: () async { + final account = selectedImportAccount; + if (account == null) { + await SimpleMessageDialog.show(context, "アカウントを選んでや"); + return; + } + await ref + .read(importExportRepository) + .import(context, account); + }, + child: const Text("選択"), + ), + ], + ), + const Padding(padding: EdgeInsets.only(top: 30)), + Text( + "設定のエクスポート", + style: Theme.of(context).textTheme.titleLarge, + ), + const Text( + "設定ファイルをドライブに保存します。設定ファイルにはこの端末でログインしているすべてのアカウントの、ログイン情報以外の情報が記録されます。", + ), + const Text("設定ファイルは1回のエクスポートにつき1つのアカウントに対して保存します。"), + Row( + children: [ + Expanded( + child: DropdownButton( + isExpanded: true, + items: [ + for (final account in ref.read(accountRepository).account) + DropdownMenuItem( + value: account, + child: Text("@${account.userId}@${account.host}"), + ), + ], + value: selectedExportAccount, + onChanged: (Account? value) { + setState(() { + selectedExportAccount = value; + }); + }, ), - ElevatedButton( - onPressed: () { - final account = selectedExportAccount; - if (account == null) { - SimpleMessageDialog.show( - context, "設定ファイルを保存するアカウントを選んでや"); - return; - } - ref - .read(importExportRepository) - .export(context, account); - }, - child: const Text("保存")), - ], - ), - ]), + ), + ElevatedButton( + onPressed: () { + final account = selectedExportAccount; + if (account == null) { + SimpleMessageDialog.show( + context, + "設定ファイルを保存するアカウントを選んでや", + ); + return; + } + ref.read(importExportRepository).export(context, account); + }, + child: const Text("保存"), + ), + ], + ), + ], + ), ), ); }