buhapp-telegram/public/js/views/map.js
root 31c53cd8d5 Fix: leaflet.js local, consent status check, OSM direct tile URL
- vendor/leaflet.js (no more CDN dependency for JS)
- direct tile.openstreetmap.org (was {s}.tile)
- backend GET /api/v1/auth/consents (check if all accepted)
- frontend ConsentApi.status() before showing overlay
- only show overlay once
2026-08-20 18:43:19 +00:00

127 lines
4.6 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// Map view — главный экран
const MapView = {
map: null,
markers: [],
radius: 5,
visible: true,
nearby: [],
async render(root) {
root.innerHTML = `
<div class="view">
<h1>🗺️ Карта</h1>
<div class="card" id="my-info">
<div class="row">
<div>
<strong>Я на карте</strong>
<div class="privacy-note">Другие пользователи видят твоё местоположение ±300м</div>
</div>
<label class="switch">
<input type="checkbox" id="visible-toggle" ${this.visible ? 'checked' : ''}>
<span class="slider"></span>
</label>
</div>
</div>
<div id="map" style="height:400px;border-radius:12px;overflow:hidden;margin-bottom:12px;"></div>
<div class="row">
<label>Радиус: <strong id="radius-label">${this.radius} км</strong></label>
<select id="radius-sel" style="background:var(--bg-elev);color:var(--text);border:1px solid var(--border);border-radius:8px;padding:6px;">
<option value="1">1 км</option>
<option value="5" selected>5 км</option>
<option value="10">10 км</option>
<option value="25">25 км</option>
<option value="50">50 км</option>
</select>
</div>
<div id="nearby-count" class="sub">—</div>
</div>
`;
if (!this.map) {
this.map = L.map('map').setView([55.7558, 37.6173], 12);
L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: '© OpenStreetMap',
maxZoom: 19,
}).addTo(this.map);
}
setTimeout(() => this.map.invalidateSize(), 100);
document.getElementById('visible-toggle').addEventListener('change', async (e) => {
this.visible = e.target.checked;
try {
await API.Auth.setVisibility(this.visible);
this.refreshLocation();
} catch (err) {
API.Store && console.warn(err);
TG_APP.showAlert('Не удалось обновить видимость');
}
});
document.getElementById('radius-sel').addEventListener('change', (e) => {
this.radius = parseInt(e.target.value, 10);
document.getElementById('radius-label').textContent = this.radius + ' км';
this.refreshNearby();
});
this.refreshLocation();
},
async refreshLocation() {
if (!navigator.geolocation) {
document.getElementById('nearby-count').textContent = 'Геолокация недоступна';
return;
}
navigator.geolocation.getCurrentPosition(
async (pos) => {
const lat = pos.coords.latitude;
const lng = pos.coords.longitude;
try {
await API.Auth.updateLocation(lat, lng, this.visible);
} catch (e) {
console.warn(e);
}
this.map.setView([lat, lng], 13);
L.circle([lat, lng], {
radius: this.radius * 1000,
color: '#FF6B35', fillColor: '#FF6B35', fillOpacity: 0.1, weight: 2,
}).addTo(this.map);
this.refreshNearby();
},
(err) => {
document.getElementById('nearby-count').textContent = 'Нет доступа к GPS: ' + err.message;
},
{ enableHighAccuracy: true, timeout: 15000 }
);
},
async refreshNearby() {
if (!navigator.geolocation) return;
navigator.geolocation.getCurrentPosition(async (pos) => {
try {
const r = await API.Auth.searchNearby(pos.coords.latitude, pos.coords.longitude, this.radius);
this.nearby = r.results || [];
document.getElementById('nearby-count').textContent = this.nearby.length + ' чел. рядом';
// удалить старые маркеры (кроме своего круга)
this.markers.forEach(m => this.map.removeLayer(m));
this.markers = [];
this.nearby.forEach((n) => {
const icon = L.divIcon({
className: 'custom-marker',
html: `<div style="width:36px;height:36px;border-radius:50%;background:#FF6B35;color:white;display:flex;align-items:center;justify-content:center;font-weight:700;border:2px solid white;">${escapeHtml((n.name || '?')[0])}</div>`,
iconSize: [36, 36],
});
const m = L.marker([n.lat, n.lng], { icon }).addTo(this.map);
m.bindTooltip(n.name + ' • ' + (n.distance_m / 1000).toFixed(1) + ' км', { direction: 'top' });
m.on('click', () => App.openUserProfile(n.user_id, n.name));
this.markers.push(m);
});
} catch (e) {
console.warn(e);
}
});
},
};
window.MapView = MapView;