fix(journey): keep the picker's date headings chronological, and name any provider

Since the provider picker started sorting by distance to the entry, the date
groups were built from the already-sorted list, so first-seen order put the day
holding the nearest photo at the top and the headings stopped reading in order.
Grouping now sorts its own output; the order within a day is untouched, so the
distance sort still does its job where it belongs.

The picker also called every non-Immich provider "Synology Photos". It now
falls back to the provider's own id, the way the gallery view already did.
This commit is contained in:
Maurice
2026-08-18 20:02:28 +02:00
committed by Maurice
parent edb487fbef
commit 85794a5a59
3 changed files with 41 additions and 3 deletions
@@ -167,7 +167,7 @@ export function ProviderPicker({ provider, userId, entries, trips, existingAsset
{/* Header */}
{!embedded && <div className="flex items-center justify-between px-6 py-4 border-b border-zinc-200 dark:border-zinc-700 flex-shrink-0">
<h2 className="text-[16px] font-bold text-zinc-900 dark:text-white">
{provider === 'immich' ? 'Immich' : 'Synology Photos'}
{provider === 'immich' ? 'Immich' : provider === 'synologyphotos' ? 'Synology Photos' : provider}
</h2>
<button onClick={onClose} className="w-8 h-8 rounded-lg flex items-center justify-center text-zinc-400 hover:bg-zinc-100 dark:hover:bg-zinc-800">
<X size={16} />
@@ -1,5 +1,5 @@
import { describe, expect, it } from 'vitest';
import { distanceBetweenGeoPoints, sortProviderPhotos } from './JourneyDetailPage.helpers';
import { distanceBetweenGeoPoints, groupPhotosByDate, sortProviderPhotos } from './JourneyDetailPage.helpers';
describe('Journey provider photo ranking', () => {
it('places GPS photos nearest the selected Journey location and keeps photos without GPS', () => {
@@ -25,3 +25,31 @@ describe('Journey provider photo ranking', () => {
expect(distanceBetweenGeoPoints({ lat: 41.9, lng: 12.5 }, { lat: 41.9, lng: 12.5 })).toBe(0);
});
});
describe('Journey provider photo grouping', () => {
it('orders the date headings newest first, whatever order the photos arrive in', () => {
const photos = [
{ id: 'b', takenAt: '2026-03-14T09:00:00Z' },
{ id: 'a', takenAt: '2026-03-16T09:00:00Z' },
{ id: 'c', takenAt: '2026-03-15T09:00:00Z' },
];
expect(groupPhotosByDate(photos).map((g) => g.date)).toEqual(['2026-03-16', '2026-03-15', '2026-03-14']);
});
it('keeps the distance sort inside a day but not across days', () => {
// What sortProviderPhotos hands over: nearest first, so the 14th leads the list.
const sorted = [
{ id: 'near', takenAt: '2026-03-14T10:00:00Z' },
{ id: 'mid', takenAt: '2026-03-16T10:00:00Z' },
{ id: 'far', takenAt: '2026-03-14T11:00:00Z' },
];
const groups = groupPhotosByDate(sorted);
expect(groups.map((g) => g.date)).toEqual(['2026-03-16', '2026-03-14']);
expect(groups[1].assets.map((a: { id: string }) => a.id)).toEqual(['near', 'far']);
});
it('sorts photos without a date to the end', () => {
const photos = [{ id: 'x' }, { id: 'y', takenAt: '2026-03-16T09:00:00Z' }];
expect(groupPhotosByDate(photos).map((g) => g.date)).toEqual(['2026-03-16', '__unknown__']);
});
});
@@ -66,7 +66,17 @@ export function groupPhotosByDate(photos: any[]): { date: string; label: string;
if (!map.has(key)) map.set(key, [])
map.get(key)!.push(asset)
}
return [...map.entries()].map(([date, assets]) => ({
// Group order must not depend on the order of `photos`. Once the caller sorts by
// distance to the entry, first-seen order would put the day holding the nearest
// photo first and the date headings would stop reading chronologically. Order
// within a day is left as handed in, so the distance sort still applies there.
return [...map.entries()]
.sort((a, b) => {
if (a[0] === '__unknown__') return 1
if (b[0] === '__unknown__') return -1
return b[0].localeCompare(a[0])
})
.map(([date, assets]) => ({
date,
label: date === '__unknown__'
? 'Unknown date'