new: bulk import servers (#301)

This commit is contained in:
lollipopkit
2024-03-10 16:20:27 +08:00
parent ff1780bb04
commit 5e9f0bf8a1
15 changed files with 132 additions and 30 deletions

View File

@@ -660,7 +660,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
CURRENT_PROJECT_VERSION = 796;
CURRENT_PROJECT_VERSION = 798;
DEVELOPMENT_TEAM = BA88US33G6;
ENABLE_BITCODE = NO;
INFOPLIST_FILE = "Runner/Info-$(CONFIGURATION).plist";
@@ -670,7 +670,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 1.0.796;
MARKETING_VERSION = 1.0.798;
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
@@ -796,7 +796,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
CURRENT_PROJECT_VERSION = 796;
CURRENT_PROJECT_VERSION = 798;
DEVELOPMENT_TEAM = BA88US33G6;
ENABLE_BITCODE = NO;
INFOPLIST_FILE = "Runner/Info-$(CONFIGURATION).plist";
@@ -806,7 +806,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 1.0.796;
MARKETING_VERSION = 1.0.798;
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
@@ -824,7 +824,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
CURRENT_PROJECT_VERSION = 796;
CURRENT_PROJECT_VERSION = 798;
DEVELOPMENT_TEAM = BA88US33G6;
ENABLE_BITCODE = NO;
INFOPLIST_FILE = "Runner/Info-$(CONFIGURATION).plist";
@@ -834,7 +834,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 1.0.796;
MARKETING_VERSION = 1.0.798;
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
@@ -855,7 +855,7 @@
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 796;
CURRENT_PROJECT_VERSION = 798;
DEVELOPMENT_TEAM = BA88US33G6;
GCC_C_LANGUAGE_STANDARD = gnu11;
GENERATE_INFOPLIST_FILE = YES;
@@ -868,7 +868,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 1.0.796;
MARKETING_VERSION = 1.0.798;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox.StatusWidget;
@@ -894,7 +894,7 @@
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 796;
CURRENT_PROJECT_VERSION = 798;
DEVELOPMENT_TEAM = BA88US33G6;
GCC_C_LANGUAGE_STANDARD = gnu11;
GENERATE_INFOPLIST_FILE = YES;
@@ -907,7 +907,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 1.0.796;
MARKETING_VERSION = 1.0.798;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox.StatusWidget;
PRODUCT_NAME = "$(TARGET_NAME)";
@@ -930,7 +930,7 @@
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 796;
CURRENT_PROJECT_VERSION = 798;
DEVELOPMENT_TEAM = BA88US33G6;
GCC_C_LANGUAGE_STANDARD = gnu11;
GENERATE_INFOPLIST_FILE = YES;
@@ -943,7 +943,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 1.0.796;
MARKETING_VERSION = 1.0.798;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox.StatusWidget;
PRODUCT_NAME = "$(TARGET_NAME)";
@@ -966,7 +966,7 @@
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 796;
CURRENT_PROJECT_VERSION = 798;
DEVELOPMENT_ASSET_PATHS = "";
DEVELOPMENT_TEAM = BA88US33G6;
ENABLE_PREVIEWS = YES;
@@ -978,7 +978,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 1.0.796;
MARKETING_VERSION = 1.0.798;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox.WatchEnd;
@@ -1007,7 +1007,7 @@
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 796;
CURRENT_PROJECT_VERSION = 798;
DEVELOPMENT_ASSET_PATHS = "";
DEVELOPMENT_TEAM = BA88US33G6;
ENABLE_PREVIEWS = YES;
@@ -1019,7 +1019,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 1.0.796;
MARKETING_VERSION = 1.0.798;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox.WatchEnd;
PRODUCT_NAME = ServerBox;
@@ -1045,7 +1045,7 @@
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 796;
CURRENT_PROJECT_VERSION = 798;
DEVELOPMENT_ASSET_PATHS = "";
DEVELOPMENT_TEAM = BA88US33G6;
ENABLE_PREVIEWS = YES;
@@ -1057,7 +1057,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 1.0.796;
MARKETING_VERSION = 1.0.798;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox.WatchEnd;
PRODUCT_NAME = ServerBox;

View File

@@ -2,9 +2,9 @@
class BuildData {
static const String name = "ServerBox";
static const int build = 796;
static const int build = 798;
static const String engine = "3.19.2";
static const String buildAt = "2024-03-09 16:45:22";
static const int modifications = 1;
static const String buildAt = "2024-03-09 18:43:21";
static const int modifications = 2;
static const int script = 40;
}

View File

@@ -26,6 +26,8 @@
"bgRun": "Hintergrundaktualisierung",
"bgRunTip": "Dieser Schalter bedeutet nur, dass die App versuchen wird, im Hintergrund zu laufen. Ob sie im Hintergrund laufen kann, hängt davon ab, ob die Berechtigungen aktiviert sind oder nicht. Bei nativem Android deaktivieren Sie bitte \"Batterieoptimierung\" in dieser App, und bei miui ändern Sie bitte die Energiesparrichtlinie auf \"Unbegrenzt\".",
"bioAuth": "Biozertifizierung",
"bulkImportServers": "Server im Batch importieren",
"bulkImportServersTip": "Sie können das [Format]({url}) hier finden.",
"canPullRefresh": "Danach: herunterziehen zum Aktualisieren",
"cancel": "Abbrechen",
"choose": "Auswählen",

View File

@@ -26,6 +26,8 @@
"bgRun": "Run in backgroud",
"bgRunTip": "This switch only means the program will try to run in the background, whether it can run in the background depends on whether the permission is enabled or not. For native Android, please disable \"Battery Optimization\" in this app, and for miui, please change the power saving policy to \"Unlimited\".",
"bioAuth": "Biometric auth",
"bulkImportServers": "Batch import servers",
"bulkImportServersTip": "You can find the [format]({url}) here.",
"canPullRefresh": "You can pull to refresh.",
"cancel": "Cancel",
"choose": "Choose",

View File

@@ -26,6 +26,8 @@
"bgRun": "Ejecución en segundo plano",
"bgRunTip": "Este interruptor solo indica que la aplicación intentará correr en segundo plano, si puede hacerlo o no depende de si tiene el permiso correspondiente. En Android puro, por favor desactiva la “optimización de batería” para esta app, en MIUI por favor cambia la estrategia de ahorro de energía a “Sin restricciones”.",
"bioAuth": "Autenticación biométrica",
"bulkImportServers": "Importar servidores en masa",
"bulkImportServersTip": "Puede encontrar el [formato]]({url}) aquí.",
"canPullRefresh": "Se puede deslizar hacia abajo para refrescar",
"cancel": "Cancelar",
"choose": "Elegir",

View File

@@ -26,6 +26,8 @@
"bgRun": "Exécuter en arrière-plan",
"bgRunTip": "Ce commutateur signifie seulement que l'application essaiera de fonctionner en arrière-plan. La possibilité de fonctionner en arrière-plan dépend de l'activation ou non des autorisations. Pour Android, veuillez désactiver l'option \"Optimisation de la batterie\" dans cette application, et pour Miui, veuillez changer la politique d'économie d'énergie en \"Illimité\".",
"bioAuth": "Authentification biométrique",
"bulkImportServers": "Importer des serveurs en lot",
"bulkImportServersTip": "Vous pouvez trouver le [format]({url}) ici.",
"canPullRefresh": "Vous pouvez tirer pour actualiser.",
"cancel": "Annuler",
"choose": "Choisir",

View File

@@ -26,6 +26,8 @@
"bgRun": "Jalankan di Backgroud",
"bgRunTip": "Sakelar ini hanya berarti aplikasi akan mencoba berjalan di latar belakang, apakah aplikasi dapat berjalan di latar belakang tergantung pada apakah izin diaktifkan atau tidak. Untuk Android asli, nonaktifkan \"Pengoptimalan Baterai\" di aplikasi ini, dan untuk miui, ubah kebijakan penghematan daya ke \"Tidak Terbatas\".",
"bioAuth": "Biosertifikasi",
"bulkImportServers": "Impor server secara massal",
"bulkImportServersTip": "Anda dapat menemukan [format]({url}) di sini.",
"canPullRefresh": "Anda dapat menarik untuk menyegarkan.",
"cancel": "Membatalkan",
"choose": "Memilih",

View File

@@ -26,6 +26,8 @@
"bgRun": "バックグラウンド実行",
"bgRunTip": "このスイッチはプログラムがバックグラウンドで実行を試みることを意味しますが、実際にバックグラウンドで実行できるかどうかは、権限が有効になっているかに依存します。ネイティブAndroidでは、このアプリの「バッテリー最適化」をオフにしてください。MIUIでは、省エネモードを「無制限」に変更してください。",
"bioAuth": "生体認証",
"bulkImportServers": "サーバーを一括インポートする",
"bulkImportServersTip": "[こちら]({url})でフォーマットを見つけることができます",
"canPullRefresh": "引っ張って更新できます",
"cancel": "キャンセル",
"choose": "選択",

View File

@@ -26,6 +26,8 @@
"bgRun": "Execução em segundo plano",
"bgRunTip": "Este interruptor indica que o programa tentará rodar em segundo plano, mas a capacidade de fazer isso depende das permissões concedidas. No Android nativo, desative a 'Otimização de bateria' para este app, no MIUI, altere a estratégia de economia de energia para 'Sem restrições'.",
"bioAuth": "Autenticação biométrica",
"bulkImportServers": "Importar servidores em lote",
"bulkImportServersTip": "Você pode encontrar o [formato]({url}) aqui.",
"canPullRefresh": "Pode puxar para atualizar",
"cancel": "Cancelar",
"choose": "Escolher",

View File

@@ -26,6 +26,8 @@
"bgRun": "работа в фоновом режиме",
"bgRunTip": "Этот переключатель означает, что программа будет пытаться работать в фоновом режиме, но фактическое выполнение зависит от того, включено ли разрешение. Для нативного Android отключите «Оптимизацию батареи» для этого приложения, для MIUI измените стратегию энергосбережения на «Без ограничений».",
"bioAuth": "биометрическая аутентификация",
"bulkImportServers": "Пакетный импорт серверов",
"bulkImportServersTip": "[Формат]({url}) можно найти здесь.",
"canPullRefresh": "можно обновить, потянув вниз",
"cancel": "отмена",
"choose": "выбрать",

View File

@@ -26,6 +26,8 @@
"bgRun": "后台运行",
"bgRunTip": "此开关只代表程序会尝试在后台运行,具体能否后台运行取决于是否开启了权限。原生 Android 请关闭本 App 的“电池优化”MIUI 请修改省电策略为“无限制”。",
"bioAuth": "生物认证",
"bulkImportServers": "批量导入服务器",
"bulkImportServersTip": "可以在这里找到[格式]({url})",
"canPullRefresh": "可以下拉刷新",
"cancel": "取消",
"choose": "选择",

View File

@@ -26,6 +26,8 @@
"bgRun": "背景運行",
"bgRunTip": "此開關只代表程式會嘗試在背景執行,具體能否背景運行取決於是否開啟了權限。 原生 Android 請關閉本 App 的“電池優化”MIUI 請修改省電策略為“無限制”。",
"bioAuth": "生物認證",
"bulkImportServers": "批量導入伺服器",
"bulkImportServersTip": "您可以在此處找到[格式]({url})",
"canPullRefresh": "可以下拉更新",
"cancel": "取消",
"choose": "選擇",

View File

@@ -1,7 +1,9 @@
import 'dart:convert';
import 'dart:io';
import 'package:computer/computer.dart';
import 'package:flutter/material.dart';
import 'package:flutter_markdown/flutter_markdown.dart';
import 'package:toolbox/core/extension/context/common.dart';
import 'package:toolbox/core/extension/context/dialog.dart';
import 'package:toolbox/core/extension/context/locale.dart';
@@ -12,11 +14,15 @@ import 'package:toolbox/core/utils/sync/icloud.dart';
import 'package:toolbox/core/utils/platform/base.dart';
import 'package:toolbox/core/utils/share.dart';
import 'package:toolbox/core/utils/sync/webdav.dart';
import 'package:toolbox/core/utils/ui.dart';
import 'package:toolbox/data/model/app/backup.dart';
import 'package:toolbox/data/model/server/server_private_info.dart';
import 'package:toolbox/data/res/color.dart';
import 'package:toolbox/data/res/logger.dart';
import 'package:toolbox/data/res/path.dart';
import 'package:toolbox/data/res/store.dart';
import 'package:toolbox/data/res/ui.dart';
import 'package:toolbox/data/res/url.dart';
import 'package:toolbox/view/widget/appbar.dart';
import 'package:toolbox/view/widget/expand_tile.dart';
import 'package:toolbox/view/widget/cardx.dart';
@@ -48,6 +54,7 @@ class BackupPage extends StatelessWidget {
_buildWebdav(context),
_buildFile(context),
_buildClipboard(context),
_buildBulkImportServers(context),
],
);
}
@@ -214,6 +221,26 @@ class BackupPage extends StatelessWidget {
);
}
Widget _buildBulkImportServers(BuildContext context) {
return CardX(
child: ListTile(
title: Text(l10n.bulkImportServers),
subtitle: MarkdownBody(
data: l10n.bulkImportServersTip(Urls.appWiki),
styleSheet: MarkdownStyleSheet(
p: UIs.textGrey,
a: TextStyle(
color: primaryColor,
)),
onTapLink: (text, href, title) {
if (href != null) openUrl(href);
},
),
trailing: const Icon(Icons.import_export),
onTap: () => _onBulkImportServers(context),
));
}
Future<void> _onTapFileRestore(BuildContext context) async {
final path = await pickOneFile();
if (path == null) return;
@@ -419,4 +446,61 @@ class BackupPage extends StatelessWidget {
context.showSnackBar(e.toString());
}
}
void _onBulkImportServers(BuildContext context) async {
final path = await pickOneFile();
if (path == null) return;
final file = File(path);
if (!await file.exists()) {
context.showRoundDialog(
title: Text(l10n.error),
child: Text(l10n.fileNotExist(path)),
);
return;
}
final text = await file.readAsString();
if (text.isEmpty) {
context.showRoundDialog(
title: Text(l10n.error),
child: Text(l10n.fieldMustNotEmpty),
);
return;
}
try {
final spis = await context.showLoadingDialog(
fn: () => Computer.shared.start((val) {
final list = json.decode(val) as List;
return list.map((e) => ServerPrivateInfo.fromJson(e)).toList();
}, text.trim()),
);
final sure = await context.showRoundDialog<bool>(
title: Text(l10n.import),
child: Text(l10n.askContinue('${spis.length} ${l10n.server}')),
actions: [
TextButton(
onPressed: () => context.pop(true),
child: Text(l10n.ok),
),
],
);
if (sure == true) {
await context.showLoadingDialog(
fn: () async {
for (var spi in spis) {
Stores.server.put(spi);
}
},
);
context.showSnackBar(l10n.success);
}
} catch (e) {
context.showRoundDialog(
title: Text(l10n.error),
child: Text(e.toString()),
);
}
}
}

