feat: align mobile videoConf settings with web settings (#5136)
* add new videoConf settings * add call-management permission * create useSetting hook * create useVideoConfCall hook * use new videoconf settings * change isAdmin check * ignore older versions
This commit is contained in:
parent
1de24befe5
commit
25c3f46750
|
@ -1,4 +1,31 @@
|
|||
// 🚨🚨 48 settings after login. Pay attention not to reach 50 as that's the limit per request.
|
||||
// DEPRECATED: This settings are deprecated and will be removed in the LTS only support
|
||||
const deprecatedSettings = {
|
||||
Jitsi_Enable_Teams: {
|
||||
type: 'valueAsBoolean'
|
||||
},
|
||||
Jitsi_Enable_Channels: {
|
||||
type: 'valuesAsBoolean'
|
||||
},
|
||||
Jitsi_Enabled: {
|
||||
type: 'valueAsBoolean'
|
||||
},
|
||||
Jitsi_SSL: {
|
||||
type: 'valueAsBoolean'
|
||||
},
|
||||
Jitsi_Domain: {
|
||||
type: 'valueAsString'
|
||||
},
|
||||
Jitsi_Enabled_TokenAuth: {
|
||||
type: 'valueAsBoolean'
|
||||
},
|
||||
Jitsi_URL_Room_Hash: {
|
||||
type: 'valueAsBoolean'
|
||||
},
|
||||
Jitsi_URL_Room_Prefix: {
|
||||
type: 'valueAsString'
|
||||
}
|
||||
};
|
||||
|
||||
export const defaultSettings = {
|
||||
Accounts_AllowEmailChange: {
|
||||
type: 'valueAsBoolean'
|
||||
|
@ -90,24 +117,6 @@ export const defaultSettings = {
|
|||
Livechat_request_comment_when_closing_conversation: {
|
||||
type: 'valueAsBoolean'
|
||||
},
|
||||
Jitsi_Enabled: {
|
||||
type: 'valueAsBoolean'
|
||||
},
|
||||
Jitsi_SSL: {
|
||||
type: 'valueAsBoolean'
|
||||
},
|
||||
Jitsi_Domain: {
|
||||
type: 'valueAsString'
|
||||
},
|
||||
Jitsi_Enabled_TokenAuth: {
|
||||
type: 'valueAsBoolean'
|
||||
},
|
||||
Jitsi_URL_Room_Hash: {
|
||||
type: 'valueAsBoolean'
|
||||
},
|
||||
Jitsi_URL_Room_Prefix: {
|
||||
type: 'valueAsString'
|
||||
},
|
||||
Message_AllowDeleting: {
|
||||
type: 'valueAsBoolean'
|
||||
},
|
||||
|
@ -198,12 +207,6 @@ export const defaultSettings = {
|
|||
Accounts_AllowInvisibleStatusOption: {
|
||||
type: 'valueAsString'
|
||||
},
|
||||
Jitsi_Enable_Teams: {
|
||||
type: 'valueAsBoolean'
|
||||
},
|
||||
Jitsi_Enable_Channels: {
|
||||
type: 'valuesAsBoolean'
|
||||
},
|
||||
Canned_Responses_Enable: {
|
||||
type: 'valueAsBoolean'
|
||||
},
|
||||
|
@ -236,5 +239,9 @@ export const defaultSettings = {
|
|||
},
|
||||
Presence_broadcast_disabled: {
|
||||
type: 'valueAsBoolean'
|
||||
}
|
||||
},
|
||||
Omnichannel_call_provider: {
|
||||
type: 'valueAsBoolean'
|
||||
},
|
||||
...deprecatedSettings
|
||||
} as const;
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
import { TSettingsValues, TSupportedSettings } from '../../reducers/settings';
|
||||
import { useAppSelector } from './useAppSelector';
|
||||
|
||||
export function useSetting(key: TSupportedSettings): TSettingsValues {
|
||||
return useAppSelector(state => state.settings[key]) as TSettingsValues;
|
||||
}
|
|
@ -1,17 +1,16 @@
|
|||
import { Camera } from 'expo-camera';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import React from 'react';
|
||||
|
||||
import { useActionSheet } from '../../../containers/ActionSheet';
|
||||
import { SubscriptionType } from '../../../definitions';
|
||||
import i18n from '../../../i18n';
|
||||
import { getUserSelector } from '../../../selectors/login';
|
||||
import { getSubscriptionByRoomId } from '../../database/services/Subscription';
|
||||
import { compareServerVersion, showErrorAlert } from '../../methods/helpers';
|
||||
import { handleAndroidBltPermission } from '../../methods/videoConf';
|
||||
import { Services } from '../../services';
|
||||
import { useAppSelector } from '../useAppSelector';
|
||||
import { useSnaps } from '../useSnaps';
|
||||
import StartACallActionSheet from './StartACallActionSheet';
|
||||
import { useVideoConfCall } from './useVideoConfCall';
|
||||
|
||||
const availabilityErrors = {
|
||||
NOT_CONFIGURED: 'video-conf-provider-not-configured',
|
||||
|
@ -24,58 +23,44 @@ const handleErrors = (isAdmin: boolean, error: typeof availabilityErrors[keyof t
|
|||
return showErrorAlert(i18n.t(`${error}-body`), i18n.t(`${error}-header`));
|
||||
};
|
||||
|
||||
export const useVideoConf = (rid: string): { showInitCallActionSheet: () => Promise<void>; showCallOption: boolean } => {
|
||||
const [showCallOption, setShowCallOption] = useState(false);
|
||||
|
||||
const serverVersion = useAppSelector(state => state.server.version);
|
||||
const jitsiEnabled = useAppSelector(state => state.settings.Jitsi_Enabled);
|
||||
const jitsiEnableTeams = useAppSelector(state => state.settings.Jitsi_Enable_Teams);
|
||||
const jitsiEnableChannels = useAppSelector(state => state.settings.Jitsi_Enable_Channels);
|
||||
export const useVideoConf = (
|
||||
rid: string
|
||||
): { showInitCallActionSheet: () => Promise<void>; callEnabled: boolean; disabledTooltip?: boolean } => {
|
||||
const user = useAppSelector(state => getUserSelector(state));
|
||||
const serverVersion = useAppSelector(state => state.server.version);
|
||||
|
||||
const { callEnabled, disabledTooltip } = useVideoConfCall(rid);
|
||||
|
||||
const [permission, requestPermission] = Camera.useCameraPermissions();
|
||||
|
||||
const isServer5OrNewer = compareServerVersion(serverVersion, 'greaterThanOrEqualTo', '5.0.0');
|
||||
|
||||
const { showActionSheet } = useActionSheet();
|
||||
const snaps = useSnaps(404);
|
||||
|
||||
const handleShowCallOption = async () => {
|
||||
if (isServer5OrNewer) return setShowCallOption(true);
|
||||
const room = await getSubscriptionByRoomId(rid);
|
||||
|
||||
if (room) {
|
||||
const isJitsiDisabledForTeams = room.teamMain && !jitsiEnableTeams;
|
||||
const isJitsiDisabledForChannels = !room.teamMain && (room.t === 'p' || room.t === 'c') && !jitsiEnableChannels;
|
||||
|
||||
if (room.t === SubscriptionType.DIRECT) return setShowCallOption(!!jitsiEnabled);
|
||||
if (room.t === SubscriptionType.CHANNEL) return setShowCallOption(!isJitsiDisabledForChannels);
|
||||
if (room.t === SubscriptionType.GROUP) return setShowCallOption(!isJitsiDisabledForTeams);
|
||||
}
|
||||
|
||||
return setShowCallOption(false);
|
||||
};
|
||||
const isServer5OrNewer = compareServerVersion(serverVersion, 'greaterThanOrEqualTo', '5.0.0');
|
||||
|
||||
const canInitAnCall = async () => {
|
||||
if (isServer5OrNewer) {
|
||||
try {
|
||||
await Services.videoConferenceGetCapabilities();
|
||||
return true;
|
||||
} catch (error: any) {
|
||||
const isAdmin = !!['admin'].find(role => user.roles?.includes(role));
|
||||
switch (error?.error) {
|
||||
case availabilityErrors.NOT_CONFIGURED:
|
||||
return handleErrors(isAdmin, availabilityErrors.NOT_CONFIGURED);
|
||||
case availabilityErrors.NOT_ACTIVE:
|
||||
return handleErrors(isAdmin, availabilityErrors.NOT_ACTIVE);
|
||||
case availabilityErrors.NO_APP:
|
||||
return handleErrors(isAdmin, availabilityErrors.NO_APP);
|
||||
default:
|
||||
return handleErrors(isAdmin, availabilityErrors.NOT_CONFIGURED);
|
||||
if (callEnabled) {
|
||||
if (isServer5OrNewer) {
|
||||
try {
|
||||
await Services.videoConferenceGetCapabilities();
|
||||
return true;
|
||||
} catch (error: any) {
|
||||
const isAdmin = !!user.roles?.includes('admin');
|
||||
switch (error?.error) {
|
||||
case availabilityErrors.NOT_CONFIGURED:
|
||||
return handleErrors(isAdmin, availabilityErrors.NOT_CONFIGURED);
|
||||
case availabilityErrors.NOT_ACTIVE:
|
||||
return handleErrors(isAdmin, availabilityErrors.NOT_ACTIVE);
|
||||
case availabilityErrors.NO_APP:
|
||||
return handleErrors(isAdmin, availabilityErrors.NO_APP);
|
||||
default:
|
||||
return handleErrors(isAdmin, availabilityErrors.NOT_CONFIGURED);
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
return false;
|
||||
};
|
||||
|
||||
const showInitCallActionSheet = async () => {
|
||||
|
@ -92,9 +77,5 @@ export const useVideoConf = (rid: string): { showInitCallActionSheet: () => Prom
|
|||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
handleShowCallOption();
|
||||
}, []);
|
||||
|
||||
return { showInitCallActionSheet, showCallOption };
|
||||
return { showInitCallActionSheet, callEnabled, disabledTooltip };
|
||||
};
|
||||
|
|
|
@ -0,0 +1,68 @@
|
|||
import { useEffect, useState } from 'react';
|
||||
|
||||
import { SubscriptionType } from '../../../definitions';
|
||||
import { getUserSelector } from '../../../selectors/login';
|
||||
import { getSubscriptionByRoomId } from '../../database/services/Subscription';
|
||||
import { compareServerVersion, isReadOnly } from '../../methods/helpers';
|
||||
import { useAppSelector } from '../useAppSelector';
|
||||
import { usePermissions } from '../usePermissions';
|
||||
import { useSetting } from '../useSetting';
|
||||
import { isRoomFederated } from '../../methods';
|
||||
|
||||
export const useVideoConfCall = (rid: string): { callEnabled: boolean; disabledTooltip?: boolean } => {
|
||||
const [callEnabled, setCallEnabled] = useState(false);
|
||||
const [disabledTooltip, setDisabledTooltip] = useState(false);
|
||||
|
||||
// OLD SETTINGS
|
||||
const jitsiEnabled = useSetting('Jitsi_Enabled');
|
||||
const jitsiEnableTeams = useSetting('Jitsi_Enable_Teams');
|
||||
const jitsiEnableChannels = useSetting('Jitsi_Enable_Channels');
|
||||
|
||||
// NEW SETTINGS
|
||||
// Only disable video conf if the settings are explicitly FALSE - any falsy value counts as true
|
||||
const enabledDMs = useSetting('VideoConf_Enable_DMs') !== false;
|
||||
const enabledChannel = useSetting('VideoConf_Enable_Channels') !== false;
|
||||
const enabledTeams = useSetting('VideoConf_Enable_Teams') !== false;
|
||||
const enabledGroups = useSetting('VideoConf_Enable_Groups') !== false;
|
||||
const enabledLiveChat = useSetting('Omnichannel_call_provider') === 'default-provider';
|
||||
|
||||
const serverVersion = useAppSelector(state => state.server.version);
|
||||
const isServer5OrNewer = compareServerVersion(serverVersion, 'greaterThanOrEqualTo', '5.0.0');
|
||||
const [canStartCall] = usePermissions(['call-management'], rid);
|
||||
const user = useAppSelector(state => getUserSelector(state));
|
||||
|
||||
const init = async () => {
|
||||
const room = await getSubscriptionByRoomId(rid);
|
||||
if (room) {
|
||||
if (isServer5OrNewer) {
|
||||
const isReadyOnly = await isReadOnly(room, user.username);
|
||||
const ownUser = room.uids && room.uids.length === 1;
|
||||
const enabled = enabledDMs || enabledChannel || enabledTeams || enabledGroups || enabledLiveChat;
|
||||
const enableOption = enabled && canStartCall && (!user?.username || !room.muted?.includes(user.username));
|
||||
const federated = isRoomFederated(room);
|
||||
|
||||
if (enableOption && !ownUser) {
|
||||
if (federated || (room.ro && isReadyOnly)) {
|
||||
setDisabledTooltip(true);
|
||||
}
|
||||
return setCallEnabled(true);
|
||||
}
|
||||
return;
|
||||
}
|
||||
// OLD SERVERS VERSIONS
|
||||
const isJitsiDisabledForTeams = room.teamMain && !jitsiEnableTeams;
|
||||
const isJitsiDisabledForChannels = !room.teamMain && (room.t === 'p' || room.t === 'c') && !jitsiEnableChannels;
|
||||
|
||||
if (room.t === SubscriptionType.DIRECT) return setCallEnabled(!!jitsiEnabled);
|
||||
if (room.t === SubscriptionType.CHANNEL) return setCallEnabled(!isJitsiDisabledForChannels);
|
||||
if (room.t === SubscriptionType.GROUP) return setCallEnabled(!isJitsiDisabledForTeams);
|
||||
}
|
||||
return setCallEnabled(false);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
init();
|
||||
}, []);
|
||||
|
||||
return { callEnabled, disabledTooltip };
|
||||
};
|
|
@ -58,7 +58,8 @@ export const SUPPORTED_PERMISSIONS = [
|
|||
'edit-livechat-room-customfields',
|
||||
'view-canned-responses',
|
||||
'mobile-upload-file',
|
||||
'delete-own-message'
|
||||
'delete-own-message',
|
||||
'call-management'
|
||||
] as const;
|
||||
|
||||
export async function setPermissions(): Promise<void> {
|
||||
|
|
|
@ -37,3 +37,4 @@ export * from './crashReport';
|
|||
export * from './parseSettings';
|
||||
export * from './subscribeRooms';
|
||||
export * from './serializeAsciiUrl';
|
||||
export * from './isRoomFederated';
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
import { ISubscription } from '../../definitions';
|
||||
|
||||
interface IRoomFederated extends ISubscription {
|
||||
federated: true;
|
||||
}
|
||||
|
||||
export const isRoomFederated = (room: ISubscription): room is IRoomFederated =>
|
||||
'federated' in room && (room as any).federated === true;
|
|
@ -4,9 +4,8 @@ import * as List from '../../../containers/List';
|
|||
import { useVideoConf } from '../../../lib/hooks/useVideoConf';
|
||||
|
||||
export default function CallSection({ rid }: { rid: string }): React.ReactElement | null {
|
||||
const { showCallOption, showInitCallActionSheet } = useVideoConf(rid);
|
||||
|
||||
if (showCallOption)
|
||||
const { callEnabled, showInitCallActionSheet, disabledTooltip } = useVideoConf(rid);
|
||||
if (callEnabled)
|
||||
return (
|
||||
<List.Section>
|
||||
<List.Separator />
|
||||
|
@ -16,6 +15,7 @@ export default function CallSection({ rid }: { rid: string }): React.ReactElemen
|
|||
testID='room-actions-call'
|
||||
left={() => <List.Icon name='phone' />}
|
||||
showActionIndicator
|
||||
disabled={disabledTooltip}
|
||||
/>
|
||||
<List.Separator />
|
||||
</List.Section>
|
||||
|
|
|
@ -16,20 +16,22 @@ function UserInfoButton({
|
|||
iconName,
|
||||
onPress,
|
||||
label,
|
||||
showIcon
|
||||
showIcon,
|
||||
enabled = true
|
||||
}: {
|
||||
danger?: boolean;
|
||||
iconName: TIconsName;
|
||||
onPress?: (prop: any) => void;
|
||||
label: string;
|
||||
showIcon?: boolean;
|
||||
enabled?: boolean;
|
||||
}): React.ReactElement | null {
|
||||
const { colors } = useTheme();
|
||||
const color = danger ? colors.dangerColor : colors.actionTintColor;
|
||||
|
||||
let color = danger ? colors.dangerColor : colors.actionTintColor;
|
||||
if (!enabled) color = colors.auxiliaryText;
|
||||
if (showIcon)
|
||||
return (
|
||||
<BorderlessButton testID={`room-info-view-${iconName}`} onPress={onPress} style={styles.roomButton}>
|
||||
<BorderlessButton enabled={enabled} testID={`room-info-view-${iconName}`} onPress={onPress} style={styles.roomButton}>
|
||||
<CustomIcon name={iconName} size={30} color={color} />
|
||||
<Text style={[styles.roomButtonText, { color }]}>{label}</Text>
|
||||
</BorderlessButton>
|
||||
|
@ -38,11 +40,19 @@ function UserInfoButton({
|
|||
}
|
||||
|
||||
export function CallButton({ rid, isDirect }: { rid: string; isDirect: boolean }): React.ReactElement | null {
|
||||
const { showCallOption, showInitCallActionSheet } = useVideoConf(rid);
|
||||
const { callEnabled, showInitCallActionSheet, disabledTooltip } = useVideoConf(rid);
|
||||
const serverVersion = useAppSelector(state => state.server.version);
|
||||
const greaterThanFive = compareServerVersion(serverVersion, 'greaterThanOrEqualTo', '5.0.0');
|
||||
|
||||
const showIcon = greaterThanFive ? showCallOption : showCallOption && isDirect;
|
||||
const showIcon = greaterThanFive ? callEnabled : callEnabled && isDirect;
|
||||
|
||||
return <UserInfoButton onPress={showInitCallActionSheet} iconName='phone' label={i18n.t('Call')} showIcon={showIcon} />;
|
||||
return (
|
||||
<UserInfoButton
|
||||
enabled={!disabledTooltip}
|
||||
onPress={showInitCallActionSheet}
|
||||
iconName='phone'
|
||||
label={i18n.t('Call')}
|
||||
showIcon={showIcon}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -4,9 +4,16 @@ import * as HeaderButton from '../../../containers/HeaderButton';
|
|||
import { useVideoConf } from '../../../lib/hooks/useVideoConf';
|
||||
|
||||
export default function HeaderCallButton({ rid }: { rid: string }): React.ReactElement | null {
|
||||
const { showInitCallActionSheet, showCallOption } = useVideoConf(rid);
|
||||
const { showInitCallActionSheet, callEnabled, disabledTooltip } = useVideoConf(rid);
|
||||
|
||||
if (showCallOption)
|
||||
return <HeaderButton.Item iconName='phone' onPress={showInitCallActionSheet} testID='room-view-header-call' />;
|
||||
if (callEnabled)
|
||||
return (
|
||||
<HeaderButton.Item
|
||||
disabled={disabledTooltip}
|
||||
iconName='phone'
|
||||
onPress={showInitCallActionSheet}
|
||||
testID='room-view-header-call'
|
||||
/>
|
||||
);
|
||||
return null;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue