diff --git a/web/src/api/media.ts b/web/src/api/media.ts
index 028ab6f..5d5ee67 100644
--- a/web/src/api/media.ts
+++ b/web/src/api/media.ts
@@ -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)
}
diff --git a/web/src/api/statestore/main.ts b/web/src/api/statestore/main.ts
index c430fa7..5e808a4 100644
--- a/web/src/api/statestore/main.ts
+++ b/web/src/api/statestore/main.ts
@@ -13,7 +13,7 @@
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see .
-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})`
diff --git a/web/src/ui/composer/Autocompleter.tsx b/web/src/ui/composer/Autocompleter.tsx
index 432d327..0d9eff5 100644
--- a/web/src/ui/composer/Autocompleter.tsx
+++ b/web/src/ui/composer/Autocompleter.tsx
@@ -14,7 +14,7 @@
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see .
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 = {
{user.displayName}
diff --git a/web/src/ui/composer/TypingNotifications.tsx b/web/src/ui/composer/TypingNotifications.tsx
index 10812ce..2f689f9 100644
--- a/web/src/ui/composer/TypingNotifications.tsx
+++ b/web/src/ui/composer/TypingNotifications.tsx
@@ -15,7 +15,7 @@
// along with this program. If not, see .
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))
diff --git a/web/src/ui/modal/Lightbox.tsx b/web/src/ui/modal/Lightbox.tsx
index a8e395a..abf4dd2 100644
--- a/web/src/ui/modal/Lightbox.tsx
+++ b/web/src/ui/modal/Lightbox.tsx
@@ -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)
diff --git a/web/src/ui/rightpanel/MemberList.tsx b/web/src/ui/rightpanel/MemberList.tsx
index a88853e..8e41e27 100644
--- a/web/src/ui/rightpanel/MemberList.tsx
+++ b/web/src/ui/rightpanel/MemberList.tsx
@@ -14,7 +14,7 @@
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see .
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

diff --git a/web/src/ui/rightpanel/UserInfo.tsx b/web/src/ui/rightpanel/UserInfo.tsx
index 87b5ea0..dd10c81 100644
--- a/web/src/ui/rightpanel/UserInfo.tsx
+++ b/web/src/ui/rightpanel/UserInfo.tsx
@@ -61,6 +61,7 @@ const UserInfo = ({ userID }: UserInfoProps) => {
className="avatar-loader"
/> :

.
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) {
diff --git a/web/src/ui/roomlist/Space.tsx b/web/src/ui/roomlist/Space.tsx
index daeb35c..6cdea09 100644
--- a/web/src/ui/roomlist/Space.tsx
+++ b/web/src/ui/roomlist/Space.tsx
@@ -15,7 +15,7 @@
// along with this program. If not, see .
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
-
})
+
}
diff --git a/web/src/ui/roomview/RoomPreview.tsx b/web/src/ui/roomview/RoomPreview.tsx
index dab8003..fd6700d 100644
--- a/web/src/ui/roomview/RoomPreview.tsx
+++ b/web/src/ui/roomview/RoomPreview.tsx
@@ -15,7 +15,7 @@
// along with this program. If not, see .
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) => {
@@ -100,6 +101,7 @@ const RoomPreview = ({ roomID, via, alias, invite }: RoomPreviewProps) => {
: null}
{name}
.
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) => {
diff --git a/web/src/ui/settings/SettingsView.tsx b/web/src/ui/settings/SettingsView.tsx
index 67ec3c7..fb8cc44 100644
--- a/web/src/ui/settings/SettingsView.tsx
+++ b/web/src/ui/settings/SettingsView.tsx
@@ -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) => {
diff --git a/web/src/ui/timeline/ReadReceipts.tsx b/web/src/ui/timeline/ReadReceipts.tsx
index c4d3436..b342211 100644
--- a/web/src/ui/timeline/ReadReceipts.tsx
+++ b/web/src/ui/timeline/ReadReceipts.tsx
@@ -14,7 +14,7 @@
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see .
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=""
/>
})
diff --git a/web/src/ui/timeline/ReplyBody.tsx b/web/src/ui/timeline/ReplyBody.tsx
index 65c20c8..93f7c63 100644
--- a/web/src/ui/timeline/ReplyBody.tsx
+++ b/web/src/ui/timeline/ReplyBody.tsx
@@ -14,7 +14,7 @@
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see .
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 = ({
diff --git a/web/src/ui/timeline/TimelineEvent.tsx b/web/src/ui/timeline/TimelineEvent.tsx
index f4a7ae8..0c6e711 100644
--- a/web/src/ui/timeline/TimelineEvent.tsx
+++ b/web/src/ui/timeline/TimelineEvent.tsx
@@ -15,7 +15,7 @@
// along with this program. If not, see .
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
}
diff --git a/web/src/ui/timeline/content/MemberBody.tsx b/web/src/ui/timeline/content/MemberBody.tsx
index e86ce34..d3011e2 100644
--- a/web/src/ui/timeline/content/MemberBody.tsx
+++ b/web/src/ui/timeline/content/MemberBody.tsx
@@ -14,7 +14,7 @@
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see .
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 =
@@ -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}
diff --git a/web/src/ui/timeline/content/RoomAvatarBody.tsx b/web/src/ui/timeline/content/RoomAvatarBody.tsx
index 7878bbb..3b1e225 100644
--- a/web/src/ui/timeline/content/RoomAvatarBody.tsx
+++ b/web/src/ui/timeline/content/RoomAvatarBody.tsx
@@ -14,7 +14,7 @@
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see .
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=""
/>