From 4eee794a1775f477484233de8ee93ef41d13f1b5 Mon Sep 17 00:00:00 2001 From: Jakub <106778416+MarekZegare4@users.noreply.github.com> Date: Mon, 12 Oct 2026 18:14:08 +0200 Subject: [PATCH] fix(companion): hashtag join trims like the app, prefs survive a downgrade, a sender named Me - Joining #topic drops surrounding spaces and keeps the case, as meshcore-open's derivePskFromHashtag() does. - /prefs written by a newer build (bigger NodePrefs, same version): the extra bytes are read through the CRC and skipped instead of resetting every setting. - A channel post from someone named "Me" is filed as "\"Me\": ...", so the views don't take it for our own. Co-Authored-By: Claude Opus 5.5 --- examples/companion_radio/DataStore.cpp | 13 ++++++++++++- examples/companion_radio/ui-core/ChannelControl.h | 10 ++++++++-- examples/companion_radio/ui-core/UiCore.h | 5 +++++ 3 files changed, 25 insertions(+), 3 deletions(-) diff --git a/examples/companion_radio/DataStore.cpp b/examples/companion_radio/DataStore.cpp index 43db96a2..0695d28b 100644 --- a/examples/companion_radio/DataStore.cpp +++ b/examples/companion_radio/DataStore.cpp @@ -445,7 +445,18 @@ void DataStore::loadPrefs(NodePrefs& prefs, double& node_lat, double& node_lon) ok = file.read((uint8_t*)tmp, n) == (int)n; uint32_t crc = crc32(0, (const uint8_t*)&lat, sizeof(lat)); crc = crc32(crc, (const uint8_t*)&lon, sizeof(lon)); - ok = ok && crc32(crc, (const uint8_t*)tmp, n) == h.crc; + crc = crc32(crc, (const uint8_t*)tmp, n); + // Written by a newer build (a downgrade): its extra fields are skipped, + // but the CRC covers them, so read them through it rather than losing + // every setting to a mismatch. + for (size_t left = h.size - n; ok && left > 0; ) { + uint8_t buf[32]; + size_t k = left < sizeof(buf) ? left : sizeof(buf); + ok = file.read(buf, k) == (int)k; + crc = crc32(crc, buf, k); + left -= k; + } + ok = ok && crc == h.crc; } file.close(); if (ok) { prefs = *tmp; node_lat = lat; node_lon = lon; } diff --git a/examples/companion_radio/ui-core/ChannelControl.h b/examples/companion_radio/ui-core/ChannelControl.h index d29e7577..d7f4b96b 100644 --- a/examples/companion_radio/ui-core/ChannelControl.h +++ b/examples/companion_radio/ui-core/ChannelControl.h @@ -113,11 +113,17 @@ static Result addPublic() { } // "#topic": name and passphrase both "#topic" (a leading '#' typed is kept once). +// Spaces around it are dropped and the case kept, as the phone app does +// (meshcore-open's derivePskFromHashtag()), so "#Kraków " joins the same channel. static Result addHashtag(const char* topic) { - if (topic && topic[0] == '#') topic++; - if (!topic || !topic[0]) return NO_NAME; + if (!topic) return NO_NAME; + while (*topic == ' ') topic++; + if (topic[0] == '#') topic++; char name[32]; snprintf(name, sizeof(name), "#%s", topic); + size_t n = strlen(name); + while (n > 1 && name[n - 1] == ' ') name[--n] = '\0'; + if (n < 2) return NO_NAME; uint8_t secret[32]; phraseToSecret(name, secret); return save(freeSlot(), name, secret); diff --git a/examples/companion_radio/ui-core/UiCore.h b/examples/companion_radio/ui-core/UiCore.h index bd8d2d30..60422003 100644 --- a/examples/companion_radio/ui-core/UiCore.h +++ b/examples/companion_radio/ui-core/UiCore.h @@ -246,6 +246,11 @@ public: } void onChannelMessageRecvEx(mesh::Packet *pkt, uint8_t channel_idx, ChannelDetails& channel_details, uint32_t timestamp, const char* text) override { + // Our own posts are filed as "Me: ..." and every view reads that prefix as + // "ours". Someone else named "Me" gets quotes, so theirs stays theirs (a + // bubble on the left, counted unread). + char quoted[MSG_TEXT_BUF]; + if (strncmp(text, "Me: ", 4) == 0) { snprintf(quoted, sizeof(quoted), "\"Me\"%s", text + 2); text = quoted; } addChannelMsg(channel_idx, text, timestamp, pkt->path, (uint8_t)pkt->path_len); pushMessageArrived(UIEventType::channelMessage, channel_details.name, channel_idx); }