From 3c4fd14baf973f0843f844de59eed6676827d3c8 Mon Sep 17 00:00:00 2001 From: Olivier Freyssinet Date: Thu, 13 Oct 2022 11:25:12 +0200 Subject: [PATCH 1/5] Add env variable FEATURE_FLAGS for overriding flags with env Add feature flags parameter to playwright test fixtures doc:ljs Add env overriding of feature flags in LLM changesets Fix FeatureFlagsButton LLD Fix FeatureFlagsButton remove console log Refactor feature flag settings on LLD Add null to JSONValue type Refactor FeatureFlagsSettings (split in components) Move LLD's FeatureFlagEdit to own component Apply switch toggle directly rather than just modifying the input Remove useless badly typed tryParse --- .changeset/pretty-dingos-sing.md | 5 + .changeset/quiet-doors-cross.md | 5 + .changeset/silent-beers-carry.md | 6 + .../components/FirebaseFeatureFlags.tsx | 15 +- .../sections/Developer/FeatureFlagsButton.tsx | 248 ----- .../FeatureFlagDetails.tsx | 74 ++ .../FeatureFlagsSettings/FeatureFlagEdit.tsx | 103 ++ .../Developer/FeatureFlagsSettings/index.tsx | 102 ++ .../settings/sections/Developer/index.jsx | 4 +- .../src/renderer/styles/StyleProvider.jsx | 15 + .../tests/fixtures/common.ts | 16 +- .../src/components/FirebaseFeatureFlags.tsx | 45 +- .../RootNavigator/SettingsNavigator.tsx | 2 +- .../src/screens/DebugFeatureFlags.tsx | 294 ------ .../FeatureFlagDetails.tsx | 85 ++ .../FeatureFlagsSettings/FeatureFlagEdit.tsx | 112 +++ .../screens/FeatureFlagsSettings/index.tsx | 107 ++ libs/ledger-live-common/src/env.ts | 22 + libs/ledgerjs/packages/types-live/README.md | 932 +++++++++++++++++- .../packages/types-live/src/feature.ts | 2 + 20 files changed, 1634 insertions(+), 560 deletions(-) create mode 100644 .changeset/pretty-dingos-sing.md create mode 100644 .changeset/quiet-doors-cross.md create mode 100644 .changeset/silent-beers-carry.md delete mode 100644 apps/ledger-live-desktop/src/renderer/screens/settings/sections/Developer/FeatureFlagsButton.tsx create mode 100644 apps/ledger-live-desktop/src/renderer/screens/settings/sections/Developer/FeatureFlagsSettings/FeatureFlagDetails.tsx create mode 100644 apps/ledger-live-desktop/src/renderer/screens/settings/sections/Developer/FeatureFlagsSettings/FeatureFlagEdit.tsx create mode 100644 apps/ledger-live-desktop/src/renderer/screens/settings/sections/Developer/FeatureFlagsSettings/index.tsx delete mode 100644 apps/ledger-live-mobile/src/screens/DebugFeatureFlags.tsx create mode 100644 apps/ledger-live-mobile/src/screens/FeatureFlagsSettings/FeatureFlagDetails.tsx create mode 100644 apps/ledger-live-mobile/src/screens/FeatureFlagsSettings/FeatureFlagEdit.tsx create mode 100644 apps/ledger-live-mobile/src/screens/FeatureFlagsSettings/index.tsx diff --git a/.changeset/pretty-dingos-sing.md b/.changeset/pretty-dingos-sing.md new file mode 100644 index 00000000000..babb94ab6b5 --- /dev/null +++ b/.changeset/pretty-dingos-sing.md @@ -0,0 +1,5 @@ +--- +"live-mobile": patch +--- + +Feature flags: take into account env variable FEATURE_FLAGS to override feature flags diff --git a/.changeset/quiet-doors-cross.md b/.changeset/quiet-doors-cross.md new file mode 100644 index 00000000000..af547b9dd2f --- /dev/null +++ b/.changeset/quiet-doors-cross.md @@ -0,0 +1,5 @@ +--- +"ledger-live-desktop": patch +--- + +Feature flags: take into account env variable FEATURE_FLAGS to override feature flags diff --git a/.changeset/silent-beers-carry.md b/.changeset/silent-beers-carry.md new file mode 100644 index 00000000000..9797daaca26 --- /dev/null +++ b/.changeset/silent-beers-carry.md @@ -0,0 +1,6 @@ +--- +"@ledgerhq/live-common": patch +"@ledgerhq/types-live": patch +--- + +Add FEATURE_FLAGS env variable to override feature flags diff --git a/apps/ledger-live-desktop/src/renderer/components/FirebaseFeatureFlags.tsx b/apps/ledger-live-desktop/src/renderer/components/FirebaseFeatureFlags.tsx index f60e2e2f739..6531ae273a2 100644 --- a/apps/ledger-live-desktop/src/renderer/components/FirebaseFeatureFlags.tsx +++ b/apps/ledger-live-desktop/src/renderer/components/FirebaseFeatureFlags.tsx @@ -6,6 +6,7 @@ import { Feature, FeatureId } from "@ledgerhq/types-live"; import { getValue } from "firebase/remote-config"; import { formatFeatureId, useFirebaseRemoteConfig } from "./FirebaseRemoteConfig"; +import { getEnv } from "@ledgerhq/live-common/env"; const checkFeatureFlagVersion = (feature: Feature) => { if ( @@ -42,6 +43,17 @@ export const FirebaseFeatureFlagsProvider = ({ children }: Props): JSX.Element = return checkFeatureFlagVersion(localOverrides[key]); } + const envFlags = getEnv("FEATURE_FLAGS") as { [key in FeatureId]?: Feature } | undefined; + if (allowOverride && envFlags) { + const feature = envFlags[key]; + if (feature) + return { + ...feature, + overridesRemote: true, + overriddenByEnv: true, + }; + } + const value = getValue(remoteConfig, formatFeatureId(key)); const feature: Feature = JSON.parse(value.asString()); @@ -58,7 +70,8 @@ export const FirebaseFeatureFlagsProvider = ({ children }: Props): JSX.Element = (key: FeatureId, value: Feature): void => { const actualRemoteValue = getFeature(key, false); if (!isEqual(actualRemoteValue, value)) { - const overridenValue = { ...value, overridesRemote: true }; + const { overriddenByEnv, ...pureValue } = value; // eslint-disable-line + const overridenValue = { ...pureValue, overridesRemote: true }; setLocalOverrides(currentOverrides => ({ ...currentOverrides, [key]: overridenValue })); } else { setLocalOverrides(currentOverrides => ({ ...currentOverrides, [key]: undefined })); diff --git a/apps/ledger-live-desktop/src/renderer/screens/settings/sections/Developer/FeatureFlagsButton.tsx b/apps/ledger-live-desktop/src/renderer/screens/settings/sections/Developer/FeatureFlagsButton.tsx deleted file mode 100644 index ff4c755ba58..00000000000 --- a/apps/ledger-live-desktop/src/renderer/screens/settings/sections/Developer/FeatureFlagsButton.tsx +++ /dev/null @@ -1,248 +0,0 @@ -import React, { useState, useMemo, useCallback } from "react"; -import Button from "~/renderer/components/Button"; -import { useTranslation } from "react-i18next"; -import { defaultFeatures, useFeatureFlags } from "@ledgerhq/live-common/featureFlags/index"; -import { SettingsSectionRow as Row } from "../../SettingsSection"; -import { Text, Input, Icons, Flex, Tag, SearchInput } from "@ledgerhq/react-ui"; -import { - InputRenderLeftContainer, - InputRenderRightContainer, -} from "@ledgerhq/react-ui/components/form/BaseInput/index"; -import { withV3StyleProvider } from "~/renderer/styles/StyleProviderV3"; -import { includes, lowerCase } from "lodash"; -import Box from "~/renderer/components/Box"; -import Switch from "~/renderer/components/Switch"; -import Alert from "~/renderer/components/Alert"; -import { Feature, FeatureId } from "@ledgerhq/types-live"; - -type EditSectionProps = { - error?: Error; - value: string; - disabled?: boolean; - - onOverride: () => void; - onRestore: () => void; - onChange: (_: string) => void; -}; - -const tryParse = (jsonString: string, fallback: any) => { - try { - return JSON.parse(jsonString); - } catch (e) { - return fallback; - } -}; - -const EditSection = ({ - error, - value, - onOverride, - onRestore, - onChange, - disabled, -}: EditSectionProps) => { - const { t } = useTranslation(); - const handleSwitchChange = useCallback( - enabled => { - const prevVal = JSON.parse(value); - onChange(JSON.stringify({ ...prevVal, enabled })); - }, - [value, onChange], - ); - return ( - - {error ? ( - - {error.toString()} - - ) : null} - - ( - - - - )} - /> - - - - - ); -}; - -const FeatureFlagsButton = () => { - const { t } = useTranslation(); - const featureFlagsProvider = useFeatureFlags(); - const [visible, setVisible] = useState(false); - const [error, setError] = useState(); - const [focusedName, setFocusedName] = useState(""); - const [hiddenFlagName, setHiddenFlagName] = useState(null); - const [inputValues, setInputValues] = useState< - { - [key in FeatureId | string]?: string | undefined; - } - >({}); - const [searchInput, setSearchInput] = useState(""); - - const featureFlags = useMemo(() => { - const features: { [key in FeatureId | string]: Feature } = {}; - const featureKeys = Object.keys(defaultFeatures); - if (hiddenFlagName && !featureKeys.includes(hiddenFlagName)) featureKeys.push(hiddenFlagName); - featureKeys.forEach((key: FeatureId | string) => { - const value = featureFlagsProvider.getFeature(key as FeatureId); - if (value) { - features[key] = value; - } - }); - return features; - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [focusedName, featureFlagsProvider, hiddenFlagName]); - - const handleInputChange = useCallback( - value => { - setError(); - setInputValues(currentValues => ({ - ...currentValues, - [focusedName]: value, - })); - }, - [focusedName], - ); - - const handleRestoreFeature = useCallback(() => { - setError(); - setInputValues(currentValues => ({ - ...currentValues, - [focusedName]: undefined, - })); - featureFlagsProvider.resetFeature(focusedName); - }, [featureFlagsProvider, focusedName]); - - const handleOverrideFeature = useCallback(() => { - setError(); - try { - // Nb if value is invalid or missing, JSON parse will fail - const newValue = JSON.parse(inputValues[focusedName]); - featureFlagsProvider.overrideFeature(focusedName, newValue); - } catch (e) { - setError(e); - } - }, [inputValues, focusedName, featureFlagsProvider]); - - const handleAddHiddenFlag = useCallback( - value => { - setHiddenFlagName(value); - setSearchInput(value); - }, - [setSearchInput, setHiddenFlagName], - ); - - const filteredFlags = useMemo(() => { - return Object.entries(featureFlags) - .sort((a, b) => a[0].localeCompare(b[0])) - .filter(([name]) => !searchInput || includes(lowerCase(name), lowerCase(searchInput))); - }, [featureFlags, searchInput]); - - return ( - - {t("settings.developer.featureFlagsDesc")} - {!visible ? null : ( - <> - - ( - - - - )} - clearable - placeholder="Add missing flag" - value={hiddenFlagName} - onChange={handleAddHiddenFlag} - /> - - {filteredFlags.map(([flagName, value]) => ( - <> - - {focusedName === flagName ? ( - - - - - {JSON.stringify(featureFlags[flagName], null, 2)} - - - - ) : null} - - ))} - - )} - - } - > - - - ); -}; - -export default withV3StyleProvider(FeatureFlagsButton); diff --git a/apps/ledger-live-desktop/src/renderer/screens/settings/sections/Developer/FeatureFlagsSettings/FeatureFlagDetails.tsx b/apps/ledger-live-desktop/src/renderer/screens/settings/sections/Developer/FeatureFlagsSettings/FeatureFlagDetails.tsx new file mode 100644 index 00000000000..d0b78d6d82c --- /dev/null +++ b/apps/ledger-live-desktop/src/renderer/screens/settings/sections/Developer/FeatureFlagsSettings/FeatureFlagDetails.tsx @@ -0,0 +1,74 @@ +import React, { useCallback } from "react"; +import ButtonV2 from "~/renderer/components/Button"; +import { useFeatureFlags } from "@ledgerhq/live-common/featureFlags/index"; +import { Text, Flex, Tag } from "@ledgerhq/react-ui"; +import { FeatureId } from "@ledgerhq/types-live"; +import { withV2StyleProvider } from "~/renderer/styles/StyleProvider"; +import Box from "~/renderer/components/Box"; +import FeatureFlagEdit from "./FeatureFlagEdit"; + +const OldButton = withV2StyleProvider(ButtonV2); + +type Props = { + flagName: FeatureId; + focused?: boolean; + setFocusedName: (arg0: string | undefined) => void; +}; + +const FeatureFlagDetails: React.FC = props => { + const { flagName, focused, setFocusedName } = props; + const { getFeature } = useFeatureFlags(); + const flagValue = getFeature(flagName as FeatureId); + + const { + overriddenByEnv, + overridesRemote, + enabledOverriddenForCurrentLanguage, + enabledOverriddenForCurrentDesktopVersion, + } = flagValue || {}; + + const handleClick = useCallback(() => { + focused ? setFocusedName(undefined) : setFocusedName(flagName); + }, [focused, flagName, setFocusedName]); + + if (!flagValue) return null; + + return ( + <> + + + + {flagName} + {overriddenByEnv ? ( + + overridden by env + + ) : overridesRemote ? ( + + overridden locally + + ) : null} + {enabledOverriddenForCurrentLanguage ? ( + + disabled for current language + + ) : null} + {enabledOverriddenForCurrentDesktopVersion ? ( + + disabled for current version + + ) : null} + + + {focused ? : null} + + ); +}; + +export default FeatureFlagDetails; diff --git a/apps/ledger-live-desktop/src/renderer/screens/settings/sections/Developer/FeatureFlagsSettings/FeatureFlagEdit.tsx b/apps/ledger-live-desktop/src/renderer/screens/settings/sections/Developer/FeatureFlagsSettings/FeatureFlagEdit.tsx new file mode 100644 index 00000000000..3975d2c43b3 --- /dev/null +++ b/apps/ledger-live-desktop/src/renderer/screens/settings/sections/Developer/FeatureFlagsSettings/FeatureFlagEdit.tsx @@ -0,0 +1,103 @@ +import React, { useState, useMemo, useCallback } from "react"; +import Button from "~/renderer/components/ButtonV3"; +import { useTranslation } from "react-i18next"; +import { useFeatureFlags } from "@ledgerhq/live-common/featureFlags/index"; +import { Text, Input, Flex } from "@ledgerhq/react-ui"; +import { Feature, FeatureId } from "@ledgerhq/types-live"; +import { InputRenderRightContainer } from "@ledgerhq/react-ui/components/form/BaseInput/index"; +import { withV2StyleProvider } from "~/renderer/styles/StyleProvider"; +import SwitchV2 from "~/renderer/components/Switch"; +import Alert from "~/renderer/components/Alert"; + +const Switch = withV2StyleProvider(SwitchV2); + +const FeatureFlagEdit: React.FC<{ flagName: FeatureId; flagValue: Feature }> = props => { + const { flagName, flagValue } = props; + const [error, setError] = useState(); + const [inputValue, setInputValue] = useState(undefined); + + const stringifiedFlagValue = useMemo(() => (flagValue ? JSON.stringify(flagValue) : undefined), [ + flagValue, + ]); + const inputValueDefaulted = inputValue || stringifiedFlagValue; + + const { overriddenByEnv, overridesRemote, enabledOverriddenForCurrentLanguage, ...pureValue } = // eslint-disable-line + flagValue || {}; + + const featureFlagsProvider = useFeatureFlags(); + + const { t } = useTranslation(); + + const handleInputChange = useCallback(value => { + setError(undefined); + setInputValue(value); + }, []); + + const handleRestoreFeature = useCallback(() => { + setError(undefined); + setInputValue(undefined); + featureFlagsProvider.resetFeature(flagName); + }, [featureFlagsProvider, flagName]); + + const handleOverrideFeature = useCallback(() => { + setError(undefined); + try { + // Nb if value is invalid or missing, JSON parse will fail + const newValue = inputValue ? JSON.parse(inputValue) : undefined; + featureFlagsProvider.overrideFeature(flagName, newValue); + } catch (e) { + setError(e); + } + }, [inputValue, flagName, featureFlagsProvider]); + + const isChecked = useMemo(() => { + if (!inputValueDefaulted) return false; + try { + return JSON.parse(inputValueDefaulted)?.enabled; + } catch (e) { + return false; + } + }, [inputValueDefaulted]); + + const handleSwitchChange = useCallback( + enabled => { + featureFlagsProvider.overrideFeature(flagName, { ...flagValue, enabled }); + }, + [featureFlagsProvider, flagName, flagValue], + ); + + return ( + + + {error ? ( + + {error.toString()} + + ) : null} + + ( + + + + )} + /> + + + + + + + {JSON.stringify(pureValue, null, 2)} + + + ); +}; + +export default FeatureFlagEdit; diff --git a/apps/ledger-live-desktop/src/renderer/screens/settings/sections/Developer/FeatureFlagsSettings/index.tsx b/apps/ledger-live-desktop/src/renderer/screens/settings/sections/Developer/FeatureFlagsSettings/index.tsx new file mode 100644 index 00000000000..99cdf0c79c5 --- /dev/null +++ b/apps/ledger-live-desktop/src/renderer/screens/settings/sections/Developer/FeatureFlagsSettings/index.tsx @@ -0,0 +1,102 @@ +import React, { useState, useMemo, useCallback } from "react"; +import ButtonV2 from "~/renderer/components/Button"; +import { useTranslation } from "react-i18next"; +import { defaultFeatures } from "@ledgerhq/live-common/featureFlags/index"; +import { SettingsSectionRow as Row } from "../../../SettingsSection"; +import { Input, Icons, Flex, SearchInput } from "@ledgerhq/react-ui"; +import { FeatureId } from "@ledgerhq/types-live"; +import { InputRenderLeftContainer } from "@ledgerhq/react-ui/components/form/BaseInput/index"; +import { includes, lowerCase } from "lodash"; +import { withV2StyleProvider } from "~/renderer/styles/StyleProvider"; +import { withV3StyleProvider } from "~/renderer/styles/StyleProviderV3"; +import FeatureFlagDetails from "./FeatureFlagDetails"; + +const OldButton = withV2StyleProvider(ButtonV2); + +const FeatureFlagsSettings = () => { + const { t } = useTranslation(); + const [visible, setVisible] = useState(false); + const [focusedName, setFocusedName] = useState(); + const [hiddenFlagName, setHiddenFlagName] = useState(null); + const [searchInput, setSearchInput] = useState(""); + + const featureFlags = useMemo(() => { + const featureKeys = Object.keys(defaultFeatures); + if (hiddenFlagName && !featureKeys.includes(hiddenFlagName)) featureKeys.push(hiddenFlagName); + return featureKeys; + }, [hiddenFlagName]); + + const handleAddHiddenFlag = useCallback( + value => { + setHiddenFlagName(value); + setSearchInput(value); + }, + [setSearchInput, setHiddenFlagName], + ); + + const filteredFlags = useMemo(() => { + return featureFlags + .sort((a, b) => a[0].localeCompare(b[0])) + .filter(name => !searchInput || includes(lowerCase(name), lowerCase(searchInput))); + }, [featureFlags, searchInput]); + + const handleClick = useCallback(() => { + setVisible(!visible); + }, [visible]); + + const content = useMemo( + () => + filteredFlags.map(flagName => ( + + )), + [filteredFlags, focusedName], + ); + + return ( + + {t("settings.developer.featureFlagsDesc")} + {!visible ? null : ( + <> + + ( + + + + )} + clearable + placeholder="Add missing flag" + value={hiddenFlagName} + onChange={handleAddHiddenFlag} + /> + + {content} + + )} + + } + > + + {visible ? "Hide" : "Show"} + + + ); +}; + +export default withV3StyleProvider(FeatureFlagsSettings); diff --git a/apps/ledger-live-desktop/src/renderer/screens/settings/sections/Developer/index.jsx b/apps/ledger-live-desktop/src/renderer/screens/settings/sections/Developer/index.jsx index 8cdba548a3d..96e5eee8ebb 100644 --- a/apps/ledger-live-desktop/src/renderer/screens/settings/sections/Developer/index.jsx +++ b/apps/ledger-live-desktop/src/renderer/screens/settings/sections/Developer/index.jsx @@ -9,7 +9,7 @@ import AllowDebugAppsToggle from "./AllowDebugAppsToggle"; import EnablePlatformDevToolsToggle from "./EnablePlatformDevToolsToggle"; import CatalogProviderSelect from "./CatalogProviderSelect"; import RunLocalAppButton from "./RunLocalAppButton"; -import FeatureFlagsButton from "./FeatureFlagsButton"; +import FeatureFlagsSettings from "./FeatureFlagsSettings"; import EnableLearnPageStagingUrlToggle from "./EnableLearnPageStagingUrlToggle"; const SectionDeveloper = () => { @@ -44,7 +44,7 @@ const SectionDeveloper = () => { - + { ); }; +export const withV2StyleProvider = (Component: React.ComponentType) => { + const WrappedComponent = props => { + const selectedPalette = useSelector(themeSelector) || "light"; + + return ( + + + + ); + }; + return WrappedComponent; +}; + export default StyleProvider; diff --git a/apps/ledger-live-desktop/tests/fixtures/common.ts b/apps/ledger-live-desktop/tests/fixtures/common.ts index a4f29ac0514..246bd4bff7e 100644 --- a/apps/ledger-live-desktop/tests/fixtures/common.ts +++ b/apps/ledger-live-desktop/tests/fixtures/common.ts @@ -3,6 +3,7 @@ import { test as base, expect, Page, ElectronApplication } from "@playwright/tes import * as fs from "fs"; import * as path from "path"; import * as crypto from "crypto"; +import { Feature, FeatureId } from "@ledgerhq/types-live"; export function generateUUID(): string { return crypto.randomBytes(16).toString("hex"); @@ -17,6 +18,7 @@ type TestFixtures = { userdataFile: any; env: Record; page: Page; + featureFlags: { [key in FeatureId]?: Feature }; }; const test = base.extend({ @@ -24,6 +26,7 @@ const test = base.extend({ lang: "en-US", theme: "dark", userdata: undefined, + featureFlags: undefined, userdataDestinationPath: async ({}, use) => { use(path.join(__dirname, "../artifacts/userdata", generateUUID())); }, @@ -35,7 +38,15 @@ const test = base.extend({ use(fullFilePath); }, page: async ( - { lang, theme, userdata, userdataDestinationPath, userdataOriginalFile, env }: TestFixtures, + { + lang, + theme, + userdata, + userdataDestinationPath, + userdataOriginalFile, + env, + featureFlags, + }: TestFixtures, use: (page: Page) => void, ) => { // create userdata path @@ -55,6 +66,7 @@ const test = base.extend({ CI: process.env.CI || undefined, PLAYWRIGHT_RUN: true, LEDGER_MIN_HEIGHT: 768, + FEATURE_FLAGS: JSON.stringify(featureFlags), }, env, ); @@ -105,7 +117,7 @@ const test = base.extend({ }); // app is loaded - //expect(await page.title()).toBe("Ledger Live"); + // expect(await page.title()).toBe("Ledger Live"); await page.waitForLoadState("domcontentloaded"); await page.waitForSelector("#loader-container", { state: "hidden" }); diff --git a/apps/ledger-live-mobile/src/components/FirebaseFeatureFlags.tsx b/apps/ledger-live-mobile/src/components/FirebaseFeatureFlags.tsx index f51ef376c40..ccad3a5b089 100644 --- a/apps/ledger-live-mobile/src/components/FirebaseFeatureFlags.tsx +++ b/apps/ledger-live-mobile/src/components/FirebaseFeatureFlags.tsx @@ -7,6 +7,7 @@ import { defaultFeatures, } from "@ledgerhq/live-common/featureFlags/index"; import { FeatureId, Feature } from "@ledgerhq/types-live"; +import { getEnv } from "@ledgerhq/live-common/env"; import { formatFeatureId } from "./FirebaseRemoteConfig"; @@ -15,17 +16,33 @@ import { languageSelector } from "../reducers/settings"; // eslint-disable-next-line @typescript-eslint/ban-types type Props = PropsWithChildren<{}>; -const getFeature = ( - key: FeatureId, - appLanguage: string, - localOverrides?: { [key in FeatureId]?: Feature }, -) => { +const getFeature = (args: { + key: FeatureId; + appLanguage: string; + localOverrides?: { [key in FeatureId]?: Feature }; + allowOverride?: boolean; +}) => { + const { key, appLanguage, localOverrides, allowOverride = true } = args; try { // Nb prioritize local overrides - if (localOverrides && localOverrides[key]) { + if (allowOverride && localOverrides && localOverrides[key]) { return localOverrides[key]; } + const envFlags = getEnv("FEATURE_FLAGS") as + | { [key in FeatureId]?: Feature } + | undefined; + + if (allowOverride && envFlags) { + const feature = envFlags[key]; + if (feature) + return { + ...feature, + overridesRemote: true, + overriddenByEnv: true, + }; + } + const value = remoteConfig().getValue(formatFeatureId(key)); const feature = JSON.parse(value.asString()); @@ -57,8 +74,8 @@ export const getAllDivergedFlags = ( appLanguage: string, ): { [key in FeatureId]: boolean } => { const res: { [key in FeatureId]: boolean } = {}; - Object.keys(defaultFeatures).forEach(key => { - const value = getFeature(key, appLanguage); + (Object.keys(defaultFeatures) as FeatureId[]).forEach(key => { + const value = getFeature({ key, appLanguage }); if (value && value.enabled !== defaultFeatures[key].enabled) { res[key] = value.enabled; } @@ -73,9 +90,14 @@ export const FirebaseFeatureFlagsProvider: React.FC = ({ children }) => { const overrideFeature = useCallback( (key: FeatureId, value: Feature): void => { - const actualRemoteValue = getFeature(key, appLanguage); + const actualRemoteValue = getFeature({ + key, + appLanguage, + allowOverride: false, + }); if (!isEqual(actualRemoteValue, value)) { - const overridenValue = { ...value, overridesRemote: true }; + const { overriddenByEnv: _, ...pureValue } = value; + const overridenValue = { ...pureValue, overridesRemote: true }; setLocalOverrides(currentOverrides => ({ ...currentOverrides, [key]: overridenValue, @@ -99,7 +121,8 @@ export const FirebaseFeatureFlagsProvider: React.FC = ({ children }) => { // Nb wrapped because the method is also called from outside. const wrappedGetFeature = useCallback( - (key: FeatureId): Feature => getFeature(key, appLanguage, localOverrides), + (key: FeatureId): Feature => + getFeature({ key, appLanguage, localOverrides }), [localOverrides, appLanguage], ); diff --git a/apps/ledger-live-mobile/src/components/RootNavigator/SettingsNavigator.tsx b/apps/ledger-live-mobile/src/components/RootNavigator/SettingsNavigator.tsx index a978314467a..9500e1c3d4a 100644 --- a/apps/ledger-live-mobile/src/components/RootNavigator/SettingsNavigator.tsx +++ b/apps/ledger-live-mobile/src/components/RootNavigator/SettingsNavigator.tsx @@ -16,7 +16,7 @@ import DebugBLE from "../../screens/DebugBLE"; import DebugBLEBenchmark from "../../screens/DebugBLEBenchmark"; import DebugCrash from "../../screens/DebugCrash"; import DebugHttpTransport from "../../screens/DebugHttpTransport"; -import DebugFeatureFlags from "../../screens/DebugFeatureFlags"; +import DebugFeatureFlags from "../../screens/FeatureFlagsSettings"; import DebugIcons from "../../screens/DebugIcons"; import DebugLottie from "../../screens/DebugLottie"; import DebugMultiAppInstall from "../../screens/DebugMultiAppInstall"; diff --git a/apps/ledger-live-mobile/src/screens/DebugFeatureFlags.tsx b/apps/ledger-live-mobile/src/screens/DebugFeatureFlags.tsx deleted file mode 100644 index b8dbd9bb11d..00000000000 --- a/apps/ledger-live-mobile/src/screens/DebugFeatureFlags.tsx +++ /dev/null @@ -1,294 +0,0 @@ -import React, { useCallback, useState, useMemo } from "react"; -import { Pressable, ScrollView, StyleSheet, View } from "react-native"; -import { useTranslation } from "react-i18next"; -import { - defaultFeatures, - useFeatureFlags, -} from "@ledgerhq/live-common/featureFlags/index"; -import type { FeatureId, Feature } from "@ledgerhq/types-live"; - -import { - BaseInput, - Text, - Flex, - Button, - Box, - Tag, - SearchInput, - Switch, - Icons, -} from "@ledgerhq/native-ui"; -import styled from "styled-components/native"; -import { includes, lowerCase } from "lodash"; -import { - InputRenderLeftContainer, - InputRenderRightContainer, -} from "@ledgerhq/native-ui/components/Form/Input/BaseInput"; -import NavigationScrollView from "../components/NavigationScrollView"; -import Alert from "../components/Alert"; - -const Divider = styled(Box).attrs({ - width: "100%", - my: 4, - height: 1, - bg: "neutral.c50", -})``; - -const TagEnabled = styled(Tag).attrs({ - bg: "success.c100", - uppercase: false, - type: "color", - mr: 2, -})``; - -const TagDisabled = styled(Tag).attrs({ - bg: "error.c100", - uppercase: false, - type: "color", - mr: 2, -})``; - -type EditSectionProps = { - error?: Error; - value: string; - disabled?: boolean; - - onOverride: () => void; - onRestore: () => void; - onChange: (_: string) => void; -}; - -const tryParse = (jsonString: string, fallback: any) => { - try { - return JSON.parse(jsonString); - } catch (e) { - return fallback; - } -}; - -const EditSection = ({ - error, - value, - onOverride, - onRestore, - onChange, - disabled, -}: EditSectionProps) => { - const { t } = useTranslation(); - const handleSwitchChange = useCallback( - newVal => { - onChange(JSON.stringify({ ...JSON.parse(value), enabled: newVal })); - }, - [value, onChange], - ); - return ( - - {error ? ( - - {error.toString()} - - ) : null} - ( - - - - )} - /> - - - - - - ); -}; - -export default function DebugFeatureFlags() { - const { t } = useTranslation(); - const featureFlagsProvider = useFeatureFlags(); - const [error, setError] = useState(null); - const [focusedName, setFocusedName] = useState(null); - const [hiddenFlagName, setHiddenFlagName] = useState(null); - const [searchInput, setSearchInput] = useState(""); - const [inputValues, setInputValues] = useState<{ - [key in FeatureId | string]?: string | undefined; - }>({}); - - const featureFlags = useMemo(() => { - const features: { [key in FeatureId | string]: Feature } = {}; - const featureKeys = Object.keys(defaultFeatures); - if (hiddenFlagName && !featureKeys.includes(hiddenFlagName)) - featureKeys.push(hiddenFlagName); - featureKeys.forEach((key: FeatureId | string) => { - const value = featureFlagsProvider.getFeature(key as FeatureId); - if (value) { - features[key] = value; - } - }); - return features; - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [focusedName, featureFlagsProvider, hiddenFlagName]); - - const handleInputChange = useCallback( - value => { - setError(null); - if (!focusedName) return; - setInputValues(currentValues => ({ - ...currentValues, - [focusedName]: value, - })); - }, - [focusedName], - ); - - const handleRestoreFeature = useCallback(() => { - setError(null); - if (!focusedName) return; - setInputValues(currentValues => ({ - ...currentValues, - [focusedName]: undefined, - })); - featureFlagsProvider.resetFeature(focusedName); - }, [featureFlagsProvider, focusedName]); - - const handleOverrideFeature = useCallback(() => { - setError(null); - if (!focusedName) return; - try { - // Nb if value is invalid or missing, JSON parse will fail - const newValue = JSON.parse(inputValues[focusedName]); - featureFlagsProvider.overrideFeature(focusedName as FeatureId, newValue); - } catch (e) { - setError(e); - } - }, [inputValues, focusedName, featureFlagsProvider]); - - const handleAddHiddenFlag = useCallback( - value => { - setHiddenFlagName(value); - setSearchInput(value); - }, - [setSearchInput, setHiddenFlagName], - ); - - const handleSearch = useCallback(value => { - setSearchInput(value); - }, []); - - const filteredFlags = useMemo(() => { - return Object.entries(featureFlags) - .sort((a, b) => a[0].localeCompare(b[0])) - .filter( - ([name]) => - !searchInput || includes(lowerCase(name), lowerCase(searchInput)), - ); - }, [featureFlags, searchInput]); - - return ( - - - {t("settings.debug.featureFlagsTitle")} - - Legend: - enabled flag - disabled flag - - - - - ( - - - - )} - placeholder="Add missing flag" - onChange={handleAddHiddenFlag} - autoCapitalize="none" - /> - - {filteredFlags.length === 0 ? ( - {`No flag matching "${searchInput}"`} - ) : null} - {filteredFlags.map(([flagName, value], index, arr) => { - const isFocused = focusedName === flagName; - const isLast = index === arr.length - 1; - return ( - - setFocusedName(isFocused ? null : flagName)} - > - - {value?.enabled ? ( - {flagName} - ) : ( - {flagName} - )} - {value?.overridesRemote && ( - - overridden locally - - )} - {value?.enabledOverriddenForCurrentLanguage && ( - - disabled for current language - - )} - - - {isFocused ? ( - - ) : null} - {isFocused && ( - - - - {JSON.stringify(featureFlags[flagName], null, 2)} - - - - )} - {!isLast && isFocused ? : null} - - ); - })} - - - ); -} - -const styles = StyleSheet.create({ - root: { - padding: 16, - }, -}); diff --git a/apps/ledger-live-mobile/src/screens/FeatureFlagsSettings/FeatureFlagDetails.tsx b/apps/ledger-live-mobile/src/screens/FeatureFlagsSettings/FeatureFlagDetails.tsx new file mode 100644 index 00000000000..4647f48f9a6 --- /dev/null +++ b/apps/ledger-live-mobile/src/screens/FeatureFlagsSettings/FeatureFlagDetails.tsx @@ -0,0 +1,85 @@ +import React from "react"; +import { Pressable, View } from "react-native"; +import { useFeatureFlags } from "@ledgerhq/live-common/featureFlags/index"; +import type { FeatureId } from "@ledgerhq/types-live"; + +import { Flex, Box, Tag } from "@ledgerhq/native-ui"; +import styled from "styled-components/native"; +import FeatureFlagEdit from "./FeatureFlagEdit"; + +export const Divider = styled(Box).attrs({ + width: "100%", + my: 4, + height: 1, + bg: "neutral.c50", +})``; + +export const TagEnabled = styled(Tag).attrs({ + bg: "success.c100", + uppercase: false, + type: "color", + mr: 2, +})``; + +export const TagDisabled = styled(Tag).attrs({ + bg: "error.c100", + uppercase: false, + type: "color", + mr: 2, +})``; + +type Props = { + flagName: FeatureId; + focused?: boolean; + setFocusedName: (arg0: string | undefined) => void; + isLast?: boolean; +}; + +const FeatureFlagDetails: React.FC = props => { + const { flagName, focused, setFocusedName, isLast } = props; + + const { getFeature } = useFeatureFlags(); + const flagValue = getFeature(flagName as FeatureId); + + if (!flagValue) return null; + + const { + overriddenByEnv, + overridesRemote, + enabledOverriddenForCurrentLanguage, + } = flagValue; + + return ( + + setFocusedName(focused ? undefined : flagName)}> + + {flagValue?.enabled ? ( + {flagName} + ) : ( + {flagName} + )} + {overriddenByEnv ? ( + + overridden by env + + ) : overridesRemote ? ( + + overridden locally + + ) : null} + {enabledOverriddenForCurrentLanguage && ( + + disabled for current language + + )} + + + {focused ? ( + + ) : null} + {!isLast && focused ? : null} + + ); +}; + +export default FeatureFlagDetails; diff --git a/apps/ledger-live-mobile/src/screens/FeatureFlagsSettings/FeatureFlagEdit.tsx b/apps/ledger-live-mobile/src/screens/FeatureFlagsSettings/FeatureFlagEdit.tsx new file mode 100644 index 00000000000..18830a40682 --- /dev/null +++ b/apps/ledger-live-mobile/src/screens/FeatureFlagsSettings/FeatureFlagEdit.tsx @@ -0,0 +1,112 @@ +import React, { useCallback, useState, useMemo } from "react"; +import { ScrollView } from "react-native"; +import { useTranslation } from "react-i18next"; +import { useFeatureFlags } from "@ledgerhq/live-common/featureFlags/index"; +import type { FeatureId, Feature } from "@ledgerhq/types-live"; + +import { BaseInput, Text, Flex, Button, Switch } from "@ledgerhq/native-ui"; +import { InputRenderRightContainer } from "@ledgerhq/native-ui/components/Form/Input/BaseInput"; +import Alert from "../../components/Alert"; + +const FeatureFlagEdit: React.FC<{ + flagName: FeatureId; + flagValue: Feature; +}> = props => { + const { flagName, flagValue } = props; + const [error, setError] = useState(); + const [inputValue, setInputValue] = useState(undefined); + + const stringifiedFlagValue = useMemo( + () => (flagValue ? JSON.stringify(flagValue) : undefined), + [flagValue], + ); + const inputValueDefaulted = inputValue || stringifiedFlagValue; + + const { + overriddenByEnv, + overridesRemote, + enabledOverriddenForCurrentLanguage, + ...pureValue + } = flagValue || {}; + + const featureFlagsProvider = useFeatureFlags(); + + const { t } = useTranslation(); + + const handleInputChange = useCallback(value => { + setError(undefined); + setInputValue(value); + }, []); + + const handleRestoreFeature = useCallback(() => { + setError(undefined); + setInputValue(undefined); + featureFlagsProvider.resetFeature(flagName); + }, [featureFlagsProvider, flagName]); + + const handleOverrideFeature = useCallback(() => { + setError(undefined); + try { + // Nb if value is invalid or missing, JSON parse will fail + const newValue = inputValue ? JSON.parse(inputValue) : undefined; + featureFlagsProvider.overrideFeature(flagName, newValue); + } catch (e) { + setError(e); + } + }, [inputValue, flagName, featureFlagsProvider]); + + const isChecked = useMemo(() => { + if (!inputValueDefaulted) return false; + try { + return JSON.parse(inputValueDefaulted)?.enabled; + } catch (e) { + return false; + } + }, [inputValueDefaulted]); + + const handleSwitchChange = useCallback( + enabled => { + featureFlagsProvider.overrideFeature(flagName, { ...flagValue, enabled }); + }, + [featureFlagsProvider, flagName, flagValue], + ); + + return ( + + {error ? ( + + {error.toString()} + + ) : null} + ( + + + + )} + /> + + + + + + + {JSON.stringify(pureValue, null, 2)} + + + + ); +}; + +export default FeatureFlagEdit; diff --git a/apps/ledger-live-mobile/src/screens/FeatureFlagsSettings/index.tsx b/apps/ledger-live-mobile/src/screens/FeatureFlagsSettings/index.tsx new file mode 100644 index 00000000000..1094a829cee --- /dev/null +++ b/apps/ledger-live-mobile/src/screens/FeatureFlagsSettings/index.tsx @@ -0,0 +1,107 @@ +import React, { useCallback, useState, useMemo } from "react"; +import { StyleSheet, View } from "react-native"; +import { useTranslation } from "react-i18next"; +import { defaultFeatures } from "@ledgerhq/live-common/featureFlags/index"; +import type { FeatureId } from "@ledgerhq/types-live"; + +import { BaseInput, Text, Flex, SearchInput, Icons } from "@ledgerhq/native-ui"; +import { includes, lowerCase } from "lodash"; +import { InputRenderLeftContainer } from "@ledgerhq/native-ui/components/Form/Input/BaseInput"; +import NavigationScrollView from "../../components/NavigationScrollView"; +import FeatureFlagDetails, { + Divider, + TagDisabled, + TagEnabled, +} from "./FeatureFlagDetails"; + +export default function DebugFeatureFlags() { + const { t } = useTranslation(); + const [focusedName, setFocusedName] = useState(); + const [hiddenFlagName, setHiddenFlagName] = useState(null); + const [searchInput, setSearchInput] = useState(""); + + const featureFlags = useMemo(() => { + const featureKeys = Object.keys(defaultFeatures); + if (hiddenFlagName && !featureKeys.includes(hiddenFlagName)) + featureKeys.push(hiddenFlagName); + return featureKeys; + }, [hiddenFlagName]); + + const handleAddHiddenFlag = useCallback( + value => { + setHiddenFlagName(value); + setSearchInput(value); + }, + [setSearchInput, setHiddenFlagName], + ); + + const handleSearch = useCallback(value => { + setSearchInput(value); + }, []); + + const filteredFlags = useMemo(() => { + return featureFlags + .sort((a, b) => a[0].localeCompare(b[0])) + .filter( + name => + !searchInput || includes(lowerCase(name), lowerCase(searchInput)), + ); + }, [featureFlags, searchInput]); + + const content = useMemo( + () => + filteredFlags.map((flagName, index, arr) => ( + + )), + [filteredFlags, focusedName], + ); + + return ( + + + {t("settings.debug.featureFlagsTitle")} + + Legend: + enabled flag + disabled flag + + + + + ( + + + + )} + placeholder="Add missing flag" + onChange={handleAddHiddenFlag} + autoCapitalize="none" + /> + + {filteredFlags.length === 0 ? ( + {`No flag matching "${searchInput}"`} + ) : null} + {content} + + + ); +} + +const styles = StyleSheet.create({ + root: { + padding: 16, + }, +}); diff --git a/libs/ledger-live-common/src/env.ts b/libs/ledger-live-common/src/env.ts index 96c2c025c66..c143cd18b99 100644 --- a/libs/ledger-live-common/src/env.ts +++ b/libs/ledger-live-common/src/env.ts @@ -29,6 +29,23 @@ const boolParser = (v: unknown): boolean | null | undefined => { const stringParser = (v: unknown): string | null | undefined => typeof v === "string" ? v : undefined; +type JSONValue = + | string + | number + | boolean + | null + | { [x: string]: JSONValue } + | Array; + +const jsonParser = (v: unknown): JSONValue | undefined => { + try { + if (typeof v !== "string") throw new Error(); + return JSON.parse(v); + } catch (e) { + return undefined; + } +}; + const envDefinitions = { ANALYTICS_CONSOLE: { def: false, @@ -644,6 +661,11 @@ const envDefinitions = { parser: boolParser, desc: "use the staging URL for the learn page", }, + FEATURE_FLAGS: { + def: "", + parser: jsonParser, + desc: "key value map for feature flags: {[key in FeatureId]?: Feature]}", + }, }; const getDefinition = (name: string): EnvDef | null | undefined => diff --git a/libs/ledgerjs/packages/types-live/README.md b/libs/ledgerjs/packages/types-live/README.md index 69b66c83dc8..41e4d8f1359 100644 --- a/libs/ledgerjs/packages/types-live/README.md +++ b/libs/ledgerjs/packages/types-live/README.md @@ -60,6 +60,7 @@ Ledger Live main types. * [languages_blacklisted](#languages_blacklisted) * [enabledOverriddenForCurrentLanguage](#enabledoverriddenforcurrentlanguage) * [overridesRemote](#overridesremote) + * [overriddenByEnv](#overriddenbyenv) * [params](#params) * [DefaultFeatures](#defaultfeatures) * [LedgerScriptParams](#ledgerscriptparams) @@ -493,7 +494,7 @@ Type: (`"learn"` | `"pushNotifications"` | `"llmUsbFirmwareUpdate"` | `"ratings" We use objects instead of direct booleans for potential future improvements like feature versioning etc -Type: {enabled: [boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean), desktop_version: [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)?, enabledOverriddenForCurrentDesktopVersion: [boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean)?, languages_whitelisted: \[[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)]?, languages_blacklisted: \[[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)]?, enabledOverriddenForCurrentLanguage: [boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean)?, overridesRemote: [boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean)?, params: any?} +Type: {enabled: [boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean), desktop_version: [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)?, enabledOverriddenForCurrentDesktopVersion: [boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean)?, languages_whitelisted: \[[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)]?, languages_blacklisted: \[[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)]?, enabledOverriddenForCurrentLanguage: [boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean)?, overridesRemote: [boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean)?, overriddenByEnv: [boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean)?, params: any?} #### Properties @@ -504,6 +505,7 @@ Type: {enabled: [boolean](https://developer.mozilla.org/docs/Web/JavaScript/Refe * `languages_blacklisted` **\[[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)]?** * `enabledOverriddenForCurrentLanguage` **[boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean)?** * `overridesRemote` **[boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean)?** +* `overriddenByEnv` **[boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean)?** * `params` **any?** #### enabled @@ -566,6 +568,12 @@ Whether the remote value of this object was overriden locally Type: [boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean) +#### overriddenByEnv + +Whether the remote value of this object was overriden by an environment variable + +Type: [boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean) + #### params Additional params @@ -1403,3 +1411,925 @@ Type: {errors: Record<[string](https://developer.mozilla.org/docs/Web/JavaScript * `totalSpent` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)** * `useAllAmount` **[boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean)?** * `recipientIsReadOnly` **[boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean)?** + +# <<<<<<< 459e02d258e1aa0e3d946e951b104da7ca6ab275 Type: {enabled: [boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean), desktop_version: [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)?, enabledOverriddenForCurrentDesktopVersion: [boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean)?, languages_whitelisted: \[[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)]?, languages_blacklisted: \[[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)]?, enabledOverriddenForCurrentLanguage: [boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean)?, overridesRemote: [boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean)?, params: any?} + +Type: {enabled: [boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean), languages_whitelisted: \[[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)]?, languages_blacklisted: \[[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)]?, enabledOverriddenForCurrentLanguage: [boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean)?, overridesRemote: [boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean)?, overriddenByEnv: [boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean)?, params: any?} + +> > > > > > > Add env variable FEATURE_FLAGS for overriding flags with env + +#### Properties + +* `enabled` **[boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean)** +* `desktop_version` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)?** +* `enabledOverriddenForCurrentDesktopVersion` **[boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean)?** +* `languages_whitelisted` **\[[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)]?** +* `languages_blacklisted` **\[[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)]?** +* `enabledOverriddenForCurrentLanguage` **[boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean)?** +* `overridesRemote` **[boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean)?** +* `overriddenByEnv` **[boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean)?** +* `params` **any?** + +#### enabled + +If false, the feature is disabled (for every languages regardless of the languages_whitelisted option) + +Type: [boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean) + +#### desktop_version + +The `desktop_version` option is desktop specific, it has no impact on mobile + +Type: [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String) + +#### desktop_version + +If set, the feature is disabled when the desktop app version does not satisfies this param + +Type: [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String) + +#### desktop_version + +It should respect the semantic versioning specification (https://semver.org/) + +Type: [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String) + +#### enabledOverriddenForCurrentDesktopVersion + +Whether the remote value of `enabled` was overriden due to `desktop_version` + +Type: [boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean) + +#### languages_whitelisted + +You can optionnally use one of the two following options (languages_whitelisted and languages_blacklisted) (Only implemented on mobile for now) + +Type: \[[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)] + +#### languages_whitelisted + +List of languages for which the feature is enabled (it will be disabled by default for all of the others) + +Type: \[[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)] + +#### languages_blacklisted + +List of languages for which the feature is disabled + +Type: \[[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)] + +#### enabledOverriddenForCurrentLanguage + +Whether the remote value of `enabled` was overriden due to `languages_whitelisted` or `languages_blacklisted` + +Type: [boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean) + +#### overridesRemote + +Whether the remote value of this object was overriden locally + +Type: [boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean) + +#### overriddenByEnv + +Whether the remote value of this object was overriden by an environment variable + +Type: [boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean) + +#### params + +Additional params + +Type: any + +### DefaultFeatures + +Type: any + +### LedgerScriptParams + +Type: {firmware: [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String), firmwareKey: [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String), delete: [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)?, deleteKey: [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)?, targetId: ([string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String) | [number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number))?, hash: [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String), perso: [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)} + +#### Properties + +* `firmware` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)** +* `firmwareKey` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)** +* `delete` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)?** +* `deleteKey` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)?** +* `targetId` **([string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String) | [number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number))?** +* `hash` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)** +* `perso` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)** + +### DeviceInfo + +Type: {mcuVersion: [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String), version: [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String), majMin: [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String), targetId: ([string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String) | [number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)), isBootloader: [boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean), isRecoveryMode: [boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean)?, isOSU: [boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean), providerName: ([string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String) | null | [undefined](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/undefined)), managerAllowed: [boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean), pinValidated: [boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean), seVersion: [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)?, mcuBlVersion: [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)?, mcuTargetId: [number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)?, seTargetId: [number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)?, onboarded: [boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean)?, hasDevFirmware: [boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean)?, bootloaderVersion: [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)?, hardwareVersion: [number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)?, languageId: [number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)?} + +#### Properties + +* `mcuVersion` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)** +* `version` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)** +* `majMin` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)** +* `targetId` **([string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String) | [number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number))** +* `isBootloader` **[boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean)** +* `isRecoveryMode` **[boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean)?** +* `isOSU` **[boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean)** +* `providerName` **([string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String) | null | [undefined](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/undefined))** +* `managerAllowed` **[boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean)** +* `pinValidated` **[boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean)** +* `seVersion` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)?** +* `mcuBlVersion` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)?** +* `mcuTargetId` **[number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)?** +* `seTargetId` **[number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)?** +* `onboarded` **[boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean)?** +* `hasDevFirmware` **[boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean)?** +* `bootloaderVersion` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)?** +* `hardwareVersion` **[number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)?** +* `languageId` **[number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)?** + +### DeviceModelInfo + +Type: {modelId: DeviceModelId, deviceInfo: [DeviceInfo](#deviceinfo), apps: [Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array)<{name: [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String), version: [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)}>} + +#### Properties + +* `modelId` **DeviceModelId** +* `deviceInfo` **[DeviceInfo](#deviceinfo)** +* `apps` **[Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array)<{name: [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String), version: [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)}>** + +### DeviceVersion + +Type: {id: Id, name: [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String), display_name: [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String), target_id: [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String), description: [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String), device: Id, providers: [Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array)\, mcu_versions: [Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array)\, se_firmware_final_versions: [Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array)\, osu_versions: [Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array)\, application_versions: [Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array)\, date_creation: [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String), date_last_modified: [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)} + +#### Properties + +* `id` **Id** +* `name` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)** +* `display_name` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)** +* `target_id` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)** +* `description` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)** +* `device` **Id** +* `providers` **[Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array)\** +* `mcu_versions` **[Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array)\** +* `se_firmware_final_versions` **[Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array)\** +* `osu_versions` **[Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array)\** +* `application_versions` **[Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array)\** +* `date_creation` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)** +* `date_last_modified` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)** + +### McuVersion + +Type: {id: Id, mcu: Id, name: [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String), description: ([string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String) | null | [undefined](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/undefined)), providers: [Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array)\, from_bootloader_version: [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String), device_versions: [Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array)\, se_firmware_final_versions: [Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array)\, date_creation: [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String), date_last_modified: [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)} + +#### Properties + +* `id` **Id** +* `mcu` **Id** +* `name` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)** +* `description` **([string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String) | null | [undefined](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/undefined))** +* `providers` **[Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array)\** +* `from_bootloader_version` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)** +* `device_versions` **[Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array)\** +* `se_firmware_final_versions` **[Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array)\** +* `date_creation` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)** +* `date_last_modified` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)** + +### SeedPhraseType + +### FirmwareInfo + +Type: {isBootloader: [boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean), rawVersion: [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String), targetId: [number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number), seVersion: [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)?, mcuVersion: [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String), mcuBlVersion: [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)?, mcuTargetId: [number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)?, seTargetId: [number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)?, flags: [Buffer](https://nodejs.org/api/buffer.html), bootloaderVersion: [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)?, hardwareVersion: [number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)?, languageId: [number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)?} + +#### Properties + +* `isBootloader` **[boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean)** +* `rawVersion` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)** +* `targetId` **[number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)** +* `seVersion` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)?** +* `mcuVersion` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)** +* `mcuBlVersion` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)?** +* `mcuTargetId` **[number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)?** +* `seTargetId` **[number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)?** +* `flags` **[Buffer](https://nodejs.org/api/buffer.html)** +* `bootloaderVersion` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)?** +* `hardwareVersion` **[number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)?** +* `languageId` **[number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)?** + +### OsuFirmware + +Type: any + +### FinalFirmware + +Type: any + +### FirmwareUpdateContext + +Type: {osu: [OsuFirmware](#osufirmware), final: [FinalFirmware](#finalfirmware), shouldFlashMCU: [boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean)} + +#### Properties + +* `osu` **[OsuFirmware](#osufirmware)** +* `final` **[FinalFirmware](#finalfirmware)** +* `shouldFlashMCU` **[boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean)** + +### ApplicationVersion + +Type: {id: Id, name: [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String), version: [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String), app: Id, description: ([string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String) | null | [undefined](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/undefined)), display_name: [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String), icon: [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String), notes: ([string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String) | null | [undefined](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/undefined)), perso: [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String), hash: [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String), firmware: [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String), firmware_key: [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String), delete: [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String), delete_key: [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String), device_versions: [Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array)\, se_firmware_final_versions: [Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array)\, providers: [Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array)\, date_creation: [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String), date_last_modified: [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String), type: [AppType](#apptype)?, bytes: ([number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number) | null | [undefined](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/undefined)), warning: ([string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String) | null | [undefined](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/undefined)), currency: CryptoCurrency?} + +#### Properties + +* `id` **Id** +* `name` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)** +* `version` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)** +* `app` **Id** +* `description` **([string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String) | null | [undefined](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/undefined))** +* `display_name` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)** +* `icon` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)** +* `notes` **([string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String) | null | [undefined](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/undefined))** +* `perso` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)** +* `hash` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)** +* `firmware` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)** +* `firmware_key` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)** +* `delete` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)** +* `delete_key` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)** +* `device_versions` **[Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array)\** +* `se_firmware_final_versions` **[Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array)\** +* `providers` **[Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array)\** +* `date_creation` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)** +* `date_last_modified` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)** +* `type` **[AppType](#apptype)?** +* `bytes` **([number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number) | null | [undefined](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/undefined))** +* `warning` **([string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String) | null | [undefined](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/undefined))** +* `currency` **CryptoCurrency?** + +### Application + +Type: {id: Id, name: [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String), description: ([string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String) | null | [undefined](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/undefined)), application_versions: [Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array)<[ApplicationVersion](#applicationversion)>, providers: [Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array)\, category: Id, publisher: (Id | null | [undefined](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/undefined)), date_creation: [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String), date_last_modified: [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String), currencyId: ([string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String) | null | [undefined](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/undefined)), authorName: ([string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String) | null | [undefined](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/undefined)), supportURL: ([string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String) | null | [undefined](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/undefined)), contactURL: ([string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String) | null | [undefined](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/undefined)), sourceURL: ([string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String) | null | [undefined](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/undefined)), compatibleWalletsJSON: ([string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String) | null | [undefined](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/undefined))} + +#### Properties + +* `id` **Id** +* `name` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)** +* `description` **([string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String) | null | [undefined](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/undefined))** +* `application_versions` **[Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array)<[ApplicationVersion](#applicationversion)>** +* `providers` **[Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array)\** +* `category` **Id** +* `publisher` **(Id | null | [undefined](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/undefined))** +* `date_creation` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)** +* `date_last_modified` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)** +* `currencyId` **([string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String) | null | [undefined](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/undefined))** +* `authorName` **([string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String) | null | [undefined](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/undefined))** +* `supportURL` **([string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String) | null | [undefined](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/undefined))** +* `contactURL` **([string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String) | null | [undefined](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/undefined))** +* `sourceURL` **([string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String) | null | [undefined](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/undefined))** +* `compatibleWalletsJSON` **([string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String) | null | [undefined](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/undefined))** + +### AppType + +### App + +App is higher level on top of Application and ApplicationVersion +with all fields Live needs and in normalized form (but still serializable) + +Type: {id: Id, name: [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String), displayName: [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String), version: [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String), currencyId: ([string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String) | null | [undefined](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/undefined)), description: ([string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String) | null | [undefined](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/undefined)), dateModified: [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String), icon: [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String), authorName: ([string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String) | null | [undefined](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/undefined)), supportURL: ([string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String) | null | [undefined](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/undefined)), contactURL: ([string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String) | null | [undefined](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/undefined)), sourceURL: ([string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String) | null | [undefined](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/undefined)), compatibleWallets: [Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array)<{name: [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String), url: ([string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String) | null | [undefined](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/undefined))}>, hash: [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String), perso: [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String), firmware: [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String), firmware_key: [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String), delete: [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String), delete_key: [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String), dependencies: [Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array)<[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)>, bytes: ([number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number) | null | [undefined](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/undefined)), warning: ([string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String) | null | [undefined](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/undefined)), indexOfMarketCap: [number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number), isDevTools: [boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean), type: [AppType](#apptype)} + +#### Properties + +* `id` **Id** +* `name` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)** +* `displayName` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)** +* `version` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)** +* `currencyId` **([string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String) | null | [undefined](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/undefined))** +* `description` **([string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String) | null | [undefined](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/undefined))** +* `dateModified` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)** +* `icon` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)** +* `authorName` **([string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String) | null | [undefined](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/undefined))** +* `supportURL` **([string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String) | null | [undefined](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/undefined))** +* `contactURL` **([string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String) | null | [undefined](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/undefined))** +* `sourceURL` **([string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String) | null | [undefined](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/undefined))** +* `compatibleWallets` **[Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array)<{name: [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String), url: ([string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String) | null | [undefined](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/undefined))}>** +* `hash` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)** +* `perso` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)** +* `firmware` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)** +* `firmware_key` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)** +* `delete` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)** +* `delete_key` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)** +* `dependencies` **[Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array)<[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)>** +* `bytes` **([number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number) | null | [undefined](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/undefined))** +* `warning` **([string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String) | null | [undefined](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/undefined))** +* `indexOfMarketCap` **[number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)** +* `isDevTools` **[boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean)** +* `type` **[AppType](#apptype)** + +### Category + +Type: {id: Id, name: [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String), description: ([string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String) | null | [undefined](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/undefined)), providers: [Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array)\, applications: [Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array)\, date_creation: [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String), date_last_modified: [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)} + +#### Properties + +* `id` **Id** +* `name` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)** +* `description` **([string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String) | null | [undefined](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/undefined))** +* `providers` **[Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array)\** +* `applications` **[Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array)\** +* `date_creation` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)** +* `date_last_modified` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)** + +### SocketEvent + +Type: ({type: `"bulk-progress"`, progress: [number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number), index: [number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number), total: [number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)} | {type: `"result"`, payload: any} | {type: `"warning"`, message: [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)} | {type: `"device-permission-requested"`, wording: [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)} | {type: `"device-permission-granted"`} | {type: `"exchange-before"`, nonce: [number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number), apdu: [Buffer](https://nodejs.org/api/buffer.html)} | {type: `"exchange"`, nonce: [number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number), apdu: [Buffer](https://nodejs.org/api/buffer.html), data: [Buffer](https://nodejs.org/api/buffer.html), status: [number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)} | {type: `"opened"`} | {type: `"closed"`}) + +### NFTStandard + +Type: (`"ERC721"` | `"ERC1155"`) + +### NFTMediaSize + +Type: (`"preview"` | `"big"` | `"original"`) + +### NFTMedias + +Type: Record<[NFTMediaSize](#nftmediasize), {uri: [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String), mediaType: [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)}> + +### NFTMetadata + +Type: {tokenName: ([string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String) | null), nftName: ([string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String) | null), medias: [NFTMedias](#nftmedias), description: ([string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String) | null), properties: [Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array)\>, links: Record<[NFTMetadataLinksProviders](#nftmetadatalinksproviders), [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)>} + +#### Properties + +* `tokenName` **([string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String) | null)** +* `nftName` **([string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String) | null)** +* `medias` **[NFTMedias](#nftmedias)** +* `description` **([string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String) | null)** +* `properties` **[Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array)\>** +* `links` **Record<[NFTMetadataLinksProviders](#nftmetadatalinksproviders), [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)>** + +### NFTCollectionMetadata + +Type: {tokenName: ([string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String) | null)} + +#### Properties + +* `tokenName` **([string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String) | null)** + +### ProtoNFT + +Type: {id: [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String), tokenId: [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String), amount: BigNumber, contract: [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String), standard: [NFTStandard](#nftstandard), currencyId: CryptoCurrencyIds, metadata: [NFTMetadata](#nftmetadata)?} + +#### Properties + +* `id` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)** +* `tokenId` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)** +* `amount` **BigNumber** +* `contract` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)** +* `standard` **[NFTStandard](#nftstandard)** +* `currencyId` **CryptoCurrencyIds** +* `metadata` **[NFTMetadata](#nftmetadata)?** + +### ProtoNFTRaw + +Type: any + +### NFT + +Type: any + +### NFTMetadataLinksProviders + +Type: (`"opensea"` | `"rarible"` | `"explorer"`) + +### NFTMetadataResponse + +Type: {status: (`200` | `404` | `500`), result: ({contract: [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String), tokenId: [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String), tokenName: ([string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String) | null), nftName: ([string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String) | null), medias: [NFTMedias](#nftmedias), description: ([string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String) | null), properties: [Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array)\>, links: Record<[NFTMetadataLinksProviders](#nftmetadatalinksproviders), [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)>} | null)?} + +#### Properties + +* `status` **(`200` | `404` | `500`)** +* `result` **({contract: [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String), tokenId: [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String), tokenName: ([string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String) | null), nftName: ([string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String) | null), medias: [NFTMedias](#nftmedias), description: ([string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String) | null), properties: [Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array)\>, links: Record<[NFTMetadataLinksProviders](#nftmetadatalinksproviders), [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)>} | null)?** + +### NFTCollectionMetadataResponse + +Type: {status: (`200` | `404` | `500`), result: ({contract: [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String), tokenName: ([string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String) | null)} | null)?} + +#### Properties + +* `status` **(`200` | `404` | `500`)** +* `result` **({contract: [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String), tokenName: ([string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String) | null)} | null)?** + +### FloorPrice + +Type: {ticker: [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String), value: [number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)} + +#### Properties + +* `ticker` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)** +* `value` **[number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)** + +### OperationType + +Type: (`"IN"` | `"OUT"` | `"NONE"` | `"CREATE"` | `"REVEAL"` | `"DELEGATE"` | `"UNDELEGATE"` | `"REDELEGATE"` | `"REWARD"` | `"FEES"` | `"FREEZE"` | `"UNFREEZE"` | `"VOTE"` | `"REWARD_PAYOUT"` | `"BOND"` | `"UNBOND"` | `"WITHDRAW_UNBONDED"` | `"SET_CONTROLLER"` | `"SLASH"` | `"NOMINATE"` | `"CHILL"` | `"SUPPLY"` | `"REDEEM"` | `"APPROVE"` | `"OPT_IN"` | `"OPT_OUT"` | `"LOCK"` | `"UNLOCK"` | `"WITHDRAW"` | `"REVOKE"` | `"ACTIVATE"` | `"REGISTER"` | `"NFT_IN"` | `"NFT_OUT"`) + +### Operation + +Type: {id: [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String), hash: [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String), type: [OperationType](#operationtype), value: BigNumber, fee: BigNumber, senders: [Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array)<[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)>, recipients: [Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array)<[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)>, blockHeight: ([number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number) | null | [undefined](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/undefined)), blockHash: ([string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String) | null | [undefined](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/undefined)), transactionSequenceNumber: [number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)?, accountId: [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String), standard: ([NFTStandard](#nftstandard) | [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String))?, operator: [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)?, contract: [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)?, tokenId: [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)?, date: [Date](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Date), extra: Record<[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String), any>, hasFailed: [boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean)?, subOperations: [Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array)<[Operation](#operation)>?, internalOperations: [Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array)<[Operation](#operation)>?, nftOperations: [Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array)<[Operation](#operation)>?} + +#### Properties + +* `id` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)** +* `hash` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)** +* `type` **[OperationType](#operationtype)** +* `value` **BigNumber** +* `fee` **BigNumber** +* `senders` **[Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array)<[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)>** +* `recipients` **[Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array)<[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)>** +* `blockHeight` **([number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number) | null | [undefined](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/undefined))** +* `blockHash` **([string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String) | null | [undefined](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/undefined))** +* `transactionSequenceNumber` **[number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)?** +* `accountId` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)** +* `standard` **([NFTStandard](#nftstandard) | [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String))?** +* `operator` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)?** +* `contract` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)?** +* `tokenId` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)?** +* `date` **[Date](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Date)** +* `extra` **Record<[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String), any>** +* `hasFailed` **[boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean)?** +* `subOperations` **[Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array)<[Operation](#operation)>?** +* `internalOperations` **[Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array)<[Operation](#operation)>?** +* `nftOperations` **[Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array)<[Operation](#operation)>?** + +### OperationRaw + +Type: {id: [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String), hash: [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String), type: [OperationType](#operationtype), value: [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String), fee: [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String), senders: [Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array)<[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)>, recipients: [Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array)<[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)>, blockHeight: ([number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number) | null | [undefined](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/undefined)), blockHash: ([string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String) | null | [undefined](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/undefined)), transactionSequenceNumber: [number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)?, accountId: [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String), hasFailed: [boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean)?, standard: ([NFTStandard](#nftstandard) | [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String))?, operator: [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)?, contract: [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)?, tokenId: [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)?, date: [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String), extra: Record<[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String), any>, subOperations: [Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array)<[OperationRaw](#operationraw)>?, internalOperations: [Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array)<[OperationRaw](#operationraw)>?, nftOperations: [Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array)<[OperationRaw](#operationraw)>?} + +#### Properties + +* `id` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)** +* `hash` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)** +* `type` **[OperationType](#operationtype)** +* `value` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)** +* `fee` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)** +* `senders` **[Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array)<[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)>** +* `recipients` **[Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array)<[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)>** +* `blockHeight` **([number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number) | null | [undefined](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/undefined))** +* `blockHash` **([string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String) | null | [undefined](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/undefined))** +* `transactionSequenceNumber` **[number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)?** +* `accountId` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)** +* `hasFailed` **[boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean)?** +* `standard` **([NFTStandard](#nftstandard) | [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String))?** +* `operator` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)?** +* `contract` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)?** +* `tokenId` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)?** +* `date` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)** +* `extra` **Record<[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String), any>** +* `subOperations` **[Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array)<[OperationRaw](#operationraw)>?** +* `internalOperations` **[Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array)<[OperationRaw](#operationraw)>?** +* `nftOperations` **[Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array)<[OperationRaw](#operationraw)>?** + +### DailyOperationsSection + +Type: {day: [Date](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Date), data: [Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array)<[Operation](#operation)>} + +#### Properties + +* `day` **[Date](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Date)** +* `data` **[Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array)<[Operation](#operation)>** + +### DailyOperations + +Type: {sections: [Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array)<[DailyOperationsSection](#dailyoperationssection)>, completed: [boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean)} + +#### Properties + +* `sections` **[Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array)<[DailyOperationsSection](#dailyoperationssection)>** +* `completed` **[boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean)** + +### PaginationConfig + +A pagination config holds the user's pagination state +this is a state that usually should leave during the app lifecycle, but is not persisted +it drives the number of operations to poll in accounts +when a user paginate more, the number should accordingly be incremented +The UI should manage scrolling ahead of time (e.g. if 30 ops is displayed and UI have pages of 20 ops, the UI can already request to poll 70 ops so it have 2 pages in advance) +The UI must always do max() to keep the increasing the counter and not going back to lower value: that optim the sync to not recompute things too much + +Type: {operationsPerAccountId: Record<[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String), [number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)>?, operations: [number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)?} + +#### Properties + +* `operationsPerAccountId` **Record<[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String), [number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)>?** +* `operations` **[number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)?** + +### SyncConfig + +Type: {paginationConfig: [PaginationConfig](#paginationconfig), withoutSynchronize: [boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean)?, blacklistedTokenIds: [Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array)<[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)>?} + +#### Properties + +* `paginationConfig` **[PaginationConfig](#paginationconfig)** +* `withoutSynchronize` **[boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean)?** +* `blacklistedTokenIds` **[Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array)<[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)>?** + +### BalanceHistoryData + +Type: {date: [Date](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Date), value: [number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)} + +#### Properties + +* `date` **[Date](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Date)** +* `value` **[number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)** + +### BalanceHistory + +Type: [Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array)<[BalanceHistoryData](#balancehistorydata)> + +### BalanceHistoryRaw + +Type: [Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array)<\[[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String), [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)]> + +### BalanceHistoryWithCountervalue + +Type: [Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array)\ + +### ValueChange + +Type: {percentage: ([number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number) | null | [undefined](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/undefined)), value: [number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)} + +#### Properties + +* `percentage` **([number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number) | null | [undefined](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/undefined))** +* `value` **[number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)** + +### AccountPortfolio + +Type: {history: [BalanceHistoryWithCountervalue](#balancehistorywithcountervalue), countervalueAvailable: [boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean), countervalueReceiveSum: [number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number), countervalueSendSum: [number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number), cryptoChange: [ValueChange](#valuechange), countervalueChange: [ValueChange](#valuechange)} + +#### Properties + +* `history` **[BalanceHistoryWithCountervalue](#balancehistorywithcountervalue)** +* `countervalueAvailable` **[boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean)** +* `countervalueReceiveSum` **[number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)** +* `countervalueSendSum` **[number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)** +* `cryptoChange` **[ValueChange](#valuechange)** +* `countervalueChange` **[ValueChange](#valuechange)** + +### CurrencyPortfolio + +Type: {history: [BalanceHistoryWithCountervalue](#balancehistorywithcountervalue), countervalueAvailable: [boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean), histories: [Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array)<[BalanceHistoryWithCountervalue](#balancehistorywithcountervalue)>, accounts: [AccountLikeArray](#accountlikearray), cryptoChange: [ValueChange](#valuechange), range: [PortfolioRange](#portfoliorange), countervalueChange: [ValueChange](#valuechange)} + +#### Properties + +* `history` **[BalanceHistoryWithCountervalue](#balancehistorywithcountervalue)** +* `countervalueAvailable` **[boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean)** +* `histories` **[Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array)<[BalanceHistoryWithCountervalue](#balancehistorywithcountervalue)>** +* `accounts` **[AccountLikeArray](#accountlikearray)** +* `cryptoChange` **[ValueChange](#valuechange)** +* `range` **[PortfolioRange](#portfoliorange)** +* `countervalueChange` **[ValueChange](#valuechange)** + +### Portfolio + +Type: {balanceHistory: [BalanceHistory](#balancehistory), balanceAvailable: [boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean), availableAccounts: [Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array)<[AccountLike](#accountlike)>, unavailableCurrencies: [Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array)<(CryptoCurrency | TokenCurrency)>, accounts: [Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array)<[AccountLike](#accountlike)>, range: [PortfolioRange](#portfoliorange), histories: [Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array)<[BalanceHistoryWithCountervalue](#balancehistorywithcountervalue)>, countervalueReceiveSum: [number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number), countervalueSendSum: [number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number), countervalueChange: [ValueChange](#valuechange)} + +#### Properties + +* `balanceHistory` **[BalanceHistory](#balancehistory)** +* `balanceAvailable` **[boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean)** +* `availableAccounts` **[Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array)<[AccountLike](#accountlike)>** +* `unavailableCurrencies` **[Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array)<(CryptoCurrency | TokenCurrency)>** +* `accounts` **[Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array)<[AccountLike](#accountlike)>** +* `range` **[PortfolioRange](#portfoliorange)** +* `histories` **[Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array)<[BalanceHistoryWithCountervalue](#balancehistorywithcountervalue)>** +* `countervalueReceiveSum` **[number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)** +* `countervalueSendSum` **[number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)** +* `countervalueChange` **[ValueChange](#valuechange)** + +### PortfolioRangeConfig + +Type: {count: [number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)?, granularityId: GranularityId, startOf: function (arg0: [Date](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Date)): [Date](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Date), increment: [number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)} + +#### Properties + +* `count` **[number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)?** +* `granularityId` **GranularityId** +* `startOf` **function (arg0: [Date](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Date)): [Date](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Date)** +* `increment` **[number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)** + +### PortfolioRange + +Type: (`"all"` | `"year"` | `"month"` | `"week"` | `"day"`) + +### AssetsDistribution + +Type: {isAvailable: [boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean), list: [Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array)<{currency: (CryptoCurrency | TokenCurrency), accounts: [Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array)<[AccountLike](#accountlike)>, distribution: [number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number), amount: [number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number), countervalue: [number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)}>, showFirst: [number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number), sum: [number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)} + +#### Properties + +* `isAvailable` **[boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean)** +* `list` **[Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array)<{currency: (CryptoCurrency | TokenCurrency), accounts: [Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array)<[AccountLike](#accountlike)>, distribution: [number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number), amount: [number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number), countervalue: [number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)}>** +* `showFirst` **[number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)** +* `sum` **[number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)** + +### PostOnboardingActionId + +Unique identifier of a post onboarding action. + +### PostOnboardingAction + +All necessary information for complete integration of a post onboarding +action. + +Type: {id: [PostOnboardingActionId](#postonboardingactionid), featureFlagId: [FeatureId](#featureid)?, navigationParams: [Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array)\?, Icon: function (props: {size: [number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number), color: [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)}): any, title: [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String), description: [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String), tagLabel: [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)?, actionCompletedPopupLabel: [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String), actionCompletedHubTitle: [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String), startEvent: [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)?, startEventProperties: any?} + +#### Properties + +* `id` **[PostOnboardingActionId](#postonboardingactionid)** +* `featureFlagId` **[FeatureId](#featureid)?** +* `navigationParams` **[Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array)\?** +* `Icon` **function (props: {size: [number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number), color: [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)}): any** +* `title` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)** +* `description` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)** +* `tagLabel` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)?** +* `actionCompletedPopupLabel` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)** +* `actionCompletedHubTitle` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)** +* `startEvent` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)?** +* `startEventProperties` **any?** + +#### featureFlagId + +If this action is linked to a feature that is enabled by a feature flag, +use this property to identify the feature flag. + +Type: [FeatureId](#featureid) + +#### navigationParams + +Navigation params when the user presses the button for this action + +* In LLM, this will be used like this: + `navigation.navigate(...navigationParams)` +* In LLD, this will be used like this: + `history.push(...navigationParams)` + +Type: [Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array)\ + +#### Icon + +Icon displayed for this action in the post onboarding hub. + +Type: function (props: {size: [number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number), color: [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)}): any + +#### title + +Title displayed for this action in the post onboarding hub. + +Type: [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String) + +#### description + +Description displayed for this action in the post onboarding hub. + +Type: [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String) + +#### tagLabel + +Tag displayed for this action in the post onboarding hub. + +Type: [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String) + +#### actionCompletedPopupLabel + +Will appear in an success alert at the bottom of the post-onboarding hub +after completing this action. + +Type: [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String) + +#### actionCompletedHubTitle + +Will be used as a title success alert at the bottom of the post-onboarding +hub after completing this action. + +Type: [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String) + +#### startEvent + +Event that will be dispatched when starting this action. + +Type: [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String) + +#### startEventProperties + +Event properties that will be dispatched when starting this action. + +Type: any + +### PostOnboardingActionState + +State of a post onboarding action. + +Type: {completed: [boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean)} + +#### Properties + +* `completed` **[boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean)** + +#### completed + +Whether the user has completed this action. This will be reflected in the +UI of the post onboarding hub. + +Type: [boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean) + +### PostOnboardingState + +To be used for a redux reducer. +Keeps all necessary information about the state of the post onboarding hub +and can be persisted in storage. + +Type: {deviceModelId: (DeviceModelId | null), walletEntryPointDismissed: [boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean), actionsToComplete: [Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array)<[PostOnboardingActionId](#postonboardingactionid)>, actionsCompleted: any, lastActionCompleted: ([PostOnboardingActionId](#postonboardingactionid) | null)} + +#### Properties + +* `deviceModelId` **(DeviceModelId | null)** +* `walletEntryPointDismissed` **[boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean)** +* `actionsToComplete` **[Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array)<[PostOnboardingActionId](#postonboardingactionid)>** +* `actionsCompleted` **any** +* `lastActionCompleted` **([PostOnboardingActionId](#postonboardingactionid) | null)** + +#### deviceModelId + +Model Id of the device for which the post onboarding was started. + +Type: (DeviceModelId | null) + +#### walletEntryPointDismissed + +Did the user dismiss the post onboarding entry point on the wallet page. + +Type: [boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean) + +#### actionsToComplete + +List of all actions that have to be completed in this post onboarding +(whether they are completed or). +This is used to populate the list of actions in the post onboarding hub UI. + +Type: [Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array)<[PostOnboardingActionId](#postonboardingactionid)> + +#### actionsCompleted + +"completed" state for each action. + +Type: any + +#### lastActionCompleted + +Last action that the user has completed. + +This is used to display potentially different content in the post +onboarding hub UI depending on the last action that was completed. + +Type: ([PostOnboardingActionId](#postonboardingactionid) | null) + +### PostOnboardingHubState + +Digest of the store & list of actions into something directly consumable +by UI. (All UI data will be in there). + +Type: {deviceModelId: (DeviceModelId | null), lastActionCompleted: ([PostOnboardingAction](#postonboardingaction) | null), actionsState: [Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array)\} + +#### Properties + +* `deviceModelId` **(DeviceModelId | null)** +* `lastActionCompleted` **([PostOnboardingAction](#postonboardingaction) | null)** +* `actionsState` **[Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array)\** + +### SwapOperation + +Type: {provider: [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String), swapId: [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String), status: [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String), receiverAccountId: [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String), tokenId: [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)?, operationId: [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String), fromAmount: BigNumber, toAmount: BigNumber} + +#### Properties + +* `provider` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)** +* `swapId` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)** +* `status` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)** +* `receiverAccountId` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)** +* `tokenId` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)?** +* `operationId` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)** +* `fromAmount` **BigNumber** +* `toAmount` **BigNumber** + +### SwapOperationRaw + +Type: {provider: [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String), swapId: [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String), status: [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String), receiverAccountId: [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String), tokenId: [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)?, operationId: [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String), fromAmount: [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String), toAmount: [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)} + +#### Properties + +* `provider` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)** +* `swapId` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)** +* `status` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)** +* `receiverAccountId` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)** +* `tokenId` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)?** +* `operationId` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)** +* `fromAmount` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)** +* `toAmount` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)** + +### SignedOperation + +Type: {operation: [Operation](#operation), signature: [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String), signatureRaw: Record<[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String), any>?, expirationDate: ([Date](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Date) | null | [undefined](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/undefined))} + +#### Properties + +* `operation` **[Operation](#operation)** +* `signature` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)** +* `signatureRaw` **Record<[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String), any>?** +* `expirationDate` **([Date](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Date) | null | [undefined](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/undefined))** + +### SignedOperationRaw + +Type: {operation: [OperationRaw](#operationraw), signature: [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String), signatureRaw: Record<[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String), any>?, expirationDate: ([string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String) | null | [undefined](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/undefined))} + +#### Properties + +* `operation` **[OperationRaw](#operationraw)** +* `signature` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)** +* `signatureRaw` **Record<[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String), any>?** +* `expirationDate` **([string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String) | null | [undefined](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/undefined))** + +### SignOperationEvent + +Type: ({type: `"device-streaming"`, progress: [number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number), index: [number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number), total: [number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)} | {type: `"device-signature-requested"`} | {type: `"device-signature-granted"`} | {type: `"signed"`, signedOperation: [SignedOperation](#signedoperation)}) + +### SignOperationEventRaw + +Type: ({type: `"device-streaming"`, progress: [number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number), index: [number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number), total: [number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)} | {type: `"device-signature-requested"`} | {type: `"device-signature-granted"`} | {type: `"signed"`, signedOperation: [SignedOperationRaw](#signedoperationraw)}) + +### TransactionCommon + +Transaction is a generic object that holds all state for all transactions +there are generic fields and coin specific fields. That's why almost all fields are optionals + +Type: {amount: BigNumber, recipient: [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String), useAllAmount: [boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean)?, subAccountId: ([string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String) | null | [undefined](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/undefined))?, feesStrategy: (`"slow"` | `"medium"` | `"fast"` | `"custom"` | null)?} + +#### Properties + +* `amount` **BigNumber** +* `recipient` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)** +* `useAllAmount` **[boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean)?** +* `subAccountId` **([string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String) | null | [undefined](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/undefined))?** +* `feesStrategy` **(`"slow"` | `"medium"` | `"fast"` | `"custom"` | null)?** + +### TransactionCommonRaw + +Type: {amount: [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String), recipient: [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String), useAllAmount: [boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean)?, subAccountId: ([string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String) | null | [undefined](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/undefined))?, feesStrategy: (`"slow"` | `"medium"` | `"fast"` | `"custom"` | null)?} + +#### Properties + +* `amount` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)** +* `recipient` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)** +* `useAllAmount` **[boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean)?** +* `subAccountId` **([string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String) | null | [undefined](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/undefined))?** +* `feesStrategy` **(`"slow"` | `"medium"` | `"fast"` | `"custom"` | null)?** + +### FeeStrategy + +User can have 3 differents choice for their fee +Most of the time mid is low \* 1.25 and high is low \* 1.5 +They are some exception as eth that got his own meter + +Type: {amount: BigNumber, displayedAmount: BigNumber?, label: [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String), unit: Unit?} + +#### Properties + +* `amount` **BigNumber** +* `displayedAmount` **BigNumber?** +* `label` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)** +* `unit` **Unit?** + +### TransactionStatusCommon + +TransactionStatus is a view of Transaction with general info to be used on the UI and status info. + +Type: {errors: Record<[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String), [Error](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Error)>, warnings: Record<[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String), [Error](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Error)>, estimatedFees: BigNumber, amount: BigNumber, totalSpent: BigNumber, recipientIsReadOnly: [boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean)?} + +#### Properties + +* `errors` **Record<[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String), [Error](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Error)>** +* `warnings` **Record<[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String), [Error](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Error)>** +* `estimatedFees` **BigNumber** +* `amount` **BigNumber** +* `totalSpent` **BigNumber** +* `recipientIsReadOnly` **[boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean)?** + +### TransactionStatusCommonRaw + +Type: {errors: Record<[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String), [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)>, warnings: Record<[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String), [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)>, estimatedFees: [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String), amount: [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String), totalSpent: [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String), useAllAmount: [boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean)?, recipientIsReadOnly: [boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean)?} + +#### Properties + +* `errors` **Record<[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String), [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)>** +* `warnings` **Record<[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String), [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)>** +* `estimatedFees` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)** +* `amount` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)** +* `totalSpent` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)** +* `useAllAmount` **[boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean)?** +* `recipientIsReadOnly` **[boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean)?** diff --git a/libs/ledgerjs/packages/types-live/src/feature.ts b/libs/ledgerjs/packages/types-live/src/feature.ts index 035000286c6..5170a969398 100644 --- a/libs/ledgerjs/packages/types-live/src/feature.ts +++ b/libs/ledgerjs/packages/types-live/src/feature.ts @@ -49,6 +49,8 @@ export type Feature = { enabledOverriddenForCurrentLanguage?: boolean; /** Whether the remote value of this object was overriden locally */ overridesRemote?: boolean; + /** Whether the remote value of this object was overriden by an environment variable */ + overriddenByEnv?: boolean; /** Additional params */ params?: any; }; From e1aefef3ff7dc6faa90988912fcaacf39adb13d8 Mon Sep 17 00:00:00 2001 From: Olivier Freyssinet Date: Thu, 13 Oct 2022 11:59:12 +0200 Subject: [PATCH 2/5] Fix styling of LLD feature flags settings, trim input & hide programatically set flag keys --- .../FeatureFlagsSettings/FeatureFlagEdit.tsx | 20 +++- .../Developer/FeatureFlagsSettings/index.tsx | 94 ++++++++++--------- 2 files changed, 66 insertions(+), 48 deletions(-) diff --git a/apps/ledger-live-desktop/src/renderer/screens/settings/sections/Developer/FeatureFlagsSettings/FeatureFlagEdit.tsx b/apps/ledger-live-desktop/src/renderer/screens/settings/sections/Developer/FeatureFlagsSettings/FeatureFlagEdit.tsx index 3975d2c43b3..4e474d3e34d 100644 --- a/apps/ledger-live-desktop/src/renderer/screens/settings/sections/Developer/FeatureFlagsSettings/FeatureFlagEdit.tsx +++ b/apps/ledger-live-desktop/src/renderer/screens/settings/sections/Developer/FeatureFlagsSettings/FeatureFlagEdit.tsx @@ -16,13 +16,23 @@ const FeatureFlagEdit: React.FC<{ flagName: FeatureId; flagValue: Feature }> = p const [error, setError] = useState(); const [inputValue, setInputValue] = useState(undefined); - const stringifiedFlagValue = useMemo(() => (flagValue ? JSON.stringify(flagValue) : undefined), [ - flagValue, + /** + * pureValue is the value of the flag without the keys set programmatically + * by Legder Live. + * */ + const { + overriddenByEnv, // eslint-disable-line @typescript-eslint/no-unused-vars + overridesRemote, // eslint-disable-line @typescript-eslint/no-unused-vars + enabledOverriddenForCurrentLanguage, // eslint-disable-line @typescript-eslint/no-unused-vars + enabledOverriddenForCurrentDesktopVersion, // eslint-disable-line @typescript-eslint/no-unused-vars + ...pureValue + } = flagValue || {}; + + const stringifiedPureValue = useMemo(() => (pureValue ? JSON.stringify(pureValue) : undefined), [ + pureValue, ]); - const inputValueDefaulted = inputValue || stringifiedFlagValue; - const { overriddenByEnv, overridesRemote, enabledOverriddenForCurrentLanguage, ...pureValue } = // eslint-disable-line - flagValue || {}; + const inputValueDefaulted = inputValue || stringifiedPureValue; const featureFlagsProvider = useFeatureFlags(); diff --git a/apps/ledger-live-desktop/src/renderer/screens/settings/sections/Developer/FeatureFlagsSettings/index.tsx b/apps/ledger-live-desktop/src/renderer/screens/settings/sections/Developer/FeatureFlagsSettings/index.tsx index 99cdf0c79c5..416d11086c0 100644 --- a/apps/ledger-live-desktop/src/renderer/screens/settings/sections/Developer/FeatureFlagsSettings/index.tsx +++ b/apps/ledger-live-desktop/src/renderer/screens/settings/sections/Developer/FeatureFlagsSettings/index.tsx @@ -6,25 +6,24 @@ import { SettingsSectionRow as Row } from "../../../SettingsSection"; import { Input, Icons, Flex, SearchInput } from "@ledgerhq/react-ui"; import { FeatureId } from "@ledgerhq/types-live"; import { InputRenderLeftContainer } from "@ledgerhq/react-ui/components/form/BaseInput/index"; -import { includes, lowerCase } from "lodash"; -import { withV2StyleProvider } from "~/renderer/styles/StyleProvider"; +import { includes, lowerCase, trim } from "lodash"; import { withV3StyleProvider } from "~/renderer/styles/StyleProviderV3"; import FeatureFlagDetails from "./FeatureFlagDetails"; -const OldButton = withV2StyleProvider(ButtonV2); - -const FeatureFlagsSettings = () => { +const Content = withV3StyleProvider((props: { visible?: boolean }) => { const { t } = useTranslation(); - const [visible, setVisible] = useState(false); const [focusedName, setFocusedName] = useState(); const [hiddenFlagName, setHiddenFlagName] = useState(null); const [searchInput, setSearchInput] = useState(""); + const trimmedHiddenFlagName = hiddenFlagName ? trim(hiddenFlagName) : ""; + const featureFlags = useMemo(() => { const featureKeys = Object.keys(defaultFeatures); - if (hiddenFlagName && !featureKeys.includes(hiddenFlagName)) featureKeys.push(hiddenFlagName); + if (trimmedHiddenFlagName && !featureKeys.includes(trimmedHiddenFlagName)) + featureKeys.push(trimmedHiddenFlagName); return featureKeys; - }, [hiddenFlagName]); + }, [trimmedHiddenFlagName]); const handleAddHiddenFlag = useCallback( value => { @@ -40,10 +39,6 @@ const FeatureFlagsSettings = () => { .filter(name => !searchInput || includes(lowerCase(name), lowerCase(searchInput))); }, [featureFlags, searchInput]); - const handleClick = useCallback(() => { - setVisible(!visible); - }, [visible]); - const content = useMemo( () => filteredFlags.map(flagName => ( @@ -57,46 +52,59 @@ const FeatureFlagsSettings = () => { [filteredFlags, focusedName], ); + return ( + + {t("settings.developer.featureFlagsDesc")} + {!props.visible ? null : ( + <> + + ( + + + + )} + clearable + placeholder={ + 'Add missing flag by name (type the flag name in camelCase without the "feature" prefix)' + } + value={hiddenFlagName} + onChange={handleAddHiddenFlag} + /> + + {content} + + )} + + ); +}); + +const FeatureFlagsSettings = () => { + const { t } = useTranslation(); + const [visible, setVisible] = useState(false); + + const handleClick = useCallback(() => { + setVisible(!visible); + }, [visible]); + return ( - {t("settings.developer.featureFlagsDesc")} - {!visible ? null : ( - <> - - ( - - - - )} - clearable - placeholder="Add missing flag" - value={hiddenFlagName} - onChange={handleAddHiddenFlag} - /> - - {content} - - )} - - } + desc={} > - + {visible ? "Hide" : "Show"} - + ); }; -export default withV3StyleProvider(FeatureFlagsSettings); +export default FeatureFlagsSettings; From 4723ae433094f3dac27c41fbeb0355f6b5dd2460 Mon Sep 17 00:00:00 2001 From: Olivier Freyssinet Date: Thu, 13 Oct 2022 12:04:12 +0200 Subject: [PATCH 3/5] LLM feature flag settings: trim add flag input & hide programatically set flag keys --- .../FeatureFlagsSettings/FeatureFlagEdit.tsx | 20 +++++++++---------- .../screens/FeatureFlagsSettings/index.tsx | 11 +++++----- 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/apps/ledger-live-mobile/src/screens/FeatureFlagsSettings/FeatureFlagEdit.tsx b/apps/ledger-live-mobile/src/screens/FeatureFlagsSettings/FeatureFlagEdit.tsx index 18830a40682..f8c0fad6a91 100644 --- a/apps/ledger-live-mobile/src/screens/FeatureFlagsSettings/FeatureFlagEdit.tsx +++ b/apps/ledger-live-mobile/src/screens/FeatureFlagsSettings/FeatureFlagEdit.tsx @@ -16,18 +16,18 @@ const FeatureFlagEdit: React.FC<{ const [error, setError] = useState(); const [inputValue, setInputValue] = useState(undefined); - const stringifiedFlagValue = useMemo( - () => (flagValue ? JSON.stringify(flagValue) : undefined), - [flagValue], + /** + * pureValue is the value of the flag without the keys set programmatically + * by Legder Live. + * */ + const { overriddenByEnv, overridesRemote, ...pureValue } = flagValue || {}; + + const stringifiedPureValue = useMemo( + () => (pureValue ? JSON.stringify(pureValue) : undefined), + [pureValue], ); - const inputValueDefaulted = inputValue || stringifiedFlagValue; - const { - overriddenByEnv, - overridesRemote, - enabledOverriddenForCurrentLanguage, - ...pureValue - } = flagValue || {}; + const inputValueDefaulted = inputValue || stringifiedPureValue; const featureFlagsProvider = useFeatureFlags(); diff --git a/apps/ledger-live-mobile/src/screens/FeatureFlagsSettings/index.tsx b/apps/ledger-live-mobile/src/screens/FeatureFlagsSettings/index.tsx index 1094a829cee..5be68749e82 100644 --- a/apps/ledger-live-mobile/src/screens/FeatureFlagsSettings/index.tsx +++ b/apps/ledger-live-mobile/src/screens/FeatureFlagsSettings/index.tsx @@ -5,7 +5,7 @@ import { defaultFeatures } from "@ledgerhq/live-common/featureFlags/index"; import type { FeatureId } from "@ledgerhq/types-live"; import { BaseInput, Text, Flex, SearchInput, Icons } from "@ledgerhq/native-ui"; -import { includes, lowerCase } from "lodash"; +import { includes, lowerCase, trim } from "lodash"; import { InputRenderLeftContainer } from "@ledgerhq/native-ui/components/Form/Input/BaseInput"; import NavigationScrollView from "../../components/NavigationScrollView"; import FeatureFlagDetails, { @@ -18,18 +18,19 @@ export default function DebugFeatureFlags() { const { t } = useTranslation(); const [focusedName, setFocusedName] = useState(); const [hiddenFlagName, setHiddenFlagName] = useState(null); + const trimmedHiddenFlagName = hiddenFlagName ? trim(hiddenFlagName) : ""; const [searchInput, setSearchInput] = useState(""); const featureFlags = useMemo(() => { const featureKeys = Object.keys(defaultFeatures); - if (hiddenFlagName && !featureKeys.includes(hiddenFlagName)) - featureKeys.push(hiddenFlagName); + if (trimmedHiddenFlagName && !featureKeys.includes(trimmedHiddenFlagName)) + featureKeys.push(trimmedHiddenFlagName); return featureKeys; - }, [hiddenFlagName]); + }, [trimmedHiddenFlagName]); const handleAddHiddenFlag = useCallback( value => { - setHiddenFlagName(value); + setHiddenFlagName(trim(value)); setSearchInput(value); }, [setSearchInput, setHiddenFlagName], From 64b0e7c5554fa11adb1637d8bd352812a066bc10 Mon Sep 17 00:00:00 2001 From: Olivier Freyssinet Date: Thu, 13 Oct 2022 12:19:46 +0200 Subject: [PATCH 4/5] Add hint on how to use the "add missing flag name" input --- .../Developer/FeatureFlagsSettings/index.tsx | 16 +++++++++++----- .../src/screens/FeatureFlagsSettings/index.tsx | 14 ++++++++++++-- 2 files changed, 23 insertions(+), 7 deletions(-) diff --git a/apps/ledger-live-desktop/src/renderer/screens/settings/sections/Developer/FeatureFlagsSettings/index.tsx b/apps/ledger-live-desktop/src/renderer/screens/settings/sections/Developer/FeatureFlagsSettings/index.tsx index 416d11086c0..0b65f181964 100644 --- a/apps/ledger-live-desktop/src/renderer/screens/settings/sections/Developer/FeatureFlagsSettings/index.tsx +++ b/apps/ledger-live-desktop/src/renderer/screens/settings/sections/Developer/FeatureFlagsSettings/index.tsx @@ -3,13 +3,20 @@ import ButtonV2 from "~/renderer/components/Button"; import { useTranslation } from "react-i18next"; import { defaultFeatures } from "@ledgerhq/live-common/featureFlags/index"; import { SettingsSectionRow as Row } from "../../../SettingsSection"; -import { Input, Icons, Flex, SearchInput } from "@ledgerhq/react-ui"; +import { Input, Icons, Flex, SearchInput, Alert } from "@ledgerhq/react-ui"; import { FeatureId } from "@ledgerhq/types-live"; import { InputRenderLeftContainer } from "@ledgerhq/react-ui/components/form/BaseInput/index"; import { includes, lowerCase, trim } from "lodash"; import { withV3StyleProvider } from "~/renderer/styles/StyleProviderV3"; import FeatureFlagDetails from "./FeatureFlagDetails"; +const addFlagHint = `\ +If a feature flag is defined in the targeted Firebase environment \ +but it is missing from the following list, you can type its name in \ +the input field below and it will appear in the list. Type the \ +flag name in camelCase without the "feature" prefix.\ +`; + const Content = withV3StyleProvider((props: { visible?: boolean }) => { const { t } = useTranslation(); const [focusedName, setFocusedName] = useState(); @@ -53,7 +60,7 @@ const Content = withV3StyleProvider((props: { visible?: boolean }) => { ); return ( - + {t("settings.developer.featureFlagsDesc")} {!props.visible ? null : ( <> @@ -63,6 +70,7 @@ const Content = withV3StyleProvider((props: { visible?: boolean }) => { onChange={setSearchInput} clearable /> + ( @@ -70,9 +78,7 @@ const Content = withV3StyleProvider((props: { visible?: boolean }) => { )} clearable - placeholder={ - 'Add missing flag by name (type the flag name in camelCase without the "feature" prefix)' - } + placeholder={"Add missing flag (instructions above)"} value={hiddenFlagName} onChange={handleAddHiddenFlag} /> diff --git a/apps/ledger-live-mobile/src/screens/FeatureFlagsSettings/index.tsx b/apps/ledger-live-mobile/src/screens/FeatureFlagsSettings/index.tsx index 5be68749e82..ca435cbc9c2 100644 --- a/apps/ledger-live-mobile/src/screens/FeatureFlagsSettings/index.tsx +++ b/apps/ledger-live-mobile/src/screens/FeatureFlagsSettings/index.tsx @@ -13,6 +13,14 @@ import FeatureFlagDetails, { TagDisabled, TagEnabled, } from "./FeatureFlagDetails"; +import Alert from "../../components/Alert"; + +const addFlagHint = `\ +If a feature flag is defined in the targeted Firebase environment \ +but it is missing from the following list, you can type its name in \ +the input field below and it will appear in the list.\nType the \ +flag name in camelCase without the "feature" prefix.\ +`; export default function DebugFeatureFlags() { const { t } = useTranslation(); @@ -79,7 +87,9 @@ export default function DebugFeatureFlags() { onChange={handleSearch} autoCapitalize="none" /> - + + + ( @@ -87,7 +97,7 @@ export default function DebugFeatureFlags() { )} - placeholder="Add missing flag" + placeholder="Add missing flag (instructions above)" onChange={handleAddHiddenFlag} autoCapitalize="none" /> From 571ca9a29342fdff02439bbb883f5586fb97b0dd Mon Sep 17 00:00:00 2001 From: Olivier Freyssinet Date: Thu, 13 Oct 2022 12:24:33 +0200 Subject: [PATCH 5/5] Fix bad "value" prop in inputs --- .../sections/Developer/FeatureFlagsSettings/index.tsx | 4 ++-- .../src/screens/FeatureFlagsSettings/index.tsx | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/ledger-live-desktop/src/renderer/screens/settings/sections/Developer/FeatureFlagsSettings/index.tsx b/apps/ledger-live-desktop/src/renderer/screens/settings/sections/Developer/FeatureFlagsSettings/index.tsx index 0b65f181964..1cd762341a9 100644 --- a/apps/ledger-live-desktop/src/renderer/screens/settings/sections/Developer/FeatureFlagsSettings/index.tsx +++ b/apps/ledger-live-desktop/src/renderer/screens/settings/sections/Developer/FeatureFlagsSettings/index.tsx @@ -20,10 +20,10 @@ flag name in camelCase without the "feature" prefix.\ const Content = withV3StyleProvider((props: { visible?: boolean }) => { const { t } = useTranslation(); const [focusedName, setFocusedName] = useState(); - const [hiddenFlagName, setHiddenFlagName] = useState(null); + const [hiddenFlagName, setHiddenFlagName] = useState(""); const [searchInput, setSearchInput] = useState(""); - const trimmedHiddenFlagName = hiddenFlagName ? trim(hiddenFlagName) : ""; + const trimmedHiddenFlagName = trim(hiddenFlagName); const featureFlags = useMemo(() => { const featureKeys = Object.keys(defaultFeatures); diff --git a/apps/ledger-live-mobile/src/screens/FeatureFlagsSettings/index.tsx b/apps/ledger-live-mobile/src/screens/FeatureFlagsSettings/index.tsx index ca435cbc9c2..f2a2f362c06 100644 --- a/apps/ledger-live-mobile/src/screens/FeatureFlagsSettings/index.tsx +++ b/apps/ledger-live-mobile/src/screens/FeatureFlagsSettings/index.tsx @@ -25,8 +25,8 @@ flag name in camelCase without the "feature" prefix.\ export default function DebugFeatureFlags() { const { t } = useTranslation(); const [focusedName, setFocusedName] = useState(); - const [hiddenFlagName, setHiddenFlagName] = useState(null); - const trimmedHiddenFlagName = hiddenFlagName ? trim(hiddenFlagName) : ""; + const [hiddenFlagName, setHiddenFlagName] = useState(""); + const trimmedHiddenFlagName = trim(hiddenFlagName); const [searchInput, setSearchInput] = useState(""); const featureFlags = useMemo(() => {