Copy and cut files

Former-commit-id: ac9399efef37f13706590384e29295c6234acb7c [formerly f62dcb89aa49125769405c502688f3f8d4348da3] [formerly 4b9f0fd696f449c30da2d2f7a11cf5d6c7054c54 [formerly 64e70e39dcf6759e04315bc97ec6a0fed927c737]]
Former-commit-id: 422165231c785b121b1f6725909d3ce7c8dcdfee [formerly 2b26582a03f674d40abb39e27b2cc880d2bd3ebb]
Former-commit-id: ee743808a3c012514c9b7d57a34c79e408a8a78b
This commit is contained in:
Henrique Dias
2017-07-26 15:55:39 +01:00
parent 42bab1458e
commit 149465ab52
19 changed files with 371 additions and 230 deletions

View File

@@ -38,6 +38,7 @@
<div id="file-selection" v-if="isMobile && req.kind === 'listing'">
<span v-if="selectedCount > 0">{{ selectedCount }} selected</span>
<rename-button v-show="showRenameButton"></rename-button>
<copy-button v-show="showMoveButton"></copy-button>
<move-button v-show="showMoveButton"></move-button>
<delete-button v-show="showDeleteButton"></delete-button>
</div>
@@ -46,6 +47,7 @@
<div id="dropdown" :class="{ active: showMore }">
<div v-if="!isListing || !isMobile">
<rename-button v-show="showRenameButton"></rename-button>
<copy-button v-show="showMoveButton"></copy-button>
<move-button v-show="showMoveButton"></move-button>
<delete-button v-show="showDeleteButton"></delete-button>
</div>
@@ -88,6 +90,7 @@ import UploadButton from './buttons/Upload'
import DownloadButton from './buttons/Download'
import SwitchButton from './buttons/SwitchView'
import MoveButton from './buttons/Move'
import CopyButton from './buttons/Copy'
import {mapGetters, mapState} from 'vuex'
import api from '@/utils/api'
import buttons from '@/utils/buttons'
@@ -100,6 +103,7 @@ export default {
DeleteButton,
RenameButton,
DownloadButton,
CopyButton,
UploadButton,
SwitchButton,
MoveButton

View File

@@ -82,7 +82,7 @@ export default {
name: 'listing',
components: { Item },
computed: {
...mapState(['req']),
...mapState(['req', 'selected']),
nameSorted () {
return (this.req.sort === 'name')
},
@@ -130,17 +130,69 @@ export default {
return
}
if (String.fromCharCode(event.which).toLowerCase() !== 'f') {
return
}
let key = String.fromCharCode(event.which).toLowerCase()
event.preventDefault()
this.$store.commit('showHover', 'search')
switch (key) {
case 'f':
event.preventDefault()
this.$store.commit('showHover', 'search')
break
case 'c':
case 'x':
this.copyCut(event, key)
break
case 'v':
this.paste(event)
break
}
},
preventDefault (event) {
// Wrapper around prevent default.
event.preventDefault()
},
copyCut (event, key) {
event.preventDefault()
let items = []
for (let i of this.selected) {
items.push({
from: this.req.items[i].url,
name: encodeURIComponent(this.req.items[i].name)
})
}
this.$store.commit('updateClipboard', {
key: key,
items: items
})
},
paste (event) {
event.preventDefault()
let items = []
for (let item of this.$store.state.clipboard.items) {
items.push({
from: item.from,
to: this.$route.path + item.name
})
}
if (this.$store.state.clipboard.key === 'x') {
api.move(items).then(() => {
this.$store.commit('setReload', true)
}).catch(error => {
this.$store.commit('showError', error)
})
return
}
api.copy(items).then(() => {
this.$store.commit('setReload', true)
}).catch(error => {
this.$store.commit('showError', error)
})
},
resizeEvent () {
// Update the columns size based on the window width.
let columns = Math.floor(document.querySelector('main').offsetWidth / 300)

View File

@@ -92,16 +92,16 @@ export default {
if (this.selectedCount === 0) return
let promises = []
let items = []
for (let i of this.selected) {
let url = this.req.items[i].url
let name = this.req.items[i].name
promises.push(api.move(url, this.url + encodeURIComponent(name)))
items.push({
from: this.req.items[i].url,
to: this.url + encodeURIComponent(this.req.items[i].name)
})
}
Promise.all(promises)
api.move(items)
.then(() => {
this.$store.commit('setReload', true)
})

View File

@@ -0,0 +1,17 @@
<template>
<button @click="show" aria-label="Copy" title="Copy" class="action" id="copy-button">
<i class="material-icons">content_copy</i>
<span>Copy file</span>
</button>
</template>
<script>
export default {
name: 'copy-button',
methods: {
show: function (event) {
this.$store.commit('showHover', 'copy')
}
}
}
</script>

View File

@@ -0,0 +1,58 @@
<template>
<div class="prompt">
<h3>Copy</h3>
<p>Choose the place to copy your files:</p>
<file-list @update:selected="val => dest = val"></file-list>
<div>
<button class="ok" @click="copy">Copy</button>
<button class="cancel" @click="$store.commit('closeHovers')">Cancel</button>
</div>
</div>
</template>
<script>
import { mapState } from 'vuex'
import FileList from './FileList'
import api from '@/utils/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: 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)
})
}
// Execute the promises.
api.copy(items)
.then(() => {
buttons.done('copy')
this.$router.push({ path: this.dest })
})
.catch(error => {
buttons.done('copy')
this.$store.commit('showError', error)
})
}
}
}
</script>

View File

@@ -0,0 +1,137 @@
<template>
<div>
<ul class="file-list">
<li @click="select"
@touchstart="touchstart"
@dblclick="next"
:aria-selected="selected == item.url"
:key="item.name" v-for="item in items"
:data-url="item.url">{{ item.name }}</li>
</ul>
<p>Currently navigating on: <code>{{ nav }}</code>.</p>
</div>
</template>
<script>
import { mapState } from 'vuex'
import url from '@/utils/url'
import api from '@/utils/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.
api.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
api.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

@@ -3,16 +3,7 @@
<h3>Move</h3>
<p>Choose new house for your file(s)/folder(s):</p>
<ul class="file-list">
<li @click="select"
@touchstart="touchstart"
@dblclick="next"
:aria-selected="moveTo == item.url"
:key="item.name" v-for="item in items"
:data-url="item.url">{{ item.name }}</li>
</ul>
<p>Currently navigating on: <code>{{ current }}</code>.</p>
<file-list @update:selected="val => dest = val"></file-list>
<div>
<button class="ok" @click="move">Move</button>
@@ -23,145 +14,46 @@
<script>
import { mapState } from 'vuex'
import url from '@/utils/url'
import FileList from './FileList'
import api from '@/utils/api'
import buttons from '@/utils/buttons'
export default {
name: 'move',
components: { FileList },
data: function () {
return {
items: [],
touches: {
id: '',
count: 0
},
current: window.location.pathname,
moveTo: null
dest: null
}
},
computed: mapState(['req', 'selected', 'baseURL']),
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.
api.fetch(url.removeLastDir(this.$rute.path))
.then(this.fillOptions)
.catch(this.showError)
},
computed: mapState(['req', 'selected']),
methods: {
move: function (event) {
event.preventDefault()
// Set the destination and create the promises array.
let promises = []
let dest = (this.moveTo === null) ? this.current : this.moveTo
buttons.loading('move')
let items = []
// Create a new promise for each file.
for (let item of this.selected) {
let from = this.req.items[item].url
let to = dest + '/' + encodeURIComponent(this.req.items[item].name)
to = to.replace('//', '/')
promises.push(api.move(from, to))
items.push({
from: this.req.items[item].url,
to: this.dest + encodeURIComponent(this.req.items[item].name)
})
}
// Execute the promises.
Promise.all(promises)
api.move(items)
.then(() => {
buttons.done('move')
this.$router.push({ path: dest })
this.$router.push({ path: this.dest })
})
.catch(error => {
buttons.done('move')
this.$store.commit('showError', error)
})
},
fillOptions (req) {
// Sets the current path and resets
// the current items.
this.current = req.url
this.items = []
// 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
})
}
},
showError (error) {
this.$store.commit('showError', error)
},
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
api.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.moveTo === event.currentTarget.dataset.url) {
this.moveTo = null
return
}
// Otherwise select the element.
this.moveTo = event.currentTarget.dataset.url
event.preventDefault()
}
}
}

View File

@@ -8,6 +8,7 @@
<delete v-else-if="showDelete"></delete>
<info v-else-if="showInfo"></info>
<move v-else-if="showMove"></move>
<copy v-else-if="showCopy"></copy>
<error v-else-if="showError"></error>
<success v-else-if="showSuccess"></success>
@@ -42,6 +43,7 @@ import Delete from './Delete'
import Rename from './Rename'
import Download from './Download'
import Move from './Move'
import Copy from './Copy'
import Error from './Error'
import Success from './Success'
import NewFile from './NewFile'
@@ -60,6 +62,7 @@ export default {
Download,
Success,
Move,
Copy,
NewFile,
NewDir,
Help
@@ -83,6 +86,7 @@ export default {
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' },

View File

@@ -53,7 +53,7 @@ export default {
this.name = encodeURIComponent(this.name)
newLink = url.removeLastDir(oldLink) + '/' + this.name
api.move(oldLink, newLink)
api.move([{ from: oldLink, to: newLink }])
.then(() => {
if (this.req.kind !== 'listing') {
this.$router.push({ path: newLink })