mirror of
https://github.com/tulir/gomuks.git
synced 2025-04-20 10:33:41 -05:00
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
|
reason?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface RoomAvatarEventContent {
|
||||||
|
url?: ContentURI
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface RoomNameEventContent {
|
||||||
|
name?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface RoomTopicEventContent {
|
||||||
|
topic?: string
|
||||||
|
}
|
||||||
|
|
||||||
export interface ACLEventContent {
|
export interface ACLEventContent {
|
||||||
allow?: string[]
|
allow?: string[]
|
||||||
allow_ip_literals?: boolean
|
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 {
|
div.message-text {
|
||||||
&.plaintext-body {
|
&.plaintext-body {
|
||||||
white-space: pre-wrap;
|
white-space: pre-wrap;
|
||||||
|
|
|
@ -8,6 +8,8 @@ import MemberBody from "./MemberBody.tsx"
|
||||||
import PinnedEventsBody from "./PinnedEventsBody.tsx"
|
import PinnedEventsBody from "./PinnedEventsBody.tsx"
|
||||||
import PowerLevelBody from "./PowerLevelBody.tsx"
|
import PowerLevelBody from "./PowerLevelBody.tsx"
|
||||||
import RedactedBody from "./RedactedBody.tsx"
|
import RedactedBody from "./RedactedBody.tsx"
|
||||||
|
import RoomAvatarBody from "./RoomAvatarBody.tsx"
|
||||||
|
import RoomNameBody from "./RoomNameBody.tsx"
|
||||||
import TextMessageBody from "./TextMessageBody.tsx"
|
import TextMessageBody from "./TextMessageBody.tsx"
|
||||||
import UnknownMessageBody from "./UnknownMessageBody.tsx"
|
import UnknownMessageBody from "./UnknownMessageBody.tsx"
|
||||||
import EventContentProps from "./props.ts"
|
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 PinnedEventsBody } from "./PinnedEventsBody.tsx"
|
||||||
export { default as PowerLevelBody } from "./PowerLevelBody.tsx"
|
export { default as PowerLevelBody } from "./PowerLevelBody.tsx"
|
||||||
export { default as RedactedBody } from "./RedactedBody.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 TextMessageBody } from "./TextMessageBody.tsx"
|
||||||
export { default as UnknownMessageBody } from "./UnknownMessageBody.tsx"
|
export { default as UnknownMessageBody } from "./UnknownMessageBody.tsx"
|
||||||
export type { default as EventContentProps } from "./props.ts"
|
export type { default as EventContentProps } from "./props.ts"
|
||||||
|
@ -68,6 +72,10 @@ export function getBodyType(evt: MemDBEvent, forReply = false): React.FunctionCo
|
||||||
return EncryptedBody
|
return EncryptedBody
|
||||||
case "m.room.member":
|
case "m.room.member":
|
||||||
return MemberBody
|
return MemberBody
|
||||||
|
case "m.room.name":
|
||||||
|
return RoomNameBody
|
||||||
|
case "m.room.avatar":
|
||||||
|
return RoomAvatarBody
|
||||||
case "m.room.server_acl":
|
case "m.room.server_acl":
|
||||||
return ACLBody
|
return ACLBody
|
||||||
case "m.room.pinned_events":
|
case "m.room.pinned_events":
|
||||||
|
@ -82,6 +90,8 @@ export function isSmallEvent(bodyType: React.FunctionComponent<EventContentProps
|
||||||
switch (bodyType) {
|
switch (bodyType) {
|
||||||
case HiddenEvent:
|
case HiddenEvent:
|
||||||
case MemberBody:
|
case MemberBody:
|
||||||
|
case RoomNameBody:
|
||||||
|
case RoomAvatarBody:
|
||||||
case ACLBody:
|
case ACLBody:
|
||||||
case PinnedEventsBody:
|
case PinnedEventsBody:
|
||||||
case PowerLevelBody:
|
case PowerLevelBody:
|
||||||
|
|
Loading…
Add table
Reference in a new issue