Skip to content

Issue/3350/feat/follow reorganization#4363

Open
lsabor wants to merge 13 commits intomainfrom
issue/3350/feat/follow-reorganization
Open

Issue/3350/feat/follow reorganization#4363
lsabor wants to merge 13 commits intomainfrom
issue/3350/feat/follow-reorganization

Conversation

@lsabor
Copy link
Copy Markdown
Contributor

@lsabor lsabor commented Feb 15, 2026

closes #3350

Summary by CodeRabbit

  • New Features

    • Metaculus News newsletter toggle; "Auto-follow on predict"; weekly top-comments email; follow triggers for mentions, predicted resolves, CP changes, and auto-withdrawal.
  • Improvements

    • Redesigned notifications settings with sectional switches, per-item saving feedback, default follow controls (CP threshold, comment frequency, milestone step, status-change), and localized CP-change size labels plus translations for multiple languages.
  • Removed

    • Legacy notification labels and the prior per-question notification list.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Feb 15, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds a consolidated "Site News / Keeping up" notifications UI, auto-follow-on-predict and per-user default follow preferences, new user/profile fields and migration, backend wiring to create per-post subscriptions on predict, and i18n key updates across multiple locales.

Changes

Cohort / File(s) Summary
Localization files
front_end/messages/cs.json, front_end/messages/en.json, front_end/messages/es.json, front_end/messages/pt.json, front_end/messages/zh-TW.json, front_end/messages/zh.json
Removed legacy notification keys and added new "Site News"/"Keeping up"/follow-default translation keys plus cpChangeSmall, cpChangeMedium, cpChangeLarge.
Email notifications UI
front_end/src/app/(main)/accounts/settings/notifications/components/email_notifications.tsx
Replaced checkbox list with SwitchRow/Switch layout, per-field pending/error UX, optimistic server updates, newsletter and auto-follow toggles, "keeping up" rows, and default-follow controls (CP threshold, comments frequency, milestone step, status-change).
Question notifications removal & page update
front_end/src/app/(main)/accounts/settings/notifications/components/question_notifications.tsx, front_end/src/app/(main)/accounts/settings/notifications/page.tsx
Deleted QuestionNotifications component and removed posts/subscriptions fetch; page now only renders EmailNotifications.
Frontend types & profile API
front_end/src/types/users.ts, front_end/src/types/notifications.ts, front_end/src/app/(main)/accounts/profile/actions.tsx, front_end/src/services/api/profile/profile.server.ts
Added new CurrentUser fields for news/auto-follow/follow-notify settings; expanded UpdateProfileRequest and updateProfileAction to accept them; reordered SubscriptionEmailType members.
Post-subscribe & UI components
front_end/src/components/post_subscribe/subscription_types_customisation/subscription_cp_change.tsx, front_end/src/components/ui/button_group.tsx
Localized CP-change labels to new i18n keys; ButtonGroup now accepts disabled and forwards it to buttons.
Server: user model, migration & serializer/view
users/models.py, users/migrations/0019_user_automatically_follow_on_predict_and_more.py, users/serializers.py, users/views.py
Added automatically_follow_on_predict and follow-notify default fields to User model and migration; serializers expose/read/write these fields plus computed metaculus_news_subscription; view toggles "platform" project subscription when provided.
Auto-follow behaviour on predict
questions/services/forecasts.py
When a forecast is created and user has automatically_follow_on_predict, create non-global per-post subscriptions (CP change, comments, milestones, status) based on user-default preferences if not already present.
Mailing tags & tests
notifications/constants.py, tests/unit/test_questions/test_views.py
Reordered MailingTags entries (no value changes); updated test to set scheduled_resolve_time and refresh pseudo-materialized post fields before assertions.
Hook and small frontend changes
front_end/src/hooks/use_server_action.ts, front_end/src/components/post_subscribe/.../subscription_cp_change.tsx, front_end/src/components/ui/button_group.tsx
useServerAction now returns an error flag; CP-change selector uses localized labels; ButtonGroup accepts disabled.

Sequence Diagram

sequenceDiagram
    participant User
    participant Frontend as Frontend (Browser)
    participant API as Backend API
    participant SubSvc as Subscription Service
    participant DB as Database

    User->>Frontend: Create forecast (predict)
    Frontend->>API: POST /forecasts
    API->>DB: Load user (includes auto-follow & follow prefs)
    DB-->>API: Return user
    alt automatically_follow_on_predict = true
        API->>SubSvc: Fetch existing non-global subscriptions for post
        SubSvc->>DB: Query subscriptions (user, post, non-global)
        DB-->>SubSvc: Return existing subscriptions
        alt missing subscription type(s)
            API->>SubSvc: create_subscription(type=cp_change/comment/milestone/status)
            SubSvc->>DB: Insert subscription(s)
            DB-->>SubSvc: Confirm creation
        end
    end
    API-->>Frontend: Return created forecast (and created subscriptions)
    Frontend-->>User: Show success and follow status
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • elisescu
  • lsabor

Poem

🐰 I hopped through toggles, switches bright,

Auto-follow woke to greet the light,
News and defaults stitched with care,
Translations scattered here and there,
A rabbit cheers — settings take flight!

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Issue/3350/feat/follow reorganization' is directly related to the main change—reorganizing notification settings and adding new follow options per issue #3350.
Linked Issues check ✅ Passed The PR successfully implements all core coding requirements from issue #3350: removed notification table UI, reorganized notification options into Site News/Keeping up/Auto-follow/Default Follow sections, added newsletter and Metaculus News follow options, implemented auto-follow-on-predict functionality with appropriate defaults, added default follow notification settings, and converted checkboxes to switch buttons.
Out of Scope Changes check ✅ Passed All changes are directly scoped to implementing issue #3350 requirements: translation updates, database schema, model/serializer expansions, API updates, and UI component refactoring. No unrelated features or cleanup were introduced.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch issue/3350/feat/follow-reorganization

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@lsabor lsabor force-pushed the issue/3350/feat/follow-reorganization branch from 87f684b to 86bc72b Compare February 16, 2026 18:00
@github-actions
Copy link
Copy Markdown
Contributor

