- migration 013: create languages table (code PK, native_name, english_name, is_active, sort_order) with all 12 existing languages seeded - locale: add Language struct, Languages []Language, LoadFromDB() — queries languages table at startup and populates both Supported map and Languages slice; existing Parse/FromContext/FromRequest unchanged - main.go: call locale.LoadFromDB after pool is ready - gemini/recipe.go: remove hardcoded langNames map, use locale.Languages linear lookup for English name in prompt - language/handler.go: new package with GET /languages handler returning active languages list (no auth required) - server.go: register GET /languages as public route - Flutter: add LanguageRepository + languageRepositoryProvider that fetches /languages from backend - language_provider.dart: replace const supportedLanguages map with supportedLanguagesProvider (FutureProvider) backed by LanguageRepository - profile_provider.dart: remove supportedLanguages.containsKey validation — backend is source of truth; sync any non-empty language from preferences - profile_screen.dart: use supportedLanguagesProvider for display name and dropdown (async with loading/error states) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
50 lines
1.3 KiB
Dart
50 lines
1.3 KiB
Dart
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|
|
|
import '../../core/locale/language_provider.dart';
|
|
import '../../shared/models/user.dart';
|
|
import 'profile_service.dart';
|
|
|
|
|
|
class ProfileNotifier extends StateNotifier<AsyncValue<User>> {
|
|
final ProfileService _service;
|
|
final void Function(String) _setLanguage;
|
|
|
|
ProfileNotifier(this._service, this._setLanguage)
|
|
: super(const AsyncValue.loading()) {
|
|
load();
|
|
}
|
|
|
|
Future<void> load() async {
|
|
state = const AsyncValue.loading();
|
|
state = await AsyncValue.guard(() => _service.getProfile());
|
|
_syncLanguage();
|
|
}
|
|
|
|
Future<bool> update(UpdateProfileRequest req) async {
|
|
try {
|
|
final updated = await _service.updateProfile(req);
|
|
state = AsyncValue.data(updated);
|
|
_syncLanguage();
|
|
return true;
|
|
} catch (_) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// Propagates the user's preferred language to the global languageProvider.
|
|
void _syncLanguage() {
|
|
final lang = state.valueOrNull?.preferences['language'] as String?;
|
|
if (lang != null && lang.isNotEmpty) {
|
|
_setLanguage(lang);
|
|
}
|
|
}
|
|
}
|
|
|
|
final profileProvider =
|
|
StateNotifierProvider<ProfileNotifier, AsyncValue<User>>(
|
|
(ref) => ProfileNotifier(
|
|
ref.read(profileServiceProvider),
|
|
(lang) => ref.read(languageProvider.notifier).state = lang,
|
|
),
|
|
);
|