Compare commits

..

1 Commits

Author SHA1 Message Date
Andrew Ferrazzutti
5a5ca14967 Explicitly bump matrix-widget-api
Was already present in yarn.lock
2024-11-11 12:22:25 -05:00
36 changed files with 675 additions and 798 deletions

View File

@@ -23,4 +23,3 @@ jobs:
SENTRY_PROJECT: ${{ secrets.SENTRY_PROJECT }}
SENTRY_URL: ${{ secrets.SENTRY_URL }}
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}

View File

@@ -14,8 +14,6 @@ on:
required: true
SENTRY_AUTH_TOKEN:
required: true
CODECOV_TOKEN:
required: false
jobs:
build:
@@ -38,7 +36,6 @@ jobs:
SENTRY_PROJECT: ${{ secrets.SENTRY_PROJECT }}
SENTRY_URL: ${{ secrets.SENTRY_URL }}
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
VITE_APP_VERSION: ${{ inputs.vite_app_version }}
NODE_OPTIONS: "--max-old-space-size=4096"
- name: Upload Artifact

View File

@@ -106,7 +106,7 @@ rc_message:
MSC3266 allows to request a room summary of rooms you are not joined. The
summary contains the room join rules. We need that to decide if the user gets
prompted with the option to knock ("Request to join call"), a cannot join error or the
prompted with the option to knock ("ask to join"), a cannot join error or the
join view.
Element Call requires a Livekit SFU alongside a [Livekit JWT

View File

@@ -24,9 +24,6 @@
"@babel/preset-env": "^7.22.20",
"@babel/preset-react": "^7.22.15",
"@babel/preset-typescript": "^7.23.0",
"@codecov/vite-plugin": "^1.3.0",
"@fontsource/inconsolata": "^5.1.0",
"@fontsource/inter": "^5.1.0",
"@formatjs/intl-durationformat": "^0.6.1",
"@formatjs/intl-segmenter": "^11.7.3",
"@livekit/components-core": "^0.11.0",
@@ -90,7 +87,7 @@
"livekit-client": "^2.5.7",
"lodash": "^4.17.21",
"loglevel": "^1.9.1",
"matrix-js-sdk": "matrix-org/matrix-js-sdk#8e9a04cdec0f88fc876bbbf406db55b0677f005d",
"matrix-js-sdk": "matrix-org/matrix-js-sdk#fcb69b16ad8d170c67ea844f83543d467bbd7707",
"matrix-widget-api": "^1.10.0",
"normalize.css": "^8.0.1",
"observable-hooks": "^4.2.3",

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -81,8 +81,8 @@
"call_ended_heading": "Anruf beendet",
"failed_heading": "Beitreten fehlgeschlagen",
"failed_text": "Anruf nicht gefunden oder Beitritt nicht erlaubt",
"knock_reject_body": "Die Teilnahmeanfrage wurde abgelehnt.",
"knock_reject_heading": "Zugriff verweigert",
"knock_reject_body": "Die Mitglieder des Raums haben Deine Beitrittsanfrage abgelehnt.",
"knock_reject_heading": "Beitritt nicht erlaubt",
"reason": "Grund"
},
"hangup_button_label": "Anruf beenden",
@@ -100,11 +100,11 @@
"layout_grid_label": "Raster",
"layout_spotlight_label": "Fokus",
"lobby": {
"ask_to_join": "Teilnahmeanfrage senden",
"ask_to_join": "Beitritt anfragen",
"join_as_guest": "Als Gast beitreten",
"join_button": "Anruf beitreten",
"leave_button": "Zurück zu kürzlichen Anrufen",
"waiting_for_invite": "Anfrage gesendet! Warten auf Teilnahmefreigabe …"
"waiting_for_invite": "Anfrage gesendet"
},
"log_in": "Anmelden",
"logging_in": "Anmelden …",

View File

