Skip to content

Commit

Permalink
web/satellite: list object versions
Browse files Browse the repository at this point in the history
This change implements UI functionality to view a list of an object's versions.

Issue: #6833

Change-Id: I88c98b3c070e50e9424720550cc6681e9219baa6
  • Loading branch information
wilfred-asomanii authored and Storj Robot committed Apr 16, 2024
1 parent a01e62e commit 4d20183
Show file tree
Hide file tree
Showing 7 changed files with 213 additions and 27 deletions.
4 changes: 2 additions & 2 deletions web/satellite/src/components/BrowserSnackbarComponent.vue
Expand Up @@ -3,9 +3,9 @@

<template>
<v-snackbar
v-model="isObjectsUploadModal"
:model-value="isObjectsUploadModal"
vertical
:timeout="4000"
:timeout="-1"
color="default"
elevation="24"
rounded="lg"
Expand Down
109 changes: 91 additions & 18 deletions web/satellite/src/components/BrowserTableComponent.vue
Expand Up @@ -21,6 +21,7 @@
<v-data-table-server
v-model="selectedFiles"
v-model:options="options"
v-model:expanded="expandedKeys"
:sort-by="sortBy"
:headers="headers"
:items="tableFiles"
Expand All @@ -34,16 +35,50 @@
:loading="isFetching || loading"
:items-length="isPaginationEnabled ? totalObjectCount : allFiles.length"
:items-per-page-options="isPaginationEnabled ? tableSizeOptions(totalObjectCount, true) : undefined"
:show-expand="showObjectVersions"
@update:page="onPageChange"
@update:itemsPerPage="onLimitChange"
>
<template #expanded-row="{ internalItem: {key} }">
<tr v-for="file in versionsCache.get(key) as BrowserObject[]" :key="file.VersionId" class="bg-altbg">
<td />
<td>
<v-list-item class="rounded-lg text-caption pl-1 ml-n1" link>
<template #prepend>
<icon-curve-right />
<icon-versioning-clock class="ml-4 mr-3" size="32" dotted />
</template>
{{ file.Key }}
</v-list-item>
</td>
<td>
<p class="text-caption">
{{ getFormattedSize(file) }}
</p>
</td>
<td>
<p class="text-caption">
{{ getFileInfo(file).typeInfo.title }}
</p>
</td>
<td>
<p class="text-caption">
{{ getFormattedDate(file) }}
</p>
</td>
<td />
<td />
</tr>
</template>

<template #no-data>
<p class="text-body-2 cursor-pointer py-14 rounded-xlg my-4" @click="emit('uploadClick')">
{{ search ? 'No data found' : 'Drag and drop files or folders here, or click to upload files.' }}
</p>
</template>
<template #item="{ props: rowProps }">
<v-data-table-row v-bind="rowProps">
<template v-if="rowProps.item.raw.browserObject.type === 'folder'" #item.data-table-expand />
<template #item.name="{ item }: ItemSlotProps">
<v-btn
class="rounded-lg w-100 px-1 ml-n1 justify-start font-weight-bold"
Expand Down Expand Up @@ -146,18 +181,19 @@
</template>

