From 6cfe0bd94ed05d6cb40673fc8cc997c2297c9dfc Mon Sep 17 00:00:00 2001 From: Yassin Nouh <70436855+YassinNouh21@users.noreply.github.com> Date: Wed, 16 Oct 2024 18:50:20 +0300 Subject: [PATCH] feat:improve Quran data caching mechanism - Add cache validity duration check in QuranImpl - Implement last update timestamp functionality in QuranLocalDataSource - Modify QuranImpl to use cache when valid and fall back to remote data - Add SharedPreferences for storing timestamp in QuranLocalDataSource - Create new SaveLastUpdateTimestampException - Update quranLocalDataSourceProvider to include SharedPreferences --- .../quran/quran_local_data_source.dart | 26 ++++++++++++- lib/src/data/repository/quran/quran_impl.dart | 37 +++++++++++++++++-- lib/src/domain/error/quran_exceptions.dart | 5 +++ 3 files changed, 62 insertions(+), 6 deletions(-) diff --git a/lib/src/data/data_source/quran/quran_local_data_source.dart b/lib/src/data/data_source/quran/quran_local_data_source.dart index 415cf93b1..66711cc87 100644 --- a/lib/src/data/data_source/quran/quran_local_data_source.dart +++ b/lib/src/data/data_source/quran/quran_local_data_source.dart @@ -1,16 +1,19 @@ import 'dart:developer'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:fpdart/fpdart.dart'; import 'package:hive_flutter/adapters.dart'; import 'package:mawaqit/src/const/constants.dart'; import 'package:mawaqit/src/domain/model/quran/surah_model.dart'; import 'package:mawaqit/src/domain/error/quran_exceptions.dart'; +import 'package:shared_preferences/shared_preferences.dart'; class QuranLocalDataSource { final Box _surahBox; + final SharedPreferences _prefs; - QuranLocalDataSource(this._surahBox); + QuranLocalDataSource(this._surahBox, this._prefs); /// [saveSuwarByLanguage] save the list of surwars by language code Future saveSuwarByLanguage(String languageCode, List suwar) async { @@ -69,9 +72,28 @@ class QuranLocalDataSource { throw CannotFindSuwarByLanguageException(e.toString()); } } + + Future> getLastUpdateTimestamp(String languageCode) async { + try { + final timestamp = _prefs.getInt('${languageCode}_last_fetch'); + return Option.fromNullable(timestamp != null ? DateTime.fromMillisecondsSinceEpoch(timestamp) : null); + } catch (e) { + log('quran: getLastUpdateTimestamp: ${e.toString()}'); + return None(); + } + } + + Future saveLastUpdateTimestamp(String languageCode, DateTime timestamp) async { + try { + await _prefs.setInt('${languageCode}_last_fetch', timestamp.millisecondsSinceEpoch); + } catch (e) { + throw SaveLastUpdateTimestampException(e.toString()); + } + } } final quranLocalDataSourceProvider = FutureProvider((ref) async { final surahBox = await Hive.openBox(QuranConstant.kSurahBox); - return QuranLocalDataSource(surahBox); + final prefs = await SharedPreferences.getInstance(); + return QuranLocalDataSource(surahBox, prefs); }); diff --git a/lib/src/data/repository/quran/quran_impl.dart b/lib/src/data/repository/quran/quran_impl.dart index b8dd6e7f7..73c0beb00 100644 --- a/lib/src/data/repository/quran/quran_impl.dart +++ b/lib/src/data/repository/quran/quran_impl.dart @@ -10,6 +10,7 @@ import 'package:mawaqit/src/data/data_source/quran/quran_remote_data_source.dart class QuranImpl extends QuranRepository { final QuranRemoteDataSource _quranRemoteDataSource; final QuranLocalDataSource _quranLocalDataSource; + final Duration _cacheValidityDuration = Duration(hours: 24); QuranImpl( this._quranRemoteDataSource, @@ -28,15 +29,43 @@ class QuranImpl extends QuranRepository { String languageCode = 'en', }) async { try { + // Check if cached data exists and is still valid + if (await _isCacheValid(languageCode)) { + final cachedSuwar = await _quranLocalDataSource.getSuwarByLanguage(languageCode); + return cachedSuwar; + } + + // If cache is invalid or doesn't exist, fetch from remote final suwar = await _quranRemoteDataSource.getSuwarByLanguage(languageCode: languageCode); - log('quran: QuranImpl: getSuwarByLanguage: ${suwar[0]}'); - await _quranLocalDataSource.saveSuwarByLanguage(languageCode, suwar); + + // Save the new data to cache with current timestamp + await _saveSuwarWithTimestamp(languageCode, suwar); + return suwar; } on Exception catch (_) { - final suwar = await _quranLocalDataSource.getSuwarByLanguage(languageCode); - return suwar; + // If remote fetch fails, try to return cached data even if it's outdated + final cachedSuwar = await _quranLocalDataSource.getSuwarByLanguage(languageCode); + if (cachedSuwar.isNotEmpty) { + return cachedSuwar; + } + // If no cached data, rethrow the exception + rethrow; } } + + Future _isCacheValid(String languageCode) async { + final lastUpdateTimestamp = await _quranLocalDataSource.getLastUpdateTimestamp(languageCode); + return lastUpdateTimestamp.fold(() => false, (time) { + final currentTime = DateTime.now(); + final difference = currentTime.difference(time); + return difference < _cacheValidityDuration; + }); + } + + Future _saveSuwarWithTimestamp(String languageCode, List suwar) async { + await _quranLocalDataSource.saveSuwarByLanguage(languageCode, suwar); + await _quranLocalDataSource.saveLastUpdateTimestamp(languageCode, DateTime.now()); + } } final quranRepositoryProvider = FutureProvider((ref) async { diff --git a/lib/src/domain/error/quran_exceptions.dart b/lib/src/domain/error/quran_exceptions.dart index facb60b51..1bf25f2a8 100644 --- a/lib/src/domain/error/quran_exceptions.dart +++ b/lib/src/domain/error/quran_exceptions.dart @@ -103,3 +103,8 @@ class CannotFindSuwarByLanguageException extends QuranException { CannotFindSuwarByLanguageException(String message) : super('Error occurred while finding suwar by language: $message', 'FIND_SUWAR_BY_LANGUAGE_ERROR'); } + +class SaveLastUpdateTimestampException extends QuranException { + SaveLastUpdateTimestampException(String message) + : super('Error occurred while saving last update timestamp: $message', 'SAVE_LAST_UPDATE_TIMESTAMP_ERROR'); +}