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 = {
|
export const defaultSettings = {
|
||||||
Accounts_AllowEmailChange: {
|
Accounts_AllowEmailChange: {
|
||||||
type: 'valueAsBoolean'
|
type: 'valueAsBoolean'
|
||||||
|
@ -90,24 +117,6 @@ export const defaultSettings = {
|
||||||
Livechat_request_comment_when_closing_conversation: {
|
Livechat_request_comment_when_closing_conversation: {
|
||||||
type: 'valueAsBoolean'
|
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: {
|
Message_AllowDeleting: {
|
||||||
type: 'valueAsBoolean'
|
type: 'valueAsBoolean'
|
||||||
},
|
},
|
||||||
|
@ -198,12 +207,6 @@ export const defaultSettings = {
|
||||||
Accounts_AllowInvisibleStatusOption: {
|
Accounts_AllowInvisibleStatusOption: {
|
||||||
type: 'valueAsString'
|
type: 'valueAsString'
|
||||||
},
|
},
|
||||||
Jitsi_Enable_Teams: {
|
|
||||||
type: 'valueAsBoolean'
|
|
||||||
},
|
|
||||||
Jitsi_Enable_Channels: {
|
|
||||||
type: 'valuesAsBoolean'
|
|
||||||
},
|
|
||||||
Canned_Responses_Enable: {
|
Canned_Responses_Enable: {
|
||||||
type: 'valueAsBoolean'
|
type: 'valueAsBoolean'
|
||||||
},
|
},
|
||||||
|
@ -236,5 +239,9 @@ export const defaultSettings = {
|
||||||
},
|
},
|
||||||
Presence_broadcast_disabled: {
|
Presence_broadcast_disabled: {
|
||||||
type: 'valueAsBoolean'
|
type: 'valueAsBoolean'
|
||||||
}
|
},
|
||||||
|
Omnichannel_call_provider: {
|
||||||
|
type: 'valueAsBoolean'
|
||||||
|
},
|
||||||
|
...deprecatedSettings
|
||||||
} as const;
|
} 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 { Camera } from 'expo-camera';
|
||||||
import React, { useEffect, useState } from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import { useActionSheet } from '../../../containers/ActionSheet';
|
import { useActionSheet } from '../../../containers/ActionSheet';
|
||||||
import { SubscriptionType } from '../../../definitions';
|
|
||||||
import i18n from '../../../i18n';
|
import i18n from '../../../i18n';
|
||||||
import { getUserSelector } from '../../../selectors/login';
|
import { getUserSelector } from '../../../selectors/login';
|
||||||
import { getSubscriptionByRoomId } from '../../database/services/Subscription';
|
|
||||||
import { compareServerVersion, showErrorAlert } from '../../methods/helpers';
|
import { compareServerVersion, showErrorAlert } from '../../methods/helpers';
|
||||||
import { handleAndroidBltPermission } from '../../methods/videoConf';
|
import { handleAndroidBltPermission } from '../../methods/videoConf';
|
||||||
import { Services } from '../../services';
|
import { Services } from '../../services';
|
||||||
import { useAppSelector } from '../useAppSelector';
|
import { useAppSelector } from '../useAppSelector';
|
||||||
import { useSnaps } from '../useSnaps';
|
import { useSnaps } from '../useSnaps';
|
||||||
import StartACallActionSheet from './StartACallActionSheet';
|
import StartACallActionSheet from './StartACallActionSheet';
|
||||||
|
import { useVideoConfCall } from './useVideoConfCall';
|
||||||
|
|
||||||
const availabilityErrors = {
|
const availabilityErrors = {
|
||||||
NOT_CONFIGURED: 'video-conf-provider-not-configured',
|
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`));
|
return showErrorAlert(i18n.t(`${error}-body`), i18n.t(`${error}-header`));
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useVideoConf = (rid: string): { showInitCallActionSheet: () => Promise<void>; showCallOption: boolean } => {
|
export const useVideoConf = (
|
||||||
const [showCallOption, setShowCallOption] = useState(false);
|
rid: string
|
||||||
|
): { showInitCallActionSheet: () => Promise<void>; callEnabled: boolean; disabledTooltip?: boolean } => {
|
||||||
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);
|
|
||||||
const user = useAppSelector(state => getUserSelector(state));
|
const user = useAppSelector(state => getUserSelector(state));
|
||||||
|
const serverVersion = useAppSelector(state => state.server.version);
|
||||||
|
|
||||||
|
const { callEnabled, disabledTooltip } = useVideoConfCall(rid);
|
||||||
|
|
||||||
const [permission, requestPermission] = Camera.useCameraPermissions();
|
const [permission, requestPermission] = Camera.useCameraPermissions();
|
||||||
|
|
||||||
const isServer5OrNewer = compareServerVersion(serverVersion, 'greaterThanOrEqualTo', '5.0.0');
|
|
||||||
|
|
||||||
const { showActionSheet } = useActionSheet();
|
const { showActionSheet } = useActionSheet();
|
||||||
const snaps = useSnaps(404);
|
const snaps = useSnaps(404);
|
||||||
|
|
||||||
const handleShowCallOption = async () => {
|
const isServer5OrNewer = compareServerVersion(serverVersion, 'greaterThanOrEqualTo', '5.0.0');
|
||||||
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 canInitAnCall = async () => {
|
const canInitAnCall = async () => {
|
||||||
if (isServer5OrNewer) {
|
if (callEnabled) {
|
||||||
try {
|
if (isServer5OrNewer) {
|
||||||
await Services.videoConferenceGetCapabilities();
|
try {
|
||||||
return true;
|
await Services.videoConferenceGetCapabilities();
|
||||||
} catch (error: any) {
|
return true;
|
||||||
const isAdmin = !!['admin'].find(role => user.roles?.includes(role));
|
} catch (error: any) {
|
||||||
switch (error?.error) {
|
const isAdmin = !!user.roles?.includes('admin');
|
||||||
case availabilityErrors.NOT_CONFIGURED:
|
switch (error?.error) {
|
||||||
return handleErrors(isAdmin, availabilityErrors.NOT_CONFIGURED);
|
case availabilityErrors.NOT_CONFIGURED:
|
||||||
case availabilityErrors.NOT_ACTIVE:
|
return handleErrors(isAdmin, availabilityErrors.NOT_CONFIGURED);
|
||||||
return handleErrors(isAdmin, availabilityErrors.NOT_ACTIVE);
|
case availabilityErrors.NOT_ACTIVE:
|
||||||
case availabilityErrors.NO_APP:
|
return handleErrors(isAdmin, availabilityErrors.NOT_ACTIVE);
|
||||||
return handleErrors(isAdmin, availabilityErrors.NO_APP);
|
case availabilityErrors.NO_APP:
|
||||||
default:
|
return handleErrors(isAdmin, availabilityErrors.NO_APP);
|
||||||
return handleErrors(isAdmin, availabilityErrors.NOT_CONFIGURED);
|
default:
|
||||||
|
return handleErrors(isAdmin, availabilityErrors.NOT_CONFIGURED);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
return true;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
const showInitCallActionSheet = async () => {
|
const showInitCallActionSheet = async () => {
|
||||||
|
@ -92,9 +77,5 @@ export const useVideoConf = (rid: string): { showInitCallActionSheet: () => Prom
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
return { showInitCallActionSheet, callEnabled, disabledTooltip };
|
||||||
handleShowCallOption();
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return { showInitCallActionSheet, showCallOption };
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -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',
|
'edit-livechat-room-customfields',
|
||||||
'view-canned-responses',
|
'view-canned-responses',
|
||||||
'mobile-upload-file',
|
'mobile-upload-file',
|
||||||
'delete-own-message'
|
'delete-own-message',
|
||||||
|
'call-management'
|
||||||
] as const;
|
] as const;
|
||||||
|
|
||||||
export async function setPermissions(): Promise<void> {
|
export async function setPermissions(): Promise<void> {
|
||||||
|
|
|
@ -37,3 +37,4 @@ export * from './crashReport';
|
||||||
export * from './parseSettings';
|
export * from './parseSettings';
|
||||||
export * from './subscribeRooms';
|
export * from './subscribeRooms';
|
||||||
export * from './serializeAsciiUrl';
|
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';
|
import { useVideoConf } from '../../../lib/hooks/useVideoConf';
|
||||||
|
|
||||||
export default function CallSection({ rid }: { rid: string }): React.ReactElement | null {
|
export default function CallSection({ rid }: { rid: string }): React.ReactElement | null {
|
||||||
const { showCallOption, showInitCallActionSheet } = useVideoConf(rid);
|
const { callEnabled, showInitCallActionSheet, disabledTooltip } = useVideoConf(rid);
|
||||||
|
if (callEnabled)
|
||||||
if (showCallOption)
|
|
||||||
return (
|
return (
|
||||||
<List.Section>
|
<List.Section>
|
||||||
<List.Separator />
|
<List.Separator />
|
||||||
|
@ -16,6 +15,7 @@ export default function CallSection({ rid }: { rid: string }): React.ReactElemen
|
||||||
testID='room-actions-call'
|
testID='room-actions-call'
|
||||||
left={() => <List.Icon name='phone' />}
|
left={() => <List.Icon name='phone' />}
|
||||||
showActionIndicator
|
showActionIndicator
|
||||||
|
disabled={disabledTooltip}
|
||||||
/>
|
/>
|
||||||
<List.Separator />
|
<List.Separator />
|
||||||
</List.Section>
|
</List.Section>
|
||||||
|
|
|
@ -16,20 +16,22 @@ function UserInfoButton({
|
||||||
iconName,
|
iconName,
|
||||||
onPress,
|
onPress,
|
||||||
label,
|
label,
|
||||||
showIcon
|
showIcon,
|
||||||
|
enabled = true
|
||||||
}: {
|
}: {
|
||||||
danger?: boolean;
|
danger?: boolean;
|
||||||
iconName: TIconsName;
|
iconName: TIconsName;
|
||||||
onPress?: (prop: any) => void;
|
onPress?: (prop: any) => void;
|
||||||
label: string;
|
label: string;
|
||||||
showIcon?: boolean;
|
showIcon?: boolean;
|
||||||
|
enabled?: boolean;
|
||||||
}): React.ReactElement | null {
|
}): React.ReactElement | null {
|
||||||
const { colors } = useTheme();
|
const { colors } = useTheme();
|
||||||
const color = danger ? colors.dangerColor : colors.actionTintColor;
|
let color = danger ? colors.dangerColor : colors.actionTintColor;
|
||||||
|
if (!enabled) color = colors.auxiliaryText;
|
||||||
if (showIcon)
|
if (showIcon)
|
||||||
return (
|
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} />
|
<CustomIcon name={iconName} size={30} color={color} />
|
||||||
<Text style={[styles.roomButtonText, { color }]}>{label}</Text>
|
<Text style={[styles.roomButtonText, { color }]}>{label}</Text>
|
||||||
</BorderlessButton>
|
</BorderlessButton>
|
||||||
|
@ -38,11 +40,19 @@ function UserInfoButton({
|
||||||
}
|
}
|
||||||
|
|
||||||
export function CallButton({ rid, isDirect }: { rid: string; isDirect: boolean }): React.ReactElement | null {
|
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 serverVersion = useAppSelector(state => state.server.version);
|
||||||
const greaterThanFive = compareServerVersion(serverVersion, 'greaterThanOrEqualTo', '5.0.0');
|
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';
|
import { useVideoConf } from '../../../lib/hooks/useVideoConf';
|
||||||
|
|
||||||
export default function HeaderCallButton({ rid }: { rid: string }): React.ReactElement | null {
|
export default function HeaderCallButton({ rid }: { rid: string }): React.ReactElement | null {
|
||||||
const { showInitCallActionSheet, showCallOption } = useVideoConf(rid);
|
const { showInitCallActionSheet, callEnabled, disabledTooltip } = useVideoConf(rid);
|
||||||
|
|
||||||
if (showCallOption)
|
if (callEnabled)
|
||||||
return <HeaderButton.Item iconName='phone' onPress={showInitCallActionSheet} testID='room-view-header-call' />;
|
return (
|
||||||
|
<HeaderButton.Item
|
||||||
|
disabled={disabledTooltip}
|
||||||
|
iconName='phone'
|
||||||
|
onPress={showInitCallActionSheet}
|
||||||
|
testID='room-view-header-call'
|
||||||
|
/>
|
||||||
|
);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue