import { useCallback, useState } from 'react'; import { useParams } from 'react-router'; import { sprintf } from 'sprintf-js'; import { CustomProxy, NamedCustomProxy, NewAccessMethodSetting, } from '../../shared/daemon-rpc-types'; import { messages } from '../../shared/gettext'; import { useScheduler } from '../../shared/scheduler'; import { useAppContext } from '../context'; import { useApiAccessMethodTest } from '../lib/api-access-methods'; import { Button } from '../lib/components'; import { useHistory } from '../lib/history'; import { useLastDefinedValue } from '../lib/utility-hooks'; import { useSelector } from '../redux/store'; import { AppNavigationHeader } from './'; import { SettingsForm } from './cell/SettingsForm'; import { BackAction } from './KeyboardNavigation'; import { Layout, SettingsContainer, SettingsContent, SettingsNavigationScrollbars } from './Layout'; import { ModalAlert, ModalAlertType } from './Modal'; import { NavigationContainer } from './NavigationContainer'; import { NamedProxyForm, ProxyFormButtons, ProxyFormInner, ProxyFormNameField } from './ProxyForm'; import SettingsHeader, { HeaderSubTitle, HeaderTitle } from './SettingsHeader'; export function EditApiAccessMethod() { return ( ); } function AccessMethodForm() { const { pop } = useHistory(); const { addApiAccessMethod, updateApiAccessMethod } = useAppContext(); const methods = useSelector((state) => state.settings.apiAccessMethods.custom); const [testing, testResult, testApiAccessMethod, resetTestResult] = useApiAccessMethodTest( false, 500, ); const saveScheduler = useScheduler(); // Use id in url to figure out which method is to be edited. undefined means this is a new method. const { id } = useParams<{ id: string | undefined }>(); const method = methods.find((method) => method.id === id); const [updatedMethod, setUpdatedMethod] = useState< NewAccessMethodSetting | undefined >(method); const save = useCallback( (method: NewAccessMethodSetting) => { if (method !== undefined) { resetTestResult(); if (id === undefined) { void addApiAccessMethod(method); } else { void updateApiAccessMethod({ ...method, id }); } pop(); } }, [resetTestResult, id, pop, addApiAccessMethod, updateApiAccessMethod], ); const onSave = useCallback( async (newMethod: NamedCustomProxy) => { const enabled = id === undefined ? true : (method?.enabled ?? true); const updatedMethod = { ...newMethod, enabled }; setUpdatedMethod(updatedMethod); if ( updatedMethod !== undefined && (await testApiAccessMethod(updatedMethod as CustomProxy)) ) { // Hide the save dialog after 1.5 seconds. saveScheduler.schedule(() => save(updatedMethod), 1500); } }, [id, method?.enabled, testApiAccessMethod, saveScheduler, save], ); const handleDialogSave = useCallback(() => { if (updatedMethod !== undefined) { save(updatedMethod); } }, [save, updatedMethod]); const title = getTitle(id === undefined); const subtitle = getSubtitle(id === undefined); const customAccessMethods = useSelector((state) => state.settings.apiAccessMethods.custom); const onValidate = useCallback( (value: string) => { const nameUsedInOtherAccessMethod = customAccessMethods.some( (customAccessMethod) => method?.id !== customAccessMethod.id && customAccessMethod.name === value, ); return !nameUsedInOtherAccessMethod; }, [customAccessMethods, method], ); return ( {title} {subtitle} {id !== undefined && method === undefined ? ( Failed to open method ) : ( )} ); } function getTitle(isNewMethod: boolean) { return isNewMethod ? messages.pgettext('api-access-methods-view', 'Add method') : messages.pgettext('api-access-methods-view', 'Edit method'); } function getSubtitle(isNewMethod: boolean) { return isNewMethod ? messages.pgettext('api-access-methods-view', 'Adding a new API access method also tests it.') : messages.pgettext('api-access-methods-view', 'Editing an API access method also tests it.'); } interface TestingDialogProps { name: string; newMethod: boolean; testing: boolean; testResult?: boolean; cancel: () => void; save: () => void; } function TestingDialog(props: TestingDialogProps) { let currentType: ModalAlertType | undefined; if (props.testing) { currentType = ModalAlertType.loading; } else if (props.testResult) { currentType = ModalAlertType.success; } else if (props.testResult === false) { currentType = ModalAlertType.failure; } const type = useLastDefinedValue(currentType); const displayType = type ?? ModalAlertType.failure; return ( ); } function getTestingDialogTitle(type: ModalAlertType, newMethod: boolean) { switch (type) { case ModalAlertType.success: return newMethod ? messages.pgettext('api-access-methods-view', 'API reachable, adding method…') : messages.pgettext('api-access-methods-view', 'API reachable, saving method…'); case ModalAlertType.failure: return newMethod ? messages.pgettext('api-access-methods-view', 'API unreachable, add anyway?') : messages.pgettext('api-access-methods-view', 'API unreachable, save anyway?'); default: case ModalAlertType.loading: return messages.pgettext('api-access-methods-view', 'Testing method...'); } } function getTestingDialogSubTitle(type: ModalAlertType, newMethod: boolean, name: string) { switch (type) { case ModalAlertType.failure: return newMethod ? sprintf( messages.pgettext( 'api-access-methods-view', 'The API could not be reached using the %(name)s method.', ), { name }, ) : sprintf( // TRANSLATORS: %(save)s - Will be replaced with the translation for the word "Save". messages.pgettext( 'api-access-methods-view', 'Clicking “%(save)s” changes the in use method.', ), { save: messages.gettext('Save') }, ); default: return undefined; } } function getTestingDialogButtons(type: ModalAlertType, save: () => void, cancel: () => void) { const saveButton = ( ); const cancelButton = ( ); const disabledCancelButton = ( ); switch (type) { case ModalAlertType.success: return [disabledCancelButton]; case ModalAlertType.failure: return [cancelButton, saveButton]; case ModalAlertType.loading: default: return [cancelButton]; } }