This pull request has been automatically marked as stale because it has not had recent activity. It will not be closed automatically, but please consider updating it or closing it if it is no longer relevant.

@github-actions github-actions Bot added the Stale label Mar 27, 2026
@cemreinanc cemreinanc marked this pull request as ready for review March 31, 2026 22:00
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Mar 31, 2026

🚀 Preview Environment

Your preview environment is ready!

Resource Details
🌐 Preview URL https://metaculus-pr-4363-issue-3350-feat-follow-reorgan-preview.mtcl.cc
📦 Docker Image ghcr.io/metaculus/metaculus:issue-3350-feat-follow-reorganization-43e2df1
🗄️ PostgreSQL NeonDB branch preview/pr-4363-issue-3350-feat-follow-reorgan
Redis Fly Redis mtc-redis-pr-4363-issue-3350-feat-follow-reorgan

Details

  • Commit: 43e2df16b06079b1f586d4200015c2857796385f
  • Branch: issue/3350/feat/follow-reorganization
  • Fly App: metaculus-pr-4363-issue-3350-feat-follow-reorgan

ℹ️ Preview Environment Info

Isolation:

  • PostgreSQL and Redis are fully isolated from production
  • Each PR gets its own database branch and Redis instance
  • Changes pushed to this PR will trigger a new deployment

Limitations:

  • Background workers and cron jobs are not deployed in preview environments
  • If you need to test background jobs, use Heroku staging environments

Cleanup:

  • This preview will be automatically destroyed when the PR is closed

@cemreinanc cemreinanc removed the Stale label Mar 31, 2026
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 6

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
front_end/src/app/(main)/accounts/settings/notifications/components/email_notifications.tsx (1)

97-123: ⚠️ Potential issue | 🟠 Major

Don’t build mailing-tag updates from the original user prop.

These switches still render from, and diff against, user.unsubscribed_mailing_tags. After the first toggle, a second change is computed from stale data and can silently undo the first choice. Keep a local unsubscribedMailingTags state here (or send per-tag deltas) and render/update from that instead.

Also applies to: 231-247, 281-290

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@front_end/src/app/`(main)/accounts/settings/notifications/components/email_notifications.tsx
around lines 97 - 123, The toggle handler and render currently read and diff
against the stale user.unsubscribed_mailing_tags prop (in
handleEmailSubscriptionChange), causing racey undos; create a local state like
unsubscribedMailingTags initialized from user.unsubscribed_mailing_tags, render
switches from that state, and in handleEmailSubscriptionChange (and the places
referenced at 231-247, 281-290) compute the new set from
unsubscribedMailingTags, immediately update unsubscribedMailingTags
optimistically and setLoadingTag, then call updateProfileAction (via
updateMailingTag / useServerAction) and reconcile on success/failure — keep
references to handleEmailSubscriptionChange, updateProfileAction,
updateMailingTag, isMailingTagPending so the UI uses the local state and not the
original user prop.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@front_end/src/app/`(main)/accounts/settings/notifications/components/email_notifications.tsx:
- Around line 163-169: updateDefaultFollow currently fire-and-forgets
runDefaultFollowUpdate which allows overlapping writes; change it to serialize
the save by awaiting the server action and managing the pending state: call
setSavingDefaultField(field) before the await, await
runDefaultFollowUpdate(updates) inside a try/catch, clear
setSavingDefaultField(null) in finally and handle/log errors in catch; also
ensure the button groups use the same saving state (setSavingDefaultField) to
disable themselves while pending (same pattern applies to the similar handlers
runDefaultMentionUpdate/runDefaultMuteUpdate and their wrappers at the other
ranges).
- Around line 46-50: CP_BUTTONS currently uses hardcoded English labels
("small","medium","large") which prevents localization; replace the static
labels with translated strings obtained via useTranslations() (or the project's
i18n helper) and construct CP_BUTTONS using those translated values so
ButtonGroup renders localized labels; update the code where CP_BUTTONS is
defined (the GroupButton<CpKey> array) to call useTranslations() inside the
component or pass translated labels in from the parent and ensure keys (value:
"small"/"medium"/"large") remain unchanged while label uses t('...') lookups.

In `@questions/services/forecasts.py`:
- Around line 292-343: The auto-follow subscription creation guarded by created
and user.automatically_follow_on_predict (the block using post = question.post,
existing_subscription, PostSubscription.SubscriptionType.* and
create_subscription) is currently placed after early returns that prevent it
from running for normal forecasts; extract these checks into a helper (e.g.,
ensure_auto_follow_subscriptions(created, user, question)) or move the entire
block to execute immediately when created is True (before the early returns),
and use the same existing_subscription and create_subscription calls to add
CP_CHANGE, NEW_COMMENTS, MILESTONE, and STATUS_CHANGE subscriptions only if they
don't already exist.
- Around line 294-343: The subscription creation is racy because you check
existence then call create_subscription which uses direct .create(), risking
IntegrityError under concurrent requests; update the logic so
create_subscription (or the underlying factory) uses get_or_create (or
update_or_create) keyed on (type, user, post, is_global) or wraps the create in
a try/except that handles IntegrityError by returning the existing
PostSubscription; specifically modify create_subscription (and any factory it
calls) to call PostSubscription.objects.get_or_create(...) for
PostSubscription.SubscriptionType values (CP_CHANGE, NEW_COMMENTS, MILESTONE,
STATUS_CHANGE) instead of relying on the external exists() checks referenced by
existing_subscription.

