Skip to content

Add notifications settings - #271

Open
isaistvo wants to merge 5 commits into
mainfrom
feature/notifications-preferences
Open

Add notifications settings#271
isaistvo wants to merge 5 commits into
mainfrom
feature/notifications-preferences

Conversation

@isaistvo

@isaistvo isaistvo commented Jun 23, 2026

Copy link
Copy Markdown
Collaborator

Closes #60

Summary by CodeRabbit

  • New Features
    • Added notification preferences with per-key toggles and persistent saved settings
    • Introduced real-time notifications via streaming updates, plus optional sound alerts with volume control (including “test sound”)
    • Added notification controls to account settings
    • Added post invitations and follow/unfollow actions, along with authenticated notification history/mark-as-read support
  • Documentation
    • Updated local development instructions and refreshed the app tab title

@coderabbitai

coderabbitai Bot commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 381f5a8e-6e7f-4120-8edb-5f5792cbf77a

📥 Commits

Reviewing files that changed from the base of the PR and between 2f20211 and de2f1ca.

📒 Files selected for processing (5)
  • frontend/src/hooks/useRealtimeNotifications.tsx
  • frontend/src/pages/guest/Settings/AccountSettingsPage.tsx
  • frontend/src/utils/audio.ts
  • internal/notification/repository/repository.go
  • internal/notification/service/service.go
🚧 Files skipped from review as they are similar to previous changes (5)
  • frontend/src/utils/audio.ts
  • internal/notification/service/service.go
  • internal/notification/repository/repository.go
  • frontend/src/hooks/useRealtimeNotifications.tsx
  • frontend/src/pages/guest/Settings/AccountSettingsPage.tsx

📝 Walkthrough

Walkthrough

Adds end-to-end notification preference management: a new PostgreSQL notification_preferences table with JSONB settings, repository upsert logic with merge, service-level event gating per preference, and HTTP GET/PUT endpoints. On the frontend, a new SSE hook (useRealtimeNotifications), Web Audio notification sound utility, updated NotificationBell, and new settings cards in AccountSettingsPage expose preference toggles and sound controls. The frontend package is also renamed from business-frontend to share-bite.

Changes

Notification Preferences Feature

Layer / File(s) Summary
DB schema and repository layer
migrations/20260622100000_notification_preferences.sql, internal/notification/repository/repository.go
Adds the notification_preferences table (UUID PK → auth.users, JSONB settings). Extends NotificationRepository interface and SQLRepository with GetPreferences/UpdatePreferences, including JSON upsert-merge via || operator.
Backend service gating and HTTP endpoints
internal/notification/service/service.go, internal/notification/handler/handler.go
ProcessMessage fetches recipient preferences and skips delivery when an EventType preference is explicitly false. GetPreferences seeds known keys defaulting to true then merges persisted values. UpdatePreferences validates keys. Two new routes (GET/PUT /preferences) registered and implemented.
Frontend API client — token threading and new methods
frontend/src/types/api.ts, frontend/src/api/client.ts, frontend/src/api/notifications.ts
CreatePostInput gains invitedCustomerIds. Existing apiClient methods accept optional bearer tokens. New authenticated methods added (follow/unfollow, invitations, lab auth, notification history/mark-read). fetchNotificationPreferences and updateNotificationPreferences helpers added.
SSE realtime hook and audio utility
frontend/src/hooks/useRealtimeNotifications.tsx, frontend/src/utils/audio.ts, frontend/tsconfig.app.json
New useRealtimeNotifications hook manages SSE connection lifecycle with auto-reconnect, prepending parsed LabNotification events. playNotificationSound synthesizes a tone via Web Audio API. tsconfig.app.json include glob broadened to src/**/*.
NotificationBell audio alert and AccountSettingsPage UI
frontend/src/components/Notifications/NotificationBell.tsx, frontend/src/pages/guest/Settings/AccountSettingsPage.tsx
NotificationBell plays a sound on new unread notifications, gated by localStorage flags. AccountSettingsPage adds a reusable Switch component and PREFERENCE_DETAILS lookup. Adds notification sound state (localStorage-backed), React Query fetch/mutation, and two new settings cards: Notification Preferences (per-key toggles filtered by role) and Notification Sounds (enable/volume/test button).

Frontend Rebrand and Cosmetic Cleanup

