calling: add internal preferences for DRED, bitrate, VP9, sfu url

This commit is contained in:
adel-signal
2026-03-19 09:46:33 -07:00
committed by GitHub
parent b624e48cf1
commit 8b510c3b30
8 changed files with 350 additions and 29 deletions

View File

@@ -56,6 +56,9 @@ export type SemverKeyType = ArrayValues<typeof SemverKeys>;
const ScalarKeys = [ const ScalarKeys = [
'desktop.callQualitySurveyPPM', 'desktop.callQualitySurveyPPM',
'desktop.calling.dredDuration.alpha',
'desktop.calling.dredDuration.beta',
'desktop.calling.dredDuration.prod',
'desktop.clientExpiration', 'desktop.clientExpiration',
'desktop.internalUser', 'desktop.internalUser',
'desktop.loggingErrorToasts', 'desktop.loggingErrorToasts',

View File

@@ -647,6 +647,18 @@ export default {
}, },
cqsTestMode: false, cqsTestMode: false,
setCqsTestMode: action('setCqsTestMode'), setCqsTestMode: action('setCqsTestMode'),
dredDuration: 0,
setDredDuration: action('setDredDuration'),
directMaxBitrate: 1000000,
setDirectMaxBitrate: action('setDirectMaxBitrate'),
isDirectVp9Enabled: true,
setIsDirectVp9Enabled: action('setIsDirectVp9Enabled'),
groupMaxBitrate: 1000000,
setGroupMaxBitrate: action('setGroupMaxBitrate'),
isGroupVp9Enabled: false,
setIsGroupVp9Enabled: action('setIsDirectVp9Enabled'),
sfuUrl: 'https://sfu.voip.signal.org',
setSfuUrl: action('setSfuUrl'),
} satisfies PropsType, } satisfies PropsType,
} satisfies Meta<PropsType>; } satisfies Meta<PropsType>;

View File

@@ -210,6 +210,14 @@ export type PropsDataType = {
>; >;
donationReceipts: ReadonlyArray<DonationReceipt>; donationReceipts: ReadonlyArray<DonationReceipt>;
// calling internal preferences
dredDuration: number | undefined;
isDirectVp9Enabled: boolean | undefined;
directMaxBitrate: number | undefined;
isGroupVp9Enabled: boolean | undefined;
groupMaxBitrate: number | undefined;
sfuUrl: string | undefined;
} & Omit<MediaDeviceSettings, 'availableCameras'>; } & Omit<MediaDeviceSettings, 'availableCameras'>;
type PropsFunctionType = { type PropsFunctionType = {
@@ -345,6 +353,12 @@ type PropsFunctionType = {
) => Promise<ReadonlyArray<RowType<object>>>; ) => Promise<ReadonlyArray<RowType<object>>>;
cqsTestMode: boolean; cqsTestMode: boolean;
setCqsTestMode: (value: boolean) => void; setCqsTestMode: (value: boolean) => void;
setDredDuration: (value: number | undefined) => void;
setIsDirectVp9Enabled: (value: boolean | undefined) => void;
setDirectMaxBitrate: (value: number | undefined) => void;
setIsGroupVp9Enabled: (value: boolean | undefined) => void;
setGroupMaxBitrate: (value: number | undefined) => void;
setSfuUrl: (value: string | undefined) => void;
// Localization // Localization
i18n: LocalizerType; i18n: LocalizerType;
@@ -554,6 +568,18 @@ export function Preferences({
__dangerouslyRunAbitraryReadOnlySqlQuery, __dangerouslyRunAbitraryReadOnlySqlQuery,
cqsTestMode, cqsTestMode,
setCqsTestMode, setCqsTestMode,
setDredDuration,
dredDuration,
setIsDirectVp9Enabled,
isDirectVp9Enabled,
setDirectMaxBitrate,
directMaxBitrate,
setIsGroupVp9Enabled,
isGroupVp9Enabled,
setGroupMaxBitrate,
groupMaxBitrate,
setSfuUrl,
sfuUrl,
}: PropsType): React.JSX.Element { }: PropsType): React.JSX.Element {
const storiesId = useId(); const storiesId = useId();
const themeSelectId = useId(); const themeSelectId = useId();
@@ -2333,6 +2359,18 @@ export function Preferences({
} }
cqsTestMode={cqsTestMode} cqsTestMode={cqsTestMode}
setCqsTestMode={setCqsTestMode} setCqsTestMode={setCqsTestMode}
dredDuration={dredDuration}
setDredDuration={setDredDuration}
setIsDirectVp9Enabled={setIsDirectVp9Enabled}
isDirectVp9Enabled={isDirectVp9Enabled}
setDirectMaxBitrate={setDirectMaxBitrate}
directMaxBitrate={directMaxBitrate}
setIsGroupVp9Enabled={setIsGroupVp9Enabled}
isGroupVp9Enabled={isGroupVp9Enabled}
setGroupMaxBitrate={setGroupMaxBitrate}
groupMaxBitrate={groupMaxBitrate}
sfuUrl={sfuUrl}
setSfuUrl={setSfuUrl}
/> />
} }
contentsRef={settingsPaneRef} contentsRef={settingsPaneRef}

