mirror of
https://github.com/lollipopkit/flutter_server_box.git
synced 2025-12-17 07:14:28 +01:00
opt.
This commit is contained in:
@@ -2,7 +2,7 @@ import 'package:fl_lib/fl_lib.dart';
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:server_box/data/model/app/scripts/cmd_types.dart';
|
import 'package:server_box/data/model/app/scripts/cmd_types.dart';
|
||||||
import 'package:server_box/data/model/server/dist.dart';
|
import 'package:server_box/data/model/server/dist.dart';
|
||||||
import 'package:server_box/data/provider/server.dart';
|
import 'package:server_box/data/provider/server/single.dart';
|
||||||
import 'package:server_box/data/res/store.dart';
|
import 'package:server_box/data/res/store.dart';
|
||||||
|
|
||||||
extension LogoExt on ServerState {
|
extension LogoExt on ServerState {
|
||||||
|
|||||||
@@ -104,6 +104,11 @@ Future<ServerStatus> _getLinuxStatus(ServerStatusUpdateReq req) async {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
req.ss.disk = Disk.parse(StatusCmdType.disk.findInMap(parsedOutput));
|
req.ss.disk = Disk.parse(StatusCmdType.disk.findInMap(parsedOutput));
|
||||||
|
} catch (e, s) {
|
||||||
|
Loggers.app.warning(e, s);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
req.ss.diskUsage = DiskUsage.parse(req.ss.disk);
|
req.ss.diskUsage = DiskUsage.parse(req.ss.disk);
|
||||||
} catch (e, s) {
|
} catch (e, s) {
|
||||||
Loggers.app.warning(e, s);
|
Loggers.app.warning(e, s);
|
||||||
|
|||||||
@@ -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/app.dart';
|
||||||
import 'package:server_box/data/provider/private_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/server/all.dart';
|
||||||
import 'package:server_box/data/provider/sftp.dart';
|
import 'package:server_box/data/provider/sftp.dart';
|
||||||
import 'package:server_box/data/provider/snippet.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);
|
T call<T>(ProviderBase<T> provider) => ref.read(provider);
|
||||||
|
|
||||||
// Specific provider getters
|
// Specific provider getters
|
||||||
ServersState get server => ref.read(serverNotifierProvider);
|
ServersState get server => ref.read(serversNotifierProvider);
|
||||||
SnippetState get snippet => ref.read(snippetNotifierProvider);
|
SnippetState get snippet => ref.read(snippetNotifierProvider);
|
||||||
AppState get app => ref.read(appStatesProvider);
|
AppState get app => ref.read(appStatesProvider);
|
||||||
PrivateKeyState get privateKey => ref.read(privateKeyNotifierProvider);
|
PrivateKeyState get privateKey => ref.read(privateKeyNotifierProvider);
|
||||||
@@ -59,7 +59,7 @@ final class WatchMyProvider {
|
|||||||
T call<T>(ProviderBase<T> provider) => ref.watch(provider);
|
T call<T>(ProviderBase<T> provider) => ref.watch(provider);
|
||||||
|
|
||||||
// Specific provider getters
|
// Specific provider getters
|
||||||
ServersState get server => ref.watch(serverNotifierProvider);
|
ServersState get server => ref.watch(serversNotifierProvider);
|
||||||
SnippetState get snippet => ref.watch(snippetNotifierProvider);
|
SnippetState get snippet => ref.watch(snippetNotifierProvider);
|
||||||
AppState get app => ref.watch(appStatesProvider);
|
AppState get app => ref.watch(appStatesProvider);
|
||||||
PrivateKeyState get privateKey => ref.watch(privateKeyNotifierProvider);
|
PrivateKeyState get privateKey => ref.watch(privateKeyNotifierProvider);
|
||||||
@@ -74,7 +74,7 @@ final class UseNotifierMyProvider {
|
|||||||
ref.read(provider.notifier);
|
ref.read(provider.notifier);
|
||||||
|
|
||||||
// Specific provider notifier getters
|
// 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);
|
SnippetNotifier get snippet => ref.read(snippetNotifierProvider.notifier);
|
||||||
AppStates get app => ref.read(appStatesProvider.notifier);
|
AppStates get app => ref.read(appStatesProvider.notifier);
|
||||||
PrivateKeyNotifier get privateKey => ref.read(privateKeyNotifierProvider.notifier);
|
PrivateKeyNotifier get privateKey => ref.read(privateKeyNotifierProvider.notifier);
|
||||||
|
|||||||
@@ -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/app/error.dart';
|
||||||
import 'package:server_box/data/model/server/pve.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/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.freezed.dart';
|
||||||
part 'pve.g.dart';
|
part 'pve.g.dart';
|
||||||
@@ -45,7 +45,7 @@ class PveNotifier extends _$PveNotifier {
|
|||||||
@override
|
@override
|
||||||
PveState build(Spi spiParam) {
|
PveState build(Spi spiParam) {
|
||||||
spi = spiParam;
|
spi = spiParam;
|
||||||
final serverState = ref.watch(individualServerNotifierProvider(spi.id));
|
final serverState = ref.watch(serverNotifierProvider(spi.id));
|
||||||
final client = serverState.client;
|
final client = serverState.client;
|
||||||
if (client == null) {
|
if (client == null) {
|
||||||
return const PveState(error: PveErr(type: PveErrType.net, message: 'Server client is null'));
|
return const PveState(error: PveErr(type: PveErrType.net, message: 'Server client is null'));
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ part of 'pve.dart';
|
|||||||
// RiverpodGenerator
|
// RiverpodGenerator
|
||||||
// **************************************************************************
|
// **************************************************************************
|
||||||
|
|
||||||
String _$pveNotifierHash() => r'667cfb11cd7118d57b29918d137ef2cda2bad7ad';
|
String _$pveNotifierHash() => r'b5da7240db1b9ee7d61f238cebca45821b7a3445';
|
||||||
|
|
||||||
/// Copied from Dart SDK
|
/// Copied from Dart SDK
|
||||||
class _SystemHash {
|
class _SystemHash {
|
||||||
|
|||||||
273
lib/data/provider/server/all.dart
Normal file
273
lib/data/provider/server/all.dart
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,7 +3,7 @@
|
|||||||
// ignore_for_file: type=lint
|
// 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
|
// 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
|
// 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
|
// dart format on
|
||||||
26
lib/data/provider/server/all.g.dart
Normal file
26
lib/data/provider/server/all.g.dart
Normal 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
|
||||||
@@ -8,7 +8,6 @@ import 'package:flutter_gbk2utf8/flutter_gbk2utf8.dart';
|
|||||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||||
import 'package:server_box/core/extension/ssh_client.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/server.dart';
|
||||||
import 'package:server_box/core/utils/ssh_auth.dart';
|
import 'package:server_box/core/utils/ssh_auth.dart';
|
||||||
import 'package:server_box/data/helper/system_detector.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/server_status_update_req.dart';
|
||||||
import 'package:server_box/data/model/server/system.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/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/status.dart';
|
||||||
import 'package:server_box/data/res/store.dart';
|
import 'package:server_box/data/res/store.dart';
|
||||||
import 'package:server_box/data/ssh/session_manager.dart';
|
import 'package:server_box/data/ssh/session_manager.dart';
|
||||||
|
|
||||||
part 'server.freezed.dart';
|
part 'single.g.dart';
|
||||||
part 'server.g.dart';
|
part 'single.freezed.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;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Individual server state, including connection and status information
|
// Individual server state, including connection and status information
|
||||||
@freezed
|
@freezed
|
||||||
@@ -50,20 +39,12 @@ abstract class ServerState with _$ServerState {
|
|||||||
}) = _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
|
// Individual server state management
|
||||||
@riverpod
|
@riverpod
|
||||||
class IndividualServerNotifier extends _$IndividualServerNotifier {
|
class ServerNotifier extends _$ServerNotifier {
|
||||||
@override
|
@override
|
||||||
ServerState build(String serverId) {
|
ServerState build(String serverId) {
|
||||||
final serverNotifier = ref.read(serverNotifierProvider);
|
final serverNotifier = ref.read(serversNotifierProvider);
|
||||||
final spi = serverNotifier.servers[serverId];
|
final spi = serverNotifier.servers[serverId];
|
||||||
if (spi == null) {
|
if (spi == null) {
|
||||||
throw StateError('Server $serverId not found');
|
throw StateError('Server $serverId not found');
|
||||||
@@ -169,7 +150,7 @@ class IndividualServerNotifier extends _$IndividualServerNotifier {
|
|||||||
id: sessionId,
|
id: sessionId,
|
||||||
spi: spi,
|
spi: spi,
|
||||||
startTimeMs: time1.millisecondsSinceEpoch,
|
startTimeMs: time1.millisecondsSinceEpoch,
|
||||||
disconnect: () => ref.read(serverNotifierProvider.notifier)._closeOneServer(spi.id),
|
disconnect: () => ref.read(serversNotifierProvider.notifier).closeOneServer(spi.id),
|
||||||
status: TermSessionStatus.connecting,
|
status: TermSessionStatus.connecting,
|
||||||
);
|
);
|
||||||
TermSessionManager.setActive(sessionId, hasTerminal: false);
|
TermSessionManager.setActive(sessionId, hasTerminal: false);
|
||||||
@@ -371,249 +352,10 @@ class IndividualServerNotifier extends _$IndividualServerNotifier {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Riverpod(keepAlive: true)
|
extension IndividualServerStateExtension on ServerState {
|
||||||
class ServerNotifier extends _$ServerNotifier {
|
bool get needGenClient => conn < ServerConn.connecting;
|
||||||
@override
|
|
||||||
ServersState build() {
|
bool get canViewDetails => conn == ServerConn.finished;
|
||||||
// Initialize with empty state, load data asynchronously
|
|
||||||
Future.microtask(() => _load());
|
String get id => spi.id;
|
||||||
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(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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
301
lib/data/provider/server/single.freezed.dart
Normal file
301
lib/data/provider/server/single.freezed.dart
Normal 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
|
||||||
@@ -1,13 +1,12 @@
|
|||||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
|
||||||
part of 'server.dart';
|
part of 'single.dart';
|
||||||
|
|
||||||
// **************************************************************************
|
// **************************************************************************
|
||||||
// RiverpodGenerator
|
// RiverpodGenerator
|
||||||
// **************************************************************************
|
// **************************************************************************
|
||||||
|
|
||||||
String _$individualServerNotifierHash() =>
|
String _$serverNotifierHash() => r'5625b0a4762c28efdbc124809c03b84a51d213b1';
|
||||||
r'e3d74fb95ca994cd8419b1deab743e8b3e21bee2';
|
|
||||||
|
|
||||||
/// Copied from Dart SDK
|
/// Copied from Dart SDK
|
||||||
class _SystemHash {
|
class _SystemHash {
|
||||||
@@ -30,30 +29,30 @@ class _SystemHash {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract class _$IndividualServerNotifier
|
abstract class _$ServerNotifier
|
||||||
extends BuildlessAutoDisposeNotifier<ServerState> {
|
extends BuildlessAutoDisposeNotifier<ServerState> {
|
||||||
late final String serverId;
|
late final String serverId;
|
||||||
|
|
||||||
ServerState build(String serverId);
|
ServerState build(String serverId);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// See also [IndividualServerNotifier].
|
/// See also [ServerNotifier].
|
||||||
@ProviderFor(IndividualServerNotifier)
|
@ProviderFor(ServerNotifier)
|
||||||
const individualServerNotifierProvider = IndividualServerNotifierFamily();
|
const serverNotifierProvider = ServerNotifierFamily();
|
||||||
|
|
||||||
/// See also [IndividualServerNotifier].
|
/// See also [ServerNotifier].
|
||||||
class IndividualServerNotifierFamily extends Family<ServerState> {
|
class ServerNotifierFamily extends Family<ServerState> {
|
||||||
/// See also [IndividualServerNotifier].
|
/// See also [ServerNotifier].
|
||||||
const IndividualServerNotifierFamily();
|
const ServerNotifierFamily();
|
||||||
|
|
||||||
/// See also [IndividualServerNotifier].
|
/// See also [ServerNotifier].
|
||||||
IndividualServerNotifierProvider call(String serverId) {
|
ServerNotifierProvider call(String serverId) {
|
||||||
return IndividualServerNotifierProvider(serverId);
|
return ServerNotifierProvider(serverId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
IndividualServerNotifierProvider getProviderOverride(
|
ServerNotifierProvider getProviderOverride(
|
||||||
covariant IndividualServerNotifierProvider provider,
|
covariant ServerNotifierProvider provider,
|
||||||
) {
|
) {
|
||||||
return call(provider.serverId);
|
return call(provider.serverId);
|
||||||
}
|
}
|
||||||
@@ -70,29 +69,28 @@ class IndividualServerNotifierFamily extends Family<ServerState> {
|
|||||||
_allTransitiveDependencies;
|
_allTransitiveDependencies;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String? get name => r'individualServerNotifierProvider';
|
String? get name => r'serverNotifierProvider';
|
||||||
}
|
}
|
||||||
|
|
||||||
/// See also [IndividualServerNotifier].
|
/// See also [ServerNotifier].
|
||||||
class IndividualServerNotifierProvider
|
class ServerNotifierProvider
|
||||||
extends
|
extends AutoDisposeNotifierProviderImpl<ServerNotifier, ServerState> {
|
||||||
AutoDisposeNotifierProviderImpl<IndividualServerNotifier, ServerState> {
|
/// See also [ServerNotifier].
|
||||||
/// See also [IndividualServerNotifier].
|
ServerNotifierProvider(String serverId)
|
||||||
IndividualServerNotifierProvider(String serverId)
|
|
||||||
: this._internal(
|
: this._internal(
|
||||||
() => IndividualServerNotifier()..serverId = serverId,
|
() => ServerNotifier()..serverId = serverId,
|
||||||
from: individualServerNotifierProvider,
|
from: serverNotifierProvider,
|
||||||
name: r'individualServerNotifierProvider',
|
name: r'serverNotifierProvider',
|
||||||
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
|
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
|
||||||
? null
|
? null
|
||||||
: _$individualServerNotifierHash,
|
: _$serverNotifierHash,
|
||||||
dependencies: IndividualServerNotifierFamily._dependencies,
|
dependencies: ServerNotifierFamily._dependencies,
|
||||||
allTransitiveDependencies:
|
allTransitiveDependencies:
|
||||||
IndividualServerNotifierFamily._allTransitiveDependencies,
|
ServerNotifierFamily._allTransitiveDependencies,
|
||||||
serverId: serverId,
|
serverId: serverId,
|
||||||
);
|
);
|
||||||
|
|
||||||
IndividualServerNotifierProvider._internal(
|
ServerNotifierProvider._internal(
|
||||||
super._createNotifier, {
|
super._createNotifier, {
|
||||||
required super.name,
|
required super.name,
|
||||||
required super.dependencies,
|
required super.dependencies,
|
||||||
@@ -105,15 +103,15 @@ class IndividualServerNotifierProvider
|
|||||||
final String serverId;
|
final String serverId;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
ServerState runNotifierBuild(covariant IndividualServerNotifier notifier) {
|
ServerState runNotifierBuild(covariant ServerNotifier notifier) {
|
||||||
return notifier.build(serverId);
|
return notifier.build(serverId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Override overrideWith(IndividualServerNotifier Function() create) {
|
Override overrideWith(ServerNotifier Function() create) {
|
||||||
return ProviderOverride(
|
return ProviderOverride(
|
||||||
origin: this,
|
origin: this,
|
||||||
override: IndividualServerNotifierProvider._internal(
|
override: ServerNotifierProvider._internal(
|
||||||
() => create()..serverId = serverId,
|
() => create()..serverId = serverId,
|
||||||
from: from,
|
from: from,
|
||||||
name: null,
|
name: null,
|
||||||
@@ -126,15 +124,14 @@ class IndividualServerNotifierProvider
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
AutoDisposeNotifierProviderElement<IndividualServerNotifier, ServerState>
|
AutoDisposeNotifierProviderElement<ServerNotifier, ServerState>
|
||||||
createElement() {
|
createElement() {
|
||||||
return _IndividualServerNotifierProviderElement(this);
|
return _ServerNotifierProviderElement(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(Object other) {
|
bool operator ==(Object other) {
|
||||||
return other is IndividualServerNotifierProvider &&
|
return other is ServerNotifierProvider && other.serverId == serverId;
|
||||||
other.serverId == serverId;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -148,40 +145,19 @@ class IndividualServerNotifierProvider
|
|||||||
|
|
||||||
@Deprecated('Will be removed in 3.0. Use Ref instead')
|
@Deprecated('Will be removed in 3.0. Use Ref instead')
|
||||||
// ignore: unused_element
|
// ignore: unused_element
|
||||||
mixin IndividualServerNotifierRef
|
mixin ServerNotifierRef on AutoDisposeNotifierProviderRef<ServerState> {
|
||||||
on AutoDisposeNotifierProviderRef<ServerState> {
|
|
||||||
/// The parameter `serverId` of this provider.
|
/// The parameter `serverId` of this provider.
|
||||||
String get serverId;
|
String get serverId;
|
||||||
}
|
}
|
||||||
|
|
||||||
class _IndividualServerNotifierProviderElement
|
class _ServerNotifierProviderElement
|
||||||
extends
|
extends AutoDisposeNotifierProviderElement<ServerNotifier, ServerState>
|
||||||
AutoDisposeNotifierProviderElement<
|
with ServerNotifierRef {
|
||||||
IndividualServerNotifier,
|
_ServerNotifierProviderElement(super.provider);
|
||||||
ServerState
|
|
||||||
>
|
|
||||||
with IndividualServerNotifierRef {
|
|
||||||
_IndividualServerNotifierProviderElement(super.provider);
|
|
||||||
|
|
||||||
@override
|
@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: 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
|
// 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
|
||||||
@@ -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/app/scripts/script_consts.dart';
|
||||||
import 'package:server_box/data/model/server/server_private_info.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/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.freezed.dart';
|
||||||
part 'systemd.g.dart';
|
part 'systemd.g.dart';
|
||||||
@@ -25,7 +25,7 @@ class SystemdNotifier extends _$SystemdNotifier {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
SystemdState build(Spi spi) {
|
SystemdState build(Spi spi) {
|
||||||
final si = ref.read(individualServerNotifierProvider(spi.id));
|
final si = ref.read(serverNotifierProvider(spi.id));
|
||||||
_si = si;
|
_si = si;
|
||||||
// Async initialization
|
// Async initialization
|
||||||
Future.microtask(() => getUnits());
|
Future.microtask(() => getUnits());
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ part of 'systemd.dart';
|
|||||||
// RiverpodGenerator
|
// RiverpodGenerator
|
||||||
// **************************************************************************
|
// **************************************************************************
|
||||||
|
|
||||||
String _$systemdNotifierHash() => r'617fb7637fbc5c5100e5b522d246984f22b44cca';
|
String _$systemdNotifierHash() => r'98466bd176518545be49cae52f8dbe12af3a88a6';
|
||||||
|
|
||||||
/// Copied from Dart SDK
|
/// Copied from Dart SDK
|
||||||
class _SystemHash {
|
class _SystemHash {
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import 'package:server_box/data/model/container/image.dart';
|
|||||||
import 'package:server_box/data/model/container/ps.dart';
|
import 'package:server_box/data/model/container/ps.dart';
|
||||||
import 'package:server_box/data/model/container/type.dart';
|
import 'package:server_box/data/model/container/type.dart';
|
||||||
import 'package:server_box/data/provider/container.dart';
|
import 'package:server_box/data/provider/container.dart';
|
||||||
import 'package:server_box/data/provider/server.dart';
|
import 'package:server_box/data/provider/server/single.dart';
|
||||||
import 'package:server_box/data/res/store.dart';
|
import 'package:server_box/data/res/store.dart';
|
||||||
import 'package:server_box/view/page/ssh/page/page.dart';
|
import 'package:server_box/view/page/ssh/page/page.dart';
|
||||||
|
|
||||||
@@ -43,7 +43,7 @@ class _ContainerPageState extends ConsumerState<ContainerPage> {
|
|||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
final serverState = ref.read(individualServerNotifierProvider(widget.args.spi.id));
|
final serverState = ref.read(serverNotifierProvider(widget.args.spi.id));
|
||||||
_provider = containerNotifierProvider(
|
_provider = containerNotifierProvider(
|
||||||
serverState.client,
|
serverState.client,
|
||||||
widget.args.spi.user,
|
widget.args.spi.user,
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import 'package:responsive_framework/responsive_framework.dart';
|
|||||||
import 'package:server_box/core/chan.dart';
|
import 'package:server_box/core/chan.dart';
|
||||||
import 'package:server_box/core/sync.dart';
|
import 'package:server_box/core/sync.dart';
|
||||||
import 'package:server_box/data/model/app/tab.dart';
|
import 'package:server_box/data/model/app/tab.dart';
|
||||||
import 'package:server_box/data/provider/server.dart';
|
import 'package:server_box/data/provider/server/all.dart';
|
||||||
import 'package:server_box/data/res/build_data.dart';
|
import 'package:server_box/data/res/build_data.dart';
|
||||||
import 'package:server_box/data/res/store.dart';
|
import 'package:server_box/data/res/store.dart';
|
||||||
import 'package:server_box/data/res/url.dart';
|
import 'package:server_box/data/res/url.dart';
|
||||||
@@ -31,8 +31,8 @@ class _HomePageState extends ConsumerState<HomePage>
|
|||||||
bool _shouldAuth = false;
|
bool _shouldAuth = false;
|
||||||
DateTime? _pausedTime;
|
DateTime? _pausedTime;
|
||||||
|
|
||||||
late final _notifier = ref.read(serverNotifierProvider.notifier);
|
late final _notifier = ref.read(serversNotifierProvider.notifier);
|
||||||
late final _provider = ref.read(serverNotifierProvider);
|
late final _provider = ref.read(serversNotifierProvider);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
|
|||||||
@@ -5,7 +5,8 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:server_box/core/extension/context/locale.dart';
|
import 'package:server_box/core/extension/context/locale.dart';
|
||||||
import 'package:server_box/data/model/server/ping_result.dart';
|
import 'package:server_box/data/model/server/ping_result.dart';
|
||||||
import 'package:server_box/data/provider/server.dart';
|
import 'package:server_box/data/provider/server/all.dart';
|
||||||
|
import 'package:server_box/data/provider/server/single.dart';
|
||||||
|
|
||||||
/// Only permit ipv4 / ipv6 / domain chars
|
/// Only permit ipv4 / ipv6 / domain chars
|
||||||
final targetReg = RegExp(r'[a-zA-Z0-9\.-_:]+');
|
final targetReg = RegExp(r'[a-zA-Z0-9\.-_:]+');
|
||||||
@@ -130,7 +131,7 @@ class _PingPageState extends ConsumerState<PingPage> with AutomaticKeepAliveClie
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ref.read(serverNotifierProvider).serverOrder.isEmpty) {
|
if (ref.read(serversNotifierProvider).serverOrder.isEmpty) {
|
||||||
context.showSnackBar(l10n.pingNoServer);
|
context.showSnackBar(l10n.pingNoServer);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -142,8 +143,8 @@ class _PingPageState extends ConsumerState<PingPage> with AutomaticKeepAliveClie
|
|||||||
}
|
}
|
||||||
|
|
||||||
await Future.wait(
|
await Future.wait(
|
||||||
ref.read(serverNotifierProvider).servers.values.map((spi) async {
|
ref.read(serversNotifierProvider).servers.values.map((spi) async {
|
||||||
final serverState = ref.read(individualServerNotifierProvider(spi.id));
|
final serverState = ref.read(serverNotifierProvider(spi.id));
|
||||||
if (serverState.client == null) {
|
if (serverState.client == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import 'package:server_box/core/extension/context/locale.dart';
|
|||||||
import 'package:server_box/core/route.dart';
|
import 'package:server_box/core/route.dart';
|
||||||
import 'package:server_box/data/model/app/scripts/shell_func.dart';
|
import 'package:server_box/data/model/app/scripts/shell_func.dart';
|
||||||
import 'package:server_box/data/model/server/proc.dart';
|
import 'package:server_box/data/model/server/proc.dart';
|
||||||
import 'package:server_box/data/provider/server.dart';
|
import 'package:server_box/data/provider/server/single.dart';
|
||||||
import 'package:server_box/data/res/store.dart';
|
import 'package:server_box/data/res/store.dart';
|
||||||
|
|
||||||
class ProcessPage extends ConsumerStatefulWidget {
|
class ProcessPage extends ConsumerStatefulWidget {
|
||||||
@@ -37,7 +37,7 @@ class _ProcessPageState extends ConsumerState<ProcessPage> {
|
|||||||
ProcSortMode _procSortMode = ProcSortMode.cpu;
|
ProcSortMode _procSortMode = ProcSortMode.cpu;
|
||||||
List<ProcSortMode> _sortModes = List.from(ProcSortMode.values);
|
List<ProcSortMode> _sortModes = List.from(ProcSortMode.values);
|
||||||
|
|
||||||
late final _provider = individualServerNotifierProvider(widget.args.spi.id);
|
late final _provider = serverNotifierProvider(widget.args.spi.id);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ import 'package:server_box/data/model/server/sensors.dart';
|
|||||||
import 'package:server_box/data/model/server/server.dart' as server_model;
|
import 'package:server_box/data/model/server/server.dart' as server_model;
|
||||||
import 'package:server_box/data/model/server/server_private_info.dart';
|
import 'package:server_box/data/model/server/server_private_info.dart';
|
||||||
import 'package:server_box/data/model/server/system.dart';
|
import 'package:server_box/data/model/server/system.dart';
|
||||||
import 'package:server_box/data/provider/server.dart';
|
import 'package:server_box/data/provider/server/single.dart';
|
||||||
import 'package:server_box/data/res/store.dart';
|
import 'package:server_box/data/res/store.dart';
|
||||||
import 'package:server_box/view/page/pve.dart';
|
import 'package:server_box/view/page/pve.dart';
|
||||||
import 'package:server_box/view/page/server/edit.dart';
|
import 'package:server_box/view/page/server/edit.dart';
|
||||||
@@ -86,7 +86,7 @@ class _ServerDetailPageState extends ConsumerState<ServerDetailPage> with Single
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final serverState = ref.watch(individualServerNotifierProvider(widget.args.spi.id));
|
final serverState = ref.watch(serverNotifierProvider(widget.args.spi.id));
|
||||||
if (serverState.client == null) {
|
if (serverState.client == null) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: CustomAppBar(),
|
appBar: CustomAppBar(),
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ import 'package:server_box/data/model/server/server_private_info.dart';
|
|||||||
import 'package:server_box/data/model/server/system.dart';
|
import 'package:server_box/data/model/server/system.dart';
|
||||||
import 'package:server_box/data/model/server/wol_cfg.dart';
|
import 'package:server_box/data/model/server/wol_cfg.dart';
|
||||||
import 'package:server_box/data/provider/private_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/server/all.dart';
|
||||||
import 'package:server_box/data/res/store.dart';
|
import 'package:server_box/data/res/store.dart';
|
||||||
import 'package:server_box/data/store/server.dart';
|
import 'package:server_box/data/store/server.dart';
|
||||||
import 'package:server_box/view/page/private_key/edit.dart';
|
import 'package:server_box/view/page/private_key/edit.dart';
|
||||||
@@ -172,7 +172,7 @@ class _ServerEditPageState extends ConsumerState<ServerEditPage> with AfterLayou
|
|||||||
hint: 'root',
|
hint: 'root',
|
||||||
suggestion: false,
|
suggestion: false,
|
||||||
),
|
),
|
||||||
TagTile(tags: _tags, allTags: ref.watch(serverNotifierProvider).tags).cardx,
|
TagTile(tags: _tags, allTags: ref.watch(serversNotifierProvider).tags).cardx,
|
||||||
ListTile(
|
ListTile(
|
||||||
title: Text(l10n.autoConnect),
|
title: Text(l10n.autoConnect),
|
||||||
trailing: _autoConnect.listenVal(
|
trailing: _autoConnect.listenVal(
|
||||||
@@ -492,7 +492,7 @@ class _ServerEditPageState extends ConsumerState<ServerEditPage> with AfterLayou
|
|||||||
Widget _buildJumpServer() {
|
Widget _buildJumpServer() {
|
||||||
const padding = EdgeInsets.only(left: 13, right: 13, bottom: 7);
|
const padding = EdgeInsets.only(left: 13, right: 13, bottom: 7);
|
||||||
final srvs = ref
|
final srvs = ref
|
||||||
.watch(serverNotifierProvider)
|
.watch(serversNotifierProvider)
|
||||||
.servers
|
.servers
|
||||||
.values
|
.values
|
||||||
.where((e) => e.jumpId == null)
|
.where((e) => e.jumpId == null)
|
||||||
@@ -587,7 +587,7 @@ class _ServerEditPageState extends ConsumerState<ServerEditPage> with AfterLayou
|
|||||||
actions: Btn.ok(
|
actions: Btn.ok(
|
||||||
onTap: () async {
|
onTap: () async {
|
||||||
context.pop();
|
context.pop();
|
||||||
ref.read(serverNotifierProvider.notifier).delServer(spi!.id);
|
ref.read(serversNotifierProvider.notifier).delServer(spi!.id);
|
||||||
context.pop(true);
|
context.pop(true);
|
||||||
},
|
},
|
||||||
red: true,
|
red: true,
|
||||||
@@ -683,7 +683,7 @@ extension _Actions on _ServerEditPageState {
|
|||||||
|
|
||||||
if (shouldImport == true) {
|
if (shouldImport == true) {
|
||||||
for (final server in resolved) {
|
for (final server in resolved) {
|
||||||
ref.read(serverNotifierProvider.notifier).addServer(server);
|
ref.read(serversNotifierProvider.notifier).addServer(server);
|
||||||
}
|
}
|
||||||
context.showSnackBar(l10n.sshConfigImported('${resolved.length}'));
|
context.showSnackBar(l10n.sshConfigImported('${resolved.length}'));
|
||||||
}
|
}
|
||||||
@@ -802,9 +802,9 @@ extension _Actions on _ServerEditPageState {
|
|||||||
context.showSnackBar('${l10n.sameIdServerExist}: ${spi.id}');
|
context.showSnackBar('${l10n.sameIdServerExist}: ${spi.id}');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
ref.read(serverNotifierProvider.notifier).addServer(spi);
|
ref.read(serversNotifierProvider.notifier).addServer(spi);
|
||||||
} else {
|
} else {
|
||||||
ref.read(serverNotifierProvider.notifier).updateServer(this.spi!, spi);
|
ref.read(serversNotifierProvider.notifier).updateServer(this.spi!, spi);
|
||||||
}
|
}
|
||||||
|
|
||||||
context.pop();
|
context.pop();
|
||||||
@@ -860,7 +860,7 @@ extension _Utils on _ServerEditPageState {
|
|||||||
|
|
||||||
// Import without asking again since user already gave permission
|
// Import without asking again since user already gave permission
|
||||||
for (final server in resolved) {
|
for (final server in resolved) {
|
||||||
ref.read(serverNotifierProvider.notifier).addServer(server);
|
ref.read(serversNotifierProvider.notifier).addServer(server);
|
||||||
}
|
}
|
||||||
context.showSnackBar(l10n.sshConfigImported('${resolved.length}'));
|
context.showSnackBar(l10n.sshConfigImported('${resolved.length}'));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,16 +30,16 @@ extension on _ServerPageState {
|
|||||||
const Icon(Icons.refresh, size: 21, color: Colors.grey),
|
const Icon(Icons.refresh, size: 21, color: Colors.grey),
|
||||||
() {
|
() {
|
||||||
TryLimiter.reset(s.spi.id);
|
TryLimiter.reset(s.spi.id);
|
||||||
ref.read(serverNotifierProvider.notifier).refresh(spi: s.spi);
|
ref.read(serversNotifierProvider.notifier).refresh(spi: s.spi);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
ServerConn.disconnected => (
|
ServerConn.disconnected => (
|
||||||
const Icon(MingCute.link_3_line, size: 19, color: Colors.grey),
|
const Icon(MingCute.link_3_line, size: 19, color: Colors.grey),
|
||||||
() => ref.read(serverNotifierProvider.notifier).refresh(spi: s.spi),
|
() => ref.read(serversNotifierProvider.notifier).refresh(spi: s.spi),
|
||||||
),
|
),
|
||||||
ServerConn.finished => (
|
ServerConn.finished => (
|
||||||
const Icon(MingCute.unlink_2_line, size: 17, color: Colors.grey),
|
const Icon(MingCute.unlink_2_line, size: 17, color: Colors.grey),
|
||||||
() => ref.read(serverNotifierProvider.notifier).closeServer(id: s.spi.id),
|
() => ref.read(serversNotifierProvider.notifier).closeServer(id: s.spi.id),
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -106,7 +106,7 @@ ${ss.err?.message ?? 'null'}
|
|||||||
Widget _buildNet(ServerStatus ss, String id) {
|
Widget _buildNet(ServerStatus ss, String id) {
|
||||||
final cardNoti = _getCardNoti(id);
|
final cardNoti = _getCardNoti(id);
|
||||||
final type = cardNoti.value.net ?? Stores.setting.netViewType.fetch();
|
final type = cardNoti.value.net ?? Stores.setting.netViewType.fetch();
|
||||||
final device = ref.watch(serverNotifierProvider).servers[id]?.custom?.netDev;
|
final device = ref.watch(serversNotifierProvider).servers[id]?.custom?.netDev;
|
||||||
final (a, b) = type.build(ss, dev: device);
|
final (a, b) = type.build(ss, dev: device);
|
||||||
return AnimatedSwitcher(
|
return AnimatedSwitcher(
|
||||||
duration: const Duration(milliseconds: 377),
|
duration: const Duration(milliseconds: 377),
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ extension on _ServerPageState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildLandscapeBody() {
|
Widget _buildLandscapeBody() {
|
||||||
final serverState = ref.watch(serverNotifierProvider);
|
final serverState = ref.watch(serversNotifierProvider);
|
||||||
final order = serverState.serverOrder;
|
final order = serverState.serverOrder;
|
||||||
|
|
||||||
if (order.isEmpty) {
|
if (order.isEmpty) {
|
||||||
@@ -37,7 +37,7 @@ extension on _ServerPageState {
|
|||||||
itemCount: order.length,
|
itemCount: order.length,
|
||||||
itemBuilder: (_, idx) {
|
itemBuilder: (_, idx) {
|
||||||
final id = order[idx];
|
final id = order[idx];
|
||||||
final srv = ref.watch(individualServerNotifierProvider(id));
|
final srv = ref.watch(serverNotifierProvider(id));
|
||||||
|
|
||||||
final title = _buildServerCardTitle(srv);
|
final title = _buildServerCardTitle(srv);
|
||||||
final List<Widget> children = [title, _buildNormalCard(srv.status, srv.spi)];
|
final List<Widget> children = [title, _buildNormalCard(srv.status, srv.spi)];
|
||||||
|
|||||||
@@ -17,7 +17,8 @@ import 'package:server_box/data/model/app/scripts/shell_func.dart';
|
|||||||
import 'package:server_box/data/model/server/server.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/server_private_info.dart';
|
||||||
import 'package:server_box/data/model/server/try_limiter.dart';
|
import 'package:server_box/data/model/server/try_limiter.dart';
|
||||||
import 'package:server_box/data/provider/server.dart';
|
import 'package:server_box/data/provider/server/all.dart';
|
||||||
|
import 'package:server_box/data/provider/server/single.dart';
|
||||||
import 'package:server_box/data/res/build_data.dart';
|
import 'package:server_box/data/res/build_data.dart';
|
||||||
import 'package:server_box/data/res/store.dart';
|
import 'package:server_box/data/res/store.dart';
|
||||||
import 'package:server_box/view/page/server/detail/view.dart';
|
import 'package:server_box/view/page/server/detail/view.dart';
|
||||||
@@ -73,7 +74,7 @@ class _ServerPageState extends ConsumerState<ServerPage>
|
|||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
_tags = ValueNotifier(ref.read(serverNotifierProvider).tags);
|
_tags = ValueNotifier(ref.read(serversNotifierProvider).tags);
|
||||||
_startAvoidJitterTimer();
|
_startAvoidJitterTimer();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -88,7 +89,7 @@ class _ServerPageState extends ConsumerState<ServerPage>
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
super.build(context);
|
super.build(context);
|
||||||
// Listen to provider changes and update the ValueNotifier
|
// Listen to provider changes and update the ValueNotifier
|
||||||
ref.listen(serverNotifierProvider, (previous, next) {
|
ref.listen(serversNotifierProvider, (previous, next) {
|
||||||
_tags.value = next.tags;
|
_tags.value = next.tags;
|
||||||
});
|
});
|
||||||
return OrientationBuilder(
|
return OrientationBuilder(
|
||||||
@@ -132,7 +133,7 @@ class _ServerPageState extends ConsumerState<ServerPage>
|
|||||||
|
|
||||||
Widget _buildPortrait() {
|
Widget _buildPortrait() {
|
||||||
// final isMobile = ResponsiveBreakpoints.of(context).isMobile;
|
// final isMobile = ResponsiveBreakpoints.of(context).isMobile;
|
||||||
final serverState = ref.watch(serverNotifierProvider);
|
final serverState = ref.watch(serversNotifierProvider);
|
||||||
return _tag.listenVal((val) {
|
return _tag.listenVal((val) {
|
||||||
final filtered = _filterServers(serverState.serverOrder);
|
final filtered = _filterServers(serverState.serverOrder);
|
||||||
final child = _buildScaffold(_buildBodySmall(filtered: filtered));
|
final child = _buildScaffold(_buildBodySmall(filtered: filtered));
|
||||||
@@ -182,7 +183,7 @@ class _ServerPageState extends ConsumerState<ServerPage>
|
|||||||
// Last item is just spacing
|
// Last item is just spacing
|
||||||
if (index == lens) return SizedBox(height: 77);
|
if (index == lens) return SizedBox(height: 77);
|
||||||
|
|
||||||
final individualState = ref.watch(individualServerNotifierProvider(serversInThisColumn[index]));
|
final individualState = ref.watch(serverNotifierProvider(serversInThisColumn[index]));
|
||||||
|
|
||||||
return _buildEachServerCard(individualState);
|
return _buildEachServerCard(individualState);
|
||||||
},
|
},
|
||||||
@@ -338,8 +339,8 @@ class _ServerPageState extends ConsumerState<ServerPage>
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> afterFirstLayout(BuildContext context) async {
|
Future<void> afterFirstLayout(BuildContext context) async {
|
||||||
ref.read(serverNotifierProvider.notifier).refresh();
|
ref.read(serversNotifierProvider.notifier).refresh();
|
||||||
ref.read(serverNotifierProvider.notifier).startAutoRefresh();
|
ref.read(serversNotifierProvider.notifier).startAutoRefresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
static const _kCardHeightMin = 23.0;
|
static const _kCardHeightMin = 23.0;
|
||||||
|
|||||||
@@ -98,7 +98,7 @@ extension _Utils on _ServerPageState {
|
|||||||
final tag = _tag.value;
|
final tag = _tag.value;
|
||||||
if (tag == TagSwitcher.kDefaultTag) return order;
|
if (tag == TagSwitcher.kDefaultTag) return order;
|
||||||
return order.where((e) {
|
return order.where((e) {
|
||||||
final tags = ref.read(serverNotifierProvider).servers[e]?.tags;
|
final tags = ref.read(serversNotifierProvider).servers[e]?.tags;
|
||||||
if (tags == null) return false;
|
if (tags == null) return false;
|
||||||
return tags.contains(tag);
|
return tags.contains(tag);
|
||||||
}).toList();
|
}).toList();
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ extension _Server on _AppSettingsPageState {
|
|||||||
onTap: () async {
|
onTap: () async {
|
||||||
final keys = Stores.server.keys();
|
final keys = Stores.server.keys();
|
||||||
final names = Map.fromEntries(
|
final names = Map.fromEntries(
|
||||||
keys.map((e) => MapEntry(e, ref.read(serverNotifierProvider).servers[e]?.name ?? e)),
|
keys.map((e) => MapEntry(e, ref.read(serversNotifierProvider).servers[e]?.name ?? e)),
|
||||||
);
|
);
|
||||||
final deleteKeys = await context.showPickDialog<String>(
|
final deleteKeys = await context.showPickDialog<String>(
|
||||||
clearable: true,
|
clearable: true,
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|||||||
import 'package:icons_plus/icons_plus.dart';
|
import 'package:icons_plus/icons_plus.dart';
|
||||||
import 'package:server_box/core/extension/context/locale.dart';
|
import 'package:server_box/core/extension/context/locale.dart';
|
||||||
import 'package:server_box/data/model/app/net_view.dart';
|
import 'package:server_box/data/model/app/net_view.dart';
|
||||||
import 'package:server_box/data/provider/server.dart';
|
import 'package:server_box/data/provider/server/all.dart';
|
||||||
import 'package:server_box/data/res/build_data.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/github_id.dart';
|
||||||
import 'package:server_box/data/res/store.dart';
|
import 'package:server_box/data/res/store.dart';
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:server_box/core/extension/context/locale.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/server/server_private_info.dart';
|
||||||
import 'package:server_box/data/provider/server.dart';
|
import 'package:server_box/data/provider/server/all.dart';
|
||||||
import 'package:server_box/data/res/store.dart';
|
import 'package:server_box/data/res/store.dart';
|
||||||
|
|
||||||
class ServerOrderPage extends ConsumerStatefulWidget {
|
class ServerOrderPage extends ConsumerStatefulWidget {
|
||||||
@@ -42,7 +42,7 @@ class _ServerOrderPageState extends ConsumerState<ServerOrderPage> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildBody() {
|
Widget _buildBody() {
|
||||||
final serverState = ref.watch(serverNotifierProvider);
|
final serverState = ref.watch(serversNotifierProvider);
|
||||||
final order = serverState.serverOrder;
|
final order = serverState.serverOrder;
|
||||||
|
|
||||||
if (order.isEmpty) {
|
if (order.isEmpty) {
|
||||||
@@ -77,7 +77,7 @@ class _ServerOrderPageState extends ConsumerState<ServerOrderPage> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildCardTile(int index) {
|
Widget _buildCardTile(int index) {
|
||||||
final serverState = ref.watch(serverNotifierProvider);
|
final serverState = ref.watch(serversNotifierProvider);
|
||||||
final order = serverState.serverOrder;
|
final order = serverState.serverOrder;
|
||||||
final id = order[index];
|
final id = order[index];
|
||||||
final spi = serverState.servers[id];
|
final spi = serverState.servers[id];
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import 'package:flutter_markdown/flutter_markdown.dart';
|
|||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:server_box/core/extension/context/locale.dart';
|
import 'package:server_box/core/extension/context/locale.dart';
|
||||||
import 'package:server_box/data/model/server/snippet.dart';
|
import 'package:server_box/data/model/server/snippet.dart';
|
||||||
import 'package:server_box/data/provider/server.dart';
|
import 'package:server_box/data/provider/server/all.dart';
|
||||||
import 'package:server_box/data/provider/snippet.dart';
|
import 'package:server_box/data/provider/snippet.dart';
|
||||||
|
|
||||||
final class SnippetEditPageArgs {
|
final class SnippetEditPageArgs {
|
||||||
@@ -157,7 +157,7 @@ class _SnippetEditPageState extends ConsumerState<SnippetEditPage> with AfterLay
|
|||||||
builder: (vals) {
|
builder: (vals) {
|
||||||
final subtitle = vals.isEmpty
|
final subtitle = vals.isEmpty
|
||||||
? null
|
? null
|
||||||
: vals.map((e) => ref.read(serverNotifierProvider).servers[e]?.name ?? e).join(', ');
|
: vals.map((e) => ref.read(serversNotifierProvider).servers[e]?.name ?? e).join(', ');
|
||||||
return ListTile(
|
return ListTile(
|
||||||
leading: const Padding(
|
leading: const Padding(
|
||||||
padding: EdgeInsets.only(left: 5),
|
padding: EdgeInsets.only(left: 5),
|
||||||
@@ -169,12 +169,13 @@ class _SnippetEditPageState extends ConsumerState<SnippetEditPage> with AfterLay
|
|||||||
? null
|
? null
|
||||||
: Text(subtitle, maxLines: 1, style: UIs.textGrey, overflow: TextOverflow.ellipsis),
|
: Text(subtitle, maxLines: 1, style: UIs.textGrey, overflow: TextOverflow.ellipsis),
|
||||||
onTap: () async {
|
onTap: () async {
|
||||||
vals.removeWhere((e) => !ref.read(serverNotifierProvider).serverOrder.contains(e));
|
// Create a filtered copy for the dialog, don't modify the original
|
||||||
|
final validServerIds = vals.where((e) => ref.read(serversNotifierProvider).serverOrder.contains(e)).toList();
|
||||||
final serverIds = await context.showPickDialog(
|
final serverIds = await context.showPickDialog(
|
||||||
title: l10n.autoRun,
|
title: l10n.autoRun,
|
||||||
items: ref.read(serverNotifierProvider).serverOrder,
|
items: ref.read(serversNotifierProvider).serverOrder,
|
||||||
display: (e) => ref.read(serverNotifierProvider).servers[e]?.name ?? e,
|
display: (e) => ref.read(serversNotifierProvider).servers[e]?.name ?? e,
|
||||||
initial: vals,
|
initial: validServerIds,
|
||||||
clearable: true,
|
clearable: true,
|
||||||
);
|
);
|
||||||
if (serverIds != null) {
|
if (serverIds != null) {
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ import 'package:server_box/core/utils/ssh_auth.dart';
|
|||||||
import 'package:server_box/data/model/server/server_private_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/model/server/snippet.dart';
|
||||||
import 'package:server_box/data/model/ssh/virtual_key.dart';
|
import 'package:server_box/data/model/ssh/virtual_key.dart';
|
||||||
import 'package:server_box/data/provider/server.dart';
|
import 'package:server_box/data/provider/server/single.dart';
|
||||||
import 'package:server_box/data/provider/snippet.dart';
|
import 'package:server_box/data/provider/snippet.dart';
|
||||||
import 'package:server_box/data/provider/virtual_keyboard.dart';
|
import 'package:server_box/data/provider/virtual_keyboard.dart';
|
||||||
import 'package:server_box/data/res/store.dart';
|
import 'package:server_box/data/res/store.dart';
|
||||||
@@ -118,7 +118,7 @@ class SSHPageState extends ConsumerState<SSHPage>
|
|||||||
_setupDiscontinuityTimer();
|
_setupDiscontinuityTimer();
|
||||||
|
|
||||||
// Initialize client from provider
|
// Initialize client from provider
|
||||||
final serverState = ref.read(individualServerNotifierProvider(widget.args.spi.id));
|
final serverState = ref.read(serverNotifierProvider(widget.args.spi.id));
|
||||||
_client = serverState.client;
|
_client = serverState.client;
|
||||||
|
|
||||||
if (++_sshConnCount == 1) {
|
if (++_sshConnCount == 1) {
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|||||||
import 'package:icons_plus/icons_plus.dart';
|
import 'package:icons_plus/icons_plus.dart';
|
||||||
import 'package:server_box/core/extension/context/locale.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/server/server_private_info.dart';
|
||||||
import 'package:server_box/data/provider/server.dart';
|
import 'package:server_box/data/provider/server/all.dart';
|
||||||
import 'package:server_box/view/page/server/edit.dart';
|
import 'package:server_box/view/page/server/edit.dart';
|
||||||
import 'package:server_box/view/page/ssh/page/page.dart';
|
import 'package:server_box/view/page/ssh/page/page.dart';
|
||||||
|
|
||||||
@@ -249,7 +249,7 @@ class _AddPage extends ConsumerWidget {
|
|||||||
const viewPadding = 7.0;
|
const viewPadding = 7.0;
|
||||||
final viewWidth = context.windowSize.width - 2 * viewPadding;
|
final viewWidth = context.windowSize.width - 2 * viewPadding;
|
||||||
|
|
||||||
final serverState = ref.watch(serverNotifierProvider);
|
final serverState = ref.watch(serversNotifierProvider);
|
||||||
final itemCount = serverState.servers.length;
|
final itemCount = serverState.servers.length;
|
||||||
const itemPadding = 1.0;
|
const itemPadding = 1.0;
|
||||||
const itemWidth = 150.0;
|
const itemWidth = 150.0;
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import 'package:server_box/core/extension/context/locale.dart';
|
|||||||
import 'package:server_box/data/model/app/path_with_prefix.dart';
|
import 'package:server_box/data/model/app/path_with_prefix.dart';
|
||||||
import 'package:server_box/data/model/server/server_private_info.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/model/sftp/worker.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/sftp.dart';
|
||||||
import 'package:server_box/data/res/misc.dart';
|
import 'package:server_box/data/res/misc.dart';
|
||||||
import 'package:server_box/data/store/setting.dart';
|
import 'package:server_box/data/store/setting.dart';
|
||||||
@@ -359,7 +359,7 @@ extension _OnTapFile on _LocalFilePageState {
|
|||||||
|
|
||||||
final spi = await context.showPickSingleDialog<Spi>(
|
final spi = await context.showPickSingleDialog<Spi>(
|
||||||
title: libL10n.select,
|
title: libL10n.select,
|
||||||
items: ref.read(serverNotifierProvider).servers.values.toList(),
|
items: ref.read(serversNotifierProvider).servers.values.toList(),
|
||||||
display: (e) => e.name,
|
display: (e) => e.name,
|
||||||
);
|
);
|
||||||
if (spi == null) return;
|
if (spi == null) return;
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ 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/server/server_private_info.dart';
|
||||||
import 'package:server_box/data/model/sftp/browser_status.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/model/sftp/worker.dart';
|
||||||
import 'package:server_box/data/provider/server.dart';
|
import 'package:server_box/data/provider/server/single.dart';
|
||||||
import 'package:server_box/data/provider/sftp.dart';
|
import 'package:server_box/data/provider/sftp.dart';
|
||||||
import 'package:server_box/data/res/misc.dart';
|
import 'package:server_box/data/res/misc.dart';
|
||||||
import 'package:server_box/data/res/store.dart';
|
import 'package:server_box/data/res/store.dart';
|
||||||
@@ -50,7 +50,7 @@ class _SftpPageState extends ConsumerState<SftpPage> with AfterLayoutMixin {
|
|||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
final serverState = ref.read(individualServerNotifierProvider(widget.args.spi.id));
|
final serverState = ref.read(serverNotifierProvider(widget.args.spi.id));
|
||||||
_client = serverState.client!;
|
_client = serverState.client!;
|
||||||
_status = SftpBrowserStatus(_client);
|
_status = SftpBrowserStatus(_client);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ 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/app/menu/server_func.dart';
|
||||||
import 'package:server_box/data/model/server/server_private_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/model/server/snippet.dart';
|
||||||
import 'package:server_box/data/provider/server.dart';
|
import 'package:server_box/data/provider/server/single.dart';
|
||||||
import 'package:server_box/data/provider/snippet.dart';
|
import 'package:server_box/data/provider/snippet.dart';
|
||||||
import 'package:server_box/data/res/store.dart';
|
import 'package:server_box/data/res/store.dart';
|
||||||
import 'package:server_box/view/page/container/container.dart';
|
import 'package:server_box/view/page/container/container.dart';
|
||||||
@@ -273,7 +273,7 @@ void _gotoSSH(Spi spi, BuildContext context) async {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool _checkClient(BuildContext context, String id, WidgetRef ref) {
|
bool _checkClient(BuildContext context, String id, WidgetRef ref) {
|
||||||
final serverState = ref.read(individualServerNotifierProvider(id));
|
final serverState = ref.read(serverNotifierProvider(id));
|
||||||
if (serverState.client == null) {
|
if (serverState.client == null) {
|
||||||
context.showSnackBar(l10n.waitConnection);
|
context.showSnackBar(l10n.waitConnection);
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
Reference in New Issue
Block a user