@flagship.io/react-sdk 2.2.0-beta.5 → 3.0.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/README.md CHANGED
@@ -7,8 +7,8 @@
7
7
 
8
8
  ## Docs
9
9
 
10
- - coming soon
10
+ - [docs](https://docs.developers.flagship.io/docs/react-v3-0-x)
11
11
 
12
12
  ## Licence
13
13
 
14
- [Apache License.](https://github.com/abtasty/flagship-react-sdk/blob/master/LICENSE)
14
+ [Apache License.](https://github.com/flagship-io/flagship-react-sdk/blob/master/LICENSE)
@@ -0,0 +1,36 @@
1
+ import { FlagMetadata, LogLevel } from '@flagship.io/js-sdk';
2
+ import { noVisitorMessage } from './constants';
3
+ import { log } from './utils';
4
+ export class Flag {
5
+ constructor(defaultValue) {
6
+ log(LogLevel.WARNING, noVisitorMessage, 'GetFlag');
7
+ this._defaultValue = defaultValue;
8
+ }
9
+
10
+ getValue() {
11
+ log(LogLevel.WARNING, noVisitorMessage, 'getValue');
12
+ return this._defaultValue;
13
+ }
14
+
15
+ exists() {
16
+ log(LogLevel.WARNING, noVisitorMessage, 'exists');
17
+ return false;
18
+ }
19
+
20
+ userExposed() {
21
+ log(LogLevel.WARNING, noVisitorMessage, 'userExposed');
22
+ return Promise.resolve();
23
+ }
24
+
25
+ get metadata() {
26
+ log(LogLevel.WARNING, noVisitorMessage, 'metadata');
27
+ return new FlagMetadata({
28
+ campaignId: '',
29
+ campaignType: '',
30
+ isReference: false,
31
+ variationGroupId: '',
32
+ variationId: ''
33
+ });
34
+ }
35
+
36
+ }
@@ -1,7 +1,7 @@
1
1
  // eslint-disable-next-line no-use-before-define
2
- import React, { useState, useEffect, createContext, useRef } from 'react';
3
- import { Flagship, FlagshipStatus } from '@flagship.io/js-sdk';
4
- import { getModificationsFromCampaigns, logError } from './utils';
2
+ import React, { useState, useEffect, createContext, useRef } from "react";
3
+ import { Flagship, FlagshipStatus } from "@flagship.io/js-sdk";
4
+ import { getModificationsFromCampaigns, logError, useNonInitialEffect } from "./utils";
5
5
  const initStat = {
6
6
  status: {
7
7
  isLoading: true,
@@ -36,13 +36,21 @@ export const FlagshipProvider = ({
36
36
  initialBucketing,
37
37
  initialCampaigns,
38
38
  initialModifications,
39
+ initialFlagsData,
39
40
  synchronizeOnBucketingUpdated,
40
41
  activateDeduplicationTime,
41
- hitDeduplicationTime
42
+ hitDeduplicationTime,
43
+ visitorCacheImplementation,
44
+ hitCacheImplementation,
45
+ disableCache
42
46
  }) => {
43
47
  let modifications = new Map();
44
48
 
45
- if (initialModifications) {
49
+ if (initialFlagsData && initialFlagsData.forEach) {
50
+ initialFlagsData.forEach(flag => {
51
+ modifications.set(flag.key, flag);
52
+ });
53
+ } else if (initialModifications && initialModifications.forEach) {
46
54
  initialModifications.forEach(modification => {
47
55
  modifications.set(modification.key, modification);
48
56
  });
@@ -56,47 +64,20 @@ export const FlagshipProvider = ({
56
64
  const [lastModified, setLastModified] = useState();
57
65
  const stateRef = useRef();
58
66
  stateRef.current = state;
59
- useEffect(() => {
67
+ useNonInitialEffect(() => {
60
68
  if (synchronizeOnBucketingUpdated) {
61
69
  var _state$visitor;
62
70
 
63
- (_state$visitor = state.visitor) === null || _state$visitor === void 0 ? void 0 : _state$visitor.synchronizeModifications();
71
+ (_state$visitor = state.visitor) === null || _state$visitor === void 0 ? void 0 : _state$visitor.fetchFlags();
64
72
  }
65
73
  }, [lastModified]);
66
- useEffect(() => {
67
- updateVisitor();
74
+ useNonInitialEffect(() => {
75
+ createVisitor(true);
68
76
  }, [JSON.stringify(visitorData)]);
69
77
  useEffect(() => {
70
78
  initSdk();
71
79
  }, [envId, apiKey, decisionMode]);
72
80
 
73
- const updateVisitor = () => {
74
- if (!state.visitor) {
75
- return;
76
- }
77
-
78
- if (visitorData.context) {
79
- state.visitor.clearContext();
80
- state.visitor.updateContext(visitorData.context);
81
- }
82
-
83
- if (typeof visitorData.hasConsented === 'boolean') {
84
- state.visitor.setConsent(visitorData.hasConsented);
85
- }
86
-
87
- if (state.visitor.anonymousId && !visitorData.isAuthenticated) {
88
- state.visitor.unauthenticate();
89
- } else if (!state.visitor.anonymousId && visitorData.isAuthenticated) {
90
- state.visitor.authenticate(visitorData.id);
91
- }
92
-
93
- if (visitorData.id) {
94
- state.visitor.visitorId = visitorData.id;
95
- }
96
-
97
- state.visitor.synchronizeModifications();
98
- };
99
-
100
81
  function initializeState(param) {
101
82
  const newStatus = {
102
83
  isSdkReady: param.isSdkReady,
@@ -124,7 +105,7 @@ export const FlagshipProvider = ({
124
105
 
125
106
  const onVisitorReady = (fsVisitor, error) => {
126
107
  if (error) {
127
- logError(Flagship.getConfig(), error.message || error, 'onReady');
108
+ logError(Flagship.getConfig(), error.message || error, "onReady");
128
109
  return;
129
110
  }
130
111
 
@@ -143,6 +124,37 @@ export const FlagshipProvider = ({
143
124
  }
144
125
  };
145
126
 
127
+ const createVisitor = (fetchFlags = false) => {
128
+ if (!visitorData) {
129
+ return;
130
+ }
131
+
132
+ const fsVisitor = Flagship.newVisitor({
133
+ visitorId: visitorData.id,
134
+ context: visitorData.context,
135
+ isAuthenticated: visitorData.isAuthenticated,
136
+ hasConsented: visitorData.hasConsented,
137
+ initialCampaigns,
138
+ initialModifications,
139
+ initialFlagsData
140
+ });
141
+ fsVisitor === null || fsVisitor === void 0 ? void 0 : fsVisitor.on("ready", error => {
142
+ onVisitorReady(fsVisitor, error);
143
+ });
144
+
145
+ if (!fetchNow && fsVisitor && !fetchFlags) {
146
+ initializeState({
147
+ fsVisitor,
148
+ isSdkReady: true,
149
+ isLoading: false
150
+ });
151
+ }
152
+
153
+ if (fetchFlags && !fetchNow) {
154
+ fsVisitor === null || fsVisitor === void 0 ? void 0 : fsVisitor.fetchFlags();
155
+ }
156
+ };
157
+
146
158
  const statusChanged = status => {
147
159
  if (statusChangedCallback) {
148
160
  statusChangedCallback(status);
@@ -150,38 +162,15 @@ export const FlagshipProvider = ({
150
162
 
151
163
  if (status === FlagshipStatus.STARTING && onInitStart) {
152
164
  onInitStart();
153
- } else if (status === FlagshipStatus.READY_PANIC_ON || status === FlagshipStatus.READY) {
154
- var _stateRef$current;
165
+ return;
166
+ }
155
167
 
168
+ if (status === FlagshipStatus.READY_PANIC_ON || status === FlagshipStatus.READY) {
156
169
  if (onInitDone) {
157
170
  onInitDone();
158
171
  }
159
172
 
160
- if ((_stateRef$current = stateRef.current) !== null && _stateRef$current !== void 0 && _stateRef$current.visitor) {
161
- var _stateRef$current2;
162
-
163
- (_stateRef$current2 = stateRef.current) === null || _stateRef$current2 === void 0 ? void 0 : _stateRef$current2.visitor.synchronizeModifications();
164
- } else {
165
- const fsVisitor = Flagship.newVisitor({
166
- visitorId: visitorData.id,
167
- context: visitorData.context,
168
- isAuthenticated: visitorData.isAuthenticated,
169
- hasConsented: visitorData.hasConsented,
170
- initialCampaigns,
171
- initialModifications
172
- });
173
- fsVisitor === null || fsVisitor === void 0 ? void 0 : fsVisitor.on('ready', error => {
174
- onVisitorReady(fsVisitor, error);
175
- });
176
-
177
- if (!fetchNow && fsVisitor) {
178
- initializeState({
179
- fsVisitor,
180
- isSdkReady: true,
181
- isLoading: false
182
- });
183
- }
184
- }
173
+ createVisitor();
185
174
  }
186
175
  };
187
176
 
@@ -209,14 +198,18 @@ export const FlagshipProvider = ({
209
198
  onBucketingUpdated: onBucketingLastModified,
210
199
  initialBucketing,
211
200
  activateDeduplicationTime,
212
- hitDeduplicationTime
201
+ hitDeduplicationTime,
202
+ visitorCacheImplementation,
203
+ hitCacheImplementation,
204
+ disableCache,
205
+ language: 1
213
206
  });
214
207
  };
215
208
 
216
209
  const handleDisplay = () => {
217
210
  const isFirstInit = !state.visitor;
218
211
 
219
- if (state.status.isLoading && loadingComponent && isFirstInit) {
212
+ if (state.status.isLoading && loadingComponent && isFirstInit && fetchNow) {
220
213
  return /*#__PURE__*/React.createElement(React.Fragment, null, loadingComponent);
221
214
  }
222
215
 
@@ -231,7 +224,6 @@ export const FlagshipProvider = ({
231
224
  }, handleDisplay());
232
225
  };
233
226
  FlagshipProvider.defaultProps = {
234
- nodeEnv: 'production',
235
227
  activateDeduplicationTime: 10,
236
228
  hitDeduplicationTime: 10
237
229
  };
@@ -1,6 +1,8 @@
1
1
  import { useContext } from 'react';
2
2
  import { FlagshipContext } from './FlagshipContext';
3
3
  import { logError, logWarn } from './utils';
4
+ import { Flag } from './Flag';
5
+ import { noVisitorDefault, noVisitorMessage } from './constants';
4
6
 
5
7
  const checkType = (value, defaultValue) => typeof value === 'object' && typeof defaultValue === 'object' && Array.isArray(value) === Array.isArray(defaultValue) || typeof value === typeof defaultValue;
6
8
 
@@ -44,6 +46,7 @@ const fsModificationsSync = args => {
44
46
  };
45
47
  /**
46
48
  * Retrieve a modification value by its key. If no modification match the given key or if the stored value type and default value type do not match, default value will be returned.
49
+ * @deprecated use useFsGetFlag instead
47
50
  */
48
51
 
49
52
 
@@ -67,6 +70,7 @@ export const useFsModifications = (params, activateAll) => {
67
70
  };
68
71
  /**
69
72
  * Retrieve a modification value by its key. If no modification match the given key or if the stored value type and default value type do not match, default value will be returned.
73
+ * @deprecated use useFsGetFlag instead
70
74
  */
71
75
 
72
76
  export const useFsModification = params => {
@@ -119,6 +123,7 @@ const fsModificationInfoSync = args => {
119
123
  /**
120
124
  * Get the campaign modification information value matching the given key.
121
125
  * @param {string} key key which identify the modification.
126
+ * @deprecated use useFsGetFlag instead
122
127
  */
123
128
 
124
129
 
@@ -136,54 +141,47 @@ export const useFsModificationInfo = key => {
136
141
  });
137
142
  };
138
143
 
139
- const fsSynchronizeModifications = async (functionName, visitor, config) => {
144
+ const fsActivate = async (params, functionName, visitor, config) => {
140
145
  try {
141
146
  if (!visitor) {
142
- reportNoVisitor(config, functionName);
147
+ logWarn(config, noVisitorMessage, functionName);
143
148
  return;
144
149
  }
145
150
 
146
- await visitor.synchronizeModifications(); // eslint-disable-next-line @typescript-eslint/no-explicit-any
151
+ await visitor.activateModifications(params); // eslint-disable-next-line @typescript-eslint/no-explicit-any
147
152
  } catch (error) {
148
- logError(config, error.message || error, functionName);
153
+ logWarn(config, error.message || error, functionName);
149
154
  }
150
155
  };
151
156
  /**
152
- * This function calls the decision api and update all the campaigns modifications from the server according to the visitor context.
153
- */
157
+ * This hook returns a flag object by its key. If no flag match the given key an empty flag will be returned.
158
+ * @param key
159
+ * @param defaultValue
160
+ * @returns
161
+ */
154
162
 
155
163
 
156
- export const useFsSynchronizeModifications = async () => {
164
+ export const useFsFlag = (key, defaultValue) => {
157
165
  const {
158
166
  state
159
167
  } = useContext(FlagshipContext);
160
168
  const {
161
- visitor,
162
- config
169
+ visitor
163
170
  } = state;
164
- const functionName = 'useFsSynchronizeModifications';
165
- await fsSynchronizeModifications(functionName, visitor, config);
166
- };
167
171
 
168
- const fsActivate = async (params, functionName, visitor, config) => {
169
- try {
170
- if (!visitor) {
171
- logWarn(config, noVisitorMessage, functionName);
172
- return;
173
- }
174
-
175
- await visitor.activateModifications(params); // eslint-disable-next-line @typescript-eslint/no-explicit-any
176
- } catch (error) {
177
- logWarn(config, error.message || error, functionName);
172
+ if (!visitor) {
173
+ return new Flag(defaultValue);
178
174
  }
175
+
176
+ return visitor.getFlag(key, defaultValue);
179
177
  };
180
178
  /**
181
179
  * Report this user has seen this modification. Report this user has seen these modifications.
182
180
  * @param params
181
+ * @deprecated use useFsGetFlag instead
183
182
  * @returns
184
183
  */
185
184
 
186
-
187
185
  export const useFsActivate = async params => {
188
186
  const {
189
187
  state
@@ -249,8 +247,8 @@ export const useFlagship = () => {
249
247
  visitor.unauthenticate();
250
248
  };
251
249
  /**
252
- * Send a Hit to Flagship servers for reporting.
253
- */
250
+ * Send a Hit to Flagship servers for reporting.
251
+ */
254
252
 
255
253
 
256
254
  const fsSendHit = hit => {
@@ -264,8 +262,8 @@ export const useFlagship = () => {
264
262
  return visitor.sendHit(hit);
265
263
  };
266
264
  /**
267
- * Send a Hit to Flagship servers for reporting.
268
- */
265
+ * Send a Hit to Flagship servers for reporting.
266
+ */
269
267
 
270
268
 
271
269
  const fsSendHits = hit => {
@@ -292,8 +290,12 @@ export const useFlagship = () => {
292
290
  };
293
291
 
294
292
  const synchronizeModifications = async () => {
295
- const functionName = 'synchronizeModifications';
296
- await fsSynchronizeModifications(functionName, visitor, config);
293
+ if (!visitor) {
294
+ logWarn(config, noVisitorMessage, 'synchronizeModifications');
295
+ return;
296
+ }
297
+
298
+ await visitor.synchronizeModifications();
297
299
  };
298
300
 
299
301
  const getModifications = (params, activateAll) => {
@@ -316,7 +318,39 @@ export const useFlagship = () => {
316
318
  });
317
319
  };
318
320
 
321
+ function getFlag(key, defaultValue) {
322
+ if (!visitor) {
323
+ return new Flag(defaultValue);
324
+ }
325
+
326
+ return visitor.getFlag(key, defaultValue);
327
+ }
328
+
329
+ function fetchFlags() {
330
+ if (!visitor) {
331
+ logWarn(config, noVisitorMessage, 'fetchFlags');
332
+ return Promise.resolve();
333
+ }
334
+
335
+ return visitor.fetchFlags();
336
+ }
337
+
338
+ function setConsent(hasConsented) {
339
+ if (!visitor) {
340
+ logWarn(config, noVisitorMessage, 'setConsent');
341
+ return;
342
+ }
343
+
344
+ visitor.setConsent(hasConsented);
345
+ }
346
+
319
347
  return {
348
+ visitorId: visitor === null || visitor === void 0 ? void 0 : visitor.visitorId,
349
+ anonymousId: visitor === null || visitor === void 0 ? void 0 : visitor.anonymousId,
350
+ context: { ...(visitor === null || visitor === void 0 ? void 0 : visitor.context)
351
+ },
352
+ hasConsented: visitor === null || visitor === void 0 ? void 0 : visitor.hasConsented,
353
+ setConsent,
320
354
  updateContext: fsUpdateContext,
321
355
  clearContext: fsClearContext,
322
356
  authenticate: fsAuthenticate,
@@ -326,16 +360,13 @@ export const useFlagship = () => {
326
360
  synchronizeModifications,
327
361
  getModifications,
328
362
  modifications: modifications || [],
363
+ FlagsData: (visitor === null || visitor === void 0 ? void 0 : visitor.getFlagsDataArray()) || [],
329
364
  getModificationInfo,
330
365
  hit: {
331
366
  send: fsSendHit,
332
367
  sendMultiple: fsSendHits
333
- }
368
+ },
369
+ getFlag,
370
+ fetchFlags
334
371
  };
335
- };
336
- export const noVisitorMessage = 'sdk not correctly initialized... Make sure fsVisitor is ready.';
337
- export const noVisitorDefault = 'fsVisitor not initialized, returns default value';
338
-
339
- const reportNoVisitor = (config, tag) => {
340
- logError(config, noVisitorMessage, tag);
341
372
  };
@@ -0,0 +1,2 @@
1
+ export const noVisitorMessage = 'flagship Visitor not initialized.';
2
+ export const noVisitorDefault = 'fsVisitor not initialized, returns default value';
package/dist/es/utils.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import { LogLevel } from '@flagship.io/js-sdk';
2
+ import { useEffect, useRef } from 'react';
2
3
  export function logError(config, message, tag) {
3
4
  if (!config || !config.logManager || typeof config.logManager.error !== 'function' || !config.logLevel || config.logLevel < LogLevel.ERROR) {
4
5
  return;
@@ -20,6 +21,16 @@ export function logWarn(config, message, tag) {
20
21
 
21
22
  config.logManager.warning(message, tag);
22
23
  }
24
+ export function log(level, message, tag) {
25
+ const now = new Date(); // eslint-disable-next-line @typescript-eslint/no-explicit-any
26
+
27
+ const getTwoDigit = value => {
28
+ return value.toString().length === 1 ? `0${value}` : value;
29
+ };
30
+
31
+ const out = `[${getTwoDigit(now.getFullYear())}-${getTwoDigit(now.getMonth())}-${getTwoDigit(now.getDay())} ${getTwoDigit(now.getHours())}:${getTwoDigit(now.getMinutes())}] [Flagship SDK] [${LogLevel[level]}] [${tag}] : ${message}`;
32
+ console.log(out);
33
+ }
23
34
  export const getModificationsFromCampaigns = campaigns => {
24
35
  const modifications = new Map();
25
36
 
@@ -43,4 +54,24 @@ export const getModificationsFromCampaigns = campaigns => {
43
54
  }
44
55
  });
45
56
  return modifications;
46
- };
57
+ };
58
+ export function uuidV4() {
59
+ return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (char) {
60
+ const rand = Math.random() * 16 | 0;
61
+ const value = char === 'x' ? rand : rand & 0x3 | 0x8;
62
+ return value.toString(16);
63
+ });
64
+ }
65
+ export function useNonInitialEffect(effect, deps) {
66
+ const initialRender = useRef(true);
67
+ useEffect(() => {
68
+ if (initialRender.current) {
69
+ initialRender.current = false;
70
+ return;
71
+ }
72
+
73
+ if (typeof effect === 'function') {
74
+ return effect();
75
+ }
76
+ }, deps);
77
+ }
@@ -0,0 +1,55 @@
1
+ function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
2
+
3
+ function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }
4
+
5
+ function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }
6
+
7
+ function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
8
+
9
+ import { FlagMetadata, LogLevel } from '@flagship.io/js-sdk';
10
+ import { noVisitorMessage } from './constants';
11
+ import { log } from './utils';
12
+ export var Flag = /*#__PURE__*/function () {
13
+ function Flag(defaultValue) {
14
+ _classCallCheck(this, Flag);
15
+
16
+ _defineProperty(this, "_defaultValue", void 0);
17
+
18
+ log(LogLevel.WARNING, noVisitorMessage, 'GetFlag');
19
+ this._defaultValue = defaultValue;
20
+ }
21
+
22
+ _createClass(Flag, [{
23
+ key: "getValue",
24
+ value: function getValue() {
25
+ log(LogLevel.WARNING, noVisitorMessage, 'getValue');
26
+ return this._defaultValue;
27
+ }
28
+ }, {
29
+ key: "exists",
30
+ value: function exists() {
31
+ log(LogLevel.WARNING, noVisitorMessage, 'exists');
32
+ return false;
33
+ }
34
+ }, {
35
+ key: "userExposed",
36
+ value: function userExposed() {
37
+ log(LogLevel.WARNING, noVisitorMessage, 'userExposed');
38
+ return Promise.resolve();
39
+ }
40
+ }, {
41
+ key: "metadata",
42
+ get: function get() {
43
+ log(LogLevel.WARNING, noVisitorMessage, 'metadata');
44
+ return new FlagMetadata({
45
+ campaignId: '',
46
+ campaignType: '',
47
+ isReference: false,
48
+ variationGroupId: '',
49
+ variationId: ''
50
+ });
51
+ }
52
+ }]);
53
+
54
+ return Flag;
55
+ }();