Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(files): large file downloads, improved qol, and improve extension fixing #2846

Merged
merged 4 commits into from
Apr 22, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 10 additions & 10 deletions components/views/files/view/View.html
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,16 @@
<TypographyText :text="$dayjs(file.modified).fromNow()" :size="6" />
</div>
<div class="controls">
<UiLoadersSpinner v-if="isLoading" class="control disabled" spinning />
<UiLoadersSpinner
v-if="isDownloading"
class="control disabled"
spinning
/>
<a
v-else
@click="download"
:data-tooltip="$t('controls.download')"
class="has-tooltip has-tooltip-primary has-tooltip-bottom control"
:href="file.url"
target="_blank"
:download="name"
>
<download-icon size="1x" />
</a>
Expand All @@ -35,16 +37,14 @@
</div>
</div>
</div>
<UiProgress v-if="isLoading" :progress="progress" />
<img v-else-if="file.thumbnail" class="file-image" :src="file.thumbnail" />
<img v-if="file.thumbnail" class="file-image" :src="file.thumbnail" />
<div v-else class="no-preview">
<UiLoadersSpinner v-if="isDownloading" class="file-icon" spinning />
<a
v-else
@click="download"
:data-tooltip="$t('controls.download')"
class="has-tooltip has-tooltip-primary has-tooltip-top"
:class="{'disabled' : isLoading}"
:href="file.url"
target="_blank"
:download="name"
>
<download-icon size="1x" class="file-icon" />
</a>
Expand Down
81 changes: 23 additions & 58 deletions components/views/files/view/View.vue
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import {
XIcon,
LinkIcon,
} from 'satellite-lucide-icons'
import { filetypeextension } from 'magic-bytes.js'
import { Fil } from '~/libraries/Files/Fil'

