Add support for copying a file or folder

This commit is contained in:
Dane Everitt 2019-03-10 18:42:53 -07:00
parent 3970a24218
commit 5aa40800c8
No known key found for this signature in database
GPG key ID: EEA66103B3D71F53
8 changed files with 119 additions and 16 deletions

View file

@ -0,0 +1,19 @@
import {withCredentials} from "@/api/http";
import {ServerApplicationCredentials} from "@/store/types";
/**
* Creates a copy of the given file or directory on the Daemon. Expects a fully resolved path
* to be passed through for both data arguments.
*/
export function copyElement(server: string, credentials: ServerApplicationCredentials, data: {
currentPath: string, newPath: string
}): Promise<void> {
return new Promise((resolve, reject) => {
withCredentials(server, credentials).post('/v1/server/file/copy', {
from: data.currentPath,
to: data.newPath,
})
.then(() => resolve())
.catch(reject);
});
}

View file

@ -5,10 +5,10 @@ import {ServerApplicationCredentials} from "@/store/types";
* Deletes files and/or folders from the server. You should pass through an array of
* file or folder paths to be deleted.
*/
export function deleteElement(server: string, credentials: ServerApplicationCredentials, items: Array<string>): Promise<any> {
export function deleteElement(server: string, credentials: ServerApplicationCredentials, items: Array<string>): Promise<void> {
return new Promise((resolve, reject) => {
withCredentials(server, credentials).post('/v1/server/file/delete', { items })
.then(resolve)
.then(() => resolve())
.catch(reject);
})
}

View file

@ -0,0 +1,24 @@
<template>
<transition name="modal">
<div class="modal-mask" v-show="visible">
<div class="modal-container w-auto">
<div class="p-8 pb-0">
<div class="spinner spinner-thick spinner-relative blue spinner-xl"></div>
<p class="text-neutral-700 mt-8 text-sm">
<slot/>
</p>
</div>
</div>
</div>
</transition>
</template>
<script lang="ts">
import Vue from 'vue';
export default Vue.extend({
props: {
visible: { type: Boolean, default: false },
},
});
</script>

View file

@ -1,7 +1,7 @@
<template>
<div class="context-menu">
<div>
<div class="context-row" v-on:click="openRenameModal">
<div class="context-row" v-on:click="triggerAction('rename')">
<div class="icon">
<Icon name="edit-3"/>
</div>
@ -13,7 +13,7 @@
</div>
<div class="action"><span class="text-left">Move</span></div>
</div>
<div class="context-row">
<div class="context-row" v-on:click="triggerAction('copy')">
<div class="icon">
<Icon name="copy" class="h-4"/>
</div>
@ -41,7 +41,7 @@
</div>
</div>
<div>
<div class="context-row danger" v-on:click="openDeleteModal">
<div class="context-row danger" v-on:click="triggerAction('delete')">
<div class="icon">
<Icon name="delete" class="h-4"/>
</div>
@ -73,12 +73,8 @@
this.$emit('close');
},
openRenameModal: function () {
this.$emit('action:rename');
},
openDeleteModal: function () {
this.$emit('action:delete');
triggerAction: function (action: string) {
this.$emit(`action:${action}`);
}
}
});

View file

@ -33,10 +33,12 @@
v-on:close="contextMenuVisible = false"
v-on:action:delete="showModal('delete')"
v-on:action:rename="showModal('rename')"
v-on:action:copy="showModal('copy')"
ref="contextMenu"
/>
<CopyFileModal :file="file" v-if="modals.copy" v-on:close="$emit('list')"/>
<DeleteFileModal :visible.sync="modals.delete" :object="file" v-on:deleted="$emit('deleted')" v-on:close="modal.delete = false"/>
<RenameModal :visible.sync="modals.rename" :object="file" v-on:renamed="$emit('renamed')" v-on:close="modal.rename = false"/>
<RenameModal :visible.sync="modals.rename" :object="file" v-on:renamed="$emit('list')" v-on:close="modal.rename = false"/>
</div>
</template>
@ -49,6 +51,7 @@
import {DirectoryContentObject} from "@/api/server/types";
import DeleteFileModal from "@/components/server/components/filemanager/modals/DeleteFileModal.vue";
import RenameModal from "@/components/server/components/filemanager/modals/RenameModal.vue";
import CopyFileModal from "@/components/server/components/filemanager/modals/CopyFileModal.vue";
type DataStructure = {
currentDirectory: string,
@ -58,7 +61,7 @@
export default Vue.extend({
name: 'FileRow',
components: {DeleteFileModal, Icon, FileContextMenu, RenameModal},
components: {CopyFileModal, DeleteFileModal, Icon, FileContextMenu, RenameModal},
props: {
file: {
@ -79,6 +82,7 @@
modals: {
rename: false,
delete: false,
copy: false,
},
};
},
@ -102,7 +106,6 @@
methods: {
showModal: function (name: string) {
console.warn('showModal', name);
this.contextMenuVisible = false;
Object.keys(this.modals).forEach(k => {

View file

@ -0,0 +1,58 @@
<template>
<SpinnerModal :visible="true">
Copying {{ file.directory ? 'directory' : 'file' }}...
</SpinnerModal>
</template>
<script lang="ts">
import Vue from 'vue';
import SpinnerModal from "../../../../core/SpinnerModal.vue";
import {DirectoryContentObject} from '@/api/server/types';
import {mapState} from "vuex";
import {ServerState} from '@/store/types';
import { join } from 'path';
import {copyElement} from '@/api/server/files/copyElement';
import {AxiosError} from "axios";
export default Vue.extend({
components: { SpinnerModal },
computed: mapState('server', {
server: (state: ServerState) => state.server,
credentials: (state: ServerState) => state.credentials,
fm: (state: ServerState) => state.fm,
}),
props: {
file: { type: Object as () => DirectoryContentObject, required: true },
},
/**
* This modal works differently than the other modals that exist for the file manager.
* When it is mounted we will immediately show the spinner, and begin the copy operation
* on the give file or directory. Once that operation is complete we will emit the event
* and allow the parent to close the modal and do whatever else it thinks is needed.
*/
mounted: function () {
let newPath = join(this.fm.currentDirectory, `${this.file.name} copy`);
if (!this.file.directory) {
const extension = this.file.name.substring(this.file.name.lastIndexOf('.') + 1);
if (extension !== this.file.name && extension.length > 0) {
const name = this.file.name.substring(0, this.file.name.lastIndexOf('.'));
newPath = join(this.fm.currentDirectory, `${name} copy.${extension}`)
}
}
copyElement(this.server.uuid, this.credentials, {currentPath: join(this.fm.currentDirectory, this.file.name), newPath})
.then(() => this.$emit('close'))
.catch((error: AxiosError) => {
alert(`There was an error creating a copy of this item: ${error.message}`);
console.error('Error at Server::Files::Copy', {error});
})
.then(() => this.$emit('close'));
},
})
</script>

View file

@ -37,7 +37,7 @@
:file="file"
:editable="editableFiles"
v-on:deleted="fileRowDeleted(file, file.directory)"
v-on:renamed="listDirectory"
v-on:list="listDirectory"
/>
</div>
</div>
@ -57,7 +57,6 @@
<script lang="ts">
import Vue from 'vue';
import {mapState} from "vuex";
import { join } from 'path';
import {map} from 'lodash';
import getDirectoryContents from "@/api/server/getDirectoryContents";

View file

@ -17,4 +17,8 @@
width: 90%;
}
}
& > .modal-container.w-auto {
@apply .w-auto;
}
}