Consolidate TileShape into TimelineRenderingType (#7843)
This commit is contained in:
parent
ca89d3b96e
commit
5f5bb4a4fe
21 changed files with 174 additions and 154 deletions
|
@ -64,7 +64,7 @@ import SettingsStore from "../../../settings/SettingsStore";
|
|||
import MKeyVerificationConclusion from "../messages/MKeyVerificationConclusion";
|
||||
import { showThread } from '../../../dispatcher/dispatch-actions/threads';
|
||||
import { MessagePreviewStore } from '../../../stores/room-list/MessagePreviewStore';
|
||||
import { TimelineRenderingType } from "../../../contexts/RoomContext";
|
||||
import RoomContext, { TimelineRenderingType } from "../../../contexts/RoomContext";
|
||||
import { MediaEventHelper } from "../../../utils/MediaEventHelper";
|
||||
import Toolbar from '../../../accessibility/Toolbar';
|
||||
import { RovingAccessibleTooltipButton } from '../../../accessibility/roving/RovingAccessibleTooltipButton';
|
||||
|
@ -213,14 +213,6 @@ export interface IReadReceiptProps {
|
|||
ts: number;
|
||||
}
|
||||
|
||||
export enum TileShape {
|
||||
Notif = "notif",
|
||||
FileGrid = "file_grid",
|
||||
Pinned = "pinned",
|
||||
Thread = "thread",
|
||||
ThreadPanel = "thread_list"
|
||||
}
|
||||
|
||||
interface IProps {
|
||||
// the MatrixEvent to show
|
||||
mxEvent: MatrixEvent;
|
||||
|
@ -281,14 +273,6 @@ interface IProps {
|
|||
// that we can tell when it changes.
|
||||
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;
|
||||
|
||||
// show twelve hour timestamps
|
||||
|
@ -367,6 +351,7 @@ interface IState {
|
|||
threadNotification?: NotificationCountType;
|
||||
}
|
||||
|
||||
// MUST be rendered within a RoomContext with a set timelineRenderingType
|
||||
@replaceableComponent("views.rooms.EventTile")
|
||||
export default class EventTile extends React.Component<IProps, IState> {
|
||||
private suppressReadReceiptAnimation: boolean;
|
||||
|
@ -385,13 +370,12 @@ export default class EventTile extends React.Component<IProps, IState> {
|
|||
layout: Layout.Group,
|
||||
};
|
||||
|
||||
static contextType = MatrixClientContext;
|
||||
public context!: React.ContextType<typeof MatrixClientContext>;
|
||||
static contextType = RoomContext;
|
||||
public context!: React.ContextType<typeof RoomContext>;
|
||||
|
||||
constructor(props: IProps, context: React.ContextType<typeof MatrixClientContext>) {
|
||||
super(props, context);
|
||||
|
||||
this.context = context;
|
||||
const thread = this.thread;
|
||||
|
||||
this.state = {
|
||||
|
@ -437,12 +421,12 @@ export default class EventTile extends React.Component<IProps, IState> {
|
|||
if (!this.props.mxEvent) return false;
|
||||
|
||||
// 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;
|
||||
|
||||
// Quickly check to see if the event was sent by us. If it wasn't, it won't qualify for
|
||||
// special read receipts.
|
||||
const myUserId = this.context.getUserId();
|
||||
const myUserId = MatrixClientPeg.get().getUserId();
|
||||
if (this.props.mxEvent.getSender() !== myUserId) return false;
|
||||
|
||||
// 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.
|
||||
const receipts = this.props.readReceipts || [];
|
||||
const myUserId = this.context.getUserId();
|
||||
const myUserId = MatrixClientPeg.get().getUserId();
|
||||
if (receipts.some(r => r.userId !== myUserId)) return false;
|
||||
|
||||
// Finally, we should show a receipt.
|
||||
|
@ -501,7 +485,7 @@ export default class EventTile extends React.Component<IProps, IState> {
|
|||
|
||||
componentDidMount() {
|
||||
this.suppressReadReceiptAnimation = false;
|
||||
const client = this.context;
|
||||
const client = MatrixClientPeg.get();
|
||||
if (!this.props.forExport) {
|
||||
client.on("deviceVerificationChanged", this.onDeviceVerificationChanged);
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
this.threadState = notifications.getThreadRoomState(thread);
|
||||
|
@ -592,7 +576,7 @@ export default class EventTile extends React.Component<IProps, IState> {
|
|||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
const client = this.context;
|
||||
const client = MatrixClientPeg.get();
|
||||
client.removeListener("deviceVerificationChanged", this.onDeviceVerificationChanged);
|
||||
client.removeListener("userTrustStatusChanged", this.onUserVerificationChanged);
|
||||
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);
|
||||
}
|
||||
|
||||
const room = this.context.getRoom(this.props.mxEvent.getRoomId());
|
||||
const room = MatrixClientPeg.get().getRoom(this.props.mxEvent.getRoomId());
|
||||
room?.off(ThreadEvent.New, this.onNewThread);
|
||||
if (this.threadState) {
|
||||
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) {
|
||||
// If we're not listening for receipts and expect to be, register a listener.
|
||||
if (!this.isListeningForReceipts && (this.shouldShowSentReceipt || this.shouldShowSendingReceipt)) {
|
||||
this.context.on("Room.receipt", this.onRoomReceipt);
|
||||
MatrixClientPeg.get().on("Room.receipt", this.onRoomReceipt);
|
||||
this.isListeningForReceipts = true;
|
||||
}
|
||||
}
|
||||
|
@ -624,7 +608,7 @@ export default class EventTile extends React.Component<IProps, IState> {
|
|||
private onNewThread = (thread: Thread) => {
|
||||
if (thread.id === this.props.mxEvent.getId()) {
|
||||
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);
|
||||
}
|
||||
};
|
||||
|
@ -734,7 +718,7 @@ export default class EventTile extends React.Component<IProps, IState> {
|
|||
|
||||
private onRoomReceipt = (ev: MatrixEvent, room: Room): void => {
|
||||
// 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 (!this.shouldShowSentReceipt && !this.shouldShowSendingReceipt && !this.isListeningForReceipts) {
|
||||
|
@ -746,7 +730,7 @@ export default class EventTile extends React.Component<IProps, IState> {
|
|||
this.forceUpdate(() => {
|
||||
// Per elsewhere in this file, we can remove the listener once we will have no further purpose for it.
|
||||
if (!this.shouldShowSentReceipt && !this.shouldShowSendingReceipt) {
|
||||
this.context.removeListener("Room.receipt", this.onRoomReceipt);
|
||||
MatrixClientPeg.get().removeListener("Room.receipt", this.onRoomReceipt);
|
||||
this.isListeningForReceipts = false;
|
||||
}
|
||||
});
|
||||
|
@ -779,9 +763,9 @@ export default class EventTile extends React.Component<IProps, IState> {
|
|||
return;
|
||||
}
|
||||
|
||||
const encryptionInfo = this.context.getEventEncryptionInfo(mxEvent);
|
||||
const encryptionInfo = MatrixClientPeg.get().getEventEncryptionInfo(mxEvent);
|
||||
const senderId = mxEvent.getSender();
|
||||
const userTrust = this.context.checkUserTrust(senderId);
|
||||
const userTrust = MatrixClientPeg.get().checkUserTrust(senderId);
|
||||
|
||||
if (encryptionInfo.mismatchedSender) {
|
||||
// something definitely wrong is going on here
|
||||
|
@ -799,7 +783,7 @@ export default class EventTile extends React.Component<IProps, IState> {
|
|||
return;
|
||||
}
|
||||
|
||||
const eventSenderTrust = encryptionInfo.sender && this.context.checkDeviceTrust(
|
||||
const eventSenderTrust = encryptionInfo.sender && MatrixClientPeg.get().checkDeviceTrust(
|
||||
senderId, encryptionInfo.sender.deviceId,
|
||||
);
|
||||
if (!eventSenderTrust) {
|
||||
|
@ -878,14 +862,16 @@ export default class EventTile extends React.Component<IProps, IState> {
|
|||
|
||||
private shouldHighlight(): boolean {
|
||||
if (this.props.forExport) return false;
|
||||
if (this.props.tileShape === TileShape.Notif) return false;
|
||||
if (this.props.tileShape === TileShape.ThreadPanel) return false;
|
||||
if (this.context.timelineRenderingType === TimelineRenderingType.Notification) 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; }
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
// is received for the request with the required keys, the event could be
|
||||
// decrypted successfully.
|
||||
this.context.cancelAndResendEventRoomKeyRequest(this.props.mxEvent);
|
||||
MatrixClientPeg.get().cancelAndResendEventRoomKeyRequest(this.props.mxEvent);
|
||||
};
|
||||
|
||||
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
|
||||
// and event is being encrypted or is not_sent (Unknown Devices/Network Error)
|
||||
if (ev.status === EventStatus.ENCRYPTING) {
|
||||
|
@ -1169,7 +1155,10 @@ export default class EventTile extends React.Component<IProps, IState> {
|
|||
const isEncryptionFailure = this.props.mxEvent.isDecryptionFailure();
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -1196,7 +1185,7 @@ export default class EventTile extends React.Component<IProps, IState> {
|
|||
mx_EventTile_bad: isEncryptionFailure,
|
||||
mx_EventTile_emote: msgtype === MsgType.Emote,
|
||||
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,
|
||||
});
|
||||
|
||||
|
@ -1219,7 +1208,9 @@ export default class EventTile extends React.Component<IProps, IState> {
|
|||
let avatarSize;
|
||||
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;
|
||||
needsSenderProfile = true;
|
||||
} else if (tileHandler === 'messages.RoomCreate' || isBubbleMessage) {
|
||||
|
@ -1234,7 +1225,7 @@ export default class EventTile extends React.Component<IProps, IState> {
|
|||
avatarSize = 14;
|
||||
needsSenderProfile = true;
|
||||
} else if (
|
||||
(this.props.continuation && this.props.tileShape !== TileShape.FileGrid) ||
|
||||
(this.props.continuation && this.context.timelineRenderingType !== TimelineRenderingType.File) ||
|
||||
eventType === EventType.CallInvite
|
||||
) {
|
||||
// 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 (!this.props.tileShape || this.props.tileShape === TileShape.Thread) {
|
||||
sender = <SenderProfile onClick={this.onSenderProfileClick}
|
||||
if (this.context.timelineRenderingType === TimelineRenderingType.Room ||
|
||||
this.context.timelineRenderingType === TimelineRenderingType.Search ||
|
||||
this.context.timelineRenderingType === TimelineRenderingType.Pinned ||
|
||||
this.context.timelineRenderingType === TimelineRenderingType.Thread
|
||||
) {
|
||||
sender = <SenderProfile
|
||||
onClick={this.onSenderProfileClick}
|
||||
mxEvent={this.props.mxEvent}
|
||||
enableFlair={this.props.enableFlair}
|
||||
tileShape={this.props.tileShape}
|
||||
/>;
|
||||
} else {
|
||||
sender = <SenderProfile
|
||||
mxEvent={this.props.mxEvent}
|
||||
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.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);
|
||||
|
||||
// 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()
|
||||
: thread?.replyToEvent.getTs();
|
||||
|
||||
const messageTimestamp = <MessageTimestamp
|
||||
showRelative={this.props.tileShape === TileShape.ThreadPanel}
|
||||
showRelative={this.context.timelineRenderingType === TimelineRenderingType.ThreadsList}
|
||||
showTwelveHour={this.props.isTwelveHour}
|
||||
ts={ts}
|
||||
/>;
|
||||
|
@ -1392,7 +1386,7 @@ export default class EventTile extends React.Component<IProps, IState> {
|
|||
msgOption = readAvatars;
|
||||
}
|
||||
|
||||
const renderTarget = this.props.tileShape === TileShape.Thread
|
||||
const renderTarget = this.context.timelineRenderingType === TimelineRenderingType.Thread
|
||||
? RelationType.Thread
|
||||
: undefined;
|
||||
|
||||
|
@ -1412,11 +1406,11 @@ export default class EventTile extends React.Component<IProps, IState> {
|
|||
/>
|
||||
: 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) {
|
||||
case TileShape.Notif: {
|
||||
const room = this.context.getRoom(this.props.mxEvent.getRoomId());
|
||||
switch (this.context.timelineRenderingType) {
|
||||
case TimelineRenderingType.Notification: {
|
||||
const room = MatrixClientPeg.get().getRoom(this.props.mxEvent.getRoomId());
|
||||
return React.createElement(this.props.as || "li", {
|
||||
"className": classes,
|
||||
"aria-live": ariaLive,
|
||||
|
@ -1443,7 +1437,6 @@ export default class EventTile extends React.Component<IProps, IState> {
|
|||
highlightLink={this.props.highlightLink}
|
||||
showUrlPreview={this.props.showUrlPreview}
|
||||
onHeightChanged={this.props.onHeightChanged}
|
||||
tileShape={this.props.tileShape}
|
||||
editState={this.props.editState}
|
||||
getRelationsForEvent={this.props.getRelationsForEvent}
|
||||
isSeeingThroughMessageHiddenForModeration={isSeeingThroughMessageHiddenForModeration}
|
||||
|
@ -1451,8 +1444,8 @@ export default class EventTile extends React.Component<IProps, IState> {
|
|||
</div>,
|
||||
]);
|
||||
}
|
||||
case TileShape.Thread: {
|
||||
const room = this.context.getRoom(this.props.mxEvent.getRoomId());
|
||||
case TimelineRenderingType.Thread: {
|
||||
const room = MatrixClientPeg.get().getRoom(this.props.mxEvent.getRoomId());
|
||||
return React.createElement(this.props.as || "li", {
|
||||
"ref": this.ref,
|
||||
"className": classes,
|
||||
|
@ -1485,7 +1478,6 @@ export default class EventTile extends React.Component<IProps, IState> {
|
|||
highlightLink={this.props.highlightLink}
|
||||
showUrlPreview={this.props.showUrlPreview}
|
||||
onHeightChanged={this.props.onHeightChanged}
|
||||
tileShape={this.props.tileShape}
|
||||
editState={this.props.editState}
|
||||
replacingEventId={this.props.replacingEventId}
|
||||
getRelationsForEvent={this.props.getRelationsForEvent}
|
||||
|
@ -1497,7 +1489,7 @@ export default class EventTile extends React.Component<IProps, IState> {
|
|||
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
|
||||
return (
|
||||
React.createElement(this.props.as || "li", {
|
||||
|
@ -1508,7 +1500,7 @@ export default class EventTile extends React.Component<IProps, IState> {
|
|||
"aria-atomic": "true",
|
||||
"data-scroll-tokens": scrollToken,
|
||||
"data-layout": this.props.layout,
|
||||
"data-shape": this.props.tileShape,
|
||||
"data-shape": this.context.timelineRenderingType,
|
||||
"data-self": isOwnEvent,
|
||||
"data-has-reply": !!replyChain,
|
||||
"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", {
|
||||
"className": classes,
|
||||
"aria-live": ariaLive,
|
||||
|
@ -1563,7 +1555,6 @@ export default class EventTile extends React.Component<IProps, IState> {
|
|||
highlights={this.props.highlights}
|
||||
highlightLink={this.props.highlightLink}
|
||||
showUrlPreview={this.props.showUrlPreview}
|
||||
tileShape={this.props.tileShape}
|
||||
onHeightChanged={this.props.onHeightChanged}
|
||||
editState={this.props.editState}
|
||||
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
|
||||
return (
|
||||
React.createElement(this.props.as || "li", {
|
||||
|
|
|
@ -30,7 +30,6 @@ import { replaceableComponent } from "../../../utils/replaceableComponent";
|
|||
import MatrixClientContext from "../../../contexts/MatrixClientContext";
|
||||
import { getUserNameColorClass } from "../../../utils/FormattingUtils";
|
||||
import AccessibleTooltipButton from "../elements/AccessibleTooltipButton";
|
||||
import { TileShape } from "./EventTile";
|
||||
import { ViewRoomPayload } from "../../../dispatcher/payloads/ViewRoomPayload";
|
||||
|
||||
interface IProps {
|
||||
|
@ -92,7 +91,6 @@ export default class PinnedEventTile extends React.Component<IProps> {
|
|||
className="mx_PinnedEventTile_body"
|
||||
maxImageHeight={150}
|
||||
onHeightChanged={() => {}} // we need to give this, apparently
|
||||
tileShape={TileShape.Pinned}
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue