feat: download and preview files from local backups automatically, if a local backup is available (#2076)
This commit is contained in:
@@ -8,9 +8,9 @@ describe('byte chunker', () => {
|
||||
it('should hold back small chunks until minimum size is met', async () => {
|
||||
let receivedBytes = new Uint8Array()
|
||||
let numChunks = 0
|
||||
const chunker = new ByteChunker(100, async (bytes) => {
|
||||
const chunker = new ByteChunker(100, async (chunk) => {
|
||||
numChunks++
|
||||
receivedBytes = new Uint8Array([...receivedBytes, ...bytes])
|
||||
receivedBytes = new Uint8Array([...receivedBytes, ...chunk.data])
|
||||
})
|
||||
|
||||
await chunker.addBytes(chunkOfSize(50), false)
|
||||
@@ -25,9 +25,9 @@ describe('byte chunker', () => {
|
||||
it('should send back big chunks immediately', async () => {
|
||||
let receivedBytes = new Uint8Array()
|
||||
let numChunks = 0
|
||||
const chunker = new ByteChunker(100, async (bytes) => {
|
||||
const chunker = new ByteChunker(100, async (chunk) => {
|
||||
numChunks++
|
||||
receivedBytes = new Uint8Array([...receivedBytes, ...bytes])
|
||||
receivedBytes = new Uint8Array([...receivedBytes, ...chunk.data])
|
||||
})
|
||||
|
||||
await chunker.addBytes(chunkOfSize(150), false)
|
||||
@@ -42,9 +42,9 @@ describe('byte chunker', () => {
|
||||
it('last chunk should be popped regardless of size', async () => {
|
||||
let receivedBytes = new Uint8Array()
|
||||
let numChunks = 0
|
||||
const chunker = new ByteChunker(100, async (bytes) => {
|
||||
const chunker = new ByteChunker(100, async (chunk) => {
|
||||
numChunks++
|
||||
receivedBytes = new Uint8Array([...receivedBytes, ...bytes])
|
||||
receivedBytes = new Uint8Array([...receivedBytes, ...chunk.data])
|
||||
})
|
||||
|
||||
await chunker.addBytes(chunkOfSize(50), false)
|
||||
@@ -57,9 +57,9 @@ describe('byte chunker', () => {
|
||||
it('single chunk should be popped immediately', async () => {
|
||||
let receivedBytes = new Uint8Array()
|
||||
let numChunks = 0
|
||||
const chunker = new ByteChunker(100, async (bytes) => {
|
||||
const chunker = new ByteChunker(100, async (chunk) => {
|
||||
numChunks++
|
||||
receivedBytes = new Uint8Array([...receivedBytes, ...bytes])
|
||||
receivedBytes = new Uint8Array([...receivedBytes, ...chunk.data])
|
||||
})
|
||||
|
||||
await chunker.addBytes(chunkOfSize(50), true)
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { OnChunkCallback } from './OnChunkCallback'
|
||||
import { OnChunkCallbackNoProgress } from './OnChunkCallback'
|
||||
|
||||
export class ByteChunker {
|
||||
public loggingEnabled = false
|
||||
private bytes = new Uint8Array()
|
||||
private index = 1
|
||||
|
||||
constructor(private minimumChunkSize: number, private onChunk: OnChunkCallback) {}
|
||||
constructor(private minimumChunkSize: number, private onChunk: OnChunkCallbackNoProgress) {}
|
||||
|
||||
private log(...args: any[]): void {
|
||||
if (!this.loggingEnabled) {
|
||||
@@ -27,9 +27,13 @@ export class ByteChunker {
|
||||
|
||||
private async popBytes(isLast: boolean): Promise<void> {
|
||||
const maxIndex = Math.max(this.minimumChunkSize, this.bytes.length)
|
||||
|
||||
const chunk = this.bytes.slice(0, maxIndex)
|
||||
|
||||
this.bytes = new Uint8Array([...this.bytes.slice(maxIndex)])
|
||||
|
||||
this.log(`Chunker popping ${chunk.length}, total size in queue ${this.bytes.length}`)
|
||||
await this.onChunk(chunk, this.index++, isLast)
|
||||
|
||||
await this.onChunk({ data: chunk, index: this.index++, isLast })
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1 +1,10 @@
|
||||
export type OnChunkCallback = (chunk: Uint8Array, index: number, isLast: boolean) => Promise<void>
|
||||
import { FileDownloadProgress } from '../Types/FileDownloadProgress'
|
||||
|
||||
export type OnChunkCallback = (chunk: {
|
||||
data: Uint8Array
|
||||
index: number
|
||||
isLast: boolean
|
||||
progress: FileDownloadProgress
|
||||
}) => Promise<void>
|
||||
|
||||
export type OnChunkCallbackNoProgress = (chunk: { data: Uint8Array; index: number; isLast: boolean }) => Promise<void>
|
||||
|
||||
@@ -10,9 +10,30 @@ describe('ordered byte chunker', () => {
|
||||
let receivedBytes = new Uint8Array()
|
||||
let numCallbacks = 0
|
||||
|
||||
const chunker = new OrderedByteChunker(chunkSizes, async (bytes) => {
|
||||
const chunker = new OrderedByteChunker(chunkSizes, 'network', async (chunk) => {
|
||||
numCallbacks++
|
||||
receivedBytes = new Uint8Array([...receivedBytes, ...bytes])
|
||||
receivedBytes = new Uint8Array([...receivedBytes, ...chunk.data])
|
||||
})
|
||||
|
||||
await chunker.addBytes(chunkOfSize(30))
|
||||
|
||||
expect(numCallbacks).toEqual(3)
|
||||
expect(receivedBytes.length).toEqual(30)
|
||||
})
|
||||
|
||||
it('should correctly report progress', async () => {
|
||||
const chunkSizes = [10, 10, 10]
|
||||
let receivedBytes = new Uint8Array()
|
||||
let numCallbacks = 0
|
||||
|
||||
const chunker = new OrderedByteChunker(chunkSizes, 'network', async (chunk) => {
|
||||
numCallbacks++
|
||||
|
||||
receivedBytes = new Uint8Array([...receivedBytes, ...chunk.data])
|
||||
|
||||
expect(chunk.progress.encryptedBytesDownloaded).toEqual(receivedBytes.length)
|
||||
|
||||
expect(chunk.progress.percentComplete).toEqual((numCallbacks / chunkSizes.length) * 100.0)
|
||||
})
|
||||
|
||||
await chunker.addBytes(chunkOfSize(30))
|
||||
|
||||
@@ -1,13 +1,28 @@
|
||||
import { FileDownloadProgress } from '../Types/FileDownloadProgress'
|
||||
import { OnChunkCallback } from './OnChunkCallback'
|
||||
|
||||
export class OrderedByteChunker {
|
||||
private bytes = new Uint8Array()
|
||||
private index = 1
|
||||
private remainingChunks: number[] = []
|
||||
private fileSize: number
|
||||
|
||||
constructor(
|
||||
private chunkSizes: number[],
|
||||
private onChunk: (chunk: Uint8Array, index: number, isLast: boolean) => Promise<void>,
|
||||
private source: FileDownloadProgress['source'],
|
||||
private onChunk: OnChunkCallback,
|
||||
) {
|
||||
this.remainingChunks = chunkSizes.slice()
|
||||
|
||||
this.fileSize = chunkSizes.reduce((acc, size) => acc + size, 0)
|
||||
}
|
||||
|
||||
private get bytesPopped(): number {
|
||||
return this.fileSize - this.bytesRemaining
|
||||
}
|
||||
|
||||
private get bytesRemaining(): number {
|
||||
return this.remainingChunks.reduce((acc, size) => acc + size, 0)
|
||||
}
|
||||
|
||||
private needsPop(): boolean {
|
||||
@@ -31,7 +46,18 @@ export class OrderedByteChunker {
|
||||
|
||||
this.remainingChunks.shift()
|
||||
|
||||
await this.onChunk(chunk, this.index++, this.index === this.chunkSizes.length - 1)
|
||||
await this.onChunk({
|
||||
data: chunk,
|
||||
index: this.index++,
|
||||
isLast: this.index === this.chunkSizes.length - 1,
|
||||
progress: {
|
||||
encryptedFileSize: this.fileSize,
|
||||
encryptedBytesDownloaded: this.bytesPopped,
|
||||
encryptedBytesRemaining: this.bytesRemaining,
|
||||
percentComplete: (this.bytesPopped / this.fileSize) * 100.0,
|
||||
source: this.source,
|
||||
},
|
||||
})
|
||||
|
||||
if (this.needsPop()) {
|
||||
await this.popBytes()
|
||||
|
||||
Reference in New Issue
Block a user