opt.: servers list -> map

This commit is contained in:
lollipopkit
2023-04-23 14:08:46 +08:00
parent bd608ce06f
commit 71243ea1fe
10 changed files with 85 additions and 80 deletions

View File

@@ -2,8 +2,8 @@ import 'dart:async';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:logging/logging.dart'; import 'package:logging/logging.dart';
import 'package:toolbox/core/build_mode.dart';
import '../../core/build_mode.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,9 +18,11 @@ import '../res/status.dart';
import '../store/server.dart'; import '../store/server.dart';
import '../store/setting.dart'; import '../store/setting.dart';
typedef ServersMap = Map<String, Server>;
class ServerProvider extends BusyProvider { class ServerProvider extends BusyProvider {
List<Server> _servers = []; final ServersMap _servers = {};
List<Server> get servers => _servers; ServersMap get servers => _servers;
final _limiter = TryLimiter(); final _limiter = TryLimiter();
Timer? _timer; Timer? _timer;
@@ -30,7 +32,9 @@ class ServerProvider extends BusyProvider {
Future<void> loadLocalData() async { Future<void> loadLocalData() async {
setBusyState(true); setBusyState(true);
final infos = locator<ServerStore>().fetch(); final infos = locator<ServerStore>().fetch();
_servers = List.generate(infos.length, (index) => genServer(infos[index])); for (final info in infos) {
_servers[info.id] = genServer(info);
}
setBusyState(false); setBusyState(false);
notifyListeners(); notifyListeners();
} }
@@ -45,13 +49,14 @@ class ServerProvider extends BusyProvider {
await _getData(spi); await _getData(spi);
return; return;
} }
await Future.wait(_servers.map((s) async { final futures = _servers.values.map((s) async {
if (onlyFailed) { if (onlyFailed) {
if (s.state != ServerState.failed) return; if (s.state != ServerState.failed) return;
_limiter.resetTryTimes(s.spi.id); _limiter.resetTryTimes(s.spi.id);
} }
await _getData(s.spi); return await _getData(s.spi);
})); });
await Future.wait(futures);
} }
Future<void> startAutoRefresh() async { Future<void> startAutoRefresh() async {
@@ -71,102 +76,91 @@ class ServerProvider extends BusyProvider {
} }
} }
bool get isAutoRefreshOn => _timer != null;
void setDisconnected() { void setDisconnected() {
for (var i = 0; i < _servers.length; i++) { for (final s in _servers.values) {
_servers[i].state = ServerState.disconnected; s.state = ServerState.disconnected;
} }
_limiter.clear(); _limiter.clear();
notifyListeners(); notifyListeners();
} }
void closeServer({ServerPrivateInfo? spi}) { void closeServer({String? id}) {
if (spi == null) { if (id == null) {
for (var i = 0; i < _servers.length; i++) { for (final s in _servers.values) {
_servers[i].client?.close(); _closeOneServer(s.spi.id);
_servers[i].client = null;
} }
return; return;
} }
final idx = getServerIdx(spi.id); _closeOneServer(id);
_servers[idx].client?.close(); }
_servers[idx].client = null;
void _closeOneServer(String id) {
_servers[id]?.client?.close();
_servers[id]?.client = null;
} }
void addServer(ServerPrivateInfo spi) { void addServer(ServerPrivateInfo spi) {
_servers.add(genServer(spi)); _servers[spi.id] = genServer(spi);
notifyListeners(); notifyListeners();
locator<ServerStore>().put(spi); locator<ServerStore>().put(spi);
refreshData(spi: spi); refreshData(spi: spi);
} }
void delServer(String id) { void delServer(String id) {
final idx = getServerIdx(id); _servers.remove(id);
_servers[idx].client?.close();
_servers.removeAt(idx);
notifyListeners(); notifyListeners();
locator<ServerStore>().delete(id); locator<ServerStore>().delete(id);
} }
Future<void> updateServer( Future<void> updateServer(
ServerPrivateInfo old, ServerPrivateInfo newSpi) async { ServerPrivateInfo old, ServerPrivateInfo newSpi) async {
final idx = _servers.indexWhere((e) => e.spi.id == old.id); _servers.remove(old.id);
if (idx < 0) {
throw RangeError.index(idx, _servers);
}
_servers[idx].spi = newSpi;
locator<ServerStore>().update(old, newSpi); locator<ServerStore>().update(old, newSpi);
_servers[idx].client = await genClient(newSpi); _servers[newSpi.id] = genServer(newSpi);
_servers[newSpi.id]?.client = await genClient(newSpi);
notifyListeners(); notifyListeners();
refreshData(spi: newSpi); refreshData(spi: newSpi);
} }
int getServerIdx(String id) {
final idx = _servers.indexWhere((s) => s.spi.id == id);
if (idx < 0) {
throw Exception('Server not found: $id');
}
return idx;
}
Server getServer(String id) => _servers[getServerIdx(id)];
Future<void> _getData(ServerPrivateInfo spi) async { Future<void> _getData(ServerPrivateInfo spi) async {
final sid = spi.id; final sid = spi.id;
final s = getServer(sid); final s = _servers[sid];
final state = s.state; if (s == null) return;
if (!state.shouldConnect) {
return;
}
if (!_limiter.shouldTry(sid)) {
s.state = ServerState.failed;
notifyListeners();
return;
}
s.state = ServerState.connecting;
notifyListeners();
try { try {
// try to connect final state = s.state;
final time1 = DateTime.now(); if (state.shouldConnect) {
s.client = await genClient(spi); if (!_limiter.shouldTry(sid)) {
final time2 = DateTime.now(); s.state = ServerState.failed;
final spentTime = time2.difference(time1).inMilliseconds; notifyListeners();
_logger.info('Connected to [$sid] in $spentTime ms.'); return;
}
s.state = ServerState.connecting;
notifyListeners();
// after connected // try to connect
s.state = ServerState.connected; final time1 = DateTime.now();
notifyListeners(); s.client = await genClient(spi);
final time2 = DateTime.now();
final spentTime = time2.difference(time1).inMilliseconds;
_logger.info('Connected to [$sid] in $spentTime ms.');
// write script to server // after connected
final writeResult = await s.client!.run(installShellCmd).string; s.state = ServerState.connected;
notifyListeners();
// write script to server
final writeResult = await s.client!.run(installShellCmd).string;
// if write failed // if write failed
if (writeResult.isNotEmpty) { if (writeResult.isNotEmpty) {
throw Exception(writeResult); throw Exception(writeResult);
}
// reset try times if connected successfully
_limiter.resetTryTimes(sid);
} }
// reset try times if connected successfully
_limiter.resetTryTimes(sid);
if (s.client == null) return; if (s.client == null) return;
// run script to get server status // run script to get server status
final raw = await s.client!.run("sh $shellPath").string; final raw = await s.client!.run("sh $shellPath").string;
@@ -181,6 +175,7 @@ class ServerProvider extends BusyProvider {
// remove first empty segment // remove first empty segment
// for `export xxx` in shell script // for `export xxx` in shell script
segments.removeAt(0); segments.removeAt(0);
final req = ServerStatusUpdateReq(s.status, segments); final req = ServerStatusUpdateReq(s.status, segments);
s.status = await compute(getStatus, req); s.status = await compute(getStatus, req);
} catch (e) { } catch (e) {
@@ -196,7 +191,7 @@ class ServerProvider extends BusyProvider {
} }
Future<String?> runSnippet(String id, Snippet snippet) async { Future<String?> runSnippet(String id, Snippet snippet) async {
final client = getServer(id).client; final client = _servers[id]?.client;
if (client == null) { if (client == null) {
return null; return null;
} }

View File

@@ -48,7 +48,7 @@ class _DockerManagePageState extends State<DockerManagePage> {
@override @override
void initState() { void initState() {
super.initState(); super.initState();
final client = locator<ServerProvider>().getServer(widget.spi.id).client; final client = locator<ServerProvider>().servers[widget.spi.id]?.client;
if (client == null) { if (client == null) {
showSnackBar(context, Text(_s.noClient)); showSnackBar(context, Text(_s.noClient));
context.pop(); context.pop();

View File

@@ -84,7 +84,7 @@ class _MyHomePageState extends State<MyHomePage>
switch (state) { switch (state) {
case AppLifecycleState.resumed: case AppLifecycleState.resumed:
if (isIOS) { if (!_serverProvider.isAutoRefreshOn) {
_serverProvider.startAutoRefresh(); _serverProvider.startAutoRefresh();
} }
break; break;

View File

@@ -156,7 +156,7 @@ class _PingPageState extends State<PingPage>
} }
try { try {
await Future.wait(_serverProvider.servers.map((e) async { await Future.wait(_serverProvider.servers.values.map((e) async {
if (e.client == null) { if (e.client == null) {
return; return;
} }

View File

@@ -49,8 +49,8 @@ class _PkgManagePageState extends State<PkgManagePage>
@override @override
void initState() { void initState() {
super.initState(); super.initState();
final si = locator<ServerProvider>().getServer(widget.spi.id); final si = locator<ServerProvider>().servers[widget.spi.id];
if (si.client == null) { if (si == null || si.client == null) {
showSnackBar(context, Text(_s.waitConnection)); showSnackBar(context, Text(_s.waitConnection));
context.pop(); context.pop();
return; return;

View File

@@ -40,9 +40,15 @@ class _ServerDetailPageState extends State<ServerDetailPage>
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Consumer<ServerProvider>(builder: (_, provider, __) { return Consumer<ServerProvider>(builder: (_, provider, __) {
return _buildMainPage( final s = provider.servers[widget.id];
provider.getServer(widget.id), if (s == null) {
); return Scaffold(
body: Center(
child: Text(_s.noClient),
),
);
}
return _buildMainPage(s);
}); });
} }

View File

@@ -75,6 +75,7 @@ class _ServerPageState extends State<ServerPage>
), ),
); );
} }
final keys = pro.servers.keys.toList();
return ListView.separated( return ListView.separated(
padding: const EdgeInsets.all(7), padding: const EdgeInsets.all(7),
controller: ScrollController(), controller: ScrollController(),
@@ -83,9 +84,9 @@ class _ServerPageState extends State<ServerPage>
if (idx == pro.servers.length) { if (idx == pro.servers.length) {
return SizedBox(height: _media.padding.bottom); return SizedBox(height: _media.padding.bottom);
} }
return _buildEachServerCard(pro.servers[idx]); return _buildEachServerCard(pro.servers[keys[idx]]);
}, },
itemCount: pro.servers.length + 1, itemCount: pro.servers.length,
separatorBuilder: (_, __) => const SizedBox( separatorBuilder: (_, __) => const SizedBox(
height: 3, height: 3,
), ),
@@ -105,7 +106,10 @@ class _ServerPageState extends State<ServerPage>
); );
} }
Widget _buildEachServerCard(Server si) { Widget _buildEachServerCard(Server? si) {
if (si == null) {
return const SizedBox();
}
return RoundRectCard( return RoundRectCard(
InkWell( InkWell(
onLongPress: () => AppRoute( onLongPress: () => AppRoute(

View File

@@ -53,7 +53,7 @@ class _SFTPPageState extends State<SFTPPage> {
void initState() { void initState() {
super.initState(); super.initState();
final serverProvider = locator<ServerProvider>(); final serverProvider = locator<ServerProvider>();
_si = serverProvider.getServer(widget.spi.id); _si = serverProvider.servers[widget.spi.id];
_client = _si?.client; _client = _si?.client;
} }