@live-change/video-call-frontend 0.8.37

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,133 @@
1
+ function allocateSpace(left, top, width, height, tiles, styles) {
2
+ console.log("ALLOCATE SPACE FOR TILES", tiles)
3
+ if(!width || !height) return
4
+ let visibleTiles = []
5
+ let allRatioSum = 0
6
+ for(let tile of tiles) {
7
+ const size = tile.size
8
+ if(!size || !size.width || !size.height) {
9
+ styles[tile.id] = { display: 'none' }
10
+ continue
11
+ }
12
+ const { width, height } = size
13
+ allRatioSum += width / height
14
+ visibleTiles.push({ id: tile.id, width, height })
15
+ }
16
+ /// check all options, find best fill
17
+ console.log("SEARCH FOR BEST FILL FOR AREA", width, height, "FOR TILES", visibleTiles)
18
+ if(visibleTiles.length === 0) return
19
+ const aspectRatio = width / height
20
+ const availableArea = width * height
21
+ let bestFill = { area: 0 }
22
+ for(let rows = 1; rows <= tiles.length; rows++) {
23
+ const breakingPoint = (allRatioSum / rows)
24
+ let ratioSum = 0
25
+ let maxRatioSum = 0
26
+ let row = 0
27
+ for(const tile of visibleTiles) { // find maximal aspect ratio sum (maximal row width)
28
+ const canBreak = (row < rows - 1)
29
+ const ratio = tile.width / tile.height
30
+ if(ratioSum + ratio * 0.5 > breakingPoint && canBreak) { // break!
31
+ //console.log("BREAK ROW BEFORE SUM!")
32
+ row += 1
33
+ if(ratioSum > maxRatioSum) maxRatioSum = ratioSum
34
+ ratioSum = 0
35
+ }
36
+ ratioSum += ratio
37
+ //console.log("RATIO SUM", ratioSum, "ROW", row)
38
+ if(ratioSum > breakingPoint && canBreak) { // break!
39
+ //console.log("BREAK ROW AFTER SUM!")
40
+ row += 1
41
+ if(ratioSum > maxRatioSum) maxRatioSum = ratioSum
42
+ ratioSum = 0
43
+ }
44
+ }
45
+ if(ratioSum > maxRatioSum) maxRatioSum = ratioSum
46
+ // compute area and fill:
47
+ const ratio = maxRatioSum / rows
48
+ let filledWidth, filledHeight
49
+ if(ratio > aspectRatio) { // empty bottom and top
50
+ filledWidth = width
51
+ filledHeight = filledWidth / ratio
52
+ } else { // empty left and right
53
+ filledHeight = height
54
+ filledWidth = filledHeight * ratio
55
+ }
56
+ const filledArea = filledWidth * filledHeight
57
+ if(filledArea > bestFill.area) {
58
+ bestFill = { rows, area: filledArea, filledWidth, filledHeight }
59
+ } else {
60
+ console.log("ROWS", rows, "RATIO", ratio, "FILL", filledArea / availableArea)
61
+ console.log("TILES RATIO SUM", maxRatioSum, "ASPECT RATIO", aspectRatio)
62
+ }
63
+ }
64
+ //console.log("BEST FILL", bestFill)
65
+
66
+ if(!bestFill.rows) throw new Error("Couldn't find best fill!?!")
67
+
68
+ /// we determined rows count, time to place tiles
69
+ let ratioSum = 0
70
+ let row = 0
71
+ let rowTiles = []
72
+ let leftSum = 0
73
+ const { rows, filledWidth, filledHeight } = bestFill
74
+ const leftMargin = (width - filledWidth) / 2
75
+ const topMargin = (height - filledHeight) / 2
76
+ const rowHeight = filledHeight / rows
77
+ const breakingPoint = (allRatioSum / rows)
78
+
79
+ //console.log("rowHeight", rowHeight)
80
+
81
+ const placeTilesInRow = (rowLeftMargin, rowTiles) => {
82
+ for(const tile of rowTiles) {
83
+ const { left, width } = tile
84
+ //console.log("PLACE TILE", tile)
85
+ styles[tile.id] = {
86
+ width: width + 'px',
87
+ height: rowHeight + 'px',
88
+ left: (left + leftMargin + rowLeftMargin) + 'px',
89
+ top: (top + topMargin + rowHeight * row) + 'px'
90
+ }
91
+ //console.log("TILE STYLE", tile.id, styles[tile.id])
92
+ }
93
+ }
94
+
95
+ for(const tile of visibleTiles) {
96
+ const canBreak = (row < rows - 1)
97
+ const ratio = tile.width / tile.height
98
+ if(ratioSum + ratio * 0.5 > breakingPoint && canBreak) { // break!
99
+ //console.log("BREAK ROW BEFORE SUM!")
100
+ const rowLeftMargin = (filledWidth - leftSum) / 2
101
+ //console.log("ROW LEFT MARGIN", rowLeftMargin)
102
+ placeTilesInRow(rowLeftMargin, rowTiles)
103
+ row += 1
104
+ leftSum = 0
105
+ ratioSum = 0
106
+ rowTiles = []
107
+ }
108
+ ratioSum += ratio
109
+ const width = rowHeight * ratio
110
+ rowTiles.push({ id: tile.id, left: leftSum, width })
111
+ leftSum += width
112
+ //console.log("RATIO SUM", ratioSum, "ROW", row)
113
+ if(ratioSum > breakingPoint && canBreak) { // break!
114
+ //console.log("BREAK ROW AFTER SUM!")
115
+ const rowLeftMargin = (filledWidth - leftSum) / 2
116
+ //console.log("ROW LEFT MARGIN", rowLeftMargin)
117
+ placeTilesInRow(rowLeftMargin, rowTiles)
118
+ row += 1
119
+ leftSum = 0
120
+ ratioSum = 0
121
+ rowTiles = []
122
+ }
123
+ if(ratioSum >= (allRatioSum / rows) * (row + 1) && row < rows - 1) {
124
+ // break!
125
+ }
126
+ }
127
+ if(rowTiles.length > 0) {
128
+ const rowLeftMargin = (filledWidth - leftSum) / 2
129
+ placeTilesInRow(rowLeftMargin, rowTiles)
130
+ }
131
+ }
132
+
133
+ export default allocateSpace
@@ -0,0 +1,48 @@
1
+ export default {
2
+ mainVideos: [
3
+ {
4
+ id: '1',
5
+ type: 'image',
6
+ src: '/video-test/1280x960.png',
7
+ },
8
+ {
9
+ id: '2',
10
+ type: 'image',
11
+ src: '/video-test/2048x1080.png',
12
+ },
13
+ {
14
+ id: '3',
15
+ type: 'image',
16
+ src: '/video-test/1280x720.png',
17
+ },
18
+ {
19
+ id: '3b',
20
+ type: 'image',
21
+ src: '/video-test/1280x720.png',
22
+ }
23
+ ],
24
+ otherVideos: [
25
+ {
26
+ id: '4',
27
+ type: 'image',
28
+ src: '/video-test/1280x960.png',
29
+ },
30
+ {
31
+ id: '5',
32
+ type: 'image',
33
+ src: '/video-test/1280x720.png',
34
+ },
35
+ {
36
+ id: '6',
37
+ type: 'image',
38
+ src: '/video-test/2048x1080.png',
39
+ }
40
+ ],
41
+ myVideos: [
42
+ {
43
+ id: 'me',
44
+ type: 'image',
45
+ src: '/video-test/2048x1080.png',
46
+ }
47
+ ],
48
+ }
@@ -0,0 +1,75 @@
1
+ import {
2
+ createMemoryHistory,
3
+ createRouter as _createRouter,
4
+ createWebHistory
5
+ } from 'vue-router'
6
+
7
+ import {
8
+ installRouterAnalytics
9
+ } from '@live-change/vue3-components'
10
+
11
+ import { dbAdminRoutes } from "@live-change/db-admin"
12
+ import { peerConnectionRoutes } from "@live-change/peer-connection-frontend"
13
+ import { userRoutes } from '@live-change/user-frontend'
14
+
15
+ export function videoCallRoutes(config = {}) {
16
+ const { prefix = '/', route = (r) => r } = config
17
+ return [
18
+ route({
19
+ name: 'video-call:room', path: prefix+ '/room/:room', meta: { },
20
+ component: () => import("./room/Room.vue"),
21
+ }),
22
+
23
+ ]
24
+ }
25
+
26
+ export async function sitemap(route, api) {
27
+
28
+ }
29
+
30
+ import { client as useClient } from '@live-change/vue3-ssr'
31
+
32
+ export function createRouter(app, config) {
33
+ const client = useClient(app._context)
34
+
35
+ const router = _createRouter({
36
+ // use appropriate history implementation for server/client
37
+ // import.meta.env.SSR is injected by Vite.
38
+ history: import.meta.env.SSR ? createMemoryHistory() : createWebHistory(),
39
+ routes: [
40
+ ...userRoutes({ ...config, prefix: '/user/' }),
41
+ ...peerConnectionRoutes({ ...config, prefix: '/peer-connection/' }),
42
+ ...dbAdminRoutes({ prefix: '/_db', route: r => ({ ...r, meta: { ...r.meta, raw: true }}) }),
43
+ ...videoCallRoutes(config),
44
+ {
45
+ name: 'video-call:test-room', path: '/', meta: { },
46
+ component: () => import("./room/Room.vue"),
47
+ props: {
48
+ room: '[test-room]'
49
+ }
50
+ }
51
+ ]
52
+ })
53
+ installRouterAnalytics(router)
54
+ router.beforeEach(async (to, from) => {
55
+ if(to?.matched.find(m => m?.meta.signedIn)) {
56
+ if(!client.value.user) {
57
+ console.log("REDIRECT TO LOGIN BECAUSE PAGE REQUIRES LOGIN!")
58
+ router.redirectAfterSignIn = to.fullPath
59
+ return { name: 'user:signInEmail' }
60
+ }
61
+ }
62
+ if(to?.matched.find(m => m?.meta.signedOut)) {
63
+ if(client.value.user) {
64
+ console.log("REDIRECT TO USER INDEX BECAUSE PAGE REQUIRES LOGOUT!")
65
+ return { name: 'user:settings' }
66
+ }
67
+ }
68
+ if(to && to.name === 'user:signInEmail' && from?.matched.find(m => m?.meta.saveForSignIn)) {
69
+ console.log("SAVE FOR LOGIN", from.fullPath)
70
+ localStorage.redirectAfterLogin = from.fullPath
71
+ }
72
+ })
73
+ return router
74
+ }
75
+
@@ -0,0 +1,26 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2020",
4
+ "useDefineForClassFields": true,
5
+ "module": "ESNext",
6
+ "lib": ["ES2020", "DOM", "DOM.Iterable"],
7
+ "skipLibCheck": true,
8
+
9
+ /* Bundler mode */
10
+ "moduleResolution": "bundler",
11
+ "allowImportingTsExtensions": true,
12
+ "resolveJsonModule": true,
13
+ "isolatedModules": true,
14
+ "noEmit": true,
15
+ "jsx": "preserve",
16
+
17
+ /* Linting */
18
+ "strict": true,
19
+ "noUnusedLocals": true,
20
+ "noUnusedParameters": true,
21
+ "noFallthroughCasesInSwitch": true
22
+ },
23
+ "include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.vue"],
24
+ "references": [{ "path": "./tsconfig.node.json" }]
25
+ }
26
+
@@ -0,0 +1,11 @@
1
+ {
2
+ "compilerOptions": {
3
+ "composite": true,
4
+ "skipLibCheck": true,
5
+ "module": "ESNext",
6
+ "moduleResolution": "bundler",
7
+ "allowSyntheticDefaultImports": true,
8
+ "strict": true
9
+ },
10
+ "include": ["vite.config.ts"]
11
+ }
@@ -0,0 +1,53 @@
1
+ import { defineConfig } from 'vite'
2
+ import Pages from 'vite-plugin-pages'
3
+
4
+ import { fileURLToPath } from 'url'
5
+ import { dirname, join } from 'path'
6
+ import { accessSync, readFileSync } from 'fs'
7
+ const currentModuleDir = dirname(fileURLToPath(import.meta.url))
8
+ const packageJsonPath = dirname(fileURLToPath(import.meta.url))
9
+ .split('/').map((part, i, arr) =>
10
+ join(arr.slice(0, arr.length - i).join('/'), 'package.json')
11
+ ).find(p => { try { accessSync(p); return true } catch(e) { return false }})
12
+ const packageJson = packageJsonPath ? JSON.parse(readFileSync(packageJsonPath, 'utf-8')) : {}
13
+ const name = packageJson.name ?? "Example"
14
+ const version = packageJson.version ?? process.env.VERSION ?? 'unknown'
15
+ const homepage = process.env.BASE_HREF ?? packageJson.homepage
16
+ const domain = (homepage && homepage.match(/https\:\/\/([^\/]+)/)?.[1]) || 'example.com'
17
+
18
+ // @ts-ignore
19
+ import baseViteConfig from '@live-change/frontend-base/vite-config.js'
20
+
21
+ export default defineConfig(async ({ command, mode }) => {
22
+ const baseConfig = (await baseViteConfig({ command, mode }))
23
+ return {
24
+ ...baseConfig,
25
+
26
+ define: {
27
+ ...baseConfig.define,
28
+ ENV_VERSION: JSON.stringify(version),
29
+ ENV_BRAND_NAME: JSON.stringify(name[0].toUpperCase() + name.slice(1)),
30
+ ENV_BRAND_DOMAIN: JSON.stringify(domain),
31
+ },
32
+
33
+ plugins: [
34
+ ...baseConfig.plugins,
35
+ Pages({
36
+ dirs: [
37
+ // basic
38
+ { dir: 'src/pages', baseRoute: '' },
39
+ // blog
40
+ // { dir: 'src/blog', baseRoute: 'blog' },
41
+ ],
42
+ extensions: ['vue', 'md'],
43
+ }),
44
+ ],
45
+
46
+ resolve: {
47
+ ...baseConfig.resolve,
48
+ alias: [
49
+ ...baseConfig.resolve.alias,
50
+ ]
51
+ }
52
+ }
53
+ })
package/index.js ADDED
@@ -0,0 +1,2 @@
1
+
2
+ export { }
package/package.json ADDED
@@ -0,0 +1,108 @@
1
+ {
2
+ "name": "@live-change/video-call-frontend",
3
+ "version": "0.8.37",
4
+ "scripts": {
5
+ "memDev": "node server/start.js memDev --enableSessions --initScript ./init.js --dbAccess",
6
+ "localDevInit": "rm tmp.db; node server/start.js localDev --enableSessions --initScript ./init.js",
7
+ "localDev": "node server/start.js localDev --enableSessions --dbAccess",
8
+ "dev": "node server/start.js dev --enableSessions",
9
+ "ssrDev": "node server/start.js ssrDev --enableSessions",
10
+ "serveAllMem": "cross-env NODE_ENV=production node server/start.js ssrServer --withApi --withServices --updateServices --enableSessions --withDb --dbBackend mem --createDb",
11
+ "serveAll": "cross-env NODE_ENV=production node server/start.js ssrServer --withApi --withServices --updateServices --enableSessions",
12
+ "serve": "cross-env NODE_ENV=production node server/start.js ssrServer --enableSessions",
13
+ "memDev:spa": "node server/start.js memDev --enableSessions --initScript ./init.js --dbAccess --spa",
14
+ "localDevInit:spa": "rm tmp.db; node server/start.js localDev --enableSessions --initScript ./init.js --spa",
15
+ "localDev:spa": "node server/start.js localDev --enableSessions --spa",
16
+ "dev:spa": "node server/start.js dev --enableSessions --spa",
17
+ "ssrDev:spa": "node server/start.js ssrDev --enableSessions --spa",
18
+ "serveAllMem:spa": "cross-env NODE_ENV=production node server/start.js server --withApi --withServices --updateServices --enableSessions --withDb --dbBackend mem --createDb --spa",
19
+ "serveAll:spa": "cross-env NODE_ENV=production node server/start.js server --withApi --withServices --updateServices --enableSessions --spa",
20
+ "serve:spa": "cross-env NODE_ENV=production node server/start.js server --enableSessions --spa",
21
+ "apiServer": "node server/start.js apiServer --enableSessions",
22
+ "devApiServer": "node server/start.js devApiServer --enableSessions",
23
+ "memApiServer": "node server/start.js memApiServer --enableSessions",
24
+ "build": "cd front; yarn build:client && yarn build:server",
25
+ "build:client": "cd front; dotenvx run -f ../.env -- vite build --ssrManifest --outDir dist/client",
26
+ "build:server": "cd front; dotenvx run -f ../.env -- vite build --ssr src/entry-server.js --outDir dist/server",
27
+ "build:spa": "cd front; dotenvx run -f ../.env -- vite build --outDir dist/spa",
28
+ "generate": "vite build --ssrManifest --outDir dist/static && yarn build:server && node prerender",
29
+ "debug": "node --inspect-brk server",
30
+ "describe": "node server/start.js describe",
31
+ "changes": "node server/start.js changes"
32
+ },
33
+ "type": "module",
34
+ "dependencies": {
35
+ "@codemirror/language": "6.10.1",
36
+ "@dotenvx/dotenvx": "0.27.0",
37
+ "@fortawesome/fontawesome-free": "^6.5.2",
38
+ "@live-change/access-control-frontend": "^0.8.37",
39
+ "@live-change/access-control-service": "^0.8.37",
40
+ "@live-change/backup-service": "^0.8.37",
41
+ "@live-change/blog-frontend": "^0.8.37",
42
+ "@live-change/blog-service": "^0.8.37",
43
+ "@live-change/cli": "^0.8.37",
44
+ "@live-change/content-frontend": "^0.8.37",
45
+ "@live-change/content-service": "^0.8.37",
46
+ "@live-change/dao": "^0.8.37",
47
+ "@live-change/dao-vue3": "^0.8.37",
48
+ "@live-change/dao-websocket": "^0.8.37",
49
+ "@live-change/db-client": "^0.8.37",
50
+ "@live-change/email-service": "^0.8.37",
51
+ "@live-change/framework": "^0.8.37",
52
+ "@live-change/frontend-auto-form": "^0.8.37",
53
+ "@live-change/frontend-base": "^0.8.37",
54
+ "@live-change/geoip-service": "^0.8.37",
55
+ "@live-change/image-frontend": "^0.8.37",
56
+ "@live-change/locale-settings-service": "^0.8.37",
57
+ "@live-change/password-authentication-service": "^0.8.37",
58
+ "@live-change/peer-connection-service": "^0.8.37",
59
+ "@live-change/prosemirror-service": "^0.8.37",
60
+ "@live-change/secret-code-service": "^0.8.37",
61
+ "@live-change/secret-link-service": "^0.8.37",
62
+ "@live-change/session-service": "^0.8.37",
63
+ "@live-change/task-service": "^0.8.37",
64
+ "@live-change/upload-frontend": "^0.8.37",
65
+ "@live-change/url-frontend": "^0.8.37",
66
+ "@live-change/url-service": "^0.8.37",
67
+ "@live-change/user-frontend": "^0.8.37",
68
+ "@live-change/user-identification-service": "^0.8.37",
69
+ "@live-change/user-service": "^0.8.37",
70
+ "@live-change/vote-service": "^0.8.37",
71
+ "@live-change/vue3-components": "^0.8.37",
72
+ "@live-change/vue3-ssr": "^0.8.37",
73
+ "@vueuse/core": "^10.11.0",
74
+ "boxicons": "^2.1.4",
75
+ "codeceptjs-assert": "^0.0.5",
76
+ "compression": "^1.7.4",
77
+ "cross-env": "^7.0.3",
78
+ "feather-icons": "^4.29.2",
79
+ "get-port-sync": "1.0.1",
80
+ "pica": "^9.0.1",
81
+ "pretty-bytes": "^6.1.1",
82
+ "primeflex": "^3.3.1",
83
+ "primeicons": "^7.0.0",
84
+ "primevue": "^3.52.0",
85
+ "rollup-plugin-node-builtins": "^2.1.2",
86
+ "rollup-plugin-visualizer": "5.12.0",
87
+ "serialize-javascript": "^6.0.2",
88
+ "serve-static": "^1.15.0",
89
+ "v-shared-element": "3.1.1",
90
+ "vue": "^3.4.29",
91
+ "vue-i18n": "^9.10.1",
92
+ "vue-router": "^4.3.3",
93
+ "vue3-scroll-border": "0.1.6"
94
+ },
95
+ "devDependencies": {
96
+ "@live-change/codeceptjs-helper": "^0.8.37",
97
+ "codeceptjs": "^3.5.12",
98
+ "generate-password": "1.7.1",
99
+ "playwright": "^1.41.2",
100
+ "random-profile-generator": "^2.3.0",
101
+ "txtgen": "^3.0.6",
102
+ "webdriverio": "^8.31.1"
103
+ },
104
+ "author": "Michał Łaszczewski <michal@laszczewski.pl>",
105
+ "license": "ISC",
106
+ "description": "",
107
+ "gitHead": "8b97a83258a3eccd08534d0376015781c4eefdec"
108
+ }
@@ -0,0 +1,118 @@
1
+ import App from "@live-change/framework"
2
+ const app = App.app()
3
+
4
+ const contactTypes = ['email', 'phone']
5
+ const remoteAccountTypes = ['google']
6
+
7
+ import securityConfig from './security.config.js'
8
+
9
+ app.config = {
10
+ services: [
11
+ {
12
+ name: 'timer',
13
+ path: '@live-change/timer-service'
14
+ },
15
+ {
16
+ name: 'session',
17
+ path: '@live-change/session-service',
18
+ createSessionOnUpdate: true
19
+ },
20
+ {
21
+ name: 'user',
22
+ path: '@live-change/user-service',
23
+ remoteAccountTypes
24
+ },
25
+ {
26
+ name: 'email',
27
+ path: '@live-change/email-service'
28
+ },
29
+ {
30
+ name: 'phone',
31
+ path: '@live-change/phone-service'
32
+ },
33
+ {
34
+ name: 'secretLink',
35
+ path: '@live-change/secret-link-service'
36
+ },
37
+ {
38
+ name: 'secretCode',
39
+ path: '@live-change/secret-code-service'
40
+ },
41
+ {
42
+ name: 'messageAuthentication',
43
+ path: '@live-change/message-authentication-service',
44
+ contactTypes,
45
+ signUp: true,
46
+ signIn: true,
47
+ connect: true
48
+ },
49
+ {
50
+ name: 'passwordAuthentication',
51
+ path: '@live-change/password-authentication-service',
52
+ contactTypes,
53
+ signInWithoutPassword: true
54
+ },
55
+ {
56
+ name: 'googleAuthentication',
57
+ path: '@live-change/google-authentication-service',
58
+ },
59
+ {
60
+ name: 'security',
61
+ path: '@live-change/security-service',
62
+ ...securityConfig
63
+ },
64
+ {
65
+ name: 'userIdentification',
66
+ path: '@live-change/user-identification-service'
67
+ },
68
+ {
69
+ name: 'identicon',
70
+ path: '@live-change/identicon-service'
71
+ },
72
+ {
73
+ name: 'localeSettings'
74
+ },
75
+ {
76
+ name: 'notification',
77
+ path: '@live-change/notification-service',
78
+ contactTypes,
79
+ notificationTypes: ['example_TestNotification']
80
+ },
81
+ {
82
+ name: 'upload',
83
+ path: '@live-change/upload-service'
84
+ },
85
+ {
86
+ name: 'image',
87
+ path: '@live-change/image-service'
88
+ },
89
+ {
90
+ name: 'session',
91
+ path: '@live-change/session-service',
92
+ createSessionOnUpdate: true
93
+ },
94
+ {
95
+ name: 'online',
96
+ path: '@live-change/online-service',
97
+ createSessionOnUpdate: true
98
+ },
99
+ {
100
+ name: 'accessControl',
101
+ path: '@live-change/access-control-service',
102
+ createSessionOnUpdate: true,
103
+ contactTypes,
104
+ },
105
+ {
106
+ name: 'peerConnection',
107
+ path: '@live-change/peer-connection-service',
108
+ turn: {
109
+ urls: 'turn:turn.chaosu.pl:4433'
110
+ }
111
+ },
112
+ {
113
+ name: 'videoCall',
114
+ path: '@live-change/video-call-service'
115
+ }
116
+ ]
117
+ }
118
+ export default app.config
package/server/init.js ADDED
@@ -0,0 +1,23 @@
1
+ import App from '@live-change/framework'
2
+ const app = App.app()
3
+
4
+ export default async function(services) {
5
+
6
+ const { Room } = services.videoCall.models
7
+
8
+ await Room.create({
9
+ id: '[test-room]',
10
+ name: 'Test Room',
11
+ description: 'This is a test room',
12
+ })
13
+
14
+ const { PublicAccess, Access, AccessRequest, AccessInvitation } = services.accessControl.models
15
+
16
+ await PublicAccess.create({
17
+ id: App.encodeIdentifier(['videoCall_Room', '[test-room]']),
18
+ objectType: 'Room', object: '[test-room]',
19
+ userRoles: ['speaker'],
20
+ sessionRoles: ['speaker']
21
+ })
22
+
23
+ }