fix: offline download — wait for completion, suppress transient errors

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.
This commit is contained in:
Davide Scaini
2026-06-04 01:06:39 +02:00
parent ccfc60934a
commit 86397e0980
2 changed files with 30 additions and 19 deletions
-1
View File
@@ -110,7 +110,6 @@ export function RecordingScreen() {
dlName.trim(), dlName.trim(),
expanded, expanded,
(pct, _tiles, sizeBytes) => { setDlProgress(pct); setDlSize(sizeBytes); }, (pct, _tiles, sizeBytes) => { setDlProgress(pct); setDlSize(sizeBytes); },
(msg) => Alert.alert('Download error', msg),
); );
setDlVisible(false); setDlVisible(false);
setDlProgress(null); setDlProgress(null);
+30 -18
View File
@@ -22,31 +22,43 @@ export interface OfflineRegion {
// ── Download ────────────────────────────────────────────────────────────────── // ── 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, name: string,
bounds: LngLatBounds, bounds: LngLatBounds,
onProgress: (pct: number, tilesDown: number, sizeBytes: number) => void, onProgress: (pct: number, tilesDown: number, sizeBytes: number) => void,
onError: (msg: string) => void,
): Promise<string> { ): Promise<string> {
OfflineManager.setProgressEventThrottle(500); OfflineManager.setProgressEventThrottle(500);
const pack = await OfflineManager.createPack( return new Promise((resolve, reject) => {
{ let packId: string;
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 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 ────────────────────────────────────────────────────────────────────── // ── List ──────────────────────────────────────────────────────────────────────