diff --git a/frontend/css/styles.css b/frontend/css/styles.css
index 2c16fb8..2b5b3c4 100644
--- a/frontend/css/styles.css
+++ b/frontend/css/styles.css
@@ -1149,6 +1149,9 @@ select {
/* Dark map: invert ONLY the tile layer (overlays — track, route, roads, puck — keep
their colours). User-tuned filter on OSM standard tiles. Applies to every Leaflet map. */
body.dark-map .leaflet-tile-pane { filter: invert(1) hue-rotate(180deg) brightness(1.5); }
+/* Darken the map backdrop too, else the light Leaflet container (#ddd) flashes bright
+ between/behind tiles while zooming or where a tile is missing. */
+body.dark-map .leaflet-container { background: #1b1f24; }
/* Always-on compass needle: red half = North, grey = South. Rotates to point to real north.
Top-RIGHT (zoom controls are top-left), below the status-bar safe area. */
/* Position/direction puck (SVG divIcon) — no default leaflet-div-icon box. */
diff --git a/frontend/js/tilecache.js b/frontend/js/tilecache.js
index e38cdda..fb07f84 100644
--- a/frontend/js/tilecache.js
+++ b/frontend/js/tilecache.js
@@ -130,29 +130,33 @@ TileCache.createLayer = function() {
createTile: function(coords, done) {
const tile = document.createElement("img");
const key = `${coords.z}/${coords.x}/${coords.y}`;
+ const url = `https://a.tile.openstreetmap.org/${key}.png`;
+ const show = (blob) => { tile.src = URL.createObjectURL(blob); done(null, tile); };
+ // Fetch ONCE: cache and display from the same response. The old code loaded the
+ // display
from the URL and cached via a SEPARATE fetch — on a flaky mobile
+ // link that second request often failed, so tiles showed but were never cached,
+ // and re-zooming re-fetched them. One fetch → every shown tile is also cached.
+ const fetchAndCache = () => {
+ fetch(url).then(async resp => {
+ if (!resp.ok) throw new Error("http " + resp.status);
+ const blob = await resp.blob();
+ putTile(key, blob).catch(() => {});
+ show(blob);
+ }).catch(() => {
+ // fetch blocked/failed → last resort plain
(no caching)
+ tile.src = url;
+ tile.onload = () => done(null, tile);
+ tile.onerror = () => done(null, tile);
+ });
+ };
getTile(key).then(blob => {
- if (blob) {
- tile.src = URL.createObjectURL(blob);
- done(null, tile);
- } else if (TileCache.offlineMode) {
- // Offline: show empty tile
- done(null, tile);
- } else {
- // Fetch from network
- tile.crossOrigin = "";
- tile.src = `https://a.tile.openstreetmap.org/${key}.png`;
- tile.onload = () => {
- fetch(tile.src).then(r => r.blob()).then(b => putTile(key, b)).catch(() => {});
- done(null, tile);
- };
- tile.onerror = () => done(null, tile);
- }
+ if (blob) { show(blob); }
+ else if (TileCache.offlineMode) { done(null, tile); } // offline + not cached → empty
+ else { fetchAndCache(); }
}).catch(() => {
if (TileCache.offlineMode) { done(null, tile); return; }
- tile.src = `https://a.tile.openstreetmap.org/${key}.png`;
- tile.onload = () => done(null, tile);
- tile.onerror = () => done(null, tile);
+ fetchAndCache();
});
return tile;