Stop asking whether Element Call is a widget

The remaining reads of the widget global were all asking one of two
different questions, so they get two different answers.

The app shell — the auth hooks, automatic guest registration, the group
call loader's diagnostic and the initial mute state — wants to know
whether Element Call was launched as a widget. That is a property of the
URL it was launched with, so expose the isWidget that computeUrlParams
already computed internally, documented as being for shell use only.

The call interface — whether to offer the profile settings tab — wants to
know something about its host, so it asks the bridge. A host that can
dismiss Element Call owns the user's account, so their profile is not ours
to edit; this reuses the close capability as a proxy, with a TODO
alongside the others.

ClientContext also takes supportsReactions from the bridge rather than
checking four widget capabilities itself, which removes widgetApi from
InitResult — a field that was always null outside widget mode.

Note this changes behaviour for a malformed widget URL: one carrying a
widget ID and parent URL but missing the room, user, device or base URL
would previously have fallen back to registering a guest user, and will
now not.
This commit is contained in:
Valere
2026-09-02 15:04:24 +02:00
parent bc6c232ef0
commit f28d9e9b06
9 changed files with 45 additions and 46 deletions
+4 -3
View File
@@ -17,7 +17,7 @@ import { logger } from "matrix-js-sdk/lib/logger";
import { initClient } from "../utils/matrix";
import { type Session } from "../ClientContext";
import { Config } from "../config/Config";
import { widget } from "../widget";
import { useUrlParams } from "../UrlParams";
export const useInteractiveRegistration = (
oldClient?: MatrixClient,
@@ -32,6 +32,7 @@ export const useInteractiveRegistration = (
passwordlessUser: boolean,
) => Promise<[MatrixClient, Session]>;
} => {
const { isWidget } = useUrlParams();
const [privacyPolicyUrl, setPrivacyPolicyUrl] = useState<string | undefined>(
undefined,
);
@@ -47,7 +48,7 @@ export const useInteractiveRegistration = (
}
useEffect(() => {
if (widget) return;
if (isWidget) return;
// An empty registerRequest is used to get the privacy policy and recaptcha key.
authClient.current!.registerRequest({}).catch((error) => {
setPrivacyPolicyUrl(
@@ -55,7 +56,7 @@ export const useInteractiveRegistration = (
);
setRecaptchaKey(error.data?.params["m.login.recaptcha"]?.public_key);
});
}, []);
}, [isWidget]);
const register = useCallback(
async (
+4 -3
View File
@@ -12,7 +12,7 @@ import { useClient } from "../ClientContext";
import { useInteractiveRegistration } from "../auth/useInteractiveRegistration";
import { generateRandomName } from "../auth/generateRandomName";
import { useRecaptcha } from "../auth/useRecaptcha";
import { widget } from "../widget";
import { useUrlParams } from "../UrlParams";
interface UseRegisterPasswordlessUserType {
privacyPolicyUrl?: string;
@@ -22,6 +22,7 @@ interface UseRegisterPasswordlessUserType {
export function useRegisterPasswordlessUser(): UseRegisterPasswordlessUserType {
const { setClient } = useClient();
const { isWidget } = useUrlParams();
const { privacyPolicyUrl, recaptchaKey, register } =
useInteractiveRegistration();
const { execute, reset, recaptchaId } = useRecaptcha(recaptchaKey);
@@ -31,7 +32,7 @@ export function useRegisterPasswordlessUser(): UseRegisterPasswordlessUserType {
if (!setClient) {
throw new Error("No client context");
}
if (widget) {
if (isWidget) {
throw new Error(
"Registration was skipped: We should never try to register password-less user in embedded mode.",
);
@@ -53,7 +54,7 @@ export function useRegisterPasswordlessUser(): UseRegisterPasswordlessUserType {
throw e;
}
},
[execute, reset, register, setClient],
[execute, reset, register, setClient, isWidget],
);
return { privacyPolicyUrl, registerPasswordlessUser, recaptchaId };