Add Pin/Unpin action in quick access of the message action bar (#12897)
* Add Pin/Unpin action in quick access of the message action bar * Add tests for `MessageActionBar` * Add tests for `PinningUtils` * Fix `MessageContextMenu-test` * Add e2e test to pin/unpin from message action bar
This commit is contained in:
parent
4064db1d02
commit
3d80eff65b
9 changed files with 503 additions and 105 deletions
|
@ -36,9 +36,8 @@ 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 { canEditContent, 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";
|
||||
|
@ -60,6 +59,7 @@ 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 PinningUtils from "../../../utils/PinningUtils";
|
||||
|
||||
interface IReplyInThreadButton {
|
||||
mxEvent: MatrixEvent;
|
||||
|
@ -177,24 +177,11 @@ export default class MessageContextMenu extends React.Component<IProps, IState>
|
|||
this.props.mxEvent.getType() !== EventType.RoomServerAcl &&
|
||||
this.props.mxEvent.getType() !== EventType.RoomEncryption;
|
||||
|
||||
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
|
||||
if (!SettingsStore.getValue("feature_pinning")) canPin = false;
|
||||
const canPin = PinningUtils.canPinOrUnpin(cli, this.props.mxEvent);
|
||||
|
||||
this.setState({ canRedact, canPin });
|
||||
};
|
||||
|
||||
private isPinned(): boolean {
|
||||
const room = MatrixClientPeg.safeGet().getRoom(this.props.mxEvent.getRoomId());
|
||||
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());
|
||||
}
|
||||
|
||||
private canEndPoll(mxEvent: MatrixEvent): boolean {
|
||||
return (
|
||||
M_POLL_START.matches(mxEvent.getType()) &&
|
||||
|
@ -257,22 +244,8 @@ export default class MessageContextMenu extends React.Component<IProps, IState>
|
|||
};
|
||||
|
||||
private onPinClick = (): void => {
|
||||
const cli = MatrixClientPeg.safeGet();
|
||||
const room = cli.getRoom(this.props.mxEvent.getRoomId());
|
||||
if (!room) return;
|
||||
const eventId = this.props.mxEvent.getId();
|
||||
|
||||
const pinnedIds = room.currentState?.getStateEvents(EventType.RoomPinnedEvents, "")?.getContent().pinned || [];
|
||||
|
||||
if (pinnedIds.includes(eventId)) {
|
||||
pinnedIds.splice(pinnedIds.indexOf(eventId), 1);
|
||||
} else {
|
||||
pinnedIds.push(eventId);
|
||||
cli.setRoomAccountData(room.roomId, ReadPinsEventId, {
|
||||
event_ids: [...(room.getAccountData(ReadPinsEventId)?.getContent()?.event_ids || []), eventId],
|
||||
});
|
||||
}
|
||||
cli.sendStateEvent(room.roomId, EventType.RoomPinnedEvents, { pinned: pinnedIds }, "");
|
||||
// Pin or unpin in background
|
||||
PinningUtils.pinOrUnpinEvent(MatrixClientPeg.safeGet(), this.props.mxEvent);
|
||||
this.closeMenu();
|
||||
};
|
||||
|
||||
|
@ -452,17 +425,6 @@ export default class MessageContextMenu extends React.Component<IProps, IState>
|
|||
);
|
||||
}
|
||||
|
||||
let pinButton: JSX.Element | undefined;
|
||||
if (contentActionable && this.state.canPin) {
|
||||
pinButton = (
|
||||
<IconizedContextMenuOption
|
||||
iconClassName="mx_MessageContextMenu_iconPin"
|
||||
label={this.isPinned() ? _t("action|unpin") : _t("action|pin")}
|
||||
onClick={this.onPinClick}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
// This is specifically not behind the developerMode flag to give people insight into the Matrix
|
||||
const viewSourceButton = (
|
||||
<IconizedContextMenuOption
|
||||
|
@ -649,6 +611,18 @@ export default class MessageContextMenu extends React.Component<IProps, IState>
|
|||
);
|
||||
}
|
||||
|
||||
let pinButton: JSX.Element | undefined;
|
||||
if (rightClick && this.state.canPin) {
|
||||
const isPinned = PinningUtils.isPinned(MatrixClientPeg.safeGet(), this.props.mxEvent);
|
||||
pinButton = (
|
||||
<IconizedContextMenuOption
|
||||
iconClassName={isPinned ? "mx_MessageContextMenu_iconUnpin" : "mx_MessageContextMenu_iconPin"}
|
||||
label={isPinned ? _t("action|unpin") : _t("action|pin")}
|
||||
onClick={this.onPinClick}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
let viewInRoomButton: JSX.Element | undefined;
|
||||
if (isThreadRootEvent) {
|
||||
viewInRoomButton = (
|
||||
|
@ -671,13 +645,14 @@ export default class MessageContextMenu extends React.Component<IProps, IState>
|
|||
}
|
||||
|
||||
let quickItemsList: JSX.Element | undefined;
|
||||
if (editButton || replyButton || reactButton) {
|
||||
if (editButton || replyButton || reactButton || pinButton) {
|
||||
quickItemsList = (
|
||||
<IconizedContextMenuOptionList>
|
||||
{reactButton}
|
||||
{replyButton}
|
||||
{replyInThreadButton}
|
||||
{editButton}
|
||||
{pinButton}
|
||||
</IconizedContextMenuOptionList>
|
||||
);
|
||||
}
|
||||
|
@ -688,7 +663,6 @@ export default class MessageContextMenu extends React.Component<IProps, IState>
|
|||
{openInMapSiteButton}
|
||||
{endPollButton}
|
||||
{forwardButton}
|
||||
{pinButton}
|
||||
{permalinkButton}
|
||||
{reportEventButton}
|
||||
{externalURLButton}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue