From db74fbe89f67c69c6be240f021abf2a9bad87c5e Mon Sep 17 00:00:00 2001 From: monsieurtanuki Date: Wed, 10 Jan 2024 11:01:41 +0100 Subject: [PATCH] feat: 4947 - added local "last access" timestamp for products New file: * `dao_product_last_access.dart`: Table that stores the local last access timestamp for a product. Impacted files: * `local_database.dart`: new version of the database, because of the new "product last access" table * `new_product_page.dart`: added a call to refresh the product "last access" timestamp * `offline_data_page.dart`: added a call to delete the product "last access" timestamps * `product_model.dart`: added a call to refresh the product "last access" timestamp * `pubspec.lock`: wtf --- .../lib/database/dao_product_last_access.dart | 40 +++++++++ .../lib/database/local_database.dart | 4 +- .../lib/pages/offline_data_page.dart | 4 + .../pages/product/common/product_model.dart | 2 + .../lib/pages/product/new_product_page.dart | 5 +- packages/smooth_app/pubspec.lock | 90 +++++++++---------- 6 files changed, 98 insertions(+), 47 deletions(-) create mode 100644 packages/smooth_app/lib/database/dao_product_last_access.dart diff --git a/packages/smooth_app/lib/database/dao_product_last_access.dart b/packages/smooth_app/lib/database/dao_product_last_access.dart new file mode 100644 index 00000000000..98f38809ba6 --- /dev/null +++ b/packages/smooth_app/lib/database/dao_product_last_access.dart @@ -0,0 +1,40 @@ +import 'dart:async'; +import 'package:smooth_app/database/abstract_sql_dao.dart'; +import 'package:smooth_app/database/local_database.dart'; +import 'package:sqflite/sqflite.dart'; + +/// Table that stores the local last access timestamp for a product. +class DaoProductLastAccess extends AbstractSqlDao { + DaoProductLastAccess(super.localDatabase); + + static const String TABLE = 'product_last_access'; + static const String COLUMN_BARCODE = 'barcode'; + static const String COLUMN_LAST_ACCESS = 'last_access'; + + static FutureOr onUpgrade( + final Database db, + final int oldVersion, + final int newVersion, + ) async { + if (oldVersion < 5) { + await db.execute('create table $TABLE(' + // cf. https://www.sqlite.org/lang_conflict.html + '$COLUMN_BARCODE TEXT PRIMARY KEY on conflict replace' + ',$COLUMN_LAST_ACCESS INT NOT NULL' + ')'); + } + } + + Future put(final String barcode) async => + localDatabase.database.rawInsert( + 'insert into $TABLE($COLUMN_BARCODE, $COLUMN_LAST_ACCESS) ' + 'values(?, ?)', + [ + barcode, + LocalDatabase.nowInMillis(), + ], + ); + + /// Delete all items from the database + Future deleteAll() async => localDatabase.database.delete(TABLE); +} diff --git a/packages/smooth_app/lib/database/local_database.dart b/packages/smooth_app/lib/database/local_database.dart index a1a282929fd..9acdbbdf6e1 100644 --- a/packages/smooth_app/lib/database/local_database.dart +++ b/packages/smooth_app/lib/database/local_database.dart @@ -13,6 +13,7 @@ import 'package:smooth_app/database/dao_hive_product.dart'; import 'package:smooth_app/database/dao_instant_string.dart'; import 'package:smooth_app/database/dao_int.dart'; import 'package:smooth_app/database/dao_product.dart'; +import 'package:smooth_app/database/dao_product_last_access.dart'; import 'package:smooth_app/database/dao_product_list.dart'; import 'package:smooth_app/database/dao_string.dart'; import 'package:smooth_app/database/dao_string_list.dart'; @@ -62,7 +63,7 @@ class LocalDatabase extends ChangeNotifier { final String databasePath = join(databasesRootPath, 'smoothie.db'); final Database database = await openDatabase( databasePath, - version: 4, + version: 5, singleInstance: true, onUpgrade: _onUpgrade, ); @@ -105,5 +106,6 @@ class LocalDatabase extends ChangeNotifier { ) async { await DaoProduct.onUpgrade(db, oldVersion, newVersion); await DaoWorkBarcode.onUpgrade(db, oldVersion, newVersion); + await DaoProductLastAccess.onUpgrade(db, oldVersion, newVersion); } } diff --git a/packages/smooth_app/lib/pages/offline_data_page.dart b/packages/smooth_app/lib/pages/offline_data_page.dart index 8ef915aeded..f90250541f7 100644 --- a/packages/smooth_app/lib/pages/offline_data_page.dart +++ b/packages/smooth_app/lib/pages/offline_data_page.dart @@ -5,6 +5,7 @@ import 'package:provider/provider.dart'; import 'package:smooth_app/background/background_task_full_refresh.dart'; import 'package:smooth_app/background/background_task_offline.dart'; import 'package:smooth_app/database/dao_product.dart'; +import 'package:smooth_app/database/dao_product_last_access.dart'; import 'package:smooth_app/database/local_database.dart'; import 'package:smooth_app/generic_lib/design_constants.dart'; import 'package:smooth_app/generic_lib/duration_constants.dart'; @@ -34,6 +35,8 @@ class _OfflineDataPageState extends State { final double backgroundHeight = MediaQuery.of(context).size.height * .20; final LocalDatabase localDatabase = context.watch(); final DaoProduct daoProduct = DaoProduct(localDatabase); + final DaoProductLastAccess daoProductLastAccess = + DaoProductLastAccess(localDatabase); final AppLocalizations appLocalizations = AppLocalizations.of(context); return SmoothScaffold( appBar: SmoothAppBar( @@ -82,6 +85,7 @@ class _OfflineDataPageState extends State { trailing: const Icon(Icons.delete), onTap: () async { final int totalProductsDeleted = await daoProduct.deleteAll(); + await daoProductLastAccess.deleteAll(); if (mounted) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( diff --git a/packages/smooth_app/lib/pages/product/common/product_model.dart b/packages/smooth_app/lib/pages/product/common/product_model.dart index 9a6369aa65f..4895cdebf3f 100644 --- a/packages/smooth_app/lib/pages/product/common/product_model.dart +++ b/packages/smooth_app/lib/pages/product/common/product_model.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:openfoodfacts/openfoodfacts.dart'; import 'package:smooth_app/data_models/fetched_product.dart'; import 'package:smooth_app/database/dao_product.dart'; +import 'package:smooth_app/database/dao_product_last_access.dart'; import 'package:smooth_app/database/local_database.dart'; import 'package:smooth_app/query/barcode_product_query.dart'; @@ -25,6 +26,7 @@ class ProductModel with ChangeNotifier { /// In the constructor we retrieve async'ly the product from the local db. ProductModel(this.barcode, this.localDatabase) { localDatabase.upToDate.showInterest(barcode); + DaoProductLastAccess(localDatabase).put(barcode); _asyncLoad(); } diff --git a/packages/smooth_app/lib/pages/product/new_product_page.dart b/packages/smooth_app/lib/pages/product/new_product_page.dart index 4d99c38fa12..0e0579360f3 100644 --- a/packages/smooth_app/lib/pages/product/new_product_page.dart +++ b/packages/smooth_app/lib/pages/product/new_product_page.dart @@ -12,6 +12,7 @@ import 'package:smooth_app/data_models/preferences/user_preferences.dart'; import 'package:smooth_app/data_models/product_list.dart'; import 'package:smooth_app/data_models/product_preferences.dart'; import 'package:smooth_app/data_models/up_to_date_mixin.dart'; +import 'package:smooth_app/database/dao_product_last_access.dart'; import 'package:smooth_app/database/dao_product_list.dart'; import 'package:smooth_app/database/local_database.dart'; import 'package:smooth_app/generic_lib/buttons/smooth_large_button_with_icon.dart'; @@ -72,7 +73,9 @@ class _ProductPageState extends State @override void initState() { super.initState(); - initUpToDate(widget.product, context.read()); + final LocalDatabase localDatabase = context.read(); + initUpToDate(widget.product, localDatabase); + DaoProductLastAccess(localDatabase).put(barcode); questionsLayout = getUserQuestionsLayout(context.read()); WidgetsBinding.instance.addPostFrameCallback((_) { _updateLocalDatabaseWithProductHistory(context); diff --git a/packages/smooth_app/pubspec.lock b/packages/smooth_app/pubspec.lock index ab013576cd7..62acf6aa604 100644 --- a/packages/smooth_app/pubspec.lock +++ b/packages/smooth_app/pubspec.lock @@ -65,10 +65,10 @@ packages: dependency: transitive description: name: archive - sha256: "7b875fd4a20b165a3084bd2d210439b22ebc653f21cea4842729c0c30c82596b" + sha256: "22600aa1e926be775fa5fe7e6894e7fb3df9efda8891c73f70fb3262399a432d" url: "https://pub.dev" source: hosted - version: "3.4.9" + version: "3.4.10" args: dependency: transitive description: @@ -161,10 +161,10 @@ packages: dependency: transitive description: name: barcode - sha256: "2a8b2ee065f419c2aeda141436cc556d91ae772d220fd80679f4d431d6c2ab43" + sha256: "91b143666f7bb13636f716b6d4e412e372ab15ff7969799af8c9e30a382e9385" url: "https://pub.dev" source: hosted - version: "2.2.5" + version: "2.2.6" barcode_widget: dependency: "direct main" description: @@ -217,34 +217,34 @@ packages: dependency: transitive description: name: camera_android - sha256: "7215e38fa0be58cc3203a6e48de3636fb9b1bf93d6eeedf667f882d51b3a4bf3" + sha256: "351429510121d179b9aac5a2e8cb525c3cd6c39f4d709c5f72dfb21726e52371" url: "https://pub.dev" source: hosted - version: "0.10.8+15" + version: "0.10.8+16" camera_avfoundation: dependency: transitive description: name: camera_avfoundation - sha256: "3c8dd395f18722f01b5f325ddd7f5256e9bcdce538fb9243b378ba759df3283c" + sha256: "1408600aa45faad05c518afccaabcd58419412ab67755a405b2ddce4f93fa120" url: "https://pub.dev" source: hosted - version: "0.9.13+8" + version: "0.9.13+9" camera_platform_interface: dependency: transitive description: name: camera_platform_interface - sha256: b6a568984254cadaca41a6b896d87d3b2e79a2e5791afa036f8d524c6783b93a + sha256: e971ebca970f7cfee396f76ef02070b5e441b4aa04942da9c108d725f57bbd32 url: "https://pub.dev" source: hosted - version: "2.7.0" + version: "2.7.2" camera_web: dependency: transitive description: name: camera_web - sha256: d4c2c571c7af04f8b10702ca16bb9ed2a26e64534171e8f75c9349b2c004d8f1 + sha256: f18ccfb33b2a7c49a52ad5aa3f07330b7422faaecbdfd9b9fe8e51182f6ad67d url: "https://pub.dev" source: hosted - version: "0.3.2+3" + version: "0.3.2+4" carousel_slider: dependency: "direct main" description: @@ -289,10 +289,10 @@ packages: dependency: transitive description: name: code_builder - sha256: feee43a5c05e7b3199bb375a86430b8ada1b04104f2923d0e03cc01ca87b6d84 + sha256: f692079e25e7869c14132d39f223f8eec9830eb76131925143b2129c4bb01b37 url: "https://pub.dev" source: hosted - version: "4.9.0" + version: "4.10.0" collection: dependency: "direct main" description: @@ -489,10 +489,10 @@ packages: dependency: transitive description: name: file_selector_platform_interface - sha256: "0aa47a725c346825a2bd396343ce63ac00bda6eff2fbc43eabe99737dede8262" + sha256: a3994c26f10378a039faa11de174d7b78eb8f79e4dd0af2a451410c1a5c3f66b url: "https://pub.dev" source: hosted - version: "2.6.1" + version: "2.6.2" file_selector_windows: dependency: transitive description: @@ -847,18 +847,18 @@ packages: dependency: transitive description: name: image_picker_android - sha256: ecdc963d2aa67af5195e723a40580f802d4392e31457a12a562b3e2bd6a396fe + sha256: "1a27bf4cc0330389cebe465bab08fe6dec97e44015b4899637344bb7297759ec" url: "https://pub.dev" source: hosted - version: "0.8.9+1" + version: "0.8.9+2" image_picker_for_web: dependency: transitive description: name: image_picker_for_web - sha256: "50bc9ae6a77eea3a8b11af5eb6c661eeb858fdd2f734c2a4fd17086922347ef7" + sha256: e2423c53a68b579a7c37a1eda967b8ae536c3d98518e5db95ca1fe5719a730a3 url: "https://pub.dev" source: hosted - version: "3.0.1" + version: "3.0.2" image_picker_ios: dependency: transitive description: @@ -887,10 +887,10 @@ packages: dependency: transitive description: name: image_picker_platform_interface - sha256: ed9b00e63977c93b0d2d2b343685bed9c324534ba5abafbb3dfbd6a780b1b514 + sha256: fa4e815e6fcada50e35718727d83ba1c92f1edf95c0b4436554cec301b56233b url: "https://pub.dev" source: hosted - version: "2.9.1" + version: "2.9.3" image_picker_windows: dependency: transitive description: @@ -1101,7 +1101,7 @@ packages: description: path: "." ref: HEAD - resolved-ref: "215b1027d8df69f621e18c855c8cb09db2fa9640" + resolved-ref: e91bc71bccc317948dc769b8c5d55052d949ec2e url: "https://github.com/openfoodfacts/openfoodfacts_flutter_lints.git" source: git version: "1.0.0" @@ -1157,10 +1157,10 @@ packages: dependency: transitive description: name: path_provider_android - sha256: e595b98692943b4881b219f0a9e3945118d3c16bd7e2813f98ec6e532d905f72 + sha256: "477184d672607c0a3bf68fbbf601805f92ef79c82b64b4d6eb318cbca4c48668" url: "https://pub.dev" source: hosted - version: "2.2.1" + version: "2.2.2" path_provider_foundation: dependency: transitive description: @@ -1466,10 +1466,10 @@ packages: dependency: transitive description: name: shared_preferences_platform_interface - sha256: d4ec5fc9ebb2f2e056c617112aa75dcf92fc2e4faaf2ae999caa297473f75d8a + sha256: "22e2ecac9419b4246d7c22bfbbda589e3acf5c0351137d87dd2939d984d37c3b" url: "https://pub.dev" source: hosted - version: "2.3.1" + version: "2.3.2" shared_preferences_web: dependency: transitive description: @@ -1639,18 +1639,18 @@ packages: dependency: transitive description: name: url_launcher_android - sha256: "31222ffb0063171b526d3e569079cf1f8b294075ba323443fdc690842bfd4def" + sha256: "507dc655b1d9cb5ebc756032eb785f114e415f91557b73bf60b7e201dfedeb2f" url: "https://pub.dev" source: hosted - version: "6.2.0" + version: "6.2.2" url_launcher_ios: dependency: transitive description: name: url_launcher_ios - sha256: bba3373219b7abb6b5e0d071b0fe66dfbe005d07517a68e38d4fc3638f35c6d3 + sha256: cdb7b6da34483f9b2c9f8b2b29bc468fa7271d92e2021607ca0c4d3bcb04cdd4 url: "https://pub.dev" source: hosted - version: "6.2.1" + version: "6.2.3" url_launcher_linux: dependency: transitive description: @@ -1671,18 +1671,18 @@ packages: dependency: transitive description: name: url_launcher_platform_interface - sha256: "980e8d9af422f477be6948bdfb68df8433be71f5743a188968b0c1b887807e50" + sha256: a932c3a8082e118f80a475ce692fde89dc20fddb24c57360b96bc56f7035de1f url: "https://pub.dev" source: hosted - version: "2.2.0" + version: "2.3.1" url_launcher_web: dependency: transitive description: name: url_launcher_web - sha256: "7286aec002c8feecc338cc33269e96b73955ab227456e9fb2a91f7fab8a358e9" + sha256: fff0932192afeedf63cdd50ecbb1bc825d31aed259f02bb8dba0f3b729a5e88b url: "https://pub.dev" source: hosted - version: "2.2.2" + version: "2.2.3" url_launcher_windows: dependency: transitive description: @@ -1703,26 +1703,26 @@ packages: dependency: transitive description: name: vector_graphics - sha256: "0f0c746dd2d6254a0057218ff980fc7f5670fd0fcf5e4db38a490d31eed4ad43" + sha256: "18f6690295af52d081f6808f2f7c69f0eed6d7e23a71539d75f4aeb8f0062172" url: "https://pub.dev" source: hosted - version: "1.1.9+1" + version: "1.1.9+2" vector_graphics_codec: dependency: transitive description: name: vector_graphics_codec - sha256: "0edf6d630d1bfd5589114138ed8fada3234deacc37966bec033d3047c29248b7" + sha256: "531d20465c10dfac7f5cd90b60bbe4dd9921f1ec4ca54c83ebb176dbacb7bb2d" url: "https://pub.dev" source: hosted - version: "1.1.9+1" + version: "1.1.9+2" vector_graphics_compiler: dependency: transitive description: name: vector_graphics_compiler - sha256: d24333727332d9bd20990f1483af4e09abdb9b1fc7c3db940b56ab5c42790c26 + sha256: "03012b0a33775c5530576b70240308080e1d5050f0faf000118c20e6463bc0ad" url: "https://pub.dev" source: hosted - version: "1.1.9+1" + version: "1.1.9+2" vector_math: dependency: transitive description: @@ -1807,10 +1807,10 @@ packages: dependency: transitive description: name: win32 - sha256: b0f37db61ba2f2e9b7a78a1caece0052564d1bc70668156cf3a29d676fe4e574 + sha256: "464f5674532865248444b4c3daca12bd9bf2d7c47f759ce2617986e7229494a8" url: "https://pub.dev" source: hosted - version: "5.1.1" + version: "5.2.0" win32_registry: dependency: transitive description: @@ -1831,10 +1831,10 @@ packages: dependency: transitive description: name: xdg_directories - sha256: "589ada45ba9e39405c198fe34eb0f607cddb2108527e658136120892beac46d2" + sha256: faea9dee56b520b55a566385b84f2e8de55e7496104adada9962e0bd11bcff1d url: "https://pub.dev" source: hosted - version: "1.0.3" + version: "1.0.4" xml: dependency: transitive description: