Skip to content

Commit 5c1220a

Browse files
Add More Display Options to getMessages (#123)
* adds more display options to getMessages * adds tests and example app integration with new display setting * adds documentation * updates readme * updates readme * updates readme
1 parent 71891ab commit 5c1220a

File tree

6 files changed

+240
-14
lines changed

6 files changed

+240
-14
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,4 @@ log
1010
*.log
1111
results
1212
browserstack.json
13+
.DS_Store

README.md

+133-9
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ Below are the methods this SDK exposes. See [Iterable's API Docs](https://api.it
4848

4949
| Method Name | Description |
5050
|----------------------- |--------------------------------------------------------------------------------------------------------------------------- |
51-
| [`getInAppMessages`](#getInAppMessages) | Either return in-app messages as a Promise or automatically paint them to the DOM if the second argument is passed `true` |
51+
| [`getInAppMessages`](#getInAppMessages) | Either return in-app messages as a Promise or automatically paint them to the DOM if the second argument is passed `DisplayOptions` |
5252
| [`initialize`](#initialize) | Method for identifying users and setting a JWT |
5353
| [`track`](#track) | Track custom events |
5454
| [`trackInAppClick`](#trackInAppClick) | Track when a user clicks on a button or link within a message |
@@ -60,7 +60,7 @@ Below are the methods this SDK exposes. See [Iterable's API Docs](https://api.it
6060
| [`updateCart`](#updateCart) | Update _shoppingCartItems_ field on user profile |
6161
| [`updateSubscriptions`](#updateSubscriptions) | Updates user's subscriptions |
6262
| [`updateUser`](#updateUser) | Change data on a user's profile or create a user if none exists |
63-
| [`updateUserEmail`](#updateUserEmail) | Change a user's email address |
63+
| [`updateUserEmail`](#updateUserEmail) | Change a user's email and reauthenticate user with the new email address (in other words, we will call `setEmail` for you) |
6464

6565
# Usage
6666

@@ -69,7 +69,7 @@ Below are the methods this SDK exposes. See [Iterable's API Docs](https://api.it
6969
API [(see required API payload here)](https://api.iterable.com/api/docs#In-app_getMessages):
7070

7171
```ts
72-
getInAppMessages: (payload: InAppMessagesRequestParams, showMessagesAutomatically?: boolean) => Promise<TrackConsumeData> | PaintInAppMessageData
72+
getInAppMessages: (payload: InAppMessagesRequestParams, showMessagesAutomatically?: boolean | { display: 'deferred' | 'immediate' }) => Promise<TrackConsumeData> | PaintInAppMessageData
7373
```
7474

7575
SDK Specific Options:
@@ -112,7 +112,7 @@ getInAppMessages({
112112
.catch()
113113
```
114114

115-
or
115+
or if you want to show messages automatically
116116

117117
```ts
118118
import { getInAppMessages } from '@iterable/web-sdk/dist/inapp';
@@ -135,14 +135,65 @@ const {
135135
topOffset: '20px'
136136
}
137137
},
138-
true
138+
{ display: 'immediate' }
139139
);
140140

141141
request()
142142
.then()
143143
.catch();
144144
```
145145

146+
or if you want to show messages with your own custom filtering/sorting and choose to display later:
147+
148+
```ts
149+
import {
150+
getInAppMessages,
151+
sortInAppMessages,
152+
filterHiddenInAppMessages
153+
} from '@iterable/web-sdk/dist/inapp';
154+
155+
const {
156+
request,
157+
pauseMessageStream,
158+
resumeMessageStream,
159+
triggerDisplayMessages
160+
} = getInAppMessages(
161+
{
162+
count: 20,
163+
packageName: 'my-website',
164+
displayInterval: 5000,
165+
onOpenScreenReaderMessage:
166+
'hey screen reader here telling you something just popped up on your screen!',
167+
onOpenNodeToTakeFocus: 'input',
168+
closeButton: {
169+
color: 'red',
170+
size: '16px',
171+
topOffset: '20px'
172+
}
173+
},
174+
{ display: 'deferred' }
175+
);
176+
177+
request()
178+
.then(response => {
179+
/* do your own manipulation here */
180+
const filteredMessages = doStuffToMessages(response.data.inAppMessages);
181+
182+
/* also feel free to take advantage of the sorting/filtering methods we use internally */
183+
const furtherManipulatedMessages = sortInAppMessages(
184+
filterHiddenInAppMessages(response.data.inAppMessages)
185+
) as InAppMessage[];
186+
187+
/* then display them whenever you want */
188+
triggerDisplayMessages(furtherManipulatedMessages)
189+
})
190+
.catch();
191+
```
192+
193+
:rotating_light: *PLEASE NOTE*: If you choose the `deferred` option, we will _not_ do any filtering or sorting on the messages internally. You will get the messages exactly as they come down from the API, untouched. This means you may (for example) show in-app messages marked `read` or show the messages in the wrong order based on `priority`.
194+
195+
If you want to keep the default sorting and filtering, please take advantage of the `sortInAppMessages` and `filterHiddenInAppMessages` methods we provide.
196+
146197
## initialize
147198

148199
API:
@@ -171,6 +222,24 @@ const { clearRefresh, setEmail, setUserID, logout } = initialize(
171222
)
172223
```
173224

225+
:rotating_light: *PLEASE NOTE*: When you call `updateUserEmail`, we will invoke `yourAsyncJWTGenerationMethod` with the new email address even if you originally authenticated with a user ID, so if you chose to first call `setUserID`, you will need to ensure your backend can also handle JWT generation with email addresses. In other words, you need to make sure both invocations of your async JWT generation method work:
226+
227+
```ts
228+
/*
229+
the key "email" can be whatever. You just need to make sure your method can be passed an
230+
email somehow when originally calling "initialize"
231+
*/
232+
yourAsyncJWTGenerationMethod({ email: '[email protected]' })
233+
```
234+
235+
```ts
236+
/*
237+
the key "userID" can be whatever. You just need to make sure your method can be passed a
238+
user ID somehow when originally calling "initialize"
239+
*/
240+
yourAsyncJWTGenerationMethod({ userID: '1sfds32' })
241+
```
242+
174243
## track
175244

176245
API [(see required API payload here)](https://api.iterable.com/api/docs#events_track):
@@ -529,7 +598,7 @@ import { baseAxiosRequest } from '@iterable/web-sdk/dist/request';
529598

530599
## I want to automatically show my in-app messages with a delay between each
531600

532-
This SDK allows that. Simply call the `getMessages` method but pass `true` as the second parameter. This will expose some methods used to make the request to show the messages and pause and resume the queue.
601+
This SDK allows that. Simply call the `getMessages` method but pass `{ display: 'immediate' }` as the second parameter. This will expose some methods used to make the request to show the messages and pause and resume the queue.
533602

534603
Normally to request a list of in-app messages, you'd make a request like this:
535604

@@ -579,7 +648,7 @@ import { getInAppMessages } from '@iterable/web-sdk/dist/inapp';
579648
count: 20,
580649
packageName: 'my-website'
581650
},
582-
true
651+
{ display: 'immediate' }
583652
);
584653

585654
/* trigger the start of message presentation */
@@ -621,7 +690,7 @@ import { getInAppMessages } from '@iterable/web-sdk/dist/inapp';
621690
animationDuration: 400,
622691
handleLinks: 'external-new-tab'
623692
},
624-
true
693+
{ display: 'immediate' }
625694
);
626695

627696
/* trigger the start of message presentation */
@@ -658,7 +727,7 @@ import { getInAppMessages } from '@iterable/web-sdk/dist/inapp';
658727
count: 20,
659728
packageName: 'my-website'
660729
},
661-
true
730+
{ display: 'immediate' }
662731
);
663732

664733
/* trigger the start of message presentation */
@@ -679,6 +748,61 @@ import { getInAppMessages } from '@iterable/web-sdk/dist/inapp';
679748
})();
680749
```
681750

751+
Finally, you can also choose to do your own manipulation to the messages before choosing to display them:
752+
753+
```ts
754+
import { initialize } from '@iterable/web-sdk/dist/authorization';
755+
import {
756+
getInAppMessages,
757+
sortInAppMessages,
758+
filterHiddenInAppMessages
759+
} from '@iterable/web-sdk/dist/inapp';
760+
761+
(() => {
762+
const { setUserID } = initialize(
763+
'YOUR_API_KEY_HERE',
764+
({ email, userID }) => yourAsyncJWTGeneratorMethod(({ email, userID })).then(({ jwt_token }) => jwt_token)
765+
);
766+
767+
yourAsyncLoginMethod()
768+
.then(response => {
769+
setUserID(response.user_id)
770+
.then(() => {
771+
const {
772+
request,
773+
pauseMessageStream,
774+
resumeMessageStream
775+
} = getInAppMessages(
776+
{
777+
count: 20,
778+
packageName: 'my-website'
779+
},
780+
{ display: 'deferred' }
781+
);
782+
783+
/* trigger the start of message presentation */
784+
request()
785+
.then(response => {
786+
/* do your own manipulation here */
787+
const filteredMessages = doStuffToMessages(response.data.inAppMessages);
788+
789+
/*
790+
also feel free to take advantage of the sorting/filtering
791+
methods we use internally
792+
*/
793+
const furtherManipulatedMessages = sortInAppMessages(
794+
filterHiddenInAppMessages(response.data.inAppMessages)
795+
) as InAppMessage[];
796+
797+
/* then display them whenever you want */
798+
triggerDisplayMessages(furtherManipulatedMessages)
799+
})
800+
.catch();
801+
})
802+
})
803+
})();
804+
```
805+
682806
## I want my messages to look good on every device and be responsive
683807

684808
This SDK already handles that for you. The rules for the in-app message presentation varies based on which display type you've selected. Here's a table to explain how it works:

example/src/index.ts

+8-2
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,12 @@ import {
3131
}
3232
);
3333

34-
const { request, pauseMessageStream, resumeMessageStream } = getInAppMessages(
34+
const {
35+
request,
36+
pauseMessageStream,
37+
resumeMessageStream,
38+
triggerDisplayMessages
39+
} = getInAppMessages(
3540
{
3641
count: 20,
3742
displayInterval: 1000,
@@ -52,7 +57,7 @@ import {
5257
// topOffset: '6%'
5358
}
5459
},
55-
true
60+
{ display: 'deferred' }
5661
);
5762

5863
const startBtn = document.getElementById('start');
@@ -68,6 +73,7 @@ import {
6873
startBtn.className = 'disabled';
6974
request()
7075
.then((response) => {
76+
triggerDisplayMessages(response.data.inAppMessages);
7177
startBtn.innerText = `${response.data.inAppMessages.length} total messages retrieved!`;
7278
})
7379
.catch(console.warn);

src/inapp/inapp.ts

+43-2
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,20 @@ export function getInAppMessages(
5959
};
6060
export function getInAppMessages(
6161
payload: InAppMessagesRequestParams,
62-
showInAppMessagesAutomatically?: boolean
62+
showInAppMessagesAutomatically: { display: 'immediate' | 'deferred' }
63+
): {
64+
pauseMessageStream: () => void;
65+
resumeMessageStream: () => Promise<HTMLIFrameElement | ''>;
66+
request: () => IterablePromise<InAppMessageResponse>;
67+
triggerDisplayMessages: (
68+
messages: Partial<InAppMessage>[]
69+
) => Promise<HTMLIFrameElement | ''>;
70+
};
71+
export function getInAppMessages(
72+
payload: InAppMessagesRequestParams,
73+
showInAppMessagesAutomatically?:
74+
| boolean
75+
| { display: 'immediate' | 'deferred' }
6376
) {
6477
clearMessages();
6578

@@ -499,6 +512,18 @@ export function getInAppMessages(
499512
return Promise.resolve('');
500513
};
501514

515+
const maybeDisplayFn =
516+
typeof showInAppMessagesAutomatically !== 'boolean' &&
517+
showInAppMessagesAutomatically.display === 'deferred'
518+
? {
519+
triggerDisplayMessages: (messages: Partial<InAppMessage>[]) => {
520+
parsedMessages = messages as InAppMessage[];
521+
522+
return paintMessageToDOM();
523+
}
524+
}
525+
: {};
526+
502527
return {
503528
request: (): IterablePromise<InAppMessageResponse> =>
504529
baseIterableRequest<InAppMessageResponse>({
@@ -521,6 +546,21 @@ export function getInAppMessages(
521546
return response;
522547
})
523548
.then((response) => {
549+
if (
550+
typeof showInAppMessagesAutomatically !== 'boolean' &&
551+
showInAppMessagesAutomatically.display === 'deferred'
552+
) {
553+
/*
554+
if the user passed "deferred" for the second argument to _getMessages_
555+
then they're going to choose to display the in-app messages when they want
556+
with the returned, _triggerDisplayMessages_ function. So early return here
557+
with no filtering or sorting.
558+
*/
559+
return response;
560+
}
561+
562+
/* otherwise, they're choosing to show the messages automatically */
563+
524564
/*
525565
if the user passed the flag to automatically paint the in-app messages
526566
to the DOM, start a timer and show each in-app message upon close + timer countdown
@@ -553,7 +593,8 @@ export function getInAppMessages(
553593
},
554594
resumeMessageStream: () => {
555595
return paintMessageToDOM();
556-
}
596+
},
597+
...maybeDisplayFn
557598
};
558599
}
559600

src/inapp/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
export * from './inapp';
22
export * from './types';
3+
export { filterHiddenInAppMessages, sortInAppMessages } from './utils';

0 commit comments

Comments
 (0)