dawarich/app/javascript/maps_maplibre/utils/settings_manager.js

273 lines
7.7 KiB
JavaScript
Raw Normal View History

2025-11-20 16:36:58 -05:00
/**
* Settings manager for persisting user preferences
2025-11-25 14:27:18 -05:00
* Supports both localStorage (fallback) and backend API (primary)
2025-11-20 16:36:58 -05:00
*/
2025-12-02 16:56:53 -05:00
const STORAGE_KEY = 'dawarich-maps-maplibre-settings'
2025-11-20 16:36:58 -05:00
const DEFAULT_SETTINGS = {
2025-11-25 14:27:18 -05:00
mapStyle: 'light',
2025-11-26 13:40:12 -05:00
enabledMapLayers: ['Points', 'Routes'], // Compatible with v1 map
// Advanced settings
routeOpacity: 1.0,
fogOfWarRadius: 1000,
fogOfWarThreshold: 1,
metersBetweenRoutes: 500,
minutesBetweenRoutes: 60,
pointsRenderingMode: 'raw',
speedColoredRoutes: false
}
// Mapping between v2 layer names and v1 layer names in enabled_map_layers array
const LAYER_NAME_MAP = {
'Points': 'pointsVisible',
'Routes': 'routesVisible',
'Heatmap': 'heatmapEnabled',
'Visits': 'visitsEnabled',
'Photos': 'photosEnabled',
'Areas': 'areasEnabled',
'Tracks': 'tracksEnabled',
'Fog of War': 'fogEnabled',
'Scratch map': 'scratchEnabled'
2025-11-20 16:36:58 -05:00
}
2025-11-25 14:27:18 -05:00
// Mapping between frontend settings and backend API keys
const BACKEND_SETTINGS_MAP = {
2025-12-01 15:56:06 -05:00
mapStyle: 'maps_maplibre_style',
2025-11-26 13:40:12 -05:00
enabledMapLayers: 'enabled_map_layers'
2025-11-25 14:27:18 -05:00
}
2025-11-20 16:36:58 -05:00
export class SettingsManager {
2025-11-25 14:27:18 -05:00
static apiKey = null
2025-11-20 16:36:58 -05:00
/**
2025-11-25 14:27:18 -05:00
* Initialize settings manager with API key
* @param {string} apiKey - User's API key for backend requests
*/
static initialize(apiKey) {
this.apiKey = apiKey
}
/**
* Get all settings (localStorage first, then merge with defaults)
2025-11-26 13:40:12 -05:00
* Converts enabled_map_layers array to individual boolean flags
2025-11-20 16:36:58 -05:00
* @returns {Object} Settings object
*/
static getSettings() {
try {
const stored = localStorage.getItem(STORAGE_KEY)
2025-11-26 13:40:12 -05:00
const settings = stored ? { ...DEFAULT_SETTINGS, ...JSON.parse(stored) } : DEFAULT_SETTINGS
// Convert enabled_map_layers array to individual boolean flags
return this._expandLayerSettings(settings)
2025-11-20 16:36:58 -05:00
} catch (error) {
console.error('Failed to load settings:', error)
return DEFAULT_SETTINGS
}
}
2025-11-26 13:40:12 -05:00
/**
* Convert enabled_map_layers array to individual boolean flags
* @param {Object} settings - Settings with enabledMapLayers array
* @returns {Object} Settings with individual layer booleans
*/
static _expandLayerSettings(settings) {
const enabledLayers = settings.enabledMapLayers || []
// Set boolean flags based on array contents
Object.entries(LAYER_NAME_MAP).forEach(([layerName, settingKey]) => {
settings[settingKey] = enabledLayers.includes(layerName)
})
return settings
}
/**
* Convert individual boolean flags to enabled_map_layers array
* @param {Object} settings - Settings with individual layer booleans
* @returns {Array} Array of enabled layer names
*/
static _collapseLayerSettings(settings) {
const enabledLayers = []
Object.entries(LAYER_NAME_MAP).forEach(([layerName, settingKey]) => {
if (settings[settingKey] === true) {
enabledLayers.push(layerName)
}
})
return enabledLayers
}
2025-11-20 16:36:58 -05:00
/**
2025-11-25 14:27:18 -05:00
* Load settings from backend API
* @returns {Promise<Object>} Settings object from backend
*/
static async loadFromBackend() {
if (!this.apiKey) {
console.warn('[Settings] API key not set, cannot load from backend')
return null
}
try {
const response = await fetch('/api/v1/settings', {
headers: {
'Authorization': `Bearer ${this.apiKey}`,
'Content-Type': 'application/json'
}
})
if (!response.ok) {
throw new Error(`Failed to load settings: ${response.status}`)
}
const data = await response.json()
const backendSettings = data.settings
// Convert backend settings to frontend format
const frontendSettings = {}
Object.entries(BACKEND_SETTINGS_MAP).forEach(([frontendKey, backendKey]) => {
if (backendKey in backendSettings) {
frontendSettings[frontendKey] = backendSettings[backendKey]
}
})
2025-11-26 13:40:12 -05:00
// Merge with defaults, but prioritize backend's enabled_map_layers completely
2025-11-25 14:27:18 -05:00
const mergedSettings = { ...DEFAULT_SETTINGS, ...frontendSettings }
2025-11-26 13:40:12 -05:00
// If backend has enabled_map_layers, use it as-is (don't merge with defaults)
if (backendSettings.enabled_map_layers) {
mergedSettings.enabledMapLayers = backendSettings.enabled_map_layers
}
// Convert enabled_map_layers array to individual boolean flags
const expandedSettings = this._expandLayerSettings(mergedSettings)
// Save to localStorage
this.saveToLocalStorage(expandedSettings)
return expandedSettings
2025-11-25 14:27:18 -05:00
} catch (error) {
console.error('[Settings] Failed to load from backend:', error)
return null
}
}
/**
* Save all settings to localStorage
2025-11-20 16:36:58 -05:00
* @param {Object} settings - Settings object
*/
2025-11-25 14:27:18 -05:00
static saveToLocalStorage(settings) {
2025-11-20 16:36:58 -05:00
try {
localStorage.setItem(STORAGE_KEY, JSON.stringify(settings))
} catch (error) {
2025-11-25 14:27:18 -05:00
console.error('Failed to save settings to localStorage:', error)
}
}
/**
* Save settings to backend API
* @param {Object} settings - Settings to save
* @returns {Promise<boolean>} Success status
*/
static async saveToBackend(settings) {
if (!this.apiKey) {
console.warn('[Settings] API key not set, cannot save to backend')
return false
}
try {
2025-11-26 13:40:12 -05:00
// Convert individual layer booleans to enabled_map_layers array
const enabledMapLayers = this._collapseLayerSettings(settings)
2025-11-25 14:27:18 -05:00
// Convert frontend settings to backend format
const backendSettings = {}
Object.entries(BACKEND_SETTINGS_MAP).forEach(([frontendKey, backendKey]) => {
2025-11-26 13:40:12 -05:00
if (frontendKey === 'enabledMapLayers') {
// Use the collapsed array
backendSettings[backendKey] = enabledMapLayers
} else if (frontendKey in settings) {
2025-11-25 14:27:18 -05:00
backendSettings[backendKey] = settings[frontendKey]
}
})
const response = await fetch('/api/v1/settings', {
method: 'PATCH',
headers: {
'Authorization': `Bearer ${this.apiKey}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({ settings: backendSettings })
})
if (!response.ok) {
throw new Error(`Failed to save settings: ${response.status}`)
}
2025-11-26 13:40:12 -05:00
console.log('[Settings] Saved to backend successfully:', backendSettings)
2025-11-25 14:27:18 -05:00
return true
} catch (error) {
console.error('[Settings] Failed to save to backend:', error)
return false
2025-11-20 16:36:58 -05:00
}
}
/**
* Get a specific setting
* @param {string} key - Setting key
* @returns {*} Setting value
*/
static getSetting(key) {
return this.getSettings()[key]
}
/**
2025-11-25 14:27:18 -05:00
* Update a specific setting (saves to both localStorage and backend)
2025-11-20 16:36:58 -05:00
* @param {string} key - Setting key
* @param {*} value - New value
*/
2025-11-25 14:27:18 -05:00
static async updateSetting(key, value) {
2025-11-20 16:36:58 -05:00
const settings = this.getSettings()
settings[key] = value
2025-11-25 14:27:18 -05:00
// Save to localStorage immediately
this.saveToLocalStorage(settings)
// Save to backend (non-blocking)
this.saveToBackend(settings).catch(error => {
console.warn('[Settings] Backend save failed, but localStorage updated:', error)
})
2025-11-20 16:36:58 -05:00
}
/**
* Reset to defaults
*/
static resetToDefaults() {
try {
localStorage.removeItem(STORAGE_KEY)
2025-11-25 14:27:18 -05:00
// Also reset on backend
if (this.apiKey) {
this.saveToBackend(DEFAULT_SETTINGS).catch(error => {
console.warn('[Settings] Failed to reset backend settings:', error)
})
}
2025-11-20 16:36:58 -05:00
} catch (error) {
console.error('Failed to reset settings:', error)
}
}
2025-11-25 14:27:18 -05:00
/**
* Sync settings: load from backend and merge with localStorage
* Call this on app initialization
* @returns {Promise<Object>} Merged settings
*/
static async sync() {
const backendSettings = await this.loadFromBackend()
if (backendSettings) {
return backendSettings
}
return this.getSettings()
}
2025-11-20 16:36:58 -05:00
}