Skip to content

Commit

Permalink
feat(files): large file downloads, improved qol, and improve extensio…
Browse files Browse the repository at this point in the history
…n fixing
  • Loading branch information
josephmcg authored and stavares843 committed Apr 13, 2022
1 parent 3dec325 commit d0bc115
Show file tree
Hide file tree
Showing 14 changed files with 113 additions and 151 deletions.
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
47 changes: 17 additions & 30 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
*/
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
* @returns {string} link of localally 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} content the content to set the file description to
*/
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 @@ -35,7 +35,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

0 comments on commit d0bc115

Please sign in to comment.