Skip to content

Commit

Permalink
Add support for extra widgets in the session rooms
Browse files Browse the repository at this point in the history
  • Loading branch information
dhenneke committed Sep 26, 2023
1 parent fa9aac6 commit fb0697b
Show file tree
Hide file tree
Showing 6 changed files with 318 additions and 26 deletions.
5 changes: 5 additions & 0 deletions .changeset/eight-mails-taste.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'matrix-barcamp-widget': minor
---

Add support for extra widgets in the session rooms.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ typings/
# dotenv environment variables file
.env
.env.test
.env.local

# parcel-bundler cache (https://parceljs.org/)
.cache
Expand Down
7 changes: 6 additions & 1 deletion docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,17 @@ Runtime configuration can be performed via environment variables.
## Environment Variables

```sh
# Configures the base URL of Element, used to register the Jitsi Widget.
# Configures the base URL of Element, used to register the Jitsi Widget.
# Setting the variable is not important, as an Element intance chooses to use a
# local version of the Jitsi Widget anyway. Defaults to `https://app.element.io`.
REACT_APP_ELEMENT_BASE_URL=https://app.element.io

# The hostname of the Jitsi Instance where video conferences should be hosted.
# Defaults to `jitsi.riot.im`
REACT_APP_JITSI_HOST_NAME=jitsi.riot.im

