diff --git a/lib/core/route.dart b/lib/core/route.dart index 4fb04d0f..e32f3c62 100644 --- a/lib/core/route.dart +++ b/lib/core/route.dart @@ -58,7 +58,7 @@ class AppRoutes { return Future.value(null); } - static AppRoutes serverDetail({Key? key, required ServerPrivateInfo spi}) { + static AppRoutes serverDetail({Key? key, required Spi spi}) { return AppRoutes(ServerDetailPage(key: key, spi: spi), 'server_detail'); } @@ -66,7 +66,7 @@ class AppRoutes { return AppRoutes(ServerPage(key: key), 'server_tab'); } - static AppRoutes serverEdit({Key? key, ServerPrivateInfo? spi}) { + static AppRoutes serverEdit({Key? key, Spi? spi}) { return AppRoutes( ServerEditPage(spi: spi), 'server_${spi == null ? 'add' : 'edit'}', @@ -97,7 +97,7 @@ class AppRoutes { static AppRoutes ssh({ Key? key, - required ServerPrivateInfo spi, + required Spi spi, String? initCmd, Snippet? initSnippet, }) { @@ -133,7 +133,7 @@ class AppRoutes { static AppRoutes sftp( {Key? key, - required ServerPrivateInfo spi, + required Spi spi, String? initPath, bool isSelect = false}) { return AppRoutes( @@ -150,7 +150,7 @@ class AppRoutes { return AppRoutes(BackupPage(key: key), 'backup'); } - static AppRoutes docker({Key? key, required ServerPrivateInfo spi}) { + static AppRoutes docker({Key? key, required Spi spi}) { return AppRoutes(ContainerPage(key: key, spi: spi), 'docker'); } @@ -186,7 +186,7 @@ class AppRoutes { return AppRoutes(PingPage(key: key), 'ping'); } - static AppRoutes process({Key? key, required ServerPrivateInfo spi}) { + static AppRoutes process({Key? key, required Spi spi}) { return AppRoutes(ProcessPage(key: key, spi: spi), 'process'); } @@ -220,7 +220,7 @@ class AppRoutes { 'snippet_result'); } - static AppRoutes iperf({Key? key, required ServerPrivateInfo spi}) { + static AppRoutes iperf({Key? key, required Spi spi}) { return AppRoutes(IPerfPage(key: key, spi: spi), 'iperf'); } @@ -228,7 +228,7 @@ class AppRoutes { return AppRoutes(ServerFuncBtnsOrderPage(key: key), 'server_func_btns_seq'); } - static AppRoutes pve({Key? key, required ServerPrivateInfo spi}) { + static AppRoutes pve({Key? key, required Spi spi}) { return AppRoutes(PvePage(key: key, spi: spi), 'pve'); } } diff --git a/lib/core/utils/server.dart b/lib/core/utils/server.dart index 0e7b4330..8d97c14e 100644 --- a/lib/core/utils/server.dart +++ b/lib/core/utils/server.dart @@ -42,7 +42,7 @@ String getPrivateKey(String id) { } Future genClient( - ServerPrivateInfo spi, { + Spi spi, { void Function(GenSSHClientStatus)? onStatus, /// Only pass this param if using multi-threading and key login @@ -52,10 +52,10 @@ Future genClient( String? jumpPrivateKey, Duration timeout = const Duration(seconds: 5), - /// [ServerPrivateInfo] of the jump server + /// [Spi] of the jump server /// /// Must pass this param if using multi-threading and key login - ServerPrivateInfo? jumpSpi, + Spi? jumpSpi, /// Handle keyboard-interactive authentication FutureOr?> Function(SSHUserInfoRequest)? onKeyboardInteractive, diff --git a/lib/core/utils/ssh_auth.dart b/lib/core/utils/ssh_auth.dart index b2754a12..c489712e 100644 --- a/lib/core/utils/ssh_auth.dart +++ b/lib/core/utils/ssh_auth.dart @@ -8,7 +8,7 @@ import 'package:server_box/data/provider/app.dart'; abstract final class KeybordInteractive { static FutureOr?> defaultHandle( - ServerPrivateInfo spi, { + Spi spi, { BuildContext? ctx, }) async { try { diff --git a/lib/data/model/app/backup.dart b/lib/data/model/app/backup.dart index b95c2a5d..f828a833 100644 --- a/lib/data/model/app/backup.dart +++ b/lib/data/model/app/backup.dart @@ -7,8 +7,8 @@ import 'package:logging/logging.dart'; import 'package:server_box/data/model/server/private_key_info.dart'; import 'package:server_box/data/model/server/server_private_info.dart'; import 'package:server_box/data/model/server/snippet.dart'; +import 'package:server_box/data/provider/base.dart'; import 'package:server_box/data/res/misc.dart'; -import 'package:server_box/data/res/provider.dart'; import 'package:server_box/data/res/rebuild.dart'; import 'package:server_box/data/res/store.dart'; @@ -23,7 +23,7 @@ class Backup { // backup format version final int version; final String date; - final List spis; + final List spis; final List snippets; final List keys; final Map container; @@ -177,7 +177,7 @@ class Backup { } } - Pros.reload(); + Provider.reload(); RNodes.app.notify(); _logger.info('Restore success'); diff --git a/lib/data/model/app/backup.g.dart b/lib/data/model/app/backup.g.dart index 29dcc24a..06e2a09c 100644 --- a/lib/data/model/app/backup.g.dart +++ b/lib/data/model/app/backup.g.dart @@ -10,7 +10,7 @@ Backup _$BackupFromJson(Map json) => Backup( version: (json['version'] as num).toInt(), date: json['date'] as String, spis: (json['spis'] as List) - .map((e) => ServerPrivateInfo.fromJson(e as Map)) + .map((e) => Spi.fromJson(e as Map)) .toList(), snippets: (json['snippets'] as List) .map((e) => Snippet.fromJson(e as Map)) diff --git a/lib/data/model/server/server.dart b/lib/data/model/server/server.dart index 74e6cbaa..b96b25c2 100644 --- a/lib/data/model/server/server.dart +++ b/lib/data/model/server/server.dart @@ -15,7 +15,7 @@ import 'package:server_box/data/model/server/system.dart'; import 'package:server_box/data/model/server/temp.dart'; class Server implements TagPickable { - ServerPrivateInfo spi; + Spi spi; ServerStatus status; SSHClient? client; ServerConn conn; diff --git a/lib/data/model/server/server_private_info.dart b/lib/data/model/server/server_private_info.dart index f2e9327d..2957b9a8 100644 --- a/lib/data/model/server/server_private_info.dart +++ b/lib/data/model/server/server_private_info.dart @@ -1,11 +1,12 @@ import 'dart:convert'; +import 'package:fl_lib/fl_lib.dart'; import 'package:hive_flutter/hive_flutter.dart'; import 'package:json_annotation/json_annotation.dart'; import 'package:server_box/data/model/server/custom.dart'; import 'package:server_box/data/model/server/server.dart'; import 'package:server_box/data/model/server/wol_cfg.dart'; -import 'package:server_box/data/res/provider.dart'; +import 'package:server_box/data/provider/server.dart'; import 'package:server_box/data/model/app/error.dart'; @@ -13,13 +14,13 @@ part 'server_private_info.g.dart'; /// In the first version, it's called `ServerPrivateInfo` which was designed to /// store the private information of a server. -/// +/// /// Some params named as `spi` in the codebase which is the abbreviation of `ServerPrivateInfo`. /// -/// Nowaday, more fields are added to this class, but the name is still the same. +/// Nowaday, more fields are added to this class, and it's renamed to `Spi`. @JsonSerializable() @HiveType(typeId: 3) -class ServerPrivateInfo { +class Spi { @HiveField(0) final String name; @HiveField(1) @@ -58,7 +59,7 @@ class ServerPrivateInfo { final String id; - const ServerPrivateInfo({ + const Spi({ required this.name, required this.ip, required this.port, @@ -74,17 +75,22 @@ class ServerPrivateInfo { this.envs, }) : id = '$user@$ip:$port'; - factory ServerPrivateInfo.fromJson(Map json) => - _$ServerPrivateInfoFromJson(json); + factory Spi.fromJson(Map json) => + _$SpiFromJson(json); - Map toJson() => _$ServerPrivateInfoToJson(this); + Map toJson() => _$SpiToJson(this); + @override + String toString() => id; +} + +extension Spix on Spi { String toJsonString() => json.encode(toJson()); - Server? get server => Pros.server.pick(spi: this); - Server? get jumpServer => Pros.server.pick(id: jumpId); + VNode? get server => ServerProvider.pick(spi: this); + VNode? get jumpServer => ServerProvider.pick(id: jumpId); - bool shouldReconnect(ServerPrivateInfo old) { + bool shouldReconnect(Spi old) { return id != old.id || pwd != old.pwd || keyId != old.keyId || @@ -113,12 +119,7 @@ class ServerPrivateInfo { return (ip_, port_); } - @override - String toString() { - return id; - } - - static const example = ServerPrivateInfo( + static const example = Spi( name: 'name', ip: 'ip', port: 22, diff --git a/lib/data/model/server/server_private_info.g.dart b/lib/data/model/server/server_private_info.g.dart index 97594438..8c7d184e 100644 --- a/lib/data/model/server/server_private_info.g.dart +++ b/lib/data/model/server/server_private_info.g.dart @@ -6,17 +6,17 @@ part of 'server_private_info.dart'; // TypeAdapterGenerator // ************************************************************************** -class ServerPrivateInfoAdapter extends TypeAdapter { +class SpiAdapter extends TypeAdapter { @override final int typeId = 3; @override - ServerPrivateInfo read(BinaryReader reader) { + Spi read(BinaryReader reader) { final numOfFields = reader.readByte(); final fields = { for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(), }; - return ServerPrivateInfo( + return Spi( name: fields[0] as String, ip: fields[1] as String, port: fields[2] as int, @@ -34,7 +34,7 @@ class ServerPrivateInfoAdapter extends TypeAdapter { } @override - void write(BinaryWriter writer, ServerPrivateInfo obj) { + void write(BinaryWriter writer, Spi obj) { writer ..writeByte(13) ..writeByte(0) @@ -71,7 +71,7 @@ class ServerPrivateInfoAdapter extends TypeAdapter { @override bool operator ==(Object other) => identical(this, other) || - other is ServerPrivateInfoAdapter && + other is SpiAdapter && runtimeType == other.runtimeType && typeId == other.typeId; } @@ -80,8 +80,7 @@ class ServerPrivateInfoAdapter extends TypeAdapter { // JsonSerializableGenerator // ************************************************************************** -ServerPrivateInfo _$ServerPrivateInfoFromJson(Map json) => - ServerPrivateInfo( +Spi _$SpiFromJson(Map json) => Spi( name: json['name'] as String, ip: json['ip'] as String, port: (json['port'] as num).toInt(), @@ -103,8 +102,7 @@ ServerPrivateInfo _$ServerPrivateInfoFromJson(Map json) => ), ); -Map _$ServerPrivateInfoToJson(ServerPrivateInfo instance) => - { +Map _$SpiToJson(Spi instance) => { 'name': instance.name, 'ip': instance.ip, 'port': instance.port, diff --git a/lib/data/model/server/snippet.dart b/lib/data/model/server/snippet.dart index 6b27183c..09ff3a76 100644 --- a/lib/data/model/server/snippet.dart +++ b/lib/data/model/server/snippet.dart @@ -48,7 +48,7 @@ class Snippet implements TagPickable { static final fmtFinder = RegExp(r'\$\{[^{}]+\}'); - String fmtWithSpi(ServerPrivateInfo spi) { + String fmtWithSpi(Spi spi) { return script.replaceAllMapped( fmtFinder, (match) { @@ -63,7 +63,7 @@ class Snippet implements TagPickable { Future runInTerm( Terminal terminal, - ServerPrivateInfo spi, { + Spi spi, { bool autoEnter = false, }) async { final argsFmted = fmtWithSpi(spi); @@ -159,12 +159,12 @@ class Snippet implements TagPickable { } static final fmtArgs = { - r'${host}': (ServerPrivateInfo spi) => spi.ip, - r'${port}': (ServerPrivateInfo spi) => spi.port.toString(), - r'${user}': (ServerPrivateInfo spi) => spi.user, - r'${pwd}': (ServerPrivateInfo spi) => spi.pwd ?? '', - r'${id}': (ServerPrivateInfo spi) => spi.id, - r'${name}': (ServerPrivateInfo spi) => spi.name, + r'${host}': (Spi spi) => spi.ip, + r'${port}': (Spi spi) => spi.port.toString(), + r'${user}': (Spi spi) => spi.user, + r'${pwd}': (Spi spi) => spi.pwd ?? '', + r'${id}': (Spi spi) => spi.id, + r'${name}': (Spi spi) => spi.name, }; /// r'${ctrl+ad}' -> TerminalKey.control, a, d diff --git a/lib/data/model/sftp/req.dart b/lib/data/model/sftp/req.dart index 8a23e017..a27a8c35 100644 --- a/lib/data/model/sftp/req.dart +++ b/lib/data/model/sftp/req.dart @@ -1,12 +1,12 @@ part of 'worker.dart'; class SftpReq { - final ServerPrivateInfo spi; + final Spi spi; final String remotePath; final String localPath; final SftpReqType type; String? privateKey; - ServerPrivateInfo? jumpSpi; + Spi? jumpSpi; String? jumpPrivateKey; SftpReq( diff --git a/lib/data/provider/base.dart b/lib/data/provider/base.dart new file mode 100644 index 00000000..05116018 --- /dev/null +++ b/lib/data/provider/base.dart @@ -0,0 +1,20 @@ +import 'dart:async'; + +import 'package:flutter/foundation.dart'; + +abstract class Provider { + const Provider(); + + /// (Re)Load data from store / network / etc. + @mustCallSuper + FutureOr load() { + all.add(this); + debugPrint('$runtimeType added'); + } + + static final all = []; + + static Future reload() { + return Future.wait(all.map((e) async => await e.load())); + } +} diff --git a/lib/data/provider/private_key.dart b/lib/data/provider/private_key.dart index 93f3798a..58ca691c 100644 --- a/lib/data/provider/private_key.dart +++ b/lib/data/provider/private_key.dart @@ -1,35 +1,40 @@ -import 'package:flutter/material.dart'; +import 'package:fl_lib/fl_lib.dart'; import 'package:server_box/data/model/server/private_key_info.dart'; +import 'package:server_box/data/provider/base.dart'; import 'package:server_box/data/res/store.dart'; -class PrivateKeyProvider extends ChangeNotifier { - List get pkis => _pkis; - late List _pkis; +class PrivateKeyProvider extends Provider { + const PrivateKeyProvider._(); + static const instance = PrivateKeyProvider._(); + static final pkis = [].vn; + + @override void load() { - _pkis = Stores.key.fetch(); + super.load(); + pkis.value = Stores.key.fetch(); } - void add(PrivateKeyInfo info) { - _pkis.add(info); + static void add(PrivateKeyInfo info) { + pkis.value.add(info); + pkis.notify(); Stores.key.put(info); - notifyListeners(); } - void delete(PrivateKeyInfo info) { - _pkis.removeWhere((e) => e.id == info.id); + static void delete(PrivateKeyInfo info) { + pkis.value.removeWhere((e) => e.id == info.id); + pkis.notify(); Stores.key.delete(info); - notifyListeners(); } - void update(PrivateKeyInfo old, PrivateKeyInfo newInfo) { - final idx = _pkis.indexWhere((e) => e.id == old.id); + static void update(PrivateKeyInfo old, PrivateKeyInfo newInfo) { + final idx = pkis.value.indexWhere((e) => e.id == old.id); if (idx == -1) { - _pkis.add(newInfo); + pkis.value.add(newInfo); } else { - _pkis[idx] = newInfo; + pkis.value[idx] = newInfo; } + pkis.notify(); Stores.key.put(newInfo); - notifyListeners(); } } diff --git a/lib/data/provider/pve.dart b/lib/data/provider/pve.dart index b0d482d3..21febbd4 100644 --- a/lib/data/provider/pve.dart +++ b/lib/data/provider/pve.dart @@ -15,7 +15,7 @@ import 'package:dartssh2/dartssh2.dart'; typedef PveCtrlFunc = Future Function(String node, String id); final class PveProvider extends ChangeNotifier { - final ServerPrivateInfo spi; + final Spi spi; late String addr; late final SSHClient _client; late final ServerSocket _serverSocket; @@ -23,7 +23,7 @@ final class PveProvider extends ChangeNotifier { int _localPort = 0; PveProvider({required this.spi}) { - final client = spi.server?.client; + final client = spi.server?.value.client; if (client == null) { throw Exception('Server client is null'); } diff --git a/lib/data/provider/server.dart b/lib/data/provider/server.dart index 328daf2c..acdfbfed 100644 --- a/lib/data/provider/server.dart +++ b/lib/data/provider/server.dart @@ -4,12 +4,12 @@ import 'dart:async'; import 'package:computer/computer.dart'; import 'package:dartssh2/dartssh2.dart'; import 'package:fl_lib/fl_lib.dart'; -import 'package:flutter/material.dart'; import 'package:server_box/core/extension/ssh_client.dart'; import 'package:server_box/core/utils/ssh_auth.dart'; import 'package:server_box/data/model/app/error.dart'; import 'package:server_box/data/model/app/shell_func.dart'; import 'package:server_box/data/model/server/system.dart'; +import 'package:server_box/data/provider/base.dart'; // import 'package:server_box/data/model/sftp/req.dart'; // import 'package:server_box/data/res/provider.dart'; import 'package:server_box/data/res/store.dart'; @@ -21,24 +21,27 @@ import 'package:server_box/data/model/server/server_status_update_req.dart'; import 'package:server_box/data/model/server/try_limiter.dart'; import 'package:server_box/data/res/status.dart'; -class ServerProvider extends ChangeNotifier { - final Map _servers = {}; - Iterable get servers => _servers.values; - final List _serverOrder = []; - List get serverOrder => _serverOrder; - final _tags = ValueNotifier({}); - ValueNotifier> get tags => _tags; +class ServerProvider extends Provider { + const ServerProvider._(); + static const instance = ServerProvider._(); - Timer? _timer; + static final Map> servers = {}; + static final serverOrder = [].vn; + static final _tags = {}.vn; + static VNode> get tags => _tags; - final _manualDisconnectedIds = {}; + static Timer? _timer; + static final _manualDisconnectedIds = {}; + + @override Future load() async { + super.load(); // #147 // Clear all servers because of restarting app will cause duplicate servers - final oldServers = Map.from(_servers); - _servers.clear(); - _serverOrder.clear(); + final oldServers = Map>.from(servers); + servers.clear(); + serverOrder.value.clear(); final spis = Stores.server.fetch(); for (int idx = 0; idx < spis.length; idx++) { @@ -48,10 +51,11 @@ class ServerProvider extends ChangeNotifier { /// #258 /// If not [shouldReconnect], then keep the old state. - if (originServer != null && !originServer.spi.shouldReconnect(spi)) { - newServer.conn = originServer.conn; + if (originServer != null && + !originServer.value.spi.shouldReconnect(spi)) { + newServer.conn = originServer.value.conn; } - _servers[spi.id] = newServer; + servers[spi.id] = newServer.vn; } final serverOrder_ = Stores.setting.serverOrder.fetch(); if (serverOrder_.isNotEmpty) { @@ -59,35 +63,37 @@ class ServerProvider extends ChangeNotifier { order: serverOrder_, finder: (n, id) => n.id == id, ); - _serverOrder.addAll(spis.map((e) => e.id)); + serverOrder.value.addAll(spis.map((e) => e.id)); } else { - _serverOrder.addAll(_servers.keys); + serverOrder.value.addAll(servers.keys); } // Must use [equals] to compare [Order] here. - if (!_serverOrder.equals(serverOrder_)) { - Stores.setting.serverOrder.put(_serverOrder); + if (!serverOrder.value.equals(serverOrder_)) { + Stores.setting.serverOrder.put(serverOrder.value); } _updateTags(); - notifyListeners(); + // Must notify here, or the UI will not be updated. + serverOrder.notify(); } /// Get a [Server] by [spi] or [id]. /// /// Priority: [spi] > [id] - Server? pick({ServerPrivateInfo? spi, String? id}) { + static VNode? pick({Spi? spi, String? id}) { if (spi != null) { - return _servers[spi.id]; + return servers[spi.id]; } if (id != null) { - return _servers[id]; + return servers[id]; } return null; } - void _updateTags() { - for (final s in _servers.values) { - if (s.spi.tags == null) continue; - for (final t in s.spi.tags!) { + static void _updateTags() { + for (final s in servers.values) { + final tags = s.value.spi.tags; + if (tags == null) continue; + for (final t in tags) { if (!_tags.value.contains(t)) { _tags.value.add(t); } @@ -96,14 +102,14 @@ class ServerProvider extends ChangeNotifier { _tags.value = (_tags.value.toList()..sort()).toSet(); } - Server genServer(ServerPrivateInfo spi) { + static Server genServer(Spi spi) { return Server(spi, InitStatus.status, ServerConn.disconnected); } /// if [spi] is specificed then only refresh this server /// [onlyFailed] only refresh failed servers - Future refresh({ - ServerPrivateInfo? spi, + static Future refresh({ + Spi? spi, bool onlyFailed = false, }) async { if (spi != null) { @@ -112,7 +118,8 @@ class ServerProvider extends ChangeNotifier { return; } - await Future.wait(_servers.values.map((s) async { + await Future.wait(servers.values.map((val) async { + final s = val.value; if (onlyFailed) { if (s.conn != ServerConn.failed) return; TryLimiter.reset(s.spi.id); @@ -128,7 +135,7 @@ class ServerProvider extends ChangeNotifier { })); } - Future startAutoRefresh() async { + static Future startAutoRefresh() async { var duration = Stores.setting.serverStatusUpdateInterval.fetch(); stopAutoRefresh(); if (duration == 0) return; @@ -141,84 +148,85 @@ class ServerProvider extends ChangeNotifier { }); } - void stopAutoRefresh() { + static void stopAutoRefresh() { if (_timer != null) { _timer!.cancel(); _timer = null; } } - bool get isAutoRefreshOn => _timer != null; + static bool get isAutoRefreshOn => _timer != null; - void setDisconnected() { - for (final s in _servers.values) { - s.conn = ServerConn.disconnected; + static void setDisconnected() { + for (final s in servers.values) { + s.value.conn = ServerConn.disconnected; + s.notify(); } //TryLimiter.clear(); - notifyListeners(); } - void closeServer({String? id}) { + static void closeServer({String? id}) { if (id == null) { - for (final s in _servers.values) { - _closeOneServer(s.spi.id); + for (final s in servers.values) { + _closeOneServer(s.value.spi.id); } return; } _closeOneServer(id); } - void _closeOneServer(String id) { - final item = _servers[id]; + static void _closeOneServer(String id) { + final s = servers[id]; + final item = s?.value; item?.client?.close(); item?.client = null; item?.conn = ServerConn.disconnected; _manualDisconnectedIds.add(id); - notifyListeners(); + s?.notify(); } - void addServer(ServerPrivateInfo spi) { - _servers[spi.id] = genServer(spi); - notifyListeners(); + static void addServer(Spi spi) { + servers[spi.id] = genServer(spi).vn; Stores.server.put(spi); - _serverOrder.add(spi.id); - Stores.setting.serverOrder.put(_serverOrder); + serverOrder.value.add(spi.id); + serverOrder.notify(); + Stores.setting.serverOrder.put(serverOrder.value); _updateTags(); refresh(spi: spi); } - void delServer(String id) { - _servers.remove(id); - _serverOrder.remove(id); - Stores.setting.serverOrder.put(_serverOrder); - _updateTags(); - notifyListeners(); + static void delServer(String id) { + servers.remove(id); + serverOrder.value.remove(id); + serverOrder.notify(); + Stores.setting.serverOrder.put(serverOrder.value); Stores.server.delete(id); - } - - void deleteAll() { - _servers.clear(); - _serverOrder.clear(); - Stores.setting.serverOrder.put(_serverOrder); _updateTags(); - notifyListeners(); - Stores.server.deleteAll(); } - Future updateServer( - ServerPrivateInfo old, - ServerPrivateInfo newSpi, + static void deleteAll() { + servers.clear(); + serverOrder.value.clear(); + serverOrder.notify(); + Stores.setting.serverOrder.put(serverOrder.value); + Stores.server.deleteAll(); + _updateTags(); + } + + static Future updateServer( + Spi old, + Spi newSpi, ) async { if (old != newSpi) { Stores.server.update(old, newSpi); - _servers[old.id]?.spi = newSpi; + servers[old.id]?.value.spi = newSpi; if (newSpi.id != old.id) { - _servers[newSpi.id] = _servers[old.id]!; - _servers[newSpi.id]?.spi = newSpi; - _servers.remove(old.id); - _serverOrder.update(old.id, newSpi.id); - Stores.setting.serverOrder.put(_serverOrder); + servers[newSpi.id] = servers[old.id]!; + servers.remove(old.id); + serverOrder.value.update(old.id, newSpi.id); + Stores.setting.serverOrder.put(serverOrder.value); + serverOrder.notify(); } // Only reconnect if neccessary @@ -227,33 +235,32 @@ class ServerProvider extends ChangeNotifier { TryLimiter.reset(newSpi.id); refresh(spi: newSpi); } - - // Only update if [spi.tags] changed - _updateTags(); } + _updateTags(); } - void _setServerState(Server s, ServerConn ss) { - s.conn = ss; - notifyListeners(); + static void _setServerState(VNode s, ServerConn ss) { + s.value.conn = ss; + s.notify(); } - Future _getData(ServerPrivateInfo spi) async { + static Future _getData(Spi spi) async { final sid = spi.id; - final s = _servers[sid]; + final s = servers[sid]; if (s == null) return; + final sv = s.value; if (!TryLimiter.canTry(sid)) { - if (s.conn != ServerConn.failed) { + if (sv.conn != ServerConn.failed) { _setServerState(s, ServerConn.failed); } return; } - s.status.err = null; + sv.status.err = null; - if (s.needGenClient || (s.client?.isClosed ?? true)) { + if (sv.needGenClient || (sv.client?.isClosed ?? true)) { _setServerState(s, ServerConn.connecting); final wol = spi.wolCfg; @@ -274,7 +281,7 @@ class ServerProvider extends ChangeNotifier { try { final time1 = DateTime.now(); - s.client = await genClient( + sv.client = await genClient( spi, timeout: Duration(seconds: Stores.setting.timeout.fetch()), onKeyboardInteractive: (_) => KeybordInteractive.defaultHandle(spi), @@ -288,7 +295,7 @@ class ServerProvider extends ChangeNotifier { } } catch (e) { TryLimiter.inc(sid); - s.status.err = SSHErr(type: SSHErrType.connect, message: e.toString()); + sv.status.err = SSHErr(type: SSHErrType.connect, message: e.toString()); _setServerState(s, ServerConn.failed); /// In order to keep privacy, print [spi.name] instead of [spi.id] @@ -301,7 +308,7 @@ class ServerProvider extends ChangeNotifier { final scriptRaw = ShellFunc.allScript(spi.custom?.cmds).uint8List; try { - final (_, writeScriptResult) = await s.client!.exec( + final (_, writeScriptResult) = await sv.client!.exec( (session) async { session.stdin.add(scriptRaw); session.stdin.close(); @@ -315,14 +322,14 @@ class ServerProvider extends ChangeNotifier { } on SSHAuthAbortError catch (e) { TryLimiter.inc(sid); final err = SSHErr(type: SSHErrType.auth, message: e.toString()); - s.status.err = err; + sv.status.err = err; Loggers.app.warning(err); _setServerState(s, ServerConn.failed); return; } on SSHAuthFailError catch (e) { TryLimiter.inc(sid); final err = SSHErr(type: SSHErrType.auth, message: e.toString()); - s.status.err = err; + sv.status.err = err; Loggers.app.warning(err); _setServerState(s, ServerConn.failed); return; @@ -330,18 +337,18 @@ class ServerProvider extends ChangeNotifier { // If max try times < 2 and can't write script, this will stop the status getting and etc. // TryLimiter.inc(sid); final err = SSHErr(type: SSHErrType.writeScript, message: e.toString()); - s.status.err = err; + sv.status.err = err; Loggers.app.warning(err); _setServerState(s, ServerConn.failed); } } - if (s.conn == ServerConn.connecting) return; + if (sv.conn == ServerConn.connecting) return; /// Keep [finished] state, or the UI will be refreshed to [loading] state /// instead of the '$Temp | $Uptime'. /// eg: '32C | 7 days' - if (s.conn != ServerConn.finished) { + if (sv.conn != ServerConn.finished) { _setServerState(s, ServerConn.loading); } @@ -349,17 +356,17 @@ class ServerProvider extends ChangeNotifier { String? raw; try { - raw = await s.client?.run(ShellFunc.status.exec(spi.id)).string; + raw = await sv.client?.run(ShellFunc.status.exec(spi.id)).string; segments = raw?.split(ShellFunc.seperator).map((e) => e.trim()).toList(); if (raw == null || raw.isEmpty || segments == null || segments.isEmpty) { if (Stores.setting.keepStatusWhenErr.fetch()) { // Keep previous server status when err occurs - if (s.conn != ServerConn.failed && s.status.more.isNotEmpty) { + if (sv.conn != ServerConn.failed && sv.status.more.isNotEmpty) { return; } } TryLimiter.inc(sid); - s.status.err = SSHErr( + sv.status.err = SSHErr( type: SSHErrType.segements, message: 'Seperate segments failed, raw:\n$raw', ); @@ -368,7 +375,7 @@ class ServerProvider extends ChangeNotifier { } } catch (e) { TryLimiter.inc(sid); - s.status.err = SSHErr(type: SSHErrType.getStatus, message: e.toString()); + sv.status.err = SSHErr(type: SSHErrType.getStatus, message: e.toString()); _setServerState(s, ServerConn.failed); Loggers.app.warning('Get status from ${spi.name} failed', e); return; @@ -379,36 +386,36 @@ class ServerProvider extends ChangeNotifier { if (!systemType.isSegmentsLenMatch(segments.length - customCmdLen)) { TryLimiter.inc(sid); if (raw.contains('Could not chdir to home directory /var/services/')) { - s.status.err = SSHErr(type: SSHErrType.chdir, message: raw); + sv.status.err = SSHErr(type: SSHErrType.chdir, message: raw); _setServerState(s, ServerConn.failed); return; } final expected = systemType.segmentsLen; final actual = segments.length; - s.status.err = SSHErr( + sv.status.err = SSHErr( type: SSHErrType.segements, message: 'Segments: expect $expected, got $actual, raw:\n\n$raw', ); _setServerState(s, ServerConn.failed); return; } - s.status.system = systemType; + sv.status.system = systemType; try { final req = ServerStatusUpdateReq( - ss: s.status, + ss: sv.status, segments: segments, system: systemType, customCmds: spi.custom?.cmds ?? {}, ); - s.status = await Computer.shared.start( + sv.status = await Computer.shared.start( getStatus, req, - taskName: 'StatusUpdateReq<${s.id}>', + taskName: 'StatusUpdateReq<${sv.id}>', ); } catch (e, trace) { TryLimiter.inc(sid); - s.status.err = SSHErr( + sv.status.err = SSHErr( type: SSHErrType.getStatus, message: 'Parse failed: $e\n\n$raw', ); diff --git a/lib/data/provider/sftp.dart b/lib/data/provider/sftp.dart index 0d4e030e..7f6f4ce5 100644 --- a/lib/data/provider/sftp.dart +++ b/lib/data/provider/sftp.dart @@ -1,38 +1,42 @@ import 'dart:async'; -import 'package:flutter/material.dart'; +import 'package:fl_lib/fl_lib.dart'; import 'package:server_box/data/model/sftp/worker.dart'; +import 'package:server_box/data/provider/base.dart'; -class SftpProvider extends ChangeNotifier { - final List _status = []; - List get status => _status; +class SftpProvider extends Provider { + const SftpProvider._(); + static const instance = SftpProvider._(); - SftpReqStatus? get(int id) { - return _status.singleWhere((element) => element.id == id); + static final status = [].vn; + + static SftpReqStatus? get(int id) { + return status.value.singleWhere((element) => element.id == id); } - int add(SftpReq req, {Completer? completer}) { - final status = SftpReqStatus( - notifyListeners: notifyListeners, + static int add(SftpReq req, {Completer? completer}) { + final reqStat = SftpReqStatus( + notifyListeners: status.notify, completer: completer, req: req, ); - _status.add(status); - return status.id; + status.value.add(reqStat); + status.notify(); + return reqStat.id; } - @override - void dispose() { - for (final item in _status) { + static void dispose() { + for (final item in status.value) { item.dispose(); } - super.dispose(); + status.value.clear(); + status.notify(); } - void cancel(int id) { - final idx = _status.indexWhere((element) => element.id == id); - _status[idx].dispose(); - _status.removeAt(idx); - notifyListeners(); + static void cancel(int id) { + final idx = status.value.indexWhere((e) => e.id == id); + status.value[idx].dispose(); + status.value.removeAt(idx); + status.notify(); } } diff --git a/lib/data/provider/snippet.dart b/lib/data/provider/snippet.dart index 914b639a..a4c49019 100644 --- a/lib/data/provider/snippet.dart +++ b/lib/data/provider/snippet.dart @@ -1,21 +1,22 @@ -import 'dart:convert'; - import 'package:fl_lib/fl_lib.dart'; -import 'package:flutter/material.dart'; import 'package:server_box/data/model/server/snippet.dart'; +import 'package:server_box/data/provider/base.dart'; import 'package:server_box/data/res/store.dart'; -class SnippetProvider extends ChangeNotifier { - late List _snippets; - List get snippets => _snippets; +class SnippetProvider extends Provider { + const SnippetProvider._(); + static const instance = SnippetProvider._(); - final tags = ValueNotifier({}); + static final snippets = [].vn; + static final tags = {}.vn; + @override void load() { - _snippets = Stores.snippet.fetch(); + super.load(); + final snippets_ = Stores.snippet.fetch(); final order = Stores.setting.snippetOrder.fetch(); if (order.isNotEmpty) { - final surplus = _snippets.reorder( + final surplus = snippets_.reorder( order: order, finder: (n, name) => n.name == name, ); @@ -24,12 +25,13 @@ class SnippetProvider extends ChangeNotifier { Stores.setting.snippetOrder.put(order); } } + snippets.value = snippets_; _updateTags(); } - void _updateTags() { + static void _updateTags() { final tags_ = {}; - for (final s in _snippets) { + for (final s in snippets.value) { final t = s.tags; if (t != null) { tags_.addAll(t); @@ -38,31 +40,31 @@ class SnippetProvider extends ChangeNotifier { tags.value = tags_; } - void add(Snippet snippet) { - _snippets.add(snippet); + static void add(Snippet snippet) { + snippets.value.add(snippet); + snippets.notify(); Stores.snippet.put(snippet); _updateTags(); - notifyListeners(); } - void del(Snippet snippet) { - _snippets.remove(snippet); + static void del(Snippet snippet) { + snippets.value.remove(snippet); + snippets.notify(); Stores.snippet.delete(snippet); _updateTags(); - notifyListeners(); } - void update(Snippet old, Snippet newOne) { + static void update(Snippet old, Snippet newOne) { + snippets.value.remove(old); + snippets.value.add(newOne); + snippets.notify(); Stores.snippet.delete(old); Stores.snippet.put(newOne); - _snippets.remove(old); - _snippets.add(newOne); _updateTags(); - notifyListeners(); } - void renameTag(String old, String newOne) { - for (final s in _snippets) { + static void renameTag(String old, String newOne) { + for (final s in snippets.value) { if (s.tags?.contains(old) ?? false) { s.tags?.remove(old); s.tags?.add(newOne); @@ -70,8 +72,5 @@ class SnippetProvider extends ChangeNotifier { } } _updateTags(); - notifyListeners(); } - - String get export => json.encode(snippets); } diff --git a/lib/data/provider/systemd.dart b/lib/data/provider/systemd.dart index 817c6653..e619e518 100644 --- a/lib/data/provider/systemd.dart +++ b/lib/data/provider/systemd.dart @@ -1,18 +1,18 @@ -import 'package:dartssh2/dartssh2.dart'; import 'package:fl_lib/fl_lib.dart'; import 'package:server_box/core/extension/ssh_client.dart'; import 'package:server_box/data/model/app/shell_func.dart'; +import 'package:server_box/data/model/server/server.dart'; import 'package:server_box/data/model/server/server_private_info.dart'; import 'package:server_box/data/model/server/systemd.dart'; -import 'package:server_box/data/res/provider.dart'; +import 'package:server_box/data/provider/server.dart'; final class SystemdProvider { - late final SSHClient _client; - late final bool isRoot; + late final VNode _si; + late final bool _isRoot; - SystemdProvider.init(ServerPrivateInfo spi) { - isRoot = spi.isRoot; - _client = Pros.server.pick(spi: spi)!.client!; + SystemdProvider.init(Spi spi) { + _isRoot = spi.isRoot; + _si = ServerProvider.pick(spi: spi)!; getUnits(); } @@ -23,7 +23,8 @@ final class SystemdProvider { isBusy.value = true; try { - final result = await _client.execForOutput(_getUnitsCmd); + final client = _si.value.client; + final result = await client!.execForOutput(_getUnitsCmd); final units = result.split('\n'); final userUnits = []; @@ -63,7 +64,8 @@ for unit in ${unitNames_.join(' ')}; do echo -n "${ShellFunc.seperator}\n\$state" done '''; - final result = await _client.execForOutput(script); + final client = _si.value.client!; + final result = await client.execForOutput(script); final units = result.split(ShellFunc.seperator); final parsedUnits = []; @@ -141,7 +143,7 @@ get_type_files() { unit_type=\$1 base_dir="" -${isRoot ? """ +${_isRoot ? """ get_files \$unit_type /etc/systemd/system get_files \$unit_type ~/.config/systemd/user""" : """ get_files \$unit_type ~/.config/systemd/user"""} diff --git a/lib/data/res/provider.dart b/lib/data/res/provider.dart deleted file mode 100644 index 8db595e3..00000000 --- a/lib/data/res/provider.dart +++ /dev/null @@ -1,17 +0,0 @@ -import 'package:server_box/data/provider/private_key.dart'; -import 'package:server_box/data/provider/server.dart'; -import 'package:server_box/data/provider/sftp.dart'; -import 'package:server_box/data/provider/snippet.dart'; - -abstract final class Pros { - static final key = PrivateKeyProvider(); - static final server = ServerProvider(); - static final sftp = SftpProvider(); - static final snippet = SnippetProvider(); - - static void reload() { - key.load(); - server.load(); - snippet.load(); - } -} diff --git a/lib/data/store/server.dart b/lib/data/store/server.dart index 2f773d35..0bbd4e1b 100644 --- a/lib/data/store/server.dart +++ b/lib/data/store/server.dart @@ -5,17 +5,17 @@ import 'package:server_box/data/model/server/server_private_info.dart'; class ServerStore extends PersistentStore { ServerStore() : super('server'); - void put(ServerPrivateInfo info) { + void put(Spi info) { box.put(info.id, info); box.updateLastModified(); } - List fetch() { + List fetch() { final ids = box.keys; - final List ss = []; + final List ss = []; for (final id in ids) { final s = box.get(id); - if (s != null && s is ServerPrivateInfo) { + if (s != null && s is Spi) { ss.add(s); } } @@ -32,7 +32,7 @@ class ServerStore extends PersistentStore { box.updateLastModified(); } - void update(ServerPrivateInfo old, ServerPrivateInfo newInfo) { + void update(Spi old, Spi newInfo) { if (!have(old)) { throw Exception('Old spi: $old not found'); } @@ -40,5 +40,5 @@ class ServerStore extends PersistentStore { put(newInfo); } - bool have(ServerPrivateInfo s) => box.get(s.id) != null; + bool have(Spi s) => box.get(s.id) != null; } diff --git a/lib/main.dart b/lib/main.dart index 9feea17d..fbceb053 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -8,7 +8,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_displaymode/flutter_displaymode.dart'; import 'package:hive_flutter/hive_flutter.dart'; import 'package:logging/logging.dart'; -import 'package:provider/provider.dart'; import 'package:server_box/app.dart'; import 'package:server_box/core/utils/sync/icloud.dart'; import 'package:server_box/core/utils/sync/webdav.dart'; @@ -21,25 +20,18 @@ import 'package:server_box/data/model/server/server_private_info.dart'; import 'package:server_box/data/model/server/snippet.dart'; import 'package:server_box/data/model/server/wol_cfg.dart'; import 'package:server_box/data/model/ssh/virtual_key.dart'; +import 'package:server_box/data/provider/private_key.dart'; +import 'package:server_box/data/provider/server.dart'; +import 'package:server_box/data/provider/sftp.dart'; +import 'package:server_box/data/provider/snippet.dart'; import 'package:server_box/data/res/build_data.dart'; import 'package:server_box/data/res/misc.dart'; -import 'package:server_box/data/res/provider.dart'; import 'package:server_box/data/res/store.dart'; Future main() async { _runInZone(() async { await _initApp(); - runApp( - MultiProvider( - providers: [ - ChangeNotifierProvider(create: (_) => Pros.server), - ChangeNotifierProvider(create: (_) => Pros.snippet), - ChangeNotifierProvider(create: (_) => Pros.key), - ChangeNotifierProvider(create: (_) => Pros.sftp), - ], - child: const MyApp(), - ), - ); + runApp(const MyApp()); }); } @@ -84,7 +76,7 @@ Future _initData() async { // Ordered by typeId Hive.registerAdapter(PrivateKeyInfoAdapter()); // 1 Hive.registerAdapter(SnippetAdapter()); // 2 - Hive.registerAdapter(ServerPrivateInfoAdapter()); // 3 + Hive.registerAdapter(SpiAdapter()); // 3 Hive.registerAdapter(VirtKeyAdapter()); // 4 Hive.registerAdapter(NetViewTypeAdapter()); // 5 Hive.registerAdapter(ServerFuncBtnAdapter()); // 6 @@ -94,8 +86,11 @@ Future _initData() async { await PrefStore.init(); // Call this before accessing any store await Stores.init(); - Pros.snippet.load(); - Pros.key.load(); + // DO NOT change the order of these providers. + PrivateKeyProvider.instance.load(); + SnippetProvider.instance.load(); + ServerProvider.instance.load(); + SftpProvider.instance.load(); if (Stores.setting.betaTest.fetch()) AppUpdate.chan = AppUpdateChan.beta; } diff --git a/lib/view/page/backup.dart b/lib/view/page/backup.dart index 20245f9a..b03a80a8 100644 --- a/lib/view/page/backup.dart +++ b/lib/view/page/backup.dart @@ -10,8 +10,8 @@ import 'package:server_box/core/utils/sync/webdav.dart'; import 'package:server_box/data/model/app/backup.dart'; import 'package:server_box/data/model/server/server_private_info.dart'; import 'package:server_box/data/model/server/snippet.dart'; +import 'package:server_box/data/provider/snippet.dart'; import 'package:server_box/data/res/misc.dart'; -import 'package:server_box/data/res/provider.dart'; import 'package:server_box/data/res/store.dart'; import 'package:icons_plus/icons_plus.dart'; @@ -266,7 +266,7 @@ class BackupPage extends StatelessWidget { actions: Btn.ok( onTap: () { for (final snippet in snippets) { - Pros.snippet.add(snippet); + SnippetProvider.add(snippet); } context.pop(); context.pop(); @@ -441,7 +441,7 @@ class BackupPage extends StatelessWidget { void _onBulkImportServers(BuildContext context) async { final data = await context.showImportDialog( title: l10n.server, - modelDef: ServerPrivateInfo.example.toJson(), + modelDef: Spix.example.toJson(), ); if (data == null) return; final text = String.fromCharCodes(data); @@ -450,7 +450,7 @@ class BackupPage extends StatelessWidget { final (spis, err) = await context.showLoadingDialog( fn: () => Computer.shared.start((val) { final list = json.decode(val) as List; - return list.map((e) => ServerPrivateInfo.fromJson(e)).toList(); + return list.map((e) => Spi.fromJson(e)).toList(); }, text.trim()), ); if (err != null || spis == null) return; diff --git a/lib/view/page/container.dart b/lib/view/page/container.dart index 1a90b753..32867285 100644 --- a/lib/view/page/container.dart +++ b/lib/view/page/container.dart @@ -17,7 +17,7 @@ import 'package:server_box/data/provider/container.dart'; import 'package:server_box/view/widget/two_line_text.dart'; class ContainerPage extends StatefulWidget { - final ServerPrivateInfo spi; + final Spi spi; const ContainerPage({required this.spi, super.key}); @override @@ -27,7 +27,7 @@ class ContainerPage extends StatefulWidget { class _ContainerPageState extends State { final _textController = TextEditingController(); late final _container = ContainerProvider( - client: widget.spi.server?.client, + client: widget.spi.server?.value.client, userName: widget.spi.user, hostId: widget.spi.id, context: context, diff --git a/lib/view/page/home/home.dart b/lib/view/page/home/home.dart index 0c0ea82b..36d36bfe 100644 --- a/lib/view/page/home/home.dart +++ b/lib/view/page/home/home.dart @@ -9,10 +9,10 @@ import 'package:server_box/core/extension/context/locale.dart'; import 'package:server_box/core/route.dart'; import 'package:server_box/data/model/app/tab.dart'; import 'package:server_box/data/provider/app.dart'; +import 'package:server_box/data/provider/server.dart'; import 'package:server_box/data/res/build_data.dart'; import 'package:server_box/data/res/github_id.dart'; import 'package:server_box/data/res/misc.dart'; -import 'package:server_box/data/res/provider.dart'; import 'package:server_box/data/res/store.dart'; import 'package:server_box/data/res/url.dart'; import 'package:wakelock_plus/wakelock_plus.dart'; @@ -65,7 +65,7 @@ class _HomePageState extends State void dispose() { super.dispose(); WidgetsBinding.instance.removeObserver(this); - Pros.server.closeServer(); + ServerProvider.closeServer(); _pageController.dispose(); WakelockPlus.disable(); } @@ -78,8 +78,8 @@ class _HomePageState extends State switch (state) { case AppLifecycleState.resumed: if (_shouldAuth) _goAuth(); - if (!Pros.server.isAutoRefreshOn) { - Pros.server.startAutoRefresh(); + if (!ServerProvider.isAutoRefreshOn) { + ServerProvider.startAutoRefresh(); } HomeWidgetMC.update(); break; @@ -93,7 +93,7 @@ class _HomePageState extends State // } } else { //Pros.server.setDisconnected(); - Pros.server.stopAutoRefresh(); + ServerProvider.stopAutoRefresh(); } break; default: @@ -120,7 +120,7 @@ class _HomePageState extends State icon: const Icon(Icons.refresh), tooltip: 'Refresh', onPressed: () async { - await Pros.server.refresh(); + await ServerProvider.refresh(); }, ); }, @@ -333,8 +333,7 @@ ${GithubIds.participants.map((e) => '[$e](${e.url})').join(' ')} ); } HomeWidgetMC.update(); - await Pros.server.load(); - await Pros.server.refresh(); + await ServerProvider.refresh(); } // Future _reqNotiPerm() async { diff --git a/lib/view/page/home/wear.dart b/lib/view/page/home/wear.dart deleted file mode 100644 index f8a8fceb..00000000 --- a/lib/view/page/home/wear.dart +++ /dev/null @@ -1,102 +0,0 @@ -import 'dart:async'; - -import 'package:fl_lib/fl_lib.dart'; -import 'package:flutter/material.dart'; -import 'package:provider/provider.dart'; -import 'package:server_box/data/model/server/server.dart'; -import 'package:server_box/data/provider/server.dart'; -import 'package:server_box/data/res/build_data.dart'; -import 'package:server_box/data/res/provider.dart'; -import 'package:server_box/data/res/store.dart'; -import 'package:server_box/data/res/url.dart'; - -final class WearHome extends StatefulWidget { - const WearHome({super.key}); - - @override - State createState() => _WearHomeState(); -} - -final class _WearHomeState extends State with AfterLayoutMixin { - late final _pageCtrl = - PageController(initialPage: Pros.server.servers.isNotEmpty ? 1 : 0); - - @override - Widget build(BuildContext context) { - return _buildBody(); - } - - Widget _buildBody() { - return Consumer(builder: (_, pro, __) { - if (pro.servers.isEmpty) { - return const Center(child: Text('No server')); - } - return PageView.builder( - controller: _pageCtrl, - itemCount: pro.servers.length + 1, - itemBuilder: (_, index) { - if (index == 0) return _buildInit(); - - final id = pro.serverOrder[index]; - final server = Pros.server.pick(id: id); - if (server == null) return UIs.placeholder; - return _buildEachSever(server); - }, - ); - }); - } - - Widget _buildInit() { - return Center( - child: Column( - children: [ - IconButton(onPressed: () {}, icon: const Icon(Icons.add)), - UIs.height7, - Text(libL10n.restore) - ], - ), - ); - } - - Widget _buildEachSever(Server srv) { - final mem = () { - final total = srv.status.mem.total; - final used = srv.status.mem.total - srv.status.mem.avail; - return '${used.bytes2Str} / ${total.bytes2Str}'; - }(); - final disk = () { - final total = srv.status.diskUsage?.size.kb2Str; - final used = srv.status.diskUsage?.used.kb2Str; - return '$used / $total'; - }(); - final net = '↓ ${srv.status.netSpeed.cachedRealVals.speedIn}' - '↑ ${srv.status.netSpeed.cachedRealVals.speedOut}'; - return Padding( - padding: const EdgeInsets.all(7), - child: Column( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text(srv.spi.name, style: UIs.text15Bold), - UIs.height7, - KvRow(k: 'CPU', v: '${srv.status.cpu.usedPercent()}%'), - KvRow(k: 'Mem', v: mem), - KvRow(k: 'Disk', v: disk), - KvRow(k: 'Net', v: net) - ], - ), - ); - } - - @override - FutureOr afterFirstLayout(BuildContext context) async { - if (Stores.setting.autoCheckAppUpdate.fetch()) { - AppUpdateIface.doUpdate( - build: BuildData.build, - url: Urls.updateCfg, - context: context, - ); - } - await Pros.server.load(); - await Pros.server.refresh(); - } -} diff --git a/lib/view/page/iperf.dart b/lib/view/page/iperf.dart index 1f541a70..b13b569b 100644 --- a/lib/view/page/iperf.dart +++ b/lib/view/page/iperf.dart @@ -5,7 +5,7 @@ import 'package:server_box/core/route.dart'; import 'package:server_box/data/model/server/server_private_info.dart'; class IPerfPage extends StatefulWidget { - final ServerPrivateInfo spi; + final Spi spi; const IPerfPage({super.key, required this.spi}); @override diff --git a/lib/view/page/ping.dart b/lib/view/page/ping.dart index af36194d..8539b2b3 100644 --- a/lib/view/page/ping.dart +++ b/lib/view/page/ping.dart @@ -3,7 +3,7 @@ import 'dart:async'; import 'package:fl_lib/fl_lib.dart'; import 'package:flutter/material.dart'; import 'package:server_box/core/extension/context/locale.dart'; -import 'package:server_box/data/res/provider.dart'; +import 'package:server_box/data/provider/server.dart'; import 'package:server_box/data/model/server/ping_result.dart'; @@ -150,7 +150,7 @@ class _PingPageState extends State return; } - if (Pros.server.serverOrder.isEmpty) { + if (ServerProvider.serverOrder.value.isEmpty) { context.showSnackBar(l10n.pingNoServer); return; } @@ -161,7 +161,8 @@ class _PingPageState extends State return; } - await Future.wait(Pros.server.servers.map((e) async { + await Future.wait(ServerProvider.servers.values.map((v) async { + final e = v.value; if (e.client == null) { return; } diff --git a/lib/view/page/private_key/edit.dart b/lib/view/page/private_key/edit.dart index 30cfc0d6..1bce62a5 100644 --- a/lib/view/page/private_key/edit.dart +++ b/lib/view/page/private_key/edit.dart @@ -5,8 +5,8 @@ import 'package:fl_lib/fl_lib.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:server_box/core/extension/context/locale.dart'; +import 'package:server_box/data/provider/private_key.dart'; import 'package:server_box/data/res/misc.dart'; -import 'package:server_box/data/res/provider.dart'; import 'package:server_box/core/utils/server.dart'; import 'package:server_box/data/model/server/private_key_info.dart'; @@ -89,7 +89,7 @@ class _PrivateKeyEditPageState extends State { )), actions: Btn.ok( onTap: () { - Pros.key.delete(widget.pki!); + PrivateKeyProvider.delete(widget.pki!); context.pop(); context.pop(); }, @@ -204,9 +204,9 @@ class _PrivateKeyEditPageState extends State { final decrypted = await Computer.shared.start(decyptPem, [key, pwd]); final pki = PrivateKeyInfo(id: name, key: decrypted); if (widget.pki != null) { - Pros.key.update(widget.pki!, pki); + PrivateKeyProvider.update(widget.pki!, pki); } else { - Pros.key.add(pki); + PrivateKeyProvider.add(pki); } } catch (e) { context.showSnackBar(e.toString()); diff --git a/lib/view/page/private_key/list.dart b/lib/view/page/private_key/list.dart index 448a1748..dc0b3706 100644 --- a/lib/view/page/private_key/list.dart +++ b/lib/view/page/private_key/list.dart @@ -3,7 +3,6 @@ import 'dart:async'; import 'package:fl_lib/fl_lib.dart'; import 'package:flutter/material.dart'; -import 'package:provider/provider.dart'; import 'package:server_box/core/extension/context/locale.dart'; import 'package:server_box/data/res/store.dart'; @@ -35,16 +34,16 @@ class _PrivateKeyListState extends State } Widget _buildBody() { - return Consumer( - builder: (_, key, __) { - if (key.pkis.isEmpty) { + return PrivateKeyProvider.pkis.listenVal( + (pkis) { + if (pkis.isEmpty) { return Center(child: Text(libL10n.empty)); } return ListView.builder( padding: const EdgeInsets.all(13), - itemCount: key.pkis.length, + itemCount: pkis.length, itemBuilder: (context, idx) { - final item = key.pkis[idx]; + final item = pkis[idx]; return CardX( child: ListTile( leading: Text( diff --git a/lib/view/page/process.dart b/lib/view/page/process.dart index fec929bb..6c73f902 100644 --- a/lib/view/page/process.dart +++ b/lib/view/page/process.dart @@ -12,7 +12,7 @@ import 'package:server_box/data/model/server/server_private_info.dart'; import 'package:server_box/view/widget/two_line_text.dart'; class ProcessPage extends StatefulWidget { - final ServerPrivateInfo spi; + final Spi spi; const ProcessPage({super.key, required this.spi}); @override @@ -37,7 +37,7 @@ class _ProcessPageState extends State { @override void initState() { super.initState(); - _client = widget.spi.server?.client; + _client = widget.spi.server?.value.client; final duration = Duration(seconds: Stores.setting.serverStatusUpdateInterval.fetch()); _timer = Timer.periodic(duration, (_) => _refresh()); diff --git a/lib/view/page/pve.dart b/lib/view/page/pve.dart index 889ff401..23063744 100644 --- a/lib/view/page/pve.dart +++ b/lib/view/page/pve.dart @@ -11,7 +11,7 @@ import 'package:server_box/view/widget/percent_circle.dart'; import 'package:server_box/view/widget/two_line_text.dart'; final class PvePage extends StatefulWidget { - final ServerPrivateInfo spi; + final Spi spi; const PvePage({ super.key, diff --git a/lib/view/page/server/detail/view.dart b/lib/view/page/server/detail/view.dart index 5d50bb2f..772b8b82 100644 --- a/lib/view/page/server/detail/view.dart +++ b/lib/view/page/server/detail/view.dart @@ -4,7 +4,6 @@ import 'package:fl_lib/fl_lib.dart'; import 'package:flutter/material.dart'; import 'package:flutter_markdown/flutter_markdown.dart'; import 'package:icons_plus/icons_plus.dart'; -import 'package:provider/provider.dart'; import 'package:server_box/core/extension/context/locale.dart'; import 'package:server_box/data/model/app/server_detail_card.dart'; import 'package:server_box/data/model/app/shell_func.dart'; @@ -22,14 +21,13 @@ import 'package:server_box/view/widget/server_func_btns.dart'; import 'package:server_box/core/route.dart'; import 'package:server_box/data/model/server/server.dart'; -import 'package:server_box/data/provider/server.dart'; part 'misc.dart'; class ServerDetailPage extends StatefulWidget { const ServerDetailPage({super.key, required this.spi}); - final ServerPrivateInfo spi; + final Spi spi; @override State createState() => _ServerDetailPageState(); @@ -79,16 +77,14 @@ class _ServerDetailPageState extends State @override Widget build(BuildContext context) { - return Consumer(builder: (_, provider, __) { - final s = widget.spi.server; - if (s == null) { - return Scaffold( - appBar: const CustomAppBar(), - body: Center(child: Text(libL10n.empty)), - ); - } - return _buildMainPage(s); - }); + final s = widget.spi.server; + if (s == null) { + return Scaffold( + appBar: const CustomAppBar(), + body: Center(child: Text(libL10n.empty)), + ); + } + return s.listenVal(_buildMainPage); } Widget _buildMainPage(Server si) { @@ -96,12 +92,12 @@ class _ServerDetailPageState extends State final logo = _buildLogo(si); final children = [ logo, - if (buildFuncs) ServerFuncBtns(spi: widget.spi), + if (buildFuncs) ServerFuncBtns(spi: si.spi), ]; for (final card in _cardsOrder) { final buildFunc = _cardBuildMap[card]; if (buildFunc != null) { - children.add(buildFunc(si.status)); + children.add(buildFunc(si)); } } return Scaffold( @@ -122,8 +118,8 @@ class _ServerDetailPageState extends State title: Text(si.spi.name), actions: [ ShareBtn( - data: widget.spi.toJsonString(), - tip: widget.spi.name, + data: si.spi.toJsonString(), + tip: si.spi.name, tip2: '${libL10n.share} ${l10n.server} ~ ServerBox', ), IconButton( @@ -160,7 +156,8 @@ class _ServerDetailPageState extends State ); } - Widget _buildAbout(ServerStatus ss) { + Widget _buildAbout(Server si) { + final ss = si.status; return CardX( child: ExpandTile( leading: const Icon(MingCute.information_fill, size: 20), @@ -188,7 +185,8 @@ class _ServerDetailPageState extends State ); } - Widget _buildCPUView(ServerStatus ss) { + Widget _buildCPUView(Server si) { + final ss = si.status; final percent = ss.cpu.usedPercent(coreIdx: 0).toInt(); final details = [ _buildDetailPercent(ss.cpu.user, 'user'), @@ -352,7 +350,8 @@ class _ServerDetailPageState extends State ); } - Widget _buildMemView(ServerStatus ss) { + Widget _buildMemView(Server si) { + final ss = si.status; final free = ss.mem.free / ss.mem.total * 100; final avail = ss.mem.availPercent * 100; final used = ss.mem.usedPercent * 100; @@ -399,7 +398,8 @@ class _ServerDetailPageState extends State ); } - Widget _buildSwapView(ServerStatus ss) { + Widget _buildSwapView(Server si) { + final ss = si.status; if (ss.swap.total == 0) return UIs.placeholder; final used = ss.swap.usedPercent * 100; final cached = ss.swap.cached / ss.swap.total * 100; @@ -434,7 +434,8 @@ class _ServerDetailPageState extends State ); } - Widget _buildGpuView(ServerStatus ss) { + Widget _buildGpuView(Server si) { + final ss = si.status; if (ss.nvidia == null || ss.nvidia?.isEmpty == true) return UIs.placeholder; final children = ss.nvidia?.map((e) => _buildGpuItem(e)).toList() ?? []; return CardX( @@ -544,7 +545,8 @@ class _ServerDetailPageState extends State ); } - Widget _buildDiskView(ServerStatus ss) { + Widget _buildDiskView(Server si) { + final ss = si.status; final children = List.generate( ss.disk.length, (idx) => _buildDiskItem(ss.disk[idx], ss)); return CardX( @@ -608,7 +610,8 @@ class _ServerDetailPageState extends State ); } - Widget _buildNetView(ServerStatus ss) { + Widget _buildNetView(Server si) { + final ss = si.status; final ns = ss.netSpeed; final children = []; final devices = ns.devices; @@ -691,14 +694,15 @@ class _ServerDetailPageState extends State ); } - Widget _buildTemperature(ServerStatus ss) { + Widget _buildTemperature(Server si) { + final ss = si.status; if (ss.temps.isEmpty) { return UIs.placeholder; } return CardX( child: ExpandTile( title: Text(l10n.temperature), - leading: const Icon(Icons.ac_unit, size: 17), + leading: const Icon(Icons.ac_unit, size: 20), initiallyExpanded: _getInitExpand(ss.temps.devices.length), childrenPadding: const EdgeInsets.only(bottom: 7), children: ss.temps.devices @@ -726,7 +730,8 @@ class _ServerDetailPageState extends State ); } - Widget _buildBatteries(ServerStatus ss) { + Widget _buildBatteries(Server si) { + final ss = si.status; if (ss.batteries.isEmpty) { return UIs.placeholder; } @@ -767,7 +772,8 @@ class _ServerDetailPageState extends State ); } - Widget _buildSensors(ServerStatus ss) { + Widget _buildSensors(Server si) { + final ss = si.status; if (ss.sensors.isEmpty) return UIs.placeholder; return CardX( child: ExpandTile( @@ -830,20 +836,21 @@ class _ServerDetailPageState extends State ); } - Widget _buildPve(_) { - final addr = widget.spi.custom?.pveAddr; - if (addr == null) return UIs.placeholder; + Widget _buildPve(Server si) { + final addr = si.spi.custom?.pveAddr; + if (addr == null || addr.isEmpty) return UIs.placeholder; return CardX( child: ListTile( title: const Text('PVE'), leading: const Icon(FontAwesome.server_solid, size: 17), trailing: const Icon(Icons.chevron_right), - onTap: () => AppRoutes.pve(spi: widget.spi).go(context), + onTap: () => AppRoutes.pve(spi: si.spi).go(context), ), ); } - Widget _buildCustom(ServerStatus ss) { + Widget _buildCustom(Server si) { + final ss = si.status; if (ss.customCmds.isEmpty) return UIs.placeholder; return CardX( child: ExpandTile( diff --git a/lib/view/page/server/edit.dart b/lib/view/page/server/edit.dart index 9fa06397..1c4a68fc 100644 --- a/lib/view/page/server/edit.dart +++ b/lib/view/page/server/edit.dart @@ -3,11 +3,10 @@ import 'dart:convert'; import 'package:fl_lib/fl_lib.dart'; import 'package:flutter/material.dart'; import 'package:icons_plus/icons_plus.dart'; -import 'package:provider/provider.dart'; import 'package:server_box/core/extension/context/locale.dart'; import 'package:server_box/data/model/server/custom.dart'; import 'package:server_box/data/model/server/wol_cfg.dart'; -import 'package:server_box/data/res/provider.dart'; +import 'package:server_box/data/provider/server.dart'; import 'package:server_box/core/route.dart'; import 'package:server_box/data/model/server/server_private_info.dart'; @@ -16,7 +15,7 @@ import 'package:server_box/data/provider/private_key.dart'; class ServerEditPage extends StatefulWidget { const ServerEditPage({super.key, this.spi}); - final ServerPrivateInfo? spi; + final Spi? spi; @override State createState() => _ServerEditPageState(); @@ -148,7 +147,7 @@ class _ServerEditPageState extends State with AfterLayoutMixin { hint: 'root', suggestion: false, ), - TagTile(tags: _tags, allTags: Pros.server.tags.value).cardx, + TagTile(tags: _tags, allTags: ServerProvider.tags.value).cardx, ListTile( title: Text(l10n.autoConnect), trailing: ListenableBuilder( @@ -219,10 +218,10 @@ class _ServerEditPageState extends State with AfterLayoutMixin { Widget _buildKeyAuth() { const padding = EdgeInsets.only(left: 23, right: 13); - return Consumer( - builder: (_, key, __) { - final tiles = List.generate(key.pkis.length, (index) { - final e = key.pkis[index]; + return PrivateKeyProvider.pkis.listenVal( + (pkis) { + final tiles = List.generate(pkis.length, (index) { + final e = pkis[index]; return ListTile( contentPadding: padding, leading: Text( @@ -289,7 +288,6 @@ class _ServerEditPageState extends State with AfterLayoutMixin { return ExpandTile( title: Text(l10n.more), children: [ - UIs.height7, Input( controller: _logoUrlCtrl, type: TextInputType.url, @@ -300,13 +298,9 @@ class _ServerEditPageState extends State with AfterLayoutMixin { ), _buildAltUrl(), _buildEnvs(), - UIs.height7, ..._buildPVEs(), - UIs.height7, ..._buildCustomCmds(), - UIs.height7, - Text(l10n.temperature, style: UIs.text13Grey), - UIs.height7, + CenterGreyTitle(l10n.temperature), Input( controller: _preferTempDevCtrl, type: TextInputType.text, @@ -336,26 +330,14 @@ class _ServerEditPageState extends State with AfterLayoutMixin { List _buildPVEs() { const addr = 'https://127.0.0.1:8006'; return [ - const Text('PVE', style: UIs.text13Grey), - UIs.height7, - Autocomplete( - optionsBuilder: (val) { - final v = val.text; - if (v.startsWith(addr.substring(0, v.length))) { - return [addr]; - } - return []; - }, - onSelected: (val) => _pveAddrCtrl.text = val, - fieldViewBuilder: (_, ctrl, node, __) => Input( - controller: ctrl, - type: TextInputType.url, - icon: MingCute.web_line, - node: node, - label: 'URL', - hint: addr, - suggestion: false, - ), + const CenterGreyTitle('PVE'), + Input( + controller: _pveAddrCtrl, + type: TextInputType.url, + icon: MingCute.web_line, + label: 'URL', + hint: addr, + suggestion: false, ), ListTile( leading: const Icon(MingCute.certificate_line), @@ -376,8 +358,7 @@ class _ServerEditPageState extends State with AfterLayoutMixin { List _buildCustomCmds() { return [ - Text(l10n.customCmd, style: UIs.text13Grey), - UIs.height7, + CenterGreyTitle(l10n.customCmd), _customCmds.listenVal( (vals) { return ListTile( @@ -455,9 +436,10 @@ class _ServerEditPageState extends State with AfterLayoutMixin { return ListenableBuilder( listenable: _jumpServer, builder: (_, __) { - final children = Pros.server.servers - .where((element) => element.spi.jumpId == null) - .where((element) => element.spi.id != widget.spi?.id) + final children = ServerProvider.servers.values + .map((e) => e.value) + .where((e) => e.spi.jumpId == null) + .where((e) => e.spi.id != widget.spi?.id) .map( (e) => ListTile( title: Text(e.spi.name), @@ -552,7 +534,7 @@ class _ServerEditPageState extends State with AfterLayoutMixin { } } - final spi = ServerPrivateInfo( + final spi = Spi( name: _nameController.text.isEmpty ? _ipController.text : _nameController.text, @@ -561,7 +543,7 @@ class _ServerEditPageState extends State with AfterLayoutMixin { user: _usernameController.text, pwd: _passwordController.text.selfIfNotNullEmpty, keyId: _keyIdx.value != null - ? Pros.key.pkis.elementAt(_keyIdx.value!).id + ? PrivateKeyProvider.pkis.value.elementAt(_keyIdx.value!).id : null, tags: _tags.value.isEmpty ? null : _tags.value.toList(), alterUrl: _altUrlController.text.selfIfNotNullEmpty, @@ -573,9 +555,9 @@ class _ServerEditPageState extends State with AfterLayoutMixin { ); if (widget.spi == null) { - Pros.server.addServer(spi); + ServerProvider.addServer(spi); } else { - Pros.server.updateServer(widget.spi!, spi); + ServerProvider.updateServer(widget.spi!, spi); } context.pop(); @@ -589,7 +571,7 @@ class _ServerEditPageState extends State with AfterLayoutMixin { } } - void _initWithSpi(ServerPrivateInfo spi) { + void _initWithSpi(Spi spi) { _nameController.text = spi.name; _ipController.text = spi.ip; _portController.text = spi.port.toString(); @@ -597,7 +579,7 @@ class _ServerEditPageState extends State with AfterLayoutMixin { if (spi.keyId == null) { _passwordController.text = spi.pwd ?? ''; } else { - _keyIdx.value = Pros.key.pkis.indexWhere( + _keyIdx.value = PrivateKeyProvider.pkis.value.indexWhere( (e) => e.id == widget.spi!.keyId, ); } @@ -656,7 +638,7 @@ class _ServerEditPageState extends State with AfterLayoutMixin { final code = codes?.firstOrNull?.rawValue; if (code == null) return; try { - final spi = ServerPrivateInfo.fromJson(json.decode(code)); + final spi = Spi.fromJson(json.decode(code)); _initWithSpi(spi); } catch (e, s) { context.showErrDialog(e, s); @@ -680,7 +662,7 @@ class _ServerEditPageState extends State with AfterLayoutMixin { actions: Btn.ok( onTap: () async { context.pop(); - Pros.server.delServer(widget.spi!.id); + ServerProvider.delServer(widget.spi!.id); context.pop(true); }, red: true, diff --git a/lib/view/page/server/tab.dart b/lib/view/page/server/tab.dart index 4deab018..eab5f0a8 100644 --- a/lib/view/page/server/tab.dart +++ b/lib/view/page/server/tab.dart @@ -4,12 +4,10 @@ import 'dart:math' as math; import 'package:fl_lib/fl_lib.dart'; import 'package:flutter/material.dart'; import 'package:icons_plus/icons_plus.dart'; -import 'package:provider/provider.dart'; import 'package:server_box/core/extension/context/locale.dart'; import 'package:server_box/core/extension/ssh_client.dart'; import 'package:server_box/data/model/app/shell_func.dart'; import 'package:server_box/data/model/server/try_limiter.dart'; -import 'package:server_box/data/res/provider.dart'; import 'package:server_box/data/res/store.dart'; import 'package:server_box/view/widget/percent_circle.dart'; @@ -86,7 +84,7 @@ class _ServerPageState extends State Widget _buildPortrait() { return Scaffold( - appBar: _buildTagsSwitcher(Pros.server), + appBar: _buildTagsSwitcher(), body: GestureDetector( behavior: HitTestBehavior.opaque, onTap: () => _autoHideKey.currentState?.show(), @@ -138,74 +136,74 @@ class _ServerPageState extends State } Widget _buildLandscapeBody() { - return Consumer(builder: (_, pro, __) { - if (pro.serverOrder.isEmpty) { + return ServerProvider.serverOrder.listenVal((order) { + if (order.isEmpty) { return Center( child: Text(libL10n.empty, textAlign: TextAlign.center), ); } return PageView.builder( - itemCount: pro.serverOrder.length, + itemCount: order.length, itemBuilder: (_, idx) { - final id = pro.serverOrder[idx]; - final srv = pro.pick(id: id); + final id = order[idx]; + final srv = ServerProvider.pick(id: id); if (srv == null) return UIs.placeholder; - final title = _buildServerCardTitle(srv); - final List children = [ - title, - ..._buildNormalCard(srv.status, srv.spi).joinWith(SizedBox( - height: _media.size.height / 10, - )) - ]; + return srv.listenVal((srv) { + final title = _buildServerCardTitle(srv); + final List children = [ + title, + ..._buildNormalCard(srv.status, srv.spi).joinWith(SizedBox( + height: _media.size.height / 10, + )) + ]; - return Padding( - padding: _media.padding, - child: ListenableBuilder( - listenable: _getCardNoti(id), - builder: (_, __) { - return Column( - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.center, - children: children, - ); - }, - ), - ); + return Padding( + padding: _media.padding, + child: ListenableBuilder( + listenable: _getCardNoti(id), + builder: (_, __) { + return Column( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: children, + ); + }, + ), + ); + }); }, ); }); } Widget _buildBody() { - final child = Consumer( - builder: (_, pro, __) { - if (!pro.tags.value.contains(_tag)) { + return ServerProvider.serverOrder.listenVal( + (order) { + if (!ServerProvider.tags.value.contains(_tag)) { _tag = null; } - if (pro.serverOrder.isEmpty) { + if (order.isEmpty) { return Center( child: Text(libL10n.empty, textAlign: TextAlign.center), ); } - final filtered = _filterServers(pro); + final filtered = _filterServers(order); if (_useDoubleColumn && Stores.setting.doubleColumnServersPage.fetch()) { - return _buildBodyMedium(pro: pro, filtered: filtered); + return _buildBodyMedium(filtered); } - return _buildBodySmall(pro: pro, filtered: filtered); + return _buildBodySmall(filtered: filtered); }, ); - - return child; } - TagSwitcher _buildTagsSwitcher(ServerProvider provider) { + TagSwitcher _buildTagsSwitcher() { return TagSwitcher( - tags: provider.tags, + tags: ServerProvider.tags, width: _media.size.width, onTagChanged: (p0) => setState(() { _tag = p0; @@ -215,7 +213,6 @@ class _ServerPageState extends State } Widget _buildBodySmall({ - required ServerProvider pro, required List filtered, EdgeInsets? padding = const EdgeInsets.fromLTRB(7, 0, 7, 7), }) { @@ -227,15 +224,14 @@ class _ServerPageState extends State itemBuilder: (_, index) { // Issue #130 if (index == count - 1) return UIs.height77; - return _buildEachServerCard(pro.pick(id: filtered[index])); + final vnode = ServerProvider.pick(id: filtered[index]); + if (vnode == null) return UIs.placeholder; + return vnode.listenVal(_buildEachServerCard); }, ); } - Widget _buildBodyMedium({ - required ServerProvider pro, - required List filtered, - }) { + Widget _buildBodyMedium(List filtered) { final mid = (filtered.length / 2).ceil(); final filteredLeft = filtered.sublist(0, mid); final filteredRight = filtered.sublist(mid); @@ -243,14 +239,12 @@ class _ServerPageState extends State children: [ Expanded( child: _buildBodySmall( - pro: pro, filtered: filteredLeft, padding: const EdgeInsets.only(left: 7), ), ), Expanded( child: _buildBodySmall( - pro: pro, filtered: filteredRight, padding: const EdgeInsets.only(right: 7), ), @@ -416,7 +410,7 @@ class _ServerPageState extends State ]; } - List _buildNormalCard(ServerStatus ss, ServerPrivateInfo spi) { + List _buildNormalCard(ServerStatus ss, Spi spi) { return [ UIs.height13, Row( @@ -491,7 +485,7 @@ class _ServerPageState extends State ), () { TryLimiter.reset(s.spi.id); - Pros.server.refresh(spi: s.spi); + ServerProvider.refresh(spi: s.spi); }, ), ServerConn.disconnected => ( @@ -500,7 +494,7 @@ class _ServerPageState extends State size: 19, color: Colors.grey, ), - () => Pros.server.refresh(spi: s.spi) + () => ServerProvider.refresh(spi: s.spi) ), ServerConn.finished => ( const Icon( @@ -508,7 +502,7 @@ class _ServerPageState extends State size: 17, color: Colors.grey, ), - () => Pros.server.closeServer(id: s.spi.id), + () => ServerProvider.closeServer(id: s.spi.id), ), _ when Stores.setting.serverTabUseOldUI.fetch() => ( ServerFuncBtnsTopRight(spi: s.spi), @@ -653,14 +647,14 @@ ${ss.err?.message ?? 'null'} @override Future afterFirstLayout(BuildContext context) async { - await Pros.server.load(); - Pros.server.startAutoRefresh(); + ServerProvider.refresh(); + ServerProvider.startAutoRefresh(); } - List _filterServers(ServerProvider pro) => pro.serverOrder - .where((e) => pro.serverOrder.contains(e)) + List _filterServers(List order) => order .where((e) => - _tag == null || (pro.pick(id: e)?.spi.tags?.contains(_tag) ?? false)) + _tag == null || + (ServerProvider.pick(id: e)?.value.spi.tags?.contains(_tag) ?? false)) .toList(); static const _kCardHeightMin = 23.0; @@ -747,7 +741,7 @@ class _CardStatus { } extension _ServerX on Server { - String? getTopRightStr(ServerPrivateInfo spi) { + String? getTopRightStr(Spi spi) { switch (conn) { case ServerConn.disconnected: return null; diff --git a/lib/view/page/setting/seq/srv_seq.dart b/lib/view/page/setting/seq/srv_seq.dart index 1c2f2f10..b7a393de 100644 --- a/lib/view/page/setting/seq/srv_seq.dart +++ b/lib/view/page/setting/seq/srv_seq.dart @@ -2,7 +2,7 @@ import 'dart:ui'; import 'package:fl_lib/fl_lib.dart'; import 'package:flutter/material.dart'; import 'package:server_box/core/extension/context/locale.dart'; -import 'package:server_box/data/res/provider.dart'; +import 'package:server_box/data/provider/server.dart'; import 'package:server_box/data/res/store.dart'; class ServerOrderPage extends StatefulWidget { @@ -45,24 +45,28 @@ class _ServerOrderPageState extends State { } Widget _buildBody() { - if (Pros.server.serverOrder.isEmpty) { - return Center(child: Text(libL10n.empty)); - } - return ReorderableListView.builder( - footer: const SizedBox(height: 77), - onReorder: (oldIndex, newIndex) => setState(() { - Pros.server.serverOrder.move( - oldIndex, - newIndex, - property: Stores.setting.serverOrder, - ); - }), - padding: const EdgeInsets.symmetric(horizontal: 7, vertical: 3), - buildDefaultDragHandles: false, - itemBuilder: (_, idx) => _buildItem(idx), - itemCount: Pros.server.serverOrder.length, - proxyDecorator: _proxyDecorator, - ); + final orderNode = ServerProvider.serverOrder; + return orderNode.listenVal((order) { + if (order.isEmpty) { + return Center(child: Text(libL10n.empty)); + } + return ReorderableListView.builder( + footer: const SizedBox(height: 77), + onReorder: (oldIndex, newIndex) => setState(() { + orderNode.value.move( + oldIndex, + newIndex, + property: Stores.setting.serverOrder, + ); + orderNode.notify(); + }), + padding: const EdgeInsets.symmetric(horizontal: 7, vertical: 3), + buildDefaultDragHandles: false, + itemBuilder: (_, idx) => _buildItem(idx), + itemCount: order.length, + proxyDecorator: _proxyDecorator, + ); + }); } Widget _buildItem(int index) { @@ -74,8 +78,8 @@ class _ServerOrderPageState extends State { } Widget _buildCardTile(int index) { - final id = Pros.server.serverOrder[index]; - final spi = Pros.server.pick(id: id)?.spi; + final id = ServerProvider.serverOrder.value[index]; + final spi = ServerProvider.pick(id: id)?.value.spi; if (spi == null) { return const SizedBox(); } diff --git a/lib/view/page/snippet/edit.dart b/lib/view/page/snippet/edit.dart index 984ac182..d5d6e7a3 100644 --- a/lib/view/page/snippet/edit.dart +++ b/lib/view/page/snippet/edit.dart @@ -3,7 +3,8 @@ import 'package:flutter/material.dart'; import 'package:flutter_markdown/flutter_markdown.dart'; import 'package:server_box/core/extension/context/locale.dart'; import 'package:server_box/data/model/server/snippet.dart'; -import 'package:server_box/data/res/provider.dart'; +import 'package:server_box/data/provider/server.dart'; +import 'package:server_box/data/provider/snippet.dart'; class SnippetEditPage extends StatefulWidget { const SnippetEditPage({super.key, this.snippet}); @@ -55,7 +56,7 @@ class _SnippetEditPageState extends State )), actions: Btn.ok( onTap: () { - Pros.snippet.del(widget.snippet!); + SnippetProvider.del(widget.snippet!); context.pop(); context.pop(); }, @@ -89,9 +90,9 @@ class _SnippetEditPageState extends State autoRunOn: _autoRunOn.value.isEmpty ? null : _autoRunOn.value, ); if (widget.snippet != null) { - Pros.snippet.update(widget.snippet!, snippet); + SnippetProvider.update(widget.snippet!, snippet); } else { - Pros.snippet.add(snippet); + SnippetProvider.add(snippet); } context.pop(); }, @@ -120,7 +121,7 @@ class _SnippetEditPageState extends State icon: Icons.note, suggestion: true, ), - TagTile(tags: _tags, allTags: Pros.snippet.tags.value).cardx, + TagTile(tags: _tags, allTags: SnippetProvider.tags.value).cardx, Input( controller: _scriptController, node: _scriptNode, @@ -145,7 +146,7 @@ class _SnippetEditPageState extends State final subtitle = vals.isEmpty ? null : vals - .map((e) => Pros.server.pick(id: e)?.spi.name ?? e) + .map((e) => ServerProvider.pick(id: e)?.value.spi.name ?? e) .join(', '); return ListTile( leading: const Padding( @@ -163,11 +164,12 @@ class _SnippetEditPageState extends State overflow: TextOverflow.ellipsis, ), onTap: () async { - vals.removeWhere((e) => !Pros.server.serverOrder.contains(e)); + vals.removeWhere( + (e) => !ServerProvider.serverOrder.value.contains(e)); final serverIds = await context.showPickDialog( title: l10n.autoRun, - items: Pros.server.serverOrder, - name: (e) => Pros.server.pick(id: e)?.spi.name ?? e, + items: ServerProvider.serverOrder.value, + name: (e) => ServerProvider.pick(id: e)?.value.spi.name ?? e, initial: vals, clearable: true, ); diff --git a/lib/view/page/snippet/list.dart b/lib/view/page/snippet/list.dart index 94fcbae0..b2e7d753 100644 --- a/lib/view/page/snippet/list.dart +++ b/lib/view/page/snippet/list.dart @@ -1,6 +1,5 @@ import 'package:fl_lib/fl_lib.dart'; import 'package:flutter/material.dart'; -import 'package:provider/provider.dart'; import 'package:server_box/data/res/store.dart'; import 'package:server_box/data/model/server/snippet.dart'; @@ -38,13 +37,13 @@ class _SnippetListPageState extends State { } Widget _buildBody() { - return Consumer( - builder: (_, provider, __) { - if (provider.snippets.isEmpty) { + return SnippetProvider.snippets.listenVal( + (snippets) { + if (snippets.isEmpty) { return Center(child: Text(libL10n.empty)); } - final filtered = provider.snippets + final filtered = snippets .where((e) => _tag == null || (e.tags?.contains(_tag) ?? false)) .toList(); @@ -52,7 +51,7 @@ class _SnippetListPageState extends State { padding: const EdgeInsets.symmetric(horizontal: 11), itemCount: filtered.length, onReorder: (oldIdx, newIdx) => setState(() { - provider.snippets.moveByItem( + snippets.moveByItem( oldIdx, newIdx, filtered: filtered, @@ -62,7 +61,7 @@ class _SnippetListPageState extends State { ); }), header: TagSwitcher( - tags: provider.tags, + tags: SnippetProvider.tags, onTagChanged: (tag) => setState(() => _tag = tag), initTag: _tag, width: _media.size.width, diff --git a/lib/view/page/ssh/page.dart b/lib/view/page/ssh/page.dart index 2ec755eb..3a9cc920 100644 --- a/lib/view/page/ssh/page.dart +++ b/lib/view/page/ssh/page.dart @@ -11,8 +11,8 @@ import 'package:server_box/core/extension/context/locale.dart'; import 'package:server_box/core/utils/ssh_auth.dart'; import 'package:server_box/core/utils/server.dart'; import 'package:server_box/data/model/server/snippet.dart'; +import 'package:server_box/data/provider/snippet.dart'; import 'package:server_box/data/provider/virtual_keyboard.dart'; -import 'package:server_box/data/res/provider.dart'; import 'package:server_box/data/res/store.dart'; import 'package:wakelock_plus/wakelock_plus.dart'; import 'package:xterm/core.dart'; @@ -26,7 +26,7 @@ import 'package:server_box/data/res/terminal.dart'; const _echoPWD = 'echo \$PWD'; class SSHPage extends StatefulWidget { - final ServerPrivateInfo spi; + final Spi spi; final String? initCmd; final Snippet? initSnippet; final bool notFromTab; @@ -68,7 +68,7 @@ class SSHPageState extends State bool _isDark = false; Timer? _virtKeyLongPressTimer; - late SSHClient? _client = widget.spi.server?.client; + late SSHClient? _client = widget.spi.server?.value.client; Timer? _discontinuityTimer; @override @@ -298,10 +298,10 @@ class SSHPageState extends State case VirtualKeyFunc.snippet: final snippets = await context.showPickWithTagDialog( title: l10n.snippet, - tags: Pros.snippet.tags, + tags: SnippetProvider.tags, itemsBuilder: (e) { - if (e == null) return Pros.snippet.snippets; - return Pros.snippet.snippets + if (e == null) return SnippetProvider.snippets.value; + return SnippetProvider.snippets.value .where((element) => element.tags?.contains(e) ?? false) .toList(); }, @@ -417,7 +417,7 @@ class SSHPageState extends State _initService(); - for (final snippet in Pros.snippet.snippets) { + for (final snippet in SnippetProvider.snippets.value) { if (snippet.autoRunOn?.contains(widget.spi.id) == true) { snippet.runInTerm(_terminal, widget.spi); } diff --git a/lib/view/page/ssh/tab.dart b/lib/view/page/ssh/tab.dart index 0f2bcec0..607b321d 100644 --- a/lib/view/page/ssh/tab.dart +++ b/lib/view/page/ssh/tab.dart @@ -2,12 +2,10 @@ import 'package:fl_lib/fl_lib.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:icons_plus/icons_plus.dart'; -import 'package:provider/provider.dart'; import 'package:server_box/core/extension/context/locale.dart'; import 'package:server_box/core/route.dart'; import 'package:server_box/data/model/server/server_private_info.dart'; import 'package:server_box/data/provider/server.dart'; -import 'package:server_box/data/res/provider.dart'; import 'package:server_box/view/page/ssh/page.dart'; class SSHTabPage extends StatefulWidget { @@ -86,8 +84,8 @@ class _SSHTabPageState extends State Widget _buildAddPage() { return Center( key: const Key('sshTabAddServer'), - child: Consumer(builder: (_, pro, __) { - if (pro.serverOrder.isEmpty) { + child: ServerProvider.serverOrder.listenVal((order) { + if (order.isEmpty) { return Center( child: Text(libL10n.empty, textAlign: TextAlign.center), ); @@ -98,7 +96,7 @@ class _SSHTabPageState extends State padding: const EdgeInsets.all(7), cacheExtent: 50, itemBuilder: (context, idx) { - final spi = Pros.server.pick(id: pro.serverOrder[idx])?.spi; + final spi = ServerProvider.pick(id: order[idx])?.value.spi; if (spi == null) return UIs.placeholder; return CardX( child: InkWell( @@ -117,7 +115,7 @@ class _SSHTabPageState extends State ), ); }, - itemCount: pro.servers.length, + itemCount: ServerProvider.servers.length, gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: 2, childAspectRatio: 3 * (ratio / (9 / 16)), @@ -147,7 +145,7 @@ class _SSHTabPageState extends State ); } - void _onTapInitCard(ServerPrivateInfo spi) async { + void _onTapInitCard(Spi spi) async { final name = () { final reg = RegExp('${spi.name}\\((\\d+)\\)'); final idxs = _tabMap.keys diff --git a/lib/view/page/storage/local.dart b/lib/view/page/storage/local.dart index 74d6a256..e73d320f 100644 --- a/lib/view/page/storage/local.dart +++ b/lib/view/page/storage/local.dart @@ -5,8 +5,9 @@ import 'package:flutter/material.dart'; import 'package:server_box/core/extension/context/locale.dart'; import 'package:server_box/data/model/server/server_private_info.dart'; import 'package:server_box/data/model/sftp/worker.dart'; +import 'package:server_box/data/provider/server.dart'; +import 'package:server_box/data/provider/sftp.dart'; import 'package:server_box/data/res/misc.dart'; -import 'package:server_box/data/res/provider.dart'; import 'package:server_box/view/widget/omit_start_text.dart'; import 'package:server_box/core/route.dart'; @@ -282,10 +283,10 @@ class _LocalStoragePageState extends State { onTap: () async { context.pop(); - final spi = await context.showPickSingleDialog( + final spi = await context.showPickSingleDialog( title: libL10n.select, - items: Pros.server.serverOrder - .map((e) => Pros.server.pick(id: e)?.spi) + items: ServerProvider.serverOrder.value + .map((e) => ServerProvider.pick(id: e)?.value.spi) .toList(), name: (e) => e.name, ); @@ -299,7 +300,7 @@ class _LocalStoragePageState extends State { return; } - Pros.sftp.add(SftpReq( + SftpProvider.add(SftpReq( spi, '$remotePath/$fileName', file.absolute.path, diff --git a/lib/view/page/storage/sftp.dart b/lib/view/page/storage/sftp.dart index fc1bc6dd..df548ce2 100644 --- a/lib/view/page/storage/sftp.dart +++ b/lib/view/page/storage/sftp.dart @@ -11,8 +11,8 @@ import 'package:server_box/core/utils/comparator.dart'; import 'package:server_box/data/model/server/server_private_info.dart'; import 'package:server_box/data/model/sftp/browser_status.dart'; import 'package:server_box/data/model/sftp/worker.dart'; +import 'package:server_box/data/provider/sftp.dart'; import 'package:server_box/data/res/misc.dart'; -import 'package:server_box/data/res/provider.dart'; import 'package:server_box/data/res/store.dart'; import 'package:server_box/view/widget/omit_start_text.dart'; import 'package:server_box/view/widget/two_line_text.dart'; @@ -21,7 +21,7 @@ import 'package:server_box/view/widget/unix_perm.dart'; import 'package:icons_plus/icons_plus.dart'; class SftpPage extends StatefulWidget { - final ServerPrivateInfo spi; + final Spi spi; final String? initPath; final bool isSelect; @@ -38,7 +38,7 @@ class SftpPage extends StatefulWidget { class _SftpPageState extends State with AfterLayoutMixin { late final _status = SftpBrowserStatus(_client); - late final _client = widget.spi.server!.client!; + late final _client = widget.spi.server!.value.client!; final _sortOption = _SortOption().vn; @override @@ -303,7 +303,7 @@ class _SftpPageState extends State with AfterLayoutMixin { localPath, SftpReqType.download, ); - Pros.sftp.add(req, completer: completer); + SftpProvider.add(req, completer: completer); final (suc, err) = await context.showLoadingDialog( fn: () => completer.future, ); @@ -311,7 +311,7 @@ class _SftpPageState extends State with AfterLayoutMixin { final result = await AppRoutes.editor(path: localPath).go(context); if (result != null && result) { - Pros.sftp.add(SftpReq( + SftpProvider.add(SftpReq( req.spi, remotePath, localPath, @@ -335,7 +335,7 @@ class _SftpPageState extends State with AfterLayoutMixin { context.pop(); final remotePath = _getRemotePath(name); - Pros.sftp.add( + SftpProvider.add( SftpReq( widget.spi, remotePath, @@ -659,12 +659,13 @@ class _SftpPageState extends State with AfterLayoutMixin { } showSearch( - context: context, - delegate: SearchPage( - padding: const EdgeInsets.symmetric(horizontal: 7, vertical: 3), - future: (q) => find(q).toList(), - builder: (ctx, e) => _buildItem(e, beforeTap: () => ctx.pop()), - )); + context: context, + delegate: SearchPage( + padding: const EdgeInsets.symmetric(horizontal: 7, vertical: 3), + future: (q) => find(q).toList(), + builder: (ctx, e) => _buildItem(e, beforeTap: ctx.pop), + ), + ); }, icon: const Icon(Icons.search), ); @@ -701,7 +702,7 @@ class _SftpPageState extends State with AfterLayoutMixin { final fileName = path.split(Platform.pathSeparator).lastOrNull; final remotePath = '$remoteDir/$fileName'; Loggers.app.info('SFTP upload local: $path, remote: $remotePath'); - Pros.sftp.add( + SftpProvider.add( SftpReq(widget.spi, remotePath, path, SftpReqType.upload), ); }, diff --git a/lib/view/page/storage/sftp_mission.dart b/lib/view/page/storage/sftp_mission.dart index 360ed25d..e1d40830 100644 --- a/lib/view/page/storage/sftp_mission.dart +++ b/lib/view/page/storage/sftp_mission.dart @@ -1,11 +1,9 @@ import 'package:fl_lib/fl_lib.dart'; import 'package:flutter/material.dart'; -import 'package:provider/provider.dart'; import 'package:server_box/core/extension/context/locale.dart'; import 'package:server_box/core/route.dart'; import 'package:server_box/data/model/sftp/worker.dart'; import 'package:server_box/data/provider/sftp.dart'; -import 'package:server_box/data/res/provider.dart'; class SftpMissionPage extends StatefulWidget { const SftpMissionPage({super.key}); @@ -26,16 +24,15 @@ class _SftpMissionPageState extends State { } Widget _buildBody() { - return Consumer(builder: (__, pro, _) { - if (pro.status.isEmpty) { + return SftpProvider.status.listenVal((status) { + if (status.isEmpty) { return Center(child: Text(libL10n.empty)); } return ListView.builder( padding: const EdgeInsets.all(11), - itemCount: pro.status.length, + itemCount: status.length, itemBuilder: (context, index) { - final status = pro.status[index]; - return _buildItem(status); + return _buildItem(status[index]); }, ); }); @@ -165,7 +162,7 @@ class _SftpMissionPageState extends State { )), actions: Btn.ok( onTap: () { - Pros.sftp.cancel(id); + SftpProvider.cancel(id); context.pop(); }, ).toList, diff --git a/lib/view/page/systemd.dart b/lib/view/page/systemd.dart index 2a47f95e..bf850ff1 100644 --- a/lib/view/page/systemd.dart +++ b/lib/view/page/systemd.dart @@ -6,7 +6,7 @@ import 'package:server_box/data/model/server/systemd.dart'; import 'package:server_box/data/provider/systemd.dart'; final class SystemdPageArgs { - final ServerPrivateInfo spi; + final Spi spi; const SystemdPageArgs({ required this.spi, @@ -102,7 +102,7 @@ final class _SystemdPageState extends State { return PopupMenu( items: unit.availableFuncs.map(_buildUnitFuncBtn).toList(), onSelected: (val) async { - final cmd = unit.getCmd(func: val, isRoot: _pro.isRoot); + final cmd = unit.getCmd(func: val, isRoot: widget.args.spi.isRoot); final sure = await context.showRoundDialog( title: libL10n.attention, child: SimpleMarkdown(data: '```shell\n$cmd\n```'), diff --git a/lib/view/widget/server_func_btns.dart b/lib/view/widget/server_func_btns.dart index b4238ea5..22cf7626 100644 --- a/lib/view/widget/server_func_btns.dart +++ b/lib/view/widget/server_func_btns.dart @@ -6,7 +6,8 @@ import 'package:server_box/core/extension/context/locale.dart'; import 'package:server_box/data/model/app/menu/base.dart'; import 'package:server_box/data/model/app/menu/server_func.dart'; import 'package:server_box/data/model/server/snippet.dart'; -import 'package:server_box/data/res/provider.dart'; +import 'package:server_box/data/provider/server.dart'; +import 'package:server_box/data/provider/snippet.dart'; import 'package:server_box/data/res/store.dart'; import 'package:server_box/view/page/systemd.dart'; @@ -15,7 +16,7 @@ import 'package:server_box/core/utils/server.dart'; import 'package:server_box/data/model/server/server_private_info.dart'; class ServerFuncBtnsTopRight extends StatelessWidget { - final ServerPrivateInfo spi; + final Spi spi; const ServerFuncBtnsTopRight({ super.key, @@ -40,7 +41,7 @@ class ServerFuncBtns extends StatelessWidget { required this.spi, }); - final ServerPrivateInfo spi; + final Spi spi; @override Widget build(BuildContext context) { @@ -90,7 +91,7 @@ class ServerFuncBtns extends StatelessWidget { void _onTapMoreBtns( ServerFuncBtn value, - ServerPrivateInfo spi, + Spi spi, BuildContext context, ) async { switch (value) { @@ -104,16 +105,16 @@ void _onTapMoreBtns( ); break; case ServerFuncBtn.snippet: - if (Pros.snippet.snippets.isEmpty) { + if (SnippetProvider.snippets.value.isEmpty) { context.showSnackBar(libL10n.empty); return; } final snippets = await context.showPickWithTagDialog( title: l10n.snippet, - tags: Pros.snippet.tags, + tags: SnippetProvider.tags, itemsBuilder: (e) { - if (e == null) return Pros.snippet.snippets; - return Pros.snippet.snippets + if (e == null) return SnippetProvider.snippets.value; + return SnippetProvider.snippets.value .where((element) => element.tags?.contains(e) ?? false) .toList(); }, @@ -172,7 +173,7 @@ void _onTapMoreBtns( } } -void _gotoSSH(ServerPrivateInfo spi, BuildContext context) async { +void _gotoSSH(Spi spi, BuildContext context) async { // run built-in ssh on macOS due to incompatibility if (isMobile || isMacOS) { AppRoutes.ssh(spi: spi).go(context); @@ -219,7 +220,7 @@ void _gotoSSH(ServerPrivateInfo spi, BuildContext context) async { } bool _checkClient(BuildContext context, String id) { - final server = Pros.server.pick(id: id); + final server = ServerProvider.pick(id: id)?.value; if (server == null || server.client == null) { context.showSnackBar(l10n.waitConnection); return false; diff --git a/pubspec.lock b/pubspec.lock index 83eea9af..46232ca6 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -400,14 +400,6 @@ packages: description: flutter source: sdk version: "0.0.0" - flutter_adaptive_scaffold: - dependency: "direct main" - description: - name: flutter_adaptive_scaffold - sha256: a464b74540401cade07af0ae84d19f210534cac67651a150fb413507040b74f6 - url: "https://pub.dev" - source: hosted - version: "0.1.12" flutter_background_service: dependency: "direct main" description: @@ -1049,7 +1041,7 @@ packages: source: hosted version: "3.3.0" provider: - dependency: "direct main" + dependency: transitive description: name: provider sha256: c8a055ee5ce3fd98d6fc872478b03823ffdb448699c6ebdbbc71d59b596fd48c diff --git a/pubspec.yaml b/pubspec.yaml index abe1ade9..10030c1c 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -11,7 +11,6 @@ dependencies: sdk: flutter flutter_localizations: sdk: flutter - provider: ^6.0.0 hive_flutter: ^1.1.0 dio: ^5.2.1 easy_isolate: ^1.3.0 @@ -27,7 +26,6 @@ dependencies: fl_chart: ^0.67.0 wakelock_plus: ^1.2.4 wake_on_lan: ^4.1.1+3 - flutter_adaptive_scaffold: ^0.1.10+2 extended_image: ^8.2.1 json_annotation: ^4.9.0 dartssh2: