Skip to content

Commit

Permalink
feat:improve Quran data caching mechanism
Browse files Browse the repository at this point in the history
- 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
  • Loading branch information
YassinNouh21 committed Oct 16, 2024
1 parent 3daaf05 commit 6cfe0bd
Show file tree
Hide file tree
Showing 3 changed files with 62 additions and 6 deletions.
26 changes: 24 additions & 2 deletions lib/src/data/data_source/quran/quran_local_data_source.dart
Original file line number Diff line number Diff line change
@@ -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<void> saveSuwarByLanguage(String languageCode, List<SurahModel> suwar) async {
Expand Down Expand Up @@ -69,9 +72,28 @@ class QuranLocalDataSource {
throw CannotFindSuwarByLanguageException(e.toString());
}
}

Future<Option<DateTime>> 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<void> saveLastUpdateTimestamp(String languageCode, DateTime timestamp) async {
try {
await _prefs.setInt('${languageCode}_last_fetch', timestamp.millisecondsSinceEpoch);
} catch (e) {
throw SaveLastUpdateTimestampException(e.toString());
}
}
}

final quranLocalDataSourceProvider = FutureProvider<QuranLocalDataSource>((ref) async {
final surahBox = await Hive.openBox(QuranConstant.kSurahBox);
return QuranLocalDataSource(surahBox);
final prefs = await SharedPreferences.getInstance();
return QuranLocalDataSource(surahBox, prefs);
});
37 changes: 33 additions & 4 deletions lib/src/data/repository/quran/quran_impl.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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<bool> _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<void> _saveSuwarWithTimestamp(String languageCode, List<SurahModel> suwar) async {
await _quranLocalDataSource.saveSuwarByLanguage(languageCode, suwar);
await _quranLocalDataSource.saveLastUpdateTimestamp(languageCode, DateTime.now());
}
}

final quranRepositoryProvider = FutureProvider<QuranImpl>((ref) async {
Expand Down
5 changes: 5 additions & 0 deletions lib/src/domain/error/quran_exceptions.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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');
}

0 comments on commit 6cfe0bd

Please sign in to comment.