# Additional widgets that should be added to the session rooms.
# Example: `[{"id":"widget","name":"Widget","type":"net.nordeck.widget-2:pad","url":"http://2.widget.example"}]`
# Important: The id and type fields must be unique.
REACT_APP_EXTRA_WIDGETS=[]
```
217 changes: 216 additions & 1 deletion src/store/api/roomWidgetsApi.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,24 @@
*/

import { MockedWidgetApi, mockWidgetApi } from '@matrix-widget-toolkit/testing';
import { getEnvironment } from '../../lib/environment';
import { createStore } from '../store';
import { roomWidgetsApi } from './roomWidgetsApi';
import { getExtraWidgetsWidgets, roomWidgetsApi } from './roomWidgetsApi';

jest.mock('../../lib/environment');

let widgetApi: MockedWidgetApi;

afterEach(() => widgetApi.stop());

beforeEach(() => (widgetApi = mockWidgetApi()));

beforeEach(() => {
jest
.mocked(getEnvironment)
.mockImplementation((_, defaultValue) => defaultValue);
});

describe('setupLobbyRoomWidgets', () => {
it('should setup jitsi widget and widget layout', async () => {
const store = createStore({ widgetApi });
Expand Down Expand Up @@ -218,6 +227,118 @@ describe('setupSessionRoomWidgets', () => {
);
});

it('should setup extra widgets', async () => {
jest.mocked(getEnvironment).mockImplementation((name, defaultValue) => {
switch (name) {
case 'REACT_APP_EXTRA_WIDGETS':
return JSON.stringify([
{
id: 'widget-1',
type: 'net.nordeck.widget-1:pad',
name: 'Widget 1',
url: 'http://1.widget.example',
},
{
id: 'widget-2',
type: 'net.nordeck.widget-2:pad',
name: 'Widget 2',
url: 'http://2.widget.example',
},
]);
default:
return defaultValue;
}
});

const store = createStore({ widgetApi });

await store
.dispatch(
roomWidgetsApi.endpoints.setupSessionRoomWidgets.initiate({
roomId: '!room-id',
roomName: 'My Room',
})
)
.unwrap();

expect(widgetApi.sendStateEvent).toBeCalledTimes(5);
expect(widgetApi.sendStateEvent).toBeCalledWith(
'im.vector.modular.widgets',
{
creatorUserId: '@user-id',
id: 'barcamp',
name: 'BarCamp',
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' }
);
expect(widgetApi.sendStateEvent).toBeCalledWith(
'im.vector.modular.widgets',
{
creatorUserId: '@user-id',
data: {
conferenceId: 'EFZG633NFVUWI',
domain: 'jitsi.riot.im',
roomName: 'My Room',
},
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).toBeCalledWith(
'im.vector.modular.widgets',
{
creatorUserId: '@user-id',
id: 'widget-1',
name: 'Widget 1',
type: 'net.nordeck.widget-1:pad',
url: 'http://1.widget.example',
},
{ roomId: '!room-id', stateKey: 'widget-1' }
);
expect(widgetApi.sendStateEvent).toBeCalledWith(
'im.vector.modular.widgets',
{
creatorUserId: '@user-id',
id: 'widget-2',
name: 'Widget 2',
type: 'net.nordeck.widget-2:pad',
url: 'http://2.widget.example',
},
{ roomId: '!room-id', stateKey: 'widget-2' }
);
expect(widgetApi.sendStateEvent).toBeCalledWith(
'io.element.widgets.layout',
{
widgets: {
jitsi: {
container: 'top',
height: 100,
index: 0,
width: 50,
},
'widget-1': {
container: 'top',
height: 100,
index: 1,
width: 30,
},
barcamp: {
container: 'top',
height: 100,
index: 2,
width: 20,
},
},
},
{ roomId: '!room-id' }
);
});

it('should update existing jitsi widget, barcamp widget, and widget layout', async () => {
widgetApi.mockSendStateEvent({
content: {
Expand Down Expand Up @@ -325,3 +446,97 @@ describe('setupSessionRoomWidgets', () => {
);
});
});

describe('getExtraWidgetsWidgets', () => {
it('should accept configuration', () => {
jest.mocked(getEnvironment).mockImplementation((name, defaultValue) => {
switch (name) {
case 'REACT_APP_EXTRA_WIDGETS':
return JSON.stringify([
{
id: 'widget-1',
type: 'net.nordeck.widget-1:pad',
name: 'Widget 1',
url: 'http://1.widget.example',
},
]);
default:
return defaultValue;
}
});

expect(getExtraWidgetsWidgets('@user-id')).toEqual([
{
creatorUserId: '@user-id',
id: 'widget-1',
type: 'net.nordeck.widget-1:pad',
name: 'Widget 1',
url: 'http://1.widget.example',
},
]);
});

it('should accept additional properties', () => {
jest.mocked(getEnvironment).mockImplementation((name, defaultValue) => {
switch (name) {
case 'REACT_APP_EXTRA_WIDGETS':
return JSON.stringify([
{
id: 'widget-1',
type: 'net.nordeck.widget-1:pad',
name: 'Widget 1',
url: 'http://1.widget.example',
additional: 'tmp',
},
]);
default:
return defaultValue;
}
});

expect(getExtraWidgetsWidgets('@user-id')).toEqual([
{
creatorUserId: '@user-id',
id: 'widget-1',
type: 'net.nordeck.widget-1:pad',
name: 'Widget 1',
url: 'http://1.widget.example',
},
]);
});

it.each<Object>([
{ id: undefined },
{ id: null },
{ id: 111 },
{ name: undefined },
{ name: null },
{ name: 111 },
{ type: undefined },
{ type: null },
{ type: 111 },
{ url: undefined },
{ url: null },
{ url: 111 },
{ url: 'no-uri' },
])('should reject event with patch %j', (patch: Object) => {
jest.mocked(getEnvironment).mockImplementation((name, defaultValue) => {
switch (name) {
case 'REACT_APP_EXTRA_WIDGETS':
return JSON.stringify([
{
id: 'widget-1',
type: 'net.nordeck.widget-1:pad',
name: 'Widget 1',
url: 'http://1.widget.example',
...patch,
},
]);
default:
return defaultValue;
}
});

expect(getExtraWidgetsWidgets('@user-id')).toEqual([]);
});
});
Loading

0 comments on commit fb0697b

Please sign in to comment.