diff --git a/src/components/structures/MessagePanel.tsx b/src/components/structures/MessagePanel.tsx index a5a9164bb7..4f7a6ca724 100644 --- a/src/components/structures/MessagePanel.tsx +++ b/src/components/structures/MessagePanel.tsx @@ -50,7 +50,7 @@ import Spinner from "../views/elements/Spinner"; import { RoomPermalinkCreator } from "../../utils/permalinks/Permalinks"; import EditorStateTransfer from "../../utils/EditorStateTransfer"; import { Action } from '../../dispatcher/actions'; -import { getEventDisplayInfo } from "../../utils/EventUtils"; +import { getEventDisplayInfo } from "../../utils/EventRenderingUtils"; import { IReadReceiptInfo } from "../views/rooms/ReadReceiptMarker"; import { haveRendererForEvent } from "../../events/EventTileFactory"; diff --git a/src/components/views/rooms/EventTile.tsx b/src/components/views/rooms/EventTile.tsx index aa2e38ea8d..bee349a8cc 100644 --- a/src/components/views/rooms/EventTile.tsx +++ b/src/components/views/rooms/EventTile.tsx @@ -54,7 +54,7 @@ import TooltipButton from '../elements/TooltipButton'; import ReadReceiptMarker, { IReadReceiptInfo } from "./ReadReceiptMarker"; import MessageActionBar from "../messages/MessageActionBar"; import ReactionsRow from '../messages/ReactionsRow'; -import { getEventDisplayInfo } from '../../../utils/EventUtils'; +import { getEventDisplayInfo } from '../../../utils/EventRenderingUtils'; import SettingsStore from "../../../settings/SettingsStore"; import { showThread } from '../../../dispatcher/dispatch-actions/threads'; import { MessagePreviewStore } from '../../../stores/room-list/MessagePreviewStore'; diff --git a/src/components/views/rooms/ReplyTile.tsx b/src/components/views/rooms/ReplyTile.tsx index 1d23e7a56a..bfd592f698 100644 --- a/src/components/views/rooms/ReplyTile.tsx +++ b/src/components/views/rooms/ReplyTile.tsx @@ -27,7 +27,8 @@ import { Action } from '../../../dispatcher/actions'; import { RoomPermalinkCreator } from '../../../utils/permalinks/Permalinks'; import SenderProfile from "../messages/SenderProfile"; import MImageReplyBody from "../messages/MImageReplyBody"; -import { getEventDisplayInfo, isVoiceMessage } from '../../../utils/EventUtils'; +import { isVoiceMessage } from '../../../utils/EventUtils'; +import { getEventDisplayInfo } from "../../../utils/EventRenderingUtils"; import MFileBody from "../messages/MFileBody"; import MVoiceMessageBody from "../messages/MVoiceMessageBody"; import { ViewRoomPayload } from "../../../dispatcher/payloads/ViewRoomPayload"; diff --git a/src/utils/EventRenderingUtils.ts b/src/utils/EventRenderingUtils.ts new file mode 100644 index 0000000000..62cca4bd9e --- /dev/null +++ b/src/utils/EventRenderingUtils.ts @@ -0,0 +1,112 @@ +/* +Copyright 2022 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +import { MatrixEvent } from "matrix-js-sdk/src/models/event"; +import { EventType, MsgType } from "matrix-js-sdk/src/@types/event"; +import { M_POLL_START } from "matrix-events-sdk"; +import { M_LOCATION } from "matrix-js-sdk/src/@types/location"; + +import SettingsStore from "../settings/SettingsStore"; +import { haveRendererForEvent, JitsiEventFactory, JSONEventFactory, pickFactory } from "../events/EventTileFactory"; +import { MatrixClientPeg } from "../MatrixClientPeg"; +import { getMessageModerationState, MessageModerationState } from "./EventUtils"; + +export function getEventDisplayInfo(mxEvent: MatrixEvent, hideEvent?: boolean): { + isInfoMessage: boolean; + hasRenderer: boolean; + isBubbleMessage: boolean; + isLeftAlignedBubbleMessage: boolean; + noBubbleEvent: boolean; + isSeeingThroughMessageHiddenForModeration: boolean; +} { + const content = mxEvent.getContent(); + const msgtype = content.msgtype; + const eventType = mxEvent.getType(); + + let isSeeingThroughMessageHiddenForModeration = false; + if (SettingsStore.getValue("feature_msc3531_hide_messages_pending_moderation")) { + switch (getMessageModerationState(mxEvent)) { + case MessageModerationState.VISIBLE_FOR_ALL: + case MessageModerationState.HIDDEN_TO_CURRENT_USER: + // Nothing specific to do here + break; + case MessageModerationState.SEE_THROUGH_FOR_CURRENT_USER: + // Show message with a marker. + isSeeingThroughMessageHiddenForModeration = true; + break; + } + } + + // TODO: Thread a MatrixClient through to here + let factory = pickFactory(mxEvent, MatrixClientPeg.get()); + + // Info messages are basically information about commands processed on a room + let isBubbleMessage = ( + eventType.startsWith("m.key.verification") || + (eventType === EventType.RoomMessage && msgtype?.startsWith("m.key.verification")) || + (eventType === EventType.RoomCreate) || + (eventType === EventType.RoomEncryption) || + (factory === JitsiEventFactory) + ); + const isLeftAlignedBubbleMessage = ( + !isBubbleMessage && + eventType === EventType.CallInvite + ); + let isInfoMessage = ( + !isBubbleMessage && + !isLeftAlignedBubbleMessage && + eventType !== EventType.RoomMessage && + eventType !== EventType.RoomMessageEncrypted && + eventType !== EventType.Sticker && + eventType !== EventType.RoomCreate && + !M_POLL_START.matches(eventType) + ); + // Some non-info messages want to be rendered in the appropriate bubble column but without the bubble background + const noBubbleEvent = ( + (eventType === EventType.RoomMessage && msgtype === MsgType.Emote) || + M_POLL_START.matches(eventType) || + M_LOCATION.matches(eventType) || + ( + eventType === EventType.RoomMessage && + M_LOCATION.matches(msgtype) + ) + ); + + // If we're showing hidden events in the timeline, we should use the + // source tile when there's no regular tile for an event and also for + // replace relations (which otherwise would display as a confusing + // duplicate of the thing they are replacing). + if (hideEvent || !haveRendererForEvent(mxEvent)) { + // forcefully ask for a factory for a hidden event (hidden event + // setting is checked internally) + // TODO: Thread a MatrixClient through to here + factory = pickFactory(mxEvent, MatrixClientPeg.get(), true); + if (factory === JSONEventFactory) { + isBubbleMessage = false; + // Reuse info message avatar and sender profile styling + isInfoMessage = true; + } + } + + return { + hasRenderer: !!factory, + isInfoMessage, + isBubbleMessage, + isLeftAlignedBubbleMessage, + noBubbleEvent, + isSeeingThroughMessageHiddenForModeration, + }; +} diff --git a/src/utils/EventUtils.ts b/src/utils/EventUtils.ts index 4300ee26e0..49ec5c29b3 100644 --- a/src/utils/EventUtils.ts +++ b/src/utils/EventUtils.ts @@ -18,7 +18,6 @@ import { EventStatus, MatrixEvent } from 'matrix-js-sdk/src/models/event'; import { EventType, EVENT_VISIBILITY_CHANGE_TYPE, MsgType, RelationType } from "matrix-js-sdk/src/@types/event"; import { MatrixClient } from 'matrix-js-sdk/src/client'; import { logger } from 'matrix-js-sdk/src/logger'; -import { M_LOCATION } from 'matrix-js-sdk/src/@types/location'; import { M_POLL_START } from "matrix-events-sdk"; import { MatrixClientPeg } from '../MatrixClientPeg'; @@ -29,7 +28,6 @@ import defaultDispatcher from "../dispatcher/dispatcher"; import { TimelineRenderingType } from "../contexts/RoomContext"; import { launchPollEditor } from "../components/views/messages/MPollBody"; import { Action } from "../dispatcher/actions"; -import { haveRendererForEvent, JitsiEventFactory, JSONEventFactory, pickFactory } from "../events/EventTileFactory"; /** * Returns whether an event should allow actions like reply, reactions, edit, etc. @@ -196,93 +194,6 @@ export function getMessageModerationState(mxEvent: MatrixEvent, client?: MatrixC return MessageModerationState.HIDDEN_TO_CURRENT_USER; } -export function getEventDisplayInfo(mxEvent: MatrixEvent, hideEvent?: boolean): { - isInfoMessage: boolean; - hasRenderer: boolean; - isBubbleMessage: boolean; - isLeftAlignedBubbleMessage: boolean; - noBubbleEvent: boolean; - isSeeingThroughMessageHiddenForModeration: boolean; -} { - const content = mxEvent.getContent(); - const msgtype = content.msgtype; - const eventType = mxEvent.getType(); - - let isSeeingThroughMessageHiddenForModeration = false; - if (SettingsStore.getValue("feature_msc3531_hide_messages_pending_moderation")) { - switch (getMessageModerationState(mxEvent)) { - case MessageModerationState.VISIBLE_FOR_ALL: - case MessageModerationState.HIDDEN_TO_CURRENT_USER: - // Nothing specific to do here - break; - case MessageModerationState.SEE_THROUGH_FOR_CURRENT_USER: - // Show message with a marker. - isSeeingThroughMessageHiddenForModeration = true; - break; - } - } - - // TODO: Thread a MatrixClient through to here - let factory = pickFactory(mxEvent, MatrixClientPeg.get()); - - // Info messages are basically information about commands processed on a room - let isBubbleMessage = ( - eventType.startsWith("m.key.verification") || - (eventType === EventType.RoomMessage && msgtype?.startsWith("m.key.verification")) || - (eventType === EventType.RoomCreate) || - (eventType === EventType.RoomEncryption) || - (factory === JitsiEventFactory) - ); - const isLeftAlignedBubbleMessage = ( - !isBubbleMessage && - eventType === EventType.CallInvite - ); - let isInfoMessage = ( - !isBubbleMessage && - !isLeftAlignedBubbleMessage && - eventType !== EventType.RoomMessage && - eventType !== EventType.RoomMessageEncrypted && - eventType !== EventType.Sticker && - eventType !== EventType.RoomCreate && - !M_POLL_START.matches(eventType) - ); - // Some non-info messages want to be rendered in the appropriate bubble column but without the bubble background - const noBubbleEvent = ( - (eventType === EventType.RoomMessage && msgtype === MsgType.Emote) || - M_POLL_START.matches(eventType) || - M_LOCATION.matches(eventType) || - ( - eventType === EventType.RoomMessage && - M_LOCATION.matches(msgtype) - ) - ); - - // If we're showing hidden events in the timeline, we should use the - // source tile when there's no regular tile for an event and also for - // replace relations (which otherwise would display as a confusing - // duplicate of the thing they are replacing). - if (hideEvent || !haveRendererForEvent(mxEvent)) { - // forcefully ask for a factory for a hidden event (hidden event - // setting is checked internally) - // TODO: Thread a MatrixClient through to here - factory = pickFactory(mxEvent, MatrixClientPeg.get(), true); - if (factory === JSONEventFactory) { - isBubbleMessage = false; - // Reuse info message avatar and sender profile styling - isInfoMessage = true; - } - } - - return { - hasRenderer: !!factory, - isInfoMessage, - isBubbleMessage, - isLeftAlignedBubbleMessage, - noBubbleEvent, - isSeeingThroughMessageHiddenForModeration, - }; -} - export function isVoiceMessage(mxEvent: MatrixEvent): boolean { const content = mxEvent.getContent(); // MSC2516 is a legacy identifier. See https://github.com/matrix-org/matrix-doc/pull/3245