'use client'; import type { JournalBlock, MapItem } from '@/lib/journal'; import { isValidCoordinate } from '@/lib/journal'; import type { AlbumAssetRef } from '@/lib/journalAlbum'; import type AlbumPicker from '../AlbumPicker'; import { IconCamera, IconChevronUp, IconChevronDown, IconPlus, IconMap, IconFolder, IconX, } from '../Icons'; import { InlineMarkdownField } from '../InlineMarkdownField'; import { fillAssetSlots, isLegacyAssetRef } from './blockOps'; /** What the asset picker is opened for: its title, and where the pick goes. */ export interface AssetPickTarget { title: string; onSelect?: (assetId: string) => void; /** Multi-select: several picks confirmed at once (#602). */ onSelectMany?: (assetIds: string[]) => void; /** Most assets `onSelectMany` takes. */ max?: number; } interface BlockFieldsProps { block: JournalBlock; onChange: (block: JournalBlock) => void; onPickAsset: (target: AssetPickTarget) => void; onPickAlbum: (onSelect: (albumId: string) => void) => void; /** Assets of the albums referenced by album blocks, as loaded so far. */ albumAssets: Record; /** The picker's album list, once fetched — used for the album's name. */ albumList: Parameters[0]['albums'] | null; mapEnabled?: boolean; } /** * The form of one block in the journal editor, one branch per block type * (#555). Every edit goes out through `onChange` as a whole new block; the * editor owns the block list and its order. */ export function BlockFields({ block, onChange, onPickAsset, onPickAlbum, albumAssets, albumList, mapEnabled, }: BlockFieldsProps) { return (
{block.type === 'heading' && (
onChange({ ...block, text: e.target.value })} />
)} {block.type === 'paragraph' && ( onChange({ ...block, html })} /> )} {block.type === 'quote' && (
onChange({ ...block, text })} /> onChange({ ...block, author: e.target.value })} />
)} {block.type === 'photo' && isLegacyAssetRef(block.assetId) && (

Reference "{block.assetId}" is a legacy album position, not a photo. It will not appear on the published page — pick a photo below.

)} {block.type === 'photo' && (
onPickAsset({ title: 'Select Photo for Story', onSelect: (id) => onChange({ ...block, assetId: id }), }) } > {block.assetId ? ( // eslint-disable-next-line @next/next/no-img-element Thumb ) : ( + Pick )}
onChange({ ...block, caption })} />
)} {block.type === 'photo-pair' && block.assetIds.some(isLegacyAssetRef) && (

References "{block.assetIds.filter(isLegacyAssetRef).join('", "')} " are legacy album positions, not photos. They will not appear on the published page — pick photos below.

)} {block.type === 'photo-pair' && (
{[0, 1].map((pIdx) => (
onPickAsset({ title: `Select Photo #${pIdx + 1} for Pair`, onSelect: (id) => { const newIds = [...block.assetIds] as [string, string]; newIds[pIdx] = id; onChange({ ...block, assetIds: newIds }); }, }) } > {block.assetIds[pIdx] ? ( // eslint-disable-next-line @next/next/no-img-element Thumb ) : ( + Pick Photo #{pIdx + 1} )}
))}
onChange({ ...block, caption })} />
)} {block.type === 'photo-grid' && block.assetIds.some(isLegacyAssetRef) && (

References "{block.assetIds.filter(isLegacyAssetRef).join('", "')} " are legacy album positions, not photos. They will not appear on the published page — pick photos below.

)} {block.type === 'photo-grid' && (
{block.assetIds.map((assetId, pIdx) => (
onPickAsset({ title: `Select Photo #${pIdx + 1} for Grid`, onSelect: (id) => { const newIds = [...block.assetIds]; newIds[pIdx] = id; onChange({ ...block, assetIds: newIds }); }, }) } > {assetId ? ( // eslint-disable-next-line @next/next/no-img-element Thumb ) : ( + Pick #{pIdx + 1} )}
{block.assetIds.length > 3 && ( )}
))}
Three or more photos, laid out in rows of three.
onChange({ ...block, caption })} />
)} {block.type === 'facts' && (
{block.items.map((item, fIdx) => (
onChange({ ...block, items: block.items.map((it, i) => i === fIdx ? { ...it, label: e.target.value } : it, ), }) } /> onChange({ ...block, items: block.items.map((it, i) => (i === fIdx ? { ...it, value } : it)), }) } />
))}
)} {block.type === 'album' && (
{block.albumId && ( {albumAssets[block.albumId] ? `${albumAssets[block.albumId].length} photos in album` : 'loading…'} )}
onChange({ ...block, caption: caption || undefined })} /> Photos follow the album's order (a manual order in the gallery comes first). Count and skip pick a slice; leave count empty for all.
)} {block.type === 'map' && (
{mapEnabled === false && (

The map is switched off in Settings → General. This block will not render until it is enabled.

)} onChange({ ...block, caption })} /> {block.items.map((item, mIdx) => { const setItem = (next: MapItem) => onChange({ ...block, items: block.items.map((it, i) => (i === mIdx ? next : it)), }); const move = (dir: -1 | 1) => { const items = [...block.items]; const target = mIdx + dir; if (target < 0 || target >= items.length) return; [items[mIdx], items[target]] = [items[target], items[mIdx]]; onChange({ ...block, items }); }; return (
{mIdx + 1} {item.kind === 'point' && ( <> setItem({ ...item, label: e.target.value || undefined })} /> setItem({ ...item, lat: parseFloat(e.target.value) })} /> setItem({ ...item, lng: parseFloat(e.target.value) })} /> )} {item.kind === 'photo' && (
onPickAsset({ title: 'Select a photo to place on the map', onSelect: (id) => setItem({ kind: 'photo', assetId: id }), }) } > {item.assetId ? ( // eslint-disable-next-line @next/next/no-img-element Thumb ) : ( + Pick photo )}
)} {item.kind === 'all-photos' && ( All geotagged photos of this entry, in order )}
); })} {block.items.some((it) => it.kind === 'point' && !isValidCoordinate(it.lat, it.lng)) && (

A point needs a latitude within ±90 and a longitude within ±180. Points without valid coordinates are not saved.

)}
Typed points show in the preview right away. Photo pins are placed on the live page from the photo's GPS, under the album's location precision.
)}
); }