From 86397e0980839c375ea5fa9103fad920c40f9c7d Mon Sep 17 00:00:00 2001 From: Davide Scaini Date: Thu, 4 Jun 2026 01:06:39 +0200 Subject: [PATCH] =?UTF-8?q?fix:=20offline=20download=20=E2=80=94=20wait=20?= =?UTF-8?q?for=20completion,=20suppress=20transient=20errors?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit createPack() resolves when the pack is registered, not when tiles are done downloading. Wrapping in a Promise that resolves only on status.state === 'complete' keeps the progress modal visible and the 'Download complete' alert fires only when the pack is actually ready. Transient tile errors ('stream was reset: CANCEL') are silently ignored in the error listener — MapLibre retries them internally and they do not indicate a failed download. The onError callback is removed from the public API since it was causing spurious alerts mid-download. --- src/screens/RecordingScreen.tsx | 1 - src/services/offline.ts | 48 ++++++++++++++++++++------------- 2 files changed, 30 insertions(+), 19 deletions(-) diff --git a/src/screens/RecordingScreen.tsx b/src/screens/RecordingScreen.tsx index 03861e1..c329a32 100644 --- a/src/screens/RecordingScreen.tsx +++ b/src/screens/RecordingScreen.tsx @@ -110,7 +110,6 @@ export function RecordingScreen() { dlName.trim(), expanded, (pct, _tiles, sizeBytes) => { setDlProgress(pct); setDlSize(sizeBytes); }, - (msg) => Alert.alert('Download error', msg), ); setDlVisible(false); setDlProgress(null); diff --git a/src/services/offline.ts b/src/services/offline.ts index 0bb951a..875eb85 100644 --- a/src/services/offline.ts +++ b/src/services/offline.ts @@ -22,31 +22,43 @@ export interface OfflineRegion { // ── Download ────────────────────────────────────────────────────────────────── -export async function downloadRegion( +/** + * Download a region and return a Promise that resolves only when the pack + * reaches state 'complete'. Transient tile-fetch errors (e.g. "stream was + * reset: CANCEL") are silently ignored — MapLibre retries them internally + * and they do not indicate a failed download. + */ +export function downloadRegion( name: string, bounds: LngLatBounds, onProgress: (pct: number, tilesDown: number, sizeBytes: number) => void, - onError: (msg: string) => void, ): Promise { OfflineManager.setProgressEventThrottle(500); - const pack = await OfflineManager.createPack( - { - mapStyle: OFFLINE_STYLE_URL, - bounds, - minZoom: MIN_ZOOM, - maxZoom: MAX_ZOOM, - metadata: { name, createdAt: new Date().toISOString() }, - }, - (_pack, status: OfflinePackStatus) => { - onProgress(status.percentage, status.completedTileCount, status.completedTileSize); - }, - (_pack, error) => { - onError(error.message); - }, - ); + return new Promise((resolve, reject) => { + let packId: string; - return pack.id; + OfflineManager.createPack( + { + mapStyle: OFFLINE_STYLE_URL, + bounds, + minZoom: MIN_ZOOM, + maxZoom: MAX_ZOOM, + metadata: { name, createdAt: new Date().toISOString() }, + }, + (pack, status: OfflinePackStatus) => { + packId = pack.id; + onProgress(status.percentage, status.completedTileCount, status.completedTileSize); + if (status.state === 'complete') { + resolve(packId); + } + }, + (_pack, _error) => { + // Transient tile-level errors — MapLibre retries these automatically. + // Do not reject: the overall download is still in progress. + }, + ).catch(reject); // only rejects on createPack failure (e.g. bad style URL) + }); } // ── List ──────────────────────────────────────────────────────────────────────