@ledgerhq/live-config 1.0.0-next.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.
Files changed (136) hide show
  1. package/.eslintignore +2 -0
  2. package/.eslintrc.js +20 -0
  3. package/.turbo/turbo-build.log +4 -0
  4. package/.unimportedrc.json +5 -0
  5. package/CHANGELOG.md +14 -0
  6. package/LICENSE.txt +21 -0
  7. package/jest.config.js +10 -0
  8. package/lib/featureFlags/FeatureFlagsContext.d.ts +41 -0
  9. package/lib/featureFlags/FeatureFlagsContext.d.ts.map +1 -0
  10. package/lib/featureFlags/FeatureFlagsContext.js +26 -0
  11. package/lib/featureFlags/FeatureFlagsContext.js.map +1 -0
  12. package/lib/featureFlags/FeatureFlagsContext.test.d.ts +2 -0
  13. package/lib/featureFlags/FeatureFlagsContext.test.d.ts.map +1 -0
  14. package/lib/featureFlags/FeatureFlagsContext.test.js +15 -0
  15. package/lib/featureFlags/FeatureFlagsContext.test.js.map +1 -0
  16. package/lib/featureFlags/FeatureToggle.d.ts +10 -0
  17. package/lib/featureFlags/FeatureToggle.d.ts.map +1 -0
  18. package/lib/featureFlags/FeatureToggle.js +16 -0
  19. package/lib/featureFlags/FeatureToggle.js.map +1 -0
  20. package/lib/featureFlags/LiveConfig.d.ts +25 -0
  21. package/lib/featureFlags/LiveConfig.d.ts.map +1 -0
  22. package/lib/featureFlags/LiveConfig.js +30 -0
  23. package/lib/featureFlags/LiveConfig.js.map +1 -0
  24. package/lib/featureFlags/defaultFeatures.d.ts +61 -0
  25. package/lib/featureFlags/defaultFeatures.d.ts.map +1 -0
  26. package/lib/featureFlags/defaultFeatures.js +319 -0
  27. package/lib/featureFlags/defaultFeatures.js.map +1 -0
  28. package/lib/featureFlags/firebaseFeatureFlags.d.ts +109 -0
  29. package/lib/featureFlags/firebaseFeatureFlags.d.ts.map +1 -0
  30. package/lib/featureFlags/firebaseFeatureFlags.js +90 -0
  31. package/lib/featureFlags/firebaseFeatureFlags.js.map +1 -0
  32. package/lib/featureFlags/groupedFeatures.d.ts +6 -0
  33. package/lib/featureFlags/groupedFeatures.d.ts.map +1 -0
  34. package/lib/featureFlags/groupedFeatures.js +22 -0
  35. package/lib/featureFlags/groupedFeatures.js.map +1 -0
  36. package/lib/featureFlags/helper.d.ts +2 -0
  37. package/lib/featureFlags/helper.d.ts.map +1 -0
  38. package/lib/featureFlags/helper.js +10 -0
  39. package/lib/featureFlags/helper.js.map +1 -0
  40. package/lib/featureFlags/index.d.ts +11 -0
  41. package/lib/featureFlags/index.d.ts.map +1 -0
  42. package/lib/featureFlags/index.js +32 -0
  43. package/lib/featureFlags/index.js.map +1 -0
  44. package/lib/featureFlags/mock.d.ts +11 -0
  45. package/lib/featureFlags/mock.d.ts.map +1 -0
  46. package/lib/featureFlags/mock.js +25 -0
  47. package/lib/featureFlags/mock.js.map +1 -0
  48. package/lib/featureFlags/useFeature.d.ts +14 -0
  49. package/lib/featureFlags/useFeature.d.ts.map +1 -0
  50. package/lib/featureFlags/useFeature.js +21 -0
  51. package/lib/featureFlags/useFeature.js.map +1 -0
  52. package/lib/featureFlags/useFeature.test.d.ts +2 -0
  53. package/lib/featureFlags/useFeature.test.d.ts.map +1 -0
  54. package/lib/featureFlags/useFeature.test.js +27 -0
  55. package/lib/featureFlags/useFeature.test.js.map +1 -0
  56. package/lib/featureFlags/useHasOverriddenFeatureFlags.d.ts +6 -0
  57. package/lib/featureFlags/useHasOverriddenFeatureFlags.d.ts.map +1 -0
  58. package/lib/featureFlags/useHasOverriddenFeatureFlags.js +24 -0
  59. package/lib/featureFlags/useHasOverriddenFeatureFlags.js.map +1 -0
  60. package/lib/index.d.ts +2 -0
  61. package/lib/index.d.ts.map +1 -0
  62. package/lib/index.js +18 -0
  63. package/lib/index.js.map +1 -0
  64. package/lib-es/featureFlags/FeatureFlagsContext.d.ts +41 -0
  65. package/lib-es/featureFlags/FeatureFlagsContext.d.ts.map +1 -0
  66. package/lib-es/featureFlags/FeatureFlagsContext.js +22 -0
  67. package/lib-es/featureFlags/FeatureFlagsContext.js.map +1 -0
  68. package/lib-es/featureFlags/FeatureFlagsContext.test.d.ts +2 -0
  69. package/lib-es/featureFlags/FeatureFlagsContext.test.d.ts.map +1 -0
  70. package/lib-es/featureFlags/FeatureFlagsContext.test.js +13 -0
  71. package/lib-es/featureFlags/FeatureFlagsContext.test.js.map +1 -0
  72. package/lib-es/featureFlags/FeatureToggle.d.ts +10 -0
  73. package/lib-es/featureFlags/FeatureToggle.d.ts.map +1 -0
  74. package/lib-es/featureFlags/FeatureToggle.js +11 -0
  75. package/lib-es/featureFlags/FeatureToggle.js.map +1 -0
  76. package/lib-es/featureFlags/LiveConfig.d.ts +25 -0
  77. package/lib-es/featureFlags/LiveConfig.d.ts.map +1 -0
  78. package/lib-es/featureFlags/LiveConfig.js +26 -0
  79. package/lib-es/featureFlags/LiveConfig.js.map +1 -0
  80. package/lib-es/featureFlags/defaultFeatures.d.ts +61 -0
  81. package/lib-es/featureFlags/defaultFeatures.d.ts.map +1 -0
  82. package/lib-es/featureFlags/defaultFeatures.js +314 -0
  83. package/lib-es/featureFlags/defaultFeatures.js.map +1 -0
  84. package/lib-es/featureFlags/firebaseFeatureFlags.d.ts +109 -0
  85. package/lib-es/featureFlags/firebaseFeatureFlags.d.ts.map +1 -0
  86. package/lib-es/featureFlags/firebaseFeatureFlags.js +80 -0
  87. package/lib-es/featureFlags/firebaseFeatureFlags.js.map +1 -0
  88. package/lib-es/featureFlags/groupedFeatures.d.ts +6 -0
  89. package/lib-es/featureFlags/groupedFeatures.d.ts.map +1 -0
  90. package/lib-es/featureFlags/groupedFeatures.js +19 -0
  91. package/lib-es/featureFlags/groupedFeatures.js.map +1 -0
  92. package/lib-es/featureFlags/helper.d.ts +2 -0
  93. package/lib-es/featureFlags/helper.d.ts.map +1 -0
  94. package/lib-es/featureFlags/helper.js +6 -0
  95. package/lib-es/featureFlags/helper.js.map +1 -0
  96. package/lib-es/featureFlags/index.d.ts +11 -0
  97. package/lib-es/featureFlags/index.d.ts.map +1 -0
  98. package/lib-es/featureFlags/index.js +11 -0
  99. package/lib-es/featureFlags/index.js.map +1 -0
  100. package/lib-es/featureFlags/mock.d.ts +11 -0
  101. package/lib-es/featureFlags/mock.d.ts.map +1 -0
  102. package/lib-es/featureFlags/mock.js +17 -0
  103. package/lib-es/featureFlags/mock.js.map +1 -0
  104. package/lib-es/featureFlags/useFeature.d.ts +14 -0
  105. package/lib-es/featureFlags/useFeature.d.ts.map +1 -0
  106. package/lib-es/featureFlags/useFeature.js +19 -0
  107. package/lib-es/featureFlags/useFeature.js.map +1 -0
  108. package/lib-es/featureFlags/useFeature.test.d.ts +2 -0
  109. package/lib-es/featureFlags/useFeature.test.d.ts.map +1 -0
  110. package/lib-es/featureFlags/useFeature.test.js +22 -0
  111. package/lib-es/featureFlags/useFeature.test.js.map +1 -0
  112. package/lib-es/featureFlags/useHasOverriddenFeatureFlags.d.ts +6 -0
  113. package/lib-es/featureFlags/useHasOverriddenFeatureFlags.d.ts.map +1 -0
  114. package/lib-es/featureFlags/useHasOverriddenFeatureFlags.js +20 -0
  115. package/lib-es/featureFlags/useHasOverriddenFeatureFlags.js.map +1 -0
  116. package/lib-es/index.d.ts +2 -0
  117. package/lib-es/index.d.ts.map +1 -0
  118. package/lib-es/index.js +2 -0
  119. package/lib-es/index.js.map +1 -0
  120. package/package.json +82 -0
  121. package/src/featureFlags/FeatureFlagsContext.test.tsx +13 -0
  122. package/src/featureFlags/FeatureFlagsContext.tsx +62 -0
  123. package/src/featureFlags/FeatureToggle.tsx +22 -0
  124. package/src/featureFlags/LiveConfig.ts +45 -0
  125. package/src/featureFlags/defaultFeatures.ts +428 -0
  126. package/src/featureFlags/firebaseFeatureFlags.ts +115 -0
  127. package/src/featureFlags/groupedFeatures.ts +25 -0
  128. package/src/featureFlags/helper.tsx +9 -0
  129. package/src/featureFlags/index.ts +12 -0
  130. package/src/featureFlags/mock.tsx +28 -0
  131. package/src/featureFlags/useFeature.test.tsx +23 -0
  132. package/src/featureFlags/useFeature.ts +21 -0
  133. package/src/featureFlags/useHasOverriddenFeatureFlags.ts +24 -0
  134. package/src/index.ts +1 -0
  135. package/tsconfig.json +15 -0
  136. package/types.ts +5 -0
