feat(live-share): own scope for [LOC] posts and time-limited sessions (#23, #24)

- Live Share gets a "Scope" row (Target / * / any scope from the shared
  list). It applies to [LOC] sends only, beats the app's send_scope and a
  channel's own pick, and never touches normal messages. Target keeps the
  previous behaviour. removeScope() fixes the stored index up.
- Auto share now always ends: "Stop after" 1/2/4/8/12 h (default 1 h, no
  unlimited option). On expiry it switches itself off and shows "Live share
  ended". The session clock is RAM-only, so a reboot starts a new session.
- NodePrefs: loc_share_scope + loc_share_duration_idx appended, sentinel
  0xC0DE002C -> 0xC0DE002E, sizeof unchanged (2824).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Jakub
2026-09-19 14:22:27 +02:00
co-authored by Claude Sonnet 5
parent dafd6c1266
commit 4af6d7d013
8 changed files with 113 additions and 9 deletions
+12 -2
View File
@@ -822,6 +822,12 @@ void MyMesh::removeScope(uint8_t idx) {
if (ci == idx) _prefs.ch_scope_idx[i] = 0;
else if (ci > idx) _prefs.ch_scope_idx[i] = ci - 1;
}
// Live Share's own scope (stored as list index + 1; 0 = follow the target).
if (_prefs.loc_share_scope) {
uint8_t li = _prefs.loc_share_scope - 1;
if (li == idx) _prefs.loc_share_scope = 0;
else if (li > idx) _prefs.loc_share_scope--;
}
syncLegacyDefaultScope(); // ScopeList::remove() may have moved or cleared the default
if (_store) _store->saveScopeList(_scope_list);
// The fix-ups above live in NodePrefs, not in /scopes1, so both files have to
@@ -874,7 +880,9 @@ void MyMesh::sendFloodScoped(const TransportKey& scope, mesh::Packet* pkt, uint3
void MyMesh::sendFloodScoped(const ContactInfo& recipient, mesh::Packet* pkt, uint32_t delay_millis) {
// TODO: dynamic send_scope, depending on recipient and current 'home' Region
if (send_unscoped) {
if (_oneshot_scope_on) {
sendFloodScoped(_oneshot_scope, pkt, delay_millis);
} else if (send_unscoped) {
sendFlood(pkt, delay_millis, _prefs.path_hash_mode + 1); // app has explicitly requested un-scoped
} else {
TransportKey default_scope = _scope_list.key(_scope_list.default_idx);
@@ -885,7 +893,9 @@ void MyMesh::sendFloodScoped(const ContactInfo& recipient, mesh::Packet* pkt, ui
void MyMesh::sendFloodScoped(const mesh::GroupChannel& channel, mesh::Packet* pkt, uint32_t delay_millis) {
if (apcActive()) apcTrackFloodSend(pkt); // listen for a repeater echo to drive APC (channels have no ACK)
trackRelaySend(pkt); // and for the UI "relayed" marker
if (send_unscoped) {
if (_oneshot_scope_on) {
sendFloodScoped(_oneshot_scope, pkt, delay_millis);
} else if (send_unscoped) {
sendFlood(pkt, delay_millis, _prefs.path_hash_mode + 1); // app has explicitly requested un-scoped
} else if (!send_scope.isNull()) {
// App-driven per-send override (CMD_SET_FLOOD_SCOPE_KEY) still wins over