forked from Mirrors/gomuks
web/timeline: add room name and avatar event bodies
This commit is contained in:
parent
ef937ae0d8
commit
297193fa73
5 changed files with 125 additions and 0 deletions
|
@ -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
|
||||
|
|
52
web/src/ui/timeline/content/RoomAvatarBody.tsx
Normal file
52
web/src/ui/timeline/content/RoomAvatarBody.tsx
Normal 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
|
45
web/src/ui/timeline/content/RoomNameBody.tsx
Normal file
45
web/src/ui/timeline/content/RoomNameBody.tsx
Normal 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
|
|
@ -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;
|
||||
|
|
|
@ -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:
|
||||
|
|
Loading…
Add table
Reference in a new issue