feat(pairing): solo sealed answers that unlock on pairing

Unpaired users could only reach a waiting-room dead end and could not answer
questions until a partner joined. Responses.couple_id is now nullable so a
solo user answers immediately and banks "sealed answers" (couple_id IS NULL).
Pairing claims both members' sealed answers and computes matches for every
question both have now answered. Invite copy and the unpaired push nudge
escalate with the sealed answer count.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
jjbruntonandClaude Fable 5 committed 2026-08-30 19:34:42 +01:00
1 parent f22d4a3649
commit 09255da7db
26 files changed
+510 -75

No files matched your search

+10 -14
View File
@@ -12,6 +12,8 @@ interface AuthState {
user: Profile | null;
couple: Couple | null;
partner: Profile | null;
/** This user's banked (sealed) answers with no couple yet. Zero once claimed at pairing. */
sealedCount: number;
isLoading: boolean;
isAuthenticated: boolean;
isAnonymous: boolean;
@@ -44,6 +46,7 @@ export const useAuthStore = create<AuthState>((set, get) => ({
user: null,
couple: null,
partner: null,
sealedCount: 0,
isLoading: true,
isAuthenticated: false,
isAnonymous: false,
@@ -77,12 +80,9 @@ export const useAuthStore = create<AuthState>((set, get) => ({
set({ isAuthenticated: true, isAnonymous });
}
// If user has a couple, fetch couple data; otherwise clear couple/partner
if (profile?.couple_id) {
await get().fetchCouple();
} else {
set({ couple: null, partner: null });
}
// Always fetch couple state: it reports the sealed answer count for a
// solo user even before any couple exists, not just once paired.
await get().fetchCouple();
set({ isLoading: false });
} catch (error) {
@@ -107,14 +107,10 @@ export const useAuthStore = create<AuthState>((set, get) => ({
},
fetchCouple: async () => {
const user = get().user;
if (!user?.couple_id) {
set({ couple: null, partner: null });
return;
}
const { couple, partner } = await coupleApi.getState();
set({ couple, partner });
// Fetches unconditionally: getState() reports sealed_count for a solo user
// who has no couple yet too, not only once paired.
const { couple, partner, sealed_count } = await coupleApi.getState();
set({ couple, partner, sealedCount: sealed_count });
},
/**