diff --git a/src/components/phrase-search/index.tsx b/src/components/phrase-search/index.tsx index 15d0a01..7c9e015 100644 --- a/src/components/phrase-search/index.tsx +++ b/src/components/phrase-search/index.tsx @@ -10,7 +10,7 @@ export async function PhraseSearch() { "items/phrases", // Ensure the phrases are cached for a maximum of 30 minutes // Otherwise, they are only fetched when the app is built for the **first time** - { headers: { "Cache-Control": "max-age=1800" } }, + { next: { revalidate: 1800 } }, )); } catch (error) { console.error("Could not load the phrases", error); diff --git a/src/components/phrase-search/letter.tsx b/src/components/phrase-search/letter.tsx index 3c99bab..9360128 100644 --- a/src/components/phrase-search/letter.tsx +++ b/src/components/phrase-search/letter.tsx @@ -18,30 +18,6 @@ import { const PATHS = ["/", "/artists", "/map", "/news"]; -/** Gets the current page and location to display the letter on, based on the hash of the UUID. */ -function getPhraseMetadata(phrase: SearchPhrase, currentIndex: number) { - const hashBase = `${phrase.uuid}:${currentIndex.toString()}:`; - const pathHash = hashString(`${hashBase}path`); - const leftHash = hashString(`${hashBase}leftPercentage`); - const topHash = hashString(`${hashBase}topPercentage`); - // Ensure the index is within the bounds of the PATHS array - const pathIndex = Number(pathHash % BigInt(PATHS.length)); - const page = PATHS[pathIndex]; - const leftPercentage = Number(leftHash % BigInt(1000)) / 1000; - const topPercentage = Number(topHash % BigInt(1000)) / 1000; - return { page, leftPercentage, topPercentage }; -} - -/** Simple hashing function using FNV-1a. Result is a positive BigInt. */ -function hashString(value: string) { - let hash = BigInt("2166136261"); // FNV-1a hash seed (prime number) - for (let index = 0; index < value.length; index++) { - hash ^= BigInt(value.codePointAt(index) ?? 0); - hash *= BigInt("16777619"); // FNV-1a multiplier (prime number) - } - return hash > 0 ? hash : -hash; -} - export function LetterSearch({ phrase, currentIndex, @@ -49,7 +25,9 @@ export function LetterSearch({ phrase: SearchPhrase; currentIndex: number; }) { - const [isClient, setIsClient] = useState(false); + const [leftPercentage, setLeftPercentage] = useState(-1); + const [topPercentage, setTopPercentage] = useState(-1); + const [page, setPage] = useState(""); const [cookies, setCookie] = useCookies< "foundLetters", { foundLetters?: Record } @@ -58,16 +36,19 @@ export function LetterSearch({ const windowDimensions = useWindowDimensions(); useEffect(() => { - setIsClient(true); + // Randomise top, left, and page + setLeftPercentage(Math.random() * 100); + setTopPercentage(Math.random() * 100); + setPage(PATHS[Math.floor(Math.random() * PATHS.length)]); }, [phrase, currentIndex]); - const { page, leftPercentage, topPercentage } = getPhraseMetadata( - phrase, - currentIndex, - ); - // Need to check if rendering on client to prevent hydration error - if (pathname !== page || !isClient) { + if ( + pathname !== page || + leftPercentage === -1 || + topPercentage === -1 || + page === "" + ) { return null; } diff --git a/src/lib/facebook.ts b/src/lib/facebook.ts index dda9398..0024fdb 100644 --- a/src/lib/facebook.ts +++ b/src/lib/facebook.ts @@ -70,7 +70,10 @@ async function fetchFromFacebook( if (fields) { url.searchParams.append("fields", fields); } - const headers: RequestInit["headers"] = {}; + const options: RequestInit = {}; + // Can't define options.headers and options.next immediately when declaring `options` because TypeScript later complains they may be undefined + options.headers = {}; + options.next = {}; if (useAuthentication) { getAccessTokenPromise = getAccessToken(); const token = await getAccessTokenPromise; @@ -78,13 +81,14 @@ async function fetchFromFacebook( console.error("No long-lived access token set for Facebook API"); return null; } - - headers.Authorization = `Bearer ${token}`; + // Cache responses for posts, user data etc. for 30 minutes + options.next.revalidate = 1800; + options.headers.Authorization = `Bearer ${token}`; } else { - // This prevents Next.js from caching and reusing a stale access token - headers["Cache-Control"] = "no-store"; + // Don't cache responses for generating access tokens + options.next.revalidate = 0; } - const response = await fetch(url.toString(), { headers }); + const response = await fetch(url.toString(), options); const data = (await response.json()) as T; if (!response.ok) { console.error(