AI_Agent_Final / web /src /lib /reviewStar.ts
SarahXia0405's picture
Create reviewStar.ts
c6a412f verified
// web/src/lib/reviewStar.ts
export type ReviewEventType = "send_message" | "review_topic" | "review_all";
export type ReviewStarState = {
lastActiveDay: string; // YYYY-MM-DD (local)
todayCount: number; // today's valid review actions count
streakDays: number; // consecutive active days
totalDaysActive: number; // total active days
};
function dayKeyLocal(d = new Date()) {
const yyyy = d.getFullYear();
const mm = String(d.getMonth() + 1).padStart(2, "0");
const dd = String(d.getDate()).padStart(2, "0");
return `${yyyy}-${mm}-${dd}`;
}
function yesterdayKeyLocal() {
return dayKeyLocal(new Date(Date.now() - 86400000));
}
export function loadReviewStar(key: string): ReviewStarState | null {
try {
const raw = localStorage.getItem(key);
if (!raw) return null;
return JSON.parse(raw) as ReviewStarState;
} catch {
return null;
}
}
export function saveReviewStar(key: string, state: ReviewStarState) {
localStorage.setItem(key, JSON.stringify(state));
}
/**
* Called when user enters Review section (or on workspace switch while in Review):
* If day changed, reset today's count to 0 (i.e., dim star until there's real review activity).
*/
export function normalizeToday(key: string): ReviewStarState | null {
const cur = loadReviewStar(key);
if (!cur) return null;
const today = dayKeyLocal();
if (cur.lastActiveDay === today) return cur;
const next: ReviewStarState = { ...cur, todayCount: 0 };
saveReviewStar(key, next);
return next;
}
/**
* Mark a valid review activity.
* - Same day: increment todayCount
* - New day: todayCount=1, streak updates, totalDaysActive increments
*/
export function markReviewActive(key: string, _event: ReviewEventType): ReviewStarState {
const today = dayKeyLocal();
const prev = loadReviewStar(key);
if (!prev) {
const next: ReviewStarState = {
lastActiveDay: today,
todayCount: 1,
streakDays: 1,
totalDaysActive: 1,
};
saveReviewStar(key, next);
return next;
}
if (prev.lastActiveDay === today) {
const next: ReviewStarState = { ...prev, todayCount: prev.todayCount + 1 };
saveReviewStar(key, next);
return next;
}
const y = yesterdayKeyLocal();
const streak = prev.lastActiveDay === y ? prev.streakDays + 1 : 1;
const next: ReviewStarState = {
...prev,
lastActiveDay: today,
todayCount: 1,
streakDays: streak,
totalDaysActive: (prev.totalDaysActive ?? 0) + 1,
};
saveReviewStar(key, next);
return next;
}
/**
* UI mapping: opacity/energy
* - 0 activity today => dim
* - 1 activity => medium
* - 2+ => bright
* - streak >= 3 => max
*/
export function starOpacity(state: ReviewStarState | null) {
if (!state) return 0.15;
if (state.todayCount <= 0) return 0.15;
if (state.todayCount === 1) return 0.55;
if (state.streakDays >= 3) return 1.0;
return 0.85;
}
export function energyPct(state: ReviewStarState | null) {
return Math.round(starOpacity(state) * 100);
}