Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(mobile): Fix iOS swipe not paging #6726

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions mobile/lib/modules/asset_viewer/models/asset_index.dart
Original file line number Diff line number Diff line change
@@ -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,
);
}
}
69 changes: 69 additions & 0 deletions mobile/lib/modules/asset_viewer/ui/stacked_children.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
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';

/// 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<Asset> stackElements;

/// A callback function to get the stack index of the tapped element
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),
),
),
),
),
);
},
);
}
}
90 changes: 27 additions & 63 deletions mobile/lib/modules/asset_viewer/views/gallery_viewer.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -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';
Expand Down Expand Up @@ -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))
: <Asset>[];
Expand All @@ -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, (_, __) {});
Expand Down Expand Up @@ -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);
}
}

Expand Down Expand Up @@ -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<void>(
context: context,
Expand All @@ -558,7 +518,7 @@ class GalleryViewerPage extends HookConsumerWidget {
.read(assetStackServiceProvider)
.updateStackParent(
currentAsset,
stackElements.elementAt(stackIndex.value),
stackElements.elementAt(stackIndex),
);
ctx.pop();
context.popRoute();
Expand Down Expand Up @@ -593,7 +553,7 @@ class GalleryViewerPage extends HookConsumerWidget {
await ref.read(assetStackServiceProvider).updateStack(
currentAsset,
childrenToRemove: [
stackElements.elementAt(stackIndex.value),
stackElements.elementAt(stackIndex),
],
);
removeAssetFromStack();
Expand Down Expand Up @@ -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(
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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) {
Expand Down
Loading