This commit is contained in:
lollipopkit🏳️‍⚧️
2025-08-31 23:59:53 +08:00
parent 5291d316a2
commit 3b7fdf36fb
32 changed files with 726 additions and 687 deletions

View File

@@ -2,7 +2,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:server_box/data/provider/app.dart';
import 'package:server_box/data/provider/private_key.dart';
import 'package:server_box/data/provider/server.dart';
import 'package:server_box/data/provider/server/all.dart';
import 'package:server_box/data/provider/sftp.dart';
import 'package:server_box/data/provider/snippet.dart';
@@ -45,7 +45,7 @@ final class ReadMyProvider {
T call<T>(ProviderBase<T> provider) => ref.read(provider);
// Specific provider getters
ServersState get server => ref.read(serverNotifierProvider);
ServersState get server => ref.read(serversNotifierProvider);
SnippetState get snippet => ref.read(snippetNotifierProvider);
AppState get app => ref.read(appStatesProvider);
PrivateKeyState get privateKey => ref.read(privateKeyNotifierProvider);
@@ -59,7 +59,7 @@ final class WatchMyProvider {
T call<T>(ProviderBase<T> provider) => ref.watch(provider);
// Specific provider getters
ServersState get server => ref.watch(serverNotifierProvider);
ServersState get server => ref.watch(serversNotifierProvider);
SnippetState get snippet => ref.watch(snippetNotifierProvider);
AppState get app => ref.watch(appStatesProvider);
PrivateKeyState get privateKey => ref.watch(privateKeyNotifierProvider);
@@ -74,7 +74,7 @@ final class UseNotifierMyProvider {
ref.read(provider.notifier);
// Specific provider notifier getters
ServerNotifier get server => ref.read(serverNotifierProvider.notifier);
ServersNotifier get server => ref.read(serversNotifierProvider.notifier);
SnippetNotifier get snippet => ref.read(snippetNotifierProvider.notifier);
AppStates get app => ref.read(appStatesProvider.notifier);
PrivateKeyNotifier get privateKey => ref.read(privateKeyNotifierProvider.notifier);

View File

@@ -13,7 +13,7 @@ import 'package:server_box/core/extension/context/locale.dart';
import 'package:server_box/data/model/app/error.dart';
import 'package:server_box/data/model/server/pve.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/provider/server/single.dart';
part 'pve.freezed.dart';
part 'pve.g.dart';
@@ -45,7 +45,7 @@ class PveNotifier extends _$PveNotifier {
@override
PveState build(Spi spiParam) {
spi = spiParam;
final serverState = ref.watch(individualServerNotifierProvider(spi.id));
final serverState = ref.watch(serverNotifierProvider(spi.id));
final client = serverState.client;
if (client == null) {
return const PveState(error: PveErr(type: PveErrType.net, message: 'Server client is null'));

View File

@@ -6,7 +6,7 @@ part of 'pve.dart';
// RiverpodGenerator
// **************************************************************************
String _$pveNotifierHash() => r'667cfb11cd7118d57b29918d137ef2cda2bad7ad';
String _$pveNotifierHash() => r'b5da7240db1b9ee7d61f238cebca45821b7a3445';
/// Copied from Dart SDK
class _SystemHash {

View File

@@ -0,0 +1,273 @@
import 'dart:async';
import 'package:fl_lib/fl_lib.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:server_box/core/sync.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/try_limiter.dart';
import 'package:server_box/data/provider/server/single.dart';
import 'package:server_box/data/res/store.dart';
import 'package:server_box/data/ssh/session_manager.dart';
part 'all.freezed.dart';
part 'all.g.dart';
@freezed
abstract class ServersState with _$ServersState {
const factory ServersState({
@Default({}) Map<String, Spi> servers,
@Default([]) List<String> serverOrder,
@Default(<String>{}) Set<String> tags,
@Default(<String>{}) Set<String> manualDisconnectedIds,
Timer? autoRefreshTimer,
}) = _ServersState;
}
@Riverpod(keepAlive: true)
class ServersNotifier extends _$ServersNotifier {
@override
ServersState build() {
// Initialize with empty state, load data asynchronously
Future.microtask(() => _load());
return const ServersState();
}
Future<void> _load() async {
final spis = Stores.server.fetch();
final newServers = <String, Spi>{};
final newServerOrder = <String>[];
for (final spi in spis) {
newServers[spi.id] = spi;
}
final serverOrder_ = Stores.setting.serverOrder.fetch();
if (serverOrder_.isNotEmpty) {
spis.reorder(order: serverOrder_, finder: (n, id) => n.id == id);
newServerOrder.addAll(spis.map((e) => e.id));
} else {
newServerOrder.addAll(newServers.keys);
}
// Must use [equals] to compare [Order] here.
if (!newServerOrder.equals(serverOrder_)) {
Stores.setting.serverOrder.put(newServerOrder);
}
final newTags = _calculateTags(newServers);
state = state.copyWith(servers: newServers, serverOrder: newServerOrder, tags: newTags);
}
Set<String> _calculateTags(Map<String, Spi> servers) {
final tags = <String>{};
for (final spi in servers.values) {
final spiTags = spi.tags;
if (spiTags == null) continue;
for (final t in spiTags) {
tags.add(t);
}
}
return tags;
}
/// Get a [Spi] by [spi] or [id].
///
/// Priority: [spi] > [id]
Spi? pick({Spi? spi, String? id}) {
if (spi != null) {
return state.servers[spi.id];
}
if (id != null) {
return state.servers[id];
}
return null;
}
/// if [spi] is specificed then only refresh this server
/// [onlyFailed] only refresh failed servers
Future<void> refresh({Spi? spi, bool onlyFailed = false}) async {
if (spi != null) {
final newManualDisconnected = Set<String>.from(state.manualDisconnectedIds)..remove(spi.id);
state = state.copyWith(manualDisconnectedIds: newManualDisconnected);
final serverNotifier = ref.read(serverNotifierProvider(spi.id).notifier);
await serverNotifier.refresh();
return;
}
await Future.wait(
state.servers.entries.map((entry) async {
final serverId = entry.key;
final spi = entry.value;
if (onlyFailed) {
final serverState = ref.read(serverNotifierProvider(serverId));
if (serverState.conn != ServerConn.failed) return;
TryLimiter.reset(serverId);
}
if (state.manualDisconnectedIds.contains(serverId)) return;
final serverState = ref.read(serverNotifierProvider(serverId));
if (serverState.conn == ServerConn.disconnected && !spi.autoConnect) {
return;
}
final serverNotifier = ref.read(serverNotifierProvider(serverId).notifier);
await serverNotifier.refresh();
}),
);
}
Future<void> startAutoRefresh() async {
var duration = Stores.setting.serverStatusUpdateInterval.fetch();
stopAutoRefresh();
if (duration == 0) return;
if (duration < 0 || duration > 10 || duration == 1) {
duration = 3;
Loggers.app.warning('Invalid duration: $duration, use default 3');
}
final timer = Timer.periodic(Duration(seconds: duration), (_) async {
await refresh();
});
state = state.copyWith(autoRefreshTimer: timer);
}
void stopAutoRefresh() {
final timer = state.autoRefreshTimer;
if (timer != null) {
timer.cancel();
state = state.copyWith(autoRefreshTimer: null);
}
}
bool get isAutoRefreshOn => state.autoRefreshTimer != null;
void setDisconnected() {
for (final serverId in state.servers.keys) {
final serverNotifier = ref.read(serverNotifierProvider(serverId).notifier);
serverNotifier.updateConnection(ServerConn.disconnected);
// Update SSH session status to disconnected
final sessionId = 'ssh_$serverId';
TermSessionManager.updateStatus(sessionId, TermSessionStatus.disconnected);
}
//TryLimiter.clear();
}
void closeServer({String? id}) {
if (id == null) {
for (final serverId in state.servers.keys) {
closeOneServer(serverId);
}
return;
}
closeOneServer(id);
}
void closeOneServer(String id) {
final spi = state.servers[id];
if (spi == null) {
Loggers.app.warning('Server with id $id not found');
return;
}
final serverNotifier = ref.read(serverNotifierProvider(id).notifier);
serverNotifier.closeConnection();
final newManualDisconnected = Set<String>.from(state.manualDisconnectedIds)..add(id);
state = state.copyWith(manualDisconnectedIds: newManualDisconnected);
// Remove SSH session when server is manually closed
final sessionId = 'ssh_$id';
TermSessionManager.remove(sessionId);
}
void addServer(Spi spi) {
final newServers = Map<String, Spi>.from(state.servers);
newServers[spi.id] = spi;
final newOrder = List<String>.from(state.serverOrder)..add(spi.id);
final newTags = _calculateTags(newServers);
state = state.copyWith(servers: newServers, serverOrder: newOrder, tags: newTags);
Stores.server.put(spi);
Stores.setting.serverOrder.put(newOrder);
refresh(spi: spi);
bakSync.sync(milliDelay: 1000);
}
void delServer(String id) {
final newServers = Map<String, Spi>.from(state.servers);
newServers.remove(id);
final newOrder = List<String>.from(state.serverOrder)..remove(id);
final newTags = _calculateTags(newServers);
state = state.copyWith(servers: newServers, serverOrder: newOrder, tags: newTags);
Stores.setting.serverOrder.put(newOrder);
Stores.server.delete(id);
// Remove SSH session when server is deleted
final sessionId = 'ssh_$id';
TermSessionManager.remove(sessionId);
bakSync.sync(milliDelay: 1000);
}
void deleteAll() {
// Remove all SSH sessions before clearing servers
for (final id in state.servers.keys) {
final sessionId = 'ssh_$id';
TermSessionManager.remove(sessionId);
}
state = const ServersState();
Stores.setting.serverOrder.put([]);
Stores.server.clear();
bakSync.sync(milliDelay: 1000);
}
Future<void> updateServer(Spi old, Spi newSpi) async {
if (old != newSpi) {
Stores.server.update(old, newSpi);
final newServers = Map<String, Spi>.from(state.servers);
final newOrder = List<String>.from(state.serverOrder);
if (newSpi.id != old.id) {
newServers[newSpi.id] = newSpi;
newServers.remove(old.id);
newOrder.update(old.id, newSpi.id);
Stores.setting.serverOrder.put(newOrder);
// Update SSH session ID when server ID changes
final oldSessionId = 'ssh_${old.id}';
TermSessionManager.remove(oldSessionId);
// Session will be re-added when reconnecting if necessary
} else {
newServers[old.id] = newSpi;
// Update SPI in the corresponding IndividualServerNotifier
final serverNotifier = ref.read(serverNotifierProvider(old.id).notifier);
serverNotifier.updateSpi(newSpi);
}
final newTags = _calculateTags(newServers);
state = state.copyWith(servers: newServers, serverOrder: newOrder, tags: newTags);
// Only reconnect if neccessary
if (newSpi.shouldReconnect(old)) {
// Use [newSpi.id] instead of [old.id] because [old.id] may be changed
TryLimiter.reset(newSpi.id);
refresh(spi: newSpi);
}
}
bakSync.sync(milliDelay: 1000);
}
}

View File

@@ -3,7 +3,7 @@
// ignore_for_file: type=lint
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
part of 'server.dart';
part of 'all.dart';
// **************************************************************************
// FreezedGenerator
@@ -304,291 +304,4 @@ as Timer?,
}
/// @nodoc
mixin _$ServerState {
Spi get spi; ServerStatus get status; ServerConn get conn; SSHClient? get client; Future<void>? get updateFuture;
/// Create a copy of ServerState
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
$ServerStateCopyWith<ServerState> get copyWith => _$ServerStateCopyWithImpl<ServerState>(this as ServerState, _$identity);
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is ServerState&&(identical(other.spi, spi) || other.spi == spi)&&(identical(other.status, status) || other.status == status)&&(identical(other.conn, conn) || other.conn == conn)&&(identical(other.client, client) || other.client == client)&&(identical(other.updateFuture, updateFuture) || other.updateFuture == updateFuture));
}
@override
int get hashCode => Object.hash(runtimeType,spi,status,conn,client,updateFuture);
@override
String toString() {
return 'ServerState(spi: $spi, status: $status, conn: $conn, client: $client, updateFuture: $updateFuture)';
}
}
/// @nodoc
abstract mixin class $ServerStateCopyWith<$Res> {
factory $ServerStateCopyWith(ServerState value, $Res Function(ServerState) _then) = _$ServerStateCopyWithImpl;
@useResult
$Res call({
Spi spi, ServerStatus status, ServerConn conn, SSHClient? client, Future<void>? updateFuture
});
$SpiCopyWith<$Res> get spi;
}
/// @nodoc
class _$ServerStateCopyWithImpl<$Res>
implements $ServerStateCopyWith<$Res> {
_$ServerStateCopyWithImpl(this._self, this._then);
final ServerState _self;
final $Res Function(ServerState) _then;
/// Create a copy of ServerState
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') @override $Res call({Object? spi = null,Object? status = null,Object? conn = null,Object? client = freezed,Object? updateFuture = freezed,}) {
return _then(_self.copyWith(
spi: null == spi ? _self.spi : spi // ignore: cast_nullable_to_non_nullable
as Spi,status: null == status ? _self.status : status // ignore: cast_nullable_to_non_nullable
as ServerStatus,conn: null == conn ? _self.conn : conn // ignore: cast_nullable_to_non_nullable
as ServerConn,client: freezed == client ? _self.client : client // ignore: cast_nullable_to_non_nullable
as SSHClient?,updateFuture: freezed == updateFuture ? _self.updateFuture : updateFuture // ignore: cast_nullable_to_non_nullable
as Future<void>?,
));
}
/// Create a copy of ServerState
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')
$SpiCopyWith<$Res> get spi {
return $SpiCopyWith<$Res>(_self.spi, (value) {
return _then(_self.copyWith(spi: value));
});
}
}
/// Adds pattern-matching-related methods to [ServerState].
extension ServerStatePatterns on ServerState {
/// A variant of `map` that fallback to returning `orElse`.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case final Subclass value:
/// return ...;
/// case _:
/// return orElse();
/// }
/// ```
@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _ServerState value)? $default,{required TResult orElse(),}){
final _that = this;
switch (_that) {
case _ServerState() when $default != null:
return $default(_that);case _:
return orElse();
}
}
/// A `switch`-like method, using callbacks.
///
/// Callbacks receives the raw object, upcasted.
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case final Subclass value:
/// return ...;
/// case final Subclass2 value:
/// return ...;
/// }
/// ```
@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _ServerState value) $default,){
final _that = this;
switch (_that) {
case _ServerState():
return $default(_that);case _:
throw StateError('Unexpected subclass');
}
}
/// A variant of `map` that fallback to returning `null`.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case final Subclass value:
/// return ...;
/// case _:
/// return null;
/// }
/// ```
@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _ServerState value)? $default,){
final _that = this;
switch (_that) {
case _ServerState() when $default != null:
return $default(_that);case _:
return null;
}
}
/// A variant of `when` that fallback to an `orElse` callback.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case _:
/// return orElse();
/// }
/// ```
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( Spi spi, ServerStatus status, ServerConn conn, SSHClient? client, Future<void>? updateFuture)? $default,{required TResult orElse(),}) {final _that = this;
switch (_that) {
case _ServerState() when $default != null:
return $default(_that.spi,_that.status,_that.conn,_that.client,_that.updateFuture);case _:
return orElse();
}
}
/// A `switch`-like method, using callbacks.
///
/// As opposed to `map`, this offers destructuring.
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case Subclass2(:final field2):
/// return ...;
/// }
/// ```
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( Spi spi, ServerStatus status, ServerConn conn, SSHClient? client, Future<void>? updateFuture) $default,) {final _that = this;
switch (_that) {
case _ServerState():
return $default(_that.spi,_that.status,_that.conn,_that.client,_that.updateFuture);case _:
throw StateError('Unexpected subclass');
}
}
/// A variant of `when` that fallback to returning `null`
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case _:
/// return null;
/// }
/// ```
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( Spi spi, ServerStatus status, ServerConn conn, SSHClient? client, Future<void>? updateFuture)? $default,) {final _that = this;
switch (_that) {
case _ServerState() when $default != null:
return $default(_that.spi,_that.status,_that.conn,_that.client,_that.updateFuture);case _:
return null;
}
}
}
/// @nodoc
class _ServerState implements ServerState {
const _ServerState({required this.spi, required this.status, this.conn = ServerConn.disconnected, this.client, this.updateFuture});
@override final Spi spi;
@override final ServerStatus status;
@override@JsonKey() final ServerConn conn;
@override final SSHClient? client;
@override final Future<void>? updateFuture;
/// Create a copy of ServerState
/// with the given fields replaced by the non-null parameter values.
@override @JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
_$ServerStateCopyWith<_ServerState> get copyWith => __$ServerStateCopyWithImpl<_ServerState>(this, _$identity);
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is _ServerState&&(identical(other.spi, spi) || other.spi == spi)&&(identical(other.status, status) || other.status == status)&&(identical(other.conn, conn) || other.conn == conn)&&(identical(other.client, client) || other.client == client)&&(identical(other.updateFuture, updateFuture) || other.updateFuture == updateFuture));
}
@override
int get hashCode => Object.hash(runtimeType,spi,status,conn,client,updateFuture);
@override
String toString() {
return 'ServerState(spi: $spi, status: $status, conn: $conn, client: $client, updateFuture: $updateFuture)';
}
}
/// @nodoc
abstract mixin class _$ServerStateCopyWith<$Res> implements $ServerStateCopyWith<$Res> {
factory _$ServerStateCopyWith(_ServerState value, $Res Function(_ServerState) _then) = __$ServerStateCopyWithImpl;
@override @useResult
$Res call({
Spi spi, ServerStatus status, ServerConn conn, SSHClient? client, Future<void>? updateFuture
});
@override $SpiCopyWith<$Res> get spi;
}
/// @nodoc
class __$ServerStateCopyWithImpl<$Res>
implements _$ServerStateCopyWith<$Res> {
__$ServerStateCopyWithImpl(this._self, this._then);
final _ServerState _self;
final $Res Function(_ServerState) _then;
/// Create a copy of ServerState
/// with the given fields replaced by the non-null parameter values.
@override @pragma('vm:prefer-inline') $Res call({Object? spi = null,Object? status = null,Object? conn = null,Object? client = freezed,Object? updateFuture = freezed,}) {
return _then(_ServerState(
spi: null == spi ? _self.spi : spi // ignore: cast_nullable_to_non_nullable
as Spi,status: null == status ? _self.status : status // ignore: cast_nullable_to_non_nullable
as ServerStatus,conn: null == conn ? _self.conn : conn // ignore: cast_nullable_to_non_nullable
as ServerConn,client: freezed == client ? _self.client : client // ignore: cast_nullable_to_non_nullable
as SSHClient?,updateFuture: freezed == updateFuture ? _self.updateFuture : updateFuture // ignore: cast_nullable_to_non_nullable
as Future<void>?,
));
}
/// Create a copy of ServerState
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')
$SpiCopyWith<$Res> get spi {
return $SpiCopyWith<$Res>(_self.spi, (value) {
return _then(_self.copyWith(spi: value));
});
}
}
// dart format on

