import type { PlacePhotoCandidate } from '@trek/shared';
import { Check, ImageOff, ImagePlus, Loader2 } from 'lucide-react';
import React, { useEffect, useRef, useState } from 'react';
import { placePictureRepo } from '../../repo/placePictureRepo';
import type { TranslationFn, TripFile } from '../../types';
import { normalizeImageFile } from '../../utils/convertHeic';
import { Tooltip } from '../shared/Tooltip';
import { offersPictures, type ChosenImage } from './placeImageChoice';
import { creditOf } from './placePhotoCredit';
/** The place's own pictures beside the sources' ones: what it has, what it can get, what waits for the save. */
export interface PlaceImageOwn {
/** The picture the place has now; null on a new place or one without. */
currentUrl: string | null;
/** Images attached to the place that can stand as its picture. */
attached: TripFile[];
/** The picture from the device that waits for the save, as a preview. */
pendingPreview: string | null;
/** False where the user may not upload files; the upload tile is then left out. */
canUpload: boolean;
onUpload: (file: File) => void;
onPickAttached: (file: TripFile) => void;
onRemove: () => void;
}
interface PlaceImagePickerProps {
/** What the sources know, as found for the place; empty while they load or when there are none. */
photos: PlacePhotoCandidate[];
chosen: ChosenImage;
onPickUrl: (url: string | null) => void;
/** Left out where only the sources' pictures are offered. */
own?: PlaceImageOwn;
/** On a white card, without native tooltips: hovering moves the credit line instead. */
dialog?: boolean;
t: TranslationFn;
}
const TILE = 'group relative block aspect-square w-full overflow-hidden rounded-lg transition-shadow';
function ring(selected: boolean, dialog: boolean): string {
if (selected)
return `ring-2 ring-accent ring-offset-2 ${dialog ? 'ring-offset-surface-card' : 'ring-offset-surface-secondary'}`;
return 'ring-1 ring-edge hover:ring-content-muted';
}
function Picked(): React.ReactElement {
return (
);
}
/** What the credit line under the grid says: a source's photo with its author and licence, or the place's own. */
type Credit = { kind: 'photo'; photo: PlacePhotoCandidate } | { kind: 'own'; label: string } | null;
/**
* The one place a place's picture is chosen, in the editor: a tile to upload
* one from the device, the picture it has now, the images attached to it and
* the pictures its sources know. The chosen tile is ringed and ticked; a
* second tap on a source's photo lets go of it, and "Remove picture" leaves
* the place without one. Nothing is sent before the form is saved.
*/
export default function PlaceImagePicker({
photos,
chosen,
onPickUrl,
own,
dialog = false,
t,
}: PlaceImagePickerProps): React.ReactElement | null {
const [hovered, setHovered] = useState(null);
const current = own?.currentUrl && !photos.some((p) => p.url === own.currentUrl) ? own.currentUrl : null;
const attached = own?.attached ?? [];
if (!offersPictures(photos, own)) return null;
const shown = hovered ?? creditFor(chosen, photos, attached, own, t);
const removable = !!own && chosen.kind !== 'none';
return (
<>