mirror of
https://github.com/tulir/gomuks.git
synced 2025-04-19 18:13:41 -05:00
web/roomlist: add option to hide invite avatars
This commit is contained in:
parent
86843d61f6
commit
ef05bc71f9
7 changed files with 46 additions and 15 deletions
|
@ -78,11 +78,16 @@ function getFallbackCharacter(from: unknown, idx: number): string {
|
||||||
return Array.from(from.slice(0, (idx + 1) * 2))[idx]?.toUpperCase().toWellFormed() ?? ""
|
return Array.from(from.slice(0, (idx + 1) * 2))[idx]?.toUpperCase().toWellFormed() ?? ""
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getAvatarURL = (userID: UserID, content?: UserProfile | null, thumbnail = false): string | undefined => {
|
export const getAvatarURL = (
|
||||||
|
userID: UserID,
|
||||||
|
content?: UserProfile | null,
|
||||||
|
thumbnail = false,
|
||||||
|
forceFallback = false,
|
||||||
|
): string | undefined => {
|
||||||
const fallbackCharacter = getFallbackCharacter(content?.displayname, 0) || getFallbackCharacter(userID, 1)
|
const fallbackCharacter = getFallbackCharacter(content?.displayname, 0) || getFallbackCharacter(userID, 1)
|
||||||
const backgroundColor = getUserColor(userID)
|
const backgroundColor = getUserColor(userID)
|
||||||
const [server, mediaID] = parseMXC(content?.avatar_file?.url ?? content?.avatar_url)
|
const [server, mediaID] = parseMXC(content?.avatar_file?.url ?? content?.avatar_url)
|
||||||
if (!mediaID) {
|
if (!mediaID || forceFallback) {
|
||||||
return makeFallbackAvatar(backgroundColor, fallbackCharacter)
|
return makeFallbackAvatar(backgroundColor, fallbackCharacter)
|
||||||
}
|
}
|
||||||
const encrypted = !!content?.avatar_file
|
const encrypted = !!content?.avatar_file
|
||||||
|
@ -91,8 +96,12 @@ export const getAvatarURL = (userID: UserID, content?: UserProfile | null, thumb
|
||||||
return thumbnail ? `${url}&thumbnail=avatar` : url
|
return thumbnail ? `${url}&thumbnail=avatar` : url
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getAvatarThumbnailURL = (userID: UserID, content?: UserProfile | null): string | undefined => {
|
export const getAvatarThumbnailURL = (
|
||||||
return getAvatarURL(userID, content, true)
|
userID: UserID,
|
||||||
|
content?: UserProfile | null,
|
||||||
|
forceFallback = false,
|
||||||
|
): string | undefined => {
|
||||||
|
return getAvatarURL(userID, content, true, forceFallback)
|
||||||
}
|
}
|
||||||
|
|
||||||
interface RoomForAvatarURL {
|
interface RoomForAvatarURL {
|
||||||
|
@ -104,14 +113,21 @@ interface RoomForAvatarURL {
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getRoomAvatarURL = (
|
export const getRoomAvatarURL = (
|
||||||
room: RoomForAvatarURL, avatarOverride?: ContentURI, thumbnail = false,
|
room: RoomForAvatarURL,
|
||||||
|
avatarOverride?: ContentURI,
|
||||||
|
thumbnail = false,
|
||||||
|
forceFallback = false,
|
||||||
): string | undefined => {
|
): string | undefined => {
|
||||||
return getAvatarURL(room.dm_user_id ?? room.room_id, {
|
return getAvatarURL(room.dm_user_id ?? room.room_id, {
|
||||||
displayname: room.name,
|
displayname: room.name,
|
||||||
avatar_url: avatarOverride ?? room.avatar ?? room.avatar_url,
|
avatar_url: avatarOverride ?? room.avatar ?? room.avatar_url,
|
||||||
}, thumbnail)
|
}, thumbnail, forceFallback)
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getRoomAvatarThumbnailURL = (room: RoomForAvatarURL, avatarOverride?: ContentURI): string | undefined => {
|
export const getRoomAvatarThumbnailURL = (
|
||||||
return getRoomAvatarURL(room, avatarOverride, true)
|
room: RoomForAvatarURL,
|
||||||
|
avatarOverride?: ContentURI,
|
||||||
|
forceFallback = false,
|
||||||
|
): string | undefined => {
|
||||||
|
return getRoomAvatarURL(room, avatarOverride, true, forceFallback)
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,6 +45,7 @@ export class InvitedRoomStore implements RoomListEntry, RoomSummary {
|
||||||
readonly invited_by?: UserID
|
readonly invited_by?: UserID
|
||||||
readonly inviter_profile?: MemberEventContent
|
readonly inviter_profile?: MemberEventContent
|
||||||
readonly is_direct: boolean
|
readonly is_direct: boolean
|
||||||
|
readonly is_invite = true
|
||||||
|
|
||||||
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
|
||||||
|
|
|
@ -55,6 +55,7 @@ export interface RoomListEntry {
|
||||||
unread_notifications: number
|
unread_notifications: number
|
||||||
unread_highlights: number
|
unread_highlights: number
|
||||||
marked_unread: boolean
|
marked_unread: boolean
|
||||||
|
is_invite?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface GCSettings {
|
export interface GCSettings {
|
||||||
|
|
|
@ -65,6 +65,12 @@ export const preferences = {
|
||||||
allowedContexts: anyContext,
|
allowedContexts: anyContext,
|
||||||
defaultValue: true,
|
defaultValue: true,
|
||||||
}),
|
}),
|
||||||
|
show_invite_avatars: new Preference<boolean>({
|
||||||
|
displayName: "Show avatars in invites",
|
||||||
|
description: "If disabled, the avatar of the room or invitee will not be shown in the invite view.",
|
||||||
|
allowedContexts: anyGlobalContext,
|
||||||
|
defaultValue: true,
|
||||||
|
}),
|
||||||
code_block_line_wrap: new Preference<boolean>({
|
code_block_line_wrap: new Preference<boolean>({
|
||||||
displayName: "Code block line wrap",
|
displayName: "Code block line wrap",
|
||||||
description: "Whether to wrap long lines in code blocks instead of scrolling horizontally.",
|
description: "Whether to wrap long lines in code blocks instead of scrolling horizontally.",
|
||||||
|
|
|
@ -29,6 +29,7 @@ export interface RoomListEntryProps {
|
||||||
room: RoomListEntry
|
room: RoomListEntry
|
||||||
isActive: boolean
|
isActive: boolean
|
||||||
hidden: boolean
|
hidden: boolean
|
||||||
|
hideAvatar?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
function getPreviewText(evt?: MemDBEvent, senderMemberEvt?: MemDBEvent | null): [string, JSX.Element | null] {
|
function getPreviewText(evt?: MemDBEvent, senderMemberEvt?: MemDBEvent | null): [string, JSX.Element | null] {
|
||||||
|
@ -57,7 +58,7 @@ function getPreviewText(evt?: MemDBEvent, senderMemberEvt?: MemDBEvent | null):
|
||||||
return ["", null]
|
return ["", null]
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderEntry(room: RoomListEntry) {
|
function renderEntry(room: RoomListEntry, hideAvatar: boolean | undefined) {
|
||||||
const [previewText, croppedPreviewText] = getPreviewText(room.preview_event, room.preview_sender)
|
const [previewText, croppedPreviewText] = getPreviewText(room.preview_event, room.preview_sender)
|
||||||
|
|
||||||
return <>
|
return <>
|
||||||
|
@ -65,7 +66,7 @@ function renderEntry(room: RoomListEntry) {
|
||||||
<img
|
<img
|
||||||
loading="lazy"
|
loading="lazy"
|
||||||
className="avatar room-avatar"
|
className="avatar room-avatar"
|
||||||
src={getRoomAvatarThumbnailURL(room)}
|
src={getRoomAvatarThumbnailURL(room, undefined, hideAvatar)}
|
||||||
alt=""
|
alt=""
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
@ -77,7 +78,7 @@ function renderEntry(room: RoomListEntry) {
|
||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
|
|
||||||
const Entry = ({ room, isActive, hidden }: RoomListEntryProps) => {
|
const Entry = ({ room, isActive, hidden, hideAvatar }: RoomListEntryProps) => {
|
||||||
const [isVisible, divRef] = useContentVisibility<HTMLDivElement>()
|
const [isVisible, divRef] = useContentVisibility<HTMLDivElement>()
|
||||||
const openModal = use(ModalContext)
|
const openModal = use(ModalContext)
|
||||||
const mainScreen = use(MainScreenContext)
|
const mainScreen = use(MainScreenContext)
|
||||||
|
@ -105,7 +106,7 @@ const Entry = ({ room, isActive, hidden }: RoomListEntryProps) => {
|
||||||
onContextMenu={onContextMenu}
|
onContextMenu={onContextMenu}
|
||||||
data-room-id={room.room_id}
|
data-room-id={room.room_id}
|
||||||
>
|
>
|
||||||
{isVisible ? renderEntry(room) : null}
|
{isVisible ? renderEntry(room, hideAvatar) : null}
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
// You should have received a copy of the GNU Affero General Public License
|
// 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/>.
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
import React, { use, useCallback, useRef, useState } from "react"
|
import React, { use, useCallback, useRef, useState } from "react"
|
||||||
import { RoomListFilter, Space as SpaceStore, SpaceUnreadCounts } from "@/api/statestore"
|
import { RoomListFilter, Space as SpaceStore, SpaceUnreadCounts, usePreference } from "@/api/statestore"
|
||||||
import type { RoomID } from "@/api/types"
|
import type { RoomID } from "@/api/types"
|
||||||
import { useEventAsState } from "@/util/eventdispatcher.ts"
|
import { useEventAsState } from "@/util/eventdispatcher.ts"
|
||||||
import reverseMap from "@/util/reversemap.ts"
|
import reverseMap from "@/util/reversemap.ts"
|
||||||
|
@ -103,6 +103,7 @@ const RoomList = ({ activeRoomID, space }: RoomListProps) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const showInviteAvatars = usePreference(client.store, null, "show_invite_avatars")
|
||||||
const roomListFilter = client.store.roomListFilterFunc
|
const roomListFilter = client.store.roomListFilterFunc
|
||||||
return <div className="room-list-wrapper">
|
return <div className="room-list-wrapper">
|
||||||
<div className="room-search-wrapper">
|
<div className="room-search-wrapper">
|
||||||
|
@ -145,6 +146,7 @@ const RoomList = ({ activeRoomID, space }: RoomListProps) => {
|
||||||
isActive={room.room_id === activeRoomID}
|
isActive={room.room_id === activeRoomID}
|
||||||
hidden={roomListFilter ? !roomListFilter(room) : false}
|
hidden={roomListFilter ? !roomListFilter(room) : false}
|
||||||
room={room}
|
room={room}
|
||||||
|
hideAvatar={room.is_invite && !showInviteAvatars}
|
||||||
/>,
|
/>,
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
import { use, useEffect, useState } from "react"
|
import { use, useEffect, useState } from "react"
|
||||||
import { ScaleLoader } from "react-spinners"
|
import { ScaleLoader } from "react-spinners"
|
||||||
import { getAvatarThumbnailURL, getAvatarURL, getRoomAvatarURL } from "@/api/media.ts"
|
import { getAvatarThumbnailURL, getAvatarURL, getRoomAvatarURL } from "@/api/media.ts"
|
||||||
|
import { usePreference } from "@/api/statestore/hooks.ts"
|
||||||
import { InvitedRoomStore } from "@/api/statestore/invitedroom.ts"
|
import { InvitedRoomStore } from "@/api/statestore/invitedroom.ts"
|
||||||
import { RoomID, RoomSummary } from "@/api/types"
|
import { RoomID, RoomSummary } from "@/api/types"
|
||||||
import { getDisplayname, getServerName } from "@/util/validation.ts"
|
import { getDisplayname, getServerName } from "@/util/validation.ts"
|
||||||
|
@ -84,13 +85,15 @@ const RoomPreview = ({ roomID, via, alias, invite }: RoomPreviewProps) => {
|
||||||
const name = summary?.name ?? summary?.canonical_alias ?? invite?.name ?? invite?.canonical_alias ?? alias ?? roomID
|
const name = summary?.name ?? summary?.canonical_alias ?? invite?.name ?? invite?.canonical_alias ?? alias ?? roomID
|
||||||
const memberCount = summary?.num_joined_members || null
|
const memberCount = summary?.num_joined_members || null
|
||||||
const topic = summary?.topic ?? invite?.topic ?? ""
|
const topic = summary?.topic ?? invite?.topic ?? ""
|
||||||
|
const showInviteAvatars = usePreference(client.store, null, "show_invite_avatars")
|
||||||
|
const noAvatarPreview = invite && !showInviteAvatars
|
||||||
return <div className="room-view preview">
|
return <div className="room-view preview">
|
||||||
<div className="preview-inner">
|
<div className="preview-inner">
|
||||||
{invite?.invited_by && !invite.dm_user_id ? <div className="inviter-info">
|
{invite?.invited_by && !invite.dm_user_id ? <div className="inviter-info">
|
||||||
<img
|
<img
|
||||||
className="small avatar"
|
className="small avatar"
|
||||||
onClick={use(LightboxContext)}
|
onClick={use(LightboxContext)}
|
||||||
src={getAvatarThumbnailURL(invite.invited_by, invite.inviter_profile)}
|
src={getAvatarThumbnailURL(invite.invited_by, invite.inviter_profile, noAvatarPreview)}
|
||||||
data-full-src={getAvatarURL(invite.invited_by, invite.inviter_profile)}
|
data-full-src={getAvatarURL(invite.invited_by, invite.inviter_profile)}
|
||||||
alt=""
|
alt=""
|
||||||
/>
|
/>
|
||||||
|
@ -102,7 +105,8 @@ const RoomPreview = ({ roomID, via, alias, invite }: RoomPreviewProps) => {
|
||||||
<h2 className="room-name">{name}</h2>
|
<h2 className="room-name">{name}</h2>
|
||||||
<img
|
<img
|
||||||
// this is a big avatar (120px), use full resolution
|
// this is a big avatar (120px), use full resolution
|
||||||
src={getRoomAvatarURL(invite ?? summary ?? { room_id: roomID })}
|
src={getRoomAvatarURL(invite ?? summary ?? { room_id: roomID }, undefined, false, noAvatarPreview)}
|
||||||
|
data-full-src={getRoomAvatarURL(invite ?? summary ?? { room_id: roomID })}
|
||||||
className="large avatar"
|
className="large avatar"
|
||||||
onClick={use(LightboxContext)}
|
onClick={use(LightboxContext)}
|
||||||
alt=""
|
alt=""
|
||||||
|
|
Loading…
Add table
Reference in a new issue