diff --git a/web/settings/account-settings.react.js b/web/settings/account-settings.react.js
--- a/web/settings/account-settings.react.js
+++ b/web/settings/account-settings.react.js
@@ -12,6 +12,7 @@
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';
@@ -76,6 +77,11 @@
[addListener, popModal, pushModal, removeListener],
);
+ const openBackupTestRestoreModal = React.useCallback(
+ () => pushModal(),
+ [popModal, pushModal],
+ );
+
const showAppearanceModal = React.useCallback(
() => pushModal(),
[pushModal],
@@ -146,6 +152,24 @@
);
}
+ let backup;
+ if (staffCanSee) {
+ backup = (
+
+
Backup menu
+
+
+ -
+ Test backup restore
+
+
+
+
+
+ );
+ }
return (
@@ -180,6 +204,7 @@
{preferences}
{tunnelbroker}
+ {backup}
);
diff --git a/web/settings/backup-test-restore-modal.css b/web/settings/backup-test-restore-modal.css
new file mode 100644
--- /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
--- /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 (
+
+
+
+ );
+}
+
+export default BackupTestRestoreModal;