Layer / File(s) Summary
Package rename and Go whitespace cleanup
frontend/README.md, frontend/index.html, frontend/package.json, internal/business/handler/business/handler.go
README cd path, HTML <title>, and package.json name updated from business-frontend to share-bite/Share Bite. Minor whitespace cleanup in Go business handler.

Sequence Diagram(s)

sequenceDiagram
  participant Browser
  participant AccountSettingsPage
  participant NotificationAPI
  participant NotificationHandler
  participant NotificationService
  participant Repository

  Browser->>AccountSettingsPage: load settings page
  AccountSettingsPage->>NotificationAPI: fetchNotificationPreferences(token)
  NotificationAPI->>NotificationHandler: GET /api/notifications/preferences
  NotificationHandler->>NotificationService: GetPreferences(ctx, recipientID)
  NotificationService->>Repository: GetPreferences(ctx, recipientID)
  Repository-->>NotificationService: map[string]bool (from JSONB)
  NotificationService-->>NotificationHandler: merged defaults + stored prefs
  NotificationHandler-->>AccountSettingsPage: JSON preferences
  AccountSettingsPage-->>Browser: render Switch toggles

  Browser->>AccountSettingsPage: toggle preference
  AccountSettingsPage->>NotificationAPI: updateNotificationPreferences(token, prefs)
  NotificationAPI->>NotificationHandler: PUT /api/notifications/preferences
  NotificationHandler->>NotificationService: UpdatePreferences(ctx, recipientID, prefs)
  NotificationService->>Repository: UpdatePreferences (upsert JSONB)
  Repository-->>NotificationService: ok
  NotificationHandler-->>AccountSettingsPage: 204 No Content
  AccountSettingsPage-->>Browser: toast success
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 minutes

Possibly related PRs

  • ua-academy-projects/share-bite#192: This PR extends the notification handler, repository, and service introduced in #192 by adding preference persistence endpoints and per-event gating in ProcessMessage.
  • ua-academy-projects/share-bite#264: Both PRs modify the frontend real-time notifications stack, notably NotificationBell.tsx and useRealtimeNotifications hook, integrating notification UI/behavior changes.

Suggested reviewers

  • mblinovv
  • viktorzhabskyi
  • DmyMi

Poem

🐇 A rabbit hops in, bells ring and chime,
SSE streams flow, preferences in time,
A toggle for each sound, a switch for each note,
JSONB holds the keys this bunny wrote.
From business-frontend to Share Bite we go —
The carrots taste sweeter when notifications flow! 🥕

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Out of Scope Changes check ❓ Inconclusive Minor out-of-scope changes detected: frontend README working directory update and package.json renaming are unrelated to notification settings; HTML title and tsconfig include pattern changes are incidental. Consider whether README, package.json name, HTML title, and tsconfig changes align with project scope or should be moved to a separate PR for clarity.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title 'Add notifications settings' is clear and directly describes the main change—adding notification settings functionality—matching the primary objective.
Linked Issues check ✅ Passed The PR fully implements issue #60 requirements by adding notification preference management (API endpoints, database persistence, UI controls) and notification sound settings allowing users to tune both volume and relevance.

✏️ 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 feature/notifications-preferences

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.

@coderabbitai coderabbitai Bot left a comment

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.

Actionable comments posted: 5

🧹 Nitpick comments (1)
frontend/src/hooks/useRealtimeNotifications.tsx (1)

45-45: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Cap notification list growth in memory.

Line 45 prepends indefinitely; long sessions can accumulate a large array and degrade render performance. Keep only the latest N items.

♻️ Suggested change
-                    setNotifications((prev) => [data, ...prev]);
+                    setNotifications((prev) => [data, ...prev].slice(0, 200));
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@frontend/src/hooks/useRealtimeNotifications.tsx` at line 45, In the
setNotifications state update, the notifications array grows indefinitely by
prepending each new notification without any limit. This can cause memory issues
and performance degradation over long sessions. Modify the callback in
setNotifications to cap the array size by keeping only the latest N items (for
example, the most recent 100 or 1000 notifications). After prepending the new
data to the previous notifications, use array slicing to trim the array to the
desired maximum length, ensuring old notifications are discarded when the limit
is exceeded.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@frontend/src/hooks/useRealtimeNotifications.tsx`:
- Around line 34-35: Remove the access token from the query string in the
EventSource URL construction (line 34 where the url is built with the token
parameter) and instead pass the withCredentials option to the EventSource
constructor to enable cookie-based authentication. Remove any logging that
exposes token fragments from lines 38 and 52. Apply the same changes to the
buildNotificationsStreamUrl function in ui/src/api/business/notifications.ts.
The backend would need to support cookie-based or session-ticket authentication
instead of accepting tokens in query parameters.