In `@users/migrations/0019_user_automatically_follow_on_predict_and_more.py`:
- Around line 6-23: The migrate() function that sets existing users'
automatically_follow_on_predict=False is defined but not executed; add a
migrations.RunPython step to operations so the backfill runs before/after the
AddField. Update the Migration.operations list to include
migrations.RunPython(migrate, migrations.RunPython.noop) (referencing the
migrate function name) alongside the existing migrations.AddField for
model_name="user" name="automatically_follow_on_predict" to ensure existing rows
are set to False instead of being backfilled to True.

In `@users/serializers.py`:
- Around line 191-196: The serializer in users/serializers.py that exposes the
follow-default fields ("automatically_follow_on_predict",
"follow_notify_cp_change_threshold", "follow_notify_comments_frequency",
"follow_notify_milestone_step", "follow_notify_on_status_change",
"metaculus_news_subscription") must validate incoming values before save:
replace or augment the current writable field definitions with explicit DRF
validators (e.g. serializers.IntegerField with min_value and max_value or
serializers.ChoiceField for preset enums) or add model validators on the
corresponding model fields; ensure numeric fields reject 0 and non-integers
(e.g. use IntegerField to prevent 1.5), allow null only when intended, and
include clear error messages so invalid values are rejected by the
ProfileSerializer (or the serializer class in users/serializers.py) before
persisting.

---

Outside diff comments:
In
`@front_end/src/app/`(main)/accounts/settings/notifications/components/email_notifications.tsx:
- Around line 97-123: The toggle handler and render currently read and diff
against the stale user.unsubscribed_mailing_tags prop (in
handleEmailSubscriptionChange), causing racey undos; create a local state like
unsubscribedMailingTags initialized from user.unsubscribed_mailing_tags, render
switches from that state, and in handleEmailSubscriptionChange (and the places
referenced at 231-247, 281-290) compute the new set from
unsubscribedMailingTags, immediately update unsubscribedMailingTags
optimistically and setLoadingTag, then call updateProfileAction (via
updateMailingTag / useServerAction) and reconcile on success/failure — keep
references to handleEmailSubscriptionChange, updateProfileAction,
updateMailingTag, isMailingTagPending so the UI uses the local state and not the
original user prop.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 5333c05b-70d1-4882-97c0-154dfd62a0cd

📥 Commits

Reviewing files that changed from the base of the PR and between 12b0564 and 7928048.

📒 Files selected for processing (20)
  • front_end/messages/cs.json
  • front_end/messages/en.json
  • front_end/messages/es.json
  • front_end/messages/pt.json
  • front_end/messages/zh-TW.json
  • front_end/messages/zh.json
  • front_end/src/app/(main)/accounts/profile/actions.tsx
  • front_end/src/app/(main)/accounts/settings/notifications/components/email_notifications.tsx
  • front_end/src/app/(main)/accounts/settings/notifications/components/question_notifications.tsx
  • front_end/src/app/(main)/accounts/settings/notifications/page.tsx
  • front_end/src/services/api/profile/profile.server.ts
  • front_end/src/types/notifications.ts
  • front_end/src/types/users.ts
  • notifications/constants.py
  • questions/services/forecasts.py
  • tests/unit/test_questions/test_views.py
  • users/migrations/0019_user_automatically_follow_on_predict_and_more.py
  • users/models.py
  • users/serializers.py
  • users/views.py
💤 Files with no reviewable changes (1)
  • front_end/src/app/(main)/accounts/settings/notifications/components/question_notifications.tsx

Comment on lines +292 to +343
if created and user.automatically_follow_on_predict:
post = question.post
existing_subscription = post.subscriptions.filter(user=user).exclude(
is_global=True
)
if (
user.follow_notify_cp_change_threshold
and not existing_subscription.filter(
type=PostSubscription.SubscriptionType.CP_CHANGE
).exists()
):
create_subscription(
subscription_type=PostSubscription.SubscriptionType.CP_CHANGE,
user=user,
post=post,
cp_change_threshold=user.follow_notify_cp_change_threshold,
)
if (
user.follow_notify_comments_frequency
and not existing_subscription.filter(
type=PostSubscription.SubscriptionType.NEW_COMMENTS
).exists()
):
create_subscription(
subscription_type=PostSubscription.SubscriptionType.NEW_COMMENTS,
user=user,
post=post,
comments_frequency=user.follow_notify_comments_frequency,
)
if (
user.follow_notify_milestone_step
and not existing_subscription.filter(
type=PostSubscription.SubscriptionType.MILESTONE
).exists()
):
create_subscription(
subscription_type=PostSubscription.SubscriptionType.MILESTONE,
user=user,
post=post,
milestone_step=user.follow_notify_milestone_step,
)
if (
user.follow_notify_on_status_change
and not existing_subscription.filter(
type=PostSubscription.SubscriptionType.STATUS_CHANGE
).exists()
):
create_subscription(
subscription_type=PostSubscription.SubscriptionType.STATUS_CHANGE,
user=user,
post=post,
)
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot Mar 31, 2026

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Don’t gate auto-follow on withdrawal-reminder eligibility.

