Files
Sauci/apps/mobile/src/features/quiz/QuizScreen.tsx
T
jjbruntonandClaude Fable 5 6523ddeb1c feat(quiz): couples quiz with shareable watermarked results card
Replaces the notify-me quiz stub with a full loop: quiz sessions per
couple, answer-and-guess flow, dual-direction scoring, and a score-only
share card (no question or answer content) captured via ViewShot with
Sauci branding.

API: migration 0019 (quiz_questions, quiz_sessions, quiz_answers, 12
seeded questions), new quiz domain with session start / current /
answers / result routes, couple isolation, route + integration +
scoring unit tests.

Mobile: src/features/quiz feature (intro, per-question self+guess flow,
waiting-for-partner nudge, results with breakdown), quizStore with
sign-out clearing, QuizShareModal following SharePreviewModal, discovery
QuizTile, analytics events, and tests.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-30 18:09:19 +01:00

107 lines
2.9 KiB
TypeScript

import { ActivityIndicator, View, StyleSheet } from "react-native";
import { GradientBackground } from "../../components/ui";
import { featureColors } from "../../theme";
import { SwipeNoPartnerState } from "../swipe/components/SwipeNoPartnerState";
import { useQuizScreen } from "./hooks/useQuizScreen";
import {
QuizIntro,
QuizQuestionCard,
QuizResults,
QuizShareModal,
QuizWaitingState,
} from "./components";
export function QuizScreen() {
const {
screenState,
session,
result,
currentStep,
totalQuestions,
isStarting,
isSubmitting,
isLoadingResult,
shareModalVisible,
handleSelectOption,
handleStart,
handleNewQuiz,
handleRefresh,
handlePairPress,
handleNudgePartner,
openShareModal,
closeShareModal,
} = useQuizScreen();
if (screenState === "not_paired") {
return <SwipeNoPartnerState hasCouple={false} onPairPress={handlePairPress} />;
}
if (screenState === "loading") {
return (
<GradientBackground>
<View style={styles.loadingContainer}>
<ActivityIndicator size="large" color={featureColors.quiz.accent} />
</View>
</GradientBackground>
);
}
if (screenState === "intro") {
return <QuizIntro isStarting={isStarting} onStart={handleStart} />;
}
if (screenState === "waiting") {
return <QuizWaitingState onNudgePartner={handleNudgePartner} onRefresh={handleRefresh} />;
}
if (screenState === "results") {
return (
<>
<QuizResults
result={result}
isLoadingResult={isLoadingResult}
isStarting={isStarting}
onNewQuiz={handleNewQuiz}
onShare={openShareModal}
/>
<QuizShareModal
visible={shareModalVisible}
onClose={closeShareModal}
scorePercent={result?.score_percent ?? 0}
/>
</>
);
}
// screenState === "answering"
if (!session || !currentStep || isSubmitting) {
return (
<GradientBackground>
<View style={styles.loadingContainer}>
<ActivityIndicator size="large" color={featureColors.quiz.accent} />
</View>
</GradientBackground>
);
}
return (
<QuizQuestionCard
question={currentStep.question}
step={currentStep.step}
questionNumber={currentStep.questionNumber}
totalQuestions={totalQuestions}
onSelectOption={handleSelectOption}
/>
);
}
const styles = StyleSheet.create({
loadingContainer: {
flex: 1,
justifyContent: "center",
alignItems: "center",
},
});
export default QuizScreen;