@flagship.io/react-sdk 2.2.0-beta.6 → 3.0.2

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,6 +36,7 @@ export const FlagshipProvider = ({
36
36
  initialBucketing,
37
37
  initialCampaigns,
38
38
  initialModifications,
39
+ initialFlagsData,
39
40
  synchronizeOnBucketingUpdated,
40
41
  activateDeduplicationTime,
41
42
  hitDeduplicationTime,
@@ -45,7 +46,11 @@ export const FlagshipProvider = ({
45
46
  }) => {
46
47
  let modifications = new Map();
47
48
 
48
- if (initialModifications) {
49
+ if (initialFlagsData && initialFlagsData.forEach) {
50
+ initialFlagsData.forEach(flag => {
51
+ modifications.set(flag.key, flag);
52
+ });
53
+ } else if (initialModifications && initialModifications.forEach) {
49
54
  initialModifications.forEach(modification => {
50
55
  modifications.set(modification.key, modification);
51
56
  });
@@ -59,47 +64,20 @@ export const FlagshipProvider = ({
59
64
  const [lastModified, setLastModified] = useState();
60
65
  const stateRef = useRef();
61
66
  stateRef.current = state;
62
- useEffect(() => {
67
+ useNonInitialEffect(() => {
63
68
  if (synchronizeOnBucketingUpdated) {
64
69
  var _state$visitor;
65
70
 
66
- (_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();
67
72
  }
68
73
  }, [lastModified]);
69
- useEffect(() => {
70
- updateVisitor();
74
+ useNonInitialEffect(() => {
75
+ createVisitor(true);
71
76
  }, [JSON.stringify(visitorData)]);
72
77
  useEffect(() => {
73
78
  initSdk();
74
79
  }, [envId, apiKey, decisionMode]);
75
80
 
76
- const updateVisitor = () => {
77
- if (!state.visitor) {
78
- return;
79
- }
80
-
81
- if (visitorData.context) {
82
- state.visitor.clearContext();
83
- state.visitor.updateContext(visitorData.context);
84
- }
85
-
86
- if (typeof visitorData.hasConsented === 'boolean') {
87
- state.visitor.setConsent(visitorData.hasConsented);
88
- }
89
-
90
- if (state.visitor.anonymousId && !visitorData.isAuthenticated) {
91
- state.visitor.unauthenticate();
92
- } else if (!state.visitor.anonymousId && visitorData.isAuthenticated) {
93
- state.visitor.authenticate(visitorData.id);
94
- }
95
-
96
- if (visitorData.id) {
97
- state.visitor.visitorId = visitorData.id;
98
- }
99
-
100
- state.visitor.synchronizeModifications();
101
- };
102
-
103
81
  function initializeState(param) {
104
82
  const newStatus = {
105
83
  isSdkReady: param.isSdkReady,
@@ -127,7 +105,7 @@ export const FlagshipProvider = ({
127
105
 
128
106
  const onVisitorReady = (fsVisitor, error) => {
129
107
  if (error) {
130
- logError(Flagship.getConfig(), error.message || error, 'onReady');
108
+ logError(Flagship.getConfig(), error.message || error, "onReady");
131
109
  return;
132
110
  }
133
111
 
@@ -146,6 +124,37 @@ export const FlagshipProvider = ({
146
124
  }
147
125
  };
148
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
+
149
158
  const statusChanged = status => {
150
159
  if (statusChangedCallback) {
151
160
  statusChangedCallback(status);
@@ -153,38 +162,15 @@ export const FlagshipProvider = ({
153
162
 
154
163
  if (status === FlagshipStatus.STARTING && onInitStart) {
155
164
  onInitStart();
156
- } else if (status === FlagshipStatus.READY_PANIC_ON || status === FlagshipStatus.READY) {
157
- var _stateRef$current;
165
+ return;
166
+ }
158
167
 
168
+ if (status === FlagshipStatus.READY_PANIC_ON || status === FlagshipStatus.READY) {
159
169
  if (onInitDone) {
160
170
  onInitDone();
161
171
  }
162
172
 
163
- if ((_stateRef$current = stateRef.current) !== null && _stateRef$current !== void 0 && _stateRef$current.visitor) {
164
- var _stateRef$current2;
165
-
166
- (_stateRef$current2 = stateRef.current) === null || _stateRef$current2 === void 0 ? void 0 : _stateRef$current2.visitor.synchronizeModifications();
167
- } else {
168
- const fsVisitor = Flagship.newVisitor({
169
- visitorId: visitorData.id,
170
- context: visitorData.context,
171
- isAuthenticated: visitorData.isAuthenticated,
172
- hasConsented: visitorData.hasConsented,
173
- initialCampaigns,
174
- initialModifications
175
- });
176
- fsVisitor === null || fsVisitor === void 0 ? void 0 : fsVisitor.on('ready', error => {
177
- onVisitorReady(fsVisitor, error);
178
- });
179
-
180
- if (!fetchNow && fsVisitor) {
181
- initializeState({
182
- fsVisitor,
183
- isSdkReady: true,
184
- isLoading: false
185
- });
186
- }
187
- }
173
+ createVisitor();
188
174
  }
189
175
  };
190
176
 
@@ -215,14 +201,15 @@ export const FlagshipProvider = ({
215
201
  hitDeduplicationTime,
216
202
  visitorCacheImplementation,
217
203
  hitCacheImplementation,
218
- disableCache
204
+ disableCache,
205
+ language: 1
219
206
  });
220
207
  };
221
208
 
222
209
  const handleDisplay = () => {
223
210
  const isFirstInit = !state.visitor;
224
211
 
225
- if (state.status.isLoading && loadingComponent && isFirstInit) {
212
+ if (state.status.isLoading && loadingComponent && isFirstInit && fetchNow) {
226
213
  return /*#__PURE__*/React.createElement(React.Fragment, null, loadingComponent);
227
214
  }
228
215
 
@@ -237,7 +224,6 @@ export const FlagshipProvider = ({
237
224
  }, handleDisplay());
238
225
  };
239
226
  FlagshipProvider.defaultProps = {
240
- nodeEnv: 'production',
241
227
  activateDeduplicationTime: 10,
242
228
  hitDeduplicationTime: 10
243
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
+ }();