web/satellite: add loader to object browser table

This is a fix based on early feedback from QA team.
Added loader to object browser table so that user can't change pages while request is still in progress because it breaks pagination.

Change-Id: I5cc2ff057955478b3c745c169d520e1a639eff92
This commit is contained in:
Vitalii 2023-09-14 16:34:55 +03:00 committed by Storj Robot
parent bd48a5cbe6
commit 92a69c7de4
4 changed files with 64 additions and 42 deletions

View File

@ -111,6 +111,7 @@
:total-page-count="isPaginationEnabled ? pageCount : 0"
:total-items-count="isPaginationEnabled ? fetchedObjectsCount : files.length"
show-select
:loading="isLoading"
class="file-browser-table"
:on-page-change="isPaginationEnabled ? changePageAndLimit : null"
@selectAllClicked="toggleSelectAllFiles"
@ -193,7 +194,7 @@
</template>
</v-table>
<div
v-if="!fetchingFilesSpinner"
v-if="!isLoading"
class="upload-help"
@click="buttonFileUpload"
>
@ -202,12 +203,6 @@
Drop Files Here to Upload
</p>
</div>
<div
v-else
class="d-flex justify-content-center"
>
<div class="spinner-border" />
</div>
</div>
</div>
</div>
@ -239,6 +234,7 @@ import { useBucketsStore } from '@/store/modules/bucketsStore';
import { useConfigStore } from '@/store/modules/configStore';
import { useAnalyticsStore } from '@/store/modules/analyticsStore';
import { DEFAULT_PAGE_LIMIT } from '@/types/pagination';
import { useLoading } from '@/composables/useLoading';
import VButton from '@/components/common/VButton.vue';
import BucketSettingsNav from '@/components/objects/BucketSettingsNav.vue';
@ -261,11 +257,11 @@ const analyticsStore = useAnalyticsStore();
const router = useRouter();
const route = useRoute();
const notify = useNotify();
const { isLoading, withLoading } = useLoading();
const folderInput = ref<HTMLInputElement>();
const fileInput = ref<HTMLInputElement>();
const fetchingFilesSpinner = ref<boolean>(false);
const isUploadDropDownShown = ref<boolean>(false);
const isLockedBanner = ref<boolean>(true);
const isTooManyObjectsBanner = ref<boolean>(true);
@ -363,7 +359,7 @@ const objectsCount = computed((): number => {
const lockedFilesEntryDisplayed = computed((): boolean => {
return lockedFilesCount.value > 0 &&
objectsCount.value <= NUMBER_OF_DISPLAYED_OBJECTS &&
!fetchingFilesSpinner.value &&
!isLoading.value &&
!currentPath.value;
});
@ -444,7 +440,7 @@ const bucket = computed((): string => {
/**
* Changes table page and limit.
*/
function changePageAndLimit(page: number, limit: number): void {
async function changePageAndLimit(page: number, limit: number): void {
obStore.setCursor({ limit, page });
const lastObjectOnPage = page * limit;
@ -454,15 +450,17 @@ function changePageAndLimit(page: number, limit: number): void {
return;
}
const tokenKey = Math.ceil(lastObjectOnPage / MAX_KEY_COUNT) * MAX_KEY_COUNT;
await withLoading(async () => {
const tokenKey = Math.ceil(lastObjectOnPage / MAX_KEY_COUNT) * MAX_KEY_COUNT;
const tokenToBeFetched = obStore.state.continuationTokens.get(tokenKey);
if (!tokenToBeFetched) {
obStore.initList(routePath.value);
return;
}
const tokenToBeFetched = obStore.state.continuationTokens.get(tokenKey);
if (!tokenToBeFetched) {
await obStore.initList(routePath.value);
return;
}
obStore.listByToken(routePath.value, tokenKey, tokenToBeFetched);
await obStore.listByToken(routePath.value, tokenKey, tokenToBeFetched);
});
}
/**
@ -496,11 +494,13 @@ async function onRouteChange(): Promise<void> {
routePath.value = calculateRoutePath();
obStore.closeDropdown();
if (isPaginationEnabled.value) {
await obStore.initList(routePath.value);
} else {
await list(routePath.value);
}
await withLoading(async () => {
if (isPaginationEnabled.value) {
await obStore.initList(routePath.value);
} else {
await list(routePath.value);
}
});
}
/**
@ -653,24 +653,20 @@ onBeforeMount(async () => {
// clear previous file selections.
obStore.clearAllSelectedFiles();
// display the spinner while files are being fetched
fetchingFilesSpinner.value = true;
try {
if (isPaginationEnabled.value) {
await obStore.initList('');
} else {
await Promise.all([
list(''),
obStore.getObjectCount(),
]);
await withLoading(async () => {
try {
if (isPaginationEnabled.value) {
await obStore.initList('');
} else {
await Promise.all([
list(''),
obStore.getObjectCount(),
]);
}
} catch (err) {
notify.error(err.message, AnalyticsErrorEventSource.FILE_BROWSER_LIST_CALL);
}
} catch (err) {
notify.error(err.message, AnalyticsErrorEventSource.FILE_BROWSER_LIST_CALL);
}
// remove the spinner after files have been fetched
fetchingFilesSpinner.value = false;
});
});
onBeforeUnmount(() => {

View File

@ -10,7 +10,7 @@
<span v-else class="size-changer__selector__content__label">Size</span>
<arrow-down-icon class="size-changer__selector__content__arrow" :class="{ open: isOpen }" />
</div>
<div v-if="isOpen" v-click-outside="closeSelector" class="size-changer__selector__dropdown">
<div v-if="isOpen" v-click-outside="closeSelector" class="size-changer__selector__dropdown" :class="{ 'custom-top': !withAllOption }">
<div
v-for="(option, index) in options"
:key="index"
@ -54,12 +54,16 @@ const options = computed((): {label:string, value:number}[] => {
{ label: '50', value: 50 },
{ label: '100', value: 100 },
];
if (props.itemCount && props.itemCount < 1000 && !props.simplePagination) {
if (props.itemCount && withAllOption.value) {
return [{ label: 'All', value: props.itemCount }, ...opts];
}
return opts;
});
const withAllOption = computed<boolean>(() => {
return props.itemCount !== undefined && props.itemCount < 1000 && !props.simplePagination;
});
/**
* whether the selector drop down is open
* */
@ -191,4 +195,8 @@ function toggleSelector() {
}
}
}
.custom-top {
top: -150px;
}
</style>

View File

@ -3,6 +3,9 @@
<template>
<div class="table-wrapper">
<div v-if="loading" class="table-wrapper__loader">
<VLoader width="100px" height="100px" />
</div>
<table class="base-table" border="0" cellpadding="0" cellspacing="0">
<thead>
<tr>
@ -38,6 +41,7 @@ import { PageChangeCallback } from '@/types/pagination';
import TablePagination from '@/components/common/TablePagination.vue';
import VTableCheckbox from '@/components/common/VTableCheckbox.vue';
import VLoader from '@/components/common/VLoader.vue';
const props = withDefaults(defineProps<{
itemsLabel?: string,
@ -52,6 +56,7 @@ const props = withDefaults(defineProps<{
selected?: boolean,
showSelect?: boolean,
simplePagination?: boolean,
loading?: boolean,
}>(), {
selectable: false,
selected: false,
@ -65,6 +70,7 @@ const props = withDefaults(defineProps<{
onNextClicked: null,
onPreviousClicked: null,
onPageSizeChanged: null,
loading: false,
});
const emit = defineEmits(['selectAllClicked']);
@ -74,6 +80,18 @@ const emit = defineEmits(['selectAllClicked']);
.table-wrapper {
background: #fff;
border-radius: 12px;
position: relative;
&__loader {
border-radius: 12px;
z-index: 1;
background-color: rgb(0 0 0 / 5%);
position: absolute;
inset: 0;
display: flex;
align-items: center;
justify-content: center;
}
}
.base-table {

View File

@ -8,7 +8,7 @@
transition="fade-transition"
:persistent="isLoading"
>
<v-card rounded="xlg" ref="innerContent">
<v-card ref="innerContent" rounded="xlg">
<v-card-item class="pl-7 py-4">
<template #prepend>
<v-sheet