diff --git a/client/src/components/Journey/JourneyDetailPageProviderPicker.tsx b/client/src/components/Journey/JourneyDetailPageProviderPicker.tsx index fd4200b03..d74af8777 100644 --- a/client/src/components/Journey/JourneyDetailPageProviderPicker.tsx +++ b/client/src/components/Journey/JourneyDetailPageProviderPicker.tsx @@ -94,7 +94,7 @@ export function ProviderPicker({ setSearchPage(page); try { const data = await memoriesApi.search(provider, { from, to, page, size: 50 }, signal); - const assets = data.assets || []; + const assets = sortProviderPhotos(data.assets || [], contextLocation); setPhotos((prev) => (append ? [...prev, ...assets] : assets)); setHasMore(!!data.hasMore); } catch { @@ -119,7 +119,8 @@ export function ProviderPicker({ setPhotos([]); setHasMore(false); try { - setPhotos((await memoriesApi.albumPhotos(provider, album.id, album.passphrase, signal)).assets || []); + const assets = (await memoriesApi.albumPhotos(provider, album.id, album.passphrase, signal)).assets || []; + setPhotos(sortProviderPhotos(assets, contextLocation)); } catch { /* ignore */ } @@ -149,11 +150,6 @@ export function ProviderPicker({ if (customFrom && customTo) searchPhotos(customFrom, customTo); }; - const sortedPhotos = useMemo( - () => sortProviderPhotos(photos, contextLocation), - [photos, contextLocation?.lat, contextLocation?.lng] - ); - const toggleAsset = (id: string) => { setSelected((prev) => { const next = new Map(prev); @@ -414,9 +410,9 @@ export function ProviderPicker({ {/* Select all bar — sticky above grid */} {!loading && - sortedPhotos.length > 0 && + photos.length > 0 && (() => { - const selectable = sortedPhotos.filter((a: any) => !existingAssetIds.has(a.id)); + const selectable = photos.filter((a: any) => !existingAssetIds.has(a.id)); const allSelected = selectable.length > 0 && selectable.every((a: any) => selected.has(a.id)); if (selectable.length === 0) return null; return ( @@ -464,7 +460,7 @@ export function ProviderPicker({
{filter === 'trip' && !tripRange.from @@ -474,7 +470,7 @@ export function ProviderPicker({
diff --git a/client/src/pages/journeyDetail/JourneyDetailPage.helpers.test.ts b/client/src/pages/journeyDetail/JourneyDetailPage.helpers.test.ts
index 5b3eecad9..42258516e 100644
--- a/client/src/pages/journeyDetail/JourneyDetailPage.helpers.test.ts
+++ b/client/src/pages/journeyDetail/JourneyDetailPage.helpers.test.ts
@@ -16,7 +16,15 @@ describe('Journey provider photo ranking', () => {
]);
});
- it('preserves provider order when the selected entry has no valid location', () => {
+ it('falls back to newest-taken-first when the selected entry has no valid location', () => {
+ const photos = [
+ { id: 'older', takenAt: '2026-03-14T09:00:00Z' },
+ { id: 'newer', takenAt: '2026-03-16T09:00:00Z' },
+ ];
+ expect(sortProviderPhotos(photos, { lat: 200, lng: 12 }).map((photo) => photo.id)).toEqual(['newer', 'older']);
+ });
+
+ it('keeps original relative order for photos without a valid location and without takenAt', () => {
const photos = [{ id: 'first' }, { id: 'second' }];
expect(sortProviderPhotos(photos, { lat: 200, lng: 12 }).map((photo) => photo.id)).toEqual(['first', 'second']);
});
diff --git a/client/src/pages/journeyDetail/JourneyDetailPage.helpers.ts b/client/src/pages/journeyDetail/JourneyDetailPage.helpers.ts
index 11c6857da..607c2fe69 100644
--- a/client/src/pages/journeyDetail/JourneyDetailPage.helpers.ts
+++ b/client/src/pages/journeyDetail/JourneyDetailPage.helpers.ts
@@ -132,10 +132,24 @@ export function distanceBetweenGeoPoints(a: GeoPoint, b: GeoPoint): number {
/**
* Keep every asset, but put assets with usable GPS nearest to the selected
- * Journey location. The original provider order is the stable fallback.
+ * Journey location. Without a usable location, fall back to newest-taken-first
+ * (provider search order isn't guaranteed chronological), with a stable index
+ * tiebreak so photos missing `takenAt` keep a deterministic relative order.
*/
export function sortProviderPhotos