perfect-postcode/frontend/src/components/map/StreetViewEmbed.tsx

77 lines
2.4 KiB
TypeScript

import { useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next';
import type { HexagonLocation } from '../../lib/external-search';
import { apiUrl, logNonAbortError } from '../../lib/api';
interface StreetViewEmbedProps {
location: HexagonLocation;
}
type Status = 'loading' | 'ok' | 'none' | 'error';
export default function StreetViewEmbed({ location }: StreetViewEmbedProps) {
const { t } = useTranslation();
const [status, setStatus] = useState<Status>('loading');
const [panoId, setPanoId] = useState<string | null>(null);
useEffect(() => {
setStatus('loading');
setPanoId(null);
const controller = new AbortController();
const params = new URLSearchParams({
lat: String(location.lat),
lon: String(location.lon),
});
fetch(apiUrl('streetview', params), { signal: controller.signal })
.then((res) => {
if (!res.ok) throw new Error(`HTTP ${res.status}`);
return res.json();
})
.then((data: { status: string; pano_id?: string }) => {
if (data.status === 'OK' && data.pano_id) {
setPanoId(data.pano_id);
setStatus('ok');
} else {
setStatus('none');
}
})
.catch((err) => {
logNonAbortError('streetview', err);
if (!controller.signal.aborted) {
setStatus('error');
}
});
return () => controller.abort();
}, [location.lat, location.lon]);
if (status === 'none' || status === 'error') return null;
return (
<div>
<div className="px-3 py-1.5 text-xs font-bold text-warm-500 bg-warm-50 dark:bg-warm-900 dark:text-warm-400 sticky top-0">
{t('streetView.title')}
</div>
<div className="px-3 py-2">
<div className="rounded overflow-hidden border border-warm-200 dark:border-warm-700">
{status === 'loading' ? (
<div
className="w-full animate-pulse bg-warm-200 dark:bg-warm-700"
style={{ height: 240 }}
/>
) : (
<iframe
className="w-full"
style={{ height: 240, border: 0 }}
loading="lazy"
referrerPolicy="no-referrer-when-downgrade"
src={`https://maps.google.com/maps?layer=c&panoid=${panoId}&cbp=11,0,0,0,0&output=svembed`}
/>
)}
</div>
</div>
</div>
);
}