diff --git a/.dart_tool/flutter_gen/gen_l10n/l10n.dart b/.dart_tool/flutter_gen/gen_l10n/l10n.dart index 61a8af82..ef966d3f 100644 --- a/.dart_tool/flutter_gen/gen_l10n/l10n.dart +++ b/.dart_tool/flutter_gen/gen_l10n/l10n.dart @@ -443,6 +443,12 @@ abstract class S { /// **'Files'** String get files; + /// No description provided for @font. + /// + /// In en, this message translates to: + /// **'Font'** + String get font; + /// No description provided for @fontSize. /// /// In en, this message translates to: @@ -1007,11 +1013,11 @@ abstract class S { /// **'Are you sure to delete server [{server}]?'** String sureToDeleteServer(Object server); - /// No description provided for @termTheme. + /// No description provided for @theme. /// /// In en, this message translates to: - /// **'Terminal theme'** - String get termTheme; + /// **'Theme'** + String get theme; /// No description provided for @themeMode. /// diff --git a/.dart_tool/flutter_gen/gen_l10n/l10n_de.dart b/.dart_tool/flutter_gen/gen_l10n/l10n_de.dart index 8013f6c5..efb81504 100644 --- a/.dart_tool/flutter_gen/gen_l10n/l10n_de.dart +++ b/.dart_tool/flutter_gen/gen_l10n/l10n_de.dart @@ -192,6 +192,9 @@ class SDe extends S { @override String get files => 'Dateien'; + @override + String get font => 'Schriftarten'; + @override String get fontSize => 'Schriftgröße'; @@ -495,7 +498,7 @@ class SDe extends S { } @override - String get termTheme => 'Farbschema des Terminals'; + String get theme => 'Themen'; @override String get themeMode => 'Thememodus'; diff --git a/.dart_tool/flutter_gen/gen_l10n/l10n_en.dart b/.dart_tool/flutter_gen/gen_l10n/l10n_en.dart index de91a140..4b309dc6 100644 --- a/.dart_tool/flutter_gen/gen_l10n/l10n_en.dart +++ b/.dart_tool/flutter_gen/gen_l10n/l10n_en.dart @@ -192,6 +192,9 @@ class SEn extends S { @override String get files => 'Files'; + @override + String get font => 'Font'; + @override String get fontSize => 'Font size'; @@ -495,7 +498,7 @@ class SEn extends S { } @override - String get termTheme => 'Terminal theme'; + String get theme => 'Theme'; @override String get themeMode => 'Theme mode'; diff --git a/.dart_tool/flutter_gen/gen_l10n/l10n_zh.dart b/.dart_tool/flutter_gen/gen_l10n/l10n_zh.dart index 19fe19dc..fe8fbd30 100644 --- a/.dart_tool/flutter_gen/gen_l10n/l10n_zh.dart +++ b/.dart_tool/flutter_gen/gen_l10n/l10n_zh.dart @@ -192,6 +192,9 @@ class SZh extends S { @override String get files => '文件'; + @override + String get font => '字体'; + @override String get fontSize => '字体大小'; @@ -495,7 +498,7 @@ class SZh extends S { } @override - String get termTheme => '终端主题'; + String get theme => '主题'; @override String get themeMode => '主题模式'; diff --git a/README.md b/README.md index 7773d513..a7facf1a 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ English | [简体中文](README_zh.md)

-A Flutter project which provide charts to display server status and tools to manage server. +A Flutter project which provide charts to display Linux[](../../issues/43) server status and tools to manage server.
Especially thanks to dartssh2 & xterm.dart.

diff --git a/lib/data/model/server/cpu_status.dart b/lib/data/model/server/cpu_status.dart index 297923c6..4b6a69aa 100644 --- a/lib/data/model/server/cpu_status.dart +++ b/lib/data/model/server/cpu_status.dart @@ -1,8 +1,7 @@ class CpuStatus { List _pre; List _now; - String temp; - CpuStatus(this._pre, this._now, this.temp); + CpuStatus(this._pre, this._now); double usedPercent({int coreIdx = 0}) { if (_now.length != _pre.length) return 0; @@ -12,10 +11,9 @@ class CpuStatus { return used.isNaN ? 0 : 100 - used * 100; } - void update(List newStatus, String newTemp) { + void update(List newStatus) { _pre = _now; _now = newStatus; - temp = newTemp; } int get coresCount => _now.length; @@ -96,27 +94,3 @@ List parseCPU(String raw) { } return cpus; } - -final cpuTempReg = RegExp(r'(x86_pkg_temp|cpu_thermal)'); - -String parseCPUTemp(String type, String value) { - const noMatch = "/sys/class/thermal/thermal_zone*/type"; - // Not support to get CPU temperature - if (type.contains(noMatch) || value.isEmpty || type.isEmpty) { - return ''; - } - final split = type.split('\n'); - // if no match, use idx 0 - int idx = 0; - for (var item in split) { - if (item.contains(cpuTempReg)) { - break; - } - idx++; - } - final valueSplited = value.split('\n'); - if (idx >= valueSplited.length) return ''; - final temp = int.tryParse(valueSplited[idx].trim()); - if (temp == null) return ''; - return '${(temp / 1000).toStringAsFixed(1)}°C'; -} diff --git a/lib/data/model/server/server_status.dart b/lib/data/model/server/server_status.dart index bb399229..e8bde5e7 100644 --- a/lib/data/model/server/server_status.dart +++ b/lib/data/model/server/server_status.dart @@ -1,3 +1,5 @@ +import 'package:toolbox/data/model/server/temp.dart'; + import 'cpu_status.dart'; import 'disk_info.dart'; import 'memory.dart'; @@ -13,6 +15,7 @@ class ServerStatus { List disk; ConnStatus tcp; NetSpeed netSpeed; + Temperatures temps; String? failedInfo; ServerStatus({ @@ -24,6 +27,7 @@ class ServerStatus { required this.tcp, required this.netSpeed, required this.swap, + required this.temps, this.failedInfo, }); } diff --git a/lib/data/model/server/server_status_update_req.dart b/lib/data/model/server/server_status_update_req.dart index 755faa57..06492e24 100644 --- a/lib/data/model/server/server_status_update_req.dart +++ b/lib/data/model/server/server_status_update_req.dart @@ -24,6 +24,7 @@ extension _SegmentsExt on List { Future getStatus(ServerStatusUpdateReq req) async { final net = parseNetSpeed(req.segments.at(CmdType.net)); req.ss.netSpeed.update(net); + final sys = _parseSysVer( req.segments.at(CmdType.sys), req.segments.at(CmdType.host), @@ -32,22 +33,29 @@ Future getStatus(ServerStatusUpdateReq req) async { if (sys != null) { req.ss.sysVer = sys; } + final cpus = parseCPU(req.segments.at(CmdType.cpu)); - final cpuTemp = parseCPUTemp( + req.ss.cpu.update(cpus); + + req.ss.temps.parse( req.segments.at(CmdType.tempType), req.segments.at(CmdType.tempVal), ); - req.ss.cpu.update(cpus, cpuTemp); + final tcp = parseConn(req.segments.at(CmdType.conn)); if (tcp != null) { req.ss.tcp = tcp; } + req.ss.disk = parseDisk(req.segments.at(CmdType.disk)); + req.ss.mem = parseMem(req.segments.at(CmdType.mem)); + final uptime = _parseUpTime(req.segments.at(CmdType.uptime)); if (uptime != null) { req.ss.uptime = uptime; } + req.ss.swap = parseSwap(req.segments.at(CmdType.mem)); return req.ss; } diff --git a/lib/data/model/server/temp.dart b/lib/data/model/server/temp.dart new file mode 100644 index 00000000..9e3e1a0f --- /dev/null +++ b/lib/data/model/server/temp.dart @@ -0,0 +1,61 @@ +class Temperatures { + final Map _map = {}; + + Temperatures(); + + void parse(String type, String value) { + const noMatch = "/sys/class/thermal/thermal_zone*/type"; + // Not support to get CPU temperature + if (type.contains(noMatch) || value.isEmpty || type.isEmpty) { + return; + } + final typeSplited = type.split('\n'); + final valueSplited = value.split('\n'); + if (typeSplited.length != valueSplited.length) { + return; + } + for (var i = 0; i < typeSplited.length; i++) { + final t = typeSplited[i]; + final v = valueSplited[i]; + if (t.isEmpty || v.isEmpty) { + continue; + } + final name = t.split('/').last; + final temp = double.tryParse(v); + if (temp == null) { + continue; + } + _map[name] = temp / 1000; + } + } + + void add(String name, double value) { + _map[name] = value; + } + + double? get(String name) { + return _map[name]; + } + + Iterable get devices { + return _map.keys; + } + + bool get isEmpty { + return _map.isEmpty; + } + + double? get first { + if (_map.isEmpty) { + return null; + } + for (final key in _map.keys) { + if (cpuTempReg.hasMatch(key)) { + return _map[key]; + } + } + return _map.values.first; + } +} + +final cpuTempReg = RegExp(r'(x86_pkg_temp|cpu_thermal)'); diff --git a/lib/data/provider/debug.dart b/lib/data/provider/debug.dart index b0dd8c06..f9607c1d 100644 --- a/lib/data/provider/debug.dart +++ b/lib/data/provider/debug.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:toolbox/data/res/ui.dart'; import '../../data/res/misc.dart'; @@ -51,7 +52,7 @@ class DebugProvider extends ChangeNotifier { void _addWidget(Widget widget) { widgets.add(widget); - widgets.add(const SizedBox(height: 13)); + widgets.add(height13); if (widgets.length > maxDebugLogLines) { widgets.removeRange(0, widgets.length - maxDebugLogLines); } diff --git a/lib/data/res/status.dart b/lib/data/res/status.dart index 2fffcf39..a017a695 100644 --- a/lib/data/res/status.dart +++ b/lib/data/res/status.dart @@ -1,3 +1,5 @@ +import 'package:toolbox/data/model/server/temp.dart'; + import '../model/server/cpu_status.dart'; import '../model/server/disk_info.dart'; import '../model/server/memory.dart'; @@ -24,7 +26,6 @@ OneTimeCpuStatus get _initOneTimeCpuStatus => OneTimeCpuStatus( CpuStatus get initCpuStatus => CpuStatus( [_initOneTimeCpuStatus], [_initOneTimeCpuStatus], - '', ); NetSpeedPart get _initNetSpeedPart => NetSpeedPart( '', @@ -50,4 +51,5 @@ ServerStatus get initStatus => ServerStatus( tcp: ConnStatus(0, 0, 0, 0), netSpeed: initNetSpeed, swap: _initSwap, + temps: Temperatures(), ); diff --git a/lib/data/res/ui.dart b/lib/data/res/ui.dart index 3ac0b4e6..f6d21c6b 100644 --- a/lib/data/res/ui.dart +++ b/lib/data/res/ui.dart @@ -22,6 +22,7 @@ const roundRectCardPadding = EdgeInsets.symmetric(horizontal: 17, vertical: 13); /// SizedBox +const placeholder = SizedBox.shrink(); const height13 = SizedBox(height: 13); const width13 = SizedBox(width: 13); const width7 = SizedBox(width: 7); diff --git a/lib/l10n/app_de.arb b/lib/l10n/app_de.arb index 5b41cbbd..20745a3c 100644 --- a/lib/l10n/app_de.arb +++ b/lib/l10n/app_de.arb @@ -57,6 +57,7 @@ "fileNotExist": "{file} existiert nicht", "fileTooLarge": "Datei '{file}' ist zu groß {size}, max {sizeMax}", "files": "Dateien", + "font": "Schriftarten", "fontSize": "Schriftgröße", "foundNUpdate": "Update {count} gefunden", "getPushTokenFailed": "Push-Token kann nicht abgerufen werden", @@ -151,7 +152,7 @@ "sureDirEmpty": "Stelle sicher, dass der Ordner leer ist.", "sureNoPwd": "Bist du sicher, dass du kein Passwort verwenden willst?", "sureToDeleteServer": "Bist du sicher, dass du [{server}] löschen willst?", - "termTheme": "Farbschema des Terminals", + "theme": "Themen", "themeMode": "Thememodus", "times": "x", "ttl": "ttl", diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index d7a83682..555259bd 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -57,6 +57,7 @@ "fileNotExist": "{file} not exist", "fileTooLarge": "File '{file}' too large {size}, max {sizeMax}", "files": "Files", + "font": "Font", "fontSize": "Font size", "foundNUpdate": "Found {count} update", "getPushTokenFailed": "Can't fetch push token", @@ -151,7 +152,7 @@ "sureDirEmpty": "Make sure dir is empty.", "sureNoPwd": "Are you sure to use no password?", "sureToDeleteServer": "Are you sure to delete server [{server}]?", - "termTheme": "Terminal theme", + "theme": "Theme", "themeMode": "Theme mode", "times": "Times", "ttl": "ttl", diff --git a/lib/l10n/app_zh.arb b/lib/l10n/app_zh.arb index 41670f3c..6a26070d 100644 --- a/lib/l10n/app_zh.arb +++ b/lib/l10n/app_zh.arb @@ -57,6 +57,7 @@ "fileNotExist": "{file} 不存在", "fileTooLarge": "文件 '{file}' 过大 '{size}',超过了 {sizeMax}", "files": "文件", + "font": "字体", "fontSize": "字体大小", "foundNUpdate": "找到 {count} 个更新", "getPushTokenFailed": "未能获取到推送token", @@ -151,7 +152,7 @@ "sureDirEmpty": "请确保文件夹为空", "sureNoPwd": "确认使用无密码?", "sureToDeleteServer": "你确定要删除服务器 [{server}] 吗?", - "termTheme": "终端主题", + "theme": "主题", "themeMode": "主题模式", "times": "次", "ttl": "缓存时间", diff --git a/lib/view/page/backup.dart b/lib/view/page/backup.dart index 570a620f..f6e6c17e 100644 --- a/lib/view/page/backup.dart +++ b/lib/view/page/backup.dart @@ -55,9 +55,7 @@ class BackupPage extends StatelessWidget { textAlign: TextAlign.center, ), ), - const SizedBox( - height: 107, - ), + const SizedBox(height: 107), _buildCard(s.restore, Icons.download, media, () async { final path = await pickOneFile(); if (path == null) { @@ -72,12 +70,12 @@ class BackupPage extends StatelessWidget { final text = await file.readAsString(); _import(text, context, s); }), - const SizedBox(height: 17), + height13, const SizedBox( width: 37, child: Divider(), ), - const SizedBox(height: 17), + height13, _buildCard( s.backup, Icons.file_upload, @@ -122,7 +120,7 @@ class BackupPage extends StatelessWidget { icon, color: textColor, ), - const SizedBox(width: 7), + width7, Text(text, style: TextStyle(color: textColor)), ], ), diff --git a/lib/view/page/convert.dart b/lib/view/page/convert.dart index 3767352e..bfd5d4e7 100644 --- a/lib/view/page/convert.dart +++ b/lib/view/page/convert.dart @@ -3,6 +3,7 @@ import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart'; +import 'package:toolbox/data/res/ui.dart'; import '../../core/utils/ui.dart'; import '../widget/input_field.dart'; @@ -48,7 +49,7 @@ class _ConvertPageState extends State controller: ScrollController(), child: Column( children: [ - const SizedBox(height: 13), + height13, _buildInputTop(), _buildMiddleBtns(), _buildResult(), diff --git a/lib/view/page/docker.dart b/lib/view/page/docker.dart index 142d9ea6..5eae5dd3 100644 --- a/lib/view/page/docker.dart +++ b/lib/view/page/docker.dart @@ -277,7 +277,7 @@ class _DockerManagePageState extends State { Widget _buildImages() { if (_docker.images == null) { - return const SizedBox(); + return placeholder; } final items = _docker.images! .map( @@ -336,7 +336,7 @@ class _DockerManagePageState extends State { } Widget _buildLoading() { - if (!_docker.isBusy) return const SizedBox(); + if (!_docker.isBusy) return placeholder; return Padding( padding: const EdgeInsets.all(17), child: Column( @@ -344,7 +344,7 @@ class _DockerManagePageState extends State { const Center( child: CircularProgressIndicator(), ), - const SizedBox(height: 17), + height13, Text(_docker.runLog ?? '...'), ], ), @@ -353,7 +353,7 @@ class _DockerManagePageState extends State { Widget _buildEditHost() { if (_docker.items!.isNotEmpty || _docker.images!.isNotEmpty) { - return const SizedBox(); + return placeholder; } return Padding( padding: const EdgeInsets.fromLTRB(17, 17, 17, 0), diff --git a/lib/view/page/ping.dart b/lib/view/page/ping.dart index 7102d099..734f56ee 100644 --- a/lib/view/page/ping.dart +++ b/lib/view/page/ping.dart @@ -53,7 +53,7 @@ class _PingPageState extends State padding: const EdgeInsets.symmetric(horizontal: 7), child: Column( children: [ - const SizedBox(height: 13), + height13, Input( controller: _textEditingController, hint: s.inputDomainHere, diff --git a/lib/view/page/pkg.dart b/lib/view/page/pkg.dart index a281171b..c63d5b07 100644 --- a/lib/view/page/pkg.dart +++ b/lib/view/page/pkg.dart @@ -133,7 +133,7 @@ class _PkgManagePageState extends State Widget _buildFAB(PkgProvider pkg) { if (pkg.isBusy || (pkg.upgradeable?.isEmpty ?? true)) { - return const SizedBox(); + return placeholder; } return FloatingActionButton( onPressed: () { diff --git a/lib/view/page/private_key/edit.dart b/lib/view/page/private_key/edit.dart index d3a46aae..cfefbb65 100644 --- a/lib/view/page/private_key/edit.dart +++ b/lib/view/page/private_key/edit.dart @@ -42,13 +42,13 @@ class _PrivateKeyEditPageState extends State late PrivateKeyProvider _provider; late S _s; - Widget _loading = const SizedBox(); + Widget _loading = placeholder; @override void initState() { super.initState(); _provider = locator(); - _loading = const SizedBox(); + _loading = placeholder; } @override @@ -79,7 +79,7 @@ class _PrivateKeyEditPageState extends State context.pop(); }, icon: const Icon(Icons.delete)) - : const SizedBox() + : placeholder ], ); } @@ -112,7 +112,7 @@ class _PrivateKeyEditPageState extends State rethrow; } finally { setState(() { - _loading = const SizedBox(); + _loading = placeholder; }); } if (widget.info != null) { diff --git a/lib/view/page/server/detail.dart b/lib/view/page/server/detail.dart index c719469e..8611abc9 100644 --- a/lib/view/page/server/detail.dart +++ b/lib/view/page/server/detail.dart @@ -60,47 +60,38 @@ class _ServerDetailPageState extends State body: ListView( padding: const EdgeInsets.all(13), children: [ - ...(_buildLinuxIcon(si.status.sysVer) ?? []), + _buildLinuxIcon(si.status.sysVer), _buildUpTimeAndSys(si.status), _buildCPUView(si.status), _buildMemView(si.status), _buildSwapView(si.status), _buildDiskView(si.status), _buildNetView(si.status.netSpeed), - // avoid the hieght of navigation bar + _buildTemperature(si.status), + // height of navigation bar SizedBox(height: _media.padding.bottom), ], ), ); } - List? _buildLinuxIcon(String sysVer) { - if (!_showDistLogo) return null; + Widget _buildLinuxIcon(String sysVer) { + if (!_showDistLogo) return placeholder; final iconPath = sysVer.dist?.iconPath; - if (iconPath == null) return null; - return [ - SizedBox(height: _media.size.height * 0.03), - ConstrainedBox( - constraints: BoxConstraints( - maxHeight: _media.size.height * 0.13, - maxWidth: _media.size.width * 0.6, - ), - child: Image.asset( - iconPath, - fit: BoxFit.contain, - ), + if (iconPath == null) return placeholder; + return ConstrainedBox( + constraints: BoxConstraints( + maxHeight: _media.size.height * 0.13, + maxWidth: _media.size.width * 0.6, ), - SizedBox(height: _media.size.height * 0.03), - ]; + child: Image.asset( + iconPath, + fit: BoxFit.contain, + ), + ); } Widget _buildCPUView(ServerStatus ss) { - final tempWidget = ss.cpu.temp.isEmpty - ? const SizedBox() - : Text( - ss.cpu.temp, - style: textSize13Grey, - ); return RoundRectCard( Padding( padding: roundRectCardPadding, @@ -108,16 +99,10 @@ class _ServerDetailPageState extends State Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Row( - children: [ - Text( - '${ss.cpu.usedPercent(coreIdx: 0).toInt()}%', - style: textSize27, - textScaleFactor: 1.0, - ), - width7, - tempWidget - ], + Text( + '${ss.cpu.usedPercent(coreIdx: 0).toInt()}%', + style: textSize27, + textScaleFactor: 1.0, ), Row( children: [ @@ -244,7 +229,7 @@ class _ServerDetailPageState extends State } Widget _buildSwapView(ServerStatus ss) { - if (ss.swap.total == 0) return const SizedBox(); + if (ss.swap.total == 0) return placeholder; final used = ss.swap.usedPercent * 100; final cached = ss.swap.cached / ss.swap.total * 100; return RoundRectCard( @@ -396,5 +381,40 @@ class _ServerDetailPageState extends State ); } + Widget _buildTemperature(ServerStatus ss) { + if (ss.temps.isEmpty) { + return placeholder; + } + final List children = [ + const Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Icon(Icons.device_hub, size: 17), + Icon(Icons.arrow_downward, size: 17), + ], + ), + const Padding(padding: EdgeInsets.symmetric(vertical: 3), child: Divider(height: 7),), + ]; + children.addAll(ss.temps.devices.map((key) => Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + key, + style: textSize11, + textScaleFactor: 1.0, + ), + Text( + '${ss.temps.get(key)}°C', + style: textSize11, + textScaleFactor: 1.0, + ), + ], + ))); + return RoundRectCard(Padding( + padding: roundRectCardPadding, + child: Column(children: children), + )); + } + static const _ignorePath = ['udev', 'tmpfs', 'devtmpfs']; } diff --git a/lib/view/page/server/edit.dart b/lib/view/page/server/edit.dart index 4020e978..37110d28 100644 --- a/lib/view/page/server/edit.dart +++ b/lib/view/page/server/edit.dart @@ -96,7 +96,7 @@ class _ServerEditPageState extends State with AfterLayoutMixin { return AppBar( title: Text(_s.edit, style: textSize18), actions: [ - widget.spi != null ? delBtn : const SizedBox(), + widget.spi != null ? delBtn : placeholder, ], ); } @@ -165,8 +165,8 @@ class _ServerEditPageState extends State with AfterLayoutMixin { hint: _s.pwd, onSubmitted: (_) => {}, ) - : const SizedBox(), - usePublicKey ? _buildKeyAuth() : const SizedBox() + : placeholder, + usePublicKey ? _buildKeyAuth() : placeholder ], ), ); diff --git a/lib/view/page/server/tab.dart b/lib/view/page/server/tab.dart index 4407b879..1f8b2f01 100644 --- a/lib/view/page/server/tab.dart +++ b/lib/view/page/server/tab.dart @@ -108,20 +108,20 @@ class _ServerPageState extends State Widget _buildEachServerCard(Server? si) { if (si == null) { - return const SizedBox(); + return placeholder; } - return RoundRectCard( - GestureDetector( - child: Padding( + return GestureDetector( + key: Key(si.spi.id), + onTap: () => AppRoute( + ServerDetailPage(si.spi.id), + 'server detail page', + ).go(context), + child: RoundRectCard( + Padding( padding: const EdgeInsets.all(13), child: _buildRealServerCard(si.status, si.state, si.spi), ), - onTap: () => AppRoute( - ServerDetailPage(si.spi.id), - 'server detail page', - ).go(context), ), - key: Key(si.spi.id), ); } @@ -136,9 +136,7 @@ class _ServerPageState extends State crossAxisAlignment: CrossAxisAlignment.start, children: [ _buildServerCardTitle(ss, cs, spi), - const SizedBox( - height: 17, - ), + height13, Row( mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ @@ -149,7 +147,7 @@ class _ServerPageState extends State 'Total:\n${rootDisk.size}', 'Used:\n${rootDisk.usedPercent}%') ], ), - const SizedBox(height: 13), + height13, Row( mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ @@ -169,9 +167,6 @@ class _ServerPageState extends State ServerState cs, ServerPrivateInfo spi, ) { - final topRightStr = - getTopRightStr(cs, ss.cpu.temp, ss.uptime, ss.failedInfo); - final hasError = cs == ServerState.failed && ss.failedInfo != null; return Padding( padding: const EdgeInsets.symmetric(horizontal: 7), child: Row( @@ -194,32 +189,8 @@ class _ServerPageState extends State ), Row( children: [ - hasError - ? GestureDetector( - onTap: () => showRoundDialog( - context: context, - title: Text(_s.error), - child: Text(ss.failedInfo ?? _s.unknownError), - actions: [ - TextButton( - onPressed: () => copy2Clipboard( - ss.failedInfo ?? _s.unknownError), - child: Text(_s.copy), - ) - ], - ), - child: Text( - _s.viewErr, - style: textSize12Grey, - textScaleFactor: 1.0, - ), - ) - : Text( - topRightStr, - style: textSize12Grey, - textScaleFactor: 1.0, - ), - const SizedBox(width: 9), + _buildTopRightText(ss, cs), + width7, _buildSSHBtn(spi), _buildMoreBtn(spi), ], @@ -229,6 +200,41 @@ class _ServerPageState extends State ); } + Widget _buildTopRightText(ServerStatus ss, ServerState cs) { + final topRightStr = getTopRightStr( + cs, + ss.temps.first, + ss.uptime, + ss.failedInfo, + ); + final hasError = cs == ServerState.failed && ss.failedInfo != null; + return hasError + ? GestureDetector( + onTap: () => showRoundDialog( + context: context, + title: Text(_s.error), + child: Text(ss.failedInfo ?? _s.unknownError), + actions: [ + TextButton( + onPressed: () => + copy2Clipboard(ss.failedInfo ?? _s.unknownError), + child: Text(_s.copy), + ) + ], + ), + child: Text( + _s.viewErr, + style: textSize12Grey, + textScaleFactor: 1.0, + ), + ) + : Text( + topRightStr, + style: textSize12Grey, + textScaleFactor: 1.0, + ); + } + Widget _buildSSHBtn(ServerPrivateInfo spi) { return GestureDetector( child: const Icon( @@ -311,24 +317,18 @@ class _ServerPageState extends State } String getTopRightStr( - ServerState cs, String temp, String upTime, String? failedInfo) { + ServerState cs, + double? temp, + String upTime, + String? failedInfo, + ) { switch (cs) { case ServerState.disconnected: return _s.disconnected; case ServerState.connected: - if (temp == '') { - if (upTime == '') { - return _s.serverTabLoading; - } else { - return upTime; - } - } else { - if (upTime == '') { - return temp; - } else { - return '$temp | $upTime'; - } - } + final tempStr = temp == null ? '' : '${temp.toStringAsFixed(1)}°C'; + final items = [tempStr, upTime]; + return items.where((element) => element.isNotEmpty).join(' | '); case ServerState.connecting: return _s.serverTabConnecting; case ServerState.failed: diff --git a/lib/view/page/setting.dart b/lib/view/page/setting.dart index 346c123d..5d903bbe 100644 --- a/lib/view/page/setting.dart +++ b/lib/view/page/setting.dart @@ -317,7 +317,7 @@ class _SettingPageState extends State { .toList(); return ListTile( title: Text( - _s.termTheme, + _s.theme, ), onTap: () { termThemeKey.currentState?.showButtonMenu(); @@ -465,7 +465,7 @@ class _SettingPageState extends State { Widget _buildFont() { final fontName = getFileName(_setting.fontPath.fetch()); return ListTile( - title: Text(_s.choose), + title: Text(_s.font), trailing: Text( fontName ?? _s.notSelected, style: textSize15, diff --git a/lib/view/page/sftp/view.dart b/lib/view/page/sftp/view.dart index 99ecac62..d2bf29b1 100644 --- a/lib/view/page/sftp/view.dart +++ b/lib/view/page/sftp/view.dart @@ -266,7 +266,7 @@ class _SFTPPageState extends State { title: Text(_s.download), onTap: () => download(context, file), ) - : const SizedBox() + : placeholder ], ), actions: [ diff --git a/lib/view/page/snippet/edit.dart b/lib/view/page/snippet/edit.dart index bd93bb85..56059e31 100644 --- a/lib/view/page/snippet/edit.dart +++ b/lib/view/page/snippet/edit.dart @@ -54,7 +54,7 @@ class _SnippetEditPageState extends State }, tooltip: _s.delete, icon: const Icon(Icons.delete)) - : const SizedBox() + : placeholder ], ), body: _buildBody(),