mirror of
https://github.com/MarekZegare4/MeshCore-Solo.git
synced 2026-09-14 15:16:40 +00:00
fix(ui): unify the four navigate views, add Set as target everywhere
Continuing the consistency pass: the four screens that show the distance/bearing "navigate to a point" view (Nodes, Waypoints, Trail's Track back, and navigating to a location shared in a message) had drifted apart in three ways. Only two of the four passed an EtaTracker to navview::draw(), so only Nodes and Track back showed the closing-speed/ETA line -- navigating to a waypoint or a shared location left it off for no reason. All four get one now. They also left the view on three different key sets (Back alone, Back+LEFT/RIGHT, Back+Enter); Back is now the only way out of any of them, so a stray sideways nudge can't drop you out of a running track-back. Messages' renderNav() also switched from reading node_prefs directly to the shared useImperial() helper the other three already used. Set as target -- the row Nodes and Waypoints both offer for a coordinate -- was missing from the message-location Options menu; added alongside Navigate and Save waypoint. Nodes' own Set as target required a full 32-byte public key, which a name-only live-track entry (someone sharing position on a channel who isn't a saved contact -- the group-outing case this exists for) never has. One flag was doing two jobs: "can be pinged" (needs the full key) and "can be identified" (needs only the 6-byte prefix a person target actually uses). Split into has_key/has_prefix; Set as target now only needs a position, resolving to a person target (follows them) when a prefix is available and a place target (pinned where they were) when it isn't -- the same distinction Locator's own picker already draws. Locator's target picker separately still listed the people pinned to the Favourites Dial as its privileged top tier, which stopped making sense once pinning and favouriting became separate concepts. It now leads with favourites instead, matching every other list in the firmware.
This commit is contained in:
@@ -64,7 +64,7 @@ Posting to a **room server** needs a login handshake — the device does this on
|
|||||||
|
|
||||||
Messages appear as chat bubbles sized to their content — **right**-anchored for outgoing, **left** for incoming — with sender name and a compact age indicator (`3m`, `2h`, `>1d`) in the top-right corner. List runs **newest at the bottom**; opening a history starts at the latest message, scrolling up goes further back.
|
Messages appear as chat bubbles sized to their content — **right**-anchored for outgoing, **left** for incoming — with sender name and a compact age indicator (`3m`, `2h`, `>1d`) in the top-right corner. List runs **newest at the bottom**; opening a history starts at the latest message, scrolling up goes further back.
|
||||||
|
|
||||||
**Short Enter** on a message opens it in fullscreen. **Hold Enter** — on a history row or in fullscreen — opens the same options menu: Reply, plus **Navigate** / **Save waypoint** when the message contains a location, and **Path** / **Relayed by** when hop data is available (see Fullscreen message view). You don't need to open the message first.
|
**Short Enter** on a message opens it in fullscreen. **Hold Enter** — on a history row or in fullscreen — opens the same options menu: Reply, plus **Navigate** / **Save waypoint** / **Set as target** when the message contains a location, and **Path** / **Relayed by** when hop data is available (see Fullscreen message view). You don't need to open the message first.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -82,10 +82,11 @@ If the message is a reply addressed to someone (`@[nick]`), a **To: nick** bar i
|
|||||||
| :-----------------------: | :-----------------------: |
|
| :-----------------------: | :-----------------------: |
|
||||||
|  |  |
|
|  |  |
|
||||||
|
|
||||||
**Hold Enter** in fullscreen opens the options menu. It always offers **Reply** for an incoming message, and when the message contains a **location** it adds two more:
|
**Hold Enter** in fullscreen opens the options menu. It always offers **Reply** for an incoming message, and when the message contains a **location** it adds three more:
|
||||||
|
|
||||||
- **Navigate** — opens the bearing/distance view to those coordinates (the same two-bearing screen as Waypoints and Nearby; **Back** returns to the message).
|
- **Navigate** — opens the bearing/distance view to those coordinates (the same two-bearing screen as Waypoints and Nearby; **Back** returns to the message).
|
||||||
- **Save waypoint** — stores the location as a waypoint (visible on the trail map and in the Waypoints list).
|
- **Save waypoint** — stores the location as a waypoint (visible on the trail map and in the Waypoints list).
|
||||||
|
- **Set as target** — pins those coordinates as the active **Locator/Nav target** in one step, the same row Nodes and Waypoints offer (see Tools › Locator).
|
||||||
|
|
||||||
A location is any `lat,lon` pair in the text — exactly what the `{loc}` placeholder inserts — so you can navigate to anything a contact shares. A `[WAY]lat,lon label` share also carries a name, used as the waypoint label. This works on DMs and channel messages, incoming or outgoing.
|
A location is any `lat,lon` pair in the text — exactly what the `{loc}` placeholder inserts — so you can navigate to anything a contact shares. A `[WAY]lat,lon label` share also carries a name, used as the waypoint label. This works on DMs and channel messages, incoming or outgoing.
|
||||||
|
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ Select a node to see its coordinates, distance, bearing with cardinal direction,
|
|||||||
| Navigate | selected node has GPS — for a node sharing live position, the view follows it as it moves and adds an ETA line |
|
| Navigate | selected node has GPS — for a node sharing live position, the view follows it as it moves and adds an ETA line |
|
||||||
| Ping | a public key is known for the node |
|
| Ping | a public key is known for the node |
|
||||||
| Save waypoint | selected node has GPS |
|
| Save waypoint | selected node has GPS |
|
||||||
| Set as target | selected node has GPS **and** a public key — pins it as the active **Locator/Nav target** right away (see **Locator**) |
|
| Set as target | selected node has a position — pins it as the active **Locator/Nav target** right away (see **Locator**). A node with a known public key (a contact, a scan result, or someone sharing over DM) becomes a **person** target that keeps following them; a name-only row — someone sharing their position on a channel who isn't your contact — becomes a **place** target pinned where they were, since there's no identity to re-resolve |
|
||||||
| Fav: ON / OFF | selected node is a saved contact — **LEFT/RIGHT** or **Enter** toggles it in place, as in the Messages menus; the same starred flag those lists use, shared with the companion app |
|
| Fav: ON / OFF | selected node is a saved contact — **LEFT/RIGHT** or **Enter** toggles it in place, as in the Messages menus; the same starred flag those lists use, shared with the companion app |
|
||||||
| Pin to dial / Unpin (slot N) | selected node is a saved contact — puts it on the [Favourites Dial](../favourites_dial/favourites_dial.md), taking the first free slot |
|
| Pin to dial / Unpin (slot N) | selected node is a saved contact — puts it on the [Favourites Dial](../favourites_dial/favourites_dial.md), taking the first free slot |
|
||||||
| Admin | selected node is a saved **repeater or room server** contact — opens **Tools › Admin** for it directly (see **Admin**) |
|
| Admin | selected node is a saved **repeater or room server** contact — opens **Tools › Admin** for it directly (see **Admin**) |
|
||||||
@@ -184,11 +184,11 @@ A waypoint is a saved spot — your car, camp, a water source — that you can n
|
|||||||
|
|
||||||
<!-- screenshot pending: Waypoints navigation view — target label, distance, To/Hdg bearings (shared with Nearby/message Navigate) -->
|
<!-- screenshot pending: Waypoints navigation view — target label, distance, To/Hdg bearings (shared with Nearby/message Navigate) -->
|
||||||
|
|
||||||
There is no magnetometer, so the screen shows two *absolute* bearings and you compare them: target at 145°, travelling at 90° → bear right. The **Hdg** line is derived from GPS movement (see Compass) and reads `--` until you move.
|
There is no magnetometer, so the screen shows two *absolute* bearings and you compare them: target at 145°, travelling at 90° → bear right. The **Hdg** line is derived from GPS movement (see Compass) and reads `--` until you move. A fourth line shows closing speed and **ETA** once you're actually approaching. **Back** leaves the view — it's the only key that does, on every navigate view.
|
||||||
|
|
||||||
**Managing** — **Hold Enter** on a waypoint row offers **Rename** / **Delete** / **Send** / **Set as target** (the *Trail start* row is navigate-only). **Set as target** pins the waypoint as the active **Locator/Nav target** in one step (see **Locator**). Delete removes one at a time; there is no bulk clear.
|
**Managing** — **Hold Enter** on a waypoint row offers **Rename** / **Delete** / **Send** / **Set as target** (the *Trail start* row is navigate-only). **Set as target** pins the waypoint as the active **Locator/Nav target** in one step (see **Locator**). Delete removes one at a time; there is no bulk clear.
|
||||||
|
|
||||||
**Sharing** — **Send** hands the waypoint to the Messages screen: pick a contact or channel, and the message is pre-filled as `[WAY]<lat>,<lon> <label>` (e.g. `[WAY]37.42123,-122.08456 CAR`) for you to confirm or edit before sending. On the receiving device, opening that message and **Hold Enter → Navigate / Save waypoint** turns it back into a navigable point (see *Messages › Fullscreen message view*). The format is plain text, so it stays readable on other firmware and the phone app.
|
**Sharing** — **Send** hands the waypoint to the Messages screen: pick a contact or channel, and the message is pre-filled as `[WAY]<lat>,<lon> <label>` (e.g. `[WAY]37.42123,-122.08456 CAR`) for you to confirm or edit before sending. On the receiving device, opening that message and **Hold Enter → Navigate / Save waypoint / Set as target** turns it back into a navigable point (see *Messages › Fullscreen message view*). The format is plain text, so it stays readable on other firmware and the phone app.
|
||||||
|
|
||||||
### Downloading GPX
|
### Downloading GPX
|
||||||
|
|
||||||
@@ -274,7 +274,7 @@ Navigate with **UP/DOWN**, change a value with **LEFT/RIGHT** (or **Enter**); **
|
|||||||
| Setting | Options | Notes |
|
| Setting | Options | Notes |
|
||||||
| ------- | -------------------------------- | -------------------------------------------------------------------------------------- |
|
| ------- | -------------------------------- | -------------------------------------------------------------------------------------- |
|
||||||
| Alert | ON / OFF | Master switch. Enabling without a target prompts you to pick one. |
|
| Alert | ON / OFF | Master switch. Enabling without a target prompts you to pick one. |
|
||||||
| Target | none / person / waypoint | **Enter** opens a picker — **None** first (clears the target), then **dial-pinned people** (offered even with no known position yet, so you can arm ahead of time), then any other contact with a currently-resolvable position (live-sharing *or* just last-advertised, e.g. a repeater), then waypoints; **UP/DOWN** + **Enter** to choose. **LEFT/RIGHT** quick-cycles the same set in place, including back to **None**. A person is shown with an `@` prefix, a ★ if they're a favourite, plus a compact **age tag** (e.g. `@Bob (5m)`) when the position is last-advertised rather than a live share. Shows `none` until set. |
|
| Target | none / person / waypoint | **Enter** opens a picker — **None** first (clears the target), then your **favourites** (offered even with no known position yet, so you can arm ahead of time), then any other contact with a currently-resolvable position (live-sharing *or* just last-advertised, e.g. a repeater), then waypoints; **UP/DOWN** + **Enter** to choose. **LEFT/RIGHT** quick-cycles the same set in place, including back to **None**. A person is shown with an `@` prefix, a ★ when they're a favourite, plus a compact **age tag** (e.g. `@Bob (5m)`) when the position is last-advertised rather than a live share. Shows `none` until set. |
|
||||||
| Radius | 50 / 100 / 250 / 500 m / 1 km | Geofence size. |
|
| Radius | 50 / 100 / 250 / 500 m / 1 km | Geofence size. |
|
||||||
| Mode | Arrive / Leave / Both | Which crossing fires the alert — entering the radius, leaving it, or both. |
|
| Mode | Arrive / Leave / Both | Which crossing fires the alert — entering the radius, leaving it, or both. |
|
||||||
| Beeper | ON / OFF | Optional homing tone — shown only in **Arrive** / **Both** modes (see below). |
|
| Beeper | ON / OFF | Optional homing tone — shown only in **Arrive** / **Both** modes (see below). |
|
||||||
@@ -285,7 +285,7 @@ Navigate with **UP/DOWN**, change a value with **LEFT/RIGHT** (or **Enter**); **
|
|||||||
|
|
||||||
**Proximity beeper.** With **Beeper** on, the device ticks while inside the radius, shortening the gap as you get closer — slow near the edge, rapid near the centre — like a homing beeper. Silent outside the radius. As an opt-in toggle, it **overrides the global buzzer mute** — an explicit "I want to hear this." It only appears in **Arrive**/**Both** mode (hidden and silent in **Leave**-only). Independent of the crossing alert, which does follow the mute — use either or both.
|
**Proximity beeper.** With **Beeper** on, the device ticks while inside the radius, shortening the gap as you get closer — slow near the edge, rapid near the centre — like a homing beeper. Silent outside the radius. As an opt-in toggle, it **overrides the global buzzer mute** — an explicit "I want to hear this." It only appears in **Arrive**/**Both** mode (hidden and silent in **Leave**-only). Independent of the crossing alert, which does follow the mute — use either or both.
|
||||||
|
|
||||||
**Setting the target from anywhere.** Besides this screen's picker, the *same* active target can be set in one step with **Set as target** from **Nearby Nodes**' or **Waypoints**' own **Hold Enter** menu — handy so you don't need a detour through Tools. Picking from this screen's picker saves on exit (so **LEFT/RIGHT** cycling stays cheap); the per-item shortcuts save immediately and confirm with a `Target set` toast.
|
**Setting the target from anywhere.** Besides this screen's picker, the *same* active target can be set in one step with **Set as target** from the **Hold Enter** menu of **Nearby Nodes**, **Waypoints**, or a message carrying a location — handy so you don't need a detour through Tools. Picking from this screen's picker saves on exit (so **LEFT/RIGHT** cycling stays cheap); the per-item shortcuts save immediately and confirm with a `Target set` toast.
|
||||||
|
|
||||||
**On the map.** Whatever the active target is — person or waypoint — it's drawn as a **flag marker** on both the home **Map** preview and the full **Trail Map**, on top of any waypoint/contact it overlaps and folded into the frame so it never sits off-screen. This shows even when the **Alert** master switch is off, so a target you set purely to navigate to still appears.
|
**On the map.** Whatever the active target is — person or waypoint — it's drawn as a **flag marker** on both the home **Map** preview and the full **Trail Map**, on top of any waypoint/contact it overlaps and folded into the frame so it never sits off-screen. This shows even when the **Alert** master switch is off, so a target you set purely to navigate to still appears.
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
// Locator config tool. Tools › Locator.
|
// Locator config tool. Tools › Locator.
|
||||||
// A single geofence whose target is either a saved waypoint (a place) or a
|
// A single geofence whose target is either a saved waypoint (a place) or a
|
||||||
// person — a favourite/contact or a live [LOC] sender, keyed by pubkey prefix.
|
// person — a contact or a live [LOC] sender, keyed by pubkey prefix.
|
||||||
// When armed the device beeps / alerts as it crosses into (arrive/near) or out
|
// When armed the device beeps / alerts as it crosses into (arrive/near) or out
|
||||||
// of (leave/away) the radius. A waypoint target is snapshotted (coord + label);
|
// of (leave/away) the radius. A waypoint target is snapshotted (coord + label);
|
||||||
// a person target follows their latest shared position. The crossing engine
|
// a person target follows their latest shared position. The crossing engine
|
||||||
// lives in UITask::evaluateLocator(). The Target row's Enter opens a picker
|
// lives in UITask::evaluateLocator(). The Target row's Enter opens a picker
|
||||||
// ("None" first — the only way to unset a target once chosen — then
|
// ("None" first — the only way to unset a target once chosen — then your
|
||||||
// favourites, offered even with no known position yet so you can arm ahead
|
// favourites, offered even with no known position yet so you can arm ahead
|
||||||
// of time, then any other contact with a currently-known position:
|
// of time, then any other contact with a currently-known position:
|
||||||
// live-sharing or just last-advertised, e.g. a repeater; then waypoints).
|
// live-sharing or just last-advertised, e.g. a repeater; then waypoints).
|
||||||
@@ -188,8 +188,8 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Build the selectable target set into _targets: "None" first (clears the
|
// Build the selectable target set into _targets: "None" first (clears the
|
||||||
// target — the only way to unset it once chosen), then favourites (the quick
|
// target — the only way to unset it once chosen), then your favourites (the
|
||||||
// path you pin ahead of time, offered even with no known position yet), then
|
// quick path, offered even with no known position yet), then
|
||||||
// any other contact with a currently-known position — live-sharing or just
|
// any other contact with a currently-known position — live-sharing or just
|
||||||
// last-advertised (a repeater, a room, or someone who shared a fix once) —
|
// last-advertised (a repeater, a room, or someone who shared a fix once) —
|
||||||
// then saved waypoints. A person is keyed by pubkey prefix so the engine
|
// then saved waypoints. A person is keyed by pubkey prefix so the engine
|
||||||
@@ -200,14 +200,11 @@ public:
|
|||||||
none.kind = 2; none.lat = 0; none.lon = 0; none.ts = 0; none.live = false; none.fav = false;
|
none.kind = 2; none.lat = 0; none.lon = 0; none.ts = 0; none.live = false; none.fav = false;
|
||||||
memset(none.key, 0, 6);
|
memset(none.key, 0, 6);
|
||||||
snprintf(none.name, sizeof(none.name), "(none)");
|
snprintf(none.name, sizeof(none.name), "(none)");
|
||||||
for (int i = 0; i < NodePrefs::FAVOURITES_COUNT; i++) {
|
for (int idx = 0; _target_n < TARGET_MAX; idx++) {
|
||||||
const uint8_t* pre = _prefs->favourite_contacts[i];
|
ContactInfo c;
|
||||||
bool empty = true;
|
if (!the_mesh.getContactByIdx(idx, c)) break;
|
||||||
for (int b = 0; b < NodePrefs::FAVOURITE_PREFIX_LEN; b++) if (pre[b]) { empty = false; break; }
|
if (!(c.flags & 0x01)) continue;
|
||||||
if (empty) continue;
|
addPersonTarget(c.id.pub_key, c.name, /*require_position=*/false, true);
|
||||||
ContactInfo* c = the_mesh.lookupContactByPubKey(pre, NodePrefs::FAVOURITE_PREFIX_LEN);
|
|
||||||
if (!c) continue;
|
|
||||||
addPersonTarget(pre, c->name, /*require_position=*/false, (c->flags & 0x01) != 0);
|
|
||||||
}
|
}
|
||||||
for (int idx = 0; _target_n < TARGET_MAX; idx++) {
|
for (int idx = 0; _target_n < TARGET_MAX; idx++) {
|
||||||
ContactInfo c;
|
ContactInfo c;
|
||||||
|
|||||||
@@ -81,8 +81,8 @@ class MessagesScreen : public UIScreen {
|
|||||||
// location (a {loc} string or a [WAY] share), plus Path/Relayed by when the
|
// location (a {loc} string or a [WAY] share), plus Path/Relayed by when the
|
||||||
// entry has hop data recorded. _fs_act maps each visible row back to an
|
// entry has hop data recorded. _fs_act maps each visible row back to an
|
||||||
// action so the index math survives the conditional layout.
|
// action so the index math survives the conditional layout.
|
||||||
enum FsAct : uint8_t { FS_REPLY, FS_NAV, FS_SAVE, FS_PATH };
|
enum FsAct : uint8_t { FS_REPLY, FS_NAV, FS_SAVE, FS_TARGET, FS_PATH };
|
||||||
uint8_t _fs_act[4];
|
uint8_t _fs_act[5];
|
||||||
int _fs_act_n = 0;
|
int _fs_act_n = 0;
|
||||||
// Which history entry the currently-open Options menu (and, after FS_PATH is
|
// Which history entry the currently-open Options menu (and, after FS_PATH is
|
||||||
// chosen, the path/relay detail popup) refers to -- set by buildFsMenu().
|
// chosen, the path/relay detail popup) refers to -- set by buildFsMenu().
|
||||||
@@ -102,6 +102,7 @@ class MessagesScreen : public UIScreen {
|
|||||||
char _fs_path_item[24];
|
char _fs_path_item[24];
|
||||||
// Inline navigate-to-location view layered over the fullscreen message.
|
// Inline navigate-to-location view layered over the fullscreen message.
|
||||||
bool _nav_active = false;
|
bool _nav_active = false;
|
||||||
|
navview::EtaTracker _nav_eta; // closing speed / ETA, as in the other navigate views
|
||||||
int32_t _nav_lat = 0, _nav_lon = 0;
|
int32_t _nav_lat = 0, _nav_lon = 0;
|
||||||
char _nav_label[24] = "";
|
char _nav_label[24] = "";
|
||||||
|
|
||||||
@@ -292,11 +293,11 @@ class MessagesScreen : public UIScreen {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Build the fullscreen-message options popup: Reply (if allowed), Navigate /
|
// Build the fullscreen-message options popup: Reply (if allowed), Navigate /
|
||||||
// Save waypoint when `body` carries a location, and Path / Relayed by when
|
// Save waypoint / Set as target when `body` carries a location, and Path /
|
||||||
// the entry (ring_pos, in the DM ring or the channel ring per is_channel)
|
// Relayed by when the entry (ring_pos, in the DM ring or the channel ring
|
||||||
// has hop data recorded. Opens _ctx_menu only when there's at least one
|
// per is_channel) has hop data recorded. Opens _ctx_menu only when there's at
|
||||||
// action. Parses the location once here and stashes it for the action
|
// least one action. Parses the location once here and stashes it for the
|
||||||
// handler; stashes ring_pos/is_channel too, for showPathDetail().
|
// action handler; stashes ring_pos/is_channel too, for showPathDetail().
|
||||||
void buildFsMenu(const char* body, bool reply_allowed, int ring_pos, bool is_channel) {
|
void buildFsMenu(const char* body, bool reply_allowed, int ring_pos, bool is_channel) {
|
||||||
_fs_menu_pos = ring_pos;
|
_fs_menu_pos = ring_pos;
|
||||||
_fs_menu_is_channel = is_channel;
|
_fs_menu_is_channel = is_channel;
|
||||||
@@ -312,13 +313,14 @@ class MessagesScreen : public UIScreen {
|
|||||||
}
|
}
|
||||||
bool has_path_item = hop_count > 0;
|
bool has_path_item = hop_count > 0;
|
||||||
|
|
||||||
int n = (reply_allowed ? 1 : 0) + (has_loc ? 2 : 0) + (has_path_item ? 1 : 0);
|
int n = (reply_allowed ? 1 : 0) + (has_loc ? 3 : 0) + (has_path_item ? 1 : 0);
|
||||||
if (n == 0) return;
|
if (n == 0) return;
|
||||||
_fs_act_n = 0;
|
_fs_act_n = 0;
|
||||||
_ctx_menu.begin("Options", n);
|
_ctx_menu.begin("Options", n);
|
||||||
if (reply_allowed) { _ctx_menu.addItem("Reply"); _fs_act[_fs_act_n++] = FS_REPLY; }
|
if (reply_allowed) { _ctx_menu.addItem("Reply"); _fs_act[_fs_act_n++] = FS_REPLY; }
|
||||||
if (has_loc) { _ctx_menu.addItem("Navigate"); _fs_act[_fs_act_n++] = FS_NAV;
|
if (has_loc) { _ctx_menu.addItem("Navigate"); _fs_act[_fs_act_n++] = FS_NAV;
|
||||||
_ctx_menu.addItem("Save waypoint"); _fs_act[_fs_act_n++] = FS_SAVE; }
|
_ctx_menu.addItem("Save waypoint"); _fs_act[_fs_act_n++] = FS_SAVE;
|
||||||
|
_ctx_menu.addItem("Set as target"); _fs_act[_fs_act_n++] = FS_TARGET; }
|
||||||
if (has_path_item) {
|
if (has_path_item) {
|
||||||
if (path_is_relay) snprintf(_fs_path_item, sizeof(_fs_path_item), "Relayed by (%u)", (unsigned)hop_count);
|
if (path_is_relay) snprintf(_fs_path_item, sizeof(_fs_path_item), "Relayed by (%u)", (unsigned)hop_count);
|
||||||
else snprintf(_fs_path_item, sizeof(_fs_path_item), "Path (%u hop%s)", (unsigned)hop_count, hop_count == 1 ? "" : "s");
|
else snprintf(_fs_path_item, sizeof(_fs_path_item), "Path (%u hop%s)", (unsigned)hop_count, hop_count == 1 ? "" : "s");
|
||||||
@@ -384,8 +386,14 @@ class MessagesScreen : public UIScreen {
|
|||||||
startReply(channel);
|
startReply(channel);
|
||||||
} else if (a == FS_NAV) {
|
} else if (a == FS_NAV) {
|
||||||
_nav_active = true; // keep the message view active underneath
|
_nav_active = true; // keep the message view active underneath
|
||||||
|
_nav_eta.reset();
|
||||||
} else if (a == FS_SAVE) {
|
} else if (a == FS_SAVE) {
|
||||||
saveSharedWaypoint();
|
saveSharedWaypoint();
|
||||||
|
} else if (a == FS_TARGET) {
|
||||||
|
// Kind 0 (a place): the coordinates were snapshotted out of the message
|
||||||
|
// text, so there is no contact to keep re-resolving them from.
|
||||||
|
_task->setTargetNow(0, nullptr, _nav_lat, _nav_lon,
|
||||||
|
_nav_label[0] ? _nav_label : "Msg loc");
|
||||||
} else if (a == FS_PATH) {
|
} else if (a == FS_PATH) {
|
||||||
showPathDetail();
|
showPathDetail();
|
||||||
}
|
}
|
||||||
@@ -407,9 +415,8 @@ class MessagesScreen : public UIScreen {
|
|||||||
void renderNav(DisplayDriver& display) {
|
void renderNav(DisplayDriver& display) {
|
||||||
int32_t mylat, mylon; bool have = _task->currentLocation(mylat, mylon);
|
int32_t mylat, mylon; bool have = _task->currentLocation(mylat, mylon);
|
||||||
int cog; bool cogv = _task->currentCourse(cog);
|
int cog; bool cogv = _task->currentCourse(cog);
|
||||||
NodePrefs* p = _task->getNodePrefs();
|
|
||||||
navview::draw(display, have, mylat, mylon, _nav_lat, _nav_lon,
|
navview::draw(display, have, mylat, mylon, _nav_lat, _nav_lon,
|
||||||
_nav_label[0] ? _nav_label : "Msg loc", cogv, cog, p && p->units_imperial);
|
_nav_label[0] ? _nav_label : "Msg loc", cogv, cog, _task->useImperial(), &_nav_eta);
|
||||||
}
|
}
|
||||||
|
|
||||||
void setupMsgPick() {
|
void setupMsgPick() {
|
||||||
@@ -1783,9 +1790,9 @@ public:
|
|||||||
// Channel Add/Edit form consumes all input while active.
|
// Channel Add/Edit form consumes all input while active.
|
||||||
if (_ch_view.active()) return _ch_view.handleInput(c);
|
if (_ch_view.active()) return _ch_view.handleInput(c);
|
||||||
|
|
||||||
// Navigate view: Back or Enter returns to the message it was opened from.
|
// Navigate view: Back returns to the message it was opened from.
|
||||||
if (_nav_active) {
|
if (_nav_active) {
|
||||||
if (c == KEY_CANCEL || c == KEY_ENTER) _nav_active = false;
|
if (c == KEY_CANCEL) _nav_active = false;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (_phase == MODE_SELECT) {
|
if (_phase == MODE_SELECT) {
|
||||||
|
|||||||
@@ -41,7 +41,14 @@ class NearbyScreen : public UIScreen {
|
|||||||
char name[32];
|
char name[32];
|
||||||
uint8_t type;
|
uint8_t type;
|
||||||
uint8_t pub_key[PUB_KEY_SIZE];
|
uint8_t pub_key[PUB_KEY_SIZE];
|
||||||
|
// has_key: the full 32-byte pubkey is present -- what Ping and the base64
|
||||||
|
// key view need. has_prefix: at least the leading FAVOURITE_PREFIX_LEN
|
||||||
|
// bytes are, which is all an identity-keyed reference needs (a Locator
|
||||||
|
// person target, resolved via UITask::resolvePersonPos). Every has_key row
|
||||||
|
// also has_prefix; the reverse doesn't hold -- a [LOC] share and a heard
|
||||||
|
// advert carry a prefix and nothing more.
|
||||||
bool has_key;
|
bool has_key;
|
||||||
|
bool has_prefix;
|
||||||
// stored-source fields
|
// stored-source fields
|
||||||
int32_t lat_e6, lon_e6;
|
int32_t lat_e6, lon_e6;
|
||||||
float dist_km;
|
float dist_km;
|
||||||
@@ -193,6 +200,7 @@ class NearbyScreen : public UIScreen {
|
|||||||
e.name[sizeof(e.name) - 1] = '\0';
|
e.name[sizeof(e.name) - 1] = '\0';
|
||||||
memcpy(e.pub_key, ci.id.pub_key, PUB_KEY_SIZE);
|
memcpy(e.pub_key, ci.id.pub_key, PUB_KEY_SIZE);
|
||||||
e.has_key = true;
|
e.has_key = true;
|
||||||
|
e.has_prefix = true;
|
||||||
e.lat_e6 = ci.gps_lat;
|
e.lat_e6 = ci.gps_lat;
|
||||||
e.lon_e6 = ci.gps_lon;
|
e.lon_e6 = ci.gps_lon;
|
||||||
bool remote_gps = (ci.gps_lat != 0 || ci.gps_lon != 0);
|
bool remote_gps = (ci.gps_lat != 0 || ci.gps_lon != 0);
|
||||||
@@ -280,7 +288,12 @@ class NearbyScreen : public UIScreen {
|
|||||||
// We only keep a key *prefix* for shares, not the full pubkey, so Ping
|
// We only keep a key *prefix* for shares, not the full pubkey, so Ping
|
||||||
// and the base64 key view (which need 32 bytes) stay unavailable for a
|
// and the base64 key view (which need 32 bytes) stay unavailable for a
|
||||||
// non-contact live entry. Navigate / Save-waypoint work off lat/lon.
|
// non-contact live entry. Navigate / Save-waypoint work off lat/lon.
|
||||||
|
// A DM share is pubkey-keyed, so the prefix is real and good enough to
|
||||||
|
// keep re-resolving them as a Locator person target; a channel share is
|
||||||
|
// matched by name and carries no identity at all.
|
||||||
e.has_key = false;
|
e.has_key = false;
|
||||||
|
e.has_prefix = s.verified;
|
||||||
|
if (s.verified) memcpy(e.pub_key, s.key, LiveTrackStore::KEY_LEN);
|
||||||
e.type = ADV_TYPE_CHAT;
|
e.type = ADV_TYPE_CHAT;
|
||||||
e.lat_e6 = s.lat_1e6;
|
e.lat_e6 = s.lat_1e6;
|
||||||
e.lon_e6 = s.lon_1e6;
|
e.lon_e6 = s.lon_1e6;
|
||||||
@@ -318,6 +331,8 @@ class NearbyScreen : public UIScreen {
|
|||||||
e.name[sizeof(e.name) - 1] = '\0';
|
e.name[sizeof(e.name) - 1] = '\0';
|
||||||
e.type = ADV_TYPE_CHAT; // unknown from AdvertPath — best-effort label
|
e.type = ADV_TYPE_CHAT; // unknown from AdvertPath — best-effort label
|
||||||
e.has_key = false;
|
e.has_key = false;
|
||||||
|
e.has_prefix = true;
|
||||||
|
memcpy(e.pub_key, a.pubkey_prefix, sizeof(a.pubkey_prefix));
|
||||||
e.dist_km = -1.0f;
|
e.dist_km = -1.0f;
|
||||||
e.lastmod = a.recv_timestamp;
|
e.lastmod = a.recv_timestamp;
|
||||||
e.contact_idx = -1;
|
e.contact_idx = -1;
|
||||||
@@ -363,6 +378,7 @@ class NearbyScreen : public UIScreen {
|
|||||||
e.type = dr[i].type;
|
e.type = dr[i].type;
|
||||||
memcpy(e.pub_key, dr[i].pub_key, PUB_KEY_SIZE);
|
memcpy(e.pub_key, dr[i].pub_key, PUB_KEY_SIZE);
|
||||||
e.has_key = true;
|
e.has_key = true;
|
||||||
|
e.has_prefix = true;
|
||||||
e.rssi = dr[i].rssi;
|
e.rssi = dr[i].rssi;
|
||||||
e.snr_x4 = dr[i].snr_x4;
|
e.snr_x4 = dr[i].snr_x4;
|
||||||
e.remote_snr_x4 = dr[i].remote_snr_x4;
|
e.remote_snr_x4 = dr[i].remote_snr_x4;
|
||||||
@@ -598,9 +614,9 @@ class NearbyScreen : public UIScreen {
|
|||||||
if (has_gps) add("Navigate", ACT_NAV);
|
if (has_gps) add("Navigate", ACT_NAV);
|
||||||
if (has_key) add("Ping", ACT_PING);
|
if (has_key) add("Ping", ACT_PING);
|
||||||
if (has_gps) add("Save waypoint", ACT_WAYPOINT);
|
if (has_gps) add("Save waypoint", ACT_WAYPOINT);
|
||||||
// Needs both a position and a stable identity — a person target is keyed
|
// Only a position is needed: with an identity prefix this becomes a person
|
||||||
// by pubkey prefix, so a name-only live-scan/channel row can't offer this.
|
// target that follows them, without one a place target pinned where they were.
|
||||||
if (has_gps && has_key) add("Set as target", ACT_LOCATOR);
|
if (has_gps) add("Set as target", ACT_LOCATOR);
|
||||||
if (can_add) add("Add contact", ACT_ADD);
|
if (can_add) add("Add contact", ACT_ADD);
|
||||||
if (is_contact && has_key) {
|
if (is_contact && has_key) {
|
||||||
snprintf(_fav_label, sizeof(_fav_label), e->fav ? "Fav: ON" : "Fav: OFF");
|
snprintf(_fav_label, sizeof(_fav_label), e->fav ? "Fav: ON" : "Fav: OFF");
|
||||||
@@ -661,8 +677,12 @@ class NearbyScreen : public UIScreen {
|
|||||||
case ACT_WAYPOINT: saveSelectedWaypoint(); break;
|
case ACT_WAYPOINT: saveSelectedWaypoint(); break;
|
||||||
case ACT_LOCATOR: {
|
case ACT_LOCATOR: {
|
||||||
const Entry* e = selected();
|
const Entry* e = selected();
|
||||||
if (e && e->has_key && (e->lat_e6 != 0 || e->lon_e6 != 0))
|
if (!e || (e->lat_e6 == 0 && e->lon_e6 == 0)) break;
|
||||||
_task->setTargetNow(1, e->pub_key, e->lat_e6, e->lon_e6, e->name);
|
// A prefix is enough to keep re-resolving someone who is moving; without
|
||||||
|
// one (a channel share, matched by name) the honest target is the place
|
||||||
|
// they were last seen, snapshotted like a waypoint.
|
||||||
|
if (e->has_prefix) _task->setTargetNow(1, e->pub_key, e->lat_e6, e->lon_e6, e->name);
|
||||||
|
else _task->setTargetNow(0, nullptr, e->lat_e6, e->lon_e6, e->name);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ACT_ADD: {
|
case ACT_ADD: {
|
||||||
@@ -963,7 +983,7 @@ public:
|
|||||||
bool handleInput(char c) override {
|
bool handleInput(char c) override {
|
||||||
// ── navigate-to-node view — any nav key returns to detail ─────────────────
|
// ── navigate-to-node view — any nav key returns to detail ─────────────────
|
||||||
if (_nav) {
|
if (_nav) {
|
||||||
if (c == KEY_CANCEL || keyIsPrev(c) || keyIsNext(c)) _nav = false;
|
if (c == KEY_CANCEL) _nav = false; // only Back leaves a navigate view
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ class WaypointsView {
|
|||||||
static const int TB_ARRIVE_M = 20;
|
static const int TB_ARRIVE_M = 20;
|
||||||
int _tb_idx = 0;
|
int _tb_idx = 0;
|
||||||
navview::EtaTracker _tb_eta;
|
navview::EtaTracker _tb_eta;
|
||||||
|
navview::EtaTracker _wp_eta; // same readout for plain waypoint navigation
|
||||||
|
|
||||||
// GPS averaging (Tools › Trail › Settings › Mark avg). When enabled, markHere()
|
// GPS averaging (Tools › Trail › Settings › Mark avg). When enabled, markHere()
|
||||||
// accumulates fixes for gps_avg_idx seconds and marks the mean position — a
|
// accumulates fixes for gps_avg_idx seconds and marks the mean position — a
|
||||||
@@ -235,7 +236,7 @@ class WaypointsView {
|
|||||||
if (!rowTarget(_sel, tlat, tlon, label)) { _mode = LIST; return; }
|
if (!rowTarget(_sel, tlat, tlon, label)) { _mode = LIST; return; }
|
||||||
int32_t mylat, mylon; bool have = ownPos(mylat, mylon);
|
int32_t mylat, mylon; bool have = ownPos(mylat, mylon);
|
||||||
int cog; bool cogv = _task->currentCourse(cog);
|
int cog; bool cogv = _task->currentCourse(cog);
|
||||||
navview::draw(display, have, mylat, mylon, tlat, tlon, label, cogv, cog, useImperial());
|
navview::draw(display, have, mylat, mylon, tlat, tlon, label, cogv, cog, useImperial(), &_wp_eta);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Navigate to the current track-back breadcrumb. The header doubles as the
|
// Navigate to the current track-back breadcrumb. The header doubles as the
|
||||||
@@ -462,9 +463,9 @@ public:
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Navigation view — any nav key returns to the list.
|
// Navigation view — Back returns to the list, as in every navigate view.
|
||||||
if (_mode == NAV) {
|
if (_mode == NAV) {
|
||||||
if (c == KEY_CANCEL || keyIsPrev(c) || keyIsNext(c)) { _mode = LIST; }
|
if (c == KEY_CANCEL) { _mode = LIST; }
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -485,7 +486,7 @@ public:
|
|||||||
if (c == KEY_DOWN) { _sel = (_sel < total - 1) ? _sel + 1 : 0; return true; }
|
if (c == KEY_DOWN) { _sel = (_sel < total - 1) ? _sel + 1 : 0; return true; }
|
||||||
if (c == KEY_ENTER) {
|
if (c == KEY_ENTER) {
|
||||||
if (_sel == n) openAddForm(); // last row → open the add form
|
if (_sel == n) openAddForm(); // last row → open the add form
|
||||||
else _mode = NAV; // a waypoint / Trail-start row
|
else { _mode = NAV; _wp_eta.reset(); } // a waypoint / Trail-start row
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
// Rename/Delete/Send/Locator apply to saved waypoints only — not Trail-start or Add.
|
// Rename/Delete/Send/Locator apply to saved waypoints only — not Trail-start or Add.
|
||||||
|
|||||||
@@ -16,6 +16,10 @@
|
|||||||
- **Enter on a `Notif:` / `Melody:` / `Fav:` / `Sort:` row in a context menu now changes the value instead of closing the menu.** Those rows show a value you step through with LEFT/RIGHT, but Enter treated them as ordinary menu picks and dismissed the popup, so changing two of them meant reopening the menu in between. Enter now advances the value and the menu stays open — only Back closes it — across the Messages contact/room/channel menus, Tools › Nodes, and the Ringtone editor, matching what Trail › Settings already did. The Nodes `Fav` row was the worst of it: LEFT/RIGHT did nothing there at all, so the only way to toggle a favourite was an Enter that closed the menu on every flip.
|
- **Enter on a `Notif:` / `Melody:` / `Fav:` / `Sort:` row in a context menu now changes the value instead of closing the menu.** Those rows show a value you step through with LEFT/RIGHT, but Enter treated them as ordinary menu picks and dismissed the popup, so changing two of them meant reopening the menu in between. Enter now advances the value and the menu stays open — only Back closes it — across the Messages contact/room/channel menus, Tools › Nodes, and the Ringtone editor, matching what Trail › Settings already did. The Nodes `Fav` row was the worst of it: LEFT/RIGHT did nothing there at all, so the only way to toggle a favourite was an Enter that closed the menu on every flip.
|
||||||
- **Settings rows Auto-off, Low battery, GPS pwr and Battery ignored Enter**, even though their options wrap around exactly like the melody, keyboard and clock rows next to them, where Enter has always stepped to the next value. They now accept Enter too. Rows that ramp between fixed ends instead of wrapping — Brightness, Volume, TX Pwr, Timezone, SF/BW/CR — stay LEFT/RIGHT-only, since there is nothing to wrap to.
|
- **Settings rows Auto-off, Low battery, GPS pwr and Battery ignored Enter**, even though their options wrap around exactly like the melody, keyboard and clock rows next to them, where Enter has always stepped to the next value. They now accept Enter too. Rows that ramp between fixed ends instead of wrapping — Brightness, Volume, TX Pwr, Timezone, SF/BW/CR — stay LEFT/RIGHT-only, since there is nothing to wrap to.
|
||||||
- **The Settings › Contacts filter rows read `DM`, `all` and `fav` in three different casings** from each other and from the rest of the screen. Now `DMs`, `All` and `Fav`.
|
- **The Settings › Contacts filter rows read `DM`, `all` and `fav` in three different casings** from each other and from the rest of the screen. Now `DMs`, `All` and `Fav`.
|
||||||
|
- **The four navigate views behaved differently from each other.** They all draw the same screen — distance, bearing to target, your heading — but only two of them (Nodes › Navigate and Trail › Track back) showed the closing-speed/**ETA** line; navigating to a waypoint or to a location shared in a message left it off for no reason. All four show it now. They also left the view on three different key sets — Back, LEFT/RIGHT on some, Enter on another. **Back** is now the only way out of any of them, so a stray sideways nudge can't drop you out of a running track-back.
|
||||||
|
- **A location shared in a message could be navigated to and saved as a waypoint, but not set as the Locator target** — the one row that Nodes and Waypoints both offer for a coordinate. **Set as target** now joins Navigate and Save waypoint in the message Options menu.
|
||||||
|
- **Tools › Nodes refused to make a Locator target out of a node whose full public key it didn't have**, even with a perfectly good position on screen — the row you get when someone shares their location on a channel and isn't in your contacts, which is exactly the group-outing case the feature is for. One flag was standing in for two different things: "can be pinged" (needs the whole 32-byte key) and "can be identified" (needs only a 6-byte prefix, which is all a person target ever uses). Those are separate now, so **Set as target** is offered whenever the node has a position: with an identity it follows them as they move, and without one — a channel share, matched by name — it pins the place they were last seen, the same way a waypoint or a location out of a message does. **Navigate** was never restricted and is unchanged.
|
||||||
|
- **Tools › Locator's target picker listed the people pinned to the Favourites Dial as its top tier**, which stopped making sense once pinning and favouriting became separate things — pinning puts something on a home page, and has nothing to say about who you'd geofence. The picker now leads with your **favourites** instead (still offered before they have a known position, so you can arm ahead of time); everyone else with a resolvable position follows, as before.
|
||||||
- **Hold Enter no longer doubles as a second Back key.** On Tools, Locator, Live Share, Repeater, Remote Bot, Auto-Advert, GPIO, Compass, the Dashboard config and the Messages navigate view it quietly meant "go back", while on other screens the same gesture opens a context menu — so the same long press did two unrelated things depending on where you were. It now only ever opens a menu (or does nothing where there is none), and Back is the single way back. This also applies inside an open popup, which Hold Enter used to dismiss.
|
- **Hold Enter no longer doubles as a second Back key.** On Tools, Locator, Live Share, Repeater, Remote Bot, Auto-Advert, GPIO, Compass, the Dashboard config and the Messages navigate view it quietly meant "go back", while on other screens the same gesture opens a context menu — so the same long press did two unrelated things depending on where you were. It now only ever opens a menu (or does nothing where there is none), and Back is the single way back. This also applies inside an open popup, which Hold Enter used to dismiss.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|||||||
Reference in New Issue
Block a user