Merge pull request #1621 from vector-im/renovate/prettier-3.x
Update dependency prettier to v3
This commit is contained in:
@@ -14,7 +14,7 @@ module.exports = {
|
|||||||
Array.isArray(item) &&
|
Array.isArray(item) &&
|
||||||
item.length > 0 &&
|
item.length > 0 &&
|
||||||
item[0].name === "vite-plugin-mdx"
|
item[0].name === "vite-plugin-mdx"
|
||||||
)
|
),
|
||||||
);
|
);
|
||||||
config.plugins.push(svgrPlugin());
|
config.plugins.push(svgrPlugin());
|
||||||
config.resolve = config.resolve || {};
|
config.resolve = config.resolve || {};
|
||||||
|
|||||||
@@ -117,7 +117,7 @@
|
|||||||
"jest": "^29.2.2",
|
"jest": "^29.2.2",
|
||||||
"jest-environment-jsdom": "^29.3.1",
|
"jest-environment-jsdom": "^29.3.1",
|
||||||
"jest-mock": "^29.5.0",
|
"jest-mock": "^29.5.0",
|
||||||
"prettier": "^2.6.2",
|
"prettier": "^3.0.0",
|
||||||
"sass": "^1.42.1",
|
"sass": "^1.42.1",
|
||||||
"typescript": "^5.1.6",
|
"typescript": "^5.1.6",
|
||||||
"typescript-eslint-language-service": "^5.0.5",
|
"typescript-eslint-language-service": "^5.0.5",
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<!DOCTYPE html>
|
<!doctype html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ export const Avatar: FC<Props> = ({
|
|||||||
Object.values(Size).includes(size as Size)
|
Object.values(Size).includes(size as Size)
|
||||||
? sizes.get(size as Size)
|
? sizes.get(size as Size)
|
||||||
: (size as number),
|
: (size as number),
|
||||||
[size]
|
[size],
|
||||||
);
|
);
|
||||||
|
|
||||||
const resolvedSrc = useMemo(() => {
|
const resolvedSrc = useMemo(() => {
|
||||||
|
|||||||
@@ -190,7 +190,7 @@ export const ClientProvider: FC<Props> = ({ children }) => {
|
|||||||
user: session.user_id,
|
user: session.user_id,
|
||||||
password: session.tempPassword,
|
password: session.tempPassword,
|
||||||
},
|
},
|
||||||
password
|
password,
|
||||||
);
|
);
|
||||||
|
|
||||||
saveSession({ ...session, passwordlessUser: false });
|
saveSession({ ...session, passwordlessUser: false });
|
||||||
@@ -200,7 +200,7 @@ export const ClientProvider: FC<Props> = ({ children }) => {
|
|||||||
passwordlessUser: false,
|
passwordlessUser: false,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
[initClientState?.client]
|
[initClientState?.client],
|
||||||
);
|
);
|
||||||
|
|
||||||
const setClient = useCallback(
|
const setClient = useCallback(
|
||||||
@@ -222,7 +222,7 @@ export const ClientProvider: FC<Props> = ({ children }) => {
|
|||||||
setInitClientState(null);
|
setInitClientState(null);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[initClientState?.client]
|
[initClientState?.client],
|
||||||
);
|
);
|
||||||
|
|
||||||
const logout = useCallback(async () => {
|
const logout = useCallback(async () => {
|
||||||
@@ -250,7 +250,7 @@ export const ClientProvider: FC<Props> = ({ children }) => {
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const [alreadyOpenedErr, setAlreadyOpenedErr] = useState<Error | undefined>(
|
const [alreadyOpenedErr, setAlreadyOpenedErr] = useState<Error | undefined>(
|
||||||
undefined
|
undefined,
|
||||||
);
|
);
|
||||||
useEventTarget(
|
useEventTarget(
|
||||||
loadChannel,
|
loadChannel,
|
||||||
@@ -258,9 +258,9 @@ export const ClientProvider: FC<Props> = ({ children }) => {
|
|||||||
useCallback(() => {
|
useCallback(() => {
|
||||||
initClientState?.client.stopClient();
|
initClientState?.client.stopClient();
|
||||||
setAlreadyOpenedErr(
|
setAlreadyOpenedErr(
|
||||||
translatedError("This application has been opened in another tab.", t)
|
translatedError("This application has been opened in another tab.", t),
|
||||||
);
|
);
|
||||||
}, [initClientState?.client, setAlreadyOpenedErr, t])
|
}, [initClientState?.client, setAlreadyOpenedErr, t]),
|
||||||
);
|
);
|
||||||
|
|
||||||
const [isDisconnected, setIsDisconnected] = useState(false);
|
const [isDisconnected, setIsDisconnected] = useState(false);
|
||||||
@@ -301,7 +301,7 @@ export const ClientProvider: FC<Props> = ({ children }) => {
|
|||||||
(state: SyncState, _old: SyncState | null, data?: ISyncStateData) => {
|
(state: SyncState, _old: SyncState | null, data?: ISyncStateData) => {
|
||||||
setIsDisconnected(clientIsDisconnected(state, data));
|
setIsDisconnected(clientIsDisconnected(state, data));
|
||||||
},
|
},
|
||||||
[]
|
[],
|
||||||
);
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -387,7 +387,7 @@ async function loadClient(): Promise<InitResult | null> {
|
|||||||
logger.warn(
|
logger.warn(
|
||||||
"The previous session was lost, and we couldn't log it out, " +
|
"The previous session was lost, and we couldn't log it out, " +
|
||||||
err +
|
err +
|
||||||
"either"
|
"either",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -423,6 +423,6 @@ const loadSession = (): Session | undefined => {
|
|||||||
|
|
||||||
const clientIsDisconnected = (
|
const clientIsDisconnected = (
|
||||||
syncState: SyncState,
|
syncState: SyncState,
|
||||||
syncData?: ISyncStateData
|
syncData?: ISyncStateData,
|
||||||
): boolean =>
|
): boolean =>
|
||||||
syncState === "ERROR" && syncData?.error?.name === "ConnectionError";
|
syncState === "ERROR" && syncData?.error?.name === "ConnectionError";
|
||||||
|
|||||||
@@ -48,5 +48,5 @@ export const Glass = forwardRef<HTMLDivElement, Props>(
|
|||||||
>
|
>
|
||||||
{Children.only(children)}
|
{Children.only(children)}
|
||||||
</div>
|
</div>
|
||||||
)
|
),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ export const LeftNav: FC<LeftNavProps> = ({
|
|||||||
styles.nav,
|
styles.nav,
|
||||||
styles.leftNav,
|
styles.leftNav,
|
||||||
{ [styles.hideMobile]: hideMobile },
|
{ [styles.hideMobile]: hideMobile },
|
||||||
className
|
className,
|
||||||
)}
|
)}
|
||||||
{...rest}
|
{...rest}
|
||||||
>
|
>
|
||||||
@@ -85,7 +85,7 @@ export const RightNav: FC<RightNavProps> = ({
|
|||||||
styles.nav,
|
styles.nav,
|
||||||
styles.rightNav,
|
styles.rightNav,
|
||||||
{ [styles.hideMobile]: hideMobile },
|
{ [styles.hideMobile]: hideMobile },
|
||||||
className
|
className,
|
||||||
)}
|
)}
|
||||||
{...rest}
|
{...rest}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ export class LazyEventEmitter extends EventEmitter {
|
|||||||
public addListener(
|
public addListener(
|
||||||
type: string | symbol,
|
type: string | symbol,
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
listener: (...args: any[]) => void
|
listener: (...args: any[]) => void,
|
||||||
): this {
|
): this {
|
||||||
return this.on(type, listener);
|
return this.on(type, listener);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -77,7 +77,7 @@ function Option<T>({ item, state, className }: OptionProps<T>): ReactNode {
|
|||||||
const { optionProps, isSelected, isFocused, isDisabled } = useOption(
|
const { optionProps, isSelected, isFocused, isDisabled } = useOption(
|
||||||
{ key: item.key },
|
{ key: item.key },
|
||||||
state,
|
state,
|
||||||
ref
|
ref,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Hack: remove the onPointerUp event handler and re-wire it to
|
// Hack: remove the onPointerUp event handler and re-wire it to
|
||||||
@@ -97,7 +97,7 @@ function Option<T>({ item, state, className }: OptionProps<T>): ReactNode {
|
|||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
origPointerUp(e as unknown as PointerEvent<HTMLElement>);
|
origPointerUp(e as unknown as PointerEvent<HTMLElement>);
|
||||||
},
|
},
|
||||||
[origPointerUp]
|
[origPointerUp],
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -82,7 +82,7 @@ function MenuItem<T>({
|
|||||||
onClose,
|
onClose,
|
||||||
},
|
},
|
||||||
state,
|
state,
|
||||||
ref
|
ref,
|
||||||
);
|
);
|
||||||
|
|
||||||
const [isFocused, setFocused] = useState(false);
|
const [isFocused, setFocused] = useState(false);
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ export const Modal: FC<Props> = ({
|
|||||||
(open: boolean) => {
|
(open: boolean) => {
|
||||||
if (!open) onDismiss?.();
|
if (!open) onDismiss?.();
|
||||||
},
|
},
|
||||||
[onDismiss]
|
[onDismiss],
|
||||||
);
|
);
|
||||||
|
|
||||||
if (touchscreen) {
|
if (touchscreen) {
|
||||||
@@ -92,7 +92,7 @@ export const Modal: FC<Props> = ({
|
|||||||
className,
|
className,
|
||||||
overlayStyles.overlay,
|
overlayStyles.overlay,
|
||||||
styles.modal,
|
styles.modal,
|
||||||
styles.drawer
|
styles.drawer,
|
||||||
)}
|
)}
|
||||||
{...rest}
|
{...rest}
|
||||||
>
|
>
|
||||||
@@ -124,7 +124,7 @@ export const Modal: FC<Props> = ({
|
|||||||
overlayStyles.overlay,
|
overlayStyles.overlay,
|
||||||
overlayStyles.animate,
|
overlayStyles.animate,
|
||||||
styles.modal,
|
styles.modal,
|
||||||
styles.dialog
|
styles.dialog,
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<div className={styles.content}>
|
<div className={styles.content}>
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ export const Toast: FC<Props> = ({
|
|||||||
(open: boolean) => {
|
(open: boolean) => {
|
||||||
if (!open) onDismiss();
|
if (!open) onDismiss();
|
||||||
},
|
},
|
||||||
[onDismiss]
|
[onDismiss],
|
||||||
);
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -91,7 +91,7 @@ export const Toast: FC<Props> = ({
|
|||||||
className={classNames(
|
className={classNames(
|
||||||
overlayStyles.overlay,
|
overlayStyles.overlay,
|
||||||
overlayStyles.animate,
|
overlayStyles.animate,
|
||||||
styles.toast
|
styles.toast,
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<DialogTitle asChild>
|
<DialogTitle asChild>
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ interface TooltipProps {
|
|||||||
const Tooltip = forwardRef<HTMLDivElement, TooltipProps>(
|
const Tooltip = forwardRef<HTMLDivElement, TooltipProps>(
|
||||||
(
|
(
|
||||||
{ state, className, children, ...rest }: TooltipProps,
|
{ state, className, children, ...rest }: TooltipProps,
|
||||||
ref: ForwardedRef<HTMLDivElement>
|
ref: ForwardedRef<HTMLDivElement>,
|
||||||
) => {
|
) => {
|
||||||
const { tooltipProps } = useTooltip(rest, state);
|
const { tooltipProps } = useTooltip(rest, state);
|
||||||
|
|
||||||
@@ -56,7 +56,7 @@ const Tooltip = forwardRef<HTMLDivElement, TooltipProps>(
|
|||||||
{children}
|
{children}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
interface TooltipTriggerProps {
|
interface TooltipTriggerProps {
|
||||||
@@ -69,7 +69,7 @@ interface TooltipTriggerProps {
|
|||||||
export const TooltipTrigger = forwardRef<HTMLElement, TooltipTriggerProps>(
|
export const TooltipTrigger = forwardRef<HTMLElement, TooltipTriggerProps>(
|
||||||
(
|
(
|
||||||
{ children, placement, tooltip, ...rest }: TooltipTriggerProps,
|
{ children, placement, tooltip, ...rest }: TooltipTriggerProps,
|
||||||
ref: ForwardedRef<HTMLElement>
|
ref: ForwardedRef<HTMLElement>,
|
||||||
) => {
|
) => {
|
||||||
const tooltipTriggerProps = { delay: 250, ...rest };
|
const tooltipTriggerProps = { delay: 250, ...rest };
|
||||||
const tooltipState = useTooltipTriggerState(tooltipTriggerProps);
|
const tooltipState = useTooltipTriggerState(tooltipTriggerProps);
|
||||||
@@ -78,7 +78,7 @@ export const TooltipTrigger = forwardRef<HTMLElement, TooltipTriggerProps>(
|
|||||||
const { triggerProps, tooltipProps } = useTooltipTrigger(
|
const { triggerProps, tooltipProps } = useTooltipTrigger(
|
||||||
tooltipTriggerProps,
|
tooltipTriggerProps,
|
||||||
tooltipState,
|
tooltipState,
|
||||||
triggerRef
|
triggerRef,
|
||||||
);
|
);
|
||||||
|
|
||||||
const { overlayProps } = useOverlayPosition({
|
const { overlayProps } = useOverlayPosition({
|
||||||
@@ -94,7 +94,7 @@ export const TooltipTrigger = forwardRef<HTMLElement, TooltipTriggerProps>(
|
|||||||
<children.type
|
<children.type
|
||||||
{...mergeProps<typeof children.props | typeof rest>(
|
{...mergeProps<typeof children.props | typeof rest>(
|
||||||
children.props,
|
children.props,
|
||||||
rest
|
rest,
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
{tooltipState.isOpen && (
|
{tooltipState.isOpen && (
|
||||||
@@ -110,5 +110,5 @@ export const TooltipTrigger = forwardRef<HTMLElement, TooltipTriggerProps>(
|
|||||||
)}
|
)}
|
||||||
</FocusableProvider>
|
</FocusableProvider>
|
||||||
);
|
);
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -39,5 +39,5 @@ class TranslatedErrorImpl extends TranslatedError {}
|
|||||||
// function instead
|
// function instead
|
||||||
export const translatedError = (
|
export const translatedError = (
|
||||||
messageKey: string,
|
messageKey: string,
|
||||||
t: typeof i18n.t
|
t: typeof i18n.t,
|
||||||
): TranslatedError => new TranslatedErrorImpl(messageKey, t);
|
): TranslatedError => new TranslatedErrorImpl(messageKey, t);
|
||||||
|
|||||||
@@ -119,17 +119,17 @@ interface UrlParams {
|
|||||||
// file.
|
// file.
|
||||||
export function editFragmentQuery(
|
export function editFragmentQuery(
|
||||||
hash: string,
|
hash: string,
|
||||||
edit: (params: URLSearchParams) => URLSearchParams
|
edit: (params: URLSearchParams) => URLSearchParams,
|
||||||
): string {
|
): string {
|
||||||
const fragmentQueryStart = hash.indexOf("?");
|
const fragmentQueryStart = hash.indexOf("?");
|
||||||
const fragmentParams = edit(
|
const fragmentParams = edit(
|
||||||
new URLSearchParams(
|
new URLSearchParams(
|
||||||
fragmentQueryStart === -1 ? "" : hash.substring(fragmentQueryStart)
|
fragmentQueryStart === -1 ? "" : hash.substring(fragmentQueryStart),
|
||||||
)
|
),
|
||||||
);
|
);
|
||||||
return `${hash.substring(
|
return `${hash.substring(
|
||||||
0,
|
0,
|
||||||
fragmentQueryStart
|
fragmentQueryStart,
|
||||||
)}?${fragmentParams.toString()}`;
|
)}?${fragmentParams.toString()}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -142,7 +142,7 @@ class ParamParser {
|
|||||||
|
|
||||||
const fragmentQueryStart = hash.indexOf("?");
|
const fragmentQueryStart = hash.indexOf("?");
|
||||||
this.fragmentParams = new URLSearchParams(
|
this.fragmentParams = new URLSearchParams(
|
||||||
fragmentQueryStart === -1 ? "" : hash.substring(fragmentQueryStart)
|
fragmentQueryStart === -1 ? "" : hash.substring(fragmentQueryStart),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -174,7 +174,7 @@ class ParamParser {
|
|||||||
*/
|
*/
|
||||||
export const getUrlParams = (
|
export const getUrlParams = (
|
||||||
search = window.location.search,
|
search = window.location.search,
|
||||||
hash = window.location.hash
|
hash = window.location.hash,
|
||||||
): UrlParams => {
|
): UrlParams => {
|
||||||
const parser = new ParamParser(search, hash);
|
const parser = new ParamParser(search, hash);
|
||||||
|
|
||||||
@@ -221,7 +221,7 @@ export const useUrlParams = (): UrlParams => {
|
|||||||
export function getRoomIdentifierFromUrl(
|
export function getRoomIdentifierFromUrl(
|
||||||
pathname: string,
|
pathname: string,
|
||||||
search: string,
|
search: string,
|
||||||
hash: string
|
hash: string,
|
||||||
): RoomIdentifier {
|
): RoomIdentifier {
|
||||||
let roomAlias: string | null = null;
|
let roomAlias: string | null = null;
|
||||||
pathname = pathname.substring(1); // Strip the "/"
|
pathname = pathname.substring(1); // Strip the "/"
|
||||||
@@ -281,6 +281,6 @@ export const useRoomIdentifier = (): RoomIdentifier => {
|
|||||||
const { pathname, search, hash } = useLocation();
|
const { pathname, search, hash } = useLocation();
|
||||||
return useMemo(
|
return useMemo(
|
||||||
() => getRoomIdentifierFromUrl(pathname, search, hash),
|
() => getRoomIdentifierFromUrl(pathname, search, hash),
|
||||||
[pathname, search, hash]
|
[pathname, search, hash],
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ export const UserMenuContainer: FC<Props> = ({ preventNavigation = false }) => {
|
|||||||
const [settingsModalOpen, setSettingsModalOpen] = useState(false);
|
const [settingsModalOpen, setSettingsModalOpen] = useState(false);
|
||||||
const onDismissSettingsModal = useCallback(
|
const onDismissSettingsModal = useCallback(
|
||||||
() => setSettingsModalOpen(false),
|
() => setSettingsModalOpen(false),
|
||||||
[setSettingsModalOpen]
|
[setSettingsModalOpen],
|
||||||
);
|
);
|
||||||
|
|
||||||
const [defaultSettingsTab, setDefaultSettingsTab] = useState<string>();
|
const [defaultSettingsTab, setDefaultSettingsTab] = useState<string>();
|
||||||
@@ -58,7 +58,7 @@ export const UserMenuContainer: FC<Props> = ({ preventNavigation = false }) => {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[history, location, logout, setSettingsModalOpen]
|
[history, location, logout, setSettingsModalOpen],
|
||||||
);
|
);
|
||||||
|
|
||||||
const userName = client?.getUserIdLocalpart() ?? "";
|
const userName = client?.getUserIdLocalpart() ?? "";
|
||||||
|
|||||||
@@ -146,7 +146,7 @@ export class PosthogAnalytics {
|
|||||||
this.enabled = true;
|
this.enabled = true;
|
||||||
} else {
|
} else {
|
||||||
logger.info(
|
logger.info(
|
||||||
"Posthog is not enabled because there is no api key or no host given in the config"
|
"Posthog is not enabled because there is no api key or no host given in the config",
|
||||||
);
|
);
|
||||||
this.enabled = false;
|
this.enabled = false;
|
||||||
}
|
}
|
||||||
@@ -157,7 +157,7 @@ export class PosthogAnalytics {
|
|||||||
|
|
||||||
private sanitizeProperties = (
|
private sanitizeProperties = (
|
||||||
properties: Properties,
|
properties: Properties,
|
||||||
_eventName: string
|
_eventName: string,
|
||||||
): Properties => {
|
): Properties => {
|
||||||
// Callback from posthog to sanitize properties before sending them to the server.
|
// Callback from posthog to sanitize properties before sending them to the server.
|
||||||
// Here we sanitize posthog's built in properties which leak PII e.g. url reporting.
|
// Here we sanitize posthog's built in properties which leak PII e.g. url reporting.
|
||||||
@@ -201,7 +201,7 @@ export class PosthogAnalytics {
|
|||||||
private capture(
|
private capture(
|
||||||
eventName: string,
|
eventName: string,
|
||||||
properties: Properties,
|
properties: Properties,
|
||||||
options?: CaptureOptions
|
options?: CaptureOptions,
|
||||||
): void {
|
): void {
|
||||||
if (!this.enabled) {
|
if (!this.enabled) {
|
||||||
return;
|
return;
|
||||||
@@ -237,7 +237,7 @@ export class PosthogAnalytics {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async identifyUser(
|
private async identifyUser(
|
||||||
analyticsIdGenerator: () => string
|
analyticsIdGenerator: () => string,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
if (this.anonymity == Anonymity.Pseudonymous && this.enabled) {
|
if (this.anonymity == Anonymity.Pseudonymous && this.enabled) {
|
||||||
// Check the user's account_data for an analytics ID to use. Storing the ID in account_data allows
|
// Check the user's account_data for an analytics ID to use. Storing the ID in account_data allows
|
||||||
@@ -260,14 +260,14 @@ export class PosthogAnalytics {
|
|||||||
// The above could fail due to network requests, but not essential to starting the application,
|
// The above could fail due to network requests, but not essential to starting the application,
|
||||||
// so swallow it.
|
// so swallow it.
|
||||||
logger.log(
|
logger.log(
|
||||||
"Unable to identify user for tracking" + (e as Error)?.toString()
|
"Unable to identify user for tracking" + (e as Error)?.toString(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (analyticsID) {
|
if (analyticsID) {
|
||||||
this.posthog.identify(analyticsID);
|
this.posthog.identify(analyticsID);
|
||||||
} else {
|
} else {
|
||||||
logger.info(
|
logger.info(
|
||||||
"No analyticsID is availble. Should not try to setup posthog"
|
"No analyticsID is availble. Should not try to setup posthog",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -280,7 +280,7 @@ export class PosthogAnalytics {
|
|||||||
accountAnalyticsId = getUrlParams().analyticsID;
|
accountAnalyticsId = getUrlParams().analyticsID;
|
||||||
} else {
|
} else {
|
||||||
const accountData = await client.getAccountDataFromServer(
|
const accountData = await client.getAccountDataFromServer(
|
||||||
PosthogAnalytics.ANALYTICS_EVENT_TYPE
|
PosthogAnalytics.ANALYTICS_EVENT_TYPE,
|
||||||
);
|
);
|
||||||
accountAnalyticsId = accountData?.id;
|
accountAnalyticsId = accountData?.id;
|
||||||
}
|
}
|
||||||
@@ -294,13 +294,13 @@ export class PosthogAnalytics {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async hashedEcAnalyticsId(
|
private async hashedEcAnalyticsId(
|
||||||
accountAnalyticsId: string
|
accountAnalyticsId: string,
|
||||||
): Promise<string> {
|
): Promise<string> {
|
||||||
const client: MatrixClient = window.matrixclient;
|
const client: MatrixClient = window.matrixclient;
|
||||||
const posthogIdMaterial = "ec" + accountAnalyticsId + client.getUserId();
|
const posthogIdMaterial = "ec" + accountAnalyticsId + client.getUserId();
|
||||||
const bufferForPosthogId = await crypto.subtle.digest(
|
const bufferForPosthogId = await crypto.subtle.digest(
|
||||||
"sha-256",
|
"sha-256",
|
||||||
Buffer.from(posthogIdMaterial, "utf-8")
|
Buffer.from(posthogIdMaterial, "utf-8"),
|
||||||
);
|
);
|
||||||
const view = new Int32Array(bufferForPosthogId);
|
const view = new Int32Array(bufferForPosthogId);
|
||||||
return Array.from(view)
|
return Array.from(view)
|
||||||
@@ -314,11 +314,11 @@ export class PosthogAnalytics {
|
|||||||
|
|
||||||
// the analytics ID only needs to be set in the standalone version.
|
// the analytics ID only needs to be set in the standalone version.
|
||||||
const accountData = await client.getAccountDataFromServer(
|
const accountData = await client.getAccountDataFromServer(
|
||||||
PosthogAnalytics.ANALYTICS_EVENT_TYPE
|
PosthogAnalytics.ANALYTICS_EVENT_TYPE,
|
||||||
);
|
);
|
||||||
await client.setAccountData(
|
await client.setAccountData(
|
||||||
PosthogAnalytics.ANALYTICS_EVENT_TYPE,
|
PosthogAnalytics.ANALYTICS_EVENT_TYPE,
|
||||||
Object.assign({ id: analyticsID }, accountData)
|
Object.assign({ id: analyticsID }, accountData),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -360,7 +360,7 @@ export class PosthogAnalytics {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async updateAnonymityAndIdentifyUser(
|
private async updateAnonymityAndIdentifyUser(
|
||||||
pseudonymousOptIn: boolean
|
pseudonymousOptIn: boolean,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
// Update this.anonymity based on the user's analytics opt-in settings
|
// Update this.anonymity based on the user's analytics opt-in settings
|
||||||
const anonymity = pseudonymousOptIn
|
const anonymity = pseudonymousOptIn
|
||||||
@@ -376,11 +376,11 @@ export class PosthogAnalytics {
|
|||||||
this.setRegistrationType(
|
this.setRegistrationType(
|
||||||
window.matrixclient.isGuest() || window.passwordlessUser
|
window.matrixclient.isGuest() || window.passwordlessUser
|
||||||
? RegistrationType.Guest
|
? RegistrationType.Guest
|
||||||
: RegistrationType.Registered
|
: RegistrationType.Registered,
|
||||||
);
|
);
|
||||||
// store the promise to await posthog-tracking-events until the identification is done.
|
// store the promise to await posthog-tracking-events until the identification is done.
|
||||||
this.identificationPromise = this.identifyUser(
|
this.identificationPromise = this.identifyUser(
|
||||||
PosthogAnalytics.getRandomAnalyticsId
|
PosthogAnalytics.getRandomAnalyticsId,
|
||||||
);
|
);
|
||||||
await this.identificationPromise;
|
await this.identificationPromise;
|
||||||
if (this.userRegisteredInThisSession()) {
|
if (this.userRegisteredInThisSession()) {
|
||||||
@@ -395,7 +395,7 @@ export class PosthogAnalytics {
|
|||||||
|
|
||||||
public async trackEvent<E extends IPosthogEvent>(
|
public async trackEvent<E extends IPosthogEvent>(
|
||||||
{ eventName, ...properties }: E,
|
{ eventName, ...properties }: E,
|
||||||
options?: CaptureOptions
|
options?: CaptureOptions,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
if (this.identificationPromise) {
|
if (this.identificationPromise) {
|
||||||
// only make calls to posthog after the identificaion is done
|
// only make calls to posthog after the identificaion is done
|
||||||
|
|||||||
@@ -43,14 +43,14 @@ export class CallEndedTracker {
|
|||||||
public cacheParticipantCountChanged(count: number): void {
|
public cacheParticipantCountChanged(count: number): void {
|
||||||
this.cache.maxParticipantsCount = Math.max(
|
this.cache.maxParticipantsCount = Math.max(
|
||||||
count,
|
count,
|
||||||
this.cache.maxParticipantsCount
|
this.cache.maxParticipantsCount,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public track(
|
public track(
|
||||||
callId: string,
|
callId: string,
|
||||||
callParticipantsNow: number,
|
callParticipantsNow: number,
|
||||||
sendInstantly: boolean
|
sendInstantly: boolean,
|
||||||
): void {
|
): void {
|
||||||
PosthogAnalytics.instance.trackEvent<CallEnded>(
|
PosthogAnalytics.instance.trackEvent<CallEnded>(
|
||||||
{
|
{
|
||||||
@@ -60,7 +60,7 @@ export class CallEndedTracker {
|
|||||||
callParticipantsOnLeave: callParticipantsNow,
|
callParticipantsOnLeave: callParticipantsNow,
|
||||||
callDuration: (Date.now() - this.cache.startTime.getTime()) / 1000,
|
callDuration: (Date.now() - this.cache.startTime.getTime()) / 1000,
|
||||||
},
|
},
|
||||||
{ send_instantly: sendInstantly }
|
{ send_instantly: sendInstantly },
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -148,7 +148,7 @@ export class PosthogSpanProcessor implements SpanProcessor {
|
|||||||
ratioPeerConnectionToDevices: ratioPeerConnectionToDevices,
|
ratioPeerConnectionToDevices: ratioPeerConnectionToDevices,
|
||||||
},
|
},
|
||||||
// Send instantly because the window might be closing
|
// Send instantly because the window might be closing
|
||||||
{ send_instantly: true }
|
{ send_instantly: true },
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ import {
|
|||||||
} from "@opentelemetry/sdk-trace-base";
|
} from "@opentelemetry/sdk-trace-base";
|
||||||
|
|
||||||
const dumpAttributes = (
|
const dumpAttributes = (
|
||||||
attr: Attributes
|
attr: Attributes,
|
||||||
): {
|
): {
|
||||||
key: string;
|
key: string;
|
||||||
type:
|
type:
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ limitations under the License.
|
|||||||
// Array.prototype.findLastIndex
|
// Array.prototype.findLastIndex
|
||||||
export function findLastIndex<T>(
|
export function findLastIndex<T>(
|
||||||
array: T[],
|
array: T[],
|
||||||
predicate: (item: T, index: number) => boolean
|
predicate: (item: T, index: number) => boolean,
|
||||||
): number | null {
|
): number | null {
|
||||||
for (let i = array.length - 1; i >= 0; i--) {
|
for (let i = array.length - 1; i >= 0; i--) {
|
||||||
if (predicate(array[i], i)) return i;
|
if (predicate(array[i], i)) return i;
|
||||||
@@ -36,9 +36,9 @@ export function findLastIndex<T>(
|
|||||||
*/
|
*/
|
||||||
export const count = <T>(
|
export const count = <T>(
|
||||||
array: T[],
|
array: T[],
|
||||||
predicate: (item: T, index: number) => boolean
|
predicate: (item: T, index: number) => boolean,
|
||||||
): number =>
|
): number =>
|
||||||
array.reduce(
|
array.reduce(
|
||||||
(acc, item, index) => (predicate(item, index) ? acc + 1 : acc),
|
(acc, item, index) => (predicate(item, index) ? acc + 1 : acc),
|
||||||
0
|
0,
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -80,7 +80,7 @@ export const LoginPage: FC = () => {
|
|||||||
setLoading(false);
|
setLoading(false);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
[login, location, history, homeserver, setClient]
|
[login, location, history, homeserver, setClient],
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -78,7 +78,7 @@ export const RegisterPage: FC = () => {
|
|||||||
password,
|
password,
|
||||||
userName,
|
userName,
|
||||||
recaptchaResponse,
|
recaptchaResponse,
|
||||||
passwordlessUser
|
passwordlessUser,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (client && client?.groupCallEventHandler && passwordlessUser) {
|
if (client && client?.groupCallEventHandler && passwordlessUser) {
|
||||||
@@ -135,7 +135,7 @@ export const RegisterPage: FC = () => {
|
|||||||
execute,
|
execute,
|
||||||
client,
|
client,
|
||||||
setClient,
|
setClient,
|
||||||
]
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|||||||
@@ -24,13 +24,13 @@ import { Session } from "../ClientContext";
|
|||||||
export function useInteractiveLogin(): (
|
export function useInteractiveLogin(): (
|
||||||
homeserver: string,
|
homeserver: string,
|
||||||
username: string,
|
username: string,
|
||||||
password: string
|
password: string,
|
||||||
) => Promise<[MatrixClient, Session]> {
|
) => Promise<[MatrixClient, Session]> {
|
||||||
return useCallback<
|
return useCallback<
|
||||||
(
|
(
|
||||||
homeserver: string,
|
homeserver: string,
|
||||||
username: string,
|
username: string,
|
||||||
password: string
|
password: string,
|
||||||
) => Promise<[MatrixClient, Session]>
|
) => Promise<[MatrixClient, Session]>
|
||||||
>(async (homeserver: string, username: string, password: string) => {
|
>(async (homeserver: string, username: string, password: string) => {
|
||||||
const authClient = createClient({ baseUrl: homeserver });
|
const authClient = createClient({ baseUrl: homeserver });
|
||||||
@@ -70,7 +70,7 @@ export function useInteractiveLogin(): (
|
|||||||
userId: user_id,
|
userId: user_id,
|
||||||
deviceId: device_id,
|
deviceId: device_id,
|
||||||
},
|
},
|
||||||
false
|
false,
|
||||||
);
|
);
|
||||||
/* eslint-enable camelcase */
|
/* eslint-enable camelcase */
|
||||||
return [client, session];
|
return [client, session];
|
||||||
|
|||||||
@@ -30,14 +30,14 @@ export const useInteractiveRegistration = (): {
|
|||||||
password: string,
|
password: string,
|
||||||
displayName: string,
|
displayName: string,
|
||||||
recaptchaResponse: string,
|
recaptchaResponse: string,
|
||||||
passwordlessUser: boolean
|
passwordlessUser: boolean,
|
||||||
) => Promise<[MatrixClient, Session]>;
|
) => Promise<[MatrixClient, Session]>;
|
||||||
} => {
|
} => {
|
||||||
const [privacyPolicyUrl, setPrivacyPolicyUrl] = useState<string | undefined>(
|
const [privacyPolicyUrl, setPrivacyPolicyUrl] = useState<string | undefined>(
|
||||||
undefined
|
undefined,
|
||||||
);
|
);
|
||||||
const [recaptchaKey, setRecaptchaKey] = useState<string | undefined>(
|
const [recaptchaKey, setRecaptchaKey] = useState<string | undefined>(
|
||||||
undefined
|
undefined,
|
||||||
);
|
);
|
||||||
|
|
||||||
const authClient = useRef<MatrixClient>();
|
const authClient = useRef<MatrixClient>();
|
||||||
@@ -50,7 +50,7 @@ export const useInteractiveRegistration = (): {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
authClient.current!.registerRequest({}).catch((error) => {
|
authClient.current!.registerRequest({}).catch((error) => {
|
||||||
setPrivacyPolicyUrl(
|
setPrivacyPolicyUrl(
|
||||||
error.data?.params["m.login.terms"]?.policies?.privacy_policy?.en?.url
|
error.data?.params["m.login.terms"]?.policies?.privacy_policy?.en?.url,
|
||||||
);
|
);
|
||||||
setRecaptchaKey(error.data?.params["m.login.recaptcha"]?.public_key);
|
setRecaptchaKey(error.data?.params["m.login.recaptcha"]?.public_key);
|
||||||
});
|
});
|
||||||
@@ -62,7 +62,7 @@ export const useInteractiveRegistration = (): {
|
|||||||
password: string,
|
password: string,
|
||||||
displayName: string,
|
displayName: string,
|
||||||
recaptchaResponse: string,
|
recaptchaResponse: string,
|
||||||
passwordlessUser: boolean
|
passwordlessUser: boolean,
|
||||||
): Promise<[MatrixClient, Session]> => {
|
): Promise<[MatrixClient, Session]> => {
|
||||||
const interactiveAuth = new InteractiveAuth({
|
const interactiveAuth = new InteractiveAuth({
|
||||||
matrixClient: authClient.current!,
|
matrixClient: authClient.current!,
|
||||||
@@ -106,7 +106,7 @@ export const useInteractiveRegistration = (): {
|
|||||||
userId: user_id,
|
userId: user_id,
|
||||||
deviceId: device_id,
|
deviceId: device_id,
|
||||||
},
|
},
|
||||||
false
|
false,
|
||||||
);
|
);
|
||||||
|
|
||||||
await client.setDisplayName(displayName);
|
await client.setDisplayName(displayName);
|
||||||
@@ -129,7 +129,7 @@ export const useInteractiveRegistration = (): {
|
|||||||
|
|
||||||
return [client, session];
|
return [client, session];
|
||||||
},
|
},
|
||||||
[]
|
[],
|
||||||
);
|
);
|
||||||
|
|
||||||
return { privacyPolicyUrl, recaptchaKey, register };
|
return { privacyPolicyUrl, recaptchaKey, register };
|
||||||
|
|||||||
@@ -108,7 +108,7 @@ export function useRecaptcha(sitekey?: string): {
|
|||||||
window.grecaptcha.execute();
|
window.grecaptcha.execute();
|
||||||
|
|
||||||
const iframe = document.querySelector<HTMLIFrameElement>(
|
const iframe = document.querySelector<HTMLIFrameElement>(
|
||||||
'iframe[src*="recaptcha/api2/bframe"]'
|
'iframe[src*="recaptcha/api2/bframe"]',
|
||||||
);
|
);
|
||||||
|
|
||||||
if (iframe?.parentNode?.parentNode) {
|
if (iframe?.parentNode?.parentNode) {
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ export function useRegisterPasswordlessUser(): UseRegisterPasswordlessUserType {
|
|||||||
randomString(16),
|
randomString(16),
|
||||||
displayName,
|
displayName,
|
||||||
recaptchaResponse,
|
recaptchaResponse,
|
||||||
true
|
true,
|
||||||
);
|
);
|
||||||
setClient({ client, session });
|
setClient({ client, session });
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@@ -56,7 +56,7 @@ export function useRegisterPasswordlessUser(): UseRegisterPasswordlessUserType {
|
|||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[execute, reset, register, setClient]
|
[execute, reset, register, setClient],
|
||||||
);
|
);
|
||||||
|
|
||||||
return { privacyPolicyUrl, registerPasswordlessUser, recaptchaId };
|
return { privacyPolicyUrl, registerPasswordlessUser, recaptchaId };
|
||||||
|
|||||||
@@ -146,7 +146,9 @@ limitations under the License.
|
|||||||
.copyButton {
|
.copyButton {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 40px;
|
height: 40px;
|
||||||
transition: border-color 250ms, background-color 250ms;
|
transition:
|
||||||
|
border-color 250ms,
|
||||||
|
background-color 250ms;
|
||||||
}
|
}
|
||||||
|
|
||||||
.copyButton span {
|
.copyButton span {
|
||||||
|
|||||||
@@ -94,12 +94,12 @@ export const Button = forwardRef<HTMLButtonElement, Props>(
|
|||||||
onPressStart,
|
onPressStart,
|
||||||
...rest
|
...rest
|
||||||
},
|
},
|
||||||
ref
|
ref,
|
||||||
) => {
|
) => {
|
||||||
const buttonRef = useObjectRef<HTMLButtonElement>(ref);
|
const buttonRef = useObjectRef<HTMLButtonElement>(ref);
|
||||||
const { buttonProps } = useButton(
|
const { buttonProps } = useButton(
|
||||||
{ onPress, onPressStart, ...rest },
|
{ onPress, onPressStart, ...rest },
|
||||||
buttonRef
|
buttonRef,
|
||||||
);
|
);
|
||||||
|
|
||||||
// TODO: react-aria's useButton hook prevents form submission via keyboard
|
// TODO: react-aria's useButton hook prevents form submission via keyboard
|
||||||
@@ -121,7 +121,7 @@ export const Button = forwardRef<HTMLButtonElement, Props>(
|
|||||||
{
|
{
|
||||||
[styles.on]: on,
|
[styles.on]: on,
|
||||||
[styles.off]: off,
|
[styles.off]: off,
|
||||||
}
|
},
|
||||||
)}
|
)}
|
||||||
{...mergeProps(rest, filteredButtonProps)}
|
{...mergeProps(rest, filteredButtonProps)}
|
||||||
ref={buttonRef}
|
ref={buttonRef}
|
||||||
@@ -132,7 +132,7 @@ export const Button = forwardRef<HTMLButtonElement, Props>(
|
|||||||
</>
|
</>
|
||||||
</button>
|
</button>
|
||||||
);
|
);
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
export const MicButton: FC<{
|
export const MicButton: FC<{
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ export const LinkButton: FC<Props> = ({
|
|||||||
className={classNames(
|
className={classNames(
|
||||||
variantToClassName[variant || "secondary"],
|
variantToClassName[variant || "secondary"],
|
||||||
size ? sizeToClassName[size] : [],
|
size ? sizeToClassName[size] : [],
|
||||||
className
|
className,
|
||||||
)}
|
)}
|
||||||
to={to}
|
to={to}
|
||||||
{...rest}
|
{...rest}
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ export class Config {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function downloadConfig(
|
async function downloadConfig(
|
||||||
configJsonFilename: string
|
configJsonFilename: string,
|
||||||
): Promise<ConfigOptions> {
|
): Promise<ConfigOptions> {
|
||||||
const url = new URL(configJsonFilename, window.location.href);
|
const url = new URL(configJsonFilename, window.location.href);
|
||||||
url.searchParams.set("cachebuster", Date.now().toString());
|
url.searchParams.set("cachebuster", Date.now().toString());
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ export const getRoomSharedKeyLocalStorageKey = (roomId: string): string =>
|
|||||||
`room-shared-key-${roomId}`;
|
`room-shared-key-${roomId}`;
|
||||||
|
|
||||||
const useInternalRoomSharedKey = (
|
const useInternalRoomSharedKey = (
|
||||||
roomId: string
|
roomId: string,
|
||||||
): [string | null, (value: string) => void] => {
|
): [string | null, (value: string) => void] => {
|
||||||
const key = useMemo(() => getRoomSharedKeyLocalStorageKey(roomId), [roomId]);
|
const key = useMemo(() => getRoomSharedKeyLocalStorageKey(roomId), [roomId]);
|
||||||
const [e2eeEnabled] = useEnableE2EE();
|
const [e2eeEnabled] = useEnableE2EE();
|
||||||
@@ -68,6 +68,6 @@ export const useIsRoomE2EE = (roomId: string): boolean | null => {
|
|||||||
// should inspect the e2eEnabled URL parameter here?
|
// should inspect the e2eEnabled URL parameter here?
|
||||||
return useMemo(
|
return useMemo(
|
||||||
() => widget === null && (room === null || !room.getCanonicalAlias()),
|
() => widget === null && (room === null || !room.getCanonicalAlias()),
|
||||||
[room]
|
[room],
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -36,5 +36,5 @@ export const Form = forwardRef<HTMLFormElement, FormProps>(
|
|||||||
{children}
|
{children}
|
||||||
</form>
|
</form>
|
||||||
);
|
);
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ const CallTile: FC<CallTileProps> = ({ name, avatarUrl, room }) => {
|
|||||||
to={getRelativeRoomUrl(
|
to={getRelativeRoomUrl(
|
||||||
room.roomId,
|
room.roomId,
|
||||||
room.name,
|
room.name,
|
||||||
roomSharedKey ?? undefined
|
roomSharedKey ?? undefined,
|
||||||
)}
|
)}
|
||||||
className={styles.callTileLink}
|
className={styles.callTileLink}
|
||||||
>
|
>
|
||||||
@@ -92,7 +92,7 @@ const CallTile: FC<CallTileProps> = ({ name, avatarUrl, room }) => {
|
|||||||
value={getAbsoluteRoomUrl(
|
value={getAbsoluteRoomUrl(
|
||||||
room.roomId,
|
room.roomId,
|
||||||
room.name,
|
room.name,
|
||||||
roomSharedKey ?? undefined
|
roomSharedKey ?? undefined,
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ export const RegisteredView: FC<Props> = ({ client }) => {
|
|||||||
useState(false);
|
useState(false);
|
||||||
const onDismissJoinExistingCallModal = useCallback(
|
const onDismissJoinExistingCallModal = useCallback(
|
||||||
() => setJoinExistingCallModalOpen(false),
|
() => setJoinExistingCallModalOpen(false),
|
||||||
[setJoinExistingCallModalOpen]
|
[setJoinExistingCallModalOpen],
|
||||||
);
|
);
|
||||||
const [e2eeEnabled] = useEnableE2EE();
|
const [e2eeEnabled] = useEnableE2EE();
|
||||||
|
|
||||||
@@ -77,15 +77,15 @@ export const RegisteredView: FC<Props> = ({ client }) => {
|
|||||||
const createRoomResult = await createRoom(
|
const createRoomResult = await createRoom(
|
||||||
client,
|
client,
|
||||||
roomName,
|
roomName,
|
||||||
e2eeEnabled ?? false
|
e2eeEnabled ?? false,
|
||||||
);
|
);
|
||||||
|
|
||||||
history.push(
|
history.push(
|
||||||
getRelativeRoomUrl(
|
getRelativeRoomUrl(
|
||||||
createRoomResult.roomId,
|
createRoomResult.roomId,
|
||||||
roomName,
|
roomName,
|
||||||
createRoomResult.password
|
createRoomResult.password,
|
||||||
)
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -102,7 +102,7 @@ export const RegisteredView: FC<Props> = ({ client }) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
[client, history, setJoinExistingCallModalOpen, e2eeEnabled]
|
[client, history, setJoinExistingCallModalOpen, e2eeEnabled],
|
||||||
);
|
);
|
||||||
|
|
||||||
const recentRooms = useGroupCallRooms(client);
|
const recentRooms = useGroupCallRooms(client);
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ export const UnauthenticatedView: FC = () => {
|
|||||||
useState(false);
|
useState(false);
|
||||||
const onDismissJoinExistingCallModal = useCallback(
|
const onDismissJoinExistingCallModal = useCallback(
|
||||||
() => setJoinExistingCallModalOpen(false),
|
() => setJoinExistingCallModalOpen(false),
|
||||||
[setJoinExistingCallModalOpen]
|
[setJoinExistingCallModalOpen],
|
||||||
);
|
);
|
||||||
const [onFinished, setOnFinished] = useState<() => void>();
|
const [onFinished, setOnFinished] = useState<() => void>();
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
@@ -82,7 +82,7 @@ export const UnauthenticatedView: FC = () => {
|
|||||||
randomString(16),
|
randomString(16),
|
||||||
displayName,
|
displayName,
|
||||||
recaptchaResponse,
|
recaptchaResponse,
|
||||||
true
|
true,
|
||||||
);
|
);
|
||||||
|
|
||||||
let createRoomResult;
|
let createRoomResult;
|
||||||
@@ -90,7 +90,7 @@ export const UnauthenticatedView: FC = () => {
|
|||||||
createRoomResult = await createRoom(
|
createRoomResult = await createRoom(
|
||||||
client,
|
client,
|
||||||
roomName,
|
roomName,
|
||||||
e2eeEnabled ?? false
|
e2eeEnabled ?? false,
|
||||||
);
|
);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (!setClient) {
|
if (!setClient) {
|
||||||
@@ -124,8 +124,8 @@ export const UnauthenticatedView: FC = () => {
|
|||||||
getRelativeRoomUrl(
|
getRelativeRoomUrl(
|
||||||
createRoomResult.roomId,
|
createRoomResult.roomId,
|
||||||
roomName,
|
roomName,
|
||||||
createRoomResult.password
|
createRoomResult.password,
|
||||||
)
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -144,7 +144,7 @@ export const UnauthenticatedView: FC = () => {
|
|||||||
setJoinExistingCallModalOpen,
|
setJoinExistingCallModalOpen,
|
||||||
setClient,
|
setClient,
|
||||||
e2eeEnabled,
|
e2eeEnabled,
|
||||||
]
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ function getLastTs(client: MatrixClient, r: Room): number {
|
|||||||
if (r.getMyMembership() !== "join") {
|
if (r.getMyMembership() !== "join") {
|
||||||
const membershipEvent = r.currentState.getStateEvents(
|
const membershipEvent = r.currentState.getStateEvents(
|
||||||
"m.room.member",
|
"m.room.member",
|
||||||
myUserId
|
myUserId,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (membershipEvent && !Array.isArray(membershipEvent)) {
|
if (membershipEvent && !Array.isArray(membershipEvent)) {
|
||||||
@@ -115,7 +115,7 @@ export function useGroupCallRooms(client: MatrixClient): GroupCallRoom[] {
|
|||||||
client.removeListener(GroupCallEventHandlerEvent.Incoming, updateRooms);
|
client.removeListener(GroupCallEventHandlerEvent.Incoming, updateRooms);
|
||||||
client.removeListener(
|
client.removeListener(
|
||||||
GroupCallEventHandlerEvent.Participants,
|
GroupCallEventHandlerEvent.Participants,
|
||||||
updateRooms
|
updateRooms,
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
}, [client]);
|
}, [client]);
|
||||||
|
|||||||
@@ -68,7 +68,8 @@ limitations under the License.
|
|||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
font-display: swap;
|
font-display: swap;
|
||||||
unicode-range: var(--inter-unicode-range);
|
unicode-range: var(--inter-unicode-range);
|
||||||
src: url("/fonts/Inter/Inter-Regular.woff2") format("woff2"),
|
src:
|
||||||
|
url("/fonts/Inter/Inter-Regular.woff2") format("woff2"),
|
||||||
url("/fonts/Inter/Inter-Regular.woff") format("woff");
|
url("/fonts/Inter/Inter-Regular.woff") format("woff");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -78,7 +79,8 @@ limitations under the License.
|
|||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
font-display: swap;
|
font-display: swap;
|
||||||
unicode-range: var(--inter-unicode-range);
|
unicode-range: var(--inter-unicode-range);
|
||||||
src: url("/fonts/Inter/Inter-Italic.woff2") format("woff2"),
|
src:
|
||||||
|
url("/fonts/Inter/Inter-Italic.woff2") format("woff2"),
|
||||||
url("/fonts/Inter/Inter-Italic.woff") format("woff");
|
url("/fonts/Inter/Inter-Italic.woff") format("woff");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -88,7 +90,8 @@ limitations under the License.
|
|||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
font-display: swap;
|
font-display: swap;
|
||||||
unicode-range: var(--inter-unicode-range);
|
unicode-range: var(--inter-unicode-range);
|
||||||
src: url("/fonts/Inter/Inter-Medium.woff2") format("woff2"),
|
src:
|
||||||
|
url("/fonts/Inter/Inter-Medium.woff2") format("woff2"),
|
||||||
url("/fonts/Inter/Inter-Medium.woff") format("woff");
|
url("/fonts/Inter/Inter-Medium.woff") format("woff");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -98,7 +101,8 @@ limitations under the License.
|
|||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
font-display: swap;
|
font-display: swap;
|
||||||
unicode-range: var(--inter-unicode-range);
|
unicode-range: var(--inter-unicode-range);
|
||||||
src: url("/fonts/Inter/Inter-MediumItalic.woff2") format("woff2"),
|
src:
|
||||||
|
url("/fonts/Inter/Inter-MediumItalic.woff2") format("woff2"),
|
||||||
url("/fonts/Inter/Inter-MediumItalic.woff") format("woff");
|
url("/fonts/Inter/Inter-MediumItalic.woff") format("woff");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -108,7 +112,8 @@ limitations under the License.
|
|||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
font-display: swap;
|
font-display: swap;
|
||||||
unicode-range: var(--inter-unicode-range);
|
unicode-range: var(--inter-unicode-range);
|
||||||
src: url("/fonts/Inter/Inter-SemiBold.woff2") format("woff2"),
|
src:
|
||||||
|
url("/fonts/Inter/Inter-SemiBold.woff2") format("woff2"),
|
||||||
url("/fonts/Inter/Inter-SemiBold.woff") format("woff");
|
url("/fonts/Inter/Inter-SemiBold.woff") format("woff");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -118,7 +123,8 @@ limitations under the License.
|
|||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
font-display: swap;
|
font-display: swap;
|
||||||
unicode-range: var(--inter-unicode-range);
|
unicode-range: var(--inter-unicode-range);
|
||||||
src: url("/fonts/Inter/Inter-SemiBoldItalic.woff2") format("woff2"),
|
src:
|
||||||
|
url("/fonts/Inter/Inter-SemiBoldItalic.woff2") format("woff2"),
|
||||||
url("/fonts/Inter/Inter-SemiBoldItalic.woff") format("woff");
|
url("/fonts/Inter/Inter-SemiBoldItalic.woff") format("woff");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -128,7 +134,8 @@ limitations under the License.
|
|||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
font-display: swap;
|
font-display: swap;
|
||||||
unicode-range: var(--inter-unicode-range);
|
unicode-range: var(--inter-unicode-range);
|
||||||
src: url("/fonts/Inter/Inter-Bold.woff2") format("woff2"),
|
src:
|
||||||
|
url("/fonts/Inter/Inter-Bold.woff2") format("woff2"),
|
||||||
url("/fonts/Inter/Inter-Bold.woff") format("woff");
|
url("/fonts/Inter/Inter-Bold.woff") format("woff");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -138,7 +145,8 @@ limitations under the License.
|
|||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
font-display: swap;
|
font-display: swap;
|
||||||
unicode-range: var(--inter-unicode-range);
|
unicode-range: var(--inter-unicode-range);
|
||||||
src: url("/fonts/Inter/Inter-BoldItalic.woff2") format("woff2"),
|
src:
|
||||||
|
url("/fonts/Inter/Inter-BoldItalic.woff2") format("woff2"),
|
||||||
url("/fonts/Inter/Inter-BoldItalic.woff") format("woff");
|
url("/fonts/Inter/Inter-BoldItalic.woff") format("woff");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -99,13 +99,13 @@ export class Initializer {
|
|||||||
if (fontScale !== null) {
|
if (fontScale !== null) {
|
||||||
document.documentElement.style.setProperty(
|
document.documentElement.style.setProperty(
|
||||||
"--font-scale",
|
"--font-scale",
|
||||||
fontScale.toString()
|
fontScale.toString(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (fonts.length > 0) {
|
if (fonts.length > 0) {
|
||||||
document.documentElement.style.setProperty(
|
document.documentElement.style.setProperty(
|
||||||
"--font-family",
|
"--font-family",
|
||||||
fonts.map((f) => `"${f}"`).join(", ")
|
fonts.map((f) => `"${f}"`).join(", "),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ export const AvatarInputField = forwardRef<HTMLInputElement, Props>(
|
|||||||
onRemoveAvatar,
|
onRemoveAvatar,
|
||||||
...rest
|
...rest
|
||||||
},
|
},
|
||||||
ref
|
ref,
|
||||||
) => {
|
) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
@@ -120,5 +120,5 @@ export const AvatarInputField = forwardRef<HTMLInputElement, Props>(
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -85,8 +85,11 @@ limitations under the License.
|
|||||||
}
|
}
|
||||||
|
|
||||||
.inputField label {
|
.inputField label {
|
||||||
transition: font-size 0.25s ease-out 0.1s, color 0.25s ease-out 0.1s,
|
transition:
|
||||||
top 0.25s ease-out 0.1s, background-color 0.25s ease-out 0.1s;
|
font-size 0.25s ease-out 0.1s,
|
||||||
|
color 0.25s ease-out 0.1s,
|
||||||
|
top 0.25s ease-out 0.1s,
|
||||||
|
background-color 0.25s ease-out 0.1s;
|
||||||
color: var(--cpd-color-text-secondary);
|
color: var(--cpd-color-text-secondary);
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
font-size: var(--font-size-body);
|
font-size: var(--font-size-body);
|
||||||
@@ -118,8 +121,11 @@ limitations under the License.
|
|||||||
.inputField textarea:not(:placeholder-shown) + label,
|
.inputField textarea:not(:placeholder-shown) + label,
|
||||||
.inputField.prefix textarea + label {
|
.inputField.prefix textarea + label {
|
||||||
background-color: var(--cpd-color-bg-canvas-default);
|
background-color: var(--cpd-color-bg-canvas-default);
|
||||||
transition: font-size 0.25s ease-out 0s, color 0.25s ease-out 0s,
|
transition:
|
||||||
top 0.25s ease-out 0s, background-color 0.25s ease-out 0s;
|
font-size 0.25s ease-out 0s,
|
||||||
|
color 0.25s ease-out 0s,
|
||||||
|
top 0.25s ease-out 0s,
|
||||||
|
background-color 0.25s ease-out 0s;
|
||||||
font-size: var(--font-size-micro);
|
font-size: var(--font-size-micro);
|
||||||
top: -13px;
|
top: -13px;
|
||||||
padding: 0 2px;
|
padding: 0 2px;
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ export function FieldRow({
|
|||||||
className={classNames(
|
className={classNames(
|
||||||
styles.fieldRow,
|
styles.fieldRow,
|
||||||
{ [styles.rightAlign]: rightAlign },
|
{ [styles.rightAlign]: rightAlign },
|
||||||
className
|
className,
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
@@ -102,7 +102,7 @@ export const InputField = forwardRef<
|
|||||||
disabled,
|
disabled,
|
||||||
...rest
|
...rest
|
||||||
},
|
},
|
||||||
ref
|
ref,
|
||||||
) => {
|
) => {
|
||||||
const descriptionId = useId();
|
const descriptionId = useId();
|
||||||
|
|
||||||
@@ -114,7 +114,7 @@ export const InputField = forwardRef<
|
|||||||
[styles.prefix]: !!prefix,
|
[styles.prefix]: !!prefix,
|
||||||
[styles.disabled]: disabled,
|
[styles.disabled]: disabled,
|
||||||
},
|
},
|
||||||
className
|
className,
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{prefix && <span>{prefix}</span>}
|
{prefix && <span>{prefix}</span>}
|
||||||
@@ -163,7 +163,7 @@ export const InputField = forwardRef<
|
|||||||
)}
|
)}
|
||||||
</Field>
|
</Field>
|
||||||
);
|
);
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
interface ErrorMessageProps {
|
interface ErrorMessageProps {
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ export function SelectInput(props: Props): JSX.Element {
|
|||||||
const { labelProps, triggerProps, valueProps, menuProps } = useSelect(
|
const { labelProps, triggerProps, valueProps, menuProps } = useSelect(
|
||||||
props,
|
props,
|
||||||
state,
|
state,
|
||||||
ref
|
ref,
|
||||||
);
|
);
|
||||||
|
|
||||||
const { buttonProps } = useButton(triggerProps, ref);
|
const { buttonProps } = useButton(triggerProps, ref);
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ export interface MediaDevices {
|
|||||||
// Cargo-culted from @livekit/components-react
|
// Cargo-culted from @livekit/components-react
|
||||||
function useObservableState<T>(
|
function useObservableState<T>(
|
||||||
observable: Observable<T> | undefined,
|
observable: Observable<T> | undefined,
|
||||||
startWith: T
|
startWith: T,
|
||||||
): T {
|
): T {
|
||||||
const [state, setState] = useState<T>(startWith);
|
const [state, setState] = useState<T>(startWith);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -67,7 +67,7 @@ function useMediaDevice(
|
|||||||
kind: MediaDeviceKind,
|
kind: MediaDeviceKind,
|
||||||
fallbackDevice: string | undefined,
|
fallbackDevice: string | undefined,
|
||||||
usingNames: boolean,
|
usingNames: boolean,
|
||||||
alwaysDefault: boolean = false
|
alwaysDefault: boolean = false,
|
||||||
): MediaDevice {
|
): MediaDevice {
|
||||||
// Make sure we don't needlessly reset to a device observer without names,
|
// Make sure we don't needlessly reset to a device observer without names,
|
||||||
// once permissions are already given
|
// once permissions are already given
|
||||||
@@ -83,7 +83,7 @@ function useMediaDevice(
|
|||||||
// kind, which then results in multiple permissions requests.
|
// kind, which then results in multiple permissions requests.
|
||||||
const deviceObserver = useMemo(
|
const deviceObserver = useMemo(
|
||||||
() => createMediaDeviceObserver(kind, requestPermissions),
|
() => createMediaDeviceObserver(kind, requestPermissions),
|
||||||
[kind, requestPermissions]
|
[kind, requestPermissions],
|
||||||
);
|
);
|
||||||
const available = useObservableState(deviceObserver, []);
|
const available = useObservableState(deviceObserver, []);
|
||||||
const [selectedId, select] = useState(fallbackDevice);
|
const [selectedId, select] = useState(fallbackDevice);
|
||||||
@@ -143,18 +143,18 @@ export const MediaDevicesProvider: FC<Props> = ({ children }) => {
|
|||||||
const audioInput = useMediaDevice(
|
const audioInput = useMediaDevice(
|
||||||
"audioinput",
|
"audioinput",
|
||||||
audioInputSetting,
|
audioInputSetting,
|
||||||
usingNames
|
usingNames,
|
||||||
);
|
);
|
||||||
const audioOutput = useMediaDevice(
|
const audioOutput = useMediaDevice(
|
||||||
"audiooutput",
|
"audiooutput",
|
||||||
audioOutputSetting,
|
audioOutputSetting,
|
||||||
useOutputNames,
|
useOutputNames,
|
||||||
alwaysUseDefaultAudio
|
alwaysUseDefaultAudio,
|
||||||
);
|
);
|
||||||
const videoInput = useMediaDevice(
|
const videoInput = useMediaDevice(
|
||||||
"videoinput",
|
"videoinput",
|
||||||
videoInputSetting,
|
videoInputSetting,
|
||||||
usingNames
|
usingNames,
|
||||||
);
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -176,11 +176,11 @@ export const MediaDevicesProvider: FC<Props> = ({ children }) => {
|
|||||||
|
|
||||||
const startUsingDeviceNames = useCallback(
|
const startUsingDeviceNames = useCallback(
|
||||||
() => setNumCallersUsingNames((n) => n + 1),
|
() => setNumCallersUsingNames((n) => n + 1),
|
||||||
[setNumCallersUsingNames]
|
[setNumCallersUsingNames],
|
||||||
);
|
);
|
||||||
const stopUsingDeviceNames = useCallback(
|
const stopUsingDeviceNames = useCallback(
|
||||||
() => setNumCallersUsingNames((n) => n - 1),
|
() => setNumCallersUsingNames((n) => n - 1),
|
||||||
[setNumCallersUsingNames]
|
[setNumCallersUsingNames],
|
||||||
);
|
);
|
||||||
|
|
||||||
const context: MediaDevices = useMemo(
|
const context: MediaDevices = useMemo(
|
||||||
@@ -197,7 +197,7 @@ export const MediaDevicesProvider: FC<Props> = ({ children }) => {
|
|||||||
videoInput,
|
videoInput,
|
||||||
startUsingDeviceNames,
|
startUsingDeviceNames,
|
||||||
stopUsingDeviceNames,
|
stopUsingDeviceNames,
|
||||||
]
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -218,7 +218,7 @@ export const useMediaDevices = (): MediaDevices =>
|
|||||||
*/
|
*/
|
||||||
export const useMediaDeviceNames = (
|
export const useMediaDeviceNames = (
|
||||||
context: MediaDevices,
|
context: MediaDevices,
|
||||||
enabled = true
|
enabled = true,
|
||||||
): void =>
|
): void =>
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (enabled) {
|
if (enabled) {
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ export type OpenIDClientParts = Pick<
|
|||||||
|
|
||||||
export function useOpenIDSFU(
|
export function useOpenIDSFU(
|
||||||
client: OpenIDClientParts,
|
client: OpenIDClientParts,
|
||||||
rtcSession: MatrixRTCSession
|
rtcSession: MatrixRTCSession,
|
||||||
): SFUConfig | undefined {
|
): SFUConfig | undefined {
|
||||||
const [sfuConfig, setSFUConfig] = useState<SFUConfig | undefined>(undefined);
|
const [sfuConfig, setSFUConfig] = useState<SFUConfig | undefined>(undefined);
|
||||||
|
|
||||||
@@ -62,20 +62,20 @@ export function useOpenIDSFU(
|
|||||||
|
|
||||||
export async function getSFUConfigWithOpenID(
|
export async function getSFUConfigWithOpenID(
|
||||||
client: OpenIDClientParts,
|
client: OpenIDClientParts,
|
||||||
activeFocus: LivekitFocus
|
activeFocus: LivekitFocus,
|
||||||
): Promise<SFUConfig | undefined> {
|
): Promise<SFUConfig | undefined> {
|
||||||
const openIdToken = await client.getOpenIdToken();
|
const openIdToken = await client.getOpenIdToken();
|
||||||
logger.debug("Got openID token", openIdToken);
|
logger.debug("Got openID token", openIdToken);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
logger.info(
|
logger.info(
|
||||||
`Trying to get JWT from call's active focus URL of ${activeFocus.livekit_service_url}...`
|
`Trying to get JWT from call's active focus URL of ${activeFocus.livekit_service_url}...`,
|
||||||
);
|
);
|
||||||
const sfuConfig = await getLiveKitJWT(
|
const sfuConfig = await getLiveKitJWT(
|
||||||
client,
|
client,
|
||||||
activeFocus.livekit_service_url,
|
activeFocus.livekit_service_url,
|
||||||
activeFocus.livekit_alias,
|
activeFocus.livekit_alias,
|
||||||
openIdToken
|
openIdToken,
|
||||||
);
|
);
|
||||||
logger.info(`Got JWT from call's active focus URL.`);
|
logger.info(`Got JWT from call's active focus URL.`);
|
||||||
|
|
||||||
@@ -83,7 +83,7 @@ export async function getSFUConfigWithOpenID(
|
|||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger.warn(
|
logger.warn(
|
||||||
`Failed to get JWT from RTC session's active focus URL of ${activeFocus.livekit_service_url}.`,
|
`Failed to get JWT from RTC session's active focus URL of ${activeFocus.livekit_service_url}.`,
|
||||||
e
|
e,
|
||||||
);
|
);
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
@@ -93,7 +93,7 @@ async function getLiveKitJWT(
|
|||||||
client: OpenIDClientParts,
|
client: OpenIDClientParts,
|
||||||
livekitServiceURL: string,
|
livekitServiceURL: string,
|
||||||
roomName: string,
|
roomName: string,
|
||||||
openIDToken: IOpenIDToken
|
openIDToken: IOpenIDToken,
|
||||||
): Promise<SFUConfig> {
|
): Promise<SFUConfig> {
|
||||||
try {
|
try {
|
||||||
const res = await fetch(livekitServiceURL + "/sfu/get", {
|
const res = await fetch(livekitServiceURL + "/sfu/get", {
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ async function doConnect(
|
|||||||
livekitRoom: Room,
|
livekitRoom: Room,
|
||||||
sfuConfig: SFUConfig,
|
sfuConfig: SFUConfig,
|
||||||
audioEnabled: boolean,
|
audioEnabled: boolean,
|
||||||
audioOptions: AudioCaptureOptions
|
audioOptions: AudioCaptureOptions,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
await livekitRoom!.connect(sfuConfig!.url, sfuConfig!.jwt);
|
await livekitRoom!.connect(sfuConfig!.url, sfuConfig!.jwt);
|
||||||
|
|
||||||
@@ -76,12 +76,12 @@ export function useECConnectionState(
|
|||||||
initialAudioOptions: AudioCaptureOptions,
|
initialAudioOptions: AudioCaptureOptions,
|
||||||
initialAudioEnabled: boolean,
|
initialAudioEnabled: boolean,
|
||||||
livekitRoom?: Room,
|
livekitRoom?: Room,
|
||||||
sfuConfig?: SFUConfig
|
sfuConfig?: SFUConfig,
|
||||||
): ECConnectionState {
|
): ECConnectionState {
|
||||||
const [connState, setConnState] = useState(
|
const [connState, setConnState] = useState(
|
||||||
sfuConfig && livekitRoom
|
sfuConfig && livekitRoom
|
||||||
? livekitRoom.state
|
? livekitRoom.state
|
||||||
: ECAddonConnectionState.ECWaiting
|
: ECAddonConnectionState.ECWaiting,
|
||||||
);
|
);
|
||||||
|
|
||||||
const [isSwitchingFocus, setSwitchingFocus] = useState(false);
|
const [isSwitchingFocus, setSwitchingFocus] = useState(false);
|
||||||
@@ -116,7 +116,7 @@ export function useECConnectionState(
|
|||||||
!sfuConfigEquals(currentSFUConfig.current, sfuConfig)
|
!sfuConfigEquals(currentSFUConfig.current, sfuConfig)
|
||||||
) {
|
) {
|
||||||
logger.info(
|
logger.info(
|
||||||
`SFU config changed! URL was ${currentSFUConfig.current?.url} now ${sfuConfig?.url}`
|
`SFU config changed! URL was ${currentSFUConfig.current?.url} now ${sfuConfig?.url}`,
|
||||||
);
|
);
|
||||||
|
|
||||||
(async (): Promise<void> => {
|
(async (): Promise<void> => {
|
||||||
@@ -128,7 +128,7 @@ export function useECConnectionState(
|
|||||||
livekitRoom!,
|
livekitRoom!,
|
||||||
sfuConfig!,
|
sfuConfig!,
|
||||||
initialAudioEnabled,
|
initialAudioEnabled,
|
||||||
initialAudioOptions
|
initialAudioOptions,
|
||||||
);
|
);
|
||||||
} finally {
|
} finally {
|
||||||
setIsInDoConnect(false);
|
setIsInDoConnect(false);
|
||||||
@@ -149,7 +149,7 @@ export function useECConnectionState(
|
|||||||
livekitRoom!,
|
livekitRoom!,
|
||||||
sfuConfig!,
|
sfuConfig!,
|
||||||
initialAudioEnabled,
|
initialAudioEnabled,
|
||||||
initialAudioOptions
|
initialAudioOptions,
|
||||||
).finally(() => setIsInDoConnect(false));
|
).finally(() => setIsInDoConnect(false));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ interface UseLivekitResult {
|
|||||||
export function useLiveKit(
|
export function useLiveKit(
|
||||||
muteStates: MuteStates,
|
muteStates: MuteStates,
|
||||||
sfuConfig?: SFUConfig,
|
sfuConfig?: SFUConfig,
|
||||||
e2eeConfig?: E2EEConfig
|
e2eeConfig?: E2EEConfig,
|
||||||
): UseLivekitResult {
|
): UseLivekitResult {
|
||||||
const e2eeOptions = useMemo(() => {
|
const e2eeOptions = useMemo(() => {
|
||||||
if (!e2eeConfig?.sharedKey) return undefined;
|
if (!e2eeConfig?.sharedKey) return undefined;
|
||||||
@@ -67,7 +67,7 @@ export function useLiveKit(
|
|||||||
if (!e2eeConfig?.sharedKey || !e2eeOptions) return;
|
if (!e2eeConfig?.sharedKey || !e2eeOptions) return;
|
||||||
|
|
||||||
(e2eeOptions.keyProvider as ExternalE2EEKeyProvider).setKey(
|
(e2eeOptions.keyProvider as ExternalE2EEKeyProvider).setKey(
|
||||||
e2eeConfig?.sharedKey
|
e2eeConfig?.sharedKey,
|
||||||
);
|
);
|
||||||
}, [e2eeOptions, e2eeConfig?.sharedKey]);
|
}, [e2eeOptions, e2eeConfig?.sharedKey]);
|
||||||
|
|
||||||
@@ -93,7 +93,7 @@ export function useLiveKit(
|
|||||||
},
|
},
|
||||||
e2ee: e2eeOptions,
|
e2ee: e2eeOptions,
|
||||||
}),
|
}),
|
||||||
[e2eeOptions]
|
[e2eeOptions],
|
||||||
);
|
);
|
||||||
|
|
||||||
// useECConnectionState creates and publishes an audio track by hand. To keep
|
// useECConnectionState creates and publishes an audio track by hand. To keep
|
||||||
@@ -131,7 +131,7 @@ export function useLiveKit(
|
|||||||
},
|
},
|
||||||
initialMuteStates.current.audio.enabled,
|
initialMuteStates.current.audio.enabled,
|
||||||
room,
|
room,
|
||||||
sfuConfig
|
sfuConfig,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Unblock audio once the connection is finished
|
// Unblock audio once the connection is finished
|
||||||
@@ -215,11 +215,11 @@ export function useLiveKit(
|
|||||||
room.options.audioCaptureDefaults?.deviceId === "default"
|
room.options.audioCaptureDefaults?.deviceId === "default"
|
||||||
) {
|
) {
|
||||||
const activeMicTrack = Array.from(
|
const activeMicTrack = Array.from(
|
||||||
room.localParticipant.audioTracks.values()
|
room.localParticipant.audioTracks.values(),
|
||||||
).find((d) => d.source === Track.Source.Microphone)?.track;
|
).find((d) => d.source === Track.Source.Microphone)?.track;
|
||||||
|
|
||||||
const defaultDevice = device.available.find(
|
const defaultDevice = device.available.find(
|
||||||
(d) => d.deviceId === "default"
|
(d) => d.deviceId === "default",
|
||||||
);
|
);
|
||||||
if (
|
if (
|
||||||
defaultDevice &&
|
defaultDevice &&
|
||||||
@@ -245,7 +245,7 @@ export function useLiveKit(
|
|||||||
room
|
room
|
||||||
.switchActiveDevice(kind, id)
|
.switchActiveDevice(kind, id)
|
||||||
.catch((e) =>
|
.catch((e) =>
|
||||||
logger.error(`Failed to sync ${kind} device with LiveKit`, e)
|
logger.error(`Failed to sync ${kind} device with LiveKit`, e),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ if (!window.isSecureContext) {
|
|||||||
fatalError = new Error(
|
fatalError = new Error(
|
||||||
"This app cannot run in an insecure context. To fix this, access the app " +
|
"This app cannot run in an insecure context. To fix this, access the app " +
|
||||||
"via a local loopback address, or serve it over HTTPS.\n" +
|
"via a local loopback address, or serve it over HTTPS.\n" +
|
||||||
"https://developer.mozilla.org/en-US/docs/Web/Security/Secure_Contexts"
|
"https://developer.mozilla.org/en-US/docs/Web/Security/Secure_Contexts",
|
||||||
);
|
);
|
||||||
} else if (!navigator.mediaDevices) {
|
} else if (!navigator.mediaDevices) {
|
||||||
fatalError = new Error("Your browser does not support WebRTC.");
|
fatalError = new Error("Your browser does not support WebRTC.");
|
||||||
@@ -66,5 +66,5 @@ const history = createBrowserHistory();
|
|||||||
root.render(
|
root.render(
|
||||||
<StrictMode>
|
<StrictMode>
|
||||||
<App history={history} />
|
<App history={history} />
|
||||||
</StrictMode>
|
</StrictMode>,
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ function waitForSync(client: MatrixClient): Promise<void> {
|
|||||||
const onSync = (
|
const onSync = (
|
||||||
state: SyncState,
|
state: SyncState,
|
||||||
_old: SyncState | null,
|
_old: SyncState | null,
|
||||||
data?: ISyncStateData
|
data?: ISyncStateData,
|
||||||
): void => {
|
): void => {
|
||||||
if (state === "PREPARED") {
|
if (state === "PREPARED") {
|
||||||
client.removeListener(ClientEvent.Sync, onSync);
|
client.removeListener(ClientEvent.Sync, onSync);
|
||||||
@@ -83,7 +83,7 @@ function secureRandomString(entropyBytes: number): string {
|
|||||||
// yet) so just use the built-in one and convert, replace the chars and strip the
|
// yet) so just use the built-in one and convert, replace the chars and strip the
|
||||||
// padding from the end (otherwise we'd need to pull in another dependency).
|
// padding from the end (otherwise we'd need to pull in another dependency).
|
||||||
return btoa(
|
return btoa(
|
||||||
key.reduce((acc, current) => acc + String.fromCharCode(current), "")
|
key.reduce((acc, current) => acc + String.fromCharCode(current), ""),
|
||||||
)
|
)
|
||||||
.replace("+", "-")
|
.replace("+", "-")
|
||||||
.replace("/", "_")
|
.replace("/", "_")
|
||||||
@@ -101,7 +101,7 @@ function secureRandomString(entropyBytes: number): string {
|
|||||||
*/
|
*/
|
||||||
export async function initClient(
|
export async function initClient(
|
||||||
clientOptions: ICreateClientOpts,
|
clientOptions: ICreateClientOpts,
|
||||||
restore: boolean
|
restore: boolean,
|
||||||
): Promise<MatrixClient> {
|
): Promise<MatrixClient> {
|
||||||
await loadOlm();
|
await loadOlm();
|
||||||
|
|
||||||
@@ -148,7 +148,7 @@ export async function initClient(
|
|||||||
if (indexedDB) {
|
if (indexedDB) {
|
||||||
const cryptoStoreExists = await IndexedDBCryptoStore.exists(
|
const cryptoStoreExists = await IndexedDBCryptoStore.exists(
|
||||||
indexedDB,
|
indexedDB,
|
||||||
CRYPTO_STORE_NAME
|
CRYPTO_STORE_NAME,
|
||||||
);
|
);
|
||||||
if (!cryptoStoreExists) throw new CryptoStoreIntegrityError();
|
if (!cryptoStoreExists) throw new CryptoStoreIntegrityError();
|
||||||
} else if (localStorage) {
|
} else if (localStorage) {
|
||||||
@@ -164,7 +164,7 @@ export async function initClient(
|
|||||||
if (indexedDB) {
|
if (indexedDB) {
|
||||||
baseOpts.cryptoStore = new IndexedDBCryptoStore(
|
baseOpts.cryptoStore = new IndexedDBCryptoStore(
|
||||||
indexedDB,
|
indexedDB,
|
||||||
CRYPTO_STORE_NAME
|
CRYPTO_STORE_NAME,
|
||||||
);
|
);
|
||||||
} else if (localStorage) {
|
} else if (localStorage) {
|
||||||
baseOpts.cryptoStore = new LocalStorageCryptoStore(localStorage);
|
baseOpts.cryptoStore = new LocalStorageCryptoStore(localStorage);
|
||||||
@@ -198,7 +198,7 @@ export async function initClient(
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error(
|
logger.error(
|
||||||
"Error starting matrix client store. Falling back to memory store.",
|
"Error starting matrix client store. Falling back to memory store.",
|
||||||
error
|
error,
|
||||||
);
|
);
|
||||||
client.store = new MemoryStore({ localStorage });
|
client.store = new MemoryStore({ localStorage });
|
||||||
await client.store.startup();
|
await client.store.startup();
|
||||||
@@ -268,7 +268,7 @@ export function roomNameFromRoomId(roomId: string): string {
|
|||||||
.substring(1)
|
.substring(1)
|
||||||
.split("-")
|
.split("-")
|
||||||
.map((part) =>
|
.map((part) =>
|
||||||
part.length > 0 ? part.charAt(0).toUpperCase() + part.slice(1) : part
|
part.length > 0 ? part.charAt(0).toUpperCase() + part.slice(1) : part,
|
||||||
)
|
)
|
||||||
.join(" ")
|
.join(" ")
|
||||||
.toLowerCase();
|
.toLowerCase();
|
||||||
@@ -297,7 +297,7 @@ interface CreateRoomResult {
|
|||||||
export async function createRoom(
|
export async function createRoom(
|
||||||
client: MatrixClient,
|
client: MatrixClient,
|
||||||
name: string,
|
name: string,
|
||||||
e2ee: boolean
|
e2ee: boolean,
|
||||||
): Promise<CreateRoomResult> {
|
): Promise<CreateRoomResult> {
|
||||||
logger.log(`Creating room for group call`);
|
logger.log(`Creating room for group call`);
|
||||||
const createPromise = client.createRoom({
|
const createPromise = client.createRoom({
|
||||||
@@ -358,7 +358,7 @@ export async function createRoom(
|
|||||||
GroupCallType.Video,
|
GroupCallType.Video,
|
||||||
false,
|
false,
|
||||||
GroupCallIntent.Room,
|
GroupCallIntent.Room,
|
||||||
true
|
true,
|
||||||
);
|
);
|
||||||
|
|
||||||
let password;
|
let password;
|
||||||
@@ -366,7 +366,7 @@ export async function createRoom(
|
|||||||
password = secureRandomString(16);
|
password = secureRandomString(16);
|
||||||
setLocalStorageItem(
|
setLocalStorageItem(
|
||||||
getRoomSharedKeyLocalStorageKey(result.room_id),
|
getRoomSharedKeyLocalStorageKey(result.room_id),
|
||||||
password
|
password,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -386,7 +386,7 @@ export async function createRoom(
|
|||||||
export function getAbsoluteRoomUrl(
|
export function getAbsoluteRoomUrl(
|
||||||
roomId: string,
|
roomId: string,
|
||||||
roomName?: string,
|
roomName?: string,
|
||||||
password?: string
|
password?: string,
|
||||||
): string {
|
): string {
|
||||||
return `${window.location.protocol}//${
|
return `${window.location.protocol}//${
|
||||||
window.location.host
|
window.location.host
|
||||||
@@ -402,7 +402,7 @@ export function getAbsoluteRoomUrl(
|
|||||||
export function getRelativeRoomUrl(
|
export function getRelativeRoomUrl(
|
||||||
roomId: string,
|
roomId: string,
|
||||||
roomName?: string,
|
roomName?: string,
|
||||||
password?: string
|
password?: string,
|
||||||
): string {
|
): string {
|
||||||
// The password shouldn't need URL encoding here (we generate URL-safe ones) but encode
|
// The password shouldn't need URL encoding here (we generate URL-safe ones) but encode
|
||||||
// it in case it came from another client that generated a non url-safe one
|
// it in case it came from another client that generated a non url-safe one
|
||||||
@@ -419,7 +419,7 @@ export function getRelativeRoomUrl(
|
|||||||
export function getAvatarUrl(
|
export function getAvatarUrl(
|
||||||
client: MatrixClient,
|
client: MatrixClient,
|
||||||
mxcUrl: string,
|
mxcUrl: string,
|
||||||
avatarSize = 96
|
avatarSize = 96,
|
||||||
): string {
|
): string {
|
||||||
const width = Math.floor(avatarSize * window.devicePixelRatio);
|
const width = Math.floor(avatarSize * window.devicePixelRatio);
|
||||||
const height = Math.floor(avatarSize * window.devicePixelRatio);
|
const height = Math.floor(avatarSize * window.devicePixelRatio);
|
||||||
|
|||||||
@@ -23,10 +23,10 @@ limitations under the License.
|
|||||||
export async function findDeviceByName(
|
export async function findDeviceByName(
|
||||||
deviceName: string,
|
deviceName: string,
|
||||||
kind: MediaDeviceKind,
|
kind: MediaDeviceKind,
|
||||||
devices: MediaDeviceInfo[]
|
devices: MediaDeviceInfo[],
|
||||||
): Promise<string | undefined> {
|
): Promise<string | undefined> {
|
||||||
const deviceInfo = devices.find(
|
const deviceInfo = devices.find(
|
||||||
(d) => d.kind === kind && d.label === deviceName
|
(d) => d.kind === kind && d.label === deviceName,
|
||||||
);
|
);
|
||||||
return deviceInfo?.deviceId;
|
return deviceInfo?.deviceId;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -48,14 +48,14 @@ export class OTelCall {
|
|||||||
public userId: string,
|
public userId: string,
|
||||||
public deviceId: string,
|
public deviceId: string,
|
||||||
public call: MatrixCall,
|
public call: MatrixCall,
|
||||||
public span: Span
|
public span: Span,
|
||||||
) {
|
) {
|
||||||
if (call.peerConn) {
|
if (call.peerConn) {
|
||||||
this.addCallPeerConnListeners();
|
this.addCallPeerConnListeners();
|
||||||
} else {
|
} else {
|
||||||
this.call.once(
|
this.call.once(
|
||||||
CallEvent.PeerConnectionCreated,
|
CallEvent.PeerConnectionCreated,
|
||||||
this.addCallPeerConnListeners
|
this.addCallPeerConnListeners,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -63,46 +63,46 @@ export class OTelCall {
|
|||||||
public dispose(): void {
|
public dispose(): void {
|
||||||
this.call.peerConn?.removeEventListener(
|
this.call.peerConn?.removeEventListener(
|
||||||
"connectionstatechange",
|
"connectionstatechange",
|
||||||
this.onCallConnectionStateChanged
|
this.onCallConnectionStateChanged,
|
||||||
);
|
);
|
||||||
this.call.peerConn?.removeEventListener(
|
this.call.peerConn?.removeEventListener(
|
||||||
"signalingstatechange",
|
"signalingstatechange",
|
||||||
this.onCallSignalingStateChanged
|
this.onCallSignalingStateChanged,
|
||||||
);
|
);
|
||||||
this.call.peerConn?.removeEventListener(
|
this.call.peerConn?.removeEventListener(
|
||||||
"iceconnectionstatechange",
|
"iceconnectionstatechange",
|
||||||
this.onIceConnectionStateChanged
|
this.onIceConnectionStateChanged,
|
||||||
);
|
);
|
||||||
this.call.peerConn?.removeEventListener(
|
this.call.peerConn?.removeEventListener(
|
||||||
"icegatheringstatechange",
|
"icegatheringstatechange",
|
||||||
this.onIceGatheringStateChanged
|
this.onIceGatheringStateChanged,
|
||||||
);
|
);
|
||||||
this.call.peerConn?.removeEventListener(
|
this.call.peerConn?.removeEventListener(
|
||||||
"icecandidateerror",
|
"icecandidateerror",
|
||||||
this.onIceCandidateError
|
this.onIceCandidateError,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private addCallPeerConnListeners = (): void => {
|
private addCallPeerConnListeners = (): void => {
|
||||||
this.call.peerConn?.addEventListener(
|
this.call.peerConn?.addEventListener(
|
||||||
"connectionstatechange",
|
"connectionstatechange",
|
||||||
this.onCallConnectionStateChanged
|
this.onCallConnectionStateChanged,
|
||||||
);
|
);
|
||||||
this.call.peerConn?.addEventListener(
|
this.call.peerConn?.addEventListener(
|
||||||
"signalingstatechange",
|
"signalingstatechange",
|
||||||
this.onCallSignalingStateChanged
|
this.onCallSignalingStateChanged,
|
||||||
);
|
);
|
||||||
this.call.peerConn?.addEventListener(
|
this.call.peerConn?.addEventListener(
|
||||||
"iceconnectionstatechange",
|
"iceconnectionstatechange",
|
||||||
this.onIceConnectionStateChanged
|
this.onIceConnectionStateChanged,
|
||||||
);
|
);
|
||||||
this.call.peerConn?.addEventListener(
|
this.call.peerConn?.addEventListener(
|
||||||
"icegatheringstatechange",
|
"icegatheringstatechange",
|
||||||
this.onIceGatheringStateChanged
|
this.onIceGatheringStateChanged,
|
||||||
);
|
);
|
||||||
this.call.peerConn?.addEventListener(
|
this.call.peerConn?.addEventListener(
|
||||||
"icecandidateerror",
|
"icecandidateerror",
|
||||||
this.onIceCandidateError
|
this.onIceCandidateError,
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -147,8 +147,8 @@ export class OTelCall {
|
|||||||
new OTelCallFeedMediaStreamSpan(
|
new OTelCallFeedMediaStreamSpan(
|
||||||
ElementCallOpenTelemetry.instance,
|
ElementCallOpenTelemetry.instance,
|
||||||
this.span,
|
this.span,
|
||||||
feed
|
feed,
|
||||||
)
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
this.trackFeedSpan.get(feed.stream)?.update(feed);
|
this.trackFeedSpan.get(feed.stream)?.update(feed);
|
||||||
@@ -171,13 +171,13 @@ export class OTelCall {
|
|||||||
new OTelCallTransceiverMediaStreamSpan(
|
new OTelCallTransceiverMediaStreamSpan(
|
||||||
ElementCallOpenTelemetry.instance,
|
ElementCallOpenTelemetry.instance,
|
||||||
this.span,
|
this.span,
|
||||||
transStats
|
transStats,
|
||||||
)
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
this.trackTransceiverSpan.get(transStats.mid)?.update(transStats);
|
this.trackTransceiverSpan.get(transStats.mid)?.update(transStats);
|
||||||
prvTransSpan = prvTransSpan.filter(
|
prvTransSpan = prvTransSpan.filter(
|
||||||
(prvStreamId) => prvStreamId !== transStats.mid
|
(prvStreamId) => prvStreamId !== transStats.mid,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -190,7 +190,7 @@ export class OTelCall {
|
|||||||
public end(): void {
|
public end(): void {
|
||||||
this.trackFeedSpan.forEach((feedSpan) => feedSpan.end());
|
this.trackFeedSpan.forEach((feedSpan) => feedSpan.end());
|
||||||
this.trackTransceiverSpan.forEach((transceiverSpan) =>
|
this.trackTransceiverSpan.forEach((transceiverSpan) =>
|
||||||
transceiverSpan.end()
|
transceiverSpan.end(),
|
||||||
);
|
);
|
||||||
this.span.end();
|
this.span.end();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,11 +32,11 @@ export abstract class OTelCallAbstractMediaStreamSpan {
|
|||||||
public constructor(
|
public constructor(
|
||||||
protected readonly oTel: ElementCallOpenTelemetry,
|
protected readonly oTel: ElementCallOpenTelemetry,
|
||||||
protected readonly callSpan: Span,
|
protected readonly callSpan: Span,
|
||||||
protected readonly type: string
|
protected readonly type: string,
|
||||||
) {
|
) {
|
||||||
const ctx = opentelemetry.trace.setSpan(
|
const ctx = opentelemetry.trace.setSpan(
|
||||||
opentelemetry.context.active(),
|
opentelemetry.context.active(),
|
||||||
callSpan
|
callSpan,
|
||||||
);
|
);
|
||||||
const options = {
|
const options = {
|
||||||
links: [
|
links: [
|
||||||
@@ -54,7 +54,7 @@ export abstract class OTelCallAbstractMediaStreamSpan {
|
|||||||
if (!this.trackSpans.has(t.id)) {
|
if (!this.trackSpans.has(t.id)) {
|
||||||
this.trackSpans.set(
|
this.trackSpans.set(
|
||||||
t.id,
|
t.id,
|
||||||
new OTelCallMediaStreamTrackSpan(this.oTel, this.span, t)
|
new OTelCallMediaStreamTrackSpan(this.oTel, this.span, t),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
this.trackSpans.get(t.id)?.update(t);
|
this.trackSpans.get(t.id)?.update(t);
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ export class OTelCallFeedMediaStreamSpan extends OTelCallAbstractMediaStreamSpan
|
|||||||
public constructor(
|
public constructor(
|
||||||
protected readonly oTel: ElementCallOpenTelemetry,
|
protected readonly oTel: ElementCallOpenTelemetry,
|
||||||
protected readonly callSpan: Span,
|
protected readonly callSpan: Span,
|
||||||
callFeed: CallFeedStats
|
callFeed: CallFeedStats,
|
||||||
) {
|
) {
|
||||||
const postFix =
|
const postFix =
|
||||||
callFeed.type === "local" && callFeed.prefix === "from-call-feed"
|
callFeed.type === "local" && callFeed.prefix === "from-call-feed"
|
||||||
|
|||||||
@@ -26,11 +26,11 @@ export class OTelCallMediaStreamTrackSpan {
|
|||||||
public constructor(
|
public constructor(
|
||||||
protected readonly oTel: ElementCallOpenTelemetry,
|
protected readonly oTel: ElementCallOpenTelemetry,
|
||||||
protected readonly streamSpan: Span,
|
protected readonly streamSpan: Span,
|
||||||
data: TrackStats
|
data: TrackStats,
|
||||||
) {
|
) {
|
||||||
const ctx = opentelemetry.trace.setSpan(
|
const ctx = opentelemetry.trace.setSpan(
|
||||||
opentelemetry.context.active(),
|
opentelemetry.context.active(),
|
||||||
streamSpan
|
streamSpan,
|
||||||
);
|
);
|
||||||
const options = {
|
const options = {
|
||||||
links: [
|
links: [
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ export class OTelCallTransceiverMediaStreamSpan extends OTelCallAbstractMediaStr
|
|||||||
public constructor(
|
public constructor(
|
||||||
protected readonly oTel: ElementCallOpenTelemetry,
|
protected readonly oTel: ElementCallOpenTelemetry,
|
||||||
protected readonly callSpan: Span,
|
protected readonly callSpan: Span,
|
||||||
stats: TransceiverStats
|
stats: TransceiverStats,
|
||||||
) {
|
) {
|
||||||
super(oTel, callSpan, `matrix.call.transceiver.${stats.mid}`);
|
super(oTel, callSpan, `matrix.call.transceiver.${stats.mid}`);
|
||||||
this.span.setAttribute("transceiver.mid", stats.mid);
|
this.span.setAttribute("transceiver.mid", stats.mid);
|
||||||
|
|||||||
@@ -62,7 +62,10 @@ export class OTelGroupCallMembership {
|
|||||||
};
|
};
|
||||||
private readonly speakingSpans = new Map<RoomMember, Map<string, Span>>();
|
private readonly speakingSpans = new Map<RoomMember, Map<string, Span>>();
|
||||||
|
|
||||||
public constructor(private groupCall: GroupCall, client: MatrixClient) {
|
public constructor(
|
||||||
|
private groupCall: GroupCall,
|
||||||
|
client: MatrixClient,
|
||||||
|
) {
|
||||||
const clientId = client.getUserId();
|
const clientId = client.getUserId();
|
||||||
if (clientId) {
|
if (clientId) {
|
||||||
this.myUserId = clientId;
|
this.myUserId = clientId;
|
||||||
@@ -79,7 +82,7 @@ export class OTelGroupCallMembership {
|
|||||||
public dispose(): void {
|
public dispose(): void {
|
||||||
this.groupCall.removeListener(
|
this.groupCall.removeListener(
|
||||||
GroupCallEvent.CallsChanged,
|
GroupCallEvent.CallsChanged,
|
||||||
this.onCallsChanged
|
this.onCallsChanged,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -93,22 +96,22 @@ export class OTelGroupCallMembership {
|
|||||||
// Create the main span that tracks the time we intend to be in the call
|
// Create the main span that tracks the time we intend to be in the call
|
||||||
this.callMembershipSpan =
|
this.callMembershipSpan =
|
||||||
ElementCallOpenTelemetry.instance.tracer.startSpan(
|
ElementCallOpenTelemetry.instance.tracer.startSpan(
|
||||||
"matrix.groupCallMembership"
|
"matrix.groupCallMembership",
|
||||||
);
|
);
|
||||||
this.callMembershipSpan.setAttribute(
|
this.callMembershipSpan.setAttribute(
|
||||||
"matrix.confId",
|
"matrix.confId",
|
||||||
this.groupCall.groupCallId
|
this.groupCall.groupCallId,
|
||||||
);
|
);
|
||||||
this.callMembershipSpan.setAttribute("matrix.userId", this.myUserId);
|
this.callMembershipSpan.setAttribute("matrix.userId", this.myUserId);
|
||||||
this.callMembershipSpan.setAttribute("matrix.deviceId", this.myDeviceId);
|
this.callMembershipSpan.setAttribute("matrix.deviceId", this.myDeviceId);
|
||||||
this.callMembershipSpan.setAttribute(
|
this.callMembershipSpan.setAttribute(
|
||||||
"matrix.displayName",
|
"matrix.displayName",
|
||||||
this.myMember ? this.myMember.name : "unknown-name"
|
this.myMember ? this.myMember.name : "unknown-name",
|
||||||
);
|
);
|
||||||
|
|
||||||
this.groupCallContext = opentelemetry.trace.setSpan(
|
this.groupCallContext = opentelemetry.trace.setSpan(
|
||||||
opentelemetry.context.active(),
|
opentelemetry.context.active(),
|
||||||
this.callMembershipSpan
|
this.callMembershipSpan,
|
||||||
);
|
);
|
||||||
|
|
||||||
this.callMembershipSpan?.addEvent("matrix.joinCall");
|
this.callMembershipSpan?.addEvent("matrix.joinCall");
|
||||||
@@ -138,7 +141,7 @@ export class OTelGroupCallMembership {
|
|||||||
|
|
||||||
this.callMembershipSpan?.addEvent(
|
this.callMembershipSpan?.addEvent(
|
||||||
`matrix.roomStateEvent_${event.getType()}`,
|
`matrix.roomStateEvent_${event.getType()}`,
|
||||||
ObjectFlattener.flattenVoipEvent(event.getContent())
|
ObjectFlattener.flattenVoipEvent(event.getContent()),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -150,7 +153,7 @@ export class OTelGroupCallMembership {
|
|||||||
const span = ElementCallOpenTelemetry.instance.tracer.startSpan(
|
const span = ElementCallOpenTelemetry.instance.tracer.startSpan(
|
||||||
`matrix.call`,
|
`matrix.call`,
|
||||||
undefined,
|
undefined,
|
||||||
this.groupCallContext
|
this.groupCallContext,
|
||||||
);
|
);
|
||||||
// XXX: anonymity
|
// XXX: anonymity
|
||||||
span.setAttribute("matrix.call.target.userId", userId);
|
span.setAttribute("matrix.call.target.userId", userId);
|
||||||
@@ -160,7 +163,7 @@ export class OTelGroupCallMembership {
|
|||||||
span.setAttribute("matrix.call.target.displayName", displayName);
|
span.setAttribute("matrix.call.target.displayName", displayName);
|
||||||
this.callsByCallId.set(
|
this.callsByCallId.set(
|
||||||
call.callId,
|
call.callId,
|
||||||
new OTelCall(userId, deviceId, call, span)
|
new OTelCall(userId, deviceId, call, span),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -210,12 +213,12 @@ export class OTelGroupCallMembership {
|
|||||||
if (event.type === "toDevice") {
|
if (event.type === "toDevice") {
|
||||||
callTrackingInfo.span.addEvent(
|
callTrackingInfo.span.addEvent(
|
||||||
`matrix.sendToDeviceEvent_${event.eventType}`,
|
`matrix.sendToDeviceEvent_${event.eventType}`,
|
||||||
ObjectFlattener.flattenVoipEvent(event)
|
ObjectFlattener.flattenVoipEvent(event),
|
||||||
);
|
);
|
||||||
} else if (event.type === "sendEvent") {
|
} else if (event.type === "sendEvent") {
|
||||||
callTrackingInfo.span.addEvent(
|
callTrackingInfo.span.addEvent(
|
||||||
`matrix.sendToRoomEvent_${event.eventType}`,
|
`matrix.sendToRoomEvent_${event.eventType}`,
|
||||||
ObjectFlattener.flattenVoipEvent(event)
|
ObjectFlattener.flattenVoipEvent(event),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -239,7 +242,7 @@ export class OTelGroupCallMembership {
|
|||||||
"matrix.receive_voip_event_unknown_callid",
|
"matrix.receive_voip_event_unknown_callid",
|
||||||
{
|
{
|
||||||
"sender.userId": event.getSender(),
|
"sender.userId": event.getSender(),
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
logger.error("Received call event for unknown call ID " + callId);
|
logger.error("Received call event for unknown call ID " + callId);
|
||||||
return;
|
return;
|
||||||
@@ -284,7 +287,7 @@ export class OTelGroupCallMembership {
|
|||||||
public onSpeaking(
|
public onSpeaking(
|
||||||
member: RoomMember,
|
member: RoomMember,
|
||||||
deviceId: string,
|
deviceId: string,
|
||||||
speaking: boolean
|
speaking: boolean,
|
||||||
): void {
|
): void {
|
||||||
if (speaking) {
|
if (speaking) {
|
||||||
// Ensure that there's an audio activity span for this speaker
|
// Ensure that there's an audio activity span for this speaker
|
||||||
@@ -298,7 +301,7 @@ export class OTelGroupCallMembership {
|
|||||||
const span = ElementCallOpenTelemetry.instance.tracer.startSpan(
|
const span = ElementCallOpenTelemetry.instance.tracer.startSpan(
|
||||||
"matrix.audioActivity",
|
"matrix.audioActivity",
|
||||||
undefined,
|
undefined,
|
||||||
this.groupCallContext
|
this.groupCallContext,
|
||||||
);
|
);
|
||||||
span.setAttribute("matrix.userId", member.userId);
|
span.setAttribute("matrix.userId", member.userId);
|
||||||
span.setAttribute("matrix.displayName", member.rawDisplayName);
|
span.setAttribute("matrix.displayName", member.rawDisplayName);
|
||||||
@@ -336,7 +339,7 @@ export class OTelGroupCallMembership {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public onCallFeedStatsReport(
|
public onCallFeedStatsReport(
|
||||||
report: GroupCallStatsReport<CallFeedReport>
|
report: GroupCallStatsReport<CallFeedReport>,
|
||||||
): void {
|
): void {
|
||||||
if (!ElementCallOpenTelemetry.instance) return;
|
if (!ElementCallOpenTelemetry.instance) return;
|
||||||
let call: OTelCall | undefined;
|
let call: OTelCall | undefined;
|
||||||
@@ -354,10 +357,10 @@ export class OTelGroupCallMembership {
|
|||||||
"call.opponentMemberId": report.report?.opponentMemberId
|
"call.opponentMemberId": report.report?.opponentMemberId
|
||||||
? report.report?.opponentMemberId
|
? report.report?.opponentMemberId
|
||||||
: "unknown",
|
: "unknown",
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
logger.error(
|
logger.error(
|
||||||
`Received ${OTelStatsReportType.CallFeedReport} with unknown call ID: ${callId}`
|
`Received ${OTelStatsReportType.CallFeedReport} with unknown call ID: ${callId}`,
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
@@ -367,26 +370,26 @@ export class OTelGroupCallMembership {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public onConnectionStatsReport(
|
public onConnectionStatsReport(
|
||||||
statsReport: GroupCallStatsReport<ConnectionStatsReport>
|
statsReport: GroupCallStatsReport<ConnectionStatsReport>,
|
||||||
): void {
|
): void {
|
||||||
this.buildCallStatsSpan(
|
this.buildCallStatsSpan(
|
||||||
OTelStatsReportType.ConnectionReport,
|
OTelStatsReportType.ConnectionReport,
|
||||||
statsReport.report
|
statsReport.report,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public onByteSentStatsReport(
|
public onByteSentStatsReport(
|
||||||
statsReport: GroupCallStatsReport<ByteSentStatsReport>
|
statsReport: GroupCallStatsReport<ByteSentStatsReport>,
|
||||||
): void {
|
): void {
|
||||||
this.buildCallStatsSpan(
|
this.buildCallStatsSpan(
|
||||||
OTelStatsReportType.ByteSentReport,
|
OTelStatsReportType.ByteSentReport,
|
||||||
statsReport.report
|
statsReport.report,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public buildCallStatsSpan(
|
public buildCallStatsSpan(
|
||||||
type: OTelStatsReportType,
|
type: OTelStatsReportType,
|
||||||
report: ByteSentStatsReport | ConnectionStatsReport
|
report: ByteSentStatsReport | ConnectionStatsReport,
|
||||||
): void {
|
): void {
|
||||||
if (!ElementCallOpenTelemetry.instance) return;
|
if (!ElementCallOpenTelemetry.instance) return;
|
||||||
let call: OTelCall | undefined;
|
let call: OTelCall | undefined;
|
||||||
@@ -409,7 +412,7 @@ export class OTelGroupCallMembership {
|
|||||||
const data = ObjectFlattener.flattenReportObject(type, report);
|
const data = ObjectFlattener.flattenReportObject(type, report);
|
||||||
const ctx = opentelemetry.trace.setSpan(
|
const ctx = opentelemetry.trace.setSpan(
|
||||||
opentelemetry.context.active(),
|
opentelemetry.context.active(),
|
||||||
call.span
|
call.span,
|
||||||
);
|
);
|
||||||
|
|
||||||
const options = {
|
const options = {
|
||||||
@@ -423,20 +426,20 @@ export class OTelGroupCallMembership {
|
|||||||
const span = ElementCallOpenTelemetry.instance.tracer.startSpan(
|
const span = ElementCallOpenTelemetry.instance.tracer.startSpan(
|
||||||
type,
|
type,
|
||||||
options,
|
options,
|
||||||
ctx
|
ctx,
|
||||||
);
|
);
|
||||||
|
|
||||||
span.setAttribute("matrix.callId", callId ?? "unknown");
|
span.setAttribute("matrix.callId", callId ?? "unknown");
|
||||||
span.setAttribute(
|
span.setAttribute(
|
||||||
"matrix.opponentMemberId",
|
"matrix.opponentMemberId",
|
||||||
report.opponentMemberId ? report.opponentMemberId : "unknown"
|
report.opponentMemberId ? report.opponentMemberId : "unknown",
|
||||||
);
|
);
|
||||||
span.addEvent("matrix.call.connection_stats_event", data);
|
span.addEvent("matrix.call.connection_stats_event", data);
|
||||||
span.end();
|
span.end();
|
||||||
}
|
}
|
||||||
|
|
||||||
public onSummaryStatsReport(
|
public onSummaryStatsReport(
|
||||||
statsReport: GroupCallStatsReport<SummaryStatsReport>
|
statsReport: GroupCallStatsReport<SummaryStatsReport>,
|
||||||
): void {
|
): void {
|
||||||
if (!ElementCallOpenTelemetry.instance) return;
|
if (!ElementCallOpenTelemetry.instance) return;
|
||||||
|
|
||||||
@@ -445,12 +448,12 @@ export class OTelGroupCallMembership {
|
|||||||
if (this.statsReportSpan.span === undefined && this.callMembershipSpan) {
|
if (this.statsReportSpan.span === undefined && this.callMembershipSpan) {
|
||||||
const ctx = setSpan(
|
const ctx = setSpan(
|
||||||
opentelemetry.context.active(),
|
opentelemetry.context.active(),
|
||||||
this.callMembershipSpan
|
this.callMembershipSpan,
|
||||||
);
|
);
|
||||||
const span = ElementCallOpenTelemetry.instance?.tracer.startSpan(
|
const span = ElementCallOpenTelemetry.instance?.tracer.startSpan(
|
||||||
"matrix.groupCallMembership.summaryReport",
|
"matrix.groupCallMembership.summaryReport",
|
||||||
undefined,
|
undefined,
|
||||||
ctx
|
ctx,
|
||||||
);
|
);
|
||||||
if (span === undefined) {
|
if (span === undefined) {
|
||||||
return;
|
return;
|
||||||
@@ -459,7 +462,7 @@ export class OTelGroupCallMembership {
|
|||||||
span.setAttribute("matrix.userId", this.myUserId);
|
span.setAttribute("matrix.userId", this.myUserId);
|
||||||
span.setAttribute(
|
span.setAttribute(
|
||||||
"matrix.displayName",
|
"matrix.displayName",
|
||||||
this.myMember ? this.myMember.name : "unknown-name"
|
this.myMember ? this.myMember.name : "unknown-name",
|
||||||
);
|
);
|
||||||
span.addEvent(type, data);
|
span.addEvent(type, data);
|
||||||
span.end();
|
span.end();
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ import {
|
|||||||
export class ObjectFlattener {
|
export class ObjectFlattener {
|
||||||
public static flattenReportObject(
|
public static flattenReportObject(
|
||||||
prefix: string,
|
prefix: string,
|
||||||
report: ConnectionStatsReport | ByteSentStatsReport
|
report: ConnectionStatsReport | ByteSentStatsReport,
|
||||||
): Attributes {
|
): Attributes {
|
||||||
const flatObject = {};
|
const flatObject = {};
|
||||||
ObjectFlattener.flattenObjectRecursive(report, flatObject, `${prefix}.`, 0);
|
ObjectFlattener.flattenObjectRecursive(report, flatObject, `${prefix}.`, 0);
|
||||||
@@ -33,27 +33,27 @@ export class ObjectFlattener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static flattenByteSentStatsReportObject(
|
public static flattenByteSentStatsReportObject(
|
||||||
statsReport: GroupCallStatsReport<ByteSentStatsReport>
|
statsReport: GroupCallStatsReport<ByteSentStatsReport>,
|
||||||
): Attributes {
|
): Attributes {
|
||||||
const flatObject = {};
|
const flatObject = {};
|
||||||
ObjectFlattener.flattenObjectRecursive(
|
ObjectFlattener.flattenObjectRecursive(
|
||||||
statsReport.report,
|
statsReport.report,
|
||||||
flatObject,
|
flatObject,
|
||||||
"matrix.stats.bytesSent.",
|
"matrix.stats.bytesSent.",
|
||||||
0
|
0,
|
||||||
);
|
);
|
||||||
return flatObject;
|
return flatObject;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static flattenSummaryStatsReportObject(
|
public static flattenSummaryStatsReportObject(
|
||||||
statsReport: GroupCallStatsReport<SummaryStatsReport>
|
statsReport: GroupCallStatsReport<SummaryStatsReport>,
|
||||||
): Attributes {
|
): Attributes {
|
||||||
const flatObject = {};
|
const flatObject = {};
|
||||||
ObjectFlattener.flattenObjectRecursive(
|
ObjectFlattener.flattenObjectRecursive(
|
||||||
statsReport.report,
|
statsReport.report,
|
||||||
flatObject,
|
flatObject,
|
||||||
"matrix.stats.summary.",
|
"matrix.stats.summary.",
|
||||||
0
|
0,
|
||||||
);
|
);
|
||||||
return flatObject;
|
return flatObject;
|
||||||
}
|
}
|
||||||
@@ -67,7 +67,7 @@ export class ObjectFlattener {
|
|||||||
event as unknown as Record<string, unknown>, // XXX Types
|
event as unknown as Record<string, unknown>, // XXX Types
|
||||||
flatObject,
|
flatObject,
|
||||||
"matrix.event.",
|
"matrix.event.",
|
||||||
0
|
0,
|
||||||
);
|
);
|
||||||
|
|
||||||
return flatObject;
|
return flatObject;
|
||||||
@@ -77,12 +77,12 @@ export class ObjectFlattener {
|
|||||||
obj: Object,
|
obj: Object,
|
||||||
flatObject: Attributes,
|
flatObject: Attributes,
|
||||||
prefix: string,
|
prefix: string,
|
||||||
depth: number
|
depth: number,
|
||||||
): void {
|
): void {
|
||||||
if (depth > 10)
|
if (depth > 10)
|
||||||
throw new Error(
|
throw new Error(
|
||||||
"Depth limit exceeded: aborting VoipEvent recursion. Prefix is " +
|
"Depth limit exceeded: aborting VoipEvent recursion. Prefix is " +
|
||||||
prefix
|
prefix,
|
||||||
);
|
);
|
||||||
let entries;
|
let entries;
|
||||||
if (obj instanceof Map) {
|
if (obj instanceof Map) {
|
||||||
@@ -101,7 +101,7 @@ export class ObjectFlattener {
|
|||||||
v,
|
v,
|
||||||
flatObject,
|
flatObject,
|
||||||
prefix + k + ".",
|
prefix + k + ".",
|
||||||
depth + 1
|
depth + 1,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ export class ElementCallOpenTelemetry {
|
|||||||
|
|
||||||
sharedInstance = new ElementCallOpenTelemetry(
|
sharedInstance = new ElementCallOpenTelemetry(
|
||||||
config.opentelemetry?.collector_url,
|
config.opentelemetry?.collector_url,
|
||||||
config.rageshake?.submit_url
|
config.rageshake?.submit_url,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -61,7 +61,7 @@ export class ElementCallOpenTelemetry {
|
|||||||
|
|
||||||
private constructor(
|
private constructor(
|
||||||
collectorUrl: string | undefined,
|
collectorUrl: string | undefined,
|
||||||
rageshakeUrl: string | undefined
|
rageshakeUrl: string | undefined,
|
||||||
) {
|
) {
|
||||||
// This is how we can make Jaeger show a reasonable service in the dropdown on the left.
|
// This is how we can make Jaeger show a reasonable service in the dropdown on the left.
|
||||||
const providerConfig = {
|
const providerConfig = {
|
||||||
@@ -77,7 +77,7 @@ export class ElementCallOpenTelemetry {
|
|||||||
url: collectorUrl,
|
url: collectorUrl,
|
||||||
});
|
});
|
||||||
this._provider.addSpanProcessor(
|
this._provider.addSpanProcessor(
|
||||||
new SimpleSpanProcessor(this.otlpExporter)
|
new SimpleSpanProcessor(this.otlpExporter),
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
logger.info("OTLP collector disabled");
|
logger.info("OTLP collector disabled");
|
||||||
@@ -93,7 +93,7 @@ export class ElementCallOpenTelemetry {
|
|||||||
|
|
||||||
this._tracer = opentelemetry.trace.getTracer(
|
this._tracer = opentelemetry.trace.getTracer(
|
||||||
// This is not the serviceName shown in jaeger
|
// This is not the serviceName shown in jaeger
|
||||||
"my-element-call-otl-tracer"
|
"my-element-call-otl-tracer",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ export const Popover = forwardRef<HTMLDivElement, Props>(
|
|||||||
shouldCloseOnBlur: true,
|
shouldCloseOnBlur: true,
|
||||||
isDismissable: true,
|
isDismissable: true,
|
||||||
},
|
},
|
||||||
popoverRef
|
popoverRef,
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -56,5 +56,5 @@ export const Popover = forwardRef<HTMLDivElement, Props>(
|
|||||||
</div>
|
</div>
|
||||||
</FocusScope>
|
</FocusScope>
|
||||||
);
|
);
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ export const PopoverMenuTrigger = forwardRef<
|
|||||||
const { menuTriggerProps, menuProps } = useMenuTrigger(
|
const { menuTriggerProps, menuProps } = useMenuTrigger(
|
||||||
{},
|
{},
|
||||||
popoverMenuState,
|
popoverMenuState,
|
||||||
buttonRef
|
buttonRef,
|
||||||
);
|
);
|
||||||
|
|
||||||
const popoverRef = useRef(null);
|
const popoverRef = useRef(null);
|
||||||
@@ -62,7 +62,7 @@ export const PopoverMenuTrigger = forwardRef<
|
|||||||
typeof children[1] !== "function"
|
typeof children[1] !== "function"
|
||||||
) {
|
) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
"PopoverMenu must have two props. The first being a button and the second being a render prop."
|
"PopoverMenu must have two props. The first being a button and the second being a render prop.",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ export function useProfile(client: MatrixClient | undefined): UseProfile {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const onChangeUser = (
|
const onChangeUser = (
|
||||||
_event: MatrixEvent | undefined,
|
_event: MatrixEvent | undefined,
|
||||||
{ displayName, avatarUrl }: User
|
{ displayName, avatarUrl }: User,
|
||||||
): void => {
|
): void => {
|
||||||
setState({
|
setState({
|
||||||
success: false,
|
success: false,
|
||||||
@@ -108,9 +108,8 @@ export function useProfile(client: MatrixClient | undefined): UseProfile {
|
|||||||
if (removeAvatar) {
|
if (removeAvatar) {
|
||||||
await client.setAvatarUrl("");
|
await client.setAvatarUrl("");
|
||||||
} else if (avatar) {
|
} else if (avatar) {
|
||||||
({ content_uri: mxcAvatarUrl } = await client.uploadContent(
|
({ content_uri: mxcAvatarUrl } =
|
||||||
avatar
|
await client.uploadContent(avatar));
|
||||||
));
|
|
||||||
await client.setAvatarUrl(mxcAvatarUrl);
|
await client.setAvatarUrl(mxcAvatarUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -135,7 +134,7 @@ export function useProfile(client: MatrixClient | undefined): UseProfile {
|
|||||||
logger.error("Client not initialized before calling saveProfile");
|
logger.error("Client not initialized before calling saveProfile");
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[client]
|
[client],
|
||||||
);
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -40,14 +40,14 @@ export const AppSelectionModal: FC<Props> = ({ roomId }) => {
|
|||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
setOpen(false);
|
setOpen(false);
|
||||||
},
|
},
|
||||||
[setOpen]
|
[setOpen],
|
||||||
);
|
);
|
||||||
|
|
||||||
const roomSharedKey = useRoomSharedKey(roomId ?? "");
|
const roomSharedKey = useRoomSharedKey(roomId ?? "");
|
||||||
const roomIsEncrypted = useIsRoomE2EE(roomId ?? "");
|
const roomIsEncrypted = useIsRoomE2EE(roomId ?? "");
|
||||||
if (roomIsEncrypted && roomSharedKey === undefined) {
|
if (roomIsEncrypted && roomSharedKey === undefined) {
|
||||||
logger.error(
|
logger.error(
|
||||||
"Generating app redirect URL for encrypted room but don't have key available!"
|
"Generating app redirect URL for encrypted room but don't have key available!",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -60,7 +60,7 @@ export const AppSelectionModal: FC<Props> = ({ roomId }) => {
|
|||||||
const url = new URL(
|
const url = new URL(
|
||||||
roomId === null
|
roomId === null
|
||||||
? window.location.href
|
? window.location.href
|
||||||
: getAbsoluteRoomUrl(roomId, undefined, roomSharedKey ?? undefined)
|
: getAbsoluteRoomUrl(roomId, undefined, roomSharedKey ?? undefined),
|
||||||
);
|
);
|
||||||
// Edit the URL to prevent the app selection prompt from appearing a second
|
// Edit the URL to prevent the app selection prompt from appearing a second
|
||||||
// time within the app, and to keep the user confined to the current room
|
// time within the app, and to keep the user confined to the current room
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ export const CallEndedView: FC<Props> = ({
|
|||||||
PosthogAnalytics.instance.eventQualitySurvey.track(
|
PosthogAnalytics.instance.eventQualitySurvey.track(
|
||||||
endedCallId,
|
endedCallId,
|
||||||
feedbackText,
|
feedbackText,
|
||||||
starRating
|
starRating,
|
||||||
);
|
);
|
||||||
|
|
||||||
setSubmitting(true);
|
setSubmitting(true);
|
||||||
@@ -83,7 +83,7 @@ export const CallEndedView: FC<Props> = ({
|
|||||||
}, 1000);
|
}, 1000);
|
||||||
}, 1000);
|
}, 1000);
|
||||||
},
|
},
|
||||||
[endedCallId, history, isPasswordlessUser, confineToRoom, starRating]
|
[endedCallId, history, isPasswordlessUser, confineToRoom, starRating],
|
||||||
);
|
);
|
||||||
|
|
||||||
const createAccountDialog = isPasswordlessUser && (
|
const createAccountDialog = isPasswordlessUser && (
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ export function GroupCallLoader({
|
|||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
history.push("/");
|
history.push("/");
|
||||||
},
|
},
|
||||||
[history]
|
[history],
|
||||||
);
|
);
|
||||||
|
|
||||||
switch (groupCallState.kind) {
|
switch (groupCallState.kind) {
|
||||||
@@ -66,7 +66,7 @@ export function GroupCallLoader({
|
|||||||
<Heading>{t("Call not found")}</Heading>
|
<Heading>{t("Call not found")}</Heading>
|
||||||
<Text>
|
<Text>
|
||||||
{t(
|
{t(
|
||||||
"Calls are now end-to-end encrypted and need to be created from the home page. This helps make sure everyone's using the same encryption key."
|
"Calls are now end-to-end encrypted and need to be created from the home page. This helps make sure everyone's using the same encryption key.",
|
||||||
)}
|
)}
|
||||||
</Text>
|
</Text>
|
||||||
{/* XXX: A 'create it for me' button would be the obvious UX here. Two screens already have
|
{/* XXX: A 'create it for me' button would be the obvious UX here. Two screens already have
|
||||||
|
|||||||
@@ -111,7 +111,7 @@ export const GroupCallView: FC<Props> = ({
|
|||||||
// Count each member only once, regardless of how many devices they use
|
// Count each member only once, regardless of how many devices they use
|
||||||
const participantCount = useMemo(
|
const participantCount = useMemo(
|
||||||
() => new Set<string>(memberships.map((m) => m.sender!)).size,
|
() => new Set<string>(memberships.map((m) => m.sender!)).size,
|
||||||
[memberships]
|
[memberships],
|
||||||
);
|
);
|
||||||
|
|
||||||
const deviceContext = useMediaDevices();
|
const deviceContext = useMediaDevices();
|
||||||
@@ -126,7 +126,7 @@ export const GroupCallView: FC<Props> = ({
|
|||||||
if (widget && preload) {
|
if (widget && preload) {
|
||||||
// In preload mode, wait for a join action before entering
|
// In preload mode, wait for a join action before entering
|
||||||
const onJoin = async (
|
const onJoin = async (
|
||||||
ev: CustomEvent<IWidgetApiRequest>
|
ev: CustomEvent<IWidgetApiRequest>,
|
||||||
): Promise<void> => {
|
): Promise<void> => {
|
||||||
// XXX: I think this is broken currently - LiveKit *won't* request
|
// XXX: I think this is broken currently - LiveKit *won't* request
|
||||||
// permissions and give you device names unless you specify a kind, but
|
// permissions and give you device names unless you specify a kind, but
|
||||||
@@ -143,14 +143,14 @@ export const GroupCallView: FC<Props> = ({
|
|||||||
const deviceId = await findDeviceByName(
|
const deviceId = await findDeviceByName(
|
||||||
audioInput,
|
audioInput,
|
||||||
"audioinput",
|
"audioinput",
|
||||||
devices
|
devices,
|
||||||
);
|
);
|
||||||
if (!deviceId) {
|
if (!deviceId) {
|
||||||
logger.warn("Unknown audio input: " + audioInput);
|
logger.warn("Unknown audio input: " + audioInput);
|
||||||
latestMuteStates.current!.audio.setEnabled?.(false);
|
latestMuteStates.current!.audio.setEnabled?.(false);
|
||||||
} else {
|
} else {
|
||||||
logger.debug(
|
logger.debug(
|
||||||
`Found audio input ID ${deviceId} for name ${audioInput}`
|
`Found audio input ID ${deviceId} for name ${audioInput}`,
|
||||||
);
|
);
|
||||||
latestDevices.current!.audioInput.select(deviceId);
|
latestDevices.current!.audioInput.select(deviceId);
|
||||||
latestMuteStates.current!.audio.setEnabled?.(true);
|
latestMuteStates.current!.audio.setEnabled?.(true);
|
||||||
@@ -163,14 +163,14 @@ export const GroupCallView: FC<Props> = ({
|
|||||||
const deviceId = await findDeviceByName(
|
const deviceId = await findDeviceByName(
|
||||||
videoInput,
|
videoInput,
|
||||||
"videoinput",
|
"videoinput",
|
||||||
devices
|
devices,
|
||||||
);
|
);
|
||||||
if (!deviceId) {
|
if (!deviceId) {
|
||||||
logger.warn("Unknown video input: " + videoInput);
|
logger.warn("Unknown video input: " + videoInput);
|
||||||
latestMuteStates.current!.video.setEnabled?.(false);
|
latestMuteStates.current!.video.setEnabled?.(false);
|
||||||
} else {
|
} else {
|
||||||
logger.debug(
|
logger.debug(
|
||||||
`Found video input ID ${deviceId} for name ${videoInput}`
|
`Found video input ID ${deviceId} for name ${videoInput}`,
|
||||||
);
|
);
|
||||||
latestDevices.current!.videoInput.select(deviceId);
|
latestDevices.current!.videoInput.select(deviceId);
|
||||||
latestMuteStates.current!.video.setEnabled?.(true);
|
latestMuteStates.current!.video.setEnabled?.(true);
|
||||||
@@ -182,7 +182,7 @@ export const GroupCallView: FC<Props> = ({
|
|||||||
PosthogAnalytics.instance.eventCallEnded.cacheStartCall(new Date());
|
PosthogAnalytics.instance.eventCallEnded.cacheStartCall(new Date());
|
||||||
// we only have room sessions right now, so call ID is the emprty string - we use the room ID
|
// we only have room sessions right now, so call ID is the emprty string - we use the room ID
|
||||||
PosthogAnalytics.instance.eventCallStarted.track(
|
PosthogAnalytics.instance.eventCallStarted.track(
|
||||||
rtcSession.room.roomId
|
rtcSession.room.roomId,
|
||||||
);
|
);
|
||||||
|
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
@@ -213,7 +213,7 @@ export const GroupCallView: FC<Props> = ({
|
|||||||
PosthogAnalytics.instance.eventCallEnded.track(
|
PosthogAnalytics.instance.eventCallEnded.track(
|
||||||
rtcSession.room.roomId,
|
rtcSession.room.roomId,
|
||||||
rtcSession.memberships.length,
|
rtcSession.memberships.length,
|
||||||
sendInstantly
|
sendInstantly,
|
||||||
);
|
);
|
||||||
|
|
||||||
await leaveRTCSession(rtcSession);
|
await leaveRTCSession(rtcSession);
|
||||||
@@ -237,13 +237,13 @@ export const GroupCallView: FC<Props> = ({
|
|||||||
history.push("/");
|
history.push("/");
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[rtcSession, isPasswordlessUser, confineToRoom, history]
|
[rtcSession, isPasswordlessUser, confineToRoom, history],
|
||||||
);
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (widget && isJoined) {
|
if (widget && isJoined) {
|
||||||
const onHangup = async (
|
const onHangup = async (
|
||||||
ev: CustomEvent<IWidgetApiRequest>
|
ev: CustomEvent<IWidgetApiRequest>,
|
||||||
): Promise<void> => {
|
): Promise<void> => {
|
||||||
leaveRTCSession(rtcSession);
|
leaveRTCSession(rtcSession);
|
||||||
widget!.api.transport.reply(ev.detail, {});
|
widget!.api.transport.reply(ev.detail, {});
|
||||||
@@ -260,7 +260,7 @@ export const GroupCallView: FC<Props> = ({
|
|||||||
|
|
||||||
const e2eeConfig = useMemo(
|
const e2eeConfig = useMemo(
|
||||||
() => (e2eeSharedKey ? { sharedKey: e2eeSharedKey } : undefined),
|
() => (e2eeSharedKey ? { sharedKey: e2eeSharedKey } : undefined),
|
||||||
[e2eeSharedKey]
|
[e2eeSharedKey],
|
||||||
);
|
);
|
||||||
|
|
||||||
const onReconnect = useCallback(() => {
|
const onReconnect = useCallback(() => {
|
||||||
@@ -274,12 +274,12 @@ export const GroupCallView: FC<Props> = ({
|
|||||||
const [shareModalOpen, setInviteModalOpen] = useState(false);
|
const [shareModalOpen, setInviteModalOpen] = useState(false);
|
||||||
const onDismissInviteModal = useCallback(
|
const onDismissInviteModal = useCallback(
|
||||||
() => setInviteModalOpen(false),
|
() => setInviteModalOpen(false),
|
||||||
[setInviteModalOpen]
|
[setInviteModalOpen],
|
||||||
);
|
);
|
||||||
|
|
||||||
const onShareClickFn = useCallback(
|
const onShareClickFn = useCallback(
|
||||||
() => setInviteModalOpen(true),
|
() => setInviteModalOpen(true),
|
||||||
[setInviteModalOpen]
|
[setInviteModalOpen],
|
||||||
);
|
);
|
||||||
const onShareClick = joinRule === JoinRule.Public ? onShareClickFn : null;
|
const onShareClick = joinRule === JoinRule.Public ? onShareClickFn : null;
|
||||||
|
|
||||||
@@ -288,7 +288,7 @@ export const GroupCallView: FC<Props> = ({
|
|||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
history.push("/");
|
history.push("/");
|
||||||
},
|
},
|
||||||
[history]
|
[history],
|
||||||
);
|
);
|
||||||
|
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
@@ -298,7 +298,7 @@ export const GroupCallView: FC<Props> = ({
|
|||||||
<ErrorView
|
<ErrorView
|
||||||
error={
|
error={
|
||||||
new Error(
|
new Error(
|
||||||
"No E2EE key provided: please make sure the URL you're using to join this call has been retrieved using the in-app button."
|
"No E2EE key provided: please make sure the URL you're using to join this call has been retrieved using the in-app button.",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
@@ -309,7 +309,7 @@ export const GroupCallView: FC<Props> = ({
|
|||||||
<Heading>Incompatible Browser</Heading>
|
<Heading>Incompatible Browser</Heading>
|
||||||
<Text>
|
<Text>
|
||||||
{t(
|
{t(
|
||||||
"Your web browser does not support media end-to-end encryption. Supported Browsers are Chrome, Safari, Firefox >=117"
|
"Your web browser does not support media end-to-end encryption. Supported Browsers are Chrome, Safari, Firefox >=117",
|
||||||
)}
|
)}
|
||||||
</Text>
|
</Text>
|
||||||
<Link href="/" onClick={onHomeClick}>
|
<Link href="/" onClick={onHomeClick}>
|
||||||
|
|||||||
@@ -105,7 +105,7 @@ export const ActiveCall: FC<ActiveCallProps> = (props) => {
|
|||||||
const { livekitRoom, connState } = useLiveKit(
|
const { livekitRoom, connState } = useLiveKit(
|
||||||
props.muteStates,
|
props.muteStates,
|
||||||
sfuConfig,
|
sfuConfig,
|
||||||
props.e2eeConfig
|
props.e2eeConfig,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!livekitRoom) {
|
if (!livekitRoom) {
|
||||||
@@ -172,10 +172,10 @@ export const InCallView: FC<InCallViewProps> = ({
|
|||||||
[{ source: Track.Source.ScreenShare, withPlaceholder: false }],
|
[{ source: Track.Source.ScreenShare, withPlaceholder: false }],
|
||||||
{
|
{
|
||||||
room: livekitRoom,
|
room: livekitRoom,
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
const { layout, setLayout } = useVideoGridLayout(
|
const { layout, setLayout } = useVideoGridLayout(
|
||||||
screenSharingTracks.length > 0
|
screenSharingTracks.length > 0,
|
||||||
);
|
);
|
||||||
|
|
||||||
const [showConnectionStats] = useShowConnectionStats();
|
const [showConnectionStats] = useShowConnectionStats();
|
||||||
@@ -188,11 +188,11 @@ export const InCallView: FC<InCallViewProps> = ({
|
|||||||
|
|
||||||
const toggleMicrophone = useCallback(
|
const toggleMicrophone = useCallback(
|
||||||
() => muteStates.audio.setEnabled?.((e) => !e),
|
() => muteStates.audio.setEnabled?.((e) => !e),
|
||||||
[muteStates]
|
[muteStates],
|
||||||
);
|
);
|
||||||
const toggleCamera = useCallback(
|
const toggleCamera = useCallback(
|
||||||
() => muteStates.video.setEnabled?.((e) => !e),
|
() => muteStates.video.setEnabled?.((e) => !e),
|
||||||
[muteStates]
|
[muteStates],
|
||||||
);
|
);
|
||||||
|
|
||||||
// This function incorrectly assumes that there is a camera and microphone, which is not always the case.
|
// This function incorrectly assumes that there is a camera and microphone, which is not always the case.
|
||||||
@@ -201,7 +201,7 @@ export const InCallView: FC<InCallViewProps> = ({
|
|||||||
containerRef1,
|
containerRef1,
|
||||||
toggleMicrophone,
|
toggleMicrophone,
|
||||||
toggleCamera,
|
toggleCamera,
|
||||||
(muted) => muteStates.audio.setEnabled?.(!muted)
|
(muted) => muteStates.audio.setEnabled?.(!muted),
|
||||||
);
|
);
|
||||||
|
|
||||||
const onLeavePress = useCallback(() => {
|
const onLeavePress = useCallback(() => {
|
||||||
@@ -213,7 +213,7 @@ export const InCallView: FC<InCallViewProps> = ({
|
|||||||
layout === "grid"
|
layout === "grid"
|
||||||
? ElementWidgetActions.TileLayout
|
? ElementWidgetActions.TileLayout
|
||||||
: ElementWidgetActions.SpotlightLayout,
|
: ElementWidgetActions.SpotlightLayout,
|
||||||
{}
|
{},
|
||||||
);
|
);
|
||||||
}, [layout]);
|
}, [layout]);
|
||||||
|
|
||||||
@@ -231,14 +231,14 @@ export const InCallView: FC<InCallViewProps> = ({
|
|||||||
widget.lazyActions.on(ElementWidgetActions.TileLayout, onTileLayout);
|
widget.lazyActions.on(ElementWidgetActions.TileLayout, onTileLayout);
|
||||||
widget.lazyActions.on(
|
widget.lazyActions.on(
|
||||||
ElementWidgetActions.SpotlightLayout,
|
ElementWidgetActions.SpotlightLayout,
|
||||||
onSpotlightLayout
|
onSpotlightLayout,
|
||||||
);
|
);
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
widget!.lazyActions.off(ElementWidgetActions.TileLayout, onTileLayout);
|
widget!.lazyActions.off(ElementWidgetActions.TileLayout, onTileLayout);
|
||||||
widget!.lazyActions.off(
|
widget!.lazyActions.off(
|
||||||
ElementWidgetActions.SpotlightLayout,
|
ElementWidgetActions.SpotlightLayout,
|
||||||
onSpotlightLayout
|
onSpotlightLayout,
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -261,7 +261,7 @@ export const InCallView: FC<InCallViewProps> = ({
|
|||||||
(noControls
|
(noControls
|
||||||
? items.find((item) => item.isSpeaker) ?? items.at(0) ?? null
|
? items.find((item) => item.isSpeaker) ?? items.at(0) ?? null
|
||||||
: null),
|
: null),
|
||||||
[fullscreenItem, noControls, items]
|
[fullscreenItem, noControls, items],
|
||||||
);
|
);
|
||||||
|
|
||||||
const Grid =
|
const Grid =
|
||||||
@@ -320,18 +320,18 @@ export const InCallView: FC<InCallViewProps> = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const rageshakeRequestModalProps = useRageshakeRequestModal(
|
const rageshakeRequestModalProps = useRageshakeRequestModal(
|
||||||
rtcSession.room.roomId
|
rtcSession.room.roomId,
|
||||||
);
|
);
|
||||||
|
|
||||||
const [settingsModalOpen, setSettingsModalOpen] = useState(false);
|
const [settingsModalOpen, setSettingsModalOpen] = useState(false);
|
||||||
|
|
||||||
const openSettings = useCallback(
|
const openSettings = useCallback(
|
||||||
() => setSettingsModalOpen(true),
|
() => setSettingsModalOpen(true),
|
||||||
[setSettingsModalOpen]
|
[setSettingsModalOpen],
|
||||||
);
|
);
|
||||||
const closeSettings = useCallback(
|
const closeSettings = useCallback(
|
||||||
() => setSettingsModalOpen(false),
|
() => setSettingsModalOpen(false),
|
||||||
[setSettingsModalOpen]
|
[setSettingsModalOpen],
|
||||||
);
|
);
|
||||||
|
|
||||||
const toggleScreensharing = useCallback(async () => {
|
const toggleScreensharing = useCallback(async () => {
|
||||||
@@ -365,7 +365,7 @@ export const InCallView: FC<InCallViewProps> = ({
|
|||||||
onPress={toggleCamera}
|
onPress={toggleCamera}
|
||||||
disabled={muteStates.video.setEnabled === null}
|
disabled={muteStates.video.setEnabled === null}
|
||||||
data-testid="incall_videomute"
|
data-testid="incall_videomute"
|
||||||
/>
|
/>,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!reducedControls) {
|
if (!reducedControls) {
|
||||||
@@ -376,14 +376,18 @@ export const InCallView: FC<InCallViewProps> = ({
|
|||||||
enabled={isScreenShareEnabled}
|
enabled={isScreenShareEnabled}
|
||||||
onPress={toggleScreensharing}
|
onPress={toggleScreensharing}
|
||||||
data-testid="incall_screenshare"
|
data-testid="incall_screenshare"
|
||||||
/>
|
/>,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
buttons.push(<SettingsButton key="4" onPress={openSettings} />);
|
buttons.push(<SettingsButton key="4" onPress={openSettings} />);
|
||||||
}
|
}
|
||||||
|
|
||||||
buttons.push(
|
buttons.push(
|
||||||
<HangupButton key="6" onPress={onLeavePress} data-testid="incall_leave" />
|
<HangupButton
|
||||||
|
key="6"
|
||||||
|
onPress={onLeavePress}
|
||||||
|
data-testid="incall_leave"
|
||||||
|
/>,
|
||||||
);
|
);
|
||||||
footer = (
|
footer = (
|
||||||
<div className={styles.footer}>
|
<div className={styles.footer}>
|
||||||
@@ -447,7 +451,7 @@ export const InCallView: FC<InCallViewProps> = ({
|
|||||||
|
|
||||||
function findMatrixMember(
|
function findMatrixMember(
|
||||||
room: MatrixRoom,
|
room: MatrixRoom,
|
||||||
id: string
|
id: string,
|
||||||
): RoomMember | undefined {
|
): RoomMember | undefined {
|
||||||
if (!id) return undefined;
|
if (!id) return undefined;
|
||||||
|
|
||||||
@@ -455,7 +459,7 @@ function findMatrixMember(
|
|||||||
// must be at least 3 parts because we know the first part is a userId which must necessarily contain a colon
|
// must be at least 3 parts because we know the first part is a userId which must necessarily contain a colon
|
||||||
if (parts.length < 3) {
|
if (parts.length < 3) {
|
||||||
logger.warn(
|
logger.warn(
|
||||||
"Livekit participants ID doesn't look like a userId:deviceId combination"
|
"Livekit participants ID doesn't look like a userId:deviceId combination",
|
||||||
);
|
);
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
@@ -469,7 +473,7 @@ function findMatrixMember(
|
|||||||
function useParticipantTiles(
|
function useParticipantTiles(
|
||||||
livekitRoom: Room,
|
livekitRoom: Room,
|
||||||
matrixRoom: MatrixRoom,
|
matrixRoom: MatrixRoom,
|
||||||
connState: ECConnectionState
|
connState: ECConnectionState,
|
||||||
): TileDescriptor<ItemData>[] {
|
): TileDescriptor<ItemData>[] {
|
||||||
const previousTiles = useRef<TileDescriptor<ItemData>[]>([]);
|
const previousTiles = useRef<TileDescriptor<ItemData>[]>([]);
|
||||||
|
|
||||||
@@ -498,7 +502,7 @@ function useParticipantTiles(
|
|||||||
// connected, this is fine and we'll be in "all ghosts" mode.
|
// connected, this is fine and we'll be in "all ghosts" mode.
|
||||||
if (id !== "" && member === undefined) {
|
if (id !== "" && member === undefined) {
|
||||||
logger.warn(
|
logger.warn(
|
||||||
`Ruh, roh! No matrix member found for SFU participant '${id}': creating g-g-g-ghost!`
|
`Ruh, roh! No matrix member found for SFU participant '${id}': creating g-g-g-ghost!`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
allGhosts &&= member === undefined;
|
allGhosts &&= member === undefined;
|
||||||
@@ -542,11 +546,11 @@ function useParticipantTiles(
|
|||||||
return screenShareTile
|
return screenShareTile
|
||||||
? [userMediaTile, screenShareTile]
|
? [userMediaTile, screenShareTile]
|
||||||
: [userMediaTile];
|
: [userMediaTile];
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
PosthogAnalytics.instance.eventCallEnded.cacheParticipantCountChanged(
|
PosthogAnalytics.instance.eventCallEnded.cacheParticipantCountChanged(
|
||||||
tiles.length
|
tiles.length,
|
||||||
);
|
);
|
||||||
|
|
||||||
// If every item is a ghost, that probably means we're still connecting and
|
// If every item is a ghost, that probably means we're still connecting and
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ export const InviteModal: FC<Props> = ({ room, open, onDismiss }) => {
|
|||||||
const url = useMemo(
|
const url = useMemo(
|
||||||
() =>
|
() =>
|
||||||
getAbsoluteRoomUrl(room.roomId, room.name, roomSharedKey ?? undefined),
|
getAbsoluteRoomUrl(room.roomId, room.name, roomSharedKey ?? undefined),
|
||||||
[room, roomSharedKey]
|
[room, roomSharedKey],
|
||||||
);
|
);
|
||||||
const [, setCopied] = useClipboard(url);
|
const [, setCopied] = useClipboard(url);
|
||||||
const [toastOpen, setToastOpen] = useState(false);
|
const [toastOpen, setToastOpen] = useState(false);
|
||||||
@@ -53,7 +53,7 @@ export const InviteModal: FC<Props> = ({ room, open, onDismiss }) => {
|
|||||||
onDismiss();
|
onDismiss();
|
||||||
setToastOpen(true);
|
setToastOpen(true);
|
||||||
},
|
},
|
||||||
[setCopied, onDismiss]
|
[setCopied, onDismiss],
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ export const LayoutToggle: FC<Props> = ({ layout, setLayout, className }) => {
|
|||||||
|
|
||||||
const onChange = useCallback(
|
const onChange = useCallback(
|
||||||
(e: ChangeEvent<HTMLInputElement>) => setLayout(e.target.value as Layout),
|
(e: ChangeEvent<HTMLInputElement>) => setLayout(e.target.value as Layout),
|
||||||
[setLayout]
|
[setLayout],
|
||||||
);
|
);
|
||||||
|
|
||||||
const spotlightId = useId();
|
const spotlightId = useId();
|
||||||
|
|||||||
@@ -63,22 +63,22 @@ export const LobbyView: FC<Props> = ({
|
|||||||
|
|
||||||
const onAudioPress = useCallback(
|
const onAudioPress = useCallback(
|
||||||
() => muteStates.audio.setEnabled?.((e) => !e),
|
() => muteStates.audio.setEnabled?.((e) => !e),
|
||||||
[muteStates]
|
[muteStates],
|
||||||
);
|
);
|
||||||
const onVideoPress = useCallback(
|
const onVideoPress = useCallback(
|
||||||
() => muteStates.video.setEnabled?.((e) => !e),
|
() => muteStates.video.setEnabled?.((e) => !e),
|
||||||
[muteStates]
|
[muteStates],
|
||||||
);
|
);
|
||||||
|
|
||||||
const [settingsModalOpen, setSettingsModalOpen] = useState(false);
|
const [settingsModalOpen, setSettingsModalOpen] = useState(false);
|
||||||
|
|
||||||
const openSettings = useCallback(
|
const openSettings = useCallback(
|
||||||
() => setSettingsModalOpen(true),
|
() => setSettingsModalOpen(true),
|
||||||
[setSettingsModalOpen]
|
[setSettingsModalOpen],
|
||||||
);
|
);
|
||||||
const closeSettings = useCallback(
|
const closeSettings = useCallback(
|
||||||
() => setSettingsModalOpen(false),
|
() => setSettingsModalOpen(false),
|
||||||
[setSettingsModalOpen]
|
[setSettingsModalOpen],
|
||||||
);
|
);
|
||||||
|
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
|
|||||||
@@ -49,18 +49,18 @@ export interface MuteStates {
|
|||||||
|
|
||||||
function useMuteState(
|
function useMuteState(
|
||||||
device: MediaDevice,
|
device: MediaDevice,
|
||||||
enabledByDefault: () => boolean
|
enabledByDefault: () => boolean,
|
||||||
): MuteState {
|
): MuteState {
|
||||||
const [enabled, setEnabled] = useReactiveState<boolean>(
|
const [enabled, setEnabled] = useReactiveState<boolean>(
|
||||||
(prev) => device.available.length > 0 && (prev ?? enabledByDefault()),
|
(prev) => device.available.length > 0 && (prev ?? enabledByDefault()),
|
||||||
[device]
|
[device],
|
||||||
);
|
);
|
||||||
return useMemo(
|
return useMemo(
|
||||||
() =>
|
() =>
|
||||||
device.available.length === 0
|
device.available.length === 0
|
||||||
? deviceUnavailable
|
? deviceUnavailable
|
||||||
: { enabled, setEnabled },
|
: { enabled, setEnabled },
|
||||||
[device, enabled, setEnabled]
|
[device, enabled, setEnabled],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -69,7 +69,7 @@ export function useMuteStates(participantCount: number): MuteStates {
|
|||||||
|
|
||||||
const audio = useMuteState(
|
const audio = useMuteState(
|
||||||
devices.audioInput,
|
devices.audioInput,
|
||||||
() => participantCount <= MUTE_PARTICIPANT_COUNT
|
() => participantCount <= MUTE_PARTICIPANT_COUNT,
|
||||||
);
|
);
|
||||||
const video = useMuteState(devices.videoInput, () => true);
|
const video = useMuteState(devices.videoInput, () => true);
|
||||||
|
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ export const RageshakeRequestModal: FC<Props> = ({
|
|||||||
<Modal title={t("Debug log request")} open={open} onDismiss={onDismiss}>
|
<Modal title={t("Debug log request")} open={open} onDismiss={onDismiss}>
|
||||||
<Body>
|
<Body>
|
||||||
{t(
|
{t(
|
||||||
"Another user on this call is having an issue. In order to better diagnose these issues we'd like to collect a debug log."
|
"Another user on this call is having an issue. In order to better diagnose these issues we'd like to collect a debug log.",
|
||||||
)}
|
)}
|
||||||
</Body>
|
</Body>
|
||||||
<FieldRow>
|
<FieldRow>
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ export const RoomAuthView: FC = () => {
|
|||||||
setError(error);
|
setError(error);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
[registerPasswordlessUser]
|
[registerPasswordlessUser],
|
||||||
);
|
);
|
||||||
|
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|||||||
@@ -81,7 +81,7 @@ export const RoomPage: FC = () => {
|
|||||||
hideHeader={hideHeader}
|
hideHeader={hideHeader}
|
||||||
/>
|
/>
|
||||||
),
|
),
|
||||||
[client, passwordlessUser, confineToRoom, preload, hideHeader]
|
[client, passwordlessUser, confineToRoom, preload, hideHeader],
|
||||||
);
|
);
|
||||||
|
|
||||||
let content: ReactNode;
|
let content: ReactNode;
|
||||||
|
|||||||
@@ -82,14 +82,14 @@ export const VideoPreview: FC<Props> = ({
|
|||||||
},
|
},
|
||||||
(error) => {
|
(error) => {
|
||||||
logger.error("Error while creating preview Tracks:", error);
|
logger.error("Error while creating preview Tracks:", error);
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
const videoTrack = useMemo(
|
const videoTrack = useMemo(
|
||||||
() =>
|
() =>
|
||||||
tracks?.find((t) => t.kind === Track.Kind.Video) as
|
tracks?.find((t) => t.kind === Track.Kind.Video) as
|
||||||
| LocalVideoTrack
|
| LocalVideoTrack
|
||||||
| undefined,
|
| undefined,
|
||||||
[tracks]
|
[tracks],
|
||||||
);
|
);
|
||||||
|
|
||||||
const videoEl = useRef<HTMLVideoElement | null>(null);
|
const videoEl = useRef<HTMLVideoElement | null>(null);
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ import { deepCompare } from "matrix-js-sdk/src/utils";
|
|||||||
import { LivekitFocus } from "../livekit/LivekitFocus";
|
import { LivekitFocus } from "../livekit/LivekitFocus";
|
||||||
|
|
||||||
function getActiveFocus(
|
function getActiveFocus(
|
||||||
rtcSession: MatrixRTCSession
|
rtcSession: MatrixRTCSession,
|
||||||
): LivekitFocus | undefined {
|
): LivekitFocus | undefined {
|
||||||
const oldestMembership = rtcSession.getOldestMembership();
|
const oldestMembership = rtcSession.getOldestMembership();
|
||||||
return oldestMembership?.getActiveFoci()[0] as LivekitFocus;
|
return oldestMembership?.getActiveFoci()[0] as LivekitFocus;
|
||||||
@@ -36,10 +36,10 @@ function getActiveFocus(
|
|||||||
* and the same focus.
|
* and the same focus.
|
||||||
*/
|
*/
|
||||||
export function useActiveFocus(
|
export function useActiveFocus(
|
||||||
rtcSession: MatrixRTCSession
|
rtcSession: MatrixRTCSession,
|
||||||
): LivekitFocus | undefined {
|
): LivekitFocus | undefined {
|
||||||
const [activeFocus, setActiveFocus] = useState(() =>
|
const [activeFocus, setActiveFocus] = useState(() =>
|
||||||
getActiveFocus(rtcSession)
|
getActiveFocus(rtcSession),
|
||||||
);
|
);
|
||||||
|
|
||||||
const onMembershipsChanged = useCallback(() => {
|
const onMembershipsChanged = useCallback(() => {
|
||||||
@@ -53,13 +53,13 @@ export function useActiveFocus(
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
rtcSession.on(
|
rtcSession.on(
|
||||||
MatrixRTCSessionEvent.MembershipsChanged,
|
MatrixRTCSessionEvent.MembershipsChanged,
|
||||||
onMembershipsChanged
|
onMembershipsChanged,
|
||||||
);
|
);
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
rtcSession.off(
|
rtcSession.off(
|
||||||
MatrixRTCSessionEvent.MembershipsChanged,
|
MatrixRTCSessionEvent.MembershipsChanged,
|
||||||
onMembershipsChanged
|
onMembershipsChanged,
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ export function useFullscreen<T>(items: TileDescriptor<T>[]): {
|
|||||||
prevItem == null
|
prevItem == null
|
||||||
? null
|
? null
|
||||||
: items.find((i) => i.id === prevItem.id) ?? null,
|
: items.find((i) => i.id === prevItem.id) ?? null,
|
||||||
[items]
|
[items],
|
||||||
);
|
);
|
||||||
|
|
||||||
const latestItems = useRef<TileDescriptor<T>[]>(items);
|
const latestItems = useRef<TileDescriptor<T>[]>(items);
|
||||||
@@ -80,15 +80,15 @@ export function useFullscreen<T>(items: TileDescriptor<T>[]): {
|
|||||||
setFullscreenItem(
|
setFullscreenItem(
|
||||||
latestFullscreenItem.current === null
|
latestFullscreenItem.current === null
|
||||||
? latestItems.current.find((i) => i.id === itemId) ?? null
|
? latestItems.current.find((i) => i.id === itemId) ?? null
|
||||||
: null
|
: null,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
[setFullscreenItem]
|
[setFullscreenItem],
|
||||||
);
|
);
|
||||||
|
|
||||||
const exitFullscreenCallback = useCallback(
|
const exitFullscreenCallback = useCallback(
|
||||||
() => setFullscreenItem(null),
|
() => setFullscreenItem(null),
|
||||||
[setFullscreenItem]
|
[setFullscreenItem],
|
||||||
);
|
);
|
||||||
|
|
||||||
useLayoutEffect(() => {
|
useLayoutEffect(() => {
|
||||||
@@ -103,7 +103,7 @@ export function useFullscreen<T>(items: TileDescriptor<T>[]): {
|
|||||||
useFullscreenChange(
|
useFullscreenChange(
|
||||||
useCallback(() => {
|
useCallback(() => {
|
||||||
if (!isFullscreen()) setFullscreenItem(null);
|
if (!isFullscreen()) setFullscreenItem(null);
|
||||||
}, [setFullscreenItem])
|
}, [setFullscreenItem]),
|
||||||
);
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -23,6 +23,6 @@ import { useRoomState } from "./useRoomState";
|
|||||||
export function useJoinRule(room: Room): JoinRule {
|
export function useJoinRule(room: Room): JoinRule {
|
||||||
return useRoomState(
|
return useRoomState(
|
||||||
room,
|
room,
|
||||||
useCallback((state) => state.getJoinRule(), [])
|
useCallback((state) => state.getJoinRule(), []),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ export interface GroupCallLoadState {
|
|||||||
export const useLoadGroupCall = (
|
export const useLoadGroupCall = (
|
||||||
client: MatrixClient,
|
client: MatrixClient,
|
||||||
roomIdOrAlias: string,
|
roomIdOrAlias: string,
|
||||||
viaServers: string[]
|
viaServers: string[],
|
||||||
): GroupCallStatus => {
|
): GroupCallStatus => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [state, setState] = useState<GroupCallStatus>({ kind: "loading" });
|
const [state, setState] = useState<GroupCallStatus>({ kind: "loading" });
|
||||||
@@ -70,7 +70,7 @@ export const useLoadGroupCall = (
|
|||||||
// join anyway but the js-sdk recreates the room if you pass the alias for a
|
// join anyway but the js-sdk recreates the room if you pass the alias for a
|
||||||
// room you're already joined to (which it probably ought not to).
|
// room you're already joined to (which it probably ought not to).
|
||||||
const lookupResult = await client.getRoomIdForAlias(
|
const lookupResult = await client.getRoomIdForAlias(
|
||||||
roomIdOrAlias.toLowerCase()
|
roomIdOrAlias.toLowerCase(),
|
||||||
);
|
);
|
||||||
logger.info(`${roomIdOrAlias} resolved to ${lookupResult.room_id}`);
|
logger.info(`${roomIdOrAlias} resolved to ${lookupResult.room_id}`);
|
||||||
room = client.getRoom(lookupResult.room_id);
|
room = client.getRoom(lookupResult.room_id);
|
||||||
@@ -81,7 +81,7 @@ export const useLoadGroupCall = (
|
|||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
logger.info(
|
logger.info(
|
||||||
`Already in room ${lookupResult.room_id}, not rejoining.`
|
`Already in room ${lookupResult.room_id}, not rejoining.`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -92,7 +92,7 @@ export const useLoadGroupCall = (
|
|||||||
}
|
}
|
||||||
|
|
||||||
logger.info(
|
logger.info(
|
||||||
`Joined ${roomIdOrAlias}, waiting room to be ready for group calls`
|
`Joined ${roomIdOrAlias}, waiting room to be ready for group calls`,
|
||||||
);
|
);
|
||||||
await client.waitUntilRoomReadyForGroupCalls(room.roomId);
|
await client.waitUntilRoomReadyForGroupCalls(room.roomId);
|
||||||
logger.info(`${roomIdOrAlias}, is ready for group calls`);
|
logger.info(`${roomIdOrAlias}, is ready for group calls`);
|
||||||
@@ -110,7 +110,7 @@ export const useLoadGroupCall = (
|
|||||||
const waitForClientSyncing = async (): Promise<void> => {
|
const waitForClientSyncing = async (): Promise<void> => {
|
||||||
if (client.getSyncState() !== SyncState.Syncing) {
|
if (client.getSyncState() !== SyncState.Syncing) {
|
||||||
logger.debug(
|
logger.debug(
|
||||||
"useLoadGroupCall: waiting for client to start syncing..."
|
"useLoadGroupCall: waiting for client to start syncing...",
|
||||||
);
|
);
|
||||||
await new Promise<void>((resolve) => {
|
await new Promise<void>((resolve) => {
|
||||||
const onSync = (): void => {
|
const onSync = (): void => {
|
||||||
|
|||||||
@@ -22,6 +22,6 @@ import { useRoomState } from "./useRoomState";
|
|||||||
export function useRoomAvatar(room: Room): string | null {
|
export function useRoomAvatar(room: Room): string | null {
|
||||||
return useRoomState(
|
return useRoomState(
|
||||||
room,
|
room,
|
||||||
useCallback(() => room.getMxcAvatarUrl(), [room])
|
useCallback(() => room.getMxcAvatarUrl(), [room]),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ export const useRoomState = <T>(room: Room, f: (state: RoomState) => T): T => {
|
|||||||
useTypedEventEmitter(
|
useTypedEventEmitter(
|
||||||
room,
|
room,
|
||||||
RoomStateEvent.Update,
|
RoomStateEvent.Update,
|
||||||
useCallback(() => setNumUpdates((n) => n + 1), [setNumUpdates])
|
useCallback(() => setNumUpdates((n) => n + 1), [setNumUpdates]),
|
||||||
);
|
);
|
||||||
// We want any change to the update counter to trigger an update here
|
// We want any change to the update counter to trigger an update here
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ export function enterRTCSession(rtcSession: MatrixRTCSession): void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function leaveRTCSession(
|
export async function leaveRTCSession(
|
||||||
rtcSession: MatrixRTCSession
|
rtcSession: MatrixRTCSession,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
//groupCallOTelMembership?.onLeaveCall();
|
//groupCallOTelMembership?.onLeaveCall();
|
||||||
await rtcSession.leaveRoomSession();
|
await rtcSession.leaveRoomSession();
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ export const FeedbackSettingsTab: FC<Props> = ({ roomId }) => {
|
|||||||
sendRageshakeRequest(roomId, rageshakeRequestId);
|
sendRageshakeRequest(roomId, rageshakeRequestId);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[submitRageshake, roomId, sendRageshakeRequest]
|
[submitRageshake, roomId, sendRageshakeRequest],
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -65,7 +65,7 @@ export const FeedbackSettingsTab: FC<Props> = ({ roomId }) => {
|
|||||||
<h4 className={styles.label}>{t("Submit feedback")}</h4>
|
<h4 className={styles.label}>{t("Submit feedback")}</h4>
|
||||||
<Body>
|
<Body>
|
||||||
{t(
|
{t(
|
||||||
"If you are experiencing issues or simply would like to provide some feedback, please send us a short description below."
|
"If you are experiencing issues or simply would like to provide some feedback, please send us a short description below.",
|
||||||
)}
|
)}
|
||||||
</Body>
|
</Body>
|
||||||
<form onSubmit={onSubmitFeedback}>
|
<form onSubmit={onSubmitFeedback}>
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ export const SettingsModal: FC<Props> = (props) => {
|
|||||||
// Generate a `SelectInput` with a list of devices for a given device kind.
|
// Generate a `SelectInput` with a list of devices for a given device kind.
|
||||||
const generateDeviceSelection = (
|
const generateDeviceSelection = (
|
||||||
devices: MediaDevice,
|
devices: MediaDevice,
|
||||||
caption: string
|
caption: string,
|
||||||
): ReactNode => {
|
): ReactNode => {
|
||||||
if (devices.available.length == 0) return null;
|
if (devices.available.length == 0) return null;
|
||||||
|
|
||||||
@@ -100,7 +100,7 @@ export const SettingsModal: FC<Props> = (props) => {
|
|||||||
(tab: Key) => {
|
(tab: Key) => {
|
||||||
setSelectedTab(tab.toString());
|
setSelectedTab(tab.toString());
|
||||||
},
|
},
|
||||||
[setSelectedTab]
|
[setSelectedTab],
|
||||||
);
|
);
|
||||||
|
|
||||||
const optInDescription = (
|
const optInDescription = (
|
||||||
|
|||||||
@@ -133,7 +133,7 @@ class IndexedDBLogStore {
|
|||||||
|
|
||||||
public constructor(
|
public constructor(
|
||||||
private indexedDB: IDBFactory,
|
private indexedDB: IDBFactory,
|
||||||
private loggerInstance: ConsoleLogger
|
private loggerInstance: ConsoleLogger,
|
||||||
) {
|
) {
|
||||||
this.id = "instance-" + randomString(16);
|
this.id = "instance-" + randomString(16);
|
||||||
|
|
||||||
@@ -177,7 +177,7 @@ class IndexedDBLogStore {
|
|||||||
logObjStore.createIndex("id", "id", { unique: false });
|
logObjStore.createIndex("id", "id", { unique: false });
|
||||||
|
|
||||||
logObjStore.add(
|
logObjStore.add(
|
||||||
this.generateLogEntry(new Date() + " ::: Log database was created.")
|
this.generateLogEntry(new Date() + " ::: Log database was created."),
|
||||||
);
|
);
|
||||||
|
|
||||||
// This records the last time each instance ID generated a log message, such
|
// This records the last time each instance ID generated a log message, such
|
||||||
@@ -208,7 +208,7 @@ class IndexedDBLogStore {
|
|||||||
{
|
{
|
||||||
leading: false,
|
leading: false,
|
||||||
trailing: true,
|
trailing: true,
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -366,8 +366,8 @@ class IndexedDBLogStore {
|
|||||||
txn.onerror = (): void => {
|
txn.onerror = (): void => {
|
||||||
reject(
|
reject(
|
||||||
new Error(
|
new Error(
|
||||||
"Failed to delete logs for " + `'${id}' : ${txn?.error?.message}`
|
"Failed to delete logs for " + `'${id}' : ${txn?.error?.message}`,
|
||||||
)
|
),
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
// delete last modified entries
|
// delete last modified entries
|
||||||
@@ -410,7 +410,7 @@ class IndexedDBLogStore {
|
|||||||
},
|
},
|
||||||
(err) => {
|
(err) => {
|
||||||
logger.error(err);
|
logger.error(err);
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return logs;
|
return logs;
|
||||||
@@ -445,7 +445,7 @@ class IndexedDBLogStore {
|
|||||||
function selectQuery<T>(
|
function selectQuery<T>(
|
||||||
store: IDBObjectStore,
|
store: IDBObjectStore,
|
||||||
keyRange: IDBKeyRange | undefined,
|
keyRange: IDBKeyRange | undefined,
|
||||||
resultMapper: (cursor: IDBCursorWithValue) => T
|
resultMapper: (cursor: IDBCursorWithValue) => T,
|
||||||
): Promise<T[]> {
|
): Promise<T[]> {
|
||||||
const query = store.openCursor(keyRange);
|
const query = store.openCursor(keyRange);
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
@@ -510,7 +510,7 @@ function tryInitStorage(): Promise<void> {
|
|||||||
if (indexedDB) {
|
if (indexedDB) {
|
||||||
global.mx_rage_store = new IndexedDBLogStore(
|
global.mx_rage_store = new IndexedDBLogStore(
|
||||||
indexedDB,
|
indexedDB,
|
||||||
global.mx_rage_logger
|
global.mx_rage_logger,
|
||||||
);
|
);
|
||||||
global.mx_rage_initStoragePromise = global.mx_rage_store.connect();
|
global.mx_rage_initStoragePromise = global.mx_rage_store.connect();
|
||||||
return global.mx_rage_initStoragePromise;
|
return global.mx_rage_initStoragePromise;
|
||||||
@@ -547,7 +547,7 @@ export async function getLogsForReport(): Promise<LogEntry[]> {
|
|||||||
type StringifyReplacer = (
|
type StringifyReplacer = (
|
||||||
this: unknown,
|
this: unknown,
|
||||||
key: string,
|
key: string,
|
||||||
value: unknown
|
value: unknown,
|
||||||
) => unknown;
|
) => unknown;
|
||||||
|
|
||||||
// From https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors/Cyclic_object_value#circular_references
|
// From https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors/Cyclic_object_value#circular_references
|
||||||
@@ -600,7 +600,7 @@ export function setLogExtension(extension: LogExtensionFunc): void {
|
|||||||
logger.methodFactory = function (
|
logger.methodFactory = function (
|
||||||
methodName,
|
methodName,
|
||||||
configLevel,
|
configLevel,
|
||||||
loggerName
|
loggerName,
|
||||||
): LoggingMethod {
|
): LoggingMethod {
|
||||||
const rawMethod = originalFactory(methodName, configLevel, loggerName);
|
const rawMethod = originalFactory(methodName, configLevel, loggerName);
|
||||||
|
|
||||||
|
|||||||
@@ -95,12 +95,12 @@ export function useSubmitRageshake(): {
|
|||||||
const body = new FormData();
|
const body = new FormData();
|
||||||
body.append(
|
body.append(
|
||||||
"text",
|
"text",
|
||||||
description ?? "User did not supply any additional text."
|
description ?? "User did not supply any additional text.",
|
||||||
);
|
);
|
||||||
body.append("app", "matrix-video-chat");
|
body.append("app", "matrix-video-chat");
|
||||||
body.append(
|
body.append(
|
||||||
"version",
|
"version",
|
||||||
(import.meta.env.VITE_APP_VERSION as string) || "dev"
|
(import.meta.env.VITE_APP_VERSION as string) || "dev",
|
||||||
);
|
);
|
||||||
body.append("user_agent", userAgent);
|
body.append("user_agent", userAgent);
|
||||||
body.append("installed_pwa", "false");
|
body.append("installed_pwa", "false");
|
||||||
@@ -132,22 +132,22 @@ export function useSubmitRageshake(): {
|
|||||||
|
|
||||||
body.append(
|
body.append(
|
||||||
"cross_signing_ready",
|
"cross_signing_ready",
|
||||||
String(await client.isCrossSigningReady())
|
String(await client.isCrossSigningReady()),
|
||||||
);
|
);
|
||||||
body.append(
|
body.append(
|
||||||
"cross_signing_supported_by_hs",
|
"cross_signing_supported_by_hs",
|
||||||
String(
|
String(
|
||||||
await client.doesServerSupportUnstableFeature(
|
await client.doesServerSupportUnstableFeature(
|
||||||
"org.matrix.e2e_cross_signing"
|
"org.matrix.e2e_cross_signing",
|
||||||
)
|
),
|
||||||
)
|
),
|
||||||
);
|
);
|
||||||
body.append("cross_signing_key", crossSigning.getId()!);
|
body.append("cross_signing_key", crossSigning.getId()!);
|
||||||
body.append(
|
body.append(
|
||||||
"cross_signing_privkey_in_secret_storage",
|
"cross_signing_privkey_in_secret_storage",
|
||||||
String(
|
String(
|
||||||
!!(await crossSigning.isStoredInSecretStorage(secretStorage))
|
!!(await crossSigning.isStoredInSecretStorage(secretStorage)),
|
||||||
)
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
const pkCache = client.getCrossSigningCacheCallbacks();
|
const pkCache = client.getCrossSigningCacheCallbacks();
|
||||||
@@ -157,8 +157,8 @@ export function useSubmitRageshake(): {
|
|||||||
!!(
|
!!(
|
||||||
pkCache?.getCrossSigningKeyCache &&
|
pkCache?.getCrossSigningKeyCache &&
|
||||||
(await pkCache.getCrossSigningKeyCache("master"))
|
(await pkCache.getCrossSigningKeyCache("master"))
|
||||||
)
|
),
|
||||||
)
|
),
|
||||||
);
|
);
|
||||||
body.append(
|
body.append(
|
||||||
"cross_signing_self_signing_privkey_cached",
|
"cross_signing_self_signing_privkey_cached",
|
||||||
@@ -166,8 +166,8 @@ export function useSubmitRageshake(): {
|
|||||||
!!(
|
!!(
|
||||||
pkCache?.getCrossSigningKeyCache &&
|
pkCache?.getCrossSigningKeyCache &&
|
||||||
(await pkCache.getCrossSigningKeyCache("self_signing"))
|
(await pkCache.getCrossSigningKeyCache("self_signing"))
|
||||||
)
|
),
|
||||||
)
|
),
|
||||||
);
|
);
|
||||||
body.append(
|
body.append(
|
||||||
"cross_signing_user_signing_privkey_cached",
|
"cross_signing_user_signing_privkey_cached",
|
||||||
@@ -175,32 +175,32 @@ export function useSubmitRageshake(): {
|
|||||||
!!(
|
!!(
|
||||||
pkCache?.getCrossSigningKeyCache &&
|
pkCache?.getCrossSigningKeyCache &&
|
||||||
(await pkCache.getCrossSigningKeyCache("user_signing"))
|
(await pkCache.getCrossSigningKeyCache("user_signing"))
|
||||||
)
|
),
|
||||||
)
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
body.append(
|
body.append(
|
||||||
"secret_storage_ready",
|
"secret_storage_ready",
|
||||||
String(await client.isSecretStorageReady())
|
String(await client.isSecretStorageReady()),
|
||||||
);
|
);
|
||||||
body.append(
|
body.append(
|
||||||
"secret_storage_key_in_account",
|
"secret_storage_key_in_account",
|
||||||
String(!!(await secretStorage.hasKey()))
|
String(!!(await secretStorage.hasKey())),
|
||||||
);
|
);
|
||||||
|
|
||||||
body.append(
|
body.append(
|
||||||
"session_backup_key_in_secret_storage",
|
"session_backup_key_in_secret_storage",
|
||||||
String(!!(await client.isKeyBackupKeyStored()))
|
String(!!(await client.isKeyBackupKeyStored())),
|
||||||
);
|
);
|
||||||
const sessionBackupKeyFromCache =
|
const sessionBackupKeyFromCache =
|
||||||
await client.crypto!.getSessionBackupPrivateKey();
|
await client.crypto!.getSessionBackupPrivateKey();
|
||||||
body.append(
|
body.append(
|
||||||
"session_backup_key_cached",
|
"session_backup_key_cached",
|
||||||
String(!!sessionBackupKeyFromCache)
|
String(!!sessionBackupKeyFromCache),
|
||||||
);
|
);
|
||||||
body.append(
|
body.append(
|
||||||
"session_backup_key_well_formed",
|
"session_backup_key_well_formed",
|
||||||
String(sessionBackupKeyFromCache instanceof Uint8Array)
|
String(sessionBackupKeyFromCache instanceof Uint8Array),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -214,7 +214,7 @@ export function useSubmitRageshake(): {
|
|||||||
try {
|
try {
|
||||||
body.append(
|
body.append(
|
||||||
"storageManager_persisted",
|
"storageManager_persisted",
|
||||||
String(await navigator.storage.persisted())
|
String(await navigator.storage.persisted()),
|
||||||
);
|
);
|
||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
} else if (document.hasStorageAccess) {
|
} else if (document.hasStorageAccess) {
|
||||||
@@ -222,7 +222,7 @@ export function useSubmitRageshake(): {
|
|||||||
try {
|
try {
|
||||||
body.append(
|
body.append(
|
||||||
"storageManager_persisted",
|
"storageManager_persisted",
|
||||||
String(await document.hasStorageAccess())
|
String(await document.hasStorageAccess()),
|
||||||
);
|
);
|
||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
}
|
}
|
||||||
@@ -240,7 +240,7 @@ export function useSubmitRageshake(): {
|
|||||||
Object.keys(estimate.usageDetails).forEach((k) => {
|
Object.keys(estimate.usageDetails).forEach((k) => {
|
||||||
body.append(
|
body.append(
|
||||||
`storageManager_usage_${k}`,
|
`storageManager_usage_${k}`,
|
||||||
String(estimate.usageDetails![k])
|
String(estimate.usageDetails![k]),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -257,14 +257,14 @@ export function useSubmitRageshake(): {
|
|||||||
body.append(
|
body.append(
|
||||||
"file",
|
"file",
|
||||||
gzip(ElementCallOpenTelemetry.instance.rageshakeProcessor!.dump()),
|
gzip(ElementCallOpenTelemetry.instance.rageshakeProcessor!.dump()),
|
||||||
"traces.json.gz"
|
"traces.json.gz",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (opts.rageshakeRequestId) {
|
if (opts.rageshakeRequestId) {
|
||||||
body.append(
|
body.append(
|
||||||
"group_call_rageshake_request_id",
|
"group_call_rageshake_request_id",
|
||||||
opts.rageshakeRequestId
|
opts.rageshakeRequestId,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -279,7 +279,7 @@ export function useSubmitRageshake(): {
|
|||||||
logger.error(error);
|
logger.error(error);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[client, sending]
|
[client, sending],
|
||||||
);
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@@ -292,7 +292,7 @@ export function useSubmitRageshake(): {
|
|||||||
|
|
||||||
export function useRageshakeRequest(): (
|
export function useRageshakeRequest(): (
|
||||||
roomId: string,
|
roomId: string,
|
||||||
rageshakeRequestId: string
|
rageshakeRequestId: string,
|
||||||
) => void {
|
) => void {
|
||||||
const { client } = useClient();
|
const { client } = useClient();
|
||||||
|
|
||||||
@@ -302,14 +302,14 @@ export function useRageshakeRequest(): (
|
|||||||
request_id: rageshakeRequestId,
|
request_id: rageshakeRequestId,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
[client]
|
[client],
|
||||||
);
|
);
|
||||||
|
|
||||||
return sendRageshakeRequest;
|
return sendRageshakeRequest;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function useRageshakeRequestModal(
|
export function useRageshakeRequestModal(
|
||||||
roomId: string
|
roomId: string,
|
||||||
): ComponentProps<typeof RageshakeRequestModal> {
|
): ComponentProps<typeof RageshakeRequestModal> {
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
const onDismiss = useCallback(() => setOpen(false), [setOpen]);
|
const onDismiss = useCallback(() => setOpen(false), [setOpen]);
|
||||||
|
|||||||
@@ -38,13 +38,13 @@ export const useSetting = <T>(name: string, defaultValue: T): Setting<T> => {
|
|||||||
|
|
||||||
const value = useMemo(
|
const value = useMemo(
|
||||||
() => (item == null ? defaultValue : JSON.parse(item)),
|
() => (item == null ? defaultValue : JSON.parse(item)),
|
||||||
[item, defaultValue]
|
[item, defaultValue],
|
||||||
);
|
);
|
||||||
const setValue = useCallback(
|
const setValue = useCallback(
|
||||||
(value: T) => {
|
(value: T) => {
|
||||||
setItem(JSON.stringify(value));
|
setItem(JSON.stringify(value));
|
||||||
},
|
},
|
||||||
[setItem]
|
[setItem],
|
||||||
);
|
);
|
||||||
|
|
||||||
return [value, setValue];
|
return [value, setValue];
|
||||||
@@ -94,7 +94,7 @@ export const useOptInAnalytics = (): DisableableSetting<boolean | null> => {
|
|||||||
export const useEnableE2EE = (): DisableableSetting<boolean | null> => {
|
export const useEnableE2EE = (): DisableableSetting<boolean | null> => {
|
||||||
const settingVal = useSetting<boolean | null>(
|
const settingVal = useSetting<boolean | null>(
|
||||||
"enable-end-to-end-encryption",
|
"enable-end-to-end-encryption",
|
||||||
true
|
true,
|
||||||
);
|
);
|
||||||
if (isE2EESupported()) return settingVal;
|
if (isE2EESupported()) return settingVal;
|
||||||
|
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ interface TabContainerProps<T> extends TabListProps<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function TabContainer<T extends object>(
|
export function TabContainer<T extends object>(
|
||||||
props: TabContainerProps<T>
|
props: TabContainerProps<T>,
|
||||||
): JSX.Element {
|
): JSX.Element {
|
||||||
const state = useTabListState<T>(props);
|
const state = useTabListState<T>(props);
|
||||||
const ref = useRef<HTMLUListElement>(null);
|
const ref = useRef<HTMLUListElement>(null);
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ export const Headline = forwardRef<HTMLHeadingElement, TypographyProps>(
|
|||||||
overflowEllipsis,
|
overflowEllipsis,
|
||||||
...rest
|
...rest
|
||||||
},
|
},
|
||||||
ref
|
ref,
|
||||||
) => {
|
) => {
|
||||||
return createElement(
|
return createElement(
|
||||||
Component,
|
Component,
|
||||||
@@ -48,13 +48,13 @@ export const Headline = forwardRef<HTMLHeadingElement, TypographyProps>(
|
|||||||
className: classNames(
|
className: classNames(
|
||||||
styles[fontWeight ?? ""],
|
styles[fontWeight ?? ""],
|
||||||
{ [styles.overflowEllipsis]: overflowEllipsis },
|
{ [styles.overflowEllipsis]: overflowEllipsis },
|
||||||
className
|
className,
|
||||||
),
|
),
|
||||||
ref,
|
ref,
|
||||||
},
|
},
|
||||||
children
|
children,
|
||||||
);
|
);
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
export const Title = forwardRef<HTMLHeadingElement, TypographyProps>(
|
export const Title = forwardRef<HTMLHeadingElement, TypographyProps>(
|
||||||
@@ -67,7 +67,7 @@ export const Title = forwardRef<HTMLHeadingElement, TypographyProps>(
|
|||||||
overflowEllipsis,
|
overflowEllipsis,
|
||||||
...rest
|
...rest
|
||||||
},
|
},
|
||||||
ref
|
ref,
|
||||||
) => {
|
) => {
|
||||||
return createElement(
|
return createElement(
|
||||||
Component,
|
Component,
|
||||||
@@ -76,13 +76,13 @@ export const Title = forwardRef<HTMLHeadingElement, TypographyProps>(
|
|||||||
className: classNames(
|
className: classNames(
|
||||||
styles[fontWeight ?? ""],
|
styles[fontWeight ?? ""],
|
||||||
{ [styles.overflowEllipsis]: overflowEllipsis },
|
{ [styles.overflowEllipsis]: overflowEllipsis },
|
||||||
className
|
className,
|
||||||
),
|
),
|
||||||
ref,
|
ref,
|
||||||
},
|
},
|
||||||
children
|
children,
|
||||||
);
|
);
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
export const Subtitle = forwardRef<HTMLParagraphElement, TypographyProps>(
|
export const Subtitle = forwardRef<HTMLParagraphElement, TypographyProps>(
|
||||||
@@ -95,7 +95,7 @@ export const Subtitle = forwardRef<HTMLParagraphElement, TypographyProps>(
|
|||||||
overflowEllipsis,
|
overflowEllipsis,
|
||||||
...rest
|
...rest
|
||||||
},
|
},
|
||||||
ref
|
ref,
|
||||||
) => {
|
) => {
|
||||||
return createElement(
|
return createElement(
|
||||||
Component,
|
Component,
|
||||||
@@ -104,13 +104,13 @@ export const Subtitle = forwardRef<HTMLParagraphElement, TypographyProps>(
|
|||||||
className: classNames(
|
className: classNames(
|
||||||
styles[fontWeight ?? ""],
|
styles[fontWeight ?? ""],
|
||||||
{ [styles.overflowEllipsis]: overflowEllipsis },
|
{ [styles.overflowEllipsis]: overflowEllipsis },
|
||||||
className
|
className,
|
||||||
),
|
),
|
||||||
ref,
|
ref,
|
||||||
},
|
},
|
||||||
children
|
children,
|
||||||
);
|
);
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
export const Body = forwardRef<HTMLParagraphElement, TypographyProps>(
|
export const Body = forwardRef<HTMLParagraphElement, TypographyProps>(
|
||||||
@@ -123,7 +123,7 @@ export const Body = forwardRef<HTMLParagraphElement, TypographyProps>(
|
|||||||
overflowEllipsis,
|
overflowEllipsis,
|
||||||
...rest
|
...rest
|
||||||
},
|
},
|
||||||
ref
|
ref,
|
||||||
) => {
|
) => {
|
||||||
return createElement(
|
return createElement(
|
||||||
Component,
|
Component,
|
||||||
@@ -132,13 +132,13 @@ export const Body = forwardRef<HTMLParagraphElement, TypographyProps>(
|
|||||||
className: classNames(
|
className: classNames(
|
||||||
styles[fontWeight ?? ""],
|
styles[fontWeight ?? ""],
|
||||||
{ [styles.overflowEllipsis]: overflowEllipsis },
|
{ [styles.overflowEllipsis]: overflowEllipsis },
|
||||||
className
|
className,
|
||||||
),
|
),
|
||||||
ref,
|
ref,
|
||||||
},
|
},
|
||||||
children
|
children,
|
||||||
);
|
);
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
export const Caption = forwardRef<HTMLParagraphElement, TypographyProps>(
|
export const Caption = forwardRef<HTMLParagraphElement, TypographyProps>(
|
||||||
@@ -151,7 +151,7 @@ export const Caption = forwardRef<HTMLParagraphElement, TypographyProps>(
|
|||||||
overflowEllipsis,
|
overflowEllipsis,
|
||||||
...rest
|
...rest
|
||||||
},
|
},
|
||||||
ref
|
ref,
|
||||||
) => {
|
) => {
|
||||||
return createElement(
|
return createElement(
|
||||||
Component,
|
Component,
|
||||||
@@ -161,13 +161,13 @@ export const Caption = forwardRef<HTMLParagraphElement, TypographyProps>(
|
|||||||
styles.caption,
|
styles.caption,
|
||||||
styles[fontWeight ?? ""],
|
styles[fontWeight ?? ""],
|
||||||
{ [styles.overflowEllipsis]: overflowEllipsis },
|
{ [styles.overflowEllipsis]: overflowEllipsis },
|
||||||
className
|
className,
|
||||||
),
|
),
|
||||||
ref,
|
ref,
|
||||||
},
|
},
|
||||||
children
|
children,
|
||||||
);
|
);
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
export const Micro = forwardRef<HTMLParagraphElement, TypographyProps>(
|
export const Micro = forwardRef<HTMLParagraphElement, TypographyProps>(
|
||||||
@@ -180,7 +180,7 @@ export const Micro = forwardRef<HTMLParagraphElement, TypographyProps>(
|
|||||||
overflowEllipsis,
|
overflowEllipsis,
|
||||||
...rest
|
...rest
|
||||||
},
|
},
|
||||||
ref
|
ref,
|
||||||
) => {
|
) => {
|
||||||
return createElement(
|
return createElement(
|
||||||
Component,
|
Component,
|
||||||
@@ -190,13 +190,13 @@ export const Micro = forwardRef<HTMLParagraphElement, TypographyProps>(
|
|||||||
styles.micro,
|
styles.micro,
|
||||||
styles[fontWeight ?? ""],
|
styles[fontWeight ?? ""],
|
||||||
{ [styles.overflowEllipsis]: overflowEllipsis },
|
{ [styles.overflowEllipsis]: overflowEllipsis },
|
||||||
className
|
className,
|
||||||
),
|
),
|
||||||
ref,
|
ref,
|
||||||
},
|
},
|
||||||
children
|
children,
|
||||||
);
|
);
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
interface LinkProps extends TypographyProps {
|
interface LinkProps extends TypographyProps {
|
||||||
@@ -217,7 +217,7 @@ export const Link = forwardRef<HTMLAnchorElement, LinkProps>(
|
|||||||
overflowEllipsis,
|
overflowEllipsis,
|
||||||
...rest
|
...rest
|
||||||
},
|
},
|
||||||
ref
|
ref,
|
||||||
) => {
|
) => {
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
@@ -246,11 +246,11 @@ export const Link = forwardRef<HTMLAnchorElement, LinkProps>(
|
|||||||
styles[color],
|
styles[color],
|
||||||
styles[fontWeight ?? ""],
|
styles[fontWeight ?? ""],
|
||||||
{ [styles.overflowEllipsis]: overflowEllipsis },
|
{ [styles.overflowEllipsis]: overflowEllipsis },
|
||||||
className
|
className,
|
||||||
),
|
),
|
||||||
ref: ref,
|
ref: ref,
|
||||||
},
|
},
|
||||||
children
|
children,
|
||||||
);
|
);
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ export function useCallViewKeyboardShortcuts(
|
|||||||
focusElement: RefObject<HTMLElement | null>,
|
focusElement: RefObject<HTMLElement | null>,
|
||||||
toggleMicrophoneMuted: () => void,
|
toggleMicrophoneMuted: () => void,
|
||||||
toggleLocalVideoMuted: () => void,
|
toggleLocalVideoMuted: () => void,
|
||||||
setMicrophoneMuted: (muted: boolean) => void
|
setMicrophoneMuted: (muted: boolean) => void,
|
||||||
): void {
|
): void {
|
||||||
const spacebarHeld = useRef(false);
|
const spacebarHeld = useRef(false);
|
||||||
|
|
||||||
@@ -63,8 +63,8 @@ export function useCallViewKeyboardShortcuts(
|
|||||||
toggleLocalVideoMuted,
|
toggleLocalVideoMuted,
|
||||||
toggleMicrophoneMuted,
|
toggleMicrophoneMuted,
|
||||||
setMicrophoneMuted,
|
setMicrophoneMuted,
|
||||||
]
|
],
|
||||||
)
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
useEventTarget(
|
useEventTarget(
|
||||||
@@ -80,8 +80,8 @@ export function useCallViewKeyboardShortcuts(
|
|||||||
setMicrophoneMuted(true);
|
setMicrophoneMuted(true);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[focusElement, setMicrophoneMuted]
|
[focusElement, setMicrophoneMuted],
|
||||||
)
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
useEventTarget(
|
useEventTarget(
|
||||||
@@ -92,6 +92,6 @@ export function useCallViewKeyboardShortcuts(
|
|||||||
spacebarHeld.current = false;
|
spacebarHeld.current = false;
|
||||||
setMicrophoneMuted(true);
|
setMicrophoneMuted(true);
|
||||||
}
|
}
|
||||||
}, [setMicrophoneMuted, spacebarHeld])
|
}, [setMicrophoneMuted, spacebarHeld]),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ export function useEventTarget<T extends Event>(
|
|||||||
target: EventTarget | null | undefined,
|
target: EventTarget | null | undefined,
|
||||||
eventType: string,
|
eventType: string,
|
||||||
listener: (event: T) => void,
|
listener: (event: T) => void,
|
||||||
options?: AddEventListenerOptions
|
options?: AddEventListenerOptions,
|
||||||
): void {
|
): void {
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (target) {
|
if (target) {
|
||||||
@@ -37,7 +37,7 @@ export function useEventTarget<T extends Event>(
|
|||||||
target.removeEventListener(
|
target.removeEventListener(
|
||||||
eventType,
|
eventType,
|
||||||
listener as EventListener,
|
listener as EventListener,
|
||||||
options
|
options,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}, [target, eventType, listener, options]);
|
}, [target, eventType, listener, options]);
|
||||||
@@ -47,11 +47,11 @@ export function useEventTarget<T extends Event>(
|
|||||||
export function useTypedEventEmitter<
|
export function useTypedEventEmitter<
|
||||||
Events extends string,
|
Events extends string,
|
||||||
Arguments extends ListenerMap<Events>,
|
Arguments extends ListenerMap<Events>,
|
||||||
T extends Events
|
T extends Events,
|
||||||
>(
|
>(
|
||||||
emitter: TypedEventEmitter<Events, Arguments>,
|
emitter: TypedEventEmitter<Events, Arguments>,
|
||||||
eventType: T,
|
eventType: T,
|
||||||
listener: Listener<Events, Arguments, T>
|
listener: Listener<Events, Arguments, T>,
|
||||||
): void {
|
): void {
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
emitter.on(eventType, listener);
|
emitter.on(eventType, listener);
|
||||||
@@ -64,11 +64,11 @@ export function useTypedEventEmitter<
|
|||||||
// Shortcut for registering a listener on an eventemitter3 EventEmitter (ie. what the LiveKit SDK uses)
|
// Shortcut for registering a listener on an eventemitter3 EventEmitter (ie. what the LiveKit SDK uses)
|
||||||
export function useEventEmitterThree<
|
export function useEventEmitterThree<
|
||||||
EventType extends keyof T,
|
EventType extends keyof T,
|
||||||
T extends EventMap
|
T extends EventMap,
|
||||||
>(
|
>(
|
||||||
emitter: EventEmitter<T>,
|
emitter: EventEmitter<T>,
|
||||||
eventType: EventType,
|
eventType: EventType,
|
||||||
listener: T[EventType]
|
listener: T[EventType],
|
||||||
): void {
|
): void {
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
emitter.on(eventType, listener);
|
emitter.on(eventType, listener);
|
||||||
|
|||||||
@@ -24,10 +24,10 @@ export const localStorageBus = new EventEmitter();
|
|||||||
|
|
||||||
// Like useState, but reads from and persists the value to localStorage
|
// Like useState, but reads from and persists the value to localStorage
|
||||||
export const useLocalStorage = (
|
export const useLocalStorage = (
|
||||||
key: string
|
key: string,
|
||||||
): [LocalStorageItem, (value: string) => void] => {
|
): [LocalStorageItem, (value: string) => void] => {
|
||||||
const [value, setValue] = useState<LocalStorageItem>(() =>
|
const [value, setValue] = useState<LocalStorageItem>(() =>
|
||||||
localStorage.getItem(key)
|
localStorage.getItem(key),
|
||||||
);
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -45,7 +45,7 @@ export const useLocalStorage = (
|
|||||||
localStorage.setItem(key, newValue);
|
localStorage.setItem(key, newValue);
|
||||||
localStorageBus.emit(key, newValue);
|
localStorageBus.emit(key, newValue);
|
||||||
},
|
},
|
||||||
[key, setValue]
|
[key, setValue],
|
||||||
),
|
),
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ import {
|
|||||||
import { useCallback, useEffect, useState } from "react";
|
import { useCallback, useEffect, useState } from "react";
|
||||||
|
|
||||||
export function useMatrixRTCSessionJoinState(
|
export function useMatrixRTCSessionJoinState(
|
||||||
rtcSession: MatrixRTCSession
|
rtcSession: MatrixRTCSession,
|
||||||
): boolean {
|
): boolean {
|
||||||
const [isJoined, setJoined] = useState(rtcSession.isJoined());
|
const [isJoined, setJoined] = useState(rtcSession.isJoined());
|
||||||
|
|
||||||
@@ -30,7 +30,7 @@ export function useMatrixRTCSessionJoinState(
|
|||||||
logger.info(
|
logger.info(
|
||||||
`Session in room ${rtcSession.room.roomId} changed to ${
|
`Session in room ${rtcSession.room.roomId} changed to ${
|
||||||
rtcSession.isJoined() ? "joined" : "left"
|
rtcSession.isJoined() ? "joined" : "left"
|
||||||
}`
|
}`,
|
||||||
);
|
);
|
||||||
setJoined(rtcSession.isJoined());
|
setJoined(rtcSession.isJoined());
|
||||||
}, [rtcSession]);
|
}, [rtcSession]);
|
||||||
@@ -41,7 +41,7 @@ export function useMatrixRTCSessionJoinState(
|
|||||||
return () => {
|
return () => {
|
||||||
rtcSession.off(
|
rtcSession.off(
|
||||||
MatrixRTCSessionEvent.JoinStateChanged,
|
MatrixRTCSessionEvent.JoinStateChanged,
|
||||||
onJoinStateChanged
|
onJoinStateChanged,
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
}, [rtcSession, onJoinStateChanged]);
|
}, [rtcSession, onJoinStateChanged]);
|
||||||
|
|||||||
@@ -23,13 +23,13 @@ import {
|
|||||||
import { useCallback, useEffect, useState } from "react";
|
import { useCallback, useEffect, useState } from "react";
|
||||||
|
|
||||||
export function useMatrixRTCSessionMemberships(
|
export function useMatrixRTCSessionMemberships(
|
||||||
rtcSession: MatrixRTCSession
|
rtcSession: MatrixRTCSession,
|
||||||
): CallMembership[] {
|
): CallMembership[] {
|
||||||
const [memberships, setMemberships] = useState(rtcSession.memberships);
|
const [memberships, setMemberships] = useState(rtcSession.memberships);
|
||||||
|
|
||||||
const onMembershipsChanged = useCallback(() => {
|
const onMembershipsChanged = useCallback(() => {
|
||||||
logger.info(
|
logger.info(
|
||||||
`Memberships changed for call in room ${rtcSession.room.roomId} (${rtcSession.memberships.length} members)`
|
`Memberships changed for call in room ${rtcSession.room.roomId} (${rtcSession.memberships.length} members)`,
|
||||||
);
|
);
|
||||||
setMemberships(rtcSession.memberships);
|
setMemberships(rtcSession.memberships);
|
||||||
}, [rtcSession]);
|
}, [rtcSession]);
|
||||||
@@ -37,13 +37,13 @@ export function useMatrixRTCSessionMemberships(
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
rtcSession.on(
|
rtcSession.on(
|
||||||
MatrixRTCSessionEvent.MembershipsChanged,
|
MatrixRTCSessionEvent.MembershipsChanged,
|
||||||
onMembershipsChanged
|
onMembershipsChanged,
|
||||||
);
|
);
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
rtcSession.off(
|
rtcSession.off(
|
||||||
MatrixRTCSessionEvent.MembershipsChanged,
|
MatrixRTCSessionEvent.MembershipsChanged,
|
||||||
onMembershipsChanged
|
onMembershipsChanged,
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
}, [rtcSession, onMembershipsChanged]);
|
}, [rtcSession, onMembershipsChanged]);
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ export function useMediaQuery(query: string): boolean {
|
|||||||
useEventTarget(
|
useEventTarget(
|
||||||
mediaQuery,
|
mediaQuery,
|
||||||
"change",
|
"change",
|
||||||
useCallback(() => setNumChanges((n) => n + 1), [setNumChanges])
|
useCallback(() => setNumChanges((n) => n + 1), [setNumChanges]),
|
||||||
);
|
);
|
||||||
|
|
||||||
// We want any change to the update counter to trigger an update here
|
// We want any change to the update counter to trigger an update here
|
||||||
|
|||||||
@@ -35,5 +35,5 @@ export const useMergedRefs = <T>(
|
|||||||
// Since this isn't an array literal, we can't use the static dependency
|
// Since this isn't an array literal, we can't use the static dependency
|
||||||
// checker, but that's okay
|
// checker, but that's okay
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
refs
|
refs,
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ import {
|
|||||||
*/
|
*/
|
||||||
export const useReactiveState = <T>(
|
export const useReactiveState = <T>(
|
||||||
updateFn: (prevState?: T) => T,
|
updateFn: (prevState?: T) => T,
|
||||||
deps: DependencyList
|
deps: DependencyList,
|
||||||
): [T, Dispatch<SetStateAction<T>>] => {
|
): [T, Dispatch<SetStateAction<T>>] => {
|
||||||
const state = useRef<T>();
|
const state = useRef<T>();
|
||||||
if (state.current === undefined) state.current = updateFn();
|
if (state.current === undefined) state.current = updateFn();
|
||||||
@@ -61,7 +61,7 @@ export const useReactiveState = <T>(
|
|||||||
}
|
}
|
||||||
setNumUpdates((n) => n + 1); // Force an update
|
setNumUpdates((n) => n + 1); // Force an update
|
||||||
},
|
},
|
||||||
[setNumUpdates]
|
[setNumUpdates],
|
||||||
),
|
),
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -78,7 +78,7 @@ export interface SparseGrid {
|
|||||||
export function getPaths(
|
export function getPaths(
|
||||||
g: SparseGrid,
|
g: SparseGrid,
|
||||||
dest: number,
|
dest: number,
|
||||||
avoid: (cell: number) => boolean = (): boolean => false
|
avoid: (cell: number) => boolean = (): boolean => false,
|
||||||
): (number | null)[] {
|
): (number | null)[] {
|
||||||
const destRow = row(dest, g);
|
const destRow = row(dest, g);
|
||||||
const destColumn = column(dest, g);
|
const destColumn = column(dest, g);
|
||||||
@@ -145,7 +145,7 @@ function inArea(
|
|||||||
index: number,
|
index: number,
|
||||||
start: number,
|
start: number,
|
||||||
end: number,
|
end: number,
|
||||||
g: SparseGrid
|
g: SparseGrid,
|
||||||
): boolean {
|
): boolean {
|
||||||
const indexColumn = column(index, g);
|
const indexColumn = column(index, g);
|
||||||
const indexRow = row(index, g);
|
const indexRow = row(index, g);
|
||||||
@@ -160,7 +160,7 @@ function inArea(
|
|||||||
function* cellsInArea(
|
function* cellsInArea(
|
||||||
start: number,
|
start: number,
|
||||||
end: number,
|
end: number,
|
||||||
g: SparseGrid
|
g: SparseGrid,
|
||||||
): Generator<number, void, unknown> {
|
): Generator<number, void, unknown> {
|
||||||
const startColumn = column(start, g);
|
const startColumn = column(start, g);
|
||||||
const endColumn = column(end, g);
|
const endColumn = column(end, g);
|
||||||
@@ -179,7 +179,7 @@ export function forEachCellInArea<G extends Grid | SparseGrid>(
|
|||||||
start: number,
|
start: number,
|
||||||
end: number,
|
end: number,
|
||||||
g: G,
|
g: G,
|
||||||
fn: (c: G["cells"][0], i: number) => void
|
fn: (c: G["cells"][0], i: number) => void,
|
||||||
): void {
|
): void {
|
||||||
for (const i of cellsInArea(start, end, g)) fn(g.cells[i], i);
|
for (const i of cellsInArea(start, end, g)) fn(g.cells[i], i);
|
||||||
}
|
}
|
||||||
@@ -188,7 +188,7 @@ function allCellsInArea<G extends Grid | SparseGrid>(
|
|||||||
start: number,
|
start: number,
|
||||||
end: number,
|
end: number,
|
||||||
g: G,
|
g: G,
|
||||||
fn: (c: G["cells"][0], i: number) => boolean
|
fn: (c: G["cells"][0], i: number) => boolean,
|
||||||
): boolean {
|
): boolean {
|
||||||
for (const i of cellsInArea(start, end, g)) {
|
for (const i of cellsInArea(start, end, g)) {
|
||||||
if (!fn(g.cells[i], i)) return false;
|
if (!fn(g.cells[i], i)) return false;
|
||||||
@@ -204,7 +204,7 @@ function countCellsInArea<G extends Grid | SparseGrid>(
|
|||||||
start: number,
|
start: number,
|
||||||
end: number,
|
end: number,
|
||||||
g: G,
|
g: G,
|
||||||
predicate: (c: G["cells"][0], i: number) => boolean
|
predicate: (c: G["cells"][0], i: number) => boolean,
|
||||||
): number {
|
): number {
|
||||||
let count = 0;
|
let count = 0;
|
||||||
for (const i of cellsInArea(start, end, g)) {
|
for (const i of cellsInArea(start, end, g)) {
|
||||||
@@ -217,7 +217,7 @@ const areaEnd = (
|
|||||||
start: number,
|
start: number,
|
||||||
columns: number,
|
columns: number,
|
||||||
rows: number,
|
rows: number,
|
||||||
g: SparseGrid
|
g: SparseGrid,
|
||||||
): number => start + columns - 1 + g.columns * (rows - 1);
|
): number => start + columns - 1 + g.columns * (rows - 1);
|
||||||
|
|
||||||
const cloneGrid = <G extends Grid | SparseGrid>(g: G): G => ({
|
const cloneGrid = <G extends Grid | SparseGrid>(g: G): G => ({
|
||||||
@@ -231,7 +231,7 @@ const cloneGrid = <G extends Grid | SparseGrid>(g: G): G => ({
|
|||||||
*/
|
*/
|
||||||
function getNextGap(
|
function getNextGap(
|
||||||
g: SparseGrid,
|
g: SparseGrid,
|
||||||
ignoreGap: (cell: number) => boolean
|
ignoreGap: (cell: number) => boolean,
|
||||||
): number | null {
|
): number | null {
|
||||||
const last1By1Index = findLast1By1Index(g);
|
const last1By1Index = findLast1By1Index(g);
|
||||||
if (last1By1Index === null) return null;
|
if (last1By1Index === null) return null;
|
||||||
@@ -278,13 +278,13 @@ function moveTileUnchecked(g: SparseGrid, from: number, to: number): void {
|
|||||||
to,
|
to,
|
||||||
toEnd,
|
toEnd,
|
||||||
g,
|
g,
|
||||||
(_c, i) => (g.cells[i] = movingCells.shift())
|
(_c, i) => (g.cells[i] = movingCells.shift()),
|
||||||
);
|
);
|
||||||
forEachCellInArea(
|
forEachCellInArea(
|
||||||
from,
|
from,
|
||||||
fromEnd,
|
fromEnd,
|
||||||
g,
|
g,
|
||||||
(_c, i) => (g.cells[i] ??= displacedTiles.shift())
|
(_c, i) => (g.cells[i] ??= displacedTiles.shift()),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -294,7 +294,7 @@ function moveTileUnchecked(g: SparseGrid, from: number, to: number): void {
|
|||||||
export function moveTile<G extends Grid | SparseGrid>(
|
export function moveTile<G extends Grid | SparseGrid>(
|
||||||
g: G,
|
g: G,
|
||||||
from: number,
|
from: number,
|
||||||
to: number
|
to: number,
|
||||||
): G {
|
): G {
|
||||||
const tile = g.cells[from]!;
|
const tile = g.cells[from]!;
|
||||||
|
|
||||||
@@ -333,7 +333,7 @@ function pushTileUp(
|
|||||||
g: SparseGrid,
|
g: SparseGrid,
|
||||||
from: number,
|
from: number,
|
||||||
rows: number,
|
rows: number,
|
||||||
avoid: (cell: number) => boolean = (): boolean => false
|
avoid: (cell: number) => boolean = (): boolean => false,
|
||||||
): number {
|
): number {
|
||||||
const tile = g.cells[from]!;
|
const tile = g.cells[from]!;
|
||||||
|
|
||||||
@@ -347,7 +347,7 @@ function pushTileUp(
|
|||||||
to,
|
to,
|
||||||
Math.min(from - g.columns + tile.columns - 1, toEnd),
|
Math.min(from - g.columns + tile.columns - 1, toEnd),
|
||||||
g,
|
g,
|
||||||
(c, i) => (c === undefined || is1By1(c)) && !avoid(i)
|
(c, i) => (c === undefined || is1By1(c)) && !avoid(i),
|
||||||
);
|
);
|
||||||
|
|
||||||
if (cellsAboveAreDisplacable) {
|
if (cellsAboveAreDisplacable) {
|
||||||
@@ -376,7 +376,7 @@ function canVacateArea(g: SparseGrid, start: number, end: number): boolean {
|
|||||||
start,
|
start,
|
||||||
end - newFullRows * g.columns,
|
end - newFullRows * g.columns,
|
||||||
g,
|
g,
|
||||||
(c) => c === undefined || is1By1(c)
|
(c) => c === undefined || is1By1(c),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -391,7 +391,7 @@ function vacateArea(g: SparseGrid, start: number, end: number): SparseGrid {
|
|||||||
start,
|
start,
|
||||||
end,
|
end,
|
||||||
g,
|
g,
|
||||||
(c, i) => c !== undefined || i >= g.cells.length
|
(c, i) => c !== undefined || i >= g.cells.length,
|
||||||
);
|
);
|
||||||
const newFullRows = Math.floor(newCellCount / g.columns);
|
const newFullRows = Math.floor(newCellCount / g.columns);
|
||||||
const endRow = row(end, g);
|
const endRow = row(end, g);
|
||||||
@@ -452,7 +452,7 @@ function vacateArea(g: SparseGrid, start: number, end: number): SparseGrid {
|
|||||||
const inputStructure = fillGaps(
|
const inputStructure = fillGaps(
|
||||||
outputStructure,
|
outputStructure,
|
||||||
false,
|
false,
|
||||||
(i) => inArea(i, start, end, g) && g.cells[i] === undefined
|
(i) => inArea(i, start, end, g) && g.cells[i] === undefined,
|
||||||
);
|
);
|
||||||
|
|
||||||
// We exploit the fact that g and inputStructure have the same structure to
|
// We exploit the fact that g and inputStructure have the same structure to
|
||||||
@@ -464,7 +464,7 @@ function vacateArea(g: SparseGrid, start: number, end: number): SparseGrid {
|
|||||||
return {
|
return {
|
||||||
columns: g.columns,
|
columns: g.columns,
|
||||||
cells: outputStructure.cells.map((placeholder) =>
|
cells: outputStructure.cells.map((placeholder) =>
|
||||||
structureMapping.get(placeholder)
|
structureMapping.get(placeholder),
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -475,21 +475,21 @@ function vacateArea(g: SparseGrid, start: number, end: number): SparseGrid {
|
|||||||
export function fillGaps(
|
export function fillGaps(
|
||||||
g: SparseGrid,
|
g: SparseGrid,
|
||||||
packLargeTiles?: true,
|
packLargeTiles?: true,
|
||||||
ignoreGap?: () => false
|
ignoreGap?: () => false,
|
||||||
): Grid;
|
): Grid;
|
||||||
export function fillGaps(
|
export function fillGaps(
|
||||||
g: SparseGrid,
|
g: SparseGrid,
|
||||||
packLargeTiles?: boolean,
|
packLargeTiles?: boolean,
|
||||||
ignoreGap?: (cell: number) => boolean
|
ignoreGap?: (cell: number) => boolean,
|
||||||
): SparseGrid;
|
): SparseGrid;
|
||||||
export function fillGaps(
|
export function fillGaps(
|
||||||
g: SparseGrid,
|
g: SparseGrid,
|
||||||
packLargeTiles = true,
|
packLargeTiles = true,
|
||||||
ignoreGap: (cell: number) => boolean = (): boolean => false
|
ignoreGap: (cell: number) => boolean = (): boolean => false,
|
||||||
): SparseGrid {
|
): SparseGrid {
|
||||||
const lastGap = findLastIndex(
|
const lastGap = findLastIndex(
|
||||||
g.cells,
|
g.cells,
|
||||||
(c, i) => c === undefined && !ignoreGap(i)
|
(c, i) => c === undefined && !ignoreGap(i),
|
||||||
);
|
);
|
||||||
if (lastGap === null) return g; // There are no gaps to fill
|
if (lastGap === null) return g; // There are no gaps to fill
|
||||||
const lastGapRow = row(lastGap, g);
|
const lastGapRow = row(lastGap, g);
|
||||||
@@ -500,10 +500,10 @@ export function fillGaps(
|
|||||||
// allowed to pack the large tiles into the rest of the grid as necessary)
|
// allowed to pack the large tiles into the rest of the grid as necessary)
|
||||||
let idealLength = count(
|
let idealLength = count(
|
||||||
result.cells,
|
result.cells,
|
||||||
(c, i) => c !== undefined || ignoreGap(i)
|
(c, i) => c !== undefined || ignoreGap(i),
|
||||||
);
|
);
|
||||||
const fullRowsRemoved = Math.floor(
|
const fullRowsRemoved = Math.floor(
|
||||||
(g.cells.length - idealLength) / g.columns
|
(g.cells.length - idealLength) / g.columns,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Step 1: Push all large tiles below the last gap upwards, so that they move
|
// Step 1: Push all large tiles below the last gap upwards, so that they move
|
||||||
@@ -620,7 +620,7 @@ function createRows(g: SparseGrid, count: number, atRow: number): SparseGrid {
|
|||||||
g,
|
g,
|
||||||
(c, i) => {
|
(c, i) => {
|
||||||
result.cells[i + offset] = c;
|
result.cells[i + offset] = c;
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -633,7 +633,7 @@ function createRows(g: SparseGrid, count: number, atRow: number): SparseGrid {
|
|||||||
*/
|
*/
|
||||||
export function addItems(
|
export function addItems(
|
||||||
items: TileDescriptor<unknown>[],
|
items: TileDescriptor<unknown>[],
|
||||||
g: SparseGrid
|
g: SparseGrid,
|
||||||
): SparseGrid {
|
): SparseGrid {
|
||||||
let result: SparseGrid = cloneGrid(g);
|
let result: SparseGrid = cloneGrid(g);
|
||||||
|
|
||||||
@@ -655,7 +655,7 @@ export function addItems(
|
|||||||
// This item wants to be placed near another; let's put it on a row
|
// This item wants to be placed near another; let's put it on a row
|
||||||
// directly below the related tile
|
// directly below the related tile
|
||||||
const placeNear = result.cells.findIndex(
|
const placeNear = result.cells.findIndex(
|
||||||
(c) => c?.item.id === item.placeNear
|
(c) => c?.item.id === item.placeNear,
|
||||||
);
|
);
|
||||||
if (placeNear === -1) {
|
if (placeNear === -1) {
|
||||||
// Can't find the related tile, so let's give up and place it at the end
|
// Can't find the related tile, so let's give up and place it at the end
|
||||||
@@ -666,7 +666,7 @@ export function addItems(
|
|||||||
placeNear,
|
placeNear,
|
||||||
placeNearCell.columns,
|
placeNearCell.columns,
|
||||||
placeNearCell.rows,
|
placeNearCell.rows,
|
||||||
result
|
result,
|
||||||
);
|
);
|
||||||
|
|
||||||
result = createRows(result, 1, row(placeNearEnd, result) + 1);
|
result = createRows(result, 1, row(placeNearEnd, result) + 1);
|
||||||
@@ -699,7 +699,7 @@ const extraLargeTileDimensions = (g: SparseGrid): [number, number] =>
|
|||||||
|
|
||||||
export function cycleTileSize<G extends Grid | SparseGrid>(
|
export function cycleTileSize<G extends Grid | SparseGrid>(
|
||||||
g: G,
|
g: G,
|
||||||
tile: TileDescriptor<unknown>
|
tile: TileDescriptor<unknown>,
|
||||||
): G {
|
): G {
|
||||||
const from = g.cells.findIndex((c) => c?.item === tile);
|
const from = g.cells.findIndex((c) => c?.item === tile);
|
||||||
if (from === -1) return g; // Tile removed, no change
|
if (from === -1) return g; // Tile removed, no change
|
||||||
@@ -727,7 +727,7 @@ function findNearestCell<G extends Grid | SparseGrid>(
|
|||||||
g: G,
|
g: G,
|
||||||
nearestTo: number,
|
nearestTo: number,
|
||||||
shouldScan: (index: number) => boolean,
|
shouldScan: (index: number) => boolean,
|
||||||
predicate: (cell: G["cells"][0], index: number) => boolean
|
predicate: (cell: G["cells"][0], index: number) => boolean,
|
||||||
): number | null {
|
): number | null {
|
||||||
const scanLocations = new Set([nearestTo]);
|
const scanLocations = new Set([nearestTo]);
|
||||||
|
|
||||||
@@ -758,7 +758,7 @@ export function setTileSize<G extends Grid | SparseGrid>(
|
|||||||
g: G,
|
g: G,
|
||||||
from: number,
|
from: number,
|
||||||
toWidth: number,
|
toWidth: number,
|
||||||
toHeight: number
|
toHeight: number,
|
||||||
): G {
|
): G {
|
||||||
const fromCell = g.cells[from]!;
|
const fromCell = g.cells[from]!;
|
||||||
const fromWidth = fromCell.columns;
|
const fromWidth = fromCell.columns;
|
||||||
@@ -771,12 +771,12 @@ export function setTileSize<G extends Grid | SparseGrid>(
|
|||||||
0,
|
0,
|
||||||
Math.min(
|
Math.min(
|
||||||
g.columns - toWidth,
|
g.columns - toWidth,
|
||||||
column(from, g) + Math.trunc((fromWidth - toWidth) / 2)
|
column(from, g) + Math.trunc((fromWidth - toWidth) / 2),
|
||||||
)
|
),
|
||||||
);
|
);
|
||||||
const toRow = Math.max(
|
const toRow = Math.max(
|
||||||
0,
|
0,
|
||||||
row(from, g) + Math.trunc((fromHeight - toHeight) / 2)
|
row(from, g) + Math.trunc((fromHeight - toHeight) / 2),
|
||||||
);
|
);
|
||||||
const targetDest = toColumn + toRow * g.columns;
|
const targetDest = toColumn + toRow * g.columns;
|
||||||
|
|
||||||
@@ -788,7 +788,7 @@ export function setTileSize<G extends Grid | SparseGrid>(
|
|||||||
const placeTile = (
|
const placeTile = (
|
||||||
to: number,
|
to: number,
|
||||||
toEnd: number,
|
toEnd: number,
|
||||||
grid: Grid | SparseGrid
|
grid: Grid | SparseGrid,
|
||||||
): void => {
|
): void => {
|
||||||
forEachCellInArea(to, toEnd, grid, (_c, i) => {
|
forEachCellInArea(to, toEnd, grid, (_c, i) => {
|
||||||
grid.cells[i] = {
|
grid.cells[i] = {
|
||||||
@@ -824,7 +824,7 @@ export function setTileSize<G extends Grid | SparseGrid>(
|
|||||||
(_c, i) => {
|
(_c, i) => {
|
||||||
const end = areaEnd(i, toWidth, toHeight, g);
|
const end = areaEnd(i, toWidth, toHeight, g);
|
||||||
return end < newGridSize && canVacateArea(gridWithoutTile, i, end);
|
return end < newGridSize && canVacateArea(gridWithoutTile, i, end);
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
if (to !== null) {
|
if (to !== null) {
|
||||||
@@ -848,7 +848,7 @@ export function setTileSize<G extends Grid | SparseGrid>(
|
|||||||
(_c, i) => {
|
(_c, i) => {
|
||||||
const end = areaEnd(i, toWidth, toHeight, g);
|
const end = areaEnd(i, toWidth, toHeight, g);
|
||||||
return end < newGridSize && canVacateArea(packedGridWithoutTile, i, end);
|
return end < newGridSize && canVacateArea(packedGridWithoutTile, i, end);
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
if (to === null) return g; // There's no space anywhere; give up
|
if (to === null) return g; // There's no space anywhere; give up
|
||||||
@@ -949,7 +949,7 @@ function updateTiles(g: Grid, tiles: TileDescriptor<unknown>[]): Grid {
|
|||||||
|
|
||||||
// Step 2: Add new tiles
|
// Step 2: Add new tiles
|
||||||
const existingItemIds = new Set(
|
const existingItemIds = new Set(
|
||||||
grid1.cells.filter((c) => c !== undefined).map((c) => c!.item.id)
|
grid1.cells.filter((c) => c !== undefined).map((c) => c!.item.id),
|
||||||
);
|
);
|
||||||
const newItems = tiles.filter((i) => !existingItemIds.has(i.id));
|
const newItems = tiles.filter((i) => !existingItemIds.has(i.id));
|
||||||
const grid2 = addItems(newItems, grid1);
|
const grid2 = addItems(newItems, grid1);
|
||||||
@@ -967,7 +967,7 @@ function updateBounds(g: Grid, bounds: RectReadOnly): Grid {
|
|||||||
|
|
||||||
const Slots: FC<{ s: Grid }> = memo(({ s: g }) => {
|
const Slots: FC<{ s: Grid }> = memo(({ s: g }) => {
|
||||||
const areas = new Array<(number | null)[]>(
|
const areas = new Array<(number | null)[]>(
|
||||||
Math.ceil(g.cells.length / g.columns)
|
Math.ceil(g.cells.length / g.columns),
|
||||||
);
|
);
|
||||||
for (let i = 0; i < areas.length; i++)
|
for (let i = 0; i < areas.length; i++)
|
||||||
areas[i] = new Array<number | null>(g.columns).fill(null);
|
areas[i] = new Array<number | null>(g.columns).fill(null);
|
||||||
@@ -981,7 +981,7 @@ const Slots: FC<{ s: Grid }> = memo(({ s: g }) => {
|
|||||||
i,
|
i,
|
||||||
slotEnd,
|
slotEnd,
|
||||||
g,
|
g,
|
||||||
(_c, j) => (areas[row(j, g)][column(j, g)] = slotCount)
|
(_c, j) => (areas[row(j, g)][column(j, g)] = slotCount),
|
||||||
);
|
);
|
||||||
slotCount++;
|
slotCount++;
|
||||||
}
|
}
|
||||||
@@ -993,7 +993,7 @@ const Slots: FC<{ s: Grid }> = memo(({ s: g }) => {
|
|||||||
(row) =>
|
(row) =>
|
||||||
`'${row
|
`'${row
|
||||||
.map((slotId) => (slotId === null ? "." : `s${slotId}`))
|
.map((slotId) => (slotId === null ? "." : `s${slotId}`))
|
||||||
.join(" ")}'`
|
.join(" ")}'`,
|
||||||
)
|
)
|
||||||
.join(" "),
|
.join(" "),
|
||||||
gridTemplateColumns: `repeat(${g.columns}, 1fr)`,
|
gridTemplateColumns: `repeat(${g.columns}, 1fr)`,
|
||||||
@@ -1019,7 +1019,7 @@ function positionOnTileToCell(
|
|||||||
g: SparseGrid,
|
g: SparseGrid,
|
||||||
tileOriginIndex: number,
|
tileOriginIndex: number,
|
||||||
xPositionOnTile: number,
|
xPositionOnTile: number,
|
||||||
yPositionOnTile: number
|
yPositionOnTile: number,
|
||||||
): number {
|
): number {
|
||||||
const tileOrigin = g.cells[tileOriginIndex]!;
|
const tileOrigin = g.cells[tileOriginIndex]!;
|
||||||
const columnOnTile = Math.floor(xPositionOnTile * tileOrigin.columns);
|
const columnOnTile = Math.floor(xPositionOnTile * tileOrigin.columns);
|
||||||
@@ -1034,7 +1034,7 @@ function dragTile(
|
|||||||
xPositionOnFrom: number,
|
xPositionOnFrom: number,
|
||||||
yPositionOnFrom: number,
|
yPositionOnFrom: number,
|
||||||
xPositionOnTo: number,
|
xPositionOnTo: number,
|
||||||
yPositionOnTo: number
|
yPositionOnTo: number,
|
||||||
): Grid {
|
): Grid {
|
||||||
const fromOrigin = g.cells.findIndex((c) => c.item === from);
|
const fromOrigin = g.cells.findIndex((c) => c.item === from);
|
||||||
const toOrigin = g.cells.findIndex((c) => c.item === to);
|
const toOrigin = g.cells.findIndex((c) => c.item === to);
|
||||||
@@ -1042,13 +1042,13 @@ function dragTile(
|
|||||||
g,
|
g,
|
||||||
fromOrigin,
|
fromOrigin,
|
||||||
xPositionOnFrom,
|
xPositionOnFrom,
|
||||||
yPositionOnFrom
|
yPositionOnFrom,
|
||||||
);
|
);
|
||||||
const toCell = positionOnTileToCell(
|
const toCell = positionOnTileToCell(
|
||||||
g,
|
g,
|
||||||
toOrigin,
|
toOrigin,
|
||||||
xPositionOnTo,
|
xPositionOnTo,
|
||||||
yPositionOnTo
|
yPositionOnTo,
|
||||||
);
|
);
|
||||||
|
|
||||||
return moveTile(g, fromOrigin, fromOrigin + toCell - fromCell);
|
return moveTile(g, fromOrigin, fromOrigin + toCell - fromCell);
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ export interface Layout<State> {
|
|||||||
xPositionOnFrom: number,
|
xPositionOnFrom: number,
|
||||||
yPositionOnFrom: number,
|
yPositionOnFrom: number,
|
||||||
xPositionOnTo: number,
|
xPositionOnTo: number,
|
||||||
yPositionOnTo: number
|
yPositionOnTo: number,
|
||||||
) => State;
|
) => State;
|
||||||
/**
|
/**
|
||||||
* Toggles the focus of the given tile (if this layout has the concept of
|
* Toggles the focus of the given tile (if this layout has the concept of
|
||||||
@@ -109,7 +109,7 @@ interface UseLayout<State, T> {
|
|||||||
xPositionOnFrom: number,
|
xPositionOnFrom: number,
|
||||||
yPositionOnFrom: number,
|
yPositionOnFrom: number,
|
||||||
xPositionOnTo: number,
|
xPositionOnTo: number,
|
||||||
yPositionOnTo: number
|
yPositionOnTo: number,
|
||||||
) => void;
|
) => void;
|
||||||
toggleFocus: ((tile: TileDescriptor<T>) => void) | undefined;
|
toggleFocus: ((tile: TileDescriptor<T>) => void) | undefined;
|
||||||
slots: ReactNode;
|
slots: ReactNode;
|
||||||
@@ -123,7 +123,7 @@ export function useLayout<State, T>(
|
|||||||
layout: Layout<State>,
|
layout: Layout<State>,
|
||||||
items: TileDescriptor<T>[],
|
items: TileDescriptor<T>[],
|
||||||
bounds: RectReadOnly,
|
bounds: RectReadOnly,
|
||||||
layoutStates: LayoutStatesMap
|
layoutStates: LayoutStatesMap,
|
||||||
): UseLayout<State, T> {
|
): UseLayout<State, T> {
|
||||||
const prevLayout = useRef<Layout<unknown>>();
|
const prevLayout = useRef<Layout<unknown>>();
|
||||||
const prevState = layoutStates.get(layout);
|
const prevState = layoutStates.get(layout);
|
||||||
@@ -159,7 +159,7 @@ export function useLayout<State, T>(
|
|||||||
generation: generation.current,
|
generation: generation.current,
|
||||||
canDragTile: useCallback(
|
canDragTile: useCallback(
|
||||||
(tile: TileDescriptor<T>) => layout.canDragTile(state, tile),
|
(tile: TileDescriptor<T>) => layout.canDragTile(state, tile),
|
||||||
[layout, state]
|
[layout, state],
|
||||||
),
|
),
|
||||||
dragTile: useCallback(
|
dragTile: useCallback(
|
||||||
(
|
(
|
||||||
@@ -168,7 +168,7 @@ export function useLayout<State, T>(
|
|||||||
xPositionOnFrom: number,
|
xPositionOnFrom: number,
|
||||||
yPositionOnFrom: number,
|
yPositionOnFrom: number,
|
||||||
xPositionOnTo: number,
|
xPositionOnTo: number,
|
||||||
yPositionOnTo: number
|
yPositionOnTo: number,
|
||||||
) =>
|
) =>
|
||||||
setState((s) =>
|
setState((s) =>
|
||||||
layout.dragTile(
|
layout.dragTile(
|
||||||
@@ -178,17 +178,17 @@ export function useLayout<State, T>(
|
|||||||
xPositionOnFrom,
|
xPositionOnFrom,
|
||||||
yPositionOnFrom,
|
yPositionOnFrom,
|
||||||
xPositionOnTo,
|
xPositionOnTo,
|
||||||
yPositionOnTo
|
yPositionOnTo,
|
||||||
)
|
),
|
||||||
),
|
),
|
||||||
[layout, setState]
|
[layout, setState],
|
||||||
),
|
),
|
||||||
toggleFocus: useMemo(
|
toggleFocus: useMemo(
|
||||||
() =>
|
() =>
|
||||||
layout.toggleFocus &&
|
layout.toggleFocus &&
|
||||||
((tile: TileDescriptor<T>): void =>
|
((tile: TileDescriptor<T>): void =>
|
||||||
setState((s) => layout.toggleFocus!(s, tile))),
|
setState((s) => layout.toggleFocus!(s, tile))),
|
||||||
[layout, setState]
|
[layout, setState],
|
||||||
),
|
),
|
||||||
slots: <layout.Slots s={state} />,
|
slots: <layout.Slots s={state} />,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -98,13 +98,13 @@ export function NewVideoGrid<T>({
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (slotsRoot !== null) {
|
if (slotsRoot !== null) {
|
||||||
setRenderedGeneration(
|
setRenderedGeneration(
|
||||||
parseInt(slotsRoot.getAttribute("data-generation")!)
|
parseInt(slotsRoot.getAttribute("data-generation")!),
|
||||||
);
|
);
|
||||||
|
|
||||||
const observer = new MutationObserver((mutations) => {
|
const observer = new MutationObserver((mutations) => {
|
||||||
if (mutations.some((m) => m.type === "attributes")) {
|
if (mutations.some((m) => m.type === "attributes")) {
|
||||||
setRenderedGeneration(
|
setRenderedGeneration(
|
||||||
parseInt(slotsRoot.getAttribute("data-generation")!)
|
parseInt(slotsRoot.getAttribute("data-generation")!),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -158,13 +158,13 @@ export function NewVideoGrid<T>({
|
|||||||
if (renderedGeneration !== generation) return prevTiles ?? [];
|
if (renderedGeneration !== generation) return prevTiles ?? [];
|
||||||
|
|
||||||
const tileRects = new Map(
|
const tileRects = new Map(
|
||||||
zip(orderedItems, slotRects) as [TileDescriptor<T>, Rect][]
|
zip(orderedItems, slotRects) as [TileDescriptor<T>, Rect][],
|
||||||
);
|
);
|
||||||
// In order to not break drag gestures, it's critical that we render tiles
|
// In order to not break drag gestures, it's critical that we render tiles
|
||||||
// in a stable order (that of 'items')
|
// in a stable order (that of 'items')
|
||||||
return items.map((item) => ({ ...tileRects.get(item)!, item }));
|
return items.map((item) => ({ ...tileRects.get(item)!, item }));
|
||||||
},
|
},
|
||||||
[slotRects, grid, renderedGeneration]
|
[slotRects, grid, renderedGeneration],
|
||||||
);
|
);
|
||||||
|
|
||||||
// Drag state is stored in a ref rather than component state, because we use
|
// Drag state is stored in a ref rather than component state, because we use
|
||||||
@@ -200,7 +200,7 @@ export function NewVideoGrid<T>({
|
|||||||
},
|
},
|
||||||
leave: { opacity: 0, scale: 0, immediate: disableAnimations },
|
leave: { opacity: 0, scale: 0, immediate: disableAnimations },
|
||||||
config: { mass: 0.7, tension: 252, friction: 25 },
|
config: { mass: 0.7, tension: 252, friction: 25 },
|
||||||
})
|
}),
|
||||||
// react-spring's types are bugged and can't infer the spring type
|
// react-spring's types are bugged and can't infer the spring type
|
||||||
) as unknown as [TransitionFn<Tile<T>, TileSpring>, SpringRef<TileSpring>];
|
) as unknown as [TransitionFn<Tile<T>, TileSpring>, SpringRef<TileSpring>];
|
||||||
|
|
||||||
@@ -242,7 +242,7 @@ export function NewVideoGrid<T>({
|
|||||||
disableAnimations ||
|
disableAnimations ||
|
||||||
((key): boolean =>
|
((key): boolean =>
|
||||||
key === "zIndex" || key === "x" || key === "y"),
|
key === "zIndex" || key === "x" || key === "y"),
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
const overTile = tiles.find(
|
const overTile = tiles.find(
|
||||||
@@ -250,7 +250,7 @@ export function NewVideoGrid<T>({
|
|||||||
cursorX >= t.x &&
|
cursorX >= t.x &&
|
||||||
cursorX < t.x + t.width &&
|
cursorX < t.x + t.width &&
|
||||||
cursorY >= t.y &&
|
cursorY >= t.y &&
|
||||||
cursorY < t.y + t.height
|
cursorY < t.y + t.height,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (overTile !== undefined)
|
if (overTile !== undefined)
|
||||||
@@ -260,7 +260,7 @@ export function NewVideoGrid<T>({
|
|||||||
(cursorX - tileX) / tile.width,
|
(cursorX - tileX) / tile.width,
|
||||||
(cursorY - tileY) / tile.height,
|
(cursorY - tileY) / tile.height,
|
||||||
(cursorX - overTile.x) / overTile.width,
|
(cursorX - overTile.x) / overTile.width,
|
||||||
(cursorY - overTile.y) / overTile.height
|
(cursorY - overTile.y) / overTile.height,
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -287,7 +287,7 @@ export function NewVideoGrid<T>({
|
|||||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
last,
|
last,
|
||||||
}: Parameters<Handler<"drag", EventTypes["drag"]>>[0]
|
}: Parameters<Handler<"drag", EventTypes["drag"]>>[0],
|
||||||
): void => {
|
): void => {
|
||||||
if (tap) {
|
if (tap) {
|
||||||
const now = Date.now();
|
const now = Date.now();
|
||||||
@@ -303,7 +303,7 @@ export function NewVideoGrid<T>({
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const tileController = springRef.current.find(
|
const tileController = springRef.current.find(
|
||||||
(c) => (c.item as Tile<T>).item.id === tileId
|
(c) => (c.item as Tile<T>).item.id === tileId,
|
||||||
)!;
|
)!;
|
||||||
|
|
||||||
if (canDragTile((tileController.item as Tile<T>).item)) {
|
if (canDragTile((tileController.item as Tile<T>).item)) {
|
||||||
@@ -347,7 +347,7 @@ export function NewVideoGrid<T>({
|
|||||||
animateDraggedTile(false);
|
animateDraggedTile(false);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{ target: gridRef2 }
|
{ target: gridRef2 },
|
||||||
);
|
);
|
||||||
|
|
||||||
// Render nothing if the grid has yet to be generated
|
// Render nothing if the grid has yet to be generated
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ interface Props<T> {
|
|||||||
onDragRef: RefObject<
|
onDragRef: RefObject<
|
||||||
(
|
(
|
||||||
tileId: string,
|
tileId: string,
|
||||||
state: Parameters<Handler<"drag", EventTypes["drag"]>>[0]
|
state: Parameters<Handler<"drag", EventTypes["drag"]>>[0],
|
||||||
) => void
|
) => void
|
||||||
>;
|
>;
|
||||||
targetWidth: number;
|
targetWidth: number;
|
||||||
@@ -87,7 +87,7 @@ export const TileWrapper = memo(
|
|||||||
height,
|
height,
|
||||||
boxShadow: to(
|
boxShadow: to(
|
||||||
[shadow, shadowSpread],
|
[shadow, shadowSpread],
|
||||||
(s, ss) => `rgba(0, 0, 0, 0.5) 0px ${s}px ${2 * s}px ${ss}px`
|
(s, ss) => `rgba(0, 0, 0, 0.5) 0px ${s}px ${2 * s}px ${ss}px`,
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
targetWidth,
|
targetWidth,
|
||||||
@@ -96,7 +96,7 @@ export const TileWrapper = memo(
|
|||||||
})}
|
})}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
},
|
||||||
// We pretend this component is a simple function rather than a
|
// We pretend this component is a simple function rather than a
|
||||||
// NamedExoticComponent, because that's the only way we can fit in a type
|
// NamedExoticComponent, because that's the only way we can fit in a type
|
||||||
// parameter
|
// parameter
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user