diff --git a/assets/translation.json b/assets/translation.json index c1dbf46..652b60d 100644 --- a/assets/translation.json +++ b/assets/translation.json @@ -201,6 +201,10 @@ "Sync Data": "同步数据", "Syncing Data": "正在同步数据", "Data Sync": "数据同步", + "Skip Setting Fields": "跳过设置项", + "Skip Setting Fields (Optional)": "跳过设置项(可选)", + "When sync data, skip certain setting fields, which means these won't be uploaded / override.": "同步时跳过指定设置项,这些项不会被上传或覆盖。", + "See source code for available fields.": "可用的设置项名称详见源码。", "Quick Favorite": "快速收藏", "Long press on the favorite button to quickly add to this folder": "长按收藏按钮快速添加到这个文件夹", "Added": "已添加", @@ -624,6 +628,10 @@ "Sync Data": "同步資料", "Syncing Data": "正在同步資料", "Data Sync": "資料同步", + "Skip Setting Fields": "跳過設定項", + "Skip Setting Fields (Optional)": "跳過設定項(可選)", + "When sync data, skip certain setting fields, which means these won't be uploaded / override.": "同步時跳過指定設定項,這些項不會被上傳或覆寫。", + "See source code for available fields.": "可用的設定項名稱詳見源碼。", "Quick Favorite": "快速收藏", "Long press on the favorite button to quickly add to this folder": "長按收藏按鈕快速添加到這個資料夾", "Added": "已添加", diff --git a/lib/foundation/appdata.dart b/lib/foundation/appdata.dart index 4b6b735..c6842f5 100644 --- a/lib/foundation/appdata.dart +++ b/lib/foundation/appdata.dart @@ -23,9 +23,26 @@ class Appdata with Init { } _isSavingData = true; try { - var data = jsonEncode(toJson()); + var futures = []; + var json = toJson(); + var data = jsonEncode(json); var file = File(FilePath.join(App.dataPath, 'appdata.json')); - await file.writeAsString(data); + futures.add(file.writeAsString(data)); + + var disableSyncFields = json["settings"]["disableSyncFields"] as String; + if (disableSyncFields.isNotEmpty){ + var json4sync = jsonDecode(data); + List customDisableSync = splitField(disableSyncFields); + for (var field in customDisableSync) { + json4sync["settings"].remove(field); + } + var data4sync = jsonEncode(json4sync); + var file4sync = File(FilePath.join(App.dataPath, 'syncdata.json')); + futures.add(file4sync.writeAsString(data4sync)); + } + + await Future.wait(futures); + } finally { _isSavingData = false; } @@ -59,20 +76,33 @@ class Appdata with Init { return {'settings': settings._data, 'searchHistory': searchHistory}; } + List splitField(String merged) { + return merged + .split(',') + .map((field) => field.trim()) + .where((field) => field.isNotEmpty) + .toList(); + } + /// Following fields are related to device-specific data and should not be synced. static const _disableSync = [ "proxy", "authorizationRequired", "customImageProcessing", "webdav", + "disableSyncFields", ]; /// Sync data from another device void syncData(Map data) { if (data['settings'] is Map) { var settings = data['settings'] as Map; + + List customDisableSync = splitField(this.settings["disableSyncFields"] as String); + for (var key in settings.keys) { - if (!_disableSync.contains(key)) { + if (!_disableSync.contains(key) && + !customDisableSync.contains(key)) { this.settings[key] = settings[key]; } } @@ -166,6 +196,7 @@ class Settings with ChangeNotifier { 'checkUpdateOnStart': false, 'limitImageWidth': true, 'webdav': [], // empty means not configured + "disableSyncFields": "", // "field1, field2, ..." 'dataVersion': 0, 'quickFavorite': null, 'enableTurnPageByVolumeKey': true, diff --git a/lib/pages/settings/app.dart b/lib/pages/settings/app.dart index 217edc3..59f5ac5 100644 --- a/lib/pages/settings/app.dart +++ b/lib/pages/settings/app.dart @@ -100,7 +100,7 @@ class _AppSettingsState extends State { title: "Export App Data".tl, callback: () async { var controller = showLoadingDialog(context); - var file = await exportAppData(); + var file = await exportAppData(false); await saveFile(filename: "data.venera", file: file); controller.close(); }, @@ -353,6 +353,8 @@ class _WebdavSettingState extends State<_WebdavSetting> { String url = ""; String user = ""; String pass = ""; + String disableSync = ""; + bool autoSync = true; bool isTesting = false; @@ -364,6 +366,9 @@ class _WebdavSettingState extends State<_WebdavSetting> { if (appdata.settings['webdav'] is! List) { appdata.settings['webdav'] = []; } + if (appdata.settings['disableSyncFields'].trim().isNotEmpty) { + disableSync = appdata.settings['disableSyncFields']; + } var configs = appdata.settings['webdav'] as List; if (configs.whereType().length != 3) { return; @@ -418,6 +423,56 @@ class _WebdavSettingState extends State<_WebdavSetting> { onChanged: (value) => pass = value, ), const SizedBox(height: 12), + TextField( + decoration: InputDecoration( + labelText: "Skip Setting Fields (Optional)".tl, + hintText: "field0, field1, field2, ...", + hintStyle: TextStyle(color: Theme.of(context).hintColor), + border: OutlineInputBorder(), + suffixIcon: IconButton( + icon: Icon(Icons.help_outline), + onPressed: () { + showDialog( + context: context, + builder: (_) => AlertDialog( + title: Text("Skip Setting Fields".tl), + content: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + "When sync data, skip certain setting fields, which means these won't be uploaded / override.".tl, + ), + const SizedBox(height: 12), + Row( + children: [ + Expanded( + child: Text( + "See source code for available fields.".tl, + ), + ), + Align( + alignment: Alignment.centerRight, + child: IconButton( + icon: const Icon(Icons.open_in_new), + onPressed: () { + launchUrlString("https://github.com/venera-app/venera/blob/b08f11f6ac49bd07d34b4fcde233ed07e86efbc9/lib/foundation/appdata.dart#L138"); + }, + ), + ), + ], + ), + ], + ), + ), + ); + }, + ), + ), + controller: TextEditingController(text: disableSync), + onChanged: (value) => disableSync = value, + ), + const SizedBox(height: 12), ListTile( leading: Icon(Icons.sync), title: Text("Auto Sync Data".tl), @@ -494,6 +549,7 @@ class _WebdavSettingState extends State<_WebdavSetting> { } appdata.settings['webdav'] = [url, user, pass]; + appdata.settings['disableSyncFields'] = disableSync; appdata.implicitData['webdavAutoSync'] = autoSync; appdata.writeImplicitData(); diff --git a/lib/utils/data.dart b/lib/utils/data.dart index bd5195b..8ea6632 100644 --- a/lib/utils/data.dart +++ b/lib/utils/data.dart @@ -15,7 +15,7 @@ import 'package:zip_flutter/zip_flutter.dart'; import 'io.dart'; -Future exportAppData() async { +Future exportAppData([bool sync = true]) async { var time = DateTime.now().millisecondsSinceEpoch ~/ 1000; var cacheFilePath = FilePath.join(App.cachePath, '$time.venera'); var cacheFile = File(cacheFilePath); @@ -27,7 +27,7 @@ Future exportAppData() async { var zipFile = ZipFile.open(cacheFilePath); var historyFile = FilePath.join(dataPath, "history.db"); var localFavoriteFile = FilePath.join(dataPath, "local_favorite.db"); - var appdata = FilePath.join(dataPath, "appdata.json"); + var appdata = FilePath.join(dataPath, sync ? "syncdata.json" : "appdata.json"); var cookies = FilePath.join(dataPath, "cookie.db"); zipFile.addFile("history.db", historyFile); zipFile.addFile("local_favorite.db", localFavoriteFile); diff --git a/lib/utils/data_sync.dart b/lib/utils/data_sync.dart index 2d6e048..e1ca58f 100644 --- a/lib/utils/data_sync.dart +++ b/lib/utils/data_sync.dart @@ -130,7 +130,9 @@ class DataSync with ChangeNotifier { try { appdata.settings['dataVersion']++; await appdata.saveData(false); - var data = await exportAppData(); + var data = await exportAppData( + appdata.settings['disableSyncFields'].toString().isNotEmpty + ); var time = (DateTime.now().millisecondsSinceEpoch ~/ 86400000).toString(); var filename = time;