Refactors data repositories for single-user mode
This commit refactors the data repositories to remove user-specific filtering. It simplifies the data access logic by retrieving all data from the Hive boxes and removing the need to filter by `userId` in the repository methods. This change makes the app function in single-user mode and removes the need to manage multiple user contexts within the data layer. Specifically: - Removes `userId` parameter from repository methods. - Updates the setting repository to use fixed keys. - Removes user ID filtering from queries. - Removes user ID parameters from Cubits and Blocs - Updates initial data creation
This commit is contained in:
@@ -76,7 +76,7 @@ class HiveCategoryRepository implements ICategoryRepository {
|
||||
@override
|
||||
Future<List<Category>> getAllByUser(String userId) async {
|
||||
try {
|
||||
return _box.values.where((c) => c.userId == userId).toList();
|
||||
return _box.values.toList();
|
||||
} catch (e) {
|
||||
throw Exception('Ошибка получения категорий пользователя: $e');
|
||||
}
|
||||
@@ -86,7 +86,7 @@ class HiveCategoryRepository implements ICategoryRepository {
|
||||
Future<List<Category>> getIncomeCategoriesByUser(String userId) async {
|
||||
try {
|
||||
return _box.values
|
||||
.where((c) => c.userId == userId && c.isIncome)
|
||||
.where((c) => c.isIncome)
|
||||
.toList();
|
||||
} catch (e) {
|
||||
throw Exception('Ошибка получения категорий доходов пользователя: $e');
|
||||
@@ -97,7 +97,7 @@ class HiveCategoryRepository implements ICategoryRepository {
|
||||
Future<List<Category>> getExpenseCategoriesByUser(String userId) async {
|
||||
try {
|
||||
return _box.values
|
||||
.where((c) => c.userId == userId && !c.isIncome)
|
||||
.where((c) => !c.isIncome)
|
||||
.toList();
|
||||
} catch (e) {
|
||||
throw Exception('Ошибка получения категорий расходов пользователя: $e');
|
||||
|
||||
@@ -5,15 +5,26 @@ import 'package:hive_ce/hive.dart';
|
||||
/// Реализация репозитория настроек с использованием Hive
|
||||
class HiveSettingsRepository implements ISettingsRepository {
|
||||
final Box<AppSettings> _box;
|
||||
|
||||
// Фиксированный ключ для хранения настроек приложения
|
||||
static const String _settingsKey = 'user_settings';
|
||||
|
||||
HiveSettingsRepository(this._box);
|
||||
|
||||
@override
|
||||
Future<AppSettings> getSettings(String userId) async {
|
||||
Future<AppSettings> getSettings() async {
|
||||
try {
|
||||
// Возвращаем настройки пользователя или создаем новые по умолчанию
|
||||
final settings = _box.get(userId, defaultValue: AppSettings(userId: userId));
|
||||
return settings!; // Гарантируем возврат не-null значения
|
||||
// Получаем настройки по ключу вместо индекса
|
||||
final settings = _box.get(_settingsKey);
|
||||
|
||||
// Если настройки не существуют, создаем новые с значениями по умолчанию
|
||||
if (settings == null) {
|
||||
final defaultSettings = AppSettings();
|
||||
await _box.put(_settingsKey, defaultSettings);
|
||||
return defaultSettings;
|
||||
}
|
||||
|
||||
return settings;
|
||||
} catch (e) {
|
||||
throw Exception('Ошибка получения настроек: $e');
|
||||
}
|
||||
@@ -22,17 +33,18 @@ class HiveSettingsRepository implements ISettingsRepository {
|
||||
@override
|
||||
Future<void> saveSettings(AppSettings settings) async {
|
||||
try {
|
||||
// Сохраняем настройки с ключом = userId
|
||||
await _box.put(settings.userId, settings);
|
||||
// Сохраняем настройки с фиксированным ключом
|
||||
await _box.put(_settingsKey, settings);
|
||||
} catch (e) {
|
||||
throw Exception('Ошибка сохранения настроек: $e');
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> deleteSettings(String userId) async {
|
||||
Future<void> deleteSettings() async {
|
||||
try {
|
||||
await _box.delete(userId);
|
||||
// Удаляем настройки по фиксированному ключу
|
||||
await _box.delete(_settingsKey);
|
||||
} catch (e) {
|
||||
throw Exception('Ошибка удаления настроек: $e');
|
||||
}
|
||||
|
||||
@@ -10,26 +10,25 @@ class HiveSmsHandlerRepository implements ISmsHandlerRepository {
|
||||
HiveSmsHandlerRepository(this._smsHandlerBox);
|
||||
|
||||
@override
|
||||
Future<SmsHandlerSettings?> getSmsHandlerSettings(String userId) async {
|
||||
Future<SmsHandlerSettings?> getSmsHandlerSettings() async {
|
||||
// В Hive мы будем использовать ID пользователя как ключ для его настроек.
|
||||
return _smsHandlerBox.get(userId);
|
||||
return _smsHandlerBox.getAt(1);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> saveSmsHandlerSettings(SmsHandlerSettings settings) async {
|
||||
// Сохраняем объект настроек по ключу, равному ID пользователя.
|
||||
await _smsHandlerBox.put(settings.userId, settings);
|
||||
await _smsHandlerBox.put(settings.id, settings);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> saveRuleForSender(String userId, String sender, SmsProcessingRule rule) async {
|
||||
Future<void> saveRuleForSender(String sender, SmsProcessingRule rule) async {
|
||||
// 1. Получаем текущие настройки.
|
||||
SmsHandlerSettings? settings = await getSmsHandlerSettings(userId);
|
||||
SmsHandlerSettings? settings = await getSmsHandlerSettings();
|
||||
|
||||
if (settings == null) {
|
||||
// 2. Если настроек нет, создаем новый объект.
|
||||
settings = SmsHandlerSettings(
|
||||
userId: userId,
|
||||
rulesBySender: {sender: rule}, // Создаем карту с первым правилом
|
||||
);
|
||||
} else {
|
||||
@@ -42,9 +41,9 @@ class HiveSmsHandlerRepository implements ISmsHandlerRepository {
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> deleteRuleForSender(String userId, String sender) async {
|
||||
Future<void> deleteRuleForSender(String sender) async {
|
||||
// 1. Получаем текущие настройки.
|
||||
final settings = await getSmsHandlerSettings(userId);
|
||||
final settings = await getSmsHandlerSettings();
|
||||
|
||||
if (settings != null) {
|
||||
// 2. Если настройки существуют, удаляем правило для отправителя.
|
||||
|
||||
@@ -60,40 +60,6 @@ class HiveTransactionRepository implements ITransactionRepository {
|
||||
.toList();
|
||||
}
|
||||
|
||||
// Добавляем новый метод для получения всех транзакций конкретного пользователя
|
||||
@override
|
||||
Future<List<TransactionRecord>> getAllByUser(String userId) async {
|
||||
// Фильтруем все транзакции в Box по userId
|
||||
return _box.values.where((t) => t.userId == userId).toList();
|
||||
}
|
||||
|
||||
// Добавляем новый метод для получения транзакций пользователя по диапазону дат
|
||||
@override
|
||||
Future<List<TransactionRecord>> getByUserAndDateRange(String userId, DateTime from, DateTime to) async {
|
||||
// Фильтруем транзакции сначала по userId, затем по диапазону дат
|
||||
return _box.values
|
||||
.where((t) => t.userId == userId && t.dateTime.isAfter(from) && t.dateTime.isBefore(to))
|
||||
.toList();
|
||||
}
|
||||
|
||||
// Добавляем новый метод для получения транзакций пользователя по категории
|
||||
@override
|
||||
Future<List<TransactionRecord>> getByUserAndCategory(String userId, String categoryId) async {
|
||||
// Фильтруем транзакции сначала по userId, затем по категории
|
||||
return _box.values
|
||||
.where((t) => t.userId == userId && t.category.id == categoryId)
|
||||
.toList();
|
||||
}
|
||||
|
||||
// Добавляем новый метод для получения транзакций пользователя по тегу
|
||||
@override
|
||||
Future<List<TransactionRecord>> getByUserAndTag(String userId, String tagId) async {
|
||||
// Фильтруем транзакции сначала по userId, затем по тегу
|
||||
return _box.values
|
||||
.where((t) => t.userId == userId && t.tag?.id == tagId)
|
||||
.toList();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> addAll(List<TransactionRecord> transactions) async {
|
||||
final Map<String, TransactionRecord> transactionMap = {
|
||||
|
||||
@@ -10,16 +10,6 @@ abstract class ICategoryRepository {
|
||||
Future<List<Category>> getIncomeCategories();
|
||||
Future<List<Category>> getExpenseCategories();
|
||||
|
||||
// Новые методы для работы с пользователями
|
||||
/// Получить все категории конкретного пользователя
|
||||
Future<List<Category>> getAllByUser(String userId);
|
||||
|
||||
/// Получить категории доходов конкретного пользователя
|
||||
Future<List<Category>> getIncomeCategoriesByUser(String userId);
|
||||
|
||||
/// Получить категории расходов конкретного пользователя
|
||||
Future<List<Category>> getExpenseCategoriesByUser(String userId);
|
||||
|
||||
/// Добавить список категорий
|
||||
Future<void> addAll(List<Category> categories);
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ import 'package:budget_app/models/app_settings.dart';
|
||||
abstract class ISettingsRepository {
|
||||
/// Получает настройки для указанного пользователя
|
||||
/// [userId] - идентификатор пользователя
|
||||
Future<AppSettings> getSettings(String userId);
|
||||
Future<AppSettings> getSettings();
|
||||
|
||||
/// Сохраняет настройки
|
||||
/// [settings] - объект настроек для сохранения
|
||||
@@ -12,5 +12,5 @@ abstract class ISettingsRepository {
|
||||
|
||||
/// Удаляет настройки для указанного пользователя
|
||||
/// [userId] - идентификатор пользователя
|
||||
Future<void> deleteSettings(String userId);
|
||||
Future<void> deleteSettings();
|
||||
}
|
||||
|
||||
@@ -6,9 +6,7 @@ import '../../../models/sms_handler_settings.dart';
|
||||
abstract class ISmsHandlerRepository {
|
||||
/// Получает настройки обработки СМС для указанного пользователя.
|
||||
///
|
||||
/// [userId] - Уникальный идентификатор пользователя.
|
||||
/// Возвращает [SmsHandlerSettings] или null, если настроек нет.
|
||||
Future<SmsHandlerSettings?> getSmsHandlerSettings(String userId);
|
||||
Future<SmsHandlerSettings?> getSmsHandlerSettings();
|
||||
|
||||
/// Сохраняет или обновляет настройки обработки СМС для пользователя.
|
||||
///
|
||||
@@ -20,11 +18,11 @@ abstract class ISmsHandlerRepository {
|
||||
/// [userId] - ID пользователя.
|
||||
/// [sender] - Идентификатор отправителя (например, 'SBERBANK').
|
||||
/// [rule] - Правило обработки.
|
||||
Future<void> saveRuleForSender(String userId, String sender, SmsProcessingRule rule);
|
||||
Future<void> saveRuleForSender(String sender, SmsProcessingRule rule);
|
||||
|
||||
/// Удаляет правило для конкретного отправителя.
|
||||
///
|
||||
/// [userId] - ID пользователя.
|
||||
/// [sender] - Идентификатор отправителя, чье правило нужно удалить.
|
||||
Future<void> deleteRuleForSender(String userId, String sender);
|
||||
Future<void> deleteRuleForSender(String sender);
|
||||
}
|
||||
|
||||
@@ -7,12 +7,6 @@ abstract class ITagRepository {
|
||||
Future<void> update(Tag tag);
|
||||
Future<void> delete(String id);
|
||||
|
||||
// Комментарий: Добавляем новый абстрактный метод в интерфейс.
|
||||
// Все классы, которые реализуют этот интерфейс, должны будут предоставить
|
||||
// реализацию этого метода. Это гарантирует, что наш репозиторий
|
||||
// сможет получать теги для конкретного пользователя.
|
||||
Future<List<Tag>> getAllByUser(String userId);
|
||||
|
||||
/// Добавить список тегов
|
||||
Future<void> addAll(List<Tag> tags);
|
||||
}
|
||||
|
||||
@@ -11,13 +11,6 @@ abstract class ITransactionRepository {
|
||||
Future<List<TransactionRecord>> getByCategory(String categoryId);
|
||||
Future<List<TransactionRecord>> getByTag(String tagId);
|
||||
|
||||
// Добавляем новые методы для работы с транзакциями конкретного пользователя
|
||||
// Это важно для многопользовательской архитектуры
|
||||
Future<List<TransactionRecord>> getAllByUser(String userId);
|
||||
Future<List<TransactionRecord>> getByUserAndDateRange(String userId, DateTime from, DateTime to);
|
||||
Future<List<TransactionRecord>> getByUserAndCategory(String userId, String categoryId);
|
||||
Future<List<TransactionRecord>> getByUserAndTag(String userId, String tagId);
|
||||
|
||||
/// Добавить список транзакций
|
||||
Future<void> addAll(List<TransactionRecord> transactions);
|
||||
}
|
||||
|
||||
@@ -4,8 +4,6 @@ import 'package:hive_ce/hive.dart';
|
||||
|
||||
|
||||
@GenerateAdapters([
|
||||
AdapterSpec<Color>(),
|
||||
AdapterSpec<IconData>(),
|
||||
AdapterSpec<DateTime>(),
|
||||
])
|
||||
part 'hive_adapters.g.dart';
|
||||
|
||||
@@ -6,38 +6,6 @@ part of 'hive_adapters.dart';
|
||||
// AdaptersGenerator
|
||||
// **************************************************************************
|
||||
|
||||
class ColorAdapter extends TypeAdapter<Color> {
|
||||
@override
|
||||
final typeId = 0;
|
||||
|
||||
@override
|
||||
Color read(BinaryReader reader) {
|
||||
final numOfFields = reader.readByte();
|
||||
final fields = <int, dynamic>{
|
||||
for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
|
||||
};
|
||||
return Color((fields[0] as num).toInt());
|
||||
}
|
||||
|
||||
@override
|
||||
void write(BinaryWriter writer, Color obj) {
|
||||
writer
|
||||
..writeByte(1)
|
||||
..writeByte(0)
|
||||
..write(obj.value);
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => typeId.hashCode;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) =>
|
||||
identical(this, other) ||
|
||||
other is ColorAdapter &&
|
||||
runtimeType == other.runtimeType &&
|
||||
typeId == other.typeId;
|
||||
}
|
||||
|
||||
class IconDataAdapter extends TypeAdapter<IconData> {
|
||||
@override
|
||||
final typeId = 1;
|
||||
@@ -83,58 +51,3 @@ class IconDataAdapter extends TypeAdapter<IconData> {
|
||||
runtimeType == other.runtimeType &&
|
||||
typeId == other.typeId;
|
||||
}
|
||||
|
||||
class DateTimeAdapter extends TypeAdapter<DateTime> {
|
||||
@override
|
||||
final typeId = 2;
|
||||
|
||||
@override
|
||||
DateTime read(BinaryReader reader) {
|
||||
final numOfFields = reader.readByte();
|
||||
final fields = <int, dynamic>{
|
||||
for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
|
||||
};
|
||||
return DateTime(
|
||||
(fields[0] as num).toInt(),
|
||||
fields[1] == null ? 1 : (fields[1] as num).toInt(),
|
||||
fields[2] == null ? 1 : (fields[2] as num).toInt(),
|
||||
fields[3] == null ? 0 : (fields[3] as num).toInt(),
|
||||
fields[4] == null ? 0 : (fields[4] as num).toInt(),
|
||||
fields[5] == null ? 0 : (fields[5] as num).toInt(),
|
||||
fields[6] == null ? 0 : (fields[6] as num).toInt(),
|
||||
fields[7] == null ? 0 : (fields[7] as num).toInt(),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void write(BinaryWriter writer, DateTime obj) {
|
||||
writer
|
||||
..writeByte(8)
|
||||
..writeByte(0)
|
||||
..write(obj.year)
|
||||
..writeByte(1)
|
||||
..write(obj.month)
|
||||
..writeByte(2)
|
||||
..write(obj.day)
|
||||
..writeByte(3)
|
||||
..write(obj.hour)
|
||||
..writeByte(4)
|
||||
..write(obj.minute)
|
||||
..writeByte(5)
|
||||
..write(obj.second)
|
||||
..writeByte(6)
|
||||
..write(obj.millisecond)
|
||||
..writeByte(7)
|
||||
..write(obj.microsecond);
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => typeId.hashCode;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) =>
|
||||
identical(this, other) ||
|
||||
other is DateTimeAdapter &&
|
||||
runtimeType == other.runtimeType &&
|
||||
typeId == other.typeId;
|
||||
}
|
||||
|
||||
@@ -3,12 +3,6 @@
|
||||
# Check in to version control
|
||||
nextTypeId: 3
|
||||
types:
|
||||
Color:
|
||||
typeId: 0
|
||||
nextIndex: 1
|
||||
fields:
|
||||
value:
|
||||
index: 0
|
||||
IconData:
|
||||
typeId: 1
|
||||
nextIndex: 5
|
||||
@@ -23,23 +17,3 @@ types:
|
||||
index: 3
|
||||
fontFamilyFallback:
|
||||
index: 4
|
||||
DateTime:
|
||||
typeId: 2
|
||||
nextIndex: 8
|
||||
fields:
|
||||
year:
|
||||
index: 0
|
||||
month:
|
||||
index: 1
|
||||
day:
|
||||
index: 2
|
||||
hour:
|
||||
index: 3
|
||||
minute:
|
||||
index: 4
|
||||
second:
|
||||
index: 5
|
||||
millisecond:
|
||||
index: 6
|
||||
microsecond:
|
||||
index: 7
|
||||
|
||||
@@ -17,8 +17,6 @@ extension HiveRegistrar on HiveInterface {
|
||||
void registerAdapters() {
|
||||
registerAdapter(AppSettingsAdapter());
|
||||
registerAdapter(CategoryAdapter());
|
||||
registerAdapter(ColorAdapter());
|
||||
registerAdapter(DateTimeAdapter());
|
||||
registerAdapter(GlobalSettingsAdapter());
|
||||
registerAdapter(IconDataAdapter());
|
||||
registerAdapter(SmsHandlerSettingsAdapter());
|
||||
@@ -35,8 +33,6 @@ extension IsolatedHiveRegistrar on IsolatedHiveInterface {
|
||||
void registerAdapters() {
|
||||
registerAdapter(AppSettingsAdapter());
|
||||
registerAdapter(CategoryAdapter());
|
||||
registerAdapter(ColorAdapter());
|
||||
registerAdapter(DateTimeAdapter());
|
||||
registerAdapter(GlobalSettingsAdapter());
|
||||
registerAdapter(IconDataAdapter());
|
||||
registerAdapter(SmsHandlerSettingsAdapter());
|
||||
|
||||
@@ -155,7 +155,7 @@ Future<void> initUserSpecificDependencies(String userId) async {
|
||||
await getIt.unregister<CategoryCubit>();
|
||||
}
|
||||
getIt.registerFactory<CategoryCubit>(
|
||||
() => CategoryCubit(getIt(), userId), // Добавляем userId
|
||||
() => CategoryCubit(getIt()), // Добавляем userId
|
||||
);
|
||||
|
||||
// Теги
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
|
||||
import 'package:equatable/equatable.dart'; // equatable для сравнения объектов
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import '../../data/repositories/interfaces/icategory_repository.dart';
|
||||
import '../../models/category.dart';
|
||||
import 'category_state.dart';
|
||||
|
||||
part 'category_state.dart'; // Используем part для разделения файла
|
||||
|
||||
/// Cubit для управления категориями
|
||||
/// Изменения:
|
||||
@@ -10,15 +12,14 @@ import 'category_state.dart';
|
||||
/// - Упрощена работа с состояниями по аналогии с TagCubit
|
||||
class CategoryCubit extends Cubit<CategoryState> {
|
||||
final ICategoryRepository repository;
|
||||
final String userId; // Храним userId в cubit
|
||||
|
||||
CategoryCubit(this.repository, this.userId) : super(CategoryInitial());
|
||||
CategoryCubit(this.repository) : super(CategoryInitial());
|
||||
|
||||
/// Загружает категории для текущего пользователя
|
||||
Future<void> loadCategories() async {
|
||||
emit(CategoryLoading());
|
||||
try {
|
||||
final categories = await repository.getAllByUser(userId);
|
||||
final categories = await repository.getAll();
|
||||
emit(CategoryLoaded(categories));
|
||||
} catch (e) {
|
||||
emit(CategoryError('Ошибка загрузки категорий: $e'));
|
||||
@@ -29,7 +30,7 @@ class CategoryCubit extends Cubit<CategoryState> {
|
||||
Future<void> addCategory(Category category) async {
|
||||
try {
|
||||
// Устанавливаем userId для категории из cubit
|
||||
final newCategory = category.copyWith(userId: userId);
|
||||
final newCategory = category.copyWith();
|
||||
await repository.add(newCategory);
|
||||
// Перезагружаем список категорий
|
||||
await loadCategories();
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
part of 'category_cubit.dart';
|
||||
|
||||
abstract class CategoryState extends Equatable {
|
||||
const CategoryState();
|
||||
|
||||
@override
|
||||
List<Object> get props => [];
|
||||
}
|
||||
|
||||
class CategoryInitial extends CategoryState {}
|
||||
|
||||
class CategoryLoading extends CategoryState {}
|
||||
|
||||
class CategoryLoaded extends CategoryState {
|
||||
final List<Category> categories;
|
||||
|
||||
const CategoryLoaded(this.categories);
|
||||
|
||||
@override
|
||||
List<Object> get props => [categories];
|
||||
}
|
||||
|
||||
class CategoryError extends CategoryState {
|
||||
final String message;
|
||||
|
||||
const CategoryError(this.message);
|
||||
|
||||
@override
|
||||
List<Object> get props => [message];
|
||||
}
|
||||
@@ -12,10 +12,10 @@ class SettingsCubit extends Cubit<SettingsState> {
|
||||
|
||||
SettingsCubit(this._settingsRepository) : super(SettingsInitial());
|
||||
|
||||
Future<void> loadSettings(String userId) async {
|
||||
Future<void> loadSettings() async {
|
||||
emit(SettingsLoading());
|
||||
try {
|
||||
final settings = await _settingsRepository.getSettings(userId);
|
||||
final settings = await _settingsRepository.getSettings();
|
||||
emit(SettingsLoaded(
|
||||
isDarkMode: settings.isDarkMode,
|
||||
languageCode: settings.languageCode,
|
||||
@@ -26,48 +26,47 @@ class SettingsCubit extends Cubit<SettingsState> {
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> toggleDarkMode(bool value, String userId) async {
|
||||
Future<void> toggleDarkMode(bool value) async {
|
||||
if (state is SettingsLoaded) {
|
||||
try {
|
||||
final currentState = state as SettingsLoaded;
|
||||
final newState = currentState.copyWith(isDarkMode: value);
|
||||
emit(newState);
|
||||
await _saveSettings(newState, userId);
|
||||
await _saveSettings(newState);
|
||||
} catch (e) {
|
||||
emit(SettingsError(e.toString()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> changeLanguage(String languageCode, String userId) async {
|
||||
Future<void> changeLanguage(String languageCode) async {
|
||||
if (state is SettingsLoaded) {
|
||||
try {
|
||||
final currentState = state as SettingsLoaded;
|
||||
final newState = currentState.copyWith(languageCode: languageCode);
|
||||
emit(newState);
|
||||
await _saveSettings(newState, userId);
|
||||
await _saveSettings(newState);
|
||||
} catch (e) {
|
||||
emit(SettingsError(e.toString()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> changeCurrency(String currency, String userId) async {
|
||||
Future<void> changeCurrency(String currency) async {
|
||||
if (state is SettingsLoaded) {
|
||||
try {
|
||||
final currentState = state as SettingsLoaded;
|
||||
final newState = currentState.copyWith(defaultCurrency: currency);
|
||||
emit(newState);
|
||||
await _saveSettings(newState, userId);
|
||||
await _saveSettings(newState);
|
||||
} catch (e) {
|
||||
emit(SettingsError(e.toString()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _saveSettings(SettingsLoaded settings, String userId) async {
|
||||
Future<void> _saveSettings(SettingsLoaded settings) async {
|
||||
final appSettings = AppSettings(
|
||||
userId: userId,
|
||||
isDarkMode: settings.isDarkMode,
|
||||
languageCode: settings.languageCode,
|
||||
defaultCurrency: settings.defaultCurrency,
|
||||
|
||||
@@ -31,13 +31,8 @@ class SmsCubit extends Cubit<SmsState> {
|
||||
try {
|
||||
final hasPermissions = await _smsService.requestPermissions();
|
||||
if (hasPermissions) {
|
||||
final userState = _userCubit.state;
|
||||
if (userState is UserLoaded) {
|
||||
final messages = await _smsService.getLastSmsMessages(10, userState.user!.id);
|
||||
emit(SmsLoaded(messages));
|
||||
} else {
|
||||
emit(SmsError("User not loaded"));
|
||||
}
|
||||
final messages = await _smsService.getLastSmsMessages(10);
|
||||
emit(SmsLoaded(messages));
|
||||
} else {
|
||||
emit(SmsPermissionDenied());
|
||||
}
|
||||
@@ -60,7 +55,7 @@ class SmsCubit extends Cubit<SmsState> {
|
||||
final user = userState.user;
|
||||
|
||||
// Комментарий: Получаем все SMS-сообщения с момента последней синхронизации.
|
||||
final messages = await _smsService.getSmsMessagesSince(user!.lastSmsSyncTime, user.id);
|
||||
final messages = await _smsService.getSmsMessagesSince(user!.lastSmsSyncTime);
|
||||
|
||||
// Комментарий: Сохраняем новые сообщения через репозиторий и создаем транзакции.
|
||||
await _smsRepository.addAll(messages);
|
||||
|
||||
@@ -21,7 +21,7 @@ class TransactionBloc extends Bloc<TransactionEvent, TransactionState> {
|
||||
void _onLoadTransactions(LoadTransactions event, Emitter<TransactionState> emit) async {
|
||||
emit(TransactionLoading());
|
||||
try {
|
||||
final transactions = await _transactionRepository.getAllByUser(event.userId);
|
||||
final transactions = await _transactionRepository.getAll();
|
||||
emit(TransactionLoaded(transactions: transactions));
|
||||
} catch (e) {
|
||||
emit(TransactionError(message: e.toString()));
|
||||
@@ -31,7 +31,7 @@ class TransactionBloc extends Bloc<TransactionEvent, TransactionState> {
|
||||
void _onAddTransaction(AddTransaction event, Emitter<TransactionState> emit) async {
|
||||
try {
|
||||
await _transactionRepository.add(event.transaction);
|
||||
final transactions = await _transactionRepository.getAllByUser(event.transaction.userId);
|
||||
final transactions = await _transactionRepository.getAll();
|
||||
emit(TransactionLoaded(transactions: transactions));
|
||||
} catch (e) {
|
||||
emit(TransactionError(message: e.toString()));
|
||||
@@ -41,7 +41,7 @@ class TransactionBloc extends Bloc<TransactionEvent, TransactionState> {
|
||||
void _onUpdateTransaction(UpdateTransaction event, Emitter<TransactionState> emit) async {
|
||||
try {
|
||||
await _transactionRepository.update(event.transaction);
|
||||
final transactions = await _transactionRepository.getAllByUser(event.transaction.userId);
|
||||
final transactions = await _transactionRepository.getAll();
|
||||
emit(TransactionLoaded(transactions: transactions));
|
||||
} catch (e) {
|
||||
emit(TransactionError(message: e.toString()));
|
||||
@@ -54,9 +54,8 @@ class TransactionBloc extends Bloc<TransactionEvent, TransactionState> {
|
||||
if (state is TransactionLoaded) {
|
||||
final loadedState = state as TransactionLoaded;
|
||||
if (loadedState.transactions.isNotEmpty) {
|
||||
final userId = loadedState.transactions.first.userId;
|
||||
await _transactionRepository.delete(event.transactionId);
|
||||
final transactions = await _transactionRepository.getAllByUser(userId);
|
||||
final transactions = await _transactionRepository.getAll();
|
||||
emit(TransactionLoaded(transactions: transactions));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -87,22 +87,7 @@ class UserCubit extends Cubit<UserState> {
|
||||
emit(UserError('Ошибка загрузки пользователя: ${e.toString()}'));
|
||||
}
|
||||
}
|
||||
|
||||
Future<User> _createDefaultUser() async {
|
||||
final user = User(
|
||||
name: 'Пользователь по умолчанию',
|
||||
email: 'default@example.com',
|
||||
);
|
||||
await _userRepository.add(user);
|
||||
|
||||
// Создаем начальные данные для нового пользователя
|
||||
await _createInitialData(user.id);
|
||||
|
||||
await _setCurrentUser(user);
|
||||
return user;
|
||||
}
|
||||
|
||||
Future<void> _createInitialData(String userId) async {
|
||||
Future<void> createInitialData(String userId) async {
|
||||
// Проверяем, что репозитории были установлены, прежде чем их использовать.
|
||||
if (_categoryRepository == null ||
|
||||
_tagRepository == null ||
|
||||
@@ -115,15 +100,15 @@ class UserCubit extends Cubit<UserState> {
|
||||
emit(UserLoading(progress: 0.7, message: 'Создание категорий...'));
|
||||
// Используем '!', так как мы уже проверили на null.
|
||||
await _categoryRepository!.addAll(
|
||||
CategoryUtils.getDefaultCategories(userId),
|
||||
CategoryUtils.getDefaultCategories(),
|
||||
);
|
||||
|
||||
emit(UserLoading(progress: 0.8, message: 'Создание тегов...'));
|
||||
await _tagRepository!.addAll(TagUtils.getDefaultTags(userId));
|
||||
await _tagRepository!.addAll(TagUtils.getDefaultTags());
|
||||
|
||||
emit(UserLoading(progress: 0.9, message: 'Создание транзакций...'));
|
||||
await _transactionRepository!.addAll(
|
||||
TransactionUtils.getSampleTransactions(userId),
|
||||
TransactionUtils.getSampleTransactions(),
|
||||
);
|
||||
} catch (e, stack) {
|
||||
|
||||
@@ -176,7 +161,7 @@ class UserCubit extends Cubit<UserState> {
|
||||
await _userRepository.add(user);
|
||||
|
||||
// Создаем начальные данные для нового пользователя
|
||||
await _createInitialData(user.id);
|
||||
await createInitialData(user.id);
|
||||
|
||||
await _setCurrentUser(user);
|
||||
} catch (e, stack) {
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:hive_ce/hive.dart';
|
||||
import '../utils/id_generator.dart';
|
||||
|
||||
part 'app_settings.g.dart';
|
||||
|
||||
@HiveType(typeId: 1005)
|
||||
class AppSettings extends Equatable {
|
||||
/// Ссылка на ID пользователя, которому принадлежат настройки
|
||||
@HiveField(0)
|
||||
final String userId;
|
||||
final String id;
|
||||
|
||||
/// Код языка интерфейса (например, 'ru', 'en')
|
||||
@HiveField(1)
|
||||
@@ -26,17 +26,17 @@ class AppSettings extends Equatable {
|
||||
final DateTime updatedAt;
|
||||
|
||||
AppSettings({
|
||||
required this.userId,
|
||||
String? id,
|
||||
this.languageCode = 'ru',
|
||||
this.isDarkMode = false,
|
||||
this.defaultCurrency = 'RUB',
|
||||
DateTime? updatedAt,
|
||||
}) : updatedAt = updatedAt ?? DateTime.now();
|
||||
}) : id = id ?? IdGenerator.generateId(),
|
||||
updatedAt = updatedAt ?? DateTime.now();
|
||||
|
||||
/// Преобразование в Map для сохранения
|
||||
Map<String, dynamic> toMap() {
|
||||
return {
|
||||
'userId': userId,
|
||||
'languageCode': languageCode,
|
||||
'isDarkMode': isDarkMode,
|
||||
'defaultCurrency': defaultCurrency,
|
||||
@@ -47,7 +47,6 @@ class AppSettings extends Equatable {
|
||||
/// Создание из Map
|
||||
factory AppSettings.fromMap(Map<String, dynamic> map) {
|
||||
return AppSettings(
|
||||
userId: map['userId'],
|
||||
languageCode: map['languageCode'] ?? 'ru',
|
||||
isDarkMode: map['isDarkMode'] ?? false,
|
||||
defaultCurrency: map['defaultCurrency'] ?? 'RUB',
|
||||
@@ -63,7 +62,6 @@ class AppSettings extends Equatable {
|
||||
String? defaultCurrency,
|
||||
}) {
|
||||
return AppSettings(
|
||||
userId: userId ?? this.userId,
|
||||
languageCode: languageCode ?? this.languageCode,
|
||||
isDarkMode: isDarkMode ?? this.isDarkMode,
|
||||
defaultCurrency: defaultCurrency ?? this.defaultCurrency,
|
||||
@@ -72,17 +70,11 @@ class AppSettings extends Equatable {
|
||||
}
|
||||
|
||||
@override
|
||||
List<Object> get props => [
|
||||
userId,
|
||||
languageCode,
|
||||
isDarkMode,
|
||||
defaultCurrency,
|
||||
updatedAt,
|
||||
];
|
||||
List<Object> get props => [id];
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'AppSettings(userId: $userId, languageCode: $languageCode, '
|
||||
return 'AppSettings(languageCode: $languageCode, '
|
||||
'isDarkMode: $isDarkMode, defaultCurrency: $defaultCurrency)';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,6 @@ class AppSettingsAdapter extends TypeAdapter<AppSettings> {
|
||||
for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
|
||||
};
|
||||
return AppSettings(
|
||||
userId: fields[0] as String,
|
||||
languageCode: fields[1] == null ? 'ru' : fields[1] as String,
|
||||
isDarkMode: fields[2] == null ? false : fields[2] as bool,
|
||||
defaultCurrency: fields[3] == null ? 'RUB' : fields[3] as String,
|
||||
@@ -28,9 +27,7 @@ class AppSettingsAdapter extends TypeAdapter<AppSettings> {
|
||||
@override
|
||||
void write(BinaryWriter writer, AppSettings obj) {
|
||||
writer
|
||||
..writeByte(5)
|
||||
..writeByte(0)
|
||||
..write(obj.userId)
|
||||
..writeByte(4)
|
||||
..writeByte(1)
|
||||
..write(obj.languageCode)
|
||||
..writeByte(2)
|
||||
|
||||
@@ -32,11 +32,6 @@ class Category extends Equatable {
|
||||
/// false - расход (например покупки)
|
||||
final bool isIncome;
|
||||
|
||||
@HiveField(5)
|
||||
/// Идентификатор пользователя, которому принадлежит эта категория
|
||||
/// Это позволяет разделять категории между разными пользователями
|
||||
final String userId;
|
||||
|
||||
@HiveField(6)
|
||||
/// Дата и время последнего обновления объекта
|
||||
final DateTime updatedAt;
|
||||
@@ -48,7 +43,6 @@ class Category extends Equatable {
|
||||
required this.color,
|
||||
required this.icon,
|
||||
required this.isIncome,
|
||||
required this.userId, // Теперь userId обязательный параметр
|
||||
DateTime? updatedAt, // Добавлено поле updatedAt, теперь необязательное
|
||||
}) : id = id ?? IdGenerator.generateId(),
|
||||
updatedAt = updatedAt ?? DateTime.now(); // Устанавливаем текущее время по умолчанию
|
||||
@@ -61,7 +55,6 @@ class Category extends Equatable {
|
||||
'color': color.toARGB32(), // Сохраняем только значение цвета
|
||||
'icon': icon.codePoint,
|
||||
'isIncome': isIncome,
|
||||
'userId': userId, // Добавляем userId в Map
|
||||
'updatedAt': updatedAt.toIso8601String(), // Добавлено updatedAt в Map
|
||||
};
|
||||
}
|
||||
@@ -74,7 +67,6 @@ class Category extends Equatable {
|
||||
color: Color(map['color']),
|
||||
icon: IconData(map['icon'], fontFamily: 'MaterialIcons'),
|
||||
isIncome: map['isIncome'],
|
||||
userId: map['userId'], // Добавляем userId при создании из Map
|
||||
updatedAt: DateTime.parse(map['updatedAt']), // Добавлено updatedAt при создании из Map
|
||||
);
|
||||
}
|
||||
@@ -94,7 +86,6 @@ class Category extends Equatable {
|
||||
color: color ?? this.color,
|
||||
icon: icon ?? this.icon,
|
||||
isIncome: isIncome ?? this.isIncome,
|
||||
userId: userId ?? this.userId,
|
||||
updatedAt: DateTime.now(), // Обновляем updatedAt при каждом копировании
|
||||
);
|
||||
}
|
||||
|
||||
@@ -22,7 +22,6 @@ class CategoryAdapter extends TypeAdapter<Category> {
|
||||
color: fields[2] as Color,
|
||||
icon: fields[3] as IconData,
|
||||
isIncome: fields[4] as bool,
|
||||
userId: fields[5] as String,
|
||||
updatedAt: fields[6] as DateTime?,
|
||||
);
|
||||
}
|
||||
@@ -30,7 +29,7 @@ class CategoryAdapter extends TypeAdapter<Category> {
|
||||
@override
|
||||
void write(BinaryWriter writer, Category obj) {
|
||||
writer
|
||||
..writeByte(7)
|
||||
..writeByte(6)
|
||||
..writeByte(0)
|
||||
..write(obj.id)
|
||||
..writeByte(1)
|
||||
@@ -41,8 +40,6 @@ class CategoryAdapter extends TypeAdapter<Category> {
|
||||
..write(obj.icon)
|
||||
..writeByte(4)
|
||||
..write(obj.isIncome)
|
||||
..writeByte(5)
|
||||
..write(obj.userId)
|
||||
..writeByte(6)
|
||||
..write(obj.updatedAt);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
|
||||
import 'package:hive_ce/hive.dart';
|
||||
|
||||
import '/utils/id_generator.dart';
|
||||
part 'sms_handler_settings.g.dart';
|
||||
|
||||
/// Перечисление для определения типа обработки СМС.
|
||||
@@ -31,7 +32,11 @@ class SmsProcessingRule extends HiveObject {
|
||||
@HiveField(2)
|
||||
final String? customFunctionId;
|
||||
|
||||
@HiveField(3)
|
||||
final String id;
|
||||
|
||||
SmsProcessingRule({
|
||||
String? id,
|
||||
required this.type,
|
||||
this.pattern,
|
||||
this.customFunctionId,
|
||||
@@ -39,23 +44,22 @@ class SmsProcessingRule extends HiveObject {
|
||||
(type == SmsProcessingType.regexp && pattern != null) ||
|
||||
(type == SmsProcessingType.customFunction && customFunctionId != null),
|
||||
'Pattern must be provided for regexp type, and customFunctionId for customFunction type.',
|
||||
);
|
||||
), id = id ?? IdGenerator.generateId();
|
||||
}
|
||||
|
||||
/// Модель для хранения всех настроек обработки СМС для одного пользователя.
|
||||
@HiveType(typeId: 12)
|
||||
class SmsHandlerSettings extends HiveObject {
|
||||
/// Уникальный идентификатор пользователя, к которому относятся эти настройки.
|
||||
@HiveField(0)
|
||||
final String userId;
|
||||
|
||||
@HiveField(0)
|
||||
final String id;
|
||||
/// Карта правил обработки, где ключ - это идентификатор отправителя (например, 'SBERBANK' или номер телефона),
|
||||
/// а значение - правило обработки для этого отправителя.
|
||||
@HiveField(1)
|
||||
final Map<String, SmsProcessingRule> rulesBySender;
|
||||
|
||||
SmsHandlerSettings({
|
||||
required this.userId,
|
||||
String? id,
|
||||
required this.rulesBySender,
|
||||
});
|
||||
}) : id = id ?? IdGenerator.generateId();
|
||||
}
|
||||
|
||||
@@ -57,7 +57,6 @@ class SmsHandlerSettingsAdapter extends TypeAdapter<SmsHandlerSettings> {
|
||||
for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
|
||||
};
|
||||
return SmsHandlerSettings(
|
||||
userId: fields[0] as String,
|
||||
rulesBySender: (fields[1] as Map).cast<String, SmsProcessingRule>(),
|
||||
);
|
||||
}
|
||||
@@ -65,9 +64,7 @@ class SmsHandlerSettingsAdapter extends TypeAdapter<SmsHandlerSettings> {
|
||||
@override
|
||||
void write(BinaryWriter writer, SmsHandlerSettings obj) {
|
||||
writer
|
||||
..writeByte(2)
|
||||
..writeByte(0)
|
||||
..write(obj.userId)
|
||||
..writeByte(1)
|
||||
..writeByte(1)
|
||||
..write(obj.rulesBySender);
|
||||
}
|
||||
|
||||
@@ -21,17 +21,12 @@ class SmsMessage extends HiveObject {
|
||||
@HiveField(4)
|
||||
String? transactionId;
|
||||
|
||||
// Комментарий: Добавлено поле для связи с пользователем.
|
||||
@HiveField(5)
|
||||
final String userId;
|
||||
|
||||
SmsMessage({
|
||||
String? id,
|
||||
this.body,
|
||||
this.sender,
|
||||
this.date,
|
||||
this.transactionId,
|
||||
required this.userId,
|
||||
}) : id = id ?? IdGenerator.generateId();
|
||||
|
||||
// Комментарий: Добавляем метод для обновления transactionId
|
||||
@@ -42,7 +37,6 @@ class SmsMessage extends HiveObject {
|
||||
sender: sender,
|
||||
date: date,
|
||||
transactionId: transactionId ?? this.transactionId,
|
||||
userId: userId ?? this.userId,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,14 +22,13 @@ class SmsMessageAdapter extends TypeAdapter<SmsMessage> {
|
||||
sender: fields[2] as String?,
|
||||
date: fields[3] as DateTime?,
|
||||
transactionId: fields[4] as String?,
|
||||
userId: fields[5] as String,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void write(BinaryWriter writer, SmsMessage obj) {
|
||||
writer
|
||||
..writeByte(6)
|
||||
..writeByte(5)
|
||||
..writeByte(0)
|
||||
..write(obj.id)
|
||||
..writeByte(1)
|
||||
@@ -39,9 +38,7 @@ class SmsMessageAdapter extends TypeAdapter<SmsMessage> {
|
||||
..writeByte(3)
|
||||
..write(obj.date)
|
||||
..writeByte(4)
|
||||
..write(obj.transactionId)
|
||||
..writeByte(5)
|
||||
..write(obj.userId);
|
||||
..write(obj.transactionId);
|
||||
}
|
||||
|
||||
@override
|
||||
|
||||
@@ -14,12 +14,6 @@ class Tag extends Equatable {
|
||||
/// Название тега (например: "Важное", "Работа")
|
||||
final String name;
|
||||
|
||||
// Комментарий: Добавляем новое поле для хранения идентификатора пользователя.
|
||||
// Это позволит нам связать каждый тег с конкретным пользователем.
|
||||
// Мы используем аннотацию @HiveField(2), чтобы указать Hive, как сохранять это поле.
|
||||
@HiveField(2)
|
||||
final String userId;
|
||||
|
||||
@HiveField(3)
|
||||
/// Дата и время последнего обновления объекта
|
||||
final DateTime updatedAt;
|
||||
@@ -30,7 +24,6 @@ class Tag extends Equatable {
|
||||
required this.name,
|
||||
// Комментарий: Добавляем userId в конструктор как обязательный параметр.
|
||||
// Теперь при создании тега необходимо будет указать, какому пользователю он принадлежит.
|
||||
required this.userId,
|
||||
DateTime? updatedAt, // Добавлено поле updatedAt, теперь необязательное
|
||||
}) : id = id ?? IdGenerator.generateId(),
|
||||
updatedAt = updatedAt ?? DateTime.now(); // Устанавливаем текущее время по умолчанию
|
||||
@@ -42,7 +35,6 @@ class Tag extends Equatable {
|
||||
'name': name,
|
||||
// Комментарий: Добавляем userId в Map. Это нужно для сохранения
|
||||
// данных в форматах, которые не работают напрямую с объектами Dart (например, при отправке на сервер).
|
||||
'userId': userId,
|
||||
'updatedAt': updatedAt.toIso8601String(), // Добавлено updatedAt в Map
|
||||
};
|
||||
}
|
||||
@@ -54,7 +46,6 @@ class Tag extends Equatable {
|
||||
name: map['name'],
|
||||
// Комментарий: Извлекаем userId из Map при создании объекта.
|
||||
// Это позволит восстановить полный объект Tag из данных, например, из базы данных.
|
||||
userId: map['userId'],
|
||||
updatedAt: DateTime.parse(map['updatedAt']), // Добавлено updatedAt при создании из Map
|
||||
);
|
||||
}
|
||||
@@ -68,7 +59,6 @@ class Tag extends Equatable {
|
||||
return Tag(
|
||||
id: id ?? this.id,
|
||||
name: name ?? this.name,
|
||||
userId: userId ?? this.userId,
|
||||
updatedAt: DateTime.now(), // Обновляем updatedAt при каждом копировании
|
||||
);
|
||||
}
|
||||
|
||||
@@ -19,7 +19,6 @@ class TagAdapter extends TypeAdapter<Tag> {
|
||||
return Tag(
|
||||
id: fields[0] as String?,
|
||||
name: fields[1] as String,
|
||||
userId: fields[2] as String,
|
||||
updatedAt: fields[3] as DateTime?,
|
||||
);
|
||||
}
|
||||
@@ -27,13 +26,11 @@ class TagAdapter extends TypeAdapter<Tag> {
|
||||
@override
|
||||
void write(BinaryWriter writer, Tag obj) {
|
||||
writer
|
||||
..writeByte(4)
|
||||
..writeByte(3)
|
||||
..writeByte(0)
|
||||
..write(obj.id)
|
||||
..writeByte(1)
|
||||
..write(obj.name)
|
||||
..writeByte(2)
|
||||
..write(obj.userId)
|
||||
..writeByte(3)
|
||||
..write(obj.updatedAt);
|
||||
}
|
||||
|
||||
@@ -39,10 +39,6 @@ class TransactionRecord extends Equatable {
|
||||
/// Валюта операции (код валюты, например "RUB", "USD")
|
||||
final String currency;
|
||||
|
||||
// Добавляем новое поле для хранения идентификатора пользователя
|
||||
@HiveField(7) // Используем следующий доступный номер поля Hive
|
||||
final String userId;
|
||||
|
||||
@HiveField(8)
|
||||
/// Дата и время последнего обновления объекта
|
||||
final DateTime updatedAt;
|
||||
@@ -56,7 +52,6 @@ class TransactionRecord extends Equatable {
|
||||
required this.dateTime,
|
||||
required this.vendor,
|
||||
required this.currency,
|
||||
required this.userId, // Добавляем userId в конструктор
|
||||
DateTime? updatedAt, // Добавлено поле updatedAt, теперь необязательное
|
||||
}) : id = id ?? IdGenerator.generateId(),
|
||||
updatedAt =
|
||||
@@ -73,7 +68,6 @@ class TransactionRecord extends Equatable {
|
||||
'dateTime': dateTime.toIso8601String(),
|
||||
'vendor': vendor,
|
||||
'currency': currency,
|
||||
'userId': userId, // Добавляем userId в Map
|
||||
'updatedAt': updatedAt.toIso8601String(), // Добавлено updatedAt в Map
|
||||
};
|
||||
}
|
||||
@@ -88,7 +82,6 @@ class TransactionRecord extends Equatable {
|
||||
dateTime: DateTime.parse(map['dateTime']),
|
||||
vendor: map['vendor'],
|
||||
currency: map['currency'],
|
||||
userId: map['userId'], // Извлекаем userId из Map
|
||||
updatedAt: DateTime.parse(
|
||||
map['updatedAt'],
|
||||
), // Добавлено updatedAt при создании из Map
|
||||
@@ -118,7 +111,6 @@ class TransactionRecord extends Equatable {
|
||||
dateTime: dateTime ?? this.dateTime,
|
||||
vendor: vendor ?? this.vendor,
|
||||
currency: currency ?? this.currency,
|
||||
userId: userId ?? this.userId,
|
||||
updatedAt: DateTime.now(), // Обновляем updatedAt при каждом копировании
|
||||
);
|
||||
}
|
||||
|
||||
@@ -24,7 +24,6 @@ class TransactionRecordAdapter extends TypeAdapter<TransactionRecord> {
|
||||
dateTime: fields[4] as DateTime,
|
||||
vendor: fields[5] as String,
|
||||
currency: fields[6] as String,
|
||||
userId: fields[7] as String,
|
||||
updatedAt: fields[8] as DateTime?,
|
||||
);
|
||||
}
|
||||
@@ -32,7 +31,7 @@ class TransactionRecordAdapter extends TypeAdapter<TransactionRecord> {
|
||||
@override
|
||||
void write(BinaryWriter writer, TransactionRecord obj) {
|
||||
writer
|
||||
..writeByte(9)
|
||||
..writeByte(8)
|
||||
..writeByte(0)
|
||||
..write(obj.id)
|
||||
..writeByte(1)
|
||||
@@ -47,8 +46,6 @@ class TransactionRecordAdapter extends TypeAdapter<TransactionRecord> {
|
||||
..write(obj.vendor)
|
||||
..writeByte(6)
|
||||
..write(obj.currency)
|
||||
..writeByte(7)
|
||||
..write(obj.userId)
|
||||
..writeByte(8)
|
||||
..write(obj.updatedAt);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import 'package:budget_app/l10n/app_localizations.dart';
|
||||
import 'package:budget_app/logic/category/category_cubit.dart';
|
||||
import 'package:budget_app/logic/category/category_state.dart';
|
||||
import 'package:budget_app/models/category.dart';
|
||||
import 'package:budget_app/pages/category/category_edit_page.dart';
|
||||
import 'package:budget_app/pages/category/widgets/add_category_button.dart';
|
||||
@@ -63,16 +62,11 @@ class _CategoryListPageState extends State<CategoryListPage> {
|
||||
MaterialPageRoute(
|
||||
builder: (_) => CategoryEditPage(
|
||||
onSave: (name, color, icon, isIncome) {
|
||||
final userState = context.read<UserCubit>().state;
|
||||
if (userState is! UserLoaded || userState.user == null) return;
|
||||
|
||||
final currentUserId = userState.user!.id;
|
||||
final newCategory = Category(
|
||||
name: name,
|
||||
color: color,
|
||||
icon: icon,
|
||||
isIncome: isIncome,
|
||||
userId: currentUserId,
|
||||
);
|
||||
|
||||
context.read<CategoryCubit>().addCategory(newCategory);
|
||||
@@ -89,10 +83,6 @@ class _CategoryListPageState extends State<CategoryListPage> {
|
||||
builder: (_) => CategoryEditPage(
|
||||
category: category,
|
||||
onSave: (name, color, icon, isIncome) {
|
||||
final userState = context.read<UserCubit>().state;
|
||||
if (userState is! UserLoaded || userState.user == null) return;
|
||||
|
||||
final currentUserId = userState.user!.id;
|
||||
final updatedCategory = category.copyWith(
|
||||
name: name,
|
||||
color: color,
|
||||
@@ -108,10 +98,6 @@ class _CategoryListPageState extends State<CategoryListPage> {
|
||||
}
|
||||
|
||||
void _deleteCategory(BuildContext context, Category category) {
|
||||
final userState = context.read<UserCubit>().state;
|
||||
if (userState is! UserLoaded || userState.user == null) return;
|
||||
|
||||
final currentUserId = userState.user!.id;
|
||||
context.read<CategoryCubit>().deleteCategory(category.id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -122,17 +122,14 @@ class _AddTransactionDialogState extends State<AddTransactionDialog> {
|
||||
: 'RUB';
|
||||
|
||||
// Получаем ID текущего пользователя из UserCubit
|
||||
final userState = context.read<UserCubit>().state;
|
||||
if (userState is! UserLoaded || userState.user == null) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(
|
||||
AppLocalizations.of(context)!.transactionErrorText('User not found'),
|
||||
),
|
||||
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(
|
||||
AppLocalizations.of(context)!.transactionErrorText('User not found'),
|
||||
),
|
||||
);
|
||||
return;
|
||||
}
|
||||
),
|
||||
);
|
||||
|
||||
final newTransaction = TransactionRecord(
|
||||
amount: amount,
|
||||
@@ -141,7 +138,6 @@ class _AddTransactionDialogState extends State<AddTransactionDialog> {
|
||||
dateTime: _selectedDateTime,
|
||||
tag: _selectedTag,
|
||||
currency: currency,
|
||||
userId: userState.user!.id,
|
||||
);
|
||||
|
||||
context.read<TransactionBloc>().add(
|
||||
@@ -154,14 +150,9 @@ class _AddTransactionDialogState extends State<AddTransactionDialog> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final localizations = AppLocalizations.of(context)!;
|
||||
|
||||
|
||||
// Получаем ID текущего пользователя из UserCubit
|
||||
final userState = context.read<UserCubit>().state;
|
||||
final userId = userState is UserLoaded && userState.user != null
|
||||
? userState.user!.id
|
||||
: '';
|
||||
|
||||
final categories = CategoryUtils.getDefaultCategories(userId)
|
||||
final categories = CategoryUtils.getDefaultCategories()
|
||||
.where((c) => c.isIncome == _isIncome).toList();
|
||||
|
||||
return AlertDialog(
|
||||
@@ -233,7 +224,7 @@ class _AddTransactionDialogState extends State<AddTransactionDialog> {
|
||||
// Комментарий: Добавляем выпадающий список для выбора тега.
|
||||
// Он будет загружать теги асинхронно для текущего пользователя.
|
||||
FutureBuilder<List<Tag>>(
|
||||
future: GetIt.instance<ITagRepository>().getAllByUser(userId),
|
||||
future: GetIt.instance<ITagRepository>().getAll(),
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.connectionState == ConnectionState.waiting) {
|
||||
return const Center(child: CircularProgressIndicator());
|
||||
|
||||
+116
-130
@@ -37,145 +37,131 @@ class SettingsPage extends StatelessWidget {
|
||||
builder: (context, userState) {
|
||||
if (userState is UserLoaded && userState.user != null) {
|
||||
// Комментарий: Как только пользователь загружен, мы загружаем его настройки.
|
||||
context.read<SettingsCubit>().loadSettings(userState.user!.id);
|
||||
context.read<SettingsCubit>().loadSettings();
|
||||
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);
|
||||
}
|
||||
}
|
||||
// Комментарий: Обернули Column в SingleChildScrollView, чтобы избежать переполнения по вертикали.
|
||||
// Это позволяет прокручивать содержимое, если оно не помещается на экране.
|
||||
child: SingleChildScrollView(
|
||||
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
|
||||
context
|
||||
.read<SettingsCubit>()
|
||||
.toggleDarkMode(value);
|
||||
},
|
||||
// Комментарий: Формируем список доступных языков.
|
||||
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);
|
||||
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) {
|
||||
context
|
||||
.read<SettingsCubit>()
|
||||
.changeLanguage(newValue);
|
||||
}
|
||||
}
|
||||
},
|
||||
// Комментарий: Формируем список доступных валют. Можно расширить этот список.
|
||||
items: <String>['RUB', 'USD', 'EUR']
|
||||
.map<DropdownMenuItem<String>>(
|
||||
(String value) {
|
||||
return DropdownMenuItem<String>(
|
||||
value: value,
|
||||
child: Text(value),
|
||||
);
|
||||
}).toList(),
|
||||
},
|
||||
// Комментарий: Формируем список доступных языков.
|
||||
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 для перехода на страницу редактирования категорий.
|
||||
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.currencySetting),
|
||||
subtitle: Text(localizations.currencyDescription),
|
||||
trailing: DropdownButton<String>(
|
||||
value: state.defaultCurrency,
|
||||
// Комментарий: При выборе новой валюты вызываем метод `changeCurrency` у Cubit.
|
||||
// Также передаем userId из UserService.
|
||||
onChanged: (String? newValue) {
|
||||
if (newValue != null) {
|
||||
|
||||
context
|
||||
.read<SettingsCubit>()
|
||||
.changeCurrency(newValue);
|
||||
}
|
||||
},
|
||||
// Комментарий: Формируем список доступных валют. Можно расширить этот список.
|
||||
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(),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
} else {
|
||||
|
||||
@@ -69,14 +69,8 @@ class _TagListPageState extends State<TagListPage> {
|
||||
MaterialPageRoute(
|
||||
builder: (_) => TagEditPage(
|
||||
onSave: (name) {
|
||||
final userState = context.read<UserCubit>().state;
|
||||
if (userState is! UserLoaded || userState.user == null) {
|
||||
return;
|
||||
}
|
||||
final currentUserId = userState.user!.id;
|
||||
final newTag = Tag(
|
||||
name: name,
|
||||
userId: currentUserId,
|
||||
);
|
||||
context.read<TagCubit>().addTag(newTag);
|
||||
_listKey.currentState?.insertItem(0);
|
||||
|
||||
@@ -23,7 +23,7 @@ class SmsService {
|
||||
///
|
||||
/// Возвращает список объектов [SmsMessage].
|
||||
/// В случае ошибки или отсутствия разрешений, возвращает пустой список.
|
||||
Future<List<SmsMessage>> getLastSmsMessages(int count, String userId) async {
|
||||
Future<List<SmsMessage>> getLastSmsMessages(int count) async {
|
||||
final bool? permissionsGranted =
|
||||
await _telephony.requestPhoneAndSmsPermissions;
|
||||
if (permissionsGranted ?? false) {
|
||||
@@ -36,14 +36,13 @@ class SmsService {
|
||||
body: msg.body,
|
||||
sender: msg.address,
|
||||
date: DateTime.fromMillisecondsSinceEpoch(msg.date ?? 0),
|
||||
userId: userId,
|
||||
)).toList();
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
// Комментарий: Метод для получения SMS-сообщений с определенной даты.
|
||||
Future<List<SmsMessage>> getSmsMessagesSince(DateTime sinceDate, String userId) async {
|
||||
Future<List<SmsMessage>> getSmsMessagesSince(DateTime sinceDate) async {
|
||||
final bool? permissionsGranted = await _telephony.requestPhoneAndSmsPermissions;
|
||||
if (permissionsGranted ?? false) {
|
||||
final List<telephony_package.SmsMessage> messages = await _telephony.getInboxSms(
|
||||
@@ -57,7 +56,6 @@ class SmsService {
|
||||
body: msg.body,
|
||||
sender: msg.address,
|
||||
date: DateTime.fromMillisecondsSinceEpoch(msg.date ?? 0),
|
||||
userId: userId,
|
||||
))
|
||||
.toList();
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ class CategoryUtils {
|
||||
/// Возвращает список предопределенных категорий для конкретного пользователя
|
||||
/// Включает как категории доходов, так и расходов
|
||||
/// [userId] - идентификатор пользователя, для которого создаются категории
|
||||
static List<Category> getDefaultCategories(String userId) {
|
||||
static List<Category> getDefaultCategories() {
|
||||
return [
|
||||
// Категории доходов
|
||||
Category(
|
||||
@@ -16,7 +16,6 @@ class CategoryUtils {
|
||||
color: Colors.green,
|
||||
icon: Icons.attach_money,
|
||||
isIncome: true,
|
||||
userId: userId, // Привязываем к конкретному пользователю
|
||||
),
|
||||
Category(
|
||||
id: 'income_gift',
|
||||
@@ -24,7 +23,6 @@ class CategoryUtils {
|
||||
color: Colors.blue,
|
||||
icon: Icons.card_giftcard,
|
||||
isIncome: true,
|
||||
userId: userId, // Привязываем к конкретному пользователю
|
||||
),
|
||||
Category(
|
||||
id: 'income_freelance',
|
||||
@@ -32,7 +30,6 @@ class CategoryUtils {
|
||||
color: Colors.teal,
|
||||
icon: Icons.computer,
|
||||
isIncome: true,
|
||||
userId: userId, // Привязываем к конкретному пользователю
|
||||
),
|
||||
|
||||
// Категории расходов
|
||||
@@ -42,7 +39,6 @@ class CategoryUtils {
|
||||
color: Colors.red,
|
||||
icon: Icons.fastfood,
|
||||
isIncome: false,
|
||||
userId: userId, // Привязываем к конкретному пользователю
|
||||
),
|
||||
Category(
|
||||
id: 'expense_transport',
|
||||
@@ -50,7 +46,6 @@ class CategoryUtils {
|
||||
color: Colors.orange,
|
||||
icon: Icons.directions_car,
|
||||
isIncome: false,
|
||||
userId: userId, // Привязываем к конкретному пользователю
|
||||
),
|
||||
Category(
|
||||
id: 'expense_entertainment',
|
||||
@@ -58,7 +53,6 @@ class CategoryUtils {
|
||||
color: Colors.purple,
|
||||
icon: Icons.movie,
|
||||
isIncome: false,
|
||||
userId: userId, // Привязываем к конкретному пользователю
|
||||
),
|
||||
Category(
|
||||
id: 'expense_utilities',
|
||||
@@ -66,7 +60,6 @@ class CategoryUtils {
|
||||
color: Colors.blueGrey,
|
||||
icon: Icons.home,
|
||||
isIncome: false,
|
||||
userId: userId, // Привязываем к конкретному пользователю
|
||||
),
|
||||
Category(
|
||||
id: 'expense_shopping',
|
||||
@@ -74,7 +67,6 @@ class CategoryUtils {
|
||||
color: Colors.pink,
|
||||
icon: Icons.shopping_bag,
|
||||
isIncome: false,
|
||||
userId: userId, // Привязываем к конкретному пользователю
|
||||
),
|
||||
];
|
||||
}
|
||||
@@ -82,12 +74,12 @@ class CategoryUtils {
|
||||
/// Возвращает только категории доходов для конкретного пользователя
|
||||
/// [userId] - идентификатор пользователя
|
||||
static List<Category> getIncomeCategories(String userId) {
|
||||
return getDefaultCategories(userId).where((c) => c.isIncome).toList();
|
||||
return getDefaultCategories().where((c) => c.isIncome).toList();
|
||||
}
|
||||
|
||||
/// Возвращает только категории расходов для конкретного пользователя
|
||||
/// [userId] - идентификатор пользователя
|
||||
static List<Category> getExpenseCategories(String userId) {
|
||||
return getDefaultCategories(userId).where((c) => !c.isIncome).toList();
|
||||
return getDefaultCategories().where((c) => !c.isIncome).toList();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,35 +5,28 @@ class TagUtils {
|
||||
// Комментарий: Мы изменили сигнатуру метода, добавив параметр `userId`.
|
||||
// Это необходимо, потому что конструктор `Tag` теперь требует `userId`.
|
||||
/// Возвращает список предопределенных тегов для конкретного пользователя
|
||||
static List<Tag> getDefaultTags(String userId) {
|
||||
static List<Tag> getDefaultTags() {
|
||||
return [
|
||||
Tag(
|
||||
id: 'tag_important',
|
||||
name: 'Важное',
|
||||
// Комментарий: Передаем `userId` при создании каждого тега по умолчанию.
|
||||
// Таким образом, эти теги будут принадлежать конкретному пользователю.
|
||||
userId: userId,
|
||||
),
|
||||
Tag(
|
||||
id: 'tag_work',
|
||||
|
||||
name: 'Работа',
|
||||
userId: userId,
|
||||
),
|
||||
Tag(
|
||||
id: 'tag_family',
|
||||
name: 'Семья',
|
||||
userId: userId,
|
||||
),
|
||||
Tag(
|
||||
id: 'tag_friends',
|
||||
name: 'Друзья',
|
||||
userId: userId,
|
||||
),
|
||||
Tag(
|
||||
id: 'tag_holiday',
|
||||
name: 'Отпуск',
|
||||
userId: userId,
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
@@ -5,11 +5,10 @@ import 'tag_utils.dart';
|
||||
/// Утилиты для работы с тестовыми транзакциями
|
||||
class TransactionUtils {
|
||||
/// Возвращает список тестовых транзакций для конкретного пользователя
|
||||
/// [userId] - идентификатор пользователя, для которого создаются транзакции
|
||||
static List<TransactionRecord> getSampleTransactions(String userId) {
|
||||
static List<TransactionRecord> getSampleTransactions() {
|
||||
// Получаем категории и теги для этого пользователя
|
||||
final categories = CategoryUtils.getDefaultCategories(userId);
|
||||
final tags = TagUtils.getDefaultTags(userId);
|
||||
final categories = CategoryUtils.getDefaultCategories();
|
||||
final tags = TagUtils.getDefaultTags();
|
||||
|
||||
return [
|
||||
// Доходы
|
||||
@@ -20,7 +19,6 @@ class TransactionUtils {
|
||||
dateTime: DateTime.now().subtract(const Duration(days: 5)),
|
||||
vendor: 'ООО "Рога и копыта"',
|
||||
currency: 'RUB',
|
||||
userId: userId, // Указываем userId для тестовой транзакции
|
||||
),
|
||||
TransactionRecord(
|
||||
category: categories.firstWhere((c) => c.id == 'income_freelance'),
|
||||
@@ -29,7 +27,6 @@ class TransactionUtils {
|
||||
dateTime: DateTime.now().subtract(const Duration(days: 2)),
|
||||
vendor: 'Фриланс проект',
|
||||
currency: 'RUB',
|
||||
userId: userId, // Указываем userId для тестовой транзакции
|
||||
),
|
||||
|
||||
// Расходы
|
||||
@@ -40,7 +37,6 @@ class TransactionUtils {
|
||||
dateTime: DateTime.now().subtract(const Duration(days: 1)),
|
||||
vendor: 'Пятерочка',
|
||||
currency: 'RUB',
|
||||
userId: userId, // Указываем userId для тестовой транзакции
|
||||
),
|
||||
TransactionRecord(
|
||||
category: categories.firstWhere((c) => c.id == 'expense_transport'),
|
||||
@@ -49,7 +45,6 @@ class TransactionUtils {
|
||||
dateTime: DateTime.now().subtract(const Duration(hours: 12)),
|
||||
vendor: 'Яндекс Такси',
|
||||
currency: 'RUB',
|
||||
userId: userId, // Указываем userId для тестовой транзакции
|
||||
),
|
||||
TransactionRecord(
|
||||
category: categories.firstWhere((c) => c.id == 'expense_entertainment'),
|
||||
@@ -58,7 +53,6 @@ class TransactionUtils {
|
||||
dateTime: DateTime.now().subtract(const Duration(hours: 6)),
|
||||
vendor: 'Кинотеатр',
|
||||
currency: 'RUB',
|
||||
userId: userId, // Указываем userId для тестовой транзакции
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user