<script setup lang="ts">
import { ref, computed, watch, WritableComputedRef } from 'vue';
import { computed, ref, watch, WritableComputedRef } from 'vue';
import { useRouter } from 'vue-router';
import {
VBtn,
VCard,
VCol,
VDataTableRow,
VDataTableServer,
VListItem,
VRow,
VSnackbar,
VTextField,
VBtn,
VTooltip,
VDataTableServer,
VDataTableRow,
VSnackbar,
} from 'vuetify/components';
import { mdiMagnify } from '@mdi/js';
Expand All @@ -169,7 +205,6 @@ import {
} from '@/store/modules/objectBrowserStore';
import { useProjectsStore } from '@/store/modules/projectsStore';
import { useNotify } from '@/utils/hooks';
import { SHORT_MONTHS_NAMES } from '@/utils/constants/date';
import { Size } from '@/utils/bytesSize';
import { AnalyticsErrorEventSource, AnalyticsEvent } from '@/utils/constants/analyticsEventNames';
import { useBucketsStore } from '@/store/modules/bucketsStore';
Expand All @@ -180,12 +215,16 @@ import { useAnalyticsStore } from '@/store/modules/analyticsStore';
import { useUsersStore } from '@/store/modules/usersStore';
import { ROUTES } from '@/router';
import { Time } from '@/utils/time';
import { Versioning } from '@/types/versioning';
import { BucketMetadata } from '@/types/buckets';
import BrowserRowActions from '@/components/BrowserRowActions.vue';
import FilePreviewDialog from '@/components/dialogs/FilePreviewDialog.vue';
import DeleteFileDialog from '@/components/dialogs/DeleteFileDialog.vue';
import ShareDialog from '@/components/dialogs/ShareDialog.vue';
import IconTrash from '@/components/icons/IconTrash.vue';
import IconCurveRight from '@/components/icons/IconCurveRight.vue';
import IconVersioningClock from '@/components/icons/IconVersioningClock.vue';
type SortKey = 'name' | 'type' | 'size' | 'date';
Expand All @@ -203,6 +242,7 @@ type ItemSlotProps = { item: BrowserObjectWrapper };
const props = defineProps<{
forceEmpty?: boolean;
loading?: boolean;
bucket: BucketMetadata;
}>();
const emit = defineEmits<{
Expand Down Expand Up @@ -255,10 +295,24 @@ const bucketName = computed<string>(() => bucketsStore.state.fileComponentBucket
const filePath = computed<string>(() => bucketsStore.state.fileComponentPath);
/**
* Returns total object count from store.
* Whether versioning has been enabled for current project and versions should be shown.
*/
const showObjectVersions = computed(() => {
if (!projectsStore.versioningUIEnabled) {
return false;
}
return obStore.state.showObjectVersions && props.bucket && props.bucket?.versioning !== Versioning.NotSupported;
});
const isPaginationEnabled = computed<boolean>(() => config.state.config.objectBrowserPaginationEnabled);
const versionsCache = computed<Map<string, BrowserObject[]>>(() => obStore.state.objectVersions);
const expandedKeys = computed<string[]>({
get: () => obStore.state.versionsExpandedKeys,
set: (keys: string[]) => obStore.updateVersionsExpandedKeys(keys),
});
/**
* Returns total object count from store.
*/
Expand All @@ -277,13 +331,12 @@ const allFiles = computed<BrowserObjectWrapper[]>(() => {
const objects = isPaginationEnabled.value ? obStore.displayedObjects : obStore.state.files;
return objects.map<BrowserObjectWrapper>(file => {
const lowerName = file.Key.toLowerCase();
const dotIdx = lowerName.lastIndexOf('.');
const ext = dotIdx === -1 ? '' : file.Key.slice(dotIdx + 1);
const { name, ext, typeInfo } = getFileInfo(file);
return {
browserObject: file,
typeInfo: getFileTypeInfo(ext, file.type),
lowerName,
typeInfo,
lowerName : name,
ext,
};
});
Expand Down Expand Up @@ -432,15 +485,17 @@ function getFormattedSize(file: BrowserObject): string {
/**
* Returns the title and icon representing a file's type.
*/
function getFileTypeInfo(ext: string, type: BrowserObject['type']): BrowserObjectTypeInfo {
if (!type) return FILE_INFO;
if (type === 'folder') return FOLDER_INFO;
function getFileInfo(file: BrowserObject): { name: string; ext: string; typeInfo: BrowserObjectTypeInfo } {
const name = file.Key.toLowerCase();
if (!file.type) return { name, ext: '', typeInfo: FILE_INFO };
if (file.type === 'folder') return { name, ext: '', typeInfo: FOLDER_INFO };
ext = ext.toLowerCase();
const dotIdx = name.lastIndexOf('.');
const ext = dotIdx === -1 ? '' : file.Key.slice(dotIdx + 1).toLowerCase();
for (const [exts, info] of EXTENSION_INFOS.entries()) {
if (exts.indexOf(ext) !== -1) return info;
if (exts.indexOf(ext) !== -1) return { name, ext, typeInfo: info };
}
return FILE_INFO;
return { name, ext, typeInfo: FILE_INFO };
}
/**
Expand Down Expand Up @@ -530,6 +585,24 @@ async function dismissFileGuide() {
watch(filePath, fetchFiles, { immediate: true });
watch(() => props.forceEmpty, v => !v && fetchFiles());
// watch which table rows are expanded and fetch their versions.
watch(expandedKeys, (keys, oldKeys) => {
const newKeys = keys.filter(key => {
return !oldKeys?.some(oldKey => {
return oldKey === key;
});
});
newKeys.forEach(key => {
obStore.listVersions(key);
});
});
watch(() => obStore.state.showObjectVersions, showObjectVersions => {
if (!showObjectVersions) {
obStore.updateVersionsExpandedKeys([]);
}
});
if (!userStore.noticeDismissal.fileGuide) {
const unwatch = watch(firstFile, () => {
isFileGuideShown.value = true;
Expand Down
12 changes: 12 additions & 0 deletions web/satellite/src/components/icons/IconCurveRight.vue
@@ -0,0 +1,12 @@
// Copyright (C) 2024 Storj Labs, Inc.
// See LICENSE for copying information.

<template>
<svg
xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-corner-down-right"
>
<polyline points="15 10 20 15 15 20" />
<path d="M4 4v7a4 4 0 0 0 4 4h12" />
</svg>
</template>
19 changes: 19 additions & 0 deletions web/satellite/src/components/icons/IconFingerPrint.vue
@@ -0,0 +1,19 @@
// Copyright (C) 2024 Storj Labs, Inc.
// See LICENSE for copying information.

<template>
<svg :width="size" :height="size" viewBox="0 0 19 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
d="M9.02199 9.30523C9.46652 9.30523 9.82893 9.6568 9.84634 10.097L9.84699 10.1302V17.175C9.84699 17.6306 9.47763 18 9.02199 18C8.57747 18 8.21506 17.6484 8.19765 17.2082L8.19699 17.175V10.1302C8.19699 9.67459 8.56636 9.30523 9.02199 9.30523ZM9.02199 6.13985C11.1645 6.13985 12.9054 7.85932 12.94 9.99357L12.9405 10.0584V15.008C12.9405 15.4636 12.5711 15.833 12.1155 15.833C11.671 15.833 11.3086 15.4814 11.2912 15.0412L11.2905 15.008V10.0584C11.2905 8.8055 10.2749 7.78985 9.02199 7.78985C7.78499 7.78985 6.77923 8.77995 6.75396 10.0109L6.75348 10.0584V15.008C6.75348 15.4636 6.38411 15.833 5.92848 15.833C5.48395 15.833 5.12155 15.4814 5.10413 15.0412L5.10348 15.008V10.0584C5.10348 7.89423 6.85786 6.13985 9.02199 6.13985ZM9.02199 3.04986C12.7605 3.04986 15.7983 6.05023 15.8587 9.77437L15.8596 9.88744V13.8958C15.8596 14.3515 15.4902 14.7208 15.0346 14.7208C14.5901 14.7208 14.2276 14.3693 14.2102 13.929L14.2096 13.8958V9.88744C14.2096 7.02242 11.887 4.69986 9.02199 4.69986C6.18562 4.69986 3.88092 6.9762 3.83511 9.80165L3.83441 9.88744V13.8958C3.83441 14.3515 3.46505 14.7208 3.00941 14.7208C2.56489 14.7208 2.20248 14.3693 2.18507 13.929L2.18441 13.8958V9.88744C2.18441 6.11115 5.2457 3.04986 9.02199 3.04986ZM9.02199 0C12.9984 0 16.4375 2.19523 17.9656 5.72562C18.1466 6.14377 17.9543 6.62946 17.5362 6.81045C17.118 6.99144 16.6323 6.79919 16.4513 6.38104C15.188 3.46232 12.3488 1.65 9.02199 1.65C5.69608 1.65 2.85526 3.46171 1.58145 6.38308C1.39933 6.80073 0.913122 6.99168 0.495464 6.80957C0.0778064 6.62745 -0.11314 6.14124 0.0689727 5.72359C1.60829 2.19332 5.04749 0 9.02199 0Z"
fill="currentColor"
/>
</svg>
</template>

<script setup lang="ts">
withDefaults(defineProps<{
size?: number | string;
}>(), {
size: 19,
});
</script>
10 changes: 9 additions & 1 deletion web/satellite/src/components/icons/IconVersioningClock.vue
Expand Up @@ -3,12 +3,18 @@

<template>
<svg :width="size" :height="size" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_5125_630)">
<g v-if="!dotted" clip-path="url(#clip0_5125_630)">
<path d="M18 0.375H6C2.8934 0.375 0.375 2.8934 0.375 6V18C0.375 21.1066 2.8934 23.625 6 23.625H18C21.1066 23.625 23.625 21.1066 23.625 18V6C23.625 2.8934 21.1066 0.375 18 0.375Z" fill="white" stroke="black" stroke-opacity="0.2" stroke-width="0.75" />
<path d="M6 12C6 13.1867 6.35189 14.3467 7.01118 15.3334C7.67047 16.3201 8.60754 17.0892 9.7039 17.5433C10.8003 17.9974 12.0067 18.1162 13.1705 17.8847C14.3344 17.6532 15.4035 17.0818 16.2426 16.2426C17.0818 15.4035 17.6532 14.3344 17.8847 13.1705C18.1162 12.0067 17.9974 10.8003 17.5433 9.7039C17.0892 8.60754 16.3201 7.67047 15.3334 7.01118C14.3467 6.35189 13.1867 6 12 6C10.3226 6.00631 8.71265 6.66082 7.50667 7.82667L6 9.33333" stroke="black" stroke-linecap="round" stroke-linejoin="round" />
<path d="M6 6.85718V9.33336H8.57143" stroke="black" stroke-linecap="round" stroke-linejoin="round" />
<path d="M12 8.57141V12L15.4286 11.9999" stroke="black" stroke-linecap="round" stroke-linejoin="round" />
</g>
<g v-else clip-path="url(#clip0_5112_641)">
<path d="M18 0.375H6C2.8934 0.375 0.375 2.8934 0.375 6V18C0.375 21.1066 2.8934 23.625 6 23.625H18C21.1066 23.625 23.625 21.1066 23.625 18V6C23.625 2.8934 21.1066 0.375 18 0.375Z" stroke="currentColor" stroke-opacity="0.2" stroke-width="0.75" stroke-dasharray="1.5 1.5" />
<path d="M6.75 12C6.75 13.0384 7.05791 14.0534 7.63478 14.9167C8.21166 15.7801 9.0316 16.453 9.99091 16.8504C10.9502 17.2477 12.0058 17.3517 13.0242 17.1491C14.0426 16.9466 14.9781 16.4465 15.7123 15.7123C16.4465 14.9781 16.9466 14.0426 17.1491 13.0242C17.3517 12.0058 17.2477 10.9502 16.8504 9.99091C16.453 9.0316 15.7801 8.21166 14.9167 7.63478C14.0534 7.05791 13.0384 6.75 12 6.75C10.5323 6.75552 9.12357 7.32821 8.06833 8.34833L6.75 9.66667" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" />
<path d="M6.75 7.5V9.66666H9" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" />
<path d="M12 9V12L15 12" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" />
</g>
<defs>
<clipPath id="clip0_5125_630">
<rect width="24" height="24" fill="white" />
Expand All @@ -20,7 +26,9 @@
<script setup lang="ts">
withDefaults(defineProps<{
size?: number | string;
dotted?: boolean;
}>(), {
size: 24,
dotted: false,
});
</script>

0 comments on commit 4d20183

Please sign in to comment.