Allow Element Call to be started without audio / video interface (#924)

* config: add feature in `config.json`

* groupCall: adjust connection state in feed if allowCallWithoutVideoAndAudio

* matrix-js-sdk: update version for allowCallWithoutVideoAndAudio

- I modified the SDK so that mute unmute work without media and check device permission inside the SDK
- allowCallWithoutVideoAndAudio is only checked at one point outside the SDK

* docu: add join group call without media docu in READMe

---------

Co-authored-by: Robin Townsend <robin@robin.town>
Co-authored-by: Šimon Brandner <simon.bra.ag@gmail.com>
This commit is contained in:
Enrico Schwendig 2023-03-02 18:48:32 +01:00 committed by GitHub
commit 29e41c7227
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 89 additions and 38 deletions

View file

@ -44,6 +44,14 @@ export interface ConfigOptions {
server_name: string;
};
};
/**
* Allow to join a group calls without audio and video.
* TEMPORARY: Is a feature that's not proved and experimental
*/
features?: {
feature_group_calls_without_video_and_audio: boolean;
};
}
// Overrides members from ConfigOptions that are always provided by the

View file

@ -92,10 +92,14 @@ export async function initClient(
indexedDB = window.indexedDB;
} catch (e) {}
const storeOpts = {} as ICreateClientOpts;
const baseOpts = {
fallbackICEServerAllowed: fallbackICEServerAllowed,
isVoipWithNoMediaAllowed:
Config.get().features?.feature_group_calls_without_video_and_audio,
} as ICreateClientOpts;
if (indexedDB && localStorage) {
storeOpts.store = new IndexedDBStore({
baseOpts.store = new IndexedDBStore({
indexedDB: window.indexedDB,
localStorage,
dbName: SYNC_STORE_NAME,
@ -107,7 +111,7 @@ export async function initClient(
: () => new IndexedDBWorker(),
});
} else if (localStorage) {
storeOpts.store = new MemoryStore({ localStorage });
baseOpts.store = new MemoryStore({ localStorage });
}
// Check whether we have crypto data store. If we are restoring a session
@ -139,14 +143,14 @@ export async function initClient(
}
if (indexedDB) {
storeOpts.cryptoStore = new IndexedDBCryptoStore(
baseOpts.cryptoStore = new IndexedDBCryptoStore(
indexedDB,
CRYPTO_STORE_NAME
);
} else if (localStorage) {
storeOpts.cryptoStore = new LocalStorageCryptoStore(localStorage);
baseOpts.cryptoStore = new LocalStorageCryptoStore(localStorage);
} else {
storeOpts.cryptoStore = new MemoryCryptoStore();
baseOpts.cryptoStore = new MemoryCryptoStore();
}
// XXX: we read from the URL params in RoomPage too:
@ -160,7 +164,7 @@ export async function initClient(
}
const client = createClient({
...storeOpts,
...baseOpts,
...clientOptions,
useAuthorizationHeader: true,
// Use a relatively low timeout for API calls: this is a realtime app

View file

@ -372,27 +372,36 @@ export function InCallView({
if (noControls) {
footer = null;
} else if (reducedControls) {
footer = (
<div className={styles.footer}>
<MicButton muted={microphoneMuted} onPress={toggleMicrophoneMuted} />
<VideoButton muted={localVideoMuted} onPress={toggleLocalVideoMuted} />
<HangupButton onPress={onLeave} />
</div>
);
} else {
footer = (
<div className={styles.footer}>
<MicButton muted={microphoneMuted} onPress={toggleMicrophoneMuted} />
<VideoButton muted={localVideoMuted} onPress={toggleLocalVideoMuted} />
{canScreenshare && !hideScreensharing && !isSafari && (
const buttons: JSX.Element[] = [];
buttons.push(
<MicButton
key="1"
muted={microphoneMuted}
onPress={toggleMicrophoneMuted}
/>,
<VideoButton
key="2"
muted={localVideoMuted}
onPress={toggleLocalVideoMuted}
/>
);
if (!reducedControls) {
if (canScreenshare && !hideScreensharing && !isSafari) {
buttons.push(
<ScreenshareButton
key="3"
enabled={isScreensharing}
onPress={toggleScreensharing}
/>
)}
{!maximisedParticipant && (
);
}
if (!maximisedParticipant) {
buttons.push(
<OverflowMenu
key="4"
inCall
roomIdOrAlias={roomIdOrAlias}
groupCall={groupCall}
@ -400,10 +409,12 @@ export function InCallView({
feedbackModalState={feedbackModalState}
feedbackModalProps={feedbackModalProps}
/>
)}
<HangupButton onPress={onLeave} />
</div>
);
);
}
}
buttons.push(<HangupButton key="6" onPress={onLeave} />);
footer = <div className={styles.footer}>{buttons}</div>;
}
return (

View file

@ -98,12 +98,24 @@ function getParticipants(
(f) => f.userId === member.userId && f.deviceId === deviceId
);
participantInfoMap.set(deviceId, {
connectionState: feed
let connectionState: ConnectionState;
// If we allow calls without media, we have no feeds and cannot read the connection status from them.
// @TODO: The connection state should generally not be determined by the feed.
if (
groupCall.allowCallWithoutVideoAndAudio &&
!feed &&
!participant.screensharing
) {
connectionState = ConnectionState.Connected;
} else {
connectionState = feed
? feed.connected
? ConnectionState.Connected
: ConnectionState.WaitMedia
: ConnectionState.EstablishingCall,
: ConnectionState.EstablishingCall;
}
participantInfoMap.set(deviceId, {
connectionState,
presenter: participant.screensharing,
});
}