diff --git a/public/locales/en-GB/app.json b/public/locales/en-GB/app.json index f769c7e0..5d3d524c 100644 --- a/public/locales/en-GB/app.json +++ b/public/locales/en-GB/app.json @@ -9,7 +9,6 @@ "<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", - "<0>Join call now<1>Or<2>Copy call link and join later": "<0>Join call now<1>Or<2>Copy call link and join later", "<0>Oops, something's gone wrong.": "<0>Oops, something's gone wrong.", "<0>Submitting debug logs will help us track down the problem.": "<0>Submitting debug logs will help us track down the problem.", "<0>Thanks for your feedback!": "<0>Thanks for your feedback!", @@ -18,10 +17,10 @@ "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", + "Back to recents": "Back to recents", "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", "Camera": "Camera", "Close": "Close", "Confirm password": "Confirm password", @@ -107,7 +106,6 @@ "Submit": "Submit", "Submit feedback": "Submit feedback", "Submitting…": "Submitting…", - "Take me Home": "Take me Home", "Thanks, we received your feedback!": "Thanks, we received your feedback!", "Thanks!": "Thanks!", "This call already exists, would you like to join?": "This call already exists, would you like to join?", diff --git a/src/Header.module.css b/src/Header.module.css index 84b9df7b..4f36182d 100644 --- a/src/Header.module.css +++ b/src/Header.module.css @@ -21,6 +21,7 @@ limitations under the License. align-items: center; user-select: none; flex-shrink: 0; + padding-inline: var(--inline-content-inset); } .nav { @@ -28,7 +29,6 @@ limitations under the License. flex: 1; align-items: center; white-space: nowrap; - padding-inline: var(--inline-content-inset); height: 80px; } diff --git a/src/room/InCallView.module.css b/src/room/InCallView.module.css index b34daa13..588753ca 100644 --- a/src/room/InCallView.module.css +++ b/src/room/InCallView.module.css @@ -21,7 +21,7 @@ limitations under the License. min-height: 100%; height: 100%; width: 100%; - --footerPadding: 8px; + --footerPadding: var(--cpd-space-4x); --footerHeight: calc(50px + 2 * var(--footerPadding)); } @@ -83,17 +83,19 @@ limitations under the License. justify-self: end; } -@media (min-height: 300px) { +@media (min-height: 400px) { .inRoom { - --footerPadding: 40px; + --footerPadding: var(--cpd-space-10x); + } +} + +@media (min-height: 800px) { + .inRoom { + --footerPadding: var(--cpd-space-15x); } } @media (min-width: 800px) { - .inRoom { - --footerPadding: 60px; - } - .buttons { gap: var(--cpd-space-4x); } diff --git a/src/room/LobbyView.module.css b/src/room/LobbyView.module.css index 3bb0162e..53ff796e 100644 --- a/src/room/LobbyView.module.css +++ b/src/room/LobbyView.module.css @@ -14,61 +14,26 @@ See the License for the specific language governing permissions and limitations under the License. */ -.room { - position: relative; - display: flex; - flex-direction: column; - overflow: hidden; - min-height: 100%; -} - -.joinRoom { +.content { display: flex; flex-direction: column; align-items: center; + justify-content: center; + gap: var(--cpd-space-6x); flex: 1; overflow: hidden; height: 100%; + padding-block-end: var(--footerHeight); } -.joinRoomContent { - display: flex; - flex-direction: column; - align-items: center; - flex: 1; +@media (max-width: 500px) { + .join { + width: 100%; + } } -.joinRoomFooter { - margin: 20px 0; -} - -.homeLink { - margin-top: 50px; -} - -.joinCallButton { - position: absolute; - width: 100%; - max-width: 222px; - height: 40px; - bottom: 86px; - left: 50%; - font-weight: 600; - font-size: var(--font-size-body); - transform: translateX(-50%); -} - -.copyButton { - width: 320px !important; - margin-bottom: 15px; -} - -.copyButton:last-child { - margin-bottom: 0; -} - -.passwordField { - width: 320px !important; - margin-bottom: 20px; - flex: 0; +@media (min-height: 650px) { + .content { + gap: var(--cpd-space-10x); + } } diff --git a/src/room/LobbyView.tsx b/src/room/LobbyView.tsx index 716e1200..6b2ddf70 100644 --- a/src/room/LobbyView.tsx +++ b/src/room/LobbyView.tsx @@ -14,20 +14,28 @@ See the License for the specific language governing permissions and limitations under the License. */ -import { useRef, useEffect, FC } from "react"; -import { Trans, useTranslation } from "react-i18next"; +import { FC, useCallback, useState } from "react"; +import { useTranslation } from "react-i18next"; import { MatrixClient, RoomMember } from "matrix-js-sdk/src/matrix"; +import { Button, Link } from "@vector-im/compound-web"; +import classNames from "classnames"; +import { useHistory } from "react-router-dom"; import styles from "./LobbyView.module.css"; -import { Button, CopyButton } from "../button"; +import inCallStyles from "./InCallView.module.css"; import { Header, LeftNav, RightNav, RoomHeaderInfo } from "../Header"; -import { getRoomUrl } from "../matrix-utils"; -import { Body, Link } from "../typography/Typography"; import { useLocationNavigation } from "../useLocationNavigation"; import { MatrixInfo, VideoPreview } from "./VideoPreview"; import { MuteStates } from "./MuteStates"; -import { useRoomSharedKey } from "../e2ee/sharedKeyManagement"; import { ShareButton } from "../button/ShareButton"; +import { + HangupButton, + MicButton, + SettingsButton, + VideoButton, +} from "../button/Button"; +import { SettingsModal } from "../settings/SettingsModal"; +import { useMediaQuery } from "../useMediaQuery"; interface Props { client: MatrixClient; @@ -51,68 +59,98 @@ export const LobbyView: FC = ({ onShareClick, }) => { const { t } = useTranslation(); - const roomSharedKey = useRoomSharedKey(matrixInfo.roomId); useLocationNavigation(); - const joinCallButtonRef = useRef(null); - useEffect(() => { - if (joinCallButtonRef.current) { - joinCallButtonRef.current.focus(); - } - }, [joinCallButtonRef]); + const onAudioPress = useCallback( + () => muteStates.audio.setEnabled?.((e) => !e), + [muteStates] + ); + const onVideoPress = useCallback( + () => muteStates.video.setEnabled?.((e) => !e), + [muteStates] + ); + const [settingsModalOpen, setSettingsModalOpen] = useState(false); + + const openSettings = useCallback( + () => setSettingsModalOpen(true), + [setSettingsModalOpen] + ); + const closeSettings = useCallback( + () => setSettingsModalOpen(false), + [setSettingsModalOpen] + ); + + const history = useHistory(); + const onLeaveClick = useCallback(() => history.push("/"), [history]); + + const recentsButtonInFooter = useMediaQuery("(max-height: 500px)"); + const recentsButton = !isEmbedded && ( + + {t("Back to recents")} + + ); + + // TODO: Unify this component with InCallView, so we can get slick joining + // animations and don't have to feel bad about reusing its CSS return ( -
- {!hideHeader && ( -
- - - - - {onShareClick !== null && } - -
- )} -
-
- - + <> +
+ {!hideHeader && ( +
+ + + + + {onShareClick !== null && } + +
+ )} +
+ - Or - - Copy call link and join later - - + + {!recentsButtonInFooter && recentsButton} +
+
+ {recentsButtonInFooter && recentsButton} +
+ + + + {!isEmbedded && } +
- {!isEmbedded && ( - - - {t("Take me Home")} - - - )}
-
+ {client && ( + + )} + ); }; diff --git a/src/room/VideoPreview.module.css b/src/room/VideoPreview.module.css index aa4ffbb2..ad7b2671 100644 --- a/src/room/VideoPreview.module.css +++ b/src/room/VideoPreview.module.css @@ -15,21 +15,29 @@ limitations under the License. */ .preview { - position: relative; - min-height: 280px; - height: 50vh; - border-radius: 24px; - overflow: hidden; - background-color: var(--stopgap-bgColor3); - margin: 20px; + margin-inline: var(--inline-content-inset); + min-block-size: 0; + block-size: 50vh; } -.preview video { +.preview.content { + margin-inline: 0; +} + +.content { + position: relative; + block-size: 100%; + inline-size: 100%; + overflow: hidden; +} + +.content video { width: 100%; height: 100%; - object-fit: contain; + object-fit: cover; background-color: black; transform: scaleX(-1); + background-color: var(--cpd-color-bg-subtle-primary); } .avatarContainer { @@ -41,40 +49,32 @@ limitations under the License. display: flex; justify-content: center; align-items: center; - background-color: var(--stopgap-bgColor3); + background-color: var(--cpd-color-bg-subtle-secondary); } -.cameraPermissions { - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); - margin: 0; - text-align: center; -} - -.previewButtons { +.buttonBar { position: absolute; bottom: 0; left: 0; right: 0; - height: 66px; + height: calc(30 * var(--cpd-space-1x)); display: flex; justify-content: center; align-items: center; - background-color: var(--stopgap-background-85); + gap: var(--cpd-space-4x); + background: linear-gradient( + 180deg, + rgba(0, 0, 0, 0) 0%, + var(--cpd-color-bg-canvas-default) 100% + ); } -.previewButtons > * { - margin-right: 30px; +.preview.content .buttonBar { + padding-inline: var(--inline-content-inset); } -.previewButtons > :last-child { - margin-right: 0px; -} - -@media (min-width: 800px) { - .preview { - margin-top: 40px; +@media (min-aspect-ratio: 1 / 1) { + .preview video { + aspect-ratio: 16 / 9; } } diff --git a/src/room/VideoPreview.tsx b/src/room/VideoPreview.tsx index 5385ee73..fda5c3a4 100644 --- a/src/room/VideoPreview.tsx +++ b/src/room/VideoPreview.tsx @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -import { useEffect, useCallback, useMemo, useRef, FC, useState } from "react"; +import { useEffect, useMemo, useRef, FC, ReactNode } from "react"; import useMeasure from "react-use-measure"; import { ResizeObserver } from "@juggle/resize-observer"; import { usePreviewTracks } from "@livekit/components-react"; @@ -23,14 +23,14 @@ import { LocalVideoTrack, Track, } from "livekit-client"; +import classNames from "classnames"; -import { MicButton, SettingsButton, VideoButton } from "../button"; import { Avatar } from "../Avatar"; import styles from "./VideoPreview.module.css"; -import { SettingsModal } from "../settings/SettingsModal"; -import { useClient } from "../ClientContext"; import { useMediaDevices } from "../livekit/MediaDevicesContext"; import { MuteStates } from "./MuteStates"; +import { Glass } from "../Glass"; +import { useMediaQuery } from "../useMediaQuery"; export type MatrixInfo = { userId: string; @@ -46,23 +46,16 @@ export type MatrixInfo = { interface Props { matrixInfo: MatrixInfo; muteStates: MuteStates; + children: ReactNode; } -export const VideoPreview: FC = ({ matrixInfo, muteStates }) => { - const { client } = useClient(); +export const VideoPreview: FC = ({ + matrixInfo, + muteStates, + children, +}) => { const [previewRef, previewBounds] = useMeasure({ polyfill: ResizeObserver }); - const [settingsModalOpen, setSettingsModalOpen] = useState(false); - - const openSettings = useCallback( - () => setSettingsModalOpen(true), - [setSettingsModalOpen] - ); - const closeSettings = useCallback( - () => setSettingsModalOpen(false), - [setSettingsModalOpen] - ); - const devices = useMediaDevices(); // Capture the audio options as they were when we first mounted, because @@ -110,50 +103,35 @@ export const VideoPreview: FC = ({ matrixInfo, muteStates }) => { }; }, [videoTrack]); - const onAudioPress = useCallback( - () => muteStates.audio.setEnabled?.((e) => !e), - [muteStates] - ); - const onVideoPress = useCallback( - () => muteStates.video.setEnabled?.((e) => !e), - [muteStates] + const content = ( + <> +