web/satellite/vuetify-poc: allow for sharing files and folders

This change allows linksharing URLs to be generated for files and
folders within the Vuetify project's file browser.

Resolves #6111

Change-Id: I8cbe81b33cb5e35de0c34bba8ccc9175c727bd94
This commit is contained in:
Jeremy Wharton 2023-09-12 20:02:29 -05:00 committed by Storj Robot
parent 8f1682941e
commit ec8f3b4528
5 changed files with 63 additions and 20 deletions

View File

@ -59,7 +59,7 @@
</v-list-item>
</template>
<v-list-item density="comfortable" link rounded="lg">
<v-list-item density="comfortable" link rounded="lg" @click="() => emit('shareClick')">
<template #prepend>
<icon-share bold />
</template>
@ -134,6 +134,7 @@ const props = defineProps<{
const emit = defineEmits<{
deleteFolderClick: [];
shareClick: [];
}>();
const isDownloading = ref<boolean>(false);

View File

@ -67,6 +67,7 @@
<browser-row-actions
:file="item.raw.browserObject"
@delete-folder-click="() => onDeleteFolderClick(item.raw.browserObject)"
@share-click="() => onShareClick(item.raw.browserObject)"
/>
</template>
</v-data-table-row>
@ -76,7 +77,18 @@
<file-preview-dialog v-model="previewDialog" />
</v-card>
<delete-folder-dialog v-if="folderToDelete" v-model="isDeleteFolderDialogShown" :folder="folderToDelete" />
<delete-folder-dialog
v-if="folderToDelete"
v-model="isDeleteFolderDialogShown"
:folder="folderToDelete"
@content-removed="folderToDelete = null"
/>
<share-dialog
v-model="isShareDialogShown"
:bucket-name="bucketName"
:file="fileToShare || undefined"
@content-removed="fileToShare = null"
/>
</template>
<script setup lang="ts">
@ -107,6 +119,7 @@ import { tableSizeOptions } from '@/types/common';
import BrowserRowActions from '@poc/components/BrowserRowActions.vue';
import FilePreviewDialog from '@poc/components/dialogs/FilePreviewDialog.vue';
import DeleteFolderDialog from '@poc/components/dialogs/DeleteFolderDialog.vue';
import ShareDialog from '@poc/components/dialogs/ShareDialog.vue';
import folderIcon from '@poc/assets/icon-folder-tonal.svg';
import pdfIcon from '@poc/assets/icon-pdf-tonal.svg';
@ -164,8 +177,10 @@ const search = ref<string>('');
const selected = ref([]);
const previewDialog = ref<boolean>(false);
const options = ref<TableOptions>();
const folderToDelete = ref<BrowserObject>();
const folderToDelete = ref<BrowserObject | null>(null);
const isDeleteFolderDialogShown = ref<boolean>(false);
const fileToShare = ref<BrowserObject | null>(null);
const isShareDialogShown = ref<boolean>(false);
const sortBy = [{ key: 'name', order: 'asc' }];
const headers = [
@ -405,6 +420,14 @@ function onDeleteFolderClick(folder: BrowserObject): void {
isDeleteFolderDialogShown.value = true;
}
/**
* Handles Share button click event.
*/
function onShareClick(file: BrowserObject): void {
fileToShare.value = file;
isShareDialogShown.value = true;
}
watch(filePath, fetchFiles, { immediate: true });
watch(() => props.forceEmpty, v => !v && fetchFiles());
</script>

View File

@ -8,7 +8,7 @@
transition="fade-transition"
:persistent="isLoading"
>
<v-card rounded="xlg">
<v-card rounded="xlg" ref="innerContent">
<v-card-item class="pl-7 py-4">
<template #prepend>
<v-sheet
@ -62,7 +62,7 @@
</template>
<script setup lang="ts">
import { computed } from 'vue';
import { computed, ref, Component, watch } from 'vue';
import {
VDialog,
VCard,
@ -91,6 +91,7 @@ const props = defineProps<{
const emit = defineEmits<{
'update:modelValue': [value: boolean],
'contentRemoved': [],
}>();
const model = computed<boolean>({
@ -104,6 +105,8 @@ const bucketsStore = useBucketsStore();
const { isLoading, withLoading } = useLoading();
const notify = useNotify();
const innerContent = ref<Component | null>(null);
const filePath = computed<string>(() => bucketsStore.state.fileComponentPath);
async function onDeleteClick(): Promise<void> {
@ -120,4 +123,6 @@ async function onDeleteClick(): Promise<void> {
model.value = false;
});
}
watch(innerContent, comp => !comp && emit('contentRemoved'));
</script>

View File

@ -42,7 +42,7 @@
Download
</v-tooltip>
</v-btn>
<v-btn icon size="small" color="white">
<v-btn icon size="small" color="white" @click="isShareDialogShown = true">
<icon-share size="22" />
<v-tooltip
activator="parent"
@ -90,6 +90,8 @@
</v-carousel>
</v-card>
</v-dialog>
<share-dialog v-model="isShareDialogShown" :bucket-name="bucketName" :file="currentFile" />
</template>
<script setup lang="ts">
@ -105,9 +107,7 @@ import {
VToolbarTitle,
VTooltip,
} from 'vuetify/components';
import { useRoute } from 'vue-router';
import { useAppStore } from '@/store/modules/appStore';
import { BrowserObject, useObjectBrowserStore } from '@/store/modules/objectBrowserStore';
import { useBucketsStore } from '@/store/modules/bucketsStore';
import { useNotify } from '@/utils/hooks';
@ -115,15 +115,14 @@ import { AnalyticsErrorEventSource } from '@/utils/constants/analyticsEventNames
import IconShare from '@poc/components/icons/IconShare.vue';
import FilePreviewItem from '@poc/components/dialogs/filePreviewComponents/FilePreviewItem.vue';
import ShareDialog from '@poc/components/dialogs/ShareDialog.vue';
const appStore = useAppStore();
const obStore = useObjectBrowserStore();
const bucketsStore = useBucketsStore();
const notify = useNotify();
const route = useRoute();
const isDownloading = ref<boolean>(false);
const isShareDialogShown = ref<boolean>(false);
const folderType = 'folder';
@ -182,6 +181,13 @@ const currentPath = computed((): string => {
return obStore.state.path;
});
/**
* Returns the name of the current bucket from the store.
*/
const bucketName = computed((): string => {
return bucketsStore.state.fileComponentBucketName;
});
/**
* Download the current opened file.
*/

View File

@ -21,7 +21,7 @@
</v-sheet>
</template>
<v-card-title class="font-weight-bold">
Share {{ !filePath ? 'Bucket' : isFolder ? 'Folder' : 'File' }}
Share {{ !file ? 'Bucket' : file.type == 'folder' ? 'Folder' : 'File' }}
</v-card-title>
<template #append>
<v-btn
@ -118,18 +118,20 @@ import { useAnalyticsStore } from '@/store/modules/analyticsStore';
import { useNotify } from '@/utils/hooks';
import { useLinksharing } from '@/composables/useLinksharing';
import { SHARE_BUTTON_CONFIGS, ShareOptions } from '@/types/browser';
import { BrowserObject } from '@/store/modules/objectBrowserStore';
import { useBucketsStore } from '@/store/modules/bucketsStore';
import IconShare from '@poc/components/icons/IconShare.vue';
const props = defineProps<{
modelValue: boolean,
bucketName: string;
filePath?: string;
isFolder?: boolean;
bucketName: string,
file?: BrowserObject,
}>();
const emit = defineEmits<{
'update:modelValue': [value: boolean],
'update:modelValue': [value: boolean];
'contentRemoved': [];
}>();
const model = computed<boolean>({
@ -138,6 +140,7 @@ const model = computed<boolean>({
});
const analyticsStore = useAnalyticsStore();
const bucketsStore = useBucketsStore();
const notify = useNotify();
const { generateBucketShareURL, generateFileOrFolderShareURL } = useLinksharing();
@ -149,6 +152,8 @@ const link = ref<string>('');
const copiedTimeout = ref<ReturnType<typeof setTimeout> | null>(null);
const justCopied = computed<boolean>(() => copiedTimeout.value !== null);
const filePath = computed<string>(() => bucketsStore.state.fileComponentPath);
/**
* Saves link to clipboard.
*/
@ -166,20 +171,23 @@ function onCopy(): void {
* Generates linksharing URL when the dialog is opened.
*/
watch(() => innerContent.value, async (comp: Component | null): Promise<void> => {
if (!comp) return;
if (!comp) {
emit('contentRemoved');
return;
}
isLoading.value = true;
link.value = '';
analyticsStore.eventTriggered(AnalyticsEvent.LINK_SHARED);
try {
if (!props.filePath) {
if (!props.file) {
link.value = await generateBucketShareURL(props.bucketName);
} else {
link.value = await generateFileOrFolderShareURL(
props.bucketName,
props.filePath,
props.isFolder,
`${filePath.value ? filePath.value + '/' : ''}${props.file.Key}`,
props.file.type === 'folder',
);
}
} catch (error) {