Favourites
This commit is contained in:
parent
1a7abb9e4b
commit
56a9e3f421
10 changed files with 66 additions and 36 deletions
|
@ -7,7 +7,7 @@ import '/features/core/services/logging_service.dart';
|
|||
import '/l10n/generated/l10n.dart';
|
||||
import '/locator.dart';
|
||||
import '../abstracts/images_api.dart';
|
||||
import '../data/models/image_model.dart';
|
||||
import '../data/dtos/image_model_dto.dart';
|
||||
|
||||
class UnsplashImagesApi implements ImagesApi {
|
||||
final LoggingService _loggingService = LoggingService.locate;
|
||||
|
@ -29,7 +29,7 @@ class UnsplashImagesApi implements ImagesApi {
|
|||
|
||||
final imageUri = _imageUrlGenerator(imageSide: imageSide);
|
||||
|
||||
return ImageModel(
|
||||
return ImageModelDto(
|
||||
imageIndex: imageIndex,
|
||||
uri: imageUri,
|
||||
// Custom dummy name for the image
|
||||
|
@ -66,7 +66,7 @@ class UnsplashImagesApi implements ImagesApi {
|
|||
|
||||
final imageUri = _imageUrlGenerator(imageSide: imageSide);
|
||||
|
||||
return ImageModel(
|
||||
return ImageModelDto(
|
||||
imageIndex: imageIndex,
|
||||
uri: imageUri,
|
||||
// Custom dummy name for the image
|
||||
|
|
28
lib/features/home/data/dtos/image_model_dto.dart
Normal file
28
lib/features/home/data/dtos/image_model_dto.dart
Normal file
|
@ -0,0 +1,28 @@
|
|||
import 'package:json_annotation/json_annotation.dart';
|
||||
|
||||
part 'image_model_dto.g.dart';
|
||||
|
||||
@JsonSerializable()
|
||||
class ImageModelDto {
|
||||
const ImageModelDto({
|
||||
required this.uri,
|
||||
required this.imageIndex,
|
||||
required this.imageName,
|
||||
});
|
||||
|
||||
/// An image's target [Uri].
|
||||
///
|
||||
/// Storing an image's [ByteData] is more expensive, memory-wise.
|
||||
final Uri uri;
|
||||
|
||||
/// A unique identifier that can be used for indexing the image.
|
||||
final int imageIndex;
|
||||
|
||||
/// Given name of the image.
|
||||
final String imageName;
|
||||
|
||||
factory ImageModelDto.fromJson(Map<String, dynamic> json) => _$ImageModelDtoFromJson(json);
|
||||
|
||||
/// Connect the generated [_$PersonToJson] function to the `toJson` method.
|
||||
Map<String, dynamic> toJson() => _$ImageModelDtoToJson(this);
|
||||
}
|
|
@ -1,18 +1,19 @@
|
|||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'image_model.dart';
|
||||
part of 'image_model_dto.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
ImageModel _$ImageModelFromJson(Map<String, dynamic> json) => ImageModel(
|
||||
ImageModelDto _$ImageModelDtoFromJson(Map<String, dynamic> json) =>
|
||||
ImageModelDto(
|
||||
uri: Uri.parse(json['uri'] as String),
|
||||
imageIndex: json['imageIndex'] as int,
|
||||
imageName: json['imageName'] as String,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$ImageModelToJson(ImageModel instance) =>
|
||||
Map<String, dynamic> _$ImageModelDtoToJson(ImageModelDto instance) =>
|
||||
<String, dynamic>{
|
||||
'uri': instance.uri.toString(),
|
||||
'imageIndex': instance.imageIndex,
|
|
@ -1,13 +1,12 @@
|
|||
import 'package:json_annotation/json_annotation.dart';
|
||||
|
||||
part 'image_model.g.dart';
|
||||
|
||||
@JsonSerializable()
|
||||
class ImageModel {
|
||||
const ImageModel({
|
||||
class ImageModelDto {
|
||||
const ImageModelDto({
|
||||
required this.uri,
|
||||
required this.imageIndex,
|
||||
required this.imageName,
|
||||
required this.isFavourite,
|
||||
});
|
||||
|
||||
/// An image's target [Uri].
|
||||
|
@ -21,8 +20,6 @@ class ImageModel {
|
|||
/// Given name of the image.
|
||||
final String imageName;
|
||||
|
||||
factory ImageModel.fromJson(Map<String, dynamic> json) => _$ImageModelFromJson(json);
|
||||
|
||||
/// Connect the generated [_$PersonToJson] function to the `toJson` method.
|
||||
Map<String, dynamic> toJson() => _$ImageModelToJson(this);
|
||||
/// Whether the image was 'Starred' ot not.
|
||||
final bool isFavourite;
|
||||
}
|
||||
|
|
|
@ -1,16 +1,15 @@
|
|||
import 'dart:async';
|
||||
|
||||
import 'package:mc_gallery/features/core/data/extensions/string_extensions.dart';
|
||||
|
||||
import '/features/core/data/constants/const_sorters.dart';
|
||||
import '/features/core/data/extensions/iterable_extensions.dart';
|
||||
import '/features/core/data/extensions/map_extensions.dart';
|
||||
import '/features/core/data/extensions/string_extensions.dart';
|
||||
import '/features/core/services/logging_service.dart';
|
||||
import '/features/core/utils/mutex.dart';
|
||||
import '/locator.dart';
|
||||
import '../abstracts/images_api.dart';
|
||||
import '../data/dtos/image_model_dto.dart';
|
||||
import '../data/enums/search_option.dart';
|
||||
import '../data/models/image_model.dart';
|
||||
|
||||
/// Handles fetching and storing of Images.
|
||||
///
|
||||
|
@ -28,8 +27,8 @@ class ImagesService {
|
|||
final ImagesApi _imagesApi;
|
||||
final LoggingService _loggingService;
|
||||
|
||||
late final Map<String, ImageModel> _imageModels;
|
||||
Iterable<ImageModel> get imageModels => _imageModels.values.deepCopy;
|
||||
late final Map<String, ImageModelDto> _imageModels;
|
||||
Iterable<ImageModelDto> get imageModels => _imageModels.values.deepCopy;
|
||||
|
||||
final Mutex _searchMutex = Mutex();
|
||||
|
||||
|
@ -43,7 +42,7 @@ class ImagesService {
|
|||
_loggingService.info('Fetching and creating image models...');
|
||||
_imageModels = {
|
||||
for (final imageModel in (await _imagesApi.fetchImageUri(token: ''))
|
||||
.map((final emulatedModelSerialized) => ImageModel.fromJson(emulatedModelSerialized)))
|
||||
.map((final emulatedModelSerialized) => ImageModelDto.fromJson(emulatedModelSerialized)))
|
||||
imageModel.imageName: imageModel
|
||||
};
|
||||
|
||||
|
@ -58,7 +57,7 @@ class ImagesService {
|
|||
int get lastAvailableImageIndex => _imageModels.length - 1;
|
||||
int get numberOfImages => _imageModels.length;
|
||||
|
||||
ImageModel imageModelAt({required int index}) => _imageModels.values.elementAt(index);
|
||||
ImageModelDto imageModelAt({required int index}) => _imageModels.values.elementAt(index);
|
||||
|
||||
Future<void> get lastQueryIsCompleted => _searchMutex.lastOperationCompletionAwaiter;
|
||||
|
||||
|
@ -70,7 +69,7 @@ class ImagesService {
|
|||
/// There are lots of optimizations possible for new inputs, for example reducing search frontier
|
||||
/// by using set-cover/subsetting optimizations on backspace, and so on, but again, not the point,
|
||||
/// I think.
|
||||
Future<List<ImageModel>> searchImages({
|
||||
Future<List<ImageModelDto>> searchImages({
|
||||
required SearchOption searchOption,
|
||||
required String imageNamePart,
|
||||
bool treatAsInSequence = false,
|
||||
|
@ -89,14 +88,18 @@ class ImagesService {
|
|||
..sort((final a, final b) =>
|
||||
ConstSorters.stringsSimilarityTarget(targetWord: imageNamePart, a, b))
|
||||
..reversed;
|
||||
|
||||
return _imageModels.valuesByKeys(keys: rankedKeys).toList(growable: false);
|
||||
|
||||
case SearchOption.web:
|
||||
return (await _imagesApi.searchImages(
|
||||
searchStr: imageNamePart,
|
||||
token: '',
|
||||
))
|
||||
.map(
|
||||
(final emulatedModelSerialized) => ImageModel.fromJson(emulatedModelSerialized))
|
||||
(final emulatedModelSerialized) =>
|
||||
ImageModelDto.fromJson(emulatedModelSerialized),
|
||||
)
|
||||
.toList(growable: false);
|
||||
}
|
||||
} finally {
|
||||
|
|
|
@ -7,8 +7,8 @@ import '/features/core/widgets/gap.dart';
|
|||
import '/features/core/widgets/mcg_scaffold.dart';
|
||||
import '/features/core/widgets/state/multi_value_listenable_builder.dart';
|
||||
import '/features/core/widgets/state/view_model_builder.dart';
|
||||
import '../../data/dtos/image_model_dto.dart';
|
||||
import '../../data/enums/search_option.dart';
|
||||
import '../../data/models/image_model.dart';
|
||||
import 'gallery_view_model.dart';
|
||||
|
||||
part 'downloaded_gallery_view.dart';
|
||||
|
|
|
@ -8,8 +8,8 @@ import '/features/core/abstracts/base_view_model.dart';
|
|||
import '/features/core/services/logging_service.dart';
|
||||
import '/features/core/services/navigation_service.dart';
|
||||
import '/locator.dart';
|
||||
import '../../data/dtos/image_model_dto.dart';
|
||||
import '../../data/enums/search_option.dart';
|
||||
import '../../data/models/image_model.dart';
|
||||
import '../../services/image_cache_manager_service.dart';
|
||||
import '../../services/images_service.dart';
|
||||
import '../image_carousel/image_carousel_view.dart';
|
||||
|
@ -37,8 +37,9 @@ class GalleryViewModel extends BaseViewModel {
|
|||
ValueListenable<bool> get isSearchingListenable => _isSearchingNotifier;
|
||||
final ValueNotifier<SearchOption> _searchOptionNotifier = ValueNotifier(SearchOption.web);
|
||||
ValueListenable<SearchOption> get searchOptionListenable => _searchOptionNotifier;
|
||||
final ValueNotifier<List<ImageModel>> _imageSearchResultsNotifier = ValueNotifier([]);
|
||||
ValueListenable<List<ImageModel>> get imageSearchResultsListenable => _imageSearchResultsNotifier;
|
||||
final ValueNotifier<List<ImageModelDto>> _imageSearchResultsNotifier = ValueNotifier([]);
|
||||
ValueListenable<List<ImageModelDto>> get imageSearchResultsListenable =>
|
||||
_imageSearchResultsNotifier;
|
||||
|
||||
@override
|
||||
Future<void> initialise(bool Function() mounted, [arguments]) async {
|
||||
|
@ -97,13 +98,13 @@ class GalleryViewModel extends BaseViewModel {
|
|||
|
||||
void onPromptPressed() => _isDisplayingPressingPrompt.value = false;
|
||||
|
||||
Iterable<ImageModel> get imageModels => _imagesService.imageModels;
|
||||
Iterable<ImageModelDto> get imageModels => _imagesService.imageModels;
|
||||
Future<void> get initImageFetchIsDone => _imagesService.initAwaiter;
|
||||
|
||||
double? downloadProgressValue({required DownloadProgress progress}) =>
|
||||
progress.totalSize != null ? progress.downloaded / progress.totalSize! : null;
|
||||
|
||||
void pushImageCarouselView(BuildContext context, {required ImageModel imageModel}) =>
|
||||
void pushImageCarouselView(BuildContext context, {required ImageModelDto imageModel}) =>
|
||||
_navigationService.pushImageCarouselView(
|
||||
context,
|
||||
imageCarouselViewArguments: ImageCarouselViewArguments(
|
||||
|
|
|
@ -10,7 +10,7 @@ class _SearchGalleryView extends StatelessWidget {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ValueListenableBuilder<List<ImageModel>>(
|
||||
return ValueListenableBuilder<List<ImageModelDto>>(
|
||||
valueListenable: galleryViewModel.imageSearchResultsListenable,
|
||||
builder: (context, final resultsImageModels, _) => FutureBuilder(
|
||||
future: galleryViewModel.lastQueryResultDone,
|
||||
|
|
|
@ -3,7 +3,7 @@ import 'package:cached_network_image/cached_network_image.dart';
|
|||
import 'package:carousel_slider/carousel_slider.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_markdown/flutter_markdown.dart';
|
||||
import 'package:mc_gallery/features/home/data/models/image_model.dart';
|
||||
import 'package:mc_gallery/features/home/data/dtos/image_model_dto.dart';
|
||||
|
||||
import '/features/core/data/constants/const_colors.dart';
|
||||
import '/features/core/data/constants/const_text.dart';
|
||||
|
@ -59,7 +59,7 @@ class ImageCarouselView extends StatelessWidget {
|
|||
itemBuilder: (context, _, __) => Stack(
|
||||
fit: StackFit.expand,
|
||||
children: [
|
||||
ValueListenableBuilder<ImageModel>(
|
||||
ValueListenableBuilder<ImageModelDto>(
|
||||
valueListenable: model.currentImageModelListenable,
|
||||
builder: (context, _, __) => CachedNetworkImage(
|
||||
imageUrl: model.currentImageUrl,
|
||||
|
@ -71,7 +71,7 @@ class ImageCarouselView extends StatelessWidget {
|
|||
),
|
||||
),
|
||||
),
|
||||
ValueListenableBuilder<ImageModel>(
|
||||
ValueListenableBuilder<ImageModelDto>(
|
||||
valueListenable: model.currentImageModelListenable,
|
||||
builder: (context, _, __) => Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
|
|
|
@ -4,10 +4,10 @@ import 'package:flutter_cache_manager/flutter_cache_manager.dart';
|
|||
import '/features/core/abstracts/base_view_model.dart';
|
||||
import '/features/core/services/logging_service.dart';
|
||||
import '/features/core/services/navigation_service.dart';
|
||||
import '/features/home/data/models/image_model.dart';
|
||||
import '/features/home/services/images_service.dart';
|
||||
import '/features/home/views/image_carousel/image_carousel_view.dart';
|
||||
import '/locator.dart';
|
||||
import '../../data/dtos/image_model_dto.dart';
|
||||
|
||||
class ImageCarouselViewModel extends BaseViewModel {
|
||||
ImageCarouselViewModel({
|
||||
|
@ -22,8 +22,8 @@ class ImageCarouselViewModel extends BaseViewModel {
|
|||
final NavigationService _navigationService;
|
||||
final LoggingService _loggingService;
|
||||
|
||||
late final ValueNotifier<ImageModel> _currentImageModelNotifier;
|
||||
ValueListenable<ImageModel> get currentImageModelListenable => _currentImageModelNotifier;
|
||||
late final ValueNotifier<ImageModelDto> _currentImageModelNotifier;
|
||||
ValueListenable<ImageModelDto> get currentImageModelListenable => _currentImageModelNotifier;
|
||||
|
||||
@override
|
||||
Future<void> initialise(bool Function() mounted, [arguments]) async {
|
||||
|
|
Loading…
Reference in a new issue