diff --git a/src/lib/components/game/index.svelte b/src/lib/components/game/index.svelte index 335cf58..7ac65bb 100644 --- a/src/lib/components/game/index.svelte +++ b/src/lib/components/game/index.svelte @@ -11,6 +11,8 @@ import { addToCollection, removeFromCollection } from '$lib/util/manipulateCollection'; import { addToWishlist } from '$lib/util/manipulateWishlist'; import { browser } from '$app/environment'; + import { binarySearchOnStore } from '$root/lib/util/binarySearchOnStore'; + import { convertToSavedGame } from '$root/lib/util/gameMapper'; export let game: GameType | SavedGameType; export let detailed: boolean = false; @@ -29,7 +31,8 @@ if (existsInCollection) { removeGameFromCollection(); } else { - addToCollection(game); + let index = binarySearchOnStore($collectionStore, convertToSavedGame(game), 'en'); + addToCollection(game, index); if (browser) { localStorage.collection = JSON.stringify($collectionStore); } diff --git a/src/lib/stores/collectionStore.ts b/src/lib/stores/collectionStore.ts index fceee99..86a69ce 100644 --- a/src/lib/stores/collectionStore.ts +++ b/src/lib/stores/collectionStore.ts @@ -15,6 +15,13 @@ const state = () => { update((store) => [...store, game]); } + function addSorted(game: SavedGameType, index: number) { + update((store) => { + store.splice(index, 0, game); + return store; + }); + } + function remove(id: string) { update((store) => { const newStore = store.filter((item: SavedGameType) => item.id !== id); @@ -28,7 +35,7 @@ const state = () => { }); } - return { subscribe, set, update, add, addAll, remove, removeAll }; + return { subscribe, set, update, add, addSorted, addAll, remove, removeAll }; }; export const collectionStore = state(); diff --git a/src/lib/util/binarySearchOnStore.ts b/src/lib/util/binarySearchOnStore.ts new file mode 100644 index 0000000..562843e --- /dev/null +++ b/src/lib/util/binarySearchOnStore.ts @@ -0,0 +1,25 @@ +import type { SavedGameType } from "../types"; + +export function binarySearchOnStore(inputArray: SavedGameType[], item: SavedGameType, locale = 'en') { + const collator = Intl.Collator(locale) + let low = 0; + let high = inputArray?.length - 1; + let mid; + let comparison; + + while (low <= high) { + mid = (low + high) >>> 1; /* equivalent to Math.floor((low + hight) / 2) but faster */ + const midValue = inputArray[mid]; + comparison = collator.compare(midValue?.name, item?.name); + + if (comparison < 0) { + low = mid + 1; + } else if (comparison > 0) { + high = mid - 1; + } else { + return mid; + } + } + + return low; +} \ No newline at end of file diff --git a/src/lib/util/manipulateCollection.ts b/src/lib/util/manipulateCollection.ts index 4ba80e9..51e5649 100644 --- a/src/lib/util/manipulateCollection.ts +++ b/src/lib/util/manipulateCollection.ts @@ -4,11 +4,15 @@ import { ToastType, type GameType, type SavedGameType } from '$lib/types'; import { convertToSavedGame } from './gameMapper'; import { saved_game_schema } from '../zodValidation'; -export function addToCollection(game: GameType | SavedGameType) { +export function addToCollection(game: GameType | SavedGameType, index: number) { try { console.log(`Saving game: ${JSON.stringify(game)}`); saved_game_schema.parse(game); - collectionStore.add(convertToSavedGame(game)); + if (index === -1) { + collectionStore.add(convertToSavedGame(game)); + } else { + collectionStore.addSorted(convertToSavedGame(game), index); + } toast.send('Added to collection', { duration: 3000, type: ToastType.INFO }); } catch (error) { console.log(error); diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte index 40b2304..0f926ea 100644 --- a/src/routes/+layout.svelte +++ b/src/routes/+layout.svelte @@ -34,11 +34,14 @@ $: isOpen = $boredState?.dialog?.isOpen; if (browser) { + const collator = new Intl.Collator('en'); + let collectionEmpty = $collectionStore.length === 0 || false; let wishlistEmpty = $wishlistStore.length === 0 || false; if (wishlistEmpty && localStorage?.wishlist && localStorage?.wishlist?.length !== 0) { const wishlist: SavedGameType[] = JSON.parse(localStorage.wishlist); if (wishlist?.length !== 0) { + wishlist.sort((a, b) => collator.compare(a.name, b.name)); for (const item of wishlist) { if (!item?.searchTerms) { item.searchTerms = `${item?.name?.toLowerCase()}`; @@ -53,6 +56,7 @@ if (collectionEmpty && localStorage?.collection && localStorage?.collection?.length !== 0) { const collection: SavedGameType[] = JSON.parse(localStorage.collection); if (collection?.length !== 0) { + collection.sort((a, b) => collator.compare(a.name, b.name)); for (const item of collection) { if (!item?.searchTerms) { item.searchTerms = `${item?.name?.toLowerCase()}`; diff --git a/src/routes/collection/+page.server.ts b/src/routes/collection/+page.server.ts index 02aeef0..741b409 100644 --- a/src/routes/collection/+page.server.ts +++ b/src/routes/collection/+page.server.ts @@ -1,3 +1,5 @@ +import type { PageServerLoad } from "../$types"; + export const load: PageServerLoad = async ({ fetch, url }) => { const searchParams = Object.fromEntries(url?.searchParams); const q = searchParams?.q; diff --git a/src/routes/collection/+page.svelte b/src/routes/collection/+page.svelte index ae89c03..a97a4d3 100644 --- a/src/routes/collection/+page.svelte +++ b/src/routes/collection/+page.svelte @@ -27,7 +27,7 @@ }); $: skip = (page - 1) * pageSize; - $: gamesShown = $searchStore.filtered.slice(skip, skip + pageSize); + $: gamesShown = $searchStore.data.slice(skip, skip + pageSize); $: totalItems = $searchStore.search === '' ? $collectionStore.length : $searchStore.filtered.length; interface RemoveGameEvent extends Event { diff --git a/src/routes/game/[id]/+page.svelte b/src/routes/game/[id]/+page.svelte index 3e4045d..0225fa9 100644 --- a/src/routes/game/[id]/+page.svelte +++ b/src/routes/game/[id]/+page.svelte @@ -23,6 +23,8 @@ import LinkWithIcon from '$root/lib/components/LinkWithIcon.svelte'; import { addToWishlist } from '$root/lib/util/manipulateWishlist'; import RemoveWishlistDialog from '$root/lib/components/dialog/RemoveWishlistDialog.svelte'; + import { binarySearchOnStore } from '$root/lib/util/binarySearchOnStore'; + import { convertToSavedGame } from '$root/lib/util/gameMapper'; $: existsInCollection = $collectionStore.find((item: SavedGameType) => item.id === game.id); $: existsInWishlist = $wishlistStore.find((item: SavedGameType) => item.id === game.id); @@ -41,11 +43,13 @@ firstParagraphEnd = game?.description?.indexOf('') + 5; } - function onCollectionClick() { + function onCollectionClick() { if (existsInCollection) { removeFromCollection(); } else { - addToCollection(game); + let index = binarySearchOnStore($collectionStore, convertToSavedGame(game), 'en'); + console.log(`Binary index: ${index}`) + addToCollection(game, index); if (browser) { localStorage.collection = JSON.stringify($collectionStore); }