chore: move files to frontend
This commit is contained in:
67
frontend/src/components/prompts/Copy.vue
Normal file
67
frontend/src/components/prompts/Copy.vue
Normal file
@@ -0,0 +1,67 @@
|
||||
<template>
|
||||
<div class="card floating">
|
||||
<div class="card-title">
|
||||
<h2>{{ $t('prompts.copy') }}</h2>
|
||||
</div>
|
||||
|
||||
<div class="card-content">
|
||||
<p>{{ $t('prompts.copyMessage') }}</p>
|
||||
<file-list @update:selected="val => dest = val"></file-list>
|
||||
</div>
|
||||
|
||||
<div class="card-action">
|
||||
<button class="button button--flat button--grey"
|
||||
@click="$store.commit('closeHovers')"
|
||||
:aria-label="$t('buttons.cancel')"
|
||||
:title="$t('buttons.cancel')">{{ $t('buttons.cancel') }}</button>
|
||||
<button class="button button--flat"
|
||||
@click="copy"
|
||||
:disabled="$route.path === dest"
|
||||
:aria-label="$t('buttons.copy')"
|
||||
:title="$t('buttons.copy')">{{ $t('buttons.copy') }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapState } from 'vuex'
|
||||
import FileList from './FileList'
|
||||
import { files as api } from '@/api'
|
||||
import buttons from '@/utils/buttons'
|
||||
|
||||
export default {
|
||||
name: 'copy',
|
||||
components: { FileList },
|
||||
data: function () {
|
||||
return {
|
||||
current: window.location.pathname,
|
||||
dest: null
|
||||
}
|
||||
},
|
||||
computed: mapState(['req', 'selected']),
|
||||
methods: {
|
||||
copy: async function (event) {
|
||||
event.preventDefault()
|
||||
buttons.loading('copy')
|
||||
let items = []
|
||||
|
||||
// Create a new promise for each file.
|
||||
for (let item of this.selected) {
|
||||
items.push({
|
||||
from: this.req.items[item].url,
|
||||
to: this.dest + encodeURIComponent(this.req.items[item].name)
|
||||
})
|
||||
}
|
||||
|
||||
try {
|
||||
await api.copy(items)
|
||||
buttons.success('copy')
|
||||
this.$router.push({ path: this.dest })
|
||||
} catch (e) {
|
||||
buttons.done('copy')
|
||||
this.$showError(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
66
frontend/src/components/prompts/Delete.vue
Normal file
66
frontend/src/components/prompts/Delete.vue
Normal file
@@ -0,0 +1,66 @@
|
||||
<template>
|
||||
<div class="card floating">
|
||||
<div class="card-content">
|
||||
<p v-if="req.kind !== 'listing'">{{ $t('prompts.deleteMessageSingle') }}</p>
|
||||
<p v-else>{{ $t('prompts.deleteMessageMultiple', { count: selectedCount}) }}</p>
|
||||
</div>
|
||||
<div class="card-action">
|
||||
<button @click="$store.commit('closeHovers')"
|
||||
class="button button--flat button--grey"
|
||||
:aria-label="$t('buttons.cancel')"
|
||||
:title="$t('buttons.cancel')">{{ $t('buttons.cancel') }}</button>
|
||||
<button @click="submit"
|
||||
class="button button--flat button--red"
|
||||
:aria-label="$t('buttons.delete')"
|
||||
:title="$t('buttons.delete')">{{ $t('buttons.delete') }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {mapGetters, mapMutations, mapState} from 'vuex'
|
||||
import { files as api } from '@/api'
|
||||
import url from '@/utils/url'
|
||||
import buttons from '@/utils/buttons'
|
||||
|
||||
export default {
|
||||
name: 'delete',
|
||||
computed: {
|
||||
...mapGetters(['isListing', 'selectedCount']),
|
||||
...mapState(['req', 'selected'])
|
||||
},
|
||||
methods: {
|
||||
...mapMutations(['closeHovers']),
|
||||
submit: async function () {
|
||||
this.closeHovers()
|
||||
buttons.loading('delete')
|
||||
|
||||
try {
|
||||
if (!this.isListing) {
|
||||
await api.remove(this.$route.path)
|
||||
buttons.success('delete')
|
||||
this.$router.push({ path: url.removeLastDir(this.$route.path) + '/' })
|
||||
return
|
||||
}
|
||||
|
||||
if (this.selectedCount === 0) {
|
||||
return
|
||||
}
|
||||
|
||||
let promises = []
|
||||
for (let index of this.selected) {
|
||||
promises.push(api.remove(this.req.items[index].url))
|
||||
}
|
||||
|
||||
await Promise.all(promises)
|
||||
buttons.success('delete')
|
||||
this.$store.commit('setReload', true)
|
||||
} catch (e) {
|
||||
buttons.done('delete')
|
||||
this.$showError(e)
|
||||
if (this.isListing) this.$store.commit('setReload', true)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
49
frontend/src/components/prompts/Download.vue
Normal file
49
frontend/src/components/prompts/Download.vue
Normal file
@@ -0,0 +1,49 @@
|
||||
<template>
|
||||
<div class="card floating" id="download">
|
||||
<div class="card-title">
|
||||
<h2>{{ $t('prompts.download') }}</h2>
|
||||
</div>
|
||||
|
||||
<div class="card-content">
|
||||
<p>{{ $t('prompts.downloadMessage') }}</p>
|
||||
|
||||
<button class="button button--block" @click="download('zip')" v-focus>zip</button>
|
||||
<button class="button button--block" @click="download('tar')" v-focus>tar</button>
|
||||
<button class="button button--block" @click="download('targz')" v-focus>tar.gz</button>
|
||||
<button class="button button--block" @click="download('tarbz2')" v-focus>tar.bz2</button>
|
||||
<button class="button button--block" @click="download('tarxz')" v-focus>tar.xz</button>
|
||||
<button class="button button--block" @click="download('tarlz4')" v-focus>tar.lz4</button>
|
||||
<button class="button button--block" @click="download('tarsz')" v-focus>tar.sz</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {mapGetters, mapState} from 'vuex'
|
||||
import { files as api } from '@/api'
|
||||
|
||||
export default {
|
||||
name: 'download',
|
||||
computed: {
|
||||
...mapState(['selected', 'req']),
|
||||
...mapGetters(['selectedCount'])
|
||||
},
|
||||
methods: {
|
||||
download: function (format) {
|
||||
if (this.selectedCount === 0) {
|
||||
api.download(format, this.$route.path)
|
||||
} else {
|
||||
let files = []
|
||||
|
||||
for (let i of this.selected) {
|
||||
files.push(this.req.items[i].url)
|
||||
}
|
||||
|
||||
api.download(format, ...files)
|
||||
}
|
||||
|
||||
this.$store.commit('closeHovers')
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
140
frontend/src/components/prompts/FileList.vue
Normal file
140
frontend/src/components/prompts/FileList.vue
Normal file
@@ -0,0 +1,140 @@
|
||||
<template>
|
||||
<div>
|
||||
<ul class="file-list">
|
||||
<li @click="select"
|
||||
@touchstart="touchstart"
|
||||
@dblclick="next"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
:aria-label="item.name"
|
||||
:aria-selected="selected == item.url"
|
||||
:key="item.name" v-for="item in items"
|
||||
:data-url="item.url">{{ item.name }}</li>
|
||||
</ul>
|
||||
|
||||
<p>{{ $t('prompts.currentlyNavigating') }} <code>{{ nav }}</code>.</p>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapState } from 'vuex'
|
||||
import url from '@/utils/url'
|
||||
import { files } from '@/api'
|
||||
|
||||
export default {
|
||||
name: 'file-list',
|
||||
data: function () {
|
||||
return {
|
||||
items: [],
|
||||
touches: {
|
||||
id: '',
|
||||
count: 0
|
||||
},
|
||||
selected: null,
|
||||
current: window.location.pathname
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapState([ 'req' ]),
|
||||
nav () {
|
||||
return decodeURIComponent(this.current)
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
// If we're showing this on a listing,
|
||||
// we can use the current request object
|
||||
// to fill the move options.
|
||||
if (this.req.kind === 'listing') {
|
||||
this.fillOptions(this.req)
|
||||
return
|
||||
}
|
||||
|
||||
// Otherwise, we must be on a preview or editor
|
||||
// so we fetch the data from the previous directory.
|
||||
files.fetch(url.removeLastDir(this.$route.path))
|
||||
.then(this.fillOptions)
|
||||
.catch(this.$showError)
|
||||
},
|
||||
methods: {
|
||||
fillOptions (req) {
|
||||
// Sets the current path and resets
|
||||
// the current items.
|
||||
this.current = req.url
|
||||
this.items = []
|
||||
|
||||
this.$emit('update:selected', this.current)
|
||||
|
||||
// If the path isn't the root path,
|
||||
// show a button to navigate to the previous
|
||||
// directory.
|
||||
if (req.url !== '/files/') {
|
||||
this.items.push({
|
||||
name: '..',
|
||||
url: url.removeLastDir(req.url) + '/'
|
||||
})
|
||||
}
|
||||
|
||||
// If this folder is empty, finish here.
|
||||
if (req.items === null) return
|
||||
|
||||
// Otherwise we add every directory to the
|
||||
// move options.
|
||||
for (let item of req.items) {
|
||||
if (!item.isDir) continue
|
||||
|
||||
this.items.push({
|
||||
name: item.name,
|
||||
url: item.url
|
||||
})
|
||||
}
|
||||
},
|
||||
next: function (event) {
|
||||
// Retrieves the URL of the directory the user
|
||||
// just clicked in and fill the options with its
|
||||
// content.
|
||||
let uri = event.currentTarget.dataset.url
|
||||
|
||||
files.fetch(uri)
|
||||
.then(this.fillOptions)
|
||||
.catch(this.$showError)
|
||||
},
|
||||
touchstart (event) {
|
||||
let url = event.currentTarget.dataset.url
|
||||
|
||||
// In 300 milliseconds, we shall reset the count.
|
||||
setTimeout(() => {
|
||||
this.touches.count = 0
|
||||
}, 300)
|
||||
|
||||
// If the element the user is touching
|
||||
// is different from the last one he touched,
|
||||
// reset the count.
|
||||
if (this.touches.id !== url) {
|
||||
this.touches.id = url
|
||||
this.touches.count = 1
|
||||
return
|
||||
}
|
||||
|
||||
this.touches.count++
|
||||
|
||||
// If there is more than one touch already,
|
||||
// open the next screen.
|
||||
if (this.touches.count > 1) {
|
||||
this.next(event)
|
||||
}
|
||||
},
|
||||
select: function (event) {
|
||||
// If the element is already selected, unselect it.
|
||||
if (this.selected === event.currentTarget.dataset.url) {
|
||||
this.selected = null
|
||||
this.$emit('update:selected', this.current)
|
||||
return
|
||||
}
|
||||
|
||||
// Otherwise select the element.
|
||||
this.selected = event.currentTarget.dataset.url
|
||||
this.$emit('update:selected', this.selected)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
34
frontend/src/components/prompts/Help.vue
Normal file
34
frontend/src/components/prompts/Help.vue
Normal file
@@ -0,0 +1,34 @@
|
||||
<template>
|
||||
<div class="card floating help">
|
||||
<div class="card-title">
|
||||
<h2>{{ $t('help.help') }}</h2>
|
||||
</div>
|
||||
|
||||
<div class="card-content">
|
||||
<ul>
|
||||
<li><strong>F1</strong> - {{ $t('help.f1') }}</li>
|
||||
<li><strong>F2</strong> - {{ $t('help.f2') }}</li>
|
||||
<li><strong>DEL</strong> - {{ $t('help.del') }}</li>
|
||||
<li><strong>ESC</strong> - {{ $t('help.esc') }}</li>
|
||||
<li><strong>CTRL + S</strong> - {{ $t('help.ctrl.s') }}</li>
|
||||
<li><strong>CTRL + F</strong> - {{ $t('help.ctrl.f') }}</li>
|
||||
<li><strong>CTRL + Click</strong> - {{ $t('help.ctrl.click') }}</li>
|
||||
<li><strong>Click</strong> - {{ $t('help.click') }}</li>
|
||||
<li><strong>Double click</strong> - {{ $t('help.doubleClick') }}</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="card-action">
|
||||
<button type="submit"
|
||||
@click="$store.commit('closeHovers')"
|
||||
class="button button--flat"
|
||||
:aria-label="$t('buttons.ok')"
|
||||
:title="$t('buttons.ok')">{{ $t('buttons.ok') }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default { name: 'help' }
|
||||
</script>
|
||||
|
||||
98
frontend/src/components/prompts/Info.vue
Normal file
98
frontend/src/components/prompts/Info.vue
Normal file
@@ -0,0 +1,98 @@
|
||||
<template>
|
||||
<div class="card floating">
|
||||
<div class="card-title">
|
||||
<h2>{{ $t('prompts.fileInfo') }}</h2>
|
||||
</div>
|
||||
|
||||
<div class="card-content">
|
||||
<p v-if="selected.length > 1">{{ $t('prompts.filesSelected', { count: selected.length }) }}</p>
|
||||
|
||||
<p v-if="selected.length < 2"><strong>{{ $t('prompts.displayName') }}</strong> {{ name }}</p>
|
||||
<p v-if="!dir || selected.length > 1"><strong>{{ $t('prompts.size') }}:</strong> <span id="content_length"></span> {{ humanSize }}</p>
|
||||
<p v-if="selected.length < 2"><strong>{{ $t('prompts.lastModified') }}:</strong> {{ humanTime }}</p>
|
||||
|
||||
<template v-if="dir && selected.length === 0">
|
||||
<p><strong>{{ $t('prompts.numberFiles') }}:</strong> {{ req.numFiles }}</p>
|
||||
<p><strong>{{ $t('prompts.numberDirs') }}:</strong> {{ req.numDirs }}</p>
|
||||
</template>
|
||||
|
||||
<template v-if="!dir">
|
||||
<p><strong>MD5: </strong><code><a @click="checksum($event, 'md5')">{{ $t('prompts.show') }}</a></code></p>
|
||||
<p><strong>SHA1: </strong><code><a @click="checksum($event, 'sha1')">{{ $t('prompts.show') }}</a></code></p>
|
||||
<p><strong>SHA256: </strong><code><a @click="checksum($event, 'sha256')">{{ $t('prompts.show') }}</a></code></p>
|
||||
<p><strong>SHA512: </strong><code><a @click="checksum($event, 'sha512')">{{ $t('prompts.show') }}</a></code></p>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
<div class="card-action">
|
||||
<button type="submit"
|
||||
@click="$store.commit('closeHovers')"
|
||||
class="button button--flat"
|
||||
:aria-label="$t('buttons.ok')"
|
||||
:title="$t('buttons.ok')">{{ $t('buttons.ok') }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {mapState, mapGetters} from 'vuex'
|
||||
import filesize from 'filesize'
|
||||
import moment from 'moment'
|
||||
import { files as api } from '@/api'
|
||||
|
||||
export default {
|
||||
name: 'info',
|
||||
computed: {
|
||||
...mapState(['req', 'selected']),
|
||||
...mapGetters(['selectedCount', 'isListing']),
|
||||
humanSize: function () {
|
||||
if (this.selectedCount === 0 || !this.isListing) {
|
||||
return filesize(this.req.size)
|
||||
}
|
||||
|
||||
let sum = 0
|
||||
|
||||
for (let selected of this.selected) {
|
||||
sum += this.req.items[selected].size
|
||||
}
|
||||
|
||||
return filesize(sum)
|
||||
},
|
||||
humanTime: function () {
|
||||
if (this.selectedCount === 0) {
|
||||
return moment(this.req.modified).fromNow()
|
||||
}
|
||||
|
||||
return moment(this.req.items[this.selected[0]]).fromNow()
|
||||
},
|
||||
name: function () {
|
||||
return this.selectedCount === 0 ? this.req.name : this.req.items[this.selected[0]].name
|
||||
},
|
||||
dir: function () {
|
||||
return this.selectedCount > 1 || (this.selectedCount === 0
|
||||
? this.req.isDir
|
||||
: this.req.items[this.selected[0]].isDir)
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
checksum: async function (event, algo) {
|
||||
event.preventDefault()
|
||||
|
||||
let link
|
||||
|
||||
if (this.selectedCount) {
|
||||
link = this.req.items[this.selected[0]].url
|
||||
} else {
|
||||
link = this.$route.path
|
||||
}
|
||||
|
||||
try {
|
||||
const hash = await api.checksum(link, algo)
|
||||
event.target.innerHTML = hash
|
||||
} catch (e) {
|
||||
this.$showError(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
67
frontend/src/components/prompts/Move.vue
Normal file
67
frontend/src/components/prompts/Move.vue
Normal file
@@ -0,0 +1,67 @@
|
||||
<template>
|
||||
<div class="card floating">
|
||||
<div class="card-title">
|
||||
<h2>{{ $t('prompts.move') }}</h2>
|
||||
</div>
|
||||
|
||||
<div class="card-content">
|
||||
<file-list @update:selected="val => dest = val"></file-list>
|
||||
</div>
|
||||
|
||||
<div class="card-action">
|
||||
<button class="button button--flat button--grey"
|
||||
@click="$store.commit('closeHovers')"
|
||||
:aria-label="$t('buttons.cancel')"
|
||||
:title="$t('buttons.cancel')">{{ $t('buttons.cancel') }}</button>
|
||||
<button class="button button--flat"
|
||||
@click="move"
|
||||
:disabled="$route.path === dest"
|
||||
:aria-label="$t('buttons.move')"
|
||||
:title="$t('buttons.move')">{{ $t('buttons.move') }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapState } from 'vuex'
|
||||
import FileList from './FileList'
|
||||
import { files as api } from '@/api'
|
||||
import buttons from '@/utils/buttons'
|
||||
|
||||
export default {
|
||||
name: 'move',
|
||||
components: { FileList },
|
||||
data: function () {
|
||||
return {
|
||||
current: window.location.pathname,
|
||||
dest: null
|
||||
}
|
||||
},
|
||||
computed: mapState(['req', 'selected']),
|
||||
methods: {
|
||||
move: async function (event) {
|
||||
event.preventDefault()
|
||||
buttons.loading('move')
|
||||
let items = []
|
||||
|
||||
for (let item of this.selected) {
|
||||
items.push({
|
||||
from: this.req.items[item].url,
|
||||
to: this.dest + encodeURIComponent(this.req.items[item].name)
|
||||
})
|
||||
}
|
||||
|
||||
try {
|
||||
api.move(items)
|
||||
buttons.success('move')
|
||||
this.$router.push({ path: this.dest })
|
||||
} catch (e) {
|
||||
buttons.done('move')
|
||||
this.$showError(e)
|
||||
}
|
||||
|
||||
event.preventDefault()
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
71
frontend/src/components/prompts/NewDir.vue
Normal file
71
frontend/src/components/prompts/NewDir.vue
Normal file
@@ -0,0 +1,71 @@
|
||||
<template>
|
||||
<div class="card floating">
|
||||
<div class="card-title">
|
||||
<h2>{{ $t('prompts.newDir') }}</h2>
|
||||
</div>
|
||||
|
||||
<div class="card-content">
|
||||
<p>{{ $t('prompts.newDirMessage') }}</p>
|
||||
<input class="input input--block" type="text" @keyup.enter="submit" v-model.trim="name" v-focus>
|
||||
</div>
|
||||
|
||||
<div class="card-action">
|
||||
<button
|
||||
class="button button--flat button--grey"
|
||||
@click="$store.commit('closeHovers')"
|
||||
:aria-label="$t('buttons.cancel')"
|
||||
:title="$t('buttons.cancel')"
|
||||
>{{ $t('buttons.cancel') }}</button>
|
||||
<button
|
||||
class="button button--flat"
|
||||
:aria-label="$t('buttons.create')"
|
||||
:title="$t('buttons.create')"
|
||||
@click="submit"
|
||||
>{{ $t('buttons.create') }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapGetters } from 'vuex'
|
||||
import { files as api } from '@/api'
|
||||
import url from '@/utils/url'
|
||||
|
||||
export default {
|
||||
name: 'new-dir',
|
||||
data: function() {
|
||||
return {
|
||||
name: ''
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapGetters([ 'isFiles', 'isListing' ])
|
||||
},
|
||||
methods: {
|
||||
submit: async function(event) {
|
||||
event.preventDefault()
|
||||
if (this.new === '') return
|
||||
|
||||
// Build the path of the new directory.
|
||||
let uri = this.isFiles ? this.$route.path + '/' : '/'
|
||||
|
||||
if (!this.isListing) {
|
||||
uri = url.removeLastDir(uri) + '/'
|
||||
}
|
||||
|
||||
uri += encodeURIComponent(this.name) + '/'
|
||||
uri = uri.replace('//', '/')
|
||||
|
||||
try {
|
||||
await api.post(uri)
|
||||
this.$router.push({ path: uri })
|
||||
} catch (e) {
|
||||
this.$showError(e)
|
||||
}
|
||||
|
||||
this.$store.commit('closeHovers')
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
71
frontend/src/components/prompts/NewFile.vue
Normal file
71
frontend/src/components/prompts/NewFile.vue
Normal file
@@ -0,0 +1,71 @@
|
||||
<template>
|
||||
<div class="card floating">
|
||||
<div class="card-title">
|
||||
<h2>{{ $t('prompts.newFile') }}</h2>
|
||||
</div>
|
||||
|
||||
<div class="card-content">
|
||||
<p>{{ $t('prompts.newFileMessage') }}</p>
|
||||
<input class="input input--block" v-focus type="text" @keyup.enter="submit" v-model.trim="name">
|
||||
</div>
|
||||
|
||||
<div class="card-action">
|
||||
<button
|
||||
class="button button--flat button--grey"
|
||||
@click="$store.commit('closeHovers')"
|
||||
:aria-label="$t('buttons.cancel')"
|
||||
:title="$t('buttons.cancel')"
|
||||
>{{ $t('buttons.cancel') }}</button>
|
||||
<button
|
||||
class="button button--flat"
|
||||
@click="submit"
|
||||
:aria-label="$t('buttons.create')"
|
||||
:title="$t('buttons.create')"
|
||||
>{{ $t('buttons.create') }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapGetters } from 'vuex'
|
||||
import { files as api } from '@/api'
|
||||
import url from '@/utils/url'
|
||||
|
||||
export default {
|
||||
name: 'new-file',
|
||||
data: function() {
|
||||
return {
|
||||
name: ''
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapGetters([ 'isFiles', 'isListing' ])
|
||||
},
|
||||
methods: {
|
||||
submit: async function(event) {
|
||||
event.preventDefault()
|
||||
if (this.new === '') return
|
||||
|
||||
// Build the path of the new directory.
|
||||
let uri = this.isFiles ? this.$route.path + '/' : '/'
|
||||
|
||||
if (!this.isListing) {
|
||||
uri = url.removeLastDir(uri) + '/'
|
||||
}
|
||||
|
||||
uri += encodeURIComponent(this.name)
|
||||
uri = uri.replace('//', '/')
|
||||
|
||||
try {
|
||||
await api.post(uri)
|
||||
this.$router.push({ path: uri })
|
||||
} catch (e) {
|
||||
this.$showError(e)
|
||||
}
|
||||
|
||||
this.$store.commit('closeHovers')
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
79
frontend/src/components/prompts/Prompts.vue
Normal file
79
frontend/src/components/prompts/Prompts.vue
Normal file
@@ -0,0 +1,79 @@
|
||||
<template>
|
||||
<div>
|
||||
<help v-if="showHelp" ></help>
|
||||
<download v-else-if="showDownload"></download>
|
||||
<new-file v-else-if="showNewFile"></new-file>
|
||||
<new-dir v-else-if="showNewDir"></new-dir>
|
||||
<rename v-else-if="showRename"></rename>
|
||||
<delete v-else-if="showDelete"></delete>
|
||||
<info v-else-if="showInfo"></info>
|
||||
<move v-else-if="showMove"></move>
|
||||
<copy v-else-if="showCopy"></copy>
|
||||
<replace v-else-if="showReplace"></replace>
|
||||
<share v-else-if="show === 'share'"></share>
|
||||
<div v-show="showOverlay" @click="resetPrompts" class="overlay"></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Help from './Help'
|
||||
import Info from './Info'
|
||||
import Delete from './Delete'
|
||||
import Rename from './Rename'
|
||||
import Download from './Download'
|
||||
import Move from './Move'
|
||||
import Copy from './Copy'
|
||||
import NewFile from './NewFile'
|
||||
import NewDir from './NewDir'
|
||||
import Replace from './Replace'
|
||||
import Share from './Share'
|
||||
import { mapState } from 'vuex'
|
||||
import buttons from '@/utils/buttons'
|
||||
|
||||
export default {
|
||||
name: 'prompts',
|
||||
components: {
|
||||
Info,
|
||||
Delete,
|
||||
Rename,
|
||||
Download,
|
||||
Move,
|
||||
Copy,
|
||||
Share,
|
||||
NewFile,
|
||||
NewDir,
|
||||
Help,
|
||||
Replace
|
||||
},
|
||||
data: function () {
|
||||
return {
|
||||
pluginData: {
|
||||
buttons,
|
||||
'store': this.$store,
|
||||
'router': this.$router
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapState(['show', 'plugins']),
|
||||
showInfo: function () { return this.show === 'info' },
|
||||
showHelp: function () { return this.show === 'help' },
|
||||
showDelete: function () { return this.show === 'delete' },
|
||||
showRename: function () { return this.show === 'rename' },
|
||||
showMove: function () { return this.show === 'move' },
|
||||
showCopy: function () { return this.show === 'copy' },
|
||||
showNewFile: function () { return this.show === 'newFile' },
|
||||
showNewDir: function () { return this.show === 'newDir' },
|
||||
showDownload: function () { return this.show === 'download' },
|
||||
showReplace: function () { return this.show === 'replace' },
|
||||
showOverlay: function () {
|
||||
return (this.show !== null && this.show !== 'search' && this.show !== 'more')
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
resetPrompts () {
|
||||
this.$store.commit('closeHovers')
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
89
frontend/src/components/prompts/Rename.vue
Normal file
89
frontend/src/components/prompts/Rename.vue
Normal file
@@ -0,0 +1,89 @@
|
||||
<template>
|
||||
<div class="card floating">
|
||||
<div class="card-title">
|
||||
<h2>{{ $t('prompts.rename') }}</h2>
|
||||
</div>
|
||||
|
||||
<div class="card-content">
|
||||
<p>{{ $t('prompts.renameMessage') }} <code>{{ oldName() }}</code>:</p>
|
||||
<input class="input input--block" v-focus type="text" @keyup.enter="submit" v-model.trim="name">
|
||||
</div>
|
||||
|
||||
<div class="card-action">
|
||||
<button class="button button--flat button--grey"
|
||||
@click="$store.commit('closeHovers')"
|
||||
:aria-label="$t('buttons.cancel')"
|
||||
:title="$t('buttons.cancel')">{{ $t('buttons.cancel') }}</button>
|
||||
<button @click="submit"
|
||||
class="button button--flat"
|
||||
type="submit"
|
||||
:aria-label="$t('buttons.rename')"
|
||||
:title="$t('buttons.rename')">{{ $t('buttons.rename') }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapState, mapGetters } from 'vuex'
|
||||
import url from '@/utils/url'
|
||||
import { files as api } from '@/api'
|
||||
|
||||
export default {
|
||||
name: 'rename',
|
||||
data: function () {
|
||||
return {
|
||||
name: ''
|
||||
}
|
||||
},
|
||||
created () {
|
||||
this.name = this.oldName()
|
||||
},
|
||||
computed: {
|
||||
...mapState(['req', 'selected', 'selectedCount']),
|
||||
...mapGetters(['isListing'])
|
||||
},
|
||||
methods: {
|
||||
cancel: function () {
|
||||
this.$store.commit('closeHovers')
|
||||
},
|
||||
oldName: function () {
|
||||
if (!this.isListing) {
|
||||
return this.req.name
|
||||
}
|
||||
|
||||
if (this.selectedCount === 0 || this.selectedCount > 1) {
|
||||
// This shouldn't happen.
|
||||
return
|
||||
}
|
||||
|
||||
return this.req.items[this.selected[0]].name
|
||||
},
|
||||
submit: async function () {
|
||||
let oldLink = ''
|
||||
let newLink = ''
|
||||
|
||||
if (!this.isListing) {
|
||||
oldLink = this.req.url
|
||||
} else {
|
||||
oldLink = this.req.items[this.selected[0]].url
|
||||
}
|
||||
|
||||
newLink = url.removeLastDir(oldLink) + '/' + encodeURIComponent(this.name)
|
||||
|
||||
try {
|
||||
await api.move([{ from: oldLink, to: newLink }])
|
||||
if (!this.isListing) {
|
||||
this.$router.push({ path: newLink })
|
||||
return
|
||||
}
|
||||
|
||||
this.$store.commit('setReload', true)
|
||||
} catch (e) {
|
||||
this.$showError(e)
|
||||
}
|
||||
|
||||
this.$store.commit('closeHovers')
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
31
frontend/src/components/prompts/Replace.vue
Normal file
31
frontend/src/components/prompts/Replace.vue
Normal file
@@ -0,0 +1,31 @@
|
||||
<template>
|
||||
<div class="card floating">
|
||||
<div class="card-title">
|
||||
<h2>{{ $t('prompts.replace') }}</h2>
|
||||
</div>
|
||||
|
||||
<div class="card-content">
|
||||
<p>{{ $t('prompts.replaceMessage') }}</p>
|
||||
</div>
|
||||
|
||||
<div class="card-action">
|
||||
<button class="button button--flat button--grey"
|
||||
@click="$store.commit('closeHovers')"
|
||||
:aria-label="$t('buttons.cancel')"
|
||||
:title="$t('buttons.cancel')">{{ $t('buttons.cancel') }}</button>
|
||||
<button class="button button--flat button--red"
|
||||
@click="showConfirm"
|
||||
:aria-label="$t('buttons.replace')"
|
||||
:title="$t('buttons.replace')">{{ $t('buttons.replace') }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapState } from 'vuex'
|
||||
|
||||
export default {
|
||||
name: 'replace',
|
||||
computed: mapState(['showConfirm'])
|
||||
}
|
||||
</script>
|
||||
167
frontend/src/components/prompts/Share.vue
Normal file
167
frontend/src/components/prompts/Share.vue
Normal file
@@ -0,0 +1,167 @@
|
||||
<template>
|
||||
<div class="card floating" id="share">
|
||||
<div class="card-title">
|
||||
<h2>{{ $t('buttons.share') }}</h2>
|
||||
</div>
|
||||
|
||||
<div class="card-content">
|
||||
<ul>
|
||||
<li v-if="!hasPermanent">
|
||||
<a @click="getPermalink" :aria-label="$t('buttons.permalink')">{{ $t('buttons.permalink') }}</a>
|
||||
</li>
|
||||
|
||||
<li v-for="link in links" :key="link.hash">
|
||||
<a :href="buildLink(link.hash)" target="_blank">
|
||||
<template v-if="link.expire !== 0">{{ humanTime(link.expire) }}</template>
|
||||
<template v-else>{{ $t('permanent') }}</template>
|
||||
</a>
|
||||
|
||||
<button class="action"
|
||||
@click="deleteLink($event, link)"
|
||||
:aria-label="$t('buttons.delete')"
|
||||
:title="$t('buttons.delete')"><i class="material-icons">delete</i></button>
|
||||
|
||||
<button class="action copy-clipboard"
|
||||
:data-clipboard-text="buildLink(link.hash)"
|
||||
:aria-label="$t('buttons.copyToClipboard')"
|
||||
:title="$t('buttons.copyToClipboard')"><i class="material-icons">content_paste</i></button>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<input v-focus
|
||||
type="number"
|
||||
max="2147483647"
|
||||
min="0"
|
||||
@keyup.enter="submit"
|
||||
v-model.trim="time">
|
||||
<select v-model="unit" :aria-label="$t('time.unit')">
|
||||
<option value="seconds">{{ $t('time.seconds') }}</option>
|
||||
<option value="minutes">{{ $t('time.minutes') }}</option>
|
||||
<option value="hours">{{ $t('time.hours') }}</option>
|
||||
<option value="days">{{ $t('time.days') }}</option>
|
||||
</select>
|
||||
<button class="action"
|
||||
@click="submit"
|
||||
:aria-label="$t('buttons.create')"
|
||||
:title="$t('buttons.create')"><i class="material-icons">add</i></button>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="card-action">
|
||||
<button class="button button--flat"
|
||||
@click="$store.commit('closeHovers')"
|
||||
:aria-label="$t('buttons.close')"
|
||||
:title="$t('buttons.close')">{{ $t('buttons.close') }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapState, mapGetters } from 'vuex'
|
||||
import { share as api } from '@/api'
|
||||
import { baseURL } from '@/utils/constants'
|
||||
import moment from 'moment'
|
||||
import Clipboard from 'clipboard'
|
||||
|
||||
export default {
|
||||
name: 'share',
|
||||
data: function () {
|
||||
return {
|
||||
time: '',
|
||||
unit: 'hours',
|
||||
hasPermanent: false,
|
||||
links: [],
|
||||
clip: null
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapState([ 'req', 'selected', 'selectedCount' ]),
|
||||
...mapGetters([ 'isListing' ]),
|
||||
url () {
|
||||
if (!this.isListing) {
|
||||
return this.$route.path
|
||||
}
|
||||
|
||||
if (this.selectedCount === 0 || this.selectedCount > 1) {
|
||||
// This shouldn't happen.
|
||||
return
|
||||
}
|
||||
|
||||
return this.req.items[this.selected[0]].url
|
||||
}
|
||||
},
|
||||
async beforeMount () {
|
||||
try {
|
||||
const links = await api.get(this.url)
|
||||
this.links = links
|
||||
this.sort()
|
||||
|
||||
for (let link of this.links) {
|
||||
if (link.expire === 0) {
|
||||
this.hasPermanent = true
|
||||
break
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
this.$showError(e)
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
this.clip = new Clipboard('.copy-clipboard')
|
||||
this.clip.on('success', () => {
|
||||
this.$showSuccess(this.$t('success.linkCopied'))
|
||||
})
|
||||
},
|
||||
beforeDestroy () {
|
||||
this.clip.destroy()
|
||||
},
|
||||
methods: {
|
||||
submit: async function () {
|
||||
if (!this.time) return
|
||||
|
||||
try {
|
||||
const res = await api.create(this.url, this.time, this.unit)
|
||||
this.links.push(res)
|
||||
this.sort()
|
||||
} catch (e) {
|
||||
this.$showError(e)
|
||||
}
|
||||
},
|
||||
getPermalink: async function () {
|
||||
try {
|
||||
const res = await api.create(this.url)
|
||||
this.links.push(res)
|
||||
this.sort()
|
||||
this.hasPermanent = true
|
||||
} catch (e) {
|
||||
this.$showError(e)
|
||||
}
|
||||
},
|
||||
deleteLink: async function (event, link) {
|
||||
event.preventDefault()
|
||||
try {
|
||||
await api.remove(link.hash)
|
||||
if (link.expire === 0) this.hasPermanent = false
|
||||
this.links = this.links.filter(item => item.hash !== link.hash)
|
||||
} catch (e) {
|
||||
this.$showError(e)
|
||||
}
|
||||
},
|
||||
humanTime (time) {
|
||||
return moment(time * 1000).fromNow()
|
||||
},
|
||||
buildLink (hash) {
|
||||
return `${window.location.origin}${baseURL}/share/${hash}`
|
||||
},
|
||||
sort () {
|
||||
this.links = this.links.sort((a, b) => {
|
||||
if (a.expire === 0) return -1
|
||||
if (b.expire === 0) return 1
|
||||
return new Date(a.expire) - new Date(b.expire)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user