From bd6a7c292a554eadd031e4f71df14c6b4d173207 Mon Sep 17 00:00:00 2001 From: Day Dreamer <87640074+HugoXK@users.noreply.github.com> Date: Fri, 23 Oct 2026 16:54:30 +0800 Subject: [PATCH] fix(social): treat an unknown room count as missing, not zero `Number('')` is 0, so `faceInvitePreview` drew a room whose count the host did not send as `0 / 8 in the room`. A trimmed, non-empty value that parses to a finite number now counts; `''`, `' '` and `'eight'` are missing like `null`, while a real `0` still renders. Docs-not-needed: the invite card's room-count parsing is not an owned path in docs/subsystems/. --- changelog.d/823-empty-members-count.fixed.md | 1 + plugins/social/client.js | 14 +++++++++-- plugins/social/test/social.test.js | 25 ++++++++++++++++++++ 3 files changed, 38 insertions(+), 2 deletions(-) create mode 100644 changelog.d/823-empty-members-count.fixed.md diff --git a/changelog.d/823-empty-members-count.fixed.md b/changelog.d/823-empty-members-count.fixed.md new file mode 100644 index 000000000..4b69f4075 --- /dev/null +++ b/changelog.d/823-empty-members-count.fixed.md @@ -0,0 +1 @@ +An invite card no longer shows an unknown member count as `0 / 8 in the room`: an empty or non-numeric value is treated as missing, while a real `0` still shows as a real count ([#823](https://github.com/ThinkFlowLab/FlyBot/issues/823)). diff --git a/plugins/social/client.js b/plugins/social/client.js index 229a36254..d0b040429 100644 --- a/plugins/social/client.js +++ b/plugins/social/client.js @@ -893,14 +893,24 @@ window.__ModuleLoader__.load({ .replace(/\s+/g, ' ') .trim() .slice(0, ROOM_NAME_MAX) || fallback + // A count is only what reads as one (#823): `Number('')` is 0, which would draw an unknown room + // as empty, so an empty or non-numeric string is "not sent", like null. + const roomCount = (value) => { + if (typeof value === 'number') return Number.isFinite(value) ? value : NaN + if (typeof value !== 'string') return NaN + const text = value.trim() + if (!text) return NaN + const count = Number(text) + return Number.isFinite(count) ? count : NaN + } // What the card can honestly say, and nothing else: a room with no field worth showing is no // card at all, so the join below runs as before. const faceInvitePreview = (seen) => { if (!seen || typeof seen !== 'object' || seen.unsupported === true) return null const name = cleanRoomName(seen.name) const lead = cleanRoomName(seen.lead) - const members = seen.members === undefined || seen.members === null ? NaN : Number(seen.members) - const maxMembers = seen.maxMembers === undefined || seen.maxMembers === null ? NaN : Number(seen.maxMembers) + const members = roomCount(seen.members) + const maxMembers = roomCount(seen.maxMembers) const people = Number.isFinite(members) && Number.isFinite(maxMembers) ? `${members} / ${maxMembers} in the room` : Number.isFinite(members) ? `${members} in the room` : '' diff --git a/plugins/social/test/social.test.js b/plugins/social/test/social.test.js index 1c43df1eb..16db1cc20 100644 --- a/plugins/social/test/social.test.js +++ b/plugins/social/test/social.test.js @@ -9122,6 +9122,31 @@ test('#798: a room name with markup renders cleaned, and a count without a maxim assert.equal(dialog.text(dialog.find('face-link-preview-members')), '2 in the room') }) +test('#823: an empty count is missing, not a zero, and a real zero still shows', async () => { + const card = async (members) => { + const dialog = dialogWithState(async (endpoint) => { + if (endpoint === 'social/face-invite-preview') return { name: 'Kitchen sync', members, maxMembers: 8 } + return {} + }) + dialog.find('face-link-join-go').props.onClick() + await dialog.settle() + dialog.render() + return dialog + } + const empty = await card('') + assert.equal(empty.has('face-link-preview-members'), false, "an empty string is a missing count, not '0 / 8'") + const blank = await card(' ') + assert.equal(blank.has('face-link-preview-members'), false, 'whitespace is a missing count, not 0') + const word = await card('eight') + assert.equal(word.has('face-link-preview-members'), false, 'a non-number is a missing count') + const zeroNumber = await card(0) + assert.equal(zeroNumber.text(zeroNumber.find('face-link-preview-members')), '0 / 8 in the room', 'a real numeric zero is a real count') + const zero = await card('0') + assert.equal(zero.text(zero.find('face-link-preview-members')), '0 / 8 in the room', 'a real zero is a real count') + const spaced = await card(' 8 ') + assert.equal(spaced.text(spaced.find('face-link-preview-members')), '8 / 8 in the room', 'whitespace around a number is trimmed') +}) + test('#798: an older host says so and does not join; a 404 still joins', async () => { const older = [] const unsupported = dialogWithState(async (endpoint, payload) => {