Fix login and settings
This commit is contained in:
@@ -16,16 +16,14 @@ class AuthBloc extends Bloc<AuthEvent, AuthState> {
|
||||
}
|
||||
|
||||
void _onAuthStarted(AuthStarted event, Emitter<AuthState> emit) async {
|
||||
// Комментарий: Инициализируем UserCubit при старте AuthBloc.
|
||||
// Это гарантирует, что UserCubit загрузит данные пользователя
|
||||
// перед тем, как AuthBloc будет принимать решение об аутентификации.
|
||||
await _userCubit.init();
|
||||
|
||||
// Получаем текущее состояние пользователя из UserCubit после инициализации
|
||||
// Комментарий: Этот метод теперь просто проверяет ТЕКУЩЕЕ состояние UserCubit,
|
||||
// не инициируя его повторную загрузку. Инициализация происходит один раз в main.dart.
|
||||
final userState = _userCubit.state;
|
||||
if (userState is UserLoaded && userState.user != null) {
|
||||
// Если пользователь уже загружен в UserCubit, считаем его аутентифицированным.
|
||||
emit(AuthAuthenticated(user: userState.user!));
|
||||
} else {
|
||||
// Если пользователь не загружен (null) или состояние другое, считаем его неаутентифицированным.
|
||||
emit(AuthUnauthenticated());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,7 +23,17 @@ class UserCubit extends Cubit<UserState> {
|
||||
final ICategoryRepository _categoryRepository;
|
||||
final ITagRepository _tagRepository;
|
||||
final ITransactionRepository _transactionRepository;
|
||||
final Logger _logger = Logger();
|
||||
final Logger _logger = Logger(
|
||||
printer: PrettyPrinter(
|
||||
methodCount: 15, // Number of method calls to be displayed
|
||||
errorMethodCount: 8, // Number of method calls if stacktrace is provided
|
||||
lineLength: 120, // Width of the output
|
||||
colors: true, // Colorful log messages
|
||||
printEmojis: true, // Print an emoji for each log message
|
||||
// Should each log print contain a timestamp
|
||||
dateTimeFormat: DateTimeFormat.onlyTimeAndSinceStart,
|
||||
),
|
||||
);
|
||||
|
||||
UserCubit({
|
||||
required IGlobalSettingsRepository settingsRepository,
|
||||
@@ -44,48 +54,25 @@ class UserCubit extends Cubit<UserState> {
|
||||
}
|
||||
|
||||
Future<void> _init() async {
|
||||
emit(UserLoading(progress: 0.1, message: 'Поиск пользователя...'));
|
||||
emit(UserLoading(progress: 0.1, message: 'Инициализация...'));
|
||||
try {
|
||||
// Этап 1: Проверка существующего пользователя
|
||||
final userId = await _settingsRepository.getCurrentUserId();
|
||||
emit(UserLoading(progress: 0.3, message: 'Проверка пользователя...'));
|
||||
|
||||
User? currentUser;
|
||||
|
||||
if (userId != null) {
|
||||
emit(UserLoading(progress: 0.3, message: 'Поиск пользователя...'));
|
||||
currentUser = await _userRepository.getById(userId);
|
||||
if (currentUser == null) {
|
||||
_logger.w('User with ID $userId not found. Clearing key.');
|
||||
await _settingsRepository.setCurrentUserId(null);
|
||||
}
|
||||
}
|
||||
|
||||
// Этап 2: Создание пользователя по умолчанию при необходимости
|
||||
if (currentUser == null) {
|
||||
emit(UserLoading(progress: 0.5, message: 'Проверка данных...'));
|
||||
final allUsers = await _userRepository.getAll();
|
||||
if (allUsers.isEmpty) {
|
||||
_logger.i('No users found, creating default user');
|
||||
emit(UserLoading(progress: 0.6, message: 'Создание пользователя...'));
|
||||
// Комментарий: Вызываем создание пользователя по умолчанию.
|
||||
// Этот метод сам установит состояние UserLoaded, поэтому после него нужно завершить выполнение _init.
|
||||
await _createDefaultUser();
|
||||
return;
|
||||
} else {
|
||||
_logger.i('Setting first user as current');
|
||||
currentUser = allUsers.first;
|
||||
// Комментарий: Устанавливаем первого пользователя как текущего.
|
||||
// Этот метод также устанавливает состояние UserLoaded, поэтому выходим.
|
||||
await _setCurrentUser(currentUser);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Этап 3: Завершение инициализации (этот блок теперь выполняется только для уже существующих пользователей)
|
||||
emit(UserLoading(progress: 1.0, message: 'Завершение...'));
|
||||
await Future.delayed(const Duration(milliseconds: 300));
|
||||
emit(UserLoaded(currentUser));
|
||||
|
||||
} catch (e, stack) {
|
||||
_logger.e('Error initializing user cubit', error: e, stackTrace: stack);
|
||||
_logger.e('--- UserCubit: FATAL ERROR in _init ---', error: e, stackTrace: stack);
|
||||
emit(UserError('Ошибка загрузки пользователя: ${e.toString()}'));
|
||||
}
|
||||
}
|
||||
|
||||
+5
-4
@@ -54,10 +54,11 @@ class MyApp extends StatelessWidget {
|
||||
BlocProvider(create: (context) => GetIt.instance<SmsCubit>()),
|
||||
],
|
||||
child: BlocListener<UserCubit, UserState>(
|
||||
// Запускаем AuthBloc, как только UserCubit завершил загрузку,
|
||||
// независимо от того, найден пользователь или нет.
|
||||
listenWhen: (previous, current) => current is UserLoaded,
|
||||
listener: (context, userState) {
|
||||
if (userState is UserLoaded && userState.user != null) {
|
||||
context.read<AuthBloc>().add(AuthStarted());
|
||||
}
|
||||
context.read<AuthBloc>().add(AuthStarted());
|
||||
},
|
||||
child: BlocBuilder<SettingsCubit, SettingsState>(
|
||||
builder: (context, settingsState) {
|
||||
@@ -83,7 +84,7 @@ class MyApp extends StatelessWidget {
|
||||
locale: Locale(languageCode),
|
||||
home: BlocBuilder<UserCubit, UserState>(
|
||||
builder: (context, userState) {
|
||||
if (userState is UserLoading) {
|
||||
if (userState is UserLoading || userState is UserInitial) {
|
||||
return const SplashScreen();
|
||||
}
|
||||
|
||||
|
||||
+149
-123
@@ -33,131 +33,157 @@ class SettingsPage extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
},
|
||||
child: BlocBuilder<SettingsCubit, SettingsState>(
|
||||
builder: (context, state) {
|
||||
if (state is SettingsLoaded) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
SwitchListTile(
|
||||
title: Text(localizations.darkModeSetting),
|
||||
subtitle: Text(localizations.darkModeDescription),
|
||||
value: state.isDarkMode,
|
||||
// Комментарий: При изменении положения переключателя (onChanged) мы вызываем метод `toggleDarkMode` у Cubit.
|
||||
// `context.read<SettingsCubit>()` используется для доступа к Cubit без подписки на его изменения.
|
||||
// Это хорошо для вызова методов. Также передаем userId из UserService.
|
||||
onChanged: (value) {
|
||||
// Получаем ID текущего пользователя из UserCubit
|
||||
final userState = context.read<UserCubit>().state;
|
||||
if (userState is UserLoaded && userState.user != null) {
|
||||
final userId = userState.user!.id;
|
||||
context.read<SettingsCubit>().toggleDarkMode(value, userId);
|
||||
}
|
||||
},
|
||||
),
|
||||
const Divider(),
|
||||
ListTile(
|
||||
title: Text(localizations.languageSetting),
|
||||
subtitle: Text(localizations.languageDescription),
|
||||
trailing: DropdownButton<String>(
|
||||
value: state.languageCode,
|
||||
// Комментарий: При выборе нового языка вызываем метод `changeLanguage` у Cubit.
|
||||
// Также передаем userId из UserService.
|
||||
onChanged: (String? newValue) {
|
||||
if (newValue != null) {
|
||||
// Получаем ID текущего пользователя из UserCubit
|
||||
final userState = context.read<UserCubit>().state;
|
||||
if (userState is UserLoaded && userState.user != null) {
|
||||
final userId = userState.user!.id;
|
||||
context.read<SettingsCubit>().changeLanguage(newValue, userId);
|
||||
}
|
||||
}
|
||||
},
|
||||
// Комментарий: Формируем список доступных языков.
|
||||
items: <String>['en', 'ru']
|
||||
.map<DropdownMenuItem<String>>((String value) {
|
||||
return DropdownMenuItem<String>(
|
||||
value: value,
|
||||
// Комментарий: Отображаем локализованное название языка.
|
||||
child: Text(value == 'en'
|
||||
? localizations.englishLanguage
|
||||
: localizations.russianLanguage),
|
||||
);
|
||||
}).toList(),
|
||||
),
|
||||
),
|
||||
const Divider(),
|
||||
ListTile(
|
||||
title: Text(localizations.currencySetting),
|
||||
subtitle: Text(localizations.currencyDescription),
|
||||
trailing: DropdownButton<String>(
|
||||
value: state.defaultCurrency,
|
||||
// Комментарий: При выборе новой валюты вызываем метод `changeCurrency` у Cubit.
|
||||
// Также передаем userId из UserService.
|
||||
onChanged: (String? newValue) {
|
||||
if (newValue != null) {
|
||||
// Получаем ID текущего пользователя из UserCubit
|
||||
final userState = context.read<UserCubit>().state;
|
||||
if (userState is UserLoaded && userState.user != null) {
|
||||
final userId = userState.user!.id;
|
||||
context.read<SettingsCubit>().changeCurrency(newValue, userId);
|
||||
}
|
||||
}
|
||||
},
|
||||
// Комментарий: Формируем список доступных валют. Можно расширить этот список.
|
||||
items: <String>['RUB', 'USD', 'EUR']
|
||||
.map<DropdownMenuItem<String>>((String value) {
|
||||
return DropdownMenuItem<String>(
|
||||
value: value,
|
||||
child: Text(value),
|
||||
);
|
||||
}).toList(),
|
||||
),
|
||||
),
|
||||
const Divider(),
|
||||
// Комментарий: ListTile для перехода на страницу редактирования категорий.
|
||||
ListTile(
|
||||
title: Text(localizations.editCategories),
|
||||
subtitle: Text(localizations.editCategoriesDescription),
|
||||
onTap: () {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => const CategoryListPage(),
|
||||
child: BlocBuilder<UserCubit, UserState>(
|
||||
builder: (context, userState) {
|
||||
if (userState is UserLoaded && userState.user != null) {
|
||||
// Комментарий: Как только пользователь загружен, мы загружаем его настройки.
|
||||
context.read<SettingsCubit>().loadSettings(userState.user!.id);
|
||||
return BlocBuilder<SettingsCubit, SettingsState>(
|
||||
builder: (context, state) {
|
||||
if (state is SettingsLoaded) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
SwitchListTile(
|
||||
title: Text(localizations.darkModeSetting),
|
||||
subtitle: Text(localizations.darkModeDescription),
|
||||
value: state.isDarkMode,
|
||||
// Комментарий: При изменении положения переключателя (onChanged) мы вызываем метод `toggleDarkMode` у Cubit.
|
||||
// `context.read<SettingsCubit>()` используется для доступа к Cubit без подписки на его изменения.
|
||||
// Это хорошо для вызова методов. Также передаем userId из UserService.
|
||||
onChanged: (value) {
|
||||
// Получаем ID текущего пользователя из UserCubit
|
||||
final userState = context.read<UserCubit>().state;
|
||||
if (userState is UserLoaded &&
|
||||
userState.user != null) {
|
||||
final userId = userState.user!.id;
|
||||
context
|
||||
.read<SettingsCubit>()
|
||||
.toggleDarkMode(value, userId);
|
||||
}
|
||||
},
|
||||
),
|
||||
const Divider(),
|
||||
ListTile(
|
||||
title: Text(localizations.languageSetting),
|
||||
subtitle: Text(localizations.languageDescription),
|
||||
trailing: DropdownButton<String>(
|
||||
value: state.languageCode,
|
||||
// Комментарий: При выборе нового языка вызываем метод `changeLanguage` у Cubit.
|
||||
// Также передаем userId из UserService.
|
||||
onChanged: (String? newValue) {
|
||||
if (newValue != null) {
|
||||
// Получаем ID текущего пользователя из UserCubit
|
||||
final userState =
|
||||
context.read<UserCubit>().state;
|
||||
if (userState is UserLoaded &&
|
||||
userState.user != null) {
|
||||
final userId = userState.user!.id;
|
||||
context
|
||||
.read<SettingsCubit>()
|
||||
.changeLanguage(newValue, userId);
|
||||
}
|
||||
}
|
||||
},
|
||||
// Комментарий: Формируем список доступных языков.
|
||||
items: <String>['en', 'ru']
|
||||
.map<DropdownMenuItem<String>>(
|
||||
(String value) {
|
||||
return DropdownMenuItem<String>(
|
||||
value: value,
|
||||
// Комментарий: Отображаем локализованное название языка.
|
||||
child: Text(value == 'en'
|
||||
? localizations.englishLanguage
|
||||
: localizations.russianLanguage),
|
||||
);
|
||||
}).toList(),
|
||||
),
|
||||
),
|
||||
const Divider(),
|
||||
ListTile(
|
||||
title: Text(localizations.currencySetting),
|
||||
subtitle: Text(localizations.currencyDescription),
|
||||
trailing: DropdownButton<String>(
|
||||
value: state.defaultCurrency,
|
||||
// Комментарий: При выборе новой валюты вызываем метод `changeCurrency` у Cubit.
|
||||
// Также передаем userId из UserService.
|
||||
onChanged: (String? newValue) {
|
||||
if (newValue != null) {
|
||||
// Получаем ID текущего пользователя из UserCubit
|
||||
final userState =
|
||||
context.read<UserCubit>().state;
|
||||
if (userState is UserLoaded &&
|
||||
userState.user != null) {
|
||||
final userId = userState.user!.id;
|
||||
context
|
||||
.read<SettingsCubit>()
|
||||
.changeCurrency(newValue, userId);
|
||||
}
|
||||
}
|
||||
},
|
||||
// Комментарий: Формируем список доступных валют. Можно расширить этот список.
|
||||
items: <String>['RUB', 'USD', 'EUR']
|
||||
.map<DropdownMenuItem<String>>(
|
||||
(String value) {
|
||||
return DropdownMenuItem<String>(
|
||||
value: value,
|
||||
child: Text(value),
|
||||
);
|
||||
}).toList(),
|
||||
),
|
||||
),
|
||||
const Divider(),
|
||||
// Комментарий: ListTile для перехода на страницу редактирования категорий.
|
||||
ListTile(
|
||||
title: Text(localizations.editCategories),
|
||||
subtitle:
|
||||
Text(localizations.editCategoriesDescription),
|
||||
onTap: () {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) =>
|
||||
const CategoryListPage(),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
const Divider(),
|
||||
ListTile(
|
||||
title: Text(localizations.editTags),
|
||||
subtitle: Text(localizations.editTagsDescription),
|
||||
onTap: () {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => const TagListPage(),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
const Divider(),
|
||||
// Комментарий: ListTile для запуска процесса загрузки SMS-сообщений.
|
||||
ListTile(
|
||||
title: Text(localizations.loadSmsMessages),
|
||||
subtitle:
|
||||
Text(localizations.loadSmsMessagesDescription),
|
||||
onTap: () {
|
||||
// Комментарий: При нажатии на кнопку мы вызываем метод `loadSmsMessages` у SmsCubit.
|
||||
// Это инициирует процесс получения и сохранения SMS-сообщений.
|
||||
context.read<SmsCubit>().loadSmsMessages();
|
||||
},
|
||||
),
|
||||
const Divider(),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
const Divider(),
|
||||
ListTile(
|
||||
title: Text(localizations.editTags),
|
||||
subtitle: Text(localizations.editTagsDescription),
|
||||
onTap: () {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => const TagListPage(),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
const Divider(),
|
||||
// Комментарий: ListTile для запуска процесса загрузки SMS-сообщений.
|
||||
ListTile(
|
||||
title: Text(localizations.loadSmsMessages),
|
||||
subtitle: Text(localizations.loadSmsMessagesDescription),
|
||||
onTap: () {
|
||||
// Комментарий: При нажатии на кнопку мы вызываем метод `loadSmsMessages` у SmsCubit.
|
||||
// Это инициирует процесс получения и сохранения SMS-сообщений.
|
||||
context.read<SmsCubit>().loadSmsMessages();
|
||||
},
|
||||
),
|
||||
const Divider(),
|
||||
],
|
||||
),
|
||||
);
|
||||
} else {
|
||||
return const Center(child: CircularProgressIndicator());
|
||||
}
|
||||
},
|
||||
);
|
||||
} else {
|
||||
return const Center(child: CircularProgressIndicator());
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user