feat:Notifications: preferences, unread count, and tip/goal triggers - #1115
Merged
Akanimoh12 merged 4 commits intoJul 25, 2026
Merged
Conversation
prisma/schema.prisma had duplicate User fields and a duplicate TipStatus enum from an unresolved merge, src/app.ts had two concatenated import blocks with a dangling `}` and duplicate route registrations, and AppError.ts declared ServiceUnavailableError twice. These broke `prisma generate` and `tsc` outright. Also fixes an unrelated x.test.ts type error (unsafe globalThis cast) that was blocking `npm run typecheck`.
Adds the NotificationPreference model (per-user tipReceived/goalReached toggles, defaulting to all-enabled when no row exists) with GET/PATCH /notifications/preferences, a GET /notifications/unread-count endpoint, and a createNotification helper that persists a notification, checks the caller's preferences, and broadcasts it over the realtime gateway. The helper is shared infrastructure for the tip and goal notification triggers. Closes Akanimoh12#966, Akanimoh12#967
recordTip now notifies the receiving creator via createNotification once a tip is newly inserted (never on the existing-tip or P2002 dedupe paths, so replays don't double-notify). Skips self-tips and recipients without an off-chain User row, and never lets a notification failure block the tip response. Closes Akanimoh12#963
projectGoalReached now calls createNotification once the goal transitions into COMPLETED. The transition is detected by comparing against the row's prior status rather than event replay state, so re-processing the same ledger never creates a duplicate notification. Closes Akanimoh12#964
|
@Samuel1505 Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits. You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀 |
Owner
|
You tried |
Akanimoh12
merged commit Jul 25, 2026
f438456
into
Akanimoh12:test-implement-drips
4 of 5 checks passed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Notifications: preferences, unread count, and tip/goal triggers
Closes #963,
closes #964,
closes #966,
closes #967
Summary
Implements the four open
notificationsissues on top of the existinglist/get/mark-read module:
NotificationPreferencemodel (tipReceived/goalReachedbooleans,default
true) withGETandPATCH /api/v1/notifications/preferences.A missing row is treated as all-enabled, so existing users don't need a
backfill.
GET /api/v1/notifications/unread-countreturns
{ count }for the caller's unread, non-deleted notifications.recordTipnow calls the newcreateNotificationhelper once a tip is newly inserted (not on theexisting-tip or
P2002dedupe paths). Skips self-tips and recipients withno off-chain
Userrow; a notification failure is logged and never blocksthe tip response.
projectGoalReachedprojection notifies the creator the moment a
Goaltransitions intoCOMPLETED. The transition is detected from the row's prior status (notfrom event-replay bookkeeping), so replaying the same ledger never creates
a duplicate notification.
createNotificationis the shared piece: it persists theNotificationrow,checks the caller's preference for that type, and broadcasts over the
existing Socket.IO realtime gateway (
emitNotificationCreated) — the samemechanism already used for
tip.created.Also fixed (blocking, unrelated to the four issues above)
The branch had several unresolved-merge artifacts that broke the build
outright —
prisma generateandtscboth failed before any of this workcould be verified:
prisma/schema.prisma: theUsermodel had every relation field listedtwice, and
TipStatuswas declared as two conflicting enums.src/app.ts: two import blocks were concatenated mid-statement (}on itsown line) and several routers were mounted twice.
src/common/errors/AppError.ts:ServiceUnavailableErrorwas declaredtwice (duplicate identifier).
src/modules/x/x.test.ts: a pre-existingtscerror from an unsafeglobalThiscast (one-line fix,unknownintermediate cast).These are isolated in their own
fix:commit so they're easy to review ordrop separately.
Verification
The sandbox this was built in has no Docker/Postgres/Redis available, so the
suites that need a live database (
tests/*.test.tsintegration tests viaresetDb, andsrc/modules/x/x.test.ts's real-prisma cases) fail exactly asthey did before this change — confirmed by diffing the failing-test list
before and after. All new notification behavior is covered by
mocked-Prisma unit/integration tests (
vitest+supertest), matching theexisting pattern in
notifications.test.ts, and passes locally. Pleasere-run the full suite with
docker compose -f backend/docker-compose.yml up -dbefore merging to confirm the DB-backed suites are unaffected.New migration
prisma/migrations/20260725033000_add_notification_preference/— createsNotificationPreference(uniqueuserId, FK toUserwith cascadedelete).
Test plan
npm run typechecknpm run lintnpm run test(mocked unit/integration suites; DB-backed suitesunverifiable in this sandbox, see above)
npm run testwith Postgres + Redis up to confirm DB-backedsuites still pass
GET/PATCH /api/v1/notifications/preferences,GET /api/v1/notifications/unread-count