feat(useNotifications): notification lifecycle management with adapters#146
Open
johnleider wants to merge 49 commits intomasterfrom
Open
feat(useNotifications): notification lifecycle management with adapters#146johnleider wants to merge 49 commits intomasterfrom
johnleider wants to merge 49 commits intomasterfrom
Conversation
|
commit: |
a54eb68 to
50d5acc
Compare
- Types: NotificationInput, NotificationTicket, NotificationsContext - Core: createNotifications wrapping createQueue with lifecycle state mutations (read/seen/archive/snooze), bulk ops, reactive counts - Plugin: createNotificationsContext, createNotificationsPlugin, useNotifications - Adapter: event-driven integration hook for external services - Barrel export from composables/index.ts
Core lifecycle, state mutations, events, and adapter integration.
Built-in adapters for popular notification services: - createFcmAdapter: Firebase Cloud Messaging (inbound) - createOneSignalAdapter: OneSignal Web SDK (inbound) - createKnockAdapter: Knock feeds (inbound + outbound) Available via `@vuetify/v0/notifications` subpath export.
primaryAction/secondaryAction are component-level concerns. Use the data bag for custom payloads instead.
Aligns with plugin convention — useTheme, useLogger, useFeatures, etc.
- Add fallback to createPluginContext (useNotifications works without provider) - Remove double onScopeDispose (queue already self-disposes) - Fix stale "actions" references in JSDoc and docs - Fix mermaid diagram arrow direction - Fix dismiss(id) → unregister(id) in docs - Remove redundant type assertions - Add @see URL to @module JSDoc
- unreadItems: notifications without readAt - archivedItems: notifications with archivedAt - snoozedItems: notifications with snoozedUntil unreadCount now derives from unreadItems.length for efficiency.
- Adapter: interface with setup/dispose, wired in plugin setup callback with app.onUnmount cleanup (matches useFeatures/useTheme pattern) - Adapter removed from NotificationsOptions, lives on NotificationsPluginOptions - createNotifications() is now adapter-agnostic - Bulk ops (readAll/archiveAll) wrapped in queue.batch() - Updated all 3 adapters (FCM, OneSignal, Knock) to implement interface - Manual sync pattern confirmed correct (reactive:true doesn't help)
Interactive example demonstrating derived collections, ticket convenience methods, seen/read distinction, bulk operations, and state lifecycle diagram.
Eliminates shallowRef + triggerRef + event subscriptions in favor of useProxyRegistry which handles reactive state via shallowReactive. Derived collections (unreadItems, archivedItems, snoozedItems) now compute from proxy.values instead of items.value.
Bulk ops (readAll/archiveAll) now emit per-item domain events so adapters like Knock correctly sync state. Fixes type safety issues, adapter memory leaks, and aligns fallback with established patterns.
…use useProxyRegistry Also wires up useNotifications in docs app as a local message bus: - Skillz store publishes notification on pending tour - SkillzResume.vue consumes via useProxyRegistry filtered by data.type
aede424 to
7c0f4e6
Compare
… SSR safety Replace manual createNotificationsContext/createNotificationsPlugin/useNotifications with createPluginContext pattern matching useLogger, useRules, useRtl. Complete the fallback stub with all RegistryContext methods. Add IN_BROWSER guard in Knock adapter to prevent server-side WebSocket/HTTP connections during SSR.
notifications.notify() was tautological. send() reads more naturally and aligns with messaging API conventions.
a6133f0 to
eb8ed41
Compare
… 1 notification Linter collapsed separate send() calls into multi-arg form. send() takes 1 arg — extra args were silently ignored.
Registry stores full notification lifecycle (permanent record). Queue manages display surface (toasts) with FIFO ordering and auto-dismiss. - send() registers in both registry and queue - onboard() registers in registry only (bulk load, no toast) - ticket.dismiss() removes from queue only (toast gone, stays in inbox) - ticket.unregister() removes from both (full removal) - Queue auto-dismiss preserves registry entry - Configurable timeout per-notification or via options default (3000ms)
- Fix "built on createQueue" to describe dual registry+queue architecture - Fix architecture diagram (Registry+Queue→Core, not Registry→Queue→Core) - Replace fabricated reactivity table with actual API surface - Remove stale Firebase/OneSignal references - Remove fabricated timeout TIP (timeout is now a real field) - Add create-registry to related links
Bridges @novu/js with useNotifications. Handles inbound (initial list + real-time notification_received event) and outbound (read, unread, seen, archive, unarchive). Maps Novu severity levels to NotificationSeverity.
Accept optional severity() function in NovuAdapterOptions. Falls back to default critical/high→error, medium→warning, low→info.
- Replace manual toast timers (watch+setTimeout) with queue auto-dismiss - Use Snackbar.* components for toast rendering with TransitionGroup - Fix proxy: use useProxyRegistry instead of nonexistent notifications.proxy - Add timeout per scenario type (-1 for persistent, 4000 for toasts)
…ration - SnackbarPortal: remove ticket.select() to prevent scrim on mount, use ticket.unregister() on unmount, add inheritAttrs:false so class binds to the inner Atom rather than the Teleport root - basic.vue: rewrite example with stacked card UI — newest toast front, older toasts peek behind with scale/inset offset, hover expands all with smooth transitions, queue pause/resume on hover, leave delay prevents collapse while mouse moves to upper items - NotificationProvider.vue: use Snackbar.Portal with fixed positioning, drive toasts from notifications.queue via useProxyRegistry
…ean itemStyle - SnackbarRoot: use useId() as prop default (stable) instead of inside toRef getter - NotificationProvider example: remove stale :severity prop (no longer on SnackbarRoot) - queue.vue example: remove unused _total param from itemStyle - index.test.ts: fix scoped slot render syntax in integration test
… Novu tests, docs fixes - Add app.onUnmount for context/queue disposal before adapter guard - Add tests for 6 untested events (unread, seen, archived, unarchived, snoozed, unsnoozed) - Add Novu adapter test suite (8 tests) - Strengthen persistent timeout test to verify timer survival - Document Snackbar.Close queue removal behavior - Replace inline inject type with SnackbarQueueContext - Remove empty SnackbarCloseSlotProps interface - Add JSDoc to NotificationTicket.dismiss - Simplify Knock adapter dispose with single ctx guard
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.
Summary
createQueuewith notification-specific lifecycle (read/seen/archive/snooze timestamps, bulk ops, reactive counters)createPluginContext—createNotificationsPlugin,createNotificationsContext,useNotifications@vuetify/v0/notificationssubpathcomposables/plugins/use-notificationsWhat's included
packages/0/src/composables/useNotifications/index.tspackages/0/src/composables/useNotifications/index.test.tspackages/0/src/composables/useNotifications/adapters/fcm.tspackages/0/src/composables/useNotifications/adapters/onesignal.tspackages/0/src/composables/useNotifications/adapters/knock.tspackages/0/src/notifications/index.ts+package.jsonapps/docs/src/pages/composables/plugins/use-notifications.mdAPI
Test plan