ideas
This commit is contained in:
@@ -452,14 +452,18 @@ export function createCallViewModel$(
|
|||||||
|
|
||||||
const localMembership = createLocalMembership$({
|
const localMembership = createLocalMembership$({
|
||||||
scope: scope,
|
scope: scope,
|
||||||
homeserverConnected: createHomeserverConnected$(
|
homeserverConnected$: createHomeserverConnected$(
|
||||||
scope,
|
scope,
|
||||||
client,
|
client,
|
||||||
matrixRTCSession,
|
matrixRTCSession,
|
||||||
),
|
),
|
||||||
muteStates: muteStates,
|
muteStates: muteStates,
|
||||||
joinMatrixRTC: (transport: LivekitTransport) => {
|
joinMatrixRTC: async (transport: LivekitTransport) => {
|
||||||
enterRTCSession(matrixRTCSession, transport, connectOptions$.value);
|
return enterRTCSession(
|
||||||
|
matrixRTCSession,
|
||||||
|
transport,
|
||||||
|
connectOptions$.value,
|
||||||
|
);
|
||||||
},
|
},
|
||||||
createPublisherFactory: (connection: Connection) => {
|
createPublisherFactory: (connection: Connection) => {
|
||||||
return new Publisher(
|
return new Publisher(
|
||||||
@@ -569,6 +573,17 @@ export function createCallViewModel$(
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether various media/event sources should pretend to be disconnected from
|
||||||
|
* all network input, even if their connection still technically works.
|
||||||
|
*/
|
||||||
|
// We do this when the app is in the 'reconnecting' state, because it might be
|
||||||
|
// that the LiveKit connection is still functional while the homeserver is
|
||||||
|
// down, for example, and we want to avoid making people worry that the app is
|
||||||
|
// in a split-brained state.
|
||||||
|
// DISCUSSION own membership manager ALSO this probably can be simplifis
|
||||||
|
const reconnecting$ = localMembership.reconnecting$;
|
||||||
|
|
||||||
const audioParticipants$ = scope.behavior(
|
const audioParticipants$ = scope.behavior(
|
||||||
matrixLivekitMembers$.pipe(
|
matrixLivekitMembers$.pipe(
|
||||||
switchMap((membersWithEpoch) => {
|
switchMap((membersWithEpoch) => {
|
||||||
@@ -616,7 +631,7 @@ export function createCallViewModel$(
|
|||||||
);
|
);
|
||||||
|
|
||||||
const handsRaised$ = scope.behavior(
|
const handsRaised$ = scope.behavior(
|
||||||
handsRaisedSubject$.pipe(pauseWhen(localMembership.reconnecting$)),
|
handsRaisedSubject$.pipe(pauseWhen(reconnecting$)),
|
||||||
);
|
);
|
||||||
|
|
||||||
const reactions$ = scope.behavior(
|
const reactions$ = scope.behavior(
|
||||||
@@ -629,7 +644,7 @@ export function createCallViewModel$(
|
|||||||
]),
|
]),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
pauseWhen(localMembership.reconnecting$),
|
pauseWhen(reconnecting$),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -720,7 +735,7 @@ export function createCallViewModel$(
|
|||||||
livekitRoom$,
|
livekitRoom$,
|
||||||
focusUrl$,
|
focusUrl$,
|
||||||
mediaDevices,
|
mediaDevices,
|
||||||
localMembership.reconnecting$,
|
reconnecting$,
|
||||||
displayName$,
|
displayName$,
|
||||||
matrixMemberMetadataStore.createAvatarUrlBehavior$(userId),
|
matrixMemberMetadataStore.createAvatarUrlBehavior$(userId),
|
||||||
handsRaised$.pipe(map((v) => v[participantId]?.time ?? null)),
|
handsRaised$.pipe(map((v) => v[participantId]?.time ?? null)),
|
||||||
@@ -812,17 +827,11 @@ export function createCallViewModel$(
|
|||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
const shouldLeave$: Observable<
|
const leave$: Observable<"user" | "timeout" | "decline" | "allOthersLeft"> =
|
||||||
"user" | "timeout" | "decline" | "allOthersLeft"
|
merge(
|
||||||
> = merge(
|
autoLeave$,
|
||||||
autoLeave$,
|
merge(userHangup$, widgetHangup$).pipe(map(() => "user" as const)),
|
||||||
merge(userHangup$, widgetHangup$).pipe(map(() => "user" as const)),
|
).pipe(scope.share);
|
||||||
).pipe(scope.share);
|
|
||||||
|
|
||||||
shouldLeave$.pipe(scope.bind()).subscribe((reason) => {
|
|
||||||
logger.info(`Call left due to ${reason}`);
|
|
||||||
localMembership.requestDisconnect();
|
|
||||||
});
|
|
||||||
|
|
||||||
const spotlightSpeaker$ = scope.behavior<UserMediaViewModel | null>(
|
const spotlightSpeaker$ = scope.behavior<UserMediaViewModel | null>(
|
||||||
userMedia$.pipe(
|
userMedia$.pipe(
|
||||||
@@ -1444,7 +1453,7 @@ export function createCallViewModel$(
|
|||||||
autoLeave$: autoLeave$,
|
autoLeave$: autoLeave$,
|
||||||
callPickupState$: callPickupState$,
|
callPickupState$: callPickupState$,
|
||||||
ringOverlay$: ringOverlay$,
|
ringOverlay$: ringOverlay$,
|
||||||
leave$: shouldLeave$,
|
leave$: leave$,
|
||||||
hangup: (): void => userHangup$.next(),
|
hangup: (): void => userHangup$.next(),
|
||||||
join: localMembership.requestConnect,
|
join: localMembership.requestConnect,
|
||||||
toggleScreenSharing: toggleScreenSharing,
|
toggleScreenSharing: toggleScreenSharing,
|
||||||
@@ -1491,7 +1500,7 @@ export function createCallViewModel$(
|
|||||||
showFooter$: showFooter$,
|
showFooter$: showFooter$,
|
||||||
earpieceMode$: earpieceMode$,
|
earpieceMode$: earpieceMode$,
|
||||||
audioOutputSwitcher$: audioOutputSwitcher$,
|
audioOutputSwitcher$: audioOutputSwitcher$,
|
||||||
reconnecting$: localMembership.reconnecting$,
|
reconnecting$: reconnecting$,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import {
|
|||||||
} from "@livekit/components-core";
|
} from "@livekit/components-core";
|
||||||
import {
|
import {
|
||||||
ConnectionError,
|
ConnectionError,
|
||||||
type ConnectionState as LivekitConnectionState,
|
ConnectionState as LivekitConnectionState,
|
||||||
type Room as LivekitRoom,
|
type Room as LivekitRoom,
|
||||||
type LocalParticipant,
|
type LocalParticipant,
|
||||||
type RemoteParticipant,
|
type RemoteParticipant,
|
||||||
@@ -47,17 +47,24 @@ export interface ConnectionOpts {
|
|||||||
/** Optional factory to create the LiveKit room, mainly for testing purposes. */
|
/** Optional factory to create the LiveKit room, mainly for testing purposes. */
|
||||||
livekitRoomFactory: () => LivekitRoom;
|
livekitRoomFactory: () => LivekitRoom;
|
||||||
}
|
}
|
||||||
export enum ConnectionAdditionalState {
|
export class FailedToStartError extends Error {
|
||||||
|
public constructor(message: string) {
|
||||||
|
super(message);
|
||||||
|
this.name = "FailedToStartError";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum ConnectionState {
|
||||||
Initialized = "Initialized",
|
Initialized = "Initialized",
|
||||||
FetchingConfig = "FetchingConfig",
|
FetchingConfig = "FetchingConfig",
|
||||||
// FailedToStart = "FailedToStart",
|
|
||||||
Stopped = "Stopped",
|
Stopped = "Stopped",
|
||||||
ConnectingToLkRoom = "ConnectingToLkRoom",
|
ConnectingToLkRoom = "ConnectingToLkRoom",
|
||||||
|
LivekitDisconnected = "disconnected",
|
||||||
|
LivekitConnecting = "connecting",
|
||||||
|
LivekitConnected = "connected",
|
||||||
|
LivekitReconnecting = "reconnecting",
|
||||||
|
LivekitSignalReconnecting = "signalReconnecting",
|
||||||
}
|
}
|
||||||
export type ConnectionState =
|
|
||||||
| { state: ConnectionAdditionalState }
|
|
||||||
| { state: LivekitConnectionState }
|
|
||||||
| { state: "FailedToStart"; error: Error };
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A connection to a Matrix RTC LiveKit backend.
|
* A connection to a Matrix RTC LiveKit backend.
|
||||||
@@ -66,14 +73,15 @@ export type ConnectionState =
|
|||||||
*/
|
*/
|
||||||
export class Connection {
|
export class Connection {
|
||||||
// Private Behavior
|
// Private Behavior
|
||||||
private readonly _state$ = new BehaviorSubject<ConnectionState>({
|
private readonly _state$ = new BehaviorSubject<
|
||||||
state: ConnectionAdditionalState.Initialized,
|
ConnectionState | FailedToStartError
|
||||||
});
|
>(ConnectionState.Initialized);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The current state of the connection to the media transport.
|
* The current state of the connection to the media transport.
|
||||||
*/
|
*/
|
||||||
public readonly state$: Behavior<ConnectionState> = this._state$;
|
public readonly state$: Behavior<ConnectionState | FailedToStartError> =
|
||||||
|
this._state$;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The media transport to connect to.
|
* The media transport to connect to.
|
||||||
@@ -117,16 +125,12 @@ export class Connection {
|
|||||||
this.logger.debug("Starting Connection");
|
this.logger.debug("Starting Connection");
|
||||||
this.stopped = false;
|
this.stopped = false;
|
||||||
try {
|
try {
|
||||||
this._state$.next({
|
this._state$.next(ConnectionState.FetchingConfig);
|
||||||
state: ConnectionAdditionalState.FetchingConfig,
|
|
||||||
});
|
|
||||||
const { url, jwt } = await this.getSFUConfigWithOpenID();
|
const { url, jwt } = await this.getSFUConfigWithOpenID();
|
||||||
// If we were stopped while fetching the config, don't proceed to connect
|
// If we were stopped while fetching the config, don't proceed to connect
|
||||||
if (this.stopped) return;
|
if (this.stopped) return;
|
||||||
|
|
||||||
this._state$.next({
|
this._state$.next(ConnectionState.ConnectingToLkRoom);
|
||||||
state: ConnectionAdditionalState.ConnectingToLkRoom,
|
|
||||||
});
|
|
||||||
try {
|
try {
|
||||||
await this.livekitRoom.connect(url, jwt);
|
await this.livekitRoom.connect(url, jwt);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@@ -157,9 +161,8 @@ export class Connection {
|
|||||||
connectionStateObserver(this.livekitRoom)
|
connectionStateObserver(this.livekitRoom)
|
||||||
.pipe(this.scope.bind())
|
.pipe(this.scope.bind())
|
||||||
.subscribe((lkState) => {
|
.subscribe((lkState) => {
|
||||||
this._state$.next({
|
// It si save to cast lkState to ConnectionState as they are fully overlapping.
|
||||||
state: lkState,
|
this._state$.next(lkState as unknown as ConnectionState);
|
||||||
});
|
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.logger.debug(`Failed to connect to LiveKit room: ${error}`);
|
this.logger.debug(`Failed to connect to LiveKit room: ${error}`);
|
||||||
|
|||||||
Reference in New Issue
Block a user