Skip to content

Commit

Permalink
fix: uid check on upload/download state changes (#1517)
Browse files Browse the repository at this point in the history
Signed-off-by: Pedro Lamas <pedrolamas@gmail.com>
  • Loading branch information
pedrolamas authored Oct 31, 2024
1 parent 603a1ea commit 4363ed3
Show file tree
Hide file tree
Showing 5 changed files with 78 additions and 50 deletions.
8 changes: 4 additions & 4 deletions src/components/widgets/filesystem/FileSystem.vue
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@
<script lang="ts">
import { Component, Prop, Mixins, Watch } from 'vue-property-decorator'
import { SocketActions } from '@/api/socketActions'
import type { AppDirectory, AppFile, AppFileWithMeta, FilesUpload, FileFilterType, FileBrowserEntry, RootProperties } from '@/store/files/types'
import type { AppDirectory, AppFile, AppFileWithMeta, FileUpload, FileFilterType, FileBrowserEntry, RootProperties } from '@/store/files/types'
import StateMixin from '@/mixins/state'
import FilesMixin from '@/mixins/files'
import ServicesMixin from '@/mixins/services'
Expand Down Expand Up @@ -467,7 +467,7 @@ export default class FileSystem extends Mixins(StateMixin, FilesMixin, ServicesM
}
// Get a list of currently active uploads.
get currentUploads (): FilesUpload[] {
get currentUploads (): FileUpload[] {
return this.$store.state.files.uploads
}
Expand Down Expand Up @@ -884,12 +884,12 @@ export default class FileSystem extends Mixins(StateMixin, FilesMixin, ServicesM
this.$store.dispatch('wait/removeWait', wait)
}
handleCancelUpload (file: FilesUpload) {
handleCancelUpload (file: FileUpload) {
if (!file.complete) {
// Hasn't started uploading...
if (file.loaded === 0) {
this.$store.dispatch('files/updateFileUpload', {
filepath: file.filepath,
uid: file.uid,
cancelled: true
})
}
Expand Down
6 changes: 3 additions & 3 deletions src/components/widgets/filesystem/FileSystemUploadDialog.vue
Original file line number Diff line number Diff line change
Expand Up @@ -75,15 +75,15 @@
<script lang="ts">
import { Component, Prop, Mixins } from 'vue-property-decorator'
import StateMixin from '@/mixins/state'
import type { FilesUpload } from '@/store/files/types'
import type { FileUpload } from '@/store/files/types'
@Component({})
export default class FileSystemUploadDialog extends Mixins(StateMixin) {
@Prop({ type: Boolean })
readonly value?: boolean
@Prop({ type: Array<FilesUpload>, required: true })
readonly files!: FilesUpload[]
@Prop({ type: Array<FileUpload>, required: true })
readonly files!: FileUpload[]
}
</script>

Expand Down
86 changes: 53 additions & 33 deletions src/mixins/files.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import type { AppFile, FilesUpload, AppFileThumbnail, KlipperFileMeta } from '@/store/files/types'
import type { AppFile, FileUpload, AppFileThumbnail, KlipperFileMeta, FileDownload } from '@/store/files/types'
import Vue from 'vue'
import { Component } from 'vue-property-decorator'
import type { AxiosRequestConfig, AxiosProgressEvent } from 'axios'
import { httpClientActions } from '@/api/httpClientActions'
import type { FileWithPath } from '@/types'
import consola from 'consola'
import { v4 as uuidv4 } from 'uuid'

@Component
export default class FilesMixin extends Vue {
Expand Down Expand Up @@ -71,14 +72,26 @@ export default class FilesMixin extends Vue {
* @param path The path to the file
*/
async getFile<T = any> (filename: string, path: string, size = 0, options?: AxiosRequestConfig) {
const currentDownload: FileDownload | null = this.$store.state.files.download

if (currentDownload) {
currentDownload.abortController.abort()

this.$store.dispatch('files/removeFileDownload', currentDownload.uid)
}

// Sort out the filepath
const filepath = path ? `${path}/${filename}` : filename
const filepath = path
? `${path}/${filename}`
: filename
const uid = uuidv4()

try {
const abortController = new AbortController()

// Add an entry to vuex indicating we're downloading a file.
this.$store.dispatch('files/updateFileDownload', {
uid,
filepath,
size,
loaded: 0,
Expand All @@ -102,7 +115,7 @@ export default class FilesMixin extends Vue {
)

const payload: any = {
filepath,
uid,
loaded: event.loaded,
percent: Math.round(progress * 100),
speed: event.rate ?? 0
Expand All @@ -120,7 +133,7 @@ export default class FilesMixin extends Vue {

return response
} finally {
this.$store.dispatch('files/removeFileDownload')
this.$store.dispatch('files/removeFileDownload', uid)
}
}

Expand Down Expand Up @@ -176,23 +189,26 @@ export default class FilesMixin extends Vue {
* @param andPrint If we should attempt to print this file or not.
* @param options Axios request options
*/
async uploadFile (file: File, path: string, root: string, andPrint: boolean, options?: AxiosRequestConfig) {
async uploadFile (file: File, path: string, root: string, andPrint: boolean, uid?: string, options?: AxiosRequestConfig) {
const filepath = path
? `${path}/${file.name}`
: file.name
uid = uid || uuidv4()

try {
const abortController = new AbortController()

this.$store.dispatch('files/updateFileUpload', {
uid,
filepath,
size: file.size,
loaded: 0,
percent: 0,
speed: 0,
cancelled: false,
complete: false,
abortController
})
} satisfies FileUpload)

const response = await httpClientActions.serverFilesUploadPost(file, path, root, andPrint, {
...options,
Expand All @@ -203,7 +219,7 @@ export default class FilesMixin extends Vue {
}

this.$store.dispatch('files/updateFileUpload', {
filepath,
uid,
loaded: event.loaded,
percent: event.progress ? Math.round(event.progress * 100) : 0,
speed: event.rate ?? 0
Expand All @@ -215,7 +231,7 @@ export default class FilesMixin extends Vue {

return response
} finally {
this.$store.dispatch('files/removeFileUpload', filepath)
this.$store.dispatch('files/removeFileUpload', uid)
}
}

Expand All @@ -238,45 +254,49 @@ export default class FilesMixin extends Vue {
// Upload some files.
async uploadFiles (files: FileList | File[] | FileWithPath[], path: string, root: string, andPrint: boolean) {
// For each file, adds the associated state.
for (const file of files) {
const [fullPath, fileObject] = this.getFullPathAndFile(path, file)

const filepath = fullPath
? `${fullPath}/${fileObject.name}`
: fileObject.name
const fileUploads = [...files]
.map(file => {
const uid = uuidv4()
const [fullPath, fileObject] = this.getFullPathAndFile(path, file)

const filepath = fullPath
? `${fullPath}/${fileObject.name}`
: fileObject.name

this.$store.dispatch('files/updateFileUpload', {
uid,
filepath,
size: fileObject.size,
loaded: 0,
percent: 0,
speed: 0,
cancelled: false,
complete: false
})

this.$store.dispatch('files/updateFileUpload', {
filepath,
size: fileObject.size,
loaded: 0,
percent: 0,
speed: 0,
unit: 'kB',
cancelled: false
return {
uid,
file
}
})
}

// Async uploads cause issues in moonraker / klipper.
// So instead, upload sequentially waiting for moonraker to finish
// processing of each file.
if (files.length > 1) andPrint = false
for (const file of files) {
const [fullPath, fileObject] = this.getFullPathAndFile(path, file)

const filepath = fullPath
? `${fullPath}/${fileObject.name}`
: fileObject.name
if (fileUploads.length > 1) andPrint = false
for (const fileUpload of fileUploads) {
const [fullPath, fileObject] = this.getFullPathAndFile(path, fileUpload.file)

const fileState = this.$store.state.files.uploads.find((u: FilesUpload) => u.filepath === filepath)
const fileState = this.$store.state.files.uploads.find((u: FileUpload) => u.uid === fileUpload.uid)

if (fileState && !fileState?.cancelled) {
try {
await this.uploadFile(fileObject, fullPath, root, andPrint)
await this.uploadFile(fileObject, fullPath, root, andPrint, fileUpload.uid)
} catch (error: unknown) {
consola.error('[FileUpload] file', error)
}
} else {
this.$store.dispatch('files/removeFileUpload', filepath)
this.$store.dispatch('files/removeFileUpload', fileUpload.uid)
}
}
}
Expand Down
23 changes: 15 additions & 8 deletions src/store/files/mutations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,30 +96,37 @@ export const mutations: MutationTree<FilesState> = {
},

setUpdateFileUpload (state, payload) {
const i = state.uploads.findIndex((u) => u.filepath === payload.filepath)
const i = state.uploads.findIndex((u) => u.uid === payload.uid)
if (i >= 0) {
Vue.set(state.uploads, i, { ...state.uploads[i], ...payload })
} else {
state.uploads.push(payload)
}
},

setRemoveFileUpload (state, payload) {
const i = state.uploads.findIndex((u) => u.filepath === payload)
setRemoveFileUpload (state, payload: string) {
const i = state.uploads.findIndex((u) => u.uid === payload)
if (i >= 0) {
state.uploads.splice(i, 1)
}
},

setUpdateFileDownload (state, payload) {
state.download = {
...state.download,
...payload
if (
state.download == null ||
state.download.uid === payload.uid
) {
state.download = {
...state.download,
...payload
}
}
},

setRemoveFileDownload (state) {
state.download = null
setRemoveFileDownload (state, payload: string) {
if (state.download?.uid === payload) {
state.download = null
}
},

setCurrentPath (state, payload) {
Expand Down
5 changes: 3 additions & 2 deletions src/store/files/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import type { HistoryItem } from '@/store/history/types'
export type { KlipperFileMeta, KlipperFileMetaThumbnail }

export interface FilesState {
uploads: FilesUpload[];
uploads: FileUpload[];
download: FileDownload | null;
currentPaths: Record<string, string>;
disk_usage: DiskUsage;
Expand Down Expand Up @@ -97,6 +97,7 @@ export interface FileUpdate {
}

export interface FileDownload {
uid: string;
filepath: string;
size: number;
loaded: number;
Expand All @@ -105,7 +106,7 @@ export interface FileDownload {
abortController: AbortController;
}

export interface FilesUpload extends FileDownload {
export interface FileUpload extends FileDownload {
complete: boolean; // indicates moonraker is finished with the file.
cancelled: boolean; // in a cancelled state, don't show - nor try to upload.
}
Expand Down

0 comments on commit 4363ed3

Please sign in to comment.