kopia lustrzana https://gitlab.com/mysocialportal/relatica
223 wiersze
6.9 KiB
Dart
223 wiersze
6.9 KiB
Dart
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|
import 'package:logging/logging.dart';
|
|
import 'package:result_monad/result_monad.dart';
|
|
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
|
|
|
import '../models/auth/profile.dart';
|
|
import '../models/exec_error.dart';
|
|
import '../models/gallery_data.dart';
|
|
import '../models/image_entry.dart';
|
|
import '../models/networking/paging_data.dart';
|
|
import 'networking/friendica_gallery_client_services.dart';
|
|
import 'networking/friendica_image_client_services.dart';
|
|
import 'rp_provider_extension.dart';
|
|
|
|
part 'gallery_services.g.dart';
|
|
|
|
final _galleryLogger = Logger('GalleriesProvider');
|
|
|
|
@riverpod
|
|
class _Galleries extends _$Galleries {
|
|
@override
|
|
Future<Result<Map<String, GalleryData>, ExecError>> build(
|
|
Profile profile,
|
|
) async {
|
|
_galleryLogger.info('Build for $profile');
|
|
final result = await updateGalleries();
|
|
ref.cacheFor(const Duration(minutes: 30));
|
|
return result;
|
|
}
|
|
|
|
Future<Result<Map<String, GalleryData>, ExecError>> updateGalleries() async {
|
|
_galleryLogger.info('Updating galleries for $profile');
|
|
//may need to force update
|
|
final result = await ref.read(galleryDataProvider(profile).future);
|
|
if (result.isFailure) {
|
|
return result.errorCast();
|
|
}
|
|
|
|
final galleriesMap = {for (final g in result.value) g.name: g};
|
|
_galleryLogger
|
|
.info(() => 'New gallery data for $profile : ${galleriesMap.values}');
|
|
final rval = Result.ok(galleriesMap).execErrorCast();
|
|
state = AsyncData(rval);
|
|
return rval;
|
|
}
|
|
}
|
|
|
|
final _glLogger = Logger('GalleryListProvider');
|
|
|
|
@riverpod
|
|
class GalleryList extends _$GalleryList {
|
|
@override
|
|
Future<Result<List<GalleryData>, ExecError>> build(Profile profile) async {
|
|
_glLogger.info('Build for $profile');
|
|
final result = await ref
|
|
.watch(_galleriesProvider(profile).future)
|
|
.transform((gm) => gm.values.toList())
|
|
.execErrorCastAsync();
|
|
ref.cacheFor(const Duration(minutes: 30));
|
|
return result;
|
|
}
|
|
|
|
Future<Result<Map<String, GalleryData>, ExecError>> updateGalleries() async {
|
|
_galleryLogger.info('Updating galleries for $profile');
|
|
return await ref
|
|
.read(_galleriesProvider(profile).notifier)
|
|
.updateGalleries();
|
|
}
|
|
}
|
|
|
|
@riverpod
|
|
class Gallery extends _$Gallery {
|
|
@override
|
|
Future<Result<GalleryData, ExecError>> build(
|
|
Profile profile, String galleryName) async {
|
|
final result = await ref
|
|
.watch(_galleriesProvider(profile).future)
|
|
.andThen<GalleryData, ExecError>((gm) => gm.containsKey(galleryName)
|
|
? Result.ok(gm[galleryName]!)
|
|
: buildErrorResult(
|
|
type: ErrorType.notFound,
|
|
message: '$galleryName does not exist'))
|
|
.execErrorCastAsync();
|
|
ref.cacheFor(const Duration(minutes: 30));
|
|
return result;
|
|
}
|
|
|
|
Future<Result<bool, ExecError>> rename(String newName) async {
|
|
if (newName.isEmpty) {
|
|
return buildErrorResult(
|
|
type: ErrorType.argumentError,
|
|
message: 'Gallery name cannot be empty',
|
|
);
|
|
}
|
|
|
|
final result = await ref
|
|
.read(renameGalleryProvider(profile, galleryName, newName).future)
|
|
.withResultAsync(
|
|
(_) async => await ref
|
|
.read(_galleriesProvider(profile).notifier)
|
|
.updateGalleries(),
|
|
)
|
|
.withResult((_) => ref.invalidateSelf());
|
|
|
|
return result.execErrorCast();
|
|
}
|
|
}
|
|
|
|
@riverpod
|
|
class GalleryImages extends _$GalleryImages {
|
|
static const _imagesPerPage = 50;
|
|
final pages = <PagingData>[];
|
|
|
|
@override
|
|
Future<Result<List<ImageEntry>, ExecError>> build(
|
|
Profile profile, String galleryName) async {
|
|
final result = await updateGalleryImages(
|
|
withNextPage: true,
|
|
);
|
|
ref.cacheFor(const Duration(minutes: 30));
|
|
return result;
|
|
}
|
|
|
|
FutureResult<List<ImageEntry>, ExecError> updateGalleryImages(
|
|
{required bool withNextPage, bool nextPageOnly = true}) async {
|
|
if (pages.isEmpty) {
|
|
pages.add(PagingData(offset: 0, limit: _imagesPerPage));
|
|
} else if (withNextPage) {
|
|
final offset = pages.last.offset! + _imagesPerPage;
|
|
pages.add(PagingData(offset: offset, limit: _imagesPerPage));
|
|
}
|
|
|
|
final imageSet = switch (state) {
|
|
AsyncData(:final value) => value.fold(
|
|
onSuccess: (images) => images.toSet(),
|
|
onError: (_) => <ImageEntry>{}),
|
|
_ => <ImageEntry>{},
|
|
};
|
|
|
|
final pagesToUse = nextPageOnly ? [pages.last] : pages;
|
|
for (final page in pagesToUse) {
|
|
final result = await ref
|
|
.read(galleryImagesClientProvider(profile, galleryName, page).future);
|
|
if (result.isFailure) {
|
|
return result.errorCast();
|
|
} else {
|
|
imageSet.addAll(result.value);
|
|
}
|
|
}
|
|
|
|
final result = Result.ok(imageSet.toList()).execErrorCast();
|
|
state = AsyncData(result);
|
|
return result;
|
|
}
|
|
|
|
FutureResult<ImageEntry, ExecError> updateImage(ImageEntry image) async {
|
|
final List<ImageEntry> images = switch (state) {
|
|
AsyncData(:final value) => value.fold(
|
|
onSuccess: (images) => List.from(images),
|
|
onError: (_) => <ImageEntry>[]),
|
|
_ => <ImageEntry>[],
|
|
};
|
|
|
|
final index = images.indexOf(image);
|
|
if (index < 0) {
|
|
return buildErrorResult(
|
|
type: ErrorType.notFound,
|
|
message: 'Image ${image.id} does not exist for ${image.album}');
|
|
}
|
|
final result = await ref
|
|
.read(editImageDataProvider(profile, image).future)
|
|
.withResult((_) {
|
|
images[index] = image;
|
|
});
|
|
|
|
state = AsyncData(result.transform((_) => images).execErrorCast());
|
|
return result.execErrorCast();
|
|
}
|
|
|
|
FutureResult<ImageEntry, ExecError> deleteImage(ImageEntry image) async {
|
|
final images = switch (state) {
|
|
AsyncData(:final value) => value.fold(
|
|
onSuccess: (images) => images.toSet(),
|
|
onError: (_) => <ImageEntry>{}),
|
|
_ => <ImageEntry>{},
|
|
};
|
|
|
|
if (!images.contains(image)) {
|
|
return buildErrorResult(
|
|
type: ErrorType.notFound,
|
|
message: 'Image ${image.id} does not exist for ${image.album}');
|
|
}
|
|
|
|
final result = await ref
|
|
.read(deleteImageProvider(profile, image).future)
|
|
.withResult((_) => images.remove(image));
|
|
|
|
state = AsyncData(result.transform((_) => images.toList()).execErrorCast());
|
|
return result.execErrorCast();
|
|
}
|
|
|
|
void refresh() {
|
|
ref.invalidateSelf();
|
|
}
|
|
}
|
|
|
|
@riverpod
|
|
Future<Result<ImageEntry, ExecError>> galleryImage(
|
|
Ref ref, Profile profile, String galleryName, String id) async {
|
|
final result = await ref
|
|
.read(galleryImagesProvider(profile, galleryName).future)
|
|
.transform((images) => images.where((i) => i.id == id).toList())
|
|
.andThen<ImageEntry, ExecError>((images) => images.isNotEmpty
|
|
? Result.ok(images.first)
|
|
: buildErrorResult(
|
|
type: ErrorType.notFound,
|
|
message: 'Image $id not found in gallery $galleryName',
|
|
))
|
|
.execErrorCastAsync();
|
|
|
|
return result;
|
|
}
|