@@ -81,8 +81,8 @@
"call_ended_heading": "Call ended",
"failed_heading": "Failed to join",
"failed_text": "Call not found or is not accessible.",
"knock_reject_body": "Your request to join was declined.",
"knock_reject_heading": "Access denied",
"knock_reject_body": "The room members declined your request to join.",
"knock_reject_heading": "Not allowed to join",
"reason": "Reason"
},
"hangup_button_label": "End call",
@@ -100,11 +100,11 @@
"layout_grid_label": "Grid",
"layout_spotlight_label": "Spotlight",
"lobby": {
"ask_to_join": "Request to join call",
"ask_to_join": "Ask to join call",
"join_as_guest": "Join as guest",
"join_button": "Join call",
"leave_button": "Back to recents",
"waiting_for_invite": "Request sent! Waiting for permission to join…"
"waiting_for_invite": "Request sent"
},
"log_in": "Log In",
"logging_in": "Logging in…",
@@ -192,7 +192,6 @@
"collapse": "Collapse",
"expand": "Expand",
"mute_for_me": "Mute for me",
"muted_for_me": "Muted for me",
"volume": "Volume"
}
}

View File

@@ -8,7 +8,6 @@ Please see LICENSE in the repository root for full details.
import { FC, useCallback } from "react";
import { Root, Track, Range, Thumb } from "@radix-ui/react-slider";
import classNames from "classnames";
import { Tooltip } from "@vector-im/compound-web";
import styles from "./Slider.module.css";
@@ -67,10 +66,7 @@ export const Slider: FC<Props> = ({
<Track className={styles.track}>
<Range className={styles.highlight} />
</Track>
{/* Note: This is expected not to be visible on mobile.*/}
<Tooltip placement="top" label={Math.round(value * 100).toString() + "%"}>
<Thumb className={styles.handle} aria-label={label} />
</Tooltip>
<Thumb className={styles.handle} aria-label={label} />
</Root>
);
};

View File