This block is unreachable for the common “normal forecast” path. Because end_time falls back to start_time above, Line 273 returns for every forecast without an explicit end_time; Line 269 also exits when the forecast ends at or after scheduled_close_time. That means automatically_follow_on_predict only runs for a narrow subset of expiring forecasts instead of all new predictions. Please move the follow-subscription creation before those returns, or split it into a helper that runs unconditionally on created=True.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@questions/services/forecasts.py` around lines 292 - 343, The auto-follow
subscription creation guarded by created and
user.automatically_follow_on_predict (the block using post = question.post,
existing_subscription, PostSubscription.SubscriptionType.* and
create_subscription) is currently placed after early returns that prevent it
from running for normal forecasts; extract these checks into a helper (e.g.,
ensure_auto_follow_subscriptions(created, user, question)) or move the entire
block to execute immediately when created is True (before the early returns),
and use the same existing_subscription and create_subscription calls to add
CP_CHANGE, NEW_COMMENTS, MILESTONE, and STATUS_CHANGE subscriptions only if they
don't already exist.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@lsabor can you please check that? It might not be for every forecast but still looked like it can block subscriptions for some cases.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Seems like the humans are having a chat. I'll hop back into my burrow for now. If you need me again, just tag @coderabbitai in a new comment, and I'll come hopping out!

Comment on lines +294 to +343
existing_subscription = post.subscriptions.filter(user=user).exclude(
is_global=True
)
if (
user.follow_notify_cp_change_threshold
and not existing_subscription.filter(
type=PostSubscription.SubscriptionType.CP_CHANGE
).exists()
):
create_subscription(
subscription_type=PostSubscription.SubscriptionType.CP_CHANGE,
user=user,
post=post,
cp_change_threshold=user.follow_notify_cp_change_threshold,
)
if (
user.follow_notify_comments_frequency
and not existing_subscription.filter(
type=PostSubscription.SubscriptionType.NEW_COMMENTS
).exists()
):
create_subscription(
subscription_type=PostSubscription.SubscriptionType.NEW_COMMENTS,
user=user,
post=post,
comments_frequency=user.follow_notify_comments_frequency,
)
if (
user.follow_notify_milestone_step
and not existing_subscription.filter(
type=PostSubscription.SubscriptionType.MILESTONE
).exists()
):
create_subscription(
subscription_type=PostSubscription.SubscriptionType.MILESTONE,
user=user,
post=post,
milestone_step=user.follow_notify_milestone_step,
)
if (
user.follow_notify_on_status_change
and not existing_subscription.filter(
type=PostSubscription.SubscriptionType.STATUS_CHANGE
).exists()
):
create_subscription(
subscription_type=PostSubscription.SubscriptionType.STATUS_CHANGE,
user=user,
post=post,
)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

# First, find the PostSubscription model definition
find . -type f -name "*.py" | xargs grep -l "class PostSubscription" | head -5

Repository: Metaculus/metaculus

Length of output: 80


🏁 Script executed:

# Find the posts/services/subscriptions.py file mentioned in the review
find . -type f -path "*/posts/services/subscriptions.py"

Repository: Metaculus/metaculus

Length of output: 96


🏁 Script executed:

# Also check the questions/services/forecasts.py file structure
find . -type f -path "*/questions/services/forecasts.py" | head -1

Repository: Metaculus/metaculus

Length of output: 96


🏁 Script executed:

# Check the PostSubscription model for unique constraints
cat -n ./posts/models.py | grep -A 30 "class PostSubscription"

Repository: Metaculus/metaculus

Length of output: 1581


🏁 Script executed:

# Check the full create_subscription function in posts/services/subscriptions.py
cat -n ./posts/services/subscriptions.py | head -100

Repository: Metaculus/metaculus

Length of output: 4471


🏁 Script executed:

# Get more of the PostSubscription model to see constraints
cat -n ./posts/models.py | sed -n '960,1010p'

Repository: Metaculus/metaculus

Length of output: 2406


🏁 Script executed:

# Find the create_subscription function definition
grep -n "def create_subscription" ./posts/services/subscriptions.py

Repository: Metaculus/metaculus

Length of output: 321


🏁 Script executed:

# Check the main create_subscription function
cat -n ./posts/services/subscriptions.py | sed -n '539,600p'

Repository: Metaculus/metaculus

Length of output: 1900


🏁 Script executed:

# Check the factory functions to see if they use create() or get_or_create()
cat -n ./posts/services/subscriptions.py | sed -n '468,490p'

Repository: Metaculus/metaculus

Length of output: 704


🏁 Script executed:

# Also check create_subscription_new_comments
cat -n ./posts/services/subscriptions.py | sed -n '452,467p'

Repository: Metaculus/metaculus

Length of output: 558


🏁 Script executed:

# And create_subscription_status_change
cat -n ./posts/services/subscriptions.py | sed -n '508,518p'

Repository: Metaculus/metaculus

Length of output: 389


🏁 Script executed:

# Look at the context around lines 294-343 in questions/services/forecasts.py
cat -n ./questions/services/forecasts.py | sed -n '280,350p'

Repository: Metaculus/metaculus

Length of output: 3097


🏁 Script executed:

# Find what function contains this subscription creation code
grep -B 30 "user.follow_notify_cp_change_threshold" ./questions/services/forecasts.py | head -50

Repository: Metaculus/metaculus

Length of output: 1516


🏁 Script executed:

# Find the function containing this subscription creation code
grep -B 50 "if created and user.automatically_follow_on_predict" ./questions/services/forecasts.py | grep "^def " | tail -1

Repository: Metaculus/metaculus

Length of output: 45


🏁 Script executed:

# Get the full function signature and check for transaction handling
cat -n ./questions/services/forecasts.py | sed -n '200,295p' | grep -A 1 "^.*def "</parameter>

Repository: Metaculus/metaculus

Length of output: 250


🏁 Script executed:

# More precise: look for def around line 250-280
cat -n ./questions/services/forecasts.py | sed -n '200,300p'

Repository: Metaculus/metaculus

Length of output: 4553


🏁 Script executed:

# Check if update_forecast_notification has transaction decorator
cat -n ./questions/services/forecasts.py | sed -n '230,245p'

Repository: Metaculus/metaculus

Length of output: 838


🏁 Script executed:

# Find where update_forecast_notification is called from
grep -n "update_forecast_notification" ./questions/services/forecasts.py

Repository: Metaculus/metaculus

Length of output: 262


🏁 Script executed:

# Check the create_forecast function for transaction handling
grep -B 10 "update_forecast_notification" ./questions/services/forecasts.py | grep -E "(def |@transaction|atomic)" | head -20

Repository: Metaculus/metaculus

Length of output: 174


🏁 Script executed:

# Check the create_forecast function around line 172 for transaction handling
cat -n ./questions/services/forecasts.py | sed -n '100,180p'

Repository: Metaculus/metaculus

Length of output: 3409


🏁 Script executed:

# Search for transaction decorators in the file
grep -n "@transaction\|@atomic\|atomic(" ./questions/services/forecasts.py

Repository: Metaculus/metaculus

Length of output: 97


🏁 Script executed:

# Check what transaction.atomic() is being used for at line 494
cat -n ./questions/services/forecasts.py | sed -n '485,510p'

Repository: Metaculus/metaculus

Length of output: 1275


🏁 Script executed:

# Check if create_forecast_bulk has any transaction handling
cat -n ./questions/services/forecasts.py | sed -n '160,175p'

Repository: Metaculus/metaculus

Length of output: 676


🏁 Script executed:

# Look for imports of transaction at the top of the file
head -50 ./questions/services/forecasts.py | grep -i transaction

Repository: Metaculus/metaculus

Length of output: 96


Use get_or_create to make subscription creation idempotent.

The code checks for existing subscriptions with .exists() then calls create_subscription() which invokes .objects.create() directly. PostSubscription has a unique constraint on (type, user_id, post_id, is_global). Two concurrent forecast submissions can both pass the existence check and race to insert, causing one to fail with IntegrityError after the forecast row has already been persisted.

Replace the existence checks and create_subscription() calls with get_or_create() logic in the subscription factory functions, or use ignore_conflicts=True in a bulk operation, to eliminate the race.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@questions/services/forecasts.py` around lines 294 - 343, The subscription
creation is racy because you check existence then call create_subscription which
uses direct .create(), risking IntegrityError under concurrent requests; update
the logic so create_subscription (or the underlying factory) uses get_or_create
(or update_or_create) keyed on (type, user, post, is_global) or wraps the create
in a try/except that handles IntegrityError by returning the existing
PostSubscription; specifically modify create_subscription (and any factory it
calls) to call PostSubscription.objects.get_or_create(...) for
PostSubscription.SubscriptionType values (CP_CHANGE, NEW_COMMENTS, MILESTONE,
STATUS_CHANGE) instead of relying on the external exists() checks referenced by
existing_subscription.

