diff --git a/frontend/css/styles.css b/frontend/css/styles.css index d79417f..276915a 100644 --- a/frontend/css/styles.css +++ b/frontend/css/styles.css @@ -1318,6 +1318,7 @@ select { /* Left-hand group (bottom-left), opposite the right control stack: SMS + live toggle. */ .rec-sms-btn { left: 8px; bottom: 48px; font-size: 11px; } .rec-live-btn { left: 8px; bottom: 86px; font-size: 10px; } +.rec-share-btn { left: 8px; bottom: 124px; font-size: 11px; } /* Route editor: danger indicator (fords / rough tracks the route passes). */ .re-danger { color: #ff7043; font-size: 13px; font-weight: 600; min-height: 16px; margin: 2px 0; text-align: center; } /* Dark map: invert ONLY the tile layer (track, route, puck keep their colours). diff --git a/frontend/js/main.js b/frontend/js/main.js index 38e1778..e77a76d 100644 --- a/frontend/js/main.js +++ b/frontend/js/main.js @@ -4875,6 +4875,36 @@ async function sendLocationSms() { } catch (e) { appAlert("Ошибка отправки: " + e); } } +// "Отдать смотрящему": hand the viewer the active route + ridden roads + track-so-far. Sent to +// the server's RAM relay (NOT stored on its disk); the viewer pulls it once into their own +// localStorage. One-shot; press again to resend (e.g. after the track grew). +async function shareRideContext() { + let url = (localStorage.getItem("baldav_live_url") || "").trim(); + let token = (localStorage.getItem("baldav_live_token") || "").trim(); + if (!url || !token) { appAlert("Сначала задай адрес и токен трансляции в профиле"); return; } + let shareUrl = url.endsWith("/push") ? url.slice(0, -5) + "/share" : url.replace(/\/[^/]*$/, "/share"); + let track = (GPS.recording.points || []).map(p => [+p.lat, +p.lon]); + let route = []; + try { + let rid = localStorage.getItem("baldav_active_route"); + if (rid) { let r = await DB.getRoute(rid); if (r && r.points) route = r.points.map(p => [+p.lat, +p.lon]); } + } catch (e) {} + let roads = []; + try { + let segs = buildRoadSegments(await DB.getRoadPoints()); + roads = segs.map(s => ({ p: s.pts.map(q => [+q.lat, +q.lon]), t: s.atype || "b" })); + } catch (e) {} + showToast("Отдаю смотрящему..."); + try { + let raw = await window.__TAURI__.core.invoke("http_request", { + url: shareUrl, method: "POST", body: JSON.stringify({ token, route, roads, track }) + }); + let res = JSON.parse(raw || "{}"); + if (res.ok) showToast(`Отдано: маршрут ${route.length}, дороги ${roads.length}, трек ${track.length}`); + else appAlert("Не отдалось: " + (res.error || "ошибка")); + } catch (e) { appAlert("Не отдалось: " + (e.message || e)); } +} + async function renderRecordingInline(container) { if (!GPS.recording.active) { renderScreen("gps"); return; } @@ -5161,6 +5191,14 @@ async function renderRecordingInline(container) { smsBtn.onclick = () => sendLocationSms(); btnWrap.appendChild(smsBtn); + // Hand the viewer the route + ridden roads + track (via the server's RAM relay, not its disk). + let shareBtn = document.createElement("button"); + shareBtn.className = "rec-map-btn rec-share-btn"; + shareBtn.textContent = "Отд"; + shareBtn.title = "Отдать смотрящему: маршрут, проезженные дороги и пройденный трек"; + shareBtn.onclick = () => shareRideContext(); + btnWrap.appendChild(shareBtn); + // Roads toggle button. Roads are viewport-culled onto a canvas pane (only the segments in // view are drawn, redrawn on pan/zoom) so a huge road network doesn't jank the WebView. let roadsBtn = document.createElement("button");