diff --git a/lib/pages/transactions/create_transaction/widgets/category_selector.dart b/lib/pages/transactions/create_transaction/widgets/category_selector.dart index 69fcb8f4..00f928e7 100644 --- a/lib/pages/transactions/create_transaction/widgets/category_selector.dart +++ b/lib/pages/transactions/create_transaction/widgets/category_selector.dart @@ -30,6 +30,9 @@ class _CategorySelectorState extends ConsumerState { final categoriesList = ref.watch( categoriesByTypeProvider(transactionType.categoryType), ); + final frequentCategories = ref.watch( + frequentCategoriesProvider(transactionType.categoryType), + ); final selectedCategory = ref.watch(selectedCategoryProvider); return Container( @@ -71,7 +74,7 @@ class _CategorySelectorState extends ConsumerState { color: Theme.of(context).colorScheme.surface, height: 74, width: double.infinity, - child: categoriesList.when( + child: frequentCategories.when( data: (categories) => ListView.builder( itemCount: categories.length, // to prevent range error scrollDirection: Axis.horizontal, diff --git a/lib/providers/categories_provider.dart b/lib/providers/categories_provider.dart index da456b6b..38faea37 100644 --- a/lib/providers/categories_provider.dart +++ b/lib/providers/categories_provider.dart @@ -208,6 +208,20 @@ Future> subcategories(Ref ref, int categoryId) async { return categories; } +@riverpod +Future> frequentCategories( + Ref ref, + CategoryTransactionType? type, +) async { + List categories = []; + if (type != null) { + categories = await ref + .read(categoryRepositoryProvider) + .selectFrequentCategories(type); + } + return categories; +} + @Riverpod(keepAlive: true) Future> categoryMap(Ref ref) async { final categoryType = ref.watch(categoryTypeProvider); diff --git a/lib/services/database/repositories/category_repository.dart b/lib/services/database/repositories/category_repository.dart index a427be2e..c5654349 100644 --- a/lib/services/database/repositories/category_repository.dart +++ b/lib/services/database/repositories/category_repository.dart @@ -1,6 +1,7 @@ import 'package:riverpod_annotation/riverpod_annotation.dart'; import '../../../model/category_transaction.dart'; +import '../../../model/transaction.dart'; import '../sossoldi_database.dart'; part 'category_repository.g.dart'; @@ -112,6 +113,33 @@ class CategoryRepository { } } + Future> selectFrequentCategories( + CategoryTransactionType type, + ) async { + final db = await _sossoldiDB.database; + // Select the last 100 transactions, group by category and return the + // top 5 most used categories ordered by usage count desc. + final result = await db.rawQuery( + ''' + SELECT c.* + FROM "$categoryTransactionTable" c + JOIN ( + SELECT * FROM "$transactionTable" + WHERE "${TransactionFields.type}" = ? + ORDER BY "${TransactionFields.date}" DESC + LIMIT 100 + ) t ON t."${TransactionFields.idCategory}" = c."${CategoryTransactionFields.id}" + WHERE c."${CategoryTransactionFields.type}" = ? + GROUP BY c."${CategoryTransactionFields.id}" + ORDER BY COUNT(t."${TransactionFields.id}") DESC + LIMIT 5 + ''', + [type.transactionType.code, type.code], + ); + + return result.map((json) => CategoryTransaction.fromJson(json)).toList(); + } + Future updateItem(CategoryTransaction item) async { final db = await _sossoldiDB.database;