From 44c5123ef4296f44e142f0ea10b9fb2a63079b7d Mon Sep 17 00:00:00 2001 From: Dominik Henneke Date: Fri, 20 Jan 2023 11:24:45 +0100 Subject: [PATCH 01/19] Disable the feature to link matrix rooms to a session Signed-off-by: Dominik Henneke --- src/components/Layout/Layout.test.tsx | 4 ++-- src/components/SessionGrid/SessionSlot.tsx | 3 +-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/components/Layout/Layout.test.tsx b/src/components/Layout/Layout.test.tsx index ad0b5fd..f94a934 100644 --- a/src/components/Layout/Layout.test.tsx +++ b/src/components/Layout/Layout.test.tsx @@ -788,7 +788,7 @@ describe('', () => { ).resolves.toBeInTheDocument(); }); - it('should assign a matrix room to a session topic', async () => { + it.skip('should assign a matrix room to a session topic', async () => { render(, { wrapper }); const stickyNote = await screen.findByRole('button', { @@ -817,7 +817,7 @@ describe('', () => { expect(linkRoomButton).not.toBeInTheDocument(); }); - it('should navigate to a session room', async () => { + it.skip('should navigate to a session room', async () => { mockInitializeSpaceParent(widgetApi, { room_id: '!linked-room-id' }); widgetApi.mockSendStateEvent(mockLinkedRoom()); diff --git a/src/components/SessionGrid/SessionSlot.tsx b/src/components/SessionGrid/SessionSlot.tsx index b76ddd4..7f91605 100644 --- a/src/components/SessionGrid/SessionSlot.tsx +++ b/src/components/SessionGrid/SessionSlot.tsx @@ -23,7 +23,6 @@ import { } from '../DragAndDropProvider'; import { DraggableStickyNote, TopicChanges } from '../StickyNote'; import { styled } from '../StyledComponentsThemeProvider'; -import { LinkedRoomButton } from './LinkedRoomButton'; import { PinIcon } from './PinIcon'; import { PinnedNoteButton } from './PinnedNoteButton'; @@ -79,7 +78,7 @@ export function SessionSlot({ ? (changes) => onTopicChange(topicId, changes) : undefined } - headerSlot={} + //headerSlot={} expandedHeaderSlot={ canModerate ? ( Date: Fri, 20 Jan 2023 13:30:15 +0100 Subject: [PATCH 02/19] Store the session grid in the same room and don't expect the room to be part of a space Signed-off-by: Dominik Henneke --- src/components/Layout/Layout.test.tsx | 37 +++++-------- src/components/Layout/WelcomeLayout.tsx | 5 -- .../LinkRoomDialog/LinkRoomDialog.test.tsx | 2 +- src/components/ParkingLot/ParkingLot.test.tsx | 2 +- .../SessionGrid/SessionSlot.test.tsx | 2 +- src/lib/testUtils.ts | 6 +-- src/store/api/linkedRoomApi.test.ts | 2 +- src/store/api/roomMemberApi.test.ts | 2 +- src/store/api/sessionGridApi.test.ts | 54 +++++++++---------- src/store/api/spaceApi.test.ts | 20 +++---- src/store/api/spaceApi.ts | 9 ++++ src/store/api/topicApi.test.ts | 10 ++-- src/store/api/usePowerLevels.test.tsx | 2 +- src/store/api/useRoomNavigation.test.tsx | 2 +- src/store/api/useSessionTopic.test.tsx | 2 +- 15 files changed, 74 insertions(+), 83 deletions(-) diff --git a/src/components/Layout/Layout.test.tsx b/src/components/Layout/Layout.test.tsx index f94a934..b368aae 100644 --- a/src/components/Layout/Layout.test.tsx +++ b/src/components/Layout/Layout.test.tsx @@ -153,7 +153,7 @@ describe('', () => { ).toBeInTheDocument(); }); - it('should render error message if not in a space', async () => { + it.skip('should render error message if not in a space', async () => { widgetApi.clearStateEvents(); render(, { wrapper }); @@ -166,7 +166,7 @@ describe('', () => { it('should render welcome screen for participants if no session grid was found', async () => { widgetApi.clearStateEvents(); widgetApi.mockSendStateEvent( - mockParticipantPowerLevelsEvent({ room_id: '!space-id' }) + mockParticipantPowerLevelsEvent({ room_id: '!room-id' }) ); mockInitializeSpaceParent(widgetApi); @@ -185,9 +185,7 @@ describe('', () => { it('should render welcome screen for moderators if no session grid was found and setup the widget', async () => { widgetApi.clearStateEvents(); - widgetApi.mockSendStateEvent( - mockPowerLevelsEvent({ room_id: '!space-id' }) - ); + widgetApi.mockSendStateEvent(mockPowerLevelsEvent({ room_id: '!room-id' })); mockInitializeSpaceParent(widgetApi); render(, { wrapper }); @@ -204,7 +202,7 @@ describe('', () => { ); await waitFor(() => { - expect(widgetApi.sendStateEvent).toBeCalledTimes(6); + expect(widgetApi.sendStateEvent).toBeCalledTimes(5); }); expect(widgetApi.sendRoomEvent).toBeCalledTimes(1); @@ -224,7 +222,7 @@ describe('', () => { topicStartEventId: expect.any(String), }, { - roomId: '!space-id', + roomId: '!room-id', stateKey: '!room-id', } ); @@ -233,21 +231,12 @@ describe('', () => { { history_visibility: 'shared' }, { roomId: '!room-id' } ); - expect(widgetApi.sendStateEvent).toBeCalledWith( - 'm.space.child', - { - via: ['matrix.to'], - order: ' lobby', - suggested: true, - }, - { - roomId: '!space-id', - stateKey: '!room-id', - } - ); expect(widgetApi.sendStateEvent).toBeCalledWith( 'm.room.power_levels', { + users: { + '@user-id': 100, + }, events: { 'net.nordeck.barcamp.topic_submission': 50, }, @@ -275,9 +264,7 @@ describe('', () => { it('should render welcome screen and ask for confirmation if setup is performed in an e2ee enabled room', async () => { widgetApi.clearStateEvents(); - widgetApi.mockSendStateEvent( - mockPowerLevelsEvent({ room_id: '!space-id' }) - ); + widgetApi.mockSendStateEvent(mockPowerLevelsEvent({ room_id: '!room-id' })); widgetApi.mockSendStateEvent(mockRoomEncryption()); mockInitializeSpaceParent(widgetApi); @@ -306,7 +293,7 @@ describe('', () => { expect(confirmModal).not.toBeInTheDocument(); await waitFor(() => { - expect(widgetApi.sendStateEvent).toBeCalledTimes(6); + expect(widgetApi.sendStateEvent).toBeCalledTimes(5); }); }); @@ -839,7 +826,7 @@ describe('', () => { ); }); - it('should display the current topic if added to a session room', async () => { + it.skip('should display the current topic if added to a session room', async () => { mockInitializeSpaceParent(widgetApi, { room_id: 'lobby-room-id' }); widgetApi.mockSendStateEvent( @@ -916,7 +903,7 @@ describe('', () => { expect(screen.getByText(/We share updates/)).toBeInTheDocument(); }); - it('should navigate to the lobby room', async () => { + it.skip('should navigate to the lobby room', async () => { mockInitializeSpaceParent(widgetApi, { room_id: 'lobby-room-id' }); widgetApi.mockSendStateEvent( diff --git a/src/components/Layout/WelcomeLayout.tsx b/src/components/Layout/WelcomeLayout.tsx index f5bbbf2..63a0bbe 100644 --- a/src/components/Layout/WelcomeLayout.tsx +++ b/src/components/Layout/WelcomeLayout.tsx @@ -26,7 +26,6 @@ import { import { ROOM_EVENT_BARCAMP_TOPIC_SUBMISSION } from '../../lib/events'; import { useHasRoomEncryptionQuery, - useMarkRoomAsSuggestedMutation, usePatchPowerLevelsMutation, usePatchRoomHistoryVisibilityMutation, usePowerLevels, @@ -38,7 +37,6 @@ import { ConfirmDialog } from '../ConfirmDialog'; function ModeratorWelcomeLayout() { const { t } = useTranslation(); const [setupSessionGrid] = useSetupSessionGridMutation(); - const [markRoomAsSuggested] = useMarkRoomAsSuggestedMutation(); const [patchPowerLevels] = usePatchPowerLevelsMutation(); const [patchRoomHistoryVisibility] = usePatchRoomHistoryVisibilityMutation(); const [setupLobbyRoomWidgets] = useSetupLobbyRoomWidgetsMutation(); @@ -46,11 +44,8 @@ function ModeratorWelcomeLayout() { async function handleSetup() { const { event } = await setupSessionGrid().unwrap(); - const spaceId = event.room_id; const roomId = event.state_key; - await markRoomAsSuggested({ spaceId, roomId }).unwrap(); - await patchPowerLevels({ roomId, changes: { diff --git a/src/components/LinkRoomDialog/LinkRoomDialog.test.tsx b/src/components/LinkRoomDialog/LinkRoomDialog.test.tsx index acde5d5..6c8ad53 100644 --- a/src/components/LinkRoomDialog/LinkRoomDialog.test.tsx +++ b/src/components/LinkRoomDialog/LinkRoomDialog.test.tsx @@ -29,7 +29,7 @@ import { import { StoreProvider } from '../../store'; import { LinkRoomDialog } from './LinkRoomDialog'; -describe('', () => { +describe.skip('', () => { let wrapper: ComponentType>; let widgetApi: MockedWidgetApi; diff --git a/src/components/ParkingLot/ParkingLot.test.tsx b/src/components/ParkingLot/ParkingLot.test.tsx index 7b2bcbf..37ba062 100644 --- a/src/components/ParkingLot/ParkingLot.test.tsx +++ b/src/components/ParkingLot/ParkingLot.test.tsx @@ -140,7 +140,7 @@ describe('', () => { it('should make topics read-only for participants', async () => { widgetApi.mockSendStateEvent( - mockParticipantPowerLevelsEvent({ room_id: '!space-id' }) + mockParticipantPowerLevelsEvent({ room_id: '!room-id' }) ); render(, { wrapper }); diff --git a/src/components/SessionGrid/SessionSlot.test.tsx b/src/components/SessionGrid/SessionSlot.test.tsx index c923610..be834c2 100644 --- a/src/components/SessionGrid/SessionSlot.test.tsx +++ b/src/components/SessionGrid/SessionSlot.test.tsx @@ -145,7 +145,7 @@ describe('', () => { it('should make topics read only for participants', async () => { widgetApi.mockSendStateEvent( - mockParticipantPowerLevelsEvent({ room_id: '!space-id' }) + mockParticipantPowerLevelsEvent({ room_id: '!room-id' }) ); render( diff --git a/src/lib/testUtils.ts b/src/lib/testUtils.ts index b64bf3f..4bf558c 100644 --- a/src/lib/testUtils.ts +++ b/src/lib/testUtils.ts @@ -100,7 +100,7 @@ export function mockParticipantPowerLevelsEvent({ */ export function mockSessionGrid({ state_key = '!room-id', - room_id = '!space-id', + room_id = '!room-id', content = {}, }: { state_key?: string; @@ -159,7 +159,7 @@ export function mockSessionGridStart({ */ export function mockTopic({ state_key = 'topic-0', - room_id = '!space-id', + room_id = '!room-id', content = {}, }: { state_key?: string; @@ -277,7 +277,7 @@ export function mockTopicSubmission({ */ export function mockRoomMember({ event_id = nanoid(), - room_id = '!space-id', + room_id = '!room-id', state_key = '@user-id', content = {}, }: { diff --git a/src/store/api/linkedRoomApi.test.ts b/src/store/api/linkedRoomApi.test.ts index ed0b4a3..601ab0e 100644 --- a/src/store/api/linkedRoomApi.test.ts +++ b/src/store/api/linkedRoomApi.test.ts @@ -30,7 +30,7 @@ afterEach(() => widgetApi.stop()); beforeEach(() => (widgetApi = mockWidgetApi())); -describe('getLinkedRooms', () => { +describe.skip('getLinkedRooms', () => { it('should return linked rooms', async () => { mockInitializeSpaceParent(widgetApi); diff --git a/src/store/api/roomMemberApi.test.ts b/src/store/api/roomMemberApi.test.ts index 3430376..dfe703e 100644 --- a/src/store/api/roomMemberApi.test.ts +++ b/src/store/api/roomMemberApi.test.ts @@ -96,7 +96,7 @@ describe('getRoomMembers', () => { ); }); - it('should observe room members when space changes', async () => { + it.skip('should observe room members when space changes', async () => { const store = createStore({ widgetApi }); store.dispatch(roomMemberApi.endpoints.getRoomMembers.initiate()); diff --git a/src/store/api/sessionGridApi.test.ts b/src/store/api/sessionGridApi.test.ts index 6dece33..88c3bea 100644 --- a/src/store/api/sessionGridApi.test.ts +++ b/src/store/api/sessionGridApi.test.ts @@ -50,7 +50,7 @@ describe('getSessionGrid', () => { ).resolves.toEqual({ event: sessionGrid }); }); - it('should handle missing session grid if not in a space', async () => { + it.skip('should handle missing session grid if not in a space', async () => { const store = createStore({ widgetApi }); await expect( @@ -114,7 +114,7 @@ describe('getSessionGrid', () => { }); }); - it('should observe session grid when space changes', async () => { + it.skip('should observe session grid when space changes', async () => { const store = createStore({ widgetApi }); store.dispatch(sessionGridApi.endpoints.getSessionGrid.initiate()); @@ -183,7 +183,7 @@ describe('setupSessionGrid', () => { ], topicStartEventId: expect.any(String), }, - { roomId: '!space-id', stateKey: '!room-id' } + { roomId: '!room-id', stateKey: '!room-id' } ); }); }); @@ -251,7 +251,7 @@ describe('selectNextTopic', () => { title: 'Title 1', authors: [{ id: '@author-1' }], }, - { roomId: '!space-id', stateKey: '$event-1' } + { roomId: '!room-id', stateKey: '$event-1' } ); expect(widgetApi.sendStateEvent).toBeCalledWith( 'net.nordeck.barcamp.session_grid', @@ -261,7 +261,7 @@ describe('selectNextTopic', () => { parkingLot: [{ topicId: '$event-1' }, { topicId: '$event-0' }], }, }).content, - { roomId: '!space-id', stateKey: '!room-id' } + { roomId: '!room-id', stateKey: '!room-id' } ); }); @@ -370,7 +370,7 @@ describe('moveTopicToParkingArea', () => { ], }, }).content, - { roomId: '!space-id', stateKey: '!room-id' } + { roomId: '!room-id', stateKey: '!room-id' } ); }); @@ -434,7 +434,7 @@ describe('moveTopicToParkingArea', () => { sessions: [], }, }).content, - { roomId: '!space-id', stateKey: '!room-id' } + { roomId: '!room-id', stateKey: '!room-id' } ); }); }); @@ -539,7 +539,7 @@ describe('moveTopicToSession', () => { }, ], }), - { roomId: '!space-id', stateKey: '!room-id' } + { roomId: '!room-id', stateKey: '!room-id' } ); }); @@ -618,7 +618,7 @@ describe('moveTopicToSession', () => { ], parkingLot: [], }), - { roomId: '!space-id', stateKey: '!room-id' } + { roomId: '!room-id', stateKey: '!room-id' } ); }); @@ -746,7 +746,7 @@ describe('updateTimeSlot', () => { }, ], }), - { roomId: '!space-id', stateKey: '!room-id' } + { roomId: '!room-id', stateKey: '!room-id' } ); }); @@ -819,7 +819,7 @@ describe('updateTimeSlot', () => { }, ], }), - { roomId: '!space-id', stateKey: '!room-id' } + { roomId: '!room-id', stateKey: '!room-id' } ); }); @@ -1039,7 +1039,7 @@ describe('updateCommonEvent', () => { }, ], }), - { roomId: '!space-id', stateKey: '!room-id' } + { roomId: '!room-id', stateKey: '!room-id' } ); }); }); @@ -1088,7 +1088,7 @@ describe('updateTrack', () => { }, ], }), - { roomId: '!space-id', stateKey: '!room-id' } + { roomId: '!room-id', stateKey: '!room-id' } ); }); }); @@ -1133,7 +1133,7 @@ describe('addTrack', () => { }, ], }), - { roomId: '!space-id', stateKey: '!room-id' } + { roomId: '!room-id', stateKey: '!room-id' } ); }); }); @@ -1208,7 +1208,7 @@ describe('deleteTrack', () => { sessions: [], parkingLot: [{ topicId: 'id-1' }, { topicId: 'id-0' }], }), - { roomId: '!space-id', stateKey: '!room-id' } + { roomId: '!room-id', stateKey: '!room-id' } ); }); @@ -1279,7 +1279,7 @@ describe('deleteTopic', () => { expect.objectContaining({ sessions: [], }), - { roomId: '!space-id', stateKey: '!room-id' } + { roomId: '!room-id', stateKey: '!room-id' } ); }); @@ -1309,7 +1309,7 @@ describe('deleteTopic', () => { expect.objectContaining({ parkingLot: [], }), - { roomId: '!space-id', stateKey: '!room-id' } + { roomId: '!room-id', stateKey: '!room-id' } ); }); }); @@ -1402,7 +1402,7 @@ describe('deleteTimeSlot', () => { { topicId: 'parking-lot-topic-0' }, ], }), - { roomId: '!space-id', stateKey: '!room-id' } + { roomId: '!room-id', stateKey: '!room-id' } ); }); @@ -1480,7 +1480,7 @@ describe('deleteTimeSlot', () => { }, ], }), - { roomId: '!space-id', stateKey: '!room-id' } + { roomId: '!room-id', stateKey: '!room-id' } ); }); @@ -1570,7 +1570,7 @@ describe('addTimeSlot', () => { }, ], }), - { roomId: '!space-id', stateKey: '!room-id' } + { roomId: '!room-id', stateKey: '!room-id' } ); }); @@ -1613,7 +1613,7 @@ describe('addTimeSlot', () => { }, ], }), - { roomId: '!space-id', stateKey: '!room-id' } + { roomId: '!room-id', stateKey: '!room-id' } ); }); @@ -1667,7 +1667,7 @@ describe('addTimeSlot', () => { }, ], }), - { roomId: '!space-id', stateKey: '!room-id' } + { roomId: '!room-id', stateKey: '!room-id' } ); }); }); @@ -1740,7 +1740,7 @@ describe('moveTimeSlot', () => { }, ], }), - { roomId: '!space-id', stateKey: '!room-id' } + { roomId: '!room-id', stateKey: '!room-id' } ); }); @@ -1811,7 +1811,7 @@ describe('moveTimeSlot', () => { }, ], }), - { roomId: '!space-id', stateKey: '!room-id' } + { roomId: '!room-id', stateKey: '!room-id' } ); }); @@ -1882,7 +1882,7 @@ describe('moveTimeSlot', () => { }, ], }), - { roomId: '!space-id', stateKey: '!room-id' } + { roomId: '!room-id', stateKey: '!room-id' } ); }); }); @@ -1924,7 +1924,7 @@ describe('updateSessionGrid', () => { tracks: [], topicStartEventId: '$start-event-id', }, - room_id: '!space-id', + room_id: '!room-id', sender: '@user-id', state_key: '!room-id', }), @@ -1948,7 +1948,7 @@ describe('updateSessionGrid', () => { tracks: [], topicStartEventId: '$start-event-id', }, - { roomId: '!space-id', stateKey: '!room-id' } + { roomId: '!room-id', stateKey: '!room-id' } ); }); diff --git a/src/store/api/spaceApi.test.ts b/src/store/api/spaceApi.test.ts index 0009028..a1292f8 100644 --- a/src/store/api/spaceApi.test.ts +++ b/src/store/api/spaceApi.test.ts @@ -44,10 +44,10 @@ describe('getSpaceRoom', () => { await expect( store.dispatch(spaceApi.endpoints.getSpaceRoom.initiate()).unwrap() - ).resolves.toEqual({ spaceId: '!space-id' }); + ).resolves.toEqual({ spaceId: '!room-id' }); }); - it('should handle missing space room and recover from it', async () => { + it.skip('should handle missing space room and recover from it', async () => { const store = createStore({ widgetApi }); await expect( @@ -66,7 +66,7 @@ describe('getSpaceRoom', () => { ); }); - it('should handle changed space room', async () => { + it.skip('should handle changed space room', async () => { const store = createStore({ widgetApi }); const { reset } = mockInitializeSpaceParent(widgetApi); @@ -87,7 +87,7 @@ describe('getSpaceRoom', () => { ); }); - it('should handle late create event update', async () => { + it.skip('should handle late create event update', async () => { const { createEvent } = mockInitializeSpaceParent(widgetApi); const store = createStore({ widgetApi }); @@ -109,7 +109,7 @@ describe('getSpaceRoom', () => { ); }); - it('should handle late state parent event update', async () => { + it.skip('should handle late state parent event update', async () => { const { parentEvent } = mockInitializeSpaceParent(widgetApi); const store = createStore({ widgetApi }); @@ -131,7 +131,7 @@ describe('getSpaceRoom', () => { ); }); - it('should handle late state child event update', async () => { + it.skip('should handle late state child event update', async () => { const { childEvent } = mockInitializeSpaceParent(widgetApi); const store = createStore({ widgetApi }); @@ -167,7 +167,7 @@ describe('getLobbyRoom', () => { ).resolves.toEqual({ roomId: '!room-id' }); }); - it('should return lobby room from within session room', async () => { + it.skip('should return lobby room from within session room', async () => { mockInitializeSpaceParent(widgetApi, {}); mockInitializeSpaceParent(widgetApi, { room_id: 'lobby-room-id' }); widgetApi.mockSendStateEvent( @@ -189,7 +189,7 @@ describe('getLobbyRoom', () => { ).resolves.toEqual({ roomId: 'lobby-room-id' }); }); - it('should handle missing lobby room if not in a space', async () => { + it.skip('should handle missing lobby room if not in a space', async () => { const store = createStore({ widgetApi }); await expect( @@ -225,7 +225,7 @@ describe('getLobbyRoom', () => { spaceApi.endpoints.getLobbyRoom.select()(store.getState()) ).toMatchObject({ error: { - name: 'LoadFailed', + name: 'NoLobby', message: expect.stringMatching(/could not determine lobby room/i), }, }) @@ -275,7 +275,7 @@ describe('getLobbyRoom', () => { }); }); -describe('getUnassignedRooms', () => { +describe.skip('getUnassignedRooms', () => { it('should return rooms', async () => { // the own room is the lobby mockInitializeSpaceParent(widgetApi); diff --git a/src/store/api/spaceApi.ts b/src/store/api/spaceApi.ts index e76e4fa..4e67d75 100644 --- a/src/store/api/spaceApi.ts +++ b/src/store/api/spaceApi.ts @@ -62,6 +62,15 @@ export const spaceApi = baseApi.injectEndpoints({ queryFn: async (_, { extra }) => { const { widgetApi } = extra as ThunkExtraArgument; + // FOSDEM: use the current room to store everything that was intended for the space room + if (widgetApi.widgetParameters.roomId) { + return { + data: { + spaceId: widgetApi.widgetParameters.roomId, + }, + }; + } + try { // get the canonical space event of the current room const spaceParentEvents = await widgetApi.receiveStateEvents( diff --git a/src/store/api/topicApi.test.ts b/src/store/api/topicApi.test.ts index 90d1f4c..229249b 100644 --- a/src/store/api/topicApi.test.ts +++ b/src/store/api/topicApi.test.ts @@ -90,7 +90,7 @@ describe('getTopic', () => { ); }); - it('should observe topic when space changes', async () => { + it.skip('should observe topic when space changes', async () => { const store = createStore({ widgetApi }); store.dispatch( @@ -173,7 +173,7 @@ describe('getTopics', () => { ); }); - it('should observe space room changes', async () => { + it.skip('should observe space room changes', async () => { const topic1 = widgetApi.mockSendStateEvent( mockTopic({ state_key: '$topic-1' }) ); @@ -248,7 +248,7 @@ describe('createTopic', () => { event: { type: 'net.nordeck.barcamp.topic', state_key: '$topic-1', - room_id: '!space-id', + room_id: '!room-id', content: { title: 'My Topic', description: 'My Description', @@ -265,7 +265,7 @@ describe('createTopic', () => { description: 'My Description', authors: [{ id: '@user-1' }], }, - { stateKey: '$topic-1', roomId: '!space-id' } + { stateKey: '$topic-1', roomId: '!room-id' } ); }); @@ -338,7 +338,7 @@ describe('updateTopic', () => { description: 'Another Description', authors: topic.content.authors, }, - { stateKey: '$topic-1', roomId: '!space-id' } + { stateKey: '$topic-1', roomId: '!room-id' } ); }); diff --git a/src/store/api/usePowerLevels.test.tsx b/src/store/api/usePowerLevels.test.tsx index 3290c6f..0b937d8 100644 --- a/src/store/api/usePowerLevels.test.tsx +++ b/src/store/api/usePowerLevels.test.tsx @@ -64,7 +64,7 @@ describe('usePowerLevels', () => { 'net.nordeck.barcamp.linked_room': 50, }, }, - room_id: '!space-id', + room_id: '!room-id', }) ); diff --git a/src/store/api/useRoomNavigation.test.tsx b/src/store/api/useRoomNavigation.test.tsx index 8c7164a..6739d4a 100644 --- a/src/store/api/useRoomNavigation.test.tsx +++ b/src/store/api/useRoomNavigation.test.tsx @@ -37,7 +37,7 @@ beforeEach(() => { ); }); -describe('navigateToRoom', () => { +describe.skip('navigateToRoom', () => { it('should navigate to the room', async () => { mockInitializeSpaceParent(widgetApi); diff --git a/src/store/api/useSessionTopic.test.tsx b/src/store/api/useSessionTopic.test.tsx index 7991761..b9aef56 100644 --- a/src/store/api/useSessionTopic.test.tsx +++ b/src/store/api/useSessionTopic.test.tsx @@ -69,7 +69,7 @@ describe('useSessionTopic', () => { expect(result.current).toEqual({ session: undefined }); }); - it('should return session in linked room', async () => { + it.skip('should return session in linked room', async () => { widgetApi.mockSendStateEvent( mockSessionGrid({ state_key: 'lobby-room-id', From 5649a62eae0e832a3030a3a5ca63dc1103775a7d Mon Sep 17 00:00:00 2001 From: Dominik Henneke Date: Fri, 20 Jan 2023 13:37:06 +0100 Subject: [PATCH 03/19] Don't create a jitsi widget when initiating a new barcamp room Signed-off-by: Dominik Henneke --- public/locales/de/translation.json | 1 - public/locales/en/translation.json | 1 - src/components/Layout/Layout.test.tsx | 12 ++-------- src/store/api/roomWidgetsApi.test.ts | 32 +++++---------------------- src/store/api/roomWidgetsApi.ts | 25 +++------------------ 5 files changed, 10 insertions(+), 61 deletions(-) diff --git a/public/locales/de/translation.json b/public/locales/de/translation.json index e4449f1..4b445f1 100644 --- a/public/locales/de/translation.json +++ b/public/locales/de/translation.json @@ -157,7 +157,6 @@ }, "widgets": { "jitsi": { - "lobbyConferenceTitle": "Lobby", "title": "Video Konferenz" } } diff --git a/public/locales/en/translation.json b/public/locales/en/translation.json index cbc5db8..63f1402 100644 --- a/public/locales/en/translation.json +++ b/public/locales/en/translation.json @@ -157,7 +157,6 @@ }, "widgets": { "jitsi": { - "lobbyConferenceTitle": "Lobby", "title": "Video Conference" } } diff --git a/src/components/Layout/Layout.test.tsx b/src/components/Layout/Layout.test.tsx index b368aae..70c4573 100644 --- a/src/components/Layout/Layout.test.tsx +++ b/src/components/Layout/Layout.test.tsx @@ -202,7 +202,7 @@ describe('', () => { ); await waitFor(() => { - expect(widgetApi.sendStateEvent).toBeCalledTimes(5); + expect(widgetApi.sendStateEvent).toBeCalledTimes(4); }); expect(widgetApi.sendRoomEvent).toBeCalledTimes(1); @@ -243,18 +243,10 @@ describe('', () => { }, { roomId: '!room-id' } ); - expect(widgetApi.sendStateEvent).toBeCalledWith( - 'im.vector.modular.widgets', - expect.objectContaining({ - type: 'jitsi', - }), - { roomId: '!room-id', stateKey: 'jitsi' } - ); expect(widgetApi.sendStateEvent).toBeCalledWith( 'io.element.widgets.layout', { widgets: { - jitsi: expect.any(Object), 'widget-id': expect.any(Object), }, }, @@ -293,7 +285,7 @@ describe('', () => { expect(confirmModal).not.toBeInTheDocument(); await waitFor(() => { - expect(widgetApi.sendStateEvent).toBeCalledTimes(5); + expect(widgetApi.sendStateEvent).toBeCalledTimes(4); }); }); diff --git a/src/store/api/roomWidgetsApi.test.ts b/src/store/api/roomWidgetsApi.test.ts index 9af9175..1a95edb 100644 --- a/src/store/api/roomWidgetsApi.test.ts +++ b/src/store/api/roomWidgetsApi.test.ts @@ -25,7 +25,7 @@ afterEach(() => widgetApi.stop()); beforeEach(() => (widgetApi = mockWidgetApi())); describe('setupLobbyRoomWidgets', () => { - it('should setup jitsi widget and widget layout', async () => { + it('should setup the widget layout', async () => { const store = createStore({ widgetApi }); await store @@ -36,38 +36,16 @@ describe('setupLobbyRoomWidgets', () => { ) .unwrap(); - expect(widgetApi.sendStateEvent).toBeCalledTimes(2); - expect(widgetApi.sendStateEvent).toBeCalledWith( - 'im.vector.modular.widgets', - { - creatorUserId: '@user-id', - data: { - conferenceId: 'EFZG633NFVUWI', - domain: 'jitsi.riot.im', - roomName: 'Lobby', - }, - id: 'jitsi', - name: 'Video Conference', - type: 'jitsi', - url: 'https://app.element.io/jitsi.html?confId=EFZG633NFVUWI#conferenceId=$conferenceId&domain=$domain&displayName=$matrix_display_name&avatarUrl=$matrix_avatar_url&userId=$matrix_user_id&roomId=$matrix_room_id&roomName=$roomName&theme=$theme', - }, - { roomId: '!room-id', stateKey: 'jitsi' } - ); + expect(widgetApi.sendStateEvent).toBeCalledTimes(1); expect(widgetApi.sendStateEvent).toBeCalledWith( 'io.element.widgets.layout', { widgets: { - jitsi: { - container: 'top', - height: 100, - index: 0, - width: 50, - }, 'widget-id': { container: 'top', height: 100, - index: 1, - width: 50, + index: 0, + width: 100, }, }, }, @@ -75,7 +53,7 @@ describe('setupLobbyRoomWidgets', () => { ); }); - it('should update existing jitsi widget and widget layout', async () => { + it.skip('should update existing widget layout', async () => { widgetApi.mockSendStateEvent({ content: { creatorUserId: '@user-id', diff --git a/src/store/api/roomWidgetsApi.ts b/src/store/api/roomWidgetsApi.ts index c3e1285..f2eab1f 100644 --- a/src/store/api/roomWidgetsApi.ts +++ b/src/store/api/roomWidgetsApi.ts @@ -55,31 +55,12 @@ export const roomWidgetsApi = baseApi.injectEndpoints({ const barcampWidgetId = widgetApi.widgetId; try { - const creatorUserId = widgetApi.widgetParameters.userId ?? ''; - const jitsiWidget = await applyWidget( - widgetApi, - roomId, - createJitsiWidget( - roomId, - creatorUserId, - // We don't have the name of the lobby room, let's just call the - // conference "Lobby" - t('widgets.jitsi.lobbyConferenceTitle', 'Lobby') - ) - ); - const widgetsLayout = await applyWidgetsLayout(widgetApi, roomId, { widgets: { - [jitsiWidget.state_key]: { - container: 'top', - index: 0, - width: 50, - height: 100, - }, [barcampWidgetId]: { container: 'top', - index: 1, - width: 50, + index: 0, + width: 100, height: 100, }, }, @@ -88,7 +69,7 @@ export const roomWidgetsApi = baseApi.injectEndpoints({ return { data: { widgetsLayout, - widgets: [jitsiWidget], + widgets: [], }, }; } catch (e) { From 91f69a8691c2aecef264ec5fe06f3a3eab5e9ce5 Mon Sep 17 00:00:00 2001 From: Dominik Henneke Date: Fri, 20 Jan 2023 13:38:00 +0100 Subject: [PATCH 04/19] Add nordeck branding to the default widget name Signed-off-by: Dominik Henneke --- src/lib/registration.ts | 2 +- src/store/api/roomWidgetsApi.test.ts | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/lib/registration.ts b/src/lib/registration.ts index 41ee337..7c57114 100644 --- a/src/lib/registration.ts +++ b/src/lib/registration.ts @@ -43,7 +43,7 @@ import { } from './events'; export const widgetRegistration = { - name: 'BarCamp', + name: 'BarCamp (developed by Nordeck)', // "clock" suffix to get a custom icon type: 'net.nordeck.barcamp:clock', }; diff --git a/src/store/api/roomWidgetsApi.test.ts b/src/store/api/roomWidgetsApi.test.ts index 1a95edb..2858a50 100644 --- a/src/store/api/roomWidgetsApi.test.ts +++ b/src/store/api/roomWidgetsApi.test.ts @@ -152,7 +152,7 @@ describe('setupSessionRoomWidgets', () => { { creatorUserId: '@user-id', id: 'barcamp', - name: 'BarCamp', + name: 'BarCamp (developed by Nordeck)', type: 'net.nordeck.barcamp:clock', url: 'http://localhost/#/?theme=$org.matrix.msc2873.client_theme&matrix_user_id=$matrix_user_id&matrix_display_name=$matrix_display_name&matrix_avatar_url=$matrix_avatar_url&matrix_room_id=$matrix_room_id&matrix_client_id=$org.matrix.msc2873.client_id&matrix_client_language=$org.matrix.msc2873.client_language', }, @@ -201,7 +201,7 @@ describe('setupSessionRoomWidgets', () => { content: { creatorUserId: '@user-id', id: 'existing-barcamp', - name: 'BarCamp', + name: 'BarCamp (developed by Nordeck)', type: 'net.nordeck.barcamp:clock', url: 'wrong-url', }, @@ -259,7 +259,7 @@ describe('setupSessionRoomWidgets', () => { { creatorUserId: '@user-id', id: 'existing-barcamp', - name: 'BarCamp', + name: 'BarCamp (developed by Nordeck)', type: 'net.nordeck.barcamp:clock', url: 'http://localhost/#/?theme=$org.matrix.msc2873.client_theme&matrix_user_id=$matrix_user_id&matrix_display_name=$matrix_display_name&matrix_avatar_url=$matrix_avatar_url&matrix_room_id=$matrix_room_id&matrix_client_id=$org.matrix.msc2873.client_id&matrix_client_language=$org.matrix.msc2873.client_language', }, From bba34ac7dee1149105ef0145588a6115ad928576 Mon Sep 17 00:00:00 2001 From: Dominik Henneke Date: Fri, 20 Jan 2023 14:06:36 +0100 Subject: [PATCH 05/19] Add the option to add an author to the topic submission This can be useful when a bot creates the topic events for other users. Signed-off-by: Dominik Henneke --- .../SubmittedTopics/SubmittedTopics.test.tsx | 81 +++++++++++++++++++ .../SubmittedTopics/SubmittedTopics.tsx | 12 ++- src/lib/events/topicSubmissionEvent.test.ts | 20 +++++ src/lib/events/topicSubmissionEvent.ts | 3 + src/store/api/sessionGridApi.test.ts | 50 ++++++++++++ src/store/api/sessionGridApi.ts | 8 +- 6 files changed, 170 insertions(+), 4 deletions(-) diff --git a/src/components/SubmittedTopics/SubmittedTopics.test.tsx b/src/components/SubmittedTopics/SubmittedTopics.test.tsx index 4350719..b22cafe 100644 --- a/src/components/SubmittedTopics/SubmittedTopics.test.tsx +++ b/src/components/SubmittedTopics/SubmittedTopics.test.tsx @@ -183,6 +183,87 @@ describe('', () => { ]); }); + it('should render a topic with a custom author', async () => { + widgetApi.clearRoomEvents({ type: 'net.nordeck.barcamp.topic_submission' }); + widgetApi.mockSendRoomEvent( + mockTopicSubmission({ + content: { + title: 'Goals', + description: + 'Discussing goals in a group setting can be fun and useful.', + author: '@walter-trinkenschuh', + }, + origin_server_ts: 0, + event_id: '$event-0', + sender: '@klaus-durchdenwald', + }) + ); + + render( {}} />, { + wrapper, + }); + + await expect( + screen.findByText(/suggestion from @walter-trinkenschuh/i) + ).resolves.toBeInTheDocument(); + expect( + screen.getByRole('button', { name: /select next topic/i }) + ).toBeInTheDocument(); + + await userEvent.hover( + screen.getByText(/suggestion from @walter-trinkenschuh/i) + ); + + await expect( + screen.findByText(/topic suggestions/i) + ).resolves.toBeInTheDocument(); + + expect(screen.getAllByRole('listitem').map((e) => e.textContent)).toEqual([ + '@walter-trinkenschuhGoals', + ]); + }); + + it('should render multiple topics with a custom author and the topic', async () => { + widgetApi.mockSendRoomEvent( + mockTopicSubmission({ + content: { + title: 'Goals', + description: + 'Discussing goals in a group setting can be fun and useful.', + author: '@walter-trinkenschuh', + }, + origin_server_ts: 0, + event_id: '$event-0', + sender: '@klaus-durchdenwald', + }) + ); + + render( {}} />, { wrapper }); + + await expect( + screen.findByText(/suggestions from @walter-trinkenschuh and 4 more/i) + ).resolves.toBeInTheDocument(); + expect( + screen.getByRole('button', { name: /select next topic/i }) + ).toBeInTheDocument(); + + await userEvent.hover( + screen.getByText(/suggestions from @walter-trinkenschuh and 4 more/i) + ); + + await expect( + screen.findByText(/topic suggestions/i) + ).resolves.toBeInTheDocument(); + + expect(screen.getAllByRole('listitem').map((e) => e.textContent)).toEqual([ + '@walter-trinkenschuhGoals', + '@guenter-nachtnebelPredictions', + '@karl-handschuhFails', + '@walter-trinkenschuhHypotheticals', + '@uwe-bierhalsMoonshots', + ]); + }); + it('should select the next topic', async () => { const onSelectNextTopic = jest.fn(); diff --git a/src/components/SubmittedTopics/SubmittedTopics.tsx b/src/components/SubmittedTopics/SubmittedTopics.tsx index 08a29df..3fb18b5 100644 --- a/src/components/SubmittedTopics/SubmittedTopics.tsx +++ b/src/components/SubmittedTopics/SubmittedTopics.tsx @@ -99,7 +99,9 @@ export function SubmittedTopics({ onSelectNextTopic }: SubmittedTopicsProps) { {topics.map((s) => (
  • - {lookupDisplayName(s.sender)} + + {lookupDisplayName(s.content.author ?? s.sender)} + {s.content.title}
  • ))} @@ -115,7 +117,9 @@ export function SubmittedTopics({ onSelectNextTopic }: SubmittedTopicsProps) { 'submittedTopics.summary.multiple', 'Suggestions from {{author}} and {{count}} more…', { - author: lookupDisplayName(firstTopic.sender), + author: lookupDisplayName( + firstTopic.content.author ?? firstTopic.sender + ), count: topics.length - 1, } ) @@ -123,7 +127,9 @@ export function SubmittedTopics({ onSelectNextTopic }: SubmittedTopicsProps) { 'submittedTopics.summary.single', 'Suggestion from {{author}}', { - author: lookupDisplayName(firstTopic.sender), + author: lookupDisplayName( + firstTopic.content.author ?? firstTopic.sender + ), } )} diff --git a/src/lib/events/topicSubmissionEvent.test.ts b/src/lib/events/topicSubmissionEvent.test.ts index 9b67bdd..fdac107 100644 --- a/src/lib/events/topicSubmissionEvent.test.ts +++ b/src/lib/events/topicSubmissionEvent.test.ts @@ -53,6 +53,23 @@ describe('isValidTopicSubmissionEvent', () => { ).toBe(true); }); + it('should accept event with a different author', () => { + expect( + isValidTopicSubmissionEvent({ + content: { + title: 'My Topic', + description: 'I want to talk about…', + author: '@another-user-id', + }, + event_id: '$event-id', + origin_server_ts: 0, + room_id: '!room-id', + sender: '@user-id', + type: 'net.nordeck.barcamp.topic_submission', + }) + ).toBe(true); + }); + it('should accept additional properties', () => { expect( isValidTopicSubmissionEvent({ @@ -79,6 +96,9 @@ describe('isValidTopicSubmissionEvent', () => { { description: null }, { description: 111 }, { description: '' }, + { author: null }, + { author: 111 }, + { author: '' }, { 'm.relates_to': { rel_type: undefined, event_id: '$event-id' } }, { 'm.relates_to': { rel_type: null, event_id: '$event-id' } }, { 'm.relates_to': { rel_type: '', event_id: '$event-id' } }, diff --git a/src/lib/events/topicSubmissionEvent.ts b/src/lib/events/topicSubmissionEvent.ts index e9960c3..31aa547 100644 --- a/src/lib/events/topicSubmissionEvent.ts +++ b/src/lib/events/topicSubmissionEvent.ts @@ -29,6 +29,8 @@ export type TopicSubmissionEvent = { title: string; /** The description of the submission */ description: string; + /** The author of this submission that should be used instead of the event sender */ + author?: string; /** The relation to the start event of the session grid */ 'm.relates_to'?: RelatesTo<'m.reference'>; }; @@ -36,6 +38,7 @@ export type TopicSubmissionEvent = { const topicSubmissionEventSchema = Joi.object({ title: Joi.string().min(1).required(), description: Joi.string().min(1).required(), + author: Joi.string().min(1), 'm.relates_to': Joi.object({ rel_type: Joi.string().valid('m.reference').required(), event_id: Joi.string().required(), diff --git a/src/store/api/sessionGridApi.test.ts b/src/store/api/sessionGridApi.test.ts index 88c3bea..b4caae2 100644 --- a/src/store/api/sessionGridApi.test.ts +++ b/src/store/api/sessionGridApi.test.ts @@ -265,6 +265,56 @@ describe('selectNextTopic', () => { ); }); + it('should consider the custom author of the next topic and add it to the parking lot', async () => { + mockInitializeSpaceParent(widgetApi); + + widgetApi.mockSendRoomEvent(mockSessionGridStart()); + + widgetApi.mockSendRoomEvent( + mockTopicSubmission({ + content: { + title: 'Title 1', + description: 'Description 1', + author: '@author-2', + }, + origin_server_ts: 1, + event_id: '$event-1', + sender: '@author-1', + }) + ); + + widgetApi.mockSendStateEvent(mockSessionGrid()); + + const store = createStore({ widgetApi }); + + await expect( + store + .dispatch(sessionGridApi.endpoints.selectNextTopic.initiate()) + .unwrap() + ).resolves.toMatchObject({ event: expect.any(Object) }); + + expect(widgetApi.sendStateEvent).toBeCalledTimes(2); + expect(widgetApi.sendStateEvent).toBeCalledWith( + 'net.nordeck.barcamp.topic', + { + description: 'Description 1', + title: 'Title 1', + authors: [{ id: '@author-2' }], + }, + { roomId: '!room-id', stateKey: '$event-1' } + ); + expect(widgetApi.sendStateEvent).toBeCalledWith( + 'net.nordeck.barcamp.session_grid', + mockSessionGrid({ + content: { + consumedTopicSubmissions: ['$event-1'], + parkingLot: [{ topicId: '$event-1' }], + }, + }).content, + { roomId: '!room-id', stateKey: '!room-id' } + ); + }); + it('should handle empty submission queue', async () => { mockInitializeSpaceParent(widgetApi); diff --git a/src/store/api/sessionGridApi.ts b/src/store/api/sessionGridApi.ts index f6b61ac..ce7e9ff 100644 --- a/src/store/api/sessionGridApi.ts +++ b/src/store/api/sessionGridApi.ts @@ -481,7 +481,13 @@ export const sessionGridApi = baseApi.injectEndpoints({ content: { title: nextTopicSubmission.content.title, description: nextTopicSubmission.content.description, - authors: [{ id: nextTopicSubmission.sender }], + authors: [ + { + id: + nextTopicSubmission.content.author ?? + nextTopicSubmission.sender, + }, + ], }, }) ); From 84594a15754424e2b5344a5f0609ed42d3658347 Mon Sep 17 00:00:00 2001 From: Dominik Henneke Date: Fri, 20 Jan 2023 15:26:39 +0100 Subject: [PATCH 06/19] Remove unused capabilities Signed-off-by: Dominik Henneke --- src/components/Layout/Layout.test.tsx | 25 +++------ .../PersonalSpace/PersonalSpace.test.tsx | 6 +- src/lib/registration.ts | 56 +------------------ src/store/api/linkedRoomApi.ts | 5 ++ src/store/api/powerLevelsApi.test.ts | 34 +++++------ src/store/api/powerLevelsApi.ts | 13 +++-- src/store/api/roomEncryptionApi.test.ts | 2 +- src/store/api/roomEncryptionApi.ts | 4 +- .../api/roomHistoryVisibilityApi.test.ts | 6 +- src/store/api/roomHistoryVisibilityApi.ts | 9 ++- src/store/api/roomMemberApi.ts | 7 +-- src/store/api/roomWidgetsApi.test.ts | 22 +++----- src/store/api/roomWidgetsApi.ts | 14 ++--- src/store/api/sessionGridApi.test.ts | 52 ++++++++--------- src/store/api/sessionGridApi.ts | 18 ++++-- src/store/api/spaceApi.test.ts | 2 +- src/store/api/spaceApi.ts | 28 +++++----- src/store/api/topicApi.test.ts | 4 +- src/store/api/topicApi.ts | 25 ++++----- src/store/api/useRoomNavigation.ts | 2 +- 20 files changed, 135 insertions(+), 199 deletions(-) diff --git a/src/components/Layout/Layout.test.tsx b/src/components/Layout/Layout.test.tsx index 70c4573..49eb3f0 100644 --- a/src/components/Layout/Layout.test.tsx +++ b/src/components/Layout/Layout.test.tsx @@ -222,35 +222,28 @@ describe('', () => { topicStartEventId: expect.any(String), }, { - roomId: '!room-id', stateKey: '!room-id', } ); expect(widgetApi.sendStateEvent).toBeCalledWith( 'm.room.history_visibility', - { history_visibility: 'shared' }, - { roomId: '!room-id' } + { history_visibility: 'shared' } ); - expect(widgetApi.sendStateEvent).toBeCalledWith( - 'm.room.power_levels', - { - users: { - '@user-id': 100, - }, - events: { - 'net.nordeck.barcamp.topic_submission': 50, - }, + expect(widgetApi.sendStateEvent).toBeCalledWith('m.room.power_levels', { + users: { + '@user-id': 100, }, - { roomId: '!room-id' } - ); + events: { + 'net.nordeck.barcamp.topic_submission': 50, + }, + }); expect(widgetApi.sendStateEvent).toBeCalledWith( 'io.element.widgets.layout', { widgets: { 'widget-id': expect.any(Object), }, - }, - { roomId: '!room-id' } + } ); }); diff --git a/src/components/PersonalSpace/PersonalSpace.test.tsx b/src/components/PersonalSpace/PersonalSpace.test.tsx index fff9d65..8556bba 100644 --- a/src/components/PersonalSpace/PersonalSpace.test.tsx +++ b/src/components/PersonalSpace/PersonalSpace.test.tsx @@ -123,8 +123,7 @@ describe('', () => { events: { 'net.nordeck.barcamp.topic_submission': 0, }, - }), - { room_id: undefined } + }) ); }); }); @@ -153,8 +152,7 @@ describe('', () => { events: { 'net.nordeck.barcamp.topic_submission': 50, }, - }), - { room_id: undefined } + }) ); }); }); diff --git a/src/lib/registration.ts b/src/lib/registration.ts index 7c57114..2285219 100644 --- a/src/lib/registration.ts +++ b/src/lib/registration.ts @@ -15,29 +15,17 @@ */ import { - generateRoomTimelineCapabilities, STATE_EVENT_POWER_LEVELS, STATE_EVENT_ROOM_MEMBER, - WIDGET_CAPABILITY_NAVIGATE, } from '@matrix-widget-toolkit/api'; -import { - EventDirection, - Symbols, - WidgetEventCapability, -} from 'matrix-widget-api'; +import { EventDirection, WidgetEventCapability } from 'matrix-widget-api'; import { ROOM_EVENT_BARCAMP_SESSION_GRID_START, ROOM_EVENT_BARCAMP_TOPIC_SUBMISSION, - STATE_EVENT_BARCAMP_LINKED_ROOM, STATE_EVENT_BARCAMP_SESSION_GRID, STATE_EVENT_BARCAMP_TOPIC, - STATE_EVENT_ROOM_CREATE, STATE_EVENT_ROOM_ENCRYPTION, STATE_EVENT_ROOM_HISTORY_VISIBILITY, - STATE_EVENT_ROOM_NAME, - STATE_EVENT_ROOM_TOPIC, - STATE_EVENT_SPACE_CHILD, - STATE_EVENT_SPACE_PARENT, STATE_EVENT_WIDGETS, STATE_EVENT_WIDGETS_LAYOUT, } from './events'; @@ -81,22 +69,6 @@ export const capabilities = [ EventDirection.Send, ROOM_EVENT_BARCAMP_TOPIC_SUBMISSION ), - WidgetEventCapability.forStateEvent( - EventDirection.Receive, - STATE_EVENT_ROOM_CREATE - ), - WidgetEventCapability.forStateEvent( - EventDirection.Receive, - STATE_EVENT_SPACE_PARENT - ), - WidgetEventCapability.forStateEvent( - EventDirection.Receive, - STATE_EVENT_SPACE_CHILD - ), - WidgetEventCapability.forStateEvent( - EventDirection.Send, - STATE_EVENT_SPACE_CHILD - ), WidgetEventCapability.forStateEvent( EventDirection.Receive, STATE_EVENT_ROOM_MEMBER @@ -109,30 +81,6 @@ export const capabilities = [ EventDirection.Send, STATE_EVENT_POWER_LEVELS ), - WidgetEventCapability.forStateEvent( - EventDirection.Receive, - STATE_EVENT_BARCAMP_LINKED_ROOM - ), - WidgetEventCapability.forStateEvent( - EventDirection.Send, - STATE_EVENT_BARCAMP_LINKED_ROOM - ), - WidgetEventCapability.forStateEvent( - EventDirection.Receive, - STATE_EVENT_ROOM_NAME - ), - WidgetEventCapability.forStateEvent( - EventDirection.Send, - STATE_EVENT_ROOM_NAME - ), - WidgetEventCapability.forStateEvent( - EventDirection.Receive, - STATE_EVENT_ROOM_TOPIC - ), - WidgetEventCapability.forStateEvent( - EventDirection.Send, - STATE_EVENT_ROOM_TOPIC - ), WidgetEventCapability.forStateEvent( EventDirection.Receive, STATE_EVENT_WIDGETS @@ -158,6 +106,4 @@ export const capabilities = [ EventDirection.Receive, STATE_EVENT_ROOM_ENCRYPTION ), - ...generateRoomTimelineCapabilities(Symbols.AnyRoom), - WIDGET_CAPABILITY_NAVIGATE, ]; diff --git a/src/store/api/linkedRoomApi.ts b/src/store/api/linkedRoomApi.ts index cef667b..0aa3ec1 100644 --- a/src/store/api/linkedRoomApi.ts +++ b/src/store/api/linkedRoomApi.ts @@ -14,6 +14,8 @@ * limitations under the License. */ +/* eslint-disable no-unreachable */ + import { compareOriginServerTS, StateEvent } from '@matrix-widget-toolkit/api'; import { createEntityAdapter, EntityState } from '@reduxjs/toolkit'; import { isError } from 'lodash'; @@ -57,6 +59,7 @@ export const linkedRoomApi = baseApi.injectEndpoints({ const { widgetApi } = extra as ThunkExtraArgument; try { + return { data: linkedRoomsEntityAdapter.getInitialState() }; const { spaceId } = await dispatch( spaceApi.endpoints.getSpaceRoom.initiate() ).unwrap(); @@ -94,6 +97,8 @@ export const linkedRoomApi = baseApi.injectEndpoints({ dispatch, } ) { + return; + const { widgetApi } = extra as ThunkExtraArgument; // wait until first data is cached diff --git a/src/store/api/powerLevelsApi.test.ts b/src/store/api/powerLevelsApi.test.ts index 36521c8..24372f9 100644 --- a/src/store/api/powerLevelsApi.test.ts +++ b/src/store/api/powerLevelsApi.test.ts @@ -57,7 +57,7 @@ describe('getPowerLevels', () => { }); }); - it('should return power levels for specific room', async () => { + it.skip('should return power levels for specific room', async () => { widgetApi.mockSendStateEvent( mockPowerLevelsEvent({ room_id: '!space-id' }) ); @@ -120,7 +120,7 @@ describe('getPowerLevels', () => { ); }); - it('should observe power levels for specific room', async () => { + it.skip('should observe power levels for specific room', async () => { widgetApi.mockSendStateEvent( mockPowerLevelsEvent({ room_id: '!space-id' }) ); @@ -196,18 +196,14 @@ describe('patchPowerLevels', () => { }), }); - expect(widgetApi.sendStateEvent).toBeCalledWith( - 'm.room.power_levels', - { - users: { '@user-id': 100 }, - users_default: 0, - events_default: 0, - }, - { roomId: undefined } - ); + expect(widgetApi.sendStateEvent).toBeCalledWith('m.room.power_levels', { + users: { '@user-id': 100 }, + users_default: 0, + events_default: 0, + }); }); - it('should patch power levels event in specific room', async () => { + it.skip('should patch power levels event in specific room', async () => { widgetApi.mockSendStateEvent( mockPowerLevelsEvent({ content: { users_default: 100 }, @@ -235,15 +231,11 @@ describe('patchPowerLevels', () => { }), }); - expect(widgetApi.sendStateEvent).toBeCalledWith( - 'm.room.power_levels', - { - users: { '@user-id': 100 }, - users_default: 0, - events_default: 0, - }, - { roomId: '!space-id' } - ); + expect(widgetApi.sendStateEvent).toBeCalledWith('m.room.power_levels', { + users: { '@user-id': 100 }, + users_default: 0, + events_default: 0, + }); }); it('should perform optimistic update of power levels', async () => { diff --git a/src/store/api/powerLevelsApi.ts b/src/store/api/powerLevelsApi.ts index 9773e61..a7ad501 100644 --- a/src/store/api/powerLevelsApi.ts +++ b/src/store/api/powerLevelsApi.ts @@ -47,7 +47,7 @@ export const powerLevelsApi = baseApi.injectEndpoints({ const events = await widgetApi.receiveStateEvents( STATE_EVENT_POWER_LEVELS, { - roomIds: roomId ? [roomId] : undefined, + //roomIds: roomId ? [roomId] : undefined, stateKey: '', } ); @@ -78,7 +78,7 @@ export const powerLevelsApi = baseApi.injectEndpoints({ const subscription = widgetApi .observeStateEvents(STATE_EVENT_POWER_LEVELS, { - roomIds: roomId ? [roomId] : undefined, + //roomIds: roomId ? [roomId] : undefined, stateKey: '', }) .pipe( @@ -109,7 +109,10 @@ export const powerLevelsApi = baseApi.injectEndpoints({ try { const powerLevelsEvents = await widgetApi.receiveStateEvents( STATE_EVENT_POWER_LEVELS, - { stateKey: '', roomIds: roomId ? [roomId] : undefined } + { + stateKey: '', + //roomIds: roomId ? [roomId] : undefined, + } ); const powerLevelEvent = last( powerLevelsEvents.filter(isValidPowerLevelStateEvent) @@ -129,8 +132,8 @@ export const powerLevelsApi = baseApi.injectEndpoints({ const data = await widgetApi.sendStateEvent( STATE_EVENT_POWER_LEVELS, - powerLevels, - { roomId } + powerLevels + //{ roomId } ); return { data }; diff --git a/src/store/api/roomEncryptionApi.test.ts b/src/store/api/roomEncryptionApi.test.ts index a05d3eb..908e169 100644 --- a/src/store/api/roomEncryptionApi.test.ts +++ b/src/store/api/roomEncryptionApi.test.ts @@ -38,7 +38,7 @@ describe('hasRoomEncryption', () => { ).resolves.toEqual(true); }); - it('should return room encryption state', async () => { + it.skip('should return room encryption state', async () => { widgetApi.mockSendStateEvent( mockRoomEncryption({ room_id: '!linked-room-id' }) ); diff --git a/src/store/api/roomEncryptionApi.ts b/src/store/api/roomEncryptionApi.ts index ac5fb37..a44a85c 100644 --- a/src/store/api/roomEncryptionApi.ts +++ b/src/store/api/roomEncryptionApi.ts @@ -39,8 +39,8 @@ export const roomEncryptionApi = baseApi.injectEndpoints({ try { const events = await widgetApi.receiveStateEvents( - STATE_EVENT_ROOM_ENCRYPTION, - { roomIds: roomId ? [roomId] : undefined } + STATE_EVENT_ROOM_ENCRYPTION + //{ roomIds: roomId ? [roomId] : undefined } ); const event = first(events.filter(isValidRoomEncryptionEvent)); const isEncrypted = event !== undefined; diff --git a/src/store/api/roomHistoryVisibilityApi.test.ts b/src/store/api/roomHistoryVisibilityApi.test.ts index a85948a..5e96ec4 100644 --- a/src/store/api/roomHistoryVisibilityApi.test.ts +++ b/src/store/api/roomHistoryVisibilityApi.test.ts @@ -52,8 +52,7 @@ describe('patchRoomHistoryVisibility', () => { expect(widgetApi.sendStateEvent).toBeCalledWith( 'm.room.history_visibility', - { history_visibility: 'shared' }, - { roomId: '!room-id' } + { history_visibility: 'shared' } ); }); @@ -81,8 +80,7 @@ describe('patchRoomHistoryVisibility', () => { expect(widgetApi.sendStateEvent).toBeCalledWith( 'm.room.history_visibility', - { history_visibility: 'shared' }, - { roomId: '!room-id' } + { history_visibility: 'shared' } ); }); diff --git a/src/store/api/roomHistoryVisibilityApi.ts b/src/store/api/roomHistoryVisibilityApi.ts index 259d17f..4b6eeb8 100644 --- a/src/store/api/roomHistoryVisibilityApi.ts +++ b/src/store/api/roomHistoryVisibilityApi.ts @@ -43,7 +43,10 @@ export const roomHistoryVisibilityApi = baseApi.injectEndpoints({ const roomHistoryVisibilityEvents = await widgetApi.receiveStateEvents( STATE_EVENT_ROOM_HISTORY_VISIBILITY, - { roomIds: roomId ? [roomId] : undefined, stateKey: '' } + { + //roomIds: roomId ? [roomId] : undefined, + stateKey: '', + } ); const roomHistoryVisibilityEvent = last( @@ -69,8 +72,8 @@ export const roomHistoryVisibilityApi = baseApi.injectEndpoints({ const event = await widgetApi.sendStateEvent( STATE_EVENT_ROOM_HISTORY_VISIBILITY, - roomHistoryVisibility, - { roomId } + roomHistoryVisibility + //{ roomId } ); return { data: { event } }; diff --git a/src/store/api/roomMemberApi.ts b/src/store/api/roomMemberApi.ts index ed1966f..12fe5a2 100644 --- a/src/store/api/roomMemberApi.ts +++ b/src/store/api/roomMemberApi.ts @@ -22,7 +22,6 @@ import { } from '@matrix-widget-toolkit/api'; import { createEntityAdapter, EntityState } from '@reduxjs/toolkit'; import { isError } from 'lodash'; -import { Symbols } from 'matrix-widget-api'; import { bufferTime, filter } from 'rxjs'; import { ThunkExtraArgument } from '../store'; import { baseApi } from './baseApi'; @@ -67,8 +66,8 @@ export const roomMemberApi = baseApi.injectEndpoints({ } const events = await widgetApi.receiveStateEvents( - STATE_EVENT_ROOM_MEMBER, - { roomIds: [spaceId] } + STATE_EVENT_ROOM_MEMBER + //{ roomIds: [spaceId] } ); return { @@ -110,7 +109,7 @@ export const roomMemberApi = baseApi.injectEndpoints({ const subscription = widgetApi .observeStateEvents(STATE_EVENT_ROOM_MEMBER, { - roomIds: Symbols.AnyRoom, + //roomIds: Symbols.AnyRoom, }) .pipe( filter(isValidRoomMemberStateEvent), diff --git a/src/store/api/roomWidgetsApi.test.ts b/src/store/api/roomWidgetsApi.test.ts index 2858a50..a1c6e6b 100644 --- a/src/store/api/roomWidgetsApi.test.ts +++ b/src/store/api/roomWidgetsApi.test.ts @@ -48,8 +48,7 @@ describe('setupLobbyRoomWidgets', () => { width: 100, }, }, - }, - { roomId: '!room-id' } + } ); }); @@ -108,7 +107,7 @@ describe('setupLobbyRoomWidgets', () => { type: 'jitsi', url: 'https://app.element.io/jitsi.html?confId=EFZG633NFVUWI#conferenceId=$conferenceId&domain=$domain&displayName=$matrix_display_name&avatarUrl=$matrix_avatar_url&userId=$matrix_user_id&roomId=$matrix_room_id&roomName=$roomName&theme=$theme', }, - { roomId: '!room-id', stateKey: 'existing-jitsi' } + { stateKey: 'existing-jitsi' } ); expect(widgetApi.sendStateEvent).toBeCalledWith( 'io.element.widgets.layout', @@ -127,8 +126,7 @@ describe('setupLobbyRoomWidgets', () => { width: 50, }, }, - }, - { roomId: '!room-id' } + } ); }); }); @@ -156,7 +154,7 @@ describe('setupSessionRoomWidgets', () => { type: 'net.nordeck.barcamp:clock', url: 'http://localhost/#/?theme=$org.matrix.msc2873.client_theme&matrix_user_id=$matrix_user_id&matrix_display_name=$matrix_display_name&matrix_avatar_url=$matrix_avatar_url&matrix_room_id=$matrix_room_id&matrix_client_id=$org.matrix.msc2873.client_id&matrix_client_language=$org.matrix.msc2873.client_language', }, - { roomId: '!room-id', stateKey: 'barcamp' } + { stateKey: 'barcamp' } ); expect(widgetApi.sendStateEvent).toBeCalledWith( 'im.vector.modular.widgets', @@ -172,7 +170,7 @@ describe('setupSessionRoomWidgets', () => { type: 'jitsi', url: 'https://app.element.io/jitsi.html?confId=EFZG633NFVUWI#conferenceId=$conferenceId&domain=$domain&displayName=$matrix_display_name&avatarUrl=$matrix_avatar_url&userId=$matrix_user_id&roomId=$matrix_room_id&roomName=$roomName&theme=$theme', }, - { roomId: '!room-id', stateKey: 'jitsi' } + { stateKey: 'jitsi' } ); expect(widgetApi.sendStateEvent).toBeCalledWith( 'io.element.widgets.layout', @@ -191,8 +189,7 @@ describe('setupSessionRoomWidgets', () => { width: 20, }, }, - }, - { roomId: '!room-id' } + } ); }); @@ -263,7 +260,7 @@ describe('setupSessionRoomWidgets', () => { type: 'net.nordeck.barcamp:clock', url: 'http://localhost/#/?theme=$org.matrix.msc2873.client_theme&matrix_user_id=$matrix_user_id&matrix_display_name=$matrix_display_name&matrix_avatar_url=$matrix_avatar_url&matrix_room_id=$matrix_room_id&matrix_client_id=$org.matrix.msc2873.client_id&matrix_client_language=$org.matrix.msc2873.client_language', }, - { roomId: '!room-id', stateKey: 'existing-barcamp' } + { stateKey: 'existing-barcamp' } ); expect(widgetApi.sendStateEvent).toBeCalledWith( 'im.vector.modular.widgets', @@ -279,7 +276,7 @@ describe('setupSessionRoomWidgets', () => { type: 'jitsi', url: 'https://app.element.io/jitsi.html?confId=EFZG633NFVUWI#conferenceId=$conferenceId&domain=$domain&displayName=$matrix_display_name&avatarUrl=$matrix_avatar_url&userId=$matrix_user_id&roomId=$matrix_room_id&roomName=$roomName&theme=$theme', }, - { roomId: '!room-id', stateKey: 'existing-jitsi' } + { stateKey: 'existing-jitsi' } ); expect(widgetApi.sendStateEvent).toBeCalledWith( 'io.element.widgets.layout', @@ -298,8 +295,7 @@ describe('setupSessionRoomWidgets', () => { width: 20, }, }, - }, - { roomId: '!room-id' } + } ); }); }); diff --git a/src/store/api/roomWidgetsApi.ts b/src/store/api/roomWidgetsApi.ts index f2eab1f..653acf8 100644 --- a/src/store/api/roomWidgetsApi.ts +++ b/src/store/api/roomWidgetsApi.ts @@ -211,8 +211,8 @@ async function applyWidget( widget: WidgetsEvent ): Promise> { const widgetsEvents = await widgetApi.receiveStateEvents( - STATE_EVENT_WIDGETS, - { roomIds: [roomId] } + STATE_EVENT_WIDGETS + //{ roomIds: [roomId] } ); const widgetsEvent = last( widgetsEvents @@ -232,7 +232,7 @@ async function applyWidget( } return await widgetApi.sendStateEvent(STATE_EVENT_WIDGETS, content, { - roomId, + //roomId, stateKey: id, }); } @@ -243,8 +243,8 @@ async function applyWidgetsLayout( widgetsLayout: WidgetsLayoutEvent ): Promise> { const widgetsLayoutEvents = await widgetApi.receiveStateEvents( - STATE_EVENT_WIDGETS_LAYOUT, - { roomIds: [roomId] } + STATE_EVENT_WIDGETS_LAYOUT + //{ roomIds: [roomId] } ); const widgetsLayoutEvent = last( widgetsLayoutEvents.filter(isValidWidgetsLayoutEvent) @@ -260,8 +260,8 @@ async function applyWidgetsLayout( return await widgetApi.sendStateEvent( STATE_EVENT_WIDGETS_LAYOUT, - widgetsLayout, - { roomId } + widgetsLayout + //{ roomId } ); } diff --git a/src/store/api/sessionGridApi.test.ts b/src/store/api/sessionGridApi.test.ts index b4caae2..5c58001 100644 --- a/src/store/api/sessionGridApi.test.ts +++ b/src/store/api/sessionGridApi.test.ts @@ -183,7 +183,7 @@ describe('setupSessionGrid', () => { ], topicStartEventId: expect.any(String), }, - { roomId: '!room-id', stateKey: '!room-id' } + { stateKey: '!room-id' } ); }); }); @@ -251,7 +251,7 @@ describe('selectNextTopic', () => { title: 'Title 1', authors: [{ id: '@author-1' }], }, - { roomId: '!room-id', stateKey: '$event-1' } + { stateKey: '$event-1' } ); expect(widgetApi.sendStateEvent).toBeCalledWith( 'net.nordeck.barcamp.session_grid', @@ -261,7 +261,7 @@ describe('selectNextTopic', () => { parkingLot: [{ topicId: '$event-1' }, { topicId: '$event-0' }], }, }).content, - { roomId: '!room-id', stateKey: '!room-id' } + { stateKey: '!room-id' } ); }); @@ -301,7 +301,7 @@ describe('selectNextTopic', () => { title: 'Title 1', authors: [{ id: '@author-2' }], }, - { roomId: '!room-id', stateKey: '$event-1' } + { stateKey: '$event-1' } ); expect(widgetApi.sendStateEvent).toBeCalledWith( 'net.nordeck.barcamp.session_grid', @@ -311,7 +311,7 @@ describe('selectNextTopic', () => { parkingLot: [{ topicId: '$event-1' }], }, }).content, - { roomId: '!room-id', stateKey: '!room-id' } + { stateKey: '!room-id' } ); }); @@ -420,7 +420,7 @@ describe('moveTopicToParkingArea', () => { ], }, }).content, - { roomId: '!room-id', stateKey: '!room-id' } + { stateKey: '!room-id' } ); }); @@ -484,7 +484,7 @@ describe('moveTopicToParkingArea', () => { sessions: [], }, }).content, - { roomId: '!room-id', stateKey: '!room-id' } + { stateKey: '!room-id' } ); }); }); @@ -589,7 +589,7 @@ describe('moveTopicToSession', () => { }, ], }), - { roomId: '!room-id', stateKey: '!room-id' } + { stateKey: '!room-id' } ); }); @@ -668,7 +668,7 @@ describe('moveTopicToSession', () => { ], parkingLot: [], }), - { roomId: '!room-id', stateKey: '!room-id' } + { stateKey: '!room-id' } ); }); @@ -796,7 +796,7 @@ describe('updateTimeSlot', () => { }, ], }), - { roomId: '!room-id', stateKey: '!room-id' } + { stateKey: '!room-id' } ); }); @@ -869,7 +869,7 @@ describe('updateTimeSlot', () => { }, ], }), - { roomId: '!room-id', stateKey: '!room-id' } + { stateKey: '!room-id' } ); }); @@ -1089,7 +1089,7 @@ describe('updateCommonEvent', () => { }, ], }), - { roomId: '!room-id', stateKey: '!room-id' } + { stateKey: '!room-id' } ); }); }); @@ -1138,7 +1138,7 @@ describe('updateTrack', () => { }, ], }), - { roomId: '!room-id', stateKey: '!room-id' } + { stateKey: '!room-id' } ); }); }); @@ -1183,7 +1183,7 @@ describe('addTrack', () => { }, ], }), - { roomId: '!room-id', stateKey: '!room-id' } + { stateKey: '!room-id' } ); }); }); @@ -1258,7 +1258,7 @@ describe('deleteTrack', () => { sessions: [], parkingLot: [{ topicId: 'id-1' }, { topicId: 'id-0' }], }), - { roomId: '!room-id', stateKey: '!room-id' } + { stateKey: '!room-id' } ); }); @@ -1329,7 +1329,7 @@ describe('deleteTopic', () => { expect.objectContaining({ sessions: [], }), - { roomId: '!room-id', stateKey: '!room-id' } + { stateKey: '!room-id' } ); }); @@ -1359,7 +1359,7 @@ describe('deleteTopic', () => { expect.objectContaining({ parkingLot: [], }), - { roomId: '!room-id', stateKey: '!room-id' } + { stateKey: '!room-id' } ); }); }); @@ -1452,7 +1452,7 @@ describe('deleteTimeSlot', () => { { topicId: 'parking-lot-topic-0' }, ], }), - { roomId: '!room-id', stateKey: '!room-id' } + { stateKey: '!room-id' } ); }); @@ -1530,7 +1530,7 @@ describe('deleteTimeSlot', () => { }, ], }), - { roomId: '!room-id', stateKey: '!room-id' } + { stateKey: '!room-id' } ); }); @@ -1620,7 +1620,7 @@ describe('addTimeSlot', () => { }, ], }), - { roomId: '!room-id', stateKey: '!room-id' } + { stateKey: '!room-id' } ); }); @@ -1663,7 +1663,7 @@ describe('addTimeSlot', () => { }, ], }), - { roomId: '!room-id', stateKey: '!room-id' } + { stateKey: '!room-id' } ); }); @@ -1717,7 +1717,7 @@ describe('addTimeSlot', () => { }, ], }), - { roomId: '!room-id', stateKey: '!room-id' } + { stateKey: '!room-id' } ); }); }); @@ -1790,7 +1790,7 @@ describe('moveTimeSlot', () => { }, ], }), - { roomId: '!room-id', stateKey: '!room-id' } + { stateKey: '!room-id' } ); }); @@ -1861,7 +1861,7 @@ describe('moveTimeSlot', () => { }, ], }), - { roomId: '!room-id', stateKey: '!room-id' } + { stateKey: '!room-id' } ); }); @@ -1932,7 +1932,7 @@ describe('moveTimeSlot', () => { }, ], }), - { roomId: '!room-id', stateKey: '!room-id' } + { stateKey: '!room-id' } ); }); }); @@ -1998,7 +1998,7 @@ describe('updateSessionGrid', () => { tracks: [], topicStartEventId: '$start-event-id', }, - { roomId: '!room-id', stateKey: '!room-id' } + { stateKey: '!room-id' } ); }); diff --git a/src/store/api/sessionGridApi.ts b/src/store/api/sessionGridApi.ts index ce7e9ff..883865f 100644 --- a/src/store/api/sessionGridApi.ts +++ b/src/store/api/sessionGridApi.ts @@ -25,7 +25,6 @@ import { t } from 'i18next'; import produce from 'immer'; import { first, isError, last } from 'lodash'; import { DateTime, Duration } from 'luxon'; -import { Symbols } from 'matrix-widget-api'; import { nanoid } from 'nanoid'; import { bufferTime, filter } from 'rxjs'; import { randomIcon } from '../../components/IconPicker'; @@ -93,7 +92,10 @@ export const sessionGridApi = baseApi.injectEndpoints({ try { const events = await widgetApi.receiveStateEvents( STATE_EVENT_BARCAMP_SESSION_GRID, - { roomIds: [roomId], stateKey } + { + //roomIds: [roomId], + stateKey, + } ); const gridEvent = last(events.filter(isValidSessionGridEvent)); @@ -132,7 +134,7 @@ export const sessionGridApi = baseApi.injectEndpoints({ const subscription = widgetApi .observeStateEvents(STATE_EVENT_BARCAMP_SESSION_GRID, { - roomIds: Symbols.AnyRoom, + //roomIds: Symbols.AnyRoom, }) .pipe( filter(isValidSessionGridEvent), @@ -213,7 +215,10 @@ export const sessionGridApi = baseApi.injectEndpoints({ const event = await widgetApi.sendStateEvent( STATE_EVENT_BARCAMP_SESSION_GRID, content, - { roomId, stateKey } + { + //roomId, + stateKey, + } ); return { data: { event } }; @@ -677,7 +682,10 @@ export async function updateSessionGrid( const newEvent = await widgetApi.sendStateEvent( STATE_EVENT_BARCAMP_SESSION_GRID, nextContent, - { roomId: event.room_id, stateKey: event.state_key } + { + //roomId: event.room_id, + stateKey: event.state_key, + } ); return { data: { event: newEvent } }; diff --git a/src/store/api/spaceApi.test.ts b/src/store/api/spaceApi.test.ts index a1292f8..b21f2e9 100644 --- a/src/store/api/spaceApi.test.ts +++ b/src/store/api/spaceApi.test.ts @@ -500,7 +500,7 @@ describe.skip('getUnassignedRooms', () => { }); }); -describe('markRoomAsSuggested', () => { +describe.skip('markRoomAsSuggested', () => { it('should mark room as suggested', async () => { mockInitializeSpaceParent(widgetApi); diff --git a/src/store/api/spaceApi.ts b/src/store/api/spaceApi.ts index 4e67d75..2cbccbb 100644 --- a/src/store/api/spaceApi.ts +++ b/src/store/api/spaceApi.ts @@ -14,6 +14,8 @@ * limitations under the License. */ +/* eslint-disable no-unreachable */ + import { hasStateEventPower, isValidPowerLevelStateEvent, @@ -149,7 +151,7 @@ export const spaceApi = baseApi.injectEndpoints({ { cacheEntryRemoved, extra, getCacheEntry, dispatch } ) { const { widgetApi } = extra as ThunkExtraArgument; - + return; // don't wait until first data is cached because we want to observe // the room for events even though the first call failed. This makes // sure that we can notify the store if the space connection is @@ -257,16 +259,11 @@ export const spaceApi = baseApi.injectEndpoints({ } } - // Cast to string to break the circular type dependency - const spaceId = ( - await dispatch(spaceApi.endpoints.getSpaceRoom.initiate()).unwrap() - ).spaceId as string; - // check if the current room is a lobby room const events = await widgetApi.receiveStateEvents( STATE_EVENT_BARCAMP_SESSION_GRID, { - roomIds: [spaceId], + //roomIds: [spaceId], stateKey: roomId, } ); @@ -312,7 +309,7 @@ export const spaceApi = baseApi.injectEndpoints({ const sessionGridSubscription = widgetApi .observeStateEvents(STATE_EVENT_BARCAMP_SESSION_GRID, { - roomIds: Symbols.AnyRoom, + //roomIds: Symbols.AnyRoom, stateKey: widgetApi.widgetParameters.roomId, }) .pipe(filter(isValidSessionGridEvent)) @@ -604,15 +601,13 @@ export const spaceApi = baseApi.injectEndpoints({ const { widgetApi } = extra as ThunkExtraArgument; try { - // Cast to string to break the circular type dependency - const spaceId = ( - await dispatch(spaceApi.endpoints.getSpaceRoom.initiate()).unwrap() - ).spaceId as string; - const event = await widgetApi.sendStateEvent( STATE_EVENT_BARCAMP_LINKED_ROOM, { topicId, sessionGridId }, - { stateKey: roomId, roomId: spaceId } + { + stateKey: roomId, + //roomId: spaceId, + } ); return { data: { event } }; @@ -665,7 +660,10 @@ export const spaceApi = baseApi.injectEndpoints({ const data = await widgetApi.sendStateEvent( STATE_EVENT_SPACE_CHILD, spaceChild, - { stateKey: roomId, roomId: spaceId } + { + stateKey: roomId, + //roomId: spaceId, + } ); return { data }; diff --git a/src/store/api/topicApi.test.ts b/src/store/api/topicApi.test.ts index 229249b..00ac902 100644 --- a/src/store/api/topicApi.test.ts +++ b/src/store/api/topicApi.test.ts @@ -265,7 +265,7 @@ describe('createTopic', () => { description: 'My Description', authors: [{ id: '@user-1' }], }, - { stateKey: '$topic-1', roomId: '!room-id' } + { stateKey: '$topic-1' } ); }); @@ -338,7 +338,7 @@ describe('updateTopic', () => { description: 'Another Description', authors: topic.content.authors, }, - { stateKey: '$topic-1', roomId: '!room-id' } + { stateKey: '$topic-1' } ); }); diff --git a/src/store/api/topicApi.ts b/src/store/api/topicApi.ts index b296437..5a0f783 100644 --- a/src/store/api/topicApi.ts +++ b/src/store/api/topicApi.ts @@ -17,7 +17,6 @@ import { StateEvent } from '@matrix-widget-toolkit/api'; import { createEntityAdapter, EntityState } from '@reduxjs/toolkit'; import { isPlainObject } from 'lodash'; -import { Symbols } from 'matrix-widget-api'; import { bufferTime, filter } from 'rxjs'; import { TopicChanges } from '../../components/StickyNote'; import { @@ -59,13 +58,9 @@ export const topicApi = baseApi.injectEndpoints({ const initialState = topicEventEntityAdapter.getInitialState(); try { - const { spaceId } = await dispatch( - spaceApi.endpoints.getSpaceRoom.initiate() - ).unwrap(); - const events = await widgetApi.receiveStateEvents( - STATE_EVENT_BARCAMP_TOPIC, - { roomIds: [spaceId] } + STATE_EVENT_BARCAMP_TOPIC + //{ roomIds: [spaceId] } ); return { @@ -108,7 +103,7 @@ export const topicApi = baseApi.injectEndpoints({ const subscription = widgetApi .observeStateEvents(STATE_EVENT_BARCAMP_TOPIC, { - roomIds: Symbols.AnyRoom, + //roomIds: Symbols.AnyRoom, }) .pipe( filter(isValidTopicEvent), @@ -210,10 +205,6 @@ export const topicApi = baseApi.injectEndpoints({ const { widgetApi } = extra as ThunkExtraArgument; try { - const { spaceId } = await dispatch( - spaceApi.endpoints.getSpaceRoom.initiate() - ).unwrap(); - // don't override a topic that already exists. const topicsData = await dispatch( topicApi.endpoints.getTopics.initiate() @@ -230,7 +221,10 @@ export const topicApi = baseApi.injectEndpoints({ const event = await widgetApi.sendStateEvent( STATE_EVENT_BARCAMP_TOPIC, content, - { roomId: spaceId, stateKey: id } + { + //roomId: spaceId, + stateKey: id, + } ); dispatch( @@ -304,7 +298,10 @@ export const topicApi = baseApi.injectEndpoints({ const newEvent = await widgetApi.sendStateEvent( STATE_EVENT_BARCAMP_TOPIC, { ...topic.content, ...changes }, - { roomId: topic.room_id, stateKey: topic.state_key } + { + //roomId: topic.room_id, + stateKey: topic.state_key, + } ); return { data: { event: newEvent } }; diff --git a/src/store/api/useRoomNavigation.ts b/src/store/api/useRoomNavigation.ts index 99861df..dd2cf67 100644 --- a/src/store/api/useRoomNavigation.ts +++ b/src/store/api/useRoomNavigation.ts @@ -37,7 +37,7 @@ export function useRoomNavigation(): { STATE_EVENT_SPACE_CHILD, { stateKey: roomId, - roomIds: [data.spaceId], + //roomIds: [data.spaceId], } ); From f56009e7395bd249e91f677f9d8a78179c31d4de Mon Sep 17 00:00:00 2001 From: Kim Brose Date: Tue, 24 Jan 2023 16:21:21 +0100 Subject: [PATCH 07/19] add barcamp bot Signed-off-by: Kim Brose --- .gitignore | 3 + docs/img/barcamp-bot.png | Bin 0 -> 18891 bytes matrix-barcamp-bot/.gitignore | 4 + matrix-barcamp-bot/README.md | 44 ++++++ matrix-barcamp-bot/config.toml.sample | 8 ++ matrix-barcamp-bot/main.py | 188 ++++++++++++++++++++++++++ matrix-barcamp-bot/requirements.txt | 1 + 7 files changed, 248 insertions(+) create mode 100644 docs/img/barcamp-bot.png create mode 100644 matrix-barcamp-bot/.gitignore create mode 100644 matrix-barcamp-bot/README.md create mode 100644 matrix-barcamp-bot/config.toml.sample create mode 100644 matrix-barcamp-bot/main.py create mode 100644 matrix-barcamp-bot/requirements.txt diff --git a/.gitignore b/.gitignore index 71bd2aa..fa258f8 100644 --- a/.gitignore +++ b/.gitignore @@ -111,3 +111,6 @@ build/ /test-results/ /playwright-report/ /playwright/.cache/ + +# jetbrains +/.idea/* diff --git a/docs/img/barcamp-bot.png b/docs/img/barcamp-bot.png new file mode 100644 index 0000000000000000000000000000000000000000..b9a135966bc7274274e80235767498d4952d7af1 GIT binary patch literal 18891 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 zℜ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: `, replacing `` by the title and `<description>` by the intended description of your topic submission +4. Watch the bot react to your submission and the topic appearing in the widget + +## Getting Started + +Development on the widget and bot happens at [GitHub](https://github.com/nordeck/matrix-barcamp). + +### How to Contribute + +Please take a look at our [Contribution Guidelines](https://github.com/nordeck/.github/blob/main/docs/CONTRIBUTING.md). +Check the following steps to develop for the widget: + +### Requirements + +You need to install Python and pip. This was tested with Python 3.10.9 and pip 22.3.1. + +We suggest you set up a [Python virtual environment](https://docs.python.org/3/library/venv.html). + +### Installation + +After checkout, run `pip install -r requirements.txt` to install the required dependencies. + +### Configuration + +Rename the provided `config.toml.sample` to `config.toml` and fill it with your configuration. +In particular, it needs the homeserver, username, and password that your bot should use. + +At this time, encryption is not supported. + +### Running the Bot Locally + +1. Enter your virtual environment, e.g. `source venv/bin/activate` +2. Run the bot `python main.py` + diff --git a/matrix-barcamp-bot/config.toml.sample b/matrix-barcamp-bot/config.toml.sample new file mode 100644 index 0000000..c4f8b50 --- /dev/null +++ b/matrix-barcamp-bot/config.toml.sample @@ -0,0 +1,8 @@ +[simplematrixbotlib.config] +homeserver = "https://example.org" +username = "example-barcamp-bot" +password = "test123" +join_on_invite = true +store_path = "./store/" +allowlist = [] +blocklist = [] diff --git a/matrix-barcamp-bot/main.py b/matrix-barcamp-bot/main.py new file mode 100644 index 0000000..456d1db --- /dev/null +++ b/matrix-barcamp-bot/main.py @@ -0,0 +1,188 @@ +#!/usr/bin/env python + +# Copyright (c) 2023 Nordeck IT + Consulting GmbH +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and limitations +# under the License. + +import simplematrixbotlib as botlib +import nio +from typing import Union +from dataclasses import dataclass +import sys + + +@dataclass +class MyConfig(botlib.Config): + _homeserver: str = "" + _username: str = "" + _password: str = "" + + @property + def homeserver(self) -> str: + return self._homeserver + + @homeserver.setter + def homeserver(self, value: str) -> None: + self._homeserver = value + + @property + def username(self) -> str: + return self._username + + @username.setter + def username(self, value: str) -> None: + self._username = value + + @property + def password(self) -> str: + return self._password + + @password.setter + def password(self, value: str) -> None: + self._password = value + + +config = MyConfig() +config.load_toml('config.toml') +# enforce these settings regardless of config +config.encryption_enabled = False # barcamp widget isn't encrypted +config.ignore_unverified_devices = True # TOFU + +creds = botlib.Creds(homeserver=config.homeserver, username=config.username, password=config.password) +bot = botlib.Bot(creds, config) + +PREFIX = '!' +TOPIC_SUBMISSION_TYPE = "net.nordeck.barcamp.topic_submission" + + +async def send_reaction(room_id, message, key: str): + await bot.api._send_room( + room_id=room_id, + content={ + "m.relates_to": { + "event_id": message.event_id, + "key": key, + "rel_type": "m.annotation" + } + }, + message_type="m.reaction" + ) + + +async def get_session_grid_start_id(room_id: str) -> Union[str, None]: + res = await bot.async_client.room_get_state_event( + room_id=room_id, + event_type="net.nordeck.barcamp.session_grid", + state_key=room_id + ) + + if not isinstance(res, nio.responses.RoomGetStateEventResponse): + await bot.api.send_markdown_message( + room_id=room_id, + message="could not find `net.nordeck.barcamp.session_grid` state event", + msgtype="m.notice" + ) + return None + + session_grid_start_id = res.content.get("topicStartEventId") + + if session_grid_start_id is None: + await bot.api.send_markdown_message( + room_id=room_id, + message="could not find `topicStartEventId` in `net.nordeck.barcamp.session_grid` state event", + msgtype="m.notice" + ) + + return session_grid_start_id + + +@bot.listener.on_message_event +async def submit(room, message): + match = botlib.MessageMatch(room, message, bot, PREFIX) + + if match.is_not_from_this_bot() and match.prefix() and match.command("submit"): + try: + submission = match.event.body.split(maxsplit=1)[1] + submission = submission.split(':', 1) + + # invalid input + if len(submission) < 2: + await send_reaction(room.room_id, message, "❌") + return + + # parse the submission + title, description = submission + title = title.strip() + description = description.strip() + + # get the topicStartEventId https://github.com/nordeck/matrix-barcamp/pull/57 + session_grid_start_id = await get_session_grid_start_id(room.room_id) + if session_grid_start_id is None: + return + + # are submissions still locked? + locked = room.power_levels.can_user_send_message(bot.async_client.user_id, event_type=TOPIC_SUBMISSION_TYPE) + if not locked: + await send_reaction(room.room_id, message, "🔒️") + return + + # send the "translated" event to the barcamp widget + await bot.api._send_room( + room_id=room.room_id, + content={ + "title": title, + "description": description, + "author": message.sender, + "m.relates_to": { + "event_id": session_grid_start_id, + "rel_type": "m.reference" + } + }, + message_type=TOPIC_SUBMISSION_TYPE + ) + + # send a checkmark reaction to notify the user + await send_reaction(room.room_id, message, "✅") + + except KeyboardInterrupt as e: + raise e + + except Exception: + await send_reaction(room.room_id, message, "❌") + + +@bot.listener.on_message_event +async def help_message(room, message): + match = botlib.MessageMatch(room, message, bot, PREFIX) + + if match.is_not_from_this_bot() and match.prefix() and match.command("help"): + await bot.api.send_markdown_message( + room.room_id, + "Hi, I'm matrix-barcamp-bot! Suggest a topic by writing `!submit <title>: <description>`.\n" + "I will react with these emoji:\n" + "- ✅ if your submission was handled\n" + "- ❌ if there was an error (please notify you local admin)\n" + "- 🔒 if submissions are locked and you may not submit", + msgtype="m.notice" + ) + + # send a checkmark reaction to notify the user + await send_reaction(room.room_id, message, "✅") + + +if __name__ == '__main__': + try: + bot.run() + except KeyboardInterrupt: + print("Received keyboard interrupt.") + sys.exit(0) diff --git a/matrix-barcamp-bot/requirements.txt b/matrix-barcamp-bot/requirements.txt new file mode 100644 index 0000000..28e93cc --- /dev/null +++ b/matrix-barcamp-bot/requirements.txt @@ -0,0 +1 @@ +simplematrixbotlib~=2.8.0 \ No newline at end of file From e22abf05c3920847bff4fb314f0e35e751cd716c Mon Sep 17 00:00:00 2001 From: Kim Brose <kim.brose@nordeck.net> Date: Wed, 25 Jan 2023 12:32:37 +0100 Subject: [PATCH 08/19] add barcamp bot Dockerfile Signed-off-by: Kim Brose <kim.brose@nordeck.net> --- matrix-barcamp-bot/Dockerfile | 10 ++++++++++ matrix-barcamp-bot/README.md | 12 ++++++++++++ 2 files changed, 22 insertions(+) create mode 100644 matrix-barcamp-bot/Dockerfile diff --git a/matrix-barcamp-bot/Dockerfile b/matrix-barcamp-bot/Dockerfile new file mode 100644 index 0000000..a3f95b8 --- /dev/null +++ b/matrix-barcamp-bot/Dockerfile @@ -0,0 +1,10 @@ +FROM python:3.10-slim + +WORKDIR /usr/src/app + +COPY requirements.txt ./ +RUN pip install --no-cache-dir -r requirements.txt + +COPY . . + +CMD [ "python", "./main.py" ] \ No newline at end of file diff --git a/matrix-barcamp-bot/README.md b/matrix-barcamp-bot/README.md index 5b847d3..6b80493 100644 --- a/matrix-barcamp-bot/README.md +++ b/matrix-barcamp-bot/README.md @@ -42,3 +42,15 @@ At this time, encryption is not supported. 1. Enter your virtual environment, e.g. `source venv/bin/activate` 2. Run the bot `python main.py` +## Deployment + +Yon can build and run the widget using Docker: + +```sh +docker build --tag=ghcr.io/nordeck/matrix-barcamp-bot:latest . +# copy config.toml.sample to config.toml and edit your credentials +docker run --rm -it \ +--mount type=bind,source=$(pwd)/config.toml,dst=/usr/src/app/config.toml \ +--mount type=bind,source=$(pwd)/session.txt,dst=/usr/src/app/session.txt \ +ghcr.io/nordeck/matrix-barcamp-bot:latest +``` From 6830e76187e571122a7127605473f9ba66d3729f Mon Sep 17 00:00:00 2001 From: Dominik Henneke <dominik.henneke@nordeck.net> Date: Wed, 25 Jan 2023 17:26:19 +0100 Subject: [PATCH 09/19] Add a pipeline to create a docker image of the bot --- .github/workflows/ci.yml | 48 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8c70a61..2cd22be 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -107,6 +107,54 @@ jobs: env: GITHUB_TOKEN: ${{ steps.generate_token.outputs.token }} + build-bot: + name: Build Bot + runs-on: ubuntu-latest + timeout-minutes: 15 + outputs: + docker-tag: ${{ steps.meta.outputs.version }} + steps: + - uses: actions/checkout@v3 + + - uses: actions/setup-python@v4 + with: + python-version: '3.10' + + - name: Login to ghcr.io + uses: docker/login-action@v2 + with: + registry: ghcr.io + username: ${{ github.repository_owner }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v2 + + - name: Generate Docker metadata + id: meta + uses: docker/metadata-action@v4 + env: + DOCKER_METADATA_PR_HEAD_SHA: true + with: + images: ghcr.io/nordeck/matrix-barcamp-bot + labels: | + org.opencontainers.image.title=Matrix BarCamp Bot + org.opencontainers.image.description=A bot accompanying the BarCamp widget to support clients without support for the required widget API calls + org.opencontainers.image.vendor=Nordeck IT + Consulting GmbH + tags: | + type=sha,prefix= + type=raw,value=latest,enable={{is_default_branch}} + + - name: Build and push + uses: docker/build-push-action@v3 + id: dockerBuild + with: + push: ${{ secrets.GH_APP_OS_APP_ID != '' }} + context: . + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + #platforms: linux/amd64,linux/arm64,linux/s390x + e2e: runs-on: ubuntu-latest timeout-minutes: 25 From 1a11cd3c151afa9c96e95a2e438884cd041e63c1 Mon Sep 17 00:00:00 2001 From: Dominik Henneke <dominik.henneke@nordeck.net> Date: Wed, 25 Jan 2023 17:31:09 +0100 Subject: [PATCH 10/19] Enable builds for the branch --- .github/workflows/ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2cd22be..b8a971a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -4,6 +4,7 @@ on: push: branches: - main + - nic/env/fosdem-2023 pull_request: env: From 59d577529862d533d888081da85f4c3231f68a11 Mon Sep 17 00:00:00 2001 From: Dominik Henneke <dominik.henneke@nordeck.net> Date: Wed, 25 Jan 2023 17:31:58 +0100 Subject: [PATCH 11/19] Fix build context --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b8a971a..5fe477f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -87,7 +87,7 @@ jobs: id: dockerBuild with: push: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' || github.event_name == 'pull_request' && secrets.GH_APP_OS_APP_ID != '' }} - context: . + context: matrix-barcamp-bot tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} platforms: linux/amd64,linux/arm64,linux/s390x From 4fe3e0873dd057390b577a069e7bcdaf1d6aee36 Mon Sep 17 00:00:00 2001 From: Dominik Henneke <dominik.henneke@nordeck.net> Date: Wed, 25 Jan 2023 17:33:10 +0100 Subject: [PATCH 12/19] Fix build context in the correct place --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5fe477f..6f24cde 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -87,7 +87,7 @@ jobs: id: dockerBuild with: push: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' || github.event_name == 'pull_request' && secrets.GH_APP_OS_APP_ID != '' }} - context: matrix-barcamp-bot + context: . tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} platforms: linux/amd64,linux/arm64,linux/s390x @@ -151,7 +151,7 @@ jobs: id: dockerBuild with: push: ${{ secrets.GH_APP_OS_APP_ID != '' }} - context: . + context: matrix-barcamp-bot tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} #platforms: linux/amd64,linux/arm64,linux/s390x From 8d5b7f5aa8fef3308b708031f8fd4d53daea1a8c Mon Sep 17 00:00:00 2001 From: Dominik Henneke <dominik.henneke@nordeck.net> Date: Wed, 25 Jan 2023 17:40:20 +0100 Subject: [PATCH 13/19] Add PYTHONUNBUFFERED to the bot dockerfile --- matrix-barcamp-bot/Dockerfile | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/matrix-barcamp-bot/Dockerfile b/matrix-barcamp-bot/Dockerfile index a3f95b8..1b8cba4 100644 --- a/matrix-barcamp-bot/Dockerfile +++ b/matrix-barcamp-bot/Dockerfile @@ -1,5 +1,7 @@ FROM python:3.10-slim +ENV PYTHONUNBUFFERED=1 + WORKDIR /usr/src/app COPY requirements.txt ./ @@ -7,4 +9,4 @@ RUN pip install --no-cache-dir -r requirements.txt COPY . . -CMD [ "python", "./main.py" ] \ No newline at end of file +CMD [ "python", "./main.py" ] From efb4729f8f5d8f72440921857ada6f186719be6d Mon Sep 17 00:00:00 2001 From: Dominik Henneke <dominik.henneke@nordeck.net> Date: Wed, 25 Jan 2023 17:47:39 +0100 Subject: [PATCH 14/19] Support configuring the session_stored_file --- matrix-barcamp-bot/main.py | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/matrix-barcamp-bot/main.py b/matrix-barcamp-bot/main.py index 456d1db..7fedc6e 100644 --- a/matrix-barcamp-bot/main.py +++ b/matrix-barcamp-bot/main.py @@ -26,6 +26,15 @@ class MyConfig(botlib.Config): _homeserver: str = "" _username: str = "" _password: str = "" + _session_stored_file: str = "session.txt" + + @property + def session_stored_file(self) -> str: + return self._session_stored_file + + @session_stored_file.setter + def session_stored_file(self, value: str) -> None: + self._session_stored_file = value @property def homeserver(self) -> str: @@ -58,7 +67,12 @@ def password(self, value: str) -> None: config.encryption_enabled = False # barcamp widget isn't encrypted config.ignore_unverified_devices = True # TOFU -creds = botlib.Creds(homeserver=config.homeserver, username=config.username, password=config.password) +creds = botlib.Creds( + homeserver=config.homeserver, + username=config.username, + password=config.password, + session_stored_file=config.session_stored_file + ) bot = botlib.Bot(creds, config) PREFIX = '!' From bba7f3218bb6a43e8b1abb7debfcc431b5438faa Mon Sep 17 00:00:00 2001 From: Dominik Henneke <dominik.henneke@nordeck.net> Date: Wed, 25 Jan 2023 18:09:30 +0100 Subject: [PATCH 15/19] Add a experimental helm chart for the bot --- charts/matrix-barcamp-bot/.helmignore | 23 +++++ charts/matrix-barcamp-bot/Chart.yaml | 7 ++ charts/matrix-barcamp-bot/templates/NOTES.txt | 1 + .../matrix-barcamp-bot/templates/_helpers.tpl | 51 ++++++++++ .../matrix-barcamp-bot/templates/service.yaml | 15 +++ .../templates/statefulset.yaml | 98 +++++++++++++++++++ charts/matrix-barcamp-bot/values.demo.yaml | 12 +++ charts/matrix-barcamp-bot/values.yaml | 85 ++++++++++++++++ 8 files changed, 292 insertions(+) create mode 100644 charts/matrix-barcamp-bot/.helmignore create mode 100644 charts/matrix-barcamp-bot/Chart.yaml create mode 100644 charts/matrix-barcamp-bot/templates/NOTES.txt create mode 100644 charts/matrix-barcamp-bot/templates/_helpers.tpl create mode 100644 charts/matrix-barcamp-bot/templates/service.yaml create mode 100644 charts/matrix-barcamp-bot/templates/statefulset.yaml create mode 100644 charts/matrix-barcamp-bot/values.demo.yaml create mode 100644 charts/matrix-barcamp-bot/values.yaml diff --git a/charts/matrix-barcamp-bot/.helmignore b/charts/matrix-barcamp-bot/.helmignore new file mode 100644 index 0000000..0e8a0eb --- /dev/null +++ b/charts/matrix-barcamp-bot/.helmignore @@ -0,0 +1,23 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*.orig +*~ +# Various IDEs +.project +.idea/ +*.tmproj +.vscode/ diff --git a/charts/matrix-barcamp-bot/Chart.yaml b/charts/matrix-barcamp-bot/Chart.yaml new file mode 100644 index 0000000..074f9a0 --- /dev/null +++ b/charts/matrix-barcamp-bot/Chart.yaml @@ -0,0 +1,7 @@ +apiVersion: v2 +name: matrix-barcamp-bot +description: A bot accompanying the BarCamp widget to support clients without support for the required widget API calls +type: application +version: 0.1.0 +appVersion: "0.0.0" +home: https://github.com/nordeck/matrix-barcamp diff --git a/charts/matrix-barcamp-bot/templates/NOTES.txt b/charts/matrix-barcamp-bot/templates/NOTES.txt new file mode 100644 index 0000000..acdbc0b --- /dev/null +++ b/charts/matrix-barcamp-bot/templates/NOTES.txt @@ -0,0 +1 @@ +TODO: Invite the user xxx to start diff --git a/charts/matrix-barcamp-bot/templates/_helpers.tpl b/charts/matrix-barcamp-bot/templates/_helpers.tpl new file mode 100644 index 0000000..bc2ea21 --- /dev/null +++ b/charts/matrix-barcamp-bot/templates/_helpers.tpl @@ -0,0 +1,51 @@ +{{/* +Expand the name of the chart. +*/}} +{{- define "matrix-barcamp-bot.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If release name contains chart name it will be used as a full name. +*/}} +{{- define "matrix-barcamp-bot.fullname" -}} +{{- if .Values.fullnameOverride }} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- $name := default .Chart.Name .Values.nameOverride }} +{{- if contains $name .Release.Name }} +{{- .Release.Name | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} +{{- end }} +{{- end }} +{{- end }} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "matrix-barcamp-bot.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Common labels +*/}} +{{- define "matrix-barcamp-bot.labels" -}} +helm.sh/chart: {{ include "matrix-barcamp-bot.chart" . }} +{{ include "matrix-barcamp-bot.selectorLabels" . }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end }} + +{{/* +Selector labels +*/}} +{{- define "matrix-barcamp-bot.selectorLabels" -}} +app.kubernetes.io/name: {{ include "matrix-barcamp-bot.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} diff --git a/charts/matrix-barcamp-bot/templates/service.yaml b/charts/matrix-barcamp-bot/templates/service.yaml new file mode 100644 index 0000000..c13f24d --- /dev/null +++ b/charts/matrix-barcamp-bot/templates/service.yaml @@ -0,0 +1,15 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ include "matrix-barcamp-bot.fullname" . }} + labels: + {{- include "matrix-barcamp-bot.labels" . | nindent 4 }} +spec: + type: {{ .Values.service.type }} + ports: + - port: {{ .Values.service.port }} + targetPort: http + protocol: TCP + name: http + selector: + {{- include "matrix-barcamp-bot.selectorLabels" . | nindent 4 }} diff --git a/charts/matrix-barcamp-bot/templates/statefulset.yaml b/charts/matrix-barcamp-bot/templates/statefulset.yaml new file mode 100644 index 0000000..ffeb38a --- /dev/null +++ b/charts/matrix-barcamp-bot/templates/statefulset.yaml @@ -0,0 +1,98 @@ +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: {{ include "matrix-barcamp-bot.fullname" . }} + labels: + {{- include "matrix-barcamp-bot.labels" . | nindent 4 }} +spec: + replicas: 1 + serviceName: {{ include "matrix-barcamp-bot.fullname" . }} + selector: + matchLabels: + {{- include "matrix-barcamp-bot.selectorLabels" . | nindent 6 }} + template: + metadata: + {{- with .Values.podAnnotations }} + annotations: + {{- toYaml . | nindent 8 }} + {{- end }} + labels: + {{- include "matrix-barcamp-bot.selectorLabels" . | nindent 8 }} + spec: + {{- with .Values.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} + securityContext: + {{- toYaml .Values.podSecurityContext | nindent 8 }} + containers: + - name: {{ .Chart.Name }} + securityContext: + {{- toYaml .Values.securityContext | nindent 12 }} + image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" + imagePullPolicy: {{ .Values.image.pullPolicy }} + #ports: + # - name: http + # containerPort: 80 + # protocol: TCP + #livenessProbe: + # httpGet: + # path: / + # port: http + #readinessProbe: + # httpGet: + # path: / + # port: http + resources: + {{- toYaml .Values.resources | nindent 12 }} + volumeMounts: + - name: config + mountPath: /usr/src/app/config.toml + subPath: "config.toml" + - name: bot-data + mountPath: /data + subPath: {{ .Values.persistence.subPath }} + volumes: + - name: "config" + secret: + secretName: {{ .Values.config.secretName }} + {{- if not .Values.persistence.enabled }} + - name: "bot-data" + emptyDir: {} + {{- else }} + {{- if .Values.persistence.existingClaim }} + - name: "bot-data" + persistentVolumeClaim: + claimName: {{ .Values.persistence.existingClaim }} + {{- end }} + {{- end }} + {{- with .Values.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.tolerations }} + tolerations: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- if and .Values.persistence.enabled (not .Values.persistence.existingClaim) }} + volumeClaimTemplates: + - metadata: + name: bot-data + labels: + {{- include "matrix-barcamp-bot.labels" . | nindent 10 }} + spec: + accessModes: + {{- range .Values.persistence.accessModes }} + - {{ . | quote }} + {{- end }} + resources: + requests: + storage: {{ .Values.persistence.size | quote }} + {{- with .Values.persistence.storageClassName }} + storageClassName: {{ . }} + {{- end }} + {{- end }} diff --git a/charts/matrix-barcamp-bot/values.demo.yaml b/charts/matrix-barcamp-bot/values.demo.yaml new file mode 100644 index 0000000..7e15979 --- /dev/null +++ b/charts/matrix-barcamp-bot/values.demo.yaml @@ -0,0 +1,12 @@ +# helm upgrade -n env-demo --install matrix-barcamp-bot ./charts/matrix-barcamp-bot -f ./charts/matrix-barcamp-bot/values.demo.yaml + +image: + tag: "efb4729" + +persistence: + enabled: true + size: 1Gi + storageClassName: gp2 + +config: + secretName: "matrix-barcamp-bot-config" diff --git a/charts/matrix-barcamp-bot/values.yaml b/charts/matrix-barcamp-bot/values.yaml new file mode 100644 index 0000000..5f009d3 --- /dev/null +++ b/charts/matrix-barcamp-bot/values.yaml @@ -0,0 +1,85 @@ +# Default values for matrix-barcamp-bot. +# This is a YAML-formatted file. +# Declare variables to be passed into your templates. + +replicaCount: 1 + +image: + repository: ghcr.io/nordeck/matrix-barcamp-bot + pullPolicy: IfNotPresent + # Overrides the image tag whose default is the chart appVersion. + tag: "" + +imagePullSecrets: [] +nameOverride: "" +fullnameOverride: "" + +podAnnotations: {} + +podSecurityContext: {} + # fsGroup: 2000 + +securityContext: {} + # capabilities: + # drop: + # - ALL + # readOnlyRootFilesystem: true + # runAsNonRoot: true + # runAsUser: 1000 + +service: + type: ClusterIP + port: 8080 + +resources: {} + # We usually recommend not to specify default resources and to leave this as a conscious + # choice for the user. This also increases chances charts run on environments with little + # resources, such as Minikube. If you do want to specify resources, uncomment the following + # lines, adjust them as necessary, and remove the curly braces after 'resources:'. + # limits: + # cpu: 100m + # memory: 128Mi + # requests: + # cpu: 100m + # memory: 128Mi + +nodeSelector: {} + +tolerations: [] + +affinity: {} + +persistence: + enabled: false + # The subdirectory of the volume to mount to, useful in dev environments + # and one PV for multiple services. + subPath: "" + # Persistent Volume Storage Class + # If defined, storageClassName: <storageClass> + # storageClassName: "" + accessModes: + - ReadWriteOnce + size: 8Gi + +config: + # The name of the secret that contains a `config.toml` key + # + # Example: + # + # apiVersion: v1 + # kind: Secret + # metadata: + # name: secret-name + # type: Opaque + # stringData: + # 'config.toml': | + # [simplematrixbotlib.config] + # homeserver = "https://example.org" + # username = "example-barcamp-bot" + # password = "test123" + # join_on_invite = true + # store_path = "/data/store/" + # allowlist = [] + # blocklist = [] + # session_stored_file: "/data/session.txt" + secretName: "" From 574717e49833036e0ff8f3b820ece478407ec0b3 Mon Sep 17 00:00:00 2001 From: Dominik Henneke <dominik.henneke@nordeck.net> Date: Thu, 26 Jan 2023 16:31:09 +0100 Subject: [PATCH 16/19] Add more icons --- src/components/IconPicker/IconPicker.test.tsx | 13 ++++++++----- src/components/IconPicker/icons.ts | 5 +++++ 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/src/components/IconPicker/IconPicker.test.tsx b/src/components/IconPicker/IconPicker.test.tsx index 19e7de7..ca7972d 100644 --- a/src/components/IconPicker/IconPicker.test.tsx +++ b/src/components/IconPicker/IconPicker.test.tsx @@ -42,7 +42,7 @@ describe('<IconPicker>', () => { const list = screen.getByRole('listbox', { name: /available icons/i }); - expect(within(list).getAllByRole('option')).toHaveLength(25); + expect(within(list).getAllByRole('option')).toHaveLength(30); expect( within(list).getByRole('option', { name: 'Icon "dog"', selected: true }) ).toBeInTheDocument(); @@ -109,12 +109,12 @@ describe('<IconPicker>', () => { expect( within(list).getByRole('option', { - name: 'Icon "compass"', + name: 'Icon "server"', selected: true, }) ).toBeInTheDocument(); expect( - screen.getByRole('combobox', { name: 'Icon "compass"', expanded: true }) + screen.getByRole('combobox', { name: 'Icon "server"', expanded: true }) ).toBeInTheDocument(); // Go to end @@ -122,12 +122,15 @@ describe('<IconPicker>', () => { expect( within(list).getByRole('option', { - name: 'Icon "fire"', + name: 'Icon "face surprise"', selected: true, }) ).toBeInTheDocument(); expect( - screen.getByRole('combobox', { name: 'Icon "fire"', expanded: true }) + screen.getByRole('combobox', { + name: 'Icon "face surprise"', + expanded: true, + }) ).toBeInTheDocument(); // Wrap over last diff --git a/src/components/IconPicker/icons.ts b/src/components/IconPicker/icons.ts index 214ac0d..b1db98a 100644 --- a/src/components/IconPicker/icons.ts +++ b/src/components/IconPicker/icons.ts @@ -42,6 +42,11 @@ export const iconSet = [ 'car', 'compass', 'fire', + 'pizza slice', + 'beer mug', + 'comment', + 'server', + 'face surprise', ]; export function randomIcon(): string { From 7a69a1253618c1e57110ad34952d069a82c5291d Mon Sep 17 00:00:00 2001 From: Dominik Henneke <dominik.henneke@nordeck.net> Date: Mon, 30 Jan 2023 18:15:31 +0100 Subject: [PATCH 17/19] Reduce the height of the slots and make the tracks fill the full width --- src/components/SessionGrid/SessionGrid.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/components/SessionGrid/SessionGrid.tsx b/src/components/SessionGrid/SessionGrid.tsx index be3a6a5..121b321 100644 --- a/src/components/SessionGrid/SessionGrid.tsx +++ b/src/components/SessionGrid/SessionGrid.tsx @@ -68,6 +68,7 @@ const TableContainer = styled.table<{ canDropSession, theme, }) => ({ + width: '100%', borderSpacing: 0, background: canDropSession ? `repeating-linear-gradient(45deg, ${theme.pageBackground}, ${theme.pageBackground} 10px, transparent 10px, transparent 20px)` @@ -88,8 +89,8 @@ const TableContainer = styled.table<{ 'td.session': { minWidth: 200, width: `${100 / trackCount}%`, - minHeight: 200, - height: 200, + minHeight: 115, + height: 115, }, // set the default borders, padding, and alignment for all cells From df1918c7a5d21e0317ae1941bf3f1ebd0a9ebff6 Mon Sep 17 00:00:00 2001 From: Dominik Henneke <dominik.henneke@nordeck.net> Date: Mon, 30 Jan 2023 18:16:02 +0100 Subject: [PATCH 18/19] Add the option to minimize the sidebar --- src/components/Layout/LobbyLayout.tsx | 18 +++++--- src/components/ParkingLot/ParkingLot.tsx | 55 ++++++++++++++++++------ 2 files changed, 54 insertions(+), 19 deletions(-) diff --git a/src/components/Layout/LobbyLayout.tsx b/src/components/Layout/LobbyLayout.tsx index 2033c10..ce835cb 100644 --- a/src/components/Layout/LobbyLayout.tsx +++ b/src/components/Layout/LobbyLayout.tsx @@ -14,7 +14,7 @@ * limitations under the License. */ -import React from 'react'; +import React, { useState } from 'react'; import { ParkingLotEntry, Session, TimeSlot, Track } from '../../lib/events'; import { useMoveTimeSlotMutation, @@ -40,9 +40,9 @@ const LeftContainer = styled.div({ minWidth: 0, }); -const RightContainer = styled.div({ - width: 250, -}); +const RightContainer = styled.div<{ open: boolean }>(({ open }) => ({ + width: open ? 250 : 35, +})); export function LobbyLayout({ timeSlots, @@ -59,6 +59,8 @@ export function LobbyLayout({ const [moveTopicToSession] = useMoveTopicToSessionMutation(); const [moveTimeSlot] = useMoveTimeSlotMutation(); + const [parkingLotOpen, setParkingLotOpen] = useState(true); + return ( <DragAndDropProvider onMoveTimeSlot={(timeSlotId, toIndex) => { @@ -79,8 +81,12 @@ export function LobbyLayout({ sessions={sessions} /> </LeftContainer> - <RightContainer> - <ParkingLot topics={parkingLotTopics} /> + <RightContainer open={parkingLotOpen}> + <ParkingLot + open={parkingLotOpen} + topics={parkingLotTopics} + onImageClick={() => setParkingLotOpen((old) => !old)} + /> </RightContainer> </Container> </DragAndDropProvider> diff --git a/src/components/ParkingLot/ParkingLot.tsx b/src/components/ParkingLot/ParkingLot.tsx index 9072a62..a5c7a48 100644 --- a/src/components/ParkingLot/ParkingLot.tsx +++ b/src/components/ParkingLot/ParkingLot.tsx @@ -14,8 +14,9 @@ * limitations under the License. */ +import { DispatchWithoutAction } from 'react'; import { useTranslation } from 'react-i18next'; -import { Icon, Segment } from 'semantic-ui-react'; +import { Button, Segment } from 'semantic-ui-react'; import { ParkingLotEntry } from '../../lib/events'; import { useDeleteTopicMutation, @@ -28,10 +29,29 @@ import { SubmittedTopics } from '../SubmittedTopics'; import { Tooltip } from '../Tooltip'; import { TopicList } from './TopicList'; -const Container = styled(Segment)({ +const Container = styled(Segment)<{ open: boolean }>(({ open }) => ({ display: 'flex', flexDirection: 'column', height: '100%', + '&&&': open + ? {} + : { + paddingLeft: 0, + paddingRight: 0, + marginLeft: 0, + marginRight: 0, + }, +})); + +const IconButton = styled(Button)({ + '&&&&': { + fontSize: '2em', + margin: 0, + padding: 0, + borderColor: 'transparent', + boxShadow: 'none', + verticalAlign: 'middle', + }, }); const Title = styled.div({ @@ -41,36 +61,45 @@ const Title = styled.div({ type ParkingLotProps = { topics: ParkingLotEntry[]; + open?: boolean; + onImageClick?: DispatchWithoutAction; }; -export function ParkingLot({ topics }: ParkingLotProps) { +export function ParkingLot({ topics, open, onImageClick }: ParkingLotProps) { const { t } = useTranslation(); const [selectNextTopic] = useSelectNextTopicMutation(); const [deleteTopic] = useDeleteTopicMutation(); const [updateTopic] = useUpdateTopicMutation(); return ( - <Container> + <Container open={open}> <Tooltip + suppress={!open} content={t( 'parkingLot.explanation', 'The Parking Lot collects topics before they are placed on the timetable. In addition, it allows to move sessions out of the timetable or to store sessions for later consideration.' )} > <Title> - <Icon size="big" className="parking" /> - {t('parkingLot.title', 'Parking Lot')} + <IconButton icon="parking" basic size="big" onClick={onImageClick} /> + {open && t('parkingLot.title', 'Parking Lot')} - deleteTopic({ topicId })} - onTopicChange={(topicId, changes) => updateTopic({ topicId, changes })} - /> + {open && ( + <> + deleteTopic({ topicId })} + onTopicChange={(topicId, changes) => + updateTopic({ topicId, changes }) + } + /> - - + + + + )} ); } From 4094eab3c3401dfcdd542f7c5a2c1fffbe2bb915 Mon Sep 17 00:00:00 2001 From: Dominik Henneke Date: Mon, 30 Jan 2023 18:22:49 +0100 Subject: [PATCH 19/19] Fix light-mode color of the parking lot icon --- src/components/ParkingLot/ParkingLot.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/components/ParkingLot/ParkingLot.tsx b/src/components/ParkingLot/ParkingLot.tsx index a5c7a48..50cb21f 100644 --- a/src/components/ParkingLot/ParkingLot.tsx +++ b/src/components/ParkingLot/ParkingLot.tsx @@ -43,7 +43,7 @@ const Container = styled(Segment)<{ open: boolean }>(({ open }) => ({ }, })); -const IconButton = styled(Button)({ +const IconButton = styled(Button)(({ theme }) => ({ '&&&&': { fontSize: '2em', margin: 0, @@ -51,8 +51,9 @@ const IconButton = styled(Button)({ borderColor: 'transparent', boxShadow: 'none', verticalAlign: 'middle', + color: theme.type === 'dark' ? 'white !important' : 'black !important', }, -}); +})); const Title = styled.div({ marginBottom: '1em',