export default Vue.extend({
Expand All @@ -25,74 +24,40 @@ export default Vue.extend({
data() {
return {
file: undefined as Fil | undefined,
name: '' as string,
progress: 0 as number,
}
},
computed: {
...mapState(['ui']),
isLoading(): boolean {
return this.progress >= 0 && this.progress < 100
isDownloading(): boolean {
return this.ui.fileDownloadList.includes(this.file?.name)
},
},
/**
*/
async created() {
created() {
this.file = this.$FileSystem.getChild(this.ui.filePreview) as Fil
this.name = this.file?.name

// if no file data available, pull encrypted file from textile bucket
if (!this.file.file) {
const fsFil: Fil = this.$FileSystem.getChild(this.file.name) as Fil
fsFil.file = await this.$TextileManager.bucket?.pullFile(
this.file.id,
this.file.name,
this.file.type,
this.file.size,
this.setProgress,
)
}
// file extension according to file name
const fileExt = this.file.name
.slice(((this.file.name.lastIndexOf('.') - 1) >>> 0) + 2)
.toLowerCase()
// you only need the first 256 bytes or so to confirm file type
const buffer = new Uint8Array(
await this.file.file.slice(0, 256).arrayBuffer(),
)
// file extension according to byte data
const dataExt = filetypeextension(buffer)[0]

// magicbytes declares svg as xml, so we need to manually check
const decodedFile = new TextDecoder().decode(buffer)
if (decodedFile.includes('xmlns="http://www.w3.org/2000/svg"')) {
// if corrupted, set .svg extension
if (fileExt !== 'svg') {
this.name += '.svg'
}
return
}

// if corrupted txt file
if (!dataExt && fileExt !== 'txt') {
this.name += '.txt'
return
}

// if corrupted file with wrong extension, force the correct one
if (fileExt !== dataExt && dataExt) {
this.name += `.${dataExt}`
}
},
methods: {
/**
* @method setProgress
* @description set progress (% out of 100) while file is being pulled from textile bucket. passed as a callback
* @param num current progress in bytes
* @param size total file size in bytes
* @method download
* @description download file using stream saver, apply original extension if it was removed
* add name to store so the user doesn't start another download of the same file
* also takes a bit to get started for large files, this adds loading indicator
*/
setProgress(num: number, size: number) {
this.progress = Math.floor((num / size) * 100)
async download() {
if (this.file) {
this.$store.commit('ui/addFileDownload', this.file.name)
const fileExt = this.file.name
.slice(((this.file.name.lastIndexOf('.') - 1) >>> 0) + 2)
.toLowerCase()

await this.$TextileManager.bucket?.pullFileStream(
this.file.id,
this.file.extension === fileExt
? this.file.name
: (this.file.name += `.${this.file.extension}`),
this.file.size,
)
this.$store.commit('ui/removeFileDownload', this.file.name)
}
},
/**
* @method share
Expand Down
49 changes: 18 additions & 31 deletions libraries/Files/Fil.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ import { FILE_TYPE } from './types/file'
export class Fil extends Item {
private _description: string = ''
private _size: number = 0
private _file: File | undefined
private _thumbnail: string
private _extension: string

/**
* @constructor
Expand All @@ -17,34 +17,37 @@ export class Fil extends Item {
constructor({
id,
name,
file,
size,
liked,
shared,
modified,
description,
type,
thumbnail,
extension,
}: {
id?: string
name: string
file?: File
size: number
liked?: boolean
shared?: boolean
modified?: number
description?: string
type?: FILE_TYPE
thumbnail?: string
extension?: string
}) {
if (!size) {
throw new Error(FileSystemErrors.FILE_SIZE)
}
super({ name, liked, shared, modified, id, type })
this._file = file || undefined
this._description = description || ''
this._size = size
this._thumbnail = thumbnail || ''
// set original extension in case user changes it during rename
this._extension =
extension ||
name.slice(((name.lastIndexOf('.') - 1) >>> 0) + 2).toLowerCase()
}

/**
Expand All @@ -62,63 +65,47 @@ export class Fil extends Item {
get copy(): Fil {
return new Fil({
name: `${this.name} copy`,
file: this._file,
size: this.size,
modified: this.modified,
liked: this.liked,
shared: this.shared,
description: this.description,
type: this.type as FILE_TYPE,
thumbnail: this.thumbnail,
extension: this.extension,
})
}

/**
* @getter size
* @returns file size
* @returns {number} file size
*/
get size(): number {
return this._size
}

/**
* @getter modified
* @returns last modified timestamp
* @returns {number} last modified timestamp
*/
get modified(): number {
return this.modifiedVal
}

/**
* @getter file
* @returns file object fetched from textile bucket
* @getter thumbnail
* @returns {string} base64 string of scaled down image, or '' if non-embeddable image
*/
get file(): File | undefined {
return this._file
}

/**
* @setter file
* @param {File} file file object
*/
set file(file: File | undefined) {
this._file = file
}

/**
* @getter url
* @returns link of locally stored File for image preview and downloads
*/
get url(): string {
return this.file ? URL.createObjectURL(this.file) : ''
get thumbnail(): string {
return this._thumbnail
}

/**
* @getter url
* @returns link of localally stored File for image preview and downloads
* @getter extension
* @returns {string} original extension on user upload
*/
get thumbnail(): string {
return this._thumbnail
get extension(): string {
return this._extension
}

/**
Expand Down
10 changes: 7 additions & 3 deletions libraries/Files/FilSystem.ts
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,7 @@ export class FilSystem {
description,
modified,
thumbnail,
extension,
} = item
return {
id,
Expand All @@ -168,6 +169,7 @@ export class FilSystem {
description,
modified,
thumbnail,
extension,
}
}
const { id, name, liked, shared, type, modified } = item
Expand Down Expand Up @@ -213,6 +215,7 @@ export class FilSystem {
description,
modified,
thumbnail,
extension,
} = item as ExportFile
const type = item.type as FILE_TYPE
this.createFile({
Expand All @@ -225,6 +228,7 @@ export class FilSystem {
type,
modified,
thumbnail,
extension,
})
}
if ((Object.values(DIRECTORY_TYPE) as string[]).includes(item.type)) {
Expand All @@ -248,37 +252,37 @@ export class FilSystem {
public createFile({
id,
name,
file,
size,
liked,
shared,
description,
type,
modified,
thumbnail,
extension,
}: {
id?: string
name: string
file?: File
size: number
liked?: boolean
shared?: boolean
description?: string
type?: FILE_TYPE
modified?: number
thumbnail?: string
extension?: string
}): Fil | null {
const newFile = new Fil({
id,
name,
file,
size,
liked,
shared,
description,
type,
modified,
thumbnail,
extension,
})
const inserted = this.addChild(newFile)
return inserted ? newFile : null
Expand Down
1 change: 0 additions & 1 deletion libraries/Files/TextileFileSystem.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@ export class TextileFileSystem extends FilSystem {
this.createFile({
id,
name: file.name,
file,
size: file.size,
type: Object.values(FILE_TYPE).includes(type) ? type : FILE_TYPE.GENERIC,
thumbnail: await this._createThumbnail(file, byteType),
Expand Down
Loading