From 14a32c0fb392750895c92dc0d113003ba6c4d3e5 Mon Sep 17 00:00:00 2001 From: Timo K Date: Fri, 9 Jun 2023 19:18:30 +0200 Subject: [PATCH 01/59] connection lost banner if there is no connection to the home server Signed-off-by: Timo K --- public/locales/en-GB/app.json | 1 + src/App.tsx | 2 ++ src/ClientContext.tsx | 50 ++++++++++++++++++++++++++++--- src/DisconnectedBanner.module.css | 27 +++++++++++++++++ src/DisconnectedBanner.tsx | 50 +++++++++++++++++++++++++++++++ src/initializer.tsx | 7 +++++ src/matrix-utils.ts | 4 +-- 7 files changed, 135 insertions(+), 6 deletions(-) create mode 100644 src/DisconnectedBanner.module.css create mode 100644 src/DisconnectedBanner.tsx diff --git a/public/locales/en-GB/app.json b/public/locales/en-GB/app.json index ccd111b8..80f40d54 100644 --- a/public/locales/en-GB/app.json +++ b/public/locales/en-GB/app.json @@ -8,6 +8,7 @@ "{{name}} is talking…": "{{name}} is talking…", "{{names}}, {{name}}": "{{names}}, {{name}}", "{{roomName}} - Walkie-talkie call": "{{roomName}} - Walkie-talkie call", + "<0>{children}Connectivity to the server has been lost.": "<0>{children}Connectivity to the server has been lost.", "<0><1>You may withdraw consent by unchecking this box. If you are currently in a call, this setting will take effect at the end of the call.": "<0><1>You may withdraw consent by unchecking this box. If you are currently in a call, this setting will take effect at the end of the call.", "<0>Already have an account?<1><0>Log in Or <2>Access as a guest": "<0>Already have an account?<1><0>Log in Or <2>Access as a guest", "<0>Create an account Or <2>Access as a guest": "<0>Create an account Or <2>Access as a guest", diff --git a/src/App.tsx b/src/App.tsx index 2ae27b73..655e3789 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -29,6 +29,7 @@ import { usePageFocusStyle } from "./usePageFocusStyle"; import { SequenceDiagramViewerPage } from "./SequenceDiagramViewerPage"; import { InspectorContextProvider } from "./room/GroupCallInspector"; import { CrashView, LoadingView } from "./FullScreenView"; +import { DisconnectedBanner } from "./DisconnectedBanner"; import { Initializer } from "./initializer"; import { MediaHandlerProvider } from "./settings/useMediaHandler"; @@ -60,6 +61,7 @@ export default function App({ history }: AppProps) { + diff --git a/src/ClientContext.tsx b/src/ClientContext.tsx index 08593a4b..75c1bed3 100644 --- a/src/ClientContext.tsx +++ b/src/ClientContext.tsx @@ -25,9 +25,10 @@ import React, { useRef, } from "react"; import { useHistory } from "react-router-dom"; -import { MatrixClient } from "matrix-js-sdk/src/client"; +import { ClientEvent, MatrixClient } from "matrix-js-sdk/src/client"; import { logger } from "matrix-js-sdk/src/logger"; import { useTranslation } from "react-i18next"; +import { ISyncStateData, SyncState } from "matrix-js-sdk/src/sync"; import { ErrorView } from "./FullScreenView"; import { @@ -70,7 +71,8 @@ const loadSession = (): Session => { const saveSession = (session: Session) => localStorage.setItem("matrix-auth-store", JSON.stringify(session)); const clearSession = () => localStorage.removeItem("matrix-auth-store"); - +const isDisconnected = (syncState, syncData) => + syncState === "ERROR" && syncData?.error?.name === "ConnectionError"; interface ClientState { loading: boolean; isAuthenticated: boolean; @@ -81,6 +83,7 @@ interface ClientState { logout: () => void; setClient: (client: MatrixClient, session: Session) => void; error?: Error; + disconnected: boolean; } const ClientContext = createContext(null); @@ -98,7 +101,15 @@ export const ClientProvider: FC = ({ children }) => { const history = useHistory(); const initializing = useRef(false); const [ - { loading, isAuthenticated, isPasswordlessUser, client, userName, error }, + { + loading, + isAuthenticated, + isPasswordlessUser, + client, + userName, + error, + disconnected, + }, setState, ] = useState({ loading: true, @@ -107,8 +118,20 @@ export const ClientProvider: FC = ({ children }) => { client: undefined, userName: null, error: undefined, + disconnected: false, }); + const onSync = (state: SyncState, _old: SyncState, data: ISyncStateData) => { + setState((currentState) => { + logger.log("syntData.name", data?.error?.name, "state:", state); + console.log("Current state:", currentState); + return { + ...currentState, + disconnected: isDisconnected(state, data), + }; + }); + }; + useEffect(() => { // In case the component is mounted, unmounted, and remounted quickly (as // React does in strict mode), we need to make sure not to doubly initialize @@ -183,9 +206,10 @@ export const ClientProvider: FC = ({ children }) => { } } }; - + let clientWithListener; init() .then(({ client, isPasswordlessUser }) => { + clientWithListener = client; setState({ client, loading: false, @@ -193,7 +217,12 @@ export const ClientProvider: FC = ({ children }) => { isPasswordlessUser, userName: client?.getUserIdLocalpart(), error: undefined, + disconnected: isDisconnected( + client?.getSyncState, + client?.getSyncStateData + ), }); + clientWithListener?.on(ClientEvent.Sync, onSync); }) .catch((err) => { logger.error(err); @@ -204,9 +233,13 @@ export const ClientProvider: FC = ({ children }) => { isPasswordlessUser: false, userName: null, error: undefined, + disconnected: false, }); }) .finally(() => (initializing.current = false)); + return () => { + clientWithListener?.removeListener(ClientEvent.Sync, onSync); + }; }, []); const changePassword = useCallback( @@ -235,6 +268,7 @@ export const ClientProvider: FC = ({ children }) => { isPasswordlessUser: false, userName: client.getUserIdLocalpart(), error: undefined, + disconnected: false, }); }, [client] @@ -256,6 +290,10 @@ export const ClientProvider: FC = ({ children }) => { isPasswordlessUser: session.passwordlessUser, userName: newClient.getUserIdLocalpart(), error: undefined, + disconnected: isDisconnected( + newClient.getSyncState(), + newClient.getSyncStateData() + ), }); } else { clearSession(); @@ -267,6 +305,7 @@ export const ClientProvider: FC = ({ children }) => { isPasswordlessUser: false, userName: null, error: undefined, + disconnected: false, }); } }, @@ -284,6 +323,7 @@ export const ClientProvider: FC = ({ children }) => { isPasswordlessUser: true, userName: "", error: undefined, + disconnected: false, }); history.push("/"); PosthogAnalytics.instance.setRegistrationType(RegistrationType.Guest); @@ -326,6 +366,7 @@ export const ClientProvider: FC = ({ children }) => { userName, setClient, error: undefined, + disconnected, }), [ loading, @@ -336,6 +377,7 @@ export const ClientProvider: FC = ({ children }) => { logout, userName, setClient, + disconnected, ] ); diff --git a/src/DisconnectedBanner.module.css b/src/DisconnectedBanner.module.css new file mode 100644 index 00000000..aa6f6f5a --- /dev/null +++ b/src/DisconnectedBanner.module.css @@ -0,0 +1,27 @@ +/* +Copyright 2022 New Vector Ltd + +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. +*/ + +.banner { + position: absolute; + padding: 29px; + background-color: var(--quaternary-content); + vertical-align: middle; + font-size: var(--font-size-body); + text-align: center; + z-index: 1; + top: 76px; + width: 100%; +} diff --git a/src/DisconnectedBanner.tsx b/src/DisconnectedBanner.tsx new file mode 100644 index 00000000..84ecc432 --- /dev/null +++ b/src/DisconnectedBanner.tsx @@ -0,0 +1,50 @@ +/* +Copyright 2022 New Vector Ltd + +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 classNames from "classnames"; +import React, { HTMLAttributes, ReactNode } from "react"; +import { logger } from "@sentry/utils"; +import { Trans } from "react-i18next"; + +import styles from "./DisconnectedBanner.module.css"; +import { useClient } from "./ClientContext"; + +interface DisconnectedBannerProps extends HTMLAttributes { + children?: ReactNode; + className?: string; +} + +export function DisconnectedBanner({ + children, + className, + ...rest +}: DisconnectedBannerProps) { + const clientrp = useClient(); + logger.log(clientrp); + const disconnected = clientrp.disconnected; + return ( + <> + {disconnected && ( + +
+ {children} + Connectivity to the server has been lost. +
+
+ )} + + ); +} diff --git a/src/initializer.tsx b/src/initializer.tsx index 37e659e7..c6507668 100644 --- a/src/initializer.tsx +++ b/src/initializer.tsx @@ -45,6 +45,12 @@ class DependencyLoadStates { export class Initializer { private static internalInstance: Initializer; + private isInitialized = false; + + public static isInitialized(): boolean { + return Boolean(Initializer.internalInstance?.isInitialized); + } + public static initBeforeReact() { // this maybe also needs to return a promise in the future, // if we have to do async inits before showing the loading screen @@ -223,6 +229,7 @@ export class Initializer { if (this.loadStates.allDepsAreLoaded()) { // resolve if there is no dependency that is not loaded resolve(); + this.isInitialized = true; } } private initPromise: Promise | null; diff --git a/src/matrix-utils.ts b/src/matrix-utils.ts index 8cec21d0..2830b823 100644 --- a/src/matrix-utils.ts +++ b/src/matrix-utils.ts @@ -61,11 +61,11 @@ function waitForSync(client: MatrixClient) { data: ISyncStateData ) => { if (state === "PREPARED") { + client.removeListener(ClientEvent.Sync, onSync); resolve(); - client.removeListener(ClientEvent.Sync, onSync); } else if (state === "ERROR") { - reject(data?.error); client.removeListener(ClientEvent.Sync, onSync); + reject(data?.error); } }; client.on(ClientEvent.Sync, onSync); From 532dddcb2bd35c1d30ee93211dc14a6755b8b831 Mon Sep 17 00:00:00 2001 From: Timo K Date: Mon, 12 Jun 2023 10:14:11 +0200 Subject: [PATCH 02/59] cleanup Signed-off-by: Timo K --- public/locales/en-GB/app.json | 2 +- src/ClientContext.tsx | 3 +-- src/DisconnectedBanner.tsx | 19 ++++++++----------- 3 files changed, 10 insertions(+), 14 deletions(-) diff --git a/public/locales/en-GB/app.json b/public/locales/en-GB/app.json index f4892717..47bdbdb5 100644 --- a/public/locales/en-GB/app.json +++ b/public/locales/en-GB/app.json @@ -10,7 +10,6 @@ "{{name}} is talking…": "{{name}} is talking…", "{{names}}, {{name}}": "{{names}}, {{name}}", "{{roomName}} - Walkie-talkie call": "{{roomName}} - Walkie-talkie call", - "<0>{children}Connectivity to the server has been lost.": "<0>{children}Connectivity to the server has been lost.", "<0><1>You may withdraw consent by unchecking this box. If you are currently in a call, this setting will take effect at the end of the call.": "<0><1>You may withdraw consent by unchecking this box. If you are currently in a call, this setting will take effect at the end of the call.", "<0>Already have an account?<1><0>Log in Or <2>Access as a guest": "<0>Already have an account?<1><0>Log in Or <2>Access as a guest", "<0>Create an account Or <2>Access as a guest": "<0>Create an account Or <2>Access as a guest", @@ -37,6 +36,7 @@ "Close": "Close", "Confirm password": "Confirm password", "Connection lost": "Connection lost", + "Connectivity to the server has been lost.": "Connectivity to the server has been lost.", "Copied!": "Copied!", "Copy": "Copy", "Copy and share this call link": "Copy and share this call link", diff --git a/src/ClientContext.tsx b/src/ClientContext.tsx index 75c1bed3..1ffced8f 100644 --- a/src/ClientContext.tsx +++ b/src/ClientContext.tsx @@ -73,6 +73,7 @@ const saveSession = (session: Session) => const clearSession = () => localStorage.removeItem("matrix-auth-store"); const isDisconnected = (syncState, syncData) => syncState === "ERROR" && syncData?.error?.name === "ConnectionError"; + interface ClientState { loading: boolean; isAuthenticated: boolean; @@ -123,8 +124,6 @@ export const ClientProvider: FC = ({ children }) => { const onSync = (state: SyncState, _old: SyncState, data: ISyncStateData) => { setState((currentState) => { - logger.log("syntData.name", data?.error?.name, "state:", state); - console.log("Current state:", currentState); return { ...currentState, disconnected: isDisconnected(state, data), diff --git a/src/DisconnectedBanner.tsx b/src/DisconnectedBanner.tsx index 84ecc432..9ba58700 100644 --- a/src/DisconnectedBanner.tsx +++ b/src/DisconnectedBanner.tsx @@ -16,8 +16,7 @@ limitations under the License. import classNames from "classnames"; import React, { HTMLAttributes, ReactNode } from "react"; -import { logger } from "@sentry/utils"; -import { Trans } from "react-i18next"; +import { useTranslation } from "react-i18next"; import styles from "./DisconnectedBanner.module.css"; import { useClient } from "./ClientContext"; @@ -32,18 +31,16 @@ export function DisconnectedBanner({ className, ...rest }: DisconnectedBannerProps) { - const clientrp = useClient(); - logger.log(clientrp); - const disconnected = clientrp.disconnected; + const { t } = useTranslation(); + const { disconnected } = useClient(); + return ( <> {disconnected && ( - -
- {children} - Connectivity to the server has been lost. -
-
+
+ {children} + {t("Connectivity to the server has been lost.")} +
)} ); From 24997f1d3a8deb7c70eeb4901a2303ddc3394ea1 Mon Sep 17 00:00:00 2001 From: Timo K Date: Mon, 12 Jun 2023 10:16:02 +0200 Subject: [PATCH 03/59] update date Signed-off-by: Timo K --- src/DisconnectedBanner.module.css | 2 +- src/DisconnectedBanner.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/DisconnectedBanner.module.css b/src/DisconnectedBanner.module.css index aa6f6f5a..333c99d3 100644 --- a/src/DisconnectedBanner.module.css +++ b/src/DisconnectedBanner.module.css @@ -1,5 +1,5 @@ /* -Copyright 2022 New Vector Ltd +Copyright 2023 New Vector Ltd Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/src/DisconnectedBanner.tsx b/src/DisconnectedBanner.tsx index 9ba58700..b96c0b01 100644 --- a/src/DisconnectedBanner.tsx +++ b/src/DisconnectedBanner.tsx @@ -1,5 +1,5 @@ /* -Copyright 2022 New Vector Ltd +Copyright 2023 New Vector Ltd Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. From 49786762bf0016a16bb4e3f80a36484e4dbab626 Mon Sep 17 00:00:00 2001 From: Timo K Date: Tue, 13 Jun 2023 17:08:29 +0200 Subject: [PATCH 04/59] review Signed-off-by: Timo K --- src/ClientContext.tsx | 10 +++++----- src/DisconnectedBanner.module.css | 2 +- src/initializer.tsx | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/ClientContext.tsx b/src/ClientContext.tsx index 1ffced8f..ebe84f7e 100644 --- a/src/ClientContext.tsx +++ b/src/ClientContext.tsx @@ -124,10 +124,10 @@ export const ClientProvider: FC = ({ children }) => { const onSync = (state: SyncState, _old: SyncState, data: ISyncStateData) => { setState((currentState) => { - return { - ...currentState, - disconnected: isDisconnected(state, data), - }; + const disconnected = isDisconnected(state, data); + return disconnected === currentState.disconnected + ? currentState + : { ...currentState, disconnected }; }); }; @@ -205,7 +205,7 @@ export const ClientProvider: FC = ({ children }) => { } } }; - let clientWithListener; + let clientWithListener: MatrixClient; init() .then(({ client, isPasswordlessUser }) => { clientWithListener = client; diff --git a/src/DisconnectedBanner.module.css b/src/DisconnectedBanner.module.css index 333c99d3..5827953d 100644 --- a/src/DisconnectedBanner.module.css +++ b/src/DisconnectedBanner.module.css @@ -23,5 +23,5 @@ limitations under the License. text-align: center; z-index: 1; top: 76px; - width: 100%; + width: calc(100% - 58px); } diff --git a/src/initializer.tsx b/src/initializer.tsx index c6507668..ceea035b 100644 --- a/src/initializer.tsx +++ b/src/initializer.tsx @@ -48,7 +48,7 @@ export class Initializer { private isInitialized = false; public static isInitialized(): boolean { - return Boolean(Initializer.internalInstance?.isInitialized); + return Initializer.internalInstance?.isInitialized; } public static initBeforeReact() { From f0c9a2624245b0a281327b0b9fc08f1a021b546b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Mon, 3 Jul 2023 14:03:29 +0200 Subject: [PATCH 05/59] Add `jest-mock` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- package.json | 1 + yarn.lock | 45 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+) diff --git a/package.json b/package.json index 39f930af..e3ef048e 100644 --- a/package.json +++ b/package.json @@ -101,6 +101,7 @@ "identity-obj-proxy": "^3.0.0", "jest": "^29.2.2", "jest-environment-jsdom": "^29.3.1", + "jest-mock": "^29.5.0", "prettier": "^2.6.2", "sass": "^1.42.1", "storybook-builder-vite": "^0.1.12", diff --git a/yarn.lock b/yarn.lock index 0051059e..2324d1e1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2014,6 +2014,13 @@ dependencies: "@sinclair/typebox" "^0.24.1" +"@jest/schemas@^29.4.3": + version "29.4.3" + resolved "https://registry.yarnpkg.com/@jest/schemas/-/schemas-29.4.3.tgz#39cf1b8469afc40b6f5a2baaa146e332c4151788" + integrity sha512-VLYKXQmtmuEz6IxJsrZwzG9NvtkQsWNnWMsKxqWNu3+CnfzJQhp0WDDKWLVV9hLKr0l3SLLFRqcYHjhtyuDVxg== + dependencies: + "@sinclair/typebox" "^0.25.16" + "@jest/source-map@^29.2.0": version "29.2.0" resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-29.2.0.tgz#ab3420c46d42508dcc3dc1c6deee0b613c235744" @@ -2088,6 +2095,18 @@ "@types/yargs" "^17.0.8" chalk "^4.0.0" +"@jest/types@^29.5.0": + version "29.5.0" + resolved "https://registry.yarnpkg.com/@jest/types/-/types-29.5.0.tgz#f59ef9b031ced83047c67032700d8c807d6e1593" + integrity sha512-qbu7kN6czmVRc3xWFQcAN03RAUamgppVUdXrvl1Wr3jlNF93o9mJbGcDWrwGB6ht44u7efB1qCFgVQmca24Uog== + dependencies: + "@jest/schemas" "^29.4.3" + "@types/istanbul-lib-coverage" "^2.0.0" + "@types/istanbul-reports" "^3.0.0" + "@types/node" "*" + "@types/yargs" "^17.0.8" + chalk "^4.0.0" + "@joshwooding/vite-plugin-react-docgen-typescript@0.0.2": version "0.0.2" resolved "https://registry.yarnpkg.com/@joshwooding/vite-plugin-react-docgen-typescript/-/vite-plugin-react-docgen-typescript-0.0.2.tgz#e0ae8c94f468da3a273a7b0acf23ba3565f86cbc" @@ -3098,6 +3117,11 @@ resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.24.51.tgz#645f33fe4e02defe26f2f5c0410e1c094eac7f5f" integrity sha512-1P1OROm/rdubP5aFDSZQILU0vrLCJ4fvHt6EoqHEM+2D/G5MK3bIaymUKLit8Js9gbns5UyJnkP/TZROLw4tUA== +"@sinclair/typebox@^0.25.16": + version "0.25.24" + resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.25.24.tgz#8c7688559979f7079aacaf31aa881c3aa410b718" + integrity sha512-XJfwUVUKDHF5ugKwIcxEgc9k8b7HbznCp6eUfWgu710hMPNIO4aw4/zB5RogDQz8nd6gyCDpU9O/m6qYEWY6yQ== + "@sinonjs/commons@^1.7.0": version "1.8.3" resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.8.3.tgz#3802ddd21a50a949b6721ddd72da36e67e7f1b2d" @@ -10269,6 +10293,15 @@ jest-mock@^29.3.1: "@types/node" "*" jest-util "^29.3.1" +jest-mock@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-29.5.0.tgz#26e2172bcc71d8b0195081ff1f146ac7e1518aed" + integrity sha512-GqOzvdWDE4fAV2bWQLQCkujxYWL7RxjCnj71b5VhDAGOevB3qj3Ovg26A5NI84ZpODxyzaozXLOh2NCgkbvyaw== + dependencies: + "@jest/types" "^29.5.0" + "@types/node" "*" + jest-util "^29.5.0" + jest-pnp-resolver@^1.2.2: version "1.2.2" resolved "https://registry.yarnpkg.com/jest-pnp-resolver/-/jest-pnp-resolver-1.2.2.tgz#b704ac0ae028a89108a4d040b3f919dfddc8e33c" @@ -10411,6 +10444,18 @@ jest-util@^29.3.1: graceful-fs "^4.2.9" picomatch "^2.2.3" +jest-util@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-29.5.0.tgz#24a4d3d92fc39ce90425311b23c27a6e0ef16b8f" + integrity sha512-RYMgG/MTadOr5t8KdhejfvUU82MxsCu5MF6KuDUHl+NuwzUt+Sm6jJWxTJVrDR1j5M/gJVCPKQEpWXY+yIQ6lQ== + dependencies: + "@jest/types" "^29.5.0" + "@types/node" "*" + chalk "^4.0.0" + ci-info "^3.2.0" + graceful-fs "^4.2.9" + picomatch "^2.2.3" + jest-validate@^29.2.2: version "29.2.2" resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-29.2.2.tgz#e43ce1931292dfc052562a11bc681af3805eadce" From 11785fc243c7fab7b6c997111b6b932c785afd8b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Mon, 3 Jul 2023 14:59:26 +0200 Subject: [PATCH 06/59] Avoid redirects MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/App.tsx | 3 +- src/UrlParams.ts | 64 ++++++++++++----- src/initializer.tsx | 4 +- src/room/RoomRedirect.tsx | 44 ------------ test/UrlParams-test.ts | 146 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 197 insertions(+), 64 deletions(-) delete mode 100644 src/room/RoomRedirect.tsx create mode 100644 test/UrlParams-test.ts diff --git a/src/App.tsx b/src/App.tsx index d020655d..0822849d 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -23,7 +23,6 @@ import { HomePage } from "./home/HomePage"; import { LoginPage } from "./auth/LoginPage"; import { RegisterPage } from "./auth/RegisterPage"; import { RoomPage } from "./room/RoomPage"; -import { RoomRedirect } from "./room/RoomRedirect"; import { ClientProvider } from "./ClientContext"; import { usePageFocusStyle } from "./usePageFocusStyle"; import { SequenceDiagramViewerPage } from "./SequenceDiagramViewerPage"; @@ -75,7 +74,7 @@ export default function App({ history }: AppProps) {
- +
diff --git a/src/UrlParams.ts b/src/UrlParams.ts index eb2b1795..55d8ee10 100644 --- a/src/UrlParams.ts +++ b/src/UrlParams.ts @@ -1,5 +1,5 @@ /* -Copyright 2022 New Vector Ltd +Copyright 2022 - 2023 New Vector Ltd Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -14,9 +14,11 @@ See the License for the specific language governing permissions and limitations under the License. */ -import { useMemo } from "react"; +import { useCallback, useEffect, useState } from "react"; import { useLocation } from "react-router-dom"; +import { Config } from "./config/Config"; + interface UrlParams { roomAlias: string | null; roomId: string | null; @@ -93,14 +95,38 @@ interface UrlParams { * @returns The app parameters encoded in the URL */ export const getUrlParams = ( - query: string = window.location.search, - fragment: string = window.location.hash + ignoreRoomAlias?: boolean, + location: Location = window.location ): UrlParams => { - const fragmentQueryStart = fragment.indexOf("?"); + const { href, origin, search, hash } = location; + + let roomAlias: string | undefined; + if (!ignoreRoomAlias) { + roomAlias = href.substring(origin.length + 1); + + // If we have none, we throw + if (!roomAlias) { + throw Error("No pathname"); + } + // Delete "/room/" and "?", if present + if (roomAlias.startsWith("room/")) { + roomAlias = roomAlias.substring("room/".length).split("?")[0]; + } + // Add "#", if not present + if (!roomAlias.includes("#")) { + roomAlias = `#${roomAlias}`; + } + // Add server part, if missing + if (!roomAlias.includes(":")) { + roomAlias = `${roomAlias}:${Config.defaultServerName()}`; + } + } + + const fragmentQueryStart = hash.indexOf("?"); const fragmentParams = new URLSearchParams( - fragmentQueryStart === -1 ? "" : fragment.substring(fragmentQueryStart) + fragmentQueryStart === -1 ? "" : hash.substring(fragmentQueryStart) ); - const queryParams = new URLSearchParams(query); + const queryParams = new URLSearchParams(search); // Normally, URL params should be encoded in the fragment so as to avoid // leaking them to the server. However, we also check the normal query @@ -114,16 +140,10 @@ export const getUrlParams = ( ...queryParams.getAll(name), ]; - // The part of the fragment before the ? - const fragmentRoute = - fragmentQueryStart === -1 - ? fragment - : fragment.substring(0, fragmentQueryStart); - const fontScale = parseFloat(getParam("fontScale") ?? ""); return { - roomAlias: fragmentRoute.length > 1 ? fragmentRoute : null, + roomAlias: !roomAlias || roomAlias.includes("!") ? null : roomAlias, roomId: getParam("roomId"), viaServers: getAllParams("via"), isEmbedded: hasParam("embed"), @@ -149,6 +169,18 @@ export const getUrlParams = ( * @returns The app parameters for the current URL */ export const useUrlParams = (): UrlParams => { - const { hash, search } = useLocation(); - return useMemo(() => getUrlParams(search, hash), [search, hash]); + const getParams = useCallback(() => { + return getUrlParams(false, window.location); + }, []); + + const reactDomLocation = useLocation(); + const [urlParams, setUrlParams] = useState(getParams()); + + useEffect(() => { + if (window.location !== reactDomLocation) { + setUrlParams(getParams()); + } + }, [getParams, reactDomLocation]); + + return urlParams; }; diff --git a/src/initializer.tsx b/src/initializer.tsx index 37e659e7..f8a6c985 100644 --- a/src/initializer.tsx +++ b/src/initializer.tsx @@ -55,7 +55,7 @@ export class Initializer { languageDetector.addDetector({ name: "urlFragment", // Look for a language code in the URL's fragment - lookup: () => getUrlParams().lang ?? undefined, + lookup: () => getUrlParams(true).lang ?? undefined, }); i18n @@ -136,7 +136,7 @@ export class Initializer { } // Custom fonts - const { fonts, fontScale } = getUrlParams(); + const { fonts, fontScale } = getUrlParams(true); if (fontScale !== null) { document.documentElement.style.setProperty( "--font-scale", diff --git a/src/room/RoomRedirect.tsx b/src/room/RoomRedirect.tsx deleted file mode 100644 index 3d45086d..00000000 --- a/src/room/RoomRedirect.tsx +++ /dev/null @@ -1,44 +0,0 @@ -/* -Copyright 2022 New Vector Ltd - -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 { useEffect } from "react"; -import { useLocation, useHistory } from "react-router-dom"; - -import { Config } from "../config/Config"; -import { LoadingView } from "../FullScreenView"; - -// A component that, when loaded, redirects the client to a full room URL -// based on the current URL being an abbreviated room URL -export function RoomRedirect() { - const { pathname } = useLocation(); - const history = useHistory(); - - useEffect(() => { - let roomId = pathname; - - if (pathname.startsWith("/")) { - roomId = roomId.substring(1, roomId.length); - } - - if (!roomId.startsWith("#") && !roomId.startsWith("!")) { - roomId = `#${roomId}:${Config.defaultServerName()}`; - } - - history.replace(`/room/${roomId.toLowerCase()}`); - }, [pathname, history]); - - return ; -} diff --git a/test/UrlParams-test.ts b/test/UrlParams-test.ts new file mode 100644 index 00000000..9f691326 --- /dev/null +++ b/test/UrlParams-test.ts @@ -0,0 +1,146 @@ +/* +Copyright 2023 New Vector Ltd + +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 { mocked } from "jest-mock"; +import { getUrlParams } from "../src/UrlParams"; +import { Config } from "../src/config/Config"; + +const ROOM_NAME = "roomNameHere"; +const ROOM_ID = "d45f138fsd"; +const ORIGIN = "https://call.element.io"; +const HOMESERVER = "call.ems.host"; + +jest.mock("../src/config/Config"); + +describe("UrlParams", () => { + beforeAll(() => { + mocked(Config.defaultServerName).mockReturnValue("call.ems.host"); + }); + + describe("handles URL with /room/", () => { + it("and nothing else", () => { + expect( + getUrlParams(false, { + origin: ORIGIN, + href: `${ORIGIN}/room/${ROOM_NAME}`, + search: "", + hash: "", + } as Location).roomAlias + ).toBe(`#${ROOM_NAME}:${HOMESERVER}`); + }); + + it("and #", () => { + expect( + getUrlParams(false, { + origin: ORIGIN, + href: `${ORIGIN}/room/#${ROOM_NAME}`, + search: "", + hash: "", + } as Location).roomAlias + ).toBe(`#${ROOM_NAME}:${HOMESERVER}`); + }); + + it("and # and server part", () => { + expect( + getUrlParams(false, { + origin: ORIGIN, + href: `${ORIGIN}/room/#${ROOM_NAME}:${HOMESERVER}`, + search: "", + hash: "", + } as Location).roomAlias + ).toBe(`#${ROOM_NAME}:${HOMESERVER}`); + }); + + it("and server part", () => { + expect( + getUrlParams(false, { + origin: ORIGIN, + href: `${ORIGIN}/room/${ROOM_NAME}:${HOMESERVER}`, + search: "", + hash: "", + } as Location).roomAlias + ).toBe(`#${ROOM_NAME}:${HOMESERVER}`); + }); + }); + + describe("handles URL without /room/", () => { + it("and nothing else", () => { + expect( + getUrlParams(false, { + origin: ORIGIN, + href: `${ORIGIN}/${ROOM_NAME}`, + search: "", + hash: "", + } as Location).roomAlias + ).toBe(`#${ROOM_NAME}:${HOMESERVER}`); + }); + + it("and with #", () => { + expect( + getUrlParams(false, { + origin: ORIGIN, + href: `${ORIGIN}/room/#${ROOM_NAME}`, + search: "", + hash: "", + } as Location).roomAlias + ).toBe(`#${ROOM_NAME}:${HOMESERVER}`); + }); + + it("and with # and server part", () => { + expect( + getUrlParams(false, { + origin: ORIGIN, + href: `${ORIGIN}/room/#${ROOM_NAME}:${HOMESERVER}`, + search: "", + hash: "", + } as Location).roomAlias + ).toBe(`#${ROOM_NAME}:${HOMESERVER}`); + }); + + it("and with server part", () => { + expect( + getUrlParams(false, { + origin: ORIGIN, + href: `${ORIGIN}/room/${ROOM_NAME}:${HOMESERVER}`, + search: "", + hash: "", + } as Location).roomAlias + ).toBe(`#${ROOM_NAME}:${HOMESERVER}`); + }); + }); + + describe("handles search params", () => { + it("(roomId)", () => { + expect( + getUrlParams(true, { + search: `?roomId=${ROOM_ID}`, + hash: "", + } as Location).roomId + ).toBe(ROOM_ID); + }); + }); + + it("ignores room alias", () => { + expect( + getUrlParams(true, { + origin: ORIGIN, + href: `${ORIGIN}/room/${ROOM_NAME}:${HOMESERVER}`, + hash: "", + search: "", + } as Location).roomAlias + ).toBeFalsy(); + }); +}); From fdc41aaa50238209235aa5dff0e8c2c6cea9a4b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Mon, 3 Jul 2023 16:20:19 +0200 Subject: [PATCH 07/59] Use friendly URLs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/App.tsx | 3 --- src/home/CallList.tsx | 5 ++++- src/home/RegisteredView.tsx | 2 +- src/home/UnauthenticatedView.tsx | 5 ++--- src/matrix-utils.ts | 12 ++++-------- 5 files changed, 11 insertions(+), 16 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index 0822849d..e9a1129d 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -67,9 +67,6 @@ export default function App({ history }: AppProps) { - - - diff --git a/src/home/CallList.tsx b/src/home/CallList.tsx index 545dfade..2cc821b9 100644 --- a/src/home/CallList.tsx +++ b/src/home/CallList.tsx @@ -74,7 +74,10 @@ function CallTile({ }: CallTileProps) { return (
- + { setOnFinished(() => { setClient(client, session); const aliasLocalpart = roomAliasLocalpartFromRoomName(roomName); - const [, serverName] = client.getUserId().split(":"); - history.push(`/room/#${aliasLocalpart}:${serverName}`); + history.push(`/${aliasLocalpart}`); }); setLoading(false); @@ -100,7 +99,7 @@ export const UnauthenticatedView: FC = () => { // Only consider the registration successful if we managed to create the room, too setClient(client, session); - history.push(`/room/${roomIdOrAlias}`); + history.push(`/${roomIdOrAlias.substring(1).split(":")[0]}`); } submit().catch((error) => { diff --git a/src/matrix-utils.ts b/src/matrix-utils.ts index 9a7645d2..85a7b313 100644 --- a/src/matrix-utils.ts +++ b/src/matrix-utils.ts @@ -346,15 +346,11 @@ export async function createRoom( // Returns a URL to that will load Element Call with the given room export function getRoomUrl(roomIdOrAlias: string): string { if (roomIdOrAlias.startsWith("#")) { - const [localPart, host] = roomIdOrAlias.replace("#", "").split(":"); - - if (host !== Config.defaultServerName()) { - return `${window.location.protocol}//${window.location.host}/room/${roomIdOrAlias}`; - } else { - return `${window.location.protocol}//${window.location.host}/${localPart}`; - } + return `${window.location.protocol}//${window.location.host}/${ + roomIdOrAlias.substring(1).split(":")[0] + }`; } else { - return `${window.location.protocol}//${window.location.host}/room/#?roomId=${roomIdOrAlias}`; + return `${window.location.protocol}//${window.location.host}?roomId=${roomIdOrAlias}`; } } From e1ef18093f6ee7dede747b2c83ae0f636de09558 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Mon, 3 Jul 2023 19:23:26 +0200 Subject: [PATCH 08/59] Fix alias vs id + participants bug MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/home/CallList.tsx | 16 ++++++++-------- src/home/useGroupCallRooms.ts | 13 +++++++------ test/home/CallList-test.tsx | 2 +- 3 files changed, 16 insertions(+), 15 deletions(-) diff --git a/src/home/CallList.tsx b/src/home/CallList.tsx index 2cc821b9..c3d5a6ae 100644 --- a/src/home/CallList.tsx +++ b/src/home/CallList.tsx @@ -35,13 +35,13 @@ export function CallList({ rooms, client, disableFacepile }: CallListProps) { return ( <>
- {rooms.map(({ roomId, roomName, avatarUrl, participants }) => ( + {rooms.map(({ roomAlias, roomName, avatarUrl, participants }) => ( @@ -59,7 +59,7 @@ export function CallList({ rooms, client, disableFacepile }: CallListProps) { interface CallTileProps { name: string; avatarUrl: string; - roomId: string; + roomAlias: string; participants: RoomMember[]; client: MatrixClient; disableFacepile?: boolean; @@ -67,7 +67,7 @@ interface CallTileProps { function CallTile({ name, avatarUrl, - roomId, + roomAlias, participants, client, disableFacepile, @@ -75,7 +75,7 @@ function CallTile({ return (
{name} - {getRoomUrl(roomId)} + {getRoomUrl(roomAlias)} {participants && !disableFacepile && (
); diff --git a/src/home/useGroupCallRooms.ts b/src/home/useGroupCallRooms.ts index fcbe67ff..b1eb2e7a 100644 --- a/src/home/useGroupCallRooms.ts +++ b/src/home/useGroupCallRooms.ts @@ -22,7 +22,7 @@ import { GroupCallEventHandlerEvent } from "matrix-js-sdk/src/webrtc/groupCallEv import { useState, useEffect } from "react"; export interface GroupCallRoom { - roomId: string; + roomAlias: string; roomName: string; avatarUrl: string; room: Room; @@ -79,23 +79,24 @@ function sortRooms(client: MatrixClient, rooms: Room[]): Room[] { } export function useGroupCallRooms(client: MatrixClient): GroupCallRoom[] { - const [rooms, setRooms] = useState([]); + const [rooms, setRooms] = useState([]); useEffect(() => { function updateRooms() { const groupCalls = client.groupCallEventHandler.groupCalls.values(); const rooms = Array.from(groupCalls).map((groupCall) => groupCall.room); - const sortedRooms = sortRooms(client, rooms); - const items = sortedRooms.map((room) => { + const filteredRooms = rooms.filter((r) => r.getCanonicalAlias()); // We don't display rooms without an alias + const sortedRooms = sortRooms(client, filteredRooms); + const items: GroupCallRoom[] = sortedRooms.map((room) => { const groupCall = client.getGroupCallForRoom(room.roomId); return { - roomId: room.getCanonicalAlias() || room.roomId, + roomAlias: room.getCanonicalAlias(), roomName: room.name, avatarUrl: room.getMxcAvatarUrl(), room, groupCall, - participants: [...groupCall.participants], + participants: [...groupCall.participants.keys()], }; }); setRooms(items); diff --git a/test/home/CallList-test.tsx b/test/home/CallList-test.tsx index 7b3925d4..7dfb7b21 100644 --- a/test/home/CallList-test.tsx +++ b/test/home/CallList-test.tsx @@ -34,7 +34,7 @@ describe("CallList", () => { it("should show room", async () => { const rooms = [ - { roomName: "Room #1", roomId: "!roomId" }, + { roomName: "Room #1", roomAlias: "!roomId" }, ] as GroupCallRoom[]; const result = renderComponent(rooms); From b08044c06d48b95456fbcc0533e8d13a1b38f723 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Mon, 3 Jul 2023 19:30:38 +0200 Subject: [PATCH 09/59] More fixes of id vs alias MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/home/UnauthenticatedView.tsx | 6 +++--- test/home/CallList-test.tsx | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/home/UnauthenticatedView.tsx b/src/home/UnauthenticatedView.tsx index f49cf218..820b11ab 100644 --- a/src/home/UnauthenticatedView.tsx +++ b/src/home/UnauthenticatedView.tsx @@ -78,9 +78,9 @@ export const UnauthenticatedView: FC = () => { true ); - let roomIdOrAlias: string; + let roomAlias: string; try { - [roomIdOrAlias] = await createRoom(client, roomName, ptt); + [roomAlias] = await createRoom(client, roomName, ptt); } catch (error) { if (error.errcode === "M_ROOM_IN_USE") { setOnFinished(() => { @@ -99,7 +99,7 @@ export const UnauthenticatedView: FC = () => { // Only consider the registration successful if we managed to create the room, too setClient(client, session); - history.push(`/${roomIdOrAlias.substring(1).split(":")[0]}`); + history.push(`/${roomAlias.substring(1).split(":")[0]}`); } submit().catch((error) => { diff --git a/test/home/CallList-test.tsx b/test/home/CallList-test.tsx index 7dfb7b21..a161960f 100644 --- a/test/home/CallList-test.tsx +++ b/test/home/CallList-test.tsx @@ -34,7 +34,7 @@ describe("CallList", () => { it("should show room", async () => { const rooms = [ - { roomName: "Room #1", roomAlias: "!roomId" }, + { roomName: "Room #1", roomAlias: "#room-name:server.org" }, ] as GroupCallRoom[]; const result = renderComponent(rooms); From 0bba62045110c1722263029b35ae7f39f71e044f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Mon, 3 Jul 2023 19:32:25 +0200 Subject: [PATCH 10/59] Further id vs alias MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/home/RegisteredView.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/home/RegisteredView.tsx b/src/home/RegisteredView.tsx index cd5bc02a..3d0c6eb0 100644 --- a/src/home/RegisteredView.tsx +++ b/src/home/RegisteredView.tsx @@ -69,10 +69,10 @@ export function RegisteredView({ client, isPasswordlessUser }: Props) { setError(undefined); setLoading(true); - const [roomIdOrAlias] = await createRoom(client, roomName, ptt); + const [roomAlias] = await createRoom(client, roomName, ptt); - if (roomIdOrAlias) { - history.push(`/${roomIdOrAlias.substring(1).split(":")[0]}`); + if (roomAlias) { + history.push(`/${roomAlias.substring(1).split(":")[0]}`); } } From 8fbcc06cd8f5635fc166665204c7300f1557883d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Mon, 3 Jul 2023 20:05:08 +0200 Subject: [PATCH 11/59] Don't use the whole `Location` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/UrlParams.ts | 54 ++++++++++++--------------- test/UrlParams-test.ts | 84 +++++++++--------------------------------- 2 files changed, 41 insertions(+), 97 deletions(-) diff --git a/src/UrlParams.ts b/src/UrlParams.ts index 55d8ee10..f41e3cfa 100644 --- a/src/UrlParams.ts +++ b/src/UrlParams.ts @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -import { useCallback, useEffect, useState } from "react"; +import { useMemo } from "react"; import { useLocation } from "react-router-dom"; import { Config } from "./config/Config"; @@ -96,27 +96,28 @@ interface UrlParams { */ export const getUrlParams = ( ignoreRoomAlias?: boolean, - location: Location = window.location + search = window.location.search, + pathname = window.location.pathname, + hash = window.location.hash ): UrlParams => { - const { href, origin, search, hash } = location; - let roomAlias: string | undefined; if (!ignoreRoomAlias) { - roomAlias = href.substring(origin.length + 1); + if (hash === "") { + roomAlias = pathname.substring(1); // Strip the "/" - // If we have none, we throw - if (!roomAlias) { - throw Error("No pathname"); + // Delete "/room/" and "?", if present + if (roomAlias.startsWith("room/")) { + roomAlias = roomAlias.substring("room/".length); + } + // Add "#", if not present + if (!roomAlias.startsWith("#")) { + roomAlias = `#${roomAlias}`; + } + } else { + roomAlias = hash; } - // Delete "/room/" and "?", if present - if (roomAlias.startsWith("room/")) { - roomAlias = roomAlias.substring("room/".length).split("?")[0]; - } - // Add "#", if not present - if (!roomAlias.includes("#")) { - roomAlias = `#${roomAlias}`; - } - // Add server part, if missing + + // Add server part, if not present if (!roomAlias.includes(":")) { roomAlias = `${roomAlias}:${Config.defaultServerName()}`; } @@ -169,18 +170,9 @@ export const getUrlParams = ( * @returns The app parameters for the current URL */ export const useUrlParams = (): UrlParams => { - const getParams = useCallback(() => { - return getUrlParams(false, window.location); - }, []); - - const reactDomLocation = useLocation(); - const [urlParams, setUrlParams] = useState(getParams()); - - useEffect(() => { - if (window.location !== reactDomLocation) { - setUrlParams(getParams()); - } - }, [getParams, reactDomLocation]); - - return urlParams; + const { search, pathname, hash } = useLocation(); + return useMemo( + () => getUrlParams(false, search, pathname, hash), + [search, pathname, hash] + ); }; diff --git a/test/UrlParams-test.ts b/test/UrlParams-test.ts index 9f691326..9d1e5198 100644 --- a/test/UrlParams-test.ts +++ b/test/UrlParams-test.ts @@ -32,115 +32,67 @@ describe("UrlParams", () => { describe("handles URL with /room/", () => { it("and nothing else", () => { - expect( - getUrlParams(false, { - origin: ORIGIN, - href: `${ORIGIN}/room/${ROOM_NAME}`, - search: "", - hash: "", - } as Location).roomAlias - ).toBe(`#${ROOM_NAME}:${HOMESERVER}`); + expect(getUrlParams(false, "", `/room/${ROOM_NAME}`, "").roomAlias).toBe( + `#${ROOM_NAME}:${HOMESERVER}` + ); }); it("and #", () => { expect( - getUrlParams(false, { - origin: ORIGIN, - href: `${ORIGIN}/room/#${ROOM_NAME}`, - search: "", - hash: "", - } as Location).roomAlias + getUrlParams(false, "", `${ORIGIN}/room/`, `#${ROOM_NAME}`).roomAlias ).toBe(`#${ROOM_NAME}:${HOMESERVER}`); }); it("and # and server part", () => { expect( - getUrlParams(false, { - origin: ORIGIN, - href: `${ORIGIN}/room/#${ROOM_NAME}:${HOMESERVER}`, - search: "", - hash: "", - } as Location).roomAlias + getUrlParams(false, "", `/room/`, `#${ROOM_NAME}:${HOMESERVER}`) + .roomAlias ).toBe(`#${ROOM_NAME}:${HOMESERVER}`); }); it("and server part", () => { expect( - getUrlParams(false, { - origin: ORIGIN, - href: `${ORIGIN}/room/${ROOM_NAME}:${HOMESERVER}`, - search: "", - hash: "", - } as Location).roomAlias + getUrlParams(false, "", `/room/${ROOM_NAME}:${HOMESERVER}`, "") + .roomAlias ).toBe(`#${ROOM_NAME}:${HOMESERVER}`); }); }); describe("handles URL without /room/", () => { it("and nothing else", () => { - expect( - getUrlParams(false, { - origin: ORIGIN, - href: `${ORIGIN}/${ROOM_NAME}`, - search: "", - hash: "", - } as Location).roomAlias - ).toBe(`#${ROOM_NAME}:${HOMESERVER}`); + expect(getUrlParams(false, "", `/${ROOM_NAME}`, "").roomAlias).toBe( + `#${ROOM_NAME}:${HOMESERVER}` + ); }); it("and with #", () => { - expect( - getUrlParams(false, { - origin: ORIGIN, - href: `${ORIGIN}/room/#${ROOM_NAME}`, - search: "", - hash: "", - } as Location).roomAlias - ).toBe(`#${ROOM_NAME}:${HOMESERVER}`); + expect(getUrlParams(false, "", "", `#${ROOM_NAME}`).roomAlias).toBe( + `#${ROOM_NAME}:${HOMESERVER}` + ); }); it("and with # and server part", () => { expect( - getUrlParams(false, { - origin: ORIGIN, - href: `${ORIGIN}/room/#${ROOM_NAME}:${HOMESERVER}`, - search: "", - hash: "", - } as Location).roomAlias + getUrlParams(false, "", "", `#${ROOM_NAME}:${HOMESERVER}`).roomAlias ).toBe(`#${ROOM_NAME}:${HOMESERVER}`); }); it("and with server part", () => { expect( - getUrlParams(false, { - origin: ORIGIN, - href: `${ORIGIN}/room/${ROOM_NAME}:${HOMESERVER}`, - search: "", - hash: "", - } as Location).roomAlias + getUrlParams(false, "", `/${ROOM_NAME}:${HOMESERVER}`, "").roomAlias ).toBe(`#${ROOM_NAME}:${HOMESERVER}`); }); }); describe("handles search params", () => { it("(roomId)", () => { - expect( - getUrlParams(true, { - search: `?roomId=${ROOM_ID}`, - hash: "", - } as Location).roomId - ).toBe(ROOM_ID); + expect(getUrlParams(true, `?roomId=${ROOM_ID}`).roomId).toBe(ROOM_ID); }); }); it("ignores room alias", () => { expect( - getUrlParams(true, { - origin: ORIGIN, - href: `${ORIGIN}/room/${ROOM_NAME}:${HOMESERVER}`, - hash: "", - search: "", - } as Location).roomAlias + getUrlParams(true, "", `/room/${ROOM_NAME}:${HOMESERVER}`).roomAlias ).toBeFalsy(); }); }); From 6c8b96ed3d5393882cb0ebed8e678f726774e0f9 Mon Sep 17 00:00:00 2001 From: David Baker Date: Tue, 4 Jul 2023 18:57:35 +0100 Subject: [PATCH 12/59] Use prebuilt image for docker compose --- backend-docker-compose.yml | 11 ++-- backend/auth/Dockerfile | 15 ----- backend/auth/go.mod | 20 ------ backend/auth/go.sum | 94 --------------------------- backend/auth/server.go | 126 ------------------------------------- 5 files changed, 4 insertions(+), 262 deletions(-) delete mode 100644 backend/auth/Dockerfile delete mode 100644 backend/auth/go.mod delete mode 100644 backend/auth/go.sum delete mode 100644 backend/auth/server.go diff --git a/backend-docker-compose.yml b/backend-docker-compose.yml index d575cf18..58b147e9 100644 --- a/backend-docker-compose.yml +++ b/backend-docker-compose.yml @@ -1,5 +1,3 @@ -# LiveKit requires host networking, which is only available on Linux -# This compose will not function correctly on Mac or Windows version: "3.9" networks: @@ -7,15 +5,14 @@ networks: services: auth-service: - build: - context: ./backend/auth - container_name: auth-server + image: ghcr.io/vector-im/lk-jwt-service:latest-ci hostname: auth-server ports: - 8881:8080 environment: - - LIVEKIT_KEY=${LIVEKIT_KEY} - - LIVEKIT_SECRET=${LIVEKIT_SECRET} + - LIVEKIT_URL=ws://localhost:7880 + - LIVEKIT_KEY=devkey + - LIVEKIT_SECRET=secret deploy: restart_policy: condition: on-failure diff --git a/backend/auth/Dockerfile b/backend/auth/Dockerfile deleted file mode 100644 index c7bbd955..00000000 --- a/backend/auth/Dockerfile +++ /dev/null @@ -1,15 +0,0 @@ -FROM golang:1.20-alpine - -WORKDIR /app - -COPY go.mod ./ -COPY go.sum ./ -RUN go mod download - -COPY *.go ./ - -RUN go build -o /auth-server - -EXPOSE 8080 - -CMD [ "/auth-server" ] diff --git a/backend/auth/go.mod b/backend/auth/go.mod deleted file mode 100644 index 75d1a533..00000000 --- a/backend/auth/go.mod +++ /dev/null @@ -1,20 +0,0 @@ -module vector-auth-server - -go 1.20 - -require github.com/livekit/protocol v1.5.7 - -require ( - github.com/go-jose/go-jose/v3 v3.0.0 // indirect - github.com/golang/protobuf v1.5.3 // indirect - github.com/twitchtv/twirp v8.1.3+incompatible // indirect - golang.org/x/crypto v0.9.0 // indirect - golang.org/x/exp v0.0.0-20230515195305-f3d0a9c9a5cc // indirect - golang.org/x/net v0.10.0 // indirect - golang.org/x/sys v0.8.0 // indirect - golang.org/x/text v0.9.0 // indirect - google.golang.org/genproto v0.0.0-20230403163135-c38d8f061ccd // indirect - google.golang.org/grpc v1.55.0 // indirect - google.golang.org/protobuf v1.30.0 // indirect - gopkg.in/yaml.v3 v3.0.1 // indirect -) diff --git a/backend/auth/go.sum b/backend/auth/go.sum deleted file mode 100644 index e8419741..00000000 --- a/backend/auth/go.sum +++ /dev/null @@ -1,94 +0,0 @@ -github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= -github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= -github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= -github.com/eapache/channels v1.1.0 h1:F1taHcn7/F0i8DYqKXJnyhJcVpp2kgFcNePxXtnyu4k= -github.com/eapache/queue v1.1.0 h1:YOEu7KNc61ntiQlcEeUIoDTJ2o8mQznoNvUhiigpIqc= -github.com/frostbyte73/core v0.0.9 h1:AmE9GjgGpPsWk9ZkmY3HsYUs2hf2tZt+/W6r49URBQI= -github.com/gammazero/deque v0.2.1 h1:qSdsbG6pgp6nL7A0+K/B7s12mcCY/5l5SIUpMOl+dC0= -github.com/go-jose/go-jose/v3 v3.0.0 h1:s6rrhirfEP/CGIoc6p+PZAeogN2SxKav6Wp7+dyMWVo= -github.com/go-jose/go-jose/v3 v3.0.0/go.mod h1:RNkWWRld676jZEYoV3+XK8L2ZnNSvIsxFMht0mSX+u8= -github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= -github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= -github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= -github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= -github.com/jxskiss/base62 v1.1.0 h1:A5zbF8v8WXx2xixnAKD2w+abC+sIzYJX+nxmhA6HWFw= -github.com/lithammer/shortuuid/v4 v4.0.0 h1:QRbbVkfgNippHOS8PXDkti4NaWeyYfcBTHtw7k08o4c= -github.com/livekit/mageutil v0.0.0-20230125210925-54e8a70427c1 h1:jm09419p0lqTkDaKb5iXdynYrzB84ErPPO4LbRASk58= -github.com/livekit/protocol v1.5.7 h1:jZeFQEmLuIhFblXDGPRCBbfjVJHb+YU7AsO+SMoXF70= -github.com/livekit/protocol v1.5.7/go.mod h1:ZaOnsvP+JS4s7vI1UO+JVdBagvvLp/lBXDAl2hkDS0I= -github.com/livekit/psrpc v0.3.0 h1:giBZsfM3CWA0oIYXofsMITbVQtyW7u/ES9sQmVspHPM= -github.com/mackerelio/go-osstat v0.2.4 h1:qxGbdPkFo65PXOb/F/nhDKpF2nGmGaCFDLXoZjJTtUs= -github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= -github.com/nats-io/nats.go v1.25.0 h1:t5/wCPGciR7X3Mu8QOi4jiJaXaWM8qtkLu4lzGZvYHE= -github.com/nats-io/nkeys v0.4.4 h1:xvBJ8d69TznjcQl9t6//Q5xXuVhyYiSos6RPtvQNTwA= -github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw= -github.com/pion/datachannel v1.5.5 h1:10ef4kwdjije+M9d7Xm9im2Y3O6A6ccQb0zcqZcJew8= -github.com/pion/dtls/v2 v2.2.6 h1:yXMxKr0Skd+Ub6A8UqXTRLSywskx93ooMRHsQUtd+Z4= -github.com/pion/ice/v2 v2.3.4 h1:tjYjTLpWyZzUjpDnzk6T1y3oQyhyY2DiM2t095iDhyQ= -github.com/pion/interceptor v0.1.16 h1:0GDZrfNO+BmVNWymS31fMlVtPO2IJVBzy2Qq5XCYMIg= -github.com/pion/logging v0.2.2 h1:M9+AIj/+pxNsDfAT64+MAVgJO0rsyLnoJKCqf//DoeY= -github.com/pion/mdns v0.0.7 h1:P0UB4Sr6xDWEox0kTVxF0LmQihtCbSAdW0H2nEgkA3U= -github.com/pion/randutil v0.1.0 h1:CFG1UdESneORglEsnimhUjf33Rwjubwj6xfiOXBa3mA= -github.com/pion/rtcp v1.2.10 h1:nkr3uj+8Sp97zyItdN60tE/S6vk4al5CPRR6Gejsdjc= -github.com/pion/rtp v1.7.13 h1:qcHwlmtiI50t1XivvoawdCGTP4Uiypzfrsap+bijcoA= -github.com/pion/sctp v1.8.7 h1:JnABvFakZueGAn4KU/4PSKg+GWbF6QWbKTWZOSGJjXw= -github.com/pion/sdp/v3 v3.0.6 h1:WuDLhtuFUUVpTfus9ILC4HRyHsW6TdugjEX/QY9OiUw= -github.com/pion/srtp/v2 v2.0.14 h1:Glt0MqEvINrDxL+aanmK4DiFjvs+uN2iYc6XD/iKpoY= -github.com/pion/stun v0.5.2 h1:J/8glQnDV91dfk2+ZnGN0o9bUJgABhTNljwfQWByoXE= -github.com/pion/transport/v2 v2.2.0 h1:u5lFqFHkXLMXMzai8tixZDfVjb8eOjH35yCunhPeb1c= -github.com/pion/turn/v2 v2.1.0 h1:5wGHSgGhJhP/RpabkUb/T9PdsAjkGLS6toYz5HNzoSI= -github.com/pion/udp/v2 v2.0.1 h1:xP0z6WNux1zWEjhC7onRA3EwwSliXqu1ElUZAQhUP54= -github.com/pion/webrtc/v3 v3.2.4 h1:gWSx4dqQb77051qBT9ipDrOyP6/sGYcAQP3UPjM8pU8= -github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prometheus/client_golang v1.15.1 h1:8tXpTmJbyH5lydzFPoxSIJ0J46jdh3tylbvM1xCv0LI= -github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4= -github.com/prometheus/common v0.42.0 h1:EKsfXEYo4JpWMHH5cg+KOUWeuJSov1Id8zGR8eeI1YM= -github.com/prometheus/procfs v0.9.0 h1:wzCHvIvM5SxWqYvwgVL7yJY8Lz3PKn49KQtpgMYJfhI= -github.com/redis/go-redis/v9 v9.0.4 h1:FC82T+CHJ/Q/PdyLW++GeCO+Ol59Y4T7R4jbgjvktgc= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY= -github.com/twitchtv/twirp v8.1.3+incompatible h1:+F4TdErPgSUbMZMwp13Q/KgDVuI7HJXP61mNV3/7iuU= -github.com/twitchtv/twirp v8.1.3+incompatible/go.mod h1:RRJoFSAmTEh2weEqWtpPE3vFK5YBhA6bqp2l1kfCC5A= -go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= -go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4= -go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.9.0 h1:LF6fAI+IutBocDJ2OT0Q1g8plpYljMZ4+lty+dsqw3g= -golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0= -golang.org/x/exp v0.0.0-20230515195305-f3d0a9c9a5cc h1:mCRnTeVUjcrhlRmO0VK8a6k6Rrf6TF9htwo2pJVSjIU= -golang.org/x/exp v0.0.0-20230515195305-f3d0a9c9a5cc/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M= -golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= -golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU= -golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= -golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/genproto v0.0.0-20230403163135-c38d8f061ccd h1:sLpv7bNL1AsX3fdnWh9WVh7ejIzXdOc1RRHGeAmeStU= -google.golang.org/genproto v0.0.0-20230403163135-c38d8f061ccd/go.mod h1:UUQDJDOlWu4KYeJZffbWgBkS1YFobzKbLVfK69pe0Ak= -google.golang.org/grpc v1.55.0 h1:3Oj82/tFSCeUrRTg/5E/7d/W5A1tj6Ky1ABAuZuv5ag= -google.golang.org/grpc v1.55.0/go.mod h1:iYEXKGkEBhg1PjZQvoYEVPTDkHo1/bjTnfwTeGONTY8= -google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= -google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/backend/auth/server.go b/backend/auth/server.go deleted file mode 100644 index f099784e..00000000 --- a/backend/auth/server.go +++ /dev/null @@ -1,126 +0,0 @@ -package main - -import ( - "encoding/json" - "log" - "net/http" - "os" - - "time" - - "github.com/livekit/protocol/auth" -) - -type Handler struct { - key, secret string -} - -type OpenIDTokenType struct { -} - -type SFURequest struct { - Room string `json:"room"` - OpenIDToken OpenIDTokenType `json:"openid_token"` - DeviceID string `json:"device_id"` - RemoveMeUserID string `json:"remove_me_user_id"` // we'll get this from OIDC -} - -type SFUResponse struct { - URL string `json:"url"` - JWT string `json:"jwt"` -} - -func (h *Handler) handle(w http.ResponseWriter, r *http.Request) { - log.Printf("Request from %s", r.RemoteAddr) - - // Set the CORS headers - w.Header().Set("Access-Control-Allow-Origin", "*") - w.Header().Set("Access-Control-Allow-Methods", "POST") - w.Header().Set("Access-Control-Allow-Headers", "Accept, Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token") - - // Handle preflight request (CORS) - if r.Method == "OPTIONS" { - w.WriteHeader(http.StatusOK) - return - } else if r.Method == "POST" { - var body SFURequest - err := json.NewDecoder(r.Body).Decode(&body) - if err != nil { - log.Printf("Error decoding JSON: %v", err) - w.WriteHeader(http.StatusBadRequest) - return - } - - if body.Room == "" { - log.Printf("Request missing room") - w.WriteHeader(http.StatusBadRequest) - return - } - - token, err := getJoinToken(h.key, h.secret, body.Room, body.RemoveMeUserID+":"+body.DeviceID) - if err != nil { - w.WriteHeader(http.StatusInternalServerError) - return - } - - res := SFUResponse{URL: "http://localhost:7880/", JWT: token} - - w.Header().Set("Content-Type", "application/json") - json.NewEncoder(w).Encode(res) - } else { - w.WriteHeader(http.StatusMethodNotAllowed) - } - - /* - roomName := r.URL.Query().Get("roomName") - name := r.URL.Query().Get("name") - identity := r.URL.Query().Get("identity") - - log.Printf("roomName: %s, name: %s, identity: %s", roomName, name, identity) - - if roomName == "" || name == "" || identity == "" { - w.WriteHeader(http.StatusBadRequest) - return - } - */ -} - -func main() { - key := os.Getenv("LIVEKIT_KEY") - secret := os.Getenv("LIVEKIT_SECRET") - - // Check if the key and secret are empty. - if key == "" || secret == "" { - log.Fatal("LIVEKIT_KEY and LIVEKIT_SECRET environment variables must be set") - } - - log.Printf("LIVEKIT_KEY: %s and LIVEKIT_SECRET %s", key, secret) - - handler := &Handler{ - key: key, - secret: secret, - } - - http.HandleFunc("/sfu/get", handler.handle) - log.Fatal(http.ListenAndServe(":8080", nil)) -} - -func getJoinToken(apiKey, apiSecret, room, identity string) (string, error) { - at := auth.NewAccessToken(apiKey, apiSecret) - - canPublish := true - canSubscribe := true - grant := &auth.VideoGrant{ - RoomJoin: true, - RoomCreate: true, - CanPublish: &canPublish, - CanSubscribe: &canSubscribe, - Room: room, - } - - at.AddGrant(grant). - SetIdentity(identity). - SetValidFor(time.Hour) - - return at.ToJWT() -} From 607e1b7b43aac502e9ca1c790fe9370eaf298573 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Thu, 6 Jul 2023 12:02:39 +0200 Subject: [PATCH 13/59] Fix `getRoomUrl()` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/matrix-utils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/matrix-utils.ts b/src/matrix-utils.ts index 85a7b313..3bef9e40 100644 --- a/src/matrix-utils.ts +++ b/src/matrix-utils.ts @@ -350,7 +350,7 @@ export function getRoomUrl(roomIdOrAlias: string): string { roomIdOrAlias.substring(1).split(":")[0] }`; } else { - return `${window.location.protocol}//${window.location.host}?roomId=${roomIdOrAlias}`; + return `${window.location.protocol}//${window.location.host}/room?roomId=${roomIdOrAlias}`; } } From e264a71d1e051d01d45e5aafe40f8eae915a0f70 Mon Sep 17 00:00:00 2001 From: David Baker Date: Wed, 12 Jul 2023 17:57:54 +0100 Subject: [PATCH 14/59] Merge v0.4.2 hotfixes --- package.json | 2 +- src/ClientContext.tsx | 6 +-- src/livekit/OpenIDLoader.tsx | 14 ++---- src/livekit/openIDSFU.ts | 93 +++++++++++++++++++++++++++++------- src/room/GroupCallView.tsx | 10 +--- src/widget.ts | 6 +-- yarn.lock | 10 ++-- 7 files changed, 92 insertions(+), 49 deletions(-) diff --git a/package.json b/package.json index 3d3d938b..6a5a47a1 100644 --- a/package.json +++ b/package.json @@ -60,7 +60,7 @@ "i18next-http-backend": "^1.4.4", "livekit-client": "1.12.0", "lodash": "^4.17.21", - "matrix-js-sdk": "github:matrix-org/matrix-js-sdk#d79d9ae69c3220c02406706d4a1ec52c22c44fbd", + "matrix-js-sdk": "github:matrix-org/matrix-js-sdk#b698217445318f453e0b1086364a33113eaa85d9", "matrix-widget-api": "^1.3.1", "mermaid": "^8.13.8", "normalize.css": "^8.0.1", diff --git a/src/ClientContext.tsx b/src/ClientContext.tsx index d488d6de..69a9295e 100644 --- a/src/ClientContext.tsx +++ b/src/ClientContext.tsx @@ -327,10 +327,6 @@ async function loadClient(): Promise { logger.log("Using a standalone client"); - const foci = Config.get().livekit - ? [{ livekitServiceUrl: Config.get().livekit!.livekit_service_url }] - : undefined; - /* eslint-disable camelcase */ const { user_id, device_id, access_token, passwordlessUser } = session; const initClientParams = { @@ -339,7 +335,7 @@ async function loadClient(): Promise { userId: user_id, deviceId: device_id, fallbackICEServerAllowed: fallbackICEServerAllowed, - foci, + livekitServiceURL: Config.get().livekit!.livekit_service_url, }; try { diff --git a/src/livekit/OpenIDLoader.tsx b/src/livekit/OpenIDLoader.tsx index 66b5cc76..911a1ae7 100644 --- a/src/livekit/OpenIDLoader.tsx +++ b/src/livekit/OpenIDLoader.tsx @@ -22,6 +22,7 @@ import { useState, } from "react"; import { logger } from "matrix-js-sdk/src/logger"; +import { GroupCall } from "matrix-js-sdk"; import { OpenIDClientParts, @@ -32,7 +33,7 @@ import { ErrorView, LoadingView } from "../FullScreenView"; interface Props { client: OpenIDClientParts; - livekitServiceURL: string; + groupCall: GroupCall; roomName: string; children: ReactNode; } @@ -41,12 +42,7 @@ const SFUConfigContext = createContext(undefined); export const useSFUConfig = () => useContext(SFUConfigContext); -export function OpenIDLoader({ - client, - livekitServiceURL, - roomName, - children, -}: Props) { +export function OpenIDLoader({ client, groupCall, roomName, children }: Props) { const [state, setState] = useState< SFUConfigLoading | SFUConfigLoaded | SFUConfigFailed >({ kind: "loading" }); @@ -56,7 +52,7 @@ export function OpenIDLoader({ try { const result = await getSFUConfigWithOpenID( client, - livekitServiceURL, + groupCall, roomName ); setState({ kind: "loaded", sfuConfig: result }); @@ -65,7 +61,7 @@ export function OpenIDLoader({ setState({ kind: "failed", error: e as Error }); } })(); - }, [client, livekitServiceURL, roomName]); + }, [client, groupCall, roomName]); switch (state.kind) { case "loading": diff --git a/src/livekit/openIDSFU.ts b/src/livekit/openIDSFU.ts index 88adcc70..3cc61fbb 100644 --- a/src/livekit/openIDSFU.ts +++ b/src/livekit/openIDSFU.ts @@ -14,9 +14,11 @@ See the License for the specific language governing permissions and limitations under the License. */ -import { MatrixClient } from "matrix-js-sdk"; +import { GroupCall, IOpenIDToken, MatrixClient } from "matrix-js-sdk"; import { logger } from "matrix-js-sdk/src/logger"; +import { Config } from "../config/Config"; + export interface SFUConfig { url: string; jwt: string; @@ -30,25 +32,84 @@ export type OpenIDClientParts = Pick< export async function getSFUConfigWithOpenID( client: OpenIDClientParts, - livekitServiceURL: string, + groupCall: GroupCall, roomName: string ): Promise { const openIdToken = await client.getOpenIdToken(); logger.debug("Got openID token", openIdToken); - const res = await fetch(livekitServiceURL + "/sfu/get", { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify({ - room: roomName, - openid_token: openIdToken, - device_id: client.getDeviceId(), - }), - }); - if (!res.ok) { - throw new Error("SFU Config fetch failed with status code " + res.status); + // if the call has a livekit service URL, try it. + if (groupCall.livekitServiceURL) { + try { + logger.info(`Trying to get JWT from ${groupCall.livekitServiceURL}...`); + const sfuConfig = await getLiveKitJWT( + client, + groupCall.livekitServiceURL, + roomName, + openIdToken + ); + + return sfuConfig; + } catch (e) { + logger.warn( + `Failed to get JWT from group call's configured URL of ${groupCall.livekitServiceURL}.`, + e + ); + } + } + + // otherwise, try our configured one and, if it works, update the call's service URL in the state event + // NB. This wuill update it for everyone so we may end up with multiple clients updating this when they + // join at similar times, but we don't have a huge number of options here. + const urlFromConf = Config.get().livekit!.livekit_service_url; + logger.info(`Trying livekit service URL from our config: ${urlFromConf}...`); + try { + const sfuConfig = await getLiveKitJWT( + client, + urlFromConf, + roomName, + openIdToken + ); + + logger.info(`Updating call livekit service URL with: ${urlFromConf}...`); + try { + await groupCall.updateLivekitServiceURL(urlFromConf); + } catch (e) { + logger.warn( + `Failed to update call livekit service URL: continuing anyway.` + ); + } + + return sfuConfig; + } catch (e) { + logger.error("Failed to get JWT from URL defined in Config.", e); + throw e; + } +} + +async function getLiveKitJWT( + client: OpenIDClientParts, + livekitServiceURL: string, + roomName: string, + openIDToken: IOpenIDToken +): Promise { + try { + const res = await fetch(livekitServiceURL + "/sfu/get", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + room: roomName, + openid_token: openIDToken, + device_id: client.getDeviceId(), + }), + }); + if (!res.ok) { + throw new Error("SFU Config fetch failed with status code " + res.status); + } + return await res.json(); + } catch (e) { + throw new Error("SFU Config fetch failed with exception " + e); } - return await res.json(); } diff --git a/src/room/GroupCallView.tsx b/src/room/GroupCallView.tsx index 9afc2ec3..29d14b82 100644 --- a/src/room/GroupCallView.tsx +++ b/src/room/GroupCallView.tsx @@ -36,7 +36,6 @@ import { UserChoices } from "../livekit/useLiveKit"; import { findDeviceByName } from "../media-utils"; import { OpenIDLoader } from "../livekit/OpenIDLoader"; import { ActiveCall } from "./InCallView"; -import { Config } from "../config/Config"; declare global { interface Window { @@ -219,20 +218,13 @@ export function GroupCallView({ undefined ); - const livekitServiceURL = - groupCall.foci[0]?.livekitServiceUrl ?? - Config.get().livekit?.livekit_service_url; - if (!livekitServiceURL) { - return ; - } - if (error) { return ; } else if (state === GroupCallState.Entered && userChoices) { return ( { // message so that we can use the widget API in less racy mode, but we need to change // element-web to use waitForIFrameLoad=false. Once that change has rolled out, // we can just start the client after we've fetched the config. - foci: [], + livekitServiceURL: undefined, } ); @@ -178,9 +178,7 @@ export const widget: WidgetHelpers | null = (() => { // Now we've fetched the config, be evil and use the getter to inject the focus // into the client (see above XXX). if (focus) { - client.getFoci().push({ - livekitServiceUrl: livekit.livekit_service_url, - }); + client.setLivekitServiceURL(livekit.livekit_service_url); } await client.startClient(); resolve(client); diff --git a/yarn.lock b/yarn.lock index 7073bdf9..d8207984 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2187,9 +2187,9 @@ clsx "^1.2.1" "@matrix-org/matrix-sdk-crypto-js@^0.1.1": - version "0.1.2" - resolved "https://registry.yarnpkg.com/@matrix-org/matrix-sdk-crypto-js/-/matrix-sdk-crypto-js-0.1.2.tgz#b58679e161f3d734359a8665922956309b1a4417" - integrity sha512-bbal0RcWwerS/DgqhOgM7wkXJ2YSv9fySK/qgLlrAsdYLpMSTqG8wDQ89/v+RYo9WmA5hwUN/wXcCDdFaFEXQQ== + version "0.1.3" + resolved "https://registry.yarnpkg.com/@matrix-org/matrix-sdk-crypto-js/-/matrix-sdk-crypto-js-0.1.3.tgz#19981e7613d3673d07c885a98d39276b5fe74ef0" + integrity sha512-RcRlE3wcMnE5ijACHIHmhXFogEEJdIcb/CbJ4rK1PCMduQ4yvxycVpMxwh7aKxFNitZbHZLCK7TfRzUpzjU2tw== "@matrix-org/olm@https://gitlab.matrix.org/api/v4/projects/27/packages/npm/@matrix-org/olm/-/@matrix-org/olm-3.2.14.tgz": version "3.2.14" @@ -11012,9 +11012,9 @@ matrix-events-sdk@0.0.1: resolved "https://registry.yarnpkg.com/matrix-events-sdk/-/matrix-events-sdk-0.0.1.tgz#c8c38911e2cb29023b0bbac8d6f32e0de2c957dd" integrity sha512-1QEOsXO+bhyCroIe2/A5OwaxHvBm7EsSQ46DEDn8RBIfQwN5HWBpFvyWWR4QY0KHPPnnJdI99wgRiAl7Ad5qaA== -"matrix-js-sdk@github:matrix-org/matrix-js-sdk#d79d9ae69c3220c02406706d4a1ec52c22c44fbd": +"matrix-js-sdk@github:matrix-org/matrix-js-sdk#b698217445318f453e0b1086364a33113eaa85d9": version "26.2.0" - resolved "https://codeload.github.com/matrix-org/matrix-js-sdk/tar.gz/d79d9ae69c3220c02406706d4a1ec52c22c44fbd" + resolved "https://codeload.github.com/matrix-org/matrix-js-sdk/tar.gz/b698217445318f453e0b1086364a33113eaa85d9" dependencies: "@babel/runtime" "^7.12.5" "@matrix-org/matrix-sdk-crypto-js" "^0.1.1" From 4a4c66caf2ab947ea1d02f15591c77304e2e5451 Mon Sep 17 00:00:00 2001 From: David Baker Date: Thu, 13 Jul 2023 09:55:33 +0100 Subject: [PATCH 15/59] Update README with new dev backend instructions. --- README.md | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 8210bf77..5545d44e 100644 --- a/README.md +++ b/README.md @@ -88,15 +88,12 @@ yarn dev ### Backend -Add in you `.env` in root dir with: +A docker compose file is provided to start a LiveKit server and auth +service for development. These use a test 'secret' published in this +repository, so this must be used only for local development and +***never be exposed to the public Internet.*** -```yaml -# Develop backend settings: -LIVEKIT_KEY="devkey" -LIVEKIT_SECRET="secret" -``` - -Add SFU parameter in your local config `./public/config.yml`: +To use it, add SFU parameter in your local config `./public/config.yml`: ```yaml "livekit": { From c99a400dd1181183c7d516b5a66547c484e28794 Mon Sep 17 00:00:00 2001 From: David Baker Date: Thu, 13 Jul 2023 09:58:56 +0100 Subject: [PATCH 16/59] Prettier --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5545d44e..b671ac88 100644 --- a/README.md +++ b/README.md @@ -91,7 +91,7 @@ yarn dev A docker compose file is provided to start a LiveKit server and auth service for development. These use a test 'secret' published in this repository, so this must be used only for local development and -***never be exposed to the public Internet.*** +**_never be exposed to the public Internet._** To use it, add SFU parameter in your local config `./public/config.yml`: From cac06f98525642d737f832b3733bcf3c69b04807 Mon Sep 17 00:00:00 2001 From: Timo <16718859+toger5@users.noreply.github.com> Date: Thu, 13 Jul 2023 11:20:24 +0200 Subject: [PATCH 17/59] Remove unecassary state for lobby device initialization (#1238) * remove unecassary state Signed-off-by: Timo K * hotfix Signed-off-by: Timo K * remove video/audioAvailableAndEnabled this is not required anymore since we disable the button. Signed-off-by: Timo K --------- Signed-off-by: Timo K --- src/room/VideoPreview.tsx | 38 +++++++++----------------------------- 1 file changed, 9 insertions(+), 29 deletions(-) diff --git a/src/room/VideoPreview.tsx b/src/room/VideoPreview.tsx index f76c8e5b..ae5e311c 100644 --- a/src/room/VideoPreview.tsx +++ b/src/room/VideoPreview.tsx @@ -66,11 +66,6 @@ export function VideoPreview({ matrixInfo, onUserChoicesChanged }: Props) { const [videoEnabled, setVideoEnabled] = useState(true); const [audioEnabled, setAudioEnabled] = useState(true); - // we store if the tracks are currently initializing to not show them as muted. - // showing them as muted while they are not yet available makes the buttons flicker undesirable during startup. - const [initializingVideo, setInitializingVideo] = useState(true); - const [initializingAudio, setInitializingAudio] = useState(true); - // The settings are updated as soon as the device changes. We wrap the settings value in a ref to store their initial value. // Not changing the device options prohibits the usePreviewTracks hook to recreate the tracks. const initialDefaultDevices = useRef(useDefaultDevices()[0]); @@ -99,32 +94,23 @@ export function VideoPreview({ matrixInfo, onUserChoicesChanged }: Props) { const requestPermissions = !!audioTrack && !!videoTrack; const mediaSwitcher = useMediaDevicesSwitcher( undefined, - { - videoTrack, - audioTrack, - }, + { videoTrack, audioTrack }, requestPermissions ); const { videoIn, audioIn } = mediaSwitcher; const videoEl = React.useRef(null); - // pretend the video is available until the initialization is over - const videoAvailableAndEnabled = - videoEnabled && (!!videoTrack || initializingVideo); - const audioAvailableAndEnabled = - audioEnabled && (!!videoTrack || initializingAudio); - useEffect(() => { // Effect to update the settings onUserChoicesChanged({ video: { selectedId: videoIn.selectedId, - enabled: videoAvailableAndEnabled, + enabled: videoEnabled && !!videoTrack, }, audio: { selectedId: audioIn.selectedId, - enabled: audioAvailableAndEnabled, + enabled: audioEnabled && !!audioTrack, }, }); }, [ @@ -133,24 +119,18 @@ export function VideoPreview({ matrixInfo, onUserChoicesChanged }: Props) { videoEnabled, audioIn.selectedId, audioEnabled, - videoAvailableAndEnabled, - audioAvailableAndEnabled, + videoTrack, + audioTrack, ]); useEffect(() => { // Effect to update the initial device selection for the ui elements based on the current preview track. if (!videoIn.selectedId || videoIn.selectedId == "") { - if (videoTrack) { - setInitializingVideo(false); - } videoTrack?.getDeviceId().then((videoId) => { videoIn.setSelected(videoId ?? "default"); }); } if (!audioIn.selectedId || audioIn.selectedId == "") { - if (audioTrack) { - setInitializingAudio(false); - } audioTrack?.getDeviceId().then((audioId) => { // getDeviceId() can return undefined for audio devices. This happens if // the devices list uses "default" as the device id for the current @@ -197,13 +177,13 @@ export function VideoPreview({ matrixInfo, onUserChoicesChanged }: Props) { )}
setAudioEnabled(!audioAvailableAndEnabled)} + muted={!audioEnabled} + onPress={() => setAudioEnabled(!audioEnabled)} disabled={!audioTrack} /> setVideoEnabled(!videoAvailableAndEnabled)} + muted={!videoEnabled} + onPress={() => setVideoEnabled(!videoEnabled)} disabled={!videoTrack} /> From 64becad7ed66216a9afe6edb634615eaa7e3fb28 Mon Sep 17 00:00:00 2001 From: Vri Date: Wed, 12 Jul 2023 08:09:09 +0000 Subject: [PATCH 18/59] Translated using Weblate (German) Currently translated at 100.0% (118 of 118 strings) Translation: Element Call/element-call Translate-URL: https://translate.element.io/projects/element-call/element-call/de/ --- public/locales/de/app.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/public/locales/de/app.json b/public/locales/de/app.json index 3c9a08cf..99866dc5 100644 --- a/public/locales/de/app.json +++ b/public/locales/de/app.json @@ -115,5 +115,6 @@ "Show connection stats": "Verbindungsstatistiken zeigen", "By clicking \"Join call now\", you agree to our <2>End User Licensing Agreement (EULA)": "Mit einem Klick auf „Anruf beitreten“ akzeptierst du unseren <2>Endbenutzer-Lizenzvertrag (EULA)", "By clicking \"Go\", you agree to our <2>End User Licensing Agreement (EULA)": "Mit einem Klick auf „Los geht’s“ akzeptierst du unseren <2>Endbenutzer-Lizenzvertrag (EULA)", - "This site is protected by ReCAPTCHA and the Google <2>Privacy Policy and <6>Terms of Service apply.<9>By clicking \"Register\", you agree to our <12>End User Licensing Agreement (EULA)": "Diese Seite wird durch reCAPTCHA geschützt und es gelten Googles <2>Datenschutzerklärung und <6>Nutzungsbedingungen. <9>Mit einem Klick auf „Registrieren“ akzeptierst du unseren <2>Endbenutzer-Lizenzvertrag (EULA)" + "This site is protected by ReCAPTCHA and the Google <2>Privacy Policy and <6>Terms of Service apply.<9>By clicking \"Register\", you agree to our <12>End User Licensing Agreement (EULA)": "Diese Seite wird durch reCAPTCHA geschützt und es gelten Googles <2>Datenschutzerklärung und <6>Nutzungsbedingungen. <9>Mit einem Klick auf „Registrieren“ akzeptierst du unseren <2>Endbenutzer-Lizenzvertrag (EULA)", + "Element Call is temporarily not end-to-end encrypted while we test scalability.": "Element Call ist temporär nicht Ende-zu-Ende-verschlüsselt, während wir die Skalierbarkeit testen." } From e36839f1ce8763de58c06c2d427437f8f0116a24 Mon Sep 17 00:00:00 2001 From: Linerly Date: Wed, 12 Jul 2023 12:03:29 +0000 Subject: [PATCH 19/59] Translated using Weblate (Indonesian) Currently translated at 100.0% (118 of 118 strings) Translation: Element Call/element-call Translate-URL: https://translate.element.io/projects/element-call/element-call/id/ --- public/locales/id/app.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/public/locales/id/app.json b/public/locales/id/app.json index 1ead563a..e64c08b4 100644 --- a/public/locales/id/app.json +++ b/public/locales/id/app.json @@ -115,5 +115,6 @@ "{{count}} stars|other": "{{count}} bintang", "By clicking \"Go\", you agree to our <2>End User Licensing Agreement (EULA)": "Dengan mengeklik \"Bergabung\", Anda menyetujui <2>Perjanjian Lisensi Pengguna Akhir (EULA)", "This site is protected by ReCAPTCHA and the Google <2>Privacy Policy and <6>Terms of Service apply.<9>By clicking \"Register\", you agree to our <12>End User Licensing Agreement (EULA)": "Situs ini dilindungi oleh reCAPTCHA dan <2>Kebijakan Privasi dan <6>Ketentuan Layanan Google berlaku.<9>Dengan mengeklik \"Daftar\", Anda menyetujui <12>Perjanjian Lisensi Pengguna Akhir (EULA) kami", - "By clicking \"Join call now\", you agree to our <2>End User Licensing Agreement (EULA)": "Dengan mengeklik \"Bergabung ke panggilan sekarang\", Anda menyetujui <2>Perjanjian Lisensi Pengguna Akhir (EULA) kami" + "By clicking \"Join call now\", you agree to our <2>End User Licensing Agreement (EULA)": "Dengan mengeklik \"Bergabung ke panggilan sekarang\", Anda menyetujui <2>Perjanjian Lisensi Pengguna Akhir (EULA) kami", + "Element Call is temporarily not end-to-end encrypted while we test scalability.": "Element Call sementara tidak dienkripsi secara ujung ke ujung selagi kami menguji skalabilitas." } From 285ae01b1fe55936a6966b426c4d95134ecf18a1 Mon Sep 17 00:00:00 2001 From: Glandos Date: Wed, 12 Jul 2023 07:41:54 +0000 Subject: [PATCH 20/59] Translated using Weblate (French) Currently translated at 100.0% (118 of 118 strings) Translation: Element Call/element-call Translate-URL: https://translate.element.io/projects/element-call/element-call/fr/ --- public/locales/fr/app.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/public/locales/fr/app.json b/public/locales/fr/app.json index 5a942f85..5ac60999 100644 --- a/public/locales/fr/app.json +++ b/public/locales/fr/app.json @@ -115,5 +115,6 @@ "Show connection stats": "Afficher les statistiques de la connexion", "By clicking \"Join call now\", you agree to our <2>End User Licensing Agreement (EULA)": "En cliquant sur « Rejoindre l’appel maintenant », vous acceptez notre <2>Contrat de Licence Utilisateur Final (CLUF)", "By clicking \"Go\", you agree to our <2>End User Licensing Agreement (EULA)": "En cliquant sur « Commencer », vous acceptez notre <2>Contrat de Licence Utilisateur Final (CLUF)", - "This site is protected by ReCAPTCHA and the Google <2>Privacy Policy and <6>Terms of Service apply.<9>By clicking \"Register\", you agree to our <12>End User Licensing Agreement (EULA)": "Ce site est protégé par ReCAPTCHA, la <2>politique de confidentialité et les <6>conditions d’utilisation de Google s’appliquent.<9>En cliquant sur « S’enregistrer » vous acceptez également notre <12>Contrat de Licence Utilisateur Final (CLUF)" + "This site is protected by ReCAPTCHA and the Google <2>Privacy Policy and <6>Terms of Service apply.<9>By clicking \"Register\", you agree to our <12>End User Licensing Agreement (EULA)": "Ce site est protégé par ReCAPTCHA, la <2>politique de confidentialité et les <6>conditions d’utilisation de Google s’appliquent.<9>En cliquant sur « S’enregistrer » vous acceptez également notre <12>Contrat de Licence Utilisateur Final (CLUF)", + "Element Call is temporarily not end-to-end encrypted while we test scalability.": "Element Call n’est temporairement plus chiffré de bout en bout le temps de tester l’extensibilité." } From 873d852609ae0ba79200286ea47ededee6f3f4dc Mon Sep 17 00:00:00 2001 From: Ihor Hordiichuk Date: Wed, 12 Jul 2023 18:38:43 +0000 Subject: [PATCH 21/59] Translated using Weblate (Ukrainian) Currently translated at 100.0% (118 of 118 strings) Translation: Element Call/element-call Translate-URL: https://translate.element.io/projects/element-call/element-call/uk/ --- public/locales/uk/app.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/public/locales/uk/app.json b/public/locales/uk/app.json index be0da18a..af192f88 100644 --- a/public/locales/uk/app.json +++ b/public/locales/uk/app.json @@ -115,5 +115,6 @@ "Show connection stats": "Показати стан з'єднання", "By clicking \"Go\", you agree to our <2>End User Licensing Agreement (EULA)": "Натискаючи \"Далі\", ви погоджуєтеся з нашою <2>Ліцензійною угодою з кінцевим користувачем (EULA)", "By clicking \"Join call now\", you agree to our <2>End User Licensing Agreement (EULA)": "Натискаючи \"Приєднатися до виклику зараз\", ви погоджуєтеся з нашою <2>Ліцензійною угодою з кінцевим користувачем (EULA)", - "This site is protected by ReCAPTCHA and the Google <2>Privacy Policy and <6>Terms of Service apply.<9>By clicking \"Register\", you agree to our <12>End User Licensing Agreement (EULA)": "Цей сайт захищений ReCAPTCHA і до нього застосовується <2>Політика приватності і <6>Умови надання послуг Google.<9>Натискаючи \"Зареєструватися\", ви погоджуєтеся з нашою <12>Ліцензійною угодою з кінцевим користувачем (EULA)" + "This site is protected by ReCAPTCHA and the Google <2>Privacy Policy and <6>Terms of Service apply.<9>By clicking \"Register\", you agree to our <12>End User Licensing Agreement (EULA)": "Цей сайт захищений ReCAPTCHA і до нього застосовується <2>Політика приватності і <6>Умови надання послуг Google.<9>Натискаючи \"Зареєструватися\", ви погоджуєтеся з нашою <12>Ліцензійною угодою з кінцевим користувачем (EULA)", + "Element Call is temporarily not end-to-end encrypted while we test scalability.": "Виклики Element тимчасово не захищаються наскрізним шифруванням, поки ми тестуємо масштабованість." } From ed3c11da5a0eae03a3b36ee29ef6c61c314d74b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Priit=20J=C3=B5er=C3=BC=C3=BCt?= Date: Wed, 12 Jul 2023 06:29:46 +0000 Subject: [PATCH 22/59] Translated using Weblate (Estonian) Currently translated at 100.0% (118 of 118 strings) Translation: Element Call/element-call Translate-URL: https://translate.element.io/projects/element-call/element-call/et/ --- public/locales/et/app.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/public/locales/et/app.json b/public/locales/et/app.json index ae6e9154..d8659dbf 100644 --- a/public/locales/et/app.json +++ b/public/locales/et/app.json @@ -115,5 +115,6 @@ "{{displayName}} is presenting": "{{displayName}} on esitlemas", "By clicking \"Go\", you agree to our <2>End User Licensing Agreement (EULA)": "Klõpsides „Jätka“, nõustud sa meie <2>Lõppkasutaja litsentsilepinguga (EULA)", "By clicking \"Join call now\", you agree to our <2>End User Licensing Agreement (EULA)": "Klõpsides „Liitu kõnega kohe“, nõustud sa meie <2>Lõppkasutaja litsentsilepinguga (EULA)", - "This site is protected by ReCAPTCHA and the Google <2>Privacy Policy and <6>Terms of Service apply.<9>By clicking \"Register\", you agree to our <12>End User Licensing Agreement (EULA)": "Selles saidis on kasutusel ReCAPTCHA ja kehtivad Google'i <2>Privaatsuspoliitika ning <6>Teenusetingimused.<9>Klõpsides „Registreeru“, sa nõustud meie <12>Lõppkasutaja litsentsilepingu (EULA) tingimustega" + "This site is protected by ReCAPTCHA and the Google <2>Privacy Policy and <6>Terms of Service apply.<9>By clicking \"Register\", you agree to our <12>End User Licensing Agreement (EULA)": "Selles saidis on kasutusel ReCAPTCHA ja kehtivad Google'i <2>Privaatsuspoliitika ning <6>Teenusetingimused.<9>Klõpsides „Registreeru“, sa nõustud meie <12>Lõppkasutaja litsentsilepingu (EULA) tingimustega", + "Element Call is temporarily not end-to-end encrypted while we test scalability.": "Seni kuni me testime skaleeritavust, siis Element Call ajutiselt pole läbivalt krüptitud." } From 5f23c3b9c67be19e70359099a005bb007cdd39b4 Mon Sep 17 00:00:00 2001 From: Avery Date: Wed, 12 Jul 2023 13:22:11 +0000 Subject: [PATCH 23/59] Translated using Weblate (Spanish) Currently translated at 100.0% (118 of 118 strings) Translation: Element Call/element-call Translate-URL: https://translate.element.io/projects/element-call/element-call/es/ --- public/locales/es/app.json | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/public/locales/es/app.json b/public/locales/es/app.json index d4027923..019f3a1a 100644 --- a/public/locales/es/app.json +++ b/public/locales/es/app.json @@ -98,5 +98,23 @@ "Expose developer settings in the settings window.": "Muestra los ajustes de desarrollador en la ventana de ajustes.", "Developer Settings": "Ajustes de desarrollador", "By participating in this beta, you consent to the collection of anonymous data, which we use to improve the product. You can find more information about which data we track in our <2>Privacy Policy and our <5>Cookie Policy.": "Al participar en esta beta, consientes a la recogida de datos anónimos, los cuales usaremos para mejorar el producto. Puedes encontrar más información sobre que datos recogemos en nuestra <2>Política de privacidad y en nuestra <5>Política sobre Cookies.", - "<0><1>You may withdraw consent by unchecking this box. If you are currently in a call, this setting will take effect at the end of the call.": "<0><1>Puedes retirar tu consentimiento desmarcando esta casilla. Si estás en una llamada, este ajuste se aplicará al final de esta." + "<0><1>You may withdraw consent by unchecking this box. If you are currently in a call, this setting will take effect at the end of the call.": "<0><1>Puedes retirar tu consentimiento desmarcando esta casilla. Si estás en una llamada, este ajuste se aplicará al final de esta.", + "{{displayName}} is presenting": "{{displayName}} está presentando", + "<0>Thanks for your feedback!": "<0>¡Gracias por tus comentarios!", + "How did it go?": "¿Cómo ha ido?", + "This site is protected by ReCAPTCHA and the Google <2>Privacy Policy and <6>Terms of Service apply.<9>By clicking \"Register\", you agree to our <12>End User Licensing Agreement (EULA)": "Este sitio está protegido por ReCAPTCHA y se aplican la <2>Política de Privacidad y los <6>Términos de Servicio de Google.<9>Al hacer clic en \"Registrar\", aceptas nuestro <12>Contrato de Licencia de Usuario Final (CLUF)", + "Show connection stats": "Mostrar estadísticas de conexión", + "Element Call is temporarily not end-to-end encrypted while we test scalability.": "Element Call no está encriptado de extremo a extremo de manera temporal mientras probamos la escalabilidad del servicio.", + "By clicking \"Go\", you agree to our <2>End User Licensing Agreement (EULA)": "Al hacer clic en \"Comenzar\", aceptas nuestro <2>Contrato de Licencia de Usuario Final (CLUF)", + "Thanks, we received your feedback!": "¡Gracias, hemos recibido tus comentarios!", + "If you are experiencing issues or simply would like to provide some feedback, please send us a short description below.": "Si tienes algún problema o simplemente quieres darnos tu opinión, por favor envíanos una breve descripción.", + "By clicking \"Join call now\", you agree to our <2>End User Licensing Agreement (EULA)": "Al hacer clic en \"Unirse a la llamada ahora\", aceptas nuestro <2>Contrato de Licencia de Usuario Final (CLUF)", + "<0>We'd love to hear your feedback so we can improve your experience.": "<0>Nos encantaría conocer tu opinión para que podamos mejorar tu experiencia", + "Feedback": "Danos tu opinión", + "Submit": "Enviar", + "{{count}} stars|one": "{{count}} estrella", + "{{count}} stars|other": "{{count}} estrellas", + "{{displayName}}, your call has ended.": "{{displayName}}, tu llamada ha finalizado.", + "Submitting…": "Enviando…", + "Your feedback": "Tus comentarios" } From 69426176aacdd479c9f96b58f1424ef15835f80c Mon Sep 17 00:00:00 2001 From: Jeff Huang Date: Wed, 12 Jul 2023 01:54:00 +0000 Subject: [PATCH 24/59] Translated using Weblate (Chinese (Traditional)) Currently translated at 100.0% (118 of 118 strings) Translation: Element Call/element-call Translate-URL: https://translate.element.io/projects/element-call/element-call/zh_Hant/ --- public/locales/zh-Hant/app.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/public/locales/zh-Hant/app.json b/public/locales/zh-Hant/app.json index fccd94ab..28eed1a5 100644 --- a/public/locales/zh-Hant/app.json +++ b/public/locales/zh-Hant/app.json @@ -115,5 +115,6 @@ "Show connection stats": "顯示連線統計資料", "By clicking \"Go\", you agree to our <2>End User Licensing Agreement (EULA)": "點擊「前往」即表示您同意我們的<2>終端使用者授權協議 (EULA)", "By clicking \"Join call now\", you agree to our <2>End User Licensing Agreement (EULA)": "點擊「立刻加入通話」即表示您同意我們的<2>終端使用者授權協議 (EULA)", - "This site is protected by ReCAPTCHA and the Google <2>Privacy Policy and <6>Terms of Service apply.<9>By clicking \"Register\", you agree to our <12>End User Licensing Agreement (EULA)": "此網站被 ReCAPTCHA 保護,並適用 Google 的<2>隱私權政策與<6>服務條款。<9>點擊「註冊」即表示您同意我們的<12>終端使用者授權協議 (EULA)" + "This site is protected by ReCAPTCHA and the Google <2>Privacy Policy and <6>Terms of Service apply.<9>By clicking \"Register\", you agree to our <12>End User Licensing Agreement (EULA)": "此網站被 ReCAPTCHA 保護,並適用 Google 的<2>隱私權政策與<6>服務條款。<9>點擊「註冊」即表示您同意我們的<12>終端使用者授權協議 (EULA)", + "Element Call is temporarily not end-to-end encrypted while we test scalability.": "在我們測試可擴展性時,Element Call 暫時未進行端到端加密。" } From 606a4e4bfcca01eef7bdf191b103d15f0c7ec3ee Mon Sep 17 00:00:00 2001 From: Jozef Gaal Date: Wed, 12 Jul 2023 16:10:38 +0000 Subject: [PATCH 25/59] Translated using Weblate (Slovak) Currently translated at 100.0% (118 of 118 strings) Translation: Element Call/element-call Translate-URL: https://translate.element.io/projects/element-call/element-call/sk/ --- public/locales/sk/app.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/public/locales/sk/app.json b/public/locales/sk/app.json index 12737076..6f588a91 100644 --- a/public/locales/sk/app.json +++ b/public/locales/sk/app.json @@ -115,5 +115,6 @@ "Show connection stats": "Zobraziť štatistiky pripojenia", "By clicking \"Join call now\", you agree to our <2>End User Licensing Agreement (EULA)": "Kliknutím na \"Pripojiť sa k hovoru teraz\" súhlasíte s našou <2>Licenčnou zmluvou s koncovým používateľom (EULA)", "By clicking \"Go\", you agree to our <2>End User Licensing Agreement (EULA)": "Kliknutím na tlačidlo \"Prejsť\" vyjadrujete súhlas s našou <2>Licenčnou zmluvou s koncovým používateľom (EULA)", - "This site is protected by ReCAPTCHA and the Google <2>Privacy Policy and <6>Terms of Service apply.<9>By clicking \"Register\", you agree to our <12>End User Licensing Agreement (EULA)": "Táto stránka je chránená systémom ReCAPTCHA a platia na ňu <2>Pravidlá ochrany osobných údajov spoločnosti Google a <6>Podmienky poskytovania služieb.<9>Kliknutím na tlačidlo \"Registrovať sa\" súhlasíte s našou <12>Licenčnou zmluvou s koncovým používateľom (EULA)" + "This site is protected by ReCAPTCHA and the Google <2>Privacy Policy and <6>Terms of Service apply.<9>By clicking \"Register\", you agree to our <12>End User Licensing Agreement (EULA)": "Táto stránka je chránená systémom ReCAPTCHA a platia na ňu <2>Pravidlá ochrany osobných údajov spoločnosti Google a <6>Podmienky poskytovania služieb.<9>Kliknutím na tlačidlo \"Registrovať sa\" súhlasíte s našou <12>Licenčnou zmluvou s koncovým používateľom (EULA)", + "Element Call is temporarily not end-to-end encrypted while we test scalability.": "Element Call nie je dočasne šifrovaný, kým testujeme škálovateľnosť." } From 3c30a5812baeff309951d3c44ddb3e6257fe7865 Mon Sep 17 00:00:00 2001 From: Robin Townsend Date: Fri, 14 Jul 2023 12:41:21 -0400 Subject: [PATCH 26/59] Fix registration not logging you into the new account --- src/auth/RegisterPage.tsx | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/auth/RegisterPage.tsx b/src/auth/RegisterPage.tsx index a486aa46..c49d9b43 100644 --- a/src/auth/RegisterPage.tsx +++ b/src/auth/RegisterPage.tsx @@ -80,11 +80,7 @@ export const RegisterPage: FC = () => { passwordlessUser ); - if (!client || !client.groupCallEventHandler || !setClient) { - return; - } - - if (passwordlessUser) { + if (client && client?.groupCallEventHandler && passwordlessUser) { // Migrate the user's rooms for (const groupCall of client.groupCallEventHandler.groupCalls.values()) { const roomId = groupCall.room.roomId; @@ -107,7 +103,7 @@ export const RegisterPage: FC = () => { } } - setClient({ client: newClient, session }); + setClient?.({ client: newClient, session }); PosthogAnalytics.instance.eventSignup.cacheSignupEnd(new Date()); }; From be340ca4e4b02d870165bce7f02bad81e884b400 Mon Sep 17 00:00:00 2001 From: David Baker Date: Fri, 14 Jul 2023 18:42:31 +0100 Subject: [PATCH 27/59] Add more logs to JWT service code --- src/livekit/openIDSFU.ts | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/livekit/openIDSFU.ts b/src/livekit/openIDSFU.ts index 3cc61fbb..083e95b2 100644 --- a/src/livekit/openIDSFU.ts +++ b/src/livekit/openIDSFU.ts @@ -41,13 +41,16 @@ export async function getSFUConfigWithOpenID( // if the call has a livekit service URL, try it. if (groupCall.livekitServiceURL) { try { - logger.info(`Trying to get JWT from ${groupCall.livekitServiceURL}...`); + logger.info( + `Trying to get JWT from call's configured URL of ${groupCall.livekitServiceURL}...` + ); const sfuConfig = await getLiveKitJWT( client, groupCall.livekitServiceURL, roomName, openIdToken ); + logger.info(`Got JWT from call state event URL.`); return sfuConfig; } catch (e) { @@ -71,9 +74,12 @@ export async function getSFUConfigWithOpenID( openIdToken ); - logger.info(`Updating call livekit service URL with: ${urlFromConf}...`); + logger.info( + `Got JWT, updating call livekit service URL with: ${urlFromConf}...` + ); try { await groupCall.updateLivekitServiceURL(urlFromConf); + logger.info(`Call livekit service URL updated.`); } catch (e) { logger.warn( `Failed to update call livekit service URL: continuing anyway.` From 72eca769a42df7743be15f7f972d6a6555a99357 Mon Sep 17 00:00:00 2001 From: Robin Townsend Date: Fri, 14 Jul 2023 14:08:42 -0400 Subject: [PATCH 28/59] Put the settings tabs back in the right order --- src/settings/SettingsModal.tsx | 154 ++++++++++++++++++--------------- 1 file changed, 84 insertions(+), 70 deletions(-) diff --git a/src/settings/SettingsModal.tsx b/src/settings/SettingsModal.tsx index 19379cb4..82d32338 100644 --- a/src/settings/SettingsModal.tsx +++ b/src/settings/SettingsModal.tsx @@ -118,7 +118,7 @@ export const SettingsModal = (props: Props) => { const devices = props.mediaDevicesSwitcher; - const tabs = [ + const audioTab = ( { > {devices && generateDeviceSelection(devices.audioIn, t("Microphone"))} {devices && generateDeviceSelection(devices.audioOut, t("Speaker"))} - , + + ); + + const videoTab = ( { } > {devices && generateDeviceSelection(devices.videoIn, t("Camera"))} - , + + ); + + const profileTab = ( + + + {t("Profile")} + + } + > + + + ); + + const feedbackTab = ( { } > - , + + ); + + const moreTab = ( { }} /> - , + + ); + + const developerTab = ( + + + {t("Developer")} + + } + > + + + {t("Version: {{version}}", { + version: import.meta.env.VITE_APP_VERSION || "dev", + })} + + + + ) => + setShowInspector(e.target.checked) + } + /> + + + ) => + setShowConnectionStats(e.target.checked) + } + /> + + + + + + ); + + const tabs = [ + audioTab, + videoTab, + ...(isEmbedded ? [] : [profileTab]), + feedbackTab, + moreTab, + ...(developerSettingsTab ? [developerTab] : []), ]; - if (!isEmbedded) { - tabs.push( - - - {t("Profile")} - - } - > - - - ); - } - - if (developerSettingsTab) { - tabs.push( - - - {t("Developer")} - - } - > - - - {t("Version: {{version}}", { - version: import.meta.env.VITE_APP_VERSION || "dev", - })} - - - - ) => - setShowInspector(e.target.checked) - } - /> - - - ) => - setShowConnectionStats(e.target.checked) - } - /> - - - - - - ); - } - return ( Date: Fri, 14 Jul 2023 14:46:45 -0400 Subject: [PATCH 29/59] Fix avatar field in profile settings being hidden It was being hidden when you didn't yet have an avatar set, which made it impossible to ever set one. --- src/input/AvatarInputField.tsx | 2 +- src/settings/ProfileSettingsTab.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/input/AvatarInputField.tsx b/src/input/AvatarInputField.tsx index 484f440e..0218258e 100644 --- a/src/input/AvatarInputField.tsx +++ b/src/input/AvatarInputField.tsx @@ -34,7 +34,7 @@ import styles from "./AvatarInputField.module.css"; interface Props extends AllHTMLAttributes { id: string; label: string; - avatarUrl: string; + avatarUrl: string | undefined; displayName: string; onRemoveAvatar: () => void; } diff --git a/src/settings/ProfileSettingsTab.tsx b/src/settings/ProfileSettingsTab.tsx index 8de8dd52..e6a59634 100644 --- a/src/settings/ProfileSettingsTab.tsx +++ b/src/settings/ProfileSettingsTab.tsx @@ -77,7 +77,7 @@ export function ProfileSettingsTab({ client }: Props) { return (
- {avatarUrl && displayName && ( + {displayName && ( Date: Mon, 17 Jul 2023 12:41:46 +0100 Subject: [PATCH 30/59] Remove redundant server url from readme example --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index b671ac88..4133ed31 100644 --- a/README.md +++ b/README.md @@ -97,7 +97,6 @@ To use it, add SFU parameter in your local config `./public/config.yml`: ```yaml "livekit": { - "server_url": "ws://localhost:7880", "jwt_service_url": "http:/localhost:8881" }, ``` From f28cf0ce699dd2f8391afc9251371754b78746b1 Mon Sep 17 00:00:00 2001 From: David Baker Date: Mon, 17 Jul 2023 19:16:28 +0100 Subject: [PATCH 31/59] Fix embedded mode This was trying to get the room alias, which causes the config to be read. We don't need the room alias here though, so pass the flag to not return it. --- src/widget.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/widget.ts b/src/widget.ts index 93107fcd..fc7e529b 100644 --- a/src/widget.ts +++ b/src/widget.ts @@ -109,7 +109,7 @@ export const widget: WidgetHelpers | null = (() => { baseUrl, e2eEnabled, allowIceFallback, - } = getUrlParams(); + } = getUrlParams(true); if (!roomId) throw new Error("Room ID must be supplied"); if (!userId) throw new Error("User ID must be supplied"); if (!deviceId) throw new Error("Device ID must be supplied"); From c15b2a62d5d8a69a40ebe247d1168233616c1b77 Mon Sep 17 00:00:00 2001 From: David Baker Date: Mon, 17 Jul 2023 19:22:22 +0100 Subject: [PATCH 32/59] Fix jsdoc --- src/UrlParams.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/UrlParams.ts b/src/UrlParams.ts index f41e3cfa..4a37254a 100644 --- a/src/UrlParams.ts +++ b/src/UrlParams.ts @@ -90,8 +90,10 @@ interface UrlParams { /** * Gets the app parameters for the current URL. - * @param query The URL query string - * @param fragment The URL fragment string + * @param ignoreRoomAlias If true, does not try to parse a room alias from the URL + * @param search The URL search string + * @param pathname The URL path name + * @param hash The URL hash * @returns The app parameters encoded in the URL */ export const getUrlParams = ( From 4eb9326bd2079b87262181dcad6320f2d645e921 Mon Sep 17 00:00:00 2001 From: David Baker Date: Mon, 17 Jul 2023 19:33:29 +0100 Subject: [PATCH 33/59] Enable debug logging Fixes https://github.com/vector-im/element-call/issues/1267 --- src/livekit/useLiveKit.ts | 4 +++- vite.config.js | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/livekit/useLiveKit.ts b/src/livekit/useLiveKit.ts index 7a0a5330..dac6a052 100644 --- a/src/livekit/useLiveKit.ts +++ b/src/livekit/useLiveKit.ts @@ -1,4 +1,4 @@ -import { Room, RoomOptions } from "livekit-client"; +import { Room, RoomOptions, setLogLevel } from "livekit-client"; import { useLiveKitRoom } from "@livekit/components-react"; import { useMemo } from "react"; @@ -15,6 +15,8 @@ export type DeviceChoices = { enabled: boolean; }; +setLogLevel("debug"); + export function useLiveKit( userChoices: UserChoices, sfuConfig?: SFUConfig diff --git a/vite.config.js b/vite.config.js index bbebd667..13274776 100644 --- a/vite.config.js +++ b/vite.config.js @@ -27,7 +27,7 @@ export default defineConfig(({ mode }) => { const plugins = [ react(), - basicSsl(), + //basicSsl(), svgrPlugin(), htmlTemplate.default({ data: { From b7f5069e9854eefbffe5d3e3d0424961a0b2af17 Mon Sep 17 00:00:00 2001 From: Timo <16718859+toger5@users.noreply.github.com> Date: Tue, 18 Jul 2023 17:41:54 +0200 Subject: [PATCH 34/59] set video enabled even if tracks are not yet initialized (#1273) This could fix "muted on join issues" but could introduce issues where the buttons show unmuted even if no device is available. Signed-off-by: Timo K --- src/room/VideoPreview.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/room/VideoPreview.tsx b/src/room/VideoPreview.tsx index ae5e311c..5fac133e 100644 --- a/src/room/VideoPreview.tsx +++ b/src/room/VideoPreview.tsx @@ -106,11 +106,11 @@ export function VideoPreview({ matrixInfo, onUserChoicesChanged }: Props) { onUserChoicesChanged({ video: { selectedId: videoIn.selectedId, - enabled: videoEnabled && !!videoTrack, + enabled: videoEnabled, }, audio: { selectedId: audioIn.selectedId, - enabled: audioEnabled && !!audioTrack, + enabled: audioEnabled, }, }); }, [ From 50ba23b7ea43a2f3b610a0510a61075d37c3e19d Mon Sep 17 00:00:00 2001 From: David Baker Date: Tue, 18 Jul 2023 16:59:51 +0100 Subject: [PATCH 35/59] Revert unintentional change --- vite.config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vite.config.js b/vite.config.js index 13274776..bbebd667 100644 --- a/vite.config.js +++ b/vite.config.js @@ -27,7 +27,7 @@ export default defineConfig(({ mode }) => { const plugins = [ react(), - //basicSsl(), + basicSsl(), svgrPlugin(), htmlTemplate.default({ data: { From 16757701963e0391a96057b94dbc1e4231754e2f Mon Sep 17 00:00:00 2001 From: David Baker Date: Thu, 20 Jul 2023 17:55:50 +0100 Subject: [PATCH 36/59] Add disconnected screen for when livekit disconnects from the call eg. when it gives up retrying to connect Also refactors the rageshake button into a dedicated component that we can just put wherever we want. Fixes https://github.com/vector-im/element-call/issues/1268 --- src/FullScreenView.tsx | 36 +---------- src/room/CallEndedView.module.css | 9 +++ src/room/CallEndedView.tsx | 83 ++++++++++++++++++------- src/room/GroupCallView.tsx | 76 +++++++++++++--------- src/room/InCallView.tsx | 29 ++++++++- src/settings/RageshakeButton.module.css | 22 +++++++ src/settings/RageshakeButton.tsx | 67 ++++++++++++++++++++ 7 files changed, 232 insertions(+), 90 deletions(-) create mode 100644 src/settings/RageshakeButton.module.css create mode 100644 src/settings/RageshakeButton.tsx diff --git a/src/FullScreenView.tsx b/src/FullScreenView.tsx index 95a8a7ae..e3a5403c 100644 --- a/src/FullScreenView.tsx +++ b/src/FullScreenView.tsx @@ -21,11 +21,10 @@ import { Trans, useTranslation } from "react-i18next"; import { Header, HeaderLogo, LeftNav, RightNav } from "./Header"; import { LinkButton, Button } from "./button"; -import { useSubmitRageshake } from "./settings/submit-rageshake"; -import { ErrorMessage } from "./input/Input"; import styles from "./FullScreenView.module.css"; -import { translatedError, TranslatedError } from "./TranslatedError"; +import { TranslatedError } from "./TranslatedError"; import { Config } from "./config/Config"; +import { RageshakeButton } from "./settings/RageshakeButton"; interface FullScreenViewProps { className?: string; @@ -97,37 +96,11 @@ export function ErrorView({ error }: ErrorViewProps) { export function CrashView() { const { t } = useTranslation(); - const { submitRageshake, sending, sent, error } = useSubmitRageshake(); - - const sendDebugLogs = useCallback(() => { - submitRageshake({ - description: "**Soft Crash**", - sendLogs: true, - }); - }, [submitRageshake]); const onReload = useCallback(() => { window.location.href = "/"; }, []); - let logsComponent: JSX.Element | null = null; - if (sent) { - logsComponent =
{t("Thanks! We'll get right on it.")}
; - } else if (sending) { - logsComponent =
{t("Sending…")}
; - } else if (Config.get().rageshake?.submit_url) { - logsComponent = ( - - ); - } - return ( @@ -139,10 +112,7 @@ export function CrashView() { )} -
{logsComponent}
- {error && ( - - )} + +
+ +
+
+ + + + {t("Return to home screen")} + + + + ); + } else { + return ( + <> +
+ + {surveySubmitted + ? t("{{displayName}}, your call has ended.", { + displayName, + }) + : t("{{displayName}}, your call has ended.", { + displayName, + }) + + "\n" + + t("How did it go?")} + + {!surveySubmitted && PosthogAnalytics.instance.isEnabled() + ? qualitySurveyDialog + : createAccountDialog} +
+ + + {t("Not now, return to home screen")} + + + + ); + } + }; + return ( <>
@@ -146,29 +205,7 @@ export function CallEndedView({
-
-
- - {surveySubmitted - ? t("{{displayName}}, your call has ended.", { - displayName, - }) - : t("{{displayName}}, your call has ended.", { - displayName, - }) + - "\n" + - t("How did it go?")} - - {!surveySubmitted && PosthogAnalytics.instance.isEnabled() - ? qualitySurveyDialog - : createAccountDialog} -
- - - {t("Not now, return to home screen")} - - -
+
{renderBody()}
); } diff --git a/src/room/GroupCallView.tsx b/src/room/GroupCallView.tsx index 29d14b82..da7cb768 100644 --- a/src/room/GroupCallView.tsx +++ b/src/room/GroupCallView.tsx @@ -163,42 +163,47 @@ export function GroupCallView({ useSentryGroupCallHandler(groupCall); const [left, setLeft] = useState(false); + const [leaveError, setLeaveError] = useState(undefined); const history = useHistory(); - const onLeave = useCallback(async () => { - setLeft(true); + const onLeave = useCallback( + async (leaveError?: Error) => { + setLeaveError(leaveError); + setLeft(true); - let participantCount = 0; - for (const deviceMap of groupCall.participants.values()) { - participantCount += deviceMap.size; - } + let participantCount = 0; + for (const deviceMap of groupCall.participants.values()) { + participantCount += deviceMap.size; + } - // In embedded/widget mode the iFrame will be killed right after the call ended prohibiting the posthog event from getting sent, - // therefore we want the event to be sent instantly without getting queued/batched. - const sendInstantly = !!widget; - PosthogAnalytics.instance.eventCallEnded.track( - groupCall.groupCallId, - participantCount, - sendInstantly - ); + // In embedded/widget mode the iFrame will be killed right after the call ended prohibiting the posthog event from getting sent, + // therefore we want the event to be sent instantly without getting queued/batched. + const sendInstantly = !!widget; + PosthogAnalytics.instance.eventCallEnded.track( + groupCall.groupCallId, + participantCount, + sendInstantly + ); - leave(); - if (widget) { - // we need to wait until the callEnded event is tracked. Otherwise the iFrame gets killed before the callEnded event got tracked. - await new Promise((resolve) => window.setTimeout(resolve, 10)); // 10ms - widget.api.setAlwaysOnScreen(false); - PosthogAnalytics.instance.logout(); - widget.api.transport.send(ElementWidgetActions.HangupCall, {}); - } + leave(); + if (widget) { + // we need to wait until the callEnded event is tracked. Otherwise the iFrame gets killed before the callEnded event got tracked. + await new Promise((resolve) => window.setTimeout(resolve, 10)); // 10ms + widget.api.setAlwaysOnScreen(false); + PosthogAnalytics.instance.logout(); + widget.api.transport.send(ElementWidgetActions.HangupCall, {}); + } - if ( - !isPasswordlessUser && - !isEmbedded && - !PosthogAnalytics.instance.isEnabled() - ) { - history.push("/"); - } - }, [groupCall, leave, isPasswordlessUser, isEmbedded, history]); + if ( + !isPasswordlessUser && + !isEmbedded && + !PosthogAnalytics.instance.isEnabled() + ) { + history.push("/"); + } + }, + [groupCall, leave, isPasswordlessUser, isEmbedded, history] + ); useEffect(() => { if (widget && state === GroupCallState.Entered) { @@ -218,6 +223,12 @@ export function GroupCallView({ undefined ); + const onReconnect = useCallback(() => { + setLeft(false); + setLeaveError(undefined); + groupCall.enter(); + }, [groupCall]); + if (error) { return ; } else if (state === GroupCallState.Entered && userChoices) { @@ -248,13 +259,16 @@ export function GroupCallView({ // submitting anything. if ( isPasswordlessUser || - (PosthogAnalytics.instance.isEnabled() && !isEmbedded) + (PosthogAnalytics.instance.isEnabled() && !isEmbedded) || + leaveError ) { return ( ); } else { diff --git a/src/room/InCallView.tsx b/src/room/InCallView.tsx index b8284975..30458599 100644 --- a/src/room/InCallView.tsx +++ b/src/room/InCallView.tsx @@ -24,7 +24,7 @@ import { } from "@livekit/components-react"; import { usePreventScroll } from "@react-aria/overlays"; import classNames from "classnames"; -import { Room, Track } from "livekit-client"; +import { DisconnectReason, Room, RoomEvent, Track } from "livekit-client"; import { MatrixClient } from "matrix-js-sdk/src/client"; import { RoomMember } from "matrix-js-sdk/src/models/room-member"; import { GroupCall } from "matrix-js-sdk/src/webrtc/groupCall"; @@ -33,6 +33,7 @@ import { useTranslation } from "react-i18next"; import useMeasure from "react-use-measure"; import { OverlayTriggerState } from "@react-stately/overlays"; import { JoinRule } from "matrix-js-sdk/src/@types/partials"; +import { logger } from "matrix-js-sdk/src/logger"; import type { IWidgetApiRequest } from "matrix-widget-api"; import { @@ -114,7 +115,7 @@ export interface InCallViewProps { groupCall: GroupCall; livekitRoom: Room; participants: Map>; - onLeave: () => void; + onLeave: (error?: Error) => void; unencryptedEventsFromUsers: Set; hideHeader: boolean; otelGroupCallMembership?: OTelGroupCallMembership; @@ -188,6 +189,28 @@ export function InCallView({ async (muted) => await localParticipant.setMicrophoneEnabled(!muted) ); + const onDisconnected = useCallback( + (reason?: DisconnectReason) => { + logger.info("Disconnected from livekit call with reason ", reason); + onLeave( + new Error("Disconnected from LiveKit call with reason " + reason) + ); + }, + [onLeave] + ); + + const onLeavePress = useCallback(() => { + onLeave(); + }, [onLeave]); + + useEffect(() => { + livekitRoom.on(RoomEvent.Disconnected, onDisconnected); + + return () => { + livekitRoom.off(RoomEvent.Disconnected, onDisconnected); + }; + }, [onDisconnected, livekitRoom]); + useEffect(() => { widget?.api.transport.send( layout === "freedom" @@ -384,7 +407,7 @@ export function InCallView({ } buttons.push( - + ); footer =
{buttons}
; } diff --git a/src/settings/RageshakeButton.module.css b/src/settings/RageshakeButton.module.css new file mode 100644 index 00000000..66d0e2d9 --- /dev/null +++ b/src/settings/RageshakeButton.module.css @@ -0,0 +1,22 @@ +/* +Copyright 2022 New Vector Ltd + +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. +*/ + +.rageshakeControl { + height: 50px; + width: 176px; + text-align: center; + vertical-align: middle; +} diff --git a/src/settings/RageshakeButton.tsx b/src/settings/RageshakeButton.tsx new file mode 100644 index 00000000..b6b045d8 --- /dev/null +++ b/src/settings/RageshakeButton.tsx @@ -0,0 +1,67 @@ +/* +Copyright 2023 New Vector Ltd + +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 { useTranslation } from "react-i18next"; +import { useCallback } from "react"; + +import { Button } from "../button"; +import { Config } from "../config/Config"; +import styles from "./RageshakeButton.module.css"; +import { useSubmitRageshake } from "./submit-rageshake"; + +interface Props { + description: string; +} + +export const RageshakeButton = ({ description }: Props) => { + const { submitRageshake, sending, sent, error } = useSubmitRageshake(); + const { t } = useTranslation(); + + const sendDebugLogs = useCallback(() => { + submitRageshake({ + description, + sendLogs: true, + }); + }, [submitRageshake, description]); + + if (!Config.get().rageshake?.submit_url) return null; + + let logsComponent: JSX.Element | null = null; + if (sent) { + logsComponent =
{t("Thanks!")}
; + } else { + let caption = t("Send debug logs"); + if (error) { + caption = t("Retry sending logs"); + } else if (sending) { + logsComponent = {t("Sending…")}; + } + + logsComponent = ( + + ); + } + + return
{logsComponent}
; +}; From 4071c9029cc9fb080a6d4cdae167be8081adbeb5 Mon Sep 17 00:00:00 2001 From: David Baker Date: Thu, 20 Jul 2023 18:22:17 +0100 Subject: [PATCH 37/59] Track call disconnections --- src/analytics/PosthogAnalytics.ts | 2 ++ src/analytics/PosthogEvents.ts | 16 ++++++++++++++++ src/room/InCallView.tsx | 1 + 3 files changed, 19 insertions(+) diff --git a/src/analytics/PosthogAnalytics.ts b/src/analytics/PosthogAnalytics.ts index 9ef2e70c..0ca23fb3 100644 --- a/src/analytics/PosthogAnalytics.ts +++ b/src/analytics/PosthogAnalytics.ts @@ -30,6 +30,7 @@ import { MuteMicrophoneTracker, UndecryptableToDeviceEventTracker, QualitySurveyEventTracker, + CallDisconnectedEventTracker, } from "./PosthogEvents"; import { Config } from "../config/Config"; import { getUrlParams } from "../UrlParams"; @@ -437,4 +438,5 @@ export class PosthogAnalytics { public eventMuteCamera = new MuteCameraTracker(); public eventUndecryptableToDevice = new UndecryptableToDeviceEventTracker(); public eventQualitySurvey = new QualitySurveyEventTracker(); + public eventCallDisconnected = new CallDisconnectedEventTracker(); } diff --git a/src/analytics/PosthogEvents.ts b/src/analytics/PosthogEvents.ts index f2fecb4e..97e25b6f 100644 --- a/src/analytics/PosthogEvents.ts +++ b/src/analytics/PosthogEvents.ts @@ -14,6 +14,8 @@ See the License for the specific language governing permissions and limitations under the License. */ +import { DisconnectReason } from "livekit-client"; + import { IPosthogEvent, PosthogAnalytics, @@ -181,3 +183,17 @@ export class QualitySurveyEventTracker { }); } } + +interface CallDisconnectedEvent { + eventName: "CallDisconnected"; + reason?: DisconnectReason; +} + +export class CallDisconnectedEventTracker { + track(reason?: DisconnectReason) { + PosthogAnalytics.instance.trackEvent({ + eventName: "CallDisconnected", + reason, + }); + } +} diff --git a/src/room/InCallView.tsx b/src/room/InCallView.tsx index 30458599..604a5d95 100644 --- a/src/room/InCallView.tsx +++ b/src/room/InCallView.tsx @@ -191,6 +191,7 @@ export function InCallView({ const onDisconnected = useCallback( (reason?: DisconnectReason) => { + PosthogAnalytics.instance.eventCallDisconnected.track(reason); logger.info("Disconnected from livekit call with reason ", reason); onLeave( new Error("Disconnected from LiveKit call with reason " + reason) From e37783ad7e778c254b309a44111a42d799442b6a Mon Sep 17 00:00:00 2001 From: David Baker Date: Thu, 20 Jul 2023 18:25:54 +0100 Subject: [PATCH 38/59] i18n --- public/locales/en-GB/app.json | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/public/locales/en-GB/app.json b/public/locales/en-GB/app.json index 5fe1ffa8..2973ac79 100644 --- a/public/locales/en-GB/app.json +++ b/public/locales/en-GB/app.json @@ -76,9 +76,11 @@ "Profile": "Profile", "Recaptcha dismissed": "Recaptcha dismissed", "Recaptcha not loaded": "Recaptcha not loaded", + "Reconnect": "Reconnect", "Register": "Register", "Registering…": "Registering…", "Remove": "Remove", + "Retry sending logs": "Retry sending logs", "Return to home screen": "Return to home screen", "Select an option": "Select an option", "Send debug logs": "Send debug logs", @@ -98,7 +100,7 @@ "Submitting…": "Submitting…", "Take me Home": "Take me Home", "Thanks, we received your feedback!": "Thanks, we received your feedback!", - "Thanks! We'll get right on it.": "Thanks! We'll get right on it.", + "Thanks!": "Thanks!", "This call already exists, would you like to join?": "This call already exists, would you like to join?", "This site is protected by ReCAPTCHA and the Google <2>Privacy Policy and <6>Terms of Service apply.<9>By clicking \"Register\", you agree to our <12>End User Licensing Agreement (EULA)": "This site is protected by ReCAPTCHA and the Google <2>Privacy Policy and <6>Terms of Service apply.<9>By clicking \"Register\", you agree to our <12>End User Licensing Agreement (EULA)", "Turn off camera": "Turn off camera", @@ -115,6 +117,7 @@ "Walkie-talkie call name": "Walkie-talkie call name", "WebRTC is not supported or is being blocked in this browser.": "WebRTC is not supported or is being blocked in this browser.", "Yes, join call": "Yes, join call", + "You were disconnected from the call": "You were disconnected from the call", "Your feedback": "Your feedback", "Your recent calls": "Your recent calls" } From 9200fac28e28650f556b631b64131a691d30ea83 Mon Sep 17 00:00:00 2001 From: David Baker Date: Thu, 20 Jul 2023 18:30:20 +0100 Subject: [PATCH 39/59] Fix URL Co-authored-by: Robin --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 4133ed31..7731d6fe 100644 --- a/README.md +++ b/README.md @@ -97,7 +97,7 @@ To use it, add SFU parameter in your local config `./public/config.yml`: ```yaml "livekit": { - "jwt_service_url": "http:/localhost:8881" + "jwt_service_url": "http://localhost:8881" }, ``` From 8903930d569fe94496fa5c5587681195c8cc7cdd Mon Sep 17 00:00:00 2001 From: Robin Townsend Date: Fri, 21 Jul 2023 00:00:04 -0400 Subject: [PATCH 40/59] Respond to review feedback --- src/settings/SettingsModal.tsx | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/src/settings/SettingsModal.tsx b/src/settings/SettingsModal.tsx index 82d32338..8508af25 100644 --- a/src/settings/SettingsModal.tsx +++ b/src/settings/SettingsModal.tsx @@ -261,14 +261,11 @@ export const SettingsModal = (props: Props) => { ); - const tabs = [ - audioTab, - videoTab, - ...(isEmbedded ? [] : [profileTab]), - feedbackTab, - moreTab, - ...(developerSettingsTab ? [developerTab] : []), - ]; + const tabs: JSX.Element[] = []; + tabs.push(audioTab, videoTab); + if (!isEmbedded) tabs.push(profileTab); + tabs.push(feedbackTab, moreTab); + if (developerSettingsTab) tabs.push(developerTab); return ( Date: Fri, 21 Jul 2023 00:51:07 -0400 Subject: [PATCH 41/59] Stop devices from automatically sleeping A TypeScript upgrade was necessary to bring in the types for the wake lock API. --- package.json | 20 +-- src/room/InCallView.tsx | 2 + src/useWakeLock.ts | 56 ++++++ yarn.lock | 369 +++++++++++++++------------------------- 4 files changed, 202 insertions(+), 245 deletions(-) create mode 100644 src/useWakeLock.ts diff --git a/package.json b/package.json index 7cf8eb1d..5934f89f 100644 --- a/package.json +++ b/package.json @@ -46,9 +46,6 @@ "@react-stately/tree": "^3.2.0", "@sentry/react": "^6.13.3", "@sentry/tracing": "^6.13.3", - "@types/grecaptcha": "^3.0.4", - "@types/react-router-dom": "^5.3.3", - "@types/sdp-transform": "^2.4.5", "@use-gesture/react": "^10.2.11", "@vitejs/plugin-basic-ssl": "^1.0.1", "@vitejs/plugin-react": "^4.0.1", @@ -78,9 +75,7 @@ "sdp-transform": "^2.14.1", "tinyqueue": "^2.0.3", "unique-names-generator": "^4.6.0", - "uuid": "9", - "@types/uuid": "9", - "@types/content-type": "^1.1.5" + "uuid": "9" }, "devDependencies": { "@babel/core": "^7.16.5", @@ -90,8 +85,14 @@ "@testing-library/react": "^13.4.0", "@types/node": "^18.13.0", "@types/request": "^2.48.8", - "@typescript-eslint/eslint-plugin": "^5.52.0", - "@typescript-eslint/parser": "^5.52.0", + "@types/content-type": "^1.1.5", + "@types/dom-screen-wake-lock": "^1.0.1", + "@types/grecaptcha": "^3.0.4", + "@types/react-router-dom": "^5.3.3", + "@types/sdp-transform": "^2.4.5", + "@types/uuid": "9", + "@typescript-eslint/eslint-plugin": "^6.1.0", + "@typescript-eslint/parser": "^6.1.0", "babel-loader": "^8.2.3", "babel-plugin-transform-vite-meta-env": "^1.0.3", "eslint": "^8.14.0", @@ -110,8 +111,7 @@ "prettier": "^2.6.2", "sass": "^1.42.1", "storybook-builder-vite": "^0.1.12", - "typescript": "^4.9.5", - "typescript-strict-plugin": "^2.0.1", + "typescript": "^5.1.6", "vite": "^4.2.0", "vite-plugin-html-template": "^1.1.0", "vite-plugin-svgr": "^3.2.0" diff --git a/src/room/InCallView.tsx b/src/room/InCallView.tsx index b8284975..c45edbc2 100644 --- a/src/room/InCallView.tsx +++ b/src/room/InCallView.tsx @@ -83,6 +83,7 @@ import { useFullscreen } from "./useFullscreen"; import { useLayoutStates } from "../video-grid/Layout"; import { useSFUConfig } from "../livekit/OpenIDLoader"; import { E2EELock } from "../E2EELock"; +import { useWakeLock } from "../useWakeLock"; const canScreenshare = "getDisplayMedia" in (navigator.mediaDevices ?? {}); // There is currently a bug in Safari our our code with cloning and sending MediaStreams @@ -132,6 +133,7 @@ export function InCallView({ }: InCallViewProps) { const { t } = useTranslation(); usePreventScroll(); + useWakeLock(); const containerRef1 = useRef(null); const [containerRef2, bounds] = useMeasure({ polyfill: ResizeObserver }); diff --git a/src/useWakeLock.ts b/src/useWakeLock.ts new file mode 100644 index 00000000..5a8ccb6e --- /dev/null +++ b/src/useWakeLock.ts @@ -0,0 +1,56 @@ +/* +Copyright 2023 New Vector Ltd + +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 { logger } from "matrix-js-sdk/src/logger"; +import { useEffect } from "react"; + +/** + * React hook that inhibits the device from automatically going to sleep. + */ +export const useWakeLock = () => { + useEffect(() => { + if ("wakeLock" in navigator) { + let mounted = true; + let lock: WakeLockSentinel | null = null; + + const onVisiblityChange = async () => { + if (document.visibilityState === "visible") { + try { + lock = await navigator.wakeLock.request("screen"); + if (!mounted) + lock + .release() + .catch((e) => logger.warn("Can't release wake lock", e)); + } catch (e) { + logger.warn("Can't acquire wake lock", e); + } + } + }; + + onVisiblityChange(); + document.addEventListener("visiblitychange", onVisiblityChange); + + return () => { + mounted = false; + if (lock !== null) + lock + .release() + .catch((e) => logger.warn("Can't release wake lock", e)); + document.removeEventListener("visiblitychange", onVisiblityChange); + }; + } + }, []); +}; diff --git a/yarn.lock b/yarn.lock index e35ce34b..e690a5e5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1739,6 +1739,18 @@ resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.17.19.tgz#8cfaf2ff603e9aabb910e9c0558c26cf32744061" integrity sha512-lAhycmKnVOuRYNtRtatQR1LPQf2oYCkRGkSFnseDAKPl8lu5SOsK/e1sXe5a0Pc5kHIHe6P2I/ilntNv2xf3cA== +"@eslint-community/eslint-utils@^4.4.0": + version "4.4.0" + resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz#a23514e8fb9af1269d5f7788aa556798d61c6b59" + integrity sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA== + dependencies: + eslint-visitor-keys "^3.3.0" + +"@eslint-community/regexpp@^4.5.1": + version "4.5.1" + resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.5.1.tgz#cdd35dce4fa1a89a4fd42b1599eb35b3af408884" + integrity sha512-Z5ba73P98O1KUYCCJTUeVpja9RcGoMdncZ6T49FCUl2lN38JtCJ+3WgIDBv0AuY4WChU5PmtJmOCTlN6FZTFKQ== + "@eslint/eslintrc@^1.3.0": version "1.3.0" resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-1.3.0.tgz#29f92c30bb3e771e4a2048c95fa6855392dfac4f" @@ -2205,10 +2217,10 @@ "@react-hook/latest" "^1.0.3" clsx "^1.2.1" -"@matrix-org/matrix-sdk-crypto-js@^0.1.1": - version "0.1.3" - resolved "https://registry.yarnpkg.com/@matrix-org/matrix-sdk-crypto-js/-/matrix-sdk-crypto-js-0.1.3.tgz#19981e7613d3673d07c885a98d39276b5fe74ef0" - integrity sha512-RcRlE3wcMnE5ijACHIHmhXFogEEJdIcb/CbJ4rK1PCMduQ4yvxycVpMxwh7aKxFNitZbHZLCK7TfRzUpzjU2tw== +"@matrix-org/matrix-sdk-crypto-js@^0.1.0": + version "0.1.4" + resolved "https://registry.yarnpkg.com/@matrix-org/matrix-sdk-crypto-js/-/matrix-sdk-crypto-js-0.1.4.tgz#c13c7c8c3a1d8da08e6ad195d25e5e61cc402df7" + integrity sha512-OxG84iSeR89zYLFeb+DCaFtZT+DDiIu+kTkqY8OYfhE5vpGLFX2sDVBRrAdos1IUqEoboDloDBR9+yU7hNRyog== "@matrix-org/olm@https://gitlab.matrix.org/api/v4/projects/27/packages/npm/@matrix-org/olm/-/@matrix-org/olm-3.2.14.tgz": version "3.2.14" @@ -3922,6 +3934,11 @@ resolved "https://registry.yarnpkg.com/@types/content-type/-/content-type-1.1.5.tgz#aa02dca40864749a9e2bf0161a6216da57e3ede5" integrity sha512-dgMN+syt1xb7Hk8LU6AODOfPlvz5z1CbXpPuJE5ZrX9STfBOIXF09pEB8N7a97WT9dbngt3ksDCm6GW6yMrxfQ== +"@types/dom-screen-wake-lock@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@types/dom-screen-wake-lock/-/dom-screen-wake-lock-1.0.1.tgz#8f125f91821d49f51feee95e6db128eb6974aea9" + integrity sha512-WJQas3OFGcC8AeMzaa7FwzzbNNfanuV2R12kQYNp4BkUMghsRz5JxJ5RgVhJifhw7t0s6LvRSWZArmKbMDZ+5g== + "@types/eslint-scope@^3.7.3": version "3.7.4" resolved "https://registry.yarnpkg.com/@types/eslint-scope/-/eslint-scope-3.7.4.tgz#37fc1223f0786c39627068a12e94d6e6fc61de16" @@ -4036,11 +4053,16 @@ "@types/tough-cookie" "*" parse5 "^7.0.0" -"@types/json-schema@*", "@types/json-schema@^7.0.4", "@types/json-schema@^7.0.5", "@types/json-schema@^7.0.8", "@types/json-schema@^7.0.9": +"@types/json-schema@*", "@types/json-schema@^7.0.4", "@types/json-schema@^7.0.5", "@types/json-schema@^7.0.8": version "7.0.11" resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.11.tgz#d421b6c527a3037f7c84433fd2c4229e016863d3" integrity sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ== +"@types/json-schema@^7.0.12": + version "7.0.12" + resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.12.tgz#d70faba7039d5fca54c83c7dbab41051d2b6f6cb" + integrity sha512-Hr5Jfhc9eYOQNPYO5WLDq/n4jqijdHNlDXjuAQkkt+mWdQR+XJToOHrsD4cPaMXpn6KO7y2+wM8AZEs8VpBLVA== + "@types/json5@^0.0.29": version "0.0.29" resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" @@ -4196,10 +4218,10 @@ resolved "https://registry.yarnpkg.com/@types/sdp-transform/-/sdp-transform-2.4.5.tgz#3167961e0a1a5265545e278627aa37c606003f53" integrity sha512-GVO0gnmbyO3Oxm2HdPsYUNcyihZE3GyCY8ysMYHuQGfLhGZq89Nm4lSzULWTzZoyHtg+VO/IdrnxZHPnPSGnAg== -"@types/semver@^7.3.12": - version "7.3.13" - resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.3.13.tgz#da4bfd73f49bd541d28920ab0e2bf0ee80f71c91" - integrity sha512-21cFJr9z3g5dW8B0CVI9g2O9beqaThGQ6ZFBqHfwhzLDKUxaqTIy3vnfah/UPkfOiF2pLq+tGz+W8RyCskuslw== +"@types/semver@^7.5.0": + version "7.5.0" + resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.5.0.tgz#591c1ce3a702c45ee15f47a42ade72c2fd78978a" + integrity sha512-G8hZ6XJiHnuhQKR7ZmysCeJWE08o8T0AXtk5darsCaTVsYZhhgUrq53jizaR2FvsoeCwJhlmwTjkXBY5Pn/ZHw== "@types/source-list-map@*": version "0.1.2" @@ -4288,89 +4310,91 @@ dependencies: "@types/yargs-parser" "*" -"@typescript-eslint/eslint-plugin@^5.52.0": - version "5.52.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.52.0.tgz#5fb0d43574c2411f16ea80f5fc335b8eaa7b28a8" - integrity sha512-lHazYdvYVsBokwCdKOppvYJKaJ4S41CgKBcPvyd0xjZNbvQdhn/pnJlGtQksQ/NhInzdaeaSarlBjDXHuclEbg== +"@typescript-eslint/eslint-plugin@^6.1.0": + version "6.1.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.1.0.tgz#96f3ca6615717659d06c9f7161a1d14ab0c49c66" + integrity sha512-qg7Bm5TyP/I7iilGyp6DRqqkt8na00lI6HbjWZObgk3FFSzH5ypRwAHXJhJkwiRtTcfn+xYQIMOR5kJgpo6upw== dependencies: - "@typescript-eslint/scope-manager" "5.52.0" - "@typescript-eslint/type-utils" "5.52.0" - "@typescript-eslint/utils" "5.52.0" + "@eslint-community/regexpp" "^4.5.1" + "@typescript-eslint/scope-manager" "6.1.0" + "@typescript-eslint/type-utils" "6.1.0" + "@typescript-eslint/utils" "6.1.0" + "@typescript-eslint/visitor-keys" "6.1.0" debug "^4.3.4" - grapheme-splitter "^1.0.4" - ignore "^5.2.0" + graphemer "^1.4.0" + ignore "^5.2.4" + natural-compare "^1.4.0" natural-compare-lite "^1.4.0" - regexpp "^3.2.0" - semver "^7.3.7" - tsutils "^3.21.0" + semver "^7.5.4" + ts-api-utils "^1.0.1" -"@typescript-eslint/parser@^5.52.0": - version "5.52.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.52.0.tgz#73c136df6c0133f1d7870de7131ccf356f5be5a4" - integrity sha512-e2KiLQOZRo4Y0D/b+3y08i3jsekoSkOYStROYmPUnGMEoA0h+k2qOH5H6tcjIc68WDvGwH+PaOrP1XRzLJ6QlA== +"@typescript-eslint/parser@^6.1.0": + version "6.1.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-6.1.0.tgz#3135bf65dca5340d8650703eb8cb83113e156ee5" + integrity sha512-hIzCPvX4vDs4qL07SYzyomamcs2/tQYXg5DtdAfj35AyJ5PIUqhsLf4YrEIFzZcND7R2E8tpQIZKayxg8/6Wbw== dependencies: - "@typescript-eslint/scope-manager" "5.52.0" - "@typescript-eslint/types" "5.52.0" - "@typescript-eslint/typescript-estree" "5.52.0" + "@typescript-eslint/scope-manager" "6.1.0" + "@typescript-eslint/types" "6.1.0" + "@typescript-eslint/typescript-estree" "6.1.0" + "@typescript-eslint/visitor-keys" "6.1.0" debug "^4.3.4" -"@typescript-eslint/scope-manager@5.52.0": - version "5.52.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.52.0.tgz#a993d89a0556ea16811db48eabd7c5b72dcb83d1" - integrity sha512-AR7sxxfBKiNV0FWBSARxM8DmNxrwgnYMPwmpkC1Pl1n+eT8/I2NAUPuwDy/FmDcC6F8pBfmOcaxcxRHspgOBMw== +"@typescript-eslint/scope-manager@6.1.0": + version "6.1.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-6.1.0.tgz#a6cdbe11630614f8c04867858a42dd56590796ed" + integrity sha512-AxjgxDn27hgPpe2rQe19k0tXw84YCOsjDJ2r61cIebq1t+AIxbgiXKvD4999Wk49GVaAcdJ/d49FYel+Pp3jjw== dependencies: - "@typescript-eslint/types" "5.52.0" - "@typescript-eslint/visitor-keys" "5.52.0" + "@typescript-eslint/types" "6.1.0" + "@typescript-eslint/visitor-keys" "6.1.0" -"@typescript-eslint/type-utils@5.52.0": - version "5.52.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.52.0.tgz#9fd28cd02e6f21f5109e35496df41893f33167aa" - integrity sha512-tEKuUHfDOv852QGlpPtB3lHOoig5pyFQN/cUiZtpw99D93nEBjexRLre5sQZlkMoHry/lZr8qDAt2oAHLKA6Jw== +"@typescript-eslint/type-utils@6.1.0": + version "6.1.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-6.1.0.tgz#21cc6c3bc1980b03f9eb4e64580d0c5be6f08215" + integrity sha512-kFXBx6QWS1ZZ5Ni89TyT1X9Ag6RXVIVhqDs0vZE/jUeWlBv/ixq2diua6G7ece6+fXw3TvNRxP77/5mOMusx2w== dependencies: - "@typescript-eslint/typescript-estree" "5.52.0" - "@typescript-eslint/utils" "5.52.0" + "@typescript-eslint/typescript-estree" "6.1.0" + "@typescript-eslint/utils" "6.1.0" debug "^4.3.4" - tsutils "^3.21.0" + ts-api-utils "^1.0.1" -"@typescript-eslint/types@5.52.0": - version "5.52.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.52.0.tgz#19e9abc6afb5bd37a1a9bea877a1a836c0b3241b" - integrity sha512-oV7XU4CHYfBhk78fS7tkum+/Dpgsfi91IIDy7fjCyq2k6KB63M6gMC0YIvy+iABzmXThCRI6xpCEyVObBdWSDQ== +"@typescript-eslint/types@6.1.0": + version "6.1.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-6.1.0.tgz#2d607c62827bb416ada5c96ebfa2ef84e45a8dfa" + integrity sha512-+Gfd5NHCpDoHDOaU/yIF3WWRI2PcBRKKpP91ZcVbL0t5tQpqYWBs3z/GGhvU+EV1D0262g9XCnyqQh19prU0JQ== -"@typescript-eslint/typescript-estree@5.52.0": - version "5.52.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.52.0.tgz#6408cb3c2ccc01c03c278cb201cf07e73347dfca" - integrity sha512-WeWnjanyEwt6+fVrSR0MYgEpUAuROxuAH516WPjUblIrClzYJj0kBbjdnbQXLpgAN8qbEuGywiQsXUVDiAoEuQ== +"@typescript-eslint/typescript-estree@6.1.0": + version "6.1.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-6.1.0.tgz#ea382f6482ba698d7e993a88ce5391ea7a66c33d" + integrity sha512-nUKAPWOaP/tQjU1IQw9sOPCDavs/iU5iYLiY/6u7gxS7oKQoi4aUxXS1nrrVGTyBBaGesjkcwwHkbkiD5eBvcg== dependencies: - "@typescript-eslint/types" "5.52.0" - "@typescript-eslint/visitor-keys" "5.52.0" + "@typescript-eslint/types" "6.1.0" + "@typescript-eslint/visitor-keys" "6.1.0" debug "^4.3.4" globby "^11.1.0" is-glob "^4.0.3" - semver "^7.3.7" - tsutils "^3.21.0" + semver "^7.5.4" + ts-api-utils "^1.0.1" -"@typescript-eslint/utils@5.52.0": - version "5.52.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.52.0.tgz#b260bb5a8f6b00a0ed51db66bdba4ed5e4845a72" - integrity sha512-As3lChhrbwWQLNk2HC8Ree96hldKIqk98EYvypd3It8Q1f8d5zWyIoaZEp2va5667M4ZyE7X8UUR+azXrFl+NA== +"@typescript-eslint/utils@6.1.0": + version "6.1.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-6.1.0.tgz#1641843792b4e3451cc692e2c73055df8b26f453" + integrity sha512-wp652EogZlKmQoMS5hAvWqRKplXvkuOnNzZSE0PVvsKjpexd/XznRVHAtrfHFYmqaJz0DFkjlDsGYC9OXw+OhQ== dependencies: - "@types/json-schema" "^7.0.9" - "@types/semver" "^7.3.12" - "@typescript-eslint/scope-manager" "5.52.0" - "@typescript-eslint/types" "5.52.0" - "@typescript-eslint/typescript-estree" "5.52.0" - eslint-scope "^5.1.1" - eslint-utils "^3.0.0" - semver "^7.3.7" + "@eslint-community/eslint-utils" "^4.4.0" + "@types/json-schema" "^7.0.12" + "@types/semver" "^7.5.0" + "@typescript-eslint/scope-manager" "6.1.0" + "@typescript-eslint/types" "6.1.0" + "@typescript-eslint/typescript-estree" "6.1.0" + semver "^7.5.4" -"@typescript-eslint/visitor-keys@5.52.0": - version "5.52.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.52.0.tgz#e38c971259f44f80cfe49d97dbffa38e3e75030f" - integrity sha512-qMwpw6SU5VHCPr99y274xhbm+PRViK/NATY6qzt+Et7+mThGuFSl/ompj2/hrBlRP/kq+BFdgagnOSgw9TB0eA== +"@typescript-eslint/visitor-keys@6.1.0": + version "6.1.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-6.1.0.tgz#d2b84dff6b58944d3257ea03687e269a788c73be" + integrity sha512-yQeh+EXhquh119Eis4k0kYhj9vmFzNpbhM3LftWQVwqVjipCkwHBQOZutcYW+JVkjtTG9k8nrZU1UoNedPDd1A== dependencies: - "@typescript-eslint/types" "5.52.0" - eslint-visitor-keys "^3.3.0" + "@typescript-eslint/types" "6.1.0" + eslint-visitor-keys "^3.4.1" "@use-gesture/core@10.2.16": version "10.2.16" @@ -5336,7 +5360,7 @@ base16@^1.0.0: resolved "https://registry.yarnpkg.com/base16/-/base16-1.0.0.tgz#e297f60d7ec1014a7a971a39ebc8a98c0b681e70" integrity sha512-pNdYkNPiJUnEhnfXV56+sQy8+AaPcG3POZAUnwr4EeqCUZFz4u2PePbo3e5Gj4ziYPCWGUZT9RHisvJKnwFuBQ== -base64-js@^1.0.2, base64-js@^1.3.1: +base64-js@^1.0.2: version "1.5.1" resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== @@ -5388,15 +5412,6 @@ bindings@^1.5.0: dependencies: file-uri-to-path "1.0.0" -bl@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/bl/-/bl-4.1.0.tgz#451535264182bec2fbbc83a62ab98cf11d9f7b3a" - integrity sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w== - dependencies: - buffer "^5.5.0" - inherits "^2.0.4" - readable-stream "^3.4.0" - bluebird@^3.5.5: version "3.7.2" resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f" @@ -5643,14 +5658,6 @@ buffer@^4.3.0: ieee754 "^1.1.4" isarray "^1.0.0" -buffer@^5.5.0: - version "5.7.1" - resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0" - integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ== - dependencies: - base64-js "^1.3.1" - ieee754 "^1.1.13" - bufrw@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/bufrw/-/bufrw-1.3.0.tgz#28d6cfdaf34300376836310f5c31d57eeb40c8fa" @@ -5992,18 +5999,6 @@ cli-boxes@^2.2.1: resolved "https://registry.yarnpkg.com/cli-boxes/-/cli-boxes-2.2.1.tgz#ddd5035d25094fce220e9cab40a45840a440318f" integrity sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw== -cli-cursor@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-3.1.0.tgz#264305a7ae490d1d03bf0c9ba7c925d1753af307" - integrity sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw== - dependencies: - restore-cursor "^3.1.0" - -cli-spinners@^2.5.0: - version "2.7.0" - resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.7.0.tgz#f815fd30b5f9eaac02db604c7a231ed7cb2f797a" - integrity sha512-qu3pN8Y3qHNgE2AFweciB1IfMnmZ/fsNTEE+NOFjmGB2F/7rLhnhzppvpCnN4FovtP26k8lHyy9ptEbNwWFLzw== - cli-table3@^0.6.1: version "0.6.2" resolved "https://registry.yarnpkg.com/cli-table3/-/cli-table3-0.6.2.tgz#aaf5df9d8b5bf12634dc8b3040806a0c07120d2a" @@ -6050,11 +6045,6 @@ clone-stats@^1.0.0: resolved "https://registry.yarnpkg.com/clone-stats/-/clone-stats-1.0.0.tgz#b3782dff8bb5474e18b9b6bf0fdfe782f8777680" integrity sha512-au6ydSpg6nsrigcZ4m8Bc9hxjeW+GJ8xh5G3BJCMt4WXe1H10UNaVOamqQTmrx1kjVuxAHIQSNU6hY4Nsn9/ag== -clone@^1.0.2: - version "1.0.4" - resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e" - integrity sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg== - clone@^2.1.1, clone@^2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/clone/-/clone-2.1.2.tgz#1b7f4b9f591f1e8f83670401600345a02887435f" @@ -6460,11 +6450,6 @@ crypto-browserify@^3.11.0: randombytes "^2.0.0" randomfill "^1.0.3" -crypto-js@^4.1.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/crypto-js/-/crypto-js-4.1.1.tgz#9e485bcf03521041bd85844786b83fb7619736cf" - integrity sha512-o2JlM7ydqd3Qk9CA0L4NL6mTzU2sdx96a+oOfPu8Mkl/PK51vSyoi8/rQ8NknZtk44vq15lmhAj9CIAGwgeWKw== - css-blank-pseudo@^3.0.3: version "3.0.3" resolved "https://registry.yarnpkg.com/css-blank-pseudo/-/css-blank-pseudo-3.0.3.tgz#36523b01c12a25d812df343a32c322d2a2324561" @@ -7194,13 +7179,6 @@ default-browser-id@^1.0.4: meow "^3.1.0" untildify "^2.0.0" -defaults@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/defaults/-/defaults-1.0.3.tgz#c656051e9817d9ff08ed881477f3fe4019f3ef7d" - integrity sha512-s82itHOnYrN0Ib8r+z7laQz3sdE+4FP3d9Q7VLO7U+KRT+CR0GsWuyHxzdAY82I7cXv0G/twrqomTJLOssO5HA== - dependencies: - clone "^1.0.2" - define-lazy-prop@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz#3f7ae421129bcaaac9bc74905c98a0009ec9ee7f" @@ -7988,7 +7966,7 @@ eslint-plugin-react@^7.29.4: semver "^6.3.0" string.prototype.matchall "^4.0.7" -eslint-scope@5.1.1, eslint-scope@^5.1.1: +eslint-scope@5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c" integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw== @@ -8029,6 +8007,11 @@ eslint-visitor-keys@^3.3.0: resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz#f6480fa6b1f30efe2d1968aa8ac745b862469826" integrity sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA== +eslint-visitor-keys@^3.4.1: + version "3.4.1" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.1.tgz#c22c48f48942d08ca824cc526211ae400478a994" + integrity sha512-pZnmmLwYzf+kWaM/Qgrvpen51upAktaaiI01nsJD/Yr3lMOdNtq0cxkrrg16w64VtisN6okbs7Q8AfGqj4c9fA== + eslint@^8.14.0: version "8.19.0" resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.19.0.tgz#7342a3cbc4fbc5c106a1eefe0fd0b50b6b1a7d28" @@ -8150,21 +8133,6 @@ evp_bytestokey@^1.0.0, evp_bytestokey@^1.0.3: md5.js "^1.3.4" safe-buffer "^5.1.1" -execa@^4.0.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/execa/-/execa-4.1.0.tgz#4e5491ad1572f2f17a77d388c6c857135b22847a" - integrity sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA== - dependencies: - cross-spawn "^7.0.0" - get-stream "^5.0.0" - human-signals "^1.1.1" - is-stream "^2.0.0" - merge-stream "^2.0.0" - npm-run-path "^4.0.0" - onetime "^5.1.0" - signal-exit "^3.0.2" - strip-final-newline "^2.0.0" - execa@^5.0.0, execa@^5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/execa/-/execa-5.1.1.tgz#f80ad9cbf4298f7bd1d4c9555c21e93741c411dd" @@ -8822,13 +8790,6 @@ get-stdin@^4.0.1: resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-4.0.1.tgz#b968c6b0a04384324902e8bf1a5df32579a450fe" integrity sha512-F5aQMywwJ2n85s4hJPTT9RPxGmubonuB10MNYo17/xph174n2MIR33HRguhzVag10O/npM7SPk73LMZNP+FaWw== -get-stream@^5.0.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-5.2.0.tgz#4966a1795ee5ace65e706c4b7beb71257d6e22d3" - integrity sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA== - dependencies: - pump "^3.0.0" - get-stream@^6.0.0: version "6.0.1" resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7" @@ -8984,10 +8945,10 @@ graceful-fs@^4.0.0, graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2 resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.10.tgz#147d3a006da4ca3ce14728c7aefc287c367d7a6c" integrity sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA== -grapheme-splitter@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz#9cf3a665c6247479896834af35cf1dbb4400767e" - integrity sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ== +graphemer@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/graphemer/-/graphemer-1.4.0.tgz#fb2f1d55e0e3a1849aeffc90c4fa0dd53a0e66c6" + integrity sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag== graphlib@^2.1.8: version "2.1.8" @@ -9372,11 +9333,6 @@ https-proxy-agent@^5.0.0, https-proxy-agent@^5.0.1: agent-base "6" debug "4" -human-signals@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-1.1.1.tgz#c5b1cd14f50aeae09ab6c59fe63ba3395fe4dfa3" - integrity sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw== - human-signals@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0" @@ -9455,7 +9411,7 @@ identity-obj-proxy@^3.0.0: dependencies: harmony-reflect "^1.4.6" -ieee754@^1.1.13, ieee754@^1.1.4: +ieee754@^1.1.4: version "1.2.1" resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== @@ -9475,6 +9431,11 @@ ignore@^5.2.0: resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.0.tgz#6d3bac8fa7fe0d45d9f9be7bac2fc279577e345a" integrity sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ== +ignore@^5.2.4: + version "5.2.4" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.4.tgz#a291c0c6178ff1b960befe47fcdec301674a6324" + integrity sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ== + immutable@^4.0.0: version "4.1.0" resolved "https://registry.yarnpkg.com/immutable/-/immutable-4.1.0.tgz#f795787f0db780183307b9eb2091fcac1f6fafef" @@ -9799,11 +9760,6 @@ is-hexadecimal@^1.0.0: resolved "https://registry.yarnpkg.com/is-hexadecimal/-/is-hexadecimal-1.0.4.tgz#cc35c97588da4bd49a8eedd6bc4082d44dcb23a7" integrity sha512-gyPJuv83bHMpocVYoqof5VDiZveEoGoFL8m3BXNb2VW8Xs+rz9kqO8LOQ5DH6EsuvilT1ApazU0pyl+ytbPtlw== -is-interactive@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-interactive/-/is-interactive-1.0.0.tgz#cea6e6ae5c870a7b0a0004070b7b587e0252912e" - integrity sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w== - is-map@^2.0.1, is-map@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/is-map/-/is-map-2.0.2.tgz#00922db8c9bf73e81b7a335827bc2a43f2b91127" @@ -9929,11 +9885,6 @@ is-unc-path@^1.0.0: dependencies: unc-path-regex "^0.1.2" -is-unicode-supported@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz#3f26c76a809593b52bfa2ecb5710ed2779b522a7" - integrity sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw== - is-utf8@^0.2.0, is-utf8@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72" @@ -10911,14 +10862,6 @@ lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.21: resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== -log-symbols@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.1.0.tgz#3fbdbb95b4683ac9fc785111e792e558d4abd503" - integrity sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg== - dependencies: - chalk "^4.1.0" - is-unicode-supported "^0.1.0" - loglevel@^1.7.1, loglevel@^1.8.0, loglevel@^1.8.1: version "1.8.1" resolved "https://registry.yarnpkg.com/loglevel/-/loglevel-1.8.1.tgz#5c621f83d5b48c54ae93b6156353f555963377b4" @@ -11062,7 +11005,7 @@ matrix-events-sdk@0.0.1: resolved "https://codeload.github.com/matrix-org/matrix-js-sdk/tar.gz/b698217445318f453e0b1086364a33113eaa85d9" dependencies: "@babel/runtime" "^7.12.5" - "@matrix-org/matrix-sdk-crypto-js" "^0.1.1" + "@matrix-org/matrix-sdk-crypto-js" "^0.1.0" another-json "^0.2.0" bs58 "^5.0.0" content-type "^1.0.4" @@ -11070,7 +11013,6 @@ matrix-events-sdk@0.0.1: loglevel "^1.7.1" matrix-events-sdk "0.0.1" matrix-widget-api "^1.3.1" - oidc-client-ts "^2.2.4" p-retry "4" sdp-transform "^2.14.1" unhomoglyph "^1.0.6" @@ -11607,7 +11549,7 @@ now-and-later@^2.0.0: dependencies: once "^1.3.2" -npm-run-path@^4.0.0, npm-run-path@^4.0.1: +npm-run-path@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea" integrity sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw== @@ -11757,14 +11699,6 @@ objectorarray@^1.0.5: resolved "https://registry.yarnpkg.com/objectorarray/-/objectorarray-1.0.5.tgz#2c05248bbefabd8f43ad13b41085951aac5e68a5" integrity sha512-eJJDYkhJFFbBBAxeh8xW+weHlkI28n2ZdQV/J/DNfWfSKlGEf2xcfAbZTv3riEXHAhL9SVOTs2pRmXiSTf78xg== -oidc-client-ts@^2.2.4: - version "2.2.4" - resolved "https://registry.yarnpkg.com/oidc-client-ts/-/oidc-client-ts-2.2.4.tgz#7d86b5efe2248f3637a6f3a0ee1af86764aea125" - integrity sha512-nOZwIomju+AmXObl5Oq5PjrES/qTt8bLsENJCIydVgi9TEWk7SCkOU6X3RNkY7yfySRM1OJJvDKdREZdmnDT2g== - dependencies: - crypto-js "^4.1.1" - jwt-decode "^3.1.2" - on-finished@2.4.1: version "2.4.1" resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.4.1.tgz#58c8c44116e54845ad57f14ab10b03533184ac3f" @@ -11784,7 +11718,7 @@ once@^1.3.0, once@^1.3.1, once@^1.3.2, once@^1.4.0: dependencies: wrappy "1" -onetime@^5.1.0, onetime@^5.1.2: +onetime@^5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e" integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg== @@ -11837,21 +11771,6 @@ optionator@^0.9.1: type-check "^0.4.0" word-wrap "^1.2.3" -ora@^5.4.1: - version "5.4.1" - resolved "https://registry.yarnpkg.com/ora/-/ora-5.4.1.tgz#1b2678426af4ac4a509008e5e4ac9e9959db9e18" - integrity sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ== - dependencies: - bl "^4.1.0" - chalk "^4.1.0" - cli-cursor "^3.1.0" - cli-spinners "^2.5.0" - is-interactive "^1.0.0" - is-unicode-supported "^0.1.0" - log-symbols "^4.1.0" - strip-ansi "^6.0.0" - wcwidth "^1.0.1" - ordered-read-streams@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/ordered-read-streams/-/ordered-read-streams-1.0.1.tgz#77c0cb37c41525d64166d990ffad7ec6a0e1363e" @@ -13176,7 +13095,7 @@ read-pkg@^5.2.0: string_decoder "~1.1.1" util-deprecate "~1.0.1" -readable-stream@3, readable-stream@^3.0.2, readable-stream@^3.4.0, readable-stream@^3.6.0: +readable-stream@3, readable-stream@^3.0.2, readable-stream@^3.6.0: version "3.6.0" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== @@ -13497,14 +13416,6 @@ resolve@^2.0.0-next.3: path-parse "^1.0.7" supports-preserve-symlinks-flag "^1.0.0" -restore-cursor@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-3.1.0.tgz#39f67c54b3a7a58cea5236d95cf0034239631f7e" - integrity sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA== - dependencies: - onetime "^5.1.0" - signal-exit "^3.0.2" - ret@~0.1.10: version "0.1.15" resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc" @@ -13715,13 +13626,20 @@ semver@^6.0.0, semver@^6.1.1, semver@^6.1.2, semver@^6.3.0: resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== -semver@^7.3.2, semver@^7.3.4, semver@^7.3.5, semver@^7.3.7: +semver@^7.3.2, semver@^7.3.4, semver@^7.3.5: version "7.3.7" resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.7.tgz#12c5b649afdbf9049707796e22a4028814ce523f" integrity sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g== dependencies: lru-cache "^6.0.0" +semver@^7.5.4: + version "7.5.4" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e" + integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA== + dependencies: + lru-cache "^6.0.0" + send@0.18.0: version "0.18.0" resolved "https://registry.yarnpkg.com/send/-/send-0.18.0.tgz#670167cc654b05f5aa4a767f9113bb371bc706be" @@ -14632,6 +14550,11 @@ trough@^1.0.0: resolved "https://registry.yarnpkg.com/trough/-/trough-1.0.5.tgz#b8b639cefad7d0bb2abd37d433ff8293efa5f406" integrity sha512-rvuRbTarPXmMb79SmzEp8aqXNKcK+y0XaB298IXueQ8I2PsrATcPBCSPyK/dDNa2iWOhKlfNnOjdAOTBU/nkFA== +ts-api-utils@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/ts-api-utils/-/ts-api-utils-1.0.1.tgz#8144e811d44c749cd65b2da305a032510774452d" + integrity sha512-lC/RGlPmwdrIBFTX59wwNzqh7aR2otPNPR/5brHZm/XKFYKsfqxihXUe9pU3JI+3vGkl+vyCoNNnPhJn3aLK1A== + ts-debounce@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/ts-debounce/-/ts-debounce-4.0.0.tgz#33440ef64fab53793c3d546a8ca6ae539ec15841" @@ -14657,7 +14580,7 @@ tsconfig-paths@^3.14.1: minimist "^1.2.6" strip-bom "^3.0.0" -tslib@^1.8.1, tslib@^1.9.3: +tslib@^1.9.3: version "1.14.1" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== @@ -14672,13 +14595,6 @@ tslib@^2.3.0: resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.5.0.tgz#42bfed86f5787aeb41d031866c8f402429e0fddf" integrity sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg== -tsutils@^3.21.0: - version "3.21.0" - resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.21.0.tgz#b48717d394cea6c1e096983eed58e9d61715b623" - integrity sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA== - dependencies: - tslib "^1.8.1" - tty-browserify@0.0.0: version "0.0.0" resolved "https://registry.yarnpkg.com/tty-browserify/-/tty-browserify-0.0.0.tgz#a157ba402da24e9bf957f9aa69d524eed42901a6" @@ -14736,25 +14652,15 @@ typedarray@^0.0.6: resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" integrity sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA== -typescript-strict-plugin@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/typescript-strict-plugin/-/typescript-strict-plugin-2.0.1.tgz#4e676704818c4458a8b11125e9d32032e0513de4" - integrity sha512-8LHbwpkeQN12KZMK4BsmC6U1AyF+QisiLlaPH6GoCDV3xd52emyg6mOsL4I3C1Uy2n65HrnAdSkc8yi6bWb/6Q== - dependencies: - chalk "^3.0.0" - execa "^4.0.0" - ora "^5.4.1" - yargs "^16.2.0" - typescript@^4.2.4: version "4.8.4" resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.8.4.tgz#c464abca159669597be5f96b8943500b238e60e6" integrity sha512-QCh+85mCy+h0IGff8r5XWzOVSbBO+KfeYrMQh7NJ58QujwcE22u+NUSmUxqF+un70P9GXKxa2HCNiTTMJknyjQ== -typescript@^4.9.5: - version "4.9.5" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.9.5.tgz#095979f9bcc0d09da324d58d03ce8f8374cbe65a" - integrity sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g== +typescript@^5.1.6: + version "5.1.6" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.1.6.tgz#02f8ac202b6dad2c0dd5e0913745b47a37998274" + integrity sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA== ua-parser-js@^0.7.30: version "0.7.31" @@ -15350,13 +15256,6 @@ watchpack@^2.2.0, watchpack@^2.3.1: glob-to-regexp "^0.4.1" graceful-fs "^4.1.2" -wcwidth@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/wcwidth/-/wcwidth-1.0.1.tgz#f0b0dcf915bc5ff1528afadb2c0e17b532da2fe8" - integrity sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg== - dependencies: - defaults "^1.0.3" - web-namespaces@^1.0.0: version "1.1.4" resolved "https://registry.yarnpkg.com/web-namespaces/-/web-namespaces-1.1.4.tgz#bc98a3de60dadd7faefc403d1076d529f5e030ec" From b44ab47a1ac1e0bc7c81d41f24c422bc9bee7d1b Mon Sep 17 00:00:00 2001 From: David Baker Date: Fri, 21 Jul 2023 11:58:21 +0100 Subject: [PATCH 42/59] Report more errors to sentry Mostly from ErrorView --- src/FullScreenView.tsx | 2 ++ src/room/useGroupCall.ts | 2 ++ 2 files changed, 4 insertions(+) diff --git a/src/FullScreenView.tsx b/src/FullScreenView.tsx index 95a8a7ae..cdbc0297 100644 --- a/src/FullScreenView.tsx +++ b/src/FullScreenView.tsx @@ -18,6 +18,7 @@ import { ReactNode, useCallback, useEffect } from "react"; import { useLocation } from "react-router-dom"; import classNames from "classnames"; import { Trans, useTranslation } from "react-i18next"; +import * as Sentry from "@sentry/react"; import { Header, HeaderLogo, LeftNav, RightNav } from "./Header"; import { LinkButton, Button } from "./button"; @@ -58,6 +59,7 @@ export function ErrorView({ error }: ErrorViewProps) { useEffect(() => { console.error(error); + Sentry.captureException(error); }, [error]); const onReload = useCallback(() => { diff --git a/src/room/useGroupCall.ts b/src/room/useGroupCall.ts index 63e19443..b0cbf76f 100644 --- a/src/room/useGroupCall.ts +++ b/src/room/useGroupCall.ts @@ -15,6 +15,7 @@ limitations under the License. */ import { useCallback, useEffect, useReducer, useState } from "react"; +import * as Sentry from "@sentry/react"; import { GroupCallEvent, GroupCallState, @@ -331,6 +332,7 @@ export function useGroupCall( } function onError(e: GroupCallError): void { + Sentry.captureException(e); if (e.code === GroupCallErrorCode.UnknownDevice) { const unknownDeviceError = e as GroupCallUnknownDeviceError; addUnencryptedEventUser(unknownDeviceError.userId); From 96dc0b6638e8711f5dbb9fc302c2f8485dd0d1b1 Mon Sep 17 00:00:00 2001 From: Robin Townsend Date: Fri, 21 Jul 2023 11:58:18 -0400 Subject: [PATCH 43/59] Fix the build? --- yarn.lock | 1 + 1 file changed, 1 insertion(+) diff --git a/yarn.lock b/yarn.lock index e690a5e5..5941702c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -11013,6 +11013,7 @@ matrix-events-sdk@0.0.1: loglevel "^1.7.1" matrix-events-sdk "0.0.1" matrix-widget-api "^1.3.1" + oidc-client-ts "^2.2.4" p-retry "4" sdp-transform "^2.14.1" unhomoglyph "^1.0.6" From 00c81ad85bef1a702485eb5fcd859b080efcc656 Mon Sep 17 00:00:00 2001 From: Robin Townsend Date: Fri, 21 Jul 2023 12:12:44 -0400 Subject: [PATCH 44/59] Add comments --- src/useWakeLock.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/useWakeLock.ts b/src/useWakeLock.ts index 5a8ccb6e..f4af4c52 100644 --- a/src/useWakeLock.ts +++ b/src/useWakeLock.ts @@ -26,10 +26,14 @@ export const useWakeLock = () => { let mounted = true; let lock: WakeLockSentinel | null = null; + // The lock is automatically released whenever the window goes invisible, + // so we need to reacquire it on visiblity changes const onVisiblityChange = async () => { if (document.visibilityState === "visible") { try { lock = await navigator.wakeLock.request("screen"); + // Handle the edge case where this component unmounts before the + // promise resolves if (!mounted) lock .release() From 4643e167ce91ecf6b926aed215b18391f2f63e35 Mon Sep 17 00:00:00 2001 From: David Baker Date: Fri, 21 Jul 2023 20:08:46 +0100 Subject: [PATCH 45/59] i18n --- public/locales/en-GB/app.json | 1 - 1 file changed, 1 deletion(-) diff --git a/public/locales/en-GB/app.json b/public/locales/en-GB/app.json index df4daaa3..d697a70d 100644 --- a/public/locales/en-GB/app.json +++ b/public/locales/en-GB/app.json @@ -25,7 +25,6 @@ "Change layout": "Change layout", "Close": "Close", "Confirm password": "Confirm password", - "Connection lost": "Connection lost", "Connectivity to the server has been lost.": "Connectivity to the server has been lost.", "Copied!": "Copied!", "Copy": "Copy", From c34eac03e53f7bce403bc2341766f9df4951e35d Mon Sep 17 00:00:00 2001 From: Robin Townsend Date: Fri, 21 Jul 2023 15:08:53 -0400 Subject: [PATCH 46/59] Restore the client 'loading' state https://github.com/vector-im/element-call/pull/1173 regressed the client loading sequence, such that the app would pretend that you were signed out when it was really just loading your saved session. This makes the proper loading state appear again. --- src/ClientContext.tsx | 39 ++++++++++++++++++--------------------- 1 file changed, 18 insertions(+), 21 deletions(-) diff --git a/src/ClientContext.tsx b/src/ClientContext.tsx index 69a9295e..9e87e1a2 100644 --- a/src/ClientContext.tsx +++ b/src/ClientContext.tsx @@ -149,8 +149,9 @@ interface Props { export const ClientProvider: FC = ({ children }) => { const history = useHistory(); + // null = signed out, undefined = loading const [initClientState, setInitClientState] = useState< - InitResult | undefined + InitResult | null | undefined >(undefined); const initializing = useRef(false); @@ -162,14 +163,7 @@ export const ClientProvider: FC = ({ children }) => { initializing.current = true; loadClient() - .then((maybeClient) => { - if (!maybeClient) { - logger.error("Failed to initialize client"); - return; - } - - setInitClientState(maybeClient); - }) + .then(setInitClientState) .catch((err) => logger.error(err)) .finally(() => (initializing.current = false)); }, []); @@ -264,20 +258,22 @@ export const ClientProvider: FC = ({ children }) => { }, [initClientState?.client, setAlreadyOpenedErr, t]) ); - const state: ClientState = useMemo(() => { + const state: ClientState | undefined = useMemo(() => { if (alreadyOpenedErr) { return { state: "error", error: alreadyOpenedErr }; } - let authenticated = undefined; - if (initClientState) { - authenticated = { - client: initClientState.client, - isPasswordlessUser: initClientState.passwordlessUser, - changePassword, - logout, - }; - } + if (initClientState === undefined) return undefined; + + const authenticated = + initClientState === null + ? undefined + : { + client: initClientState.client, + isPasswordlessUser: initClientState.passwordlessUser, + changePassword, + logout, + }; return { state: "valid", authenticated, setClient }; }, [alreadyOpenedErr, changePassword, initClientState, logout, setClient]); @@ -308,7 +304,7 @@ type InitResult = { passwordlessUser: boolean; }; -async function loadClient(): Promise { +async function loadClient(): Promise { if (widget) { // We're inside a widget, so let's engage *matryoshka mode* logger.log("Using a matryoshka client"); @@ -322,7 +318,8 @@ async function loadClient(): Promise { try { const session = loadSession(); if (!session) { - throw new Error("No session stored"); + logger.log("No session stored; continuing without a client"); + return null; } logger.log("Using a standalone client"); From 9ddf574ae43ae4c07cac7a1e9761a2aaaad576d2 Mon Sep 17 00:00:00 2001 From: snoutie <71790678+SnoutBug@users.noreply.github.com> Date: Sat, 22 Jul 2023 14:10:20 +0200 Subject: [PATCH 47/59] Update README.md Update readme to reflect the host that uses the current latest version of the livekit branch --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 7731d6fe..83b1df2b 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ For prior version of the Element Call that relied solely on full-mesh logic, che ![A demo of Element Call with six people](demo.jpg) -To try it out, visit our hosted version at [call.element.io](https://call.element.io). You can also find the latest development version continuously deployed to [element-call.netlify.app](https://element-call.netlify.app). +To try it out, visit our hosted version at [call.element.io](https://call.element.io). You can also find the latest development version continuously deployed to [call.element.dev](https://call.element.dev/). ## Host it yourself From 8ee0e207bd9451a914460b8f0c6980e05e06267b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Mon, 24 Jul 2023 12:06:51 +0200 Subject: [PATCH 48/59] Automatically mute the user, if there already are more than 8 participants MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/livekit/useLiveKit.ts | 2 +- src/room/InCallView.tsx | 15 ++++++++++++++- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/src/livekit/useLiveKit.ts b/src/livekit/useLiveKit.ts index dac6a052..adb5c72c 100644 --- a/src/livekit/useLiveKit.ts +++ b/src/livekit/useLiveKit.ts @@ -11,7 +11,7 @@ export type UserChoices = { }; export type DeviceChoices = { - selectedId: string; + selectedId?: string; enabled: boolean; }; diff --git a/src/room/InCallView.tsx b/src/room/InCallView.tsx index c45edbc2..866c75af 100644 --- a/src/room/InCallView.tsx +++ b/src/room/InCallView.tsx @@ -97,7 +97,20 @@ export interface ActiveCallProps extends Omit { export function ActiveCall(props: ActiveCallProps) { const sfuConfig = useSFUConfig(); - const livekitRoom = useLiveKit(props.userChoices, sfuConfig); + const livekitRoom = useLiveKit( + { + audio: { + selectedId: props.userChoices.audio?.selectedId, + enabled: + (props.userChoices.audio?.enabled ?? false) && + // Automatically mute the user, if there is more than 8 participants + // in the call + props.groupCall.participants.size <= 8, + }, + video: props.userChoices.video, + }, + sfuConfig + ); if (!livekitRoom) { return null; From 856158bf348648679e57acdff567039450069944 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Mon, 24 Jul 2023 12:12:02 +0200 Subject: [PATCH 49/59] Hide media device sections in settings if empty MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/settings/SettingsModal.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/settings/SettingsModal.tsx b/src/settings/SettingsModal.tsx index 8508af25..44f10d96 100644 --- a/src/settings/SettingsModal.tsx +++ b/src/settings/SettingsModal.tsx @@ -262,7 +262,7 @@ export const SettingsModal = (props: Props) => { ); const tabs: JSX.Element[] = []; - tabs.push(audioTab, videoTab); + if (devices) tabs.push(audioTab, videoTab); if (!isEmbedded) tabs.push(profileTab); tabs.push(feedbackTab, moreTab); if (developerSettingsTab) tabs.push(developerTab); From c4d9f967e78ed67f4f564e4d05a9a07346f3e06e Mon Sep 17 00:00:00 2001 From: raspin0 Date: Fri, 21 Jul 2023 20:35:36 +0000 Subject: [PATCH 50/59] Translated using Weblate (Polish) Currently translated at 100.0% (118 of 118 strings) Translation: Element Call/element-call Translate-URL: https://translate.element.io/projects/element-call/element-call/pl/ --- public/locales/pl/app.json | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/public/locales/pl/app.json b/public/locales/pl/app.json index df3c9a71..7aa2e81f 100644 --- a/public/locales/pl/app.json +++ b/public/locales/pl/app.json @@ -112,5 +112,9 @@ "<0>We'd love to hear your feedback so we can improve your experience.": "<0>Z przyjemnością wysłuchamy Twojej opinii, aby poprawić Twoje doświadczenia.", "How did it go?": "Jak poszło?", "{{displayName}} is presenting": "{{displayName}} prezentuje", - "Show connection stats": "Pokaż statystyki połączenia" + "Show connection stats": "Pokaż statystyki połączenia", + "Element Call is temporarily not end-to-end encrypted while we test scalability.": "Usługa Element Call tymczasowo nie jest szyfrowana end-to-end w trakcie, gdy testujemy możliwość jej rozszerzenia.", + "By clicking \"Go\", you agree to our <2>End User Licensing Agreement (EULA)": "Klikając \"Przejdź\", zgadzasz się na naszą <2>Umowę licencyjną (EULA)", + "This site is protected by ReCAPTCHA and the Google <2>Privacy Policy and <6>Terms of Service apply.<9>By clicking \"Register\", you agree to our <12>End User Licensing Agreement (EULA)": "Ta witryna jest chroniona przez ReCAPTCHA, więc obowiązują <2>Polityka prywatności i <6>Warunki usług Google. Klikając \"Zarejestruj\", zgadzasz się na naszą <12>Umowę licencyjną (EULA)", + "By clicking \"Join call now\", you agree to our <2>End User Licensing Agreement (EULA)": "Klikając \"Dołącz teraz do rozmowy\", zgadzasz się na naszą <2>Umowę licencyjną (EULA)" } From c2a3d60efd544d721d8e6abc583156537c7525e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Mon, 24 Jul 2023 17:44:07 +0200 Subject: [PATCH 51/59] Mute in lobby MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/room/GroupCallView.tsx | 1 + src/room/InCallView.tsx | 15 +-------------- src/room/LobbyView.tsx | 2 ++ src/room/VideoPreview.tsx | 15 +++++++++++++-- 4 files changed, 17 insertions(+), 16 deletions(-) diff --git a/src/room/GroupCallView.tsx b/src/room/GroupCallView.tsx index 29d14b82..89a72002 100644 --- a/src/room/GroupCallView.tsx +++ b/src/room/GroupCallView.tsx @@ -279,6 +279,7 @@ export function GroupCallView({ setUserChoices(choices); enter(); }} + muteAudio={participants.size > 8} isEmbedded={isEmbedded} hideHeader={hideHeader} /> diff --git a/src/room/InCallView.tsx b/src/room/InCallView.tsx index 866c75af..c45edbc2 100644 --- a/src/room/InCallView.tsx +++ b/src/room/InCallView.tsx @@ -97,20 +97,7 @@ export interface ActiveCallProps extends Omit { export function ActiveCall(props: ActiveCallProps) { const sfuConfig = useSFUConfig(); - const livekitRoom = useLiveKit( - { - audio: { - selectedId: props.userChoices.audio?.selectedId, - enabled: - (props.userChoices.audio?.enabled ?? false) && - // Automatically mute the user, if there is more than 8 participants - // in the call - props.groupCall.participants.size <= 8, - }, - video: props.userChoices.video, - }, - sfuConfig - ); + const livekitRoom = useLiveKit(props.userChoices, sfuConfig); if (!livekitRoom) { return null; diff --git a/src/room/LobbyView.tsx b/src/room/LobbyView.tsx index 0f07ae7d..6b51542a 100644 --- a/src/room/LobbyView.tsx +++ b/src/room/LobbyView.tsx @@ -33,6 +33,7 @@ interface Props { onEnter: (userChoices: UserChoices) => void; isEmbedded: boolean; hideHeader: boolean; + muteAudio: boolean; } export function LobbyView(props: Props) { @@ -66,6 +67,7 @@ export function LobbyView(props: Props) {
diff --git a/src/room/VideoPreview.tsx b/src/room/VideoPreview.tsx index 5fac133e..d39aeb17 100644 --- a/src/room/VideoPreview.tsx +++ b/src/room/VideoPreview.tsx @@ -40,10 +40,15 @@ export type MatrixInfo = { interface Props { matrixInfo: MatrixInfo; + muteAudio: boolean; onUserChoicesChanged: (choices: UserChoices) => void; } -export function VideoPreview({ matrixInfo, onUserChoicesChanged }: Props) { +export function VideoPreview({ + matrixInfo, + muteAudio, + onUserChoicesChanged, +}: Props) { const { client } = useClient(); const [previewRef, previewBounds] = useMeasure({ polyfill: ResizeObserver }); @@ -64,7 +69,13 @@ export function VideoPreview({ matrixInfo, onUserChoicesChanged }: Props) { // Create local media tracks. const [videoEnabled, setVideoEnabled] = useState(true); - const [audioEnabled, setAudioEnabled] = useState(true); + const [audioEnabled, setAudioEnabled] = useState(!muteAudio); + + useEffect(() => { + if (muteAudio) { + setAudioEnabled(false); + } + }, [muteAudio]); // The settings are updated as soon as the device changes. We wrap the settings value in a ref to store their initial value. // Not changing the device options prohibits the usePreviewTracks hook to recreate the tracks. From 1fc17a5b64b9121fdabde424395dbc66c795b2ea Mon Sep 17 00:00:00 2001 From: Vri Date: Mon, 24 Jul 2023 13:39:36 +0000 Subject: [PATCH 52/59] Translated using Weblate (German) Currently translated at 100.0% (119 of 119 strings) Translation: Element Call/element-call Translate-URL: https://translate.element.io/projects/element-call/element-call/de/ --- public/locales/de/app.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/public/locales/de/app.json b/public/locales/de/app.json index 99866dc5..60d68ea1 100644 --- a/public/locales/de/app.json +++ b/public/locales/de/app.json @@ -116,5 +116,6 @@ "By clicking \"Join call now\", you agree to our <2>End User Licensing Agreement (EULA)": "Mit einem Klick auf „Anruf beitreten“ akzeptierst du unseren <2>Endbenutzer-Lizenzvertrag (EULA)", "By clicking \"Go\", you agree to our <2>End User Licensing Agreement (EULA)": "Mit einem Klick auf „Los geht’s“ akzeptierst du unseren <2>Endbenutzer-Lizenzvertrag (EULA)", "This site is protected by ReCAPTCHA and the Google <2>Privacy Policy and <6>Terms of Service apply.<9>By clicking \"Register\", you agree to our <12>End User Licensing Agreement (EULA)": "Diese Seite wird durch reCAPTCHA geschützt und es gelten Googles <2>Datenschutzerklärung und <6>Nutzungsbedingungen. <9>Mit einem Klick auf „Registrieren“ akzeptierst du unseren <2>Endbenutzer-Lizenzvertrag (EULA)", - "Element Call is temporarily not end-to-end encrypted while we test scalability.": "Element Call ist temporär nicht Ende-zu-Ende-verschlüsselt, während wir die Skalierbarkeit testen." + "Element Call is temporarily not end-to-end encrypted while we test scalability.": "Element Call ist temporär nicht Ende-zu-Ende-verschlüsselt, während wir die Skalierbarkeit testen.", + "Connectivity to the server has been lost.": "Die Verbindung zum Server wurde getrennt." } From 82050427bab2410f80e15d82c5cd9dba776b9888 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Priit=20J=C3=B5er=C3=BC=C3=BCt?= Date: Mon, 24 Jul 2023 15:42:24 +0000 Subject: [PATCH 53/59] Translated using Weblate (Estonian) Currently translated at 100.0% (119 of 119 strings) Translation: Element Call/element-call Translate-URL: https://translate.element.io/projects/element-call/element-call/et/ --- public/locales/et/app.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/public/locales/et/app.json b/public/locales/et/app.json index d8659dbf..8d934ccc 100644 --- a/public/locales/et/app.json +++ b/public/locales/et/app.json @@ -116,5 +116,6 @@ "By clicking \"Go\", you agree to our <2>End User Licensing Agreement (EULA)": "Klõpsides „Jätka“, nõustud sa meie <2>Lõppkasutaja litsentsilepinguga (EULA)", "By clicking \"Join call now\", you agree to our <2>End User Licensing Agreement (EULA)": "Klõpsides „Liitu kõnega kohe“, nõustud sa meie <2>Lõppkasutaja litsentsilepinguga (EULA)", "This site is protected by ReCAPTCHA and the Google <2>Privacy Policy and <6>Terms of Service apply.<9>By clicking \"Register\", you agree to our <12>End User Licensing Agreement (EULA)": "Selles saidis on kasutusel ReCAPTCHA ja kehtivad Google'i <2>Privaatsuspoliitika ning <6>Teenusetingimused.<9>Klõpsides „Registreeru“, sa nõustud meie <12>Lõppkasutaja litsentsilepingu (EULA) tingimustega", - "Element Call is temporarily not end-to-end encrypted while we test scalability.": "Seni kuni me testime skaleeritavust, siis Element Call ajutiselt pole läbivalt krüptitud." + "Element Call is temporarily not end-to-end encrypted while we test scalability.": "Seni kuni me testime skaleeritavust, siis Element Call ajutiselt pole läbivalt krüptitud.", + "Connectivity to the server has been lost.": "Võrguühendus serveriga on katkenud." } From 71a56c22fdcce80209c2967c504f50faf2e690e5 Mon Sep 17 00:00:00 2001 From: Jozef Gaal Date: Mon, 24 Jul 2023 18:02:27 +0000 Subject: [PATCH 54/59] Translated using Weblate (Slovak) Currently translated at 100.0% (119 of 119 strings) Translation: Element Call/element-call Translate-URL: https://translate.element.io/projects/element-call/element-call/sk/ --- public/locales/sk/app.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/public/locales/sk/app.json b/public/locales/sk/app.json index 6f588a91..52668084 100644 --- a/public/locales/sk/app.json +++ b/public/locales/sk/app.json @@ -116,5 +116,6 @@ "By clicking \"Join call now\", you agree to our <2>End User Licensing Agreement (EULA)": "Kliknutím na \"Pripojiť sa k hovoru teraz\" súhlasíte s našou <2>Licenčnou zmluvou s koncovým používateľom (EULA)", "By clicking \"Go\", you agree to our <2>End User Licensing Agreement (EULA)": "Kliknutím na tlačidlo \"Prejsť\" vyjadrujete súhlas s našou <2>Licenčnou zmluvou s koncovým používateľom (EULA)", "This site is protected by ReCAPTCHA and the Google <2>Privacy Policy and <6>Terms of Service apply.<9>By clicking \"Register\", you agree to our <12>End User Licensing Agreement (EULA)": "Táto stránka je chránená systémom ReCAPTCHA a platia na ňu <2>Pravidlá ochrany osobných údajov spoločnosti Google a <6>Podmienky poskytovania služieb.<9>Kliknutím na tlačidlo \"Registrovať sa\" súhlasíte s našou <12>Licenčnou zmluvou s koncovým používateľom (EULA)", - "Element Call is temporarily not end-to-end encrypted while we test scalability.": "Element Call nie je dočasne šifrovaný, kým testujeme škálovateľnosť." + "Element Call is temporarily not end-to-end encrypted while we test scalability.": "Element Call nie je dočasne šifrovaný, kým testujeme škálovateľnosť.", + "Connectivity to the server has been lost.": "Spojenie so serverom sa stratilo." } From 9fdafaf39522b171a2daa7ad0ba2c854beecb5f9 Mon Sep 17 00:00:00 2001 From: David Baker Date: Mon, 24 Jul 2023 21:03:56 +0100 Subject: [PATCH 55/59] Add a useEventEmitterThree for livekit's event emitters and use it --- src/room/InCallView.tsx | 9 ++------- src/useEvents.ts | 18 ++++++++++++++++++ 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/src/room/InCallView.tsx b/src/room/InCallView.tsx index 604a5d95..0daa0282 100644 --- a/src/room/InCallView.tsx +++ b/src/room/InCallView.tsx @@ -84,6 +84,7 @@ import { useFullscreen } from "./useFullscreen"; import { useLayoutStates } from "../video-grid/Layout"; import { useSFUConfig } from "../livekit/OpenIDLoader"; import { E2EELock } from "../E2EELock"; +import { useEventEmitterThree } from "../useEvents"; const canScreenshare = "getDisplayMedia" in (navigator.mediaDevices ?? {}); // There is currently a bug in Safari our our code with cloning and sending MediaStreams @@ -204,13 +205,7 @@ export function InCallView({ onLeave(); }, [onLeave]); - useEffect(() => { - livekitRoom.on(RoomEvent.Disconnected, onDisconnected); - - return () => { - livekitRoom.off(RoomEvent.Disconnected, onDisconnected); - }; - }, [onDisconnected, livekitRoom]); + useEventEmitterThree(livekitRoom, RoomEvent.Disconnected, onDisconnected); useEffect(() => { widget?.api.transport.send( diff --git a/src/useEvents.ts b/src/useEvents.ts index 91a03abd..669cf863 100644 --- a/src/useEvents.ts +++ b/src/useEvents.ts @@ -15,6 +15,7 @@ limitations under the License. */ import { useEffect } from "react"; +import EventEmitter from "eventemitter3"; import type { Listener, @@ -59,3 +60,20 @@ export const useTypedEventEmitter = < }; }, [emitter, eventType, listener]); }; + +// Shortcut for registering a listener on an eventemitter3 EventEmitter (ie. what the LiveKit SDK uses) +export const useEventEmitterThree = < + EventType extends EventEmitter.ValidEventTypes, + T extends EventEmitter.EventNames +>( + emitter: EventEmitter, + eventType: T, + listener: EventEmitter.EventListener +) => { + useEffect(() => { + emitter.on(eventType, listener); + return () => { + emitter.off(eventType, listener); + }; + }, [emitter, eventType, listener]); +}; From 8e6380db0b68b93bdd8feebf6ff4f83ef6435b17 Mon Sep 17 00:00:00 2001 From: David Baker Date: Mon, 24 Jul 2023 21:33:13 +0100 Subject: [PATCH 56/59] Fix logs component states Also just display text when sending which makes the button jump around a bit but avoids hardcoding the width --- src/settings/RageshakeButton.module.css | 1 - src/settings/RageshakeButton.tsx | 6 +++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/settings/RageshakeButton.module.css b/src/settings/RageshakeButton.module.css index 66d0e2d9..5a621b6c 100644 --- a/src/settings/RageshakeButton.module.css +++ b/src/settings/RageshakeButton.module.css @@ -16,7 +16,6 @@ limitations under the License. .rageshakeControl { height: 50px; - width: 176px; text-align: center; vertical-align: middle; } diff --git a/src/settings/RageshakeButton.tsx b/src/settings/RageshakeButton.tsx index b6b045d8..599c565d 100644 --- a/src/settings/RageshakeButton.tsx +++ b/src/settings/RageshakeButton.tsx @@ -40,14 +40,14 @@ export const RageshakeButton = ({ description }: Props) => { if (!Config.get().rageshake?.submit_url) return null; let logsComponent: JSX.Element | null = null; - if (sent) { + if (sending) { + logsComponent = {t("Sending…")}; + } else if (sent) { logsComponent =
{t("Thanks!")}
; } else { let caption = t("Send debug logs"); if (error) { caption = t("Retry sending logs"); - } else if (sending) { - logsComponent = {t("Sending…")}; } logsComponent = ( From a6f803a091a588741ef7d9eb11c428c6e62a8830 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Tue, 25 Jul 2023 13:40:22 +0200 Subject: [PATCH 57/59] Don't change mute state while in the lobby MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/room/GroupCallView.tsx | 4 +++- src/room/LobbyView.tsx | 4 ++-- src/room/VideoPreview.tsx | 14 +++++--------- src/room/useGroupCall.ts | 2 +- 4 files changed, 11 insertions(+), 13 deletions(-) diff --git a/src/room/GroupCallView.tsx b/src/room/GroupCallView.tsx index 58fa0595..7de67cd8 100644 --- a/src/room/GroupCallView.tsx +++ b/src/room/GroupCallView.tsx @@ -229,6 +229,8 @@ export function GroupCallView({ groupCall.enter(); }, [groupCall]); + console.log("LOG participant size", participants.size); + if (error) { return ; } else if (state === GroupCallState.Entered && userChoices) { @@ -293,7 +295,7 @@ export function GroupCallView({ setUserChoices(choices); enter(); }} - muteAudio={participants.size > 8} + initWithMutedAudio={participants.size > 0} isEmbedded={isEmbedded} hideHeader={hideHeader} /> diff --git a/src/room/LobbyView.tsx b/src/room/LobbyView.tsx index 6b51542a..c97cc3ca 100644 --- a/src/room/LobbyView.tsx +++ b/src/room/LobbyView.tsx @@ -33,7 +33,7 @@ interface Props { onEnter: (userChoices: UserChoices) => void; isEmbedded: boolean; hideHeader: boolean; - muteAudio: boolean; + initWithMutedAudio: boolean; } export function LobbyView(props: Props) { @@ -67,7 +67,7 @@ export function LobbyView(props: Props) {
diff --git a/src/room/VideoPreview.tsx b/src/room/VideoPreview.tsx index d39aeb17..9ba11a89 100644 --- a/src/room/VideoPreview.tsx +++ b/src/room/VideoPreview.tsx @@ -40,13 +40,13 @@ export type MatrixInfo = { interface Props { matrixInfo: MatrixInfo; - muteAudio: boolean; + initWithMutedAudio: boolean; onUserChoicesChanged: (choices: UserChoices) => void; } export function VideoPreview({ matrixInfo, - muteAudio, + initWithMutedAudio, onUserChoicesChanged, }: Props) { const { client } = useClient(); @@ -69,13 +69,9 @@ export function VideoPreview({ // Create local media tracks. const [videoEnabled, setVideoEnabled] = useState(true); - const [audioEnabled, setAudioEnabled] = useState(!muteAudio); - - useEffect(() => { - if (muteAudio) { - setAudioEnabled(false); - } - }, [muteAudio]); + const [audioEnabled, setAudioEnabled] = useState( + !initWithMutedAudio + ); // The settings are updated as soon as the device changes. We wrap the settings value in a ref to store their initial value. // Not changing the device options prohibits the usePreviewTracks hook to recreate the tracks. diff --git a/src/room/useGroupCall.ts b/src/room/useGroupCall.ts index b0cbf76f..b02e7113 100644 --- a/src/room/useGroupCall.ts +++ b/src/room/useGroupCall.ts @@ -171,7 +171,7 @@ export function useGroupCall( isScreensharing: false, screenshareFeeds: [], requestingScreenshare: false, - participants: new Map(), + participants: getParticipants(groupCall), hasLocalParticipant: false, }); From f3615b73fcb939b6cf18c2c71bd208b2ec43b07b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Tue, 25 Jul 2023 15:18:45 +0200 Subject: [PATCH 58/59] /me is an idiot MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/room/GroupCallView.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/room/GroupCallView.tsx b/src/room/GroupCallView.tsx index 7de67cd8..2af6e314 100644 --- a/src/room/GroupCallView.tsx +++ b/src/room/GroupCallView.tsx @@ -295,7 +295,7 @@ export function GroupCallView({ setUserChoices(choices); enter(); }} - initWithMutedAudio={participants.size > 0} + initWithMutedAudio={participants.size > 8} isEmbedded={isEmbedded} hideHeader={hideHeader} /> From d8fcaa6eba13ad9421aa83a2ea56441227f3f810 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Tue, 25 Jul 2023 15:23:00 +0200 Subject: [PATCH 59/59] Make it a const MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/room/GroupCallView.tsx | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/room/GroupCallView.tsx b/src/room/GroupCallView.tsx index 2af6e314..d47cbb31 100644 --- a/src/room/GroupCallView.tsx +++ b/src/room/GroupCallView.tsx @@ -37,6 +37,12 @@ import { findDeviceByName } from "../media-utils"; import { OpenIDLoader } from "../livekit/OpenIDLoader"; import { ActiveCall } from "./InCallView"; +/** + * If there already is this many participants in the call, we automatically mute + * the user + */ +const MUTE_PARTICIPANT_COUNT = 8; + declare global { interface Window { groupCall?: GroupCall; @@ -295,7 +301,7 @@ export function GroupCallView({ setUserChoices(choices); enter(); }} - initWithMutedAudio={participants.size > 8} + initWithMutedAudio={participants.size > MUTE_PARTICIPANT_COUNT} isEmbedded={isEmbedded} hideHeader={hideHeader} />