TJK: In-app notification bell with per-user history#8
Open
gqcorneby wants to merge 24 commits into
Open
Conversation
…gnup withNewSession opened a separate Hibernate session while handleSignup held a write lock on the user row. The FK check in the notification insert then blocked until lock timeout. withTransaction alone is sufficient: it participates in the caller's session when one exists (request threads) and creates a new one when there isn't one (Quartz/GPars background threads).
Feature is always-on; a config flag adds complexity for no gain. Removing the flag and the grailsApplication dependency it required.
…eation - Add PRODUCT NotificationType enum value - ProductController.sendProductCreatedNotification: replace mailService call with notificationDispatcherService (handles both in-app + email) - UserController.sendUserStatusChanged: same replacement for user activation/deactivation emails (USER_ACCOUNT type) - UserController.toggleActivation: add @transactional to fix pre-existing upstream bug (save flush:true in a controller with no transaction)
Flag was removed in a prior refactor. Clean up the stale comment in NotificationDispatcherService, the dead integration test that toggled the flag, and the commented-out block in the client config template.
sandbox="" blocked all navigation so links redirected inside the sessionless frame → login screen. allow-popups lets clicks escape; base target="_blank" forces all anchors into a real browser tab.
- Badge top/right set to 2px/2px on both GSP and React bells for consistent positioning across dashboard and other pages - Add X-Requested-With: XMLHttpRequest to GSP bell fetch calls so Spring Security returns 401 instead of redirecting to login, keeping the saved redirect URL on the actual page the user was on
- Map body column as TEXT in GORM static mapping (Liquibase already creates it as TEXT; without this GORM would generate VARCHAR(255) on schema-create) - Add X-Requested-With header to GSP bell PUT calls for session-expiry parity - Show fetch error message in dropdown when load fails and list is empty - Remove identity-wrapper handlers in NotificationBell; pass markRead / markAllRead directly as props - Clarify notifyUsers catch comment: Hibernate constraint violations can still roll back the full batch - Fix notificationsApi tests: assert offset in all getNotifications cases, add forwarded-offset test case - Add notifications.bell.error i18n key (EN / RU / TG)
Archive in-app-notifications and notifications-decouple-email to openspec/changes/archive/. Promote the synced capability spec to openspec/specs/in-app-notifications/, reflecting the user-keyed, email-decoupled dispatcher design. Drop the inApp.enabled toggle requirement (flag was removed when toggle-off scope grew to hiding the bell).
Drop the never-populated linkUrl deep-link slot across the notification domain, controller DTO, React modal/dropdown, GSP bell, i18n bundles, and migration. No production caller ever set the value, so both UI guards (isSameOriginPath) were unreachable.
Sync the in-app-notifications capability spec: drop link_url navigation from the click scenarios and assert the payload carries no linkUrl.
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.
📌 References
Purpose
📝 Implementation
CustomNotificationdomain class +custom_notificationLiquibase migration — stores one record per user per event (user-keyed, never email-keyed)CustomNotificationService—notifyUsers(),listForUser(),markRead(),markAllRead(),countUnread()NotificationDispatcherService— single entry point: records in-app notification and optionally sends email; replaces all directmailService.sendHtmlMailcalls inNotificationServiceNotificationTypeenum —SHIPMENT,REQUISITION,FULFILLMENT,STOCK_ALERT,USER_ACCOUNT,PRODUCT,SYSTEM,EMAIL_TRIGGERCustomNotificationController+ REST API (GET/PUT /api/custom/notifications, mark-read, mark-all-read, unread count)_bell.gsppartial for GSP pages +NotificationBell.jsxReact component for React pages — polling unread count, dropdown list, mark-read on clickUserController.toggleActivation: added@Transactionalto fix pre-existing upstream bug (flush:true save in a non-transactional controller action)✨ Description of Change
Adds a persistent in-app notification inbox to complement (not replace) email alerts. Every event that previously sent an email now also writes a
custom_notificationrow per recipient. Users see a bell icon with an unread badge; clicking opens a dropdown of recent notifications with mark-read support.Key design decisions:
withTransactionoverwithNewSession: participates in the caller's transaction when one exists (avoids MySQL lock-timeout on signup), creates a new session when called from background threads (Quartz/GPars)sendEmail=falseescape hatch: per-recipient flows (shipment items shipped/received) handle their own email send; the dispatcher records in-app onlyorg.pih.warehouse.custom.notifications.*andsrc/js/custom/notifications/; upstream files have surgical, documented edits📷 Screenshots & Recordings (optional)
2026-05-25.15-00-15.trimmed.trimmed.mp4
🔥 Notes to the tester
SendStockAlertsJobvia Admin > Quartz > Jobs — bell badge increments, dropdown shows alertROLE_SHIPMENT_NOTIFICATIONat origin/destination each get a notification; per-item recipients get an individual oneROLE_PRODUCT_NOTIFICATIONget a PRODUCT notification