'use client';
import { formatDay } from '../../lib/format';
import { groupByTime } from '../../lib/grouping';
import { resolveLabel } from '../../lib/smartAlbums';
import { Icon, type IconName } from '../../icons';
import { useGallery, useGalleryStoreApi } from '../../store/context';
import { albumMedia, liveMedia, objectLabelCounts } from '../../store/selectors';
import type { MediaItem, ViewId } from '../../types';
import { openContextMenu } from '../ContextMenu';
import { confirmAction, promptAlbumName } from '../modals';
function SectionHeader({
title,
chevronTo,
action,
}: {
title: string;
chevronTo?: () => void;
action?: { label: string; onClick: () => void };
}) {
return (
{
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
chevronTo();
}
}
: undefined
}
>
{title}
{chevronTo ? : null}
{action ? (
) : null}
);
}
function PinnedCard({
label,
cover,
badge,
onClick,
}: {
label: string;
cover?: MediaItem;
badge?: IconName;
onClick: () => void;
}) {
return (
);
}
function EmptyCard({
icon,
title,
text,
}: {
icon: IconName;
title: string;
text: string;
}) {
return (
);
}
export function CollectionsView() {
const api = useGalleryStoreApi();
const media = useGallery((s) => s.media);
const albums = useGallery((s) => s.albums);
const labelAliases = useGallery((s) => s.labelAliases);
const live = liveMedia(media);
const userAlbums = albums.filter((a) => a.kind === 'user' || a.kind === 'folder');
const go = (v: ViewId) => () => api.getState().setView(v);
const recentDays = groupByTime(live, 'day').slice(0, 8);
const objectEntries = [...objectLabelCounts(media, labelAliases).entries()]
.sort((a, b) => b[1] - a[1])
.slice(0, 14);
// Right-click an object card โ permanently rename its tag (car โ excavator).
const renameTag = (label: string) => (e: React.MouseEvent) => {
e.preventDefault();
openContextMenu(e.clientX, e.clientY, [
{
label: 'Rename Tag',
icon: 'tag',
onClick: () =>
promptAlbumName('Rename Tag', label, (name) => api.getState().renameLabel(label, name), {
placeholder: 'Tag name',
}),
},
{
label: 'Delete Tag',
icon: 'trash',
danger: true,
onClick: () =>
confirmAction({
title: 'Delete Tag',
message: `Remove the "${label}" tag? It's deleted from all photos and won't be created again.`,
confirmLabel: 'Delete',
danger: true,
onConfirm: () => api.getState().deleteLabel(label),
}),
},
]);
};
return (
{/* Albums */}
promptAlbumName('New Album', '', (name) => {
const id = api.getState().createAlbum(name);
api.getState().setView(`album:${id}` as ViewId);
}),
}}
/>
{userAlbums.length === 0 ? (
) : (
{userAlbums.map((a) => {
const am = albumMedia(a, media);
return (
m.id === a.coverId) ?? am[0]}
onClick={go(a.id as ViewId)}
/>
);
})}
)}
{/* Objects (AI) โ auto categories from on-device object detection */}
{objectEntries.length > 0 ? (
{objectEntries.map(([label, count]) => (
))}
) : null}
{/* Shared Albums */}
api.getState().setView('shared-albums') }} />
{/* Recent Days */}
{recentDays.length === 0 ? (
) : (
{recentDays.map((d) => (
api.getState().openLightbox(d.items[0]!.id)}
/>
))}
)}
);
}