mirror of
https://github.com/immich-app/immich
synced 2025-11-07 17:27:20 +00:00
fix(mobile): asset state remain in gallery view after being deleted (#10603)
* fix(mobile): asset doesn't get removed from state renderList * fix delete last assets * refactor
This commit is contained in:
parent
922430da36
commit
d8175d8da8
8 changed files with 158 additions and 140 deletions
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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';
|
||||
|
|
@ -815,6 +817,7 @@ 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,
|
||||
|
|
@ -822,21 +825,40 @@ class _AssetRow extends StatelessWidget {
|
|||
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,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,40 +1,42 @@
|
|||
import 'package:auto_route/auto_route.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
||||
import 'package:immich_mobile/routing/router.dart';
|
||||
import 'package:immich_mobile/entities/asset.entity.dart';
|
||||
import 'package:immich_mobile/providers/haptic_feedback.provider.dart';
|
||||
import 'package:immich_mobile/widgets/common/immich_thumbnail.dart';
|
||||
import 'package:immich_mobile/utils/storage_indicator.dart';
|
||||
import 'package:isar/isar.dart';
|
||||
|
||||
class ThumbnailImage extends ConsumerWidget {
|
||||
/// The asset to show the thumbnail image for
|
||||
final Asset asset;
|
||||
final int index;
|
||||
final Asset Function(int index) loadAsset;
|
||||
final int totalAssets;
|
||||
|
||||
/// Whether to show the storage indicator icont over the image or not
|
||||
final bool showStorageIndicator;
|
||||
|
||||
/// Whether to show the show stack icon over the image or not
|
||||
final bool showStack;
|
||||
|
||||
/// Whether to show the checkmark indicating that this image is selected
|
||||
final bool isSelected;
|
||||
|
||||
/// Can override [isSelected] and never show the selection indicator
|
||||
final bool multiselectEnabled;
|
||||
final Function? onSelect;
|
||||
final Function? onDeselect;
|
||||
|
||||
/// If we are allowed to deselect this image
|
||||
final bool canDeselect;
|
||||
|
||||
/// The offset index to apply to this hero tag for animation
|
||||
final int heroOffset;
|
||||
|
||||
const ThumbnailImage({
|
||||
super.key,
|
||||
required this.asset,
|
||||
required this.index,
|
||||
required this.loadAsset,
|
||||
required this.totalAssets,
|
||||
this.showStorageIndicator = true,
|
||||
this.showStack = false,
|
||||
this.isSelected = false,
|
||||
this.multiselectEnabled = false,
|
||||
this.onDeselect,
|
||||
this.onSelect,
|
||||
this.heroOffset = 0,
|
||||
this.canDeselect = true,
|
||||
});
|
||||
|
||||
@override
|
||||
|
|
@ -147,11 +149,7 @@ class ThumbnailImage extends ConsumerWidget {
|
|||
}
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(
|
||||
width: 0,
|
||||
color: onDeselect == null ? Colors.grey : assetContainerColor,
|
||||
),
|
||||
color: onDeselect == null ? Colors.grey : assetContainerColor,
|
||||
color: canDeselect ? assetContainerColor : Colors.grey,
|
||||
),
|
||||
child: ClipRRect(
|
||||
borderRadius: const BorderRadius.only(
|
||||
|
|
@ -165,79 +163,52 @@ class ThumbnailImage extends ConsumerWidget {
|
|||
);
|
||||
}
|
||||
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
if (multiselectEnabled) {
|
||||
if (isSelected) {
|
||||
onDeselect?.call();
|
||||
} else {
|
||||
onSelect?.call();
|
||||
}
|
||||
} else {
|
||||
context.pushRoute(
|
||||
GalleryViewerRoute(
|
||||
initialIndex: index,
|
||||
loadAsset: loadAsset,
|
||||
totalAssets: totalAssets,
|
||||
heroOffset: heroOffset,
|
||||
showStack: showStack,
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
onLongPress: () {
|
||||
onSelect?.call();
|
||||
ref.read(hapticFeedbackProvider.notifier).heavyImpact();
|
||||
},
|
||||
child: Stack(
|
||||
children: [
|
||||
AnimatedContainer(
|
||||
duration: const Duration(milliseconds: 300),
|
||||
curve: Curves.decelerate,
|
||||
decoration: BoxDecoration(
|
||||
border: multiselectEnabled && isSelected
|
||||
? Border.all(
|
||||
color: onDeselect == null
|
||||
? Colors.grey
|
||||
: assetContainerColor,
|
||||
width: 8,
|
||||
)
|
||||
: const Border(),
|
||||
),
|
||||
child: buildImage(),
|
||||
return Stack(
|
||||
children: [
|
||||
AnimatedContainer(
|
||||
duration: const Duration(milliseconds: 300),
|
||||
curve: Curves.decelerate,
|
||||
decoration: BoxDecoration(
|
||||
border: multiselectEnabled && isSelected
|
||||
? Border.all(
|
||||
color: canDeselect ? assetContainerColor : Colors.grey,
|
||||
width: 8,
|
||||
)
|
||||
: const Border(),
|
||||
),
|
||||
if (multiselectEnabled)
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(3.0),
|
||||
child: Align(
|
||||
alignment: Alignment.topLeft,
|
||||
child: buildSelectionIcon(asset),
|
||||
),
|
||||
child: buildImage(),
|
||||
),
|
||||
if (multiselectEnabled)
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(3.0),
|
||||
child: Align(
|
||||
alignment: Alignment.topLeft,
|
||||
child: buildSelectionIcon(asset),
|
||||
),
|
||||
if (showStorageIndicator)
|
||||
Positioned(
|
||||
right: 8,
|
||||
bottom: 5,
|
||||
child: Icon(
|
||||
storageIcon(asset),
|
||||
color: Colors.white,
|
||||
size: 18,
|
||||
),
|
||||
),
|
||||
if (showStorageIndicator)
|
||||
Positioned(
|
||||
right: 8,
|
||||
bottom: 5,
|
||||
child: Icon(
|
||||
storageIcon(asset),
|
||||
color: Colors.white,
|
||||
size: 18,
|
||||
),
|
||||
if (asset.isFavorite)
|
||||
const Positioned(
|
||||
left: 8,
|
||||
bottom: 5,
|
||||
child: Icon(
|
||||
Icons.favorite,
|
||||
color: Colors.white,
|
||||
size: 18,
|
||||
),
|
||||
),
|
||||
if (asset.isFavorite)
|
||||
const Positioned(
|
||||
left: 8,
|
||||
bottom: 5,
|
||||
child: Icon(
|
||||
Icons.favorite,
|
||||
color: Colors.white,
|
||||
size: 18,
|
||||
),
|
||||
if (!asset.isImage) buildVideoIcon(),
|
||||
if (asset.stackChildrenCount > 0) buildStackIcon(),
|
||||
],
|
||||
),
|
||||
),
|
||||
if (!asset.isImage) buildVideoIcon(),
|
||||
if (asset.stackChildrenCount > 0) buildStackIcon(),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue