Server detail page cards support sorting

This commit is contained in:
lollipopkit
2023-05-26 18:03:43 +08:00
parent 20ef9d4575
commit 00fdcb1ee8
7 changed files with 128 additions and 71 deletions

View File

@@ -0,0 +1,22 @@
import 'package:toolbox/core/persistant_store.dart';
typedef StringOrder = List<String>;
extension StringOrderX on StringOrder {
void move(int oldIndex, int newIndex, StoreProperty property) {
if (oldIndex == newIndex) return;
if (oldIndex < newIndex) {
newIndex -= 1;
}
final item = this[oldIndex];
removeAt(oldIndex);
insert(newIndex, item);
property.put(this);
}
void update(String id, String newId) {
final index = indexOf(id);
if (index == -1) return;
this[index] = newId;
}
}

View File

@@ -8,8 +8,13 @@ class VirtualKey {
final IconData? icon; final IconData? icon;
final VirtualKeyFunc? func; final VirtualKeyFunc? func;
VirtualKey(this.text, VirtualKey(
{this.key, this.toggleable = false, this.icon, this.func,}); this.text, {
this.key,
this.toggleable = false,
this.icon,
this.func,
});
} }
enum VirtualKeyFunc { toggleIME, backspace, copy, paste, snippet } enum VirtualKeyFunc { toggleIME, backspace, copy, paste, snippet }

View File

