@aws-amplify/notifications 0.0.2-unstable.7462
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +0 -0
- package/LICENSE +201 -0
- package/lib/.tsbuildinfo +3 -0
- package/lib/InAppMessaging/InAppMessaging.d.ts +55 -0
- package/lib/InAppMessaging/InAppMessaging.js +342 -0
- package/lib/InAppMessaging/InAppMessaging.js.map +1 -0
- package/lib/InAppMessaging/Providers/AWSPinpointProvider/index.d.ts +50 -0
- package/lib/InAppMessaging/Providers/AWSPinpointProvider/index.js +482 -0
- package/lib/InAppMessaging/Providers/AWSPinpointProvider/index.js.map +1 -0
- package/lib/InAppMessaging/Providers/AWSPinpointProvider/types.d.ts +25 -0
- package/lib/InAppMessaging/Providers/AWSPinpointProvider/types.js +11 -0
- package/lib/InAppMessaging/Providers/AWSPinpointProvider/types.js.map +1 -0
- package/lib/InAppMessaging/Providers/AWSPinpointProvider/utils.d.ts +18 -0
- package/lib/InAppMessaging/Providers/AWSPinpointProvider/utils.js +254 -0
- package/lib/InAppMessaging/Providers/AWSPinpointProvider/utils.js.map +1 -0
- package/lib/InAppMessaging/Providers/index.d.ts +1 -0
- package/lib/InAppMessaging/Providers/index.js +7 -0
- package/lib/InAppMessaging/Providers/index.js.map +1 -0
- package/lib/InAppMessaging/SessionTracker/SessionTracker.d.ts +9 -0
- package/lib/InAppMessaging/SessionTracker/SessionTracker.js +68 -0
- package/lib/InAppMessaging/SessionTracker/SessionTracker.js.map +1 -0
- package/lib/InAppMessaging/SessionTracker/SessionTracker.native.d.ts +10 -0
- package/lib/InAppMessaging/SessionTracker/SessionTracker.native.js +53 -0
- package/lib/InAppMessaging/SessionTracker/SessionTracker.native.js.map +1 -0
- package/lib/InAppMessaging/SessionTracker/index.d.ts +3 -0
- package/lib/InAppMessaging/SessionTracker/index.js +8 -0
- package/lib/InAppMessaging/SessionTracker/index.js.map +1 -0
- package/lib/InAppMessaging/SessionTracker/types.d.ts +6 -0
- package/lib/InAppMessaging/SessionTracker/types.js +5 -0
- package/lib/InAppMessaging/SessionTracker/types.js.map +1 -0
- package/lib/InAppMessaging/eventListeners.d.ts +3 -0
- package/lib/InAppMessaging/eventListeners.js +28 -0
- package/lib/InAppMessaging/eventListeners.js.map +1 -0
- package/lib/InAppMessaging/index.d.ts +4 -0
- package/lib/InAppMessaging/index.js +11 -0
- package/lib/InAppMessaging/index.js.map +1 -0
- package/lib/InAppMessaging/types.d.ts +99 -0
- package/lib/InAppMessaging/types.js +10 -0
- package/lib/InAppMessaging/types.js.map +1 -0
- package/lib/Notifications.d.ts +20 -0
- package/lib/Notifications.js +46 -0
- package/lib/Notifications.js.map +1 -0
- package/lib/index.d.ts +3 -0
- package/lib/index.js +9 -0
- package/lib/index.js.map +1 -0
- package/lib/types.d.ts +7 -0
- package/lib/types.js +3 -0
- package/lib/types.js.map +1 -0
- package/lib-esm/.tsbuildinfo +3 -0
- package/lib-esm/InAppMessaging/InAppMessaging.d.ts +55 -0
- package/lib-esm/InAppMessaging/InAppMessaging.js +340 -0
- package/lib-esm/InAppMessaging/InAppMessaging.js.map +1 -0
- package/lib-esm/InAppMessaging/Providers/AWSPinpointProvider/index.d.ts +50 -0
- package/lib-esm/InAppMessaging/Providers/AWSPinpointProvider/index.js +480 -0
- package/lib-esm/InAppMessaging/Providers/AWSPinpointProvider/index.js.map +1 -0
- package/lib-esm/InAppMessaging/Providers/AWSPinpointProvider/types.d.ts +25 -0
- package/lib-esm/InAppMessaging/Providers/AWSPinpointProvider/types.js +9 -0
- package/lib-esm/InAppMessaging/Providers/AWSPinpointProvider/types.js.map +1 -0
- package/lib-esm/InAppMessaging/Providers/AWSPinpointProvider/utils.d.ts +18 -0
- package/lib-esm/InAppMessaging/Providers/AWSPinpointProvider/utils.js +252 -0
- package/lib-esm/InAppMessaging/Providers/AWSPinpointProvider/utils.js.map +1 -0
- package/lib-esm/InAppMessaging/Providers/index.d.ts +1 -0
- package/lib-esm/InAppMessaging/Providers/index.js +4 -0
- package/lib-esm/InAppMessaging/Providers/index.js.map +1 -0
- package/lib-esm/InAppMessaging/SessionTracker/SessionTracker.d.ts +9 -0
- package/lib-esm/InAppMessaging/SessionTracker/SessionTracker.js +65 -0
- package/lib-esm/InAppMessaging/SessionTracker/SessionTracker.js.map +1 -0
- package/lib-esm/InAppMessaging/SessionTracker/SessionTracker.native.d.ts +10 -0
- package/lib-esm/InAppMessaging/SessionTracker/SessionTracker.native.js +50 -0
- package/lib-esm/InAppMessaging/SessionTracker/SessionTracker.native.js.map +1 -0
- package/lib-esm/InAppMessaging/SessionTracker/index.d.ts +3 -0
- package/lib-esm/InAppMessaging/SessionTracker/index.js +5 -0
- package/lib-esm/InAppMessaging/SessionTracker/index.js.map +1 -0
- package/lib-esm/InAppMessaging/SessionTracker/types.d.ts +6 -0
- package/lib-esm/InAppMessaging/SessionTracker/types.js +3 -0
- package/lib-esm/InAppMessaging/SessionTracker/types.js.map +1 -0
- package/lib-esm/InAppMessaging/eventListeners.d.ts +3 -0
- package/lib-esm/InAppMessaging/eventListeners.js +26 -0
- package/lib-esm/InAppMessaging/eventListeners.js.map +1 -0
- package/lib-esm/InAppMessaging/index.d.ts +4 -0
- package/lib-esm/InAppMessaging/index.js +6 -0
- package/lib-esm/InAppMessaging/index.js.map +1 -0
- package/lib-esm/InAppMessaging/types.d.ts +99 -0
- package/lib-esm/InAppMessaging/types.js +8 -0
- package/lib-esm/InAppMessaging/types.js.map +1 -0
- package/lib-esm/Notifications.d.ts +20 -0
- package/lib-esm/Notifications.js +44 -0
- package/lib-esm/Notifications.js.map +1 -0
- package/lib-esm/index.d.ts +3 -0
- package/lib-esm/index.js +5 -0
- package/lib-esm/index.js.map +1 -0
- package/lib-esm/types.d.ts +7 -0
- package/lib-esm/types.js +1 -0
- package/lib-esm/types.js.map +1 -0
- package/package.json +99 -0
- package/src/InAppMessaging/InAppMessaging.ts +330 -0
- package/src/InAppMessaging/Providers/AWSPinpointProvider/index.ts +522 -0
- package/src/InAppMessaging/Providers/AWSPinpointProvider/types.ts +38 -0
- package/src/InAppMessaging/Providers/AWSPinpointProvider/utils.ts +329 -0
- package/src/InAppMessaging/Providers/index.ts +4 -0
- package/src/InAppMessaging/SessionTracker/SessionTracker.native.ts +60 -0
- package/src/InAppMessaging/SessionTracker/SessionTracker.ts +78 -0
- package/src/InAppMessaging/SessionTracker/index.ts +6 -0
- package/src/InAppMessaging/SessionTracker/types.ts +11 -0
- package/src/InAppMessaging/eventListeners.ts +42 -0
- package/src/InAppMessaging/index.ts +20 -0
- package/src/InAppMessaging/types.ts +146 -0
- package/src/Notifications.ts +49 -0
- package/src/index.ts +20 -0
- package/src/types.ts +11 -0
|
@@ -0,0 +1,329 @@
|
|
|
1
|
+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
|
|
4
|
+
import { Amplify, ConsoleLogger, Hub } from '@aws-amplify/core';
|
|
5
|
+
import {
|
|
6
|
+
InAppMessageCampaign as PinpointInAppMessage,
|
|
7
|
+
Layout as PinpointInAppMessageLayout,
|
|
8
|
+
} from '@aws-sdk/client-pinpoint';
|
|
9
|
+
import isEmpty from 'lodash/isEmpty';
|
|
10
|
+
import {
|
|
11
|
+
InAppMessage,
|
|
12
|
+
InAppMessageAction,
|
|
13
|
+
InAppMessageContent,
|
|
14
|
+
InAppMessageLayout,
|
|
15
|
+
InAppMessageTextAlign,
|
|
16
|
+
InAppMessagingEvent,
|
|
17
|
+
} from '../../types';
|
|
18
|
+
import { AWSPinpointMessageEvent, MetricsComparator } from './types';
|
|
19
|
+
|
|
20
|
+
const AMPLIFY_SYMBOL = (
|
|
21
|
+
typeof Symbol !== 'undefined' && typeof Symbol.for === 'function'
|
|
22
|
+
? Symbol.for('amplify_default')
|
|
23
|
+
: '@@amplify_default'
|
|
24
|
+
) as Symbol;
|
|
25
|
+
const DELIVERY_TYPE = 'IN_APP_MESSAGE';
|
|
26
|
+
|
|
27
|
+
let eventNameMemo = {};
|
|
28
|
+
let eventAttributesMemo = {};
|
|
29
|
+
let eventMetricsMemo = {};
|
|
30
|
+
|
|
31
|
+
export const logger = new ConsoleLogger('AWSPinpointProvider');
|
|
32
|
+
|
|
33
|
+
export const dispatchInAppMessagingEvent = (
|
|
34
|
+
event: string,
|
|
35
|
+
data: any,
|
|
36
|
+
message?: string
|
|
37
|
+
) => {
|
|
38
|
+
Hub.dispatch(
|
|
39
|
+
'inAppMessaging',
|
|
40
|
+
{ event, data, message },
|
|
41
|
+
'InAppMessaging',
|
|
42
|
+
AMPLIFY_SYMBOL
|
|
43
|
+
);
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
export const recordAnalyticsEvent = (
|
|
47
|
+
event: AWSPinpointMessageEvent,
|
|
48
|
+
message: InAppMessage
|
|
49
|
+
) => {
|
|
50
|
+
if (!message) {
|
|
51
|
+
logger.debug(
|
|
52
|
+
'Unable to record analytics event - no InAppMessage was received'
|
|
53
|
+
);
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
if (Amplify.Analytics && typeof Amplify.Analytics.record === 'function') {
|
|
57
|
+
const { id, metadata } = message;
|
|
58
|
+
Amplify.Analytics.record({
|
|
59
|
+
name: event,
|
|
60
|
+
attributes: {
|
|
61
|
+
campaign_id: id,
|
|
62
|
+
delivery_type: DELIVERY_TYPE,
|
|
63
|
+
treatment_id: metadata?.treatmentId,
|
|
64
|
+
},
|
|
65
|
+
});
|
|
66
|
+
} else {
|
|
67
|
+
logger.debug('Analytics module is not registered into Amplify');
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
export const getStartOfDay = (): string => {
|
|
72
|
+
const now = new Date();
|
|
73
|
+
now.setHours(0, 0, 0, 0);
|
|
74
|
+
return now.toISOString();
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
export const matchesEventType = (
|
|
78
|
+
{ CampaignId, Schedule }: PinpointInAppMessage,
|
|
79
|
+
{ name: eventType }: InAppMessagingEvent
|
|
80
|
+
) => {
|
|
81
|
+
const { EventType } = Schedule?.EventFilter?.Dimensions;
|
|
82
|
+
const memoKey = `${CampaignId}:${eventType}`;
|
|
83
|
+
if (!eventNameMemo.hasOwnProperty(memoKey)) {
|
|
84
|
+
eventNameMemo[memoKey] = !!EventType?.Values.includes(eventType);
|
|
85
|
+
}
|
|
86
|
+
return eventNameMemo[memoKey];
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
export const matchesAttributes = (
|
|
90
|
+
{ CampaignId, Schedule }: PinpointInAppMessage,
|
|
91
|
+
{ attributes }: InAppMessagingEvent
|
|
92
|
+
): boolean => {
|
|
93
|
+
const { Attributes } = Schedule?.EventFilter?.Dimensions;
|
|
94
|
+
if (isEmpty(Attributes)) {
|
|
95
|
+
// if message does not have attributes defined it does not matter what attributes are on the event
|
|
96
|
+
return true;
|
|
97
|
+
}
|
|
98
|
+
if (isEmpty(attributes)) {
|
|
99
|
+
// if message does have attributes but the event does not then it always fails the check
|
|
100
|
+
return false;
|
|
101
|
+
}
|
|
102
|
+
const memoKey = `${CampaignId}:${JSON.stringify(attributes)}`;
|
|
103
|
+
if (!eventAttributesMemo.hasOwnProperty(memoKey)) {
|
|
104
|
+
eventAttributesMemo[memoKey] = Object.entries(Attributes).every(
|
|
105
|
+
([key, { Values }]) => Values.includes(attributes[key])
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
return eventAttributesMemo[memoKey];
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
export const matchesMetrics = (
|
|
112
|
+
{ CampaignId, Schedule }: PinpointInAppMessage,
|
|
113
|
+
{ metrics }: InAppMessagingEvent
|
|
114
|
+
): boolean => {
|
|
115
|
+
const { Metrics } = Schedule?.EventFilter?.Dimensions;
|
|
116
|
+
if (isEmpty(Metrics)) {
|
|
117
|
+
// if message does not have metrics defined it does not matter what metrics are on the event
|
|
118
|
+
return true;
|
|
119
|
+
}
|
|
120
|
+
if (isEmpty(metrics)) {
|
|
121
|
+
// if message does have metrics but the event does not then it always fails the check
|
|
122
|
+
return false;
|
|
123
|
+
}
|
|
124
|
+
const memoKey = `${CampaignId}:${JSON.stringify(metrics)}`;
|
|
125
|
+
if (!eventMetricsMemo.hasOwnProperty(memoKey)) {
|
|
126
|
+
eventMetricsMemo[memoKey] = Object.entries(Metrics).every(
|
|
127
|
+
([key, { ComparisonOperator, Value }]) => {
|
|
128
|
+
const compare = getComparator(ComparisonOperator);
|
|
129
|
+
// if there is some unknown comparison operator, treat as a comparison failure
|
|
130
|
+
return compare ? compare(Value, metrics[key]) : false;
|
|
131
|
+
}
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
return eventMetricsMemo[memoKey];
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
export const getComparator = (operator: string): MetricsComparator => {
|
|
138
|
+
switch (operator) {
|
|
139
|
+
case 'EQUAL':
|
|
140
|
+
return (metricsVal, eventVal) => metricsVal === eventVal;
|
|
141
|
+
case 'GREATER_THAN':
|
|
142
|
+
return (metricsVal, eventVal) => metricsVal < eventVal;
|
|
143
|
+
case 'GREATER_THAN_OR_EQUAL':
|
|
144
|
+
return (metricsVal, eventVal) => metricsVal <= eventVal;
|
|
145
|
+
case 'LESS_THAN':
|
|
146
|
+
return (metricsVal, eventVal) => metricsVal > eventVal;
|
|
147
|
+
case 'LESS_THAN_OR_EQUAL':
|
|
148
|
+
return (metricsVal, eventVal) => metricsVal >= eventVal;
|
|
149
|
+
default:
|
|
150
|
+
return null;
|
|
151
|
+
}
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
export const isBeforeEndDate = ({
|
|
155
|
+
Schedule,
|
|
156
|
+
}: PinpointInAppMessage): boolean => {
|
|
157
|
+
if (!Schedule?.EndDate) {
|
|
158
|
+
return true;
|
|
159
|
+
}
|
|
160
|
+
return new Date() < new Date(Schedule.EndDate);
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
export const isQuietTime = (message: PinpointInAppMessage): boolean => {
|
|
164
|
+
const { Schedule } = message;
|
|
165
|
+
if (!Schedule?.QuietTime) {
|
|
166
|
+
return false;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
const pattern = /^[0-2]\d:[0-5]\d$/; // basic sanity check, not a fully featured HH:MM validation
|
|
170
|
+
const { Start, End } = Schedule.QuietTime;
|
|
171
|
+
if (
|
|
172
|
+
!Start ||
|
|
173
|
+
!End ||
|
|
174
|
+
Start === End ||
|
|
175
|
+
!pattern.test(Start) ||
|
|
176
|
+
!pattern.test(End)
|
|
177
|
+
) {
|
|
178
|
+
return false;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
const now = new Date();
|
|
182
|
+
const start = new Date(now);
|
|
183
|
+
const end = new Date(now);
|
|
184
|
+
const [startHours, startMinutes] = Start.split(':');
|
|
185
|
+
const [endHours, endMinutes] = End.split(':');
|
|
186
|
+
|
|
187
|
+
start.setHours(
|
|
188
|
+
Number.parseInt(startHours, 10),
|
|
189
|
+
Number.parseInt(startMinutes, 10),
|
|
190
|
+
0,
|
|
191
|
+
0
|
|
192
|
+
);
|
|
193
|
+
end.setHours(
|
|
194
|
+
Number.parseInt(endHours, 10),
|
|
195
|
+
Number.parseInt(endMinutes, 10),
|
|
196
|
+
0,
|
|
197
|
+
0
|
|
198
|
+
);
|
|
199
|
+
|
|
200
|
+
// if quiet time includes midnight, bump the end time to the next day
|
|
201
|
+
if (start > end) {
|
|
202
|
+
end.setDate(end.getDate() + 1);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
const isQuietTime = now >= start && now <= end;
|
|
206
|
+
if (isQuietTime) {
|
|
207
|
+
logger.debug('message filtered due to quiet time', message);
|
|
208
|
+
}
|
|
209
|
+
return isQuietTime;
|
|
210
|
+
};
|
|
211
|
+
|
|
212
|
+
export const clearMemo = () => {
|
|
213
|
+
eventNameMemo = {};
|
|
214
|
+
eventAttributesMemo = {};
|
|
215
|
+
eventMetricsMemo = {};
|
|
216
|
+
};
|
|
217
|
+
|
|
218
|
+
// in the pinpoint console when a message is created with a Modal or Full Screen layout,
|
|
219
|
+
// it is assigned a layout value of MOBILE_FEED or OVERLAYS respectively in the message payload.
|
|
220
|
+
// In the future, Pinpoint will be updating the layout values in the aforementioned scenario
|
|
221
|
+
// to MODAL and FULL_SCREEN.
|
|
222
|
+
//
|
|
223
|
+
// This utility acts as a safeguard to ensure that:
|
|
224
|
+
// - 1. the usage of MOBILE_FEED and OVERLAYS as values for message layouts are not leaked
|
|
225
|
+
// outside the Pinpoint provider
|
|
226
|
+
// - 2. Amplify correctly handles the legacy layout values from Pinpoint after they are updated
|
|
227
|
+
export const interpretLayout = (
|
|
228
|
+
layout: PinpointInAppMessage['InAppMessage']['Layout']
|
|
229
|
+
): InAppMessageLayout => {
|
|
230
|
+
if (layout === PinpointInAppMessageLayout.MOBILE_FEED) {
|
|
231
|
+
return 'MODAL';
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
if (layout === PinpointInAppMessageLayout.OVERLAYS) {
|
|
235
|
+
return 'FULL_SCREEN';
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// cast as PinpointInAppMessage['InAppMessage']['Layout'] allows `string` as a value
|
|
239
|
+
return layout as InAppMessageLayout;
|
|
240
|
+
};
|
|
241
|
+
|
|
242
|
+
export const extractContent = ({
|
|
243
|
+
InAppMessage: message,
|
|
244
|
+
}: PinpointInAppMessage): InAppMessageContent[] => {
|
|
245
|
+
return (
|
|
246
|
+
message?.Content?.map(content => {
|
|
247
|
+
const {
|
|
248
|
+
BackgroundColor,
|
|
249
|
+
BodyConfig,
|
|
250
|
+
HeaderConfig,
|
|
251
|
+
ImageUrl,
|
|
252
|
+
PrimaryBtn,
|
|
253
|
+
SecondaryBtn,
|
|
254
|
+
} = content;
|
|
255
|
+
const defaultPrimaryButton = PrimaryBtn?.DefaultConfig;
|
|
256
|
+
const defaultSecondaryButton = SecondaryBtn?.DefaultConfig;
|
|
257
|
+
const extractedContent: InAppMessageContent = {};
|
|
258
|
+
if (BackgroundColor) {
|
|
259
|
+
extractedContent.container = {
|
|
260
|
+
style: {
|
|
261
|
+
backgroundColor: BackgroundColor,
|
|
262
|
+
},
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
if (HeaderConfig) {
|
|
266
|
+
extractedContent.header = {
|
|
267
|
+
content: HeaderConfig.Header,
|
|
268
|
+
style: {
|
|
269
|
+
color: HeaderConfig.TextColor,
|
|
270
|
+
textAlign:
|
|
271
|
+
HeaderConfig.Alignment.toLowerCase() as InAppMessageTextAlign,
|
|
272
|
+
},
|
|
273
|
+
};
|
|
274
|
+
}
|
|
275
|
+
if (BodyConfig) {
|
|
276
|
+
extractedContent.body = {
|
|
277
|
+
content: BodyConfig.Body,
|
|
278
|
+
style: {
|
|
279
|
+
color: BodyConfig.TextColor,
|
|
280
|
+
textAlign:
|
|
281
|
+
BodyConfig.Alignment.toLowerCase() as InAppMessageTextAlign,
|
|
282
|
+
},
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
if (ImageUrl) {
|
|
286
|
+
extractedContent.image = {
|
|
287
|
+
src: ImageUrl,
|
|
288
|
+
};
|
|
289
|
+
}
|
|
290
|
+
if (defaultPrimaryButton) {
|
|
291
|
+
extractedContent.primaryButton = {
|
|
292
|
+
title: defaultPrimaryButton.Text,
|
|
293
|
+
action: defaultPrimaryButton.ButtonAction as InAppMessageAction,
|
|
294
|
+
url: defaultPrimaryButton.Link,
|
|
295
|
+
style: {
|
|
296
|
+
backgroundColor: defaultPrimaryButton.BackgroundColor,
|
|
297
|
+
borderRadius: defaultPrimaryButton.BorderRadius,
|
|
298
|
+
color: defaultPrimaryButton.TextColor,
|
|
299
|
+
},
|
|
300
|
+
};
|
|
301
|
+
}
|
|
302
|
+
if (defaultSecondaryButton) {
|
|
303
|
+
extractedContent.secondaryButton = {
|
|
304
|
+
title: defaultSecondaryButton.Text,
|
|
305
|
+
action: defaultSecondaryButton.ButtonAction as InAppMessageAction,
|
|
306
|
+
url: defaultSecondaryButton.Link,
|
|
307
|
+
style: {
|
|
308
|
+
backgroundColor: defaultSecondaryButton.BackgroundColor,
|
|
309
|
+
borderRadius: defaultSecondaryButton.BorderRadius,
|
|
310
|
+
color: defaultSecondaryButton.TextColor,
|
|
311
|
+
},
|
|
312
|
+
};
|
|
313
|
+
}
|
|
314
|
+
return extractedContent;
|
|
315
|
+
}) ?? []
|
|
316
|
+
);
|
|
317
|
+
};
|
|
318
|
+
|
|
319
|
+
export const extractMetadata = ({
|
|
320
|
+
InAppMessage,
|
|
321
|
+
Priority,
|
|
322
|
+
Schedule,
|
|
323
|
+
TreatmentId,
|
|
324
|
+
}: PinpointInAppMessage): InAppMessage['metadata'] => ({
|
|
325
|
+
customData: InAppMessage?.CustomConfig,
|
|
326
|
+
endDate: Schedule?.EndDate,
|
|
327
|
+
priority: Priority,
|
|
328
|
+
treatmentId: TreatmentId,
|
|
329
|
+
});
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
|
|
4
|
+
import { ConsoleLogger as Logger } from '@aws-amplify/core';
|
|
5
|
+
import { AppState, AppStateStatus } from 'react-native';
|
|
6
|
+
import noop from 'lodash/noop';
|
|
7
|
+
import {
|
|
8
|
+
SessionState,
|
|
9
|
+
SessionStateChangeHandler,
|
|
10
|
+
SessionTrackerInterface,
|
|
11
|
+
} from './types';
|
|
12
|
+
|
|
13
|
+
const isActive = appState => appState === 'active';
|
|
14
|
+
const isInactive = appState =>
|
|
15
|
+
appState === 'inactive' || appState === 'background';
|
|
16
|
+
|
|
17
|
+
const logger = new Logger('InAppMessagingSessionTracker');
|
|
18
|
+
|
|
19
|
+
export default class SessionTracker implements SessionTrackerInterface {
|
|
20
|
+
private currentAppState: AppStateStatus;
|
|
21
|
+
private sessionStateChangeHandler: SessionStateChangeHandler;
|
|
22
|
+
|
|
23
|
+
constructor(sessionStateChangeHandler: SessionStateChangeHandler = noop) {
|
|
24
|
+
this.currentAppState = 'active';
|
|
25
|
+
this.sessionStateChangeHandler = sessionStateChangeHandler;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
start = (): SessionState => {
|
|
29
|
+
AppState.addEventListener('change', this.appStateChangeHandler);
|
|
30
|
+
return this.getSessionState();
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
end = (): SessionState => {
|
|
34
|
+
AppState.removeEventListener('change', this.appStateChangeHandler);
|
|
35
|
+
return this.getSessionState();
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
private getSessionState = (): SessionState => {
|
|
39
|
+
if (isActive(this.currentAppState)) {
|
|
40
|
+
return 'started';
|
|
41
|
+
}
|
|
42
|
+
// consider any other app state as session ended
|
|
43
|
+
return 'ended';
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
private appStateChangeHandler = nextAppState => {
|
|
47
|
+
// if going from active to inactive
|
|
48
|
+
if (isActive(this.currentAppState) && isInactive(nextAppState)) {
|
|
49
|
+
logger.debug('App has gone to the background');
|
|
50
|
+
this.sessionStateChangeHandler('ended');
|
|
51
|
+
}
|
|
52
|
+
// if going from inactive to active
|
|
53
|
+
if (isInactive(this.currentAppState) && isActive(nextAppState)) {
|
|
54
|
+
logger.debug('App has come to the foreground');
|
|
55
|
+
this.sessionStateChangeHandler('started');
|
|
56
|
+
}
|
|
57
|
+
// otherwise do nothing but always update current state
|
|
58
|
+
this.currentAppState = nextAppState;
|
|
59
|
+
};
|
|
60
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
import { browserOrNode, ConsoleLogger as Logger } from '@aws-amplify/core';
|
|
4
|
+
import noop from 'lodash/noop';
|
|
5
|
+
import {
|
|
6
|
+
SessionState,
|
|
7
|
+
SessionStateChangeHandler,
|
|
8
|
+
SessionTrackerInterface,
|
|
9
|
+
} from './types';
|
|
10
|
+
|
|
11
|
+
// Per https://developer.mozilla.org/en-US/docs/Web/API/Page_Visibility_API
|
|
12
|
+
let hidden: string;
|
|
13
|
+
let visibilityChange: string;
|
|
14
|
+
const { isBrowser } = browserOrNode();
|
|
15
|
+
|
|
16
|
+
if (isBrowser && document) {
|
|
17
|
+
if (typeof document.hidden !== 'undefined') {
|
|
18
|
+
hidden = 'hidden';
|
|
19
|
+
visibilityChange = 'visibilitychange';
|
|
20
|
+
} else if (typeof document['msHidden'] !== 'undefined') {
|
|
21
|
+
hidden = 'msHidden';
|
|
22
|
+
visibilityChange = 'msvisibilitychange';
|
|
23
|
+
} else if (typeof document['webkitHidden'] !== 'undefined') {
|
|
24
|
+
hidden = 'webkitHidden';
|
|
25
|
+
visibilityChange = 'webkitvisibilitychange';
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const logger = new Logger('InAppMessagingSessionTracker');
|
|
30
|
+
|
|
31
|
+
export default class SessionTracker implements SessionTrackerInterface {
|
|
32
|
+
private sessionStateChangeHandler: SessionStateChangeHandler;
|
|
33
|
+
|
|
34
|
+
constructor(sessionStateChangeHandler: SessionStateChangeHandler = noop) {
|
|
35
|
+
this.sessionStateChangeHandler = sessionStateChangeHandler;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
start = (): SessionState => {
|
|
39
|
+
if (isBrowser) {
|
|
40
|
+
document?.addEventListener(
|
|
41
|
+
visibilityChange,
|
|
42
|
+
this.visibilityChangeHandler
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
return this.getSessionState();
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
end = (): SessionState => {
|
|
49
|
+
if (isBrowser) {
|
|
50
|
+
document?.removeEventListener(
|
|
51
|
+
visibilityChange,
|
|
52
|
+
this.visibilityChangeHandler
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
return this.getSessionState();
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
private getSessionState = (): SessionState => {
|
|
59
|
+
if (isBrowser && document && !document[hidden]) {
|
|
60
|
+
return 'started';
|
|
61
|
+
}
|
|
62
|
+
// If, for any reason, document is undefined the session will never start
|
|
63
|
+
return 'ended';
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
private visibilityChangeHandler = () => {
|
|
67
|
+
if (!isBrowser || !document) {
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
if (document[hidden]) {
|
|
71
|
+
logger.debug('App is now hidden');
|
|
72
|
+
this.sessionStateChangeHandler('ended');
|
|
73
|
+
} else {
|
|
74
|
+
logger.debug('App is now visible');
|
|
75
|
+
this.sessionStateChangeHandler('started');
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
|
|
4
|
+
export type SessionState = 'started' | 'ended';
|
|
5
|
+
|
|
6
|
+
export type SessionStateChangeHandler = (state: SessionState) => void;
|
|
7
|
+
|
|
8
|
+
export interface SessionTrackerInterface {
|
|
9
|
+
start: () => SessionState;
|
|
10
|
+
end: () => SessionState;
|
|
11
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
InAppMessage,
|
|
6
|
+
InAppMessageInteractionEvent,
|
|
7
|
+
OnMessageInteractionEventHandler,
|
|
8
|
+
OnMessageInteractionEventListener,
|
|
9
|
+
} from './types';
|
|
10
|
+
|
|
11
|
+
const onMessageActionListeners: Record<
|
|
12
|
+
InAppMessageInteractionEvent,
|
|
13
|
+
Set<OnMessageInteractionEventListener>
|
|
14
|
+
> = {
|
|
15
|
+
[InAppMessageInteractionEvent.MESSAGE_RECEIVED]: new Set(),
|
|
16
|
+
[InAppMessageInteractionEvent.MESSAGE_DISPLAYED]: new Set(),
|
|
17
|
+
[InAppMessageInteractionEvent.MESSAGE_DISMISSED]: new Set(),
|
|
18
|
+
[InAppMessageInteractionEvent.MESSAGE_ACTION_TAKEN]: new Set(),
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export const notifyMessageInteractionEventListeners = (
|
|
22
|
+
message: InAppMessage,
|
|
23
|
+
event: InAppMessageInteractionEvent
|
|
24
|
+
): void => {
|
|
25
|
+
onMessageActionListeners[event].forEach(listener => {
|
|
26
|
+
listener.handleEvent(message);
|
|
27
|
+
});
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export const addMessageInteractionEventListener = (
|
|
31
|
+
handler: OnMessageInteractionEventHandler,
|
|
32
|
+
event: InAppMessageInteractionEvent
|
|
33
|
+
): OnMessageInteractionEventListener => {
|
|
34
|
+
const listener = {
|
|
35
|
+
handleEvent: handler,
|
|
36
|
+
remove: () => {
|
|
37
|
+
onMessageActionListeners[event].delete(listener);
|
|
38
|
+
},
|
|
39
|
+
};
|
|
40
|
+
onMessageActionListeners[event].add(listener);
|
|
41
|
+
return listener;
|
|
42
|
+
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
|
|
4
|
+
export { default } from './InAppMessaging';
|
|
5
|
+
export { AWSPinpointProvider } from './Providers';
|
|
6
|
+
export { AWSPinpointUserInfo } from './Providers/AWSPinpointProvider/types';
|
|
7
|
+
export {
|
|
8
|
+
InAppMessage,
|
|
9
|
+
InAppMessageAction,
|
|
10
|
+
InAppMessageButton,
|
|
11
|
+
InAppMessageContent,
|
|
12
|
+
InAppMessageImage,
|
|
13
|
+
InAppMessageInteractionEvent,
|
|
14
|
+
InAppMessageLayout,
|
|
15
|
+
InAppMessageStyle,
|
|
16
|
+
InAppMessageTextAlign,
|
|
17
|
+
InAppMessagingConfig,
|
|
18
|
+
InAppMessagingEvent,
|
|
19
|
+
UserInfo,
|
|
20
|
+
} from './types';
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
import { NotificationsCategory } from '../types';
|
|
4
|
+
import { AWSPinpointProviderConfig } from './Providers/AWSPinpointProvider/types';
|
|
5
|
+
|
|
6
|
+
export type NotificationsSubcategory = 'InAppMessaging';
|
|
7
|
+
|
|
8
|
+
export type UserInfo = {
|
|
9
|
+
attributes?: Record<string, string[]>;
|
|
10
|
+
demographic?: {
|
|
11
|
+
appVersion?: string;
|
|
12
|
+
locale?: string;
|
|
13
|
+
make?: string;
|
|
14
|
+
model?: string;
|
|
15
|
+
modelVersion?: string;
|
|
16
|
+
platform?: string;
|
|
17
|
+
platformVersion?: string;
|
|
18
|
+
timezone?: string;
|
|
19
|
+
};
|
|
20
|
+
location?: {
|
|
21
|
+
city?: string;
|
|
22
|
+
country?: string;
|
|
23
|
+
latitude?: number;
|
|
24
|
+
longitude?: number;
|
|
25
|
+
postalCode?: string;
|
|
26
|
+
region?: string;
|
|
27
|
+
};
|
|
28
|
+
metrics?: Record<string, number>;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export type InAppMessagingEvent = {
|
|
32
|
+
name: string;
|
|
33
|
+
attributes?: Record<string, string>;
|
|
34
|
+
metrics?: Record<string, number>;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export interface InAppMessagingConfig {
|
|
38
|
+
listenForAnalyticsEvents?: boolean;
|
|
39
|
+
AWSPinpoint?: AWSPinpointProviderConfig;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export interface InAppMessagingProvider {
|
|
43
|
+
// you need to implement these methods
|
|
44
|
+
|
|
45
|
+
// configure your provider
|
|
46
|
+
configure(config: object): object;
|
|
47
|
+
|
|
48
|
+
// return category ('Notifications')
|
|
49
|
+
getCategory(): NotificationsCategory;
|
|
50
|
+
|
|
51
|
+
// return sub-category ('InAppMessaging')
|
|
52
|
+
getSubCategory(): NotificationsSubcategory;
|
|
53
|
+
|
|
54
|
+
// return the name of you provider
|
|
55
|
+
getProviderName(): string;
|
|
56
|
+
|
|
57
|
+
// get in-app messages from provider
|
|
58
|
+
getInAppMessages(): Promise<any>;
|
|
59
|
+
|
|
60
|
+
// filters in-app messages based on event input and provider logic
|
|
61
|
+
processInAppMessages(
|
|
62
|
+
messages: InAppMessage[],
|
|
63
|
+
event: InAppMessagingEvent
|
|
64
|
+
): Promise<InAppMessage[]>;
|
|
65
|
+
|
|
66
|
+
// identify the current user with the provider
|
|
67
|
+
identifyUser(userId: string, userInfo: UserInfo): Promise<void>;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export type InAppMessageLayout =
|
|
71
|
+
| 'BOTTOM_BANNER'
|
|
72
|
+
| 'CAROUSEL'
|
|
73
|
+
| 'FULL_SCREEN'
|
|
74
|
+
| 'MIDDLE_BANNER'
|
|
75
|
+
| 'MODAL'
|
|
76
|
+
| 'TOP_BANNER';
|
|
77
|
+
|
|
78
|
+
export type InAppMessageAction = 'CLOSE' | 'DEEP_LINK' | 'LINK';
|
|
79
|
+
|
|
80
|
+
export type InAppMessageTextAlign = 'center' | 'left' | 'right';
|
|
81
|
+
|
|
82
|
+
interface InAppMessageContainer {
|
|
83
|
+
style?: InAppMessageStyle;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
interface InAppMessageHeader {
|
|
87
|
+
content: string;
|
|
88
|
+
style?: InAppMessageStyle;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
interface InAppMessageBody {
|
|
92
|
+
content: string;
|
|
93
|
+
style?: InAppMessageStyle;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export interface InAppMessageImage {
|
|
97
|
+
src: string;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export interface InAppMessageButton {
|
|
101
|
+
title: string;
|
|
102
|
+
action: InAppMessageAction;
|
|
103
|
+
url?: string;
|
|
104
|
+
style?: InAppMessageStyle;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export interface InAppMessageStyle {
|
|
108
|
+
backgroundColor?: string;
|
|
109
|
+
borderRadius?: number;
|
|
110
|
+
color?: string;
|
|
111
|
+
textAlign?: InAppMessageTextAlign;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export interface InAppMessageContent {
|
|
115
|
+
container?: InAppMessageContainer;
|
|
116
|
+
header?: InAppMessageHeader;
|
|
117
|
+
body?: InAppMessageBody;
|
|
118
|
+
image?: InAppMessageImage;
|
|
119
|
+
primaryButton?: InAppMessageButton;
|
|
120
|
+
secondaryButton?: InAppMessageButton;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export interface InAppMessage {
|
|
124
|
+
id: string;
|
|
125
|
+
layout: InAppMessageLayout;
|
|
126
|
+
content: InAppMessageContent[];
|
|
127
|
+
metadata?: any;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
export type OnMessageInteractionEventHandler = (message: InAppMessage) => any;
|
|
131
|
+
|
|
132
|
+
export interface OnMessageInteractionEventListener {
|
|
133
|
+
handleEvent: OnMessageInteractionEventHandler;
|
|
134
|
+
remove: () => void;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
export enum InAppMessageInteractionEvent {
|
|
138
|
+
MESSAGE_RECEIVED = 'MESSAGE_RECEIVED_EVENT',
|
|
139
|
+
MESSAGE_DISPLAYED = 'MESSAGE_DISPLAYED_EVENT',
|
|
140
|
+
MESSAGE_DISMISSED = 'MESSAGE_DISMISSED_EVENT',
|
|
141
|
+
MESSAGE_ACTION_TAKEN = 'MESSAGE_ACTION_TAKEN_EVENT',
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
export type InAppMessageConflictHandler = (
|
|
145
|
+
messages: InAppMessage[]
|
|
146
|
+
) => InAppMessage;
|