From 10dfaaed7deb806c3f004a5e3597bba00bf85bbc Mon Sep 17 00:00:00 2001 From: Mo Date: Wed, 11 May 2022 13:17:23 -0500 Subject: [PATCH 1/8] feat(wip): file backups --- .../javascripts/Components/Footer/index.tsx | 12 +++--- .../Device/DesktopDeviceInterface.ts | 11 ----- .../javascripts/Device/DesktopSnjsExports.ts | 10 ++++- .../Device/DesktopWebCommunication.ts | 42 ------------------- .../javascripts/Device/StartApplication.ts | 4 +- app/assets/javascripts/Device/WebDevice.ts | 2 +- .../javascripts/Device/WebOrDesktopDevice.ts | 2 +- .../Device/WebOrDesktopDeviceInterface.ts | 9 ---- .../javascripts/Services/DesktopManager.ts | 4 +- .../javascripts/Services/StatusManager.ts | 30 ------------- .../javascripts/UIModels/Application.ts | 9 +--- .../javascripts/UIModels/ApplicationGroup.ts | 13 +++--- 12 files changed, 31 insertions(+), 117 deletions(-) delete mode 100644 app/assets/javascripts/Device/DesktopDeviceInterface.ts delete mode 100644 app/assets/javascripts/Device/DesktopWebCommunication.ts delete mode 100644 app/assets/javascripts/Device/WebOrDesktopDeviceInterface.ts delete mode 100644 app/assets/javascripts/Services/StatusManager.ts diff --git a/app/assets/javascripts/Components/Footer/index.tsx b/app/assets/javascripts/Components/Footer/index.tsx index 19f88b54121..7304bd6126e 100644 --- a/app/assets/javascripts/Components/Footer/index.tsx +++ b/app/assets/javascripts/Components/Footer/index.tsx @@ -76,7 +76,7 @@ export class Footer extends PureComponent { override componentDidMount(): void { super.componentDidMount() - this.application.getStatusManager().onStatusChange((message) => { + this.application.status.addEventObserver((_event, message) => { this.setState({ arbitraryStatusMessage: message, }) @@ -124,7 +124,7 @@ export class Footer extends PureComponent { } override onAppStateEvent(eventName: AppStateEvent, data: any) { - const statusService = this.application.getStatusManager() + const statusService = this.application.status switch (eventName) { case AppStateEvent.EditorFocused: if (data.eventSource === EventSource.UserInteraction) { @@ -172,7 +172,7 @@ export class Footer extends PureComponent { break case ApplicationEvent.CompletedFullSync: if (!this.completedInitialSync) { - this.application.getStatusManager().setMessage('') + this.application.status.setMessage('') this.completedInitialSync = true } if (!this.didCheckForOffline) { @@ -202,7 +202,7 @@ export class Footer extends PureComponent { break case ApplicationEvent.WillSync: if (!this.completedInitialSync) { - this.application.getStatusManager().setMessage('Syncing…') + this.application.status.setMessage('Syncing…') } break } @@ -213,7 +213,7 @@ export class Footer extends PureComponent { } updateSyncStatus() { - const statusManager = this.application.getStatusManager() + const statusManager = this.application.status const syncStatus = this.application.sync.getSyncStatus() const stats = syncStatus.getStats() if (syncStatus.hasError()) { @@ -243,7 +243,7 @@ export class Footer extends PureComponent { } updateLocalDataStatus() { - const statusManager = this.application.getStatusManager() + const statusManager = this.application.status const syncStatus = this.application.sync.getSyncStatus() const stats = syncStatus.getStats() const encryption = this.application.isEncryptionAvailable() diff --git a/app/assets/javascripts/Device/DesktopDeviceInterface.ts b/app/assets/javascripts/Device/DesktopDeviceInterface.ts deleted file mode 100644 index c36d1295241..00000000000 --- a/app/assets/javascripts/Device/DesktopDeviceInterface.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { DeviceInterface, Environment } from '@standardnotes/snjs' -import { WebClientRequiresDesktopMethods } from './DesktopWebCommunication' -import { WebOrDesktopDeviceInterface } from './WebOrDesktopDeviceInterface' - -export function isDesktopDevice(x: DeviceInterface): x is DesktopDeviceInterface { - return x.environment === Environment.Desktop -} - -export interface DesktopDeviceInterface extends WebOrDesktopDeviceInterface, WebClientRequiresDesktopMethods { - environment: Environment.Desktop -} diff --git a/app/assets/javascripts/Device/DesktopSnjsExports.ts b/app/assets/javascripts/Device/DesktopSnjsExports.ts index acd1a5c72c0..c6728d84796 100644 --- a/app/assets/javascripts/Device/DesktopSnjsExports.ts +++ b/app/assets/javascripts/Device/DesktopSnjsExports.ts @@ -1 +1,9 @@ -export { Environment, RawKeychainValue } from '@standardnotes/snjs' +export { + Environment, + RawKeychainValue, + DesktopDeviceInterface, + WebOrDesktopDeviceInterface, + DesktopClientRequiresWebMethods, + FileBackupsMapping, + FileBackupsDevice, +} from '@standardnotes/snjs' diff --git a/app/assets/javascripts/Device/DesktopWebCommunication.ts b/app/assets/javascripts/Device/DesktopWebCommunication.ts deleted file mode 100644 index 47e17af7278..00000000000 --- a/app/assets/javascripts/Device/DesktopWebCommunication.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { DecryptedTransferPayload } from '@standardnotes/snjs' - -export interface WebClientRequiresDesktopMethods { - localBackupsCount(): Promise - - viewlocalBackups(): void - - deleteLocalBackups(): Promise - - syncComponents(payloads: unknown[]): void - - onMajorDataChange(): void - - onInitialDataLoad(): void - - /** - * Destroys all sensitive storage data, such as localStorage, IndexedDB, and other log files. - */ - destroyAllData(): void - - onSearch(text?: string): void - - downloadBackup(): void | Promise - - get extensionsServerHost(): string -} - -export interface DesktopClientRequiresWebMethods { - updateAvailable(): void - - windowGainedFocus(): void - - windowLostFocus(): void - - onComponentInstallationComplete(componentData: DecryptedTransferPayload, error: unknown): Promise - - requestBackupFile(): Promise - - didBeginBackup(): void - - didFinishBackup(success: boolean): void -} diff --git a/app/assets/javascripts/Device/StartApplication.ts b/app/assets/javascripts/Device/StartApplication.ts index b0d8f35ee88..de14ea2c96f 100644 --- a/app/assets/javascripts/Device/StartApplication.ts +++ b/app/assets/javascripts/Device/StartApplication.ts @@ -1,8 +1,8 @@ -import { WebOrDesktopDevice } from './WebOrDesktopDevice' +import { WebOrDesktopDeviceInterface } from '@standardnotes/snjs' export type StartApplication = ( defaultSyncServerHost: string, - device: WebOrDesktopDevice, + device: WebOrDesktopDeviceInterface, enableUnfinishedFeatures: boolean, webSocketUrl: string, ) => Promise diff --git a/app/assets/javascripts/Device/WebDevice.ts b/app/assets/javascripts/Device/WebDevice.ts index 97f3917b001..e38d5bfa329 100644 --- a/app/assets/javascripts/Device/WebDevice.ts +++ b/app/assets/javascripts/Device/WebDevice.ts @@ -1,5 +1,5 @@ import { Environment, RawKeychainValue } from '@standardnotes/snjs' -import { WebOrDesktopDevice } from '@/Device/WebOrDesktopDevice' +import { WebOrDesktopDevice } from './WebOrDesktopDevice' const KEYCHAIN_STORAGE_KEY = 'keychain' diff --git a/app/assets/javascripts/Device/WebOrDesktopDevice.ts b/app/assets/javascripts/Device/WebOrDesktopDevice.ts index 1adaf05567e..72f5cac0524 100644 --- a/app/assets/javascripts/Device/WebOrDesktopDevice.ts +++ b/app/assets/javascripts/Device/WebOrDesktopDevice.ts @@ -7,9 +7,9 @@ import { TransferPayload, NamespacedRootKeyInKeychain, extendArray, + WebOrDesktopDeviceInterface, } from '@standardnotes/snjs' import { Database } from '../Database' -import { WebOrDesktopDeviceInterface } from './WebOrDesktopDeviceInterface' export abstract class WebOrDesktopDevice implements WebOrDesktopDeviceInterface { constructor(public appVersion: string) {} diff --git a/app/assets/javascripts/Device/WebOrDesktopDeviceInterface.ts b/app/assets/javascripts/Device/WebOrDesktopDeviceInterface.ts deleted file mode 100644 index ccd8e08f264..00000000000 --- a/app/assets/javascripts/Device/WebOrDesktopDeviceInterface.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { DeviceInterface, RawKeychainValue } from '@standardnotes/snjs' - -export interface WebOrDesktopDeviceInterface extends DeviceInterface { - readonly appVersion: string - - getKeychainValue(): Promise - - setKeychainValue(value: RawKeychainValue): Promise -} diff --git a/app/assets/javascripts/Services/DesktopManager.ts b/app/assets/javascripts/Services/DesktopManager.ts index ecdf8485578..c45d1a560ea 100644 --- a/app/assets/javascripts/Services/DesktopManager.ts +++ b/app/assets/javascripts/Services/DesktopManager.ts @@ -10,10 +10,10 @@ import { DecryptedTransferPayload, ComponentContent, assert, + DesktopClientRequiresWebMethods, + DesktopDeviceInterface, } from '@standardnotes/snjs' import { WebAppEvent, WebApplication } from '@/UIModels/Application' -import { DesktopDeviceInterface } from '../Device/DesktopDeviceInterface' -import { DesktopClientRequiresWebMethods } from '@/Device/DesktopWebCommunication' export class DesktopManager extends ApplicationService diff --git a/app/assets/javascripts/Services/StatusManager.ts b/app/assets/javascripts/Services/StatusManager.ts deleted file mode 100644 index 5b606aa3071..00000000000 --- a/app/assets/javascripts/Services/StatusManager.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { removeFromArray } from '@standardnotes/snjs' - -type StatusCallback = (string: string) => void - -export class StatusManager { - private _message = '' - private observers: StatusCallback[] = [] - - get message(): string { - return this._message - } - - setMessage(message: string) { - this._message = message - this.notifyObservers() - } - - onStatusChange(callback: StatusCallback) { - this.observers.push(callback) - return () => { - removeFromArray(this.observers, callback) - } - } - - private notifyObservers() { - for (const observer of this.observers) { - observer(this._message) - } - } -} diff --git a/app/assets/javascripts/UIModels/Application.ts b/app/assets/javascripts/UIModels/Application.ts index c02ac0b5e36..8fef1e5ec3e 100644 --- a/app/assets/javascripts/UIModels/Application.ts +++ b/app/assets/javascripts/UIModels/Application.ts @@ -2,10 +2,8 @@ import { WebCrypto } from '@/Crypto' import { WebAlertService } from '@/Services/AlertService' import { ArchiveManager } from '@/Services/ArchiveManager' import { AutolockService } from '@/Services/AutolockService' -import { DesktopDeviceInterface, isDesktopDevice } from '@/Device/DesktopDeviceInterface' import { DesktopManager } from '@/Services/DesktopManager' import { IOService } from '@/Services/IOService' -import { StatusManager } from '@/Services/StatusManager' import { ThemeManager } from '@/Services/ThemeManager' import { AppState } from '@/UIModels/AppState' import { WebOrDesktopDevice } from '@/Device/WebOrDesktopDevice' @@ -17,6 +15,8 @@ import { removeFromArray, IconsController, Runtime, + DesktopDeviceInterface, + isDesktopDevice, } from '@standardnotes/snjs' type WebServices = { @@ -24,7 +24,6 @@ type WebServices = { desktopService?: DesktopManager autolockService: AutolockService archiveService: ArchiveManager - statusManager: StatusManager themeService: ThemeManager io: IOService } @@ -137,10 +136,6 @@ export class WebApplication extends SNApplication { return undefined } - getStatusManager() { - return this.webServices.statusManager - } - public getThemeService() { return this.webServices.themeService } diff --git a/app/assets/javascripts/UIModels/ApplicationGroup.ts b/app/assets/javascripts/UIModels/ApplicationGroup.ts index 64b6a3c1f88..036bee6abe7 100644 --- a/app/assets/javascripts/UIModels/ApplicationGroup.ts +++ b/app/assets/javascripts/UIModels/ApplicationGroup.ts @@ -1,15 +1,20 @@ import { WebApplication } from './Application' -import { ApplicationDescriptor, SNApplicationGroup, Platform, Runtime, InternalEventBus } from '@standardnotes/snjs' +import { + ApplicationDescriptor, + SNApplicationGroup, + Platform, + Runtime, + InternalEventBus, + isDesktopDevice, +} from '@standardnotes/snjs' import { AppState } from '@/UIModels/AppState' import { getPlatform, isDesktopApplication } from '@/Utils' import { ArchiveManager } from '@/Services/ArchiveManager' import { DesktopManager } from '@/Services/DesktopManager' import { IOService } from '@/Services/IOService' import { AutolockService } from '@/Services/AutolockService' -import { StatusManager } from '@/Services/StatusManager' import { ThemeManager } from '@/Services/ThemeManager' import { WebOrDesktopDevice } from '@/Device/WebOrDesktopDevice' -import { isDesktopDevice } from '@/Device/DesktopDeviceInterface' export class ApplicationGroup extends SNApplicationGroup { constructor( @@ -53,7 +58,6 @@ export class ApplicationGroup extends SNApplicationGroup { const archiveService = new ArchiveManager(application) const io = new IOService(platform === Platform.MacWeb || platform === Platform.MacDesktop) const autolockService = new AutolockService(application, new InternalEventBus()) - const statusManager = new StatusManager() const themeService = new ThemeManager(application) application.setWebServices({ @@ -62,7 +66,6 @@ export class ApplicationGroup extends SNApplicationGroup { desktopService: isDesktopDevice(this.device) ? new DesktopManager(application, this.device) : undefined, io, autolockService, - statusManager, themeService, }) From 293157d5ca73a6d656e61f60f3ed40dc43658ed5 Mon Sep 17 00:00:00 2001 From: Mo Date: Wed, 11 May 2022 20:51:52 -0500 Subject: [PATCH 2/8] feat: file backups prefs --- .../Preferences/Panes/Backups/FileBackups.tsx | 139 ++++++++++++++++++ .../Preferences/Panes/Backups/index.tsx | 2 + .../Preferences/Panes/Security/Encryption.tsx | 7 +- .../javascripts/Device/DesktopSnjsExports.ts | 1 + .../UIModels/AppState/PreferencesState.ts | 2 +- .../javascripts/UIModels/Application.ts | 10 ++ 6 files changed, 157 insertions(+), 4 deletions(-) create mode 100644 app/assets/javascripts/Components/Preferences/Panes/Backups/FileBackups.tsx diff --git a/app/assets/javascripts/Components/Preferences/Panes/Backups/FileBackups.tsx b/app/assets/javascripts/Components/Preferences/Panes/Backups/FileBackups.tsx new file mode 100644 index 00000000000..7d5e84fb52e --- /dev/null +++ b/app/assets/javascripts/Components/Preferences/Panes/Backups/FileBackups.tsx @@ -0,0 +1,139 @@ +import { WebApplication } from '@/UIModels/Application' +import { observer } from 'mobx-react-lite' +import { + PreferencesGroup, + PreferencesSegment, + Title, + Text, + Subtitle, +} from '@/Components/Preferences/PreferencesComponents' +import { useCallback, useEffect, useState } from 'preact/hooks' +import { Button } from '@/Components/Button/Button' +import { isDesktopDevice } from '@standardnotes/snjs' +import { Switch } from '@/Components/Switch' +import { HorizontalSeparator } from '@/Components/Shared/HorizontalSeparator' +import { EncryptionStatusItem } from '../Security/Encryption' +import { Icon } from '@/Components/Icon' + +type Props = { + application: WebApplication +} + +export const FileBackups = observer(({ application }: Props) => { + const [backupsEnabled, setBackupsEnabled] = useState(false) + const [backupsLocation, setBackupsLocation] = useState('') + const [backupsService, _] = useState(application.fileBackups) + + if (!isDesktopDevice(application.deviceInterface)) { + return ( + <> + + + File Backups + + Automatically save encrypted backups of files uploaded to any device to this computer. To use file + backups, use the Standard Notes desktop application. + + + + + ) + } + + useEffect(() => { + void backupsService.isFilesBackupsEnabled().then(setBackupsEnabled) + }, [backupsService]) + + useEffect(() => { + if (backupsEnabled) { + void backupsService.getFilesBackupsLocation().then(setBackupsLocation) + } + }, [backupsService, backupsEnabled]) + + const changeBackupsLocation = useCallback(async () => { + await backupsService.changeFilesBackupsLocation() + + setBackupsLocation(await backupsService.getFilesBackupsLocation()) + }, [backupsService]) + + const openBackupsLocation = useCallback(async () => { + await backupsService.openFilesBackupsLocation() + }, [backupsService]) + + const toggleBackups = useCallback(async () => { + if (backupsEnabled) { + await backupsService.disableFilesBackups() + } else { + await backupsService.enableFilesBackups() + } + + setBackupsEnabled(await backupsService.isFilesBackupsEnabled()) + }, [backupsService, backupsEnabled]) + + return ( + <> + + + File Backups + +
+
+ + Automatically save encrypted backups of files uploaded on any device to this computer. + +
+ +
+ + {!backupsEnabled && ( + <> + + File backups are not enabled. Enable to choose where your files are backed up. + + )} +
+ + {backupsEnabled && ( + <> + + <> + + Files backups are enabled. When you upload a new file on any device and open this application, files + will be downloaded in encrypted form to: + + + ]} + checkmark={false} + /> +
+
+ +
+ + + + + + To decrypt a backup file, drag and drop the file's respective metadata.sn.json file here. + + + + )} +
+ + ) +}) diff --git a/app/assets/javascripts/Components/Preferences/Panes/Backups/index.tsx b/app/assets/javascripts/Components/Preferences/Panes/Backups/index.tsx index 2912525c7a9..8dc7be86814 100644 --- a/app/assets/javascripts/Components/Preferences/Panes/Backups/index.tsx +++ b/app/assets/javascripts/Components/Preferences/Panes/Backups/index.tsx @@ -5,6 +5,7 @@ import { PreferencesPane } from '@/Components/Preferences/PreferencesComponents' import { CloudLink } from './CloudBackups' import { DataBackups } from './DataBackups' import { EmailBackups } from './EmailBackups' +import { FileBackups } from './FileBackups' interface Props { appState: AppState @@ -15,6 +16,7 @@ export const Backups: FunctionComponent = ({ application, appState }) => return ( + diff --git a/app/assets/javascripts/Components/Preferences/Panes/Security/Encryption.tsx b/app/assets/javascripts/Components/Preferences/Panes/Security/Encryption.tsx index 6acb0e57173..2d9380e71a2 100644 --- a/app/assets/javascripts/Components/Preferences/Panes/Security/Encryption.tsx +++ b/app/assets/javascripts/Components/Preferences/Panes/Security/Encryption.tsx @@ -7,16 +7,17 @@ import { PreferencesGroup, PreferencesSegment, Text, Title } from '@/Components/ const formatCount = (count: number, itemType: string) => `${count} / ${count} ${itemType}` -const EncryptionStatusItem: FunctionComponent<{ +export const EncryptionStatusItem: FunctionComponent<{ icon: ComponentChild status: string -}> = ({ icon, status }) => ( + checkmark?: boolean +}> = ({ icon, status, checkmark = true }) => (
{icon}
{status}
- + {checkmark && }
) diff --git a/app/assets/javascripts/Device/DesktopSnjsExports.ts b/app/assets/javascripts/Device/DesktopSnjsExports.ts index c6728d84796..394bbb61d6a 100644 --- a/app/assets/javascripts/Device/DesktopSnjsExports.ts +++ b/app/assets/javascripts/Device/DesktopSnjsExports.ts @@ -6,4 +6,5 @@ export { DesktopClientRequiresWebMethods, FileBackupsMapping, FileBackupsDevice, + FileBackupMetadataName, } from '@standardnotes/snjs' diff --git a/app/assets/javascripts/UIModels/AppState/PreferencesState.ts b/app/assets/javascripts/UIModels/AppState/PreferencesState.ts index a576c770e69..ea81b09940e 100644 --- a/app/assets/javascripts/UIModels/AppState/PreferencesState.ts +++ b/app/assets/javascripts/UIModels/AppState/PreferencesState.ts @@ -4,7 +4,7 @@ import { action, computed, makeObservable, observable } from 'mobx' const DEFAULT_PANE = 'account' export class PreferencesState { - private _open = false + private _open = true currentPane: PreferenceId = DEFAULT_PANE constructor() { diff --git a/app/assets/javascripts/UIModels/Application.ts b/app/assets/javascripts/UIModels/Application.ts index 8fef1e5ec3e..d0de6a1221b 100644 --- a/app/assets/javascripts/UIModels/Application.ts +++ b/app/assets/javascripts/UIModels/Application.ts @@ -17,7 +17,9 @@ import { Runtime, DesktopDeviceInterface, isDesktopDevice, + SNFile, } from '@standardnotes/snjs' +import { StreamingFileApi } from '@standardnotes/filepicker' type WebServices = { appState: AppState @@ -144,6 +146,14 @@ export class WebApplication extends SNApplication { return this.webServices.io } + public async triggerBackupFileDemo(file: SNFile) { + const fileSystem = new StreamingFileApi() + + const result = await this.files.selectFileBackupAndSaveDecrypted(file.content, fileSystem) + + console.log('triggerBackupFileDemo result', result) + } + async checkForSecurityUpdate() { return this.protocolUpgradeAvailable() } From b8a23a5e0e69f9aec03a656a8789fc4e3a2fef41 Mon Sep 17 00:00:00 2001 From: Mo Date: Wed, 11 May 2022 21:37:20 -0500 Subject: [PATCH 3/8] feat: file backups prefs pane drag and drop --- .../AttachedFilesButton.tsx | 68 ++++++---- .../Preferences/Panes/Backups/FileBackups.tsx | 121 +++++++++++++++++- .../javascripts/Device/DesktopSnjsExports.ts | 1 - .../javascripts/UIModels/Application.ts | 10 -- 4 files changed, 163 insertions(+), 37 deletions(-) diff --git a/app/assets/javascripts/Components/AttachedFilesPopover/AttachedFilesButton.tsx b/app/assets/javascripts/Components/AttachedFilesPopover/AttachedFilesButton.tsx index 78799a23ff4..72c360a5553 100644 --- a/app/assets/javascripts/Components/AttachedFilesPopover/AttachedFilesButton.tsx +++ b/app/assets/javascripts/Components/AttachedFilesPopover/AttachedFilesButton.tsx @@ -23,8 +23,20 @@ type Props = { onClickPreprocessing?: () => Promise } -const isHandlingFileDrag = (event: DragEvent) => - event.dataTransfer?.items && Array.from(event.dataTransfer.items).some((item) => item.kind === 'file') +const isHandlingFileDrag = (event: DragEvent, application: WebApplication) => { + const items = event.dataTransfer?.items + + if (!items) { + return false + } + + return Array.from(items).some((item) => { + const isFile = item.kind === 'file' + const fileName = item.getAsFile()?.name || '' + const isBackupMetadataFile = application.fileBackups?.isFileNameMetadataFile(fileName) + return isFile && !isBackupMetadataFile + }) +} export const AttachedFilesButton: FunctionComponent = observer( ({ application, appState, onClickPreprocessing }) => { @@ -229,16 +241,19 @@ export const AttachedFilesButton: FunctionComponent = observer( const [isDraggingFiles, setIsDraggingFiles] = useState(false) const dragCounter = useRef(0) - const handleDrag = (event: DragEvent) => { - if (isHandlingFileDrag(event)) { - event.preventDefault() - event.stopPropagation() - } - } + const handleDrag = useCallback( + (event: DragEvent) => { + if (isHandlingFileDrag(event, application)) { + event.preventDefault() + event.stopPropagation() + } + }, + [application], + ) const handleDragIn = useCallback( (event: DragEvent) => { - if (!isHandlingFileDrag(event)) { + if (!isHandlingFileDrag(event, application)) { return } @@ -263,29 +278,32 @@ export const AttachedFilesButton: FunctionComponent = observer( } } }, - [open, toggleAttachedFilesMenu], + [open, toggleAttachedFilesMenu, application], ) - const handleDragOut = (event: DragEvent) => { - if (!isHandlingFileDrag(event)) { - return - } + const handleDragOut = useCallback( + (event: DragEvent) => { + if (!isHandlingFileDrag(event, application)) { + return + } - event.preventDefault() - event.stopPropagation() + event.preventDefault() + event.stopPropagation() - dragCounter.current = dragCounter.current - 1 + dragCounter.current = dragCounter.current - 1 - if (dragCounter.current > 0) { - return - } + if (dragCounter.current > 0) { + return + } - setIsDraggingFiles(false) - } + setIsDraggingFiles(false) + }, + [application], + ) const handleDrop = useCallback( (event: DragEvent) => { - if (!isHandlingFileDrag(event)) { + if (!isHandlingFileDrag(event, application)) { return } @@ -325,7 +343,7 @@ export const AttachedFilesButton: FunctionComponent = observer( dragCounter.current = 0 } }, - [appState.files, appState.features.hasFiles, attachFileToNote, currentTab], + [appState.files, appState.features.hasFiles, attachFileToNote, currentTab, application], ) useEffect(() => { @@ -340,7 +358,7 @@ export const AttachedFilesButton: FunctionComponent = observer( window.removeEventListener('dragover', handleDrag) window.removeEventListener('drop', handleDrop) } - }, [handleDragIn, handleDrop]) + }, [handleDragIn, handleDrop, handleDrag, handleDragOut]) return (
diff --git a/app/assets/javascripts/Components/Preferences/Panes/Backups/FileBackups.tsx b/app/assets/javascripts/Components/Preferences/Panes/Backups/FileBackups.tsx index 7d5e84fb52e..4e46a545dd0 100644 --- a/app/assets/javascripts/Components/Preferences/Panes/Backups/FileBackups.tsx +++ b/app/assets/javascripts/Components/Preferences/Panes/Backups/FileBackups.tsx @@ -9,11 +9,13 @@ import { } from '@/Components/Preferences/PreferencesComponents' import { useCallback, useEffect, useState } from 'preact/hooks' import { Button } from '@/Components/Button/Button' -import { isDesktopDevice } from '@standardnotes/snjs' +import { FileBackupMetadataFile, isDesktopDevice } from '@standardnotes/snjs' import { Switch } from '@/Components/Switch' import { HorizontalSeparator } from '@/Components/Shared/HorizontalSeparator' import { EncryptionStatusItem } from '../Security/Encryption' import { Icon } from '@/Components/Icon' +import { StreamingFileApi } from '@standardnotes/filepicker' +import { FunctionComponent } from 'preact' type Props = { application: WebApplication @@ -130,6 +132,7 @@ export const FileBackups = observer(({ application }: Props) => { To decrypt a backup file, drag and drop the file's respective metadata.sn.json file here. + )} @@ -137,3 +140,119 @@ export const FileBackups = observer(({ application }: Props) => { ) }) + +const isHandlingBackupDrag = (event: DragEvent, application: WebApplication) => { + const items = event.dataTransfer?.items + + if (!items) { + return false + } + + return Array.from(items).every((item) => { + const isFile = item.kind === 'file' + const fileName = item.getAsFile()?.name || '' + const isBackupMetadataFile = application.fileBackups?.isFileNameMetadataFile(fileName) + return isFile && isBackupMetadataFile + }) +} + +export const BackupsDropZone: FunctionComponent = ({ application }) => { + const handleMetadataFileDrop = useCallback( + async (metadata: FileBackupMetadataFile) => { + const decryptedFile = await application.files.decryptBackupMetadataFile(metadata) + + if (!decryptedFile) { + return + } + + const fileSystem = new StreamingFileApi() + await application.files.selectFileBackupAndSaveDecrypted(decryptedFile, fileSystem) + }, + [application], + ) + + const handleDrag = useCallback( + (event: DragEvent) => { + if (isHandlingBackupDrag(event, application)) { + event.preventDefault() + event.stopPropagation() + } + }, + [application], + ) + + const handleDragIn = useCallback( + (event: DragEvent) => { + if (!isHandlingBackupDrag(event, application)) { + return + } + + event.preventDefault() + event.stopPropagation() + }, + [application], + ) + + const handleDragOut = useCallback( + (event: DragEvent) => { + if (!isHandlingBackupDrag(event, application)) { + return + } + + event.preventDefault() + event.stopPropagation() + }, + [application], + ) + + const handleDrop = useCallback( + async (event: DragEvent) => { + if (!isHandlingBackupDrag(event, application)) { + return + } + + event.preventDefault() + event.stopPropagation() + + const items = event.dataTransfer?.items + + if (!items || items.length === 0) { + return + } + + const item = items[0] + const file = item.getAsFile() + if (!file) { + return + } + + const text = await file.text() + + try { + const metadata = JSON.parse(text) as FileBackupMetadataFile + void handleMetadataFileDrop(metadata) + } catch (error) { + console.error(error) + } + + event.dataTransfer.clearData() + }, + [application, handleMetadataFileDrop], + ) + + useEffect(() => { + window.addEventListener('dragenter', handleDragIn) + window.addEventListener('dragleave', handleDragOut) + window.addEventListener('dragover', handleDrag) + window.addEventListener('drop', handleDrop) + + return () => { + window.removeEventListener('dragenter', handleDragIn) + window.removeEventListener('dragleave', handleDragOut) + window.removeEventListener('dragover', handleDrag) + window.removeEventListener('drop', handleDrop) + } + }, [handleDragIn, handleDrop, handleDrag, handleDragOut]) + + return null +} diff --git a/app/assets/javascripts/Device/DesktopSnjsExports.ts b/app/assets/javascripts/Device/DesktopSnjsExports.ts index 394bbb61d6a..c6728d84796 100644 --- a/app/assets/javascripts/Device/DesktopSnjsExports.ts +++ b/app/assets/javascripts/Device/DesktopSnjsExports.ts @@ -6,5 +6,4 @@ export { DesktopClientRequiresWebMethods, FileBackupsMapping, FileBackupsDevice, - FileBackupMetadataName, } from '@standardnotes/snjs' diff --git a/app/assets/javascripts/UIModels/Application.ts b/app/assets/javascripts/UIModels/Application.ts index d0de6a1221b..8fef1e5ec3e 100644 --- a/app/assets/javascripts/UIModels/Application.ts +++ b/app/assets/javascripts/UIModels/Application.ts @@ -17,9 +17,7 @@ import { Runtime, DesktopDeviceInterface, isDesktopDevice, - SNFile, } from '@standardnotes/snjs' -import { StreamingFileApi } from '@standardnotes/filepicker' type WebServices = { appState: AppState @@ -146,14 +144,6 @@ export class WebApplication extends SNApplication { return this.webServices.io } - public async triggerBackupFileDemo(file: SNFile) { - const fileSystem = new StreamingFileApi() - - const result = await this.files.selectFileBackupAndSaveDecrypted(file.content, fileSystem) - - console.log('triggerBackupFileDemo result', result) - } - async checkForSecurityUpdate() { return this.protocolUpgradeAvailable() } From 36014c25d987a64c1b68ab43d99fa8b3750ca463 Mon Sep 17 00:00:00 2001 From: Mo Date: Thu, 12 May 2022 12:08:15 -0500 Subject: [PATCH 4/8] feat: backup decryption wizard --- .../Preferences/Panes/Backups/FileBackups.tsx | 146 +++++++++++++++--- 1 file changed, 121 insertions(+), 25 deletions(-) diff --git a/app/assets/javascripts/Components/Preferences/Panes/Backups/FileBackups.tsx b/app/assets/javascripts/Components/Preferences/Panes/Backups/FileBackups.tsx index 4e46a545dd0..b70d83a33be 100644 --- a/app/assets/javascripts/Components/Preferences/Panes/Backups/FileBackups.tsx +++ b/app/assets/javascripts/Components/Preferences/Panes/Backups/FileBackups.tsx @@ -7,9 +7,9 @@ import { Text, Subtitle, } from '@/Components/Preferences/PreferencesComponents' -import { useCallback, useEffect, useState } from 'preact/hooks' +import { useCallback, useEffect, useMemo, useState } from 'preact/hooks' import { Button } from '@/Components/Button/Button' -import { FileBackupMetadataFile, isDesktopDevice } from '@standardnotes/snjs' +import { FileBackupMetadataFile, FileContent, FileHandleRead, isDesktopDevice } from '@standardnotes/snjs' import { Switch } from '@/Components/Switch' import { HorizontalSeparator } from '@/Components/Shared/HorizontalSeparator' import { EncryptionStatusItem } from '../Security/Encryption' @@ -125,17 +125,12 @@ export const FileBackups = observer(({ application }: Props) => {
- - - - - - To decrypt a backup file, drag and drop the file's respective metadata.sn.json file here. - - - )} + + + + ) @@ -157,19 +152,55 @@ const isHandlingBackupDrag = (event: DragEvent, application: WebApplication) => } export const BackupsDropZone: FunctionComponent = ({ application }) => { - const handleMetadataFileDrop = useCallback( - async (metadata: FileBackupMetadataFile) => { - const decryptedFile = await application.files.decryptBackupMetadataFile(metadata) + const [droppedFile, setDroppedFile] = useState(undefined) + const [decryptedFileContent, setDecryptedFileContent] = useState(undefined) + const [binaryFile, setBinaryFile] = useState(undefined) + const [isSavingAsDecrypted, setIsSavingAsDecrypted] = useState(false) - if (!decryptedFile) { - return - } + const fileSystem = useMemo(() => new StreamingFileApi(), []) - const fileSystem = new StreamingFileApi() - await application.files.selectFileBackupAndSaveDecrypted(decryptedFile, fileSystem) - }, - [application], - ) + useEffect(() => { + if (droppedFile) { + void application.files.decryptBackupMetadataFile(droppedFile).then(setDecryptedFileContent) + } else { + setDecryptedFileContent(undefined) + } + }, [droppedFile, application]) + + const chooseRelatedBinaryFile = useCallback(async () => { + const selection = await application.files.selectFile(fileSystem) + + if (selection === 'aborted' || selection === 'failed') { + return + } + + setBinaryFile(selection) + }, [application, fileSystem]) + + const downloadBinaryFileAsDecrypted = useCallback(async () => { + if (!decryptedFileContent || !binaryFile) { + return + } + + setIsSavingAsDecrypted(true) + + const result = await application.files.readBackupFileAndSaveDecrypted(binaryFile, decryptedFileContent, fileSystem) + + if (result === 'success') { + void application.alertService.alert( + `${decryptedFileContent.name} has been successfully decrypted and saved to your chosen directory.`, + ) + setBinaryFile(undefined) + setDecryptedFileContent(undefined) + setDroppedFile(undefined) + } else if (result === 'failed') { + void application.alertService.alert( + 'Unable to save file to local directory. This may be caused by failure to decrypt, or failure to save the file locally.', + ) + } + + setIsSavingAsDecrypted(false) + }, [decryptedFileContent, application, binaryFile, fileSystem]) const handleDrag = useCallback( (event: DragEvent) => { @@ -222,6 +253,7 @@ export const BackupsDropZone: FunctionComponent = ({ application }) => { const item = items[0] const file = item.getAsFile() + if (!file) { return } @@ -230,14 +262,14 @@ export const BackupsDropZone: FunctionComponent = ({ application }) => { try { const metadata = JSON.parse(text) as FileBackupMetadataFile - void handleMetadataFileDrop(metadata) + setDroppedFile(metadata) } catch (error) { console.error(error) } event.dataTransfer.clearData() }, - [application, handleMetadataFileDrop], + [application], ) useEffect(() => { @@ -254,5 +286,69 @@ export const BackupsDropZone: FunctionComponent = ({ application }) => { } }, [handleDragIn, handleDrop, handleDrag, handleDragOut]) - return null + if (!droppedFile) { + return ( + + To decrypt a backup file, drag and drop the file's respective metadata.sn.json file here. + + ) + } + + return ( + <> + + {!decryptedFileContent && Attempting to decrypt metadata file...} + + {decryptedFileContent && ( + <> + Backup Decryption + + + Successfully decrypted metadata file for {decryptedFileContent.name}. + + + + +
+
+ 1. Choose related data file + {droppedFile.file.uuid} +
+
+
+
+ + + +
+ 2. Decrypt and save file to your computer + +
+ +
+
+ + )} +
+ + ) } From 8e74e7a672c2caa24d62b826585044af32fa2da7 Mon Sep 17 00:00:00 2001 From: Mo Date: Thu, 12 May 2022 12:33:53 -0500 Subject: [PATCH 5/8] chore: cleanup --- app/assets/javascripts/UIModels/AppState/PreferencesState.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/assets/javascripts/UIModels/AppState/PreferencesState.ts b/app/assets/javascripts/UIModels/AppState/PreferencesState.ts index ea81b09940e..a576c770e69 100644 --- a/app/assets/javascripts/UIModels/AppState/PreferencesState.ts +++ b/app/assets/javascripts/UIModels/AppState/PreferencesState.ts @@ -4,7 +4,7 @@ import { action, computed, makeObservable, observable } from 'mobx' const DEFAULT_PANE = 'account' export class PreferencesState { - private _open = true + private _open = false currentPane: PreferenceId = DEFAULT_PANE constructor() { From e848a252d203b9346f80d35cd418868b03a0c2ac Mon Sep 17 00:00:00 2001 From: Mo Date: Thu, 12 May 2022 17:10:41 -0500 Subject: [PATCH 6/8] chore: fix types --- .../ChallengeModal/ChallengeModal.tsx | 2 +- .../Preferences/Panes/Backups/FileBackups.tsx | 17 +++++++++-------- app/assets/javascripts/Database.ts | 4 ++-- .../javascripts/Device/StartApplication.ts | 4 ++-- .../javascripts/Device/WebOrDesktopDevice.ts | 2 +- .../javascripts/UIModels/AppState/AppState.ts | 4 ++-- 6 files changed, 17 insertions(+), 16 deletions(-) diff --git a/app/assets/javascripts/Components/ChallengeModal/ChallengeModal.tsx b/app/assets/javascripts/Components/ChallengeModal/ChallengeModal.tsx index d961747fecf..c3210f5af72 100644 --- a/app/assets/javascripts/Components/ChallengeModal/ChallengeModal.tsx +++ b/app/assets/javascripts/Components/ChallengeModal/ChallengeModal.tsx @@ -90,7 +90,7 @@ export const ChallengeModal: FunctionComponent = ({ const valuesToProcess: ChallengeValue[] = [] for (const inputValue of Object.values(validatedValues)) { const rawValue = inputValue.value - const value = new ChallengeValue(inputValue.prompt, rawValue) + const value = { prompt: inputValue.prompt, value: rawValue } valuesToProcess.push(value) } const processingPrompts = valuesToProcess.map((v) => v.prompt) diff --git a/app/assets/javascripts/Components/Preferences/Panes/Backups/FileBackups.tsx b/app/assets/javascripts/Components/Preferences/Panes/Backups/FileBackups.tsx index b70d83a33be..5c7a9343988 100644 --- a/app/assets/javascripts/Components/Preferences/Panes/Backups/FileBackups.tsx +++ b/app/assets/javascripts/Components/Preferences/Panes/Backups/FileBackups.tsx @@ -9,7 +9,7 @@ import { } from '@/Components/Preferences/PreferencesComponents' import { useCallback, useEffect, useMemo, useState } from 'preact/hooks' import { Button } from '@/Components/Button/Button' -import { FileBackupMetadataFile, FileContent, FileHandleRead, isDesktopDevice } from '@standardnotes/snjs' +import { FileBackupMetadataFile, FileContent, FileHandleRead } from '@standardnotes/snjs' import { Switch } from '@/Components/Switch' import { HorizontalSeparator } from '@/Components/Shared/HorizontalSeparator' import { EncryptionStatusItem } from '../Security/Encryption' @@ -24,18 +24,19 @@ type Props = { export const FileBackups = observer(({ application }: Props) => { const [backupsEnabled, setBackupsEnabled] = useState(false) const [backupsLocation, setBackupsLocation] = useState('') - const [backupsService, _] = useState(application.fileBackups) + const backupsService = useMemo(() => application.fileBackups, [application.fileBackups]) - if (!isDesktopDevice(application.deviceInterface)) { + if (!backupsService) { return ( <> File Backups - - Automatically save encrypted backups of files uploaded to any device to this computer. To use file - backups, use the Standard Notes desktop application. - + Automatically save encrypted backups of files uploaded to any device to this computer. + To enable file backups, use the Standard Notes desktop application. + + + @@ -146,7 +147,7 @@ const isHandlingBackupDrag = (event: DragEvent, application: WebApplication) => return Array.from(items).every((item) => { const isFile = item.kind === 'file' const fileName = item.getAsFile()?.name || '' - const isBackupMetadataFile = application.fileBackups?.isFileNameMetadataFile(fileName) + const isBackupMetadataFile = application.files.isFileNameMetadataFile(fileName) return isFile && isBackupMetadataFile }) } diff --git a/app/assets/javascripts/Database.ts b/app/assets/javascripts/Database.ts index 640c0ee95be..9de3e9dbf50 100644 --- a/app/assets/javascripts/Database.ts +++ b/app/assets/javascripts/Database.ts @@ -21,7 +21,7 @@ export class Database { constructor(public databaseName: string, private alertService: AlertService) {} - public deinit() { + public deinit(): void { ;(this.alertService as any) = undefined this.db = undefined } @@ -29,7 +29,7 @@ export class Database { /** * Relinquishes the lock and allows db operations to proceed */ - public unlock() { + public unlock(): void { this.locked = false } diff --git a/app/assets/javascripts/Device/StartApplication.ts b/app/assets/javascripts/Device/StartApplication.ts index de14ea2c96f..b0d8f35ee88 100644 --- a/app/assets/javascripts/Device/StartApplication.ts +++ b/app/assets/javascripts/Device/StartApplication.ts @@ -1,8 +1,8 @@ -import { WebOrDesktopDeviceInterface } from '@standardnotes/snjs' +import { WebOrDesktopDevice } from './WebOrDesktopDevice' export type StartApplication = ( defaultSyncServerHost: string, - device: WebOrDesktopDeviceInterface, + device: WebOrDesktopDevice, enableUnfinishedFeatures: boolean, webSocketUrl: string, ) => Promise diff --git a/app/assets/javascripts/Device/WebOrDesktopDevice.ts b/app/assets/javascripts/Device/WebOrDesktopDevice.ts index 72f5cac0524..66c63c9226c 100644 --- a/app/assets/javascripts/Device/WebOrDesktopDevice.ts +++ b/app/assets/javascripts/Device/WebOrDesktopDevice.ts @@ -18,7 +18,7 @@ export abstract class WebOrDesktopDevice implements WebOrDesktopDeviceInterface abstract environment: Environment - setApplication(application: SNApplication) { + setApplication(application: SNApplication): void { const database = new Database(application.identifier, application.alertService) this.databases.push(database) diff --git a/app/assets/javascripts/UIModels/AppState/AppState.ts b/app/assets/javascripts/UIModels/AppState/AppState.ts index 5402695f2bb..b959e7cecc3 100644 --- a/app/assets/javascripts/UIModels/AppState/AppState.ts +++ b/app/assets/javascripts/UIModels/AppState/AppState.ts @@ -15,6 +15,7 @@ import { removeFromArray, Uuid, PayloadEmitSource, + WebOrDesktopDeviceInterface, } from '@standardnotes/snjs' import { action, computed, IReactionDisposer, makeObservable, observable, reaction } from 'mobx' import { ActionsMenuState } from './ActionsMenuState' @@ -31,7 +32,6 @@ import { SearchOptionsState } from './SearchOptionsState' import { SubscriptionState } from './SubscriptionState' import { SyncState } from './SyncState' import { TagsState } from './TagsState' -import { WebOrDesktopDevice } from '@/Device/WebOrDesktopDevice' import { FilePreviewModalState } from './FilePreviewModalState' export enum AppStateEvent { @@ -93,7 +93,7 @@ export class AppState { private readonly tagChangedDisposer: IReactionDisposer - constructor(application: WebApplication, private device: WebOrDesktopDevice) { + constructor(application: WebApplication, private device: WebOrDesktopDeviceInterface) { this.application = application this.notes = new NotesState( application, From 240ae1a1124ec0dbdc5a97f26e43fb60f709b3cd Mon Sep 17 00:00:00 2001 From: Mo Date: Thu, 12 May 2022 17:34:01 -0500 Subject: [PATCH 7/8] chore: upgrade deps --- package.json | 6 +-- yarn.lock | 122 ++++++++++++++++++++++++++------------------------- 2 files changed, 65 insertions(+), 63 deletions(-) diff --git a/package.json b/package.json index 66beb7bfbfc..ac791b0866c 100644 --- a/package.json +++ b/package.json @@ -70,10 +70,10 @@ "@reach/tooltip": "^0.16.2", "@reach/visually-hidden": "^0.16.0", "@standardnotes/components": "1.8.0", - "@standardnotes/filepicker": "1.13.7", + "@standardnotes/filepicker": "1.14.0", "@standardnotes/sncrypto-web": "1.9.2", - "@standardnotes/snjs": "2.106.9", - "@standardnotes/stylekit": "5.25.0", + "@standardnotes/snjs": "2.107.0", + "@standardnotes/stylekit": "5.26.0", "@zip.js/zip.js": "^2.4.10", "mobx": "^6.5.0", "mobx-react-lite": "^3.3.0", diff --git a/yarn.lock b/yarn.lock index 6b6aee0db5b..ebc0d61545d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2453,76 +2453,78 @@ eslint-plugin-prettier "^4.0.0" prettier "^2.6.2" -"@standardnotes/domain-events@^2.28.1": - version "2.28.1" - resolved "https://registry.yarnpkg.com/@standardnotes/domain-events/-/domain-events-2.28.1.tgz#473350cf867b5af9f18d19cdf5e772f37f11a621" - integrity sha512-1IjE0HA30AwzZ/7RZkVYUQNgWbkRSB8h3xFsbuCLvBUfl/lXrDlHvD2CZMUGKPXlMHBjRivW/r8f7FjDlXNJ9w== +"@standardnotes/domain-events@^2.28.3": + version "2.28.3" + resolved "https://registry.yarnpkg.com/@standardnotes/domain-events/-/domain-events-2.28.3.tgz#96e56009faf6626bd0cf967c7db9969fd08e7dd7" + integrity sha512-ehWn+6Ii1R0BcNX+ro02eIUZ6j8kiu9hR4Rvd59NS7NNsnAJCTyUmuDxURchwLNZTT4OSeqDBuvp98PiWn5Adg== dependencies: "@standardnotes/auth" "^3.18.13" - "@standardnotes/features" "^1.42.0" + "@standardnotes/features" "^1.42.1" -"@standardnotes/encryption@^1.6.10": - version "1.6.10" - resolved "https://registry.yarnpkg.com/@standardnotes/encryption/-/encryption-1.6.10.tgz#054bb07e2d49e8b6ec690219c71e4d567838d8aa" - integrity sha512-S89WpBqEPnruFV4vMn8etfxKUAGL3StN+I70ujz61OI5ReZhOEf/DobQBFm2u/lSTtnvleEdThLsRp5QZZH0kA== +"@standardnotes/encryption@^1.7.0": + version "1.7.0" + resolved "https://registry.yarnpkg.com/@standardnotes/encryption/-/encryption-1.7.0.tgz#3012bed5283e23ddeaae01e9fbaa80c24e8af42d" + integrity sha512-gBVcTCZ53Eg3/2eZkVrBrS98BU95CR2h23ofnNGhCcy0FlWwv6fAqupSsPufOdce8gh27gF0mhRbi80ljYxCUA== dependencies: - "@standardnotes/models" "^1.6.10" - "@standardnotes/responses" "^1.6.18" - "@standardnotes/services" "^1.10.9" + "@standardnotes/models" "^1.8.0" + "@standardnotes/responses" "^1.6.19" + "@standardnotes/services" "^1.11.0" -"@standardnotes/features@^1.42.0": - version "1.42.0" - resolved "https://registry.yarnpkg.com/@standardnotes/features/-/features-1.42.0.tgz#154171a8436f68bef1f9e797415feea04f60b0c2" - integrity sha512-W79WVV68jWwG0CtiYcT/51N7/IIOCPbkL5UBjKLcZ3XNUoptVTFYvHd+UDhdkc3DuMPghjDghD7pQS7W4KdbVA== +"@standardnotes/features@^1.42.1": + version "1.42.1" + resolved "https://registry.yarnpkg.com/@standardnotes/features/-/features-1.42.1.tgz#29d1f753f445328c485ccc0ea95d557b9299c131" + integrity sha512-wMm0N112HeIa+VskpmPMfL5eHh3U3s+aCpxiq0HvITD+3BOoQgbbAQ+O+FjpwwhvN8mfBAM+LvNjksXFHhqoJg== dependencies: "@standardnotes/auth" "^3.18.13" "@standardnotes/common" "^1.19.8" -"@standardnotes/filepicker@1.13.7", "@standardnotes/filepicker@^1.13.7": - version "1.13.7" - resolved "https://registry.yarnpkg.com/@standardnotes/filepicker/-/filepicker-1.13.7.tgz#8e9f0736defa6630e8c3fb541d02a9286d8d8875" - integrity sha512-SOqgYqzerhCg2d47j+QkzOBISN9KHU5uxsV7+Heaxag/jJFfyplxWYOskNJzFm5DtgSwV57Y753hIQN8S7YTKA== +"@standardnotes/filepicker@1.14.0", "@standardnotes/filepicker@^1.14.0": + version "1.14.0" + resolved "https://registry.yarnpkg.com/@standardnotes/filepicker/-/filepicker-1.14.0.tgz#f1ea5e66a453cfc2131bbf665f676b430f649cf2" + integrity sha512-E/frksUJhHbMVlLNu4UdHLIdFNuQe9Co8dq/WQgeK/X+Ic+5t5Jm91c4GA1LvCUVF1vhYl/Vo76LY8gE1J2mfg== dependencies: "@standardnotes/common" "^1.19.8" + "@standardnotes/services" "^1.11.0" "@standardnotes/utils" "^1.6.6" -"@standardnotes/files@^1.0.13": - version "1.0.13" - resolved "https://registry.yarnpkg.com/@standardnotes/files/-/files-1.0.13.tgz#45feff6f34c49f6a3a845c3b6e71cb25e0132fad" - integrity sha512-MISbGjXnjAIYIVUqalw3pKMoxXh5sVBbXYrWcpB8okiq/EcLHiVPhGkedxqjkELqzlM/fa3DkougmJBKWh43fQ== +"@standardnotes/files@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@standardnotes/files/-/files-1.1.0.tgz#c2499f50fdc31addf3b0307388d4c78420415dfe" + integrity sha512-O4cB8nxBTSLPUCoLyglzbj5oshaJ6hKuBleAWPaC+treaiwpWVyfxn3TF7G4MejlNUMIrGfb566YAZ3R5YVfGg== dependencies: - "@standardnotes/models" "^1.6.10" - "@standardnotes/responses" "^1.6.18" - "@standardnotes/services" "^1.10.9" + "@standardnotes/encryption" "^1.7.0" + "@standardnotes/models" "^1.8.0" + "@standardnotes/responses" "^1.6.19" + "@standardnotes/services" "^1.11.0" "@standardnotes/utils" "^1.6.6" -"@standardnotes/models@^1.6.10": - version "1.6.10" - resolved "https://registry.yarnpkg.com/@standardnotes/models/-/models-1.6.10.tgz#3a6142b849ed1d7973611011be44dcbefa6df461" - integrity sha512-EpxDvB7GVN+suPYCQ5QmDq/1NezPBVeAzfg56O1I5I8yhl1cyaqLmtSHcPGdgfgnQiio2kbTA16cta3YxtnG/g== +"@standardnotes/models@^1.8.0": + version "1.8.0" + resolved "https://registry.yarnpkg.com/@standardnotes/models/-/models-1.8.0.tgz#b295ba16f0392549eb79cd02d67b7e2455f2d9be" + integrity sha512-Tl1EZNk/8axTSTb6p7kU9qfS6Fyqxyc+/MxRGj0th+Ry70/UHvRV7iDGT6FKBzBUQR8/5hDK0JG1pDzUSRAhhQ== dependencies: - "@standardnotes/features" "^1.42.0" - "@standardnotes/responses" "^1.6.18" + "@standardnotes/features" "^1.42.1" + "@standardnotes/responses" "^1.6.19" "@standardnotes/utils" "^1.6.6" -"@standardnotes/responses@^1.6.18": - version "1.6.18" - resolved "https://registry.yarnpkg.com/@standardnotes/responses/-/responses-1.6.18.tgz#ad448cfe78449f7cef27b9ff169635ef6dca3476" - integrity sha512-Onmhxd8zKbFGJ3ECbD1yuGqiY58JV/FE7PymNT8WL6P1CuThzqQ3V6hS10tswJgbu50A570i1uBg7i+WjyaHiA== +"@standardnotes/responses@^1.6.19": + version "1.6.19" + resolved "https://registry.yarnpkg.com/@standardnotes/responses/-/responses-1.6.19.tgz#ac8bf70f35cd7a5649e52b280f53a2bd97f39b76" + integrity sha512-Oj9CS52UCJhAZCqCN6GReqQ9zosgCV2HZ1uaJLXEDxNeNTxDnPOU9C7CH7tXmA3zoQ44pFpQbeAuee8ZnlH3XQ== dependencies: "@standardnotes/auth" "^3.18.13" "@standardnotes/common" "^1.19.8" - "@standardnotes/features" "^1.42.0" + "@standardnotes/features" "^1.42.1" -"@standardnotes/services@^1.10.9": - version "1.10.9" - resolved "https://registry.yarnpkg.com/@standardnotes/services/-/services-1.10.9.tgz#44a79c8235bc7eaf55d66fc1e8df90ce1ecbceb7" - integrity sha512-009VuEWbSV9ULaabFodXIUnzLkOxuy8QTNNrXusQg30kY02dzxLqhh0F+t5YLc1SbVkk+n2/IahPTlUUaCKHQQ== +"@standardnotes/services@^1.11.0": + version "1.11.0" + resolved "https://registry.yarnpkg.com/@standardnotes/services/-/services-1.11.0.tgz#7b214093a4d5ef540ce2b6a40cf0f83517c1a2da" + integrity sha512-bR084UoR7EzTC6GD5QxFi7FmcoqhOhZmdeWIwByEOQEDeWG9h0i/lNZo4pAyYykDDv8Z/U3+kNjify4wI+ksZw== dependencies: "@standardnotes/auth" "^3.18.13" "@standardnotes/common" "^1.19.8" - "@standardnotes/models" "^1.6.10" - "@standardnotes/responses" "^1.6.18" + "@standardnotes/models" "^1.8.0" + "@standardnotes/responses" "^1.6.19" "@standardnotes/utils" "^1.6.6" "@standardnotes/settings@^1.14.3": @@ -2544,29 +2546,29 @@ buffer "^6.0.3" libsodium-wrappers "^0.7.9" -"@standardnotes/snjs@2.106.9": - version "2.106.9" - resolved "https://registry.yarnpkg.com/@standardnotes/snjs/-/snjs-2.106.9.tgz#34197275ec2ebfd4522c63c091b47b5c974fcaac" - integrity sha512-B/kujDluQNs/xYpRaWYvEWpfflYnLhl4ugW3r15WgcLJSjSLreBOVLaRWXhALl8I4e63rkRwMKeCiwrL7auXWg== +"@standardnotes/snjs@2.107.0": + version "2.107.0" + resolved "https://registry.yarnpkg.com/@standardnotes/snjs/-/snjs-2.107.0.tgz#1254f8ae439023e6dab60dae73b4c8435bf117d2" + integrity sha512-3FOxSDlRADoQoSr3gSvAA9eFQbvo7vOaeeLXtoN7ElUUtG78DadPORtwyQtsUee9EfxQnlaWBXVImPAoy2LUew== dependencies: "@standardnotes/auth" "^3.18.13" "@standardnotes/common" "^1.19.8" - "@standardnotes/domain-events" "^2.28.1" - "@standardnotes/encryption" "^1.6.10" - "@standardnotes/features" "^1.42.0" - "@standardnotes/filepicker" "^1.13.7" - "@standardnotes/files" "^1.0.13" - "@standardnotes/models" "^1.6.10" - "@standardnotes/responses" "^1.6.18" - "@standardnotes/services" "^1.10.9" + "@standardnotes/domain-events" "^2.28.3" + "@standardnotes/encryption" "^1.7.0" + "@standardnotes/features" "^1.42.1" + "@standardnotes/filepicker" "^1.14.0" + "@standardnotes/files" "^1.1.0" + "@standardnotes/models" "^1.8.0" + "@standardnotes/responses" "^1.6.19" + "@standardnotes/services" "^1.11.0" "@standardnotes/settings" "^1.14.3" "@standardnotes/sncrypto-common" "^1.8.2" "@standardnotes/utils" "^1.6.6" -"@standardnotes/stylekit@5.25.0": - version "5.25.0" - resolved "https://registry.yarnpkg.com/@standardnotes/stylekit/-/stylekit-5.25.0.tgz#dd79f8c7bd9c2a6f810bc6283f76928dd268f9e8" - integrity sha512-zc/qsWJ6UYNFZeEPpifJbN19GXZy2VBrha2njvnmLy+Yr9XZuDBR+kwzBvvDYIb33OijkATNQ4EyUja245edKQ== +"@standardnotes/stylekit@5.26.0": + version "5.26.0" + resolved "https://registry.yarnpkg.com/@standardnotes/stylekit/-/stylekit-5.26.0.tgz#76c1fd2d9db872fe67cef147a9956c38d49a7078" + integrity sha512-CLR3sXp3NARkuJOw/zYwYBrDjxYQvS/M+4B76Fn3vNncHCvu1nRFos78yrvvIjYf/Ck4IqChxJC57HBP2Fojpw== dependencies: "@nanostores/preact" "^0.1.3" "@reach/listbox" "^0.16.2" From bb4bdc7246c7eb584c62bdbe319993d0fd3c968b Mon Sep 17 00:00:00 2001 From: Mo Date: Thu, 12 May 2022 17:42:31 -0500 Subject: [PATCH 8/8] fix: function call --- .../Components/AttachedFilesPopover/AttachedFilesButton.tsx | 2 +- .../Components/Preferences/Panes/Backups/FileBackups.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/assets/javascripts/Components/AttachedFilesPopover/AttachedFilesButton.tsx b/app/assets/javascripts/Components/AttachedFilesPopover/AttachedFilesButton.tsx index 7125d30ba1a..5877363626d 100644 --- a/app/assets/javascripts/Components/AttachedFilesPopover/AttachedFilesButton.tsx +++ b/app/assets/javascripts/Components/AttachedFilesPopover/AttachedFilesButton.tsx @@ -33,7 +33,7 @@ const isHandlingFileDrag = (event: DragEvent, application: WebApplication) => { return Array.from(items).some((item) => { const isFile = item.kind === 'file' const fileName = item.getAsFile()?.name || '' - const isBackupMetadataFile = application.fileBackups?.isFileNameMetadataFile(fileName) + const isBackupMetadataFile = application.files.isFileNameFileBackupMetadataFile(fileName) return isFile && !isBackupMetadataFile }) } diff --git a/app/assets/javascripts/Components/Preferences/Panes/Backups/FileBackups.tsx b/app/assets/javascripts/Components/Preferences/Panes/Backups/FileBackups.tsx index 5c7a9343988..e31868b7937 100644 --- a/app/assets/javascripts/Components/Preferences/Panes/Backups/FileBackups.tsx +++ b/app/assets/javascripts/Components/Preferences/Panes/Backups/FileBackups.tsx @@ -147,7 +147,7 @@ const isHandlingBackupDrag = (event: DragEvent, application: WebApplication) => return Array.from(items).every((item) => { const isFile = item.kind === 'file' const fileName = item.getAsFile()?.name || '' - const isBackupMetadataFile = application.files.isFileNameMetadataFile(fileName) + const isBackupMetadataFile = application.files.isFileNameFileBackupMetadataFile(fileName) return isFile && isBackupMetadataFile }) }