mirror of
https://github.com/vector-im/element-call.git
synced 2024-11-15 00:04:59 +08:00
Merge branch 'livekit' into remove-walkie-talkie
This commit is contained in:
commit
8eb789d6aa
@ -26,6 +26,7 @@
|
||||
"Close": "Close",
|
||||
"Confirm password": "Confirm password",
|
||||
"Connectivity to the server has been lost.": "Connectivity to the server has been lost.",
|
||||
"Continue in browser": "Continue in browser",
|
||||
"Copied!": "Copied!",
|
||||
"Copy": "Copy",
|
||||
"Copy and share this call link": "Copy and share this call link",
|
||||
@ -65,14 +66,17 @@
|
||||
"Microphone off": "Microphone off",
|
||||
"Microphone on": "Microphone on",
|
||||
"More": "More",
|
||||
"Mute microphone": "Mute microphone",
|
||||
"Name of call": "Name of call",
|
||||
"No": "No",
|
||||
"Not encrypted": "Not encrypted",
|
||||
"Not now, return to home screen": "Not now, return to home screen",
|
||||
"Not registered yet? <2>Create an account</2>": "Not registered yet? <2>Create an account</2>",
|
||||
"Open in the app": "Open in the app",
|
||||
"Password": "Password",
|
||||
"Passwords must match": "Passwords must match",
|
||||
"Profile": "Profile",
|
||||
"Ready to join?": "Ready to join?",
|
||||
"Recaptcha dismissed": "Recaptcha dismissed",
|
||||
"Recaptcha not loaded": "Recaptcha not loaded",
|
||||
"Reconnect": "Reconnect",
|
||||
@ -82,6 +86,7 @@
|
||||
"Retry sending logs": "Retry sending logs",
|
||||
"Return to home screen": "Return to home screen",
|
||||
"Select an option": "Select an option",
|
||||
"Select app": "Select app",
|
||||
"Send debug logs": "Send debug logs",
|
||||
"Sending debug logs…": "Sending debug logs…",
|
||||
"Sending…": "Sending…",
|
||||
@ -97,6 +102,8 @@
|
||||
"Speaker": "Speaker",
|
||||
"Spotlight": "Spotlight",
|
||||
"Start new call": "Start new call",
|
||||
"Start video": "Start video",
|
||||
"Stop video": "Stop video",
|
||||
"Submit": "Submit",
|
||||
"Submit feedback": "Submit feedback",
|
||||
"Submitting…": "Submitting…",
|
||||
@ -105,12 +112,11 @@
|
||||
"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</2> and <6>Terms of Service</6> apply.<9></9>By clicking \"Register\", you agree to our <12>End User Licensing Agreement (EULA)</12>": "This site is protected by ReCAPTCHA and the Google <2>Privacy Policy</2> and <6>Terms of Service</6> apply.<9></9>By clicking \"Register\", you agree to our <12>End User Licensing Agreement (EULA)</12>",
|
||||
"Unmute microphone": "Unmute microphone",
|
||||
"User menu": "User menu",
|
||||
"Username": "Username",
|
||||
"Version: {{version}}": "Version: {{version}}",
|
||||
"Video": "Video",
|
||||
"Video off": "Video off",
|
||||
"Video on": "Video on",
|
||||
"Waiting for other participants…": "Waiting for other participants…",
|
||||
"Yes, join call": "Yes, join call",
|
||||
"You": "You",
|
||||
|
@ -90,6 +90,22 @@ interface UrlParams {
|
||||
password: string | null;
|
||||
}
|
||||
|
||||
export function editFragmentQuery(
|
||||
hash: string,
|
||||
edit: (params: URLSearchParams) => URLSearchParams
|
||||
): string {
|
||||
const fragmentQueryStart = hash.indexOf("?");
|
||||
const fragmentParams = edit(
|
||||
new URLSearchParams(
|
||||
fragmentQueryStart === -1 ? "" : hash.substring(fragmentQueryStart)
|
||||
)
|
||||
);
|
||||
return `${hash.substring(
|
||||
0,
|
||||
fragmentQueryStart
|
||||
)}?${fragmentParams.toString()}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the app parameters for the current URL.
|
||||
* @param ignoreRoomAlias If true, does not try to parse a room alias from the URL
|
||||
|
@ -145,11 +145,11 @@ export function MicButton({
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
const Icon = muted ? MicOffSolidIcon : MicOnSolidIcon;
|
||||
const label = muted ? t("Microphone off") : t("Microphone on");
|
||||
const label = muted ? t("Unmute microphone") : t("Mute microphone");
|
||||
|
||||
return (
|
||||
<Tooltip label={label}>
|
||||
<Button variant="toolbar" {...rest} on={!muted}>
|
||||
<Button variant="toolbar" {...rest} on={muted}>
|
||||
<Icon aria-label={label} />
|
||||
</Button>
|
||||
</Tooltip>
|
||||
@ -166,11 +166,11 @@ export function VideoButton({
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
const Icon = muted ? VideoCallOffIcon : VideoCallIcon;
|
||||
const label = muted ? t("Video off") : t("Video on");
|
||||
const label = muted ? t("Start video") : t("Stop video");
|
||||
|
||||
return (
|
||||
<Tooltip label={label}>
|
||||
<Button variant="toolbar" {...rest} on={!muted}>
|
||||
<Button variant="toolbar" {...rest} on={muted}>
|
||||
<Icon aria-label={label} />
|
||||
</Button>
|
||||
</Tooltip>
|
||||
|
33
src/room/AppSelectionModal.module.css
Normal file
33
src/room/AppSelectionModal.module.css
Normal file
@ -0,0 +1,33 @@
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
.modal p {
|
||||
text-align: center;
|
||||
margin-block-end: var(--cpd-space-8x);
|
||||
}
|
||||
|
||||
.modal button,
|
||||
.modal a {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.modal button {
|
||||
margin-block-end: var(--cpd-space-6x);
|
||||
}
|
||||
|
||||
.modal a {
|
||||
box-sizing: border-box;
|
||||
}
|
80
src/room/AppSelectionModal.tsx
Normal file
80
src/room/AppSelectionModal.tsx
Normal file
@ -0,0 +1,80 @@
|
||||
/*
|
||||
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 { FC, MouseEvent, useCallback, useMemo, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Button, Text } from "@vector-im/compound-web";
|
||||
import { ReactComponent as PopOutIcon } from "@vector-im/compound-design-tokens/icons/pop-out.svg";
|
||||
|
||||
import { Modal } from "../Modal";
|
||||
import { useRoomSharedKey } from "../e2ee/sharedKeyManagement";
|
||||
import { getRoomUrl } from "../matrix-utils";
|
||||
import styles from "./AppSelectionModal.module.css";
|
||||
import { editFragmentQuery } from "../UrlParams";
|
||||
|
||||
interface Props {
|
||||
roomId: string | null;
|
||||
}
|
||||
|
||||
export const AppSelectionModal: FC<Props> = ({ roomId }) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const [open, setOpen] = useState(true);
|
||||
const onBrowserClick = useCallback(
|
||||
(e: MouseEvent) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
setOpen(false);
|
||||
},
|
||||
[setOpen]
|
||||
);
|
||||
|
||||
const roomSharedKey = useRoomSharedKey(roomId ?? "");
|
||||
const appUrl = useMemo(() => {
|
||||
// If the room ID is not known, fall back to the URL of the current page
|
||||
const url = new URL(
|
||||
roomId === null
|
||||
? window.location.href
|
||||
: getRoomUrl(roomId, roomSharedKey ?? undefined)
|
||||
);
|
||||
// Edit the URL so that it opens in embedded mode. We do this for two
|
||||
// reasons: It causes the mobile app to limit the user to only visiting the
|
||||
// room in question, and it prevents this app selection prompt from being
|
||||
// shown a second time.
|
||||
url.hash = editFragmentQuery(url.hash, (params) => {
|
||||
params.set("isEmbedded", "");
|
||||
return params;
|
||||
});
|
||||
|
||||
const result = new URL("element://call");
|
||||
result.searchParams.set("url", url.toString());
|
||||
return result.toString();
|
||||
}, [roomId, roomSharedKey]);
|
||||
|
||||
return (
|
||||
<Modal className={styles.modal} title={t("Select app")} open={open}>
|
||||
<Text size="md" weight="semibold">
|
||||
{t("Ready to join?")}
|
||||
</Text>
|
||||
<Button kind="secondary" onClick={onBrowserClick}>
|
||||
{t("Continue in browser")}
|
||||
</Button>
|
||||
<Button as="a" href={appUrl} Icon={PopOutIcon}>
|
||||
{t("Open in the app")}
|
||||
</Button>
|
||||
</Modal>
|
||||
);
|
||||
};
|
@ -17,7 +17,8 @@ limitations under the License.
|
||||
.headline {
|
||||
text-align: center;
|
||||
margin-bottom: 60px;
|
||||
white-space: pre;
|
||||
white-space: pre-wrap;
|
||||
overflow-wrap: break-word;
|
||||
}
|
||||
|
||||
.callEndedContent {
|
||||
@ -66,6 +67,7 @@ limitations under the License.
|
||||
flex: 1;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding-inline: var(--inline-content-inset);
|
||||
}
|
||||
|
||||
.logo {
|
||||
|
@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import { FC, useEffect, useState, useCallback } from "react";
|
||||
import { FC, useEffect, useState, useCallback, ReactNode } from "react";
|
||||
import { MatrixRTCSession } from "matrix-js-sdk/src/matrixrtc/MatrixRTCSession";
|
||||
|
||||
import { useClientLegacy } from "../ClientContext";
|
||||
@ -26,6 +26,8 @@ import { useUrlParams } from "../UrlParams";
|
||||
import { useRegisterPasswordlessUser } from "../auth/useRegisterPasswordlessUser";
|
||||
import { useOptInAnalytics } from "../settings/useSetting";
|
||||
import { HomePage } from "../home/HomePage";
|
||||
import { platform } from "../Platform";
|
||||
import { AppSelectionModal } from "./AppSelectionModal";
|
||||
|
||||
export const RoomPage: FC = () => {
|
||||
const {
|
||||
@ -85,23 +87,18 @@ export const RoomPage: FC = () => {
|
||||
[client, passwordlessUser, isEmbedded, preload, hideHeader]
|
||||
);
|
||||
|
||||
let content: ReactNode;
|
||||
if (loading || isRegistering) {
|
||||
return <LoadingView />;
|
||||
}
|
||||
|
||||
if (error) {
|
||||
return <ErrorView error={error} />;
|
||||
}
|
||||
|
||||
if (!client) {
|
||||
return <RoomAuthView />;
|
||||
}
|
||||
|
||||
if (!roomIdOrAlias) {
|
||||
return <HomePage />;
|
||||
}
|
||||
|
||||
return (
|
||||
content = <LoadingView />;
|
||||
} else if (error) {
|
||||
content = <ErrorView error={error} />;
|
||||
} else if (!client) {
|
||||
content = <RoomAuthView />;
|
||||
} else if (!roomIdOrAlias) {
|
||||
// TODO: This doesn't belong here, the app routes need to be reworked
|
||||
content = <HomePage />;
|
||||
} else {
|
||||
content = (
|
||||
<GroupCallLoader
|
||||
client={client}
|
||||
roomIdOrAlias={roomIdOrAlias}
|
||||
@ -110,4 +107,16 @@ export const RoomPage: FC = () => {
|
||||
{groupCallView}
|
||||
</GroupCallLoader>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{content}
|
||||
{/* On mobile, show a prompt to launch the mobile app. If in embedded mode,
|
||||
that means we *are* in the mobile app and should show no further prompt. */}
|
||||
{(platform === "android" || platform === "ios") && !isEmbedded && (
|
||||
<AppSelectionModal roomId={roomId} />
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
53
yarn.lock
53
yarn.lock
@ -4564,11 +4564,16 @@
|
||||
"@types/tough-cookie" "*"
|
||||
parse5 "^7.0.0"
|
||||
|
||||
"@types/json-schema@*", "@types/json-schema@^7.0.12", "@types/json-schema@^7.0.4", "@types/json-schema@^7.0.5", "@types/json-schema@^7.0.8":
|
||||
"@types/json-schema@*", "@types/json-schema@^7.0.12", "@types/json-schema@^7.0.4", "@types/json-schema@^7.0.8":
|
||||
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/json-schema@^7.0.5":
|
||||
version "7.0.13"
|
||||
resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.13.tgz#02c24f4363176d2d18fc8b70b9f3c54aba178a85"
|
||||
integrity sha512-RbSSoHliUbnXj3ny0CNFOoxrIDV6SUGyStHsvDqosw6CkdPV8TtWGlfecuK4ToyMEAql6pzNxgCFKanovUzlgQ==
|
||||
|
||||
"@types/json5@^0.0.29":
|
||||
version "0.0.29"
|
||||
resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee"
|
||||
@ -4615,9 +4620,9 @@
|
||||
integrity sha512-Mnq3O9Xz52exs3mlxMcQuA7/9VFe/dXcrgAyfjLkABIqxXKOgBRjyazTxUbjsxDa4BP7hhPliyjVTP9RDP14xg==
|
||||
|
||||
"@types/node@^18.13.0":
|
||||
version "18.13.0"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-18.13.0.tgz#0400d1e6ce87e9d3032c19eb6c58205b0d3f7850"
|
||||
integrity sha512-gC3TazRzGoOnoKAhUx+Q0t8S9Tzs74z7m0ipwGpSqQrleP14hKxP4/JUeEQcD3W1/aIpnWl8pHowI7WokuZpXg==
|
||||
version "18.17.17"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-18.17.17.tgz#53cc07ce582c9d7c5850702a3c2cb0af0d7b0ca1"
|
||||
integrity sha512-cOxcXsQ2sxiwkykdJqvyFS+MLQPLvIdwh5l6gNg8qF6s+C7XSkEWOZjK+XhUZd+mYvHV/180g2cnCcIl4l06Pw==
|
||||
|
||||
"@types/normalize-package-data@^2.4.0":
|
||||
version "2.4.1"
|
||||
@ -4650,9 +4655,9 @@
|
||||
integrity sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==
|
||||
|
||||
"@types/q@^1.5.1":
|
||||
version "1.5.5"
|
||||
resolved "https://registry.yarnpkg.com/@types/q/-/q-1.5.5.tgz#75a2a8e7d8ab4b230414505d92335d1dcb53a6df"
|
||||
integrity sha512-L28j2FcJfSZOnL1WBjDYp2vUHCeIFlyYI/53EwD/rKUBQ7MtUUfbQWiyKJGpcnv4/WgrhWsFKrcPstcAt/J0tQ==
|
||||
version "1.5.6"
|
||||
resolved "https://registry.yarnpkg.com/@types/q/-/q-1.5.6.tgz#a6edffe8283910e46dc7a573621f928e6b47fa56"
|
||||
integrity sha512-IKjZ8RjTSwD4/YG+2gtj7BPFRB/lNbWKTiSj3M7U/TD2B7HfYCxvp2Zz6xA2WIY7pAuL1QOUPw8gQRbUrrq4fQ==
|
||||
|
||||
"@types/qs@^6.9.5":
|
||||
version "6.9.8"
|
||||
@ -4660,9 +4665,9 @@
|
||||
integrity sha512-u95svzDlTysU5xecFNTgfFG5RUWu1A9P0VzgpcIiGZA9iraHOdSzcxMxQ55DyeRaGCSxQi7LxXDI4rzq/MYfdg==
|
||||
|
||||
"@types/react-dom@^18.0.0":
|
||||
version "18.0.7"
|
||||
resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-18.0.7.tgz#ee7cf8ec4e6977e3f0a7b1d38bd89c75aa2aec28"
|
||||
integrity sha512-HaXc+BbqAZE1RdsK3tC8SbkFy6UL2xF76lT9rQs5JkPrJg3rWA3Ou/Lhw3YJQzEDkBpmJ79nBsfnd05WrBd2QQ==
|
||||
version "18.2.7"
|
||||
resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-18.2.7.tgz#67222a08c0a6ae0a0da33c3532348277c70abb63"
|
||||
integrity sha512-GRaAEriuT4zp9N4p1i8BDBYmEyfo+xQ3yHjJU4eiK5NDa1RmUZG+unZABUTK4/Ox/M+GaHwb6Ow8rUITrtjszA==
|
||||
dependencies:
|
||||
"@types/react" "*"
|
||||
|
||||
@ -4905,17 +4910,17 @@
|
||||
"@typescript-eslint/types" "6.1.0"
|
||||
eslint-visitor-keys "^3.4.1"
|
||||
|
||||
"@use-gesture/core@10.2.27":
|
||||
version "10.2.27"
|
||||
resolved "https://registry.yarnpkg.com/@use-gesture/core/-/core-10.2.27.tgz#0f24b17c036cd828ba07e3451ff45e2df959c6f5"
|
||||
integrity sha512-V4XV7hn9GAD2MYu8yBBVi5iuWBsAMfjPRMsEVzoTNGYH72tf0kFP+OKqGKc8YJFQIJx6yj+AOqxmEHOmx2/MEA==
|
||||
"@use-gesture/core@10.3.0":
|
||||
version "10.3.0"
|
||||
resolved "https://registry.yarnpkg.com/@use-gesture/core/-/core-10.3.0.tgz#9afd3777a45b2a08990a5dcfcf8d9ddd55b00db9"
|
||||
integrity sha512-rh+6MND31zfHcy9VU3dOZCqGY511lvGcfyJenN4cWZe0u1BH6brBpBddLVXhF2r4BMqWbvxfsbL7D287thJU2A==
|
||||
|
||||
"@use-gesture/react@^10.2.11":
|
||||
version "10.2.27"
|
||||
resolved "https://registry.yarnpkg.com/@use-gesture/react/-/react-10.2.27.tgz#7fbd50d14449ec5bc49c9b6cfef8a2845f5e0608"
|
||||
integrity sha512-7E5vnWCxeslWlxwZ8uKIcnUZVMTRMZ8cvSnLLKF1NkyNb3PnNiAzoXM4G1vTKJKRhgOTeI6wK1YsEpwo9ABV5w==
|
||||
version "10.3.0"
|
||||
resolved "https://registry.yarnpkg.com/@use-gesture/react/-/react-10.3.0.tgz#180534c821fd635c2853cbcfa813f92c94f27e3f"
|
||||
integrity sha512-3zc+Ve99z4usVP6l9knYVbVnZgfqhKah7sIG+PS2w+vpig2v2OLct05vs+ZXMzwxdNCMka8B+8WlOo0z6Pn6DA==
|
||||
dependencies:
|
||||
"@use-gesture/core" "10.2.27"
|
||||
"@use-gesture/core" "10.3.0"
|
||||
|
||||
"@vector-im/compound-design-tokens@^0.0.5":
|
||||
version "0.0.5"
|
||||
@ -5789,7 +5794,7 @@ babel-jest@^29.7.0:
|
||||
graceful-fs "^4.2.9"
|
||||
slash "^3.0.0"
|
||||
|
||||
babel-loader@^8.0.0:
|
||||
babel-loader@^8.0.0, babel-loader@^8.2.3:
|
||||
version "8.3.0"
|
||||
resolved "https://registry.yarnpkg.com/babel-loader/-/babel-loader-8.3.0.tgz#124936e841ba4fe8176786d6ff28add1f134d6a8"
|
||||
integrity sha512-H8SvsMF+m9t15HNLMipppzkC+Y2Yq+v3SonZyU70RBL/h1gxPkH08Ot8pEE9Z4Kd+czyWJClmFS8qzIP9OZ04Q==
|
||||
@ -5799,16 +5804,6 @@ babel-loader@^8.0.0:
|
||||
make-dir "^3.1.0"
|
||||
schema-utils "^2.6.5"
|
||||
|
||||
babel-loader@^8.2.3:
|
||||
version "8.2.5"
|
||||
resolved "https://registry.yarnpkg.com/babel-loader/-/babel-loader-8.2.5.tgz#d45f585e654d5a5d90f5350a779d7647c5ed512e"
|
||||
integrity sha512-OSiFfH89LrEMiWd4pLNqGz4CwJDtbs2ZVc+iGu2HrkRfPxId9F2anQj38IxWpmRfsUY0aBZYi1EFcd3mhtRMLQ==
|
||||
dependencies:
|
||||
find-cache-dir "^3.3.1"
|
||||
loader-utils "^2.0.0"
|
||||
make-dir "^3.1.0"
|
||||
schema-utils "^2.6.5"
|
||||
|
||||
babel-plugin-add-react-displayname@^0.0.5:
|
||||
version "0.0.5"
|
||||
resolved "https://registry.yarnpkg.com/babel-plugin-add-react-displayname/-/babel-plugin-add-react-displayname-0.0.5.tgz#339d4cddb7b65fd62d1df9db9fe04de134122bd5"
|
||||
|
Loading…
Reference in New Issue
Block a user