Unify notifications panel event design (#9754)

This commit is contained in:
Germain 2022-12-21 10:25:50 +00:00 committed by GitHub
parent 6585fb1f55
commit bef8e077f6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 191 additions and 219 deletions

View file

@ -29,8 +29,6 @@ import { CryptoEvent } from "matrix-js-sdk/src/crypto";
import { UserTrustLevel } from "matrix-js-sdk/src/crypto/CrossSigning";
import { Feature, ServerSupport } from "matrix-js-sdk/src/feature";
import { Icon as LinkIcon } from "../../../../res/img/element-icons/link.svg";
import { Icon as ViewInRoomIcon } from "../../../../res/img/element-icons/view-in-room.svg";
import ReplyChain from "../elements/ReplyChain";
import { _t } from "../../../languageHandler";
import dis from "../../../dispatcher/dispatcher";
@ -63,8 +61,6 @@ import SettingsStore from "../../../settings/SettingsStore";
import { MessagePreviewStore } from "../../../stores/room-list/MessagePreviewStore";
import RoomContext, { TimelineRenderingType } from "../../../contexts/RoomContext";
import { MediaEventHelper } from "../../../utils/MediaEventHelper";
import Toolbar from "../../../accessibility/Toolbar";
import { RovingAccessibleTooltipButton } from "../../../accessibility/roving/RovingAccessibleTooltipButton";
import { ThreadNotificationState } from "../../../stores/notifications/ThreadNotificationState";
import { RoomNotificationStateStore } from "../../../stores/notifications/RoomNotificationStateStore";
import { NotificationStateEvents } from "../../../stores/notifications/NotificationState";
@ -85,6 +81,7 @@ import { ShowThreadPayload } from "../../../dispatcher/payloads/ShowThreadPayloa
import { isLocalRoom } from "../../../utils/localRoom/isLocalRoom";
import { ElementCall } from "../../../models/Call";
import { UnreadNotificationBadge } from "./NotificationBadge/UnreadNotificationBadge";
import { EventTileThreadToolbar } from "./EventTile/EventTileThreadToolbar";
export type GetRelationsForEvent = (
eventId: string,
@ -972,6 +969,8 @@ export class UnwrappedEventTile extends React.Component<EventTileProps, IState>
isContinuation = false;
}
const isRenderingNotification = this.context.timelineRenderingType === TimelineRenderingType.Notification;
const isEditing = !!this.props.editState;
const classes = classNames({
mx_EventTile_bubbleContainer: isBubbleMessage,
@ -996,7 +995,8 @@ export class UnwrappedEventTile extends React.Component<EventTileProps, IState>
mx_EventTile_bad: isEncryptionFailure,
mx_EventTile_emote: msgtype === MsgType.Emote,
mx_EventTile_noSender: this.props.hideSender,
mx_EventTile_clamp: this.context.timelineRenderingType === TimelineRenderingType.ThreadsList,
mx_EventTile_clamp:
this.context.timelineRenderingType === TimelineRenderingType.ThreadsList || isRenderingNotification,
mx_EventTile_noBubble: noBubbleEvent,
});
@ -1012,12 +1012,12 @@ export class UnwrappedEventTile extends React.Component<EventTileProps, IState>
// Local echos have a send "status".
const scrollToken = this.props.mxEvent.status ? undefined : this.props.mxEvent.getId();
let avatar: JSX.Element;
let sender: JSX.Element;
let avatar: JSX.Element | null = null;
let sender: JSX.Element | null = null;
let avatarSize: number;
let needsSenderProfile: boolean;
if (this.context.timelineRenderingType === TimelineRenderingType.Notification) {
if (isRenderingNotification) {
avatarSize = 24;
needsSenderProfile = true;
} else if (isInfoMessage) {
@ -1061,7 +1061,9 @@ export class UnwrappedEventTile extends React.Component<EventTileProps, IState>
member = this.props.mxEvent.sender;
}
// In the ThreadsList view we use the entire EventTile as a click target to open the thread instead
const viewUserOnClick = this.context.timelineRenderingType !== TimelineRenderingType.ThreadsList;
const viewUserOnClick = ![TimelineRenderingType.ThreadsList, TimelineRenderingType.Notification].includes(
this.context.timelineRenderingType,
);
avatar = (
<div className="mx_EventTile_avatar">
<MemberAvatar
@ -1202,57 +1204,6 @@ export class UnwrappedEventTile extends React.Component<EventTileProps, IState>
const isOwnEvent = this.props.mxEvent?.getSender() === MatrixClientPeg.get().getUserId();
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,
"aria-atomic": true,
"data-scroll-tokens": scrollToken,
},
[
<div className="mx_EventTile_roomName" key="mx_EventTile_roomName">
<RoomAvatar room={room} width={28} height={28} />
<a href={permalink} onClick={this.onPermalinkClicked}>
{room ? room.name : ""}
</a>
</div>,
<div className="mx_EventTile_senderDetails" key="mx_EventTile_senderDetails">
{avatar}
<a
href={permalink}
onClick={this.onPermalinkClicked}
onContextMenu={this.onTimestampContextMenu}
>
{sender}
{timestamp}
</a>
</div>,
<div className={lineClasses} key="mx_EventTile_line" onContextMenu={this.onContextMenu}>
{this.renderContextMenu()}
{renderTile(
TimelineRenderingType.Notification,
{
...this.props,
// overrides
ref: this.tile,
isSeeingThroughMessageHiddenForModeration,
// appease TS
highlights: this.props.highlights,
highlightLink: this.props.highlightLink,
onHeightChanged: this.props.onHeightChanged,
permalinkCreator: this.props.permalinkCreator,
},
this.context.showHiddenEvents,
)}
</div>,
],
);
}
case TimelineRenderingType.Thread: {
return React.createElement(
this.props.as || "li",
@ -1289,8 +1240,8 @@ export class UnwrappedEventTile extends React.Component<EventTileProps, IState>
// appease TS
highlights: this.props.highlights,
highlightLink: this.props.highlightLink,
onHeightChanged: this.props.onHeightChanged,
permalinkCreator: this.props.permalinkCreator,
onHeightChanged: () => this.props.onHeightChanged,
permalinkCreator: this.props.permalinkCreator!,
},
this.context.showHiddenEvents,
)}
@ -1304,6 +1255,7 @@ export class UnwrappedEventTile extends React.Component<EventTileProps, IState>
],
);
}
case TimelineRenderingType.Notification:
case TimelineRenderingType.ThreadsList: {
const room = MatrixClientPeg.get().getRoom(this.props.mxEvent.getRoomId());
// tab-index=-1 to allow it to be focusable but do not add tab stop for it, primarily for screen readers
@ -1326,20 +1278,48 @@ export class UnwrappedEventTile extends React.Component<EventTileProps, IState>
"onMouseEnter": () => this.setState({ hover: true }),
"onMouseLeave": () => this.setState({ hover: false }),
"onClick": (ev: MouseEvent) => {
dis.dispatch<ShowThreadPayload>({
action: Action.ShowThread,
rootEvent: this.props.mxEvent,
push: true,
});
const target = ev.currentTarget as HTMLElement;
const index = Array.from(target.parentElement.children).indexOf(target);
PosthogTrackers.trackInteraction("WebThreadsPanelThreadItem", ev, index);
let index = -1;
if (target.parentElement) index = Array.from(target.parentElement.children).indexOf(target);
switch (this.context.timelineRenderingType) {
case TimelineRenderingType.Notification:
this.viewInRoom(ev);
break;
case TimelineRenderingType.ThreadsList:
dis.dispatch<ShowThreadPayload>({
action: Action.ShowThread,
rootEvent: this.props.mxEvent,
push: true,
});
PosthogTrackers.trackInteraction("WebThreadsPanelThreadItem", ev, index ?? -1);
break;
}
},
},
<>
{sender}
{avatar}
{timestamp}
<div className="mx_EventTile_details">
{sender}
{isRenderingNotification && room ? (
<span className="mx_EventTile_truncated">
{" "}
{_t(
" in <strong>%(room)s</strong>",
{ room: room.name },
{ strong: (sub) => <strong>{sub}</strong> },
)}
</span>
) : (
""
)}
{timestamp}
</div>
{isRenderingNotification && room ? (
<div className="mx_EventTile_avatar">
<RoomAvatar room={room} width={28} height={28} />
</div>
) : (
avatar
)}
<div className={lineClasses} key="mx_EventTile_line">
<div className="mx_EventTile_body">
{this.props.mxEvent.isRedacted() ? (
@ -1350,24 +1330,13 @@ export class UnwrappedEventTile extends React.Component<EventTileProps, IState>
</div>
{this.renderThreadPanelSummary()}
</div>
<Toolbar className="mx_MessageActionBar" aria-label={_t("Message Actions")} aria-live="off">
<RovingAccessibleTooltipButton
className="mx_MessageActionBar_iconButton"
onClick={this.viewInRoom}
title={_t("View in room")}
key="view_in_room"
>
<ViewInRoomIcon />
</RovingAccessibleTooltipButton>
<RovingAccessibleTooltipButton
className="mx_MessageActionBar_iconButton"
onClick={this.copyLinkToThread}
title={_t("Copy link to thread")}
key="copy_link_to_thread"
>
<LinkIcon />
</RovingAccessibleTooltipButton>
</Toolbar>
{this.context.timelineRenderingType === TimelineRenderingType.ThreadsList && (
<EventTileThreadToolbar
viewInRoom={this.viewInRoom}
copyLinkToThread={this.copyLinkToThread}
/>
)}
{msgOption}
<UnreadNotificationBadge room={room} threadId={this.props.mxEvent.getId()} />
</>,