opt.: store

This commit is contained in:
lollipopkit
2023-08-28 18:07:22 +08:00
parent e20f2d32e8
commit 11c3bf795b
20 changed files with 193 additions and 122 deletions

View File

@@ -18,8 +18,8 @@ class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
setTransparentNavigationBar(context);
primaryColor = Color(_setting.primaryColor.fetch()!);
final fullScreen = _setting.fullScreen.fetch()!;
primaryColor = Color(_setting.primaryColor.fetch());
final fullScreen = _setting.fullScreen.fetch();
return ValueListenableBuilder<int>(
valueListenable: _setting.themeMode.listenable(),
@@ -28,7 +28,7 @@ class MyApp extends StatelessWidget {
// Issue #57
// if not [ok] -> [AMOLED] mode, use [ThemeMode.dark]
final themeMode = isAMOLED ? ThemeMode.values[tMode] : ThemeMode.dark;
final locale = _setting.locale.fetch()?.toLocale;
final locale = _setting.locale.fetch().toLocale;
final darkTheme = ThemeData(
useMaterial3: true,
brightness: Brightness.dark,

View File

@@ -7,7 +7,7 @@ extension OrderX<T> on Order<T> {
void move(
int oldIndex,
int newIndex, {
StoreProperty<List<T>>? property,
StorePropertyBase<List<T>>? property,
_OnMove<T>? onMove,
}) {
if (oldIndex == newIndex) return;
@@ -35,7 +35,7 @@ extension OrderX<T> on Order<T> {
List<T> items,
int o,
int n, {
StoreProperty<List<T>>? property,
StorePropertyBase<List<T>>? property,
_OnMove<T>? onMove,
}) {
if (o == n) return;

View File

@@ -10,35 +10,72 @@ class PersistentStore<E> {
box = await Hive.openBox(boxName);
return this;
}
StoreProperty<T> property<T>(String key, {T? defaultValue}) {
return StoreProperty<T>(box, key, defaultValue);
}
}
class StoreProperty<T> {
abstract class StorePropertyBase<T> {
ValueListenable<T> listenable();
T fetch();
Future<void> put(T value);
Future<void> delete();
}
class StoreProperty<T> implements StorePropertyBase<T> {
StoreProperty(this._box, this._key, this.defaultValue);
final Box _box;
final String _key;
T? defaultValue;
T defaultValue;
@override
ValueListenable<T> listenable() {
return PropertyListenable<T>(_box, _key, defaultValue);
}
T? fetch() {
return _box.get(_key, defaultValue: defaultValue);
}
dynamic fetchRaw() {
return _box.get(_key, defaultValue: defaultValue);
@override
T fetch() {
return _box.get(_key, defaultValue: defaultValue)!;
}
@override
Future<void> put(T value) {
return _box.put(_key, value);
}
@override
Future<void> delete() {
return _box.delete(_key);
}
}
class StoreListProperty<T> implements StorePropertyBase<List<T>> {
StoreListProperty(this._box, this._key, this.defaultValue);
final Box _box;
final String _key;
List<T> defaultValue;
@override
ValueListenable<List<T>> listenable() {
return PropertyListenable<List<T>>(_box, _key, defaultValue);
}
@override
List<T> fetch() {
final val = _box.get(_key, defaultValue: defaultValue)!;
if (val is! List) {
throw Exception('StoreListProperty("$_key") is: ${val.runtimeType}');
}
return List<T>.from(val);
}
@override
Future<void> put(List<T> value) {
return _box.put(_key, value);
}
@override
Future<void> delete() {
return _box.delete(_key);
}

View File

@@ -76,7 +76,7 @@ void showLoadingDialog(BuildContext context, {bool barrierDismiss = false}) {
Widget buildSwitch(
BuildContext context,
StoreProperty<bool> prop, {
StorePropertyBase<bool> prop, {
void Function(bool)? func,
}) {
return ValueListenableBuilder(
@@ -115,8 +115,8 @@ String tabTitleName(BuildContext context, AppTab tab) {
}
}
Future<void> loadFontFile(String? localPath) async {
if (localPath == null) return;
Future<void> loadFontFile(String localPath) async {
if (localPath.isEmpty) return;
final name = getFileName(localPath);
if (name == null) return;
var fontLoader = FontLoader(name);

View File

@@ -5,7 +5,7 @@ class TryLimiter {
final Map<String, int> _triedTimes = {};
bool canTry(String id) {
final maxCount = locator<SettingStore>().maxRetryCount.fetch()!;
final maxCount = locator<SettingStore>().maxRetryCount.fetch();
if (maxCount <= 0) {
return true;
}

View File

@@ -49,7 +49,7 @@ class ServerProvider extends ChangeNotifier {
_servers[spi.id] = genServer(spi);
}
final serverOrder_ = _settingStore.serverOrder.fetch();
if (serverOrder_ != null) {
if (serverOrder_.isNotEmpty) {
spis.reorder(
order: serverOrder_,
finder: (n, id) => n.id == id,
@@ -112,7 +112,7 @@ class ServerProvider extends ChangeNotifier {
}
Future<void> startAutoRefresh() async {
final duration = _settingStore.serverStatusUpdateInterval.fetch()!;
final duration = _settingStore.serverStatusUpdateInterval.fetch();
stopAutoRefresh();
if (duration == 0) return;
_timer = Timer.periodic(Duration(seconds: duration), (_) async {

View File

@@ -21,7 +21,7 @@ class SnippetProvider extends ChangeNotifier {
void loadData() {
_snippets = _store.fetch();
final order = _setting.snippetOrder.fetch();
if (order != null) {
if (order.isNotEmpty) {
final surplus = _snippets.reorder(
order: order,
finder: (n, name) => n.name == name,

View File

@@ -42,7 +42,7 @@ class VirtualKeyboard extends TerminalInputHandler with ChangeNotifier {
ctrl: event.ctrl || ctrl,
alt: event.alt || alt,
);
if (_setting.sshVirtualKeyAutoOff.fetch()!) {
if (_setting.sshVirtualKeyAutoOff.fetch()) {
reset(e);
}
return defaultInputHandler.call(e);

View File

@@ -4,7 +4,7 @@ import 'package:toolbox/locator.dart';
import '../model/app/dynamic_color.dart';
Color primaryColor = Color(locator<SettingStore>().primaryColor.fetch()!);
Color primaryColor = Color(locator<SettingStore>().primaryColor.fetch());
final contentColor = DynamicColor(Colors.black87, Colors.white70);
final bgColor = DynamicColor(Colors.white, Colors.black);

View File

@@ -1,7 +1,6 @@
import 'package:flutter/material.dart';
import 'package:toolbox/core/persistant_store.dart';
import 'package:toolbox/core/utils/platform.dart';
import 'package:toolbox/data/model/ssh/virtual_key.dart';
import '../model/app/net_view.dart';
import '../res/default.dart';
@@ -9,112 +8,142 @@ import '../res/default.dart';
class SettingStore extends PersistentStore {
Map<String, dynamic> toJson() => {for (var e in box.keys) e: box.get(e)};
StoreProperty<int> get primaryColor => property<int>(
late final primaryColor = StoreProperty(
box,
'primaryColor',
defaultValue: 4287106639,
4287106639,
);
StoreProperty<int> get serverStatusUpdateInterval => property(
late final serverStatusUpdateInterval = StoreProperty(
box,
'serverStatusUpdateInterval',
defaultValue: defaultUpdateInterval,
defaultUpdateInterval,
);
// Lanch page idx
StoreProperty<int> get launchPage => property(
late final launchPage = StoreProperty(
box,
'launchPage',
defaultValue: defaultLaunchPageIdx,
defaultLaunchPageIdx,
);
// Version of store db
StoreProperty<int> get storeVersion =>
property('storeVersion', defaultValue: 0);
late final storeVersion = StoreProperty(box, 'storeVersion', 0);
StoreProperty<int> get termColorIdx =>
property('termColorIdx', defaultValue: 0);
late final termColorIdx = StoreProperty(box, 'termColorIdx', 0);
// Max retry count when connect to server
StoreProperty<int> get maxRetryCount =>
property('maxRetryCount', defaultValue: 2);
late final maxRetryCount = StoreProperty(box, 'maxRetryCount', 2);
// Night mode: 0 -> auto, 1 -> light, 2 -> dark
StoreProperty<int> get themeMode => property('themeMode', defaultValue: 0);
late final themeMode = StoreProperty(box, 'themeMode', 0);
// Font file path
StoreProperty<String> get fontPath => property('fontPath');
late final fontPath = StoreProperty(box, 'fontPath', '');
// Backgroud running (Android)
StoreProperty<bool> get bgRun => property('bgRun', defaultValue: isAndroid);
late final bgRun = StoreProperty(box, 'bgRun', isAndroid);
// Server order
StoreProperty<List<String>> get serverOrder =>
property('serverOrder', defaultValue: null);
late final serverOrder = StoreListProperty<String>(box, 'serverOrder', []);
StoreProperty<List<String>> get snippetOrder => property(
'snippetOrder',
defaultValue: null,
);
late final snippetOrder = StoreListProperty<String>(box, 'snippetOrder', []);
// Server details page cards order
StoreProperty<List<String>> get detailCardOrder =>
property('detailCardPrder', defaultValue: defaultDetailCardOrder);
late final detailCardOrder =
StoreListProperty(box, 'detailCardPrder', defaultDetailCardOrder);
// SSH term font size
StoreProperty<double> get termFontSize =>
property('termFontSize', defaultValue: 13);
late final termFontSize = StoreProperty(box, 'termFontSize', 13.0);
// Server detail disk ignore path
StoreProperty<List<String>> get diskIgnorePath =>
property('diskIgnorePath', defaultValue: defaultDiskIgnorePath);
late final diskIgnorePath =
StoreListProperty(box, 'diskIgnorePath', defaultDiskIgnorePath);
// Locale
StoreProperty<String> get locale => property('locale', defaultValue: null);
late final locale = StoreProperty<String>(box, 'locale', '');
// SSH virtual key (ctrl | alt) auto turn off
StoreProperty<bool> get sshVirtualKeyAutoOff =>
property('sshVirtualKeyAutoOff', defaultValue: true);
late final sshVirtualKeyAutoOff =
StoreProperty(box, 'sshVirtualKeyAutoOff', true);
StoreProperty<double> get editorFontSize =>
property('editorFontSize', defaultValue: 13);
late final editorFontSize = StoreProperty(box, 'editorFontSize', 13.0);
// Editor theme
StoreProperty<String> get editorTheme =>
property('editorTheme', defaultValue: defaultEditorTheme);
late final editorTheme = StoreProperty(
box,
'editorTheme',
defaultEditorTheme,
);
StoreProperty<String> get editorDarkTheme =>
property('editorDarkTheme', defaultValue: defaultEditorDarkTheme);
late final editorDarkTheme = StoreProperty(
box,
'editorDarkTheme',
defaultEditorDarkTheme,
);
StoreProperty<bool> get fullScreen =>
property('fullScreen', defaultValue: false);
late final fullScreen = StoreProperty(
box,
'fullScreen',
false,
);
StoreProperty<bool> get fullScreenJitter =>
property('fullScreenJitter', defaultValue: true);
late final fullScreenJitter = StoreProperty(
box,
'fullScreenJitter',
true,
);
StoreProperty<int> get fullScreenRotateQuarter =>
property('fullScreenRotateQuarter', defaultValue: 1);
late final fullScreenRotateQuarter = StoreProperty(
box,
'fullScreenRotateQuarter',
1,
);
StoreProperty<int> get keyboardType =>
property('keyboardType', defaultValue: TextInputType.text.index);
late final keyboardType = StoreProperty(
box,
'keyboardType',
TextInputType.text.index,
);
StoreProperty<List<VirtKey>> get sshVirtKeys =>
property('sshVirtKeys', defaultValue: defaultSSHVirtKeys);
late final sshVirtKeys = StoreListProperty(
box,
'sshVirtKeys',
defaultSSHVirtKeys,
);
StoreProperty<NetViewType> get netViewType =>
property('netViewType', defaultValue: NetViewType.speed);
late final netViewType = StoreProperty(
box,
'netViewType',
NetViewType.speed,
);
// Only valid on iOS
StoreProperty<bool> get autoUpdateHomeWidget =>
property('autoUpdateHomeWidget', defaultValue: isIOS);
late final autoUpdateHomeWidget = StoreProperty(
box,
'autoUpdateHomeWidget',
isIOS,
);
StoreProperty<bool> get autoCheckAppUpdate =>
property('autoCheckAppUpdate', defaultValue: true);
late final autoCheckAppUpdate = StoreProperty(
box,
'autoCheckAppUpdate',
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
StoreProperty<bool> get moveOutServerTabFuncBtns =>
property('moveOutServerTabFuncBtns', defaultValue: true);
late final moveOutServerTabFuncBtns = StoreProperty(
box,
'moveOutServerTabFuncBtns',
true,
);
/// Whether use `rm -rf` to delete directory on SFTP
StoreProperty<bool> get sftpRmrfDir =>
property('sftpRmrfDir', defaultValue: true);
late final sftpRmrfDir = StoreProperty(
box,
'sftpRmrfDir',
true,
);
}

View File

@@ -96,7 +96,7 @@ Future<void> initApp() async {
// Android only
if (!isAndroid) return;
// Only start service when [bgRun] is true.
if (locator<SettingStore>().bgRun.fetch() ?? false) {
if (locator<SettingStore>().bgRun.fetch()) {
bgRunChannel.invokeMethod('startService');
}
// SharedPreferences is only used on Android for saving home widgets settings.

View File

@@ -45,7 +45,7 @@ class _FullScreenPageState extends State<FullScreenPage> with AfterLayoutMixin {
void initState() {
super.initState();
switchStatusBar(hide: true);
_rotateQuarter = _setting.fullScreenRotateQuarter.fetch()!;
_rotateQuarter = _setting.fullScreenRotateQuarter.fetch();
_timer = Timer.periodic(const Duration(minutes: 1), (_) {
if (mounted) {
setState(() {});
@@ -362,7 +362,7 @@ class _FullScreenPageState extends State<FullScreenPage> with AfterLayoutMixin {
@override
Future<void> afterFirstLayout(BuildContext context) async {
if (_setting.autoCheckAppUpdate.fetch()!) {
if (_setting.autoCheckAppUpdate.fetch()) {
doUpdate(context);
}
await GetIt.I.allReady();

View File

@@ -54,7 +54,7 @@ class _HomePageState extends State<HomePage>
super.initState();
switchStatusBar(hide: false);
WidgetsBinding.instance.addObserver(this);
_selectIndex.value = _setting.launchPage.fetch()!;
_selectIndex.value = _setting.launchPage.fetch();
// avoid index out of range
if (_selectIndex.value >= AppTab.values.length || _selectIndex.value < 0) {
_selectIndex.value = 0;
@@ -90,7 +90,7 @@ class _HomePageState extends State<HomePage>
break;
case AppLifecycleState.paused:
// Keep running in background on Android device
if (isAndroid && _setting.bgRun.fetch()!) {
if (isAndroid && _setting.bgRun.fetch()) {
if (_app.moveBg) {
bgRunChannel.invokeMethod('sendToBackground');
}
@@ -366,7 +366,7 @@ class _HomePageState extends State<HomePage>
@override
Future<void> afterFirstLayout(BuildContext context) async {
if (_setting.autoCheckAppUpdate.fetch()!) {
if (_setting.autoCheckAppUpdate.fetch()) {
doUpdate(context);
}
updateHomeWidget();
@@ -379,7 +379,7 @@ class _HomePageState extends State<HomePage>
}
void updateHomeWidget() {
if (_setting.autoUpdateHomeWidget.fetch()!) {
if (_setting.autoUpdateHomeWidget.fetch()) {
homeWidgetChannel.invokeMethod('update');
}
}

View File

@@ -61,7 +61,7 @@ class _ServerDetailPageState extends State<ServerDetailPage>
@override
void initState() {
super.initState();
_cardsOrder.addAll(_setting.detailCardOrder.fetch()!);
_cardsOrder.addAll(_setting.detailCardOrder.fetch());
}
@override
@@ -80,7 +80,7 @@ class _ServerDetailPageState extends State<ServerDetailPage>
}
Widget _buildMainPage(Server si) {
final buildFuncs = !_setting.moveOutServerTabFuncBtns.fetch()!;
final buildFuncs = !_setting.moveOutServerTabFuncBtns.fetch();
return Scaffold(
appBar: CustomAppBar(
title: Text(si.spi.name, style: textSize18),
@@ -299,7 +299,7 @@ class _ServerDetailPageState extends State<ServerDetailPage>
Widget _buildDiskView(ServerStatus ss) {
final disk = ss.disk;
disk.removeWhere((e) {
for (final ingorePath in _setting.diskIgnorePath.fetch()!) {
for (final ingorePath in _setting.diskIgnorePath.fetch()) {
if (e.path.startsWith(ingorePath)) return true;
}
return false;

View File

@@ -238,7 +238,7 @@ class _ServerPageState extends State<ServerPage>
),
),
height13,
if (_settingStore.moveOutServerTabFuncBtns.fetch()!)
if (_settingStore.moveOutServerTabFuncBtns.fetch())
SizedBox(
height: 27,
child: ServerFuncBtns(spi: spi, s: _s),
@@ -446,7 +446,7 @@ class _ServerPageState extends State<ServerPage>
if (cs != ServerState.finished) {
return 23.0;
}
if (_settingStore.moveOutServerTabFuncBtns.fetch()!) {
if (_settingStore.moveOutServerTabFuncBtns.fetch()) {
return 132;
}
return 107;

View File

@@ -77,7 +77,12 @@ class _SettingPageState extends State<SettingPage> {
void didChangeDependencies() {
super.didChangeDependencies();
_s = S.of(context)!;
_localeCode.value = _setting.locale.fetch() ?? _s.localeName;
final localeSettingVal = _setting.locale.fetch();
if (localeSettingVal.isEmpty) {
_localeCode.value = _s.localeName;
} else {
_localeCode.value = localeSettingVal;
}
}
@override
@@ -85,18 +90,18 @@ class _SettingPageState extends State<SettingPage> {
super.initState();
_serverProvider = locator<ServerProvider>();
_setting = locator<SettingStore>();
_launchPageIdx.value = _setting.launchPage.fetch()!;
_nightMode.value = _setting.themeMode.fetch()!;
_updateInterval.value = _setting.serverStatusUpdateInterval.fetch()!;
_maxRetryCount.value = _setting.maxRetryCount.fetch()!;
_selectedColorValue.value = _setting.primaryColor.fetch()!;
_termFontSize.value = _setting.termFontSize.fetch()!;
_editorFontSize.value = _setting.editorFontSize.fetch()!;
_editorTheme.value = _setting.editorTheme.fetch()!;
_editorDarkTheme.value = _setting.editorDarkTheme.fetch()!;
_keyboardType.value = _setting.keyboardType.fetch()!;
_rotateQuarter.value = _setting.fullScreenRotateQuarter.fetch()!;
_netViewType.value = _setting.netViewType.fetch()!;
_launchPageIdx.value = _setting.launchPage.fetch();
_nightMode.value = _setting.themeMode.fetch();
_updateInterval.value = _setting.serverStatusUpdateInterval.fetch();
_maxRetryCount.value = _setting.maxRetryCount.fetch();
_selectedColorValue.value = _setting.primaryColor.fetch();
_termFontSize.value = _setting.termFontSize.fetch();
_editorFontSize.value = _setting.editorFontSize.fetch();
_editorTheme.value = _setting.editorTheme.fetch();
_editorDarkTheme.value = _setting.editorDarkTheme.fetch();
_keyboardType.value = _setting.keyboardType.fetch();
_rotateQuarter.value = _setting.fullScreenRotateQuarter.fetch();
_netViewType.value = _setting.netViewType.fetch();
SharedPreferences.getInstance().then((value) => _sp = value);
}
@@ -563,7 +568,7 @@ class _SettingPageState extends State<SettingPage> {
}
Widget _buildDiskIgnorePath() {
final paths = _setting.diskIgnorePath.fetch()!;
final paths = _setting.diskIgnorePath.fetch();
return ListTile(
title: Text(_s.diskIgnorePath),
trailing: Text(_s.edit, style: textSize15),
@@ -987,7 +992,7 @@ class _SettingPageState extends State<SettingPage> {
void _showFontSizeDialog(
ValueNotifier<double> notifier,
StoreProperty property,
StorePropertyBase<double> property,
) {
final ctrller = TextEditingController(text: notifier.value.toString());
void onSave() {

View File

@@ -30,7 +30,7 @@ class _ServerDetailOrderPageState extends State<ServerDetailOrderPage> {
@override
void initState() {
super.initState();
_cardsOrder.addAll(_store.detailCardOrder.fetch()!);
_cardsOrder.addAll(_store.detailCardOrder.fetch());
}
@override

View File

@@ -39,7 +39,7 @@ class _SSHVirtKeySettingPageState extends State<SSHVirtKeySettingPage> {
}
Widget _buildBody() {
final keys_ = _setting.sshVirtKeys.fetchRaw()!;
final keys_ = _setting.sshVirtKeys.fetch();
final keys = <VirtKey>[];
for (final key in keys_) {
keys.add(key);

View File

@@ -61,10 +61,10 @@ class _SSHPageState extends State<SSHPage> {
final fontFamilly = getFileName(_setting.fontPath.fetch());
final textStyle = TextStyle(
fontFamily: fontFamilly,
fontSize: _setting.termFontSize.fetch()!,
fontSize: _setting.termFontSize.fetch(),
);
_terminalStyle = TerminalStyle.fromTextStyle(textStyle);
_keyboardType = TextInputType.values[_setting.keyboardType.fetch()!];
_keyboardType = TextInputType.values[_setting.keyboardType.fetch()];
_initTerminal();
_initVirtKeys();
}
@@ -299,7 +299,7 @@ class _SSHPageState extends State<SSHPage> {
}
void _initVirtKeys() {
final virtKeys = List<VirtKey>.from(_setting.sshVirtKeys.fetchRaw());
final virtKeys = List<VirtKey>.from(_setting.sshVirtKeys.fetch());
for (int len = 0; len < virtKeys.length; len += 7) {
if (len + 7 > virtKeys.length) {

View File

@@ -430,7 +430,7 @@ class _SftpPageState extends State<SftpPage> with AfterLayoutMixin {
void _delete(BuildContext context, SftpName file) {
context.pop();
final isDir = file.attr.isDirectory;
final useRmrf = _setting.sftpRmrfDir.fetch()!;
final useRmrf = _setting.sftpRmrfDir.fetch();
final dirText = (isDir && !useRmrf) ? '\n${_s.sureDirEmpty}' : '';
final text = '${_s.sureDelete(file.filename)}$dirText';
final child = Text(text);