Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
84 changes: 84 additions & 0 deletions examples/inbox.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
async function refreshInboxMessages(messages) {
const unreadCount = await window.Gist.getInboxUnopenedCount();
const badge = document.getElementById('inboxBadge');
if (unreadCount > 0) {
badge.textContent = unreadCount;
badge.style.display = 'inline-block';
} else {
badge.style.display = 'none';
}

if (!messages) {
messages = await window.Gist.getInboxMessages();
}
Copy link

Choose a reason for hiding this comment

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

Bug: Badge count and message list use different data sources

When refreshInboxMessages is called with a messages parameter (from the messageInboxUpdated event), the badge count is still fetched separately via getInboxUnopenedCount() which reads from localStorage. This causes the badge count and the rendered message list to potentially show inconsistent data - the badge reflects filtered/stored messages while the list renders the event's messages. The unopened count calculation needs to use the same messages array when it's provided.

Fix in Cursor Fix in Web


const content = document.getElementById('inboxPanelContent');

if (messages.length === 0) {
content.innerHTML = '<p class="no-messages">No messages</p>';
return;
}

let html = '';
for (const message of messages) {
const props = message.properties || {};
const queueId = message.queueId;
const propertiesJson = JSON.stringify(props, null, 2);

html += `
<div class="inbox-message ${!message.opened ? 'unopened' : ''}" data-queue-id="${queueId}">
<div class="inbox-message-header">
<strong>Properties</strong>
<p>Sent at ${new Date(message.sentAt).toLocaleString()}</p>
${!message.opened ? '<span class="unread-dot"></span>' : ''}
</div>
<div class="inbox-message-body">
<pre>${propertiesJson}</pre>
</div>
<div class="inbox-message-actions">
${!message.opened ? `<button onclick="updateInboxMessageOpenState('${queueId}',true)">Mark as opened</button>` : `<button onclick="updateInboxMessageOpenState('${queueId}',false)">Mark as unopened</button>`}
<button onclick="deleteMessage('${queueId}')">Delete</button>
</div>
</div>
`;
}

content.innerHTML = html;
}

// eslint-disable-next-line no-unused-vars
async function updateInboxMessageOpenState(queueId, opened) {
try {
await window.Gist.updateInboxMessageOpenState(queueId, opened);
} catch (error) {
console.error('Failed to mark message as read:', error);
alert('Failed to mark message as read. Please try again.');
}
}

// eslint-disable-next-line no-unused-vars
async function deleteMessage(queueId) {
try {
await window.Gist.removeInboxMessage(queueId);
} catch (error) {
console.error('Failed to delete message:', error);
alert('Failed to delete message. Please try again.');
}
}

document.querySelectorAll(".toggle-inbox").forEach(element => {
element.addEventListener("click", () => {
const panel = document.getElementById('inboxPanel');
if (panel.style.display === 'none') {
panel.style.display = 'block';
} else {
panel.style.display = 'none';
}
});
});

refreshInboxMessages();

window.Gist.events.on('messageInboxUpdated', async function(messages) {
await refreshInboxMessages(messages);
});
19 changes: 19 additions & 0 deletions examples/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,24 @@
<link href="styles.css" rel="stylesheet">
</head>
<body>
<div class="inbox-header">
<div class="inbox-icon-container toggle-inbox">
<svg class="inbox-icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<polyline points="22 12 16 12 14 15 10 15 8 12 2 12"></polyline>
<path d="M5.45 5.11L2 12v6a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-6l-3.45-6.89A2 2 0 0 0 16.76 4H7.24a2 2 0 0 0-1.79 1.11z"></path>
</svg>
<span class="inbox-badge" id="inboxBadge" style="display: none;">0</span>
</div>
</div>
<div class="inbox-panel" id="inboxPanel" style="display: none;">
<div class="inbox-panel-header">
<h3>Inbox Messages</h3>
<button class="close-btn toggle-inbox">×</button>
</div>
<div class="inbox-panel-content" id="inboxPanelContent">
<p class="no-messages">No messages</p>
</div>
</div>
<div id="banner"></div>
<div class="row header">
<h1>Gist for Web</h1>
Expand Down Expand Up @@ -122,5 +140,6 @@ <h1>Gist for Web</h1>
Gist.setCustomAttribute("cio_anonymous_id", "123456");
}
</script>
<script src="inbox.js"></script>
</body>
</html>
219 changes: 219 additions & 0 deletions examples/styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ html, body {
line-height: 1.4;
}

body {
padding-top: 40px;
}

