Consolidate TileShape into TimelineRenderingType (#7843)

This commit is contained in:
Michael Telatynski 2022-02-18 15:56:05 +00:00 committed by GitHub
parent ca89d3b96e
commit 5f5bb4a4fe
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
21 changed files with 174 additions and 154 deletions

View file

@ -56,7 +56,7 @@ $left-gutter: 64px;
font-size: $font-14px; font-size: $font-14px;
position: relative; position: relative;
&[data-shape=thread_list][data-notification]::before { &[data-shape=ThreadsList][data-notification]::before {
content: ""; content: "";
position: absolute; position: absolute;
width: 8px; width: 8px;
@ -67,11 +67,11 @@ $left-gutter: 64px;
left: auto; left: auto;
} }
&[data-shape=thread_list][data-notification=total]::before { &[data-shape=ThreadsList][data-notification=total]::before {
background-color: $roomtile-default-badge-bg-color; background-color: $roomtile-default-badge-bg-color;
} }
&[data-shape=thread_list][data-notification=highlight]::before { &[data-shape=ThreadsList][data-notification=highlight]::before {
background-color: $alert; background-color: $alert;
} }
@ -797,7 +797,7 @@ $left-gutter: 64px;
white-space: nowrap; white-space: nowrap;
} }
.mx_EventTile[data-shape=thread_list] { .mx_EventTile[data-shape=ThreadsList] {
--topOffset: 20px; --topOffset: 20px;
--leftOffset: 46px; --leftOffset: 46px;

View file

@ -33,7 +33,6 @@ import { replaceableComponent } from "../../utils/replaceableComponent";
import ResizeNotifier from '../../utils/ResizeNotifier'; import ResizeNotifier from '../../utils/ResizeNotifier';
import TimelinePanel from "./TimelinePanel"; import TimelinePanel from "./TimelinePanel";
import Spinner from "../views/elements/Spinner"; import Spinner from "../views/elements/Spinner";
import { TileShape } from '../views/rooms/EventTile';
import { Layout } from "../../settings/enums/Layout"; import { Layout } from "../../settings/enums/Layout";
import RoomContext, { TimelineRenderingType } from '../../contexts/RoomContext'; import RoomContext, { TimelineRenderingType } from '../../contexts/RoomContext';
@ -270,7 +269,6 @@ class FilePanel extends React.Component<IProps, IState> {
timelineSet={this.state.timelineSet} timelineSet={this.state.timelineSet}
showUrlPreview={false} showUrlPreview={false}
onPaginationRequest={this.onPaginationRequest} onPaginationRequest={this.onPaginationRequest}
tileShape={TileShape.FileGrid}
resizeNotifier={this.props.resizeNotifier} resizeNotifier={this.props.resizeNotifier}
empty={emptyState} empty={emptyState}
layout={Layout.Group} layout={Layout.Group}

View file

@ -29,7 +29,7 @@ import SettingsStore from '../../settings/SettingsStore';
import RoomContext, { TimelineRenderingType } from "../../contexts/RoomContext"; import RoomContext, { TimelineRenderingType } from "../../contexts/RoomContext";
import { Layout } from "../../settings/enums/Layout"; import { Layout } from "../../settings/enums/Layout";
import { _t } from "../../languageHandler"; import { _t } from "../../languageHandler";
import EventTile, { haveTileForEvent, IReadReceiptProps, TileShape } from "../views/rooms/EventTile"; import EventTile, { haveTileForEvent, IReadReceiptProps } from "../views/rooms/EventTile";
import { hasText } from "../../TextForEvent"; import { hasText } from "../../TextForEvent";
import IRCTimelineProfileResizer from "../views/elements/IRCTimelineProfileResizer"; import IRCTimelineProfileResizer from "../views/elements/IRCTimelineProfileResizer";
import DMRoomMap from "../../utils/DMRoomMap"; import DMRoomMap from "../../utils/DMRoomMap";
@ -144,9 +144,6 @@ interface IProps {
// className for the panel // className for the panel
className: string; className: string;
// shape parameter to be passed to EventTiles
tileShape?: TileShape;
// show twelve hour timestamps // show twelve hour timestamps
isTwelveHour?: boolean; isTwelveHour?: boolean;
@ -802,7 +799,6 @@ export default class MessagePanel extends React.Component<IProps, IState> {
showUrlPreview={this.props.showUrlPreview} showUrlPreview={this.props.showUrlPreview}
checkUnmounting={this.isUnmounting} checkUnmounting={this.isUnmounting}
eventSendStatus={mxEv.getAssociatedStatus()} eventSendStatus={mxEv.getAssociatedStatus()}
tileShape={this.props.tileShape}
isTwelveHour={this.props.isTwelveHour} isTwelveHour={this.props.isTwelveHour}
permalinkCreator={this.props.permalinkCreator} permalinkCreator={this.props.permalinkCreator}
last={last} last={last}
@ -994,7 +990,10 @@ export default class MessagePanel extends React.Component<IProps, IState> {
const style = this.props.hidden ? { display: 'none' } : {}; const style = this.props.hidden ? { display: 'none' } : {};
let whoIsTyping; let whoIsTyping;
if (this.props.room && !this.props.tileShape && this.state.showTypingNotifications) { if (this.props.room &&
this.state.showTypingNotifications &&
this.context.timelineRenderingType === TimelineRenderingType.Room
) {
whoIsTyping = (<WhoIsTypingTile whoIsTyping = (<WhoIsTypingTile
room={this.props.room} room={this.props.room}
onShown={this.onTypingShown} onShown={this.onTypingShown}

View file

@ -23,7 +23,6 @@ import BaseCard from "../views/right_panel/BaseCard";
import { replaceableComponent } from "../../utils/replaceableComponent"; import { replaceableComponent } from "../../utils/replaceableComponent";
import TimelinePanel from "./TimelinePanel"; import TimelinePanel from "./TimelinePanel";
import Spinner from "../views/elements/Spinner"; import Spinner from "../views/elements/Spinner";
import { TileShape } from "../views/rooms/EventTile";
import { Layout } from "../../settings/enums/Layout"; import { Layout } from "../../settings/enums/Layout";
import RoomContext, { TimelineRenderingType } from "../../contexts/RoomContext"; import RoomContext, { TimelineRenderingType } from "../../contexts/RoomContext";
@ -53,7 +52,6 @@ export default class NotificationPanel extends React.PureComponent<IProps> {
manageReadMarkers={false} manageReadMarkers={false}
timelineSet={timelineSet} timelineSet={timelineSet}
showUrlPreview={false} showUrlPreview={false}
tileShape={TileShape.Notif}
empty={emptyState} empty={emptyState}
alwaysShowTimestamps={true} alwaysShowTimestamps={true}
layout={Layout.Group} layout={Layout.Group}

View file

@ -36,7 +36,6 @@ import ContextMenu, { ChevronFace, MenuItemRadio, useContextMenu } from './Conte
import RoomContext, { TimelineRenderingType } from '../../contexts/RoomContext'; import RoomContext, { TimelineRenderingType } from '../../contexts/RoomContext';
import TimelinePanel from './TimelinePanel'; import TimelinePanel from './TimelinePanel';
import { Layout } from '../../settings/enums/Layout'; import { Layout } from '../../settings/enums/Layout';
import { TileShape } from '../views/rooms/EventTile';
import { RoomPermalinkCreator } from '../../utils/permalinks/Permalinks'; import { RoomPermalinkCreator } from '../../utils/permalinks/Permalinks';
import { useEventEmitter } from '../../hooks/useEventEmitter'; import { useEventEmitter } from '../../hooks/useEventEmitter';
@ -302,7 +301,6 @@ const ThreadPanel: React.FC<IProps> = ({ roomId, onClose, permalinkCreator }) =>
className="mx_RoomView_messagePanel mx_GroupLayout" className="mx_RoomView_messagePanel mx_GroupLayout"
membersLoaded={true} membersLoaded={true}
permalinkCreator={permalinkCreator} permalinkCreator={permalinkCreator}
tileShape={TileShape.ThreadPanel}
disableGrouping={true} disableGrouping={true}
/> />
) } ) }

View file

@ -28,7 +28,6 @@ import BaseCard from "../views/right_panel/BaseCard";
import { RightPanelPhases } from "../../stores/right-panel/RightPanelStorePhases"; import { RightPanelPhases } from "../../stores/right-panel/RightPanelStorePhases";
import { replaceableComponent } from "../../utils/replaceableComponent"; import { replaceableComponent } from "../../utils/replaceableComponent";
import ResizeNotifier from '../../utils/ResizeNotifier'; import ResizeNotifier from '../../utils/ResizeNotifier';
import { TileShape } from '../views/rooms/EventTile';
import MessageComposer from '../views/rooms/MessageComposer'; import MessageComposer from '../views/rooms/MessageComposer';
import { RoomPermalinkCreator } from '../../utils/permalinks/Permalinks'; import { RoomPermalinkCreator } from '../../utils/permalinks/Permalinks';
import { Layout } from '../../settings/enums/Layout'; import { Layout } from '../../settings/enums/Layout';
@ -284,7 +283,6 @@ export default class ThreadView extends React.Component<IProps, IState> {
sendReadReceiptOnLoad={true} sendReadReceiptOnLoad={true}
timelineSet={this.state?.thread?.timelineSet} timelineSet={this.state?.thread?.timelineSet}
showUrlPreview={true} showUrlPreview={true}
tileShape={TileShape.Thread}
// ThreadView doesn't support IRC layout at this time // ThreadView doesn't support IRC layout at this time
layout={this.state.layout === Layout.Bubble ? Layout.Bubble : Layout.Group} layout={this.state.layout === Layout.Bubble ? Layout.Bubble : Layout.Group}
hideThreadedMessages={false} hideThreadedMessages={false}

View file

@ -39,7 +39,7 @@ import { Action } from '../../dispatcher/actions';
import { Key } from '../../Keyboard'; import { Key } from '../../Keyboard';
import Timer from '../../utils/Timer'; import Timer from '../../utils/Timer';
import shouldHideEvent from '../../shouldHideEvent'; import shouldHideEvent from '../../shouldHideEvent';
import { haveTileForEvent, TileShape } from "../views/rooms/EventTile"; import { haveTileForEvent } from "../views/rooms/EventTile";
import { UIFeature } from "../../settings/UIFeature"; import { UIFeature } from "../../settings/UIFeature";
import { replaceableComponent } from "../../utils/replaceableComponent"; import { replaceableComponent } from "../../utils/replaceableComponent";
import { arrayFastClone } from "../../utils/arrays"; import { arrayFastClone } from "../../utils/arrays";
@ -103,9 +103,6 @@ interface IProps {
// classname to use for the messagepanel // classname to use for the messagepanel
className?: string; className?: string;
// shape property to be passed to EventTiles
tileShape?: TileShape;
// placeholder to use if the timeline is empty // placeholder to use if the timeline is empty
empty?: ReactNode; empty?: ReactNode;
@ -1644,7 +1641,6 @@ class TimelinePanel extends React.Component<IProps, IState> {
this.state.alwaysShowTimestamps this.state.alwaysShowTimestamps
} }
className={this.props.className} className={this.props.className}
tileShape={this.props.tileShape}
resizeNotifier={this.props.resizeNotifier} resizeNotifier={this.props.resizeNotifier}
getRelationsForEvent={this.getRelationsForEvent} getRelationsForEvent={this.getRelationsForEvent}
editState={this.props.editState} editState={this.props.editState}

View file

@ -18,7 +18,6 @@ import React, { ReactNode } from "react";
import { logger } from "matrix-js-sdk/src/logger"; import { logger } from "matrix-js-sdk/src/logger";
import { Playback, PlaybackState } from "../../../audio/Playback"; import { Playback, PlaybackState } from "../../../audio/Playback";
import { TileShape } from "../rooms/EventTile";
import { UPDATE_EVENT } from "../../../stores/AsyncStore"; import { UPDATE_EVENT } from "../../../stores/AsyncStore";
import { replaceableComponent } from "../../../utils/replaceableComponent"; import { replaceableComponent } from "../../../utils/replaceableComponent";
import { _t } from "../../../languageHandler"; import { _t } from "../../../languageHandler";
@ -29,7 +28,6 @@ interface IProps {
playback: Playback; playback: Playback;
mediaName?: string; mediaName?: string;
tileShape?: TileShape;
} }
interface IState { interface IState {

View file

@ -19,16 +19,19 @@ import React, { ReactNode } from "react";
import PlayPauseButton from "./PlayPauseButton"; import PlayPauseButton from "./PlayPauseButton";
import PlaybackClock from "./PlaybackClock"; import PlaybackClock from "./PlaybackClock";
import { replaceableComponent } from "../../../utils/replaceableComponent"; import { replaceableComponent } from "../../../utils/replaceableComponent";
import { TileShape } from "../rooms/EventTile";
import PlaybackWaveform from "./PlaybackWaveform"; import PlaybackWaveform from "./PlaybackWaveform";
import AudioPlayerBase from "./AudioPlayerBase"; import AudioPlayerBase from "./AudioPlayerBase";
import RoomContext, { TimelineRenderingType } from "../../../contexts/RoomContext";
@replaceableComponent("views.audio_messages.RecordingPlayback") @replaceableComponent("views.audio_messages.RecordingPlayback")
export default class RecordingPlayback extends AudioPlayerBase { export default class RecordingPlayback extends AudioPlayerBase {
static contextType = RoomContext;
public context!: React.ContextType<typeof RoomContext>;
private get isWaveformable(): boolean { private get isWaveformable(): boolean {
return this.props.tileShape !== TileShape.Notif return this.context.timelineRenderingType !== TimelineRenderingType.Notification
&& this.props.tileShape !== TileShape.FileGrid && this.context.timelineRenderingType !== TimelineRenderingType.File
&& this.props.tileShape !== TileShape.Pinned; && this.context.timelineRenderingType !== TimelineRenderingType.Pinned;
} }
protected renderComponent(): ReactNode { protected renderComponent(): ReactNode {

View file

@ -17,7 +17,6 @@ limitations under the License.
import { MatrixEvent } from "matrix-js-sdk/src/models/event"; import { MatrixEvent } from "matrix-js-sdk/src/models/event";
import { Relations } from "matrix-js-sdk/src/models/relations"; import { Relations } from "matrix-js-sdk/src/models/relations";
import { TileShape } from "../rooms/EventTile";
import { MediaEventHelper } from "../../../utils/MediaEventHelper"; import { MediaEventHelper } from "../../../utils/MediaEventHelper";
import EditorStateTransfer from "../../../utils/EditorStateTransfer"; import EditorStateTransfer from "../../../utils/EditorStateTransfer";
import { RoomPermalinkCreator } from "../../../utils/permalinks/Permalinks"; import { RoomPermalinkCreator } from "../../../utils/permalinks/Permalinks";
@ -36,7 +35,6 @@ export interface IBodyProps {
showUrlPreview?: boolean; showUrlPreview?: boolean;
forExport?: boolean; forExport?: boolean;
tileShape: TileShape;
maxImageHeight?: number; maxImageHeight?: number;
replacingEventId?: string; replacingEventId?: string;
editState?: EditorStateTransfer; editState?: EditorStateTransfer;

View file

@ -28,6 +28,7 @@ import { IBodyProps } from "./IBodyProps";
import { PlaybackManager } from "../../../audio/PlaybackManager"; import { PlaybackManager } from "../../../audio/PlaybackManager";
import { isVoiceMessage } from "../../../utils/EventUtils"; import { isVoiceMessage } from "../../../utils/EventUtils";
import { PlaybackQueue } from "../../../audio/PlaybackQueue"; import { PlaybackQueue } from "../../../audio/PlaybackQueue";
import RoomContext, { TimelineRenderingType } from "../../../contexts/RoomContext";
interface IState { interface IState {
error?: Error; error?: Error;
@ -36,6 +37,9 @@ interface IState {
@replaceableComponent("views.messages.MAudioBody") @replaceableComponent("views.messages.MAudioBody")
export default class MAudioBody extends React.PureComponent<IBodyProps, IState> { export default class MAudioBody extends React.PureComponent<IBodyProps, IState> {
static contextType = RoomContext;
public context!: React.ContextType<typeof RoomContext>;
constructor(props: IBodyProps) { constructor(props: IBodyProps) {
super(props); super(props);
@ -82,6 +86,12 @@ export default class MAudioBody extends React.PureComponent<IBodyProps, IState>
this.state.playback?.destroy(); this.state.playback?.destroy();
} }
protected get showFileBody(): boolean {
return this.context.timelineRenderingType !== TimelineRenderingType.Room &&
this.context.timelineRenderingType !== TimelineRenderingType.Pinned &&
this.context.timelineRenderingType !== TimelineRenderingType.Search;
}
public render() { public render() {
if (this.state.error) { if (this.state.error) {
return ( return (
@ -115,7 +125,7 @@ export default class MAudioBody extends React.PureComponent<IBodyProps, IState>
return ( return (
<span className="mx_MAudioBody"> <span className="mx_MAudioBody">
<AudioPlayer playback={this.state.playback} mediaName={this.props.mxEvent.getContent().body} /> <AudioPlayer playback={this.state.playback} mediaName={this.props.mxEvent.getContent().body} />
{ this.props.tileShape && <MFileBody {...this.props} showGenericPlaceholder={false} /> } { this.showFileBody && <MFileBody {...this.props} showGenericPlaceholder={false} /> }
</span> </span>
); );
} }

View file

@ -24,12 +24,12 @@ import AccessibleButton from "../elements/AccessibleButton";
import { replaceableComponent } from "../../../utils/replaceableComponent"; import { replaceableComponent } from "../../../utils/replaceableComponent";
import { mediaFromContent } from "../../../customisations/Media"; import { mediaFromContent } from "../../../customisations/Media";
import ErrorDialog from "../dialogs/ErrorDialog"; import ErrorDialog from "../dialogs/ErrorDialog";
import { TileShape } from "../rooms/EventTile";
import { presentableTextForFile } from "../../../utils/FileUtils"; import { presentableTextForFile } from "../../../utils/FileUtils";
import { IMediaEventContent } from "../../../customisations/models/IMediaEventContent"; import { IMediaEventContent } from "../../../customisations/models/IMediaEventContent";
import { IBodyProps } from "./IBodyProps"; import { IBodyProps } from "./IBodyProps";
import { FileDownloader } from "../../../utils/FileDownloader"; import { FileDownloader } from "../../../utils/FileDownloader";
import TextWithTooltip from "../elements/TextWithTooltip"; import TextWithTooltip from "../elements/TextWithTooltip";
import RoomContext, { TimelineRenderingType } from "../../../contexts/RoomContext";
export let DOWNLOAD_ICON_URL; // cached copy of the download.svg asset for the sandboxed iframe later on export let DOWNLOAD_ICON_URL; // cached copy of the download.svg asset for the sandboxed iframe later on
@ -108,6 +108,9 @@ interface IState {
@replaceableComponent("views.messages.MFileBody") @replaceableComponent("views.messages.MFileBody")
export default class MFileBody extends React.Component<IProps, IState> { export default class MFileBody extends React.Component<IProps, IState> {
static contextType = RoomContext;
public context!: React.ContextType<typeof RoomContext>;
static defaultProps = { static defaultProps = {
showGenericPlaceholder: true, showGenericPlaceholder: true,
}; };
@ -223,8 +226,15 @@ export default class MFileBody extends React.Component<IProps, IState> {
</span>; </span>;
} }
const showDownloadLink = (this.props.tileShape || !this.props.showGenericPlaceholder) && let showDownloadLink = !this.props.showGenericPlaceholder || (
this.props.tileShape !== TileShape.Thread; this.context.timelineRenderingType !== TimelineRenderingType.Room &&
this.context.timelineRenderingType !== TimelineRenderingType.Search &&
this.context.timelineRenderingType !== TimelineRenderingType.Pinned
);
if (this.context.timelineRenderingType === TimelineRenderingType.Thread) {
showDownloadLink = false;
}
if (isEncrypted) { if (isEncrypted) {
if (!this.state.decryptedBlob) { if (!this.state.decryptedBlob) {
@ -334,9 +344,11 @@ export default class MFileBody extends React.Component<IProps, IState> {
<span className="mx_MFileBody_download_icon" /> <span className="mx_MFileBody_download_icon" />
{ _t("Download %(text)s", { text: this.linkText }) } { _t("Download %(text)s", { text: this.linkText }) }
</a> </a>
{ this.props.tileShape === TileShape.FileGrid && <div className="mx_MImageBody_size"> { this.context.timelineRenderingType === TimelineRenderingType.File && (
<div className="mx_MImageBody_size">
{ this.content.info && this.content.info.size ? filesize(this.content.info.size) : "" } { this.content.info && this.content.info.size ? filesize(this.content.info.size) : "" }
</div> } </div>
) }
</div> } </div> }
</span> </span>
); );

View file

@ -26,7 +26,6 @@ import MFileBody from './MFileBody';
import Modal from '../../../Modal'; import Modal from '../../../Modal';
import { _t } from '../../../languageHandler'; import { _t } from '../../../languageHandler';
import SettingsStore from "../../../settings/SettingsStore"; import SettingsStore from "../../../settings/SettingsStore";
import MatrixClientContext from "../../../contexts/MatrixClientContext";
import InlineSpinner from '../elements/InlineSpinner'; import InlineSpinner from '../elements/InlineSpinner';
import { replaceableComponent } from "../../../utils/replaceableComponent"; import { replaceableComponent } from "../../../utils/replaceableComponent";
import { Media, mediaFromContent } from "../../../customisations/Media"; import { Media, mediaFromContent } from "../../../customisations/Media";
@ -34,8 +33,9 @@ import { BLURHASH_FIELD } from "../../../ContentMessages";
import { IMediaEventContent } from '../../../customisations/models/IMediaEventContent'; import { IMediaEventContent } from '../../../customisations/models/IMediaEventContent';
import ImageView from '../elements/ImageView'; import ImageView from '../elements/ImageView';
import { IBodyProps } from "./IBodyProps"; import { IBodyProps } from "./IBodyProps";
import { TileShape } from '../rooms/EventTile';
import { ImageSize, suggestedSize as suggestedImageSize } from "../../../settings/enums/ImageSize"; import { ImageSize, suggestedSize as suggestedImageSize } from "../../../settings/enums/ImageSize";
import { MatrixClientPeg } from '../../../MatrixClientPeg';
import RoomContext, { TimelineRenderingType } from "../../../contexts/RoomContext";
interface IState { interface IState {
decryptedUrl?: string; decryptedUrl?: string;
@ -55,7 +55,9 @@ interface IState {
@replaceableComponent("views.messages.MImageBody") @replaceableComponent("views.messages.MImageBody")
export default class MImageBody extends React.Component<IBodyProps, IState> { export default class MImageBody extends React.Component<IBodyProps, IState> {
static contextType = MatrixClientContext; static contextType = RoomContext;
public context!: React.ContextType<typeof RoomContext>;
private unmounted = true; private unmounted = true;
private image = createRef<HTMLImageElement>(); private image = createRef<HTMLImageElement>();
private timeout?: number; private timeout?: number;
@ -297,7 +299,7 @@ export default class MImageBody extends React.Component<IBodyProps, IState> {
componentDidMount() { componentDidMount() {
this.unmounted = false; this.unmounted = false;
this.context.on('sync', this.onClientSync); MatrixClientPeg.get().on('sync', this.onClientSync);
const showImage = this.state.showImage || const showImage = this.state.showImage ||
localStorage.getItem("mx_ShowImage_" + this.props.mxEvent.getId()) === "true"; localStorage.getItem("mx_ShowImage_" + this.props.mxEvent.getId()) === "true";
@ -327,7 +329,7 @@ export default class MImageBody extends React.Component<IBodyProps, IState> {
componentWillUnmount() { componentWillUnmount() {
this.unmounted = true; this.unmounted = true;
this.context.removeListener('sync', this.onClientSync); MatrixClientPeg.get().removeListener('sync', this.onClientSync);
this.clearBlurhashTimeout(); this.clearBlurhashTimeout();
SettingsStore.unwatchSetting(this.sizeWatcher); SettingsStore.unwatchSetting(this.sizeWatcher);
} }
@ -524,9 +526,11 @@ export default class MImageBody extends React.Component<IBodyProps, IState> {
* In the room timeline or the thread context we don't need the download * In the room timeline or the thread context we don't need the download
* link as the message action bar will fullfil that * link as the message action bar will fullfil that
*/ */
const hasMessageActionBar = !this.props.tileShape const hasMessageActionBar = this.context.timelineRenderingType === TimelineRenderingType.Room
|| this.props.tileShape === TileShape.Thread || this.context.timelineRenderingType === TimelineRenderingType.Pinned
|| this.props.tileShape === TileShape.ThreadPanel; || this.context.timelineRenderingType === TimelineRenderingType.Search
|| this.context.timelineRenderingType === TimelineRenderingType.Thread
|| this.context.timelineRenderingType === TimelineRenderingType.ThreadsList;
if (!hasMessageActionBar) { if (!hasMessageActionBar) {
return <MFileBody {...this.props} showGenericPlaceholder={false} />; return <MFileBody {...this.props} showGenericPlaceholder={false} />;
} }

View file

@ -28,6 +28,7 @@ import { IMediaEventContent } from "../../../customisations/models/IMediaEventCo
import { IBodyProps } from "./IBodyProps"; import { IBodyProps } from "./IBodyProps";
import MFileBody from "./MFileBody"; import MFileBody from "./MFileBody";
import { ImageSize, suggestedSize as suggestedVideoSize } from "../../../settings/enums/ImageSize"; import { ImageSize, suggestedSize as suggestedVideoSize } from "../../../settings/enums/ImageSize";
import RoomContext, { TimelineRenderingType } from "../../../contexts/RoomContext";
interface IState { interface IState {
decryptedUrl?: string; decryptedUrl?: string;
@ -41,6 +42,9 @@ interface IState {
@replaceableComponent("views.messages.MVideoBody") @replaceableComponent("views.messages.MVideoBody")
export default class MVideoBody extends React.PureComponent<IBodyProps, IState> { export default class MVideoBody extends React.PureComponent<IBodyProps, IState> {
static contextType = RoomContext;
public context!: React.ContextType<typeof RoomContext>;
private videoRef = React.createRef<HTMLVideoElement>(); private videoRef = React.createRef<HTMLVideoElement>();
private sizeWatcher: string; private sizeWatcher: string;
@ -235,9 +239,15 @@ export default class MVideoBody extends React.PureComponent<IBodyProps, IState>
this.props.onHeightChanged(); this.props.onHeightChanged();
}; };
protected get showFileBody(): boolean {
return this.context.timelineRenderingType !== TimelineRenderingType.Room &&
this.context.timelineRenderingType !== TimelineRenderingType.Pinned &&
this.context.timelineRenderingType !== TimelineRenderingType.Search;
}
private getFileBody = () => { private getFileBody = () => {
if (this.props.forExport) return null; if (this.props.forExport) return null;
return this.props.tileShape && <MFileBody {...this.props} showGenericPlaceholder={false} />; return this.showFileBody && <MFileBody {...this.props} showGenericPlaceholder={false} />;
}; };
render() { render() {

View file

@ -47,8 +47,8 @@ export default class MVoiceMessageBody extends MAudioBody {
// At this point we should have a playable state // At this point we should have a playable state
return ( return (
<span className="mx_MVoiceMessageBody"> <span className="mx_MVoiceMessageBody">
<RecordingPlayback playback={this.state.playback} tileShape={this.props.tileShape} /> <RecordingPlayback playback={this.state.playback} />
{ this.props.tileShape && <MFileBody {...this.props} showGenericPlaceholder={false} /> } { this.showFileBody && <MFileBody {...this.props} showGenericPlaceholder={false} /> }
</span> </span>
); );
} }

View file

@ -161,7 +161,6 @@ export default class MessageEvent extends React.Component<IProps> implements IMe
highlights={this.props.highlights} highlights={this.props.highlights}
highlightLink={this.props.highlightLink} highlightLink={this.props.highlightLink}
showUrlPreview={this.props.showUrlPreview} showUrlPreview={this.props.showUrlPreview}
tileShape={this.props.tileShape}
forExport={this.props.forExport} forExport={this.props.forExport}
maxImageHeight={this.props.maxImageHeight} maxImageHeight={this.props.maxImageHeight}
replacingEventId={this.props.replacingEventId} replacingEventId={this.props.replacingEventId}

View file

@ -24,7 +24,7 @@ import { getUserNameColorClass } from '../../../utils/FormattingUtils';
import MatrixClientContext from "../../../contexts/MatrixClientContext"; import MatrixClientContext from "../../../contexts/MatrixClientContext";
import { replaceableComponent } from "../../../utils/replaceableComponent"; import { replaceableComponent } from "../../../utils/replaceableComponent";
import UserIdentifier from '../../../customisations/UserIdentifier'; import UserIdentifier from '../../../customisations/UserIdentifier';
import { TileShape } from '../rooms/EventTile'; import RoomContext, { TimelineRenderingType } from '../../../contexts/RoomContext';
import SettingsStore from "../../../settings/SettingsStore"; import SettingsStore from "../../../settings/SettingsStore";
import { MatrixClientPeg } from "../../../MatrixClientPeg"; import { MatrixClientPeg } from "../../../MatrixClientPeg";
@ -32,7 +32,6 @@ interface IProps {
mxEvent: MatrixEvent; mxEvent: MatrixEvent;
onClick?(): void; onClick?(): void;
enableFlair: boolean; enableFlair: boolean;
tileShape?: TileShape;
} }
interface IState { interface IState {
@ -121,7 +120,11 @@ export default class SenderProfile extends React.Component<IProps, IState> {
const displayName = member?.rawDisplayName || mxEvent.getSender() || ""; const displayName = member?.rawDisplayName || mxEvent.getSender() || "";
const mxid = member?.userId || mxEvent.getSender() || ""; const mxid = member?.userId || mxEvent.getSender() || "";
if (msgtype === MsgType.Emote && this.props.tileShape !== TileShape.ThreadPanel) { return <RoomContext.Consumer>
{ roomContext => {
if (msgtype === MsgType.Emote &&
roomContext.timelineRenderingType !== TimelineRenderingType.ThreadsList
) {
return null; // emote message must include the name so don't duplicate it return null; // emote message must include the name so don't duplicate it
} }
@ -142,10 +145,7 @@ export default class SenderProfile extends React.Component<IProps, IState> {
this.state.userGroups, this.state.relatedGroups, this.state.userGroups, this.state.relatedGroups,
); );
flair = <Flair key='flair' flair = <Flair key='flair' userId={mxEvent.getSender()} groups={displayedGroups} />;
userId={mxEvent.getSender()}
groups={displayedGroups}
/>;
} }
return ( return (
@ -157,5 +157,7 @@ export default class SenderProfile extends React.Component<IProps, IState> {
{ flair } { flair }
</div> </div>
); );
} }
</RoomContext.Consumer>;
} }
} }

View file

@ -29,6 +29,7 @@ import PinningUtils from "../../../utils/PinningUtils";
import { useAsyncMemo } from "../../../hooks/useAsyncMemo"; import { useAsyncMemo } from "../../../hooks/useAsyncMemo";
import PinnedEventTile from "../rooms/PinnedEventTile"; import PinnedEventTile from "../rooms/PinnedEventTile";
import { useRoomState } from "../../../hooks/useRoomState"; import { useRoomState } from "../../../hooks/useRoomState";
import RoomContext, { TimelineRenderingType } from "../../../contexts/RoomContext";
interface IProps { interface IProps {
room: Room; room: Room;
@ -78,6 +79,7 @@ export const useReadPinnedEvents = (room: Room): Set<string> => {
const PinnedMessagesCard = ({ room, onClose }: IProps) => { const PinnedMessagesCard = ({ room, onClose }: IProps) => {
const cli = useContext(MatrixClientContext); const cli = useContext(MatrixClientContext);
const roomContext = useContext(RoomContext);
const canUnpin = useRoomState(room, state => state.mayClientSendStateEvent(EventType.RoomPinnedEvents, cli)); const canUnpin = useRoomState(room, state => state.mayClientSendStateEvent(EventType.RoomPinnedEvents, cli));
const pinnedEventIds = usePinnedEvents(room); const pinnedEventIds = usePinnedEvents(room);
const readPinnedEvents = useReadPinnedEvents(room); const readPinnedEvents = useReadPinnedEvents(room);
@ -166,7 +168,12 @@ const PinnedMessagesCard = ({ room, onClose }: IProps) => {
className="mx_PinnedMessagesCard" className="mx_PinnedMessagesCard"
onClose={onClose} onClose={onClose}
> >
<RoomContext.Provider value={{
...roomContext,
timelineRenderingType: TimelineRenderingType.Pinned,
}}>
{ content } { content }
</RoomContext.Provider>
</BaseCard>; </BaseCard>;
}; };

View file

@ -64,7 +64,7 @@ import SettingsStore from "../../../settings/SettingsStore";
import MKeyVerificationConclusion from "../messages/MKeyVerificationConclusion"; import MKeyVerificationConclusion from "../messages/MKeyVerificationConclusion";
import { showThread } from '../../../dispatcher/dispatch-actions/threads'; import { showThread } from '../../../dispatcher/dispatch-actions/threads';
import { MessagePreviewStore } from '../../../stores/room-list/MessagePreviewStore'; import { MessagePreviewStore } from '../../../stores/room-list/MessagePreviewStore';
import { TimelineRenderingType } from "../../../contexts/RoomContext"; import RoomContext, { TimelineRenderingType } from "../../../contexts/RoomContext";
import { MediaEventHelper } from "../../../utils/MediaEventHelper"; import { MediaEventHelper } from "../../../utils/MediaEventHelper";
import Toolbar from '../../../accessibility/Toolbar'; import Toolbar from '../../../accessibility/Toolbar';
import { RovingAccessibleTooltipButton } from '../../../accessibility/roving/RovingAccessibleTooltipButton'; import { RovingAccessibleTooltipButton } from '../../../accessibility/roving/RovingAccessibleTooltipButton';
@ -213,14 +213,6 @@ export interface IReadReceiptProps {
ts: number; ts: number;
} }
export enum TileShape {
Notif = "notif",
FileGrid = "file_grid",
Pinned = "pinned",
Thread = "thread",
ThreadPanel = "thread_list"
}
interface IProps { interface IProps {
// the MatrixEvent to show // the MatrixEvent to show
mxEvent: MatrixEvent; mxEvent: MatrixEvent;
@ -281,14 +273,6 @@ interface IProps {
// that we can tell when it changes. // that we can tell when it changes.
eventSendStatus?: string; eventSendStatus?: string;
// the shape of the tile. by default, the layout is intended for the
// normal room timeline. alternative values are: "file_list", "file_grid"
// and "notif". This could be done by CSS, but it'd be horribly inefficient.
// It could also be done by subclassing EventTile, but that'd be quite
// boiilerplatey. So just make the necessary render decisions conditional
// for now.
tileShape?: TileShape;
forExport?: boolean; forExport?: boolean;
// show twelve hour timestamps // show twelve hour timestamps
@ -367,6 +351,7 @@ interface IState {
threadNotification?: NotificationCountType; threadNotification?: NotificationCountType;
} }
// MUST be rendered within a RoomContext with a set timelineRenderingType
@replaceableComponent("views.rooms.EventTile") @replaceableComponent("views.rooms.EventTile")
export default class EventTile extends React.Component<IProps, IState> { export default class EventTile extends React.Component<IProps, IState> {
private suppressReadReceiptAnimation: boolean; private suppressReadReceiptAnimation: boolean;
@ -385,13 +370,12 @@ export default class EventTile extends React.Component<IProps, IState> {
layout: Layout.Group, layout: Layout.Group,
}; };
static contextType = MatrixClientContext; static contextType = RoomContext;
public context!: React.ContextType<typeof MatrixClientContext>; public context!: React.ContextType<typeof RoomContext>;
constructor(props: IProps, context: React.ContextType<typeof MatrixClientContext>) { constructor(props: IProps, context: React.ContextType<typeof MatrixClientContext>) {
super(props, context); super(props, context);
this.context = context;
const thread = this.thread; const thread = this.thread;
this.state = { this.state = {
@ -437,12 +421,12 @@ export default class EventTile extends React.Component<IProps, IState> {
if (!this.props.mxEvent) return false; if (!this.props.mxEvent) return false;
// Sanity check (should never happen, but we shouldn't explode if it does) // Sanity check (should never happen, but we shouldn't explode if it does)
const room = this.context.getRoom(this.props.mxEvent.getRoomId()); const room = MatrixClientPeg.get().getRoom(this.props.mxEvent.getRoomId());
if (!room) return false; if (!room) return false;
// Quickly check to see if the event was sent by us. If it wasn't, it won't qualify for // Quickly check to see if the event was sent by us. If it wasn't, it won't qualify for
// special read receipts. // special read receipts.
const myUserId = this.context.getUserId(); const myUserId = MatrixClientPeg.get().getUserId();
if (this.props.mxEvent.getSender() !== myUserId) return false; if (this.props.mxEvent.getSender() !== myUserId) return false;
// Finally, determine if the type is relevant to the user. This notably excludes state // Finally, determine if the type is relevant to the user. This notably excludes state
@ -473,7 +457,7 @@ export default class EventTile extends React.Component<IProps, IState> {
// If anyone has read the event besides us, we don't want to show a sent receipt. // If anyone has read the event besides us, we don't want to show a sent receipt.
const receipts = this.props.readReceipts || []; const receipts = this.props.readReceipts || [];
const myUserId = this.context.getUserId(); const myUserId = MatrixClientPeg.get().getUserId();
if (receipts.some(r => r.userId !== myUserId)) return false; if (receipts.some(r => r.userId !== myUserId)) return false;
// Finally, we should show a receipt. // Finally, we should show a receipt.
@ -501,7 +485,7 @@ export default class EventTile extends React.Component<IProps, IState> {
componentDidMount() { componentDidMount() {
this.suppressReadReceiptAnimation = false; this.suppressReadReceiptAnimation = false;
const client = this.context; const client = MatrixClientPeg.get();
if (!this.props.forExport) { if (!this.props.forExport) {
client.on("deviceVerificationChanged", this.onDeviceVerificationChanged); client.on("deviceVerificationChanged", this.onDeviceVerificationChanged);
client.on("userTrustStatusChanged", this.onUserVerificationChanged); client.on("userTrustStatusChanged", this.onUserVerificationChanged);
@ -526,12 +510,12 @@ export default class EventTile extends React.Component<IProps, IState> {
} }
} }
const room = this.context.getRoom(this.props.mxEvent.getRoomId()); const room = MatrixClientPeg.get().getRoom(this.props.mxEvent.getRoomId());
room?.on(ThreadEvent.New, this.onNewThread); room?.on(ThreadEvent.New, this.onNewThread);
} }
private setupNotificationListener = (thread: Thread): void => { private setupNotificationListener = (thread: Thread): void => {
const room = this.context.getRoom(this.props.mxEvent.getRoomId()); const room = MatrixClientPeg.get().getRoom(this.props.mxEvent.getRoomId());
const notifications = RoomNotificationStateStore.instance.getThreadsRoomState(room); const notifications = RoomNotificationStateStore.instance.getThreadsRoomState(room);
this.threadState = notifications.getThreadRoomState(thread); this.threadState = notifications.getThreadRoomState(thread);
@ -592,7 +576,7 @@ export default class EventTile extends React.Component<IProps, IState> {
} }
componentWillUnmount() { componentWillUnmount() {
const client = this.context; const client = MatrixClientPeg.get();
client.removeListener("deviceVerificationChanged", this.onDeviceVerificationChanged); client.removeListener("deviceVerificationChanged", this.onDeviceVerificationChanged);
client.removeListener("userTrustStatusChanged", this.onUserVerificationChanged); client.removeListener("userTrustStatusChanged", this.onUserVerificationChanged);
client.removeListener("Room.receipt", this.onRoomReceipt); client.removeListener("Room.receipt", this.onRoomReceipt);
@ -606,7 +590,7 @@ export default class EventTile extends React.Component<IProps, IState> {
this.props.mxEvent.off(ThreadEvent.Update, this.updateThread); this.props.mxEvent.off(ThreadEvent.Update, this.updateThread);
} }
const room = this.context.getRoom(this.props.mxEvent.getRoomId()); const room = MatrixClientPeg.get().getRoom(this.props.mxEvent.getRoomId());
room?.off(ThreadEvent.New, this.onNewThread); room?.off(ThreadEvent.New, this.onNewThread);
if (this.threadState) { if (this.threadState) {
this.threadState.off(NotificationStateEvents.Update, this.onThreadStateUpdate); this.threadState.off(NotificationStateEvents.Update, this.onThreadStateUpdate);
@ -616,7 +600,7 @@ export default class EventTile extends React.Component<IProps, IState> {
componentDidUpdate(prevProps: IProps, prevState: IState, snapshot) { componentDidUpdate(prevProps: IProps, prevState: IState, snapshot) {
// If we're not listening for receipts and expect to be, register a listener. // If we're not listening for receipts and expect to be, register a listener.
if (!this.isListeningForReceipts && (this.shouldShowSentReceipt || this.shouldShowSendingReceipt)) { if (!this.isListeningForReceipts && (this.shouldShowSentReceipt || this.shouldShowSendingReceipt)) {
this.context.on("Room.receipt", this.onRoomReceipt); MatrixClientPeg.get().on("Room.receipt", this.onRoomReceipt);
this.isListeningForReceipts = true; this.isListeningForReceipts = true;
} }
} }
@ -624,7 +608,7 @@ export default class EventTile extends React.Component<IProps, IState> {
private onNewThread = (thread: Thread) => { private onNewThread = (thread: Thread) => {
if (thread.id === this.props.mxEvent.getId()) { if (thread.id === this.props.mxEvent.getId()) {
this.updateThread(thread); this.updateThread(thread);
const room = this.context.getRoom(this.props.mxEvent.getRoomId()); const room = MatrixClientPeg.get().getRoom(this.props.mxEvent.getRoomId());
room.off(ThreadEvent.New, this.onNewThread); room.off(ThreadEvent.New, this.onNewThread);
} }
}; };
@ -734,7 +718,7 @@ export default class EventTile extends React.Component<IProps, IState> {
private onRoomReceipt = (ev: MatrixEvent, room: Room): void => { private onRoomReceipt = (ev: MatrixEvent, room: Room): void => {
// ignore events for other rooms // ignore events for other rooms
const tileRoom = this.context.getRoom(this.props.mxEvent.getRoomId()); const tileRoom = MatrixClientPeg.get().getRoom(this.props.mxEvent.getRoomId());
if (room !== tileRoom) return; if (room !== tileRoom) return;
if (!this.shouldShowSentReceipt && !this.shouldShowSendingReceipt && !this.isListeningForReceipts) { if (!this.shouldShowSentReceipt && !this.shouldShowSendingReceipt && !this.isListeningForReceipts) {
@ -746,7 +730,7 @@ export default class EventTile extends React.Component<IProps, IState> {
this.forceUpdate(() => { this.forceUpdate(() => {
// Per elsewhere in this file, we can remove the listener once we will have no further purpose for it. // Per elsewhere in this file, we can remove the listener once we will have no further purpose for it.
if (!this.shouldShowSentReceipt && !this.shouldShowSendingReceipt) { if (!this.shouldShowSentReceipt && !this.shouldShowSendingReceipt) {
this.context.removeListener("Room.receipt", this.onRoomReceipt); MatrixClientPeg.get().removeListener("Room.receipt", this.onRoomReceipt);
this.isListeningForReceipts = false; this.isListeningForReceipts = false;
} }
}); });
@ -779,9 +763,9 @@ export default class EventTile extends React.Component<IProps, IState> {
return; return;
} }
const encryptionInfo = this.context.getEventEncryptionInfo(mxEvent); const encryptionInfo = MatrixClientPeg.get().getEventEncryptionInfo(mxEvent);
const senderId = mxEvent.getSender(); const senderId = mxEvent.getSender();
const userTrust = this.context.checkUserTrust(senderId); const userTrust = MatrixClientPeg.get().checkUserTrust(senderId);
if (encryptionInfo.mismatchedSender) { if (encryptionInfo.mismatchedSender) {
// something definitely wrong is going on here // something definitely wrong is going on here
@ -799,7 +783,7 @@ export default class EventTile extends React.Component<IProps, IState> {
return; return;
} }
const eventSenderTrust = encryptionInfo.sender && this.context.checkDeviceTrust( const eventSenderTrust = encryptionInfo.sender && MatrixClientPeg.get().checkDeviceTrust(
senderId, encryptionInfo.sender.deviceId, senderId, encryptionInfo.sender.deviceId,
); );
if (!eventSenderTrust) { if (!eventSenderTrust) {
@ -878,14 +862,16 @@ export default class EventTile extends React.Component<IProps, IState> {
private shouldHighlight(): boolean { private shouldHighlight(): boolean {
if (this.props.forExport) return false; if (this.props.forExport) return false;
if (this.props.tileShape === TileShape.Notif) return false; if (this.context.timelineRenderingType === TimelineRenderingType.Notification) return false;
if (this.props.tileShape === TileShape.ThreadPanel) return false; if (this.context.timelineRenderingType === TimelineRenderingType.ThreadsList) return false;
const actions = this.context.getPushActionsForEvent(this.props.mxEvent.replacingEvent() || this.props.mxEvent); const actions = MatrixClientPeg.get().getPushActionsForEvent(
this.props.mxEvent.replacingEvent() || this.props.mxEvent,
);
if (!actions || !actions.tweaks) { return false; } if (!actions || !actions.tweaks) { return false; }
// don't show self-highlights from another of our clients // don't show self-highlights from another of our clients
if (this.props.mxEvent.getSender() === this.context.credentials.userId) { if (this.props.mxEvent.getSender() === MatrixClientPeg.get().credentials.userId) {
return false; return false;
} }
@ -1013,7 +999,7 @@ export default class EventTile extends React.Component<IProps, IState> {
// Cancel any outgoing key request for this event and resend it. If a response // Cancel any outgoing key request for this event and resend it. If a response
// is received for the request with the required keys, the event could be // is received for the request with the required keys, the event could be
// decrypted successfully. // decrypted successfully.
this.context.cancelAndResendEventRoomKeyRequest(this.props.mxEvent); MatrixClientPeg.get().cancelAndResendEventRoomKeyRequest(this.props.mxEvent);
}; };
private onPermalinkClicked = e => { private onPermalinkClicked = e => {
@ -1054,7 +1040,7 @@ export default class EventTile extends React.Component<IProps, IState> {
} }
} }
if (this.context.isRoomEncrypted(ev.getRoomId())) { if (MatrixClientPeg.get().isRoomEncrypted(ev.getRoomId())) {
// else if room is encrypted // else if room is encrypted
// and event is being encrypted or is not_sent (Unknown Devices/Network Error) // and event is being encrypted or is not_sent (Unknown Devices/Network Error)
if (ev.status === EventStatus.ENCRYPTING) { if (ev.status === EventStatus.ENCRYPTING) {
@ -1169,7 +1155,10 @@ export default class EventTile extends React.Component<IProps, IState> {
const isEncryptionFailure = this.props.mxEvent.isDecryptionFailure(); const isEncryptionFailure = this.props.mxEvent.isDecryptionFailure();
let isContinuation = this.props.continuation; let isContinuation = this.props.continuation;
if (this.props.tileShape && this.props.layout !== Layout.Bubble) { if (this.context.timelineRenderingType !== TimelineRenderingType.Room &&
this.context.timelineRenderingType !== TimelineRenderingType.Search &&
this.props.layout !== Layout.Bubble
) {
isContinuation = false; isContinuation = false;
} }
@ -1196,7 +1185,7 @@ export default class EventTile extends React.Component<IProps, IState> {
mx_EventTile_bad: isEncryptionFailure, mx_EventTile_bad: isEncryptionFailure,
mx_EventTile_emote: msgtype === MsgType.Emote, mx_EventTile_emote: msgtype === MsgType.Emote,
mx_EventTile_noSender: this.props.hideSender, mx_EventTile_noSender: this.props.hideSender,
mx_EventTile_clamp: this.props.tileShape === TileShape.ThreadPanel, mx_EventTile_clamp: this.context.timelineRenderingType === TimelineRenderingType.ThreadsList,
mx_EventTile_noBubble: noBubbleEvent, mx_EventTile_noBubble: noBubbleEvent,
}); });
@ -1219,7 +1208,9 @@ export default class EventTile extends React.Component<IProps, IState> {
let avatarSize; let avatarSize;
let needsSenderProfile; let needsSenderProfile;
if (this.props.tileShape === TileShape.Notif || this.props.tileShape === TileShape.ThreadPanel) { if (this.context.timelineRenderingType === TimelineRenderingType.Notification ||
this.context.timelineRenderingType === TimelineRenderingType.ThreadsList
) {
avatarSize = 24; avatarSize = 24;
needsSenderProfile = true; needsSenderProfile = true;
} else if (tileHandler === 'messages.RoomCreate' || isBubbleMessage) { } else if (tileHandler === 'messages.RoomCreate' || isBubbleMessage) {
@ -1234,7 +1225,7 @@ export default class EventTile extends React.Component<IProps, IState> {
avatarSize = 14; avatarSize = 14;
needsSenderProfile = true; needsSenderProfile = true;
} else if ( } else if (
(this.props.continuation && this.props.tileShape !== TileShape.FileGrid) || (this.props.continuation && this.context.timelineRenderingType !== TimelineRenderingType.File) ||
eventType === EventType.CallInvite eventType === EventType.CallInvite
) { ) {
// no avatar or sender profile for continuation messages and call tiles // no avatar or sender profile for continuation messages and call tiles
@ -1269,17 +1260,20 @@ export default class EventTile extends React.Component<IProps, IState> {
} }
if (needsSenderProfile && this.props.hideSender !== true) { if (needsSenderProfile && this.props.hideSender !== true) {
if (!this.props.tileShape || this.props.tileShape === TileShape.Thread) { if (this.context.timelineRenderingType === TimelineRenderingType.Room ||
sender = <SenderProfile onClick={this.onSenderProfileClick} this.context.timelineRenderingType === TimelineRenderingType.Search ||
this.context.timelineRenderingType === TimelineRenderingType.Pinned ||
this.context.timelineRenderingType === TimelineRenderingType.Thread
) {
sender = <SenderProfile
onClick={this.onSenderProfileClick}
mxEvent={this.props.mxEvent} mxEvent={this.props.mxEvent}
enableFlair={this.props.enableFlair} enableFlair={this.props.enableFlair}
tileShape={this.props.tileShape}
/>; />;
} else { } else {
sender = <SenderProfile sender = <SenderProfile
mxEvent={this.props.mxEvent} mxEvent={this.props.mxEvent}
enableFlair={this.props.enableFlair} enableFlair={this.props.enableFlair}
tileShape={this.props.tileShape}
/>; />;
} }
} }
@ -1303,16 +1297,16 @@ export default class EventTile extends React.Component<IProps, IState> {
|| this.state.hover || this.state.hover
|| this.state.actionBarFocused); || this.state.actionBarFocused);
const room = this.context.getRoom(this.props.mxEvent.getRoomId()); const room = MatrixClientPeg.get().getRoom(this.props.mxEvent.getRoomId());
const thread = room?.findThreadForEvent?.(this.props.mxEvent); const thread = room?.findThreadForEvent?.(this.props.mxEvent);
// Thread panel shows the timestamp of the last reply in that thread // Thread panel shows the timestamp of the last reply in that thread
const ts = this.props.tileShape !== TileShape.ThreadPanel const ts = this.context.timelineRenderingType !== TimelineRenderingType.ThreadsList
? this.props.mxEvent.getTs() ? this.props.mxEvent.getTs()
: thread?.replyToEvent.getTs(); : thread?.replyToEvent.getTs();
const messageTimestamp = <MessageTimestamp const messageTimestamp = <MessageTimestamp
showRelative={this.props.tileShape === TileShape.ThreadPanel} showRelative={this.context.timelineRenderingType === TimelineRenderingType.ThreadsList}
showTwelveHour={this.props.isTwelveHour} showTwelveHour={this.props.isTwelveHour}
ts={ts} ts={ts}
/>; />;
@ -1392,7 +1386,7 @@ export default class EventTile extends React.Component<IProps, IState> {
msgOption = readAvatars; msgOption = readAvatars;
} }
const renderTarget = this.props.tileShape === TileShape.Thread const renderTarget = this.context.timelineRenderingType === TimelineRenderingType.Thread
? RelationType.Thread ? RelationType.Thread
: undefined; : undefined;
@ -1412,11 +1406,11 @@ export default class EventTile extends React.Component<IProps, IState> {
/> />
: null; : null;
const isOwnEvent = this.props.mxEvent?.sender?.userId === this.context.getUserId(); const isOwnEvent = this.props.mxEvent?.sender?.userId === MatrixClientPeg.get().getUserId();
switch (this.props.tileShape) { switch (this.context.timelineRenderingType) {
case TileShape.Notif: { case TimelineRenderingType.Notification: {
const room = this.context.getRoom(this.props.mxEvent.getRoomId()); const room = MatrixClientPeg.get().getRoom(this.props.mxEvent.getRoomId());
return React.createElement(this.props.as || "li", { return React.createElement(this.props.as || "li", {
"className": classes, "className": classes,
"aria-live": ariaLive, "aria-live": ariaLive,
@ -1443,7 +1437,6 @@ export default class EventTile extends React.Component<IProps, IState> {
highlightLink={this.props.highlightLink} highlightLink={this.props.highlightLink}
showUrlPreview={this.props.showUrlPreview} showUrlPreview={this.props.showUrlPreview}
onHeightChanged={this.props.onHeightChanged} onHeightChanged={this.props.onHeightChanged}
tileShape={this.props.tileShape}
editState={this.props.editState} editState={this.props.editState}
getRelationsForEvent={this.props.getRelationsForEvent} getRelationsForEvent={this.props.getRelationsForEvent}
isSeeingThroughMessageHiddenForModeration={isSeeingThroughMessageHiddenForModeration} isSeeingThroughMessageHiddenForModeration={isSeeingThroughMessageHiddenForModeration}
@ -1451,8 +1444,8 @@ export default class EventTile extends React.Component<IProps, IState> {
</div>, </div>,
]); ]);
} }
case TileShape.Thread: { case TimelineRenderingType.Thread: {
const room = this.context.getRoom(this.props.mxEvent.getRoomId()); const room = MatrixClientPeg.get().getRoom(this.props.mxEvent.getRoomId());
return React.createElement(this.props.as || "li", { return React.createElement(this.props.as || "li", {
"ref": this.ref, "ref": this.ref,
"className": classes, "className": classes,
@ -1485,7 +1478,6 @@ export default class EventTile extends React.Component<IProps, IState> {
highlightLink={this.props.highlightLink} highlightLink={this.props.highlightLink}
showUrlPreview={this.props.showUrlPreview} showUrlPreview={this.props.showUrlPreview}
onHeightChanged={this.props.onHeightChanged} onHeightChanged={this.props.onHeightChanged}
tileShape={this.props.tileShape}
editState={this.props.editState} editState={this.props.editState}
replacingEventId={this.props.replacingEventId} replacingEventId={this.props.replacingEventId}
getRelationsForEvent={this.props.getRelationsForEvent} getRelationsForEvent={this.props.getRelationsForEvent}
@ -1497,7 +1489,7 @@ export default class EventTile extends React.Component<IProps, IState> {
reactionsRow, reactionsRow,
]); ]);
} }
case TileShape.ThreadPanel: { case TimelineRenderingType.ThreadsList: {
// tab-index=-1 to allow it to be focusable but do not add tab stop for it, primarily for screen readers // tab-index=-1 to allow it to be focusable but do not add tab stop for it, primarily for screen readers
return ( return (
React.createElement(this.props.as || "li", { React.createElement(this.props.as || "li", {
@ -1508,7 +1500,7 @@ export default class EventTile extends React.Component<IProps, IState> {
"aria-atomic": "true", "aria-atomic": "true",
"data-scroll-tokens": scrollToken, "data-scroll-tokens": scrollToken,
"data-layout": this.props.layout, "data-layout": this.props.layout,
"data-shape": this.props.tileShape, "data-shape": this.context.timelineRenderingType,
"data-self": isOwnEvent, "data-self": isOwnEvent,
"data-has-reply": !!replyChain, "data-has-reply": !!replyChain,
"data-notification": this.state.threadNotification, "data-notification": this.state.threadNotification,
@ -1550,7 +1542,7 @@ export default class EventTile extends React.Component<IProps, IState> {
</>) </>)
); );
} }
case TileShape.FileGrid: { case TimelineRenderingType.File: {
return React.createElement(this.props.as || "li", { return React.createElement(this.props.as || "li", {
"className": classes, "className": classes,
"aria-live": ariaLive, "aria-live": ariaLive,
@ -1563,7 +1555,6 @@ export default class EventTile extends React.Component<IProps, IState> {
highlights={this.props.highlights} highlights={this.props.highlights}
highlightLink={this.props.highlightLink} highlightLink={this.props.highlightLink}
showUrlPreview={this.props.showUrlPreview} showUrlPreview={this.props.showUrlPreview}
tileShape={this.props.tileShape}
onHeightChanged={this.props.onHeightChanged} onHeightChanged={this.props.onHeightChanged}
editState={this.props.editState} editState={this.props.editState}
getRelationsForEvent={this.props.getRelationsForEvent} getRelationsForEvent={this.props.getRelationsForEvent}
@ -1584,7 +1575,7 @@ export default class EventTile extends React.Component<IProps, IState> {
]); ]);
} }
default: { default: { // Pinned, Room, Search
// tab-index=-1 to allow it to be focusable but do not add tab stop for it, primarily for screen readers // tab-index=-1 to allow it to be focusable but do not add tab stop for it, primarily for screen readers
return ( return (
React.createElement(this.props.as || "li", { React.createElement(this.props.as || "li", {

View file

@ -30,7 +30,6 @@ import { replaceableComponent } from "../../../utils/replaceableComponent";
import MatrixClientContext from "../../../contexts/MatrixClientContext"; import MatrixClientContext from "../../../contexts/MatrixClientContext";
import { getUserNameColorClass } from "../../../utils/FormattingUtils"; import { getUserNameColorClass } from "../../../utils/FormattingUtils";
import AccessibleTooltipButton from "../elements/AccessibleTooltipButton"; import AccessibleTooltipButton from "../elements/AccessibleTooltipButton";
import { TileShape } from "./EventTile";
import { ViewRoomPayload } from "../../../dispatcher/payloads/ViewRoomPayload"; import { ViewRoomPayload } from "../../../dispatcher/payloads/ViewRoomPayload";
interface IProps { interface IProps {
@ -92,7 +91,6 @@ export default class PinnedEventTile extends React.Component<IProps> {
className="mx_PinnedEventTile_body" className="mx_PinnedEventTile_body"
maxImageHeight={150} maxImageHeight={150}
onHeightChanged={() => {}} // we need to give this, apparently onHeightChanged={() => {}} // we need to give this, apparently
tileShape={TileShape.Pinned}
/> />
</div> </div>

View file

@ -25,7 +25,8 @@ export enum TimelineRenderingType {
ThreadsList = "ThreadsList", ThreadsList = "ThreadsList",
File = "File", File = "File",
Notification = "Notification", Notification = "Notification",
Search = "Search" Search = "Search",
Pinned = "Pinned",
} }
const RoomContext = createContext<IRoomState>({ const RoomContext = createContext<IRoomState>({