diff --git a/web/settings/account-settings.react.js b/web/settings/account-settings.react.js index 27d1209bb..a6329fce4 100644 --- a/web/settings/account-settings.react.js +++ b/web/settings/account-settings.react.js @@ -1,188 +1,213 @@ // @flow import * as React from 'react'; import { useLogOut, logOutActionTypes } from 'lib/actions/user-actions.js'; import { useModalContext } from 'lib/components/modal-provider.react.js'; import SWMansionIcon from 'lib/components/swmansion-icon.react.js'; import { useStringForUser } from 'lib/hooks/ens-cache.js'; import { accountHasPassword } from 'lib/shared/account-utils.js'; import { useTunnelbroker } from 'lib/tunnelbroker/tunnelbroker-context.js'; import { useDispatchActionPromise } from 'lib/utils/redux-promise-utils.js'; import css from './account-settings.css'; import AppearanceChangeModal from './appearance-change-modal.react.js'; +import BackupTestRestoreModal from './backup-test-restore-modal.react.js'; import PasswordChangeModal from './password-change-modal.js'; import BlockListModal from './relationship/block-list-modal.react.js'; import FriendListModal from './relationship/friend-list-modal.react.js'; import TunnelbrokerMessagesScreen from './tunnelbroker-message-list.react.js'; import TunnelbrokerTestScreen from './tunnelbroker-test.react.js'; import EditUserAvatar from '../avatars/edit-user-avatar.react.js'; import Button from '../components/button.react.js'; import { useSelector } from '../redux/redux-utils.js'; import { useStaffCanSee } from '../utils/staff-utils.js'; function AccountSettings(): React.Node { const sendLogoutRequest = useLogOut(); const dispatchActionPromise = useDispatchActionPromise(); const logOutUser = React.useCallback( () => dispatchActionPromise(logOutActionTypes, sendLogoutRequest()), [dispatchActionPromise, sendLogoutRequest], ); const { pushModal, popModal } = useModalContext(); const showPasswordChangeModal = React.useCallback( () => pushModal(), [pushModal], ); const openFriendList = React.useCallback( () => pushModal(), [pushModal], ); const openBlockList = React.useCallback( () => pushModal(), [pushModal], ); const isAccountWithPassword = useSelector(state => accountHasPassword(state.currentUserInfo), ); const currentUserInfo = useSelector(state => state.currentUserInfo); const stringForUser = useStringForUser(currentUserInfo); const staffCanSee = useStaffCanSee(); const { sendMessage, connected, addListener, removeListener } = useTunnelbroker(); const openTunnelbrokerModal = React.useCallback( () => pushModal( , ), [popModal, pushModal, sendMessage], ); const openTunnelbrokerMessagesModal = React.useCallback( () => pushModal( , ), [addListener, popModal, pushModal, removeListener], ); + const openBackupTestRestoreModal = React.useCallback( + () => pushModal(), + [popModal, pushModal], + ); + const showAppearanceModal = React.useCallback( () => pushModal(), [pushModal], ); if (!currentUserInfo || currentUserInfo.anonymous) { return null; } let changePasswordSection; if (isAccountWithPassword) { changePasswordSection = (
  • Password ******
  • ); } let preferences; if (staffCanSee) { preferences = (

    Preferences

    • Appearance
    ); } let tunnelbroker; if (staffCanSee) { tunnelbroker = (

    Tunnelbroker menu

    • Connected {connected.toString()}
    • Send message to device
    • Trace received messages
    ); } + let backup; + if (staffCanSee) { + backup = ( +
    +

    Backup menu

    +
    +
      +
    • + Test backup restore + +
    • +
    +
    +
    + ); + } return (

    My Account

    • {'Logged in as '} {stringForUser}

    • {changePasswordSection}
    • Friend List
    • Block List
    {preferences} {tunnelbroker} + {backup}
    ); } export default AccountSettings; diff --git a/web/settings/backup-test-restore-modal.css b/web/settings/backup-test-restore-modal.css new file mode 100644 index 000000000..bfb878280 --- /dev/null +++ b/web/settings/backup-test-restore-modal.css @@ -0,0 +1,25 @@ +.modalBody { + padding: 24px 40px 32px; + color: var(--fg); +} + +.content { + display: flex; + flex-direction: column; + gap: 10px; +} + +.footer { + display: flex; + flex-direction: row-reverse; + justify-content: space-between; + padding-top: 8px; +} + +.modalError { + font-size: var(--xs-font-12); + color: var(--error); + font-style: italic; + padding-left: 6px; + align-self: center; +} diff --git a/web/settings/backup-test-restore-modal.react.js b/web/settings/backup-test-restore-modal.react.js new file mode 100644 index 000000000..bc77c5680 --- /dev/null +++ b/web/settings/backup-test-restore-modal.react.js @@ -0,0 +1,118 @@ +// @flow + +import invariant from 'invariant'; +import * as React from 'react'; + +import { IdentityClientContext } from 'lib/shared/identity-client-context.js'; + +import css from './backup-test-restore-modal.css'; +import Button from '../components/button.react.js'; +import { getDatabaseModule } from '../database/database-module-provider.js'; +import Input from '../modals/input.react.js'; +import Modal from '../modals/modal.react.js'; +import { workerRequestMessageTypes } from '../types/worker-types.js'; + +type Props = { + +onClose: () => void, +}; + +function BackupTestRestoreModal(props: Props): React.Node { + const { onClose } = props; + const [backupID, setBackupID] = React.useState(''); + const [backupDataKey, setBackupDataKey] = React.useState(''); + const [backupLogDataKey, setBackupLogDataKey] = React.useState(''); + const [inProgress, setInProgress] = React.useState(false); + const [errorMessage, setErrorMessage] = React.useState(''); + + const client = React.useContext(IdentityClientContext); + + const onSubmit = React.useCallback( + async (event: SyntheticEvent) => { + event.preventDefault(); + + setInProgress(true); + void (async () => { + try { + if (!client) { + throw new Error('No identity client'); + } + + const authMetadata = await client.getAuthMetadata(); + + const databaseModule = await getDatabaseModule(); + await databaseModule.schedule({ + type: workerRequestMessageTypes.BACKUP_RESTORE, + authMetadata, + backupID, + backupDataKey, + backupLogDataKey, + }); + } catch (e) { + setErrorMessage(e.message); + } + setInProgress(false); + })(); + }, + [backupDataKey, backupID, backupLogDataKey, client], + ); + + let errorMsg; + if (errorMessage) { + errorMsg =
    {errorMessage}
    ; + } + + return ( + +
    +
    + ) => { + const target = event.target; + invariant(target instanceof HTMLInputElement, 'target not input'); + setBackupID(target.value); + }} + disabled={inProgress} + label="Backup ID" + /> + ) => { + const target = event.target; + invariant(target instanceof HTMLInputElement, 'target not input'); + setBackupDataKey(target.value); + }} + disabled={inProgress} + label="Backup Data Encryption Key" + /> + ) => { + const target = event.target; + invariant(target instanceof HTMLInputElement, 'target not input'); + setBackupLogDataKey(target.value); + }} + disabled={inProgress} + label="Backup Logs Encryption Key" + /> +
    +
    + + {errorMsg} +
    +
    +
    + ); +} + +export default BackupTestRestoreModal;