@ordergroove/offers 2.40.4-alpha-PR-1095-4.10 → 2.41.0
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 +22 -0
- package/dist/bundle-report.html +20 -13
- package/dist/offers.js +37 -37
- package/dist/offers.js.map +4 -4
- package/package.json +2 -2
- package/src/__tests__/offers.spec.js +13 -24
- package/src/core/__tests__/actions.spec.js +1 -43
- package/src/core/__tests__/experiments.spec.js +5 -1
- package/src/core/__tests__/offerRequest.spec.js +78 -0
- package/src/core/actions.js +1 -21
- package/src/core/api.js +3 -11
- package/src/core/experiments.js +30 -14
- package/src/core/offerRequest.js +34 -0
- package/src/core/reducer.js +1 -1
- package/src/core/store.js +10 -1
- package/src/index.js +1 -3
- package/src/make-api.js +1 -5
- package/src/shopify/shopifyMiddleware.ts +2 -2
- package/src/shopify/shopifyReducer.js +5 -5
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ordergroove/offers",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.41.0",
|
|
4
4
|
"description": "offer state component",
|
|
5
5
|
"author": "Eugenio Lattanzio <eugenio63@gmail.com>",
|
|
6
6
|
"homepage": "https://github.com/ordergroove/plush-toys#readme",
|
|
@@ -49,5 +49,5 @@
|
|
|
49
49
|
"@ordergroove/offers-templates": "^0.9.6",
|
|
50
50
|
"@types/lodash.memoize": "^4.1.9"
|
|
51
51
|
},
|
|
52
|
-
"gitHead": "
|
|
52
|
+
"gitHead": "24e315047abbe0e265409f1f5265e3d9fd462672"
|
|
53
53
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import * as offersAll from '../index';
|
|
2
2
|
import { api } from '../core/api';
|
|
3
|
+
import { REQUEST_OFFER } from '../core/constants';
|
|
3
4
|
|
|
4
5
|
const offers = offersAll.offers;
|
|
5
6
|
|
|
@@ -71,16 +72,13 @@ describe('Offers', () => {
|
|
|
71
72
|
});
|
|
72
73
|
|
|
73
74
|
describe('offers.resolveSettings', () => {
|
|
74
|
-
it('should fetch offer for single product in pdp', () => {
|
|
75
|
+
it('should fetch offer for single product in pdp', async () => {
|
|
75
76
|
offers.resolveSettings('0e5de2bedc5e11e3a2e4bc764e106cf4', 'staging', { product: '123' }, mockStore);
|
|
76
|
-
expect(
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
'
|
|
80
|
-
|
|
81
|
-
'pdp',
|
|
82
|
-
theState
|
|
83
|
-
);
|
|
77
|
+
expect(mockStore.dispatch).toHaveBeenCalledTimes(1);
|
|
78
|
+
expect(mockStore.dispatch).toHaveBeenCalledWith({
|
|
79
|
+
type: REQUEST_OFFER,
|
|
80
|
+
payload: { product: '123', module: 'pdp', offer: undefined }
|
|
81
|
+
});
|
|
84
82
|
});
|
|
85
83
|
|
|
86
84
|
it('should fetch offer for each product in cart', () => {
|
|
@@ -90,23 +88,14 @@ describe('Offers', () => {
|
|
|
90
88
|
{ cart: { products: ['123', '456'] } },
|
|
91
89
|
mockStore
|
|
92
90
|
);
|
|
93
|
-
expect(fetchOfferSpy).toHaveBeenCalledTimes(2);
|
|
94
91
|
|
|
95
|
-
expect(
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
'
|
|
99
|
-
'123',
|
|
100
|
-
'pdp',
|
|
101
|
-
theState
|
|
92
|
+
expect(mockStore.dispatch).toHaveBeenCalledTimes(2);
|
|
93
|
+
|
|
94
|
+
expect(mockStore.dispatch.calls.argsFor(0)).toEqual([
|
|
95
|
+
{ type: REQUEST_OFFER, payload: { product: '123', module: 'pdp', offer: undefined } }
|
|
102
96
|
]);
|
|
103
|
-
expect(
|
|
104
|
-
'
|
|
105
|
-
'0e5de2bedc5e11e3a2e4bc764e106cf4',
|
|
106
|
-
'xyz',
|
|
107
|
-
'456',
|
|
108
|
-
'pdp',
|
|
109
|
-
theState
|
|
97
|
+
expect(mockStore.dispatch.calls.argsFor(1)).toEqual([
|
|
98
|
+
{ type: REQUEST_OFFER, payload: { product: '456', module: 'pdp', offer: undefined } }
|
|
110
99
|
]);
|
|
111
100
|
});
|
|
112
101
|
|
|
@@ -157,49 +157,7 @@ describe('redux actions', function () {
|
|
|
157
157
|
});
|
|
158
158
|
|
|
159
159
|
it('fetchOffer should return a function', () => {
|
|
160
|
-
expect(fetchOffer()).toEqual(
|
|
161
|
-
});
|
|
162
|
-
|
|
163
|
-
it('should call api.fetchOffer with environment.apiUrl from state as first param ', async () => {
|
|
164
|
-
const state = {
|
|
165
|
-
merchantId: 'the merchantId',
|
|
166
|
-
sessionId: 'the sessionId',
|
|
167
|
-
environment: { apiUrl: 'the environment.apiUrl' }
|
|
168
|
-
};
|
|
169
|
-
const getState = jasmine.createSpy('getState').and.returnValue(state);
|
|
170
|
-
const fetchOfferSpy = spyOn(api, 'fetchOffer').and.resolveTo({ hey: 'ho' });
|
|
171
|
-
await fetchOffer('the product')(this.dispatch, getState);
|
|
172
|
-
expect(fetchOfferSpy).toHaveBeenCalledWith(
|
|
173
|
-
'the environment.apiUrl',
|
|
174
|
-
'the merchantId',
|
|
175
|
-
'the sessionId',
|
|
176
|
-
'the product',
|
|
177
|
-
'pdp',
|
|
178
|
-
state
|
|
179
|
-
);
|
|
180
|
-
});
|
|
181
|
-
|
|
182
|
-
it('should dispatch the receiveOffer if api returns', async () => {
|
|
183
|
-
const fetchOfferSpy = spyOn(api, 'fetchOffer').and.resolveTo({ hey: 'ho' });
|
|
184
|
-
|
|
185
|
-
await fetchOffer('yum product')(this.dispatch, this.getState);
|
|
186
|
-
|
|
187
|
-
expect(this.dispatch.calls.count()).toEqual(3);
|
|
188
|
-
expect(this.dispatch.calls.argsFor(0)[0]).toEqual(requestOffer('yum product', 'pdp'));
|
|
189
|
-
expect(this.dispatch.calls.argsFor(1)[0]).toEqual(receiveOffer({ hey: 'ho' }));
|
|
190
|
-
expect(this.getState).toHaveBeenCalled();
|
|
191
|
-
expect(fetchOfferSpy).toHaveBeenCalledWith('some-apiUrl', 'foo', 'bar', 'yum product', 'pdp', theState);
|
|
192
|
-
});
|
|
193
|
-
|
|
194
|
-
it('should dispatch fetchResponseError if api fails', async () => {
|
|
195
|
-
const fetchOfferSpy = spyOn(api, 'fetchOffer').and.rejectWith(Error({ hey: 'ho' }));
|
|
196
|
-
await fetchOffer('yum product')(this.dispatch, this.getState);
|
|
197
|
-
|
|
198
|
-
expect(this.dispatch.calls.count()).toEqual(3);
|
|
199
|
-
expect(this.dispatch.calls.argsFor(0)[0]).toEqual(requestOffer('yum product', 'pdp'));
|
|
200
|
-
expect(this.dispatch.calls.argsFor(1)[0]).toEqual(fetchResponseError(Error({ hey: 'ho' })));
|
|
201
|
-
expect(fetchOfferSpy).toHaveBeenCalledWith('some-apiUrl', 'foo', 'bar', 'yum product', 'pdp', theState);
|
|
202
|
-
expect(this.getState).toHaveBeenCalled();
|
|
160
|
+
expect(fetchOffer()).toEqual(requestOffer());
|
|
203
161
|
});
|
|
204
162
|
});
|
|
205
163
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { experimentsMiddleware, experimentsReducer, getAssignedExperimentVariant } from '../experiments';
|
|
2
|
-
import { CREATED_SESSION_ID, RECEIVE_MERCHANT_SETTINGS } from '../constants';
|
|
2
|
+
import { CREATED_SESSION_ID, READY, RECEIVE_MERCHANT_SETTINGS } from '../constants';
|
|
3
3
|
import { createSessionId } from '../actions';
|
|
4
4
|
import { createStore, combineReducers, applyMiddleware } from 'redux';
|
|
5
5
|
import { sessionId } from '../reducer';
|
|
@@ -34,7 +34,9 @@ describe('experiments', () => {
|
|
|
34
34
|
|
|
35
35
|
store.dispatch({ type: CREATED_SESSION_ID, payload: 'abc' });
|
|
36
36
|
store.dispatch({ type: RECEIVE_MERCHANT_SETTINGS, payload: { experiments } });
|
|
37
|
+
store.dispatch({ type: READY });
|
|
37
38
|
|
|
39
|
+
await new Promise(r => setTimeout(r, 10));
|
|
38
40
|
const {
|
|
39
41
|
experiments: { currentVariant }
|
|
40
42
|
} = store.getState();
|
|
@@ -56,7 +58,9 @@ describe('experiments', () => {
|
|
|
56
58
|
|
|
57
59
|
store.dispatch({ type: CREATED_SESSION_ID, payload: 'cda' });
|
|
58
60
|
store.dispatch({ type: RECEIVE_MERCHANT_SETTINGS, payload: { experiments } });
|
|
61
|
+
store.dispatch({ type: READY });
|
|
59
62
|
|
|
63
|
+
await new Promise(r => setTimeout(r, 10));
|
|
60
64
|
const {
|
|
61
65
|
experiments: { currentVariant }
|
|
62
66
|
} = store.getState();
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { offerRequestMiddleware } from '../offerRequest';
|
|
2
|
+
|
|
3
|
+
import { environment, merchantId, offer, sessionId } from '../reducer';
|
|
4
|
+
import { requestOffer } from '../actions';
|
|
5
|
+
import { createStore, combineReducers, applyMiddleware } from 'redux';
|
|
6
|
+
import { FETCH_RESPONSE_ERROR } from '../constants';
|
|
7
|
+
|
|
8
|
+
const mockOfferResponse = (productId, inStock = true, autoship = true, defaultFrequency) => {
|
|
9
|
+
return Promise.resolve({
|
|
10
|
+
json() {
|
|
11
|
+
return Promise.resolve({
|
|
12
|
+
in_stock: { [productId]: inStock },
|
|
13
|
+
eligibility_groups: { [productId]: ['subscription', 'upsell'] },
|
|
14
|
+
result: 'success',
|
|
15
|
+
autoship: { [productId]: autoship },
|
|
16
|
+
autoship_by_default: { [productId]: false },
|
|
17
|
+
modifiers: {},
|
|
18
|
+
module_view: { regular: '096135e6650111e9a444bc764e106cf4' },
|
|
19
|
+
incentives_display: {},
|
|
20
|
+
incentives: {
|
|
21
|
+
[productId]: { initial: [], ongoing: [] }
|
|
22
|
+
},
|
|
23
|
+
...(defaultFrequency && {
|
|
24
|
+
default_frequencies: {
|
|
25
|
+
[productId]: defaultFrequency
|
|
26
|
+
}
|
|
27
|
+
})
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
describe('offerRequest', () => {
|
|
34
|
+
let fetch, store;
|
|
35
|
+
const lastError = (acc = null, cur) => (cur.type == FETCH_RESPONSE_ERROR ? cur : acc);
|
|
36
|
+
beforeEach(() => {
|
|
37
|
+
fetch = spyOn(window, 'fetch');
|
|
38
|
+
|
|
39
|
+
store = createStore(
|
|
40
|
+
combineReducers({ offer, environment, merchantId, sessionId, lastError }),
|
|
41
|
+
{
|
|
42
|
+
environment: { apiUrl: 'http://localhost' },
|
|
43
|
+
merchantId: 'abc-merchant',
|
|
44
|
+
sessionId: 'x.y.z',
|
|
45
|
+
lastError: null
|
|
46
|
+
},
|
|
47
|
+
applyMiddleware(offerRequestMiddleware)
|
|
48
|
+
);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it('should call fetch offer with environment apiUrl from state as first param', async () => {
|
|
52
|
+
fetch.and.returnValue(mockOfferResponse('123', true, true));
|
|
53
|
+
store.dispatch(requestOffer('123'));
|
|
54
|
+
expect(fetch).toHaveBeenCalledTimes(1);
|
|
55
|
+
expect(fetch).toHaveBeenCalledWith(
|
|
56
|
+
'http://localhost/offer/abc-merchant/pdp?session_id=x.y.z&page_type=1&p=%5B%22123%22%5D&module_view=%5B%22regular%22%5D',
|
|
57
|
+
|
|
58
|
+
{}
|
|
59
|
+
);
|
|
60
|
+
|
|
61
|
+
await new Promise(r => setTimeout(r, 10));
|
|
62
|
+
expect(store.getState().offer.offerId).toEqual('096135e6650111e9a444bc764e106cf4');
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it('should dispatch fetchResponseError if api fails', async () => {
|
|
66
|
+
fetch.and.rejectWith({ error: true });
|
|
67
|
+
store.dispatch(requestOffer('1234'));
|
|
68
|
+
await new Promise(r => setTimeout(r, 100));
|
|
69
|
+
expect(fetch).toHaveBeenCalledTimes(1);
|
|
70
|
+
expect(fetch).toHaveBeenCalledWith(
|
|
71
|
+
'http://localhost/offer/abc-merchant/pdp?session_id=x.y.z&page_type=1&p=%5B%221234%22%5D&module_view=%5B%22regular%22%5D',
|
|
72
|
+
|
|
73
|
+
{}
|
|
74
|
+
);
|
|
75
|
+
|
|
76
|
+
expect(store.getState().lastError).toEqual({ type: FETCH_RESPONSE_ERROR, payload: { error: true } });
|
|
77
|
+
});
|
|
78
|
+
});
|
package/src/core/actions.js
CHANGED
|
@@ -198,27 +198,7 @@ export const requestOffer = (product, module = constants.DEFAULT_OFFER_MODULE, o
|
|
|
198
198
|
payload: { product, module, offer }
|
|
199
199
|
});
|
|
200
200
|
|
|
201
|
-
export const fetchOffer =
|
|
202
|
-
function fetchOfferThunk(dispatch, getState) {
|
|
203
|
-
const state = getState();
|
|
204
|
-
const {
|
|
205
|
-
merchantId,
|
|
206
|
-
sessionId,
|
|
207
|
-
environment: { apiUrl }
|
|
208
|
-
} = state;
|
|
209
|
-
const requestAction = requestOffer(product, module, offerElement);
|
|
210
|
-
dispatch(requestAction);
|
|
211
|
-
|
|
212
|
-
const productId = safeProductId(product);
|
|
213
|
-
if (!productId) return null;
|
|
214
|
-
return api
|
|
215
|
-
.fetchOffer(apiUrl, merchantId, sessionId, productId, module, state)
|
|
216
|
-
.then(
|
|
217
|
-
response => dispatch(receiveOffer(response, offerElement)),
|
|
218
|
-
err => dispatch(fetchResponseError(err))
|
|
219
|
-
)
|
|
220
|
-
.finally(() => dispatch(fetchDone(requestAction)));
|
|
221
|
-
};
|
|
201
|
+
export const fetchOffer = requestOffer;
|
|
222
202
|
|
|
223
203
|
export const checkout = () => ({
|
|
224
204
|
type: constants.CHECKOUT
|
package/src/core/api.js
CHANGED
|
@@ -66,7 +66,7 @@ export const toProductId = product =>
|
|
|
66
66
|
|
|
67
67
|
export const fetchOffer = memoize(
|
|
68
68
|
withFetchJson(
|
|
69
|
-
withHost((merchantId, sessionId, product, module = 'pdp',
|
|
69
|
+
withHost((merchantId, sessionId, product, module = 'pdp', searchParams = {}) => {
|
|
70
70
|
if (!merchantId) throw Error('merchantId required');
|
|
71
71
|
if (!sessionId) throw Error('sessionId required');
|
|
72
72
|
if (!product) throw Error('product required');
|
|
@@ -75,18 +75,10 @@ export const fetchOffer = memoize(
|
|
|
75
75
|
['session_id', sessionId],
|
|
76
76
|
['page_type', 1],
|
|
77
77
|
['p', toProductId(product)],
|
|
78
|
-
['module_view', JSON.stringify(['regular'])]
|
|
78
|
+
['module_view', JSON.stringify(['regular'])],
|
|
79
|
+
...Object.entries(searchParams)
|
|
79
80
|
];
|
|
80
81
|
|
|
81
|
-
// NOTE: This block is only relevant for merchants actively running an AB test.
|
|
82
|
-
// For all other merchants, experiments.variants will be null.
|
|
83
|
-
const { experiments } = state;
|
|
84
|
-
const variant = experiments?.variants?.at(experiments.currentVariant);
|
|
85
|
-
|
|
86
|
-
if (variant) {
|
|
87
|
-
query.push(['variant', variant.public_id]);
|
|
88
|
-
}
|
|
89
|
-
|
|
90
82
|
return [`/offer/${merchantId}/${module}?${toQuery(query)}`];
|
|
91
83
|
})
|
|
92
84
|
),
|
package/src/core/experiments.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { RECEIVE_MERCHANT_SETTINGS, SET_EXPERIMENT_VARIANT } from './constants';
|
|
1
|
+
import { READY, RECEIVE_MERCHANT_SETTINGS, REQUEST_OFFER, SET_EXPERIMENT_VARIANT } from './constants';
|
|
2
2
|
import murmur from 'murmurhash-js';
|
|
3
|
+
import { waitFor } from './waitUntilOffersReady';
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
6
|
* Returns the index of a variant based on the provided key and variants.
|
|
@@ -82,22 +83,37 @@ export function getAssignedExperimentVariant(experimentSettings, sessionId) {
|
|
|
82
83
|
* @return {function} A function that takes the next middleware function and an action as arguments.
|
|
83
84
|
*/
|
|
84
85
|
export function experimentsMiddleware(store) {
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
86
|
+
const [waitForReady, resolveReady] = waitFor();
|
|
87
|
+
|
|
88
|
+
let variant, experimentSettings;
|
|
89
|
+
|
|
90
|
+
return next => async action => {
|
|
91
|
+
if (action.type === READY) {
|
|
92
|
+
resolveReady();
|
|
93
|
+
} else if (action.type === RECEIVE_MERCHANT_SETTINGS) {
|
|
94
|
+
await waitForReady;
|
|
95
|
+
experimentSettings = action.payload.experiments;
|
|
96
|
+
|
|
97
|
+
const { sessionId } = store.getState();
|
|
90
98
|
|
|
91
|
-
|
|
92
|
-
const variant = getAssignedExperimentVariant(experimentSettings, sessionId);
|
|
99
|
+
variant = getAssignedExperimentVariant(experimentSettings, sessionId);
|
|
93
100
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
101
|
+
if (variant) {
|
|
102
|
+
store.dispatch({
|
|
103
|
+
type: SET_EXPERIMENT_VARIANT,
|
|
104
|
+
payload: variant
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
} else if (action.type === REQUEST_OFFER) {
|
|
108
|
+
await waitForReady;
|
|
109
|
+
if (variant) {
|
|
110
|
+
action.payload.searchParams = {
|
|
111
|
+
...action.payload.searchParams,
|
|
112
|
+
variant: variant.public_id
|
|
113
|
+
};
|
|
114
|
+
}
|
|
99
115
|
}
|
|
100
116
|
|
|
101
|
-
return
|
|
117
|
+
return next(action);
|
|
102
118
|
};
|
|
103
119
|
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { fetchDone, fetchResponseError, receiveOffer } from './actions';
|
|
2
|
+
import api from './api';
|
|
3
|
+
import { DEFAULT_OFFER_MODULE, REQUEST_OFFER } from './constants';
|
|
4
|
+
import { safeProductId } from './utils';
|
|
5
|
+
|
|
6
|
+
export function offerRequestMiddleware(store) {
|
|
7
|
+
return next => action => {
|
|
8
|
+
if (action.type === REQUEST_OFFER) {
|
|
9
|
+
const {
|
|
10
|
+
merchantId,
|
|
11
|
+
sessionId,
|
|
12
|
+
environment: { apiUrl }
|
|
13
|
+
} = store.getState();
|
|
14
|
+
|
|
15
|
+
const productId = safeProductId(action.payload.product);
|
|
16
|
+
if (productId)
|
|
17
|
+
api
|
|
18
|
+
.fetchOffer(
|
|
19
|
+
apiUrl,
|
|
20
|
+
merchantId,
|
|
21
|
+
sessionId,
|
|
22
|
+
productId,
|
|
23
|
+
action.payload.module || DEFAULT_OFFER_MODULE,
|
|
24
|
+
action.payload.searchParams
|
|
25
|
+
)
|
|
26
|
+
.then(
|
|
27
|
+
response => store.dispatch(receiveOffer(response, action.payload.offer)),
|
|
28
|
+
err => store.dispatch(fetchResponseError(err))
|
|
29
|
+
)
|
|
30
|
+
.finally(() => store.dispatch(fetchDone(action)));
|
|
31
|
+
}
|
|
32
|
+
return next(action);
|
|
33
|
+
};
|
|
34
|
+
}
|
package/src/core/reducer.js
CHANGED
|
@@ -271,7 +271,7 @@ export const productOffer = (state = {}, action) => {
|
|
|
271
271
|
...state,
|
|
272
272
|
...Object.entries(action.payload.autoship)
|
|
273
273
|
.map(([key]) => ({ [key]: Object.keys(action.payload.modifiers) }))
|
|
274
|
-
.reduce((acc, object) => ({ ...acc, ...object }))
|
|
274
|
+
.reduce((acc, object) => ({ ...acc, ...object }), {})
|
|
275
275
|
};
|
|
276
276
|
case constants.CHECKOUT:
|
|
277
277
|
return {};
|
package/src/core/store.js
CHANGED
|
@@ -4,6 +4,8 @@ import thunk from 'redux-thunk';
|
|
|
4
4
|
import { loadState } from './localStorage';
|
|
5
5
|
import { dispatchMiddleware, localStorageMiddleware, offerEvents } from './middleware';
|
|
6
6
|
import { waitUntilOffersReady } from './waitUntilOffersReady';
|
|
7
|
+
import { offerRequestMiddleware } from './offerRequest';
|
|
8
|
+
import { experimentsMiddleware } from './experiments';
|
|
7
9
|
|
|
8
10
|
export function makeStore(reducer, ...extraMiddlewares) {
|
|
9
11
|
if (window.og && window.og.store) return window.og.store;
|
|
@@ -17,7 +19,14 @@ export function makeStore(reducer, ...extraMiddlewares) {
|
|
|
17
19
|
})
|
|
18
20
|
: compose;
|
|
19
21
|
|
|
20
|
-
const middlewares = [
|
|
22
|
+
const middlewares = [
|
|
23
|
+
waitUntilOffersReady,
|
|
24
|
+
thunk,
|
|
25
|
+
experimentsMiddleware,
|
|
26
|
+
offerRequestMiddleware,
|
|
27
|
+
dispatchMiddleware,
|
|
28
|
+
offerEvents
|
|
29
|
+
];
|
|
21
30
|
|
|
22
31
|
let initial = {};
|
|
23
32
|
|
package/src/index.js
CHANGED
|
@@ -7,12 +7,10 @@ import platform from './platform';
|
|
|
7
7
|
import { autoInitializeOffers, onReady } from './core/utils';
|
|
8
8
|
import { authorizeShopifyCustomer } from './shopify/shopifyBootstrap';
|
|
9
9
|
import shopifyTrackingMiddleware from './shopify/shopifyTrackingMiddleware';
|
|
10
|
-
import { experimentsMiddleware } from './core/experiments';
|
|
11
10
|
|
|
12
11
|
export const store = makeStore(
|
|
13
12
|
...(platform?.shopify_selling_plans ? [shopifyReducer, shopifyMiddleware] : [defaultReducer]),
|
|
14
|
-
platform.shopify && shopifyTrackingMiddleware
|
|
15
|
-
experimentsMiddleware
|
|
13
|
+
platform.shopify && shopifyTrackingMiddleware
|
|
16
14
|
);
|
|
17
15
|
|
|
18
16
|
export const offers = makeApi(store);
|
package/src/make-api.js
CHANGED
|
@@ -146,15 +146,11 @@ export default function makeApi(store) {
|
|
|
146
146
|
} else if (settings.cart && Array.isArray(settings.cart.products)) {
|
|
147
147
|
products = products.concat(settings.cart.products);
|
|
148
148
|
}
|
|
149
|
-
const { apiUrl } = environment({}, actions.setEnvironment(env));
|
|
150
149
|
|
|
151
150
|
const state = storeInstance.getState();
|
|
152
151
|
const { sessionId } = state;
|
|
153
152
|
if (sessionId) {
|
|
154
|
-
products.forEach(product =>
|
|
155
|
-
const id = safeProductId(product);
|
|
156
|
-
api.fetchOffer(apiUrl, merchantId, sessionId, `${id}`, DEFAULT_OFFER_MODULE, state);
|
|
157
|
-
});
|
|
153
|
+
products.forEach(product => storeInstance.dispatch(actions.requestOffer(product)));
|
|
158
154
|
}
|
|
159
155
|
|
|
160
156
|
if (settings.product_discounts && typeof settings.product_discounts === 'object') {
|
|
@@ -345,8 +345,8 @@ export function getSubscribedFrequency(productId, store) {
|
|
|
345
345
|
* @param store
|
|
346
346
|
*/
|
|
347
347
|
function synchronizeSellingPlan(store: any, offerElement?: HTMLElement) {
|
|
348
|
-
if (offerElement
|
|
349
|
-
if (!offerElement
|
|
348
|
+
if (offerElement?.isCart) return; // hidden inputs are used when product page, not cart.
|
|
349
|
+
if (!offerElement?.shouldEnableOffer) return; // do not set a selling plan if we're hiding the offer
|
|
350
350
|
|
|
351
351
|
[...document.querySelectorAll('form[action$="/cart/add"] [name=id]')].forEach((productIdInput: HTMLInputElement) => {
|
|
352
352
|
const productId = productIdInput.value;
|
|
@@ -85,7 +85,7 @@ export const mapExistingOptinsFromOfferResponse = (state, offerEl) =>
|
|
|
85
85
|
mapFrequencyToSellingPlan(
|
|
86
86
|
offerEl?.config?.frequencies,
|
|
87
87
|
offerEl?.config?.frequenciesEveryPeriod,
|
|
88
|
-
offerEl
|
|
88
|
+
offerEl?.defaultFrequency
|
|
89
89
|
) ||
|
|
90
90
|
getFirstSellingPlan(offerEl?.config?.frequencies)
|
|
91
91
|
: it.frequency
|
|
@@ -103,7 +103,7 @@ export const reduceNewOptinsFromOfferResponse = (
|
|
|
103
103
|
Object.keys(autoship).reduce((acc, id) => {
|
|
104
104
|
if (!existingOptins.some(it => it.id === id)) {
|
|
105
105
|
if (!(autoship[id] && autoship_by_default[id] && in_stock[id])) return acc;
|
|
106
|
-
const { config: { frequencies: sellingPlans, frequenciesEveryPeriod } = {}, defaultFrequency } = offerEl;
|
|
106
|
+
const { config: { frequencies: sellingPlans, frequenciesEveryPeriod } = {}, defaultFrequency } = offerEl || {};
|
|
107
107
|
const psdf = default_frequencies[id];
|
|
108
108
|
let frequency;
|
|
109
109
|
|
|
@@ -314,7 +314,7 @@ export const config = (
|
|
|
314
314
|
defaultFrequency,
|
|
315
315
|
config: { frequencies: sellingPlans, frequenciesEveryPeriod, prepaidSellingPlans = {} } = {},
|
|
316
316
|
product
|
|
317
|
-
} = offerEl;
|
|
317
|
+
} = offerEl || {};
|
|
318
318
|
|
|
319
319
|
// We don't want to be setting the default frequency to a prepaid selling plan
|
|
320
320
|
if (prepaidSellingPlans[product?.id]?.some(({ sellingPlan }) => sellingPlan === defaultFrequency)) {
|
|
@@ -381,10 +381,10 @@ export const offer = (state = {}, _action) => state;
|
|
|
381
381
|
function getFrequencyForPrepaidShipments({ prepaidShipments, offer: offerEl, product }) {
|
|
382
382
|
if (prepaidShipments) {
|
|
383
383
|
const productId = safeProductId(product.id);
|
|
384
|
-
const plan = offerEl
|
|
384
|
+
const plan = offerEl?.config.prepaidSellingPlans[productId]?.find(p => p.numberShipments === prepaidShipments);
|
|
385
385
|
return plan ? plan.sellingPlan : null;
|
|
386
386
|
}
|
|
387
|
-
return offerEl
|
|
387
|
+
return offerEl?.config.frequencies[0];
|
|
388
388
|
}
|
|
389
389
|
|
|
390
390
|
function getOptedInItem(cartItem) {
|