import { useCallback, useEffect, useRef } 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 { useHistory } from '../lib/history'; import { useSelector } from '../redux/store'; import { SettingsForm } from './cell/SettingsForm'; import { BackAction } from './KeyboardNavigation'; import { Layout, SettingsContainer } from './Layout'; import { ModalAlert, ModalAlertType } from './Modal'; import { NavigationBar, NavigationContainer, NavigationItems, TitleBarItem } from './NavigationBar'; import { NamedProxyForm } from './ProxyForm'; import SettingsHeader, { HeaderSubTitle, HeaderTitle } from './SettingsHeader'; import { StyledContent, StyledNavigationScrollbars, StyledSettingsContent } from './SettingsStyles'; import { SmallButton } from './SmallButton'; export function EditApiAccessMethod() { return ( ); } function AccessMethodForm() { const history = 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 = useRef | undefined>(method); const save = useCallback(() => { if (updatedMethod.current !== undefined) { resetTestResult(); if (id === undefined) { void addApiAccessMethod(updatedMethod.current); } else { void updateApiAccessMethod({ ...updatedMethod.current, id }); } history.pop(); } }, [updatedMethod.current, id]); const onSave = useCallback( async (newMethod: NamedCustomProxy) => { const enabled = id === undefined ? true : method?.enabled ?? true; updatedMethod.current = { ...newMethod, enabled }; if ( updatedMethod.current !== undefined && (await testApiAccessMethod(updatedMethod.current as CustomProxy)) ) { // Hide the save dialog after 1.5 seconds. saveScheduler.schedule(save, 1500); } }, [updatedMethod, save, history.pop], ); const title = getTitle(id === undefined); const subtitle = getSubtitle(id === undefined); return ( {title} {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) { const type = props.testing ? ModalAlertType.loading : props.testResult ? ModalAlertType.success : ModalAlertType.failure; const prevType = useRef(type); const isOpen = props.testing || props.testResult !== undefined; const typeValue = isOpen ? type : prevType.current; useEffect(() => { if (isOpen) { prevType.current = type; } }, [type]); 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 = ( {messages.gettext('Save')} ); const cancelButton = ( {messages.gettext('Cancel')} ); const disabledCancelButton = ( {messages.gettext('Cancel')} ); switch (type) { case ModalAlertType.success: return [disabledCancelButton]; case ModalAlertType.failure: return [cancelButton, saveButton]; case ModalAlertType.loading: default: return [cancelButton]; } }