View File

@@ -0,0 +1,26 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'all.dart';
// **************************************************************************
// RiverpodGenerator
// **************************************************************************
String _$serversNotifierHash() => r'2ae641188f772794a32e8700c008f51ba0cc1ec9';
/// See also [ServersNotifier].
@ProviderFor(ServersNotifier)
final serversNotifierProvider =
NotifierProvider<ServersNotifier, ServersState>.internal(
ServersNotifier.new,
name: r'serversNotifierProvider',
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
? null
: _$serversNotifierHash,
dependencies: null,
allTransitiveDependencies: null,
);
typedef _$ServersNotifier = Notifier<ServersState>;
// ignore_for_file: type=lint
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package

View File

@@ -8,7 +8,6 @@ import 'package:flutter_gbk2utf8/flutter_gbk2utf8.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:server_box/core/extension/ssh_client.dart';
import 'package:server_box/core/sync.dart';
import 'package:server_box/core/utils/server.dart';
import 'package:server_box/core/utils/ssh_auth.dart';
import 'package:server_box/data/helper/system_detector.dart';
@@ -20,23 +19,13 @@ import 'package:server_box/data/model/server/server_private_info.dart';
import 'package:server_box/data/model/server/server_status_update_req.dart';
import 'package:server_box/data/model/server/system.dart';
import 'package:server_box/data/model/server/try_limiter.dart';
import 'package:server_box/data/provider/server/all.dart';
import 'package:server_box/data/res/status.dart';
import 'package:server_box/data/res/store.dart';
import 'package:server_box/data/ssh/session_manager.dart';
part 'server.freezed.dart';
part 'server.g.dart';
@freezed
abstract class ServersState with _$ServersState {
const factory ServersState({
@Default({}) Map<String, Spi> servers,
@Default([]) List<String> serverOrder,
@Default(<String>{}) Set<String> tags,
@Default(<String>{}) Set<String> manualDisconnectedIds,
Timer? autoRefreshTimer,
}) = _ServersState;
}
part 'single.g.dart';
part 'single.freezed.dart';
// Individual server state, including connection and status information
@freezed
@@ -50,20 +39,12 @@ abstract class ServerState with _$ServerState {
}) = _ServerState;
}
extension IndividualServerStateExtension on ServerState {
bool get needGenClient => conn < ServerConn.connecting;
bool get canViewDetails => conn == ServerConn.finished;
String get id => spi.id;
}
// Individual server state management
@riverpod
class IndividualServerNotifier extends _$IndividualServerNotifier {
class ServerNotifier extends _$ServerNotifier {
@override
ServerState build(String serverId) {
final serverNotifier = ref.read(serverNotifierProvider);
final serverNotifier = ref.read(serversNotifierProvider);
final spi = serverNotifier.servers[serverId];
if (spi == null) {
throw StateError('Server $serverId not found');
@@ -169,7 +150,7 @@ class IndividualServerNotifier extends _$IndividualServerNotifier {
id: sessionId,
spi: spi,
startTimeMs: time1.millisecondsSinceEpoch,
disconnect: () => ref.read(serverNotifierProvider.notifier)._closeOneServer(spi.id),
disconnect: () => ref.read(serversNotifierProvider.notifier).closeOneServer(spi.id),
status: TermSessionStatus.connecting,
);
TermSessionManager.setActive(sessionId, hasTerminal: false);
@@ -371,249 +352,10 @@ class IndividualServerNotifier extends _$IndividualServerNotifier {
}
}
@Riverpod(keepAlive: true)
class ServerNotifier extends _$ServerNotifier {
@override
ServersState build() {
// Initialize with empty state, load data asynchronously
Future.microtask(() => _load());
return const ServersState();
}
extension IndividualServerStateExtension on ServerState {
bool get needGenClient => conn < ServerConn.connecting;
Future<void> _load() async {
final spis = Stores.server.fetch();
final newServers = <String, Spi>{};
final newServerOrder = <String>[];
bool get canViewDetails => conn == ServerConn.finished;
for (final spi in spis) {
newServers[spi.id] = spi;
}
final serverOrder_ = Stores.setting.serverOrder.fetch();
if (serverOrder_.isNotEmpty) {
spis.reorder(order: serverOrder_, finder: (n, id) => n.id == id);
newServerOrder.addAll(spis.map((e) => e.id));
} else {
newServerOrder.addAll(newServers.keys);
}
// Must use [equals] to compare [Order] here.
if (!newServerOrder.equals(serverOrder_)) {
Stores.setting.serverOrder.put(newServerOrder);
}
final newTags = _calculateTags(newServers);
state = state.copyWith(servers: newServers, serverOrder: newServerOrder, tags: newTags);
}
Set<String> _calculateTags(Map<String, Spi> servers) {
final tags = <String>{};
for (final spi in servers.values) {
final spiTags = spi.tags;
if (spiTags == null) continue;
for (final t in spiTags) {
tags.add(t);
}
}
return tags;
}
/// Get a [Spi] by [spi] or [id].
///
/// Priority: [spi] > [id]
Spi? pick({Spi? spi, String? id}) {
if (spi != null) {
return state.servers[spi.id];
}
if (id != null) {
return state.servers[id];
}
return null;
}
/// if [spi] is specificed then only refresh this server
/// [onlyFailed] only refresh failed servers
Future<void> refresh({Spi? spi, bool onlyFailed = false}) async {
if (spi != null) {
final newManualDisconnected = Set<String>.from(state.manualDisconnectedIds)..remove(spi.id);
state = state.copyWith(manualDisconnectedIds: newManualDisconnected);
final serverNotifier = ref.read(individualServerNotifierProvider(spi.id).notifier);
await serverNotifier.refresh();
return;
}
await Future.wait(
state.servers.entries.map((entry) async {
final serverId = entry.key;
final spi = entry.value;
if (onlyFailed) {
final serverState = ref.read(individualServerNotifierProvider(serverId));
if (serverState.conn != ServerConn.failed) return;
TryLimiter.reset(serverId);
}
if (state.manualDisconnectedIds.contains(serverId)) return;
final serverState = ref.read(individualServerNotifierProvider(serverId));
if (serverState.conn == ServerConn.disconnected && !spi.autoConnect) {
return;
}
final serverNotifier = ref.read(individualServerNotifierProvider(serverId).notifier);
await serverNotifier.refresh();
}),
);
}
Future<void> startAutoRefresh() async {
var duration = Stores.setting.serverStatusUpdateInterval.fetch();
stopAutoRefresh();
if (duration == 0) return;
if (duration < 0 || duration > 10 || duration == 1) {
duration = 3;
Loggers.app.warning('Invalid duration: $duration, use default 3');
}
final timer = Timer.periodic(Duration(seconds: duration), (_) async {
await refresh();
});
state = state.copyWith(autoRefreshTimer: timer);
}
void stopAutoRefresh() {
final timer = state.autoRefreshTimer;
if (timer != null) {
timer.cancel();
state = state.copyWith(autoRefreshTimer: null);
}
}
bool get isAutoRefreshOn => state.autoRefreshTimer != null;
void setDisconnected() {
for (final serverId in state.servers.keys) {
final serverNotifier = ref.read(individualServerNotifierProvider(serverId).notifier);
serverNotifier.updateConnection(ServerConn.disconnected);
// Update SSH session status to disconnected
final sessionId = 'ssh_$serverId';
TermSessionManager.updateStatus(sessionId, TermSessionStatus.disconnected);
}
//TryLimiter.clear();
}
void closeServer({String? id}) {
if (id == null) {
for (final serverId in state.servers.keys) {
_closeOneServer(serverId);
}
return;
}
_closeOneServer(id);
}
void _closeOneServer(String id) {
final spi = state.servers[id];
if (spi == null) {
Loggers.app.warning('Server with id $id not found');
return;
}
final serverNotifier = ref.read(individualServerNotifierProvider(id).notifier);
serverNotifier.closeConnection();
final newManualDisconnected = Set<String>.from(state.manualDisconnectedIds)..add(id);
state = state.copyWith(manualDisconnectedIds: newManualDisconnected);
// Remove SSH session when server is manually closed
final sessionId = 'ssh_$id';
TermSessionManager.remove(sessionId);
}
void addServer(Spi spi) {
final newServers = Map<String, Spi>.from(state.servers);
newServers[spi.id] = spi;
final newOrder = List<String>.from(state.serverOrder)..add(spi.id);
final newTags = _calculateTags(newServers);
state = state.copyWith(servers: newServers, serverOrder: newOrder, tags: newTags);
Stores.server.put(spi);
Stores.setting.serverOrder.put(newOrder);
refresh(spi: spi);
bakSync.sync(milliDelay: 1000);
}
void delServer(String id) {
final newServers = Map<String, Spi>.from(state.servers);
newServers.remove(id);
final newOrder = List<String>.from(state.serverOrder)..remove(id);
final newTags = _calculateTags(newServers);
state = state.copyWith(servers: newServers, serverOrder: newOrder, tags: newTags);
Stores.setting.serverOrder.put(newOrder);
Stores.server.delete(id);
// Remove SSH session when server is deleted
final sessionId = 'ssh_$id';
TermSessionManager.remove(sessionId);
bakSync.sync(milliDelay: 1000);
}
void deleteAll() {
// Remove all SSH sessions before clearing servers
for (final id in state.servers.keys) {
final sessionId = 'ssh_$id';
TermSessionManager.remove(sessionId);
}
state = const ServersState();
Stores.setting.serverOrder.put([]);
Stores.server.clear();
bakSync.sync(milliDelay: 1000);
}
Future<void> updateServer(Spi old, Spi newSpi) async {
if (old != newSpi) {
Stores.server.update(old, newSpi);
final newServers = Map<String, Spi>.from(state.servers);
final newOrder = List<String>.from(state.serverOrder);
if (newSpi.id != old.id) {
newServers[newSpi.id] = newSpi;
newServers.remove(old.id);
newOrder.update(old.id, newSpi.id);
Stores.setting.serverOrder.put(newOrder);
// Update SSH session ID when server ID changes
final oldSessionId = 'ssh_${old.id}';
TermSessionManager.remove(oldSessionId);
// Session will be re-added when reconnecting if necessary
} else {
newServers[old.id] = newSpi;
// Update SPI in the corresponding IndividualServerNotifier
final serverNotifier = ref.read(individualServerNotifierProvider(old.id).notifier);
serverNotifier.updateSpi(newSpi);
}
final newTags = _calculateTags(newServers);
state = state.copyWith(servers: newServers, serverOrder: newOrder, tags: newTags);
// Only reconnect if neccessary
if (newSpi.shouldReconnect(old)) {
// Use [newSpi.id] instead of [old.id] because [old.id] may be changed
TryLimiter.reset(newSpi.id);
refresh(spi: newSpi);
}
}
bakSync.sync(milliDelay: 1000);
}
String get id => spi.id;
}

View File

@@ -0,0 +1,301 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
// coverage:ignore-file
// ignore_for_file: type=lint
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
part of 'single.dart';
// **************************************************************************
// FreezedGenerator
// **************************************************************************
// dart format off
T _$identity<T>(T value) => value;
/// @nodoc
mixin _$ServerState {
Spi get spi; ServerStatus get status; ServerConn get conn; SSHClient? get client; Future<void>? get updateFuture;
/// Create a copy of ServerState
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
$ServerStateCopyWith<ServerState> get copyWith => _$ServerStateCopyWithImpl<ServerState>(this as ServerState, _$identity);
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is ServerState&&(identical(other.spi, spi) || other.spi == spi)&&(identical(other.status, status) || other.status == status)&&(identical(other.conn, conn) || other.conn == conn)&&(identical(other.client, client) || other.client == client)&&(identical(other.updateFuture, updateFuture) || other.updateFuture == updateFuture));
}
@override
int get hashCode => Object.hash(runtimeType,spi,status,conn,client,updateFuture);
@override
String toString() {
return 'ServerState(spi: $spi, status: $status, conn: $conn, client: $client, updateFuture: $updateFuture)';
}
}
/// @nodoc
abstract mixin class $ServerStateCopyWith<$Res> {
factory $ServerStateCopyWith(ServerState value, $Res Function(ServerState) _then) = _$ServerStateCopyWithImpl;
@useResult
$Res call({
Spi spi, ServerStatus status, ServerConn conn, SSHClient? client, Future<void>? updateFuture
});
$SpiCopyWith<$Res> get spi;
}
/// @nodoc
class _$ServerStateCopyWithImpl<$Res>
implements $ServerStateCopyWith<$Res> {
_$ServerStateCopyWithImpl(this._self, this._then);
final ServerState _self;
final $Res Function(ServerState) _then;
/// Create a copy of ServerState
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') @override $Res call({Object? spi = null,Object? status = null,Object? conn = null,Object? client = freezed,Object? updateFuture = freezed,}) {
return _then(_self.copyWith(
spi: null == spi ? _self.spi : spi // ignore: cast_nullable_to_non_nullable
as Spi,status: null == status ? _self.status : status // ignore: cast_nullable_to_non_nullable
as ServerStatus,conn: null == conn ? _self.conn : conn // ignore: cast_nullable_to_non_nullable
as ServerConn,client: freezed == client ? _self.client : client // ignore: cast_nullable_to_non_nullable
as SSHClient?,updateFuture: freezed == updateFuture ? _self.updateFuture : updateFuture // ignore: cast_nullable_to_non_nullable
as Future<void>?,
));
}
/// Create a copy of ServerState
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')
$SpiCopyWith<$Res> get spi {
return $SpiCopyWith<$Res>(_self.spi, (value) {
return _then(_self.copyWith(spi: value));
});
}
}
/// Adds pattern-matching-related methods to [ServerState].
extension ServerStatePatterns on ServerState {
/// A variant of `map` that fallback to returning `orElse`.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case final Subclass value:
/// return ...;
/// case _:
/// return orElse();
/// }
/// ```
@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _ServerState value)? $default,{required TResult orElse(),}){
final _that = this;
switch (_that) {
case _ServerState() when $default != null:
return $default(_that);case _:
return orElse();
}
}
/// A `switch`-like method, using callbacks.
///
/// Callbacks receives the raw object, upcasted.
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case final Subclass value:
/// return ...;
/// case final Subclass2 value:
/// return ...;
/// }
/// ```
@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _ServerState value) $default,){
final _that = this;
switch (_that) {
case _ServerState():
return $default(_that);case _:
throw StateError('Unexpected subclass');
}
}
/// A variant of `map` that fallback to returning `null`.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case final Subclass value:
/// return ...;
/// case _:
/// return null;
/// }
/// ```
@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _ServerState value)? $default,){
final _that = this;
switch (_that) {
case _ServerState() when $default != null:
return $default(_that);case _:
return null;
}
}
/// A variant of `when` that fallback to an `orElse` callback.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case _:
/// return orElse();
/// }
/// ```
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( Spi spi, ServerStatus status, ServerConn conn, SSHClient? client, Future<void>? updateFuture)? $default,{required TResult orElse(),}) {final _that = this;
switch (_that) {
case _ServerState() when $default != null:
return $default(_that.spi,_that.status,_that.conn,_that.client,_that.updateFuture);case _:
return orElse();
}
}
/// A `switch`-like method, using callbacks.
///
/// As opposed to `map`, this offers destructuring.
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case Subclass2(:final field2):
/// return ...;
/// }
/// ```
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( Spi spi, ServerStatus status, ServerConn conn, SSHClient? client, Future<void>? updateFuture) $default,) {final _that = this;
switch (_that) {
case _ServerState():
return $default(_that.spi,_that.status,_that.conn,_that.client,_that.updateFuture);case _:
throw StateError('Unexpected subclass');
}
}
/// A variant of `when` that fallback to returning `null`
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case _:
/// return null;
/// }
/// ```
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( Spi spi, ServerStatus status, ServerConn conn, SSHClient? client, Future<void>? updateFuture)? $default,) {final _that = this;
switch (_that) {
case _ServerState() when $default != null:
return $default(_that.spi,_that.status,_that.conn,_that.client,_that.updateFuture);case _:
return null;
}
}
}
/// @nodoc
class _ServerState implements ServerState {
const _ServerState({required this.spi, required this.status, this.conn = ServerConn.disconnected, this.client, this.updateFuture});
@override final Spi spi;
@override final ServerStatus status;
@override@JsonKey() final ServerConn conn;
@override final SSHClient? client;
@override final Future<void>? updateFuture;
/// Create a copy of ServerState
/// with the given fields replaced by the non-null parameter values.
@override @JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
_$ServerStateCopyWith<_ServerState> get copyWith => __$ServerStateCopyWithImpl<_ServerState>(this, _$identity);
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is _ServerState&&(identical(other.spi, spi) || other.spi == spi)&&(identical(other.status, status) || other.status == status)&&(identical(other.conn, conn) || other.conn == conn)&&(identical(other.client, client) || other.client == client)&&(identical(other.updateFuture, updateFuture) || other.updateFuture == updateFuture));
}
@override
int get hashCode => Object.hash(runtimeType,spi,status,conn,client,updateFuture);
@override
String toString() {
return 'ServerState(spi: $spi, status: $status, conn: $conn, client: $client, updateFuture: $updateFuture)';
}
}
/// @nodoc
abstract mixin class _$ServerStateCopyWith<$Res> implements $ServerStateCopyWith<$Res> {
factory _$ServerStateCopyWith(_ServerState value, $Res Function(_ServerState) _then) = __$ServerStateCopyWithImpl;
@override @useResult
$Res call({
Spi spi, ServerStatus status, ServerConn conn, SSHClient? client, Future<void>? updateFuture
});
@override $SpiCopyWith<$Res> get spi;
}
/// @nodoc
class __$ServerStateCopyWithImpl<$Res>
implements _$ServerStateCopyWith<$Res> {
__$ServerStateCopyWithImpl(this._self, this._then);
final _ServerState _self;
final $Res Function(_ServerState) _then;
/// Create a copy of ServerState
/// with the given fields replaced by the non-null parameter values.
@override @pragma('vm:prefer-inline') $Res call({Object? spi = null,Object? status = null,Object? conn = null,Object? client = freezed,Object? updateFuture = freezed,}) {
return _then(_ServerState(
spi: null == spi ? _self.spi : spi // ignore: cast_nullable_to_non_nullable
as Spi,status: null == status ? _self.status : status // ignore: cast_nullable_to_non_nullable
as ServerStatus,conn: null == conn ? _self.conn : conn // ignore: cast_nullable_to_non_nullable
as ServerConn,client: freezed == client ? _self.client : client // ignore: cast_nullable_to_non_nullable
as SSHClient?,updateFuture: freezed == updateFuture ? _self.updateFuture : updateFuture // ignore: cast_nullable_to_non_nullable
as Future<void>?,
));
}
/// Create a copy of ServerState
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')
$SpiCopyWith<$Res> get spi {
return $SpiCopyWith<$Res>(_self.spi, (value) {
return _then(_self.copyWith(spi: value));
});
}
}
// dart format on

