stuff
This commit is contained in:
parent
1bfec9d511
commit
9157706cad
2 changed files with 53 additions and 174 deletions
|
|
@ -6,7 +6,6 @@ import 'package:yx_scope_flutter/yx_scope_flutter.dart';
|
||||||
|
|
||||||
import 'package:mnemo_cards_web_v2/di/app_scope/app_scope_container.dart';
|
import 'package:mnemo_cards_web_v2/di/app_scope/app_scope_container.dart';
|
||||||
import 'package:mnemo_cards_web_v2/domain/config/api_config_v2.dart';
|
import 'package:mnemo_cards_web_v2/domain/config/api_config_v2.dart';
|
||||||
import 'package:mnemo_cards_web_v2/presentation/pages/auth/sign_in_with_google_button.dart';
|
|
||||||
import 'package:mnemo_cards_web_v2/presentation/pages/auth/sign_in_with_telegram.dart';
|
import 'package:mnemo_cards_web_v2/presentation/pages/auth/sign_in_with_telegram.dart';
|
||||||
|
|
||||||
/// Authentication page
|
/// Authentication page
|
||||||
|
|
@ -23,75 +22,6 @@ class _AuthPageState extends State<AuthPage> {
|
||||||
bool _isLoading = false;
|
bool _isLoading = false;
|
||||||
String? _errorMessage;
|
String? _errorMessage;
|
||||||
|
|
||||||
Future<void> _loginWithGoogle(BuildContext context) async {
|
|
||||||
setState(() {
|
|
||||||
_isLoading = true;
|
|
||||||
_errorMessage = null;
|
|
||||||
});
|
|
||||||
|
|
||||||
// Get router before async operations to avoid context issues
|
|
||||||
final router = GoRouter.of(context);
|
|
||||||
|
|
||||||
try {
|
|
||||||
final appScope = ScopeProvider.of<AppScopeContainer>(
|
|
||||||
context,
|
|
||||||
listen: false,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (appScope == null) {
|
|
||||||
throw Exception('AppScope not available');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Login via Google
|
|
||||||
final user = await appScope.authService.loginWithGoogle();
|
|
||||||
|
|
||||||
if (!mounted) return;
|
|
||||||
|
|
||||||
if (user != null) {
|
|
||||||
log('Google login successful: ${user.email}', name: 'AuthPage');
|
|
||||||
|
|
||||||
// Create UserScope if it doesn't exist
|
|
||||||
if (appScope.userScopeHolder.scope == null) {
|
|
||||||
await appScope.userScopeHolder.create();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update user state and wait for it to complete
|
|
||||||
await appScope.userScopeHolder.scope!.userStateManager.setUser(user);
|
|
||||||
|
|
||||||
// Notify that UserScope has changed
|
|
||||||
appScope.notifyUserScopeChanged();
|
|
||||||
|
|
||||||
// Notify router about auth change
|
|
||||||
appScope.userScopeHolder.notifyAuthChanged();
|
|
||||||
|
|
||||||
// Router will automatically redirect to /home when authentication state changes
|
|
||||||
// Force router refresh to ensure redirect happens
|
|
||||||
if (mounted) {
|
|
||||||
// Small delay to ensure state is fully updated
|
|
||||||
await Future<void>.delayed(const Duration(milliseconds: 50));
|
|
||||||
if (mounted && appScope.userScopeHolder.isAuthenticated) {
|
|
||||||
router.go('/home');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
setState(() {
|
|
||||||
_errorMessage = 'Login cancelled';
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} catch (e, s) {
|
|
||||||
log('Error in Google login', error: e, stackTrace: s, name: 'AuthPage');
|
|
||||||
if (mounted) {
|
|
||||||
setState(() {
|
|
||||||
_errorMessage = 'Login failed: ${e.toString()}';
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
if (mounted) {
|
|
||||||
setState(() => _isLoading = false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> _onTelegramLoginSuccess(BuildContext context) async {
|
Future<void> _onTelegramLoginSuccess(BuildContext context) async {
|
||||||
// Router will automatically redirect to /home when authentication state changes
|
// Router will automatically redirect to /home when authentication state changes
|
||||||
// No manual navigation needed - let the GoRouter handle the redirect
|
// No manual navigation needed - let the GoRouter handle the redirect
|
||||||
|
|
@ -115,7 +45,7 @@ class _AuthPageState extends State<AuthPage> {
|
||||||
backgroundColor: theme.colorScheme.surface,
|
backgroundColor: theme.colorScheme.surface,
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: Text(
|
title: Text(
|
||||||
'Login',
|
'Вход',
|
||||||
style: theme.textTheme.titleLarge?.copyWith(
|
style: theme.textTheme.titleLarge?.copyWith(
|
||||||
fontWeight: FontWeight.w700,
|
fontWeight: FontWeight.w700,
|
||||||
),
|
),
|
||||||
|
|
@ -149,7 +79,7 @@ class _AuthPageState extends State<AuthPage> {
|
||||||
),
|
),
|
||||||
const SizedBox(height: 24),
|
const SizedBox(height: 24),
|
||||||
Text(
|
Text(
|
||||||
'Sign in to your account',
|
'Войдите в свой аккаунт',
|
||||||
style: theme.textTheme.headlineMedium?.copyWith(
|
style: theme.textTheme.headlineMedium?.copyWith(
|
||||||
fontWeight: FontWeight.w700,
|
fontWeight: FontWeight.w700,
|
||||||
color: theme.colorScheme.onSurface,
|
color: theme.colorScheme.onSurface,
|
||||||
|
|
@ -158,7 +88,7 @@ class _AuthPageState extends State<AuthPage> {
|
||||||
),
|
),
|
||||||
const SizedBox(height: 8),
|
const SizedBox(height: 8),
|
||||||
Text(
|
Text(
|
||||||
'Save your progress and sync data',
|
'Сохраняйте прогресс и синхронизируйте данные',
|
||||||
style: theme.textTheme.bodyMedium?.copyWith(
|
style: theme.textTheme.bodyMedium?.copyWith(
|
||||||
fontWeight: FontWeight.w700,
|
fontWeight: FontWeight.w700,
|
||||||
color: theme.colorScheme.onSurfaceVariant,
|
color: theme.colorScheme.onSurfaceVariant,
|
||||||
|
|
@ -205,40 +135,6 @@ class _AuthPageState extends State<AuthPage> {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
// Login buttons
|
|
||||||
// SignInWithGoogleButton(
|
|
||||||
// onPressed: () => _loginWithGoogle(context),
|
|
||||||
// isLoading: _isLoading,
|
|
||||||
// ),
|
|
||||||
// const SizedBox(height: 24),
|
|
||||||
// Divider
|
|
||||||
Row(
|
|
||||||
children: [
|
|
||||||
Expanded(
|
|
||||||
child: Divider(
|
|
||||||
color: theme.dividerColor,
|
|
||||||
thickness: 1,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
|
||||||
child: Text(
|
|
||||||
'OR',
|
|
||||||
style: theme.textTheme.bodySmall?.copyWith(
|
|
||||||
fontWeight: FontWeight.w700,
|
|
||||||
color: theme.colorScheme.onSurfaceVariant,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Expanded(
|
|
||||||
child: Divider(
|
|
||||||
color: theme.dividerColor,
|
|
||||||
thickness: 1,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
const SizedBox(height: 24),
|
|
||||||
// Telegram auth code input
|
// Telegram auth code input
|
||||||
SignInWithTelegram(
|
SignInWithTelegram(
|
||||||
onLoginSuccess: () => _onTelegramLoginSuccess(context),
|
onLoginSuccess: () => _onTelegramLoginSuccess(context),
|
||||||
|
|
|
||||||
|
|
@ -34,17 +34,38 @@ class _SignInWithTelegramState extends State<SignInWithTelegram> {
|
||||||
TelegramAuthCodeStatus? _webCodeStatus;
|
TelegramAuthCodeStatus? _webCodeStatus;
|
||||||
Timer? _codeStatusTimer;
|
Timer? _codeStatusTimer;
|
||||||
Timer? _countdownTimer;
|
Timer? _countdownTimer;
|
||||||
|
Timer? _autoCheckTimer;
|
||||||
DateTime? _codeExpiryTime;
|
DateTime? _codeExpiryTime;
|
||||||
bool _autoLoginAttempted = false;
|
bool _autoLoginAttempted = false;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_telegramCodeController.addListener(_onCodeChanged);
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
_codeStatusTimer?.cancel();
|
_codeStatusTimer?.cancel();
|
||||||
_countdownTimer?.cancel();
|
_countdownTimer?.cancel();
|
||||||
|
_autoCheckTimer?.cancel();
|
||||||
|
_telegramCodeController.removeListener(_onCodeChanged);
|
||||||
_telegramCodeController.dispose();
|
_telegramCodeController.dispose();
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _onCodeChanged() {
|
||||||
|
_autoCheckTimer?.cancel();
|
||||||
|
final code = _telegramCodeController.text.trim();
|
||||||
|
if (code.length > 4) {
|
||||||
|
_autoCheckTimer = Timer(const Duration(seconds: 1), () {
|
||||||
|
if (mounted && _telegramCodeController.text.trim().length > 4) {
|
||||||
|
_loginWithTelegram();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> _loginWithTelegram({String? codeParam}) async {
|
Future<void> _loginWithTelegram({String? codeParam}) async {
|
||||||
final code = codeParam ?? _telegramCodeController.text.trim();
|
final code = codeParam ?? _telegramCodeController.text.trim();
|
||||||
|
|
||||||
|
|
@ -428,29 +449,6 @@ class _SignInWithTelegramState extends State<SignInWithTelegram> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _openTelegramBot() async {
|
|
||||||
final deepLink = Uri.parse(ApiConfigV2.telegramBotDeepLinkBase);
|
|
||||||
|
|
||||||
try {
|
|
||||||
final launched = await launchUrl(
|
|
||||||
deepLink,
|
|
||||||
mode: LaunchMode.externalApplication,
|
|
||||||
);
|
|
||||||
if (!launched) {
|
|
||||||
throw Exception('Could not launch Telegram');
|
|
||||||
}
|
|
||||||
} catch (e, s) {
|
|
||||||
log(
|
|
||||||
'Error opening Telegram bot',
|
|
||||||
error: e,
|
|
||||||
stackTrace: s,
|
|
||||||
name: 'SignInWithTelegram',
|
|
||||||
);
|
|
||||||
widget.onError(
|
|
||||||
'Не удалось открыть Telegram. Попробуйте вручную: ${deepLink.toString()}',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
|
@ -460,12 +458,29 @@ class _SignInWithTelegramState extends State<SignInWithTelegram> {
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
'Введите код из Telegram бота',
|
'Два способа входа:',
|
||||||
|
style: theme.textTheme.titleMedium?.copyWith(
|
||||||
|
fontWeight: FontWeight.w700,
|
||||||
|
color: theme.colorScheme.onSurface,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
// Вариант 1: Ввод кода вручную
|
||||||
|
Text(
|
||||||
|
'Вариант 1: Введите код вручную',
|
||||||
style: theme.textTheme.bodyLarge?.copyWith(
|
style: theme.textTheme.bodyLarge?.copyWith(
|
||||||
fontWeight: FontWeight.w700,
|
fontWeight: FontWeight.w700,
|
||||||
color: theme.colorScheme.onSurface,
|
color: theme.colorScheme.onSurface,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
Text(
|
||||||
|
'Код будет проверен автоматически при длине более 4 символов',
|
||||||
|
style: theme.textTheme.bodySmall?.copyWith(
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
color: theme.colorScheme.onSurfaceVariant,
|
||||||
|
),
|
||||||
|
),
|
||||||
const SizedBox(height: 12),
|
const SizedBox(height: 12),
|
||||||
TextField(
|
TextField(
|
||||||
controller: _telegramCodeController,
|
controller: _telegramCodeController,
|
||||||
|
|
@ -500,60 +515,28 @@ class _SignInWithTelegramState extends State<SignInWithTelegram> {
|
||||||
maxLength: 6,
|
maxLength: 6,
|
||||||
onSubmitted: (_) => _loginWithTelegram(),
|
onSubmitted: (_) => _loginWithTelegram(),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 16),
|
|
||||||
Row(
|
|
||||||
children: [
|
|
||||||
Expanded(
|
|
||||||
child: ElevatedButton.icon(
|
|
||||||
onPressed: widget.isLoading ? null : _generateTelegramCode,
|
|
||||||
icon: const Icon(Icons.bolt, size: 20),
|
|
||||||
label: Text(
|
|
||||||
widget.isLoading ? 'Создание...' : 'Получить код',
|
|
||||||
style: theme.textTheme.labelLarge?.copyWith(
|
|
||||||
fontWeight: FontWeight.w700,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
style: ElevatedButton.styleFrom(
|
|
||||||
shape: RoundedRectangleBorder(
|
|
||||||
borderRadius: BorderRadius.circular(12),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(width: 12),
|
|
||||||
Expanded(
|
|
||||||
child: OutlinedButton.icon(
|
|
||||||
onPressed: widget.isLoading ? null : _openTelegramBot,
|
|
||||||
icon: const Icon(Icons.telegram, size: 20),
|
|
||||||
label: Text(
|
|
||||||
'Открыть бота',
|
|
||||||
style: theme.textTheme.labelLarge?.copyWith(
|
|
||||||
fontWeight: FontWeight.w700,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
style: OutlinedButton.styleFrom(
|
|
||||||
shape: RoundedRectangleBorder(
|
|
||||||
borderRadius: BorderRadius.circular(12),
|
|
||||||
),
|
|
||||||
side: BorderSide(color: theme.dividerColor, width: 1),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
if (_webCodeStatus != null) ...[
|
if (_webCodeStatus != null) ...[
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: 16),
|
||||||
_buildTelegramStatusCard(),
|
_buildTelegramStatusCard(),
|
||||||
],
|
],
|
||||||
const SizedBox(height: 24),
|
const SizedBox(height: 24),
|
||||||
|
// Вариант 2: Кнопка входа
|
||||||
|
Text(
|
||||||
|
'Вариант 2: Нажмите кнопку для автоматической генерации кода',
|
||||||
|
style: theme.textTheme.bodyLarge?.copyWith(
|
||||||
|
fontWeight: FontWeight.w700,
|
||||||
|
color: theme.colorScheme.onSurface,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 12),
|
||||||
SizedBox(
|
SizedBox(
|
||||||
width: double.infinity,
|
width: double.infinity,
|
||||||
height: 56,
|
height: 56,
|
||||||
child: ElevatedButton.icon(
|
child: ElevatedButton.icon(
|
||||||
onPressed: widget.isLoading ? null : _loginWithTelegram,
|
onPressed: widget.isLoading ? null : _generateTelegramCode,
|
||||||
icon: const Icon(Icons.telegram, size: 24),
|
icon: const Icon(Icons.telegram, size: 24),
|
||||||
label: Text(
|
label: Text(
|
||||||
'Войти с кодом Telegram',
|
'Войти через телеграм',
|
||||||
style: theme.textTheme.labelLarge?.copyWith(
|
style: theme.textTheme.labelLarge?.copyWith(
|
||||||
fontWeight: FontWeight.w700,
|
fontWeight: FontWeight.w700,
|
||||||
),
|
),
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue