Studio lot 8e : lecteur audio dans la fiche d'un point de carte
Audio résolu par identifiant de ressource, portrait et nom du narrateur au-dessus du lecteur. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011VxSQeGQYUvPmSEoGdnidA
This commit is contained in:
parent
81c807cd77
commit
d371b447c6
@ -13,6 +13,8 @@ import { t, tPlain } from '@/lib/i18n'
|
||||
import type { SectionDTO, GeoPointDTO } from '@/lib/api/types'
|
||||
import { trackEvent } from '@/lib/stats'
|
||||
import { getGeoPointLatLng } from '@/lib/geo'
|
||||
import NarratorAvatar from '@/components/ui/NarratorAvatar'
|
||||
import { useAudioResource } from '@/hooks/useAudioResource'
|
||||
import './map/map.css'
|
||||
|
||||
const LeafletMap = dynamic(() => import('./map/LeafletMap'), {
|
||||
@ -29,6 +31,7 @@ interface Props {
|
||||
slug: string
|
||||
configId: string
|
||||
languages: string[]
|
||||
apiKey: string
|
||||
}
|
||||
|
||||
type Mode = 'map' | 'list'
|
||||
@ -39,7 +42,7 @@ const DOCK_WIDTH = 340
|
||||
const DETAIL_WIDTH = 320
|
||||
const PANEL_TOP = 60
|
||||
|
||||
export default function MapSection({ section, configId, languages }: Props) {
|
||||
export default function MapSection({ section, configId, languages, apiKey }: Props) {
|
||||
const { language, setAvailableLanguages, instanceId } = useVisitor()
|
||||
const back = useBack()
|
||||
|
||||
@ -322,6 +325,7 @@ export default function MapSection({ section, configId, languages }: Props) {
|
||||
{/* Detail sheet (POI) */}
|
||||
{selected && (
|
||||
<PointDetail
|
||||
apiKey={apiKey}
|
||||
point={selected}
|
||||
language={language}
|
||||
variant={isLandscape ? 'card' : 'sheet'}
|
||||
@ -396,8 +400,9 @@ function PointList({
|
||||
|
||||
// ── Detail sheet ────────────────────────────────────────────────────────────
|
||||
function PointDetail({
|
||||
point, language, variant, width, top, onClose,
|
||||
apiKey, point, language, variant, width, top, onClose,
|
||||
}: {
|
||||
apiKey: string
|
||||
point: GeoPointDTO
|
||||
language: string
|
||||
/// `sheet` : feuille basse (portrait). `card` : colonne à droite (paysage),
|
||||
@ -413,6 +418,10 @@ function PointDetail({
|
||||
const prices = t(point.prices, language)
|
||||
const schedules = t(point.schedules, language)
|
||||
const isCard = variant === 'card'
|
||||
const audio = useAudioResource(
|
||||
point.audioIds?.find((a) => a.language === language)?.value ?? point.audioIds?.[0]?.value,
|
||||
apiKey,
|
||||
)
|
||||
|
||||
return (
|
||||
<>
|
||||
@ -476,6 +485,15 @@ function PointDetail({
|
||||
className="overflow-y-auto px-5 py-4 flex flex-col gap-4"
|
||||
style={isCard ? { flex: 1, minHeight: 0 } : { maxHeight: 'calc(80vh - 180px)' }}
|
||||
>
|
||||
{audio.url && (
|
||||
<div className="flex flex-col gap-2">
|
||||
{audio.narratorName && (
|
||||
<NarratorAvatar name={audio.narratorName} portraitUrl={audio.narratorPortraitUrl} color="var(--color-text)" />
|
||||
)}
|
||||
<audio controls src={audio.url} style={{ width: '100%' }} />
|
||||
</div>
|
||||
)}
|
||||
|
||||
{t(point.description, language) && (
|
||||
<div
|
||||
className="text-sm leading-relaxed [&_p]:m-0 [&_p+p]:mt-2"
|
||||
|
||||
@ -265,6 +265,8 @@ export interface GeoPointDTO {
|
||||
site?: TranslationDTO[]
|
||||
geometry?: GeometryDTO
|
||||
polyColor?: string
|
||||
/** Un audio par langue : identifiants de ressource (lot 8e). */
|
||||
audioIds?: TranslationDTO[]
|
||||
}
|
||||
|
||||
export interface CategorieDTO {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user