feat: Automatic plaintext backup option in Preferences > Backups will backup your notes and tags into plaintext, unencrypted folders on your computer. In addition, automatic encrypted text backups preference management has moved from the top-level menu in the desktop app to Preferences > Backups. (#2322)
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { FunctionComponent, useCallback, useEffect, useRef, useState } from 'react'
|
||||
import { FunctionComponent, useCallback, useRef } from 'react'
|
||||
import { STRING_SIGN_OUT_CONFIRMATION } from '@/Constants/Strings'
|
||||
import { WebApplication } from '@/Application/Application'
|
||||
import { ViewControllerManager } from '@/Controllers/ViewControllerManager'
|
||||
@@ -8,6 +8,7 @@ import { isDesktopApplication } from '@/Utils'
|
||||
import Button from '@/Components/Button/Button'
|
||||
import Icon from '../Icon/Icon'
|
||||
import AlertDialog from '../AlertDialog/AlertDialog'
|
||||
import HorizontalSeparator from '../Shared/HorizontalSeparator'
|
||||
|
||||
type Props = {
|
||||
application: WebApplication
|
||||
@@ -16,30 +17,24 @@ type Props = {
|
||||
}
|
||||
|
||||
const ConfirmSignoutModal: FunctionComponent<Props> = ({ application, viewControllerManager, applicationGroup }) => {
|
||||
const [deleteLocalBackups, setDeleteLocalBackups] = useState(false)
|
||||
const hasAnyBackupsEnabled =
|
||||
application.fileBackups?.isFilesBackupsEnabled() ||
|
||||
application.fileBackups?.isPlaintextBackupsEnabled() ||
|
||||
application.fileBackups?.isTextBackupsEnabled()
|
||||
|
||||
const cancelRef = useRef<HTMLButtonElement>(null)
|
||||
const closeDialog = useCallback(() => {
|
||||
viewControllerManager.accountMenuController.setSigningOut(false)
|
||||
}, [viewControllerManager.accountMenuController])
|
||||
|
||||
const [localBackupsCount, setLocalBackupsCount] = useState(0)
|
||||
|
||||
useEffect(() => {
|
||||
application.desktopDevice?.localBackupsCount().then(setLocalBackupsCount).catch(console.error)
|
||||
}, [viewControllerManager.accountMenuController.signingOut, application.desktopDevice])
|
||||
|
||||
const workspaces = applicationGroup.getDescriptors()
|
||||
const showWorkspaceWarning = workspaces.length > 1 && isDesktopApplication()
|
||||
|
||||
const confirm = useCallback(() => {
|
||||
if (deleteLocalBackups) {
|
||||
application.signOutAndDeleteLocalBackups().catch(console.error)
|
||||
} else {
|
||||
application.user.signOut().catch(console.error)
|
||||
}
|
||||
application.user.signOut().catch(console.error)
|
||||
|
||||
closeDialog()
|
||||
}, [application, closeDialog, deleteLocalBackups])
|
||||
}, [application, closeDialog])
|
||||
|
||||
return (
|
||||
<AlertDialog closeDialog={closeDialog}>
|
||||
@@ -66,31 +61,26 @@ const ConfirmSignoutModal: FunctionComponent<Props> = ({ application, viewContro
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{localBackupsCount > 0 && (
|
||||
<div className="flex">
|
||||
<div className="sk-panel-row"></div>
|
||||
<label className="flex items-center">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={deleteLocalBackups}
|
||||
onChange={(event) => {
|
||||
setDeleteLocalBackups((event.target as HTMLInputElement).checked)
|
||||
}}
|
||||
/>
|
||||
<span className="ml-2">
|
||||
Delete {localBackupsCount} local backup file
|
||||
{localBackupsCount > 1 ? 's' : ''}
|
||||
</span>
|
||||
</label>
|
||||
<button
|
||||
className="sk-a ml-1.5 cursor-pointer rounded p-0 capitalize"
|
||||
onClick={() => {
|
||||
application.desktopDevice?.viewlocalBackups()
|
||||
}}
|
||||
>
|
||||
View backup files
|
||||
</button>
|
||||
</div>
|
||||
{hasAnyBackupsEnabled && (
|
||||
<>
|
||||
<HorizontalSeparator classes="my-2" />
|
||||
<div className="flex">
|
||||
<div className="sk-panel-row"></div>
|
||||
<div>
|
||||
<p className="text-base text-foreground lg:text-sm">
|
||||
Local backups are enabled for this workspace. Review your backup files manually to decide what to keep.
|
||||
</p>
|
||||
<button
|
||||
className="sk-a mt-2 cursor-pointer rounded p-0 capitalize lg:text-sm"
|
||||
onClick={() => {
|
||||
void application.fileBackups?.openAllDirectoriesContainingBackupFiles()
|
||||
}}
|
||||
>
|
||||
View backup files
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
|
||||
<div className="mt-4 flex justify-end gap-2">
|
||||
|
||||
@@ -35,7 +35,7 @@ export const FileContextMenuBackupOption: FunctionComponent<{ file: FileItem }>
|
||||
>
|
||||
<div className="ml-2">
|
||||
<div className="font-semibold text-success">Backed up on {dateToStringStyle1(backupInfo.backedUpOn)}</div>
|
||||
<div className="text-xs text-neutral">{backupInfo.absolutePath}</div>
|
||||
<div className="text-xs text-neutral">{application.fileBackups?.getFileBackupAbsolutePath(backupInfo)}</div>
|
||||
</div>
|
||||
</MenuItem>
|
||||
)}
|
||||
|
||||
@@ -153,7 +153,7 @@ class PasswordWizard extends AbstractComponent<Props, State> {
|
||||
}
|
||||
|
||||
async processPasswordChange() {
|
||||
await this.application.downloadBackup()
|
||||
await this.application.performDesktopTextBackup()
|
||||
|
||||
this.setState({
|
||||
lockContinue: true,
|
||||
|
||||
@@ -58,7 +58,7 @@ const ChangeEmail: FunctionComponent<Props> = ({ onCloseDialog, application }) =
|
||||
}
|
||||
|
||||
const processEmailChange = useCallback(async () => {
|
||||
await application.downloadBackup()
|
||||
await application.performDesktopTextBackup()
|
||||
|
||||
setLockContinue(true)
|
||||
|
||||
|
||||
@@ -6,6 +6,8 @@ import DataBackups from './DataBackups'
|
||||
import EmailBackups from './EmailBackups'
|
||||
import FileBackupsCrossPlatform from './Files/FileBackupsCrossPlatform'
|
||||
import { observer } from 'mobx-react-lite'
|
||||
import TextBackupsCrossPlatform from './TextBackups/TextBackupsCrossPlatform'
|
||||
import PlaintextBackupsCrossPlatform from './PlaintextBackups/PlaintextBackupsCrossPlatform'
|
||||
|
||||
type Props = {
|
||||
viewControllerManager: ViewControllerManager
|
||||
@@ -16,6 +18,8 @@ const Backups: FunctionComponent<Props> = ({ application, viewControllerManager
|
||||
return (
|
||||
<PreferencesPane>
|
||||
<DataBackups application={application} viewControllerManager={viewControllerManager} />
|
||||
<TextBackupsCrossPlatform application={application} />
|
||||
<PlaintextBackupsCrossPlatform />
|
||||
<FileBackupsCrossPlatform application={application} />
|
||||
<EmailBackups application={application} />
|
||||
</PreferencesPane>
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { isDesktopApplication } from '@/Utils'
|
||||
import { alertDialog, sanitizeFileName } from '@standardnotes/ui-services'
|
||||
import {
|
||||
STRING_IMPORT_SUCCESS,
|
||||
@@ -15,7 +14,7 @@ import { ChangeEventHandler, MouseEventHandler, useCallback, useEffect, useRef,
|
||||
import { WebApplication } from '@/Application/Application'
|
||||
import { ViewControllerManager } from '@/Controllers/ViewControllerManager'
|
||||
import { observer } from 'mobx-react-lite'
|
||||
import { Title, Text, Subtitle } from '@/Components/Preferences/PreferencesComponents/Content'
|
||||
import { Title, Subtitle } from '@/Components/Preferences/PreferencesComponents/Content'
|
||||
import Button from '@/Components/Button/Button'
|
||||
import PreferencesGroup from '../../PreferencesComponents/PreferencesGroup'
|
||||
import PreferencesSegment from '../../PreferencesComponents/PreferencesSegment'
|
||||
@@ -177,14 +176,7 @@ const DataBackups = ({ application, viewControllerManager }: Props) => {
|
||||
<PreferencesGroup>
|
||||
<PreferencesSegment>
|
||||
<Title>Data Backups</Title>
|
||||
|
||||
{isDesktopApplication() && (
|
||||
<Text className="mb-3">
|
||||
Backups are automatically created on desktop and can be managed via the "Backups" top-level menu.
|
||||
</Text>
|
||||
)}
|
||||
|
||||
<Subtitle>Download a backup of all your data</Subtitle>
|
||||
<Subtitle>Download a backup of all your text-based data</Subtitle>
|
||||
|
||||
{isEncryptionEnabled && (
|
||||
<form className="sk-panel-form sk-panel-row">
|
||||
|
||||
@@ -118,7 +118,7 @@ const EmailBackups = ({ application }: Props) => {
|
||||
)}
|
||||
|
||||
<div className={`${!hasAccount ? 'pointer-events-none cursor-default opacity-50' : ''}`}>
|
||||
<Subtitle>Email frequency</Subtitle>
|
||||
<Subtitle>Frequency</Subtitle>
|
||||
<Text>How often to receive backups.</Text>
|
||||
<div className="mt-2">
|
||||
{isLoading ? (
|
||||
|
||||
@@ -15,13 +15,13 @@ const FileBackupsCrossPlatform = ({ application }: Props) => {
|
||||
const fileBackupsService = useMemo(() => application.fileBackups, [application])
|
||||
|
||||
return fileBackupsService ? (
|
||||
<FileBackupsDesktop application={application} backupsService={fileBackupsService} />
|
||||
<FileBackupsDesktop backupsService={fileBackupsService} />
|
||||
) : (
|
||||
<>
|
||||
<PreferencesGroup>
|
||||
<PreferencesSegment>
|
||||
<Title>File Backups</Title>
|
||||
<Subtitle>Automatically save encrypted backups of files uploaded on any device to this computer.</Subtitle>
|
||||
<Title>Automatic File Backups</Title>
|
||||
<Subtitle>Automatically save encrypted backups of your files.</Subtitle>
|
||||
<Text className="mt-3">To enable file backups, use the Standard Notes desktop application.</Text>
|
||||
</PreferencesSegment>
|
||||
<HorizontalSeparator classes="my-4" />
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { WebApplication } from '@/Application/Application'
|
||||
import { observer } from 'mobx-react-lite'
|
||||
import { Title, Text, Subtitle } from '@/Components/Preferences/PreferencesComponents/Content'
|
||||
import { useCallback, useEffect, useState } from 'react'
|
||||
import { useCallback, useState } from 'react'
|
||||
import Button from '@/Components/Button/Button'
|
||||
import Switch from '@/Components/Switch/Switch'
|
||||
import HorizontalSeparator from '@/Components/Shared/HorizontalSeparator'
|
||||
@@ -10,30 +9,21 @@ import BackupsDropZone from './BackupsDropZone'
|
||||
import EncryptionStatusItem from '../../Security/EncryptionStatusItem'
|
||||
import PreferencesGroup from '@/Components/Preferences/PreferencesComponents/PreferencesGroup'
|
||||
import PreferencesSegment from '@/Components/Preferences/PreferencesComponents/PreferencesSegment'
|
||||
import { BackupServiceInterface } from '@standardnotes/snjs'
|
||||
import { useApplication } from '@/Components/ApplicationProvider'
|
||||
|
||||
type Props = {
|
||||
application: WebApplication
|
||||
backupsService: NonNullable<WebApplication['fileBackups']>
|
||||
backupsService: BackupServiceInterface
|
||||
}
|
||||
|
||||
const FileBackupsDesktop = ({ application, backupsService }: Props) => {
|
||||
const [backupsEnabled, setBackupsEnabled] = useState(false)
|
||||
const [backupsLocation, setBackupsLocation] = useState('')
|
||||
|
||||
useEffect(() => {
|
||||
void backupsService.isFilesBackupsEnabled().then(setBackupsEnabled)
|
||||
}, [backupsService])
|
||||
|
||||
useEffect(() => {
|
||||
if (backupsEnabled) {
|
||||
void backupsService.getFilesBackupsLocation().then(setBackupsLocation)
|
||||
}
|
||||
}, [backupsService, backupsEnabled])
|
||||
const FileBackupsDesktop = ({ backupsService }: Props) => {
|
||||
const application = useApplication()
|
||||
const [backupsEnabled, setBackupsEnabled] = useState(backupsService.isFilesBackupsEnabled())
|
||||
const [backupsLocation, setBackupsLocation] = useState(backupsService.getFilesBackupsLocation())
|
||||
|
||||
const changeBackupsLocation = useCallback(async () => {
|
||||
await backupsService.changeFilesBackupsLocation()
|
||||
|
||||
setBackupsLocation(await backupsService.getFilesBackupsLocation())
|
||||
const newLocation = await backupsService.changeFilesBackupsLocation()
|
||||
setBackupsLocation(newLocation)
|
||||
}, [backupsService])
|
||||
|
||||
const openBackupsLocation = useCallback(async () => {
|
||||
@@ -42,25 +32,24 @@ const FileBackupsDesktop = ({ application, backupsService }: Props) => {
|
||||
|
||||
const toggleBackups = useCallback(async () => {
|
||||
if (backupsEnabled) {
|
||||
await backupsService.disableFilesBackups()
|
||||
backupsService.disableFilesBackups()
|
||||
} else {
|
||||
await backupsService.enableFilesBackups()
|
||||
}
|
||||
|
||||
setBackupsEnabled(await backupsService.isFilesBackupsEnabled())
|
||||
setBackupsEnabled(backupsService.isFilesBackupsEnabled())
|
||||
setBackupsLocation(backupsService.getFilesBackupsLocation())
|
||||
}, [backupsService, backupsEnabled])
|
||||
|
||||
return (
|
||||
<>
|
||||
<PreferencesGroup>
|
||||
<PreferencesSegment>
|
||||
<Title>File Backups</Title>
|
||||
<Title>Automatic File Backups</Title>
|
||||
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="mr-10 flex flex-col">
|
||||
<Subtitle>
|
||||
Automatically save encrypted backups of files uploaded on any device to this computer.
|
||||
</Subtitle>
|
||||
<Subtitle>Automatically save encrypted backups of your uploaded files to this computer.</Subtitle>
|
||||
</div>
|
||||
<Switch onChange={toggleBackups} checked={backupsEnabled} />
|
||||
</div>
|
||||
@@ -85,14 +74,14 @@ const FileBackupsDesktop = ({ application, backupsService }: Props) => {
|
||||
</Text>
|
||||
|
||||
<EncryptionStatusItem
|
||||
status={backupsLocation}
|
||||
status={backupsLocation || 'Not Set'}
|
||||
icon={<Icon type="attachment-file" className="min-h-5 min-w-5" />}
|
||||
checkmark={false}
|
||||
/>
|
||||
|
||||
<div className="mt-2.5 flex flex-row">
|
||||
<Button label="Open Backups Location" className={'mr-3 text-xs'} onClick={openBackupsLocation} />
|
||||
<Button label="Change Backups Location" className={'mr-3 text-xs'} onClick={changeBackupsLocation} />
|
||||
<Button label="Open Location" className={'mr-3 text-xs'} onClick={openBackupsLocation} />
|
||||
<Button label="Change Location" className={'mr-3 text-xs'} onClick={changeBackupsLocation} />
|
||||
</div>
|
||||
</>
|
||||
</PreferencesSegment>
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
import { Subtitle, Title, Text } from '@/Components/Preferences/PreferencesComponents/Content'
|
||||
import PreferencesGroup from '@/Components/Preferences/PreferencesComponents/PreferencesGroup'
|
||||
import PreferencesSegment from '@/Components/Preferences/PreferencesComponents/PreferencesSegment'
|
||||
|
||||
import { useMemo } from 'react'
|
||||
import PlaintextBackupsDesktop from './PlaintextBackupsDesktop'
|
||||
import { useApplication } from '@/Components/ApplicationProvider'
|
||||
|
||||
const PlaintextBackupsCrossPlatform = () => {
|
||||
const application = useApplication()
|
||||
const fileBackupsService = useMemo(() => application.fileBackups, [application])
|
||||
|
||||
return fileBackupsService ? (
|
||||
<PlaintextBackupsDesktop backupsService={fileBackupsService} />
|
||||
) : (
|
||||
<>
|
||||
<PreferencesGroup>
|
||||
<PreferencesSegment>
|
||||
<Title>Automatic Plaintext Backups</Title>
|
||||
<Subtitle>Automatically save backups of all your notes into plaintext, non-encrypted folders.</Subtitle>
|
||||
<Text className="mt-3">To enable plaintext backups, use the Standard Notes desktop application.</Text>
|
||||
</PreferencesSegment>
|
||||
</PreferencesGroup>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default PlaintextBackupsCrossPlatform
|
||||
@@ -0,0 +1,89 @@
|
||||
import { observer } from 'mobx-react-lite'
|
||||
import { Title, Text, Subtitle } from '@/Components/Preferences/PreferencesComponents/Content'
|
||||
import { useCallback, useState } from 'react'
|
||||
import Button from '@/Components/Button/Button'
|
||||
import Switch from '@/Components/Switch/Switch'
|
||||
import HorizontalSeparator from '@/Components/Shared/HorizontalSeparator'
|
||||
import Icon from '@/Components/Icon/Icon'
|
||||
import EncryptionStatusItem from '../../Security/EncryptionStatusItem'
|
||||
import PreferencesGroup from '@/Components/Preferences/PreferencesComponents/PreferencesGroup'
|
||||
import PreferencesSegment from '@/Components/Preferences/PreferencesComponents/PreferencesSegment'
|
||||
import { BackupServiceInterface } from '@standardnotes/snjs'
|
||||
|
||||
type Props = {
|
||||
backupsService: BackupServiceInterface
|
||||
}
|
||||
|
||||
const PlaintextBackupsDesktop = ({ backupsService }: Props) => {
|
||||
const [backupsEnabled, setBackupsEnabled] = useState(backupsService.isPlaintextBackupsEnabled())
|
||||
const [backupsLocation, setBackupsLocation] = useState(backupsService.getPlaintextBackupsLocation())
|
||||
|
||||
const changeBackupsLocation = useCallback(async () => {
|
||||
const newLocation = await backupsService.changePlaintextBackupsLocation()
|
||||
setBackupsLocation(newLocation)
|
||||
}, [backupsService])
|
||||
|
||||
const openBackupsLocation = useCallback(async () => {
|
||||
await backupsService.openPlaintextBackupsLocation()
|
||||
}, [backupsService])
|
||||
|
||||
const toggleBackups = useCallback(async () => {
|
||||
if (backupsEnabled) {
|
||||
backupsService.disablePlaintextBackups()
|
||||
} else {
|
||||
await backupsService.enablePlaintextBackups()
|
||||
}
|
||||
|
||||
setBackupsEnabled(backupsService.isPlaintextBackupsEnabled())
|
||||
setBackupsLocation(backupsService.getPlaintextBackupsLocation())
|
||||
}, [backupsEnabled, backupsService])
|
||||
|
||||
return (
|
||||
<>
|
||||
<PreferencesGroup>
|
||||
<PreferencesSegment>
|
||||
<Title>Automatic Plaintext Backups</Title>
|
||||
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="mr-10 flex flex-col">
|
||||
<Subtitle>
|
||||
Automatically save backups of all your notes to this computer into plaintext, non-encrypted folders.
|
||||
</Subtitle>
|
||||
</div>
|
||||
<Switch onChange={toggleBackups} checked={backupsEnabled} />
|
||||
</div>
|
||||
|
||||
{!backupsEnabled && (
|
||||
<>
|
||||
<HorizontalSeparator classes="mt-2.5 mb-4" />
|
||||
<Text>Plaintext backups are not enabled. Enable to choose where your data is backed up.</Text>
|
||||
</>
|
||||
)}
|
||||
</PreferencesSegment>
|
||||
|
||||
{backupsEnabled && (
|
||||
<>
|
||||
<HorizontalSeparator classes="my-4" />
|
||||
<PreferencesSegment>
|
||||
<>
|
||||
<Text className="mb-3">Plaintext backups are enabled and saved to:</Text>
|
||||
<EncryptionStatusItem
|
||||
status={backupsLocation || 'Not Set'}
|
||||
icon={<Icon type="attachment-file" className="min-h-5 min-w-5" />}
|
||||
checkmark={false}
|
||||
/>
|
||||
|
||||
<div className="mt-2.5 flex flex-row">
|
||||
<Button label="Open Location" className={'mr-3 text-xs'} onClick={openBackupsLocation} />
|
||||
<Button label="Change Location" className={'mr-3 text-xs'} onClick={changeBackupsLocation} />
|
||||
</div>
|
||||
</>
|
||||
</PreferencesSegment>
|
||||
</>
|
||||
)}
|
||||
</PreferencesGroup>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default observer(PlaintextBackupsDesktop)
|
||||
@@ -0,0 +1,30 @@
|
||||
import { Subtitle, Title, Text } from '@/Components/Preferences/PreferencesComponents/Content'
|
||||
import PreferencesGroup from '@/Components/Preferences/PreferencesComponents/PreferencesGroup'
|
||||
import PreferencesSegment from '@/Components/Preferences/PreferencesComponents/PreferencesSegment'
|
||||
import { WebApplication } from '@/Application/Application'
|
||||
import { useMemo } from 'react'
|
||||
import TextBackupsDesktop from './TextBackupsDesktop'
|
||||
|
||||
type Props = {
|
||||
application: WebApplication
|
||||
}
|
||||
|
||||
const TextBackupsCrossPlatform = ({ application }: Props) => {
|
||||
const fileBackupsService = useMemo(() => application.fileBackups, [application])
|
||||
|
||||
return fileBackupsService ? (
|
||||
<TextBackupsDesktop backupsService={fileBackupsService} />
|
||||
) : (
|
||||
<>
|
||||
<PreferencesGroup>
|
||||
<PreferencesSegment>
|
||||
<Title>Automatic Text Backups</Title>
|
||||
<Subtitle>Automatically save encrypted and decrypted backups of your note and tag data.</Subtitle>
|
||||
<Text className="mt-3">To enable text backups, use the Standard Notes desktop application.</Text>
|
||||
</PreferencesSegment>
|
||||
</PreferencesGroup>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default TextBackupsCrossPlatform
|
||||
@@ -0,0 +1,106 @@
|
||||
import { observer } from 'mobx-react-lite'
|
||||
import { Title, Text, Subtitle } from '@/Components/Preferences/PreferencesComponents/Content'
|
||||
import { useCallback, useState } from 'react'
|
||||
import Button from '@/Components/Button/Button'
|
||||
import Switch from '@/Components/Switch/Switch'
|
||||
import HorizontalSeparator from '@/Components/Shared/HorizontalSeparator'
|
||||
import Icon from '@/Components/Icon/Icon'
|
||||
import EncryptionStatusItem from '../../Security/EncryptionStatusItem'
|
||||
import PreferencesGroup from '@/Components/Preferences/PreferencesComponents/PreferencesGroup'
|
||||
import PreferencesSegment from '@/Components/Preferences/PreferencesComponents/PreferencesSegment'
|
||||
import { BackupServiceInterface } from '@standardnotes/snjs'
|
||||
import { useApplication } from '@/Components/ApplicationProvider'
|
||||
|
||||
type Props = {
|
||||
backupsService: BackupServiceInterface
|
||||
}
|
||||
|
||||
const TextBackupsDesktop = ({ backupsService }: Props) => {
|
||||
const application = useApplication()
|
||||
const [backupsEnabled, setBackupsEnabled] = useState(backupsService.isTextBackupsEnabled())
|
||||
const [backupsLocation, setBackupsLocation] = useState(backupsService.getTextBackupsLocation())
|
||||
|
||||
const changeBackupsLocation = useCallback(async () => {
|
||||
const newLocation = await backupsService.changeTextBackupsLocation()
|
||||
setBackupsLocation(newLocation)
|
||||
}, [backupsService])
|
||||
|
||||
const openBackupsLocation = useCallback(async () => {
|
||||
await backupsService.openTextBackupsLocation()
|
||||
}, [backupsService])
|
||||
|
||||
const toggleBackups = useCallback(async () => {
|
||||
if (backupsEnabled) {
|
||||
backupsService.disableTextBackups()
|
||||
} else {
|
||||
await backupsService.enableTextBackups()
|
||||
}
|
||||
|
||||
setBackupsEnabled(backupsService.isTextBackupsEnabled())
|
||||
setBackupsLocation(backupsService.getTextBackupsLocation())
|
||||
}, [backupsEnabled, backupsService])
|
||||
|
||||
const performBackup = useCallback(async () => {
|
||||
void application.getDesktopService()?.saveDesktopBackup()
|
||||
}, [application])
|
||||
|
||||
return (
|
||||
<>
|
||||
<PreferencesGroup>
|
||||
<PreferencesSegment>
|
||||
<Title>Automatic Encrypted Text Backups</Title>
|
||||
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="mr-10 flex flex-col">
|
||||
<Subtitle>
|
||||
Automatically save encrypted text backups of all your note and tag data to this computer.
|
||||
</Subtitle>
|
||||
</div>
|
||||
<Switch onChange={toggleBackups} checked={backupsEnabled} />
|
||||
</div>
|
||||
|
||||
{!backupsEnabled && (
|
||||
<>
|
||||
<HorizontalSeparator classes="mt-2.5 mb-4" />
|
||||
<Text>Text backups are not enabled. Enable to choose where your data is backed up.</Text>
|
||||
</>
|
||||
)}
|
||||
</PreferencesSegment>
|
||||
|
||||
{backupsEnabled && (
|
||||
<>
|
||||
<HorizontalSeparator classes="my-4" />
|
||||
|
||||
<PreferencesSegment>
|
||||
<>
|
||||
<Text className="mb-3">Text backups are enabled and saved to:</Text>
|
||||
|
||||
<EncryptionStatusItem
|
||||
status={backupsLocation || 'Not Set'}
|
||||
icon={<Icon type="attachment-file" className="min-h-5 min-w-5" />}
|
||||
checkmark={false}
|
||||
/>
|
||||
|
||||
<div className="mt-2.5 flex flex-row">
|
||||
<Button label="Open Location" className={'mr-3 text-xs'} onClick={openBackupsLocation} />
|
||||
<Button label="Change Location" className={'mr-3 text-xs'} onClick={changeBackupsLocation} />
|
||||
</div>
|
||||
</>
|
||||
|
||||
<HorizontalSeparator classes="my-4" />
|
||||
|
||||
<Text className="mb-3">
|
||||
Backups are saved automatically throughout the day. You can perform a one-time backup now below.
|
||||
</Text>
|
||||
<div className="flex flex-row">
|
||||
<Button label="Perform Backup" className={'mr-3 text-xs'} onClick={performBackup} />
|
||||
</div>
|
||||
</PreferencesSegment>
|
||||
</>
|
||||
)}
|
||||
</PreferencesGroup>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default observer(TextBackupsDesktop)
|
||||
@@ -12,7 +12,7 @@ const TwoFactorTitle: FunctionComponent<Props> = ({ auth }) => {
|
||||
return <Title>Two-factor authentication not available</Title>
|
||||
}
|
||||
|
||||
return <Title>Two-factor authentication</Title>
|
||||
return <Title>Two-Factor Authentication</Title>
|
||||
}
|
||||
|
||||
export default observer(TwoFactorTitle)
|
||||
|
||||
Reference in New Issue
Block a user