mirror of
https://github.com/lollipopkit/flutter_server_box.git
synced 2025-12-17 07:14:28 +01:00
Server detail page cards support sorting
This commit is contained in:
22
lib/core/extension/order.dart
Normal file
22
lib/core/extension/order.dart
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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 }
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|
||||||
|
|||||||
@@ -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'
|
||||||
|
];
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|
||||||
|
|||||||
@@ -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,
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -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))
|
||||||
|
|||||||
Reference in New Issue
Block a user