Recording map: follow toggle to free-pan while riding

New ⊕ button on the recording screen toggles map auto-follow. When off,
the map stays put so you can look around when unsure where you are (off
the track, or the track leads where there is no road); new GPS points no
longer recentre it. Tapping it back on recentres on the current position
and resumes following. Default on per screen entry (not persisted).

Guards followMode around panTo/setView in onPoint/onBatchUpdate/
onStateChange. Independent of the compass freeze/rotate controls.
This commit is contained in:
4aiman 2026-09-26 23:44:18 +03:00
commit d6fe47a924
6 changed files with 151 additions and 5 deletions

View file

@ -0,0 +1,76 @@
# План: кнопка тоггла "следование карты за позицией" на экране записи
## Context (зачем)
Во время записи карта на экране трекинга постоянно снэпится (`panTo`) на текущую GPS-позицию
при каждой новой точке. Кейс пользователя: отклонился от трека или трек ведёт туда, где дороги
нет, надо осмотреться по карте вокруг - но карта тут же прыгает обратно на позицию, подвигать
её невозможно. Нужна кнопка-тумблер: нажал - карта перестаёт следовать за позицией (можно
свободно двигать/смотреть), нажал снова - карта центрируется на текущей позиции и следование
возобновляется.
Решения по UX (согласованы с пользователем):
- Кнопка - плитка 32x32 в общем правом ряду кнопок карты, с символом (не словом), активное
состояние подсвечивается (как freeze/roads). Активна = следование включено.
- Только кнопка-тумблер. Драг карты пальцем следование НЕ выключает (строго как в запросе).
- Повторный тап (включение следования) сразу центрирует карту на текущей позиции.
- Следование по умолчанию включено при каждом заходе на экран (в localStorage не храним).
## Где сейчас следование (frontend/js/main.js, функция renderRecordingInline)
Автоследование = центрирование вида карты на текущей точке. Происходит в:
- `GPS.recording.onPoint`, ~4840: `map.panTo(ll)` - основное следование при записи.
- `GPS.recording.onPoint` состояние waiting_gps, ~4857: `map.setView([p.lat,p.lon],15)`.
- `GPS.recording.onStateChange` "ready", ~4814: `map.setView([p.lat,p.lon],16)` - разовый locate.
- `GPS.recording.onBatchUpdate`, ~5005: `map.panTo(last)` - следование при пакетном апдейте.
Маркер позиции (`updateMe`/`refreshMe`) вид карты НЕ трогает - при выключенном следовании
точка продолжит обновляться на своём месте, просто карта не будет прыгать. Компас/поворот
(`compassFrozen`, `applyBearing`) ортогональны следованию - `panTo` на bearing не влияет.
## Фикс (frontend/js/main.js, только renderRecordingInline)
1. Флаг: рядом с `compassFrozen`/`rotOffset` (~4470-4477) добавить `let followMode = true;`.
2. Кнопка: по образцу `freezeBtn`/`roadsBtn` (~4624-4676) создать `followBtn`
(`class="rec-map-btn rec-follow-btn"`, символ-цель, `title="Следить за позицией"`),
добавить в `btnWrap`, сразу выставить `followBtn.classList.add("active")` (followMode=true).
onclick:
- `followMode = !followMode;`
- `followBtn.classList.toggle("active", followMode);`
- при включении - центрировать на текущей точке: взять последнюю точку
`GPS.recording.points` (как в onBatchUpdate: сортировка по ts, последняя) и `map.panTo`.
3. Загардить все 4 места следования условием `if (followMode)`:
- ~4840 `map.panTo(ll)` -> `if (followMode) map.panTo(ll);`
- ~4857 waiting_gps `map.setView(...)` -> обернуть в `if (followMode)` (return оставить).
- ~4814 "ready" `map.setView(...)` -> обернуть в `if (followMode)`.
- ~5005 `map.panTo(last)` -> `if (followMode) map.panTo(last);`
## CSS (frontend/css/styles.css)
После `.rec-dark-toggle { right:8px; bottom:238px; }` (~1248) добавить позицию для новой кнопки
в следующем свободном слоте стека: `.rec-follow-btn { right: 8px; bottom: 276px; }`.
Базовый вид и `.active` уже есть у `.rec-map-btn` / `.rec-map-btn.active` - переиспользуем.
## Символ кнопки
Плитка-цель, читается как "центрировать на мне": glyph круг-с-точкой/крест-в-круге
(например "⊕" плюс-в-круге или "◎" bullseye) - выбрать широко отрисовываемый.
Смысл несёт `title` (текст). Активная подсветка = следование включено.
## Регрессии (проверить рядом)
- Freeze-кнопка компаса (`compassFrozen`) и поворот карты - не задеты (следование = только
центр вида, поворот = bearing). Убедиться, что обе кнопки работают независимо.
- Маркер позиции `updateMe` при выключенном следовании продолжает двигаться (карта стоит).
- Второй, отдельный экран `setupLiveUpdates` (мини-карта gpsMap, ~5030, свой panTo 5039/5055)
НЕ трогаем - это превью в списке, у него нет ряда кнопок. Пользователь просил "экран
трекинга" = renderRecordingInline.
## Верификация
- Десктоп (Xvfb :99, `DISPLAY=:99 ./build.sh`): открыть экран записи, начать запись
(или подсадить точки), проверить:
1) следование по умолчанию включено, кнопка подсвечена, карта идёт за точкой;
2) тап -> подсветка гаснет, карту можно свободно двигать, новые точки её не возвращают;
3) тап -> карта центрируется на текущей позиции, подсветка загорается, следование снова идёт.
Скрин каждого состояния.
- Сборка APK + телефон (по явной просьбе): в реальной записи проехать/постоять, отжать кнопку,
подвигать карту, вернуть кнопкой. Коммит ДО теста на телефоне, доработки - отдельным коммитом.
- README/PROJECT_MAP + лог сессии обновить вместе с коммитом.
## Не в этой задаче
Авто-выключение следования при драге карты (пользователь выбрал "только кнопка").
Хранение состояния следования между сессиями (по умолчанию всегда включено).

