commit 875cc90da173b926c56ee49d768459c7072f0238
parent a97d52191d45e0002c40503f9340597a7be87339
Author: MTRNord <mtrnord1@gmail.com>
Date: Fri, 19 May 2023 20:21:32 +0200
Implement MSC4015 and make displaynames smaller
Diffstat:
5 files changed, 164 insertions(+), 112 deletions(-)
diff --git a/src/app/sdk/room.ts b/src/app/sdk/room.ts
@@ -223,18 +223,26 @@ export class Room extends EventEmitter {
public getMemberAvatar(userID: string, size: number = 32): string | undefined {
let avatarURL: string | undefined = undefined;
this.stateEvents.forEach((event) => {
- if (event.type === "m.room.member") {
- if (event.state_key === userID && event.content.membership == "join") {
- const rawAvatarURL = event.content.avatar_url;
- if (rawAvatarURL?.startsWith("mxc://")) {
- avatarURL = this.client.convertMXC(rawAvatarURL, size);
- }
+ if (event.type === "m.room.member" && event.state_key === userID && event.content.membership == "join") {
+ const rawAvatarURL = event.content.avatar_url;
+ if (rawAvatarURL?.startsWith("mxc://")) {
+ avatarURL = this.client.convertMXC(rawAvatarURL, size);
}
}
});
return avatarURL;
}
+ public isBot(userID: string): boolean {
+ let isBot: boolean = false;
+ this.stateEvents.forEach((event) => {
+ if (event.type === "m.room.member" && event.state_key === userID && event.content.membership == "join") {
+ isBot = event.content.bot;
+ }
+ });
+ return isBot;
+ }
+
public isEncrypted(): boolean {
let isEncrypted: boolean = false;
this.stateEvents.forEach((event) => {
diff --git a/src/components/avatar/avatar.tsx b/src/components/avatar/avatar.tsx
@@ -18,6 +18,11 @@ type AvatarProps = {
* Wether the user is online. Only used if dm is true.
*/
online: OnlineState
+ /**
+ * Wether the user is a bot
+ * @default false
+ */
+ isBot: boolean
};
const Avatar: FC<AvatarProps> = memo(({ avatarUrl, displayname, dm = false, online = OnlineState.Unknown }: AvatarProps) => {
diff --git a/src/components/events/messageEvent.tsx b/src/components/events/messageEvent.tsx
@@ -1,7 +1,6 @@
import { memo, useContext, useEffect, useState } from "react";
import { IRoomEvent, isRoomMessageAudioEvent, isRoomMessageImageEvent, isRoomMessageNoticeEvent, isRoomMessageTextEvent } from "../../app/sdk/api/events";
import { FC } from "react";
-import Avatar from "../avatar/avatar";
import { MatrixClient, MatrixContext } from "../../app/sdk/client";
import Linkify from "linkify-react";
import linkifyHtml from 'linkify-html';
@@ -13,6 +12,7 @@ import hljs from 'highlight.js';
import 'highlight.js/styles/base16/solarized-dark.css';
import { OnlineState } from "../../app/sdk/api/otherEnums";
import { Room } from "../../app/sdk/room";
+import { MessageWrapper } from "./wrapper";
type MessageEventProps = {
/**
@@ -117,24 +117,22 @@ const MessageEvent: FC<MessageEventProps> = memo(({ event, room, hasPreviousEven
}
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) || event.sender}
- avatarUrl={room?.getMemberAvatar(event.sender)}
- online={room?.presence || OnlineState.Unknown}
- dm={room?.isDM() || false}
- />}
- <div className={!hasPreviousEvent ? "flex flex-col gap-1" : "ml-[3.7rem]"}>
- {!hasPreviousEvent && <h2 className="text-xl 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-[50rem] max-w-[50rem] h-[unset]"
- />
- </div>
- </div>
+ <MessageWrapper
+ displayname={room?.getMemberName(event.sender) || event.sender}
+ avatar_url={room?.getMemberAvatar(event.sender) || ""}
+ onlineState={room?.presence || OnlineState.Unknown}
+ isBot={room?.isBot(event.sender) || false}
+ dm={room?.isDM() || false}
+ hasPreviousEvent={hasPreviousEvent}
+ >
+ {/* 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-[50rem] max-w-[50rem] h-[unset]"
+ />
+ </MessageWrapper>
)
} else if (isRoomMessageAudioEvent(event)) {
const [url, setUrl] = useState<string | undefined>(undefined);
@@ -169,33 +167,29 @@ const MessageEvent: FC<MessageEventProps> = memo(({ event, room, hasPreviousEven
}
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) || event.sender}
- avatarUrl={room?.getMemberAvatar(event.sender)}
- online={room?.presence || OnlineState.Unknown}
- dm={room?.isDM() || false}
- />}
- <div className={!hasPreviousEvent ? "flex flex-col gap-1" : "flex-1 ml-[3.7rem]"}>
- {!hasPreviousEvent && <h2 className="text-xl font-medium text-red-500 whitespace-pre-wrap">{room?.getMemberName(event.sender) || event.sender}</h2>}
- {url && <Waveform src_url={url} />}
- </div>
- </div>
+ <MessageWrapper
+ displayname={room?.getMemberName(event.sender) || event.sender}
+ avatar_url={room?.getMemberAvatar(event.sender) || ""}
+ onlineState={room?.presence || OnlineState.Unknown}
+ isBot={room?.isBot(event.sender) || false}
+ dm={room?.isDM() || false}
+ hasPreviousEvent={hasPreviousEvent}
+ >
+ {url && <Waveform src_url={url} />}
+ </MessageWrapper>
)
} else {
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) || event.sender}
- avatarUrl={room?.getMemberAvatar(event.sender)}
- online={room?.presence || OnlineState.Unknown}
- dm={room?.isDM() || false}
- />}
- <div className={!hasPreviousEvent ? "flex flex-col gap-1" : "ml-[3.7rem]"}>
- {!hasPreviousEvent && <h2 className="text-xl font-medium text-red-500 whitespace-pre-wrap">{room?.getMemberName(event.sender) || event.sender}</h2>}
- <Linkify options={linkifyOptions} as='p' className="text-black text-base font-normal">{event.content.body}</Linkify>
- </div>
- </div>
+ <MessageWrapper
+ displayname={room?.getMemberName(event.sender) || event.sender}
+ avatar_url={room?.getMemberAvatar(event.sender) || ""}
+ onlineState={room?.presence || OnlineState.Unknown}
+ isBot={room?.isBot(event.sender) || false}
+ dm={room?.isDM() || false}
+ hasPreviousEvent={hasPreviousEvent}
+ >
+ <Linkify options={linkifyOptions} as='p' className="text-black text-base font-normal">{event.content.body}</Linkify>
+ </MessageWrapper >
)
}
}
@@ -286,34 +280,30 @@ const TextMessage: FC<TextMessage> = memo(({ event, room, hasPreviousEvent, mess
// 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) || event.sender}
- avatarUrl={room?.getMemberAvatar(event.sender)}
- online={room?.presence || OnlineState.Unknown}
- dm={room?.isDM() || false}
- />}
- <div className={!hasPreviousEvent ? "flex flex-col gap-1" : "ml-[3.7rem]"}>
- {!hasPreviousEvent && <h2 className="text-xl font-medium text-red-500 whitespace-pre-wrap">{room?.getMemberName(event.sender) || event.sender}</h2>}
- {/* TODO: Fixme */}
- <p className={`${text_color} text-base font-normal`} dangerouslySetInnerHTML={{ __html: linkified }}></p>
- </div>
- </div>
+ <MessageWrapper
+ displayname={room?.getMemberName(event.sender) || event.sender}
+ avatar_url={room?.getMemberAvatar(event.sender) || ""}
+ onlineState={room?.presence || OnlineState.Unknown}
+ isBot={room?.isBot(event.sender) || false}
+ dm={room?.isDM() || false}
+ hasPreviousEvent={hasPreviousEvent}
+ >
+ {/* TODO: Fixme */}
+ <p className={`${text_color} text-base font-normal`} dangerouslySetInnerHTML={{ __html: linkified }}></p>
+ </MessageWrapper>
)
} else {
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) || event.sender}
- avatarUrl={room?.getMemberAvatar(event.sender)}
- online={room?.presence || OnlineState.Unknown}
- dm={room?.isDM() || false}
- />}
- <div className={!hasPreviousEvent ? "flex flex-col gap-1" : "ml-[3.7rem]"}>
- {!hasPreviousEvent && <h2 className="text-xl font-medium text-red-500 whitespace-pre-wrap">{room?.getMemberName(event.sender) || event.sender}</h2>}
- <Linkify options={linkifyOptions} as='p' className={`${text_color} text-base font-normal`}>{event.content.body}</Linkify>
- </div>
- </div>
+ <MessageWrapper
+ displayname={room?.getMemberName(event.sender) || event.sender}
+ avatar_url={room?.getMemberAvatar(event.sender) || ""}
+ onlineState={room?.presence || OnlineState.Unknown}
+ isBot={room?.isBot(event.sender) || false}
+ dm={room?.isDM() || false}
+ hasPreviousEvent={hasPreviousEvent}
+ >
+ <Linkify options={linkifyOptions} as='p' className={`${text_color} text-base font-normal`}>{event.content.body}</Linkify>
+ </MessageWrapper>
)
}
})
diff --git a/src/components/events/unknownEvent.tsx b/src/components/events/unknownEvent.tsx
@@ -2,9 +2,9 @@ import { memo } from "react";
import { IRoomEvent } from "../../app/sdk/api/events";
import { FC } from "react";
import Linkify from "linkify-react";
-import Avatar from "../avatar/avatar";
import { OnlineState } from "../../app/sdk/api/otherEnums";
import { Room } from "../../app/sdk/room";
+import { MessageWrapper } from "./wrapper";
type UnknownEventProps = {
/**
@@ -30,18 +30,16 @@ const linkifyOptions = {
const UnknownEvent: FC<UnknownEventProps> = memo(({ event, room, hasPreviousEvent }) => {
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) || event.sender}
- avatarUrl={room?.getMemberAvatar(event.sender)}
- online={room?.presence || OnlineState.Unknown}
- dm={room?.isDM() || false}
- />}
- <div className={!hasPreviousEvent ? "flex flex-col gap-1" : "ml-[3.7rem]"}>
- {!hasPreviousEvent && <h2 className="text-xl 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>
+ <MessageWrapper
+ displayname={room?.getMemberName(event.sender) || event.sender}
+ avatar_url={room?.getMemberAvatar(event.sender) || ""}
+ onlineState={room?.presence || OnlineState.Unknown}
+ isBot={room?.isBot(event.sender) || false}
+ dm={room?.isDM() || false}
+ hasPreviousEvent={hasPreviousEvent}
+ >
+ <Linkify options={linkifyOptions} as='p' className="whitespace-break-spaces text-black text-base font-normal">{JSON.stringify(event, null, 4)}</Linkify>
+ </MessageWrapper>
)
});
@@ -64,18 +62,16 @@ type UndecryptableEventProps = {
export const UndecryptableEvent: FC<UndecryptableEventProps> = memo(({ event, room, hasPreviousEvent }) => {
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) || event.sender}
- avatarUrl={room?.getMemberAvatar(event.sender)}
- online={room?.presence || OnlineState.Unknown}
- dm={room?.isDM() || false}
- />}
- <div className={!hasPreviousEvent ? "flex flex-col gap-1" : "ml-[3.7rem]"}>
- {!hasPreviousEvent && <h2 className="text-xl 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>
+ <MessageWrapper
+ displayname={room?.getMemberName(event.sender) || event.sender}
+ avatar_url={room?.getMemberAvatar(event.sender) || ""}
+ onlineState={room?.presence || OnlineState.Unknown}
+ isBot={room?.isBot(event.sender) || false}
+ dm={room?.isDM() || false}
+ hasPreviousEvent={hasPreviousEvent}
+ >
+ <Linkify options={linkifyOptions} as='p' className="whitespace-pre-wrap text-base font-normal text-orange-600">Unable to decrypt event</Linkify>
+ </MessageWrapper>
)
});
@@ -100,19 +96,17 @@ type RedactedEventProps = {
export const RedactedEvent: FC<RedactedEventProps> = memo(({ event, redacted_because, room, hasPreviousEvent }) => {
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) || event.sender}
- avatarUrl={room?.getMemberAvatar(event.sender)}
- online={room?.presence || OnlineState.Unknown}
- dm={room?.isDM() || false}
- />}
- <div className={!hasPreviousEvent ? "flex flex-col gap-1" : "ml-[3.7rem]"}>
- {!hasPreviousEvent && <h2 className="text-xl 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>
+ <MessageWrapper
+ displayname={room?.getMemberName(event.sender) || event.sender}
+ avatar_url={room?.getMemberAvatar(event.sender) || ""}
+ onlineState={room?.presence || OnlineState.Unknown}
+ isBot={room?.isBot(event.sender) || false}
+ dm={room?.isDM() || false}
+ hasPreviousEvent={hasPreviousEvent}
+ >
+ {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>}
+ </MessageWrapper>
)
});
diff --git a/src/components/events/wrapper.tsx b/src/components/events/wrapper.tsx
@@ -0,0 +1,54 @@
+import { FC, memo } from "react";
+import { OnlineState } from "../../app/sdk/api/otherEnums";
+import Avatar from "../avatar/avatar";
+import { Bot } from "lucide-react";
+
+export type MessageWrapperProps = {
+ /**
+ * The displayname to render
+ */
+ displayname: string;
+ /**
+ * The avatar_url to render
+ */
+ avatar_url: string;
+ /**
+ * If the sender is a bot
+ */
+ isBot: boolean;
+ /**
+ * If the sender is online
+ */
+ onlineState: OnlineState;
+ /**
+ * If the room is a DM
+ */
+ dm: boolean;
+ /**
+ * If the previous event was sent by the same user
+ */
+ hasPreviousEvent?: boolean;
+ /**
+ * Children to render
+ */
+ children: React.ReactNode;
+};
+
+
+export const MessageWrapper: FC<MessageWrapperProps> = memo(({ hasPreviousEvent, displayname, avatar_url, onlineState, isBot, dm, children }) => {
+ 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={displayname}
+ avatarUrl={avatar_url}
+ online={onlineState}
+ dm={dm}
+ isBot={isBot}
+ />}
+ <div className={!hasPreviousEvent ? "flex flex-col gap-1" : "ml-[3.7rem]"}>
+ {!hasPreviousEvent && <h2 className="flex flex-row items-center gap-2 text-base font-medium text-red-500 whitespace-pre-wrap">{isBot ? <Bot size={16} /> : <></>}{displayname}</h2>}
+ {children}
+ </div>
+ </div>
+ )
+});
+\ No newline at end of file