{children}
@@ -66,7 +119,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 +131,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 +143,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 +155,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 +167,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);
diff --git a/src/SequenceDiagramViewerPage.jsx b/src/SequenceDiagramViewerPage.tsx
similarity index 57%
rename from src/SequenceDiagramViewerPage.jsx
rename to src/SequenceDiagramViewerPage.tsx
index 3752dc29..f06d3cae 100644
--- a/src/SequenceDiagramViewerPage.jsx
+++ b/src/SequenceDiagramViewerPage.tsx
@@ -1,13 +1,36 @@
+/*
+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";
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) => {
diff --git a/src/Tooltip.jsx b/src/Tooltip.jsx
deleted file mode 100644
index 9f613083..00000000
--- 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 00000000..14905d9b
--- /dev/null
+++ b/src/Tooltip.tsx
@@ -0,0 +1,114 @@
+/*
+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,
+ 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()}
+
+
+ )}
+
+ );
+ }
+);
diff --git a/src/UserMenu.jsx b/src/UserMenu.tsx
similarity index 83%
rename from src/UserMenu.jsx
rename to src/UserMenu.tsx
index 6363948c..83da187a 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">
{isAuthenticated && (!isPasswordlessUser || avatarUrl) ? (
)}
- {() => "Profile"}
{(props) => (
{items.map(({ key, icon: Icon, label }) => (
- -
+
-
{label}
diff --git a/src/UserMenuContainer.jsx b/src/UserMenuContainer.tsx
similarity index 90%
rename from src/UserMenuContainer.jsx
rename to src/UserMenuContainer.tsx
index 18d52dbf..85cb1d7d 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();
diff --git a/src/button/Button.tsx b/src/button/Button.tsx
index bd7a0dd4..e9936614 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 ? : }
- {() => (muted ? "Unmute microphone" : "Mute microphone")}
);
}
@@ -156,11 +157,12 @@ export function VideoButton({
[index: string]: unknown;
}) {
return (
-
+ (muted ? "Turn on camera" : "Turn off camera")}
+ >
{muted ? : }
- {() => (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 0ed0cb14..10d64b41 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 26b99e97..3b3033ae 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 02f324bf..05d12a47 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 === "spotlight" ? : }
- {() => "Layout Type"}
{(props) => (
diff --git a/src/room/OverflowMenu.jsx b/src/room/OverflowMenu.jsx
index c5810f0e..69334c7a 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 04c01d0f..aff42ba2 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 20b0785e..f34b0668 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(): {
diff --git a/src/useLocationNavigation.js b/src/useLocationNavigation.ts
similarity index 81%
rename from src/useLocationNavigation.js
rename to src/useLocationNavigation.ts
index 2ae52341..81ecdbcd 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;
});
}
diff --git a/src/usePageFocusStyle.js b/src/usePageFocusStyle.ts
similarity index 92%
rename from src/usePageFocusStyle.js
rename to src/usePageFocusStyle.ts
index c7ec75f3..542cffdb 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(() => {
diff --git a/src/usePageTitle.js b/src/usePageTitle.js
deleted file mode 100644
index e7b5d9ff..00000000
--- 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 00000000..1516eccb
--- /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]);
+}