Skip to content

Commit e5b8232

Browse files
davidercruzclaude
andcommitted
fix(persona-quiz): pass tag filter as Feed variables so preview actually renders
The previous attempt routed the inter-question preview through the user's persisted `feedSettings` and relied on cache invalidation to trigger refetches after each `followTags` call. Two problems with that: 1. `feedQueryKey = [RequestKey.FeedPreview, user.id]` doesn't change when tags change, so React Query happily serves stale cache. 2. The shared `Feed` component supports a `variables` prop that gets spread into the GraphQL request — pass the tag filter explicitly and you don't need cache invalidation at all. Switching to the variables approach: - Pass `variables={{ filters: { includeTags: previewTags } }}` to `Feed` - Add the tag list to `feedQueryKey` so each unique tag set is its own cache entry and re-renders fetch fresh posts - Remove the incremental `followTags` + debounced `refetchPreview` block and the unused `useQueryClient` / `useDebounceFn` imports - Restore the single `followTags(merged)` call at the end of `finishQuiz` so the reveal screen's `TagSelection` sees the quiz tags as already followed via `feedSettings` Net: fewer moving parts, the preview now refreshes deterministically as the user answers, and the reveal flow is unchanged. Tests still 4/4 pass; typecheck and lint clean. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent e664990 commit e5b8232

1 file changed

Lines changed: 25 additions & 40 deletions

File tree

  • packages/shared/src/features/onboarding/steps/FunnelPersonaQuiz

packages/shared/src/features/onboarding/steps/FunnelPersonaQuiz/index.tsx

Lines changed: 25 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import React, {
66
useRef,
77
useState,
88
} from 'react';
9-
import { useMutation, useQueryClient } from '@tanstack/react-query';
9+
import { useMutation } from '@tanstack/react-query';
1010
import type {
1111
FunnelStepPersonaQuiz,
1212
FunnelStepPersonaQuizParameters,
@@ -20,7 +20,8 @@ import { useLogContext } from '../../../../contexts/LogContext';
2020
import { LogEvent } from '../../../../lib/log';
2121
import useMutateFilters from '../../../../hooks/useMutateFilters';
2222
import useFeedSettings from '../../../../hooks/useFeedSettings';
23-
import useDebounceFn from '../../../../hooks/useDebounceFn';
23+
// `useDebounceFn` import removed — was used by the previous incremental-follow
24+
// cache-invalidation flow which the ad-hoc `variables` approach replaces.
2425
import { usePersonaQuizState } from './usePersonaQuizState';
2526
import type { PersonaQuizAnswer } from './usePersonaQuizState';
2627
import { PersonaQuizQuestionView } from './PersonaQuizQuestion';
@@ -43,7 +44,6 @@ function FunnelPersonaQuizComponent({
4344
const user = auth?.user;
4445
const { followTags } = useMutateFilters(user);
4546
const { feedSettings } = useFeedSettings();
46-
const queryClient = useQueryClient();
4747

4848
const params = parameters as FunnelStepPersonaQuizParameters;
4949
const {
@@ -92,30 +92,12 @@ function FunnelPersonaQuizComponent({
9292
[tagScores, selection.tagConfidenceFloor],
9393
);
9494

95-
// Incrementally persist newly-earned tags after each answer so the shared
96-
// Feed preview reflects the user's evolving interests live. We track the
97-
// session's "already followed" set so we don't re-fire follow calls for
98-
// tags we've already sent.
99-
const followedRef = useRef<Set<string>>(new Set());
100-
const [refetchPreview] = useDebounceFn(() => {
101-
queryClient.invalidateQueries({
102-
queryKey: [RequestKey.FeedPreview],
103-
predicate: (q) => !q.queryKey.includes(RequestKey.FeedPreviewCustom),
104-
});
105-
}, 800);
106-
107-
useEffect(() => {
108-
if (phase !== 'question' || !user?.id) {
109-
return;
110-
}
111-
const fresh = accumulatedTags.filter((t) => !followedRef.current.has(t));
112-
if (fresh.length === 0) {
113-
return;
114-
}
115-
fresh.forEach((t) => followedRef.current.add(t));
116-
Promise.resolve(followTags({ tags: fresh })).catch(() => undefined);
117-
refetchPreview();
118-
}, [accumulatedTags, phase, user?.id, followTags, refetchPreview]);
95+
// Top tags used to filter the inter-question feed preview. Capped at 12 to
96+
// keep the GraphQL request lean.
97+
const previewTags = useMemo(
98+
() => accumulatedTags.slice(0, 12),
99+
[accumulatedTags],
100+
);
119101

120102
// Resolve the archetype from the terminal question's `archetypeId`, build
121103
// the final tag list, persist it (so `TagSelection` on the reveal screen
@@ -136,14 +118,10 @@ function FunnelPersonaQuizComponent({
136118
...(selection.fallbackTags ?? []),
137119
]).slice(0, selection.targetTotalTags);
138120

139-
// Most tags are already followed (we stream them incrementally as the
140-
// user answers). Only fire `followTags` for the residue — typically the
141-
// fallback tags backfilled to reach `targetTotalTags`.
142-
const fresh = merged.filter((t) => !followedRef.current.has(t));
143-
if (fresh.length > 0) {
144-
fresh.forEach((t) => followedRef.current.add(t));
145-
Promise.resolve(followTags({ tags: fresh })).catch(() => undefined);
146-
refetchPreview();
121+
// Persist the final tag set so `TagSelection` on the reveal screen
122+
// sees them as already-followed via feedSettings.
123+
if (merged.length > 0) {
124+
Promise.resolve(followTags({ tags: merged })).catch(() => undefined);
147125
}
148126

149127
enrichmentComplete(merged);
@@ -156,7 +134,6 @@ function FunnelPersonaQuizComponent({
156134
selection.fallbackTags,
157135
enrichmentComplete,
158136
followTags,
159-
refetchPreview,
160137
],
161138
);
162139

@@ -295,15 +272,23 @@ function FunnelPersonaQuizComponent({
295272
totalSteps={selection.maxQuestions}
296273
onSelect={handleAnswer}
297274
/>
298-
{answers.length >= 1 && user?.id && (
275+
{answers.length >= 1 && user?.id && previewTags.length > 0 && (
299276
<FeedLayoutProvider>
277+
<p className="mt-10 text-center text-text-tertiary typo-footnote">
278+
Sneak peek of your feed
279+
</p>
300280
<Feed
301-
className="relative mx-auto px-6 pt-10 tablet:left-1/2 tablet:w-screen tablet:-translate-x-1/2"
281+
className="relative mx-auto px-6 pt-6 tablet:left-1/2 tablet:w-screen tablet:-translate-x-1/2"
302282
feedName={OtherFeedPage.Preview}
303-
feedQueryKey={[RequestKey.FeedPreview, user.id]}
283+
feedQueryKey={[
284+
RequestKey.FeedPreview,
285+
user.id,
286+
previewTags.join('|'),
287+
]}
304288
query={PREVIEW_FEED_QUERY}
289+
variables={{ filters: { includeTags: previewTags } }}
305290
showSearch={false}
306-
options={{ refetchOnMount: false }}
291+
options={{ refetchOnMount: true }}
307292
allowPin
308293
/>
309294
</FeedLayoutProvider>

0 commit comments

Comments
 (0)