Refactors HiveService and dependency injection
Improves the management of Hive boxes by introducing separate initialization for global and user-specific data. This change enhances data isolation and allows for dynamic dependency injection based on the authentication state. It introduces `initGlobalDependencies` and `initUserSpecificDependencies` to manage the application's dependencies. User-scoped repositories and cubits are now registered/unregistered upon login/logout. The UserCubit is updated to allow dynamic injection of user-specific repositories after authentication. The TagCubit is refactored to use a repository.
This commit is contained in:
@@ -1,81 +1,107 @@
|
||||
import 'package:budget_app/hive/hive_registrar.g.dart';
|
||||
import 'package:budget_app/models/app_settings.dart'; // Добавлен импорт AppSettings
|
||||
import 'package:budget_app/models/global_settings.dart';
|
||||
import 'package:budget_app/models/sms_handler_settings.dart';
|
||||
import 'package:hive_ce_flutter/hive_flutter.dart';
|
||||
import 'package:logger/logger.dart';
|
||||
|
||||
import '/models/category.dart';
|
||||
import '/models/sms_message.dart';
|
||||
import '/models/tag.dart';
|
||||
import '/models/transaction_record.dart';
|
||||
import '/models/user.dart';
|
||||
|
||||
final _logger = Logger();
|
||||
import '../../models/app_settings.dart';
|
||||
import '../../models/category.dart';
|
||||
import '../../models/global_settings.dart';
|
||||
import '../../models/sms_handler_settings.dart';
|
||||
import '../../models/sms_message.dart';
|
||||
import '../../models/tag.dart';
|
||||
import '../../models/transaction_record.dart';
|
||||
import '../../models/user.dart';
|
||||
|
||||
/// Сервис для управления Hive Box.
|
||||
/// Отвечает за инициализацию, открытие и закрытие глобальных и пользовательских хранилищ.
|
||||
class HiveService {
|
||||
static const String _appSettingsBox =
|
||||
'app_settings'; // Переименовано для настроек приложения
|
||||
static const String _categoryBox = 'categories';
|
||||
static const String _tagBox = 'tags';
|
||||
static const String _transactionBox = 'transactions';
|
||||
static const String _userBox = 'users';
|
||||
static const String _smsMessageBox = 'smsMessages';
|
||||
static const String _globalSettings = 'global_settings';
|
||||
static const String _smsHandlerSettingsBox = 'sms_handler_settings';
|
||||
// --- Глобальные Box ---
|
||||
// Эти хранилища не зависят от пользователя и инициализируются один раз.
|
||||
static late final Box<User> users;
|
||||
static late final Box<GlobalSettings> globalSettings;
|
||||
|
||||
static Future<void> init() async {
|
||||
_logger.i('Initializing Hive database');
|
||||
/// Инициализация глобальных Hive Box.
|
||||
/// Должен вызываться при старте приложения.
|
||||
static Future<void> initGlobalBoxes() async {
|
||||
await Hive.initFlutter();
|
||||
// Регистрация адаптеров Hive...
|
||||
_registerAdapters();
|
||||
|
||||
Hive.registerAdapters();
|
||||
|
||||
// Открытие всех Box'ов
|
||||
await Future.wait([
|
||||
Hive.openBox<AppSettings>(
|
||||
_appSettingsBox,
|
||||
), // Открываем Box для AppSettings
|
||||
Hive.openBox<Category>(_categoryBox),
|
||||
Hive.openBox<Tag>(_tagBox),
|
||||
Hive.openBox<TransactionRecord>(_transactionBox),
|
||||
Hive.openBox<User>(_userBox),
|
||||
Hive.openBox<SmsMessage>(_smsMessageBox),
|
||||
Hive.openBox<GlobalSettings>(_globalSettings),
|
||||
Hive.openBox<SmsHandlerSettings>(_smsHandlerSettingsBox),
|
||||
]);
|
||||
|
||||
// Проверка и заполнение начальными данными
|
||||
_checkAndFillInitialData();
|
||||
// Открытие глобальных хранилищ
|
||||
users = await Hive.openBox<User>('users');
|
||||
globalSettings = await Hive.openBox<GlobalSettings>('global_settings');
|
||||
}
|
||||
|
||||
static Box<AppSettings> get appSettings =>
|
||||
Hive.box<AppSettings>(_appSettingsBox); // Геттер для настроек
|
||||
static Box<Category> get categories => Hive.box<Category>(_categoryBox);
|
||||
static Box<Tag> get tags => Hive.box<Tag>(_tagBox);
|
||||
static Box<TransactionRecord> get transactions =>
|
||||
Hive.box<TransactionRecord>(_transactionBox);
|
||||
static Box<User> get users => Hive.box<User>(_userBox);
|
||||
static Box<SmsMessage> get smsMessages =>
|
||||
Hive.box<SmsMessage>(_smsMessageBox);
|
||||
static Box<GlobalSettings> get globalSettings =>
|
||||
Hive.box<GlobalSettings>(_globalSettings);
|
||||
static Box<SmsHandlerSettings> get smsHandlerSettings =>
|
||||
Hive.box<SmsHandlerSettings>(_smsHandlerSettingsBox);
|
||||
// --- Пользовательские Box ---
|
||||
// Эти хранилища привязаны к конкретному пользователю.
|
||||
// Они открываются после входа пользователя в систему.
|
||||
static late Box<Category> categories;
|
||||
static late Box<Tag> tags;
|
||||
static late Box<TransactionRecord> transactions;
|
||||
static late Box<AppSettings> appSettings;
|
||||
static late Box<SmsHandlerSettings> smsHandlerSettings;
|
||||
static late Box<SmsMessage> smsMessages;
|
||||
|
||||
/// Проверяет и заполняет боксы начальными данными при первом запуске
|
||||
static void _checkAndFillInitialData() {
|
||||
final userBox = users;
|
||||
final catBox = categories;
|
||||
final tagBox = tags;
|
||||
final transactionBox = transactions;
|
||||
/// Инициализация Hive Box для конкретного пользователя.
|
||||
/// [userId] - Уникальный идентификатор пользователя.
|
||||
static Future<void> initUserBoxes(String userId) async {
|
||||
// Открываем Box'ы с именами, включающими userId для изоляции данных.
|
||||
// Например: 'categories_user123'
|
||||
categories = await Hive.openBox<Category>('categories_\$userId');
|
||||
tags = await Hive.openBox<Tag>('tags_\$userId');
|
||||
transactions = await Hive.openBox<TransactionRecord>(
|
||||
'transactions_\$userId',
|
||||
);
|
||||
appSettings = await Hive.openBox<AppSettings>('app_settings_\$userId');
|
||||
smsHandlerSettings = await Hive.openBox<SmsHandlerSettings>(
|
||||
'sms_handler_settings_\$userId',
|
||||
);
|
||||
smsMessages = await Hive.openBox<SmsMessage>('sms_messages_\$userId');
|
||||
}
|
||||
|
||||
if (userBox.isEmpty &&
|
||||
catBox.isEmpty &&
|
||||
tagBox.isEmpty &&
|
||||
transactionBox.isEmpty) {
|
||||
_logger.i(
|
||||
'Initial data is empty. It will be filled when a user is created.',
|
||||
);
|
||||
/// Закрытие пользовательских Hive Box.
|
||||
/// Должен вызываться при выходе пользователя из системы.
|
||||
static Future<void> closeUserBoxes() async {
|
||||
await categories.close();
|
||||
await tags.close();
|
||||
await transactions.close();
|
||||
await appSettings.close();
|
||||
await smsHandlerSettings.close();
|
||||
await smsMessages.close();
|
||||
}
|
||||
|
||||
/// Регистрация всех адаптеров Hive.
|
||||
// Регистрация всех адаптеров Hive.
|
||||
static void _registerAdapters() {
|
||||
if (!Hive.isAdapterRegistered(UserAdapter().typeId)) {
|
||||
Hive.registerAdapter(UserAdapter());
|
||||
}
|
||||
if (!Hive.isAdapterRegistered(CategoryAdapter().typeId)) {
|
||||
Hive.registerAdapter(CategoryAdapter());
|
||||
}
|
||||
if (!Hive.isAdapterRegistered(TagAdapter().typeId)) {
|
||||
Hive.registerAdapter(TagAdapter());
|
||||
}
|
||||
if (!Hive.isAdapterRegistered(TransactionRecordAdapter().typeId)) {
|
||||
Hive.registerAdapter(TransactionRecordAdapter());
|
||||
}
|
||||
if (!Hive.isAdapterRegistered(AppSettingsAdapter().typeId)) {
|
||||
Hive.registerAdapter(AppSettingsAdapter());
|
||||
}
|
||||
if (!Hive.isAdapterRegistered(GlobalSettingsAdapter().typeId)) {
|
||||
Hive.registerAdapter(GlobalSettingsAdapter());
|
||||
}
|
||||
if (!Hive.isAdapterRegistered(SmsHandlerSettingsAdapter().typeId)) {
|
||||
Hive.registerAdapter(SmsHandlerSettingsAdapter());
|
||||
}
|
||||
if (!Hive.isAdapterRegistered(SmsMessageAdapter().typeId)) {
|
||||
Hive.registerAdapter(SmsMessageAdapter());
|
||||
}
|
||||
|
||||
// Добавляем регистрацию SmsProcessingRuleAdapter
|
||||
if (!Hive.isAdapterRegistered(SmsProcessingRuleAdapter().typeId)) {
|
||||
Hive.registerAdapter(SmsProcessingRuleAdapter());
|
||||
}
|
||||
// Добавляем регистрацию SmsProcessingTypeAdapter
|
||||
if (!Hive.isAdapterRegistered(SmsProcessingTypeAdapter().typeId)) {
|
||||
Hive.registerAdapter(SmsProcessingTypeAdapter());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,19 @@
|
||||
import 'package:budget_app/data/database/hive_service.dart';
|
||||
import 'package:budget_app/data/repositories/interfaces/itag_repository.dart';
|
||||
import 'package:budget_app/models/tag.dart';
|
||||
import 'package:hive_ce/hive.dart';
|
||||
import '/data/repositories/interfaces/itag_repository.dart';
|
||||
import '../../models/tag.dart';
|
||||
|
||||
class HiveTagRepository implements ITagRepository {
|
||||
final Box<Tag> _box;
|
||||
HiveTagRepository(); // Конструктор без параметров
|
||||
|
||||
HiveTagRepository(this._box);
|
||||
// Получаем бокс тегов из HiveService
|
||||
Box<Tag> get _box {
|
||||
if (HiveService.tags.isOpen) {
|
||||
return HiveService.tags;
|
||||
} else {
|
||||
throw Exception('Tags box is not initialized');
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<Tag>> getAll() async {
|
||||
@@ -32,13 +40,10 @@ class HiveTagRepository implements ITagRepository {
|
||||
await _box.delete(id);
|
||||
}
|
||||
|
||||
// Комментарий: Реализуем новый метод, объявленный в интерфейсе ITagRepository.
|
||||
@override
|
||||
Future<List<Tag>> getAllByUser(String userId) async {
|
||||
// Комментарий: Мы используем метод `where` для фильтрации всех записей в хранилище Hive.
|
||||
// Он перебирает все теги (`_box.values`) и возвращает только те,
|
||||
// у которых поле `userId` совпадает с идентификатором, переданным в метод.
|
||||
return _box.values.where((tag) => tag.userId == userId).toList();
|
||||
// Фильтрация по userId больше не требуется, так как бокс уже пользовательский
|
||||
return _box.values.toList();
|
||||
}
|
||||
|
||||
@override
|
||||
|
||||
+134
-49
@@ -1,100 +1,185 @@
|
||||
import 'package:budget_app/data/repositories/hive_sms_handler_repository.dart';
|
||||
import 'package:budget_app/data/repositories/hive_sms_message_repository.dart';
|
||||
import 'package:budget_app/data/repositories/interfaces/isms_handler_repository.dart';
|
||||
import 'package:budget_app/data/repositories/interfaces/isms_message_repository.dart';
|
||||
import 'package:get_it/get_it.dart';
|
||||
|
||||
import 'data/database/hive_service.dart';
|
||||
import 'data/repositories/hive_category_repository.dart';
|
||||
import 'data/repositories/hive_global_settings_repository.dart';
|
||||
import 'data/repositories/hive_settings_repository.dart'; // Добавляем импорт HiveSettingsRepository
|
||||
import 'data/repositories/hive_settings_repository.dart';
|
||||
import 'data/repositories/hive_sms_handler_repository.dart';
|
||||
import 'data/repositories/hive_sms_message_repository.dart';
|
||||
import 'data/repositories/hive_tag_repository.dart';
|
||||
import 'data/repositories/hive_transaction_repository.dart';
|
||||
import 'data/repositories/hive_user_repository.dart';
|
||||
import 'data/repositories/interfaces/icategory_repository.dart';
|
||||
import 'data/repositories/interfaces/iglobal_settings_repository.dart';
|
||||
import 'data/repositories/interfaces/isettings_repository.dart'; // Добавляем импорт ISettingsRepository
|
||||
import 'data/repositories/interfaces/isettings_repository.dart';
|
||||
import 'data/repositories/interfaces/isms_handler_repository.dart';
|
||||
import 'data/repositories/interfaces/isms_message_repository.dart';
|
||||
import 'data/repositories/interfaces/itag_repository.dart';
|
||||
import 'data/repositories/interfaces/itransaction_repository.dart';
|
||||
import 'data/repositories/interfaces/iuser_repository.dart';
|
||||
import 'logic/auth/auth_bloc.dart';
|
||||
import 'logic/category/category_cubit.dart';
|
||||
import 'logic/settings/settings_cubit.dart'; // Импортируем SettingsCubit
|
||||
import 'logic/settings/settings_cubit.dart';
|
||||
import 'logic/sms/sms_cubit.dart';
|
||||
import 'logic/tag/tag_cubit.dart';
|
||||
import 'logic/transaction/transaction_bloc.dart';
|
||||
import 'logic/user/user_cubit.dart'; // Импортируем UserCubit вместо UserService
|
||||
import 'logic/user/user_cubit.dart';
|
||||
import 'services/sms_service.dart';
|
||||
|
||||
final getIt = GetIt.instance;
|
||||
|
||||
Future<void> initDependencies() async {
|
||||
/// Инициализация глобальных зависимостей, которые не зависят от пользователя.
|
||||
/// Вызывается один раз при старте приложения.
|
||||
Future<void> initGlobalDependencies() async {
|
||||
// Инициализация Hive
|
||||
await HiveService.init();
|
||||
|
||||
// Регистрация репозиториев
|
||||
getIt.registerSingleton<ICategoryRepository>(
|
||||
HiveCategoryRepository(HiveService.categories),
|
||||
);
|
||||
|
||||
getIt.registerSingleton<ITagRepository>(HiveTagRepository(HiveService.tags));
|
||||
|
||||
getIt.registerSingleton<ITransactionRepository>(
|
||||
HiveTransactionRepository(HiveService.transactions),
|
||||
);
|
||||
await HiveService.initGlobalBoxes();
|
||||
|
||||
// Глобальные репозитории
|
||||
getIt.registerSingleton<IUserRepository>(
|
||||
HiveUserRepository(HiveService.users),
|
||||
);
|
||||
|
||||
// Регистрируем ISettingsRepository с реализацией HiveSettingsRepository
|
||||
getIt.registerSingleton<ISettingsRepository>(
|
||||
HiveSettingsRepository(HiveService.appSettings),
|
||||
);
|
||||
|
||||
getIt.registerSingleton<IGlobalSettingsRepository>(
|
||||
HiveGlobalSettingsRepository(HiveService.globalSettings),
|
||||
);
|
||||
|
||||
getIt.registerSingleton<ISmsHandlerRepository>(
|
||||
HiveSmsHandlerRepository(HiveService.smsHandlerSettings),
|
||||
);
|
||||
|
||||
// Services
|
||||
// Сервисы
|
||||
getIt.registerSingleton<SmsService>(SmsService());
|
||||
|
||||
// Регистрируем UserCubit с передачей всех необходимых репозиториев
|
||||
// Cubits & Blocs, которые нужны до входа пользователя
|
||||
getIt.registerSingleton<UserCubit>(
|
||||
UserCubit(
|
||||
settingsRepository: getIt<IGlobalSettingsRepository>(),
|
||||
userRepository: getIt<IUserRepository>(),
|
||||
categoryRepository: getIt<ICategoryRepository>(),
|
||||
tagRepository: getIt<ITagRepository>(),
|
||||
transactionRepository: getIt<ITransactionRepository>(),
|
||||
// Эти репозитории будут заменены после входа пользователя
|
||||
categoryRepository:
|
||||
null, // Временно null, будет заменен в initUserSpecificDependencies
|
||||
tagRepository:
|
||||
null, // Временно null, будет заменен в initUserSpecificDependencies
|
||||
transactionRepository:
|
||||
null, // Временно null, будет заменен в initUserSpecificDependencies
|
||||
),
|
||||
);
|
||||
|
||||
// Blocs
|
||||
getIt.registerFactory<AuthBloc>(() => AuthBloc(userCubit: getIt()));
|
||||
}
|
||||
|
||||
/// Инициализация зависимостей, специфичных для пользователя.
|
||||
/// Вызывается после успешной аутентификации.
|
||||
Future<void> initUserSpecificDependencies(String userId) async {
|
||||
// Открываем пользовательские Hive Box'ы
|
||||
await HiveService.initUserBoxes(userId);
|
||||
|
||||
// --- Регистрация пользовательских репозиториев ---
|
||||
// Используем lazy singletons, чтобы их можно было легко сбросить и создать заново при смене пользователя.
|
||||
|
||||
// Категории
|
||||
if (getIt.isRegistered<ICategoryRepository>()) {
|
||||
await getIt.unregister<ICategoryRepository>();
|
||||
}
|
||||
getIt.registerLazySingleton<ICategoryRepository>(
|
||||
() => HiveCategoryRepository(HiveService.categories),
|
||||
);
|
||||
|
||||
// Теги
|
||||
if (getIt.isRegistered<ITagRepository>()) {
|
||||
await getIt.unregister<ITagRepository>();
|
||||
}
|
||||
getIt.registerLazySingleton<ITagRepository>(
|
||||
() => HiveTagRepository(),
|
||||
);
|
||||
|
||||
// Транзакции
|
||||
if (getIt.isRegistered<ITransactionRepository>()) {
|
||||
await getIt.unregister<ITransactionRepository>();
|
||||
}
|
||||
getIt.registerLazySingleton<ITransactionRepository>(
|
||||
() => HiveTransactionRepository(HiveService.transactions),
|
||||
);
|
||||
|
||||
// Настройки приложения
|
||||
if (getIt.isRegistered<ISettingsRepository>()) {
|
||||
await getIt.unregister<ISettingsRepository>();
|
||||
}
|
||||
getIt.registerLazySingleton<ISettingsRepository>(
|
||||
() => HiveSettingsRepository(HiveService.appSettings),
|
||||
);
|
||||
|
||||
// Настройки обработчика SMS
|
||||
if (getIt.isRegistered<ISmsHandlerRepository>()) {
|
||||
await getIt.unregister<ISmsHandlerRepository>();
|
||||
}
|
||||
getIt.registerLazySingleton<ISmsHandlerRepository>(
|
||||
() => HiveSmsHandlerRepository(HiveService.smsHandlerSettings),
|
||||
);
|
||||
|
||||
// Сообщения SMS
|
||||
if (getIt.isRegistered<ISmsMessageRepository>()) {
|
||||
await getIt.unregister<ISmsMessageRepository>();
|
||||
}
|
||||
getIt.registerLazySingleton<ISmsMessageRepository>(
|
||||
() => HiveSmsMessageRepository(HiveService.smsMessages),
|
||||
);
|
||||
|
||||
// --- Обновление UserCubit новыми репозиториями ---
|
||||
// Мы не пересоздаем UserCubit, а просто обновляем его зависимости.
|
||||
final userCubit = getIt<UserCubit>();
|
||||
userCubit.categoryRepository = getIt<ICategoryRepository>();
|
||||
userCubit.tagRepository = getIt<ITagRepository>();
|
||||
userCubit.transactionRepository = getIt<ITransactionRepository>();
|
||||
|
||||
// --- Регистрация Cubits & Blocs, которые зависят от пользовательских данных ---
|
||||
|
||||
// Настройки
|
||||
if (getIt.isRegistered<SettingsCubit>()) {
|
||||
await getIt.unregister<SettingsCubit>();
|
||||
}
|
||||
getIt.registerSingleton<SettingsCubit>(
|
||||
SettingsCubit(getIt()),
|
||||
);
|
||||
|
||||
// Транзакции
|
||||
if (getIt.isRegistered<TransactionBloc>()) {
|
||||
await getIt.unregister<TransactionBloc>();
|
||||
}
|
||||
getIt.registerFactory<TransactionBloc>(
|
||||
() => TransactionBloc(transactionRepository: getIt()),
|
||||
);
|
||||
|
||||
// SMS
|
||||
if (getIt.isRegistered<SmsCubit>()) {
|
||||
await getIt.unregister<SmsCubit>();
|
||||
}
|
||||
getIt.registerFactory<SmsCubit>(() => SmsCubit(getIt(), getIt(), getIt()));
|
||||
|
||||
// Категории
|
||||
if (getIt.isRegistered<CategoryCubit>()) {
|
||||
await getIt.unregister<CategoryCubit>();
|
||||
}
|
||||
getIt.registerFactory<CategoryCubit>(
|
||||
() => CategoryCubit(getIt()),
|
||||
); // Регистрируем CategoryCubit с зависимостью от UserCubit
|
||||
|
||||
getIt.registerFactory<TagCubit>(() => TagCubit(getIt()));
|
||||
|
||||
|
||||
getIt.registerSingleton<ISmsMessageRepository>(
|
||||
HiveSmsMessageRepository(HiveService.smsMessages),
|
||||
);
|
||||
|
||||
// Регистрация сервисов
|
||||
getIt.registerSingleton<SettingsCubit>(
|
||||
SettingsCubit(getIt()),
|
||||
); // Регистрируем Cubit и передаем IUserRepository и UserService
|
||||
// Теги
|
||||
if (getIt.isRegistered<TagCubit>()) {
|
||||
await getIt.unregister<TagCubit>();
|
||||
}
|
||||
getIt.registerFactory<TagCubit>(() => TagCubit(getIt()));
|
||||
}
|
||||
|
||||
/// Сброс пользовательских зависимостей при выходе из системы.
|
||||
Future<void> resetUserSpecificDependencies() async {
|
||||
// Закрываем пользовательские Hive Box'ы
|
||||
await HiveService.closeUserBoxes();
|
||||
|
||||
// Разрегистрация всех пользовательских зависимостей
|
||||
await getIt.unregister<ICategoryRepository>();
|
||||
await getIt.unregister<ITagRepository>();
|
||||
await getIt.unregister<ITransactionRepository>();
|
||||
await getIt.unregister<ISettingsRepository>();
|
||||
await getIt.unregister<ISmsHandlerRepository>();
|
||||
await getIt.unregister<ISmsMessageRepository>();
|
||||
await getIt.unregister<SettingsCubit>();
|
||||
await getIt.unregister<TransactionBloc>();
|
||||
await getIt.unregister<SmsCubit>();
|
||||
await getIt.unregister<CategoryCubit>();
|
||||
await getIt.unregister<TagCubit>();
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:budget_app/models/user.dart';
|
||||
import 'package:budget_app/logic/user/user_cubit.dart';
|
||||
import 'package:budget_app/injection_container.dart' as di;
|
||||
|
||||
part 'auth_event.dart';
|
||||
part 'auth_state.dart';
|
||||
@@ -16,27 +17,28 @@ class AuthBloc extends Bloc<AuthEvent, AuthState> {
|
||||
}
|
||||
|
||||
void _onAuthStarted(AuthStarted event, Emitter<AuthState> emit) async {
|
||||
// Комментарий: Этот метод теперь просто проверяет ТЕКУЩЕЕ состояние UserCubit,
|
||||
// не инициируя его повторную загрузку. Инициализация происходит один раз в main.dart.
|
||||
final userState = _userCubit.state;
|
||||
if (userState is UserLoaded && userState.user != null) {
|
||||
// Если пользователь уже загружен в UserCubit, считаем его аутентифицированным.
|
||||
// Если пользователь уже загружен, инициализируем его зависимости.
|
||||
await di.initUserSpecificDependencies(userState.user!.id);
|
||||
emit(AuthAuthenticated(user: userState.user!));
|
||||
} else {
|
||||
// Если пользователь не загружен (null) или состояние другое, считаем его неаутентифицированным.
|
||||
emit(AuthUnauthenticated());
|
||||
}
|
||||
}
|
||||
|
||||
void _onAuthLoggedIn(AuthLoggedIn event, Emitter<AuthState> emit) {
|
||||
// Комментарий: Обновляем UserCubit с новым пользователем.
|
||||
void _onAuthLoggedIn(AuthLoggedIn event, Emitter<AuthState> emit) async {
|
||||
// Инициализируем зависимости для вошедшего пользователя.
|
||||
await di.initUserSpecificDependencies(event.user.id);
|
||||
_userCubit.setUser(event.user);
|
||||
emit(AuthAuthenticated(user: event.user));
|
||||
}
|
||||
|
||||
void _onAuthLoggedOut(AuthLoggedOut event, Emitter<AuthState> emit) {
|
||||
// Вызываем logout у UserCubit
|
||||
void _onAuthLoggedOut(AuthLoggedOut event, Emitter<AuthState> emit) async {
|
||||
// Сбрасываем пользовательские зависимости.
|
||||
await di.resetUserSpecificDependencies();
|
||||
_userCubit.logout();
|
||||
emit(AuthUnauthenticated());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,53 +1,52 @@
|
||||
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:budget_app/models/tag.dart';
|
||||
import 'package:hive_ce/hive.dart';
|
||||
import 'package:budget_app/logic/user/user_cubit.dart';
|
||||
import 'package:budget_app/data/repositories/interfaces/itag_repository.dart';
|
||||
import 'package:budget_app/logic/tag/tag_state.dart';
|
||||
import 'package:budget_app/models/tag.dart'; // Добавляем импорт модели Tag
|
||||
|
||||
class TagCubit extends Cubit<List<Tag>> {
|
||||
final UserCubit _userCubit;
|
||||
final Box<Tag> _tagBox;
|
||||
class TagCubit extends Cubit<TagState> {
|
||||
final ITagRepository _repository;
|
||||
|
||||
TagCubit(this._userCubit)
|
||||
: _tagBox = Hive.box<Tag>('tags'),
|
||||
super([]);
|
||||
TagCubit(this._repository) : super(TagInitial());
|
||||
|
||||
|
||||
void loadTags() {
|
||||
// Получаем текущего пользователя из состояния UserCubit
|
||||
final userState = _userCubit.state;
|
||||
if (userState is UserLoaded && userState.user != null) {
|
||||
final currentUserId = userState.user!.id;
|
||||
final userTags = _tagBox.values
|
||||
.where((tag) => tag.userId == currentUserId)
|
||||
.toList();
|
||||
emit(List.from(userTags));
|
||||
} else {
|
||||
emit([]);
|
||||
// Загрузка тегов
|
||||
Future<void> loadTags() async {
|
||||
emit(TagLoading());
|
||||
try {
|
||||
final tags = await _repository.getAll();
|
||||
emit(TagLoaded(tags));
|
||||
} catch (e) {
|
||||
emit(TagError('Ошибка загрузки тегов: ${e.toString()}'));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void addTag(Tag tag) {
|
||||
// Получаем текущего пользователя из состояния UserCubit
|
||||
final userState = _userCubit.state;
|
||||
if (userState is UserLoaded && userState.user != null) {
|
||||
final currentUserId = userState.user!.id;
|
||||
final newTag = tag.copyWith(userId: currentUserId);
|
||||
_tagBox.put(newTag.id, newTag);
|
||||
loadTags();
|
||||
// Добавление тега
|
||||
Future<void> addTag(Tag tag) async {
|
||||
try {
|
||||
await _repository.add(tag);
|
||||
await loadTags(); // Перезагружаем список
|
||||
} catch (e) {
|
||||
emit(TagError('Ошибка добавления тега: ${e.toString()}'));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void updateTag(Tag tag) {
|
||||
_tagBox.put(tag.id, tag);
|
||||
loadTags();
|
||||
// Обновление тега
|
||||
Future<void> updateTag(Tag tag) async {
|
||||
try {
|
||||
await _repository.update(tag);
|
||||
await loadTags(); // Перезагружаем список
|
||||
} catch (e) {
|
||||
emit(TagError('Ошибка обновления тега: ${e.toString()}'));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void deleteTag(String id) {
|
||||
_tagBox.delete(id);
|
||||
loadTags();
|
||||
// Удаление тега
|
||||
Future<void> deleteTag(String id) async {
|
||||
try {
|
||||
await _repository.delete(id);
|
||||
await loadTags(); // Перезагружаем список
|
||||
} catch (e) {
|
||||
emit(TagError('Ошибка удаления тега: ${e.toString()}'));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
import 'package:budget_app/models/tag.dart';
|
||||
|
||||
// Состояния для TagCubit
|
||||
abstract class TagState {}
|
||||
|
||||
class TagInitial extends TagState {}
|
||||
|
||||
class TagLoading extends TagState {}
|
||||
|
||||
class TagLoaded extends TagState {
|
||||
final List<Tag> tags;
|
||||
TagLoaded(this.tags);
|
||||
}
|
||||
|
||||
class TagError extends TagState {
|
||||
final String message;
|
||||
TagError(this.message);
|
||||
}
|
||||
@@ -20,9 +20,10 @@ part 'user_state.dart';
|
||||
class UserCubit extends Cubit<UserState> {
|
||||
final IGlobalSettingsRepository _settingsRepository;
|
||||
final IUserRepository _userRepository;
|
||||
final ICategoryRepository _categoryRepository;
|
||||
final ITagRepository _tagRepository;
|
||||
final ITransactionRepository _transactionRepository;
|
||||
// Эти репозитории теперь могут быть null и устанавливаются после входа пользователя.
|
||||
ICategoryRepository? _categoryRepository;
|
||||
ITagRepository? _tagRepository;
|
||||
ITransactionRepository? _transactionRepository;
|
||||
final Logger _logger = Logger(
|
||||
printer: PrettyPrinter(
|
||||
methodCount: 2, // Number of method calls to be displayed
|
||||
@@ -38,15 +39,22 @@ class UserCubit extends Cubit<UserState> {
|
||||
UserCubit({
|
||||
required IGlobalSettingsRepository settingsRepository,
|
||||
required IUserRepository userRepository,
|
||||
required ICategoryRepository categoryRepository,
|
||||
required ITagRepository tagRepository,
|
||||
required ITransactionRepository transactionRepository,
|
||||
}) : _settingsRepository = settingsRepository,
|
||||
_userRepository = userRepository,
|
||||
_categoryRepository = categoryRepository,
|
||||
_tagRepository = tagRepository,
|
||||
_transactionRepository = transactionRepository,
|
||||
super(UserInitial());
|
||||
// Репозитории сделаны опциональными в конструкторе.
|
||||
ICategoryRepository? categoryRepository,
|
||||
ITagRepository? tagRepository,
|
||||
ITransactionRepository? transactionRepository,
|
||||
}) : _settingsRepository = settingsRepository,
|
||||
_userRepository = userRepository,
|
||||
_categoryRepository = categoryRepository,
|
||||
_tagRepository = tagRepository,
|
||||
_transactionRepository = transactionRepository,
|
||||
super(UserInitial());
|
||||
|
||||
// Сеттеры для внедрения зависимостей после аутентификации.
|
||||
set categoryRepository(ICategoryRepository? repo) => _categoryRepository = repo;
|
||||
set tagRepository(ITagRepository? repo) => _tagRepository = repo;
|
||||
set transactionRepository(ITransactionRepository? repo) =>
|
||||
_transactionRepository = repo;
|
||||
|
||||
// Метод для инициализации UserCubit
|
||||
Future<void> init() async {
|
||||
@@ -95,21 +103,30 @@ class UserCubit extends Cubit<UserState> {
|
||||
}
|
||||
|
||||
Future<void> _createInitialData(String userId) async {
|
||||
// Проверяем, что репозитории были установлены, прежде чем их использовать.
|
||||
if (_categoryRepository == null ||
|
||||
_tagRepository == null ||
|
||||
_transactionRepository == null) {
|
||||
_logger.w('User-specific repositories are not initialized. Skipping initial data creation.');
|
||||
return;
|
||||
}
|
||||
try {
|
||||
// Разбиваем создание данных на этапы
|
||||
emit(UserLoading(progress: 0.7, message: 'Создание категорий...'));
|
||||
await _categoryRepository.addAll(
|
||||
// Используем '!', так как мы уже проверили на null.
|
||||
await _categoryRepository!.addAll(
|
||||
CategoryUtils.getDefaultCategories(userId),
|
||||
);
|
||||
|
||||
emit(UserLoading(progress: 0.8, message: 'Создание тегов...'));
|
||||
await _tagRepository.addAll(TagUtils.getDefaultTags(userId));
|
||||
await _tagRepository!.addAll(TagUtils.getDefaultTags(userId));
|
||||
|
||||
emit(UserLoading(progress: 0.9, message: 'Создание транзакций...'));
|
||||
await _transactionRepository.addAll(
|
||||
await _transactionRepository!.addAll(
|
||||
TransactionUtils.getSampleTransactions(userId),
|
||||
);
|
||||
} catch (e, stack) {
|
||||
|
||||
_logger.e(
|
||||
'Error creating initial data for user: $userId',
|
||||
error: e,
|
||||
|
||||
+52
-43
@@ -5,6 +5,7 @@ import 'package:flutter_localizations/flutter_localizations.dart'; // Добав
|
||||
import 'package:get_it/get_it.dart';
|
||||
|
||||
import '/l10n/app_localizations.dart';
|
||||
import '/pages/splash/splash_screen.dart';
|
||||
import 'injection_container.dart' as di;
|
||||
import 'logic/auth/auth_bloc.dart';
|
||||
import 'logic/settings/settings_cubit.dart'; // Импортируем SettingsCubit
|
||||
@@ -14,25 +15,9 @@ import 'logic/user/user_cubit.dart'; // Импортируем UserCubit
|
||||
import 'pages/login/login_page.dart';
|
||||
import 'theme/app_theme.dart';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:flutter_localizations/flutter_localizations.dart';
|
||||
|
||||
import '/l10n/app_localizations.dart';
|
||||
import '/logic/auth/auth_bloc.dart';
|
||||
import '/logic/settings/settings_cubit.dart';
|
||||
import '/logic/sms/sms_cubit.dart';
|
||||
import '/logic/transaction/transaction_bloc.dart';
|
||||
import '/logic/user/user_cubit.dart';
|
||||
import '/pages/home/home_page.dart';
|
||||
import '/pages/login/login_page.dart';
|
||||
import '/pages/splash/splash_screen.dart';
|
||||
import '/theme/app_theme.dart';
|
||||
import 'injection_container.dart' as di;
|
||||
|
||||
void main() async {
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
await di.initDependencies();
|
||||
await di.initGlobalDependencies();
|
||||
runApp(const MyApp());
|
||||
}
|
||||
|
||||
@@ -41,6 +26,7 @@ class MyApp extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
// 1. Предоставляем глобальные Blocs, которые доступны всегда.
|
||||
return MultiBlocProvider(
|
||||
providers: [
|
||||
BlocProvider(
|
||||
@@ -49,31 +35,63 @@ class MyApp extends StatelessWidget {
|
||||
BlocProvider(
|
||||
create: (context) => GetIt.instance<AuthBloc>(),
|
||||
),
|
||||
BlocProvider(create: (context) => GetIt.instance<SettingsCubit>()),
|
||||
BlocProvider(create: (context) => GetIt.instance<TransactionBloc>()),
|
||||
BlocProvider(create: (context) => GetIt.instance<SmsCubit>()),
|
||||
],
|
||||
child: BlocListener<UserCubit, UserState>(
|
||||
// Запускаем AuthBloc, как только UserCubit завершил загрузку,
|
||||
// независимо от того, найден пользователь или нет.
|
||||
// 2. Запускаем проверку аутентификации, как только UserCubit загрузил данные.
|
||||
listenWhen: (previous, current) => current is UserLoaded,
|
||||
listener: (context, userState) {
|
||||
context.read<AuthBloc>().add(AuthStarted());
|
||||
},
|
||||
child: BlocBuilder<SettingsCubit, SettingsState>(
|
||||
builder: (context, settingsState) {
|
||||
final isDarkMode = settingsState is SettingsLoaded
|
||||
? settingsState.isDarkMode
|
||||
: false;
|
||||
final languageCode = settingsState is SettingsLoaded
|
||||
? settingsState.languageCode
|
||||
: 'ru';
|
||||
// 3. В зависимости от статуса аутентификации, строим разное дерево виджетов.
|
||||
child: BlocBuilder<AuthBloc, AuthState>(
|
||||
builder: (context, authState) {
|
||||
// 4. Если пользователь аутентифицирован...
|
||||
if (authState is AuthAuthenticated) {
|
||||
// ...предоставляем все пользовательские зависимости.
|
||||
return MultiBlocProvider(
|
||||
providers: [
|
||||
BlocProvider(create: (context) => GetIt.instance<SettingsCubit>()),
|
||||
BlocProvider(create: (context) => GetIt.instance<TransactionBloc>()),
|
||||
BlocProvider(create: (context) => GetIt.instance<SmsCubit>()),
|
||||
// Можно добавить и остальные, если они нужны глобально в авторизованной зоне
|
||||
],
|
||||
// И строим приложение с пользовательской темой.
|
||||
child: BlocBuilder<SettingsCubit, SettingsState>(
|
||||
builder: (context, settingsState) {
|
||||
final isDarkMode = settingsState is SettingsLoaded
|
||||
? settingsState.isDarkMode
|
||||
: false;
|
||||
final languageCode = settingsState is SettingsLoaded
|
||||
? settingsState.languageCode
|
||||
: 'ru';
|
||||
|
||||
return MaterialApp(
|
||||
title: 'Budget App',
|
||||
theme: AppTheme.lightTheme(),
|
||||
darkTheme: AppTheme.darkTheme(),
|
||||
themeMode: isDarkMode ? ThemeMode.dark : ThemeMode.light,
|
||||
localizationsDelegates: const [
|
||||
AppLocalizations.delegate,
|
||||
GlobalMaterialLocalizations.delegate,
|
||||
GlobalWidgetsLocalizations.delegate,
|
||||
GlobalCupertinoLocalizations.delegate,
|
||||
],
|
||||
supportedLocales: AppLocalizations.supportedLocales,
|
||||
locale: Locale(languageCode),
|
||||
home: const HomePage(),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// 5. Если пользователь НЕ аутентифицирован, показываем SplashScreen или LoginPage
|
||||
// с темой по умолчанию.
|
||||
return MaterialApp(
|
||||
title: AppLocalizations.of(context)?.appTitle ?? 'Budget App',
|
||||
title: 'Budget App',
|
||||
theme: AppTheme.lightTheme(),
|
||||
darkTheme: AppTheme.darkTheme(),
|
||||
themeMode: isDarkMode ? ThemeMode.dark : ThemeMode.light,
|
||||
themeMode: ThemeMode.light, // Тема по умолчанию
|
||||
localizationsDelegates: const [
|
||||
AppLocalizations.delegate,
|
||||
GlobalMaterialLocalizations.delegate,
|
||||
@@ -81,22 +99,13 @@ class MyApp extends StatelessWidget {
|
||||
GlobalCupertinoLocalizations.delegate,
|
||||
],
|
||||
supportedLocales: AppLocalizations.supportedLocales,
|
||||
locale: Locale(languageCode),
|
||||
locale: const Locale('ru'), // Язык по умолчанию
|
||||
home: BlocBuilder<UserCubit, UserState>(
|
||||
builder: (context, userState) {
|
||||
if (userState is UserLoading || userState is UserInitial) {
|
||||
return const SplashScreen();
|
||||
}
|
||||
|
||||
return BlocBuilder<AuthBloc, AuthState>(
|
||||
builder: (context, authState) {
|
||||
if (authState is AuthAuthenticated) {
|
||||
return const HomePage();
|
||||
} else {
|
||||
return const LoginPage();
|
||||
}
|
||||
},
|
||||
);
|
||||
return const LoginPage();
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import 'package:budget_app/l10n/app_localizations.dart';
|
||||
import 'package:budget_app/logic/tag/tag_cubit.dart';
|
||||
import 'package:budget_app/logic/tag/tag_state.dart'; // Импортируем состояния
|
||||
import 'package:budget_app/models/tag.dart';
|
||||
import 'package:budget_app/pages/tag/tag_edit_page.dart';
|
||||
import 'package:budget_app/pages/tag/widgets/add_tag_button.dart';
|
||||
import 'package:budget_app/pages/tag/widgets/tag_list_item.dart';
|
||||
import 'package:budget_app/logic/user/user_cubit.dart'; // Импортируем UserCubit
|
||||
import 'package:budget_app/logic/user/user_cubit.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:get_it/get_it.dart';
|
||||
@@ -21,36 +22,40 @@ class _TagListPageState extends State<TagListPage> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
||||
return BlocProvider(
|
||||
create: (context) => GetIt.instance<TagCubit>()..loadTags(),
|
||||
|
||||
child: Builder(builder: (context) {
|
||||
final localizations = AppLocalizations.of(context)!;
|
||||
return Scaffold(
|
||||
appBar: AppBar(title: Text(localizations.editTags)),
|
||||
body: BlocBuilder<TagCubit, List<Tag>>(
|
||||
builder: (context, tags) {
|
||||
return AnimatedList(
|
||||
key: _listKey,
|
||||
initialItemCount: tags.length,
|
||||
itemBuilder: (context, index, animation) {
|
||||
final tag = tags[index];
|
||||
return SizeTransition(
|
||||
sizeFactor: animation,
|
||||
child: TagListItem(
|
||||
tag: tag,
|
||||
onEdit: () => _editTag(context, tag),
|
||||
onDelete: () =>
|
||||
_deleteTag(context, tag, index),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
body: BlocBuilder<TagCubit, TagState>(
|
||||
builder: (context, state) {
|
||||
if (state is TagLoading) {
|
||||
return const Center(child: CircularProgressIndicator());
|
||||
} else if (state is TagError) {
|
||||
return Center(child: Text(state.message));
|
||||
} else if (state is TagLoaded) {
|
||||
return AnimatedList(
|
||||
key: _listKey,
|
||||
initialItemCount: state.tags.length,
|
||||
itemBuilder: (context, index, animation) {
|
||||
final tag = state.tags[index];
|
||||
return SizeTransition(
|
||||
sizeFactor: animation,
|
||||
child: TagListItem(
|
||||
tag: tag,
|
||||
onEdit: () => _editTag(context, tag),
|
||||
onDelete: () => _deleteTag(context, tag, index),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
} else {
|
||||
return const Center(child: Text('Начните работу с тегами'));
|
||||
}
|
||||
},
|
||||
),
|
||||
floatingActionButton: AddTagButton(
|
||||
|
||||
onPressed: () => _addTag(context),
|
||||
),
|
||||
);
|
||||
@@ -64,11 +69,8 @@ class _TagListPageState extends State<TagListPage> {
|
||||
MaterialPageRoute(
|
||||
builder: (_) => TagEditPage(
|
||||
onSave: (name) {
|
||||
// Получаем ID текущего пользователя из UserCubit
|
||||
final userState = context.read<UserCubit>().state;
|
||||
if (userState is! UserLoaded || userState.user == null) {
|
||||
// Обработка случая, когда пользователь не аутентифицирован
|
||||
// Возможно, показать сообщение об ошибке или перенаправить на страницу входа
|
||||
return;
|
||||
}
|
||||
final currentUserId = userState.user!.id;
|
||||
@@ -91,9 +93,7 @@ class _TagListPageState extends State<TagListPage> {
|
||||
builder: (_) => TagEditPage(
|
||||
tag: tag,
|
||||
onSave: (name) {
|
||||
final updatedTag = tag.copyWith(
|
||||
name: name,
|
||||
);
|
||||
final updatedTag = tag.copyWith(name: name);
|
||||
context.read<TagCubit>().updateTag(updatedTag);
|
||||
},
|
||||
),
|
||||
|
||||
Reference in New Issue
Block a user