Apply prettier formatting
This commit is contained in:
parent
1cac306093
commit
526645c791
1576 changed files with 65385 additions and 62478 deletions
|
@ -34,12 +34,14 @@ interface IDeviceContextMenuDeviceProps {
|
|||
}
|
||||
|
||||
const DeviceContextMenuDevice: React.FC<IDeviceContextMenuDeviceProps> = ({ label, selected, onClick }) => {
|
||||
return <IconizedContextMenuRadio
|
||||
iconClassName="mx_DeviceContextMenu_device_icon"
|
||||
label={label}
|
||||
active={selected}
|
||||
onClick={onClick}
|
||||
/>;
|
||||
return (
|
||||
<IconizedContextMenuRadio
|
||||
iconClassName="mx_DeviceContextMenu_device_icon"
|
||||
label={label}
|
||||
active={selected}
|
||||
onClick={onClick}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
interface IDeviceContextMenuSectionProps {
|
||||
|
@ -62,16 +64,20 @@ const DeviceContextMenuSection: React.FC<IDeviceContextMenuSectionProps> = ({ de
|
|||
setSelectedDevice(deviceId);
|
||||
};
|
||||
|
||||
return <IconizedContextMenuOptionList label={_t(SECTION_NAMES[deviceKind])}>
|
||||
{ devices.map(({ label, deviceId }) => {
|
||||
return <DeviceContextMenuDevice
|
||||
key={deviceId}
|
||||
label={label}
|
||||
selected={selectedDevice === deviceId}
|
||||
onClick={() => onDeviceClick(deviceId)}
|
||||
/>;
|
||||
}) }
|
||||
</IconizedContextMenuOptionList>;
|
||||
return (
|
||||
<IconizedContextMenuOptionList label={_t(SECTION_NAMES[deviceKind])}>
|
||||
{devices.map(({ label, deviceId }) => {
|
||||
return (
|
||||
<DeviceContextMenuDevice
|
||||
key={deviceId}
|
||||
label={label}
|
||||
selected={selectedDevice === deviceId}
|
||||
onClick={() => onDeviceClick(deviceId)}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</IconizedContextMenuOptionList>
|
||||
);
|
||||
};
|
||||
|
||||
interface IProps extends IContextMenuProps {
|
||||
|
@ -79,11 +85,13 @@ interface IProps extends IContextMenuProps {
|
|||
}
|
||||
|
||||
const DeviceContextMenu: React.FC<IProps> = ({ deviceKinds, ...props }) => {
|
||||
return <IconizedContextMenu compact className="mx_DeviceContextMenu" {...props}>
|
||||
{ deviceKinds.map((kind) => {
|
||||
return <DeviceContextMenuSection key={kind} deviceKind={kind as MediaDeviceKindEnum} />;
|
||||
}) }
|
||||
</IconizedContextMenu>;
|
||||
return (
|
||||
<IconizedContextMenu compact className="mx_DeviceContextMenu" {...props}>
|
||||
{deviceKinds.map((kind) => {
|
||||
return <DeviceContextMenuSection key={kind} deviceKind={kind as MediaDeviceKindEnum} />;
|
||||
})}
|
||||
</IconizedContextMenu>
|
||||
);
|
||||
};
|
||||
|
||||
export default DeviceContextMenu;
|
||||
|
|
|
@ -16,12 +16,12 @@ limitations under the License.
|
|||
|
||||
import * as React from "react";
|
||||
import { createRef } from "react";
|
||||
import { MatrixCall } from 'matrix-js-sdk/src/webrtc/call';
|
||||
import { MatrixCall } from "matrix-js-sdk/src/webrtc/call";
|
||||
|
||||
import AccessibleButton, { ButtonEvent } from "../elements/AccessibleButton";
|
||||
import ContextMenu, { IProps as IContextMenuProps } from '../../structures/ContextMenu';
|
||||
import ContextMenu, { IProps as IContextMenuProps } from "../../structures/ContextMenu";
|
||||
import Field from "../elements/Field";
|
||||
import DialPad from '../voip/DialPad';
|
||||
import DialPad from "../voip/DialPad";
|
||||
|
||||
interface IProps extends IContextMenuProps {
|
||||
call: MatrixCall;
|
||||
|
@ -38,7 +38,7 @@ export default class DialpadContextMenu extends React.Component<IProps, IState>
|
|||
super(props);
|
||||
|
||||
this.state = {
|
||||
value: '',
|
||||
value: "",
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -70,25 +70,27 @@ export default class DialpadContextMenu extends React.Component<IProps, IState>
|
|||
};
|
||||
|
||||
render() {
|
||||
return <ContextMenu {...this.props}>
|
||||
<div className="mx_DialPadContextMenuWrapper">
|
||||
<div>
|
||||
<AccessibleButton className="mx_DialPadContextMenu_cancel" onClick={this.onCancelClick} />
|
||||
return (
|
||||
<ContextMenu {...this.props}>
|
||||
<div className="mx_DialPadContextMenuWrapper">
|
||||
<div>
|
||||
<AccessibleButton className="mx_DialPadContextMenu_cancel" onClick={this.onCancelClick} />
|
||||
</div>
|
||||
<div className="mx_DialPadContextMenu_header">
|
||||
<Field
|
||||
ref={this.numberEntryFieldRef}
|
||||
className="mx_DialPadContextMenu_dialled"
|
||||
value={this.state.value}
|
||||
autoFocus={true}
|
||||
onKeyDown={this.onKeyDown}
|
||||
onChange={this.onChange}
|
||||
/>
|
||||
</div>
|
||||
<div className="mx_DialPadContextMenu_dialPad">
|
||||
<DialPad onDigitPress={this.onDigitPress} hasDial={false} />
|
||||
</div>
|
||||
</div>
|
||||
<div className="mx_DialPadContextMenu_header">
|
||||
<Field
|
||||
ref={this.numberEntryFieldRef}
|
||||
className="mx_DialPadContextMenu_dialled"
|
||||
value={this.state.value}
|
||||
autoFocus={true}
|
||||
onKeyDown={this.onKeyDown}
|
||||
onChange={this.onChange}
|
||||
/>
|
||||
</div>
|
||||
<div className="mx_DialPadContextMenu_dialPad">
|
||||
<DialPad onDigitPress={this.onDigitPress} hasDial={false} />
|
||||
</div>
|
||||
</div>
|
||||
</ContextMenu>;
|
||||
</ContextMenu>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import React from "react";
|
||||
|
||||
interface IProps {
|
||||
element: React.ReactNode;
|
||||
|
@ -48,6 +48,6 @@ export default class GenericElementContextMenu extends React.Component<IProps> {
|
|||
};
|
||||
|
||||
public render(): JSX.Element {
|
||||
return <div>{ this.props.element }</div>;
|
||||
return <div>{this.props.element}</div>;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import React from "react";
|
||||
|
||||
interface IProps {
|
||||
message: string;
|
||||
|
@ -22,8 +22,10 @@ interface IProps {
|
|||
|
||||
export default class GenericTextContextMenu extends React.Component<IProps> {
|
||||
public render(): JSX.Element {
|
||||
return <div className="mx_Tooltip mx_Tooltip_visible" style={{ display: "block" }}>
|
||||
{ this.props.message }
|
||||
</div>;
|
||||
return (
|
||||
<div className="mx_Tooltip mx_Tooltip_visible" style={{ display: "block" }}>
|
||||
{this.props.message}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,7 +21,8 @@ import ContextMenu, {
|
|||
ChevronFace,
|
||||
IProps as IContextMenuProps,
|
||||
MenuItem,
|
||||
MenuItemCheckbox, MenuItemRadio,
|
||||
MenuItemCheckbox,
|
||||
MenuItemRadio,
|
||||
} from "../../structures/ContextMenu";
|
||||
import { _t } from "../../../languageHandler";
|
||||
|
||||
|
@ -58,19 +59,21 @@ export const IconizedContextMenuRadio: React.FC<IRadioProps> = ({
|
|||
className,
|
||||
...props
|
||||
}) => {
|
||||
return <MenuItemRadio
|
||||
{...props}
|
||||
className={classNames(className, {
|
||||
mx_IconizedContextMenu_item: true,
|
||||
mx_IconizedContextMenu_active: active,
|
||||
})}
|
||||
active={active}
|
||||
label={label}
|
||||
>
|
||||
{ iconClassName && <span className={classNames("mx_IconizedContextMenu_icon", iconClassName)} /> }
|
||||
<span className="mx_IconizedContextMenu_label">{ label }</span>
|
||||
{ active && <span className="mx_IconizedContextMenu_icon mx_IconizedContextMenu_checked" /> }
|
||||
</MenuItemRadio>;
|
||||
return (
|
||||
<MenuItemRadio
|
||||
{...props}
|
||||
className={classNames(className, {
|
||||
mx_IconizedContextMenu_item: true,
|
||||
mx_IconizedContextMenu_active: active,
|
||||
})}
|
||||
active={active}
|
||||
label={label}
|
||||
>
|
||||
{iconClassName && <span className={classNames("mx_IconizedContextMenu_icon", iconClassName)} />}
|
||||
<span className="mx_IconizedContextMenu_label">{label}</span>
|
||||
{active && <span className="mx_IconizedContextMenu_icon mx_IconizedContextMenu_checked" />}
|
||||
</MenuItemRadio>
|
||||
);
|
||||
};
|
||||
|
||||
export const IconizedContextMenuCheckbox: React.FC<ICheckboxProps> = ({
|
||||
|
@ -83,29 +86,33 @@ export const IconizedContextMenuCheckbox: React.FC<ICheckboxProps> = ({
|
|||
}) => {
|
||||
let marker: JSX.Element;
|
||||
if (words) {
|
||||
marker = <span className="mx_IconizedContextMenu_activeText">
|
||||
{ active ? _t("On") : _t("Off") }
|
||||
</span>;
|
||||
marker = <span className="mx_IconizedContextMenu_activeText">{active ? _t("On") : _t("Off")}</span>;
|
||||
} else {
|
||||
marker = <span className={classNames("mx_IconizedContextMenu_icon", {
|
||||
mx_IconizedContextMenu_checked: active,
|
||||
mx_IconizedContextMenu_unchecked: !active,
|
||||
})} />;
|
||||
marker = (
|
||||
<span
|
||||
className={classNames("mx_IconizedContextMenu_icon", {
|
||||
mx_IconizedContextMenu_checked: active,
|
||||
mx_IconizedContextMenu_unchecked: !active,
|
||||
})}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return <MenuItemCheckbox
|
||||
{...props}
|
||||
className={classNames(className, {
|
||||
mx_IconizedContextMenu_item: true,
|
||||
mx_IconizedContextMenu_active: active,
|
||||
})}
|
||||
active={active}
|
||||
label={label}
|
||||
>
|
||||
<span className={classNames("mx_IconizedContextMenu_icon", iconClassName)} />
|
||||
<span className="mx_IconizedContextMenu_label">{ label }</span>
|
||||
{ marker }
|
||||
</MenuItemCheckbox>;
|
||||
return (
|
||||
<MenuItemCheckbox
|
||||
{...props}
|
||||
className={classNames(className, {
|
||||
mx_IconizedContextMenu_item: true,
|
||||
mx_IconizedContextMenu_active: active,
|
||||
})}
|
||||
active={active}
|
||||
label={label}
|
||||
>
|
||||
<span className={classNames("mx_IconizedContextMenu_icon", iconClassName)} />
|
||||
<span className="mx_IconizedContextMenu_label">{label}</span>
|
||||
{marker}
|
||||
</MenuItemCheckbox>
|
||||
);
|
||||
};
|
||||
|
||||
export const IconizedContextMenuOption: React.FC<IOptionProps> = ({
|
||||
|
@ -116,18 +123,20 @@ export const IconizedContextMenuOption: React.FC<IOptionProps> = ({
|
|||
isDestructive,
|
||||
...props
|
||||
}) => {
|
||||
return <MenuItem
|
||||
{...props}
|
||||
className={classNames(className, {
|
||||
mx_IconizedContextMenu_item: true,
|
||||
mx_IconizedContextMenu_itemDestructive: isDestructive,
|
||||
})}
|
||||
label={label}
|
||||
>
|
||||
{ iconClassName && <span className={classNames("mx_IconizedContextMenu_icon", iconClassName)} /> }
|
||||
<span className="mx_IconizedContextMenu_label">{ label }</span>
|
||||
{ children }
|
||||
</MenuItem>;
|
||||
return (
|
||||
<MenuItem
|
||||
{...props}
|
||||
className={classNames(className, {
|
||||
mx_IconizedContextMenu_item: true,
|
||||
mx_IconizedContextMenu_itemDestructive: isDestructive,
|
||||
})}
|
||||
label={label}
|
||||
>
|
||||
{iconClassName && <span className={classNames("mx_IconizedContextMenu_icon", iconClassName)} />}
|
||||
<span className="mx_IconizedContextMenu_label">{label}</span>
|
||||
{children}
|
||||
</MenuItem>
|
||||
);
|
||||
};
|
||||
|
||||
export const IconizedContextMenuOptionList: React.FC<IOptionListProps> = ({
|
||||
|
@ -142,10 +151,16 @@ export const IconizedContextMenuOptionList: React.FC<IOptionListProps> = ({
|
|||
mx_IconizedContextMenu_optionList_red: red,
|
||||
});
|
||||
|
||||
return <div className={classes}>
|
||||
{ label && <div><span className="mx_IconizedContextMenu_optionList_label">{ label }</span></div> }
|
||||
{ children }
|
||||
</div>;
|
||||
return (
|
||||
<div className={classes}>
|
||||
{label && (
|
||||
<div>
|
||||
<span className="mx_IconizedContextMenu_optionList_label">{label}</span>
|
||||
</div>
|
||||
)}
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const IconizedContextMenu: React.FC<IProps> = ({ className, children, compact, ...props }) => {
|
||||
|
@ -153,12 +168,11 @@ const IconizedContextMenu: React.FC<IProps> = ({ className, children, compact, .
|
|||
mx_IconizedContextMenu_compact: compact,
|
||||
});
|
||||
|
||||
return <ContextMenu chevronFace={ChevronFace.None} {...props}>
|
||||
<div className={classes}>
|
||||
{ children }
|
||||
</div>
|
||||
</ContextMenu>;
|
||||
return (
|
||||
<ContextMenu chevronFace={ChevronFace.None} {...props}>
|
||||
<div className={classes}>{children}</div>
|
||||
</ContextMenu>
|
||||
);
|
||||
};
|
||||
|
||||
export default IconizedContextMenu;
|
||||
|
||||
|
|
|
@ -14,12 +14,12 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import React from "react";
|
||||
|
||||
import { Icon as ContextMenuIcon } from '../../../../res/img/element-icons/context-menu.svg';
|
||||
import { ChevronFace, ContextMenuButton, useContextMenu } from '../../structures/ContextMenu';
|
||||
import AccessibleButton from '../elements/AccessibleButton';
|
||||
import IconizedContextMenu, { IconizedContextMenuOptionList } from './IconizedContextMenu';
|
||||
import { Icon as ContextMenuIcon } from "../../../../res/img/element-icons/context-menu.svg";
|
||||
import { ChevronFace, ContextMenuButton, useContextMenu } from "../../structures/ContextMenu";
|
||||
import AccessibleButton from "../elements/AccessibleButton";
|
||||
import IconizedContextMenu, { IconizedContextMenuOptionList } from "./IconizedContextMenu";
|
||||
|
||||
const contextMenuBelow = (elementRect: DOMRect) => {
|
||||
// align the context menu's icons with the icon which opened the context menu
|
||||
|
@ -34,33 +34,25 @@ interface KebabContextMenuProps extends Partial<React.ComponentProps<typeof Acce
|
|||
title: string;
|
||||
}
|
||||
|
||||
export const KebabContextMenu: React.FC<KebabContextMenuProps> = ({
|
||||
options,
|
||||
title,
|
||||
...props
|
||||
}) => {
|
||||
export const KebabContextMenu: React.FC<KebabContextMenuProps> = ({ options, title, ...props }) => {
|
||||
const [menuDisplayed, button, openMenu, closeMenu] = useContextMenu();
|
||||
|
||||
return <>
|
||||
<ContextMenuButton
|
||||
{...props}
|
||||
onClick={openMenu}
|
||||
title={title}
|
||||
isExpanded={menuDisplayed}
|
||||
inputRef={button}
|
||||
>
|
||||
<ContextMenuIcon className='mx_KebabContextMenu_icon' />
|
||||
</ContextMenuButton>
|
||||
{ menuDisplayed && (<IconizedContextMenu
|
||||
onFinished={closeMenu}
|
||||
compact
|
||||
rightAligned
|
||||
closeOnInteraction
|
||||
{...contextMenuBelow(button.current.getBoundingClientRect())}
|
||||
>
|
||||
<IconizedContextMenuOptionList>
|
||||
{ options }
|
||||
</IconizedContextMenuOptionList>
|
||||
</IconizedContextMenu>) }
|
||||
</>;
|
||||
return (
|
||||
<>
|
||||
<ContextMenuButton {...props} onClick={openMenu} title={title} isExpanded={menuDisplayed} inputRef={button}>
|
||||
<ContextMenuIcon className="mx_KebabContextMenu_icon" />
|
||||
</ContextMenuButton>
|
||||
{menuDisplayed && (
|
||||
<IconizedContextMenu
|
||||
onFinished={closeMenu}
|
||||
compact
|
||||
rightAligned
|
||||
closeOnInteraction
|
||||
{...contextMenuBelow(button.current.getBoundingClientRect())}
|
||||
>
|
||||
<IconizedContextMenuOptionList>{options}</IconizedContextMenuOptionList>
|
||||
</IconizedContextMenu>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -14,12 +14,12 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { MatrixCall } from 'matrix-js-sdk/src/webrtc/call';
|
||||
import React from "react";
|
||||
import { MatrixCall } from "matrix-js-sdk/src/webrtc/call";
|
||||
|
||||
import { _t } from '../../../languageHandler';
|
||||
import ContextMenu, { IProps as IContextMenuProps, MenuItem } from '../../structures/ContextMenu';
|
||||
import LegacyCallHandler from '../../../LegacyCallHandler';
|
||||
import { _t } from "../../../languageHandler";
|
||||
import ContextMenu, { IProps as IContextMenuProps, MenuItem } from "../../structures/ContextMenu";
|
||||
import LegacyCallHandler from "../../../LegacyCallHandler";
|
||||
|
||||
interface IProps extends IContextMenuProps {
|
||||
call: MatrixCall;
|
||||
|
@ -52,16 +52,20 @@ export default class LegacyCallContextMenu extends React.Component<IProps> {
|
|||
|
||||
let transferItem;
|
||||
if (this.props.call.opponentCanBeTransferred()) {
|
||||
transferItem = <MenuItem className="mx_LegacyCallContextMenu_item" onClick={this.onTransferClick}>
|
||||
{ _t("Transfer") }
|
||||
</MenuItem>;
|
||||
transferItem = (
|
||||
<MenuItem className="mx_LegacyCallContextMenu_item" onClick={this.onTransferClick}>
|
||||
{_t("Transfer")}
|
||||
</MenuItem>
|
||||
);
|
||||
}
|
||||
|
||||
return <ContextMenu {...this.props}>
|
||||
<MenuItem className="mx_LegacyCallContextMenu_item" onClick={handler}>
|
||||
{ holdUnholdCaption }
|
||||
</MenuItem>
|
||||
{ transferItem }
|
||||
</ContextMenu>;
|
||||
return (
|
||||
<ContextMenu {...this.props}>
|
||||
<MenuItem className="mx_LegacyCallContextMenu_item" onClick={handler}>
|
||||
{holdUnholdCaption}
|
||||
</MenuItem>
|
||||
{transferItem}
|
||||
</ContextMenu>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,49 +16,44 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
import React, { createRef, useContext } from 'react';
|
||||
import { EventStatus, MatrixEvent } from 'matrix-js-sdk/src/models/event';
|
||||
import React, { createRef, useContext } from "react";
|
||||
import { EventStatus, MatrixEvent } from "matrix-js-sdk/src/models/event";
|
||||
import { EventType, RelationType } from "matrix-js-sdk/src/@types/event";
|
||||
import { Relations } from 'matrix-js-sdk/src/models/relations';
|
||||
import { Relations } from "matrix-js-sdk/src/models/relations";
|
||||
import { RoomMemberEvent } from "matrix-js-sdk/src/models/room-member";
|
||||
import { M_POLL_START } from "matrix-events-sdk";
|
||||
import { Thread } from "matrix-js-sdk/src/models/thread";
|
||||
|
||||
import { MatrixClientPeg } from '../../../MatrixClientPeg';
|
||||
import dis from '../../../dispatcher/dispatcher';
|
||||
import { _t } from '../../../languageHandler';
|
||||
import Modal from '../../../Modal';
|
||||
import Resend from '../../../Resend';
|
||||
import SettingsStore from '../../../settings/SettingsStore';
|
||||
import { isUrlPermitted } from '../../../HtmlUtils';
|
||||
import {
|
||||
canEditContent,
|
||||
canPinEvent,
|
||||
editEvent,
|
||||
isContentActionable,
|
||||
} from '../../../utils/EventUtils';
|
||||
import IconizedContextMenu, { IconizedContextMenuOption, IconizedContextMenuOptionList } from './IconizedContextMenu';
|
||||
import { MatrixClientPeg } from "../../../MatrixClientPeg";
|
||||
import dis from "../../../dispatcher/dispatcher";
|
||||
import { _t } from "../../../languageHandler";
|
||||
import Modal from "../../../Modal";
|
||||
import Resend from "../../../Resend";
|
||||
import SettingsStore from "../../../settings/SettingsStore";
|
||||
import { isUrlPermitted } from "../../../HtmlUtils";
|
||||
import { canEditContent, canPinEvent, editEvent, isContentActionable } from "../../../utils/EventUtils";
|
||||
import IconizedContextMenu, { IconizedContextMenuOption, IconizedContextMenuOptionList } from "./IconizedContextMenu";
|
||||
import { ReadPinsEventId } from "../right_panel/types";
|
||||
import { Action } from "../../../dispatcher/actions";
|
||||
import { RoomPermalinkCreator } from '../../../utils/permalinks/Permalinks';
|
||||
import { ButtonEvent } from '../elements/AccessibleButton';
|
||||
import { copyPlaintext, getSelectedText } from '../../../utils/strings';
|
||||
import ContextMenu, { toRightOf, IPosition, ChevronFace } from '../../structures/ContextMenu';
|
||||
import ReactionPicker from '../emojipicker/ReactionPicker';
|
||||
import ViewSource from '../../structures/ViewSource';
|
||||
import { createRedactEventDialog } from '../dialogs/ConfirmRedactDialog';
|
||||
import ShareDialog from '../dialogs/ShareDialog';
|
||||
import RoomContext, { TimelineRenderingType } from '../../../contexts/RoomContext';
|
||||
import { RoomPermalinkCreator } from "../../../utils/permalinks/Permalinks";
|
||||
import { ButtonEvent } from "../elements/AccessibleButton";
|
||||
import { copyPlaintext, getSelectedText } from "../../../utils/strings";
|
||||
import ContextMenu, { toRightOf, IPosition, ChevronFace } from "../../structures/ContextMenu";
|
||||
import ReactionPicker from "../emojipicker/ReactionPicker";
|
||||
import ViewSource from "../../structures/ViewSource";
|
||||
import { createRedactEventDialog } from "../dialogs/ConfirmRedactDialog";
|
||||
import ShareDialog from "../dialogs/ShareDialog";
|
||||
import RoomContext, { TimelineRenderingType } from "../../../contexts/RoomContext";
|
||||
import { ComposerInsertPayload } from "../../../dispatcher/payloads/ComposerInsertPayload";
|
||||
import EndPollDialog from '../dialogs/EndPollDialog';
|
||||
import { isPollEnded } from '../messages/MPollBody';
|
||||
import EndPollDialog from "../dialogs/EndPollDialog";
|
||||
import { isPollEnded } from "../messages/MPollBody";
|
||||
import { ViewRoomPayload } from "../../../dispatcher/payloads/ViewRoomPayload";
|
||||
import { GetRelationsForEvent, IEventTileOps } from "../rooms/EventTile";
|
||||
import { OpenForwardDialogPayload } from "../../../dispatcher/payloads/OpenForwardDialogPayload";
|
||||
import { OpenReportEventDialogPayload } from "../../../dispatcher/payloads/OpenReportEventDialogPayload";
|
||||
import { createMapSiteLinkFromEvent } from '../../../utils/location';
|
||||
import { getForwardableEvent } from '../../../events/forward/getForwardableEvent';
|
||||
import { getShareableLocationEvent } from '../../../events/location/getShareableLocationEvent';
|
||||
import { createMapSiteLinkFromEvent } from "../../../utils/location";
|
||||
import { getForwardableEvent } from "../../../events/forward/getForwardableEvent";
|
||||
import { getShareableLocationEvent } from "../../../events/location/getShareableLocationEvent";
|
||||
import { ShowThreadPayload } from "../../../dispatcher/payloads/ShowThreadPayload";
|
||||
import { CardContext } from "../right_panel/context";
|
||||
import { UserTab } from "../dialogs/UserTab";
|
||||
|
@ -178,11 +173,13 @@ export default class MessageContextMenu extends React.Component<IProps, IState>
|
|||
// We explicitly decline to show the redact option on ACL events as it has a potential
|
||||
// to obliterate the room - https://github.com/matrix-org/synapse/issues/4042
|
||||
// Similarly for encryption events, since redacting them "breaks everything"
|
||||
const canRedact = room.currentState.maySendRedactionForEvent(this.props.mxEvent, cli.credentials.userId)
|
||||
&& this.props.mxEvent.getType() !== EventType.RoomServerAcl
|
||||
&& this.props.mxEvent.getType() !== EventType.RoomEncryption;
|
||||
const canRedact =
|
||||
room.currentState.maySendRedactionForEvent(this.props.mxEvent, cli.credentials.userId) &&
|
||||
this.props.mxEvent.getType() !== EventType.RoomServerAcl &&
|
||||
this.props.mxEvent.getType() !== EventType.RoomEncryption;
|
||||
|
||||
let canPin = room.currentState.mayClientSendStateEvent(EventType.RoomPinnedEvents, cli) &&
|
||||
let canPin =
|
||||
room.currentState.mayClientSendStateEvent(EventType.RoomPinnedEvents, cli) &&
|
||||
canPinEvent(this.props.mxEvent);
|
||||
|
||||
// HACK: Intentionally say we can't pin if the user doesn't want to use the functionality
|
||||
|
@ -193,7 +190,7 @@ export default class MessageContextMenu extends React.Component<IProps, IState>
|
|||
|
||||
private isPinned(): boolean {
|
||||
const room = MatrixClientPeg.get().getRoom(this.props.mxEvent.getRoomId());
|
||||
const pinnedEvent = room.currentState.getStateEvents(EventType.RoomPinnedEvents, '');
|
||||
const pinnedEvent = room.currentState.getStateEvents(EventType.RoomPinnedEvents, "");
|
||||
if (!pinnedEvent) return false;
|
||||
const content = pinnedEvent.getContent();
|
||||
return content.pinned && Array.isArray(content.pinned) && content.pinned.includes(this.props.mxEvent.getId());
|
||||
|
@ -232,9 +229,13 @@ export default class MessageContextMenu extends React.Component<IProps, IState>
|
|||
};
|
||||
|
||||
private onViewSourceClick = (): void => {
|
||||
Modal.createDialog(ViewSource, {
|
||||
mxEvent: this.props.mxEvent,
|
||||
}, 'mx_Dialog_viewsource');
|
||||
Modal.createDialog(
|
||||
ViewSource,
|
||||
{
|
||||
mxEvent: this.props.mxEvent,
|
||||
},
|
||||
"mx_Dialog_viewsource",
|
||||
);
|
||||
this.closeMenu();
|
||||
};
|
||||
|
||||
|
@ -268,10 +269,7 @@ export default class MessageContextMenu extends React.Component<IProps, IState>
|
|||
} else {
|
||||
pinnedIds.push(eventId);
|
||||
cli.setRoomAccountData(room.roomId, ReadPinsEventId, {
|
||||
event_ids: [
|
||||
...(room.getAccountData(ReadPinsEventId)?.getContent()?.event_ids || []),
|
||||
eventId,
|
||||
],
|
||||
event_ids: [...(room.getAccountData(ReadPinsEventId)?.getContent()?.event_ids || []), eventId],
|
||||
});
|
||||
}
|
||||
cli.sendStateEvent(this.props.mxEvent.getRoomId(), EventType.RoomPinnedEvents, { pinned: pinnedIds }, "");
|
||||
|
@ -328,7 +326,7 @@ export default class MessageContextMenu extends React.Component<IProps, IState>
|
|||
|
||||
private onReplyClick = (): void => {
|
||||
dis.dispatch({
|
||||
action: 'reply_to_event',
|
||||
action: "reply_to_event",
|
||||
event: this.props.mxEvent,
|
||||
context: this.context.timelineRenderingType,
|
||||
});
|
||||
|
@ -346,11 +344,15 @@ export default class MessageContextMenu extends React.Component<IProps, IState>
|
|||
|
||||
private onEndPollClick = (): void => {
|
||||
const matrixClient = MatrixClientPeg.get();
|
||||
Modal.createDialog(EndPollDialog, {
|
||||
matrixClient,
|
||||
event: this.props.mxEvent,
|
||||
getRelationsForEvent: this.props.getRelationsForEvent,
|
||||
}, 'mx_Dialog_endPoll');
|
||||
Modal.createDialog(
|
||||
EndPollDialog,
|
||||
{
|
||||
matrixClient,
|
||||
event: this.props.mxEvent,
|
||||
getRelationsForEvent: this.props.getRelationsForEvent,
|
||||
},
|
||||
"mx_Dialog_endPoll",
|
||||
);
|
||||
this.closeMenu();
|
||||
};
|
||||
|
||||
|
@ -358,14 +360,14 @@ export default class MessageContextMenu extends React.Component<IProps, IState>
|
|||
const cli = MatrixClientPeg.get();
|
||||
const room = cli.getRoom(this.props.mxEvent.getRoomId());
|
||||
const eventId = this.props.mxEvent.getId();
|
||||
return room.getPendingEvents().filter(e => {
|
||||
return room.getPendingEvents().filter((e) => {
|
||||
const relation = e.getRelation();
|
||||
return relation?.rel_type === RelationType.Annotation && relation.event_id === eventId && filter(e);
|
||||
});
|
||||
}
|
||||
|
||||
private getUnsentReactions(): MatrixEvent[] {
|
||||
return this.getReactions(e => e.status === EventStatus.NOT_SENT);
|
||||
return this.getReactions((e) => e.status === EventStatus.NOT_SENT);
|
||||
}
|
||||
|
||||
private viewInRoom = (): void => {
|
||||
|
@ -382,10 +384,7 @@ export default class MessageContextMenu extends React.Component<IProps, IState>
|
|||
public render(): JSX.Element {
|
||||
const cli = MatrixClientPeg.get();
|
||||
const me = cli.getUserId();
|
||||
const {
|
||||
mxEvent, rightClick, link, eventTileOps, reactions, collapseReplyChain,
|
||||
...other
|
||||
} = this.props;
|
||||
const { mxEvent, rightClick, link, eventTileOps, reactions, collapseReplyChain, ...other } = this.props;
|
||||
delete other.getRelationsForEvent;
|
||||
delete other.permalinkCreator;
|
||||
|
||||
|
@ -396,10 +395,9 @@ export default class MessageContextMenu extends React.Component<IProps, IState>
|
|||
// status is SENT before remote-echo, null after
|
||||
const isSent = !eventStatus || eventStatus === EventStatus.SENT;
|
||||
const { timelineRenderingType, canReact, canSendMessages } = this.context;
|
||||
const isThread = (
|
||||
const isThread =
|
||||
timelineRenderingType === TimelineRenderingType.Thread ||
|
||||
timelineRenderingType === TimelineRenderingType.ThreadsList
|
||||
);
|
||||
timelineRenderingType === TimelineRenderingType.ThreadsList;
|
||||
const isThreadRootEvent = isThread && mxEvent?.getThread()?.rootEvent === mxEvent;
|
||||
|
||||
let resendReactionsButton: JSX.Element;
|
||||
|
@ -407,7 +405,7 @@ export default class MessageContextMenu extends React.Component<IProps, IState>
|
|||
resendReactionsButton = (
|
||||
<IconizedContextMenuOption
|
||||
iconClassName="mx_MessageContextMenu_iconResend"
|
||||
label={_t('Resend %(unsentCount)s reaction(s)', { unsentCount: unsentReactionsCount })}
|
||||
label={_t("Resend %(unsentCount)s reaction(s)", { unsentCount: unsentReactionsCount })}
|
||||
onClick={this.onResendReactionsClick}
|
||||
/>
|
||||
);
|
||||
|
@ -432,15 +430,13 @@ export default class MessageContextMenu extends React.Component<IProps, IState>
|
|||
<IconizedContextMenuOption
|
||||
iconClassName="mx_MessageContextMenu_iconOpenInMapSite"
|
||||
onClick={null}
|
||||
label={_t('Open in OpenStreetMap')}
|
||||
label={_t("Open in OpenStreetMap")}
|
||||
element="a"
|
||||
{
|
||||
...{
|
||||
href: mapSiteLink,
|
||||
target: "_blank",
|
||||
rel: "noreferrer noopener",
|
||||
}
|
||||
}
|
||||
{...{
|
||||
href: mapSiteLink,
|
||||
target: "_blank",
|
||||
rel: "noreferrer noopener",
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
@ -462,7 +458,7 @@ export default class MessageContextMenu extends React.Component<IProps, IState>
|
|||
pinButton = (
|
||||
<IconizedContextMenuOption
|
||||
iconClassName="mx_MessageContextMenu_iconPin"
|
||||
label={this.isPinned() ? _t('Unpin') : _t('Pin')}
|
||||
label={this.isPinned() ? _t("Unpin") : _t("Pin")}
|
||||
onClick={this.onPinClick}
|
||||
/>
|
||||
);
|
||||
|
@ -494,7 +490,7 @@ export default class MessageContextMenu extends React.Component<IProps, IState>
|
|||
<IconizedContextMenuOption
|
||||
iconClassName="mx_MessageContextMenu_iconPermalink"
|
||||
onClick={this.onShareClick}
|
||||
label={_t('Share')}
|
||||
label={_t("Share")}
|
||||
element="a"
|
||||
{
|
||||
// XXX: Typescript signature for AccessibleButton doesn't work properly for non-inputs like `a`
|
||||
|
@ -520,7 +516,8 @@ export default class MessageContextMenu extends React.Component<IProps, IState>
|
|||
}
|
||||
|
||||
let quoteButton: JSX.Element;
|
||||
if (eventTileOps && canSendMessages) { // this event is rendered using TextualBody
|
||||
if (eventTileOps && canSendMessages) {
|
||||
// this event is rendered using TextualBody
|
||||
quoteButton = (
|
||||
<IconizedContextMenuOption
|
||||
iconClassName="mx_MessageContextMenu_iconQuote"
|
||||
|
@ -533,14 +530,14 @@ export default class MessageContextMenu extends React.Component<IProps, IState>
|
|||
// Bridges can provide a 'external_url' to link back to the source.
|
||||
let externalURLButton: JSX.Element;
|
||||
if (
|
||||
typeof (mxEvent.getContent().external_url) === "string" &&
|
||||
typeof mxEvent.getContent().external_url === "string" &&
|
||||
isUrlPermitted(mxEvent.getContent().external_url)
|
||||
) {
|
||||
externalURLButton = (
|
||||
<IconizedContextMenuOption
|
||||
iconClassName="mx_MessageContextMenu_iconLink"
|
||||
onClick={this.closeMenu}
|
||||
label={_t('Source URL')}
|
||||
label={_t("Source URL")}
|
||||
element="a"
|
||||
{
|
||||
// XXX: Typescript signature for AccessibleButton doesn't work properly for non-inputs like `a`
|
||||
|
@ -594,10 +591,10 @@ export default class MessageContextMenu extends React.Component<IProps, IState>
|
|||
<IconizedContextMenuOption
|
||||
iconClassName="mx_MessageContextMenu_iconCopy"
|
||||
onClick={this.onCopyLinkClick}
|
||||
label={_t('Copy link')}
|
||||
label={_t("Copy link")}
|
||||
element="a"
|
||||
{
|
||||
// XXX: Typescript signature for AccessibleButton doesn't work properly for non-inputs like `a`
|
||||
// XXX: Typescript signature for AccessibleButton doesn't work properly for non-inputs like `a`
|
||||
...{
|
||||
href: link,
|
||||
target: "_blank",
|
||||
|
@ -651,12 +648,7 @@ export default class MessageContextMenu extends React.Component<IProps, IState>
|
|||
Thread.hasServerSideSupport &&
|
||||
timelineRenderingType !== TimelineRenderingType.Thread
|
||||
) {
|
||||
replyInThreadButton = (
|
||||
<ReplyInThreadButton
|
||||
mxEvent={mxEvent}
|
||||
closeMenu={this.closeMenu}
|
||||
/>
|
||||
);
|
||||
replyInThreadButton = <ReplyInThreadButton mxEvent={mxEvent} closeMenu={this.closeMenu} />;
|
||||
}
|
||||
|
||||
let reactButton;
|
||||
|
@ -686,8 +678,8 @@ export default class MessageContextMenu extends React.Component<IProps, IState>
|
|||
if (copyButton || copyLinkButton) {
|
||||
nativeItemsList = (
|
||||
<IconizedContextMenuOptionList>
|
||||
{ copyButton }
|
||||
{ copyLinkButton }
|
||||
{copyButton}
|
||||
{copyLinkButton}
|
||||
</IconizedContextMenuOptionList>
|
||||
);
|
||||
}
|
||||
|
@ -696,56 +688,44 @@ export default class MessageContextMenu extends React.Component<IProps, IState>
|
|||
if (editButton || replyButton || reactButton) {
|
||||
quickItemsList = (
|
||||
<IconizedContextMenuOptionList>
|
||||
{ reactButton }
|
||||
{ replyButton }
|
||||
{ replyInThreadButton }
|
||||
{ editButton }
|
||||
{reactButton}
|
||||
{replyButton}
|
||||
{replyInThreadButton}
|
||||
{editButton}
|
||||
</IconizedContextMenuOptionList>
|
||||
);
|
||||
}
|
||||
|
||||
const commonItemsList = (
|
||||
<IconizedContextMenuOptionList>
|
||||
{ viewInRoomButton }
|
||||
{ openInMapSiteButton }
|
||||
{ endPollButton }
|
||||
{ quoteButton }
|
||||
{ forwardButton }
|
||||
{ pinButton }
|
||||
{ permalinkButton }
|
||||
{ reportEventButton }
|
||||
{ externalURLButton }
|
||||
{ jumpToRelatedEventButton }
|
||||
{ unhidePreviewButton }
|
||||
{ viewSourceButton }
|
||||
{ resendReactionsButton }
|
||||
{ collapseReplyChainButton }
|
||||
{viewInRoomButton}
|
||||
{openInMapSiteButton}
|
||||
{endPollButton}
|
||||
{quoteButton}
|
||||
{forwardButton}
|
||||
{pinButton}
|
||||
{permalinkButton}
|
||||
{reportEventButton}
|
||||
{externalURLButton}
|
||||
{jumpToRelatedEventButton}
|
||||
{unhidePreviewButton}
|
||||
{viewSourceButton}
|
||||
{resendReactionsButton}
|
||||
{collapseReplyChainButton}
|
||||
</IconizedContextMenuOptionList>
|
||||
);
|
||||
|
||||
let redactItemList: JSX.Element;
|
||||
if (redactButton) {
|
||||
redactItemList = (
|
||||
<IconizedContextMenuOptionList red>
|
||||
{ redactButton }
|
||||
</IconizedContextMenuOptionList>
|
||||
);
|
||||
redactItemList = <IconizedContextMenuOptionList red>{redactButton}</IconizedContextMenuOptionList>;
|
||||
}
|
||||
|
||||
let reactionPicker: JSX.Element;
|
||||
if (this.state.reactionPickerDisplayed) {
|
||||
const buttonRect = (this.reactButtonRef.current as HTMLElement)?.getBoundingClientRect();
|
||||
reactionPicker = (
|
||||
<ContextMenu
|
||||
{...toRightOf(buttonRect)}
|
||||
onFinished={this.closeMenu}
|
||||
managed={false}
|
||||
>
|
||||
<ReactionPicker
|
||||
mxEvent={mxEvent}
|
||||
onFinished={this.onCloseReactionPicker}
|
||||
reactions={reactions}
|
||||
/>
|
||||
<ContextMenu {...toRightOf(buttonRect)} onFinished={this.closeMenu} managed={false}>
|
||||
<ReactionPicker mxEvent={mxEvent} onFinished={this.onCloseReactionPicker} reactions={reactions} />
|
||||
</ContextMenu>
|
||||
);
|
||||
}
|
||||
|
@ -758,14 +738,13 @@ export default class MessageContextMenu extends React.Component<IProps, IState>
|
|||
compact={true}
|
||||
data-testid="mx_MessageContextMenu"
|
||||
>
|
||||
{ nativeItemsList }
|
||||
{ quickItemsList }
|
||||
{ commonItemsList }
|
||||
{ redactItemList }
|
||||
{nativeItemsList}
|
||||
{quickItemsList}
|
||||
{commonItemsList}
|
||||
{redactItemList}
|
||||
</IconizedContextMenu>
|
||||
{ reactionPicker }
|
||||
{reactionPicker}
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -37,7 +37,7 @@ import Modal from "../../../Modal";
|
|||
import ExportDialog from "../dialogs/ExportDialog";
|
||||
import { useFeatureEnabled } from "../../../hooks/useSettings";
|
||||
import { usePinnedEvents } from "../right_panel/PinnedMessagesCard";
|
||||
import { RightPanelPhases } from '../../../stores/right-panel/RightPanelStorePhases';
|
||||
import { RightPanelPhases } from "../../../stores/right-panel/RightPanelStorePhases";
|
||||
import { ROOM_NOTIFICATIONS_TAB } from "../dialogs/RoomSettingsDialog";
|
||||
import { useEventEmitterState } from "../../../hooks/useEventEmitter";
|
||||
import RightPanelStore from "../../../stores/right-panel/RightPanelStore";
|
||||
|
@ -57,10 +57,8 @@ interface IProps extends IContextMenuProps {
|
|||
|
||||
const RoomContextMenu = ({ room, onFinished, ...props }: IProps) => {
|
||||
const cli = useContext(MatrixClientContext);
|
||||
const roomTags = useEventEmitterState(
|
||||
RoomListStore.instance,
|
||||
LISTS_UPDATE_EVENT,
|
||||
() => RoomListStore.instance.getTagsForRoom(room),
|
||||
const roomTags = useEventEmitterState(RoomListStore.instance, LISTS_UPDATE_EVENT, () =>
|
||||
RoomListStore.instance.getTagsForRoom(room),
|
||||
);
|
||||
|
||||
let leaveOption: JSX.Element;
|
||||
|
@ -76,12 +74,14 @@ const RoomContextMenu = ({ room, onFinished, ...props }: IProps) => {
|
|||
onFinished();
|
||||
};
|
||||
|
||||
leaveOption = <IconizedContextMenuOption
|
||||
iconClassName="mx_RoomTile_iconSignOut"
|
||||
label={_t("Forget")}
|
||||
className="mx_IconizedContextMenu_option_red"
|
||||
onClick={onForgetRoomClick}
|
||||
/>;
|
||||
leaveOption = (
|
||||
<IconizedContextMenuOption
|
||||
iconClassName="mx_RoomTile_iconSignOut"
|
||||
label={_t("Forget")}
|
||||
className="mx_IconizedContextMenu_option_red"
|
||||
onClick={onForgetRoomClick}
|
||||
/>
|
||||
);
|
||||
} else {
|
||||
const onLeaveRoomClick = (ev: ButtonEvent) => {
|
||||
ev.preventDefault();
|
||||
|
@ -96,20 +96,21 @@ const RoomContextMenu = ({ room, onFinished, ...props }: IProps) => {
|
|||
PosthogTrackers.trackInteraction("WebRoomHeaderContextMenuLeaveItem", ev);
|
||||
};
|
||||
|
||||
leaveOption = <IconizedContextMenuOption
|
||||
onClick={onLeaveRoomClick}
|
||||
label={_t("Leave")}
|
||||
className="mx_IconizedContextMenu_option_red"
|
||||
iconClassName="mx_RoomTile_iconSignOut"
|
||||
/>;
|
||||
leaveOption = (
|
||||
<IconizedContextMenuOption
|
||||
onClick={onLeaveRoomClick}
|
||||
label={_t("Leave")}
|
||||
className="mx_IconizedContextMenu_option_red"
|
||||
iconClassName="mx_RoomTile_iconSignOut"
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
const isDm = DMRoomMap.shared().getUserIdForRoomId(room.roomId);
|
||||
const videoRoomsEnabled = useFeatureEnabled("feature_video_rooms");
|
||||
const elementCallVideoRoomsEnabled = useFeatureEnabled("feature_element_call_video_rooms");
|
||||
const isVideoRoom = videoRoomsEnabled && (
|
||||
room.isElementVideoRoom() || (elementCallVideoRoomsEnabled && room.isCallRoom())
|
||||
);
|
||||
const isVideoRoom =
|
||||
videoRoomsEnabled && (room.isElementVideoRoom() || (elementCallVideoRoomsEnabled && room.isCallRoom()));
|
||||
|
||||
let inviteOption: JSX.Element;
|
||||
if (room.canInvite(cli.getUserId()!) && !isDm) {
|
||||
|
@ -126,11 +127,13 @@ const RoomContextMenu = ({ room, onFinished, ...props }: IProps) => {
|
|||
PosthogTrackers.trackInteraction("WebRoomHeaderContextMenuInviteItem", ev);
|
||||
};
|
||||
|
||||
inviteOption = <IconizedContextMenuOption
|
||||
onClick={onInviteClick}
|
||||
label={_t("Invite")}
|
||||
iconClassName="mx_RoomTile_iconInvite"
|
||||
/>;
|
||||
inviteOption = (
|
||||
<IconizedContextMenuOption
|
||||
onClick={onInviteClick}
|
||||
label={_t("Invite")}
|
||||
iconClassName="mx_RoomTile_iconInvite"
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
let favouriteOption: JSX.Element;
|
||||
|
@ -138,23 +141,27 @@ const RoomContextMenu = ({ room, onFinished, ...props }: IProps) => {
|
|||
let notificationOption: JSX.Element;
|
||||
if (room.getMyMembership() === "join") {
|
||||
const isFavorite = roomTags.includes(DefaultTagID.Favourite);
|
||||
favouriteOption = <IconizedContextMenuCheckbox
|
||||
onClick={(e) => {
|
||||
onTagRoom(e, DefaultTagID.Favourite);
|
||||
PosthogTrackers.trackInteraction("WebRoomHeaderContextMenuFavouriteToggle", e);
|
||||
}}
|
||||
active={isFavorite}
|
||||
label={isFavorite ? _t("Favourited") : _t("Favourite")}
|
||||
iconClassName="mx_RoomTile_iconStar"
|
||||
/>;
|
||||
favouriteOption = (
|
||||
<IconizedContextMenuCheckbox
|
||||
onClick={(e) => {
|
||||
onTagRoom(e, DefaultTagID.Favourite);
|
||||
PosthogTrackers.trackInteraction("WebRoomHeaderContextMenuFavouriteToggle", e);
|
||||
}}
|
||||
active={isFavorite}
|
||||
label={isFavorite ? _t("Favourited") : _t("Favourite")}
|
||||
iconClassName="mx_RoomTile_iconStar"
|
||||
/>
|
||||
);
|
||||
|
||||
const isLowPriority = roomTags.includes(DefaultTagID.LowPriority);
|
||||
lowPriorityOption = <IconizedContextMenuCheckbox
|
||||
onClick={(e) => onTagRoom(e, DefaultTagID.LowPriority)}
|
||||
active={isLowPriority}
|
||||
label={_t("Low priority")}
|
||||
iconClassName="mx_RoomTile_iconArrowDown"
|
||||
/>;
|
||||
lowPriorityOption = (
|
||||
<IconizedContextMenuCheckbox
|
||||
onClick={(e) => onTagRoom(e, DefaultTagID.LowPriority)}
|
||||
active={isLowPriority}
|
||||
label={_t("Low priority")}
|
||||
iconClassName="mx_RoomTile_iconArrowDown"
|
||||
/>
|
||||
);
|
||||
|
||||
const echoChamber = EchoChamber.forRoom(room);
|
||||
let notificationLabel: string;
|
||||
|
@ -178,80 +185,84 @@ const RoomContextMenu = ({ room, onFinished, ...props }: IProps) => {
|
|||
break;
|
||||
}
|
||||
|
||||
notificationOption = <IconizedContextMenuOption
|
||||
onClick={(ev: ButtonEvent) => {
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
notificationOption = (
|
||||
<IconizedContextMenuOption
|
||||
onClick={(ev: ButtonEvent) => {
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
|
||||
dis.dispatch({
|
||||
action: "open_room_settings",
|
||||
room_id: room.roomId,
|
||||
initial_tab_id: ROOM_NOTIFICATIONS_TAB,
|
||||
});
|
||||
onFinished();
|
||||
dis.dispatch({
|
||||
action: "open_room_settings",
|
||||
room_id: room.roomId,
|
||||
initial_tab_id: ROOM_NOTIFICATIONS_TAB,
|
||||
});
|
||||
onFinished();
|
||||
|
||||
PosthogTrackers.trackInteraction("WebRoomHeaderContextMenuNotificationsItem", ev);
|
||||
}}
|
||||
label={_t("Notifications")}
|
||||
iconClassName={iconClassName}
|
||||
>
|
||||
<span className="mx_IconizedContextMenu_sublabel">
|
||||
{ notificationLabel }
|
||||
</span>
|
||||
</IconizedContextMenuOption>;
|
||||
PosthogTrackers.trackInteraction("WebRoomHeaderContextMenuNotificationsItem", ev);
|
||||
}}
|
||||
label={_t("Notifications")}
|
||||
iconClassName={iconClassName}
|
||||
>
|
||||
<span className="mx_IconizedContextMenu_sublabel">{notificationLabel}</span>
|
||||
</IconizedContextMenuOption>
|
||||
);
|
||||
}
|
||||
|
||||
let peopleOption: JSX.Element;
|
||||
let copyLinkOption: JSX.Element;
|
||||
if (!isDm) {
|
||||
peopleOption = <IconizedContextMenuOption
|
||||
onClick={(ev: ButtonEvent) => {
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
peopleOption = (
|
||||
<IconizedContextMenuOption
|
||||
onClick={(ev: ButtonEvent) => {
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
|
||||
ensureViewingRoom(ev);
|
||||
RightPanelStore.instance.pushCard({ phase: RightPanelPhases.RoomMemberList }, false);
|
||||
onFinished();
|
||||
PosthogTrackers.trackInteraction("WebRoomHeaderContextMenuPeopleItem", ev);
|
||||
}}
|
||||
label={_t("People")}
|
||||
iconClassName="mx_RoomTile_iconPeople"
|
||||
>
|
||||
<span className="mx_IconizedContextMenu_sublabel">
|
||||
{ room.getJoinedMemberCount() }
|
||||
</span>
|
||||
</IconizedContextMenuOption>;
|
||||
ensureViewingRoom(ev);
|
||||
RightPanelStore.instance.pushCard({ phase: RightPanelPhases.RoomMemberList }, false);
|
||||
onFinished();
|
||||
PosthogTrackers.trackInteraction("WebRoomHeaderContextMenuPeopleItem", ev);
|
||||
}}
|
||||
label={_t("People")}
|
||||
iconClassName="mx_RoomTile_iconPeople"
|
||||
>
|
||||
<span className="mx_IconizedContextMenu_sublabel">{room.getJoinedMemberCount()}</span>
|
||||
</IconizedContextMenuOption>
|
||||
);
|
||||
|
||||
copyLinkOption = <IconizedContextMenuOption
|
||||
onClick={(ev: ButtonEvent) => {
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
copyLinkOption = (
|
||||
<IconizedContextMenuOption
|
||||
onClick={(ev: ButtonEvent) => {
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
|
||||
dis.dispatch({
|
||||
action: "copy_room",
|
||||
room_id: room.roomId,
|
||||
});
|
||||
onFinished();
|
||||
}}
|
||||
label={_t("Copy room link")}
|
||||
iconClassName="mx_RoomTile_iconCopyLink"
|
||||
/>;
|
||||
dis.dispatch({
|
||||
action: "copy_room",
|
||||
room_id: room.roomId,
|
||||
});
|
||||
onFinished();
|
||||
}}
|
||||
label={_t("Copy room link")}
|
||||
iconClassName="mx_RoomTile_iconCopyLink"
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
let filesOption: JSX.Element;
|
||||
if (!isVideoRoom) {
|
||||
filesOption = <IconizedContextMenuOption
|
||||
onClick={(ev: ButtonEvent) => {
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
filesOption = (
|
||||
<IconizedContextMenuOption
|
||||
onClick={(ev: ButtonEvent) => {
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
|
||||
ensureViewingRoom(ev);
|
||||
RightPanelStore.instance.pushCard({ phase: RightPanelPhases.FilePanel }, false);
|
||||
onFinished();
|
||||
}}
|
||||
label={_t("Files")}
|
||||
iconClassName="mx_RoomTile_iconFiles"
|
||||
/>;
|
||||
ensureViewingRoom(ev);
|
||||
RightPanelStore.instance.pushCard({ phase: RightPanelPhases.FilePanel }, false);
|
||||
onFinished();
|
||||
}}
|
||||
label={_t("Files")}
|
||||
iconClassName="mx_RoomTile_iconFiles"
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
const pinningEnabled = useFeatureEnabled("feature_pinning");
|
||||
|
@ -259,53 +270,57 @@ const RoomContextMenu = ({ room, onFinished, ...props }: IProps) => {
|
|||
|
||||
let pinsOption: JSX.Element;
|
||||
if (pinningEnabled && !isVideoRoom) {
|
||||
pinsOption = <IconizedContextMenuOption
|
||||
onClick={(ev: ButtonEvent) => {
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
pinsOption = (
|
||||
<IconizedContextMenuOption
|
||||
onClick={(ev: ButtonEvent) => {
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
|
||||
ensureViewingRoom(ev);
|
||||
RightPanelStore.instance.pushCard({ phase: RightPanelPhases.PinnedMessages }, false);
|
||||
onFinished();
|
||||
}}
|
||||
label={_t("Pinned")}
|
||||
iconClassName="mx_RoomTile_iconPins"
|
||||
>
|
||||
{ pinCount > 0 && <span className="mx_IconizedContextMenu_sublabel">
|
||||
{ pinCount }
|
||||
</span> }
|
||||
</IconizedContextMenuOption>;
|
||||
ensureViewingRoom(ev);
|
||||
RightPanelStore.instance.pushCard({ phase: RightPanelPhases.PinnedMessages }, false);
|
||||
onFinished();
|
||||
}}
|
||||
label={_t("Pinned")}
|
||||
iconClassName="mx_RoomTile_iconPins"
|
||||
>
|
||||
{pinCount > 0 && <span className="mx_IconizedContextMenu_sublabel">{pinCount}</span>}
|
||||
</IconizedContextMenuOption>
|
||||
);
|
||||
}
|
||||
|
||||
let widgetsOption: JSX.Element;
|
||||
if (!isVideoRoom) {
|
||||
widgetsOption = <IconizedContextMenuOption
|
||||
onClick={(ev: ButtonEvent) => {
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
widgetsOption = (
|
||||
<IconizedContextMenuOption
|
||||
onClick={(ev: ButtonEvent) => {
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
|
||||
ensureViewingRoom(ev);
|
||||
RightPanelStore.instance.setCard({ phase: RightPanelPhases.RoomSummary }, false);
|
||||
onFinished();
|
||||
}}
|
||||
label={_t("Widgets")}
|
||||
iconClassName="mx_RoomTile_iconWidgets"
|
||||
/>;
|
||||
ensureViewingRoom(ev);
|
||||
RightPanelStore.instance.setCard({ phase: RightPanelPhases.RoomSummary }, false);
|
||||
onFinished();
|
||||
}}
|
||||
label={_t("Widgets")}
|
||||
iconClassName="mx_RoomTile_iconWidgets"
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
let exportChatOption: JSX.Element;
|
||||
if (!isVideoRoom) {
|
||||
exportChatOption = <IconizedContextMenuOption
|
||||
onClick={(ev: ButtonEvent) => {
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
exportChatOption = (
|
||||
<IconizedContextMenuOption
|
||||
onClick={(ev: ButtonEvent) => {
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
|
||||
Modal.createDialog(ExportDialog, { room });
|
||||
onFinished();
|
||||
}}
|
||||
label={_t("Export chat")}
|
||||
iconClassName="mx_RoomTile_iconExport"
|
||||
/>;
|
||||
Modal.createDialog(ExportDialog, { room });
|
||||
onFinished();
|
||||
}}
|
||||
label={_t("Export chat")}
|
||||
iconClassName="mx_RoomTile_iconExport"
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
const onTagRoom = (ev: ButtonEvent, tagId: TagID) => {
|
||||
|
@ -333,62 +348,72 @@ const RoomContextMenu = ({ room, onFinished, ...props }: IProps) => {
|
|||
|
||||
const ensureViewingRoom = (ev: ButtonEvent) => {
|
||||
if (SdkContextClass.instance.roomViewStore.getRoomId() === room.roomId) return;
|
||||
dis.dispatch<ViewRoomPayload>({
|
||||
action: Action.ViewRoom,
|
||||
room_id: room.roomId,
|
||||
metricsTrigger: "RoomList",
|
||||
metricsViaKeyboard: ev.type !== "click",
|
||||
}, true);
|
||||
dis.dispatch<ViewRoomPayload>(
|
||||
{
|
||||
action: Action.ViewRoom,
|
||||
room_id: room.roomId,
|
||||
metricsTrigger: "RoomList",
|
||||
metricsViaKeyboard: ev.type !== "click",
|
||||
},
|
||||
true,
|
||||
);
|
||||
};
|
||||
|
||||
return <IconizedContextMenu {...props} onFinished={onFinished} className="mx_RoomTile_contextMenu" compact>
|
||||
<IconizedContextMenuOptionList>
|
||||
{ inviteOption }
|
||||
{ notificationOption }
|
||||
{ favouriteOption }
|
||||
{ peopleOption }
|
||||
{ filesOption }
|
||||
{ pinsOption }
|
||||
{ widgetsOption }
|
||||
{ lowPriorityOption }
|
||||
{ copyLinkOption }
|
||||
return (
|
||||
<IconizedContextMenu {...props} onFinished={onFinished} className="mx_RoomTile_contextMenu" compact>
|
||||
<IconizedContextMenuOptionList>
|
||||
{inviteOption}
|
||||
{notificationOption}
|
||||
{favouriteOption}
|
||||
{peopleOption}
|
||||
{filesOption}
|
||||
{pinsOption}
|
||||
{widgetsOption}
|
||||
{lowPriorityOption}
|
||||
{copyLinkOption}
|
||||
|
||||
<IconizedContextMenuOption
|
||||
onClick={(ev: ButtonEvent) => {
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
<IconizedContextMenuOption
|
||||
onClick={(ev: ButtonEvent) => {
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
|
||||
dis.dispatch({
|
||||
action: "open_room_settings",
|
||||
room_id: room.roomId,
|
||||
});
|
||||
onFinished();
|
||||
PosthogTrackers.trackInteraction("WebRoomHeaderContextMenuSettingsItem", ev);
|
||||
}}
|
||||
label={_t("Settings")}
|
||||
iconClassName="mx_RoomTile_iconSettings"
|
||||
/>
|
||||
dis.dispatch({
|
||||
action: "open_room_settings",
|
||||
room_id: room.roomId,
|
||||
});
|
||||
onFinished();
|
||||
PosthogTrackers.trackInteraction("WebRoomHeaderContextMenuSettingsItem", ev);
|
||||
}}
|
||||
label={_t("Settings")}
|
||||
iconClassName="mx_RoomTile_iconSettings"
|
||||
/>
|
||||
|
||||
{ exportChatOption }
|
||||
{exportChatOption}
|
||||
|
||||
{ SettingsStore.getValue("developerMode") && <IconizedContextMenuOption
|
||||
onClick={(ev: ButtonEvent) => {
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
{SettingsStore.getValue("developerMode") && (
|
||||
<IconizedContextMenuOption
|
||||
onClick={(ev: ButtonEvent) => {
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
|
||||
Modal.createDialog(DevtoolsDialog, {
|
||||
roomId: SdkContextClass.instance.roomViewStore.getRoomId(),
|
||||
}, "mx_DevtoolsDialog_wrapper");
|
||||
onFinished();
|
||||
}}
|
||||
label={_t("Developer tools")}
|
||||
iconClassName="mx_RoomTile_iconDeveloperTools"
|
||||
/> }
|
||||
Modal.createDialog(
|
||||
DevtoolsDialog,
|
||||
{
|
||||
roomId: SdkContextClass.instance.roomViewStore.getRoomId(),
|
||||
},
|
||||
"mx_DevtoolsDialog_wrapper",
|
||||
);
|
||||
onFinished();
|
||||
}}
|
||||
label={_t("Developer tools")}
|
||||
iconClassName="mx_RoomTile_iconDeveloperTools"
|
||||
/>
|
||||
)}
|
||||
|
||||
{ leaveOption }
|
||||
</IconizedContextMenuOptionList>
|
||||
</IconizedContextMenu>;
|
||||
{leaveOption}
|
||||
</IconizedContextMenuOptionList>
|
||||
</IconizedContextMenu>
|
||||
);
|
||||
};
|
||||
|
||||
export default RoomContextMenu;
|
||||
|
||||
|
|
|
@ -48,22 +48,27 @@ interface IProps extends IContextMenuProps {
|
|||
}
|
||||
|
||||
export const RoomGeneralContextMenu = ({
|
||||
room, onFinished,
|
||||
onPostFavoriteClick, onPostLowPriorityClick, onPostInviteClick, onPostCopyLinkClick, onPostSettingsClick,
|
||||
onPostLeaveClick, onPostForgetClick, ...props
|
||||
room,
|
||||
onFinished,
|
||||
onPostFavoriteClick,
|
||||
onPostLowPriorityClick,
|
||||
onPostInviteClick,
|
||||
onPostCopyLinkClick,
|
||||
onPostSettingsClick,
|
||||
onPostLeaveClick,
|
||||
onPostForgetClick,
|
||||
...props
|
||||
}: IProps) => {
|
||||
const cli = useContext(MatrixClientContext);
|
||||
const roomTags = useEventEmitterState(
|
||||
RoomListStore.instance,
|
||||
LISTS_UPDATE_EVENT,
|
||||
() => RoomListStore.instance.getTagsForRoom(room),
|
||||
const roomTags = useEventEmitterState(RoomListStore.instance, LISTS_UPDATE_EVENT, () =>
|
||||
RoomListStore.instance.getTagsForRoom(room),
|
||||
);
|
||||
const isDm = DMRoomMap.shared().getUserIdForRoomId(room.roomId);
|
||||
const wrapHandler = (
|
||||
handler: (ev: ButtonEvent) => void,
|
||||
postHandler?: (ev: ButtonEvent) => void,
|
||||
persistent = false,
|
||||
): (ev: ButtonEvent) => void => {
|
||||
): ((ev: ButtonEvent) => void) => {
|
||||
return (ev: ButtonEvent) => {
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
|
@ -91,96 +96,123 @@ export const RoomGeneralContextMenu = ({
|
|||
};
|
||||
|
||||
const isFavorite = roomTags.includes(DefaultTagID.Favourite);
|
||||
const favoriteOption: JSX.Element = <IconizedContextMenuCheckbox
|
||||
onClick={wrapHandler((ev) =>
|
||||
onTagRoom(ev, DefaultTagID.Favourite), onPostFavoriteClick, true)}
|
||||
active={isFavorite}
|
||||
label={isFavorite ? _t("Favourited") : _t("Favourite")}
|
||||
iconClassName="mx_RoomGeneralContextMenu_iconStar"
|
||||
/>;
|
||||
const favoriteOption: JSX.Element = (
|
||||
<IconizedContextMenuCheckbox
|
||||
onClick={wrapHandler((ev) => onTagRoom(ev, DefaultTagID.Favourite), onPostFavoriteClick, true)}
|
||||
active={isFavorite}
|
||||
label={isFavorite ? _t("Favourited") : _t("Favourite")}
|
||||
iconClassName="mx_RoomGeneralContextMenu_iconStar"
|
||||
/>
|
||||
);
|
||||
|
||||
const isLowPriority = roomTags.includes(DefaultTagID.LowPriority);
|
||||
const lowPriorityOption: JSX.Element = <IconizedContextMenuCheckbox
|
||||
onClick={wrapHandler((ev) =>
|
||||
onTagRoom(ev, DefaultTagID.LowPriority), onPostLowPriorityClick, true)}
|
||||
active={isLowPriority}
|
||||
label={_t("Low Priority")}
|
||||
iconClassName="mx_RoomGeneralContextMenu_iconArrowDown"
|
||||
/>;
|
||||
const lowPriorityOption: JSX.Element = (
|
||||
<IconizedContextMenuCheckbox
|
||||
onClick={wrapHandler((ev) => onTagRoom(ev, DefaultTagID.LowPriority), onPostLowPriorityClick, true)}
|
||||
active={isLowPriority}
|
||||
label={_t("Low Priority")}
|
||||
iconClassName="mx_RoomGeneralContextMenu_iconArrowDown"
|
||||
/>
|
||||
);
|
||||
|
||||
let inviteOption: JSX.Element;
|
||||
if (room.canInvite(cli.getUserId()) && !isDm) {
|
||||
inviteOption = <IconizedContextMenuOption
|
||||
onClick={wrapHandler(() => dis.dispatch({
|
||||
action: "view_invite",
|
||||
roomId: room.roomId,
|
||||
}), onPostInviteClick)}
|
||||
label={_t("Invite")}
|
||||
iconClassName="mx_RoomGeneralContextMenu_iconInvite"
|
||||
/>;
|
||||
inviteOption = (
|
||||
<IconizedContextMenuOption
|
||||
onClick={wrapHandler(
|
||||
() =>
|
||||
dis.dispatch({
|
||||
action: "view_invite",
|
||||
roomId: room.roomId,
|
||||
}),
|
||||
onPostInviteClick,
|
||||
)}
|
||||
label={_t("Invite")}
|
||||
iconClassName="mx_RoomGeneralContextMenu_iconInvite"
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
let copyLinkOption: JSX.Element;
|
||||
if (!isDm) {
|
||||
copyLinkOption = <IconizedContextMenuOption
|
||||
onClick={wrapHandler(() => dis.dispatch({
|
||||
action: "copy_room",
|
||||
room_id: room.roomId,
|
||||
}), onPostCopyLinkClick)}
|
||||
label={_t("Copy room link")}
|
||||
iconClassName="mx_RoomGeneralContextMenu_iconCopyLink"
|
||||
/>;
|
||||
copyLinkOption = (
|
||||
<IconizedContextMenuOption
|
||||
onClick={wrapHandler(
|
||||
() =>
|
||||
dis.dispatch({
|
||||
action: "copy_room",
|
||||
room_id: room.roomId,
|
||||
}),
|
||||
onPostCopyLinkClick,
|
||||
)}
|
||||
label={_t("Copy room link")}
|
||||
iconClassName="mx_RoomGeneralContextMenu_iconCopyLink"
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
const settingsOption: JSX.Element = <IconizedContextMenuOption
|
||||
onClick={wrapHandler(() => dis.dispatch({
|
||||
action: "open_room_settings",
|
||||
room_id: room.roomId,
|
||||
}), onPostSettingsClick)}
|
||||
label={_t("Settings")}
|
||||
iconClassName="mx_RoomGeneralContextMenu_iconSettings"
|
||||
/>;
|
||||
const settingsOption: JSX.Element = (
|
||||
<IconizedContextMenuOption
|
||||
onClick={wrapHandler(
|
||||
() =>
|
||||
dis.dispatch({
|
||||
action: "open_room_settings",
|
||||
room_id: room.roomId,
|
||||
}),
|
||||
onPostSettingsClick,
|
||||
)}
|
||||
label={_t("Settings")}
|
||||
iconClassName="mx_RoomGeneralContextMenu_iconSettings"
|
||||
/>
|
||||
);
|
||||
|
||||
let leaveOption: JSX.Element;
|
||||
if (roomTags.includes(DefaultTagID.Archived)) {
|
||||
leaveOption = <IconizedContextMenuOption
|
||||
iconClassName="mx_RoomGeneralContextMenu_iconSignOut"
|
||||
label={_t("Forget Room")}
|
||||
className="mx_IconizedContextMenu_option_red"
|
||||
onClick={wrapHandler(() => dis.dispatch({
|
||||
action: "forget_room",
|
||||
room_id: room.roomId,
|
||||
}), onPostForgetClick)}
|
||||
/>;
|
||||
leaveOption = (
|
||||
<IconizedContextMenuOption
|
||||
iconClassName="mx_RoomGeneralContextMenu_iconSignOut"
|
||||
label={_t("Forget Room")}
|
||||
className="mx_IconizedContextMenu_option_red"
|
||||
onClick={wrapHandler(
|
||||
() =>
|
||||
dis.dispatch({
|
||||
action: "forget_room",
|
||||
room_id: room.roomId,
|
||||
}),
|
||||
onPostForgetClick,
|
||||
)}
|
||||
/>
|
||||
);
|
||||
} else {
|
||||
leaveOption = <IconizedContextMenuOption
|
||||
onClick={wrapHandler(() => dis.dispatch({
|
||||
action: "leave_room",
|
||||
room_id: room.roomId,
|
||||
}), onPostLeaveClick)}
|
||||
label={_t("Leave")}
|
||||
className="mx_IconizedContextMenu_option_red"
|
||||
iconClassName="mx_RoomGeneralContextMenu_iconSignOut"
|
||||
/>;
|
||||
leaveOption = (
|
||||
<IconizedContextMenuOption
|
||||
onClick={wrapHandler(
|
||||
() =>
|
||||
dis.dispatch({
|
||||
action: "leave_room",
|
||||
room_id: room.roomId,
|
||||
}),
|
||||
onPostLeaveClick,
|
||||
)}
|
||||
label={_t("Leave")}
|
||||
className="mx_IconizedContextMenu_option_red"
|
||||
iconClassName="mx_RoomGeneralContextMenu_iconSignOut"
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return <IconizedContextMenu
|
||||
{...props}
|
||||
onFinished={onFinished}
|
||||
className="mx_RoomGeneralContextMenu"
|
||||
compact
|
||||
>
|
||||
{ !roomTags.includes(DefaultTagID.Archived) && (
|
||||
<IconizedContextMenuOptionList>
|
||||
{ favoriteOption }
|
||||
{ lowPriorityOption }
|
||||
{ inviteOption }
|
||||
{ copyLinkOption }
|
||||
{ settingsOption }
|
||||
</IconizedContextMenuOptionList>
|
||||
) }
|
||||
<IconizedContextMenuOptionList red>
|
||||
{ leaveOption }
|
||||
</IconizedContextMenuOptionList>
|
||||
</IconizedContextMenu>;
|
||||
return (
|
||||
<IconizedContextMenu {...props} onFinished={onFinished} className="mx_RoomGeneralContextMenu" compact>
|
||||
{!roomTags.includes(DefaultTagID.Archived) && (
|
||||
<IconizedContextMenuOptionList>
|
||||
{favoriteOption}
|
||||
{lowPriorityOption}
|
||||
{inviteOption}
|
||||
{copyLinkOption}
|
||||
{settingsOption}
|
||||
</IconizedContextMenuOptionList>
|
||||
)}
|
||||
<IconizedContextMenuOptionList red>{leaveOption}</IconizedContextMenuOptionList>
|
||||
</IconizedContextMenu>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -36,10 +36,7 @@ interface IProps extends IContextMenuProps {
|
|||
export const RoomNotificationContextMenu = ({ room, onFinished, ...props }: IProps) => {
|
||||
const [notificationState, setNotificationState] = useNotificationState(room);
|
||||
|
||||
const wrapHandler = (
|
||||
handler: (ev: ButtonEvent) => void,
|
||||
persistent = false,
|
||||
): (ev: ButtonEvent) => void => {
|
||||
const wrapHandler = (handler: (ev: ButtonEvent) => void, persistent = false): ((ev: ButtonEvent) => void) => {
|
||||
return (ev: ButtonEvent) => {
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
|
@ -53,45 +50,50 @@ export const RoomNotificationContextMenu = ({ room, onFinished, ...props }: IPro
|
|||
};
|
||||
};
|
||||
|
||||
const defaultOption: JSX.Element = <IconizedContextMenuRadio
|
||||
label={_t("Use default")}
|
||||
active={notificationState === RoomNotifState.AllMessages}
|
||||
iconClassName="mx_RoomNotificationContextMenu_iconBell"
|
||||
onClick={wrapHandler(() => setNotificationState(RoomNotifState.AllMessages))}
|
||||
/>;
|
||||
const defaultOption: JSX.Element = (
|
||||
<IconizedContextMenuRadio
|
||||
label={_t("Use default")}
|
||||
active={notificationState === RoomNotifState.AllMessages}
|
||||
iconClassName="mx_RoomNotificationContextMenu_iconBell"
|
||||
onClick={wrapHandler(() => setNotificationState(RoomNotifState.AllMessages))}
|
||||
/>
|
||||
);
|
||||
|
||||
const allMessagesOption: JSX.Element = <IconizedContextMenuRadio
|
||||
label={_t("All messages")}
|
||||
active={notificationState === RoomNotifState.AllMessagesLoud}
|
||||
iconClassName="mx_RoomNotificationContextMenu_iconBellDot"
|
||||
onClick={wrapHandler(() => setNotificationState(RoomNotifState.AllMessagesLoud))}
|
||||
/>;
|
||||
const allMessagesOption: JSX.Element = (
|
||||
<IconizedContextMenuRadio
|
||||
label={_t("All messages")}
|
||||
active={notificationState === RoomNotifState.AllMessagesLoud}
|
||||
iconClassName="mx_RoomNotificationContextMenu_iconBellDot"
|
||||
onClick={wrapHandler(() => setNotificationState(RoomNotifState.AllMessagesLoud))}
|
||||
/>
|
||||
);
|
||||
|
||||
const mentionsOption: JSX.Element = <IconizedContextMenuRadio
|
||||
label={_t("Mentions & Keywords")}
|
||||
active={notificationState === RoomNotifState.MentionsOnly}
|
||||
iconClassName="mx_RoomNotificationContextMenu_iconBellMentions"
|
||||
onClick={wrapHandler(() => setNotificationState(RoomNotifState.MentionsOnly))}
|
||||
/>;
|
||||
const mentionsOption: JSX.Element = (
|
||||
<IconizedContextMenuRadio
|
||||
label={_t("Mentions & Keywords")}
|
||||
active={notificationState === RoomNotifState.MentionsOnly}
|
||||
iconClassName="mx_RoomNotificationContextMenu_iconBellMentions"
|
||||
onClick={wrapHandler(() => setNotificationState(RoomNotifState.MentionsOnly))}
|
||||
/>
|
||||
);
|
||||
|
||||
const muteOption: JSX.Element = <IconizedContextMenuRadio
|
||||
label={_t("Off")}
|
||||
active={notificationState === RoomNotifState.Mute}
|
||||
iconClassName="mx_RoomNotificationContextMenu_iconBellCrossed"
|
||||
onClick={wrapHandler(() => setNotificationState(RoomNotifState.Mute))}
|
||||
/>;
|
||||
const muteOption: JSX.Element = (
|
||||
<IconizedContextMenuRadio
|
||||
label={_t("Off")}
|
||||
active={notificationState === RoomNotifState.Mute}
|
||||
iconClassName="mx_RoomNotificationContextMenu_iconBellCrossed"
|
||||
onClick={wrapHandler(() => setNotificationState(RoomNotifState.Mute))}
|
||||
/>
|
||||
);
|
||||
|
||||
return <IconizedContextMenu
|
||||
{...props}
|
||||
onFinished={onFinished}
|
||||
className="mx_RoomNotificationContextMenu"
|
||||
compact
|
||||
>
|
||||
<IconizedContextMenuOptionList first>
|
||||
{ defaultOption }
|
||||
{ allMessagesOption }
|
||||
{ mentionsOption }
|
||||
{ muteOption }
|
||||
</IconizedContextMenuOptionList>
|
||||
</IconizedContextMenu>;
|
||||
return (
|
||||
<IconizedContextMenu {...props} onFinished={onFinished} className="mx_RoomNotificationContextMenu" compact>
|
||||
<IconizedContextMenuOptionList first>
|
||||
{defaultOption}
|
||||
{allMessagesOption}
|
||||
{mentionsOption}
|
||||
{muteOption}
|
||||
</IconizedContextMenuOptionList>
|
||||
</IconizedContextMenu>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -63,7 +63,7 @@ const SpaceContextMenu = ({ space, hideHeader, onFinished, ...props }: IProps) =
|
|||
|
||||
inviteOption = (
|
||||
<IconizedContextMenuOption
|
||||
data-test-id='invite-option'
|
||||
data-test-id="invite-option"
|
||||
className="mx_SpacePanel_contextMenu_inviteButton"
|
||||
iconClassName="mx_SpacePanel_iconInvite"
|
||||
label={_t("Invite")}
|
||||
|
@ -85,7 +85,7 @@ const SpaceContextMenu = ({ space, hideHeader, onFinished, ...props }: IProps) =
|
|||
|
||||
settingsOption = (
|
||||
<IconizedContextMenuOption
|
||||
data-test-id='settings-option'
|
||||
data-test-id="settings-option"
|
||||
iconClassName="mx_SpacePanel_iconSettings"
|
||||
label={_t("Settings")}
|
||||
onClick={onSettingsClick}
|
||||
|
@ -102,7 +102,7 @@ const SpaceContextMenu = ({ space, hideHeader, onFinished, ...props }: IProps) =
|
|||
|
||||
leaveOption = (
|
||||
<IconizedContextMenuOption
|
||||
data-test-id='leave-option'
|
||||
data-test-id="leave-option"
|
||||
iconClassName="mx_SpacePanel_iconLeave"
|
||||
className="mx_IconizedContextMenu_option_red"
|
||||
label={_t("Leave space")}
|
||||
|
@ -170,39 +170,41 @@ const SpaceContextMenu = ({ space, hideHeader, onFinished, ...props }: IProps) =
|
|||
onFinished();
|
||||
};
|
||||
|
||||
newRoomSection = <>
|
||||
<div data-test-id='add-to-space-header' className="mx_SpacePanel_contextMenu_separatorLabel">
|
||||
{ _t("Add") }
|
||||
</div>
|
||||
{ canAddRooms &&
|
||||
<IconizedContextMenuOption
|
||||
data-test-id='new-room-option'
|
||||
iconClassName="mx_SpacePanel_iconPlus"
|
||||
label={_t("Room")}
|
||||
onClick={onNewRoomClick}
|
||||
/>
|
||||
}
|
||||
{ canAddVideoRooms &&
|
||||
<IconizedContextMenuOption
|
||||
data-test-id='new-video-room-option'
|
||||
iconClassName="mx_SpacePanel_iconPlus"
|
||||
label={_t("Video room")}
|
||||
onClick={onNewVideoRoomClick}
|
||||
>
|
||||
<BetaPill />
|
||||
</IconizedContextMenuOption>
|
||||
}
|
||||
{ canAddSubSpaces &&
|
||||
<IconizedContextMenuOption
|
||||
data-test-id='new-subspace-option'
|
||||
iconClassName="mx_SpacePanel_iconPlus"
|
||||
label={_t("Space")}
|
||||
onClick={onNewSubspaceClick}
|
||||
>
|
||||
<BetaPill />
|
||||
</IconizedContextMenuOption>
|
||||
}
|
||||
</>;
|
||||
newRoomSection = (
|
||||
<>
|
||||
<div data-test-id="add-to-space-header" className="mx_SpacePanel_contextMenu_separatorLabel">
|
||||
{_t("Add")}
|
||||
</div>
|
||||
{canAddRooms && (
|
||||
<IconizedContextMenuOption
|
||||
data-test-id="new-room-option"
|
||||
iconClassName="mx_SpacePanel_iconPlus"
|
||||
label={_t("Room")}
|
||||
onClick={onNewRoomClick}
|
||||
/>
|
||||
)}
|
||||
{canAddVideoRooms && (
|
||||
<IconizedContextMenuOption
|
||||
data-test-id="new-video-room-option"
|
||||
iconClassName="mx_SpacePanel_iconPlus"
|
||||
label={_t("Video room")}
|
||||
onClick={onNewVideoRoomClick}
|
||||
>
|
||||
<BetaPill />
|
||||
</IconizedContextMenuOption>
|
||||
)}
|
||||
{canAddSubSpaces && (
|
||||
<IconizedContextMenuOption
|
||||
data-test-id="new-subspace-option"
|
||||
iconClassName="mx_SpacePanel_iconPlus"
|
||||
label={_t("Space")}
|
||||
onClick={onNewSubspaceClick}
|
||||
>
|
||||
<BetaPill />
|
||||
</IconizedContextMenuOption>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
const onPreferencesClick = (ev: ButtonEvent) => {
|
||||
|
@ -235,38 +237,33 @@ const SpaceContextMenu = ({ space, hideHeader, onFinished, ...props }: IProps) =
|
|||
openSpace(ev);
|
||||
};
|
||||
|
||||
return <IconizedContextMenu
|
||||
{...props}
|
||||
onFinished={onFinished}
|
||||
className="mx_SpacePanel_contextMenu"
|
||||
compact
|
||||
>
|
||||
{ !hideHeader && <div className="mx_SpacePanel_contextMenu_header">
|
||||
{ space.name }
|
||||
</div> }
|
||||
<IconizedContextMenuOptionList first>
|
||||
<IconizedContextMenuOption
|
||||
iconClassName="mx_SpacePanel_iconHome"
|
||||
label={_t("Space home")}
|
||||
onClick={onHomeClick}
|
||||
/>
|
||||
{ inviteOption }
|
||||
<IconizedContextMenuOption
|
||||
iconClassName="mx_SpacePanel_iconExplore"
|
||||
label={canAddRooms ? _t("Manage & explore rooms") : _t("Explore rooms")}
|
||||
onClick={onExploreRoomsClick}
|
||||
/>
|
||||
<IconizedContextMenuOption
|
||||
iconClassName="mx_SpacePanel_iconPreferences"
|
||||
label={_t("Preferences")}
|
||||
onClick={onPreferencesClick}
|
||||
/>
|
||||
{ devtoolsOption }
|
||||
{ settingsOption }
|
||||
{ leaveOption }
|
||||
{ newRoomSection }
|
||||
</IconizedContextMenuOptionList>
|
||||
</IconizedContextMenu>;
|
||||
return (
|
||||
<IconizedContextMenu {...props} onFinished={onFinished} className="mx_SpacePanel_contextMenu" compact>
|
||||
{!hideHeader && <div className="mx_SpacePanel_contextMenu_header">{space.name}</div>}
|
||||
<IconizedContextMenuOptionList first>
|
||||
<IconizedContextMenuOption
|
||||
iconClassName="mx_SpacePanel_iconHome"
|
||||
label={_t("Space home")}
|
||||
onClick={onHomeClick}
|
||||
/>
|
||||
{inviteOption}
|
||||
<IconizedContextMenuOption
|
||||
iconClassName="mx_SpacePanel_iconExplore"
|
||||
label={canAddRooms ? _t("Manage & explore rooms") : _t("Explore rooms")}
|
||||
onClick={onExploreRoomsClick}
|
||||
/>
|
||||
<IconizedContextMenuOption
|
||||
iconClassName="mx_SpacePanel_iconPreferences"
|
||||
label={_t("Preferences")}
|
||||
onClick={onPreferencesClick}
|
||||
/>
|
||||
{devtoolsOption}
|
||||
{settingsOption}
|
||||
{leaveOption}
|
||||
{newRoomSection}
|
||||
</IconizedContextMenuOptionList>
|
||||
</IconizedContextMenu>
|
||||
);
|
||||
};
|
||||
|
||||
export default SpaceContextMenu;
|
||||
|
|
|
@ -18,7 +18,7 @@ import React, { useCallback, useEffect } from "react";
|
|||
import { MatrixEvent } from "matrix-js-sdk/src/matrix";
|
||||
|
||||
import { ButtonEvent } from "../elements/AccessibleButton";
|
||||
import dis from '../../../dispatcher/dispatcher';
|
||||
import dis from "../../../dispatcher/dispatcher";
|
||||
import { Action } from "../../../dispatcher/actions";
|
||||
import { RoomPermalinkCreator } from "../../../utils/permalinks/Permalinks";
|
||||
import { copyPlaintext } from "../../../utils/strings";
|
||||
|
@ -51,28 +51,34 @@ const ThreadListContextMenu: React.FC<ThreadListContextMenuProps> = ({
|
|||
}) => {
|
||||
const [menuDisplayed, button, openMenu, closeThreadOptions] = useContextMenu();
|
||||
|
||||
const viewInRoom = useCallback((evt: ButtonEvent): void => {
|
||||
evt.preventDefault();
|
||||
evt.stopPropagation();
|
||||
dis.dispatch<ViewRoomPayload>({
|
||||
action: Action.ViewRoom,
|
||||
event_id: mxEvent.getId(),
|
||||
highlighted: true,
|
||||
room_id: mxEvent.getRoomId(),
|
||||
metricsTrigger: undefined, // room doesn't change
|
||||
});
|
||||
closeThreadOptions();
|
||||
}, [mxEvent, closeThreadOptions]);
|
||||
|
||||
const copyLinkToThread = useCallback(async (evt: ButtonEvent | undefined) => {
|
||||
if (permalinkCreator) {
|
||||
evt?.preventDefault();
|
||||
evt?.stopPropagation();
|
||||
const matrixToUrl = permalinkCreator.forEvent(mxEvent.getId());
|
||||
await copyPlaintext(matrixToUrl);
|
||||
const viewInRoom = useCallback(
|
||||
(evt: ButtonEvent): void => {
|
||||
evt.preventDefault();
|
||||
evt.stopPropagation();
|
||||
dis.dispatch<ViewRoomPayload>({
|
||||
action: Action.ViewRoom,
|
||||
event_id: mxEvent.getId(),
|
||||
highlighted: true,
|
||||
room_id: mxEvent.getRoomId(),
|
||||
metricsTrigger: undefined, // room doesn't change
|
||||
});
|
||||
closeThreadOptions();
|
||||
}
|
||||
}, [mxEvent, closeThreadOptions, permalinkCreator]);
|
||||
},
|
||||
[mxEvent, closeThreadOptions],
|
||||
);
|
||||
|
||||
const copyLinkToThread = useCallback(
|
||||
async (evt: ButtonEvent | undefined) => {
|
||||
if (permalinkCreator) {
|
||||
evt?.preventDefault();
|
||||
evt?.stopPropagation();
|
||||
const matrixToUrl = permalinkCreator.forEvent(mxEvent.getId());
|
||||
await copyPlaintext(matrixToUrl);
|
||||
closeThreadOptions();
|
||||
}
|
||||
},
|
||||
[mxEvent, closeThreadOptions, permalinkCreator],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
onMenuToggle?.(menuDisplayed);
|
||||
|
@ -81,41 +87,46 @@ const ThreadListContextMenu: React.FC<ThreadListContextMenuProps> = ({
|
|||
const isMainSplitTimelineShown = !WidgetLayoutStore.instance.hasMaximisedWidget(
|
||||
MatrixClientPeg.get().getRoom(mxEvent.getRoomId()),
|
||||
);
|
||||
return <React.Fragment>
|
||||
<ContextMenuTooltipButton
|
||||
{...props}
|
||||
className="mx_BaseCard_header_title_button--option"
|
||||
onClick={openMenu}
|
||||
title={_t("Thread options")}
|
||||
isExpanded={menuDisplayed}
|
||||
inputRef={button}
|
||||
data-testid="threadlist-dropdown-button"
|
||||
/>
|
||||
{ menuDisplayed && (<IconizedContextMenu
|
||||
onFinished={closeThreadOptions}
|
||||
className="mx_RoomTile_contextMenu"
|
||||
compact
|
||||
rightAligned
|
||||
{...contextMenuBelow(button.current.getBoundingClientRect())}
|
||||
>
|
||||
<IconizedContextMenuOptionList>
|
||||
{ isMainSplitTimelineShown &&
|
||||
<IconizedContextMenuOption
|
||||
onClick={(e) => viewInRoom(e)}
|
||||
label={_t("View in room")}
|
||||
iconClassName="mx_ThreadPanel_viewInRoom"
|
||||
/> }
|
||||
{ permalinkCreator &&
|
||||
<IconizedContextMenuOption
|
||||
data-testid="copy-thread-link"
|
||||
onClick={(e) => copyLinkToThread(e)}
|
||||
label={_t("Copy link to thread")}
|
||||
iconClassName="mx_ThreadPanel_copyLinkToThread"
|
||||
/>
|
||||
}
|
||||
</IconizedContextMenuOptionList>
|
||||
</IconizedContextMenu>) }
|
||||
</React.Fragment>;
|
||||
return (
|
||||
<React.Fragment>
|
||||
<ContextMenuTooltipButton
|
||||
{...props}
|
||||
className="mx_BaseCard_header_title_button--option"
|
||||
onClick={openMenu}
|
||||
title={_t("Thread options")}
|
||||
isExpanded={menuDisplayed}
|
||||
inputRef={button}
|
||||
data-testid="threadlist-dropdown-button"
|
||||
/>
|
||||
{menuDisplayed && (
|
||||
<IconizedContextMenu
|
||||
onFinished={closeThreadOptions}
|
||||
className="mx_RoomTile_contextMenu"
|
||||
compact
|
||||
rightAligned
|
||||
{...contextMenuBelow(button.current.getBoundingClientRect())}
|
||||
>
|
||||
<IconizedContextMenuOptionList>
|
||||
{isMainSplitTimelineShown && (
|
||||
<IconizedContextMenuOption
|
||||
onClick={(e) => viewInRoom(e)}
|
||||
label={_t("View in room")}
|
||||
iconClassName="mx_ThreadPanel_viewInRoom"
|
||||
/>
|
||||
)}
|
||||
{permalinkCreator && (
|
||||
<IconizedContextMenuOption
|
||||
data-testid="copy-thread-link"
|
||||
onClick={(e) => copyLinkToThread(e)}
|
||||
label={_t("Copy link to thread")}
|
||||
iconClassName="mx_ThreadPanel_copyLinkToThread"
|
||||
/>
|
||||
)}
|
||||
</IconizedContextMenuOptionList>
|
||||
</IconizedContextMenu>
|
||||
)}
|
||||
</React.Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
export default ThreadListContextMenu;
|
||||
|
|
|
@ -70,20 +70,19 @@ const WidgetContextMenu: React.FC<IProps> = ({
|
|||
// XXX: won't i18n well, but looks like widget api only support 'message'?
|
||||
const message = err.message || _t("Unable to start audio streaming.");
|
||||
Modal.createDialog(ErrorDialog, {
|
||||
title: _t('Failed to start livestream'),
|
||||
title: _t("Failed to start livestream"),
|
||||
description: message,
|
||||
});
|
||||
}
|
||||
onFinished();
|
||||
};
|
||||
streamAudioStreamButton = <IconizedContextMenuOption
|
||||
onClick={onStreamAudioClick}
|
||||
label={_t("Start audio stream")}
|
||||
/>;
|
||||
streamAudioStreamButton = (
|
||||
<IconizedContextMenuOption onClick={onStreamAudioClick} label={_t("Start audio stream")} />
|
||||
);
|
||||
}
|
||||
|
||||
const pinnedWidgets = WidgetLayoutStore.instance.getContainerWidgets(room, Container.Top);
|
||||
const widgetIndex = pinnedWidgets.findIndex(widget => widget.id === app.id);
|
||||
const widgetIndex = pinnedWidgets.findIndex((widget) => widget.id === app.id);
|
||||
|
||||
let editButton;
|
||||
if (canModify && WidgetUtils.isManagedByManager(app)) {
|
||||
|
@ -103,14 +102,17 @@ const WidgetContextMenu: React.FC<IProps> = ({
|
|||
const screenshotsEnabled = SettingsStore.getValue("enableWidgetScreenshots");
|
||||
if (screenshotsEnabled && widgetMessaging?.hasCapability(MatrixCapabilities.Screenshots)) {
|
||||
const onSnapshotClick = () => {
|
||||
widgetMessaging?.takeScreenshot().then(data => {
|
||||
dis.dispatch({
|
||||
action: 'picture_snapshot',
|
||||
file: data.screenshot,
|
||||
widgetMessaging
|
||||
?.takeScreenshot()
|
||||
.then((data) => {
|
||||
dis.dispatch({
|
||||
action: "picture_snapshot",
|
||||
file: data.screenshot,
|
||||
});
|
||||
})
|
||||
.catch((err) => {
|
||||
logger.error("Failed to take screenshot: ", err);
|
||||
});
|
||||
}).catch(err => {
|
||||
logger.error("Failed to take screenshot: ", err);
|
||||
});
|
||||
onFinished();
|
||||
};
|
||||
|
||||
|
@ -128,7 +130,8 @@ const WidgetContextMenu: React.FC<IProps> = ({
|
|||
title: _t("Delete Widget"),
|
||||
description: _t(
|
||||
"Deleting a widget removes it for all users in this room." +
|
||||
" Are you sure you want to delete this widget?"),
|
||||
" Are you sure you want to delete this widget?",
|
||||
),
|
||||
button: _t("Delete widget"),
|
||||
onFinished: (confirmed) => {
|
||||
if (!confirmed) return;
|
||||
|
@ -140,15 +143,17 @@ const WidgetContextMenu: React.FC<IProps> = ({
|
|||
onFinished();
|
||||
};
|
||||
|
||||
deleteButton = <IconizedContextMenuOption
|
||||
onClick={_onDeleteClick}
|
||||
label={userWidget ? _t("Remove") : _t("Remove for everyone")}
|
||||
/>;
|
||||
deleteButton = (
|
||||
<IconizedContextMenuOption
|
||||
onClick={_onDeleteClick}
|
||||
label={userWidget ? _t("Remove") : _t("Remove for everyone")}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
const isAllowedWidget =
|
||||
(app.eventId !== undefined && (SettingsStore.getValue("allowedWidgets", roomId)[app.eventId] ?? false))
|
||||
|| app.creatorUserId === cli.getUserId();
|
||||
(app.eventId !== undefined && (SettingsStore.getValue("allowedWidgets", roomId)[app.eventId] ?? false)) ||
|
||||
app.creatorUserId === cli.getUserId();
|
||||
|
||||
const isLocalWidget = WidgetType.JITSI.matches(app.type);
|
||||
let revokeButton;
|
||||
|
@ -158,7 +163,7 @@ const WidgetContextMenu: React.FC<IProps> = ({
|
|||
const current = SettingsStore.getValue("allowedWidgets", roomId);
|
||||
if (app.eventId !== undefined) current[app.eventId] = false;
|
||||
const level = SettingsStore.firstSupportedLevel("allowedWidgets");
|
||||
SettingsStore.setValue("allowedWidgets", roomId, level, current).catch(err => {
|
||||
SettingsStore.setValue("allowedWidgets", roomId, level, current).catch((err) => {
|
||||
logger.error(err);
|
||||
// We don't really need to do anything about this - the user will just hit the button again.
|
||||
});
|
||||
|
@ -188,17 +193,19 @@ const WidgetContextMenu: React.FC<IProps> = ({
|
|||
moveRightButton = <IconizedContextMenuOption onClick={onClick} label={_t("Move right")} />;
|
||||
}
|
||||
|
||||
return <IconizedContextMenu {...props} chevronFace={ChevronFace.None} onFinished={onFinished}>
|
||||
<IconizedContextMenuOptionList>
|
||||
{ streamAudioStreamButton }
|
||||
{ editButton }
|
||||
{ revokeButton }
|
||||
{ deleteButton }
|
||||
{ snapshotButton }
|
||||
{ moveLeftButton }
|
||||
{ moveRightButton }
|
||||
</IconizedContextMenuOptionList>
|
||||
</IconizedContextMenu>;
|
||||
return (
|
||||
<IconizedContextMenu {...props} chevronFace={ChevronFace.None} onFinished={onFinished}>
|
||||
<IconizedContextMenuOptionList>
|
||||
{streamAudioStreamButton}
|
||||
{editButton}
|
||||
{revokeButton}
|
||||
{deleteButton}
|
||||
{snapshotButton}
|
||||
{moveLeftButton}
|
||||
{moveRightButton}
|
||||
</IconizedContextMenuOptionList>
|
||||
</IconizedContextMenu>
|
||||
);
|
||||
};
|
||||
|
||||
export default WidgetContextMenu;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue