alright
This commit is contained in:
parent
c645b0f1d4
commit
39ef5c6646
79 changed files with 5660 additions and 2199 deletions
|
|
@ -135,73 +135,86 @@ export function useExportController({
|
|||
|
||||
useEffect(() => clearExportNoticeTimer, [clearExportNoticeTimer]);
|
||||
|
||||
const handleExport = useCallback(() => {
|
||||
if (exporting) return;
|
||||
if (!bounds) {
|
||||
showExportNotice({ kind: 'error', message: t('header.exportUnavailable') });
|
||||
return;
|
||||
}
|
||||
const handleExport = useCallback(
|
||||
(options?: { postcodes?: string[] }) => {
|
||||
if (exporting) return;
|
||||
|
||||
const { south, west, north, east } = bounds;
|
||||
const params = new URLSearchParams({
|
||||
bounds: `${south},${west},${north},${east}`,
|
||||
});
|
||||
const filterStr = buildFilterString(filters, features);
|
||||
if (filterStr) params.set('filters', filterStr);
|
||||
const travelParam = buildTravelParam(travelTimeEntries);
|
||||
if (travelParam) params.set('travel', travelParam);
|
||||
appendTravelStateParams(params, travelTimeEntries);
|
||||
for (const overlay of selectedOverlays) {
|
||||
params.append('overlay', overlay);
|
||||
}
|
||||
if (shareCode) params.set('share', shareCode);
|
||||
const url = apiUrl('export', params);
|
||||
const postcodeList = options?.postcodes?.map((p) => p.trim()).filter(Boolean) ?? [];
|
||||
const isListMode = postcodeList.length > 0;
|
||||
|
||||
const controller = new AbortController();
|
||||
let timedOut = false;
|
||||
const timeoutId = window.setTimeout(() => {
|
||||
timedOut = true;
|
||||
controller.abort();
|
||||
}, EXPORT_TIMEOUT_MS);
|
||||
|
||||
setExporting(true);
|
||||
clearExportNotice();
|
||||
|
||||
void (async () => {
|
||||
try {
|
||||
const res = await fetch(url, authHeaders({ signal: controller.signal }));
|
||||
if (!res.ok) throw new Error(await getExportErrorMessage(res));
|
||||
|
||||
const blob = await res.blob();
|
||||
if (blob.size === 0) throw new Error(t('header.exportEmpty'));
|
||||
|
||||
triggerExportDownload(blob, getExportFileName(res));
|
||||
trackEvent('Export');
|
||||
showExportNotice({ kind: 'success', message: t('header.exportReady') });
|
||||
} catch (err) {
|
||||
if (!timedOut) logNonAbortError('Export failed', err);
|
||||
const detail = err instanceof Error && err.message.trim() ? ` ${err.message}` : '';
|
||||
showExportNotice({
|
||||
kind: 'error',
|
||||
message: timedOut ? t('header.exportTimedOut') : `${t('header.exportFailed')}${detail}`,
|
||||
});
|
||||
} finally {
|
||||
window.clearTimeout(timeoutId);
|
||||
setExporting(false);
|
||||
if (!isListMode && !bounds) {
|
||||
showExportNotice({ kind: 'error', message: t('header.exportUnavailable') });
|
||||
return;
|
||||
}
|
||||
})();
|
||||
}, [
|
||||
bounds,
|
||||
clearExportNotice,
|
||||
exporting,
|
||||
features,
|
||||
filters,
|
||||
selectedOverlays,
|
||||
shareCode,
|
||||
showExportNotice,
|
||||
t,
|
||||
travelTimeEntries,
|
||||
]);
|
||||
|
||||
const params = new URLSearchParams();
|
||||
if (isListMode) {
|
||||
params.set('postcodes', postcodeList.join(','));
|
||||
if (shareCode) params.set('share', shareCode);
|
||||
} else {
|
||||
const { south, west, north, east } = bounds!;
|
||||
params.set('bounds', `${south},${west},${north},${east}`);
|
||||
const filterStr = buildFilterString(filters, features);
|
||||
if (filterStr) params.set('filters', filterStr);
|
||||
const travelParam = buildTravelParam(travelTimeEntries);
|
||||
if (travelParam) params.set('travel', travelParam);
|
||||
appendTravelStateParams(params, travelTimeEntries);
|
||||
for (const overlay of selectedOverlays) {
|
||||
params.append('overlay', overlay);
|
||||
}
|
||||
if (shareCode) params.set('share', shareCode);
|
||||
}
|
||||
const url = apiUrl('export', params);
|
||||
|
||||
const controller = new AbortController();
|
||||
let timedOut = false;
|
||||
const timeoutId = window.setTimeout(() => {
|
||||
timedOut = true;
|
||||
controller.abort();
|
||||
}, EXPORT_TIMEOUT_MS);
|
||||
|
||||
setExporting(true);
|
||||
clearExportNotice();
|
||||
|
||||
void (async () => {
|
||||
try {
|
||||
const res = await fetch(url, authHeaders({ signal: controller.signal }));
|
||||
if (!res.ok) throw new Error(await getExportErrorMessage(res));
|
||||
|
||||
const blob = await res.blob();
|
||||
if (blob.size === 0) throw new Error(t('header.exportEmpty'));
|
||||
|
||||
triggerExportDownload(blob, getExportFileName(res));
|
||||
trackEvent('Export');
|
||||
showExportNotice({ kind: 'success', message: t('header.exportReady') });
|
||||
} catch (err) {
|
||||
if (!timedOut) logNonAbortError('Export failed', err);
|
||||
const detail = err instanceof Error && err.message.trim() ? ` ${err.message}` : '';
|
||||
showExportNotice({
|
||||
kind: 'error',
|
||||
message: timedOut
|
||||
? t('header.exportTimedOut')
|
||||
: `${t('header.exportFailed')}${detail}`,
|
||||
});
|
||||
} finally {
|
||||
window.clearTimeout(timeoutId);
|
||||
setExporting(false);
|
||||
}
|
||||
})();
|
||||
},
|
||||
[
|
||||
bounds,
|
||||
clearExportNotice,
|
||||
exporting,
|
||||
features,
|
||||
filters,
|
||||
selectedOverlays,
|
||||
shareCode,
|
||||
showExportNotice,
|
||||
t,
|
||||
travelTimeEntries,
|
||||
]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
onExportStateChange?.({ onExport: handleExport, exporting });
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue