Skip to content

feat: Add notification bell with unread badge and recent notifications dropdown #720 - #738

Closed
Mona-i wants to merge 240 commits into
accesslayerorg:mainfrom
Mona-i:feat/notification-bell-720
Closed

feat: Add notification bell with unread badge and recent notifications dropdown #720#738
Mona-i wants to merge 240 commits into
accesslayerorg:mainfrom
Mona-i:feat/notification-bell-720

Conversation

@Mona-i

@Mona-i Mona-i commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

closes #720

Summary

Implements the notification bell feature in the nav bar as described in issue #720.

What was changed

New files:

  • src/services/notification.service.ts — NotificationService extending BaseApiService; exposes getNotifications(userId) (GET /notifications) and markAsRead(notificationId) (PATCH /notifications/:id/read). Exports Notification, NotificationType, NotificationsResponse types.
  • src/hooks/useNotifications.ts — React Query hook wrapping useQuery + useMutation. Caps the recent array at 5 items, applies an optimistic update on markAsRead (flips read flag, decrements unreadCount, rolls back on error).
  • src/components/common/NotificationBell.tsx — Bell icon button in the nav bar (Radix DropdownMenu). Shows red badge when unreadCount > 0, capped at 99+. Dropdown lists up to 5 recent notifications with message, relative time, and unread indicator dot. Clicking an item marks it read and navigates to its href. "View all" link navigates to /notifications.
  • src/pages/NotificationsPage.tsx — Full notification list page served at /notifications (the "View all" destination).
  • src/components/common/__tests__/NotificationBell.test.tsx — 14 unit tests covering badge visibility, count cap, accessible labels, dropdown open, empty state, skeleton, item rendering, markAsRead callback, View all link, and badge decrement.
  • src/hooks/__tests__/useNotifications.test.ts — 6 unit tests covering loading state, empty result, resolved data, 5-item cap, disabled-when-no-userId, and optimistic update.

Modified files:

  • src/lib/queryKeys.ts — Added notifications.all() and notifications.list(userId) query key factories.
  • src/components/home/Header.tsx — Imports NotificationBell and useProfileStore; renders NotificationBell to the left of WalletStatusChip when a user profile is present.
  • src/routes.tsx — Added /notifications route pointing to NotificationsPage.

Acceptance criteria check

Criterion Status
Unread count badge visible when there are unread notifications
Dropdown shows up to five most recent notifications
Clicking a notification marks it read and navigates correctly
Badge count decrements when a notification is marked read ✅ (optimistic update)
View all link navigates to /notifications

Testing / Validation

  • Zero TypeScript diagnostics across all new and modified files
  • All new files use tabs (matching .prettierrc config: useTabs: true)
  • Single quotes, trailing commas, and semicolons match project prettier config
  • No unrelated files changed
  • No debug code, console.logs, or temporary hacks

Links

T-kesh and others added 30 commits June 23, 2026 23:26
- Rewrite .env.example with a one-line description for every variable
  and remove duplicate entries; group into required vs optional
- Add an 'Environment variables' section to CONTRIBUTING.md covering
  required vs optional vars and where to source testnet RPC URLs
Verifies that the GitHub and Telegram community links render with
the correct href values, open in a new tab, and carry the expected
rel="noopener noreferrer" attribute so a future refactor cannot
silently break these URLs.

Closes accesslayerorg#428
…on-test

 Add integration tests for MarketingPage community links
…-add-helper-for-formatting-key-price-display-in-xlm-with-correct-decimal-places

feat: accesslayerorg#420 Add helper for formatting key price display in XLM with co…
- Add computePriceChange(current: bigint, previous: bigint) in src/utils/priceChange.utils.ts
- Returns { percent: number, direction: 'up' | 'down' | 'flat' }
- Returns flat when previous is zero or current equals previous
- 7 unit tests covering: price up, price down, no change, zero previous, both zero

Closes accesslayerorg#429
…rorg#444 accesslayerorg#445

- Add copy-to-clipboard button to ConnectWalletButton with 2s feedback and screen-reader announcement (accesslayerorg#442)
- Add src/lib/queryKeys.ts factory-style React Query key constants with unit tests (accesslayerorg#443)
- Add LandingPage.holdings.test.tsx integration tests asserting portfolio totals and per-holding values (accesslayerorg#444)
- Add docs/api-layer.md documenting service conventions, error handling, and a worked end-to-end example (accesslayerorg#445)
…cesslayerorg#437

- Add ledgerToTimestamp helper for converting Stellar ledger sequence numbers to estimated timestamps
- Add transaction status badge legend tooltip for user-friendly status explanations
- Add creator card keyboard shortcut hint (press 'B' to quick buy) with desktop-only display
- Add integration test for creator card price update on snapshot data change
…elper-tx-tooltip-keyboard-shortcut-price-test

Add ledger timestamp helper, transaction status tooltip, keyboard shortcuts, and price update tests
…-address

feat: add shortenAddress helper for wallet address display — Close accesslayerorg#423
Closes accesslayerorg#438 by formatting counts under 1K as plain strings and larger values with one-decimal K/M suffixes, with unit tests for each range and boundary values.

Co-authored-by: Cursor <cursoragent@cursor.com>
Documents the standard pattern contributors should follow when writing
new useQuery and useMutation hooks — covering the onError callback
convention, when to show a toast vs inline error vs error boundary,
how to distinguish 4xx client errors from 5xx server errors and network
failures, and a worked example hook that handles all three cases.

Closes accesslayerorg#460
…-holder-count

Add formatHolderCount helper for compact holder display.
closes accesslayerorg#460 - docs: add error handling guide for React Query hooks
Closes accesslayerorg#424

Add a React error boundary scoped to creator detail pages so a render
error shows a fallback UI instead of crashing the whole app to a blank
screen. Follows the existing SectionErrorBoundary class pattern.

- Fallback shows a short message and a "Back to creators" link (/creators)
- Logs the error to console only in development (import.meta.env.DEV)
- Add tests covering normal render, fallback on error, and the list link
…d layout

- Add new CreatorCardSkeleton component that mirrors CreatorCard's
  dimensions (avatar, name + verified/change/supply/recent-activity
  badges, handle, bio, sparkline placeholder, 3 mini stat chips, 3
  meta rows, social links, action row, helper text) so it acts as a
  layout-stable placeholder while the creator list is fetching.
- Use the existing dark-theme .skeleton-shimmer animation and
  CREATOR_CARD_MEDIA_RADIUS_CLASS token so the skeleton visually
  matches the rest of the marketplace.
- Respect prefers-reduced-motion via the shared CSS rule and a
  disableShimmer prop, matching CreatorSkeleton's API.
- Add a CreatorCardGridSkeleton helper that defaults to count = 6
  to satisfy the acceptance criterion.
- Wire CreatorCardGridSkeleton into LandingPage's initial-loading
  branch so the marketplace shows 6 stable skeleton cards instead of
  the generic grid skeleton.
- Cover the new component with vitest tests asserting role/aria
  coverage, 6-by-default grid, disableShimmer propagation, className
  merging, and shimmer-vs-static block toggling.
Closes accesslayerorg#426.

Adds docs/adding-page-routes.md covering where routes are registered in src/App.tsx, the file naming convention for page components, the recommended pattern for auth-protected routes (with a RequireAuth wrapper example), and a four-step worked example adding a public AboutPage at /about. Adds a one-line pointer in CONTRIBUTING.md under Frontend conventions so contributors can find the guide.
…ice-change

feat: add computePriceChange helper for key price percentage change
…imestamp-test

test: add integration test for ledgerToTimestamp (accesslayerorg#461)
…rage-preferences

feat: add generic localStorage preference helpers (accesslayerorg#458)
…-component

Feat/error boundary component
…e-contributing-folder-structure

docs: update CONTRIBUTING.md with folder structure and conventions
OTimileyin and others added 26 commits July 29, 2026 08:19
…e-load-perf-log-tests

test(nav-timing): assert page-load-perf fields present, typed, and non-negative (accesslayerorg#678)
…pending-indicator-dev

test(buy-flow): assert in-flight pending indicator and post-confirm clear (accesslayerorg#672)
…ress-popover-dismissal

fix: replace wallet address Dialog with Popover, add dismissal integr…
feat: add structured log for infinite scroll next page fetch
…ofile-price-background-refetch

test: add creator profile price refetch coverage
Wire useNavigationTiming('portfolio') into LandingPage so production
builds emit the same Navigation Timing metrics as marketplace and profile.

Co-authored-by: Cursor <cursoragent@cursor.com>
test: add unit tests for sparkline hiding when fewer than two data points (accesslayerorg#676)
…rtfolio-page-load-perf-log

feat(perf): log page-load timing on portfolio page (accesslayerorg#726)
…ght-723

feat: add creator spotlight section to home page showcasing top 3 cre…
… disconnect flow

- Add popover showing full wallet address when connected
- Include copy-to-clipboard button with confirmation feedback
- Add disconnect button that opens confirmation dialog
- Close popover on outside click or Escape key

Closes accesslayerorg#548
…t-connection-ui

feat: add wallet connection UI with address display
Copy the profile URL via the Clipboard API, show Copied! for 2s, fall back to window.prompt, and omit the control when window is undefined.

Co-authored-by: Cursor <cursoragent@cursor.com>
- Remove unused 'within' import from WalletConnectionPopover.test.tsx
- Replace 'any' types with proper TypeScript types across test files
- Add React import to LandingPage.portfolio.test.tsx for type definitions
- Import Toast type from react-hot-toast for proper type checking
- Remove unused 'container' and 'waitFor' variables from test files
…board-timing-tests

test: add copy confirmation timing tests for CopyField (accesslayerorg#600)
…t/598-holdings-grand-total-sum-integration-test

feat(accesslayerorg#598): add integration test for holdings grand total sum across multiple held creators
…fetch-wallet-switch

test(holdings): add integration test for holdings refetch on wallet s…
…are-creator-profile

feat: add share button to copy creator profile URL (accesslayerorg#724)
Merged two separate queryKeys exports into a single object containing creatorProfile, creators, and wallet keys. This fixes TypeScript build errors where .creators and .wallet properties were not found.
…ccesslayerorg#720

- Add NotificationBell component (src/components/common/NotificationBell.tsx)
  - Bell icon in the nav bar using lucide-react Bell icon
  - Unread count badge (red, capped at 99+) visible only when count > 0
  - Radix DropdownMenu shows up to 5 most recent notifications
  - Each item shows message, relative time (via formatRelativeTime), and
    an unread indicator dot
  - Clicking an item calls markAsRead() then navigates to notification.href
  - View all link at the bottom navigates to /notifications
  - Accessible aria-label includes unread count when present
  - Loading skeleton rows while query is in flight
  - Empty state when no notifications exist

- Add notification.service.ts (src/services/notification.service.ts)
  - Extends BaseApiService following existing service pattern
  - getNotifications(userId) - GET /notifications
  - markAsRead(notificationId) - PATCH /notifications/:id/read
  - Notification, NotificationType, NotificationsResponse types exported

- Add useNotifications hook (src/hooks/useNotifications.ts)
  - Wraps useQuery for data fetching (queryFn injected for testability)
  - Wraps useMutation for markAsRead with optimistic update:
    flips read flag and decrements unreadCount immediately, rolls back
    on error, invalidates on settled
  - Caps recent array at 5 items for dropdown display
  - Disabled when userId is empty

- Add notifications query key family (src/lib/queryKeys.ts)
  - queryKeys.notifications.all()
  - queryKeys.notifications.list(userId)

- Update Header (src/components/home/Header.tsx)
  - Import NotificationBell and useProfileStore
  - Render <NotificationBell userId={profile.id} /> when profile is set,
    placed left of WalletStatusChip in a flex gap-2 wrapper

- Add /notifications route (src/routes.tsx + src/pages/NotificationsPage.tsx)
  - Full-page notifications list matching View all destination
  - Uses the same useNotifications hook and formatRelativeTime

- Add tests
  - src/hooks/__tests__/useNotifications.test.ts: loading state, empty,
    data resolved, 5-item cap, disabled when no userId, optimistic update
  - src/components/common/__tests__/NotificationBell.test.tsx: badge
    visibility, 99+ cap, accessible labels, dropdown open, empty state,
    skeleton, 5 items rendered, message text, markAsRead called on click,
    View all link, badge decrement after read
@github-actions

Copy link
Copy Markdown

👋 Hey @Mona-i, thanks for your contribution!

This PR is targeting main directly. We use main for stable releases only — all contributions should be opened against the dev branch instead.

Please close this PR and reopen it with dev as the base branch. If you're unsure how to do that, you can change the base branch using the Edit button at the top of this PR page.

Closing this PR automatically. See you in dev! 🚀

@github-actions github-actions Bot closed this Jul 30, 2026
@drips-wave

drips-wave Bot commented Jul 30, 2026

Copy link
Copy Markdown

@Mona-i 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! 🚀

Learn more about application limits

@netlify

netlify Bot commented Jul 30, 2026

Copy link
Copy Markdown

Deploy Preview for accesslayer ready!

Name Link
🔨 Latest commit c23b035
🔍 Latest deploy log https://app.netlify.com/projects/accesslayer/deploys/6a6ad0ee70efea0007b2a8b6
😎 Deploy Preview https://deploy-preview-738--accesslayer.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

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.