Simplify widget detection

Use the exists check for the widget API directly
instead of a feature flag.
This commit is contained in:
JephDiel
2026-03-12 22:18:38 -05:00
parent 699e31f59a
commit 8ecb1b3dbf
4 changed files with 16 additions and 30 deletions

View File

@@ -13,19 +13,14 @@ import { type FC, type PropsWithChildren } from "react";
import { ClientContextProvider } from "./ClientContext"; import { ClientContextProvider } from "./ClientContext";
import { Avatar } from "./Avatar"; import { Avatar } from "./Avatar";
import { mockMatrixRoomMember, mockRtcMembership } from "./utils/test"; import { mockMatrixRoomMember, mockRtcMembership } from "./utils/test";
import EventEmitter from "events";
import { widget } from "./widget"; import { widget } from "./widget";
import { WidgetApi } from "matrix-widget-api";
const TestComponent: FC< const TestComponent: FC<
PropsWithChildren<{ PropsWithChildren<{
client: MatrixClient; client: MatrixClient;
supportsAuthenticatedMedia?: boolean;
}> }>
> = ({ > = ({ client, children }) => {
client,
children,
supportsAuthenticatedMedia: supportsAuthenticatedMedia,
}) => {
return ( return (
<ClientContextProvider <ClientContextProvider
value={{ value={{
@@ -33,7 +28,6 @@ const TestComponent: FC<
disconnected: false, disconnected: false,
supportedFeatures: { supportedFeatures: {
reactions: true, reactions: true,
authenticatedMedia: supportsAuthenticatedMedia ?? true,
}, },
setClient: vi.fn(), setClient: vi.fn(),
authenticated: { authenticated: {
@@ -51,7 +45,7 @@ const TestComponent: FC<
vi.mock("./widget", () => ({ vi.mock("./widget", () => ({
widget: { widget: {
api: { downloadFile: vi.fn() }, api: null, // Ideally we'd only mock this in the as a widget test so the whole module is otherwise null, but just nulling `api` by default works well enough
}, },
})); }));
@@ -88,7 +82,7 @@ test("should just render a placeholder when the user has no avatar", () => {
expect(client.mxcUrlToHttp).toBeCalledTimes(0); expect(client.mxcUrlToHttp).toBeCalledTimes(0);
}); });
test("should attempt to fetch authenticated media if supported", async () => { test("should attempt to fetch authenticated media from the server", async () => {
const expectedAuthUrl = "http://example.org/media/alice-avatar"; const expectedAuthUrl = "http://example.org/media/alice-avatar";
const expectedObjectURL = "my-object-url"; const expectedObjectURL = "my-object-url";
const accessToken = "my-access-token"; const accessToken = "my-access-token";
@@ -120,7 +114,7 @@ test("should attempt to fetch authenticated media if supported", async () => {
); );
const displayName = "Alice"; const displayName = "Alice";
render( render(
<TestComponent client={client} supportsAuthenticatedMedia={true}> <TestComponent client={client}>
<Avatar <Avatar
id={member.userId} id={member.userId}
name={displayName} name={displayName}
@@ -141,7 +135,7 @@ test("should attempt to fetch authenticated media if supported", async () => {
}); });
}); });
test("should attempt to use widget API if authenticate media is not supported", async () => { test("should attempt to use widget API if running as a widget", async () => {
const expectedMXCUrl = "mxc://example.org/alice-avatar"; const expectedMXCUrl = "mxc://example.org/alice-avatar";
const expectedObjectURL = "my-object-url"; const expectedObjectURL = "my-object-url";
const theBlob = new Blob([]); const theBlob = new Blob([]);
@@ -157,6 +151,7 @@ test("should attempt to use widget API if authenticate media is not supported",
getAccessToken: () => undefined, getAccessToken: () => undefined,
} as unknown as MatrixClient); } as unknown as MatrixClient);
widget!.api = { downloadFile: vi.fn() } as unknown as WidgetApi;
vi.spyOn(widget!.api, "downloadFile").mockResolvedValue({ file: theBlob }); vi.spyOn(widget!.api, "downloadFile").mockResolvedValue({ file: theBlob });
const member = mockMatrixRoomMember( const member = mockMatrixRoomMember(
mockRtcMembership("@alice:example.org", "AAAA"), mockRtcMembership("@alice:example.org", "AAAA"),
@@ -166,7 +161,7 @@ test("should attempt to use widget API if authenticate media is not supported",
); );
const displayName = "Alice"; const displayName = "Alice";
render( render(
<TestComponent client={client} supportsAuthenticatedMedia={false}> <TestComponent client={client}>
<Avatar <Avatar
id={member.userId} id={member.userId}
name={displayName} name={displayName}

View File

@@ -87,23 +87,23 @@ export const Avatar: FC<Props> = ({
const [avatarUrl, setAvatarUrl] = useState<string | undefined>(undefined); const [avatarUrl, setAvatarUrl] = useState<string | undefined>(undefined);
// In theory, a change in `clientState` or `sizePx` could run extra getAvatarFromWidgetAPI calls, but in practice they should be stable long before this code runs.
useEffect(() => { useEffect(() => {
if (!src || clientState?.state !== "valid") { if (!src) {
setAvatarUrl(undefined); setAvatarUrl(undefined);
return; return;
} }
const { authenticated, supportedFeatures } = clientState;
let blob: Promise<Blob>; let blob: Promise<Blob>;
if ( if (widget?.api) {
supportedFeatures.authenticatedMedia && blob = getAvatarFromWidgetAPI(widget.api, src);
authenticated?.client && } else if (
clientState?.state === "valid" &&
clientState.authenticated?.client &&
sizePx sizePx
) { ) {
blob = getAvatarFromServer(authenticated.client, src, sizePx); blob = getAvatarFromServer(clientState.authenticated.client, src, sizePx);
} else if (widget?.api) {
blob = getAvatarFromWidgetAPI(widget.api, src);
} else { } else {
setAvatarUrl(undefined); setAvatarUrl(undefined);
return; return;

View File

@@ -48,7 +48,6 @@ export type ValidClientState = {
disconnected: boolean; disconnected: boolean;
supportedFeatures: { supportedFeatures: {
reactions: boolean; reactions: boolean;
authenticatedMedia: boolean;
}; };
setClient: (client: MatrixClient, session: Session) => void; setClient: (client: MatrixClient, session: Session) => void;
}; };
@@ -249,8 +248,6 @@ export const ClientProvider: FC<Props> = ({ children }) => {
const [isDisconnected, setIsDisconnected] = useState(false); const [isDisconnected, setIsDisconnected] = useState(false);
const [supportsReactions, setSupportsReactions] = useState(false); const [supportsReactions, setSupportsReactions] = useState(false);
const [supportsAuthenticatedMedia, setSupportsAuthenticatedMedia] =
useState(false);
const state: ClientState | undefined = useMemo(() => { const state: ClientState | undefined = useMemo(() => {
if (alreadyOpenedErr) { if (alreadyOpenedErr) {
@@ -276,7 +273,6 @@ export const ClientProvider: FC<Props> = ({ children }) => {
disconnected: isDisconnected, disconnected: isDisconnected,
supportedFeatures: { supportedFeatures: {
reactions: supportsReactions, reactions: supportsReactions,
authenticatedMedia: supportsAuthenticatedMedia,
}, },
}; };
}, [ }, [
@@ -287,7 +283,6 @@ export const ClientProvider: FC<Props> = ({ children }) => {
setClient, setClient,
isDisconnected, isDisconnected,
supportsReactions, supportsReactions,
supportsAuthenticatedMedia,
]); ]);
const onSync = useCallback( const onSync = useCallback(
@@ -313,8 +308,6 @@ export const ClientProvider: FC<Props> = ({ children }) => {
} }
if (initClientState.widgetApi) { if (initClientState.widgetApi) {
// There is currently no way for widgets to request authenticated media directly from the server.
setSupportsAuthenticatedMedia(false);
const reactSend = initClientState.widgetApi.hasCapability( const reactSend = initClientState.widgetApi.hasCapability(
"org.matrix.msc2762.send.event:m.reaction", "org.matrix.msc2762.send.event:m.reaction",
); );
@@ -336,7 +329,6 @@ export const ClientProvider: FC<Props> = ({ children }) => {
} }
} else { } else {
setSupportsReactions(true); setSupportsReactions(true);
setSupportsAuthenticatedMedia(true);
} }
return (): void => { return (): void => {

View File

@@ -78,7 +78,6 @@ function renderWithMockClient(
disconnected: false, disconnected: false,
supportedFeatures: { supportedFeatures: {
reactions: true, reactions: true,
authenticatedMedia: true,
}, },
setClient: vi.fn(), setClient: vi.fn(),
authenticated: { authenticated: {