Skip to content

Add DM and store notifications support - #209

Open
PastaPastaPasta wants to merge 5 commits into
masterfrom
claude/add-notifications-actions-Fp31V
Open

Add DM and store notifications support#209
PastaPastaPasta wants to merge 5 commits into
masterfrom
claude/add-notifications-actions-Fp31V

Conversation

@PastaPastaPasta

@PastaPastaPasta PastaPastaPasta commented Jan 29, 2026

Copy link
Copy Markdown
Owner

Summary

Extends the notification system to support direct messages and e-commerce features including new orders, order status updates, and customer reviews. This enables sellers to receive notifications for store activity and buyers to track order progress.

Key Changes

Notification Types

  • Added 4 new notification types: newMessage, orderReceived, orderStatusUpdate, newReview
  • Updated Notification interface to include optional fields for DM and store data (conversationId, orderId, orderStatus, reviewRating, etc.)

Notification Service (lib/services/notification-service.ts)

  • Implemented getNewMessages() to query direct messages from the DM contract
  • Implemented getNewOrders() to fetch new orders received by sellers
  • Implemented getOrderStatusUpdates() to track order status changes for buyers (with chunked querying for efficiency)
  • Implemented getNewReviews() to fetch new reviews on seller stores
  • Updated fetchNotifications() to run all notification queries in parallel batches
  • Added enrichment support for new notification fields

UI Components (app/notifications/page.tsx)

  • Added 4 new icons (EnvelopeIcon, ShoppingBagIcon, TruckIcon, StarIcon) for visual distinction
  • Added "Messages" and "Orders" filter tabs to notification feed
  • Implemented action buttons for each notification type (Open Messages, View Order, View Review)
  • Added order status badges with color-coded states (shipped, delivered, cancelled, etc.)
  • Added star rating display for review notifications
  • Updated navigation logic to route to appropriate pages (/messages, /orders, /orders/seller)

Settings & Store

  • Added orders and reviews notification settings to NotificationSettings
  • Updated notification filter types and store logic to handle new categories
  • Added empty state messages for messages and orders filters

Constants

  • References new contract IDs: YAPPR_DM_CONTRACT_ID and YAPPR_STOREFRONT_CONTRACT_ID

Implementation Details

  • All notification queries run in parallel for performance
  • Order status updates use chunked querying to handle large order lists
  • Color-coded UI elements match notification severity/type (blue for messages, emerald for orders, orange for shipping, yellow for reviews)
  • Maintains backward compatibility with existing social notifications

https://claude.ai/code/session_01ABKfq9pPndsHuxrweAZ66L

Summary by CodeRabbit

  • New Features
    • Direct message notifications with quick-open action and message preview
    • Store order notifications (new orders, status updates) with contextual badges and "View Order" actions
    • Review notifications showing star ratings and optional titles with "View Review" action
    • New Messages and Orders tabs in the notification center and updated empty-state texts
    • Per-type notification preferences and unread counts respecting those settings

✏️ Tip: You can customize this high-level summary in your review settings.

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex usage limits have been reached for code reviews. Please check with the admins of this repo to increase the limits by adding credits.
Credits must be used to enable repository wide code reviews.

@coderabbitai

coderabbitai Bot commented Jan 29, 2026

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

Adds direct-message and store-related notification types, fetches them in parallel from DM/store contracts, enriches notifications with conversation/order/review metadata, extends types and settings, and surfaces new “Messages” and “Orders” tabs with per-notification actions and UI elements.

Changes

Cohort / File(s) Summary
Notifications UI
app/notifications/page.tsx
Added Messages and Orders tabs; new notification type-to-setting mappings, icons, messages, and empty-state texts; renders message previews, order status badges, and review ratings; adds action buttons routing to messages/orders pages.
Notification Service
lib/services/notification-service.ts
Introduced DM/store query paths and constants; added public methods getNewMessages, getNewOrders, getOrderStatusUpdates, getNewReviews; fetchNotifications runs parallel queries for DM/store and social sources; RawNotification/ enrichment extended with conversationId, messagePreview, orderId, storeId, storeName, orderStatus, reviewRating, reviewTitle; INITIAL_FETCH_* constants added.
Types
lib/types.ts
Extended Notification.type union with newMessage, orderReceived, orderStatusUpdate, newReview; added optional fields on Notification for DM/order/review metadata; added recipientId to DirectMessageDocument.
State / Store
lib/store.ts, lib/stores/notification-store.ts
Added orders and reviews boolean settings to NotificationSettings; added messages and orders to NotificationFilter; updated unread-count and filtering logic to include new notification type groupings.

Sequence Diagram

sequenceDiagram
    participant UI as UI Layer
    participant Service as NotificationService
    participant DM as DM Contract
    participant Store as Storefront Contract
    participant Enricher as Enrichment Logic

    UI->>Service: fetchNotifications(userId)
    
    par Parallel Queries
        Service->>DM: getNewMessages(userId)
        Service->>Store: getNewOrders(userId)
        Service->>Store: getOrderStatusUpdates(userId)
        Service->>Store: getNewReviews(userId)
        Service->>Service: getSocialNotifications()
    end
    
    DM-->>Service: rawNotifications (newMessage)
    Store-->>Service: rawNotifications (orderReceived, orderStatusUpdate, newReview)
    Service-->>Service: rawNotifications (followers, mentions, likes, etc.)
    
    Service->>Enricher: enrichNotifications(aggregatedRaw)
    Enricher->>Enricher: attach conversationId, messagePreview, orderId, storeId, orderStatus, reviewRating
    Enricher-->>Service: enrichedNotifications
    
    Service-->>UI: Notification[]
    UI->>UI: Filter by tab (messages, orders, follow, etc.)
    UI->>UI: Render items with actions (Open Messages / View Order / View Review)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • yappr#131: Adds foundational DM and storefront notification types and fetch/enrichment patterns used here.
  • yappr#173: Touches the same notifications page and filter/tab mappings; overlapping UI/filter changes.
  • yappr#196: Modifies notification fetch/enrichment and navigation handling; intersects with service and UI changes introduced here.

Poem

🐰
I hopped through threads and parcels bright,
Messages chimed in soft moonlight,
Orders stamped and stars aglow,
Notifications bounce to-and-fro —
Hooray, hop on, the inbox grows!

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and accurately summarizes the main change: adding support for direct message and store-related notifications to the system.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch claude/add-notifications-actions-Fp31V

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Jan 29, 2026

Copy link
Copy Markdown

Deploying yappr with  Cloudflare Pages  Cloudflare Pages

Latest commit: 0591bf6
Status:🚫  Build failed.

View logs

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@lib/services/notification-service.ts`:
- Around line 266-287: The four methods getNewMessages, getNewOrders,
getOrderStatusUpdates, and getNewReviews currently call sdk.documents.query(...)
with unsafe "as any" casts and untyped document variables; replace those calls
with the queryDocuments(sdk, queryOptions) helper and strongly type the results
using the existing interfaces DirectMessageDocument, StoreOrderDocument,
OrderStatusUpdateDocument, and StoreReviewDocument respectively, remove
normalizeSDKResponse and the any casts, type local vars (e.g., documents,
orders, statusUpdates, reviews) appropriately, and use .catch(() => []) on
queryDocuments to return an empty array on error so downstream code (like
building orderStoreMap from order.$id) has proper types.

Comment thread lib/services/notification-service.ts Outdated
- Add new notification types: newMessage, orderReceived, orderStatusUpdate, newReview
- Query DM contract for new direct messages (recipientId index)
- Query storefront contract for new orders, status updates, and reviews
- Add Messages and Orders tabs to notifications page
- Display action buttons for navigating to messages and orders
- Show order status badges and review ratings in notifications
- Add notification settings for orders and reviews
- All notification queries run in parallel for efficiency
- Proper chunking for order status updates to handle large order counts

https://claude.ai/code/session_01ABKfq9pPndsHuxrweAZ66L
@PastaPastaPasta
PastaPastaPasta force-pushed the claude/add-notifications-actions-Fp31V branch from 30e4cf1 to 018227f Compare January 29, 2026 21:13
…ifications

- Replace sdk.documents.query with queryDocuments helper
- Use DirectMessageDocument, StoreOrderDocument, OrderStatusUpdateDocument,
  and StoreReviewDocument interfaces for proper typing
- Remove normalizeSDKResponse and unsafe 'as any' casts
- Use .catch(() => []) for error handling on all queries
- Add recipientId field to DirectMessageDocument interface

https://claude.ai/code/session_01ABKfq9pPndsHuxrweAZ66L

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
lib/services/notification-service.ts (1)

96-96: ⚠️ Potential issue | 🔴 Critical

Build failure: normalizeSDKResponse is not defined or imported.

The pipeline fails with "Cannot find name 'normalizeSDKResponse'" at this line. This function is used here and at lines 139 and 247, but it's never imported. The new methods (getNewMessages, getNewOrders, etc.) correctly use the queryDocuments helper which handles normalization internally.

Either import normalizeSDKResponse from ./sdk-helpers, or refactor these older methods to use queryDocuments for consistency with the new code.

Option 1: Import normalizeSDKResponse
-import { identifierToBase58, queryDocuments, QueryDocumentsOptions } from './sdk-helpers';
+import { identifierToBase58, queryDocuments, QueryDocumentsOptions, normalizeSDKResponse } from './sdk-helpers';
Option 2 (preferred): Refactor to use queryDocuments consistently
   async getNewFollowers(userId: string, sinceTimestamp: number): Promise<RawNotification[]> {
     try {
       const sdk = await getEvoSdk();

-      // SDK query types are incomplete, cast needed for valid query options
-      const response = await sdk.documents.query({
+      const queryOptions: QueryDocumentsOptions = {
         dataContractId: YAPPR_CONTRACT_ID,
         documentTypeName: 'follow',
         where: [
           ['followingId', '==', userId],
           ['$createdAt', '>', sinceTimestamp]
         ],
         orderBy: [['followingId', 'asc'], ['$createdAt', 'asc']],
         limit: NOTIFICATION_QUERY_LIMIT
-      } as any);
+      };

-      const documents = normalizeSDKResponse(response);
+      const documents = await queryDocuments(sdk, queryOptions);

-      return documents.map((doc: any) => ({
+      return documents.map((doc) => ({
         id: doc.$id,
         type: 'follow' as const,
         fromUserId: doc.$ownerId, // The follower
         createdAt: doc.$createdAt
       }));

Apply the same pattern to getPrivateFeedNotifications and getNewMentions.

🤖 Fix all issues with AI agents
In `@lib/services/notification-service.ts`:
- Around line 385-400: The map lookup can fail because orderStoreMap is keyed by
order.$id while the lookup uses the potentially different format
identifierToBase58(doc.orderId); fix by normalizing both sides to the same
identifier format: when building orderStoreMap, use a normalized key (e.g.,
identifierToBase58(order.$id) or identifierToBase58(order.storeId) as
appropriate) and when creating the status update compute a normalizedOrderId
(e.g., doc.orderId ? identifierToBase58(doc.orderId) :
identifierToBase58(doc.$id)) and use orderStoreMap.get(normalizedOrderId) for
storeId; update references to orderStoreMap, orders, identifierToBase58,
statusUpdates, doc.orderId and doc.$id accordingly and ensure you preserve
existing null/undefined fallbacks.
🧹 Nitpick comments (1)
app/notifications/page.tsx (1)

388-425: Consider extracting notification URL to avoid duplication.

The URL for newMessage action button (line 391) duplicates the logic in getNotificationUrl (lines 51-54). Consider reusing getNotificationUrl here for consistency.

Suggested refactor
                          {/* Action buttons for DM notifications */}
                          {notification.type === 'newMessage' && (
                            <Link
-                              href={notification.conversationId ? `/messages?conversation=${notification.conversationId}` : '/messages'}
+                              href={getNotificationUrl(notification) || '/messages'}
                              onClick={(e) => e.stopPropagation()}

Comment on lines +385 to +400
const orderStoreMap = new Map<string, string>();
for (const order of orders) {
const storeId = order.storeId ? identifierToBase58(order.storeId) : undefined;
if (storeId) {
orderStoreMap.set(order.$id, storeId);
}
}

return statusUpdates.map((doc) => {
const orderId = (doc.orderId ? identifierToBase58(doc.orderId) : null) ?? doc.$id;
return {
id: `status-${doc.$id}`,
type: 'orderStatusUpdate' as const,
fromUserId: doc.$ownerId, // The seller who posted the update
orderId,
storeId: orderStoreMap.get(orderId),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Potential key mismatch in orderStoreMap lookup.

The map is keyed by order.$id (line 389), but the lookup on line 400 uses orderId which is derived from identifierToBase58(doc.orderId). If the status update's orderId field stores a different format than order.$id, this lookup will silently fail and storeId will be undefined.

Consider normalizing both to the same format:

Suggested fix
     // Build a map of orderId -> storeId from orders for enrichment
     const orderStoreMap = new Map<string, string>();
     for (const order of orders) {
       const storeId = order.storeId ? identifierToBase58(order.storeId) : undefined;
       if (storeId) {
-        orderStoreMap.set(order.$id, storeId);
+        // Use base58-converted $id for consistent lookup
+        const orderIdKey = identifierToBase58(order.$id) ?? order.$id;
+        orderStoreMap.set(orderIdKey, storeId);
       }
     }

     return statusUpdates.map((doc) => {
       const orderId = (doc.orderId ? identifierToBase58(doc.orderId) : null) ?? doc.$id;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const orderStoreMap = new Map<string, string>();
for (const order of orders) {
const storeId = order.storeId ? identifierToBase58(order.storeId) : undefined;
if (storeId) {
orderStoreMap.set(order.$id, storeId);
}
}
return statusUpdates.map((doc) => {
const orderId = (doc.orderId ? identifierToBase58(doc.orderId) : null) ?? doc.$id;
return {
id: `status-${doc.$id}`,
type: 'orderStatusUpdate' as const,
fromUserId: doc.$ownerId, // The seller who posted the update
orderId,
storeId: orderStoreMap.get(orderId),
const orderStoreMap = new Map<string, string>();
for (const order of orders) {
const storeId = order.storeId ? identifierToBase58(order.storeId) : undefined;
if (storeId) {
// Use base58-converted $id for consistent lookup
const orderIdKey = identifierToBase58(order.$id) ?? order.$id;
orderStoreMap.set(orderIdKey, storeId);
}
}
return statusUpdates.map((doc) => {
const orderId = (doc.orderId ? identifierToBase58(doc.orderId) : null) ?? doc.$id;
return {
id: `status-${doc.$id}`,
type: 'orderStatusUpdate' as const,
fromUserId: doc.$ownerId, // The seller who posted the update
orderId,
storeId: orderStoreMap.get(orderId),
🤖 Prompt for AI Agents
In `@lib/services/notification-service.ts` around lines 385 - 400, The map lookup
can fail because orderStoreMap is keyed by order.$id while the lookup uses the
potentially different format identifierToBase58(doc.orderId); fix by normalizing
both sides to the same identifier format: when building orderStoreMap, use a
normalized key (e.g., identifierToBase58(order.$id) or
identifierToBase58(order.storeId) as appropriate) and when creating the status
update compute a normalizedOrderId (e.g., doc.orderId ?
identifierToBase58(doc.orderId) : identifierToBase58(doc.$id)) and use
orderStoreMap.get(normalizedOrderId) for storeId; update references to
orderStoreMap, orders, identifierToBase58, statusUpdates, doc.orderId and
doc.$id accordingly and ensure you preserve existing null/undefined fallbacks.

@thepastaclaw

thepastaclaw commented Jul 1, 2026

Copy link
Copy Markdown
Collaborator

🕓 Ready for review — 59 ahead in queue (commit 0591bf6)
Queue position: 60/74 · 3 reviews active
ETA: start ~07:23 UTC · complete ~08:00 UTC (median 36m across 30 recent reviews; 3 slots)
Queued 40m ago · Last checked: 2026-07-21 19:20 UTC

@thepastaclaw thepastaclaw left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Preliminary review — Codex only

At exact head 0591bf6, the PR fails TypeScript compilation because normalizeSDKResponse is still referenced after its import was removed. The new DM and store notification paths also contain confirmed query, authorization, persistence, badge, and navigation defects, so the PR requires changes.

Validated blockers were found in the Codex precheck. Sonnet is deferred until a fresh Codex revalidation clears the blocker gate.

Review provenance

  • Codex reviewers: gpt-5.6-sol — general (failed), gpt-5.6-sol — general (failed), gpt-5.6-sol — general (failed), gpt-5.6-sol — general (completed)
  • Verifier: gpt-5.6-sol — verifier
  • Sonnet: not run (deferred by blocker gate)

🔴 9 blocking

2 additional finding(s) omitted (not in diff).

🤖 Prompt for all review comments with AI agents
These findings are from an automated code review. Verify each finding against the current code and only fix it if needed.

In `lib/services/notification-service.ts`:
- [BLOCKING] lib/services/notification-service.ts:4: Restore the removed normalizeSDKResponse import
  This refactor removed `normalizeSDKResponse` from the import, but lines 96, 139, and 247 still call it. `npx tsc --noEmit` reproduces three TS2304 errors, so the production build cannot pass type checking.
- [BLOCKING] lib/services/notification-service.ts:277-285: Query the v3 DM model instead of a nonexistent recipient field
  The active v3 write path creates `directMessage` documents with only `conversationId` and `encryptedContent`; recipient identity is stored on `conversationInvite`. The deployed v3 model therefore has no `recipientId` field or `receiverMessages` index on `directMessage`. This query is rejected and the catch converts that failure to an empty array, silently suppressing every received-message notification. Adding `recipientId` to the TypeScript interface does not change deployed documents.
- [BLOCKING] lib/services/notification-service.ts:372-378: Select the compound index for order status queries
  The storefront contract exposes status lookups through `orderAndTime`, whose fields are `[orderId, $createdAt]`. This `in` plus timestamp-range query omits the matching ordering clauses, unlike the repository's other compound Platform queries. Platform can reject the query, and the catch then turns the failure into an empty result, preventing buyers from receiving status notifications.
- [BLOCKING] lib/services/notification-service.ts:347-351: Fetch status updates for orders beyond the newest 100
  The buyer-order query retrieves only the newest 100 orders and never paginates. A recent status update for the buyer's 101st-most-recent order can therefore never be discovered. The later chunking does not address this because `orderIds` can contain at most 100 entries, so it always produces at most one chunk.
- [BLOCKING] lib/services/notification-service.ts:393-403: Verify status updates were authored by the order's seller
  The contract validates the shape of `orderStatusUpdate` but does not require its owner to match the referenced order's `sellerId`. Any identity can publish an update for a public order ID, and this new notification path presents that identity and supplied status as an authentic seller update. Use the already-fetched orders to discard updates whose `$ownerId` does not match the corresponding seller.

In `lib/store.ts`:
- [BLOCKING] lib/store.ts:234-236: Merge new settings into persisted notification preferences
  Zustand persist performs a shallow merge by default. For existing users, the persisted `notificationSettings` object lacks `orders` and `reviews` and replaces the complete default object during hydration. Those properties remain undefined, so the notifications page treats them as disabled while the settings page's `Object.entries` rendering provides no switches through which to enable them.

In `app/notifications/page.tsx`:
- [BLOCKING] app/notifications/page.tsx:391: Use the query parameter supported by the messages page
  The messages page reads only `startConversation`; it never reads `conversation`. The new Open Messages action therefore opens the inbox without selecting the sender's conversation. The DM branch in `getNotificationUrl()` also constructs the same unsupported parameter.
- [BLOCKING] app/notifications/page.tsx:419: Navigate review notifications to a page that displays reviews
  `/orders/seller` loads seller orders and status controls but never fetches or renders store reviews, so the new View Review action cannot show the referenced review. The notification already carries `storeId`, and `/store/view?id=...` is the route that loads that store's reviews.

In `components/layout/sidebar.tsx`:
- [BLOCKING] components/layout/sidebar.tsx:74: Exclude disabled notification categories from the sidebar badge
  The notification page hides disabled message, order, and review notifications and hides Mark all as read when no enabled unread notifications remain. The sidebar still counts every unread notification without consulting those preferences. Disabling one of the new categories can therefore leave a persistent badge for notifications the user cannot see or clear from the page.

import { normalizeSDKResponse, identifierToBase58, queryDocuments, QueryDocumentsOptions } from './sdk-helpers';
import { YAPPR_CONTRACT_ID } from '../constants';
import { Notification, User, Post } from '../types';
import { identifierToBase58, queryDocuments, QueryDocumentsOptions } from './sdk-helpers';

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 Blocking: Restore the removed normalizeSDKResponse import

This refactor removed normalizeSDKResponse from the import, but lines 96, 139, and 247 still call it. npx tsc --noEmit reproduces three TS2304 errors, so the production build cannot pass type checking.

Suggested change
import { identifierToBase58, queryDocuments, QueryDocumentsOptions } from './sdk-helpers';
import { normalizeSDKResponse, identifierToBase58, queryDocuments, QueryDocumentsOptions } from './sdk-helpers';

source: ['codex']

Comment on lines +277 to +285
where: [
['recipientId', '==', userId],
['$createdAt', '>', sinceTimestamp]
],
orderBy: [['recipientId', 'asc'], ['$createdAt', 'asc']],
limit: NOTIFICATION_QUERY_LIMIT
};

const documents = await queryDocuments(sdk, queryOptions).catch(() => []) as DirectMessageDocument[];

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 Blocking: Query the v3 DM model instead of a nonexistent recipient field

The active v3 write path creates directMessage documents with only conversationId and encryptedContent; recipient identity is stored on conversationInvite. The deployed v3 model therefore has no recipientId field or receiverMessages index on directMessage. This query is rejected and the catch converts that failure to an empty array, silently suppressing every received-message notification. Adding recipientId to the TypeScript interface does not change deployed documents.

source: ['codex']

Comment on lines +372 to +378
where: [
['orderId', 'in', chunk],
['$createdAt', '>', sinceTimestamp]
],
limit: NOTIFICATION_QUERY_LIMIT
};
return queryDocuments(sdk, statusQueryOptions).catch(() => []) as Promise<OrderStatusUpdateDocument[]>;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 Blocking: Select the compound index for order status queries

The storefront contract exposes status lookups through orderAndTime, whose fields are [orderId, $createdAt]. This in plus timestamp-range query omits the matching ordering clauses, unlike the repository's other compound Platform queries. Platform can reject the query, and the catch then turns the failure into an empty result, preventing buyers from receiving status notifications.

Suggested change
where: [
['orderId', 'in', chunk],
['$createdAt', '>', sinceTimestamp]
],
limit: NOTIFICATION_QUERY_LIMIT
};
return queryDocuments(sdk, statusQueryOptions).catch(() => []) as Promise<OrderStatusUpdateDocument[]>;
const statusQueryOptions: QueryDocumentsOptions = {
dataContractId: YAPPR_STOREFRONT_CONTRACT_ID,
documentTypeName: 'orderStatusUpdate',
where: [
['orderId', 'in', chunk],
['$createdAt', '>', sinceTimestamp]
],
orderBy: [['orderId', 'asc'], ['$createdAt', 'asc']],
limit: NOTIFICATION_QUERY_LIMIT
};

source: ['codex']

Comment on lines +347 to +351
orderBy: [['$ownerId', 'asc'], ['$createdAt', 'desc']],
limit: NOTIFICATION_QUERY_LIMIT
};

const orders = await queryDocuments(sdk, ordersQueryOptions).catch(() => []) as StoreOrderDocument[];

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 Blocking: Fetch status updates for orders beyond the newest 100

The buyer-order query retrieves only the newest 100 orders and never paginates. A recent status update for the buyer's 101st-most-recent order can therefore never be discovered. The later chunking does not address this because orderIds can contain at most 100 entries, so it always produces at most one chunk.

source: ['codex']

Comment on lines +393 to +403
return statusUpdates.map((doc) => {
const orderId = (doc.orderId ? identifierToBase58(doc.orderId) : null) ?? doc.$id;
return {
id: `status-${doc.$id}`,
type: 'orderStatusUpdate' as const,
fromUserId: doc.$ownerId, // The seller who posted the update
orderId,
storeId: orderStoreMap.get(orderId),
orderStatus: doc.status,
createdAt: doc.$createdAt
};

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 Blocking: Verify status updates were authored by the order's seller

The contract validates the shape of orderStatusUpdate but does not require its owner to match the referenced order's sellerId. Any identity can publish an update for a public order ID, and this new notification path presents that identity and supplied status as an authentic seller update. Use the already-fetched orders to discard updates whose $ownerId does not match the corresponding seller.

source: ['codex']

{/* Action buttons for DM notifications */}
{notification.type === 'newMessage' && (
<Link
href={notification.conversationId ? `/messages?conversation=${notification.conversationId}` : '/messages'}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 Blocking: Use the query parameter supported by the messages page

The messages page reads only startConversation; it never reads conversation. The new Open Messages action therefore opens the inbox without selecting the sender's conversation. The DM branch in getNotificationUrl() also constructs the same unsupported parameter.

Suggested change
href={notification.conversationId ? `/messages?conversation=${notification.conversationId}` : '/messages'}
href={`/messages?startConversation=${notification.from.id}`}

source: ['codex']

)}
{notification.type === 'newReview' && (
<Link
href="/orders/seller"

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 Blocking: Navigate review notifications to a page that displays reviews

/orders/seller loads seller orders and status controls but never fetches or renders store reviews, so the new View Review action cannot show the referenced review. The notification already carries storeId, and /store/view?id=... is the route that loads that store's reviews.

Suggested change
href="/orders/seller"
href={notification.storeId ? `/store/view?id=${notification.storeId}` : '/store'}

source: ['codex']

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.

3 participants