From 04be7fdb3c12b84a6922e8a2a98c90ff33f9f4ca Mon Sep 17 00:00:00 2001 From: James Collins Date: Mon, 28 Aug 2023 23:16:04 +1000 Subject: [PATCH] implement callback/caching --- resources/js/helpers/api.ts | 82 ++++++++++++++++++++++++++++---- resources/js/store/CacheStore.ts | 57 ++++++++++++++++++++++ 2 files changed, 129 insertions(+), 10 deletions(-) create mode 100644 resources/js/store/CacheStore.ts diff --git a/resources/js/helpers/api.ts b/resources/js/helpers/api.ts index 3b9b322..2ed8df1 100644 --- a/resources/js/helpers/api.ts +++ b/resources/js/helpers/api.ts @@ -1,5 +1,6 @@ import { useUserStore } from "../store/UserStore"; import { useApplicationStore } from "../store/ApplicationStore"; +import { useCacheStore } from "../store/CacheStore"; import { ImportMetaExtras } from "../../../import-meta"; interface ApiProgressData { @@ -7,7 +8,16 @@ interface ApiProgressData { total: number; } +interface ApiCallbackData { + status: number; + statusText: string; + url: string; + headers: unknown; + data: unknown; +} + type ApiProgressCallback = (progress: ApiProgressData) => void; +type ApiResultCallback = (data: ApiCallbackData) => void; interface ApiOptions { url: string; @@ -17,6 +27,7 @@ interface ApiOptions { body?: string | object | FormData | ArrayBuffer | Blob; signal?: AbortSignal | null; progress?: ApiProgressCallback; + callback?: ApiResultCallback; } export interface ApiResponse { @@ -47,11 +58,11 @@ export const api = { if (url.includes(placeholder)) { url = url.replace( placeholder, - encodeURIComponent(value) + encodeURIComponent(value), ); } else { params += `&${encodeURIComponent( - key + key, )}=${encodeURIComponent(value)}`; } } @@ -79,7 +90,7 @@ export const api = { if ( Object.prototype.hasOwnProperty.call( options.headers, - "Content-Type" + "Content-Type", ) ) { // remove the "Content-Type" key from the headers object @@ -155,12 +166,25 @@ export const api = { useApplicationStore().unavailable = false; if (xhr.status < 300) { - resolve(result); + if (options.callback) { + options.callback(result); + } else { + resolve(result); + } + + return; } else { if (xhr.status == 503) { useApplicationStore().unavailable = true; } - reject(result); + + if (options.callback) { + options.callback(result); + } else { + reject(result); + } + + return; } }; } else { @@ -178,6 +202,13 @@ export const api = { fetchOptions.body = options.body; } + if (fetchOptions.method == "GET" && options.callback) { + const cache = useCacheStore().getCacheByUrl(url); + if (cache != null) { + options.callback(cache); + } + } + fetch(url, fetchOptions) .then(async (response) => { let data: string | object = ""; @@ -220,7 +251,30 @@ export const api = { if (response.status === 503) { useApplicationStore().unavailable = true; } - reject(result); + + if (options.callback) { + options.callback(result); + } else { + reject(result); + } + + return; + } + + if (options.callback) { + if (fetchOptions.method == "GET") { + const modified = useCacheStore().updateCache( + url, + result, + ); + + if (modified == false) { + return; + } + } + + options.callback(result); + return; } resolve(result); @@ -228,10 +282,18 @@ export const api = { .catch((error) => { // Handle any errors thrown during the fetch process const { response, ...rest } = error; - reject({ + const result = { ...rest, response: response && response.json(), - }); + }; + + if (options.callback) { + options.callback(result); + } else { + reject(result); + } + + return; }); } }); @@ -277,7 +339,7 @@ export const api = { }, delete: async function ( - options: ApiOptions | string + options: ApiOptions | string, ): Promise { let apiOptions = {} as ApiOptions; @@ -301,7 +363,7 @@ export const api = { export function getApiResultData( // eslint-disable-next-line @typescript-eslint/no-explicit-any result: any, - defaultValue: T | null = null + defaultValue: T | null = null, ): T | null { if (!result || !Object.prototype.hasOwnProperty.call(result, "data")) { return defaultValue; diff --git a/resources/js/store/CacheStore.ts b/resources/js/store/CacheStore.ts new file mode 100644 index 0000000..c438149 --- /dev/null +++ b/resources/js/store/CacheStore.ts @@ -0,0 +1,57 @@ +import { defineStore } from "pinia"; + +interface CacheItem { + url: string; + data: unknown; +} + +export const useCacheStore = defineStore({ + id: "cache", + state: () => ({ + cache: [] as CacheItem[], + }), + + actions: { + // Method to retrieve cached JSON data based on a URL + getCacheByUrl(url: string) { + const cachedItem = this.cache.find((item) => item.url === url); + return cachedItem ? cachedItem.data : null; + }, + + // Method to update the cache with new data and check for modifications + updateCache(url: string, newData: unknown): boolean { + const index = this.cache.findIndex((item) => item.url === url); + + if (index !== -1) { + // If the URL is already in the cache, check for modifications + const existingData = this.cache[index].data; + + if (JSON.stringify(existingData) === JSON.stringify(newData)) { + // Data is not modified, return false + return false; + } else { + // Data is modified, update the cache + this.cache[index].data = newData; + return true; + } + } else { + // If the URL is not in the cache, add it + this.cache.push({ url, data: newData }); + return true; + } + }, + + // Method to clear the cache for a specific URL + clearCacheByUrl(url: string) { + const index = this.cache.findIndex((item) => item.url === url); + if (index !== -1) { + this.cache.splice(index, 1); + } + }, + + // Method to clear the entire cache + clearCache() { + this.cache = []; + }, + }, +});