Comment on lines +6 to +23
def migrate(apps, schema_editor):
# automatically follow on predict should be set to false for existing users
User = apps.get_model("users", "User")
User.objects.all().update(automatically_follow_on_predict=False)


class Migration(migrations.Migration):

dependencies = [
("users", "0018_add_auth_revoked_at"),
]

operations = [
migrations.AddField(
model_name="user",
name="automatically_follow_on_predict",
field=models.BooleanField(default=True),
),
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot Mar 31, 2026

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# First, locate and examine the migration file
find . -name "0019_user_automatically_follow_on_predict_and_more.py" -type f

Repository: Metaculus/metaculus

Length of output: 135


🏁 Script executed:

# Also search for the users migrations directory to understand context
fd "users.*migrations" -t d | head -5

Repository: Metaculus/metaculus

Length of output: 45


🏁 Script executed:

# Look at the users migrations directory
ls -la users/migrations/ | grep -E "^-.*\.py$" | tail -20

Repository: Metaculus/metaculus

Length of output: 1715


🏁 Script executed:

cat -n ./users/migrations/0019_user_automatically_follow_on_predict_and_more.py

Repository: Metaculus/metaculus

Length of output: 2255


🏁 Script executed:

# Search for related PRs or issues mentioning this migration or field
rg "automatically_follow_on_predict" --type py -l | head -20

Repository: Metaculus/metaculus

Length of output: 202


🏁 Script executed:

# Check the User model to see the field definition
rg "automatically_follow_on_predict" -A 3 -B 1 users/models.py

Repository: Metaculus/metaculus

Length of output: 303


🏁 Script executed:

# Search for any follow-up PR numbers or related context
rg "4507|4499" --type py,md | head -20

Repository: Metaculus/metaculus

Length of output: 92


🏁 Script executed:

# Search for any follow-up PR references or TODO comments related to this field
rg "automatically_follow_on_predict" -B 5 -A 5 --type py | grep -E "(TODO|FIXME|follow-up|PR|4[0-9]{3})"

Repository: Metaculus/metaculus

Length of output: 45


🏁 Script executed:

# Check git log for this migration to see if there's a follow-up commit
cd . && git log --oneline --all -- users/migrations/0019_user_automatically_follow_on_predict_and_more.py | head -10

Repository: Metaculus/metaculus

Length of output: 77


🏁 Script executed:

# Search for any references to the migration or field in PR/issue context
rg "RunPython" users/migrations/0019_user_automatically_follow_on_predict_and_more.py

Repository: Metaculus/metaculus

Length of output: 45


🏁 Script executed:

# Check how the field is used in the forecasts service
rg "automatically_follow_on_predict" -B 3 -A 3 questions/services/forecasts.py

Repository: Metaculus/metaculus

Length of output: 279


🏁 Script executed:

# Check the commit that touches this migration
git log --pretty=format:"%h %s" --all -- users/migrations/0019_user_automatically_follow_on_predict_and_more.py | head -5

Repository: Metaculus/metaculus

Length of output: 77


🏁 Script executed:

# Check if there's a subsequent migration that might handle this
ls -la users/migrations/ | grep "^-" | tail -5

Repository: Metaculus/metaculus

Length of output: 478


🏁 Script executed:

# Search for any RunPython usage in user migrations
rg "RunPython" users/migrations/ | head -10

Repository: Metaculus/metaculus

Length of output: 577


🏁 Script executed:

# Check the git diff or any follow-up changes to understand intent
git log -p --all -S "automatically_follow_on_predict" -- users/migrations/ | head -100

Repository: Metaculus/metaculus

Length of output: 45


🏁 Script executed:

# Check the commit details to see if there's any mention of follow-up
git show 7928048d --quiet || git show 7928048d | head -50

Repository: Metaculus/metaculus

Length of output: 202


🏁 Script executed:

# Search for any TODO, FIXME, or comments about deferred backfill in this migration
cat -n ./users/migrations/0019_user_automatically_follow_on_predict_and_more.py | grep -E "(TODO|FIXME|defer|follow-up|PR)"

Repository: Metaculus/metaculus

Length of output: 45


🏁 Script executed:

# Check if there's a follow-up migration file or any mention in recent PRs
fd "0020" users/migrations/

Repository: Metaculus/metaculus

Length of output: 45


🏁 Script executed:

# Let me check one of the other migrations to see the exact pattern
cat -n users/migrations/0002_user_is_onboarding_complete.py

Repository: Metaculus/metaculus

Length of output: 1011


🏁 Script executed:

# Also check 0017 to see another example
cat -n users/migrations/0017_user_is_primary_bot_user_owner_and_more.py | head -50

Repository: Metaculus/metaculus

Length of output: 2223


🏁 Script executed:

# Verify the RunPython pattern in 0017 fully
cat -n users/migrations/0017_user_is_primary_bot_user_owner_and_more.py | tail -30

Repository: Metaculus/metaculus

Length of output: 1545


Add the missing RunPython step to wire the migration helper into operations.

The migrate() function is defined but never executed because it is not registered in operations. Without migrations.RunPython(migrate, migrations.RunPython.noop), the AddField(default=True) will backfill automatically_follow_on_predict=True for all existing users, inverting the intent stated in the helper comment ("automatically follow on predict should be set to false for existing users").

This pattern is established throughout the user migrations: 0002_user_is_onboarding_complete.py and 0017_user_is_primary_bot_user_owner_and_more.py both follow the same structure—define a migration function, then wire it with migrations.RunPython() in operations.

Suggested fix
     operations = [
         migrations.AddField(
             model_name="user",
             name="automatically_follow_on_predict",
             field=models.BooleanField(default=True),
         ),
+        migrations.RunPython(migrate, migrations.RunPython.noop),
         migrations.AddField(
             model_name="user",
             name="follow_notify_comments_frequency",
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@users/migrations/0019_user_automatically_follow_on_predict_and_more.py`
around lines 6 - 23, The migrate() function that sets existing users'
automatically_follow_on_predict=False is defined but not executed; add a
migrations.RunPython step to operations so the backfill runs before/after the
AddField. Update the Migration.operations list to include
migrations.RunPython(migrate, migrations.RunPython.noop) (referencing the
migrate function name) alongside the existing migrations.AddField for
model_name="user" name="automatically_follow_on_predict" to ensure existing rows
are set to False instead of being backfilled to True.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Seems like the humans are having a chat. I'll hop back into my burrow for now. If you need me again, just tag @coderabbitai in a new comment, and I'll come hopping out!

Comment thread users/serializers.py
Comment on lines +191 to +196
"automatically_follow_on_predict",
"follow_notify_cp_change_threshold",
"follow_notify_comments_frequency",
"follow_notify_milestone_step",
"follow_notify_on_status_change",
"metaculus_news_subscription",
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Validate the new follow-default fields before saving them.

These settings are now writable through the profile API, but the serializer accepts any integer/float/null for them. That means clients can persist invalid defaults like follow_notify_comments_frequency=0 or follow_notify_milestone_step=1.5, and those bad values will later be copied into new subscriptions. Please add explicit serializer validation or model validators for the allowed ranges/presets.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@users/serializers.py` around lines 191 - 196, The serializer in
users/serializers.py that exposes the follow-default fields
("automatically_follow_on_predict", "follow_notify_cp_change_threshold",
"follow_notify_comments_frequency", "follow_notify_milestone_step",
"follow_notify_on_status_change", "metaculus_news_subscription") must validate
incoming values before save: replace or augment the current writable field
definitions with explicit DRF validators (e.g. serializers.IntegerField with
min_value and max_value or serializers.ChoiceField for preset enums) or add
model validators on the corresponding model fields; ensure numeric fields reject
0 and non-integers (e.g. use IntegerField to prevent 1.5), allow null only when
intended, and include clear error messages so invalid values are rejected by the
ProfileSerializer (or the serializer class in users/serializers.py) before
persisting.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (1)
front_end/src/app/(main)/accounts/settings/notifications/components/email_notifications.tsx (1)

173-179: ⚠️ Potential issue | 🟠 Major

Race condition: savingDefaultField is never cleared after save completes.

updateDefaultFollow sets savingDefaultField but never resets it to null after runDefaultFollowUpdate finishes. This means:

  1. The pending indicator may stay visible indefinitely after isDefaultFollowPending becomes false
  2. Rapid clicks on different fields can issue overlapping writes where an earlier slow response could overwrite a later user selection

Consider either:

  • Awaiting the action and clearing state in a finally block, or
  • Disabling the controls while any default-follow save is pending
🔧 Suggested fix pattern
 const updateDefaultFollow = useCallback(
-  (field: string, updates: Parameters<typeof updateProfileAction>[0]) => {
+  async (field: string, updates: Parameters<typeof updateProfileAction>[0]) => {
     setSavingDefaultField(field);
-    runDefaultFollowUpdate(updates);
+    try {
+      await runDefaultFollowUpdate(updates);
+    } finally {
+      setSavingDefaultField(null);
+    }
   },
   [runDefaultFollowUpdate]
 );
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@front_end/src/app/`(main)/accounts/settings/notifications/components/email_notifications.tsx
around lines 173 - 179, The callback updateDefaultFollow sets savingDefaultField
but never clears it, causing a stuck pending indicator and possible race
overwrites; change updateDefaultFollow (and/or runDefaultFollowUpdate usage) to
await the save action and clear setSavingDefaultField(null) in a finally block
(or alternatively disable controls when isDefaultFollowPending is true) so
savingDefaultField is reset after the save completes; reference the
savingDefaultField state, updateDefaultFollow function, and
runDefaultFollowUpdate action when making this change.
🧹 Nitpick comments (1)
front_end/src/app/(main)/accounts/settings/notifications/components/email_notifications.tsx (1)

101-104: Consider reverting optimistic state on error.

handleNewsChange updates local state optimistically before the server action completes. If updateProfileAction fails, the UI will show the new state but the server will retain the old value.

💡 Pattern for reverting on error
 const handleNewsChange = useCallback(async (checked: boolean) => {
+  const previousValue = newsSubscribed;
   setNewsSubscribed(checked);
-  await updateProfileAction({ metaculus_news_subscription: checked });
+  try {
+    await updateProfileAction({ metaculus_news_subscription: checked });
+  } catch {
+    setNewsSubscribed(previousValue);
+  }
 }, []);

Note: A similar pattern could apply to handleAutoFollowChange at lines 139-142.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@front_end/src/app/`(main)/accounts/settings/notifications/components/email_notifications.tsx
around lines 101 - 104, handleNewsChange currently sets local state
optimistically via setNewsSubscribed(checked) before awaiting
updateProfileAction; change it to save the previous value, call
updateProfileAction first (or await it) and only setNewsSubscribed(checked) on
success, and if the action throws revert to the saved previous value (or set it
in a catch block); apply the same pattern to handleAutoFollowChange (preserve
previous auto-follow state, await updateProfileAction, then update local state
or revert on error) so the UI reflects server state on failure.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@front_end/messages/pt.json`:
- Around line 678-680: The CP-change size labels use masculine singular
Portuguese forms but must be feminine plural to agree with surrounding words
like "alterações"/"mudanças"; update the translation values for the keys
cpChangeSmall, cpChangeMedium, and cpChangeLarge to the feminine plural forms
("pequenas", "médias", "grandes") so the CP-change selector and sentence
templates render correctly.

---

Duplicate comments:
In
`@front_end/src/app/`(main)/accounts/settings/notifications/components/email_notifications.tsx:
- Around line 173-179: The callback updateDefaultFollow sets savingDefaultField
but never clears it, causing a stuck pending indicator and possible race
overwrites; change updateDefaultFollow (and/or runDefaultFollowUpdate usage) to
await the save action and clear setSavingDefaultField(null) in a finally block
(or alternatively disable controls when isDefaultFollowPending is true) so
savingDefaultField is reset after the save completes; reference the
savingDefaultField state, updateDefaultFollow function, and
runDefaultFollowUpdate action when making this change.

---

Nitpick comments:
In
`@front_end/src/app/`(main)/accounts/settings/notifications/components/email_notifications.tsx:
- Around line 101-104: handleNewsChange currently sets local state
optimistically via setNewsSubscribed(checked) before awaiting
updateProfileAction; change it to save the previous value, call
updateProfileAction first (or await it) and only setNewsSubscribed(checked) on
success, and if the action throws revert to the saved previous value (or set it
in a catch block); apply the same pattern to handleAutoFollowChange (preserve
previous auto-follow state, await updateProfileAction, then update local state
or revert on error) so the UI reflects server state on failure.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 9b25e10d-2703-4272-b680-d9800ef53b03

📥 Commits

Reviewing files that changed from the base of the PR and between 7928048 and 86ed862.

📒 Files selected for processing (9)
  • front_end/messages/cs.json
  • front_end/messages/en.json
  • front_end/messages/es.json
  • front_end/messages/pt.json
  • front_end/messages/zh-TW.json
  • front_end/messages/zh.json
  • front_end/src/app/(main)/accounts/settings/notifications/components/email_notifications.tsx
  • front_end/src/components/post_subscribe/subscription_types_customisation/subscription_cp_change.tsx
  • front_end/src/components/ui/button_group.tsx
🚧 Files skipped from review as they are similar to previous changes (3)
  • front_end/messages/es.json
  • front_end/messages/zh-TW.json
  • front_end/messages/zh.json

Comment thread front_end/messages/pt.json Outdated
Copy link
Copy Markdown
Contributor

@ncarazon ncarazon left a comment

Choose a reason for hiding this comment

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

LGTM with suggestions

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (1)
front_end/src/app/(main)/accounts/settings/notifications/components/email_notifications.tsx (1)

424-424: Type assertion assumes server data matches expected values.

If commentsFreq contains an unexpected value from the server (e.g., 5), the ButtonGroup won't match any button and may behave unexpectedly. The same applies to milestoneStep at line 469.

Consider adding a fallback or validation to handle unexpected values gracefully.

🛡️ Optional defensive approach
-                    value={String(commentsFreq) as "1" | "3" | "10"}
+                    value={
+                      commentsFreq === 1 || commentsFreq === 3 || commentsFreq === 10
+                        ? (String(commentsFreq) as "1" | "3" | "10")
+                        : "10" // fallback to default
+                    }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@front_end/src/app/`(main)/accounts/settings/notifications/components/email_notifications.tsx
at line 424, The ButtonGroup value currently asserts commentsFreq as "1" | "3" |
"10" (and similarly milestoneStep) which breaks if the server returns an
unexpected value; update the component to validate/map the incoming value
(commentsFreq and milestoneStep) to one of the accepted string options before
passing to value, e.g., convert to string and check against the allowed set
["1","3","10"] and fall back to a safe default (or nearest allowed choice) when
it isn’t present; implement this validation where commentsFreq and milestoneStep
are read or passed to the ButtonGroup so the component always receives a
guaranteed valid value.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@front_end/messages/es.json`:
- Line 657: Replace the literal value for the "autoFollow" key in
front_end/messages/es.json with a more natural Spanish label; change
"Auto-seguir:" to either "Seguimiento automático:" or "Seguir automáticamente:"
(pick one consistent with surrounding UI tone) so the key "autoFollow" displays
a native-sounding phrase.

In
`@front_end/src/app/`(main)/accounts/settings/notifications/components/email_notifications.tsx:
- Around line 524-532: The saved indicator is shown whenever isPending
transitions false regardless of success; modify the logic to only show the saved
state when the server action actually succeeded by wiring an error/success
signal from useServerAction into the effect. Concretely, expose the action's
error (or success boolean) alongside isPending from useServerAction, pass that
state into SwitchRow (or the component containing the effect), and change the
useEffect to check that wasPending.current was true AND there is no error (or
success === true) before calling setShowSaved(true); keep the existing
timer/cleanup logic unchanged.

---

Nitpick comments:
In
`@front_end/src/app/`(main)/accounts/settings/notifications/components/email_notifications.tsx:
- Line 424: The ButtonGroup value currently asserts commentsFreq as "1" | "3" |
"10" (and similarly milestoneStep) which breaks if the server returns an
unexpected value; update the component to validate/map the incoming value
(commentsFreq and milestoneStep) to one of the accepted string options before
passing to value, e.g., convert to string and check against the allowed set
["1","3","10"] and fall back to a safe default (or nearest allowed choice) when
it isn’t present; implement this validation where commentsFreq and milestoneStep
are read or passed to the ButtonGroup so the component always receives a
guaranteed valid value.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: dc9fcc6b-9b7e-4fe5-bef7-10ae6eebff14

📥 Commits

Reviewing files that changed from the base of the PR and between d261a03 and 3ee791d.

📒 Files selected for processing (7)
  • front_end/messages/cs.json
  • front_end/messages/en.json
  • front_end/messages/es.json
  • front_end/messages/pt.json
  • front_end/messages/zh-TW.json
  • front_end/messages/zh.json
  • front_end/src/app/(main)/accounts/settings/notifications/components/email_notifications.tsx
✅ Files skipped from review due to trivial changes (1)
  • front_end/messages/en.json
🚧 Files skipped from review as they are similar to previous changes (3)
  • front_end/messages/pt.json
  • front_end/messages/zh-TW.json
  • front_end/messages/zh.json

Comment thread front_end/messages/es.json
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
front_end/src/hooks/use_server_action.ts (1)

21-34: ⚠️ Potential issue | 🔴 Critical

Reset finished/result per invocation to prevent hung promises and stale resolution.

At Line 23, hasError is reset, but finished and result are not. After the first run, finished stays true; on later runs (especially when action fails or returns the same value), the effect at Lines 13-19 may not fire, so the promise from Lines 37-39 can stay pending. This also allows stale result to be emitted on failed runs.

💡 Proposed fix
   const runAction = async (...args: P): Promise<R | undefined> => {
     startTransition(() => {
+      setFinished(false);
       setHasError(false);
+      setResult(undefined);
       action(...args)
         .then((data) => {
           setResult(data);
         })
         .catch((err) => {
           setHasError(true);
+          setResult(undefined);
           console.error(err);
         })
         .finally(() => {
           setFinished(true);
         });
     });

Also applies to: 37-39

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@front_end/src/hooks/use_server_action.ts` around lines 21 - 34, The runAction
function currently only resets hasError before invoking action, leaving finished
and result stale; update runAction to reset setFinished(false) and clear
previous result via setResult(undefined) (in addition to setHasError(false))
before calling action so each invocation starts fresh, and apply the same reset
(setFinished(false) and setResult(undefined)) in the other invocation block
referenced around lines 37-39; keep existing then/catch/finally logic but ensure
finally still sets setFinished(true).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@front_end/src/hooks/use_server_action.ts`:
- Around line 21-34: The runAction function currently only resets hasError
before invoking action, leaving finished and result stale; update runAction to
reset setFinished(false) and clear previous result via setResult(undefined) (in
addition to setHasError(false)) before calling action so each invocation starts
fresh, and apply the same reset (setFinished(false) and setResult(undefined)) in
the other invocation block referenced around lines 37-39; keep existing
then/catch/finally logic but ensure finally still sets setFinished(true).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 80ec0164-c4eb-4fee-9319-61693946023c

📥 Commits

Reviewing files that changed from the base of the PR and between 3ee791d and 43e2df1.

📒 Files selected for processing (2)
  • front_end/src/app/(main)/accounts/settings/notifications/components/email_notifications.tsx
  • front_end/src/hooks/use_server_action.ts
✅ Files skipped from review due to trivial changes (1)
  • front_end/src/app/(main)/accounts/settings/notifications/components/email_notifications.tsx

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.

Default question follow settings and notification settings reorganisation

3 participants