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/2286-locked-empty-card.fixed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
A Bot's **Scheduled tasks** card in a read-only window draws nothing when the Bot has no scheduled tasks, instead of the bare heading over an empty body; a writable window keeps its **New task** row, and a locked card that has tasks keeps its heading and rows ([#2286](https://github.com/ThinkFlowLab/FlyBot/issues/2286)).
1 change: 1 addition & 0 deletions core/client.js
Original file line number Diff line number Diff line change
Expand Up @@ -5924,6 +5924,7 @@ function BotSchedules({ bot, roster, actions }) {
}
if (view === null) return null;
const tasks = view.available ? view.tasks.filter((task) => task.botId === bot.id) : [];
if (locked && view.available && tasks.length === 0) return null;
return (0, import_react6.createElement)(
"section",
{ className: "bt-set-section" },
Expand Down
5 changes: 5 additions & 0 deletions core/src/client/schedules.js
Original file line number Diff line number Diff line change
Expand Up @@ -447,6 +447,11 @@ export function BotSchedules({ bot, roster, actions }) {
}
if (view === null) return null
const tasks = view.available ? view.tasks.filter(task => task.botId === bot.id) : []
// A locked card with no tasks drew the heading over an empty body: it adds no row, and
// TaskList draws nothing without one. The details line hides this whole block in that state
// (#2232, P-457), so the settings card says nothing either. The Automation-tasks-off branch
// below is a different state and keeps its hint and button.
if (locked && view.available && tasks.length === 0) return null
return h('section', { className: 'bt-set-section' },
h('h3', null, t('Scheduled tasks')),
!view.available
Expand Down
28 changes: 28 additions & 0 deletions core/test/schedules-client.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -813,3 +813,31 @@ test('P-457: a read-only details line offers no Add one, and a writable one does
const writable = render(ScheduleSummary({ bot: { id: 'b1' }, roster: ROSTER, actions }))
assert.deepEqual(buttonsOf(writable).map(labelOf), ['No scheduled tasks yet. Add one'], 'a writable details line keeps its Add one')
})

// P-482: after #2246 the same "locked, no tasks" state drew three shapes — ScheduleSummary hid
// the whole block, RoomSchedules drew nothing, and a Bot's card kept its "Scheduled tasks"
// heading over an empty body (TaskList returns null with no rows, and a locked card adds no row).
// A locked card with no tasks now draws nothing; the writable card and a locked card with tasks
// stay as they are.
const sectionsOf = tree => all(tree).filter(node => node.type === 'section')

test('P-482: a locked Bot card with no tasks draws no bare "Scheduled tasks" heading', () => {
seeded.push({ available: true, tasks: [] })
const locked = render(BotSchedules({ bot: { id: 'b1' }, roster: LOCKED_ROSTER, actions: scheduleActions() }))
assert.equal(sectionsOf(locked).length, 0, `a locked zero-task card still draws a section, saw ${text(locked).join('|')}`)
assert.ok(!text(locked).includes('Scheduled tasks'), `a locked zero-task card still says "Scheduled tasks", saw ${text(locked).join('|')}`)
// The same fixture in a writable window is the control: the rule is the lock, not an empty list.
seeded.push({ available: true, tasks: [] })
const writable = render(BotSchedules({ bot: { id: 'b1' }, roster: ROSTER, actions: scheduleActions() }))
assert.equal(sectionsOf(writable).length, 1, 'a writable zero-task card keeps its section')
assert.ok(text(writable).includes('Scheduled tasks'), 'a writable zero-task card keeps its heading')
assert.ok(buttonsOf(writable).map(labelOf).includes('New task'), 'a writable zero-task card keeps the New task row')
})

test('P-482: a locked Bot card with tasks keeps the heading and the rows', () => {
seeded.push({ available: true, tasks: [ACTIVE] })
const locked = render(BotSchedules({ bot: { id: 'b1' }, roster: LOCKED_ROSTER, actions: scheduleActions() }))
assert.equal(sectionsOf(locked).length, 1, 'the rule must not hide a locked card that has tasks')
assert.ok(text(locked).includes('Scheduled tasks'), 'the heading stays with tasks')
assert.ok(text(locked).includes('Briefing'), 'the task row stays')
})
2 changes: 1 addition & 1 deletion docs/subsystems/core-client.md

Large diffs are not rendered by default.

7 changes: 6 additions & 1 deletion patches/ds-bot/0001-core-fixes.patch
Original file line number Diff line number Diff line change
Expand Up @@ -2474,7 +2474,7 @@ diff --git a/src/client/schedules.js b/src/client/schedules.js
kind === 'at' ? h('input', { className: 'bt-input', type: 'datetime-local', value: at, 'aria-label': t('Date and time'), onChange: event => setAt(event.target.value) }) : null,
kind === 'weekly' ? h('div', { className: 'bt-task-days', role: 'group', 'aria-label': t('Days') }, [1, 2, 3, 4, 5, 6, 7].map(day => h('button', {
key: day, type: 'button', className: 'bt-task-day', 'aria-pressed': weekdays.includes(day), onClick: () => toggleDay(day),
@@ -163,75 +163,246 @@ function TaskForm({ roster, actions, start, onDone, onCancel }) {
@@ -163,75 +163,251 @@ function TaskForm({ roster, actions, start, onDone, onCancel }) {
h('input', { className: 'bt-input bt-task-count', type: 'number', min: 1, value: count, 'aria-label': t('How many'), onChange: event => setCount(event.target.value) }),
h('select', { className: 'bt-select bt-task-unit', value: unit, 'aria-label': t('Unit'), onChange: event => setUnit(event.target.value) },
UNITS.map(([id, , label]) => h('option', { key: id, value: id }, t(label))))) : null)),
Expand Down Expand Up @@ -2658,6 +2658,11 @@ diff --git a/src/client/schedules.js b/src/client/schedules.js
+ }
if (view === null) return null
const tasks = view.available ? view.tasks.filter(task => task.botId === bot.id) : []
+ // A locked card with no tasks drew the heading over an empty body: it adds no row, and
+ // TaskList draws nothing without one. The details line hides this whole block in that state
+ // (#2232, P-457), so the settings card says nothing either. The Automation-tasks-off branch
+ // below is a different state and keeps its hint and button.
+ if (locked && view.available && tasks.length === 0) return null
return h('section', { className: 'bt-set-section' },
h('h3', null, t('Scheduled tasks')),
- h('div', { className: 'bt-set-card' },
Expand Down
3 changes: 2 additions & 1 deletion patches/ds-bot/0008-client-bundle.patch
Original file line number Diff line number Diff line change
Expand Up @@ -5029,7 +5029,7 @@ diff --git a/client.js b/client.js
.bt-mem-skel{display:flex;flex-direction:column;gap:24px}
.bt-mem-skel-block{display:flex;flex-direction:column;gap:10px}
.bt-mem-skel-block span{display:block;height:12px;border-radius:6px;background:linear-gradient(90deg,var(--bt-hover) 25%,var(--bt-active) 50%,var(--bt-hover) 75%);background-size:200% 100%;animation:bt-mem-skel 1.4s ease-in-out infinite}
@@ -2574,4596 +2574,9011 @@ body[data-ds-dark-theme] .bt-theme-chip i{background:var(--da)}
@@ -2574,4596 +2574,9012 @@ body[data-ds-dark-theme] .bt-theme-chip i{background:var(--da)}
@media (max-width:640px){.bt-mem{width:100vw;height:100%;border-radius:0;border:0}.bt-mem-body{padding:18px 18px 20px}.bt-mem-foot{padding:8px 12px 12px}}
@media (prefers-reduced-motion:reduce){.bt-mem,.bt-mem-scrim,.bt-mem-summary>*,.bt-mem-empty,.bt-mem-reply,.bt-mem-skel-block span,.bt-mem-entries,.bt-mem-send>*,.bt-mem-changes li svg{animation:none}}

Expand Down Expand Up @@ -6360,6 +6360,7 @@ diff --git a/client.js b/client.js
+ }
+ if (view === null) return null;
+ const tasks = view.available ? view.tasks.filter((task) => task.botId === bot.id) : [];
+ if (locked && view.available && tasks.length === 0) return null;
+ return (0, import_react6.createElement)(
+ "section",
+ { className: "bt-set-section" },
Expand Down
28 changes: 28 additions & 0 deletions patches/ds-bot/files/test/schedules-client.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -813,3 +813,31 @@ test('P-457: a read-only details line offers no Add one, and a writable one does
const writable = render(ScheduleSummary({ bot: { id: 'b1' }, roster: ROSTER, actions }))
assert.deepEqual(buttonsOf(writable).map(labelOf), ['No scheduled tasks yet. Add one'], 'a writable details line keeps its Add one')
})

// P-482: after #2246 the same "locked, no tasks" state drew three shapes — ScheduleSummary hid
// the whole block, RoomSchedules drew nothing, and a Bot's card kept its "Scheduled tasks"
// heading over an empty body (TaskList returns null with no rows, and a locked card adds no row).
// A locked card with no tasks now draws nothing; the writable card and a locked card with tasks
// stay as they are.
const sectionsOf = tree => all(tree).filter(node => node.type === 'section')

test('P-482: a locked Bot card with no tasks draws no bare "Scheduled tasks" heading', () => {
seeded.push({ available: true, tasks: [] })
const locked = render(BotSchedules({ bot: { id: 'b1' }, roster: LOCKED_ROSTER, actions: scheduleActions() }))
assert.equal(sectionsOf(locked).length, 0, `a locked zero-task card still draws a section, saw ${text(locked).join('|')}`)
assert.ok(!text(locked).includes('Scheduled tasks'), `a locked zero-task card still says "Scheduled tasks", saw ${text(locked).join('|')}`)
// The same fixture in a writable window is the control: the rule is the lock, not an empty list.
seeded.push({ available: true, tasks: [] })
const writable = render(BotSchedules({ bot: { id: 'b1' }, roster: ROSTER, actions: scheduleActions() }))
assert.equal(sectionsOf(writable).length, 1, 'a writable zero-task card keeps its section')
assert.ok(text(writable).includes('Scheduled tasks'), 'a writable zero-task card keeps its heading')
assert.ok(buttonsOf(writable).map(labelOf).includes('New task'), 'a writable zero-task card keeps the New task row')
})

test('P-482: a locked Bot card with tasks keeps the heading and the rows', () => {
seeded.push({ available: true, tasks: [ACTIVE] })
const locked = render(BotSchedules({ bot: { id: 'b1' }, roster: LOCKED_ROSTER, actions: scheduleActions() }))
assert.equal(sectionsOf(locked).length, 1, 'the rule must not hide a locked card that has tasks')
assert.ok(text(locked).includes('Scheduled tasks'), 'the heading stays with tasks')
assert.ok(text(locked).includes('Briefing'), 'the task row stays')
})
Loading