Add streak helper utilities
cgen-f0cb506b21104388a1be4a2671622a4b
This commit is contained in:
parent
6041c0afd8
commit
6b8da6efea
1 changed files with 108 additions and 0 deletions
|
|
@ -31,6 +31,114 @@ const ensureSupabase = () => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const MS_PER_DAY = 1000 * 60 * 60 * 24;
|
||||||
|
|
||||||
|
const startOfUTC = (date: Date) =>
|
||||||
|
new Date(Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate()));
|
||||||
|
|
||||||
|
const isoDate = (date: Date) => date.toISOString().slice(0, 10);
|
||||||
|
|
||||||
|
const normalizeDateInput = (value?: string | null) => {
|
||||||
|
if (!value) return null;
|
||||||
|
const normalized = value.includes("T") ? value : `${value}T00:00:00Z`;
|
||||||
|
return startOfUTC(new Date(normalized));
|
||||||
|
};
|
||||||
|
|
||||||
|
const normalizeProfile = (
|
||||||
|
row: any,
|
||||||
|
email?: string | null,
|
||||||
|
): AethexUserProfile => ({
|
||||||
|
...(row as AethexUserProfile),
|
||||||
|
email: email ?? (row as any)?.email,
|
||||||
|
username:
|
||||||
|
(row as any)?.username ?? email?.split("@")[0] ?? "user",
|
||||||
|
onboarded: true,
|
||||||
|
role: (row as any)?.role ?? "member",
|
||||||
|
loyalty_points: (row as any)?.loyalty_points ?? 0,
|
||||||
|
current_streak: (row as any)?.current_streak ?? 0,
|
||||||
|
longest_streak:
|
||||||
|
(row as any)?.longest_streak ?? Math.max((row as any)?.current_streak ?? 0, 0),
|
||||||
|
last_streak_at: (row as any)?.last_streak_at ?? null,
|
||||||
|
});
|
||||||
|
|
||||||
|
const ensureDailyStreakForProfile = async (
|
||||||
|
profile: AethexUserProfile,
|
||||||
|
): Promise<AethexUserProfile> => {
|
||||||
|
try {
|
||||||
|
ensureSupabase();
|
||||||
|
} catch {
|
||||||
|
return {
|
||||||
|
...profile,
|
||||||
|
current_streak: profile.current_streak ?? 0,
|
||||||
|
longest_streak: profile.longest_streak ?? profile.current_streak ?? 0,
|
||||||
|
last_streak_at: profile.last_streak_at ?? null,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const today = startOfUTC(new Date());
|
||||||
|
const isoToday = isoDate(today);
|
||||||
|
const lastRecorded = normalizeDateInput(profile.last_streak_at);
|
||||||
|
let current = profile.current_streak ?? 0;
|
||||||
|
let longest = profile.longest_streak ?? 0;
|
||||||
|
let lastValue = profile.last_streak_at ?? null;
|
||||||
|
let needsUpdate = false;
|
||||||
|
|
||||||
|
if (!lastRecorded) {
|
||||||
|
current = Math.max(current, 1);
|
||||||
|
longest = Math.max(longest, current);
|
||||||
|
lastValue = isoToday;
|
||||||
|
needsUpdate = true;
|
||||||
|
} else {
|
||||||
|
const diffDays = Math.floor(
|
||||||
|
(today.getTime() - lastRecorded.getTime()) / MS_PER_DAY,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (diffDays === 1) {
|
||||||
|
current = current + 1;
|
||||||
|
longest = Math.max(longest, current);
|
||||||
|
lastValue = isoToday;
|
||||||
|
needsUpdate = true;
|
||||||
|
} else if (diffDays > 1 || diffDays < 0) {
|
||||||
|
current = 1;
|
||||||
|
longest = Math.max(longest, current);
|
||||||
|
lastValue = isoToday;
|
||||||
|
needsUpdate = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (needsUpdate) {
|
||||||
|
const { data, error } = await supabase
|
||||||
|
.from("user_profiles")
|
||||||
|
.update({
|
||||||
|
current_streak: current,
|
||||||
|
longest_streak: longest,
|
||||||
|
last_streak_at: isoToday,
|
||||||
|
})
|
||||||
|
.eq("id", profile.id)
|
||||||
|
.select()
|
||||||
|
.single();
|
||||||
|
|
||||||
|
if (!error && data) {
|
||||||
|
return normalizeProfile(data, profile.email);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.warn("Failed to persist streak update:", error);
|
||||||
|
return {
|
||||||
|
...profile,
|
||||||
|
current_streak: current,
|
||||||
|
longest_streak: longest,
|
||||||
|
last_streak_at: lastValue,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
...profile,
|
||||||
|
current_streak: current,
|
||||||
|
longest_streak: longest,
|
||||||
|
last_streak_at: lastValue,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
export function checkProfileComplete(p?: AethexUserProfile | null): boolean {
|
export function checkProfileComplete(p?: AethexUserProfile | null): boolean {
|
||||||
if (!p) return false;
|
if (!p) return false;
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue