mirror of
https://github.com/tulir/gomuks.git
synced 2025-04-19 18:13:41 -05:00
web: use thumbnails for small avatars
This commit is contained in:
parent
1b5467cf0e
commit
6d12e6e009
17 changed files with 53 additions and 34 deletions
|
@ -78,7 +78,7 @@ function getFallbackCharacter(from: unknown, idx: number): string {
|
|||
return Array.from(from.slice(0, (idx + 1) * 2))[idx]?.toUpperCase().toWellFormed() ?? ""
|
||||
}
|
||||
|
||||
export const getAvatarURL = (userID: UserID, content?: UserProfile | null): string | undefined => {
|
||||
export const getAvatarURL = (userID: UserID, content?: UserProfile | null, thumbnail = false): string | undefined => {
|
||||
const fallbackCharacter = getFallbackCharacter(content?.displayname, 0) || getFallbackCharacter(userID, 1)
|
||||
const backgroundColor = getUserColor(userID)
|
||||
const [server, mediaID] = parseMXC(content?.avatar_file?.url ?? content?.avatar_url)
|
||||
|
@ -87,7 +87,12 @@ export const getAvatarURL = (userID: UserID, content?: UserProfile | null): stri
|
|||
}
|
||||
const encrypted = !!content?.avatar_file
|
||||
const fallback = `${backgroundColor}:${fallbackCharacter}`
|
||||
return `_gomuks/media/${server}/${mediaID}?encrypted=${encrypted}&fallback=${encodeURIComponent(fallback)}`
|
||||
const url = `_gomuks/media/${server}/${mediaID}?encrypted=${encrypted}&fallback=${encodeURIComponent(fallback)}`
|
||||
return thumbnail ? `${url}&thumbnail=avatar` : url
|
||||
}
|
||||
|
||||
export const getAvatarThumbnailURL = (userID: UserID, content?: UserProfile | null): string | undefined => {
|
||||
return getAvatarURL(userID, content, true)
|
||||
}
|
||||
|
||||
interface RoomForAvatarURL {
|
||||
|
@ -98,9 +103,15 @@ interface RoomForAvatarURL {
|
|||
avatar_url?: ContentURI
|
||||
}
|
||||
|
||||
export const getRoomAvatarURL = (room: RoomForAvatarURL, avatarOverride?: ContentURI): string | undefined => {
|
||||
export const getRoomAvatarURL = (
|
||||
room: RoomForAvatarURL, avatarOverride?: ContentURI, thumbnail = false,
|
||||
): string | undefined => {
|
||||
return getAvatarURL(room.dm_user_id ?? room.room_id, {
|
||||
displayname: room.name,
|
||||
avatar_url: avatarOverride ?? room.avatar ?? room.avatar_url,
|
||||
})
|
||||
}, thumbnail)
|
||||
}
|
||||
|
||||
export const getRoomAvatarThumbnailURL = (room: RoomForAvatarURL, avatarOverride?: ContentURI): string | undefined => {
|
||||
return getRoomAvatarURL(room, avatarOverride, true)
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
//
|
||||
// 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 { getAvatarURL } from "@/api/media.ts"
|
||||
import { getAvatarThumbnailURL } from "@/api/media.ts"
|
||||
import { Preferences, getLocalStoragePreferences, getPreferenceProxy } from "@/api/types/preferences"
|
||||
import { CustomEmojiPack, parseCustomEmojiPack } from "@/util/emoji"
|
||||
import { NonNullCachedEventDispatcher } from "@/util/eventdispatcher.ts"
|
||||
|
@ -469,7 +469,7 @@ export class StateStore {
|
|||
body = body.slice(0, 350) + " […]"
|
||||
}
|
||||
const memberEvt = room.getStateEvent("m.room.member", evt.sender)
|
||||
const icon = `${getAvatarURL(evt.sender, memberEvt?.content)}&image_auth=${this.imageAuthToken}`
|
||||
const icon = `${getAvatarThumbnailURL(evt.sender, memberEvt?.content)}&image_auth=${this.imageAuthToken}`
|
||||
const roomName = room.meta.current.name ?? "Unnamed room"
|
||||
const senderName = memberEvt?.content.displayname ?? evt.sender
|
||||
const title = senderName === roomName ? senderName : `${senderName} (${roomName})`
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
// 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, RefObject, use, useEffect } from "react"
|
||||
import { getAvatarURL, getMediaURL } from "@/api/media.ts"
|
||||
import { getAvatarThumbnailURL, getMediaURL } from "@/api/media.ts"
|
||||
import { AutocompleteMemberEntry, RoomStateStore, useCustomEmojis } from "@/api/statestore"
|
||||
import { Emoji, emojiToMarkdown, useSortedAndFilteredEmojis } from "@/util/emoji"
|
||||
import { escapeMarkdown } from "@/util/markdown.ts"
|
||||
|
@ -138,7 +138,7 @@ const userFuncs = {
|
|||
<img
|
||||
className="small avatar"
|
||||
loading="lazy"
|
||||
src={getAvatarURL(user.userID, { displayname: user.displayName, avatar_url: user.avatarURL })}
|
||||
src={getAvatarThumbnailURL(user.userID, { displayname: user.displayName, avatar_url: user.avatarURL })}
|
||||
alt=""
|
||||
/>
|
||||
{user.displayName}
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
import { JSX, use } from "react"
|
||||
import { PulseLoader } from "react-spinners"
|
||||
import { getAvatarURL } from "@/api/media.ts"
|
||||
import { getAvatarThumbnailURL } from "@/api/media.ts"
|
||||
import { useMultipleRoomMembers, useRoomTyping } from "@/api/statestore"
|
||||
import { humanJoin } from "@/util/join.ts"
|
||||
import { getDisplayname } from "@/util/validation.ts"
|
||||
|
@ -40,7 +40,7 @@ const TypingNotifications = () => {
|
|||
key={sender}
|
||||
className="small avatar"
|
||||
loading="lazy"
|
||||
src={getAvatarURL(sender, member)}
|
||||
src={getAvatarThumbnailURL(sender, member)}
|
||||
alt=""
|
||||
/>)
|
||||
memberNames.push(getDisplayname(sender, member))
|
||||
|
|
|
@ -36,7 +36,7 @@ const LightboxWrapper = ({ children }: { children: React.ReactNode }) => {
|
|||
return
|
||||
}
|
||||
params = {
|
||||
src: target.src,
|
||||
src: target.getAttribute("data-full-src") ?? target.src,
|
||||
alt: target.alt,
|
||||
}
|
||||
setParams(params)
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
// 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 React, { use, useState } from "react"
|
||||
import { getAvatarURL } from "@/api/media.ts"
|
||||
import { getAvatarThumbnailURL } from "@/api/media.ts"
|
||||
import { MemDBEvent, MemberEventContent } from "@/api/types"
|
||||
import { getDisplayname } from "@/util/validation.ts"
|
||||
import ClientContext from "../ClientContext.ts"
|
||||
|
@ -33,7 +33,7 @@ const MemberRow = ({ evt, onClick }: MemberRowProps) => {
|
|||
return <div className="member" data-target-panel="user" data-target-user={userID} onClick={onClick}>
|
||||
<img
|
||||
className="avatar"
|
||||
src={getAvatarURL(userID, content)}
|
||||
src={getAvatarThumbnailURL(userID, content)}
|
||||
alt=""
|
||||
loading="lazy"
|
||||
/>
|
||||
|
|
|
@ -61,6 +61,7 @@ const UserInfo = ({ userID }: UserInfoProps) => {
|
|||
className="avatar-loader"
|
||||
/> : <img
|
||||
className="avatar"
|
||||
// this is a big avatar (236px by default), use full resolution
|
||||
src={getAvatarURL(userID, member ?? globalProfile)}
|
||||
onClick={openLightbox}
|
||||
alt=""
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
// 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, memo, use } from "react"
|
||||
import { getRoomAvatarURL } from "@/api/media.ts"
|
||||
import { getRoomAvatarThumbnailURL } from "@/api/media.ts"
|
||||
import type { RoomListEntry } from "@/api/statestore"
|
||||
import type { MemDBEvent, MemberEventContent } from "@/api/types"
|
||||
import useContentVisibility from "@/util/contentvisibility.ts"
|
||||
|
@ -63,7 +63,7 @@ function renderEntry(room: RoomListEntry) {
|
|||
<img
|
||||
loading="lazy"
|
||||
className="avatar room-avatar"
|
||||
src={getRoomAvatarURL(room)}
|
||||
src={getRoomAvatarThumbnailURL(room)}
|
||||
alt=""
|
||||
/>
|
||||
</div>
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
import React from "react"
|
||||
import Client from "@/api/client.ts"
|
||||
import { getRoomAvatarURL } from "@/api/media.ts"
|
||||
import { getRoomAvatarThumbnailURL } from "@/api/media.ts"
|
||||
import type { RoomID } from "@/api/types"
|
||||
import { useEventAsState } from "@/util/eventdispatcher.ts"
|
||||
import UnreadCount from "./UnreadCount.tsx"
|
||||
|
@ -37,7 +37,7 @@ const Space = ({ roomID, client, onClick, isActive, onClickUnread }: SpaceProps)
|
|||
}
|
||||
return <div className={`space-entry ${isActive ? "active" : ""}`} onClick={onClick} data-target-space={roomID}>
|
||||
<UnreadCount counts={unreads} space={true} onClick={onClickUnread} />
|
||||
<img src={getRoomAvatarURL(room)} alt={room.name} title={room.name} className="avatar" />
|
||||
<img src={getRoomAvatarThumbnailURL(room)} alt={room.name} title={room.name} className="avatar" />
|
||||
</div>
|
||||
}
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
import { use, useEffect, useState } from "react"
|
||||
import { ScaleLoader } from "react-spinners"
|
||||
import { getAvatarURL, getRoomAvatarURL } from "@/api/media.ts"
|
||||
import { getAvatarThumbnailURL, getAvatarURL, getRoomAvatarURL } from "@/api/media.ts"
|
||||
import { InvitedRoomStore } from "@/api/statestore/invitedroom.ts"
|
||||
import { RoomID, RoomSummary } from "@/api/types"
|
||||
import { getDisplayname, getServerName } from "@/util/validation.ts"
|
||||
|
@ -90,7 +90,8 @@ const RoomPreview = ({ roomID, via, alias, invite }: RoomPreviewProps) => {
|
|||
<img
|
||||
className="small avatar"
|
||||
onClick={use(LightboxContext)}
|
||||
src={getAvatarURL(invite.invited_by, invite.inviter_profile)}
|
||||
src={getAvatarThumbnailURL(invite.invited_by, invite.inviter_profile)}
|
||||
data-full-src={getAvatarURL(invite.invited_by, invite.inviter_profile)}
|
||||
alt=""
|
||||
/>
|
||||
<span className="inviter-name" title={invite.invited_by}>
|
||||
|
@ -100,6 +101,7 @@ const RoomPreview = ({ roomID, via, alias, invite }: RoomPreviewProps) => {
|
|||
</div> : null}
|
||||
<h2 className="room-name">{name}</h2>
|
||||
<img
|
||||
// this is a big avatar (120px), use full resolution
|
||||
src={getRoomAvatarURL(invite ?? summary ?? { room_id: roomID })}
|
||||
className="large avatar"
|
||||
onClick={use(LightboxContext)}
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
// 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 { use } from "react"
|
||||
import { getRoomAvatarURL } from "@/api/media.ts"
|
||||
import { getRoomAvatarThumbnailURL, getRoomAvatarURL } from "@/api/media.ts"
|
||||
import { RoomStateStore } from "@/api/statestore"
|
||||
import { useEventAsState } from "@/util/eventdispatcher.ts"
|
||||
import MainScreenContext from "../MainScreenContext.ts"
|
||||
|
@ -48,7 +48,8 @@ const RoomViewHeader = ({ room }: RoomViewHeaderProps) => {
|
|||
<img
|
||||
className="avatar"
|
||||
loading="lazy"
|
||||
src={getRoomAvatarURL(roomMeta)}
|
||||
src={getRoomAvatarThumbnailURL(roomMeta)}
|
||||
data-full-src={getRoomAvatarURL(roomMeta)}
|
||||
onClick={use(LightboxContext)}
|
||||
alt=""
|
||||
/>
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
import { Suspense, lazy, use, useCallback, useRef, useState } from "react"
|
||||
import { ScaleLoader } from "react-spinners"
|
||||
import Client from "@/api/client.ts"
|
||||
import { getRoomAvatarURL } from "@/api/media.ts"
|
||||
import { getRoomAvatarThumbnailURL, getRoomAvatarURL } from "@/api/media.ts"
|
||||
import { RoomStateStore, usePreferences } from "@/api/statestore"
|
||||
import {
|
||||
Preference,
|
||||
|
@ -355,7 +355,8 @@ const SettingsView = ({ room }: SettingsViewProps) => {
|
|||
<img
|
||||
className="avatar large"
|
||||
loading="lazy"
|
||||
src={getRoomAvatarURL(roomMeta)}
|
||||
src={getRoomAvatarThumbnailURL(roomMeta)}
|
||||
data-full-src={getRoomAvatarURL(roomMeta)}
|
||||
onClick={use(LightboxContext)}
|
||||
alt=""
|
||||
/>
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
// 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 { use } from "react"
|
||||
import { getAvatarURL } from "@/api/media.ts"
|
||||
import { getAvatarThumbnailURL } from "@/api/media.ts"
|
||||
import { RoomStateStore, useMultipleRoomMembers, useReadReceipts } from "@/api/statestore"
|
||||
import { EventID } from "@/api/types"
|
||||
import { humanJoin } from "@/util/join.ts"
|
||||
|
@ -37,7 +37,7 @@ const ReadReceipts = ({ room, eventID }: { room: RoomStateStore, eventID: EventI
|
|||
key={userID}
|
||||
className="small avatar"
|
||||
loading="lazy"
|
||||
src={getAvatarURL(userID, member)}
|
||||
src={getAvatarThumbnailURL(userID, member)}
|
||||
alt=""
|
||||
/>
|
||||
})
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
// 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 { use } from "react"
|
||||
import { getAvatarURL, getUserColorIndex } from "@/api/media.ts"
|
||||
import { getAvatarThumbnailURL, getUserColorIndex } from "@/api/media.ts"
|
||||
import { RoomStateStore, useRoomEvent, useRoomMember } from "@/api/statestore"
|
||||
import type { EventID, MemDBEvent, MemberEventContent } from "@/api/types"
|
||||
import { getDisplayname } from "@/util/validation.ts"
|
||||
|
@ -124,7 +124,7 @@ export const ReplyBody = ({
|
|||
<img
|
||||
className="small avatar"
|
||||
loading="lazy"
|
||||
src={getAvatarURL(perMessageSender?.id ?? event.sender, renderMemberEvtContent)}
|
||||
src={getAvatarThumbnailURL(perMessageSender?.id ?? event.sender, renderMemberEvtContent)}
|
||||
alt=""
|
||||
/>
|
||||
</div>
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
import React, { JSX, use, useState } from "react"
|
||||
import { createPortal } from "react-dom"
|
||||
import { getAvatarURL, getMediaURL, getUserColorIndex } from "@/api/media.ts"
|
||||
import { getAvatarThumbnailURL, getMediaURL, getUserColorIndex } from "@/api/media.ts"
|
||||
import { useRoomMember } from "@/api/statestore"
|
||||
import { MemDBEvent, MemberEventContent, UnreadType } from "@/api/types"
|
||||
import { isMobileDevice } from "@/util/ismobile.ts"
|
||||
|
@ -233,7 +233,7 @@ const TimelineEvent = ({ evt, prevEvt, disableMenu, smallReplies, isFocused }: T
|
|||
<img
|
||||
className={`${smallAvatar ? "small" : ""} avatar`}
|
||||
loading="lazy"
|
||||
src={getAvatarURL(perMessageSender?.id ?? evt.sender, renderMemberEvtContent)}
|
||||
src={getAvatarThumbnailURL(perMessageSender?.id ?? evt.sender, renderMemberEvtContent)}
|
||||
alt=""
|
||||
/>
|
||||
</div>}
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
// 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 React, { use } from "react"
|
||||
import { getAvatarURL } from "@/api/media.ts"
|
||||
import { getAvatarThumbnailURL, getAvatarURL } from "@/api/media.ts"
|
||||
import { MemberEventContent, UserID } from "@/api/types"
|
||||
import { LightboxContext } from "../../modal"
|
||||
import EventContentProps from "./props.ts"
|
||||
|
@ -25,7 +25,8 @@ function useChangeDescription(
|
|||
const targetAvatar = <img
|
||||
className="small avatar"
|
||||
loading="lazy"
|
||||
src={getAvatarURL(target, content)}
|
||||
src={getAvatarThumbnailURL(target, content)}
|
||||
data-full-src={getAvatarURL(target, content)}
|
||||
onClick={use(LightboxContext)!}
|
||||
alt=""
|
||||
/>
|
||||
|
@ -59,7 +60,8 @@ function useChangeDescription(
|
|||
className="small avatar"
|
||||
loading="lazy"
|
||||
height={16}
|
||||
src={getAvatarURL(target, prevContent)}
|
||||
src={getAvatarThumbnailURL(target, prevContent)}
|
||||
data-full-src={getAvatarURL(target, prevContent)}
|
||||
onClick={use(LightboxContext)!}
|
||||
alt=""
|
||||
/> to {targetAvatar}
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
// 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 { getRoomAvatarThumbnailURL, getRoomAvatarURL } from "@/api/media.ts"
|
||||
import { ContentURI, RoomAvatarEventContent } from "@/api/types"
|
||||
import { ensureString } from "@/util/validation.ts"
|
||||
import { LightboxContext } from "../../modal"
|
||||
|
@ -31,7 +31,8 @@ const RoomAvatarBody = ({ event, sender, room }: EventContentProps) => {
|
|||
className="small avatar"
|
||||
loading="lazy"
|
||||
height={16}
|
||||
src={getRoomAvatarURL(room.meta.current, url)}
|
||||
src={getRoomAvatarThumbnailURL(room.meta.current, url)}
|
||||
data-full-src={getRoomAvatarURL(room.meta.current, url)}
|
||||
onClick={openLightbox}
|
||||
alt=""
|
||||
/>
|
||||
|
|
Loading…
Add table
Reference in a new issue