Render timeline separator for late event groups (#11739)

* Use Compound tooltips on MessageTimestamp to improve UX of date time discovery

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Show io.element.late_event in MessageTimestamp when known

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Update snapshot

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Avoid needing new Compound changes

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Move groupers into their own directory

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Refactor date separator code to be more generic

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Render timeline separator for late event groups

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Fix date used in copy

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Update snapshot

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Move groupers into their own directory

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Update copy

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Update copy

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Update snapshot

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* i18n

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Add comments

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Add comments

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

---------

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
This commit is contained in:
Michael Telatynski
2023-10-16 14:14:04 +00:00
committed by GitHub
parent dfdb613673
commit 5ff965106a
18 changed files with 198 additions and 66 deletions
+51 -27
View File
@@ -20,10 +20,9 @@ import classNames from "classnames";
import { Room, MatrixClient, RoomStateEvent, EventStatus, MatrixEvent, EventType } from "matrix-js-sdk/src/matrix";
import { logger } from "matrix-js-sdk/src/logger";
import { isSupportedReceiptType } from "matrix-js-sdk/src/utils";
import { Optional } from "matrix-events-sdk";
import shouldHideEvent from "../../shouldHideEvent";
import { wantsDateSeparator } from "../../DateUtils";
import { formatDate, wantsDateSeparator } from "../../DateUtils";
import { MatrixClientPeg } from "../../MatrixClientPeg";
import SettingsStore from "../../settings/SettingsStore";
import RoomContext, { TimelineRenderingType } from "../../contexts/RoomContext";
@@ -40,6 +39,7 @@ import LegacyCallEventGrouper from "./LegacyCallEventGrouper";
import WhoIsTypingTile from "../views/rooms/WhoIsTypingTile";
import ScrollPanel, { IScrollState } from "./ScrollPanel";
import DateSeparator from "../views/messages/DateSeparator";
import TimelineSeparator, { SeparatorKind } from "../views/messages/TimelineSeparator";
import ErrorBoundary from "../views/elements/ErrorBoundary";
import ResizeNotifier from "../../utils/ResizeNotifier";
import Spinner from "../views/elements/Spinner";
@@ -54,6 +54,8 @@ import { hasThreadSummary } from "../../utils/EventUtils";
import { BaseGrouper } from "./grouper/BaseGrouper";
import { MainGrouper } from "./grouper/MainGrouper";
import { CreationGrouper } from "./grouper/CreationGrouper";
import { _t } from "../../languageHandler";
import { getLateEventInfo } from "./grouper/LateEventGrouper";
const CONTINUATION_MAX_INTERVAL = 5 * 60 * 1000; // 5 minutes
const continuedTypes = [EventType.Sticker, EventType.RoomMessage];
@@ -739,31 +741,38 @@ export default class MessagePanel extends React.Component<IProps, IState> {
const isEditing = this.props.editState?.getEvent().getId() === mxEv.getId();
// local echoes have a fake date, which could even be yesterday. Treat them as 'today' for the date separators.
let ts1 = mxEv.getTs();
let eventDate = mxEv.getDate();
if (mxEv.status) {
eventDate = new Date();
ts1 = eventDate.getTime();
}
const ts1 = mxEv.getTs() ?? Date.now();
// do we need a date separator since the last event?
const wantsDateSeparator = this.wantsDateSeparator(prevEvent, eventDate);
if (wantsDateSeparator && !isGrouped && this.props.room) {
const dateSeparator = (
<li key={ts1}>
<DateSeparator key={ts1} roomId={this.props.room.roomId} ts={ts1} />
</li>
);
ret.push(dateSeparator);
// do we need a separator since the last event?
const wantsSeparator = this.wantsSeparator(prevEvent, mxEv);
if (!isGrouped && this.props.room) {
if (wantsSeparator === SeparatorKind.Date) {
ret.push(
<li key={ts1}>
<DateSeparator key={ts1} roomId={this.props.room.roomId} ts={ts1} />
</li>,
);
} else if (wantsSeparator === SeparatorKind.LateEvent) {
const text = _t("timeline|late_event_separator", {
dateTime: formatDate(mxEv.getDate() ?? new Date()),
});
ret.push(
<li key={ts1}>
<TimelineSeparator key={ts1} label={text}>
{text}
</TimelineSeparator>
</li>,
);
}
}
const cli = MatrixClientPeg.safeGet();
let lastInSection = true;
if (nextEventWithTile) {
const nextEv = nextEventWithTile;
const willWantDateSeparator = this.wantsDateSeparator(mxEv, nextEv.getDate() || new Date());
const willWantSeparator = this.wantsSeparator(mxEv, nextEv);
lastInSection =
willWantDateSeparator ||
willWantSeparator === SeparatorKind.Date ||
mxEv.getSender() !== nextEv.getSender() ||
getEventDisplayInfo(cli, nextEv, this.showHiddenEvents).isInfoMessage ||
!shouldFormContinuation(mxEv, nextEv, cli, this.showHiddenEvents, this.context.timelineRenderingType);
@@ -771,7 +780,7 @@ export default class MessagePanel extends React.Component<IProps, IState> {
// is this a continuation of the previous message?
const continuation =
!wantsDateSeparator &&
wantsSeparator === SeparatorKind.None &&
shouldFormContinuation(prevEvent, mxEv, cli, this.showHiddenEvents, this.context.timelineRenderingType);
const eventId = mxEv.getId()!;
@@ -816,16 +825,31 @@ export default class MessagePanel extends React.Component<IProps, IState> {
return ret;
}
public wantsDateSeparator(prevEvent: MatrixEvent | null, nextEventDate: Optional<Date>): boolean {
public wantsSeparator(prevEvent: MatrixEvent | null, mxEvent: MatrixEvent): SeparatorKind {
if (this.context.timelineRenderingType === TimelineRenderingType.ThreadsList) {
return false;
return SeparatorKind.None;
}
if (prevEvent == null) {
// first event in the panel: depends if we could back-paginate from
// here.
return !this.props.canBackPaginate;
if (prevEvent !== null) {
// If the previous event was late but current is not then show a date separator for orientation
// Otherwise if the current event is of a different late group than the previous show a late event separator
const lateEventInfo = getLateEventInfo(mxEvent);
if (lateEventInfo?.group_id !== getLateEventInfo(prevEvent)?.group_id) {
return lateEventInfo !== undefined ? SeparatorKind.LateEvent : SeparatorKind.Date;
}
}
return wantsDateSeparator(prevEvent.getDate() || undefined, nextEventDate);
// first event in the panel: depends on if we could back-paginate from here.
if (prevEvent === null && !this.props.canBackPaginate) {
return SeparatorKind.Date;
}
const nextEventDate = mxEvent.getDate() ?? new Date();
if (prevEvent !== null && wantsDateSeparator(prevEvent.getDate() || undefined, nextEventDate)) {
return SeparatorKind.Date;
}
return SeparatorKind.None;
}
// Get a list of read receipts that should be shown next to this event