Refactor DateSeparator using MVVM and move to shared-components (#32482)
* Refactor DateSeparator using MVVM and move to shared-components
* Add a few more stories, tests and screenshots
* Use the shared component and viewmodel in element-web
* Renaming custom content property an updating snapshots
* Fix lint errors and update snapshot after merge
* Change lifecycle handling for DateSeparatoreViewModel in components where manual handling is preferrable over wrapper component.
* Move context menu from viewmodel to shared components - step 1
* Create a jump to date picker component in shared components
* Add tests for coverage and fix layout issues and roving indexes
* Make element-web use the new component
* Simplify context menu and adjusting tests
* The HTMLExport now render shared components and need a I18nContext.Provider
* Updating unit tests for context menu
* Changed to {translate: _t} to let scripts pick up translations
* Fix lint issue and updating screenshots after merge
* Update snaps for element web components
* Renaming MVVM view components with suffix View.
* Fixing problem with input date calendar icon and system dark theme
* Changed the rendering of the menu and added a separate button component
* Handle input control with useRef in onKeyDown
* Updating DateSeparator snapshots on unit tests
* Updating layout after compound Menu got a className property
* Move files to new subfolder after merge
* Updated snapshot after merge
* Updating lock file
* Updates to styling from PR review
* Updates to focus/blur functionality
* Fixed tabbing and export documentation to stories
* Updated snapshots
---------
Co-authored-by: Zack <zazi21@student.bth.se>
This commit is contained in:
@@ -18,7 +18,11 @@ import {
|
||||
} from "matrix-js-sdk/src/matrix";
|
||||
import { logger } from "matrix-js-sdk/src/logger";
|
||||
import { isSupportedReceiptType } from "matrix-js-sdk/src/utils";
|
||||
import { TimelineSeparator } from "@element-hq/web-shared-components";
|
||||
import {
|
||||
DateSeparatorView,
|
||||
TimelineSeparator,
|
||||
useCreateAutoDisposedViewModel,
|
||||
} from "@element-hq/web-shared-components";
|
||||
|
||||
import shouldHideEvent from "../../shouldHideEvent";
|
||||
import { formatDate, wantsDateSeparator } from "../../DateUtils";
|
||||
@@ -37,7 +41,6 @@ import defaultDispatcher from "../../dispatcher/dispatcher";
|
||||
import type LegacyCallEventGrouper from "./LegacyCallEventGrouper";
|
||||
import WhoIsTypingTile from "../views/rooms/WhoIsTypingTile";
|
||||
import ScrollPanel, { type IScrollState } from "./ScrollPanel";
|
||||
import DateSeparator from "../views/messages/DateSeparator";
|
||||
import ErrorBoundary from "../views/elements/ErrorBoundary";
|
||||
import Spinner from "../views/elements/Spinner";
|
||||
import { type RoomPermalinkCreator } from "../../utils/permalinks/Permalinks";
|
||||
@@ -53,10 +56,19 @@ import { MainGrouper } from "./grouper/MainGrouper";
|
||||
import { CreationGrouper } from "./grouper/CreationGrouper";
|
||||
import { _t } from "../../languageHandler";
|
||||
import { getLateEventInfo } from "./grouper/LateEventGrouper";
|
||||
import { DateSeparatorViewModel } from "../../viewmodels/timeline/DateSeparatorViewModel";
|
||||
|
||||
const CONTINUATION_MAX_INTERVAL = 5 * 60 * 1000; // 5 minutes
|
||||
const continuedTypes = [EventType.Sticker, EventType.RoomMessage];
|
||||
|
||||
/**
|
||||
* Creates and auto-disposes the DateSeparatorViewModel for message panel rendering.
|
||||
*/
|
||||
function DateSeparatorWrapper({ roomId, ts }: { roomId: string; ts: number }): JSX.Element {
|
||||
const vm = useCreateAutoDisposedViewModel(() => new DateSeparatorViewModel({ roomId, ts }));
|
||||
return <DateSeparatorView vm={vm} />;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates which separator (if any) should be rendered between timeline events.
|
||||
*/
|
||||
@@ -757,9 +769,10 @@ export default class MessagePanel extends React.Component<IProps, IState> {
|
||||
const wantsSeparator = this.wantsSeparator(prevEvent, mxEv);
|
||||
if (!isGrouped && this.props.room) {
|
||||
if (wantsSeparator === SeparatorKind.Date) {
|
||||
const separatorRoomId = this.props.room.roomId;
|
||||
ret.push(
|
||||
<li key={ts1}>
|
||||
<DateSeparator key={ts1} roomId={this.props.room.roomId} ts={ts1} />
|
||||
<li key={`${separatorRoomId}-${ts1}`}>
|
||||
<DateSeparatorWrapper key={`${separatorRoomId}-${ts1}`} roomId={separatorRoomId} ts={ts1} />
|
||||
</li>,
|
||||
);
|
||||
} else if (wantsSeparator === SeparatorKind.LateEvent) {
|
||||
|
||||
@@ -9,20 +9,29 @@ Please see LICENSE files in the repository root for full details.
|
||||
import React, { type ReactNode } from "react";
|
||||
import { EventType, M_BEACON_INFO, type MatrixEvent } from "matrix-js-sdk/src/matrix";
|
||||
import { KnownMembership } from "matrix-js-sdk/src/types";
|
||||
import { DateSeparatorView, useCreateAutoDisposedViewModel } from "@element-hq/web-shared-components";
|
||||
|
||||
import { BaseGrouper } from "./BaseGrouper";
|
||||
import { SeparatorKind, type WrappedEvent } from "../MessagePanel";
|
||||
import type MessagePanel from "../MessagePanel";
|
||||
import DMRoomMap from "../../../utils/DMRoomMap";
|
||||
import { _t } from "../../../languageHandler";
|
||||
import DateSeparator from "../../views/messages/DateSeparator";
|
||||
import NewRoomIntro from "../../views/rooms/NewRoomIntro";
|
||||
import GenericEventListSummary from "../../views/elements/GenericEventListSummary";
|
||||
import { DateSeparatorViewModel } from "../../../viewmodels/timeline/DateSeparatorViewModel";
|
||||
|
||||
// Wrap initial room creation events into a GenericEventListSummary
|
||||
// Grouping only events sent by the same user that sent the `m.room.create` and only until
|
||||
// the first non-state event, beacon_info event or membership event which is not regarding the sender of the `m.room.create` event
|
||||
|
||||
/**
|
||||
* Creates and auto-disposes the DateSeparatorViewModel for creation-group rendering.
|
||||
*/
|
||||
function DateSeparatorWrapper({ roomId, ts }: { roomId: string; ts: number }): ReactNode {
|
||||
const vm = useCreateAutoDisposedViewModel(() => new DateSeparatorViewModel({ roomId, ts }));
|
||||
return <DateSeparatorView vm={vm} />;
|
||||
}
|
||||
|
||||
export class CreationGrouper extends BaseGrouper {
|
||||
public static canStartGroup = function (_panel: MessagePanel, { event }: WrappedEvent): boolean {
|
||||
return event.getType() === EventType.RoomCreate;
|
||||
@@ -86,10 +95,11 @@ export class CreationGrouper extends BaseGrouper {
|
||||
const lastShownEvent = this.lastShownEvent;
|
||||
|
||||
if (panel.wantsSeparator(this.prevEvent, createEvent.event) === SeparatorKind.Date) {
|
||||
const separatorRoomId = createEvent.event.getRoomId()!;
|
||||
const ts = createEvent.event.getTs();
|
||||
ret.push(
|
||||
<li key={ts + "~"}>
|
||||
<DateSeparator roomId={createEvent.event.getRoomId()!} ts={ts} />
|
||||
<li key={`${separatorRoomId}-${ts}~`}>
|
||||
<DateSeparatorWrapper roomId={separatorRoomId} ts={ts} />
|
||||
</li>,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -8,15 +8,16 @@ Please see LICENSE files in the repository root for full details.
|
||||
|
||||
import React, { type ReactNode } from "react";
|
||||
import { EventType, type MatrixEvent } from "matrix-js-sdk/src/matrix";
|
||||
import { DateSeparatorView, useCreateAutoDisposedViewModel } from "@element-hq/web-shared-components";
|
||||
|
||||
import type MessagePanel from "../MessagePanel";
|
||||
import { SeparatorKind, type WrappedEvent } from "../MessagePanel";
|
||||
import { BaseGrouper } from "./BaseGrouper";
|
||||
import { hasText } from "../../../TextForEvent";
|
||||
import { MatrixClientPeg } from "../../../MatrixClientPeg";
|
||||
import DateSeparator from "../../views/messages/DateSeparator";
|
||||
import HistoryTile from "../../views/rooms/HistoryTile";
|
||||
import EventListSummary from "../../views/elements/EventListSummary";
|
||||
import { DateSeparatorViewModel } from "../../../viewmodels/timeline/DateSeparatorViewModel";
|
||||
|
||||
const groupedStateEvents = [
|
||||
EventType.RoomMember,
|
||||
@@ -25,6 +26,14 @@ const groupedStateEvents = [
|
||||
EventType.RoomPinnedEvents,
|
||||
];
|
||||
|
||||
/**
|
||||
* Creates and auto-disposes the DateSeparatorViewModel for grouped timeline rendering.
|
||||
*/
|
||||
function DateSeparatorWrapper({ roomId, ts }: { roomId: string; ts: number }): ReactNode {
|
||||
const vm = useCreateAutoDisposedViewModel(() => new DateSeparatorViewModel({ roomId, ts }));
|
||||
return <DateSeparatorView vm={vm} />;
|
||||
}
|
||||
|
||||
// Wrap consecutive grouped events in a ListSummary
|
||||
export class MainGrouper extends BaseGrouper {
|
||||
public static canStartGroup = function (panel: MessagePanel, { event: ev, shouldShow }: WrappedEvent): boolean {
|
||||
@@ -113,10 +122,11 @@ export class MainGrouper extends BaseGrouper {
|
||||
const ret: ReactNode[] = [];
|
||||
|
||||
if (panel.wantsSeparator(this.prevEvent, this.events[0].event) === SeparatorKind.Date) {
|
||||
const separatorRoomId = this.events[0].event.getRoomId()!;
|
||||
const ts = this.events[0].event.getTs();
|
||||
ret.push(
|
||||
<li key={ts + "~"}>
|
||||
<DateSeparator roomId={this.events[0].event.getRoomId()!} ts={ts} />
|
||||
<li key={`${separatorRoomId}-${ts}~`}>
|
||||
<DateSeparatorWrapper roomId={separatorRoomId} ts={ts} />
|
||||
</li>,
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user