173 lines
5.1 KiB
Dart
173 lines
5.1 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:flutter_localizations/flutter_localizations.dart';
|
|
|
|
import '../core/auth/session_store.dart';
|
|
import '../data/network/api_client.dart';
|
|
import '../data/storage/local_kv_store.dart';
|
|
import '../features/auth/data/apis/auth_api.dart';
|
|
import '../features/auth/data/repositories/auth_repository.dart';
|
|
import '../features/auth/presentation/bloc/auth_bloc.dart';
|
|
import '../features/auth/presentation/bloc/auth_state.dart';
|
|
import '../features/auth/presentation/screens/login_screen.dart';
|
|
import '../features/divination/data/apis/divination_api.dart';
|
|
import '../features/home/presentation/screens/home_screen.dart';
|
|
import '../features/settings/data/models/profile_settings.dart';
|
|
import '../l10n/app_localizations.dart';
|
|
import '../shared/widgets/app_loading_indicator.dart';
|
|
import 'app_theme.dart';
|
|
import 'di/injection.dart';
|
|
|
|
class EryaoApp extends StatefulWidget {
|
|
const EryaoApp({super.key});
|
|
|
|
@override
|
|
State<EryaoApp> createState() => _EryaoAppState();
|
|
}
|
|
|
|
class _EryaoAppState extends State<EryaoApp> {
|
|
final SessionStore _sessionStore = SessionStore(LocalKvStore());
|
|
late final AuthBloc _authBloc;
|
|
late final DivinationApi _divinationApi;
|
|
Locale _locale = const Locale('zh');
|
|
ProfileSettingsV1 _profileSettings = ProfileSettingsV1.defaultsForLocale(
|
|
const Locale('zh'),
|
|
);
|
|
int _creditsBalance = 0;
|
|
bool _loadingCredits = false;
|
|
String? _loadedCreditsUserEmail;
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
final apiClient = ApiClient(
|
|
baseUrl: appDependencies.backendUrl,
|
|
tokenProvider: _sessionStore.getToken,
|
|
onUnauthorized: () {
|
|
return _authBloc.handleUnauthorized401();
|
|
},
|
|
);
|
|
final authApi = AuthApi(apiClient: apiClient);
|
|
_divinationApi = DivinationApi(apiClient: apiClient);
|
|
final authRepository = AuthRepositoryImpl(
|
|
authApi: authApi,
|
|
sessionStore: _sessionStore,
|
|
);
|
|
_authBloc = AuthBloc(repository: authRepository);
|
|
_bootstrap();
|
|
}
|
|
|
|
void _ensureCreditsLoaded(String userEmail) {
|
|
if (_loadingCredits) {
|
|
return;
|
|
}
|
|
if (_loadedCreditsUserEmail == userEmail) {
|
|
return;
|
|
}
|
|
_loadingCredits = true;
|
|
_divinationApi
|
|
.getPointsBalance()
|
|
.then((balance) {
|
|
if (!mounted) {
|
|
return;
|
|
}
|
|
setState(() {
|
|
_creditsBalance = balance.availableBalance;
|
|
_loadedCreditsUserEmail = userEmail;
|
|
});
|
|
})
|
|
.whenComplete(() {
|
|
_loadingCredits = false;
|
|
});
|
|
}
|
|
|
|
@override
|
|
void dispose() {
|
|
_authBloc.dispose();
|
|
super.dispose();
|
|
}
|
|
|
|
Future<void> _bootstrap() async {
|
|
final localeCode = await _sessionStore.getLocaleCode();
|
|
final locale = localeCode == 'en' ? const Locale('en') : const Locale('zh');
|
|
if (mounted) {
|
|
setState(() {
|
|
_locale = locale;
|
|
_profileSettings = ProfileSettingsV1.defaultsForLocale(locale);
|
|
});
|
|
}
|
|
await _authBloc.start();
|
|
}
|
|
|
|
Future<void> _handleInterfaceLanguageChanged(String languageTag) async {
|
|
final locale = localeFromLanguageTag(languageTag);
|
|
await _sessionStore.saveLocaleCode(locale.languageCode);
|
|
if (!mounted) {
|
|
return;
|
|
}
|
|
setState(() {
|
|
_locale = locale;
|
|
_profileSettings = _profileSettings.copyWith(
|
|
preferences: _profileSettings.preferences.copyWith(
|
|
interfaceLanguage: languageTag,
|
|
),
|
|
);
|
|
});
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return AnimatedBuilder(
|
|
animation: _authBloc,
|
|
builder: (context, _) {
|
|
return MaterialApp(
|
|
debugShowCheckedModeBanner: false,
|
|
onGenerateTitle: (context) => AppLocalizations.of(context)!.appTitle,
|
|
locale: _locale,
|
|
localizationsDelegates: const [
|
|
AppLocalizations.delegate,
|
|
GlobalMaterialLocalizations.delegate,
|
|
GlobalWidgetsLocalizations.delegate,
|
|
GlobalCupertinoLocalizations.delegate,
|
|
],
|
|
supportedLocales: AppLocalizations.supportedLocales,
|
|
theme: AppTheme.light(),
|
|
home: _buildHomeByAuthState(_authBloc.state),
|
|
);
|
|
},
|
|
);
|
|
}
|
|
|
|
Widget _buildHomeByAuthState(AuthState state) {
|
|
if (state.status == AuthStatus.initial ||
|
|
state.status == AuthStatus.loading) {
|
|
return const Scaffold(
|
|
body: Center(
|
|
child: AppLoadingIndicator(variant: AppLoadingVariant.surface),
|
|
),
|
|
);
|
|
}
|
|
|
|
if (state.status == AuthStatus.authenticated && state.user != null) {
|
|
_ensureCreditsLoaded(state.user!.email);
|
|
return HomeScreen(
|
|
account: state.user!.email,
|
|
sessionStore: _sessionStore,
|
|
currentLocale: _locale,
|
|
profileSettings: _profileSettings,
|
|
coinBalance: _creditsBalance,
|
|
onLocaleChanged: _handleInterfaceLanguageChanged,
|
|
onLogout: _authBloc.logout,
|
|
);
|
|
}
|
|
|
|
return LoginScreen(
|
|
currentLocale: _locale,
|
|
onLocaleChanged: (_) {},
|
|
onRequestOtp: _authBloc.sendOtp,
|
|
onLoginWithOtp: (email, otp) {
|
|
return _authBloc.loginWithOtp(email: email, otp: otp);
|
|
},
|
|
);
|
|
}
|
|
}
|