.row {
margin: 0px 16px;
display: flex;
Expand Down Expand Up @@ -52,4 +56,219 @@ h1 {
.button {
width: 100%;
}
}

/* Inbox Header */
.inbox-header {
height: 40px;
background-color: #e76f51;
display: flex;
align-items: center;
justify-content: flex-end;
padding: 0 16px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
position: fixed;
top: 0;
left: 0;
right: 0;
z-index: 1000;
}

.inbox-icon-container {
position: relative;
cursor: pointer;
padding: 8px;
border-radius: 4px;
transition: background-color 0.2s;
}

.inbox-icon-container:hover {
background-color: rgba(255,255,255,0.1);
}

.inbox-icon {
color: white;
display: block;
}

.inbox-badge {
position: absolute;
top: 2px;
right: 2px;
background-color: #264653;
color: white;
border-radius: 10px;
padding: 2px 6px;
font-size: 11px;
font-weight: bold;
min-width: 18px;
text-align: center;
}

/* Inbox Panel */
.inbox-panel {
position: fixed;
top: 40px;
right: 0;
width: 400px;
max-width: 100%;
max-height: calc(100vh - 40px);
background-color: white;
box-shadow: -2px 0 8px rgba(0,0,0,0.15);
z-index: 999;
overflow-y: auto;
display: flex;
flex-direction: column;
}

.inbox-panel .inbox-panel-content {
min-height: 400px;
}

.inbox-panel-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 16px;
border-bottom: 1px solid #e0e0e0;
background-color: #f5f5f5;
}

.inbox-panel-header h3 {
margin: 0;
font-size: 18px;
}

.close-btn {
background: none;
border: none;
font-size: 28px;
cursor: pointer;
color: #666;
padding: 0;
width: 30px;
height: 30px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 4px;
}

.close-btn:hover {
background-color: rgba(0,0,0,0.05);
}

.inbox-panel-content {
padding: 16px;
}

.no-messages {
text-align: center;
color: #999;
padding: 32px 16px;
font-size: 16px;
}

/* Inbox Message */
.inbox-message {
background-color: #fff;
border: 1px solid #e0e0e0;
border-radius: 8px;
padding: 16px;
margin-bottom: 12px;
transition: box-shadow 0.2s;
}

.inbox-message:hover {
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
}

.inbox-message.unread {
background-color: #f0f9ff;
border-color: #2a9d8f;
}

.inbox-message-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 8px;
}

.inbox-message-header strong {
font-size: 16px;
color: #264653;
}

.inbox-message-header p {
font-size: 12px;
font-style: italic;
}

.unread-dot {
width: 8px;
height: 8px;
background-color: #2a9d8f;
border-radius: 50%;
display: inline-block;
margin-left: 8px;
}

.inbox-message-body {
font-size: 14px;
color: #555;
line-height: 1.5;
margin-bottom: 12px;
}

.inbox-message-body pre {
background-color: #f5f5f5;
padding: 12px;
border-radius: 4px;
overflow-x: auto;
font-size: 12px;
margin: 0;
font-family: 'Courier New', monospace;
}

.inbox-message-footer {
margin-bottom: 12px;
}

.inbox-message-footer a {
color: #2a9d8f;
text-decoration: none;
font-size: 14px;
font-weight: 600;
}

.inbox-message-footer a:hover {
text-decoration: underline;
}

.inbox-message-actions {
display: flex;
gap: 8px;
}

.inbox-message-actions button {
padding: 6px 12px;
font-size: 13px;
border: 1px solid #e0e0e0;
background-color: white;
color: #555;
border-radius: 4px;
cursor: pointer;
transition: all 0.2s;
}

.inbox-message-actions button:hover {
background-color: #f5f5f5;
border-color: #2a9d8f;
}

@media (max-width: 800px) {
.inbox-panel {
width: 100%;
}
}
24 changes: 24 additions & 0 deletions src/gist.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@ import { showMessage, embedMessage, hideMessage, removePersistentMessage, fetchM
import { setUserLocale } from "./managers/locale-manager";
import { setCustomAttribute, clearCustomAttributes, removeCustomAttribute } from "./managers/custom-attribute-manager";
import { setupPreview } from "./utilities/preview-mode";
import {
getInboxMessagesFromLocalStore,
updateInboxMessageOpenState,
removeInboxMessage
} from "./managers/inbox-message-manager";

export default class {
static async setup(config) {
Expand Down Expand Up @@ -125,4 +130,23 @@ export default class {
this.events.dispatch('messageAction', {message: message, action: action, name: name});
}

// Inbox Messages

static async getInboxUnopenedCount() {
const messages = await getInboxMessagesFromLocalStore();
return messages.filter(msg => !msg.opened).length;
}

static async getInboxMessages() {
return await getInboxMessagesFromLocalStore();
}

static async updateInboxMessageOpenState(queueId, opened) {
return await updateInboxMessageOpenState(queueId, opened);
}

static async removeInboxMessage(queueId) {
return await removeInboxMessage(queueId);
}

}
Loading