View File

@@ -467,7 +467,7 @@
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 796;
CURRENT_PROJECT_VERSION = 798;
DEVELOPMENT_TEAM = BA88US33G6;
INFOPLIST_FILE = Runner/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = "Server Box";
@@ -477,7 +477,7 @@
"@executable_path/../Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 10.15;
MARKETING_VERSION = 1.0.796;
MARKETING_VERSION = 1.0.798;
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox;
PRODUCT_NAME = "Server Box";
PROVISIONING_PROFILE_SPECIFIER = "";
@@ -604,7 +604,7 @@
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 796;
CURRENT_PROJECT_VERSION = 798;
DEVELOPMENT_TEAM = BA88US33G6;
INFOPLIST_FILE = Runner/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = "Server Box";
@@ -614,7 +614,7 @@
"@executable_path/../Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 10.15;
MARKETING_VERSION = 1.0.796;
MARKETING_VERSION = 1.0.798;
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox;
PRODUCT_NAME = "Server Box";
PROVISIONING_PROFILE_SPECIFIER = "";
@@ -634,7 +634,7 @@
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "3rd Party Mac Developer Application";
CODE_SIGN_STYLE = Manual;
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 796;
CURRENT_PROJECT_VERSION = 798;
DEVELOPMENT_TEAM = "";
"DEVELOPMENT_TEAM[sdk=macosx*]" = BA88US33G6;
INFOPLIST_FILE = Runner/Info.plist;
@@ -645,7 +645,7 @@
"@executable_path/../Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 10.15;
MARKETING_VERSION = 1.0.796;
MARKETING_VERSION = 1.0.798;
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox;
PRODUCT_NAME = "Server Box";
PROVISIONING_PROFILE_SPECIFIER = "";

View File

@@ -163,12 +163,10 @@ Future<void> flutterBuildAndroid() async {
Future<void> flutterBuildLinux() async {
await flutterBuild('linux');
const appDirName = 'linux.AppDir';
// mkdir appName.AppDir
await Process.run('mkdir', [appDirName]);
// cp -r build/linux/x64/release/bundle/* appName.AppDir
await Process.run('cp', [
'-r',
'build/linux/x64/release/bundle/*',
'build/linux/x64/release/bundle',
appDirName,
]);
// cp -r assets/app_icon.png ServerBox.AppDir