@@ -3,6 +3,7 @@ import 'dart:async';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:logging/logging.dart'; import 'package:logging/logging.dart';
import '../../core/extension/order.dart';
import '../../core/extension/uint8list.dart'; import '../../core/extension/uint8list.dart';
import '../../core/provider_base.dart'; import '../../core/provider_base.dart';
import '../../core/utils/server.dart'; import '../../core/utils/server.dart';
@@ -18,31 +19,11 @@ import '../store/server.dart';
import '../store/setting.dart'; import '../store/setting.dart';
typedef ServersMap = Map<String, Server>; typedef ServersMap = Map<String, Server>;
typedef ServerOrder = List<String>;
extension ServerOrderX on ServerOrder {
void move(int oldIndex, int newIndex) {
if (oldIndex == newIndex) return;
if (oldIndex < newIndex) {
newIndex -= 1;
}
final item = this[oldIndex];
removeAt(oldIndex);
insert(newIndex, item);
locator<SettingStore>().serverOrder.put(this);
}
void update(String id, String newId) {
final index = indexOf(id);
if (index == -1) return;
this[index] = newId;
}
}
class ServerProvider extends BusyProvider { class ServerProvider extends BusyProvider {
final ServersMap _servers = {}; final ServersMap _servers = {};
ServersMap get servers => _servers; ServersMap get servers => _servers;
final ServerOrder serverOrder = []; final StringOrder serverOrder = [];
final _limiter = TryLimiter(); final _limiter = TryLimiter();

View File

@@ -12,3 +12,14 @@ const maxDebugLogLines = 100;
/// Method Channels /// Method Channels
const pkgName = 'tech.lolli.toolbox'; const pkgName = 'tech.lolli.toolbox';
const bgRunChannel = MethodChannel('$pkgName/app_retain'); const bgRunChannel = MethodChannel('$pkgName/app_retain');
// default server details page cards order
const defaultDetailCardOrder = [
'uptime',
'cpu',
'mem',
'swap',
'disk',
'net',
'temp'
];

View File

@@ -1,6 +1,7 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:toolbox/core/persistant_store.dart'; import 'package:toolbox/core/persistant_store.dart';
import 'package:toolbox/core/utils/platform.dart'; import 'package:toolbox/core/utils/platform.dart';
import 'package:toolbox/data/res/misc.dart';
class SettingStore extends PersistentStore { class SettingStore extends PersistentStore {
StoreProperty<int> get primaryColor => property( StoreProperty<int> get primaryColor => property(
@@ -42,11 +43,15 @@ class SettingStore extends PersistentStore {
/// Backgroud running (Android) /// Backgroud running (Android)
StoreProperty<bool> get bgRun => property('bgRun', defaultValue: isAndroid); StoreProperty<bool> get bgRun => property('bgRun', defaultValue: isAndroid);
// Server order /// Server order
StoreProperty<List<String>> get serverOrder => StoreProperty<List<String>> get serverOrder =>
property('serverOrder', defaultValue: null); property('serverOrder', defaultValue: null);
// SSH term font size /// Server details page cards order
StoreProperty<List<String>> get detailCardOrder =>
property('detailCardPrder', defaultValue: defaultDetailCardOrder);
/// SSH term font size
StoreProperty<double> get termFontSize => StoreProperty<double> get termFontSize =>
property('termFontSize', defaultValue: 13); property('termFontSize', defaultValue: 13);

View File

@@ -1,9 +1,15 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:toolbox/core/extension/order.dart';
import 'package:toolbox/data/model/server/cpu_status.dart';
import 'package:toolbox/data/model/server/disk_info.dart';
import 'package:toolbox/data/model/server/dist.dart';
import 'package:toolbox/data/model/server/memory.dart';
import 'package:toolbox/data/model/server/temp.dart';
import 'package:toolbox/data/res/misc.dart';
import '../../../core/extension/numx.dart'; import '../../../core/extension/numx.dart';
import '../../../data/model/server/dist.dart';
import '../../../data/model/server/net_speed.dart'; import '../../../data/model/server/net_speed.dart';
import '../../../data/model/server/server.dart'; import '../../../data/model/server/server.dart';
import '../../../data/model/server/server_status.dart'; import '../../../data/model/server/server_status.dart';
@@ -27,6 +33,7 @@ class _ServerDetailPageState extends State<ServerDetailPage>
with SingleTickerProviderStateMixin { with SingleTickerProviderStateMixin {
late MediaQueryData _media; late MediaQueryData _media;
late S _s; late S _s;
late List<String> _cardsOrder;
final _setting = locator<SettingStore>(); final _setting = locator<SettingStore>();
@override @override
@@ -36,6 +43,12 @@ class _ServerDetailPageState extends State<ServerDetailPage>
_s = S.of(context)!; _s = S.of(context)!;
} }
@override
void initState() {
super.initState();
_cardsOrder = _setting.detailCardOrder.fetch()!;
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Consumer<ServerProvider>(builder: (_, provider, __) { return Consumer<ServerProvider>(builder: (_, provider, __) {
@@ -56,24 +69,39 @@ class _ServerDetailPageState extends State<ServerDetailPage>
appBar: AppBar( appBar: AppBar(
title: Text(si.spi.name, style: textSize18), title: Text(si.spi.name, style: textSize18),
), ),
body: ListView( body: ReorderableListView(
padding: const EdgeInsets.all(13), padding: EdgeInsets.only(
children: [ left: 13, right: 13, top: 13, bottom: _media.padding.bottom),
_buildLinuxIcon(si.status.sysVer), onReorder: (int oldIndex, int newIndex) {
_buildUpTimeAndSys(si.status), setState(() {
_buildCPUView(si.status), _cardsOrder.move(oldIndex, newIndex, _setting.detailCardOrder);
_buildMemView(si.status), });
_buildSwapView(si.status), },
_buildDiskView(si.status), header: _buildLinuxIcon(si.status.sysVer),
_buildNetView(si.status.netSpeed), footer: height13,
_buildTemperature(si.status), children: _buildMainList(si.status),
// height of navigation bar
SizedBox(height: _media.padding.bottom),
],
), ),
); );
} }
List<Widget> _buildMainList(ServerStatus ss) {
final map = Map.fromIterables(
defaultDetailCardOrder,
[
_buildUpTimeAndSys(ss.sysVer, ss.uptime),
_buildCPUView(ss.cpu),
_buildMemView(ss.mem),
_buildSwapView(ss.swap),
_buildDiskView(ss.disk),
_buildNetView(ss.netSpeed),
_buildTemperature(ss.temps),
],
);
return _cardsOrder
.map((e) => SizedBox(key: ValueKey(e), child: map[e]))
.toList();
}
Widget _buildLinuxIcon(String sysVer) { Widget _buildLinuxIcon(String sysVer) {
if (!_setting.showDistLogo.fetch()!) return placeholder; if (!_setting.showDistLogo.fetch()!) return placeholder;
final iconPath = sysVer.dist?.iconPath; final iconPath = sysVer.dist?.iconPath;
@@ -90,7 +118,7 @@ class _ServerDetailPageState extends State<ServerDetailPage>
); );
} }
Widget _buildCPUView(ServerStatus ss) { Widget _buildCPUView(CpuStatus cs) {
return RoundRectCard( return RoundRectCard(
Padding( Padding(
padding: roundRectCardPadding, padding: roundRectCardPadding,
@@ -99,25 +127,25 @@ class _ServerDetailPageState extends State<ServerDetailPage>
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
Text( Text(
'${ss.cpu.usedPercent(coreIdx: 0).toInt()}%', '${cs.usedPercent(coreIdx: 0).toInt()}%',
style: textSize27, style: textSize27,
textScaleFactor: 1.0, textScaleFactor: 1.0,
), ),
Row( Row(
children: [ children: [
_buildDetailPercent(ss.cpu.user, 'user'), _buildDetailPercent(cs.user, 'user'),
width13, width13,
_buildDetailPercent(ss.cpu.sys, 'sys'), _buildDetailPercent(cs.sys, 'sys'),
width13, width13,
_buildDetailPercent(ss.cpu.iowait, 'io'), _buildDetailPercent(cs.iowait, 'io'),
width13, width13,
_buildDetailPercent(ss.cpu.idle, 'idle') _buildDetailPercent(cs.idle, 'idle')
], ],
) )
], ],
), ),
height13, height13,
_buildCPUProgress(ss) _buildCPUProgress(cs)
]), ]),
), ),
); );
@@ -143,14 +171,14 @@ class _ServerDetailPageState extends State<ServerDetailPage>
); );
} }
Widget _buildCPUProgress(ServerStatus ss) { Widget _buildCPUProgress(CpuStatus cs) {
final children = <Widget>[]; final children = <Widget>[];
for (var i = 0; i < ss.cpu.coresCount; i++) { for (var i = 0; i < cs.coresCount; i++) {
if (i == 0) continue; if (i == 0) continue;
children.add( children.add(
Padding( Padding(
padding: const EdgeInsets.all(2), padding: const EdgeInsets.all(2),
child: _buildProgress(ss.cpu.usedPercent(coreIdx: i)), child: _buildProgress(cs.usedPercent(coreIdx: i)),
), ),
); );
} }
@@ -168,16 +196,16 @@ class _ServerDetailPageState extends State<ServerDetailPage>
); );
} }
Widget _buildUpTimeAndSys(ServerStatus ss) { Widget _buildUpTimeAndSys(String sysVer, String uptime) {
return RoundRectCard( return RoundRectCard(
Padding( Padding(
padding: roundRectCardPadding, padding: roundRectCardPadding,
child: Row( child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
Text(ss.sysVer, style: textSize11, textScaleFactor: 1.0), Text(sysVer, style: textSize11, textScaleFactor: 1.0),
Text( Text(
ss.uptime, uptime,
style: textSize11, style: textSize11,
textScaleFactor: 1.0, textScaleFactor: 1.0,
), ),
@@ -187,10 +215,10 @@ class _ServerDetailPageState extends State<ServerDetailPage>
); );
} }
Widget _buildMemView(ServerStatus ss) { Widget _buildMemView(Memory mem) {
final free = ss.mem.free / ss.mem.total * 100; final free = mem.free / mem.total * 100;
final avail = ss.mem.availPercent * 100; final avail = mem.availPercent * 100;
final used = ss.mem.usedPercent * 100; final used = mem.usedPercent * 100;
return RoundRectCard( return RoundRectCard(
Padding( Padding(
@@ -206,7 +234,7 @@ class _ServerDetailPageState extends State<ServerDetailPage>
children: [ children: [
Text('${used.toStringAsFixed(0)}%', style: textSize27), Text('${used.toStringAsFixed(0)}%', style: textSize27),
width7, width7,
Text('of ${(ss.mem.total * 1024).convertBytes}', Text('of ${(mem.total * 1024).convertBytes}',
style: textSize13Grey) style: textSize13Grey)
], ],
), ),
@@ -227,10 +255,10 @@ class _ServerDetailPageState extends State<ServerDetailPage>
); );
} }
Widget _buildSwapView(ServerStatus ss) { Widget _buildSwapView(Swap swap) {
if (ss.swap.total == 0) return placeholder; if (swap.total == 0) return placeholder;
final used = ss.swap.usedPercent * 100; final used = swap.usedPercent * 100;
final cached = ss.swap.cached / ss.swap.total * 100; final cached = swap.cached / swap.total * 100;
return RoundRectCard( return RoundRectCard(
Padding( Padding(
padding: roundRectCardPadding, padding: roundRectCardPadding,
@@ -245,7 +273,7 @@ class _ServerDetailPageState extends State<ServerDetailPage>
children: [ children: [
Text('${used.toStringAsFixed(0)}%', style: textSize27), Text('${used.toStringAsFixed(0)}%', style: textSize27),
width7, width7,
Text('of ${(ss.swap.total * 1024).convertBytes} ', Text('of ${(swap.total * 1024).convertBytes} ',
style: textSize13Grey) style: textSize13Grey)
], ],
), ),
@@ -260,14 +288,14 @@ class _ServerDetailPageState extends State<ServerDetailPage>
); );
} }
Widget _buildDiskView(ServerStatus ss) { Widget _buildDiskView(List<DiskInfo> disk) {
ss.disk.removeWhere((e) { disk.removeWhere((e) {
for (final ingorePath in _setting.diskIgnorePath.fetch()!) { for (final ingorePath in _setting.diskIgnorePath.fetch()!) {
if (e.path.startsWith(ingorePath)) return true; if (e.path.startsWith(ingorePath)) return true;
} }
return false; return false;
}); });
final children = ss.disk final children = disk
.map((disk) => Padding( .map((disk) => Padding(
padding: const EdgeInsets.symmetric(vertical: 3), padding: const EdgeInsets.symmetric(vertical: 3),
child: Column( child: Column(
@@ -380,8 +408,8 @@ class _ServerDetailPageState extends State<ServerDetailPage>
); );
} }
Widget _buildTemperature(ServerStatus ss) { Widget _buildTemperature(Temperatures temps) {
if (ss.temps.isEmpty) { if (temps.isEmpty) {
return placeholder; return placeholder;
} }
final List<Widget> children = [ final List<Widget> children = [
@@ -397,7 +425,7 @@ class _ServerDetailPageState extends State<ServerDetailPage>
child: Divider(height: 7), child: Divider(height: 7),
), ),
]; ];
children.addAll(ss.temps.devices.map((key) => Row( children.addAll(temps.devices.map((key) => Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
Text( Text(
@@ -406,7 +434,7 @@ class _ServerDetailPageState extends State<ServerDetailPage>
textScaleFactor: 1.0, textScaleFactor: 1.0,
), ),
Text( Text(
'${ss.temps.get(key)}°C', '${temps.get(key)}°C',
style: textSize11, style: textSize11,
textScaleFactor: 1.0, textScaleFactor: 1.0,
), ),

View File

@@ -5,6 +5,7 @@ import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:get_it/get_it.dart'; import 'package:get_it/get_it.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:toolbox/core/extension/navigator.dart'; import 'package:toolbox/core/extension/navigator.dart';
import 'package:toolbox/core/extension/order.dart';
import 'package:toolbox/core/utils/misc.dart'; import 'package:toolbox/core/utils/misc.dart';
import '../../../core/route.dart'; import '../../../core/route.dart';
@@ -94,7 +95,11 @@ class _ServerPageState extends State<ServerPage>
padding: const EdgeInsets.fromLTRB(7, 10, 7, 7), padding: const EdgeInsets.fromLTRB(7, 10, 7, 7),
physics: const AlwaysScrollableScrollPhysics(), physics: const AlwaysScrollableScrollPhysics(),
onReorder: (oldIndex, newIndex) => setState(() { onReorder: (oldIndex, newIndex) => setState(() {
pro.serverOrder.move(oldIndex, newIndex); pro.serverOrder.move(
oldIndex,
newIndex,
_settingStore.serverOrder,
);
}), }),
children: pro.serverOrder children: pro.serverOrder
.where((e) => pro.servers.containsKey(e)) .where((e) => pro.servers.containsKey(e))