cetirizine

An experimental matrix client written in reactjs utilizing tailwind and storybook
git clone git://archive.git.mtrnord.blog/MTRNord/cetirizine.git
Log | Files | Refs | README | LICENSE

commit 4765cb4f2017b24360080848b0ad65aa7d3f4665
parent f7a5fd119a689c7f07555e50acb9e96c23932c3b
Author: MTRNord <mtrnord1@gmail.com>
Date:   Sat,  6 May 2023 21:31:20 +0200

Add events to storybook

Diffstat:
Mpackage-lock.json | 35+++++++++--------------------------
Mpackage.json | 1+
Msrc/app/sdk/api/apiTypes.ts | 1+
Asrc/components/events/memberEvent.stories.tsx | 126+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/components/events/messageEvent.stories.tsx | 83+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/components/events/messageEvent.tsx | 26++++++++++++++------------
Asrc/components/events/redactedEvent.stories.tsx | 60++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/components/events/undecryptableEvent.stories.tsx | 35+++++++++++++++++++++++++++++++++++
Asrc/components/events/unknownEvent.stories.tsx | 35+++++++++++++++++++++++++++++++++++
Msrc/components/events/unknownEvent.tsx | 18+++++++++---------
Msrc/index.scss | 7+++++++
11 files changed, 380 insertions(+), 47 deletions(-)

