chore: screenshots
BIN
imgs/detail.png
|
Before Width: | Height: | Size: 130 KiB After Width: | Height: | Size: 135 KiB |
BIN
imgs/docker.png
|
Before Width: | Height: | Size: 141 KiB After Width: | Height: | Size: 115 KiB |
BIN
imgs/editor.png
|
Before Width: | Height: | Size: 157 KiB After Width: | Height: | Size: 173 KiB |
BIN
imgs/server.png
|
Before Width: | Height: | Size: 120 KiB After Width: | Height: | Size: 135 KiB |
BIN
imgs/sftp.png
|
Before Width: | Height: | Size: 137 KiB After Width: | Height: | Size: 135 KiB |
BIN
imgs/ssh.png
|
Before Width: | Height: | Size: 95 KiB After Width: | Height: | Size: 144 KiB |
@@ -6,4 +6,12 @@ extension ListX<T> on List<T> {
|
|||||||
}
|
}
|
||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
List<T> combine(List<T> other, [bool self = true]) {
|
||||||
|
final list = self ? this : List<T>.from(this);
|
||||||
|
for (var i = 0; i < length; i++) {
|
||||||
|
list[i] = other[i];
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:io';
|
|
||||||
|
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:hive_flutter/hive_flutter.dart';
|
import 'package:hive_flutter/hive_flutter.dart';
|
||||||
import 'package:toolbox/data/res/path.dart';
|
import 'package:toolbox/core/utils/misc.dart';
|
||||||
|
|
||||||
// abstract final class SecureStore {
|
// abstract final class SecureStore {
|
||||||
// static const _secureStorage = FlutterSecureStorage();
|
// static const _secureStorage = FlutterSecureStorage();
|
||||||
@@ -30,8 +29,8 @@ import 'package:toolbox/data/res/path.dart';
|
|||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
|
|
||||||
class PersistentStore<E> {
|
class PersistentStore {
|
||||||
late final Box<E> box;
|
late final Box box;
|
||||||
|
|
||||||
final String boxName;
|
final String boxName;
|
||||||
|
|
||||||
@@ -41,35 +40,48 @@ class PersistentStore<E> {
|
|||||||
boxName,
|
boxName,
|
||||||
//encryptionCipher: SecureStore._cipher,
|
//encryptionCipher: SecureStore._cipher,
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/// Get all db filenames.
|
extension BoxX on Box {
|
||||||
///
|
static const _internalPreffix = '_sbi_';
|
||||||
/// - [suffixs] defaults to ['.hive']
|
|
||||||
///
|
/// Last modified timestamp
|
||||||
/// - If [hideSetting] is true, hide 'setting.hive'
|
static const String lastModifiedKey = '${_internalPreffix}lastModified';
|
||||||
static Future<List<String>> getFileNames({
|
int? get lastModified {
|
||||||
bool hideSetting = false,
|
final val = get(lastModifiedKey);
|
||||||
List<String>? suffixs,
|
if (val == null || val is! int) {
|
||||||
}) async {
|
final time = timeStamp;
|
||||||
final docPath = await Paths.doc;
|
put(lastModifiedKey, time);
|
||||||
final dir = Directory(docPath);
|
return time;
|
||||||
final files = await dir.list().toList();
|
|
||||||
if (suffixs != null) {
|
|
||||||
files.removeWhere((e) => !suffixs.contains(e.path.split('.').last));
|
|
||||||
} else {
|
|
||||||
// filter out non-hive(db) files
|
|
||||||
files.removeWhere((e) => !e.path.endsWith('.hive'));
|
|
||||||
}
|
}
|
||||||
if (hideSetting) {
|
return val;
|
||||||
files.removeWhere((e) => e.path.endsWith('setting.hive'));
|
|
||||||
}
|
|
||||||
final paths =
|
|
||||||
files.map((e) => e.path.replaceFirst('$docPath/', '')).toList();
|
|
||||||
return paths;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> updateLastModified() => put(lastModifiedKey, timeStamp);
|
||||||
|
|
||||||
/// Convert db to json
|
/// Convert db to json
|
||||||
Map<String, dynamic> toJson() => {for (var e in box.keys) e: box.get(e)};
|
Map<String, dynamic> toJson({bool includeInternal = true}) {
|
||||||
|
final json = <String, dynamic>{};
|
||||||
|
for (final key in keys) {
|
||||||
|
if (key is String &&
|
||||||
|
key.startsWith(_internalPreffix) &&
|
||||||
|
!includeInternal) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
json[key] = get(key);
|
||||||
|
}
|
||||||
|
return json;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension StoreX on PersistentStore {
|
||||||
|
_StoreProperty<T> property<T>(String key, T defaultValue) {
|
||||||
|
return _StoreProperty<T>(box, key, defaultValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
_StoreListProperty<T> listProperty<T>(String key, List<T> defaultValue) {
|
||||||
|
return _StoreListProperty<T>(box, key, defaultValue);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract class StorePropertyBase<T> {
|
abstract class StorePropertyBase<T> {
|
||||||
@@ -79,8 +91,8 @@ abstract class StorePropertyBase<T> {
|
|||||||
Future<void> delete();
|
Future<void> delete();
|
||||||
}
|
}
|
||||||
|
|
||||||
class StoreProperty<T> implements StorePropertyBase<T> {
|
class _StoreProperty<T> implements StorePropertyBase<T> {
|
||||||
StoreProperty(this._box, this._key, this.defaultValue);
|
_StoreProperty(this._box, this._key, this.defaultValue);
|
||||||
|
|
||||||
final Box _box;
|
final Box _box;
|
||||||
final String _key;
|
final String _key;
|
||||||
@@ -102,6 +114,7 @@ class StoreProperty<T> implements StorePropertyBase<T> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> put(T value) {
|
Future<void> put(T value) {
|
||||||
|
_box.updateLastModified();
|
||||||
return _box.put(_key, value);
|
return _box.put(_key, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -111,8 +124,8 @@ class StoreProperty<T> implements StorePropertyBase<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class StoreListProperty<T> implements StorePropertyBase<List<T>> {
|
class _StoreListProperty<T> implements StorePropertyBase<List<T>> {
|
||||||
StoreListProperty(this._box, this._key, this.defaultValue);
|
_StoreListProperty(this._box, this._key, this.defaultValue);
|
||||||
|
|
||||||
final Box _box;
|
final Box _box;
|
||||||
final String _key;
|
final String _key;
|
||||||
|
|||||||
@@ -2,29 +2,13 @@ import 'dart:async';
|
|||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:icloud_storage/icloud_storage.dart';
|
import 'package:icloud_storage/icloud_storage.dart';
|
||||||
|
import 'package:toolbox/data/model/app/sync.dart';
|
||||||
import 'package:toolbox/data/res/logger.dart';
|
import 'package:toolbox/data/res/logger.dart';
|
||||||
|
|
||||||
import '../../data/model/app/error.dart';
|
import '../../data/model/app/error.dart';
|
||||||
import '../../data/model/app/json.dart';
|
import '../../data/model/app/json.dart';
|
||||||
import '../../data/res/path.dart';
|
import '../../data/res/path.dart';
|
||||||
|
|
||||||
class SyncResult<T, E> {
|
|
||||||
final List<T> up;
|
|
||||||
final List<T> down;
|
|
||||||
final Map<T, E> err;
|
|
||||||
|
|
||||||
const SyncResult({
|
|
||||||
required this.up,
|
|
||||||
required this.down,
|
|
||||||
required this.err,
|
|
||||||
});
|
|
||||||
|
|
||||||
@override
|
|
||||||
String toString() {
|
|
||||||
return 'SyncResult{up: $up, down: $down, err: $err}';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
abstract final class ICloud {
|
abstract final class ICloud {
|
||||||
static const _containerId = 'iCloud.tech.lolli.serverbox';
|
static const _containerId = 'iCloud.tech.lolli.serverbox';
|
||||||
|
|
||||||
@@ -111,7 +95,7 @@ abstract final class ICloud {
|
|||||||
/// Return `null` if upload success, `ICloudErr` otherwise
|
/// Return `null` if upload success, `ICloudErr` otherwise
|
||||||
///
|
///
|
||||||
/// TODO: consider merge strategy, use [SyncAble] and [JsonSerializable]
|
/// TODO: consider merge strategy, use [SyncAble] and [JsonSerializable]
|
||||||
static Future<SyncResult<String, ICloudErr>> sync({
|
static Future<SyncResult<String, ICloudErr>> syncFiles({
|
||||||
required Iterable<String> relativePaths,
|
required Iterable<String> relativePaths,
|
||||||
String? bakPrefix,
|
String? bakPrefix,
|
||||||
}) async {
|
}) async {
|
||||||
@@ -196,4 +180,5 @@ abstract final class ICloud {
|
|||||||
Loggers.app.info('iCloud sync, up: $uploadFiles, down: $downloadFiles');
|
Loggers.app.info('iCloud sync, up: $uploadFiles, down: $downloadFiles');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,3 +37,5 @@ String pathJoin(String path1, String path2) {
|
|||||||
|
|
||||||
/// Check if a url is a file url (ends with a file extension)
|
/// Check if a url is a file url (ends with a file extension)
|
||||||
bool isFileUrl(String url) => url.split('/').last.contains('.');
|
bool isFileUrl(String url) => url.split('/').last.contains('.');
|
||||||
|
|
||||||
|
int get timeStamp => DateTime.now().millisecondsSinceEpoch;
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'package:toolbox/core/persistant_store.dart';
|
||||||
import 'package:toolbox/data/model/server/private_key_info.dart';
|
import 'package:toolbox/data/model/server/private_key_info.dart';
|
||||||
import 'package:toolbox/data/model/server/server_private_info.dart';
|
import 'package:toolbox/data/model/server/server_private_info.dart';
|
||||||
import 'package:toolbox/data/model/server/snippet.dart';
|
import 'package:toolbox/data/model/server/snippet.dart';
|
||||||
@@ -60,8 +61,8 @@ class Backup {
|
|||||||
spis = Stores.server.fetch(),
|
spis = Stores.server.fetch(),
|
||||||
snippets = Stores.snippet.fetch(),
|
snippets = Stores.snippet.fetch(),
|
||||||
keys = Stores.key.fetch(),
|
keys = Stores.key.fetch(),
|
||||||
dockerHosts = Stores.docker.toJson(),
|
dockerHosts = Stores.docker.box.toJson(),
|
||||||
settings = Stores.setting.toJson();
|
settings = Stores.setting.box.toJson();
|
||||||
|
|
||||||
static Future<String> backup() async {
|
static Future<String> backup() async {
|
||||||
final result = _diyEncrypt(json.encode(Backup.loadFromStore()));
|
final result = _diyEncrypt(json.encode(Backup.loadFromStore()));
|
||||||
|
|||||||
24
lib/data/model/app/sync.dart
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
|
||||||
|
class SyncResult<T, E> {
|
||||||
|
final List<T> up;
|
||||||
|
final List<T> down;
|
||||||
|
final Map<T, E> err;
|
||||||
|
|
||||||
|
const SyncResult({
|
||||||
|
required this.up,
|
||||||
|
required this.down,
|
||||||
|
required this.err,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return 'SyncResult{up: $up, down: $down, err: $err}';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class SyncIface<T> {
|
||||||
|
/// Merge [other] into [this], return [this] after merge.
|
||||||
|
/// Data in [other] has higher priority than [this].
|
||||||
|
FutureOr<void> sync(T other);
|
||||||
|
}
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
abstract class SyncAble<T> {
|
|
||||||
/// If [other] is newer than [this] then return true,
|
|
||||||
/// else return false
|
|
||||||
bool needSync(T other);
|
|
||||||
|
|
||||||
/// Merge [other] into [this],
|
|
||||||
/// return [this] after merge
|
|
||||||
T merge(T other);
|
|
||||||
}
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import '../../core/persistant_store.dart';
|
import '../../core/persistant_store.dart';
|
||||||
|
|
||||||
class DockerStore extends PersistentStore<String> {
|
class DockerStore extends PersistentStore {
|
||||||
DockerStore() : super('docker');
|
DockerStore() : super('docker');
|
||||||
|
|
||||||
String? fetch(String? id) {
|
String? fetch(String? id) {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import '../../core/persistant_store.dart';
|
import '../../core/persistant_store.dart';
|
||||||
import '../model/server/private_key_info.dart';
|
import '../model/server/private_key_info.dart';
|
||||||
|
|
||||||
class PrivateKeyStore extends PersistentStore<PrivateKeyInfo> {
|
class PrivateKeyStore extends PersistentStore {
|
||||||
PrivateKeyStore() : super('key');
|
PrivateKeyStore() : super('key');
|
||||||
|
|
||||||
void put(PrivateKeyInfo info) {
|
void put(PrivateKeyInfo info) {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import '../../core/persistant_store.dart';
|
import '../../core/persistant_store.dart';
|
||||||
import '../model/server/server_private_info.dart';
|
import '../model/server/server_private_info.dart';
|
||||||
|
|
||||||
class ServerStore extends PersistentStore<ServerPrivateInfo> {
|
class ServerStore extends PersistentStore {
|
||||||
ServerStore() : super('server');
|
ServerStore() : super('server');
|
||||||
|
|
||||||
void put(ServerPrivateInfo info) {
|
void put(ServerPrivateInfo info) {
|
||||||
|
|||||||
@@ -15,22 +15,19 @@ class SettingStore extends PersistentStore {
|
|||||||
// item in the drawer of the home page)
|
// item in the drawer of the home page)
|
||||||
|
|
||||||
/// Discussion #146
|
/// Discussion #146
|
||||||
late final serverTabUseOldUI = StoreProperty(
|
late final serverTabUseOldUI = property(
|
||||||
box,
|
|
||||||
'serverTabUseOldUI',
|
'serverTabUseOldUI',
|
||||||
false,
|
false,
|
||||||
);
|
);
|
||||||
|
|
||||||
/// Time out for server connect and more...
|
/// Time out for server connect and more...
|
||||||
late final timeout = StoreProperty(
|
late final timeout = property(
|
||||||
box,
|
|
||||||
'timeOut',
|
'timeOut',
|
||||||
5,
|
5,
|
||||||
);
|
);
|
||||||
|
|
||||||
/// Record history of SFTP path and etc.
|
/// Record history of SFTP path and etc.
|
||||||
late final recordHistory = StoreProperty(
|
late final recordHistory = property(
|
||||||
box,
|
|
||||||
'recordHistory',
|
'recordHistory',
|
||||||
true,
|
true,
|
||||||
);
|
);
|
||||||
@@ -38,142 +35,125 @@ class SettingStore extends PersistentStore {
|
|||||||
/// Bigger for bigger font size
|
/// Bigger for bigger font size
|
||||||
/// 1.0 means 100%
|
/// 1.0 means 100%
|
||||||
/// Warning: This may cause some UI issues
|
/// Warning: This may cause some UI issues
|
||||||
late final textFactor = StoreProperty(
|
late final textFactor = property(
|
||||||
box,
|
|
||||||
'textFactor',
|
'textFactor',
|
||||||
1.0,
|
1.0,
|
||||||
);
|
);
|
||||||
|
|
||||||
/// Lanch page idx
|
/// Lanch page idx
|
||||||
late final launchPage = StoreProperty(
|
late final launchPage = property(
|
||||||
box,
|
|
||||||
'launchPage',
|
'launchPage',
|
||||||
Defaults.launchPageIdx,
|
Defaults.launchPageIdx,
|
||||||
);
|
);
|
||||||
|
|
||||||
/// Server detail disk ignore path
|
/// Server detail disk ignore path
|
||||||
late final diskIgnorePath =
|
late final diskIgnorePath =
|
||||||
StoreListProperty(box, 'diskIgnorePath', Defaults.diskIgnorePath);
|
property('diskIgnorePath', Defaults.diskIgnorePath);
|
||||||
|
|
||||||
/// Use double column servers page on Desktop
|
/// Use double column servers page on Desktop
|
||||||
late final doubleColumnServersPage = StoreProperty(
|
late final doubleColumnServersPage = property(
|
||||||
box,
|
|
||||||
'doubleColumnServersPage',
|
'doubleColumnServersPage',
|
||||||
isDesktop,
|
isDesktop,
|
||||||
);
|
);
|
||||||
|
|
||||||
/// Disk view: amount / IO
|
/// Disk view: amount / IO
|
||||||
late final serverTabPreferDiskAmount = StoreProperty(
|
late final serverTabPreferDiskAmount = property(
|
||||||
box,
|
|
||||||
'serverTabPreferDiskAmount',
|
'serverTabPreferDiskAmount',
|
||||||
false,
|
false,
|
||||||
);
|
);
|
||||||
|
|
||||||
// ------END------
|
// ------END------
|
||||||
|
|
||||||
late final primaryColor = StoreProperty(
|
late final primaryColor = property(
|
||||||
box,
|
|
||||||
'primaryColor',
|
'primaryColor',
|
||||||
4287106639,
|
4287106639,
|
||||||
);
|
);
|
||||||
|
|
||||||
late final serverStatusUpdateInterval = StoreProperty(
|
late final serverStatusUpdateInterval = property(
|
||||||
box,
|
|
||||||
'serverStatusUpdateInterval',
|
'serverStatusUpdateInterval',
|
||||||
Defaults.updateInterval,
|
Defaults.updateInterval,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Max retry count when connect to server
|
// Max retry count when connect to server
|
||||||
late final maxRetryCount = StoreProperty(box, 'maxRetryCount', 2);
|
late final maxRetryCount = property('maxRetryCount', 2);
|
||||||
|
|
||||||
// Night mode: 0 -> auto, 1 -> light, 2 -> dark, 3 -> AMOLED, 4 -> AUTO-AMOLED
|
// Night mode: 0 -> auto, 1 -> light, 2 -> dark, 3 -> AMOLED, 4 -> AUTO-AMOLED
|
||||||
late final themeMode = StoreProperty(box, 'themeMode', 0);
|
late final themeMode = property('themeMode', 0);
|
||||||
|
|
||||||
// Font file path
|
// Font file path
|
||||||
late final fontPath = StoreProperty(box, 'fontPath', '');
|
late final fontPath = property('fontPath', '');
|
||||||
|
|
||||||
// Backgroud running (Android)
|
// Backgroud running (Android)
|
||||||
late final bgRun = StoreProperty(box, 'bgRun', isAndroid);
|
late final bgRun = property('bgRun', isAndroid);
|
||||||
|
|
||||||
// Server order
|
// Server order
|
||||||
late final serverOrder = StoreListProperty<String>(box, 'serverOrder', []);
|
late final serverOrder = listProperty<String>('serverOrder', []);
|
||||||
|
|
||||||
late final snippetOrder = StoreListProperty<String>(box, 'snippetOrder', []);
|
late final snippetOrder = listProperty<String>('snippetOrder', []);
|
||||||
|
|
||||||
// Server details page cards order
|
// Server details page cards order
|
||||||
late final detailCardOrder =
|
late final detailCardOrder =
|
||||||
StoreListProperty(box, 'detailCardPrder', Defaults.detailCardOrder);
|
listProperty('detailCardPrder', Defaults.detailCardOrder);
|
||||||
|
|
||||||
// SSH term font size
|
// SSH term font size
|
||||||
late final termFontSize = StoreProperty(box, 'termFontSize', 13.0);
|
late final termFontSize = property('termFontSize', 13.0);
|
||||||
|
|
||||||
// Locale
|
// Locale
|
||||||
late final locale = StoreProperty<String>(box, 'locale', '');
|
late final locale = property<String>('locale', '');
|
||||||
|
|
||||||
// SSH virtual key (ctrl | alt) auto turn off
|
// SSH virtual key (ctrl | alt) auto turn off
|
||||||
late final sshVirtualKeyAutoOff =
|
late final sshVirtualKeyAutoOff = property('sshVirtualKeyAutoOff', true);
|
||||||
StoreProperty(box, 'sshVirtualKeyAutoOff', true);
|
|
||||||
|
|
||||||
late final editorFontSize = StoreProperty(box, 'editorFontSize', 13.0);
|
late final editorFontSize = property('editorFontSize', 13.0);
|
||||||
|
|
||||||
// Editor theme
|
// Editor theme
|
||||||
late final editorTheme = StoreProperty(
|
late final editorTheme = property(
|
||||||
box,
|
|
||||||
'editorTheme',
|
'editorTheme',
|
||||||
Defaults.editorTheme,
|
Defaults.editorTheme,
|
||||||
);
|
);
|
||||||
|
|
||||||
late final editorDarkTheme = StoreProperty(
|
late final editorDarkTheme = property(
|
||||||
box,
|
|
||||||
'editorDarkTheme',
|
'editorDarkTheme',
|
||||||
Defaults.editorDarkTheme,
|
Defaults.editorDarkTheme,
|
||||||
);
|
);
|
||||||
|
|
||||||
late final fullScreen = StoreProperty(
|
late final fullScreen = property(
|
||||||
box,
|
|
||||||
'fullScreen',
|
'fullScreen',
|
||||||
false,
|
false,
|
||||||
);
|
);
|
||||||
|
|
||||||
late final fullScreenJitter = StoreProperty(
|
late final fullScreenJitter = property(
|
||||||
box,
|
|
||||||
'fullScreenJitter',
|
'fullScreenJitter',
|
||||||
true,
|
true,
|
||||||
);
|
);
|
||||||
|
|
||||||
late final fullScreenRotateQuarter = StoreProperty(
|
late final fullScreenRotateQuarter = property(
|
||||||
box,
|
|
||||||
'fullScreenRotateQuarter',
|
'fullScreenRotateQuarter',
|
||||||
1,
|
1,
|
||||||
);
|
);
|
||||||
|
|
||||||
late final keyboardType = StoreProperty(
|
late final keyboardType = property(
|
||||||
box,
|
|
||||||
'keyboardType',
|
'keyboardType',
|
||||||
TextInputType.text.index,
|
TextInputType.text.index,
|
||||||
);
|
);
|
||||||
|
|
||||||
late final sshVirtKeys = StoreListProperty(
|
late final sshVirtKeys = listProperty(
|
||||||
box,
|
|
||||||
'sshVirtKeys',
|
'sshVirtKeys',
|
||||||
Defaults.sshVirtKeys,
|
Defaults.sshVirtKeys,
|
||||||
);
|
);
|
||||||
|
|
||||||
late final netViewType = StoreProperty(
|
late final netViewType = property(
|
||||||
box,
|
|
||||||
'netViewType',
|
'netViewType',
|
||||||
NetViewType.speed,
|
NetViewType.speed,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Only valid on iOS
|
// Only valid on iOS
|
||||||
late final autoUpdateHomeWidget = StoreProperty(
|
late final autoUpdateHomeWidget = property(
|
||||||
box,
|
|
||||||
'autoUpdateHomeWidget',
|
'autoUpdateHomeWidget',
|
||||||
isIOS,
|
isIOS,
|
||||||
);
|
);
|
||||||
|
|
||||||
late final autoCheckAppUpdate = StoreProperty(
|
late final autoCheckAppUpdate = property(
|
||||||
box,
|
|
||||||
'autoCheckAppUpdate',
|
'autoCheckAppUpdate',
|
||||||
true,
|
true,
|
||||||
);
|
);
|
||||||
@@ -181,59 +161,54 @@ class SettingStore extends PersistentStore {
|
|||||||
/// Display server tab function buttons on the bottom of each server card if [true]
|
/// Display server tab function buttons on the bottom of each server card if [true]
|
||||||
///
|
///
|
||||||
/// Otherwise, display them on the top of server detail page
|
/// Otherwise, display them on the top of server detail page
|
||||||
late final moveOutServerTabFuncBtns = StoreProperty(
|
late final moveOutServerTabFuncBtns = property(
|
||||||
box,
|
|
||||||
'moveOutServerTabFuncBtns',
|
'moveOutServerTabFuncBtns',
|
||||||
true,
|
true,
|
||||||
);
|
);
|
||||||
|
|
||||||
/// Whether use `rm -r` to delete directory on SFTP
|
/// Whether use `rm -r` to delete directory on SFTP
|
||||||
late final sftpRmrDir = StoreProperty(
|
late final sftpRmrDir = property(
|
||||||
box,
|
|
||||||
'sftpRmrDir',
|
'sftpRmrDir',
|
||||||
false,
|
false,
|
||||||
);
|
);
|
||||||
|
|
||||||
/// Whether use system's primary color as the app's primary color
|
/// Whether use system's primary color as the app's primary color
|
||||||
late final useSystemPrimaryColor = StoreProperty(
|
late final useSystemPrimaryColor = property(
|
||||||
box,
|
|
||||||
'useSystemPrimaryColor',
|
'useSystemPrimaryColor',
|
||||||
false,
|
false,
|
||||||
);
|
);
|
||||||
|
|
||||||
/// Only valid on iOS and macOS
|
/// Only valid on iOS and macOS
|
||||||
late final icloudSync = StoreProperty(
|
late final icloudSync = property(
|
||||||
box,
|
|
||||||
'icloudSync',
|
'icloudSync',
|
||||||
false,
|
false,
|
||||||
);
|
);
|
||||||
|
|
||||||
/// Only valid on iOS / Android / Windows
|
/// Only valid on iOS / Android / Windows
|
||||||
late final useBioAuth = StoreProperty(
|
late final useBioAuth = property(
|
||||||
box,
|
|
||||||
'useBioAuth',
|
'useBioAuth',
|
||||||
false,
|
false,
|
||||||
);
|
);
|
||||||
|
|
||||||
/// The performance of highlight is bad
|
/// The performance of highlight is bad
|
||||||
late final editorHighlight = StoreProperty(box, 'editorHighlight', true);
|
late final editorHighlight = property('editorHighlight', true);
|
||||||
|
|
||||||
/// Open SFTP with last viewed path
|
/// Open SFTP with last viewed path
|
||||||
late final sftpOpenLastPath = StoreProperty(box, 'sftpOpenLastPath', true);
|
late final sftpOpenLastPath = property('sftpOpenLastPath', true);
|
||||||
|
|
||||||
/// Show tip of suspend
|
/// Show tip of suspend
|
||||||
late final showSuspendTip = StoreProperty(box, 'showSuspendTip', true);
|
late final showSuspendTip = property('showSuspendTip', true);
|
||||||
|
|
||||||
/// Server func btns display name
|
/// Server func btns display name
|
||||||
late final serverFuncBtnsDisplayName =
|
late final serverFuncBtnsDisplayName =
|
||||||
StoreProperty(box, 'serverFuncBtnsDisplayName', false);
|
property('serverFuncBtnsDisplayName', false);
|
||||||
|
|
||||||
// Never show these settings for users
|
// Never show these settings for users
|
||||||
//
|
//
|
||||||
// ------BEGIN------
|
// ------BEGIN------
|
||||||
|
|
||||||
/// Version of store db
|
/// Version of store db
|
||||||
late final storeVersion = StoreProperty(box, 'storeVersion', 0);
|
late final storeVersion = property('storeVersion', 0);
|
||||||
|
|
||||||
// ------END------
|
// ------END------
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import '../../core/persistant_store.dart';
|
import '../../core/persistant_store.dart';
|
||||||
import '../model/server/snippet.dart';
|
import '../model/server/snippet.dart';
|
||||||
|
|
||||||
class SnippetStore extends PersistentStore<Snippet> {
|
class SnippetStore extends PersistentStore {
|
||||||
SnippetStore() : super('snippet');
|
SnippetStore() : super('snippet');
|
||||||
|
|
||||||
void put(Snippet snippet) {
|
void put(Snippet snippet) {
|
||||||
|
|||||||
@@ -95,8 +95,6 @@ class BackupPage extends StatelessWidget {
|
|||||||
prop: Stores.setting.icloudSync,
|
prop: Stores.setting.icloudSync,
|
||||||
func: (val) async {
|
func: (val) async {
|
||||||
if (val) {
|
if (val) {
|
||||||
final relativePaths = await PersistentStore.getFileNames();
|
|
||||||
await ICloud.sync(relativePaths: relativePaths);
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
@@ -109,35 +107,25 @@ class BackupPage extends StatelessWidget {
|
|||||||
if (icloudLoading.value) {
|
if (icloudLoading.value) {
|
||||||
return UIs.centerSizedLoadingSmall;
|
return UIs.centerSizedLoadingSmall;
|
||||||
}
|
}
|
||||||
return ConstrainedBox(
|
return Row(
|
||||||
constraints: const BoxConstraints(maxWidth: 137),
|
mainAxisSize: MainAxisSize.min,
|
||||||
child: Row(
|
children: [
|
||||||
children: [
|
TextButton(
|
||||||
TextButton(
|
onPressed: () async {
|
||||||
onPressed: () async {
|
icloudLoading.value = true;
|
||||||
icloudLoading.value = true;
|
icloudLoading.value = false;
|
||||||
final files = await PersistentStore.getFileNames();
|
},
|
||||||
for (final file in files) {
|
child: Text(l10n.download),
|
||||||
await ICloud.download(relativePath: file);
|
),
|
||||||
}
|
UIs.width7,
|
||||||
icloudLoading.value = false;
|
TextButton(
|
||||||
},
|
onPressed: () async {
|
||||||
child: Text(l10n.download),
|
icloudLoading.value = true;
|
||||||
),
|
icloudLoading.value = false;
|
||||||
UIs.width7,
|
},
|
||||||
TextButton(
|
child: Text(l10n.upload),
|
||||||
onPressed: () async {
|
),
|
||||||
icloudLoading.value = true;
|
],
|
||||||
final files = await PersistentStore.getFileNames();
|
|
||||||
for (final file in files) {
|
|
||||||
await ICloud.upload(relativePath: file);
|
|
||||||
}
|
|
||||||
icloudLoading.value = false;
|
|
||||||
},
|
|
||||||
child: Text(l10n.upload),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import 'package:toolbox/core/channel/bg_run.dart';
|
|||||||
import 'package:toolbox/core/channel/home_widget.dart';
|
import 'package:toolbox/core/channel/home_widget.dart';
|
||||||
import 'package:toolbox/core/extension/context/dialog.dart';
|
import 'package:toolbox/core/extension/context/dialog.dart';
|
||||||
import 'package:toolbox/core/extension/context/locale.dart';
|
import 'package:toolbox/core/extension/context/locale.dart';
|
||||||
|
import 'package:toolbox/core/persistant_store.dart';
|
||||||
import 'package:toolbox/core/utils/platform/auth.dart';
|
import 'package:toolbox/core/utils/platform/auth.dart';
|
||||||
import 'package:toolbox/core/utils/platform/base.dart';
|
import 'package:toolbox/core/utils/platform/base.dart';
|
||||||
import 'package:toolbox/data/res/github_id.dart';
|
import 'package:toolbox/data/res/github_id.dart';
|
||||||
@@ -340,7 +341,7 @@ class _HomePageState extends State<HomePage>
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _onLongPressSetting() async {
|
Future<void> _onLongPressSetting() async {
|
||||||
final map = Stores.setting.toJson();
|
final map = Stores.setting.box.toJson(includeInternal: false);
|
||||||
final keys = map.keys;
|
final keys = map.keys;
|
||||||
|
|
||||||
/// Encode [map] to String with indent `\t`
|
/// Encode [map] to String with indent `\t`
|
||||||
|
|||||||
@@ -219,29 +219,31 @@ class _ServerPageState extends State<ServerPage>
|
|||||||
final cardStatus = getCardNoti(id);
|
final cardStatus = getCardNoti(id);
|
||||||
final title = _buildServerCardTitle(srv.status, srv.state, srv.spi);
|
final title = _buildServerCardTitle(srv.status, srv.state, srv.spi);
|
||||||
|
|
||||||
return AnimatedContainer(
|
return ValueBuilder(
|
||||||
duration: const Duration(milliseconds: 377),
|
listenable: cardStatus,
|
||||||
curve: Curves.fastEaseInToSlowEaseOut,
|
build: () {
|
||||||
height: _calcCardHeight(srv.state, cardStatus.value.flip),
|
late final List<Widget> children;
|
||||||
child: ValueBuilder(
|
if (srv.state == ServerState.finished) {
|
||||||
listenable: cardStatus,
|
if (cardStatus.value.flip) {
|
||||||
build: () {
|
children = [title, ..._buildFlipedCard(srv)];
|
||||||
final List<Widget> children = [title];
|
} else {
|
||||||
if (srv.state == ServerState.finished) {
|
children = [title, ..._buildNormalCard(srv.status, srv.spi)];
|
||||||
if (cardStatus.value.flip) {
|
|
||||||
children.addAll(_buildFlipedCard(srv));
|
|
||||||
} else {
|
|
||||||
children.addAll(_buildNormalCard(srv.status, srv.spi));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return Column(
|
} else {
|
||||||
|
children = [title];
|
||||||
|
}
|
||||||
|
return AnimatedContainer(
|
||||||
|
duration: const Duration(milliseconds: 377),
|
||||||
|
curve: Curves.fastEaseInToSlowEaseOut,
|
||||||
|
height: _calcCardHeight(srv.state, cardStatus.value.flip),
|
||||||
|
child: Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
mainAxisAlignment: MainAxisAlignment.start,
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
children: children,
|
children: children,
|
||||||
);
|
),
|
||||||
},
|
);
|
||||||
),
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||