View file

@ -0,0 +1,47 @@
# Сессия 2026-09-26 - тумблер "следование карты за позицией" на экране записи
Работали по утверждённому плану: `docs/plans/2026-09-26-map-follow-toggle.md`.
## Контекст
Во время записи карта на экране трекинга при каждой новой GPS-точке центрируется на текущей
позиции (`map.panTo`). Кейс: отклонился от трека или трек ведёт туда, где дороги нет, надо
осмотреться по карте - но она тут же прыгает обратно, подвигать её нельзя. Нужен тумблер:
нажал - карта перестаёт следовать (можно двигать), нажал снова - центрируется на позиции и
следование возобновляется.
Согласовано с пользователем (AskUserQuestion): кнопка - символ-плитка в общем ряду кнопок
карты (не слово), активная подсветка = слежу; только кнопка-тумблер (драг карты следование НЕ
выключает); повторный тап центрирует; состояние не запоминается между сессиями.
## Действия
- `frontend/js/main.js`, `renderRecordingInline`:
- добавлен флаг `let followMode = true;` рядом с `compassFrozen`/`rotOffset`;
- создана кнопка `followBtn` (`class="rec-map-btn rec-follow-btn active"`, символ `⊕`,
`title="Следить за позицией"`) по образцу `freezeBtn`; onclick тогглит `followMode` и класс
`active`, при включении центрирует `map.panTo` на последней точке `GPS.recording.points`
(сортировка по ts);
- 4 места автоследования загардены `if (followMode)`: `onPoint` `panTo` (~4840), `onPoint`
waiting_gps `setView` (~4857), `onStateChange` "ready" `setView` (~4814), `onBatchUpdate`
`panTo` (~5005).
- `frontend/css/styles.css`: `.rec-follow-btn { right: 8px; bottom: 276px; font-size: 18px; }`
(следующий слот стека после `.rec-dark-toggle` 238px). Базовый вид и `.active` -
переиспользован `.rec-map-btn` / `.rec-map-btn.active`.
- README: добавлен буллет "Слежение карты (тумблер ⊕)".
## Проверка
- `node -c frontend/js/main.js` - OK.
- Десктоп (Xvfb :99, `DISPLAY=:99 ./build.sh` = tauri dev, без бампа версии): открыт экран
записи (Старт), кнопка `⊕` в правом стеке вверху, по умолчанию подсвечена (слежу). Тап ->
подсветка гаснет (слежу выкл). Тап -> подсветка возвращается (слежу вкл). JS-ошибок в логе
нет. Скрины: s_rec.png / s_rec_off.png / s_rec_on2.png в scratchpad.
- НЕ проверено на десктопе: живое поведение "карта не прыгает на новую точку при выкл / прыгает
при вкл" - под Xvfb настоящего GPS нет, экран в waiting_gps, поток точек не идёт. Логика -
тривиальный guard вокруг уже работающих `panTo`/`setView`. Реальная проверка потока - на
телефоне.
## Следующие шаги
- Сборка APK + установка на телефон (по явной просьбе пользователя): проверить в живой записи -
проехать/постоять, отжать `⊕`, подвигать карту (новые точки не возвращают), вернуть кнопкой
(центрируется). Доработки - отдельным коммитом.
- Компас (freeze `X`) и поворот карты не задеты (следование = только центр вида, поворот =
bearing) - убедиться на телефоне, что обе кнопки работают независимо.