From 63ccd56573494cdfebff3099a79745c053fd3d8d Mon Sep 17 00:00:00 2001 From: Robin Townsend Date: Wed, 30 Aug 2023 21:58:29 -0400 Subject: [PATCH 01/28] Implement new designs for in-call footer buttons This basically amounts to swapping out some CSS, the icons, and the Tooltip component. --- package.json | 1 + public/locales/en-GB/app.json | 12 +- src/button/Button.module.css | 96 +++++++------ src/button/Button.tsx | 72 +++++----- src/index.css | 1 + src/room/InCallView.module.css | 17 +-- src/room/InCallView.tsx | 14 +- src/room/VideoPreview.tsx | 10 +- yarn.lock | 239 ++++++++++++++++++++++++++++++++- 9 files changed, 349 insertions(+), 113 deletions(-) diff --git a/package.json b/package.json index 05c3d048..9dc9024e 100644 --- a/package.json +++ b/package.json @@ -48,6 +48,7 @@ "@sentry/tracing": "^6.13.3", "@use-gesture/react": "^10.2.11", "@vector-im/compound-design-tokens": "^0.0.3", + "@vector-im/compound-web": "^0.2.15", "@vitejs/plugin-basic-ssl": "^1.0.1", "@vitejs/plugin-react": "^4.0.1", "classnames": "^2.3.1", diff --git a/public/locales/en-GB/app.json b/public/locales/en-GB/app.json index 076c65b8..f2605959 100644 --- a/public/locales/en-GB/app.json +++ b/public/locales/en-GB/app.json @@ -40,6 +40,7 @@ "Element Call Home": "Element Call Home", "Element Call is temporarily not end-to-end encrypted while we test scalability.": "Element Call is temporarily not end-to-end encrypted while we test scalability.", "Enable end-to-end encryption (password protected calls)": "Enable end-to-end encryption (password protected calls)", + "End call": "End call", "End-to-end encryption isn't supported on your browser.": "End-to-end encryption isn't supported on your browser.", "Exit full screen": "Exit full screen", "Expose developer settings in the settings window.": "Expose developer settings in the settings window.", @@ -61,15 +62,15 @@ "Join call": "Join call", "Join call now": "Join call now", "Join existing call?": "Join existing call?", - "Leave": "Leave", "Loading…": "Loading…", "Local volume": "Local volume", "Logging in…": "Logging in…", "Login": "Login", "Login to your account": "Login to your account", "Microphone": "Microphone", + "Microphone off": "Microphone off", + "Microphone on": "Microphone on", "More": "More", - "Mute microphone": "Mute microphone", "No": "No", "Not now, return to home screen": "Not now, return to home screen", "Not registered yet? <2>Create an account": "Not registered yet? <2>Create an account", @@ -91,13 +92,13 @@ "Sending…": "Sending…", "Settings": "Settings", "Share screen": "Share screen", + "Sharing screen": "Sharing screen", "Show call inspector": "Show call inspector", "Show connection stats": "Show connection stats", "Sign in": "Sign in", "Sign out": "Sign out", "Speaker": "Speaker", "Spotlight": "Spotlight", - "Stop sharing screen": "Stop sharing screen", "Submit": "Submit", "Submit feedback": "Submit feedback", "Submitting…": "Submitting…", @@ -107,15 +108,14 @@ "This call already exists, would you like to join?": "This call already exists, would you like to join?", "This call is not end-to-end encrypted.": "This call is not end-to-end encrypted.", "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", "User menu": "User menu", "Username": "Username", "Version: {{version}}": "Version: {{version}}", "Video": "Video", "Video call": "Video call", "Video call name": "Video call name", + "Video off": "Video off", + "Video on": "Video on", "Waiting for other participants…": "Waiting for other participants…", "Walkie-talkie call": "Walkie-talkie call", "Walkie-talkie call name": "Walkie-talkie call name", diff --git a/src/button/Button.module.css b/src/button/Button.module.css index dad18902..56108ea4 100644 --- a/src/button/Button.module.css +++ b/src/button/Button.module.css @@ -63,6 +63,7 @@ limitations under the License. .toolbarButton:disabled { background-color: var(--cpd-color-bg-action-primary-disabled); + box-shadow: none; } .toolbarButton, @@ -70,33 +71,39 @@ limitations under the License. width: 50px; height: 50px; border-radius: 50px; - background-color: var(--cpd-color-bg-subtle-secondary); -} - -.toolbarButton:hover, -.toolbarButtonSecondary:hover { - background-color: var(--cpd-color-bg-action-secondary-hovered); -} - -.toolbarButton:active, -.toolbarButtonSecondary:active { - background-color: var(--cpd-color-bg-action-secondary-pressed); + background-color: var(--cpd-color-bg-canvas-default); + color: var(--cpd-color-icon-primary); + border: 1px solid var(--cpd-color-gray-400); + box-shadow: 0px 1px 2px 0px rgba(16, 24, 40, 0.05); } .toolbarButton.on, .toolbarButton.off { background-color: var(--cpd-color-bg-action-primary-rest); + color: var(--cpd-color-icon-on-solid-primary); } .toolbarButtonSecondary.on { background-color: var(--cpd-color-text-success-primary); } +.toolbarButton:active, +.toolbarButtonSecondary:active { + background-color: var(--cpd-color-bg-subtle-primary); + border: none; + box-shadow: none; +} + +.toolbarButton.on:active, +.toolbarButton.off:active { + background-color: var(--cpd-color-bg-action-primary-pressed); +} + .iconButton:not(.stroke) svg * { fill: var(--cpd-color-bg-action-primary-rest); } -.iconButton:not(.stroke):hover svg * { +.iconButton:not(.stroke):tertiary svg * { fill: var(--cpd-color-icon-accent-tertiary); } @@ -110,31 +117,12 @@ limitations under the License. .hangupButton { background-color: var(--cpd-color-bg-critical-primary); + border-color: var(--cpd-color-border-critical-subtle); + color: var(--stopgap-color-on-solid-accent); } -.hangupButton:hover { - background-color: var(--cpd-color-bg-critical-hovered); -} - -.toolbarButton.hangupButton svg * { - fill: var(--stopgap-color-on-solid-accent); -} - -.toolbarButton svg *, -.toolbarButtonSecondary svg * { - fill: var(--cpd-color-icon-primary); -} - -.toolbarButton.on svg * { - fill: var(--cpd-color-icon-accent-tertiary); -} - -.toolbarButton.off svg * { - fill: var(--cpd-color-icon-on-solid-primary); -} - -.toolbarButtonSecondary.on svg * { - fill: var(--stopgap-color-on-solid-accent); +.hangupButton:active { + background-color: var(--cpd-color-bg-critical-pressed); } .secondary, @@ -196,10 +184,6 @@ limitations under the License. fill: var(--cpd-color-icon-accent-tertiary); } -.iconCopyButton:hover svg * { - fill: var(--cpd-color-icon-accent-tertiary); -} - .iconCopyButton.on svg *, .iconCopyButton.on:hover svg * { fill: transparent; @@ -212,10 +196,6 @@ limitations under the License. border-radius: 8px; } -.dropdownButton:hover { - background-color: var(--cpd-color-bg-action-secondary-hovered); -} - .dropdownButton:active, .dropdownButton.on { background-color: var(--cpd-color-bg-action-secondary-pressed); @@ -239,3 +219,33 @@ limitations under the License. color: var(--cpd-color-text-action-accent); cursor: pointer; } + +@media (hover: hover) { + .toolbarButton:hover, + .toolbarButtonSecondary:hover { + background-color: var(--cpd-color-bg-subtle-primary); + border: none; + box-shadow: none; + } + + .toolbarButton.on:hover, + .toolbarButton.off:hover { + background-color: var(--cpd-color-bg-action-primary-hovered); + } + + .iconButton:not(.stroke):hover svg * { + fill: var(--cpd-color-icon-accent-tertiary); + } + + .hangupButton:hover { + background-color: var(--cpd-color-bg-critical-hovered); + } + + .iconCopyButton:hover svg * { + fill: var(--cpd-color-icon-accent-tertiary); + } + + .dropdownButton:hover { + background-color: var(--cpd-color-bg-action-secondary-hovered); + } +} diff --git a/src/button/Button.tsx b/src/button/Button.tsx index 2de62a80..ac02c6ed 100644 --- a/src/button/Button.tsx +++ b/src/button/Button.tsx @@ -19,17 +19,18 @@ import classNames from "classnames"; import { useButton } from "@react-aria/button"; import { mergeProps, useObjectRef } from "@react-aria/utils"; import { useTranslation } from "react-i18next"; +import { Tooltip } from "@vector-im/compound-web"; +import { ReactComponent as MicOnSolidIcon } from "@vector-im/compound-design-tokens/icons/mic-on-solid.svg"; +import { ReactComponent as MicOffSolidIcon } from "@vector-im/compound-design-tokens/icons/mic-off-solid.svg"; +import { ReactComponent as VideoCallIcon } from "@vector-im/compound-design-tokens/icons/video-call.svg"; +import { ReactComponent as VideoCallOffIcon } from "@vector-im/compound-design-tokens/icons/video-call-off.svg"; +import { ReactComponent as EndCallIcon } from "@vector-im/compound-design-tokens/icons/end-call.svg"; +import { ReactComponent as ShareScreenSolidIcon } from "@vector-im/compound-design-tokens/icons/share-screen-solid.svg"; +import { ReactComponent as SettingsSolidIcon } from "@vector-im/compound-design-tokens/icons/settings-solid.svg"; +import { ReactComponent as UserAddSolidIcon } from "@vector-im/compound-design-tokens/icons/user-add-solid.svg"; +import { ReactComponent as ChevronDownIcon } from "@vector-im/compound-design-tokens/icons/chevron-down.svg"; import styles from "./Button.module.css"; -import { ReactComponent as MicIcon } from "../icons/Mic.svg"; -import { ReactComponent as MuteMicIcon } from "../icons/MuteMic.svg"; -import { ReactComponent as VideoIcon } from "../icons/Video.svg"; -import { ReactComponent as DisableVideoIcon } from "../icons/DisableVideo.svg"; -import { ReactComponent as HangupIcon } from "../icons/Hangup.svg"; -import { ReactComponent as ScreenshareIcon } from "../icons/Screenshare.svg"; -import { ReactComponent as SettingsIcon } from "../icons/Settings.svg"; -import { ReactComponent as AddUserIcon } from "../icons/AddUser.svg"; -import { ReactComponent as ArrowDownIcon } from "../icons/ArrowDown.svg"; import { ReactComponent as Fullscreen } from "../icons/Fullscreen.svg"; import { ReactComponent as FullscreenExit } from "../icons/FullscreenExit.svg"; import { TooltipTrigger } from "../Tooltip"; @@ -129,7 +130,7 @@ export const Button = forwardRef( > <> {children} - {variant === "dropdown" && } + {variant === "dropdown" && } ); @@ -147,13 +148,11 @@ export function MicButton({ const { t } = useTranslation(); return ( - (muted ? t("Unmute microphone") : t("Mute microphone"))} - > - - + ); } @@ -168,13 +167,11 @@ export function VideoButton({ const { t } = useTranslation(); return ( - (muted ? t("Turn on camera") : t("Turn off camera"))} - > - - + ); } @@ -191,13 +188,11 @@ export function ScreenshareButton({ const { t } = useTranslation(); return ( - (enabled ? t("Stop sharing screen") : t("Share screen"))} - > - - + ); } @@ -210,18 +205,17 @@ export function HangupButton({ [index: string]: unknown; }) { const { t } = useTranslation(); - const tooltip = useCallback(() => t("Leave"), [t]); return ( - + - + ); } @@ -234,14 +228,13 @@ export function SettingsButton({ [index: string]: unknown; }) { const { t } = useTranslation(); - const tooltip = useCallback(() => t("Settings"), [t]); return ( - + - + ); } @@ -256,14 +249,13 @@ export function InviteButton({ [index: string]: unknown; }) { const { t } = useTranslation(); - const tooltip = useCallback(() => t("Invite"), [t]); return ( - + - + ); } diff --git a/src/index.css b/src/index.css index 83c6c02f..e5969466 100644 --- a/src/index.css +++ b/src/index.css @@ -22,6 +22,7 @@ limitations under the License. @import "normalize.css/normalize.css"; @import "@vector-im/compound-design-tokens/assets/web/css/compound-design-tokens.css"; +@import "@vector-im/compound-web/dist/style.css"; :root { --font-family: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", diff --git a/src/room/InCallView.module.css b/src/room/InCallView.module.css index 963eec56..1fce9d0c 100644 --- a/src/room/InCallView.module.css +++ b/src/room/InCallView.module.css @@ -52,6 +52,7 @@ limitations under the License. display: flex; justify-content: center; align-items: center; + gap: 12px; padding: var(--footerPadding) 0; /* TODO: Un-hardcode these colors */ background: linear-gradient( @@ -68,14 +69,6 @@ limitations under the License. ); } -.footer > * { - margin-right: 30px; -} - -.footer > :last-child { - margin-right: 0px; -} - .maximised .footer { position: absolute; width: 100%; @@ -84,12 +77,16 @@ limitations under the License. @media (min-height: 300px) { .inRoom { - --footerPadding: 24px; + --footerPadding: 40px; } } @media (min-width: 800px) { .inRoom { - --footerPadding: 32px; + --footerPadding: 60px; + } + + .footer { + gap: 16px; } } diff --git a/src/room/InCallView.tsx b/src/room/InCallView.tsx index fa99a5c9..1d3701bc 100644 --- a/src/room/InCallView.tsx +++ b/src/room/InCallView.tsx @@ -381,19 +381,19 @@ export function InCallView({ const buttons: JSX.Element[] = []; buttons.push( - , , + ); diff --git a/src/room/VideoPreview.tsx b/src/room/VideoPreview.tsx index 26139967..1b5c07ce 100644 --- a/src/room/VideoPreview.tsx +++ b/src/room/VideoPreview.tsx @@ -131,16 +131,16 @@ export const VideoPreview: FC = ({ matrixInfo, muteStates }) => { )}
- +
diff --git a/yarn.lock b/yarn.lock index 0b9ca017..eb1015c8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1397,6 +1397,13 @@ dependencies: regenerator-runtime "^0.13.11" +"@babel/runtime@^7.13.10": + version "7.22.11" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.22.11.tgz#7a9ba3bbe406ad6f9e8dd4da2ece453eb23a77a4" + integrity sha512-ee7jVNlWN09+KftVOu9n7S8gQzD/Z6hN/I8VBRXW4P1+Xe7kJGXMwu8vds4aGIMHZnNbdpSWCfZZtinytpcAvA== + dependencies: + regenerator-runtime "^0.14.0" + "@babel/runtime@^7.13.9", "@babel/runtime@^7.9.2": version "7.19.4" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.19.4.tgz#a42f814502ee467d55b38dd1c256f53a7b885c78" @@ -1778,7 +1785,7 @@ dependencies: "@floating-ui/utils" "^0.1.1" -"@floating-ui/dom@^1.1.0": +"@floating-ui/dom@^1.1.0", "@floating-ui/dom@^1.5.1": version "1.5.1" resolved "https://registry.yarnpkg.com/@floating-ui/dom/-/dom-1.5.1.tgz#88b70defd002fe851f17b4a25efb2d3c04d7a8d7" integrity sha512-KwvVcPSXg6mQygvA1TjbN/gh///36kKtllIF8SUm0qpFj8+rvYrpvlYdL1JoA71SHpDqgSSdGOSoQ0Mp3uY5aw== @@ -1786,6 +1793,13 @@ "@floating-ui/core" "^1.4.1" "@floating-ui/utils" "^0.1.1" +"@floating-ui/react-dom@^2.0.0": + version "2.0.2" + resolved "https://registry.yarnpkg.com/@floating-ui/react-dom/-/react-dom-2.0.2.tgz#fab244d64db08e6bed7be4b5fcce65315ef44d20" + integrity sha512-5qhlDvjaLmAst/rKb3VdlCinwTF4EYMiVxuuc/HVUjs46W0zgtbMmAZ1UTsDrRTxRmUEzl92mOtWbeeXL26lSQ== + dependencies: + "@floating-ui/dom" "^1.5.1" + "@floating-ui/utils@^0.1.1": version "0.1.1" resolved "https://registry.yarnpkg.com/@floating-ui/utils/-/utils-0.1.1.tgz#1a5b1959a528e374e8037c4396c3e825d6cf4a83" @@ -2467,6 +2481,206 @@ schema-utils "^3.0.0" source-map "^0.7.3" +"@radix-ui/primitive@1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@radix-ui/primitive/-/primitive-1.0.1.tgz#e46f9958b35d10e9f6dc71c497305c22e3e55dbd" + integrity sha512-yQ8oGX2GVsEYMWGxcovu1uGWPCxV5BFfeeYxqPmuAzUyLT9qmaMXSAhXpb0WrspIeqYzdJpkh2vHModJPgRIaw== + dependencies: + "@babel/runtime" "^7.13.10" + +"@radix-ui/react-arrow@1.0.3": + version "1.0.3" + resolved "https://registry.yarnpkg.com/@radix-ui/react-arrow/-/react-arrow-1.0.3.tgz#c24f7968996ed934d57fe6cde5d6ec7266e1d25d" + integrity sha512-wSP+pHsB/jQRaL6voubsQ/ZlrGBHHrOjmBnr19hxYgtS0WvAFwZhK2WP/YY5yF9uKECCEEDGxuLxq1NBK51wFA== + dependencies: + "@babel/runtime" "^7.13.10" + "@radix-ui/react-primitive" "1.0.3" + +"@radix-ui/react-compose-refs@1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@radix-ui/react-compose-refs/-/react-compose-refs-1.0.1.tgz#7ed868b66946aa6030e580b1ffca386dd4d21989" + integrity sha512-fDSBgd44FKHa1FRMU59qBMPFcl2PZE+2nmqunj+BWFyYYjnhIDWL2ItDs3rrbJDQOtzt5nIebLCQc4QRfz6LJw== + dependencies: + "@babel/runtime" "^7.13.10" + +"@radix-ui/react-context@1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@radix-ui/react-context/-/react-context-1.0.1.tgz#fe46e67c96b240de59187dcb7a1a50ce3e2ec00c" + integrity sha512-ebbrdFoYTcuZ0v4wG5tedGnp9tzcV8awzsxYph7gXUyvnNLuTIcCk1q17JEbnVhXAKG9oX3KtchwiMIAYp9NLg== + dependencies: + "@babel/runtime" "^7.13.10" + +"@radix-ui/react-dismissable-layer@1.0.4": + version "1.0.4" + resolved "https://registry.yarnpkg.com/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.0.4.tgz#883a48f5f938fa679427aa17fcba70c5494c6978" + integrity sha512-7UpBa/RKMoHJYjie1gkF1DlK8l1fdU/VKDpoS3rCCo8YBJR294GwcEHyxHw72yvphJ7ld0AXEcSLAzY2F/WyCg== + dependencies: + "@babel/runtime" "^7.13.10" + "@radix-ui/primitive" "1.0.1" + "@radix-ui/react-compose-refs" "1.0.1" + "@radix-ui/react-primitive" "1.0.3" + "@radix-ui/react-use-callback-ref" "1.0.1" + "@radix-ui/react-use-escape-keydown" "1.0.3" + +"@radix-ui/react-form@^0.0.3": + version "0.0.3" + resolved "https://registry.yarnpkg.com/@radix-ui/react-form/-/react-form-0.0.3.tgz#328e7163e723ccc748459d66a2d685d7b4f85d5a" + integrity sha512-kgE+Z/haV6fxE5WqIXj05KkaXa3OkZASoTDy25yX2EIp/x0c54rOH/vFr5nOZTg7n7T1z8bSyXmiVIFP9bbhPQ== + dependencies: + "@babel/runtime" "^7.13.10" + "@radix-ui/primitive" "1.0.1" + "@radix-ui/react-compose-refs" "1.0.1" + "@radix-ui/react-context" "1.0.1" + "@radix-ui/react-id" "1.0.1" + "@radix-ui/react-label" "2.0.2" + "@radix-ui/react-primitive" "1.0.3" + +"@radix-ui/react-id@1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@radix-ui/react-id/-/react-id-1.0.1.tgz#73cdc181f650e4df24f0b6a5b7aa426b912c88c0" + integrity sha512-tI7sT/kqYp8p96yGWY1OAnLHrqDgzHefRBKQ2YAkBS5ja7QLcZ9Z/uY7bEjPUatf8RomoXM8/1sMj1IJaE5UzQ== + dependencies: + "@babel/runtime" "^7.13.10" + "@radix-ui/react-use-layout-effect" "1.0.1" + +"@radix-ui/react-label@2.0.2": + version "2.0.2" + resolved "https://registry.yarnpkg.com/@radix-ui/react-label/-/react-label-2.0.2.tgz#9c72f1d334aac996fdc27b48a8bdddd82108fb6d" + integrity sha512-N5ehvlM7qoTLx7nWPodsPYPgMzA5WM8zZChQg8nyFJKnDO5WHdba1vv5/H6IO5LtJMfD2Q3wh1qHFGNtK0w3bQ== + dependencies: + "@babel/runtime" "^7.13.10" + "@radix-ui/react-primitive" "1.0.3" + +"@radix-ui/react-popper@1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@radix-ui/react-popper/-/react-popper-1.1.2.tgz#4c0b96fcd188dc1f334e02dba2d538973ad842e9" + integrity sha512-1CnGGfFi/bbqtJZZ0P/NQY20xdG3E0LALJaLUEoKwPLwl6PPPfbeiCqMVQnhoFRAxjJj4RpBRJzDmUgsex2tSg== + dependencies: + "@babel/runtime" "^7.13.10" + "@floating-ui/react-dom" "^2.0.0" + "@radix-ui/react-arrow" "1.0.3" + "@radix-ui/react-compose-refs" "1.0.1" + "@radix-ui/react-context" "1.0.1" + "@radix-ui/react-primitive" "1.0.3" + "@radix-ui/react-use-callback-ref" "1.0.1" + "@radix-ui/react-use-layout-effect" "1.0.1" + "@radix-ui/react-use-rect" "1.0.1" + "@radix-ui/react-use-size" "1.0.1" + "@radix-ui/rect" "1.0.1" + +"@radix-ui/react-portal@1.0.3": + version "1.0.3" + resolved "https://registry.yarnpkg.com/@radix-ui/react-portal/-/react-portal-1.0.3.tgz#ffb961244c8ed1b46f039e6c215a6c4d9989bda1" + integrity sha512-xLYZeHrWoPmA5mEKEfZZevoVRK/Q43GfzRXkWV6qawIWWK8t6ifIiLQdd7rmQ4Vk1bmI21XhqF9BN3jWf+phpA== + dependencies: + "@babel/runtime" "^7.13.10" + "@radix-ui/react-primitive" "1.0.3" + +"@radix-ui/react-presence@1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@radix-ui/react-presence/-/react-presence-1.0.1.tgz#491990ba913b8e2a5db1b06b203cb24b5cdef9ba" + integrity sha512-UXLW4UAbIY5ZjcvzjfRFo5gxva8QirC9hF7wRE4U5gz+TP0DbRk+//qyuAQ1McDxBt1xNMBTaciFGvEmJvAZCg== + dependencies: + "@babel/runtime" "^7.13.10" + "@radix-ui/react-compose-refs" "1.0.1" + "@radix-ui/react-use-layout-effect" "1.0.1" + +"@radix-ui/react-primitive@1.0.3": + version "1.0.3" + resolved "https://registry.yarnpkg.com/@radix-ui/react-primitive/-/react-primitive-1.0.3.tgz#d49ea0f3f0b2fe3ab1cb5667eb03e8b843b914d0" + integrity sha512-yi58uVyoAcK/Nq1inRY56ZSjKypBNKTa/1mcL8qdl6oJeEaDbOldlzrGn7P6Q3Id5d+SYNGc5AJgc4vGhjs5+g== + dependencies: + "@babel/runtime" "^7.13.10" + "@radix-ui/react-slot" "1.0.2" + +"@radix-ui/react-slot@1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@radix-ui/react-slot/-/react-slot-1.0.2.tgz#a9ff4423eade67f501ffb32ec22064bc9d3099ab" + integrity sha512-YeTpuq4deV+6DusvVUW4ivBgnkHwECUu0BiN43L5UCDFgdhsRUWAghhTF5MbvNTPzmiFOx90asDSUjWuCNapwg== + dependencies: + "@babel/runtime" "^7.13.10" + "@radix-ui/react-compose-refs" "1.0.1" + +"@radix-ui/react-tooltip@^1.0.6": + version "1.0.6" + resolved "https://registry.yarnpkg.com/@radix-ui/react-tooltip/-/react-tooltip-1.0.6.tgz#87a7786cd9f2b4de957ac645afae1575339c58b0" + integrity sha512-DmNFOiwEc2UDigsYj6clJENma58OelxD24O4IODoZ+3sQc3Zb+L8w1EP+y9laTuKCLAysPw4fD6/v0j4KNV8rg== + dependencies: + "@babel/runtime" "^7.13.10" + "@radix-ui/primitive" "1.0.1" + "@radix-ui/react-compose-refs" "1.0.1" + "@radix-ui/react-context" "1.0.1" + "@radix-ui/react-dismissable-layer" "1.0.4" + "@radix-ui/react-id" "1.0.1" + "@radix-ui/react-popper" "1.1.2" + "@radix-ui/react-portal" "1.0.3" + "@radix-ui/react-presence" "1.0.1" + "@radix-ui/react-primitive" "1.0.3" + "@radix-ui/react-slot" "1.0.2" + "@radix-ui/react-use-controllable-state" "1.0.1" + "@radix-ui/react-visually-hidden" "1.0.3" + +"@radix-ui/react-use-callback-ref@1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.0.1.tgz#f4bb1f27f2023c984e6534317ebc411fc181107a" + integrity sha512-D94LjX4Sp0xJFVaoQOd3OO9k7tpBYNOXdVhkltUbGv2Qb9OXdrg/CpsjlZv7ia14Sylv398LswWBVVu5nqKzAQ== + dependencies: + "@babel/runtime" "^7.13.10" + +"@radix-ui/react-use-controllable-state@1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.0.1.tgz#ecd2ced34e6330caf89a82854aa2f77e07440286" + integrity sha512-Svl5GY5FQeN758fWKrjM6Qb7asvXeiZltlT4U2gVfl8Gx5UAv2sMR0LWo8yhsIZh2oQ0eFdZ59aoOOMV7b47VA== + dependencies: + "@babel/runtime" "^7.13.10" + "@radix-ui/react-use-callback-ref" "1.0.1" + +"@radix-ui/react-use-escape-keydown@1.0.3": + version "1.0.3" + resolved "https://registry.yarnpkg.com/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.0.3.tgz#217b840c250541609c66f67ed7bab2b733620755" + integrity sha512-vyL82j40hcFicA+M4Ex7hVkB9vHgSse1ZWomAqV2Je3RleKGO5iM8KMOEtfoSB0PnIelMd2lATjTGMYqN5ylTg== + dependencies: + "@babel/runtime" "^7.13.10" + "@radix-ui/react-use-callback-ref" "1.0.1" + +"@radix-ui/react-use-layout-effect@1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.0.1.tgz#be8c7bc809b0c8934acf6657b577daf948a75399" + integrity sha512-v/5RegiJWYdoCvMnITBkNNx6bCj20fiaJnWtRkU18yITptraXjffz5Qbn05uOiQnOvi+dbkznkoaMltz1GnszQ== + dependencies: + "@babel/runtime" "^7.13.10" + +"@radix-ui/react-use-rect@1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@radix-ui/react-use-rect/-/react-use-rect-1.0.1.tgz#fde50b3bb9fd08f4a1cd204572e5943c244fcec2" + integrity sha512-Cq5DLuSiuYVKNU8orzJMbl15TXilTnJKUCltMVQg53BQOF1/C5toAaGrowkgksdBQ9H+SRL23g0HDmg9tvmxXw== + dependencies: + "@babel/runtime" "^7.13.10" + "@radix-ui/rect" "1.0.1" + +"@radix-ui/react-use-size@1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@radix-ui/react-use-size/-/react-use-size-1.0.1.tgz#1c5f5fea940a7d7ade77694bb98116fb49f870b2" + integrity sha512-ibay+VqrgcaI6veAojjofPATwledXiSmX+C0KrBk/xgpX9rBzPV3OsfwlhQdUOFbh+LKQorLYT+xTXW9V8yd0g== + dependencies: + "@babel/runtime" "^7.13.10" + "@radix-ui/react-use-layout-effect" "1.0.1" + +"@radix-ui/react-visually-hidden@1.0.3": + version "1.0.3" + resolved "https://registry.yarnpkg.com/@radix-ui/react-visually-hidden/-/react-visually-hidden-1.0.3.tgz#51aed9dd0fe5abcad7dee2a234ad36106a6984ac" + integrity sha512-D4w41yN5YRKtu464TLnByKzMDG/JlMPHtfZgQAu9v6mNakUqGUI9vUrfQKz8NK41VMm/xbZbh76NUTVtIYqOMA== + dependencies: + "@babel/runtime" "^7.13.10" + "@radix-ui/react-primitive" "1.0.3" + +"@radix-ui/rect@1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@radix-ui/rect/-/rect-1.0.1.tgz#bf8e7d947671996da2e30f4904ece343bc4a883f" + integrity sha512-fyrgCaedtvMg9NK3en0pnOYJdtfwxUcNolezkNPUsoX57X8oQk+NkqcvzHXD2uKNij6GXmWU9NDru2IWjrO4BQ== + dependencies: + "@babel/runtime" "^7.13.10" + "@react-aria/button@^3.3.4": version "3.5.1" resolved "https://registry.yarnpkg.com/@react-aria/button/-/button-3.5.1.tgz#8084a50d4f7daa34dfd7b6d41b90f40dcf15e15e" @@ -4376,6 +4590,17 @@ dependencies: svg2vectordrawable "^2.9.1" +"@vector-im/compound-web@^0.2.15": + version "0.2.15" + resolved "https://registry.yarnpkg.com/@vector-im/compound-web/-/compound-web-0.2.15.tgz#89121053cae95cc4fa82b84fc4ebf87cdf75c564" + integrity sha512-F4ay88XJH9LkGrHcpHyJnqqkWOdYmzwLbTQjbjokKwOPeDvcLJDZkTyRR03GbU8Ir/tlGYSx0TbrXA5XQI0g3A== + dependencies: + "@radix-ui/react-form" "^0.0.3" + "@radix-ui/react-tooltip" "^1.0.6" + classnames "^2.3.2" + graphemer "^1.4.0" + rimraf "^3.0.1" + "@vitejs/plugin-basic-ssl@^1.0.1": version "1.0.1" resolved "https://registry.yarnpkg.com/@vitejs/plugin-basic-ssl/-/plugin-basic-ssl-1.0.1.tgz#48c46eab21e0730921986ce742563ae83fe7fe34" @@ -5955,6 +6180,11 @@ classnames@^2.3.1: resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.3.1.tgz#dfcfa3891e306ec1dad105d0e88f4417b8535e8e" integrity sha512-OlQdbZ7gLfGarSqxesMesDa5uz7KFbID8Kpq/SxIoNGDqY8lSYs0D+hhtBXhcdB3rcbXArFr7vlHheLk1voeNA== +classnames@^2.3.2: + version "2.3.2" + resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.3.2.tgz#351d813bf0137fcc6a76a16b88208d2560a0d924" + integrity sha512-CSbhY4cFEJRe6/GQzIk5qXZ4Jeg5pcsP7b5peFSDpffpe1cqjASH/n9UTjBwOp6XpMSTwQ8Za2K5V02ueA7Tmw== + clean-css@^4.2.3: version "4.2.4" resolved "https://registry.yarnpkg.com/clean-css/-/clean-css-4.2.4.tgz#733bf46eba4e607c6891ea57c24a989356831178" @@ -13188,6 +13418,11 @@ regenerator-runtime@^0.13.7: resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz#8925742a98ffd90814988d7566ad30ca3b263b52" integrity sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA== +regenerator-runtime@^0.14.0: + version "0.14.0" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.14.0.tgz#5e19d68eb12d486f797e15a3c6a918f7cec5eb45" + integrity sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA== + regenerator-transform@^0.15.0: version "0.15.0" resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.15.0.tgz#cbd9ead5d77fae1a48d957cf889ad0586adb6537" @@ -13452,7 +13687,7 @@ rimraf@^2.5.4, rimraf@^2.6.3: dependencies: glob "^7.1.3" -rimraf@^3.0.2: +rimraf@^3.0.1, rimraf@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== From 1e892b452e7dfce4d33c660bf54195592d0c1922 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 1 Sep 2023 13:23:17 +0000 Subject: [PATCH 02/28] Update dependency @react-stately/collections to v3.10.0 --- yarn.lock | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/yarn.lock b/yarn.lock index ccf3bfe0..70874d88 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2815,12 +2815,12 @@ "@react-spring/types" "~9.7.3" "@react-stately/collections@^3.3.4", "@react-stately/collections@^3.4.1": - version "3.4.1" - resolved "https://registry.yarnpkg.com/@react-stately/collections/-/collections-3.4.1.tgz#12fb2244243d3b6deec6b5e4f59b8979f745efda" - integrity sha512-CAYFGmzuc/u6qqJru57gXt50JqUz9maLvGO2YMl4ZaQ1kV2/slDVlMD30GIiBqbuYHvVUqXXJdekjPvl2dOBeQ== + version "3.10.0" + resolved "https://registry.yarnpkg.com/@react-stately/collections/-/collections-3.10.0.tgz#5c772e5eae8d21ae8d1c023fb9b9ae6fa35b1092" + integrity sha512-PyJEFmt9X0kDMF7D4StGnTdXX1hgyUcTXvvXU2fEw6OyXLtmfWFHmFARRtYbuelGKk6clmJojYmIEds0k8jdww== dependencies: - "@babel/runtime" "^7.6.2" - "@react-types/shared" "^3.13.1" + "@react-types/shared" "^3.19.0" + "@swc/helpers" "^0.5.0" "@react-stately/list@^3.5.1": version "3.5.1" @@ -2988,6 +2988,11 @@ resolved "https://registry.yarnpkg.com/@react-types/shared/-/shared-3.13.1.tgz#eda5e3744971606f753baf7879136bf8e3f707ab" integrity sha512-EHQqILDJeDvnloy5VV9lnnEjpCMwH1ghptCfa/lz9Ht9nwco3qGCvUABkWyND7yU1Adt3A/1oJxhpRUu3eTlyg== +"@react-types/shared@^3.19.0": + version "3.19.0" + resolved "https://registry.yarnpkg.com/@react-types/shared/-/shared-3.19.0.tgz#060e547d6e8c3ec84043d62f61cada1a00df1348" + integrity sha512-h852l8bWhqUxbXIG8vH3ab7gE19nnP3U1kuWf6SNSMvgmqjiRN9jXKPIFxF/PbfdvnXXm0yZSgSMWfUCARF0Cg== + "@react-types/tabs@^3.1.1": version "3.1.1" resolved "https://registry.yarnpkg.com/@react-types/tabs/-/tabs-3.1.1.tgz#98c891e13d4a7e4fa3cec8dc8fd4efd3e4f39707" @@ -3991,6 +3996,13 @@ "@svgr/hast-util-to-babel-ast" "^7.0.0" svg-parser "^2.0.4" +"@swc/helpers@^0.5.0": + version "0.5.1" + resolved "https://registry.yarnpkg.com/@swc/helpers/-/helpers-0.5.1.tgz#e9031491aa3f26bfcc974a67f48bd456c8a5357a" + integrity sha512-sJ902EfIzn1Fa+qYmjdQqh8tPsoxyBz+8yBKC2HKUxyezKJFwPGOn7pv4WY6QuQW//ySQi5lJjA/ZT9sNWWNTg== + dependencies: + tslib "^2.4.0" + "@testing-library/dom@^8.5.0": version "8.19.0" resolved "https://registry.yarnpkg.com/@testing-library/dom/-/dom-8.19.0.tgz#bd3f83c217ebac16694329e413d9ad5fdcfd785f" @@ -15064,7 +15076,7 @@ tslib@^1.9.3: resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== -tslib@^2.0.0, tslib@^2.0.1, tslib@^2.0.3, tslib@^2.1.0, tslib@^2.3.0: +tslib@^2.0.0, tslib@^2.0.1, tslib@^2.0.3, tslib@^2.1.0, tslib@^2.3.0, tslib@^2.4.0: version "2.6.2" resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.2.tgz#703ac29425e7b37cd6fd456e92404d46d1f3e4ae" integrity sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q== From 0bf96472eb5c5eda18aceb2570e0c31d9c101ed4 Mon Sep 17 00:00:00 2001 From: Robin Townsend Date: Fri, 1 Sep 2023 13:23:24 -0400 Subject: [PATCH 03/28] Work around jsdom not providing TextEncoder --- package.json | 2 +- test/environment.ts | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) create mode 100644 test/environment.ts diff --git a/package.json b/package.json index 33123334..e86faef7 100644 --- a/package.json +++ b/package.json @@ -122,7 +122,7 @@ "vite-plugin-svgr": "^3.2.0" }, "jest": { - "testEnvironment": "jsdom", + "testEnvironment": "./test/environment.ts", "testMatch": [ "/test/**/*-test.[jt]s?(x)" ], diff --git a/test/environment.ts b/test/environment.ts new file mode 100644 index 00000000..5c29e1ea --- /dev/null +++ b/test/environment.ts @@ -0,0 +1,18 @@ +import { TextEncoder } from "util"; +import JSDOMEnvironment_, { + TestEnvironment as TestEnvironment_, +} from "jest-environment-jsdom"; +import { JestEnvironmentConfig, EnvironmentContext } from "@jest/environment"; + +// This is a patched version of jsdom that adds TextEncoder, as a workaround for +// https://github.com/jsdom/jsdom/issues/2524 +// Once that issue is resolved, this custom environment file can be deleted +export default class JSDOMEnvironment extends JSDOMEnvironment_ { + constructor(config: JestEnvironmentConfig, context: EnvironmentContext) { + super(config, context); + this.global.TextEncoder ??= TextEncoder; + } +} + +export const TestEnvironment = + TestEnvironment_ === JSDOMEnvironment_ ? JSDOMEnvironment : TestEnvironment_; From 1bdb0db6d2b1a277e0f446e094af026621ca707f Mon Sep 17 00:00:00 2001 From: Robin Townsend Date: Fri, 1 Sep 2023 15:08:01 -0400 Subject: [PATCH 04/28] Tell the CI that the livekit branch isn't a PR This prevents it from trying to run the PR preview workflow on every build of the livekit branch. --- .github/workflows/netlify-pr.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/netlify-pr.yaml b/.github/workflows/netlify-pr.yaml index f1d4dced..616c290b 100644 --- a/.github/workflows/netlify-pr.yaml +++ b/.github/workflows/netlify-pr.yaml @@ -6,6 +6,7 @@ on: - completed branches-ignore: - "main" + - "livekit" jobs: deploy: if: github.event.workflow_run.conclusion == 'success' && github.event.workflow_run.event == 'pull_request' From 40283ab7609700d0293db880210f8f436e29507a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Thu, 31 Aug 2023 15:24:25 +0200 Subject: [PATCH 05/28] Remove unused `Facepile` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/Facepile.module.css | 42 ---------------- src/Facepile.tsx | 97 ------------------------------------- src/home/CallList.tsx | 22 +-------- src/home/RegisteredView.tsx | 2 +- 4 files changed, 3 insertions(+), 160 deletions(-) delete mode 100644 src/Facepile.module.css delete mode 100644 src/Facepile.tsx diff --git a/src/Facepile.module.css b/src/Facepile.module.css deleted file mode 100644 index 0c911658..00000000 --- a/src/Facepile.module.css +++ /dev/null @@ -1,42 +0,0 @@ -/* -Copyright 2022 New Vector Ltd - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - -http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -.facepile { - width: 100%; - position: relative; -} - -.facepile.xs { - height: 24px; -} - -.facepile.sm { - height: 32px; -} - -.facepile.md { - height: 36px; -} - -.facepile .avatar { - position: absolute; - top: 0; - border: 1px solid var(--cpd-color-bg-canvas-default); -} - -.facepile.md .avatar { - border-width: 2px; -} diff --git a/src/Facepile.tsx b/src/Facepile.tsx deleted file mode 100644 index 0c9ec239..00000000 --- a/src/Facepile.tsx +++ /dev/null @@ -1,97 +0,0 @@ -/* -Copyright 2022 New Vector Ltd - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -import { HTMLAttributes, useMemo } from "react"; -import classNames from "classnames"; -import { MatrixClient } from "matrix-js-sdk/src/client"; -import { RoomMember } from "matrix-js-sdk/src/models/room-member"; -import { useTranslation } from "react-i18next"; - -import styles from "./Facepile.module.css"; -import { Avatar, Size, sizes } from "./Avatar"; - -const overlapMap: Partial> = { - [Size.XS]: 2, - [Size.SM]: 4, - [Size.MD]: 8, -}; - -interface Props extends HTMLAttributes { - className: string; - client: MatrixClient; - members: RoomMember[]; - max?: number; - size?: Size; -} - -export function Facepile({ - className, - client, - members, - max = 3, - size = Size.XS, - ...rest -}: Props) { - const { t } = useTranslation(); - - const _size = sizes.get(size)!; - const _overlap = overlapMap[size]!; - - const title = useMemo(() => { - return members.reduce( - (prev, curr) => - prev === null - ? curr.name - : t("{{names}}, {{name}}", { names: prev, name: curr.name }), - null - ) as string; - }, [members, t]); - - return ( -
- {members.slice(0, max).map((member, i) => { - const avatarUrl = member.getMxcAvatarUrl(); - return ( - - ); - })} - {members.length > max && ( - - )} -
- ); -} diff --git a/src/home/CallList.tsx b/src/home/CallList.tsx index 1e36360e..01b84369 100644 --- a/src/home/CallList.tsx +++ b/src/home/CallList.tsx @@ -19,7 +19,6 @@ import { MatrixClient } from "matrix-js-sdk/src/client"; import { RoomMember } from "matrix-js-sdk/src/models/room-member"; import { CopyButton } from "../button"; -import { Facepile } from "../Facepile"; import { Avatar, Size } from "../Avatar"; import styles from "./CallList.module.css"; import { getRoomUrl } from "../matrix-utils"; @@ -30,9 +29,8 @@ import { useRoomSharedKey } from "../e2ee/sharedKeyManagement"; interface CallListProps { rooms: GroupCallRoom[]; client: MatrixClient; - disableFacepile?: boolean; } -export function CallList({ rooms, client, disableFacepile }: CallListProps) { +export function CallList({ rooms, client }: CallListProps) { return ( <>
@@ -44,7 +42,6 @@ export function CallList({ rooms, client, disableFacepile }: CallListProps) { avatarUrl={avatarUrl} roomId={room.roomId} participants={participants} - disableFacepile={disableFacepile} /> ))} {rooms.length > 3 && ( @@ -63,16 +60,8 @@ interface CallTileProps { roomId: string; participants: RoomMember[]; client: MatrixClient; - disableFacepile?: boolean; } -function CallTile({ - name, - avatarUrl, - roomId, - participants, - client, - disableFacepile, -}: CallTileProps) { +function CallTile({ name, avatarUrl, roomId }: CallTileProps) { const roomSharedKey = useRoomSharedKey(roomId); return ( @@ -89,13 +78,6 @@ function CallTile({ {name} - {participants && !disableFacepile && ( - - )}
diff --git a/src/home/RegisteredView.tsx b/src/home/RegisteredView.tsx index 42aba9e8..0f8cc93c 100644 --- a/src/home/RegisteredView.tsx +++ b/src/home/RegisteredView.tsx @@ -170,7 +170,7 @@ export function RegisteredView({ client, isPasswordlessUser }: Props) { {t("Your recent calls")} - + )} From 973d3962495ccd1776921d73e9e4eb97073929cd Mon Sep 17 00:00:00 2001 From: David Baker Date: Tue, 5 Sep 2023 17:34:55 +0100 Subject: [PATCH 06/28] Remove extra device request on the video preview page As per comment, livekit mutates the object that's passed in, so we ended up re-requesting the devices in the next render because we effectively passed in different options. --- src/main.tsx | 6 +++--- src/room/VideoPreview.tsx | 7 ++++++- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/main.tsx b/src/main.tsx index d04a3a94..efc6e8d7 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -57,7 +57,7 @@ Initializer.initBeforeReact(); const history = createBrowserHistory(); root.render( - - - + // + + // ); diff --git a/src/room/VideoPreview.tsx b/src/room/VideoPreview.tsx index 1b5c07ce..044a3221 100644 --- a/src/room/VideoPreview.tsx +++ b/src/room/VideoPreview.tsx @@ -68,6 +68,9 @@ export const VideoPreview: FC = ({ matrixInfo, muteStates }) => { const devices = useMediaDevices(); + // Capture the audio options as they were when we first mounted, because + // we're not doing anything with the audio anyway so we don't need to + // re-open the devices when they change (see below). const initialAudioOptions = useRef(); initialAudioOptions.current ??= muteStates.audio.enabled && { deviceId: devices.audioInput.selectedId, @@ -79,7 +82,9 @@ export const VideoPreview: FC = ({ matrixInfo, muteStates }) => { // request over with at the same time. But changing the audio settings // shouldn't cause this hook to recreate the track, which is why we // reference the initial values here. - audio: initialAudioOptions.current, + // We also pass in a clone because livekit mutates the object passed in, + // which would cause the devices to be re-opened on the next render. + audio: Object.assign({}, initialAudioOptions.current), video: muteStates.video.enabled && { deviceId: devices.videoInput.selectedId, }, From 3e726043914365d9999d165837a0f4f031057677 Mon Sep 17 00:00:00 2001 From: David Baker Date: Tue, 5 Sep 2023 17:38:43 +0100 Subject: [PATCH 07/28] Remove unintentional change --- src/main.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main.tsx b/src/main.tsx index efc6e8d7..d04a3a94 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -57,7 +57,7 @@ Initializer.initBeforeReact(); const history = createBrowserHistory(); root.render( - // - - // + + + ); From 1cb0ad2f65306430e73a5b6da4b8c97a5d6189f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Thu, 31 Aug 2023 15:46:09 +0200 Subject: [PATCH 08/28] Switch to `Avatar` from Compound MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/Avatar.module.css | 76 ------------------------ src/Avatar.tsx | 89 +++++++---------------------- src/UserMenu.module.css | 14 ----- src/UserMenu.tsx | 6 +- src/UserMenuContainer.tsx | 1 + src/home/CallList.tsx | 8 +-- src/input/AvatarInputField.tsx | 15 ++++- src/room/GroupCallView.tsx | 3 +- src/room/VideoPreview.tsx | 4 +- src/settings/ProfileSettingsTab.tsx | 6 +- src/video-grid/VideoTile.tsx | 5 +- 11 files changed, 52 insertions(+), 175 deletions(-) delete mode 100644 src/Avatar.module.css diff --git a/src/Avatar.module.css b/src/Avatar.module.css deleted file mode 100644 index accff6ae..00000000 --- a/src/Avatar.module.css +++ /dev/null @@ -1,76 +0,0 @@ -/* -Copyright 2022 New Vector Ltd - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - -http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -.avatar { - position: relative; - color: var(--stopgap-color-on-solid-accent); - display: flex; - align-items: center; - justify-content: center; - pointer-events: none; - font-weight: 600; - overflow: hidden; - flex-shrink: 0; -} - -.avatar img { - width: 100%; - height: 100%; - object-fit: cover; -} - -.avatar svg * { - fill: var(--cpd-color-text-primary); -} - -.avatar span { - padding-top: 1px; -} - -.xs { - width: 22px; - height: 22px; - border-radius: 22px; - font-size: 14px; -} - -.sm { - width: 32px; - height: 32px; - border-radius: 32px; - font-size: 15px; -} - -.md { - width: 36px; - height: 36px; - border-radius: 36px; - font-size: 20px; -} - -.lg { - width: 42px; - height: 42px; - border-radius: 42px; - font-size: 24px; -} - -.xl { - width: 90px; - height: 90px; - border-radius: 90px; - font-size: 48px; -} diff --git a/src/Avatar.tsx b/src/Avatar.tsx index c11d71d4..e23bd909 100644 --- a/src/Avatar.tsx +++ b/src/Avatar.tsx @@ -14,23 +14,11 @@ See the License for the specific language governing permissions and limitations under the License. */ -import { useMemo, CSSProperties, HTMLAttributes, FC } from "react"; -import classNames from "classnames"; +import { useMemo, FC } from "react"; +import { Avatar as CompoundAvatar } from "@vector-im/compound-web"; import { getAvatarUrl } from "./matrix-utils"; import { useClient } from "./ClientContext"; -import styles from "./Avatar.module.css"; - -const backgroundColors = [ - "#5C56F5", - "#03B381", - "#368BD6", - "#AC3BA8", - "#E64F7A", - "#FF812D", - "#2DC2C5", - "#74D12C", -]; export enum Size { XS = "xs", @@ -48,50 +36,28 @@ export const sizes = new Map([ [Size.XL, 90], ]); -function hashStringToArrIndex(str: string, arrLength: number) { - let sum = 0; - - for (let i = 0; i < str.length; i++) { - sum += str.charCodeAt(i); - } - - return sum % arrLength; -} - -interface Props extends HTMLAttributes { - bgKey?: string; +interface Props { + id: string; + name: string; + className?: string; src?: string; size?: Size | number; - className?: string; - style?: CSSProperties; - fallback: string; } export const Avatar: FC = ({ - bgKey, - src, - fallback, - size = Size.MD, className, - style = {}, - ...rest + id, + name, + src, + size = Size.MD, }) => { const { client } = useClient(); - const [sizeClass, sizePx, sizeStyle] = useMemo( + const sizePx = useMemo( () => Object.values(Size).includes(size as Size) - ? [styles[size as string], sizes.get(size as Size), {}] - : [ - null, - size as number, - { - width: size, - height: size, - borderRadius: size, - fontSize: Math.round((size as number) / 2), - }, - ], + ? sizes.get(size as Size) + : (size as number), [size] ); @@ -100,28 +66,13 @@ export const Avatar: FC = ({ return src.startsWith("mxc://") ? getAvatarUrl(client, src, sizePx) : src; }, [client, src, sizePx]); - const backgroundColor = useMemo(() => { - const index = hashStringToArrIndex( - bgKey || fallback || src || "", - backgroundColors.length - ); - return backgroundColors[index]; - }, [bgKey, src, fallback]); - - /* eslint-disable jsx-a11y/alt-text */ return ( -
- {resolvedSrc ? ( - - ) : typeof fallback === "string" ? ( - {fallback} - ) : ( - fallback - )} -
+ ); }; diff --git a/src/UserMenu.module.css b/src/UserMenu.module.css index d1db1071..575b71b9 100644 --- a/src/UserMenu.module.css +++ b/src/UserMenu.module.css @@ -24,17 +24,3 @@ limitations under the License. .userButton svg * { fill: var(--cpd-color-icon-primary); } - -.avatar { - width: 24px; - height: 24px; - font-size: var(--font-size-caption); -} - -@media (min-width: 800px) { - .avatar { - width: 32px; - height: 32px; - font-size: var(--font-size-body); - } -} diff --git a/src/UserMenu.tsx b/src/UserMenu.tsx index 9df3309d..515e71f0 100644 --- a/src/UserMenu.tsx +++ b/src/UserMenu.tsx @@ -35,6 +35,7 @@ interface UserMenuProps { preventNavigation: boolean; isAuthenticated: boolean; isPasswordlessUser: boolean; + userId: string; displayName: string; avatarUrl?: string; onAction: (value: string) => void; @@ -44,6 +45,7 @@ export function UserMenu({ preventNavigation, isAuthenticated, isPasswordlessUser, + userId, displayName, avatarUrl, onAction, @@ -109,10 +111,10 @@ export function UserMenu({ > {isAuthenticated && (!isPasswordlessUser || avatarUrl) ? ( ) : ( diff --git a/src/UserMenuContainer.tsx b/src/UserMenuContainer.tsx index 6a83133e..a03e5b5a 100644 --- a/src/UserMenuContainer.tsx +++ b/src/UserMenuContainer.tsx @@ -67,6 +67,7 @@ export function UserMenuContainer({ preventNavigation = false }: Props) { isPasswordlessUser={passwordlessUser} avatarUrl={avatarUrl} onAction={onAction} + userId={client?.getUserId() ?? ""} displayName={displayName || (userName ? userName.replace("@", "") : "")} /> {modalState.isOpen && client && ( diff --git a/src/home/CallList.tsx b/src/home/CallList.tsx index 01b84369..a82e4c13 100644 --- a/src/home/CallList.tsx +++ b/src/home/CallList.tsx @@ -67,13 +67,7 @@ function CallTile({ name, avatarUrl, roomId }: CallTileProps) { return (
- +
{name} diff --git a/src/input/AvatarInputField.tsx b/src/input/AvatarInputField.tsx index 0218258e..8a069718 100644 --- a/src/input/AvatarInputField.tsx +++ b/src/input/AvatarInputField.tsx @@ -35,13 +35,23 @@ interface Props extends AllHTMLAttributes { id: string; label: string; avatarUrl: string | undefined; + userId: string; displayName: string; onRemoveAvatar: () => void; } export const AvatarInputField = forwardRef( ( - { id, label, className, avatarUrl, displayName, onRemoveAvatar, ...rest }, + { + id, + label, + className, + avatarUrl, + userId, + displayName, + onRemoveAvatar, + ...rest + }, ref ) => { const { t } = useTranslation(); @@ -80,9 +90,10 @@ export const AvatarInputField = forwardRef(
{ return { + userId: client.getUserId()!, displayName: displayName!, avatarUrl: avatarUrl!, roomId: groupCall.room.roomId, roomName: groupCall.room.name, roomAlias: groupCall.room.getCanonicalAlias(), }; - }, [displayName, avatarUrl, groupCall]); + }, [client, displayName, avatarUrl, groupCall]); const deviceContext = useMediaDevices(); const latestDevices = useRef(); diff --git a/src/room/VideoPreview.tsx b/src/room/VideoPreview.tsx index 1b5c07ce..807d402f 100644 --- a/src/room/VideoPreview.tsx +++ b/src/room/VideoPreview.tsx @@ -35,6 +35,7 @@ import { useMediaDevices } from "../livekit/MediaDevicesContext"; import { MuteStates } from "./MuteStates"; export type MatrixInfo = { + userId: string; displayName: string; avatarUrl: string; roomId: string; @@ -124,9 +125,10 @@ export const VideoPreview: FC = ({ matrixInfo, muteStates }) => { {!muteStates.video.enabled && (
)} diff --git a/src/settings/ProfileSettingsTab.tsx b/src/settings/ProfileSettingsTab.tsx index e6a59634..4286a960 100644 --- a/src/settings/ProfileSettingsTab.tsx +++ b/src/settings/ProfileSettingsTab.tsx @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -import { useCallback, useEffect, useRef } from "react"; +import { useCallback, useEffect, useMemo, useRef } from "react"; import { MatrixClient } from "matrix-js-sdk/src/client"; import { useTranslation } from "react-i18next"; @@ -29,6 +29,7 @@ interface Props { export function ProfileSettingsTab({ client }: Props) { const { t } = useTranslation(); const { error, displayName, avatarUrl, saveProfile } = useProfile(client); + const userId = useMemo(() => client.getUserId(), [client]); const formRef = useRef(null); @@ -77,12 +78,13 @@ export function ProfileSettingsTab({ client }: Props) { return (
- {displayName && ( + {userId && displayName && ( diff --git a/src/video-grid/VideoTile.tsx b/src/video-grid/VideoTile.tsx index f15052e0..3433f8aa 100644 --- a/src/video-grid/VideoTile.tsx +++ b/src/video-grid/VideoTile.tsx @@ -145,6 +145,8 @@ export const VideoTile = forwardRef( // Firefox doesn't respect the disablePictureInPicture attribute // https://bugzilla.mozilla.org/show_bug.cgi?id=1611831 + console.log(`LOG VideoTIle mxcSrc=${member?.getMxcAvatarUrl()}`); + return ( (
From 40e31607d21d5aab15556662fad0daaeb6cf9f45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Mon, 4 Sep 2023 20:04:24 +0200 Subject: [PATCH 09/28] i18n 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 | 1 - 1 file changed, 1 deletion(-) diff --git a/public/locales/en-GB/app.json b/public/locales/en-GB/app.json index f2605959..9864bcf4 100644 --- a/public/locales/en-GB/app.json +++ b/public/locales/en-GB/app.json @@ -3,7 +3,6 @@ "{{count}} stars|other": "{{count}} stars", "{{displayName}} is presenting": "{{displayName}} is presenting", "{{displayName}}, your call has ended.": "{{displayName}}, your call has ended.", - "{{names}}, {{name}}": "{{names}}, {{name}}", "<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", From 8f570b7893179988566501aaa2ce8cd3e472a4b7 Mon Sep 17 00:00:00 2001 From: David Baker Date: Wed, 6 Sep 2023 09:12:04 +0100 Subject: [PATCH 10/28] Include the colon in the user ID in the numebr of parts we check for --- src/room/InCallView.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/room/InCallView.tsx b/src/room/InCallView.tsx index c7e17036..68e4e80a 100644 --- a/src/room/InCallView.tsx +++ b/src/room/InCallView.tsx @@ -456,7 +456,8 @@ function findMatrixMember( if (!id) return undefined; const parts = id.split(":"); - if (parts.length < 2) { + // must be at least 3 parts because we know the first part is a userId which must necessarily contain a colon + if (parts.length < 3) { logger.warn( "Livekit participants ID doesn't look like a userId:deviceId combination" ); From ef037c5c1db48d699ab42411373189cd488c4fe3 Mon Sep 17 00:00:00 2001 From: David Baker Date: Wed, 6 Sep 2023 10:22:02 +0100 Subject: [PATCH 11/28] Add apple site association --- public/apple-app-site-association.json | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 public/apple-app-site-association.json diff --git a/public/apple-app-site-association.json b/public/apple-app-site-association.json new file mode 100644 index 00000000..eff625a4 --- /dev/null +++ b/public/apple-app-site-association.json @@ -0,0 +1,17 @@ +{ + "applinks": { + "apps": [], + "details": [ + { + "appIDs":[ + "7J4U792NQT.io.element.elementx", + "7J4U792NQT.io.element.elementx.nightly", + "7J4U792NQT.io.element.elementx.pr" + ], + "paths": [ + "*" + ] + } + ] + } +} From 5d969563f739062b5b96fe43561c6b475a22d162 Mon Sep 17 00:00:00 2001 From: David Baker Date: Wed, 6 Sep 2023 10:35:41 +0100 Subject: [PATCH 12/28] Prettier --- public/apple-app-site-association.json | 28 ++++++++++++-------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/public/apple-app-site-association.json b/public/apple-app-site-association.json index eff625a4..b27b5c71 100644 --- a/public/apple-app-site-association.json +++ b/public/apple-app-site-association.json @@ -1,17 +1,15 @@ { - "applinks": { - "apps": [], - "details": [ - { - "appIDs":[ - "7J4U792NQT.io.element.elementx", - "7J4U792NQT.io.element.elementx.nightly", - "7J4U792NQT.io.element.elementx.pr" - ], - "paths": [ - "*" - ] - } - ] - } + "applinks": { + "apps": [], + "details": [ + { + "appIDs": [ + "7J4U792NQT.io.element.elementx", + "7J4U792NQT.io.element.elementx.nightly", + "7J4U792NQT.io.element.elementx.pr" + ], + "paths": ["*"] + } + ] + } } From 00f14c98143c0511c85a8b4a7d87f28fca970c43 Mon Sep 17 00:00:00 2001 From: David Baker Date: Wed, 6 Sep 2023 11:45:01 +0100 Subject: [PATCH 13/28] Fix the mute icon being incorrect when using PTT We were manipulating the participant's mute state directly for some reason, just for setting the mute state directly, which bypased the mutestates hook. --- src/room/InCallView.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/room/InCallView.tsx b/src/room/InCallView.tsx index 1d3701bc..9bb2a4d9 100644 --- a/src/room/InCallView.tsx +++ b/src/room/InCallView.tsx @@ -190,7 +190,8 @@ export function InCallView({ containerRef1, toggleMicrophone, toggleCamera, - async (muted) => await localParticipant.setMicrophoneEnabled(!muted) + (muted) => + muteStates?.audio?.setEnabled && muteStates.audio.setEnabled(!muted) ); const onDisconnected = useCallback( From 1757bc546dc1f328f1de1aa7f731497da30b390c Mon Sep 17 00:00:00 2001 From: David Baker Date: Wed, 6 Sep 2023 13:59:37 +0100 Subject: [PATCH 14/28] Rename to not have file extension As this is what apple need. Also make the docker container serve it with the right content type. --- config/nginx.conf | 4 ++++ ...e-app-site-association.json => apple-app-site-association} | 0 2 files changed, 4 insertions(+) rename public/{apple-app-site-association.json => apple-app-site-association} (100%) diff --git a/config/nginx.conf b/config/nginx.conf index f7253465..5dc69b2f 100644 --- a/config/nginx.conf +++ b/config/nginx.conf @@ -21,5 +21,9 @@ server { expires 1w; add_header Cache-Control "public, no-transform"; } + + location /apple-app-site-association { + default_type application/json; + } } diff --git a/public/apple-app-site-association.json b/public/apple-app-site-association similarity index 100% rename from public/apple-app-site-association.json rename to public/apple-app-site-association From b0e7b949daca13fe79f0de0845b62431feb2ac5f Mon Sep 17 00:00:00 2001 From: David Baker Date: Wed, 6 Sep 2023 16:03:54 +0100 Subject: [PATCH 15/28] Assetlinks for android too --- public/.well-known/assetlinks.json | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 public/.well-known/assetlinks.json diff --git a/public/.well-known/assetlinks.json b/public/.well-known/assetlinks.json new file mode 100644 index 00000000..f49328c2 --- /dev/null +++ b/public/.well-known/assetlinks.json @@ -0,0 +1,12 @@ +[ + { + "relation": ["delegate_permission/common.handle_all_urls"], + "target": { + "namespace": "android_app", + "package_name": "io.element.android.x.debug", + "sha256_cert_fingerprints": [ + "BC:9D:56:30:0C:3C:DB:80:16:EE:0D:3F:40:E6:65:C0:78:CA:B8:99:2E:DD:6E:FB:2B:CA:FB:60:48:FE:BF:58" + ] + } + } +] From da5bef1206c7bd13316d8afddae3bef0195ff4cc Mon Sep 17 00:00:00 2001 From: David Baker Date: Wed, 6 Sep 2023 16:23:49 +0100 Subject: [PATCH 16/28] Wrong finger or something --- public/.well-known/assetlinks.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/.well-known/assetlinks.json b/public/.well-known/assetlinks.json index f49328c2..0eb37bf0 100644 --- a/public/.well-known/assetlinks.json +++ b/public/.well-known/assetlinks.json @@ -5,7 +5,7 @@ "namespace": "android_app", "package_name": "io.element.android.x.debug", "sha256_cert_fingerprints": [ - "BC:9D:56:30:0C:3C:DB:80:16:EE:0D:3F:40:E6:65:C0:78:CA:B8:99:2E:DD:6E:FB:2B:CA:FB:60:48:FE:BF:58" + "B0:B0:51:DC:56:5C:81:2F:E1:7F:6F:3E:94:5B:4D:79:04:71:23:AB:0D:A6:12:86:76:9E:B2:94:91:97:13:0E" ] } } From 38bb2895db59f25517eec9692eed72ae95f52436 Mon Sep 17 00:00:00 2001 From: David Baker Date: Thu, 7 Sep 2023 13:16:40 +0100 Subject: [PATCH 17/28] Tidier syntax Co-authored-by: Robin --- src/room/InCallView.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/room/InCallView.tsx b/src/room/InCallView.tsx index 9bb2a4d9..1eeafece 100644 --- a/src/room/InCallView.tsx +++ b/src/room/InCallView.tsx @@ -190,8 +190,7 @@ export function InCallView({ containerRef1, toggleMicrophone, toggleCamera, - (muted) => - muteStates?.audio?.setEnabled && muteStates.audio.setEnabled(!muted) + (muted) => muteStates.audio.setEnabled?.(!muted) ); const onDisconnected = useCallback( From 3545a8152a2a49d922375dfedfeebd0423d14d2c Mon Sep 17 00:00:00 2001 From: Robin Date: Fri, 8 Sep 2023 15:39:49 -0400 Subject: [PATCH 18/28] Improve accessibility of buttons I noticed that none of these buttons had accessible labels, which is obviously no good since they rely on icons alone to convey purpose when not focused. --- src/button/Button.tsx | 64 +++++++++++++++---------------------------- 1 file changed, 22 insertions(+), 42 deletions(-) diff --git a/src/button/Button.tsx b/src/button/Button.tsx index ac02c6ed..2b8049e5 100644 --- a/src/button/Button.tsx +++ b/src/button/Button.tsx @@ -13,7 +13,7 @@ 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 { forwardRef, useCallback } from "react"; +import { forwardRef } from "react"; import { PressEvent } from "@react-types/shared"; import classNames from "classnames"; import { useButton } from "@react-aria/button"; @@ -27,13 +27,11 @@ import { ReactComponent as VideoCallOffIcon } from "@vector-im/compound-design-t import { ReactComponent as EndCallIcon } from "@vector-im/compound-design-tokens/icons/end-call.svg"; import { ReactComponent as ShareScreenSolidIcon } from "@vector-im/compound-design-tokens/icons/share-screen-solid.svg"; import { ReactComponent as SettingsSolidIcon } from "@vector-im/compound-design-tokens/icons/settings-solid.svg"; -import { ReactComponent as UserAddSolidIcon } from "@vector-im/compound-design-tokens/icons/user-add-solid.svg"; import { ReactComponent as ChevronDownIcon } from "@vector-im/compound-design-tokens/icons/chevron-down.svg"; import styles from "./Button.module.css"; import { ReactComponent as Fullscreen } from "../icons/Fullscreen.svg"; import { ReactComponent as FullscreenExit } from "../icons/FullscreenExit.svg"; -import { TooltipTrigger } from "../Tooltip"; import { VolumeIcon } from "./VolumeIcon"; export type ButtonVariant = @@ -146,11 +144,13 @@ export function MicButton({ [index: string]: unknown; }) { const { t } = useTranslation(); + const Icon = muted ? MicOffSolidIcon : MicOnSolidIcon; + const label = muted ? t("Microphone off") : t("Microphone on"); return ( - + ); @@ -165,11 +165,13 @@ export function VideoButton({ [index: string]: unknown; }) { const { t } = useTranslation(); + const Icon = muted ? VideoCallOffIcon : VideoCallIcon; + const label = muted ? t("Video off") : t("Video on"); return ( - + ); @@ -186,11 +188,12 @@ export function ScreenshareButton({ [index: string]: unknown; }) { const { t } = useTranslation(); + const label = enabled ? t("Sharing screen") : t("Share screen"); return ( - + ); @@ -213,7 +216,7 @@ export function HangupButton({ className={classNames(styles.hangupButton, className)} {...rest} > - + ); @@ -232,28 +235,7 @@ export function SettingsButton({ return ( - - ); -} - -export function InviteButton({ - className, - variant = "toolbar", - ...rest -}: { - className?: string; - variant?: string; - // TODO: add all props for ); @@ -268,14 +250,13 @@ interface AudioButtonProps extends Omit { export function AudioButton({ volume, ...rest }: AudioButtonProps) { const { t } = useTranslation(); - const tooltip = useCallback(() => t("Local volume"), [t]); return ( - + - + ); } @@ -288,15 +269,14 @@ export function FullscreenButton({ ...rest }: FullscreenButtonProps) { const { t } = useTranslation(); - const tooltip = useCallback(() => { - return fullscreen ? t("Exit full screen") : t("Full screen"); - }, [fullscreen, t]); + const Icon = fullscreen ? FullscreenExit : Fullscreen; + const label = fullscreen ? t("Exit full screen") : t("Full screen"); return ( - + - + ); } From 1445c42cc32644a1b573a800418febb81912ed83 Mon Sep 17 00:00:00 2001 From: Robin Date: Fri, 8 Sep 2023 15:39:10 -0400 Subject: [PATCH 19/28] Implement new in-call header and footer designs --- public/locales/en-GB/app.json | 16 +-- src/E2EELock.tsx | 55 --------- src/Facepile.tsx | 66 +++++++++++ src/Header.module.css | 77 ++++++------- src/Header.stories.jsx | 105 ------------------ src/Header.tsx | 92 ++++++++------- src/button/ShareButton.tsx | 31 ++++++ src/button/VolumeIcon.tsx | 14 ++- src/icons/AddUser.svg | 4 - src/icons/AlertTriangleFilled.svg | 3 - src/icons/ArrowLeft.svg | 3 - src/icons/DisableVideo.svg | 6 - src/icons/Freedom.svg | 6 - src/icons/Hangup.svg | 4 - src/icons/LogoMark.svg | 11 ++ src/icons/LogoType.svg | 17 +++ src/icons/Screenshare.svg | 3 - src/icons/Spotlight.svg | 3 - src/icons/VideoMuted.svg | 6 - src/index.css | 4 + .../EncryptionLock.module.css} | 28 +++-- src/room/EncryptionLock.tsx | 46 ++++++++ src/room/GridLayoutMenu.module.css | 0 src/room/GridLayoutMenu.tsx | 82 -------------- src/room/GroupCallView.tsx | 84 ++++++++++---- src/room/InCallView.module.css | 50 +++++---- src/room/InCallView.tsx | 102 ++++++++--------- src/room/LayoutToggle.module.css | 77 +++++++++++++ src/room/LayoutToggle.tsx | 75 +++++++++++++ src/room/LobbyView.tsx | 20 +++- ...Modal.module.css => ShareModal.module.css} | 0 src/room/{InviteModal.tsx => ShareModal.tsx} | 6 +- src/room/VideoPreview.tsx | 2 + src/room/useGroupCall.ts | 17 +-- src/room/useRoomName.ts | 27 +++++ src/useMediaQuery.ts | 37 ++++++ src/usePrefersReducedMotion.ts | 27 +---- src/video-grid/NewVideoGrid.module.css | 10 +- src/video-grid/VideoGrid.tsx | 18 +-- vite.config.js | 8 +- yarn.lock | 6 +- 41 files changed, 700 insertions(+), 548 deletions(-) delete mode 100644 src/E2EELock.tsx create mode 100644 src/Facepile.tsx delete mode 100644 src/Header.stories.jsx create mode 100644 src/button/ShareButton.tsx delete mode 100644 src/icons/AddUser.svg delete mode 100644 src/icons/AlertTriangleFilled.svg delete mode 100644 src/icons/ArrowLeft.svg delete mode 100644 src/icons/DisableVideo.svg delete mode 100644 src/icons/Freedom.svg delete mode 100644 src/icons/Hangup.svg create mode 100644 src/icons/LogoMark.svg create mode 100644 src/icons/LogoType.svg delete mode 100644 src/icons/Screenshare.svg delete mode 100644 src/icons/Spotlight.svg delete mode 100644 src/icons/VideoMuted.svg rename src/{E2EELock.module.css => room/EncryptionLock.module.css} (62%) create mode 100644 src/room/EncryptionLock.tsx delete mode 100644 src/room/GridLayoutMenu.module.css delete mode 100644 src/room/GridLayoutMenu.tsx create mode 100644 src/room/LayoutToggle.module.css create mode 100644 src/room/LayoutToggle.tsx rename src/room/{InviteModal.module.css => ShareModal.module.css} (100%) rename src/room/{InviteModal.tsx => ShareModal.tsx} (90%) create mode 100644 src/room/useRoomName.ts create mode 100644 src/useMediaQuery.ts diff --git a/public/locales/en-GB/app.json b/public/locales/en-GB/app.json index 9864bcf4..69127826 100644 --- a/public/locales/en-GB/app.json +++ b/public/locales/en-GB/app.json @@ -1,8 +1,11 @@ { + "{{count, number}}|one": "{{count, number}}", + "{{count, number}}|other": "{{count, number}}", "{{count}} stars|one": "{{count}} star", "{{count}} stars|other": "{{count}} stars", "{{displayName}} is presenting": "{{displayName}} is presenting", "{{displayName}}, your call has ended.": "{{displayName}}, your call has ended.", + "{{names, list(style: short;)}}": "{{names, list(style: short;)}}", "<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", @@ -21,7 +24,6 @@ "Call link copied": "Call link copied", "Call type menu": "Call type menu", "Camera": "Camera", - "Change layout": "Change layout", "Close": "Close", "Confirm password": "Confirm password", "Connectivity to the server has been lost.": "Connectivity to the server has been lost.", @@ -31,7 +33,6 @@ "Create account": "Create account", "Debug log": "Debug log", "Debug log request": "Debug log request", - "Details": "Details", "Developer": "Developer", "Developer Settings": "Developer Settings", "Display name": "Display name", @@ -39,25 +40,22 @@ "Element Call Home": "Element Call Home", "Element Call is temporarily not end-to-end encrypted while we test scalability.": "Element Call is temporarily not end-to-end encrypted while we test scalability.", "Enable end-to-end encryption (password protected calls)": "Enable end-to-end encryption (password protected calls)", + "Encrypted": "Encrypted", "End call": "End call", "End-to-end encryption isn't supported on your browser.": "End-to-end encryption isn't supported on your browser.", "Exit full screen": "Exit full screen", "Expose developer settings in the settings window.": "Expose developer settings in the settings window.", "Feedback": "Feedback", "Fetching group call timed out.": "Fetching group call timed out.", - "Freedom": "Freedom", "Full screen": "Full screen", "Go": "Go", - "Grid layout menu": "Grid layout menu", + "Grid": "Grid", "Home": "Home", "How did it go?": "How did it go?", "If you are experiencing issues or simply would like to provide some feedback, please send us a short description below.": "If you are experiencing issues or simply would like to provide some feedback, please send us a short description below.", "Include debug logs": "Include debug logs", "Incompatible versions": "Incompatible versions", - "Incompatible versions!": "Incompatible versions!", "Inspector": "Inspector", - "Invite": "Invite", - "Invite people": "Invite people", "Join call": "Join call", "Join call now": "Join call now", "Join existing call?": "Join existing call?", @@ -71,6 +69,7 @@ "Microphone on": "Microphone on", "More": "More", "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": "Not registered yet? <2>Create an account", "Other users are trying to join this call from incompatible versions. These users should ensure that they have refreshed their browsers:<1>{userLis}": "Other users are trying to join this call from incompatible versions. These users should ensure that they have refreshed their browsers:<1>{userLis}", @@ -90,7 +89,9 @@ "Sending debug logs…": "Sending debug logs…", "Sending…": "Sending…", "Settings": "Settings", + "Share": "Share", "Share screen": "Share screen", + "Share this call": "Share this call", "Sharing screen": "Sharing screen", "Show call inspector": "Show call inspector", "Show connection stats": "Show connection stats", @@ -105,7 +106,6 @@ "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?", - "This call is not end-to-end encrypted.": "This call is not end-to-end encrypted.", "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)", "User menu": "User menu", "Username": "Username", diff --git a/src/E2EELock.tsx b/src/E2EELock.tsx deleted file mode 100644 index 9a9a55e9..00000000 --- a/src/E2EELock.tsx +++ /dev/null @@ -1,55 +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 { useTranslation } from "react-i18next"; -import { useCallback } from "react"; -import { useObjectRef } from "@react-aria/utils"; -import { useButton } from "@react-aria/button"; - -import styles from "./E2EELock.module.css"; -import { ReactComponent as LockOffIcon } from "./icons/LockOff.svg"; -import { TooltipTrigger } from "./Tooltip"; - -export const E2EELock = () => { - const { t } = useTranslation(); - const tooltip = useCallback( - () => t("This call is not end-to-end encrypted."), - [t] - ); - - return ( - - - - ); -}; - -/** - * This component is a bit of hack - for some reason for the TooltipTrigger to - * work, it needs to contain a component which uses the useButton hook; please - * note that for some reason this also needs to be a separate component and we - * cannot just use the useButton hook inside the E2EELock. - */ -const Icon = () => { - const buttonRef = useObjectRef(); - const { buttonProps } = useButton({}, buttonRef); - - return ( -
- -
- ); -}; diff --git a/src/Facepile.tsx b/src/Facepile.tsx new file mode 100644 index 00000000..7ed995ce --- /dev/null +++ b/src/Facepile.tsx @@ -0,0 +1,66 @@ +/* +Copyright 2022 New Vector Ltd + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +import { HTMLAttributes } from "react"; +import { MatrixClient } from "matrix-js-sdk/src/client"; +import { RoomMember } from "matrix-js-sdk/src/models/room-member"; +import { useTranslation } from "react-i18next"; +import { AvatarStack } from "@vector-im/compound-web"; + +import { Avatar, Size } from "./Avatar"; + +interface Props extends HTMLAttributes { + className?: string; + client: MatrixClient; + members: RoomMember[]; + max?: number; + size?: Size | number; +} + +export function Facepile({ + className, + client, + members, + max = 3, + size = Size.XS, + ...rest +}: Props) { + const { t } = useTranslation(); + + const displayedMembers = members.slice(0, max); + + return ( + m.name), + })} + {...rest} + > + {displayedMembers.map((member, i) => { + const avatarUrl = member.getMxcAvatarUrl(); + return ( + + ); + })} + + ); +} diff --git a/src/Header.module.css b/src/Header.module.css index e54edff5..84b9df7b 100644 --- a/src/Header.module.css +++ b/src/Header.module.css @@ -28,8 +28,8 @@ limitations under the License. flex: 1; align-items: center; white-space: nowrap; - padding: 0 20px; - height: 64px; + padding-inline: var(--inline-content-inset); + height: 80px; } .headerLogo { @@ -66,51 +66,56 @@ limitations under the License. margin-right: 0; } +.roomHeaderInfo { + display: grid; + column-gap: var(--cpd-space-4x); + grid-template-columns: auto auto; + grid-template-rows: 1fr auto; +} + +.roomHeaderInfo[data-size="sm"] { + grid-template-areas: "avatar name" ". participants"; +} + +.roomHeaderInfo[data-size="lg"] { + grid-template-areas: "avatar name" "avatar participants"; +} + .roomAvatar { - position: relative; - display: none; - justify-content: center; + align-self: flex-start; + grid-area: avatar; +} + +.nameLine { + grid-area: name; + flex-grow: 1; + display: flex; align-items: center; - width: 36px; - height: 36px; - border-radius: 36px; - background-color: #5c56f5; + gap: var(--cpd-space-1x); } -.roomAvatar > * { - fill: white; - stroke: white; -} - -.userName { - font-weight: 600; - margin-right: 8px; - text-overflow: ellipsis; +.nameLine > h1 { + margin: 0; + /* XXX I can't actually get this ellipsis overflow to trigger, because + constraint propagation in a nested flexbox layout is a massive pain */ overflow: hidden; - flex-shrink: 1; + text-overflow: ellipsis; } -.versionMismatchWarning { - padding-left: 15px; +.nameLine > svg { + flex-shrink: 0; } -.versionMismatchWarning::before { - content: ""; - display: inline-block; - position: relative; - top: 1px; - width: 16px; - height: 16px; - mask-image: url("./icons/AlertTriangleFilled.svg"); - mask-repeat: no-repeat; - mask-size: contain; - background-color: var(--cpd-color-icon-critical-primary); - padding-right: 5px; +.participantsLine { + grid-area: participants; + display: flex; + align-items: center; + gap: var(--cpd-space-1-5x); + font: var(--cpd-font-body-sm-medium); } @media (min-width: 800px) { .headerLogo, - .roomAvatar, .leftNav.hideMobile, .rightNav.hideMobile { display: flex; @@ -119,8 +124,4 @@ limitations under the License. .leftNav h3 { font-size: var(--font-size-subtitle); } - - .nav { - height: 76px; - } } diff --git a/src/Header.stories.jsx b/src/Header.stories.jsx deleted file mode 100644 index e5c4473a..00000000 --- a/src/Header.stories.jsx +++ /dev/null @@ -1,105 +0,0 @@ -import { GridLayoutMenu } from "./room/GridLayoutMenu"; -import { - Header, - HeaderLogo, - LeftNav, - RightNav, - RoomHeaderInfo, -} from "./Header"; -import { UserMenu } from "./UserMenu"; - -export default { - title: "Header", - component: Header, - parameters: { - layout: "fullscreen", - }, -}; - -export const HomeAnonymous = () => ( -
- - - - - - -
-); - -export const HomeNamedGuest = () => ( -
- - - - - - -
-); - -export const HomeLoggedIn = () => ( -
- - - - - - -
-); - -export const LobbyNamedGuest = () => ( -
- - - - - - -
-); - -export const LobbyLoggedIn = () => ( -
- - - - - - -
-); - -export const InRoomNamedGuest = () => ( -
- - - - - - - -
-); - -export const InRoomLoggedIn = () => ( -
- - - - - - - -
-); - -export const CreateAccount = () => ( -
- - - - -
-); diff --git a/src/Header.tsx b/src/Header.tsx index 8d754b92..aea3da71 100644 --- a/src/Header.tsx +++ b/src/Header.tsx @@ -15,17 +15,18 @@ limitations under the License. */ import classNames from "classnames"; -import { HTMLAttributes, ReactNode, useCallback } from "react"; +import { FC, HTMLAttributes, ReactNode } from "react"; import { Link } from "react-router-dom"; -import { Room } from "matrix-js-sdk/src/models/room"; import { useTranslation } from "react-i18next"; +import { MatrixClient, RoomMember } from "matrix-js-sdk/src/matrix"; +import { Heading } from "@vector-im/compound-web"; import styles from "./Header.module.css"; -import { useModalTriggerState } from "./Modal"; -import { Button } from "./button"; import { ReactComponent as Logo } from "./icons/Logo.svg"; -import { Subtitle } from "./typography/Typography"; -import { IncompatibleVersionModal } from "./IncompatibleVersionModal"; +import { Avatar, Size } from "./Avatar"; +import { Facepile } from "./Facepile"; +import { EncryptionLock } from "./room/EncryptionLock"; +import { useMediaQuery } from "./useMediaQuery"; interface HeaderProps extends HTMLAttributes { children: ReactNode; @@ -112,47 +113,52 @@ export function HeaderLogo({ className }: HeaderLogoProps) { ); } -interface RoomHeaderInfo { - roomName: string; +interface RoomHeaderInfoProps { + id: string; + name: string; + avatarUrl: string | null; + encrypted: boolean; + participants: RoomMember[]; + client: MatrixClient; } -export function RoomHeaderInfo({ roomName }: RoomHeaderInfo) { - return ( - <> - - {roomName} - - - ); -} - -interface VersionMismatchWarningProps { - users: Set; - room: Room; -} - -export function VersionMismatchWarning({ - users, - room, -}: VersionMismatchWarningProps) { +export const RoomHeaderInfo: FC = ({ + id, + name, + avatarUrl, + encrypted, + participants, + client, +}) => { const { t } = useTranslation(); - const { modalState, modalProps } = useModalTriggerState(); - - const onDetailsClick = useCallback(() => { - modalState.open(); - }, [modalState]); - - if (users.size === 0) return null; + const size = useMediaQuery("(max-width: 550px)") ? "sm" : "lg"; return ( - - {t("Incompatible versions!")} - - {modalState.isOpen && ( - +
+ +
+ + {name} + + +
+ {participants.length > 0 && ( +
+ + {t("{{count, number}}", { count: participants.length })} +
)} - +
); -} +}; diff --git a/src/button/ShareButton.tsx b/src/button/ShareButton.tsx new file mode 100644 index 00000000..2f7f1334 --- /dev/null +++ b/src/button/ShareButton.tsx @@ -0,0 +1,31 @@ +/* +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 { ComponentPropsWithoutRef, FC } from "react"; +import { Button } from "@vector-im/compound-web"; +import { useTranslation } from "react-i18next"; +import { ReactComponent as UserAddSolidIcon } from "@vector-im/compound-design-tokens/icons/user-add-solid.svg"; + +export const ShareButton: FC< + Omit, "children"> +> = (props) => { + const { t } = useTranslation(); + return ( + + ); +}; diff --git a/src/button/VolumeIcon.tsx b/src/button/VolumeIcon.tsx index 163699f6..00aebb06 100644 --- a/src/button/VolumeIcon.tsx +++ b/src/button/VolumeIcon.tsx @@ -15,19 +15,21 @@ See the License for the specific language governing permissions and limitations under the License. */ +import { ComponentPropsWithoutRef, FC } from "react"; + import { ReactComponent as AudioMuted } from "../icons/AudioMuted.svg"; import { ReactComponent as AudioLow } from "../icons/AudioLow.svg"; import { ReactComponent as Audio } from "../icons/Audio.svg"; -interface Props { +interface Props extends ComponentPropsWithoutRef<"svg"> { /** * Number between 0 and 1 */ volume: number; } -export function VolumeIcon({ volume }: Props) { - if (volume <= 0) return ; - if (volume <= 0.5) return ; - return