Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog.d/1052-enter-6-digits.fixed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Typing a room number that is not exactly six digits keeps Join disabled and shows "Enter 6 digits" under the field (Chinese: 请输入 6 位数字). An empty field stays quiet, and six digits still join ([#1052](https://github.com/ThinkFlowLab/FlyBot/issues/1052)).
2 changes: 2 additions & 0 deletions core/client.js
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@ var ZH = {
"6-digit room number": "6 位房间号",
"Pick a Bot first.": "请先选一个 Bot。",
"Enter all 6 digits.": "请输入完整的 6 位数字。",
// #1052: visible under the field when the number is not empty and not six digits.
"Enter 6 digits": "请输入 6 位数字",
"Join with link": "用链接加入",
"Scan a code": "扫码",
"Join group": "加入群",
Expand Down
2 changes: 2 additions & 0 deletions core/src/client/i18n.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ const ZH = {
'6-digit room number': '6 位房间号',
'Pick a Bot first.': '请先选一个 Bot。',
'Enter all 6 digits.': '请输入完整的 6 位数字。',
// #1052: visible under the field when the number is not empty and not six digits.
'Enter 6 digits': '请输入 6 位数字',
'Join with link': '用链接加入',
'Scan a code': '扫码',
'Join group': '加入群',
Expand Down
2 changes: 1 addition & 1 deletion docs/subsystems/core-client.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ The overlay (`flybot-slots.js` `FlyBotApprovals`) is the fallback, not a second

### Face-to-face room lead

- A local group's **Members · N** card (`group-settings.js`) ends with the plugin rows of slot `flybot.room.members` (`FlyBotRoomMembers` in `flybot-slots.js`), after the note on who leads the group (#892) and on the card's own row padding (`.bt-flybot-room-members`), never in a face or shared (`inbound-share`) room; the rows get `host`, the core endpoint call (also `__flybot__.host`), for `room-invite` / `room-invite-mint` / `room-invite-revoke` (#876, the social plugin's **Invite by link**). Covered by `core/test/room-members-slot.test.js`. Plugin copy that core's dictionary carries is translated through `__flybot__.t` (#892: Invite by link, the Join-or-create dialog), so a zh UI reads it in Chinese. A room a link opened is titled by its own name on the joining home (`roomTitle` in `remote-label.js`: sidebar, header, drawer, brand); "Shared room from …" stays as the sidebar row's hover and the **Shared** tag's title (#892). In a room projected from another home the Members rows carry what that side could confirm (#904): a Bot learned from the host's roster reads **Unconfirmed** until this home holds its own fact about it, one with no Bot Social identity reads **Not connected**, and the remove **×** is not offered there — the host's membership is changed on the host's side.
- A local group's **Members · N** card (`group-settings.js`) ends with the plugin rows of slot `flybot.room.members` (`FlyBotRoomMembers` in `flybot-slots.js`), after the note on who leads the group (#892) and on the card's own row padding (`.bt-flybot-room-members`), never in a face or shared (`inbound-share`) room; the rows get `host`, the core endpoint call (also `__flybot__.host`), for `room-invite` / `room-invite-mint` / `room-invite-revoke` (#876, the social plugin's **Invite by link**). Covered by `core/test/room-members-slot.test.js`. Plugin copy that core's dictionary carries is translated through `__flybot__.t` (#892: Invite by link, the Join-or-create dialog, including **Enter 6 digits** under a partial room number, #1052), so a zh UI reads it in Chinese. A room a link opened is titled by its own name on the joining home (`roomTitle` in `remote-label.js`: sidebar, header, drawer, brand); "Shared room from …" stays as the sidebar row's hover and the **Shared** tag's title (#892). In a room projected from another home the Members rows carry what that side could confirm (#904): a Bot learned from the host's roster reads **Unconfirmed** until this home holds its own fact about it, one with no Bot Social identity reads **Not connected**, and the remove **×** is not offered there — the host's membership is changed on the host's side.
- The face room's details (`FaceMembers` in `details-drawer.js`) open with `FaceLead` (`group-settings.js`, #476): the current lead as `Bot (person)`, *Default* until the creator picked one. Only when `room.face.lead.canPick` (this home made the room and Bot Social stores a lead) does it show **Change**, a **Make lead** button per Bot of every home and **Use the default lead**; each calls `social/face-lead`. Everyone else sees it read-only. `FacePersonRow` marks the lead's Bot with a *Lead* tag (`isLeadSeat`, by handle). Never a chat command.

### Room avatars in face-to-face chats
Expand Down
5 changes: 3 additions & 2 deletions docs/subsystems/social.md

Large diffs are not rendered by default.

4 changes: 3 additions & 1 deletion patches/ds-bot/0001-core-fixes.patch
Original file line number Diff line number Diff line change
Expand Up @@ -447,7 +447,7 @@ diff --git a/src/client/feedback.js b/src/client/feedback.js
diff --git a/src/client/i18n.js b/src/client/i18n.js
--- a/src/client/i18n.js
+++ b/src/client/i18n.js
@@ -12,6 +12,48 @@ const ZH = {
@@ -12,6 +12,50 @@ const ZH = {
'Unread activity': '有未读',
Search: '搜索',
'New chat': '新对话',
Expand All @@ -468,6 +468,8 @@ diff --git a/src/client/i18n.js b/src/client/i18n.js
+ '6-digit room number': '6 位房间号',
+ 'Pick a Bot first.': '请先选一个 Bot。',
+ 'Enter all 6 digits.': '请输入完整的 6 位数字。',
+ // #1052: visible under the field when the number is not empty and not six digits.
+ 'Enter 6 digits': '请输入 6 位数字',
+ 'Join with link': '用链接加入',
+ 'Scan a code': '扫码',
+ 'Join group': '加入群',
Expand Down
4 changes: 3 additions & 1 deletion patches/ds-bot/0008-client-bundle.patch
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ FlyBot patch 0008-client-bundle on Fei2411/DeepSeek-Bot@2b10c70 (ds-bot/). Gener
diff --git a/client.js b/client.js
--- a/client.js
+++ b/client.js
@@ -49,6 +49,48 @@
@@ -49,6 +49,50 @@
"Unread activity": "有未读",
Search: "搜索",
"New chat": "新对话",
Expand All @@ -24,6 +24,8 @@ diff --git a/client.js b/client.js
+ "6-digit room number": "6 位房间号",
+ "Pick a Bot first.": "请先选一个 Bot。",
+ "Enter all 6 digits.": "请输入完整的 6 位数字。",
+ // #1052: visible under the field when the number is not empty and not six digits.
+ "Enter 6 digits": "请输入 6 位数字",
+ "Join with link": "用链接加入",
+ "Scan a code": "扫码",
+ "Join group": "加入群",
Expand Down
8 changes: 7 additions & 1 deletion plugins/social/client.js
Original file line number Diff line number Diff line change
Expand Up @@ -676,7 +676,7 @@
function qrRsGenerator(degree) {
let poly = [1]
for (let i = 0; i < degree; i += 1) {
const next = new Array(poly.length + 1).fill(0)

Check warning on line 679 in plugins/social/client.js

View workflow job for this annotation

GitHub Actions / plugins (social)

unicorn(no-new-array)

Do not use `new Array(singleArgument)`.
for (let j = 0; j < poly.length; j += 1) {
next[j] ^= poly[j]
next[j + 1] ^= qrMul(poly[j], QR_GF_EXP[i])
Expand All @@ -687,7 +687,7 @@
}
function qrRsRemainder(bytes, degree) {
const gen = qrRsGenerator(degree)
const res = new Array(bytes.length + degree).fill(0)

Check warning on line 690 in plugins/social/client.js

View workflow job for this annotation

GitHub Actions / plugins (social)

unicorn(no-new-array)

Do not use `new Array(singleArgument)`.
for (let i = 0; i < bytes.length; i += 1) res[i] = bytes[i]
for (let i = 0; i < bytes.length; i += 1) {
const factor = res[i]
Expand Down Expand Up @@ -761,8 +761,8 @@
}
function qrSkeleton(version) {
const size = version * 4 + 17
const modules = Array.from({ length: size }, () => new Array(size).fill(0))

Check warning on line 764 in plugins/social/client.js

View workflow job for this annotation

GitHub Actions / plugins (social)

unicorn(no-new-array)

Do not use `new Array(singleArgument)`.
const fixed = Array.from({ length: size }, () => new Array(size).fill(false))

Check warning on line 765 in plugins/social/client.js

View workflow job for this annotation

GitHub Actions / plugins (social)

unicorn(no-new-array)

Do not use `new Array(singleArgument)`.
const set = (x, y, dark) => { modules[y][x] = dark ? 1 : 0; fixed[y][x] = true }
const finder = (cx, cy) => {
for (let dy = -4; dy <= 4; dy += 1) {
Expand Down Expand Up @@ -1005,7 +1005,7 @@
return out
}
const cleanRoomName = (value, fallback = '') => stripUntrustedMarkers(value)
.replace(/[\u0000-\u001f\u007f-\u009f\u2028\u2029]/g, ' ')

Check warning on line 1008 in plugins/social/client.js

View workflow job for this annotation

GitHub Actions / plugins (social)

eslint(no-control-regex)

Unexpected control characters
.replace(/[[\]"<>`@]/g, '')
.replace(/\s+/g, ' ')
.trim()
Expand Down Expand Up @@ -1049,7 +1049,7 @@
let matrix
try { matrix = qrMatrix(text) } catch { return null }
const size = matrix.size
return h('div', { className: 'fs-qr', 'data-flybot': kind, 'data-modules': size, ...(attrs ?? {}) },

Check warning on line 1052 in plugins/social/client.js

View workflow job for this annotation

GitHub Actions / plugins (social)

unicorn(no-useless-fallback-in-spread)

Empty fallbacks in spreads are unnecessary
h('svg', {
className: 'fs-qr-svg', width: size, height: size, viewBox: `0 0 ${size} ${size}`,
role: 'img', 'aria-label': label, shapeRendering: 'crispEdges',
Expand Down Expand Up @@ -1324,7 +1324,7 @@
const next = id || null
if (next === this.roomId) return
this.roomId = next
for (const fn of [...this.listeners]) fn()

Check warning on line 1327 in plugins/social/client.js

View workflow job for this annotation

GitHub Actions / plugins (social)

unicorn(no-useless-spread)

Using a spread operator here creates a new array unnecessarily.
},
subscribe(fn) {
this.listeners.add(fn)
Expand Down Expand Up @@ -1720,7 +1720,10 @@
const preview = invite?.preview ?? null
const candidate = room?.status === 'candidate'
const waiting = step === 'waiting' && room
const digitsTyped = String(code).replace(/\D/g, '').length === 6
const digitCount = String(code).replace(/\D/g, '').length
const digitsTyped = digitCount === 6
// #1052: the helper is for a started number only. Empty stays quiet; six digits clear it.
const digitsPartial = digitCount > 0 && !digitsTyped
const nobody = step === 'nobody' ? faceWaitCode(room, code) : ''
return h('div', {
className: 'fs-dialog-back', 'data-flybot': 'face-group', role: 'dialog', 'aria-label': tr(JOIN_OR_CREATE),
Expand Down Expand Up @@ -1792,6 +1795,9 @@
disabled: busy || !selected.length || !digitsTyped,
title: !selected.length ? tr('Pick a Bot first.') : digitsTyped ? undefined : tr('Enter all 6 digits.'),
}, tr('Join'))),
digitsPartial ? h('div', {
className: 'fs-hint', 'data-flybot': 'face-join-digits', role: 'status',
}, tr('Enter 6 digits')) : null,
h('div', { className: 'fs-link-row', 'data-flybot': 'face-link-join' },
h('input', {
className: 'fs-link-input', value: invite.link, type: 'url', autoComplete: 'off', spellCheck: false,
Expand Down
40 changes: 40 additions & 0 deletions plugins/social/test/social.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -3607,6 +3607,46 @@ test('#306/#458: Join is enabled once a Bot is picked and all 6 digits are in; t
assert.deepEqual(JSON.parse(JSON.stringify(statuses)), [{ tone: 'error', text: 'Enter a 6-digit number.' }])
})

test('#1052: a partial room number shows Enter 6 digits and keeps Join disabled', async () => {
const { walk, pluginOf } = loadSocialClient(async () => ({}))
const bots = [{ id: 'a', name: 'Chief', isMain: true }]
const view = (code, selected = ['a']) => walk(pluginOf(['setup', bots, selected, code]).FaceToFaceGroup({}))
const helper = (tree) => tree.find(n => n.props['data-flybot'] === 'face-join-digits')
const join = (tree) => tree.find(n => n.type === 'button' && n.props.type === 'submit')
const text = (node) => (node?.children ?? []).map(child => (typeof child === 'string' ? child : '')).join('')

const five = view('58291')
assert.equal(text(helper(five)), 'Enter 6 digits')
assert.equal(join(five).props.disabled, true)
assert.equal(five.find(n => n.type === 'input' && n.props.className === 'fs-digits').props.placeholder, '6 digits')
assert.ok(five.some(n => n.props['data-flybot'] === 'face-link-join'), 'the invite-link field stays on the same screen')

const one = view('5')
assert.equal(text(helper(one)), 'Enter 6 digits')
assert.equal(join(one).props.disabled, true)

// Digits are what count: letters in a forced state are not a six-digit code.
const mixed = view('12ab')
assert.equal(text(helper(mixed)), 'Enter 6 digits')
assert.equal(join(mixed).props.disabled, true)

const empty = view('')
assert.equal(helper(empty), undefined, 'an empty field stays quiet')
assert.equal(join(empty).props.disabled, true)

const six = view('582913')
assert.equal(helper(six), undefined, 'six digits clear the helper')
assert.equal(join(six).props.disabled, false)

const unpicked = view('58291', [])
assert.equal(text(helper(unpicked)), 'Enter 6 digits')
assert.equal(join(unpicked).props.disabled, true)
assert.ok(unpicked.some(n => n.props['data-flybot'] === 'face-join-pick'))

const { DICTIONARY } = await import('../../../core/src/client/i18n.js')
assert.equal(DICTIONARY.zh['Enter 6 digits'], '请输入 6 位数字')
})

test('#306: the waiting dialog asks before Leave, lands on the main Bot, and the panel draws no second waiting card', async () => {
const calls = []
const { walk, pluginOf } = loadSocialClient(async (endpoint, payload) => { calls.push([endpoint, payload]); return {} })
Expand Down
Loading