diff --git a/package.json b/package.json index b493005..a337867 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "qapp-core", - "version": "1.0.49", + "version": "1.0.51", "description": "Qortal's core React library with global state, UI components, and utilities", "main": "dist/index.js", "module": "dist/index.mjs", diff --git a/src/components/VideoPlayer/LoadingVideo.tsx b/src/components/VideoPlayer/LoadingVideo.tsx index 4d6e9ae..3ce6662 100644 --- a/src/components/VideoPlayer/LoadingVideo.tsx +++ b/src/components/VideoPlayer/LoadingVideo.tsx @@ -18,6 +18,7 @@ interface LoadingVideoProps { togglePlay: () => void; startPlay: boolean; downloadResource: () => void; + isStatusWrong: boolean } export const LoadingVideo = ({ status, @@ -27,6 +28,7 @@ export const LoadingVideo = ({ togglePlay, startPlay, downloadResource, + isStatusWrong }: LoadingVideoProps) => { const getDownloadProgress = (percentLoaded: number) => { const progress = percentLoaded; @@ -80,7 +82,7 @@ export const LoadingVideo = ({ <> Refetching in 10 seconds - ) : status === "DOWNLOADED" ? ( + ) : (status === "DOWNLOADED" && !isStatusWrong) ? ( <>Download Completed: building video... ) : status === "FAILED_TO_DOWNLOAD" ? ( <>Unable to fetch video chunks from peers diff --git a/src/components/VideoPlayer/VideoPlayer.tsx b/src/components/VideoPlayer/VideoPlayer.tsx index 4b3e575..cfa335a 100644 --- a/src/components/VideoPlayer/VideoPlayer.tsx +++ b/src/components/VideoPlayer/VideoPlayer.tsx @@ -196,6 +196,7 @@ export const VideoPlayer = ({ seekTo, togglePictureInPicture, downloadResource, + isStatusWrong } = useVideoPlayerController({ autoPlay, playerRef, @@ -357,7 +358,7 @@ export const VideoPlayer = ({ if (onPlayParent) { onPlayParent(); } - }, [setIsPlaying]); + }, [setIsPlaying, onPlayParent]); const onPause = useCallback(() => { setIsPlaying(false); @@ -762,6 +763,7 @@ export const VideoPlayer = ({ isLoading={isLoading} startPlay={startPlay} downloadResource={downloadResource} + isStatusWrong={isStatusWrong} /> { const startedFetchRef = useRef(false); const { playbackSettings, setPlaybackRate } = useVideoStore(); - const { isReady, resourceUrl, status, percentLoaded, downloadResource } = + const { isReady, resourceUrl, status, localChunkCount, totalChunkCount , percentLoaded, downloadResource } = useResourceStatus({ resource: !startedFetch ? null : qortalVideoResource, retryAttempts, @@ -304,5 +304,6 @@ export const useVideoPlayerController = (props: UseVideoControls) => { seekTo, togglePictureInPicture, downloadResource, + isStatusWrong: !isNaN(totalChunkCount) && !isNaN(localChunkCount) && totalChunkCount === 2 && (totalChunkCount < localChunkCount) }; }; diff --git a/src/i18n/compiled-i18n.json b/src/i18n/compiled-i18n.json index d35bb8e..7175ae2 100644 --- a/src/i18n/compiled-i18n.json +++ b/src/i18n/compiled-i18n.json @@ -71,77 +71,6 @@ } } }, - "de": { - "lib-core": { - "subtitle": { - "subtitles": "Untertitel", - "no_subtitles": "Keine Untertitel", - "load_community_subs": "Community-Untertitel laden", - "off": "Aus", - "deleting_subtitle": "Untertitel wird gelöscht...", - "deleted": "Untertitel gelöscht", - "unable_delete": "Löschen nicht möglich", - "publishing": "Untertitel werden veröffentlicht...", - "published": "Untertitel veröffentlicht", - "unable_publish": "Veröffentlichung nicht möglich", - "my_subtitles": "Meine Untertitel", - "new": "Neu", - "existing": "Vorhanden", - "import_subtitles": "Untertitel importieren" - }, - "actions": { - "remove": "entfernen", - "publish": "veröffentlichen", - "delete": "löschen", - "publish_metadata": "Metadaten veröffentlichen", - "publish_index": "Index veröffentlichen", - "cancel": "abbrechen", - "continue": "fortfahren", - "close": "schließen", - "retry": "wiederholen" - }, - "video": { - "playback_speed": "Wiedergabegeschwindigkeit", - "toggle_fullscreen": "Vollbild umschalten (F)", - "video_speed": "Video-Geschwindigkeit. Erhöhen (+ oder >), Verringern (- oder <)", - "toggle_mute": "Stummschalten umschalten (M), Lauter (OBEN), Leiser (UNTEN)", - "seek_video": "Video in 10%-Schritten vorspulen (0–9)", - "reload_video": "Video neu laden (R)", - "play_pause": "Wiedergabe/Pause (Leertaste)" - }, - "index": { - "title": "Index-Manager", - "create_new_index": "Neuen Index erstellen", - "add_metadata": "Metadaten hinzufügen", - "publishing_metadata": "Metadaten werden veröffentlicht...", - "published_metadata": "Metadaten erfolgreich veröffentlicht", - "failed_metadata": "Veröffentlichung der Metadaten fehlgeschlagen", - "example": "Beispiel, wie es aussehen könnte:", - "metadata_title": "Titel", - "metadata_description": "Beschreibung", - "metadata_title_placeholder": "Titel für den Link hinzufügen", - "metadata_description_placeholder": "Beschreibung für den Link hinzufügen", - "characters": "Zeichen", - "publishing_index": "Index wird veröffentlicht...", - "published_index": "Index erfolgreich veröffentlicht", - "failed_index": "Veröffentlichung des Index fehlgeschlagen", - "recommended_indices": "Empfohlene Indizes", - "add_search_term": "Suchbegriff hinzufügen", - "search_terms": "Suchbegriffe", - "recommendation_size": "Es wird empfohlen, die Zeichenanzahl pro Begriff unter {{recommendedSize}} Zeichen zu halten", - "multiple_title": "Mehrere Indizes hinzufügen", - "multiple_description": "Weitere Indizes senken die Veröffentlichungsgebühren, haben aber weniger Gewicht in zukünftigen Suchergebnissen." - }, - "multi_publish": { - "title": "Veröffentlichungsstatus", - "publish_failed": "Veröffentlichung fehlgeschlagen", - "file_chunk": "Dateifragment", - "file_processing": "Datei wird verarbeitet", - "success": "Erfolgreich veröffentlicht", - "attempt_retry": "Veröffentlichung fehlgeschlagen. Erneuter Versuch..." - } - } - }, "en": { "lib-core": { "subtitle": { @@ -213,6 +142,77 @@ } } }, + "de": { + "lib-core": { + "subtitle": { + "subtitles": "Untertitel", + "no_subtitles": "Keine Untertitel", + "load_community_subs": "Community-Untertitel laden", + "off": "Aus", + "deleting_subtitle": "Untertitel wird gelöscht...", + "deleted": "Untertitel gelöscht", + "unable_delete": "Löschen nicht möglich", + "publishing": "Untertitel werden veröffentlicht...", + "published": "Untertitel veröffentlicht", + "unable_publish": "Veröffentlichung nicht möglich", + "my_subtitles": "Meine Untertitel", + "new": "Neu", + "existing": "Vorhanden", + "import_subtitles": "Untertitel importieren" + }, + "actions": { + "remove": "entfernen", + "publish": "veröffentlichen", + "delete": "löschen", + "publish_metadata": "Metadaten veröffentlichen", + "publish_index": "Index veröffentlichen", + "cancel": "abbrechen", + "continue": "fortfahren", + "close": "schließen", + "retry": "wiederholen" + }, + "video": { + "playback_speed": "Wiedergabegeschwindigkeit", + "toggle_fullscreen": "Vollbild umschalten (F)", + "video_speed": "Video-Geschwindigkeit. Erhöhen (+ oder >), Verringern (- oder <)", + "toggle_mute": "Stummschalten umschalten (M), Lauter (OBEN), Leiser (UNTEN)", + "seek_video": "Video in 10%-Schritten vorspulen (0–9)", + "reload_video": "Video neu laden (R)", + "play_pause": "Wiedergabe/Pause (Leertaste)" + }, + "index": { + "title": "Index-Manager", + "create_new_index": "Neuen Index erstellen", + "add_metadata": "Metadaten hinzufügen", + "publishing_metadata": "Metadaten werden veröffentlicht...", + "published_metadata": "Metadaten erfolgreich veröffentlicht", + "failed_metadata": "Veröffentlichung der Metadaten fehlgeschlagen", + "example": "Beispiel, wie es aussehen könnte:", + "metadata_title": "Titel", + "metadata_description": "Beschreibung", + "metadata_title_placeholder": "Titel für den Link hinzufügen", + "metadata_description_placeholder": "Beschreibung für den Link hinzufügen", + "characters": "Zeichen", + "publishing_index": "Index wird veröffentlicht...", + "published_index": "Index erfolgreich veröffentlicht", + "failed_index": "Veröffentlichung des Index fehlgeschlagen", + "recommended_indices": "Empfohlene Indizes", + "add_search_term": "Suchbegriff hinzufügen", + "search_terms": "Suchbegriffe", + "recommendation_size": "Es wird empfohlen, die Zeichenanzahl pro Begriff unter {{recommendedSize}} Zeichen zu halten", + "multiple_title": "Mehrere Indizes hinzufügen", + "multiple_description": "Weitere Indizes senken die Veröffentlichungsgebühren, haben aber weniger Gewicht in zukünftigen Suchergebnissen." + }, + "multi_publish": { + "title": "Veröffentlichungsstatus", + "publish_failed": "Veröffentlichung fehlgeschlagen", + "file_chunk": "Dateifragment", + "file_processing": "Datei wird verarbeitet", + "success": "Erfolgreich veröffentlicht", + "attempt_retry": "Veröffentlichung fehlgeschlagen. Erneuter Versuch..." + } + } + }, "es": { "lib-core": { "subtitle": { @@ -642,8 +642,8 @@ }, "supportedLanguages": [ "ar", - "de", "en", + "de", "es", "fr", "it", diff --git a/src/state/cache.ts b/src/state/cache.ts index 28088cb..e2b91e6 100644 --- a/src/state/cache.ts +++ b/src/state/cache.ts @@ -1,28 +1,31 @@ import { create } from "zustand"; -import { QortalGetMetadata, QortalMetadata } from "../types/interfaces/resources"; +import { + QortalGetMetadata, + QortalMetadata, +} from "../types/interfaces/resources"; import { persist } from "zustand/middleware"; - interface SearchCache { [listName: string]: { searches: { [searchTerm: string]: QortalMetadata[]; // List of products for each search term }; - temporaryNewResources: QortalMetadata[], + temporaryNewResources: QortalMetadata[]; expiry: number; // Expiry timestamp for the whole list searchParamsStringified: string; }; } - - -export const mergeUniqueItems = (array1: QortalMetadata[], array2: QortalMetadata[]) => { +export const mergeUniqueItems = ( + array1: QortalMetadata[], + array2: QortalMetadata[] +) => { const mergedArray = [...array1, ...array2]; // Use a Map to ensure uniqueness based on `identifier-name` const uniqueMap = new Map(); - mergedArray.forEach(item => { + mergedArray.forEach((item) => { if (item.identifier && item.name && item.service) { const key = `${item.service}-${item.name}-${item.identifier}`; uniqueMap.set(key, item); @@ -33,241 +36,278 @@ export const mergeUniqueItems = (array1: QortalMetadata[], array2: QortalMetadat }; export interface ListItem { - data: any - qortalMetadata: QortalMetadata + data: any; + qortalMetadata: QortalMetadata; } - interface resourceCache { - [id: string]: { - data: ListItem | false | null; // Cached resource data - expiry: number; // Expiry timestamp in milliseconds - }; - } + [id: string]: { + data: ListItem | false | null; // Cached resource data + expiry: number; // Expiry timestamp in milliseconds + }; +} - interface DeletedResources { - [key: string]: { deleted: true; expiry: number }; // ✅ Added expiry field - } +interface DeletedResources { + [key: string]: { deleted: true; expiry: number }; // ✅ Added expiry field +} interface CacheState { resourceCache: resourceCache; searchCache: SearchCache; // Search cache actions - setResourceCache: (id: string, data: ListItem | false | null, customExpiry?: number) => void; + setResourceCache: ( + id: string, + data: ListItem | false | null, + customExpiry?: number + ) => void; - setSearchCache: (listName: string, searchTerm: string, data: QortalMetadata[], searchParamsStringified: string | null, customExpiry?: number) => void; - setSearchParamsForList: (ListName: string, searchParamsStringified: string)=> void; - getSearchCache: (listName: string, searchTerm: string) => QortalMetadata[] | null; + setSearchCache: ( + listName: string, + searchTerm: string, + data: QortalMetadata[], + searchParamsStringified: string | null, + customExpiry?: number + ) => void; + setSearchParamsForList: ( + ListName: string, + searchParamsStringified: string + ) => void; + getSearchCache: ( + listName: string, + searchTerm: string + ) => QortalMetadata[] | null; clearExpiredCache: () => void; - getResourceCache: (id: string, ignoreExpire?: boolean) => ListItem | false | null; - addTemporaryResource: (listName: string, newResources: QortalMetadata[], customExpiry?: number)=> void; - getTemporaryResources:(listName: string)=> QortalMetadata[] + getResourceCache: ( + id: string, + ignoreExpire?: boolean + ) => ListItem | false | null; + addTemporaryResource: ( + listName: string, + newResources: QortalMetadata[], + customExpiry?: number + ) => void; + getTemporaryResources: (listName: string) => QortalMetadata[]; deletedResources: DeletedResources; markResourceAsDeleted: (item: QortalMetadata | QortalGetMetadata) => void; filterOutDeletedResources: (items: QortalMetadata[]) => QortalMetadata[]; - isListExpired: (listName: string)=> boolean | string; + isListExpired: (listName: string) => boolean | string; searchCacheExpiryDuration: number; resourceCacheExpiryDuration: number; setSearchCacheExpiryDuration: (duration: number) => void; - setResourceCacheExpiryDuration: (duration: number)=> void; + setResourceCacheExpiryDuration: (duration: number) => void; deleteSearchCache: (listName: string) => void; filterSearchCacheItemsByNames: (names: string[]) => void; - } -export const useCacheStore = create - ((set, get) => ({ - searchCacheExpiryDuration: 5 * 60 * 1000, - resourceCacheExpiryDuration: 30 * 60 * 1000, - resourceCache: {}, - searchCache: {}, - deletedResources: {}, - setSearchCacheExpiryDuration: (duration) => set({ searchCacheExpiryDuration: duration }), - setResourceCacheExpiryDuration: (duration) => set({ resourceCacheExpiryDuration: duration }), - getResourceCache: (id, ignoreExpire) => { - const cache = get().resourceCache[id]; - if (cache) { - if (cache.expiry > Date.now() || ignoreExpire) { - return cache.data; // ✅ Return data if not expired - } else { - set((state) => { - const updatedCache = { ...state.resourceCache }; - delete updatedCache[id]; // ✅ Remove expired entry - return { resourceCache: updatedCache }; - }); - } - } - return null; - }, - - setResourceCache: (id, data, customExpiry) => +export const useCacheStore = create((set, get) => ({ + searchCacheExpiryDuration: 5 * 60 * 1000, + resourceCacheExpiryDuration: 30 * 60 * 1000, + resourceCache: {}, + searchCache: {}, + deletedResources: {}, + setSearchCacheExpiryDuration: (duration) => + set({ searchCacheExpiryDuration: duration }), + setResourceCacheExpiryDuration: (duration) => + set({ resourceCacheExpiryDuration: duration }), + getResourceCache: (id, ignoreExpire) => { + const cache = get().resourceCache[id]; + if (cache) { + if (cache.expiry > Date.now() || ignoreExpire) { + return cache.data; // ✅ Return data if not expired + } else { set((state) => { - const expiry = Date.now() + (customExpiry || get().resourceCacheExpiryDuration); - return { - resourceCache: { - ...state.resourceCache, - [id]: { data, expiry }, - }, - }; - }), + const updatedCache = { ...state.resourceCache }; + delete updatedCache[id]; // ✅ Remove expired entry + return { resourceCache: updatedCache }; + }); + } + } + return null; + }, - setSearchCache: (listName, searchTerm, data, searchParamsStringified, customExpiry) => - set((state) => { - const expiry = Date.now() + (customExpiry || get().searchCacheExpiryDuration); - - return { - searchCache: { - ...state.searchCache, - [listName]: { - searches: { - ...(state.searchCache[listName]?.searches || {}), - [searchTerm]: data, - }, - temporaryNewResources: state.searchCache[listName]?.temporaryNewResources || [], - expiry, - searchParamsStringified: searchParamsStringified === null ? state.searchCache[listName]?.searchParamsStringified : searchParamsStringified - }, - }, - }; - }), - deleteSearchCache: (listName) => - set((state) => { - const updatedSearchCache = { ...state.searchCache }; - delete updatedSearchCache[listName]; - return { searchCache: updatedSearchCache }; - }), - setSearchParamsForList: (listName, searchParamsStringified) => - set((state) => { - const existingList = state.searchCache[listName] || {}; - - return { - searchCache: { - ...state.searchCache, - [listName]: { - ...existingList, - searchParamsStringified, - }, - }, - }; - }), - - getSearchCache: (listName, searchTerm) => { - const cache = get().searchCache[listName]; - if (cache) { - if (cache.expiry > Date.now()) { - return cache.searches[searchTerm] || null; // ✅ Return if valid - } else { - set((state) => { - const updatedCache = { ...state.searchCache }; - delete updatedCache[listName]; // ✅ Remove expired list - return { searchCache: updatedCache }; - }); - } - } - return null; + setResourceCache: (id, data, customExpiry) => + set((state) => { + const expiry = + Date.now() + (customExpiry || get().resourceCacheExpiryDuration); + return { + resourceCache: { + ...state.resourceCache, + [id]: { data, expiry }, }, + }; + }), - addTemporaryResource: (listName, newResources, customExpiry) => - set((state) => { + setSearchCache: ( + listName, + searchTerm, + data, + searchParamsStringified, + customExpiry + ) => + set((state) => { + const expiry = + Date.now() + (customExpiry || get().searchCacheExpiryDuration); - const expiry = Date.now() + (customExpiry || 5 * 60 * 1000); - - const existingResources = state.searchCache[listName]?.temporaryNewResources || []; - - // Merge & remove duplicates, keeping the latest by `created` timestamp - const uniqueResourcesMap = new Map(); - - [...existingResources, ...newResources].forEach((item) => { - const key = `${item.service}-${item.name}-${item.identifier}`; - const existingItem = uniqueResourcesMap.get(key); - - if (!existingItem || item.created > existingItem.created) { - uniqueResourcesMap.set(key, item); - } - }); - - return { - searchCache: { - ...state.searchCache, - [listName]: { - ...state.searchCache[listName], - temporaryNewResources: Array.from(uniqueResourcesMap.values()), - expiry, - }, + return { + searchCache: { + ...state.searchCache, + [listName]: { + searches: { + ...(state.searchCache[listName]?.searches || {}), + [searchTerm]: data, }, - }; - }), + temporaryNewResources: + state.searchCache[listName]?.temporaryNewResources || [], + expiry, + searchParamsStringified: + searchParamsStringified === null + ? state.searchCache[listName]?.searchParamsStringified + : searchParamsStringified, + }, + }, + }; + }), + deleteSearchCache: (listName) => + set((state) => { + const updatedSearchCache = { ...state.searchCache }; + delete updatedSearchCache[listName]; + return { searchCache: updatedSearchCache }; + }), + setSearchParamsForList: (listName, searchParamsStringified) => + set((state) => { + const existingList = state.searchCache[listName] || {}; - getTemporaryResources: (listName: string) => { - const cache = get().searchCache[listName]; - if (cache && cache.expiry > Date.now()) { - return cache.temporaryNewResources || []; + return { + searchCache: { + ...state.searchCache, + [listName]: { + ...existingList, + searchParamsStringified, + }, + }, + }; + }), + + getSearchCache: (listName, searchTerm) => { + const cache = get().searchCache[listName]; + if (cache) { + if (cache.expiry > Date.now()) { + return cache.searches[searchTerm] || null; // ✅ Return if valid + } else { + set((state) => { + const updatedCache = { ...state.searchCache }; + delete updatedCache[listName]; // ✅ Remove expired list + return { searchCache: updatedCache }; + }); + } + } + return null; + }, + + addTemporaryResource: (listName, newResources, customExpiry) => + set((state) => { + const expiry = Date.now() + (customExpiry || 5 * 60 * 1000); + + const existingResources = + state.searchCache[listName]?.temporaryNewResources || []; + + // Merge & remove duplicates, keeping the latest by `created` timestamp + const uniqueResourcesMap = new Map(); + + [...existingResources, ...newResources].forEach((item) => { + const key = `${item.service}-${item.name}-${item.identifier}`; + const existingItem = uniqueResourcesMap.get(key); + + if (!existingItem || item.created > existingItem.created) { + uniqueResourcesMap.set(key, item); } - return []; - }, + }); - markResourceAsDeleted: (item) => - set((state) => { - const now = Date.now(); - const expiry = now + 5 * 60 * 1000; // ✅ Expires in 5 minutes - - // ✅ Remove expired deletions before adding a new one - const validDeletedResources = Object.fromEntries( - Object.entries(state.deletedResources).filter(([_, value]) => value.expiry > now) + return { + searchCache: { + ...state.searchCache, + [listName]: { + ...state.searchCache[listName], + temporaryNewResources: Array.from(uniqueResourcesMap.values()), + expiry, + }, + }, + }; + }), + + getTemporaryResources: (listName: string) => { + const cache = get().searchCache[listName]; + if (cache && cache.expiry > Date.now()) { + const resources = cache.temporaryNewResources || []; + return [...resources].sort((a, b) => b?.created - a?.created); + } + return []; + }, + + markResourceAsDeleted: (item) => + set((state) => { + const now = Date.now(); + const expiry = now + 5 * 60 * 1000; // ✅ Expires in 5 minutes + + // ✅ Remove expired deletions before adding a new one + const validDeletedResources = Object.fromEntries( + Object.entries(state.deletedResources).filter( + ([_, value]) => value.expiry > now + ) + ); + + const key = `${item.service}-${item.name}-${item.identifier}`; + return { + deletedResources: { + ...validDeletedResources, // ✅ Keep only non-expired ones + [key]: { deleted: true, expiry }, + }, + }; + }), + + filterOutDeletedResources: (items) => { + const deletedResources = get().deletedResources; // ✅ Read without modifying store + return items.filter( + (item) => + !deletedResources[`${item.service}-${item.name}-${item.identifier}`] + ); + }, + isListExpired: (listName: string): boolean | string => { + const cache = get().searchCache[listName]; + const isExpired = cache ? cache.expiry <= Date.now() : true; // ✅ Expired if expiry timestamp is in the past + return isExpired === true ? true : cache.searchParamsStringified; + }, + + clearExpiredCache: () => + set((state) => { + const now = Date.now(); + const validSearchCache = Object.fromEntries( + Object.entries(state.searchCache).filter( + ([, value]) => value.expiry > now + ) + ); + return { searchCache: validSearchCache }; + }), + filterSearchCacheItemsByNames: (names) => + set((state) => { + const updatedSearchCache: SearchCache = {}; + + for (const [listName, list] of Object.entries(state.searchCache)) { + const updatedSearches: { [searchTerm: string]: QortalMetadata[] } = {}; + + for (const [term, items] of Object.entries(list.searches)) { + updatedSearches[term] = items.filter( + (item) => !names.includes(item.name) ); - - const key = `${item.service}-${item.name}-${item.identifier}`; - return { - deletedResources: { - ...validDeletedResources, // ✅ Keep only non-expired ones - [key]: { deleted: true, expiry }, - }, - }; - }), - - filterOutDeletedResources: (items) => { - const deletedResources = get().deletedResources; // ✅ Read without modifying store - return items.filter( - (item) => !deletedResources[`${item.service}-${item.name}-${item.identifier}`] - ); - }, - isListExpired: (listName: string): boolean | string => { - const cache = get().searchCache[listName]; - const isExpired = cache ? cache.expiry <= Date.now() : true; // ✅ Expired if expiry timestamp is in the past - return isExpired === true ? true : cache.searchParamsStringified - }, - + } - clearExpiredCache: () => - set((state) => { - const now = Date.now(); - const validSearchCache = Object.fromEntries( - Object.entries(state.searchCache).filter(([, value]) => value.expiry > now) - ); - return { searchCache: validSearchCache }; - }), - filterSearchCacheItemsByNames: (names) => - set((state) => { - const updatedSearchCache: SearchCache = {}; - - for (const [listName, list] of Object.entries(state.searchCache)) { - const updatedSearches: { [searchTerm: string]: QortalMetadata[] } = {}; - - for (const [term, items] of Object.entries(list.searches)) { - updatedSearches[term] = items.filter( - (item) => !names.includes(item.name) - ); + updatedSearchCache[listName] = { + ...list, + searches: updatedSearches, + }; } - updatedSearchCache[listName] = { - ...list, - searches: updatedSearches, - }; - } - - return { searchCache: updatedSearchCache }; - }), + return { searchCache: updatedSearchCache }; }), - -); \ No newline at end of file +}));