web-push-notifications 3.40.1 → 3.44.1
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/.editorconfig +11 -0
- package/.gitlab-ci.yml +190 -0
- package/babel.config.js +7 -0
- package/ci/cdn/Dockerfile +12 -0
- package/ci/dev/Dockerfile +30 -0
- package/ci/dev/rootfs/entrypoint.sh +18 -0
- package/ci/dev/rootfs/entrypoint.sh.d/nginx.sh +6 -0
- package/ci/dev/rootfs/entrypoint.sh.d/supervisor.sh +5 -0
- package/ci/dev/rootfs/etc/nginx/_real_ip.conf +2 -0
- package/ci/dev/rootfs/etc/nginx/conf.d/default.conf +20 -0
- package/ci/dev/rootfs/etc/supervisor.d/nginx.ini +11 -0
- package/ci/github/Dockerfile +59 -0
- package/ci/github/release-zip.js +61 -0
- package/ci/npm/Dockerfile +19 -0
- package/config/config.js +24 -0
- package/config/configBuilder.js +126 -0
- package/config/helpers.js +9 -0
- package/config/index.js +1 -0
- package/develop/README.md +42 -0
- package/develop/favicon.png +0 -0
- package/develop/index.html +511 -0
- package/eslint.config.mjs +114 -0
- package/package.json +4 -34
- package/{lib → public}/index.d.ts +10 -10
- package/scripts/zip.js +26 -0
- package/src/core/Pushwoosh.ts +768 -0
- package/src/core/Pushwoosh.types.ts +254 -0
- package/src/core/Safari.types.ts +26 -0
- package/src/core/constants.ts +58 -0
- package/src/core/events.types.ts +46 -0
- package/src/core/functions.ts +33 -0
- package/src/core/legacyEventsMap.ts +64 -0
- package/src/core/logger.ts +64 -0
- package/src/core/modules/EventBus/EventBus.ts +66 -0
- package/src/core/modules/EventBus/index.ts +1 -0
- package/src/core/storage.ts +254 -0
- package/src/helpers/logger.ts +81 -0
- package/src/helpers/pwlogger/Logger.constants.ts +31 -0
- package/src/helpers/pwlogger/Logger.ts +218 -0
- package/src/helpers/pwlogger/Logger.types.ts +66 -0
- package/src/helpers/pwlogger/handlers/handler-console/handler-console.ts +40 -0
- package/src/helpers/pwlogger/index.ts +2 -0
- package/src/helpers/unescape.ts +36 -0
- package/src/models/InboxMessages.ts +202 -0
- package/src/models/InboxMessages.types.ts +111 -0
- package/src/models/NotificationPayload.ts +216 -0
- package/src/models/NotificationPayload.types.ts +65 -0
- package/src/modules/Api/Api.ts +386 -0
- package/src/modules/Api/Api.types.ts +7 -0
- package/src/modules/ApiClient/ApiClient.ts +153 -0
- package/src/modules/ApiClient/ApiClient.types.ts +222 -0
- package/src/modules/Data/Data.ts +345 -0
- package/src/modules/DateModule.ts +53 -0
- package/src/modules/InboxMessagesPublic.ts +222 -0
- package/src/modules/PlatformChecker/PlatformChecker.ts +170 -0
- package/src/modules/PlatformChecker/PlatformChecker.types.ts +5 -0
- package/src/modules/PlatformChecker/index.ts +1 -0
- package/src/modules/storage/Storage.ts +164 -0
- package/src/modules/storage/Storage.types.ts +54 -0
- package/src/modules/storage/Store.ts +104 -0
- package/src/modules/storage/migrations/26-11-2018.ts +25 -0
- package/src/modules/storage/migrations/MigrationExecutor.ts +31 -0
- package/src/modules/storage/migrations/Migrations.ts +41 -0
- package/src/modules/storage/migrations/constants.ts +8 -0
- package/src/modules/storage/migrations/helpers.ts +16 -0
- package/src/modules/storage/migrations/initial.ts +47 -0
- package/src/modules/storage/version.ts +2 -0
- package/src/npm.ts +1 -0
- package/src/pushwoosh-web-notifications.ts +47 -0
- package/src/pushwoosh-widget-inbox.ts +8 -0
- package/src/pushwoosh-widget-subscribe-popup.ts +9 -0
- package/src/pushwoosh-widget-subscription-button.ts +8 -0
- package/src/pushwoosh-widget-subscription-prompt.ts +6 -0
- package/src/service-worker.ts +455 -0
- package/src/services/PushService/PushService.ts +2 -0
- package/src/services/PushService/PushService.types.ts +74 -0
- package/src/services/PushService/drivers/PushServiceDefault/PushServiceDefault.ts +235 -0
- package/src/services/PushService/drivers/PushServiceDefault/PushServiceDefault.types.ts +3 -0
- package/src/services/PushService/drivers/PushServiceSafari/PushServiceSafari.ts +125 -0
- package/src/services/PushService/drivers/PushServiceSafari/PushServiceSafari.types.ts +4 -0
- package/src/widget-inbox.ts +1 -0
- package/src/widget-subscribe-popup.ts +1 -0
- package/src/widget-subscription-button.ts +1 -0
- package/src/widget-subscription-prompt.ts +33 -0
- package/src/widgets/Inbox/InboxWidget.ts +564 -0
- package/src/widgets/Inbox/constants.ts +49 -0
- package/src/widgets/Inbox/css/inboxWidgetStyle.css +274 -0
- package/src/widgets/Inbox/helpers.ts +63 -0
- package/src/widgets/Inbox/inbox.d.ts +9 -0
- package/src/widgets/Inbox/inbox_widget.types.ts +41 -0
- package/src/widgets/Inbox/index.ts +1 -0
- package/src/widgets/Inbox/widgetTemplates.ts +55 -0
- package/src/widgets/SubscribePopup/SubscribePopup.ts +241 -0
- package/src/widgets/SubscribePopup/constants.ts +66 -0
- package/src/widgets/SubscribePopup/helpers.ts +11 -0
- package/src/widgets/SubscribePopup/index.ts +1 -0
- package/src/widgets/SubscribePopup/popupTemplates.ts +24 -0
- package/src/widgets/SubscribePopup/styles/popup.css +226 -0
- package/src/widgets/SubscribePopup/types/subscribe-popup.ts +68 -0
- package/src/widgets/SubscriptionButton/assets/css/main.css +205 -0
- package/src/widgets/SubscriptionButton/bell.ts +67 -0
- package/src/widgets/SubscriptionButton/constants.ts +28 -0
- package/src/widgets/SubscriptionButton/index.ts +377 -0
- package/src/widgets/SubscriptionButton/positioning.ts +165 -0
- package/src/widgets/SubscriptionButton/subscribe_widget.types.ts +53 -0
- package/src/widgets/SubscriptionPrompt/SubscriptionPromptWidget.constants.ts +1 -0
- package/src/widgets/SubscriptionPrompt/SubscriptionPromptWidget.helpers.ts +110 -0
- package/src/widgets/SubscriptionPrompt/SubscriptionPromptWidget.ts +102 -0
- package/src/widgets/SubscriptionPrompt/SubscriptionPromptWidget.types.ts +23 -0
- package/src/widgets/SubscriptionPrompt/constants.ts +22 -0
- package/src/widgets/SubscriptionPrompt/helpers.ts +42 -0
- package/src/widgets/widgets.d.ts +4 -0
- package/src/worker/global.ts +36 -0
- package/src/worker/notification.ts +34 -0
- package/src/worker/worker.types.ts +4 -0
- package/test/__helpers__/apiHelpers.ts +22 -0
- package/test/__helpers__/keyValueHelpers.ts +15 -0
- package/test/__helpers__/platformHelpers.ts +54 -0
- package/test/__helpers__/sinonHelpers.ts +7 -0
- package/test/__helpers__/storageHelpers.ts +56 -0
- package/test/__mocks__/apiRequests.ts +26 -0
- package/test/__mocks__/idbMock.ts +12 -0
- package/test/__mocks__/idbObjectStoreMock.ts +38 -0
- package/test/__mocks__/inboxMessages.ts +292 -0
- package/test/__mocks__/models/inboxModel.ts +71 -0
- package/test/__mocks__/modules/apiClientModule.ts +18 -0
- package/test/__mocks__/modules/dateModule.ts +34 -0
- package/test/__mocks__/modules/inboxParamsModule.ts +21 -0
- package/test/__mocks__/modules/paramsBuilder.ts +12 -0
- package/test/__mocks__/modules/paramsModule.ts +35 -0
- package/test/__mocks__/modules/payloadBuilderModule.ts +15 -0
- package/test/__mocks__/modules/storageModule.ts +58 -0
- package/test/__mocks__/navigator.ts +38 -0
- package/test/__mocks__/notification.ts +84 -0
- package/test/__mocks__/pushwoosh.ts +12 -0
- package/test/__mocks__/userAgents +8 -0
- package/test/functions.test.ts +22 -0
- package/test/ignore-html.js +6 -0
- package/test/mocha.opts +6 -0
- package/test/modules/DateModule/unit.test.ts +80 -0
- package/test/modules/storage/Storage/unit.test.ts +180 -0
- package/test/modules/storage/Store/unit.test.ts +192 -0
- package/testRegister.js +24 -0
- package/tsconfig.json +31 -0
- package/webpack.config.js +163 -0
- package/lib/index.js +0 -2
- package/lib/index.js.map +0 -1
- package/lib/service-worker.js +0 -2
- package/lib/service-worker.js.map +0 -1
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
.pw-inbox-trigger {
|
|
2
|
+
position: relative;
|
|
3
|
+
cursor: pointer;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
.pw-inbox-trigger:after {
|
|
7
|
+
content: attr(data-pw-count);
|
|
8
|
+
display: block;
|
|
9
|
+
position: absolute;
|
|
10
|
+
right: 0;
|
|
11
|
+
top: 0;
|
|
12
|
+
background: var(--badgeBgColor);
|
|
13
|
+
border-radius: 8px;
|
|
14
|
+
color: var(--badgeTextColor);
|
|
15
|
+
font-size: 10px;
|
|
16
|
+
font-weight: normal;
|
|
17
|
+
line-height: 16px;
|
|
18
|
+
width: 16px;
|
|
19
|
+
padding: 0 2px;
|
|
20
|
+
box-sizing: border-box;
|
|
21
|
+
text-align: center;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
.pw-inbox-trigger.pw-empty:after {
|
|
25
|
+
display: none;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
.pw-inbox-widget * {
|
|
29
|
+
position: static;
|
|
30
|
+
box-sizing: border-box;
|
|
31
|
+
font-size: 1em;
|
|
32
|
+
outline: none;
|
|
33
|
+
font-family: var(--fontFamily);
|
|
34
|
+
}
|
|
35
|
+
.pw-inbox-widget {
|
|
36
|
+
font-size: 14px;
|
|
37
|
+
position: absolute;
|
|
38
|
+
top: 0;
|
|
39
|
+
left: -1000px;
|
|
40
|
+
background: var(--bgColor);
|
|
41
|
+
border: solid 1px var(--borderColor);
|
|
42
|
+
border-radius: var(--borderRadius);
|
|
43
|
+
width: var(--widgetWidth);
|
|
44
|
+
box-shadow: 0 1px 4px 0 rgba(0,0,0,.25);
|
|
45
|
+
z-index: var(--zIndex);
|
|
46
|
+
opacity: 0;
|
|
47
|
+
transition: opacity .6s ease;
|
|
48
|
+
transition-delay: 100ms;
|
|
49
|
+
}
|
|
50
|
+
.pw-inbox-widget.pw-open {
|
|
51
|
+
opacity: 1;
|
|
52
|
+
transition-delay: 0ms;
|
|
53
|
+
}
|
|
54
|
+
.pw-inbox-widget.pw-inbox-widget--inset {
|
|
55
|
+
top: auto;
|
|
56
|
+
left: auto;
|
|
57
|
+
display: none;
|
|
58
|
+
}
|
|
59
|
+
.pw-inbox-widget.pw-inbox-widget--inset.pw-open {
|
|
60
|
+
display: block;
|
|
61
|
+
}
|
|
62
|
+
.pw-inbox-widget.pw-inbox-widget--inset.pw-bottom {
|
|
63
|
+
top: 100%;
|
|
64
|
+
left: 50%;
|
|
65
|
+
transform: translateX(-50%);
|
|
66
|
+
}
|
|
67
|
+
.pw-inbox-widget.pw-inbox-widget--inset.pw-top {
|
|
68
|
+
bottom: 100%;
|
|
69
|
+
left: 50%;
|
|
70
|
+
transform: translateX(-50%);
|
|
71
|
+
}
|
|
72
|
+
.pw-inbox-widget.pw-inbox-widget--inset.pw-right {
|
|
73
|
+
left: 100%;
|
|
74
|
+
top: 50%;
|
|
75
|
+
transform: translateY(-50%);
|
|
76
|
+
}
|
|
77
|
+
.pw-inbox-widget.pw-inbox-widget--inset.pw-left {
|
|
78
|
+
right: 100%;
|
|
79
|
+
top: 50%;
|
|
80
|
+
transform: translateY(-50%);
|
|
81
|
+
}
|
|
82
|
+
.pw-inbox-widget.pw-inbox-widget--empty {
|
|
83
|
+
max-height: none;
|
|
84
|
+
}
|
|
85
|
+
.pw-inbox-widget .pw-inbox__arrow {
|
|
86
|
+
display: block;
|
|
87
|
+
width: 0;
|
|
88
|
+
height: 0;
|
|
89
|
+
border-width: 10px;
|
|
90
|
+
border-style: solid;
|
|
91
|
+
border-color: transparent;
|
|
92
|
+
position: absolute;
|
|
93
|
+
}
|
|
94
|
+
.pw-inbox-widget .pw-inbox__arrow:before {
|
|
95
|
+
content: "";
|
|
96
|
+
display: block;
|
|
97
|
+
width: 0;
|
|
98
|
+
height: 0;
|
|
99
|
+
border-width: 10px;
|
|
100
|
+
border-style: solid;
|
|
101
|
+
border-color: transparent;
|
|
102
|
+
position: absolute;
|
|
103
|
+
}
|
|
104
|
+
.pw-inbox-widget.pw-top {
|
|
105
|
+
margin-top: -12px;
|
|
106
|
+
}
|
|
107
|
+
.pw-inbox-widget.pw-top .pw-inbox__arrow {
|
|
108
|
+
border-top-color: var(--arrowBorderColor);
|
|
109
|
+
bottom: -21px;
|
|
110
|
+
left: 50%;
|
|
111
|
+
transform: translateX(-50%);
|
|
112
|
+
}
|
|
113
|
+
.pw-inbox-widget.pw-top .pw-inbox__arrow:before {
|
|
114
|
+
border-top-color: var(--bgColor);
|
|
115
|
+
top: -11px;
|
|
116
|
+
left: -10px;
|
|
117
|
+
}
|
|
118
|
+
.pw-inbox-widget.pw-bottom {
|
|
119
|
+
margin-top: 12px;
|
|
120
|
+
}
|
|
121
|
+
.pw-inbox-widget.pw-bottom .pw-inbox__arrow {
|
|
122
|
+
border-bottom-color: var(--arrowBorderColor);
|
|
123
|
+
top: -21px;
|
|
124
|
+
left: 50%;
|
|
125
|
+
transform: translateX(-50%);
|
|
126
|
+
}
|
|
127
|
+
.pw-inbox-widget.pw-bottom .pw-inbox__arrow:before {
|
|
128
|
+
border-bottom-color: var(--bgColor);
|
|
129
|
+
bottom: -11px;
|
|
130
|
+
left: -10px;
|
|
131
|
+
}
|
|
132
|
+
.pw-inbox-widget.pw-left {
|
|
133
|
+
margin-left: -12px;
|
|
134
|
+
}
|
|
135
|
+
.pw-inbox-widget.pw-left .pw-inbox__arrow {
|
|
136
|
+
border-left-color: var(--arrowBorderColor);
|
|
137
|
+
right: -21px;
|
|
138
|
+
top: 50%;
|
|
139
|
+
transform: translateY(-50%);
|
|
140
|
+
}
|
|
141
|
+
.pw-inbox-widget.pw-left .pw-inbox__arrow:before {
|
|
142
|
+
border-left-color: var(--bgColor);
|
|
143
|
+
left: -11px;
|
|
144
|
+
top: -10px;
|
|
145
|
+
}
|
|
146
|
+
.pw-inbox-widget.pw-right {
|
|
147
|
+
margin-left: 12px;
|
|
148
|
+
}
|
|
149
|
+
.pw-inbox-widget.pw-right .pw-inbox__arrow {
|
|
150
|
+
border-right-color: var(--arrowBorderColor);
|
|
151
|
+
left: -21px;
|
|
152
|
+
top: 50%;
|
|
153
|
+
transform: translateY(-50%);
|
|
154
|
+
}
|
|
155
|
+
.pw-inbox-widget.pw-right .pw-inbox__arrow:before {
|
|
156
|
+
border-right-color: var(--bgColor);
|
|
157
|
+
right: -11px;
|
|
158
|
+
top: -10px;
|
|
159
|
+
}
|
|
160
|
+
.pw-inbox_inner {
|
|
161
|
+
overflow: hidden;
|
|
162
|
+
display: flex;
|
|
163
|
+
flex-direction: column;
|
|
164
|
+
height: 100%;
|
|
165
|
+
}
|
|
166
|
+
.pw-inbox_title {
|
|
167
|
+
color: var(--textColor);
|
|
168
|
+
margin: 0;
|
|
169
|
+
padding: 28px 32px 12px;
|
|
170
|
+
font-size: 24px;
|
|
171
|
+
font-weight: 500;
|
|
172
|
+
line-height: 1.1;
|
|
173
|
+
text-align: left;
|
|
174
|
+
flex: 0 0 66px;
|
|
175
|
+
}
|
|
176
|
+
.pw-inbox_list {
|
|
177
|
+
overflow-x: hidden;
|
|
178
|
+
overflow-y: auto;
|
|
179
|
+
min-width: 200px;
|
|
180
|
+
max-height: 300px;
|
|
181
|
+
padding: 0;
|
|
182
|
+
margin: 0;
|
|
183
|
+
list-style: none;
|
|
184
|
+
position: relative;
|
|
185
|
+
flex: 1 1 auto;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
.pw-inbox_item {
|
|
189
|
+
position: relative;
|
|
190
|
+
padding: 8px 32px;
|
|
191
|
+
margin: 0;
|
|
192
|
+
text-align: left;
|
|
193
|
+
}
|
|
194
|
+
.pw-inbox_item-inner {
|
|
195
|
+
display: flex;
|
|
196
|
+
cursor: pointer;
|
|
197
|
+
}
|
|
198
|
+
.pw-inbox_icon {
|
|
199
|
+
flex: 0 0 40px;
|
|
200
|
+
}
|
|
201
|
+
.pw-inbox_message-image {
|
|
202
|
+
width: 28px;
|
|
203
|
+
}
|
|
204
|
+
.pw-inbox_content {
|
|
205
|
+
flex: 0 1 100%;
|
|
206
|
+
}
|
|
207
|
+
.pw-inbox_item-title {
|
|
208
|
+
color: var(--messageTitleColor);
|
|
209
|
+
font-size: 14px;
|
|
210
|
+
line-height: 20px;
|
|
211
|
+
margin-bottom: 4px;
|
|
212
|
+
}
|
|
213
|
+
.pw-inbox_item-body {
|
|
214
|
+
color: var(--textColor);
|
|
215
|
+
font-size: 14px;
|
|
216
|
+
line-height: 20px;
|
|
217
|
+
margin-bottom: 4px;
|
|
218
|
+
}
|
|
219
|
+
.pw-inbox_item-time {
|
|
220
|
+
color: var(--timeTextColor);
|
|
221
|
+
font-size: 12px;
|
|
222
|
+
line-height: 17px;
|
|
223
|
+
}
|
|
224
|
+
.pw-inbox_item-remove {
|
|
225
|
+
position: absolute;
|
|
226
|
+
z-index: 2;
|
|
227
|
+
top: 8px;
|
|
228
|
+
right: 8px;
|
|
229
|
+
display: none;
|
|
230
|
+
width: 10px;
|
|
231
|
+
height: 10px;
|
|
232
|
+
background: transparent url('https://cdn.pushwoosh.com/webpush/img/iconClose.svg') 50% no-repeat;
|
|
233
|
+
cursor: pointer;
|
|
234
|
+
}
|
|
235
|
+
.pw-inbox_item:hover .pw-inbox_item-remove {
|
|
236
|
+
display: block;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
.pw-inbox_item.pw-new .pw-inbox_item-title {
|
|
240
|
+
color: var(--textColor);
|
|
241
|
+
font-weight: 700;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
.pw-inbox_item.pw-new .pw-inbox_item-body {
|
|
245
|
+
font-weight: 700;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
.pw-inbox_list--empty {
|
|
249
|
+
padding: 50px 16px;
|
|
250
|
+
text-align: center;
|
|
251
|
+
max-height: 100%;
|
|
252
|
+
overflow: auto;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
.pw-inbox_list--empty .pw-inbox_list-icon {
|
|
256
|
+
text-align: center;
|
|
257
|
+
margin-bottom: 30px;
|
|
258
|
+
}
|
|
259
|
+
.pw-inbox_list--empty .pw-inbox_list-icon img {
|
|
260
|
+
max-width: 100%;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
.pw-inbox_list--empty .pw-inbox_list-title {
|
|
264
|
+
font-size: 28px;
|
|
265
|
+
line-height: 36px;
|
|
266
|
+
color: var(--emptyInboxTitleColor);
|
|
267
|
+
margin-bottom: 20px;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
.pw-inbox_list--empty .ipw-inbox_list-body {
|
|
271
|
+
font-size: 14px;
|
|
272
|
+
line-height: 18px;
|
|
273
|
+
color: var(--emptyInboxTextColor);
|
|
274
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import {
|
|
2
|
+
MILLISECONDS_IN_HOUR,
|
|
3
|
+
MILLISECONDS_IN_DAY,
|
|
4
|
+
MONTHS,
|
|
5
|
+
COLOR_TEST_REGEXP,
|
|
6
|
+
} from './constants';
|
|
7
|
+
|
|
8
|
+
export function isElementFixed(element: HTMLElement): boolean {
|
|
9
|
+
let isFixed = window.getComputedStyle(element).position === 'fixed';
|
|
10
|
+
if (!isFixed && element.parentElement) {
|
|
11
|
+
isFixed = isElementFixed(element.parentElement);
|
|
12
|
+
}
|
|
13
|
+
return isFixed;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function getMessageTime(date: string): string {
|
|
17
|
+
const localDate = new Date(date);
|
|
18
|
+
const localTime = localDate.getHours() + (localDate.getTimezoneOffset() / 60);
|
|
19
|
+
localDate.setHours(localTime);
|
|
20
|
+
const now = new Date();
|
|
21
|
+
|
|
22
|
+
const gap = (now.getTime() - localDate.getTime());
|
|
23
|
+
|
|
24
|
+
if (gap <= 60 * 1000) {
|
|
25
|
+
return `Just now`;
|
|
26
|
+
} else if (gap < MILLISECONDS_IN_HOUR && gap > 0) {
|
|
27
|
+
const minutesAgo = Math.floor(gap / (60 * 1000));
|
|
28
|
+
return `${minutesAgo} minutes ago`;
|
|
29
|
+
} else if (gap < MILLISECONDS_IN_DAY && gap > 0) {
|
|
30
|
+
const hoursAgo = Math.floor(gap / (60 * 60 * 1000));
|
|
31
|
+
return `${hoursAgo} hours ago`;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const day = localDate.getDate();
|
|
35
|
+
const month = MONTHS[localDate.getMonth()];
|
|
36
|
+
const year = localDate.getFullYear();
|
|
37
|
+
const hours = localDate.getHours();
|
|
38
|
+
const minutes = `0${localDate.getMinutes().toString()}`.slice(-2); // padStart(2, 0)
|
|
39
|
+
|
|
40
|
+
return `${day} ${month} ${year}, ${hours}:${minutes}`;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function getValidColor(color: string): string {
|
|
44
|
+
if (color === 'transparent') {
|
|
45
|
+
return color;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return COLOR_TEST_REGEXP.test(color)
|
|
49
|
+
? color
|
|
50
|
+
: '#333';
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export function compareBySendDate(dateOne: string, dateTwo: string): number {
|
|
54
|
+
const localDateOne = new Date(dateOne);
|
|
55
|
+
const localTimeOne = localDateOne.getHours() + (localDateOne.getTimezoneOffset() / 60);
|
|
56
|
+
const localDateTwo = new Date(dateTwo);
|
|
57
|
+
const localTimeTwo = localDateTwo.getHours() + (localDateTwo.getTimezoneOffset() / 60);
|
|
58
|
+
|
|
59
|
+
localDateOne.setHours(localTimeOne);
|
|
60
|
+
localDateTwo.setHours(localTimeTwo);
|
|
61
|
+
|
|
62
|
+
return localDateTwo.getTime() - localDateOne.getTime();
|
|
63
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { type IInboxMessagePublic } from '../../models/InboxMessages.types';
|
|
2
|
+
|
|
3
|
+
interface Ipwinbox {
|
|
4
|
+
readMessagesWithCodes(codes: Array<string>): Promise<void>;
|
|
5
|
+
loadMessages(): Promise<Array<IInboxMessagePublic>>;
|
|
6
|
+
unreadMessagesCount(): Promise<number>;
|
|
7
|
+
performActionForMessageWithCode(code: string): Promise<void>;
|
|
8
|
+
deleteMessagesWithCodes(codes: Array<string>): Promise<void>;
|
|
9
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
export interface TMessagesElementsType {
|
|
2
|
+
[code: string]: HTMLElement;
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
export interface IConfigStyles {
|
|
6
|
+
name: TStylesNames;
|
|
7
|
+
type: TStylesTypes;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export type TStylesNames = 'bgColor' | 'textColor' | 'fontFamily' | 'borderRadius' | 'borderColor'
|
|
11
|
+
| 'badgeBgColor' | 'badgeTextColor' | 'widgetWidth' | 'zIndex' | 'messageTitleColor' | 'timeTextColor'
|
|
12
|
+
| 'emptyInboxTitleColor' | 'emptyInboxTextColor' | 'arrowBorderColor';
|
|
13
|
+
|
|
14
|
+
export type TStylesTypes = 'number' | 'color' | 'string' | 'size';
|
|
15
|
+
|
|
16
|
+
export type TWidgetPosition = 'left' | 'right' | 'top' | 'bottom';
|
|
17
|
+
|
|
18
|
+
export interface IInboxWidgetConfig {
|
|
19
|
+
enable: boolean;
|
|
20
|
+
triggerId: string;
|
|
21
|
+
position: TWidgetPosition;
|
|
22
|
+
appendTo: string;
|
|
23
|
+
title: string;
|
|
24
|
+
bgColor: string;
|
|
25
|
+
textColor: string;
|
|
26
|
+
fontFamily: string;
|
|
27
|
+
borderRadius: number;
|
|
28
|
+
borderColor: string;
|
|
29
|
+
badgeBgColor: string;
|
|
30
|
+
badgeTextColor: string;
|
|
31
|
+
widgetWidth: number;
|
|
32
|
+
zIndex: number;
|
|
33
|
+
messageTitleColor: string;
|
|
34
|
+
timeTextColor: string;
|
|
35
|
+
emptyInboxTitle: string;
|
|
36
|
+
emptyInboxText: string;
|
|
37
|
+
emptyInboxIconUrl: string;
|
|
38
|
+
emptyInboxTitleColor: string;
|
|
39
|
+
emptyInboxTextColor: string;
|
|
40
|
+
arrowBorderColor: string;
|
|
41
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { PWInboxWidget } from './InboxWidget';
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { getMessageTime } from './helpers';
|
|
2
|
+
import { type IInboxMessagePublic } from '../../models/InboxMessages.types';
|
|
3
|
+
|
|
4
|
+
export const widgetTemplate = (title: string): string => `
|
|
5
|
+
<div class="pw-inbox__arrow"></div>
|
|
6
|
+
<div class="pw-inbox_inner">
|
|
7
|
+
<div class="pw-inbox_title">
|
|
8
|
+
${title}
|
|
9
|
+
</div>
|
|
10
|
+
<ul class="pw-inbox_list">
|
|
11
|
+
|
|
12
|
+
</ul>
|
|
13
|
+
</div>`;
|
|
14
|
+
|
|
15
|
+
export const widgetTemplateEmpty = (
|
|
16
|
+
emptyInboxIconUrl: string,
|
|
17
|
+
emptyInboxTitle: string,
|
|
18
|
+
emptyInboxText: string,
|
|
19
|
+
) => `
|
|
20
|
+
<div class="pw-inbox__arrow"></div>
|
|
21
|
+
<div class="pw-inbox_list--empty">
|
|
22
|
+
<div class="pw-inbox_list-icon">
|
|
23
|
+
<img src="${emptyInboxIconUrl}" alt="${emptyInboxTitle}">
|
|
24
|
+
</div>
|
|
25
|
+
<div class="pw-inbox_list-title">
|
|
26
|
+
${emptyInboxTitle}
|
|
27
|
+
</div>
|
|
28
|
+
<div class="ipw-inbox_list-body">
|
|
29
|
+
${emptyInboxText}
|
|
30
|
+
</div>
|
|
31
|
+
</div>`;
|
|
32
|
+
|
|
33
|
+
export const messageTemplate = ({
|
|
34
|
+
imageUrl,
|
|
35
|
+
title,
|
|
36
|
+
message,
|
|
37
|
+
sendDate,
|
|
38
|
+
}: IInboxMessagePublic) => `
|
|
39
|
+
<div class="pw-inbox_item-inner">
|
|
40
|
+
<div class="pw-inbox_icon">
|
|
41
|
+
<img src="${imageUrl}" alt="${title || message}" class="pw-inbox_message-image">
|
|
42
|
+
</div>
|
|
43
|
+
<div class="pw-inbox_content">
|
|
44
|
+
${title ? `<div class="pw-inbox_item-title">
|
|
45
|
+
${title}
|
|
46
|
+
</div>` : null}
|
|
47
|
+
<div class="pw-inbox_item-body">
|
|
48
|
+
${message}
|
|
49
|
+
</div>
|
|
50
|
+
<div class="pw-inbox_item-time">
|
|
51
|
+
${getMessageTime(sendDate)}
|
|
52
|
+
</div>
|
|
53
|
+
</div>
|
|
54
|
+
</div>
|
|
55
|
+
<span class="pw-inbox_item-remove"></span>`;
|
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
import {
|
|
2
|
+
DEFAULT_CONFIG,
|
|
3
|
+
CONFIG_STYLES,
|
|
4
|
+
|
|
5
|
+
PERMISSION_DENIED,
|
|
6
|
+
PERMISSION_GRANTED,
|
|
7
|
+
} from './constants';
|
|
8
|
+
import { getValidColor } from './helpers';
|
|
9
|
+
import { innerTemplate } from './popupTemplates';
|
|
10
|
+
import popupCss from './styles/popup.css';
|
|
11
|
+
import {
|
|
12
|
+
type ISubscribePopupConfig,
|
|
13
|
+
type ISubscribePopupConfigStyles,
|
|
14
|
+
type ISubscribePopupVariables,
|
|
15
|
+
} from './types/subscribe-popup';
|
|
16
|
+
import { type Pushwoosh } from '../../core/Pushwoosh';
|
|
17
|
+
|
|
18
|
+
export class PWSubscribePopupWidget {
|
|
19
|
+
pw: Pushwoosh;
|
|
20
|
+
config: ISubscribePopupVariables;
|
|
21
|
+
popup: HTMLElement;
|
|
22
|
+
isShown: boolean;
|
|
23
|
+
|
|
24
|
+
constructor(pw: Pushwoosh) {
|
|
25
|
+
this.pw = pw;
|
|
26
|
+
|
|
27
|
+
const { mobileViewMargin = '' } = pw.initParams.subscribePopup || ({} as ISubscribePopupConfig);
|
|
28
|
+
|
|
29
|
+
this.config = {
|
|
30
|
+
...DEFAULT_CONFIG,
|
|
31
|
+
...pw.initParams.subscribePopup,
|
|
32
|
+
mobileViewTransition: mobileViewMargin ? 'none' : 'bottom .4s ease-out',
|
|
33
|
+
mobileViewPosition: mobileViewMargin ? 'auto!important' : 'auto',
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
this.onAskLaterClick = this.onAskLaterClick.bind(this);
|
|
37
|
+
this.onSubscribeClick = this.onSubscribeClick.bind(this);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
async initPopup() {
|
|
41
|
+
const { manualToggle } = this.config;
|
|
42
|
+
const [isSubscribed, isManualUnsubscribed] = await Promise.all([
|
|
43
|
+
this.pw.isSubscribed(),
|
|
44
|
+
this.pw.data.getStatusManualUnsubscribed(),
|
|
45
|
+
]);
|
|
46
|
+
if (isSubscribed || (!manualToggle && isManualUnsubscribed)) {
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const permission = this.pw.driver.getPermission();
|
|
51
|
+
if (permission === PERMISSION_GRANTED || permission === PERMISSION_DENIED) {
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
this.renderPopup();
|
|
56
|
+
this.appendStyles();
|
|
57
|
+
|
|
58
|
+
this.pw.moduleRegistry.subscribePopup = this;
|
|
59
|
+
this.pw.dispatchEvent('subscribe-popup-ready', {});
|
|
60
|
+
|
|
61
|
+
if (manualToggle) {
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const lastPopupOpen = localStorage.getItem('LAST_OPEN_SUBSCRIPTION_POPUP');
|
|
66
|
+
const lastPopupOpenTime = lastPopupOpen ? parseInt(lastPopupOpen) : 0;
|
|
67
|
+
const now = new Date().getTime();
|
|
68
|
+
|
|
69
|
+
if (lastPopupOpenTime + (this.config.retryOffset * 1000) < now) {
|
|
70
|
+
setTimeout(() => {
|
|
71
|
+
this.toggle(true);
|
|
72
|
+
}, this.config.delay * 1000);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
toggle(isShown?: boolean) {
|
|
77
|
+
const isPopupShown = typeof isShown === 'undefined' ? !this.isShown : !!isShown;
|
|
78
|
+
if (isPopupShown !== this.isShown) {
|
|
79
|
+
if (isPopupShown) {
|
|
80
|
+
this.show();
|
|
81
|
+
} else {
|
|
82
|
+
this.hide();
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
async show() {
|
|
88
|
+
if (await this.pw.isSubscribed()) {
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
this.isShown = true;
|
|
92
|
+
this.popup.classList.add('pw-show');
|
|
93
|
+
document.body.classList.add('pw-popup-opened');
|
|
94
|
+
|
|
95
|
+
const event = new CustomEvent('showPopup', {
|
|
96
|
+
bubbles: false,
|
|
97
|
+
cancelable: false,
|
|
98
|
+
detail: {
|
|
99
|
+
popup: this.popup,
|
|
100
|
+
},
|
|
101
|
+
});
|
|
102
|
+
this.popup.dispatchEvent(event);
|
|
103
|
+
|
|
104
|
+
const screenWidth = Math.max(document.documentElement.clientWidth, window.innerWidth || 0);
|
|
105
|
+
if (screenWidth < 541) {
|
|
106
|
+
// mobile screen doesn't support themes
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
const {
|
|
110
|
+
theme,
|
|
111
|
+
viewport,
|
|
112
|
+
position,
|
|
113
|
+
} = this.config;
|
|
114
|
+
if (theme === 'topbar' && position === 'top') {
|
|
115
|
+
const viewportElement: HTMLElement = document.querySelector(viewport) || document.createElement('div');
|
|
116
|
+
const currentMarginTop = window.getComputedStyle(viewportElement).marginTop || '0';
|
|
117
|
+
viewportElement.style.transition = 'margin-top .3s ease-out';
|
|
118
|
+
viewportElement.style.marginTop = `${parseInt(currentMarginTop) + this.popup.getBoundingClientRect().height}px`;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
this.pw.dispatchEvent('subscribe-popup-show', {});
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
hide() {
|
|
125
|
+
this.isShown = false;
|
|
126
|
+
this.popup.classList.remove('pw-show');
|
|
127
|
+
document.body.classList.remove('pw-popup-opened');
|
|
128
|
+
|
|
129
|
+
const event = new CustomEvent('hidePopup', {
|
|
130
|
+
bubbles: false,
|
|
131
|
+
cancelable: false,
|
|
132
|
+
detail: {
|
|
133
|
+
popup: this.popup,
|
|
134
|
+
},
|
|
135
|
+
});
|
|
136
|
+
this.popup.dispatchEvent(event);
|
|
137
|
+
|
|
138
|
+
const now = new Date().getTime().toString();
|
|
139
|
+
|
|
140
|
+
localStorage.setItem('LAST_OPEN_SUBSCRIPTION_POPUP', now);
|
|
141
|
+
|
|
142
|
+
const screenWidth = Math.max(document.documentElement.clientWidth, window.innerWidth || 0);
|
|
143
|
+
if (screenWidth < 541) {
|
|
144
|
+
// mobile screen doesn't support themes
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
const {
|
|
148
|
+
theme,
|
|
149
|
+
viewport,
|
|
150
|
+
position,
|
|
151
|
+
} = this.config;
|
|
152
|
+
if (theme === 'topbar' && position === 'top') {
|
|
153
|
+
const viewportElement: HTMLElement = document.querySelector(viewport) || document.createElement('div');
|
|
154
|
+
const currentMarginTop = window.getComputedStyle(viewportElement).marginTop || '0';
|
|
155
|
+
viewportElement.style.marginTop = `${parseInt(currentMarginTop) - this.popup.getBoundingClientRect().height}px`;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
this.pw.dispatchEvent('subscribe-popup-hide', {});
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
renderPopup() {
|
|
162
|
+
this.popup = document.createElement('div');
|
|
163
|
+
this.popup.id = 'pwSubscribePopup';
|
|
164
|
+
const {
|
|
165
|
+
text,
|
|
166
|
+
askLaterButtonText,
|
|
167
|
+
confirmSubscriptionButtonText,
|
|
168
|
+
iconUrl,
|
|
169
|
+
iconAlt,
|
|
170
|
+
position,
|
|
171
|
+
overlay,
|
|
172
|
+
theme,
|
|
173
|
+
} = this.config;
|
|
174
|
+
this.popup.className = `pw-subscribe-popup pw-position-${position} pw-subscribe-popup-${theme}`;
|
|
175
|
+
this.popup.classList.toggle('pw-subscribe-popup__overlay', overlay);
|
|
176
|
+
this.popup.innerHTML = innerTemplate({
|
|
177
|
+
text,
|
|
178
|
+
askLaterButtonText,
|
|
179
|
+
confirmSubscriptionButtonText,
|
|
180
|
+
iconUrl,
|
|
181
|
+
iconAlt,
|
|
182
|
+
});
|
|
183
|
+
document.body.appendChild(this.popup);
|
|
184
|
+
this.addListeners();
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
appendStyles() {
|
|
188
|
+
const style = document.createElement('style');
|
|
189
|
+
style.innerHTML = this.configureStyle(popupCss);
|
|
190
|
+
document.body.appendChild(style);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
private getStyleFormatter(style: ISubscribePopupConfigStyles): string {
|
|
194
|
+
switch (style.type) {
|
|
195
|
+
case 'size':
|
|
196
|
+
return `${this.config[style.name] || 0}px`;
|
|
197
|
+
case 'number':
|
|
198
|
+
return parseFloat(this.config[style.name].toString()).toString();
|
|
199
|
+
case 'string':
|
|
200
|
+
return this.config[style.name].toString();
|
|
201
|
+
case 'color':
|
|
202
|
+
return getValidColor(this.config[style.name].toString());
|
|
203
|
+
default:
|
|
204
|
+
return 'none';
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
configureStyle(styles: string): string {
|
|
209
|
+
let resultStyles = styles.toString();
|
|
210
|
+
|
|
211
|
+
CONFIG_STYLES.forEach((style: ISubscribePopupConfigStyles) => {
|
|
212
|
+
const template = new RegExp(`var\\(--${style.name}\\)`, 'ig');
|
|
213
|
+
const result = this.getStyleFormatter(style);
|
|
214
|
+
|
|
215
|
+
resultStyles = resultStyles.replace(template, result);
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
return resultStyles;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
addListeners() {
|
|
222
|
+
const askLaterButton = this.popup.querySelector('button[name="pwAskLater"]')
|
|
223
|
+
|| document.createElement('button');
|
|
224
|
+
const subscribeButton = this.popup.querySelector('button[name="pwSubscribe"]')
|
|
225
|
+
|| document.createElement('button');
|
|
226
|
+
|
|
227
|
+
askLaterButton.addEventListener('click', this.onAskLaterClick);
|
|
228
|
+
subscribeButton.addEventListener('click', this.onSubscribeClick);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
onAskLaterClick() {
|
|
232
|
+
this.toggle(false);
|
|
233
|
+
this.pw.dispatchEvent('subscribe-popup-decline', {});
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
onSubscribeClick() {
|
|
237
|
+
this.toggle(false);
|
|
238
|
+
this.pw.dispatchEvent('subscribe-popup-accept', {});
|
|
239
|
+
this.pw.subscribe();
|
|
240
|
+
}
|
|
241
|
+
}
|