1
0
Fork 0
forked from Mirrors/gomuks

web/timeline: add room name and avatar event bodies

This commit is contained in:
Tulir Asokan 2024-11-27 01:13:27 +02:00
parent ef937ae0d8
commit 297193fa73
5 changed files with 125 additions and 0 deletions

View file

@ -73,6 +73,18 @@ export interface MemberEventContent extends UserProfile {
reason?: string
}
export interface RoomAvatarEventContent {
url?: ContentURI
}
export interface RoomNameEventContent {
name?: string
}
export interface RoomTopicEventContent {
topic?: string
}
export interface ACLEventContent {
allow?: string[]
allow_ip_literals?: boolean

View file

@ -0,0 +1,52 @@
// gomuks - A Matrix client written in Go.
// Copyright (C) 2024 Tulir Asokan
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import { JSX, use } from "react"
import { getRoomAvatarURL } from "@/api/media.ts"
import { ContentURI, RoomAvatarEventContent } from "@/api/types"
import { ensureString } from "@/util/validation.ts"
import { LightboxContext } from "../../modal/Lightbox.tsx"
import EventContentProps from "./props.ts"
const RoomAvatarBody = ({ event, sender, room }: EventContentProps) => {
const content = event.content as RoomAvatarEventContent
const prevContent = event.unsigned.prev_content as RoomAvatarEventContent | undefined
let changeDescription: JSX.Element | string
const oldURL = ensureString(prevContent?.url)
const newURL = ensureString(content.url)
const openLightbox = use(LightboxContext)!
const makeAvatar = (url: ContentURI) => <img
className="small avatar"
loading="lazy"
height={16}
src={getRoomAvatarURL(room.meta.current, url)}
onClick={openLightbox}
alt=""
/>
if (oldURL === newURL) {
changeDescription = "sent a room avatar event with no change"
} else if (oldURL && newURL) {
changeDescription = <>changed the room avatar from {makeAvatar(oldURL)} to {makeAvatar(newURL)}</>
} else if (!oldURL) {
changeDescription = <>set the room avatar to {makeAvatar(newURL)}</>
} else {
changeDescription = "removed the room avatar"
}
return <div className="room-avatar-body">
{sender?.content.displayname ?? event.sender} {changeDescription}
</div>
}
export default RoomAvatarBody

View file

@ -0,0 +1,45 @@
// gomuks - A Matrix client written in Go.
// Copyright (C) 2024 Tulir Asokan
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import { JSX } from "react"
import { RoomNameEventContent } from "@/api/types"
import { ensureString } from "@/util/validation.ts"
import EventContentProps from "./props.ts"
function bidiIsolate(str: string): JSX.Element {
return <span style={{ unicodeBidi: "isolate" }}>{str}</span>
}
const RoomNameBody = ({ event, sender }: EventContentProps) => {
const content = event.content as RoomNameEventContent
const prevContent = event.unsigned.prev_content as RoomNameEventContent | undefined
let changeDescription: JSX.Element | string
const oldName = ensureString(prevContent?.name)
const newName = ensureString(content.name)
if (oldName === newName) {
changeDescription = "sent a room name event with no change"
} else if (oldName && newName) {
changeDescription = <>changed the room name from {bidiIsolate(oldName)} to {bidiIsolate(newName)}</>
} else if (!oldName) {
changeDescription = <>set the room name to {bidiIsolate(newName)}</>
} else {
changeDescription = "removed the room name"
}
return <div className="room-name-body">
{sender?.content.displayname ?? event.sender} {changeDescription}
</div>
}
export default RoomNameBody

View file

@ -44,6 +44,12 @@ div.member-body {
}
}
div.room-avatar-body {
display: flex;
align-items: center;
gap: .25rem;
}
div.message-text {
&.plaintext-body {
white-space: pre-wrap;

View file

@ -8,6 +8,8 @@ import MemberBody from "./MemberBody.tsx"
import PinnedEventsBody from "./PinnedEventsBody.tsx"
import PowerLevelBody from "./PowerLevelBody.tsx"
import RedactedBody from "./RedactedBody.tsx"
import RoomAvatarBody from "./RoomAvatarBody.tsx"
import RoomNameBody from "./RoomNameBody.tsx"
import TextMessageBody from "./TextMessageBody.tsx"
import UnknownMessageBody from "./UnknownMessageBody.tsx"
import EventContentProps from "./props.ts"
@ -22,6 +24,8 @@ export { default as MemberBody } from "./MemberBody.tsx"
export { default as PinnedEventsBody } from "./PinnedEventsBody.tsx"
export { default as PowerLevelBody } from "./PowerLevelBody.tsx"
export { default as RedactedBody } from "./RedactedBody.tsx"
export { default as RoomAvatarBody } from "./RoomAvatarBody.tsx"
export { default as RoomNameBody } from "./RoomNameBody.tsx"
export { default as TextMessageBody } from "./TextMessageBody.tsx"
export { default as UnknownMessageBody } from "./UnknownMessageBody.tsx"
export type { default as EventContentProps } from "./props.ts"
@ -68,6 +72,10 @@ export function getBodyType(evt: MemDBEvent, forReply = false): React.FunctionCo
return EncryptedBody
case "m.room.member":
return MemberBody
case "m.room.name":
return RoomNameBody
case "m.room.avatar":
return RoomAvatarBody
case "m.room.server_acl":
return ACLBody
case "m.room.pinned_events":
@ -82,6 +90,8 @@ export function isSmallEvent(bodyType: React.FunctionComponent<EventContentProps
switch (bodyType) {
case HiddenEvent:
case MemberBody:
case RoomNameBody:
case RoomAvatarBody:
case ACLBody:
case PinnedEventsBody:
case PowerLevelBody: