Skip to content
Twilight React Playground
ثيم رائدaren

notification

objectBeginnerserverbrowserlive demo

Reads the signed-in customer's notifications and marks one of them, or all of them, as read.

import { notification, NotificationsListResponse } from '@salla.sa/twilight-theme-engine/api/notification';

In plain words

Stores send customers notifications: an order shipped, a price dropped. notification.list() returns the latest ones, each with a title, a body and whether it has been read. markAsRead(id) and markAllAsRead() tell Salla the customer has seen them.

Like everything in a customer's account, it needs a signed-in shopper.

Signature

notification.list(): Promise<NotificationsListResponse>   // GET notifications (first page)
notification.markAsRead(id: number): Promise<void>        // PUT notifications/{id}/read
notification.markAllAsRead(): Promise<void>               // PUT notifications/read-all
notification.queries.list()                               // key ['notifications', 'list']

interface NotificationsListResponse {
  data: Notification[];
  cursor: { current: number; previous: number | null; next: number | null; count: number };
}
// import type { Notification } from '@salla.sa/twilight-theme-engine/routes/account'
// { id; title; body; icon; color; url; is_read; time_ago; created_at }

Try it live

The signed-in customer's notifications. The playground is a guest, so the query stays disabled.Try this: note the query key has no page: notification.list() always reads the first page.
Storefront canvas · ar · RTL
Runs in the browser…
What a theme writes
import { useQuery } from '@tanstack/react-query';
import { getAuthToken } from '@salla.sa/twilight-theme-engine/api/client';
import { notification } from '@salla.sa/twilight-theme-engine/api/notification';

export function NotificationBell() {
  const { data } = useQuery({
    ...notification.queries.list(),
    enabled: Boolean(getAuthToken()),
  });
  const unread = data?.data.filter((item) => !item.is_read).length ?? 0;

  return <span className="bell" data-count={unread} />;
}

Example

app/components/account/MarkAllRead.tsx
import { useQuery, useQueryClient } from '@tanstack/react-query';
import { notification } from '@salla.sa/twilight-theme-engine/api/notification';

/** On the account notifications page, where the customer is signed in. */
export function MarkAllRead() {
  const queryClient = useQueryClient();
  const { data } = useQuery(notification.queries.list());
  const unread = data?.data.filter((item) => !item.is_read).length ?? 0;

  const markAll = async () => {
    await notification.markAllAsRead();
    // The module does not touch the cache: read the list again.
    await queryClient.invalidateQueries({ queryKey: notification.queries.list().queryKey });
  };

  return (
    <button type="button" disabled={unread === 0} onClick={() => void markAll()}>
      Mark {unread} as read
    </button>
  );
}

How it behaves

  • A guest gets HTTP 400 token_not_provided (demo store). The engine notifications loader wraps list() in orUnauthorized, which lets that 400 through as an error.

  • Both marks parse the answer with ky's .json(), which throws on an empty body or a 204: they expect Salla's usual JSON envelope back.

  • Here cursor.next is a page number, not a URL or a token.

Gotchas

  • list() takes no arguments, so only the first page is reachable. docs/notifications-route.md and route-pagination.md show notification.list(1) and read response.items; that call does not compile, and the result has data, not items.

  • markAsRead and markAllAsRead do not update the cached list. Invalidate notification.queries.list().queryKey, or update it with setQueryData.

Related

Source and docs