diff --git a/package-lock.json b/package-lock.json @@ -14,6 +14,7 @@ "events": "^3.3.0", "idb": "^7.1.1", "lexical": "^0.10.0", + "linkify-html": "^4.1.1", "linkify-react": "^4.1.1", "linkifyjs": "^4.1.1", "lucide-react": "^0.214.0", @@ -25,7 +26,6 @@ "typescript": "^5.0.4" }, "devDependencies": { - "@geometricpanda/storybook-addon-badges": "^2.0.0", "@percy/cli": "^1.24.0", "@percy/storybook": "^4.3.6", "@storybook/addon-a11y": "^7.0.9", @@ -2379,31 +2379,6 @@ "integrity": "sha512-cEee/Z+I12mZcFJshKcCqC8tuX5hG3s+d+9nZ3LabqKF1vKdF41B92pJVCBggjAGORAeOzyyDDKrZwIkLffeOQ==", "dev": true }, - "node_modules/@geometricpanda/storybook-addon-badges": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@geometricpanda/storybook-addon-badges/-/storybook-addon-badges-2.0.0.tgz", - "integrity": "sha512-M1CQabr1/IDG6ku0/+n6kZBvWTCSun7LndkGsaB89nTNaCcflWxflgY2HdcbjblLL8W0iT7QiW9TgWP4kcpn5Q==", - "dev": true, - "peerDependencies": { - "@storybook/blocks": "^7.0.0", - "@storybook/components": "^7.0.0", - "@storybook/core-events": "^7.0.0", - "@storybook/manager-api": "^7.0.0", - "@storybook/preview-api": "^7.0.0", - "@storybook/theming": "^7.0.0", - "@storybook/types": "^7.0.0", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" - }, - "peerDependenciesMeta": { - "react": { - "optional": true - }, - "react-dom": { - "optional": true - } - } - }, "node_modules/@istanbuljs/load-nyc-config": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", @@ -10297,6 +10272,14 @@ "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", "dev": true }, + "node_modules/linkify-html": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/linkify-html/-/linkify-html-4.1.1.tgz", + "integrity": "sha512-7RcF7gIhEOGBBvs7orCJ2tevaz7iF0ZLZSRPWNNBOnW/uGjOOQYB+ztSeHF6dchMC2dM9H8zZlt6Z959bjteaw==", + "peerDependencies": { + "linkifyjs": "^4.0.0" + } + }, "node_modules/linkify-react": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/linkify-react/-/linkify-react-4.1.1.tgz", diff --git a/package.json b/package.json @@ -10,6 +10,7 @@ "events": "^3.3.0", "idb": "^7.1.1", "lexical": "^0.10.0", + "linkify-html": "^4.1.1", "linkify-react": "^4.1.1", "linkifyjs": "^4.1.1", "lucide-react": "^0.214.0", diff --git a/src/app/sdk/api/apiTypes.ts b/src/app/sdk/api/apiTypes.ts @@ -346,6 +346,7 @@ export interface IRoomMemberContent { displayname?: string; membership: "invite" | "join" | "knock" | "leave" | "ban"; is_direct?: boolean; + reason?: string; } export interface IRoomMemberEvent extends IRoomStateEvent<IRoomMemberContent> { } diff --git a/src/components/events/memberEvent.stories.tsx b/src/components/events/memberEvent.stories.tsx @@ -0,0 +1,125 @@ +import { Meta, StoryObj } from '@storybook/react'; +import MemberEvent from './memberEvent'; + +const meta: Meta<typeof MemberEvent> = { + title: 'Chat/Events/MembershipEvents', + component: MemberEvent, +}; + +export default meta; +type Story = StoryObj<typeof MemberEvent>; + +export const JoinEvent: Story = { + args: { + event: { + content: { + avatar_url: "mxc://example.org/SEsfnsuifSDFSSEF", + displayname: "Alice Margatroid", + membership: "join", + reason: "Looking for support" + }, + event_id: "$143273582443PhrSn:example.org", + origin_server_ts: 1432735824653, + room_id: "!jEsUZKDJdhlrceRyVU:example.org", + sender: "@example:example.org", + state_key: "@alice:example.org", + type: "m.room.member", + unsigned: { + "age": 1234 + } + } + + } +}; + +export const LeaveEvent: Story = { + args: { + event: { + content: { + avatar_url: "mxc://example.org/SEsfnsuifSDFSSEF", + displayname: "Alice Margatroid", + membership: "leave", + reason: "Looking for support" + }, + event_id: "$143273582443PhrSn:example.org", + origin_server_ts: 1432735824653, + room_id: "!jEsUZKDJdhlrceRyVU:example.org", + sender: "@example:example.org", + state_key: "@alice:example.org", + type: "m.room.member", + unsigned: { + "age": 1234 + } + } + + } +}; + +export const InviteEvent: Story = { + args: { + event: { + content: { + avatar_url: "mxc://example.org/SEsfnsuifSDFSSEF", + displayname: "Alice Margatroid", + membership: "invite", + reason: "Looking for support" + }, + event_id: "$143273582443PhrSn:example.org", + origin_server_ts: 1432735824653, + room_id: "!jEsUZKDJdhlrceRyVU:example.org", + sender: "@example:example.org", + state_key: "@alice:example.org", + type: "m.room.member", + unsigned: { + "age": 1234 + } + } + + } +}; + +export const KnockEvent: Story = { + args: { + event: { + content: { + avatar_url: "mxc://example.org/SEsfnsuifSDFSSEF", + displayname: "Alice Margatroid", + membership: "knock", + reason: "Looking for support" + }, + event_id: "$143273582443PhrSn:example.org", + origin_server_ts: 1432735824653, + room_id: "!jEsUZKDJdhlrceRyVU:example.org", + sender: "@example:example.org", + state_key: "@alice:example.org", + type: "m.room.member", + unsigned: { + "age": 1234 + } + } + + } +}; + +export const BanEvent: Story = { + args: { + event: { + content: { + avatar_url: "mxc://example.org/SEsfnsuifSDFSSEF", + displayname: "Alice Margatroid", + membership: "ban", + reason: "Looking for support" + }, + event_id: "$143273582443PhrSn:example.org", + origin_server_ts: 1432735824653, + room_id: "!jEsUZKDJdhlrceRyVU:example.org", + sender: "@example:example.org", + state_key: "@alice:example.org", + type: "m.room.member", + unsigned: { + "age": 1234 + } + } + + } +}; +\ No newline at end of file diff --git a/src/components/events/messageEvent.stories.tsx b/src/components/events/messageEvent.stories.tsx @@ -0,0 +1,82 @@ +import { Meta, StoryObj } from '@storybook/react'; +import MessageEvent from './messageEvent'; +import { defaultMatrixClient } from '../../app/sdk/client'; + +const meta: Meta<typeof MessageEvent> = { + title: 'Chat/Events/MessageEvents', + component: MessageEvent, +}; + +export default meta; +type Story = StoryObj<typeof MessageEvent>; + +export const TextEvent: Story = { + args: { + event: { + content: { + body: "._.", + msgtype: "m.text" + }, + origin_server_ts: 1683125956695, + sender: "@example:example.org", + type: "m.room.message", + unsigned: {}, + event_id: "$143273582443PhrSn:example.org", + room_id: "!jEsUZKDJdhlrceRyVU:example.org" + } + } +}; + +export const FormattedEvent: Story = { + args: { + event: { + content: { + body: "I think it's mostly a matter of doing the right `padding-block` in `.content`", + format: "org.matrix.custom.html", + formatted_body: "I think it's mostly a matter of doing the right <code>padding-block</code> in <code>.content</code>", + msgtype: "m.text" + }, + origin_server_ts: 1683125956695, + sender: "@example:example.org", + type: "m.room.message", + unsigned: {}, + event_id: "$143273582443PhrSn:example.org", + room_id: "!jEsUZKDJdhlrceRyVU:example.org" + } + } +}; + +// @ts-ignore +defaultMatrixClient.hostname = "https://matrix.org"; + +export const ImageEvent: Story = { + args: { + event: { + content: { + body: "image.png", + info: { + h: 680, + mimetype: "image/png", + size: 252133, + thumbnail_info: { + h: 600, + mimetype: "image/png", + size: 223185, + w: 600 + }, + thumbnail_url: "mxc://midnightthoughts.space/9824fd09c998f8304935bf78c047146332fea9f7", + w: 680, + "xyz.amorgan.blurhash": "UaQ]+tElbcs,NFR*bIj@_4$ejYWXxas:n$oL" + }, + msgtype: "m.image", + url: "mxc://midnightthoughts.space/59ced67d3dfbcc5c129c3e12d3083bf5652f6ff5" + }, + origin_server_ts: 1683125956695, + sender: "@example:example.org", + type: "m.room.message", + unsigned: {}, + event_id: "$143273582443PhrSn:example.org", + room_id: "!jEsUZKDJdhlrceRyVU:example.org" + } + } +}; +\ No newline at end of file diff --git a/src/components/events/messageEvent.tsx b/src/components/events/messageEvent.tsx @@ -4,6 +4,7 @@ import { FC } from "react"; import Avatar from "../avatar/avatar"; import { MatrixContext, useRoom } from "../../app/sdk/client"; import Linkify from "linkify-react"; +import linkifyHtml from 'linkify-html'; import DOMPurify from "dompurify"; import { UndecryptableEvent } from "./unknownEvent"; import { decryptAttachment } from "matrix-encrypt-attachment"; @@ -82,20 +83,21 @@ const MessageEvent: FC<MessageEventProps> = memo(({ event, roomID, hasPreviousEv "summary" ] }) + const linkified = linkifyHtml(sanitized, linkifyOptions); // TODO: sanitize the attributes allowed by matrix spec return ( <div className={!hasPreviousEvent ? "flex flex-row gap-4 p-2 pb-1 hover:bg-gray-200 rounded-md duration-200 ease-in-out items-start" : "flex flex-row p-2 pb-1 pt-0 hover:bg-gray-200 rounded-md duration-200 ease-in-out"}> {!hasPreviousEvent && <Avatar - displayname={room?.getMemberName(event.sender) || ""} + displayname={room?.getMemberName(event.sender) || event.sender} avatarUrl={room?.getMemberAvatar(event.sender)} online={room?.isOnline() || false} dm={room?.isDM() || false} />} <div className={!hasPreviousEvent ? "flex flex-col gap-1" : "ml-[3.7rem]"}> - {!hasPreviousEvent && <h2 className="text-sm font-medium text-red-500 whitespace-normal">{room?.getMemberName(event.sender)}</h2>} + {!hasPreviousEvent && <h2 className="text-sm font-medium text-red-500 whitespace-pre-wrap">{room?.getMemberName(event.sender) || event.sender}</h2>} {/* TODO: Fixme */} - <p className="whitespace-normal text-black text-base font-normal" dangerouslySetInnerHTML={{ __html: sanitized }}></p> + <p className="whitespace-pre-wrap text-black text-base font-normal" dangerouslySetInnerHTML={{ __html: linkified }}></p> </div> </div> ) @@ -103,14 +105,14 @@ const MessageEvent: FC<MessageEventProps> = memo(({ event, roomID, hasPreviousEv return ( <div className={!hasPreviousEvent ? "flex flex-row gap-4 p-2 pb-1 hover:bg-gray-200 rounded-md duration-200 ease-in-out items-start" : "flex flex-row p-2 pb-1 pt-0 hover:bg-gray-200 rounded-md duration-200 ease-in-out"}> {!hasPreviousEvent && <Avatar - displayname={room?.getMemberName(event.sender) || ""} + displayname={room?.getMemberName(event.sender) || event.sender} avatarUrl={room?.getMemberAvatar(event.sender)} online={room?.isOnline() || false} dm={room?.isDM() || false} />} <div className={!hasPreviousEvent ? "flex flex-col gap-1" : "ml-[3.7rem]"}> - {!hasPreviousEvent && <h2 className="text-sm font-medium text-red-500 whitespace-normal">{room?.getMemberName(event.sender)}</h2>} - <Linkify options={linkifyOptions} as='p' className="whitespace-normal text-black text-base font-normal">{event.content.body}</Linkify> + {!hasPreviousEvent && <h2 className="text-sm font-medium text-red-500 whitespace-pre-wrap">{room?.getMemberName(event.sender) || event.sender}</h2>} + <Linkify options={linkifyOptions} as='p' className="whitespace-pre-wrap text-black text-base font-normal">{event.content.body}</Linkify> </div> </div> ) @@ -177,19 +179,19 @@ const MessageEvent: FC<MessageEventProps> = memo(({ event, roomID, hasPreviousEv return ( <div className={!hasPreviousEvent ? "flex flex-row gap-4 p-2 pb-1 hover:bg-gray-200 rounded-md duration-200 ease-in-out items-start" : "flex flex-row p-2 pb-1 pt-1 hover:bg-gray-200 rounded-md duration-200 ease-in-out"}> {!hasPreviousEvent && <Avatar - displayname={room?.getMemberName(event.sender) || ""} + displayname={room?.getMemberName(event.sender) || event.sender} avatarUrl={room?.getMemberAvatar(event.sender)} online={room?.isOnline() || false} dm={room?.isDM() || false} />} <div className={!hasPreviousEvent ? "flex flex-col gap-1" : "ml-[3.7rem]"}> - {!hasPreviousEvent && <h2 className="text-sm font-medium text-red-500 whitespace-normal">{room?.getMemberName(event.sender)}</h2>} + {!hasPreviousEvent && <h2 className="text-sm font-medium text-red-500 whitespace-pre-wrap">{room?.getMemberName(event.sender) || event.sender}</h2>} {/* TODO: Loading circle while image is loading */} <img src={url} alt={event.content.body} title={event.content.body} - className="rounded-md object-cover border-slate-400 border-2 max-h-[512px] max-w-[512px] h-[unset]" + className="rounded-md object-cover border-slate-400 border-2 max-h-[23.333rem] max-w-[23.333rem] h-[unset]" /> </div> </div> @@ -198,14 +200,14 @@ const MessageEvent: FC<MessageEventProps> = memo(({ event, roomID, hasPreviousEv return ( <div className={!hasPreviousEvent ? "flex flex-row gap-4 p-2 pb-1 hover:bg-gray-200 rounded-md duration-200 ease-in-out items-start" : "flex flex-row p-2 pb-1 pt-0 hover:bg-gray-200 rounded-md duration-200 ease-in-out"}> {!hasPreviousEvent && <Avatar - displayname={room?.getMemberName(event.sender) || ""} + displayname={room?.getMemberName(event.sender) || event.sender} avatarUrl={room?.getMemberAvatar(event.sender)} online={room?.isOnline() || false} dm={room?.isDM() || false} />} <div className={!hasPreviousEvent ? "flex flex-col gap-1" : "ml-[3.7rem]"}> - {!hasPreviousEvent && <h2 className="text-sm font-medium text-red-500 whitespace-normal">{room?.getMemberName(event.sender)}</h2>} - <Linkify options={linkifyOptions} as='p' className="whitespace-normal text-black text-base font-normal">{event.content.body}</Linkify> + {!hasPreviousEvent && <h2 className="text-sm font-medium text-red-500 whitespace-pre-wrap">{room?.getMemberName(event.sender) || event.sender}</h2>} + <Linkify options={linkifyOptions} as='p' className="whitespace-pre-wrap text-black text-base font-normal">{event.content.body}</Linkify> </div> </div> ) diff --git a/src/components/events/redactedEvent.stories.tsx b/src/components/events/redactedEvent.stories.tsx @@ -0,0 +1,59 @@ +import { Meta, StoryObj } from '@storybook/react'; +import { RedactedEvent } from './unknownEvent'; + +const meta: Meta<typeof RedactedEvent> = { + title: 'Chat/Events/RedactedEvent', + component: RedactedEvent, +}; + +export default meta; +type Story = StoryObj<typeof RedactedEvent>; + +export const RedactedEventEvent: Story = { + name: "Redacted Event", + args: { + event: { + content: { + avatar_url: "mxc://example.org/SEsfnsuifSDFSSEF", + displayname: "Alice Margatroid", + membership: "join", + reason: "Looking for support" + }, + event_id: "$143273582443PhrSn:example.org", + origin_server_ts: 1432735824653, + room_id: "!jEsUZKDJdhlrceRyVU:example.org", + sender: "@example:example.org", + state_key: "@alice:example.org", + type: "m.room.member", + unsigned: { + "age": 1234 + } + } + + } +}; + +export const RedactedEventReasonEvent: Story = { + name: "Redacted Event with Reason", + args: { + redacted_because: "Because I can", + event: { + content: { + avatar_url: "mxc://example.org/SEsfnsuifSDFSSEF", + displayname: "Alice Margatroid", + membership: "join", + reason: "Looking for support" + }, + event_id: "$143273582443PhrSn:example.org", + origin_server_ts: 1432735824653, + room_id: "!jEsUZKDJdhlrceRyVU:example.org", + sender: "@example:example.org", + state_key: "@alice:example.org", + type: "m.room.member", + unsigned: { + "age": 1234 + } + } + + } +}; +\ No newline at end of file diff --git a/src/components/events/undecryptableEvent.stories.tsx b/src/components/events/undecryptableEvent.stories.tsx @@ -0,0 +1,34 @@ +import { Meta, StoryObj } from '@storybook/react'; +import { UndecryptableEvent } from './unknownEvent'; + +const meta: Meta<typeof UndecryptableEvent> = { + title: 'Chat/Events/UndecryptableEvent', + component: UndecryptableEvent, +}; + +export default meta; +type Story = StoryObj<typeof UndecryptableEvent>; + +export const UndecryptableEventEvent: Story = { + name: "Undecryptable Event", + args: { + event: { + content: { + avatar_url: "mxc://example.org/SEsfnsuifSDFSSEF", + displayname: "Alice Margatroid", + membership: "join", + reason: "Looking for support" + }, + event_id: "$143273582443PhrSn:example.org", + origin_server_ts: 1432735824653, + room_id: "!jEsUZKDJdhlrceRyVU:example.org", + sender: "@example:example.org", + state_key: "@alice:example.org", + type: "m.room.member", + unsigned: { + "age": 1234 + } + } + + } +}; +\ No newline at end of file diff --git a/src/components/events/unknownEvent.stories.tsx b/src/components/events/unknownEvent.stories.tsx @@ -0,0 +1,34 @@ +import { Meta, StoryObj } from '@storybook/react'; +import UnknownEvent from './unknownEvent'; + +const meta: Meta<typeof UnknownEvent> = { + title: 'Chat/Events/UnknownEvent', + component: UnknownEvent, +}; + +export default meta; +type Story = StoryObj<typeof UnknownEvent>; + +export const UnknownEventEvent: Story = { + name: "Unknown Event", + args: { + event: { + content: { + avatar_url: "mxc://example.org/SEsfnsuifSDFSSEF", + displayname: "Alice Margatroid", + membership: "join", + reason: "Looking for support" + }, + event_id: "$143273582443PhrSn:example.org", + origin_server_ts: 1432735824653, + room_id: "!jEsUZKDJdhlrceRyVU:example.org", + sender: "@example:example.org", + state_key: "@alice:example.org", + type: "m.room.member", + unsigned: { + "age": 1234 + } + } + + } +}; +\ No newline at end of file diff --git a/src/components/events/unknownEvent.tsx b/src/components/events/unknownEvent.tsx @@ -33,13 +33,13 @@ const UnknownEvent: FC<UnknownEventProps> = memo(({ event, roomID, hasPreviousEv return ( <div className={!hasPreviousEvent ? "flex flex-row gap-4 p-2 pb-1 hover:bg-gray-200 rounded-md duration-200 ease-in-out items-start" : "flex flex-row p-2 pb-1 pt-0 hover:bg-gray-200 rounded-md duration-200 ease-in-out"}> {!hasPreviousEvent && <Avatar - displayname={room?.getMemberName(event.sender) || ""} + displayname={room?.getMemberName(event.sender) || event.sender} avatarUrl={room?.getMemberAvatar(event.sender)} online={room?.isOnline() || false} dm={room?.isDM() || false} />} <div className={!hasPreviousEvent ? "flex flex-col gap-1" : "ml-[3.7rem]"}> - {!hasPreviousEvent && <h2 className="text-sm font-medium text-red-500 whitespace-normal">{room?.getMemberName(event.sender)}</h2>} + {!hasPreviousEvent && <h2 className="text-sm font-medium text-red-500 whitespace-pre-wrap">{room?.getMemberName(event.sender) || event.sender}</h2>} <Linkify options={linkifyOptions} as='p' className="whitespace-break-spaces text-black text-base font-normal">{JSON.stringify(event, null, 4)}</Linkify> </div> </div> @@ -69,14 +69,14 @@ export const UndecryptableEvent: FC<UndecryptableEventProps> = memo(({ event, ro return ( <div className={!hasPreviousEvent ? "flex flex-row gap-4 p-2 pb-1 hover:bg-gray-200 rounded-md duration-200 ease-in-out items-start" : "flex flex-row p-2 pb-1 pt-0 hover:bg-gray-200 rounded-md duration-200 ease-in-out"}> {!hasPreviousEvent && <Avatar - displayname={room?.getMemberName(event.sender) || ""} + displayname={room?.getMemberName(event.sender) || event.sender} avatarUrl={room?.getMemberAvatar(event.sender)} online={room?.isOnline() || false} dm={room?.isDM() || false} />} <div className={!hasPreviousEvent ? "flex flex-col gap-1" : "ml-[3.7rem]"}> - {!hasPreviousEvent && <h2 className="text-sm font-medium text-red-500 whitespace-normal">{room?.getMemberName(event.sender)}</h2>} - <Linkify options={linkifyOptions} as='p' className="whitespace-normal text-base font-normal text-orange-600">Unable to decrypt event</Linkify> + {!hasPreviousEvent && <h2 className="text-sm font-medium text-red-500 whitespace-pre-wrap">{room?.getMemberName(event.sender) || event.sender}</h2>} + <Linkify options={linkifyOptions} as='p' className="whitespace-pre-wrap text-base font-normal text-orange-600">Unable to decrypt event</Linkify> </div> </div> ) @@ -107,15 +107,15 @@ export const RedactedEvent: FC<RedactedEventProps> = memo(({ event, redacted_bec return ( <div className={!hasPreviousEvent ? "flex flex-row gap-4 p-2 pb-1 hover:bg-gray-200 rounded-md duration-200 ease-in-out items-start" : "flex flex-row p-2 pb-1 pt-0 hover:bg-gray-200 rounded-md duration-200 ease-in-out"}> {!hasPreviousEvent && <Avatar - displayname={room?.getMemberName(event.sender) || ""} + displayname={room?.getMemberName(event.sender) || event.sender} avatarUrl={room?.getMemberAvatar(event.sender)} online={room?.isOnline() || false} dm={room?.isDM() || false} />} <div className={!hasPreviousEvent ? "flex flex-col gap-1" : "ml-[3.7rem]"}> - {!hasPreviousEvent && <h2 className="text-sm font-medium text-red-500 whitespace-normal">{room?.getMemberName(event.sender)}</h2>} - {redacted_because && <p className="whitespace-normal text-base font-normal text-blue-600 italic">{redacted_because}</p>} - <p className="whitespace-normal text-base font-normal text-blue-600 italic">Message was redacted</p> + {!hasPreviousEvent && <h2 className="text-sm font-medium text-red-500 whitespace-pre-wrap">{room?.getMemberName(event.sender) || event.sender}</h2>} + {redacted_because && <p className="whitespace-pre-wrap text-base font-normal text-blue-600 italic">Message was redacted: {redacted_because}</p>} + {!redacted_because && <p className="whitespace-pre-wrap text-base font-normal text-blue-600 italic">Message was redacted</p>} </div> </div> ) diff --git a/src/index.scss b/src/index.scss @@ -24,6 +24,13 @@ pre { } } +/** + * Blockquote should be light grey with a thick left border + */ +blockquote { + @apply border-l-4 border-gray-300 p-4 py-0 bg-slate-100 rounded-md; +} + /* Font families defined by this CSS: