-
Notifications
You must be signed in to change notification settings - Fork 6
Support for inbox messages #70
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,164 @@ | ||
| export interface GistInboxMessage { | ||
| messageType: string | ||
| expiry: string | ||
| priority: number | ||
| topics?: string[] | ||
| properties: { [key: string]: any } | ||
| queueId: string | ||
| userToken: string | ||
| deliveryId: string | ||
| sentAt: string | ||
| opened: boolean | ||
| } | ||
|
|
||
| export interface InboxMessage { | ||
| // Unique identifier for this messeage | ||
| readonly messageId: string | ||
|
|
||
| // If the message has been marked opened | ||
| readonly opened: boolean | ||
|
|
||
| // The properties payload of the message | ||
| readonly properties: { [key: string]: any } | ||
|
|
||
| // When the message was sent | ||
| readonly sentAt: string | ||
|
|
||
| /** | ||
| * Marks this message as opened | ||
| * @returns Promise that resolves when the message is marked as opened | ||
| */ | ||
| markOpened(): Promise<void> | ||
|
|
||
| /** | ||
| * Marks this message as unopened | ||
| * @returns Promise that resolves when the message is marked as unopened | ||
| */ | ||
| markUnopened(): Promise<void> | ||
|
|
||
| /** | ||
| * Marks this message as deleted | ||
| * @returns Promise that resolves when the message is deleted | ||
| */ | ||
| markDeleted(): Promise<void> | ||
| } | ||
|
|
||
| export interface InboxAPI { | ||
| /** | ||
| * Returns the total number of messages | ||
| * @returns Promise that resolves to the total count of messages | ||
| */ | ||
| total(): Promise<number> | ||
|
|
||
| /** | ||
| * Returns the count of unopened messages | ||
| * @returns Promise that resolves to the count of unopened messages | ||
| */ | ||
| totalUnopened(): Promise<number> | ||
|
|
||
| /** | ||
| * Returns all inbox messages | ||
| * @returns Promise that resolves to an array of inbox messages | ||
| */ | ||
| messages(): Promise<InboxMessage[]> | ||
|
|
||
| /** | ||
| * Subscribe to inbox message updates | ||
| * @param callback - Function called with array of InboxMessage objects when the message list changes. | ||
| * @returns Unsubscribe function to remove the listener | ||
| */ | ||
| onUpdates(callback: (messages: InboxMessage[]) => void): () => void | ||
| } | ||
|
|
||
| function createInboxMessage( | ||
| gist: any, | ||
| gistMessage: GistInboxMessage | ||
| ): InboxMessage { | ||
| return { | ||
| sentAt: gistMessage.sentAt, | ||
| messageId: gistMessage.queueId, | ||
| opened: gistMessage?.opened === true, | ||
| properties: gistMessage.properties, | ||
| markOpened: async () => { | ||
| await gist.updateInboxMessageOpenState(gistMessage.queueId, true) | ||
| }, | ||
| markUnopened: async () => { | ||
| await gist.updateInboxMessageOpenState(gistMessage.queueId, false) | ||
| }, | ||
| markDeleted: async () => { | ||
| await gist.removeInboxMessage(gistMessage.queueId) | ||
| }, | ||
| } | ||
| } | ||
|
|
||
| async function getFilteredMessages( | ||
| gist: any, | ||
| topics: string[], | ||
| messages: GistInboxMessage[] | null | ||
| ): Promise<GistInboxMessage[]> { | ||
| let allMessages = messages | ||
| if (!allMessages || !Array.isArray(allMessages) || allMessages.length === 0) { | ||
| allMessages = (await gist.getInboxMessages()) as GistInboxMessage[] | ||
| } | ||
clabland marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| if (!allMessages || !Array.isArray(allMessages)) { | ||
| return [] | ||
| } | ||
|
|
||
| if (topics.length === 0) { | ||
| return allMessages | ||
| } | ||
|
|
||
| return allMessages.filter((message) => { | ||
| const messageTopics = message.topics | ||
| if (!messageTopics || messageTopics.length === 0) { | ||
| return false | ||
| } | ||
| return messageTopics.some((messageTopic) => topics.includes(messageTopic)) | ||
| }) | ||
| } | ||
|
|
||
| export function createInboxAPI(gist: any, topics: string[]): InboxAPI { | ||
| return { | ||
| total: async () => { | ||
| const messages = await getFilteredMessages(gist, topics, null) | ||
| return messages.length | ||
| }, | ||
| totalUnopened: async () => { | ||
| const messages = await getFilteredMessages(gist, topics, null) | ||
| return messages.filter((message) => { | ||
| return message?.opened !== true | ||
| }).length | ||
| }, | ||
| messages: async (): Promise<InboxMessage[]> => { | ||
| const messages = await getFilteredMessages(gist, topics, null) | ||
| return messages.map((msg) => createInboxMessage(gist, msg)) | ||
| }, | ||
| onUpdates: (callback: (messages: InboxMessage[]) => void): (() => void) => { | ||
| const handler = async (gistMessages: GistInboxMessage[]) => { | ||
| console.log('onUpdates handler called with messages:', gistMessages) | ||
clabland marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| try { | ||
| const filteredMessages = await getFilteredMessages( | ||
| gist, | ||
| topics, | ||
| gistMessages | ||
| ) | ||
| const inboxMessages = filteredMessages.map((msg) => | ||
| createInboxMessage(gist, msg) | ||
| ) | ||
|
|
||
| callback(inboxMessages) | ||
| } catch (error) { | ||
| console.error('Error processing inbox updates:', error) | ||
| } | ||
| } | ||
|
|
||
| gist.events.on('messageInboxUpdated', handler) | ||
|
|
||
| // Return unsubscribe function | ||
| return () => { | ||
| gist.events.off('messageInboxUpdated', handler) | ||
| } | ||
| }, | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -11,8 +11,11 @@ import { | |
| ContentType, | ||
| } from './events' | ||
| import Gist from 'customerio-gist-web' | ||
| import type { InboxAPI, InboxMessage, GistInboxMessage } from './inbox_messages' | ||
| import { createInboxAPI } from './inbox_messages' | ||
|
|
||
| export { InAppEvents } | ||
| export type { InboxAPI, InboxMessage } | ||
|
|
||
| export type InAppPluginSettings = { | ||
| siteId: string | undefined | ||
|
|
@@ -96,6 +99,12 @@ export function InAppPlugin(settings: InAppPluginSettings): Plugin { | |
| } | ||
| }) | ||
|
|
||
| Gist.events.on('inboxMessageAction', (params: any) => { | ||
| if (params?.message && params?.action !== '') { | ||
| _handleInboxMessageAction(_analytics, params.message, params.action) | ||
| } | ||
| }) | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Bug: Incorrect condition allows undefined action values throughThe condition |
||
|
|
||
| Gist.events.on('messageAction', (params: any) => { | ||
| const deliveryId: string = params?.message?.properties?.gist?.campaignId | ||
| if (settings.events) { | ||
|
|
@@ -214,6 +223,14 @@ export function InAppPlugin(settings: InAppPluginSettings): Plugin { | |
|
|
||
| await syncUserToken(ctx) | ||
| attachListeners() | ||
| ;(instance as any).inbox = (...topics: string[]): InboxAPI => { | ||
| if (!_pluginLoaded) { | ||
| throw new Error( | ||
| 'Customer.io In-App Plugin is not loaded yet. Ensure the plugin is initialized before calling inbox().' | ||
| ) | ||
| } | ||
| return createInboxAPI(Gist, topics) | ||
| } | ||
|
|
||
| _pluginLoaded = true | ||
|
|
||
|
|
@@ -236,6 +253,19 @@ export function InAppPlugin(settings: InAppPluginSettings): Plugin { | |
| return customerio | ||
| } | ||
|
|
||
| function _handleInboxMessageAction( | ||
| analyticsInstance: Analytics, | ||
| message: GistInboxMessage, | ||
| action: string | ||
| ) { | ||
| if (action === 'opened' && message?.deliveryId !== '') { | ||
clabland marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| void analyticsInstance.track(JourneysEvents.Metric, { | ||
| deliveryId: message.deliveryId, | ||
| metric: JourneysEvents.Opened, | ||
| }) | ||
| } | ||
| } | ||
|
|
||
| function _error(msg: string) { | ||
| console.error(`[Customer.io In-App Plugin] ${msg}`) | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.