web/satellite: add gallery view caching

Add caching for gallery view previews and map.

Issue:
https://github.com/storj/storj/issues/5969

Change-Id: I6c9755aec6e1d4143005835adad212cafd46f649
This commit is contained in:
Vitalii 2023-06-22 16:31:30 +03:00 committed by Storj Robot
parent 578724e9b1
commit 6f078acb8d
2 changed files with 74 additions and 8 deletions

View File

@ -116,10 +116,11 @@ import { Component, computed, onBeforeMount, onMounted, ref, Teleport, watch } f
import { useRoute } from 'vue-router'; import { useRoute } from 'vue-router';
import prettyBytes from 'pretty-bytes'; import prettyBytes from 'pretty-bytes';
import { BrowserObject, useObjectBrowserStore } from '@/store/modules/objectBrowserStore'; import { BrowserObject, PreviewCache, useObjectBrowserStore } from '@/store/modules/objectBrowserStore';
import { AnalyticsErrorEventSource } from '@/utils/constants/analyticsEventNames'; import { AnalyticsErrorEventSource } from '@/utils/constants/analyticsEventNames';
import { useAppStore } from '@/store/modules/appStore'; import { useAppStore } from '@/store/modules/appStore';
import { useNotify } from '@/utils/hooks'; import { useNotify } from '@/utils/hooks';
import { useBucketsStore } from '@/store/modules/bucketsStore';
import { RouteConfig } from '@/types/router'; import { RouteConfig } from '@/types/router';
import ButtonIcon from '@/components/browser/galleryView/ButtonIcon.vue'; import ButtonIcon from '@/components/browser/galleryView/ButtonIcon.vue';
@ -145,6 +146,7 @@ import ArrowIcon from '@/../static/images/browser/galleryView/arrow.svg';
const appStore = useAppStore(); const appStore = useAppStore();
const obStore = useObjectBrowserStore(); const obStore = useObjectBrowserStore();
const bucketsStore = useBucketsStore();
const notify = useNotify(); const notify = useNotify();
const route = useRoute(); const route = useRoute();
@ -159,6 +161,13 @@ const objectPreviewUrl = ref<string>('');
const folderType = 'folder'; const folderType = 'folder';
/**
* Returns object preview URLs cache from store.
*/
const cachedObjectPreviewURLs = computed((): Map<string, PreviewCache> => {
return obStore.state.cachedObjectPreviewURLs;
});
/** /**
* Retrieve the file object that the modal is set to from the store. * Retrieve the file object that the modal is set to from the store.
*/ */
@ -194,6 +203,13 @@ const extension = computed((): string | undefined => {
return filePath.value.split('.').pop(); return filePath.value.split('.').pop();
}); });
/**
* Returns bucket name from store.
*/
const bucket = computed((): string => {
return bucketsStore.state.fileComponentBucketName;
});
/** /**
* Check to see if the current file is an image file. * Check to see if the current file is an image file.
*/ */
@ -263,6 +279,9 @@ async function fetchPreviewAndMapUrl(): Promise<void> {
return; return;
} }
const encodedPath = encodeURIComponent(`${bucket.value}/${filePath.value.trim()}`);
obStore.cacheObjectPreviewURL(encodedPath, { url, lastModified: file.value.LastModified.getTime() });
objectMapUrl.value = `${url}?map=1`; objectMapUrl.value = `${url}?map=1`;
objectPreviewUrl.value = `${url}?view=1`; objectPreviewUrl.value = `${url}?view=1`;
isLoading.value = false; isLoading.value = false;
@ -386,11 +405,41 @@ function setNewObjectPath(objectKey: string): void {
obStore.setObjectPathForModal(`${currentPath.value}${objectKey}`); obStore.setObjectPathForModal(`${currentPath.value}${objectKey}`);
} }
/**
* Loads object URL from cache or generates new URL.
*/
function processFilePath(): void {
const url = findCachedURL();
if (!url) {
fetchPreviewAndMapUrl();
return;
}
objectMapUrl.value = `${url}?map=1`;
objectPreviewUrl.value = `${url}?view=1`;
}
/**
* Try to find current object path in cache.
*/
function findCachedURL(): string | undefined {
const encodedPath = encodeURIComponent(`${bucket.value}/${filePath.value.trim()}`);
const cache = cachedObjectPreviewURLs.value.get(encodedPath);
if (!cache) return undefined;
if (cache.lastModified !== file.value.LastModified.getTime()) {
obStore.removeFromObjectPreviewCache(encodedPath);
return undefined;
}
return cache.url;
}
/** /**
* Call `fetchPreviewAndMapUrl` on before mount lifecycle method. * Call `fetchPreviewAndMapUrl` on before mount lifecycle method.
*/ */
onBeforeMount((): void => { onBeforeMount((): void => {
fetchPreviewAndMapUrl(); processFilePath();
}); });
onMounted((): void => { onMounted((): void => {
@ -403,7 +452,7 @@ onMounted((): void => {
watch(filePath, () => { watch(filePath, () => {
if (!filePath.value) return; if (!filePath.value) return;
fetchPreviewAndMapUrl(); processFilePath();
}); });
</script> </script>

View File

@ -31,7 +31,7 @@ type Promisable<T> = T | PromiseLike<T>;
export type BrowserObject = { export type BrowserObject = {
Key: string; Key: string;
Size: number; Size: number;
LastModified: number; LastModified: Date;
type?: 'file' | 'folder'; type?: 'file' | 'folder';
progress?: number; progress?: number;
upload?: { upload?: {
@ -58,6 +58,11 @@ export type UploadingBrowserObject = BrowserObject & {
failedMessage?: FailedUploadMessage; failedMessage?: FailedUploadMessage;
} }
export type PreviewCache = {
url: string,
lastModified: number,
}
export class FilesState { export class FilesState {
s3: S3Client | null = null; s3: S3Client | null = null;
accessKey: null | string = null; accessKey: null | string = null;
@ -80,6 +85,7 @@ export class FilesState {
openModalOnFirstUpload = false; openModalOnFirstUpload = false;
objectPathForModal = ''; objectPathForModal = '';
objectsCount = 0; objectsCount = 0;
cachedObjectPreviewURLs: Map<string, PreviewCache> = new Map<string, PreviewCache>();
} }
type InitializedFilesState = FilesState & { type InitializedFilesState = FilesState & {
@ -273,7 +279,7 @@ export const useObjectBrowserStore = defineStore('objectBrowser', () => {
Prefix: string; Prefix: string;
}): BrowserObject => ({ }): BrowserObject => ({
Key: Prefix.slice(path.length, -1), Key: Prefix.slice(path.length, -1),
LastModified: 0, LastModified: new Date(),
Size: 0, Size: 0,
type: 'folder', type: 'folder',
}); });
@ -455,7 +461,7 @@ export const useObjectBrowserStore = defineStore('objectBrowser', () => {
upload, upload,
progress: 0, progress: 0,
Size: 0, Size: 0,
LastModified: 0, LastModified: new Date(),
Body: file, Body: file,
status: UploadingStatus.Failed, status: UploadingStatus.Failed,
failedMessage: FailedUploadMessage.TooBig, failedMessage: FailedUploadMessage.TooBig,
@ -471,7 +477,7 @@ export const useObjectBrowserStore = defineStore('objectBrowser', () => {
upload, upload,
progress: 0, progress: 0,
Size: 0, Size: 0,
LastModified: 0, LastModified: new Date(),
status: UploadingStatus.InProgress, status: UploadingStatus.InProgress,
}); });
@ -552,7 +558,7 @@ export const useObjectBrowserStore = defineStore('objectBrowser', () => {
upload, upload,
progress: 0, progress: 0,
Size: 0, Size: 0,
LastModified: 0, LastModified: new Date(),
status: UploadingStatus.InProgress, status: UploadingStatus.InProgress,
}; };
@ -770,6 +776,14 @@ export const useObjectBrowserStore = defineStore('objectBrowser', () => {
state.objectPathForModal = path; state.objectPathForModal = path;
} }
function cacheObjectPreviewURL(path: string, cacheValue: PreviewCache): void {
state.cachedObjectPreviewURLs.set(path, cacheValue);
}
function removeFromObjectPreviewCache(path: string): void {
state.cachedObjectPreviewURLs.delete(path);
}
function setSelectedAnchorFile(file: BrowserObject | null): void { function setSelectedAnchorFile(file: BrowserObject | null): void {
state.selectedAnchorFile = file; state.selectedAnchorFile = file;
} }
@ -803,6 +817,7 @@ export const useObjectBrowserStore = defineStore('objectBrowser', () => {
state.orderBy = 'asc'; state.orderBy = 'asc';
state.openModalOnFirstUpload = false; state.openModalOnFirstUpload = false;
state.objectPathForModal = ''; state.objectPathForModal = '';
state.cachedObjectPreviewURLs = new Map<string, PreviewCache>();
} }
return { return {
@ -835,6 +850,8 @@ export const useObjectBrowserStore = defineStore('objectBrowser', () => {
setSelectedAnchorFile, setSelectedAnchorFile,
setUnselectedAnchorFile, setUnselectedAnchorFile,
cancelUpload, cancelUpload,
cacheObjectPreviewURL,
removeFromObjectPreviewCache,
clearUploading, clearUploading,
clear, clear,
}; };