View File

@@ -1,13 +1,12 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'server.dart';
part of 'single.dart';
// **************************************************************************
// RiverpodGenerator
// **************************************************************************
String _$individualServerNotifierHash() =>
r'e3d74fb95ca994cd8419b1deab743e8b3e21bee2';
String _$serverNotifierHash() => r'5625b0a4762c28efdbc124809c03b84a51d213b1';
/// Copied from Dart SDK
class _SystemHash {
@@ -30,30 +29,30 @@ class _SystemHash {
}
}
abstract class _$IndividualServerNotifier
abstract class _$ServerNotifier
extends BuildlessAutoDisposeNotifier<ServerState> {
late final String serverId;
ServerState build(String serverId);
}
/// See also [IndividualServerNotifier].
@ProviderFor(IndividualServerNotifier)
const individualServerNotifierProvider = IndividualServerNotifierFamily();
/// See also [ServerNotifier].
@ProviderFor(ServerNotifier)
const serverNotifierProvider = ServerNotifierFamily();
/// See also [IndividualServerNotifier].
class IndividualServerNotifierFamily extends Family<ServerState> {
/// See also [IndividualServerNotifier].
const IndividualServerNotifierFamily();
/// See also [ServerNotifier].
class ServerNotifierFamily extends Family<ServerState> {
/// See also [ServerNotifier].
const ServerNotifierFamily();
/// See also [IndividualServerNotifier].
IndividualServerNotifierProvider call(String serverId) {
return IndividualServerNotifierProvider(serverId);
/// See also [ServerNotifier].
ServerNotifierProvider call(String serverId) {
return ServerNotifierProvider(serverId);
}
@override
IndividualServerNotifierProvider getProviderOverride(
covariant IndividualServerNotifierProvider provider,
ServerNotifierProvider getProviderOverride(
covariant ServerNotifierProvider provider,
) {
return call(provider.serverId);
}
@@ -70,29 +69,28 @@ class IndividualServerNotifierFamily extends Family<ServerState> {
_allTransitiveDependencies;
@override
String? get name => r'individualServerNotifierProvider';
String? get name => r'serverNotifierProvider';
}
/// See also [IndividualServerNotifier].
class IndividualServerNotifierProvider
extends
AutoDisposeNotifierProviderImpl<IndividualServerNotifier, ServerState> {
/// See also [IndividualServerNotifier].
IndividualServerNotifierProvider(String serverId)
/// See also [ServerNotifier].
class ServerNotifierProvider
extends AutoDisposeNotifierProviderImpl<ServerNotifier, ServerState> {
/// See also [ServerNotifier].
ServerNotifierProvider(String serverId)
: this._internal(
() => IndividualServerNotifier()..serverId = serverId,
from: individualServerNotifierProvider,
name: r'individualServerNotifierProvider',
() => ServerNotifier()..serverId = serverId,
from: serverNotifierProvider,
name: r'serverNotifierProvider',
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
? null
: _$individualServerNotifierHash,
dependencies: IndividualServerNotifierFamily._dependencies,
: _$serverNotifierHash,
dependencies: ServerNotifierFamily._dependencies,
allTransitiveDependencies:
IndividualServerNotifierFamily._allTransitiveDependencies,
ServerNotifierFamily._allTransitiveDependencies,
serverId: serverId,
);
IndividualServerNotifierProvider._internal(
ServerNotifierProvider._internal(
super._createNotifier, {
required super.name,
required super.dependencies,
@@ -105,15 +103,15 @@ class IndividualServerNotifierProvider
final String serverId;
@override
ServerState runNotifierBuild(covariant IndividualServerNotifier notifier) {
ServerState runNotifierBuild(covariant ServerNotifier notifier) {
return notifier.build(serverId);
}
@override
Override overrideWith(IndividualServerNotifier Function() create) {
Override overrideWith(ServerNotifier Function() create) {
return ProviderOverride(
origin: this,
override: IndividualServerNotifierProvider._internal(
override: ServerNotifierProvider._internal(
() => create()..serverId = serverId,
from: from,
name: null,
@@ -126,15 +124,14 @@ class IndividualServerNotifierProvider
}
@override
AutoDisposeNotifierProviderElement<IndividualServerNotifier, ServerState>
AutoDisposeNotifierProviderElement<ServerNotifier, ServerState>
createElement() {
return _IndividualServerNotifierProviderElement(this);
return _ServerNotifierProviderElement(this);
}
@override
bool operator ==(Object other) {
return other is IndividualServerNotifierProvider &&
other.serverId == serverId;
return other is ServerNotifierProvider && other.serverId == serverId;
}
@override
@@ -148,40 +145,19 @@ class IndividualServerNotifierProvider
@Deprecated('Will be removed in 3.0. Use Ref instead')
// ignore: unused_element
mixin IndividualServerNotifierRef
on AutoDisposeNotifierProviderRef<ServerState> {
mixin ServerNotifierRef on AutoDisposeNotifierProviderRef<ServerState> {
/// The parameter `serverId` of this provider.
String get serverId;
}
class _IndividualServerNotifierProviderElement
extends
AutoDisposeNotifierProviderElement<
IndividualServerNotifier,
ServerState
>
with IndividualServerNotifierRef {
_IndividualServerNotifierProviderElement(super.provider);
class _ServerNotifierProviderElement
extends AutoDisposeNotifierProviderElement<ServerNotifier, ServerState>
with ServerNotifierRef {
_ServerNotifierProviderElement(super.provider);
@override
String get serverId => (origin as IndividualServerNotifierProvider).serverId;
String get serverId => (origin as ServerNotifierProvider).serverId;
}
String _$serverNotifierHash() => r'8e2bc3aef3c56263f88df3c2bb1ba88b6cf83c8f';
/// See also [ServerNotifier].
@ProviderFor(ServerNotifier)
final serverNotifierProvider =
NotifierProvider<ServerNotifier, ServersState>.internal(
ServerNotifier.new,
name: r'serverNotifierProvider',
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
? null
: _$serverNotifierHash,
dependencies: null,
allTransitiveDependencies: null,
);
typedef _$ServerNotifier = Notifier<ServersState>;
// ignore_for_file: type=lint
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package

View File

@@ -5,7 +5,7 @@ import 'package:server_box/core/extension/ssh_client.dart';
import 'package:server_box/data/model/app/scripts/script_consts.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/provider/server.dart';
import 'package:server_box/data/provider/server/single.dart';
part 'systemd.freezed.dart';
part 'systemd.g.dart';
@@ -25,7 +25,7 @@ class SystemdNotifier extends _$SystemdNotifier {
@override
SystemdState build(Spi spi) {
final si = ref.read(individualServerNotifierProvider(spi.id));
final si = ref.read(serverNotifierProvider(spi.id));
_si = si;
// Async initialization
Future.microtask(() => getUnits());

View File

@@ -6,7 +6,7 @@ part of 'systemd.dart';
// RiverpodGenerator
// **************************************************************************
String _$systemdNotifierHash() => r'617fb7637fbc5c5100e5b522d246984f22b44cca';
String _$systemdNotifierHash() => r'98466bd176518545be49cae52f8dbe12af3a88a6';
/// Copied from Dart SDK
class _SystemHash {