mnemo_cards/mnemo_cards_web_v2/lib/app.dart
Dmitry 889784e11f
Some checks are pending
Backend CI / test (push) Waiting to run
Backend CI / build (push) Blocked by required conditions
Web App CI / test (push) Waiting to run
Web App CI / build (push) Blocked by required conditions
Deploy Admin Panel / Deploy Admin Panel (push) Waiting to run
Deploy Admin Panel / Admin Panel Verification (push) Blocked by required conditions
Deploy Mnemo Cards / Deploy Backend (push) Waiting to run
Deploy Mnemo Cards / Deploy Web App (push) Blocked by required conditions
Deploy Mnemo Cards / Final Verification (push) Blocked by required conditions
stuff
2025-12-14 22:21:36 +03:00

228 lines
6.9 KiB
Dart

import 'dart:developer';
import 'package:flutter/material.dart';
import 'package:mnemo_cards_web_v2/di/user_scope/user_scope.dart';
import 'package:telegram_web_app/telegram_web_app.dart';
import 'package:yx_scope_flutter/yx_scope_flutter.dart';
import 'package:yx_state_flutter/yx_state_flutter.dart';
import 'di/app_scope/app_scope_container.dart';
import 'di/app_scope/app_scope_holder.dart';
import 'domain/state/theme_state_manager.dart';
import 'main.dart' show scaffoldMessengerKey;
import 'presentation/theme/app_theme.dart';
import 'package:mnemo_cards_common/mnemo_cards_common.dart';
/// Main application widget
///
/// Wraps the entire app in ScopeProviders for AppScope and UserScope
class App extends StatelessWidget {
const App({
required this.appScopeHolder,
super.key,
});
final AppScopeHolder appScopeHolder;
@override
Widget build(BuildContext context) {
return ScopeProvider<AppScopeContainer>(
holder: appScopeHolder,
child: ScopeBuilder<AppScopeContainer>.withPlaceholder(
builder: (context, appScope) {
return _AppInitializer(appScope: appScope);
},
placeholder: const _LoadingScreen(message: 'Инициализация...'),
),
);
}
}
/// Initializes the app and handles auto-login
class _AppInitializer extends StatefulWidget {
const _AppInitializer({required this.appScope});
final AppScopeContainer appScope;
@override
State<_AppInitializer> createState() => _AppInitializerState();
}
class _AppInitializerState extends State<_AppInitializer> {
bool _isInitialized = false;
@override
void initState() {
super.initState();
_initialize();
}
@override
void didUpdateWidget(_AppInitializer oldWidget) {
super.didUpdateWidget(oldWidget);
// Rebuild when UserScope changes
if (mounted) {
setState(() {});
}
}
Future<void> _tryTelegramWebAppAuth() async {
try {
log('Checking Telegram Web App authentication...', name: 'App');
// Try to authenticate with Telegram Web App
final user = await widget.appScope.authService.loginWithTelegramWebApp();
if (user != null) {
log('Telegram Web App authentication successful', name: 'App');
// Create UserScope if it doesn't exist
if (widget.appScope.userScopeHolder.scope == null) {
await widget.appScope.userScopeHolder.create();
}
// Set authenticated user
final userScope = widget.appScope.userScopeHolder.scope;
if (userScope != null) {
await userScope.userStateManager.setUser(user);
// Notify router about auth change
widget.appScope.userScopeHolder.notifyAuthChanged();
}
// Don't continue with normal auto-login if Telegram auth succeeded
return;
} else {
log('Telegram Web App authentication not available or failed', name: 'App');
}
} catch (e, s) {
log('Error during Telegram Web App authentication', error: e, stackTrace: s);
// Continue with normal flow - this is not a critical error
}
}
Future<void> _initialize() async {
try {
log('Starting app initialization...', name: 'App');
// Try Telegram Web App authentication first
await _tryTelegramWebAppAuth();
// Try auto-login first with timeout
log('Attempting auto-login...', name: 'App');
UserDto? user;
try {
user = await widget.appScope.authService.autoLogin().timeout(
const Duration(seconds: 3),
onTimeout: () {
log('Auto-login timed out, continuing as guest', name: 'App');
return null;
},
);
} catch (e) {
log('Auto-login failed with error, continuing as guest', error: e, name: 'App');
user = null;
}
if (widget.appScope.userScopeHolder.scope == null) {
log('Creating UserScope...', name: 'App');
await widget.appScope.userScopeHolder.create();
}
if (user != null) {
log('Auto-login successful, creating UserScope', name: 'App');
// Create UserScope only for authenticated users
await widget.appScope.userScopeHolder.scope!.userStateManager.setUser(user);
// Notify router about auth change
widget.appScope.userScopeHolder.notifyAuthChanged();
log('UserScope created and user set', name: 'App');
} else {
log('No saved session, starting as guest without UserScope', name: 'App');
}
log('Setting _isInitialized = true', name: 'App');
setState(() {
_isInitialized = true;
});
log('App initialization completed', name: 'App');
} catch (e) {
log('Error during initialization', error: e, name: 'App');
// Continue as guest even if auto-login fails
log('Continuing as guest after error', name: 'App');
setState(() {
_isInitialized = true;
});
}
}
@override
Widget build(BuildContext context) {
log('_AppInitializer build called, _isInitialized: $_isInitialized', name: 'App');
if (!_isInitialized) {
log('Showing loading screen', name: 'App');
return const _LoadingScreen(message: 'Loading...');
}
log('Building main app', name: 'App');
return StateBuilder(
stateReadable: widget.appScope.themeManager,
builder: (context, themeState, _) {
log('StateBuilder builder called with themeState: ${themeState.mode}', name: 'App');
return _buildAppWithUserScope(themeState);
},
);
}
Widget _buildAppWithUserScope(ThemeState themeState) {
Widget app = MaterialApp.router(
title: 'Mnemo Cards',
routerConfig: widget.appScope.router,
theme: AppTheme.light,
darkTheme: AppTheme.dark,
themeMode: themeState.mode,
scaffoldMessengerKey: scaffoldMessengerKey,
debugShowCheckedModeBanner: false,
);
// Wrap with UserScope provider only if UserScope exists
if (widget.appScope.userScopeHolder.scope != null) {
return ScopeProvider<UserScope>(
holder: widget.appScope.userScopeHolder,
child: app,
);
}
// For guest users, return app without UserScope provider
return app;
}
}
/// Экран загрузки
class _LoadingScreen extends StatelessWidget {
const _LoadingScreen({required this.message});
final String message;
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
backgroundColor: Theme.of(context).colorScheme.brightness == Brightness.dark ? AppTheme.dark.scaffoldBackgroundColor : AppTheme.light.scaffoldBackgroundColor,
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const CircularProgressIndicator(),
const SizedBox(height: 24),
Text(
message,
style: const TextStyle(fontSize: 18),
),
],
),
),
),
);
}
}