From 2d07d535b5e99abca649bb17d87797685bf8af9b Mon Sep 17 00:00:00 2001 From: Marty Fuhry Date: Mon, 29 Jan 2024 11:12:19 -0500 Subject: [PATCH 1/2] Fixes an issue with current index and stack index rebuilding twice --- .../asset_viewer/models/asset_index.dart | 13 +++ .../asset_viewer/ui/stacked_children.dart | 62 +++++++++++++ .../asset_viewer/views/gallery_viewer.dart | 90 ++++++------------- 3 files changed, 102 insertions(+), 63 deletions(-) create mode 100644 mobile/lib/modules/asset_viewer/models/asset_index.dart create mode 100644 mobile/lib/modules/asset_viewer/ui/stacked_children.dart diff --git a/mobile/lib/modules/asset_viewer/models/asset_index.dart b/mobile/lib/modules/asset_viewer/models/asset_index.dart new file mode 100644 index 0000000000000..820be7840a7b4 --- /dev/null +++ b/mobile/lib/modules/asset_viewer/models/asset_index.dart @@ -0,0 +1,13 @@ +class AssetIndex { + final int currentIndex; + final int stackIndex; + + AssetIndex({required this.currentIndex, this.stackIndex = 0}); + + AssetIndex copyWith({int? currentIndex, int? stackIndex}) { + return AssetIndex( + currentIndex: currentIndex ?? this.currentIndex, + stackIndex: stackIndex ?? this.stackIndex, + ); + } +} diff --git a/mobile/lib/modules/asset_viewer/ui/stacked_children.dart b/mobile/lib/modules/asset_viewer/ui/stacked_children.dart new file mode 100644 index 0000000000000..fc164f779717f --- /dev/null +++ b/mobile/lib/modules/asset_viewer/ui/stacked_children.dart @@ -0,0 +1,62 @@ +import 'package:cached_network_image/cached_network_image.dart'; +import 'package:flutter/material.dart'; +import 'package:immich_mobile/shared/models/asset.dart'; +import 'package:immich_mobile/shared/models/store.dart'; + +class StackedChildren extends StatelessWidget { + final int stackIndex; + final List stackElements; + final Function(int)? onTap; + + const StackedChildren({ + super.key, + required this.stackIndex, + required this.stackElements, + this.onTap, + }); + + @override + Widget build(BuildContext context) { + return ListView.builder( + shrinkWrap: true, + scrollDirection: Axis.horizontal, + itemCount: stackElements.length, + itemBuilder: (context, i) { + final assetId = stackElements.elementAt(i).remoteId; + return Padding( + padding: const EdgeInsets.only(right: 10), + child: GestureDetector( + onTap: () => onTap?.call(i), + child: Container( + width: 40, + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(6), + border: (stackIndex == -1 && i == 0) || i == stackIndex + ? Border.all( + color: Colors.white, + width: 2, + ) + : null, + ), + child: ClipRRect( + borderRadius: BorderRadius.circular(4), + child: CachedNetworkImage( + fit: BoxFit.cover, + imageUrl: + '${Store.get(StoreKey.serverEndpoint)}/asset/thumbnail/$assetId', + httpHeaders: { + "Authorization": + "Bearer ${Store.get(StoreKey.accessToken)}", + }, + errorWidget: (context, url, error) => + const Icon(Icons.image_not_supported_outlined), + ), + ), + ), + ), + ); + }, + ); + } +} diff --git a/mobile/lib/modules/asset_viewer/views/gallery_viewer.dart b/mobile/lib/modules/asset_viewer/views/gallery_viewer.dart index 1903a7f19c01f..78cc27b8fda4c 100644 --- a/mobile/lib/modules/asset_viewer/views/gallery_viewer.dart +++ b/mobile/lib/modules/asset_viewer/views/gallery_viewer.dart @@ -10,6 +10,7 @@ import 'package:fluttertoast/fluttertoast.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/modules/album/providers/current_album.provider.dart'; +import 'package:immich_mobile/modules/asset_viewer/models/asset_index.dart'; import 'package:immich_mobile/modules/asset_viewer/providers/asset_stack.provider.dart'; import 'package:immich_mobile/modules/asset_viewer/providers/current_asset.provider.dart'; import 'package:immich_mobile/modules/asset_viewer/providers/show_controls.provider.dart'; @@ -20,6 +21,7 @@ import 'package:immich_mobile/modules/asset_viewer/providers/video_player_value_ import 'package:immich_mobile/modules/asset_viewer/services/asset_stack.service.dart'; import 'package:immich_mobile/modules/asset_viewer/ui/advanced_bottom_sheet.dart'; import 'package:immich_mobile/modules/asset_viewer/ui/exif_bottom_sheet.dart'; +import 'package:immich_mobile/modules/asset_viewer/ui/stacked_children.dart'; import 'package:immich_mobile/modules/asset_viewer/ui/top_control_app_bar.dart'; import 'package:immich_mobile/modules/asset_viewer/views/video_viewer_page.dart'; import 'package:immich_mobile/modules/backup/providers/manual_upload.provider.dart'; @@ -78,16 +80,19 @@ class GalleryViewerPage extends HookConsumerWidget { final isPlayingMotionVideo = useState(false); final isPlayingVideo = useState(false); Offset? localPosition; - final header = {"x-immich-user-token": Store.get(StoreKey.accessToken)}; - final currentIndex = useState(initialIndex); - final currentAsset = loadAsset(currentIndex.value); + final authToken = 'Bearer ${Store.get(StoreKey.accessToken)}'; + final header = {"Authorization": authToken}; final isTrashEnabled = ref.watch(serverInfoProvider.select((v) => v.serverFeatures.trash)); final navStack = AutoRouter.of(context).stackData; final isFromTrash = isTrashEnabled && navStack.length > 2 && navStack.elementAt(navStack.length - 2).name == TrashRoute.name; - final stackIndex = useState(-1); + final index = useState(AssetIndex(currentIndex: initialIndex)); + final stackIndex = index.value.stackIndex; + final currentIndex = index.value.currentIndex; + final currentAsset = loadAsset(currentIndex); + final stack = showStack && currentAsset.stackChildrenCount > 0 ? ref.watch(assetStackStateProvider(currentAsset)) : []; @@ -96,16 +101,15 @@ class GalleryViewerPage extends HookConsumerWidget { final isFromDto = currentAsset.id == Isar.autoIncrement; final album = ref.watch(currentAlbumProvider); - Asset asset() => stackIndex.value == -1 - ? currentAsset - : stackElements.elementAt(stackIndex.value); + Asset asset() => + stackIndex == -1 ? currentAsset : stackElements.elementAt(stackIndex); final isOwner = asset().ownerId == ref.watch(currentUserProvider)?.isarId; final isPartner = ref .watch(partnerSharedWithProvider) .map((e) => e.isarId) .contains(asset().ownerId); - bool isParent = stackIndex.value == -1 || stackIndex.value == 0; + bool isParent = stackIndex == -1 || stackIndex == 0; // Listen provider to prevent autoDispose when navigating to other routes from within the gallery page ref.listen(currentAssetProvider, (_, __) {}); @@ -211,11 +215,11 @@ class GalleryViewerPage extends HookConsumerWidget { } void removeAssetFromStack() { - if (stackIndex.value > 0 && showStack) { + if (stackIndex > 0 && showStack) { ref .read(assetStackStateProvider(currentAsset).notifier) - .removeChild(stackIndex.value - 1); - stackIndex.value = stackIndex.value - 1; + .removeChild(stackIndex - 1); + index.value = index.value.copyWith(stackIndex: stackIndex - 1); } } @@ -492,50 +496,6 @@ class GalleryViewerPage extends HookConsumerWidget { ); } - Widget buildStackedChildren() { - return ListView.builder( - shrinkWrap: true, - scrollDirection: Axis.horizontal, - itemCount: stackElements.length, - itemBuilder: (context, index) { - final assetId = stackElements.elementAt(index).remoteId; - return Padding( - padding: const EdgeInsets.only(right: 10), - child: GestureDetector( - onTap: () => stackIndex.value = index, - child: Container( - width: 40, - decoration: BoxDecoration( - color: Colors.white, - borderRadius: BorderRadius.circular(6), - border: (stackIndex.value == -1 && index == 0) || - index == stackIndex.value - ? Border.all( - color: Colors.white, - width: 2, - ) - : null, - ), - child: ClipRRect( - borderRadius: BorderRadius.circular(4), - child: CachedNetworkImage( - fit: BoxFit.cover, - imageUrl: - '${Store.get(StoreKey.serverEndpoint)}/asset/thumbnail/$assetId', - httpHeaders: { - "x-immich-user-token": Store.get(StoreKey.accessToken), - }, - errorWidget: (context, url, error) => - const Icon(Icons.image_not_supported_outlined), - ), - ), - ), - ), - ); - }, - ); - } - void showStackActionItems() { showModalBottomSheet( context: context, @@ -558,7 +518,7 @@ class GalleryViewerPage extends HookConsumerWidget { .read(assetStackServiceProvider) .updateStackParent( currentAsset, - stackElements.elementAt(stackIndex.value), + stackElements.elementAt(stackIndex), ); ctx.pop(); context.popRoute(); @@ -593,7 +553,7 @@ class GalleryViewerPage extends HookConsumerWidget { await ref.read(assetStackServiceProvider).updateStack( currentAsset, childrenToRemove: [ - stackElements.elementAt(stackIndex.value), + stackElements.elementAt(stackIndex), ], ); removeAssetFromStack(); @@ -697,7 +657,12 @@ class GalleryViewerPage extends HookConsumerWidget { ), child: SizedBox( height: 40, - child: buildStackedChildren(), + child: StackedChildren( + stackIndex: stackIndex, + stackElements: stackElements, + onTap: (i) => + index.value = index.value.copyWith(stackIndex: i), + ), ), ), Visibility( @@ -775,10 +740,10 @@ class GalleryViewerPage extends HookConsumerWidget { itemCount: totalAssets, scrollDirection: Axis.horizontal, onPageChanged: (value) { - final next = currentIndex.value < value ? value + 1 : value - 1; + final next = currentIndex < value ? value + 1 : value - 1; precacheNextImage(next); - currentIndex.value = value; - stackIndex.value = -1; + index.value = index.value + .copyWith(currentIndex: value, stackIndex: - 1); HapticFeedback.selectionClick(); }, loadingBuilder: (context, event, index) { @@ -819,8 +784,7 @@ class GalleryViewerPage extends HookConsumerWidget { : webPThumbnail; }, builder: (context, index) { - final a = - index == currentIndex.value ? asset() : loadAsset(index); + final a = index == currentIndex ? asset() : loadAsset(index); final ImageProvider provider = finalImageProvider(a); if (a.isImage && !isPlayingMotionVideo.value) { From a11bfe58012a6f5bd0d71b599e2f4aa4589c4443 Mon Sep 17 00:00:00 2001 From: Marty Fuhry Date: Mon, 29 Jan 2024 11:20:28 -0500 Subject: [PATCH 2/2] Adds comments and fixes video viewer size --- mobile/lib/modules/asset_viewer/ui/stacked_children.dart | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/mobile/lib/modules/asset_viewer/ui/stacked_children.dart b/mobile/lib/modules/asset_viewer/ui/stacked_children.dart index fc164f779717f..17a88d30e3077 100644 --- a/mobile/lib/modules/asset_viewer/ui/stacked_children.dart +++ b/mobile/lib/modules/asset_viewer/ui/stacked_children.dart @@ -3,9 +3,16 @@ import 'package:flutter/material.dart'; import 'package:immich_mobile/shared/models/asset.dart'; import 'package:immich_mobile/shared/models/store.dart'; +/// The stacked children assets for the gallyer viewer page class StackedChildren extends StatelessWidget { + + /// The stack index final int stackIndex; + + /// The elements in this stack final List stackElements; + + /// A callback function to get the stack index of the tapped element final Function(int)? onTap; const StackedChildren({