mirror of
https://github.com/filebrowser/filebrowser.git
synced 2024-06-07 23:00:43 +00:00
Merge pull request #1021 from ramiresviana/upload-queue
This commit is contained in:
commit
a47b69bcec
@ -89,25 +89,17 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { mapState, mapMutations } from 'vuex'
|
import { mapState, mapMutations } from 'vuex'
|
||||||
import throttle from 'lodash.throttle'
|
|
||||||
import Item from './ListingItem'
|
import Item from './ListingItem'
|
||||||
import css from '@/utils/css'
|
import css from '@/utils/css'
|
||||||
import { users, files as api } from '@/api'
|
import { users, files as api } from '@/api'
|
||||||
import buttons from '@/utils/buttons'
|
import * as upload from '@/utils/upload'
|
||||||
import url from '@/utils/url'
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'listing',
|
name: 'listing',
|
||||||
components: { Item },
|
components: { Item },
|
||||||
data: function () {
|
data: function () {
|
||||||
return {
|
return {
|
||||||
showLimit: 50,
|
showLimit: 50
|
||||||
uploading: {
|
|
||||||
id: 0,
|
|
||||||
count: 0,
|
|
||||||
size: 0,
|
|
||||||
progress: []
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
@ -194,7 +186,7 @@ export default {
|
|||||||
base64: function (name) {
|
base64: function (name) {
|
||||||
return window.btoa(unescape(encodeURIComponent(name)))
|
return window.btoa(unescape(encodeURIComponent(name)))
|
||||||
},
|
},
|
||||||
keyEvent (event) {
|
keyEvent (event) {
|
||||||
if (this.show !== null) {
|
if (this.show !== null) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -311,7 +303,7 @@ export default {
|
|||||||
dragEnd () {
|
dragEnd () {
|
||||||
this.resetOpacity()
|
this.resetOpacity()
|
||||||
},
|
},
|
||||||
drop: function (event) {
|
drop: async function (event) {
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
this.resetOpacity()
|
this.resetOpacity()
|
||||||
|
|
||||||
@ -331,65 +323,34 @@ export default {
|
|||||||
base = el.querySelector('.name').innerHTML + '/'
|
base = el.querySelector('.name').innerHTML + '/'
|
||||||
}
|
}
|
||||||
|
|
||||||
if (base === '') {
|
let files = await upload.scanFiles(dt)
|
||||||
this.scanFiles(dt).then((result) => {
|
let path = this.$route.path + base
|
||||||
this.checkConflict(result, this.req.items, base)
|
let items = this.req.items
|
||||||
})
|
|
||||||
} else {
|
if (base !== '') {
|
||||||
this.scanFiles(dt).then((result) => {
|
try {
|
||||||
api.fetch(this.$route.path + base)
|
items = (await api.fetch(path)).items
|
||||||
.then(req => {
|
} catch (error) {
|
||||||
this.checkConflict(result, req.items, base)
|
this.$showError(error)
|
||||||
})
|
}
|
||||||
.catch(this.$showError)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
},
|
|
||||||
checkConflict (files, items, base) {
|
|
||||||
if (typeof items === 'undefined' || items === null) {
|
|
||||||
items = []
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let folder_upload = false
|
let conflict = upload.checkConflict(files, items)
|
||||||
if (files[0].fullPath !== undefined) {
|
|
||||||
folder_upload = true
|
|
||||||
}
|
|
||||||
|
|
||||||
let conflict = false
|
if (conflict) {
|
||||||
for (let i = 0; i < files.length; i++) {
|
this.$store.commit('showHover', {
|
||||||
let file = files[i]
|
prompt: 'replace',
|
||||||
let name = file.name
|
confirm: (event) => {
|
||||||
|
event.preventDefault()
|
||||||
if (folder_upload) {
|
this.$store.commit('closeHovers')
|
||||||
let dirs = file.fullPath.split("/")
|
upload.handleFiles(files, path, true)
|
||||||
if (dirs.length > 1) {
|
|
||||||
name = dirs[0]
|
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
|
|
||||||
let res = items.findIndex(function hasConflict (element) {
|
|
||||||
return (element.name === this)
|
|
||||||
}, name)
|
|
||||||
|
|
||||||
if (res >= 0) {
|
|
||||||
conflict = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!conflict) {
|
|
||||||
this.handleFiles(files, base)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
this.$store.commit('showHover', {
|
upload.handleFiles(files, path)
|
||||||
prompt: 'replace',
|
|
||||||
confirm: (event) => {
|
|
||||||
event.preventDefault()
|
|
||||||
this.$store.commit('closeHovers')
|
|
||||||
this.handleFiles(files, base, true)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
},
|
},
|
||||||
uploadInput (event) {
|
uploadInput (event) {
|
||||||
this.$store.commit('closeHovers')
|
this.$store.commit('closeHovers')
|
||||||
@ -404,7 +365,22 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.checkConflict(files, this.req.items, '')
|
let path = this.$route.path
|
||||||
|
let conflict = upload.checkConflict(files, this.req.items)
|
||||||
|
|
||||||
|
if (conflict) {
|
||||||
|
this.$store.commit('showHover', {
|
||||||
|
prompt: 'replace',
|
||||||
|
confirm: (event) => {
|
||||||
|
event.preventDefault()
|
||||||
|
this.$store.commit('closeHovers')
|
||||||
|
this.handleFiles(files, path, true)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
upload.handleFiles(files, path)
|
||||||
},
|
},
|
||||||
resetOpacity () {
|
resetOpacity () {
|
||||||
let items = document.getElementsByClassName('item')
|
let items = document.getElementsByClassName('item')
|
||||||
@ -413,145 +389,6 @@ export default {
|
|||||||
file.style.opacity = 1
|
file.style.opacity = 1
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
scanFiles(dt) {
|
|
||||||
return new Promise((resolve) => {
|
|
||||||
let reading = 0
|
|
||||||
const contents = []
|
|
||||||
|
|
||||||
if (dt.items !== undefined) {
|
|
||||||
for (let item of dt.items) {
|
|
||||||
if (item.kind === "file" && typeof item.webkitGetAsEntry === "function") {
|
|
||||||
const entry = item.webkitGetAsEntry()
|
|
||||||
readEntry(entry)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
resolve(dt.files)
|
|
||||||
}
|
|
||||||
|
|
||||||
function readEntry(entry, directory = "") {
|
|
||||||
if (entry.isFile) {
|
|
||||||
reading++
|
|
||||||
entry.file(file => {
|
|
||||||
reading--
|
|
||||||
|
|
||||||
file.fullPath = `${directory}${file.name}`
|
|
||||||
contents.push(file)
|
|
||||||
|
|
||||||
if (reading === 0) {
|
|
||||||
resolve(contents)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
} else if (entry.isDirectory) {
|
|
||||||
const dir = {
|
|
||||||
isDir: true,
|
|
||||||
path: `${directory}${entry.name}`
|
|
||||||
}
|
|
||||||
|
|
||||||
contents.push(dir)
|
|
||||||
|
|
||||||
readReaderContent(entry.createReader(), `${directory}${entry.name}`)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function readReaderContent(reader, directory) {
|
|
||||||
reading++
|
|
||||||
|
|
||||||
reader.readEntries(function (entries) {
|
|
||||||
reading--
|
|
||||||
if (entries.length > 0) {
|
|
||||||
for (const entry of entries) {
|
|
||||||
readEntry(entry, `${directory}/`)
|
|
||||||
}
|
|
||||||
|
|
||||||
readReaderContent(reader, `${directory}/`)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (reading === 0) {
|
|
||||||
resolve(contents)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
},
|
|
||||||
setProgress: throttle(function() {
|
|
||||||
if (this.uploading.count == 0) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
let sum = this.uploading.progress.reduce((acc, val) => acc + val)
|
|
||||||
this.$store.commit('setProgress', Math.ceil(sum / this.uploading.size * 100))
|
|
||||||
}, 100, {leading: false, trailing: true}),
|
|
||||||
handleFiles (files, base, overwrite = false) {
|
|
||||||
if (this.uploading.count == 0) {
|
|
||||||
buttons.loading('upload')
|
|
||||||
}
|
|
||||||
|
|
||||||
let promises = []
|
|
||||||
|
|
||||||
let onupload = (id) => (event) => {
|
|
||||||
this.uploading.progress[id] = event.loaded
|
|
||||||
this.setProgress()
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let i = 0; i < files.length; i++) {
|
|
||||||
let file = files[i]
|
|
||||||
|
|
||||||
if (!file.isDir) {
|
|
||||||
let filename = (file.fullPath !== undefined) ? file.fullPath : file.name
|
|
||||||
let filenameEncoded = url.encodeRFC5987ValueChars(filename)
|
|
||||||
|
|
||||||
let id = this.uploading.id
|
|
||||||
|
|
||||||
this.uploading.size += file.size
|
|
||||||
this.uploading.id++
|
|
||||||
this.uploading.count++
|
|
||||||
|
|
||||||
let promise = api.post(this.$route.path + base + filenameEncoded, file, overwrite, throttle(onupload(id), 100)).finally(() => {
|
|
||||||
this.uploading.count--
|
|
||||||
})
|
|
||||||
|
|
||||||
promises.push(promise)
|
|
||||||
} else {
|
|
||||||
let uri = this.$route.path + base
|
|
||||||
let folders = file.path.split("/")
|
|
||||||
|
|
||||||
for (let i = 0; i < folders.length; i++) {
|
|
||||||
let folder = folders[i]
|
|
||||||
let folderEncoded = encodeURIComponent(folder)
|
|
||||||
uri += folderEncoded + "/"
|
|
||||||
}
|
|
||||||
|
|
||||||
api.post(uri)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let finish = () => {
|
|
||||||
if (this.uploading.count > 0) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
buttons.success('upload')
|
|
||||||
|
|
||||||
this.$store.commit('setProgress', 0)
|
|
||||||
this.$store.commit('setReload', true)
|
|
||||||
|
|
||||||
this.uploading.id = 0
|
|
||||||
this.uploading.sizes = []
|
|
||||||
this.uploading.progress = []
|
|
||||||
}
|
|
||||||
|
|
||||||
Promise.all(promises)
|
|
||||||
.then(() => {
|
|
||||||
finish()
|
|
||||||
})
|
|
||||||
.catch(error => {
|
|
||||||
finish()
|
|
||||||
this.$showError(error)
|
|
||||||
})
|
|
||||||
|
|
||||||
return false
|
|
||||||
},
|
|
||||||
async sort (by) {
|
async sort (by) {
|
||||||
let asc = false
|
let asc = false
|
||||||
|
|
||||||
|
@ -3,7 +3,15 @@ const getters = {
|
|||||||
isFiles: state => !state.loading && state.route.name === 'Files',
|
isFiles: state => !state.loading && state.route.name === 'Files',
|
||||||
isListing: (state, getters) => getters.isFiles && state.req.isDir,
|
isListing: (state, getters) => getters.isFiles && state.req.isDir,
|
||||||
isEditor: (state, getters) => getters.isFiles && (state.req.type === 'text' || state.req.type === 'textImmutable'),
|
isEditor: (state, getters) => getters.isFiles && (state.req.type === 'text' || state.req.type === 'textImmutable'),
|
||||||
selectedCount: state => state.selected.length
|
selectedCount: state => state.selected.length,
|
||||||
|
progress : state => {
|
||||||
|
if (state.upload.progress.length == 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
let sum = state.upload.progress.reduce((acc, val) => acc + val)
|
||||||
|
return Math.ceil(sum / state.upload.size * 100);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default getters
|
export default getters
|
||||||
|
@ -2,6 +2,7 @@ import Vue from 'vue'
|
|||||||
import Vuex from 'vuex'
|
import Vuex from 'vuex'
|
||||||
import mutations from './mutations'
|
import mutations from './mutations'
|
||||||
import getters from './getters'
|
import getters from './getters'
|
||||||
|
import upload from './modules/upload'
|
||||||
|
|
||||||
Vue.use(Vuex)
|
Vue.use(Vuex)
|
||||||
|
|
||||||
@ -29,5 +30,6 @@ export default new Vuex.Store({
|
|||||||
strict: true,
|
strict: true,
|
||||||
state,
|
state,
|
||||||
getters,
|
getters,
|
||||||
mutations
|
mutations,
|
||||||
|
modules: { upload }
|
||||||
})
|
})
|
||||||
|
95
frontend/src/store/modules/upload.js
Normal file
95
frontend/src/store/modules/upload.js
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
import Vue from 'vue'
|
||||||
|
import { files as api } from '@/api'
|
||||||
|
import throttle from 'lodash.throttle'
|
||||||
|
import buttons from '@/utils/buttons'
|
||||||
|
|
||||||
|
const UPLOADS_LIMIT = 5;
|
||||||
|
|
||||||
|
const state = {
|
||||||
|
id: 0,
|
||||||
|
size: 0,
|
||||||
|
progress: [],
|
||||||
|
queue: [],
|
||||||
|
uploads: {}
|
||||||
|
}
|
||||||
|
|
||||||
|
const mutations = {
|
||||||
|
setProgress(state, { id, loaded }) {
|
||||||
|
Vue.set(state.progress, id, loaded)
|
||||||
|
},
|
||||||
|
reset: (state) => {
|
||||||
|
state.id = 0
|
||||||
|
state.size = 0
|
||||||
|
state.progress = []
|
||||||
|
},
|
||||||
|
addJob: (state, item) => {
|
||||||
|
state.queue.push(item)
|
||||||
|
state.size += item.file.size
|
||||||
|
state.id++
|
||||||
|
},
|
||||||
|
moveJob(state) {
|
||||||
|
const item = state.queue[0]
|
||||||
|
state.queue.shift()
|
||||||
|
Vue.set(state.uploads, item.id, item)
|
||||||
|
},
|
||||||
|
removeJob(state, id) {
|
||||||
|
delete state.uploads[id]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const actions = {
|
||||||
|
upload: (context, item) => {
|
||||||
|
let uploadsCount = Object.keys(context.state.uploads).length;
|
||||||
|
|
||||||
|
let isQueueEmpty = context.state.queue.length == 0
|
||||||
|
let isUploadsEmpty = uploadsCount == 0
|
||||||
|
|
||||||
|
if (isQueueEmpty && isUploadsEmpty) {
|
||||||
|
buttons.loading('upload')
|
||||||
|
}
|
||||||
|
|
||||||
|
context.commit('addJob', item)
|
||||||
|
context.dispatch('processUploads')
|
||||||
|
},
|
||||||
|
finishUpload: (context, item) => {
|
||||||
|
context.commit('setProgress', { id: item.id, loaded: item.file.size })
|
||||||
|
context.commit('removeJob', item.id)
|
||||||
|
context.dispatch('processUploads')
|
||||||
|
},
|
||||||
|
processUploads: async (context) => {
|
||||||
|
let uploadsCount = Object.keys(context.state.uploads).length;
|
||||||
|
|
||||||
|
let isBellowLimit = uploadsCount < UPLOADS_LIMIT
|
||||||
|
let isQueueEmpty = context.state.queue.length == 0
|
||||||
|
let isUploadsEmpty = uploadsCount == 0
|
||||||
|
|
||||||
|
let isFinished = isQueueEmpty && isUploadsEmpty
|
||||||
|
let canProcess = isBellowLimit && !isQueueEmpty
|
||||||
|
|
||||||
|
if (isFinished) {
|
||||||
|
buttons.success('upload')
|
||||||
|
context.commit('reset')
|
||||||
|
context.commit('setReload', true, { root: true })
|
||||||
|
}
|
||||||
|
|
||||||
|
if (canProcess) {
|
||||||
|
const item = context.state.queue[0];
|
||||||
|
context.commit('moveJob')
|
||||||
|
|
||||||
|
if (item.file.isDir) {
|
||||||
|
await api.post(item.path).catch(Vue.prototype.$showError)
|
||||||
|
} else {
|
||||||
|
let onUpload = throttle(
|
||||||
|
(event) => context.commit('setProgress', { id: item.id, loaded: event.loaded }),
|
||||||
|
100, { leading: true, trailing: false }
|
||||||
|
)
|
||||||
|
|
||||||
|
await api.post(item.path, item.file, item.overwrite, onUpload).catch(Vue.prototype.$showError)
|
||||||
|
}
|
||||||
|
|
||||||
|
context.dispatch('finishUpload', item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default { state, mutations, actions, namespaced: true }
|
@ -82,9 +82,6 @@ const mutations = {
|
|||||||
resetClipboard: (state) => {
|
resetClipboard: (state) => {
|
||||||
state.clipboard.key = ''
|
state.clipboard.key = ''
|
||||||
state.clipboard.items = []
|
state.clipboard.items = []
|
||||||
},
|
|
||||||
setProgress: (state, value) => {
|
|
||||||
state.progress = value
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
133
frontend/src/utils/upload.js
Normal file
133
frontend/src/utils/upload.js
Normal file
@ -0,0 +1,133 @@
|
|||||||
|
import store from '@/store'
|
||||||
|
import url from '@/utils/url'
|
||||||
|
|
||||||
|
export function checkConflict(files, items) {
|
||||||
|
if (typeof items === 'undefined' || items === null) {
|
||||||
|
items = []
|
||||||
|
}
|
||||||
|
|
||||||
|
let folder_upload = false
|
||||||
|
if (files[0].fullPath !== undefined) {
|
||||||
|
folder_upload = true
|
||||||
|
}
|
||||||
|
|
||||||
|
let conflict = false
|
||||||
|
for (let i = 0; i < files.length; i++) {
|
||||||
|
let file = files[i]
|
||||||
|
let name = file.name
|
||||||
|
|
||||||
|
if (folder_upload) {
|
||||||
|
let dirs = file.fullPath.split("/")
|
||||||
|
if (dirs.length > 1) {
|
||||||
|
name = dirs[0]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let res = items.findIndex(function hasConflict(element) {
|
||||||
|
return (element.name === this)
|
||||||
|
}, name)
|
||||||
|
|
||||||
|
if (res >= 0) {
|
||||||
|
conflict = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return conflict
|
||||||
|
}
|
||||||
|
|
||||||
|
export function scanFiles(dt) {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
let reading = 0
|
||||||
|
const contents = []
|
||||||
|
|
||||||
|
if (dt.items !== undefined) {
|
||||||
|
for (let item of dt.items) {
|
||||||
|
if (item.kind === "file" && typeof item.webkitGetAsEntry === "function") {
|
||||||
|
const entry = item.webkitGetAsEntry()
|
||||||
|
readEntry(entry)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
resolve(dt.files)
|
||||||
|
}
|
||||||
|
|
||||||
|
function readEntry(entry, directory = "") {
|
||||||
|
if (entry.isFile) {
|
||||||
|
reading++
|
||||||
|
entry.file(file => {
|
||||||
|
reading--
|
||||||
|
|
||||||
|
file.fullPath = `${directory}${file.name}`
|
||||||
|
contents.push(file)
|
||||||
|
|
||||||
|
if (reading === 0) {
|
||||||
|
resolve(contents)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else if (entry.isDirectory) {
|
||||||
|
const dir = {
|
||||||
|
isDir: true,
|
||||||
|
size: 0,
|
||||||
|
path: `${directory}${entry.name}`
|
||||||
|
}
|
||||||
|
|
||||||
|
contents.push(dir)
|
||||||
|
|
||||||
|
readReaderContent(entry.createReader(), `${directory}${entry.name}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function readReaderContent(reader, directory) {
|
||||||
|
reading++
|
||||||
|
|
||||||
|
reader.readEntries(function (entries) {
|
||||||
|
reading--
|
||||||
|
if (entries.length > 0) {
|
||||||
|
for (const entry of entries) {
|
||||||
|
readEntry(entry, `${directory}/`)
|
||||||
|
}
|
||||||
|
|
||||||
|
readReaderContent(reader, `${directory}/`)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (reading === 0) {
|
||||||
|
resolve(contents)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function handleFiles(files, path, overwrite = false) {
|
||||||
|
for (let i = 0; i < files.length; i++) {
|
||||||
|
let file = files[i]
|
||||||
|
|
||||||
|
let filename = (file.fullPath !== undefined) ? file.fullPath : file.name
|
||||||
|
let filenameEncoded = url.encodeRFC5987ValueChars(filename)
|
||||||
|
|
||||||
|
let id = store.state.upload.id
|
||||||
|
|
||||||
|
let itemPath = path + filenameEncoded
|
||||||
|
|
||||||
|
if (file.isDir) {
|
||||||
|
itemPath = path
|
||||||
|
let folders = file.path.split("/")
|
||||||
|
|
||||||
|
for (let i = 0; i < folders.length; i++) {
|
||||||
|
let folder = folders[i]
|
||||||
|
let folderEncoded = encodeURIComponent(folder)
|
||||||
|
itemPath += folderEncoded + "/"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const item = {
|
||||||
|
id,
|
||||||
|
path: itemPath,
|
||||||
|
file,
|
||||||
|
overwrite
|
||||||
|
}
|
||||||
|
|
||||||
|
store.dispatch('upload/upload', item);
|
||||||
|
}
|
||||||
|
}
|
@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<div id="progress">
|
<div id="progress">
|
||||||
<div v-bind:style="{ width: $store.state.progress + '%' }"></div>
|
<div v-bind:style="{ width: this.progress + '%' }"></div>
|
||||||
</div>
|
</div>
|
||||||
<site-header></site-header>
|
<site-header></site-header>
|
||||||
<sidebar></sidebar>
|
<sidebar></sidebar>
|
||||||
@ -29,7 +29,7 @@ export default {
|
|||||||
Shell
|
Shell
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapGetters([ 'isLogged' ]),
|
...mapGetters([ 'isLogged', 'progress' ]),
|
||||||
...mapState([ 'user' ])
|
...mapState([ 'user' ])
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
|
Loading…
Reference in New Issue
Block a user