77 lines
2.4 KiB
TypeScript
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>
|
|
);
|
|
}
|