Remove more usages of singleton store getter in favour of contexts (#34099)
* Expose SDKContextClass via window for debugging * Remove stores from window if they are exposed via sdkContext * Avoid usages of global store instance where React context is accessible * Remove more usages of singleton store getter in favour of contexts * Remove more usages of singleton store getter in favour of contexts * Fix tests by adding SDKContext.Provider * Fix tests by adding SDKContext.Provider * Fix tests by adding SDKContext.Provider * Fix tests by adding SDKContext.Provider * Fix tests * Fix tests * Fix tests * Iterate * Fix bad merge * Iterate * Fix tests * Iterate * Iterate * Iterate * Iterate * Iterate * Improve coverage * Improve coverage
This commit is contained in:
+35
-30
@@ -19,6 +19,7 @@ import AsyncWrapper from "./AsyncWrapper";
|
||||
import { type Defaultize } from "./@types/common";
|
||||
import { type ActionPayload } from "./dispatcher/payloads";
|
||||
import { filterBoolean } from "./utils/arrays.ts";
|
||||
import { SDKContext } from "./contexts/SDKContext.ts";
|
||||
|
||||
const DIALOG_CONTAINER_ID = "mx_Dialog_Container";
|
||||
const STATIC_DIALOG_CONTAINER_ID = "mx_Dialog_StaticContainer";
|
||||
@@ -437,21 +438,23 @@ export class ModalManager extends TypedEventEmitter<ModalManagerEvent, HandlerMa
|
||||
|
||||
const staticDialog = (
|
||||
<StrictMode>
|
||||
{/* Provide I18nContext for shared-components used inside dialogs rendered in a separate root. */}
|
||||
<I18nContext.Provider value={window.mxModuleApi.i18n}>
|
||||
<TooltipProvider>
|
||||
<div className={classes}>
|
||||
<Glass className="mx_Dialog_border">
|
||||
<div className="mx_Dialog">{this.staticModal.elem}</div>
|
||||
</Glass>
|
||||
<div
|
||||
data-testid="dialog-background"
|
||||
className="mx_Dialog_background mx_Dialog_staticBackground"
|
||||
onClick={this.onBackgroundClick}
|
||||
/>
|
||||
</div>
|
||||
</TooltipProvider>
|
||||
</I18nContext.Provider>
|
||||
<SDKContext.Provider value={window.mxSdkContext}>
|
||||
{/* Provide I18nContext for shared-components used inside dialogs rendered in a separate root. */}
|
||||
<I18nContext.Provider value={window.mxModuleApi.i18n}>
|
||||
<TooltipProvider>
|
||||
<div className={classes}>
|
||||
<Glass className="mx_Dialog_border">
|
||||
<div className="mx_Dialog">{this.staticModal.elem}</div>
|
||||
</Glass>
|
||||
<div
|
||||
data-testid="dialog-background"
|
||||
className="mx_Dialog_background mx_Dialog_staticBackground"
|
||||
onClick={this.onBackgroundClick}
|
||||
/>
|
||||
</div>
|
||||
</TooltipProvider>
|
||||
</I18nContext.Provider>
|
||||
</SDKContext.Provider>
|
||||
</StrictMode>
|
||||
);
|
||||
|
||||
@@ -469,21 +472,23 @@ export class ModalManager extends TypedEventEmitter<ModalManagerEvent, HandlerMa
|
||||
|
||||
const dialog = (
|
||||
<StrictMode>
|
||||
{/* Provide I18nContext for shared-components used inside dialogs rendered in a separate root. */}
|
||||
<I18nContext.Provider value={window.mxModuleApi.i18n}>
|
||||
<TooltipProvider>
|
||||
<div className={classes}>
|
||||
<Glass className="mx_Dialog_border">
|
||||
<div className="mx_Dialog">{modal.elem}</div>
|
||||
</Glass>
|
||||
<div
|
||||
data-testid="dialog-background"
|
||||
className="mx_Dialog_background"
|
||||
onClick={this.onBackgroundClick}
|
||||
/>
|
||||
</div>
|
||||
</TooltipProvider>
|
||||
</I18nContext.Provider>
|
||||
<SDKContext.Provider value={window.mxSdkContext}>
|
||||
{/* Provide I18nContext for shared-components used inside dialogs rendered in a separate root. */}
|
||||
<I18nContext.Provider value={window.mxModuleApi.i18n}>
|
||||
<TooltipProvider>
|
||||
<div className={classes}>
|
||||
<Glass className="mx_Dialog_border">
|
||||
<div className="mx_Dialog">{modal.elem}</div>
|
||||
</Glass>
|
||||
<div
|
||||
data-testid="dialog-background"
|
||||
className="mx_Dialog_background"
|
||||
onClick={this.onBackgroundClick}
|
||||
/>
|
||||
</div>
|
||||
</TooltipProvider>
|
||||
</I18nContext.Provider>
|
||||
</SDKContext.Provider>
|
||||
</StrictMode>
|
||||
);
|
||||
|
||||
|
||||
@@ -53,7 +53,6 @@ import UserView from "./UserView";
|
||||
import { mediaFromMxc } from "../../customisations/Media";
|
||||
import { UserTab } from "../views/dialogs/UserTab";
|
||||
import { type OpenToTabPayload } from "../../dispatcher/payloads/OpenToTabPayload";
|
||||
import RightPanelStore from "../../stores/right-panel/RightPanelStore";
|
||||
import { TimelineRenderingType } from "../../contexts/RoomContext";
|
||||
import { KeyBindingAction } from "../../accessibility/KeyboardShortcuts";
|
||||
import { type SwitchSpacePayload } from "../../dispatcher/payloads/SwitchSpacePayload";
|
||||
@@ -494,7 +493,7 @@ class LoggedInView extends React.Component<IProps, IState> {
|
||||
break;
|
||||
case KeyBindingAction.ToggleRoomSidePanel:
|
||||
if (this.props.page_type === "room_view") {
|
||||
RightPanelStore.instance.togglePanel(null);
|
||||
this.context.rightPanelStore.togglePanel(null);
|
||||
handled = true;
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -129,7 +129,6 @@ import { WaitingForThirdPartyRoomView } from "./WaitingForThirdPartyRoomView";
|
||||
import { isNotUndefined } from "../../Typeguards";
|
||||
import { type CancelAskToJoinPayload } from "../../dispatcher/payloads/CancelAskToJoinPayload";
|
||||
import { type SubmitAskToJoinPayload } from "../../dispatcher/payloads/SubmitAskToJoinPayload";
|
||||
import RightPanelStore from "../../stores/right-panel/RightPanelStore";
|
||||
import { onView3pidInvite } from "../../stores/right-panel/action-handlers";
|
||||
import RoomSearchAuxPanel from "../views/rooms/RoomSearchAuxPanel";
|
||||
import { PinnedMessageBanner } from "../views/rooms/PinnedMessageBanner";
|
||||
@@ -1332,23 +1331,23 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
||||
case Action.ViewUser:
|
||||
if (payload.member) {
|
||||
if (payload.push) {
|
||||
RightPanelStore.instance.pushCard({
|
||||
this.context.rightPanelStore.pushCard({
|
||||
phase: RightPanelPhases.MemberInfo,
|
||||
state: { member: payload.member },
|
||||
});
|
||||
} else {
|
||||
RightPanelStore.instance.setCards([
|
||||
this.context.rightPanelStore.setCards([
|
||||
{ phase: RightPanelPhases.RoomSummary },
|
||||
{ phase: RightPanelPhases.MemberList },
|
||||
{ phase: RightPanelPhases.MemberInfo, state: { member: payload.member } },
|
||||
]);
|
||||
}
|
||||
} else {
|
||||
RightPanelStore.instance.showOrHidePhase(RightPanelPhases.MemberList);
|
||||
this.context.rightPanelStore.showOrHidePhase(RightPanelPhases.MemberList);
|
||||
}
|
||||
break;
|
||||
case Action.View3pidInvite:
|
||||
onView3pidInvite(payload, RightPanelStore.instance);
|
||||
onView3pidInvite(payload, this.context.rightPanelStore);
|
||||
break;
|
||||
case Action.FocusMessageSearch:
|
||||
if ((payload as FocusMessageSearchPayload).initialText) {
|
||||
|
||||
@@ -20,7 +20,6 @@ import {
|
||||
VideoCallSolidIcon,
|
||||
} from "@vector-im/compound-design-tokens/assets/web/icons";
|
||||
|
||||
import MatrixClientContext from "../../contexts/MatrixClientContext";
|
||||
import createRoom, { type IOpts } from "../../createRoom";
|
||||
import { shouldShowComponent } from "../../customisations/helpers/UIComponents";
|
||||
import { Action } from "../../dispatcher/actions";
|
||||
@@ -37,7 +36,6 @@ import PosthogTrackers from "../../PosthogTrackers";
|
||||
import { showRoomInviteDialog } from "../../RoomInvite";
|
||||
import { UIComponent } from "../../settings/UIFeature";
|
||||
import { UPDATE_EVENT } from "../../stores/AsyncStore";
|
||||
import RightPanelStore from "../../stores/right-panel/RightPanelStore";
|
||||
import { RightPanelPhases } from "../../stores/right-panel/RightPanelStorePhases";
|
||||
import type ResizeNotifier from "../../utils/ResizeNotifier";
|
||||
import {
|
||||
@@ -77,6 +75,7 @@ import { type RoomPermalinkCreator } from "../../utils/permalinks/Permalinks";
|
||||
import SpacePillButton from "./SpacePillButton.tsx";
|
||||
import { useRoomName } from "../../hooks/useRoomName.ts";
|
||||
import MultiInviter from "../../utils/MultiInviter.ts";
|
||||
import { SDKContext } from "../../contexts/SDKContext.ts";
|
||||
|
||||
interface IProps {
|
||||
space: Room;
|
||||
@@ -212,18 +211,18 @@ const SpaceLandingAddButton: React.FC<{ space: Room }> = ({ space }) => {
|
||||
};
|
||||
|
||||
const SpaceLanding: React.FC<{ space: Room }> = ({ space }) => {
|
||||
const cli = useContext(MatrixClientContext);
|
||||
const sdkContext = useContext(SDKContext);
|
||||
const myMembership = useMyRoomMembership(space);
|
||||
const userId = cli.getSafeUserId();
|
||||
const userId = sdkContext.client!.getSafeUserId();
|
||||
const name = useRoomName(space);
|
||||
|
||||
const storeIsShowingSpaceMembers = useCallback(
|
||||
() =>
|
||||
RightPanelStore.instance.isOpenForRoom(space.roomId) &&
|
||||
RightPanelStore.instance.currentCardForRoom(space.roomId)?.phase === RightPanelPhases.MemberList,
|
||||
[space.roomId],
|
||||
sdkContext.rightPanelStore.isOpenForRoom(space.roomId) &&
|
||||
sdkContext.rightPanelStore.currentCardForRoom(space.roomId)?.phase === RightPanelPhases.MemberList,
|
||||
[space.roomId, sdkContext.rightPanelStore],
|
||||
);
|
||||
const isShowingMembers = useEventEmitterState(RightPanelStore.instance, UPDATE_EVENT, storeIsShowingSpaceMembers);
|
||||
const isShowingMembers = useEventEmitterState(sdkContext.rightPanelStore, UPDATE_EVENT, storeIsShowingSpaceMembers);
|
||||
|
||||
let inviteButton;
|
||||
if (shouldShowSpaceInvite(space) && shouldShowComponent(UIComponent.InviteUsers)) {
|
||||
@@ -266,7 +265,7 @@ const SpaceLanding: React.FC<{ space: Room }> = ({ space }) => {
|
||||
}
|
||||
|
||||
const onMembersClick = (): void => {
|
||||
RightPanelStore.instance.setCard({ phase: RightPanelPhases.MemberList });
|
||||
sdkContext.rightPanelStore.setCard({ phase: RightPanelPhases.MemberList });
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -603,18 +602,18 @@ const SpaceSetupPrivateInvite: React.FC<{
|
||||
};
|
||||
|
||||
export default class SpaceRoomView extends React.PureComponent<IProps, IState> {
|
||||
public static contextType = MatrixClientContext;
|
||||
declare public context: React.ContextType<typeof MatrixClientContext>;
|
||||
public static contextType = SDKContext;
|
||||
declare public context: React.ContextType<typeof SDKContext>;
|
||||
|
||||
private dispatcherRef?: string;
|
||||
|
||||
public constructor(props: IProps, context: React.ContextType<typeof MatrixClientContext>) {
|
||||
public constructor(props: IProps, context: React.ContextType<typeof SDKContext>) {
|
||||
super(props, context);
|
||||
|
||||
let phase = Phase.Landing;
|
||||
|
||||
const creator = this.props.space.currentState.getStateEvents(EventType.RoomCreate, "")?.getSender();
|
||||
const showSetup = this.props.justCreatedOpts && context.getSafeUserId() === creator;
|
||||
const showSetup = this.props.justCreatedOpts && this.context.client?.getSafeUserId() === creator;
|
||||
|
||||
if (showSetup) {
|
||||
phase =
|
||||
@@ -625,21 +624,21 @@ export default class SpaceRoomView extends React.PureComponent<IProps, IState> {
|
||||
|
||||
this.state = {
|
||||
phase,
|
||||
showRightPanel: RightPanelStore.instance.isOpenForRoom(this.props.space.roomId),
|
||||
showRightPanel: this.context.rightPanelStore.isOpenForRoom(this.props.space.roomId),
|
||||
myMembership: this.props.space.getMyMembership(),
|
||||
};
|
||||
}
|
||||
|
||||
public componentDidMount(): void {
|
||||
this.dispatcherRef = defaultDispatcher.register(this.onAction);
|
||||
RightPanelStore.instance.on(UPDATE_EVENT, this.onRightPanelStoreUpdate);
|
||||
this.context.on(RoomEvent.MyMembership, this.onMyMembership);
|
||||
this.context.rightPanelStore.on(UPDATE_EVENT, this.onRightPanelStoreUpdate);
|
||||
this.context.client?.on(RoomEvent.MyMembership, this.onMyMembership);
|
||||
}
|
||||
|
||||
public componentWillUnmount(): void {
|
||||
defaultDispatcher.unregister(this.dispatcherRef);
|
||||
RightPanelStore.instance.off(UPDATE_EVENT, this.onRightPanelStoreUpdate);
|
||||
this.context.off(RoomEvent.MyMembership, this.onMyMembership);
|
||||
this.context.rightPanelStore.off(UPDATE_EVENT, this.onRightPanelStoreUpdate);
|
||||
this.context.client?.off(RoomEvent.MyMembership, this.onMyMembership);
|
||||
}
|
||||
|
||||
private onMyMembership = (room: Room, myMembership: string): void => {
|
||||
@@ -650,7 +649,7 @@ export default class SpaceRoomView extends React.PureComponent<IProps, IState> {
|
||||
|
||||
private onRightPanelStoreUpdate = (): void => {
|
||||
this.setState({
|
||||
showRightPanel: RightPanelStore.instance.isOpenForRoom(this.props.space.roomId),
|
||||
showRightPanel: this.context.rightPanelStore.isOpenForRoom(this.props.space.roomId),
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
+7
-7
@@ -8,10 +8,10 @@ import { type MatrixClient, type RoomMember, type User } from "matrix-js-sdk/src
|
||||
import { useContext } from "react";
|
||||
import { type UserVerificationStatus } from "matrix-js-sdk/src/crypto-api";
|
||||
|
||||
import MatrixClientContext from "../../../../contexts/MatrixClientContext";
|
||||
import { type IDevice } from "../../../views/right_panel/UserInfo";
|
||||
import { useAsyncMemo } from "../../../../hooks/useAsyncMemo";
|
||||
import { verifyUser } from "../../../../verification";
|
||||
import { SDKContext } from "../../../../contexts/SDKContext.ts";
|
||||
|
||||
export interface UserInfoVerificationSectionState {
|
||||
/**
|
||||
@@ -26,7 +26,7 @@ export interface UserInfoVerificationSectionState {
|
||||
/**
|
||||
* callback function when verifyUser button is clicked
|
||||
*/
|
||||
verifySelectedUser: () => Promise<void>;
|
||||
verifySelectedUser: () => void;
|
||||
}
|
||||
|
||||
const useHasCrossSigningKeys = (cli: MatrixClient, member: User, canVerify: boolean): boolean | undefined => {
|
||||
@@ -44,21 +44,21 @@ export const useUserInfoVerificationViewModel = (
|
||||
member: User | RoomMember,
|
||||
devices: IDevice[],
|
||||
): UserInfoVerificationSectionState => {
|
||||
const cli = useContext(MatrixClientContext);
|
||||
const sdkContext = useContext(SDKContext);
|
||||
|
||||
const userTrust = useAsyncMemo<UserVerificationStatus | undefined>(
|
||||
async () => cli.getCrypto()?.getUserVerificationStatus(member.userId),
|
||||
async () => sdkContext.client?.getCrypto()?.getUserVerificationStatus(member.userId),
|
||||
[member.userId],
|
||||
// the user verification status is not initialized
|
||||
undefined,
|
||||
);
|
||||
const hasUserVerificationStatus = Boolean(userTrust);
|
||||
const isUserVerified = Boolean(userTrust?.isVerified());
|
||||
const isMe = member.userId === cli.getUserId();
|
||||
const isMe = member.userId === sdkContext.client!.getUserId();
|
||||
const canVerify = hasUserVerificationStatus && !isUserVerified && !isMe && devices && devices.length > 0;
|
||||
|
||||
const hasCrossSigningKeys = useHasCrossSigningKeys(cli, member as User, canVerify);
|
||||
const verifySelectedUser = (): Promise<void> => verifyUser(cli, member as User);
|
||||
const hasCrossSigningKeys = useHasCrossSigningKeys(sdkContext.client!, member as User, canVerify);
|
||||
const verifySelectedUser = (): void => verifyUser(sdkContext.rightPanelStore, sdkContext.client!, member as User);
|
||||
|
||||
return {
|
||||
canVerify,
|
||||
|
||||
@@ -6,7 +6,7 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Com
|
||||
Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
import React, { useCallback, useEffect } from "react";
|
||||
import React, { useCallback, useContext, useEffect } from "react";
|
||||
import { type MatrixEvent } from "matrix-js-sdk/src/matrix";
|
||||
import { LinkIcon, OverflowHorizontalIcon, VisibilityOnIcon } from "@vector-im/compound-design-tokens/assets/web/icons";
|
||||
|
||||
@@ -18,9 +18,8 @@ import { copyPlaintext } from "../../../utils/strings";
|
||||
import { ChevronFace, ContextMenuTooltipButton, type MenuProps, useContextMenu } from "../../structures/ContextMenu";
|
||||
import { _t } from "../../../languageHandler";
|
||||
import IconizedContextMenu, { IconizedContextMenuOption, IconizedContextMenuOptionList } from "./IconizedContextMenu";
|
||||
import { WidgetLayoutStore } from "../../../stores/widgets/WidgetLayoutStore";
|
||||
import { MatrixClientPeg } from "../../../MatrixClientPeg";
|
||||
import { type ViewRoomPayload } from "../../../dispatcher/payloads/ViewRoomPayload";
|
||||
import { SDKContext } from "../../../contexts/SDKContext.ts";
|
||||
|
||||
export interface ThreadListContextMenuProps {
|
||||
mxEvent: MatrixEvent;
|
||||
@@ -42,6 +41,7 @@ const ThreadListContextMenu: React.FC<ThreadListContextMenuProps> = ({
|
||||
onMenuToggle,
|
||||
...props
|
||||
}) => {
|
||||
const sdkContext = useContext(SDKContext);
|
||||
const [menuDisplayed, button, openMenu, closeThreadOptions] = useContextMenu();
|
||||
|
||||
const viewInRoom = useCallback(
|
||||
@@ -77,8 +77,8 @@ const ThreadListContextMenu: React.FC<ThreadListContextMenuProps> = ({
|
||||
onMenuToggle?.(menuDisplayed);
|
||||
}, [menuDisplayed, onMenuToggle]);
|
||||
|
||||
const room = MatrixClientPeg.safeGet().getRoom(mxEvent.getRoomId());
|
||||
const isMainSplitTimelineShown = !!room && !WidgetLayoutStore.instance.hasMaximisedWidget(room);
|
||||
const room = sdkContext.client?.getRoom(mxEvent.getRoomId());
|
||||
const isMainSplitTimelineShown = !!room && !sdkContext.widgetLayoutStore.hasMaximisedWidget(room);
|
||||
return (
|
||||
<React.Fragment>
|
||||
<ContextMenuTooltipButton
|
||||
|
||||
@@ -135,7 +135,7 @@ interface IState {
|
||||
|
||||
export default class AppTile extends React.Component<IProps, IState> {
|
||||
public static contextType = SDKContext;
|
||||
declare public context: ContextType<typeof SDKContext>;
|
||||
declare public context: React.ContextType<typeof SDKContext>;
|
||||
|
||||
public static defaultProps: Partial<IProps> = {
|
||||
waitForIframeLoad: true,
|
||||
|
||||
@@ -6,7 +6,7 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Com
|
||||
Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
import React, { type ReactNode, type KeyboardEvent, type Ref, type MouseEvent, useMemo } from "react";
|
||||
import React, { type ReactNode, type KeyboardEvent, type Ref, type MouseEvent, useMemo, useContext } from "react";
|
||||
import classNames from "classnames";
|
||||
import { IconButton, Text } from "@vector-im/compound-web";
|
||||
import CloseIcon from "@vector-im/compound-design-tokens/assets/web/icons/close";
|
||||
@@ -14,9 +14,9 @@ import ChevronLeftIcon from "@vector-im/compound-design-tokens/assets/web/icons/
|
||||
import { AutoHideScrollbar } from "@element-hq/web-shared-components";
|
||||
|
||||
import { _t } from "../../../languageHandler";
|
||||
import RightPanelStore from "../../../stores/right-panel/RightPanelStore";
|
||||
import { backLabelForPhase } from "../../../stores/right-panel/RightPanelStorePhases";
|
||||
import { CardContext } from "./context";
|
||||
import { SDKContext } from "../../../contexts/SDKContext.ts";
|
||||
|
||||
interface IProps {
|
||||
header?: ReactNode | null;
|
||||
@@ -37,12 +37,6 @@ interface IProps {
|
||||
children: ReactNode;
|
||||
}
|
||||
|
||||
function closeRightPanel(ev: MouseEvent<HTMLButtonElement>): void {
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
RightPanelStore.instance.popCard();
|
||||
}
|
||||
|
||||
const BaseCard: React.FC<IProps> = ({
|
||||
closeLabel,
|
||||
onClose,
|
||||
@@ -60,13 +54,15 @@ const BaseCard: React.FC<IProps> = ({
|
||||
closeButtonRef,
|
||||
ref,
|
||||
}: IProps) => {
|
||||
const sdkContext = useContext(SDKContext);
|
||||
|
||||
let backButton;
|
||||
const cardHistory = RightPanelStore.instance.roomPhaseHistory;
|
||||
const cardHistory = sdkContext.rightPanelStore.roomPhaseHistory;
|
||||
if (cardHistory.length > 1 && !hideHeaderButtons) {
|
||||
const prevCard = cardHistory[cardHistory.length - 2];
|
||||
const onBackClick = (ev: MouseEvent<HTMLButtonElement>): void => {
|
||||
onBack?.(ev);
|
||||
RightPanelStore.instance.popCard();
|
||||
sdkContext.rightPanelStore.popCard();
|
||||
};
|
||||
const label = backLabelForPhase(prevCard.phase) ?? _t("action|back");
|
||||
backButton = (
|
||||
@@ -84,6 +80,13 @@ const BaseCard: React.FC<IProps> = ({
|
||||
|
||||
let closeButton;
|
||||
if (!hideHeaderButtons) {
|
||||
// eslint-disable-next-line no-inner-declarations
|
||||
function closeRightPanel(ev: MouseEvent<HTMLButtonElement>): void {
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
sdkContext.rightPanelStore.popCard();
|
||||
}
|
||||
|
||||
closeButton = (
|
||||
<IconButton
|
||||
size="28px"
|
||||
|
||||
@@ -6,7 +6,7 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Com
|
||||
Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
import React, { useCallback, useEffect, useRef, useState } from "react";
|
||||
import React, { useCallback, useContext, useEffect, useRef, useState } from "react";
|
||||
import { VerificationPhase, type VerificationRequest, VerificationRequestEvent } from "matrix-js-sdk/src/crypto-api";
|
||||
import { type RoomMember, type User } from "matrix-js-sdk/src/matrix";
|
||||
import { logger } from "matrix-js-sdk/src/logger";
|
||||
@@ -21,9 +21,8 @@ import { useTypedEventEmitter } from "../../../hooks/useEventEmitter";
|
||||
import Modal from "../../../Modal";
|
||||
import { _t } from "../../../languageHandler";
|
||||
import { RightPanelPhases } from "../../../stores/right-panel/RightPanelStorePhases";
|
||||
import RightPanelStore from "../../../stores/right-panel/RightPanelStore";
|
||||
import ErrorDialog from "../dialogs/ErrorDialog";
|
||||
import { useMatrixClientContext } from "../../../contexts/MatrixClientContext";
|
||||
import { SDKContext } from "../../../contexts/SDKContext.ts";
|
||||
|
||||
// cancellation codes which constitute a key mismatch
|
||||
const MISMATCHES = ["m.key_mismatch", "m.user_error", "m.mismatched_sas"];
|
||||
@@ -38,7 +37,7 @@ interface IProps {
|
||||
}
|
||||
|
||||
const EncryptionPanel: React.FC<IProps> = (props: IProps) => {
|
||||
const cli = useMatrixClientContext();
|
||||
const sdkContext = useContext(SDKContext);
|
||||
const { verificationRequest, verificationRequestPromise, member, onClose, layout, isRoomEncrypted } = props;
|
||||
const [request, setRequest] = useState(verificationRequest);
|
||||
// state to show a spinner immediately after clicking "start verification",
|
||||
@@ -111,11 +110,11 @@ const EncryptionPanel: React.FC<IProps> = (props: IProps) => {
|
||||
setRequesting(true);
|
||||
let verificationRequest_: VerificationRequest;
|
||||
try {
|
||||
const roomId = await ensureDMExists(cli, member.userId);
|
||||
const roomId = await ensureDMExists(sdkContext.client!, member.userId);
|
||||
if (!roomId) {
|
||||
throw new Error("Unable to create Room for verification");
|
||||
}
|
||||
verificationRequest_ = await cli.getCrypto()!.requestVerificationDM(member.userId, roomId);
|
||||
verificationRequest_ = await sdkContext.client!.getCrypto()!.requestVerificationDM(member.userId, roomId);
|
||||
} catch (e) {
|
||||
console.error("Error starting verification", e);
|
||||
setRequesting(false);
|
||||
@@ -130,20 +129,20 @@ const EncryptionPanel: React.FC<IProps> = (props: IProps) => {
|
||||
setRequest(verificationRequest_);
|
||||
setPhase(verificationRequest_.phase);
|
||||
// Notify the RightPanelStore about this
|
||||
if (RightPanelStore.instance.currentCard.phase != RightPanelPhases.EncryptionPanel) {
|
||||
RightPanelStore.instance.pushCard({
|
||||
if (sdkContext.rightPanelStore.currentCard.phase != RightPanelPhases.EncryptionPanel) {
|
||||
sdkContext.rightPanelStore.pushCard({
|
||||
phase: RightPanelPhases.EncryptionPanel,
|
||||
state: { member, verificationRequest: verificationRequest_ },
|
||||
});
|
||||
}
|
||||
if (!RightPanelStore.instance.isOpen) RightPanelStore.instance.togglePanel(null);
|
||||
}, [cli, member]);
|
||||
if (!sdkContext.rightPanelStore.isOpen) sdkContext.rightPanelStore.togglePanel(null);
|
||||
}, [sdkContext.client, sdkContext.rightPanelStore, member]);
|
||||
|
||||
const requested: boolean =
|
||||
(!request && isRequesting) ||
|
||||
(!!request &&
|
||||
(phase === VerificationPhase.Requested || phase === VerificationPhase.Unsent || phase === undefined));
|
||||
const isSelfVerification = request ? request.isSelfVerification : member.userId === cli.getUserId();
|
||||
const isSelfVerification = request ? request.isSelfVerification : member.userId === sdkContext.client?.getUserId();
|
||||
|
||||
if (!request || requested) {
|
||||
const initiatedByMe = (!request && isRequesting) || (!!request && request.initiatedByMe);
|
||||
|
||||
@@ -6,7 +6,7 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Com
|
||||
Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
import React, { type JSX, useEffect, useMemo, useState } from "react";
|
||||
import React, { type JSX, useContext, useEffect, useMemo, useState } from "react";
|
||||
import { type Room } from "matrix-js-sdk/src/matrix";
|
||||
import classNames from "classnames";
|
||||
import { Button, Link, Separator, Text } from "@vector-im/compound-web";
|
||||
@@ -21,10 +21,9 @@ import BaseCard from "./BaseCard";
|
||||
import WidgetUtils, { useWidgets } from "../../../utils/WidgetUtils";
|
||||
import { _t } from "../../../languageHandler";
|
||||
import { useContextMenu } from "../../structures/ContextMenu";
|
||||
import RightPanelStore from "../../../stores/right-panel/RightPanelStore";
|
||||
import { type IApp } from "../../../stores/WidgetStore";
|
||||
import { RightPanelPhases } from "../../../stores/right-panel/RightPanelStorePhases";
|
||||
import { MAX_PINNED, WidgetLayoutStore } from "../../../stores/widgets/WidgetLayoutStore";
|
||||
import { MAX_PINNED } from "../../../stores/widgets/WidgetLayoutStore";
|
||||
import AccessibleButton from "../elements/AccessibleButton";
|
||||
import WidgetAvatar from "../avatars/WidgetAvatar";
|
||||
import { IntegrationManagers } from "../../../integrations/IntegrationManagers";
|
||||
@@ -32,6 +31,7 @@ import EmptyState from "./EmptyState";
|
||||
import { shouldShowComponent } from "../../../customisations/helpers/UIComponents.ts";
|
||||
import { UIComponent } from "../../../settings/UIFeature.ts";
|
||||
import { WidgetContextMenu } from "../../../viewmodels/room/right-panel/WidgetContextMenuViewModel.tsx";
|
||||
import { SDKContext } from "../../../contexts/SDKContext.ts";
|
||||
|
||||
interface Props {
|
||||
room: Room;
|
||||
@@ -44,6 +44,7 @@ interface IAppRowProps {
|
||||
}
|
||||
|
||||
const AppRow: React.FC<IAppRowProps> = ({ app, room }) => {
|
||||
const sdkContext = useContext(SDKContext);
|
||||
const name = WidgetUtils.getWidgetName(app);
|
||||
const [canModifyWidget, setCanModifyWidget] = useState<boolean>();
|
||||
|
||||
@@ -52,24 +53,24 @@ const AppRow: React.FC<IAppRowProps> = ({ app, room }) => {
|
||||
}, [room.client, room.roomId]);
|
||||
|
||||
const onOpenWidgetClick = (): void => {
|
||||
RightPanelStore.instance.pushCard({
|
||||
sdkContext.rightPanelStore.pushCard({
|
||||
phase: RightPanelPhases.Widget,
|
||||
state: { widgetId: app.id },
|
||||
});
|
||||
};
|
||||
|
||||
const isPinned = WidgetLayoutStore.instance.isInContainer(room, app, "top");
|
||||
const isPinned = sdkContext.widgetLayoutStore.isInContainer(room, app, "top");
|
||||
const togglePin = isPinned
|
||||
? () => {
|
||||
WidgetLayoutStore.instance.moveToContainer(room, app, "right");
|
||||
sdkContext.widgetLayoutStore.moveToContainer(room, app, "right");
|
||||
}
|
||||
: () => {
|
||||
WidgetLayoutStore.instance.moveToContainer(room, app, "top");
|
||||
sdkContext.widgetLayoutStore.moveToContainer(room, app, "top");
|
||||
};
|
||||
|
||||
const [menuDisplayed, handle, openMenu, closeMenu] = useContextMenu<HTMLDivElement>();
|
||||
|
||||
const cannotPin = !isPinned && !WidgetLayoutStore.instance.canAddToContainer(room, "top");
|
||||
const cannotPin = !isPinned && !sdkContext.widgetLayoutStore.canAddToContainer(room, "top");
|
||||
|
||||
let pinTitle: string;
|
||||
if (cannotPin) {
|
||||
@@ -78,7 +79,7 @@ const AppRow: React.FC<IAppRowProps> = ({ app, room }) => {
|
||||
pinTitle = isPinned ? _t("action|unpin") : _t("action|pin");
|
||||
}
|
||||
|
||||
const isMaximised = WidgetLayoutStore.instance.isInContainer(room, app, "center");
|
||||
const isMaximised = sdkContext.widgetLayoutStore.isInContainer(room, app, "center");
|
||||
|
||||
let openTitle = "";
|
||||
if (isPinned) {
|
||||
@@ -142,6 +143,7 @@ const AppRow: React.FC<IAppRowProps> = ({ app, room }) => {
|
||||
* @param onClose callback when the card is closed
|
||||
*/
|
||||
const ExtensionsCard: React.FC<Props> = ({ room, onClose }) => {
|
||||
const sdkContext = useContext(SDKContext);
|
||||
const apps = useWidgets(room);
|
||||
// Filter out virtual widgets
|
||||
const realApps = useMemo(() => apps.filter((app) => app.eventId !== undefined), [apps]);
|
||||
@@ -169,9 +171,9 @@ const ExtensionsCard: React.FC<Props> = ({ room, onClose }) => {
|
||||
);
|
||||
} else {
|
||||
let copyLayoutBtn: JSX.Element | null = null;
|
||||
if (WidgetLayoutStore.instance.canCopyLayoutToRoom(room)) {
|
||||
if (sdkContext.widgetLayoutStore.canCopyLayoutToRoom(room)) {
|
||||
copyLayoutBtn = (
|
||||
<Link onClick={() => WidgetLayoutStore.instance.copyLayoutToRoom(room)}>
|
||||
<Link onClick={() => sdkContext.widgetLayoutStore.copyLayoutToRoom(room)}>
|
||||
{_t("widget|set_room_layout")}
|
||||
</Link>
|
||||
);
|
||||
|
||||
@@ -23,10 +23,10 @@ import EncryptionPanel from "./EncryptionPanel";
|
||||
import { useIsEncrypted } from "../../../hooks/useIsEncrypted";
|
||||
import BaseCard from "./BaseCard";
|
||||
import QuestionDialog from "../dialogs/QuestionDialog";
|
||||
import RightPanelStore from "../../../stores/right-panel/RightPanelStore";
|
||||
import PosthogTrackers from "../../../PosthogTrackers";
|
||||
import { UserInfoHeaderView } from "./user_info/UserInfoHeaderView";
|
||||
import { UserInfoBasicView } from "./user_info/UserInfoBasicView";
|
||||
import { SDKContext } from "../../../contexts/SDKContext.ts";
|
||||
|
||||
export interface IDevice extends Device {
|
||||
ambiguous?: boolean;
|
||||
@@ -181,18 +181,18 @@ interface IProps {
|
||||
}
|
||||
|
||||
const UserInfo: React.FC<IProps> = ({ user, room, onClose, phase = RightPanelPhases.MemberInfo, ...props }) => {
|
||||
const cli = useContext(MatrixClientContext);
|
||||
const sdkContext = useContext(SDKContext);
|
||||
|
||||
// fetch latest room member if we have a room, so we don't show historical information, falling back to user
|
||||
const member = useMemo(() => (room ? room.getMember(user.userId) || user : user), [room, user]);
|
||||
|
||||
const isRoomEncrypted = useIsEncrypted(cli, room);
|
||||
const isRoomEncrypted = useIsEncrypted(sdkContext.client!, room);
|
||||
const devices = useDevices(user.userId) ?? [];
|
||||
|
||||
const classes = ["mx_UserInfo"];
|
||||
|
||||
const onEncryptionPanelClose = (): void => {
|
||||
RightPanelStore.instance.popCard();
|
||||
sdkContext.rightPanelStore.popCard();
|
||||
};
|
||||
|
||||
let content: JSX.Element | undefined;
|
||||
@@ -237,7 +237,7 @@ const UserInfo: React.FC<IProps> = ({ user, room, onClose, phase = RightPanelPha
|
||||
onClose={onClose}
|
||||
closeLabel={closeLabel}
|
||||
onBack={(ev: ButtonEvent) => {
|
||||
if (RightPanelStore.instance.previousCard.phase === RightPanelPhases.MemberList) {
|
||||
if (sdkContext.rightPanelStore.previousCard.phase === RightPanelPhases.MemberList) {
|
||||
PosthogTrackers.trackInteraction("WebRightPanelRoomUserInfoBackButton", ev);
|
||||
}
|
||||
}}
|
||||
|
||||
@@ -9,16 +9,14 @@ Please see LICENSE files in the repository root for full details.
|
||||
import React, { type JSX, useContext, useEffect } from "react";
|
||||
import { type Room } from "matrix-js-sdk/src/matrix";
|
||||
|
||||
import MatrixClientContext from "../../../contexts/MatrixClientContext";
|
||||
import BaseCard from "./BaseCard";
|
||||
import WidgetUtils, { useWidgets } from "../../../utils/WidgetUtils";
|
||||
import AppTile from "../elements/AppTile";
|
||||
import { _t } from "../../../languageHandler";
|
||||
import { ContextMenuButton, useContextMenu } from "../../structures/ContextMenu";
|
||||
import { WidgetLayoutStore } from "../../../stores/widgets/WidgetLayoutStore";
|
||||
import RightPanelStore from "../../../stores/right-panel/RightPanelStore";
|
||||
import Heading from "../typography/Heading";
|
||||
import { WidgetContextMenu } from "../../../viewmodels/room/right-panel/WidgetContextMenuViewModel";
|
||||
import { SDKContext } from "../../../contexts/SDKContext.ts";
|
||||
|
||||
interface IProps {
|
||||
room: Room;
|
||||
@@ -27,20 +25,20 @@ interface IProps {
|
||||
}
|
||||
|
||||
const WidgetCard: React.FC<IProps> = ({ room, widgetId, onClose }) => {
|
||||
const cli = useContext(MatrixClientContext);
|
||||
const sdkContext = useContext(SDKContext);
|
||||
|
||||
const apps = useWidgets(room);
|
||||
const app = apps.find((a) => a.id === widgetId);
|
||||
const isRight = app && WidgetLayoutStore.instance.isInContainer(room, app, "right");
|
||||
const isRight = app && sdkContext.widgetLayoutStore.isInContainer(room, app, "right");
|
||||
|
||||
const [menuDisplayed, handle, openMenu, closeMenu] = useContextMenu();
|
||||
|
||||
useEffect(() => {
|
||||
if (!app || !isRight) {
|
||||
// stop showing this card
|
||||
RightPanelStore.instance.popCard();
|
||||
sdkContext.rightPanelStore.popCard();
|
||||
}
|
||||
}, [app, isRight]);
|
||||
}, [app, isRight, sdkContext.rightPanelStore]);
|
||||
|
||||
// Don't render anything as we are about to transition
|
||||
if (!app || !isRight) return null;
|
||||
@@ -78,7 +76,7 @@ const WidgetCard: React.FC<IProps> = ({ room, widgetId, onClose }) => {
|
||||
fullWidth
|
||||
showMenubar={false}
|
||||
room={room}
|
||||
userId={cli.getSafeUserId()}
|
||||
userId={sdkContext.client?.getUserId() ?? undefined}
|
||||
creatorUserId={app.creatorUserId}
|
||||
widgetPageTitle={WidgetUtils.getWidgetDataTitle(app)}
|
||||
waitForIframeLoad={app.waitForIframeLoad}
|
||||
|
||||
@@ -6,7 +6,7 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Com
|
||||
Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
import React, { type AriaRole } from "react";
|
||||
import React, { type AriaRole, useContext } from "react";
|
||||
import classNames from "classnames";
|
||||
import { Resizable, type Size } from "re-resizable";
|
||||
import { type Room } from "matrix-js-sdk/src/matrix";
|
||||
@@ -79,14 +79,14 @@ export default class AppsDrawer extends React.Component<IProps, IState> {
|
||||
this.context.resizeNotifier.on("isResizing", this.onIsResizing);
|
||||
|
||||
ScalarMessaging.startListening();
|
||||
WidgetLayoutStore.instance.on(WidgetLayoutStore.emissionForRoom(this.props.room), this.updateApps);
|
||||
this.context.widgetLayoutStore.on(WidgetLayoutStore.emissionForRoom(this.props.room), this.updateApps);
|
||||
this.dispatcherRef = dis.register(this.onAction);
|
||||
}
|
||||
|
||||
public componentWillUnmount(): void {
|
||||
this.unmounted = true;
|
||||
ScalarMessaging.stopListening();
|
||||
WidgetLayoutStore.instance.off(WidgetLayoutStore.emissionForRoom(this.props.room), this.updateApps);
|
||||
this.context.widgetLayoutStore.off(WidgetLayoutStore.emissionForRoom(this.props.room), this.updateApps);
|
||||
dis.unregister(this.dispatcherRef);
|
||||
if (this.resizeContainer) {
|
||||
this.resizer.detach();
|
||||
@@ -117,7 +117,7 @@ export default class AppsDrawer extends React.Component<IProps, IState> {
|
||||
},
|
||||
onResizeStop: () => {
|
||||
this.resizeContainer?.classList.remove("mx_AppsDrawer--resizing");
|
||||
WidgetLayoutStore.instance.setResizerDistributions(
|
||||
this.context.widgetLayoutStore.setResizerDistributions(
|
||||
this.props.room,
|
||||
"top",
|
||||
this.topApps()
|
||||
@@ -166,7 +166,7 @@ export default class AppsDrawer extends React.Component<IProps, IState> {
|
||||
};
|
||||
|
||||
private loadResizerPreferences = (): void => {
|
||||
const distributions = WidgetLayoutStore.instance.getResizerDistributions(this.props.room, "top");
|
||||
const distributions = this.context.widgetLayoutStore.getResizerDistributions(this.props.room, "top");
|
||||
if (this.state.apps && this.topApps().length - 1 === distributions.length) {
|
||||
distributions.forEach((size, i) => {
|
||||
const distributor = this.resizer.forHandleAt(i);
|
||||
@@ -206,8 +206,8 @@ export default class AppsDrawer extends React.Component<IProps, IState> {
|
||||
};
|
||||
|
||||
private getApps = (): IState["apps"] => ({
|
||||
["top"]: WidgetLayoutStore.instance.getContainerWidgets(this.props.room, "top"),
|
||||
["center"]: WidgetLayoutStore.instance.getContainerWidgets(this.props.room, "center"),
|
||||
["top"]: this.context.widgetLayoutStore.getContainerWidgets(this.props.room, "top"),
|
||||
["center"]: this.context.widgetLayoutStore.getContainerWidgets(this.props.room, "center"),
|
||||
});
|
||||
private topApps = (): IWidget[] => this.state.apps["top"];
|
||||
private centerApps = (): IWidget[] => this.state.apps["center"];
|
||||
@@ -321,7 +321,8 @@ const PersistentVResizer: React.FC<IPersistentResizerProps> = ({
|
||||
resizeNotifier,
|
||||
children,
|
||||
}) => {
|
||||
let defaultHeight = WidgetLayoutStore.instance.getContainerHeight(room, "top");
|
||||
const sdkContext = useContext(SDKContext);
|
||||
let defaultHeight = sdkContext.widgetLayoutStore.getContainerHeight(room, "top");
|
||||
|
||||
// Arbitrary defaults to avoid NaN problems. 100 px or 3/4 of the visible window.
|
||||
if (!minHeight) minHeight = 100;
|
||||
@@ -352,7 +353,7 @@ const PersistentVResizer: React.FC<IPersistentResizerProps> = ({
|
||||
let newHeight = defaultHeight! + d.height;
|
||||
newHeight = percentageOf(newHeight, minHeight, maxHeight) * 100;
|
||||
|
||||
WidgetLayoutStore.instance.setContainerHeight(room, "top", newHeight);
|
||||
sdkContext.widgetLayoutStore.setContainerHeight(room, "top", newHeight);
|
||||
|
||||
resizeNotifier.stopResizing();
|
||||
}}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
import React, { type JSX, useContext, useEffect, useId, useRef, useState } from "react";
|
||||
import React, { type JSX, useCallback, useContext, useEffect, useId, useRef, useState } from "react";
|
||||
import PinIcon from "@vector-im/compound-design-tokens/assets/web/icons/pin-solid";
|
||||
import { Button } from "@vector-im/compound-web";
|
||||
import { type MatrixEvent, type Room } from "matrix-js-sdk/src/matrix";
|
||||
@@ -15,7 +15,6 @@ import { EventPreviewView, useCreateAutoDisposedViewModel } from "@element-hq/we
|
||||
|
||||
import { usePinnedEvents, useSortedFetchedPinnedEvents } from "../../../hooks/usePinnedEvents";
|
||||
import { _t } from "../../../languageHandler";
|
||||
import RightPanelStore from "../../../stores/right-panel/RightPanelStore";
|
||||
import { RightPanelPhases } from "../../../stores/right-panel/RightPanelStorePhases";
|
||||
import { useEventEmitter } from "../../../hooks/useEventEmitter";
|
||||
import { UPDATE_EVENT } from "../../../stores/AsyncStore";
|
||||
@@ -254,11 +253,6 @@ function Indicator({ active, hidden }: IndicatorProps): JSX.Element {
|
||||
);
|
||||
}
|
||||
|
||||
function getRightPanelPhase(roomId: string): RightPanelPhases | null {
|
||||
if (!RightPanelStore.instance.isOpenForRoom(roomId)) return null;
|
||||
return RightPanelStore.instance.currentCard.phase;
|
||||
}
|
||||
|
||||
/**
|
||||
* The props for the {@link BannerButton} component.
|
||||
*/
|
||||
@@ -273,8 +267,18 @@ interface BannerButtonProps {
|
||||
* A button that allows the user to view or close the list of pinned messages.
|
||||
*/
|
||||
function BannerButton({ room }: BannerButtonProps): JSX.Element {
|
||||
const sdkContext = useContext(SDKContext);
|
||||
|
||||
const getRightPanelPhase = useCallback(
|
||||
(roomId: string): RightPanelPhases | null => {
|
||||
if (!sdkContext.rightPanelStore.isOpenForRoom(roomId)) return null;
|
||||
return sdkContext.rightPanelStore.currentCard.phase;
|
||||
},
|
||||
[sdkContext.rightPanelStore],
|
||||
);
|
||||
|
||||
const [currentPhase, setCurrentPhase] = useState<RightPanelPhases | null>(getRightPanelPhase(room.roomId));
|
||||
useEventEmitter(RightPanelStore.instance, UPDATE_EVENT, () => setCurrentPhase(getRightPanelPhase(room.roomId)));
|
||||
useEventEmitter(sdkContext.rightPanelStore, UPDATE_EVENT, () => setCurrentPhase(getRightPanelPhase(room.roomId)));
|
||||
const isPinnedMessagesPhase = currentPhase === RightPanelPhases.PinnedMessages;
|
||||
|
||||
return (
|
||||
@@ -285,7 +289,7 @@ function BannerButton({ room }: BannerButtonProps): JSX.Element {
|
||||
if (isPinnedMessagesPhase) PosthogTrackers.trackInteraction("PinnedMessageBannerCloseListButton");
|
||||
else PosthogTrackers.trackInteraction("PinnedMessageBannerViewAllButton");
|
||||
|
||||
RightPanelStore.instance.showOrHidePhase(RightPanelPhases.PinnedMessages);
|
||||
sdkContext.rightPanelStore.showOrHidePhase(RightPanelPhases.PinnedMessages);
|
||||
}}
|
||||
>
|
||||
{isPinnedMessagesPhase
|
||||
|
||||
@@ -7,7 +7,7 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Com
|
||||
Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
import React, { type JSX, useCallback, useState } from "react";
|
||||
import React, { type JSX, useCallback, useContext, useState } from "react";
|
||||
import { Text, Button, IconButton, Menu, MenuItem, Tooltip } from "@vector-im/compound-web";
|
||||
import VideoCallIcon from "@vector-im/compound-design-tokens/assets/web/icons/video-call-solid";
|
||||
import VoiceCallIcon from "@vector-im/compound-design-tokens/assets/web/icons/voice-call-solid";
|
||||
@@ -26,7 +26,6 @@ import { HistoryIcon, UserProfileSolidIcon } from "@vector-im/compound-design-to
|
||||
|
||||
import { useRoomName } from "../../../../hooks/useRoomName.ts";
|
||||
import { RightPanelPhases } from "../../../../stores/right-panel/RightPanelStorePhases.ts";
|
||||
import { useMatrixClientContext } from "../../../../contexts/MatrixClientContext.tsx";
|
||||
import { useRoomMemberCount, useRoomMembers } from "../../../../hooks/useRoomMembers.ts";
|
||||
import { _t } from "../../../../languageHandler.tsx";
|
||||
import { getPlatformCallTypeProps, useRoomCall } from "../../../../hooks/room/useRoomCall.tsx";
|
||||
@@ -39,7 +38,6 @@ import FacePile from "../../elements/FacePile.tsx";
|
||||
import { useRoomState } from "../../../../hooks/useRoomState.ts";
|
||||
import RoomAvatar from "../../avatars/RoomAvatar.tsx";
|
||||
import { formatCount } from "../../../../utils/FormattingUtils.ts";
|
||||
import RightPanelStore from "../../../../stores/right-panel/RightPanelStore.ts";
|
||||
import PosthogTrackers from "../../../../PosthogTrackers.ts";
|
||||
import { VideoRoomChatButton } from "./VideoRoomChatButton.tsx";
|
||||
import { RoomKnocksBar } from "../RoomKnocksBar.tsx";
|
||||
@@ -58,6 +56,7 @@ import { CurrentRightPanelPhaseContextProvider } from "../../../../contexts/Curr
|
||||
import { LocalRoom } from "../../../../models/LocalRoom.ts";
|
||||
import { useIsEncrypted } from "../../../../hooks/useIsEncrypted.ts";
|
||||
import { useUserStatus } from "../../../../hooks/useUserStatus.ts";
|
||||
import { SDKContext } from "../../../../contexts/SDKContext.ts";
|
||||
|
||||
function RoomHeaderButtons({
|
||||
room,
|
||||
@@ -68,6 +67,7 @@ function RoomHeaderButtons({
|
||||
legacyAdditionalButtons?: ViewRoomOpts["buttons"];
|
||||
extraButtons?: JSX.Element;
|
||||
}): JSX.Element {
|
||||
const sdkContext = useContext(SDKContext);
|
||||
const members = useRoomMembers(room, 2500);
|
||||
const memberCount = useRoomMemberCount(room, { throttleWait: 2500, includeInvited: true });
|
||||
|
||||
@@ -338,7 +338,7 @@ function RoomHeaderButtons({
|
||||
indicator={notificationLevelToIndicator(threadNotifications)}
|
||||
onClick={(evt) => {
|
||||
evt.stopPropagation();
|
||||
RightPanelStore.instance.showOrHidePhase(RightPanelPhases.ThreadPanel);
|
||||
sdkContext.rightPanelStore.showOrHidePhase(RightPanelPhases.ThreadPanel);
|
||||
PosthogTrackers.trackInteraction("WebRoomHeaderButtonsThreadsButton", evt);
|
||||
}}
|
||||
aria-label={_t("common|threads")}
|
||||
@@ -352,7 +352,7 @@ function RoomHeaderButtons({
|
||||
indicator={notificationLevelToIndicator(globalNotificationState.level)}
|
||||
onClick={(evt) => {
|
||||
evt.stopPropagation();
|
||||
RightPanelStore.instance.showOrHidePhase(RightPanelPhases.NotificationPanel);
|
||||
sdkContext.rightPanelStore.showOrHidePhase(RightPanelPhases.NotificationPanel);
|
||||
}}
|
||||
aria-label={_t("notifications|enable_prompt_toast_title")}
|
||||
>
|
||||
@@ -365,7 +365,7 @@ function RoomHeaderButtons({
|
||||
<IconButton
|
||||
onClick={(evt) => {
|
||||
evt.stopPropagation();
|
||||
RightPanelStore.instance.showOrHidePhase(RightPanelPhases.RoomSummary);
|
||||
sdkContext.rightPanelStore.showOrHidePhase(RightPanelPhases.RoomSummary);
|
||||
}}
|
||||
aria-label={_t("right_panel|room_summary_card|title")}
|
||||
>
|
||||
@@ -383,7 +383,7 @@ function RoomHeaderButtons({
|
||||
viewUserOnClick={false}
|
||||
tooltipLabel={_t("room|header_face_pile_tooltip")}
|
||||
onClick={(e: ButtonEvent) => {
|
||||
RightPanelStore.instance.showOrHidePhase(RightPanelPhases.MemberList);
|
||||
sdkContext.rightPanelStore.showOrHidePhase(RightPanelPhases.MemberList);
|
||||
e.stopPropagation();
|
||||
}}
|
||||
aria-label={_t("common|n_members", { count: memberCount })}
|
||||
@@ -443,15 +443,15 @@ export default function RoomHeader({
|
||||
legacyAdditionalButtons?: ViewRoomOpts["buttons"];
|
||||
oobData?: IOOBData;
|
||||
}): JSX.Element {
|
||||
const client = useMatrixClientContext();
|
||||
const sdkContext = useContext(SDKContext);
|
||||
const roomName = useRoomName(room);
|
||||
const joinRule = useRoomState(room, (state) => state.getJoinRule());
|
||||
const historyVisibility = useRoomState(room, (state) => state.getHistoryVisibility());
|
||||
const dmMember = useDmMember(room);
|
||||
const isDirectMessage = !!dmMember;
|
||||
const dmUserStatus = useUserStatus(dmMember?.userId);
|
||||
const isRoomEncrypted = useIsEncrypted(client, room);
|
||||
const e2eStatus = useEncryptionStatus(client, room);
|
||||
const isRoomEncrypted = useIsEncrypted(sdkContext.client!, room);
|
||||
const e2eStatus = useEncryptionStatus(sdkContext.client!, room);
|
||||
const askToJoinEnabled = useFeatureEnabled("feature_ask_to_join");
|
||||
const onAvatarClick = (): void => {
|
||||
defaultDispatcher.dispatch({
|
||||
@@ -482,7 +482,7 @@ export default function RoomHeader({
|
||||
onClick={
|
||||
room instanceof LocalRoom
|
||||
? undefined
|
||||
: () => RightPanelStore.instance.showOrHidePhase(RightPanelPhases.RoomSummary)
|
||||
: () => sdkContext.rightPanelStore.showOrHidePhase(RightPanelPhases.RoomSummary)
|
||||
}
|
||||
className="mx_RoomHeader_infoWrapper"
|
||||
>
|
||||
|
||||
@@ -6,25 +6,27 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Com
|
||||
Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
import React, { type JSX, type FC } from "react";
|
||||
import React, { type JSX, type FC, useContext } from "react";
|
||||
import { type Room, JoinRule, type MatrixClient } from "matrix-js-sdk/src/matrix";
|
||||
import { KnownMembership } from "matrix-js-sdk/src/types";
|
||||
import { LockSolidIcon, VideoCallSolidIcon, PublicIcon } from "@vector-im/compound-design-tokens/assets/web/icons";
|
||||
|
||||
import { _t } from "../../../languageHandler";
|
||||
import RightPanelStore from "../../../stores/right-panel/RightPanelStore";
|
||||
import { RightPanelPhases } from "../../../stores/right-panel/RightPanelStorePhases";
|
||||
import { useAsyncMemo } from "../../../hooks/useAsyncMemo";
|
||||
import { useRoomState } from "../../../hooks/useRoomState";
|
||||
import { useRoomMemberCount, useMyRoomMembership } from "../../../hooks/useRoomMembers";
|
||||
import AccessibleButton from "../elements/AccessibleButton";
|
||||
import { isVideoRoom as calcIsVideoRoom } from "../../../utils/video-rooms";
|
||||
import { SDKContext } from "../../../contexts/SDKContext.ts";
|
||||
|
||||
interface IProps {
|
||||
room: Room;
|
||||
}
|
||||
|
||||
const RoomInfoLine: FC<IProps> = ({ room }) => {
|
||||
const sdkContext = useContext(SDKContext);
|
||||
|
||||
// summary will begin as undefined whilst loading and go null if it fails to load or we are not invited.
|
||||
const summary = useAsyncMemo(async (): Promise<Awaited<ReturnType<MatrixClient["getRoomSummary"]>> | null> => {
|
||||
if (room.getMyMembership() !== KnownMembership.Invite) return null;
|
||||
@@ -64,7 +66,7 @@ const RoomInfoLine: FC<IProps> = ({ room }) => {
|
||||
} else if (memberCount && summary !== undefined) {
|
||||
// summary is not still loading
|
||||
const viewMembers = (): void =>
|
||||
RightPanelStore.instance.setCard({
|
||||
sdkContext.rightPanelStore.setCard({
|
||||
phase: RightPanelPhases.MemberList,
|
||||
});
|
||||
|
||||
|
||||
@@ -12,7 +12,6 @@ import { type IWidget } from "matrix-widget-api";
|
||||
|
||||
import { _t, _td } from "../../../languageHandler";
|
||||
import AppTile from "../elements/AppTile";
|
||||
import { MatrixClientPeg } from "../../../MatrixClientPeg";
|
||||
import dis from "../../../dispatcher/dispatcher";
|
||||
import AccessibleButton from "../elements/AccessibleButton";
|
||||
import WidgetUtils, { type UserWidget } from "../../../utils/WidgetUtils";
|
||||
@@ -24,9 +23,9 @@ import { WidgetMessagingStore } from "../../../stores/widgets/WidgetMessagingSto
|
||||
import { type ActionPayload } from "../../../dispatcher/payloads";
|
||||
import type ScalarAuthClient from "../../../ScalarAuthClient";
|
||||
import GenericElementContextMenu from "../context_menus/GenericElementContextMenu";
|
||||
import RightPanelStore from "../../../stores/right-panel/RightPanelStore";
|
||||
import { UPDATE_EVENT } from "../../../stores/AsyncStore";
|
||||
import StickerpackPlaceholder from "../../../../res/img/stickerpack-placeholder.png";
|
||||
import { SDKContext } from "../../../contexts/SDKContext.ts";
|
||||
|
||||
// This should be below the dialog level (4000), but above the rest of the UI (1000-2000).
|
||||
// We sit in a context menu, so this should be given to the context menu.
|
||||
@@ -50,6 +49,9 @@ interface IState {
|
||||
}
|
||||
|
||||
export default class Stickerpicker extends React.PureComponent<IProps, IState> {
|
||||
public static contextType = SDKContext;
|
||||
declare public context: React.ContextType<typeof SDKContext>;
|
||||
|
||||
public static defaultProps: Partial<IProps> = {
|
||||
threadId: null,
|
||||
};
|
||||
@@ -130,17 +132,16 @@ export default class Stickerpicker extends React.PureComponent<IProps, IState> {
|
||||
this.dispatcherRef = dis.register(this.onAction);
|
||||
|
||||
// Track updates to widget state in account data
|
||||
MatrixClientPeg.safeGet().on(ClientEvent.AccountData, this.updateWidget);
|
||||
this.context.client?.on(ClientEvent.AccountData, this.updateWidget);
|
||||
|
||||
RightPanelStore.instance.on(UPDATE_EVENT, this.onRightPanelStoreUpdate);
|
||||
this.context.rightPanelStore.on(UPDATE_EVENT, this.onRightPanelStoreUpdate);
|
||||
// Initialise widget state from current account data
|
||||
this.updateWidget();
|
||||
}
|
||||
|
||||
public componentWillUnmount(): void {
|
||||
const client = MatrixClientPeg.get();
|
||||
if (client) client.removeListener(ClientEvent.AccountData, this.updateWidget);
|
||||
RightPanelStore.instance.off(UPDATE_EVENT, this.onRightPanelStoreUpdate);
|
||||
this.context.client?.removeListener(ClientEvent.AccountData, this.updateWidget);
|
||||
this.context.rightPanelStore.off(UPDATE_EVENT, this.onRightPanelStoreUpdate);
|
||||
window.removeEventListener("resize", this.onResize);
|
||||
dis.unregister(this.dispatcherRef);
|
||||
}
|
||||
@@ -280,9 +281,9 @@ export default class Stickerpicker extends React.PureComponent<IProps, IState> {
|
||||
room={this.props.room}
|
||||
threadId={this.props.threadId}
|
||||
fullWidth={true}
|
||||
userId={MatrixClientPeg.safeGet().credentials.userId!}
|
||||
userId={this.context.client?.credentials.userId ?? undefined}
|
||||
creatorUserId={
|
||||
stickerpickerWidget.sender || MatrixClientPeg.safeGet().credentials.userId!
|
||||
stickerpickerWidget.sender || this.context.client?.credentials.userId || undefined
|
||||
}
|
||||
waitForIframeLoad={true}
|
||||
showMenubar={true}
|
||||
|
||||
+5
-3
@@ -6,7 +6,7 @@
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
import React, { type JSX, useState } from "react";
|
||||
import React, { type JSX, useContext, useState } from "react";
|
||||
import { Menu, MenuItem } from "@vector-im/compound-web";
|
||||
import { type Room } from "matrix-js-sdk/src/matrix";
|
||||
|
||||
@@ -16,7 +16,6 @@ import DecoratedRoomAvatar from "../../avatars/DecoratedRoomAvatar";
|
||||
import { Action } from "../../../../dispatcher/actions";
|
||||
import defaultDispatcher from "../../../../dispatcher/dispatcher";
|
||||
import { type ViewRoomPayload } from "../../../../dispatcher/payloads/ViewRoomPayload";
|
||||
import RightPanelStore from "../../../../stores/right-panel/RightPanelStore";
|
||||
import { RightPanelPhases } from "../../../../stores/right-panel/RightPanelStorePhases";
|
||||
import { useUnreadThreadRooms } from "./useUnreadThreadRooms";
|
||||
import { StatelessNotificationBadge } from "../../rooms/NotificationBadge/StatelessNotificationBadge";
|
||||
@@ -25,6 +24,7 @@ import PosthogTrackers from "../../../../PosthogTrackers";
|
||||
import { getKeyBindingsManager } from "../../../../KeyBindingsManager";
|
||||
import { KeyBindingAction } from "../../../../accessibility/KeyboardShortcuts";
|
||||
import { useSettingValue } from "../../../../hooks/useSettings";
|
||||
import { SDKContext } from "../../../../contexts/SDKContext.ts";
|
||||
|
||||
interface ThreadsActivityCentreProps {
|
||||
/**
|
||||
@@ -117,6 +117,8 @@ interface ThreadsActivityRow {
|
||||
* Display a room with unread threads.
|
||||
*/
|
||||
function ThreadsActivityCentreRow({ room, onClick, notificationLevel }: ThreadsActivityRow): JSX.Element {
|
||||
const sdkContext = useContext(SDKContext);
|
||||
|
||||
return (
|
||||
<MenuItem
|
||||
className="mx_ThreadsActivityCentreRow"
|
||||
@@ -125,7 +127,7 @@ function ThreadsActivityCentreRow({ room, onClick, notificationLevel }: ThreadsA
|
||||
|
||||
// Set the right panel card for that room so the threads panel is open before we dispatch,
|
||||
// so it will open once the room appears.
|
||||
RightPanelStore.instance.setCard({ phase: RightPanelPhases.ThreadPanel }, true, room.roomId);
|
||||
sdkContext.rightPanelStore.setCard({ phase: RightPanelPhases.ThreadPanel }, true, room.roomId);
|
||||
|
||||
// Track the click on the room
|
||||
PosthogTrackers.trackInteraction("WebThreadsActivityCentreRoomItem", event);
|
||||
|
||||
@@ -16,7 +16,6 @@ import { logger } from "matrix-js-sdk/src/logger";
|
||||
import { type Device } from "matrix-js-sdk/src/matrix";
|
||||
|
||||
import { _t } from "../../../languageHandler";
|
||||
import { MatrixClientPeg } from "../../../MatrixClientPeg";
|
||||
import { RightPanelPhases } from "../../../stores/right-panel/RightPanelStorePhases";
|
||||
import { userLabelForEventRoom } from "../../../utils/KeyVerificationStateObserver";
|
||||
import dis from "../../../dispatcher/dispatcher";
|
||||
@@ -25,9 +24,9 @@ import Modal from "../../../Modal";
|
||||
import GenericToast from "./GenericToast";
|
||||
import { Action } from "../../../dispatcher/actions";
|
||||
import VerificationRequestDialog from "../dialogs/VerificationRequestDialog";
|
||||
import RightPanelStore from "../../../stores/right-panel/RightPanelStore";
|
||||
import { type ViewRoomPayload } from "../../../dispatcher/payloads/ViewRoomPayload";
|
||||
import { getDeviceCryptoInfo } from "../../../utils/crypto/deviceInfo";
|
||||
import { SDKContext } from "../../../contexts/SDKContext.ts";
|
||||
|
||||
interface IProps {
|
||||
toastKey: string;
|
||||
@@ -42,6 +41,9 @@ interface IState {
|
||||
}
|
||||
|
||||
export default class VerificationRequestToast extends React.PureComponent<IProps, IState> {
|
||||
public static contextType = SDKContext;
|
||||
declare public context: React.ContextType<typeof SDKContext>;
|
||||
|
||||
private intervalHandle?: number;
|
||||
|
||||
public constructor(props: IProps) {
|
||||
@@ -69,7 +71,7 @@ export default class VerificationRequestToast extends React.PureComponent<IProps
|
||||
|
||||
const otherDeviceId = request.otherDeviceId;
|
||||
if (request.isSelfVerification && !!otherDeviceId) {
|
||||
const cli = MatrixClientPeg.safeGet();
|
||||
const cli = this.context.client!;
|
||||
const device = await cli.getDevice(otherDeviceId);
|
||||
this.setState({
|
||||
ip: device.last_seen_ip,
|
||||
@@ -104,7 +106,6 @@ export default class VerificationRequestToast extends React.PureComponent<IProps
|
||||
ToastStore.sharedInstance().dismissToast(this.props.toastKey);
|
||||
const { request } = this.props;
|
||||
// no room id for to_device requests
|
||||
const cli = MatrixClientPeg.safeGet();
|
||||
try {
|
||||
if (request.roomId) {
|
||||
dis.dispatch<ViewRoomPayload>({
|
||||
@@ -113,8 +114,8 @@ export default class VerificationRequestToast extends React.PureComponent<IProps
|
||||
should_peek: false,
|
||||
metricsTrigger: "VerificationRequest",
|
||||
});
|
||||
const member = cli.getUser(request.otherUserId) ?? undefined;
|
||||
RightPanelStore.instance.setCards(
|
||||
const member = this.context.client?.getUser(request.otherUserId) ?? undefined;
|
||||
this.context.rightPanelStore.setCards(
|
||||
[
|
||||
{ phase: RightPanelPhases.RoomSummary },
|
||||
{ phase: RightPanelPhases.MemberInfo, state: { member } },
|
||||
@@ -154,7 +155,7 @@ export default class VerificationRequestToast extends React.PureComponent<IProps
|
||||
});
|
||||
}
|
||||
} else {
|
||||
const client = MatrixClientPeg.safeGet();
|
||||
const client = this.context.client!;
|
||||
const userId = request.otherUserId;
|
||||
const roomId = request.roomId;
|
||||
description = roomId ? userLabelForEventRoom(client, userId, roomId) : userId;
|
||||
|
||||
@@ -199,6 +199,7 @@ function CallStartedTileViewWrapped({ mxEvent, getRelationsForEvent }: IBodyProp
|
||||
cli,
|
||||
callStore: sdkContext.callStore,
|
||||
latestRtcNotificationEventStore: sdkContext.latestRtcNotificationEventStore,
|
||||
legacyCallHandler: sdkContext.legacyCallHandler,
|
||||
}),
|
||||
);
|
||||
return <RootCallTileView vm={vm} />;
|
||||
|
||||
@@ -215,10 +215,10 @@ export const useRoomCall = (
|
||||
widget = groupCall?.widget ?? jitsiWidget;
|
||||
}
|
||||
const updateWidgetState = useCallback((): void => {
|
||||
setCanPinWidget(WidgetLayoutStore.instance.canAddToContainer(room, "top"));
|
||||
setWidgetPinned(!!widget && WidgetLayoutStore.instance.isInContainer(room, widget, "top"));
|
||||
}, [room, widget]);
|
||||
useEventEmitter(WidgetLayoutStore.instance, WidgetLayoutStore.emissionForRoom(room), updateWidgetState);
|
||||
setCanPinWidget(sdkContext.widgetLayoutStore.canAddToContainer(room, "top"));
|
||||
setWidgetPinned(!!widget && sdkContext.widgetLayoutStore.isInContainer(room, widget, "top"));
|
||||
}, [room, widget, sdkContext.widgetLayoutStore]);
|
||||
useEventEmitter(sdkContext.widgetLayoutStore, WidgetLayoutStore.emissionForRoom(room), updateWidgetState);
|
||||
useEffect(() => {
|
||||
updateWidgetState();
|
||||
}, [room, jitsiWidget, groupCall, updateWidgetState]);
|
||||
@@ -267,25 +267,39 @@ export const useRoomCall = (
|
||||
(evt: React.MouseEvent | undefined, callPlatformType: PlatformCallType): void => {
|
||||
evt?.stopPropagation();
|
||||
if (widget && promptPinWidget) {
|
||||
WidgetLayoutStore.instance.moveToContainer(room, widget, "top");
|
||||
sdkContext.widgetLayoutStore.moveToContainer(room, widget, "top");
|
||||
} else {
|
||||
placeCall(room, CallType.Voice, callPlatformType, evt?.shiftKey || undefined, true);
|
||||
placeCall(
|
||||
sdkContext.legacyCallHandler,
|
||||
room,
|
||||
CallType.Voice,
|
||||
callPlatformType,
|
||||
evt?.shiftKey || undefined,
|
||||
true,
|
||||
);
|
||||
}
|
||||
},
|
||||
[promptPinWidget, room, widget],
|
||||
[promptPinWidget, room, widget, sdkContext.widgetLayoutStore, sdkContext.legacyCallHandler],
|
||||
);
|
||||
const videoCallClick = useCallback(
|
||||
(evt: React.MouseEvent | undefined, callPlatformType: PlatformCallType): void => {
|
||||
evt?.stopPropagation();
|
||||
if (widget && promptPinWidget) {
|
||||
WidgetLayoutStore.instance.moveToContainer(room, widget, "top");
|
||||
sdkContext.widgetLayoutStore.moveToContainer(room, widget, "top");
|
||||
} else {
|
||||
// If we have pressed shift then always skip the lobby, otherwise `undefined` will defer
|
||||
// to the defaults of the call implementation.
|
||||
placeCall(room, CallType.Video, callPlatformType, evt?.shiftKey || undefined, false);
|
||||
placeCall(
|
||||
sdkContext.legacyCallHandler,
|
||||
room,
|
||||
CallType.Video,
|
||||
callPlatformType,
|
||||
evt?.shiftKey || undefined,
|
||||
false,
|
||||
);
|
||||
}
|
||||
},
|
||||
[widget, promptPinWidget, room],
|
||||
[widget, promptPinWidget, room, sdkContext.widgetLayoutStore, sdkContext.legacyCallHandler],
|
||||
);
|
||||
|
||||
let voiceCallDisabledReason: string | null;
|
||||
|
||||
@@ -20,7 +20,6 @@ import {
|
||||
} from "@vector-im/compound-design-tokens/assets/web/icons";
|
||||
|
||||
import { LegacyCallHandlerEvent } from "../LegacyCallHandler";
|
||||
import { MatrixClientPeg } from "../MatrixClientPeg";
|
||||
import { _t } from "../languageHandler";
|
||||
import RoomAvatar from "../components/views/avatars/RoomAvatar";
|
||||
import AccessibleButton, { type ButtonEvent } from "../components/views/elements/AccessibleButton";
|
||||
@@ -96,7 +95,7 @@ export default class IncomingLegacyCallToast extends React.Component<IProps, ISt
|
||||
};
|
||||
|
||||
public render(): React.ReactNode {
|
||||
const room = MatrixClientPeg.safeGet().getRoom(this.roomId);
|
||||
const room = this.context.client?.getRoom(this.roomId);
|
||||
const isVoice = this.props.call.type === CallType.Voice;
|
||||
const callForcedSilent = this.context.legacyCallHandler.isForcedSilent();
|
||||
|
||||
@@ -109,7 +108,7 @@ export default class IncomingLegacyCallToast extends React.Component<IProps, ISt
|
||||
<React.Fragment>
|
||||
<RoomAvatar room={room ?? undefined} size="32px" />
|
||||
<div className="mx_IncomingLegacyCallToast_content">
|
||||
<span className="mx_LegacyCallEvent_caller">{room ? room.name : _t("voip|unknown_caller")}</span>
|
||||
<span className="mx_LegacyCallEvent_caller">{room?.name ?? _t("voip|unknown_caller")}</span>
|
||||
<div className="mx_LegacyCallEvent_type">
|
||||
{getCallStateIcon(isVoice, undefined)}
|
||||
{isVoice ? _t("voip|voice_call") : _t("voip|video_call")}
|
||||
|
||||
@@ -9,12 +9,12 @@ Please see LICENSE files in the repository root for full details.
|
||||
import { type CallType } from "matrix-js-sdk/src/webrtc/call";
|
||||
import { type Room } from "matrix-js-sdk/src/matrix";
|
||||
|
||||
import type LegacyCallHandler from "../../LegacyCallHandler";
|
||||
import { getPlatformCallTypeProps, PlatformCallType } from "../../hooks/room/useRoomCall";
|
||||
import defaultDispatcher from "../../dispatcher/dispatcher";
|
||||
import { type ViewRoomPayload } from "../../dispatcher/payloads/ViewRoomPayload";
|
||||
import { Action } from "../../dispatcher/actions";
|
||||
import PosthogTrackers from "../../PosthogTrackers";
|
||||
import { SDKContextClass } from "../../contexts/SDKContextClass.ts";
|
||||
|
||||
/**
|
||||
* Helper to place a call in a room that works with all the legacy modes
|
||||
@@ -24,6 +24,7 @@ import { SDKContextClass } from "../../contexts/SDKContextClass.ts";
|
||||
* @param skipLobby Has the user indicated they would like to skip the lobby. Otherwise, defer to platform defaults.
|
||||
*/
|
||||
export const placeCall = async (
|
||||
legacyCallHandler: LegacyCallHandler,
|
||||
room: Room,
|
||||
callType: CallType,
|
||||
platformCallType: PlatformCallType,
|
||||
@@ -34,7 +35,7 @@ export const placeCall = async (
|
||||
PosthogTrackers.trackInteraction(analyticsName);
|
||||
|
||||
if (platformCallType == PlatformCallType.LegacyCall || platformCallType == PlatformCallType.JitsiCall) {
|
||||
await SDKContextClass.instance.legacyCallHandler.placeCall(room.roomId, callType);
|
||||
await legacyCallHandler.placeCall(room.roomId, callType);
|
||||
} else if (platformCallType == PlatformCallType.ElementCall) {
|
||||
defaultDispatcher.dispatch<ViewRoomPayload>({
|
||||
action: Action.ViewRoom,
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
Copyright 2026 Element Creations Ltd.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
// @vitest-environment happy-dom
|
||||
|
||||
import { vi, describe, it, expect } from "vitest";
|
||||
import { createTestClient, TestSDKContext } from "test-utils";
|
||||
|
||||
import { verifyUser } from "./verification";
|
||||
import defaultDispatcher from "./dispatcher/dispatcher";
|
||||
import DMRoomMap from "./utils/DMRoomMap.ts";
|
||||
import { RightPanelPhases } from "./stores/right-panel/RightPanelStorePhases.ts";
|
||||
|
||||
describe("verifyUser", () => {
|
||||
const sdkContext = new TestSDKContext();
|
||||
sdkContext._client = createTestClient();
|
||||
DMRoomMap.makeShared(sdkContext._client);
|
||||
|
||||
it("should require registration if user is a guest", () => {
|
||||
vi.spyOn(defaultDispatcher, "dispatch");
|
||||
vi.spyOn(sdkContext._client!, "isGuest").mockReturnValue(true);
|
||||
verifyUser(
|
||||
sdkContext.rightPanelStore,
|
||||
sdkContext.client!,
|
||||
sdkContext.client!.getUser(sdkContext.client!.getUserId()!)!,
|
||||
);
|
||||
expect(defaultDispatcher.dispatch).toHaveBeenCalledWith({ action: "require_registration" });
|
||||
});
|
||||
|
||||
it("should open verification in right panel", () => {
|
||||
vi.spyOn(sdkContext.rightPanelStore, "setCards");
|
||||
vi.spyOn(sdkContext._client!, "isGuest").mockReturnValue(false);
|
||||
verifyUser(
|
||||
sdkContext.rightPanelStore,
|
||||
sdkContext.client!,
|
||||
sdkContext.client!.getUser(sdkContext.client!.getUserId()!)!,
|
||||
);
|
||||
expect(sdkContext.rightPanelStore.setCards).toHaveBeenCalledWith([
|
||||
{ phase: RightPanelPhases.RoomSummary },
|
||||
expect.objectContaining({ phase: RightPanelPhases.MemberInfo }),
|
||||
expect.objectContaining({ phase: RightPanelPhases.EncryptionPanel }),
|
||||
]);
|
||||
});
|
||||
});
|
||||
@@ -11,7 +11,7 @@ import { type VerificationRequest } from "matrix-js-sdk/src/crypto-api";
|
||||
|
||||
import dis from "./dispatcher/dispatcher";
|
||||
import { RightPanelPhases } from "./stores/right-panel/RightPanelStorePhases";
|
||||
import RightPanelStore from "./stores/right-panel/RightPanelStore";
|
||||
import type RightPanelStore from "./stores/right-panel/RightPanelStore";
|
||||
import { type IRightPanelCardState } from "./stores/right-panel/RightPanelStoreIPanelState";
|
||||
import { findDMForUser } from "./utils/dm/findDMForUser";
|
||||
|
||||
@@ -20,20 +20,20 @@ import { findDMForUser } from "./utils/dm/findDMForUser";
|
||||
*
|
||||
* Note: cross-signing must be set up before calling this function.
|
||||
*/
|
||||
export async function verifyUser(matrixClient: MatrixClient, user: User): Promise<void> {
|
||||
export function verifyUser(rightPanelStore: RightPanelStore, matrixClient: MatrixClient, user: User): void {
|
||||
if (matrixClient.isGuest()) {
|
||||
dis.dispatch({ action: "require_registration" });
|
||||
return;
|
||||
}
|
||||
const existingRequest = pendingVerificationRequestForUser(matrixClient, user);
|
||||
setRightPanel({ member: user, verificationRequest: existingRequest });
|
||||
setRightPanel(rightPanelStore, { member: user, verificationRequest: existingRequest });
|
||||
}
|
||||
|
||||
function setRightPanel(state: IRightPanelCardState): void {
|
||||
if (RightPanelStore.instance.roomPhaseHistory.some((card) => card.phase == RightPanelPhases.RoomSummary)) {
|
||||
RightPanelStore.instance.pushCard({ phase: RightPanelPhases.EncryptionPanel, state });
|
||||
function setRightPanel(rightPanelStore: RightPanelStore, state: IRightPanelCardState): void {
|
||||
if (rightPanelStore.roomPhaseHistory.some((card) => card.phase == RightPanelPhases.RoomSummary)) {
|
||||
rightPanelStore.pushCard({ phase: RightPanelPhases.EncryptionPanel, state });
|
||||
} else {
|
||||
RightPanelStore.instance.setCards([
|
||||
rightPanelStore.setCards([
|
||||
{ phase: RightPanelPhases.RoomSummary },
|
||||
{ phase: RightPanelPhases.MemberInfo, state: { member: state.member } },
|
||||
{ phase: RightPanelPhases.EncryptionPanel, state },
|
||||
|
||||
+46
-6
@@ -11,7 +11,14 @@ import { it, describe, expect, vi } from "vitest";
|
||||
import { type EventTimeline, EventType, type MatrixEvent, type RoomState } from "matrix-js-sdk/src/matrix";
|
||||
import { EventEmitter } from "node:stream";
|
||||
|
||||
import { mkEvent, mkMessage, mkRoomMember, mkStubRoom, stubClient } from "../../../../../../test/test-utils";
|
||||
import {
|
||||
mkEvent,
|
||||
mkMessage,
|
||||
mkRoomMember,
|
||||
mkStubRoom,
|
||||
stubClient,
|
||||
TestSDKContext,
|
||||
} from "../../../../../../test/test-utils";
|
||||
import { getMockedRtcNotificationEvent, MockedCall, MockedCallStore } from "./call-mocks";
|
||||
import { RootCallTileViewModel } from "./RootCallTileViewModel";
|
||||
import {
|
||||
@@ -92,10 +99,19 @@ function getMocked(userIds: string[]) {
|
||||
}
|
||||
|
||||
describe("RootCallTileViewModel", () => {
|
||||
const sdkContext = new TestSDKContext();
|
||||
const legacyCallHandler = sdkContext.legacyCallHandler;
|
||||
|
||||
it("computes correct tileType for ongoing call in DM", () => {
|
||||
const { callStore, cli, mxEvent, latestRtcNotificationEventStore } = getMocked(["@alice:m.org", "@bob:m.org"]);
|
||||
latestRtcNotificationEventStore.getLatestEventId = () => "new-event";
|
||||
const vm = new RootCallTileViewModel({ latestRtcNotificationEventStore, callStore, cli, mxEvent });
|
||||
const vm = new RootCallTileViewModel({
|
||||
latestRtcNotificationEventStore,
|
||||
callStore,
|
||||
cli,
|
||||
mxEvent,
|
||||
legacyCallHandler,
|
||||
});
|
||||
|
||||
expect(vm.getSnapshot().tileType).toStrictEqual("ongoing-call-dm");
|
||||
});
|
||||
@@ -107,7 +123,13 @@ describe("RootCallTileViewModel", () => {
|
||||
"@jack:m.org",
|
||||
]);
|
||||
latestRtcNotificationEventStore.getLatestEventId = () => "new-event";
|
||||
const vm = new RootCallTileViewModel({ latestRtcNotificationEventStore, callStore, cli, mxEvent });
|
||||
const vm = new RootCallTileViewModel({
|
||||
latestRtcNotificationEventStore,
|
||||
callStore,
|
||||
cli,
|
||||
mxEvent,
|
||||
legacyCallHandler,
|
||||
});
|
||||
|
||||
expect(vm.getSnapshot().tileType).toStrictEqual("ongoing-call-room");
|
||||
});
|
||||
@@ -115,7 +137,13 @@ describe("RootCallTileViewModel", () => {
|
||||
it("computes correct tileType for tombstone call in DM", () => {
|
||||
// When there's an ongoing call
|
||||
const { callStore, cli, mxEvent, latestRtcNotificationEventStore } = getMocked(["@alice:m.org", "@bob:m.org"]);
|
||||
const vm = new RootCallTileViewModel({ latestRtcNotificationEventStore, callStore, cli, mxEvent });
|
||||
const vm = new RootCallTileViewModel({
|
||||
latestRtcNotificationEventStore,
|
||||
callStore,
|
||||
cli,
|
||||
mxEvent,
|
||||
legacyCallHandler,
|
||||
});
|
||||
|
||||
expect(vm.getSnapshot().tileType).toStrictEqual("tombstone-call-dm");
|
||||
});
|
||||
@@ -127,7 +155,13 @@ describe("RootCallTileViewModel", () => {
|
||||
"@bob:m.org",
|
||||
"@jack:m.org",
|
||||
]);
|
||||
const vm = new RootCallTileViewModel({ latestRtcNotificationEventStore, callStore, cli, mxEvent });
|
||||
const vm = new RootCallTileViewModel({
|
||||
latestRtcNotificationEventStore,
|
||||
callStore,
|
||||
cli,
|
||||
mxEvent,
|
||||
legacyCallHandler,
|
||||
});
|
||||
expect(vm.getSnapshot().tileType).toStrictEqual("tombstone-call-room");
|
||||
});
|
||||
|
||||
@@ -138,7 +172,13 @@ describe("RootCallTileViewModel", () => {
|
||||
"@bob:m.org",
|
||||
"@jack:m.org",
|
||||
]);
|
||||
const vm = new RootCallTileViewModel({ latestRtcNotificationEventStore, callStore, cli, mxEvent });
|
||||
const vm = new RootCallTileViewModel({
|
||||
latestRtcNotificationEventStore,
|
||||
callStore,
|
||||
cli,
|
||||
mxEvent,
|
||||
legacyCallHandler,
|
||||
});
|
||||
expect(vm.getSnapshot().tileType).toStrictEqual("tombstone-call-room");
|
||||
|
||||
// Tile type should update on event
|
||||
|
||||
@@ -19,6 +19,7 @@ import {
|
||||
type LatestRtcNotificationEventStore,
|
||||
} from "../../../../../stores/LatestRtcNotificationEventStore";
|
||||
import { JitsiCall } from "../../../../../models/Call";
|
||||
import type LegacyCallHandler from "../../../../../LegacyCallHandler.tsx";
|
||||
|
||||
interface Props {
|
||||
/**
|
||||
@@ -41,6 +42,11 @@ interface Props {
|
||||
*/
|
||||
callStore: CallStore;
|
||||
|
||||
/**
|
||||
* {@link LegacyCallHandler} to handle calls in a room.
|
||||
*/
|
||||
legacyCallHandler: LegacyCallHandler;
|
||||
|
||||
/**
|
||||
* {@link LatestRtcNotificationEventStore} to track the latest notification event id.
|
||||
*/
|
||||
@@ -83,6 +89,7 @@ function computeSnapshot(props: Props): RootCallTileViewSnapshot {
|
||||
getRelationsForEvent: props.getRelationsForEvent,
|
||||
callStore: props.callStore,
|
||||
cli: props.cli,
|
||||
legacyCallHandler: props.legacyCallHandler,
|
||||
}),
|
||||
};
|
||||
}
|
||||
@@ -94,6 +101,7 @@ function computeSnapshot(props: Props): RootCallTileViewSnapshot {
|
||||
getRelationsForEvent: props.getRelationsForEvent,
|
||||
callStore: props.callStore,
|
||||
cli: props.cli,
|
||||
legacyCallHandler: props.legacyCallHandler,
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
+11
-8
@@ -18,6 +18,7 @@ import { getMockedMember, getMockedRtcNotificationEvent, MockedCall, MockedCallS
|
||||
import type { EventTimeline, RoomState } from "matrix-js-sdk/src/matrix";
|
||||
import { placeCall } from "../../../../../../../utils/room/placeCall";
|
||||
import { PlatformCallType } from "../../../../../../../hooks/room/useRoomCall";
|
||||
import { SDKContextClass } from "../../../../../../../contexts/SDKContextClass.ts";
|
||||
|
||||
/**
|
||||
* There's a nasty circular dependency in useRoomCall so that we end up with:
|
||||
@@ -40,6 +41,8 @@ vi.mock(import("../../../../../../../utils/room/placeCall"), () => {
|
||||
const roomId = "!my-room:m.org";
|
||||
|
||||
describe("BaseOngoingCallViewModel", () => {
|
||||
const legacyCallHandler = SDKContextClass.instance.legacyCallHandler;
|
||||
|
||||
describe("should compute the correct snapshot", () => {
|
||||
it("startedByDisplayName", () => {
|
||||
const cli = stubClient();
|
||||
@@ -50,7 +53,7 @@ describe("BaseOngoingCallViewModel", () => {
|
||||
const call = MockedCall.create();
|
||||
const callStore = MockedCallStore.create(call);
|
||||
|
||||
const vm = new BaseOngoingCallViewModel({ mxEvent, cli, callStore, roomId });
|
||||
const vm = new BaseOngoingCallViewModel({ mxEvent, cli, callStore, roomId, legacyCallHandler });
|
||||
expect(vm.getSnapshot().startedByDisplayName).toStrictEqual("Alice");
|
||||
});
|
||||
|
||||
@@ -65,7 +68,7 @@ describe("BaseOngoingCallViewModel", () => {
|
||||
const callStore = MockedCallStore.create(call);
|
||||
|
||||
// Alice hasn't joined the call yet
|
||||
const vm = new BaseOngoingCallViewModel({ mxEvent, cli, callStore, roomId });
|
||||
const vm = new BaseOngoingCallViewModel({ mxEvent, cli, callStore, roomId, legacyCallHandler });
|
||||
expect(vm.getSnapshot().isJoined).toStrictEqual(false);
|
||||
|
||||
// Alice has joined call
|
||||
@@ -85,11 +88,11 @@ describe("BaseOngoingCallViewModel", () => {
|
||||
const call = MockedCall.create();
|
||||
const callStore = MockedCallStore.create(call);
|
||||
|
||||
const vm1 = new BaseOngoingCallViewModel({ mxEvent, cli, callStore, roomId });
|
||||
const vm1 = new BaseOngoingCallViewModel({ mxEvent, cli, callStore, roomId, legacyCallHandler });
|
||||
expect(vm1.getSnapshot().callDirection).toStrictEqual(CallDirection.Incoming);
|
||||
|
||||
vi.spyOn(cli, "getUserId").mockReturnValue("@alice:m.org");
|
||||
const vm2 = new BaseOngoingCallViewModel({ mxEvent, cli, callStore, roomId });
|
||||
const vm2 = new BaseOngoingCallViewModel({ mxEvent, cli, callStore, roomId, legacyCallHandler });
|
||||
expect(vm2.getSnapshot().callDirection).toStrictEqual(CallDirection.Outgoing);
|
||||
});
|
||||
|
||||
@@ -103,7 +106,7 @@ describe("BaseOngoingCallViewModel", () => {
|
||||
const callStore = MockedCallStore.create(call);
|
||||
|
||||
// Call has no other participants other than alice
|
||||
const vm = new BaseOngoingCallViewModel({ mxEvent, cli, callStore, roomId });
|
||||
const vm = new BaseOngoingCallViewModel({ mxEvent, cli, callStore, roomId, legacyCallHandler });
|
||||
expect(vm.getSnapshot().callHasOtherParticipants).toStrictEqual(false);
|
||||
|
||||
// Let's say others join
|
||||
@@ -135,7 +138,7 @@ describe("BaseOngoingCallViewModel", () => {
|
||||
const call = MockedCall.create();
|
||||
const callStore = MockedCallStore.create(call);
|
||||
|
||||
const vm = new BaseOngoingCallViewModel({ mxEvent, cli, callStore, roomId });
|
||||
const vm = new BaseOngoingCallViewModel({ mxEvent, cli, callStore, roomId, legacyCallHandler });
|
||||
expect(vm.getSnapshot().isJoinable).toStrictEqual(true);
|
||||
});
|
||||
});
|
||||
@@ -148,10 +151,10 @@ describe("BaseOngoingCallViewModel", () => {
|
||||
|
||||
const call = MockedCall.create().withParticipants([mxEvent.sender]);
|
||||
const callStore = MockedCallStore.create(call);
|
||||
const vm = new BaseOngoingCallViewModel({ mxEvent, cli, callStore, roomId });
|
||||
const vm = new BaseOngoingCallViewModel({ mxEvent, cli, callStore, roomId, legacyCallHandler });
|
||||
|
||||
vm.join();
|
||||
const [room, callType, platformCallType] = vi.mocked(placeCall).mock.calls[0];
|
||||
const [_, room, callType, platformCallType] = vi.mocked(placeCall).mock.calls[0];
|
||||
expect(room.roomId).toStrictEqual(roomId);
|
||||
expect(callType).toStrictEqual(CallType.Video);
|
||||
expect(platformCallType).toStrictEqual(PlatformCallType.ElementCall);
|
||||
|
||||
+13
-1
@@ -29,6 +29,7 @@ import { PlatformCallType } from "../../../../../../../hooks/room/useRoomCall";
|
||||
import { type GetRelationsForEvent } from "../../../../../../../components/views/rooms/EventTile";
|
||||
import { getIntentFromEvent } from "../../common";
|
||||
import { DurationViewModel } from "./components/DurationViewModel";
|
||||
import type LegacyCallHandler from "../../../../../../../LegacyCallHandler.tsx";
|
||||
|
||||
export interface Props {
|
||||
/**
|
||||
@@ -51,6 +52,10 @@ export interface Props {
|
||||
* {@link CallStore} to access calls in a room.
|
||||
*/
|
||||
callStore: CallStore;
|
||||
/**
|
||||
* {@link LegacyCallHandler} to handle calls in a room.
|
||||
*/
|
||||
legacyCallHandler: LegacyCallHandler;
|
||||
}
|
||||
|
||||
function getCallOrThrow(store: CallStore, roomId: string): ElementCall {
|
||||
@@ -167,7 +172,14 @@ export class BaseOngoingCallViewModel<
|
||||
}
|
||||
const callType = getIntentFromEvent(this.props.mxEvent);
|
||||
const type = callType === SharedComponentsCallType.Voice ? CallType.Voice : CallType.Video;
|
||||
placeCall(room, type, PlatformCallType.ElementCall, event?.shiftKey || undefined, type === CallType.Voice);
|
||||
placeCall(
|
||||
this.props.legacyCallHandler,
|
||||
room,
|
||||
type,
|
||||
PlatformCallType.ElementCall,
|
||||
event?.shiftKey || undefined,
|
||||
type === CallType.Voice,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+6
-3
@@ -10,13 +10,16 @@
|
||||
import { it, describe, expect } from "vitest";
|
||||
import { CallType } from "@element-hq/web-shared-components";
|
||||
|
||||
import { stubClient } from "../../../../../../../../test/test-utils";
|
||||
import { stubClient, TestSDKContext } from "../../../../../../../../test/test-utils";
|
||||
import { getMockedMember, getMockedRtcNotificationEvent, MockedCall, MockedCallStore } from "../../call-mocks";
|
||||
import { DmOngoingCallTileViewModel } from "./DmOngoingCallTileViewModel";
|
||||
|
||||
const roomId = "!my-room:m.org";
|
||||
|
||||
describe("DmOngoingCallTileViewModel", () => {
|
||||
const sdkContext = new TestSDKContext();
|
||||
const legacyCallHandler = sdkContext.legacyCallHandler;
|
||||
|
||||
describe("should compute the correct snapshot", () => {
|
||||
describe("callType", () => {
|
||||
it("voice", () => {
|
||||
@@ -27,7 +30,7 @@ describe("DmOngoingCallTileViewModel", () => {
|
||||
|
||||
const call = MockedCall.create().withParticipants([mxEvent.sender]);
|
||||
const callStore = MockedCallStore.create(call);
|
||||
const vm = new DmOngoingCallTileViewModel({ mxEvent, cli, callStore, roomId });
|
||||
const vm = new DmOngoingCallTileViewModel({ mxEvent, cli, callStore, roomId, legacyCallHandler });
|
||||
|
||||
expect(vm.getSnapshot().callType).toStrictEqual(CallType.Voice);
|
||||
});
|
||||
@@ -40,7 +43,7 @@ describe("DmOngoingCallTileViewModel", () => {
|
||||
|
||||
const call = MockedCall.create().withParticipants([mxEvent.sender]);
|
||||
const callStore = MockedCallStore.create(call);
|
||||
const vm = new DmOngoingCallTileViewModel({ mxEvent, cli, callStore, roomId });
|
||||
const vm = new DmOngoingCallTileViewModel({ mxEvent, cli, callStore, roomId, legacyCallHandler });
|
||||
|
||||
expect(vm.getSnapshot().callType).toStrictEqual(CallType.Video);
|
||||
});
|
||||
|
||||
+13
-3
@@ -10,7 +10,7 @@
|
||||
import { it, describe, expect, vi } from "vitest";
|
||||
import { EventType, MatrixEventEvent } from "matrix-js-sdk/src/matrix";
|
||||
|
||||
import { stubClient } from "../../../../../../../../test/test-utils";
|
||||
import { stubClient, TestSDKContext } from "../../../../../../../../test/test-utils";
|
||||
import {
|
||||
getMockedMember,
|
||||
getMockedRtcDeclineEvent,
|
||||
@@ -24,6 +24,9 @@ import { CallEvent } from "../../../../../../../models/Call";
|
||||
const roomId = "!my-room:m.org";
|
||||
|
||||
describe("RoomOngoingCallTileViewModel", () => {
|
||||
const sdkContext = new TestSDKContext();
|
||||
const legacyCallHandler = sdkContext.legacyCallHandler;
|
||||
|
||||
describe("should compute the correct snapshot", () => {
|
||||
it("totalParticipants", () => {
|
||||
const cli = stubClient();
|
||||
@@ -35,7 +38,7 @@ describe("RoomOngoingCallTileViewModel", () => {
|
||||
const james = getMockedMember(roomId, "@james:m.org", "James");
|
||||
const call = MockedCall.create().withParticipants([mxEvent.sender, bob, james]);
|
||||
const callStore = MockedCallStore.create(call);
|
||||
const vm = new RoomOngoingCallTileViewModel({ mxEvent, cli, callStore, roomId });
|
||||
const vm = new RoomOngoingCallTileViewModel({ mxEvent, cli, callStore, roomId, legacyCallHandler });
|
||||
|
||||
// Call has 3 participants now
|
||||
expect(vm.getSnapshot().totalParticipants).toStrictEqual(3);
|
||||
@@ -59,7 +62,14 @@ describe("RoomOngoingCallTileViewModel", () => {
|
||||
|
||||
const call = MockedCall.create().withParticipants([mxEvent.sender]);
|
||||
const callStore = MockedCallStore.create(call);
|
||||
const vm = new RoomOngoingCallTileViewModel({ mxEvent, cli, callStore, roomId, getRelationsForEvent });
|
||||
const vm = new RoomOngoingCallTileViewModel({
|
||||
mxEvent,
|
||||
cli,
|
||||
callStore,
|
||||
roomId,
|
||||
getRelationsForEvent,
|
||||
legacyCallHandler,
|
||||
});
|
||||
|
||||
// Call hasn't been ignored yet
|
||||
expect(vm.getSnapshot().isCallIgnored).toStrictEqual(false);
|
||||
|
||||
@@ -12,8 +12,9 @@ import { screen, render, waitFor } from "jest-matrix-react";
|
||||
import { mocked } from "jest-mock";
|
||||
|
||||
import FilePanel from "../../../../src/components/structures/FilePanel";
|
||||
import { mkEvent, stubClient } from "../../../test-utils";
|
||||
import { clientAndSDKContextRenderOptions, mkEvent, stubClient } from "../../../test-utils";
|
||||
import { MatrixClientPeg } from "../../../../src/MatrixClientPeg";
|
||||
import { SDKContextClass } from "../../../../src/contexts/SDKContextClass.ts";
|
||||
|
||||
jest.mock("matrix-js-sdk/src/matrix", () => ({
|
||||
...jest.requireActual("matrix-js-sdk/src/matrix"),
|
||||
@@ -38,7 +39,10 @@ describe("FilePanel", () => {
|
||||
room.getOrCreateFilteredTimelineSet = jest.fn().mockReturnValue(timelineSet);
|
||||
mocked(cli.getRoom).mockReturnValue(room);
|
||||
|
||||
const { asFragment } = render(<FilePanel roomId={room.roomId} onClose={jest.fn()} />);
|
||||
const { asFragment } = render(
|
||||
<FilePanel roomId={room.roomId} onClose={jest.fn()} />,
|
||||
clientAndSDKContextRenderOptions(cli, SDKContextClass.instance),
|
||||
);
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText("No files visible in this room")).toBeInTheDocument();
|
||||
});
|
||||
@@ -65,6 +69,7 @@ describe("FilePanel", () => {
|
||||
filePanel = ref;
|
||||
}}
|
||||
/>,
|
||||
clientAndSDKContextRenderOptions(cli, SDKContextClass.instance),
|
||||
);
|
||||
await screen.findByText("No files visible in this room");
|
||||
|
||||
|
||||
@@ -46,6 +46,7 @@ import Modal from "../../../../src/Modal";
|
||||
import { SETTINGS } from "../../../../src/settings/Settings";
|
||||
import ToastStore from "../../../../src/stores/ToastStore";
|
||||
import { ModuleApi } from "../../../../src/modules/Api";
|
||||
import { fireEvent } from "@testing-library/dom";
|
||||
|
||||
describe("<LoggedInView />", () => {
|
||||
const userId = "@alice:domain.org";
|
||||
@@ -67,6 +68,8 @@ describe("<LoggedInView />", () => {
|
||||
on: jest.fn(),
|
||||
},
|
||||
getAuthMetadata: jest.fn().mockRejectedValue(new Error("Legacy auth")),
|
||||
hasLazyLoadMembersEnabled: jest.fn(),
|
||||
isInitialSyncComplete: jest.fn(),
|
||||
});
|
||||
const mediaHandler = new MediaHandler(mockClient);
|
||||
const mockSdkContext = new TestSDKContext();
|
||||
@@ -555,4 +558,11 @@ describe("<LoggedInView />", () => {
|
||||
expect(container.querySelector(".mx_SpacePanel")).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
it("should handle KeyBindingAction.ToggleRoomSidePanel", async () => {
|
||||
getComponent({ page_type: "room_view" });
|
||||
jest.spyOn(mockSdkContext.rightPanelStore, "togglePanel");
|
||||
fireEvent.keyDown(document.body, { key: ".", code: "Period", ctrlKey: true, keyCode: 190 });
|
||||
expect(mockSdkContext.rightPanelStore.togglePanel).toHaveBeenCalledWith(null);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -21,6 +21,7 @@ import {
|
||||
RoomMember,
|
||||
RoomStateEvent,
|
||||
SearchResult,
|
||||
User,
|
||||
} from "matrix-js-sdk/src/matrix";
|
||||
import { type CryptoApi, CryptoEvent, UserVerificationStatus } from "matrix-js-sdk/src/crypto-api";
|
||||
import { KnownMembership } from "matrix-js-sdk/src/types";
|
||||
@@ -1132,6 +1133,54 @@ describe("RoomView", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("should handle Action.ViewUser", async () => {
|
||||
await mountRoomView();
|
||||
jest.spyOn(stores.rightPanelStore, "setCards");
|
||||
const member = new User("@user:server");
|
||||
defaultDispatcher.dispatch(
|
||||
{
|
||||
action: Action.ViewUser,
|
||||
member,
|
||||
},
|
||||
true,
|
||||
);
|
||||
expect(stores.rightPanelStore.setCards).toHaveBeenCalledWith([
|
||||
{ phase: RightPanelPhases.RoomSummary },
|
||||
{ phase: RightPanelPhases.MemberList },
|
||||
{ phase: RightPanelPhases.MemberInfo, state: { member } },
|
||||
]);
|
||||
});
|
||||
|
||||
it("should handle Action.ViewUser with push", async () => {
|
||||
await mountRoomView();
|
||||
jest.spyOn(stores.rightPanelStore, "pushCard");
|
||||
const member = new User("@user:server");
|
||||
defaultDispatcher.dispatch(
|
||||
{
|
||||
action: Action.ViewUser,
|
||||
member,
|
||||
push: true,
|
||||
},
|
||||
true,
|
||||
);
|
||||
expect(stores.rightPanelStore.pushCard).toHaveBeenCalledWith({
|
||||
phase: RightPanelPhases.MemberInfo,
|
||||
state: { member },
|
||||
});
|
||||
});
|
||||
|
||||
it("should handle Action.View3pidInvite", async () => {
|
||||
await mountRoomView();
|
||||
jest.spyOn(stores.rightPanelStore, "showOrHidePhase");
|
||||
defaultDispatcher.dispatch(
|
||||
{
|
||||
action: Action.View3pidInvite,
|
||||
},
|
||||
true,
|
||||
);
|
||||
expect(stores.rightPanelStore.showOrHidePhase).toHaveBeenCalledWith("MemberList");
|
||||
});
|
||||
|
||||
describe("when there is a RoomView", () => {
|
||||
const widget1Id = "widget1";
|
||||
const widget2Id = "widget2";
|
||||
|
||||
@@ -10,7 +10,7 @@ import { mocked, type MockedObject } from "jest-mock";
|
||||
import { type MatrixClient, MatrixEvent, Preset, Room } from "matrix-js-sdk/src/matrix";
|
||||
import { render, cleanup, screen, fireEvent, waitFor, act } from "jest-matrix-react";
|
||||
|
||||
import { stubClient, mockPlatformPeg, unmockPlatformPeg, withClientContextRenderOptions } from "../../../test-utils";
|
||||
import { stubClient, mockPlatformPeg, unmockPlatformPeg, clientAndSDKContextRenderOptions } from "../../../test-utils";
|
||||
import { RightPanelPhases } from "../../../../src/stores/right-panel/RightPanelStorePhases";
|
||||
import SpaceRoomView from "../../../../src/components/structures/SpaceRoomView.tsx";
|
||||
import ResizeNotifier from "../../../../src/utils/ResizeNotifier.ts";
|
||||
@@ -101,7 +101,7 @@ describe("SpaceRoomView", () => {
|
||||
onRejectButtonClicked={jest.fn()}
|
||||
justCreatedOpts={justCreatedOpts}
|
||||
/>,
|
||||
withClientContextRenderOptions(cli),
|
||||
clientAndSDKContextRenderOptions(cli, SDKContextClass.instance),
|
||||
);
|
||||
return spaceRoomView;
|
||||
};
|
||||
|
||||
@@ -24,10 +24,18 @@ import { _t } from "../../../../src/languageHandler";
|
||||
import { MatrixClientPeg } from "../../../../src/MatrixClientPeg";
|
||||
import { RoomPermalinkCreator } from "../../../../src/utils/permalinks/Permalinks";
|
||||
import ResizeNotifier from "../../../../src/utils/ResizeNotifier";
|
||||
import { createTestClient, getRoomContext, mkRoom, mockPlatformPeg, stubClient } from "../../../test-utils";
|
||||
import {
|
||||
clientAndSDKContextRenderOptions,
|
||||
createTestClient,
|
||||
getRoomContext,
|
||||
mkRoom,
|
||||
mockPlatformPeg,
|
||||
stubClient,
|
||||
} from "../../../test-utils";
|
||||
import { mkThread } from "../../../test-utils/threads";
|
||||
import { ScopedRoomContextProvider } from "../../../../src/contexts/ScopedRoomContext.tsx";
|
||||
import type { RoomContextType } from "../../../../src/contexts/RoomContext.ts";
|
||||
import { SDKContextClass } from "../../../../src/contexts/SDKContextClass.ts";
|
||||
|
||||
jest.mock("../../../../src/utils/Feedback");
|
||||
|
||||
@@ -215,7 +223,10 @@ describe("ThreadPanel", () => {
|
||||
myThreads!.addLiveEvent(mixedThread.rootEvent, { addToState: true });
|
||||
myThreads!.addLiveEvent(ownThread.rootEvent, { addToState: true });
|
||||
|
||||
const renderResult = render(<TestThreadPanel />);
|
||||
const renderResult = render(
|
||||
<TestThreadPanel />,
|
||||
clientAndSDKContextRenderOptions(createTestClient(), SDKContextClass.instance),
|
||||
);
|
||||
await waitFor(() => expect(renderResult.container.querySelector(".mx_AutoHideScrollbar")).toBeFalsy());
|
||||
await waitFor(() => {
|
||||
const events = findEvents(renderResult.container);
|
||||
@@ -260,7 +271,10 @@ describe("ThreadPanel", () => {
|
||||
const [allThreads] = room.threadsTimelineSets;
|
||||
allThreads!.addLiveEvent(otherThread.rootEvent, { addToState: true });
|
||||
|
||||
const renderResult = render(<TestThreadPanel />);
|
||||
const renderResult = render(
|
||||
<TestThreadPanel />,
|
||||
clientAndSDKContextRenderOptions(createTestClient(), SDKContextClass.instance),
|
||||
);
|
||||
await waitFor(() => expect(renderResult.container.querySelector(".mx_AutoHideScrollbar")).toBeFalsy());
|
||||
await waitFor(() => {
|
||||
const events = findEvents(renderResult.container);
|
||||
|
||||
+7
-2
@@ -10,9 +10,10 @@ import { type Mocked } from "jest-mock";
|
||||
import { UserVerificationStatus, type CryptoApi } from "matrix-js-sdk/src/crypto-api";
|
||||
import { renderHook, waitFor } from "jest-matrix-react";
|
||||
|
||||
import { createTestClient, withClientContextRenderOptions } from "../../../../../test-utils";
|
||||
import { clientAndSDKContextRenderOptions, createTestClient } from "../../../../../test-utils";
|
||||
import { MatrixClientPeg } from "../../../../../../src/MatrixClientPeg";
|
||||
import { useUserInfoVerificationViewModel } from "../../../../../../src/components/viewmodels/right_panel/user_info/UserInfoHeaderVerificationViewModel";
|
||||
import { TestSDKContext } from "../../../../TestSDKContext.ts";
|
||||
|
||||
describe("useUserInfoVerificationHeaderViewModel", () => {
|
||||
const defaultRoomId = "!fkfk";
|
||||
@@ -26,6 +27,7 @@ describe("useUserInfoVerificationHeaderViewModel", () => {
|
||||
};
|
||||
let mockClient: MatrixClient;
|
||||
let mockCrypto: Mocked<CryptoApi>;
|
||||
let sdkContext: TestSDKContext;
|
||||
|
||||
beforeEach(() => {
|
||||
mockCrypto = {
|
||||
@@ -43,6 +45,9 @@ describe("useUserInfoVerificationHeaderViewModel", () => {
|
||||
} as unknown as Mocked<CryptoApi>;
|
||||
|
||||
mockClient = createTestClient();
|
||||
sdkContext = new TestSDKContext();
|
||||
sdkContext._client = mockClient;
|
||||
|
||||
jest.spyOn(mockClient, "doesServerSupportUnstableFeature").mockResolvedValue(true);
|
||||
jest.spyOn(mockClient.secretStorage, "hasKey").mockResolvedValue(true);
|
||||
jest.spyOn(mockClient, "getCrypto").mockReturnValue(mockCrypto);
|
||||
@@ -57,7 +62,7 @@ describe("useUserInfoVerificationHeaderViewModel", () => {
|
||||
const renderUserInfoHeaderVerificationHook = (props = defaultProps) => {
|
||||
return renderHook(
|
||||
() => useUserInfoVerificationViewModel(props.member, props.devices),
|
||||
withClientContextRenderOptions(mockClient),
|
||||
clientAndSDKContextRenderOptions(mockClient, sdkContext),
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
+6
-2
@@ -17,8 +17,9 @@ import ThreadListContextMenu, {
|
||||
} from "../../../../../src/components/views/context_menus/ThreadListContextMenu";
|
||||
import { MatrixClientPeg } from "../../../../../src/MatrixClientPeg";
|
||||
import { RoomPermalinkCreator } from "../../../../../src/utils/permalinks/Permalinks";
|
||||
import { stubClient } from "../../../../test-utils/test-utils";
|
||||
import { stubClient, clientAndSDKContextRenderOptions } from "../../../../test-utils";
|
||||
import { mkThread } from "../../../../test-utils/threads";
|
||||
import { SDKContextClass } from "../../../../../src/contexts/SDKContextClass.ts";
|
||||
|
||||
describe("ThreadListContextMenu", () => {
|
||||
const ROOM_ID = "!123:matrix.org";
|
||||
@@ -28,7 +29,10 @@ describe("ThreadListContextMenu", () => {
|
||||
let event: MatrixEvent;
|
||||
|
||||
function getComponent(props: Partial<ThreadListContextMenuProps>) {
|
||||
return render(<ThreadListContextMenu mxEvent={event} {...props} />);
|
||||
return render(
|
||||
<ThreadListContextMenu mxEvent={event} {...props} />,
|
||||
clientAndSDKContextRenderOptions(mockClient, SDKContextClass.instance),
|
||||
);
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
|
||||
+9
-2
@@ -19,8 +19,9 @@ import {
|
||||
} from "matrix-js-sdk/src/crypto-api";
|
||||
import { VerificationMethod } from "matrix-js-sdk/src/types";
|
||||
|
||||
import { stubClient } from "../../../../test-utils";
|
||||
import { stubClient, withContexts } from "../../../../test-utils";
|
||||
import VerificationRequestDialog from "../../../../../src/components/views/dialogs/VerificationRequestDialog";
|
||||
import { SDKContextClass } from "../../../../../src/contexts/SDKContextClass";
|
||||
|
||||
describe("VerificationRequestDialog", () => {
|
||||
function renderComponent(phase: VerificationPhase, method?: "emoji" | "qr"): ReturnType<typeof render> {
|
||||
@@ -29,6 +30,7 @@ describe("VerificationRequestDialog", () => {
|
||||
|
||||
return render(
|
||||
<VerificationRequestDialog onFinished={jest.fn()} member={member} verificationRequest={request} />,
|
||||
withContexts({ sdkContext: SDKContextClass.instance }),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -124,6 +126,7 @@ describe("VerificationRequestDialog", () => {
|
||||
member={member}
|
||||
verificationRequestPromise={requestPromise}
|
||||
/>,
|
||||
withContexts({ sdkContext: SDKContextClass.instance }),
|
||||
);
|
||||
|
||||
// And wait for the component to mount, the promise to resolve and the component state to update
|
||||
@@ -153,6 +156,7 @@ describe("VerificationRequestDialog", () => {
|
||||
verificationRequest={request}
|
||||
verificationRequestPromise={requestPromise}
|
||||
/>,
|
||||
withContexts({ sdkContext: SDKContextClass.instance }),
|
||||
);
|
||||
|
||||
// And wait for the component to mount, the promise to resolve and the component state to update
|
||||
@@ -173,7 +177,10 @@ describe("VerificationRequestDialog", () => {
|
||||
const member = User.createUser("@alice:example.org", stubClient());
|
||||
const request = createRequest(VerificationPhase.Unsent);
|
||||
|
||||
render(<VerificationRequestDialog onFinished={jest.fn()} member={member} verificationRequest={request} />);
|
||||
render(
|
||||
<VerificationRequestDialog onFinished={jest.fn()} member={member} verificationRequest={request} />,
|
||||
withContexts({ sdkContext: SDKContextClass.instance }),
|
||||
);
|
||||
|
||||
// When I cancel the request (which changes phase and emits a Changed event)
|
||||
await act(async () => await request.cancel());
|
||||
|
||||
@@ -10,6 +10,8 @@ import { fireEvent, render, screen } from "jest-matrix-react";
|
||||
|
||||
import BaseCard from "../../../../../src/components/views/right_panel/BaseCard.tsx";
|
||||
import RightPanelStore from "../../../../../src/stores/right-panel/RightPanelStore.ts";
|
||||
import { clientAndSDKContextRenderOptions } from "../../../../test-utils";
|
||||
import { SDKContextClass } from "../../../../../src/contexts/SDKContextClass.ts";
|
||||
|
||||
jest.mock("../../../../../src/stores/right-panel/RightPanelStore", () => ({
|
||||
instance: {
|
||||
@@ -24,6 +26,7 @@ describe("<BaseCard />", () => {
|
||||
<BaseCard header="Heading text">
|
||||
<div>Content</div>
|
||||
</BaseCard>,
|
||||
clientAndSDKContextRenderOptions(SDKContextClass.instance.client!, SDKContextClass.instance),
|
||||
);
|
||||
|
||||
expect(screen.getByRole("heading")).toHaveTextContent("Heading text");
|
||||
|
||||
@@ -14,27 +14,34 @@ import { MatrixWidgetType } from "matrix-widget-api";
|
||||
import userEvent from "@testing-library/user-event";
|
||||
|
||||
import ExtensionsCard from "../../../../../src/components/views/right_panel/ExtensionsCard";
|
||||
import { stubClient } from "../../../../test-utils";
|
||||
import { clientAndSDKContextRenderOptions, stubClient } from "../../../../test-utils";
|
||||
import { type IApp } from "../../../../../src/stores/WidgetStore";
|
||||
import WidgetUtils, { useWidgets } from "../../../../../src/utils/WidgetUtils";
|
||||
import { WidgetLayoutStore } from "../../../../../src/stores/widgets/WidgetLayoutStore";
|
||||
import { IntegrationManagers } from "../../../../../src/integrations/IntegrationManagers";
|
||||
import { TestSDKContext } from "../../../TestSDKContext.ts";
|
||||
|
||||
jest.mock("../../../../../src/utils/WidgetUtils");
|
||||
|
||||
describe("<ExtensionsCard />", () => {
|
||||
let client: Mocked<MatrixClient>;
|
||||
let room: Room;
|
||||
let sdkContext: TestSDKContext;
|
||||
|
||||
beforeEach(() => {
|
||||
client = mocked(stubClient());
|
||||
sdkContext = new TestSDKContext();
|
||||
sdkContext._client = client;
|
||||
room = new Room("!room:server", client, client.getSafeUserId());
|
||||
mocked(WidgetUtils.getWidgetName).mockImplementation((app) => app?.name ?? "No Name");
|
||||
});
|
||||
|
||||
it("should render empty state", () => {
|
||||
mocked(useWidgets).mockReturnValue([]);
|
||||
const { asFragment } = render(<ExtensionsCard room={room} onClose={jest.fn()} />);
|
||||
const { asFragment } = render(
|
||||
<ExtensionsCard room={room} onClose={jest.fn()} />,
|
||||
clientAndSDKContextRenderOptions(client, sdkContext),
|
||||
);
|
||||
expect(screen.getByText("Boost productivity with more tools, widgets and bots")).toBeInTheDocument();
|
||||
expect(asFragment()).toMatchSnapshot();
|
||||
});
|
||||
@@ -61,7 +68,10 @@ describe("<ExtensionsCard />", () => {
|
||||
},
|
||||
] satisfies IApp[]);
|
||||
|
||||
const { asFragment } = render(<ExtensionsCard room={room} onClose={jest.fn()} />);
|
||||
const { asFragment } = render(
|
||||
<ExtensionsCard room={room} onClose={jest.fn()} />,
|
||||
clientAndSDKContextRenderOptions(client, sdkContext),
|
||||
);
|
||||
expect(screen.getByText("Custom Widget")).toBeInTheDocument();
|
||||
expect(screen.getByText("Jitsi")).toBeInTheDocument();
|
||||
expect(asFragment()).toMatchSnapshot();
|
||||
@@ -81,7 +91,10 @@ describe("<ExtensionsCard />", () => {
|
||||
},
|
||||
] satisfies IApp[]);
|
||||
|
||||
const { container } = render(<ExtensionsCard room={room} onClose={jest.fn()} />);
|
||||
const { container } = render(
|
||||
<ExtensionsCard room={room} onClose={jest.fn()} />,
|
||||
clientAndSDKContextRenderOptions(client, sdkContext),
|
||||
);
|
||||
await userEvent.click(container.querySelector(".mx_ExtensionsCard_app_options")!);
|
||||
expect(document.querySelector(".mx_IconizedContextMenu")).toMatchSnapshot();
|
||||
});
|
||||
@@ -100,7 +113,10 @@ describe("<ExtensionsCard />", () => {
|
||||
},
|
||||
] satisfies IApp[]);
|
||||
|
||||
render(<ExtensionsCard room={room} onClose={jest.fn()} />);
|
||||
render(
|
||||
<ExtensionsCard room={room} onClose={jest.fn()} />,
|
||||
clientAndSDKContextRenderOptions(client, sdkContext),
|
||||
);
|
||||
expect(screen.getByText("Set layout for everyone")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
@@ -118,7 +134,10 @@ describe("<ExtensionsCard />", () => {
|
||||
},
|
||||
] satisfies IApp[]);
|
||||
|
||||
render(<ExtensionsCard room={room} onClose={jest.fn()} />);
|
||||
render(
|
||||
<ExtensionsCard room={room} onClose={jest.fn()} />,
|
||||
clientAndSDKContextRenderOptions(client, sdkContext),
|
||||
);
|
||||
expect(screen.getByText("Custom Widget").closest(".mx_ExtensionsCard_Button_pinned")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
@@ -137,15 +156,52 @@ describe("<ExtensionsCard />", () => {
|
||||
},
|
||||
] satisfies IApp[]);
|
||||
|
||||
render(<ExtensionsCard room={room} onClose={jest.fn()} />);
|
||||
render(
|
||||
<ExtensionsCard room={room} onClose={jest.fn()} />,
|
||||
clientAndSDKContextRenderOptions(client, sdkContext),
|
||||
);
|
||||
expect(screen.getByLabelText("You can only pin up to 3 widgets")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("should should open integration manager on click", async () => {
|
||||
jest.spyOn(IntegrationManagers.sharedInstance(), "hasManager").mockReturnValue(false);
|
||||
const spy = jest.spyOn(IntegrationManagers.sharedInstance(), "openNoManagerDialog");
|
||||
render(<ExtensionsCard room={room} onClose={jest.fn()} />);
|
||||
render(
|
||||
<ExtensionsCard room={room} onClose={jest.fn()} />,
|
||||
clientAndSDKContextRenderOptions(client, sdkContext),
|
||||
);
|
||||
await userEvent.click(screen.getByText("Add extensions"));
|
||||
expect(spy).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("should set room layout on click", async () => {
|
||||
mocked(useWidgets).mockReturnValue([
|
||||
{
|
||||
id: "id",
|
||||
roomId: room.roomId,
|
||||
eventId: "$event1",
|
||||
creatorUserId: client.getSafeUserId(),
|
||||
type: MatrixWidgetType.Custom,
|
||||
name: "Custom Widget",
|
||||
url: "http://url1",
|
||||
},
|
||||
{
|
||||
id: "jitsi",
|
||||
roomId: room.roomId,
|
||||
eventId: "$event2",
|
||||
creatorUserId: client.getSafeUserId(),
|
||||
type: MatrixWidgetType.JitsiMeet,
|
||||
name: "Jitsi",
|
||||
url: "http://jitsi",
|
||||
},
|
||||
] satisfies IApp[]);
|
||||
|
||||
jest.spyOn(sdkContext.widgetLayoutStore, "copyLayoutToRoom");
|
||||
render(
|
||||
<ExtensionsCard room={room} onClose={jest.fn()} />,
|
||||
clientAndSDKContextRenderOptions(client, sdkContext),
|
||||
);
|
||||
await userEvent.click(screen.getByText("Set layout for everyone"));
|
||||
expect(sdkContext.widgetLayoutStore.copyLayoutToRoom).toHaveBeenCalledWith(room);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -27,13 +27,19 @@ import { PollEndEvent } from "matrix-js-sdk/src/extensible_events_v1/PollEndEven
|
||||
import { sleep } from "matrix-js-sdk/src/utils";
|
||||
import userEvent from "@testing-library/user-event";
|
||||
|
||||
import { stubClient, mkEvent, mkMessage, flushPromises } from "../../../../test-utils";
|
||||
import {
|
||||
stubClient,
|
||||
mkEvent,
|
||||
mkMessage,
|
||||
flushPromises,
|
||||
clientAndSDKContextRenderOptions,
|
||||
} from "../../../../test-utils";
|
||||
import { MatrixClientPeg } from "../../../../../src/MatrixClientPeg";
|
||||
import { PinnedMessagesCard } from "../../../../../src/components/views/right_panel/PinnedMessagesCard";
|
||||
import MatrixClientContext from "../../../../../src/contexts/MatrixClientContext";
|
||||
import { RoomPermalinkCreator } from "../../../../../src/utils/permalinks/Permalinks";
|
||||
import Modal from "../../../../../src/Modal";
|
||||
import { UnpinAllDialog } from "../../../../../src/components/views/dialogs/UnpinAllDialog";
|
||||
import { SDKContextClass } from "../../../../../src/contexts/SDKContextClass.ts";
|
||||
|
||||
describe("<PinnedMessagesCard />", () => {
|
||||
let cli: MockedObject<MatrixClient>;
|
||||
@@ -86,13 +92,12 @@ describe("<PinnedMessagesCard />", () => {
|
||||
|
||||
async function renderMessagePinList(room: Room): Promise<RenderResult> {
|
||||
const renderResult = render(
|
||||
<MatrixClientContext.Provider value={cli}>
|
||||
<PinnedMessagesCard
|
||||
room={room}
|
||||
onClose={jest.fn()}
|
||||
permalinkCreator={new RoomPermalinkCreator(room, room.roomId)}
|
||||
/>
|
||||
</MatrixClientContext.Provider>,
|
||||
<PinnedMessagesCard
|
||||
room={room}
|
||||
onClose={jest.fn()}
|
||||
permalinkCreator={new RoomPermalinkCreator(room, room.roomId)}
|
||||
/>,
|
||||
clientAndSDKContextRenderOptions(cli, SDKContextClass.instance),
|
||||
);
|
||||
// Wait a tick for state updates
|
||||
await act(() => sleep(0));
|
||||
@@ -170,13 +175,12 @@ describe("<PinnedMessagesCard />", () => {
|
||||
it("should show spinner whilst loading", async () => {
|
||||
const room = mkRoom([], [pin1]);
|
||||
render(
|
||||
<MatrixClientContext.Provider value={cli}>
|
||||
<PinnedMessagesCard
|
||||
room={room}
|
||||
onClose={jest.fn()}
|
||||
permalinkCreator={new RoomPermalinkCreator(room, room.roomId)}
|
||||
/>
|
||||
</MatrixClientContext.Provider>,
|
||||
<PinnedMessagesCard
|
||||
room={room}
|
||||
onClose={jest.fn()}
|
||||
permalinkCreator={new RoomPermalinkCreator(room, room.roomId)}
|
||||
/>,
|
||||
clientAndSDKContextRenderOptions(cli, SDKContextClass.instance),
|
||||
);
|
||||
|
||||
await waitForElementToBeRemoved(() => screen.queryAllByRole("progressbar"));
|
||||
@@ -322,13 +326,12 @@ describe("<PinnedMessagesCard />", () => {
|
||||
).mockReturnValue(false);
|
||||
|
||||
const { asFragment } = render(
|
||||
<MatrixClientContext.Provider value={cli}>
|
||||
<PinnedMessagesCard
|
||||
room={room}
|
||||
onClose={jest.fn()}
|
||||
permalinkCreator={new RoomPermalinkCreator(room, room.roomId)}
|
||||
/>
|
||||
</MatrixClientContext.Provider>,
|
||||
<PinnedMessagesCard
|
||||
room={room}
|
||||
onClose={jest.fn()}
|
||||
permalinkCreator={new RoomPermalinkCreator(room, room.roomId)}
|
||||
/>,
|
||||
clientAndSDKContextRenderOptions(cli, SDKContextClass.instance),
|
||||
);
|
||||
|
||||
// Wait a tick for state updates
|
||||
|
||||
@@ -23,6 +23,8 @@ import {
|
||||
useRoomSummaryCardViewModel,
|
||||
} from "../../../../../src/components/viewmodels/right_panel/RoomSummaryCardViewModel";
|
||||
import DMRoomMap from "../../../../../src/utils/DMRoomMap";
|
||||
import { SDKContext } from "../../../../../src/contexts/SDKContext.ts";
|
||||
import { SDKContextClass } from "../../../../../src/contexts/SDKContextClass.ts";
|
||||
|
||||
// Mock the viewmodel hooks
|
||||
jest.mock("../../../../../src/components/viewmodels/right_panel/RoomSummaryCardViewModel", () => ({
|
||||
@@ -45,9 +47,11 @@ describe("<RoomSummaryCard />", () => {
|
||||
|
||||
return render(<RoomSummaryCardView {...defaultProps} {...props} />, {
|
||||
wrapper: ({ children }) => (
|
||||
<MatrixClientContext.Provider value={mockClient}>
|
||||
<LinkedTextContext.Provider value={{}}>{children}</LinkedTextContext.Provider>
|
||||
</MatrixClientContext.Provider>
|
||||
<SDKContext.Provider value={SDKContextClass.instance}>
|
||||
<MatrixClientContext.Provider value={mockClient}>
|
||||
<LinkedTextContext.Provider value={{}}>{children}</LinkedTextContext.Provider>
|
||||
</MatrixClientContext.Provider>
|
||||
</SDKContext.Provider>
|
||||
),
|
||||
});
|
||||
};
|
||||
|
||||
@@ -32,12 +32,12 @@ import UserInfo, { disambiguateDevices } from "../../../../../src/components/vie
|
||||
import { getPowerLevels } from "../../../../../src/components/viewmodels/right_panel/user_info/UserInfoBasicViewModel";
|
||||
import { RightPanelPhases } from "../../../../../src/stores/right-panel/RightPanelStorePhases";
|
||||
import { MatrixClientPeg } from "../../../../../src/MatrixClientPeg";
|
||||
import MatrixClientContext from "../../../../../src/contexts/MatrixClientContext";
|
||||
import Modal from "../../../../../src/Modal";
|
||||
import { clearAllModals, flushPromises } from "../../../../test-utils";
|
||||
import { clearAllModals, clientAndSDKContextRenderOptions, flushPromises } from "../../../../test-utils";
|
||||
import ErrorDialog from "../../../../../src/components/views/dialogs/ErrorDialog";
|
||||
import { shouldShowComponent } from "../../../../../src/customisations/helpers/UIComponents";
|
||||
import { UIComponent } from "../../../../../src/settings/UIFeature";
|
||||
import { TestSDKContext } from "../../../TestSDKContext.ts";
|
||||
|
||||
jest.mock("../../../../../src/utils/direct-messages", () => ({
|
||||
...jest.requireActual("../../../../../src/utils/direct-messages"),
|
||||
@@ -78,6 +78,7 @@ const defaultUser = new User(defaultUserId);
|
||||
let mockRoom: Mocked<Room>;
|
||||
let mockClient: Mocked<MatrixClient>;
|
||||
let mockCrypto: Mocked<CryptoApi>;
|
||||
let sdkContext: TestSDKContext;
|
||||
const origDate = global.Date.prototype.toLocaleString;
|
||||
|
||||
beforeEach(() => {
|
||||
@@ -131,6 +132,8 @@ beforeEach(() => {
|
||||
setPowerLevel: jest.fn(),
|
||||
getCrypto: jest.fn().mockReturnValue(mockCrypto),
|
||||
} as unknown as MatrixClient);
|
||||
sdkContext = new TestSDKContext();
|
||||
sdkContext._client = mockClient;
|
||||
|
||||
jest.spyOn(MatrixClientPeg, "get").mockReturnValue(mockClient);
|
||||
jest.spyOn(MatrixClientPeg, "safeGet").mockReturnValue(mockClient);
|
||||
@@ -162,13 +165,10 @@ describe("<UserInfo />", () => {
|
||||
};
|
||||
|
||||
const renderComponent = (props = {}) => {
|
||||
const Wrapper = (wrapperProps = {}) => {
|
||||
return <MatrixClientContext.Provider value={mockClient} {...wrapperProps} />;
|
||||
};
|
||||
|
||||
return render(<UserInfo {...defaultProps} {...props} />, {
|
||||
wrapper: Wrapper,
|
||||
});
|
||||
return render(
|
||||
<UserInfo {...defaultProps} {...props} />,
|
||||
clientAndSDKContextRenderOptions(mockClient, sdkContext),
|
||||
);
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
@@ -307,11 +307,7 @@ describe("<UserInfo />", () => {
|
||||
});
|
||||
|
||||
it("renders the message button", () => {
|
||||
render(
|
||||
<MatrixClientContext.Provider value={mockClient}>
|
||||
<UserInfo {...defaultProps} />
|
||||
</MatrixClientContext.Provider>,
|
||||
);
|
||||
render(<UserInfo {...defaultProps} />, clientAndSDKContextRenderOptions(mockClient, sdkContext));
|
||||
|
||||
screen.getByRole("button", { name: "Send message" });
|
||||
});
|
||||
@@ -322,11 +318,7 @@ describe("<UserInfo />", () => {
|
||||
return component !== UIComponent.CreateRooms;
|
||||
},
|
||||
() => {
|
||||
render(
|
||||
<MatrixClientContext.Provider value={mockClient}>
|
||||
<UserInfo {...defaultProps} />
|
||||
</MatrixClientContext.Provider>,
|
||||
);
|
||||
render(<UserInfo {...defaultProps} />, clientAndSDKContextRenderOptions(mockClient, sdkContext));
|
||||
|
||||
expect(screen.queryByRole("button", { name: "Message" })).toBeNull();
|
||||
},
|
||||
|
||||
+9
-8
@@ -13,9 +13,9 @@ import { render, waitFor, screen } from "jest-matrix-react";
|
||||
import React from "react";
|
||||
|
||||
import { MatrixClientPeg } from "../../../../../../src/MatrixClientPeg";
|
||||
import MatrixClientContext from "../../../../../../src/contexts/MatrixClientContext";
|
||||
import { UserInfoHeaderVerificationView } from "../../../../../../src/components/views/right_panel/user_info/UserInfoHeaderVerificationView";
|
||||
import { createTestClient } from "../../../../../test-utils";
|
||||
import { clientAndSDKContextRenderOptions, createTestClient } from "../../../../../test-utils";
|
||||
import { TestSDKContext } from "../../../../TestSDKContext.ts";
|
||||
|
||||
describe("<UserInfoHeaderVerificationView />", () => {
|
||||
const defaultRoomId = "!fkfk";
|
||||
@@ -25,6 +25,7 @@ describe("<UserInfoHeaderVerificationView />", () => {
|
||||
|
||||
let mockClient: MatrixClient;
|
||||
let mockCrypto: Mocked<CryptoApi>;
|
||||
let sdkContext: TestSDKContext;
|
||||
|
||||
beforeEach(() => {
|
||||
mockCrypto = mocked({
|
||||
@@ -42,6 +43,8 @@ describe("<UserInfoHeaderVerificationView />", () => {
|
||||
} as unknown as CryptoApi);
|
||||
|
||||
mockClient = createTestClient();
|
||||
sdkContext = new TestSDKContext();
|
||||
sdkContext._client = mockClient;
|
||||
jest.spyOn(mockClient, "doesServerSupportUnstableFeature").mockResolvedValue(true);
|
||||
jest.spyOn(mockClient.secretStorage, "hasKey").mockResolvedValue(true);
|
||||
jest.spyOn(mockClient, "getCrypto").mockReturnValue(mockCrypto);
|
||||
@@ -62,13 +65,11 @@ describe("<UserInfoHeaderVerificationView />", () => {
|
||||
|
||||
mockCrypto.getUserDeviceInfo.mockResolvedValue(userDeviceMap);
|
||||
jest.spyOn(mockClient, "doesServerSupportUnstableFeature").mockResolvedValue(true);
|
||||
const Wrapper = (wrapperProps = {}) => {
|
||||
return <MatrixClientContext.Provider value={mockClient} {...wrapperProps} />;
|
||||
};
|
||||
|
||||
return render(<UserInfoHeaderVerificationView member={defaultMember} devices={[device1]} />, {
|
||||
wrapper: Wrapper,
|
||||
});
|
||||
return render(
|
||||
<UserInfoHeaderVerificationView member={defaultMember} devices={[device1]} />,
|
||||
clientAndSDKContextRenderOptions(mockClient, sdkContext),
|
||||
);
|
||||
};
|
||||
|
||||
it("renders verified badge when user is verified", async () => {
|
||||
|
||||
+6
-9
@@ -13,10 +13,10 @@ import { fireEvent, render, screen } from "jest-matrix-react";
|
||||
import React from "react";
|
||||
|
||||
import { MatrixClientPeg } from "../../../../../../src/MatrixClientPeg";
|
||||
import MatrixClientContext from "../../../../../../src/contexts/MatrixClientContext";
|
||||
import { UserInfoHeaderView } from "../../../../../../src/components/views/right_panel/user_info/UserInfoHeaderView";
|
||||
import { createTestClient } from "../../../../../test-utils";
|
||||
import { clientAndSDKContextRenderOptions, createTestClient } from "../../../../../test-utils";
|
||||
import { useUserfoHeaderViewModel } from "../../../../../../src/components/viewmodels/right_panel/user_info/UserInfoHeaderViewModel";
|
||||
import { TestSDKContext } from "../../../../TestSDKContext.ts";
|
||||
|
||||
// Mock the viewmodel hooks
|
||||
jest.mock("../../../../../../src/components/viewmodels/right_panel/user_info/UserInfoHeaderViewModel", () => ({
|
||||
@@ -45,6 +45,7 @@ describe("<UserInfoHeaderView />", () => {
|
||||
|
||||
let mockClient: MatrixClient;
|
||||
let mockCrypto: Mocked<CryptoApi>;
|
||||
let sdkContext: TestSDKContext;
|
||||
|
||||
beforeEach(() => {
|
||||
mockCrypto = mocked({
|
||||
@@ -63,6 +64,8 @@ describe("<UserInfoHeaderView />", () => {
|
||||
|
||||
mockClient = createTestClient();
|
||||
mockClient.doesServerSupportExtendedProfiles = () => Promise.resolve(false);
|
||||
sdkContext = new TestSDKContext();
|
||||
sdkContext._client = mockClient;
|
||||
|
||||
jest.spyOn(mockClient, "doesServerSupportUnstableFeature").mockResolvedValue(true);
|
||||
jest.spyOn(mockClient.secretStorage, "hasKey").mockResolvedValue(true);
|
||||
@@ -90,10 +93,6 @@ describe("<UserInfoHeaderView />", () => {
|
||||
|
||||
mockCrypto.getUserDeviceInfo.mockResolvedValue(userDeviceMap);
|
||||
|
||||
const Wrapper = (wrapperProps = {}) => {
|
||||
return <MatrixClientContext.Provider value={mockClient} {...wrapperProps} />;
|
||||
};
|
||||
|
||||
return render(
|
||||
<UserInfoHeaderView
|
||||
{...defaultProps}
|
||||
@@ -101,9 +100,7 @@ describe("<UserInfoHeaderView />", () => {
|
||||
devices={[device1]}
|
||||
hideVerificationSection={props.hideVerificationSection}
|
||||
/>,
|
||||
{
|
||||
wrapper: Wrapper,
|
||||
},
|
||||
clientAndSDKContextRenderOptions(mockClient, sdkContext),
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -10,7 +10,9 @@ import React from "react";
|
||||
import { MatrixEvent, Room } from "matrix-js-sdk/src/matrix";
|
||||
|
||||
import RoomInfoLine from "../../../../../src/components/views/rooms/RoomInfoLine.tsx";
|
||||
import { stubClient } from "../../../../test-utils";
|
||||
import { stubClient, TestSDKContext, withContexts } from "../../../../test-utils";
|
||||
import { fireEvent } from "@testing-library/dom";
|
||||
import { RightPanelPhases } from "../../../../../src/stores/right-panel/RightPanelStorePhases.ts";
|
||||
|
||||
describe("RoomInfoLine", () => {
|
||||
it("renders for public room", () => {
|
||||
@@ -33,4 +35,29 @@ describe("RoomInfoLine", () => {
|
||||
expect(getByText("Public room")).toBeVisible();
|
||||
expect(asFragment()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("should render members button which opens right panel", async () => {
|
||||
const sdkContext = new TestSDKContext();
|
||||
sdkContext._client = stubClient();
|
||||
const room = new Room("!roomId", sdkContext.client!, sdkContext.client!.getUserId()!);
|
||||
room.currentState.setStateEvents([
|
||||
new MatrixEvent({
|
||||
sender: sdkContext.client!.getUserId()!,
|
||||
room_id: room.roomId,
|
||||
state_key: "",
|
||||
event_id: "$eventId",
|
||||
type: "m.room.join_rules",
|
||||
content: {
|
||||
join_rule: "public",
|
||||
},
|
||||
}),
|
||||
]);
|
||||
jest.spyOn(room, "getJoinedMemberCount").mockReturnValue(50);
|
||||
|
||||
jest.spyOn(sdkContext.rightPanelStore, "setCard");
|
||||
|
||||
const { findByText } = render(<RoomInfoLine room={room} />, withContexts({ sdkContext }));
|
||||
fireEvent.click(await findByText("50 members"));
|
||||
expect(sdkContext.rightPanelStore.setCard).toHaveBeenCalledWith({ phase: RightPanelPhases.MemberList });
|
||||
});
|
||||
});
|
||||
|
||||
@@ -11,7 +11,12 @@ import { render, screen } from "jest-matrix-react";
|
||||
import { EventType, type IEvent, MatrixEvent, Room, RoomMember } from "matrix-js-sdk/src/matrix";
|
||||
|
||||
import ThirdPartyMemberInfo from "../../../../../src/components/views/rooms/ThirdPartyMemberInfo";
|
||||
import { getMockClientWithEventEmitter, mockClientMethodsUser } from "../../../../test-utils";
|
||||
import {
|
||||
clientAndSDKContextRenderOptions,
|
||||
getMockClientWithEventEmitter,
|
||||
mockClientMethodsUser,
|
||||
} from "../../../../test-utils";
|
||||
import { SDKContextClass } from "../../../../../src/contexts/SDKContextClass.ts";
|
||||
|
||||
describe("<ThirdPartyMemberInfo />", () => {
|
||||
const userId = "@alice:server.org";
|
||||
@@ -37,7 +42,11 @@ describe("<ThirdPartyMemberInfo />", () => {
|
||||
});
|
||||
const defaultEvent = makeInviteEvent();
|
||||
|
||||
const getComponent = (event: MatrixEvent = defaultEvent) => render(<ThirdPartyMemberInfo event={event} />);
|
||||
const getComponent = (event: MatrixEvent = defaultEvent) =>
|
||||
render(
|
||||
<ThirdPartyMemberInfo event={event} />,
|
||||
clientAndSDKContextRenderOptions(mockClient, SDKContextClass.instance),
|
||||
);
|
||||
const room = new Room(roomId, mockClient, userId);
|
||||
const aliceMember = new RoomMember(roomId, userId);
|
||||
aliceMember.name = "Alice DisplayName";
|
||||
|
||||
+19
-11
@@ -14,26 +14,32 @@ import { type VerificationRequest, VerificationRequestEvent } from "matrix-js-sd
|
||||
|
||||
import VerificationRequestToast from "../../../../../src/components/views/toasts/VerificationRequestToast";
|
||||
import {
|
||||
clientAndSDKContextRenderOptions,
|
||||
flushPromises,
|
||||
getMockClientWithEventEmitter,
|
||||
mockClientMethodsCrypto,
|
||||
mockClientMethodsUser,
|
||||
} from "../../../../test-utils";
|
||||
import ToastStore from "../../../../../src/stores/ToastStore";
|
||||
|
||||
function renderComponent(
|
||||
props: Partial<ComponentProps<typeof VerificationRequestToast>> & { request: VerificationRequest },
|
||||
): RenderResult {
|
||||
const propsWithDefaults = {
|
||||
toastKey: "test",
|
||||
...props,
|
||||
};
|
||||
|
||||
return render(<VerificationRequestToast {...propsWithDefaults} />);
|
||||
}
|
||||
import { TestSDKContext } from "../../../TestSDKContext.ts";
|
||||
|
||||
describe("VerificationRequestToast", () => {
|
||||
let client: Mocked<MatrixClient>;
|
||||
let sdkContext: TestSDKContext;
|
||||
|
||||
function renderComponent(
|
||||
props: Partial<ComponentProps<typeof VerificationRequestToast>> & { request: VerificationRequest },
|
||||
): RenderResult {
|
||||
const propsWithDefaults = {
|
||||
toastKey: "test",
|
||||
...props,
|
||||
};
|
||||
|
||||
return render(
|
||||
<VerificationRequestToast {...propsWithDefaults} />,
|
||||
clientAndSDKContextRenderOptions(client, sdkContext),
|
||||
);
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
client = getMockClientWithEventEmitter({
|
||||
@@ -41,6 +47,8 @@ describe("VerificationRequestToast", () => {
|
||||
...mockClientMethodsCrypto(),
|
||||
getDevice: jest.fn(),
|
||||
});
|
||||
sdkContext = new TestSDKContext();
|
||||
sdkContext._client = client;
|
||||
});
|
||||
|
||||
it("should render a self-verification", async () => {
|
||||
|
||||
@@ -27,8 +27,6 @@ describe("LegacyCallView", () => {
|
||||
document.fullscreenElement = element;
|
||||
document.exitFullscreen = jest.fn();
|
||||
|
||||
stubClient();
|
||||
|
||||
const call = {
|
||||
on: jest.fn(),
|
||||
removeListener: jest.fn(),
|
||||
@@ -50,8 +48,7 @@ describe("LegacyCallView", () => {
|
||||
});
|
||||
|
||||
it("should show/hide the sidebar based on the sidebarShown prop", async () => {
|
||||
stubClient();
|
||||
|
||||
const cli = stubClient();
|
||||
const call = {
|
||||
roomId: "test-room",
|
||||
on: jest.fn(),
|
||||
@@ -93,8 +90,7 @@ describe("LegacyCallView", () => {
|
||||
});
|
||||
|
||||
it("should not show the sidebar button in picture-in-picture mode", async () => {
|
||||
stubClient();
|
||||
|
||||
const cli = stubClient();
|
||||
const call = {
|
||||
on: jest.fn(),
|
||||
removeListener: jest.fn(),
|
||||
|
||||
@@ -16,7 +16,6 @@ import { clientAndSDKContextRenderOptions, mkStubRoom, stubClient } from "../../
|
||||
import DMRoomMap from "../../../../../src/utils/DMRoomMap";
|
||||
import { MatrixClientPeg } from "../../../../../src/MatrixClientPeg";
|
||||
import LegacyCallHandler from "../../../../../src/LegacyCallHandler";
|
||||
import { SDKContext } from "../../../../../src/contexts/SDKContext";
|
||||
import { TestSDKContext } from "../../../TestSDKContext.ts";
|
||||
|
||||
jest.mock("../../../../../src/components/views/voip/LegacyCallView", () => jest.fn(() => "LegacyCallView"));
|
||||
@@ -93,9 +92,10 @@ describe("LegacyCallViewForRoom", () => {
|
||||
jest.spyOn(sdkContext.resizeNotifier, "stopResizing");
|
||||
jest.spyOn(sdkContext.resizeNotifier, "notifyTimelineHeightChanged");
|
||||
|
||||
const { container } = render(<LegacyCallViewForRoom roomId={call.roomId} />, {
|
||||
wrapper: ({ children }) => <SDKContext.Provider value={sdkContext}>{children}</SDKContext.Provider>,
|
||||
});
|
||||
const { container } = render(
|
||||
<LegacyCallViewForRoom roomId={call.roomId} />,
|
||||
clientAndSDKContextRenderOptions(sdkContext.client!, sdkContext),
|
||||
);
|
||||
|
||||
const resizer = container.querySelector(".mx_LegacyCallViewForRoom_ResizeHandle");
|
||||
await waitFor(() => {
|
||||
|
||||
@@ -18,7 +18,7 @@ import {
|
||||
mockClientMethodsServer,
|
||||
mockClientMethodsUser,
|
||||
} from "../../test-utils";
|
||||
import { SDKContextClass } from "../../../src/contexts/SDKContextClass.ts";
|
||||
import { TestSDKContext } from "../TestSDKContext.ts";
|
||||
|
||||
describe("<IncomingLegacyCallToast />", () => {
|
||||
const userId = "@alice:server.org";
|
||||
@@ -33,6 +33,8 @@ describe("<IncomingLegacyCallToast />", () => {
|
||||
...mockClientMethodsServer(),
|
||||
getRoom: jest.fn(),
|
||||
});
|
||||
const sdkContext = new TestSDKContext();
|
||||
sdkContext._client = mockClient;
|
||||
const mockRoom = new Room("!room:server.org", mockClient, userId);
|
||||
mockClient.deviceId = deviceId;
|
||||
|
||||
@@ -46,24 +48,16 @@ describe("<IncomingLegacyCallToast />", () => {
|
||||
jest.clearAllMocks();
|
||||
mockClient.getAccountData.mockReturnValue(undefined);
|
||||
mockClient.getRoom.mockReturnValue(mockRoom);
|
||||
// @ts-ignore
|
||||
SDKContextClass.instance._client = mockClient;
|
||||
});
|
||||
|
||||
it("renders when silence button when call is not silenced", () => {
|
||||
const { getByLabelText } = render(
|
||||
getComponent(),
|
||||
clientAndSDKContextRenderOptions(mockClient, SDKContextClass.instance),
|
||||
);
|
||||
const { getByLabelText } = render(getComponent(), clientAndSDKContextRenderOptions(mockClient, sdkContext));
|
||||
expect(getByLabelText("Silence call")).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("renders sound on button when call is silenced", () => {
|
||||
SDKContextClass.instance.legacyCallHandler.silenceCall(call.callId);
|
||||
const { getByLabelText } = render(
|
||||
getComponent(),
|
||||
clientAndSDKContextRenderOptions(mockClient, SDKContextClass.instance),
|
||||
);
|
||||
sdkContext.legacyCallHandler.silenceCall(call.callId);
|
||||
const { getByLabelText } = render(getComponent(), clientAndSDKContextRenderOptions(mockClient, sdkContext));
|
||||
expect(getByLabelText("Sound on")).toMatchSnapshot();
|
||||
});
|
||||
|
||||
@@ -79,10 +73,7 @@ describe("<IncomingLegacyCallToast />", () => {
|
||||
});
|
||||
}
|
||||
});
|
||||
const { getByLabelText } = render(
|
||||
getComponent(),
|
||||
clientAndSDKContextRenderOptions(mockClient, SDKContextClass.instance),
|
||||
);
|
||||
const { getByLabelText } = render(getComponent(), clientAndSDKContextRenderOptions(mockClient, sdkContext));
|
||||
expect(getByLabelText("Notifications silenced")).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user