Skip to content

Commit

Permalink
fix(mobile): asset state remain in gallery view after being deleted (i…
Browse files Browse the repository at this point in the history
…mmich-app#10603)

* fix(mobile): asset doesn't get removed from state renderList

* fix delete last assets

* refactor
  • Loading branch information
alextran1502 committed Jun 27, 2024
1 parent 922430d commit d8175d8
Show file tree
Hide file tree
Showing 8 changed files with 158 additions and 140 deletions.
36 changes: 23 additions & 13 deletions mobile/lib/pages/common/gallery_viewer.page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import 'package:immich_mobile/providers/asset_viewer/video_player_value_provider
import 'package:immich_mobile/providers/haptic_feedback.provider.dart';
import 'package:immich_mobile/providers/image/immich_remote_image_provider.dart';
import 'package:immich_mobile/services/app_settings.service.dart';
import 'package:immich_mobile/widgets/asset_grid/asset_grid_data_structure.dart';
import 'package:immich_mobile/widgets/asset_viewer/advanced_bottom_sheet.dart';
import 'package:immich_mobile/widgets/asset_viewer/bottom_gallery_bar.dart';
import 'package:immich_mobile/widgets/asset_viewer/exif_sheet/exif_bottom_sheet.dart';
Expand All @@ -34,17 +35,15 @@ import 'package:isar/isar.dart';
@RoutePage()
// ignore: must_be_immutable
class GalleryViewerPage extends HookConsumerWidget {
final Asset Function(int index) loadAsset;
final int totalAssets;
final int initialIndex;
final int heroOffset;
final bool showStack;
final RenderList renderList;

GalleryViewerPage({
super.key,
required this.initialIndex,
required this.loadAsset,
required this.totalAssets,
required this.renderList,
this.initialIndex = 0,
this.heroOffset = 0,
this.showStack = false,
}) : controller = PageController(initialPage: initialIndex);
Expand All @@ -54,6 +53,8 @@ class GalleryViewerPage extends HookConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final settings = ref.watch(appSettingsServiceProvider);
final loadAsset = renderList.loadAsset;
final totalAssets = useState(renderList.totalAssets);
final isLoadPreview = useState(AppSettingsEnum.loadPreview.defaultValue);
final isLoadOriginal = useState(AppSettingsEnum.loadOriginal.defaultValue);
final shouldLoopVideo = useState(AppSettingsEnum.loopVideo.defaultValue);
Expand All @@ -62,6 +63,7 @@ class GalleryViewerPage extends HookConsumerWidget {
final localPosition = useState<Offset?>(null);
final currentIndex = useState(initialIndex);
final currentAsset = loadAsset(currentIndex.value);

// Update is playing motion video
ref.listen(videoPlaybackValueProvider.select((v) => v.state), (_, state) {
isPlayingVideo.value = state == VideoPlaybackState.playing;
Expand Down Expand Up @@ -112,13 +114,19 @@ class GalleryViewerPage extends HookConsumerWidget {
debugPrint('Error precaching next image: $exception, $stackTrace');
}

if (index < totalAssets && index >= 0) {
final asset = loadAsset(index);
await precacheImage(
ImmichImage.imageProvider(asset: asset),
context,
onError: onError,
);
try {
if (index < totalAssets.value && index >= 0) {
final asset = loadAsset(index);
await precacheImage(
ImmichImage.imageProvider(asset: asset),
context,
onError: onError,
);
}
} catch (e) {
// swallow error silently
debugPrint('Error precaching next image: $e');
context.maybePop();
}
}

Expand Down Expand Up @@ -297,7 +305,7 @@ class GalleryViewerPage extends HookConsumerWidget {
? const ScrollPhysics() // Use bouncing physics for iOS
: const ClampingScrollPhysics() // Use heavy physics for Android
),
itemCount: totalAssets,
itemCount: totalAssets.value,
scrollDirection: Axis.horizontal,
onPageChanged: (value) async {
final next = currentIndex.value < value ? value + 1 : value - 1;
Expand Down Expand Up @@ -406,11 +414,13 @@ class GalleryViewerPage extends HookConsumerWidget {
),
),
BottomGalleryBar(
renderList: renderList,
totalAssets: totalAssets,
controller: controller,
showStack: showStack,
stackIndex: stackIndex.value,
asset: asset,
assetIndex: currentIndex.value,
showVideoPlayerControls: !asset.isImage && !isMotionPhoto,
),
],
Expand Down
10 changes: 8 additions & 2 deletions mobile/lib/pages/search/map/map.page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import 'package:immich_mobile/models/map/map_marker.model.dart';
import 'package:immich_mobile/providers/map/map_marker.provider.dart';
import 'package:immich_mobile/providers/map/map_state.provider.dart';
import 'package:immich_mobile/utils/map_utils.dart';
import 'package:immich_mobile/widgets/asset_grid/asset_grid_data_structure.dart';
import 'package:immich_mobile/widgets/map/map_app_bar.dart';
import 'package:immich_mobile/widgets/map/map_asset_grid.dart';
import 'package:immich_mobile/widgets/map/map_bottom_sheet.dart';
Expand Down Expand Up @@ -178,12 +179,17 @@ class MapPage extends HookConsumerWidget {
return;
}

// Since we only have a single asset, we can just show GroupAssetBy.none
final renderList = await RenderList.fromAssets(
[asset],
GroupAssetsBy.none,
);

context.pushRoute(
GalleryViewerRoute(
initialIndex: 0,
loadAsset: (index) => asset,
totalAssets: 1,
heroOffset: 0,
renderList: renderList,
),
);
}
Expand Down
1 change: 1 addition & 0 deletions mobile/lib/routing/router.dart
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ import 'package:immich_mobile/routing/backup_permission_guard.dart';
import 'package:immich_mobile/routing/custom_transition_builders.dart';
import 'package:immich_mobile/routing/duplicate_guard.dart';
import 'package:immich_mobile/services/api.service.dart';
import 'package:immich_mobile/widgets/asset_grid/asset_grid_data_structure.dart';
import 'package:isar/isar.dart';
import 'package:maplibre_gl/maplibre_gl.dart';
import 'package:photo_manager/photo_manager.dart' hide LatLng;
Expand Down
24 changes: 9 additions & 15 deletions mobile/lib/routing/router.gr.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions mobile/lib/widgets/asset_grid/asset_grid_data_structure.dart
Original file line number Diff line number Diff line change
Expand Up @@ -311,4 +311,12 @@ class RenderList {
GroupAssetsBy groupBy,
) =>
_buildRenderList(assets, null, groupBy);

/// Deletes an asset from the render list and clears the buffer
/// This is only a workaround for deleted images still appearing in the gallery
void deleteAsset(Asset deleteAsset) {
allAssets?.remove(deleteAsset);
_buf.clear();
_bufOffset = 0;
}
}
52 changes: 37 additions & 15 deletions mobile/lib/widgets/asset_grid/immich_asset_grid_view.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@ import 'dart:collection';
import 'dart:developer';
import 'dart:math';

import 'package:auto_route/auto_route.dart';
import 'package:collection/collection.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/services.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/extensions/build_context_extensions.dart';
import 'package:immich_mobile/extensions/collection_extensions.dart';
Expand Down Expand Up @@ -815,28 +817,48 @@ class _AssetRow extends StatelessWidget {
key: key,
children: assets.mapIndexed((int index, Asset asset) {
final bool last = index + 1 == assetsPerRow;
final isSelected = isSelectionActive && selectedAssets.contains(asset);
return Container(
width: width * widthDistribution[index],
height: width,
margin: EdgeInsets.only(
bottom: margin,
right: last ? 0.0 : margin,
),
child: AssetIndexWrapper(
rowIndex: rowStartIndex + index,
sectionIndex: sectionIndex,
child: ThumbnailImage(
asset: asset,
index: absoluteOffset + index,
loadAsset: renderList.loadAsset,
totalAssets: renderList.totalAssets,
multiselectEnabled: selectionActive,
isSelected: isSelectionActive && selectedAssets.contains(asset),
onSelect: () => onSelect?.call(asset),
onDeselect: () => onDeselect?.call(asset),
showStorageIndicator: showStorageIndicator,
heroOffset: heroOffset,
showStack: showStack,
child: GestureDetector(
onTap: () {
if (selectionActive) {
if (isSelected) {
onDeselect?.call(asset);
} else {
onSelect?.call(asset);
}
} else {
context.pushRoute(
GalleryViewerRoute(
renderList: renderList,
initialIndex: absoluteOffset + index,
heroOffset: heroOffset,
showStack: showStack,
),
);
}
},
onLongPress: () {
onSelect?.call(asset);
HapticFeedback.heavyImpact();
},
child: AssetIndexWrapper(
rowIndex: rowStartIndex + index,
sectionIndex: sectionIndex,
child: ThumbnailImage(
asset: asset,
multiselectEnabled: selectionActive,
isSelected: isSelectionActive && selectedAssets.contains(asset),
showStorageIndicator: showStorageIndicator,
heroOffset: heroOffset,
showStack: showStack,
),
),
),
);
Expand Down
Loading

0 comments on commit d8175d8

Please sign in to comment.