Finish interrupted features: tab prefetch, pet viewport, and home logo.

Add shared quiz/habits tab cache with nav hover prefetch and AppDataPrefetcher,
make the pet screen fill and center the viewport, and point the HaBit logo to /.
This commit is contained in:
2026-05-24 04:57:13 -06:00
parent 5dda46cb87
commit d5e80ec380
13 changed files with 333 additions and 56 deletions

View File

@@ -26,26 +26,45 @@ import {
type DailyQuizSubmission,
} from "@/lib/avatar-state";
import { HABIT_PET_DATA_UPDATED_EVENT } from "@/lib/app-events";
import {
getCachedQuizTabData,
prefetchQuizTabData,
} from "@/lib/app-tab-data-cache";
import {
getAvatarConditionForToday,
getDailyEntryForToday,
saveDailyEntry,
} from "@/lib/daily-quiz-storage";
import { JOURNAL_MAX_LENGTH } from "@/lib/journal-safety";
export function DailyQuizForm() {
const cachedQuiz = getCachedQuizTabData();
const [answers, setAnswers] = useState<DailyQuizAnswers>(
defaultDailyQuizAnswers,
cachedQuiz?.submission?.answers ?? defaultDailyQuizAnswers,
);
const [journal, setJournal] = useState("");
const [journal, setJournal] = useState(cachedQuiz?.submission?.journal ?? "");
const [submission, setSubmission] = useState<DailyQuizSubmission | null>(
null,
cachedQuiz?.submission ?? null,
);
const [isSubmitting, setIsSubmitting] = useState(false);
const [error, setError] = useState<string | null>(null);
const [isReady, setIsReady] = useState(false);
const [isReady, setIsReady] = useState(cachedQuiz !== null);
const [liveCondition, setLiveCondition] = useState(
submission?.condition ?? null,
cachedQuiz?.liveCondition ?? cachedQuiz?.submission?.condition ?? null,
);
const applyQuizData = useCallback(
(data: NonNullable<ReturnType<typeof getCachedQuizTabData>>) => {
if (data.submission) {
setSubmission(data.submission);
setAnswers(data.submission.answers);
setJournal(data.submission.journal);
setLiveCondition(data.submission.condition);
} else {
setSubmission(null);
setLiveCondition(data.liveCondition);
}
},
[],
);
const refreshLiveCondition = useCallback(async () => {
@@ -53,31 +72,40 @@ export function DailyQuizForm() {
}, []);
useEffect(() => {
let cancelled = false;
async function loadDailyCheckIn() {
try {
const existingEntry = await getDailyEntryForToday();
const data = await prefetchQuizTabData();
if (existingEntry) {
setSubmission(existingEntry);
setAnswers(existingEntry.answers);
setJournal(existingEntry.journal);
setLiveCondition(existingEntry.condition);
} else {
await refreshLiveCondition();
if (cancelled) {
return;
}
applyQuizData(data);
} catch (loadError) {
if (cancelled) {
return;
}
setError(
loadError instanceof Error
? loadError.message
: "Could not load today's check-in.",
);
} finally {
setIsReady(true);
if (!cancelled) {
setIsReady(true);
}
}
}
void loadDailyCheckIn();
}, [refreshLiveCondition]);
return () => {
cancelled = true;
};
}, [applyQuizData]);
useEffect(() => {
const handleDataUpdated = () => {