From 1de9630e44c694693ee7d039c95e8e697c13c881 Mon Sep 17 00:00:00 2001 From: Germain Date: Thu, 11 Nov 2021 11:00:18 +0000 Subject: [PATCH] Fixes following threads design implementation review (#7100) --- res/css/structures/_ContextualMenu.scss | 1 - res/css/structures/_RightPanel.scss | 2 +- res/css/views/right_panel/_BaseCard.scss | 8 +- res/css/views/right_panel/_ThreadPanel.scss | 132 ++++++++++++++---- res/css/views/rooms/_EventTile.scss | 74 ++++++++-- .../element-icons/message/overflow-large.svg | 5 + src/components/structures/RightPanel.tsx | 4 +- src/components/structures/ThreadPanel.tsx | 19 ++- src/components/structures/ThreadView.tsx | 3 +- .../context_menus/ThreadListContextMenu.tsx | 13 +- .../views/messages/MessageActionBar.tsx | 4 +- src/components/views/right_panel/BaseCard.tsx | 5 +- .../views/right_panel/RoomHeaderButtons.tsx | 24 +++- .../views/right_panel/RoomSummaryCard.tsx | 6 - src/components/views/rooms/EventTile.tsx | 91 +++++++----- src/i18n/strings/en_EN.json | 4 +- 16 files changed, 280 insertions(+), 115 deletions(-) create mode 100644 res/img/element-icons/message/overflow-large.svg diff --git a/res/css/structures/_ContextualMenu.scss b/res/css/structures/_ContextualMenu.scss index 88a01f220f..42e8b403d1 100644 --- a/res/css/structures/_ContextualMenu.scss +++ b/res/css/structures/_ContextualMenu.scss @@ -38,7 +38,6 @@ limitations under the License. position: absolute; font-size: $font-14px; z-index: 5001; - contain: content; } .mx_ContextualMenu_right { diff --git a/res/css/structures/_RightPanel.scss b/res/css/structures/_RightPanel.scss index 51d7693cff..9119402490 100644 --- a/res/css/structures/_RightPanel.scss +++ b/res/css/structures/_RightPanel.scss @@ -22,7 +22,7 @@ limitations under the License. display: flex; flex-direction: column; border-radius: 8px; - padding: 4px 0; + padding: 8px 0; box-sizing: border-box; height: 100%; contain: strict; diff --git a/res/css/views/right_panel/_BaseCard.scss b/res/css/views/right_panel/_BaseCard.scss index 8c1a55fe05..4c5594ce6d 100644 --- a/res/css/views/right_panel/_BaseCard.scss +++ b/res/css/views/right_panel/_BaseCard.scss @@ -22,7 +22,7 @@ limitations under the License. flex: 1; .mx_BaseCard_header { - margin: 8px 0; + margin: 4px 0; > h2 { margin: 0 44px; @@ -40,13 +40,13 @@ limitations under the License. width: 20px; margin: 12px; top: 0; - border-radius: 10px; + border-radius: 50%; &::before { content: ""; position: absolute; - height: 20px; - width: 20px; + height: inherit; + width: inherit; top: 0; left: 0; mask-repeat: no-repeat; diff --git a/res/css/views/right_panel/_ThreadPanel.scss b/res/css/views/right_panel/_ThreadPanel.scss index 9fd3cb3635..b890658bde 100644 --- a/res/css/views/right_panel/_ThreadPanel.scss +++ b/res/css/views/right_panel/_ThreadPanel.scss @@ -18,21 +18,29 @@ limitations under the License. display: flex; flex-direction: column; - padding-right: 0; - .mx_BaseCard_header { + margin-bottom: 12px; .mx_BaseCard_close, .mx_BaseCard_back { - margin-top: 15px; + width: 24px; + height: 24px; + } + .mx_BaseCard_back { + left: -4px; } .mx_BaseCard_close { - right: -8px; + right: -4px; } } - .mx_ThreadPanel__header { + .mx_BaseCard_back ~ .mx_ThreadPanel__header { width: calc(100% - 60px); margin-left: 30px; + } + + .mx_ThreadPanel__header { + width: calc(100% - 30px); + height: 24px; display: flex; flex: 1; justify-content: space-between; @@ -47,13 +55,23 @@ limitations under the License. .mx_AccessibleButton { font-size: 12px; - color: $primary-content; + color: $secondary-content; } .mx_MessageActionBar_optionsButton { position: relative; } + .mx_MessageActionBar_maskButton { + --size: 24px; + width: var(--size); + height: var(--size); + &::after { + mask-size: var(--size); + mask-image: url("$(res)/img/element-icons/message/overflow-large.svg"); + } + } + .mx_ContextualMenu_wrapper { // It's added here due to some weird error if I pass it directly in the style, even though it's a numeric value, so it's being passed 0 instead. // The error: react_devtools_backend.js:2526 Warning: `NaN` is an invalid value for the `top` css style property. @@ -70,6 +88,25 @@ limitations under the License. font-size: 12px; color: $secondary-content; + padding-top: 10px; + padding-bottom: 10px; + + border: 1px solid $quinary-content; + box-shadow: 0px 1px 3px rgba(23, 25, 28, 0.05); + } + + .mx_ContextualMenu_chevron_top { + left: auto; + right: 22px; + border-bottom-color: $quinary-content; + &::after { + content: ""; + border: inherit; + border-bottom-color: $background; + position: absolute; + top: 1px; + left: -8px; + } } .mx_ThreadPanel_Header_FilterOptionItem { @@ -77,31 +114,33 @@ limitations under the License. flex-grow: 1; justify-content: space-between; flex-direction: column; - overflow: visible; - width: 100%; - padding: 20px; - padding-left: 30px; + padding: 10px 20px 10px 30px; position: relative; &:hover { background-color: $event-selected-color; } &[aria-selected="true"] { - &::before { + :first-child { + margin-left: -20px; + } + :first-child::before { content: ""; width: 12px; height: 12px; - grid-column: 1; - grid-row: 1; + margin-right: 8px; mask-image: url("$(res)/img/feather-customised/check.svg"); mask-size: 100%; mask-repeat: no-repeat; - position: absolute; - top: 22px; - left: 10px; background-color: $primary-content; + display: inline-block; + vertical-align: middle; } } + + :last-child { + color: $secondary-content; + } } } @@ -131,24 +170,20 @@ limitations under the License. } .mx_AutoHideScrollbar { - border-radius: 8px; - } - - .mx_RoomView_messageListWrapper { + background: #fff; background-color: $background; - padding: 8px; - border-radius: inherit; + border-radius: 8px; + width: calc(100% - 16px); + padding-right: 16px; } - .mx_ScrollPanel { - .mx_RoomView_MessageList { - padding: 0; - } + .mx_RoomView_MessageList { + padding-left: 12px; + padding-right: 0; } .mx_EventTile, .mx_EventListSummary { // Account for scrollbar when hovering - width: calc(100% - 3px); margin: 0 2px; padding-top: 0; @@ -170,19 +205,28 @@ limitations under the License. .mx_DateSeparator { display: none; } + + &.mx_EventTile_clamp:hover { + cursor: pointer; + } + } + + .mx_EventTile:not([data-layout=bubble]) { + .mx_EventTile_e2eIcon { + left: 8px; + } } .mx_MessageComposer { background-color: $background; border-radius: 8px; margin-top: 8px; - width: calc(100% - 8px); padding: 0 8px; box-sizing: border-box; } .mx_ThreadPanel_dropdown { - padding: 4px 8px; + padding: 3px 8px; border-radius: 4px; line-height: 1.5; user-select: none; @@ -207,6 +251,36 @@ limitations under the License. .mx_ThreadPanel_dropdown[aria-expanded=true]::before { transform: rotate(180deg); } + + .mx_MessageTimestamp { + font-size: $font-12px; + } +} + +.mx_ThreadPanel_replies { + margin-top: 8px; +} + +.mx_ThreadPanel_repliesSummary { + &::before { + content: ""; + mask-image: url('$(res)/img/element-icons/thread-summary.svg'); + mask-position: center; + display: inline-block; + height: 18px; + min-width: 18px; + background-color: currentColor; + mask-repeat: no-repeat; + mask-size: contain; + margin-right: 8px; + vertical-align: middle; + } + + color: $secondary-content; + font-weight: 600; + float: left; + margin-right: 12px; + font-size: $font-12px; } .mx_ThreadPanel_viewInRoom::before { diff --git a/res/css/views/rooms/_EventTile.scss b/res/css/views/rooms/_EventTile.scss index 002ff1540c..2ca72d6a43 100644 --- a/res/css/views/rooms/_EventTile.scss +++ b/res/css/views/rooms/_EventTile.scss @@ -460,6 +460,16 @@ $left-gutter: 64px; } } +.mx_EventTile_clamp { + .mx_EventTile_body { + -webkit-line-clamp: 2; + -webkit-box-orient: vertical; + overflow: hidden; + text-overflow: ellipsis; + display: -webkit-box; + } +} + .mx_EventTile_content .markdown-body { font-family: inherit !important; white-space: normal !important; @@ -663,7 +673,10 @@ $left-gutter: 64px; } .mx_ThreadInfo { - height: 35px; + min-width: 267px; + max-width: min(calc(100% - 64px), 600px); + width: auto; + height: 40px; position: relative; background-color: $system; padding-left: 12px; @@ -671,13 +684,13 @@ $left-gutter: 64px; align-items: center; border-radius: 8px; padding-right: 16px; - padding-top: 8px; - padding-bottom: 8px; + margin-top: 8px; font-size: $font-12px; color: $secondary-content; box-sizing: border-box; justify-content: flex-start; clear: both; + overflow: hidden; &:hover { cursor: pointer; @@ -687,6 +700,44 @@ $left-gutter: 64px; padding-left: 11px; padding-right: 15px; } + + &::before { + content: ""; + mask-image: url('$(res)/img/element-icons/thread-summary.svg'); + mask-position: center; + height: 18px; + min-width: 18px; + background-color: $secondary-content; + mask-repeat: no-repeat; + mask-size: contain; + } + + &::after { + content: "›"; + position: absolute; + top: 0; + right: 0; + bottom: 0; + width: 60px; + padding: 0 10px; + font-size: 15px; + line-height: 39px; + box-sizing: border-box; + + text-align: right; + font-weight: 600; + + background: linear-gradient(270deg, $system 52.6%, transparent 100%); + + opacity: 0; + transform: translateX(20px); + transition: all .1s ease-in-out; + } + + &:hover::after { + opacity: 1; + transform: translateX(0); + } } .mx_ThreadInfo_content { @@ -703,15 +754,6 @@ $left-gutter: 64px; float: left; } -.mx_ThreadInfo_thread-icon { - mask-image: url('$(res)/img/element-icons/thread-summary.svg'); - mask-position: center; - height: 16px; - min-width: 16px; - background-color: $secondary-content; - mask-repeat: no-repeat; - mask-size: contain; -} .mx_ThreadInfo_threads-amount { font-weight: 600; position: relative; @@ -720,10 +762,10 @@ $left-gutter: 64px; } .mx_EventTile[data-shape=thread_list] { - --topOffset: 24px; + --topOffset: 20px; --leftOffset: 46px; - margin: var(--topOffset) 0; + margin: var(--topOffset) 16px var(--topOffset) 0; border-radius: 8px; &:hover { @@ -819,6 +861,7 @@ $left-gutter: 64px; left: auto; right: 2px !important; top: 1px !important; + font-size: 1rem; } .mx_ReactionsRow { @@ -830,7 +873,8 @@ $left-gutter: 64px; } } - .mx_EventTile_content { + .mx_EventTile_content, + .mx_RedactedBody { margin-left: 36px; margin-right: 50px; } diff --git a/res/img/element-icons/message/overflow-large.svg b/res/img/element-icons/message/overflow-large.svg new file mode 100644 index 0000000000..65a52e4aa2 --- /dev/null +++ b/res/img/element-icons/message/overflow-large.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/components/structures/RightPanel.tsx b/src/components/structures/RightPanel.tsx index 16ef891850..570865731f 100644 --- a/src/components/structures/RightPanel.tsx +++ b/src/components/structures/RightPanel.tsx @@ -355,7 +355,9 @@ export default class RightPanel extends React.Component { panel = ; + onClose={this.onClose} + permalinkCreator={this.props.permalinkCreator} + />; break; case RightPanelPhases.RoomSummary: diff --git a/src/components/structures/ThreadPanel.tsx b/src/components/structures/ThreadPanel.tsx index f55a479c3f..3754e0d364 100644 --- a/src/components/structures/ThreadPanel.tsx +++ b/src/components/structures/ThreadPanel.tsx @@ -20,24 +20,24 @@ import { EventTimelineSet } from 'matrix-js-sdk/src/models/event-timeline-set'; import { Room } from 'matrix-js-sdk/src/models/room'; import BaseCard from "../views/right_panel/BaseCard"; -import { RightPanelPhases } from "../../stores/RightPanelStorePhases"; - import ResizeNotifier from '../../utils/ResizeNotifier'; import MatrixClientContext from '../../contexts/MatrixClientContext'; import { _t } from '../../languageHandler'; import { ContextMenuButton } from '../../accessibility/context_menu/ContextMenuButton'; -import ContextMenu, { useContextMenu } from './ContextMenu'; +import ContextMenu, { ChevronFace, useContextMenu } from './ContextMenu'; import RoomContext, { TimelineRenderingType } from '../../contexts/RoomContext'; import TimelinePanel from './TimelinePanel'; import { Layout } from '../../settings/Layout'; import { useEventEmitter } from '../../hooks/useEventEmitter'; import AccessibleButton from '../views/elements/AccessibleButton'; import { TileShape } from '../views/rooms/EventTile'; +import { RoomPermalinkCreator } from '../../utils/permalinks/Permalinks'; interface IProps { roomId: string; onClose: () => void; resizeNotifier: ResizeNotifier; + permalinkCreator: RoomPermalinkCreator; } export enum ThreadFilterType { @@ -162,7 +162,13 @@ export const ThreadPanelHeader = ({ filterOption, setFilterOption }: { }} isSelected={opt === value} />); - const contextMenu = menuDisplayed ? + const contextMenu = menuDisplayed ? { contextMenuOptions } : null; return
@@ -174,7 +180,7 @@ export const ThreadPanelHeader = ({ filterOption, setFilterOption }: {
; }; -const ThreadPanel: React.FC = ({ roomId, onClose }) => { +const ThreadPanel: React.FC = ({ roomId, onClose, permalinkCreator }) => { const mxClient = useContext(MatrixClientContext); const roomContext = useContext(RoomContext); const room = mxClient.getRoom(roomId); @@ -200,7 +206,7 @@ const ThreadPanel: React.FC = ({ roomId, onClose }) => { header={} className="mx_ThreadPanel" onClose={onClose} - previousPhase={RightPanelPhases.RoomSummary} + withoutScrollContainer={true} > = ({ roomId, onClose }) => { showReactions={true} className="mx_RoomView_messagePanel mx_GroupLayout" membersLoaded={true} + permalinkCreator={permalinkCreator} tileShape={TileShape.ThreadPanel} /> diff --git a/src/components/structures/ThreadView.tsx b/src/components/structures/ThreadView.tsx index 04c7ff576c..c07741510a 100644 --- a/src/components/structures/ThreadView.tsx +++ b/src/components/structures/ThreadView.tsx @@ -40,7 +40,7 @@ import RoomContext, { TimelineRenderingType } from '../../contexts/RoomContext'; import ContentMessages from '../../ContentMessages'; import UploadBar from './UploadBar'; import { _t } from '../../languageHandler'; -import { ThreadListContextMenu } from '../views/context_menus/ThreadListContextMenu'; +import ThreadListContextMenu from '../views/context_menus/ThreadListContextMenu'; interface IProps { room: Room; @@ -214,6 +214,7 @@ export default class ThreadView extends React.Component { className="mx_ThreadView mx_ThreadPanel" onClose={this.props.onClose} previousPhase={RightPanelPhases.ThreadPanel} + previousPhaseLabel={_t("All threads")} withoutScrollContainer={true} header={this.renderThreadViewHeader()} > diff --git a/src/components/views/context_menus/ThreadListContextMenu.tsx b/src/components/views/context_menus/ThreadListContextMenu.tsx index 38fbcda5fc..e01834b03a 100644 --- a/src/components/views/context_menus/ThreadListContextMenu.tsx +++ b/src/components/views/context_menus/ThreadListContextMenu.tsx @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -import React, { useCallback, useState } from "react"; +import React, { useCallback, useEffect, useState } from "react"; import { MatrixEvent } from "matrix-js-sdk/src"; import { ButtonEvent } from "../elements/AccessibleButton"; import dis from '../../../dispatcher/dispatcher'; @@ -27,17 +27,18 @@ import IconizedContextMenu, { IconizedContextMenuOption, IconizedContextMenuOpti interface IProps { mxEvent: MatrixEvent; permalinkCreator: RoomPermalinkCreator; + onMenuToggle?: (open: boolean) => void; } const contextMenuBelow = (elementRect: DOMRect) => { // align the context menu's icons with the icon which opened the context menu const left = elementRect.left + window.pageXOffset + elementRect.width; - const top = elementRect.bottom + window.pageYOffset + 17; + const top = elementRect.bottom + window.pageYOffset; const chevronFace = ChevronFace.None; return { left, top, chevronFace }; }; -export const ThreadListContextMenu: React.FC = ({ mxEvent, permalinkCreator }) => { +const ThreadListContextMenu: React.FC = ({ mxEvent, permalinkCreator, onMenuToggle }) => { const [optionsPosition, setOptionsPosition] = useState(null); const closeThreadOptions = useCallback(() => { setOptionsPosition(null); @@ -72,6 +73,12 @@ export const ThreadListContextMenu: React.FC = ({ mxEvent, permalinkCrea } }, [closeThreadOptions, optionsPosition]); + useEffect(() => { + if (onMenuToggle) { + onMenuToggle(!!optionsPosition); + } + }, [optionsPosition, onMenuToggle]); + return @@ -327,7 +327,7 @@ export default class MessageActionBar extends React.PureComponent); diff --git a/src/components/views/right_panel/BaseCard.tsx b/src/components/views/right_panel/BaseCard.tsx index 54bf6e769c..4b18c63e12 100644 --- a/src/components/views/right_panel/BaseCard.tsx +++ b/src/components/views/right_panel/BaseCard.tsx @@ -31,6 +31,7 @@ interface IProps { className?: string; withoutScrollContainer?: boolean; previousPhase?: RightPanelPhases; + previousPhaseLabel?: string; closeLabel?: string; onClose?(): void; refireParams?; @@ -56,6 +57,7 @@ const BaseCard: React.FC = ({ footer, withoutScrollContainer, previousPhase, + previousPhaseLabel, children, refireParams, }) => { @@ -68,7 +70,8 @@ const BaseCard: React.FC = ({ refireParams: refireParams, }); }; - backButton = ; + const label = previousPhaseLabel ?? _t("Back"); + backButton = ; } let closeButton; diff --git a/src/components/views/right_panel/RoomHeaderButtons.tsx b/src/components/views/right_panel/RoomHeaderButtons.tsx index fabe46c115..3261cacc81 100644 --- a/src/components/views/right_panel/RoomHeaderButtons.tsx +++ b/src/components/views/right_panel/RoomHeaderButtons.tsx @@ -33,6 +33,7 @@ import { useSettingValue } from "../../../hooks/useSettings"; import { useReadPinnedEvents, usePinnedEvents } from './PinnedMessagesCard'; import { dispatchShowThreadsPanelEvent } from "../../../dispatcher/dispatch-actions/threads"; import SettingsStore from "../../../settings/SettingsStore"; +import dis from "../../../dispatcher/dispatcher"; const ROOM_INFO_PHASES = [ RightPanelPhases.RoomSummary, @@ -72,6 +73,11 @@ interface IProps { @replaceableComponent("views.right_panel.RoomHeaderButtons") export default class RoomHeaderButtons extends HeaderButtons { + private static readonly THREAD_PHASES = [ + RightPanelPhases.ThreadPanel, + RightPanelPhases.ThreadView, + ]; + constructor(props: IProps) { super(props, HeaderKind.Room); } @@ -117,6 +123,17 @@ export default class RoomHeaderButtons extends HeaderButtons { this.setPhase(RightPanelPhases.PinnedMessages); }; + private onThreadsPanelClicked = () => { + if (RoomHeaderButtons.THREAD_PHASES.includes(this.state.phase)) { + dis.dispatch({ + action: Action.ToggleRightPanel, + type: "room", + }); + } else { + dispatchShowThreadsPanelEvent(); + } + }; + public renderButtons() { return <> { { SettingsStore.getValue("feature_thread") && } = ({ room, onClose }) => { - { SettingsStore.getValue("feature_thread") && ( - - ) } diff --git a/src/components/views/rooms/EventTile.tsx b/src/components/views/rooms/EventTile.tsx index f6cfa4c352..9a163aebc1 100644 --- a/src/components/views/rooms/EventTile.tsx +++ b/src/components/views/rooms/EventTile.tsx @@ -67,7 +67,7 @@ import { MediaEventHelper } from "../../../utils/MediaEventHelper"; import Toolbar from '../../../accessibility/Toolbar'; import { POLL_START_EVENT_TYPE } from '../../../polls/consts'; import { RovingAccessibleTooltipButton } from '../../../accessibility/roving/RovingAccessibleTooltipButton'; -import { ThreadListContextMenu } from '../context_menus/ThreadListContextMenu'; +import ThreadListContextMenu from '../context_menus/ThreadListContextMenu'; const eventTileTypes = { [EventType.RoomMessage]: 'messages.MessageEvent', @@ -552,7 +552,7 @@ export default class EventTile extends React.Component { } }; - private renderThreadLastMessagePreview(): JSX.Element | null { + private get thread(): Thread | null { if (!SettingsStore.getValue("feature_thread")) { return null; } @@ -570,7 +570,28 @@ export default class EventTile extends React.Component { return null; } - const [lastEvent] = thread.events + return thread; + } + + private renderThreadPanelSummary(): JSX.Element | null { + if (!this.thread) { + return null; + } + + return
+ + { this.thread.length } + + { this.renderThreadLastMessagePreview() } +
; + } + + private renderThreadLastMessagePreview(): JSX.Element | null { + if (!this.thread) { + return null; + } + + const [lastEvent] = this.thread.events .filter(event => event.isThreadRelation) .slice(-1); const threadMessagePreview = MessagePreviewStore.instance.generatePreviewForEvent(lastEvent); @@ -590,24 +611,7 @@ export default class EventTile extends React.Component { } private renderThreadInfo(): React.ReactNode { - if (!SettingsStore.getValue("feature_thread")) { - return null; - } - - /** - * Accessing the threads value through the room due to a race condition - * that will be solved when there are proper backend support for threads - * We currently have no reliable way to discover than an event is a thread - * when we are at the sync stage - */ - const room = MatrixClientPeg.get().getRoom(this.props.mxEvent.getRoomId()); - const thread = room?.threads.get(this.props.mxEvent.getId()); - - if (thread && !thread.ready) { - thread.addEvent(this.props.mxEvent, true); - } - - if (!thread || this.props.showThreadInfo === false || thread.length === 0) { + if (!this.thread) { return null; } @@ -620,10 +624,9 @@ export default class EventTile extends React.Component { ); }} > - { _t("%(count)s reply", { - count: thread.length, + count: this.thread.length, }) } { this.renderThreadLastMessagePreview() } @@ -1063,6 +1066,7 @@ export default class EventTile extends React.Component { mx_EventTile_bad: isEncryptionFailure, mx_EventTile_emote: msgtype === 'm.emote', mx_EventTile_noSender: this.props.hideSender, + mx_EventTile_clamp: this.props.tileShape === TileShape.ThreadPanel, }); // If the tile is in the Sending state, don't speak the message. @@ -1161,11 +1165,16 @@ export default class EventTile extends React.Component { || this.state.hover || this.state.actionBarFocused); + // Thread panel shows the timestamp of the last reply in that thread + const ts = this.props.tileShape !== TileShape.ThreadPanel + ? this.props.mxEvent.getTs() + : this.props.mxEvent.getThread().lastReply.getTs(); + const timestamp = showTimestamp ? : null; const keyRequestHelpText = @@ -1337,11 +1346,15 @@ export default class EventTile extends React.Component { "data-has-reply": !!replyChain, "onMouseEnter": () => this.setState({ hover: true }), "onMouseLeave": () => this.setState({ hover: false }), - "onClick": () => dispatchShowThreadEvent(this.props.mxEvent), + }, <> { sender } { avatar } -
+
dispatchShowThreadEvent(this.props.mxEvent)} + key="mx_EventTile_line" + > { linkedTimestamp } { this.renderE2EPadlock() } { replyChain } @@ -1359,19 +1372,21 @@ export default class EventTile extends React.Component { tileShape={this.props.tileShape} /> { keyRequestInfo } - - dispatchShowThreadEvent(this.props.mxEvent)} - key="thread" - /> - - - { this.renderThreadLastMessagePreview() } + { this.renderThreadPanelSummary() }
+ + dispatchShowThreadEvent(this.props.mxEvent)} + key="thread" + /> + + { msgOption } ) ); diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 86d77489cb..a25ee37386 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -1573,7 +1573,7 @@ "Key request sent.": "Key request sent.", "Re-request encryption keys from your other sessions.": "Re-request encryption keys from your other sessions.", "Message Actions": "Message Actions", - "Thread": "Thread", + "Reply in thread": "Reply in thread", "This message cannot be decrypted": "This message cannot be decrypted", "Encrypted by an unverified session": "Encrypted by an unverified session", "Unencrypted": "Unencrypted", @@ -1864,7 +1864,6 @@ "%(count)s people|one": "%(count)s person", "Show files": "Show files", "Export chat": "Export chat", - "Show threads": "Show threads", "Share room": "Share room", "Room settings": "Room settings", "Trusted": "Trusted", @@ -3012,6 +3011,7 @@ "All threads": "All threads", "Shows all threads from current room": "Shows all threads from current room", "Show:": "Show:", + "Thread": "Thread", "Tried to load a specific point in this room's timeline, but you do not have permission to view the message in question.": "Tried to load a specific point in this room's timeline, but you do not have permission to view the message in question.", "Tried to load a specific point in this room's timeline, but was unable to find it.": "Tried to load a specific point in this room's timeline, but was unable to find it.", "Failed to load timeline position": "Failed to load timeline position",