@@ -0,0 +1,428 @@
1
+ import { DefaultFeature, Feature, Features, FeatureMap } from "@ledgerhq/types-live";
2
+ import { reduce } from "lodash";
3
+ import { formatToFirebaseFeatureId } from "./firebaseFeatureFlags";
4
+
5
+ /**
6
+ * Default disabled feature.
7
+ */
8
+ const DEFAULT_FEATURE: DefaultFeature = {
9
+ enabled: false,
10
+ };
11
+
12
+ /**
13
+ * Util function that create a default feature and type its return.
14
+ *
15
+ * @dev needed for proper type infering.
16
+ *
17
+ * @param opts
18
+ * @returns typed `opts` parameter or the default feature.
19
+ */
20
+ export const initFeature = <T>(opts?: Feature<T>) => {
21
+ const feature = opts ?? DEFAULT_FEATURE;
22
+ return feature as Feature<T>;
23
+ };
24
+
25
+ /**
26
+ * Currency Features.
27
+ */
28
+ export const CURRENCY_DEFAULT_FEATURES = {
29
+ currencyArbitrum: DEFAULT_FEATURE,
30
+ currencyArbitrumGoerli: DEFAULT_FEATURE,
31
+ currencyAstar: DEFAULT_FEATURE,
32
+ currencyAvalancheCChain: DEFAULT_FEATURE,
33
+ currencyAxelar: DEFAULT_FEATURE,
34
+ currencyBase: DEFAULT_FEATURE,
35
+ currencyBaseGoerli: DEFAULT_FEATURE,
36
+ currencyBittorrent: DEFAULT_FEATURE,
37
+ currencyBoba: DEFAULT_FEATURE,
38
+ currencyCoreum: DEFAULT_FEATURE,
39
+ currencyDesmos: DEFAULT_FEATURE,
40
+ currencyDydx: DEFAULT_FEATURE,
41
+ currencyEnergyWeb: DEFAULT_FEATURE,
42
+ currencyEvmosEvm: DEFAULT_FEATURE,
43
+ currencyInjective: DEFAULT_FEATURE,
44
+ currencyInternetComputer: DEFAULT_FEATURE,
45
+ currencyKavaEvm: DEFAULT_FEATURE,
46
+ currencyKlaytn: DEFAULT_FEATURE,
47
+ currencyLukso: DEFAULT_FEATURE,
48
+ currencyMetis: DEFAULT_FEATURE,
49
+ currencyMoonriver: DEFAULT_FEATURE,
50
+ currencyOnomy: DEFAULT_FEATURE,
51
+ currencyOptimism: DEFAULT_FEATURE,
52
+ currencyOptimismGoerli: DEFAULT_FEATURE,
53
+ currencyPersistence: DEFAULT_FEATURE,
54
+ currencyPolygonZkEvm: DEFAULT_FEATURE,
55
+ currencyPolygonZkEvmTestnet: DEFAULT_FEATURE,
56
+ currencyQuicksilver: DEFAULT_FEATURE,
57
+ currencyRsk: DEFAULT_FEATURE,
58
+ currencySecretNetwork: DEFAULT_FEATURE,
59
+ currencySeiNetwork: DEFAULT_FEATURE,
60
+ currencyStacks: DEFAULT_FEATURE,
61
+ currencyStargaze: DEFAULT_FEATURE,
62
+ currencySyscoin: DEFAULT_FEATURE,
63
+ currencyTelosEvm: DEFAULT_FEATURE,
64
+ currencyUmee: DEFAULT_FEATURE,
65
+ currencyVechain: DEFAULT_FEATURE,
66
+ currencyVelasEvm: DEFAULT_FEATURE,
67
+ currencyCasper: DEFAULT_FEATURE,
68
+ currencyNeonEvm: DEFAULT_FEATURE,
69
+ };
70
+
71
+ /**
72
+ * Default Features.
73
+ */
74
+ export const DEFAULT_FEATURES: Features = {
75
+ ...CURRENCY_DEFAULT_FEATURES,
76
+
77
+ brazeLearn: DEFAULT_FEATURE,
78
+ objkt: DEFAULT_FEATURE,
79
+ portfolioExchangeBanner: DEFAULT_FEATURE,
80
+ postOnboardingAssetsTransfer: DEFAULT_FEATURE,
81
+ postOnboardingClaimNft: DEFAULT_FEATURE,
82
+ syncOnboarding: DEFAULT_FEATURE,
83
+ walletConnectEntryPoint: DEFAULT_FEATURE,
84
+ counterValue: DEFAULT_FEATURE,
85
+ llmNewDeviceSelection: DEFAULT_FEATURE,
86
+ llmNewFirmwareUpdateUx: DEFAULT_FEATURE,
87
+ mockFeature: DEFAULT_FEATURE,
88
+ multibuyNavigation: DEFAULT_FEATURE,
89
+ ptxServiceCtaExchangeDrawer: DEFAULT_FEATURE,
90
+ ptxServiceCtaScreens: DEFAULT_FEATURE,
91
+ customImage: DEFAULT_FEATURE,
92
+ referralProgramDesktopBanner: DEFAULT_FEATURE,
93
+ disableNftLedgerMarket: DEFAULT_FEATURE,
94
+ disableNftRaribleOpensea: DEFAULT_FEATURE,
95
+ disableNftSend: DEFAULT_FEATURE,
96
+ staxWelcomeScreen: DEFAULT_FEATURE,
97
+ protectServicesDiscoverDesktop: DEFAULT_FEATURE,
98
+ llmWalletQuickActions: DEFAULT_FEATURE,
99
+ listAppsV2minor1: DEFAULT_FEATURE,
100
+ ethStakingProviders: initFeature(),
101
+ referralProgramDiscoverCard: initFeature(),
102
+ newsfeedPage: initFeature(),
103
+ ptxEarn: initFeature(),
104
+ swapWalletApiPartnerList: initFeature(),
105
+ stakePrograms: initFeature(),
106
+ learn: initFeature(),
107
+ receiveStakingFlowConfigDesktop: initFeature(),
108
+ brazePushNotifications: initFeature(),
109
+ walletNftGallery: initFeature(),
110
+ stakeAccountBanner: initFeature(),
111
+
112
+ buyDeviceFromLive: {
113
+ enabled: false,
114
+ params: { debug: false, url: null },
115
+ },
116
+
117
+ depositNetworkBannerMobile: {
118
+ enabled: false,
119
+ params: { url: "https://www.ledger.com/ledger-live" },
120
+ },
121
+
122
+ depositWithdrawBannerMobile: {
123
+ enabled: false,
124
+ params: { url: "https://www.ledger.com/ledger-live" },
125
+ },
126
+
127
+ deviceInitialApps: {
128
+ enabled: false,
129
+ params: { apps: ["Bitcoin", "Ethereum"] },
130
+ },
131
+
132
+ discover: {
133
+ enabled: false,
134
+ params: { version: "1" },
135
+ },
136
+
137
+ domainInputResolution: {
138
+ enabled: false,
139
+ params: { supportedCurrencyIds: ["ethereum"] },
140
+ },
141
+
142
+ editEvmTx: {
143
+ enabled: false,
144
+ params: { supportedCurrencyIds: ["ethereum"] },
145
+ },
146
+
147
+ referralProgramDesktopSidebar: {
148
+ enabled: false,
149
+ params: { amount: "$20", isNew: true, path: "/discover/refer-a-friend" },
150
+ },
151
+
152
+ referralProgramMobile: {
153
+ enabled: false,
154
+ params: { path: "/discover/refer-a-friend" },
155
+ },
156
+
157
+ protectServicesDesktop: {
158
+ enabled: false,
159
+ params: {
160
+ availableOnDesktop: false,
161
+ isNew: false,
162
+ ledgerliveStorageState: false,
163
+ bannerSubscriptionNotification: false,
164
+ account: {
165
+ homeURI:
166
+ "ledgerlive://recover/protect-simu?redirectTo=account&source=lld-sidebar-navigation&ajs_recover_source=lld-sidebar-navigation&ajs_recover_campaign=recover-launch",
167
+ loginURI:
168
+ "ledgerlive://recover/protect-simu?redirectTo=login&source=lld-welcome-login&ajs_recover_source=lld-welcome-login&ajs_recover_campaign=recover-launch",
169
+ },
170
+ compatibleDevices: [],
171
+ discoverTheBenefitsLink: "https://www.ledger.com/recover",
172
+ onboardingCompleted: {
173
+ alreadySubscribedURI: "ledgerlive://recover/protect-simu?redirectTo=login",
174
+ alreadyDeviceSeededURI:
175
+ "ledgerlive://recover/protect-simu?redirectTo=upsell&source=lld-pairing&ajs_recover_source=lld-pairing&ajs_recover_campaign=recover-launch",
176
+ upsellURI:
177
+ "ledgerlive://recover/protect-simu?redirectTo=upsell&source=lld-onboarding-24&ajs_recover_source=lld-onboarding-24&ajs_recover_campaign=recover-launch",
178
+ restore24URI:
179
+ "ledgerlive://recover/protect-simu?redirectTo=upsell&source=lld-restore-24&ajs_recover_source=lld-restore-24&ajs_recover_campaign=recover-launch",
180
+ },
181
+ onboardingRestore: {
182
+ postOnboardingURI:
183
+ "ledgerlive://recover/protect-simu?redirectTo=restore&source=lld-restore",
184
+ restoreInfoDrawer: {
185
+ enabled: true,
186
+ manualStepsURI:
187
+ "https://support.ledger.com/hc/en-us/articles/360013349800-Update-Ledger-Nano-X-firmware?docs=true",
188
+ supportLinkURI: "https://support.ledger.com",
189
+ },
190
+ },
191
+ openRecoverFromSidebar: true,
192
+ protectId: "protect-simu",
193
+ },
194
+ },
195
+
196
+ storyly: {
197
+ enabled: true,
198
+ params: {
199
+ stories: {
200
+ recoverySeed: {
201
+ testingEnabled: false,
202
+ token:
203
+ "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhY2NfaWQiOjY5NDgsImFwcF9pZCI6MTE0MjIsImluc19pZCI6MTQ4Mjl9.iak4gUnizDdPrEXJEV3wszzJ2YkYX-RIWDXv31aJkiE",
204
+ },
205
+ storylyExample: {
206
+ testingEnabled: false,
207
+ token:
208
+ "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJhY2NfaWQiOjc2MCwiYXBwX2lkIjo0MDUsImluc19pZCI6NDA0fQ.1AkqOy_lsiownTBNhVOUKc91uc9fDcAxfQZtpm3nj40",
209
+ },
210
+ testStory: {
211
+ testingEnabled: false,
212
+ token:
213
+ "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJhY2NfaWQiOjY5NDgsImFwcF9pZCI6MTE0MjIsImluc19pZCI6MTIxOTh9.XqNitheri5VPDqebtA4JFu1VucVOHYlryki2TqCb1DQ",
214
+ },
215
+ },
216
+ },
217
+ },
218
+
219
+ transactionsAlerts: {
220
+ enabled: false,
221
+ params: {
222
+ chainwatchBaseUrl: "https://chainwatch.aws.stg.ldg-tech.com/v0",
223
+ networks: [
224
+ {
225
+ chainwatchId: "eth",
226
+ ledgerLiveId: "ethereum",
227
+ nbConfirmations: 1,
228
+ },
229
+ ],
230
+ },
231
+ },
232
+
233
+ firebaseEnvironmentReadOnly: {
234
+ enabled: false,
235
+ params: {
236
+ comment:
237
+ "Do not modify this configuration. This is just a read-only helper to display the targeted Firebase environment in Ledger Live. The value of this flag has NO functional impact.",
238
+ project: "n/a (Firebase project could not be reached)",
239
+ },
240
+ },
241
+
242
+ npsRatingsPrompt: {
243
+ enabled: false,
244
+ params: {
245
+ conditions: {
246
+ disappointed_delay: {
247
+ seconds: 60,
248
+ },
249
+ minimum_accounts_number: 1,
250
+ minimum_app_starts_number: 0,
251
+ minimum_duration_since_app_first_start: {
252
+ seconds: 0,
253
+ },
254
+ minimum_number_of_app_starts_since_last_crash: 0,
255
+ not_now_delay: {
256
+ seconds: 30,
257
+ },
258
+ satisfied_then_not_now_delay: {
259
+ seconds: 90,
260
+ },
261
+ },
262
+ happy_moments: [
263
+ {
264
+ route_name: "ReceiveVerificationConfirmation",
265
+ timer: 2000,
266
+ type: "on_leave",
267
+ },
268
+ {
269
+ route_name: "ClaimRewardsValidationSuccess",
270
+ timer: 2000,
271
+ type: "on_enter",
272
+ },
273
+ {
274
+ route_name: "CosmosClaimRewardsValidationSuccess",
275
+ timer: 2000,
276
+ type: "on_enter",
277
+ },
278
+ {
279
+ route_name: "AlgorandClaimRewardsValidationSuccess",
280
+ timer: 2000,
281
+ type: "on_enter",
282
+ },
283
+ {
284
+ route_name: "SendValidationSuccess",
285
+ timer: 2000,
286
+ type: "on_enter",
287
+ },
288
+ {
289
+ route_name: "MarketDetail",
290
+ timer: 3000,
291
+ type: "on_enter",
292
+ },
293
+ ],
294
+ support_email: "support@ledger.com",
295
+ typeform_url:
296
+ "https://ledger.typeform.com/to/UsbZ0RBk?typeform-medium=embed-sdk&typeform-medium-version=next&typeform-embed=popup-blank&dev=1",
297
+ },
298
+ },
299
+
300
+ protectServicesMobile: {
301
+ enabled: false,
302
+ params: {
303
+ ledgerliveStorageState: false,
304
+ bannerSubscriptionNotification: false,
305
+ deeplink: "",
306
+ compatibleDevices: [],
307
+ account: {
308
+ homeURI:
309
+ "ledgerlive://recover/protect-simu?redirectTo=account&source=llm-myledger-access-card&ajs_prop_source=llm-myledger-access-card&ajs_prop_campaign=recover-launch",
310
+ loginURI:
311
+ "ledgerlive://recover/protect-simu?redirectTo=login&source=llm-myledger-access-card&ajs_prop_source=llm-myledger-access-card&ajs_prop_campaign=recover-launch",
312
+ },
313
+ managerStatesData: {
314
+ NEW: {
315
+ learnMoreURI:
316
+ "ledgerlive://recover/protect-simu?redirectTo=upsell&source=llm-onboarding-24&ajs_prop_source=llm-onboarding-24&ajs_prop_campaign=recover-launch",
317
+ alreadySubscribedURI:
318
+ "ledgerlive://recover/protect-simu?redirectTo=login&source=llm-onboarding-24&ajs_prop_source=llm-onboarding-24&ajs_prop_campaign=recover-launch",
319
+ quickAccessURI:
320
+ "ledgerlive://recover/protect-simu?redirectTo=upsell&source=llm-navbar-quick-access&ajs_prop_source=llm-navbar-quick-access&ajs_prop_campaign=recover-launch",
321
+ alreadyOnboardedURI:
322
+ "ledgerlive://recover/protect-simu?redirectTo=upsell&source=llm-pairing&ajs_prop_source=llm-pairing&ajs_prop_campaign=recover-launch",
323
+ },
324
+ },
325
+ onboardingRestore: {
326
+ postOnboardingURI:
327
+ "ledgerlive://recover/protect-simu?redirectTo=restore&source=llm-restore-24&ajs_prop_source=llm-restore-24&ajs_prop_campaign=recover-launch",
328
+ restoreInfoDrawer: {
329
+ enabled: true,
330
+ manualStepsURI:
331
+ "https://support.ledger.com/hc/en-us/articles/360013349800-Update-Ledger-Nano-X-firmware?docs=true",
332
+ supportLinkURI:
333
+ "http://chat.abhishekpriyam.com/sprinklrlivechatv2.php?appId=63453067138a3f453db323b4_app_300078397&env=prod3",
334
+ },
335
+ },
336
+ protectId: "protect-simu",
337
+ },
338
+ },
339
+
340
+ ratingsPrompt: {
341
+ enabled: false,
342
+ params: {
343
+ conditions: {
344
+ disappointed_delay: {
345
+ days: 90,
346
+ },
347
+ minimum_accounts_number: 3,
348
+ minimum_app_starts_number: 3,
349
+ minimum_duration_since_app_first_start: {
350
+ days: 3,
351
+ },
352
+ minimum_number_of_app_starts_since_last_crash: 2,
353
+ not_now_delay: {
354
+ days: 15,
355
+ },
356
+ satisfied_then_not_now_delay: {
357
+ days: 3,
358
+ },
359
+ },
360
+ happy_moments: [
361
+ {
362
+ route_name: "ReceiveConfirmation",
363
+ timer: 2000,
364
+ type: "on_enter",
365
+ },
366
+ {
367
+ route_name: "ClaimRewardsValidationSuccess",
368
+ timer: 2000,
369
+ type: "on_enter",
370
+ },
371
+ {
372
+ route_name: "SendValidationSuccess",
373
+ timer: 2000,
374
+ type: "on_enter",
375
+ },
376
+ {
377
+ route_name: "MarketDetail",
378
+ timer: 3000,
379
+ type: "on_enter",
380
+ },
381
+ ],
382
+ support_email: "support@ledger.com",
383
+ typeform_url:
384
+ "https://form.typeform.com/to/Jo7gqcB4?typeform-medium=embed-sdk&typeform-medium-version=next&typeform-embed=popup-blank",
385
+ },
386
+ },
387
+
388
+ cexDepositEntryPointsDesktop: {
389
+ enabled: false,
390
+ params: {
391
+ path: "/platform/ledger-cex-deposit",
392
+ locations: {
393
+ selectCrypto: true,
394
+ },
395
+ },
396
+ },
397
+
398
+ cexDepositEntryPointsMobile: {
399
+ enabled: false,
400
+ params: {
401
+ path: "/discover/ledger-cex-deposit",
402
+ locations: {
403
+ selectCrypto: true,
404
+ },
405
+ },
406
+ },
407
+
408
+ fetchAdditionalCoins: {
409
+ enabled: false,
410
+ },
411
+
412
+ ptxSwapLiveApp: {
413
+ enabled: false,
414
+ },
415
+
416
+ ptxSwapMoonpayProvider: DEFAULT_FEATURE,
417
+ };
418
+
419
+ // Firebase SDK treat JSON values as strings
420
+ export const formatDefaultFeatures = (config: FeatureMap) =>
421
+ reduce(
422
+ config,
423
+ (acc, feature, featureId) => ({
424
+ ...acc,
425
+ [formatToFirebaseFeatureId(featureId)]: JSON.stringify(feature),
426
+ }),
427
+ {},
428
+ );
@@ -0,0 +1,115 @@
1
+ import { PropsWithChildren } from "react";
2
+ import { snakeCase } from "lodash";
3
+ import semver from "semver";
4
+ import { Feature, FeatureId } from "@ledgerhq/types-live";
5
+ import { getEnv } from "@ledgerhq/live-env";
6
+ import { LiveConfig } from "./LiveConfig";
7
+
8
+ export type FirebaseFeatureFlagsProviderProps = PropsWithChildren<unknown>;
9
+ export const formatToFirebaseFeatureId = (id: string) => `feature_${snakeCase(id)}`;
10
+
11
+ export const checkFeatureFlagVersion = (feature: Feature) => {
12
+ const platform = LiveConfig.getInstance().platform;
13
+ if (!feature?.enabled || !platform) {
14
+ return feature;
15
+ }
16
+ const featureSpecificVersion: string | undefined = (() => {
17
+ switch (platform) {
18
+ case "desktop":
19
+ return feature.desktop_version;
20
+ case "ios":
21
+ case "android":
22
+ return feature.mobile_version;
23
+ default:
24
+ return undefined;
25
+ }
26
+ })();
27
+ if (
28
+ featureSpecificVersion &&
29
+ !semver.satisfies(LiveConfig.getInstance().appVersion, featureSpecificVersion, {
30
+ includePrerelease: true,
31
+ })
32
+ ) {
33
+ return {
34
+ enabledOverriddenForCurrentVersion: true,
35
+ ...feature,
36
+ enabled: false,
37
+ };
38
+ }
39
+ return feature;
40
+ };
41
+
42
+ export const isFeature = (key: string): boolean => {
43
+ if (!LiveConfig.getInstance().providerGetvalueMethod) {
44
+ return false;
45
+ }
46
+ try {
47
+ const value = LiveConfig.getInstance().providerGetvalueMethod!["firebaseRemoteConfig"](
48
+ formatToFirebaseFeatureId(key),
49
+ );
50
+ if (!value || !value.asString()) {
51
+ return false;
52
+ }
53
+ return true;
54
+ } catch (error) {
55
+ console.error(`Failed to check if feature "${key}" exists`);
56
+ return false;
57
+ }
58
+ };
59
+
60
+ export const getFeature = (args: {
61
+ key: FeatureId;
62
+ appLanguage?: string;
63
+ localOverrides?: { [key in FeatureId]?: Feature };
64
+ allowOverride?: boolean;
65
+ }) => {
66
+ if (!LiveConfig.getInstance().providerGetvalueMethod) {
67
+ return null;
68
+ }
69
+ const { key, appLanguage, localOverrides, allowOverride = true } = args;
70
+ try {
71
+ // Nb prioritize local overrides
72
+ if (allowOverride && localOverrides && localOverrides[key]) {
73
+ const feature = localOverrides[key];
74
+ if (feature) {
75
+ return checkFeatureFlagVersion(feature);
76
+ }
77
+ }
78
+
79
+ const envFlags = getEnv("FEATURE_FLAGS") as { [key in FeatureId]?: Feature } | undefined;
80
+
81
+ if (allowOverride && envFlags) {
82
+ const feature = envFlags[key];
83
+ if (feature)
84
+ return {
85
+ ...feature,
86
+ overridesRemote: true,
87
+ overriddenByEnv: true,
88
+ };
89
+ }
90
+
91
+ const value = LiveConfig.getInstance().providerGetvalueMethod!["firebaseRemoteConfig"](
92
+ formatToFirebaseFeatureId(key),
93
+ );
94
+
95
+ const feature = JSON.parse(value.asString());
96
+
97
+ if (
98
+ feature.enabled &&
99
+ appLanguage &&
100
+ ((feature.languages_whitelisted && !feature.languages_whitelisted.includes(appLanguage)) ||
101
+ (feature.languages_blacklisted && feature.languages_blacklisted.includes(appLanguage)))
102
+ ) {
103
+ return {
104
+ enabledOverriddenForCurrentLanguage: true,
105
+ ...feature,
106
+ enabled: false,
107
+ };
108
+ }
109
+
110
+ return checkFeatureFlagVersion(feature);
111
+ } catch (error) {
112
+ console.error(`Failed to retrieve feature "${key}"`);
113
+ return null;
114
+ }
115
+ };
@@ -0,0 +1,25 @@
1
+ import { FeatureId } from "@ledgerhq/types-live";
2
+
3
+ /** Helper to group several feature flag ids under a common feature flag */
4
+ export const groupedFeatures: Record<
5
+ string,
6
+ {
7
+ featureIds: FeatureId[];
8
+ }
9
+ > = {
10
+ stax: {
11
+ featureIds: [
12
+ "customImage",
13
+ "deviceInitialApps",
14
+ "llmNewDeviceSelection",
15
+ "postOnboardingAssetsTransfer",
16
+ "postOnboardingClaimNft",
17
+ "staxWelcomeScreen",
18
+ "syncOnboarding",
19
+ "llmNewFirmwareUpdateUx",
20
+ ],
21
+ },
22
+ disableNft: {
23
+ featureIds: ["disableNftLedgerMarket", "disableNftRaribleOpensea", "disableNftSend"],
24
+ },
25
+ };
@@ -0,0 +1,9 @@
1
+ export function isRecoverDisplayed(feature, deviceModelId) {
2
+ return (
3
+ feature?.enabled &&
4
+ feature?.params?.compatibleDevices?.find(
5
+ (device: { name: string; available: boolean }) =>
6
+ device.name === deviceModelId && device.available,
7
+ )
8
+ );
9
+ }
@@ -0,0 +1,12 @@
1
+ import useFeature from "./useFeature";
2
+ import FeatureToggle from "./FeatureToggle";
3
+
4
+ export { useFeature, FeatureToggle };
5
+
6
+ export * from "./defaultFeatures";
7
+ export * from "./groupedFeatures";
8
+ export * from "./FeatureFlagsContext";
9
+ export * from "./useHasOverriddenFeatureFlags";
10
+ export * from "./firebaseFeatureFlags";
11
+ export * from "./LiveConfig";
12
+ export * from "./helper";
@@ -0,0 +1,28 @@
1
+ import React from "react";
2
+ import { Feature, FeatureId } from "@ledgerhq/types-live";
3
+ import { FeatureFlagsContextValue, FeatureFlagsProvider } from "./FeatureFlagsContext";
4
+
5
+ export function makeMockedContextValue(
6
+ mockedFeatures: Partial<Record<FeatureId, Feature>>,
7
+ ): FeatureFlagsContextValue {
8
+ return {
9
+ isFeature: () => true,
10
+ getFeature: (featureId: FeatureId) => mockedFeatures[featureId] || null,
11
+ overrideFeature: () => {},
12
+ resetFeature: () => {},
13
+ resetFeatures: () => {},
14
+ };
15
+ }
16
+
17
+ export function makeMockedFeatureFlagsProviderWrapper(
18
+ mockedContextValue: FeatureFlagsContextValue,
19
+ ) {
20
+ const MockedFeatureProviderWrapper: React.FC<{ children: React.ReactNode | null }> = ({
21
+ children,
22
+ }) => <FeatureFlagsProvider value={mockedContextValue}>{children}</FeatureFlagsProvider>;
23
+ return MockedFeatureProviderWrapper;
24
+ }
25
+
26
+ export const basicMockedFeatureFlagsProviderWrapper = makeMockedFeatureFlagsProviderWrapper(
27
+ makeMockedContextValue({}),
28
+ );
@@ -0,0 +1,23 @@
1
+ import { renderHook } from "@testing-library/react-hooks";
2
+ import useFeature from "./useFeature";
3
+ import { makeMockedFeatureFlagsProviderWrapper, makeMockedContextValue } from "./mock";
4
+
5
+ describe("useFeature hook", () => {
6
+ it("should return null if a flag is not defined remotely", () => {
7
+ const mockedFeatures = {};
8
+ const { result } = renderHook(() => useFeature("mockFeature"), {
9
+ wrapper: makeMockedFeatureFlagsProviderWrapper(makeMockedContextValue(mockedFeatures)),
10
+ });
11
+ expect(result.current).toBeNull();
12
+ });
13
+
14
+ it("should return the feature flag value if the feature flag is defined", () => {
15
+ const mockedFeatures = {
16
+ mockFeature: { enabled: true, params: { blabla: "hello" } },
17
+ };
18
+ const { result } = renderHook(() => useFeature("mockFeature"), {
19
+ wrapper: makeMockedFeatureFlagsProviderWrapper(makeMockedContextValue(mockedFeatures)),
20
+ });
21
+ expect(result.current).toBe(mockedFeatures.mockFeature);
22
+ });
23
+ });
@@ -0,0 +1,21 @@
1
+ import { useMemo } from "react";
2
+ import { useFeatureFlags } from "./FeatureFlagsContext";
3
+ import { FeatureId, Feature, FeatureParam } from "@ledgerhq/types-live";
4
+
5
+ /**
6
+ * Hook that returns the value of a feature flag based on its `featureId`.
7
+ *
8
+ * @dev do not modify this function to make it return a default arbitrary value
9
+ * instead of null, this should not be handled at this level.
10
+ *
11
+ * @param featureId
12
+ * @returns a feature flag value or null if the feature flag is not found
13
+ * (neither in the remote configuration, in the cache or in the local defaults).
14
+ */
15
+ const useFeature = <T extends FeatureId>(featureId: T): Feature<FeatureParam<T>> | null => {
16
+ const featureFlags = useFeatureFlags();
17
+ const value = useMemo(() => featureFlags.getFeature(featureId), [featureFlags, featureId]);
18
+ return value;
19
+ };
20
+
21
+ export default useFeature;
@@ -0,0 +1,24 @@
1
+ import { useMemo } from "react";
2
+ import { FeatureId } from "@ledgerhq/types-live";
3
+ import { useFeatureFlags } from "./FeatureFlagsContext";
4
+ import { DEFAULT_FEATURES } from "./defaultFeatures";
5
+
6
+ /**
7
+ *
8
+ * @returns whether one or more flags are locally overridden
9
+ */
10
+ export function useHasLocallyOverriddenFeatureFlags(): boolean {
11
+ const { getFeature } = useFeatureFlags();
12
+ return useMemo(
13
+ () =>
14
+ Object.entries(DEFAULT_FEATURES).some(([featureId]) => {
15
+ try {
16
+ const val = getFeature(featureId as FeatureId);
17
+ return val?.overridesRemote || val?.overriddenByEnv;
18
+ } catch (e) {
19
+ return false;
20
+ }
21
+ }),
22
+ [getFeature],
23
+ );
24
+ }
package/src/index.ts ADDED
@@ -0,0 +1 @@
1
+ export * from "./featureFlags";