diff --git a/.eslintrc.cjs b/.eslintrc.cjs
index ac9f913f..7f0dd576 100644
--- a/.eslintrc.cjs
+++ b/.eslintrc.cjs
@@ -1,13 +1,31 @@
+const COPYRIGHT_HEADER = `/*
+Copyright %%CURRENT_YEAR%% 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.
+*/
+
+`;
+
module.exports = {
plugins: ["matrix-org"],
extends: [
- "prettier",
"plugin:matrix-org/react",
"plugin:matrix-org/a11y",
"plugin:matrix-org/typescript",
+ "prettier",
],
parserOptions: {
- ecmaVersion: 2018,
+ ecmaVersion: "latest",
sourceType: "module",
project: ["./tsconfig.json"],
},
@@ -15,29 +33,13 @@ module.exports = {
browser: true,
node: true,
},
- parserOptions: {
- ecmaVersion: "latest",
- sourceType: "module",
- },
rules: {
- "jsx-a11y/media-has-caption": ["off"],
+ "matrix-org/require-copyright-header": ["error", COPYRIGHT_HEADER],
+ "jsx-a11y/media-has-caption": "off",
+ "deprecate/import": "off", // Disabled because it crashes the linter
+ // We should use the js-sdk logger, never console directly.
+ "no-console": ["error"],
},
- overrides: [
- {
- files: ["src/**/*.{ts,tsx}", "test/**/*.{ts,tsx}"],
- extends: [
- "plugin:matrix-org/typescript",
- "plugin:matrix-org/react",
- "prettier",
- ],
- rules: {
- // We're aiming to convert this code to strict mode
- "@typescript-eslint/no-non-null-assertion": "off",
- // We should use the js-sdk logger, never console directly.
- "no-console": ["error"],
- },
- },
- ],
settings: {
react: {
version: "detect",
diff --git a/.storybook/main.js b/.storybook/main.js
index 682489d9..9d03e947 100644
--- a/.storybook/main.js
+++ b/.storybook/main.js
@@ -14,7 +14,7 @@ module.exports = {
Array.isArray(item) &&
item.length > 0 &&
item[0].name === "vite-plugin-mdx"
- )
+ ),
);
config.plugins.push(svgrPlugin());
config.resolve = config.resolve || {};
diff --git a/package.json b/package.json
index e281939a..43414002 100644
--- a/package.json
+++ b/package.json
@@ -105,19 +105,22 @@
"eslint": "^8.14.0",
"eslint-config-google": "^0.14.0",
"eslint-config-prettier": "^9.0.0",
+ "eslint-plugin-deprecate": "^0.8.2",
"eslint-plugin-import": "^2.26.0",
"eslint-plugin-jsx-a11y": "^6.5.1",
- "eslint-plugin-matrix-org": "^0.4.0",
+ "eslint-plugin-matrix-org": "^1.2.1",
"eslint-plugin-react": "^7.29.4",
"eslint-plugin-react-hooks": "^4.5.0",
+ "eslint-plugin-unicorn": "^48.0.1",
"i18next-parser": "^8.0.0",
"identity-obj-proxy": "^3.0.0",
"jest": "^29.2.2",
"jest-environment-jsdom": "^29.3.1",
"jest-mock": "^29.5.0",
- "prettier": "^2.6.2",
+ "prettier": "^3.0.0",
"sass": "^1.42.1",
"typescript": "^5.1.6",
+ "typescript-eslint-language-service": "^5.0.5",
"vite": "^4.2.0",
"vite-plugin-html-template": "^1.1.0",
"vite-plugin-svgr": "^4.0.0"
diff --git a/public/index.html b/public/index.html
index 25987410..93dd8973 100644
--- a/public/index.html
+++ b/public/index.html
@@ -1,4 +1,4 @@
-
+
diff --git a/public/locales/pl/app.json b/public/locales/pl/app.json
index a55793f7..c3b31d36 100644
--- a/public/locales/pl/app.json
+++ b/public/locales/pl/app.json
@@ -114,5 +114,10 @@
"Call not found": "Nie znaleziono połączenia",
"Calls are now end-to-end encrypted and need to be created from the home page. This helps make sure everyone's using the same encryption key.": "Połączenia są teraz szyfrowane end-to-end i muszą zostać utworzone ze strony głównej. Pomaga to upewnić się, że każdy korzysta z tego samego klucza szyfrującego.",
"You": "Ty",
- "Your web browser does not support media end-to-end encryption. Supported Browsers are Chrome, Safari, Firefox >=117": "Twoja przeglądarka nie wspiera szyfrowania end-to-end. Wspierane przeglądarki to Chrome, Safari, Firefox >=117"
+ "Your web browser does not support media end-to-end encryption. Supported Browsers are Chrome, Safari, Firefox >=117": "Twoja przeglądarka nie wspiera szyfrowania end-to-end. Wspierane przeglądarki to Chrome, Safari, Firefox >=117",
+ "Invite": "Zaproś",
+ "Link copied to clipboard": "Skopiowano link do schowka",
+ "Participants": "Uczestnicy",
+ "Copy link": "Kopiuj link",
+ "Invite to this call": "Zaproś do połączenia"
}
diff --git a/src/App.tsx b/src/App.tsx
index 7bfd5a0c..4d6fa6a6 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
-import { Suspense, useEffect, useState } from "react";
+import { FC, Suspense, useEffect, useState } from "react";
import {
BrowserRouter as Router,
Switch,
@@ -41,7 +41,7 @@ interface BackgroundProviderProps {
children: JSX.Element;
}
-const BackgroundProvider = ({ children }: BackgroundProviderProps) => {
+const BackgroundProvider: FC = ({ children }) => {
const { pathname } = useLocation();
useEffect(() => {
@@ -61,7 +61,7 @@ interface AppProps {
history: History;
}
-export default function App({ history }: AppProps) {
+export const App: FC = ({ history }) => {
const [loaded, setLoaded] = useState(false);
useEffect(() => {
@@ -109,4 +109,4 @@ export default function App({ history }: AppProps) {
);
-}
+};
diff --git a/src/Avatar.tsx b/src/Avatar.tsx
index e23bd909..06583445 100644
--- a/src/Avatar.tsx
+++ b/src/Avatar.tsx
@@ -58,7 +58,7 @@ export const Avatar: FC = ({
Object.values(Size).includes(size as Size)
? sizes.get(size as Size)
: (size as number),
- [size]
+ [size],
);
const resolvedSrc = useMemo(() => {
diff --git a/src/Banner.tsx b/src/Banner.tsx
index fcc68a3b..87ce8a96 100644
--- a/src/Banner.tsx
+++ b/src/Banner.tsx
@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
-import { ReactNode } from "react";
+import { FC, ReactNode } from "react";
import styles from "./Banner.module.css";
@@ -22,6 +22,6 @@ interface Props {
children: ReactNode;
}
-export const Banner = ({ children }: Props) => {
+export const Banner: FC = ({ children }) => {
return {children}
;
};
diff --git a/src/ClientContext.tsx b/src/ClientContext.tsx
index f7ceea9f..9e47a624 100644
--- a/src/ClientContext.tsx
+++ b/src/ClientContext.tsx
@@ -82,7 +82,8 @@ export type SetClientParams = {
const ClientContext = createContext(undefined);
-export const useClientState = () => useContext(ClientContext);
+export const useClientState = (): ClientState | undefined =>
+ useContext(ClientContext);
export function useClient(): {
client?: MatrixClient;
@@ -189,7 +190,7 @@ export const ClientProvider: FC = ({ children }) => {
user: session.user_id,
password: session.tempPassword,
},
- password
+ password,
);
saveSession({ ...session, passwordlessUser: false });
@@ -199,7 +200,7 @@ export const ClientProvider: FC = ({ children }) => {
passwordlessUser: false,
});
},
- [initClientState?.client]
+ [initClientState?.client],
);
const setClient = useCallback(
@@ -221,7 +222,7 @@ export const ClientProvider: FC = ({ children }) => {
setInitClientState(null);
}
},
- [initClientState?.client]
+ [initClientState?.client],
);
const logout = useCallback(async () => {
@@ -249,7 +250,7 @@ export const ClientProvider: FC = ({ children }) => {
}, []);
const [alreadyOpenedErr, setAlreadyOpenedErr] = useState(
- undefined
+ undefined,
);
useEventTarget(
loadChannel,
@@ -257,9 +258,9 @@ export const ClientProvider: FC = ({ children }) => {
useCallback(() => {
initClientState?.client.stopClient();
setAlreadyOpenedErr(
- translatedError("This application has been opened in another tab.", t)
+ translatedError("This application has been opened in another tab.", t),
);
- }, [initClientState?.client, setAlreadyOpenedErr, t])
+ }, [initClientState?.client, setAlreadyOpenedErr, t]),
);
const [isDisconnected, setIsDisconnected] = useState(false);
@@ -300,7 +301,7 @@ export const ClientProvider: FC = ({ children }) => {
(state: SyncState, _old: SyncState | null, data?: ISyncStateData) => {
setIsDisconnected(clientIsDisconnected(state, data));
},
- []
+ [],
);
useEffect(() => {
@@ -386,7 +387,7 @@ async function loadClient(): Promise {
logger.warn(
"The previous session was lost, and we couldn't log it out, " +
err +
- "either"
+ "either",
);
}
}
@@ -408,8 +409,8 @@ export interface Session {
tempPassword?: string;
}
-const clearSession = () => localStorage.removeItem("matrix-auth-store");
-const saveSession = (s: Session) =>
+const clearSession = (): void => localStorage.removeItem("matrix-auth-store");
+const saveSession = (s: Session): void =>
localStorage.setItem("matrix-auth-store", JSON.stringify(s));
const loadSession = (): Session | undefined => {
const data = localStorage.getItem("matrix-auth-store");
@@ -422,5 +423,6 @@ const loadSession = (): Session | undefined => {
const clientIsDisconnected = (
syncState: SyncState,
- syncData?: ISyncStateData
-) => syncState === "ERROR" && syncData?.error?.name === "ConnectionError";
+ syncData?: ISyncStateData,
+): boolean =>
+ syncState === "ERROR" && syncData?.error?.name === "ConnectionError";
diff --git a/src/DisconnectedBanner.tsx b/src/DisconnectedBanner.tsx
index 6ec5d5ac..06369cc5 100644
--- a/src/DisconnectedBanner.tsx
+++ b/src/DisconnectedBanner.tsx
@@ -15,22 +15,22 @@ limitations under the License.
*/
import classNames from "classnames";
-import { HTMLAttributes, ReactNode } from "react";
+import { FC, HTMLAttributes, ReactNode } from "react";
import { useTranslation } from "react-i18next";
import styles from "./DisconnectedBanner.module.css";
import { ValidClientState, useClientState } from "./ClientContext";
-interface DisconnectedBannerProps extends HTMLAttributes {
+interface Props extends HTMLAttributes {
children?: ReactNode;
className?: string;
}
-export function DisconnectedBanner({
+export const DisconnectedBanner: FC = ({
children,
className,
...rest
-}: DisconnectedBannerProps) {
+}) => {
const { t } = useTranslation();
const clientState = useClientState();
let shouldShowBanner = false;
@@ -50,4 +50,4 @@ export function DisconnectedBanner({
)}
>
);
-}
+};
diff --git a/src/E2EEBanner.tsx b/src/E2EEBanner.tsx
index c4223b2d..8ce30482 100644
--- a/src/E2EEBanner.tsx
+++ b/src/E2EEBanner.tsx
@@ -15,13 +15,14 @@ limitations under the License.
*/
import { Trans } from "react-i18next";
+import { FC } from "react";
import { Banner } from "./Banner";
import styles from "./E2EEBanner.module.css";
import LockOffIcon from "./icons/LockOff.svg?react";
import { useEnableE2EE } from "./settings/useSetting";
-export const E2EEBanner = () => {
+export const E2EEBanner: FC = () => {
const [e2eeEnabled] = useEnableE2EE();
if (e2eeEnabled) return null;
diff --git a/src/FullScreenView.tsx b/src/FullScreenView.tsx
index 5ce2694c..04791a73 100644
--- a/src/FullScreenView.tsx
+++ b/src/FullScreenView.tsx
@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
-import { ReactNode, useCallback, useEffect } from "react";
+import { FC, ReactNode, useCallback, useEffect } from "react";
import { useLocation } from "react-router-dom";
import classNames from "classnames";
import { Trans, useTranslation } from "react-i18next";
@@ -33,7 +33,10 @@ interface FullScreenViewProps {
children: ReactNode;
}
-export function FullScreenView({ className, children }: FullScreenViewProps) {
+export const FullScreenView: FC = ({
+ className,
+ children,
+}) => {
return (
@@ -47,13 +50,13 @@ export function FullScreenView({ className, children }: FullScreenViewProps) {
);
-}
+};
interface ErrorViewProps {
error: Error;
}
-export function ErrorView({ error }: ErrorViewProps) {
+export const ErrorView: FC = ({ error }) => {
const location = useLocation();
const { t } = useTranslation();
@@ -96,9 +99,9 @@ export function ErrorView({ error }: ErrorViewProps) {
)}
);
-}
+};
-export function CrashView() {
+export const CrashView: FC = () => {
const { t } = useTranslation();
const onReload = useCallback(() => {
@@ -127,9 +130,9 @@ export function CrashView() {
);
-}
+};
-export function LoadingView() {
+export const LoadingView: FC = () => {
const { t } = useTranslation();
return (
@@ -137,4 +140,4 @@ export function LoadingView() {
{t("Loading…")}
);
-}
+};
diff --git a/src/Glass.tsx b/src/Glass.tsx
index 5f01d47a..be59172d 100644
--- a/src/Glass.tsx
+++ b/src/Glass.tsx
@@ -48,5 +48,5 @@ export const Glass = forwardRef(
>
{Children.only(children)}
- )
+ ),
);
diff --git a/src/Header.tsx b/src/Header.tsx
index 848319f2..bb170190 100644
--- a/src/Header.tsx
+++ b/src/Header.tsx
@@ -32,13 +32,13 @@ interface HeaderProps extends HTMLAttributes {
className?: string;
}
-export function Header({ children, className, ...rest }: HeaderProps) {
+export const Header: FC = ({ children, className, ...rest }) => {
return (
);
-}
+};
interface LeftNavProps extends HTMLAttributes {
children: ReactNode;
@@ -46,26 +46,26 @@ interface LeftNavProps extends HTMLAttributes {
hideMobile?: boolean;
}
-export function LeftNav({
+export const LeftNav: FC = ({
children,
className,
hideMobile,
...rest
-}: LeftNavProps) {
+}) => {
return (
{children}
);
-}
+};
interface RightNavProps extends HTMLAttributes {
children?: ReactNode;
@@ -73,32 +73,32 @@ interface RightNavProps extends HTMLAttributes {
hideMobile?: boolean;
}
-export function RightNav({
+export const RightNav: FC = ({
children,
className,
hideMobile,
...rest
-}: RightNavProps) {
+}) => {
return (
{children}
);
-}
+};
interface HeaderLogoProps {
className?: string;
}
-export function HeaderLogo({ className }: HeaderLogoProps) {
+export const HeaderLogo: FC = ({ className }) => {
const { t } = useTranslation();
return (
@@ -110,7 +110,7 @@ export function HeaderLogo({ className }: HeaderLogoProps) {
);
-}
+};
interface RoomHeaderInfoProps {
id: string;
diff --git a/src/LazyEventEmitter.ts b/src/LazyEventEmitter.ts
index 121eebf6..6b1730f4 100644
--- a/src/LazyEventEmitter.ts
+++ b/src/LazyEventEmitter.ts
@@ -63,7 +63,7 @@ export class LazyEventEmitter extends EventEmitter {
public addListener(
type: string | symbol,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
- listener: (...args: any[]) => void
+ listener: (...args: any[]) => void,
): this {
return this.on(type, listener);
}
diff --git a/src/ListBox.tsx b/src/ListBox.tsx
index b7ec7c72..ea6e686b 100644
--- a/src/ListBox.tsx
+++ b/src/ListBox.tsx
@@ -14,7 +14,13 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
-import { MutableRefObject, PointerEvent, useCallback, useRef } from "react";
+import {
+ MutableRefObject,
+ PointerEvent,
+ ReactNode,
+ useCallback,
+ useRef,
+} from "react";
import { useListBox, useOption, AriaListBoxOptions } from "@react-aria/listbox";
import { ListState } from "@react-stately/list";
import { Node } from "@react-types/shared";
@@ -35,7 +41,7 @@ export function ListBox({
className,
listBoxRef,
...rest
-}: ListBoxProps) {
+}: ListBoxProps): ReactNode {
const ref = useRef(null);
const listRef = listBoxRef ?? ref;
@@ -66,12 +72,12 @@ interface OptionProps {
item: Node;
}
-function Option({ item, state, className }: OptionProps) {
+function Option({ item, state, className }: OptionProps): ReactNode {
const ref = useRef(null);
const { optionProps, isSelected, isFocused, isDisabled } = useOption(
{ key: item.key },
state,
- ref
+ ref,
);
// Hack: remove the onPointerUp event handler and re-wire it to
@@ -91,7 +97,7 @@ function Option({ item, state, className }: OptionProps) {
// @ts-ignore
origPointerUp(e as unknown as PointerEvent);
},
- [origPointerUp]
+ [origPointerUp],
);
return (
diff --git a/src/Menu.tsx b/src/Menu.tsx
index c1e8e40b..cfc0bc53 100644
--- a/src/Menu.tsx
+++ b/src/Menu.tsx
@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
-import { Key, useRef, useState } from "react";
+import { Key, ReactNode, useRef, useState } from "react";
import { AriaMenuOptions, useMenu, useMenuItem } from "@react-aria/menu";
import { TreeState, useTreeState } from "@react-stately/tree";
import { mergeProps } from "@react-aria/utils";
@@ -37,7 +37,7 @@ export function Menu({
onClose,
label,
...rest
-}: MenuProps) {
+}: MenuProps): ReactNode {
const state = useTreeState({ ...rest, selectionMode: "none" });
const menuRef = useRef(null);
const { menuProps } = useMenu(rest, state, menuRef);
@@ -68,7 +68,12 @@ interface MenuItemProps {
onClose: () => void;
}
-function MenuItem({ item, state, onAction, onClose }: MenuItemProps) {
+function MenuItem({
+ item,
+ state,
+ onAction,
+ onClose,
+}: MenuItemProps): ReactNode {
const ref = useRef(null);
const { menuItemProps } = useMenuItem(
{
@@ -77,7 +82,7 @@ function MenuItem({ item, state, onAction, onClose }: MenuItemProps) {
onClose,
},
state,
- ref
+ ref,
);
const [isFocused, setFocused] = useState(false);
diff --git a/src/Modal.tsx b/src/Modal.tsx
index 83e43eb2..5a6aea94 100644
--- a/src/Modal.tsx
+++ b/src/Modal.tsx
@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
-import { ReactNode, useCallback } from "react";
+import { FC, ReactNode, useCallback } from "react";
import { AriaDialogProps } from "@react-types/dialog";
import { useTranslation } from "react-i18next";
import {
@@ -37,7 +37,7 @@ import { useMediaQuery } from "./useMediaQuery";
import { Glass } from "./Glass";
// TODO: Support tabs
-export interface ModalProps extends AriaDialogProps {
+export interface Props extends AriaDialogProps {
title: string;
children: ReactNode;
className?: string;
@@ -59,14 +59,14 @@ export interface ModalProps extends AriaDialogProps {
* A modal, taking the form of a drawer / bottom sheet on touchscreen devices,
* and a dialog box on desktop.
*/
-export function Modal({
+export const Modal: FC = ({
title,
children,
className,
open,
onDismiss,
...rest
-}: ModalProps) {
+}) => {
const { t } = useTranslation();
// Empirically, Chrome on Android can end up not matching (hover: none), but
// still matching (pointer: coarse) :/
@@ -75,7 +75,7 @@ export function Modal({
(open: boolean) => {
if (!open) onDismiss?.();
},
- [onDismiss]
+ [onDismiss],
);
if (touchscreen) {
@@ -92,7 +92,7 @@ export function Modal({
className,
overlayStyles.overlay,
styles.modal,
- styles.drawer
+ styles.drawer,
)}
{...rest}
>
@@ -124,7 +124,7 @@ export function Modal({
overlayStyles.overlay,
overlayStyles.animate,
styles.modal,
- styles.dialog
+ styles.dialog,
)}
>
@@ -152,4 +152,4 @@ export function Modal({
);
}
-}
+};
diff --git a/src/Toast.tsx b/src/Toast.tsx
index de532cde..ca3e2fab 100644
--- a/src/Toast.tsx
+++ b/src/Toast.tsx
@@ -70,7 +70,7 @@ export const Toast: FC
= ({
(open: boolean) => {
if (!open) onDismiss();
},
- [onDismiss]
+ [onDismiss],
);
useEffect(() => {
@@ -91,7 +91,7 @@ export const Toast: FC = ({
className={classNames(
overlayStyles.overlay,
overlayStyles.animate,
- styles.toast
+ styles.toast,
)}
>
diff --git a/src/Tooltip.tsx b/src/Tooltip.tsx
index da2d52ea..b90c3f86 100644
--- a/src/Tooltip.tsx
+++ b/src/Tooltip.tsx
@@ -43,7 +43,7 @@ interface TooltipProps {
const Tooltip = forwardRef(
(
{ state, className, children, ...rest }: TooltipProps,
- ref: ForwardedRef
+ ref: ForwardedRef,
) => {
const { tooltipProps } = useTooltip(rest, state);
@@ -56,7 +56,7 @@ const Tooltip = forwardRef(
{children}
);
- }
+ },
);
interface TooltipTriggerProps {
@@ -69,7 +69,7 @@ interface TooltipTriggerProps {
export const TooltipTrigger = forwardRef(
(
{ children, placement, tooltip, ...rest }: TooltipTriggerProps,
- ref: ForwardedRef
+ ref: ForwardedRef,
) => {
const tooltipTriggerProps = { delay: 250, ...rest };
const tooltipState = useTooltipTriggerState(tooltipTriggerProps);
@@ -78,7 +78,7 @@ export const TooltipTrigger = forwardRef(
const { triggerProps, tooltipProps } = useTooltipTrigger(
tooltipTriggerProps,
tooltipState,
- triggerRef
+ triggerRef,
);
const { overlayProps } = useOverlayPosition({
@@ -94,7 +94,7 @@ export const TooltipTrigger = forwardRef(
(
children.props,
- rest
+ rest,
)}
/>
{tooltipState.isOpen && (
@@ -110,5 +110,5 @@ export const TooltipTrigger = forwardRef(
)}
);
- }
+ },
);
diff --git a/src/TranslatedError.ts b/src/TranslatedError.ts
index 62960f04..6c7f5dad 100644
--- a/src/TranslatedError.ts
+++ b/src/TranslatedError.ts
@@ -37,5 +37,7 @@ class TranslatedErrorImpl extends TranslatedError {}
// i18next-parser can't detect calls to a constructor, so we expose a bare
// function instead
-export const translatedError = (messageKey: string, t: typeof i18n.t) =>
- new TranslatedErrorImpl(messageKey, t);
+export const translatedError = (
+ messageKey: string,
+ t: typeof i18n.t,
+): TranslatedError => new TranslatedErrorImpl(messageKey, t);
diff --git a/src/UrlParams.ts b/src/UrlParams.ts
index f1d9210e..498d7cc5 100644
--- a/src/UrlParams.ts
+++ b/src/UrlParams.ts
@@ -119,17 +119,17 @@ interface UrlParams {
// file.
export function editFragmentQuery(
hash: string,
- edit: (params: URLSearchParams) => URLSearchParams
+ edit: (params: URLSearchParams) => URLSearchParams,
): string {
const fragmentQueryStart = hash.indexOf("?");
const fragmentParams = edit(
new URLSearchParams(
- fragmentQueryStart === -1 ? "" : hash.substring(fragmentQueryStart)
- )
+ fragmentQueryStart === -1 ? "" : hash.substring(fragmentQueryStart),
+ ),
);
return `${hash.substring(
0,
- fragmentQueryStart
+ fragmentQueryStart,
)}?${fragmentParams.toString()}`;
}
@@ -137,30 +137,30 @@ class ParamParser {
private fragmentParams: URLSearchParams;
private queryParams: URLSearchParams;
- constructor(search: string, hash: string) {
+ public constructor(search: string, hash: string) {
this.queryParams = new URLSearchParams(search);
const fragmentQueryStart = hash.indexOf("?");
this.fragmentParams = new URLSearchParams(
- fragmentQueryStart === -1 ? "" : hash.substring(fragmentQueryStart)
+ fragmentQueryStart === -1 ? "" : hash.substring(fragmentQueryStart),
);
}
// Normally, URL params should be encoded in the fragment so as to avoid
// leaking them to the server. However, we also check the normal query
// string for backwards compatibility with versions that only used that.
- getParam(name: string): string | null {
+ public getParam(name: string): string | null {
return this.fragmentParams.get(name) ?? this.queryParams.get(name);
}
- getAllParams(name: string): string[] {
+ public getAllParams(name: string): string[] {
return [
...this.fragmentParams.getAll(name),
...this.queryParams.getAll(name),
];
}
- getFlagParam(name: string, defaultValue = false): boolean {
+ public getFlagParam(name: string, defaultValue = false): boolean {
const param = this.getParam(name);
return param === null ? defaultValue : param !== "false";
}
@@ -174,7 +174,7 @@ class ParamParser {
*/
export const getUrlParams = (
search = window.location.search,
- hash = window.location.hash
+ hash = window.location.hash,
): UrlParams => {
const parser = new ParamParser(search, hash);
@@ -221,7 +221,7 @@ export const useUrlParams = (): UrlParams => {
export function getRoomIdentifierFromUrl(
pathname: string,
search: string,
- hash: string
+ hash: string,
): RoomIdentifier {
let roomAlias: string | null = null;
pathname = pathname.substring(1); // Strip the "/"
@@ -281,6 +281,6 @@ export const useRoomIdentifier = (): RoomIdentifier => {
const { pathname, search, hash } = useLocation();
return useMemo(
() => getRoomIdentifierFromUrl(pathname, search, hash),
- [pathname, search, hash]
+ [pathname, search, hash],
);
};
diff --git a/src/UserMenu.tsx b/src/UserMenu.tsx
index 42f31951..58f61390 100644
--- a/src/UserMenu.tsx
+++ b/src/UserMenu.tsx
@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
-import { useCallback, useMemo } from "react";
+import { FC, ReactNode, useCallback, useMemo } from "react";
import { Item } from "@react-stately/collections";
import { useLocation } from "react-router-dom";
import { useTranslation } from "react-i18next";
@@ -31,7 +31,7 @@ import LogoutIcon from "./icons/Logout.svg?react";
import { Body } from "./typography/Typography";
import styles from "./UserMenu.module.css";
-interface UserMenuProps {
+interface Props {
preventNavigation: boolean;
isAuthenticated: boolean;
isPasswordlessUser: boolean;
@@ -41,7 +41,7 @@ interface UserMenuProps {
onAction: (value: string) => void;
}
-export function UserMenu({
+export const UserMenu: FC = ({
preventNavigation,
isAuthenticated,
isPasswordlessUser,
@@ -49,7 +49,7 @@ export function UserMenu({
displayName,
avatarUrl,
onAction,
-}: UserMenuProps) {
+}) => {
const { t } = useTranslation();
const location = useLocation();
@@ -123,7 +123,7 @@ export function UserMenu({
{
// eslint-disable-next-line @typescript-eslint/no-explicit-any
- (props: any) => (
+ (props: any): ReactNode => (
{items.map(({ key, icon: Icon, label, dataTestid }) => (
-
@@ -141,4 +141,4 @@ export function UserMenu({
}
);
-}
+};
diff --git a/src/UserMenuContainer.tsx b/src/UserMenuContainer.tsx
index 359f75f6..c0155dcb 100644
--- a/src/UserMenuContainer.tsx
+++ b/src/UserMenuContainer.tsx
@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
-import { useCallback, useState } from "react";
+import { FC, useCallback, useState } from "react";
import { useHistory, useLocation } from "react-router-dom";
import { useClientLegacy } from "./ClientContext";
@@ -26,7 +26,7 @@ interface Props {
preventNavigation?: boolean;
}
-export function UserMenuContainer({ preventNavigation = false }: Props) {
+export const UserMenuContainer: FC
= ({ preventNavigation = false }) => {
const location = useLocation();
const history = useHistory();
const { client, logout, authenticated, passwordlessUser } = useClientLegacy();
@@ -34,7 +34,7 @@ export function UserMenuContainer({ preventNavigation = false }: Props) {
const [settingsModalOpen, setSettingsModalOpen] = useState(false);
const onDismissSettingsModal = useCallback(
() => setSettingsModalOpen(false),
- [setSettingsModalOpen]
+ [setSettingsModalOpen],
);
const [defaultSettingsTab, setDefaultSettingsTab] = useState();
@@ -58,7 +58,7 @@ export function UserMenuContainer({ preventNavigation = false }: Props) {
break;
}
},
- [history, location, logout, setSettingsModalOpen]
+ [history, location, logout, setSettingsModalOpen],
);
const userName = client?.getUserIdLocalpart() ?? "";
@@ -83,4 +83,4 @@ export function UserMenuContainer({ preventNavigation = false }: Props) {
)}
>
);
-}
+};
diff --git a/src/analytics/AnalyticsNotice.tsx b/src/analytics/AnalyticsNotice.tsx
index 544de61a..7b1d3fb5 100644
--- a/src/analytics/AnalyticsNotice.tsx
+++ b/src/analytics/AnalyticsNotice.tsx
@@ -1,3 +1,19 @@
+/*
+Copyright 2023 New Vector Ltd
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
import { FC } from "react";
import { Trans } from "react-i18next";
diff --git a/src/analytics/PosthogAnalytics.ts b/src/analytics/PosthogAnalytics.ts
index 9d4f3f5c..c1778f8b 100644
--- a/src/analytics/PosthogAnalytics.ts
+++ b/src/analytics/PosthogAnalytics.ts
@@ -117,7 +117,7 @@ export class PosthogAnalytics {
return this.internalInstance;
}
- constructor(private readonly posthog: PostHog) {
+ private constructor(private readonly posthog: PostHog) {
const posthogConfig: PosthogSettings = {
project_api_key: Config.get().posthog?.api_key,
api_host: Config.get().posthog?.api_host,
@@ -146,7 +146,7 @@ export class PosthogAnalytics {
this.enabled = true;
} else {
logger.info(
- "Posthog is not enabled because there is no api key or no host given in the config"
+ "Posthog is not enabled because there is no api key or no host given in the config",
);
this.enabled = false;
}
@@ -157,7 +157,7 @@ export class PosthogAnalytics {
private sanitizeProperties = (
properties: Properties,
- _eventName: string
+ _eventName: string,
): Properties => {
// Callback from posthog to sanitize properties before sending them to the server.
// Here we sanitize posthog's built in properties which leak PII e.g. url reporting.
@@ -183,7 +183,7 @@ export class PosthogAnalytics {
return properties;
};
- private registerSuperProperties(properties: Properties) {
+ private registerSuperProperties(properties: Properties): void {
if (this.enabled) {
this.posthog.register(properties);
}
@@ -201,8 +201,8 @@ export class PosthogAnalytics {
private capture(
eventName: string,
properties: Properties,
- options?: CaptureOptions
- ) {
+ options?: CaptureOptions,
+ ): void {
if (!this.enabled) {
return;
}
@@ -213,7 +213,7 @@ export class PosthogAnalytics {
return this.enabled;
}
- setAnonymity(anonymity: Anonymity): void {
+ private setAnonymity(anonymity: Anonymity): void {
// Update this.anonymity.
// To update the anonymity typically you want to call updateAnonymityFromSettings
// to ensure this value is in step with the user's settings.
@@ -236,7 +236,9 @@ export class PosthogAnalytics {
.join("");
}
- private async identifyUser(analyticsIdGenerator: () => string) {
+ private async identifyUser(
+ analyticsIdGenerator: () => string,
+ ): Promise {
if (this.anonymity == Anonymity.Pseudonymous && this.enabled) {
// Check the user's account_data for an analytics ID to use. Storing the ID in account_data allows
// different devices to send the same ID.
@@ -258,27 +260,27 @@ export class PosthogAnalytics {
// The above could fail due to network requests, but not essential to starting the application,
// so swallow it.
logger.log(
- "Unable to identify user for tracking" + (e as Error)?.toString()
+ "Unable to identify user for tracking" + (e as Error)?.toString(),
);
}
if (analyticsID) {
this.posthog.identify(analyticsID);
} else {
logger.info(
- "No analyticsID is availble. Should not try to setup posthog"
+ "No analyticsID is availble. Should not try to setup posthog",
);
}
}
}
- async getAnalyticsId() {
+ private async getAnalyticsId(): Promise {
const client: MatrixClient = window.matrixclient;
let accountAnalyticsId;
if (widget) {
accountAnalyticsId = getUrlParams().analyticsID;
} else {
const accountData = await client.getAccountDataFromServer(
- PosthogAnalytics.ANALYTICS_EVENT_TYPE
+ PosthogAnalytics.ANALYTICS_EVENT_TYPE,
);
accountAnalyticsId = accountData?.id;
}
@@ -291,12 +293,14 @@ export class PosthogAnalytics {
return null;
}
- async hashedEcAnalyticsId(accountAnalyticsId: string): Promise {
+ private async hashedEcAnalyticsId(
+ accountAnalyticsId: string,
+ ): Promise {
const client: MatrixClient = window.matrixclient;
const posthogIdMaterial = "ec" + accountAnalyticsId + client.getUserId();
const bufferForPosthogId = await crypto.subtle.digest(
"sha-256",
- Buffer.from(posthogIdMaterial, "utf-8")
+ Buffer.from(posthogIdMaterial, "utf-8"),
);
const view = new Int32Array(bufferForPosthogId);
return Array.from(view)
@@ -304,17 +308,17 @@ export class PosthogAnalytics {
.join("");
}
- async setAccountAnalyticsId(analyticsID: string) {
+ private async setAccountAnalyticsId(analyticsID: string): Promise {
if (!widget) {
const client = window.matrixclient;
// the analytics ID only needs to be set in the standalone version.
const accountData = await client.getAccountDataFromServer(
- PosthogAnalytics.ANALYTICS_EVENT_TYPE
+ PosthogAnalytics.ANALYTICS_EVENT_TYPE,
);
await client.setAccountData(
PosthogAnalytics.ANALYTICS_EVENT_TYPE,
- Object.assign({ id: analyticsID }, accountData)
+ Object.assign({ id: analyticsID }, accountData),
);
}
}
@@ -335,7 +339,7 @@ export class PosthogAnalytics {
this.updateAnonymityAndIdentifyUser(optInAnalytics);
}
- private updateSuperProperties() {
+ private updateSuperProperties(): void {
// Update super properties in posthog with our platform (app version, platform).
// These properties will be subsequently passed in every event.
//
@@ -356,7 +360,7 @@ export class PosthogAnalytics {
}
private async updateAnonymityAndIdentifyUser(
- pseudonymousOptIn: boolean
+ pseudonymousOptIn: boolean,
): Promise {
// Update this.anonymity based on the user's analytics opt-in settings
const anonymity = pseudonymousOptIn
@@ -372,11 +376,11 @@ export class PosthogAnalytics {
this.setRegistrationType(
window.matrixclient.isGuest() || window.passwordlessUser
? RegistrationType.Guest
- : RegistrationType.Registered
+ : RegistrationType.Registered,
);
// store the promise to await posthog-tracking-events until the identification is done.
this.identificationPromise = this.identifyUser(
- PosthogAnalytics.getRandomAnalyticsId
+ PosthogAnalytics.getRandomAnalyticsId,
);
await this.identificationPromise;
if (this.userRegisteredInThisSession()) {
@@ -391,7 +395,7 @@ export class PosthogAnalytics {
public async trackEvent(
{ eventName, ...properties }: E,
- options?: CaptureOptions
+ options?: CaptureOptions,
): Promise {
if (this.identificationPromise) {
// only make calls to posthog after the identificaion is done
diff --git a/src/analytics/PosthogEvents.ts b/src/analytics/PosthogEvents.ts
index 97e25b6f..c200f74b 100644
--- a/src/analytics/PosthogEvents.ts
+++ b/src/analytics/PosthogEvents.ts
@@ -36,18 +36,22 @@ export class CallEndedTracker {
maxParticipantsCount: 0,
};
- cacheStartCall(time: Date) {
+ public cacheStartCall(time: Date): void {
this.cache.startTime = time;
}
- cacheParticipantCountChanged(count: number) {
+ public cacheParticipantCountChanged(count: number): void {
this.cache.maxParticipantsCount = Math.max(
count,
- this.cache.maxParticipantsCount
+ this.cache.maxParticipantsCount,
);
}
- track(callId: string, callParticipantsNow: number, sendInstantly: boolean) {
+ public track(
+ callId: string,
+ callParticipantsNow: number,
+ sendInstantly: boolean,
+ ): void {
PosthogAnalytics.instance.trackEvent(
{
eventName: "CallEnded",
@@ -56,7 +60,7 @@ export class CallEndedTracker {
callParticipantsOnLeave: callParticipantsNow,
callDuration: (Date.now() - this.cache.startTime.getTime()) / 1000,
},
- { send_instantly: sendInstantly }
+ { send_instantly: sendInstantly },
);
}
}
@@ -67,7 +71,7 @@ interface CallStarted extends IPosthogEvent {
}
export class CallStartedTracker {
- track(callId: string) {
+ public track(callId: string): void {
PosthogAnalytics.instance.trackEvent({
eventName: "CallStarted",
callId: callId,
@@ -86,19 +90,19 @@ export class SignupTracker {
signupEnd: new Date(0),
};
- cacheSignupStart(time: Date) {
+ public cacheSignupStart(time: Date): void {
this.cache.signupStart = time;
}
- getSignupEndTime() {
+ public getSignupEndTime(): Date {
return this.cache.signupEnd;
}
- cacheSignupEnd(time: Date) {
+ public cacheSignupEnd(time: Date): void {
this.cache.signupEnd = time;
}
- track() {
+ public track(): void {
PosthogAnalytics.instance.trackEvent({
eventName: "Signup",
signupDuration: Date.now() - this.cache.signupStart.getTime(),
@@ -112,7 +116,7 @@ interface Login extends IPosthogEvent {
}
export class LoginTracker {
- track() {
+ public track(): void {
PosthogAnalytics.instance.trackEvent({
eventName: "Login",
});
@@ -127,7 +131,7 @@ interface MuteMicrophone {
}
export class MuteMicrophoneTracker {
- track(targetIsMute: boolean, callId: string) {
+ public track(targetIsMute: boolean, callId: string): void {
PosthogAnalytics.instance.trackEvent({
eventName: "MuteMicrophone",
targetMuteState: targetIsMute ? "mute" : "unmute",
@@ -143,7 +147,7 @@ interface MuteCamera {
}
export class MuteCameraTracker {
- track(targetIsMute: boolean, callId: string) {
+ public track(targetIsMute: boolean, callId: string): void {
PosthogAnalytics.instance.trackEvent({
eventName: "MuteCamera",
targetMuteState: targetIsMute ? "mute" : "unmute",
@@ -158,7 +162,7 @@ interface UndecryptableToDeviceEvent {
}
export class UndecryptableToDeviceEventTracker {
- track(callId: string) {
+ public track(callId: string): void {
PosthogAnalytics.instance.trackEvent({
eventName: "UndecryptableToDeviceEvent",
callId,
@@ -174,7 +178,7 @@ interface QualitySurveyEvent {
}
export class QualitySurveyEventTracker {
- track(callId: string, feedbackText: string, stars: number) {
+ public track(callId: string, feedbackText: string, stars: number): void {
PosthogAnalytics.instance.trackEvent({
eventName: "QualitySurvey",
callId,
@@ -190,7 +194,7 @@ interface CallDisconnectedEvent {
}
export class CallDisconnectedEventTracker {
- track(reason?: DisconnectReason) {
+ public track(reason?: DisconnectReason): void {
PosthogAnalytics.instance.trackEvent({
eventName: "CallDisconnected",
reason,
diff --git a/src/analytics/PosthogSpanProcessor.ts b/src/analytics/PosthogSpanProcessor.ts
index effa6436..8904b3e1 100644
--- a/src/analytics/PosthogSpanProcessor.ts
+++ b/src/analytics/PosthogSpanProcessor.ts
@@ -39,9 +39,9 @@ const maxRejoinMs = 2 * 60 * 1000; // 2 minutes
* Span processor that extracts certain metrics from spans to send to PostHog
*/
export class PosthogSpanProcessor implements SpanProcessor {
- async forceFlush(): Promise {}
+ public async forceFlush(): Promise {}
- onStart(span: Span): void {
+ public onStart(span: Span): void {
// Hack: Yield to allow attributes to be set before processing
Promise.resolve().then(() => {
switch (span.name) {
@@ -55,7 +55,7 @@ export class PosthogSpanProcessor implements SpanProcessor {
});
}
- onEnd(span: ReadableSpan): void {
+ public onEnd(span: ReadableSpan): void {
switch (span.name) {
case "matrix.groupCallMembership":
this.onGroupCallMembershipEnd(span);
@@ -148,7 +148,7 @@ export class PosthogSpanProcessor implements SpanProcessor {
ratioPeerConnectionToDevices: ratioPeerConnectionToDevices,
},
// Send instantly because the window might be closing
- { send_instantly: true }
+ { send_instantly: true },
);
}
}
@@ -157,7 +157,7 @@ export class PosthogSpanProcessor implements SpanProcessor {
/**
* Shutdown the processor.
*/
- shutdown(): Promise {
+ public shutdown(): Promise {
return Promise.resolve();
}
}
diff --git a/src/analytics/RageshakeSpanProcessor.ts b/src/analytics/RageshakeSpanProcessor.ts
index b5b055f2..91d0e190 100644
--- a/src/analytics/RageshakeSpanProcessor.ts
+++ b/src/analytics/RageshakeSpanProcessor.ts
@@ -1,4 +1,20 @@
-import { Attributes } from "@opentelemetry/api";
+/*
+Copyright 2023 New Vector Ltd
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+import { AttributeValue, Attributes } from "@opentelemetry/api";
import { hrTimeToMicroseconds } from "@opentelemetry/core";
import {
SpanProcessor,
@@ -6,7 +22,21 @@ import {
Span,
} from "@opentelemetry/sdk-trace-base";
-const dumpAttributes = (attr: Attributes) =>
+const dumpAttributes = (
+ attr: Attributes,
+): {
+ key: string;
+ type:
+ | "string"
+ | "number"
+ | "bigint"
+ | "boolean"
+ | "symbol"
+ | "undefined"
+ | "object"
+ | "function";
+ value: AttributeValue | undefined;
+}[] =>
Object.entries(attr).map(([key, value]) => ({
key,
type: typeof value,
@@ -20,13 +50,13 @@ const dumpAttributes = (attr: Attributes) =>
export class RageshakeSpanProcessor implements SpanProcessor {
private readonly spans: ReadableSpan[] = [];
- async forceFlush(): Promise {}
+ public async forceFlush(): Promise {}
- onStart(span: Span): void {
+ public onStart(span: Span): void {
this.spans.push(span);
}
- onEnd(): void {}
+ public onEnd(): void {}
/**
* Dumps the spans collected so far as Jaeger-compatible JSON.
@@ -110,5 +140,5 @@ export class RageshakeSpanProcessor implements SpanProcessor {
});
}
- async shutdown(): Promise {}
+ public async shutdown(): Promise {}
}
diff --git a/src/array-utils.ts b/src/array-utils.ts
index 6aeb1818..0bb0270d 100644
--- a/src/array-utils.ts
+++ b/src/array-utils.ts
@@ -22,7 +22,7 @@ limitations under the License.
// Array.prototype.findLastIndex
export function findLastIndex(
array: T[],
- predicate: (item: T, index: number) => boolean
+ predicate: (item: T, index: number) => boolean,
): number | null {
for (let i = array.length - 1; i >= 0; i--) {
if (predicate(array[i], i)) return i;
@@ -36,9 +36,9 @@ export function findLastIndex(
*/
export const count = (
array: T[],
- predicate: (item: T, index: number) => boolean
+ predicate: (item: T, index: number) => boolean,
): number =>
array.reduce(
(acc, item, index) => (predicate(item, index) ? acc + 1 : acc),
- 0
+ 0,
);
diff --git a/src/auth/LoginPage.tsx b/src/auth/LoginPage.tsx
index 6569f262..d414a2bc 100644
--- a/src/auth/LoginPage.tsx
+++ b/src/auth/LoginPage.tsx
@@ -80,7 +80,7 @@ export const LoginPage: FC = () => {
setLoading(false);
});
},
- [login, location, history, homeserver, setClient]
+ [login, location, history, homeserver, setClient],
);
return (
diff --git a/src/auth/RegisterPage.tsx b/src/auth/RegisterPage.tsx
index 52c652f6..946989c8 100644
--- a/src/auth/RegisterPage.tsx
+++ b/src/auth/RegisterPage.tsx
@@ -69,7 +69,7 @@ export const RegisterPage: FC = () => {
if (password !== passwordConfirmation) return;
- const submit = async () => {
+ const submit = async (): Promise => {
setRegistering(true);
const recaptchaResponse = await execute();
@@ -78,7 +78,7 @@ export const RegisterPage: FC = () => {
password,
userName,
recaptchaResponse,
- passwordlessUser
+ passwordlessUser,
);
if (client && client?.groupCallEventHandler && passwordlessUser) {
@@ -135,7 +135,7 @@ export const RegisterPage: FC = () => {
execute,
client,
setClient,
- ]
+ ],
);
useEffect(() => {
@@ -184,7 +184,7 @@ export const RegisterPage: FC = () => {
required
name="password"
type="password"
- onChange={(e: ChangeEvent) =>
+ onChange={(e: ChangeEvent): void =>
setPassword(e.target.value)
}
value={password}
@@ -198,7 +198,7 @@ export const RegisterPage: FC = () => {
required
type="password"
name="passwordConfirmation"
- onChange={(e: ChangeEvent) =>
+ onChange={(e: ChangeEvent): void =>
setPasswordConfirmation(e.target.value)
}
value={passwordConfirmation}
diff --git a/src/auth/useInteractiveLogin.ts b/src/auth/useInteractiveLogin.ts
index 07dec12d..83767f51 100644
--- a/src/auth/useInteractiveLogin.ts
+++ b/src/auth/useInteractiveLogin.ts
@@ -21,12 +21,16 @@ import { createClient, MatrixClient } from "matrix-js-sdk/src/matrix";
import { initClient } from "../matrix-utils";
import { Session } from "../ClientContext";
-export const useInteractiveLogin = () =>
- useCallback<
+export function useInteractiveLogin(): (
+ homeserver: string,
+ username: string,
+ password: string,
+) => Promise<[MatrixClient, Session]> {
+ return useCallback<
(
homeserver: string,
username: string,
- password: string
+ password: string,
) => Promise<[MatrixClient, Session]>
>(async (homeserver: string, username: string, password: string) => {
const authClient = createClient({ baseUrl: homeserver });
@@ -41,8 +45,8 @@ export const useInteractiveLogin = () =>
},
password,
}),
- stateUpdated: (...args) => {},
- requestEmailToken: (...args): Promise<{ sid: string }> => {
+ stateUpdated: (): void => {},
+ requestEmailToken: (): Promise<{ sid: string }> => {
return Promise.resolve({ sid: "" });
},
});
@@ -66,9 +70,9 @@ export const useInteractiveLogin = () =>
userId: user_id,
deviceId: device_id,
},
- false
+ false,
);
/* eslint-enable camelcase */
-
return [client, session];
}, []);
+}
diff --git a/src/auth/useInteractiveRegistration.ts b/src/auth/useInteractiveRegistration.ts
index a4863671..9a69da6a 100644
--- a/src/auth/useInteractiveRegistration.ts
+++ b/src/auth/useInteractiveRegistration.ts
@@ -30,14 +30,14 @@ export const useInteractiveRegistration = (): {
password: string,
displayName: string,
recaptchaResponse: string,
- passwordlessUser: boolean
+ passwordlessUser: boolean,
) => Promise<[MatrixClient, Session]>;
} => {
const [privacyPolicyUrl, setPrivacyPolicyUrl] = useState(
- undefined
+ undefined,
);
const [recaptchaKey, setRecaptchaKey] = useState(
- undefined
+ undefined,
);
const authClient = useRef();
@@ -50,7 +50,7 @@ export const useInteractiveRegistration = (): {
useEffect(() => {
authClient.current!.registerRequest({}).catch((error) => {
setPrivacyPolicyUrl(
- error.data?.params["m.login.terms"]?.policies?.privacy_policy?.en?.url
+ error.data?.params["m.login.terms"]?.policies?.privacy_policy?.en?.url,
);
setRecaptchaKey(error.data?.params["m.login.recaptcha"]?.public_key);
});
@@ -62,7 +62,7 @@ export const useInteractiveRegistration = (): {
password: string,
displayName: string,
recaptchaResponse: string,
- passwordlessUser: boolean
+ passwordlessUser: boolean,
): Promise<[MatrixClient, Session]> => {
const interactiveAuth = new InteractiveAuth({
matrixClient: authClient.current!,
@@ -72,7 +72,7 @@ export const useInteractiveRegistration = (): {
password,
auth: auth || undefined,
}),
- stateUpdated: (nextStage, status) => {
+ stateUpdated: (nextStage, status): void => {
if (status.error) {
throw new Error(status.error);
}
@@ -88,7 +88,7 @@ export const useInteractiveRegistration = (): {
});
}
},
- requestEmailToken: (...args) => {
+ requestEmailToken: (): Promise<{ sid: string }> => {
return Promise.resolve({ sid: "dummy" });
},
});
@@ -106,7 +106,7 @@ export const useInteractiveRegistration = (): {
userId: user_id,
deviceId: device_id,
},
- false
+ false,
);
await client.setDisplayName(displayName);
@@ -129,7 +129,7 @@ export const useInteractiveRegistration = (): {
return [client, session];
},
- []
+ [],
);
return { privacyPolicyUrl, recaptchaKey, register };
diff --git a/src/auth/useRecaptcha.ts b/src/auth/useRecaptcha.ts
index 0ee9f33b..b7583cd4 100644
--- a/src/auth/useRecaptcha.ts
+++ b/src/auth/useRecaptcha.ts
@@ -35,7 +35,11 @@ interface RecaptchaPromiseRef {
reject: (error: Error) => void;
}
-export const useRecaptcha = (sitekey?: string) => {
+export function useRecaptcha(sitekey?: string): {
+ execute: () => Promise;
+ reset: () => void;
+ recaptchaId: string;
+} {
const { t } = useTranslation();
const [recaptchaId] = useState(() => randomString(16));
const promiseRef = useRef();
@@ -43,7 +47,7 @@ export const useRecaptcha = (sitekey?: string) => {
useEffect(() => {
if (!sitekey) return;
- const onRecaptchaLoaded = () => {
+ const onRecaptchaLoaded = (): void => {
if (!document.getElementById(recaptchaId)) return;
window.grecaptcha.render(recaptchaId, {
@@ -91,11 +95,11 @@ export const useRecaptcha = (sitekey?: string) => {
});
promiseRef.current = {
- resolve: (value) => {
+ resolve: (value): void => {
resolve(value);
observer.disconnect();
},
- reject: (error) => {
+ reject: (error): void => {
reject(error);
observer.disconnect();
},
@@ -104,7 +108,7 @@ export const useRecaptcha = (sitekey?: string) => {
window.grecaptcha.execute();
const iframe = document.querySelector(
- 'iframe[src*="recaptcha/api2/bframe"]'
+ 'iframe[src*="recaptcha/api2/bframe"]',
);
if (iframe?.parentNode?.parentNode) {
@@ -120,4 +124,4 @@ export const useRecaptcha = (sitekey?: string) => {
}, []);
return { execute, reset, recaptchaId };
-};
+}
diff --git a/src/auth/useRegisterPasswordlessUser.ts b/src/auth/useRegisterPasswordlessUser.ts
index 983789ba..d34a479e 100644
--- a/src/auth/useRegisterPasswordlessUser.ts
+++ b/src/auth/useRegisterPasswordlessUser.ts
@@ -48,7 +48,7 @@ export function useRegisterPasswordlessUser(): UseRegisterPasswordlessUserType {
randomString(16),
displayName,
recaptchaResponse,
- true
+ true,
);
setClient({ client, session });
} catch (e) {
@@ -56,7 +56,7 @@ export function useRegisterPasswordlessUser(): UseRegisterPasswordlessUserType {
throw e;
}
},
- [execute, reset, register, setClient]
+ [execute, reset, register, setClient],
);
return { privacyPolicyUrl, registerPasswordlessUser, recaptchaId };
diff --git a/src/button/Button.module.css b/src/button/Button.module.css
index d5cc8b15..7f915648 100644
--- a/src/button/Button.module.css
+++ b/src/button/Button.module.css
@@ -146,7 +146,9 @@ limitations under the License.
.copyButton {
width: 100%;
height: 40px;
- transition: border-color 250ms, background-color 250ms;
+ transition:
+ border-color 250ms,
+ background-color 250ms;
}
.copyButton span {
diff --git a/src/button/Button.tsx b/src/button/Button.tsx
index efb9f295..933be484 100644
--- a/src/button/Button.tsx
+++ b/src/button/Button.tsx
@@ -13,7 +13,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
-import { forwardRef } from "react";
+import { FC, forwardRef } from "react";
import { PressEvent } from "@react-types/shared";
import classNames from "classnames";
import { useButton } from "@react-aria/button";
@@ -94,12 +94,12 @@ export const Button = forwardRef(
onPressStart,
...rest
},
- ref
+ ref,
) => {
const buttonRef = useObjectRef(ref);
const { buttonProps } = useButton(
{ onPress, onPressStart, ...rest },
- buttonRef
+ buttonRef,
);
// TODO: react-aria's useButton hook prevents form submission via keyboard
@@ -121,7 +121,7 @@ export const Button = forwardRef(
{
[styles.on]: on,
[styles.off]: off,
- }
+ },
)}
{...mergeProps(rest, filteredButtonProps)}
ref={buttonRef}
@@ -132,17 +132,14 @@ export const Button = forwardRef(
>
);
- }
+ },
);
-export function MicButton({
- muted,
- ...rest
-}: {
+export const MicButton: FC<{
muted: boolean;
// TODO: add all props for
[index: string]: unknown;
-}) {
+}> = ({ muted, ...rest }) => {
const { t } = useTranslation();
const Icon = muted ? MicOffSolidIcon : MicOnSolidIcon;
const label = muted ? t("Unmute microphone") : t("Mute microphone");
@@ -154,16 +151,13 @@ export function MicButton({
);
-}
+};
-export function VideoButton({
- muted,
- ...rest
-}: {
+export const VideoButton: FC<{
muted: boolean;
// TODO: add all props for
[index: string]: unknown;
-}) {
+}> = ({ muted, ...rest }) => {
const { t } = useTranslation();
const Icon = muted ? VideoCallOffIcon : VideoCallIcon;
const label = muted ? t("Start video") : t("Stop video");
@@ -175,18 +169,14 @@ export function VideoButton({
);
-}
+};
-export function ScreenshareButton({
- enabled,
- className,
- ...rest
-}: {
+export const ScreenshareButton: FC<{
enabled: boolean;
className?: string;
// TODO: add all props for
[index: string]: unknown;
-}) {
+}> = ({ enabled, className, ...rest }) => {
const { t } = useTranslation();
const label = enabled ? t("Sharing screen") : t("Share screen");
@@ -197,16 +187,13 @@ export function ScreenshareButton({
);
-}
+};
-export function HangupButton({
- className,
- ...rest
-}: {
+export const HangupButton: FC<{
className?: string;
// TODO: add all props for
[index: string]: unknown;
-}) {
+}> = ({ className, ...rest }) => {
const { t } = useTranslation();
return (
@@ -220,16 +207,13 @@ export function HangupButton({
);
-}
+};
-export function SettingsButton({
- className,
- ...rest
-}: {
+export const SettingsButton: FC<{
className?: string;
// TODO: add all props for
[index: string]: unknown;
-}) {
+}> = ({ className, ...rest }) => {
const { t } = useTranslation();
return (
@@ -239,7 +223,7 @@ export function SettingsButton({
);
-}
+};
interface AudioButtonProps extends Omit {
/**
@@ -248,7 +232,7 @@ interface AudioButtonProps extends Omit {
volume: number;
}
-export function AudioButton({ volume, ...rest }: AudioButtonProps) {
+export const AudioButton: FC = ({ volume, ...rest }) => {
const { t } = useTranslation();
return (
@@ -258,16 +242,16 @@ export function AudioButton({ volume, ...rest }: AudioButtonProps) {
);
-}
+};
interface FullscreenButtonProps extends Omit {
fullscreen?: boolean;
}
-export function FullscreenButton({
+export const FullscreenButton: FC = ({
fullscreen,
...rest
-}: FullscreenButtonProps) {
+}) => {
const { t } = useTranslation();
const Icon = fullscreen ? FullscreenExit : Fullscreen;
const label = fullscreen ? t("Exit full screen") : t("Full screen");
@@ -279,4 +263,4 @@ export function FullscreenButton({
);
-}
+};
diff --git a/src/button/CopyButton.tsx b/src/button/CopyButton.tsx
index c2631c6e..1cbc6785 100644
--- a/src/button/CopyButton.tsx
+++ b/src/button/CopyButton.tsx
@@ -16,6 +16,7 @@ limitations under the License.
import { useTranslation } from "react-i18next";
import useClipboard from "react-use-clipboard";
+import { FC } from "react";
import CheckIcon from "../icons/Check.svg?react";
import CopyIcon from "../icons/Copy.svg?react";
@@ -28,14 +29,15 @@ interface Props {
variant?: ButtonVariant;
copiedMessage?: string;
}
-export function CopyButton({
+
+export const CopyButton: FC = ({
value,
children,
className,
variant,
copiedMessage,
...rest
-}: Props) {
+}) => {
const { t } = useTranslation();
const [isCopied, setCopied] = useClipboard(value, { successDuration: 3000 });
@@ -62,4 +64,4 @@ export function CopyButton({
)}
);
-}
+};
diff --git a/src/button/LinkButton.tsx b/src/button/LinkButton.tsx
index 3392935d..eb30441c 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 { HTMLAttributes } from "react";
+import { FC, HTMLAttributes } from "react";
import { Link } from "react-router-dom";
import classNames from "classnames";
import * as H from "history";
@@ -34,20 +34,20 @@ interface Props extends HTMLAttributes {
className?: string;
}
-export function LinkButton({
+export const LinkButton: FC = ({
children,
to,
size,
variant,
className,
...rest
-}: Props) {
+}) => {
return (
);
-}
+};
diff --git a/src/config/Config.ts b/src/config/Config.ts
index f414c58b..0930af49 100644
--- a/src/config/Config.ts
+++ b/src/config/Config.ts
@@ -57,7 +57,7 @@ export class Config {
}
async function downloadConfig(
- configJsonFilename: string
+ configJsonFilename: string,
): Promise {
const url = new URL(configJsonFilename, window.location.href);
url.searchParams.set("cachebuster", Date.now().toString());
diff --git a/src/form/Form.tsx b/src/form/Form.tsx
index 54961791..1f66ac1a 100644
--- a/src/form/Form.tsx
+++ b/src/form/Form.tsx
@@ -36,5 +36,5 @@ export const Form = forwardRef(
{children}
);
- }
+ },
);
diff --git a/src/home/CallList.tsx b/src/home/CallList.tsx
index 8bd3778d..187228b9 100644
--- a/src/home/CallList.tsx
+++ b/src/home/CallList.tsx
@@ -18,6 +18,7 @@ import { Link } from "react-router-dom";
import { MatrixClient } from "matrix-js-sdk/src/client";
import { RoomMember } from "matrix-js-sdk/src/models/room-member";
import { Room } from "matrix-js-sdk/src/models/room";
+import { FC } from "react";
import { CopyButton } from "../button";
import { Avatar, Size } from "../Avatar";
@@ -31,7 +32,8 @@ interface CallListProps {
rooms: GroupCallRoom[];
client: MatrixClient;
}
-export function CallList({ rooms, client }: CallListProps) {
+
+export const CallList: FC = ({ rooms, client }) => {
return (
<>
@@ -54,7 +56,7 @@ export function CallList({ rooms, client }: CallListProps) {
>
);
-}
+};
interface CallTileProps {
name: string;
avatarUrl: string;
@@ -62,7 +64,8 @@ interface CallTileProps {
participants: RoomMember[];
client: MatrixClient;
}
-function CallTile({ name, avatarUrl, room }: CallTileProps) {
+
+const CallTile: FC = ({ name, avatarUrl, room }) => {
const roomSharedKey = useRoomSharedKey(room.roomId);
return (
@@ -71,7 +74,7 @@ function CallTile({ name, avatarUrl, room }: CallTileProps) {
to={getRelativeRoomUrl(
room.roomId,
room.name,
- roomSharedKey ?? undefined
+ roomSharedKey ?? undefined,
)}
className={styles.callTileLink}
>
@@ -89,9 +92,9 @@ function CallTile({ name, avatarUrl, room }: CallTileProps) {
value={getAbsoluteRoomUrl(
room.roomId,
room.name,
- roomSharedKey ?? undefined
+ roomSharedKey ?? undefined,
)}
/>
);
-}
+};
diff --git a/src/home/HomePage.tsx b/src/home/HomePage.tsx
index a78cb8bd..779a7168 100644
--- a/src/home/HomePage.tsx
+++ b/src/home/HomePage.tsx
@@ -15,6 +15,7 @@ limitations under the License.
*/
import { useTranslation } from "react-i18next";
+import { FC } from "react";
import { useClientState } from "../ClientContext";
import { ErrorView, LoadingView } from "../FullScreenView";
@@ -22,7 +23,7 @@ import { UnauthenticatedView } from "./UnauthenticatedView";
import { RegisteredView } from "./RegisteredView";
import { usePageTitle } from "../usePageTitle";
-export function HomePage() {
+export const HomePage: FC = () => {
const { t } = useTranslation();
usePageTitle(t("Home"));
@@ -39,4 +40,4 @@ export function HomePage() {
);
}
-}
+};
diff --git a/src/home/JoinExistingCallModal.tsx b/src/home/JoinExistingCallModal.tsx
index 35672295..d8ed9fce 100644
--- a/src/home/JoinExistingCallModal.tsx
+++ b/src/home/JoinExistingCallModal.tsx
@@ -16,6 +16,7 @@ limitations under the License.
import { PressEvent } from "@react-types/shared";
import { useTranslation } from "react-i18next";
+import { FC } from "react";
import { Modal } from "../Modal";
import { Button } from "../button";
@@ -28,7 +29,11 @@ interface Props {
onJoin: (e: PressEvent) => void;
}
-export function JoinExistingCallModal({ onJoin, open, onDismiss }: Props) {
+export const JoinExistingCallModal: FC = ({
+ onJoin,
+ open,
+ onDismiss,
+}) => {
const { t } = useTranslation();
return (
@@ -42,4 +47,4 @@ export function JoinExistingCallModal({ onJoin, open, onDismiss }: Props) {
);
-}
+};
diff --git a/src/home/RegisteredView.tsx b/src/home/RegisteredView.tsx
index 995f7a54..4a130899 100644
--- a/src/home/RegisteredView.tsx
+++ b/src/home/RegisteredView.tsx
@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
-import { useState, useCallback, FormEvent, FormEventHandler } from "react";
+import { useState, useCallback, FormEvent, FormEventHandler, FC } from "react";
import { useHistory } from "react-router-dom";
import { MatrixClient } from "matrix-js-sdk/src/client";
import { useTranslation } from "react-i18next";
@@ -46,7 +46,7 @@ interface Props {
client: MatrixClient;
}
-export function RegisteredView({ client }: Props) {
+export const RegisteredView: FC = ({ client }) => {
const [loading, setLoading] = useState(false);
const [error, setError] = useState();
const [optInAnalytics] = useOptInAnalytics();
@@ -56,7 +56,7 @@ export function RegisteredView({ client }: Props) {
useState(false);
const onDismissJoinExistingCallModal = useCallback(
() => setJoinExistingCallModalOpen(false),
- [setJoinExistingCallModalOpen]
+ [setJoinExistingCallModalOpen],
);
const [e2eeEnabled] = useEnableE2EE();
@@ -70,22 +70,22 @@ export function RegisteredView({ client }: Props) {
? sanitiseRoomNameInput(roomNameData)
: "";
- async function submit() {
+ async function submit(): Promise {
setError(undefined);
setLoading(true);
const createRoomResult = await createRoom(
client,
roomName,
- e2eeEnabled ?? false
+ e2eeEnabled ?? false,
);
history.push(
getRelativeRoomUrl(
createRoomResult.roomId,
roomName,
- createRoomResult.password
- )
+ createRoomResult.password,
+ ),
);
}
@@ -102,7 +102,7 @@ export function RegisteredView({ client }: Props) {
}
});
},
- [client, history, setJoinExistingCallModalOpen, e2eeEnabled]
+ [client, history, setJoinExistingCallModalOpen, e2eeEnabled],
);
const recentRooms = useGroupCallRooms(client);
@@ -175,4 +175,4 @@ export function RegisteredView({ client }: Props) {
/>
>
);
-}
+};
diff --git a/src/home/UnauthenticatedView.tsx b/src/home/UnauthenticatedView.tsx
index 2f675436..beb419db 100644
--- a/src/home/UnauthenticatedView.tsx
+++ b/src/home/UnauthenticatedView.tsx
@@ -57,7 +57,7 @@ export const UnauthenticatedView: FC = () => {
useState(false);
const onDismissJoinExistingCallModal = useCallback(
() => setJoinExistingCallModalOpen(false),
- [setJoinExistingCallModalOpen]
+ [setJoinExistingCallModalOpen],
);
const [onFinished, setOnFinished] = useState<() => void>();
const history = useHistory();
@@ -72,7 +72,7 @@ export const UnauthenticatedView: FC = () => {
const roomName = sanitiseRoomNameInput(data.get("callName") as string);
const displayName = data.get("displayName") as string;
- async function submit() {
+ async function submit(): Promise {
setError(undefined);
setLoading(true);
const recaptchaResponse = await execute();
@@ -82,7 +82,7 @@ export const UnauthenticatedView: FC = () => {
randomString(16),
displayName,
recaptchaResponse,
- true
+ true,
);
let createRoomResult;
@@ -90,7 +90,7 @@ export const UnauthenticatedView: FC = () => {
createRoomResult = await createRoom(
client,
roomName,
- e2eeEnabled ?? false
+ e2eeEnabled ?? false,
);
} catch (error) {
if (!setClient) {
@@ -124,8 +124,8 @@ export const UnauthenticatedView: FC = () => {
getRelativeRoomUrl(
createRoomResult.roomId,
roomName,
- createRoomResult.password
- )
+ createRoomResult.password,
+ ),
);
}
@@ -144,7 +144,7 @@ export const UnauthenticatedView: FC = () => {
setJoinExistingCallModalOpen,
setClient,
e2eeEnabled,
- ]
+ ],
);
return (
diff --git a/src/home/useGroupCallRooms.ts b/src/home/useGroupCallRooms.ts
index 51633d8e..77572ad6 100644
--- a/src/home/useGroupCallRooms.ts
+++ b/src/home/useGroupCallRooms.ts
@@ -31,7 +31,7 @@ export interface GroupCallRoom {
}
const tsCache: { [index: string]: number } = {};
-function getLastTs(client: MatrixClient, r: Room) {
+function getLastTs(client: MatrixClient, r: Room): number {
if (tsCache[r.roomId]) {
return tsCache[r.roomId];
}
@@ -47,7 +47,7 @@ function getLastTs(client: MatrixClient, r: Room) {
if (r.getMyMembership() !== "join") {
const membershipEvent = r.currentState.getStateEvents(
"m.room.member",
- myUserId
+ myUserId,
);
if (membershipEvent && !Array.isArray(membershipEvent)) {
@@ -82,7 +82,7 @@ export function useGroupCallRooms(client: MatrixClient): GroupCallRoom[] {
const [rooms, setRooms] = useState([]);
useEffect(() => {
- function updateRooms() {
+ function updateRooms(): void {
if (!client.groupCallEventHandler) {
return;
}
@@ -115,7 +115,7 @@ export function useGroupCallRooms(client: MatrixClient): GroupCallRoom[] {
client.removeListener(GroupCallEventHandlerEvent.Incoming, updateRooms);
client.removeListener(
GroupCallEventHandlerEvent.Participants,
- updateRooms
+ updateRooms,
);
};
}, [client]);
diff --git a/src/index.css b/src/index.css
index d9f7d052..f647e87c 100644
--- a/src/index.css
+++ b/src/index.css
@@ -68,7 +68,8 @@ limitations under the License.
font-weight: 400;
font-display: swap;
unicode-range: var(--inter-unicode-range);
- src: url("/fonts/Inter/Inter-Regular.woff2") format("woff2"),
+ src:
+ url("/fonts/Inter/Inter-Regular.woff2") format("woff2"),
url("/fonts/Inter/Inter-Regular.woff") format("woff");
}
@@ -78,7 +79,8 @@ limitations under the License.
font-weight: 400;
font-display: swap;
unicode-range: var(--inter-unicode-range);
- src: url("/fonts/Inter/Inter-Italic.woff2") format("woff2"),
+ src:
+ url("/fonts/Inter/Inter-Italic.woff2") format("woff2"),
url("/fonts/Inter/Inter-Italic.woff") format("woff");
}
@@ -88,7 +90,8 @@ limitations under the License.
font-weight: 500;
font-display: swap;
unicode-range: var(--inter-unicode-range);
- src: url("/fonts/Inter/Inter-Medium.woff2") format("woff2"),
+ src:
+ url("/fonts/Inter/Inter-Medium.woff2") format("woff2"),
url("/fonts/Inter/Inter-Medium.woff") format("woff");
}
@@ -98,7 +101,8 @@ limitations under the License.
font-weight: 500;
font-display: swap;
unicode-range: var(--inter-unicode-range);
- src: url("/fonts/Inter/Inter-MediumItalic.woff2") format("woff2"),
+ src:
+ url("/fonts/Inter/Inter-MediumItalic.woff2") format("woff2"),
url("/fonts/Inter/Inter-MediumItalic.woff") format("woff");
}
@@ -108,7 +112,8 @@ limitations under the License.
font-weight: 600;
font-display: swap;
unicode-range: var(--inter-unicode-range);
- src: url("/fonts/Inter/Inter-SemiBold.woff2") format("woff2"),
+ src:
+ url("/fonts/Inter/Inter-SemiBold.woff2") format("woff2"),
url("/fonts/Inter/Inter-SemiBold.woff") format("woff");
}
@@ -118,7 +123,8 @@ limitations under the License.
font-weight: 600;
font-display: swap;
unicode-range: var(--inter-unicode-range);
- src: url("/fonts/Inter/Inter-SemiBoldItalic.woff2") format("woff2"),
+ src:
+ url("/fonts/Inter/Inter-SemiBoldItalic.woff2") format("woff2"),
url("/fonts/Inter/Inter-SemiBoldItalic.woff") format("woff");
}
@@ -128,7 +134,8 @@ limitations under the License.
font-weight: 700;
font-display: swap;
unicode-range: var(--inter-unicode-range);
- src: url("/fonts/Inter/Inter-Bold.woff2") format("woff2"),
+ src:
+ url("/fonts/Inter/Inter-Bold.woff2") format("woff2"),
url("/fonts/Inter/Inter-Bold.woff") format("woff");
}
@@ -138,7 +145,8 @@ limitations under the License.
font-weight: 700;
font-display: swap;
unicode-range: var(--inter-unicode-range);
- src: url("/fonts/Inter/Inter-BoldItalic.woff2") format("woff2"),
+ src:
+ url("/fonts/Inter/Inter-BoldItalic.woff2") format("woff2"),
url("/fonts/Inter/Inter-BoldItalic.woff") format("woff");
}
diff --git a/src/initializer.tsx b/src/initializer.tsx
index 9a8152dd..57000936 100644
--- a/src/initializer.tsx
+++ b/src/initializer.tsx
@@ -35,11 +35,11 @@ enum LoadState {
class DependencyLoadStates {
// TODO: decide where olm should be initialized (see TODO comment below)
// olm: LoadState = LoadState.None;
- config: LoadState = LoadState.None;
- sentry: LoadState = LoadState.None;
- openTelemetry: LoadState = LoadState.None;
+ public config: LoadState = LoadState.None;
+ public sentry: LoadState = LoadState.None;
+ public openTelemetry: LoadState = LoadState.None;
- allDepsAreLoaded() {
+ public allDepsAreLoaded(): boolean {
return !Object.values(this).some((s) => s !== LoadState.Loaded);
}
}
@@ -52,7 +52,7 @@ export class Initializer {
return Initializer.internalInstance?.isInitialized;
}
- public static initBeforeReact() {
+ public static initBeforeReact(): void {
// this maybe also needs to return a promise in the future,
// if we have to do async inits before showing the loading screen
// but this should be avioded if possible
@@ -99,13 +99,13 @@ export class Initializer {
if (fontScale !== null) {
document.documentElement.style.setProperty(
"--font-scale",
- fontScale.toString()
+ fontScale.toString(),
);
}
if (fonts.length > 0) {
document.documentElement.style.setProperty(
"--font-family",
- fonts.map((f) => `"${f}"`).join(", ")
+ fonts.map((f) => `"${f}"`).join(", "),
);
}
@@ -126,9 +126,9 @@ export class Initializer {
return Initializer.internalInstance.initPromise;
}
- loadStates = new DependencyLoadStates();
+ private loadStates = new DependencyLoadStates();
- initStep(resolve: (value: void | PromiseLike) => void) {
+ private initStep(resolve: (value: void | PromiseLike) => void): void {
// TODO: Olm is initialized with the client currently (see `initClient()` and `olm.ts`)
// we need to decide if we want to init it here or keep it in initClient
// if (this.loadStates.olm === LoadState.None) {
diff --git a/src/input/AvatarInputField.tsx b/src/input/AvatarInputField.tsx
index b8897c4a..266f2717 100644
--- a/src/input/AvatarInputField.tsx
+++ b/src/input/AvatarInputField.tsx
@@ -52,7 +52,7 @@ export const AvatarInputField = forwardRef(
onRemoveAvatar,
...rest
},
- ref
+ ref,
) => {
const { t } = useTranslation();
@@ -64,7 +64,7 @@ export const AvatarInputField = forwardRef(
useEffect(() => {
const currentInput = fileInputRef.current;
- const onChange = (e: Event) => {
+ const onChange = (e: Event): void => {
const inputEvent = e as unknown as ChangeEvent;
if (inputEvent.target.files && inputEvent.target.files.length > 0) {
setObjUrl(URL.createObjectURL(inputEvent.target.files[0]));
@@ -76,7 +76,7 @@ export const AvatarInputField = forwardRef(
currentInput.addEventListener("change", onChange);
- return () => {
+ return (): void => {
currentInput?.removeEventListener("change", onChange);
};
});
@@ -120,5 +120,5 @@ export const AvatarInputField = forwardRef(
)}
);
- }
+ },
);
diff --git a/src/input/Input.module.css b/src/input/Input.module.css
index dc1bde19..b81a903f 100644
--- a/src/input/Input.module.css
+++ b/src/input/Input.module.css
@@ -85,8 +85,11 @@ limitations under the License.
}
.inputField label {
- transition: font-size 0.25s ease-out 0.1s, color 0.25s ease-out 0.1s,
- top 0.25s ease-out 0.1s, background-color 0.25s ease-out 0.1s;
+ transition:
+ font-size 0.25s ease-out 0.1s,
+ color 0.25s ease-out 0.1s,
+ top 0.25s ease-out 0.1s,
+ background-color 0.25s ease-out 0.1s;
color: var(--cpd-color-text-secondary);
background-color: transparent;
font-size: var(--font-size-body);
@@ -118,8 +121,11 @@ limitations under the License.
.inputField textarea:not(:placeholder-shown) + label,
.inputField.prefix textarea + label {
background-color: var(--cpd-color-bg-canvas-default);
- transition: font-size 0.25s ease-out 0s, color 0.25s ease-out 0s,
- top 0.25s ease-out 0s, background-color 0.25s ease-out 0s;
+ transition:
+ font-size 0.25s ease-out 0s,
+ color 0.25s ease-out 0s,
+ top 0.25s ease-out 0s,
+ background-color 0.25s ease-out 0s;
font-size: var(--font-size-micro);
top: -13px;
padding: 0 2px;
diff --git a/src/input/Input.tsx b/src/input/Input.tsx
index b6826681..24bea83f 100644
--- a/src/input/Input.tsx
+++ b/src/input/Input.tsx
@@ -44,7 +44,7 @@ export function FieldRow({
className={classNames(
styles.fieldRow,
{ [styles.rightAlign]: rightAlign },
- className
+ className,
)}
>
{children}
@@ -102,7 +102,7 @@ export const InputField = forwardRef<
disabled,
...rest
},
- ref
+ ref,
) => {
const descriptionId = useId();
@@ -114,7 +114,7 @@ export const InputField = forwardRef<
[styles.prefix]: !!prefix,
[styles.disabled]: disabled,
},
- className
+ className,
)}
>
{prefix && {prefix} }
@@ -163,7 +163,7 @@ export const InputField = forwardRef<
)}
);
- }
+ },
);
interface ErrorMessageProps {
diff --git a/src/input/SelectInput.tsx b/src/input/SelectInput.tsx
index 0feb346e..f0817582 100644
--- a/src/input/SelectInput.tsx
+++ b/src/input/SelectInput.tsx
@@ -38,7 +38,7 @@ export function SelectInput(props: Props): JSX.Element {
const { labelProps, triggerProps, valueProps, menuProps } = useSelect(
props,
state,
- ref
+ ref,
);
const { buttonProps } = useButton(triggerProps, ref);
diff --git a/src/input/StarRatingInput.tsx b/src/input/StarRatingInput.tsx
index 8667998e..cd52082c 100644
--- a/src/input/StarRatingInput.tsx
+++ b/src/input/StarRatingInput.tsx
@@ -41,8 +41,8 @@ export function StarRatingInput({
return (
setHover(index)}
- onMouseLeave={() => setHover(rating)}
+ onMouseEnter={(): void => setHover(index)}
+ onMouseLeave={(): void => setHover(rating)}
key={index}
>
{
+ onChange={(_ev): void => {
setRating(index);
onChange(index);
}}
diff --git a/src/livekit/MediaDevicesContext.tsx b/src/livekit/MediaDevicesContext.tsx
index 1605edf3..df5b3fd9 100644
--- a/src/livekit/MediaDevicesContext.tsx
+++ b/src/livekit/MediaDevicesContext.tsx
@@ -51,8 +51,8 @@ export interface MediaDevices {
// Cargo-culted from @livekit/components-react
function useObservableState
(
observable: Observable | undefined,
- startWith: T
-) {
+ startWith: T,
+): T {
const [state, setState] = useState(startWith);
useEffect(() => {
// observable state doesn't run in SSR
@@ -67,7 +67,7 @@ function useMediaDevice(
kind: MediaDeviceKind,
fallbackDevice: string | undefined,
usingNames: boolean,
- alwaysDefault: boolean = false
+ alwaysDefault: boolean = false,
): MediaDevice {
// Make sure we don't needlessly reset to a device observer without names,
// once permissions are already given
@@ -83,7 +83,7 @@ function useMediaDevice(
// kind, which then results in multiple permissions requests.
const deviceObserver = useMemo(
() => createMediaDeviceObserver(kind, requestPermissions),
- [kind, requestPermissions]
+ [kind, requestPermissions],
);
const available = useObservableState(deviceObserver, []);
const [selectedId, select] = useState(fallbackDevice);
@@ -143,18 +143,18 @@ export const MediaDevicesProvider: FC = ({ children }) => {
const audioInput = useMediaDevice(
"audioinput",
audioInputSetting,
- usingNames
+ usingNames,
);
const audioOutput = useMediaDevice(
"audiooutput",
audioOutputSetting,
useOutputNames,
- alwaysUseDefaultAudio
+ alwaysUseDefaultAudio,
);
const videoInput = useMediaDevice(
"videoinput",
videoInputSetting,
- usingNames
+ usingNames,
);
useEffect(() => {
@@ -176,11 +176,11 @@ export const MediaDevicesProvider: FC = ({ children }) => {
const startUsingDeviceNames = useCallback(
() => setNumCallersUsingNames((n) => n + 1),
- [setNumCallersUsingNames]
+ [setNumCallersUsingNames],
);
const stopUsingDeviceNames = useCallback(
() => setNumCallersUsingNames((n) => n - 1),
- [setNumCallersUsingNames]
+ [setNumCallersUsingNames],
);
const context: MediaDevices = useMemo(
@@ -197,7 +197,7 @@ export const MediaDevicesProvider: FC = ({ children }) => {
videoInput,
startUsingDeviceNames,
stopUsingDeviceNames,
- ]
+ ],
);
return (
@@ -207,7 +207,8 @@ export const MediaDevicesProvider: FC = ({ children }) => {
);
};
-export const useMediaDevices = () => useContext(MediaDevicesContext);
+export const useMediaDevices = (): MediaDevices =>
+ useContext(MediaDevicesContext);
/**
* React hook that requests for the media devices context to be populated with
@@ -215,7 +216,10 @@ export const useMediaDevices = () => useContext(MediaDevicesContext);
* default because it may involve requesting additional permissions from the
* user.
*/
-export const useMediaDeviceNames = (context: MediaDevices, enabled = true) =>
+export const useMediaDeviceNames = (
+ context: MediaDevices,
+ enabled = true,
+): void =>
useEffect(() => {
if (enabled) {
context.startUsingDeviceNames();
diff --git a/src/livekit/openIDSFU.ts b/src/livekit/openIDSFU.ts
index d10f56fb..8b447485 100644
--- a/src/livekit/openIDSFU.ts
+++ b/src/livekit/openIDSFU.ts
@@ -42,14 +42,14 @@ export type OpenIDClientParts = Pick<
export function useOpenIDSFU(
client: OpenIDClientParts,
- rtcSession: MatrixRTCSession
-) {
+ rtcSession: MatrixRTCSession,
+): SFUConfig | undefined {
const [sfuConfig, setSFUConfig] = useState(undefined);
const activeFocus = useActiveFocus(rtcSession);
useEffect(() => {
- (async () => {
+ (async (): Promise => {
const sfuConfig = activeFocus
? await getSFUConfigWithOpenID(client, activeFocus)
: undefined;
@@ -62,20 +62,20 @@ export function useOpenIDSFU(
export async function getSFUConfigWithOpenID(
client: OpenIDClientParts,
- activeFocus: LivekitFocus
+ activeFocus: LivekitFocus,
): Promise {
const openIdToken = await client.getOpenIdToken();
logger.debug("Got openID token", openIdToken);
try {
logger.info(
- `Trying to get JWT from call's active focus URL of ${activeFocus.livekit_service_url}...`
+ `Trying to get JWT from call's active focus URL of ${activeFocus.livekit_service_url}...`,
);
const sfuConfig = await getLiveKitJWT(
client,
activeFocus.livekit_service_url,
activeFocus.livekit_alias,
- openIdToken
+ openIdToken,
);
logger.info(`Got JWT from call's active focus URL.`);
@@ -83,7 +83,7 @@ export async function getSFUConfigWithOpenID(
} catch (e) {
logger.warn(
`Failed to get JWT from RTC session's active focus URL of ${activeFocus.livekit_service_url}.`,
- e
+ e,
);
return undefined;
}
@@ -93,7 +93,7 @@ async function getLiveKitJWT(
client: OpenIDClientParts,
livekitServiceURL: string,
roomName: string,
- openIDToken: IOpenIDToken
+ openIDToken: IOpenIDToken,
): Promise {
try {
const res = await fetch(livekitServiceURL + "/sfu/get", {
diff --git a/src/livekit/options.ts b/src/livekit/options.ts
index 7f9695f1..00f19b12 100644
--- a/src/livekit/options.ts
+++ b/src/livekit/options.ts
@@ -1,3 +1,19 @@
+/*
+Copyright 2023 New Vector Ltd
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
import {
AudioPresets,
DefaultReconnectPolicy,
diff --git a/src/livekit/useECConnectionState.ts b/src/livekit/useECConnectionState.ts
index e9298eb7..0b18cd10 100644
--- a/src/livekit/useECConnectionState.ts
+++ b/src/livekit/useECConnectionState.ts
@@ -51,7 +51,7 @@ async function doConnect(
livekitRoom: Room,
sfuConfig: SFUConfig,
audioEnabled: boolean,
- audioOptions: AudioCaptureOptions
+ audioOptions: AudioCaptureOptions,
): Promise {
await livekitRoom!.connect(sfuConfig!.url, sfuConfig!.jwt);
@@ -76,12 +76,12 @@ export function useECConnectionState(
initialAudioOptions: AudioCaptureOptions,
initialAudioEnabled: boolean,
livekitRoom?: Room,
- sfuConfig?: SFUConfig
+ sfuConfig?: SFUConfig,
): ECConnectionState {
const [connState, setConnState] = useState(
sfuConfig && livekitRoom
? livekitRoom.state
- : ECAddonConnectionState.ECWaiting
+ : ECAddonConnectionState.ECWaiting,
);
const [isSwitchingFocus, setSwitchingFocus] = useState(false);
@@ -116,10 +116,10 @@ export function useECConnectionState(
!sfuConfigEquals(currentSFUConfig.current, sfuConfig)
) {
logger.info(
- `SFU config changed! URL was ${currentSFUConfig.current?.url} now ${sfuConfig?.url}`
+ `SFU config changed! URL was ${currentSFUConfig.current?.url} now ${sfuConfig?.url}`,
);
- (async () => {
+ (async (): Promise => {
setSwitchingFocus(true);
await livekitRoom?.disconnect();
setIsInDoConnect(true);
@@ -128,7 +128,7 @@ export function useECConnectionState(
livekitRoom!,
sfuConfig!,
initialAudioEnabled,
- initialAudioOptions
+ initialAudioOptions,
);
} finally {
setIsInDoConnect(false);
@@ -149,7 +149,7 @@ export function useECConnectionState(
livekitRoom!,
sfuConfig!,
initialAudioEnabled,
- initialAudioOptions
+ initialAudioOptions,
).finally(() => setIsInDoConnect(false));
}
diff --git a/src/livekit/useLiveKit.ts b/src/livekit/useLiveKit.ts
index 5396d4b3..c8a5e401 100644
--- a/src/livekit/useLiveKit.ts
+++ b/src/livekit/useLiveKit.ts
@@ -52,7 +52,7 @@ interface UseLivekitResult {
export function useLiveKit(
muteStates: MuteStates,
sfuConfig?: SFUConfig,
- e2eeConfig?: E2EEConfig
+ e2eeConfig?: E2EEConfig,
): UseLivekitResult {
const e2eeOptions = useMemo(() => {
if (!e2eeConfig?.sharedKey) return undefined;
@@ -67,7 +67,7 @@ export function useLiveKit(
if (!e2eeConfig?.sharedKey || !e2eeOptions) return;
(e2eeOptions.keyProvider as ExternalE2EEKeyProvider).setKey(
- e2eeConfig?.sharedKey
+ e2eeConfig?.sharedKey,
);
}, [e2eeOptions, e2eeConfig?.sharedKey]);
@@ -93,7 +93,7 @@ export function useLiveKit(
},
e2ee: e2eeOptions,
}),
- [e2eeOptions]
+ [e2eeOptions],
);
// useECConnectionState creates and publishes an audio track by hand. To keep
@@ -131,7 +131,7 @@ export function useLiveKit(
},
initialMuteStates.current.audio.enabled,
room,
- sfuConfig
+ sfuConfig,
);
// Unblock audio once the connection is finished
@@ -154,7 +154,7 @@ export function useLiveKit(
audio: muteStates.audio.enabled,
video: muteStates.video.enabled,
};
- const syncMuteStateAudio = async () => {
+ const syncMuteStateAudio = async (): Promise => {
if (
participant.isMicrophoneEnabled !== buttonEnabled.current.audio &&
!audioMuteUpdating.current
@@ -174,7 +174,7 @@ export function useLiveKit(
syncMuteStateAudio();
}
};
- const syncMuteStateVideo = async () => {
+ const syncMuteStateVideo = async (): Promise => {
if (
participant.isCameraEnabled !== buttonEnabled.current.video &&
!videoMuteUpdating.current
@@ -198,7 +198,7 @@ export function useLiveKit(
useEffect(() => {
// Sync the requested devices with LiveKit's devices
if (room !== undefined && connectionState === ConnectionState.Connected) {
- const syncDevice = (kind: MediaDeviceKind, device: MediaDevice) => {
+ const syncDevice = (kind: MediaDeviceKind, device: MediaDevice): void => {
const id = device.selectedId;
// Detect if we're trying to use chrome's default device, in which case
@@ -215,11 +215,11 @@ export function useLiveKit(
room.options.audioCaptureDefaults?.deviceId === "default"
) {
const activeMicTrack = Array.from(
- room.localParticipant.audioTracks.values()
+ room.localParticipant.audioTracks.values(),
).find((d) => d.source === Track.Source.Microphone)?.track;
const defaultDevice = device.available.find(
- (d) => d.deviceId === "default"
+ (d) => d.deviceId === "default",
);
if (
defaultDevice &&
@@ -245,7 +245,7 @@ export function useLiveKit(
room
.switchActiveDevice(kind, id)
.catch((e) =>
- logger.error(`Failed to sync ${kind} device with LiveKit`, e)
+ logger.error(`Failed to sync ${kind} device with LiveKit`, e),
);
}
}
diff --git a/src/main.tsx b/src/main.tsx
index 4e962181..ed64a09c 100644
--- a/src/main.tsx
+++ b/src/main.tsx
@@ -30,7 +30,7 @@ import {
setLogLevel,
} from "livekit-client";
-import App from "./App";
+import { App } from "./App";
import { init as initRageshake } from "./settings/rageshake";
import { Initializer } from "./initializer";
@@ -48,7 +48,7 @@ if (!window.isSecureContext) {
fatalError = new Error(
"This app cannot run in an insecure context. To fix this, access the app " +
"via a local loopback address, or serve it over HTTPS.\n" +
- "https://developer.mozilla.org/en-US/docs/Web/Security/Secure_Contexts"
+ "https://developer.mozilla.org/en-US/docs/Web/Security/Secure_Contexts",
);
} else if (!navigator.mediaDevices) {
fatalError = new Error("Your browser does not support WebRTC.");
@@ -66,5 +66,5 @@ const history = createBrowserHistory();
root.render(
-
+ ,
);
diff --git a/src/matrix-utils.ts b/src/matrix-utils.ts
index 54105519..52c55959 100644
--- a/src/matrix-utils.ts
+++ b/src/matrix-utils.ts
@@ -42,7 +42,7 @@ export const fallbackICEServerAllowed =
import.meta.env.VITE_FALLBACK_STUN_ALLOWED === "true";
export class CryptoStoreIntegrityError extends Error {
- constructor() {
+ public constructor() {
super("Crypto store data was expected, but none was found");
}
}
@@ -54,13 +54,13 @@ const SYNC_STORE_NAME = "element-call-sync";
// (It's a good opportunity to make the database names consistent.)
const CRYPTO_STORE_NAME = "element-call-crypto";
-function waitForSync(client: MatrixClient) {
+function waitForSync(client: MatrixClient): Promise {
return new Promise((resolve, reject) => {
const onSync = (
state: SyncState,
_old: SyncState | null,
- data?: ISyncStateData
- ) => {
+ data?: ISyncStateData,
+ ): void => {
if (state === "PREPARED") {
client.removeListener(ClientEvent.Sync, onSync);
resolve();
@@ -83,7 +83,7 @@ function secureRandomString(entropyBytes: number): string {
// yet) so just use the built-in one and convert, replace the chars and strip the
// padding from the end (otherwise we'd need to pull in another dependency).
return btoa(
- key.reduce((acc, current) => acc + String.fromCharCode(current), "")
+ key.reduce((acc, current) => acc + String.fromCharCode(current), ""),
)
.replace("+", "-")
.replace("/", "_")
@@ -101,7 +101,7 @@ function secureRandomString(entropyBytes: number): string {
*/
export async function initClient(
clientOptions: ICreateClientOpts,
- restore: boolean
+ restore: boolean,
): Promise {
await loadOlm();
@@ -127,7 +127,7 @@ export async function initClient(
// Chrome supports it. (It bundles them fine in production mode.)
workerFactory: import.meta.env.DEV
? undefined
- : () => new IndexedDBWorker(),
+ : (): Worker => new IndexedDBWorker(),
});
} else if (localStorage) {
baseOpts.store = new MemoryStore({ localStorage });
@@ -148,7 +148,7 @@ export async function initClient(
if (indexedDB) {
const cryptoStoreExists = await IndexedDBCryptoStore.exists(
indexedDB,
- CRYPTO_STORE_NAME
+ CRYPTO_STORE_NAME,
);
if (!cryptoStoreExists) throw new CryptoStoreIntegrityError();
} else if (localStorage) {
@@ -164,7 +164,7 @@ export async function initClient(
if (indexedDB) {
baseOpts.cryptoStore = new IndexedDBCryptoStore(
indexedDB,
- CRYPTO_STORE_NAME
+ CRYPTO_STORE_NAME,
);
} else if (localStorage) {
baseOpts.cryptoStore = new LocalStorageCryptoStore(localStorage);
@@ -198,7 +198,7 @@ export async function initClient(
} catch (error) {
logger.error(
"Error starting matrix client store. Falling back to memory store.",
- error
+ error,
);
client.store = new MemoryStore({ localStorage });
await client.store.startup();
@@ -268,7 +268,7 @@ export function roomNameFromRoomId(roomId: string): string {
.substring(1)
.split("-")
.map((part) =>
- part.length > 0 ? part.charAt(0).toUpperCase() + part.slice(1) : part
+ part.length > 0 ? part.charAt(0).toUpperCase() + part.slice(1) : part,
)
.join(" ")
.toLowerCase();
@@ -297,7 +297,7 @@ interface CreateRoomResult {
export async function createRoom(
client: MatrixClient,
name: string,
- e2ee: boolean
+ e2ee: boolean,
): Promise {
logger.log(`Creating room for group call`);
const createPromise = client.createRoom({
@@ -332,7 +332,7 @@ export async function createRoom(
// Wait for the room to arrive
await new Promise((resolve, reject) => {
- const onRoom = async (room: Room) => {
+ const onRoom = async (room: Room): Promise => {
if (room.roomId === (await createPromise).room_id) {
resolve();
cleanUp();
@@ -343,7 +343,7 @@ export async function createRoom(
cleanUp();
});
- const cleanUp = () => {
+ const cleanUp = (): void => {
client.off(ClientEvent.Room, onRoom);
};
client.on(ClientEvent.Room, onRoom);
@@ -358,7 +358,7 @@ export async function createRoom(
GroupCallType.Video,
false,
GroupCallIntent.Room,
- true
+ true,
);
let password;
@@ -366,7 +366,7 @@ export async function createRoom(
password = secureRandomString(16);
setLocalStorageItem(
getRoomSharedKeyLocalStorageKey(result.room_id),
- password
+ password,
);
}
@@ -386,7 +386,7 @@ export async function createRoom(
export function getAbsoluteRoomUrl(
roomId: string,
roomName?: string,
- password?: string
+ password?: string,
): string {
return `${window.location.protocol}//${
window.location.host
@@ -402,7 +402,7 @@ export function getAbsoluteRoomUrl(
export function getRelativeRoomUrl(
roomId: string,
roomName?: string,
- password?: string
+ password?: string,
): string {
// The password shouldn't need URL encoding here (we generate URL-safe ones) but encode
// it in case it came from another client that generated a non url-safe one
@@ -419,7 +419,7 @@ export function getRelativeRoomUrl(
export function getAvatarUrl(
client: MatrixClient,
mxcUrl: string,
- avatarSize = 96
+ avatarSize = 96,
): string {
const width = Math.floor(avatarSize * window.devicePixelRatio);
const height = Math.floor(avatarSize * window.devicePixelRatio);
diff --git a/src/media-utils.ts b/src/media-utils.ts
index ec829c76..74e5ca33 100644
--- a/src/media-utils.ts
+++ b/src/media-utils.ts
@@ -23,10 +23,10 @@ limitations under the License.
export async function findDeviceByName(
deviceName: string,
kind: MediaDeviceKind,
- devices: MediaDeviceInfo[]
+ devices: MediaDeviceInfo[],
): Promise {
const deviceInfo = devices.find(
- (d) => d.kind === kind && d.label === deviceName
+ (d) => d.kind === kind && d.label === deviceName,
);
return deviceInfo?.deviceId;
}
diff --git a/src/otel/OTelCall.ts b/src/otel/OTelCall.ts
index c7a7fb1a..13157763 100644
--- a/src/otel/OTelCall.ts
+++ b/src/otel/OTelCall.ts
@@ -44,65 +44,65 @@ export class OTelCall {
OTelCallAbstractMediaStreamSpan
>();
- constructor(
+ public constructor(
public userId: string,
public deviceId: string,
public call: MatrixCall,
- public span: Span
+ public span: Span,
) {
if (call.peerConn) {
this.addCallPeerConnListeners();
} else {
this.call.once(
CallEvent.PeerConnectionCreated,
- this.addCallPeerConnListeners
+ this.addCallPeerConnListeners,
);
}
}
- public dispose() {
+ public dispose(): void {
this.call.peerConn?.removeEventListener(
"connectionstatechange",
- this.onCallConnectionStateChanged
+ this.onCallConnectionStateChanged,
);
this.call.peerConn?.removeEventListener(
"signalingstatechange",
- this.onCallSignalingStateChanged
+ this.onCallSignalingStateChanged,
);
this.call.peerConn?.removeEventListener(
"iceconnectionstatechange",
- this.onIceConnectionStateChanged
+ this.onIceConnectionStateChanged,
);
this.call.peerConn?.removeEventListener(
"icegatheringstatechange",
- this.onIceGatheringStateChanged
+ this.onIceGatheringStateChanged,
);
this.call.peerConn?.removeEventListener(
"icecandidateerror",
- this.onIceCandidateError
+ this.onIceCandidateError,
);
}
private addCallPeerConnListeners = (): void => {
this.call.peerConn?.addEventListener(
"connectionstatechange",
- this.onCallConnectionStateChanged
+ this.onCallConnectionStateChanged,
);
this.call.peerConn?.addEventListener(
"signalingstatechange",
- this.onCallSignalingStateChanged
+ this.onCallSignalingStateChanged,
);
this.call.peerConn?.addEventListener(
"iceconnectionstatechange",
- this.onIceConnectionStateChanged
+ this.onIceConnectionStateChanged,
);
this.call.peerConn?.addEventListener(
"icegatheringstatechange",
- this.onIceGatheringStateChanged
+ this.onIceGatheringStateChanged,
);
this.call.peerConn?.addEventListener(
"icecandidateerror",
- this.onIceCandidateError
+ this.onIceCandidateError,
);
};
@@ -147,8 +147,8 @@ export class OTelCall {
new OTelCallFeedMediaStreamSpan(
ElementCallOpenTelemetry.instance,
this.span,
- feed
- )
+ feed,
+ ),
);
}
this.trackFeedSpan.get(feed.stream)?.update(feed);
@@ -171,13 +171,13 @@ export class OTelCall {
new OTelCallTransceiverMediaStreamSpan(
ElementCallOpenTelemetry.instance,
this.span,
- transStats
- )
+ transStats,
+ ),
);
}
this.trackTransceiverSpan.get(transStats.mid)?.update(transStats);
prvTransSpan = prvTransSpan.filter(
- (prvStreamId) => prvStreamId !== transStats.mid
+ (prvStreamId) => prvStreamId !== transStats.mid,
);
});
@@ -190,7 +190,7 @@ export class OTelCall {
public end(): void {
this.trackFeedSpan.forEach((feedSpan) => feedSpan.end());
this.trackTransceiverSpan.forEach((transceiverSpan) =>
- transceiverSpan.end()
+ transceiverSpan.end(),
);
this.span.end();
}
diff --git a/src/otel/OTelCallAbstractMediaStreamSpan.ts b/src/otel/OTelCallAbstractMediaStreamSpan.ts
index aa77051d..c34f963c 100644
--- a/src/otel/OTelCallAbstractMediaStreamSpan.ts
+++ b/src/otel/OTelCallAbstractMediaStreamSpan.ts
@@ -1,3 +1,19 @@
+/*
+Copyright 2023 New Vector Ltd
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
import opentelemetry, { Span } from "@opentelemetry/api";
import { TrackStats } from "matrix-js-sdk/src/webrtc/stats/statsReport";
@@ -14,13 +30,13 @@ export abstract class OTelCallAbstractMediaStreamSpan {
public readonly span;
public constructor(
- readonly oTel: ElementCallOpenTelemetry,
- readonly callSpan: Span,
- protected readonly type: string
+ protected readonly oTel: ElementCallOpenTelemetry,
+ protected readonly callSpan: Span,
+ protected readonly type: string,
) {
const ctx = opentelemetry.trace.setSpan(
opentelemetry.context.active(),
- callSpan
+ callSpan,
);
const options = {
links: [
@@ -32,13 +48,13 @@ export abstract class OTelCallAbstractMediaStreamSpan {
this.span = oTel.tracer.startSpan(this.type, options, ctx);
}
- protected upsertTrackSpans(tracks: TrackStats[]) {
+ protected upsertTrackSpans(tracks: TrackStats[]): void {
let prvTracks: TrackId[] = [...this.trackSpans.keys()];
tracks.forEach((t) => {
if (!this.trackSpans.has(t.id)) {
this.trackSpans.set(
t.id,
- new OTelCallMediaStreamTrackSpan(this.oTel, this.span, t)
+ new OTelCallMediaStreamTrackSpan(this.oTel, this.span, t),
);
}
this.trackSpans.get(t.id)?.update(t);
diff --git a/src/otel/OTelCallFeedMediaStreamSpan.ts b/src/otel/OTelCallFeedMediaStreamSpan.ts
index 6023fa65..02dd2f86 100644
--- a/src/otel/OTelCallFeedMediaStreamSpan.ts
+++ b/src/otel/OTelCallFeedMediaStreamSpan.ts
@@ -1,3 +1,19 @@
+/*
+Copyright 2023 New Vector Ltd
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
import { Span } from "@opentelemetry/api";
import {
CallFeedStats,
@@ -10,10 +26,10 @@ import { OTelCallAbstractMediaStreamSpan } from "./OTelCallAbstractMediaStreamSp
export class OTelCallFeedMediaStreamSpan extends OTelCallAbstractMediaStreamSpan {
private readonly prev: { isAudioMuted: boolean; isVideoMuted: boolean };
- constructor(
- readonly oTel: ElementCallOpenTelemetry,
- readonly callSpan: Span,
- callFeed: CallFeedStats
+ public constructor(
+ protected readonly oTel: ElementCallOpenTelemetry,
+ protected readonly callSpan: Span,
+ callFeed: CallFeedStats,
) {
const postFix =
callFeed.type === "local" && callFeed.prefix === "from-call-feed"
diff --git a/src/otel/OTelCallMediaStreamTrackSpan.ts b/src/otel/OTelCallMediaStreamTrackSpan.ts
index 935e22fc..72fee9b4 100644
--- a/src/otel/OTelCallMediaStreamTrackSpan.ts
+++ b/src/otel/OTelCallMediaStreamTrackSpan.ts
@@ -1,3 +1,19 @@
+/*
+Copyright 2023 New Vector Ltd
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
import { TrackStats } from "matrix-js-sdk/src/webrtc/stats/statsReport";
import opentelemetry, { Span } from "@opentelemetry/api";
@@ -8,13 +24,13 @@ export class OTelCallMediaStreamTrackSpan {
private prev: TrackStats;
public constructor(
- readonly oTel: ElementCallOpenTelemetry,
- readonly streamSpan: Span,
- data: TrackStats
+ protected readonly oTel: ElementCallOpenTelemetry,
+ protected readonly streamSpan: Span,
+ data: TrackStats,
) {
const ctx = opentelemetry.trace.setSpan(
opentelemetry.context.active(),
- streamSpan
+ streamSpan,
);
const options = {
links: [
diff --git a/src/otel/OTelCallTransceiverMediaStreamSpan.ts b/src/otel/OTelCallTransceiverMediaStreamSpan.ts
index 97006cd8..2be5dbe6 100644
--- a/src/otel/OTelCallTransceiverMediaStreamSpan.ts
+++ b/src/otel/OTelCallTransceiverMediaStreamSpan.ts
@@ -1,3 +1,19 @@
+/*
+Copyright 2023 New Vector Ltd
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
import { Span } from "@opentelemetry/api";
import {
TrackStats,
@@ -13,10 +29,10 @@ export class OTelCallTransceiverMediaStreamSpan extends OTelCallAbstractMediaStr
currentDirection: string;
};
- constructor(
- readonly oTel: ElementCallOpenTelemetry,
- readonly callSpan: Span,
- stats: TransceiverStats
+ public constructor(
+ protected readonly oTel: ElementCallOpenTelemetry,
+ protected readonly callSpan: Span,
+ stats: TransceiverStats,
) {
super(oTel, callSpan, `matrix.call.transceiver.${stats.mid}`);
this.span.setAttribute("transceiver.mid", stats.mid);
diff --git a/src/otel/OTelGroupCallMembership.ts b/src/otel/OTelGroupCallMembership.ts
index b60d5aa9..b700e2bc 100644
--- a/src/otel/OTelGroupCallMembership.ts
+++ b/src/otel/OTelGroupCallMembership.ts
@@ -62,7 +62,10 @@ export class OTelGroupCallMembership {
};
private readonly speakingSpans = new Map>();
- constructor(private groupCall: GroupCall, client: MatrixClient) {
+ public constructor(
+ private groupCall: GroupCall,
+ client: MatrixClient,
+ ) {
const clientId = client.getUserId();
if (clientId) {
this.myUserId = clientId;
@@ -76,14 +79,14 @@ export class OTelGroupCallMembership {
this.groupCall.on(GroupCallEvent.CallsChanged, this.onCallsChanged);
}
- dispose() {
+ public dispose(): void {
this.groupCall.removeListener(
GroupCallEvent.CallsChanged,
- this.onCallsChanged
+ this.onCallsChanged,
);
}
- public onJoinCall() {
+ public onJoinCall(): void {
if (!ElementCallOpenTelemetry.instance) return;
if (this.callMembershipSpan !== undefined) {
logger.warn("Call membership span is already started");
@@ -93,28 +96,28 @@ export class OTelGroupCallMembership {
// Create the main span that tracks the time we intend to be in the call
this.callMembershipSpan =
ElementCallOpenTelemetry.instance.tracer.startSpan(
- "matrix.groupCallMembership"
+ "matrix.groupCallMembership",
);
this.callMembershipSpan.setAttribute(
"matrix.confId",
- this.groupCall.groupCallId
+ this.groupCall.groupCallId,
);
this.callMembershipSpan.setAttribute("matrix.userId", this.myUserId);
this.callMembershipSpan.setAttribute("matrix.deviceId", this.myDeviceId);
this.callMembershipSpan.setAttribute(
"matrix.displayName",
- this.myMember ? this.myMember.name : "unknown-name"
+ this.myMember ? this.myMember.name : "unknown-name",
);
this.groupCallContext = opentelemetry.trace.setSpan(
opentelemetry.context.active(),
- this.callMembershipSpan
+ this.callMembershipSpan,
);
this.callMembershipSpan?.addEvent("matrix.joinCall");
}
- public onLeaveCall() {
+ public onLeaveCall(): void {
if (this.callMembershipSpan === undefined) {
logger.warn("Call membership span is already ended");
return;
@@ -127,7 +130,7 @@ export class OTelGroupCallMembership {
this.groupCallContext = undefined;
}
- public onUpdateRoomState(event: MatrixEvent) {
+ public onUpdateRoomState(event: MatrixEvent): void {
if (
!event ||
(!event.getType().startsWith("m.call") &&
@@ -138,11 +141,11 @@ export class OTelGroupCallMembership {
this.callMembershipSpan?.addEvent(
`matrix.roomStateEvent_${event.getType()}`,
- ObjectFlattener.flattenVoipEvent(event.getContent())
+ ObjectFlattener.flattenVoipEvent(event.getContent()),
);
}
- public onCallsChanged = (calls: CallsByUserAndDevice) => {
+ public onCallsChanged(calls: CallsByUserAndDevice): void {
for (const [userId, userCalls] of calls.entries()) {
for (const [deviceId, call] of userCalls.entries()) {
if (!this.callsByCallId.has(call.callId)) {
@@ -150,7 +153,7 @@ export class OTelGroupCallMembership {
const span = ElementCallOpenTelemetry.instance.tracer.startSpan(
`matrix.call`,
undefined,
- this.groupCallContext
+ this.groupCallContext,
);
// XXX: anonymity
span.setAttribute("matrix.call.target.userId", userId);
@@ -160,7 +163,7 @@ export class OTelGroupCallMembership {
span.setAttribute("matrix.call.target.displayName", displayName);
this.callsByCallId.set(
call.callId,
- new OTelCall(userId, deviceId, call, span)
+ new OTelCall(userId, deviceId, call, span),
);
}
}
@@ -179,9 +182,9 @@ export class OTelGroupCallMembership {
this.callsByCallId.delete(callTrackingInfo.call.callId);
}
}
- };
+ }
- public onCallStateChange(call: MatrixCall, newState: CallState) {
+ public onCallStateChange(call: MatrixCall, newState: CallState): void {
const callTrackingInfo = this.callsByCallId.get(call.callId);
if (!callTrackingInfo) {
logger.error(`Got call state change for unknown call ID ${call.callId}`);
@@ -193,7 +196,7 @@ export class OTelGroupCallMembership {
});
}
- public onSendEvent(call: MatrixCall, event: VoipEvent) {
+ public onSendEvent(call: MatrixCall, event: VoipEvent): void {
const eventType = event.eventType as string;
if (
!eventType.startsWith("m.call") &&
@@ -210,17 +213,17 @@ export class OTelGroupCallMembership {
if (event.type === "toDevice") {
callTrackingInfo.span.addEvent(
`matrix.sendToDeviceEvent_${event.eventType}`,
- ObjectFlattener.flattenVoipEvent(event)
+ ObjectFlattener.flattenVoipEvent(event),
);
} else if (event.type === "sendEvent") {
callTrackingInfo.span.addEvent(
`matrix.sendToRoomEvent_${event.eventType}`,
- ObjectFlattener.flattenVoipEvent(event)
+ ObjectFlattener.flattenVoipEvent(event),
);
}
}
- public onReceivedVoipEvent(event: MatrixEvent) {
+ public onReceivedVoipEvent(event: MatrixEvent): void {
// These come straight from CallEventHandler so don't have
// a call already associated (in principle we could receive
// events for calls we don't know about).
@@ -239,7 +242,7 @@ export class OTelGroupCallMembership {
"matrix.receive_voip_event_unknown_callid",
{
"sender.userId": event.getSender(),
- }
+ },
);
logger.error("Received call event for unknown call ID " + callId);
return;
@@ -251,37 +254,41 @@ export class OTelGroupCallMembership {
});
}
- public onToggleMicrophoneMuted(newValue: boolean) {
+ public onToggleMicrophoneMuted(newValue: boolean): void {
this.callMembershipSpan?.addEvent("matrix.toggleMicMuted", {
"matrix.microphone.muted": newValue,
});
}
- public onSetMicrophoneMuted(setMuted: boolean) {
+ public onSetMicrophoneMuted(setMuted: boolean): void {
this.callMembershipSpan?.addEvent("matrix.setMicMuted", {
"matrix.microphone.muted": setMuted,
});
}
- public onToggleLocalVideoMuted(newValue: boolean) {
+ public onToggleLocalVideoMuted(newValue: boolean): void {
this.callMembershipSpan?.addEvent("matrix.toggleVidMuted", {
"matrix.video.muted": newValue,
});
}
- public onSetLocalVideoMuted(setMuted: boolean) {
+ public onSetLocalVideoMuted(setMuted: boolean): void {
this.callMembershipSpan?.addEvent("matrix.setVidMuted", {
"matrix.video.muted": setMuted,
});
}
- public onToggleScreensharing(newValue: boolean) {
+ public onToggleScreensharing(newValue: boolean): void {
this.callMembershipSpan?.addEvent("matrix.setVidMuted", {
"matrix.screensharing.enabled": newValue,
});
}
- public onSpeaking(member: RoomMember, deviceId: string, speaking: boolean) {
+ public onSpeaking(
+ member: RoomMember,
+ deviceId: string,
+ speaking: boolean,
+ ): void {
if (speaking) {
// Ensure that there's an audio activity span for this speaker
let deviceMap = this.speakingSpans.get(member);
@@ -294,7 +301,7 @@ export class OTelGroupCallMembership {
const span = ElementCallOpenTelemetry.instance.tracer.startSpan(
"matrix.audioActivity",
undefined,
- this.groupCallContext
+ this.groupCallContext,
);
span.setAttribute("matrix.userId", member.userId);
span.setAttribute("matrix.displayName", member.rawDisplayName);
@@ -311,7 +318,7 @@ export class OTelGroupCallMembership {
}
}
- public onCallError(error: CallError, call: MatrixCall) {
+ public onCallError(error: CallError, call: MatrixCall): void {
const callTrackingInfo = this.callsByCallId.get(call.callId);
if (!callTrackingInfo) {
logger.error(`Got error for unknown call ID ${call.callId}`);
@@ -321,17 +328,19 @@ export class OTelGroupCallMembership {
callTrackingInfo.span.recordException(error);
}
- public onGroupCallError(error: GroupCallError) {
+ public onGroupCallError(error: GroupCallError): void {
this.callMembershipSpan?.recordException(error);
}
- public onUndecryptableToDevice(event: MatrixEvent) {
+ public onUndecryptableToDevice(event: MatrixEvent): void {
this.callMembershipSpan?.addEvent("matrix.toDevice.undecryptable", {
"sender.userId": event.getSender(),
});
}
- public onCallFeedStatsReport(report: GroupCallStatsReport) {
+ public onCallFeedStatsReport(
+ report: GroupCallStatsReport,
+ ): void {
if (!ElementCallOpenTelemetry.instance) return;
let call: OTelCall | undefined;
const callId = report.report?.callId;
@@ -348,10 +357,10 @@ export class OTelGroupCallMembership {
"call.opponentMemberId": report.report?.opponentMemberId
? report.report?.opponentMemberId
: "unknown",
- }
+ },
);
logger.error(
- `Received ${OTelStatsReportType.CallFeedReport} with unknown call ID: ${callId}`
+ `Received ${OTelStatsReportType.CallFeedReport} with unknown call ID: ${callId}`,
);
return;
} else {
@@ -361,26 +370,26 @@ export class OTelGroupCallMembership {
}
public onConnectionStatsReport(
- statsReport: GroupCallStatsReport
- ) {
+ statsReport: GroupCallStatsReport,
+ ): void {
this.buildCallStatsSpan(
OTelStatsReportType.ConnectionReport,
- statsReport.report
+ statsReport.report,
);
}
public onByteSentStatsReport(
- statsReport: GroupCallStatsReport
- ) {
+ statsReport: GroupCallStatsReport,
+ ): void {
this.buildCallStatsSpan(
OTelStatsReportType.ByteSentReport,
- statsReport.report
+ statsReport.report,
);
}
public buildCallStatsSpan(
type: OTelStatsReportType,
- report: ByteSentStatsReport | ConnectionStatsReport
+ report: ByteSentStatsReport | ConnectionStatsReport,
): void {
if (!ElementCallOpenTelemetry.instance) return;
let call: OTelCall | undefined;
@@ -403,7 +412,7 @@ export class OTelGroupCallMembership {
const data = ObjectFlattener.flattenReportObject(type, report);
const ctx = opentelemetry.trace.setSpan(
opentelemetry.context.active(),
- call.span
+ call.span,
);
const options = {
@@ -417,21 +426,21 @@ export class OTelGroupCallMembership {
const span = ElementCallOpenTelemetry.instance.tracer.startSpan(
type,
options,
- ctx
+ ctx,
);
span.setAttribute("matrix.callId", callId ?? "unknown");
span.setAttribute(
"matrix.opponentMemberId",
- report.opponentMemberId ? report.opponentMemberId : "unknown"
+ report.opponentMemberId ? report.opponentMemberId : "unknown",
);
span.addEvent("matrix.call.connection_stats_event", data);
span.end();
}
public onSummaryStatsReport(
- statsReport: GroupCallStatsReport
- ) {
+ statsReport: GroupCallStatsReport,
+ ): void {
if (!ElementCallOpenTelemetry.instance) return;
const type = OTelStatsReportType.SummaryReport;
@@ -439,12 +448,12 @@ export class OTelGroupCallMembership {
if (this.statsReportSpan.span === undefined && this.callMembershipSpan) {
const ctx = setSpan(
opentelemetry.context.active(),
- this.callMembershipSpan
+ this.callMembershipSpan,
);
const span = ElementCallOpenTelemetry.instance?.tracer.startSpan(
"matrix.groupCallMembership.summaryReport",
undefined,
- ctx
+ ctx,
);
if (span === undefined) {
return;
@@ -453,7 +462,7 @@ export class OTelGroupCallMembership {
span.setAttribute("matrix.userId", this.myUserId);
span.setAttribute(
"matrix.displayName",
- this.myMember ? this.myMember.name : "unknown-name"
+ this.myMember ? this.myMember.name : "unknown-name",
);
span.addEvent(type, data);
span.end();
diff --git a/src/otel/ObjectFlattener.ts b/src/otel/ObjectFlattener.ts
index 652f9056..76de9e3a 100644
--- a/src/otel/ObjectFlattener.ts
+++ b/src/otel/ObjectFlattener.ts
@@ -25,7 +25,7 @@ import {
export class ObjectFlattener {
public static flattenReportObject(
prefix: string,
- report: ConnectionStatsReport | ByteSentStatsReport
+ report: ConnectionStatsReport | ByteSentStatsReport,
): Attributes {
const flatObject = {};
ObjectFlattener.flattenObjectRecursive(report, flatObject, `${prefix}.`, 0);
@@ -33,27 +33,27 @@ export class ObjectFlattener {
}
public static flattenByteSentStatsReportObject(
- statsReport: GroupCallStatsReport
+ statsReport: GroupCallStatsReport,
): Attributes {
const flatObject = {};
ObjectFlattener.flattenObjectRecursive(
statsReport.report,
flatObject,
"matrix.stats.bytesSent.",
- 0
+ 0,
);
return flatObject;
}
- static flattenSummaryStatsReportObject(
- statsReport: GroupCallStatsReport
- ) {
+ public static flattenSummaryStatsReportObject(
+ statsReport: GroupCallStatsReport,
+ ): Attributes {
const flatObject = {};
ObjectFlattener.flattenObjectRecursive(
statsReport.report,
flatObject,
"matrix.stats.summary.",
- 0
+ 0,
);
return flatObject;
}
@@ -67,7 +67,7 @@ export class ObjectFlattener {
event as unknown as Record, // XXX Types
flatObject,
"matrix.event.",
- 0
+ 0,
);
return flatObject;
@@ -77,12 +77,12 @@ export class ObjectFlattener {
obj: Object,
flatObject: Attributes,
prefix: string,
- depth: number
+ depth: number,
): void {
if (depth > 10)
throw new Error(
"Depth limit exceeded: aborting VoipEvent recursion. Prefix is " +
- prefix
+ prefix,
);
let entries;
if (obj instanceof Map) {
@@ -101,7 +101,7 @@ export class ObjectFlattener {
v,
flatObject,
prefix + k + ".",
- depth + 1
+ depth + 1,
);
}
}
diff --git a/src/otel/otel.ts b/src/otel/otel.ts
index d4629591..14c22cb6 100644
--- a/src/otel/otel.ts
+++ b/src/otel/otel.ts
@@ -36,7 +36,7 @@ export class ElementCallOpenTelemetry {
private otlpExporter?: OTLPTraceExporter;
public readonly rageshakeProcessor?: RageshakeSpanProcessor;
- static globalInit(): void {
+ public static globalInit(): void {
const config = Config.get();
// we always enable opentelemetry in general. We only enable the OTLP
// collector if a URL is defined (and in future if another setting is defined)
@@ -50,18 +50,18 @@ export class ElementCallOpenTelemetry {
sharedInstance = new ElementCallOpenTelemetry(
config.opentelemetry?.collector_url,
- config.rageshake?.submit_url
+ config.rageshake?.submit_url,
);
}
}
- static get instance(): ElementCallOpenTelemetry {
+ public static get instance(): ElementCallOpenTelemetry {
return sharedInstance;
}
- constructor(
+ private constructor(
collectorUrl: string | undefined,
- rageshakeUrl: string | undefined
+ rageshakeUrl: string | undefined,
) {
// This is how we can make Jaeger show a reasonable service in the dropdown on the left.
const providerConfig = {
@@ -77,7 +77,7 @@ export class ElementCallOpenTelemetry {
url: collectorUrl,
});
this._provider.addSpanProcessor(
- new SimpleSpanProcessor(this.otlpExporter)
+ new SimpleSpanProcessor(this.otlpExporter),
);
} else {
logger.info("OTLP collector disabled");
@@ -93,7 +93,7 @@ export class ElementCallOpenTelemetry {
this._tracer = opentelemetry.trace.getTracer(
// This is not the serviceName shown in jaeger
- "my-element-call-otl-tracer"
+ "my-element-call-otl-tracer",
);
}
diff --git a/src/popover/Popover.tsx b/src/popover/Popover.tsx
index 02662eb5..36a4ac4a 100644
--- a/src/popover/Popover.tsx
+++ b/src/popover/Popover.tsx
@@ -40,7 +40,7 @@ export const Popover = forwardRef(
shouldCloseOnBlur: true,
isDismissable: true,
},
- popoverRef
+ popoverRef,
);
return (
@@ -56,5 +56,5 @@ export const Popover = forwardRef(
);
- }
+ },
);
diff --git a/src/popover/PopoverMenu.tsx b/src/popover/PopoverMenu.tsx
index faf7fd71..dc38c6d0 100644
--- a/src/popover/PopoverMenu.tsx
+++ b/src/popover/PopoverMenu.tsx
@@ -43,7 +43,7 @@ export const PopoverMenuTrigger = forwardRef<
const { menuTriggerProps, menuProps } = useMenuTrigger(
{},
popoverMenuState,
- buttonRef
+ buttonRef,
);
const popoverRef = useRef(null);
@@ -62,7 +62,7 @@ export const PopoverMenuTrigger = forwardRef<
typeof children[1] !== "function"
) {
throw new Error(
- "PopoverMenu must have two props. The first being a button and the second being a render prop."
+ "PopoverMenu must have two props. The first being a button and the second being a render prop.",
);
}
diff --git a/src/profile/useProfile.ts b/src/profile/useProfile.ts
index 11c1ec74..eba0420f 100644
--- a/src/profile/useProfile.ts
+++ b/src/profile/useProfile.ts
@@ -39,7 +39,11 @@ type ProfileSaveCallback = ({
removeAvatar: boolean;
}) => Promise;
-export function useProfile(client: MatrixClient | undefined) {
+interface UseProfile extends ProfileLoadState {
+ saveProfile: ProfileSaveCallback;
+}
+
+export function useProfile(client: MatrixClient | undefined): UseProfile {
const [{ success, loading, displayName, avatarUrl, error }, setState] =
useState(() => {
let user: User | undefined = undefined;
@@ -59,8 +63,8 @@ export function useProfile(client: MatrixClient | undefined) {
useEffect(() => {
const onChangeUser = (
_event: MatrixEvent | undefined,
- { displayName, avatarUrl }: User
- ) => {
+ { displayName, avatarUrl }: User,
+ ): void => {
setState({
success: false,
loading: false,
@@ -104,9 +108,8 @@ export function useProfile(client: MatrixClient | undefined) {
if (removeAvatar) {
await client.setAvatarUrl("");
} else if (avatar) {
- ({ content_uri: mxcAvatarUrl } = await client.uploadContent(
- avatar
- ));
+ ({ content_uri: mxcAvatarUrl } =
+ await client.uploadContent(avatar));
await client.setAvatarUrl(mxcAvatarUrl);
}
@@ -131,7 +134,7 @@ export function useProfile(client: MatrixClient | undefined) {
logger.error("Client not initialized before calling saveProfile");
}
},
- [client]
+ [client],
);
return {
diff --git a/src/room/AppSelectionModal.tsx b/src/room/AppSelectionModal.tsx
index 8220ffbc..8affa12e 100644
--- a/src/room/AppSelectionModal.tsx
+++ b/src/room/AppSelectionModal.tsx
@@ -40,14 +40,14 @@ export const AppSelectionModal: FC = ({ roomId }) => {
e.stopPropagation();
setOpen(false);
},
- [setOpen]
+ [setOpen],
);
const roomSharedKey = useRoomSharedKey(roomId ?? "");
const roomIsEncrypted = useIsRoomE2EE(roomId ?? "");
if (roomIsEncrypted && roomSharedKey === undefined) {
logger.error(
- "Generating app redirect URL for encrypted room but don't have key available!"
+ "Generating app redirect URL for encrypted room but don't have key available!",
);
}
@@ -60,7 +60,7 @@ export const AppSelectionModal: FC = ({ roomId }) => {
const url = new URL(
roomId === null
? window.location.href
- : getAbsoluteRoomUrl(roomId, undefined, roomSharedKey ?? undefined)
+ : getAbsoluteRoomUrl(roomId, undefined, roomSharedKey ?? undefined),
);
// Edit the URL to prevent the app selection prompt from appearing a second
// time within the app, and to keep the user confined to the current room
diff --git a/src/room/CallEndedView.tsx b/src/room/CallEndedView.tsx
index 6050b9e9..32b27568 100644
--- a/src/room/CallEndedView.tsx
+++ b/src/room/CallEndedView.tsx
@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
-import { FC, FormEventHandler, useCallback, useState } from "react";
+import { FC, FormEventHandler, ReactNode, useCallback, useState } from "react";
import { MatrixClient } from "matrix-js-sdk/src/client";
import { Trans, useTranslation } from "react-i18next";
import { useHistory } from "react-router-dom";
@@ -64,7 +64,7 @@ export const CallEndedView: FC = ({
PosthogAnalytics.instance.eventQualitySurvey.track(
endedCallId,
feedbackText,
- starRating
+ starRating,
);
setSubmitting(true);
@@ -83,7 +83,7 @@ export const CallEndedView: FC = ({
}, 1000);
}, 1000);
},
- [endedCallId, history, isPasswordlessUser, confineToRoom, starRating]
+ [endedCallId, history, isPasswordlessUser, confineToRoom, starRating],
);
const createAccountDialog = isPasswordlessUser && (
@@ -148,7 +148,7 @@ export const CallEndedView: FC = ({
);
- const renderBody = () => {
+ const renderBody = (): ReactNode => {
if (leaveError) {
return (
<>
diff --git a/src/room/GroupCallLoader.tsx b/src/room/GroupCallLoader.tsx
index c61f4ca4..720b6487 100644
--- a/src/room/GroupCallLoader.tsx
+++ b/src/room/GroupCallLoader.tsx
@@ -47,7 +47,7 @@ export function GroupCallLoader({
ev.preventDefault();
history.push("/");
},
- [history]
+ [history],
);
switch (groupCallState.kind) {
@@ -66,7 +66,7 @@ export function GroupCallLoader({
{t("Call not found")}
{t(
- "Calls are now end-to-end encrypted and need to be created from the home page. This helps make sure everyone's using the same encryption key."
+ "Calls are now end-to-end encrypted and need to be created from the home page. This helps make sure everyone's using the same encryption key.",
)}
{/* XXX: A 'create it for me' button would be the obvious UX here. Two screens already have
diff --git a/src/room/GroupCallView.tsx b/src/room/GroupCallView.tsx
index f834814e..c629a5dc 100644
--- a/src/room/GroupCallView.tsx
+++ b/src/room/GroupCallView.tsx
@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
-import { useCallback, useEffect, useMemo, useRef, useState } from "react";
+import { FC, useCallback, useEffect, useMemo, useRef, useState } from "react";
import { useHistory } from "react-router-dom";
import { MatrixClient } from "matrix-js-sdk/src/client";
import { Room, isE2EESupported } from "livekit-client";
@@ -61,14 +61,14 @@ interface Props {
rtcSession: MatrixRTCSession;
}
-export function GroupCallView({
+export const GroupCallView: FC = ({
client,
isPasswordlessUser,
confineToRoom,
preload,
hideHeader,
rtcSession,
-}: Props) {
+}) => {
const memberships = useMatrixRTCSessionMemberships(rtcSession);
const isJoined = useMatrixRTCSessionJoinState(rtcSession);
@@ -111,7 +111,7 @@ export function GroupCallView({
// Count each member only once, regardless of how many devices they use
const participantCount = useMemo(
() => new Set(memberships.map((m) => m.sender!)).size,
- [memberships]
+ [memberships],
);
const deviceContext = useMediaDevices();
@@ -125,7 +125,9 @@ export function GroupCallView({
useEffect(() => {
if (widget && preload) {
// In preload mode, wait for a join action before entering
- const onJoin = async (ev: CustomEvent) => {
+ const onJoin = async (
+ ev: CustomEvent,
+ ): Promise => {
// XXX: I think this is broken currently - LiveKit *won't* request
// permissions and give you device names unless you specify a kind, but
// here we want all kinds of devices. This needs a fix in livekit-client
@@ -141,14 +143,14 @@ export function GroupCallView({
const deviceId = await findDeviceByName(
audioInput,
"audioinput",
- devices
+ devices,
);
if (!deviceId) {
logger.warn("Unknown audio input: " + audioInput);
latestMuteStates.current!.audio.setEnabled?.(false);
} else {
logger.debug(
- `Found audio input ID ${deviceId} for name ${audioInput}`
+ `Found audio input ID ${deviceId} for name ${audioInput}`,
);
latestDevices.current!.audioInput.select(deviceId);
latestMuteStates.current!.audio.setEnabled?.(true);
@@ -161,14 +163,14 @@ export function GroupCallView({
const deviceId = await findDeviceByName(
videoInput,
"videoinput",
- devices
+ devices,
);
if (!deviceId) {
logger.warn("Unknown video input: " + videoInput);
latestMuteStates.current!.video.setEnabled?.(false);
} else {
logger.debug(
- `Found video input ID ${deviceId} for name ${videoInput}`
+ `Found video input ID ${deviceId} for name ${videoInput}`,
);
latestDevices.current!.videoInput.select(deviceId);
latestMuteStates.current!.video.setEnabled?.(true);
@@ -180,7 +182,7 @@ export function GroupCallView({
PosthogAnalytics.instance.eventCallEnded.cacheStartCall(new Date());
// we only have room sessions right now, so call ID is the emprty string - we use the room ID
PosthogAnalytics.instance.eventCallStarted.track(
- rtcSession.room.roomId
+ rtcSession.room.roomId,
);
await Promise.all([
@@ -211,7 +213,7 @@ export function GroupCallView({
PosthogAnalytics.instance.eventCallEnded.track(
rtcSession.room.roomId,
rtcSession.memberships.length,
- sendInstantly
+ sendInstantly,
);
await leaveRTCSession(rtcSession);
@@ -235,14 +237,16 @@ export function GroupCallView({
history.push("/");
}
},
- [rtcSession, isPasswordlessUser, confineToRoom, history]
+ [rtcSession, isPasswordlessUser, confineToRoom, history],
);
useEffect(() => {
if (widget && isJoined) {
- const onHangup = async (ev: CustomEvent) => {
+ const onHangup = async (
+ ev: CustomEvent,
+ ): Promise => {
leaveRTCSession(rtcSession);
- await widget!.api.transport.reply(ev.detail, {});
+ widget!.api.transport.reply(ev.detail, {});
widget!.api.setAlwaysOnScreen(false);
};
widget.lazyActions.once(ElementWidgetActions.HangupCall, onHangup);
@@ -256,7 +260,7 @@ export function GroupCallView({
const e2eeConfig = useMemo(
() => (e2eeSharedKey ? { sharedKey: e2eeSharedKey } : undefined),
- [e2eeSharedKey]
+ [e2eeSharedKey],
);
const onReconnect = useCallback(() => {
@@ -270,12 +274,12 @@ export function GroupCallView({
const [shareModalOpen, setInviteModalOpen] = useState(false);
const onDismissInviteModal = useCallback(
() => setInviteModalOpen(false),
- [setInviteModalOpen]
+ [setInviteModalOpen],
);
const onShareClickFn = useCallback(
() => setInviteModalOpen(true),
- [setInviteModalOpen]
+ [setInviteModalOpen],
);
const onShareClick = joinRule === JoinRule.Public ? onShareClickFn : null;
@@ -284,7 +288,7 @@ export function GroupCallView({
ev.preventDefault();
history.push("/");
},
- [history]
+ [history],
);
const { t } = useTranslation();
@@ -294,7 +298,7 @@ export function GroupCallView({
@@ -305,7 +309,7 @@ export function GroupCallView({
Incompatible Browser
{t(
- "Your web browser does not support media end-to-end encryption. Supported Browsers are Chrome, Safari, Firefox >=117"
+ "Your web browser does not support media end-to-end encryption. Supported Browsers are Chrome, Safari, Firefox >=117",
)}
@@ -381,7 +385,7 @@ export function GroupCallView({
client={client}
matrixInfo={matrixInfo}
muteStates={muteStates}
- onEnter={() => enterRTCSession(rtcSession)}
+ onEnter={(): void => enterRTCSession(rtcSession)}
confineToRoom={confineToRoom}
hideHeader={hideHeader}
participantCount={participantCount}
@@ -390,4 +394,4 @@ export function GroupCallView({
>
);
}
-}
+};
diff --git a/src/room/InCallView.tsx b/src/room/InCallView.tsx
index fbf79438..9dc124f9 100644
--- a/src/room/InCallView.tsx
+++ b/src/room/InCallView.tsx
@@ -27,7 +27,16 @@ import { ConnectionState, Room, Track } from "livekit-client";
import { MatrixClient } from "matrix-js-sdk/src/client";
import { RoomMember } from "matrix-js-sdk/src/models/room-member";
import { Room as MatrixRoom } from "matrix-js-sdk/src/models/room";
-import { Ref, useCallback, useEffect, useMemo, useRef, useState } from "react";
+import {
+ FC,
+ ReactNode,
+ Ref,
+ useCallback,
+ useEffect,
+ useMemo,
+ useRef,
+ useState,
+} from "react";
import { useTranslation } from "react-i18next";
import useMeasure from "react-use-measure";
import { logger } from "matrix-js-sdk/src/logger";
@@ -91,12 +100,12 @@ export interface ActiveCallProps
e2eeConfig?: E2EEConfig;
}
-export function ActiveCall(props: ActiveCallProps) {
+export const ActiveCall: FC = (props) => {
const sfuConfig = useOpenIDSFU(props.client, props.rtcSession);
const { livekitRoom, connState } = useLiveKit(
props.muteStates,
sfuConfig,
- props.e2eeConfig
+ props.e2eeConfig,
);
if (!livekitRoom) {
@@ -112,7 +121,7 @@ export function ActiveCall(props: ActiveCallProps) {
);
-}
+};
export interface InCallViewProps {
client: MatrixClient;
@@ -128,7 +137,7 @@ export interface InCallViewProps {
onShareClick: (() => void) | null;
}
-export function InCallView({
+export const InCallView: FC = ({
client,
matrixInfo,
rtcSession,
@@ -140,7 +149,7 @@ export function InCallView({
otelGroupCallMembership,
connState,
onShareClick,
-}: InCallViewProps) {
+}) => {
const { t } = useTranslation();
usePreventScroll();
useWakeLock();
@@ -163,10 +172,10 @@ export function InCallView({
[{ source: Track.Source.ScreenShare, withPlaceholder: false }],
{
room: livekitRoom,
- }
+ },
);
const { layout, setLayout } = useVideoGridLayout(
- screenSharingTracks.length > 0
+ screenSharingTracks.length > 0,
);
const [showConnectionStats] = useShowConnectionStats();
@@ -179,11 +188,11 @@ export function InCallView({
const toggleMicrophone = useCallback(
() => muteStates.audio.setEnabled?.((e) => !e),
- [muteStates]
+ [muteStates],
);
const toggleCamera = useCallback(
() => muteStates.video.setEnabled?.((e) => !e),
- [muteStates]
+ [muteStates],
);
// This function incorrectly assumes that there is a camera and microphone, which is not always the case.
@@ -192,7 +201,7 @@ export function InCallView({
containerRef1,
toggleMicrophone,
toggleCamera,
- (muted) => muteStates.audio.setEnabled?.(!muted)
+ (muted) => muteStates.audio.setEnabled?.(!muted),
);
const onLeavePress = useCallback(() => {
@@ -204,32 +213,32 @@ export function InCallView({
layout === "grid"
? ElementWidgetActions.TileLayout
: ElementWidgetActions.SpotlightLayout,
- {}
+ {},
);
}, [layout]);
useEffect(() => {
if (widget) {
- const onTileLayout = async (ev: CustomEvent) => {
+ const onTileLayout = (ev: CustomEvent): void => {
setLayout("grid");
- await widget!.api.transport.reply(ev.detail, {});
+ widget!.api.transport.reply(ev.detail, {});
};
- const onSpotlightLayout = async (ev: CustomEvent) => {
+ const onSpotlightLayout = (ev: CustomEvent): void => {
setLayout("spotlight");
- await widget!.api.transport.reply(ev.detail, {});
+ widget!.api.transport.reply(ev.detail, {});
};
widget.lazyActions.on(ElementWidgetActions.TileLayout, onTileLayout);
widget.lazyActions.on(
ElementWidgetActions.SpotlightLayout,
- onSpotlightLayout
+ onSpotlightLayout,
);
return () => {
widget!.lazyActions.off(ElementWidgetActions.TileLayout, onTileLayout);
widget!.lazyActions.off(
ElementWidgetActions.SpotlightLayout,
- onSpotlightLayout
+ onSpotlightLayout,
);
};
}
@@ -252,7 +261,7 @@ export function InCallView({
(noControls
? items.find((item) => item.isSpeaker) ?? items.at(0) ?? null
: null),
- [fullscreenItem, noControls, items]
+ [fullscreenItem, noControls, items],
);
const Grid =
@@ -295,7 +304,7 @@ export function InCallView({
disableAnimations={prefersReducedMotion || isSafari}
layoutStates={layoutStates}
>
- {(props) => (
+ {(props): ReactNode => (
setSettingsModalOpen(true),
- [setSettingsModalOpen]
+ [setSettingsModalOpen],
);
const closeSettings = useCallback(
() => setSettingsModalOpen(false),
- [setSettingsModalOpen]
+ [setSettingsModalOpen],
);
const toggleScreensharing = useCallback(async () => {
@@ -356,7 +365,7 @@ export function InCallView({
onPress={toggleCamera}
disabled={muteStates.video.setEnabled === null}
data-testid="incall_videomute"
- />
+ />,
);
if (!reducedControls) {
@@ -367,14 +376,18 @@ export function InCallView({
enabled={isScreenShareEnabled}
onPress={toggleScreensharing}
data-testid="incall_screenshare"
- />
+ />,
);
}
buttons.push( );
}
buttons.push(
-
+ ,
);
footer = (
@@ -434,11 +447,11 @@ export function InCallView({
/>
);
-}
+};
function findMatrixMember(
room: MatrixRoom,
- id: string
+ id: string,
): RoomMember | undefined {
if (!id) return undefined;
@@ -446,7 +459,7 @@ function findMatrixMember(
// must be at least 3 parts because we know the first part is a userId which must necessarily contain a colon
if (parts.length < 3) {
logger.warn(
- "Livekit participants ID doesn't look like a userId:deviceId combination"
+ "Livekit participants ID doesn't look like a userId:deviceId combination",
);
return undefined;
}
@@ -460,7 +473,7 @@ function findMatrixMember(
function useParticipantTiles(
livekitRoom: Room,
matrixRoom: MatrixRoom,
- connState: ECConnectionState
+ connState: ECConnectionState,
): TileDescriptor[] {
const previousTiles = useRef[]>([]);
@@ -489,7 +502,7 @@ function useParticipantTiles(
// connected, this is fine and we'll be in "all ghosts" mode.
if (id !== "" && member === undefined) {
logger.warn(
- `Ruh, roh! No matrix member found for SFU participant '${id}': creating g-g-g-ghost!`
+ `Ruh, roh! No matrix member found for SFU participant '${id}': creating g-g-g-ghost!`,
);
}
allGhosts &&= member === undefined;
@@ -533,11 +546,11 @@ function useParticipantTiles(
return screenShareTile
? [userMediaTile, screenShareTile]
: [userMediaTile];
- }
+ },
);
PosthogAnalytics.instance.eventCallEnded.cacheParticipantCountChanged(
- tiles.length
+ tiles.length,
);
// If every item is a ghost, that probably means we're still connecting and
diff --git a/src/room/InviteModal.tsx b/src/room/InviteModal.tsx
index 33d6ef97..f9d02f2b 100644
--- a/src/room/InviteModal.tsx
+++ b/src/room/InviteModal.tsx
@@ -40,7 +40,7 @@ export const InviteModal: FC = ({ room, open, onDismiss }) => {
const url = useMemo(
() =>
getAbsoluteRoomUrl(room.roomId, room.name, roomSharedKey ?? undefined),
- [room, roomSharedKey]
+ [room, roomSharedKey],
);
const [, setCopied] = useClipboard(url);
const [toastOpen, setToastOpen] = useState(false);
@@ -53,7 +53,7 @@ export const InviteModal: FC = ({ room, open, onDismiss }) => {
onDismiss();
setToastOpen(true);
},
- [setCopied, onDismiss]
+ [setCopied, onDismiss],
);
return (
diff --git a/src/room/LayoutToggle.tsx b/src/room/LayoutToggle.tsx
index 88d9aef7..01738f73 100644
--- a/src/room/LayoutToggle.tsx
+++ b/src/room/LayoutToggle.tsx
@@ -36,7 +36,7 @@ export const LayoutToggle: FC = ({ layout, setLayout, className }) => {
const onChange = useCallback(
(e: ChangeEvent) => setLayout(e.target.value as Layout),
- [setLayout]
+ [setLayout],
);
const spotlightId = useId();
diff --git a/src/room/LobbyView.tsx b/src/room/LobbyView.tsx
index e49b70cf..7116e74c 100644
--- a/src/room/LobbyView.tsx
+++ b/src/room/LobbyView.tsx
@@ -63,22 +63,22 @@ export const LobbyView: FC = ({
const onAudioPress = useCallback(
() => muteStates.audio.setEnabled?.((e) => !e),
- [muteStates]
+ [muteStates],
);
const onVideoPress = useCallback(
() => muteStates.video.setEnabled?.((e) => !e),
- [muteStates]
+ [muteStates],
);
const [settingsModalOpen, setSettingsModalOpen] = useState(false);
const openSettings = useCallback(
() => setSettingsModalOpen(true),
- [setSettingsModalOpen]
+ [setSettingsModalOpen],
);
const closeSettings = useCallback(
() => setSettingsModalOpen(false),
- [setSettingsModalOpen]
+ [setSettingsModalOpen],
);
const history = useHistory();
diff --git a/src/room/MuteStates.ts b/src/room/MuteStates.ts
index 8bdc6420..db1fb22a 100644
--- a/src/room/MuteStates.ts
+++ b/src/room/MuteStates.ts
@@ -49,18 +49,18 @@ export interface MuteStates {
function useMuteState(
device: MediaDevice,
- enabledByDefault: () => boolean
+ enabledByDefault: () => boolean,
): MuteState {
const [enabled, setEnabled] = useReactiveState(
(prev) => device.available.length > 0 && (prev ?? enabledByDefault()),
- [device]
+ [device],
);
return useMemo(
() =>
device.available.length === 0
? deviceUnavailable
: { enabled, setEnabled },
- [device, enabled, setEnabled]
+ [device, enabled, setEnabled],
);
}
@@ -69,7 +69,7 @@ export function useMuteStates(participantCount: number): MuteStates {
const audio = useMuteState(
devices.audioInput,
- () => participantCount <= MUTE_PARTICIPANT_COUNT
+ () => participantCount <= MUTE_PARTICIPANT_COUNT,
);
const video = useMuteState(devices.videoInput, () => true);
diff --git a/src/room/RageshakeRequestModal.tsx b/src/room/RageshakeRequestModal.tsx
index ed9acbcb..9bc60cdb 100644
--- a/src/room/RageshakeRequestModal.tsx
+++ b/src/room/RageshakeRequestModal.tsx
@@ -17,7 +17,7 @@ limitations under the License.
import { FC, useEffect } from "react";
import { useTranslation } from "react-i18next";
-import { Modal, ModalProps } from "../Modal";
+import { Modal, Props as ModalProps } from "../Modal";
import { Button } from "../button";
import { FieldRow, ErrorMessage } from "../input/Input";
import { useSubmitRageshake } from "../settings/submit-rageshake";
@@ -47,13 +47,13 @@ export const RageshakeRequestModal: FC = ({
{t(
- "Another user on this call is having an issue. In order to better diagnose these issues we'd like to collect a debug log."
+ "Another user on this call is having an issue. In order to better diagnose these issues we'd like to collect a debug log.",
)}
- submitRageshake({
+ onPress={(): void =>
+ void submitRageshake({
sendLogs: true,
rageshakeRequestId,
roomId,
diff --git a/src/room/RoomAuthView.tsx b/src/room/RoomAuthView.tsx
index 67365241..a52a0e8e 100644
--- a/src/room/RoomAuthView.tsx
+++ b/src/room/RoomAuthView.tsx
@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
-import { useCallback, useState } from "react";
+import { FC, useCallback, useState } from "react";
import { useLocation } from "react-router-dom";
import { Trans, useTranslation } from "react-i18next";
import { logger } from "matrix-js-sdk/src/logger";
@@ -29,7 +29,7 @@ import { UserMenuContainer } from "../UserMenuContainer";
import { useRegisterPasswordlessUser } from "../auth/useRegisterPasswordlessUser";
import { Config } from "../config/Config";
-export function RoomAuthView() {
+export const RoomAuthView: FC = () => {
const [loading, setLoading] = useState(false);
const [error, setError] = useState();
@@ -52,7 +52,7 @@ export function RoomAuthView() {
setError(error);
});
},
- [registerPasswordlessUser]
+ [registerPasswordlessUser],
);
const { t } = useTranslation();
@@ -122,4 +122,4 @@ export function RoomAuthView() {
>
);
-}
+};
diff --git a/src/room/RoomPage.tsx b/src/room/RoomPage.tsx
index ffc9562a..9bcb8a81 100644
--- a/src/room/RoomPage.tsx
+++ b/src/room/RoomPage.tsx
@@ -81,7 +81,7 @@ export const RoomPage: FC = () => {
hideHeader={hideHeader}
/>
),
- [client, passwordlessUser, confineToRoom, preload, hideHeader]
+ [client, passwordlessUser, confineToRoom, preload, hideHeader],
);
let content: ReactNode;
diff --git a/src/room/VideoPreview.tsx b/src/room/VideoPreview.tsx
index 36323ba9..df4d1480 100644
--- a/src/room/VideoPreview.tsx
+++ b/src/room/VideoPreview.tsx
@@ -82,14 +82,14 @@ export const VideoPreview: FC = ({
},
(error) => {
logger.error("Error while creating preview Tracks:", error);
- }
+ },
);
const videoTrack = useMemo(
() =>
tracks?.find((t) => t.kind === Track.Kind.Video) as
| LocalVideoTrack
| undefined,
- [tracks]
+ [tracks],
);
const videoEl = useRef(null);
diff --git a/src/room/useActiveFocus.ts b/src/room/useActiveFocus.ts
index a62ffafd..022b25ac 100644
--- a/src/room/useActiveFocus.ts
+++ b/src/room/useActiveFocus.ts
@@ -24,7 +24,7 @@ import { deepCompare } from "matrix-js-sdk/src/utils";
import { LivekitFocus } from "../livekit/LivekitFocus";
function getActiveFocus(
- rtcSession: MatrixRTCSession
+ rtcSession: MatrixRTCSession,
): LivekitFocus | undefined {
const oldestMembership = rtcSession.getOldestMembership();
return oldestMembership?.getActiveFoci()[0] as LivekitFocus;
@@ -36,10 +36,10 @@ function getActiveFocus(
* and the same focus.
*/
export function useActiveFocus(
- rtcSession: MatrixRTCSession
+ rtcSession: MatrixRTCSession,
): LivekitFocus | undefined {
const [activeFocus, setActiveFocus] = useState(() =>
- getActiveFocus(rtcSession)
+ getActiveFocus(rtcSession),
);
const onMembershipsChanged = useCallback(() => {
@@ -53,13 +53,13 @@ export function useActiveFocus(
useEffect(() => {
rtcSession.on(
MatrixRTCSessionEvent.MembershipsChanged,
- onMembershipsChanged
+ onMembershipsChanged,
);
return () => {
rtcSession.off(
MatrixRTCSessionEvent.MembershipsChanged,
- onMembershipsChanged
+ onMembershipsChanged,
);
};
});
diff --git a/src/room/useFullscreen.ts b/src/room/useFullscreen.ts
index 78ec1c87..38469808 100644
--- a/src/room/useFullscreen.ts
+++ b/src/room/useFullscreen.ts
@@ -22,11 +22,11 @@ import { TileDescriptor } from "../video-grid/VideoGrid";
import { useReactiveState } from "../useReactiveState";
import { useEventTarget } from "../useEvents";
-const isFullscreen = () =>
+const isFullscreen = (): boolean =>
Boolean(document.fullscreenElement) ||
Boolean(document.webkitFullscreenElement);
-function enterFullscreen() {
+function enterFullscreen(): void {
if (document.body.requestFullscreen) {
document.body.requestFullscreen();
} else if (document.body.webkitRequestFullscreen) {
@@ -36,7 +36,7 @@ function enterFullscreen() {
}
}
-function exitFullscreen() {
+function exitFullscreen(): void {
if (document.exitFullscreen) {
document.exitFullscreen();
} else if (document.webkitExitFullscreen) {
@@ -46,7 +46,7 @@ function exitFullscreen() {
}
}
-function useFullscreenChange(onFullscreenChange: () => void) {
+function useFullscreenChange(onFullscreenChange: () => void): void {
useEventTarget(document.body, "fullscreenchange", onFullscreenChange);
useEventTarget(document.body, "webkitfullscreenchange", onFullscreenChange);
}
@@ -66,7 +66,7 @@ export function useFullscreen(items: TileDescriptor[]): {
prevItem == null
? null
: items.find((i) => i.id === prevItem.id) ?? null,
- [items]
+ [items],
);
const latestItems = useRef[]>(items);
@@ -80,15 +80,15 @@ export function useFullscreen(items: TileDescriptor[]): {
setFullscreenItem(
latestFullscreenItem.current === null
? latestItems.current.find((i) => i.id === itemId) ?? null
- : null
+ : null,
);
},
- [setFullscreenItem]
+ [setFullscreenItem],
);
const exitFullscreenCallback = useCallback(
() => setFullscreenItem(null),
- [setFullscreenItem]
+ [setFullscreenItem],
);
useLayoutEffect(() => {
@@ -103,7 +103,7 @@ export function useFullscreen(items: TileDescriptor[]): {
useFullscreenChange(
useCallback(() => {
if (!isFullscreen()) setFullscreenItem(null);
- }, [setFullscreenItem])
+ }, [setFullscreenItem]),
);
return {
diff --git a/src/room/useJoinRule.ts b/src/room/useJoinRule.ts
index 78abc507..798999e4 100644
--- a/src/room/useJoinRule.ts
+++ b/src/room/useJoinRule.ts
@@ -15,12 +15,14 @@ limitations under the License.
*/
import { useCallback } from "react";
+import { JoinRule } from "matrix-js-sdk/src/matrix";
import type { Room } from "matrix-js-sdk/src/models/room";
import { useRoomState } from "./useRoomState";
-export const useJoinRule = (room: Room) =>
- useRoomState(
+export function useJoinRule(room: Room): JoinRule {
+ return useRoomState(
room,
- useCallback((state) => state.getJoinRule(), [])
+ useCallback((state) => state.getJoinRule(), []),
);
+}
diff --git a/src/room/useLoadGroupCall.ts b/src/room/useLoadGroupCall.ts
index e29a7cb6..423706ec 100644
--- a/src/room/useLoadGroupCall.ts
+++ b/src/room/useLoadGroupCall.ts
@@ -52,7 +52,7 @@ export interface GroupCallLoadState {
export const useLoadGroupCall = (
client: MatrixClient,
roomIdOrAlias: string,
- viaServers: string[]
+ viaServers: string[],
): GroupCallStatus => {
const { t } = useTranslation();
const [state, setState] = useState({ kind: "loading" });
@@ -70,7 +70,7 @@ export const useLoadGroupCall = (
// join anyway but the js-sdk recreates the room if you pass the alias for a
// room you're already joined to (which it probably ought not to).
const lookupResult = await client.getRoomIdForAlias(
- roomIdOrAlias.toLowerCase()
+ roomIdOrAlias.toLowerCase(),
);
logger.info(`${roomIdOrAlias} resolved to ${lookupResult.room_id}`);
room = client.getRoom(lookupResult.room_id);
@@ -81,7 +81,7 @@ export const useLoadGroupCall = (
});
} else {
logger.info(
- `Already in room ${lookupResult.room_id}, not rejoining.`
+ `Already in room ${lookupResult.room_id}, not rejoining.`,
);
}
} else {
@@ -92,7 +92,7 @@ export const useLoadGroupCall = (
}
logger.info(
- `Joined ${roomIdOrAlias}, waiting room to be ready for group calls`
+ `Joined ${roomIdOrAlias}, waiting room to be ready for group calls`,
);
await client.waitUntilRoomReadyForGroupCalls(room.roomId);
logger.info(`${roomIdOrAlias}, is ready for group calls`);
@@ -107,13 +107,13 @@ export const useLoadGroupCall = (
return rtcSession;
};
- const waitForClientSyncing = async () => {
+ const waitForClientSyncing = async (): Promise => {
if (client.getSyncState() !== SyncState.Syncing) {
logger.debug(
- "useLoadGroupCall: waiting for client to start syncing..."
+ "useLoadGroupCall: waiting for client to start syncing...",
);
await new Promise((resolve) => {
- const onSync = () => {
+ const onSync = (): void => {
if (client.getSyncState() === SyncState.Syncing) {
client.off(ClientEvent.Sync, onSync);
return resolve();
diff --git a/src/room/usePageUnload.ts b/src/room/usePageUnload.ts
index bae58d27..6af3a8f9 100644
--- a/src/room/usePageUnload.ts
+++ b/src/room/usePageUnload.ts
@@ -18,11 +18,11 @@ import { useEffect } from "react";
import { platform } from "../Platform";
-export function usePageUnload(callback: () => void) {
+export function usePageUnload(callback: () => void): void {
useEffect(() => {
let pageVisibilityTimeout: ReturnType;
- function onBeforeUnload(event: PageTransitionEvent) {
+ function onBeforeUnload(event: PageTransitionEvent): void {
if (event.type === "visibilitychange") {
if (document.visibilityState === "visible") {
clearTimeout(pageVisibilityTimeout);
diff --git a/src/room/useRoomAvatar.ts b/src/room/useRoomAvatar.ts
index 4c4d0152..b00597e7 100644
--- a/src/room/useRoomAvatar.ts
+++ b/src/room/useRoomAvatar.ts
@@ -19,8 +19,9 @@ import { Room } from "matrix-js-sdk/src/models/room";
import { useRoomState } from "./useRoomState";
-export const useRoomAvatar = (room: Room) =>
- useRoomState(
+export function useRoomAvatar(room: Room): string | null {
+ return useRoomState(
room,
- useCallback(() => room.getMxcAvatarUrl(), [room])
+ useCallback(() => room.getMxcAvatarUrl(), [room]),
);
+}
diff --git a/src/room/useRoomState.ts b/src/room/useRoomState.ts
index 2fa4d0b7..6e069b67 100644
--- a/src/room/useRoomState.ts
+++ b/src/room/useRoomState.ts
@@ -31,7 +31,7 @@ export const useRoomState = (room: Room, f: (state: RoomState) => T): T => {
useTypedEventEmitter(
room,
RoomStateEvent.Update,
- useCallback(() => setNumUpdates((n) => n + 1), [setNumUpdates])
+ useCallback(() => setNumUpdates((n) => n + 1), [setNumUpdates]),
);
// We want any change to the update counter to trigger an update here
// eslint-disable-next-line react-hooks/exhaustive-deps
diff --git a/src/rtcSessionHelpers.ts b/src/rtcSessionHelpers.ts
index 68baca6e..1ea00160 100644
--- a/src/rtcSessionHelpers.ts
+++ b/src/rtcSessionHelpers.ts
@@ -33,7 +33,7 @@ function makeFocus(livekitAlias: string): LivekitFocus {
};
}
-export function enterRTCSession(rtcSession: MatrixRTCSession) {
+export function enterRTCSession(rtcSession: MatrixRTCSession): void {
PosthogAnalytics.instance.eventCallEnded.cacheStartCall(new Date());
PosthogAnalytics.instance.eventCallStarted.track(rtcSession.room.roomId);
@@ -48,7 +48,7 @@ export function enterRTCSession(rtcSession: MatrixRTCSession) {
}
export async function leaveRTCSession(
- rtcSession: MatrixRTCSession
+ rtcSession: MatrixRTCSession,
): Promise {
//groupCallOTelMembership?.onLeaveCall();
await rtcSession.leaveRoomSession();
diff --git a/src/settings/FeedbackSettingsTab.tsx b/src/settings/FeedbackSettingsTab.tsx
index e1af3fcb..af45b42c 100644
--- a/src/settings/FeedbackSettingsTab.tsx
+++ b/src/settings/FeedbackSettingsTab.tsx
@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
-import { useCallback } from "react";
+import { FC, useCallback } from "react";
import { randomString } from "matrix-js-sdk/src/randomstring";
import { useTranslation } from "react-i18next";
@@ -29,7 +29,7 @@ interface Props {
roomId?: string;
}
-export function FeedbackSettingsTab({ roomId }: Props) {
+export const FeedbackSettingsTab: FC = ({ roomId }) => {
const { t } = useTranslation();
const { submitRageshake, sending, sent, error } = useSubmitRageshake();
const sendRageshakeRequest = useRageshakeRequest();
@@ -57,7 +57,7 @@ export function FeedbackSettingsTab({ roomId }: Props) {
sendRageshakeRequest(roomId, rageshakeRequestId);
}
},
- [submitRageshake, roomId, sendRageshakeRequest]
+ [submitRageshake, roomId, sendRageshakeRequest],
);
return (
@@ -65,7 +65,7 @@ export function FeedbackSettingsTab({ roomId }: Props) {
{t("Submit feedback")}
{t(
- "If you are experiencing issues or simply would like to provide some feedback, please send us a short description below."
+ "If you are experiencing issues or simply would like to provide some feedback, please send us a short description below.",
)}
);
-}
+};
diff --git a/src/settings/ProfileSettingsTab.tsx b/src/settings/ProfileSettingsTab.tsx
index 4286a960..6adb0650 100644
--- a/src/settings/ProfileSettingsTab.tsx
+++ b/src/settings/ProfileSettingsTab.tsx
@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
-import { useCallback, useEffect, useMemo, useRef } from "react";
+import { FC, useCallback, useEffect, useMemo, useRef } from "react";
import { MatrixClient } from "matrix-js-sdk/src/client";
import { useTranslation } from "react-i18next";
@@ -26,7 +26,7 @@ import styles from "./ProfileSettingsTab.module.css";
interface Props {
client: MatrixClient;
}
-export function ProfileSettingsTab({ client }: Props) {
+export const ProfileSettingsTab: FC = ({ client }) => {
const { t } = useTranslation();
const { error, displayName, avatarUrl, saveProfile } = useProfile(client);
const userId = useMemo(() => client.getUserId(), [client]);
@@ -120,4 +120,4 @@ export function ProfileSettingsTab({ client }: Props) {
)}
);
-}
+};
diff --git a/src/settings/RageshakeButton.tsx b/src/settings/RageshakeButton.tsx
index 599c565d..3868fa5a 100644
--- a/src/settings/RageshakeButton.tsx
+++ b/src/settings/RageshakeButton.tsx
@@ -15,7 +15,7 @@ limitations under the License.
*/
import { useTranslation } from "react-i18next";
-import { useCallback } from "react";
+import { FC, useCallback } from "react";
import { Button } from "../button";
import { Config } from "../config/Config";
@@ -26,7 +26,7 @@ interface Props {
description: string;
}
-export const RageshakeButton = ({ description }: Props) => {
+export const RageshakeButton: FC = ({ description }) => {
const { submitRageshake, sending, sent, error } = useSubmitRageshake();
const { t } = useTranslation();
diff --git a/src/settings/SettingsModal.tsx b/src/settings/SettingsModal.tsx
index 99716f88..fe183d59 100644
--- a/src/settings/SettingsModal.tsx
+++ b/src/settings/SettingsModal.tsx
@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
-import { ChangeEvent, Key, useCallback, useState } from "react";
+import { ChangeEvent, FC, Key, ReactNode, useCallback, useState } from "react";
import { Item } from "@react-stately/collections";
import { Trans, useTranslation } from "react-i18next";
import { MatrixClient } from "matrix-js-sdk";
@@ -56,7 +56,7 @@ interface Props {
defaultTab?: string;
}
-export const SettingsModal = (props: Props) => {
+export const SettingsModal: FC = (props) => {
const { t } = useTranslation();
const [optInAnalytics, setOptInAnalytics] = useOptInAnalytics();
@@ -67,7 +67,10 @@ export const SettingsModal = (props: Props) => {
const [enableE2EE, setEnableE2EE] = useEnableE2EE();
// Generate a `SelectInput` with a list of devices for a given device kind.
- const generateDeviceSelection = (devices: MediaDevice, caption: string) => {
+ const generateDeviceSelection = (
+ devices: MediaDevice,
+ caption: string,
+ ): ReactNode => {
if (devices.available.length == 0) return null;
return (
@@ -78,7 +81,7 @@ export const SettingsModal = (props: Props) => {
? "default"
: devices.selectedId
}
- onSelectionChange={(id) => devices.select(id.toString())}
+ onSelectionChange={(id): void => devices.select(id.toString())}
>
{devices.available.map(({ deviceId, label }, index) => (
-
@@ -97,7 +100,7 @@ export const SettingsModal = (props: Props) => {
(tab: Key) => {
setSelectedTab(tab.toString());
},
- [setSelectedTab]
+ [setSelectedTab],
);
const optInDescription = (
@@ -191,7 +194,7 @@ export const SettingsModal = (props: Props) => {
checked={developerSettingsTab}
label={t("Developer Settings")}
description={t("Expose developer settings in the settings window.")}
- onChange={(event: ChangeEvent
) =>
+ onChange={(event: ChangeEvent): void =>
setDeveloperSettingsTab(event.target.checked)
}
/>
@@ -203,7 +206,7 @@ export const SettingsModal = (props: Props) => {
type="checkbox"
checked={optInAnalytics ?? undefined}
description={optInDescription}
- onChange={(event: ChangeEvent) => {
+ onChange={(event: ChangeEvent): void => {
setOptInAnalytics?.(event.target.checked);
}}
/>
@@ -235,7 +238,7 @@ export const SettingsModal = (props: Props) => {
label={t("Show connection stats")}
type="checkbox"
checked={showConnectionStats}
- onChange={(e: ChangeEvent) =>
+ onChange={(e: ChangeEvent): void =>
setShowConnectionStats(e.target.checked)
}
/>
@@ -252,7 +255,7 @@ export const SettingsModal = (props: Props) => {
disabled={!setEnableE2EE}
type="checkbox"
checked={enableE2EE ?? undefined}
- onChange={(e: ChangeEvent) =>
+ onChange={(e: ChangeEvent): void =>
setEnableE2EE?.(e.target.checked)
}
/>
diff --git a/src/settings/rageshake.ts b/src/settings/rageshake.ts
index 6ded0c2d..0b0365a7 100644
--- a/src/settings/rageshake.ts
+++ b/src/settings/rageshake.ts
@@ -41,6 +41,7 @@ import EventEmitter from "events";
import { throttle } from "lodash";
import { logger } from "matrix-js-sdk/src/logger";
import { randomString } from "matrix-js-sdk/src/randomstring";
+import { LoggingMethod } from "loglevel";
// the length of log data we keep in indexeddb (and include in the reports)
const MAX_LOG_SIZE = 1024 * 1024 * 5; // 5 MB
@@ -130,9 +131,9 @@ class IndexedDBLogStore {
private flushAgainPromise?: Promise;
private id: string;
- constructor(
+ public constructor(
private indexedDB: IDBFactory,
- private loggerInstance: ConsoleLogger
+ private loggerInstance: ConsoleLogger,
) {
this.id = "instance-" + randomString(16);
@@ -146,20 +147,20 @@ class IndexedDBLogStore {
public connect(): Promise {
const req = this.indexedDB.open("logs");
return new Promise((resolve, reject) => {
- req.onsuccess = () => {
+ req.onsuccess = (): void => {
this.db = req.result;
resolve();
};
- req.onerror = () => {
+ req.onerror = (): void => {
const err = "Failed to open log database: " + req?.error?.name;
logger.error(err);
reject(new Error(err));
};
// First time: Setup the object store
- req.onupgradeneeded = () => {
+ req.onupgradeneeded = (): void => {
const db = req.result;
// This is the log entries themselves. Each entry is a chunk of
// logs (ie multiple lines). 'id' is the instance ID (so logs with
@@ -176,7 +177,7 @@ class IndexedDBLogStore {
logObjStore.createIndex("id", "id", { unique: false });
logObjStore.add(
- this.generateLogEntry(new Date() + " ::: Log database was created.")
+ this.generateLogEntry(new Date() + " ::: Log database was created."),
);
// This records the last time each instance ID generated a log message, such
@@ -190,7 +191,7 @@ class IndexedDBLogStore {
});
}
- private onLoggerLog = () => {
+ private onLoggerLog = (): void => {
if (!this.db) return;
this.throttledFlush();
@@ -207,7 +208,7 @@ class IndexedDBLogStore {
{
leading: false,
trailing: true,
- }
+ },
);
/**
@@ -261,10 +262,10 @@ class IndexedDBLogStore {
}
const txn = this.db.transaction(["logs", "logslastmod"], "readwrite");
const objStore = txn.objectStore("logs");
- txn.oncomplete = () => {
+ txn.oncomplete = (): void => {
resolve();
};
- txn.onerror = (event) => {
+ txn.onerror = (event): void => {
logger.error("Failed to flush logs : ", event);
reject(new Error("Failed to write logs: " + txn?.error?.message));
};
@@ -305,10 +306,10 @@ class IndexedDBLogStore {
.index("id")
.openCursor(IDBKeyRange.only(id), "prev");
let lines = "";
- query.onerror = () => {
+ query.onerror = (): void => {
reject(new Error("Query failed: " + query?.error?.message));
};
- query.onsuccess = () => {
+ query.onsuccess = (): void => {
const cursor = query.result;
if (!cursor) {
resolve(lines);
@@ -351,7 +352,7 @@ class IndexedDBLogStore {
const o = txn.objectStore("logs");
// only load the key path, not the data which may be huge
const query = o.index("id").openKeyCursor(IDBKeyRange.only(id));
- query.onsuccess = () => {
+ query.onsuccess = (): void => {
const cursor = query.result;
if (!cursor) {
return;
@@ -359,14 +360,14 @@ class IndexedDBLogStore {
o.delete(cursor.primaryKey);
cursor.continue();
};
- txn.oncomplete = () => {
+ txn.oncomplete = (): void => {
resolve();
};
- txn.onerror = () => {
+ txn.onerror = (): void => {
reject(
new Error(
- "Failed to delete logs for " + `'${id}' : ${txn?.error?.message}`
- )
+ "Failed to delete logs for " + `'${id}' : ${txn?.error?.message}`,
+ ),
);
};
// delete last modified entries
@@ -409,7 +410,7 @@ class IndexedDBLogStore {
},
(err) => {
logger.error(err);
- }
+ },
);
}
return logs;
@@ -444,16 +445,16 @@ class IndexedDBLogStore {
function selectQuery(
store: IDBObjectStore,
keyRange: IDBKeyRange | undefined,
- resultMapper: (cursor: IDBCursorWithValue) => T
+ resultMapper: (cursor: IDBCursorWithValue) => T,
): Promise {
const query = store.openCursor(keyRange);
return new Promise((resolve, reject) => {
const results: T[] = [];
- query.onerror = () => {
+ query.onerror = (): void => {
reject(new Error("Query failed: " + query?.error?.message));
};
// collect results
- query.onsuccess = () => {
+ query.onsuccess = (): void => {
const cursor = query.result;
if (!cursor) {
resolve(results);
@@ -509,7 +510,7 @@ function tryInitStorage(): Promise {
if (indexedDB) {
global.mx_rage_store = new IndexedDBLogStore(
indexedDB,
- global.mx_rage_logger
+ global.mx_rage_logger,
);
global.mx_rage_initStoragePromise = global.mx_rage_store.connect();
return global.mx_rage_initStoragePromise;
@@ -546,7 +547,7 @@ export async function getLogsForReport(): Promise {
type StringifyReplacer = (
this: unknown,
key: string,
- value: unknown
+ value: unknown,
) => unknown;
// From https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors/Cyclic_object_value#circular_references
@@ -593,10 +594,14 @@ type LogLevelString = keyof typeof LogLevel;
* took loglevel's example honouring log levels). Adds a loglevel logging extension
* in the recommended way.
*/
-export function setLogExtension(extension: LogExtensionFunc) {
+export function setLogExtension(extension: LogExtensionFunc): void {
const originalFactory = logger.methodFactory;
- logger.methodFactory = function (methodName, configLevel, loggerName) {
+ logger.methodFactory = function (
+ methodName,
+ configLevel,
+ loggerName,
+ ): LoggingMethod {
const rawMethod = originalFactory(methodName, configLevel, loggerName);
const logLevel = LogLevel[methodName as LogLevelString];
diff --git a/src/settings/submit-rageshake.ts b/src/settings/submit-rageshake.ts
index 83feeb00..16c036e2 100644
--- a/src/settings/submit-rageshake.ts
+++ b/src/settings/submit-rageshake.ts
@@ -95,12 +95,12 @@ export function useSubmitRageshake(): {
const body = new FormData();
body.append(
"text",
- description ?? "User did not supply any additional text."
+ description ?? "User did not supply any additional text.",
);
body.append("app", "matrix-video-chat");
body.append(
"version",
- (import.meta.env.VITE_APP_VERSION as string) || "dev"
+ (import.meta.env.VITE_APP_VERSION as string) || "dev",
);
body.append("user_agent", userAgent);
body.append("installed_pwa", "false");
@@ -132,22 +132,22 @@ export function useSubmitRageshake(): {
body.append(
"cross_signing_ready",
- String(await client.isCrossSigningReady())
+ String(await client.isCrossSigningReady()),
);
body.append(
"cross_signing_supported_by_hs",
String(
await client.doesServerSupportUnstableFeature(
- "org.matrix.e2e_cross_signing"
- )
- )
+ "org.matrix.e2e_cross_signing",
+ ),
+ ),
);
body.append("cross_signing_key", crossSigning.getId()!);
body.append(
"cross_signing_privkey_in_secret_storage",
String(
- !!(await crossSigning.isStoredInSecretStorage(secretStorage))
- )
+ !!(await crossSigning.isStoredInSecretStorage(secretStorage)),
+ ),
);
const pkCache = client.getCrossSigningCacheCallbacks();
@@ -157,8 +157,8 @@ export function useSubmitRageshake(): {
!!(
pkCache?.getCrossSigningKeyCache &&
(await pkCache.getCrossSigningKeyCache("master"))
- )
- )
+ ),
+ ),
);
body.append(
"cross_signing_self_signing_privkey_cached",
@@ -166,8 +166,8 @@ export function useSubmitRageshake(): {
!!(
pkCache?.getCrossSigningKeyCache &&
(await pkCache.getCrossSigningKeyCache("self_signing"))
- )
- )
+ ),
+ ),
);
body.append(
"cross_signing_user_signing_privkey_cached",
@@ -175,32 +175,32 @@ export function useSubmitRageshake(): {
!!(
pkCache?.getCrossSigningKeyCache &&
(await pkCache.getCrossSigningKeyCache("user_signing"))
- )
- )
+ ),
+ ),
);
body.append(
"secret_storage_ready",
- String(await client.isSecretStorageReady())
+ String(await client.isSecretStorageReady()),
);
body.append(
"secret_storage_key_in_account",
- String(!!(await secretStorage.hasKey()))
+ String(!!(await secretStorage.hasKey())),
);
body.append(
"session_backup_key_in_secret_storage",
- String(!!(await client.isKeyBackupKeyStored()))
+ String(!!(await client.isKeyBackupKeyStored())),
);
const sessionBackupKeyFromCache =
await client.crypto!.getSessionBackupPrivateKey();
body.append(
"session_backup_key_cached",
- String(!!sessionBackupKeyFromCache)
+ String(!!sessionBackupKeyFromCache),
);
body.append(
"session_backup_key_well_formed",
- String(sessionBackupKeyFromCache instanceof Uint8Array)
+ String(sessionBackupKeyFromCache instanceof Uint8Array),
);
}
}
@@ -214,7 +214,7 @@ export function useSubmitRageshake(): {
try {
body.append(
"storageManager_persisted",
- String(await navigator.storage.persisted())
+ String(await navigator.storage.persisted()),
);
} catch (e) {}
} else if (document.hasStorageAccess) {
@@ -222,7 +222,7 @@ export function useSubmitRageshake(): {
try {
body.append(
"storageManager_persisted",
- String(await document.hasStorageAccess())
+ String(await document.hasStorageAccess()),
);
} catch (e) {}
}
@@ -240,7 +240,7 @@ export function useSubmitRageshake(): {
Object.keys(estimate.usageDetails).forEach((k) => {
body.append(
`storageManager_usage_${k}`,
- String(estimate.usageDetails![k])
+ String(estimate.usageDetails![k]),
);
});
}
@@ -257,14 +257,14 @@ export function useSubmitRageshake(): {
body.append(
"file",
gzip(ElementCallOpenTelemetry.instance.rageshakeProcessor!.dump()),
- "traces.json.gz"
+ "traces.json.gz",
);
}
if (opts.rageshakeRequestId) {
body.append(
"group_call_rageshake_request_id",
- opts.rageshakeRequestId
+ opts.rageshakeRequestId,
);
}
@@ -279,7 +279,7 @@ export function useSubmitRageshake(): {
logger.error(error);
}
},
- [client, sending]
+ [client, sending],
);
return {
@@ -292,7 +292,7 @@ export function useSubmitRageshake(): {
export function useRageshakeRequest(): (
roomId: string,
- rageshakeRequestId: string
+ rageshakeRequestId: string,
) => void {
const { client } = useClient();
@@ -302,14 +302,14 @@ export function useRageshakeRequest(): (
request_id: rageshakeRequestId,
});
},
- [client]
+ [client],
);
return sendRageshakeRequest;
}
export function useRageshakeRequestModal(
- roomId: string
+ roomId: string,
): ComponentProps {
const [open, setOpen] = useState(false);
const onDismiss = useCallback(() => setOpen(false), [setOpen]);
@@ -319,7 +319,7 @@ export function useRageshakeRequestModal(
useEffect(() => {
if (!client) return;
- const onEvent = (event: MatrixEvent) => {
+ const onEvent = (event: MatrixEvent): void => {
const type = event.getType();
if (
diff --git a/src/settings/useSetting.ts b/src/settings/useSetting.ts
index d9dac3bd..399ed356 100644
--- a/src/settings/useSetting.ts
+++ b/src/settings/useSetting.ts
@@ -38,13 +38,13 @@ export const useSetting = (name: string, defaultValue: T): Setting => {
const value = useMemo(
() => (item == null ? defaultValue : JSON.parse(item)),
- [item, defaultValue]
+ [item, defaultValue],
);
const setValue = useCallback(
(value: T) => {
setItem(JSON.stringify(value));
},
- [setItem]
+ [setItem],
);
return [value, setValue];
@@ -55,15 +55,15 @@ export const getSetting = (name: string, defaultValue: T): T => {
return item === null ? defaultValue : JSON.parse(item);
};
-export const setSetting = (name: string, newValue: T) =>
+export const setSetting = (name: string, newValue: T): void =>
setLocalStorageItem(getSettingKey(name), JSON.stringify(newValue));
-export const isFirefox = () => {
+export const isFirefox = (): boolean => {
const { userAgent } = navigator;
return userAgent.includes("Firefox");
};
-const canEnableSpatialAudio = () => {
+const canEnableSpatialAudio = (): boolean => {
// Spatial audio means routing audio through audio contexts. On Chrome,
// this bypasses the AEC processor and so breaks echo cancellation.
// We only allow spatial audio to be enabled on Firefox which we know
@@ -94,22 +94,22 @@ export const useOptInAnalytics = (): DisableableSetting => {
export const useEnableE2EE = (): DisableableSetting => {
const settingVal = useSetting(
"enable-end-to-end-encryption",
- true
+ true,
);
if (isE2EESupported()) return settingVal;
return [false, null];
};
-export const useDeveloperSettingsTab = () =>
+export const useDeveloperSettingsTab = (): Setting =>
useSetting("developer-settings-tab", false);
-export const useShowConnectionStats = () =>
+export const useShowConnectionStats = (): Setting =>
useSetting("show-connection-stats", false);
-export const useAudioInput = () =>
+export const useAudioInput = (): Setting =>
useSetting("audio-input", undefined);
-export const useAudioOutput = () =>
+export const useAudioOutput = (): Setting =>
useSetting("audio-output", undefined);
-export const useVideoInput = () =>
+export const useVideoInput = (): Setting =>
useSetting("video-input", undefined);
diff --git a/src/tabs/Tabs.tsx b/src/tabs/Tabs.tsx
index 5ed75a05..953edd15 100644
--- a/src/tabs/Tabs.tsx
+++ b/src/tabs/Tabs.tsx
@@ -29,7 +29,7 @@ interface TabContainerProps extends TabListProps {
}
export function TabContainer(
- props: TabContainerProps
+ props: TabContainerProps,
): JSX.Element {
const state = useTabListState(props);
const ref = useRef(null);
diff --git a/src/typography/Typography.tsx b/src/typography/Typography.tsx
index 2c3687ae..ce55fc96 100644
--- a/src/typography/Typography.tsx
+++ b/src/typography/Typography.tsx
@@ -39,7 +39,7 @@ export const Headline = forwardRef(
overflowEllipsis,
...rest
},
- ref
+ ref,
) => {
return createElement(
Component,
@@ -48,13 +48,13 @@ export const Headline = forwardRef(
className: classNames(
styles[fontWeight ?? ""],
{ [styles.overflowEllipsis]: overflowEllipsis },
- className
+ className,
),
ref,
},
- children
+ children,
);
- }
+ },
);
export const Title = forwardRef(
@@ -67,7 +67,7 @@ export const Title = forwardRef(
overflowEllipsis,
...rest
},
- ref
+ ref,
) => {
return createElement(
Component,
@@ -76,13 +76,13 @@ export const Title = forwardRef(
className: classNames(
styles[fontWeight ?? ""],
{ [styles.overflowEllipsis]: overflowEllipsis },
- className
+ className,
),
ref,
},
- children
+ children,
);
- }
+ },
);
export const Subtitle = forwardRef(
@@ -95,7 +95,7 @@ export const Subtitle = forwardRef(
overflowEllipsis,
...rest
},
- ref
+ ref,
) => {
return createElement(
Component,
@@ -104,13 +104,13 @@ export const Subtitle = forwardRef(
className: classNames(
styles[fontWeight ?? ""],
{ [styles.overflowEllipsis]: overflowEllipsis },
- className
+ className,
),
ref,
},
- children
+ children,
);
- }
+ },
);
export const Body = forwardRef(
@@ -123,7 +123,7 @@ export const Body = forwardRef(
overflowEllipsis,
...rest
},
- ref
+ ref,
) => {
return createElement(
Component,
@@ -132,13 +132,13 @@ export const Body = forwardRef(
className: classNames(
styles[fontWeight ?? ""],
{ [styles.overflowEllipsis]: overflowEllipsis },
- className
+ className,
),
ref,
},
- children
+ children,
);
- }
+ },
);
export const Caption = forwardRef(
@@ -151,7 +151,7 @@ export const Caption = forwardRef(
overflowEllipsis,
...rest
},
- ref
+ ref,
) => {
return createElement(
Component,
@@ -161,13 +161,13 @@ export const Caption = forwardRef(
styles.caption,
styles[fontWeight ?? ""],
{ [styles.overflowEllipsis]: overflowEllipsis },
- className
+ className,
),
ref,
},
- children
+ children,
);
- }
+ },
);
export const Micro = forwardRef(
@@ -180,7 +180,7 @@ export const Micro = forwardRef(
overflowEllipsis,
...rest
},
- ref
+ ref,
) => {
return createElement(
Component,
@@ -190,13 +190,13 @@ export const Micro = forwardRef(
styles.micro,
styles[fontWeight ?? ""],
{ [styles.overflowEllipsis]: overflowEllipsis },
- className
+ className,
),
ref,
},
- children
+ children,
);
- }
+ },
);
interface LinkProps extends TypographyProps {
@@ -217,7 +217,7 @@ export const Link = forwardRef(
overflowEllipsis,
...rest
},
- ref
+ ref,
) => {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
@@ -246,11 +246,11 @@ export const Link = forwardRef(
styles[color],
styles[fontWeight ?? ""],
{ [styles.overflowEllipsis]: overflowEllipsis },
- className
+ className,
),
ref: ref,
},
- children
+ children,
);
- }
+ },
);
diff --git a/src/useCallViewKeyboardShortcuts.ts b/src/useCallViewKeyboardShortcuts.ts
index b0ce527e..9f4ba558 100644
--- a/src/useCallViewKeyboardShortcuts.ts
+++ b/src/useCallViewKeyboardShortcuts.ts
@@ -34,8 +34,8 @@ export function useCallViewKeyboardShortcuts(
focusElement: RefObject,
toggleMicrophoneMuted: () => void,
toggleLocalVideoMuted: () => void,
- setMicrophoneMuted: (muted: boolean) => void
-) {
+ setMicrophoneMuted: (muted: boolean) => void,
+): void {
const spacebarHeld = useRef(false);
// These event handlers are set on the window because we want users to be able
@@ -63,8 +63,8 @@ export function useCallViewKeyboardShortcuts(
toggleLocalVideoMuted,
toggleMicrophoneMuted,
setMicrophoneMuted,
- ]
- )
+ ],
+ ),
);
useEventTarget(
@@ -80,8 +80,8 @@ export function useCallViewKeyboardShortcuts(
setMicrophoneMuted(true);
}
},
- [focusElement, setMicrophoneMuted]
- )
+ [focusElement, setMicrophoneMuted],
+ ),
);
useEventTarget(
@@ -92,6 +92,6 @@ export function useCallViewKeyboardShortcuts(
spacebarHeld.current = false;
setMicrophoneMuted(true);
}
- }, [setMicrophoneMuted, spacebarHeld])
+ }, [setMicrophoneMuted, spacebarHeld]),
);
}
diff --git a/src/useEvents.ts b/src/useEvents.ts
index 8e513dac..a116a050 100644
--- a/src/useEvents.ts
+++ b/src/useEvents.ts
@@ -24,12 +24,12 @@ import type {
} from "matrix-js-sdk/src/models/typed-event-emitter";
// Shortcut for registering a listener on an EventTarget
-export const useEventTarget = (
+export function useEventTarget(
target: EventTarget | null | undefined,
eventType: string,
listener: (event: T) => void,
- options?: AddEventListenerOptions
-) => {
+ options?: AddEventListenerOptions,
+): void {
useEffect(() => {
if (target) {
target.addEventListener(eventType, listener as EventListener, options);
@@ -37,43 +37,43 @@ export const useEventTarget = (
target.removeEventListener(
eventType,
listener as EventListener,
- options
+ options,
);
}
}, [target, eventType, listener, options]);
-};
+}
// Shortcut for registering a listener on a TypedEventEmitter
-export const useTypedEventEmitter = <
+export function useTypedEventEmitter<
Events extends string,
Arguments extends ListenerMap,
- T extends Events
+ T extends Events,
>(
emitter: TypedEventEmitter,
eventType: T,
- listener: Listener
-) => {
+ listener: Listener