chore: move files to frontend

This commit is contained in:
Henrique Dias
2019-05-21 11:13:59 +01:00
parent d45d7f92fb
commit 7414ca10b3
137 changed files with 0 additions and 0 deletions

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>