Skip to content

Fix streak creation error and refactor to Goals pattern#369

Merged
masonfox merged 3 commits intodevelopfrom
fix/streak-creation-303-error
Feb 16, 2026
Merged

Fix streak creation error and refactor to Goals pattern#369
masonfox merged 3 commits intodevelopfrom
fix/streak-creation-303-error

Conversation

@masonfox
Copy link
Owner

Summary

Fixes a bug where users see an error toast "Failed to create streak" when first enabling streak tracking, even though the streak is successfully created. Also refactors the streak onboarding flow to match the Goals pattern for better architectural consistency.

Problem

When users enable streak tracking:

  1. A server action calls redirect("/streak") which throws a NEXT_REDIRECT error internally
  2. This error bubbles up to the client-side try-catch block in StreakOnboarding
  3. The error toast is triggered even though the streak is created successfully
  4. The 303 redirect response is followed correctly, but the user sees a confusing error message

Solution

Phase 1: Quick Fix (bb1dbf4)

  • Removed server-side redirect() from server action
  • Added client-side router.refresh() call after successful enable
  • Eliminates the NEXT_REDIRECT error without architectural changes

Phase 2: Architectural Refactor (bb6cda9)

  • Refactored streak onboarding to match the Goals pattern (React Query + API routes)
  • Removed the only "use server" directive in the codebase for consistency
  • Created consistent architecture:
    • Added enableStreak() API method with proper types
    • Added enableStreakMutation to useStreak hook with toast notifications
    • Created StreakPagePanel client wrapper component (matches GoalsPagePanel)
    • Simplified StreakOnboarding to use hooks directly (no props)
    • Refactored app/streak/page.tsx to server component pattern
    • Updated all tests to mock the useStreak hook

Changes

Files Modified (7 total):

  • /lib/api/domains/streak/types.ts - Added EnableStreakRequest/Response types
  • /lib/api/domains/streak/api.ts - Added enableStreak() method
  • /hooks/useStreak.ts - Added enable/disable mutation with React Query
  • /components/Streaks/StreakOnboarding.tsx - Refactored to use useStreak hook
  • /components/Streaks/StreakPagePanel.tsx - NEW: Client wrapper component
  • /app/streak/page.tsx - Simplified to server component with client wrapper
  • /__tests__/components/StreakOnboarding.test.tsx - Updated to mock hook

Benefits

  1. Consistency: Matches Goals pattern used throughout the codebase
  2. No server actions: Removes confusing "use server" directive
  3. Better UX: No more error toasts on successful operations
  4. Type safety: Full TypeScript coverage from API to UI
  5. Better state management: React Query handles caching and invalidation
  6. Testability: Easy to mock and test with established patterns

Testing

  • ✅ All 3815 tests passing
  • ✅ Build completes successfully (no TypeScript errors)
  • ✅ Follows established architectural patterns from Goals flow

Related

  • Fixes the only usage of "use server" directive in the codebase
  • Aligns with architecture defined in .specify/memory/patterns.md

…n streak creation

When users first enabled streak tracking, they saw an error toast saying 'Failed to create streak' even though the streak was successfully created. This was caused by Next.js redirect() throwing a NEXT_REDIRECT error that was caught by the client-side try-catch block.

Changes:
- Remove redirect('/streak') from server action in app/streak/page.tsx
- Add useRouter() hook to StreakOnboarding component
- Call router.refresh() after successful streak enable to reload server component
- Add router mock to StreakOnboarding tests to support useRouter()
- Add explanatory comments documenting why we use router.refresh() instead of redirect()

This follows the pattern already established in DesktopSidebar.tsx for logout handling. The fix ensures users see the correct success toast and the page refreshes to show the enabled streak without any error messages.

Fixes the 303 redirect error that occurred during streak creation.
…I routes)

- Add enableStreak() method to streak API with types
- Add enableStreakMutation to useStreak hook with toast notifications
- Refactor StreakOnboarding to use useStreak hook (no props)
- Create StreakPagePanel client wrapper (matches GoalsPagePanel pattern)
- Simplify app/streak/page.tsx to server component with client wrapper
- Update tests to mock useStreak hook instead of props
- Remove server action and "use server" directive (consistency)

Benefits:
- Consistent with Goals pattern (React Query + API)
- No NEXT_REDIRECT errors from server actions
- Better state management and caching via React Query
- Type-safe from API to UI
- Easier to test with mocked hooks

All 3815 tests passing.
@codecov
Copy link

codecov bot commented Feb 16, 2026

Codecov Report

❌ Patch coverage is 92.30769% with 1 line in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
hooks/useStreak.ts 88.88% 0 Missing and 1 partial ⚠️

Impacted file tree graph

@@             Coverage Diff             @@
##           develop     #369      +/-   ##
===========================================
- Coverage    78.07%   78.00%   -0.07%     
===========================================
  Files          162      162              
  Lines         7289     7312      +23     
  Branches      1759     1765       +6     
===========================================
+ Hits          5691     5704      +13     
- Misses        1132     1139       +7     
- Partials       466      469       +3     
Flag Coverage Δ
unittests 78.00% <92.30%> (-0.07%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
components/Streaks/StreakOnboarding.tsx 100.00% <100.00%> (ø)
lib/api/domains/streak/api.ts 100.00% <100.00%> (ø)
hooks/useStreak.ts 91.66% <88.88%> (-0.93%) ⬇️

... and 3 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

…thod

- Add 11 new tests for useStreak.enableStreak mutation
  - Tests for both enable and disable flows
  - Tests for loading states (isEnablingStreak)
  - Tests for success and error toasts
  - Tests for query invalidation on success
  - Tests for error handling with custom messages
  - Tests for parameter variations (with/without dailyThreshold)

- Add 6 new tests for streakApi.enableStreak method
  - Tests for PATCH endpoint with correct parameters
  - Tests for enabling/disabling flows
  - Tests for response handling
  - Tests for error propagation from baseApiClient

All 3832 tests passing. Increases coverage for new code in PR #369.
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes the misleading “Failed to create streak” toast caused by a server-action redirect() throwing NEXT_REDIRECT, and refactors streak onboarding to follow the existing Goals architecture pattern (API routes + React Query mutations).

Changes:

  • Introduces typed enableStreak request/response types and a streakApi.enableStreak() client method.
  • Adds an enableStreak React Query mutation to useStreak, and refactors StreakOnboarding to use the hook directly.
  • Adds a new client wrapper (StreakPagePanel) and simplifies /app/streak/page.tsx to a server component that delegates rendering.

Reviewed changes

Copilot reviewed 9 out of 9 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
lib/api/domains/streak/types.ts Adds EnableStreak request/response types.
lib/api/domains/streak/api.ts Adds streakApi.enableStreak() PATCH helper.
hooks/useStreak.ts Adds React Query mutation for enabling/disabling streak tracking.
components/Streaks/StreakOnboarding.tsx Refactors onboarding to call useStreak().enableStreak (no server action).
components/Streaks/StreakPagePanel.tsx New client wrapper aligning streak page rendering with the Goals pattern.
app/streak/page.tsx Moves server-side logic to fetch initial state + analytics and renders StreakPagePanel.
tests/unit/api-clients/streak.api.test.ts Adds unit tests for the new enableStreak API helper.
tests/hooks/useStreak.test.ts Adds hook-level tests for the new enable/disable mutation behavior.
tests/components/StreakOnboarding.test.tsx Updates component tests to mock useStreak instead of passing onEnable props.

Comment on lines +73 to +76
// Invalidate streak-related queries
queryClient.invalidateQueries({ queryKey: ['streak-analytics'] });
queryClient.invalidateQueries({ queryKey: ['streaks'] });
queryClient.invalidateQueries({ queryKey: ['dashboard'] });
Copy link

Copilot AI Feb 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

enableStreakMutation invalidates ['streak-analytics'], but the heatmap query uses a separate key (['streak-analytics-heatmap', 365]). If enabling/disabling also sets/changes the threshold, the heatmap can remain stale; consider invalidating the heatmap query key as well on success.

Copilot uses AI. Check for mistakes.
toast.error("Failed to enable streak tracking. Please try again.");
setIsEnabling(false);
}
enableStreak({ streakEnabled: true, dailyThreshold: dailyGoal });
Copy link

Copilot AI Feb 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After enabling streak tracking, the UI won’t transition out of onboarding because StreakPagePanel renders onboarding based on the server-provided streakEnabled prop, which won’t change until the route is refreshed/navigated. Consider triggering router.refresh() (or updating local client state / driving streakEnabled from a React Query GET) on successful enable so the analytics panel renders immediately.

Copilot uses AI. Check for mistakes.
Comment on lines 27 to +31
analyticsData = await streakService.getAnalytics(7, null);
} catch (error) {
logger.error({ error }, "Failed to fetch analytics");
return (
<div className="space-y-10">
<PageHeader
title="Streak"
subtitle="Track your reading habits and progress over time"
icon={Flame}
/>
<div className="bg-[var(--card-bg)] border border-[var(--border-color)] p-8 text-center rounded-md">
<p className="text-[var(--foreground)]/70 font-medium">
Unable to load streak analytics. Please try again later.
</p>
</div>
</div>
);
// Pass undefined analyticsData to show error state
analyticsData = undefined;
Copy link

Copilot AI Feb 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This analyticsData flow relies on an untyped variable declared above (let analyticsData;), which effectively becomes any and loses type-safety when passed into StreakPagePanel. Please give analyticsData an explicit type (e.g., Awaited<ReturnType<typeof streakService.getAnalytics>> | undefined) to keep the server-to-client contract checked by TypeScript.

Copilot uses AI. Check for mistakes.
@masonfox masonfox merged commit 4850353 into develop Feb 16, 2026
10 checks passed
@masonfox masonfox deleted the fix/streak-creation-303-error branch February 16, 2026 19:52
@masonfox masonfox mentioned this pull request Feb 16, 2026
masonfox added a commit that referenced this pull request Feb 16, 2026
## Release v0.6.5

This release includes important bug fixes and improvements to date
handling, reading goals, and streak functionality.

### Key Changes

#### Bug Fixes
- **Reading Goals (#373)**: Exclude DNF (Did Not Finish) books from
reading goal calculations to accurately reflect completed books
- **Date String Storage (#366)**: Enforce ADR-014 date string storage
compliance across the entire codebase for consistent date handling
- **Streak Creation (#369)**: Fix streak creation error and refactor to
Goals pattern for better reliability

#### Improvements
- Enhanced date validation and storage consistency
- Improved streak service reliability
- Better reading goals accuracy
- Comprehensive test coverage for new features

### Testing
- All 99+ tests passing
- New test coverage for date handling, reading goals, and streak
functionality

This release builds on v0.6.4 with critical fixes to ensure data
integrity and accurate tracking.

---------

Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
Co-authored-by: OpenCode <opencode@sst.dev>
Co-authored-by: TestForge <testforge@claude.ai>
Co-authored-by: Mason <mason@sender.net>
Co-authored-by: TestForge <testforge@tome.local>
Co-authored-by: OpenCode <opencode@tome.local>
Co-authored-by: OpenCode <opencode@anomaly.co>
Co-authored-by: OpenCode <noreply@opencode.ai>
Co-authored-by: AI Assistant <ai@opencode.ai>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Mason Fox <masonfox@pop-os.lan>
Co-authored-by: Mason Fox <masonfox@pop-os.tail3f4b5.ts.net>
Co-authored-by: OpenCode <opencode@anomalyco.ai>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

Comments