diff --git a/lib/data/model/app/menu/server_func.dart b/lib/data/model/app/menu/server_func.dart index 902963d8..f92a5374 100644 --- a/lib/data/model/app/menu/server_func.dart +++ b/lib/data/model/app/menu/server_func.dart @@ -21,29 +21,27 @@ enum ServerFuncBtn { snippet, @HiveField(6) iperf, - @HiveField(7) - pve, + // @HiveField(7) + // pve, ; - static const defaultFuncs = [ + static final defaultIdxs = [ terminal, sftp, container, process, pkg, snippet, - pve, - ]; + ].map((e) => e.index).toList(); - Icon get icon => switch (this) { - sftp => const Icon(Icons.insert_drive_file, size: 15), - snippet => const Icon(Icons.code, size: 15), - pkg => const Icon(Icons.system_security_update, size: 15), - container => const Icon(FontAwesome.docker_brand, size: 14), - process => const Icon(Icons.list_alt_outlined, size: 15), - terminal => const Icon(Icons.terminal, size: 15), - iperf => const Icon(Icons.speed, size: 15), - pve => const Icon(FontAwesome.server_solid, size: 13), + Icon icon([double? sizeDiff]) => switch (this) { + sftp => Icon(Icons.insert_drive_file, size: 15 + (sizeDiff ?? 0)), + snippet => Icon(Icons.code, size: 15 + (sizeDiff ?? 0)), + pkg => Icon(Icons.system_security_update, size: 15 + (sizeDiff ?? 0)), + container => Icon(FontAwesome.docker_brand, size: 14 + (sizeDiff ?? 0)), + process => Icon(Icons.list_alt_outlined, size: 15 + (sizeDiff ?? 0)), + terminal => Icon(Icons.terminal, size: 15 + (sizeDiff ?? 0)), + iperf => Icon(Icons.speed, size: 15 + (sizeDiff ?? 0)), }; String get toStr => switch (this) { @@ -54,10 +52,5 @@ enum ServerFuncBtn { process => l10n.process, terminal => l10n.terminal, iperf => 'iperf', - pve => 'PVE', }; - - int toJson() => index; - - static ServerFuncBtn fromJson(int i) => values[i]; } diff --git a/lib/data/model/app/menu/server_func.g.dart b/lib/data/model/app/menu/server_func.g.dart index 107700da..cf83cef3 100644 --- a/lib/data/model/app/menu/server_func.g.dart +++ b/lib/data/model/app/menu/server_func.g.dart @@ -27,8 +27,6 @@ class ServerFuncBtnAdapter extends TypeAdapter { return ServerFuncBtn.snippet; case 6: return ServerFuncBtn.iperf; - case 7: - return ServerFuncBtn.pve; default: return ServerFuncBtn.terminal; } @@ -58,9 +56,6 @@ class ServerFuncBtnAdapter extends TypeAdapter { case ServerFuncBtn.iperf: writer.writeByte(6); break; - case ServerFuncBtn.pve: - writer.writeByte(7); - break; } } diff --git a/lib/data/model/app/server_detail_card.dart b/lib/data/model/app/server_detail_card.dart new file mode 100644 index 00000000..ba9c5ced --- /dev/null +++ b/lib/data/model/app/server_detail_card.dart @@ -0,0 +1,56 @@ +import 'package:toolbox/core/extension/context/locale.dart'; +import 'package:toolbox/core/extension/listx.dart'; +import 'package:toolbox/data/model/app/version_related.dart'; +import 'package:toolbox/data/res/store.dart'; + +enum ServerDetailCards implements VersionRelated { + about, + cpu, + mem, + swap, + gpu, + disk, + net, + sensor, + temp, + battery, + pve(sinceBuild: 818), + ; + + @override + final int? sinceBuild; + + const ServerDetailCards({this.sinceBuild}); + + static ServerDetailCards? fromName(String str) => + ServerDetailCards.values.firstWhereOrNull((e) => e.name == str); + + static final names = values.map((e) => e.name).toList(); + + String get toStr => switch (this) { + about => l10n.about, + cpu => 'CPU', + mem => 'RAM', + swap => 'Swap', + gpu => 'GPU', + disk => l10n.disk, + net => l10n.net, + sensor => l10n.sensors, + temp => l10n.temperature, + battery => l10n.battery, + pve => 'PVE', + }; + + /// If: + /// Version 1 => user set [about], default is [about, cpu] + /// Version 2 => default is [about, cpu, mem] => auto add [mem] to user's setting + static void autoAddNewCards(int cur) { + if (cur >= pve.sinceBuild!) { + final prop = Stores.setting.detailCardOrder; + final list = prop.fetch(); + if (list.contains(pve.name)) return; + list.add(pve.name); + prop.put(list); + } + } +} diff --git a/lib/data/model/app/version_related.dart b/lib/data/model/app/version_related.dart new file mode 100644 index 00000000..45194d72 --- /dev/null +++ b/lib/data/model/app/version_related.dart @@ -0,0 +1,9 @@ +import 'package:toolbox/data/model/app/server_detail_card.dart'; + +abstract interface class VersionRelated { + int? get sinceBuild; + + static final funcs = [ + ServerDetailCards.autoAddNewCards, + ]; +} diff --git a/lib/data/model/ssh/virtual_key.dart b/lib/data/model/ssh/virtual_key.dart index 296a93ef..2a7a99a4 100644 --- a/lib/data/model/ssh/virtual_key.dart +++ b/lib/data/model/ssh/virtual_key.dart @@ -54,6 +54,23 @@ enum VirtKey { } } + static final defaultOrder = [ + VirtKey.esc, + VirtKey.alt, + VirtKey.home, + VirtKey.up, + VirtKey.end, + VirtKey.sftp, + VirtKey.snippet, + VirtKey.tab, + VirtKey.ctrl, + VirtKey.left, + VirtKey.down, + VirtKey.right, + VirtKey.clipboard, + VirtKey.ime, + ]; + TerminalKey? get key { switch (this) { case VirtKey.esc: diff --git a/lib/data/res/default.dart b/lib/data/res/default.dart index a6fab0a1..25a48642 100644 --- a/lib/data/res/default.dart +++ b/lib/data/res/default.dart @@ -1,39 +1,7 @@ import 'dart:ui'; -import 'package:toolbox/data/model/ssh/virtual_key.dart'; abstract final class Defaults { - // default server details page cards order - static const detailCardOrder = [ - 'about', - 'cpu', - 'mem', - 'swap', - 'gpu', - 'disk', - 'net', - 'sensor', - 'temp', - 'battery' - ]; - - static const sshVirtKeys = [ - VirtKey.esc, - VirtKey.alt, - VirtKey.home, - VirtKey.up, - VirtKey.end, - VirtKey.sftp, - VirtKey.snippet, - VirtKey.tab, - VirtKey.ctrl, - VirtKey.left, - VirtKey.down, - VirtKey.right, - VirtKey.clipboard, - VirtKey.ime, - ]; - static const primaryColor = Color.fromARGB(255, 145, 58, 31); static const launchPageIdx = 0; diff --git a/lib/data/store/setting.dart b/lib/data/store/setting.dart index 36b49b08..6ce2f9ff 100644 --- a/lib/data/store/setting.dart +++ b/lib/data/store/setting.dart @@ -1,6 +1,8 @@ import 'package:toolbox/core/persistant_store.dart'; import 'package:toolbox/core/utils/platform/base.dart'; import 'package:toolbox/data/model/app/menu/server_func.dart'; +import 'package:toolbox/data/model/app/server_detail_card.dart'; +import 'package:toolbox/data/model/ssh/virtual_key.dart'; import '../model/app/net_view.dart'; import '../res/default.dart'; @@ -82,8 +84,10 @@ class SettingStore extends PersistentStore { late final snippetOrder = listProperty('snippetOrder', []); // Server details page cards order - late final detailCardOrder = - listProperty('detailCardOrder', Defaults.detailCardOrder); + late final detailCardOrder = listProperty( + 'detailCardOrder', + ServerDetailCards.values.map((e) => e.name).toList(), + ); // SSH term font size late final termFontSize = property('termFontSize', 13.0); @@ -129,7 +133,7 @@ class SettingStore extends PersistentStore { late final sshVirtKeys = listProperty( 'sshVirtKeys', - Defaults.sshVirtKeys.map((e) => e.index).toList(), + VirtKey.defaultOrder.map((e) => e.index).toList(), ); late final netViewType = property( @@ -203,14 +207,7 @@ class SettingStore extends PersistentStore { late final serverFuncBtns = listProperty( 'serverBtns', - [ - ServerFuncBtn.terminal, - ServerFuncBtn.sftp, - ServerFuncBtn.container, - ServerFuncBtn.process, - ServerFuncBtn.pkg, - ServerFuncBtn.snippet, - ].map((e) => e.index).toList(), + ServerFuncBtn.defaultIdxs, ); /// Docker is more popular than podman, set to `false` to use docker @@ -257,6 +254,8 @@ class SettingStore extends PersistentStore { /// Set it to true, if you use Safe Keyboard on Chinese Android late final cnKeyboardComp = property('cnKeyboardComp', false); + late final lastVer = property('lastVer', 0); + // Never show these settings for users // // ------BEGIN------ diff --git a/lib/main.dart b/lib/main.dart index 16474fed..47fbe832 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -15,7 +15,9 @@ import 'package:toolbox/core/utils/platform/base.dart'; import 'package:toolbox/core/utils/sync/webdav.dart'; import 'package:toolbox/core/utils/ui.dart'; import 'package:toolbox/data/model/app/menu/server_func.dart'; +import 'package:toolbox/data/model/app/version_related.dart'; import 'package:toolbox/data/model/server/custom.dart'; +import 'package:toolbox/data/res/build_data.dart'; import 'package:toolbox/data/res/logger.dart'; import 'package:toolbox/data/res/provider.dart'; import 'package:toolbox/data/res/store.dart'; @@ -102,6 +104,8 @@ Future _initApp() async { if (Stores.setting.icloudSync.fetch()) ICloud.sync(); } if (Stores.setting.webdavSync.fetch()) Webdav.sync(); + + _doVersionRelated(); } void _setupProviders() { @@ -149,3 +153,13 @@ Future _initDesktopWindow() async { await windowManager.focus(); }); } + +Future _doVersionRelated() async { + final curVer = Stores.setting.lastVer.fetch(); + const newVer = BuildData.build; + if (curVer < newVer) { + /// Call [Iterable.toList] to consume the lazy iterable. + VersionRelated.funcs.map((e) => e(newVer)).toList(); + Stores.setting.lastVer.put(newVer); + } +} diff --git a/lib/view/page/server/detail.dart b/lib/view/page/server/detail.dart index 14339885..b90694c3 100644 --- a/lib/view/page/server/detail.dart +++ b/lib/view/page/server/detail.dart @@ -1,10 +1,12 @@ import 'package:flutter/material.dart'; +import 'package:icons_plus/icons_plus.dart'; import 'package:provider/provider.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'; import 'package:toolbox/core/extension/order.dart'; import 'package:toolbox/core/extension/status_cmd_type.dart'; +import 'package:toolbox/data/model/app/server_detail_card.dart'; import 'package:toolbox/data/model/server/battery.dart'; import 'package:toolbox/data/model/server/cpu.dart'; import 'package:toolbox/data/model/server/disk.dart'; @@ -22,7 +24,6 @@ import '../../../core/route.dart'; import '../../../data/model/server/server.dart'; import '../../../data/provider/server.dart'; import '../../../data/res/color.dart'; -import '../../../data/res/default.dart'; import '../../../data/res/ui.dart'; import '../../widget/appbar.dart'; import '../../widget/cardx.dart'; @@ -39,7 +40,7 @@ class ServerDetailPage extends StatefulWidget { class _ServerDetailPageState extends State with SingleTickerProviderStateMixin { late final _cardBuildMap = Map.fromIterables( - Defaults.detailCardOrder, + ServerDetailCards.names, [ _buildAbout, _buildCPUView, @@ -51,6 +52,7 @@ class _ServerDetailPageState extends State _buildSensors, _buildTemperature, _buildBatteries, + _buildPve, ], ); @@ -623,7 +625,7 @@ class _ServerDetailPageState extends State title: Text(l10n.battery), leading: const Icon(Icons.battery_charging_full, size: 17), childrenPadding: const EdgeInsets.only(bottom: 7), - initiallyExpanded: _getInitExpand(ss.batteries.length), + initiallyExpanded: _getInitExpand(ss.batteries.length, 2), children: ss.batteries.map(_buildBatteryItem).toList(), ), ); @@ -662,7 +664,7 @@ class _ServerDetailPageState extends State title: Text(l10n.sensors), leading: const Icon(Icons.thermostat, size: 17), childrenPadding: const EdgeInsets.only(bottom: 7), - initiallyExpanded: _getInitExpand(ss.sensors.length, 3), + initiallyExpanded: _getInitExpand(ss.sensors.length, 2), children: ss.sensors.map(_buildSensorItem).toList(), ), ); @@ -700,6 +702,19 @@ class _ServerDetailPageState extends State ); } + Widget _buildPve(_) { + if (widget.spi.custom?.pveAddr == null) return UIs.placeholder; + return CardX( + child: ListTile( + title: const Text('PVE'), + subtitle: Text(widget.spi.custom!.pveAddr!), + leading: const Icon(FontAwesome.server_solid, size: 17), + trailing: const Icon(Icons.chevron_right), + onTap: () => AppRoute.pve(spi: widget.spi).go(context), + ), + ); + } + Widget _buildAnimatedText(Key key, String text, TextStyle style) { return AnimatedSwitcher( duration: const Duration(milliseconds: 277), @@ -717,8 +732,8 @@ class _ServerDetailPageState extends State } bool _getInitExpand(int len, [int? max]) { - if (_collapse && len <= (max ?? 7)) return true; - return false; + if (!_collapse) return true; + return len <= (max ?? 3); } } diff --git a/lib/view/page/setting/seq/srv_detail_seq.dart b/lib/view/page/setting/seq/srv_detail_seq.dart index 6208a5d1..c29ea75d 100644 --- a/lib/view/page/setting/seq/srv_detail_seq.dart +++ b/lib/view/page/setting/seq/srv_detail_seq.dart @@ -2,7 +2,7 @@ import 'package:flutter/material.dart'; import 'package:toolbox/core/extension/context/locale.dart'; import 'package:toolbox/core/extension/context/snackbar.dart'; import 'package:toolbox/core/utils/platform/base.dart'; -import 'package:toolbox/data/res/default.dart'; +import 'package:toolbox/data/model/app/server_detail_card.dart'; import 'package:toolbox/data/res/logger.dart'; import 'package:toolbox/data/res/store.dart'; @@ -39,11 +39,12 @@ class _ServerDetailOrderPageState extends State { return List.from(vals); } catch (e) { Loggers.app.info('ServerDetailOrderPage: $e'); - return Defaults.detailCardOrder; + return ServerDetailCards.names; } }(); - final disabled = - Defaults.detailCardOrder.where((e) => !keys.contains(e)).toList(); + final disabled = ServerDetailCards.names + .where((e) => !keys.contains(e)) + .toList(); final allKeys = [...keys, ...disabled]; return ReorderableListView.builder( padding: const EdgeInsets.all(7), diff --git a/lib/view/page/setting/seq/srv_func_seq.dart b/lib/view/page/setting/seq/srv_func_seq.dart index 09ba3f2c..4d2dd319 100644 --- a/lib/view/page/setting/seq/srv_func_seq.dart +++ b/lib/view/page/setting/seq/srv_func_seq.dart @@ -1,10 +1,10 @@ import 'package:flutter/material.dart'; import 'package:toolbox/core/extension/context/locale.dart'; import 'package:toolbox/core/extension/context/snackbar.dart'; -import 'package:toolbox/core/utils/platform/base.dart'; import 'package:toolbox/data/model/app/menu/server_func.dart'; import 'package:toolbox/data/res/logger.dart'; import 'package:toolbox/data/res/store.dart'; +import 'package:toolbox/data/res/ui.dart'; import '../../../../core/extension/order.dart'; import '../../../widget/appbar.dart'; @@ -51,12 +51,20 @@ class _ServerDetailOrderPageState extends State { padding: const EdgeInsets.all(7), itemBuilder: (_, idx) { final key = allKeys[idx]; + final funcBtn = ServerFuncBtn.values[key]; return CardX( key: ValueKey(idx), child: ListTile( - title: Text(ServerFuncBtn.values[key].toStr), + title: RichText( + text: TextSpan( + children: [ + WidgetSpan(child: funcBtn.icon(2)), + const WidgetSpan(child: UIs.width7), + TextSpan(text: funcBtn.toStr, style: UIs.textGrey), + ], + ), + ), leading: _buildCheckBox(keys, key, idx, idx < keys.length), - trailing: isDesktop ? null : const Icon(Icons.drag_handle), ), ); }, diff --git a/lib/view/widget/server_func_btns.dart b/lib/view/widget/server_func_btns.dart index e5dcba8d..59f8cc43 100644 --- a/lib/view/widget/server_func_btns.dart +++ b/lib/view/widget/server_func_btns.dart @@ -41,10 +41,8 @@ class ServerFuncBtnsTopRight extends StatelessWidget { value: e, child: Row( children: [ - e.icon, - const SizedBox( - width: 10, - ), + e.icon(), + const SizedBox(width: 10), Text(e.toStr), ], ), @@ -68,9 +66,13 @@ class ServerFuncBtns extends StatelessWidget { Widget build(BuildContext context) { final btns = () { try { - return Stores.setting.serverFuncBtns - .fetch() - .map((e) => ServerFuncBtn.values[e]); + final vals = []; + final list = Stores.setting.serverFuncBtns.fetch(); + for (final idx in list) { + if (idx < 0 || idx >= ServerFuncBtn.values.length) continue; + vals.add(ServerFuncBtn.values[idx]); + } + return vals; } catch (e) { return ServerFuncBtn.values; } @@ -84,7 +86,7 @@ class ServerFuncBtns extends StatelessWidget { onPressed: () => _onTapMoreBtns(e, spi, context), padding: EdgeInsets.zero, tooltip: e.toStr, - icon: e.icon, + icon: e.icon(), ) : Padding( padding: const EdgeInsets.only(bottom: 13), @@ -94,7 +96,7 @@ class ServerFuncBtns extends StatelessWidget { IconButton( onPressed: () => _onTapMoreBtns(e, spi, context), padding: EdgeInsets.zero, - icon: e.icon, + icon: e.icon(), ), Text(e.toStr, style: UIs.text11Grey) ], @@ -161,12 +163,6 @@ void _onTapMoreBtns( check: () => _checkClient(context, spi.id), ); break; - case ServerFuncBtn.pve: - AppRoute.pve(spi: spi).checkGo( - context: context, - check: () => _checkClient(context, spi.id), - ); - break; } }