+WruS(W!rfT0=WXt;L@KT
zAbBy@lGY=%pL#2MWe7c>VSn21*?R=yFD@(+N
z&^j(s^Sm*p{Au6BdnG64!40DHL%!wapl=n&^~Ow!6KSPj@2tG!t^K`~8=H`xXV04|
zb(MN9Ju8GKDe5aX%m$n-o+z;zUc{a$B(AS5+stg
z9=K0FF+=fO8^SSoPhv
zDm6X-dE$9iAYsCfy7GVd!Gj~Dk
zV7;n`f&9BH8*|tfL|u6L{*cqH4XR|JyqEemkwS&ixl^c>hhWdQk
zD3NMn^;}dMR{>wthf|JtjI~#V?s~7z>2Xw)NsLbj(4%~Y1HG|@=N3l6SAM1-GS5!!
zWZhLDPJY>VR=8t%L_}UE=|C&59y044a6J*DLG+rS%#{^=q3U{FyJF&{boE480=5X6Bt)*7JNH&JV`}DnKUoI>I{#sOSjTzHE#$==9T_pA{sZF1Vl#MWxD4RmQ<-u(
zAvP7>?tiP}MiEm0+5(5hn1eV=ix$|nAu+Q3hdQ3D7zO~^+h<%q_(`1d{vqY%K>~tV
zp$zfSP1zh?Xjq78Fzpm8D2x+n`rj)z#iA`}G#p5$V}h~R{fzw>kair4Mw?rhnFm=0
b
Date: Thu, 12 Feb 2026 19:17:41 -0800
Subject: [PATCH 2/7] fixing rollouts
---
.../rollouts/checkFeatureAvailability.tsx | 10 +++--
.../ClientTimeProtectedDisplay.tsx | 30 +++-----------
app/(pages)/_hooks/useFeatureAvailability.ts | 39 ++++++++++++-------
package-lock.json | 6 +--
4 files changed, 39 insertions(+), 46 deletions(-)
diff --git a/app/(api)/_actions/rollouts/checkFeatureAvailability.tsx b/app/(api)/_actions/rollouts/checkFeatureAvailability.tsx
index 01b261b98..da1bd7d7a 100644
--- a/app/(api)/_actions/rollouts/checkFeatureAvailability.tsx
+++ b/app/(api)/_actions/rollouts/checkFeatureAvailability.tsx
@@ -15,8 +15,8 @@ export default async function checkFeatureAvailability(component_key: string) {
const rollout: Rollout = rolloutRes.body;
- const startTime = rollout.rollout_time;
- const endTime = rollout.rollback_time || Infinity;
+ const startTime = Number(rollout.rollout_time);
+ const endTime = Number(rollout.rollback_time) || Infinity;
const now = Date.now();
const available = startTime <= now && now <= endTime;
@@ -24,7 +24,11 @@ export default async function checkFeatureAvailability(component_key: string) {
ok: true,
body: {
available,
- rollout,
+ rollout: {
+ ...rollout,
+ rollout_time: startTime,
+ rollback_time: endTime === Infinity ? null : endTime,
+ },
},
error: null,
};
diff --git a/app/(pages)/_components/TimeProtectedDisplay/ClientTimeProtectedDisplay.tsx b/app/(pages)/_components/TimeProtectedDisplay/ClientTimeProtectedDisplay.tsx
index 72f62ed29..396b18a24 100644
--- a/app/(pages)/_components/TimeProtectedDisplay/ClientTimeProtectedDisplay.tsx
+++ b/app/(pages)/_components/TimeProtectedDisplay/ClientTimeProtectedDisplay.tsx
@@ -6,7 +6,6 @@ import { useFeatureAvailability } from '@pages/_hooks/useFeatureAvailability';
export default function ClientTimeProtectedDisplay({
featureId,
fallback = null,
- callback = () => {},
children,
}: {
featureId: string;
@@ -14,14 +13,11 @@ export default function ClientTimeProtectedDisplay({
callback?: () => void;
children: React.ReactNode;
}) {
- const { ok, loading, available, rollout, error, fetchAvailability } =
+ const { loading, available, rollout, fetchAvailability } =
useFeatureAvailability(featureId);
- if (loading) {
- return 'loading...';
- }
- if (!ok) {
- return JSON.stringify(error);
+ if (loading && !rollout) {
+ return 'Loading...';
}
if (!available) {
@@ -30,27 +26,11 @@ export default function ClientTimeProtectedDisplay({
{fallback}
{
- callback?.();
- fetchAvailability(featureId);
- }}
+ callback={() => fetchAvailability(featureId)}
/>
>
);
}
- return (
- <>
- {children}
- {rollout.rollback_time && (
- {
- callback?.();
- fetchAvailability(featureId);
- }}
- />
- )}
- >
- );
+ return <>{children}>;
}
diff --git a/app/(pages)/_hooks/useFeatureAvailability.ts b/app/(pages)/_hooks/useFeatureAvailability.ts
index 7ec524ad5..96e3493cb 100644
--- a/app/(pages)/_hooks/useFeatureAvailability.ts
+++ b/app/(pages)/_hooks/useFeatureAvailability.ts
@@ -1,26 +1,35 @@
import checkFeatureAvailability from '@actions/rollouts/checkFeatureAvailability';
-import { useEffect, useState } from 'react';
+import { useEffect, useState, useCallback, useRef } from 'react';
export function useFeatureAvailability(featureId: string) {
const [loading, setLoading] = useState(true);
- const [ok, setOk] = useState(true);
- const [error, setError] = useState(null);
- const [available, setAvailable] = useState(null);
+ const [available, setAvailable] = useState(false);
const [rollout, setRollout] = useState(null);
- const fetchAvailability = async (featureId: string) => {
- setLoading(true);
- const { ok, body, error } = await checkFeatureAvailability(featureId);
- setOk(ok);
- setAvailable(body?.available);
- setRollout(body?.rollout);
- setError(error);
- setLoading(false);
- };
+ const lastFetchedId = useRef(null);
+
+ const fetchAvailability = useCallback(
+ async (id: string) => {
+ if (lastFetchedId.current === id && rollout !== null) return;
+
+ setLoading(true);
+ try {
+ const { ok, body } = await checkFeatureAvailability(id);
+ if (ok && body) {
+ setAvailable(body.available);
+ setRollout(body.rollout);
+ lastFetchedId.current = id;
+ }
+ } finally {
+ setLoading(false);
+ }
+ },
+ [rollout]
+ );
useEffect(() => {
fetchAvailability(featureId);
- }, [featureId]);
+ }, [featureId, fetchAvailability]);
- return { ok, loading, available, rollout, error, fetchAvailability };
+ return { loading, available, rollout, fetchAvailability };
}
diff --git a/package-lock.json b/package-lock.json
index ce510424c..89b602b6f 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -6549,9 +6549,9 @@
}
},
"node_modules/caniuse-lite": {
- "version": "1.0.30001734",
- "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001734.tgz",
- "integrity": "sha512-uhE1Ye5vgqju6OI71HTQqcBCZrvHugk0MjLak7Q+HfoBgoq5Bi+5YnwjP4fjDgrtYr/l8MVRBvzz9dPD4KyK0A==",
+ "version": "1.0.30001769",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001769.tgz",
+ "integrity": "sha512-BCfFL1sHijQlBGWBMuJyhZUhzo7wer5sVj9hqekB/7xn0Ypy+pER/edCYQm4exbXj4WiySGp40P8UuTh6w1srg==",
"funding": [
{
"type": "opencollective",
From 58e1ef428f12aa62ccdaaae3fbd71a236017570b Mon Sep 17 00:00:00 2001
From: michelleyeoh
Date: Thu, 12 Feb 2026 19:34:28 -0800
Subject: [PATCH 3/7] fixed rollouts rerendering
---
app/(pages)/_hooks/useTimeTrigger.ts | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/app/(pages)/_hooks/useTimeTrigger.ts b/app/(pages)/_hooks/useTimeTrigger.ts
index fc91db3e4..04fe61b18 100644
--- a/app/(pages)/_hooks/useTimeTrigger.ts
+++ b/app/(pages)/_hooks/useTimeTrigger.ts
@@ -35,7 +35,8 @@ export function useTimeTrigger(triggerTime: number, callback: any) {
clearTimeout(timerRef.current);
window.removeEventListener('focus', onFocus);
};
- }, [triggerTime, callback, router]);
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, [triggerTime, router]);
return { triggered };
}
From b3a3deca1c5a6bbdeb4d8f4ac5681e6e39a3d1d3 Mon Sep 17 00:00:00 2001
From: michelleyeoh
Date: Thu, 12 Feb 2026 22:36:28 -0800
Subject: [PATCH 4/7] merge branch main
---
app/_data/tracks.ts | 11 -----------
1 file changed, 11 deletions(-)
diff --git a/app/_data/tracks.ts b/app/_data/tracks.ts
index 7980d1393..689503a6f 100644
--- a/app/_data/tracks.ts
+++ b/app/_data/tracks.ts
@@ -140,17 +140,6 @@ const nonHDTracks: Tracks = {
};
const automaticTracks: Tracks = {
- "Hacker's Choice Award": {
- name: "Hacker's Choice Award",
- filter: 'General',
- prizes: ['HackDavis Swag Bag'],
- images: [hdSwag],
- eligibility_criteria:
- 'Awarded to the project with the most votes from our 2026 hackers. All entries are automatically considered for this prize category. Vote for any project but your own!',
- },
-};
-
-const optedHDTracks: Tracks = {
'Best Hack for Social Good': {
name: 'Best Hack for Social Good',
filter: 'General',
From d07a9690ba7d12bb787a7fc59e72814e7ec3f185 Mon Sep 17 00:00:00 2001
From: michelleyeoh
Date: Fri, 13 Feb 2026 22:04:32 -0800
Subject: [PATCH 5/7] fixed font weight
---
.../ProjectInfo/WhatHappens/WhatHappens.module.scss | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/app/(pages)/(hackers)/_components/ProjectInfo/WhatHappens/WhatHappens.module.scss b/app/(pages)/(hackers)/_components/ProjectInfo/WhatHappens/WhatHappens.module.scss
index 79c3afa95..1afcab511 100644
--- a/app/(pages)/(hackers)/_components/ProjectInfo/WhatHappens/WhatHappens.module.scss
+++ b/app/(pages)/(hackers)/_components/ProjectInfo/WhatHappens/WhatHappens.module.scss
@@ -31,8 +31,8 @@
}
}
- > h2 {
- font-weight: bold;
+ h2 {
+ font-weight: 700;
padding-bottom: 32px;
color: var(--text-dark);
@@ -54,7 +54,7 @@
background: none;
color: var(--background-secondary);
border-radius: 8px;
- font-weight: 500;
+ font-weight: 600;
margin-right: 1%;
border: 1.5px solid var(--background-secondary);
From e07fceea37ae11003cca9b69d714ac461361037d Mon Sep 17 00:00:00 2001
From: michelleyeoh
Date: Fri, 13 Feb 2026 22:32:12 -0800
Subject: [PATCH 6/7] revered parts and updated callbacks
---
.../rollouts/checkFeatureAvailability.tsx | 10 ++-----
.../ClientTimeProtectedDisplay.tsx | 28 +++++++++++++++++--
app/(pages)/_hooks/useFeatureAvailability.ts | 15 +++++++---
app/(pages)/_hooks/useTimeTrigger.ts | 10 +++++--
4 files changed, 46 insertions(+), 17 deletions(-)
diff --git a/app/(api)/_actions/rollouts/checkFeatureAvailability.tsx b/app/(api)/_actions/rollouts/checkFeatureAvailability.tsx
index da1bd7d7a..01b261b98 100644
--- a/app/(api)/_actions/rollouts/checkFeatureAvailability.tsx
+++ b/app/(api)/_actions/rollouts/checkFeatureAvailability.tsx
@@ -15,8 +15,8 @@ export default async function checkFeatureAvailability(component_key: string) {
const rollout: Rollout = rolloutRes.body;
- const startTime = Number(rollout.rollout_time);
- const endTime = Number(rollout.rollback_time) || Infinity;
+ const startTime = rollout.rollout_time;
+ const endTime = rollout.rollback_time || Infinity;
const now = Date.now();
const available = startTime <= now && now <= endTime;
@@ -24,11 +24,7 @@ export default async function checkFeatureAvailability(component_key: string) {
ok: true,
body: {
available,
- rollout: {
- ...rollout,
- rollout_time: startTime,
- rollback_time: endTime === Infinity ? null : endTime,
- },
+ rollout,
},
error: null,
};
diff --git a/app/(pages)/_components/TimeProtectedDisplay/ClientTimeProtectedDisplay.tsx b/app/(pages)/_components/TimeProtectedDisplay/ClientTimeProtectedDisplay.tsx
index 396b18a24..1d8376563 100644
--- a/app/(pages)/_components/TimeProtectedDisplay/ClientTimeProtectedDisplay.tsx
+++ b/app/(pages)/_components/TimeProtectedDisplay/ClientTimeProtectedDisplay.tsx
@@ -6,6 +6,7 @@ import { useFeatureAvailability } from '@pages/_hooks/useFeatureAvailability';
export default function ClientTimeProtectedDisplay({
featureId,
fallback = null,
+ callback = () => {},
children,
}: {
featureId: string;
@@ -13,24 +14,45 @@ export default function ClientTimeProtectedDisplay({
callback?: () => void;
children: React.ReactNode;
}) {
- const { loading, available, rollout, fetchAvailability } =
+ const { loading, available, rollout, error, fetchAvailability } =
useFeatureAvailability(featureId);
+ // initial loading state
if (loading && !rollout) {
return 'Loading...';
}
+ // error or no rollout info, then don't render 24 hr timer
+ if (error || !rollout) {
+ return <>{fallback}>;
+ }
+
+ const handleTrigger = async () => {
+ await fetchAvailability(featureId, true);
+ callback?.();
+ };
+
if (!available) {
return (
<>
{fallback}
fetchAvailability(featureId)}
+ callback={handleTrigger}
/>
>
);
}
- return <>{children}>;
+ return (
+ <>
+ {children}
+ {rollout.rollback_time && (
+
+ )}
+ >
+ );
}
diff --git a/app/(pages)/_hooks/useFeatureAvailability.ts b/app/(pages)/_hooks/useFeatureAvailability.ts
index 96e3493cb..5e5ed697b 100644
--- a/app/(pages)/_hooks/useFeatureAvailability.ts
+++ b/app/(pages)/_hooks/useFeatureAvailability.ts
@@ -3,23 +3,30 @@ import { useEffect, useState, useCallback, useRef } from 'react';
export function useFeatureAvailability(featureId: string) {
const [loading, setLoading] = useState(true);
+ const [error, setError] = useState(null);
const [available, setAvailable] = useState(false);
const [rollout, setRollout] = useState(null);
const lastFetchedId = useRef(null);
const fetchAvailability = useCallback(
- async (id: string) => {
- if (lastFetchedId.current === id && rollout !== null) return;
+ async (id: string, force = false) => {
+ if (!force && lastFetchedId.current === id && rollout !== null) return;
setLoading(true);
+ setError(null);
try {
- const { ok, body } = await checkFeatureAvailability(id);
+ const { ok, body, error } = await checkFeatureAvailability(id);
if (ok && body) {
setAvailable(body.available);
setRollout(body.rollout);
lastFetchedId.current = id;
+ } else {
+ setError(error);
}
+ } catch (err: any) {
+ console.error('Failed to fetch feature availability:', err);
+ setError(err);
} finally {
setLoading(false);
}
@@ -31,5 +38,5 @@ export function useFeatureAvailability(featureId: string) {
fetchAvailability(featureId);
}, [featureId, fetchAvailability]);
- return { loading, available, rollout, fetchAvailability };
+ return { loading, available, rollout, error, fetchAvailability };
}
diff --git a/app/(pages)/_hooks/useTimeTrigger.ts b/app/(pages)/_hooks/useTimeTrigger.ts
index 04fe61b18..8aaeca611 100644
--- a/app/(pages)/_hooks/useTimeTrigger.ts
+++ b/app/(pages)/_hooks/useTimeTrigger.ts
@@ -4,9 +4,14 @@ import { useRouter } from 'next/navigation';
export function useTimeTrigger(triggerTime: number, callback: any) {
const timerRef = useRef(null);
+ const callbackRef = useRef(callback);
const [triggered, setTriggered] = useState(false);
const router = useRouter();
+ useEffect(() => {
+ callbackRef.current = callback;
+ }, [callback]);
+
useEffect(() => {
const updateTimer = () => {
if (timerRef.current) {
@@ -18,7 +23,7 @@ export function useTimeTrigger(triggerTime: number, callback: any) {
return;
}
timerRef.current = setTimeout(async () => {
- await callback?.();
+ await callbackRef.current?.();
router.refresh();
setTriggered(true);
}, timeToTrigger);
@@ -32,10 +37,9 @@ export function useTimeTrigger(triggerTime: number, callback: any) {
updateTimer();
return () => {
- clearTimeout(timerRef.current);
+ if (timerRef.current) clearTimeout(timerRef.current);
window.removeEventListener('focus', onFocus);
};
- // eslint-disable-next-line react-hooks/exhaustive-deps
}, [triggerTime, router]);
return { triggered };
From 7eb17a4e0385e76abffc190f4ccca741dd804ca6 Mon Sep 17 00:00:00 2001
From: michelleyeoh
Date: Fri, 13 Feb 2026 22:44:08 -0800
Subject: [PATCH 7/7] error
---
.../TimeProtectedDisplay/ClientTimeProtectedDisplay.tsx | 2 +-
app/(pages)/_hooks/useFeatureAvailability.ts | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/app/(pages)/_components/TimeProtectedDisplay/ClientTimeProtectedDisplay.tsx b/app/(pages)/_components/TimeProtectedDisplay/ClientTimeProtectedDisplay.tsx
index 1d8376563..cdbca2117 100644
--- a/app/(pages)/_components/TimeProtectedDisplay/ClientTimeProtectedDisplay.tsx
+++ b/app/(pages)/_components/TimeProtectedDisplay/ClientTimeProtectedDisplay.tsx
@@ -19,7 +19,7 @@ export default function ClientTimeProtectedDisplay({
// initial loading state
if (loading && !rollout) {
- return 'Loading...';
+ return 'loading...';
}
// error or no rollout info, then don't render 24 hr timer
diff --git a/app/(pages)/_hooks/useFeatureAvailability.ts b/app/(pages)/_hooks/useFeatureAvailability.ts
index 5e5ed697b..7495694ee 100644
--- a/app/(pages)/_hooks/useFeatureAvailability.ts
+++ b/app/(pages)/_hooks/useFeatureAvailability.ts
@@ -26,7 +26,7 @@ export function useFeatureAvailability(featureId: string) {
}
} catch (err: any) {
console.error('Failed to fetch feature availability:', err);
- setError(err);
+ setError(err instanceof Error ? err.message : String(err));
} finally {
setLoading(false);
}