diff --git a/src/components/AppImage.tsx b/src/components/AppImage.tsx index 0b944b2..0040831 100644 --- a/src/components/AppImage.tsx +++ b/src/components/AppImage.tsx @@ -2,32 +2,33 @@ import { type FC } from 'react'; import { Link } from 'react-router-dom'; import { cn } from '../utils/cn'; import * as styles from './AppImage.module.css'; -import type { App } from '@/types'; +import type { ComputedApp, ThinApp } from '@/types'; import { NewAppBadge } from './NewAppBadge'; type Props = { className?: string; - app: App; + app: ComputedApp; to?: string; }; export const AppImage: FC = ({ className, app, to }) => { - const url = `https://steamcdn-a.akamaihd.net/steam/apps/${app.id}/header.jpg`; - return to ? (
{app.name}
- - + + ) : (
- - + +
); }; diff --git a/src/components/IconTooltip.tsx b/src/components/IconTooltip.tsx index fa5399e..3f95f21 100644 --- a/src/components/IconTooltip.tsx +++ b/src/components/IconTooltip.tsx @@ -12,7 +12,7 @@ export const IconTooltip: FC = ({ icon: Icon, children, className }) => { return (
diff --git a/src/components/NewAppBadge.tsx b/src/components/NewAppBadge.tsx index 9aa50ed..f2faeb7 100644 --- a/src/components/NewAppBadge.tsx +++ b/src/components/NewAppBadge.tsx @@ -1,11 +1,11 @@ import { useMemo, type FC } from 'react'; -import type { App } from '@/types'; +import type { ComputedApp, ThinApp } from '@/types'; import { BadgePlus } from 'lucide-react'; import { IconTooltip } from './IconTooltip'; import { isAfter, parseISO, subWeeks } from 'date-fns'; type Props = { - app: App; + app: ComputedApp; }; export const NewAppBadge: FC = ({ app }) => { diff --git a/src/pages/apps/SearchPage.tsx b/src/pages/apps/SearchPage.tsx index bf6a438..ed5829e 100644 --- a/src/pages/apps/SearchPage.tsx +++ b/src/pages/apps/SearchPage.tsx @@ -3,14 +3,14 @@ import { useSearch } from '../../context/search'; import { AppImage } from '../../components/AppImage'; import { Button } from '@/components/Button'; import { ButtonGroup } from '@/components/ButtonGroup'; -import type { App } from '@/types'; +import type { ComputedApp, ThinApp } from '@/types'; import { PageSpinner } from '@/components/PageSpinner'; import { SearchService } from '@/service/search.service'; import { ImageService } from '@/service/image.service'; export const Component: FC = () => { const search = useSearch(); - const [filteredApps, setFilteredApps] = useState([]); + const [filteredApps, setFilteredApps] = useState[]>([]); const [loading, setLoading] = useState(true); useEffect(() => { @@ -22,9 +22,12 @@ export const Component: FC = () => { await ImageService.preload(...apps.map((app) => app.image_url)); setFilteredApps(apps); + setLoading(false); }) - .catch(() => console.log('debounced...')) - .finally(() => { + .catch(({ message, debounce }) => { + if (debounce) return; + + console.error(message); setLoading(false); }); }, [search]); diff --git a/src/service/protontweaks.service.ts b/src/service/protontweaks.service.ts index e81d1b1..650fe65 100644 --- a/src/service/protontweaks.service.ts +++ b/src/service/protontweaks.service.ts @@ -1,4 +1,4 @@ -import type { ApiInfo, App, AppsList } from '../types'; +import type { ApiInfo, App, AppsList, ComputedApp, ThinApp } from '../types'; import { fetch } from '../utils/fetch'; export async function getApiInfo() { @@ -18,11 +18,18 @@ export async function getAppRoutes() { export async function getApps() { const appsList = await getAppsList(); - return appsList.apps; + return appsList.apps.map(getComputedApp); } export async function getApp(id: string) { - return await fetch(`https://api.protontweaks.com/v4/${id}.json`); + return getComputedApp(await fetch(`https://api.protontweaks.com/v4/${id}.json`)); +} + +export function getComputedApp(app: T): ComputedApp { + return { + ...app, + image_url: `https://steamcdn-a.akamaihd.net/steam/apps/${app.id}/header.jpg`, + }; } export enum AppSettingStatus { diff --git a/src/service/search.service.ts b/src/service/search.service.ts index a7f807f..394a3ef 100644 --- a/src/service/search.service.ts +++ b/src/service/search.service.ts @@ -1,10 +1,10 @@ -import type { App, ComputedApp } from '@/types'; +import type { ComputedApp, ThinApp } from '@/types'; import SearchWorker from '@/workers/search.worker?worker'; let previousWorker: Worker; export class SearchService { - static async query(value: string): Promise { + static async query(value: string): Promise[]> { if (previousWorker) { previousWorker.terminate(); } @@ -14,13 +14,13 @@ export class SearchService { return new Promise((resolve, reject) => { previousWorker.addEventListener( 'message', - (event: MessageEvent) => { - resolve( - event.data.map((app) => ({ - ...app, - image_url: `https://steamcdn-a.akamaihd.net/steam/apps/${app.id}/header.jpg`, - })) - ); + (event) => { + if (event.data.error) + reject({ + message: event.data.error, + debounce: false, + }); + else resolve(event.data.result); }, { once: true, @@ -31,7 +31,10 @@ export class SearchService { ((terminate) => { previousWorker.terminate = function () { terminate.apply(previousWorker); - reject('Interupting due to new call'); + reject({ + message: 'Interupting due to new call', + debounce: true, + }); }; })(previousWorker.terminate); diff --git a/src/types/apps.ts b/src/types/apps.ts index 5903c46..1edd0aa 100644 --- a/src/types/apps.ts +++ b/src/types/apps.ts @@ -4,9 +4,11 @@ export type ApiInfo = { }; export type AppsList = ApiInfo & { - apps: Pick[]; + apps: ThinApp[]; }; +export type ThinApp = Pick; + export type App = { id: string; name: string; @@ -27,6 +29,6 @@ export type App = { updated_at: string; }; -export type ComputedApp = App & { +export type ComputedApp = T & { image_url: string; }; diff --git a/src/workers/search.worker.ts b/src/workers/search.worker.ts index 9bd445c..f6ea543 100644 --- a/src/workers/search.worker.ts +++ b/src/workers/search.worker.ts @@ -2,9 +2,17 @@ import { getApps } from '@/service/protontweaks.service'; import { delay } from '@ribbon-studios/js-utils'; self.onmessage = async (event) => { - const [filteredApps] = await Promise.all([filterApps(event.data.toLowerCase()), delay(500)]); - - self.postMessage(filteredApps); + try { + const [filteredApps] = await Promise.all([filterApps(event.data.toLowerCase()), delay(500)]); + + self.postMessage({ + result: filteredApps, + }); + } catch { + self.postMessage({ + error: 'Failed to retrieve apps!', + }); + } }; async function filterApps(value: string) {