New group call experience: Room header call buttons (#9311)
* Make useEventEmitterState more efficient By not invoking the initializing function on every render * Make useWidgets more efficient By not calling WidgetStore on every render * Add new group call experience Labs flag * Add viewingCall field to RoomViewStore state Currently has no effect, but in the future this will signal to RoomView to show the call or call lobby. * Add element_call.use_exclusively config flag As documented in element-web, this will tell the app to use Element Call exclusively for calls, disabling Jitsi and legacy 1:1 calls. * Make placeCall return a promise So that the UI can know when placeCall completes * Update start call buttons to new group call designs Since RoomView doesn't do anything with viewingCall yet, these buttons won't have any effect when starting native group calls, but the logic is at least all there and ready to be hooked up. * Allow calls to be detected if the new group call experience is enabled * Test the RoomHeader changes * Iterate code
This commit is contained in:
@@ -53,38 +53,75 @@ import { UPDATE_EVENT } from "./AsyncStore";
|
||||
|
||||
const NUM_JOIN_RETRY = 5;
|
||||
|
||||
const INITIAL_STATE = {
|
||||
// Whether we're joining the currently viewed room (see isJoining())
|
||||
interface State {
|
||||
/**
|
||||
* Whether we're joining the currently viewed (see isJoining())
|
||||
*/
|
||||
joining: boolean;
|
||||
/**
|
||||
* Any error that has occurred during joining
|
||||
*/
|
||||
joinError: Error | null;
|
||||
/**
|
||||
* The ID of the room currently being viewed
|
||||
*/
|
||||
roomId: string | null;
|
||||
/**
|
||||
* The ID of the room being subscribed to (in Sliding Sync)
|
||||
*/
|
||||
subscribingRoomId: string | null;
|
||||
/**
|
||||
* The event to scroll to when the room is first viewed
|
||||
*/
|
||||
initialEventId: string | null;
|
||||
initialEventPixelOffset: number | null;
|
||||
/**
|
||||
* Whether to highlight the initial event
|
||||
*/
|
||||
isInitialEventHighlighted: boolean;
|
||||
/**
|
||||
* Whether to scroll the initial event into view
|
||||
*/
|
||||
initialEventScrollIntoView: boolean;
|
||||
/**
|
||||
* The alias of the room (or null if not originally specified in view_room)
|
||||
*/
|
||||
roomAlias: string | null;
|
||||
/**
|
||||
* Whether the current room is loading
|
||||
*/
|
||||
roomLoading: boolean;
|
||||
/**
|
||||
* Any error that has occurred during loading
|
||||
*/
|
||||
roomLoadError: MatrixError | null;
|
||||
replyingToEvent: MatrixEvent | null;
|
||||
shouldPeek: boolean;
|
||||
viaServers: string[];
|
||||
wasContextSwitch: boolean;
|
||||
/**
|
||||
* Whether we're viewing a call or call lobby in this room
|
||||
*/
|
||||
viewingCall: boolean;
|
||||
}
|
||||
|
||||
const INITIAL_STATE: State = {
|
||||
joining: false,
|
||||
// Any error that has occurred during joining
|
||||
joinError: null as Error,
|
||||
// The room ID of the room currently being viewed
|
||||
roomId: null as string,
|
||||
// The room ID being subscribed to (in Sliding Sync)
|
||||
subscribingRoomId: null as string,
|
||||
|
||||
// The event to scroll to when the room is first viewed
|
||||
initialEventId: null as string,
|
||||
initialEventPixelOffset: null as number,
|
||||
// Whether to highlight the initial event
|
||||
joinError: null,
|
||||
roomId: null,
|
||||
subscribingRoomId: null,
|
||||
initialEventId: null,
|
||||
initialEventPixelOffset: null,
|
||||
isInitialEventHighlighted: false,
|
||||
// whether to scroll `event_id` into view
|
||||
initialEventScrollIntoView: true,
|
||||
|
||||
// The room alias of the room (or null if not originally specified in view_room)
|
||||
roomAlias: null as string,
|
||||
// Whether the current room is loading
|
||||
roomAlias: null,
|
||||
roomLoading: false,
|
||||
// Any error that has occurred during loading
|
||||
roomLoadError: null as MatrixError,
|
||||
|
||||
replyingToEvent: null as MatrixEvent,
|
||||
|
||||
roomLoadError: null,
|
||||
replyingToEvent: null,
|
||||
shouldPeek: false,
|
||||
|
||||
viaServers: [] as string[],
|
||||
|
||||
viaServers: [],
|
||||
wasContextSwitch: false,
|
||||
viewingCall: false,
|
||||
};
|
||||
|
||||
type Listener = (isActive: boolean) => void;
|
||||
@@ -98,7 +135,7 @@ export class RoomViewStore extends EventEmitter {
|
||||
// the app. We need to eagerly create the instance.
|
||||
public static readonly instance = new RoomViewStore(defaultDispatcher);
|
||||
|
||||
private state = INITIAL_STATE; // initialize state
|
||||
private state: State = INITIAL_STATE; // initialize state
|
||||
|
||||
private dis: MatrixDispatcher;
|
||||
private dispatchToken: string;
|
||||
@@ -120,7 +157,7 @@ export class RoomViewStore extends EventEmitter {
|
||||
this.emit(roomId, isActive);
|
||||
}
|
||||
|
||||
private setState(newState: Partial<typeof INITIAL_STATE>): void {
|
||||
private setState(newState: Partial<State>): void {
|
||||
// If values haven't changed, there's nothing to do.
|
||||
// This only tries a shallow comparison, so unchanged objects will slip
|
||||
// through, but that's probably okay for now.
|
||||
@@ -172,6 +209,7 @@ export class RoomViewStore extends EventEmitter {
|
||||
roomAlias: null,
|
||||
viaServers: [],
|
||||
wasContextSwitch: false,
|
||||
viewingCall: false,
|
||||
});
|
||||
break;
|
||||
case Action.ViewRoomError:
|
||||
@@ -286,6 +324,7 @@ export class RoomViewStore extends EventEmitter {
|
||||
roomLoadError: null,
|
||||
viaServers: payload.via_servers,
|
||||
wasContextSwitch: payload.context_switch,
|
||||
viewingCall: payload.view_call ?? false,
|
||||
});
|
||||
// set this room as the room subscription. We need to await for it as this will fetch
|
||||
// all room state for this room, which is required before we get the state below.
|
||||
@@ -303,11 +342,11 @@ export class RoomViewStore extends EventEmitter {
|
||||
return;
|
||||
}
|
||||
|
||||
const newState = {
|
||||
const newState: Partial<State> = {
|
||||
roomId: payload.room_id,
|
||||
roomAlias: payload.room_alias,
|
||||
initialEventId: payload.event_id,
|
||||
isInitialEventHighlighted: payload.highlighted,
|
||||
roomAlias: payload.room_alias ?? null,
|
||||
initialEventId: payload.event_id ?? null,
|
||||
isInitialEventHighlighted: payload.highlighted ?? false,
|
||||
initialEventScrollIntoView: payload.scroll_into_view ?? true,
|
||||
roomLoading: false,
|
||||
roomLoadError: null,
|
||||
@@ -317,8 +356,12 @@ export class RoomViewStore extends EventEmitter {
|
||||
joining: payload.joining || false,
|
||||
// Reset replyingToEvent because we don't want cross-room because bad UX
|
||||
replyingToEvent: null,
|
||||
viaServers: payload.via_servers,
|
||||
wasContextSwitch: payload.context_switch,
|
||||
viaServers: payload.via_servers ?? [],
|
||||
wasContextSwitch: payload.context_switch ?? false,
|
||||
viewingCall: payload.view_call ?? (
|
||||
// Reset to false when switching rooms
|
||||
payload.room_id === this.state.roomId ? this.state.viewingCall : false
|
||||
),
|
||||
};
|
||||
|
||||
// Allow being given an event to be replied to when switching rooms but sanity check its for this room
|
||||
@@ -351,13 +394,14 @@ export class RoomViewStore extends EventEmitter {
|
||||
roomId: null,
|
||||
initialEventId: null,
|
||||
initialEventPixelOffset: null,
|
||||
isInitialEventHighlighted: null,
|
||||
isInitialEventHighlighted: false,
|
||||
initialEventScrollIntoView: true,
|
||||
roomAlias: payload.room_alias,
|
||||
roomLoading: true,
|
||||
roomLoadError: null,
|
||||
viaServers: payload.via_servers,
|
||||
wasContextSwitch: payload.context_switch,
|
||||
viewingCall: payload.view_call ?? false,
|
||||
});
|
||||
try {
|
||||
const result = await MatrixClientPeg.get().getRoomIdForAlias(payload.room_alias);
|
||||
@@ -577,4 +621,8 @@ export class RoomViewStore extends EventEmitter {
|
||||
public getWasContextSwitch(): boolean {
|
||||
return this.state.wasContextSwitch;
|
||||
}
|
||||
|
||||
public isViewingCall(): boolean {
|
||||
return this.state.viewingCall;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -111,7 +111,7 @@ class WidgetEchoStore extends EventEmitter {
|
||||
}
|
||||
}
|
||||
|
||||
let singletonWidgetEchoStore = null;
|
||||
let singletonWidgetEchoStore: WidgetEchoStore | null = null;
|
||||
if (!singletonWidgetEchoStore) {
|
||||
singletonWidgetEchoStore = new WidgetEchoStore();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user