mirror of
https://github.com/Freika/dawarich.git
synced 2026-01-09 08:47:11 -05:00
Save enabled tag layers
This commit is contained in:
parent
a33373ae7c
commit
e965838f14
3 changed files with 103 additions and 36 deletions
|
|
@ -25,7 +25,7 @@ OIDC_REDIRECT_URI=https://your-dawarich-url.com/users/auth/openid_connect/callba
|
|||
- User can create and manage tags for places.
|
||||
- User can enable or disable places layers on the map to show/hide all or just some of their visited places based on tags.
|
||||
- User can define privacy zones around places with specific tags to hide map data within a certain radius.
|
||||
- If user has a place tagged with a tag named "Home" (case insensitive), and this place doesn't have a privacy zone defined, this place will be used as home location for days with no tracked data.
|
||||
- If user has a place tagged with a tag named "Home" (case insensitive), and this place doesn't have a privacy zone defined, this place will be used as home location for days with no tracked data. #1659 #1575
|
||||
|
||||
## Fixed
|
||||
|
||||
|
|
|
|||
|
|
@ -488,10 +488,16 @@ export default class extends BaseController {
|
|||
if (!this.placesFilteredLayers) {
|
||||
this.placesFilteredLayers = {};
|
||||
}
|
||||
// Store mapping of tag IDs to layers for persistence
|
||||
if (!this.tagLayerMapping) {
|
||||
this.tagLayerMapping = {};
|
||||
}
|
||||
|
||||
// Create Untagged layer
|
||||
const untaggedLayer = this.placesManager?.createFilteredLayer([]) || L.layerGroup();
|
||||
this.placesFilteredLayers['Untagged'] = untaggedLayer;
|
||||
// Store layer reference with special ID for untagged
|
||||
untaggedLayer._placeTagId = 'untagged';
|
||||
|
||||
const placesChildren = [
|
||||
{
|
||||
|
|
@ -507,6 +513,10 @@ export default class extends BaseController {
|
|||
const label = `${icon} #${tag.name}`;
|
||||
const tagLayer = this.placesManager?.createFilteredLayer([tag.id]) || L.layerGroup();
|
||||
this.placesFilteredLayers[label] = tagLayer;
|
||||
// Store tag ID on the layer itself for easy identification
|
||||
tagLayer._placeTagId = tag.id;
|
||||
// Store in mapping for lookup by ID
|
||||
this.tagLayerMapping[tag.id] = { layer: tagLayer, label: label };
|
||||
placesChildren.push({
|
||||
label: label,
|
||||
layer: tagLayer
|
||||
|
|
@ -660,7 +670,7 @@ export default class extends BaseController {
|
|||
endDate: endDate,
|
||||
userSettings: this.userSettings
|
||||
});
|
||||
} else if (event.name === 'Suggested Visits' || event.name === 'Confirmed Visits') {
|
||||
} else if (event.name === 'Suggested' || event.name === 'Confirmed') {
|
||||
// Load visits when layer is enabled
|
||||
console.log(`${event.name} layer enabled via layer control`);
|
||||
if (this.visitsManager && typeof this.visitsManager.fetchAndDisplayVisits === 'function') {
|
||||
|
|
@ -703,9 +713,9 @@ export default class extends BaseController {
|
|||
if (this.drawControl && this.map._controlCorners.topleft.querySelector('.leaflet-draw')) {
|
||||
this.map.removeControl(this.drawControl);
|
||||
}
|
||||
} else if (event.name === 'Suggested Visits') {
|
||||
} else if (event.name === 'Suggested') {
|
||||
// Clear suggested visits when layer is disabled
|
||||
console.log('Suggested Visits layer disabled via layer control');
|
||||
console.log('Suggested layer disabled via layer control');
|
||||
if (this.visitsManager) {
|
||||
// Clear the visit circles when layer is disabled
|
||||
this.visitsManager.visitCircles.clearLayers();
|
||||
|
|
@ -758,19 +768,34 @@ export default class extends BaseController {
|
|||
saveEnabledLayers() {
|
||||
const enabledLayers = [];
|
||||
|
||||
// Get all checked inputs from the tree control
|
||||
const layerControl = document.querySelector('.leaflet-control-layers');
|
||||
if (layerControl) {
|
||||
const inputs = layerControl.querySelectorAll('input[type="checkbox"]:checked');
|
||||
inputs.forEach(input => {
|
||||
// Get the label text for this checkbox
|
||||
const label = input.closest('label') || input.nextElementSibling;
|
||||
if (label) {
|
||||
const layerName = label.textContent.trim();
|
||||
// Skip group headers that might have checkboxes
|
||||
if (layerName && !layerName.includes('Map Styles') && !layerName.includes('Layers')) {
|
||||
enabledLayers.push(layerName);
|
||||
}
|
||||
// Iterate through all layers on the map to determine which are enabled
|
||||
// This is more reliable than parsing the DOM
|
||||
const layersToCheck = {
|
||||
'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': this.visitsManager?.getVisitCirclesLayer(),
|
||||
'Confirmed': this.visitsManager?.getConfirmedVisitCirclesLayer(),
|
||||
'Family Members': window.familyMembersController?.familyMarkersLayer
|
||||
};
|
||||
|
||||
// Check standard layers
|
||||
Object.entries(layersToCheck).forEach(([name, layer]) => {
|
||||
if (layer && this.map.hasLayer(layer)) {
|
||||
enabledLayers.push(name);
|
||||
}
|
||||
});
|
||||
|
||||
// Check place tag layers - save as "place_tag:ID" format
|
||||
if (this.placesFilteredLayers) {
|
||||
Object.values(this.placesFilteredLayers).forEach(layer => {
|
||||
if (layer && this.map.hasLayer(layer) && layer._placeTagId !== undefined) {
|
||||
enabledLayers.push(`place_tag:${layer._placeTagId}`);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
@ -1666,6 +1691,7 @@ export default class extends BaseController {
|
|||
const enabledLayers = this.userSettings.enabled_map_layers || ['Points', 'Routes', 'Heatmap'];
|
||||
console.log('Initializing layers from settings:', enabledLayers);
|
||||
|
||||
// Standard layers mapping
|
||||
const controlsLayer = {
|
||||
'Points': this.markersLayer,
|
||||
'Routes': this.polylinesLayer,
|
||||
|
|
@ -1675,14 +1701,12 @@ export default class extends BaseController {
|
|||
'Scratch map': this.scratchLayerManager?.getLayer(),
|
||||
'Areas': this.areasLayer,
|
||||
'Photos': this.photoMarkers,
|
||||
'Suggested Visits': this.visitsManager?.getVisitCirclesLayer(),
|
||||
'Confirmed Visits': this.visitsManager?.getConfirmedVisitCirclesLayer(),
|
||||
'Family Members': window.familyMembersController?.familyMarkersLayer,
|
||||
// Add Places filtered layers
|
||||
...this.placesFilteredLayers || {}
|
||||
'Suggested': this.visitsManager?.getVisitCirclesLayer(),
|
||||
'Confirmed': this.visitsManager?.getConfirmedVisitCirclesLayer(),
|
||||
'Family Members': window.familyMembersController?.familyMarkersLayer
|
||||
};
|
||||
|
||||
// Apply saved layer preferences
|
||||
// Apply saved layer preferences for standard layers
|
||||
Object.entries(controlsLayer).forEach(([name, layer]) => {
|
||||
if (!layer) {
|
||||
if (enabledLayers.includes(name)) {
|
||||
|
|
@ -1723,7 +1747,7 @@ export default class extends BaseController {
|
|||
});
|
||||
} else if (name === 'Fog of War') {
|
||||
this.updateFog(this.markers, this.clearFogRadius, this.fogLineThreshold);
|
||||
} else if (name === 'Suggested Visits' || name === 'Confirmed Visits') {
|
||||
} else if (name === 'Suggested' || name === 'Confirmed') {
|
||||
if (this.visitsManager && typeof this.visitsManager.fetchAndDisplayVisits === 'function') {
|
||||
this.visitsManager.fetchAndDisplayVisits();
|
||||
}
|
||||
|
|
@ -1752,6 +1776,30 @@ export default class extends BaseController {
|
|||
}
|
||||
});
|
||||
|
||||
// Handle place tag layers (format: "place_tag:ID" or "place_tag:untagged")
|
||||
enabledLayers.forEach(layerKey => {
|
||||
if (layerKey.startsWith('place_tag:')) {
|
||||
const tagId = layerKey.replace('place_tag:', '');
|
||||
let layer;
|
||||
|
||||
if (tagId === 'untagged') {
|
||||
// Find untagged layer
|
||||
layer = Object.values(this.placesFilteredLayers || {}).find(l => l._placeTagId === 'untagged');
|
||||
} else {
|
||||
// Find layer by tag ID
|
||||
const tagIdNum = parseInt(tagId);
|
||||
layer = Object.values(this.placesFilteredLayers || {}).find(l => l._placeTagId === tagIdNum);
|
||||
}
|
||||
|
||||
if (layer && !this.map.hasLayer(layer)) {
|
||||
this.isRestoringLayers = true;
|
||||
layer.addTo(this.map);
|
||||
console.log(`Enabled place tag layer: ${tagId}`);
|
||||
setTimeout(() => { this.isRestoringLayers = false; }, 100);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Update the tree control checkboxes to reflect the layer states
|
||||
// Wait a bit for the tree control to be fully initialized
|
||||
setTimeout(() => {
|
||||
|
|
@ -1766,13 +1814,32 @@ export default class extends BaseController {
|
|||
return;
|
||||
}
|
||||
|
||||
// Extract place tag IDs from enabledLayers
|
||||
const enabledTagIds = new Set();
|
||||
enabledLayers.forEach(key => {
|
||||
if (key.startsWith('place_tag:')) {
|
||||
const tagId = key.replace('place_tag:', '');
|
||||
enabledTagIds.add(tagId === 'untagged' ? 'untagged' : parseInt(tagId));
|
||||
}
|
||||
});
|
||||
|
||||
// Find and check/uncheck all layer checkboxes based on saved state
|
||||
const inputs = layerControl.querySelectorAll('input[type="checkbox"]');
|
||||
inputs.forEach(input => {
|
||||
const label = input.closest('label') || input.nextElementSibling;
|
||||
if (label) {
|
||||
const layerName = label.textContent.trim();
|
||||
const shouldBeEnabled = enabledLayers.includes(layerName);
|
||||
|
||||
// Check if this is a standard layer
|
||||
let shouldBeEnabled = enabledLayers.includes(layerName);
|
||||
|
||||
// Check if this is a place tag layer by finding the layer object
|
||||
if (!shouldBeEnabled && this.placesFilteredLayers) {
|
||||
const placeLayer = this.placesFilteredLayers[layerName];
|
||||
if (placeLayer && placeLayer._placeTagId !== undefined) {
|
||||
shouldBeEnabled = enabledTagIds.has(placeLayer._placeTagId);
|
||||
}
|
||||
}
|
||||
|
||||
// Skip group headers that might have checkboxes
|
||||
if (layerName && !layerName.includes('Map Styles') && !layerName.includes('Layers')) {
|
||||
|
|
|
|||
|
|
@ -150,6 +150,17 @@ class User < ApplicationRecord # rubocop:disable Metrics/ClassLength
|
|||
points.where.not(city: [nil, '']).distinct.pluck(:city).compact
|
||||
end
|
||||
|
||||
def home_place_coordinates
|
||||
home_tag = tags.find_by('LOWER(name) = ?', 'home')
|
||||
return nil unless home_tag
|
||||
return nil if home_tag.privacy_zone?
|
||||
|
||||
home_place = home_tag.places.first
|
||||
return nil unless home_place
|
||||
|
||||
[home_place.latitude, home_place.longitude]
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def create_api_key
|
||||
|
|
@ -187,15 +198,4 @@ class User < ApplicationRecord # rubocop:disable Metrics/ClassLength
|
|||
Users::MailerSendingJob.set(wait: 9.days).perform_later(id, 'post_trial_reminder_early')
|
||||
Users::MailerSendingJob.set(wait: 14.days).perform_later(id, 'post_trial_reminder_late')
|
||||
end
|
||||
|
||||
def home_place_coordinates
|
||||
home_tag = tags.find_by('LOWER(name) = ?', 'home')
|
||||
return nil unless home_tag
|
||||
return nil if home_tag.privacy_zone?
|
||||
|
||||
home_place = home_tag.places.first
|
||||
return nil unless home_place
|
||||
|
||||
[home_place.latitude, home_place.longitude]
|
||||
end
|
||||
end
|
||||
|
|
|
|||
Loading…
Reference in a new issue