From 785eca72895b65ecec7169d183d061abb25ef8a5 Mon Sep 17 00:00:00 2001 From: Timo K Date: Mon, 6 Jun 2022 22:33:13 +0200 Subject: [PATCH 01/49] typescript src/profile --- .../{ProfileModal.jsx => ProfileModal.tsx} | 18 +++++++++---- src/profile/{useProfile.js => useProfile.ts} | 25 ++++++++++++++++--- 2 files changed, 34 insertions(+), 9 deletions(-) rename src/profile/{ProfileModal.jsx => ProfileModal.tsx} (87%) rename src/profile/{useProfile.js => useProfile.ts} (83%) diff --git a/src/profile/ProfileModal.jsx b/src/profile/ProfileModal.tsx similarity index 87% rename from src/profile/ProfileModal.jsx rename to src/profile/ProfileModal.tsx index b0dee0e6..5711ef11 100644 --- a/src/profile/ProfileModal.jsx +++ b/src/profile/ProfileModal.tsx @@ -15,6 +15,8 @@ limitations under the License. */ import React, { useCallback, useEffect, useState } from "react"; +import { MatrixClient } from "matrix-js-sdk"; + import { Button } from "../button"; import { useProfile } from "./useProfile"; import { FieldRow, InputField, ErrorMessage } from "../input/Input"; @@ -22,7 +24,12 @@ import { Modal, ModalContent } from "../Modal"; import { AvatarInputField } from "../input/AvatarInputField"; import styles from "./ProfileModal.module.css"; -export function ProfileModal({ client, ...rest }) { +interface Props { + client: MatrixClient; + onClose: () => {}; + [rest: string]: unknown; +} +export function ProfileModal({ client, ...rest }: Props) { const { onClose } = rest; const { success, @@ -51,12 +58,13 @@ export function ProfileModal({ client, ...rest }) { e.preventDefault(); const data = new FormData(e.target); const displayName = data.get("displayName"); - const avatar = data.get("avatar"); - + const avatar: File | string = data.get("avatar"); + const avatarSize = + typeof avatar == "string" ? avatar.length : avatar.size; saveProfile({ displayName, - avatar: avatar && avatar.size > 0 ? avatar : undefined, - removeAvatar: removeAvatar && (!avatar || avatar.size === 0), + avatar: avatar && avatarSize > 0 ? avatar : undefined, + removeAvatar: removeAvatar && (!avatar || avatarSize === 0), }); }, [saveProfile, removeAvatar] diff --git a/src/profile/useProfile.js b/src/profile/useProfile.ts similarity index 83% rename from src/profile/useProfile.js rename to src/profile/useProfile.ts index 2207dde2..6ae5024d 100644 --- a/src/profile/useProfile.js +++ b/src/profile/useProfile.ts @@ -14,9 +14,26 @@ See the License for the specific language governing permissions and limitations under the License. */ +import { MatrixClient, User } from "matrix-js-sdk"; import { useState, useCallback, useEffect } from "react"; -export function useProfile(client) { +interface ProfileResult { + loading: boolean; + error: Error; + displayName: string; + avatarUrl: string; + saveProfile: ({ + displayName, + avatar, + removeAvatar, + }: { + displayName: string; + avatar: any; + removeAvatar: boolean; + }) => Promise; + success: boolean; +} +export function useProfile(client: MatrixClient): ProfileResult { const [{ loading, displayName, avatarUrl, error, success }, setState] = useState(() => { const user = client?.getUser(client.getUserId()); @@ -31,7 +48,7 @@ export function useProfile(client) { }); useEffect(() => { - const onChangeUser = (_event, { displayName, avatarUrl }) => { + const onChangeUser = (_event: any, { displayName, avatarUrl }: any) => { setState({ success: false, loading: false, @@ -41,7 +58,7 @@ export function useProfile(client) { }); }; - let user; + let user: User; if (client) { const userId = client.getUserId(); @@ -71,7 +88,7 @@ export function useProfile(client) { try { await client.setDisplayName(displayName); - let mxcAvatarUrl; + let mxcAvatarUrl: string; if (removeAvatar) { await client.setAvatarUrl(""); From 190c57e85359965de49845bae9437dacffa356d2 Mon Sep 17 00:00:00 2001 From: Timo K Date: Mon, 6 Jun 2022 22:42:48 +0200 Subject: [PATCH 02/49] typescript src/settings --- .../{SettingsModal.jsx => SettingsModal.tsx} | 24 ++- src/settings/{rageshake.js => rageshake.ts} | 156 ++++++++++++------ ...ubmit-rageshake.js => submit-rageshake.ts} | 63 +++++-- ...seMediaHandler.jsx => useMediaHandler.tsx} | 103 ++++++++---- 4 files changed, 236 insertions(+), 110 deletions(-) rename src/settings/{SettingsModal.jsx => SettingsModal.tsx} (89%) rename src/settings/{rageshake.js => rageshake.ts} (81%) rename src/settings/{submit-rageshake.js => submit-rageshake.ts} (85%) rename src/settings/{useMediaHandler.jsx => useMediaHandler.tsx} (70%) diff --git a/src/settings/SettingsModal.jsx b/src/settings/SettingsModal.tsx similarity index 89% rename from src/settings/SettingsModal.jsx rename to src/settings/SettingsModal.tsx index ad2aeefa..0da14d6d 100644 --- a/src/settings/SettingsModal.jsx +++ b/src/settings/SettingsModal.tsx @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/ban-ts-comment */ /* Copyright 2022 Matrix.org Foundation C.I.C. @@ -15,6 +16,8 @@ limitations under the License. */ import React from "react"; +import { Item } from "@react-stately/collections"; + import { Modal } from "../Modal"; import styles from "./SettingsModal.module.css"; import { TabContainer, TabItem } from "../tabs/Tabs"; @@ -22,15 +25,23 @@ import { ReactComponent as AudioIcon } from "../icons/Audio.svg"; import { ReactComponent as VideoIcon } from "../icons/Video.svg"; import { ReactComponent as DeveloperIcon } from "../icons/Developer.svg"; import { SelectInput } from "../input/SelectInput"; -import { Item } from "@react-stately/collections"; -import { useMediaHandler } from "./useMediaHandler"; +import { + MediaHandlerContextInterface, + useMediaHandler, +} from "./useMediaHandler"; import { useSpatialAudio, useShowInspector } from "./useSetting"; import { FieldRow, InputField } from "../input/Input"; import { Button } from "../button"; import { useDownloadDebugLog } from "./submit-rageshake"; import { Body } from "../typography/Typography"; -export const SettingsModal = (props) => { +interface Props { + setShowInspector: boolean; + showInspector: boolean; + [rest: string]: unknown; +} + +export const SettingsModal = (props: Props) => { const { audioInput, audioInputs, @@ -42,6 +53,7 @@ export const SettingsModal = (props) => { audioOutputs, setAudioOutput, } = useMediaHandler(); + const [spatialAudio, setSpatialAudio] = useSpatialAudio(); const [showInspector, setShowInspector] = useShowInspector(); @@ -90,7 +102,8 @@ export const SettingsModal = (props) => { label="Spatial audio (experimental)" type="checkbox" checked={spatialAudio} - onChange={(e) => setSpatialAudio(e.target.checked)} + // @ts-ignore + onChange={(event: Event) => setSpatialAudio(event.target.checked)} /> @@ -132,7 +145,8 @@ export const SettingsModal = (props) => { label="Show Call Inspector" type="checkbox" checked={showInspector} - onChange={(e) => setShowInspector(e.target.checked)} + // @ts-ignore + onChange={(e: Event) => setShowInspector(e.target.checked)} /> diff --git a/src/settings/rageshake.js b/src/settings/rageshake.ts similarity index 81% rename from src/settings/rageshake.js rename to src/settings/rageshake.ts index c9e855ee..702ecb4f 100644 --- a/src/settings/rageshake.js +++ b/src/settings/rageshake.ts @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/ban-ts-comment */ /* Copyright 2017 OpenMarket Ltd Copyright 2018 New Vector Ltd @@ -37,37 +38,60 @@ limitations under the License. // actually timestamps. We then purge the remaining logs. We also do this // purge on startup to prevent logs from accumulating. -// the frequency with which we flush to indexeddb import { logger } from "matrix-js-sdk/src/logger"; +// the frequency with which we flush to indexeddb const FLUSH_RATE_MS = 30 * 1000; // the length of log data we keep in indexeddb (and include in the reports) const MAX_LOG_SIZE = 1024 * 1024 * 5; // 5 MB // A class which monkey-patches the global console and stores log lines. + +interface LogEntry { + id: string; + lines: Array; + index: number; +} + +interface Cursor { + id: string; + ts: number; +} + +// interface CustomEventTarget extends EventTarget { +// result: Cursor; +// } export class ConsoleLogger { logs = ""; - monkeyPatch(consoleObj) { + monkeyPatch(consoleObj: Console): void { // Monkey-patch console logging - const consoleFunctionsToLevels = { + + const consoleFunctionsToLevels: { [level: string]: string } = { log: "I", info: "I", warn: "W", error: "E", }; - Object.keys(consoleFunctionsToLevels).forEach((fnName) => { - const level = consoleFunctionsToLevels[fnName]; - const originalFn = consoleObj[fnName].bind(consoleObj); - consoleObj[fnName] = (...args) => { - this.log(level, ...args); - originalFn(...args); - }; - }); - } - log(level, ...args) { + Object.keys(consoleFunctionsToLevels).forEach( + (fnName: "log" | "info" | "warn" | "error") => { + const level = consoleFunctionsToLevels[fnName]; + const originalFn = consoleObj[fnName].bind(consoleObj); + consoleObj[fnName] = (...args: unknown[]) => { + this.log(level, ...args); + originalFn(...args); + }; + } + ); + } + // these functions get overwritten by the monkey patch + error(...args: unknown[]): void {} + warn(...args: unknown[]): void {} + info(...args: unknown[]): void {} + + log(level: string, ...args: unknown[]): void { // We don't know what locale the user may be running so use ISO strings const ts = new Date().toISOString(); @@ -113,10 +137,10 @@ export class ConsoleLogger { /** * Retrieve log lines to flush to disk. - * @param {boolean} keepLogs True to not delete logs after flushing. + * @param {boolean} keepLogs True to not delete logs after flushing. Defaults to false. * @return {string} \n delimited log lines to flush. */ - flush(keepLogs) { + flush(keepLogs = false): string { // The ConsoleLogger doesn't care how these end up on disk, it just // flushes them to the caller. if (keepLogs) { @@ -131,11 +155,14 @@ export class ConsoleLogger { // A class which stores log lines in an IndexedDB instance. export class IndexedDBLogStore { index = 0; - db = null; - flushPromise = null; - flushAgainPromise = null; + db: IDBDatabase = null; + flushPromise: Promise = null; + flushAgainPromise: Promise = null; + indexedDB: IDBFactory; + logger: ConsoleLogger; + id: string; - constructor(indexedDB, logger) { + constructor(indexedDB: IDBFactory, logger: ConsoleLogger) { this.indexedDB = indexedDB; this.logger = logger; this.id = "instance-" + Math.random() + Date.now(); @@ -144,10 +171,10 @@ export class IndexedDBLogStore { /** * @return {Promise} Resolves when the store is ready. */ - connect() { + connect(): Promise { const req = this.indexedDB.open("logs"); - return new Promise((resolve, reject) => { - req.onsuccess = (event) => { + return new Promise((resolve, reject) => { + req.onsuccess = (event: Event) => { // @ts-ignore this.db = event.target.result; // Periodically flush logs to local storage / indexeddb @@ -155,16 +182,16 @@ export class IndexedDBLogStore { resolve(); }; - req.onerror = (event) => { + req.onerror = (event: Event) => { const err = // @ts-ignore "Failed to open log database: " + event.target.error.name; - logger.error(err); + this.logger.error(err); reject(new Error(err)); }; // First time: Setup the object store - req.onupgradeneeded = (event) => { + req.onupgradeneeded = (event: IDBVersionChangeEvent) => { // @ts-ignore const db = event.target.result; const logObjStore = db.createObjectStore("logs", { @@ -176,7 +203,7 @@ export 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."]) ); const lastModifiedStore = db.createObjectStore("logslastmod", { @@ -206,7 +233,7 @@ export class IndexedDBLogStore { * * @return {Promise} Resolved when the logs have been flushed. */ - flush() { + flush(): Promise { // check if a flush() operation is ongoing if (this.flushPromise) { if (this.flushAgainPromise) { @@ -225,7 +252,7 @@ export class IndexedDBLogStore { } // there is no flush promise or there was but it has finished, so do // a brand new one, destroying the chain which may have been built up. - this.flushPromise = new Promise((resolve, reject) => { + this.flushPromise = new Promise((resolve, reject) => { if (!this.db) { // not connected yet or user rejected access for us to r/w to the db. reject(new Error("No connected database")); @@ -242,10 +269,11 @@ export class IndexedDBLogStore { resolve(); }; txn.onerror = (event) => { - logger.error("Failed to flush logs : ", event); + this.logger.error("Failed to flush logs : ", event); + // @ts-ignore reject(new Error("Failed to write logs: " + event.target.errorCode)); }; - objStore.add(this.generateLogEntry(lines)); + objStore.add(this.generateLogEntry(lines.split("\n"))); const lastModStore = txn.objectStore("logslastmod"); lastModStore.put(this.generateLastModifiedTime()); }).then(() => { @@ -264,12 +292,13 @@ export class IndexedDBLogStore { * log ID). The objects have said log ID in an "id" field and "lines" which * is a big string with all the new-line delimited logs. */ - async consume() { + + async consume(): Promise { const db = this.db; // Returns: a string representing the concatenated logs for this ID. // Stops adding log fragments when the size exceeds maxSize - function fetchLogs(id, maxSize) { + function fetchLogs(id: string, maxSize: number): Promise { const objectStore = db .transaction("logs", "readonly") .objectStore("logs"); @@ -280,34 +309,35 @@ export class IndexedDBLogStore { .openCursor(IDBKeyRange.only(id), "prev"); let lines = ""; query.onerror = (event) => { + // @ts-ignore reject(new Error("Query failed: " + event.target.errorCode)); }; query.onsuccess = (event) => { + // @ts-ignore const cursor = event.target.result; if (!cursor) { - resolve(lines); + resolve(lines.split("\n")); return; // end of results } lines = cursor.value.lines + lines; if (lines.length >= maxSize) { - resolve(lines); + resolve(lines.split("\n")); } else { cursor.continue(); } }; }); } - // Returns: A sorted array of log IDs. (newest first) - function fetchLogIds() { + function fetchLogIds(): Promise { // To gather all the log IDs, query for all records in logslastmod. const o = db .transaction("logslastmod", "readonly") .objectStore("logslastmod"); - return selectQuery(o, undefined, (cursor) => { + return selectQuery(o, undefined, (cursor: Cursor) => { return { - id: cursor.value.id, - ts: cursor.value.ts, + id: cursor.id, + ts: cursor.ts, }; }).then((res) => { // Sort IDs by timestamp (newest first) @@ -318,14 +348,14 @@ export class IndexedDBLogStore { .map((a) => a.id); }); } - - function deleteLogs(id) { + function deleteLogs(id: string): Promise { return new Promise((resolve, reject) => { const txn = db.transaction(["logs", "logslastmod"], "readwrite"); 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 = (event) => { + // @ts-ignore const cursor = event.target.result; if (!cursor) { return; @@ -340,6 +370,7 @@ export class IndexedDBLogStore { reject( new Error( "Failed to delete logs for " + + // @ts-ignore `'${id}' : ${event.target.errorCode}` ) ); @@ -351,11 +382,14 @@ export class IndexedDBLogStore { } const allLogIds = await fetchLogIds(); - let removeLogIds = []; + let removeLogIds: string[] = []; const logs = []; let size = 0; for (let i = 0; i < allLogIds.length; i++) { - const lines = await fetchLogs(allLogIds[i], MAX_LOG_SIZE - size); + const lines: string[] = await fetchLogs( + allLogIds[i], + MAX_LOG_SIZE - size + ); // always add the log file: fetchLogs will truncate once the maxSize we give it is // exceeded, so we'll go over the max but only by one fragment's worth. @@ -375,22 +409,22 @@ export class IndexedDBLogStore { } } if (removeLogIds.length > 0) { - logger.log("Removing logs: ", removeLogIds); + this.logger.log("Removing logs: ", removeLogIds); // Don't await this because it's non-fatal if we can't clean up // logs. Promise.all(removeLogIds.map((id) => deleteLogs(id))).then( () => { - logger.log(`Removed ${removeLogIds.length} old logs.`); + this.logger.log(`Removed ${removeLogIds.length} old logs.`); }, (err) => { - logger.error(err); + this.logger.error(err); } ); } return logs; } - generateLogEntry(lines) { + generateLogEntry(lines: string[]): LogEntry { return { id: this.id, lines: lines, @@ -416,18 +450,22 @@ export class IndexedDBLogStore { * @return {Promise} Resolves to an array of whatever you returned from * resultMapper. */ -function selectQuery(store, keyRange, resultMapper) { +function selectQuery( + store: IDBObjectStore, + keyRange: IDBKeyRange, + resultMapper: (arg: Cursor) => Cursor +): Promise { const query = store.openCursor(keyRange); return new Promise((resolve, reject) => { - const results = []; - query.onerror = (event) => { + const results: Cursor[] = []; + query.onerror = (event: Event) => { // @ts-ignore reject(new Error("Query failed: " + event.target.errorCode)); }; // collect results - query.onsuccess = (event) => { + query.onsuccess = (event: Event) => { // @ts-ignore - const cursor = event.target.result; + const cursor = event.target.result?.value; if (!cursor) { resolve(results); return; // end of results @@ -437,6 +475,16 @@ function selectQuery(store, keyRange, resultMapper) { }; }); } +declare global { + // eslint-disable-next-line no-var, camelcase + var mx_rage_store: IndexedDBLogStore; + // eslint-disable-next-line no-var, camelcase + var mx_rage_logger: ConsoleLogger; + // eslint-disable-next-line no-var, camelcase + var mx_rage_initPromise: Promise; + // eslint-disable-next-line no-var, camelcase + var mx_rage_initStoragePromise: Promise; +} /** * Configure rage shaking support for sending bug reports. @@ -445,7 +493,7 @@ function selectQuery(store, keyRange, resultMapper) { * be set up immediately for the logs. * @return {Promise} Resolves when set up. */ -export function init(setUpPersistence = true) { +export function init(setUpPersistence = true): Promise { if (global.mx_rage_initPromise) { return global.mx_rage_initPromise; } @@ -465,7 +513,7 @@ export function init(setUpPersistence = true) { * then this no-ops. * @return {Promise} Resolves when complete. */ -export function tryInitStorage() { +export function tryInitStorage(): Promise { if (global.mx_rage_initStoragePromise) { return global.mx_rage_initStoragePromise; } diff --git a/src/settings/submit-rageshake.js b/src/settings/submit-rageshake.ts similarity index 85% rename from src/settings/submit-rageshake.js rename to src/settings/submit-rageshake.ts index 3c10e574..0072b0a2 100644 --- a/src/settings/submit-rageshake.js +++ b/src/settings/submit-rageshake.ts @@ -15,14 +15,30 @@ limitations under the License. */ import { useCallback, useContext, useEffect, useState } from "react"; -import { getLogsForReport } from "./rageshake"; import pako from "pako"; +import { ClientEvent, MatrixClient, MatrixEvent } from "matrix-js-sdk"; +import { OverlayTriggerState } from "@react-stately/overlays"; + +import { getLogsForReport } from "./rageshake"; import { useClient } from "../ClientContext"; import { InspectorContext } from "../room/GroupCallInspector"; import { useModalTriggerState } from "../Modal"; -export function useSubmitRageshake() { - const { client } = useClient(); +interface RageShakeSubmitOptions { + description: string; + roomId: string; + label: string; + sendLogs: boolean; + rageshakeRequestId: string; +} + +export function useSubmitRageshake(): { + submitRageshake: (opts: RageShakeSubmitOptions) => Promise; + sending: boolean; + sent: boolean; + error: Error; +} { + const client: MatrixClient = useClient().client; const [{ json }] = useContext(InspectorContext); const [{ sending, sent, error }, setState] = useState({ @@ -57,9 +73,12 @@ export function useSubmitRageshake() { opts.description || "User did not supply any additional text." ); body.append("app", "matrix-video-chat"); - body.append("version", import.meta.env.VITE_APP_VERSION || "dev"); + body.append( + "version", + (import.meta.env.VITE_APP_VERSION as string) || "dev" + ); body.append("user_agent", userAgent); - body.append("installed_pwa", false); + body.append("installed_pwa", "false"); body.append("touch_input", touchInput); if (client) { @@ -181,7 +200,11 @@ export function useSubmitRageshake() { if (navigator.storage && navigator.storage.estimate) { try { - const estimate = await navigator.storage.estimate(); + const estimate: { + quota?: number; + usage?: number; + usageDetails?: { [x: string]: unknown }; + } = await navigator.storage.estimate(); body.append("storageManager_quota", String(estimate.quota)); body.append("storageManager_usage", String(estimate.usage)); if (estimate.usageDetails) { @@ -200,7 +223,11 @@ export function useSubmitRageshake() { for (const entry of logs) { // encode as UTF-8 - let buf = new TextEncoder().encode(entry.lines); + let buf = new TextEncoder().encode( + typeof entry.lines == "string" + ? entry.lines + : entry.lines.join("\n") + ); // compress buf = pako.gzip(buf); @@ -225,7 +252,7 @@ export function useSubmitRageshake() { } await fetch( - import.meta.env.VITE_RAGESHAKE_SUBMIT_URL || + (import.meta.env.VITE_RAGESHAKE_SUBMIT_URL as string) || "https://element.io/bugreports/submit", { method: "POST", @@ -250,7 +277,7 @@ export function useSubmitRageshake() { }; } -export function useDownloadDebugLog() { +export function useDownloadDebugLog(): () => void { const [{ json }] = useContext(InspectorContext); const downloadDebugLog = useCallback(() => { @@ -271,7 +298,10 @@ export function useDownloadDebugLog() { return downloadDebugLog; } -export function useRageshakeRequest() { +export function useRageshakeRequest(): ( + roomId: string, + rageshakeRequestId: string +) => void { const { client } = useClient(); const sendRageshakeRequest = useCallback( @@ -286,13 +316,16 @@ export function useRageshakeRequest() { return sendRageshakeRequest; } -export function useRageshakeRequestModal(roomId) { +export function useRageshakeRequestModal(roomId: string): { + modalState: OverlayTriggerState; + modalProps: any; +} { const { modalState, modalProps } = useModalTriggerState(); - const { client } = useClient(); + const client: MatrixClient = useClient().client; const [rageshakeRequestId, setRageshakeRequestId] = useState(); useEffect(() => { - const onEvent = (event) => { + const onEvent = (event: MatrixEvent) => { const type = event.getType(); if ( @@ -305,10 +338,10 @@ export function useRageshakeRequestModal(roomId) { } }; - client.on("event", onEvent); + client.on(ClientEvent.Event, onEvent); return () => { - client.removeListener("event", onEvent); + client.removeListener(ClientEvent.Event, onEvent); }; }, [modalState.open, roomId, client, modalState]); diff --git a/src/settings/useMediaHandler.jsx b/src/settings/useMediaHandler.tsx similarity index 70% rename from src/settings/useMediaHandler.jsx rename to src/settings/useMediaHandler.tsx index a9c4a76f..b8de0f7d 100644 --- a/src/settings/useMediaHandler.jsx +++ b/src/settings/useMediaHandler.tsx @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/ban-ts-comment */ /* Copyright 2022 Matrix.org Foundation C.I.C. @@ -14,6 +15,7 @@ See the License for the specific language governing permissions and limitations under the License. */ +import { MatrixClient } from "matrix-js-sdk"; import React, { useState, useEffect, @@ -23,9 +25,27 @@ import React, { createContext, } from "react"; -const MediaHandlerContext = createContext(); +export interface MediaHandlerContextInterface { + audioInput: string; + audioInputs: MediaDeviceInfo[]; + setAudioInput: (deviceId: string) => void; + videoInput: string; + videoInputs: MediaDeviceInfo[]; + setVideoInput: (deviceId: string) => void; + audioOutput: string; + audioOutputs: MediaDeviceInfo[]; + setAudioOutput: (deviceId: string) => void; +} -function getMediaPreferences() { +const MediaHandlerContext = + createContext(undefined); + +interface MediaPreferences { + audioInput?: string; + videoInput?: string; + audioOutput?: string; +} +function getMediaPreferences(): MediaPreferences { const mediaPreferences = localStorage.getItem("matrix-media-preferences"); if (mediaPreferences) { @@ -39,8 +59,8 @@ function getMediaPreferences() { } } -function updateMediaPreferences(newPreferences) { - const oldPreferences = getMediaPreferences(newPreferences); +function updateMediaPreferences(newPreferences: MediaPreferences): void { + const oldPreferences = getMediaPreferences(); localStorage.setItem( "matrix-media-preferences", @@ -50,8 +70,11 @@ function updateMediaPreferences(newPreferences) { }) ); } - -export function MediaHandlerProvider({ client, children }) { +interface Props { + client: MatrixClient; + children: JSX.Element[]; +} +export function MediaHandlerProvider({ client, children }: Props): JSX.Element { const [ { audioInput, @@ -72,7 +95,9 @@ export function MediaHandlerProvider({ client, children }) { ); return { + // @ts-ignore audioInput: mediaHandler.audioInput, + // @ts-ignore videoInput: mediaHandler.videoInput, audioOutput: undefined, audioInputs: [], @@ -84,7 +109,7 @@ export function MediaHandlerProvider({ client, children }) { useEffect(() => { const mediaHandler = client.getMediaHandler(); - function updateDevices() { + function updateDevices(): void { navigator.mediaDevices.enumerateDevices().then((devices) => { const mediaPreferences = getMediaPreferences(); @@ -92,9 +117,10 @@ export function MediaHandlerProvider({ client, children }) { (device) => device.kind === "audioinput" ); const audioConnected = audioInputs.some( + // @ts-ignore (device) => device.deviceId === mediaHandler.audioInput ); - + // @ts-ignore let audioInput = mediaHandler.audioInput; if (!audioConnected && audioInputs.length > 0) { @@ -105,9 +131,11 @@ export function MediaHandlerProvider({ client, children }) { (device) => device.kind === "videoinput" ); const videoConnected = videoInputs.some( + // @ts-ignore (device) => device.deviceId === mediaHandler.videoInput ); + // @ts-ignore let videoInput = mediaHandler.videoInput; if (!videoConnected && videoInputs.length > 0) { @@ -129,7 +157,9 @@ export function MediaHandlerProvider({ client, children }) { } if ( + // @ts-ignore mediaHandler.videoInput !== videoInput || + // @ts-ignore mediaHandler.audioInput !== audioInput ) { mediaHandler.setMediaInputs(audioInput, videoInput); @@ -159,8 +189,8 @@ export function MediaHandlerProvider({ client, children }) { }; }, [client]); - const setAudioInput = useCallback( - (deviceId) => { + const setAudioInput: (deviceId: string) => void = useCallback( + (deviceId: string) => { updateMediaPreferences({ audioInput: deviceId }); setState((prevState) => ({ ...prevState, audioInput: deviceId })); client.getMediaHandler().setAudioInput(deviceId); @@ -168,7 +198,7 @@ export function MediaHandlerProvider({ client, children }) { [client] ); - const setVideoInput = useCallback( + const setVideoInput: (deviceId: string) => void = useCallback( (deviceId) => { updateMediaPreferences({ videoInput: deviceId }); setState((prevState) => ({ ...prevState, videoInput: deviceId })); @@ -177,35 +207,36 @@ export function MediaHandlerProvider({ client, children }) { [client] ); - const setAudioOutput = useCallback((deviceId) => { + const setAudioOutput: (deviceId: any) => void = useCallback((deviceId) => { updateMediaPreferences({ audioOutput: deviceId }); setState((prevState) => ({ ...prevState, audioOutput: deviceId })); }, []); - const context = useMemo( - () => ({ - audioInput, - audioInputs, - setAudioInput, - videoInput, - videoInputs, - setVideoInput, - audioOutput, - audioOutputs, - setAudioOutput, - }), - [ - audioInput, - audioInputs, - setAudioInput, - videoInput, - videoInputs, - setVideoInput, - audioOutput, - audioOutputs, - setAudioOutput, - ] - ); + const context: MediaHandlerContextInterface = + useMemo( + () => ({ + audioInput, + audioInputs, + setAudioInput, + videoInput, + videoInputs, + setVideoInput, + audioOutput, + audioOutputs, + setAudioOutput, + }), + [ + audioInput, + audioInputs, + setAudioInput, + videoInput, + videoInputs, + setVideoInput, + audioOutput, + audioOutputs, + setAudioOutput, + ] + ); return ( From 23098131b8ab5af8665e630d10e2e7a65a929a93 Mon Sep 17 00:00:00 2001 From: Timo K Date: Wed, 8 Jun 2022 16:36:22 +0200 Subject: [PATCH 03/49] couple of cleanups ModalProps fixes LogEntry interface missing return promise --- src/settings/rageshake.ts | 28 +++++++++++++--------------- src/settings/submit-rageshake.ts | 27 +++++++++++++++++---------- 2 files changed, 30 insertions(+), 25 deletions(-) diff --git a/src/settings/rageshake.ts b/src/settings/rageshake.ts index 702ecb4f..d895f53c 100644 --- a/src/settings/rageshake.ts +++ b/src/settings/rageshake.ts @@ -50,8 +50,8 @@ const MAX_LOG_SIZE = 1024 * 1024 * 5; // 5 MB interface LogEntry { id: string; - lines: Array; - index: number; + lines: string; + index?: number; } interface Cursor { @@ -59,9 +59,6 @@ interface Cursor { ts: number; } -// interface CustomEventTarget extends EventTarget { -// result: Cursor; -// } export class ConsoleLogger { logs = ""; @@ -86,6 +83,7 @@ export class ConsoleLogger { } ); } + // these functions get overwritten by the monkey patch error(...args: unknown[]): void {} warn(...args: unknown[]): void {} @@ -203,7 +201,7 @@ export 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.") ); const lastModifiedStore = db.createObjectStore("logslastmod", { @@ -273,7 +271,7 @@ export class IndexedDBLogStore { // @ts-ignore reject(new Error("Failed to write logs: " + event.target.errorCode)); }; - objStore.add(this.generateLogEntry(lines.split("\n"))); + objStore.add(this.generateLogEntry(lines)); const lastModStore = txn.objectStore("logslastmod"); lastModStore.put(this.generateLastModifiedTime()); }).then(() => { @@ -424,7 +422,7 @@ export class IndexedDBLogStore { return logs; } - generateLogEntry(lines: string[]): LogEntry { + generateLogEntry(lines: string): LogEntry { return { id: this.id, lines: lines, @@ -432,7 +430,7 @@ export class IndexedDBLogStore { }; } - generateLastModifiedTime() { + generateLastModifiedTime(): Cursor { return { id: this.id, ts: Date.now(), @@ -539,7 +537,7 @@ export function tryInitStorage(): Promise { return global.mx_rage_initStoragePromise; } -export function flush() { +export function flush(): Promise { if (!global.mx_rage_store) { return; } @@ -550,7 +548,7 @@ export function flush() { * Clean up old logs. * @return {Promise} Resolves if cleaned logs. */ -export async function cleanup() { +export async function cleanup(): Promise { if (!global.mx_rage_store) { return; } @@ -560,9 +558,9 @@ export async function cleanup() { /** * Get a recent snapshot of the logs, ready for attaching to a bug report * - * @return {Array<{lines: string, id, string}>} list of log data + * @return {LogEntry[]} list of log data */ -export async function getLogsForReport() { +export async function getLogsForReport(): Promise { if (!global.mx_rage_logger) { throw new Error("No console logger, did you forget to call init()?"); } @@ -571,13 +569,13 @@ export async function getLogsForReport() { if (global.mx_rage_store) { // flush most recent logs await global.mx_rage_store.flush(); - return await global.mx_rage_store.consume(); + return (await global.mx_rage_store.consume()) as LogEntry[]; } else { return [ { lines: global.mx_rage_logger.flush(true), id: "-", }, - ]; + ] as LogEntry[]; } } diff --git a/src/settings/submit-rageshake.ts b/src/settings/submit-rageshake.ts index 0072b0a2..c51ad7e7 100644 --- a/src/settings/submit-rageshake.ts +++ b/src/settings/submit-rageshake.ts @@ -16,8 +16,10 @@ limitations under the License. import { useCallback, useContext, useEffect, useState } from "react"; import pako from "pako"; -import { ClientEvent, MatrixClient, MatrixEvent } from "matrix-js-sdk"; +import { MatrixEvent } from "matrix-js-sdk"; import { OverlayTriggerState } from "@react-stately/overlays"; +import { MatrixClient, ClientEvent } from "matrix-js-sdk/src/client"; +import { stringToBase } from "matrix-js-sdk/src/utils"; import { getLogsForReport } from "./rageshake"; import { useClient } from "../ClientContext"; @@ -223,12 +225,7 @@ export function useSubmitRageshake(): { for (const entry of logs) { // encode as UTF-8 - let buf = new TextEncoder().encode( - typeof entry.lines == "string" - ? entry.lines - : entry.lines.join("\n") - ); - + let buf = new TextEncoder().encode(entry.lines); // compress buf = pako.gzip(buf); @@ -315,14 +312,24 @@ export function useRageshakeRequest(): ( return sendRageshakeRequest; } +interface ModalProps { + isOpen: boolean; + onClose: () => void; +} +interface ModalPropsWithId extends ModalProps { + rageshakeRequestId: string; +} export function useRageshakeRequestModal(roomId: string): { modalState: OverlayTriggerState; - modalProps: any; + modalProps: ModalPropsWithId; } { - const { modalState, modalProps } = useModalTriggerState(); + const { modalState, modalProps } = useModalTriggerState() as { + modalState: OverlayTriggerState; + modalProps: ModalProps; + }; const client: MatrixClient = useClient().client; - const [rageshakeRequestId, setRageshakeRequestId] = useState(); + const [rageshakeRequestId, setRageshakeRequestId] = useState(); useEffect(() => { const onEvent = (event: MatrixEvent) => { From 0aa29f775cb523cec8d8af3a45f2de7ada1d6e5f Mon Sep 17 00:00:00 2001 From: Timo K Date: Wed, 8 Jun 2022 17:22:46 +0200 Subject: [PATCH 04/49] linter --- src/settings/SettingsModal.tsx | 5 +---- src/settings/submit-rageshake.ts | 1 - 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/src/settings/SettingsModal.tsx b/src/settings/SettingsModal.tsx index e94e1133..69e54846 100644 --- a/src/settings/SettingsModal.tsx +++ b/src/settings/SettingsModal.tsx @@ -25,10 +25,7 @@ import { ReactComponent as AudioIcon } from "../icons/Audio.svg"; import { ReactComponent as VideoIcon } from "../icons/Video.svg"; import { ReactComponent as DeveloperIcon } from "../icons/Developer.svg"; import { SelectInput } from "../input/SelectInput"; -import { - MediaHandlerContextInterface, - useMediaHandler, -} from "./useMediaHandler"; +import { useMediaHandler } from "./useMediaHandler"; import { useSpatialAudio, useShowInspector } from "./useSetting"; import { FieldRow, InputField } from "../input/Input"; import { Button } from "../button"; diff --git a/src/settings/submit-rageshake.ts b/src/settings/submit-rageshake.ts index c51ad7e7..20b0785e 100644 --- a/src/settings/submit-rageshake.ts +++ b/src/settings/submit-rageshake.ts @@ -19,7 +19,6 @@ import pako from "pako"; import { MatrixEvent } from "matrix-js-sdk"; import { OverlayTriggerState } from "@react-stately/overlays"; import { MatrixClient, ClientEvent } from "matrix-js-sdk/src/client"; -import { stringToBase } from "matrix-js-sdk/src/utils"; import { getLogsForReport } from "./rageshake"; import { useClient } from "../ClientContext"; From 60ed54d6d3b4c67024eb320cb339c5c78d8038dd Mon Sep 17 00:00:00 2001 From: Timo K Date: Sat, 11 Jun 2022 14:28:30 +0200 Subject: [PATCH 05/49] change rageshake.ts to be more similar to the matrix-js version --- src/settings/rageshake.ts | 181 +++++++++++++++++++------------------- 1 file changed, 91 insertions(+), 90 deletions(-) diff --git a/src/settings/rageshake.ts b/src/settings/rageshake.ts index d895f53c..885e4918 100644 --- a/src/settings/rageshake.ts +++ b/src/settings/rageshake.ts @@ -39,6 +39,7 @@ limitations under the License. // purge on startup to prevent logs from accumulating. import { logger } from "matrix-js-sdk/src/logger"; +import { randomString } from "matrix-js-sdk/src/randomstring"; // the frequency with which we flush to indexeddb const FLUSH_RATE_MS = 30 * 1000; @@ -46,6 +47,11 @@ const FLUSH_RATE_MS = 30 * 1000; // the length of log data we keep in indexeddb (and include in the reports) const MAX_LOG_SIZE = 1024 * 1024 * 5; // 5 MB +type LogFunction = ( + ...args: (Error | DOMException | object | string)[] +) => void; +type LogFunctionName = "log" | "info" | "warn" | "error"; + // A class which monkey-patches the global console and stores log lines. interface LogEntry { @@ -54,42 +60,40 @@ interface LogEntry { index?: number; } -interface Cursor { - id: string; - ts: number; -} - export class ConsoleLogger { - logs = ""; + private logs = ""; + private originalFunctions: { [key in LogFunctionName]?: LogFunction } = {}; - monkeyPatch(consoleObj: Console): void { + public monkeyPatch(consoleObj: Console): void { // Monkey-patch console logging - - const consoleFunctionsToLevels: { [level: string]: string } = { + const consoleFunctionsToLevels = { log: "I", info: "I", warn: "W", error: "E", }; - - Object.keys(consoleFunctionsToLevels).forEach( - (fnName: "log" | "info" | "warn" | "error") => { - const level = consoleFunctionsToLevels[fnName]; - const originalFn = consoleObj[fnName].bind(consoleObj); - consoleObj[fnName] = (...args: unknown[]) => { - this.log(level, ...args); - originalFn(...args); - }; - } - ); + Object.keys(consoleFunctionsToLevels).forEach((fnName) => { + const level = consoleFunctionsToLevels[fnName]; + const originalFn = consoleObj[fnName].bind(consoleObj); + this.originalFunctions[fnName] = originalFn; + consoleObj[fnName] = (...args) => { + this.log(level, ...args); + originalFn(...args); + }; + }); } - // these functions get overwritten by the monkey patch - error(...args: unknown[]): void {} - warn(...args: unknown[]): void {} - info(...args: unknown[]): void {} + public bypassRageshake( + fnName: LogFunctionName, + ...args: (Error | DOMException | object | string)[] + ): void { + this.originalFunctions[fnName](...args); + } - log(level: string, ...args: unknown[]): void { + public log( + level: string, + ...args: (Error | DOMException | object | string)[] + ): void { // We don't know what locale the user may be running so use ISO strings const ts = new Date().toISOString(); @@ -100,21 +104,7 @@ export class ConsoleLogger { } else if (arg instanceof Error) { return arg.message + (arg.stack ? `\n${arg.stack}` : ""); } else if (typeof arg === "object") { - try { - return JSON.stringify(arg); - } catch (e) { - // In development, it can be useful to log complex cyclic - // objects to the console for inspection. This is fine for - // the console, but default `stringify` can't handle that. - // We workaround this by using a special replacer function - // to only log values of the root object and avoid cycles. - return JSON.stringify(arg, (key, value) => { - if (key && typeof value === "object") { - return ""; - } - return value; - }); - } + return JSON.stringify(arg, getCircularReplacer()); } else { return arg; } @@ -135,10 +125,10 @@ export class ConsoleLogger { /** * Retrieve log lines to flush to disk. - * @param {boolean} keepLogs True to not delete logs after flushing. Defaults to false. + * @param {boolean} keepLogs True to not delete logs after flushing. * @return {string} \n delimited log lines to flush. */ - flush(keepLogs = false): string { + public flush(keepLogs?: boolean): string { // The ConsoleLogger doesn't care how these end up on disk, it just // flushes them to the caller. if (keepLogs) { @@ -152,26 +142,22 @@ export class ConsoleLogger { // A class which stores log lines in an IndexedDB instance. export class IndexedDBLogStore { - index = 0; - db: IDBDatabase = null; - flushPromise: Promise = null; - flushAgainPromise: Promise = null; - indexedDB: IDBFactory; - logger: ConsoleLogger; - id: string; + private index = 0; + private db: IDBDatabase = null; + private flushPromise: Promise = null; + private flushAgainPromise: Promise = null; + private id: string; - constructor(indexedDB: IDBFactory, logger: ConsoleLogger) { - this.indexedDB = indexedDB; - this.logger = logger; - this.id = "instance-" + Math.random() + Date.now(); + constructor(private indexedDB: IDBFactory, private logger: ConsoleLogger) { + this.id = "instance-" + randomString(16); } /** * @return {Promise} Resolves when the store is ready. */ - connect(): Promise { + public connect(): Promise { const req = this.indexedDB.open("logs"); - return new Promise((resolve, reject) => { + return new Promise((resolve, reject) => { req.onsuccess = (event: Event) => { // @ts-ignore this.db = event.target.result; @@ -180,16 +166,16 @@ export class IndexedDBLogStore { resolve(); }; - req.onerror = (event: Event) => { + req.onerror = (event) => { const err = // @ts-ignore "Failed to open log database: " + event.target.error.name; - this.logger.error(err); + logger.error(err); reject(new Error(err)); }; // First time: Setup the object store - req.onupgradeneeded = (event: IDBVersionChangeEvent) => { + req.onupgradeneeded = (event) => { // @ts-ignore const db = event.target.result; const logObjStore = db.createObjectStore("logs", { @@ -231,7 +217,7 @@ export class IndexedDBLogStore { * * @return {Promise} Resolved when the logs have been flushed. */ - flush(): Promise { + public flush(): Promise { // check if a flush() operation is ongoing if (this.flushPromise) { if (this.flushAgainPromise) { @@ -267,7 +253,7 @@ export class IndexedDBLogStore { resolve(); }; txn.onerror = (event) => { - this.logger.error("Failed to flush logs : ", event); + logger.error("Failed to flush logs : ", event); // @ts-ignore reject(new Error("Failed to write logs: " + event.target.errorCode)); }; @@ -290,13 +276,12 @@ export class IndexedDBLogStore { * log ID). The objects have said log ID in an "id" field and "lines" which * is a big string with all the new-line delimited logs. */ - - async consume(): Promise { + public async consume(): Promise { const db = this.db; // Returns: a string representing the concatenated logs for this ID. // Stops adding log fragments when the size exceeds maxSize - function fetchLogs(id: string, maxSize: number): Promise { + function fetchLogs(id: string, maxSize: number): Promise { const objectStore = db .transaction("logs", "readonly") .objectStore("logs"); @@ -314,28 +299,29 @@ export class IndexedDBLogStore { // @ts-ignore const cursor = event.target.result; if (!cursor) { - resolve(lines.split("\n")); + resolve(lines); return; // end of results } lines = cursor.value.lines + lines; if (lines.length >= maxSize) { - resolve(lines.split("\n")); + resolve(lines); } else { cursor.continue(); } }; }); } + // Returns: A sorted array of log IDs. (newest first) function fetchLogIds(): Promise { // To gather all the log IDs, query for all records in logslastmod. const o = db .transaction("logslastmod", "readonly") .objectStore("logslastmod"); - return selectQuery(o, undefined, (cursor: Cursor) => { + return selectQuery<{ ts: number; id: string }>(o, undefined, (cursor) => { return { - id: cursor.id, - ts: cursor.ts, + id: cursor.value.id, + ts: cursor.value.ts, }; }).then((res) => { // Sort IDs by timestamp (newest first) @@ -346,8 +332,9 @@ export class IndexedDBLogStore { .map((a) => a.id); }); } - function deleteLogs(id: string): Promise { - return new Promise((resolve, reject) => { + + function deleteLogs(id: number): Promise { + return new Promise((resolve, reject) => { const txn = db.transaction(["logs", "logslastmod"], "readwrite"); const o = txn.objectStore("logs"); // only load the key path, not the data which may be huge @@ -380,14 +367,11 @@ export class IndexedDBLogStore { } const allLogIds = await fetchLogIds(); - let removeLogIds: string[] = []; - const logs = []; + let removeLogIds = []; + const logs: LogEntry[] = []; let size = 0; for (let i = 0; i < allLogIds.length; i++) { - const lines: string[] = await fetchLogs( - allLogIds[i], - MAX_LOG_SIZE - size - ); + const lines = await fetchLogs(allLogIds[i], MAX_LOG_SIZE - size); // always add the log file: fetchLogs will truncate once the maxSize we give it is // exceeded, so we'll go over the max but only by one fragment's worth. @@ -407,22 +391,22 @@ export class IndexedDBLogStore { } } if (removeLogIds.length > 0) { - this.logger.log("Removing logs: ", removeLogIds); + logger.log("Removing logs: ", removeLogIds); // Don't await this because it's non-fatal if we can't clean up // logs. Promise.all(removeLogIds.map((id) => deleteLogs(id))).then( () => { - this.logger.log(`Removed ${removeLogIds.length} old logs.`); + logger.log(`Removed ${removeLogIds.length} old logs.`); }, (err) => { - this.logger.error(err); + logger.error(err); } ); } return logs; } - generateLogEntry(lines: string): LogEntry { + private generateLogEntry(lines: string): LogEntry { return { id: this.id, lines: lines, @@ -430,7 +414,7 @@ export class IndexedDBLogStore { }; } - generateLastModifiedTime(): Cursor { + private generateLastModifiedTime(): { id: string; ts: number } { return { id: this.id, ts: Date.now(), @@ -448,22 +432,22 @@ export class IndexedDBLogStore { * @return {Promise} Resolves to an array of whatever you returned from * resultMapper. */ -function selectQuery( +function selectQuery( store: IDBObjectStore, keyRange: IDBKeyRange, - resultMapper: (arg: Cursor) => Cursor -): Promise { + resultMapper: (cursor: IDBCursorWithValue) => T +): Promise { const query = store.openCursor(keyRange); return new Promise((resolve, reject) => { - const results: Cursor[] = []; - query.onerror = (event: Event) => { + const results = []; + query.onerror = (event) => { // @ts-ignore reject(new Error("Query failed: " + event.target.errorCode)); }; // collect results - query.onsuccess = (event: Event) => { + query.onsuccess = (event) => { // @ts-ignore - const cursor = event.target.result?.value; + const cursor = event.target.result; if (!cursor) { resolve(results); return; // end of results @@ -569,13 +553,30 @@ export async function getLogsForReport(): Promise { if (global.mx_rage_store) { // flush most recent logs await global.mx_rage_store.flush(); - return (await global.mx_rage_store.consume()) as LogEntry[]; + return global.mx_rage_store.consume(); } else { return [ { lines: global.mx_rage_logger.flush(true), id: "-", }, - ] as LogEntry[]; + ]; } } + +type StringifyReplacer = (this: any, key: string, value: any) => any; + +// From https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors/Cyclic_object_value#circular_references +// Injects `<$ cycle-trimmed $>` wherever it cuts a cyclical object relationship +const getCircularReplacer = (): StringifyReplacer => { + const seen = new WeakSet(); + return (key: string, value: any): any => { + if (typeof value === "object" && value !== null) { + if (seen.has(value)) { + return "<$ cycle-trimmed $>"; + } + seen.add(value); + } + return value; + }; +}; From 9b2e99c559c941c681bc12770ab1140e137e272a Mon Sep 17 00:00:00 2001 From: Timo K Date: Sat, 11 Jun 2022 14:28:54 +0200 Subject: [PATCH 06/49] use React.ChangeEvent in SettingsModal --- src/settings/SettingsModal.tsx | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/settings/SettingsModal.tsx b/src/settings/SettingsModal.tsx index 69e54846..1e45d557 100644 --- a/src/settings/SettingsModal.tsx +++ b/src/settings/SettingsModal.tsx @@ -1,4 +1,3 @@ -/* eslint-disable @typescript-eslint/ban-ts-comment */ /* Copyright 2022 Matrix.org Foundation C.I.C. @@ -100,8 +99,9 @@ export const SettingsModal = (props: Props) => { type="checkbox" checked={spatialAudio} description="This will make a speaker's audio seem as if it is coming from where their tile is positioned on screen. (Experimental feature: this may impact the stability of audio.)" - // @ts-ignore - onChange={(event: Event) => setSpatialAudio(event.target.checked)} + onChange={(event: React.ChangeEvent) => + setSpatialAudio(event.target.checked) + } /> @@ -143,8 +143,9 @@ export const SettingsModal = (props: Props) => { label="Show Call Inspector" type="checkbox" checked={showInspector} - // @ts-ignore - onChange={(e: Event) => setShowInspector(e.target.checked)} + onChange={(e: React.ChangeEvent) => + setShowInspector(e.target.checked) + } /> From 885e93394829b53dc6316e6cd459527ebda8ef4c Mon Sep 17 00:00:00 2001 From: Timo K Date: Sat, 11 Jun 2022 14:29:26 +0200 Subject: [PATCH 07/49] fixes in useMediaHandler --- src/settings/useMediaHandler.tsx | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/settings/useMediaHandler.tsx b/src/settings/useMediaHandler.tsx index b8de0f7d..bd2acd38 100644 --- a/src/settings/useMediaHandler.tsx +++ b/src/settings/useMediaHandler.tsx @@ -16,6 +16,7 @@ limitations under the License. */ import { MatrixClient } from "matrix-js-sdk"; +import { MediaHandlerEvent } from "matrix-js-sdk/src/webrtc/mediaHandler"; import React, { useState, useEffect, @@ -95,9 +96,9 @@ export function MediaHandlerProvider({ client, children }: Props): JSX.Element { ); return { - // @ts-ignore + // @ts-ignore, ignore that audioInput is a private members of mediaHandler audioInput: mediaHandler.audioInput, - // @ts-ignore + // @ts-ignore, ignore that videoInput is a private members of mediaHandler videoInput: mediaHandler.videoInput, audioOutput: undefined, audioInputs: [], @@ -179,11 +180,14 @@ export function MediaHandlerProvider({ client, children }: Props): JSX.Element { } updateDevices(); - mediaHandler.on("local_streams_changed", updateDevices); + mediaHandler.on(MediaHandlerEvent.LocalStreamsChanged, updateDevices); navigator.mediaDevices.addEventListener("devicechange", updateDevices); return () => { - mediaHandler.removeListener("local_streams_changed", updateDevices); + mediaHandler.removeListener( + MediaHandlerEvent.LocalStreamsChanged, + updateDevices + ); navigator.mediaDevices.removeEventListener("devicechange", updateDevices); mediaHandler.stopAllStreams(); }; @@ -207,7 +211,7 @@ export function MediaHandlerProvider({ client, children }: Props): JSX.Element { [client] ); - const setAudioOutput: (deviceId: any) => void = useCallback((deviceId) => { + const setAudioOutput: (deviceId: string) => void = useCallback((deviceId) => { updateMediaPreferences({ audioOutput: deviceId }); setState((prevState) => ({ ...prevState, audioOutput: deviceId })); }, []); From 46f1f0f8e94e402d86185d7f38a4a4514ea85938 Mon Sep 17 00:00:00 2001 From: Timo K Date: Sat, 11 Jun 2022 14:32:25 +0200 Subject: [PATCH 08/49] remove explicit any --- src/settings/rageshake.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/settings/rageshake.ts b/src/settings/rageshake.ts index 885e4918..72b50681 100644 --- a/src/settings/rageshake.ts +++ b/src/settings/rageshake.ts @@ -564,13 +564,17 @@ export async function getLogsForReport(): Promise { } } -type StringifyReplacer = (this: any, key: string, value: any) => any; +type StringifyReplacer = ( + this: unknown, + key: string, + value: unknown +) => unknown; // From https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors/Cyclic_object_value#circular_references // Injects `<$ cycle-trimmed $>` wherever it cuts a cyclical object relationship const getCircularReplacer = (): StringifyReplacer => { const seen = new WeakSet(); - return (key: string, value: any): any => { + return (key: string, value: unknown): unknown => { if (typeof value === "object" && value !== null) { if (seen.has(value)) { return "<$ cycle-trimmed $>"; From 17a31e090417acf84f2dde9078796761523d2d5d Mon Sep 17 00:00:00 2001 From: Timo K Date: Sat, 11 Jun 2022 15:14:00 +0200 Subject: [PATCH 09/49] typing profile folder --- src/profile/ProfileModal.tsx | 8 ++++- src/profile/useProfile.ts | 68 +++++++++++++++++++++++------------- 2 files changed, 50 insertions(+), 26 deletions(-) diff --git a/src/profile/ProfileModal.tsx b/src/profile/ProfileModal.tsx index 5711ef11..26b99e97 100644 --- a/src/profile/ProfileModal.tsx +++ b/src/profile/ProfileModal.tsx @@ -57,10 +57,16 @@ export function ProfileModal({ client, ...rest }: Props) { (e) => { e.preventDefault(); const data = new FormData(e.target); - const displayName = data.get("displayName"); + const displayNameDataEntry = data.get("displayName"); const avatar: File | string = data.get("avatar"); + const avatarSize = typeof avatar == "string" ? avatar.length : avatar.size; + const displayName = + typeof displayNameDataEntry == "string" + ? displayNameDataEntry + : displayNameDataEntry.name; + saveProfile({ displayName, avatar: avatar && avatarSize > 0 ? avatar : undefined, diff --git a/src/profile/useProfile.ts b/src/profile/useProfile.ts index 6ae5024d..4eadcd86 100644 --- a/src/profile/useProfile.ts +++ b/src/profile/useProfile.ts @@ -14,28 +14,36 @@ See the License for the specific language governing permissions and limitations under the License. */ -import { MatrixClient, User } from "matrix-js-sdk"; +import { + FileType, + MatrixClient, + MatrixEvent, + User, + UserEvent, +} from "matrix-js-sdk"; import { useState, useCallback, useEffect } from "react"; -interface ProfileResult { - loading: boolean; - error: Error; +interface ProfileLoadState { + success?: boolean; + loading?: boolean; displayName: string; avatarUrl: string; - saveProfile: ({ - displayName, - avatar, - removeAvatar, - }: { - displayName: string; - avatar: any; - removeAvatar: boolean; - }) => Promise; - success: boolean; + error?: Error; } -export function useProfile(client: MatrixClient): ProfileResult { + +type ProfileSaveCallback = ({ + displayName, + avatar, + removeAvatar, +}: { + displayName: string; + avatar: FileType; + removeAvatar: boolean; +}) => Promise; + +export function useProfile(client: MatrixClient) { const [{ loading, displayName, avatarUrl, error, success }, setState] = - useState(() => { + useState(() => { const user = client?.getUser(client.getUserId()); return { @@ -48,7 +56,10 @@ export function useProfile(client: MatrixClient): ProfileResult { }); useEffect(() => { - const onChangeUser = (_event: any, { displayName, avatarUrl }: any) => { + const onChangeUser = ( + _event: MatrixEvent, + { displayName, avatarUrl }: User + ) => { setState({ success: false, loading: false, @@ -63,19 +74,19 @@ export function useProfile(client: MatrixClient): ProfileResult { if (client) { const userId = client.getUserId(); user = client.getUser(userId); - user.on("User.displayName", onChangeUser); - user.on("User.avatarUrl", onChangeUser); + user.on(UserEvent.DisplayName, onChangeUser); + user.on(UserEvent.AvatarUrl, onChangeUser); } return () => { if (user) { - user.removeListener("User.displayName", onChangeUser); - user.removeListener("User.avatarUrl", onChangeUser); + user.removeListener(UserEvent.DisplayName, onChangeUser); + user.removeListener(UserEvent.AvatarUrl, onChangeUser); } }; }, [client]); - const saveProfile = useCallback( + const saveProfile = useCallback( async ({ displayName, avatar, removeAvatar }) => { if (client) { setState((prev) => ({ @@ -104,11 +115,11 @@ export function useProfile(client: MatrixClient): ProfileResult { loading: false, success: true, })); - } catch (error) { + } catch (error: unknown) { setState((prev) => ({ ...prev, loading: false, - error, + error: error instanceof Error ? error : Error(error as string), success: false, })); } @@ -119,5 +130,12 @@ export function useProfile(client: MatrixClient): ProfileResult { [client] ); - return { loading, error, displayName, avatarUrl, saveProfile, success }; + return { + loading, + error, + displayName, + avatarUrl, + saveProfile, + success, + }; } From dc118146955be6e06055c7d4aef77f8d9e80c54c Mon Sep 17 00:00:00 2001 From: Timo K Date: Sat, 11 Jun 2022 15:23:33 +0200 Subject: [PATCH 10/49] rename files js->ts --- src/button/{Button.jsx => Button.tsx} | 0 src/button/{CopyButton.jsx => CopyButton.tsx} | 0 src/button/{LinkButton.jsx => LinkButton.tsx} | 0 src/button/{index.js => index.ts} | 0 4 files changed, 0 insertions(+), 0 deletions(-) rename src/button/{Button.jsx => Button.tsx} (100%) rename src/button/{CopyButton.jsx => CopyButton.tsx} (100%) rename src/button/{LinkButton.jsx => LinkButton.tsx} (100%) rename src/button/{index.js => index.ts} (100%) diff --git a/src/button/Button.jsx b/src/button/Button.tsx similarity index 100% rename from src/button/Button.jsx rename to src/button/Button.tsx diff --git a/src/button/CopyButton.jsx b/src/button/CopyButton.tsx similarity index 100% rename from src/button/CopyButton.jsx rename to src/button/CopyButton.tsx diff --git a/src/button/LinkButton.jsx b/src/button/LinkButton.tsx similarity index 100% rename from src/button/LinkButton.jsx rename to src/button/LinkButton.tsx diff --git a/src/button/index.js b/src/button/index.ts similarity index 100% rename from src/button/index.js rename to src/button/index.ts From 18ca92cec422ed15530ec84c75d0406ace7b441f Mon Sep 17 00:00:00 2001 From: Timo K Date: Sat, 11 Jun 2022 23:21:20 +0200 Subject: [PATCH 11/49] js->ts --- src/button/Button.tsx | 26 +++++++++++++++++++------- src/button/CopyButton.tsx | 10 +++++++++- src/button/LinkButton.tsx | 18 ++++++++++++++++-- 3 files changed, 44 insertions(+), 10 deletions(-) diff --git a/src/button/Button.tsx b/src/button/Button.tsx index aabc3093..3c2bc82b 100644 --- a/src/button/Button.tsx +++ b/src/button/Button.tsx @@ -13,9 +13,12 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ - import React, { forwardRef } from "react"; +import { PressEvent } from "@react-types/shared"; import classNames from "classnames"; +import { useButton } from "@react-aria/button"; +import { mergeProps, useObjectRef } from "@react-aria/utils"; + import styles from "./Button.module.css"; import { ReactComponent as MicIcon } from "../icons/Mic.svg"; import { ReactComponent as MuteMicIcon } from "../icons/MuteMic.svg"; @@ -26,8 +29,6 @@ import { ReactComponent as ScreenshareIcon } from "../icons/Screenshare.svg"; import { ReactComponent as SettingsIcon } from "../icons/Settings.svg"; import { ReactComponent as AddUserIcon } from "../icons/AddUser.svg"; import { ReactComponent as ArrowDownIcon } from "../icons/ArrowDown.svg"; -import { useButton } from "@react-aria/button"; -import { mergeProps, useObjectRef } from "@react-aria/utils"; import { TooltipTrigger } from "../Tooltip"; export const variantToClassName = { @@ -47,8 +48,19 @@ export const variantToClassName = { export const sizeToClassName = { lg: [styles.lg], }; - -export const Button = forwardRef( +interface Props { + variant: string; + size: number; + on: () => void; + off: () => void; + iconStyle: string; + className: string; + children: Element[]; + onPress: (e: PressEvent) => void; + onPressStart: (e: PressEvent) => void; + [index: string]: unknown; +} +export const Button = forwardRef( ( { variant = "default", @@ -64,7 +76,7 @@ export const Button = forwardRef( }, ref ) => { - const buttonRef = useObjectRef(ref); + const buttonRef = useObjectRef(ref); const { buttonProps } = useButton( { onPress, onPressStart, ...rest }, buttonRef @@ -75,7 +87,7 @@ export const Button = forwardRef( let filteredButtonProps = buttonProps; if (rest.type === "submit" && !rest.onPress) { - const { onKeyDown, onKeyUp, ...filtered } = buttonProps; + const { ...filtered } = buttonProps; filteredButtonProps = filtered; } diff --git a/src/button/CopyButton.tsx b/src/button/CopyButton.tsx index b28d693a..257ac96e 100644 --- a/src/button/CopyButton.tsx +++ b/src/button/CopyButton.tsx @@ -16,10 +16,18 @@ limitations under the License. import React from "react"; import useClipboard from "react-use-clipboard"; + import { ReactComponent as CheckIcon } from "../icons/Check.svg"; import { ReactComponent as CopyIcon } from "../icons/Copy.svg"; import { Button } from "./Button"; +interface Props { + value: string; + children: JSX.Element; + className: string; + variant: string; + copiedMessage: string; +} export function CopyButton({ value, children, @@ -27,7 +35,7 @@ export function CopyButton({ variant, copiedMessage, ...rest -}) { +}: Props) { const [isCopied, setCopied] = useClipboard(value, { successDuration: 3000 }); return ( diff --git a/src/button/LinkButton.tsx b/src/button/LinkButton.tsx index 561bba47..b96f461f 100644 --- a/src/button/LinkButton.tsx +++ b/src/button/LinkButton.tsx @@ -17,9 +17,23 @@ limitations under the License. import React from "react"; import { Link } from "react-router-dom"; import classNames from "classnames"; -import { variantToClassName, sizeToClassName } from "./Button"; -export function LinkButton({ className, variant, size, children, ...rest }) { +import { variantToClassName, sizeToClassName } from "./Button"; +interface Props { + className: string; + variant: string; + size: number; + children: JSX.Element; + [index: string]: unknown; +} + +export function LinkButton({ + className, + variant, + size, + children, + ...rest +}: Props) { return ( Date: Wed, 15 Jun 2022 21:37:42 +0100 Subject: [PATCH 12/49] catch a couple of exceptions --- src/sound/usePttSounds.ts | 8 ++++++-- src/video-grid/useMediaStream.ts | 8 +++++++- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/src/sound/usePttSounds.ts b/src/sound/usePttSounds.ts index 0bcc8a71..9f6e31d9 100644 --- a/src/sound/usePttSounds.ts +++ b/src/sound/usePttSounds.ts @@ -58,8 +58,12 @@ export const usePTTSounds = (): PTTSounds => { break; } if (ref.current) { - ref.current.currentTime = 0; - await ref.current.play(); + try { + ref.current.currentTime = 0; + await ref.current.play(); + } catch (e) { + console.log("Couldn't play sound effect", e); + } } else { console.log("No media element found"); } diff --git a/src/video-grid/useMediaStream.ts b/src/video-grid/useMediaStream.ts index 5565ab21..cce19079 100644 --- a/src/video-grid/useMediaStream.ts +++ b/src/video-grid/useMediaStream.ts @@ -68,7 +68,13 @@ export const useMediaStream = ( audioOutputDevice && mediaRef.current !== undefined ) { - console.log(`useMediaStream setSinkId ${audioOutputDevice}`); + if (mediaRef.current.setSinkId) { + console.log( + `useMediaStream setting output setSinkId ${audioOutputDevice}` + ); + } else { + console.log("Can't set output - no setsinkid"); + } // Chrome for Android doesn't support this mediaRef.current.setSinkId?.(audioOutputDevice); } From 7dc6fb27eac3eb82d20208d29b488a6f7fd5c69f Mon Sep 17 00:00:00 2001 From: David Baker Date: Tue, 28 Jun 2022 15:08:14 +0100 Subject: [PATCH 13/49] Add embed mode https://github.com/vector-im/element-call/commit/2db23e411074a9e2e0b4fc5d480a7f05abb9915c from postmessage_ptt branch done in a slightly nicer way --- src/Header.jsx | 16 +++++++++++++++- src/room/GroupCallView.jsx | 2 ++ src/room/PTTCallView.tsx | 5 ++++- src/room/RoomPage.jsx | 5 +++-- 4 files changed, 24 insertions(+), 4 deletions(-) diff --git a/src/Header.jsx b/src/Header.jsx index d4f83e55..8edb1731 100644 --- a/src/Header.jsx +++ b/src/Header.jsx @@ -77,9 +77,23 @@ export function RoomHeaderInfo({ roomName, avatarUrl }) { ); } -export function RoomSetupHeaderInfo({ roomName, avatarUrl, ...rest }) { +export function RoomSetupHeaderInfo({ + roomName, + avatarUrl, + isEmbedded, + ...rest +}) { const ref = useRef(); const { buttonProps } = useButton(rest, ref); + + if (isEmbedded) { + return ( +
+ +
+ ); + } + return ( ); } From a81c48cc22ca5d88b05b6e3a5bc3e9be2e3e9c50 Mon Sep 17 00:00:00 2001 From: David Baker Date: Fri, 8 Jul 2022 15:52:32 +0100 Subject: [PATCH 45/49] Fix 'waiting for network' after reaching time limit If you spoke for the maximum amount of time and got cut off, the next time you tried to speak you'd just get the 'waiting for network' state. Key repeats would cause more delayed state timeouts to queue up. --- src/room/PTTButton.tsx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/room/PTTButton.tsx b/src/room/PTTButton.tsx index 53f6f7bc..306a3314 100644 --- a/src/room/PTTButton.tsx +++ b/src/room/PTTButton.tsx @@ -57,13 +57,17 @@ export const PTTButton: React.FC = ({ const buttonRef = useRef(); const [activeTouchId, setActiveTouchId] = useState(null); + const [buttonHeld, setButtonHeld] = useState(false); const hold = useCallback(() => { // This update is delayed so the user only sees it if latency is significant + if (buttonHeld) return; + setButtonHeld(true); enqueueNetworkWaiting(true, 100); startTalking(); - }, [enqueueNetworkWaiting, startTalking]); + }, [enqueueNetworkWaiting, startTalking, buttonHeld]); const unhold = useCallback(() => { + setButtonHeld(false); setNetworkWaiting(false); stopTalking(); }, [setNetworkWaiting, stopTalking]); From dbdc0107649684aed702de234a9b1a9dec1c16b6 Mon Sep 17 00:00:00 2001 From: David Baker Date: Fri, 8 Jul 2022 17:19:13 +0100 Subject: [PATCH 46/49] Updgrade postcss-preset-env as it was complaining that it didn't work with our version of postcss --- package.json | 2 +- yarn.lock | 615 +++++++++++++++++++++++++++------------------------ 2 files changed, 323 insertions(+), 294 deletions(-) diff --git a/package.json b/package.json index cf17c3cc..f41a71d8 100644 --- a/package.json +++ b/package.json @@ -42,7 +42,7 @@ "mermaid": "^8.13.8", "normalize.css": "^8.0.1", "pako": "^2.0.4", - "postcss-preset-env": "^6.7.0", + "postcss-preset-env": "^7", "re-resizable": "^6.9.0", "react": "^17.0.0", "react-dom": "^17.0.0", diff --git a/yarn.lock b/yarn.lock index 9dad3bb3..2ae99ab1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1147,10 +1147,97 @@ resolved "https://registry.yarnpkg.com/@colors/colors/-/colors-1.5.0.tgz#bb504579c1cae923e6576a4f5da43d25f97bdbd9" integrity sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ== -"@csstools/convert-colors@^1.4.0": - version "1.4.0" - resolved "https://registry.yarnpkg.com/@csstools/convert-colors/-/convert-colors-1.4.0.tgz#ad495dc41b12e75d588c6db8b9834f08fa131eb7" - integrity sha512-5a6wqoJV/xEdbRNKVo6I4hO3VjyDq//8q2f9I6PBAvMesJHFauXDorcNCsr9RzvsZnaWi5NYCcfyqP1QeFHFbw== +"@csstools/postcss-cascade-layers@^1.0.4": + version "1.0.5" + resolved "https://registry.yarnpkg.com/@csstools/postcss-cascade-layers/-/postcss-cascade-layers-1.0.5.tgz#f16f2c4396ace855541e1aa693f5f27ec972e6ad" + integrity sha512-Id/9wBT7FkgFzdEpiEWrsVd4ltDxN0rI0QS0SChbeQiSuux3z21SJCRLu6h2cvCEUmaRi+VD0mHFj+GJD4GFnw== + dependencies: + "@csstools/selector-specificity" "^2.0.2" + postcss-selector-parser "^6.0.10" + +"@csstools/postcss-color-function@^1.1.0": + version "1.1.1" + resolved "https://registry.yarnpkg.com/@csstools/postcss-color-function/-/postcss-color-function-1.1.1.tgz#2bd36ab34f82d0497cfacdc9b18d34b5e6f64b6b" + integrity sha512-Bc0f62WmHdtRDjf5f3e2STwRAl89N2CLb+9iAwzrv4L2hncrbDwnQD9PCq0gtAt7pOI2leIV08HIBUd4jxD8cw== + dependencies: + "@csstools/postcss-progressive-custom-properties" "^1.1.0" + postcss-value-parser "^4.2.0" + +"@csstools/postcss-font-format-keywords@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@csstools/postcss-font-format-keywords/-/postcss-font-format-keywords-1.0.0.tgz#7e7df948a83a0dfb7eb150a96e2390ac642356a1" + integrity sha512-oO0cZt8do8FdVBX8INftvIA4lUrKUSCcWUf9IwH9IPWOgKT22oAZFXeHLoDK7nhB2SmkNycp5brxfNMRLIhd6Q== + dependencies: + postcss-value-parser "^4.2.0" + +"@csstools/postcss-hwb-function@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@csstools/postcss-hwb-function/-/postcss-hwb-function-1.0.1.tgz#5224db711ed09a965f85c80c18144ac1c2702fce" + integrity sha512-AMZwWyHbbNLBsDADWmoXT9A5yl5dsGEBeJSJRUJt8Y9n8Ziu7Wstt4MC8jtPW7xjcLecyfJwtnUTNSmOzcnWeg== + dependencies: + postcss-value-parser "^4.2.0" + +"@csstools/postcss-ic-unit@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@csstools/postcss-ic-unit/-/postcss-ic-unit-1.0.0.tgz#f484db59fc94f35a21b6d680d23b0ec69b286b7f" + integrity sha512-i4yps1mBp2ijrx7E96RXrQXQQHm6F4ym1TOD0D69/sjDjZvQ22tqiEvaNw7pFZTUO5b9vWRHzbHzP9+UKuw+bA== + dependencies: + "@csstools/postcss-progressive-custom-properties" "^1.1.0" + postcss-value-parser "^4.2.0" + +"@csstools/postcss-is-pseudo-class@^2.0.6": + version "2.0.6" + resolved "https://registry.yarnpkg.com/@csstools/postcss-is-pseudo-class/-/postcss-is-pseudo-class-2.0.6.tgz#1d82d798a2ce0b5f793d34710976f184c4f6560c" + integrity sha512-Oqs396oenuyyMdRXOstxXbxei8fYEgToYjmlYHEi5gk0QLk7xQ72LY7NDr7waWAAmdVzRqPpbE26Q7/cUrGu4Q== + dependencies: + "@csstools/selector-specificity" "^2.0.0" + postcss-selector-parser "^6.0.10" + +"@csstools/postcss-normalize-display-values@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@csstools/postcss-normalize-display-values/-/postcss-normalize-display-values-1.0.0.tgz#ce698f688c28517447aedf15a9037987e3d2dc97" + integrity sha512-bX+nx5V8XTJEmGtpWTO6kywdS725t71YSLlxWt78XoHUbELWgoCXeOFymRJmL3SU1TLlKSIi7v52EWqe60vJTQ== + dependencies: + postcss-value-parser "^4.2.0" + +"@csstools/postcss-oklab-function@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@csstools/postcss-oklab-function/-/postcss-oklab-function-1.1.0.tgz#e9a269487a292e0930760948e923e1d46b638ee6" + integrity sha512-e/Q5HopQzmnQgqimG9v3w2IG4VRABsBq3itOcn4bnm+j4enTgQZ0nWsaH/m9GV2otWGQ0nwccYL5vmLKyvP1ww== + dependencies: + "@csstools/postcss-progressive-custom-properties" "^1.1.0" + postcss-value-parser "^4.2.0" + +"@csstools/postcss-progressive-custom-properties@^1.1.0", "@csstools/postcss-progressive-custom-properties@^1.3.0": + version "1.3.0" + resolved "https://registry.yarnpkg.com/@csstools/postcss-progressive-custom-properties/-/postcss-progressive-custom-properties-1.3.0.tgz#542292558384361776b45c85226b9a3a34f276fa" + integrity sha512-ASA9W1aIy5ygskZYuWams4BzafD12ULvSypmaLJT2jvQ8G0M3I8PRQhC0h7mG0Z3LI05+agZjqSR9+K9yaQQjA== + dependencies: + postcss-value-parser "^4.2.0" + +"@csstools/postcss-stepped-value-functions@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@csstools/postcss-stepped-value-functions/-/postcss-stepped-value-functions-1.0.0.tgz#f8ffc05e163ba7bcbefc5fdcaf264ce9fd408c16" + integrity sha512-q8c4bs1GumAiRenmFjASBcWSLKrbzHzWl6C2HcaAxAXIiL2rUlUWbqQZUjwVG5tied0rld19j/Mm90K3qI26vw== + dependencies: + postcss-value-parser "^4.2.0" + +"@csstools/postcss-trigonometric-functions@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@csstools/postcss-trigonometric-functions/-/postcss-trigonometric-functions-1.0.1.tgz#e36e61f445614193dbf6d3a8408709b0cf184a6f" + integrity sha512-G78CY/+GePc6dDCTUbwI6TTFQ5fs3N9POHhI6v0QzteGpf6ylARiJUNz9HrRKi4eVYBNXjae1W2766iUEFxHlw== + dependencies: + postcss-value-parser "^4.2.0" + +"@csstools/postcss-unset-value@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@csstools/postcss-unset-value/-/postcss-unset-value-1.0.1.tgz#2cc020785db5ec82cc9444afe4cdae2a65445f89" + integrity sha512-f1G1WGDXEU/RN1TWAxBPQgQudtLnLQPyiWdtypkPC+mVYNKFKH/HYXSxH4MVNqwF8M0eDsoiU7HumJHCg/L/jg== + +"@csstools/selector-specificity@^2.0.0", "@csstools/selector-specificity@^2.0.2": + version "2.0.2" + resolved "https://registry.yarnpkg.com/@csstools/selector-specificity/-/selector-specificity-2.0.2.tgz#1bfafe4b7ed0f3e4105837e056e0a89b108ebe36" + integrity sha512-IkpVW/ehM1hWKln4fCA3NzJU8KwD+kIOvPZA4cqxoJHtE21CCzjyp+Kxbu0i5I4tBNOlXPL9mjwnWlL0VEG4Fg== "@discoveryjs/json-ext@^0.5.3": version "0.5.7" @@ -3634,7 +3721,19 @@ atob@^2.1.2: resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9" integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg== -autoprefixer@^9.6.1, autoprefixer@^9.8.6: +autoprefixer@^10.4.7: + version "10.4.7" + resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-10.4.7.tgz#1db8d195f41a52ca5069b7593be167618edbbedf" + integrity sha512-ypHju4Y2Oav95SipEcCcI5J7CGPuvz8oat7sUtYj3ClK44bldfvtvcxK6IEK++7rqB7YchDGzweZIBG+SD0ZAA== + dependencies: + browserslist "^4.20.3" + caniuse-lite "^1.0.30001335" + fraction.js "^4.2.0" + normalize-range "^0.1.2" + picocolors "^1.0.0" + postcss-value-parser "^4.2.0" + +autoprefixer@^9.8.6: version "9.8.8" resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-9.8.8.tgz#fd4bd4595385fa6f06599de749a4d5f7a474957a" integrity sha512-eM9d/swFopRt5gdJ7jrpCwgvEMIayITpojhkkSMRsFHYuH5bkSQ4p/9qTEHtmNudUZh22Tehu7I6CxAW0IXTKA== @@ -3996,7 +4095,7 @@ browserify-zlib@^0.2.0: dependencies: pako "~1.0.5" -browserslist@^4.12.0, browserslist@^4.14.5, browserslist@^4.20.2, browserslist@^4.21.0, browserslist@^4.6.4: +browserslist@^4.12.0, browserslist@^4.14.5, browserslist@^4.20.2, browserslist@^4.20.3, browserslist@^4.21.0: version "4.21.1" resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.21.1.tgz#c9b9b0a54c7607e8dc3e01a0d311727188011a00" integrity sha512-Nq8MFCSrnJXSc88yliwlzQe3qNe3VntIjhsArW9IJOEPSHNx23FalwApUVbzAWABLhYJJ7y8AynWI/XM8OdfjQ== @@ -4179,7 +4278,7 @@ camelcase@^6.2.0: resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== -caniuse-lite@^1.0.30000981, caniuse-lite@^1.0.30001109, caniuse-lite@^1.0.30001359: +caniuse-lite@^1.0.30001109, caniuse-lite@^1.0.30001335, caniuse-lite@^1.0.30001359: version "1.0.30001363" resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001363.tgz#26bec2d606924ba318235944e1193304ea7c4f15" integrity sha512-HpQhpzTGGPVMnCjIomjt+jvyUu8vNFo3TaDiZ/RcoTrlOq/5+tC8zHdsbgFB6MxmaY+jCpsH09aD80Bb4Ow3Sg== @@ -4688,20 +4787,19 @@ crypto-browserify@^3.11.0: randombytes "^2.0.0" randomfill "^1.0.3" -css-blank-pseudo@^0.1.4: - version "0.1.4" - resolved "https://registry.yarnpkg.com/css-blank-pseudo/-/css-blank-pseudo-0.1.4.tgz#dfdefd3254bf8a82027993674ccf35483bfcb3c5" - integrity sha512-LHz35Hr83dnFeipc7oqFDmsjHdljj3TQtxGGiNWSOsTLIAubSm4TEz8qCaKFpk7idaQ1GfWscF4E6mgpBysA1w== +css-blank-pseudo@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/css-blank-pseudo/-/css-blank-pseudo-3.0.3.tgz#36523b01c12a25d812df343a32c322d2a2324561" + integrity sha512-VS90XWtsHGqoM0t4KpH053c4ehxZ2E6HtGI7x68YFV0pTo/QmkV/YFA+NnlvK8guxZVNWGQhVNJGC39Q8XF4OQ== dependencies: - postcss "^7.0.5" + postcss-selector-parser "^6.0.9" -css-has-pseudo@^0.10.0: - version "0.10.0" - resolved "https://registry.yarnpkg.com/css-has-pseudo/-/css-has-pseudo-0.10.0.tgz#3c642ab34ca242c59c41a125df9105841f6966ee" - integrity sha512-Z8hnfsZu4o/kt+AuFzeGpLVhFOGO9mluyHBaA2bA8aCGTwah5sT3WV/fTHH8UNZUytOIImuGPrl/prlb4oX4qQ== +css-has-pseudo@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/css-has-pseudo/-/css-has-pseudo-3.0.4.tgz#57f6be91ca242d5c9020ee3e51bbb5b89fc7af73" + integrity sha512-Vse0xpR1K9MNlp2j5w1pgWIJtm1a8qS0JwS9goFYcImjlHEmywP9VUF05aGBXzGpDJF86QXk4L0ypBmwPhGArw== dependencies: - postcss "^7.0.6" - postcss-selector-parser "^5.0.0-rc.4" + postcss-selector-parser "^6.0.9" css-loader@^3.6.0: version "3.6.0" @@ -4722,12 +4820,10 @@ css-loader@^3.6.0: schema-utils "^2.7.0" semver "^6.3.0" -css-prefers-color-scheme@^3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/css-prefers-color-scheme/-/css-prefers-color-scheme-3.1.1.tgz#6f830a2714199d4f0d0d0bb8a27916ed65cff1f4" - integrity sha512-MTu6+tMs9S3EUqzmqLXEcgNRbNkkD/TGFvowpeoWJn5Vfq7FMgsmRQs9X5NXAURiOBmOxm/lLjsDNXDE6k9bhg== - dependencies: - postcss "^7.0.5" +css-prefers-color-scheme@^6.0.3: + version "6.0.3" + resolved "https://registry.yarnpkg.com/css-prefers-color-scheme/-/css-prefers-color-scheme-6.0.3.tgz#ca8a22e5992c10a5b9d315155e7caee625903349" + integrity sha512-4BqMbZksRkJQx2zAjrokiGMd07RqOa2IxIrrN10lyBe9xhn9DEvjUK79J6jkeiv9D9hQFXKb6g1jwU62jziJZA== css-select@^4.1.3: version "4.3.0" @@ -4745,15 +4841,10 @@ css-what@^6.0.1: resolved "https://registry.yarnpkg.com/css-what/-/css-what-6.1.0.tgz#fb5effcf76f1ddea2c81bdfaa4de44e79bac70f4" integrity sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw== -cssdb@^4.4.0: - version "4.4.0" - resolved "https://registry.yarnpkg.com/cssdb/-/cssdb-4.4.0.tgz#3bf2f2a68c10f5c6a08abd92378331ee803cddb0" - integrity sha512-LsTAR1JPEM9TpGhl/0p3nQecC2LJ0kD8X5YARu1hk/9I1gril5vDtMZyNxcEpxxDj34YNck/ucjuoUd66K03oQ== - -cssesc@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-2.0.0.tgz#3b13bd1bb1cb36e1bcb5a4dcd27f54c5dcb35703" - integrity sha512-MsCAG1z9lPdoO/IUMLSBWBSVxVtJ1395VGIQ+Fc2gNdkQ1hNDnQdw3YhA71WJCBW1vdwA0cAnk/DnW6bqoEUYg== +cssdb@^6.6.3: + version "6.6.3" + resolved "https://registry.yarnpkg.com/cssdb/-/cssdb-6.6.3.tgz#1f331a2fab30c18d9f087301e6122a878bb1e505" + integrity sha512-7GDvDSmE+20+WcSMhP17Q1EVWUrLlbxxpMDqG731n8P99JhnQZHR9YvtjPvEHfjFUjvQJvdpKCjlKOX+xe4UVA== cssesc@^3.0.0: version "3.0.0" @@ -6610,11 +6701,6 @@ flatted@^3.1.0: resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.6.tgz#022e9218c637f9f3fc9c35ab9c9193f05add60b2" integrity sha512-0sQoMh9s0BYsm+12Huy/rkKxVu4R1+r96YX5cG44rHV0pQ6iC3Q+mkoMFaGWObMFYQxCVT+ssG1ksneA2MI9KQ== -flatten@^1.0.2: - version "1.0.3" - resolved "https://registry.yarnpkg.com/flatten/-/flatten-1.0.3.tgz#c1283ac9f27b368abc1e36d1ff7b04501a30356b" - integrity sha512-dVsPA/UwQ8+2uoFe5GHtiBMu48dWLTdsuEd7CKGlZlD78r1TTWBvDuFaFGKCo/ZfEr95Uk56vZoX86OsHkUeIg== - flush-write-stream@^1.0.0: version "1.1.1" resolved "https://registry.yarnpkg.com/flush-write-stream/-/flush-write-stream-1.1.1.tgz#8dd7d873a1babc207d94ead0c2e0e44276ebf2e8" @@ -6718,6 +6804,11 @@ forwarded@0.2.0: resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811" integrity sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow== +fraction.js@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/fraction.js/-/fraction.js-4.2.0.tgz#448e5109a313a3527f5a3ab2119ec4cf0e0e2950" + integrity sha512-MhLuK+2gUcnZe8ZHlaaINnQLl0xRIGRfcGk2yl8xoQAfHrSsL3rYu6FCmBdkdbhc9EPlwyGHewaRsvwRMJtAlA== + fragment-cache@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/fragment-cache/-/fragment-cache-0.2.1.tgz#4290fad27f13e89be7f33799c6bc5a0abfff0d19" @@ -7412,11 +7503,6 @@ indent-string@^4.0.0: resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-4.0.0.tgz#624f8f4497d619b2d9768531d58f4122854d7251" integrity sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg== -indexes-of@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/indexes-of/-/indexes-of-1.0.1.tgz#f30f716c8e2bd346c7b67d3df3915566a7c05607" - integrity sha512-bup+4tap3Hympa+JBJUG7XuOsdNQ6fxt0MHyXMKuLBKn0OqsTfvUxkUrroEX1+B2VsSHvCjiIcZVxRtYa4nllA== - infer-owner@^1.0.3, infer-owner@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/infer-owner/-/infer-owner-1.0.4.tgz#c4cefcaa8e51051c2a40ba2ce8a3d27295af9467" @@ -8306,7 +8392,6 @@ matrix-events-sdk@^0.0.1-beta.7: resolved "https://codeload.github.com/matrix-org/matrix-js-sdk/tar.gz/984dd26a138411ef73903ff4e635f2752e0829f2" dependencies: "@babel/runtime" "^7.12.5" - "@types/sdp-transform" "^2.4.5" another-json "^0.2.0" browser-request "^0.3.3" bs58 "^4.0.1" @@ -8316,7 +8401,6 @@ matrix-events-sdk@^0.0.1-beta.7: p-retry "^4.5.0" qs "^6.9.6" request "^2.88.2" - sdp-transform "^2.14.1" unhomoglyph "^1.0.6" md5.js@^1.3.4: @@ -9396,102 +9480,83 @@ posix-character-classes@^0.1.0: resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab" integrity sha512-xTgYBc3fuo7Yt7JbiuFxSYGToMoz8fLoE6TC9Wx1P/u+LfeThMOAqmuyECnlBaaJb+u1m9hHiXUEtwW4OzfUJg== -postcss-attribute-case-insensitive@^4.0.1: - version "4.0.2" - resolved "https://registry.yarnpkg.com/postcss-attribute-case-insensitive/-/postcss-attribute-case-insensitive-4.0.2.tgz#d93e46b504589e94ac7277b0463226c68041a880" - integrity sha512-clkFxk/9pcdb4Vkn0hAHq3YnxBQ2p0CGD1dy24jN+reBck+EWxMbxSUqN4Yj7t0w8csl87K6p0gxBe1utkJsYA== +postcss-attribute-case-insensitive@^5.0.1: + version "5.0.2" + resolved "https://registry.yarnpkg.com/postcss-attribute-case-insensitive/-/postcss-attribute-case-insensitive-5.0.2.tgz#03d761b24afc04c09e757e92ff53716ae8ea2741" + integrity sha512-XIidXV8fDr0kKt28vqki84fRK8VW8eTuIa4PChv2MqKuT6C9UjmSKzen6KaWhWEoYvwxFCa7n/tC1SZ3tyq4SQ== dependencies: - postcss "^7.0.2" - postcss-selector-parser "^6.0.2" + postcss-selector-parser "^6.0.10" -postcss-color-functional-notation@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/postcss-color-functional-notation/-/postcss-color-functional-notation-2.0.1.tgz#5efd37a88fbabeb00a2966d1e53d98ced93f74e0" - integrity sha512-ZBARCypjEDofW4P6IdPVTLhDNXPRn8T2s1zHbZidW6rPaaZvcnCS2soYFIQJrMZSxiePJ2XIYTlcb2ztr/eT2g== +postcss-clamp@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/postcss-clamp/-/postcss-clamp-4.1.0.tgz#7263e95abadd8c2ba1bd911b0b5a5c9c93e02363" + integrity sha512-ry4b1Llo/9zz+PKC+030KUnPITTJAHeOwjfAyyB60eT0AorGLdzp52s31OsPRHRf8NchkgFoG2y6fCfn1IV1Ow== dependencies: - postcss "^7.0.2" - postcss-values-parser "^2.0.0" + postcss-value-parser "^4.2.0" -postcss-color-gray@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/postcss-color-gray/-/postcss-color-gray-5.0.0.tgz#532a31eb909f8da898ceffe296fdc1f864be8547" - integrity sha512-q6BuRnAGKM/ZRpfDascZlIZPjvwsRye7UDNalqVz3s7GDxMtqPY6+Q871liNxsonUw8oC61OG+PSaysYpl1bnw== +postcss-color-functional-notation@^4.2.3: + version "4.2.4" + resolved "https://registry.yarnpkg.com/postcss-color-functional-notation/-/postcss-color-functional-notation-4.2.4.tgz#21a909e8d7454d3612d1659e471ce4696f28caec" + integrity sha512-2yrTAUZUab9s6CpxkxC4rVgFEVaR6/2Pipvi6qcgvnYiVqZcbDHEoBDhrXzyb7Efh2CCfHQNtcqWcIruDTIUeg== dependencies: - "@csstools/convert-colors" "^1.4.0" - postcss "^7.0.5" - postcss-values-parser "^2.0.0" + postcss-value-parser "^4.2.0" -postcss-color-hex-alpha@^5.0.3: - version "5.0.3" - resolved "https://registry.yarnpkg.com/postcss-color-hex-alpha/-/postcss-color-hex-alpha-5.0.3.tgz#a8d9ca4c39d497c9661e374b9c51899ef0f87388" - integrity sha512-PF4GDel8q3kkreVXKLAGNpHKilXsZ6xuu+mOQMHWHLPNyjiUBOr75sp5ZKJfmv1MCus5/DWUGcK9hm6qHEnXYw== +postcss-color-hex-alpha@^8.0.4: + version "8.0.4" + resolved "https://registry.yarnpkg.com/postcss-color-hex-alpha/-/postcss-color-hex-alpha-8.0.4.tgz#c66e2980f2fbc1a63f5b079663340ce8b55f25a5" + integrity sha512-nLo2DCRC9eE4w2JmuKgVA3fGL3d01kGq752pVALF68qpGLmx2Qrk91QTKkdUqqp45T1K1XV8IhQpcu1hoAQflQ== dependencies: - postcss "^7.0.14" - postcss-values-parser "^2.0.1" + postcss-value-parser "^4.2.0" -postcss-color-mod-function@^3.0.3: - version "3.0.3" - resolved "https://registry.yarnpkg.com/postcss-color-mod-function/-/postcss-color-mod-function-3.0.3.tgz#816ba145ac11cc3cb6baa905a75a49f903e4d31d" - integrity sha512-YP4VG+xufxaVtzV6ZmhEtc+/aTXH3d0JLpnYfxqTvwZPbJhWqp8bSY3nfNzNRFLgB4XSaBA82OE4VjOOKpCdVQ== +postcss-color-rebeccapurple@^7.1.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/postcss-color-rebeccapurple/-/postcss-color-rebeccapurple-7.1.0.tgz#a2fe1d7be13d21ea01dc7c2363b637cc83a9eb6e" + integrity sha512-1jtE5AKnZcKq4pjOrltFHcbEM2/IvtbD1OdhZ/wqds18//bh0UmQkffcCkzDJU+/vGodfIsVQeKn+45CJvX9Bw== dependencies: - "@csstools/convert-colors" "^1.4.0" - postcss "^7.0.2" - postcss-values-parser "^2.0.0" + postcss-value-parser "^4.2.0" -postcss-color-rebeccapurple@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/postcss-color-rebeccapurple/-/postcss-color-rebeccapurple-4.0.1.tgz#c7a89be872bb74e45b1e3022bfe5748823e6de77" - integrity sha512-aAe3OhkS6qJXBbqzvZth2Au4V3KieR5sRQ4ptb2b2O8wgvB3SJBsdG+jsn2BZbbwekDG8nTfcCNKcSfe/lEy8g== +postcss-custom-media@^8.0.2: + version "8.0.2" + resolved "https://registry.yarnpkg.com/postcss-custom-media/-/postcss-custom-media-8.0.2.tgz#c8f9637edf45fef761b014c024cee013f80529ea" + integrity sha512-7yi25vDAoHAkbhAzX9dHx2yc6ntS4jQvejrNcC+csQJAXjj15e7VcWfMgLqBNAbOvqi5uIa9huOVwdHbf+sKqg== dependencies: - postcss "^7.0.2" - postcss-values-parser "^2.0.0" + postcss-value-parser "^4.2.0" -postcss-custom-media@^7.0.8: - version "7.0.8" - resolved "https://registry.yarnpkg.com/postcss-custom-media/-/postcss-custom-media-7.0.8.tgz#fffd13ffeffad73621be5f387076a28b00294e0c" - integrity sha512-c9s5iX0Ge15o00HKbuRuTqNndsJUbaXdiNsksnVH8H4gdc+zbLzr/UasOwNG6CTDpLFekVY4672eWdiiWu2GUg== +postcss-custom-properties@^12.1.8: + version "12.1.8" + resolved "https://registry.yarnpkg.com/postcss-custom-properties/-/postcss-custom-properties-12.1.8.tgz#aa003e1885c5bd28e2e32496cd597e389ca889e4" + integrity sha512-8rbj8kVu00RQh2fQF81oBqtduiANu4MIxhyf0HbbStgPtnFlWn0yiaYTpLHrPnJbffVY1s9apWsIoVZcc68FxA== dependencies: - postcss "^7.0.14" + postcss-value-parser "^4.2.0" -postcss-custom-properties@^8.0.11: - version "8.0.11" - resolved "https://registry.yarnpkg.com/postcss-custom-properties/-/postcss-custom-properties-8.0.11.tgz#2d61772d6e92f22f5e0d52602df8fae46fa30d97" - integrity sha512-nm+o0eLdYqdnJ5abAJeXp4CEU1c1k+eB2yMCvhgzsds/e0umabFrN6HoTy/8Q4K5ilxERdl/JD1LO5ANoYBeMA== +postcss-custom-selectors@^6.0.3: + version "6.0.3" + resolved "https://registry.yarnpkg.com/postcss-custom-selectors/-/postcss-custom-selectors-6.0.3.tgz#1ab4684d65f30fed175520f82d223db0337239d9" + integrity sha512-fgVkmyiWDwmD3JbpCmB45SvvlCD6z9CG6Ie6Iere22W5aHea6oWa7EM2bpnv2Fj3I94L3VbtvX9KqwSi5aFzSg== dependencies: - postcss "^7.0.17" - postcss-values-parser "^2.0.1" + postcss-selector-parser "^6.0.4" -postcss-custom-selectors@^5.1.2: - version "5.1.2" - resolved "https://registry.yarnpkg.com/postcss-custom-selectors/-/postcss-custom-selectors-5.1.2.tgz#64858c6eb2ecff2fb41d0b28c9dd7b3db4de7fba" - integrity sha512-DSGDhqinCqXqlS4R7KGxL1OSycd1lydugJ1ky4iRXPHdBRiozyMHrdu0H3o7qNOCiZwySZTUI5MV0T8QhCLu+w== +postcss-dir-pseudo-class@^6.0.4: + version "6.0.4" + resolved "https://registry.yarnpkg.com/postcss-dir-pseudo-class/-/postcss-dir-pseudo-class-6.0.4.tgz#9afe49ea631f0cb36fa0076e7c2feb4e7e3f049c" + integrity sha512-I8epwGy5ftdzNWEYok9VjW9whC4xnelAtbajGv4adql4FIF09rnrxnA9Y8xSHN47y7gqFIv10C5+ImsLeJpKBw== dependencies: - postcss "^7.0.2" - postcss-selector-parser "^5.0.0-rc.3" + postcss-selector-parser "^6.0.9" -postcss-dir-pseudo-class@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/postcss-dir-pseudo-class/-/postcss-dir-pseudo-class-5.0.0.tgz#6e3a4177d0edb3abcc85fdb6fbb1c26dabaeaba2" - integrity sha512-3pm4oq8HYWMZePJY+5ANriPs3P07q+LW6FAdTlkFH2XqDdP4HeeJYMOzn0HYLhRSjBO3fhiqSwwU9xEULSrPgw== +postcss-double-position-gradients@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/postcss-double-position-gradients/-/postcss-double-position-gradients-3.1.1.tgz#a12cfdb7d11fa1a99ccecc747f0c19718fb37152" + integrity sha512-jM+CGkTs4FcG53sMPjrrGE0rIvLDdCrqMzgDC5fLI7JHDO7o6QG8C5TQBtExb13hdBdoH9C2QVbG4jo2y9lErQ== dependencies: - postcss "^7.0.2" - postcss-selector-parser "^5.0.0-rc.3" + "@csstools/postcss-progressive-custom-properties" "^1.1.0" + postcss-value-parser "^4.2.0" -postcss-double-position-gradients@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/postcss-double-position-gradients/-/postcss-double-position-gradients-1.0.0.tgz#fc927d52fddc896cb3a2812ebc5df147e110522e" - integrity sha512-G+nV8EnQq25fOI8CH/B6krEohGWnF5+3A6H/+JEpOncu5dCnkS1QQ6+ct3Jkaepw1NGVqqOZH6lqrm244mCftA== +postcss-env-function@^4.0.6: + version "4.0.6" + resolved "https://registry.yarnpkg.com/postcss-env-function/-/postcss-env-function-4.0.6.tgz#7b2d24c812f540ed6eda4c81f6090416722a8e7a" + integrity sha512-kpA6FsLra+NqcFnL81TnsU+Z7orGtDTxcOhl6pwXeEq1yFPpRMkCDpHhrz8CFQDr/Wfm0jLiNQ1OsGGPjlqPwA== dependencies: - postcss "^7.0.5" - postcss-values-parser "^2.0.0" - -postcss-env-function@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/postcss-env-function/-/postcss-env-function-2.0.2.tgz#0f3e3d3c57f094a92c2baf4b6241f0b0da5365d7" - integrity sha512-rwac4BuZlITeUbiBq60h/xbLzXY43qOsIErngWa4l7Mt+RaSkT7QBjXVGTcBHupykkblHMDrBFh30zchYPaOUw== - dependencies: - postcss "^7.0.2" - postcss-values-parser "^2.0.0" + postcss-value-parser "^4.2.0" postcss-flexbugs-fixes@^4.2.1: version "4.2.1" @@ -9500,57 +9565,49 @@ postcss-flexbugs-fixes@^4.2.1: dependencies: postcss "^7.0.26" -postcss-focus-visible@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/postcss-focus-visible/-/postcss-focus-visible-4.0.0.tgz#477d107113ade6024b14128317ade2bd1e17046e" - integrity sha512-Z5CkWBw0+idJHSV6+Bgf2peDOFf/x4o+vX/pwcNYrWpXFrSfTkQ3JQ1ojrq9yS+upnAlNRHeg8uEwFTgorjI8g== +postcss-focus-visible@^6.0.4: + version "6.0.4" + resolved "https://registry.yarnpkg.com/postcss-focus-visible/-/postcss-focus-visible-6.0.4.tgz#50c9ea9afa0ee657fb75635fabad25e18d76bf9e" + integrity sha512-QcKuUU/dgNsstIK6HELFRT5Y3lbrMLEOwG+A4s5cA+fx3A3y/JTq3X9LaOj3OC3ALH0XqyrgQIgey/MIZ8Wczw== dependencies: - postcss "^7.0.2" + postcss-selector-parser "^6.0.9" -postcss-focus-within@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/postcss-focus-within/-/postcss-focus-within-3.0.0.tgz#763b8788596cee9b874c999201cdde80659ef680" - integrity sha512-W0APui8jQeBKbCGZudW37EeMCjDeVxKgiYfIIEo8Bdh5SpB9sxds/Iq8SEuzS0Q4YFOlG7EPFulbbxujpkrV2w== +postcss-focus-within@^5.0.4: + version "5.0.4" + resolved "https://registry.yarnpkg.com/postcss-focus-within/-/postcss-focus-within-5.0.4.tgz#5b1d2ec603195f3344b716c0b75f61e44e8d2e20" + integrity sha512-vvjDN++C0mu8jz4af5d52CB184ogg/sSxAFS+oUJQq2SuCe7T5U2iIsVJtsCp2d6R4j0jr5+q3rPkBVZkXD9fQ== dependencies: - postcss "^7.0.2" + postcss-selector-parser "^6.0.9" -postcss-font-variant@^4.0.0: +postcss-font-variant@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/postcss-font-variant/-/postcss-font-variant-5.0.0.tgz#efd59b4b7ea8bb06127f2d031bfbb7f24d32fa66" + integrity sha512-1fmkBaCALD72CK2a9i468mA/+tr9/1cBxRRMXOUaZqO43oWPR5imcyPjXwuv7PXbCid4ndlP5zWhidQVVa3hmA== + +postcss-gap-properties@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/postcss-gap-properties/-/postcss-gap-properties-3.0.3.tgz#6401bb2f67d9cf255d677042928a70a915e6ba60" + integrity sha512-rPPZRLPmEKgLk/KlXMqRaNkYTUpE7YC+bOIQFN5xcu1Vp11Y4faIXv6/Jpft6FMnl6YRxZqDZG0qQOW80stzxQ== + +postcss-image-set-function@^4.0.6: + version "4.0.6" + resolved "https://registry.yarnpkg.com/postcss-image-set-function/-/postcss-image-set-function-4.0.6.tgz#bcff2794efae778c09441498f40e0c77374870a9" + integrity sha512-KfdC6vg53GC+vPd2+HYzsZ6obmPqOk6HY09kttU19+Gj1nC3S3XBVEXDHxkhxTohgZqzbUb94bKXvKDnYWBm/A== + dependencies: + postcss-value-parser "^4.2.0" + +postcss-initial@^4.0.1: version "4.0.1" - resolved "https://registry.yarnpkg.com/postcss-font-variant/-/postcss-font-variant-4.0.1.tgz#42d4c0ab30894f60f98b17561eb5c0321f502641" - integrity sha512-I3ADQSTNtLTTd8uxZhtSOrTCQ9G4qUVKPjHiDk0bV75QSxXjVWiJVJ2VLdspGUi9fbW9BcjKJoRvxAH1pckqmA== - dependencies: - postcss "^7.0.2" + resolved "https://registry.yarnpkg.com/postcss-initial/-/postcss-initial-4.0.1.tgz#529f735f72c5724a0fb30527df6fb7ac54d7de42" + integrity sha512-0ueD7rPqX8Pn1xJIjay0AZeIuDoF+V+VvMt/uOnn+4ezUKhZM/NokDeP6DwMNyIoYByuN/94IQnt5FEkaN59xQ== -postcss-gap-properties@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/postcss-gap-properties/-/postcss-gap-properties-2.0.0.tgz#431c192ab3ed96a3c3d09f2ff615960f902c1715" - integrity sha512-QZSqDaMgXCHuHTEzMsS2KfVDOq7ZFiknSpkrPJY6jmxbugUPTuSzs/vuE5I3zv0WAS+3vhrlqhijiprnuQfzmg== +postcss-lab-function@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/postcss-lab-function/-/postcss-lab-function-4.2.0.tgz#e054e662c6480202f5760887ec1ae0d153357123" + integrity sha512-Zb1EO9DGYfa3CP8LhINHCcTTCTLI+R3t7AX2mKsDzdgVQ/GkCpHOTgOr6HBHslP7XDdVbqgHW5vvRPMdVANQ8w== dependencies: - postcss "^7.0.2" - -postcss-image-set-function@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/postcss-image-set-function/-/postcss-image-set-function-3.0.1.tgz#28920a2f29945bed4c3198d7df6496d410d3f288" - integrity sha512-oPTcFFip5LZy8Y/whto91L9xdRHCWEMs3e1MdJxhgt4jy2WYXfhkng59fH5qLXSCPN8k4n94p1Czrfe5IOkKUw== - dependencies: - postcss "^7.0.2" - postcss-values-parser "^2.0.0" - -postcss-initial@^3.0.0: - version "3.0.4" - resolved "https://registry.yarnpkg.com/postcss-initial/-/postcss-initial-3.0.4.tgz#9d32069a10531fe2ecafa0b6ac750ee0bc7efc53" - integrity sha512-3RLn6DIpMsK1l5UUy9jxQvoDeUN4gP939tDcKUHD/kM8SGSKbFAnvkpFpj3Bhtz3HGk1jWY5ZNWX6mPta5M9fg== - dependencies: - postcss "^7.0.2" - -postcss-lab-function@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/postcss-lab-function/-/postcss-lab-function-2.0.1.tgz#bb51a6856cd12289ab4ae20db1e3821ef13d7d2e" - integrity sha512-whLy1IeZKY+3fYdqQFuDBf8Auw+qFuVnChWjmxm/UhHWqNHZx+B99EwxTvGYmUBqe3Fjxs4L1BoZTJmPu6usVg== - dependencies: - "@csstools/convert-colors" "^1.4.0" - postcss "^7.0.2" - postcss-values-parser "^2.0.0" + "@csstools/postcss-progressive-custom-properties" "^1.1.0" + postcss-value-parser "^4.2.0" postcss-loader@^4.2.0: version "4.3.0" @@ -9563,19 +9620,15 @@ postcss-loader@^4.2.0: schema-utils "^3.0.0" semver "^7.3.4" -postcss-logical@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/postcss-logical/-/postcss-logical-3.0.0.tgz#2495d0f8b82e9f262725f75f9401b34e7b45d5b5" - integrity sha512-1SUKdJc2vuMOmeItqGuNaC+N8MzBWFWEkAnRnLpFYj1tGGa7NqyVBujfRtgNa2gXR+6RkGUiB2O5Vmh7E2RmiA== - dependencies: - postcss "^7.0.2" +postcss-logical@^5.0.4: + version "5.0.4" + resolved "https://registry.yarnpkg.com/postcss-logical/-/postcss-logical-5.0.4.tgz#ec75b1ee54421acc04d5921576b7d8db6b0e6f73" + integrity sha512-RHXxplCeLh9VjinvMrZONq7im4wjWGlRJAqmAVLXyZaXwfDWP73/oq4NdIp+OZwhQUMj0zjqDfM5Fj7qby+B4g== -postcss-media-minmax@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/postcss-media-minmax/-/postcss-media-minmax-4.0.0.tgz#b75bb6cbc217c8ac49433e12f22048814a4f5ed5" - integrity sha512-fo9moya6qyxsjbFAYl97qKO9gyre3qvbMnkOZeZwlsW6XYFsvs2DMGDlchVLfAd8LHPZDxivu/+qW2SMQeTHBw== - dependencies: - postcss "^7.0.2" +postcss-media-minmax@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/postcss-media-minmax/-/postcss-media-minmax-5.0.0.tgz#7140bddec173e2d6d657edbd8554a55794e2a5b5" + integrity sha512-yDUvFf9QdFZTuCUg0g0uNSHVlJ5X1lSzDZjPSFaiCWvjgsvu8vEVxtahPrLMinIDEEGnx6cBe6iqdx5YWz08wQ== postcss-modules-extract-imports@^2.0.0: version "2.0.0" @@ -9610,119 +9663,109 @@ postcss-modules-values@^3.0.0: icss-utils "^4.0.0" postcss "^7.0.6" -postcss-nesting@^7.0.0: - version "7.0.1" - resolved "https://registry.yarnpkg.com/postcss-nesting/-/postcss-nesting-7.0.1.tgz#b50ad7b7f0173e5b5e3880c3501344703e04c052" - integrity sha512-FrorPb0H3nuVq0Sff7W2rnc3SmIcruVC6YwpcS+k687VxyxO33iE1amna7wHuRVzM8vfiYofXSBHNAZ3QhLvYg== +postcss-nesting@^10.1.9: + version "10.1.10" + resolved "https://registry.yarnpkg.com/postcss-nesting/-/postcss-nesting-10.1.10.tgz#9c396df3d8232cbedfa95baaac6b765b8fd2a817" + integrity sha512-lqd7LXCq0gWc0wKXtoKDru5wEUNjm3OryLVNRZ8OnW8km6fSNUuFrjEhU3nklxXE2jvd4qrox566acgh+xQt8w== dependencies: - postcss "^7.0.2" + "@csstools/selector-specificity" "^2.0.0" + postcss-selector-parser "^6.0.10" -postcss-overflow-shorthand@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/postcss-overflow-shorthand/-/postcss-overflow-shorthand-2.0.0.tgz#31ecf350e9c6f6ddc250a78f0c3e111f32dd4c30" - integrity sha512-aK0fHc9CBNx8jbzMYhshZcEv8LtYnBIRYQD5i7w/K/wS9c2+0NSR6B3OVMu5y0hBHYLcMGjfU+dmWYNKH0I85g== +postcss-opacity-percentage@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/postcss-opacity-percentage/-/postcss-opacity-percentage-1.1.2.tgz#bd698bb3670a0a27f6d657cc16744b3ebf3b1145" + integrity sha512-lyUfF7miG+yewZ8EAk9XUBIlrHyUE6fijnesuz+Mj5zrIHIEw6KcIZSOk/elVMqzLvREmXB83Zi/5QpNRYd47w== + +postcss-overflow-shorthand@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/postcss-overflow-shorthand/-/postcss-overflow-shorthand-3.0.3.tgz#ebcfc0483a15bbf1b27fdd9b3c10125372f4cbc2" + integrity sha512-CxZwoWup9KXzQeeIxtgOciQ00tDtnylYIlJBBODqkgS/PU2jISuWOL/mYLHmZb9ZhZiCaNKsCRiLp22dZUtNsg== + +postcss-page-break@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/postcss-page-break/-/postcss-page-break-3.0.4.tgz#7fbf741c233621622b68d435babfb70dd8c1ee5f" + integrity sha512-1JGu8oCjVXLa9q9rFTo4MbeeA5FMe00/9C7lN4va606Rdb+HkxXtXsmEDrIraQ11fGz/WvKWa8gMuCKkrXpTsQ== + +postcss-place@^7.0.4: + version "7.0.4" + resolved "https://registry.yarnpkg.com/postcss-place/-/postcss-place-7.0.4.tgz#eb026650b7f769ae57ca4f938c1addd6be2f62c9" + integrity sha512-MrgKeiiu5OC/TETQO45kV3npRjOFxEHthsqGtkh3I1rPbZSbXGD/lZVi9j13cYh+NA8PIAPyk6sGjT9QbRyvSg== dependencies: - postcss "^7.0.2" + postcss-value-parser "^4.2.0" -postcss-page-break@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/postcss-page-break/-/postcss-page-break-2.0.0.tgz#add52d0e0a528cabe6afee8b46e2abb277df46bf" - integrity sha512-tkpTSrLpfLfD9HvgOlJuigLuk39wVTbbd8RKcy8/ugV2bNBUW3xU+AIqyxhDrQr1VUj1RmyJrBn1YWrqUm9zAQ== +postcss-preset-env@^7: + version "7.7.2" + resolved "https://registry.yarnpkg.com/postcss-preset-env/-/postcss-preset-env-7.7.2.tgz#769f7f21779b4688c9a6082ae1572416cab415cf" + integrity sha512-1q0ih7EDsZmCb/FMDRvosna7Gsbdx8CvYO5hYT120hcp2ZAuOHpSzibujZ4JpIUcAC02PG6b+eftxqjTFh5BNA== dependencies: - postcss "^7.0.2" + "@csstools/postcss-cascade-layers" "^1.0.4" + "@csstools/postcss-color-function" "^1.1.0" + "@csstools/postcss-font-format-keywords" "^1.0.0" + "@csstools/postcss-hwb-function" "^1.0.1" + "@csstools/postcss-ic-unit" "^1.0.0" + "@csstools/postcss-is-pseudo-class" "^2.0.6" + "@csstools/postcss-normalize-display-values" "^1.0.0" + "@csstools/postcss-oklab-function" "^1.1.0" + "@csstools/postcss-progressive-custom-properties" "^1.3.0" + "@csstools/postcss-stepped-value-functions" "^1.0.0" + "@csstools/postcss-trigonometric-functions" "^1.0.1" + "@csstools/postcss-unset-value" "^1.0.1" + autoprefixer "^10.4.7" + browserslist "^4.21.0" + css-blank-pseudo "^3.0.3" + css-has-pseudo "^3.0.4" + css-prefers-color-scheme "^6.0.3" + cssdb "^6.6.3" + postcss-attribute-case-insensitive "^5.0.1" + postcss-clamp "^4.1.0" + postcss-color-functional-notation "^4.2.3" + postcss-color-hex-alpha "^8.0.4" + postcss-color-rebeccapurple "^7.1.0" + postcss-custom-media "^8.0.2" + postcss-custom-properties "^12.1.8" + postcss-custom-selectors "^6.0.3" + postcss-dir-pseudo-class "^6.0.4" + postcss-double-position-gradients "^3.1.1" + postcss-env-function "^4.0.6" + postcss-focus-visible "^6.0.4" + postcss-focus-within "^5.0.4" + postcss-font-variant "^5.0.0" + postcss-gap-properties "^3.0.3" + postcss-image-set-function "^4.0.6" + postcss-initial "^4.0.1" + postcss-lab-function "^4.2.0" + postcss-logical "^5.0.4" + postcss-media-minmax "^5.0.0" + postcss-nesting "^10.1.9" + postcss-opacity-percentage "^1.1.2" + postcss-overflow-shorthand "^3.0.3" + postcss-page-break "^3.0.4" + postcss-place "^7.0.4" + postcss-pseudo-class-any-link "^7.1.5" + postcss-replace-overflow-wrap "^4.0.0" + postcss-selector-not "^6.0.0" + postcss-value-parser "^4.2.0" -postcss-place@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/postcss-place/-/postcss-place-4.0.1.tgz#e9f39d33d2dc584e46ee1db45adb77ca9d1dcc62" - integrity sha512-Zb6byCSLkgRKLODj/5mQugyuj9bvAAw9LqJJjgwz5cYryGeXfFZfSXoP1UfveccFmeq0b/2xxwcTEVScnqGxBg== +postcss-pseudo-class-any-link@^7.1.5: + version "7.1.5" + resolved "https://registry.yarnpkg.com/postcss-pseudo-class-any-link/-/postcss-pseudo-class-any-link-7.1.5.tgz#1233b054004c386c681c553af35f68ec03fffaa6" + integrity sha512-nSGKGScwFTaaV8Cyi27W9FegX3l3b7tmNxujxmykI/j3++cBAiq8fTUAU3ZK0s2aneN2T8cTUvKdNedzp3JIEA== dependencies: - postcss "^7.0.2" - postcss-values-parser "^2.0.0" + postcss-selector-parser "^6.0.10" -postcss-preset-env@^6.7.0: - version "6.7.1" - resolved "https://registry.yarnpkg.com/postcss-preset-env/-/postcss-preset-env-6.7.1.tgz#26563d2e9395d626a45a836450844540694bfcef" - integrity sha512-rlRkgX9t0v2On33n7TK8pnkcYOATGQSv48J2RS8GsXhqtg+xk6AummHP88Y5mJo0TLJelBjePvSjScTNkj3+qw== - dependencies: - autoprefixer "^9.6.1" - browserslist "^4.6.4" - caniuse-lite "^1.0.30000981" - css-blank-pseudo "^0.1.4" - css-has-pseudo "^0.10.0" - css-prefers-color-scheme "^3.1.1" - cssdb "^4.4.0" - postcss "^7.0.17" - postcss-attribute-case-insensitive "^4.0.1" - postcss-color-functional-notation "^2.0.1" - postcss-color-gray "^5.0.0" - postcss-color-hex-alpha "^5.0.3" - postcss-color-mod-function "^3.0.3" - postcss-color-rebeccapurple "^4.0.1" - postcss-custom-media "^7.0.8" - postcss-custom-properties "^8.0.11" - postcss-custom-selectors "^5.1.2" - postcss-dir-pseudo-class "^5.0.0" - postcss-double-position-gradients "^1.0.0" - postcss-env-function "^2.0.2" - postcss-focus-visible "^4.0.0" - postcss-focus-within "^3.0.0" - postcss-font-variant "^4.0.0" - postcss-gap-properties "^2.0.0" - postcss-image-set-function "^3.0.1" - postcss-initial "^3.0.0" - postcss-lab-function "^2.0.1" - postcss-logical "^3.0.0" - postcss-media-minmax "^4.0.0" - postcss-nesting "^7.0.0" - postcss-overflow-shorthand "^2.0.0" - postcss-page-break "^2.0.0" - postcss-place "^4.0.1" - postcss-pseudo-class-any-link "^6.0.0" - postcss-replace-overflow-wrap "^3.0.0" - postcss-selector-matches "^4.0.0" - postcss-selector-not "^4.0.0" - -postcss-pseudo-class-any-link@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/postcss-pseudo-class-any-link/-/postcss-pseudo-class-any-link-6.0.0.tgz#2ed3eed393b3702879dec4a87032b210daeb04d1" - integrity sha512-lgXW9sYJdLqtmw23otOzrtbDXofUdfYzNm4PIpNE322/swES3VU9XlXHeJS46zT2onFO7V1QFdD4Q9LiZj8mew== - dependencies: - postcss "^7.0.2" - postcss-selector-parser "^5.0.0-rc.3" - -postcss-replace-overflow-wrap@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/postcss-replace-overflow-wrap/-/postcss-replace-overflow-wrap-3.0.0.tgz#61b360ffdaedca84c7c918d2b0f0d0ea559ab01c" - integrity sha512-2T5hcEHArDT6X9+9dVSPQdo7QHzG4XKclFT8rU5TzJPDN7RIRTbO9c4drUISOVemLj03aezStHCR2AIcr8XLpw== - dependencies: - postcss "^7.0.2" - -postcss-selector-matches@^4.0.0: +postcss-replace-overflow-wrap@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/postcss-selector-matches/-/postcss-selector-matches-4.0.0.tgz#71c8248f917ba2cc93037c9637ee09c64436fcff" - integrity sha512-LgsHwQR/EsRYSqlwdGzeaPKVT0Ml7LAT6E75T8W8xLJY62CE4S/l03BWIt3jT8Taq22kXP08s2SfTSzaraoPww== - dependencies: - balanced-match "^1.0.0" - postcss "^7.0.2" + resolved "https://registry.yarnpkg.com/postcss-replace-overflow-wrap/-/postcss-replace-overflow-wrap-4.0.0.tgz#d2df6bed10b477bf9c52fab28c568b4b29ca4319" + integrity sha512-KmF7SBPphT4gPPcKZc7aDkweHiKEEO8cla/GjcBK+ckKxiZslIu3C4GCRW3DNfL0o7yW7kMQu9xlZ1kXRXLXtw== -postcss-selector-not@^4.0.0: - version "4.0.1" - resolved "https://registry.yarnpkg.com/postcss-selector-not/-/postcss-selector-not-4.0.1.tgz#263016eef1cf219e0ade9a913780fc1f48204cbf" - integrity sha512-YolvBgInEK5/79C+bdFMyzqTg6pkYqDbzZIST/PDMqa/o3qtXenD05apBG2jLgT0/BQ77d4U2UK12jWpilqMAQ== +postcss-selector-not@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/postcss-selector-not/-/postcss-selector-not-6.0.0.tgz#d100f273d345917246762300411b4d2e24905047" + integrity sha512-i/HI/VNd3V9e1WOLCwJsf9nePBRXqcGtVibcJ9FsVo0agfDEfsLSlFt94aYjY35wUNcdG0KrvdyjEr7It50wLQ== dependencies: - balanced-match "^1.0.0" - postcss "^7.0.2" + postcss-selector-parser "^6.0.10" -postcss-selector-parser@^5.0.0-rc.3, postcss-selector-parser@^5.0.0-rc.4: - version "5.0.0" - resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-5.0.0.tgz#249044356697b33b64f1a8f7c80922dddee7195c" - integrity sha512-w+zLE5Jhg6Liz8+rQOWEAwtwkyqpfnmsinXjXg6cY7YIONZZtgvE0v2O0uhQBs0peNomOJwWRKt6JBfTdTd3OQ== - dependencies: - cssesc "^2.0.0" - indexes-of "^1.0.1" - uniq "^1.0.1" - -postcss-selector-parser@^6.0.0, postcss-selector-parser@^6.0.2: +postcss-selector-parser@^6.0.0, postcss-selector-parser@^6.0.10, postcss-selector-parser@^6.0.2, postcss-selector-parser@^6.0.4, postcss-selector-parser@^6.0.9: version "6.0.10" resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz#79b61e2c0d1bfc2602d549e11d0876256f8df88d" integrity sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w== @@ -9730,21 +9773,12 @@ postcss-selector-parser@^6.0.0, postcss-selector-parser@^6.0.2: cssesc "^3.0.0" util-deprecate "^1.0.2" -postcss-value-parser@^4.1.0: +postcss-value-parser@^4.1.0, postcss-value-parser@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz#723c09920836ba6d3e5af019f92bc0971c02e514" integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ== -postcss-values-parser@^2.0.0, postcss-values-parser@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/postcss-values-parser/-/postcss-values-parser-2.0.1.tgz#da8b472d901da1e205b47bdc98637b9e9e550e5f" - integrity sha512-2tLuBsA6P4rYTNKCXYG/71C7j1pU6pK503suYOmn4xYrQIzW+opD+7FAFNuGSdZC/3Qfy334QbeMu7MEb8gOxg== - dependencies: - flatten "^1.0.2" - indexes-of "^1.0.1" - uniq "^1.0.1" - -postcss@^7.0.14, postcss@^7.0.17, postcss@^7.0.2, postcss@^7.0.26, postcss@^7.0.32, postcss@^7.0.36, postcss@^7.0.5, postcss@^7.0.6: +postcss@^7.0.14, postcss@^7.0.26, postcss@^7.0.32, postcss@^7.0.36, postcss@^7.0.5, postcss@^7.0.6: version "7.0.39" resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.39.tgz#9624375d965630e2e1f2c02a935c82a59cb48309" integrity sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA== @@ -11686,11 +11720,6 @@ union-value@^1.0.0: is-extendable "^0.1.1" set-value "^2.0.1" -uniq@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/uniq/-/uniq-1.0.1.tgz#b31c5ae8254844a3a8281541ce2b04b865a734ff" - integrity sha512-Gw+zz50YNKPDKXs+9d+aKAjVwpjNwqzvNpLigIruT4HA9lMZNdMqs9x07kKHB/L9WRzqp4+DlTU5s4wG2esdoA== - unique-filename@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/unique-filename/-/unique-filename-1.1.1.tgz#1d69769369ada0583103a1e6ae87681b56573230" From b31c6c6780bca4b47525298eac0870cee0530e37 Mon Sep 17 00:00:00 2001 From: David Baker Date: Fri, 8 Jul 2022 20:55:18 +0100 Subject: [PATCH 47/49] Bypass lobby in embedded mode --- src/room/GroupCallView.jsx | 51 +++++++++++++++++++++++--------------- 1 file changed, 31 insertions(+), 20 deletions(-) diff --git a/src/room/GroupCallView.jsx b/src/room/GroupCallView.jsx index 2403bf60..defcf801 100644 --- a/src/room/GroupCallView.jsx +++ b/src/room/GroupCallView.jsx @@ -61,7 +61,10 @@ export function GroupCallView({ useEffect(() => { window.groupCall = groupCall; - }, [groupCall]); + + // In embedded mode, bypass the lobby and just enter the call straight away + if (isEmbedded) groupCall.enter(); + }, [groupCall, isEmbedded]); useSentryGroupCallHandler(groupCall); @@ -128,24 +131,32 @@ export function GroupCallView({ } else if (left) { return ; } else { - return ( - - ); + if (isEmbedded) { + return ( + +

Loading room...

+
, + ); + } else { + return ( + + ); + } } } From 5199fd2566487932bf9f431575197c00b71d6cc5 Mon Sep 17 00:00:00 2001 From: David Baker Date: Fri, 8 Jul 2022 21:17:27 +0100 Subject: [PATCH 48/49] Prettier --- src/room/GroupCallView.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/room/GroupCallView.jsx b/src/room/GroupCallView.jsx index defcf801..9794a00c 100644 --- a/src/room/GroupCallView.jsx +++ b/src/room/GroupCallView.jsx @@ -135,7 +135,7 @@ export function GroupCallView({ return (

Loading room...

-
, + ); } else { return ( From 32907764b3b2c2fb4986afbdcf4459446db97e10 Mon Sep 17 00:00:00 2001 From: David Baker Date: Mon, 11 Jul 2022 13:23:03 +0100 Subject: [PATCH 49/49] Add ptt URL param to control what mode rooms are created in --- src/room/GroupCallLoader.jsx | 11 +++++++++-- src/room/RoomPage.jsx | 15 ++++++++++++--- src/room/useLoadGroupCall.js | 12 +++++++++--- 3 files changed, 30 insertions(+), 8 deletions(-) diff --git a/src/room/GroupCallLoader.jsx b/src/room/GroupCallLoader.jsx index dd327f9f..f0741284 100644 --- a/src/room/GroupCallLoader.jsx +++ b/src/room/GroupCallLoader.jsx @@ -19,12 +19,19 @@ import { useLoadGroupCall } from "./useLoadGroupCall"; import { ErrorView, FullScreenView } from "../FullScreenView"; import { usePageTitle } from "../usePageTitle"; -export function GroupCallLoader({ client, roomId, viaServers, children }) { +export function GroupCallLoader({ + client, + roomId, + viaServers, + createPtt, + children, +}) { const { loading, error, groupCall } = useLoadGroupCall( client, roomId, viaServers, - true + true, + createPtt ); usePageTitle(groupCall ? groupCall.room.name : "Loading..."); diff --git a/src/room/RoomPage.jsx b/src/room/RoomPage.jsx index ae3c927c..1fe7eea6 100644 --- a/src/room/RoomPage.jsx +++ b/src/room/RoomPage.jsx @@ -29,9 +29,13 @@ export function RoomPage() { const { roomId: maybeRoomId } = useParams(); const { hash, search } = useLocation(); - const [viaServers, isEmbedded] = useMemo(() => { + const [viaServers, isEmbedded, isPtt] = useMemo(() => { const params = new URLSearchParams(search); - return [params.getAll("via"), params.has("embed")]; + return [ + params.getAll("via"), + params.has("embed"), + params.get("ptt") === "true", + ]; }, [search]); const roomId = (maybeRoomId || hash || "").toLowerCase(); @@ -49,7 +53,12 @@ export function RoomPage() { return ( - + {(groupCall) => ( setState((prevState) => ({ ...prevState, loading: false, error })) ); - }, [client, roomId, state.reloadId, createIfNotFound, viaServers]); + }, [client, roomId, state.reloadId, createIfNotFound, viaServers, createPtt]); return state; }