Improve context menu

This commit is contained in:
Gabriel Massadas
2024-02-06 20:46:47 +00:00
parent 6c31236307
commit f9648ddfc6
6 changed files with 171 additions and 85 deletions
@@ -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">&lt;{{ 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