@@ -5,14 +5,13 @@ SPDX-License-Identifier: AGPL-3.0-only
Please see LICENSE in the repository root for full details.
*/
@layer normalize, compound-legacy, compound;
/* Inter unexpectedly contains various codepoints which collide with emoji, even
when variation-16 is applied to request the emoji variant. From eyeballing
the emoji picker, these are: 20e3, 23cf, 24c2, 25a0-25c1, 2665, 2764, 2b06, 2b1c.
Therefore we define a unicode-range to load which excludes the glyphs
(to avoid having to maintain a fork of Inter). */
@import url("@fontsource/inter/400.css");
@import url("@fontsource/inter/500.css");
@import url("@fontsource/inter/600.css");
@import url("@fontsource/inter/700.css");
@import url("@fontsource/inconsolata/400.css");
@import url("@fontsource/inconsolata/700.css");
@layer normalize, compound-legacy, compound;
@import url("normalize.css/normalize.css") layer(normalize);
@import url("@vector-im/compound-design-tokens/assets/web/css/compound-design-tokens.css")
@@ -53,6 +52,94 @@ layer(compound);
--stopgap-background-85: rgba(16, 19, 23, 0.85);
}
@font-face {
font-family: "Inter";
font-style: normal;
font-weight: 400;
font-display: swap;
unicode-range: var(--inter-unicode-range);
src:
url("/fonts/Inter/Inter-Regular.woff2") format("woff2"),
url("/fonts/Inter/Inter-Regular.woff") format("woff");
}
@font-face {
font-family: "Inter";
font-style: italic;
font-weight: 400;
font-display: swap;
unicode-range: var(--inter-unicode-range);
src:
url("/fonts/Inter/Inter-Italic.woff2") format("woff2"),
url("/fonts/Inter/Inter-Italic.woff") format("woff");
}
@font-face {
font-family: "Inter";
font-style: normal;
font-weight: 500;
font-display: swap;
unicode-range: var(--inter-unicode-range);
src:
url("/fonts/Inter/Inter-Medium.woff2") format("woff2"),
url("/fonts/Inter/Inter-Medium.woff") format("woff");
}
@font-face {
font-family: "Inter";
font-style: italic;
font-weight: 500;
font-display: swap;
unicode-range: var(--inter-unicode-range);
src:
url("/fonts/Inter/Inter-MediumItalic.woff2") format("woff2"),
url("/fonts/Inter/Inter-MediumItalic.woff") format("woff");
}
@font-face {
font-family: "Inter";
font-style: normal;
font-weight: 600;
font-display: swap;
unicode-range: var(--inter-unicode-range);
src:
url("/fonts/Inter/Inter-SemiBold.woff2") format("woff2"),
url("/fonts/Inter/Inter-SemiBold.woff") format("woff");
}
@font-face {
font-family: "Inter";
font-style: italic;
font-weight: 600;
font-display: swap;
unicode-range: var(--inter-unicode-range);
src:
url("/fonts/Inter/Inter-SemiBoldItalic.woff2") format("woff2"),
url("/fonts/Inter/Inter-SemiBoldItalic.woff") format("woff");
}
@font-face {
font-family: "Inter";
font-style: normal;
font-weight: 700;
font-display: swap;
unicode-range: var(--inter-unicode-range);
src:
url("/fonts/Inter/Inter-Bold.woff2") format("woff2"),
url("/fonts/Inter/Inter-Bold.woff") format("woff");
}
@font-face {
font-family: "Inter";
font-style: italic;
font-weight: 700;
font-display: swap;
unicode-range: var(--inter-unicode-range);
src:
url("/fonts/Inter/Inter-BoldItalic.woff2") format("woff2"),
url("/fonts/Inter/Inter-BoldItalic.woff") format("woff");
}
body {
background-color: var(--cpd-color-bg-canvas-default);
background-size: calc(max(1440px, 100vw)) calc(max(800px, 100vh));
@@ -98,6 +185,10 @@ body[data-platform="ios"] {
--cpd-font-family-sans: -apple-system, BlinkMacSystemFont, "Inter", sans-serif;
}
body[data-platform="desktop"] {
--cpd-font-family-sans: "Inter", sans-serif;
}
@layer compound-legacy {
h1,
h2,

View File

@@ -0,0 +1,77 @@
/*
Copyright 2022-2024 New Vector Ltd.
SPDX-License-Identifier: AGPL-3.0-only
Please see LICENSE in the repository root for full details.
*/
import { MatrixClient } from "matrix-js-sdk/src/client";
import { useTranslation } from "react-i18next";
import { MatrixError } from "matrix-js-sdk/src/matrix";
import { Heading, Text } from "@vector-im/compound-web";
import { Link } from "../button/Link";
import {
useLoadGroupCall,
GroupCallStatus,
CallTerminatedMessage,
} from "./useLoadGroupCall";
import { ErrorView, FullScreenView } from "../FullScreenView";
interface Props {
client: MatrixClient;
roomIdOrAlias: string;
viaServers: string[];
children: (groupCallState: GroupCallStatus) => JSX.Element;
}
export function GroupCallLoader({
client,
roomIdOrAlias,
viaServers,
children,
}: Props): JSX.Element {
const { t } = useTranslation();
const groupCallState = useLoadGroupCall(client, roomIdOrAlias, viaServers);
switch (groupCallState.kind) {
case "loaded":
case "waitForInvite":
case "canKnock":
return children(groupCallState);
case "loading":
return (
<FullScreenView>
<h1>{t("common.loading")}</h1>
</FullScreenView>
);
case "failed":
if ((groupCallState.error as MatrixError).errcode === "M_NOT_FOUND") {
return (
<FullScreenView>
<Heading>{t("group_call_loader.failed_heading")}</Heading>
<Text>{t("group_call_loader.failed_text")}</Text>
{/* XXX: A 'create it for me' button would be the obvious UX here. Two screens already have
dupes of this flow, let's make a common component and put it here. */}
<Link to="/">{t("common.home")}</Link>
</FullScreenView>
);
} else if (groupCallState.error instanceof CallTerminatedMessage) {
return (
<FullScreenView>
<Heading>{groupCallState.error.message}</Heading>
<Text>{groupCallState.error.messageBody}</Text>
{groupCallState.error.reason && (
<>
{t("group_call_loader.reason")}:
<Text size="sm">"{groupCallState.error.reason}"</Text>
</>
)}
<Link to="/">{t("common.home")}</Link>
</FullScreenView>
);
} else {
return <ErrorView error={groupCallState.error} />;
}
}
}

View File

@@ -177,37 +177,29 @@ export const GroupCallView: FC<Props> = ({
}
};
if (skipLobby) {
if (widget) {
if (preload) {
// In preload mode without lobby we wait for a join action before entering
const onJoin = (ev: CustomEvent<IWidgetApiRequest>): void => {
(async (): Promise<void> => {
await defaultDeviceSetup(
ev.detail.data as unknown as JoinCallData,
);
await enterRTCSession(rtcSession, perParticipantE2EE);
widget!.api.transport.reply(ev.detail, {});
})().catch((e) => {
logger.error("Error joining RTC session", e);
});
};
widget.lazyActions.on(ElementWidgetActions.JoinCall, onJoin);
return (): void => {
widget!.lazyActions.off(ElementWidgetActions.JoinCall, onJoin);
};
} else {
// No lobby and no preload: we enter the rtc session right away
(async (): Promise<void> => {
await defaultDeviceSetup({ audioInput: null, videoInput: null });
await enterRTCSession(rtcSession, perParticipantE2EE);
})().catch((e) => {
logger.error("Error joining RTC session", e);
});
}
} else {
void enterRTCSession(rtcSession, perParticipantE2EE);
}
if (widget && preload && skipLobby) {
// In preload mode without lobby we wait for a join action before entering
const onJoin = (ev: CustomEvent<IWidgetApiRequest>): void => {
(async (): Promise<void> => {
await defaultDeviceSetup(ev.detail.data as unknown as JoinCallData);
await enterRTCSession(rtcSession, perParticipantE2EE);
widget!.api.transport.reply(ev.detail, {});
})().catch((e) => {
logger.error("Error joining RTC session", e);
});
};
widget.lazyActions.on(ElementWidgetActions.JoinCall, onJoin);
return (): void => {
widget!.lazyActions.off(ElementWidgetActions.JoinCall, onJoin);
};
} else if (widget && !preload && skipLobby) {
// No lobby and no preload: we enter the rtc session right away
(async (): Promise<void> => {
await defaultDeviceSetup({ audioInput: null, videoInput: null });
await enterRTCSession(rtcSession, perParticipantE2EE);
})().catch((e) => {
logger.error("Error joining RTC session", e);
});
}
}, [rtcSession, preload, skipLobby, perParticipantE2EE]);

View File

@@ -5,16 +5,15 @@ SPDX-License-Identifier: AGPL-3.0-only
Please see LICENSE in the repository root for full details.
*/
import { FC, useEffect, useState, ReactNode, useRef } from "react";
import { FC, useEffect, useState, useCallback, ReactNode } from "react";
import { logger } from "matrix-js-sdk/src/logger";
import { useTranslation } from "react-i18next";
import { CheckIcon } from "@vector-im/compound-design-tokens/assets/web/icons";
import { MatrixError } from "matrix-js-sdk/src/http-api";
import { Heading, Text } from "@vector-im/compound-web";
import { useClientLegacy } from "../ClientContext";
import { ErrorView, FullScreenView, LoadingView } from "../FullScreenView";
import { ErrorView, LoadingView } from "../FullScreenView";
import { RoomAuthView } from "./RoomAuthView";
import { GroupCallLoader } from "./GroupCallLoader";
import { GroupCallView } from "./GroupCallView";
import { useRoomIdentifier, useUrlParams } from "../UrlParams";
import { useRegisterPasswordlessUser } from "../auth/useRegisterPasswordlessUser";
@@ -22,14 +21,13 @@ import { HomePage } from "../home/HomePage";
import { platform } from "../Platform";
import { AppSelectionModal } from "./AppSelectionModal";
import { widget } from "../widget";
import { CallTerminatedMessage, useLoadGroupCall } from "./useLoadGroupCall";
import { GroupCallStatus } from "./useLoadGroupCall";
import { LobbyView } from "./LobbyView";
import { E2eeType } from "../e2ee/e2eeType";
import { useProfile } from "../profile/useProfile";
import { useMuteStates } from "./MuteStates";
import { useOptInAnalytics } from "../settings/settings";
import { Config } from "../config/Config";
import { Link } from "../button/Link";
export const RoomPage: FC = () => {
const {
@@ -55,7 +53,6 @@ export const RoomPage: FC = () => {
useClientLegacy();
const { avatarUrl, displayName: userDisplayName } = useProfile(client);
const groupCallState = useLoadGroupCall(client, roomIdOrAlias, viaServers);
const muteStates = useMuteStates();
useEffect(() => {
@@ -85,112 +82,82 @@ export const RoomPage: FC = () => {
if (optInAnalytics === null && setOptInAnalytics) setOptInAnalytics(true);
}, [optInAnalytics, setOptInAnalytics]);
const wasInWaitForInviteState = useRef<boolean>(false);
useEffect(() => {
if (groupCallState.kind === "loaded" && wasInWaitForInviteState.current) {
logger.log("Play join sound 'Not yet implemented'");
}
}, [groupCallState.kind]);
const groupCallView = (): JSX.Element => {
switch (groupCallState.kind) {
case "loaded":
return (
<GroupCallView
client={client!}
rtcSession={groupCallState.rtcSession}
isPasswordlessUser={passwordlessUser}
confineToRoom={confineToRoom}
preload={preload}
skipLobby={skipLobby || wasInWaitForInviteState.current}
hideHeader={hideHeader}
muteStates={muteStates}
/>
);
case "waitForInvite":
case "canKnock": {
wasInWaitForInviteState.current =
wasInWaitForInviteState.current ||
groupCallState.kind === "waitForInvite";
const knock =
groupCallState.kind === "canKnock" ? groupCallState.knock : null;
const label: string | JSX.Element =
groupCallState.kind === "canKnock" ? (
t("lobby.ask_to_join")
) : (
<>
{t("lobby.waiting_for_invite")}
<CheckIcon />
</>
);
return (
<LobbyView
client={client!}
matrixInfo={{
userId: client!.getUserId() ?? "",
displayName: userDisplayName ?? "",
avatarUrl: avatarUrl ?? "",
roomAlias: null,
roomId: groupCallState.roomSummary.room_id,
roomName: groupCallState.roomSummary.name ?? "",
roomAvatar: groupCallState.roomSummary.avatar_url ?? null,
e2eeSystem: {
kind: groupCallState.roomSummary["im.nheko.summary.encryption"]
? E2eeType.PER_PARTICIPANT
: E2eeType.NONE,
},
}}
onEnter={(): void => knock?.()}
enterLabel={label}
waitingForInvite={groupCallState.kind === "waitForInvite"}
confineToRoom={confineToRoom}
hideHeader={hideHeader}
participantCount={null}
muteStates={muteStates}
onShareClick={null}
/>
);
}
case "loading":
return (
<FullScreenView>
<h1>{t("common.loading")}</h1>
</FullScreenView>
);
case "failed":
wasInWaitForInviteState.current = false;
if ((groupCallState.error as MatrixError).errcode === "M_NOT_FOUND") {
const groupCallView = useCallback(
(groupCallState: GroupCallStatus): JSX.Element => {
switch (groupCallState.kind) {
case "loaded":
return (
<FullScreenView>
<Heading>{t("group_call_loader.failed_heading")}</Heading>
<Text>{t("group_call_loader.failed_text")}</Text>
{/* XXX: A 'create it for me' button would be the obvious UX here. Two screens already have
dupes of this flow, let's make a common component and put it here. */}
<Link to="/">{t("common.home")}</Link>
</FullScreenView>
<GroupCallView
client={client!}
rtcSession={groupCallState.rtcSession}
isPasswordlessUser={passwordlessUser}
confineToRoom={confineToRoom}
preload={preload}
skipLobby={skipLobby}
hideHeader={hideHeader}
muteStates={muteStates}
/>
);
} else if (groupCallState.error instanceof CallTerminatedMessage) {
case "waitForInvite":
case "canKnock": {
const knock =
groupCallState.kind === "canKnock" ? groupCallState.knock : null;
const label: string | JSX.Element =
groupCallState.kind === "canKnock" ? (
t("lobby.ask_to_join")
) : (
<>
{t("lobby.waiting_for_invite")}
<CheckIcon />
</>
);
return (
<FullScreenView>
<Heading>{groupCallState.error.message}</Heading>
<Text>{groupCallState.error.messageBody}</Text>
{groupCallState.error.reason && (
<>
{t("group_call_loader.reason")}:
<Text size="sm">"{groupCallState.error.reason}"</Text>
</>
)}
<Link to="/">{t("common.home")}</Link>
</FullScreenView>
<LobbyView
client={client!}
matrixInfo={{
userId: client!.getUserId() ?? "",
displayName: userDisplayName ?? "",
avatarUrl: avatarUrl ?? "",
roomAlias: null,
roomId: groupCallState.roomSummary.room_id,
roomName: groupCallState.roomSummary.name ?? "",
roomAvatar: groupCallState.roomSummary.avatar_url ?? null,
e2eeSystem: {
kind: groupCallState.roomSummary[
"im.nheko.summary.encryption"
]
? E2eeType.PER_PARTICIPANT
: E2eeType.NONE,
},
}}
onEnter={(): void => knock?.()}
enterLabel={label}
waitingForInvite={groupCallState.kind === "waitForInvite"}
confineToRoom={confineToRoom}
hideHeader={hideHeader}
participantCount={null}
muteStates={muteStates}
onShareClick={null}
/>
);
} else {
return <ErrorView error={groupCallState.error} />;
}
default:
return <> </>;
}
};
default:
return <> </>;
}
},
[
client,
passwordlessUser,
confineToRoom,
preload,
skipLobby,
hideHeader,
muteStates,
t,
userDisplayName,
avatarUrl,
],
);
let content: ReactNode;
if (loading || isRegistering) {
@@ -203,7 +170,15 @@ export const RoomPage: FC = () => {
// TODO: This doesn't belong here, the app routes need to be reworked
content = <HomePage />;
} else {
content = groupCallView();
content = (
<GroupCallLoader
client={client}
roomIdOrAlias={roomIdOrAlias}
viaServers={viaServers}
>
{groupCallView}
</GroupCallLoader>
);
}
return (

View File

@@ -117,8 +117,8 @@ export class CallTerminatedMessage extends Error {
}
export const useLoadGroupCall = (
client: MatrixClient | undefined,
roomIdOrAlias: string | null,
client: MatrixClient,
roomIdOrAlias: string,
viaServers: string[],
): GroupCallStatus => {
const [state, setState] = useState<GroupCallStatus>({ kind: "loading" });
@@ -159,9 +159,6 @@ export const useLoadGroupCall = (
?.getContent().reason;
useEffect(() => {
if (!client || !roomIdOrAlias) {
return;
}
const getRoomByAlias = async (alias: string): Promise<Room> => {
// We lowercase the localpart when we create the room, so we must lowercase
// it here too (we just do the whole alias). We can't do the same to room IDs

View File

@@ -102,7 +102,7 @@ export const playReactionsSound = new Setting<boolean>(
export const soundEffectVolumeSetting = new Setting<number>(
"sound-effect-volume",
0.5,
1,
);
export const alwaysShowSelf = new Setting<boolean>("always-show-self", true);

Binary file not shown.

Binary file not shown.

View File

@@ -26,7 +26,6 @@ import {
VisibilityOnIcon,
UserProfileIcon,
ExpandIcon,
VolumeOffSolidIcon,
} from "@vector-im/compound-design-tokens/assets/web/icons";
import {
ContextMenu,
@@ -63,7 +62,6 @@ interface TileProps {
interface UserMediaTileProps extends TileProps {
vm: UserMediaViewModel;
mirror: boolean;
locallyMuted: boolean;
menuStart?: ReactNode;
menuEnd?: ReactNode;
}
@@ -73,7 +71,6 @@ const UserMediaTile = forwardRef<HTMLDivElement, UserMediaTileProps>(
{
vm,
showSpeakingIndicators,
locallyMuted,
menuStart,
menuEnd,
className,
@@ -99,16 +96,7 @@ const UserMediaTile = forwardRef<HTMLDivElement, UserMediaTileProps>(
);
const { raisedHands, lowerHand, reactions } = useReactions();
const AudioIcon = locallyMuted
? VolumeOffSolidIcon
: audioEnabled
? MicOnSolidIcon
: MicOffSolidIcon;
const audioIconLabel = locallyMuted
? t("video_tile.muted_for_me")
: audioEnabled
? t("microphone_on")
: t("microphone_off");
const MicIcon = audioEnabled ? MicOnSolidIcon : MicOffSolidIcon;
const [menuOpen, setMenuOpen] = useState(false);
const menu = (
@@ -146,11 +134,11 @@ const UserMediaTile = forwardRef<HTMLDivElement, UserMediaTileProps>(
[styles.handRaised]: !showSpeaking && !!handRaised,
})}
nameTagLeadingIcon={
<AudioIcon
<MicIcon
width={20}
height={20}
aria-label={audioIconLabel}
data-muted={locallyMuted || !audioEnabled}
aria-label={audioEnabled ? t("microphone_on") : t("microphone_off")}
data-muted={!audioEnabled}
className={styles.muteIcon}
/>
}
@@ -211,7 +199,6 @@ const LocalUserMediaTile = forwardRef<HTMLDivElement, LocalUserMediaTileProps>(
<UserMediaTile
ref={ref}
vm={vm}
locallyMuted={false}
mirror={mirror}
menuStart={
<ToggleMenuItem
@@ -268,7 +255,6 @@ const RemoteUserMediaTile = forwardRef<
<UserMediaTile
ref={ref}
vm={vm}
locallyMuted={locallyMuted}
mirror={false}
menuStart={
<>

View File

@@ -195,12 +195,11 @@ export const ReactionsProvider = ({
// Skip any event without a sender or event ID.
if (!sender || !reactionEventId) return;
room.client
.decryptEventIfNeeded(event)
.catch((e) => logger.warn(`Failed to decrypt ${event.getId()}`, e));
if (event.isBeingDecrypted() || event.isDecryptionFailure()) return;
if (event.getType() === ElementCallReactionEventType) {
room.client
.decryptEventIfNeeded(event)
.catch((e) => logger.warn(`Failed to decrypt ${event.getId()}`, e));
if (event.isBeingDecrypted() || event.isDecryptionFailure()) return;
const content: ECallReactionEventContent = event.getContent();
const membershipEventId = content?.["m.relates_to"]?.event_id;

View File

@@ -123,7 +123,6 @@ export async function initClient(
localTimeoutMs: 5000,
useE2eForGroupCall: e2eEnabled,
fallbackICEServerAllowed: fallbackICEServerAllowed,
store: new MemoryStore(),
});
// In case of logging in a new matrix account but there is still crypto local store. This is needed for:

View File

@@ -8,7 +8,6 @@ Please see LICENSE in the repository root for full details.
import { defineConfig, loadEnv } from "vite";
import svgrPlugin from "vite-plugin-svgr";
import htmlTemplate from "vite-plugin-html-template";
import { codecovVitePlugin } from "@codecov/vite-plugin";
import { sentryVitePlugin } from "@sentry/vite-plugin";
import react from "@vitejs/plugin-react";
import basicSsl from "@vitejs/plugin-basic-ssl";
@@ -32,12 +31,6 @@ export default defineConfig(({ mode }) => {
title: env.VITE_PRODUCT_NAME || "Element Call",
},
}),
codecovVitePlugin({
enableBundleAnalysis: process.env.CODECOV_TOKEN !== undefined,
bundleName: "element-call",
uploadToken: process.env.CODECOV_TOKEN,
}),
];
if (

962
yarn.lock

File diff suppressed because it is too large Load Diff