buhapp-telegram/public/js/views/map.js
root 21d281a1c6 Telegram WebApp: Mini App with map, chats, profile, reviews
- index.html with bottom tabbar (Map/Chats/Profile/Settings)
- Telegram WebApp SDK auth via /api/v1/auth/telegram
- Consent gate on first login (4 checkboxes)
- Map view with Leaflet (OSM) + nearby users + radius selector
- Chat list + single chat with WebSocket realtime
- Profile editing (drinks, activities, purposes)
- Review screen with stars + anonymous toggle
- User reviews list with stats
- User profile (public) with rating, write/block/report
- Legal pages (TERMS / PRIVACY / DISCLAIMER)
2026-08-20 17:43:16 +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://{s}.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;