feat(communication): add room visibility and project chat controls
All checks were successful
Build and Release FEDEO / verify (push) Successful in 57s
Build and Release FEDEO / build-and-release (push) Successful in 6m27s

This commit is contained in:
2026-10-02 18:27:02 +02:00
parent 0887863981
commit 7e6c2fbac5
14 changed files with 364 additions and 21 deletions

View File

@@ -19,6 +19,7 @@ function fixture(override = () => undefined) {
const result = override(url, options)
if (result !== undefined) return result
if (url === '/api/collaboration') return { rooms: [sharedRoom], invitations: [], managedRooms: [{ id: 'admin-only' }] }
if (url === '/api/communication/chat/room-preferences') return { hiddenRoomKeys: [] }
if (url === '/api/communication/chat/rooms') return { rooms: [nativeRoom] }
if (url.endsWith('/project-rooms') || url.endsWith('/direct-rooms')) return { rooms: [] }
if (url.endsWith('/unread')) return { rooms: { allgemein: { count: 2 } } }
@@ -237,3 +238,92 @@ test('unmount cancels both polling timers and ignores pending lists', async () =
assert.equal(ui.run('collaborationRooms.value.length'), 0)
assert.equal(ui.run('messages.value.length'), 0)
})
test('hiding the active room persists the preference, switches away and allows restoring it', async t => {
const ui = setup()
t.after(ui.close)
ui.mount()
await settle()
await ui.run('setRoomHidden(activeRoom.value, true)')
assert.equal(ui.run('hiddenRooms.value[0].key'), 'allgemein')
assert.equal(ui.run('visibleRooms.value.some(room => room.key === "allgemein")'), false)
assert.equal(ui.run('isCollaboration.value'), true)
assert.equal(ui.run('messages.value.length'), 0)
const save = ui.calls.find(call => call.url.endsWith('/room-preferences') && call.options?.method === 'PUT')
assert.equal(save.options.body.roomKey, 'allgemein')
assert.equal(save.options.body.hidden, true)
await ui.run('setRoomHidden(hiddenRooms.value[0], false)')
assert.equal(ui.run('hiddenRooms.value.length'), 0)
assert.equal(ui.run('activeRoomKey.value'), 'allgemein')
assert.equal(ui.run('messages.value[0].body'), 'Interne Nachricht')
})
test('saved hidden rooms stay hidden on reload, including federated room deep links', async t => {
const ui = setup(fixture(url => url.endsWith('/room-preferences') ? { hiddenRoomKeys: ['collaboration:shared-room', 'allgemein'] } : undefined), { room: 'collaboration:shared-room' })
t.after(ui.close)
ui.mount()
await settle()
assert.equal(ui.run('visibleRooms.value.length'), 0)
assert.equal(ui.run('activeRoom.value'), null)
assert.equal(ui.calls.some(({ url }) => /\/rooms\/.+\/(messages|read)/.test(url)), false)
await ui.run('selectRoom(decoratedRooms.value[0])')
assert.equal(ui.run('activeRoom.value'), null)
await ui.run('setRoomHidden(hiddenRooms.value.find(r => r.source === "collaboration"), false)')
assert.equal(ui.run('isCollaboration.value'), true)
})
test('preference save failure leaves the active conversation and list unchanged', async t => {
const ui = setup(fixture((url, options) => {
if (url.endsWith('/room-preferences') && options?.method === 'PUT') throw { data: { message: 'Speichern fehlgeschlagen' } }
}))
t.after(ui.close)
ui.mount()
await settle()
await ui.run('setRoomHidden(activeRoom.value, true)')
assert.equal(ui.run('hiddenRooms.value.length'), 0)
assert.equal(ui.run('activeRoomKey.value'), 'allgemein')
assert.equal(ui.run('messages.value[0].body'), 'Interne Nachricht')
assert.equal(ui.toasts.at(-1).title, 'Speichern fehlgeschlagen')
})
test('pending hide operations cannot change a new tenant, and hiding the last room clears its content', async t => {
const pending = deferred()
const ui = setup(fixture((url, options) => {
if (url.endsWith('/room-preferences') && options?.method === 'PUT') return pending.promise
}))
t.after(ui.close)
ui.mount()
await settle()
const hiding = ui.run('setRoomHidden(activeRoom.value, true)')
ui.auth.activeTenant = '2'
await settle()
pending.resolve({ hidden: true, roomKey: 'allgemein' })
await hiding
assert.equal(ui.run('hiddenRoomKeys.value.length'), 0)
assert.equal(ui.run('activeRoomKey.value'), 'allgemein')
const single = setup(fixture(url => url === '/api/collaboration' ? { rooms: [], invitations: [] } : undefined))
t.after(single.close)
single.mount()
await settle()
await single.run('setRoomHidden(activeRoom.value, true)')
await settle()
assert.equal(single.run('activeRoom.value'), null)
assert.equal(single.run('messages.value.length'), 0)
assert.equal(single.run('members.value.length'), 0)
assert.equal(single.run('visibleRooms.value.length'), 0)
})
test('removing an active project from the permitted room list clears its messages and members', async t => {
let disabled = false
const ui = setup(fixture(url => url.endsWith('/project-rooms') ? { rooms: disabled ? [] : [{ key: 'project_1', exists: true, type: 'project' }] } : undefined), { room: 'project_1' })
t.after(ui.close)
ui.mount()
await settle()
assert.equal(ui.run('messages.value.length'), 1)
disabled = true
await ui.run('loadRoomLists()')
await settle()
assert.equal(ui.run('activeRoom.value'), null)
assert.equal(ui.run('messages.value.length'), 0)
assert.equal(ui.run('members.value.length'), 0)
})