feat: download and preview files from local backups automatically, if a local backup is available (#2076)

This commit is contained in:
Mo
2022-12-01 11:56:28 -06:00
committed by GitHub
parent e07fed267f
commit 28e43d37c0
34 changed files with 739 additions and 110 deletions

View File

@@ -7,4 +7,7 @@ export {
FileBackupsMapping,
FileBackupsDevice,
FileBackupRecord,
FileBackupReadToken,
FileBackupReadChunkResponse,
FileDownloadProgress,
} from '@standardnotes/snjs'

View File

@@ -1,6 +1,11 @@
import { WebApplication } from '@/Application/Application'
import { concatenateUint8Arrays } from '@/Utils'
import { ApplicationEvent, FileItem } from '@standardnotes/snjs'
import {
ApplicationEvent,
FileDownloadProgress,
FileItem,
fileProgressToHumanReadableString,
} from '@standardnotes/snjs'
import { useEffect, useMemo, useState } from 'react'
import Spinner from '@/Components/Spinner/Spinner'
import FilePreviewError from './FilePreviewError'
@@ -24,7 +29,7 @@ const FilePreview = ({ file, application, isEmbeddedInSuper = false, imageZoomLe
}, [file.mimeType])
const [isDownloading, setIsDownloading] = useState(true)
const [downloadProgress, setDownloadProgress] = useState(0)
const [downloadProgress, setDownloadProgress] = useState<FileDownloadProgress | undefined>()
const [downloadedBytes, setDownloadedBytes] = useState<Uint8Array>()
useEffect(() => {
@@ -46,7 +51,7 @@ const FilePreview = ({ file, application, isEmbeddedInSuper = false, imageZoomLe
useEffect(() => {
if (!isFilePreviewable || !isAuthorized) {
setIsDownloading(false)
setDownloadProgress(0)
setDownloadProgress(undefined)
setDownloadedBytes(undefined)
return
}
@@ -60,15 +65,18 @@ const FilePreview = ({ file, application, isEmbeddedInSuper = false, imageZoomLe
try {
const chunks: Uint8Array[] = []
setDownloadProgress(0)
await application.files.downloadFile(file, async (decryptedChunk, progress) => {
setDownloadProgress(undefined)
const error = await application.files.downloadFile(file, async (decryptedChunk, progress) => {
chunks.push(decryptedChunk)
if (progress) {
setDownloadProgress(Math.round(progress.percentComplete))
setDownloadProgress(progress)
}
})
const finalDecryptedBytes = concatenateUint8Arrays(chunks)
setDownloadedBytes(finalDecryptedBytes)
if (!error) {
const finalDecryptedBytes = concatenateUint8Arrays(chunks)
setDownloadedBytes(finalDecryptedBytes)
}
} catch (error) {
console.error(error)
} finally {
@@ -109,9 +117,17 @@ const FilePreview = ({ file, application, isEmbeddedInSuper = false, imageZoomLe
<div className="flex flex-grow flex-col items-center justify-center">
<div className="flex items-center">
<Spinner className="mr-3 h-5 w-5" />
<div className="text-base font-semibold">{downloadProgress}%</div>
{downloadProgress && (
<div className="text-base font-semibold">{Math.floor(downloadProgress.percentComplete)}%</div>
)}
</div>
<span className="mt-3">Loading file...</span>
{downloadProgress ? (
<span className="mt-3">
{fileProgressToHumanReadableString(downloadProgress, file.name, { showPercent: false })}
</span>
) : (
<span className="mt-3">Loading...</span>
)}
</div>
) : downloadedBytes ? (
<PreviewComponent

View File

@@ -73,10 +73,10 @@ const FileBackupsDesktop = ({ application, backupsService }: Props) => {
)}
</PreferencesSegment>
<HorizontalSeparator classes="my-4" />
{backupsEnabled && (
<>
<HorizontalSeparator classes="my-4" />
<PreferencesSegment>
<>
<Text className="mb-3">

View File

@@ -1,3 +1,8 @@
import {
FileDownloadProgress,
fileProgressToHumanReadableString,
OnChunkCallbackNoProgress,
} from '@standardnotes/files'
import { FilePreviewModalController } from './FilePreviewModalController'
import {
PopoverFileItemAction,
@@ -260,6 +265,8 @@ export class FilesController extends AbstractViewController<FilesControllerEvent
const decryptedBytesArray: Uint8Array[] = []
let lastProgress: FileDownloadProgress | undefined
const result = await this.application.files.downloadFile(file, async (decryptedBytes, progress) => {
if (isUsingStreamingSaver) {
await saver.pushBytes(decryptedBytes)
@@ -267,14 +274,14 @@ export class FilesController extends AbstractViewController<FilesControllerEvent
decryptedBytesArray.push(decryptedBytes)
}
if (progress) {
const progressPercent = Math.floor(progress.percentComplete)
const progressPercent = Math.floor(progress.percentComplete)
updateToast(downloadingToastId, {
message: `Downloading file "${file.name}" (${progressPercent}%)`,
progress: progressPercent,
})
}
updateToast(downloadingToastId, {
message: fileProgressToHumanReadableString(progress, file.name, { showPercent: true }),
progress: progressPercent,
})
lastProgress = progress
})
if (result instanceof ClientDisplayableError) {
@@ -293,7 +300,9 @@ export class FilesController extends AbstractViewController<FilesControllerEvent
addToast({
type: ToastType.Success,
message: 'Successfully downloaded file',
message: `Successfully downloaded file${
lastProgress && lastProgress.source === 'local' ? ' from local backup' : ''
}`,
})
} catch (error) {
console.error(error)
@@ -364,13 +373,13 @@ export class FilesController extends AbstractViewController<FilesControllerEvent
progress: initialProgress,
})
const onChunk = async (chunk: Uint8Array, index: number, isLast: boolean) => {
await this.application.files.pushBytesForUpload(operation, chunk, index, isLast)
const onChunk: OnChunkCallbackNoProgress = async ({ data, index, isLast }) => {
await this.application.files.pushBytesForUpload(operation, data, index, isLast)
const progress = Math.round(operation.getProgress().percentComplete)
const percentComplete = Math.round(operation.getProgress().percentComplete)
updateToast(toastId, {
message: `Uploading file "${file.name}" (${progress}%)`,
progress,
message: `Uploading file "${file.name}" (${percentComplete}%)`,
progress: percentComplete,
})
}