mirror of
https://github.com/ApfelTeeSaft/R2-Explorer.git
synced 2026-08-26 19:33:39 +00:00
Improve context menu
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<q-btn dense flat round icon="menu" @click="$emit('toggleLeftDrawer')" />
|
||||
<q-btn dense flat round icon="menu" @click="$emit('toggle')" />
|
||||
|
||||
<q-toolbar-title class="text-bold">
|
||||
<q-avatar>
|
||||
@@ -19,6 +19,7 @@ import BucketPicker from "components/main/BucketPicker.vue";
|
||||
|
||||
export default defineComponent({
|
||||
name: "TopBar",
|
||||
emits: ['toggle'],
|
||||
components: { BucketPicker }
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<q-header reveal class="bg-green text-white">
|
||||
<q-toolbar>
|
||||
|
||||
<top-bar @toggleLeftDrawer="toggleLeftDrawer"/>
|
||||
<top-bar @toggle="toggleLeftDrawer"/>
|
||||
|
||||
</q-toolbar>
|
||||
</q-header>
|
||||
|
||||
@@ -2,11 +2,20 @@
|
||||
<template v-if="this.file">
|
||||
<div>
|
||||
<q-card>
|
||||
<q-card-section class="bg-grey-3 text-black" vertical>
|
||||
<q-btn-group push>
|
||||
<q-btn push icon="arrow_back" :to="{ name: `email-folder`, params: { bucket: $route.params.bucket, folder: $route.params.folder }}" />
|
||||
<q-btn push icon="chevron_left" />
|
||||
<q-btn push icon="chevron_right" />
|
||||
<q-card-section class="bg-grey-2 text-black" vertical>
|
||||
<q-btn-group unelevated>
|
||||
<q-btn push icon="arrow_back" :to="{ name: `email-folder`, params: { bucket: $route.params.bucket, folder: $route.params.folder }}">
|
||||
<q-tooltip>Back</q-tooltip>
|
||||
</q-btn>
|
||||
<q-btn push icon="chevron_left">
|
||||
<q-tooltip>More recent</q-tooltip>
|
||||
</q-btn>
|
||||
<q-btn push icon="chevron_right">
|
||||
<q-tooltip>More older</q-tooltip>
|
||||
</q-btn>
|
||||
<q-btn push icon="mark_email_unread">
|
||||
<q-tooltip>Mark email as unread</q-tooltip>
|
||||
</q-btn>
|
||||
</q-btn-group>
|
||||
</q-card-section>
|
||||
|
||||
@@ -19,7 +28,7 @@
|
||||
<q-icon name="account_circle" size="xl" class="q-mr-sm"/>
|
||||
<div class="d-flex column">
|
||||
<span>{{ file.from.name }} <small class="text-muted"><{{ file.from.address }}></small></span>
|
||||
<span>{{ file.to[0].address }}</span>
|
||||
<span>to {{ file.to[0].address }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -40,8 +49,10 @@
|
||||
</div>
|
||||
</q-card-actions>
|
||||
|
||||
|
||||
<q-card-actions vertical v-if="file.attachments.length > 0">
|
||||
<h6 class="q-mb-3">Attachments</h6>
|
||||
<q-separator/>
|
||||
<h6 class="q-my-md">Attachments</h6>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-xl-4 col-lg-6 col-md-6" v-for="attachment of file.attachments" :key="attachment.filename">
|
||||
|
||||
@@ -12,6 +12,23 @@
|
||||
:flat="true"
|
||||
@row-click="rowClick">
|
||||
|
||||
<template v-slot:loading>
|
||||
<div class="full-width q-my-lg">
|
||||
<h6 class="flex items-center justify-center">
|
||||
<q-spinner
|
||||
color="primary"
|
||||
size="xl"
|
||||
/>
|
||||
</h6>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template v-slot:no-data>
|
||||
<div class="full-width q-my-lg" v-if="!loading">
|
||||
<h6 class="flex items-center justify-center"><q-icon name="alternate_email" color="orange" size="lg" />This bucket doesn't have Emails</h6>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template v-slot:body-cell-has_attachments="prop">
|
||||
<td>
|
||||
<q-icon v-if="prop.row.has_attachments" name="attachment" size="sm" color="black" />
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
<template>
|
||||
<q-list style="min-width: 100px">
|
||||
<q-item clickable v-close-popup @click="openObject">
|
||||
<q-item-section>Open</q-item-section>
|
||||
</q-item>
|
||||
<q-item clickable v-close-popup @click="downloadObject" v-if="prop.row.type === 'file'">
|
||||
<q-item-section>Download</q-item-section>
|
||||
</q-item>
|
||||
<q-item clickable v-close-popup @click="shareObject">
|
||||
<q-item-section>Get sharable link</q-item-section>
|
||||
</q-item>
|
||||
<q-item clickable v-close-popup @click="deleteObject">
|
||||
<q-item-section>Delete</q-item-section>
|
||||
</q-item>
|
||||
</q-list>
|
||||
</template>
|
||||
<script>
|
||||
import { decode, encode, ROOT_FOLDER } from "src/appUtils";
|
||||
import { useMainStore } from "stores/main-store";
|
||||
import { useQuasar } from "quasar";
|
||||
|
||||
export default {
|
||||
name: "FileContextMenu",
|
||||
props: {
|
||||
prop: {},
|
||||
},
|
||||
computed: {
|
||||
selectedBucket: function() {
|
||||
return this.$route.params.bucket
|
||||
},
|
||||
selectedFolder: function() {
|
||||
if (this.$route.params.folder && this.$route.params.folder !== ROOT_FOLDER) {
|
||||
return decode(this.$route.params.folder)
|
||||
}
|
||||
return ''
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
openObject: function() {
|
||||
this.$emit('openObject', this.prop.row)
|
||||
},
|
||||
deleteObject: function() {
|
||||
this.$emit('deleteObject', this.prop.row)
|
||||
},
|
||||
shareObject: async function() {
|
||||
let url
|
||||
if (this.prop.row.type === 'folder') {
|
||||
url = window.location.origin + this.$router.resolve({
|
||||
name: 'files-folder',
|
||||
params: {
|
||||
bucket: this.selectedBucket,
|
||||
folder: encode(this.prop.row.key)
|
||||
}
|
||||
}).href
|
||||
} else {
|
||||
url = window.location.origin + this.$router.resolve({
|
||||
name: 'files-file',
|
||||
params: {
|
||||
bucket: this.selectedBucket,
|
||||
folder: encode(this.selectedFolder || ROOT_FOLDER),
|
||||
file: this.prop.row.nameHash
|
||||
}
|
||||
}).href
|
||||
}
|
||||
|
||||
try {
|
||||
await navigator.clipboard.writeText(url);
|
||||
this.q.notify({
|
||||
message: 'Link to file copied to clipboard!',
|
||||
timeout: 5000,
|
||||
type: 'positive',
|
||||
})
|
||||
} catch (err) {
|
||||
this.q.notify({
|
||||
message: 'Failed to copy: ' + err,
|
||||
timeout: 5000,
|
||||
type: 'negative',
|
||||
})
|
||||
}
|
||||
},
|
||||
downloadObject: function() {
|
||||
const link = document.createElement('a')
|
||||
link.download = this.prop.row.name
|
||||
|
||||
link.href = `${this.mainStore.serverUrl}/api/buckets/${this.selectedBucket}/${encode(this.prop.row.key)}`
|
||||
|
||||
document.body.appendChild(link)
|
||||
link.click()
|
||||
document.body.removeChild(link)
|
||||
}
|
||||
},
|
||||
setup () {
|
||||
return {
|
||||
mainStore: useMainStore(),
|
||||
q: useQuasar()
|
||||
}
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@@ -19,9 +19,25 @@
|
||||
:flat="true"
|
||||
@row-dblclick="openRowClick"
|
||||
@row-click="openRowDlbClick"
|
||||
@row-contextmenu="openRowMenu"
|
||||
>
|
||||
|
||||
<template v-slot:loading>
|
||||
<div class="full-width q-my-lg">
|
||||
<h6 class="flex items-center justify-center">
|
||||
<q-spinner
|
||||
color="primary"
|
||||
size="xl"
|
||||
/>
|
||||
</h6>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template v-slot:no-data>
|
||||
<div class="full-width q-my-lg" v-if="!loading">
|
||||
<h6 class="flex items-center justify-center"><q-icon name="folder" color="orange" size="lg" />This folder is empty</h6>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template v-slot:body-cell-name="prop">
|
||||
<td class="flex" style="align-items: center">
|
||||
<q-icon :name="prop.row.icon" size="sm" :color="prop.row.color" class="q-mr-xs" />
|
||||
@@ -29,25 +45,23 @@
|
||||
</td>
|
||||
</template>
|
||||
|
||||
<template v-slot:body-cell="prop">
|
||||
<q-td :props="prop">
|
||||
{{prop.value}}
|
||||
</q-td>
|
||||
<q-menu
|
||||
touch-position
|
||||
context-menu
|
||||
>
|
||||
<FileContextMenu :prop="prop" @openObject="openObject" @deleteObject="$refs.options.deleteObject" />
|
||||
</q-menu>
|
||||
</template>
|
||||
|
||||
<template v-slot:body-cell-options="prop">
|
||||
<td class="text-right">
|
||||
<q-btn round flat icon="more_vert" size="sm">
|
||||
<q-menu :ref="(el) => setItemRef(el, prop.rowIndex)">
|
||||
<q-list style="min-width: 100px">
|
||||
<q-item clickable v-close-popup @click="openObject(prop.row)">
|
||||
<q-item-section>Open</q-item-section>
|
||||
</q-item>
|
||||
<q-item clickable v-close-popup @click="downloadObject(prop.row)" v-if="prop.row.type === 'file'">
|
||||
<q-item-section>Download</q-item-section>
|
||||
</q-item>
|
||||
<q-item clickable v-close-popup @click="shareObject(prop.row)">
|
||||
<q-item-section>Get sharable link</q-item-section>
|
||||
</q-item>
|
||||
<q-item clickable v-close-popup @click="$refs.options.deleteObject(prop.row)">
|
||||
<q-item-section>Delete</q-item-section>
|
||||
</q-item>
|
||||
<q-separator />
|
||||
</q-list>
|
||||
<q-menu>
|
||||
<FileContextMenu :prop="prop" />
|
||||
</q-menu>
|
||||
</q-btn>
|
||||
</td>
|
||||
@@ -65,20 +79,19 @@
|
||||
|
||||
<script>
|
||||
import { defineComponent } from "vue";
|
||||
import { api } from "boot/axios";
|
||||
import { useMainStore } from "stores/main-store";
|
||||
import { apiHandler, bytesToSize, decode, encode, ROOT_FOLDER, timeSince } from "../../appUtils";
|
||||
import { apiHandler, decode, encode, ROOT_FOLDER } from "../../appUtils";
|
||||
import FilePreview from "components/preview/FilePreview.vue";
|
||||
import DragAndDrop from "components/utils/DragAndDrop.vue";
|
||||
import FileOptions from "components/files/FileOptions.vue";
|
||||
import { useQuasar } from "quasar";
|
||||
import FileContextMenu from "pages/files/FileContextMenu.vue";
|
||||
|
||||
export default defineComponent({
|
||||
name: 'FilesIndexPage',
|
||||
components: { FileOptions, DragAndDrop, FilePreview },
|
||||
components: { FileContextMenu, FileOptions, DragAndDrop, FilePreview },
|
||||
data: function () {
|
||||
return {
|
||||
rowMenu: [],
|
||||
loading: false,
|
||||
rows: [],
|
||||
columns: [
|
||||
@@ -178,11 +191,6 @@ export default defineComponent({
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
setItemRef(el, index) {
|
||||
if (el) {
|
||||
this.rowMenu[index] = el
|
||||
}
|
||||
},
|
||||
openRowClick: function(evt, row, index) {
|
||||
evt.preventDefault()
|
||||
this.openObject(row)
|
||||
@@ -191,10 +199,6 @@ export default defineComponent({
|
||||
evt.preventDefault()
|
||||
this.$bus.emit("openFileDetails", row);
|
||||
},
|
||||
openRowMenu: function(evt, row, index) {
|
||||
evt.preventDefault()
|
||||
this.rowMenu[index].show()
|
||||
},
|
||||
breadcrumbsClick: function(obj) {
|
||||
this.$router.push({ name: `files-folder`, params: { bucket: this.selectedBucket, folder: encode(obj.path) }})
|
||||
},
|
||||
@@ -214,52 +218,6 @@ export default defineComponent({
|
||||
this.$refs.preview.openFile(row)
|
||||
}
|
||||
},
|
||||
shareObject: async function(row) {
|
||||
let url
|
||||
if (row.type === 'folder') {
|
||||
url = window.location.origin + this.$router.resolve({
|
||||
name: 'files-folder',
|
||||
params: {
|
||||
bucket: this.selectedBucket,
|
||||
folder: encode(row.key)
|
||||
}
|
||||
}).href
|
||||
} else {
|
||||
url = window.location.origin + this.$router.resolve({
|
||||
name: 'files-file',
|
||||
params: {
|
||||
bucket: this.selectedBucket,
|
||||
folder: encode(this.selectedFolder || ROOT_FOLDER),
|
||||
file: row.nameHash
|
||||
}
|
||||
}).href
|
||||
}
|
||||
|
||||
try {
|
||||
await navigator.clipboard.writeText(url);
|
||||
this.q.notify({
|
||||
message: 'Link to file copied to clipboard!',
|
||||
timeout: 5000,
|
||||
type: 'positive',
|
||||
})
|
||||
} catch (err) {
|
||||
this.q.notify({
|
||||
message: 'Failed to copy: ' + err,
|
||||
timeout: 5000,
|
||||
type: 'negative',
|
||||
})
|
||||
}
|
||||
},
|
||||
downloadObject: function(row) {
|
||||
const link = document.createElement('a')
|
||||
link.download = row.name
|
||||
|
||||
link.href = `${this.mainStore.serverUrl}/api/buckets/${this.selectedBucket}/${encode(row.key)}`
|
||||
|
||||
document.body.appendChild(link)
|
||||
link.click()
|
||||
document.body.removeChild(link)
|
||||
},
|
||||
fetchFiles: async function () {
|
||||
this.loading = true
|
||||
|
||||
|
||||
Reference in New Issue
Block a user