mirror of
https://github.com/vector-im/element-call.git
synced 2026-08-20 20:49:20 +00:00
Compare commits
4 Commits
tiles-debu
...
call_bridg
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
de1c6ddb9a | ||
|
|
275f843afc | ||
|
|
560f0221fb | ||
|
|
48bbe613fe |
@@ -44,6 +44,7 @@ import {
|
||||
import { translatedError } from "./TranslatedError";
|
||||
import { useEventTarget } from "./useEvents";
|
||||
import { Config } from "./config/Config";
|
||||
import { useUrlParams } from "./UrlParams";
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
@@ -152,7 +153,7 @@ interface Props {
|
||||
|
||||
export const ClientProvider: FC<Props> = ({ children }) => {
|
||||
const history = useHistory();
|
||||
|
||||
const { token, userId, deviceId, baseUrl } = useUrlParams();
|
||||
// null = signed out, undefined = loading
|
||||
const [initClientState, setInitClientState] = useState<
|
||||
InitResult | null | undefined
|
||||
@@ -165,12 +166,26 @@ export const ClientProvider: FC<Props> = ({ children }) => {
|
||||
// the client.
|
||||
if (initializing.current) return;
|
||||
initializing.current = true;
|
||||
const tokenLogin =
|
||||
!token && !userId
|
||||
? undefined
|
||||
: ({
|
||||
token,
|
||||
userId,
|
||||
deviceId,
|
||||
homeserver: baseUrl,
|
||||
} as {
|
||||
token: string;
|
||||
userId: string;
|
||||
deviceId: string;
|
||||
homeserver: string;
|
||||
});
|
||||
|
||||
loadClient()
|
||||
loadClient(tokenLogin)
|
||||
.then(setInitClientState)
|
||||
.catch((err) => logger.error(err))
|
||||
.finally(() => (initializing.current = false));
|
||||
}, []);
|
||||
}, [token, userId, deviceId, baseUrl]);
|
||||
|
||||
const changePassword = useCallback(
|
||||
async (password: string) => {
|
||||
@@ -339,7 +354,12 @@ type InitResult = {
|
||||
passwordlessUser: boolean;
|
||||
};
|
||||
|
||||
async function loadClient(): Promise<InitResult | null> {
|
||||
async function loadClient(tokenLogin?: {
|
||||
token: string;
|
||||
userId: string;
|
||||
deviceId: string;
|
||||
homeserver: string;
|
||||
}): Promise<InitResult | null> {
|
||||
if (widget) {
|
||||
// We're inside a widget, so let's engage *matryoshka mode*
|
||||
logger.log("Using a matryoshka client");
|
||||
@@ -351,7 +371,15 @@ async function loadClient(): Promise<InitResult | null> {
|
||||
} else {
|
||||
// We're running as a standalone application
|
||||
try {
|
||||
const session = loadSession();
|
||||
let session = loadSession();
|
||||
if (tokenLogin) {
|
||||
session = {
|
||||
user_id: tokenLogin.userId,
|
||||
device_id: tokenLogin.deviceId, //"TOKEN_DEVICE",
|
||||
access_token: tokenLogin.token,
|
||||
passwordlessUser: false,
|
||||
};
|
||||
}
|
||||
if (!session) {
|
||||
logger.log("No session stored; continuing without a client");
|
||||
return null;
|
||||
@@ -362,7 +390,7 @@ async function loadClient(): Promise<InitResult | null> {
|
||||
/* eslint-disable camelcase */
|
||||
const { user_id, device_id, access_token, passwordlessUser } = session;
|
||||
const initClientParams = {
|
||||
baseUrl: Config.defaultHomeserverUrl()!,
|
||||
baseUrl: tokenLogin?.homeserver ?? Config.defaultHomeserverUrl()!,
|
||||
accessToken: access_token,
|
||||
userId: user_id,
|
||||
deviceId: device_id,
|
||||
@@ -371,7 +399,7 @@ async function loadClient(): Promise<InitResult | null> {
|
||||
};
|
||||
|
||||
try {
|
||||
const client = await initClient(initClientParams, true);
|
||||
const client = await initClient(initClientParams, !tokenLogin);
|
||||
return {
|
||||
client,
|
||||
passwordlessUser,
|
||||
|
||||
@@ -108,6 +108,16 @@ interface UrlParams {
|
||||
* E2EE password
|
||||
*/
|
||||
password: string | null;
|
||||
/**
|
||||
* Token for a user for instance login. This is used for bridge gosts.
|
||||
*/
|
||||
token: string | null;
|
||||
/**
|
||||
* Setting this flag skips the lobby and brings you in the call directly.
|
||||
* In the widget this can be combined with preload to pass the device settings
|
||||
* with the join widget action.
|
||||
*/
|
||||
skipLobby: boolean;
|
||||
}
|
||||
|
||||
// This is here as a stopgap, but what would be far nicer is a function that
|
||||
@@ -200,6 +210,8 @@ export const getUrlParams = (
|
||||
fontScale: Number.isNaN(fontScale) ? null : fontScale,
|
||||
analyticsID: parser.getParam("analyticsID"),
|
||||
allowIceFallback: parser.getFlagParam("allowIceFallback"),
|
||||
token: parser.getParam("token"),
|
||||
skipLobby: parser.getFlagParam("skipLobby"),
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -85,12 +85,17 @@ export const useManageRoomSharedKey = (roomId: string): string | null => {
|
||||
|
||||
export const useIsRoomE2EE = (roomId: string): boolean | null => {
|
||||
const { client } = useClient();
|
||||
const { token } = useUrlParams();
|
||||
const isRoomForBridgedCall = !!token;
|
||||
const room = useMemo(() => client?.getRoom(roomId) ?? null, [roomId, client]);
|
||||
// For now, rooms in widget mode are never considered encrypted.
|
||||
// In the future, when widget mode gains encryption support, then perhaps we
|
||||
// should inspect the e2eEnabled URL parameter here?
|
||||
return useMemo(
|
||||
() => widget === null && (room === null || !room.getCanonicalAlias()),
|
||||
[room]
|
||||
() =>
|
||||
!isRoomForBridgedCall &&
|
||||
widget === null &&
|
||||
(room === null || !room.getCanonicalAlias()),
|
||||
[room, isRoomForBridgedCall]
|
||||
);
|
||||
};
|
||||
|
||||
@@ -60,6 +60,7 @@ interface Props {
|
||||
isPasswordlessUser: boolean;
|
||||
confineToRoom: boolean;
|
||||
preload: boolean;
|
||||
skipLobby: boolean;
|
||||
hideHeader: boolean;
|
||||
rtcSession: MatrixRTCSession;
|
||||
}
|
||||
@@ -69,6 +70,7 @@ export function GroupCallView({
|
||||
isPasswordlessUser,
|
||||
confineToRoom,
|
||||
preload,
|
||||
skipLobby,
|
||||
hideHeader,
|
||||
rtcSession,
|
||||
}: Props) {
|
||||
@@ -133,18 +135,17 @@ export function GroupCallView({
|
||||
latestMuteStates.current = muteStates;
|
||||
|
||||
useEffect(() => {
|
||||
if (widget && preload) {
|
||||
// In preload mode, wait for a join action before entering
|
||||
const onJoin = async (ev: CustomEvent<IWidgetApiRequest>) => {
|
||||
if (skipLobby) {
|
||||
// widget && preload
|
||||
const defaultDeviceSetup = async (
|
||||
requestedDeviceData: JoinCallData
|
||||
): Promise<void> => {
|
||||
// XXX: I think this is broken currently - LiveKit *won't* request
|
||||
// permissions and give you device names unless you specify a kind, but
|
||||
// here we want all kinds of devices. This needs a fix in livekit-client
|
||||
// for the following name-matching logic to do anything useful.
|
||||
const devices = await Room.getLocalDevices(undefined, true);
|
||||
|
||||
const { audioInput, videoInput } = ev.detail
|
||||
.data as unknown as JoinCallData;
|
||||
|
||||
const { audioInput, videoInput } = requestedDeviceData;
|
||||
if (audioInput === null) {
|
||||
latestMuteStates.current!.audio.setEnabled?.(false);
|
||||
} else {
|
||||
@@ -184,27 +185,27 @@ export function GroupCallView({
|
||||
latestMuteStates.current!.video.setEnabled?.(true);
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
// In preload mode, wait for a join action before entering
|
||||
if (widget && preload) {
|
||||
const onJoin = async (ev: CustomEvent<IWidgetApiRequest>) => {
|
||||
defaultDeviceSetup(ev.detail.data as unknown as JoinCallData);
|
||||
enterRTCSession(rtcSession);
|
||||
await Promise.all([
|
||||
widget!.api.setAlwaysOnScreen(true),
|
||||
widget!.api.transport.reply(ev.detail, {}),
|
||||
]);
|
||||
};
|
||||
widget.lazyActions.on(ElementWidgetActions.JoinCall, onJoin);
|
||||
return () => {
|
||||
widget!.lazyActions.off(ElementWidgetActions.JoinCall, onJoin);
|
||||
};
|
||||
} else {
|
||||
defaultDeviceSetup({ audioInput: null, videoInput: null });
|
||||
enterRTCSession(rtcSession);
|
||||
|
||||
PosthogAnalytics.instance.eventCallEnded.cacheStartCall(new Date());
|
||||
// we only have room sessions right now, so call ID is the emprty string - we use the room ID
|
||||
PosthogAnalytics.instance.eventCallStarted.track(
|
||||
rtcSession.room.roomId
|
||||
);
|
||||
|
||||
await Promise.all([
|
||||
widget!.api.setAlwaysOnScreen(true),
|
||||
widget!.api.transport.reply(ev.detail, {}),
|
||||
]);
|
||||
};
|
||||
|
||||
widget.lazyActions.on(ElementWidgetActions.JoinCall, onJoin);
|
||||
return () => {
|
||||
widget!.lazyActions.off(ElementWidgetActions.JoinCall, onJoin);
|
||||
};
|
||||
}
|
||||
}
|
||||
}, [rtcSession, preload]);
|
||||
}, [rtcSession, preload, skipLobby]);
|
||||
|
||||
const [left, setLeft] = useState(false);
|
||||
const [leaveError, setLeaveError] = useState<Error | undefined>(undefined);
|
||||
|
||||
@@ -30,8 +30,14 @@ import { platform } from "../Platform";
|
||||
import { AppSelectionModal } from "./AppSelectionModal";
|
||||
|
||||
export const RoomPage: FC = () => {
|
||||
const { confineToRoom, appPrompt, preload, hideHeader, displayName } =
|
||||
useUrlParams();
|
||||
const {
|
||||
confineToRoom,
|
||||
appPrompt,
|
||||
preload,
|
||||
hideHeader,
|
||||
displayName,
|
||||
skipLobby,
|
||||
} = useUrlParams();
|
||||
|
||||
const { roomAlias, roomId, viaServers } = useRoomIdentifier();
|
||||
|
||||
@@ -77,10 +83,11 @@ export const RoomPage: FC = () => {
|
||||
isPasswordlessUser={passwordlessUser}
|
||||
confineToRoom={confineToRoom}
|
||||
preload={preload}
|
||||
skipLobby={skipLobby}
|
||||
hideHeader={hideHeader}
|
||||
/>
|
||||
),
|
||||
[client, passwordlessUser, confineToRoom, preload, hideHeader]
|
||||
[client, passwordlessUser, confineToRoom, preload, hideHeader, skipLobby]
|
||||
);
|
||||
|
||||
let content: ReactNode;
|
||||
|
||||
Reference in New Issue
Block a user