import 'package:flutter/foundation.dart'; import 'package:result_monad/result_monad.dart'; import '../friendica_client/friendica_client.dart'; import '../friendica_client/paging_data.dart'; import '../models/auth/profile.dart'; import '../models/exec_error.dart'; import '../models/gallery_data.dart'; import '../models/image_entry.dart'; class GalleryService extends ChangeNotifier { static const IMAGES_PER_PAGE = 50; final _galleries = {}; final _galleryPages = >{}; final _images = >{}; var _loaded = false; final Profile profile; GalleryService(this.profile); bool get loaded => _loaded; void clear() { _galleries.clear(); _galleryPages.clear(); _images.clear(); _loaded = false; notifyListeners(); } List getGalleries() { if (_galleries.isEmpty) { updateGalleries(); } return _galleries.values.toList(growable: false); } Result getGallery(String galleryName) { final gallery = _galleries[galleryName]; if (gallery == null) { return buildErrorResult( type: ErrorType.notFound, message: 'Gallery $galleryName not found', ); } return Result.ok(gallery); } FutureResult, ExecError> updateGalleries() async { final result = await GalleryClient(profile).getGalleryData(); if (result.isFailure) { return result.errorCast(); } final galleriesReturned = result.value.map((g) => g.name).toList(); _galleries.clear(); _galleryPages.removeWhere((key, value) => !galleriesReturned.contains(key)); _images.removeWhere((key, value) => !galleriesReturned.contains(key)); for (final gallery in result.value) { _galleries[gallery.name] = gallery; } _loaded = true; notifyListeners(); return Result.ok(_galleries.values.toList(growable: false)); } Result, ExecError> getGalleryImageList(String galleryName) { if (!_galleries.containsKey(galleryName)) { return Result.error( ExecError( type: ErrorType.localError, message: 'Unknown Gallery: $galleryName', ), ); } if (!_images.containsKey(galleryName)) { updateGalleryImageList(galleryName: galleryName, withNextPage: true); return Result.ok([]); } else { return Result.ok(_images[galleryName]!.toList(growable: false)); } } FutureResult renameGallery( GalleryData gallery, String newName) async { if (!_galleries.containsKey(gallery.name)) { return buildErrorResult( type: ErrorType.notFound, message: 'Unknown gallery: ${gallery.name}'); } final result = await GalleryClient(profile) .renameGallery(gallery.name, newName) .transform((_) => gallery.copy(name: newName)) .withResultAsync((_) async => await updateGalleries()); return result.execErrorCast(); } //TODO Paging FutureResult, ExecError> updateGalleryImageList( {required String galleryName, required bool withNextPage, bool nextPageOnly = true}) async { final pages = _galleryPages.putIfAbsent(galleryName, () => []); if (pages.isEmpty) { pages.add(PagingData(offset: 0, limit: IMAGES_PER_PAGE)); } else if (withNextPage) { final offset = pages.last.offset! + IMAGES_PER_PAGE; pages.add(PagingData(offset: offset, limit: IMAGES_PER_PAGE)); } final imageSet = nextPageOnly ? _images.putIfAbsent(galleryName, () => []).toSet() : {}; final pagesToUse = nextPageOnly ? [pages.last] : pages; for (final page in pagesToUse) { final result = await GalleryClient(profile).getGalleryImages(galleryName, page); if (result.isFailure) { return result.errorCast(); } imageSet.addAll(result.value); } _images[galleryName] = imageSet.toList(); notifyListeners(); return Result.ok(imageSet.toList(growable: false)); } Result getImage(String galleryName, String id) { if (!_images.containsKey(galleryName)) { return buildErrorResult( type: ErrorType.notFound, message: 'Image gallery $galleryName not known.', ); } final potentialImages = _images[galleryName]?.where((i) => i.id == id).toList() ?? []; if (potentialImages.isEmpty) { return buildErrorResult( type: ErrorType.notFound, message: 'Image $id not found in gallery $galleryName', ); } return Result.ok(potentialImages.first); } FutureResult updateImage(ImageEntry image) async { final images = _images[image.album]; if (images == null) { buildErrorResult( type: ErrorType.notFound, message: 'Album not found ${image.album}'); } final index = _images[image.album]!.indexOf(image); if (index < 0) { return buildErrorResult( type: ErrorType.notFound, message: 'Image ${image.id} does not exist for ${image.album}'); } final result = await ImageClient(profile).editImageData(image).withResult((_) { images!.removeAt(index); images.insert(index, image); }); notifyListeners(); return result.execErrorCast(); } FutureResult deleteImage(ImageEntry image) async { final images = _images[image.album]; if (images == null) { buildErrorResult( type: ErrorType.notFound, message: 'Album not found ${image.album}'); } final index = _images[image.album]!.indexOf(image); if (index < 0) { return buildErrorResult( type: ErrorType.notFound, message: 'Image ${image.id} does not exist for ${image.album}'); } final result = await ImageClient(profile) .deleteImage(image) .withResultAsync((_) async { images!.removeAt(index); await updateGalleries(); notifyListeners(); }); return result.execErrorCast(); } }