mirror of
https://github.com/Freika/dawarich.git
synced 2026-01-10 01:01:39 -05:00
Remember enabled map layers for users
This commit is contained in:
parent
e7884b1f4f
commit
632f389ace
6 changed files with 169 additions and 50 deletions
File diff suppressed because one or more lines are too long
|
|
@ -30,7 +30,8 @@ class Api::V1::SettingsController < ApiController
|
|||
:time_threshold_minutes, :merge_threshold_minutes, :route_opacity,
|
||||
:preferred_map_layer, :points_rendering_mode, :live_map_enabled,
|
||||
:immich_url, :immich_api_key, :photoprism_url, :photoprism_api_key,
|
||||
:speed_colored_routes, :speed_color_scale, :fog_of_war_threshold
|
||||
:speed_colored_routes, :speed_color_scale, :fog_of_war_threshold,
|
||||
enabled_map_layers: []
|
||||
)
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -463,6 +463,9 @@ export default class extends BaseController {
|
|||
|
||||
// Add event listeners for overlay layer changes to keep routes/tracks selector in sync
|
||||
this.map.on('overlayadd', (event) => {
|
||||
// Save enabled layers whenever a layer is added
|
||||
this.saveEnabledLayers();
|
||||
|
||||
if (event.name === 'Routes') {
|
||||
this.handleRouteLayerToggle('routes');
|
||||
// Re-establish event handlers when routes are manually added
|
||||
|
|
@ -518,6 +521,9 @@ export default class extends BaseController {
|
|||
});
|
||||
|
||||
this.map.on('overlayremove', (event) => {
|
||||
// Save enabled layers whenever a layer is removed
|
||||
this.saveEnabledLayers();
|
||||
|
||||
if (event.name === 'Routes' || event.name === 'Tracks') {
|
||||
// Don't auto-switch when layers are manually turned off
|
||||
// Just update the radio button state to reflect current visibility
|
||||
|
|
@ -551,9 +557,12 @@ export default class extends BaseController {
|
|||
}
|
||||
|
||||
updatePreferredBaseLayer(selectedLayerName) {
|
||||
fetch(`/api/v1/settings?api_key=${this.apiKey}`, {
|
||||
fetch('/api/v1/settings', {
|
||||
method: 'PATCH',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': `Bearer ${this.apiKey}`
|
||||
},
|
||||
body: JSON.stringify({
|
||||
settings: {
|
||||
preferred_map_layer: selectedLayerName
|
||||
|
|
@ -570,6 +579,68 @@ export default class extends BaseController {
|
|||
});
|
||||
}
|
||||
|
||||
saveEnabledLayers() {
|
||||
const enabledLayers = [];
|
||||
const layerNames = [
|
||||
'Points', 'Routes', 'Tracks', 'Heatmap', 'Fog of War',
|
||||
'Scratch map', 'Areas', 'Photos', 'Suggested Visits', 'Confirmed Visits'
|
||||
];
|
||||
|
||||
const controlsLayer = {
|
||||
'Points': this.markersLayer,
|
||||
'Routes': this.polylinesLayer,
|
||||
'Tracks': this.tracksLayer,
|
||||
'Heatmap': this.heatmapLayer,
|
||||
'Fog of War': this.fogOverlay,
|
||||
'Scratch map': this.scratchLayerManager?.getLayer(),
|
||||
'Areas': this.areasLayer,
|
||||
'Photos': this.photoMarkers,
|
||||
'Suggested Visits': this.visitsManager?.getVisitCirclesLayer(),
|
||||
'Confirmed Visits': this.visitsManager?.getConfirmedVisitCirclesLayer()
|
||||
};
|
||||
|
||||
layerNames.forEach(name => {
|
||||
const layer = controlsLayer[name];
|
||||
if (layer && this.map.hasLayer(layer)) {
|
||||
enabledLayers.push(name);
|
||||
}
|
||||
});
|
||||
|
||||
// Add family member layers
|
||||
if (window.familyController && window.familyController.familyLayers) {
|
||||
Object.keys(window.familyController.familyLayers).forEach(memberName => {
|
||||
const layer = window.familyController.familyLayers[memberName];
|
||||
if (layer && this.map.hasLayer(layer)) {
|
||||
enabledLayers.push(memberName);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
fetch('/api/v1/settings', {
|
||||
method: 'PATCH',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': `Bearer ${this.apiKey}`
|
||||
},
|
||||
body: JSON.stringify({
|
||||
settings: {
|
||||
enabled_map_layers: enabledLayers
|
||||
},
|
||||
}),
|
||||
})
|
||||
.then((response) => response.json())
|
||||
.then((data) => {
|
||||
if (data.status === 'success') {
|
||||
console.log('Enabled layers saved:', enabledLayers);
|
||||
} else {
|
||||
console.error('Failed to save enabled layers:', data.message);
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error saving enabled layers:', error);
|
||||
});
|
||||
}
|
||||
|
||||
deletePoint(id, apiKey) {
|
||||
fetch(`/api/v1/points/${id}`, {
|
||||
method: 'DELETE',
|
||||
|
|
@ -910,9 +981,12 @@ export default class extends BaseController {
|
|||
const opacityValue = event.target.route_opacity.value.replace('%', '');
|
||||
const decimalOpacity = parseFloat(opacityValue) / 100;
|
||||
|
||||
fetch(`/api/v1/settings?api_key=${this.apiKey}`, {
|
||||
fetch('/api/v1/settings', {
|
||||
method: 'PATCH',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': `Bearer ${this.apiKey}`
|
||||
},
|
||||
body: JSON.stringify({
|
||||
settings: {
|
||||
route_opacity: decimalOpacity.toString(),
|
||||
|
|
@ -1297,45 +1371,80 @@ export default class extends BaseController {
|
|||
// Initialize layer visibility based on user settings or defaults
|
||||
// This method sets up the initial state of overlay layers
|
||||
|
||||
// Note: Don't automatically add layers to map here - let the layer control and user preferences handle it
|
||||
// The layer control will manage which layers are visible based on user interaction
|
||||
// Get enabled layers from user settings
|
||||
const enabledLayers = this.userSettings.enabled_map_layers || ['Points', 'Routes', 'Heatmap'];
|
||||
console.log('Initializing layers from settings:', enabledLayers);
|
||||
|
||||
// Initialize photos layer if user wants it visible
|
||||
if (this.userSettings.photos_enabled) {
|
||||
console.log('Photos layer enabled via user settings');
|
||||
const urlParams = new URLSearchParams(window.location.search);
|
||||
const startDate = urlParams.get('start_at') || new Date(Date.now() - 7 * 24 * 60 * 60 * 1000).toISOString();
|
||||
const endDate = urlParams.get('end_at') || new Date().toISOString();
|
||||
const controlsLayer = {
|
||||
'Points': this.markersLayer,
|
||||
'Routes': this.polylinesLayer,
|
||||
'Tracks': this.tracksLayer,
|
||||
'Heatmap': this.heatmapLayer,
|
||||
'Fog of War': this.fogOverlay,
|
||||
'Scratch map': this.scratchLayerManager?.getLayer(),
|
||||
'Areas': this.areasLayer,
|
||||
'Photos': this.photoMarkers,
|
||||
'Suggested Visits': this.visitsManager?.getVisitCirclesLayer(),
|
||||
'Confirmed Visits': this.visitsManager?.getConfirmedVisitCirclesLayer()
|
||||
};
|
||||
|
||||
console.log('Auto-fetching photos for date range:', { startDate, endDate });
|
||||
fetchAndDisplayPhotos({
|
||||
map: this.map,
|
||||
photoMarkers: this.photoMarkers,
|
||||
apiKey: this.apiKey,
|
||||
startDate: startDate,
|
||||
endDate: endDate,
|
||||
userSettings: this.userSettings
|
||||
// Add family member layers if available
|
||||
if (window.familyController && window.familyController.familyLayers) {
|
||||
Object.entries(window.familyController.familyLayers).forEach(([memberName, layer]) => {
|
||||
controlsLayer[memberName] = layer;
|
||||
});
|
||||
}
|
||||
|
||||
// Initialize fog of war if enabled in settings
|
||||
if (this.userSettings.fog_of_war_enabled) {
|
||||
this.updateFog(this.markers, this.clearFogRadius, this.fogLineThreshold);
|
||||
}
|
||||
// Apply saved layer preferences
|
||||
Object.entries(controlsLayer).forEach(([name, layer]) => {
|
||||
if (!layer) return;
|
||||
|
||||
// Initialize visits manager functionality
|
||||
// Check if any visits layers are enabled by default and load data
|
||||
if (this.visitsManager && typeof this.visitsManager.fetchAndDisplayVisits === 'function') {
|
||||
// Check if confirmed visits layer is enabled by default (it's added to map in constructor)
|
||||
const confirmedVisitsEnabled = this.map.hasLayer(this.visitsManager.getConfirmedVisitCirclesLayer());
|
||||
const shouldBeEnabled = enabledLayers.includes(name);
|
||||
const isCurrentlyEnabled = this.map.hasLayer(layer);
|
||||
|
||||
console.log('Visits initialization - confirmedVisitsEnabled:', confirmedVisitsEnabled);
|
||||
if (shouldBeEnabled && !isCurrentlyEnabled) {
|
||||
// Add layer to map
|
||||
layer.addTo(this.map);
|
||||
console.log(`Enabled layer: ${name}`);
|
||||
|
||||
if (confirmedVisitsEnabled) {
|
||||
console.log('Confirmed visits layer enabled by default - fetching visits data');
|
||||
this.visitsManager.fetchAndDisplayVisits();
|
||||
// Trigger special initialization for certain layers
|
||||
if (name === 'Photos') {
|
||||
const urlParams = new URLSearchParams(window.location.search);
|
||||
const startDate = urlParams.get('start_at') || new Date(Date.now() - 7 * 24 * 60 * 60 * 1000).toISOString();
|
||||
const endDate = urlParams.get('end_at') || new Date().toISOString();
|
||||
fetchAndDisplayPhotos({
|
||||
map: this.map,
|
||||
photoMarkers: this.photoMarkers,
|
||||
apiKey: this.apiKey,
|
||||
startDate: startDate,
|
||||
endDate: endDate,
|
||||
userSettings: this.userSettings
|
||||
});
|
||||
} else if (name === 'Fog of War') {
|
||||
this.updateFog(this.markers, this.clearFogRadius, this.fogLineThreshold);
|
||||
} else if (name === 'Suggested Visits' || name === 'Confirmed Visits') {
|
||||
if (this.visitsManager && typeof this.visitsManager.fetchAndDisplayVisits === 'function') {
|
||||
this.visitsManager.fetchAndDisplayVisits();
|
||||
}
|
||||
} else if (name === 'Scratch map') {
|
||||
if (this.scratchLayerManager) {
|
||||
this.scratchLayerManager.addToMap();
|
||||
}
|
||||
} else if (name === 'Routes') {
|
||||
// Re-establish event handlers for routes layer
|
||||
reestablishPolylineEventHandlers(this.polylinesLayer, this.map, this.userSettings, this.distanceUnit);
|
||||
} else if (name === 'Areas') {
|
||||
// Show draw control when Areas layer is enabled
|
||||
if (this.drawControl && !this.map._controlCorners.topleft.querySelector('.leaflet-draw')) {
|
||||
this.map.addControl(this.drawControl);
|
||||
}
|
||||
}
|
||||
} else if (!shouldBeEnabled && isCurrentlyEnabled) {
|
||||
// Remove layer from map
|
||||
this.map.removeLayer(layer);
|
||||
console.log(`Disabled layer: ${name}`);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
toggleRightPanel() {
|
||||
|
|
|
|||
|
|
@ -19,7 +19,8 @@ class Users::SafeSettings
|
|||
'photoprism_url' => nil,
|
||||
'photoprism_api_key' => nil,
|
||||
'maps' => { 'distance_unit' => 'km' },
|
||||
'visits_suggestions_enabled' => 'true'
|
||||
'visits_suggestions_enabled' => 'true',
|
||||
'enabled_map_layers' => ['Routes', 'Heatmap']
|
||||
}.freeze
|
||||
|
||||
def initialize(settings = {})
|
||||
|
|
@ -47,7 +48,8 @@ class Users::SafeSettings
|
|||
distance_unit: distance_unit,
|
||||
visits_suggestions_enabled: visits_suggestions_enabled?,
|
||||
speed_color_scale: speed_color_scale,
|
||||
fog_of_war_threshold: fog_of_war_threshold
|
||||
fog_of_war_threshold: fog_of_war_threshold,
|
||||
enabled_map_layers: enabled_map_layers
|
||||
}
|
||||
end
|
||||
# rubocop:enable Metrics/MethodLength
|
||||
|
|
@ -127,4 +129,8 @@ class Users::SafeSettings
|
|||
def fog_of_war_threshold
|
||||
settings['fog_of_war_threshold']
|
||||
end
|
||||
|
||||
def enabled_map_layers
|
||||
settings['enabled_map_layers']
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -24,21 +24,17 @@
|
|||
|
||||
<body class='h-screen overflow-hidden relative'>
|
||||
<!-- Fixed Navbar -->
|
||||
<div class='fixed w-full z-50 bg-base-100 shadow-md h-16'>
|
||||
<div class='fixed w-full z-40 bg-base-100 shadow-md h-16'>
|
||||
<div class='container mx-auto h-full w-full flex items-center'>
|
||||
<%= render 'shared/navbar' %>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Flash Messages - Fixed below navbar -->
|
||||
<div class='fixed top-16 w-full z-40 h-8'>
|
||||
<div class='container mx-auto px-5'>
|
||||
<%= render 'shared/flash' %>
|
||||
</div>
|
||||
<div class='container mx-auto px-5'>
|
||||
<%= render 'shared/flash' %>
|
||||
</div>
|
||||
|
||||
<!-- Date Navigation - Fixed below flash messages -->
|
||||
|
||||
<!-- Full Screen Map Container -->
|
||||
<div class='absolute top-40 left-0 right-0 bottom-0 w-full z-10 overflow-auto'>
|
||||
<%= yield %>
|
||||
|
|
|
|||
|
|
@ -29,7 +29,8 @@ RSpec.describe Users::SafeSettings do
|
|||
distance_unit: 'km',
|
||||
visits_suggestions_enabled: true,
|
||||
speed_color_scale: nil,
|
||||
fog_of_war_threshold: nil
|
||||
fog_of_war_threshold: nil,
|
||||
enabled_map_layers: ['Routes', 'Heatmap']
|
||||
}
|
||||
)
|
||||
end
|
||||
|
|
@ -53,7 +54,8 @@ RSpec.describe Users::SafeSettings do
|
|||
'photoprism_url' => 'https://photoprism.example.com',
|
||||
'photoprism_api_key' => 'photoprism-key',
|
||||
'maps' => { 'name' => 'custom', 'url' => 'https://custom.example.com' },
|
||||
'visits_suggestions_enabled' => false
|
||||
'visits_suggestions_enabled' => false,
|
||||
'enabled_map_layers' => ['Points', 'Routes', 'Areas', 'Photos']
|
||||
}
|
||||
end
|
||||
let(:safe_settings) { described_class.new(settings) }
|
||||
|
|
@ -76,7 +78,8 @@ RSpec.describe Users::SafeSettings do
|
|||
"photoprism_url" => "https://photoprism.example.com",
|
||||
"photoprism_api_key" => "photoprism-key",
|
||||
"maps" => { "name" => "custom", "url" => "https://custom.example.com" },
|
||||
"visits_suggestions_enabled" => false
|
||||
"visits_suggestions_enabled" => false,
|
||||
"enabled_map_layers" => ['Points', 'Routes', 'Areas', 'Photos']
|
||||
}
|
||||
)
|
||||
end
|
||||
|
|
@ -102,7 +105,8 @@ RSpec.describe Users::SafeSettings do
|
|||
distance_unit: nil,
|
||||
visits_suggestions_enabled: false,
|
||||
speed_color_scale: nil,
|
||||
fog_of_war_threshold: nil
|
||||
fog_of_war_threshold: nil,
|
||||
enabled_map_layers: ['Points', 'Routes', 'Areas', 'Photos']
|
||||
}
|
||||
)
|
||||
end
|
||||
|
|
@ -132,6 +136,7 @@ RSpec.describe Users::SafeSettings do
|
|||
expect(safe_settings.photoprism_api_key).to be_nil
|
||||
expect(safe_settings.maps).to eq({ "distance_unit" => "km" })
|
||||
expect(safe_settings.visits_suggestions_enabled?).to be true
|
||||
expect(safe_settings.enabled_map_layers).to eq(['Routes', 'Heatmap'])
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -153,7 +158,8 @@ RSpec.describe Users::SafeSettings do
|
|||
'photoprism_url' => 'https://photoprism.example.com',
|
||||
'photoprism_api_key' => 'photoprism-key',
|
||||
'maps' => { 'name' => 'custom', 'url' => 'https://custom.example.com' },
|
||||
'visits_suggestions_enabled' => false
|
||||
'visits_suggestions_enabled' => false,
|
||||
'enabled_map_layers' => ['Points', 'Tracks', 'Fog of War', 'Suggested Visits']
|
||||
}
|
||||
end
|
||||
|
||||
|
|
@ -174,6 +180,7 @@ RSpec.describe Users::SafeSettings do
|
|||
expect(safe_settings.photoprism_api_key).to eq('photoprism-key')
|
||||
expect(safe_settings.maps).to eq({ 'name' => 'custom', 'url' => 'https://custom.example.com' })
|
||||
expect(safe_settings.visits_suggestions_enabled?).to be false
|
||||
expect(safe_settings.enabled_map_layers).to eq(['Points', 'Tracks', 'Fog of War', 'Suggested Visits'])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
Loading…
Reference in a new issue