Skip to content

fix(notifications): handle the whole follow family, not just follow - #1708

Merged
feruzm merged 3 commits into
developfrom
fix/follow-notification-gates
Sep 1, 2026
Merged

fix(notifications): handle the whole follow family, not just follow#1708
feruzm merged 3 commits into
developfrom
fix/follow-notification-gates

Conversation

@feruzm

@feruzm feruzm commented Sep 1, 2026

Copy link
Copy Markdown
Member

Part of the follow notification outage fix. Backend counterpart: ecency/enotify-py#20.

Problem

enotify's str_activity_type() emits four wire types for the follow family: follow, unfollow, ignore and blacklist. They all share ACTIVITY_MAIN_TYPE_FOLLOW server side. The client only knew about "follow", so the other three were mishandled at every gate:

Gate Behaviour for unfollow / ignore / blacklist
getBody() no branch, returns "", and the delivery path short-circuits on a falsy body, so the toast was dropped entirely
getLink() no branch, fell to default, so no destination at all
getNotificationType() returned null, which the delivery gate reads as always-allowed, so turning the Follows toggle off did not silence them
WsFollowNotification["type"] narrowed to "follow", so those branches could not be added without widening the union first

blacklist never reached its own component either: notification-list-item routed only follow/unfollow/ignore to NotificationFollowType, so it hit the unknown-type fallback and rendered the raw untranslated word blacklist.

This has been latent rather than visible, because the backend stopped producing any follow-family rows on 2026-06-26. It becomes visible the moment #20 ships.

