spotnotes.app/static/assets/js/map.js

202 lines
10 KiB
JavaScript
Raw Normal View History

window.SpotNotes = window.SpotNotes || {};
window.SpotNotes.initMap = function() {
// Default view (e.g., Oklahoma area if geolocation fails)
window.SpotNotes.map = L.map('map').setView([35.6528, -97.4781], 10);
// Ask for location
if (navigator.geolocation) {
navigator.geolocation.watchPosition((position) => {
const { latitude, longitude } = position.coords;
window.SpotNotes.userLocation = { lat: latitude, lng: longitude };
const centerBtn = document.getElementById('btn-center-user');
if (centerBtn) centerBtn.style.display = 'block';
if (window.SpotNotes.isTracking) {
window.SpotNotes.map.setView([latitude, longitude]);
} else if (!window.SpotNotes.userMarker && !new URLSearchParams(window.location.search).has('pad') && !new URLSearchParams(window.location.search).has('note')) {
window.SpotNotes.map.setView([latitude, longitude], 13);
console.log("Map centered on user location:", latitude, longitude);
}
// Add User Location Dot
if (window.SpotNotes.userMarker) {
window.SpotNotes.userMarker.setLatLng([latitude, longitude]);
} else {
window.SpotNotes.userMarker = L.circleMarker([latitude, longitude], {
radius: 8,
fillColor: "#3388ff",
color: "#fff",
weight: 2,
opacity: 1,
fillOpacity: 0.8
}).addTo(window.SpotNotes.map).bindPopup("You are here");
}
// Location-Based Browser Alerts
const userLatLng = L.latLng(latitude, longitude);
if (window.SpotNotes.activeNotes && window.SpotNotes.activeNotes.length > 0) {
window.SpotNotes.activeNotes.forEach(note => {
const noteLatLng = L.latLng(note.lat, note.lng);
if (userLatLng.distanceTo(noteLatLng) <= 50) {
if (!window.SpotNotes.notifiedNoteIds.has(note.id)) {
window.SpotNotes.notifiedNoteIds.add(note.id);
alert(`You are near a note!\n\n${note.title}\n${note.content}`);
}
}
});
}
}, (err) => {
console.warn("Geolocation error:", err);
}, {
enableHighAccuracy: true,
timeout: 10000,
maximumAge: 0
});
}
// Add OpenStreetMap tiles
L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', {
maxZoom: 19,
attribution: '&copy; <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a>'
}).addTo(window.SpotNotes.map);
// Listen for zoom changes to toggle note display
window.SpotNotes.map.on('zoomend', () => {
if (window.SpotNotes.activeNotes.length > 0) {
window.SpotNotes.renderMapMarkers();
}
});
window.SpotNotes.map.on('dragstart', window.SpotNotes.stopTracking);
window.SpotNotes.map.on('popupopen', window.SpotNotes.stopTracking);
// Initialize MarkerClusterGroup
window.SpotNotes.markerClusterGroup = L.markerClusterGroup({
disableClusteringAtZoom: 16
});
window.SpotNotes.map.addLayer(window.SpotNotes.markerClusterGroup);
};
window.SpotNotes.renderMapMarkers = function() {
// Clear existing
if (window.SpotNotes.markerClusterGroup) {
window.SpotNotes.markerClusterGroup.clearLayers();
}
// Also remove from map directly just in case (fallback)
window.SpotNotes.currentMarkers.forEach(marker => window.SpotNotes.map.removeLayer(marker));
window.SpotNotes.currentMarkers = [];
const zoom = window.SpotNotes.map.getZoom();
const showFull = zoom >= 14; // Zoom threshold
window.SpotNotes.activeNotes.forEach(note => {
if (!note || note.lat === undefined || note.lng === undefined || (note.lat === 0 && note.lng === 0)) {
return;
}
let marker;
let actionBtns = '';
// Determine permissions
2026-10-06 19:32:24 +00:00
const isAuthor = window.SpotNotes.currentUser && (note.user_id && note.user_id === window.SpotNotes.currentUser.id);
const isOwner = window.SpotNotes.currentUser && (note.owner_id ? note.owner_id === window.SpotNotes.currentUser.id : window.SpotNotes.activePadOwnerId === window.SpotNotes.currentUser.id);
2026-10-06 19:32:24 +00:00
const isAdmin = window.SpotNotes.currentUser && window.SpotNotes.currentUser.is_admin;
const isPublic = note.is_public !== undefined ? note.is_public : window.SpotNotes.activePadIsPublic;
2026-10-06 19:32:24 +00:00
const canModify = isAuthor || isOwner || isAdmin || ((note.notepad && note.notepad.can_write !== undefined ? note.notepad.can_write : window.SpotNotes.activePadCanWrite) && !isPublic);
2026-10-06 19:32:24 +00:00
if (canModify) {
const escapedTitle = window.SpotNotes.escapeHtml(note.title).replace(/'/g, "\\'").replace(/"/g, '&quot;');
const escapedContent = window.SpotNotes.escapeHtml(note.content).replace(/'/g, "\\'").replace(/"/g, '&quot;').replace(/\n/g, '\\n');
actionBtns = `<br><button onclick="openEditNoteModal(${note.id}, '${escapedTitle}', '${escapedContent}')" class="btn-note-edit">✏️ Edit</button><button onclick="window.SpotNotes.deleteNote(${note.id})" class="btn-note-delete">Delete</button>`;
} else if (isPublic) {
actionBtns = `<br><button onclick="window.SpotNotes.reportNote(${note.id})" class="btn-note-report">Report</button>`;
}
const shareToken = note.share_token || '';
const shareBtn = `<button onclick="window.SpotNotes.shareNote(${note.id}, '${shareToken}')" class="btn-note-share">🔗 Share</button>`;
const dateStr = new Date(note.created_at).toLocaleString();
const padColor = (note.notepad && note.notepad.color) ? note.notepad.color : '#3388ff';
const lockPrefix = note.is_encrypted ? '🔒 ' : '';
const directionsBtn = `<a href="https://www.google.com/maps/dir/?api=1&destination=${note.lat},${note.lng}" target="_blank" class="btn-note-directions" style="display:inline-block; margin-top:5px; margin-right:5px; font-size:0.8rem; text-decoration:none;">🗺️ Directions</a>`;
if (showFull) {
const icon = L.divIcon({
className: '',
html: `<div class="map-note-card">
<div class="popup-note-header" style="background-color: ${padColor}; color: white; padding: 6px 10px; font-weight: bold; border-radius: 4px 4px 0 0; text-shadow: 0 1px 2px rgba(0,0,0,0.15); overflow: hidden; text-overflow: ellipsis; white-space: nowrap;">
${lockPrefix}${window.SpotNotes.escapeHtml(note.title)}
</div>
<div style="padding: 10px; background: white; border-radius: 0 0 4px 4px; border: 1px solid #ddd; border-top: none;">
<div style="white-space: pre-wrap; margin-bottom: 6px;">${window.SpotNotes.escapeHtml(note.content)}</div>
<div style="font-size:0.7rem; color:#888; margin-top:4px;">${dateStr}</div>
<div style="margin-top: 6px;">${directionsBtn} ${shareBtn} ${actionBtns}</div>
</div>
</div>`,
iconSize: [200, 'auto'],
iconAnchor: [100, 0]
});
marker = L.marker([note.lat, note.lng], { icon: icon });
} else {
2026-06-02 04:22:59 +00:00
const teardropSvg = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 36" width="24" height="36"><path fill="${padColor}" d="M12 0C5.373 0 0 5.373 0 12c0 8.442 12 24 12 24s12-15.558 12-24c0-6.627-5.373-12-12-12zm0 17c-2.761 0-5-2.239-5-5s2.239-5 5-5 5 2.239 5 5-2.239 5-5 5z" stroke="white" stroke-width="1.5"/></svg>`;
const svgIcon = L.divIcon({
className: 'custom-div-icon',
2026-06-02 04:22:59 +00:00
html: teardropSvg,
iconSize: [24, 36],
iconAnchor: [12, 36],
popupAnchor: [0, -36]
});
marker = L.marker([note.lat, note.lng], { icon: svgIcon });
const popupHtml = `
<div class="popup-note-card">
<div class="popup-note-header" style="background-color: ${padColor}; color: white; padding: 8px 12px; font-weight: bold; text-shadow: 0 1px 2px rgba(0,0,0,0.15); overflow: hidden; text-overflow: ellipsis; white-space: nowrap;">
${window.SpotNotes.escapeHtml(note.title)}
</div>
<div class="popup-note-body">
<div style="white-space: pre-wrap; margin-bottom: 6px;">${window.SpotNotes.escapeHtml(note.content)}</div>
<div style="font-size:0.7rem; color:#888; margin-bottom: 6px;">${dateStr}</div>
<div style="margin-top: 6px;">${directionsBtn} ${shareBtn} ${actionBtns}</div>
</div>
</div>
`;
marker.bindPopup(popupHtml, { padding: [0, 0] });
}
const bumpZIndex = function() {
this.setZIndexOffset(window.SpotNotes.topZIndex++);
};
marker.on('click', bumpZIndex);
marker.on('mousedown', bumpZIndex);
if (window.SpotNotes.markerClusterGroup) {
window.SpotNotes.markerClusterGroup.addLayer(marker);
} else {
marker.addTo(window.SpotNotes.map);
}
window.SpotNotes.currentMarkers.push(marker);
});
};
window.SpotNotes.stopTracking = function() {
window.SpotNotes.isTracking = false;
window.SpotNotes.updateTrackingUI();
};
window.SpotNotes.updateTrackingUI = function() {
const btn = document.getElementById('btn-center-user');
if (btn) {
if (window.SpotNotes.isTracking) {
btn.textContent = "Tracking Location...";
btn.style.backgroundColor = "#28a745";
} else {
btn.textContent = "Target My Location";
btn.style.backgroundColor = "";
}
}
};