/* Copyright 2024 New Vector Ltd. Copyright 2024 The Matrix.org Foundation C.I.C. SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only Please see LICENSE files in the repository root for full details. */ import React, { useEffect, useMemo, useState } from "react"; import { Room } from "matrix-js-sdk/src/matrix"; import classNames from "classnames"; import { Button, Link, Separator, Text } from "@vector-im/compound-web"; import PlusIcon from "@vector-im/compound-design-tokens/assets/web/icons/plus"; import ExtensionsIcon from "@vector-im/compound-design-tokens/assets/web/icons/extensions"; import BaseCard from "./BaseCard"; import WidgetUtils, { useWidgets } from "../../../utils/WidgetUtils"; import { _t } from "../../../languageHandler"; import { ChevronFace, ContextMenuTooltipButton, useContextMenu } from "../../structures/ContextMenu"; import { WidgetContextMenu } from "../context_menus/WidgetContextMenu"; import UIStore from "../../../stores/UIStore"; import RightPanelStore from "../../../stores/right-panel/RightPanelStore"; import { IApp } from "../../../stores/WidgetStore"; import { RightPanelPhases } from "../../../stores/right-panel/RightPanelStorePhases"; import { Container, MAX_PINNED, WidgetLayoutStore } from "../../../stores/widgets/WidgetLayoutStore"; import AccessibleButton from "../elements/AccessibleButton"; import WidgetAvatar from "../avatars/WidgetAvatar"; import { IntegrationManagers } from "../../../integrations/IntegrationManagers"; import EmptyState from "./EmptyState"; interface Props { room: Room; onClose(): void; } interface IAppRowProps { app: IApp; room: Room; } const AppRow: React.FC = ({ app, room }) => { const name = WidgetUtils.getWidgetName(app); const [canModifyWidget, setCanModifyWidget] = useState(); useEffect(() => { setCanModifyWidget(WidgetUtils.canUserModifyWidgets(room.client, room.roomId)); }, [room.client, room.roomId]); const onOpenWidgetClick = (): void => { RightPanelStore.instance.pushCard({ phase: RightPanelPhases.Widget, state: { widgetId: app.id }, }); }; const isPinned = WidgetLayoutStore.instance.isInContainer(room, app, Container.Top); const togglePin = isPinned ? () => { WidgetLayoutStore.instance.moveToContainer(room, app, Container.Right); } : () => { WidgetLayoutStore.instance.moveToContainer(room, app, Container.Top); }; const [menuDisplayed, handle, openMenu, closeMenu] = useContextMenu(); let contextMenu; if (menuDisplayed) { const rect = handle.current?.getBoundingClientRect(); const rightMargin = rect?.right ?? 0; const topMargin = rect?.top ?? 0; contextMenu = ( ); } const cannotPin = !isPinned && !WidgetLayoutStore.instance.canAddToContainer(room, Container.Top); let pinTitle: string; if (cannotPin) { pinTitle = _t("right_panel|pinned_messages|limits", { count: MAX_PINNED }); } else { pinTitle = isPinned ? _t("action|unpin") : _t("action|pin"); } const isMaximised = WidgetLayoutStore.instance.isInContainer(room, app, Container.Center); let openTitle = ""; if (isPinned) { openTitle = _t("widget|unpin_to_view_right_panel"); } else if (isMaximised) { openTitle = _t("widget|close_to_view_right_panel"); } const classes = classNames("mx_BaseCard_Button mx_ExtensionsCard_Button", { mx_ExtensionsCard_Button_pinned: isPinned, }); return (
{name} {canModifyWidget && ( )} {contextMenu}
); }; /** * A right panel card displaying a list of widgets in the room and allowing the user to manage them. * @param room the room to manage widgets for * @param onClose callback when the card is closed */ const ExtensionsCard: React.FC = ({ room, onClose }) => { const apps = useWidgets(room); // Filter out virtual widgets const realApps = useMemo(() => apps.filter((app) => app.eventId !== undefined), [apps]); const onManageIntegrations = (): void => { const managers = IntegrationManagers.sharedInstance(); if (!managers.hasManager()) { managers.openNoManagerDialog(); } else { // noinspection JSIgnoredPromiseFromCall managers.getPrimaryManager()?.open(room); } }; let body: JSX.Element; if (realApps.length < 1) { body = ( ); } else { let copyLayoutBtn: JSX.Element | null = null; if (WidgetLayoutStore.instance.canCopyLayoutToRoom(room)) { copyLayoutBtn = ( WidgetLayoutStore.instance.copyLayoutToRoom(room)}> {_t("widget|set_room_layout")} ); } body = ( <> {realApps.map((app) => ( ))} {copyLayoutBtn} ); } return ( {body} ); }; export default ExtensionsCard;