Skip to content

Commit

Permalink
roar
Browse files Browse the repository at this point in the history
  • Loading branch information
stopachka committed Feb 12, 2025
1 parent 1cc500a commit d0843ed
Show file tree
Hide file tree
Showing 5 changed files with 297 additions and 182 deletions.
2 changes: 1 addition & 1 deletion client/packages/core/src/Reactor.js
Original file line number Diff line number Diff line change
Expand Up @@ -948,7 +948,7 @@ export default class Reactor {

shutdown() {
this._isShutdown = true;
this._ws.close();
this._ws?.close();
}

/**
Expand Down
29 changes: 29 additions & 0 deletions client/www/components/clientOnlyPage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { useIsHydrated } from '@/lib/hooks/useIsHydrated';
import { NextRouter, useRouter } from 'next/router';

export function useReadyRouter(): Omit<NextRouter, 'isReady'> {
const router = useRouter();
if (!router.isReady) {
throw new Error(
'Router wasn not ready. Make sure to call this hook somewhere inside an `asClientOnlyPage` component',
);
}
return router;
}

export function asClientOnlyPage<Props extends JSX.IntrinsicAttributes>(
Component: React.ComponentType<Props>,
) {
return function ClientOnlyPage(props: Props) {
const isHydrated = useIsHydrated();
const router = useRouter();
if (!isHydrated) {
return null;
}
if (!router.isReady) {
return null;
}

return <Component {...props} />;
};
}
66 changes: 66 additions & 0 deletions client/www/lib/hooks/useDashFetch.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { useContext, useEffect, useMemo } from 'react';
import { APIResponse, useAuthedFetch } from '../auth';
import config from '../config';
import { DashResponse } from '../types';
import { TokenContext } from '../contexts';
import useLocalStorage from './useLocalStorage';
import { subDays } from 'date-fns';

// FNV-1a algorithm
function stringHash(input: string) {
let hash = 0x811c9dc5; // FNV offset basis (32 bit)
for (let i = 0; i < input.length; i++) {
hash ^= input.charCodeAt(i);
hash +=
(hash << 1) + (hash << 4) + (hash << 7) + (hash << 8) + (hash << 24);
hash = hash >>> 0; // Convert to unsigned 32-bit after each iteration
}
return hash.toString(16);
}

type CachedEntry<T> = {
item: T;
updatedAt: number;
};

export type CachedAPIResponse<T> = APIResponse<T> & { fromCache?: boolean };

export function useDashFetch(): CachedAPIResponse<DashResponse> {
const now = new Date();
const oneWeekAgo = subDays(now, 7).getTime();

const token = useContext(TokenContext);
const [cachedEntry, setCachedEntry] = useLocalStorage<
CachedEntry<DashResponse> | undefined
>(`dash:${stringHash(token || 'unk')}`, undefined);

const item =
cachedEntry && cachedEntry.updatedAt > oneWeekAgo
? cachedEntry.item
: undefined;

const dashResponse = useAuthedFetch<DashResponse>(`${config.apiURI}/dash`);
useEffect(() => {
if (dashResponse.data) {
setCachedEntry({
item: dashResponse.data,
updatedAt: Date.now(),
});
return;
}
if (dashResponse.error) {
setCachedEntry(undefined);
}
}, [dashResponse.data, dashResponse.error]);

if (dashResponse.isLoading && cachedEntry && item) {
return {
...dashResponse,
isLoading: false,
data: item,
fromCache: true,
};
}

return dashResponse;
}
Loading

0 comments on commit d0843ed

Please sign in to comment.