fix(server): remove connection stats feature (#1063)
This commit is contained in:
@@ -225,9 +225,6 @@ class ServersNotifier extends _$ServersNotifier {
|
|||||||
Stores.setting.serverOrder.put(newOrder);
|
Stores.setting.serverOrder.put(newOrder);
|
||||||
Stores.server.delete(id);
|
Stores.server.delete(id);
|
||||||
|
|
||||||
// Remove connection stats when server is deleted
|
|
||||||
Stores.connectionStats.clearServerStats(id);
|
|
||||||
|
|
||||||
// Remove SSH session when server is deleted
|
// Remove SSH session when server is deleted
|
||||||
final sessionId = 'ssh_$id';
|
final sessionId = 'ssh_$id';
|
||||||
TermSessionManager.remove(sessionId);
|
TermSessionManager.remove(sessionId);
|
||||||
@@ -246,7 +243,6 @@ class ServersNotifier extends _$ServersNotifier {
|
|||||||
|
|
||||||
Stores.setting.serverOrder.put([]);
|
Stores.setting.serverOrder.put([]);
|
||||||
Stores.server.clear();
|
Stores.server.clear();
|
||||||
Stores.connectionStats.clearAll();
|
|
||||||
bakSync.sync(milliDelay: 1000);
|
bakSync.sync(milliDelay: 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ import 'package:server_box/data/helper/system_detector.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/app/scripts/script_consts.dart';
|
import 'package:server_box/data/model/app/scripts/script_consts.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/connection_stat.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/server_status_update_req.dart';
|
import 'package:server_box/data/model/server/server_status_update_req.dart';
|
||||||
@@ -141,15 +140,6 @@ class ServerNotifier extends _$ServerNotifier {
|
|||||||
Loggers.app.info('Jump to ${spi.name} in $spentTime ms.');
|
Loggers.app.info('Jump to ${spi.name} in $spentTime ms.');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Record successful connection
|
|
||||||
Stores.connectionStats.recordConnection(ConnectionStat(
|
|
||||||
serverId: spi.id,
|
|
||||||
serverName: spi.name,
|
|
||||||
timestamp: time1,
|
|
||||||
result: ConnectionResult.success,
|
|
||||||
durationMs: spentTime,
|
|
||||||
));
|
|
||||||
|
|
||||||
final sessionId = 'ssh_${spi.id}';
|
final sessionId = 'ssh_${spi.id}';
|
||||||
TermSessionManager.add(
|
TermSessionManager.add(
|
||||||
id: sessionId,
|
id: sessionId,
|
||||||
@@ -162,28 +152,6 @@ class ServerNotifier extends _$ServerNotifier {
|
|||||||
} catch (e) {
|
} catch (e) {
|
||||||
TryLimiter.inc(sid);
|
TryLimiter.inc(sid);
|
||||||
|
|
||||||
// Determine connection failure type
|
|
||||||
ConnectionResult failureResult;
|
|
||||||
if (e.toString().contains('timeout') || e.toString().contains('Timeout')) {
|
|
||||||
failureResult = ConnectionResult.timeout;
|
|
||||||
} else if (e.toString().contains('auth') || e.toString().contains('Authentication')) {
|
|
||||||
failureResult = ConnectionResult.authFailed;
|
|
||||||
} else if (e.toString().contains('network') || e.toString().contains('Network')) {
|
|
||||||
failureResult = ConnectionResult.networkError;
|
|
||||||
} else {
|
|
||||||
failureResult = ConnectionResult.unknownError;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Record failed connection
|
|
||||||
Stores.connectionStats.recordConnection(ConnectionStat(
|
|
||||||
serverId: spi.id,
|
|
||||||
serverName: spi.name,
|
|
||||||
timestamp: DateTime.now(),
|
|
||||||
result: failureResult,
|
|
||||||
errorMessage: e.toString(),
|
|
||||||
durationMs: 0,
|
|
||||||
));
|
|
||||||
|
|
||||||
final newStatus = state.status..err = SSHErr(type: SSHErrType.connect, message: e.toString());
|
final newStatus = state.status..err = SSHErr(type: SSHErrType.connect, message: e.toString());
|
||||||
updateStatus(newStatus);
|
updateStatus(newStatus);
|
||||||
updateConnection(ServerConn.failed);
|
updateConnection(ServerConn.failed);
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ abstract final class Stores {
|
|||||||
static PrivateKeyStore get key => getIt<PrivateKeyStore>();
|
static PrivateKeyStore get key => getIt<PrivateKeyStore>();
|
||||||
static SnippetStore get snippet => getIt<SnippetStore>();
|
static SnippetStore get snippet => getIt<SnippetStore>();
|
||||||
static HistoryStore get history => getIt<HistoryStore>();
|
static HistoryStore get history => getIt<HistoryStore>();
|
||||||
|
// Keep the legacy box registered so existing connection stats DB files remain intact.
|
||||||
static ConnectionStatsStore get connectionStats => getIt<ConnectionStatsStore>();
|
static ConnectionStatsStore get connectionStats => getIt<ConnectionStatsStore>();
|
||||||
|
|
||||||
/// All stores that need backup
|
/// All stores that need backup
|
||||||
|
|||||||
@@ -1,341 +0,0 @@
|
|||||||
import 'dart:io';
|
|
||||||
|
|
||||||
import 'package:fl_lib/fl_lib.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:server_box/core/extension/context/locale.dart';
|
|
||||||
import 'package:server_box/data/model/server/connection_stat.dart';
|
|
||||||
import 'package:server_box/data/res/store.dart';
|
|
||||||
|
|
||||||
class ConnectionStatsPage extends StatefulWidget {
|
|
||||||
const ConnectionStatsPage({super.key});
|
|
||||||
|
|
||||||
static const route = AppRouteNoArg(page: ConnectionStatsPage.new, path: '/server/conn_stats');
|
|
||||||
|
|
||||||
@override
|
|
||||||
State<ConnectionStatsPage> createState() => _ConnectionStatsPageState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _ConnectionStatsPageState extends State<ConnectionStatsPage> {
|
|
||||||
List<ServerConnectionStats> _serverStats = [];
|
|
||||||
bool _isLoading = true;
|
|
||||||
bool _isCompacting = false;
|
|
||||||
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
super.initState();
|
|
||||||
_loadStats();
|
|
||||||
}
|
|
||||||
|
|
||||||
void _loadStats() {
|
|
||||||
setState(() {
|
|
||||||
_isLoading = true;
|
|
||||||
});
|
|
||||||
|
|
||||||
final stats = Stores.connectionStats.getAllServerStats();
|
|
||||||
setState(() {
|
|
||||||
_serverStats = stats;
|
|
||||||
_isLoading = false;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Scaffold(
|
|
||||||
appBar: CustomAppBar(
|
|
||||||
title: Text(l10n.connectionStats),
|
|
||||||
actions: [
|
|
||||||
IconButton(onPressed: _loadStats, icon: const Icon(Icons.refresh), tooltip: libL10n.refresh),
|
|
||||||
IconButton(
|
|
||||||
onPressed: _showClearAllDialog,
|
|
||||||
icon: const Icon(Icons.clear_all, color: Colors.red),
|
|
||||||
tooltip: libL10n.clear,
|
|
||||||
),
|
|
||||||
IconButton(
|
|
||||||
onPressed: _isCompacting ? null : _showCompactDialog,
|
|
||||||
icon: _isCompacting
|
|
||||||
? SizedLoading.small
|
|
||||||
: const Icon(Icons.compress),
|
|
||||||
tooltip: l10n.compactDatabase,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
body: _buildBody,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget get _buildBody {
|
|
||||||
if (_isLoading) {
|
|
||||||
return const Center(child: SizedLoading.large);
|
|
||||||
}
|
|
||||||
if (_serverStats.isEmpty) {
|
|
||||||
return Center(child: Text(l10n.noConnectionStatsData));
|
|
||||||
}
|
|
||||||
|
|
||||||
return ListView.builder(
|
|
||||||
itemCount: _serverStats.length,
|
|
||||||
itemBuilder: (context, index) {
|
|
||||||
final stats = _serverStats[index];
|
|
||||||
return _buildServerStatsCard(stats);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _buildServerStatsCard(ServerConnectionStats stats) {
|
|
||||||
final successRate = stats.totalAttempts == 0 ? 'N/A' : '${(stats.successRate * 100).toStringAsFixed(1)}%';
|
|
||||||
final lastSuccessTime = stats.lastSuccessTime;
|
|
||||||
final lastFailureTime = stats.lastFailureTime;
|
|
||||||
|
|
||||||
return Padding(
|
|
||||||
padding: const EdgeInsets.all(16),
|
|
||||||
child: Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Row(
|
|
||||||
children: [
|
|
||||||
Expanded(
|
|
||||||
child: Text(
|
|
||||||
stats.serverName,
|
|
||||||
style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Text(
|
|
||||||
'${libL10n.success}: $successRate',
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 16,
|
|
||||||
color: stats.successRate >= 0.8
|
|
||||||
? Colors.green
|
|
||||||
: stats.successRate >= 0.5
|
|
||||||
? Colors.orange
|
|
||||||
: Colors.red,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
const SizedBox(height: 12),
|
|
||||||
Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
|
||||||
children: [
|
|
||||||
_buildStatItem(libL10n.totalAttempts, stats.totalAttempts.toString(), Icons.all_inclusive),
|
|
||||||
_buildStatItem(
|
|
||||||
libL10n.success,
|
|
||||||
stats.successCount.toString(),
|
|
||||||
Icons.check_circle,
|
|
||||||
Colors.green,
|
|
||||||
),
|
|
||||||
_buildStatItem(libL10n.fail, stats.failureCount.toString(), Icons.error, Colors.red),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
if (lastSuccessTime != null || lastFailureTime != null) ...[
|
|
||||||
const SizedBox(height: 16),
|
|
||||||
const Divider(),
|
|
||||||
const SizedBox(height: 8),
|
|
||||||
if (lastSuccessTime != null)
|
|
||||||
_buildTimeItem(l10n.lastSuccess, lastSuccessTime, Icons.check_circle, Colors.green),
|
|
||||||
if (lastFailureTime != null)
|
|
||||||
_buildTimeItem(l10n.lastFailure, lastFailureTime, Icons.error, Colors.red),
|
|
||||||
],
|
|
||||||
const SizedBox(height: 16),
|
|
||||||
Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
||||||
children: [
|
|
||||||
Text(l10n.recentConnections, style: const TextStyle(fontSize: 14, fontWeight: FontWeight.bold)),
|
|
||||||
TextButton(onPressed: () => _showServerDetailsDialog(stats), child: Text(l10n.viewDetails)),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
const SizedBox(height: 8),
|
|
||||||
...stats.recentConnections.take(3).map(_buildConnectionItem),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
).cardx;
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _buildStatItem(String label, String value, IconData icon, [Color? color]) {
|
|
||||||
return Column(
|
|
||||||
children: [
|
|
||||||
Icon(icon, size: 24, color: color ?? Colors.grey),
|
|
||||||
const SizedBox(height: 4),
|
|
||||||
Text(
|
|
||||||
value,
|
|
||||||
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold, color: color),
|
|
||||||
),
|
|
||||||
Text(label, style: TextStyle(fontSize: 12, color: Colors.grey[600])),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _buildTimeItem(String label, DateTime time, IconData icon, Color color) {
|
|
||||||
final timeStr = time.simple();
|
|
||||||
return Padding(
|
|
||||||
padding: const EdgeInsets.symmetric(vertical: 4),
|
|
||||||
child: Row(
|
|
||||||
children: [
|
|
||||||
Icon(icon, size: 16, color: color),
|
|
||||||
UIs.width7,
|
|
||||||
Text('$label: ', style: TextStyle(fontSize: 12, color: Colors.grey[600])),
|
|
||||||
Text(timeStr, style: const TextStyle(fontSize: 12)),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _buildConnectionItem(ConnectionStat stat) {
|
|
||||||
final timeStr = stat.timestamp.simple();
|
|
||||||
final isSuccess = stat.result.isSuccess;
|
|
||||||
|
|
||||||
return Padding(
|
|
||||||
padding: const EdgeInsets.symmetric(vertical: 2),
|
|
||||||
child: Row(
|
|
||||||
children: [
|
|
||||||
Icon(
|
|
||||||
isSuccess ? Icons.check_circle : Icons.error,
|
|
||||||
size: 16,
|
|
||||||
color: isSuccess ? Colors.green : Colors.red,
|
|
||||||
),
|
|
||||||
UIs.width7,
|
|
||||||
Text(timeStr, style: const TextStyle(fontSize: 12)),
|
|
||||||
UIs.width7,
|
|
||||||
Expanded(
|
|
||||||
child: Text(
|
|
||||||
isSuccess ? '${libL10n.success} (${stat.durationMs}ms)' : stat.result.displayName,
|
|
||||||
style: TextStyle(fontSize: 12, color: isSuccess ? Colors.green : Colors.red),
|
|
||||||
overflow: TextOverflow.ellipsis,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
void _showCompactDialog() {
|
|
||||||
final path = '${Paths.doc}${Pfs.seperator}connection_stats_enc.hive';
|
|
||||||
final file = File(path);
|
|
||||||
final oldSize = file.existsSync() ? file.lengthSync() : 0;
|
|
||||||
final sizeStr = oldSize < 1000 ? '$oldSize B' : oldSize < 1000 * 1000 ? '${(oldSize / 1000).toStringAsFixed(1)} KB' : '${(oldSize / (1000 * 1000)).toStringAsFixed(1)} MB';
|
|
||||||
|
|
||||||
context.showRoundDialog(
|
|
||||||
title: l10n.compactDatabase,
|
|
||||||
child: Text(l10n.compactDatabaseContent(sizeStr)),
|
|
||||||
actions: [
|
|
||||||
TextButton(onPressed: context.pop, child: Text(libL10n.cancel)),
|
|
||||||
TextButton(
|
|
||||||
onPressed: () async {
|
|
||||||
context.pop();
|
|
||||||
setState(() => _isCompacting = true);
|
|
||||||
try {
|
|
||||||
await Stores.connectionStats.compact();
|
|
||||||
final newSize = file.existsSync() ? file.lengthSync() : 0;
|
|
||||||
final newSizeStr = newSize < 1000 ? '$newSize B' : newSize < 1000 * 1000 ? '${(newSize / 1000).toStringAsFixed(1)} KB' : '${(newSize / (1000 * 1000)).toStringAsFixed(1)} MB';
|
|
||||||
if (mounted) {
|
|
||||||
setState(() => _isCompacting = false);
|
|
||||||
context.showSnackBar('${libL10n.success}: $sizeStr -> $newSizeStr');
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
if (mounted) {
|
|
||||||
setState(() => _isCompacting = false);
|
|
||||||
context.showSnackBar('${libL10n.error}: $e');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
child: Text(libL10n.confirm),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension on _ConnectionStatsPageState {
|
|
||||||
void _showServerDetailsDialog(ServerConnectionStats stats) {
|
|
||||||
context.showRoundDialog(
|
|
||||||
title: '${stats.serverName} - ${l10n.connectionDetails}',
|
|
||||||
child: SizedBox(
|
|
||||||
width: double.maxFinite,
|
|
||||||
height: MediaQuery.sizeOf(context).height * 0.7,
|
|
||||||
child: ListView.separated(
|
|
||||||
itemCount: stats.recentConnections.length,
|
|
||||||
separatorBuilder: (context, index) => const Divider(),
|
|
||||||
itemBuilder: (context, index) {
|
|
||||||
final stat = stats.recentConnections[index];
|
|
||||||
final timeStr = stat.timestamp.simple();
|
|
||||||
final isSuccess = stat.result.isSuccess;
|
|
||||||
|
|
||||||
return ListTile(
|
|
||||||
contentPadding: EdgeInsets.zero,
|
|
||||||
leading: Icon(
|
|
||||||
isSuccess ? Icons.check_circle : Icons.error,
|
|
||||||
color: isSuccess ? Colors.green : Colors.red,
|
|
||||||
),
|
|
||||||
title: Text(timeStr),
|
|
||||||
subtitle: Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
isSuccess
|
|
||||||
? '${libL10n.success} (${stat.durationMs}ms)'
|
|
||||||
: '${libL10n.fail}: ${stat.result.displayName}',
|
|
||||||
style: TextStyle(color: isSuccess ? Colors.green : Colors.red),
|
|
||||||
),
|
|
||||||
if (!isSuccess && stat.errorMessage.isNotEmpty)
|
|
||||||
Text(
|
|
||||||
stat.errorMessage,
|
|
||||||
style: const TextStyle(fontSize: 11, color: Colors.grey),
|
|
||||||
maxLines: 2,
|
|
||||||
overflow: TextOverflow.ellipsis,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
actions: [
|
|
||||||
TextButton(onPressed: context.pop, child: Text(libL10n.close)),
|
|
||||||
TextButton(
|
|
||||||
onPressed: () {
|
|
||||||
Navigator.of(context).pop();
|
|
||||||
_showClearServerStatsDialog(stats);
|
|
||||||
},
|
|
||||||
child: Text(l10n.clearThisServerStats, style: TextStyle(color: Colors.red)),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
void _showClearAllDialog() {
|
|
||||||
context.showRoundDialog(
|
|
||||||
title: l10n.clearAllStatsTitle,
|
|
||||||
child: Text(l10n.clearAllStatsContent),
|
|
||||||
actions: [
|
|
||||||
TextButton(onPressed: context.pop, child: Text(libL10n.cancel)),
|
|
||||||
CountDownBtn(
|
|
||||||
onTap: () {
|
|
||||||
context.pop();
|
|
||||||
Stores.connectionStats.clearAll();
|
|
||||||
_loadStats();
|
|
||||||
},
|
|
||||||
text: libL10n.ok,
|
|
||||||
afterColor: Colors.red,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
void _showClearServerStatsDialog(ServerConnectionStats stats) {
|
|
||||||
context.showRoundDialog(
|
|
||||||
title: l10n.clearServerStatsTitle(stats.serverName),
|
|
||||||
child: Text(l10n.clearServerStatsContent(stats.serverName)),
|
|
||||||
actions: [
|
|
||||||
TextButton(onPressed: context.pop, child: Text(libL10n.cancel)),
|
|
||||||
CountDownBtn(
|
|
||||||
onTap: () {
|
|
||||||
context.pop();
|
|
||||||
Stores.connectionStats.clearServerStats(stats.serverId);
|
|
||||||
_loadStats();
|
|
||||||
},
|
|
||||||
text: libL10n.ok,
|
|
||||||
afterColor: Colors.red,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -21,7 +21,6 @@ import 'package:server_box/data/provider/server/all.dart';
|
|||||||
import 'package:server_box/data/provider/server/single.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/connection_stats.dart';
|
|
||||||
import 'package:server_box/view/page/server/detail/view.dart';
|
import 'package:server_box/view/page/server/detail/view.dart';
|
||||||
import 'package:server_box/view/page/server/edit/edit.dart';
|
import 'package:server_box/view/page/server/edit/edit.dart';
|
||||||
import 'package:server_box/view/page/setting/entry.dart';
|
import 'package:server_box/view/page/setting/entry.dart';
|
||||||
|
|||||||
@@ -42,10 +42,7 @@ final class _TopBar extends ConsumerWidget implements PreferredSizeWidget {
|
|||||||
}
|
}
|
||||||
final total = order.length;
|
final total = order.length;
|
||||||
final connectionText = '$connected/$total ${context.libL10n.conn}';
|
final connectionText = '$connected/$total ${context.libL10n.conn}';
|
||||||
leading = InkWell(
|
leading = Text(connectionText, style: const TextStyle(fontSize: 16, fontWeight: FontWeight.w600));
|
||||||
onTap: () => ConnectionStatsPage.route.go(context),
|
|
||||||
child: Text(connectionText, style: const TextStyle(fontSize: 16, fontWeight: FontWeight.w600)),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return Padding(
|
return Padding(
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ extension _Server on _AppSettingsPageState {
|
|||||||
_buildNetViewType(),
|
_buildNetViewType(),
|
||||||
_buildServerSeq(),
|
_buildServerSeq(),
|
||||||
_buildServerDetailCardSeq(),
|
_buildServerDetailCardSeq(),
|
||||||
_buildConnectionStats(),
|
|
||||||
_buildDeleteServers(),
|
_buildDeleteServers(),
|
||||||
_buildCpuView(),
|
_buildCpuView(),
|
||||||
_buildServerMore(),
|
_buildServerMore(),
|
||||||
@@ -39,18 +38,6 @@ extension _Server on _AppSettingsPageState {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildConnectionStats() {
|
|
||||||
return ListTile(
|
|
||||||
leading: const Icon(Icons.analytics, size: _kIconSize),
|
|
||||||
title: Text(l10n.connectionStats),
|
|
||||||
subtitle: Text(l10n.connectionStatsDesc),
|
|
||||||
trailing: const Icon(Icons.keyboard_arrow_right),
|
|
||||||
onTap: () {
|
|
||||||
ConnectionStatsPage.route.go(context);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _buildDeleteServers() {
|
Widget _buildDeleteServers() {
|
||||||
return ListTile(
|
return ListTile(
|
||||||
title: Text(l10n.deleteServers),
|
title: Text(l10n.deleteServers),
|
||||||
|
|||||||
@@ -18,7 +18,6 @@ import 'package:server_box/data/store/setting.dart';
|
|||||||
import 'package:server_box/generated/l10n/l10n.dart';
|
import 'package:server_box/generated/l10n/l10n.dart';
|
||||||
import 'package:server_box/view/page/backup.dart';
|
import 'package:server_box/view/page/backup.dart';
|
||||||
import 'package:server_box/view/page/private_key/list.dart';
|
import 'package:server_box/view/page/private_key/list.dart';
|
||||||
import 'package:server_box/view/page/server/connection_stats.dart';
|
|
||||||
import 'package:server_box/view/page/setting/entries/home_tabs.dart';
|
import 'package:server_box/view/page/setting/entries/home_tabs.dart';
|
||||||
import 'package:server_box/view/page/setting/platform/ios.dart';
|
import 'package:server_box/view/page/setting/platform/ios.dart';
|
||||||
import 'package:server_box/view/page/setting/platform/platform_pub.dart';
|
import 'package:server_box/view/page/setting/platform/platform_pub.dart';
|
||||||
|
|||||||
Reference in New Issue
Block a user