Changes

  • notifications-ws-api.ts: unfollow / ignore / blacklist cases in getBody, getLink (all four go to the actor's profile) and getNotificationType (all four map to NotifyTypes.FOLLOW)
  • ws-notifications.ts and the SDK's notification.ts: widen WsFollowNotification["type"] to the four; add "blacklist" to ApiFollowNotification["type"], matching the API serializer that now returns follower/following for it
  • notification-follow-type.tsx / notification-list-item.tsx: render blacklist through the follow component instead of the raw-word fallback
  • New strings: notification.unfollowed, notification.ignored, notification.blacklisted, notifications.blacklisted-str

Not changed

push-notification-link.ts and firebase-messaging-sw.js already route follow/unfollow/ignore to the source profile. blacklist is deliberately absent from both: it has no push template server side, so it is never enqueued.

Tests

Extended notifications-ws-notify-type-mapping.spec.ts and notifications-ws-link.spec.ts to pin the whole family across all three tables, including a case asserting getBody is non-empty for each, since an empty body is what silently drops a notification.

Verified by mutation: dropping unfollow/ignore/blacklist from getNotificationType fails the new case.

pnpm test 3681 passed, pnpm typecheck clean, pnpm lint clean.

Summary by CodeRabbit

  • New Features

    • Added notifications for unfollow, ignore, and blacklist actions.
    • Blacklist notifications now display a dedicated label and link to the actor’s profile.
    • These notification types are included under the Follows notification preference.
  • Bug Fixes

    • Prevented newly supported notification types from appearing as generic or always-allowed notifications.
  • Tests

    • Added coverage for notification links, localized messages, and preference mapping.

enotify's str_activity_type() emits follow, unfollow, ignore and blacklist over
the websocket, and all four share ACTIVITY_MAIN_TYPE_FOLLOW. The client only
knew about "follow", so the other three were mishandled at every gate:

- getBody() had no branch, returning "", and the delivery path short-circuits on
  a falsy body, so the toast was dropped entirely
- getLink() had no branch, so they fell to the default and produced no
  destination at all
- getNotificationType() returned null, which the delivery gate reads as
  always-allowed, so turning the Follows toggle off did not silence them
- WsFollowNotification.type was narrowed to "follow", so those branches could
  not be added without widening the union first

blacklist also never reached its own component: notification-list-item routed
only follow/unfollow/ignore to NotificationFollowType, so it hit the unknown
type fallback and rendered the raw untranslated word "blacklist".

ApiFollowNotification gains "blacklist" to match, since the API serializer now
returns follower/following for it.

New strings: notification.unfollowed, notification.ignored,
notification.blacklisted and notifications.blacklisted-str.

The push tables (push-notification-link.ts, firebase-messaging-sw.js) already
route follow/unfollow/ignore to the source profile and are unchanged. blacklist
is deliberately absent there because it has no push template server side.

Specs pin the family across all three tables. Verified by mutation: dropping
unfollow/ignore/blacklist from getNotificationType fails the new case.
@qodo-code-review

Copy link
Copy Markdown

ⓘ Qodo reviews are paused because your trial has ended. Ask your workspace admin to add credits to resume reviews. Manage billing

@qodo-free-for-open-source-projects

Copy link
Copy Markdown

PR Summary by Qodo

Handle all follow-family notification types

🐞 Bug fix 🧪 Tests 🕐 20-40 Minutes

Grey Divider

AI Description

• Deliver unfollow, ignore, and blacklist notifications with localized bodies and profile links.
• Apply the Follows preference consistently across every follow-family wire type.
• Type and render blacklist notifications correctly, with regression coverage for all gates.
Diagram

graph TD
  A["Follow-family event"] --> B["Notification contracts"] --> C["WebSocket handler"] --> D{"Follows enabled?"} -->|Yes| E["Localized toast"]
  C --> F["Actor profile"]
  B --> G["List router"] --> H["Follow renderer"]
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Central follow-family notification registry
  • ➕ Defines body keys, link behavior, and preference mappings in one place.
  • ➕ Reduces the chance that future wire types are added to only some gates.
  • ➖ Broadens a targeted outage fix into a notification-system refactor.
  • ➖ Requires resolving package boundaries between SDK types, runtime handlers, and UI rendering.

Recommendation: Use the PR's explicit gate-by-gate fix because it is low-risk, preserves distinct action text, and directly restores compatibility with the backend contract. A centralized notification registry could prevent future table drift, but it should be considered separately rather than delaying this focused fix.

Files changed (8) +77 / -6

Bug fix (6) +38 / -6
notifications-ws-api.tsProcess every follow-family websocket activity +16/-0

Process every follow-family websocket activity

• Adds localized bodies for unfollow, ignore, and blacklist so delivery no longer drops them. Routes all four follow-family activities to the actor profile and maps them to the Follows preference gate.

apps/web/src/api/notifications-ws-api.ts

ws-notifications.tsExpand application follow notification unions +6/-2

Expand application follow notification unions

• Widens websocket and API follow notification contracts to include unfollow, ignore, and blacklist where applicable. This aligns application types with the backend's serialized activity types.

apps/web/src/entities/ws-notifications.ts

en-US.jsonAdd follow-family notification translations +4/-0

Add follow-family notification translations

• Adds English websocket body strings for unfollow, ignore, and blacklist. Also adds the blacklist action label used by notification-list rows.

apps/web/src/features/i18n/locales/en-US.json

notification-list-item.tsxRoute blacklist rows through the follow renderer +3/-2

Route blacklist rows through the follow renderer

• Recognizes blacklist as a follow-family notification and sends it to NotificationFollowType. Excludes blacklist from the unknown-type fallback that previously displayed its raw wire value.

apps/web/src/features/shared/notifications/notification-list-item.tsx

notification-follow-type.tsxRender localized blacklist actions +3/-0

Render localized blacklist actions

• Adds a blacklist branch to the follow-family notification component using the new translated action label.

apps/web/src/features/shared/notifications/notification-types/notification-follow-type.tsx

notification.tsAlign SDK follow contracts with backend wire types +6/-2

Align SDK follow contracts with backend wire types

• Expands SDK websocket and API follow notification unions to include the complete follow family, including blacklist.

packages/sdk/src/modules/notifications/types/notification.ts

Tests (2) +39 / -0
notifications-ws-link.spec.tsTest profile links for the complete follow family +8/-0

Test profile links for the complete follow family

• Adds parameterized coverage proving follow, unfollow, ignore, and blacklist all link to the source actor's profile.

apps/web/src/specs/api/notifications-ws-link.spec.ts

notifications-ws-notify-type-mapping.spec.tsTest follow-family delivery and preference gates +31/-0

Test follow-family delivery and preference gates

• Verifies every follow-family wire type maps to NotifyTypes.FOLLOW. Also asserts each activity receives the expected non-empty localized body so websocket delivery cannot silently discard it.

apps/web/src/specs/api/notifications-ws-notify-type-mapping.spec.ts

@qodo-free-for-open-source-projects

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (0) 📎 Requirement gaps (0)

Grey Divider

Great, no issues found!

Qodo reviewed your code and found no material issues that require review

Grey Divider

Tip of the day
💡 Did you know, you can describe a rule in plain language on the Rules page and Qodo drafts it for you

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

@coderabbitai

coderabbitai Bot commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

Next included review available in 19 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used the included review currently available.

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Learn how review limits work.

Review configuration:

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Team

Run ID: ba09357d-1741-4fcf-8b2c-7b04732e1d99

📥 Commits

Reviewing files that changed from the base of the PR and between 0d94088 and e694dd1.

📒 Files selected for processing (1)
  • apps/web/src/features/shared/notifications/_index.scss

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Team

Run ID: fe27b8ea-2661-4b6c-9332-481f4b428372

📥 Commits

Reviewing files that changed from the base of the PR and between aaa3ef1 and 0d94088.

📒 Files selected for processing (8)
  • apps/web/src/api/notifications-ws-api.ts
  • apps/web/src/entities/ws-notifications.ts
  • apps/web/src/features/i18n/locales/en-US.json
  • apps/web/src/features/shared/notifications/notification-list-item.tsx
  • apps/web/src/features/shared/notifications/notification-types/notification-follow-type.tsx
  • apps/web/src/specs/api/notifications-ws-link.spec.ts
  • apps/web/src/specs/api/notifications-ws-notify-type-mapping.spec.ts
  • packages/sdk/src/modules/notifications/types/notification.ts

Included review availability: Your plan provides up to 1 included review per hour; 0 remain after this review.


📝 Walkthrough

Walkthrough

The change adds unfollow, ignore, and blacklist support to notification types, WebSocket mapping, profile links, Follows filtering, localization, and notification rendering. Tests cover link resolution, toggle mapping, and localized bodies.

Changes

Follow-family notification support

Layer / File(s) Summary
Notification type contracts
packages/sdk/src/modules/notifications/types/notification.ts, apps/web/src/entities/ws-notifications.ts
The SDK and web notification types now accept follow, unfollow, ignore, and blacklist.
WebSocket notification mapping
apps/web/src/api/notifications-ws-api.ts, apps/web/src/specs/api/notifications-ws-*.spec.ts
The WebSocket API generates localized bodies, links follow-family notifications to actor profiles, and maps them to NotifyTypes.FOLLOW. Tests cover these mappings.
Notification rendering and localization
apps/web/src/features/shared/notifications/..., apps/web/src/features/i18n/locales/en-US.json
Blacklist notifications use the follow notification component and display new English message and label translations.

Estimated code review effort: 2 (Simple) | ~10 minutes

Merge Risk: ⚪ Minimal · up to 0d940

This change enables all follow-family notifications to render, link correctly, and respect the existing Follows preference without altering deployment or persistent state. No actionable merge-blocking risk remains after normal checks and review.

Poem

A rabbit saw new notices hop,
Unfollow, ignore, blacklist pop.
Profile links now point the way,
Follows filters guard the day,
Translated words make messages stay.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 1 functions across 7 files. (1 skipped: 1 … Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ 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 clearly summarizes the main change: notification handling now covers the full follow family, including follow, unfollow, ignore, and blacklist.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Full details: Docstring Coverage

Explanation

Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 1 functions across 7 files. (1 skipped: 1 unsupported.)

✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/follow-notification-gates

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.

.blacklist-label was rendered but never styled, so blacklist rows showed as
plain inline text while the other three in the family rendered as pills.

Uses blue-dark-grey rather than a brighter red: it is the darkest of the four,
matching that blacklist is the family's harshest member, and it clears AA at
12px. red-040 (#ec3323) is only 4.15:1 against white, and red-030 is already
ignore's.
@feruzm

feruzm commented Sep 1, 2026

Copy link
Copy Markdown
Member Author

Fixed in e694dd1.

.blacklist-label is now in the shared pill selector and has its own background.

On the colour: I went with bg-blue-dark-grey (#2e3d51) rather than a brighter red. Blacklist is the harshest member of the family, so it gets the darkest badge, which also keeps it clearly distinguishable from ignore. The obvious alternative, red-040 (#ec3323), only reaches 4.15:1 against white, under AA for 12px text, and red-030 is already ignore's. The chosen token clears AA comfortably.

pnpm lint clean, icon-scss-audit clean (0 unowned, 0 deleted-reappeared).

@feruzm feruzm added the patch Bug fixes and patches (1.0.0 → 1.0.1) label Sep 1, 2026
@feruzm
feruzm merged commit 26f3a74 into develop Sep 1, 2026
3 checks passed
@feruzm
feruzm deleted the fix/follow-notification-gates branch September 1, 2026 15:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

patch Bug fixes and patches (1.0.0 → 1.0.1)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant