diff --git a/lib/shared/keyserver-utils.js b/lib/shared/keyserver-utils.js index 36933b94d..acbcf876c 100644 --- a/lib/shared/keyserver-utils.js +++ b/lib/shared/keyserver-utils.js @@ -1,71 +1,67 @@ // @flow import * as React from 'react'; import { useGetVersion, getVersionActionTypes, } from '../actions/device-actions.js'; import { urlsToIDsSelector } from '../selectors/keyserver-selectors.js'; import type { VersionResponse } from '../types/device-types.js'; import { useDispatchActionPromise } from '../utils/redux-promise-utils.js'; import { useSelector } from '../utils/redux-utils.js'; function useIsKeyserverURLValid( keyserverURL?: string, -): () => Promise { +): () => Promise { const urlsToIDs: { +[keyserverID: string]: ?string } = useSelector(urlsToIDsSelector); const keyserverID: ?string = keyserverURL ? urlsToIDs[keyserverURL] : undefined; const keyserverInfo = useSelector(state => keyserverID ? state.keyserverStore.keyserverInfos[keyserverID] : undefined, ); const serverCallParamOverride = React.useMemo(() => { if (!keyserverURL) { return undefined; } if (keyserverInfo && keyserverID) { return { keyserverInfos: { [(keyserverID: string)]: keyserverInfo, }, }; } return { keyserverInfos: { [(keyserverURL: string)]: { urlPrefix: keyserverURL, }, }, }; }, [keyserverID, keyserverInfo, keyserverURL]); const getVersionCall = useGetVersion(serverCallParamOverride); const dispatchActionPromise = useDispatchActionPromise(); return React.useCallback(async () => { if (!keyserverURL) { - return null; + throw new Error('isKeyserverURLValid called with empty URL'); } const getVersionPromise = getVersionCall(); void dispatchActionPromise(getVersionActionTypes, getVersionPromise); - try { - const { versionResponses } = await getVersionPromise; - return versionResponses[Object.keys(versionResponses)[0]]; - } catch (e) { - return null; - } + const { versionResponses } = await getVersionPromise; + return versionResponses[Object.keys(versionResponses)[0]]; }, [dispatchActionPromise, getVersionCall, keyserverURL]); } export { useIsKeyserverURLValid }; diff --git a/native/profile/add-keyserver.react.js b/native/profile/add-keyserver.react.js index c9e94ff7a..dd8dc2a0a 100644 --- a/native/profile/add-keyserver.react.js +++ b/native/profile/add-keyserver.react.js @@ -1,170 +1,184 @@ // @flow import { useNavigation } from '@react-navigation/native'; import * as React from 'react'; import { View, Text } from 'react-native'; import { addKeyserverActionType } from 'lib/actions/keyserver-actions.js'; import { useIsKeyserverURLValid } from 'lib/shared/keyserver-utils.js'; import type { KeyserverInfo } from 'lib/types/keyserver-types.js'; import { defaultKeyserverInfo } from 'lib/types/keyserver-types.js'; +import { getMessageForException } from 'lib/utils/errors.js'; import { useDispatch } from 'lib/utils/redux-utils.js'; import type { ProfileNavigationProp } from './profile.react.js'; import TextInput from '../components/text-input.react.js'; import HeaderRightTextButton from '../navigation/header-right-text-button.react.js'; import type { NavigationRoute } from '../navigation/route-names.js'; import { useSelector } from '../redux/redux-utils.js'; import { useStyles, useColors } from '../themes/colors.js'; +import { AppOutOfDateAlertDetails } from '../utils/alert-messages.js'; import { useStaffCanSee } from '../utils/staff-utils.js'; type KeyserverCheckStatus = | { +status: 'inactive' } | { +status: 'loading' } - | { +status: 'error' }; + | { +status: 'error', +error: 'generic' | 'client_version_unsupported' }; const keyserverCheckStatusInactive = { status: 'inactive' }; const keyserverCheckStatusLoading = { status: 'loading' }; -const keyserverCheckStatusError = { status: 'error' }; +const keyserverCheckStatusGenericError = { status: 'error', error: 'generic' }; +const keyserverCheckStatusVersionError = { + status: 'error', + error: 'client_version_unsupported', +}; type Props = { +navigation: ProfileNavigationProp<'AddKeyserver'>, +route: NavigationRoute<'AddKeyserver'>, }; // eslint-disable-next-line no-unused-vars function AddKeyserver(props: Props): React.Node { const { goBack, setOptions } = useNavigation(); const dispatch = useDispatch(); const staffCanSee = useStaffCanSee(); const currentUserID = useSelector(state => state.currentUserInfo?.id); const customServer = useSelector(state => state.customServer); const { panelForegroundTertiaryLabel } = useColors(); const styles = useStyles(unboundStyles); const [urlInput, setUrlInput] = React.useState( customServer && staffCanSee ? customServer : '', ); const [status, setStatus] = React.useState( keyserverCheckStatusInactive, ); const isKeyserverURLValidCallback = useIsKeyserverURLValid(urlInput); const onPressSave = React.useCallback(async () => { if (!currentUserID || !urlInput) { return; } setStatus(keyserverCheckStatusLoading); - const keyserverVersionData = await isKeyserverURLValidCallback(); - if (!keyserverVersionData) { - setStatus(keyserverCheckStatusError); + let keyserverVersionData; + try { + keyserverVersionData = await isKeyserverURLValidCallback(); + } catch (e) { + const message = getMessageForException(e); + if (message === 'client_version_unsupported') { + setStatus(keyserverCheckStatusVersionError); + } else { + setStatus(keyserverCheckStatusGenericError); + } return; } setStatus(keyserverCheckStatusInactive); const newKeyserverInfo: KeyserverInfo = defaultKeyserverInfo(urlInput); dispatch({ type: addKeyserverActionType, payload: { keyserverAdminUserID: keyserverVersionData.ownerID, newKeyserverInfo, }, }); goBack(); }, [currentUserID, dispatch, goBack, isKeyserverURLValidCallback, urlInput]); const buttonDisabled = !urlInput || status.status === 'loading'; React.useEffect(() => { setOptions({ headerRight: () => ( ), }); }, [onPressSave, setOptions, buttonDisabled]); const onChangeText = React.useCallback( (text: string) => setUrlInput(text), [], ); - const showErrorMessage = status.status === 'error'; + const { error } = status; const errorMessage = React.useMemo(() => { - if (!showErrorMessage) { + let errorText; + if (error === 'client_version_unsupported') { + errorText = AppOutOfDateAlertDetails.message; + } else if (error) { + errorText = + 'Cannot connect to keyserver. Please check the URL or your ' + + 'and try again.'; + } else { return null; } - - return ( - - Cannot connect to keyserver. Please check the URL or your connection and - try again. - - ); - }, [showErrorMessage, styles.errorMessage]); + return {errorText}; + }, [error, styles.errorMessage]); return ( KEYSERVER URL {errorMessage} ); } const unboundStyles = { container: { paddingTop: 8, }, header: { color: 'panelBackgroundLabel', fontSize: 12, fontWeight: '400', paddingBottom: 3, paddingHorizontal: 24, }, inputContainer: { backgroundColor: 'panelForeground', flexDirection: 'row', justifyContent: 'space-between', paddingHorizontal: 24, paddingVertical: 12, borderBottomWidth: 1, borderColor: 'panelForegroundBorder', borderTopWidth: 1, }, input: { color: 'panelForegroundLabel', flex: 1, fontFamily: 'Arial', fontSize: 16, paddingVertical: 0, borderBottomColor: 'transparent', }, errorMessage: { marginTop: 8, marginHorizontal: 16, color: 'redText', }, }; export default AddKeyserver; diff --git a/web/modals/keyserver-selection/add-keyserver-modal.react.js b/web/modals/keyserver-selection/add-keyserver-modal.react.js index 4307b37f7..854dee610 100644 --- a/web/modals/keyserver-selection/add-keyserver-modal.react.js +++ b/web/modals/keyserver-selection/add-keyserver-modal.react.js @@ -1,143 +1,158 @@ // @flow import * as React from 'react'; import { addKeyserverActionType } from 'lib/actions/keyserver-actions.js'; import { useModalContext } from 'lib/components/modal-provider.react.js'; import { useIsKeyserverURLValid } from 'lib/shared/keyserver-utils.js'; import type { KeyserverInfo } from 'lib/types/keyserver-types.js'; import { defaultKeyserverInfo } from 'lib/types/keyserver-types.js'; +import { getMessageForException } from 'lib/utils/errors.js'; import { useDispatch } from 'lib/utils/redux-utils.js'; import css from './add-keyserver-modal.css'; import Button, { buttonThemes } from '../../components/button.react.js'; import { useSelector } from '../../redux/redux-utils.js'; import { useStaffCanSee } from '../../utils/staff-utils.js'; +import { getVersionUnsupportedError } from '../../utils/version-utils.js'; import Input from '../input.react.js'; import Modal from '../modal.react.js'; type KeyserverCheckStatus = | { +status: 'inactive' } | { +status: 'loading' } - | { +status: 'error' }; + | { +status: 'error', +error: 'generic' | 'client_version_unsupported' }; const keyserverCheckStatusInactive = { status: 'inactive' }; const keyserverCheckStatusLoading = { status: 'loading' }; -const keyserverCheckStatusError = { status: 'error' }; +const keyserverCheckStatusGenericError = { status: 'error', error: 'generic' }; +const keyserverCheckStatusVersionError = { + status: 'error', + error: 'client_version_unsupported', +}; function AddKeyserverModal(): React.Node { const { popModal } = useModalContext(); const dispatch = useDispatch(); const staffCanSee = useStaffCanSee(); const currentUserID = useSelector(state => state.currentUserInfo?.id); const customServer = useSelector(state => state.customServer); const [keyserverURL, setKeyserverURL] = React.useState( customServer && staffCanSee ? customServer : '', ); const [status, setStatus] = React.useState( keyserverCheckStatusInactive, ); const onChangeKeyserverURL = React.useCallback( (event: SyntheticEvent) => setKeyserverURL(event.currentTarget.value), [], ); const isKeyserverURLValidCallback = useIsKeyserverURLValid(keyserverURL); const onClickAddKeyserver = React.useCallback(async () => { if (!currentUserID || !keyserverURL) { return; } setStatus(keyserverCheckStatusLoading); - const keyserverVersionData = await isKeyserverURLValidCallback(); - if (!keyserverVersionData) { - setStatus(keyserverCheckStatusError); + let keyserverVersionData; + try { + keyserverVersionData = await isKeyserverURLValidCallback(); + } catch (e) { + const message = getMessageForException(e); + if (message === 'client_version_unsupported') { + setStatus(keyserverCheckStatusVersionError); + } else { + setStatus(keyserverCheckStatusGenericError); + } return; } setStatus(keyserverCheckStatusInactive); const newKeyserverInfo: KeyserverInfo = defaultKeyserverInfo(keyserverURL); dispatch({ type: addKeyserverActionType, payload: { keyserverAdminUserID: keyserverVersionData.ownerID, newKeyserverInfo, }, }); popModal(); }, [ currentUserID, dispatch, keyserverURL, popModal, isKeyserverURLValidCallback, ]); - const showErrorMessage = status.status === 'error'; + const { error } = status; const errorMessage = React.useMemo(() => { let errorText; - if (showErrorMessage) { + if (error === 'client_version_unsupported') { + errorText = getVersionUnsupportedError(); + } else if (error) { errorText = 'Cannot connect to keyserver. Please check the URL or your ' + 'connection and try again.'; } return
{errorText}
; - }, [showErrorMessage]); + }, [error]); const buttonDisabled = !keyserverURL || status.status === 'loading'; const addKeyserverButton = React.useMemo( () => ( ), [buttonDisabled, onClickAddKeyserver], ); const addKeyserverModal = React.useMemo( () => (
Keyserver URL
{errorMessage}
), [ addKeyserverButton, errorMessage, keyserverURL, onChangeKeyserverURL, popModal, ], ); return addKeyserverModal; } export default AddKeyserverModal;