Skip to content

Commit

Permalink
fix: loading spinner not appearing on initial load
Browse files Browse the repository at this point in the history
- (protontweaks) calculate image_url when the request occurs
  • Loading branch information
cecilia-sanare committed Sep 1, 2024
1 parent 31ef2be commit 06ad3a8
Show file tree
Hide file tree
Showing 8 changed files with 57 additions and 33 deletions.
17 changes: 9 additions & 8 deletions src/components/AppImage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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<ThinApp>;
to?: string;
};

export const AppImage: FC<Props> = ({ className, app, to }) => {
const url = `https://steamcdn-a.akamaihd.net/steam/apps/${app.id}/header.jpg`;

return to ? (
<Link className={cn(styles.appImage, 'group', className)} to={to}>
<NewAppBadge app={app} />
<div className="absolute inset-0 overflow-hidden flex flex-col justify-end">
<div className={styles.name}>{app.name}</div>
</div>
<img src={url} />
<img className="blur-none group-hover:blur group-focus:blur absolute inset-0 -z-10 transition-all" src={url} />
<img src={app.image_url} />
<img
className="blur-none group-hover:blur group-focus:blur absolute inset-0 -z-10 transition-all"
src={app.image_url}
/>
</Link>
) : (
<div className={cn(styles.appImage, className)}>
<NewAppBadge app={app} />
<img src={url} />
<img className="blur-sm opacity-80 absolute inset-0 -z-10 transition-all" src={url} />
<img src={app.image_url} />
<img className="blur-sm opacity-80 absolute inset-0 -z-10 transition-all" src={app.image_url} />
</div>
);
};
2 changes: 1 addition & 1 deletion src/components/IconTooltip.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export const IconTooltip: FC<Props> = ({ icon: Icon, children, className }) => {
return (
<div
className={cn(
'relative flex gap-2 p-1 pl-2 bg-blue-700/90 rounded-full transition-all group overflow-hidden z-10 font-bold italic',
'relative flex gap-2 p-1 pl-2 bg-blue-700/90 rounded-md transition-all group overflow-hidden z-10 font-bold italic',
className
)}
>
Expand Down
4 changes: 2 additions & 2 deletions src/components/NewAppBadge.tsx
Original file line number Diff line number Diff line change
@@ -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<ThinApp>;
};

export const NewAppBadge: FC<Props> = ({ app }) => {
Expand Down
11 changes: 7 additions & 4 deletions src/pages/apps/SearchPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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<App[]>([]);
const [filteredApps, setFilteredApps] = useState<ComputedApp<ThinApp>[]>([]);
const [loading, setLoading] = useState(true);

useEffect(() => {
Expand All @@ -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]);
Expand Down
13 changes: 10 additions & 3 deletions src/service/protontweaks.service.ts
Original file line number Diff line number Diff line change
@@ -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() {
Expand All @@ -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<App>(`https://api.protontweaks.com/v4/${id}.json`);
return getComputedApp(await fetch<App>(`https://api.protontweaks.com/v4/${id}.json`));
}

export function getComputedApp<T extends ThinApp>(app: T): ComputedApp<T> {
return {
...app,
image_url: `https://steamcdn-a.akamaihd.net/steam/apps/${app.id}/header.jpg`,
};
}

export enum AppSettingStatus {
Expand Down
23 changes: 13 additions & 10 deletions src/service/search.service.ts
Original file line number Diff line number Diff line change
@@ -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<ComputedApp[]> {
static async query(value: string): Promise<ComputedApp<ThinApp>[]> {
if (previousWorker) {
previousWorker.terminate();
}
Expand All @@ -14,13 +14,13 @@ export class SearchService {
return new Promise((resolve, reject) => {
previousWorker.addEventListener(
'message',
(event: MessageEvent<App[]>) => {
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,
Expand All @@ -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);

Expand Down
6 changes: 4 additions & 2 deletions src/types/apps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@ export type ApiInfo = {
};

export type AppsList = ApiInfo & {
apps: Pick<App, 'id' | 'name'>[];
apps: ThinApp[];
};

export type ThinApp = Pick<App, 'id' | 'name' | 'created_at' | 'updated_at'>;

export type App = {
id: string;
name: string;
Expand All @@ -27,6 +29,6 @@ export type App = {
updated_at: string;
};

export type ComputedApp = App & {
export type ComputedApp<T extends ThinApp = App> = T & {
image_url: string;
};
14 changes: 11 additions & 3 deletions src/workers/search.worker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down

0 comments on commit 06ad3a8

Please sign in to comment.