diff --git a/src/Avatar.test.tsx b/src/Avatar.test.tsx index 9df6384c..0de68ce2 100644 --- a/src/Avatar.test.tsx +++ b/src/Avatar.test.tsx @@ -14,10 +14,9 @@ import { ClientContextProvider } from "./ClientContext"; import { Avatar } from "./Avatar"; import { mockMatrixRoomMember } from "./utils/test"; -const TestComponent: FC> = ({ - client, - children, -}) => { +const TestComponent: FC< + PropsWithChildren<{ client: MatrixClient; supportsThumbnails?: boolean }> +> = ({ client, children, supportsThumbnails }) => { return ( > = ({ disconnected: false, supportedFeatures: { reactions: true, + thumbnails: supportsThumbnails ?? true, }, setClient: vi.fn(), authenticated: { @@ -70,6 +70,34 @@ test("should just render a placeholder when the user has no avatar", () => { expect(element.tagName).toEqual("SPAN"); expect(client.mxcUrlToHttp).toBeCalledTimes(0); }); + +test("should just render a placeholder when thumbnaisl are not supported", () => { + const client = vi.mocked({ + getAccessToken: () => "my-access-token", + mxcUrlToHttp: () => vi.fn(), + } as unknown as MatrixClient); + + vi.spyOn(client, "mxcUrlToHttp"); + const member = mockMatrixRoomMember({ + userId: "@alice:example.org", + getMxcAvatarUrl: () => "mxc://example.org/alice-avatar", + }); + const displayName = "Alice"; + render( + + + , + ); + const element = screen.getByRole("img", { name: "@alice:example.org" }); + expect(element.tagName).toEqual("SPAN"); + expect(client.mxcUrlToHttp).toBeCalledTimes(0); +}); + test("should attempt to fetch authenticated media", async () => { const expectedAuthUrl = "http://example.org/media/alice-avatar"; const expectedObjectURL = "my-object-url"; diff --git a/src/Avatar.tsx b/src/Avatar.tsx index a68c17f8..f3fe6cd8 100644 --- a/src/Avatar.tsx +++ b/src/Avatar.tsx @@ -9,7 +9,7 @@ import { useMemo, FC, CSSProperties, useState, useEffect } from "react"; import { Avatar as CompoundAvatar } from "@vector-im/compound-web"; import { MatrixClient } from "matrix-js-sdk/src/client"; -import { useClient } from "./ClientContext"; +import { useClientState } from "./ClientContext"; export enum Size { XS = "xs", @@ -67,7 +67,7 @@ export const Avatar: FC = ({ style, ...props }) => { - const { client } = useClient(); + const clientState = useClientState(); const sizePx = useMemo( () => @@ -80,9 +80,16 @@ export const Avatar: FC = ({ const [avatarUrl, setAvatarUrl] = useState(undefined); useEffect(() => { - if (!client || !src || !sizePx) { + if (clientState?.state !== "valid") { return; } + const { authenticated, supportedFeatures } = clientState; + const client = authenticated?.client; + + if (!client || !src || !sizePx || !supportedFeatures.thumbnails) { + return; + } + const token = client.getAccessToken(); if (!token) { return; @@ -113,7 +120,7 @@ export const Avatar: FC = ({ URL.revokeObjectURL(objectUrl); } }; - }, [client, src, sizePx]); + }, [clientState, src, sizePx]); return ( void; }; @@ -255,6 +256,7 @@ export const ClientProvider: FC = ({ children }) => { const [isDisconnected, setIsDisconnected] = useState(false); const [supportsReactions, setSupportsReactions] = useState(false); + const [supportsThumbnails, setSupportsThumbnails] = useState(false); const state: ClientState | undefined = useMemo(() => { if (alreadyOpenedErr) { @@ -280,6 +282,7 @@ export const ClientProvider: FC = ({ children }) => { disconnected: isDisconnected, supportedFeatures: { reactions: supportsReactions, + thumbnails: supportsThumbnails, }, }; }, [ @@ -290,6 +293,7 @@ export const ClientProvider: FC = ({ children }) => { setClient, isDisconnected, supportsReactions, + supportsThumbnails, ]); const onSync = useCallback( @@ -315,6 +319,8 @@ export const ClientProvider: FC = ({ children }) => { } if (initClientState.widgetApi) { + // There is currently no widget API for authenticated media thumbnails. + setSupportsThumbnails(false); const reactSend = initClientState.widgetApi.hasCapability( "org.matrix.msc2762.send.event:m.reaction", ); @@ -336,6 +342,7 @@ export const ClientProvider: FC = ({ children }) => { } } else { setSupportsReactions(true); + setSupportsThumbnails(true); } return (): void => {