From 3a2cee581e3315e88cb761606f66dc46174aaabe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Tue, 27 Jun 2023 17:25:35 +0200 Subject: [PATCH 01/27] Get LK info from the js-sdk MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/ClientContext.tsx | 12 ++++++++++++ src/room/InCallView.tsx | 5 ++--- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/src/ClientContext.tsx b/src/ClientContext.tsx index 08593a4b..acd9511c 100644 --- a/src/ClientContext.tsx +++ b/src/ClientContext.tsx @@ -138,6 +138,16 @@ export const ClientProvider: FC = ({ children }) => { const { user_id, device_id, access_token, passwordlessUser } = session; + const livekit = Config.get().livekit; + const foci = livekit + ? [ + { + url: livekit.server_url, + jwtServiceUrl: livekit.jwt_service_url, + }, + ] + : undefined; + try { return { client: await initClient( @@ -147,6 +157,7 @@ export const ClientProvider: FC = ({ children }) => { userId: user_id, deviceId: device_id, fallbackICEServerAllowed: fallbackICEServerAllowed, + foci, }, true ), @@ -163,6 +174,7 @@ export const ClientProvider: FC = ({ children }) => { userId: user_id, deviceId: device_id, fallbackICEServerAllowed: fallbackICEServerAllowed, + foci, }, false // Don't need the crypto store just to log out ); diff --git a/src/room/InCallView.tsx b/src/room/InCallView.tsx index 71257200..07f32523 100644 --- a/src/room/InCallView.tsx +++ b/src/room/InCallView.tsx @@ -72,7 +72,6 @@ import { MatrixInfo } from "./VideoPreview"; import { useJoinRule } from "./useJoinRule"; import { ParticipantInfo } from "./useGroupCall"; import { ItemData, TileContent } from "../video-grid/VideoTile"; -import { Config } from "../config/Config"; import { NewVideoGrid } from "../video-grid/NewVideoGrid"; import { OTelGroupCallMembership } from "../otel/OTelGroupCallMembership"; import { SettingsModal } from "../settings/SettingsModal"; @@ -96,8 +95,8 @@ interface ActiveCallProps extends Omit { export function ActiveCall(props: ActiveCallProps) { const livekitRoom = useLiveKit(props.userChoices, { - sfuUrl: Config.get().livekit!.server_url, - jwtUrl: `${Config.get().livekit!.jwt_service_url}/token`, + sfuUrl: props.groupCall.foci[0]!.url, + jwtUrl: `${props.groupCall.foci[0]!.jwtServiceUrl}/token`, roomName: props.matrixInfo.roomName, userDisplayName: props.matrixInfo.displayName, userIdentity: `${props.client.getUserId()}:${props.client.getDeviceId()}`, From 30ba12ccb84621d6d12ac3e3d5bf66bf8d62957e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Tue, 27 Jun 2023 17:31:54 +0200 Subject: [PATCH 02/27] Update js-sdk MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 6a077d05..f600b0f5 100644 --- a/package.json +++ b/package.json @@ -56,7 +56,7 @@ "i18next-http-backend": "^1.4.4", "livekit-client": "^1.9.7", "lodash": "^4.17.21", - "matrix-js-sdk": "github:matrix-org/matrix-js-sdk#af10b0c44b4a427c8d2224bfd6feb03a12cfd27e", + "matrix-js-sdk": "github:matrix-org/matrix-js-sdk#28492043ad64aa28718da169ec04ec893afeb975", "matrix-widget-api": "^1.3.1", "mermaid": "^8.13.8", "normalize.css": "^8.0.1", From 8996aa772cfc77dc1b93839f4b67d399c43928be Mon Sep 17 00:00:00 2001 From: David Baker Date: Wed, 28 Jun 2023 16:35:56 +0100 Subject: [PATCH 03/27] Initial support for getting SFO config using OIDC * Change `jwt_service_url` to `livekit_service_url` * Make it a POST so we can send the openID token sensibly * Get an OIDC token & pass it with the request * Read the SFU URL from there too and convert the auth server accordingly, althugh with no actual OIDC support yet, it just issues tokens blindly just as before and ignores the openid token completely. We'll need to update configs & the JWT service before merging this. --- backend/auth/server.go | 86 ++++++++++++++++++++++++------------- src/config/ConfigOptions.ts | 6 +-- src/livekit/useLiveKit.ts | 28 +++--------- src/room/GroupCallView.tsx | 7 +-- src/room/InCallView.tsx | 17 +++----- 5 files changed, 74 insertions(+), 70 deletions(-) diff --git a/backend/auth/server.go b/backend/auth/server.go index b3721e2e..f099784e 100644 --- a/backend/auth/server.go +++ b/backend/auth/server.go @@ -15,41 +15,74 @@ 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, GET, OPTIONS, PUT, DELETE") - w.Header().Set("Access-Control-Allow-Headers", "Accept, Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization") + 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") + /* + 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) + log.Printf("roomName: %s, name: %s, identity: %s", roomName, name, identity) - if roomName == "" || name == "" || identity == "" { - w.WriteHeader(http.StatusBadRequest) - return - } - - token, err := getJoinToken(h.key, h.secret, roomName, identity, name) - if err != nil { - w.WriteHeader(http.StatusInternalServerError) - return - } - - res := Response{token} - - w.Header().Set("Content-Type", "application/json") - json.NewEncoder(w).Encode(res) + if roomName == "" || name == "" || identity == "" { + w.WriteHeader(http.StatusBadRequest) + return + } + */ } func main() { @@ -68,15 +101,11 @@ func main() { secret: secret, } - http.HandleFunc("/token", handler.handle) + http.HandleFunc("/sfu/get", handler.handle) log.Fatal(http.ListenAndServe(":8080", nil)) } -type Response struct { - Token string `json:"accessToken"` -} - -func getJoinToken(apiKey, apiSecret, room, identity, name string) (string, error) { +func getJoinToken(apiKey, apiSecret, room, identity string) (string, error) { at := auth.NewAccessToken(apiKey, apiSecret) canPublish := true @@ -91,8 +120,7 @@ func getJoinToken(apiKey, apiSecret, room, identity, name string) (string, error at.AddGrant(grant). SetIdentity(identity). - SetValidFor(time.Hour). - SetName(name) + SetValidFor(time.Hour) return at.ToJWT() } diff --git a/src/config/ConfigOptions.ts b/src/config/ConfigOptions.ts index 1cee00fa..f878ec5e 100644 --- a/src/config/ConfigOptions.ts +++ b/src/config/ConfigOptions.ts @@ -55,10 +55,8 @@ export interface ConfigOptions { // Describes the LiveKit configuration to be used. livekit?: { - // The LiveKit server URL to connect to. - server_url: string; - // The link to the service that generates JWT tokens to join LiveKit rooms. - jwt_service_url: string; + // The link to the service that returns a livekit url and token to use it + livekit_service_url: string; }; /** diff --git a/src/livekit/useLiveKit.ts b/src/livekit/useLiveKit.ts index b1605506..3fd763eb 100644 --- a/src/livekit/useLiveKit.ts +++ b/src/livekit/useLiveKit.ts @@ -1,8 +1,9 @@ import { Room, RoomOptions } from "livekit-client"; -import { useLiveKitRoom, useToken } from "@livekit/components-react"; +import { useLiveKitRoom } from "@livekit/components-react"; import React from "react"; import { defaultLiveKitOptions } from "./options"; +import { SFUConfig } from "./openIDSFU"; export type UserChoices = { audio?: DeviceChoices; @@ -14,29 +15,10 @@ export type DeviceChoices = { enabled: boolean; }; -export type LiveKitConfig = { - sfuUrl: string; - jwtUrl: string; - roomName: string; - userDisplayName: string; - userIdentity: string; -}; - export function useLiveKit( userChoices: UserChoices, - config: LiveKitConfig + sfuConfig: SFUConfig ): Room | undefined { - const tokenOptions = React.useMemo( - () => ({ - userInfo: { - name: config.userDisplayName, - identity: config.userIdentity, - }, - }), - [config.userDisplayName, config.userIdentity] - ); - const token = useToken(config.jwtUrl, config.roomName, tokenOptions); - const roomOptions = React.useMemo((): RoomOptions => { const options = defaultLiveKitOptions; options.videoCaptureDefaults = { @@ -51,8 +33,8 @@ export function useLiveKit( }, [userChoices.video, userChoices.audio]); const { room } = useLiveKitRoom({ - token, - serverUrl: config.sfuUrl, + token: sfuConfig.jwt, + serverUrl: sfuConfig.url, audio: userChoices.audio?.enabled ?? false, video: userChoices.video?.enabled ?? false, options: roomOptions, diff --git a/src/room/GroupCallView.tsx b/src/room/GroupCallView.tsx index 471bfa47..2a81f444 100644 --- a/src/room/GroupCallView.tsx +++ b/src/room/GroupCallView.tsx @@ -28,7 +28,6 @@ import { useGroupCall } from "./useGroupCall"; import { ErrorView, FullScreenView } from "../FullScreenView"; import { LobbyView } from "./LobbyView"; import { MatrixInfo } from "./VideoPreview"; -import { ActiveCall } from "./InCallView"; import { CallEndedView } from "./CallEndedView"; import { useSentryGroupCallHandler } from "./useSentryGroupCallHandler"; import { PosthogAnalytics } from "../analytics/PosthogAnalytics"; @@ -36,6 +35,7 @@ import { useProfile } from "../profile/useProfile"; import { UserChoices } from "../livekit/useLiveKit"; import { findDeviceByName } from "../media-utils"; import { useRoomAvatar } from "./useRoomAvatar"; +import { OpenIDLoader } from "../livekit/OpenIDLoader"; declare global { interface Window { @@ -225,9 +225,10 @@ export function GroupCallView({ return ; } else if (state === GroupCallState.Entered && userChoices) { return ( - { +export interface ActiveCallProps extends Omit { userChoices: UserChoices; + sfuConfig: SFUConfig; } export function ActiveCall(props: ActiveCallProps) { - const livekitRoom = useLiveKit(props.userChoices, { - sfuUrl: Config.get().livekit!.server_url, - jwtUrl: `${Config.get().livekit!.jwt_service_url}/token`, - roomName: props.matrixInfo.roomName, - userDisplayName: props.matrixInfo.displayName, - userIdentity: `${props.client.getUserId()}:${props.client.getDeviceId()}`, - }); + const livekitRoom = useLiveKit(props.userChoices, props.sfuConfig); return livekitRoom && ; } -interface Props { +export interface InCallViewProps { client: MatrixClient; groupCall: GroupCall; livekitRoom: Room; @@ -125,7 +120,7 @@ export function InCallView({ hideHeader, matrixInfo, otelGroupCallMembership, -}: Props) { +}: InCallViewProps) { const { t } = useTranslation(); usePreventScroll(); From 008bb4f41d80a8cd46febec0795b8c8dfc37ceb0 Mon Sep 17 00:00:00 2001 From: David Baker Date: Wed, 28 Jun 2023 16:42:51 +0100 Subject: [PATCH 04/27] Also the other files --- src/livekit/OpenIDLoader.tsx | 55 ++++++++++++++++++++++++++++++++++ src/livekit/openIDSFU.ts | 58 ++++++++++++++++++++++++++++++++++++ 2 files changed, 113 insertions(+) create mode 100644 src/livekit/OpenIDLoader.tsx create mode 100644 src/livekit/openIDSFU.ts diff --git a/src/livekit/OpenIDLoader.tsx b/src/livekit/OpenIDLoader.tsx new file mode 100644 index 00000000..9e220034 --- /dev/null +++ b/src/livekit/OpenIDLoader.tsx @@ -0,0 +1,55 @@ +/* +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 { MatrixClient } from "matrix-js-sdk"; +import React, { useEffect, useState } from "react"; +import { logger } from "matrix-js-sdk/src/logger"; + +import { SFUConfig, getSFUConfigWithOpenID } from "./openIDSFU"; +import { ErrorView, LoadingView } from "../FullScreenView"; +import { ActiveCall, InCallViewProps } from "../room/InCallView"; +import { UserChoices } from "./useLiveKit"; + +interface Props extends Omit { + client: MatrixClient; + roomName: string; + userChoices: UserChoices; +} + +export function OpenIDLoader({ client, roomName, ...rest }: Props) { + const [sfuConfig, setSFUConfig] = useState(); + const [error, setError] = useState(); + + useEffect(() => { + (async () => { + try { + const result = await getSFUConfigWithOpenID(client, roomName); + setSFUConfig(result); + } catch (e) { + logger.error("Failed to fetch SFU config: ", e); + setError(new Error("Failed to fetch SFU config")); + } + })(); + }, [client, roomName]); + + if (error) { + return ; + } else if (sfuConfig) { + return ; + } else { + return ; + } +} diff --git a/src/livekit/openIDSFU.ts b/src/livekit/openIDSFU.ts new file mode 100644 index 00000000..422632a7 --- /dev/null +++ b/src/livekit/openIDSFU.ts @@ -0,0 +1,58 @@ +/* +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 { 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; +} + +export async function getSFUConfigWithOpenID( + client: MatrixClient, + roomName: string +): Promise { + const openIdToken = await client.getOpenIdToken(); + logger.debug("Got openID token", openIdToken); + + const livekitCfg = Config.get().livekit; + + if (!livekitCfg?.livekit_service_url) { + throw new Error("No livekit service URL defined"); + } + + const res = await fetch(livekitCfg.livekit_service_url + "/sfu/get", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + room: roomName, + openid_token: openIdToken, + remove_me_user_id: client.getUserId(), // the service will get this from the openid request + device_id: client.getDeviceId(), + }), + }); + if (res.status / 100 !== 2) { + throw new Error("SFO Config fetch failed with status code " + res.status); + } + const sfuConfig = await res.json(); + + return sfuConfig; +} From 4efd88905dfc7ba38b203b0cac25aa47867b61f9 Mon Sep 17 00:00:00 2001 From: David Baker Date: Fri, 30 Jun 2023 16:13:02 +0100 Subject: [PATCH 05/27] Move ...rest param --- src/livekit/OpenIDLoader.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/livekit/OpenIDLoader.tsx b/src/livekit/OpenIDLoader.tsx index 9e220034..c6d6bd9a 100644 --- a/src/livekit/OpenIDLoader.tsx +++ b/src/livekit/OpenIDLoader.tsx @@ -48,7 +48,7 @@ export function OpenIDLoader({ client, roomName, ...rest }: Props) { if (error) { return ; } else if (sfuConfig) { - return ; + return ; } else { return ; } From 23b8a61e7aae1b6d1fb43b269178af65053ada0d Mon Sep 17 00:00:00 2001 From: David Baker Date: Fri, 30 Jun 2023 18:12:58 +0100 Subject: [PATCH 06/27] Provide sfu config via context --- src/livekit/OpenIDLoader.tsx | 26 +++++++++++++++++++------- src/room/GroupCallView.tsx | 26 ++++++++++++++------------ src/room/InCallView.tsx | 6 +++--- 3 files changed, 36 insertions(+), 22 deletions(-) diff --git a/src/livekit/OpenIDLoader.tsx b/src/livekit/OpenIDLoader.tsx index c6d6bd9a..34aa5eca 100644 --- a/src/livekit/OpenIDLoader.tsx +++ b/src/livekit/OpenIDLoader.tsx @@ -15,21 +15,29 @@ limitations under the License. */ import { MatrixClient } from "matrix-js-sdk"; -import React, { useEffect, useState } from "react"; +import React, { + ReactNode, + createContext, + useContext, + useEffect, + useState, +} from "react"; import { logger } from "matrix-js-sdk/src/logger"; import { SFUConfig, getSFUConfigWithOpenID } from "./openIDSFU"; import { ErrorView, LoadingView } from "../FullScreenView"; -import { ActiveCall, InCallViewProps } from "../room/InCallView"; -import { UserChoices } from "./useLiveKit"; -interface Props extends Omit { +interface Props { client: MatrixClient; roomName: string; - userChoices: UserChoices; + children: ReactNode; } -export function OpenIDLoader({ client, roomName, ...rest }: Props) { +const SFUConfigContext = createContext(undefined); + +export const useSFUConfig = () => useContext(SFUConfigContext); + +export function OpenIDLoader({ client, roomName, children }: Props) { const [sfuConfig, setSFUConfig] = useState(); const [error, setError] = useState(); @@ -48,7 +56,11 @@ export function OpenIDLoader({ client, roomName, ...rest }: Props) { if (error) { return ; } else if (sfuConfig) { - return ; + return ( + + {children} + + ); } else { return ; } diff --git a/src/room/GroupCallView.tsx b/src/room/GroupCallView.tsx index 2bbd210b..888dcbf4 100644 --- a/src/room/GroupCallView.tsx +++ b/src/room/GroupCallView.tsx @@ -35,6 +35,7 @@ import { useProfile } from "../profile/useProfile"; import { UserChoices } from "../livekit/useLiveKit"; import { findDeviceByName } from "../media-utils"; import { OpenIDLoader } from "../livekit/OpenIDLoader"; +import { ActiveCall } from "./InCallView"; declare global { interface Window { @@ -222,18 +223,19 @@ export function GroupCallView({ return ; } else if (state === GroupCallState.Entered && userChoices) { return ( - + + + ); } else if (left) { // The call ended view is shown for two reasons: prompting guests to create diff --git a/src/room/InCallView.tsx b/src/room/InCallView.tsx index 10d8818a..79a7e73c 100644 --- a/src/room/InCallView.tsx +++ b/src/room/InCallView.tsx @@ -81,9 +81,9 @@ import { RageshakeRequestModal } from "./RageshakeRequestModal"; import { VideoTile } from "../video-grid/VideoTile"; import { UserChoices, useLiveKit } from "../livekit/useLiveKit"; import { useMediaDevices } from "../livekit/useMediaDevices"; -import { SFUConfig } from "../livekit/openIDSFU"; import { useFullscreen } from "./useFullscreen"; import { useLayoutStates } from "../video-grid/Layout"; +import { useSFUConfig } from "../livekit/OpenIDLoader"; const canScreenshare = "getDisplayMedia" in (navigator.mediaDevices ?? {}); // There is currently a bug in Safari our our code with cloning and sending MediaStreams @@ -93,11 +93,11 @@ const isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent); export interface ActiveCallProps extends Omit { userChoices: UserChoices; - sfuConfig: SFUConfig; } export function ActiveCall(props: ActiveCallProps) { - const livekitRoom = useLiveKit(props.userChoices, props.sfuConfig); + const sfuConfig = useSFUConfig(); + const livekitRoom = useLiveKit(props.userChoices, sfuConfig); return ( livekitRoom && ( From 81734b852c79f031b40b2cdcbdf0df6ceb6d0cd2 Mon Sep 17 00:00:00 2001 From: Daniel Abramov Date: Mon, 3 Jul 2023 11:26:43 +0100 Subject: [PATCH 07/27] Add simulcast layers for the screen sharing --- src/livekit/options.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/livekit/options.ts b/src/livekit/options.ts index 86db3029..86d3ee02 100644 --- a/src/livekit/options.ts +++ b/src/livekit/options.ts @@ -15,7 +15,11 @@ const defaultLiveKitPublishOptions: TrackPublishDefaults = { forceStereo: false, simulcast: true, videoSimulcastLayers: [VideoPresets.h180, VideoPresets.h216] as VideoPreset[], - screenShareEncoding: ScreenSharePresets.h1080fps15.encoding, + screenShareEncoding: ScreenSharePresets.h1080fps30.encoding, + screenShareSimulcastLayers: [ + new VideoPreset(1920, 1080, 1_500_000, 5, "medium"), + ScreenSharePresets.h1080fps15, + ] as VideoPreset[], stopMicTrackOnMute: false, videoCodec: "vp8", videoEncoding: VideoPresets.h360.encoding, From 9e95e8b5a7ae63c31c0bf813a42371f1446c9718 Mon Sep 17 00:00:00 2001 From: David Baker Date: Mon, 3 Jul 2023 13:00:25 +0100 Subject: [PATCH 08/27] User ID field no longer neccessary --- src/livekit/openIDSFU.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/livekit/openIDSFU.ts b/src/livekit/openIDSFU.ts index 422632a7..59a5da52 100644 --- a/src/livekit/openIDSFU.ts +++ b/src/livekit/openIDSFU.ts @@ -45,7 +45,6 @@ export async function getSFUConfigWithOpenID( body: JSON.stringify({ room: roomName, openid_token: openIdToken, - remove_me_user_id: client.getUserId(), // the service will get this from the openid request device_id: client.getDeviceId(), }), }); From f2eabec382b803e4be4642075f43f99191d224a4 Mon Sep 17 00:00:00 2001 From: David Baker Date: Mon, 3 Jul 2023 16:21:56 +0100 Subject: [PATCH 09/27] Be stricter with what is passed in to the openid components --- src/livekit/OpenIDLoader.tsx | 25 +++++++++++++++++++------ src/livekit/openIDSFU.ts | 19 +++++++++---------- src/room/GroupCallView.tsx | 13 ++++++++++++- 3 files changed, 40 insertions(+), 17 deletions(-) diff --git a/src/livekit/OpenIDLoader.tsx b/src/livekit/OpenIDLoader.tsx index 34aa5eca..df692f6d 100644 --- a/src/livekit/OpenIDLoader.tsx +++ b/src/livekit/OpenIDLoader.tsx @@ -14,7 +14,6 @@ See the License for the specific language governing permissions and limitations under the License. */ -import { MatrixClient } from "matrix-js-sdk"; import React, { ReactNode, createContext, @@ -24,11 +23,16 @@ import React, { } from "react"; import { logger } from "matrix-js-sdk/src/logger"; -import { SFUConfig, getSFUConfigWithOpenID } from "./openIDSFU"; +import { + OpenIDClientParts, + SFUConfig, + getSFUConfigWithOpenID, +} from "./openIDSFU"; import { ErrorView, LoadingView } from "../FullScreenView"; interface Props { - client: MatrixClient; + client: OpenIDClientParts; + livekitServiceURL: string; roomName: string; children: ReactNode; } @@ -37,21 +41,30 @@ const SFUConfigContext = createContext(undefined); export const useSFUConfig = () => useContext(SFUConfigContext); -export function OpenIDLoader({ client, roomName, children }: Props) { +export function OpenIDLoader({ + client, + livekitServiceURL, + roomName, + children, +}: Props) { const [sfuConfig, setSFUConfig] = useState(); const [error, setError] = useState(); useEffect(() => { (async () => { try { - const result = await getSFUConfigWithOpenID(client, roomName); + const result = await getSFUConfigWithOpenID( + client, + livekitServiceURL, + roomName + ); setSFUConfig(result); } catch (e) { logger.error("Failed to fetch SFU config: ", e); setError(new Error("Failed to fetch SFU config")); } })(); - }, [client, roomName]); + }, [client, livekitServiceURL, roomName]); if (error) { return ; diff --git a/src/livekit/openIDSFU.ts b/src/livekit/openIDSFU.ts index 59a5da52..9d46c518 100644 --- a/src/livekit/openIDSFU.ts +++ b/src/livekit/openIDSFU.ts @@ -17,27 +17,26 @@ limitations under the License. import { 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; } +// The bits we need from MatrixClient +export type OpenIDClientParts = Pick< + MatrixClient, + "getOpenIdToken" | "getDeviceId" +>; + export async function getSFUConfigWithOpenID( - client: MatrixClient, + client: OpenIDClientParts, + livekitServiceURL: string, roomName: string ): Promise { const openIdToken = await client.getOpenIdToken(); logger.debug("Got openID token", openIdToken); - const livekitCfg = Config.get().livekit; - - if (!livekitCfg?.livekit_service_url) { - throw new Error("No livekit service URL defined"); - } - - const res = await fetch(livekitCfg.livekit_service_url + "/sfu/get", { + const res = await fetch(livekitServiceURL + "/sfu/get", { method: "POST", headers: { "Content-Type": "application/json", diff --git a/src/room/GroupCallView.tsx b/src/room/GroupCallView.tsx index 888dcbf4..6df1246a 100644 --- a/src/room/GroupCallView.tsx +++ b/src/room/GroupCallView.tsx @@ -36,6 +36,7 @@ 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,11 +220,21 @@ export function GroupCallView({ undefined ); + const lkServiceURL = Config.get().livekit?.livekit_service_url; + + if (!lkServiceURL) { + return ; + } + if (error) { return ; } else if (state === GroupCallState.Entered && userChoices) { return ( - + Date: Mon, 3 Jul 2023 17:06:22 +0100 Subject: [PATCH 10/27] Unused import --- src/livekit/useLiveKit.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/livekit/useLiveKit.ts b/src/livekit/useLiveKit.ts index 7e8f26e1..c35f4f46 100644 --- a/src/livekit/useLiveKit.ts +++ b/src/livekit/useLiveKit.ts @@ -1,6 +1,5 @@ import { Room, RoomOptions } from "livekit-client"; import { useLiveKitRoom } from "@livekit/components-react"; -import React from "react"; import { useMemo } from "react"; import { defaultLiveKitOptions } from "./options"; From edcf9f3fd575dc0f424489a7d70c678958c45e04 Mon Sep 17 00:00:00 2001 From: David Baker Date: Mon, 3 Jul 2023 17:07:00 +0100 Subject: [PATCH 11/27] Use the repsonse,.ok rather than manual status code check --- src/livekit/openIDSFU.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/livekit/openIDSFU.ts b/src/livekit/openIDSFU.ts index 9d46c518..e4d0d45a 100644 --- a/src/livekit/openIDSFU.ts +++ b/src/livekit/openIDSFU.ts @@ -47,7 +47,7 @@ export async function getSFUConfigWithOpenID( device_id: client.getDeviceId(), }), }); - if (res.status / 100 !== 2) { + if (!res.ok) { throw new Error("SFO Config fetch failed with status code " + res.status); } const sfuConfig = await res.json(); From 5305a287fecc1574bb8b033482563f0f31e032d6 Mon Sep 17 00:00:00 2001 From: David Baker Date: Mon, 3 Jul 2023 17:10:36 +0100 Subject: [PATCH 12/27] Briefer syntax --- src/livekit/openIDSFU.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/livekit/openIDSFU.ts b/src/livekit/openIDSFU.ts index e4d0d45a..89a5d4f3 100644 --- a/src/livekit/openIDSFU.ts +++ b/src/livekit/openIDSFU.ts @@ -50,7 +50,5 @@ export async function getSFUConfigWithOpenID( if (!res.ok) { throw new Error("SFO Config fetch failed with status code " + res.status); } - const sfuConfig = await res.json(); - - return sfuConfig; + return await res.json(); } From c5f3941ad754a3d109fe1358f55fa232ce8bd5e9 Mon Sep 17 00:00:00 2001 From: David Baker Date: Mon, 3 Jul 2023 17:14:46 +0100 Subject: [PATCH 13/27] Unused import --- src/livekit/OpenIDLoader.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/livekit/OpenIDLoader.tsx b/src/livekit/OpenIDLoader.tsx index df692f6d..8ce3c998 100644 --- a/src/livekit/OpenIDLoader.tsx +++ b/src/livekit/OpenIDLoader.tsx @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -import React, { +import { ReactNode, createContext, useContext, From 6ca5b46df75cc5ccd2a72a99f8d7eac75d3a4e61 Mon Sep 17 00:00:00 2001 From: David Baker Date: Mon, 3 Jul 2023 21:43:11 +0100 Subject: [PATCH 14/27] Update config to deployed lk jwt service --- config/element_io_preview.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/config/element_io_preview.json b/config/element_io_preview.json index 30c2cc80..cb6fc088 100644 --- a/config/element_io_preview.json +++ b/config/element_io_preview.json @@ -6,8 +6,7 @@ } }, "livekit": { - "server_url": "wss://sfu.call.element.dev", - "jwt_service_url": "https://voip-sip-poc.element.io/lk/jwt_service" + "livekit_service_url": "https://lk-jwt-service.lab.element.dev" }, "posthog": { "api_key": "phc_rXGHx9vDmyEvyRxPziYtdVIv0ahEv8A9uLWFcCi1WcU", From fd8b05f909526646925a1b75983c044f9210cf3f Mon Sep 17 00:00:00 2001 From: Someone Date: Thu, 29 Jun 2023 10:48:57 +0000 Subject: [PATCH 15/27] Translated using Weblate (Vietnamese) Currently translated at 72.6% (85 of 117 strings) Translation: Element Call/element-call Translate-URL: https://translate.element.io/projects/element-call/element-call/vi/ --- public/locales/vi/app.json | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/public/locales/vi/app.json b/public/locales/vi/app.json index d8c86a7b..d67f05ae 100644 --- a/public/locales/vi/app.json +++ b/public/locales/vi/app.json @@ -78,5 +78,10 @@ "Debug log request": "Yêu cầu nhật ký gỡ lỗi", "By clicking \"Join call now\", you agree to our <2>Terms and conditions": "Khi nhấn vào \"Tham gia cuộc gọi\", bạn đồng ý với <2>Điều khoản và điều kiện của chúng tôi", "Another user on this call is having an issue. In order to better diagnose these issues we'd like to collect a debug log.": "Một người dùng khác trong cuộc gọi đang gặp vấn đề. Để có thể chẩn đoán tốt hơn chúng tôi muốn thu thập nhật ký gỡ lỗi.", - "<0>Why not finish by setting up a password to keep your account?<1>You'll be able to keep your name and set an avatar for use on future calls": "<0>Tại sao lại không hoàn thiện bằng cách đặt mật khẩu để giữ tài khoản của bạn?<1>Bạn sẽ có thể giữ tên và đặt ảnh đại diện cho những cuộc gọi tiếp theo." + "<0>Why not finish by setting up a password to keep your account?<1>You'll be able to keep your name and set an avatar for use on future calls": "<0>Tại sao lại không hoàn thiện bằng cách đặt mật khẩu để giữ tài khoản của bạn?<1>Bạn sẽ có thể giữ tên và đặt ảnh đại diện cho những cuộc gọi tiếp theo.", + "<0>Oops, something's gone wrong.": "<0>Ối, có cái gì đó sai.", + "{{displayName}} is presenting": "{{displayName}} đang trình bày", + "{{displayName}}, your call has ended.": "{{displayName}}, cuộc gọi đã kết thúc.", + "<0>We'd love to hear your feedback so we can improve your experience.": "<0>Chúng tôi muốn nghe phản hồi của bạn để còn cải thiện trải nghiệm cho bạn.", + "<0>Thanks for your feedback!": "<0>Cảm hơn vì đã phản hồi!" } From 5e6a682b9b6875d15240bad95d71421f2f12d70d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A0=D1=83=D1=81=D1=8F?= Date: Sun, 2 Jul 2023 18:16:08 +0000 Subject: [PATCH 16/27] Translated using Weblate (Russian) Currently translated at 100.0% (117 of 117 strings) Translation: Element Call/element-call Translate-URL: https://translate.element.io/projects/element-call/element-call/ru/ --- public/locales/ru/app.json | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/public/locales/ru/app.json b/public/locales/ru/app.json index 5d20cb6b..ad2b9340 100644 --- a/public/locales/ru/app.json +++ b/public/locales/ru/app.json @@ -95,11 +95,25 @@ "Audio": "Аудио", "Element Call Home": "Главная Element Call", "Copy": "Копировать", - "<0>Join call now<1>Or<2>Copy call link and join later": "<0>Присоединиться сейчас к звонку<1>или<1><2>Скопировать ссылку на звонок и присоединиться позже", + "<0>Join call now<1>Or<2>Copy call link and join later": "<0>Присоединиться сейчас<1>или<1><2>Скопировать ссылку и присоединиться позже", "<0>Submitting debug logs will help us track down the problem.": "<0>Отправка журналов поможет нам найти и устранить проблему.", "<0>Oops, something's gone wrong.": "<0>Упс, что-то пошло не так.", "Expose developer settings in the settings window.": "Раскрыть настройки разработчика в окне настроек.", "Developer Settings": "Настройки Разработчика", "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.": "Участвуя в этой бета-версии, вы соглашаетесь на сбор анонимных данных, которые мы используем для улучшения продукта. Более подробную информацию о том, какие данные мы отслеживаем, вы можете найти в нашей <2> Политике конфиденциальности и нашей <5> Политике использования файлов cookie.", - "<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>Вы можете отозвать согласие, сняв этот флажок. Если вы в данный момент находитесь в разговоре, эта настройка вступит в силу по окончании разговора." + "<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>Вы можете отозвать согласие, сняв этот флажок. Если вы в данный момент находитесь в разговоре, эта настройка вступит в силу по окончании разговора.", + "{{displayName}} is presenting": "{{displayName}} представляет", + "<0>We'd love to hear your feedback so we can improve your experience.": "<0>Мы будем рады видеть ваши отзывы, чтобы мы могли улучшить ваш опыт.", + "Thanks, we received your feedback!": "Спасибо. Мы получили ваш отзыв!", + "Feedback": "Отзыв", + "Submit": "Отправить", + "Submitting…": "Отправляем…", + "{{count}} stars|one": "{{count}} отмечен", + "{{count}} stars|other": "{{count}} отмеченных", + "{{displayName}}, your call has ended.": "{{displayName}}, ваш звонок окончен.", + "<0>Thanks for your feedback!": "<0>Спасибо за обратную связь!", + "Your feedback": "Ваш отзыв", + "How did it go?": "Как всё прошло?", + "If you are experiencing issues or simply would like to provide some feedback, please send us a short description below.": "Если у вас возникли проблемы или вы просто хотите оставить отзыв, отправьте нам краткое описание ниже.", + "Show connection stats": "Показать статистику соединения" } From fc7a7b17990aad156164a556cb728829bad6c2a5 Mon Sep 17 00:00:00 2001 From: David Baker Date: Tue, 4 Jul 2023 15:44:55 +0100 Subject: [PATCH 17/27] Use union type for openidloader state Co-authored-by: Daniel Abramov --- src/livekit/OpenIDLoader.tsx | 44 ++++++++++++++++++++++++------------ 1 file changed, 30 insertions(+), 14 deletions(-) diff --git a/src/livekit/OpenIDLoader.tsx b/src/livekit/OpenIDLoader.tsx index 8ce3c998..9e68d311 100644 --- a/src/livekit/OpenIDLoader.tsx +++ b/src/livekit/OpenIDLoader.tsx @@ -47,8 +47,9 @@ export function OpenIDLoader({ roomName, children, }: Props) { - const [sfuConfig, setSFUConfig] = useState(); - const [error, setError] = useState(); + const [state, setState] = useState< + SFUConfigLoading | SFUConfigLoaded | SFUConfigFailed + >({ kind: "loading" }); useEffect(() => { (async () => { @@ -58,23 +59,38 @@ export function OpenIDLoader({ livekitServiceURL, roomName ); - setSFUConfig(result); + setState({ kind: "loaded", sfuConfig: result }); } catch (e) { logger.error("Failed to fetch SFU config: ", e); - setError(new Error("Failed to fetch SFU config")); + setState({ kind: "failed", error: e }); } })(); }, [client, livekitServiceURL, roomName]); - if (error) { - return ; - } else if (sfuConfig) { - return ( - - {children} - - ); - } else { - return ; + switch (state.kind) { + case "loading": + return ; + case "failed": + return ; + case "loaded": + return ( + + {children} + + ); } } + +type SFUConfigLoading = { + kind: "loading"; +}; + +type SFUConfigLoaded = { + kind: "loaded"; + sfuConfig: SFUConfig; +}; + +type SFUConfigFailed = { + kind: "failed"; + error: Error; +}; From 792067ef224696aa2a4ac65f45bbdd2030c2bc41 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Tue, 4 Jul 2023 17:44:41 +0200 Subject: [PATCH 18/27] Update js-sd MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 183bdfb0..2fed945c 100644 --- a/package.json +++ b/package.json @@ -57,7 +57,7 @@ "i18next-http-backend": "^1.4.4", "livekit-client": "^1.9.7", "lodash": "^4.17.21", - "matrix-js-sdk": "github:matrix-org/matrix-js-sdk#28492043ad64aa28718da169ec04ec893afeb975", + "matrix-js-sdk": "github:matrix-org/matrix-js-sdk#426d29d6b9a9d71a3c0d7fe6f7bac3473cd10832", "matrix-widget-api": "^1.3.1", "mermaid": "^8.13.8", "normalize.css": "^8.0.1", From be89fb7dd9782aa8b6f8f8365b6d8ae9d8189ec8 Mon Sep 17 00:00:00 2001 From: David Baker Date: Tue, 4 Jul 2023 19:01:11 +0100 Subject: [PATCH 19/27] Revert changes to auth server code --- backend/auth/server.go | 86 ++++++++++++++---------------------------- 1 file changed, 29 insertions(+), 57 deletions(-) diff --git a/backend/auth/server.go b/backend/auth/server.go index f099784e..b3721e2e 100644 --- a/backend/auth/server.go +++ b/backend/auth/server.go @@ -15,74 +15,41 @@ 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") + w.Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE") + w.Header().Set("Access-Control-Allow-Headers", "Accept, Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization") // 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") + 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) + log.Printf("roomName: %s, name: %s, identity: %s", roomName, name, identity) - if roomName == "" || name == "" || identity == "" { - w.WriteHeader(http.StatusBadRequest) - return - } - */ + if roomName == "" || name == "" || identity == "" { + w.WriteHeader(http.StatusBadRequest) + return + } + + token, err := getJoinToken(h.key, h.secret, roomName, identity, name) + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + return + } + + res := Response{token} + + w.Header().Set("Content-Type", "application/json") + json.NewEncoder(w).Encode(res) } func main() { @@ -101,11 +68,15 @@ func main() { secret: secret, } - http.HandleFunc("/sfu/get", handler.handle) + http.HandleFunc("/token", handler.handle) log.Fatal(http.ListenAndServe(":8080", nil)) } -func getJoinToken(apiKey, apiSecret, room, identity string) (string, error) { +type Response struct { + Token string `json:"accessToken"` +} + +func getJoinToken(apiKey, apiSecret, room, identity, name string) (string, error) { at := auth.NewAccessToken(apiKey, apiSecret) canPublish := true @@ -120,7 +91,8 @@ func getJoinToken(apiKey, apiSecret, room, identity string) (string, error) { at.AddGrant(grant). SetIdentity(identity). - SetValidFor(time.Hour) + SetValidFor(time.Hour). + SetName(name) return at.ToJWT() } From 29553c11512c125be763db99cb8661ee8225fa9d Mon Sep 17 00:00:00 2001 From: David Baker Date: Tue, 4 Jul 2023 19:21:35 +0100 Subject: [PATCH 20/27] Revert "Support for getting SFU config using OIDC" --- config/element_io_preview.json | 3 +- src/config/ConfigOptions.ts | 6 ++- src/livekit/OpenIDLoader.tsx | 96 ---------------------------------- src/livekit/openIDSFU.ts | 54 ------------------- src/livekit/useLiveKit.ts | 28 ++++++++-- src/room/GroupCallView.tsx | 36 ++++--------- src/room/InCallView.tsx | 17 +++--- 7 files changed, 51 insertions(+), 189 deletions(-) delete mode 100644 src/livekit/OpenIDLoader.tsx delete mode 100644 src/livekit/openIDSFU.ts diff --git a/config/element_io_preview.json b/config/element_io_preview.json index cb6fc088..30c2cc80 100644 --- a/config/element_io_preview.json +++ b/config/element_io_preview.json @@ -6,7 +6,8 @@ } }, "livekit": { - "livekit_service_url": "https://lk-jwt-service.lab.element.dev" + "server_url": "wss://sfu.call.element.dev", + "jwt_service_url": "https://voip-sip-poc.element.io/lk/jwt_service" }, "posthog": { "api_key": "phc_rXGHx9vDmyEvyRxPziYtdVIv0ahEv8A9uLWFcCi1WcU", diff --git a/src/config/ConfigOptions.ts b/src/config/ConfigOptions.ts index f878ec5e..1cee00fa 100644 --- a/src/config/ConfigOptions.ts +++ b/src/config/ConfigOptions.ts @@ -55,8 +55,10 @@ export interface ConfigOptions { // Describes the LiveKit configuration to be used. livekit?: { - // The link to the service that returns a livekit url and token to use it - livekit_service_url: string; + // The LiveKit server URL to connect to. + server_url: string; + // The link to the service that generates JWT tokens to join LiveKit rooms. + jwt_service_url: string; }; /** diff --git a/src/livekit/OpenIDLoader.tsx b/src/livekit/OpenIDLoader.tsx deleted file mode 100644 index 9e68d311..00000000 --- a/src/livekit/OpenIDLoader.tsx +++ /dev/null @@ -1,96 +0,0 @@ -/* -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 { - ReactNode, - createContext, - useContext, - useEffect, - useState, -} from "react"; -import { logger } from "matrix-js-sdk/src/logger"; - -import { - OpenIDClientParts, - SFUConfig, - getSFUConfigWithOpenID, -} from "./openIDSFU"; -import { ErrorView, LoadingView } from "../FullScreenView"; - -interface Props { - client: OpenIDClientParts; - livekitServiceURL: string; - roomName: string; - children: ReactNode; -} - -const SFUConfigContext = createContext(undefined); - -export const useSFUConfig = () => useContext(SFUConfigContext); - -export function OpenIDLoader({ - client, - livekitServiceURL, - roomName, - children, -}: Props) { - const [state, setState] = useState< - SFUConfigLoading | SFUConfigLoaded | SFUConfigFailed - >({ kind: "loading" }); - - useEffect(() => { - (async () => { - try { - const result = await getSFUConfigWithOpenID( - client, - livekitServiceURL, - roomName - ); - setState({ kind: "loaded", sfuConfig: result }); - } catch (e) { - logger.error("Failed to fetch SFU config: ", e); - setState({ kind: "failed", error: e }); - } - })(); - }, [client, livekitServiceURL, roomName]); - - switch (state.kind) { - case "loading": - return ; - case "failed": - return ; - case "loaded": - return ( - - {children} - - ); - } -} - -type SFUConfigLoading = { - kind: "loading"; -}; - -type SFUConfigLoaded = { - kind: "loaded"; - sfuConfig: SFUConfig; -}; - -type SFUConfigFailed = { - kind: "failed"; - error: Error; -}; diff --git a/src/livekit/openIDSFU.ts b/src/livekit/openIDSFU.ts deleted file mode 100644 index 89a5d4f3..00000000 --- a/src/livekit/openIDSFU.ts +++ /dev/null @@ -1,54 +0,0 @@ -/* -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 { MatrixClient } from "matrix-js-sdk"; -import { logger } from "matrix-js-sdk/src/logger"; - -export interface SFUConfig { - url: string; - jwt: string; -} - -// The bits we need from MatrixClient -export type OpenIDClientParts = Pick< - MatrixClient, - "getOpenIdToken" | "getDeviceId" ->; - -export async function getSFUConfigWithOpenID( - client: OpenIDClientParts, - livekitServiceURL: string, - 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("SFO Config fetch failed with status code " + res.status); - } - return await res.json(); -} diff --git a/src/livekit/useLiveKit.ts b/src/livekit/useLiveKit.ts index c35f4f46..22767405 100644 --- a/src/livekit/useLiveKit.ts +++ b/src/livekit/useLiveKit.ts @@ -1,9 +1,8 @@ import { Room, RoomOptions } from "livekit-client"; -import { useLiveKitRoom } from "@livekit/components-react"; +import { useLiveKitRoom, useToken } from "@livekit/components-react"; import { useMemo } from "react"; import { defaultLiveKitOptions } from "./options"; -import { SFUConfig } from "./openIDSFU"; export type UserChoices = { audio?: DeviceChoices; @@ -15,10 +14,29 @@ export type DeviceChoices = { enabled: boolean; }; +export type LiveKitConfig = { + sfuUrl: string; + jwtUrl: string; + roomName: string; + userDisplayName: string; + userIdentity: string; +}; + export function useLiveKit( userChoices: UserChoices, - sfuConfig: SFUConfig + config: LiveKitConfig ): Room | undefined { + const tokenOptions = useMemo( + () => ({ + userInfo: { + name: config.userDisplayName, + identity: config.userIdentity, + }, + }), + [config.userDisplayName, config.userIdentity] + ); + const token = useToken(config.jwtUrl, config.roomName, tokenOptions); + const roomOptions = useMemo((): RoomOptions => { const options = defaultLiveKitOptions; options.videoCaptureDefaults = { @@ -33,8 +51,8 @@ export function useLiveKit( }, [userChoices.video, userChoices.audio]); const { room } = useLiveKitRoom({ - token: sfuConfig.jwt, - serverUrl: sfuConfig.url, + token, + serverUrl: config.sfuUrl, audio: userChoices.audio?.enabled ?? false, video: userChoices.video?.enabled ?? false, options: roomOptions, diff --git a/src/room/GroupCallView.tsx b/src/room/GroupCallView.tsx index 3532d737..76a4c414 100644 --- a/src/room/GroupCallView.tsx +++ b/src/room/GroupCallView.tsx @@ -28,15 +28,13 @@ import { useGroupCall } from "./useGroupCall"; import { ErrorView, FullScreenView } from "../FullScreenView"; import { LobbyView } from "./LobbyView"; import { MatrixInfo } from "./VideoPreview"; +import { ActiveCall } from "./InCallView"; import { CallEndedView } from "./CallEndedView"; import { useSentryGroupCallHandler } from "./useSentryGroupCallHandler"; import { PosthogAnalytics } from "../analytics/PosthogAnalytics"; import { useProfile } from "../profile/useProfile"; 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 { @@ -220,33 +218,21 @@ export function GroupCallView({ undefined ); - const lkServiceURL = Config.get().livekit?.livekit_service_url; - - if (!lkServiceURL) { - return ; - } - if (error) { return ; } else if (state === GroupCallState.Entered && userChoices) { return ( - - - + participants={participants} + onLeave={onLeave} + unencryptedEventsFromUsers={unencryptedEventsFromUsers} + hideHeader={hideHeader} + matrixInfo={matrixInfo} + userChoices={userChoices} + otelGroupCallMembership={otelGroupCallMembership} + /> ); } else if (left) { // The call ended view is shown for two reasons: prompting guests to create diff --git a/src/room/InCallView.tsx b/src/room/InCallView.tsx index 98a3a63b..e3c7e94f 100644 --- a/src/room/InCallView.tsx +++ b/src/room/InCallView.tsx @@ -72,6 +72,7 @@ import { MatrixInfo } from "./VideoPreview"; import { useJoinRule } from "./useJoinRule"; import { ParticipantInfo } from "./useGroupCall"; import { ItemData, TileContent } from "../video-grid/VideoTile"; +import { Config } from "../config/Config"; import { NewVideoGrid } from "../video-grid/NewVideoGrid"; import { OTelGroupCallMembership } from "../otel/OTelGroupCallMembership"; import { SettingsModal } from "../settings/SettingsModal"; @@ -83,7 +84,6 @@ import { UserChoices, useLiveKit } from "../livekit/useLiveKit"; import { useMediaDevices } from "../livekit/useMediaDevices"; import { useFullscreen } from "./useFullscreen"; import { useLayoutStates } from "../video-grid/Layout"; -import { useSFUConfig } from "../livekit/OpenIDLoader"; const canScreenshare = "getDisplayMedia" in (navigator.mediaDevices ?? {}); // There is currently a bug in Safari our our code with cloning and sending MediaStreams @@ -91,13 +91,18 @@ const canScreenshare = "getDisplayMedia" in (navigator.mediaDevices ?? {}); // For now we can disable screensharing in Safari. const isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent); -export interface ActiveCallProps extends Omit { +interface ActiveCallProps extends Omit { userChoices: UserChoices; } export function ActiveCall(props: ActiveCallProps) { - const sfuConfig = useSFUConfig(); - const livekitRoom = useLiveKit(props.userChoices, sfuConfig); + const livekitRoom = useLiveKit(props.userChoices, { + sfuUrl: Config.get().livekit!.server_url, + jwtUrl: `${Config.get().livekit!.jwt_service_url}/token`, + roomName: props.matrixInfo.roomName, + userDisplayName: props.matrixInfo.displayName, + userIdentity: `${props.client.getUserId()}:${props.client.getDeviceId()}`, + }); return ( livekitRoom && ( @@ -108,7 +113,7 @@ export function ActiveCall(props: ActiveCallProps) { ); } -export interface InCallViewProps { +interface Props { client: MatrixClient; groupCall: GroupCall; livekitRoom: Room; @@ -130,7 +135,7 @@ export function InCallView({ hideHeader, matrixInfo, otelGroupCallMembership, -}: InCallViewProps) { +}: Props) { const { t } = useTranslation(); usePreventScroll(); From fe96c4a4551d9a9a3dbf743432a5f5fa42ad73a9 Mon Sep 17 00:00:00 2001 From: David Baker Date: Tue, 4 Jul 2023 20:25:51 +0100 Subject: [PATCH 21/27] Update the livekit branch name in more places Fixes the CI to get the config from the right place. Also removes reference to an env that's already been deleted. --- .github/workflows/netlify-livekit.yaml | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/.github/workflows/netlify-livekit.yaml b/.github/workflows/netlify-livekit.yaml index f4463f91..3f66637b 100644 --- a/.github/workflows/netlify-livekit.yaml +++ b/.github/workflows/netlify-livekit.yaml @@ -12,7 +12,7 @@ jobs: runs-on: ubuntu-latest permissions: deployments: write - # Important: the 'branches' filter above will match the 'livekit-experiment' branch on forks, + # Important: the 'branches' filter above will match the 'livekit' branch on forks, # so we need to check the head repo too in order to not run on PRs from forks # We check the branch name again too just for completeness # (Is there a nicer way to see if a PR is from a fork?) @@ -24,7 +24,6 @@ jobs: with: step: start token: ${{ secrets.GITHUB_TOKEN }} - env: livekit-experiment-branch-cd ref: ${{ github.event.workflow_run.head_sha }} - name: "Download artifact" @@ -53,10 +52,10 @@ jobs: - name: Add redirects file # We fetch from github directly as we don't bother checking out the repo - run: curl -s https://raw.githubusercontent.com/vector-im/element-call/livekit-experiment/config/netlify_redirects > dist/_redirects + run: curl -s https://raw.githubusercontent.com/vector-im/element-call/livekit/config/netlify_redirects > dist/_redirects - name: Add config file - run: curl -s https://raw.githubusercontent.com/vector-im/element-call/livekit-experiment/config/element_io_preview.json > dist/config.json + run: curl -s https://raw.githubusercontent.com/vector-im/element-call/livekit/config/element_io_preview.json > dist/config.json - name: Deploy to Netlify id: netlify From 130b70756aafec0c2e4e6ef62c9dc8ea9d50b76d Mon Sep 17 00:00:00 2001 From: David Baker Date: Wed, 5 Jul 2023 13:12:37 +0100 Subject: [PATCH 22/27] Revert "Revert "Support for getting SFU config using OIDC"" --- config/element_io_preview.json | 3 +- src/config/ConfigOptions.ts | 6 +-- src/livekit/OpenIDLoader.tsx | 96 ++++++++++++++++++++++++++++++++++ src/livekit/openIDSFU.ts | 54 +++++++++++++++++++ src/livekit/useLiveKit.ts | 28 ++-------- src/room/GroupCallView.tsx | 36 +++++++++---- src/room/InCallView.tsx | 17 +++--- 7 files changed, 189 insertions(+), 51 deletions(-) create mode 100644 src/livekit/OpenIDLoader.tsx create mode 100644 src/livekit/openIDSFU.ts diff --git a/config/element_io_preview.json b/config/element_io_preview.json index 30c2cc80..cb6fc088 100644 --- a/config/element_io_preview.json +++ b/config/element_io_preview.json @@ -6,8 +6,7 @@ } }, "livekit": { - "server_url": "wss://sfu.call.element.dev", - "jwt_service_url": "https://voip-sip-poc.element.io/lk/jwt_service" + "livekit_service_url": "https://lk-jwt-service.lab.element.dev" }, "posthog": { "api_key": "phc_rXGHx9vDmyEvyRxPziYtdVIv0ahEv8A9uLWFcCi1WcU", diff --git a/src/config/ConfigOptions.ts b/src/config/ConfigOptions.ts index 1cee00fa..f878ec5e 100644 --- a/src/config/ConfigOptions.ts +++ b/src/config/ConfigOptions.ts @@ -55,10 +55,8 @@ export interface ConfigOptions { // Describes the LiveKit configuration to be used. livekit?: { - // The LiveKit server URL to connect to. - server_url: string; - // The link to the service that generates JWT tokens to join LiveKit rooms. - jwt_service_url: string; + // The link to the service that returns a livekit url and token to use it + livekit_service_url: string; }; /** diff --git a/src/livekit/OpenIDLoader.tsx b/src/livekit/OpenIDLoader.tsx new file mode 100644 index 00000000..9e68d311 --- /dev/null +++ b/src/livekit/OpenIDLoader.tsx @@ -0,0 +1,96 @@ +/* +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 { + ReactNode, + createContext, + useContext, + useEffect, + useState, +} from "react"; +import { logger } from "matrix-js-sdk/src/logger"; + +import { + OpenIDClientParts, + SFUConfig, + getSFUConfigWithOpenID, +} from "./openIDSFU"; +import { ErrorView, LoadingView } from "../FullScreenView"; + +interface Props { + client: OpenIDClientParts; + livekitServiceURL: string; + roomName: string; + children: ReactNode; +} + +const SFUConfigContext = createContext(undefined); + +export const useSFUConfig = () => useContext(SFUConfigContext); + +export function OpenIDLoader({ + client, + livekitServiceURL, + roomName, + children, +}: Props) { + const [state, setState] = useState< + SFUConfigLoading | SFUConfigLoaded | SFUConfigFailed + >({ kind: "loading" }); + + useEffect(() => { + (async () => { + try { + const result = await getSFUConfigWithOpenID( + client, + livekitServiceURL, + roomName + ); + setState({ kind: "loaded", sfuConfig: result }); + } catch (e) { + logger.error("Failed to fetch SFU config: ", e); + setState({ kind: "failed", error: e }); + } + })(); + }, [client, livekitServiceURL, roomName]); + + switch (state.kind) { + case "loading": + return ; + case "failed": + return ; + case "loaded": + return ( + + {children} + + ); + } +} + +type SFUConfigLoading = { + kind: "loading"; +}; + +type SFUConfigLoaded = { + kind: "loaded"; + sfuConfig: SFUConfig; +}; + +type SFUConfigFailed = { + kind: "failed"; + error: Error; +}; diff --git a/src/livekit/openIDSFU.ts b/src/livekit/openIDSFU.ts new file mode 100644 index 00000000..89a5d4f3 --- /dev/null +++ b/src/livekit/openIDSFU.ts @@ -0,0 +1,54 @@ +/* +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 { MatrixClient } from "matrix-js-sdk"; +import { logger } from "matrix-js-sdk/src/logger"; + +export interface SFUConfig { + url: string; + jwt: string; +} + +// The bits we need from MatrixClient +export type OpenIDClientParts = Pick< + MatrixClient, + "getOpenIdToken" | "getDeviceId" +>; + +export async function getSFUConfigWithOpenID( + client: OpenIDClientParts, + livekitServiceURL: string, + 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("SFO Config fetch failed with status code " + res.status); + } + return await res.json(); +} diff --git a/src/livekit/useLiveKit.ts b/src/livekit/useLiveKit.ts index 22767405..c35f4f46 100644 --- a/src/livekit/useLiveKit.ts +++ b/src/livekit/useLiveKit.ts @@ -1,8 +1,9 @@ import { Room, RoomOptions } from "livekit-client"; -import { useLiveKitRoom, useToken } from "@livekit/components-react"; +import { useLiveKitRoom } from "@livekit/components-react"; import { useMemo } from "react"; import { defaultLiveKitOptions } from "./options"; +import { SFUConfig } from "./openIDSFU"; export type UserChoices = { audio?: DeviceChoices; @@ -14,29 +15,10 @@ export type DeviceChoices = { enabled: boolean; }; -export type LiveKitConfig = { - sfuUrl: string; - jwtUrl: string; - roomName: string; - userDisplayName: string; - userIdentity: string; -}; - export function useLiveKit( userChoices: UserChoices, - config: LiveKitConfig + sfuConfig: SFUConfig ): Room | undefined { - const tokenOptions = useMemo( - () => ({ - userInfo: { - name: config.userDisplayName, - identity: config.userIdentity, - }, - }), - [config.userDisplayName, config.userIdentity] - ); - const token = useToken(config.jwtUrl, config.roomName, tokenOptions); - const roomOptions = useMemo((): RoomOptions => { const options = defaultLiveKitOptions; options.videoCaptureDefaults = { @@ -51,8 +33,8 @@ export function useLiveKit( }, [userChoices.video, userChoices.audio]); const { room } = useLiveKitRoom({ - token, - serverUrl: config.sfuUrl, + token: sfuConfig.jwt, + serverUrl: sfuConfig.url, audio: userChoices.audio?.enabled ?? false, video: userChoices.video?.enabled ?? false, options: roomOptions, diff --git a/src/room/GroupCallView.tsx b/src/room/GroupCallView.tsx index 76a4c414..3532d737 100644 --- a/src/room/GroupCallView.tsx +++ b/src/room/GroupCallView.tsx @@ -28,13 +28,15 @@ import { useGroupCall } from "./useGroupCall"; import { ErrorView, FullScreenView } from "../FullScreenView"; import { LobbyView } from "./LobbyView"; import { MatrixInfo } from "./VideoPreview"; -import { ActiveCall } from "./InCallView"; import { CallEndedView } from "./CallEndedView"; import { useSentryGroupCallHandler } from "./useSentryGroupCallHandler"; import { PosthogAnalytics } from "../analytics/PosthogAnalytics"; import { useProfile } from "../profile/useProfile"; 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 { @@ -218,21 +220,33 @@ export function GroupCallView({ undefined ); + const lkServiceURL = Config.get().livekit?.livekit_service_url; + + if (!lkServiceURL) { + return ; + } + if (error) { return ; } else if (state === GroupCallState.Entered && userChoices) { return ( - + livekitServiceURL={lkServiceURL} + roomName={matrixInfo.roomName} + > + + ); } else if (left) { // The call ended view is shown for two reasons: prompting guests to create diff --git a/src/room/InCallView.tsx b/src/room/InCallView.tsx index e3c7e94f..98a3a63b 100644 --- a/src/room/InCallView.tsx +++ b/src/room/InCallView.tsx @@ -72,7 +72,6 @@ import { MatrixInfo } from "./VideoPreview"; import { useJoinRule } from "./useJoinRule"; import { ParticipantInfo } from "./useGroupCall"; import { ItemData, TileContent } from "../video-grid/VideoTile"; -import { Config } from "../config/Config"; import { NewVideoGrid } from "../video-grid/NewVideoGrid"; import { OTelGroupCallMembership } from "../otel/OTelGroupCallMembership"; import { SettingsModal } from "../settings/SettingsModal"; @@ -84,6 +83,7 @@ import { UserChoices, useLiveKit } from "../livekit/useLiveKit"; import { useMediaDevices } from "../livekit/useMediaDevices"; import { useFullscreen } from "./useFullscreen"; import { useLayoutStates } from "../video-grid/Layout"; +import { useSFUConfig } from "../livekit/OpenIDLoader"; const canScreenshare = "getDisplayMedia" in (navigator.mediaDevices ?? {}); // There is currently a bug in Safari our our code with cloning and sending MediaStreams @@ -91,18 +91,13 @@ const canScreenshare = "getDisplayMedia" in (navigator.mediaDevices ?? {}); // For now we can disable screensharing in Safari. const isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent); -interface ActiveCallProps extends Omit { +export interface ActiveCallProps extends Omit { userChoices: UserChoices; } export function ActiveCall(props: ActiveCallProps) { - const livekitRoom = useLiveKit(props.userChoices, { - sfuUrl: Config.get().livekit!.server_url, - jwtUrl: `${Config.get().livekit!.jwt_service_url}/token`, - roomName: props.matrixInfo.roomName, - userDisplayName: props.matrixInfo.displayName, - userIdentity: `${props.client.getUserId()}:${props.client.getDeviceId()}`, - }); + const sfuConfig = useSFUConfig(); + const livekitRoom = useLiveKit(props.userChoices, sfuConfig); return ( livekitRoom && ( @@ -113,7 +108,7 @@ export function ActiveCall(props: ActiveCallProps) { ); } -interface Props { +export interface InCallViewProps { client: MatrixClient; groupCall: GroupCall; livekitRoom: Room; @@ -135,7 +130,7 @@ export function InCallView({ hideHeader, matrixInfo, otelGroupCallMembership, -}: Props) { +}: InCallViewProps) { const { t } = useTranslation(); usePreventScroll(); From c73c4d171d69fdf0f9d99d0a9abca7cd04bdd0ea Mon Sep 17 00:00:00 2001 From: David Baker Date: Wed, 5 Jul 2023 13:22:57 +0100 Subject: [PATCH 23/27] Fix netlify deployment Turns out the create deployment stateb really needs the env, even though it's empty. --- .github/workflows/netlify-livekit.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/netlify-livekit.yaml b/.github/workflows/netlify-livekit.yaml index 3f66637b..dffd90d3 100644 --- a/.github/workflows/netlify-livekit.yaml +++ b/.github/workflows/netlify-livekit.yaml @@ -24,6 +24,7 @@ jobs: with: step: start token: ${{ secrets.GITHUB_TOKEN }} + env: livekit-experiment-branch-cd ref: ${{ github.event.workflow_run.head_sha }} - name: "Download artifact" From 41371de50600e6a2c6583b1eb21d4ad8180a8838 Mon Sep 17 00:00:00 2001 From: David Baker Date: Wed, 5 Jul 2023 13:33:33 +0100 Subject: [PATCH 24/27] New livekit service URL --- config/element_io_preview.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/element_io_preview.json b/config/element_io_preview.json index cb6fc088..7e0caee1 100644 --- a/config/element_io_preview.json +++ b/config/element_io_preview.json @@ -6,7 +6,7 @@ } }, "livekit": { - "livekit_service_url": "https://lk-jwt-service.lab.element.dev" + "livekit_service_url": "https://livekit.element.dev/" }, "posthog": { "api_key": "phc_rXGHx9vDmyEvyRxPziYtdVIv0ahEv8A9uLWFcCi1WcU", From aab08d2bf1b099f99b8bffdcdf6c544bd1d81472 Mon Sep 17 00:00:00 2001 From: David Baker Date: Wed, 5 Jul 2023 13:45:06 +0100 Subject: [PATCH 25/27] Remove trailing slash from livekit service URL --- config/element_io_preview.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/element_io_preview.json b/config/element_io_preview.json index 7e0caee1..7d7eec2a 100644 --- a/config/element_io_preview.json +++ b/config/element_io_preview.json @@ -6,7 +6,7 @@ } }, "livekit": { - "livekit_service_url": "https://livekit.element.dev/" + "livekit_service_url": "https://livekit.element.dev" }, "posthog": { "api_key": "phc_rXGHx9vDmyEvyRxPziYtdVIv0ahEv8A9uLWFcCi1WcU", From 12f1d329349f3f980aadca0b8afac33d1746cadf Mon Sep 17 00:00:00 2001 From: raspin0 Date: Tue, 4 Jul 2023 19:30:07 +0000 Subject: [PATCH 26/27] Translated using Weblate (Polish) Currently translated at 100.0% (117 of 117 strings) Translation: Element Call/element-call Translate-URL: https://translate.element.io/projects/element-call/element-call/pl/ --- public/locales/pl/app.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/public/locales/pl/app.json b/public/locales/pl/app.json index 3e2b1d44..b702178b 100644 --- a/public/locales/pl/app.json +++ b/public/locales/pl/app.json @@ -113,5 +113,7 @@ "{{displayName}}, your call has ended.": "{{displayName}}, Twoje połączenie zostało zakończone.", "<0>Thanks for your feedback!": "<0>Dziękujemy za Twoją opinię!", "<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?" + "How did it go?": "Jak poszło?", + "{{displayName}} is presenting": "{{displayName}} prezentuje", + "Show connection stats": "Pokaż statystyki połączenia" } From 0646f327df322ffd1a325e870b33fe9d37a56d91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Thu, 6 Jul 2023 08:43:00 +0200 Subject: [PATCH 27/27] Change from TOC to EULA MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- public/locales/en-GB/app.json | 6 +++--- src/auth/RegisterPage.tsx | 4 +++- src/home/UnauthenticatedView.tsx | 4 +++- src/room/RoomAuthView.tsx | 4 +++- 4 files changed, 12 insertions(+), 6 deletions(-) diff --git a/public/locales/en-GB/app.json b/public/locales/en-GB/app.json index f14991d9..ac948232 100644 --- a/public/locales/en-GB/app.json +++ b/public/locales/en-GB/app.json @@ -16,8 +16,8 @@ "Another user on this call is having an issue. In order to better diagnose these issues we'd like to collect a debug log.": "Another user on this call is having an issue. In order to better diagnose these issues we'd like to collect a debug log.", "Audio": "Audio", "Avatar": "Avatar", - "By clicking \"Go\", you agree to our <2>Terms and conditions": "By clicking \"Go\", you agree to our <2>Terms and conditions", - "By clicking \"Join call now\", you agree to our <2>Terms and conditions": "By clicking \"Join call now\", you agree to our <2>Terms and conditions", + "By clicking \"Go\", you agree to our <2>End User Licensing Agreement (EULA)": "By clicking \"Go\", you agree to our <2>End User Licensing Agreement (EULA)", + "By clicking \"Join call now\", you agree to our <2>End User Licensing Agreement (EULA)": "By clicking \"Join call now\", you agree to our <2>End User Licensing Agreement (EULA)", "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.": "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.", "Call link copied": "Call link copied", "Call type menu": "Call type menu", @@ -99,7 +99,7 @@ "Thanks, we received your feedback!": "Thanks, we received your feedback!", "Thanks! We'll get right on it.": "Thanks! We'll get right on it.", "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>Terms and conditions": "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>Terms and conditions", + "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", "Turn on camera": "Turn on camera", "Unmute microphone": "Unmute microphone", diff --git a/src/auth/RegisterPage.tsx b/src/auth/RegisterPage.tsx index fd8e3f02..1fd3911c 100644 --- a/src/auth/RegisterPage.tsx +++ b/src/auth/RegisterPage.tsx @@ -211,7 +211,9 @@ export const RegisterPage: FC = () => { apply.
By clicking "Register", you agree to our{" "} - Terms and conditions + + End User Licensing Agreement (EULA) + {error && ( diff --git a/src/home/UnauthenticatedView.tsx b/src/home/UnauthenticatedView.tsx index 1fb1a84b..32877e4f 100644 --- a/src/home/UnauthenticatedView.tsx +++ b/src/home/UnauthenticatedView.tsx @@ -165,7 +165,9 @@ export const UnauthenticatedView: FC = () => { By clicking "Go", you agree to our{" "} - Terms and conditions + + End User Licensing Agreement (EULA) + {error && ( diff --git a/src/room/RoomAuthView.tsx b/src/room/RoomAuthView.tsx index de0c7594..e12013eb 100644 --- a/src/room/RoomAuthView.tsx +++ b/src/room/RoomAuthView.tsx @@ -83,7 +83,9 @@ export function RoomAuthView() { By clicking "Join call now", you agree to our{" "} - Terms and conditions + + End User Licensing Agreement (EULA) + {error && (