web/roomview: add invite metadata section

Closes #559
This commit is contained in:
Tulir Asokan 2024-12-20 23:52:25 +02:00
parent 800331f536
commit 08830331d7
3 changed files with 58 additions and 8 deletions

View file

@ -32,6 +32,7 @@ import type { RoomListEntry, StateStore } from "./main.ts"
export class InvitedRoomStore implements RoomListEntry, RoomSummary { export class InvitedRoomStore implements RoomListEntry, RoomSummary {
readonly room_id: RoomID readonly room_id: RoomID
readonly sorting_timestamp: number readonly sorting_timestamp: number
readonly date: string
readonly name: string = "" readonly name: string = ""
readonly search_name: string readonly search_name: string
readonly dm_user_id?: UserID readonly dm_user_id?: UserID
@ -40,13 +41,16 @@ export class InvitedRoomStore implements RoomListEntry, RoomSummary {
readonly avatar?: ContentURI readonly avatar?: ContentURI
readonly encryption?: "m.megolm.v1.aes-sha2" readonly encryption?: "m.megolm.v1.aes-sha2"
readonly room_version?: RoomVersion readonly room_version?: RoomVersion
readonly join_rules?: JoinRule readonly join_rule?: JoinRule
readonly invited_by?: UserID readonly invited_by?: UserID
readonly inviter_profile?: MemberEventContent readonly inviter_profile?: MemberEventContent
readonly is_direct: boolean
constructor(public readonly meta: DBInvitedRoom, parent: StateStore) { constructor(public readonly meta: DBInvitedRoom, parent: StateStore) {
this.room_id = meta.room_id this.room_id = meta.room_id
this.sorting_timestamp = 1000000000000000 + meta.created_at this.sorting_timestamp = 1000000000000000 + meta.created_at
this.date = new Date(meta.created_at - new Date().getTimezoneOffset() * 60000)
.toISOString().replace("T", " ").replace("Z", "")
const members = new Map<UserID, StrippedStateEvent>() const members = new Map<UserID, StrippedStateEvent>()
for (const state of this.meta.invite_state) { for (const state of this.meta.invite_state) {
if (state.type === "m.room.name") { if (state.type === "m.room.name") {
@ -57,14 +61,14 @@ export class InvitedRoomStore implements RoomListEntry, RoomSummary {
this.topic = ensureString(state.content.topic) this.topic = ensureString(state.content.topic)
} else if (state.type === "m.room.avatar") { } else if (state.type === "m.room.avatar") {
this.avatar = ensureString(state.content.url) this.avatar = ensureString(state.content.url)
} else if (state.type === "m.room.encryption" && state.content.algorithm === "m.megolm.v1.aes-sha2") { } else if (state.type === "m.room.encryption") {
this.encryption = state.content.algorithm this.encryption = state.content.algorithm as "m.megolm.v1.aes-sha2"
} else if (state.type === "m.room.create") { } else if (state.type === "m.room.create") {
this.room_version = ensureString(state.content.version) as RoomVersion this.room_version = ensureString(state.content.version) as RoomVersion
} else if (state.type === "m.room.member") { } else if (state.type === "m.room.member") {
members.set(state.state_key, state) members.set(state.state_key, state)
} else if (state.type === "m.room.join_rules") { } else if (state.type === "m.room.join_rules") {
this.join_rules = ensureString(state.content.join_rule) as JoinRule this.join_rule = ensureString(state.content.join_rule) as JoinRule
} }
} }
this.search_name = toSearchableString(this.name ?? "") this.search_name = toSearchableString(this.name ?? "")
@ -73,14 +77,15 @@ export class InvitedRoomStore implements RoomListEntry, RoomSummary {
this.invited_by = ownMemberEvt.sender this.invited_by = ownMemberEvt.sender
this.inviter_profile = members.get(ownMemberEvt.sender)?.content as MemberEventContent this.inviter_profile = members.get(ownMemberEvt.sender)?.content as MemberEventContent
} }
this.is_direct = Boolean(ownMemberEvt?.content.is_direct)
if ( if (
!this.name !this.name
&& !this.avatar && !this.avatar
&& !this.topic && !this.topic
&& !this.canonical_alias && !this.canonical_alias
&& this.join_rules === "invite" && this.join_rule === "invite"
&& this.invited_by && this.invited_by
&& ownMemberEvt?.content.is_direct && this.is_direct
) { ) {
this.dm_user_id = this.invited_by this.dm_user_id = this.invited_by
this.name = getDisplayname(this.invited_by, this.inviter_profile) this.name = getDisplayname(this.invited_by, this.inviter_profile)

View file

@ -36,6 +36,14 @@ div.room-view.preview > div.preview-inner {
gap: .25rem; gap: .25rem;
} }
> details.room-invite-meta {
width: 100%;
> table > tr > td:nth-of-type(2) {
font-family: var(--monospace-font-stack);
}
}
> div.room-topic { > div.room-topic {
white-space: pre-wrap; white-space: pre-wrap;
max-height: 15rem; max-height: 15rem;
@ -56,6 +64,7 @@ div.room-view.preview > div.preview-inner {
width: 100%; width: 100%;
display: flex; display: flex;
gap: .25rem; gap: .25rem;
margin-top: 1rem;
> button { > button {
padding: .5rem; padding: .5rem;

View file

@ -93,7 +93,10 @@ const RoomPreview = ({ roomID, via, alias, invite }: RoomPreviewProps) => {
src={getAvatarURL(invite.invited_by, invite.inviter_profile)} src={getAvatarURL(invite.invited_by, invite.inviter_profile)}
alt="" alt=""
/> />
{getDisplayname(invite.invited_by, invite.inviter_profile)} invited you to <span className="inviter-name" title={invite.invited_by}>
{getDisplayname(invite.invited_by, invite.inviter_profile)}
</span>
invited you to
</div> : null} </div> : null}
<h2 className="room-name">{name}</h2> <h2 className="room-name">{name}</h2>
<img <img
@ -105,7 +108,40 @@ const RoomPreview = ({ roomID, via, alias, invite }: RoomPreviewProps) => {
{loading && <ScaleLoader color="var(--primary-color)"/>} {loading && <ScaleLoader color="var(--primary-color)"/>}
{memberCount && <div className="member-count"><GroupIcon/> {memberCount} members</div>} {memberCount && <div className="member-count"><GroupIcon/> {memberCount} members</div>}
<div className="room-topic">{topic}</div> <div className="room-topic">{topic}</div>
{invite?.invited_by && <MutualRooms client={client} userID={invite.invited_by} />} {invite && <details className="room-invite-meta">
<summary>Invite metadata</summary>
<table>
<tr>
<td>Invited by</td>
<td>{invite.invited_by}</td>
</tr>
<tr>
<td>Room ID</td>
<td>{roomID}</td>
</tr>
<tr>
<td>Room alias</td>
<td>{invite.canonical_alias ?? summary?.canonical_alias}</td>
</tr>
<tr>
<td>Is direct</td>
<td>{invite.is_direct.toString()}</td>
</tr>
<tr>
<td>Encryption</td>
<td>{invite.encryption ?? summary?.encryption ?? summary?.["im.nheko.summary.encryption"]}</td>
</tr>
<tr>
<td>Join rule</td>
<td>{invite.join_rule ?? summary?.join_rule}</td>
</tr>
<tr>
<td>Timestamp</td>
<td>{invite.date}</td>
</tr>
</table>
</details>}
{invite?.invited_by && <MutualRooms client={client} userID={invite.invited_by}/>}
<div className="buttons"> <div className="buttons">
{invite && <button {invite && <button
disabled={buttonClicked} disabled={buttonClicked}