diff --git a/src/components/views/messages/MPollBody.tsx b/src/components/views/messages/MPollBody.tsx index f0c806205e..91132949a6 100644 --- a/src/components/views/messages/MPollBody.tsx +++ b/src/components/views/messages/MPollBody.tsx @@ -38,6 +38,9 @@ import { formatCommaSeparatedList } from '../../../utils/FormattingUtils'; import StyledRadioButton from '../elements/StyledRadioButton'; import MatrixClientContext from "../../../contexts/MatrixClientContext"; import ErrorDialog from '../dialogs/ErrorDialog'; +import { GetRelationsForEvent } from "../rooms/EventTile"; +import PollCreateDialog from "../elements/PollCreateDialog"; +import { MatrixClientPeg } from "../../../MatrixClientPeg"; interface IState { selected?: string; // Which option was clicked by the local user @@ -169,6 +172,43 @@ export function isPollEnded( return authorisedRelations.length > 0; } +export function pollAlreadyHasVotes(mxEvent: MatrixEvent, getRelationsForEvent?: GetRelationsForEvent): boolean { + if (!getRelationsForEvent) return false; + + const voteRelations = createVoteRelations(getRelationsForEvent, mxEvent.getId()); + return voteRelations.getRelations().length > 0; +} + +export function launchPollEditor(mxEvent: MatrixEvent, getRelationsForEvent?: GetRelationsForEvent): void { + if (pollAlreadyHasVotes(mxEvent, getRelationsForEvent)) { + Modal.createTrackedDialog( + 'Not allowed to edit poll', + '', + ErrorDialog, + { + title: _t("Can't edit poll"), + description: _t( + "Sorry, you can't edit a poll after votes have been cast.", + ), + }, + ); + } else { + Modal.createTrackedDialog( + 'Polls', + 'create', + PollCreateDialog, + { + room: MatrixClientPeg.get().getRoom(mxEvent.getRoomId()), + threadId: mxEvent.getThread()?.id ?? null, + editingMxEvent: mxEvent, + }, + 'mx_CompoundDialog', + false, // isPriorityModal + true, // isStaticModal + ); + } +} + @replaceableComponent("views.messages.MPollBody") export default class MPollBody extends React.Component { public static contextType = MatrixClientContext; diff --git a/src/components/views/messages/MessageActionBar.tsx b/src/components/views/messages/MessageActionBar.tsx index e07eb0b7c0..455788f3b1 100644 --- a/src/components/views/messages/MessageActionBar.tsx +++ b/src/components/views/messages/MessageActionBar.tsx @@ -20,14 +20,13 @@ import React, { ReactElement, useEffect } from 'react'; import { EventStatus, MatrixEvent } from 'matrix-js-sdk/src/models/event'; import classNames from 'classnames'; import { MsgType } from 'matrix-js-sdk/src/@types/event'; -import { M_POLL_START } from 'matrix-events-sdk'; import type { Relations } from 'matrix-js-sdk/src/models/relations'; import { _t } from '../../../languageHandler'; import dis from '../../../dispatcher/dispatcher'; import { Action } from '../../../dispatcher/actions'; import ContextMenu, { aboveLeftOf, ContextMenuTooltipButton, useContextMenu } from '../../structures/ContextMenu'; -import { isContentActionable, canEditContent } from '../../../utils/EventUtils'; +import { isContentActionable, canEditContent, editEvent } from '../../../utils/EventUtils'; import RoomContext, { TimelineRenderingType } from "../../../contexts/RoomContext"; import Toolbar from "../../../accessibility/Toolbar"; import { RovingAccessibleTooltipButton, useRovingTabIndex } from "../../../accessibility/RovingTabIndex"; @@ -40,13 +39,9 @@ import DownloadActionButton from "./DownloadActionButton"; import SettingsStore from '../../../settings/SettingsStore'; import { RoomPermalinkCreator } from '../../../utils/permalinks/Permalinks'; import ReplyChain from '../elements/ReplyChain'; -import { showThread } from '../../../dispatcher/dispatch-actions/threads'; import ReactionPicker from "../emojipicker/ReactionPicker"; import { CardContext } from '../right_panel/BaseCard'; -import Modal from '../../../Modal'; -import PollCreateDialog from '../elements/PollCreateDialog'; -import ErrorDialog from '../dialogs/ErrorDialog'; -import { createVoteRelations } from './MPollBody'; +import { showThread } from "../../../dispatcher/dispatch-actions/threads"; interface IOptionsButtonProps { mxEvent: MatrixEvent; @@ -233,59 +228,8 @@ export default class MessageActionBar extends React.PureComponent { - if (!this.props.getRelationsForEvent) { - return false; - } - - const voteRelations = createVoteRelations( - this.props.getRelationsForEvent, - this.props.mxEvent.getId(), - ); - - return voteRelations.getRelations().length > 0; - }; - - private launchPollEditor = (): void => { - if (this.pollAlreadyHasVotes()) { - Modal.createTrackedDialog( - 'Not allowed to edit poll', - '', - ErrorDialog, - { - title: _t("Can't edit poll"), - description: _t( - "Sorry, you can't edit a poll after votes have been cast.", - ), - }, - ); - } else { - Modal.createTrackedDialog( - 'Polls', - 'create', - PollCreateDialog, - { - room: this.context.room, - threadId: this.context.threadId ?? null, - editingMxEvent: this.props.mxEvent, - }, - 'mx_CompoundDialog', - false, // isPriorityModal - true, // isStaticModal - ); - } - }; - private onEditClick = (): void => { - if (M_POLL_START.matches(this.props.mxEvent.getType())) { - this.launchPollEditor(); - } else { - dis.dispatch({ - action: Action.EditEvent, - event: this.props.mxEvent, - timelineRenderingType: this.context.timelineRenderingType, - }); - } + editEvent(this.props.mxEvent, this.context.timelineRenderingType); }; private readonly forbiddenThreadHeadMsgType = [ diff --git a/src/components/views/rooms/EventTile.tsx b/src/components/views/rooms/EventTile.tsx index ea0213099e..2b95c946bd 100644 --- a/src/components/views/rooms/EventTile.tsx +++ b/src/components/views/rooms/EventTile.tsx @@ -79,6 +79,8 @@ import { DecryptionFailureTracker } from '../../../DecryptionFailureTracker'; import RedactedBody from '../messages/RedactedBody'; import { ViewRoomPayload } from "../../../dispatcher/payloads/ViewRoomPayload"; +export type GetRelationsForEvent = (eventId: string, relationType: string, eventType: string) => Relations; + const eventTileTypes = { [EventType.RoomMessage]: 'messages.MessageEvent', [EventType.Sticker]: 'messages.MessageEvent', @@ -293,7 +295,7 @@ interface IProps { isTwelveHour?: boolean; // helper function to access relations for this event - getRelationsForEvent?: (eventId: string, relationType: string, eventType: string) => Relations; + getRelationsForEvent?: GetRelationsForEvent; // whether to show reactions for this event showReactions?: boolean; diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index b6df13752c..ca38c23b05 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -2093,8 +2093,6 @@ "Go": "Go", "Error processing audio message": "Error processing audio message", "React": "React", - "Can't edit poll": "Can't edit poll", - "Sorry, you can't edit a poll after votes have been cast.": "Sorry, you can't edit a poll after votes have been cast.", "Edit": "Edit", "Reply in thread": "Reply in thread", "Reply": "Reply", @@ -2130,6 +2128,8 @@ "Failed to load map": "Failed to load map", "Zoom in": "Zoom in", "Zoom out": "Zoom out", + "Can't edit poll": "Can't edit poll", + "Sorry, you can't edit a poll after votes have been cast.": "Sorry, you can't edit a poll after votes have been cast.", "Vote not registered": "Vote not registered", "Sorry, your vote was not registered. Please try again.": "Sorry, your vote was not registered. Please try again.", "Final result based on %(count)s votes|other": "Final result based on %(count)s votes", diff --git a/src/utils/EventUtils.ts b/src/utils/EventUtils.ts index 9925f0cb75..85bf88cf67 100644 --- a/src/utils/EventUtils.ts +++ b/src/utils/EventUtils.ts @@ -23,8 +23,12 @@ import { M_POLL_START } from "matrix-events-sdk"; import { MatrixClientPeg } from '../MatrixClientPeg'; import shouldHideEvent from "../shouldHideEvent"; -import { getHandlerTile, haveTileForEvent } from "../components/views/rooms/EventTile"; +import { getHandlerTile, GetRelationsForEvent, haveTileForEvent } from "../components/views/rooms/EventTile"; import SettingsStore from "../settings/SettingsStore"; +import defaultDispatcher from "../dispatcher/dispatcher"; +import { TimelineRenderingType } from "../contexts/RoomContext"; +import { launchPollEditor } from "../components/views/messages/MPollBody"; +import { Action } from "../dispatcher/actions"; /** * Returns whether an event should allow actions like reply, reactions, edit, etc. @@ -312,3 +316,21 @@ export async function fetchInitialEvent( return initialEvent; } + +export function editEvent( + mxEvent: MatrixEvent, + timelineRenderingType: TimelineRenderingType, + getRelationsForEvent?: GetRelationsForEvent, +): void { + if (!canEditContent(mxEvent)) return; + + if (M_POLL_START.matches(mxEvent.getType())) { + launchPollEditor(mxEvent, getRelationsForEvent); + } else { + defaultDispatcher.dispatch({ + action: Action.EditEvent, + event: mxEvent, + timelineRenderingType: timelineRenderingType, + }); + } +}