From 619e3c4852cbd630ab0497d37746a11309896cc7 Mon Sep 17 00:00:00 2001 From: Timo K Date: Thu, 7 Jul 2022 23:40:29 +0200 Subject: [PATCH 01/50] form --- src/form/{Form.jsx => Form.tsx} | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) rename src/form/{Form.jsx => Form.tsx} (72%) diff --git a/src/form/Form.jsx b/src/form/Form.tsx similarity index 72% rename from src/form/Form.jsx rename to src/form/Form.tsx index 011fbb2..fbe7fe0 100644 --- a/src/form/Form.jsx +++ b/src/form/Form.tsx @@ -16,12 +16,15 @@ limitations under the License. import classNames from "classnames"; import React, { forwardRef } from "react"; + import styles from "./Form.module.css"; -export const Form = forwardRef(({ children, className, ...rest }, ref) => { - return ( -
- {children} -
- ); -}); +export const Form = forwardRef( + ({ children, className, ...rest }, ref) => { + return ( +
+ {children} +
+ ); + } +); From e17a7cedb6cf01889532f099fb885e03b40efd80 Mon Sep 17 00:00:00 2001 From: Timo K Date: Thu, 14 Jul 2022 19:20:52 +0200 Subject: [PATCH 02/50] form_home --- src/ClientContext.tsx | 1 + src/button/CopyButton.tsx | 4 +-- src/home/{CallList.jsx => CallList.tsx} | 25 ++++++++++++---- src/home/{HomePage.jsx => HomePage.tsx} | 0 ...allModal.jsx => JoinExistingCallModal.tsx} | 12 ++++++-- ...{RegisteredView.jsx => RegisteredView.tsx} | 18 +++++++----- ...GroupCallRooms.js => useGroupCallRooms.ts} | 29 ++++++++++++++----- 7 files changed, 64 insertions(+), 25 deletions(-) rename src/home/{CallList.jsx => CallList.tsx} (81%) rename src/home/{HomePage.jsx => HomePage.tsx} (100%) rename src/home/{JoinExistingCallModal.jsx => JoinExistingCallModal.tsx} (79%) rename src/home/{RegisteredView.jsx => RegisteredView.tsx} (91%) rename src/home/{useGroupCallRooms.js => useGroupCallRooms.ts} (72%) diff --git a/src/ClientContext.tsx b/src/ClientContext.tsx index 09d910b..ee4fef0 100644 --- a/src/ClientContext.tsx +++ b/src/ClientContext.tsx @@ -59,6 +59,7 @@ interface ClientState { isPasswordlessUser: boolean; client: MatrixClient; userName: string; + error: Error; changePassword: (password: string) => Promise; logout: () => void; setClient: (client: MatrixClient, session: Session) => void; diff --git a/src/button/CopyButton.tsx b/src/button/CopyButton.tsx index 480cd44..87c58da 100644 --- a/src/button/CopyButton.tsx +++ b/src/button/CopyButton.tsx @@ -23,10 +23,10 @@ import { Button, ButtonVariant } from "./Button"; interface Props { value: string; - children: JSX.Element; + children?: JSX.Element; className: string; variant: ButtonVariant; - copiedMessage: string; + copiedMessage?: string; } export function CopyButton({ value, diff --git a/src/home/CallList.jsx b/src/home/CallList.tsx similarity index 81% rename from src/home/CallList.jsx rename to src/home/CallList.tsx index e4badf2..c9c29d8 100644 --- a/src/home/CallList.jsx +++ b/src/home/CallList.tsx @@ -16,14 +16,22 @@ limitations under the License. import React from "react"; import { Link } from "react-router-dom"; +import { MatrixClient, Room, RoomMember } from "matrix-js-sdk"; + import { CopyButton } from "../button"; import { Facepile } from "../Facepile"; -import { Avatar } from "../Avatar"; +import { Avatar, Size } from "../Avatar"; import styles from "./CallList.module.css"; import { getRoomUrl } from "../matrix-utils"; import { Body, Caption } from "../typography/Typography"; +import { GroupCallRoom } from "./useGroupCallRooms"; -export function CallList({ rooms, client, disableFacepile }) { +interface CallListProps { + rooms: GroupCallRoom[]; + client: MatrixClient; + disableFacepile: boolean; +} +export function CallList({ rooms, client, disableFacepile }: CallListProps) { return ( <>
@@ -48,7 +56,14 @@ export function CallList({ rooms, client, disableFacepile }) { ); } - +interface CallTileProps { + name: string; + avatarUrl: string; + roomId: string; + participants: RoomMember[]; + client: MatrixClient; + disableFacepile: boolean; +} function CallTile({ name, avatarUrl, @@ -56,12 +71,12 @@ function CallTile({ participants, client, disableFacepile, -}) { +}: CallTileProps) { return (
void; + onClose: (e: PressEvent) => void; + [index: string]: unknown; +} +export function JoinExistingCallModal({ onJoin, onClose, ...rest }: Props) { return (

This call already exists, would you like to join?

- +
diff --git a/src/home/RegisteredView.jsx b/src/home/RegisteredView.tsx similarity index 91% rename from src/home/RegisteredView.jsx rename to src/home/RegisteredView.tsx index 1d07d1c..5f826a7 100644 --- a/src/home/RegisteredView.jsx +++ b/src/home/RegisteredView.tsx @@ -15,6 +15,9 @@ limitations under the License. */ import React, { useState, useCallback } from "react"; +import { useHistory } from "react-router-dom"; +import { MatrixClient } from "matrix-js-sdk"; + import { createRoom, roomAliasLocalpartFromRoomName } from "../matrix-utils"; import { useGroupCallRooms } from "./useGroupCallRooms"; import { Header, HeaderLogo, LeftNav, RightNav } from "../Header"; @@ -26,21 +29,23 @@ import { CallList } from "./CallList"; import { UserMenuContainer } from "../UserMenuContainer"; import { useModalTriggerState } from "../Modal"; import { JoinExistingCallModal } from "./JoinExistingCallModal"; -import { useHistory } from "react-router-dom"; import { Title } from "../typography/Typography"; import { Form } from "../form/Form"; import { CallType, CallTypeDropdown } from "./CallTypeDropdown"; -export function RegisteredView({ client }) { +export function RegisteredView({ client }: { client: MatrixClient }) { const [callType, setCallType] = useState(CallType.Video); const [loading, setLoading] = useState(false); - const [error, setError] = useState(); + const [error, setError] = useState(); const history = useHistory(); + const { modalState, modalProps } = useModalTriggerState(); + const onSubmit = useCallback( (e) => { e.preventDefault(); const data = new FormData(e.target); - const roomName = data.get("callName"); + const roomNameData = data.get("callName"); + const roomName = typeof roomNameData === "string" ? roomNameData : ""; const ptt = callType === CallType.Radio; async function submit() { @@ -68,13 +73,12 @@ export function RegisteredView({ client }) { } }); }, - [client, callType] + [callType, client, history, modalState] ); const recentRooms = useGroupCallRooms(client); - const { modalState, modalProps } = useModalTriggerState(); - const [existingRoomId, setExistingRoomId] = useState(); + const [existingRoomId, setExistingRoomId] = useState(); const onJoinExistingRoom = useCallback(() => { history.push(`/${existingRoomId}`); }, [history, existingRoomId]); diff --git a/src/home/useGroupCallRooms.js b/src/home/useGroupCallRooms.ts similarity index 72% rename from src/home/useGroupCallRooms.js rename to src/home/useGroupCallRooms.ts index 4177a5f..cf15a26 100644 --- a/src/home/useGroupCallRooms.js +++ b/src/home/useGroupCallRooms.ts @@ -14,11 +14,21 @@ See the License for the specific language governing permissions and limitations under the License. */ +import { GroupCall, MatrixClient, Room, RoomMember } from "matrix-js-sdk"; +import { GroupCallEventHandlerEvent } from "matrix-js-sdk/src/webrtc/groupCallEventHandler"; import { useState, useEffect } from "react"; -const tsCache = {}; +export interface GroupCallRoom { + roomId: string; + roomName: string; + avatarUrl: string; + room: Room; + groupCall: GroupCall; + participants: RoomMember[]; +} +const tsCache: { [index: string]: number } = {}; -function getLastTs(client, r) { +function getLastTs(client: MatrixClient, r: Room) { if (tsCache[r.roomId]) { return tsCache[r.roomId]; } @@ -59,13 +69,13 @@ function getLastTs(client, r) { return ts; } -function sortRooms(client, rooms) { +function sortRooms(client: MatrixClient, rooms: Room[]): Room[] { return rooms.sort((a, b) => { return getLastTs(client, b) - getLastTs(client, a); }); } -export function useGroupCallRooms(client) { +export function useGroupCallRooms(client: MatrixClient): GroupCallRoom[] { const [rooms, setRooms] = useState([]); useEffect(() => { @@ -90,12 +100,15 @@ export function useGroupCallRooms(client) { updateRooms(); - client.on("GroupCall.incoming", updateRooms); - client.on("GroupCall.participants", updateRooms); + client.on(GroupCallEventHandlerEvent.Incoming, updateRooms); + client.on(GroupCallEventHandlerEvent.Participants, updateRooms); return () => { - client.removeListener("GroupCall.incoming", updateRooms); - client.removeListener("GroupCall.participants", updateRooms); + client.removeListener(GroupCallEventHandlerEvent.Incoming, updateRooms); + client.removeListener( + GroupCallEventHandlerEvent.Participants, + updateRooms + ); }; }, [client]); From 3727bfb67fedaac27fea60dcbac28ae638480f8b Mon Sep 17 00:00:00 2001 From: Timo K Date: Thu, 28 Jul 2022 00:17:09 +0200 Subject: [PATCH 03/50] more types --- src/form/Form.tsx | 27 ++++++++++++++++++--------- src/home/CallList.tsx | 2 +- src/home/HomePage.tsx | 1 + src/home/JoinExistingCallModal.tsx | 1 + src/home/RegisteredView.tsx | 26 +++++++++++++++++--------- 5 files changed, 38 insertions(+), 19 deletions(-) diff --git a/src/form/Form.tsx b/src/form/Form.tsx index fbe7fe0..944724b 100644 --- a/src/form/Form.tsx +++ b/src/form/Form.tsx @@ -15,16 +15,25 @@ limitations under the License. */ import classNames from "classnames"; -import React, { forwardRef } from "react"; +import React, { FormEventHandler, forwardRef } from "react"; import styles from "./Form.module.css"; -export const Form = forwardRef( - ({ children, className, ...rest }, ref) => { - return ( -
- {children} -
- ); +export const Form = forwardRef< + HTMLFormElement, + { + className: string; + onSubmit: FormEventHandler; + children: JSX.Element[]; } -); +>(({ children, className, onSubmit }, ref) => { + return ( +
+ {children} +
+ ); +}); diff --git a/src/home/CallList.tsx b/src/home/CallList.tsx index c9c29d8..b7611aa 100644 --- a/src/home/CallList.tsx +++ b/src/home/CallList.tsx @@ -16,7 +16,7 @@ limitations under the License. import React from "react"; import { Link } from "react-router-dom"; -import { MatrixClient, Room, RoomMember } from "matrix-js-sdk"; +import { MatrixClient, RoomMember } from "matrix-js-sdk"; import { CopyButton } from "../button"; import { Facepile } from "../Facepile"; diff --git a/src/home/HomePage.tsx b/src/home/HomePage.tsx index 89c9ece..00f770f 100644 --- a/src/home/HomePage.tsx +++ b/src/home/HomePage.tsx @@ -15,6 +15,7 @@ limitations under the License. */ import React from "react"; + import { useClient } from "../ClientContext"; import { ErrorView, LoadingView } from "../FullScreenView"; import { UnauthenticatedView } from "./UnauthenticatedView"; diff --git a/src/home/JoinExistingCallModal.tsx b/src/home/JoinExistingCallModal.tsx index 36b508f..0c17ede 100644 --- a/src/home/JoinExistingCallModal.tsx +++ b/src/home/JoinExistingCallModal.tsx @@ -24,6 +24,7 @@ import styles from "./JoinExistingCallModal.module.css"; interface Props { onJoin: (e: PressEvent) => void; onClose: (e: PressEvent) => void; + // TODO: add used parameters for [index: string]: unknown; } export function JoinExistingCallModal({ onJoin, onClose, ...rest }: Props) { diff --git a/src/home/RegisteredView.tsx b/src/home/RegisteredView.tsx index 8df30ab..7a87bdc 100644 --- a/src/home/RegisteredView.tsx +++ b/src/home/RegisteredView.tsx @@ -14,7 +14,12 @@ See the License for the specific language governing permissions and limitations under the License. */ -import React, { useState, useCallback } from "react"; +import React, { + useState, + useCallback, + FormEvent, + FormEventHandler, +} from "react"; import { useHistory } from "react-router-dom"; import { MatrixClient } from "matrix-js-sdk"; @@ -32,27 +37,31 @@ import { JoinExistingCallModal } from "./JoinExistingCallModal"; import { Title } from "../typography/Typography"; import { Form } from "../form/Form"; import { CallType, CallTypeDropdown } from "./CallTypeDropdown"; +interface Props { + client: MatrixClient; + isPasswordlessUser: boolean; +} -export function RegisteredView({ client }: { client: MatrixClient }) { +export function RegisteredView({ client, isPasswordlessUser }: Props) { const [callType, setCallType] = useState(CallType.Video); const [loading, setLoading] = useState(false); const [error, setError] = useState(); const history = useHistory(); const { modalState, modalProps } = useModalTriggerState(); - const onSubmit = useCallback( - (e) => { + const onSubmit: FormEventHandler = useCallback( + (e: FormEvent) => { e.preventDefault(); - const data = new FormData(e.target); + const data = new FormData(e.target as HTMLFormElement); const roomNameData = data.get("callName"); const roomName = typeof roomNameData === "string" ? roomNameData : ""; - const ptt = callType === CallType.Radio; + // const ptt = callType === CallType.Radio; async function submit() { setError(undefined); setLoading(true); - const [roomIdOrAlias] = await createRoom(client, roomName, ptt); + const [roomIdOrAlias] = await createRoom(client, roomName); if (roomIdOrAlias) { history.push(`/room/${roomIdOrAlias}`); @@ -69,11 +78,10 @@ export function RegisteredView({ client }: { client: MatrixClient }) { console.error(error); setLoading(false); setError(error); - reset(); } }); }, - [callType, client, history, modalState] + [client, history, modalState] ); const recentRooms = useGroupCallRooms(client); From 4f36d149d73c03afd5671dd5b323bb8c2deeece0 Mon Sep 17 00:00:00 2001 From: Timo K Date: Thu, 28 Jul 2022 00:22:48 +0200 Subject: [PATCH 04/50] make error optional in ClientState --- src/ClientContext.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ClientContext.tsx b/src/ClientContext.tsx index ca2a659..5e0c3c1 100644 --- a/src/ClientContext.tsx +++ b/src/ClientContext.tsx @@ -59,10 +59,10 @@ interface ClientState { isPasswordlessUser: boolean; client: MatrixClient; userName: string; - error: Error; changePassword: (password: string) => Promise; logout: () => void; setClient: (client: MatrixClient, session: Session) => void; + error?: Error; } const ClientContext = createContext(null); From cb5b3e946811af701688039586e14b436d9030f7 Mon Sep 17 00:00:00 2001 From: Timo K Date: Fri, 29 Jul 2022 15:07:35 +0200 Subject: [PATCH 05/50] review changes --- src/form/Form.tsx | 35 +++++++++++++++--------------- src/home/CallList.tsx | 4 ++-- src/home/JoinExistingCallModal.tsx | 1 + src/home/RegisteredView.tsx | 1 + 4 files changed, 22 insertions(+), 19 deletions(-) diff --git a/src/form/Form.tsx b/src/form/Form.tsx index 944724b..55dadae 100644 --- a/src/form/Form.tsx +++ b/src/form/Form.tsx @@ -19,21 +19,22 @@ import React, { FormEventHandler, forwardRef } from "react"; import styles from "./Form.module.css"; -export const Form = forwardRef< - HTMLFormElement, - { - className: string; - onSubmit: FormEventHandler; - children: JSX.Element[]; +interface FormProps { + className: string; + onSubmit: FormEventHandler; + children: JSX.Element[]; +} + +export const Form = forwardRef( + ({ children, className, onSubmit }, ref) => { + return ( +
+ {children} +
+ ); } ->(({ children, className, onSubmit }, ref) => { - return ( -
- {children} -
- ); -}); +); diff --git a/src/home/CallList.tsx b/src/home/CallList.tsx index b7611aa..8b25723 100644 --- a/src/home/CallList.tsx +++ b/src/home/CallList.tsx @@ -29,7 +29,7 @@ import { GroupCallRoom } from "./useGroupCallRooms"; interface CallListProps { rooms: GroupCallRoom[]; client: MatrixClient; - disableFacepile: boolean; + disableFacepile?: boolean; } export function CallList({ rooms, client, disableFacepile }: CallListProps) { return ( @@ -62,7 +62,7 @@ interface CallTileProps { roomId: string; participants: RoomMember[]; client: MatrixClient; - disableFacepile: boolean; + disableFacepile?: boolean; } function CallTile({ name, diff --git a/src/home/JoinExistingCallModal.tsx b/src/home/JoinExistingCallModal.tsx index 0c17ede..b26c45d 100644 --- a/src/home/JoinExistingCallModal.tsx +++ b/src/home/JoinExistingCallModal.tsx @@ -21,6 +21,7 @@ import { Modal, ModalContent } from "../Modal"; import { Button } from "../button"; import { FieldRow } from "../input/Input"; import styles from "./JoinExistingCallModal.module.css"; + interface Props { onJoin: (e: PressEvent) => void; onClose: (e: PressEvent) => void; diff --git a/src/home/RegisteredView.tsx b/src/home/RegisteredView.tsx index 7a87bdc..4d190dd 100644 --- a/src/home/RegisteredView.tsx +++ b/src/home/RegisteredView.tsx @@ -37,6 +37,7 @@ import { JoinExistingCallModal } from "./JoinExistingCallModal"; import { Title } from "../typography/Typography"; import { Form } from "../form/Form"; import { CallType, CallTypeDropdown } from "./CallTypeDropdown"; + interface Props { client: MatrixClient; isPasswordlessUser: boolean; From 57cde41983755f34bf02b5da714d1843406f91f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Sat, 30 Jul 2022 09:41:45 +0200 Subject: [PATCH 06/50] `App` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/{App.jsx => App.tsx} | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) rename src/{App.jsx => App.tsx} (96%) diff --git a/src/App.jsx b/src/App.tsx similarity index 96% rename from src/App.jsx rename to src/App.tsx index 1782f69..b41271b 100644 --- a/src/App.jsx +++ b/src/App.tsx @@ -18,6 +18,7 @@ import React from "react"; import { BrowserRouter as Router, Switch, Route } from "react-router-dom"; import * as Sentry from "@sentry/react"; import { OverlayProvider } from "@react-aria/overlays"; + import { HomePage } from "./home/HomePage"; import { LoginPage } from "./auth/LoginPage"; import { RegisterPage } from "./auth/RegisterPage"; @@ -31,7 +32,11 @@ import { CrashView } from "./FullScreenView"; const SentryRoute = Sentry.withSentryRouting(Route); -export default function App({ history }) { +interface AppProps { + history: History; +} + +export default function App({ history }: AppProps) { usePageFocusStyle(); const errorPage = ; From 949d28a88f8bdedf1487895f6597b3ac2051e2b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Sat, 30 Jul 2022 09:46:47 +0200 Subject: [PATCH 07/50] `Facepile` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/{Facepile.jsx => Facepile.tsx} | 53 +++++++++++++++++++++--------- 1 file changed, 37 insertions(+), 16 deletions(-) rename src/{Facepile.jsx => Facepile.tsx} (54%) diff --git a/src/Facepile.jsx b/src/Facepile.tsx similarity index 54% rename from src/Facepile.jsx rename to src/Facepile.tsx index e19e642..5f661c3 100644 --- a/src/Facepile.jsx +++ b/src/Facepile.tsx @@ -1,22 +1,48 @@ -import React from "react"; -import styles from "./Facepile.module.css"; -import classNames from "classnames"; -import { Avatar, sizes } from "./Avatar"; +/* +Copyright 2022 New Vector Ltd -const overlapMap = { - xs: 2, - sm: 4, - md: 8, +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 React, { HTMLAttributes } from "react"; +import classNames from "classnames"; +import { MatrixClient, RoomMember } from "matrix-js-sdk"; + +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; + participants: RoomMember[]; + max: number; + size: Size; +} + export function Facepile({ className, client, participants, - max, - size, + max = 3, + size = Size.XS, ...rest -}) { +}: Props) { const _size = sizes.get(size); const _overlap = overlapMap[size]; @@ -56,8 +82,3 @@ export function Facepile({
); } - -Facepile.defaultProps = { - max: 3, - size: "xs", -}; From 4b01000d4c4e87f9fd90f3877117de0c43b0d8c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Sat, 30 Jul 2022 09:48:29 +0200 Subject: [PATCH 08/50] `FullScreenView` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- ...{FullScreenView.jsx => FullScreenView.tsx} | 24 +++++++++++++------ 1 file changed, 17 insertions(+), 7 deletions(-) rename src/{FullScreenView.jsx => FullScreenView.tsx} (87%) diff --git a/src/FullScreenView.jsx b/src/FullScreenView.tsx similarity index 87% rename from src/FullScreenView.jsx rename to src/FullScreenView.tsx index 6dd7127..9182111 100644 --- a/src/FullScreenView.jsx +++ b/src/FullScreenView.tsx @@ -1,13 +1,19 @@ -import React, { useCallback, useEffect } from "react"; +import React, { ReactNode, useCallback, useEffect } from "react"; import { useLocation } from "react-router-dom"; -import styles from "./FullScreenView.module.css"; -import { Header, HeaderLogo, LeftNav, RightNav } from "./Header"; import classNames from "classnames"; + +import { Header, HeaderLogo, LeftNav, RightNav } from "./Header"; import { LinkButton, Button } from "./button"; import { useSubmitRageshake } from "./settings/submit-rageshake"; import { ErrorMessage } from "./input/Input"; +import styles from "./FullScreenView.module.css"; -export function FullScreenView({ className, children }) { +interface FullScreenViewProps { + className?: string; + children: ReactNode; +} + +export function FullScreenView({ className, children }: FullScreenViewProps) { return (
@@ -23,7 +29,11 @@ export function FullScreenView({ className, children }) { ); } -export function ErrorView({ error }) { +interface ErrorViewProps { + error: Error; +} + +export function ErrorView({ error }: ErrorViewProps) { const location = useLocation(); useEffect(() => { @@ -31,7 +41,7 @@ export function ErrorView({ error }) { }, [error]); const onReload = useCallback(() => { - window.location = "/"; + window.location.href = "/"; }, []); return ( @@ -72,7 +82,7 @@ export function CrashView() { }, [submitRageshake]); const onReload = useCallback(() => { - window.location = "/"; + window.location.href = "/"; }, []); let logsComponent; From 077e5b299868ea699974f4be5771d958a1b85727 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Sat, 30 Jul 2022 09:50:16 +0200 Subject: [PATCH 09/50] `Header` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/{Header.jsx => Header.tsx} | 85 +++++++++++++++++++++++++++------- 1 file changed, 69 insertions(+), 16 deletions(-) rename src/{Header.jsx => Header.tsx} (64%) diff --git a/src/Header.jsx b/src/Header.tsx similarity index 64% rename from src/Header.jsx rename to src/Header.tsx index c1cf771..cde794f 100644 --- a/src/Header.jsx +++ b/src/Header.tsx @@ -1,18 +1,26 @@ import classNames from "classnames"; -import React, { useCallback, useRef } from "react"; +import React, { HTMLAttributes, ReactNode, useCallback, useRef } from "react"; import { Link } from "react-router-dom"; -import styles from "./Header.module.css"; -import { ReactComponent as Logo } from "./icons/Logo.svg"; -import { ReactComponent as VideoIcon } from "./icons/Video.svg"; -import { ReactComponent as ArrowLeftIcon } from "./icons/ArrowLeft.svg"; import { useButton } from "@react-aria/button"; -import { Subtitle } from "./typography/Typography"; -import { Avatar } from "./Avatar"; -import { IncompatibleVersionModal } from "./IncompatibleVersionModal"; +import { AriaButtonProps } from "@react-types/button"; +import { Room } from "matrix-js-sdk"; + +import styles from "./Header.module.css"; import { useModalTriggerState } from "./Modal"; import { Button } from "./button"; +import { ReactComponent as Logo } from "./icons/Logo.svg"; +import { ReactComponent as VideoIcon } from "./icons/Video.svg"; +import { Subtitle } from "./typography/Typography"; +import { Avatar, Size } from "./Avatar"; +import { IncompatibleVersionModal } from "./IncompatibleVersionModal"; +import { ReactComponent as ArrowLeftIcon } from "./icons/ArrowLeft.svg"; -export function Header({ children, className, ...rest }) { +interface HeaderProps extends HTMLAttributes { + children: ReactNode; + className?: string; +} + +export function Header({ children, className, ...rest }: HeaderProps) { return (
{children} @@ -20,7 +28,18 @@ export function Header({ children, className, ...rest }) { ); } -export function LeftNav({ children, className, hideMobile, ...rest }) { +interface LeftNavProps extends HTMLAttributes { + children: ReactNode; + className?: string; + hideMobile?: boolean; +} + +export function LeftNav({ + children, + className, + hideMobile, + ...rest +}: LeftNavProps) { return (
@@ -60,12 +94,17 @@ export function HeaderLogo({ className }) { ); } -export function RoomHeaderInfo({ roomName, avatarUrl }) { +interface RoomHeaderInfo { + roomName: string; + avatarUrl: string; +} + +export function RoomHeaderInfo({ roomName, avatarUrl }: RoomHeaderInfo) { return ( <>
{ + roomName: string; + avatarUrl: string; + isEmbedded: boolean; +} + export function RoomSetupHeaderInfo({ roomName, avatarUrl, isEmbedded, ...rest -}) { +}: RoomSetupHeaderInfoProps) { const ref = useRef(); const { buttonProps } = useButton(rest, ref); @@ -102,7 +147,15 @@ export function RoomSetupHeaderInfo({ ); } -export function VersionMismatchWarning({ users, room }) { +interface VersionMismatchWarningProps { + users: Set; + room: Room; +} + +export function VersionMismatchWarning({ + users, + room, +}: VersionMismatchWarningProps) { const { modalState, modalProps } = useModalTriggerState(); const onDetailsClick = useCallback(() => { From 0aa3359f963e0ca11b294d3f5aecf9d3f353fcd0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Sat, 30 Jul 2022 09:50:36 +0200 Subject: [PATCH 10/50] `IndexDBWorker` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/IndexedDBWorker.js | 5 ----- src/IndexedDBWorker.ts | 6 ++++++ 2 files changed, 6 insertions(+), 5 deletions(-) delete mode 100644 src/IndexedDBWorker.js create mode 100644 src/IndexedDBWorker.ts diff --git a/src/IndexedDBWorker.js b/src/IndexedDBWorker.js deleted file mode 100644 index 0e373ca..0000000 --- a/src/IndexedDBWorker.js +++ /dev/null @@ -1,5 +0,0 @@ -import { IndexedDBStoreWorker } from "matrix-js-sdk/src/indexeddb-worker"; - -const remoteWorker = new IndexedDBStoreWorker(self.postMessage); - -self.onmessage = remoteWorker.onMessage; diff --git a/src/IndexedDBWorker.ts b/src/IndexedDBWorker.ts new file mode 100644 index 0000000..a9ddecd --- /dev/null +++ b/src/IndexedDBWorker.ts @@ -0,0 +1,6 @@ +import { IndexedDBStoreWorker } from "matrix-js-sdk/src/indexeddb-worker"; + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +const remoteWorker = new IndexedDBStoreWorker((self as any).postMessage); + +self.onmessage = remoteWorker.onMessage; From 8634c16a472d579001169d2e0e60bc1867c2f569 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Sat, 30 Jul 2022 09:50:58 +0200 Subject: [PATCH 11/50] `ListBox` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/ListBox.jsx | 50 --------------------------- src/ListBox.tsx | 89 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 89 insertions(+), 50 deletions(-) delete mode 100644 src/ListBox.jsx create mode 100644 src/ListBox.tsx diff --git a/src/ListBox.jsx b/src/ListBox.jsx deleted file mode 100644 index 478b6f0..0000000 --- a/src/ListBox.jsx +++ /dev/null @@ -1,50 +0,0 @@ -import React, { useRef } from "react"; -import { useListBox, useOption } from "@react-aria/listbox"; -import styles from "./ListBox.module.css"; -import classNames from "classnames"; - -export function ListBox(props) { - const ref = useRef(); - let { listBoxRef = ref, state } = props; - const { listBoxProps } = useListBox(props, state, listBoxRef); - - return ( -
    - {[...state.collection].map((item) => ( -
- ); -} - -function Option({ item, state, className }) { - const ref = useRef(); - const { optionProps, isSelected, isFocused, isDisabled } = useOption( - { key: item.key }, - state, - ref - ); - - return ( -
  • - {item.rendered} -
  • - ); -} diff --git a/src/ListBox.tsx b/src/ListBox.tsx new file mode 100644 index 0000000..a57d440 --- /dev/null +++ b/src/ListBox.tsx @@ -0,0 +1,89 @@ +/* +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 React, { useRef } from "react"; +import { useListBox, useOption, AriaListBoxOptions } from "@react-aria/listbox"; +import { ListState } from "@react-stately/list"; +import { Node } from "@react-types/shared"; +import classNames from "classnames"; + +import styles from "./ListBox.module.css"; + +interface ListBoxProps extends AriaListBoxOptions { + className: string; + optionClassName: string; + listBoxRef: React.MutableRefObject; + state: ListState; +} + +export function ListBox({ + state, + optionClassName, + className, + listBoxRef, + ...rest +}: ListBoxProps) { + const ref = useRef(); + if (!listBoxRef) listBoxRef = ref; + + const { listBoxProps } = useListBox(rest, state, listBoxRef); + + return ( +
      + {[...state.collection].map((item) => ( +
    + ); +} + +interface OptionProps { + className: string; + state: ListState; + item: Node; +} + +function Option({ item, state, className }: OptionProps) { + const ref = useRef(); + const { optionProps, isSelected, isFocused, isDisabled } = useOption( + { key: item.key }, + state, + ref + ); + + return ( +
  • + {item.rendered} +
  • + ); +} From afc072da2ce6a49c7b1bbf2353fe640731d51669 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Sat, 30 Jul 2022 09:51:32 +0200 Subject: [PATCH 12/50] `Menu ` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/{Menu.jsx => Menu.tsx} | 37 ++++++++++++++++++++++++++++--------- 1 file changed, 28 insertions(+), 9 deletions(-) rename src/{Menu.jsx => Menu.tsx} (58%) diff --git a/src/Menu.jsx b/src/Menu.tsx similarity index 58% rename from src/Menu.jsx rename to src/Menu.tsx index 260db87..2b16ecf 100644 --- a/src/Menu.jsx +++ b/src/Menu.tsx @@ -1,15 +1,28 @@ import React, { useRef, useState } from "react"; -import styles from "./Menu.module.css"; -import { useMenu, useMenuItem } from "@react-aria/menu"; -import { useTreeState } from "@react-stately/tree"; +import { AriaMenuOptions, useMenu, useMenuItem } from "@react-aria/menu"; +import { TreeState, useTreeState } from "@react-stately/tree"; import { mergeProps } from "@react-aria/utils"; import { useFocus } from "@react-aria/interactions"; import classNames from "classnames"; +import { Node } from "@react-types/shared"; -export function Menu({ className, onAction, ...rest }) { - const state = useTreeState({ ...rest, selectionMode: "none" }); +import styles from "./Menu.module.css"; + +interface MenuProps extends AriaMenuOptions { + className: String; + onAction: () => void; + onClose: () => void; +} + +export function Menu({ + className, + onAction, + onClose, + ...rest +}: MenuProps) { + const state = useTreeState({ ...rest, selectionMode: "none" }); const menuRef = useRef(); - const { menuProps } = useMenu(rest, state, menuRef); + const { menuProps } = useMenu(rest, state, menuRef); return (
      ))}
    ); } -function MenuItem({ item, state, onAction, onClose }) { +interface MenuItemProps { + item: Node; + state: TreeState; + onAction: () => void; + onClose: () => void; +} + +function MenuItem({ item, state, onAction, onClose }: MenuItemProps) { const ref = useRef(); const { menuItemProps } = useMenuItem( { key: item.key, - isDisabled: item.isDisabled, onAction, onClose, }, From 6acc84fd9e3cffa8475afc467952a6d7ed95e480 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Sat, 30 Jul 2022 09:59:20 +0200 Subject: [PATCH 13/50] `Tooltip` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/Tooltip.jsx | 76 -------------------------------------- src/Tooltip.tsx | 98 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 98 insertions(+), 76 deletions(-) delete mode 100644 src/Tooltip.jsx create mode 100644 src/Tooltip.tsx diff --git a/src/Tooltip.jsx b/src/Tooltip.jsx deleted file mode 100644 index 9f61308..0000000 --- a/src/Tooltip.jsx +++ /dev/null @@ -1,76 +0,0 @@ -import React, { forwardRef, useRef } from "react"; -import { useTooltipTriggerState } from "@react-stately/tooltip"; -import { FocusableProvider } from "@react-aria/focus"; -import { useTooltipTrigger, useTooltip } from "@react-aria/tooltip"; -import { mergeProps, useObjectRef } from "@react-aria/utils"; -import styles from "./Tooltip.module.css"; -import classNames from "classnames"; -import { OverlayContainer, useOverlayPosition } from "@react-aria/overlays"; - -export const Tooltip = forwardRef( - ({ position, state, className, ...props }, ref) => { - let { tooltipProps } = useTooltip(props, state); - - return ( -
    - {props.children} -
    - ); - } -); - -export const TooltipTrigger = forwardRef(({ children, ...rest }, ref) => { - const tooltipState = useTooltipTriggerState(rest); - const triggerRef = useObjectRef(ref); - const overlayRef = useRef(); - const { triggerProps, tooltipProps } = useTooltipTrigger( - rest, - tooltipState, - triggerRef - ); - - const { overlayProps } = useOverlayPosition({ - placement: rest.placement || "top", - targetRef: triggerRef, - overlayRef, - isOpen: tooltipState.isOpen, - offset: 5, - }); - - if ( - !Array.isArray(children) || - children.length > 2 || - typeof children[1] !== "function" - ) { - throw new Error( - "TooltipTrigger must have two props. The first being a button and the second being a render prop." - ); - } - - const [tooltipTrigger, tooltip] = children; - - return ( - - {} - {tooltipState.isOpen && ( - - - {tooltip()} - - - )} - - ); -}); - -TooltipTrigger.defaultProps = { - delay: 250, -}; diff --git a/src/Tooltip.tsx b/src/Tooltip.tsx new file mode 100644 index 0000000..195443d --- /dev/null +++ b/src/Tooltip.tsx @@ -0,0 +1,98 @@ +import React, { + ForwardedRef, + forwardRef, + ReactElement, + ReactNode, + useRef, +} from "react"; +import { + TooltipTriggerState, + useTooltipTriggerState, +} from "@react-stately/tooltip"; +import { FocusableProvider } from "@react-aria/focus"; +import { useTooltipTrigger, useTooltip } from "@react-aria/tooltip"; +import { mergeProps, useObjectRef } from "@react-aria/utils"; +import classNames from "classnames"; +import { OverlayContainer, useOverlayPosition } from "@react-aria/overlays"; +import { Placement } from "@react-types/overlays"; + +import styles from "./Tooltip.module.css"; + +interface TooltipProps { + className?: string; + state: TooltipTriggerState; + children: ReactNode; +} + +export const Tooltip = forwardRef( + ( + { state, className, children, ...rest }: TooltipProps, + ref: ForwardedRef + ) => { + const { tooltipProps } = useTooltip(rest, state); + + return ( +
    + {children} +
    + ); + } +); + +interface TooltipTriggerProps { + children: ReactElement; + placement?: Placement; + delay?: number; + tooltip: () => string; +} + +export const TooltipTrigger = forwardRef( + ( + { children, placement, tooltip, ...rest }: TooltipTriggerProps, + ref: ForwardedRef + ) => { + const tooltipTriggerProps = { delay: 250, ...rest }; + const tooltipState = useTooltipTriggerState(tooltipTriggerProps); + const triggerRef = useObjectRef(ref); + const overlayRef = useRef(); + const { triggerProps, tooltipProps } = useTooltipTrigger( + tooltipTriggerProps, + tooltipState, + triggerRef + ); + + const { overlayProps } = useOverlayPosition({ + placement: placement || "top", + targetRef: triggerRef, + overlayRef, + isOpen: tooltipState.isOpen, + offset: 5, + }); + + return ( + + ( + children.props, + rest + )} + /> + {tooltipState.isOpen && ( + + + {tooltip()} + + + )} + + ); + } +); From 5841c4f38d22456d50b97d7ea39b0002371b6536 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Sat, 30 Jul 2022 09:59:51 +0200 Subject: [PATCH 14/50] `usePageTitle` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/usePageTitle.js | 9 --------- src/usePageTitle.ts | 25 +++++++++++++++++++++++++ 2 files changed, 25 insertions(+), 9 deletions(-) delete mode 100644 src/usePageTitle.js create mode 100644 src/usePageTitle.ts diff --git a/src/usePageTitle.js b/src/usePageTitle.js deleted file mode 100644 index e7b5d9f..0000000 --- a/src/usePageTitle.js +++ /dev/null @@ -1,9 +0,0 @@ -import { useEffect } from "react"; - -export function usePageTitle(title) { - useEffect(() => { - const productName = - import.meta.env.VITE_PRODUCT_NAME || "Matrix Video Chat"; - document.title = title ? `${productName} | ${title}` : productName; - }, [title]); -} diff --git a/src/usePageTitle.ts b/src/usePageTitle.ts new file mode 100644 index 0000000..1516ecc --- /dev/null +++ b/src/usePageTitle.ts @@ -0,0 +1,25 @@ +/* +Copyright 2022 New Vector Ltd + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +import { useEffect } from "react"; + +export function usePageTitle(title: string): void { + useEffect(() => { + const productName = + import.meta.env.VITE_PRODUCT_NAME || "Matrix Video Chat"; + document.title = title ? `${productName} | ${title}` : productName; + }, [title]); +} From 005762a1a2dc265ab352ece7074ebc0ed6e5d407 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Sat, 30 Jul 2022 10:00:10 +0200 Subject: [PATCH 15/50] `usePageFocusStyle` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/{usePageFocusStyle.js => usePageFocusStyle.ts} | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) rename src/{usePageFocusStyle.js => usePageFocusStyle.ts} (92%) diff --git a/src/usePageFocusStyle.js b/src/usePageFocusStyle.ts similarity index 92% rename from src/usePageFocusStyle.js rename to src/usePageFocusStyle.ts index c7ec75f..542cffd 100644 --- a/src/usePageFocusStyle.js +++ b/src/usePageFocusStyle.ts @@ -1,8 +1,9 @@ import { useEffect } from "react"; import { useFocusVisible } from "@react-aria/interactions"; + import styles from "./usePageFocusStyle.module.css"; -export function usePageFocusStyle() { +export function usePageFocusStyle(): void { const { isFocusVisible } = useFocusVisible(); useEffect(() => { From 3b74920ece5a87fbb3faa8ca3c49672138a884b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Sat, 30 Jul 2022 10:00:34 +0200 Subject: [PATCH 16/50] `useLocationNavigation` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/{useLocationNavigation.js => useLocationNavigation.ts} | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename src/{useLocationNavigation.js => useLocationNavigation.ts} (81%) diff --git a/src/useLocationNavigation.js b/src/useLocationNavigation.ts similarity index 81% rename from src/useLocationNavigation.js rename to src/useLocationNavigation.ts index 2ae5234..81ecdbc 100644 --- a/src/useLocationNavigation.js +++ b/src/useLocationNavigation.ts @@ -1,7 +1,7 @@ import { useEffect } from "react"; import { useHistory } from "react-router-dom"; -export function useLocationNavigation(enabled = false) { +export function useLocationNavigation(enabled = false): void { const history = useHistory(); useEffect(() => { @@ -12,7 +12,7 @@ export function useLocationNavigation(enabled = false) { const url = new URL(tx.pathname, window.location.href); url.search = tx.search; url.hash = tx.hash; - window.location = url.href; + window.location.href = url.href; }); } From 43b63512374592d1e29a9a5939a63d724de18d42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Sat, 30 Jul 2022 10:00:51 +0200 Subject: [PATCH 17/50] `SequenceDiagramViewerPage` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- ...amViewerPage.jsx => SequenceDiagramViewerPage.tsx} | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) rename src/{SequenceDiagramViewerPage.jsx => SequenceDiagramViewerPage.tsx} (82%) diff --git a/src/SequenceDiagramViewerPage.jsx b/src/SequenceDiagramViewerPage.tsx similarity index 82% rename from src/SequenceDiagramViewerPage.jsx rename to src/SequenceDiagramViewerPage.tsx index 3752dc2..2b98baa 100644 --- a/src/SequenceDiagramViewerPage.jsx +++ b/src/SequenceDiagramViewerPage.tsx @@ -1,13 +1,20 @@ import React, { useCallback, useState } from "react"; + import { SequenceDiagramViewer } from "./room/GroupCallInspector"; import { FieldRow, InputField } from "./input/Input"; import { usePageTitle } from "./usePageTitle"; +interface DebugLog { + localUserId: string; + eventsByUserId: Record; + remoteUserIds: string[]; +} + export function SequenceDiagramViewerPage() { usePageTitle("Inspector"); - const [debugLog, setDebugLog] = useState(); - const [selectedUserId, setSelectedUserId] = useState(); + const [debugLog, setDebugLog] = useState(); + const [selectedUserId, setSelectedUserId] = useState(); const onChangeDebugLog = useCallback((e) => { if (e.target.files && e.target.files.length > 0) { e.target.files[0].text().then((text) => { From cc7584a223736a5e59192433889b4163c8516a24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Sat, 30 Jul 2022 10:02:07 +0200 Subject: [PATCH 18/50] `UserMenuContainer` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/{UserMenuContainer.jsx => UserMenuContainer.tsx} | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) rename src/{UserMenuContainer.jsx => UserMenuContainer.tsx} (90%) diff --git a/src/UserMenuContainer.jsx b/src/UserMenuContainer.tsx similarity index 90% rename from src/UserMenuContainer.jsx rename to src/UserMenuContainer.tsx index 18d52db..85cb1d7 100644 --- a/src/UserMenuContainer.jsx +++ b/src/UserMenuContainer.tsx @@ -1,12 +1,17 @@ import React, { useCallback } from "react"; import { useHistory, useLocation } from "react-router-dom"; + import { useClient } from "./ClientContext"; import { useProfile } from "./profile/useProfile"; import { useModalTriggerState } from "./Modal"; import { ProfileModal } from "./profile/ProfileModal"; import { UserMenu } from "./UserMenu"; -export function UserMenuContainer({ preventNavigation }) { +interface Props { + preventNavigation: boolean; +} + +export function UserMenuContainer({ preventNavigation }: Props) { const location = useLocation(); const history = useHistory(); const { isAuthenticated, isPasswordlessUser, logout, userName, client } = @@ -15,7 +20,7 @@ export function UserMenuContainer({ preventNavigation }) { const { modalState, modalProps } = useModalTriggerState(); const onAction = useCallback( - (value) => { + (value: string) => { switch (value) { case "user": modalState.open(); From abf5121b7470e6409dce6f03ee6619c68e83b693 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Sat, 30 Jul 2022 10:02:20 +0200 Subject: [PATCH 19/50] `UserMenu` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/{UserMenu.jsx => UserMenu.tsx} | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) rename src/{UserMenu.jsx => UserMenu.tsx} (83%) diff --git a/src/UserMenu.jsx b/src/UserMenu.tsx similarity index 83% rename from src/UserMenu.jsx rename to src/UserMenu.tsx index 6363948..83da187 100644 --- a/src/UserMenu.jsx +++ b/src/UserMenu.tsx @@ -1,16 +1,26 @@ import React, { useMemo } from "react"; import { Item } from "@react-stately/collections"; +import { useLocation } from "react-router-dom"; + import { Button, LinkButton } from "./button"; import { PopoverMenuTrigger } from "./popover/PopoverMenu"; import { Menu } from "./Menu"; -import { Tooltip, TooltipTrigger } from "./Tooltip"; -import { Avatar } from "./Avatar"; +import { TooltipTrigger } from "./Tooltip"; +import { Avatar, Size } from "./Avatar"; import { ReactComponent as UserIcon } from "./icons/User.svg"; import { ReactComponent as LoginIcon } from "./icons/Login.svg"; import { ReactComponent as LogoutIcon } from "./icons/Logout.svg"; -import styles from "./UserMenu.module.css"; -import { useLocation } from "react-router-dom"; import { Body } from "./typography/Typography"; +import styles from "./UserMenu.module.css"; + +interface UserMenuProps { + preventNavigation: boolean; + isAuthenticated: boolean; + isPasswordlessUser: boolean; + displayName: string; + avatarUrl: string; + onAction: (value: string) => void; +} export function UserMenu({ preventNavigation, @@ -19,7 +29,7 @@ export function UserMenu({ displayName, avatarUrl, onAction, -}) { +}: UserMenuProps) { const location = useLocation(); const items = useMemo(() => { @@ -62,11 +72,11 @@ export function UserMenu({ return ( - + "Profile"} placement="bottom left"> - {() => "Profile"} {(props) => ( {items.map(({ key, icon: Icon, label }) => ( - + {label} From 02aaa06cb368dbd9a38b80363d4b970b401a8775 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Sat, 30 Jul 2022 10:06:09 +0200 Subject: [PATCH 20/50] `Modal` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/{Modal.jsx => Modal.tsx} | 102 ++++++++++++++++++++++++++++------- 1 file changed, 84 insertions(+), 18 deletions(-) rename src/{Modal.jsx => Modal.tsx} (50%) diff --git a/src/Modal.jsx b/src/Modal.tsx similarity index 50% rename from src/Modal.jsx rename to src/Modal.tsx index f1d4d6e..4333b0c 100644 --- a/src/Modal.jsx +++ b/src/Modal.tsx @@ -1,29 +1,70 @@ -import React, { useRef, useMemo } from "react"; +/* +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. +*/ + +/* eslint-disable jsx-a11y/no-autofocus */ + +import React, { useRef, useMemo, ReactNode } from "react"; import { useOverlay, usePreventScroll, useModal, OverlayContainer, + OverlayProps, } from "@react-aria/overlays"; -import { useOverlayTriggerState } from "@react-stately/overlays"; +import { + OverlayTriggerState, + useOverlayTriggerState, +} from "@react-stately/overlays"; import { useDialog } from "@react-aria/dialog"; import { FocusScope } from "@react-aria/focus"; -import { useButton } from "@react-aria/button"; +import { ButtonAria, useButton } from "@react-aria/button"; +import classNames from "classnames"; +import { AriaDialogProps } from "@react-types/dialog"; + import { ReactComponent as CloseIcon } from "./icons/Close.svg"; import styles from "./Modal.module.css"; -import classNames from "classnames"; -export function Modal(props) { - const { title, children, className, mobileFullScreen } = props; +interface ModalProps extends OverlayProps, AriaDialogProps { + title: string; + children: ReactNode; + className?: string; + mobileFullScreen?: boolean; + onClose?: () => void; +} + +export function Modal({ + title, + children, + className, + mobileFullScreen, + onClose, + ...rest +}: ModalProps) { const modalRef = useRef(); - const { overlayProps, underlayProps } = useOverlay(props, modalRef); + const { overlayProps, underlayProps } = useOverlay(rest, modalRef); usePreventScroll(); const { modalProps } = useModal(); - const { dialogProps, titleProps } = useDialog(props, modalRef); + const { dialogProps, titleProps } = useDialog(rest, modalRef); const closeButtonRef = useRef(); - const { buttonProps: closeButtonProps } = useButton({ - onPress: () => props.onClose(), - }); + const { buttonProps: closeButtonProps } = useButton( + { + onPress: () => onClose(), + }, + closeButtonRef + ); return ( @@ -58,7 +99,16 @@ export function Modal(props) { ); } -export function ModalContent({ children, className, ...rest }) { +interface ModalContentProps { + children: ReactNode; + className?: string; +} + +export function ModalContent({ + children, + className, + ...rest +}: ModalContentProps) { return (
    {children} @@ -66,7 +116,10 @@ export function ModalContent({ children, className, ...rest }) { ); } -export function useModalTriggerState() { +export function useModalTriggerState(): { + modalState: OverlayTriggerState; + modalProps: { isOpen: boolean; onClose: () => void }; +} { const modalState = useOverlayTriggerState({}); const modalProps = useMemo( () => ({ isOpen: modalState.isOpen, onClose: modalState.close }), @@ -75,7 +128,10 @@ export function useModalTriggerState() { return { modalState, modalProps }; } -export function useToggleModalButton(modalState, ref) { +export function useToggleModalButton( + modalState: OverlayTriggerState, + ref: React.RefObject +): ButtonAria> { return useButton( { onPress: () => modalState.toggle(), @@ -84,7 +140,10 @@ export function useToggleModalButton(modalState, ref) { ); } -export function useOpenModalButton(modalState, ref) { +export function useOpenModalButton( + modalState: OverlayTriggerState, + ref: React.RefObject +): ButtonAria> { return useButton( { onPress: () => modalState.open(), @@ -93,7 +152,10 @@ export function useOpenModalButton(modalState, ref) { ); } -export function useCloseModalButton(modalState, ref) { +export function useCloseModalButton( + modalState: OverlayTriggerState, + ref: React.RefObject +): ButtonAria> { return useButton( { onPress: () => modalState.close(), @@ -102,8 +164,12 @@ export function useCloseModalButton(modalState, ref) { ); } -export function ModalTrigger({ children }) { - const { modalState, modalProps } = useModalState(); +interface ModalTriggerProps { + children: ReactNode; +} + +export function ModalTrigger({ children }: ModalTriggerProps) { + const { modalState, modalProps } = useModalTriggerState(); const buttonRef = useRef(); const { buttonProps } = useToggleModalButton(modalState, buttonRef); From 2537088099624738e15f6fd1518d08c5836d4f36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Sat, 30 Jul 2022 10:06:28 +0200 Subject: [PATCH 21/50] Accompanying changes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/Avatar.tsx | 4 ++-- src/button/Button.tsx | 27 +++++++++++++-------------- src/button/LinkButton.tsx | 10 +++++----- src/profile/ProfileModal.tsx | 2 +- src/room/GridLayoutMenu.jsx | 3 +-- src/room/OverflowMenu.jsx | 3 +-- src/room/PTTCallView.tsx | 3 ++- src/settings/submit-rageshake.ts | 6 +++--- 8 files changed, 28 insertions(+), 30 deletions(-) diff --git a/src/Avatar.tsx b/src/Avatar.tsx index 9a9bae8..07c7f87 100644 --- a/src/Avatar.tsx +++ b/src/Avatar.tsx @@ -48,10 +48,10 @@ const resolveAvatarSrc = (client: MatrixClient, src: string, size: number) => interface Props extends React.HTMLAttributes { bgKey?: string; - src: string; + src?: string; fallback: string; size?: Size | number; - className: string; + className?: string; style?: CSSProperties; } diff --git a/src/button/Button.tsx b/src/button/Button.tsx index bd7a0dd..e993661 100644 --- a/src/button/Button.tsx +++ b/src/button/Button.tsx @@ -139,11 +139,12 @@ export function MicButton({ [index: string]: unknown; }) { return ( - + (muted ? "Unmute microphone" : "Mute microphone")} + > - {() => (muted ? "Unmute microphone" : "Mute microphone")} ); } @@ -156,11 +157,12 @@ export function VideoButton({ [index: string]: unknown; }) { return ( - + (muted ? "Turn on camera" : "Turn off camera")} + > - {() => (muted ? "Turn on camera" : "Turn off camera")} ); } @@ -175,11 +177,12 @@ export function ScreenshareButton({ [index: string]: unknown; }) { return ( - + (enabled ? "Stop sharing screen" : "Share screen")} + > - {() => (enabled ? "Stop sharing screen" : "Share screen")} ); } @@ -192,7 +195,7 @@ export function HangupButton({ [index: string]: unknown; }) { return ( - + "Leave"}> - {() => "Leave"} ); } @@ -213,11 +215,10 @@ export function SettingsButton({ [index: string]: unknown; }) { return ( - + "Settings"}> - {() => "Settings"} ); } @@ -230,22 +231,20 @@ export function InviteButton({ [index: string]: unknown; }) { return ( - + "Invite"}> - {() => "Invite"} ); } export function OptionsButton(props: Omit) { return ( - + "Options"}> - {() => "Options"} ); } diff --git a/src/button/LinkButton.tsx b/src/button/LinkButton.tsx index 0ed0cb1..10d64b4 100644 --- a/src/button/LinkButton.tsx +++ b/src/button/LinkButton.tsx @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -import React from "react"; +import React, { ReactNode } from "react"; import { Link } from "react-router-dom"; import classNames from "classnames"; @@ -25,10 +25,10 @@ import { ButtonSize, } from "./Button"; interface Props { - className: string; - variant: ButtonVariant; - size: ButtonSize; - children: JSX.Element; + className?: string; + variant?: ButtonVariant; + size?: ButtonSize; + children: ReactNode; [index: string]: unknown; } diff --git a/src/profile/ProfileModal.tsx b/src/profile/ProfileModal.tsx index 26b99e9..3b3033a 100644 --- a/src/profile/ProfileModal.tsx +++ b/src/profile/ProfileModal.tsx @@ -26,7 +26,7 @@ import styles from "./ProfileModal.module.css"; interface Props { client: MatrixClient; - onClose: () => {}; + onClose: () => void; [rest: string]: unknown; } export function ProfileModal({ client, ...rest }: Props) { diff --git a/src/room/GridLayoutMenu.jsx b/src/room/GridLayoutMenu.jsx index 02f324b..05d12a4 100644 --- a/src/room/GridLayoutMenu.jsx +++ b/src/room/GridLayoutMenu.jsx @@ -28,11 +28,10 @@ import { Tooltip, TooltipTrigger } from "../Tooltip"; export function GridLayoutMenu({ layout, setLayout }) { return ( - + "Layout Type"}> - {() => "Layout Type"} {(props) => ( diff --git a/src/room/OverflowMenu.jsx b/src/room/OverflowMenu.jsx index c5810f0..69334c7 100644 --- a/src/room/OverflowMenu.jsx +++ b/src/room/OverflowMenu.jsx @@ -61,11 +61,10 @@ export function OverflowMenu({ return ( <> - + "More"} placement="top"> - {() => "More"} {(props) => ( diff --git a/src/room/PTTCallView.tsx b/src/room/PTTCallView.tsx index 04c01d0..aff42ba 100644 --- a/src/room/PTTCallView.tsx +++ b/src/room/PTTCallView.tsx @@ -38,6 +38,7 @@ import { usePTTSounds } from "../sound/usePttSounds"; import { PTTClips } from "../sound/PTTClips"; import { GroupCallInspector } from "./GroupCallInspector"; import { OverflowMenu } from "./OverflowMenu"; +import { Size } from "../Avatar"; function getPromptText( networkWaiting: boolean, @@ -112,7 +113,7 @@ export const PTTCallView: React.FC = ({ const { modalState: feedbackModalState, modalProps: feedbackModalProps } = useModalTriggerState(); const [containerRef, bounds] = useMeasure({ polyfill: ResizeObserver }); - const facepileSize = bounds.width < 800 ? "sm" : "md"; + const facepileSize = bounds.width < 800 ? Size.SM : Size.MD; const showControls = bounds.height > 500; const pttButtonSize = 232; diff --git a/src/settings/submit-rageshake.ts b/src/settings/submit-rageshake.ts index 20b0785..f34b066 100644 --- a/src/settings/submit-rageshake.ts +++ b/src/settings/submit-rageshake.ts @@ -27,10 +27,10 @@ import { useModalTriggerState } from "../Modal"; interface RageShakeSubmitOptions { description: string; - roomId: string; - label: string; + roomId?: string; + label?: string; sendLogs: boolean; - rageshakeRequestId: string; + rageshakeRequestId?: string; } export function useSubmitRageshake(): { From dc3cc33893f192adfe7ce582074fc075ab7dabd9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Sat, 30 Jul 2022 10:33:44 +0200 Subject: [PATCH 22/50] Fix exiting dialog MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/Modal.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Modal.tsx b/src/Modal.tsx index 4333b0c..f259a18 100644 --- a/src/Modal.tsx +++ b/src/Modal.tsx @@ -54,7 +54,10 @@ export function Modal({ ...rest }: ModalProps) { const modalRef = useRef(); - const { overlayProps, underlayProps } = useOverlay(rest, modalRef); + const { overlayProps, underlayProps } = useOverlay( + { ...rest, onClose }, + modalRef + ); usePreventScroll(); const { modalProps } = useModal(); const { dialogProps, titleProps } = useDialog(rest, modalRef); From 4f7724dbaf5a5f3d575458c4678950a468e699b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Sun, 31 Jul 2022 22:07:08 +0200 Subject: [PATCH 23/50] Fix prop order MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/Avatar.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avatar.tsx b/src/Avatar.tsx index 07c7f87..a4aa826 100644 --- a/src/Avatar.tsx +++ b/src/Avatar.tsx @@ -49,10 +49,10 @@ const resolveAvatarSrc = (client: MatrixClient, src: string, size: number) => interface Props extends React.HTMLAttributes { bgKey?: string; src?: string; - fallback: string; size?: Size | number; className?: string; style?: CSSProperties; + fallback: string; } export const Avatar: React.FC = ({ From 44315f327b7e6d7c32c8860df5414d0d0bab54c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Sun, 31 Jul 2022 22:09:33 +0200 Subject: [PATCH 24/50] Add missing `extends` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/Header.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Header.tsx b/src/Header.tsx index cde794f..0471526 100644 --- a/src/Header.tsx +++ b/src/Header.tsx @@ -55,7 +55,7 @@ export function LeftNav({ ); } -interface RightNavProps { +interface RightNavProps extends HTMLAttributes { children?: ReactNode; className?: string; hideMobile?: string; From a679bfcd955c852c6362c2390474fc0d447c34aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Sun, 31 Jul 2022 22:11:46 +0200 Subject: [PATCH 25/50] Add missing copyrights MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/SequenceDiagramViewerPage.tsx | 16 ++++++++++++++++ src/Tooltip.tsx | 16 ++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/src/SequenceDiagramViewerPage.tsx b/src/SequenceDiagramViewerPage.tsx index 2b98baa..f06d3ca 100644 --- a/src/SequenceDiagramViewerPage.tsx +++ b/src/SequenceDiagramViewerPage.tsx @@ -1,3 +1,19 @@ +/* +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 React, { useCallback, useState } from "react"; import { SequenceDiagramViewer } from "./room/GroupCallInspector"; diff --git a/src/Tooltip.tsx b/src/Tooltip.tsx index 195443d..14905d9 100644 --- a/src/Tooltip.tsx +++ b/src/Tooltip.tsx @@ -1,3 +1,19 @@ +/* +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 React, { ForwardedRef, forwardRef, From 44ce76bcb10b1c5117d5f95f48234813fb610df9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Mon, 1 Aug 2022 18:56:59 +0200 Subject: [PATCH 26/50] Get volume button inline with design MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/button/Button.tsx | 8 ++++---- src/video-grid/VideoTile.jsx | 20 ++++++++++++++++---- src/video-grid/VideoTile.module.css | 17 ++++++++++++----- 3 files changed, 32 insertions(+), 13 deletions(-) diff --git a/src/button/Button.tsx b/src/button/Button.tsx index e993661..a119364 100644 --- a/src/button/Button.tsx +++ b/src/button/Button.tsx @@ -30,7 +30,7 @@ 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 { TooltipTrigger } from "../Tooltip"; -import { ReactComponent as OverflowIcon } from "../icons/Overflow.svg"; +import { ReactComponent as AudioIcon } from "../icons/Audio.svg"; export type ButtonVariant = | "default" @@ -239,11 +239,11 @@ export function InviteButton({ ); } -export function OptionsButton(props: Omit) { +export function AudioButton(props: Omit) { return ( "Options"}> - ); diff --git a/src/video-grid/VideoTile.jsx b/src/video-grid/VideoTile.jsx index 206f8a4..286d301 100644 --- a/src/video-grid/VideoTile.jsx +++ b/src/video-grid/VideoTile.jsx @@ -20,7 +20,7 @@ import classNames from "classnames"; import styles from "./VideoTile.module.css"; import { ReactComponent as MicMutedIcon } from "../icons/MicMuted.svg"; import { ReactComponent as VideoMutedIcon } from "../icons/VideoMuted.svg"; -import { OptionsButton } from "../button/Button"; +import { AudioButton } from "../button/Button"; export const VideoTile = forwardRef( ( @@ -65,7 +65,13 @@ export const VideoTile = forwardRef(
    ) : ( (showName || audioMuted || (videoMuted && !noVideo)) && ( -
    +
    {audioMuted && !(videoMuted && !noVideo) && } {videoMuted && !noVideo && } {showName && {name}} @@ -73,8 +79,14 @@ export const VideoTile = forwardRef( ) )} {showOptions && ( -
    - +
    +
    )}
    diff --git a/src/room/useGroupCall.ts b/src/room/useGroupCall.ts index 1e01ad5..7f3232a 100644 --- a/src/room/useGroupCall.ts +++ b/src/room/useGroupCall.ts @@ -29,7 +29,7 @@ import { RoomMember } from "matrix-js-sdk/src/models/room-member"; import { usePageUnload } from "./usePageUnload"; -export interface UseGroupCallType { +export interface UseGroupCallReturnType { state: GroupCallState; calls: MatrixCall[]; localCallFeed: CallFeed; @@ -72,7 +72,7 @@ interface State { hasLocalParticipant: boolean; } -export function useGroupCall(groupCall: GroupCall): UseGroupCallType { +export function useGroupCall(groupCall: GroupCall): UseGroupCallReturnType { const [ { state, diff --git a/src/room/usePageUnload.js b/src/room/usePageUnload.ts similarity index 93% rename from src/room/usePageUnload.js rename to src/room/usePageUnload.ts index 8fd8220..b49c778 100644 --- a/src/room/usePageUnload.js +++ b/src/room/usePageUnload.ts @@ -32,11 +32,11 @@ function isIOS() { ); } -export function usePageUnload(callback) { +export function usePageUnload(callback: () => void) { useEffect(() => { - let pageVisibilityTimeout; + let pageVisibilityTimeout: number; - function onBeforeUnload(event) { + function onBeforeUnload(event: PageTransitionEvent) { if (event.type === "visibilitychange") { if (document.visibilityState === "visible") { clearTimeout(pageVisibilityTimeout); diff --git a/src/room/useSentryGroupCallHandler.js b/src/room/useSentryGroupCallHandler.ts similarity index 64% rename from src/room/useSentryGroupCallHandler.js rename to src/room/useSentryGroupCallHandler.ts index 4520384..4afb7f7 100644 --- a/src/room/useSentryGroupCallHandler.js +++ b/src/room/useSentryGroupCallHandler.ts @@ -16,28 +16,30 @@ limitations under the License. import { useEffect } from "react"; import * as Sentry from "@sentry/react"; +import { GroupCall, GroupCallEvent } from "matrix-js-sdk/src/webrtc/groupCall"; +import { CallEvent, MatrixCall } from "matrix-js-sdk/src/webrtc/call"; -export function useSentryGroupCallHandler(groupCall) { +export function useSentryGroupCallHandler(groupCall: GroupCall) { useEffect(() => { - function onHangup(call) { + function onHangup(call: MatrixCall) { if (call.hangupReason === "ice_failed") { Sentry.captureException(new Error("Call hangup due to ICE failure.")); } } - function onError(error) { + function onError(error: Error) { Sentry.captureException(error); } if (groupCall) { - groupCall.on("hangup", onHangup); - groupCall.on("error", onError); + groupCall.on(CallEvent.Hangup, onHangup); + groupCall.on(GroupCallEvent.Error, onError); } return () => { if (groupCall) { - groupCall.removeListener("hangup", onHangup); - groupCall.removeListener("error", onError); + groupCall.removeListener(CallEvent.Hangup, onHangup); + groupCall.removeListener(GroupCallEvent.Error, onError); } }; }, [groupCall]); diff --git a/src/settings/SettingsModal.tsx b/src/settings/SettingsModal.tsx index 7613f0d..53764e3 100644 --- a/src/settings/SettingsModal.tsx +++ b/src/settings/SettingsModal.tsx @@ -32,9 +32,8 @@ import { useDownloadDebugLog } from "./submit-rageshake"; import { Body } from "../typography/Typography"; interface Props { - setShowInspector: boolean; - showInspector: boolean; - [rest: string]: unknown; + isOpen: boolean; + onClose: () => void; } export const SettingsModal = (props: Props) => { diff --git a/src/settings/submit-rageshake.ts b/src/settings/submit-rageshake.ts index f34b066..8d3ce6d 100644 --- a/src/settings/submit-rageshake.ts +++ b/src/settings/submit-rageshake.ts @@ -26,11 +26,11 @@ import { InspectorContext } from "../room/GroupCallInspector"; import { useModalTriggerState } from "../Modal"; interface RageShakeSubmitOptions { - description: string; - roomId?: string; - label?: string; sendLogs: boolean; rageshakeRequestId?: string; + description?: string; + roomId?: string; + label?: string; } export function useSubmitRageshake(): { @@ -40,7 +40,7 @@ export function useSubmitRageshake(): { error: Error; } { const client: MatrixClient = useClient().client; - const [{ json }] = useContext(InspectorContext); + const json = useContext(InspectorContext); const [{ sending, sent, error }, setState] = useState({ sending: false, @@ -274,7 +274,7 @@ export function useSubmitRageshake(): { } export function useDownloadDebugLog(): () => void { - const [{ json }] = useContext(InspectorContext); + const json = useContext(InspectorContext); const downloadDebugLog = useCallback(() => { const blob = new Blob([JSON.stringify(json)], { type: "application/json" }); diff --git a/src/settings/useMediaHandler.tsx b/src/settings/useMediaHandler.tsx index bd2acd3..05e8795 100644 --- a/src/settings/useMediaHandler.tsx +++ b/src/settings/useMediaHandler.tsx @@ -24,6 +24,7 @@ import React, { useMemo, useContext, createContext, + ReactNode, } from "react"; export interface MediaHandlerContextInterface { @@ -73,7 +74,7 @@ function updateMediaPreferences(newPreferences: MediaPreferences): void { } interface Props { client: MatrixClient; - children: JSX.Element[]; + children: ReactNode; } export function MediaHandlerProvider({ client, children }: Props): JSX.Element { const [ diff --git a/src/video-grid/useMediaStream.ts b/src/video-grid/useMediaStream.ts index d3df237..f9c5e47 100644 --- a/src/video-grid/useMediaStream.ts +++ b/src/video-grid/useMediaStream.ts @@ -34,7 +34,7 @@ export const useMediaStream = ( stream: MediaStream, audioOutputDevice: string, mute = false, - localVolume: number + localVolume?: number ): RefObject => { const mediaRef = useRef(); @@ -196,7 +196,7 @@ export const useSpatialMediaStream = ( audioContext: AudioContext, audioDestination: AudioNode, mute = false, - localVolume: number + localVolume?: number ): [RefObject, RefObject] => { const tileRef = useRef(); const [spatialAudio] = useSpatialAudio(); diff --git a/tsconfig.json b/tsconfig.json index 437a94f..bbbe0f7 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -8,14 +8,7 @@ "noImplicitAny": false, "noUnusedLocals": true, "jsx": "preserve", - "lib": [ - "es2020", - "dom", - "dom.iterable" - ], + "lib": ["es2020", "dom", "dom.iterable"] }, - "include": [ - "./src/**/*.ts", - "./src/**/*.tsx", - ], + "include": ["./src/**/*.ts", "./src/**/*.tsx"] } From 130073689db70bd1d3dec79c8acdf9cab1dd4858 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Tue, 2 Aug 2022 12:38:09 +0200 Subject: [PATCH 32/50] Fix button tooltip MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/button/Button.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/button/Button.tsx b/src/button/Button.tsx index a119364..abd8fe2 100644 --- a/src/button/Button.tsx +++ b/src/button/Button.tsx @@ -242,7 +242,7 @@ export function InviteButton({ export function AudioButton(props: Omit) { return ( "Options"}> - From a5d5f75f52c91e58363d6ad0f8bac4ca9392bc20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Tue, 2 Aug 2022 12:43:27 +0200 Subject: [PATCH 33/50] Add hover effect back MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/video-grid/VideoTile.module.css | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/video-grid/VideoTile.module.css b/src/video-grid/VideoTile.module.css index 0a484d2..10f4d94 100644 --- a/src/video-grid/VideoTile.module.css +++ b/src/video-grid/VideoTile.module.css @@ -71,12 +71,6 @@ display: initial; } -.button button:hover svg * { - fill: var( - --primary-content - ) !important; /* To override .iconButton:not(.stroke):hover svg */ -} - .button button svg { height: 20px; width: 20px; From 2e57eaad1dc3584456dfbde49435af93f689fb5a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Tue, 2 Aug 2022 12:45:21 +0200 Subject: [PATCH 34/50] Fix var name MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/index.css | 2 +- src/video-grid/VideoTile.module.css | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/index.css b/src/index.css index 3eb297c..6e9a912 100644 --- a/src/index.css +++ b/src/index.css @@ -38,7 +38,7 @@ limitations under the License. --quinary-content: #394049; --system: #21262c; --background: #15191e; - --backgrouund-85: rgba(23, 25, 28, 0.85); + --background-85: rgba(23, 25, 28, 0.85); --bgColor3: #444; /* This isn't found anywhere in the designs or Compound */ } diff --git a/src/video-grid/VideoTile.module.css b/src/video-grid/VideoTile.module.css index 10f4d94..504b39f 100644 --- a/src/video-grid/VideoTile.module.css +++ b/src/video-grid/VideoTile.module.css @@ -60,7 +60,7 @@ } .infoBubbleIcon { - background-color: var(--backgrouund-85); + background-color: var(--background-85); } .button { From aef4fd39b930c240686d9b714f061b96a81fabc0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Tue, 2 Aug 2022 13:33:09 +0200 Subject: [PATCH 35/50] Add env var for `background-85` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- .env.example | 1 + src/main.tsx | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/.env.example b/.env.example index 2f62d61..7359d2c 100644 --- a/.env.example +++ b/.env.example @@ -27,3 +27,4 @@ # VITE_THEME_QUINARY_CONTENT=#394049 # VITE_THEME_SYSTEM=#21262c # VITE_THEME_BACKGROUND=#15191e +# VITE_THEME_BACKGROUND_85=#15191ed9 diff --git a/src/main.tsx b/src/main.tsx index 2330bed..a1739c7 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -84,6 +84,10 @@ if (import.meta.env.VITE_CUSTOM_THEME) { "--background", import.meta.env.VITE_THEME_BACKGROUND as string ); + style.setProperty( + "--background-85", + import.meta.env.VITE_THEME_BACKGROUND_85 as string + ); } const history = createBrowserHistory(); From 005622800dfc01c54f951b211a0cc890065ceeb9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Tue, 2 Aug 2022 13:50:47 +0200 Subject: [PATCH 36/50] Fix tooltip (again) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/button/Button.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/button/Button.tsx b/src/button/Button.tsx index abd8fe2..f1ba94b 100644 --- a/src/button/Button.tsx +++ b/src/button/Button.tsx @@ -241,8 +241,8 @@ export function InviteButton({ export function AudioButton(props: Omit) { return ( - "Options"}> - From cd885e3b3af2b80391fcc86eb859c98b5b02ea66 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Tue, 2 Aug 2022 13:51:05 +0200 Subject: [PATCH 37/50] Add hover gradient MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/video-grid/VideoTile.module.css | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/video-grid/VideoTile.module.css b/src/video-grid/VideoTile.module.css index 504b39f..1ba6ec1 100644 --- a/src/video-grid/VideoTile.module.css +++ b/src/video-grid/VideoTile.module.css @@ -67,6 +67,19 @@ display: none; } +.videoTile:not(.isLocal):hover::after { + content: ""; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: calc( + 16px + 20px + 16px + ); /* top padding + button size + buttom padding */ + background-image: linear-gradient(to bottom, var(--background), transparent); + opacity: 0.8; +} + .videoTile:hover .button { display: initial; } From e824b3cfe2afec3cfca9ac46cb82ff39ecf23fec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Tue, 2 Aug 2022 14:28:52 +0200 Subject: [PATCH 38/50] Update icons MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/icons/Audio.svg | 8 ++++---- src/icons/AudioLow.svg | 4 ++++ src/icons/AudioMuted.svg | 3 +++ 3 files changed, 11 insertions(+), 4 deletions(-) create mode 100644 src/icons/AudioLow.svg create mode 100644 src/icons/AudioMuted.svg diff --git a/src/icons/Audio.svg b/src/icons/Audio.svg index f541ebc..0be0da9 100644 --- a/src/icons/Audio.svg +++ b/src/icons/Audio.svg @@ -1,5 +1,5 @@ - - - - + + + + diff --git a/src/icons/AudioLow.svg b/src/icons/AudioLow.svg new file mode 100644 index 0000000..bfa80ce --- /dev/null +++ b/src/icons/AudioLow.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/icons/AudioMuted.svg b/src/icons/AudioMuted.svg new file mode 100644 index 0000000..372ece5 --- /dev/null +++ b/src/icons/AudioMuted.svg @@ -0,0 +1,3 @@ + + + From 7d5fb5f041f588996ff0fd910ea5221a2efb418a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Tue, 2 Aug 2022 14:29:32 +0200 Subject: [PATCH 39/50] Add `VolumeIcon` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/button/VolumeIcon.tsx | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 src/button/VolumeIcon.tsx diff --git a/src/button/VolumeIcon.tsx b/src/button/VolumeIcon.tsx new file mode 100644 index 0000000..02e1ff2 --- /dev/null +++ b/src/button/VolumeIcon.tsx @@ -0,0 +1,35 @@ +/* +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 React 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 { + /** + * Number between 0 and 1 + */ + volume: number; +} + +export function VolumeIcon({ volume }: Props) { + if (volume <= 0) return ; + if (volume <= 0.75) return ; + return ; +} From a150619d08fc6eb2a97c07bbdf9f2fd460c0c86c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Tue, 2 Aug 2022 14:30:12 +0200 Subject: [PATCH 40/50] Make the button icon change MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/button/Button.tsx | 15 +++++++++++---- src/video-grid/VideoTile.jsx | 3 ++- src/video-grid/VideoTileContainer.jsx | 1 + 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/src/button/Button.tsx b/src/button/Button.tsx index f1ba94b..5ade524 100644 --- a/src/button/Button.tsx +++ b/src/button/Button.tsx @@ -30,7 +30,7 @@ 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 { TooltipTrigger } from "../Tooltip"; -import { ReactComponent as AudioIcon } from "../icons/Audio.svg"; +import { VolumeIcon } from "./VolumeIcon"; export type ButtonVariant = | "default" @@ -239,11 +239,18 @@ export function InviteButton({ ); } -export function AudioButton(props: Omit) { +interface AudioButtonProps extends Omit { + /** + * A number between 0 and 1 + */ + volume: number; +} + +export function AudioButton({ volume, ...rest }: AudioButtonProps) { return ( "Local volume"}> - ); diff --git a/src/video-grid/VideoTile.jsx b/src/video-grid/VideoTile.jsx index 286d301..f6103e1 100644 --- a/src/video-grid/VideoTile.jsx +++ b/src/video-grid/VideoTile.jsx @@ -38,6 +38,7 @@ export const VideoTile = forwardRef( mediaRef, onOptionsPress, showOptions, + localVolume, ...rest }, ref @@ -86,7 +87,7 @@ export const VideoTile = forwardRef( styles.audioButton )} > - +
    )}