2026-04-20 23:36:02 +00:00
|
|
|
/**
|
|
|
|
|
* Deployment config loader.
|
|
|
|
|
*
|
|
|
|
|
* Fetches /api/config on startup and caches the result.
|
|
|
|
|
* Falls back to hardcoded defaults matching the home profile if the
|
|
|
|
|
* API is unavailable (backend restart, network issue).
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
const FALLBACK_CONFIG = {
|
|
|
|
|
profile: 'home',
|
|
|
|
|
region_name: 'North America',
|
|
|
|
|
tileset: {
|
2026-05-07 21:56:14 +00:00
|
|
|
url: '/tiles/planet/current.pmtiles',
|
2026-04-20 23:36:02 +00:00
|
|
|
bounds: [-168, 14, -52, 72],
|
|
|
|
|
max_zoom: 15,
|
|
|
|
|
attribution: 'Protomaps © OSM',
|
|
|
|
|
},
|
|
|
|
|
services: {
|
|
|
|
|
geocode: '/api/geocode',
|
|
|
|
|
reverse: '/api/reverse',
|
|
|
|
|
address_book: '/api/address_book',
|
|
|
|
|
valhalla: '/valhalla',
|
|
|
|
|
},
|
|
|
|
|
features: {
|
|
|
|
|
has_nominatim_details: false,
|
|
|
|
|
has_kiwix_wiki: false,
|
|
|
|
|
has_hillshade: false,
|
|
|
|
|
has_3d_terrain: false,
|
|
|
|
|
has_traffic_overlay: false,
|
|
|
|
|
has_landclass: false,
|
2026-04-22 18:52:53 +00:00
|
|
|
has_public_lands_layer: false,
|
2026-04-24 00:44:20 +00:00
|
|
|
has_contours: true,
|
2026-04-20 23:36:02 +00:00
|
|
|
has_address_book_write: false,
|
2026-04-30 16:43:30 +00:00
|
|
|
has_usfs_trails: false,
|
2026-04-30 20:40:44 +00:00
|
|
|
has_blm_trails: false,
|
Add contacts/phone book UI with search integration
New components:
- ContactModal.jsx: Save/edit overlay with form fields and soft delete
- ContactList.jsx: Contacts tab with filter, create, and tap-to-navigate
Modified:
- store.js: Add contacts slice (contacts, activeTab, editingContact)
- api.js: Add contacts API functions (fetch, create, update, delete, nearby)
- config.js: Add has_contacts fallback flag
- Panel.jsx: Routes/Contacts tab bar (only when has_contacts enabled)
- PlaceDetail.jsx: Save button opens ContactModal, proximity annotation
- SearchBar.jsx: Prepend matching contacts before Photon results
- App.jsx: Render ContactModal at top level
- index.css: Modal overlay, tab bar, contact list item styles
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-22 05:30:19 +00:00
|
|
|
has_contacts: false,
|
2026-04-20 23:36:02 +00:00
|
|
|
},
|
|
|
|
|
defaults: {
|
|
|
|
|
center: [42.5736, -114.6066],
|
|
|
|
|
zoom: 10,
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let _config = null
|
|
|
|
|
let _configPromise = null
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Fetch config from backend. Returns cached config on subsequent calls.
|
|
|
|
|
* Falls back to FALLBACK_CONFIG if API fails.
|
|
|
|
|
*/
|
|
|
|
|
export function loadConfig() {
|
|
|
|
|
if (_configPromise) return _configPromise
|
|
|
|
|
|
|
|
|
|
_configPromise = fetch('/api/config', { signal: AbortSignal.timeout(3000) })
|
|
|
|
|
.then((resp) => {
|
|
|
|
|
if (!resp.ok) throw new Error(`Config API returned ${resp.status}`)
|
|
|
|
|
return resp.json()
|
|
|
|
|
})
|
|
|
|
|
.then((data) => {
|
|
|
|
|
_config = data
|
|
|
|
|
console.log('[navi] Config loaded:', data.profile, `(${data.region_name})`)
|
|
|
|
|
console.log('[navi] Feature flags:', data.features)
|
|
|
|
|
return data
|
|
|
|
|
})
|
|
|
|
|
.catch((err) => {
|
|
|
|
|
console.warn('[navi] Config API unavailable, using fallback:', err.message)
|
|
|
|
|
_config = FALLBACK_CONFIG
|
|
|
|
|
return FALLBACK_CONFIG
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
return _configPromise
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Get the current config synchronously. Returns null if not yet loaded.
|
|
|
|
|
*/
|
|
|
|
|
export function getConfig() {
|
|
|
|
|
return _config
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Check a feature flag from the loaded config.
|
|
|
|
|
* @param {string} flag - Feature flag name (e.g. 'has_hillshade')
|
|
|
|
|
* @returns {boolean}
|
|
|
|
|
*/
|
|
|
|
|
export function hasFeature(flag) {
|
|
|
|
|
if (!_config) return false
|
|
|
|
|
return Boolean(_config.features?.[flag])
|
|
|
|
|
}
|