diff --git a/lib/features/home/api/unsplash_images_api.dart b/lib/features/home/api/unsplash_images_api.dart index 5cde25c..5196ab2 100644 --- a/lib/features/home/api/unsplash_images_api.dart +++ b/lib/features/home/api/unsplash_images_api.dart @@ -1,29 +1,37 @@ import 'dart:async'; import 'dart:math'; +import 'package:mc_gallery/features/core/services/logging_service.dart'; +import 'package:mc_gallery/locator.dart'; + import '/features/core/data/constants/const_values.dart'; import '/l10n/generated/l10n.dart'; import '../abstracts/images_api.dart'; import '../data/models/image_model.dart'; -class UnsplashImagesApi implements ImagesApi { +class UnsplashImagesApi with LoggingService implements ImagesApi { @override FutureOr> fetchImageUri({required String token}) { final random = Random(); - return Iterable.generate(ConstValues.numberOfImages).map((final imageIndex) { - // Drawing from a normal distribution - final imageSide = ConstValues.minImageSize + - random.nextInt((ConstValues.maxImageSize + 1) - ConstValues.minImageSize); + try { + return Iterable.generate(ConstValues.numberOfImages).map((final imageIndex) { + // Drawing from a normal distribution + final imageSide = ConstValues.minImageSize + + random.nextInt((ConstValues.maxImageSize + 1) - ConstValues.minImageSize); - final imageUri = _imageUrlGenerator(imageSide: imageSide); + final imageUri = _imageUrlGenerator(imageSide: imageSide); - return ImageModel( - imageIndex: imageIndex, - uri: imageUri, - imageName: Strings.current.image, - ); - }); + return ImageModel( + imageIndex: imageIndex, + uri: imageUri, + imageName: Strings.current.image, + ); + }); + } on Exception catch (ex, stackTrace) { + handleException(ex, stackTrace); + return const Iterable.empty(); + } } Uri _imageUrlGenerator({required int imageSide}) => Uri( @@ -31,4 +39,6 @@ class UnsplashImagesApi implements ImagesApi { host: ConstValues.backendHost, pathSegments: [...ConstValues.backendUrlPathSegments, '${imageSide}x$imageSide'], ); + + static UnsplashImagesApi get locate => Locator.locate(); } diff --git a/lib/features/home/services/image_cache_manager_service.dart b/lib/features/home/services/image_cache_manager_service.dart new file mode 100644 index 0000000..c0282e4 --- /dev/null +++ b/lib/features/home/services/image_cache_manager_service.dart @@ -0,0 +1,37 @@ +import 'dart:ui'; + +import 'package:flutter_cache_manager/flutter_cache_manager.dart'; +import 'package:mc_gallery/features/core/services/app_lifecycle_service.dart'; +import 'package:mc_gallery/features/core/services/logging_service.dart'; +import 'package:mc_gallery/locator.dart'; + +class ImageCacheManagerService with LoggingService { + ImageCacheManagerService({ + required AppLifecycleService appLifecycleService, + }) : _appLifecycleService = appLifecycleService { + _init(); + } + + final AppLifecycleService _appLifecycleService; + + Future emptyCache() async => await DefaultCacheManager().emptyCache(); + + Future _init() async { + _appLifecycleService.addListener( + tag: runtimeType.toString(), + listener: (final appLifecycleState) async { + switch (appLifecycleState) { + case AppLifecycleState.resumed: + break; + case AppLifecycleState.inactive: + case AppLifecycleState.paused: + case AppLifecycleState.detached: + info('Discarding cached images'); + await DefaultCacheManager().emptyCache(); + } + }, + ); + } + + static ImageCacheManagerService get locate => Locator.locate(); +} diff --git a/lib/features/home/services/images_service.dart b/lib/features/home/services/images_service.dart index 0086262..0a138f9 100644 --- a/lib/features/home/services/images_service.dart +++ b/lib/features/home/services/images_service.dart @@ -1,3 +1,4 @@ +import 'package:mc_gallery/features/core/services/logging_service.dart'; import 'package:mc_gallery/features/home/data/models/image_model.dart'; import 'package:mc_gallery/locator.dart'; @@ -8,18 +9,28 @@ import '../abstracts/images_api.dart'; /// Since this is very simple use-case, this is the only interface. For complex (actual CRUD-based) I/O, /// an additional Repository layer interface can be used between [ImagesService] and [ImagesApi]. class ImagesService { - ImagesService({required ImagesApi imagesApi}) : _imagesApi = imagesApi { + ImagesService({ + required ImagesApi imagesApi, + required LoggingService loggingService, + }) : _imagesApi = imagesApi, + _loggingService = loggingService { _init(); } final ImagesApi _imagesApi; + final LoggingService _loggingService; late final Iterable _imageModels; Iterable get imageModels => _imageModels; Future _init() async { + _loggingService.info('Fetching and creating image models...'); _imageModels = await _imagesApi.fetchImageUri(token: ''); + _imageModels.isNotEmpty + ? _loggingService.good("Created ${_imageModels.length} images' models") + : _loggingService.warning('No images found'); + Locator.instance().signalReady(this); } diff --git a/lib/features/home/views/gallery/gallery_view_model.dart b/lib/features/home/views/gallery/gallery_view_model.dart index fcacf41..c78240e 100644 --- a/lib/features/home/views/gallery/gallery_view_model.dart +++ b/lib/features/home/views/gallery/gallery_view_model.dart @@ -1,7 +1,7 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter_cache_manager/flutter_cache_manager.dart'; -import 'package:mc_gallery/features/core/services/app_lifecycle_service.dart'; +import 'package:mc_gallery/features/home/services/image_cache_manager_service.dart'; import '/features/core/abstracts/base_view_model.dart'; import '/features/core/services/logging_service.dart'; @@ -15,16 +15,16 @@ class GalleryViewModel extends BaseViewModel { GalleryViewModel({ required ImagesService imagesService, required NavigationService navigationService, - required AppLifecycleService appLifecycleService, + required ImageCacheManagerService imageCacheManagerService, required LoggingService loggingService, }) : _imagesService = imagesService, _navigationService = navigationService, - _appLifecycleService = appLifecycleService, + _imageCacheManagerService = imageCacheManagerService, _loggingService = loggingService; final ImagesService _imagesService; final NavigationService _navigationService; - final AppLifecycleService _appLifecycleService; + final ImageCacheManagerService _imageCacheManagerService; final LoggingService _loggingService; final ValueNotifier _isDisplayingPressingPrompt = ValueNotifier(true); @@ -32,27 +32,11 @@ class GalleryViewModel extends BaseViewModel { @override Future initialise(bool Function() mounted, [arguments]) async { - _appLifecycleService.addListener( - tag: runtimeType.toString(), - listener: (final appLifecycleState) async { - switch (appLifecycleState) { - case AppLifecycleState.resumed: - break; - case AppLifecycleState.inactive: - case AppLifecycleState.paused: - case AppLifecycleState.detached: - await DefaultCacheManager().emptyCache(); - } - }, - ); - super.initialise(mounted, arguments); } @override Future dispose() async { - await _appLifecycleService.removeListener(tag: runtimeType.toString()); - super.dispose(); } diff --git a/lib/features/home/views/image_carousel/image_carousel_view_model.dart b/lib/features/home/views/image_carousel/image_carousel_view_model.dart index 76dc40f..4302ff9 100644 --- a/lib/features/home/views/image_carousel/image_carousel_view_model.dart +++ b/lib/features/home/views/image_carousel/image_carousel_view_model.dart @@ -1,8 +1,5 @@ -import 'dart:ui'; - import 'package:flutter/foundation.dart'; import 'package:flutter_cache_manager/flutter_cache_manager.dart'; -import 'package:mc_gallery/features/core/services/app_lifecycle_service.dart'; import '/features/core/abstracts/base_view_model.dart'; import '/features/core/services/logging_service.dart'; @@ -16,16 +13,13 @@ class ImageCarouselViewModel extends BaseViewModel { ImageCarouselViewModel({ required ImagesService imagesService, required NavigationService navigationService, - required AppLifecycleService appLifecycleService, required LoggingService loggingService, }) : _imagesService = imagesService, _navigationService = navigationService, - _appLifecycleService = appLifecycleService, _loggingService = loggingService; final ImagesService _imagesService; final NavigationService _navigationService; - final AppLifecycleService _appLifecycleService; final LoggingService _loggingService; late final ValueNotifier _currentImageModel; @@ -33,19 +27,6 @@ class ImageCarouselViewModel extends BaseViewModel { @override Future initialise(bool Function() mounted, [arguments]) async { - _appLifecycleService.addListener( - tag: runtimeType.toString(), - listener: (final appLifecycleState) async { - switch (appLifecycleState) { - case AppLifecycleState.resumed: - break; - case AppLifecycleState.inactive: - case AppLifecycleState.paused: - case AppLifecycleState.detached: - await DefaultCacheManager().emptyCache(); - } - }); - _currentImageModel = ValueNotifier(_imagesService.imageModels .elementAt((arguments! as ImageCarouselViewArguments).imageIndexKey)); @@ -54,8 +35,6 @@ class ImageCarouselViewModel extends BaseViewModel { @override Future dispose() async { - await _appLifecycleService.removeListener(tag: runtimeType.toString()); - super.dispose(); } diff --git a/lib/locator.dart b/lib/locator.dart index 61b4482..cb1b0f1 100644 --- a/lib/locator.dart +++ b/lib/locator.dart @@ -7,6 +7,7 @@ import 'package:mc_gallery/features/core/abstracts/router/app_router.dart'; import 'package:mc_gallery/features/core/services/logging_service.dart'; import 'package:mc_gallery/features/core/services/navigation_service.dart'; import 'package:mc_gallery/features/home/api/unsplash_images_api.dart'; +import 'package:mc_gallery/features/home/services/image_cache_manager_service.dart'; import 'package:mc_gallery/features/home/services/images_service.dart'; import 'package:mc_gallery/features/home/views/gallery/gallery_view_model.dart'; import 'package:mc_gallery/features/home/views/image_carousel/image_carousel_view_model.dart'; @@ -44,7 +45,7 @@ class Locator { () => GalleryViewModel( imagesService: ImagesService.locate, navigationService: NavigationService.locate, - appLifecycleService: AppLifecycleService.locate, + imageCacheManagerService: ImageCacheManagerService.locate, loggingService: LoggingService.locate, ), ); @@ -52,7 +53,6 @@ class Locator { () => ImageCarouselViewModel( imagesService: ImagesService.locate, navigationService: NavigationService.locate, - appLifecycleService: AppLifecycleService.locate, loggingService: LoggingService.locate, ), ); @@ -86,11 +86,14 @@ class Locator { dispose: (param) async => await param.dispose(), ); it.registerSingleton( - ImagesService( - imagesApi: UnsplashImagesApi(), - ), + ImagesService(imagesApi: UnsplashImagesApi.locate, loggingService: LoggingService.locate), signalsReady: true, ); + it.registerSingleton( + ImageCacheManagerService( + appLifecycleService: AppLifecycleService.locate, + ), + ); } static FutureOr _registerRepos(GetIt locator) {}