- Enhanced Flutter web app management in PowerShell scripts - Added Flutter web server to start-services.ps1 as 4th service - Updated stop-services.ps1 to stop Flutter web server - Improved service orchestration and startup sequence - Implemented server caching for improved resilience - Added ServerCacheService for browser localStorage caching - Server lists persist across service restarts - Automatic fallback to cached data when API unavailable - Action picker categories always visible regardless of server status - Redesigned Action Mappings view with compact table layout - Replaced card-based ListView with DataTable for higher density - Added real-time search across name, input, output, description - Implemented multi-filter support (status: enabled/disabled) - Added column sorting (name, input, output, status, executions) - Batch operations: select all/multiple, batch delete - Reduced row height from ~120px to 56px for better overview - Redesigned Servers Management view with compact table layout - Replaced card-based ListView with DataTable - Added search by alias, host, user - Multi-filter support (type: all/G-Core/GeViScope, status: all/enabled/disabled) - Column sorting (alias, host, user, type, status) - Batch operations: select all/multiple, batch delete - Color-coded type and status badges - Improved action picker dialog for GSC/G-Core actions - GSC and G-Core categories always visible - Server validation with clear error messages - Fixed duplicate checkbox issue in table headers - Debug logging for troubleshooting server parameter issues 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
96 lines
3.5 KiB
Dart
96 lines
3.5 KiB
Dart
import 'dart:convert';
|
|
import 'package:shared_preferences/shared_preferences.dart';
|
|
import '../models/action_template.dart';
|
|
|
|
/// Service for caching server lists to prevent categories from disappearing
|
|
/// when SDK Bridge is temporarily unavailable during service restarts
|
|
class ServerCacheService {
|
|
static const String _gcoreServersKey = 'cached_gcore_servers';
|
|
static const String _gscServersKey = 'cached_gsc_servers';
|
|
static const String _cacheTimestampKey = 'server_cache_timestamp';
|
|
|
|
/// Save G-Core servers to cache
|
|
static Future<void> cacheGCoreServers(List<ServerInfo> servers) async {
|
|
final prefs = await SharedPreferences.getInstance();
|
|
final json = jsonEncode(servers.map((s) => s.toJson()).toList());
|
|
await prefs.setString(_gcoreServersKey, json);
|
|
await prefs.setInt(_cacheTimestampKey, DateTime.now().millisecondsSinceEpoch);
|
|
print('[ServerCache] Cached ${servers.length} G-Core servers');
|
|
}
|
|
|
|
/// Save GSC servers to cache
|
|
static Future<void> cacheGSCServers(List<ServerInfo> servers) async {
|
|
final prefs = await SharedPreferences.getInstance();
|
|
final json = jsonEncode(servers.map((s) => s.toJson()).toList());
|
|
await prefs.setString(_gscServersKey, json);
|
|
await prefs.setInt(_cacheTimestampKey, DateTime.now().millisecondsSinceEpoch);
|
|
print('[ServerCache] Cached ${servers.length} GSC servers');
|
|
}
|
|
|
|
/// Save both server lists
|
|
static Future<void> cacheServers({
|
|
required List<ServerInfo> gcoreServers,
|
|
required List<ServerInfo> gscServers,
|
|
}) async {
|
|
await cacheGCoreServers(gcoreServers);
|
|
await cacheGSCServers(gscServers);
|
|
}
|
|
|
|
/// Load cached G-Core servers
|
|
static Future<List<ServerInfo>> getCachedGCoreServers() async {
|
|
final prefs = await SharedPreferences.getInstance();
|
|
final json = prefs.getString(_gcoreServersKey);
|
|
if (json == null) {
|
|
print('[ServerCache] No cached G-Core servers found');
|
|
return [];
|
|
}
|
|
|
|
try {
|
|
final list = jsonDecode(json) as List<dynamic>;
|
|
final servers = list.map((item) => ServerInfo.fromJson(item as Map<String, dynamic>)).toList();
|
|
print('[ServerCache] Loaded ${servers.length} cached G-Core servers');
|
|
return servers;
|
|
} catch (e) {
|
|
print('[ServerCache] Error loading cached G-Core servers: $e');
|
|
return [];
|
|
}
|
|
}
|
|
|
|
/// Load cached GSC servers
|
|
static Future<List<ServerInfo>> getCachedGSCServers() async {
|
|
final prefs = await SharedPreferences.getInstance();
|
|
final json = prefs.getString(_gscServersKey);
|
|
if (json == null) {
|
|
print('[ServerCache] No cached GSC servers found');
|
|
return [];
|
|
}
|
|
|
|
try {
|
|
final list = jsonDecode(json) as List<dynamic>;
|
|
final servers = list.map((item) => ServerInfo.fromJson(item as Map<String, dynamic>)).toList();
|
|
print('[ServerCache] Loaded ${servers.length} cached GSC servers');
|
|
return servers;
|
|
} catch (e) {
|
|
print('[ServerCache] Error loading cached GSC servers: $e');
|
|
return [];
|
|
}
|
|
}
|
|
|
|
/// Get cache age in milliseconds
|
|
static Future<int?> getCacheAge() async {
|
|
final prefs = await SharedPreferences.getInstance();
|
|
final timestamp = prefs.getInt(_cacheTimestampKey);
|
|
if (timestamp == null) return null;
|
|
return DateTime.now().millisecondsSinceEpoch - timestamp;
|
|
}
|
|
|
|
/// Clear all cached server data
|
|
static Future<void> clearCache() async {
|
|
final prefs = await SharedPreferences.getInstance();
|
|
await prefs.remove(_gcoreServersKey);
|
|
await prefs.remove(_gscServersKey);
|
|
await prefs.remove(_cacheTimestampKey);
|
|
print('[ServerCache] Cache cleared');
|
|
}
|
|
}
|