import 'package:flutter/foundation.dart'; import 'dart:convert'; import '../models/app_models.dart'; import 'runtime_models_connection.dart'; const int appUiStateSchemaVersion = 2; class AppUiState { const AppUiState({ required this.schemaVersion, required this.assistantLastSessionKey, required this.assistantNavigationDestinations, required this.savedGatewayTargets, }); final int schemaVersion; final String assistantLastSessionKey; final List assistantNavigationDestinations; final List savedGatewayTargets; factory AppUiState.defaults() { return const AppUiState( schemaVersion: appUiStateSchemaVersion, assistantLastSessionKey: '', assistantNavigationDestinations: kAssistantNavigationDestinationDefaults, savedGatewayTargets: [], ); } AppUiState copyWith({ int? schemaVersion, String? assistantLastSessionKey, List? assistantNavigationDestinations, List? savedGatewayTargets, }) { return AppUiState( schemaVersion: schemaVersion ?? this.schemaVersion, assistantLastSessionKey: assistantLastSessionKey ?? this.assistantLastSessionKey, assistantNavigationDestinations: assistantNavigationDestinations ?? this.assistantNavigationDestinations, savedGatewayTargets: normalizeSavedGatewayTargets( savedGatewayTargets ?? this.savedGatewayTargets, ), ); } Map toJson() { return { 'schemaVersion': schemaVersion, 'assistantLastSessionKey': assistantLastSessionKey, 'assistantNavigationDestinations': assistantNavigationDestinations .map((item) => item.name) .toList(growable: false), 'savedGatewayTargets': savedGatewayTargets, }; } factory AppUiState.fromJson(Map json) { final parsedSchemaVersion = (json['schemaVersion'] as num?)?.toInt() ?? -1; if (parsedSchemaVersion != appUiStateSchemaVersion) { throw const FormatException('Unsupported app ui state schema version.'); } final rawAssistantNavigationDestinations = json['assistantNavigationDestinations']; final assistantNavigationDestinations = rawAssistantNavigationDestinations is List ? normalizeAssistantNavigationDestinations( rawAssistantNavigationDestinations .map( (item) => AssistantFocusEntryCopy.fromJsonValue(item?.toString()), ) .whereType(), ) : kAssistantNavigationDestinationDefaults; return AppUiState( schemaVersion: parsedSchemaVersion, assistantLastSessionKey: json['assistantLastSessionKey'] as String? ?? '', assistantNavigationDestinations: assistantNavigationDestinations, savedGatewayTargets: normalizeSavedGatewayTargets( (json['savedGatewayTargets'] as List? ?? const []).map( (item) => item?.toString() ?? '', ), ), ); } static AppUiState fromJsonString(String? raw) { if (raw == null || raw.trim().isEmpty) { return AppUiState.defaults(); } try { final decoded = jsonDecode(raw); if (decoded is! Map) { return AppUiState.defaults(); } return AppUiState.fromJson(decoded); } catch (e, stackTrace) { debugPrint('Error: $e\n$stackTrace'); return AppUiState.defaults(); } } String toJsonString() => jsonEncode(toJson()); bool isGatewayTargetSaved(AssistantExecutionTarget target) { const targetKey = 'gateway'; return targetKey.isNotEmpty && savedGatewayTargets.contains(targetKey); } AppUiState markGatewayTargetSaved(AssistantExecutionTarget target) { const targetKey = 'gateway'; if (targetKey.isEmpty || savedGatewayTargets.contains(targetKey)) { return this; } return copyWith( savedGatewayTargets: [...savedGatewayTargets, targetKey], ); } } List normalizeSavedGatewayTargets(Iterable rawTargets) { final normalized = []; final seen = {}; for (final item in rawTargets) { final normalizedTarget = item.trim().toLowerCase(); if (normalizedTarget != 'gateway' || !seen.add(normalizedTarget)) { continue; } normalized.add(normalizedTarget); } return List.unmodifiable(normalized); }