diff --git a/lib/api/client.dart b/lib/api/client.dart index 6924cda..d290dd8 100644 --- a/lib/api/client.dart +++ b/lib/api/client.dart @@ -342,6 +342,13 @@ abstract class _EHApi { }); @PUT('/task') @MultiPart() + Future> createUpdateMeiliSearchDataTask({ + @Part(name: "gid") int? gid, + @Part(name: "type") String t = "update_meili_search_data", + @CancelRequest() CancelToken? cancel, + }); + @PUT('/task') + @MultiPart() Future> createUpdateTagTranslationTask({ @Part(name: "cfg") UpdateTagTranslationConfig? cfg, @Part(name: "type") String t = "update_tag_translation", diff --git a/lib/api/client.g.dart b/lib/api/client.g.dart index b42c50d..846c5fc 100644 --- a/lib/api/client.g.dart +++ b/lib/api/client.g.dart @@ -1911,6 +1911,59 @@ class __EHApi implements _EHApi { return _value; } + @override + Future> createUpdateMeiliSearchDataTask({ + int? gid, + String t = "update_meili_search_data", + CancelToken? cancel, + }) async { + final _extra = {}; + final queryParameters = {}; + queryParameters.removeWhere((k, v) => v == null); + final _headers = {}; + final _data = FormData(); + if (gid != null) { + _data.fields.add(MapEntry( + 'gid', + gid.toString(), + )); + } + _data.fields.add(MapEntry( + 'type', + t, + )); + final _options = _setStreamType>(Options( + method: 'PUT', + headers: _headers, + extra: _extra, + contentType: 'multipart/form-data', + ) + .compose( + _dio.options, + '/task', + queryParameters: queryParameters, + data: _data, + cancelToken: cancel, + ) + .copyWith( + baseUrl: _combineBaseUrls( + _dio.options.baseUrl, + baseUrl, + ))); + final _result = await _dio.fetch>(_options); + late ApiResult _value; + try { + _value = ApiResult.fromJson( + _result.data!, + (json) => Task.fromJson(json as Map), + ); + } on Object catch (e, s) { + errorLogger?.logError(e, s, _options); + rethrow; + } + return _value; + } + @override Future> createUpdateTagTranslationTask({ UpdateTagTranslationConfig? cfg, diff --git a/lib/components/task.dart b/lib/components/task.dart index db0a217..44565a2 100644 --- a/lib/components/task.dart +++ b/lib/components/task.dart @@ -106,6 +106,10 @@ class _TaskView extends State { return Text(i18n.updateTagTranslation, maxLines: 1, overflow: TextOverflow.ellipsis); } + if (typ == TaskType.updateMeiliSearchData) { + return Text(i18n.updateMeiliSearchDataTask, + maxLines: 1, overflow: TextOverflow.ellipsis); + } return Container(); } diff --git a/lib/dialog/new_update_meili_search_data_task_page.dart b/lib/dialog/new_update_meili_search_data_task_page.dart new file mode 100644 index 0000000..cf1c240 --- /dev/null +++ b/lib/dialog/new_update_meili_search_data_task_page.dart @@ -0,0 +1,136 @@ +import 'package:dio/dio.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +import 'package:go_router/go_router.dart'; +import 'package:logging/logging.dart'; +import '../components/number_field.dart'; +import '../globals.dart'; + +final _log = Logger("NewUpdateTagTranslationTaskPage"); + +class NewUpdateMeiliSearchDataTaskPage extends StatefulWidget { + const NewUpdateMeiliSearchDataTaskPage({this.gid, super.key}); + + final int? gid; + + static const routeName = "/dialog/new_update_meili_search_data_task"; + + @override + State createState() => + _NewUpdateMeiliSearchDataTaskPage(); +} + +class _NewUpdateMeiliSearchDataTaskPage + extends State { + final _formKey = GlobalKey(); + bool _ok = false; + bool _isCreating = false; + CancelToken? _cancel; + int? _gid; + + @override + void initState() { + _gid = widget.gid; + super.initState(); + } + + @override + void dispose() { + _cancel?.cancel(); + super.dispose(); + } + + Future create() async { + try { + _cancel = CancelToken(); + setState(() { + _isCreating = true; + }); + (await api.createUpdateMeiliSearchDataTask(gid: _gid, cancel: _cancel)) + .unwrap(); + _ok = true; + if (!_cancel!.isCancelled) { + setState(() { + _isCreating = false; + }); + } + } catch (e) { + if (!_cancel!.isCancelled) { + _log.warning("Failed to create import task:", e); + setState(() { + _isCreating = false; + }); + } + } + } + + Widget _buildWithVecticalPadding(Widget child) { + return Container( + padding: const EdgeInsets.symmetric(vertical: 8), + child: child, + ); + } + + @override + Widget build(BuildContext context) { + tryInitApi(context); + if (_ok) { + WidgetsBinding.instance!.addPostFrameCallback((_) { + context.canPop() ? context.pop() : context.go("/task_manager"); + }); + _ok = false; + } + final i18n = AppLocalizations.of(context)!; + final maxWidth = MediaQuery.of(context).size.width; + return Container( + padding: maxWidth < 400 + ? const EdgeInsets.symmetric(vertical: 20, horizontal: 5) + : const EdgeInsets.all(20), + width: maxWidth < 810 ? null : 800, + decoration: BoxDecoration(borderRadius: BorderRadius.circular(10)), + child: SingleChildScrollView( + child: Form( + key: _formKey, + child: Column( + children: [ + Stack( + alignment: Alignment.center, + children: [ + Text( + i18n.createUpdateMeiliSearchDataTask, + style: Theme.of(context).textTheme.headlineSmall, + ), + Align( + alignment: Alignment.centerRight, + child: IconButton( + onPressed: () => context.canPop() + ? context.pop() + : context.go("/task_manager"), + icon: const Icon(Icons.close), + )), + ], + ), + _buildWithVecticalPadding(NumberFormField( + min: 0, + initialValue: _gid, + decoration: InputDecoration( + border: const OutlineInputBorder(), + labelText: i18n.gid, + helperText: i18n.updateMeiliSearchDataGidHelp, + helperMaxLines: 3, + ), + onChanged: (gid) { + _gid = gid; + }, + )), + _buildWithVecticalPadding(ElevatedButton( + onPressed: _isCreating + ? null + : () { + create(); + }, + child: Text(i18n.create))), + ], + )))); + } +} diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index 90ae8f4..5b83727 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -337,5 +337,7 @@ "search": "Search", "maxSearchSuggestions": "Maximum results of search suggestions", "ok": "Ok", - "changeSettings": "Change settings" + "changeSettings": "Change settings", + "createUpdateMeiliSearchDataTask": "Create sync meilisearch server's data task", + "updateMeiliSearchDataGidHelp": "If gallery id is not empty, only specified gallery will sync to meilisearch server." } diff --git a/lib/l10n/app_zh.arb b/lib/l10n/app_zh.arb index 2ab89b6..6461134 100644 --- a/lib/l10n/app_zh.arb +++ b/lib/l10n/app_zh.arb @@ -337,5 +337,7 @@ "search": "搜索", "maxSearchSuggestions": "最大搜索建议结果数量", "ok": "确定", - "changeSettings": "修改设置" + "changeSettings": "修改设置", + "createUpdateMeiliSearchDataTask": "创建同步meilisearch服务器数据任务", + "updateMeiliSearchDataGidHelp": "如果画廊ID非空,只有指定的画廊会同步至meilisearch服务器。" } diff --git a/lib/main.dart b/lib/main.dart index deaa126..e956bf9 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -14,6 +14,7 @@ import 'dialog/gallery_share_page.dart'; import 'dialog/new_download_task_page.dart'; import 'dialog/new_export_zip_task_page.dart'; import 'dialog/new_import_task_page.dart'; +import 'dialog/new_update_meili_search_data_task_page.dart'; import 'dialog/new_update_tag_translation_task_page.dart'; import 'dialog/new_user_page.dart'; import 'dialog/task_page.dart'; @@ -342,6 +343,19 @@ final _router = GoRouter( path: SearchSettingsPage.routeName, builder: (context, state) => SearchSettingsPage(key: state.pageKey), ), + GoRoute( + path: NewUpdateMeiliSearchDataTaskPage.routeName, + pageBuilder: (context, state) { + int? gid; + if (state.uri.queryParameters.containsKey("gid")) { + gid = int.tryParse(state.uri.queryParameters["gid"]!); + } + return DialogPage( + key: state.pageKey, + builder: (context) { + return NewUpdateMeiliSearchDataTaskPage(gid: gid); + }); + }), ], observers: [ _NavigatorObserver(), diff --git a/lib/pages/task_manager.dart b/lib/pages/task_manager.dart index df29131..5b1fe5d 100644 --- a/lib/pages/task_manager.dart +++ b/lib/pages/task_manager.dart @@ -222,6 +222,10 @@ class _TaskManagerPage extends State value: TaskType.import, child: Text(i18n.createImportTask), ), + PopupMenuItem( + value: TaskType.updateMeiliSearchData, + child: Text(i18n.createUpdateMeiliSearchDataTask), + ), PopupMenuItem( value: TaskType.updateTagTranslation, child: Text(i18n.createUpdateTagTranslationTask), @@ -235,6 +239,8 @@ class _TaskManagerPage extends State context.push("/dialog/new_export_zip_task"); } else if (type == TaskType.import) { context.push("/dialog/new_import_task"); + } else if (type == TaskType.updateMeiliSearchData) { + context.push("/dialog/new_update_meili_search_data_task"); } else if (type == TaskType.updateTagTranslation) { context.push("/dialog/new_update_tag_translation_task"); }