mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-10-21 22:46:32 +00:00
fix(maps): let users and admins enter a CARTO API key (#2060)
CARTO stamps an "API KEY REQUIRED" watermark onto every basemap tile fetched without a key since 26.08.2026, and both CartoDB presets plus the built-in fallback are CARTO. The key is free and needs no account, so add it as a setting rather than dropping the provider. carto_api_key sits next to mapbox_access_token: per user in Settings/Map, instance-wide under Admin/User Defaults, encrypted at rest, injected from CARTO_API_KEY on managed instances. Unlike Mapbox it never switches the map provider, since a raster key says nothing about the renderer. The key is appended as ?key= at render time instead of being stored in the template, so a saved URL stays portable and survives a rotation. That is also what reaches the four maps that pinned CARTO and ignored the user's tile setting: atlas, collections, the public trip share and the public journey. The two share pages read it from their token-gated payload, the same way baseCurrency already travels. Also lifts the release notice above page chrome. Its overlay sat at z-index 50, under the Vacay mode toolbar at 61, so the dialog was neither on top nor modal. Adds a six-step layer scale to index.css and moves the three notice shells onto it. Closes #2054 Closes #2052
This commit is contained in:
@@ -21,7 +21,8 @@
|
||||
import type { Place } from '../types'
|
||||
import { offlineDb, upsertSyncMeta } from '../db/offlineDb'
|
||||
import { isAuthed } from './authGate'
|
||||
import { normalizeTileUrl } from '../utils/tileUrl'
|
||||
import { normalizeTileUrl, withTileApiKey } from '../utils/tileUrl'
|
||||
import { CARTO_LIGHT } from '../constants/mapDefaults'
|
||||
|
||||
// ── Constants ─────────────────────────────────────────────────────────────────
|
||||
|
||||
@@ -50,8 +51,7 @@ export const TILE_CONCURRENCY = 6
|
||||
/** Name of the Workbox runtime cache holding map tiles (see vite.config.js). */
|
||||
const TILE_CACHE = 'map-tiles'
|
||||
|
||||
const DEFAULT_TILE_URL =
|
||||
'https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}{r}.png'
|
||||
const DEFAULT_TILE_URL = CARTO_LIGHT
|
||||
|
||||
/**
|
||||
* Must stay identical to Leaflet's `subdomains` default ('abc'), because the
|
||||
@@ -158,9 +158,13 @@ export function countTiles(bbox: TileBbox, minZoom: number, maxZoom: number): nu
|
||||
* The template is normalized first: this function is also reached with a raw
|
||||
* admin default rather than the value from the settings store, so the OSM
|
||||
* sharding rewrite has to happen here too.
|
||||
*
|
||||
* The CARTO key is appended here as well, and it has to be: the Workbox cache is
|
||||
* keyed on the whole URL including the query, so a tile prefetched without the
|
||||
* key is a tile the map never asks for.
|
||||
*/
|
||||
export function buildTileUrl(template: string, z: number, x: number, y: number): string {
|
||||
return normalizeTileUrl(template)
|
||||
export function buildTileUrl(template: string, z: number, x: number, y: number, cartoKey?: string): string {
|
||||
return withTileApiKey(normalizeTileUrl(template), cartoKey)
|
||||
.replace('{z}', String(z))
|
||||
.replace('{x}', String(x))
|
||||
.replace('{y}', String(y))
|
||||
@@ -225,6 +229,7 @@ export async function prefetchTiles(
|
||||
tileUrlTemplate: string,
|
||||
minZoom = 10,
|
||||
maxZoom = 16,
|
||||
cartoKey?: string,
|
||||
): Promise<number> {
|
||||
if (!navigator.onLine) return 0
|
||||
if (!('serviceWorker' in navigator) || !navigator.serviceWorker.controller) return 0
|
||||
@@ -246,7 +251,7 @@ export async function prefetchTiles(
|
||||
if (!navigator.onLine || !isAuthed()) return
|
||||
|
||||
const [z, x, y] = coords[cursor++]
|
||||
const url = buildTileUrl(tileUrlTemplate, z, x, y)
|
||||
const url = buildTileUrl(tileUrlTemplate, z, x, y, cartoKey)
|
||||
|
||||
if (cache && (await cache.match(url))) continue
|
||||
|
||||
@@ -312,6 +317,7 @@ export async function prefetchTilesForTrip(
|
||||
places: Place[],
|
||||
tileUrlTemplate?: string,
|
||||
force = false,
|
||||
cartoKey?: string,
|
||||
): Promise<void> {
|
||||
const template = tileUrlTemplate || DEFAULT_TILE_URL
|
||||
const bbox = computeBbox(places)
|
||||
@@ -333,7 +339,7 @@ export async function prefetchTilesForTrip(
|
||||
// tile providers that don't send CORS headers. To stop the browser evicting
|
||||
// these tiles under the inflated quota, we request persistent storage at app
|
||||
// init instead (sync/persistentStorage.ts).
|
||||
const fetched = await prefetchTiles(bbox, template, 10, 16)
|
||||
const fetched = await prefetchTiles(bbox, template, 10, 16, cartoKey)
|
||||
|
||||
// Update syncMeta with bbox and tile count
|
||||
const meta = await offlineDb.syncMeta.get(tripId)
|
||||
|
||||
@@ -218,6 +218,7 @@ export const tripSyncManager = {
|
||||
// Cache file blobs + map tiles in background (don't block syncAll)
|
||||
const cacheTiles = getOfflinePrefs().cacheTiles
|
||||
const tileUrl = useSettingsStore.getState().settings.map_tile_url || undefined
|
||||
const cartoKey = useSettingsStore.getState().settings.carto_api_key || undefined
|
||||
for (const trip of toSync) {
|
||||
if (!isAuthed()) return
|
||||
const files = await offlineDb.tripFiles.where('trip_id').equals(trip.id).toArray()
|
||||
@@ -233,7 +234,7 @@ export const tripSyncManager = {
|
||||
for (const trip of toSync) {
|
||||
if (!isAuthed() || !navigator.onLine) return
|
||||
const places = await offlineDb.places.where('trip_id').equals(trip.id).toArray()
|
||||
await prefetchTilesForTrip(trip.id, places, tileUrl).catch(console.error)
|
||||
await prefetchTilesForTrip(trip.id, places, tileUrl, undefined, cartoKey).catch(console.error)
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -289,12 +290,13 @@ export const tripSyncManager = {
|
||||
// 3) Map tiles — awaited, and only when the user opted to store them.
|
||||
if (getOfflinePrefs().cacheTiles) {
|
||||
const tileUrl = useSettingsStore.getState().settings.map_tile_url || undefined
|
||||
const cartoKey = useSettingsStore.getState().settings.carto_api_key || undefined
|
||||
i = 0
|
||||
for (const trip of toSync) {
|
||||
if (!isAuthed()) return 0
|
||||
onProgress?.({ phase: 'tiles', current: ++i, total, label: trip.title })
|
||||
const places = await offlineDb.places.where('trip_id').equals(trip.id).toArray()
|
||||
await prefetchTilesForTrip(trip.id, places, tileUrl, true).catch(console.error)
|
||||
await prefetchTilesForTrip(trip.id, places, tileUrl, true, cartoKey).catch(console.error)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user