Skip to content

Commit

Permalink
fix: move from hash-based to random generation
Browse files Browse the repository at this point in the history
  • Loading branch information
kguzek committed Jan 2, 2025
1 parent 408d4a5 commit 1d238cd
Show file tree
Hide file tree
Showing 3 changed files with 24 additions and 39 deletions.
2 changes: 1 addition & 1 deletion src/components/phrase-search/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
45 changes: 13 additions & 32 deletions src/components/phrase-search/letter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,38 +18,16 @@ 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,
}: {
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<SearchPhrase["uuid"], number[]> }
Expand All @@ -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;
}

Expand Down
16 changes: 10 additions & 6 deletions src/lib/facebook.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,21 +70,25 @@ async function fetchFromFacebook<T>(
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;
if (token == null || token === "") {
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(
Expand Down

0 comments on commit 1d238cd

Please sign in to comment.