diff --git a/resources/scripts/api/account/activity.ts b/resources/scripts/api/account/activity.ts index 7746c8a49..7d56061ed 100644 --- a/resources/scripts/api/account/activity.ts +++ b/resources/scripts/api/account/activity.ts @@ -4,11 +4,12 @@ import { ActivityLog, Transformers } from '@definitions/user'; import { AxiosError } from 'axios'; import http, { PaginatedResult, QueryBuilderParams, withQueryBuilderParams } from '@/api/http'; import { toPaginatedSet } from '@definitions/helpers'; +import useFilteredObject from '@/plugins/useFilteredObject'; export type ActivityLogFilters = QueryBuilderParams<'ip' | 'event', 'timestamp'>; const useActivityLogs = (filters?: ActivityLogFilters, config?: ConfigInterface, AxiosError>): responseInterface, AxiosError> => { - const key = useUserSWRContentKey([ 'account', 'activity', JSON.stringify(filters) ]); + const key = useUserSWRContentKey([ 'account', 'activity', JSON.stringify(useFilteredObject(filters || {})) ]); return useSWR>(key, async () => { const { data } = await http.get('/api/client/account/activity', { diff --git a/resources/scripts/api/http.ts b/resources/scripts/api/http.ts index 2dc54a606..119f074aa 100644 --- a/resources/scripts/api/http.ts +++ b/resources/scripts/api/http.ts @@ -88,7 +88,7 @@ export interface FractalPaginatedResponse extends FractalResponseList { total_pages: number; /* eslint-enable camelcase */ }; - } + }; } export interface PaginatedResult { diff --git a/resources/scripts/helpers.ts b/resources/scripts/helpers.ts index 67d079427..fe6cc4f51 100644 --- a/resources/scripts/helpers.ts +++ b/resources/scripts/helpers.ts @@ -65,3 +65,13 @@ export function hashToPath (hash: string): string { export function formatIp (ip: string): string { return /([a-f0-9:]+:+)+[a-f0-9]+/.test(ip) ? `[${ip}]` : ip; } + +// eslint-disable-next-line @typescript-eslint/ban-types +export const isObject = (o: unknown): o is {} => typeof o === 'object' && o !== null; + +// eslint-disable-next-line @typescript-eslint/ban-types +export const isEmptyObject = (o: {}): boolean => + Object.keys(o).length === 0 && Object.getPrototypeOf(o) === Object.prototype; + +// eslint-disable-next-line @typescript-eslint/ban-types +export const getObjectKeys = (o: T): Array => Object.keys(o) as Array; diff --git a/resources/scripts/plugins/useFilteredObject.ts b/resources/scripts/plugins/useFilteredObject.ts new file mode 100644 index 000000000..440e422f3 --- /dev/null +++ b/resources/scripts/plugins/useFilteredObject.ts @@ -0,0 +1,22 @@ +/** + * Similar to "withQueryBuilderParams" except this function filters out any null, + * undefined, or empty string key values. This allows the parameters to be used for + * caching without having to account for all of the different data combinations. + */ +import { isEmptyObject, isObject } from '@/helpers'; + +// eslint-disable-next-line @typescript-eslint/ban-types +export default (data: T): T => { + const empty = [ undefined, null, '' ] as unknown[]; + + const removeEmptyValues = (input: T): T => + Object.entries(input) + .filter(([ _, value ]) => !empty.includes(value)) + .reduce((obj, [ k, v ]) => { + const parsed = isObject(v) ? removeEmptyValues(v as any) : v; + + return isObject(parsed) && isEmptyObject(parsed) ? obj : { ...obj, [k]: parsed }; + }, {} as T); + + return removeEmptyValues(data); +};