In `@frontend/src/pages/guest/Settings/AccountSettingsPage.tsx`:
- Around line 105-131: The Switch component lacks proper accessibility
attributes needed for assistive technologies. Add role="switch" to the button
element to identify it as a switch control, and add aria-checked with the value
of the checked prop to expose the current state to screen readers. This will
ensure that users relying on assistive technologies can properly understand and
interact with the toggle switch at the Switch function level.

In `@frontend/src/utils/audio.ts`:
- Line 5: The AudioContext created with `new AudioContextClass()` at line 5 is
never closed after playback completes, which will exhaust the browser's limit on
concurrent AudioContext instances. Ensure that after the audio playback finishes
(by attaching a listener to the audio end event or after the playback duration),
call the close() method on the `ctx` AudioContext instance to properly release
the resource and prevent context exhaustion.

In `@internal/notification/repository/repository.go`:
- Around line 139-152: The UpdatePreferences method has a vulnerability where
marshaling a nil prefs map produces JSON null, which when merged via the JSONB
|| operator on line 151, can persist a non-object settings value that breaks
downstream map unmarshalling in read operations. Add a nil guard check at the
start of the UpdatePreferences method to validate the prefs parameter is not
nil, and if it is nil, initialize it as an empty map before proceeding with the
json.Marshal call to ensure settings always remains a valid JSON object in the
database.

In `@internal/notification/service/service.go`:
- Around line 149-150: The UpdatePreferences method at line 149 accepts
preference keys from the client without validation, which allows unsupported
keys to be persisted and potentially alter delivery behavior through direct key
lookups. Add validation in the UpdatePreferences method to ensure all keys in
the incoming prefs map are restricted to the known preference key set that is
used in GetPreferences. Only allow updates for valid, supported preference keys
before calling s.repo.UpdatePreferences.

---

Nitpick comments:
In `@frontend/src/hooks/useRealtimeNotifications.tsx`:
- Line 45: In the setNotifications state update, the notifications array grows
indefinitely by prepending each new notification without any limit. This can
cause memory issues and performance degradation over long sessions. Modify the
callback in setNotifications to cap the array size by keeping only the latest N
items (for example, the most recent 100 or 1000 notifications). After prepending
the new data to the previous notifications, use array slicing to trim the array
to the desired maximum length, ensuring old notifications are discarded when the
limit is exceeded.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: 2678a446-eb40-4370-99c5-2cb54142f1b2

📥 Commits

Reviewing files that changed from the base of the PR and between 39b3551 and 2f20211.

📒 Files selected for processing (16)
  • frontend/README.md
  • frontend/index.html
  • frontend/package.json
  • frontend/src/api/client.ts
  • frontend/src/api/notifications.ts
  • frontend/src/components/Notifications/NotificationBell.tsx
  • frontend/src/hooks/useRealtimeNotifications.tsx
  • frontend/src/pages/guest/Settings/AccountSettingsPage.tsx
  • frontend/src/types/api.ts
  • frontend/src/utils/audio.ts
  • frontend/tsconfig.app.json
  • internal/business/handler/business/handler.go
  • internal/notification/handler/handler.go
  • internal/notification/repository/repository.go
  • internal/notification/service/service.go
  • migrations/20260622100000_notification_preferences.sql

Comment on lines +34 to +35
const url = `/api/notifications/stream?access_token=${encodeURIComponent(token)}`;
const es = new EventSource(url);

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.

🔒 Security & Privacy | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# First, locate and examine the file
fd -t f "useRealtimeNotifications" --type f | head -5

Repository: ua-academy-projects/share-bite

Length of output: 305


🏁 Script executed:

# Get the file structure and read the relevant lines
cat -n frontend/src/hooks/useRealtimeNotifications.tsx | sed -n '30,60p'

Repository: ua-academy-projects/share-bite

Length of output: 1603


🏁 Script executed:

# Search for the /api/notifications/stream endpoint in backend
fd -t f "\.ts$" "\.tsx$" "\.js$" | xargs rg "notifications/stream" -l 2>/dev/null

Repository: ua-academy-projects/share-bite

Length of output: 399


🏁 Script executed:

