Intro mode in Flutter client fixes

This commit is contained in:
Juan José Mata
2026-02-16 02:17:03 +01:00
parent f5469ea774
commit eb0d05a7fb
4 changed files with 70 additions and 13 deletions

View File

@@ -23,15 +23,42 @@ class User {
email: json['email'] as String,
firstName: json['first_name'] as String?,
lastName: json['last_name'] as String?,
uiLayout: (json['ui_layout'] as String?) ?? 'dashboard',
// Default to true when key is absent (legacy payloads from older app versions).
// Avoids regressing existing users who would otherwise be incorrectly gated.
aiEnabled: json.containsKey('ai_enabled')
? (json['ai_enabled'] == true)
: true,
uiLayout: _coerceUiLayout(json['ui_layout'] ?? json['uiLayout']),
aiEnabled: _coerceBool(json['ai_enabled'] ?? json['aiEnabled'], defaultValue: false),
);
}
static String _coerceUiLayout(dynamic value) {
if (value is String) {
final layout = value.trim();
if (layout.isNotEmpty) return layout;
}
return 'dashboard';
}
static bool _coerceBool(dynamic value, {required bool defaultValue}) {
if (value == null) {
return defaultValue;
}
if (value is bool) return value;
if (value is num) return value != 0;
if (value is String) {
switch (value.trim().toLowerCase()) {
case "true":
case "1":
case "yes":
return true;
case "false":
case "0":
case "no":
return false;
}
}
return defaultValue;
}
User copyWith({
String? id,
String? email,

View File

@@ -52,7 +52,7 @@ class _MainNavigationScreenState extends State<MainNavigationScreen> {
const NavigationDestination(
icon: Icon(Icons.chat_bubble_outline),
selectedIcon: Icon(Icons.chat_bubble),
label: 'AI Chat',
label: 'Assistant',
),
);

View File

@@ -180,10 +180,17 @@ class _SettingsScreenState extends State<SettingsScreen> {
),
// App version
const ListTile(
leading: Icon(Icons.info_outline),
title: Text('App Version'),
subtitle: Text('1.0.0'),
ListTile(
leading: const Icon(Icons.info_outline),
title: const Text('App Version: 0.6.8'),
subtitle: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
Text(' > ui_layout: ${authProvider.user?.uiLayout}'),
Text(' > ai_enabled: ${authProvider.user?.aiEnabled}'),
],
),
),
const Divider(),

View File

@@ -56,7 +56,9 @@ class AuthService {
// Store user data - parse once and reuse
User? user;
if (responseData['user'] != null) {
user = User.fromJson(responseData['user']);
final rawUser = responseData['user'];
_logRawUserPayload('login', rawUser);
user = User.fromJson(rawUser);
await _saveUser(user);
}
@@ -160,7 +162,9 @@ class AuthService {
// Store user data - parse once and reuse
User? user;
if (responseData['user'] != null) {
user = User.fromJson(responseData['user']);
final rawUser = responseData['user'];
_logRawUserPayload('signup', rawUser);
user = User.fromJson(rawUser);
await _saveUser(user);
}
@@ -406,6 +410,7 @@ class AuthService {
});
await _saveTokens(tokens);
_logRawUserPayload('sso_exchange', data['user']);
final user = User.fromJson(data['user']);
await _saveUser(user);
@@ -451,6 +456,7 @@ class AuthService {
final responseData = jsonDecode(response.body);
if (response.statusCode == 200) {
_logRawUserPayload('enable_ai', responseData['user']);
final user = User.fromJson(responseData['user']);
await _saveUser(user);
return {
@@ -514,6 +520,23 @@ class AuthService {
);
}
void _logRawUserPayload(String source, dynamic userPayload) {
if (userPayload == null) {
LogService.instance.debug('AuthService', '$source user payload: <missing>');
return;
}
if (userPayload is Map<String, dynamic>) {
try {
LogService.instance.debug('AuthService', '$source user payload: ${jsonEncode(userPayload)}');
} catch (_) {
LogService.instance.debug('AuthService', '$source user payload: $userPayload');
}
} else {
LogService.instance.debug('AuthService', '$source user payload type: ${userPayload.runtimeType}');
}
}
Future<void> _saveApiKey(String apiKey) async {
await _storage.write(key: _apiKeyKey, value: apiKey);
await _storage.write(key: _authModeKey, value: 'api_key');