@blotoutio/providers-blotout-wallet-sdk 0.43.0 → 0.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/index.cjs.js +138 -5
- package/index.js +138 -5
- package/index.mjs +138 -5
- package/package.json +1 -1
package/index.cjs.js
CHANGED
@@ -1,5 +1,72 @@
|
|
1
1
|
'use strict';
|
2
2
|
|
3
|
+
/**
|
4
|
+
* Returns whether A contains the entirety of B (A superset of B)
|
5
|
+
*
|
6
|
+
* A ⊇ B
|
7
|
+
*/
|
8
|
+
const isSuperset = (a, b) => {
|
9
|
+
for (const item of b) {
|
10
|
+
if (!a.has(item)) {
|
11
|
+
return false;
|
12
|
+
}
|
13
|
+
}
|
14
|
+
return true;
|
15
|
+
};
|
16
|
+
/**
|
17
|
+
* Returns whether A is entirely contained within B (A subset of B)
|
18
|
+
*
|
19
|
+
* A ⊆ B
|
20
|
+
*/
|
21
|
+
const isSubset = (a, b) => {
|
22
|
+
for (const item of a) {
|
23
|
+
if (!b.has(item)) {
|
24
|
+
return false;
|
25
|
+
}
|
26
|
+
}
|
27
|
+
return true;
|
28
|
+
};
|
29
|
+
/**
|
30
|
+
* Returns true when the two ets contain the same set of elements
|
31
|
+
*
|
32
|
+
* A = B
|
33
|
+
*/
|
34
|
+
const areEquivalent = (a, b) => a.size == b.size && isSuperset(a, b) && isSubset(a, b);
|
35
|
+
|
36
|
+
const expand = (str) => str.split(',').flatMap((entry) => {
|
37
|
+
if (!entry.includes('-')) {
|
38
|
+
return entry;
|
39
|
+
}
|
40
|
+
const result = [];
|
41
|
+
const [start, end] = entry.split('-').map(Number);
|
42
|
+
for (let i = start; i <= end; i++) {
|
43
|
+
result.push(i.toString());
|
44
|
+
}
|
45
|
+
return result;
|
46
|
+
});
|
47
|
+
/**
|
48
|
+
* Exported from https://en.wikipedia.org/wiki/List_of_North_American_Numbering_Plan_area_codes
|
49
|
+
*
|
50
|
+
* In Dev Tools, select the `tbody` element containing the area codes and run the following code,
|
51
|
+
* replacing the emdash character with a simple endash:
|
52
|
+
*
|
53
|
+
* ```ts
|
54
|
+
* [...$0.querySelectorAll('td:first-child')]
|
55
|
+
* .filter(cell => cell.firstChild.nodeName != 'A')
|
56
|
+
* .map(cell => cell.textContent.trim()).join(',')
|
57
|
+
* ```
|
58
|
+
*/
|
59
|
+
new Set([
|
60
|
+
...expand('200,211,221,222,230,232,233,235,237-238,241,243,244,245,247,255,257,258-259,261,265,266,271,273,274,275,277,278,280,282,283,285-287,288,290-299'),
|
61
|
+
...expand('300,311,322,324,327,328,333,335,338,342,344,348-349,353,355,356,357-359,362,366,369,370-379,381,382,383-384,387,388,389,390-399'),
|
62
|
+
...expand('400,411,420,421-422,426-427,428,429,433,439,444,446,449,451-454,455,456,457,459,460,461-462,465,466,467,471,476,477,481-483,485-486,487,488,489,490-499'),
|
63
|
+
...expand('511,532,535,536,537,538,542-543,545-547,549-550,552-554,555,556,558,560,565,568,569,576,578,583,589,590-599'),
|
64
|
+
...expand('611,621,624,625,627,632,633,634-635,637-638,642-643,644,648,652-654,655,663,665,666,668,673-676,677,679,685,686,687,688,690-699'),
|
65
|
+
...expand('711,722,723,729,733,735-736,739,741,744,745-746,748,749-751,752,755,756,759,761,764,766,768,776,777,783,788,789,790-799'),
|
66
|
+
...expand('811,821,822,823-824,827,834,836,841-842,846,851,852-853,871,874-875,879,880-887,889,890-899'),
|
67
|
+
...expand('911,921,922,923,924,926,927,932,933,935,942,944,946,950,953,955,957-958,960-969,974,975,976,977,981-982,987,988,990-999'),
|
68
|
+
]);
|
69
|
+
|
3
70
|
const packageName = 'blotoutWallet';
|
4
71
|
const customAttributes = {
|
5
72
|
'--bw-primary': { type: 'color', defaultValue: '#f25c2b' },
|
@@ -54,19 +121,29 @@ const logger = {
|
|
54
121
|
},
|
55
122
|
};
|
56
123
|
|
124
|
+
// eslint-disable-next-line @nx/enforce-module-boundaries
|
57
125
|
class APIError extends Error {
|
58
126
|
constructor(...args) {
|
59
127
|
super(...args);
|
60
128
|
}
|
61
129
|
}
|
130
|
+
const getItemKey = (item) => `${item.productId}-${item.variantId}`;
|
131
|
+
const postMessageKey = 'blotoutWallet';
|
62
132
|
class WalletAPI {
|
63
133
|
constructor({ baseUrl, userId, enabled }) {
|
64
134
|
this.listeners = new Set();
|
65
135
|
this._cart = { cartId: null, items: [], email: false };
|
136
|
+
this.onWindowMessage = (event) => {
|
137
|
+
var _a;
|
138
|
+
if (((_a = event.data) === null || _a === void 0 ? void 0 : _a.name) == postMessageKey) {
|
139
|
+
this.cart = event.data.cart;
|
140
|
+
}
|
141
|
+
};
|
66
142
|
this.baseUrl = baseUrl;
|
67
143
|
this.userId = userId;
|
68
144
|
this.enabled = enabled;
|
69
145
|
logger.info(`[Blotout Wallet] User is ${enabled ? 'enabled' : 'disabled'}`);
|
146
|
+
window.addEventListener('message', this.onWindowMessage);
|
70
147
|
}
|
71
148
|
getHeaders(json = false) {
|
72
149
|
const headers = new Headers({
|
@@ -81,6 +158,7 @@ class WalletAPI {
|
|
81
158
|
return `${this.baseUrl}/providers/blotoutWallet${path}`;
|
82
159
|
}
|
83
160
|
notify() {
|
161
|
+
var _a;
|
84
162
|
for (const listener of this.listeners) {
|
85
163
|
try {
|
86
164
|
listener(this.cart);
|
@@ -89,13 +167,41 @@ class WalletAPI {
|
|
89
167
|
console.error(err);
|
90
168
|
}
|
91
169
|
}
|
170
|
+
(_a = window.top) === null || _a === void 0 ? void 0 : _a.postMessage({ name: postMessageKey, cart: this.cart }, '*');
|
92
171
|
}
|
93
172
|
get cart() {
|
94
173
|
return this._cart;
|
95
174
|
}
|
96
175
|
set cart(value) {
|
97
|
-
this.
|
98
|
-
|
176
|
+
if (this.isCartUpdated(value)) {
|
177
|
+
this._cart = value;
|
178
|
+
this.notify();
|
179
|
+
}
|
180
|
+
}
|
181
|
+
isCartUpdated(value) {
|
182
|
+
if (value.cartId != this.cart.cartId ||
|
183
|
+
value.email != this.cart.email ||
|
184
|
+
value.items.length != this.cart.items.length) {
|
185
|
+
return true;
|
186
|
+
}
|
187
|
+
const newItemsMap = new Map(value.items.map((item) => [item.itemId, item]));
|
188
|
+
const newItemsKeys = new Set(newItemsMap.keys());
|
189
|
+
const currenItemsMap = new Map(this.cart.items.map((item) => [item.itemId, item]));
|
190
|
+
const currentItemsKeys = new Set(currenItemsMap.keys());
|
191
|
+
if (!areEquivalent(newItemsKeys, currentItemsKeys)) {
|
192
|
+
return true;
|
193
|
+
}
|
194
|
+
for (const [key, newItem] of newItemsMap) {
|
195
|
+
const currentItem = currenItemsMap.get(key);
|
196
|
+
if (newItem.amount != currentItem.amount ||
|
197
|
+
newItem.value != currentItem.value ||
|
198
|
+
newItem.name != currentItem.name ||
|
199
|
+
newItem.productId != currentItem.productId ||
|
200
|
+
newItem.variantId != currentItem.variantId) {
|
201
|
+
return true;
|
202
|
+
}
|
203
|
+
}
|
204
|
+
return false;
|
99
205
|
}
|
100
206
|
get segmentEnabled() {
|
101
207
|
return this.enabled;
|
@@ -131,6 +237,26 @@ class WalletAPI {
|
|
131
237
|
return (this.cart = await response.json());
|
132
238
|
});
|
133
239
|
}
|
240
|
+
removeItems(items) {
|
241
|
+
const cartLookup = new Map(this._cart.items.map((item) => [getItemKey(item), item]));
|
242
|
+
const itemLookup = new Map(items.map((item) => [getItemKey(item), item]));
|
243
|
+
const updatedCartItems = [];
|
244
|
+
for (const [itemKey, walletItem] of cartLookup.entries()) {
|
245
|
+
const itemToRemove = itemLookup.get(itemKey);
|
246
|
+
if (itemToRemove) {
|
247
|
+
// if the removed amount is equal or greater, we simply don't push the
|
248
|
+
// wallet item into the updated cart contents
|
249
|
+
if (walletItem.amount > itemToRemove.amount) {
|
250
|
+
walletItem.amount -= itemToRemove.amount;
|
251
|
+
updatedCartItems.push(walletItem);
|
252
|
+
}
|
253
|
+
}
|
254
|
+
else {
|
255
|
+
updatedCartItems.push(walletItem);
|
256
|
+
}
|
257
|
+
}
|
258
|
+
return this.setItems(updatedCartItems);
|
259
|
+
}
|
134
260
|
setItems(items) {
|
135
261
|
return fetch(this.getUrl('/items'), {
|
136
262
|
method: 'PUT',
|
@@ -144,7 +270,7 @@ class WalletAPI {
|
|
144
270
|
});
|
145
271
|
}
|
146
272
|
updateItem(item) {
|
147
|
-
fetch(this.getUrl(`/items/${item.itemId}`), {
|
273
|
+
return fetch(this.getUrl(`/items/${item.itemId}`), {
|
148
274
|
method: 'PUT',
|
149
275
|
headers: this.getHeaders(true),
|
150
276
|
body: JSON.stringify({
|
@@ -161,7 +287,7 @@ class WalletAPI {
|
|
161
287
|
});
|
162
288
|
}
|
163
289
|
removeItem(itemId) {
|
164
|
-
fetch(this.getUrl(`/items/${itemId}`), {
|
290
|
+
return fetch(this.getUrl(`/items/${itemId}`), {
|
165
291
|
method: 'DELETE',
|
166
292
|
headers: this.getHeaders(),
|
167
293
|
}).then(async (response) => {
|
@@ -500,7 +626,7 @@ let BlotoutWallet = class BlotoutWallet extends s {
|
|
500
626
|
this.hasItemsInWallet = false;
|
501
627
|
this.hasEmail = false;
|
502
628
|
this.onWalletUpdated = (walletContent) => {
|
503
|
-
logger.log(
|
629
|
+
logger.log('[Blotout Wallet] Cart updated', walletContent);
|
504
630
|
this.hasItemsInWallet = walletContent.items.length > 0;
|
505
631
|
this.hasEmail = walletContent.email;
|
506
632
|
if (this.isSegmentEnabled && (this.hasItemsInWallet || !this.hasEmail)) {
|
@@ -1009,6 +1135,13 @@ const tag = (params) => {
|
|
1009
1135
|
}
|
1010
1136
|
return;
|
1011
1137
|
}
|
1138
|
+
case 'RemoveFromCart': {
|
1139
|
+
const items = transformItems(params.data);
|
1140
|
+
if (items === null || items === void 0 ? void 0 : items.length) {
|
1141
|
+
wallet.removeItems(items).catch(logger.error);
|
1142
|
+
}
|
1143
|
+
return;
|
1144
|
+
}
|
1012
1145
|
}
|
1013
1146
|
};
|
1014
1147
|
|
package/index.js
CHANGED
@@ -1,6 +1,73 @@
|
|
1
1
|
var ProvidersBlotoutWalletSdk = (function () {
|
2
2
|
'use strict';
|
3
3
|
|
4
|
+
/**
|
5
|
+
* Returns whether A contains the entirety of B (A superset of B)
|
6
|
+
*
|
7
|
+
* A ⊇ B
|
8
|
+
*/
|
9
|
+
const isSuperset = (a, b) => {
|
10
|
+
for (const item of b) {
|
11
|
+
if (!a.has(item)) {
|
12
|
+
return false;
|
13
|
+
}
|
14
|
+
}
|
15
|
+
return true;
|
16
|
+
};
|
17
|
+
/**
|
18
|
+
* Returns whether A is entirely contained within B (A subset of B)
|
19
|
+
*
|
20
|
+
* A ⊆ B
|
21
|
+
*/
|
22
|
+
const isSubset = (a, b) => {
|
23
|
+
for (const item of a) {
|
24
|
+
if (!b.has(item)) {
|
25
|
+
return false;
|
26
|
+
}
|
27
|
+
}
|
28
|
+
return true;
|
29
|
+
};
|
30
|
+
/**
|
31
|
+
* Returns true when the two ets contain the same set of elements
|
32
|
+
*
|
33
|
+
* A = B
|
34
|
+
*/
|
35
|
+
const areEquivalent = (a, b) => a.size == b.size && isSuperset(a, b) && isSubset(a, b);
|
36
|
+
|
37
|
+
const expand = (str) => str.split(',').flatMap((entry) => {
|
38
|
+
if (!entry.includes('-')) {
|
39
|
+
return entry;
|
40
|
+
}
|
41
|
+
const result = [];
|
42
|
+
const [start, end] = entry.split('-').map(Number);
|
43
|
+
for (let i = start; i <= end; i++) {
|
44
|
+
result.push(i.toString());
|
45
|
+
}
|
46
|
+
return result;
|
47
|
+
});
|
48
|
+
/**
|
49
|
+
* Exported from https://en.wikipedia.org/wiki/List_of_North_American_Numbering_Plan_area_codes
|
50
|
+
*
|
51
|
+
* In Dev Tools, select the `tbody` element containing the area codes and run the following code,
|
52
|
+
* replacing the emdash character with a simple endash:
|
53
|
+
*
|
54
|
+
* ```ts
|
55
|
+
* [...$0.querySelectorAll('td:first-child')]
|
56
|
+
* .filter(cell => cell.firstChild.nodeName != 'A')
|
57
|
+
* .map(cell => cell.textContent.trim()).join(',')
|
58
|
+
* ```
|
59
|
+
*/
|
60
|
+
new Set([
|
61
|
+
...expand('200,211,221,222,230,232,233,235,237-238,241,243,244,245,247,255,257,258-259,261,265,266,271,273,274,275,277,278,280,282,283,285-287,288,290-299'),
|
62
|
+
...expand('300,311,322,324,327,328,333,335,338,342,344,348-349,353,355,356,357-359,362,366,369,370-379,381,382,383-384,387,388,389,390-399'),
|
63
|
+
...expand('400,411,420,421-422,426-427,428,429,433,439,444,446,449,451-454,455,456,457,459,460,461-462,465,466,467,471,476,477,481-483,485-486,487,488,489,490-499'),
|
64
|
+
...expand('511,532,535,536,537,538,542-543,545-547,549-550,552-554,555,556,558,560,565,568,569,576,578,583,589,590-599'),
|
65
|
+
...expand('611,621,624,625,627,632,633,634-635,637-638,642-643,644,648,652-654,655,663,665,666,668,673-676,677,679,685,686,687,688,690-699'),
|
66
|
+
...expand('711,722,723,729,733,735-736,739,741,744,745-746,748,749-751,752,755,756,759,761,764,766,768,776,777,783,788,789,790-799'),
|
67
|
+
...expand('811,821,822,823-824,827,834,836,841-842,846,851,852-853,871,874-875,879,880-887,889,890-899'),
|
68
|
+
...expand('911,921,922,923,924,926,927,932,933,935,942,944,946,950,953,955,957-958,960-969,974,975,976,977,981-982,987,988,990-999'),
|
69
|
+
]);
|
70
|
+
|
4
71
|
const packageName = 'blotoutWallet';
|
5
72
|
const customAttributes = {
|
6
73
|
'--bw-primary': { type: 'color', defaultValue: '#f25c2b' },
|
@@ -55,19 +122,29 @@ var ProvidersBlotoutWalletSdk = (function () {
|
|
55
122
|
},
|
56
123
|
};
|
57
124
|
|
125
|
+
// eslint-disable-next-line @nx/enforce-module-boundaries
|
58
126
|
class APIError extends Error {
|
59
127
|
constructor(...args) {
|
60
128
|
super(...args);
|
61
129
|
}
|
62
130
|
}
|
131
|
+
const getItemKey = (item) => `${item.productId}-${item.variantId}`;
|
132
|
+
const postMessageKey = 'blotoutWallet';
|
63
133
|
class WalletAPI {
|
64
134
|
constructor({ baseUrl, userId, enabled }) {
|
65
135
|
this.listeners = new Set();
|
66
136
|
this._cart = { cartId: null, items: [], email: false };
|
137
|
+
this.onWindowMessage = (event) => {
|
138
|
+
var _a;
|
139
|
+
if (((_a = event.data) === null || _a === void 0 ? void 0 : _a.name) == postMessageKey) {
|
140
|
+
this.cart = event.data.cart;
|
141
|
+
}
|
142
|
+
};
|
67
143
|
this.baseUrl = baseUrl;
|
68
144
|
this.userId = userId;
|
69
145
|
this.enabled = enabled;
|
70
146
|
logger.info(`[Blotout Wallet] User is ${enabled ? 'enabled' : 'disabled'}`);
|
147
|
+
window.addEventListener('message', this.onWindowMessage);
|
71
148
|
}
|
72
149
|
getHeaders(json = false) {
|
73
150
|
const headers = new Headers({
|
@@ -82,6 +159,7 @@ var ProvidersBlotoutWalletSdk = (function () {
|
|
82
159
|
return `${this.baseUrl}/providers/blotoutWallet${path}`;
|
83
160
|
}
|
84
161
|
notify() {
|
162
|
+
var _a;
|
85
163
|
for (const listener of this.listeners) {
|
86
164
|
try {
|
87
165
|
listener(this.cart);
|
@@ -90,13 +168,41 @@ var ProvidersBlotoutWalletSdk = (function () {
|
|
90
168
|
console.error(err);
|
91
169
|
}
|
92
170
|
}
|
171
|
+
(_a = window.top) === null || _a === void 0 ? void 0 : _a.postMessage({ name: postMessageKey, cart: this.cart }, '*');
|
93
172
|
}
|
94
173
|
get cart() {
|
95
174
|
return this._cart;
|
96
175
|
}
|
97
176
|
set cart(value) {
|
98
|
-
this.
|
99
|
-
|
177
|
+
if (this.isCartUpdated(value)) {
|
178
|
+
this._cart = value;
|
179
|
+
this.notify();
|
180
|
+
}
|
181
|
+
}
|
182
|
+
isCartUpdated(value) {
|
183
|
+
if (value.cartId != this.cart.cartId ||
|
184
|
+
value.email != this.cart.email ||
|
185
|
+
value.items.length != this.cart.items.length) {
|
186
|
+
return true;
|
187
|
+
}
|
188
|
+
const newItemsMap = new Map(value.items.map((item) => [item.itemId, item]));
|
189
|
+
const newItemsKeys = new Set(newItemsMap.keys());
|
190
|
+
const currenItemsMap = new Map(this.cart.items.map((item) => [item.itemId, item]));
|
191
|
+
const currentItemsKeys = new Set(currenItemsMap.keys());
|
192
|
+
if (!areEquivalent(newItemsKeys, currentItemsKeys)) {
|
193
|
+
return true;
|
194
|
+
}
|
195
|
+
for (const [key, newItem] of newItemsMap) {
|
196
|
+
const currentItem = currenItemsMap.get(key);
|
197
|
+
if (newItem.amount != currentItem.amount ||
|
198
|
+
newItem.value != currentItem.value ||
|
199
|
+
newItem.name != currentItem.name ||
|
200
|
+
newItem.productId != currentItem.productId ||
|
201
|
+
newItem.variantId != currentItem.variantId) {
|
202
|
+
return true;
|
203
|
+
}
|
204
|
+
}
|
205
|
+
return false;
|
100
206
|
}
|
101
207
|
get segmentEnabled() {
|
102
208
|
return this.enabled;
|
@@ -132,6 +238,26 @@ var ProvidersBlotoutWalletSdk = (function () {
|
|
132
238
|
return (this.cart = await response.json());
|
133
239
|
});
|
134
240
|
}
|
241
|
+
removeItems(items) {
|
242
|
+
const cartLookup = new Map(this._cart.items.map((item) => [getItemKey(item), item]));
|
243
|
+
const itemLookup = new Map(items.map((item) => [getItemKey(item), item]));
|
244
|
+
const updatedCartItems = [];
|
245
|
+
for (const [itemKey, walletItem] of cartLookup.entries()) {
|
246
|
+
const itemToRemove = itemLookup.get(itemKey);
|
247
|
+
if (itemToRemove) {
|
248
|
+
// if the removed amount is equal or greater, we simply don't push the
|
249
|
+
// wallet item into the updated cart contents
|
250
|
+
if (walletItem.amount > itemToRemove.amount) {
|
251
|
+
walletItem.amount -= itemToRemove.amount;
|
252
|
+
updatedCartItems.push(walletItem);
|
253
|
+
}
|
254
|
+
}
|
255
|
+
else {
|
256
|
+
updatedCartItems.push(walletItem);
|
257
|
+
}
|
258
|
+
}
|
259
|
+
return this.setItems(updatedCartItems);
|
260
|
+
}
|
135
261
|
setItems(items) {
|
136
262
|
return fetch(this.getUrl('/items'), {
|
137
263
|
method: 'PUT',
|
@@ -145,7 +271,7 @@ var ProvidersBlotoutWalletSdk = (function () {
|
|
145
271
|
});
|
146
272
|
}
|
147
273
|
updateItem(item) {
|
148
|
-
fetch(this.getUrl(`/items/${item.itemId}`), {
|
274
|
+
return fetch(this.getUrl(`/items/${item.itemId}`), {
|
149
275
|
method: 'PUT',
|
150
276
|
headers: this.getHeaders(true),
|
151
277
|
body: JSON.stringify({
|
@@ -162,7 +288,7 @@ var ProvidersBlotoutWalletSdk = (function () {
|
|
162
288
|
});
|
163
289
|
}
|
164
290
|
removeItem(itemId) {
|
165
|
-
fetch(this.getUrl(`/items/${itemId}`), {
|
291
|
+
return fetch(this.getUrl(`/items/${itemId}`), {
|
166
292
|
method: 'DELETE',
|
167
293
|
headers: this.getHeaders(),
|
168
294
|
}).then(async (response) => {
|
@@ -501,7 +627,7 @@ var ProvidersBlotoutWalletSdk = (function () {
|
|
501
627
|
this.hasItemsInWallet = false;
|
502
628
|
this.hasEmail = false;
|
503
629
|
this.onWalletUpdated = (walletContent) => {
|
504
|
-
logger.log(
|
630
|
+
logger.log('[Blotout Wallet] Cart updated', walletContent);
|
505
631
|
this.hasItemsInWallet = walletContent.items.length > 0;
|
506
632
|
this.hasEmail = walletContent.email;
|
507
633
|
if (this.isSegmentEnabled && (this.hasItemsInWallet || !this.hasEmail)) {
|
@@ -1010,6 +1136,13 @@ var ProvidersBlotoutWalletSdk = (function () {
|
|
1010
1136
|
}
|
1011
1137
|
return;
|
1012
1138
|
}
|
1139
|
+
case 'RemoveFromCart': {
|
1140
|
+
const items = transformItems(params.data);
|
1141
|
+
if (items === null || items === void 0 ? void 0 : items.length) {
|
1142
|
+
wallet.removeItems(items).catch(logger.error);
|
1143
|
+
}
|
1144
|
+
return;
|
1145
|
+
}
|
1013
1146
|
}
|
1014
1147
|
};
|
1015
1148
|
|
package/index.mjs
CHANGED
@@ -1,3 +1,70 @@
|
|
1
|
+
/**
|
2
|
+
* Returns whether A contains the entirety of B (A superset of B)
|
3
|
+
*
|
4
|
+
* A ⊇ B
|
5
|
+
*/
|
6
|
+
const isSuperset = (a, b) => {
|
7
|
+
for (const item of b) {
|
8
|
+
if (!a.has(item)) {
|
9
|
+
return false;
|
10
|
+
}
|
11
|
+
}
|
12
|
+
return true;
|
13
|
+
};
|
14
|
+
/**
|
15
|
+
* Returns whether A is entirely contained within B (A subset of B)
|
16
|
+
*
|
17
|
+
* A ⊆ B
|
18
|
+
*/
|
19
|
+
const isSubset = (a, b) => {
|
20
|
+
for (const item of a) {
|
21
|
+
if (!b.has(item)) {
|
22
|
+
return false;
|
23
|
+
}
|
24
|
+
}
|
25
|
+
return true;
|
26
|
+
};
|
27
|
+
/**
|
28
|
+
* Returns true when the two ets contain the same set of elements
|
29
|
+
*
|
30
|
+
* A = B
|
31
|
+
*/
|
32
|
+
const areEquivalent = (a, b) => a.size == b.size && isSuperset(a, b) && isSubset(a, b);
|
33
|
+
|
34
|
+
const expand = (str) => str.split(',').flatMap((entry) => {
|
35
|
+
if (!entry.includes('-')) {
|
36
|
+
return entry;
|
37
|
+
}
|
38
|
+
const result = [];
|
39
|
+
const [start, end] = entry.split('-').map(Number);
|
40
|
+
for (let i = start; i <= end; i++) {
|
41
|
+
result.push(i.toString());
|
42
|
+
}
|
43
|
+
return result;
|
44
|
+
});
|
45
|
+
/**
|
46
|
+
* Exported from https://en.wikipedia.org/wiki/List_of_North_American_Numbering_Plan_area_codes
|
47
|
+
*
|
48
|
+
* In Dev Tools, select the `tbody` element containing the area codes and run the following code,
|
49
|
+
* replacing the emdash character with a simple endash:
|
50
|
+
*
|
51
|
+
* ```ts
|
52
|
+
* [...$0.querySelectorAll('td:first-child')]
|
53
|
+
* .filter(cell => cell.firstChild.nodeName != 'A')
|
54
|
+
* .map(cell => cell.textContent.trim()).join(',')
|
55
|
+
* ```
|
56
|
+
*/
|
57
|
+
new Set([
|
58
|
+
...expand('200,211,221,222,230,232,233,235,237-238,241,243,244,245,247,255,257,258-259,261,265,266,271,273,274,275,277,278,280,282,283,285-287,288,290-299'),
|
59
|
+
...expand('300,311,322,324,327,328,333,335,338,342,344,348-349,353,355,356,357-359,362,366,369,370-379,381,382,383-384,387,388,389,390-399'),
|
60
|
+
...expand('400,411,420,421-422,426-427,428,429,433,439,444,446,449,451-454,455,456,457,459,460,461-462,465,466,467,471,476,477,481-483,485-486,487,488,489,490-499'),
|
61
|
+
...expand('511,532,535,536,537,538,542-543,545-547,549-550,552-554,555,556,558,560,565,568,569,576,578,583,589,590-599'),
|
62
|
+
...expand('611,621,624,625,627,632,633,634-635,637-638,642-643,644,648,652-654,655,663,665,666,668,673-676,677,679,685,686,687,688,690-699'),
|
63
|
+
...expand('711,722,723,729,733,735-736,739,741,744,745-746,748,749-751,752,755,756,759,761,764,766,768,776,777,783,788,789,790-799'),
|
64
|
+
...expand('811,821,822,823-824,827,834,836,841-842,846,851,852-853,871,874-875,879,880-887,889,890-899'),
|
65
|
+
...expand('911,921,922,923,924,926,927,932,933,935,942,944,946,950,953,955,957-958,960-969,974,975,976,977,981-982,987,988,990-999'),
|
66
|
+
]);
|
67
|
+
|
1
68
|
const packageName = 'blotoutWallet';
|
2
69
|
const customAttributes = {
|
3
70
|
'--bw-primary': { type: 'color', defaultValue: '#f25c2b' },
|
@@ -52,19 +119,29 @@ const logger = {
|
|
52
119
|
},
|
53
120
|
};
|
54
121
|
|
122
|
+
// eslint-disable-next-line @nx/enforce-module-boundaries
|
55
123
|
class APIError extends Error {
|
56
124
|
constructor(...args) {
|
57
125
|
super(...args);
|
58
126
|
}
|
59
127
|
}
|
128
|
+
const getItemKey = (item) => `${item.productId}-${item.variantId}`;
|
129
|
+
const postMessageKey = 'blotoutWallet';
|
60
130
|
class WalletAPI {
|
61
131
|
constructor({ baseUrl, userId, enabled }) {
|
62
132
|
this.listeners = new Set();
|
63
133
|
this._cart = { cartId: null, items: [], email: false };
|
134
|
+
this.onWindowMessage = (event) => {
|
135
|
+
var _a;
|
136
|
+
if (((_a = event.data) === null || _a === void 0 ? void 0 : _a.name) == postMessageKey) {
|
137
|
+
this.cart = event.data.cart;
|
138
|
+
}
|
139
|
+
};
|
64
140
|
this.baseUrl = baseUrl;
|
65
141
|
this.userId = userId;
|
66
142
|
this.enabled = enabled;
|
67
143
|
logger.info(`[Blotout Wallet] User is ${enabled ? 'enabled' : 'disabled'}`);
|
144
|
+
window.addEventListener('message', this.onWindowMessage);
|
68
145
|
}
|
69
146
|
getHeaders(json = false) {
|
70
147
|
const headers = new Headers({
|
@@ -79,6 +156,7 @@ class WalletAPI {
|
|
79
156
|
return `${this.baseUrl}/providers/blotoutWallet${path}`;
|
80
157
|
}
|
81
158
|
notify() {
|
159
|
+
var _a;
|
82
160
|
for (const listener of this.listeners) {
|
83
161
|
try {
|
84
162
|
listener(this.cart);
|
@@ -87,13 +165,41 @@ class WalletAPI {
|
|
87
165
|
console.error(err);
|
88
166
|
}
|
89
167
|
}
|
168
|
+
(_a = window.top) === null || _a === void 0 ? void 0 : _a.postMessage({ name: postMessageKey, cart: this.cart }, '*');
|
90
169
|
}
|
91
170
|
get cart() {
|
92
171
|
return this._cart;
|
93
172
|
}
|
94
173
|
set cart(value) {
|
95
|
-
this.
|
96
|
-
|
174
|
+
if (this.isCartUpdated(value)) {
|
175
|
+
this._cart = value;
|
176
|
+
this.notify();
|
177
|
+
}
|
178
|
+
}
|
179
|
+
isCartUpdated(value) {
|
180
|
+
if (value.cartId != this.cart.cartId ||
|
181
|
+
value.email != this.cart.email ||
|
182
|
+
value.items.length != this.cart.items.length) {
|
183
|
+
return true;
|
184
|
+
}
|
185
|
+
const newItemsMap = new Map(value.items.map((item) => [item.itemId, item]));
|
186
|
+
const newItemsKeys = new Set(newItemsMap.keys());
|
187
|
+
const currenItemsMap = new Map(this.cart.items.map((item) => [item.itemId, item]));
|
188
|
+
const currentItemsKeys = new Set(currenItemsMap.keys());
|
189
|
+
if (!areEquivalent(newItemsKeys, currentItemsKeys)) {
|
190
|
+
return true;
|
191
|
+
}
|
192
|
+
for (const [key, newItem] of newItemsMap) {
|
193
|
+
const currentItem = currenItemsMap.get(key);
|
194
|
+
if (newItem.amount != currentItem.amount ||
|
195
|
+
newItem.value != currentItem.value ||
|
196
|
+
newItem.name != currentItem.name ||
|
197
|
+
newItem.productId != currentItem.productId ||
|
198
|
+
newItem.variantId != currentItem.variantId) {
|
199
|
+
return true;
|
200
|
+
}
|
201
|
+
}
|
202
|
+
return false;
|
97
203
|
}
|
98
204
|
get segmentEnabled() {
|
99
205
|
return this.enabled;
|
@@ -129,6 +235,26 @@ class WalletAPI {
|
|
129
235
|
return (this.cart = await response.json());
|
130
236
|
});
|
131
237
|
}
|
238
|
+
removeItems(items) {
|
239
|
+
const cartLookup = new Map(this._cart.items.map((item) => [getItemKey(item), item]));
|
240
|
+
const itemLookup = new Map(items.map((item) => [getItemKey(item), item]));
|
241
|
+
const updatedCartItems = [];
|
242
|
+
for (const [itemKey, walletItem] of cartLookup.entries()) {
|
243
|
+
const itemToRemove = itemLookup.get(itemKey);
|
244
|
+
if (itemToRemove) {
|
245
|
+
// if the removed amount is equal or greater, we simply don't push the
|
246
|
+
// wallet item into the updated cart contents
|
247
|
+
if (walletItem.amount > itemToRemove.amount) {
|
248
|
+
walletItem.amount -= itemToRemove.amount;
|
249
|
+
updatedCartItems.push(walletItem);
|
250
|
+
}
|
251
|
+
}
|
252
|
+
else {
|
253
|
+
updatedCartItems.push(walletItem);
|
254
|
+
}
|
255
|
+
}
|
256
|
+
return this.setItems(updatedCartItems);
|
257
|
+
}
|
132
258
|
setItems(items) {
|
133
259
|
return fetch(this.getUrl('/items'), {
|
134
260
|
method: 'PUT',
|
@@ -142,7 +268,7 @@ class WalletAPI {
|
|
142
268
|
});
|
143
269
|
}
|
144
270
|
updateItem(item) {
|
145
|
-
fetch(this.getUrl(`/items/${item.itemId}`), {
|
271
|
+
return fetch(this.getUrl(`/items/${item.itemId}`), {
|
146
272
|
method: 'PUT',
|
147
273
|
headers: this.getHeaders(true),
|
148
274
|
body: JSON.stringify({
|
@@ -159,7 +285,7 @@ class WalletAPI {
|
|
159
285
|
});
|
160
286
|
}
|
161
287
|
removeItem(itemId) {
|
162
|
-
fetch(this.getUrl(`/items/${itemId}`), {
|
288
|
+
return fetch(this.getUrl(`/items/${itemId}`), {
|
163
289
|
method: 'DELETE',
|
164
290
|
headers: this.getHeaders(),
|
165
291
|
}).then(async (response) => {
|
@@ -498,7 +624,7 @@ let BlotoutWallet = class BlotoutWallet extends s {
|
|
498
624
|
this.hasItemsInWallet = false;
|
499
625
|
this.hasEmail = false;
|
500
626
|
this.onWalletUpdated = (walletContent) => {
|
501
|
-
logger.log(
|
627
|
+
logger.log('[Blotout Wallet] Cart updated', walletContent);
|
502
628
|
this.hasItemsInWallet = walletContent.items.length > 0;
|
503
629
|
this.hasEmail = walletContent.email;
|
504
630
|
if (this.isSegmentEnabled && (this.hasItemsInWallet || !this.hasEmail)) {
|
@@ -1007,6 +1133,13 @@ const tag = (params) => {
|
|
1007
1133
|
}
|
1008
1134
|
return;
|
1009
1135
|
}
|
1136
|
+
case 'RemoveFromCart': {
|
1137
|
+
const items = transformItems(params.data);
|
1138
|
+
if (items === null || items === void 0 ? void 0 : items.length) {
|
1139
|
+
wallet.removeItems(items).catch(logger.error);
|
1140
|
+
}
|
1141
|
+
return;
|
1142
|
+
}
|
1010
1143
|
}
|
1011
1144
|
};
|
1012
1145
|
|