# Check for backend auth patterns (cookies, auth middleware)
rg -t js -t ts "withCredentials|credentials.*include|cookie" --max-count=3 | head -20

Repository: ua-academy-projects/share-bite

Length of output: 168


🏁 Script executed:

# Read the backend notifications endpoint implementation
cat -n ui/src/api/business/notifications.ts | head -100

Repository: ua-academy-projects/share-bite

Length of output: 1845


🏁 Script executed:

# Search for API route definitions related to notifications/stream
rg "notifications.*stream" -A 10 -B 2

Repository: ua-academy-projects/share-bite

Length of output: 168


🏁 Script executed:

# Check for auth middleware patterns
rg "passport|auth.*middleware|withAuth|guards.*auth" -B 2 -A 5 | head -50

Repository: ua-academy-projects/share-bite

Length of output: 168


🏁 Script executed:

# Search for the stream endpoint implementation in backend
rg "notifications.*stream" -t ts -t js -B 5 -A 15

Repository: ua-academy-projects/share-bite

Length of output: 168


🏁 Script executed:

# Look for EventSource or SSE handling in backend
rg "EventSource|\.on\(|response\.write|stream.*auth" -t ts -t js | head -30

Repository: ua-academy-projects/share-bite

Length of output: 168


🏁 Script executed:

# Search for backend route definitions
fd -type f -name "*route*" -o -name "*controller*" | xargs rg "notifications" -l 2>/dev/null

Repository: ua-academy-projects/share-bite

Length of output: 1459


🏁 Script executed:

# Read the backend notification handler
cat -n internal/business/handler/notification/handler.go | head -150

Repository: ua-academy-projects/share-bite

Length of output: 2560


🏁 Script executed:

# Also check the other handler
cat -n internal/notification/handler/handler.go | head -150

Repository: ua-academy-projects/share-bite

Length of output: 5038


🏁 Script executed:

# Search for how the token is validated in the stream endpoint
rg "access_token|stream" internal/ -t go -B 3 -A 8 | head -80

Repository: ua-academy-projects/share-bite

Length of output: 6475


🏁 Script executed:

# Check what authMiddleware expects - look for auth implementation
rg "authMiddleware|middleware.*auth" internal/ cmd/ -B 2 -A 10 | head -100

Repository: ua-academy-projects/share-bite

Length of output: 6569


🏁 Script executed:

# Look for cookie/session configuration
rg "cookie|session|SetCookie" internal/ cmd/ -B 2 -A 5 | head -80

Repository: ua-academy-projects/share-bite

Length of output: 6144


🏁 Script executed:

# Check how the middleware validates tokens
rg "Bearer|Authorization|GetUserID" internal/middleware -B 3 -A 8

Repository: ua-academy-projects/share-bite

Length of output: 2647


Remove bearer token from SSE URL and logging; implement cookie-based or session-ticket authentication.

Line 34 embeds the access token in the query string, and lines 38/52 log token fragments. This increases token exposure through browser logs, proxy logs, and server access logs. While the backend's OptionalAuth middleware currently accepts query parameter tokens as a fallback, this pattern should be phased out in favor of more secure authentication.

Implementation note: The browser's EventSource API cannot send custom headers, so the suggested approach requires backend support for cookie-based authentication. The backend would need to:

  1. Set a secure, HttpOnly, SameSite cookie on the initial request
  2. Remove the fallback query-parameter token acceptance from the middleware
  3. Accept requests with credentials when using new EventSource(url, { withCredentials: true })

Alternatively, consider using short-lived session tickets or a WebSocket-based approach.

Also applies to: 38-39, 52, and the buildNotificationsStreamUrl function in ui/src/api/business/notifications.ts

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@frontend/src/hooks/useRealtimeNotifications.tsx` around lines 34 - 35, Remove
the access token from the query string in the EventSource URL construction (line
34 where the url is built with the token parameter) and instead pass the
withCredentials option to the EventSource constructor to enable cookie-based
authentication. Remove any logging that exposes token fragments from lines 38
and 52. Apply the same changes to the buildNotificationsStreamUrl function in
ui/src/api/business/notifications.ts. The backend would need to support
cookie-based or session-ticket authentication instead of accepting tokens in
query parameters.

Comment thread frontend/src/pages/guest/Settings/AccountSettingsPage.tsx
Comment thread frontend/src/utils/audio.ts
Comment thread internal/notification/repository/repository.go
Comment thread internal/notification/service/service.go
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.

Manage notification preferences

1 participant