Skip to content

Conversation

@clabland
Copy link
Contributor

@clabland clabland commented Nov 26, 2025

Note

Add inbox messages support across SDK (APIs, polling/SSE integration) with example UI and styles; bump user queue endpoint to v4.

  • Core SDK:
    • Inbox message manager: New src/managers/inbox-message-manager.js to cache inbox messages per-user, filter by expiry/topic, dispatch inboxMessages events, PATCH /api/v1/messages/:queueId to mark opened, and remove via logUserMessageView.
    • Public API: Expose Gist.getInboxMessages, Gist.markInboxMessageAsOpened, Gist.removeInboxMessage in src/gist.js.
    • Queue integration: In src/managers/queue-manager.js, on polling, handle response.data.inAppMessages and response.data.inboxMessages; add SSE handler for inbox_messages to update local store.
    • Queue service: Update user queue endpoint to /api/v4/users in src/services/queue-service.js.
  • Examples/UI:
    • Add inbox header, badge, and slide-out panel in examples/index.html with render/mark-read/delete handlers and periodic badge refresh.
    • Add comprehensive inbox styles and adjust layout in examples/styles.css (e.g., fixed header, panel, message states).

Written by Cursor Bugbot for commit 96df16c. This will update automatically on new commits. Configure here.

@clabland clabland requested a review from a team as a code owner November 26, 2025 23:19
}

// Update badge immediately
await updateInboxBadge();
Copy link

Choose a reason for hiding this comment

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

Bug: Badge update uses stale data before deletion completes

The deleteMessage function calls updateInboxBadge() before Gist.removeInboxMessage(queueId) executes. Since updateInboxBadge reads from the local store (via Gist.getInboxMessages()), and the message hasn't been removed from the store yet, the badge displays an incorrect count that still includes the deleted message. Furthermore, after a successful deletion there's no subsequent badge update, leaving the badge permanently out of sync until the 5-second periodic refresh.

Fix in Cursor Fix in Web

Copy link
Collaborator

Choose a reason for hiding this comment

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

Makes sense yeah.

// Inbox functionality
async function updateInboxBadge() {
const messages = await Gist.getInboxMessages();
const unreadCount = messages.filter(msg => !msg.opened).length;
Copy link
Collaborator

Choose a reason for hiding this comment

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

Should we offer this as a helper within the SDK? something like getInboxUnreadCount()?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I was going to add that on the cdp-analytics-js side, but I can add it here as well.

}
}

async function loadInboxMessages() {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Nitpick: Should we put inbox methods in their own js file? tbh the whole file could benefit from a bit of a refactor.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah, I'll clean this up.

Comment on lines +229 to +230
await loadInboxMessages();
await updateInboxBadge();
Copy link
Collaborator

Choose a reason for hiding this comment

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

Maybe put these two in a finally block?


setKeyToLocalStore(inboxLocalStoreName, messages, expiryDate);

Gist.events.dispatch('inboxMessages', messages);
Copy link
Collaborator

Choose a reason for hiding this comment

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

Should we rename this to messageInboxUpdated? to match the rest: messageShown, messageDismissed, messageAction etc...

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah that makes sense. It's really only for gist-web<->cdp-analytics-js interaction, but might be clearer as messageInboxUpdated.

export async function markInboxMessageAsOpened(queueId) {
const inboxLocalStoreName = await getInboxMessagesLocalStoreName();
if (!inboxLocalStoreName) {
throw new Error('User token not available');
Copy link
Collaborator

Choose a reason for hiding this comment

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

We usually handle errors gracefully since we're an SDK that "just works" by itself. In the inbox case, its a bit different since customers will be interfacing with it, so we need to give them some feedback.

But in this specific case, do you think we need to throw here?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think this snuck in during some refactoring, I can match the existing in-app local store behavior and just return when there isn't a name.

}

try {
const response = await UserNetworkInstance()(`/api/v1/messages/${queueId}`, {
Copy link
Collaborator

Choose a reason for hiding this comment

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

I think this should live in a message-service.js under services.


// Update badge on load and periodically
updateInboxBadge();
setInterval(updateInboxBadge, 5000);
Copy link
Collaborator

Choose a reason for hiding this comment

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

I think we should hook this up to the new messageInboxUpdated event so it can update in realtime.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

3 participants