Apply prettier formatting

This commit is contained in:
Michael Weimann 2022-12-12 12:24:14 +01:00
parent 1cac306093
commit 526645c791
No known key found for this signature in database
GPG key ID: 53F535A266BB9584
1576 changed files with 65385 additions and 62478 deletions

View file

@ -14,35 +14,35 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import React, { createRef, ReactNode } from 'react';
import React, { createRef, ReactNode } from "react";
import ReactDOM from "react-dom";
import { NotificationCountType, Room, RoomEvent } from "matrix-js-sdk/src/models/room";
import { MatrixEvent, MatrixEventEvent } from "matrix-js-sdk/src/models/event";
import { EventTimelineSet, IRoomTimelineData } from "matrix-js-sdk/src/models/event-timeline-set";
import { Direction, EventTimeline } from "matrix-js-sdk/src/models/event-timeline";
import { TimelineWindow } from "matrix-js-sdk/src/timeline-window";
import { EventType, RelationType } from 'matrix-js-sdk/src/@types/event';
import { SyncState } from 'matrix-js-sdk/src/sync';
import { RoomMember, RoomMemberEvent } from 'matrix-js-sdk/src/models/room-member';
import { debounce, throttle } from 'lodash';
import { EventType, RelationType } from "matrix-js-sdk/src/@types/event";
import { SyncState } from "matrix-js-sdk/src/sync";
import { RoomMember, RoomMemberEvent } from "matrix-js-sdk/src/models/room-member";
import { debounce, throttle } from "lodash";
import { logger } from "matrix-js-sdk/src/logger";
import { ClientEvent } from "matrix-js-sdk/src/client";
import { Thread, ThreadEvent } from 'matrix-js-sdk/src/models/thread';
import { Thread, ThreadEvent } from "matrix-js-sdk/src/models/thread";
import { ReceiptType } from "matrix-js-sdk/src/@types/read_receipts";
import { MatrixError } from 'matrix-js-sdk/src/http-api';
import { ReadReceipt } from 'matrix-js-sdk/src/models/read-receipt';
import { MatrixError } from "matrix-js-sdk/src/http-api";
import { ReadReceipt } from "matrix-js-sdk/src/models/read-receipt";
import SettingsStore from "../../settings/SettingsStore";
import { Layout } from "../../settings/enums/Layout";
import { _t } from '../../languageHandler';
import { _t } from "../../languageHandler";
import { MatrixClientPeg } from "../../MatrixClientPeg";
import RoomContext, { TimelineRenderingType } from "../../contexts/RoomContext";
import UserActivity from "../../UserActivity";
import Modal from "../../Modal";
import dis from "../../dispatcher/dispatcher";
import { Action } from '../../dispatcher/actions';
import Timer from '../../utils/Timer';
import shouldHideEvent from '../../shouldHideEvent';
import { Action } from "../../dispatcher/actions";
import Timer from "../../utils/Timer";
import shouldHideEvent from "../../shouldHideEvent";
import { arrayFastClone } from "../../utils/arrays";
import MessagePanel from "./MessagePanel";
import { IScrollState } from "./ScrollPanel";
@ -50,8 +50,8 @@ import { ActionPayload } from "../../dispatcher/payloads";
import ResizeNotifier from "../../utils/ResizeNotifier";
import { RoomPermalinkCreator } from "../../utils/permalinks/Permalinks";
import Spinner from "../views/elements/Spinner";
import EditorStateTransfer from '../../utils/EditorStateTransfer';
import ErrorDialog from '../views/dialogs/ErrorDialog';
import EditorStateTransfer from "../../utils/EditorStateTransfer";
import ErrorDialog from "../views/dialogs/ErrorDialog";
import LegacyCallEventGrouper, { buildLegacyCallEventGroupers } from "./LegacyCallEventGrouper";
import { ViewRoomPayload } from "../../dispatcher/payloads/ViewRoomPayload";
import { getKeyBindingsManager } from "../../KeyBindingsManager";
@ -232,7 +232,7 @@ class TimelinePanel extends React.Component<IProps, IState> {
// By default, disable the timelineCap in favour of unpaginating based on
// event tile heights. (See _unpaginateEvents)
timelineCap: Number.MAX_VALUE,
className: 'mx_RoomView_messagePanel',
className: "mx_RoomView_messagePanel",
sendReadReceiptOnLoad: true,
hideThreadedMessages: true,
disableGrouping: false,
@ -262,7 +262,7 @@ class TimelinePanel extends React.Component<IProps, IState> {
// but for now we just do it per room for simplicity.
let initialReadMarker: string | null = null;
if (this.props.manageReadMarkers) {
const readmarker = this.props.timelineSet.room.getAccountData('m.fully_read');
const readmarker = this.props.timelineSet.room.getAccountData("m.fully_read");
if (readmarker) {
initialReadMarker = readmarker.getContent().event_id;
} else {
@ -343,8 +343,10 @@ class TimelinePanel extends React.Component<IProps, IState> {
const differentHighlightedEventId = prevProps.highlightedEventId != this.props.highlightedEventId;
const differentAvoidJump = prevProps.eventScrollIntoView && !this.props.eventScrollIntoView;
if (differentEventId || differentHighlightedEventId || differentAvoidJump) {
logger.log(`TimelinePanel switching to eventId ${this.props.eventId} (was ${prevProps.eventId}), ` +
`scrollIntoView: ${this.props.eventScrollIntoView} (was ${prevProps.eventScrollIntoView})`);
logger.log(
`TimelinePanel switching to eventId ${this.props.eventId} (was ${prevProps.eventId}), ` +
`scrollIntoView: ${this.props.eventScrollIntoView} (was ${prevProps.eventScrollIntoView})`,
);
this.initTimeline(this.props);
}
}
@ -408,9 +410,9 @@ class TimelinePanel extends React.Component<IProps, IState> {
if (messagePanel) {
const messagePanelNode = ReactDOM.findDOMNode(messagePanel) as Element;
if (messagePanelNode) {
const actuallyRenderedEvents = messagePanelNode.querySelectorAll('[data-event-id]');
const actuallyRenderedEvents = messagePanelNode.querySelectorAll("[data-event-id]");
renderedEventIds = [...actuallyRenderedEvents].map((renderedEvent) => {
return renderedEvent.getAttribute('data-event-id');
return renderedEvent.getAttribute("data-event-id");
});
}
}
@ -439,12 +441,16 @@ class TimelinePanel extends React.Component<IProps, IState> {
// Serialize all threads in the room from theadId -> event IDs in the thread
room.getThreads().forEach((thread) => {
serializedThreadsMap[thread.id] = {
events: thread.events.map(ev => ev.getId()),
events: thread.events.map((ev) => ev.getId()),
numTimelines: thread.timelineSet.getTimelines().length,
liveTimeline: thread.timelineSet.getLiveTimeline().getEvents().length,
prevTimeline: thread.timelineSet.getLiveTimeline().getNeighbouringTimeline(Direction.Backward)
prevTimeline: thread.timelineSet
.getLiveTimeline()
.getNeighbouringTimeline(Direction.Backward)
?.getEvents().length,
nextTimeline: thread.timelineSet.getLiveTimeline().getNeighbouringTimeline(Direction.Forward)
nextTimeline: thread.timelineSet
.getLiveTimeline()
.getNeighbouringTimeline(Direction.Forward)
?.getEvents().length,
};
});
@ -455,28 +461,30 @@ class TimelinePanel extends React.Component<IProps, IState> {
let timelineWindowEventIds: string[];
try {
timelineWindowEventIds = this.timelineWindow.getEvents().map(ev => ev.getId());
timelineWindowEventIds = this.timelineWindow.getEvents().map((ev) => ev.getId());
} catch (err) {
logger.error(`onDumpDebugLogs: Failed to get event IDs from the timelineWindow`, err);
}
let pendingEventIds: string[];
try {
pendingEventIds = this.props.timelineSet.getPendingEvents().map(ev => ev.getId());
pendingEventIds = this.props.timelineSet.getPendingEvents().map((ev) => ev.getId());
} catch (err) {
logger.error(`onDumpDebugLogs: Failed to get pending event IDs`, err);
}
logger.debug(
`TimelinePanel(${this.context.timelineRenderingType}): Debugging info for ${room?.roomId}\n` +
`\tevents(${eventIdList.length})=${JSON.stringify(eventIdList)}\n` +
`\trenderedEventIds(${renderedEventIds?.length ?? 0})=` +
`${JSON.stringify(renderedEventIds)}\n` +
`\tserializedEventIdsFromTimelineSets=${JSON.stringify(serializedEventIdsFromTimelineSets)}\n` +
`\tserializedEventIdsFromThreadsTimelineSets=` +
`${JSON.stringify(serializedEventIdsFromThreadsTimelineSets)}\n` +
`\tserializedThreadsMap=${JSON.stringify(serializedThreadsMap)}\n` +
`\ttimelineWindowEventIds(${timelineWindowEventIds.length})=${JSON.stringify(timelineWindowEventIds)}\n` +
`\tpendingEventIds(${pendingEventIds.length})=${JSON.stringify(pendingEventIds)}`,
`\tevents(${eventIdList.length})=${JSON.stringify(eventIdList)}\n` +
`\trenderedEventIds(${renderedEventIds?.length ?? 0})=` +
`${JSON.stringify(renderedEventIds)}\n` +
`\tserializedEventIdsFromTimelineSets=${JSON.stringify(serializedEventIdsFromTimelineSets)}\n` +
`\tserializedEventIdsFromThreadsTimelineSets=` +
`${JSON.stringify(serializedEventIdsFromThreadsTimelineSets)}\n` +
`\tserializedThreadsMap=${JSON.stringify(serializedThreadsMap)}\n` +
`\ttimelineWindowEventIds(${timelineWindowEventIds.length})=${JSON.stringify(
timelineWindowEventIds,
)}\n` +
`\tpendingEventIds(${pendingEventIds.length})=${JSON.stringify(pendingEventIds)}`,
);
};
@ -489,11 +497,9 @@ class TimelinePanel extends React.Component<IProps, IState> {
// this particular event should be the first or last to be unpaginated.
const eventId = scrollToken;
const marker = this.state.events.findIndex(
(ev) => {
return ev.getId() === eventId;
},
);
const marker = this.state.events.findIndex((ev) => {
return ev.getId() === eventId;
});
const count = backwards ? marker + 1 : this.state.events.length - marker;
@ -536,8 +542,8 @@ class TimelinePanel extends React.Component<IProps, IState> {
if (!this.shouldPaginate()) return Promise.resolve(false);
const dir = backwards ? EventTimeline.BACKWARDS : EventTimeline.FORWARDS;
const canPaginateKey = backwards ? 'canBackPaginate' : 'canForwardPaginate';
const paginatingKey = backwards ? 'backPaginating' : 'forwardPaginating';
const canPaginateKey = backwards ? "canBackPaginate" : "canForwardPaginate";
const paginatingKey = backwards ? "backPaginating" : "forwardPaginating";
if (!this.state[canPaginateKey]) {
debuglog("have given up", dir, "paginating this timeline");
@ -555,13 +561,15 @@ class TimelinePanel extends React.Component<IProps, IState> {
return Promise.resolve(false);
}
debuglog("Initiating paginate; backwards:"+backwards);
debuglog("Initiating paginate; backwards:" + backwards);
this.setState<null>({ [paginatingKey]: true });
return this.onPaginationRequest(this.timelineWindow, dir, PAGINATE_SIZE).then((r) => {
if (this.unmounted) { return; }
if (this.unmounted) {
return;
}
debuglog("paginate complete backwards:"+backwards+"; success:"+r);
debuglog("paginate complete backwards:" + backwards + "; success:" + r);
const { events, liveEvents, firstVisibleEventIndex } = this.getEvents();
this.buildLegacyCallEventGroupers(events);
@ -576,10 +584,9 @@ class TimelinePanel extends React.Component<IProps, IState> {
// moving the window in this direction may mean that we can now
// paginate in the other where we previously could not.
const otherDirection = backwards ? EventTimeline.FORWARDS : EventTimeline.BACKWARDS;
const canPaginateOtherWayKey = backwards ? 'canForwardPaginate' : 'canBackPaginate';
if (!this.state[canPaginateOtherWayKey] &&
this.timelineWindow.canPaginate(otherDirection)) {
debuglog('can now', otherDirection, 'paginate again');
const canPaginateOtherWayKey = backwards ? "canForwardPaginate" : "canBackPaginate";
if (!this.state[canPaginateOtherWayKey] && this.timelineWindow.canPaginate(otherDirection)) {
debuglog("can now", otherDirection, "paginate again");
newState[canPaginateOtherWayKey] = true;
}
@ -600,7 +607,7 @@ class TimelinePanel extends React.Component<IProps, IState> {
});
};
private onMessageListScroll = e => {
private onMessageListScroll = (e) => {
this.props.onScroll?.(e);
if (this.props.manageReadMarkers) {
this.doManageReadMarkers();
@ -616,21 +623,25 @@ class TimelinePanel extends React.Component<IProps, IState> {
* we really only need to update this once the user has finished scrolling,
* not periodically while they scroll).
*/
private doManageReadMarkers = debounce(() => {
const rmPosition = this.getReadMarkerPosition();
// we hide the read marker when it first comes onto the screen, but if
// it goes back off the top of the screen (presumably because the user
// clicks on the 'jump to bottom' button), we need to re-enable it.
if (rmPosition < 0) {
this.setState({ readMarkerVisible: true });
}
private doManageReadMarkers = debounce(
() => {
const rmPosition = this.getReadMarkerPosition();
// we hide the read marker when it first comes onto the screen, but if
// it goes back off the top of the screen (presumably because the user
// clicks on the 'jump to bottom' button), we need to re-enable it.
if (rmPosition < 0) {
this.setState({ readMarkerVisible: true });
}
// if read marker position goes between 0 and -1/1,
// (and user is active), switch timeout
const timeout = this.readMarkerTimeout(rmPosition);
// NO-OP when timeout already has set to the given value
this.readMarkerActivityTimer?.changeTimeout(timeout);
}, READ_MARKER_DEBOUNCE_MS, { leading: false, trailing: true });
// if read marker position goes between 0 and -1/1,
// (and user is active), switch timeout
const timeout = this.readMarkerTimeout(rmPosition);
// NO-OP when timeout already has set to the given value
this.readMarkerActivityTimer?.changeTimeout(timeout);
},
READ_MARKER_DEBOUNCE_MS,
{ leading: false, trailing: true },
);
private onAction = (payload: ActionPayload): void => {
switch (payload.action) {
@ -652,8 +663,8 @@ class TimelinePanel extends React.Component<IProps, IState> {
): void => {
// ignore events for other timeline sets
if (
data.timeline.getTimelineSet() !== this.props.timelineSet
&& data.timeline.getTimelineSet() !== this.props.overlayTimelineSet
data.timeline.getTimelineSet() !== this.props.timelineSet &&
data.timeline.getTimelineSet() !== this.props.overlayTimelineSet
) {
return;
}
@ -701,7 +712,9 @@ class TimelinePanel extends React.Component<IProps, IState> {
}
})
.then(() => {
if (this.unmounted) { return; }
if (this.unmounted) {
return;
}
const { events, liveEvents, firstVisibleEventIndex } = this.getEvents();
this.buildLegacyCallEventGroupers(events);
@ -715,21 +728,21 @@ class TimelinePanel extends React.Component<IProps, IState> {
let callRMUpdated = false;
if (this.props.manageReadMarkers) {
// when a new event arrives when the user is not watching the
// window, but the window is in its auto-scroll mode, make sure the
// read marker is visible.
//
// We ignore events we have sent ourselves; we don't want to see the
// read-marker when a remote echo of an event we have just sent takes
// more than the timeout on userActiveRecently.
//
// when a new event arrives when the user is not watching the
// window, but the window is in its auto-scroll mode, make sure the
// read marker is visible.
//
// We ignore events we have sent ourselves; we don't want to see the
// read-marker when a remote echo of an event we have just sent takes
// more than the timeout on userActiveRecently.
//
const myUserId = MatrixClientPeg.get().credentials.userId;
callRMUpdated = false;
if (ev.getSender() !== myUserId && !UserActivity.sharedInstance().userActiveRecently()) {
updatedState.readMarkerVisible = true;
} else if (lastLiveEvent && this.getReadMarkerPosition() === 0) {
// we know we're stuckAtBottom, so we can advance the RM
// immediately, to save a later render cycle
// we know we're stuckAtBottom, so we can advance the RM
// immediately, to save a later render cycle
this.setReadMarker(lastLiveEvent.getId() ?? null, lastLiveEvent.getTs(), true);
updatedState.readMarkerVisible = false;
@ -871,9 +884,12 @@ class TimelinePanel extends React.Component<IProps, IState> {
// XXX: roomReadMarkerTsMap not updated here so it is now inconsistent. Replace
// this mechanism of determining where the RM is relative to the view-port with
// one supported by the server (the client needs more than an event ID).
this.setState({
readMarkerEventId: ev.getContent().event_id,
}, this.props.onReadMarkerUpdated);
this.setState(
{
readMarkerEventId: ev.getContent().event_id,
},
this.props.onReadMarkerUpdated,
);
};
private onEventDecrypted = (ev: MatrixEvent): void => {
@ -901,28 +917,35 @@ class TimelinePanel extends React.Component<IProps, IState> {
this.setState({ clientSyncState });
};
private recheckFirstVisibleEventIndex = throttle((): void => {
const firstVisibleEventIndex = this.checkForPreJoinUISI(this.state.events);
if (firstVisibleEventIndex !== this.state.firstVisibleEventIndex) {
this.setState({ firstVisibleEventIndex });
}
}, 500, { leading: true, trailing: true });
private recheckFirstVisibleEventIndex = throttle(
(): void => {
const firstVisibleEventIndex = this.checkForPreJoinUISI(this.state.events);
if (firstVisibleEventIndex !== this.state.firstVisibleEventIndex) {
this.setState({ firstVisibleEventIndex });
}
},
500,
{ leading: true, trailing: true },
);
private readMarkerTimeout(readMarkerPosition: number): number {
return readMarkerPosition === 0 ?
this.context?.readMarkerInViewThresholdMs ?? this.state.readMarkerInViewThresholdMs :
this.context?.readMarkerOutOfViewThresholdMs ?? this.state.readMarkerOutOfViewThresholdMs;
return readMarkerPosition === 0
? this.context?.readMarkerInViewThresholdMs ?? this.state.readMarkerInViewThresholdMs
: this.context?.readMarkerOutOfViewThresholdMs ?? this.state.readMarkerOutOfViewThresholdMs;
}
private async updateReadMarkerOnUserActivity(): Promise<void> {
const initialTimeout = this.readMarkerTimeout(this.getReadMarkerPosition());
this.readMarkerActivityTimer = new Timer(initialTimeout);
while (this.readMarkerActivityTimer) { //unset on unmount
while (this.readMarkerActivityTimer) {
//unset on unmount
UserActivity.sharedInstance().timeWhileActiveRecently(this.readMarkerActivityTimer);
try {
await this.readMarkerActivityTimer.finished();
} catch (e) { continue; /* aborted */ }
} catch (e) {
continue; /* aborted */
}
// outside of try/catch to not swallow errors
this.updateReadMarker();
}
@ -930,11 +953,14 @@ class TimelinePanel extends React.Component<IProps, IState> {
private async updateReadReceiptOnUserActivity(): Promise<void> {
this.readReceiptActivityTimer = new Timer(READ_RECEIPT_INTERVAL_MS);
while (this.readReceiptActivityTimer) { //unset on unmount
while (this.readReceiptActivityTimer) {
//unset on unmount
UserActivity.sharedInstance().timeWhileActiveNow(this.readReceiptActivityTimer);
try {
await this.readReceiptActivityTimer.finished();
} catch (e) { continue; /* aborted */ }
} catch (e) {
continue; /* aborted */
}
// outside of try/catch to not swallow errors
this.sendReadReceipt();
}
@ -969,8 +995,11 @@ class TimelinePanel extends React.Component<IProps, IState> {
// RRs) - but that is a bit of a niche case. It will sort itself out when
// the user eventually hits the live timeline.
//
if (currentRREventId && currentRREventIndex === null &&
this.timelineWindow.canPaginate(EventTimeline.FORWARDS)) {
if (
currentRREventId &&
currentRREventIndex === null &&
this.timelineWindow.canPaginate(EventTimeline.FORWARDS)
) {
shouldSendRR = false;
}
@ -981,7 +1010,8 @@ class TimelinePanel extends React.Component<IProps, IState> {
shouldSendRR = false;
}
let lastReadEvent: MatrixEvent | null = this.state.events[lastReadEventIndex ?? 0];
shouldSendRR = shouldSendRR &&
shouldSendRR =
shouldSendRR &&
// Only send a RR if the last read event is ahead in the timeline relative to
// the current RR event.
lastReadEventIndex > currentRREventIndex &&
@ -989,8 +1019,7 @@ class TimelinePanel extends React.Component<IProps, IState> {
this.lastRRSentEventId !== lastReadEvent?.getId();
// Only send a RM if the last RM sent != the one we would send
const shouldSendRM =
this.lastRMSentEventId != this.state.readMarkerEventId;
const shouldSendRM = this.lastRMSentEventId != this.state.readMarkerEventId;
// we also remember the last read receipt we sent to avoid spamming the
// same one at the server repeatedly
@ -1010,30 +1039,27 @@ class TimelinePanel extends React.Component<IProps, IState> {
`rm=${this.state.readMarkerEventId} `,
`rr=${sendRRs ? lastReadEvent?.getId() : null} `,
`prr=${lastReadEvent?.getId()}`,
);
if (this.props.timelineSet.thread && sendRRs && lastReadEvent) {
// There's no support for fully read markers on threads
// as defined by MSC3771
cli.sendReadReceipt(
lastReadEvent,
sendRRs ? ReceiptType.Read : ReceiptType.ReadPrivate,
);
cli.sendReadReceipt(lastReadEvent, sendRRs ? ReceiptType.Read : ReceiptType.ReadPrivate);
} else {
cli.setRoomReadMarkers(
roomId,
this.state.readMarkerEventId ?? "",
sendRRs ? (lastReadEvent ?? undefined) : undefined, // Public read receipt (could be null)
sendRRs ? lastReadEvent ?? undefined : undefined, // Public read receipt (could be null)
lastReadEvent ?? undefined, // Private read receipt (could be null)
).catch(async (e) => {
// /read_markers API is not implemented on this HS, fallback to just RR
if (e.errcode === 'M_UNRECOGNIZED' && lastReadEvent) {
if (e.errcode === "M_UNRECOGNIZED" && lastReadEvent) {
if (
!sendRRs
&& !(await cli.doesServerSupportUnstableFeature("org.matrix.msc2285.stable"))
&& !(await cli.isVersionSupported("v1.4"))
) return;
!sendRRs &&
!(await cli.doesServerSupportUnstableFeature("org.matrix.msc2285.stable")) &&
!(await cli.isVersionSupported("v1.4"))
)
return;
try {
return await cli.sendReadReceipt(
lastReadEvent,
@ -1060,7 +1086,7 @@ class TimelinePanel extends React.Component<IProps, IState> {
this.props.timelineSet.room.setUnreadNotificationCount(NotificationCountType.Total, 0);
this.props.timelineSet.room.setUnreadNotificationCount(NotificationCountType.Highlight, 0);
dis.dispatch({
action: 'on_room_read',
action: "on_room_read",
roomId: this.props.timelineSet.room.roomId,
});
}
@ -1088,10 +1114,7 @@ class TimelinePanel extends React.Component<IProps, IState> {
return;
}
const lastDisplayedEvent = this.state.events[lastDisplayedIndex];
this.setReadMarker(
lastDisplayedEvent.getId(),
lastDisplayedEvent.getTs(),
);
this.setReadMarker(lastDisplayedEvent.getId(), lastDisplayedEvent.getTs());
// the read-marker should become invisible, so that if the user scrolls
// down, they don't see it.
@ -1177,15 +1200,14 @@ class TimelinePanel extends React.Component<IProps, IState> {
if (ret !== null) {
// The messagepanel knows where the RM is, so we must have loaded
// the relevant event.
this.messagePanel.current.scrollToEvent(this.state.readMarkerEventId,
0, 1/3);
this.messagePanel.current.scrollToEvent(this.state.readMarkerEventId, 0, 1 / 3);
return;
}
// Looks like we haven't loaded the event corresponding to the read-marker.
// As with jumpToLiveTimeline, we want to reload the timeline around the
// read-marker.
this.loadTimeline(this.state.readMarkerEventId, 0, 1/3);
this.loadTimeline(this.state.readMarkerEventId, 0, 1 / 3);
};
/* update the read-up-to marker to match the read receipt
@ -1200,7 +1222,9 @@ class TimelinePanel extends React.Component<IProps, IState> {
const tl = this.props.timelineSet.getTimelineForEvent(rmId ?? "");
let rmTs: number;
if (tl) {
const event = tl.getEvents().find((e) => { return e.getId() == rmId; });
const event = tl.getEvents().find((e) => {
return e.getId() == rmId;
});
if (event) {
rmTs = event.getTs();
}
@ -1217,9 +1241,11 @@ class TimelinePanel extends React.Component<IProps, IState> {
* at the end of the live timeline.
*/
public isAtEndOfLiveTimeline = (): boolean | undefined => {
return this.messagePanel.current?.isAtBottom()
&& this.timelineWindow
&& !this.timelineWindow.canPaginate(EventTimeline.FORWARDS);
return (
this.messagePanel.current?.isAtBottom() &&
this.timelineWindow &&
!this.timelineWindow.canPaginate(EventTimeline.FORWARDS)
);
};
/* get the current scroll state. See ScrollPanel.getScrollState for
@ -1228,7 +1254,9 @@ class TimelinePanel extends React.Component<IProps, IState> {
* returns null if we are not mounted.
*/
public getScrollState = (): IScrollState => {
if (!this.messagePanel.current) { return null; }
if (!this.messagePanel.current) {
return null;
}
return this.messagePanel.current.getScrollState();
};
@ -1266,7 +1294,8 @@ class TimelinePanel extends React.Component<IProps, IState> {
// 3. We want to show the bar if the read-marker is off the top of the screen.
// 4. Also, if pos === null, the event might not be paginated - show the unread bar
const pos = this.getReadMarkerPosition();
const ret = this.state.readMarkerEventId !== null && // 1.
const ret =
this.state.readMarkerEventId !== null && // 1.
(pos < 0 || pos === null); // 3., 4.
return ret;
};
@ -1276,7 +1305,7 @@ class TimelinePanel extends React.Component<IProps, IState> {
*
* We pass it down to the scroll panel.
*/
public handleScrollKey = ev => {
public handleScrollKey = (ev) => {
if (!this.messagePanel.current) return;
// jump to the live timeline on ctrl-end, rather than the end of the
@ -1307,13 +1336,15 @@ class TimelinePanel extends React.Component<IProps, IState> {
const doScroll = () => {
if (!this.messagePanel.current) return;
if (eventId) {
debuglog("TimelinePanel scrolling to eventId " + eventId +
" at position " + (offsetBase * 100) + "% + " + pixelOffset);
this.messagePanel.current.scrollToEvent(
eventId,
pixelOffset,
offsetBase,
debuglog(
"TimelinePanel scrolling to eventId " +
eventId +
" at position " +
offsetBase * 100 +
"% + " +
pixelOffset,
);
this.messagePanel.current.scrollToEvent(eventId, pixelOffset, offsetBase);
} else {
debuglog("TimelinePanel scrolling to bottom");
this.messagePanel.current.scrollToBottom();
@ -1373,29 +1404,32 @@ class TimelinePanel extends React.Component<IProps, IState> {
// We need to skip over any which have subsequently been sent.
this.advanceReadMarkerPastMyEvents();
this.setState({
canBackPaginate: !!this.timelineWindow?.canPaginate(EventTimeline.BACKWARDS),
canForwardPaginate: !!this.timelineWindow?.canPaginate(EventTimeline.FORWARDS),
timelineLoading: false,
}, () => {
// initialise the scroll state of the message panel
if (!this.messagePanel.current) {
// this shouldn't happen - we know we're mounted because
// we're in a setState callback, and we know
// timelineLoading is now false, so render() should have
// mounted the message panel.
logger.log("can't initialise scroll state because messagePanel didn't load");
return;
}
this.setState(
{
canBackPaginate: !!this.timelineWindow?.canPaginate(EventTimeline.BACKWARDS),
canForwardPaginate: !!this.timelineWindow?.canPaginate(EventTimeline.FORWARDS),
timelineLoading: false,
},
() => {
// initialise the scroll state of the message panel
if (!this.messagePanel.current) {
// this shouldn't happen - we know we're mounted because
// we're in a setState callback, and we know
// timelineLoading is now false, so render() should have
// mounted the message panel.
logger.log("can't initialise scroll state because messagePanel didn't load");
return;
}
if (scrollIntoView) {
this.scrollIntoView(eventId, pixelOffset, offsetBase);
}
if (scrollIntoView) {
this.scrollIntoView(eventId, pixelOffset, offsetBase);
}
if (this.props.sendReadReceiptOnLoad) {
this.sendReadReceipt();
}
});
if (this.props.sendReadReceiptOnLoad) {
this.sendReadReceipt();
}
},
);
};
const onError = (error: MatrixError) => {
@ -1422,15 +1456,14 @@ class TimelinePanel extends React.Component<IProps, IState> {
}
let description: string;
if (error.errcode == 'M_FORBIDDEN') {
if (error.errcode == "M_FORBIDDEN") {
description = _t(
"Tried to load a specific point in this room's timeline, but you " +
"do not have permission to view the message in question.",
"do not have permission to view the message in question.",
);
} else {
description = _t(
"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.",
);
}
@ -1507,24 +1540,27 @@ class TimelinePanel extends React.Component<IProps, IState> {
// maintain the main timeline event order as returned from the HS
// merge overlay events at approximately the right position based on local timestamp
const events = overlayEvents.reduce((acc: MatrixEvent[], overlayEvent: MatrixEvent) => {
// find the first main tl event with a later timestamp
const index = acc.findIndex(event => event.localTimestamp > overlayEvent.localTimestamp);
// insert overlay event into timeline at approximately the right place
if (index > -1) {
acc.splice(index, 0, overlayEvent);
} else {
acc.push(overlayEvent);
}
return acc;
}, [...mainEvents]);
const events = overlayEvents.reduce(
(acc: MatrixEvent[], overlayEvent: MatrixEvent) => {
// find the first main tl event with a later timestamp
const index = acc.findIndex((event) => event.localTimestamp > overlayEvent.localTimestamp);
// insert overlay event into timeline at approximately the right place
if (index > -1) {
acc.splice(index, 0, overlayEvent);
} else {
acc.push(overlayEvent);
}
return acc;
},
[...mainEvents],
);
// `arrayFastClone` performs a shallow copy of the array
// we want the last event to be decrypted first but displayed last
// `reverse` is destructive and unfortunately mutates the "events" array
arrayFastClone(events)
.reverse()
.forEach(event => {
.forEach((event) => {
const client = MatrixClientPeg.get();
client.decryptEventIfNeeded(event);
});
@ -1538,18 +1574,21 @@ class TimelinePanel extends React.Component<IProps, IState> {
// if we're at the end of the live timeline, append the pending events
if (!this.timelineWindow?.canPaginate(EventTimeline.FORWARDS)) {
const pendingEvents = this.props.timelineSet.getPendingEvents();
events.push(...pendingEvents.filter(event => {
const {
shouldLiveInRoom,
threadId,
} = this.props.timelineSet.room!.eventShouldLiveIn(event, pendingEvents);
events.push(
...pendingEvents.filter((event) => {
const { shouldLiveInRoom, threadId } = this.props.timelineSet.room!.eventShouldLiveIn(
event,
pendingEvents,
);
if (this.context.timelineRenderingType === TimelineRenderingType.Thread) {
return threadId === this.context.threadId;
} {
return shouldLiveInRoom;
}
}));
if (this.context.timelineRenderingType === TimelineRenderingType.Thread) {
return threadId === this.context.threadId;
}
{
return shouldLiveInRoom;
}
}),
);
}
return {
@ -1573,8 +1612,9 @@ class TimelinePanel extends React.Component<IProps, IState> {
const cli = MatrixClientPeg.get();
const room = this.props.timelineSet.room;
const isThreadTimeline = [TimelineRenderingType.Thread, TimelineRenderingType.ThreadsList]
.includes(this.context.timelineRenderingType);
const isThreadTimeline = [TimelineRenderingType.Thread, TimelineRenderingType.ThreadsList].includes(
this.context.timelineRenderingType,
);
if (events.length === 0 || !room || !cli.isRoomEncrypted(room.roomId) || isThreadTimeline) {
logger.info("checkForPreJoinUISI: showing all messages, skipping check");
return 0;
@ -1594,8 +1634,7 @@ class TimelinePanel extends React.Component<IProps, IState> {
// a timeline, even though that should not happen. :(
// https://github.com/vector-im/element-web/issues/12120
logger.warn(
`Event ${events[i].getId()} in room ${room.roomId} is live, ` +
`but it does not have a timeline`,
`Event ${events[i].getId()} in room ${room.roomId} is live, ` + `but it does not have a timeline`,
);
continue;
}
@ -1633,7 +1672,9 @@ class TimelinePanel extends React.Component<IProps, IState> {
}
private indexForEventId(evId: string | null): number | null {
if (evId === null) { return null; }
if (evId === null) {
return null;
}
/* Threads do not have server side support for read receipts and the concept
is very tied to the main room timeline, we are forcing the timeline to
send read receipts for threaded events */
@ -1641,10 +1682,8 @@ class TimelinePanel extends React.Component<IProps, IState> {
if (SettingsStore.getValue("feature_thread") && isThreadTimeline) {
return 0;
}
const index = this.state.events.findIndex(ev => ev.getId() === evId);
return index > -1
? index
: null;
const index = this.state.events.findIndex((ev) => ev.getId() === evId);
return index > -1 ? index : null;
}
private getLastDisplayedEventIndex(opts: IEventIndexOpts = {}): number | null {
@ -1697,10 +1736,11 @@ class TimelinePanel extends React.Component<IProps, IState> {
adjacentInvisibleEventCount = 0;
}
const shouldIgnore = !!ev.status || // local echo
const shouldIgnore =
!!ev.status || // local echo
(ignoreOwn && ev.getSender() === myUserId); // own message
const isWithoutTile = !haveRendererForEvent(ev, this.context?.showHiddenEvents) ||
shouldHideEvent(ev, this.context);
const isWithoutTile =
!haveRendererForEvent(ev, this.context?.showHiddenEvents) || shouldHideEvent(ev, this.context);
if (isWithoutTile || !node) {
// don't start counting if the event should be ignored,
@ -1742,8 +1782,7 @@ class TimelinePanel extends React.Component<IProps, IState> {
}
const myUserId = client.credentials.userId;
const receiptStore: ReadReceipt<any, any> =
this.props.timelineSet.thread ?? this.props.timelineSet.room;
const receiptStore: ReadReceipt<any, any> = this.props.timelineSet.thread ?? this.props.timelineSet.room;
return receiptStore?.getEventReadUpTo(myUserId, ignoreSynthesized);
}
@ -1767,9 +1806,12 @@ class TimelinePanel extends React.Component<IProps, IState> {
// Do the local echo of the RM
// run the render cycle before calling the callback, so that
// getReadMarkerPosition() returns the right thing.
this.setState({
readMarkerEventId: eventId,
}, this.props.onReadMarkerUpdated);
this.setState(
{
readMarkerEventId: eventId,
},
this.props.onReadMarkerUpdated,
);
}
private shouldPaginate(): boolean {
@ -1816,7 +1858,7 @@ class TimelinePanel extends React.Component<IProps, IState> {
if (this.state.events.length == 0 && !this.state.canBackPaginate && this.props.empty) {
return (
<div className={this.props.className + " mx_RoomView_messageListWrapper"}>
<div className="mx_RoomView_empty">{ this.props.empty }</div>
<div className="mx_RoomView_empty">{this.props.empty}</div>
</div>
);
}
@ -1833,10 +1875,8 @@ class TimelinePanel extends React.Component<IProps, IState> {
// If the state is PREPARED or CATCHUP, we're still waiting for the js-sdk to sync with
// the HS and fetch the latest events, so we are effectively forward paginating.
const forwardPaginating = (
this.state.forwardPaginating ||
['PREPARED', 'CATCHUP'].includes(this.state.clientSyncState)
);
const forwardPaginating =
this.state.forwardPaginating || ["PREPARED", "CATCHUP"].includes(this.state.clientSyncState);
const events = this.state.firstVisibleEventIndex
? this.state.events.slice(this.state.firstVisibleEventIndex)
: this.state.events;
@ -1897,7 +1937,7 @@ function serializeEventIdsFromTimelineSets(timelineSets): { [key: string]: strin
// Add a special label when it is the live timeline so we can tell
// it apart from the others
const isLiveTimeline = timeline === liveTimeline;
timelineMap[isLiveTimeline ? 'liveTimeline' : `${index}`] = timeline.getEvents().map(ev => ev.getId());
timelineMap[isLiveTimeline ? "liveTimeline" : `${index}`] = timeline.getEvents().map((ev) => ev.getId());
});
return timelineMap;