diff --git a/config.json b/config.json index 9b95c355..de6015a1 100644 --- a/config.json +++ b/config.json @@ -1,21 +1,21 @@ { - "defaultHomeserver": 1, + "defaultHomeserver": 2, "homeserverList": [ "converser.eu", + "envs.net", "matrix.org", + "monero.social", "mozilla.org", - "unredacted.org", "xmr.se" ], "allowCustomHomeservers": true, - "elementCallUrl": null, "featuredCommunities": { "openAsDefault": false, "spaces": [ "#cinny-space:matrix.org", "#community:matrix.org", - "#space:unredacted.org", + "#space:envs.net", "#science-space:matrix.org", "#libregaming-games:tchncs.de", "#mathematics-on:matrix.org" @@ -28,7 +28,7 @@ "#PrivSec.dev:arcticfoxes.net", "#disroot:aria-net.org" ], - "servers": [ "matrix.org", "mozilla.org", "unredacted.org" ] + "servers": ["envs.net", "matrix.org", "monero.social", "mozilla.org"] }, "hashRouter": { diff --git a/docker-nginx.conf b/docker-nginx.conf index aea395ae..a2dbeba0 100644 --- a/docker-nginx.conf +++ b/docker-nginx.conf @@ -14,8 +14,6 @@ server { rewrite ^/public/(.*)$ /public/$1 break; rewrite ^/assets/(.*)$ /assets/$1 break; - rewrite ^/element-call/dist/(.*)$ /element-call/dist/$1 break; - rewrite ^(.+)$ /index.html break; } } diff --git a/netlify.toml b/netlify.toml index a8710303..ff4a38e1 100644 --- a/netlify.toml +++ b/netlify.toml @@ -29,6 +29,11 @@ to = "/assets/:splat" status = 200 +[[redirects]] + from = "/widgets/*" + to = "/widgets/:splat" + status = 200 + [[redirects]] from = "/*" to = "/index.html" diff --git a/package-lock.json b/package-lock.json index 48508a28..1d45ddde 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,8 +12,8 @@ "@atlaskit/pragmatic-drag-and-drop": "1.1.6", "@atlaskit/pragmatic-drag-and-drop-auto-scroll": "1.3.0", "@atlaskit/pragmatic-drag-and-drop-hitbox": "1.0.3", + "@element-hq/element-call-embedded": "0.16.0", "@fontsource/inter": "4.5.14", - "@matrix-org/react-sdk-module-api": "2.5.0", "@tanstack/react-query": "5.24.1", "@tanstack/react-query-devtools": "5.24.1", "@tanstack/react-virtual": "3.2.0", @@ -33,7 +33,7 @@ "emojibase-data": "15.3.2", "file-saver": "2.0.5", "focus-trap-react": "10.0.2", - "folds": "2.5.0", + "folds": "2.4.0", "html-dom-parser": "4.0.0", "html-react-parser": "4.2.0", "i18next": "23.12.2", @@ -45,7 +45,7 @@ "linkify-react": "4.1.3", "linkifyjs": "4.1.3", "matrix-js-sdk": "38.2.0", - "matrix-widget-api": "1.11.0", + "matrix-widget-api": "1.13.1", "millify": "6.1.0", "pdfjs-dist": "4.2.67", "prismjs": "1.30.0", @@ -68,7 +68,6 @@ "zod": "4.1.8" }, "devDependencies": { - "@element-hq/element-call-embedded": "0.16.3", "@esbuild-plugins/node-globals-polyfill": "0.2.3", "@rollup/plugin-inject": "5.0.3", "@rollup/plugin-wasm": "6.1.1", @@ -1654,10 +1653,9 @@ } }, "node_modules/@element-hq/element-call-embedded": { - "version": "0.16.3", - "resolved": "https://registry.npmjs.org/@element-hq/element-call-embedded/-/element-call-embedded-0.16.3.tgz", - "integrity": "sha512-OViKJonDaDNVBUW9WdV9mk78/Ruh34C7XsEgt3O8D9z+64C39elbIgllHSoH5S12IRlv9RYrrV37FZLo6QWsDQ==", - "dev": true + "version": "0.16.0", + "resolved": "https://registry.npmjs.org/@element-hq/element-call-embedded/-/element-call-embedded-0.16.0.tgz", + "integrity": "sha512-cTJwW9cmQHrTUvcJm0xv9uSTMYiToDEaw5AuxXQS9jVjHQT8B3W3DWtKYAzq1PRRH13JZkcwXbHjdFpkxzhzCQ==" }, "node_modules/@emotion/hash": { "version": "0.9.2", @@ -2274,18 +2272,6 @@ "node": ">= 18" } }, - "node_modules/@matrix-org/react-sdk-module-api": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@matrix-org/react-sdk-module-api/-/react-sdk-module-api-2.5.0.tgz", - "integrity": "sha512-l/SmiO47gPIRd6YJJGj+B6qbxyypJF6SEsfYr7j9rSW6E85ZYCqf+TpMM2LmfwZRADyKfCVkaJbbBZYpoD02VA==", - "license": "Apache-2.0", - "dependencies": { - "@babel/runtime": "^7.17.9" - }, - "peerDependencies": { - "react": "^18" - } - }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -7179,9 +7165,9 @@ } }, "node_modules/folds": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/folds/-/folds-2.5.0.tgz", - "integrity": "sha512-UJhvXAQ1XnZ9w10KJwSW+frvzzWE/zcF0dH3fDVCD70RFHAxwEi0UkkVS8CaZGxZF2Wvt3qTJyTS5LW3LwwUAw==", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/folds/-/folds-2.4.0.tgz", + "integrity": "sha512-Q5xCmvU3SIM8etQ9qLF6Y5Jtv01c9JpG3QcnF+Z3nlbMvtktfE13Pj7p0XgSPBcA3OuoU0zXiRwiTlMcbU7KhA==", "license": "Apache-2.0", "peerDependencies": { "@vanilla-extract/css": "1.9.2", @@ -8685,9 +8671,9 @@ } }, "node_modules/matrix-widget-api": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/matrix-widget-api/-/matrix-widget-api-1.11.0.tgz", - "integrity": "sha512-ED/9hrJqDWVLeED0g1uJnYRhINh3ZTquwurdM+Hc8wLVJIQ8G/r7A7z74NC+8bBIHQ1Jo7i1Uq5CoJp/TzFYrA==", + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/matrix-widget-api/-/matrix-widget-api-1.13.1.tgz", + "integrity": "sha512-mkOHUVzaN018TCbObfGOSaMW2GoUxOfcxNNlTVx5/HeMk3OSQPQM0C9oEME5Liiv/dBUoSrEB64V8wF7e/gb1w==", "license": "Apache-2.0", "dependencies": { "@types/events": "^3.0.0", @@ -10926,7 +10912,6 @@ "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.4.tgz", "integrity": "sha512-Uz+dTXYzxXXbsFpM86Wh3dKCxrQqUcVMxwU54orwlJjOpO3ao8L7j5lH+dWfTwgCwIuM9GQ2kvVotzYJMXTBZg==", "dev": true, - "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" diff --git a/package.json b/package.json index bcf30c93..1c6d6ea6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cinny", - "version": "4.10.2", + "version": "4.10.2-minty", "description": "Yet another matrix client", "main": "index.js", "type": "module", @@ -10,7 +10,6 @@ "scripts": { "start": "vite", "build": "vite build", - "preview": "vite preview", "lint": "yarn check:eslint && yarn check:prettier", "check:eslint": "eslint src/*", "check:prettier": "prettier --check .", @@ -24,8 +23,8 @@ "@atlaskit/pragmatic-drag-and-drop": "1.1.6", "@atlaskit/pragmatic-drag-and-drop-auto-scroll": "1.3.0", "@atlaskit/pragmatic-drag-and-drop-hitbox": "1.0.3", + "@element-hq/element-call-embedded": "0.16.0", "@fontsource/inter": "4.5.14", - "@matrix-org/react-sdk-module-api": "2.5.0", "@tanstack/react-query": "5.24.1", "@tanstack/react-query-devtools": "5.24.1", "@tanstack/react-virtual": "3.2.0", @@ -45,7 +44,7 @@ "emojibase-data": "15.3.2", "file-saver": "2.0.5", "focus-trap-react": "10.0.2", - "folds": "2.5.0", + "folds": "2.4.0", "html-dom-parser": "4.0.0", "html-react-parser": "4.2.0", "i18next": "23.12.2", @@ -56,8 +55,8 @@ "jotai": "2.6.0", "linkify-react": "4.1.3", "linkifyjs": "4.1.3", - "matrix-widget-api": "1.11.0", "matrix-js-sdk": "38.2.0", + "matrix-widget-api": "1.13.1", "millify": "6.1.0", "pdfjs-dist": "4.2.67", "prismjs": "1.30.0", @@ -80,7 +79,6 @@ "zod": "4.1.8" }, "devDependencies": { - "@element-hq/element-call-embedded": "0.16.3", "@esbuild-plugins/node-globals-polyfill": "0.2.3", "@rollup/plugin-inject": "5.0.3", "@rollup/plugin-wasm": "6.1.1", @@ -112,4 +110,4 @@ "vite-plugin-static-copy": "1.0.4", "vite-plugin-top-level-await": "1.4.4" } -} \ No newline at end of file +} diff --git a/src/app/components/create-room/utils.ts b/src/app/components/create-room/utils.ts index f10adf21..a0ca7488 100644 --- a/src/app/components/create-room/utils.ts +++ b/src/app/components/create-room/utils.ts @@ -11,7 +11,6 @@ import { CreateRoomKind } from './CreateRoomKindSelector'; import { RoomType, StateEvent } from '../../../types/matrix/room'; import { getViaServers } from '../../plugins/via-servers'; import { getMxIdServer } from '../../utils/matrix'; -import { IPowerLevels } from '../../hooks/usePowerLevels'; export const createRoomCreationContent = ( type: RoomType | undefined, @@ -83,44 +82,6 @@ export const createRoomEncryptionState = () => ({ }, }); -export const createRoomCallState = () => ({ - type: 'org.matrix.msc3401.call', - state_key: '', - content: {}, -}); - -export const createPowerLevelContentOverrides = ( - base: IPowerLevels, - overrides: Partial -): IPowerLevels => ({ - ...base, - ...overrides, - ...(base.events || overrides.events - ? { - events: { - ...base.events, - ...overrides.events, - }, - } - : {}), - ...(base.users || overrides.users - ? { - users: { - ...base.users, - ...overrides.users, - }, - } - : {}), - ...(base.notifications || overrides.notifications - ? { - notifications: { - ...base.notifications, - ...overrides.notifications, - }, - } - : {}), -}); - export type CreateRoomData = { version: string; type?: RoomType; @@ -133,7 +94,6 @@ export type CreateRoomData = { knock: boolean; allowFederation: boolean; additionalCreators?: string[]; - powerLevelContentOverrides?: IPowerLevels; }; export const createRoom = async (mx: MatrixClient, data: CreateRoomData): Promise => { const initialState: ICreateRoomStateEvent[] = []; @@ -146,10 +106,6 @@ export const createRoom = async (mx: MatrixClient, data: CreateRoomData): Promis initialState.push(createRoomParentState(data.parent)); } - if (data.type === RoomType.Call) { - initialState.push(createRoomCallState()); - } - initialState.push(createRoomJoinRulesState(data.kind, data.parent, data.knock)); const options: ICreateRoomOpts = { @@ -180,15 +136,5 @@ export const createRoom = async (mx: MatrixClient, data: CreateRoomData): Promis ); } - if (data.powerLevelContentOverrides) { - const roomPowers = await mx.getStateEvent(result.room_id, StateEvent.RoomPowerLevels, ''); - const updatedPowers = createPowerLevelContentOverrides( - roomPowers, - data.powerLevelContentOverrides - ); - - await mx.sendStateEvent(result.room_id, StateEvent.RoomPowerLevels as any, updatedPowers, ''); - } - return result.room_id; }; diff --git a/src/app/components/editor/autocomplete/RoomMentionAutocomplete.tsx b/src/app/components/editor/autocomplete/RoomMentionAutocomplete.tsx index a9e7f51f..b0c64f60 100644 --- a/src/app/components/editor/autocomplete/RoomMentionAutocomplete.tsx +++ b/src/app/components/editor/autocomplete/RoomMentionAutocomplete.tsx @@ -174,7 +174,7 @@ export function RoomMentionAutocomplete({ )} /> ) : ( - + )} } diff --git a/src/app/components/element-call/CallView.tsx b/src/app/components/element-call/CallView.tsx new file mode 100644 index 00000000..7aff6f49 --- /dev/null +++ b/src/app/components/element-call/CallView.tsx @@ -0,0 +1,151 @@ +import React, { useEffect, useRef, useState } from 'react'; +import { ClientWidgetApi } from 'matrix-widget-api'; +import { Button, Text } from 'folds'; +import ElementCall from './ElementCall'; +import { useMatrixClient } from '../../hooks/useMatrixClient'; +import { useEventEmitter } from './utils'; +import { useIsDirectRoom, useRoom } from '../../hooks/useRoom'; +import { useCallOngoing } from '../../hooks/useCallOngoing'; + +export enum CallWidgetActions { + // All of these actions are currently specific to Jitsi and Element Call + JoinCall = 'io.element.join', + HangupCall = 'im.vector.hangup', + Close = 'io.element.close', +} +export function action(type: CallWidgetActions): string { + return `action:${type}`; +} + +const iframeFeatures = + 'microphone; camera; encrypted-media; autoplay; display-capture; clipboard-write; ' + + 'clipboard-read;'; +const sandboxFlags = + 'allow-forms allow-popups allow-popups-to-escape-sandbox ' + + 'allow-same-origin allow-scripts allow-presentation allow-downloads'; +const iframeStyles = { flex: '1 1', border: 'none' }; +const containerStyle = (hidden: boolean): React.CSSProperties => ({ + display: 'flex', + flexDirection: 'column', + position: 'relative', + height: '100%', + width: '100%', + ...(hidden + ? { + overflow: 'hidden', + width: 0, + height: 0, + } + : {}), +}); +const closeButtonStyle: React.CSSProperties = { + position: 'absolute', + top: 8, + right: 8, + zIndex: 100, + maxWidth: 'fit-content', +}; + +enum State { + Preparing = 'preparing', + Lobby = 'lobby', + Joined = 'joined', + HungUp = 'hung_up', + CanClose = 'can_close', +} + +/** + * Shows a call for this room. Rendering this component will + * automatically create a call widget and join the call in the room. + * @returns + */ +export interface IRoomCallViewProps { + onClose?: () => void; + onJoin?: () => void; + onHangup?: (errorMessage?: string) => void; +} + +export function CallView({ + onJoin = undefined, + onClose = undefined, + onHangup = undefined, +}: IRoomCallViewProps): JSX.Element { + // Construct required variables + const room = useRoom(); + const client = useMatrixClient(); + const iframe = useRef(null); + + // Model state + const [elementCall, setElementCall] = useState(); + const [widgetApi, setWidgetApi] = useState(null); + const [state, setState] = useState(State.Preparing); + + // Initialization parameters + const isDirect = useIsDirectRoom(); + const callOngoing = useCallOngoing(room); + const initialCallOngoing = React.useRef(callOngoing); + const initialIsDirect = React.useRef(isDirect); + useEffect(() => { + if (client && room && !elementCall) { + const e = new ElementCall(client, room, initialIsDirect.current, initialCallOngoing.current); + setElementCall(e); + } + }, [client, room, setElementCall, elementCall]); + + // Start the messaging over the widget api. + useEffect(() => { + if (iframe.current && elementCall) { + elementCall.startMessaging(iframe.current); + } + return () => { + elementCall?.stopMessaging(); + }; + }, [iframe, elementCall]); + + // Widget api ready + useEventEmitter(elementCall, 'ready', () => { + setWidgetApi(elementCall?.widgetApi ?? null); + setState(State.Lobby); + }); + + // Use widget api to listen for hangup/join/close actions + useEventEmitter(widgetApi, action(CallWidgetActions.HangupCall), () => { + setState(State.HungUp); + onHangup?.(); + }); + useEventEmitter(widgetApi, action(CallWidgetActions.JoinCall), () => { + setState(State.Joined); + onJoin?.(); + }); + useEventEmitter(widgetApi, action(CallWidgetActions.Close), () => { + setState(State.CanClose); + onClose?.(); + }); + + // render component + return ( +
+ {/* Exit button for lobby state */} + {state === State.Lobby && ( + + )} +