View File

@@ -40,6 +40,19 @@ export function PreferencesInternal({
__dangerouslyRunAbitraryReadOnlySqlQuery, __dangerouslyRunAbitraryReadOnlySqlQuery,
cqsTestMode, cqsTestMode,
setCqsTestMode, setCqsTestMode,
dredDuration,
setDredDuration,
isDirectVp9Enabled,
setIsDirectVp9Enabled,
directMaxBitrate,
setDirectMaxBitrate,
isGroupVp9Enabled,
setIsGroupVp9Enabled,
groupMaxBitrate,
setGroupMaxBitrate,
sfuUrl,
setSfuUrl,
}: { }: {
i18n: LocalizerType; i18n: LocalizerType;
validateBackup: () => Promise<BackupValidationResultType>; validateBackup: () => Promise<BackupValidationResultType>;
@@ -65,6 +78,18 @@ export function PreferencesInternal({
) => Promise<ReadonlyArray<RowType<object>>>; ) => Promise<ReadonlyArray<RowType<object>>>;
cqsTestMode: boolean; cqsTestMode: boolean;
setCqsTestMode: (value: boolean) => void; setCqsTestMode: (value: boolean) => void;
dredDuration: number | undefined;
setDredDuration: (value: number | undefined) => void;
isDirectVp9Enabled: boolean | undefined;
setIsDirectVp9Enabled: (value: boolean | undefined) => void;
directMaxBitrate: number | undefined;
setDirectMaxBitrate: (value: number | undefined) => void;
isGroupVp9Enabled: boolean | undefined;
setIsGroupVp9Enabled: (value: boolean | undefined) => void;
groupMaxBitrate: number | undefined;
setGroupMaxBitrate: (value: number | undefined) => void;
sfuUrl: string | undefined;
setSfuUrl: (value: string | undefined) => void;
}): React.JSX.Element { }): React.JSX.Element {
const [messageCountBySchemaVersion, setMessageCountBySchemaVersion] = const [messageCountBySchemaVersion, setMessageCountBySchemaVersion] =
useState<MessageCountBySchemaVersionType>(); useState<MessageCountBySchemaVersionType>();
@@ -89,6 +114,57 @@ export function PreferencesInternal({
RowType<object> RowType<object>
> | null>(null); > | null>(null);
const stripAndParseString = (input: string): number | undefined => {
const stripped = input.replace(/\D/g, '');
return stripped.length !== 0 ? parseInt(stripped, 10) : undefined;
};
const handleDredDurationUpdate = useCallback(
(input: string) => {
const parsed = stripAndParseString(input);
if (parsed) {
setDredDuration(Math.min(100, parsed));
} else {
setDredDuration(undefined);
}
},
[setDredDuration]
);
const handleDirectMaxBitrateUpdate = useCallback(
(input: string) => {
setDirectMaxBitrate(stripAndParseString(input));
},
[setDirectMaxBitrate]
);
const handleGroupMaxBitrateUpdate = useCallback(
(input: string) => {
setGroupMaxBitrate(stripAndParseString(input));
},
[setGroupMaxBitrate]
);
const handleSfuUrlUpdate = useCallback(
(input: string) => {
const url = input.trim();
setSfuUrl(url.length !== 0 ? url : undefined);
},
[setSfuUrl]
);
const handleResetCallingOverrides = useCallback(() => {
setDredDuration(undefined);
setIsDirectVp9Enabled(undefined);
setDirectMaxBitrate(undefined);
setIsGroupVp9Enabled(undefined);
setGroupMaxBitrate(undefined);
setSfuUrl(undefined);
}, [
setDredDuration,
setIsDirectVp9Enabled,
setDirectMaxBitrate,
setIsGroupVp9Enabled,
setGroupMaxBitrate,
setSfuUrl,
]);
const validateBackup = useCallback(async () => { const validateBackup = useCallback(async () => {
setIsValidationPending(true); setIsValidationPending(true);
setValidationResult(undefined); setValidationResult(undefined);
@@ -559,6 +635,94 @@ export function PreferencesInternal({
/> />
)} )}
</SettingsRow> </SettingsRow>
<SettingsRow title="Calling General">
<FlowingSettingsControl>
<div className="Preferences__two-thirds-flow">
Clear custom calling preferences
</div>
<div className="Preferences__one-third-flow Preferences__one-third-flow--justify-end">
<AxoButton.Root
variant="destructive"
size="lg"
onClick={handleResetCallingOverrides}
>
Clear
</AxoButton.Root>
</div>
</FlowingSettingsControl>
<FlowingSettingsControl>
<div className="Preferences__two-thirds-flow">
DRED Duration (0 - 100)
</div>
<div className="Preferences__one-third-flow Preferences__one-third-flow--justify-end">
<AutoSizeTextArea
i18n={i18n}
value={dredDuration?.toString(10)}
onChange={handleDredDurationUpdate}
placeholder="0 - 100"
moduleClassName="Preferences__ReadonlySqlPlayground__Textarea"
/>
</div>
</FlowingSettingsControl>
</SettingsRow>
<SettingsRow title="Direct Calls">
<FlowingSettingsControl>
<div className="Preferences__two-thirds-flow">Enable VP9</div>
<div className="Preferences__one-third-flow Preferences__one-third-flow--justify-end">
<AxoSwitch.Root
checked={isDirectVp9Enabled ?? true}
onCheckedChange={setIsDirectVp9Enabled}
/>
</div>
</FlowingSettingsControl>
<FlowingSettingsControl>
<div className="Preferences__two-thirds-flow">Max bitrate</div>
<div className="Preferences__one-third-flow Preferences__one-third-flow--justify-end">
<AutoSizeTextArea
i18n={i18n}
value={directMaxBitrate?.toString(10)}
onChange={handleDirectMaxBitrateUpdate}
placeholder="Default"
moduleClassName="Preferences__ReadonlySqlPlayground__Textarea"
/>
</div>
</FlowingSettingsControl>
</SettingsRow>
<SettingsRow title="Group/Adhoc Calls">
<FlowingSettingsControl>
<div className="Preferences__two-thirds-flow">Enable VP9</div>
<div className="Preferences__one-third-flow Preferences__one-third-flow--justify-end">
<AxoSwitch.Root
checked={isGroupVp9Enabled ?? false}
onCheckedChange={setIsGroupVp9Enabled}
/>
</div>
</FlowingSettingsControl>
<FlowingSettingsControl>
<div className="Preferences__two-thirds-flow">Max bitrate</div>
<div className="Preferences__one-third-flow Preferences__one-third-flow--justify-end">
<AutoSizeTextArea
i18n={i18n}
value={groupMaxBitrate?.toString(10)}
onChange={handleGroupMaxBitrateUpdate}
placeholder="Default"
moduleClassName="Preferences__ReadonlySqlPlayground__Textarea"
/>
</div>
</FlowingSettingsControl>
<FlowingSettingsControl>
<div className="Preferences__one-third-flow">SFU URL</div>
<div className="Preferences__two-thirds-flow Preferences__two-thirds-flow--justify-end">
<AutoSizeTextArea
i18n={i18n}
value={sfuUrl}
onChange={handleSfuUrlUpdate}
placeholder="https://sfu.voip.signal.org"
moduleClassName="Preferences__ReadonlySqlPlayground__Textarea"
/>
</div>
</FlowingSettingsControl>
</SettingsRow>
</div> </div>
); );
} }

View File

@@ -184,6 +184,9 @@ import {
isCallFailure, isCallFailure,
shouldShowCallQualitySurvey, shouldShowCallQualitySurvey,
} from '../util/callQualitySurvey.dom.js'; } from '../util/callQualitySurvey.dom.js';
import * as RemoteConfig from '../RemoteConfig.dom.js';
import { isAlpha, isBeta, isProduction } from '../util/version.std.js';
import { parseIntOrThrow } from '../util/parseIntOrThrow.std.js';
const { i18n } = window.SignalContext; const { i18n } = window.SignalContext;
@@ -572,8 +575,11 @@ export class CallingClass {
#localPreviewContainer: HTMLDivElement | undefined; #localPreviewContainer: HTMLDivElement | undefined;
#localPreview: HTMLVideoElement | undefined; #localPreview: HTMLVideoElement | undefined;
#reduxInterface?: CallingReduxInterface; #reduxInterface?: CallingReduxInterface;
#_sfuUrl?: string;
public _sfuUrl?: string; public get sfuUrl(): string | undefined {
return itemStorage.get('sfuUrl') ?? this.#_sfuUrl;
}
public _iceServerOverride?: GetIceServersResultType | string; public _iceServerOverride?: GetIceServersResultType | string;
@@ -606,7 +612,7 @@ export class CallingClass {
throw new Error('CallingClass.initialize: Invalid uxActions.'); throw new Error('CallingClass.initialize: Invalid uxActions.');
} }
this._sfuUrl = sfuUrl; this.#_sfuUrl = sfuUrl;
RingRTC.setConfig({ RingRTC.setConfig({
field_trials: undefined, field_trials: undefined,
@@ -851,11 +857,11 @@ export class CallingClass {
async createCallLink(): Promise<CallLinkType> { async createCallLink(): Promise<CallLinkType> {
strictAssert( strictAssert(
this._sfuUrl, this.sfuUrl,
'createCallLink() missing SFU URL; not creating call link' 'createCallLink() missing SFU URL; not creating call link'
); );
const sfuUrl = this._sfuUrl; const { sfuUrl } = this;
const userId = Aci.parseFromServiceIdString( const userId = Aci.parseFromServiceIdString(
itemStorage.user.getCheckedAci() itemStorage.user.getCheckedAci()
); );
@@ -936,11 +942,11 @@ export class CallingClass {
async deleteCallLink(callLink: CallLinkType): Promise<void> { async deleteCallLink(callLink: CallLinkType): Promise<void> {
strictAssert( strictAssert(
this._sfuUrl, this.sfuUrl,
'createCallLink() missing SFU URL; not deleting call link' 'createCallLink() missing SFU URL; not deleting call link'
); );
const sfuUrl = this._sfuUrl; const { sfuUrl } = this;
const logId = `deleteCallLink(${callLink.roomId})`; const logId = `deleteCallLink(${callLink.roomId})`;
log.info(logId); log.info(logId);
@@ -973,10 +979,10 @@ export class CallingClass {
name: string name: string
): Promise<CallLinkStateType> { ): Promise<CallLinkStateType> {
strictAssert( strictAssert(
this._sfuUrl, this.sfuUrl,
'updateCallLinkName() missing SFU URL; not update call link name' 'updateCallLinkName() missing SFU URL; not update call link name'
); );
const sfuUrl = this._sfuUrl; const { sfuUrl } = this;
const logId = `updateCallLinkName(${callLink.roomId})`; const logId = `updateCallLinkName(${callLink.roomId})`;
log.info(`${logId}: Updating call link name`); log.info(`${logId}: Updating call link name`);
@@ -1011,10 +1017,10 @@ export class CallingClass {
restrictions: CallLinkRestrictions restrictions: CallLinkRestrictions
): Promise<CallLinkStateType> { ): Promise<CallLinkStateType> {
strictAssert( strictAssert(
this._sfuUrl, this.sfuUrl,
'updateCallLinkRestrictions() missing SFU URL; not update call link restrictions' 'updateCallLinkRestrictions() missing SFU URL; not update call link restrictions'
); );
const sfuUrl = this._sfuUrl; const { sfuUrl } = this;
const logId = `updateCallLinkRestrictions(${callLink.roomId})`; const logId = `updateCallLinkRestrictions(${callLink.roomId})`;
log.info(`${logId}: Updating call link restrictions`); log.info(`${logId}: Updating call link restrictions`);
@@ -1054,7 +1060,7 @@ export class CallingClass {
async readCallLink( async readCallLink(
callLinkRootKey: CallLinkRootKey callLinkRootKey: CallLinkRootKey
): Promise<CallLinkStateType | null> { ): Promise<CallLinkStateType | null> {
if (!this._sfuUrl) { if (!this.sfuUrl) {
throw new Error('readCallLink() missing SFU URL; not handling call link'); throw new Error('readCallLink() missing SFU URL; not handling call link');
} }
@@ -1066,7 +1072,7 @@ export class CallingClass {
await getCallLinkAuthCredentialPresentation(callLinkRootKey); await getCallLinkAuthCredentialPresentation(callLinkRootKey);
const result = await RingRTC.readCallLink( const result = await RingRTC.readCallLink(
this._sfuUrl, this.sfuUrl,
authCredentialPresentation.serialize(), authCredentialPresentation.serialize(),
callLinkRootKey callLinkRootKey
); );
@@ -1315,7 +1321,7 @@ export class CallingClass {
return statefulPeekInfo; return statefulPeekInfo;
} }
if (!this._sfuUrl) { if (!this.sfuUrl) {
throw new Error('Missing SFU URL; not peeking group call'); throw new Error('Missing SFU URL; not peeking group call');
} }
@@ -1338,7 +1344,7 @@ export class CallingClass {
const membershipProof = Bytes.fromString(proof); const membershipProof = Bytes.fromString(proof);
return RingRTC.peekGroupCall( return RingRTC.peekGroupCall(
this._sfuUrl, this.sfuUrl,
membershipProof, membershipProof,
this.#getGroupCallMembers(conversationId) this.#getGroupCallMembers(conversationId)
); );
@@ -1360,7 +1366,7 @@ export class CallingClass {
); );
} }
if (!this._sfuUrl) { if (!this.sfuUrl) {
throw new Error('Missing SFU URL; not peeking call link call'); throw new Error('Missing SFU URL; not peeking call link call');
} }
@@ -1369,7 +1375,7 @@ export class CallingClass {
await getCallLinkAuthCredentialPresentation(callLinkRootKey); await getCallLinkAuthCredentialPresentation(callLinkRootKey);
const result = await RingRTC.peekCallLinkCall( const result = await RingRTC.peekCallLinkCall(
this._sfuUrl, this.sfuUrl,
authCredentialPresentation.serialize(), authCredentialPresentation.serialize(),
callLinkRootKey callLinkRootKey
); );
@@ -1412,7 +1418,7 @@ export class CallingClass {
return existing; return existing;
} }
if (!this._sfuUrl) { if (!this.sfuUrl) {
throw new Error('Missing SFU URL; not connecting group call'); throw new Error('Missing SFU URL; not connecting group call');
} }
@@ -1420,16 +1426,16 @@ export class CallingClass {
log.info(logId); log.info(logId);
const groupIdBuffer = Bytes.fromBase64(groupId); const groupIdBuffer = Bytes.fromBase64(groupId);
const dredDuration = 0;
let isRequestingMembershipProof = false; let isRequestingMembershipProof = false;
const config = this.#getRemoteAndOverrideConfigValues();
const outerGroupCall = RingRTC.getGroupCall( const outerGroupCall = RingRTC.getGroupCall(
groupIdBuffer, groupIdBuffer,
this._sfuUrl, this.sfuUrl,
new Uint8Array(), new Uint8Array(),
AUDIO_LEVEL_INTERVAL_MS, AUDIO_LEVEL_INTERVAL_MS,
dredDuration, config.dredDuration,
{ {
...this.#getGroupCallObserver(conversationId, CallMode.Group), ...this.#getGroupCallObserver(conversationId, CallMode.Group),
async requestMembershipProof(groupCall) { async requestMembershipProof(groupCall) {
@@ -1496,23 +1502,22 @@ export class CallingClass {
const logId = `connectCallLinkCall(${roomId}`; const logId = `connectCallLinkCall(${roomId}`;
log.info(logId); log.info(logId);
if (!this._sfuUrl) { if (!this.sfuUrl) {
throw new Error( throw new Error(
`${logId}: Missing SFU URL; not connecting group call link call` `${logId}: Missing SFU URL; not connecting group call link call`
); );
} }
const config = this.#getRemoteAndOverrideConfigValues();
const dredDuration = 0;
const outerGroupCall = RingRTC.getCallLinkCall( const outerGroupCall = RingRTC.getCallLinkCall(
this._sfuUrl, this.sfuUrl,
endorsementsPublicKey, endorsementsPublicKey,
authCredentialPresentation.serialize(), authCredentialPresentation.serialize(),
callLinkRootKey, callLinkRootKey,
adminPasskey, adminPasskey,
new Uint8Array(), new Uint8Array(),
AUDIO_LEVEL_INTERVAL_MS, AUDIO_LEVEL_INTERVAL_MS,
dredDuration, config.dredDuration,
this.#getGroupCallObserver(roomId, CallMode.Adhoc) this.#getGroupCallObserver(roomId, CallMode.Adhoc)
); );
@@ -3863,6 +3868,59 @@ export class CallingClass {
return null; return null;
} }
#getRemoteAndOverrideConfigValues(): {
dredDuration: number | undefined;
isDirectVp9Enabled: boolean | undefined;
directMaxBitrate: number | undefined;
isGroupVp9Enabled: boolean | undefined;
groupMaxBitrate: number | undefined;
} {
function dredDuration(version: string): number | undefined {
const override = itemStorage.get('dredDuration');
if (override) {
return override;
}
if (isProduction(version)) {
return tryParseInt(
RemoteConfig.getValue('desktop.calling.dredDuration.prod')
);
}
if (isBeta(version)) {
return tryParseInt(
RemoteConfig.getValue('desktop.calling.dredDuration.beta')
);
}
if (isAlpha(version)) {
return tryParseInt(
RemoteConfig.getValue('desktop.calling.dredDuration.alpha')
);
}
return undefined;
}
function tryParseInt(v: string | undefined): number | undefined {
try {
return parseIntOrThrow(v, 'invalid');
} catch (e) {
return undefined;
}
}
const version = window.SignalContext.getVersion();
return {
dredDuration: dredDuration(version),
isDirectVp9Enabled: itemStorage.get('isDirectVp9Enabled'),
directMaxBitrate: itemStorage.get('directMaxBitrate'),
isGroupVp9Enabled: itemStorage.get('isGroupVp9Enabled'),
groupMaxBitrate: itemStorage.get('directMaxBitrate'),
};
}
async #getIceServers(): Promise<Array<IceServerType>> { async #getIceServers(): Promise<Array<IceServerType>> {
function iceServerConfigToList( function iceServerConfigToList(
iceServerConfig: GetIceServersResultType iceServerConfig: GetIceServersResultType
@@ -3964,6 +4022,7 @@ export class CallingClass {
} }
const iceServers = await this.#getIceServers(); const iceServers = await this.#getIceServers();
const config = this.#getRemoteAndOverrideConfigValues();
// We do this again, since getIceServers is a call that can take some time // We do this again, since getIceServers is a call that can take some time
if (call.endedReason) { if (call.endedReason) {
@@ -3984,7 +4043,7 @@ export class CallingClass {
hideIp: shouldRelayCalls || isContactUntrusted, hideIp: shouldRelayCalls || isContactUntrusted,
dataMode: DataMode.Normal, dataMode: DataMode.Normal,
audioLevelsIntervalMillis: AUDIO_LEVEL_INTERVAL_MS, audioLevelsIntervalMillis: AUDIO_LEVEL_INTERVAL_MS,
dredDuration: 0, dredDuration: config.dredDuration,
}; };
log.info('CallingClass.handleStartCall(): Proceeding'); log.info('CallingClass.handleStartCall(): Proceeding');

View File

@@ -784,6 +784,25 @@ export function SmartPreferences(): React.JSX.Element | null {
drop(itemStorage.put('cqsTestMode', value)); drop(itemStorage.put('cqsTestMode', value));
}, []); }, []);
const setDredDuration = useCallback((value: number | undefined) => {
drop(itemStorage.put('dredDuration', value));
}, []);
const setIsDirectVp9Enabled = useCallback((value: boolean | undefined) => {
drop(itemStorage.put('isDirectVp9Enabled', value));
}, []);
const setDirectMaxBitrate = useCallback((value: number | undefined) => {
drop(itemStorage.put('directMaxBitrate', value));
}, []);
const setIsGroupVp9Enabled = useCallback((value: boolean | undefined) => {
drop(itemStorage.put('isGroupVp9Enabled', value));
}, []);
const setGroupMaxBitrate = useCallback((value: number | undefined) => {
drop(itemStorage.put('groupMaxBitrate', value));
}, []);
const setSfuUrl = useCallback((value: string | undefined) => {
drop(itemStorage.put('sfuUrl', value));
}, []);
if (currentLocation.tab !== NavTab.Settings) { if (currentLocation.tab !== NavTab.Settings) {
return null; return null;
} }
@@ -998,6 +1017,18 @@ export function SmartPreferences(): React.JSX.Element | null {
} }
cqsTestMode={cqsTestMode} cqsTestMode={cqsTestMode}
setCqsTestMode={setCqsTestMode} setCqsTestMode={setCqsTestMode}
dredDuration={items.dredDuration}
setDredDuration={setDredDuration}
setIsDirectVp9Enabled={setIsDirectVp9Enabled}
isDirectVp9Enabled={items.isDirectVp9Enabled}
setDirectMaxBitrate={setDirectMaxBitrate}
directMaxBitrate={items.directMaxBitrate}
setIsGroupVp9Enabled={setIsGroupVp9Enabled}
isGroupVp9Enabled={items.isGroupVp9Enabled}
setGroupMaxBitrate={setGroupMaxBitrate}
groupMaxBitrate={items.groupMaxBitrate}
sfuUrl={items.sfuUrl}
setSfuUrl={setSfuUrl}
/> />
</AxoProvider> </AxoProvider>
</StrictMode> </StrictMode>

View File

@@ -291,6 +291,14 @@ export type StorageAccessType = {
defaultDimWallpaperInDarkMode: boolean; defaultDimWallpaperInDarkMode: boolean;
defaultAutoBubbleColor: boolean; defaultAutoBubbleColor: boolean;
// Used for manually controlling calling settings
dredDuration: number | undefined;
isDirectVp9Enabled: boolean | undefined;
directMaxBitrate: number | undefined;
isGroupVp9Enabled: boolean | undefined;
groupMaxBitrate: number | undefined;
sfuUrl: string | undefined;
// Deprecated // Deprecated
'challenge:retry-message-ids': never; 'challenge:retry-message-ids': never;
nextSignedKeyRotationTime: number; nextSignedKeyRotationTime: number;
@@ -514,6 +522,12 @@ const STORAGE_KEYS_TO_REMOVE_AFTER_UNLINK = [
'backupMediaDownloadIdle', 'backupMediaDownloadIdle',
'callQualitySurveyCooldownDisabled', 'callQualitySurveyCooldownDisabled',
'localDeleteWarningShown', 'localDeleteWarningShown',
'dredDuration',
'directMaxBitrate',
'isDirectVp9Enabled',
'groupMaxBitrate',
'isGroupVp9Enabled',
'sfuUrl',
] as const satisfies ReadonlyArray<keyof StorageAccessType>; ] as const satisfies ReadonlyArray<keyof StorageAccessType>;
// Ensure every storage key is explicitly marked to be preserved or removed on unlink. // Ensure every storage key is explicitly marked to be preserved or removed on unlink.

View File

@@ -87,7 +87,7 @@ if (
return message?.attributes; return message?.attributes;
}, },
getReduxState: () => window.reduxStore.getState(), getReduxState: () => window.reduxStore.getState(),
getSfuUrl: () => calling._sfuUrl, getSfuUrl: () => calling.sfuUrl,
getIceServerOverride: () => calling._iceServerOverride, getIceServerOverride: () => calling._iceServerOverride,
getSocketStatus: () => getSocketStatus(), getSocketStatus: () => getSocketStatus(),
getStorageItem: (name: keyof StorageAccessType) => itemStorage.get(name), getStorageItem: (name: keyof StorageAccessType) => itemStorage.get(name),
@@ -101,8 +101,8 @@ if (
} }
window.Flags[name] = value; window.Flags[name] = value;
}, },
setSfuUrl: (url: string) => { setSfuUrl: async (url: string) => {
calling._sfuUrl = url; await itemStorage.put('sfuUrl', url);
}, },
setIceServerOverride: ( setIceServerOverride: (
override: GetIceServersResultType | string | undefined override: GetIceServersResultType | string | undefined