diff --git a/res/css/_components.scss b/res/css/_components.scss index 4abcbbc9d4..12b71cd39f 100644 --- a/res/css/_components.scss +++ b/res/css/_components.scss @@ -148,7 +148,7 @@ @import "./views/elements/_PowerSelector.scss"; @import "./views/elements/_ProgressBar.scss"; @import "./views/elements/_QRCode.scss"; -@import "./views/elements/_ReplyThread.scss"; +@import "./views/elements/_ReplyChain.scss"; @import "./views/elements/_ResizeHandle.scss"; @import "./views/elements/_RichText.scss"; @import "./views/elements/_RoleButton.scss"; diff --git a/res/css/views/elements/_ReplyThread.scss b/res/css/views/elements/_ReplyChain.scss similarity index 83% rename from res/css/views/elements/_ReplyThread.scss rename to res/css/views/elements/_ReplyChain.scss index e19be82e25..c6b9c2bbaa 100644 --- a/res/css/views/elements/_ReplyThread.scss +++ b/res/css/views/elements/_ReplyChain.scss @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -.mx_ReplyThread { +.mx_ReplyChain { margin-top: 0; margin-left: 0; margin-right: 0; @@ -23,44 +23,44 @@ limitations under the License. border-left: 2px solid $button-bg-color; border-radius: 2px; - .mx_ReplyThread_show { + .mx_ReplyChain_show { cursor: pointer; } - &.mx_ReplyThread_color1 { + &.mx_ReplyChain_color1 { border-left-color: $username-variant1-color; } - &.mx_ReplyThread_color2 { + &.mx_ReplyChain_color2 { border-left-color: $username-variant2-color; } - &.mx_ReplyThread_color3 { + &.mx_ReplyChain_color3 { border-left-color: $username-variant3-color; } - &.mx_ReplyThread_color4 { + &.mx_ReplyChain_color4 { border-left-color: $username-variant4-color; } - &.mx_ReplyThread_color5 { + &.mx_ReplyChain_color5 { border-left-color: $username-variant5-color; } - &.mx_ReplyThread_color6 { + &.mx_ReplyChain_color6 { border-left-color: $username-variant6-color; } - &.mx_ReplyThread_color7 { + &.mx_ReplyChain_color7 { border-left-color: $username-variant7-color; } - &.mx_ReplyThread_color8 { + &.mx_ReplyChain_color8 { border-left-color: $username-variant8-color; } } -.mx_ReplyThread--expanded { +.mx_ReplyChain--expanded { .mx_EventTile_body { display: block; overflow-y: scroll !important; diff --git a/res/css/views/rooms/_EventBubbleTile.scss b/res/css/views/rooms/_EventBubbleTile.scss index 389a5c9706..72920936aa 100644 --- a/res/css/views/rooms/_EventBubbleTile.scss +++ b/res/css/views/rooms/_EventBubbleTile.scss @@ -192,11 +192,11 @@ limitations under the License. flex-direction: column; } - .mx_ReplyThread_show { + .mx_ReplyChain_show { order: 99999; } - .mx_ReplyThread { + .mx_ReplyChain { .mx_EventTile_reply { max-width: 90%; padding: 0; @@ -247,7 +247,7 @@ limitations under the License. .mx_EventTile_keyRequestInfo { grid-area: link; } - .mx_ReplyThread_wrapper { + .mx_ReplyChain_wrapper { grid-area: reply; } } diff --git a/res/css/views/rooms/_IRCLayout.scss b/res/css/views/rooms/_IRCLayout.scss index 578c0325d2..7edfff1e85 100644 --- a/res/css/views/rooms/_IRCLayout.scss +++ b/res/css/views/rooms/_IRCLayout.scss @@ -200,7 +200,7 @@ $irc-line-height: $font-18px; } } - .mx_ReplyThread { + .mx_ReplyChain { margin: 0; .mx_SenderProfile { order: unset; diff --git a/src/HtmlUtils.tsx b/src/HtmlUtils.tsx index 2eee5214af..7087386128 100644 --- a/src/HtmlUtils.tsx +++ b/src/HtmlUtils.tsx @@ -34,7 +34,7 @@ import linkifyMatrix from './linkify-matrix'; import SettingsStore from './settings/SettingsStore'; import { tryTransformPermalinkToLocalHref } from "./utils/permalinks/Permalinks"; import { getEmojiFromUnicode } from "./emoji"; -import ReplyThread from "./components/views/elements/ReplyThread"; +import ReplyChain from "./components/views/elements/ReplyChain"; import { mediaFromMxc } from "./customisations/Media"; linkifyMatrix(linkify); @@ -446,8 +446,8 @@ export function bodyToHtml(content: IContent, highlights: string[], opts: IOpts let formattedBody = typeof content.formatted_body === 'string' ? content.formatted_body : null; const plainBody = typeof content.body === 'string' ? content.body : ""; - if (opts.stripReplyFallback && formattedBody) formattedBody = ReplyThread.stripHTMLReply(formattedBody); - strippedBody = opts.stripReplyFallback ? ReplyThread.stripPlainReply(plainBody) : plainBody; + if (opts.stripReplyFallback && formattedBody) formattedBody = ReplyChain.stripHTMLReply(formattedBody); + strippedBody = opts.stripReplyFallback ? ReplyChain.stripPlainReply(plainBody) : plainBody; bodyHasEmoji = mightContainEmoji(isHtmlMessage ? formattedBody : plainBody); diff --git a/src/components/views/context_menus/MessageContextMenu.tsx b/src/components/views/context_menus/MessageContextMenu.tsx index 22dd3ac438..7447b8e32a 100644 --- a/src/components/views/context_menus/MessageContextMenu.tsx +++ b/src/components/views/context_menus/MessageContextMenu.tsx @@ -60,7 +60,7 @@ interface IProps extends IPosition { eventTileOps?: IEventTileOps; permalinkCreator?: RoomPermalinkCreator; /* an optional function to be called when the user clicks collapse thread, if not provided hide button */ - collapseReplyThread?(): void; + collapseReplyChain?(): void; /* callback called when the menu is dismissed */ onFinished(): void; /* if the menu is inside a dialog, we sometimes need to close that dialog after click (forwarding) */ @@ -203,8 +203,8 @@ export default class MessageContextMenu extends React.Component this.closeMenu(); }; - private onCollapseReplyThreadClick = (): void => { - this.props.collapseReplyThread(); + private onCollapseReplyChainClick = (): void => { + this.props.collapseReplyChain(); this.closeMenu(); }; @@ -240,7 +240,7 @@ export default class MessageContextMenu extends React.Component let unhidePreviewButton: JSX.Element; let externalURLButton: JSX.Element; let quoteButton: JSX.Element; - let collapseReplyThread: JSX.Element; + let collapseReplyChain: JSX.Element; let redactItemList: JSX.Element; // status is SENT before remote-echo, null after @@ -360,12 +360,12 @@ export default class MessageContextMenu extends React.Component ); } - if (this.props.collapseReplyThread) { - collapseReplyThread = ( + if (this.props.collapseReplyChain) { + collapseReplyChain = ( ); } @@ -392,7 +392,7 @@ export default class MessageContextMenu extends React.Component { unhidePreviewButton } { viewSourceButton } { resendReactionsButton } - { collapseReplyThread } + { collapseReplyChain } ); diff --git a/src/components/views/elements/ReplyThread.tsx b/src/components/views/elements/ReplyChain.tsx similarity index 92% rename from src/components/views/elements/ReplyThread.tsx rename to src/components/views/elements/ReplyChain.tsx index e0cca0c81e..50efdc92fb 100644 --- a/src/components/views/elements/ReplyThread.tsx +++ b/src/components/views/elements/ReplyChain.tsx @@ -46,7 +46,7 @@ const SHOW_EXPAND_QUOTE_PIXELS = 60; interface IProps { // the latest event in this chain of replies parentEv?: MatrixEvent; - // called when the ReplyThread contents has changed, including EventTiles thereof + // called when the ReplyChain contents has changed, including EventTiles thereof onHeightChanged: () => void; permalinkCreator: RoomPermalinkCreator; // Specifies which layout to use. @@ -72,8 +72,8 @@ interface IState { // This component does no cycle detection, simply because the only way to make such a cycle would be to // craft event_id's, using a homeserver that generates predictable event IDs; even then the impact would // be low as each event being loaded (after the first) is triggered by an explicit user action. -@replaceableComponent("views.elements.ReplyThread") -export default class ReplyThread extends React.Component { +@replaceableComponent("views.elements.ReplyChain") +export default class ReplyChain extends React.Component { static contextType = MatrixClientContext; private unmounted = false; private room: Room; @@ -253,8 +253,8 @@ export default class ReplyThread extends React.Component { return mixin; } - public static hasThreadReply(event: MatrixEvent) { - return Boolean(ReplyThread.getParentEventId(event)); + public static hasReply(event: MatrixEvent) { + return Boolean(ReplyChain.getParentEventId(event)); } componentDidMount() { @@ -288,7 +288,7 @@ export default class ReplyThread extends React.Component { private async initialize(): Promise { const { parentEv } = this.props; // at time of making this component we checked that props.parentEv has a parentEventId - const ev = await this.getEvent(ReplyThread.getParentEventId(parentEv)); + const ev = await this.getEvent(ReplyChain.getParentEventId(parentEv)); if (this.unmounted) return; @@ -306,7 +306,7 @@ export default class ReplyThread extends React.Component { private async getNextEvent(ev: MatrixEvent): Promise { try { - const inReplyToEventId = ReplyThread.getParentEventId(ev); + const inReplyToEventId = ReplyChain.getParentEventId(ev); return await this.getEvent(inReplyToEventId); } catch (e) { return null; @@ -354,15 +354,15 @@ export default class ReplyThread extends React.Component { dis.fire(Action.FocusSendMessageComposer); }; - private getReplyThreadColorClass(ev: MatrixEvent): string { - return getUserNameColorClass(ev.getSender()).replace("Username", "ReplyThread"); + private getReplyChainColorClass(ev: MatrixEvent): string { + return getUserNameColorClass(ev.getSender()).replace("Username", "ReplyChain"); } render() { let header = null; if (this.state.err) { - header =
+ header =
{ _t('Unable to load event that was replied to, ' + 'it either does not exist or you do not have permission to view it.') @@ -371,10 +371,10 @@ export default class ReplyThread extends React.Component { } else if (this.state.loadedEv) { const ev = this.state.loadedEv; const room = this.context.getRoom(ev.getRoomId()); - header =
+ header =
{ _t('In reply to ', {}, { - 'a': (sub) => { sub }, + 'a': (sub) => { sub }, 'pill': ( { }
; } else if (this.props.forExport) { - const eventId = ReplyThread.getParentEventId(this.props.parentEv); - header =

+ const eventId = ReplyChain.getParentEventId(this.props.parentEv); + header =

{ _t("In reply to this message", {}, { a: (sub) => ( @@ -404,12 +404,12 @@ export default class ReplyThread extends React.Component { const { isQuoteExpanded } = this.props; const evTiles = this.state.events.map((ev) => { const classname = classNames({ - 'mx_ReplyThread': true, - [this.getReplyThreadColorClass(ev)]: true, + 'mx_ReplyChain': true, + [this.getReplyChainColorClass(ev)]: true, // We don't want to add the class if it's undefined, it should only be expanded/collapsed when it's true/false - 'mx_ReplyThread--expanded': isQuoteExpanded === true, + 'mx_ReplyChain--expanded': isQuoteExpanded === true, // We don't want to add the class if it's undefined, it should only be expanded/collapsed when it's true/false - 'mx_ReplyThread--collapsed': isQuoteExpanded === false, + 'mx_ReplyChain--collapsed': isQuoteExpanded === false, }); return (

@@ -423,7 +423,7 @@ export default class ReplyThread extends React.Component { ); }); - return
+ return
{ header }
{ evTiles }
; diff --git a/src/components/views/messages/MessageActionBar.tsx b/src/components/views/messages/MessageActionBar.tsx index 2246d2bacc..dffe8be7a6 100644 --- a/src/components/views/messages/MessageActionBar.tsx +++ b/src/components/views/messages/MessageActionBar.tsx @@ -41,19 +41,19 @@ import classNames from 'classnames'; import SettingsStore from '../../../settings/SettingsStore'; import { RoomPermalinkCreator } from '../../../utils/permalinks/Permalinks'; -import ReplyThread from '../elements/ReplyThread'; +import ReplyChain from '../elements/ReplyChain'; interface IOptionsButtonProps { mxEvent: MatrixEvent; // TODO: Types getTile: () => any | null; - getReplyThread: () => ReplyThread; + getReplyChain: () => ReplyChain; permalinkCreator: RoomPermalinkCreator; onFocusChange: (menuDisplayed: boolean) => void; } const OptionsButton: React.FC = - ({ mxEvent, getTile, getReplyThread, permalinkCreator, onFocusChange }) => { + ({ mxEvent, getTile, getReplyChain, permalinkCreator, onFocusChange }) => { const [menuDisplayed, button, openMenu, closeMenu] = useContextMenu(); const [onFocus, isActive, ref] = useRovingTabIndex(button); useEffect(() => { @@ -63,7 +63,7 @@ const OptionsButton: React.FC = let contextMenu; if (menuDisplayed) { const tile = getTile && getTile(); - const replyThread = getReplyThread && getReplyThread(); + const replyThread = getReplyChain && getReplyChain(); const buttonRect = button.current.getBoundingClientRect(); contextMenu = = mxEvent={mxEvent} permalinkCreator={permalinkCreator} eventTileOps={tile && tile.getEventTileOps ? tile.getEventTileOps() : undefined} - collapseReplyThread={replyThread && replyThread.canCollapse() ? replyThread.collapse : undefined} + collapseReplyChain={replyThread && replyThread.canCollapse() ? replyThread.collapse : undefined} onFinished={closeMenu} />; } @@ -133,7 +133,7 @@ interface IMessageActionBarProps { reactions?: Relations; // TODO: Types getTile: () => any | null; - getReplyThread: () => ReplyThread | undefined; + getReplyChain: () => ReplyChain | undefined; permalinkCreator?: RoomPermalinkCreator; onFocusChange?: (menuDisplayed: boolean) => void; toggleThreadExpanded: () => void; @@ -343,7 +343,7 @@ export default class MessageActionBar extends React.PureComponent { const content = mxEvent.getContent(); // only strip reply if this is the original replying event, edits thereafter do not have the fallback - const stripReply = !mxEvent.replacingEvent() && !!ReplyThread.getParentEventId(mxEvent); + const stripReply = !mxEvent.replacingEvent() && !!ReplyChain.getParentEventId(mxEvent); let body = HtmlUtils.bodyToHtml(content, this.props.highlights, { disableBigEmoji: content.msgtype === MsgType.Emote || !SettingsStore.getValue('TextualBody.enableBigEmoji'), diff --git a/src/components/views/rooms/EventTile.tsx b/src/components/views/rooms/EventTile.tsx index 8a50284a93..3e3ef902e3 100644 --- a/src/components/views/rooms/EventTile.tsx +++ b/src/components/views/rooms/EventTile.tsx @@ -23,7 +23,7 @@ import { Relations } from "matrix-js-sdk/src/models/relations"; import { RoomMember } from "matrix-js-sdk/src/models/room-member"; import { Thread, ThreadEvent } from 'matrix-js-sdk/src/models/thread'; -import ReplyThread from "../elements/ReplyThread"; +import ReplyChain from "../elements/ReplyChain"; import { _t } from '../../../languageHandler'; import { hasText } from "../../../TextForEvent"; import * as sdk from "../../../index"; @@ -337,7 +337,7 @@ export default class EventTile extends React.Component { private isListeningForReceipts: boolean; // TODO: Types private tile = React.createRef(); - private replyThread = React.createRef(); + private replyThread = React.createRef(); public readonly ref = createRef(); @@ -931,7 +931,7 @@ export default class EventTile extends React.Component { // TODO: Types getTile: () => any | null = () => this.tile.current; - getReplyThread = () => this.replyThread.current; + getReplyChain = () => this.replyThread.current; getReactions = () => { if ( @@ -1100,7 +1100,7 @@ export default class EventTile extends React.Component { reactions={this.state.reactions} permalinkCreator={this.props.permalinkCreator} getTile={this.getTile} - getReplyThread={this.getReplyThread} + getReplyChain={this.getReplyChain} onFocusChange={this.onActionBarFocusChange} isQuoteExpanded={isQuoteExpanded} toggleThreadExpanded={() => this.setQuoteExpanded(!isQuoteExpanded)} @@ -1281,8 +1281,8 @@ export default class EventTile extends React.Component { default: { const thread = haveTileForEvent(this.props.mxEvent) && - ReplyThread.hasThreadReply(this.props.mxEvent) ? ( - { }); const wrapper = await mountForwardDialog(replyMessage); - expect(wrapper.find("ReplyThread")).toBeTruthy(); + expect(wrapper.find("ReplyChain")).toBeTruthy(); }); it("disables buttons for rooms without send permissions", async () => { diff --git a/test/components/views/elements/ReplyThread-test.js b/test/components/views/elements/ReplyChain-test.js similarity index 93% rename from test/components/views/elements/ReplyThread-test.js rename to test/components/views/elements/ReplyChain-test.js index ee81c2f210..35344929e4 100644 --- a/test/components/views/elements/ReplyThread-test.js +++ b/test/components/views/elements/ReplyChain-test.js @@ -1,8 +1,8 @@ import "../../../skinned-sdk"; import * as testUtils from '../../../test-utils'; -import ReplyThread from '../../../../src/components/views/elements/ReplyThread'; +import ReplyChain from '../../../../src/components/views/elements/ReplyChain'; -describe("ReplyThread", () => { +describe("ReplyChain", () => { describe('getParentEventId', () => { it('retrieves relation reply from unedited event', () => { const originalEventWithRelation = testUtils.mkEvent({ @@ -21,7 +21,7 @@ describe("ReplyThread", () => { room: "room_id", }); - expect(ReplyThread.getParentEventId(originalEventWithRelation)) + expect(ReplyChain.getParentEventId(originalEventWithRelation)) .toStrictEqual('$qkjmFBTEc0VvfVyzq1CJuh1QZi_xDIgNEFjZ4Pq34og'); }); @@ -65,7 +65,7 @@ describe("ReplyThread", () => { originalEventWithRelation.makeReplaced(editEvent); // The relation should be pulled from the original event - expect(ReplyThread.getParentEventId(originalEventWithRelation)) + expect(ReplyChain.getParentEventId(originalEventWithRelation)) .toStrictEqual('$qkjmFBTEc0VvfVyzq1CJuh1QZi_xDIgNEFjZ4Pq34og'); }); @@ -109,7 +109,7 @@ describe("ReplyThread", () => { originalEvent.makeReplaced(editEvent); // The relation should be pulled from the edit event - expect(ReplyThread.getParentEventId(originalEvent)) + expect(ReplyChain.getParentEventId(originalEvent)) .toStrictEqual('$qkjmFBTEc0VvfVyzq1CJuh1QZi_xDIgNEFjZ4Pq34og'); }); @@ -158,7 +158,7 @@ describe("ReplyThread", () => { originalEventWithRelation.makeReplaced(editEvent); // The relation should be pulled from the edit event - expect(ReplyThread.getParentEventId(originalEventWithRelation)).toStrictEqual('$999'); + expect(ReplyChain.getParentEventId(originalEventWithRelation)).toStrictEqual('$999'); }); it('able to clear relation reply from original event by providing empty relation field', () => { @@ -203,7 +203,7 @@ describe("ReplyThread", () => { originalEventWithRelation.makeReplaced(editEvent); // The relation should be pulled from the edit event - expect(ReplyThread.getParentEventId(originalEventWithRelation)).toStrictEqual(undefined); + expect(ReplyChain.getParentEventId(originalEventWithRelation)).toStrictEqual(undefined); }); }); });