<|start_filename|>src/logic/storeWrapper.js<|end_filename|>
/**
 * @overview A minimalistic wrapper around React Native's AsyncStorage.
 * @license MIT
 */
// It's based on https://github.com/jasonmerino/react-native-simple-store
// with the new React-native-async-store package
import AsyncStorage from "@react-native-async-storage/async-storage";
import { merge } from "lodash";

const CHUNKED_KEY = "_-_CHUNKED";
const CHUNK_SIZE = 1000000;

const getChunks = (str, size) => {
  const strLength = str.length;
  const numChunks = Math.ceil(strLength / size);
  const chunks = new Array(numChunks);

  let i = 0;
  let o = 0;

  for (; i < numChunks; ++i, o += size) {
    chunks[i] = str.substr(o, size);
  }

  return chunks;
};

const stringifyPairs = pairs =>
  pairs.reduce((acc, current) => {
    const key = current[0];
    const data = JSON.stringify(current[1]);

    if (data.length > CHUNK_SIZE) {
      const chunks = getChunks(data, CHUNK_SIZE);
      const numberOfChunks = chunks.length;
      return [
        ...acc,
        [current[0], CHUNKED_KEY + numberOfChunks],
        ...chunks.map((chunk, index) => [key + CHUNKED_KEY + index, chunk]),
      ];
    }
    return [...acc, [key, data]];
  }, []);

const getCompressedValue = async (key, value) => {
  try {
    if (value && value.includes(CHUNKED_KEY)) {
      const numberOfChunk = Number(value.replace(CHUNKED_KEY, ""));
      const keys = [];
      for (let i = 0; i < numberOfChunk; i++) {
        keys.push(key + CHUNKED_KEY + i);
      }
      let values = [];

      // multiget will failed when you got keys with a tons of data
      // it crash with 13 CHUNKS of 1MB string so we had splice it.
      while (keys.length) {
        values = [
          ...values,
          ...(await AsyncStorage.multiGet(keys.splice(0, 5))),
        ];
      }
      const concatString = values.reduce(
        (acc, current) => acc + current[1],
        "",
      );
      return JSON.parse(concatString);
    }
    return JSON.parse(value);
  } catch (e) {
    return undefined;
  }
};

const deviceStorage = {
  /**
   * Get a one or more value for a key or array of keys from AsyncStorage
   * @param {String|Array} key A key or array of keys
   * @return {Promise}
   */
  async get(key) {
    if (!Array.isArray(key)) {
      const value = await AsyncStorage.getItem(key);
      return getCompressedValue(key, value);
    }
    const values = await AsyncStorage.multiGet(key);
    const data = values.map(value => getCompressedValue(value[0], value[1]));
    return Promise.all(data).then(array => array.filter(Boolean));
  },

  /**
   * Save a key value pair or a series of key value pairs to AsyncStorage.
   * @param  {String|Array} key The key or an array of key/value pairs
   * @param  {Any} value The value to save
   * @return {Promise}
   */
  save(key, value) {
    let pairs = [];
    if (!Array.isArray(key)) {
      pairs.push([key, value]);
    } else {
      pairs = key.map(pair => [pair[0], pair[1]]);
    }

    return AsyncStorage.multiSet(stringifyPairs(pairs));
  },

  /**
   * Updates the value in the store for a given key in AsyncStorage. If the value is a string it will be replaced. If the value is an object it will be deep merged.
   * @param  {String} key The key
   * @param  {Value} value The value to update with
   * @return {Promise}
   */
  update(key, value) {
    return deviceStorage
      .get(key)
      .then(item =>
        deviceStorage.save(
          key,
          typeof value === "string" ? value : merge({}, item, value),
        ),
      );
  },

  /**
   * Delete the value for a given key in AsyncStorage.
   * @param  {String|Array} key The key or an array of keys to be deleted
   * @return {Promise}
   */
  async delete(key) {
    let keys;
    const existingKeys = await AsyncStorage.getAllKeys();
    if (!Array.isArray(key)) {
      keys = existingKeys.filter(existingKey => existingKey.include(key));
    } else {
      keys = existingKeys.filter(existingKey =>
        key.some(keyToDelete => existingKey.includes(keyToDelete)),
      );
    }
    return AsyncStorage.multiRemove(keys);
  },

  /**
   * Get all keys in AsyncStorage.
   * @return {Promise} A promise which when it resolves gets passed the saved keys in AsyncStorage.
   */
  keys() {
    return AsyncStorage.getAllKeys().then(keys =>
      keys.filter(key => !key.includes(CHUNKED_KEY)),
    );
  },

  /**
   * Push a value onto an array stored in AsyncStorage by key or create a new array in AsyncStorage for a key if it's not yet defined.
   * @param {String} key They key
   * @param {Any} value The value to push onto the array
   * @return {Promise}
   */
  push(key, value) {
    return deviceStorage.get(key).then(currentValue => {
      if (currentValue === null) {
        // if there is no current value populate it with the new value
        return deviceStorage.save(key, [value]);
      }
      if (Array.isArray(currentValue)) {
        return deviceStorage.save(key, [...currentValue, value]);
      }
      throw new Error(
        `Existing value for key "${key}" must be of type null or Array, received ${typeof currentValue}.`,
      );
    });
  },
};

module.exports = deviceStorage;


<|start_filename|>src/screens/Manager/AppsList/AppInstallProgress.js<|end_filename|>
import React from "react";
import { StyleSheet, View } from "react-native";

import { Trans } from "react-i18next";

import type { State } from "@ledgerhq/live-common/lib/apps";

import { useAppInstallProgress } from "@ledgerhq/live-common/lib/apps/react";

import { useTheme } from "@react-navigation/native";
import LText from "../../../components/LText";
import ProgressBar from "../../../components/ProgressBar";
import InfiniteProgressBar from "../../../components/InfiniteProgressBar";

type InstallProgressProps = {
  state: State,
  name: string,
  installing: boolean,
  updating: boolean,
};

export const InstallProgress = ({
  state,
  name,
  installing,
  updating,
}: InstallProgressProps) => {
  const { colors } = useTheme();
  const progress = useAppInstallProgress(state, name);

  return (
    <View style={styles.progressContainer}>
      <View style={styles.progressLabel}>
        <LText
          semiBold
          numberOfLines={1}
          style={[styles.appStateText, { color: colors.live }]}
        >
          <Trans
            i18nKey={
              updating
                ? "AppAction.update.loading"
                : installing
                ? "AppAction.install.loading.button"
                : "AppAction.install.loading.queued"
            }
          />
        </LText>
      </View>
      <ProgressBar
        progressColor={colors.live}
        style={styles.progressBar}
        height={6}
        progress={installing ? progress * 1e2 : 0}
      />
    </View>
  );
};

type UninstallProgressProps = {
  uninstalling: boolean,
};

export const UninstallProgress = ({ uninstalling }: UninstallProgressProps) => {
  const { colors } = useTheme();
  return (
    <View style={styles.progressContainer}>
      <View style={styles.progressLabel}>
        <LText
          semiBold
          numberOfLines={1}
          style={[styles.appStateText, { color: colors.live }]}
        >
          <Trans i18nKey="AppAction.uninstall.loading.button" />
        </LText>
      </View>
      {uninstalling ? (
        <InfiniteProgressBar
          progressColor={colors.live}
          style={styles.progressBar}
          height={6}
        />
      ) : (
        <ProgressBar
          progressColor={colors.live}
          style={styles.progressBar}
          height={6}
          progress={0}
        />
      )}
    </View>
  );
};

const styles = StyleSheet.create({
  appStateText: {
    fontSize: 13,
    lineHeight: 16,
    width: "100%",
    textAlign: "right",
  },
  progressContainer: {
    flexDirection: "column",
    zIndex: 10,
    width: "100%",
    height: 38,
  },
  progressLabel: {
    flex: 1,
    flexDirection: "row",
    justifyContent: "space-around",
    flexWrap: "nowrap",
    alignItems: "center",
  },
  progressBar: { flexShrink: 0, flexGrow: 0, flexBasis: 6 },
});


<|start_filename|>src/screens/Settings/Debug/GenerateMockAccounts.js<|end_filename|>
// @flow
import React from "react";
import sample from "lodash/sample";
import { Alert } from "react-native";
import { genAccount } from "@ledgerhq/live-common/lib/mock/account";
import { listSupportedCurrencies } from "@ledgerhq/live-common/lib/currencies";
import SettingsRow from "../../../components/SettingsRow";
import accountModel from "../../../logic/accountModel";
import { saveAccounts } from "../../../db";
import { useReboot } from "../../../context/Reboot";

async function injectMockAccountsInDB(count) {
  await saveAccounts({
    active: Array(count)
      .fill(null)
      .map(() =>
        accountModel.encode(
          genAccount(String(Math.random()), {
            currency: sample(listSupportedCurrencies()),
          }),
        ),
      ),
  });
}

export default function GenerateMockAccountsButton({
  count,
  title,
}: {
  title: string,
  count: number,
}) {
  const reboot = useReboot();
  return (
    <SettingsRow
      title={title}
      onPress={() => {
        Alert.alert(
          "This will erase existing accounts",
          "Continue?",
          [
            {
              text: "Cancel",
              onPress: () => {},
            },
            {
              text: "Ok",
              onPress: async () => {
                await injectMockAccountsInDB(count);
                reboot();
              },
            },
          ],
          { cancelable: true },
        );
      }}
    />
  );
}


<|start_filename|>src/screens/SendFunds/ScanRecipient.js<|end_filename|>
/* @flow */
import React, { PureComponent } from "react";
import { connect } from "react-redux";
import Config from "react-native-config";
import { decodeURIScheme } from "@ledgerhq/live-common/lib/currencies";
import type {
  Account,
  AccountLike,
  Transaction,
} from "@ledgerhq/live-common/lib/types";
import { getAccountBridge } from "@ledgerhq/live-common/lib/bridge";
import { ScreenName } from "../../const";
import { accountScreenSelector } from "../../reducers/accounts";
import Scanner from "../../components/Scanner";

type Props = {
  navigation: any,
  route: { params: RouteParams },
  account: AccountLike,
  parentAccount: ?Account,
};

type RouteParams = {
  accountId: string,
  transaction: Transaction,
};

type State = {};

class ScanRecipient extends PureComponent<Props, State> {
  componentDidMount() {
    if (Config.MOCK_SCAN_RECIPIENT) {
      this.onResult(Config.MOCK_SCAN_RECIPIENT);
    }
  }

  onResult = (result: string) => {
    const { account, parentAccount, route } = this.props;
    if (!account) return;
    const bridge = getAccountBridge(account, parentAccount);
    const { amount, address, currency, ...rest } = decodeURIScheme(result);
    const transaction = route.params?.transaction;
    const patch: Object = {};
    patch.recipient = address;
    if (amount) {
      patch.amount = amount;
    }
    for (const k in rest) {
      if (k in transaction) {
        patch[k] = rest[k];
      }
    }

    this.props.navigation.navigate(ScreenName.SendSelectRecipient, {
      accountId: account.id,
      parentId: parentAccount && parentAccount.id,
      transaction: bridge.updateTransaction(transaction, patch),
      justScanned: true,
    });
  };

  render() {
    const { navigation } = this.props;

    return (
      <Scanner
        navigation={navigation}
        screenName={ScreenName.SendCoin}
        onResult={this.onResult}
      />
    );
  }
}

const mapStateToProps = (state, { route }) =>
  accountScreenSelector(route)(state);

const m: React$ComponentType<{}> = connect(mapStateToProps)(ScanRecipient);

export default m;


<|start_filename|>src/families/tron/AccountBalanceSummaryFooter.js<|end_filename|>
// @flow

import React, { useCallback, useMemo, useState } from "react";
import { ScrollView, StyleSheet } from "react-native";
import { useSelector } from "react-redux";
import { useTranslation } from "react-i18next";
import { getCryptoCurrencyById } from "@ledgerhq/live-common/lib/currencies";
import { toLocaleString } from "@ledgerhq/live-common/lib/currencies/BigNumberToLocaleString";
import { getAccountUnit } from "@ledgerhq/live-common/lib/account/helpers";
import { getCryptoCurrencyIcon } from "@ledgerhq/live-common/lib/reactNative";

import type { Account } from "@ledgerhq/live-common/lib/types";

import invariant from "invariant";
import InfoModal from "../../modals/Info";
import type { ModalInfo } from "../../modals/Info";
import FreezeIcon from "../../icons/Freeze";
import BandwidthIcon from "../../icons/Bandwidth";
import EnergyIcon from "../../icons/Energy";
import CurrencyUnitValue from "../../components/CurrencyUnitValue";
import InfoItem from "../../components/BalanceSummaryInfoItem";
import { localeSelector } from "../../reducers/settings";

type Props = {
  account: Account,
};

type InfoName = "available" | "frozen" | "bandwidth" | "energy";

function AccountBalanceSummaryFooter({ account }: Props) {
  const { t } = useTranslation();
  const locale = useSelector(localeSelector);
  const [infoName, setInfoName] = useState<InfoName | typeof undefined>();
  const infoCandidates = useInfoCandidates();

  const { energy: formattedEnergy, bandwidth, tronPower } =
    account.tronResources || {};

  const { freeUsed, freeLimit, gainedUsed, gainedLimit } = bandwidth || {};

  const unit = getAccountUnit(account);

  const formattedBandwidth = useMemo(
    () =>
      freeLimit
        .plus(gainedLimit)
        .minus(gainedUsed)
        .minus(freeUsed),
    [freeLimit, gainedLimit, gainedUsed, freeUsed],
  );

  const onCloseModal = useCallback(() => {
    setInfoName(undefined);
  }, []);

  const onPressInfoCreator = useCallback(
    (infoName: InfoName) => () => setInfoName(infoName),
    [],
  );

  return (
    <ScrollView
      horizontal
      showsHorizontalScrollIndicator={false}
      style={[styles.root]}
      contentContainerStyle={{ paddingHorizontal: 16 }}
    >
      <InfoModal
        isOpened={!!infoName}
        onClose={onCloseModal}
        data={infoName ? infoCandidates[infoName] : []}
      />

      <InfoItem
        title={t("account.availableBalance")}
        onPress={onPressInfoCreator("available")}
        value={
          <CurrencyUnitValue unit={unit} value={account.spendableBalance} />
        }
      />
      <InfoItem
        title={t("account.tronFrozen")}
        onPress={onPressInfoCreator("frozen")}
        value={tronPower}
      />
      <InfoItem
        title={t("account.bandwidth")}
        onPress={onPressInfoCreator("bandwidth")}
        value={
          formattedBandwidth.isZero()
            ? "-"
            : toLocaleString(formattedBandwidth, locale)
        }
      />
      <InfoItem
        title={t("account.energy")}
        onPress={onPressInfoCreator("energy")}
        value={
          formattedEnergy.isZero()
            ? "-"
            : toLocaleString(formattedEnergy, locale)
        }
      />
    </ScrollView>
  );
}

export default function AccountBalanceFooter({ account }: Props) {
  if (!account.tronResources) return null;

  return <AccountBalanceSummaryFooter account={account} />;
}

const styles = StyleSheet.create({
  root: {
    flex: 1,
    flexDirection: "row",
    overflow: "visible",
  },
});

function useInfoCandidates(): { [key: InfoName]: ModalInfo[] } {
  const { t } = useTranslation();
  const currency = getCryptoCurrencyById("tron");
  const TronIcon = getCryptoCurrencyIcon(currency);
  invariant(TronIcon, "Icon is expected");

  return {
    available: [
      {
        Icon: () => <TronIcon color={currency.color} size={18} />,
        title: t("tron.info.available.title"),
        description: t("tron.info.available.description"),
      },
    ],
    frozen: [
      {
        Icon: () => <FreezeIcon size={18} />,
        title: t("tron.info.frozen.title"),
        description: t("tron.info.frozen.description"),
      },
    ],
    bandwidth: [
      {
        Icon: () => <BandwidthIcon size={18} />,
        title: t("tron.info.bandwidth.title"),
        description: t("tron.info.bandwidth.description"),
      },
    ],
    energy: [
      {
        Icon: () => <EnergyIcon size={18} />,
        title: t("tron.info.energy.title"),
        description: t("tron.info.energy.description"),
      },
    ],
  };
}


<|start_filename|>src/families/ripple/ScreenEditTag.js<|end_filename|>
/* @flow */
import React, { useCallback, useState } from "react";
import { View, StyleSheet } from "react-native";
import SafeAreaView from "react-native-safe-area-view";
import { useSelector } from "react-redux";
import { useTranslation } from "react-i18next";
import { BigNumber } from "bignumber.js";
import { getAccountBridge } from "@ledgerhq/live-common/lib/bridge";
import type { Transaction } from "@ledgerhq/live-common/lib/families/ripple/types";
import { useTheme } from "@react-navigation/native";
import { i18n } from "../../context/Locale";
import KeyboardView from "../../components/KeyboardView";
import Button from "../../components/Button";
import NavigationScrollView from "../../components/NavigationScrollView";
import { accountScreenSelector } from "../../reducers/accounts";
import { ScreenName } from "../../const";
import { track } from "../../analytics";
import TextInput from "../../components/FocusedTextInput";

type Props = {
  navigation: any,
  route: { params: RouteParams },
};

type RouteParams = {
  accountId: string,
  transaction: Transaction,
};

const forceInset = { bottom: "always" };

const uint32maxPlus1 = BigNumber(2).pow(32);

const options = {
  title: i18n.t("send.summary.tag"),
  headerLeft: null,
};

function RippleEditTag({ route, navigation }: Props) {
  const { colors } = useTheme();
  const { account } = useSelector(accountScreenSelector(route));
  const { t } = useTranslation();
  const transaction = route.params?.transaction;

  const [tag, setTag] = useState<?BigNumber>(() => {
    if (transaction.tag) {
      return BigNumber(transaction.tag);
    }

    return undefined;
  });

  const onTagFieldFocus = useCallback(() => {
    track("SendTagFieldFocusedXRP");
  }, []);

  function onChangeTag(str: string): void {
    const tagNumeric = BigNumber(str.replace(/[^0-9]/g, ""));
    const newTag =
      tagNumeric.isInteger() &&
      tagNumeric.isPositive() &&
      tagNumeric.lt(uint32maxPlus1)
        ? tagNumeric
        : undefined;
    setTag(newTag);
  }

  const onValidateText = useCallback(() => {
    if (!account) return;
    const bridge = getAccountBridge(account);
    navigation.navigate(ScreenName.SendSummary, {
      accountId: account.id,
      transaction: bridge.updateTransaction(transaction, {
        tag: tag && tag.toNumber(),
      }),
    });
  }, [navigation, account, tag, transaction]);

  return (
    <SafeAreaView style={{ flex: 1 }} forceInset={forceInset}>
      <KeyboardView
        style={[styles.body, { backgroundColor: colors.background }]}
      >
        <NavigationScrollView keyboardShouldPersistTaps="always">
          <TextInput
            allowFontScaling={false}
            autoFocus
            style={[styles.textInputAS, { color: colors.darkBlue }]}
            defaultValue={tag ? tag.toString() : ""}
            keyboardType="numeric"
            returnKeyType="done"
            onChangeText={onChangeTag}
            onFocus={onTagFieldFocus}
            onSubmitEditing={onValidateText}
          />

          <View style={styles.flex}>
            <Button
              event="RippleEditTag"
              type="primary"
              title={t("send.summary.validateTag")}
              onPress={onValidateText}
              containerStyle={styles.buttonContainer}
            />
          </View>
        </NavigationScrollView>
      </KeyboardView>
    </SafeAreaView>
  );
}

export { options, RippleEditTag as component };

const styles = StyleSheet.create({
  root: {
    flex: 1,
  },
  body: {
    flexDirection: "column",
    flex: 1,
  },
  textInputAS: {
    padding: 16,
    fontSize: 30,
  },
  buttonContainer: {
    marginHorizontal: 16,
  },
  flex: {
    flex: 1,
    flexDirection: "column",
    justifyContent: "flex-end",
    paddingBottom: 16,
  },
});


<|start_filename|>src/families/ripple/SendRowTag.js<|end_filename|>
/* @flow */
import React, { useCallback } from "react";
import { View, StyleSheet } from "react-native";
import { useNavigation, useTheme } from "@react-navigation/native";
import { Trans } from "react-i18next";
import type { Account } from "@ledgerhq/live-common/lib/types";
import type { Transaction } from "@ledgerhq/live-common/lib/families/ripple/types";
import { useSelector } from "react-redux";
import LText from "../../components/LText";
import { ScreenName } from "../../const";
import SummaryRow from "../../screens/SendFunds/SummaryRow";
import { localeSelector } from "../../reducers/settings";

type Props = {
  account: Account,
  transaction: Transaction,
};

export default function RippleTagRow({ account, transaction }: Props) {
  const { colors } = useTheme();
  const navigation = useNavigation();
  const locale = useSelector(localeSelector);

  const editTag = useCallback(() => {
    navigation.navigate(ScreenName.RippleEditTag, {
      accountId: account.id,
      transaction,
    });
  }, [navigation, account, transaction]);

  const tag = transaction.tag;

  return (
    <View>
      <SummaryRow title={<Trans i18nKey="send.summary.tag" />} info="info">
        <View style={styles.tagContainer}>
          {tag && (
            <LText style={styles.tagText}>{tag.toLocaleString(locale)}</LText>
          )}
          <LText
            style={[
              styles.link,
              {
                textDecorationColor: colors.live,
              },
            ]}
            color="live"
            onPress={editTag}
          >
            <Trans i18nKey="common.edit" />
          </LText>
        </View>
      </SummaryRow>
    </View>
  );
}

const styles = StyleSheet.create({
  link: {
    textDecorationStyle: "solid",
    textDecorationLine: "underline",
    marginLeft: 8,
  },
  tagContainer: {
    flexDirection: "row",
  },
  tagText: {
    fontSize: 16,
  },
});


<|start_filename|>src/components/OperationRowDate.js<|end_filename|>
// @flow
import { useMemo } from "react";
import { useSelector } from "react-redux";
import { localeSelector } from "../reducers/settings";

type Props = {
  date: Date,
};

export default function OperationRowDate({ date }: Props) {
  const locale = useSelector(localeSelector);

  const localeTimeString = useMemo(
    () =>
      date.toLocaleTimeString(locale, {
        hour: "2-digit",
        minute: "2-digit",
      }),
    [date, locale],
  );

  return `${localeTimeString}`;
}


<|start_filename|>src/reducers/appstate.js<|end_filename|>
// @flow
/* eslint import/no-cycle: 0 */
import { handleActions } from "redux-actions";
import { createSelector } from "reselect";
import { NetworkDown } from "@ledgerhq/errors";
import type { State } from ".";

export type AsyncState = {
  isConnected: boolean | null,
};

export type AppState = {
  isConnected: boolean | null,
  hasConnectedDevice: boolean,
  modalLock: boolean,
};

const initialState: AppState = {
  isConnected: true,
  hasConnectedDevice: false, // NB for this current session, have we done a device action with a device.
  modalLock: false,
};

const handlers: Object = {
  SYNC_IS_CONNECTED: (
    state: AppState,
    { isConnected }: { isConnected: boolean | null },
  ) => ({
    ...state,
    isConnected,
  }),
  HAS_CONNECTED_DEVICE: (
    state: AppState,
    { hasConnectedDevice }: { hasConnectedDevice: boolean },
  ) => ({ ...state, hasConnectedDevice }),
  SET_MODAL_LOCK: (state: AppState, { modalLock }: { modalLock: boolean }) => ({
    ...state,
    modalLock,
  }),
};

// Selectors

export const isConnectedSelector = (state: State) => state.appstate.isConnected;
export const isModalLockedSelector = (state: State) => state.appstate.modalLock;
export const hasConnectedDeviceSelector = (state: State) =>
  state.appstate.hasConnectedDevice;

const globalNetworkDown = new NetworkDown();

// $FlowFixMe
export const networkErrorSelector = createSelector(
  isConnectedSelector,
  isConnected => (!isConnected ? globalNetworkDown : null),
);

export default handleActions(handlers, initialState);


<|start_filename|>src/screens/AddAccounts/04-Success.js<|end_filename|>
// @flow

import React, { useCallback } from "react";
import { Trans } from "react-i18next";
import { StyleSheet, View } from "react-native";
import type { CryptoCurrency } from "@ledgerhq/live-common/lib/types";

import { useTheme } from "@react-navigation/native";
import { Icons } from "@ledgerhq/native-ui";
import { ScreenName, NavigatorName } from "../../const";
import { rgba } from "../../colors";
import { TrackScreen } from "../../analytics";
import LText from "../../components/LText";
import Button from "../../components/Button";
import IconCheck from "../../icons/Check";
import CurrencyIcon from "../../components/CurrencyIcon";

type Props = {
  navigation: any,
  route: { params: RouteParams },
};

type RouteParams = {
  currency: CryptoCurrency,
  deviceId: string,
};

export default function AddAccountsSuccess({ navigation, route }: Props) {
  const { colors } = useTheme();
  const currency = route.params.currency;

  const primaryCTA = useCallback(() => {
    navigation.replace(NavigatorName.Main, {
      screen: NavigatorName.Portfolio,
      params: {
        screen: NavigatorName.PortfolioAccounts,
        params: {
          screen: ScreenName.Accounts,
          params: { search: currency.name },
        },
      },
    });
  }, [currency, navigation]);

  const secondaryCTA = useCallback(() => {
    navigation.navigate(ScreenName.AddAccountsSelectCrypto);
  }, [navigation]);

  return (
    <View style={[styles.root, { backgroundColor: colors.background }]}>
      <TrackScreen
        category="AddAccounts"
        name="Success"
        currencyName={currency.name}
      />
      <CurrencySuccess currency={currency} />
      <LText secondary semiBold style={styles.title}>
        <Trans i18nKey="addAccounts.imported" />
      </LText>
      <View style={styles.buttonsContainer}>
        <Button
          event="AddAccountsDone"
          containerStyle={styles.button}
          type="primary"
          outline={false}
          title={<Trans i18nKey="addAccounts.success.cta" />}
          onPress={primaryCTA}
        />
        <Button
          event="AddAccountsAgain"
          onPress={secondaryCTA}
          IconLeft={Icons.WalletAddMedium}
          type="lightSecondary"
          title={<Trans i18nKey="addAccounts.success.secondaryCTA" />}
        />
      </View>
    </View>
  );
}

type CurrencySuccessProps = {
  currency: CryptoCurrency,
};

function CurrencySuccess({ currency }: CurrencySuccessProps) {
  const { colors } = useTheme();
  return (
    <View
      style={[
        styles.currencySuccess,
        {
          backgroundColor: rgba(currency.color, 0.14),
        },
      ]}
    >
      <View
        style={[
          styles.outer,
          {
            backgroundColor: colors.card,
          },
        ]}
      >
        <View
          style={[
            styles.inner,
            {
              backgroundColor: colors.green,
            },
          ]}
        >
          <IconCheck size={16} color="white" />
        </View>
      </View>
      <CurrencyIcon currency={currency} size={32} />
    </View>
  );
}

const styles = StyleSheet.create({
  root: {
    flex: 1,
    paddingHorizontal: 20,

    alignItems: "center",
    justifyContent: "center",
  },
  currencySuccess: {
    width: 80,
    height: 80,
    borderRadius: 40,
    alignItems: "center",
    justifyContent: "center",
  },
  outer: {
    position: "absolute",
    top: -5,
    right: -5,
    width: 34,
    height: 34,
    borderRadius: 100,
    flexDirection: "row",
    alignItems: "center",
    justifyContent: "center",
  },
  inner: {
    width: 26,
    height: 26,
    borderRadius: 100,
    flexDirection: "row",
    alignItems: "center",
    justifyContent: "center",
  },
  title: {
    marginTop: 32,
    marginBottom: 32,
    fontSize: 18,
  },
  desc: {
    marginTop: 16,
    marginBottom: 32,
    marginHorizontal: 32,
    textAlign: "center",
    fontSize: 14,
  },
  buttonsContainer: {
    alignSelf: "stretch",
  },
  button: {
    marginBottom: 16,
  },
});


<|start_filename|>src/families/algorand/operationDetails.js<|end_filename|>
// @flow
import React from "react";
import { View, StyleSheet } from "react-native";
import { useTranslation } from "react-i18next";
import type {
  Account,
  OperationType,
  Operation,
} from "@ledgerhq/live-common/lib/types";
import { formatCurrencyUnit } from "@ledgerhq/live-common/lib/currencies/formatCurrencyUnit";
import { BigNumber } from "bignumber.js";
import { getAccountUnit } from "@ledgerhq/live-common/lib/account/helpers";

import { useSelector } from "react-redux";
import { Icons } from "@ledgerhq/native-ui";
import Section from "../../screens/OperationDetails/Section";
import OperationStatusIcon from "../../icons/OperationStatusIcon";
import { discreetModeSelector, localeSelector } from "../../reducers/settings";

type Props = {
  extra: {
    rewards?: BigNumber,
    memo?: string,
    assetId?: string,
  },
  account: Account,
};

function OperationDetailsExtra({ extra, account }: Props) {
  const { t } = useTranslation();
  const unit = getAccountUnit(account);
  const discreet = useSelector(discreetModeSelector);
  const locale = useSelector(localeSelector);
  const formattedRewards =
    extra.rewards && extra.rewards.gt(0)
      ? formatCurrencyUnit(unit, extra.rewards, {
          locale,
          disableRounding: true,
          alwaysShowSign: false,
          showCode: true,
          discreet,
        })
      : null;

  return (
    <>
      {formattedRewards && (
        <Section
          title={t("operationDetails.extra.rewards")}
          value={formattedRewards}
        />
      )}
      {extra.assetId && (
        <Section
          title={t("operationDetails.extra.assetId")}
          value={extra.assetId}
        />
      )}
      {extra.memo && (
        <Section title={t("operationDetails.extra.memo")} value={extra.memo} />
      )}
    </>
  );
}

type OperationIconProps = {
  type: OperationType,
  size: number,
  confirmed: boolean,
  operation: Operation,
};

const OperationIcon = ({
  type,
  size,
  confirmed,
  operation: { hasFailed, extra },
}: OperationIconProps) => {
  const rewards = extra.rewards && extra.rewards.gt(0) ? extra.rewards : null;
  return rewards ? (
    <View style={styles.operationIconContainer}>
      <OperationStatusIcon
        confirmed={confirmed}
        type={type}
        Badge={Icons.ClaimRewardsMedium}
        failed={hasFailed}
        size={size}
      />
    </View>
  ) : (
    <OperationStatusIcon
      confirmed={confirmed}
      type={type}
      failed={hasFailed}
      size={size}
    />
  );
};

const styles = StyleSheet.create({
  operationIconContainer: { position: "relative" },
  operationMainIcon: {
    position: "relative",
    zIndex: 1,
    borderRadius: 100,
  },
  operationSecondaryIcon: { position: "absolute", zIndex: 0, right: 0, top: 0 },
});

const operationStatusIcon = {
  OUT: OperationIcon,
  IN: OperationIcon,
};

export default {
  OperationDetailsExtra,
  operationStatusIcon,
};


<|start_filename|>src/screens/Swap/FormSelection/SelectProviderRateScreen.js<|end_filename|>
// @flow
import { getAccountUnit } from "@ledgerhq/live-common/lib/account";
import { useTheme } from "@react-navigation/native";
import { BigNumber } from "bignumber.js";
import React, { useCallback, useMemo } from "react";
import { View, StyleSheet, FlatList } from "react-native";
import { TouchableOpacity } from "react-native-gesture-handler";

import type { SwapRouteParams } from "..";
import { providerIcons } from "./RatesSection";
import CounterValue from "../../../components/CounterValue";
import CurrencyUnitValue from "../../../components/CurrencyUnitValue";
import LText from "../../../components/LText";
import { ScreenName } from "../../../const";
import Lock from "../../../icons/Lock";
import Unlock from "../../../icons/Unlock";
import { TrackScreen } from "../../../analytics";

type Props = {
  route: { params: SwapRouteParams },
  navigation: *,
};

export default function SelectProviderRateScreen({ route, navigation }: Props) {
  const { colors } = useTheme();
  const { swap = {}, rate, transaction, provider } = route.params;

  const {
    from: { account: fromAccount } = {},
    to: { currency: toCurrency } = {},
    rates: { value: rates = [] } = {},
  } = swap;
  const filteredRates = rates.filter(r => r.provider === provider);

  const fromUnit = useMemo(() => fromAccount && getAccountUnit(fromAccount), [
    fromAccount,
  ]);

  const toUnit = toCurrency?.units[0];

  const onSelectRate = useCallback(
    newRate => {
      navigation.navigate(ScreenName.SwapForm, {
        ...route.params,
        rate: newRate,
        provider: newRate.provider,
      });
    },
    [navigation, route.params],
  );

  const renderItem = useCallback(
    ({ item }) => {
      const { magnitudeAwareRate, provider, tradeMethod, payoutNetworkFees } =
        item || {};
      const ProviderIcon = providerIcons[provider];
      const isSelected =
        provider === rate?.provider && item.rate === rate?.rate;

      const toValue =
        rate && transaction
          ? transaction.amount
              .times(magnitudeAwareRate)
              .minus(payoutNetworkFees || 0)
          : null;
      return (
        <TouchableOpacity
          style={[
            styles.row,
            styles.listItem,
            { borderColor: isSelected ? colors.live : colors.lightFog },
          ]}
          onPress={() => onSelectRate(item)}
        >
          <View style={[styles.col]}>
            <View style={[styles.providerName]}>
              {ProviderIcon ? <ProviderIcon size={12} /> : null}
              <LText semiBold style={styles.valueLabel}>
                {provider}
              </LText>
            </View>
            <View style={[styles.row]}>
              {tradeMethod === "fixed" ? (
                <Lock size={12} color={colors.grey} />
              ) : (
                <Unlock size={12} color={colors.grey} />
              )}
              <LText semiBold color="grey" style={styles.subText}>
                <CurrencyUnitValue
                  value={BigNumber(10).pow(fromUnit.magnitude)}
                  unit={fromUnit}
                  showCode
                />
                {" = "}
                <CurrencyUnitValue
                  unit={toCurrency.units[0]}
                  value={BigNumber(10)
                    .pow(fromUnit.magnitude)
                    .times(magnitudeAwareRate)}
                  showCode
                />
              </LText>
            </View>
          </View>
          <View style={[styles.col]}>
            <LText semiBold style={[styles.valueLabel, styles.alignRight]}>
              <CurrencyUnitValue
                unit={toUnit}
                value={toValue ?? BigNumber(0)}
              />
            </LText>
            <LText
              semiBold
              color="grey"
              style={[styles.subText, styles.alignRight]}
            >
              <CounterValue
                currency={toCurrency}
                value={toValue ?? BigNumber(0)}
              />
            </LText>
          </View>
        </TouchableOpacity>
      );
    },
    [
      colors.grey,
      colors.lightFog,
      colors.live,
      fromUnit,
      onSelectRate,
      rate,
      toCurrency,
      toUnit,
      transaction,
    ],
  );

  return (
    <View style={styles.root}>
      <TrackScreen category="Swap Form" name="Edit Rate" provider={provider} />
      <FlatList data={filteredRates} renderItem={renderItem} />
    </View>
  );
}

const styles = StyleSheet.create({
  root: {
    flex: 1,
    padding: 16,
  },
  row: {
    flexDirection: "row",
    justifyContent: "space-between",
    alignItems: "center",
  },
  col: {
    flexDirection: "column",
  },
  listItem: {
    height: 69,
    borderRadius: 4,
    paddingHorizontal: 16,
    borderWidth: 1,
    marginBottom: 16,
  },
  providerName: {
    flexDirection: "row",
    justifyContent: "flex-start",
    alignItems: "center",
  },
  valueLabel: { marginLeft: 4, fontSize: 14, lineHeight: 20 },
  inputText: {
    textAlign: "right",
    fontSize: 23,
    lineHeight: 28,
    height: 32,
    padding: 0,
  },
  subText: { fontSize: 13, lineHeight: 14, paddingTop: 4 },
  alignRight: { textAlign: "right" },
});


<|start_filename|>src/icons/RecoveryPhrase.js<|end_filename|>
import React from "react";
import Svg, { G, Path, Rect } from "react-native-svg";

const RecoveryPhrase = props => (
  <Svg width={145} height={109} {...props}>
    <G fill="none" fillRule="evenodd" transform="translate(-1)">
      <Path
        fill="#bdb3ff"
        fillOpacity={0.1}
        stroke="#142533"
        strokeWidth={2}
        d="M29.079 103.688l19.757-83.857-44.68 16.262a2.6 2.6 0 0 0-1.514 3.434l26.437 64.161zm89.316 1.39L97.882 34.45l44.566 16.22a2.6 2.6 0 0 1 1.434 3.61l-25.487 50.798z"
      />
      <Path
        fill="#FFF"
        stroke="#142533"
        strokeWidth={2}
        d="M32.6 1A2.6 2.6 0 0 0 30 3.6v101.8a2.6 2.6 0 0 0 2.6 2.6h82.8a2.6 2.6 0 0 0 2.6-2.6V3.6a2.6 2.6 0 0 0-2.6-2.6H32.6z"
      />
      <Rect width={50} height={2} x={39} y={16} fill="#bdb3ff" rx={1} />
      <Rect width={30} height={2} x={39} y={23} fill="#bdb3ff" rx={1} />
      <Path
        fill="#bdb3ff"
        d="M82.368 91H108a1 1 0 1 1 0 2H82.368a1 1 0 1 1 0-2zM40 91h25.632a1 1 0 1 1 0 2H40a1 1 0 0 1 0-2zm42.368-10H108a1 1 0 1 1 0 2H82.368a1 1 0 1 1 0-2zM40 81h25.632a1 1 0 1 1 0 2H40a1 1 0 0 1 0-2zm42.368-10H108a1 1 0 1 1 0 2H82.368a1 1 0 1 1 0-2zM40 71h25.632a1 1 0 1 1 0 2H40a1 1 0 0 1 0-2zm42.368-10H108a1 1 0 1 1 0 2H82.368a1 1 0 1 1 0-2zM40 61h25.632a1 1 0 1 1 0 2H40a1 1 0 0 1 0-2zm42.368-10H108a1 1 0 1 1 0 2H82.368a1 1 0 1 1 0-2zM40 51h25.632a1 1 0 1 1 0 2H40a1 1 0 0 1 0-2zm42.368-10H108a1 1 0 1 1 0 2H82.368a1 1 0 1 1 0-2zM40 41h25.632a1 1 0 1 1 0 2H40a1 1 0 0 1 0-2z"
        opacity={0.4}
      />
    </G>
  </Svg>
);

export default RecoveryPhrase;


<|start_filename|>src/screens/Asset/index.js<|end_filename|>
/* @flow */

import {
  flattenAccounts,
  getAccountCurrency,
  groupAccountsOperationsByDay,
  isAccountEmpty,
} from "@ledgerhq/live-common/lib/account";
import type {
  Account,
  Currency,
  Operation,
  Unit,
} from "@ledgerhq/live-common/lib/types";
import { isCountervalueEnabled } from "@ledgerhq/live-common/lib/countervalues/modules";
import type { PortfolioRange } from "@ledgerhq/live-common/lib/portfolio/v2/types";
import React, { PureComponent, useMemo } from "react";
import { StyleSheet, View, SectionList } from "react-native";
import { useRoute, useTheme } from "@react-navigation/native";
import { useDispatch, useSelector } from "react-redux";
import type { SectionBase } from "react-native/Libraries/Lists/SectionList";
import SafeAreaView from "react-native-safe-area-view";
import CurrencyIcon from "../../components/CurrencyIcon";
import { switchCountervalueFirst } from "../../actions/settings";
import CurrencyUnitValue from "../../components/CurrencyUnitValue";
import LText from "../../components/LText";
import Touchable from "../../components/Touchable";
import AssetGraphCard from "../../components/AssetGraphCard";
import type { RenderTitle } from "../../components/AssetGraphCard";
import { useCurrencyPortfolio } from "../../actions/portfolio";
import globalSyncRefreshControl from "../../components/globalSyncRefreshControl";
import LoadingFooter from "../../components/LoadingFooter";
import NoMoreOperationFooter from "../../components/NoMoreOperationFooter";
import NoOperationFooter from "../../components/NoOperationFooter";
import AccountDistribution from "../../components/AccountDistribution";
import OperationRow from "../../components/OperationRow";
import SectionHeader from "../../components/SectionHeader";
import { accountsSelector } from "../../reducers/accounts";
import {
  counterValueCurrencySelector,
  countervalueFirstSelector,
  selectedTimeRangeSelector,
} from "../../reducers/settings";
import { withDiscreetMode } from "../../context/DiscreetModeContext";

const List = globalSyncRefreshControl(SectionList);

type Props = {
  route: { params: any },
  counterValueUnit: Unit,
};

type AssetProps = Props & {
  currency: Currency,
  range: PortfolioRange,
  counterValue: any,
  accounts: any, // Fixme doesn't want AccountLikeArray
  allAccounts: Account[],
  counterValueCurrency: Currency,
  useCounterValue: boolean,
  portfolio: any,
  switchCountervalueFirst: () => void,
  colors: *,
};

export function HeaderTitle() {
  const route = useRoute();
  return (
    <View style={{ flexDirection: "row" }}>
      <View style={{ marginRight: 5, justifyContent: "center" }}>
        <CurrencyIcon size={16} currency={route.params?.currency} />
      </View>
      <LText semiBold secondary style={{ fontSize: 16 }}>
        {route.params?.currency.name}
      </LText>
    </View>
  );
}

class Asset extends PureComponent<AssetProps, any> {
  state = {
    opCount: 50,
  };

  renderListHeaderTitle: RenderTitle = ({
    useCounterValue,
    cryptoCurrencyUnit,
    counterValueUnit,
    item,
  }) => {
    const { switchCountervalueFirst, currency } = this.props;
    const countervalueAvailable = isCountervalueEnabled(currency);
    const items = [
      { unit: cryptoCurrencyUnit, value: item.value },
      // $FlowFixMe
      { unit: counterValueUnit, value: item.countervalue },
    ];
    const shouldUseCounterValue = countervalueAvailable && useCounterValue;
    if (shouldUseCounterValue && item.countervalue) {
      items.reverse();
    }

    return (
      <Touchable
        event="SwitchAccountCurrency"
        eventProperties={{ useCounterValue: shouldUseCounterValue }}
        onPress={countervalueAvailable ? switchCountervalueFirst : undefined}
      >
        <View style={styles.balanceContainer}>
          <LText style={styles.balanceText} semiBold>
            <CurrencyUnitValue {...items[0]} joinFragmentsSeparator=" " />
          </LText>
          <LText style={styles.balanceSubText} semiBold color="smoke">
            {/* $FlowFixMe */}
            <CurrencyUnitValue {...items[1]} />
          </LText>
        </View>
      </Touchable>
    );
  };

  renderHeader = () => {
    const {
      useCounterValue,
      range,
      currency,
      portfolio,
      counterValueCurrency,
      accounts,
    } = this.props;
    return (
      <View>
        <AssetGraphCard
          currency={currency}
          range={range}
          history={portfolio.history}
          useCounterValue={useCounterValue}
          valueChange={
            useCounterValue ? portfolio.valueChange : portfolio.cryptoChange
          }
          countervalueAvailable={portfolio.countervalueAvailable}
          counterValueCurrency={counterValueCurrency}
          renderTitle={this.renderListHeaderTitle}
        />
        <AccountDistribution
          accounts={accounts}
          counterValueCurrency={counterValueCurrency}
        />
      </View>
    );
  };

  renderSectionHeader = ({ section }: { section: * }) => (
    <SectionHeader section={section} />
  );

  renderItem = ({
    item,
    index,
    section,
  }: {
    item: Operation,
    index: number,
    section: SectionBase<*>,
  }) => {
    const { allAccounts, accounts } = this.props;
    const account = accounts.find(a => a.id === item.accountId);
    const parentAccount =
      account && account.type !== "Account"
        ? allAccounts.find(a => a.id === account.parentId)
        : null;

    if (!account) return null;

    return (
      <OperationRow
        operation={item}
        parentAccount={parentAccount}
        account={account}
        multipleAccounts
        isLast={section.data.length - 1 === index}
      />
    );
  };

  onEndReached = () => {
    this.setState(prevState => ({ opCount: prevState.opCount + 50 }));
  };

  keyExtractor = (item: Operation) => item.id;
  ref = React.createRef<React$Node>();

  render() {
    const { opCount } = this.state;
    const { accounts, currency, colors } = this.props;

    const { sections, completed } = groupAccountsOperationsByDay(accounts, {
      count: opCount,
      withSubAccounts: currency.type !== "CryptoCurrency", // Fixme, do sub accounts have 'tokens'?
    });

    return (
      <View style={[styles.root, { backgroundColor: colors.background }]}>
        <SafeAreaView style={styles.root}>
          <List
            forwardedRef={this.ref}
            sections={sections}
            style={styles.list}
            contentContainerStyle={styles.contentContainer}
            keyExtractor={this.keyExtractor}
            renderItem={this.renderItem}
            renderSectionHeader={this.renderSectionHeader}
            onEndReached={this.onEndReached}
            stickySectionHeadersEnabled={false}
            showsVerticalScrollIndicator={false}
            scrollEventThrottle={16}
            ListHeaderComponent={this.renderHeader}
            ListFooterComponent={
              !completed ? (
                <LoadingFooter />
              ) : accounts.every(isAccountEmpty) ? null : sections.length ? (
                <NoMoreOperationFooter />
              ) : (
                <NoOperationFooter />
              )
            }
          />
        </SafeAreaView>
      </View>
    );
  }
}

function Screen(props: Props) {
  const dispatch = useDispatch();
  const currency = props.route.params.currency;
  const range = useSelector(selectedTimeRangeSelector);
  const counterValue = useSelector(counterValueCurrencySelector);
  const allAccounts = useSelector(accountsSelector);
  const accounts = useMemo(
    () =>
      flattenAccounts(allAccounts)
        .filter(a => getAccountCurrency(a) === currency)
        .sort((a, b) => b.balance.comparedTo(a.balance)),
    [allAccounts, currency],
  );
  const counterValueCurrency = useSelector(counterValueCurrencySelector);
  const useCounterValue = useSelector(countervalueFirstSelector);
  const portfolio = useCurrencyPortfolio({
    currency,
    range,
  });

  const { colors } = useTheme();

  return (
    <Asset
      {...props}
      currency={currency}
      range={range}
      counterValue={counterValue}
      accounts={accounts}
      allAccounts={allAccounts}
      counterValueCurrency={counterValueCurrency}
      useCounterValue={useCounterValue}
      portfolio={portfolio}
      switchCountervalueFirst={(...args) =>
        dispatch(switchCountervalueFirst(...args))
      }
      colors={colors}
    />
  );
}

export default withDiscreetMode(Screen);

const styles = StyleSheet.create({
  root: {
    flex: 1,
  },
  list: {
    flex: 1,
    flexGrow: 1,
  },
  contentContainer: {
    flexGrow: 1,
    paddingTop: 16,
    paddingBottom: 64,
  },
  balanceTextContainer: {
    marginBottom: 5,
    alignItems: "center",
    justifyContent: "center",
  },
  balanceText: {
    fontSize: 22,
  },
  balanceSubText: {
    fontSize: 16,
  },
  balanceContainer: {
    marginLeft: 16,
    alignItems: "flex-start",
    height: 44,
  },
});


<|start_filename|>src/families/solana/SendRowsCustom.js<|end_filename|>
// @flow
import React, { useCallback } from "react";
import { View, StyleSheet } from "react-native";
import { useTranslation } from "react-i18next";
import { useTheme } from "@react-navigation/native";
import type { Account } from "@ledgerhq/live-common/lib/types";
import type { Transaction } from "@ledgerhq/live-common/lib/families/solana/types";
import LText from "../../components/LText";
import { ScreenName } from "../../const";
import SummaryRow from "../../screens/SendFunds/SummaryRow";

type Props = {
  account: Account,
  transaction: Transaction,
  navigation: any,
};

export default function SolanaSendRowsCustom({
  account,
  transaction,
  navigation,
}: Props) {
  const { colors } = useTheme();
  const { t } = useTranslation();

  const editMemo = useCallback(() => {
    navigation.navigate(ScreenName.SolanaEditMemo, {
      account,
      transaction,
    });
  }, [navigation, account, transaction]);

  return (
    <View>
      <SummaryRow title={t("send.summary.memo.title")} onPress={editMemo}>
        {transaction.model.uiState.memo ? (
          <LText
            semiBold
            style={styles.tagText}
            onPress={editMemo}
            numberOfLines={1}
          >
            {transaction.model.uiState.memo}
          </LText>
        ) : (
          <LText
            style={[
              styles.link,
              {
                textDecorationColor: colors.live,
              },
            ]}
            color="live"
            onPress={editMemo}
          >
            {t("common.edit")}
          </LText>
        )}
      </SummaryRow>
    </View>
  );
}

const styles = StyleSheet.create({
  memoContainer: {
    flexDirection: "row",
  },
  tagText: {
    flex: 1,
    fontSize: 14,
    textAlign: "right",
  },
  link: {
    textDecorationStyle: "solid",
    textDecorationLine: "underline",
    marginLeft: 8,
  },
  memo: {
    marginBottom: 10,
  },
});


<|start_filename|>src/components/RootNavigator/MarketNavigator.js<|end_filename|>
/* eslint-disable import/no-unresolved */
// @flow

import React, { useMemo } from "react";
import { createStackNavigator } from "@react-navigation/stack";
import { useTheme } from "styled-components/native";
import { ScreenName } from "../../const";
// $FlowFixMe
import MarketList from "../../screens/Market";
// $FlowFixMe
import MarketDetail from "../../screens/Market/MarketDetail";
import { getStackNavigatorConfig } from "../../navigation/navigatorConfig";

export default function MarketNavigator() {
  const { colors } = useTheme();
  const stackNavigationConfig = useMemo(
    () => getStackNavigatorConfig(colors, true),
    [colors],
  );
  return (
    <Stack.Navigator
      screenOptions={stackNavigationConfig}
      initialRouteName={ScreenName.MarketList}
      backBehavior={"initialRoute"}
    >
      <Stack.Screen
        name={ScreenName.MarketList}
        component={MarketList}
        options={{
          headerShown: false,
        }}
      />
      <Stack.Screen
        name={ScreenName.MarketDetail}
        component={MarketDetail}
        options={{
          headerShown: false,
        }}
      />
    </Stack.Navigator>
  );
}

const Stack = createStackNavigator();


<|start_filename|>src/__mocks__/react-native-locale.js<|end_filename|>
// @flow

const NativeLocale = {
  decimalStyle: (number: *) => `${number}`,
  numberFromDecimalString: (number: *) => parseFloat(number),
  dateFormat: (date: *, _1: *, _2: *) => date.toString(),
  currencyCode: "USD",
  currencySymbol: "$",
  decimalSeparator: ".",
  groupingSeparator: ",",
  localeDateFormats: {
    short: "M/d/yy",
    long: "MMMM d, y",
    medium: "MMM d, y",
    full: "EEEE, MMMM d, y",
  },
  localeIdentifier: "en_US",
  preferredLanguages: ["en"],
  quotationBeginDelimiterKey: "“",
  quotationEndDelimiterKey: "”",
};

const m = {
  constants() {
    return NativeLocale;
  },

  numberFromDecimalString(number: string) {
    return NativeLocale.numberFromDecimalString(number);
  },

  decimalStyle(number: number) {
    return NativeLocale.decimalStyle(number);
  },

  validateDateFormatStyle(style: string) {
    const valid = ["full", "long", "medium", "short", "none"];
    return valid.indexOf(style) >= 0;
  },

  dateFormat(date: *, dateStyle: string, timeStyle: string) {
    return NativeLocale.dateFormat(date, dateStyle, timeStyle);
  },
};

export default m;


<|start_filename|>src/components/RootNavigator/BuyNavigator.js<|end_filename|>
// @flow

import React from "react";
import { createStackNavigator } from "@react-navigation/stack";
import { ScreenName } from "../../const";
import Buy from "../../screens/Exchange/Buy";
import Coinify from "../../screens/Exchange/Coinify";

export default function BuyNavigator() {
  return (
    <Stack.Navigator
      screenOptions={{
        headerShown: false,
      }}
    >
      <Stack.Screen name={ScreenName.ExchangeBuy} component={Buy} />
      <Stack.Screen name={ScreenName.Coinify} component={Coinify} />
    </Stack.Navigator>
  );
}

const Stack = createStackNavigator();


<|start_filename|>src/families/algorand/Rewards/ClaimRewardsFlow/01-Started.js<|end_filename|>
// @flow
import invariant from "invariant";
import React, { useCallback } from "react";
import { View, StyleSheet, SafeAreaView } from "react-native";
import { Trans } from "react-i18next";
import { useSelector } from "react-redux";

import {
  getAccountUnit,
  getMainAccount,
} from "@ledgerhq/live-common/lib/account/helpers";
import { formatCurrencyUnit } from "@ledgerhq/live-common/lib/currencies/formatCurrencyUnit";
import useBridgeTransaction from "@ledgerhq/live-common/lib/bridge/useBridgeTransaction";
import { getAccountBridge } from "@ledgerhq/live-common/lib/bridge";

import { useTheme } from "@react-navigation/native";
import { Flex } from "@ledgerhq/native-ui";
import { ScreenName } from "../../../../const";
import Button from "../../../../components/Button";
import LText from "../../../../components/LText";
import { accountScreenSelector } from "../../../../reducers/accounts";
import { localeSelector } from "../../../../reducers/settings";
import NavigationScrollView from "../../../../components/NavigationScrollView";
import { TrackScreen } from "../../../../analytics";
import Alert from "../../../../components/Alert";
import TranslatedError from "../../../../components/TranslatedError";
import Illustration from "../../../../images/illustration/Illustration";

type RouteParams = {
  accountId: string,
};

type Props = {
  navigation: any,
  route: { params: RouteParams },
};

export default function DelegationStarted({ navigation, route }: Props) {
  const { colors } = useTheme();
  const { account } = useSelector(accountScreenSelector(route));
  const locale = useSelector(localeSelector);

  invariant(account, "Account required");

  const mainAccount = getMainAccount(account, undefined);
  const bridge = getAccountBridge(mainAccount, undefined);

  invariant(
    mainAccount && mainAccount.algorandResources,
    "algorand Account required",
  );
  const { rewards } = mainAccount.algorandResources;

  const unit = getAccountUnit(mainAccount);

  const { transaction, status } = useBridgeTransaction(() => {
    const t = bridge.createTransaction(mainAccount);

    return {
      account,
      transaction: bridge.updateTransaction(t, {
        mode: "claimReward",
      }),
    };
  });

  const formattedRewards = formatCurrencyUnit(unit, rewards, {
    showCode: true,
    disableRounding: true,
    locale,
  });

  const onNext = useCallback(() => {
    navigation.navigate(ScreenName.AlgorandClaimRewardsSelectDevice, {
      ...route.params,
      transaction,
    });
  }, [navigation, route.params, transaction]);

  const warning =
    status.warnings &&
    Object.keys(status.warnings).length > 0 &&
    Object.values(status.warnings)[0];

  return (
    <SafeAreaView style={[styles.root, { backgroundColor: colors.background }]}>
      <NavigationScrollView
        style={styles.scroll}
        contentContainerStyle={styles.scrollContainer}
      >
        <TrackScreen category="DelegationFlow" name="Started" />
        <Flex alignItems="center" justifyContent="center" mb={6}>
          <Illustration
            size={200}
            lightSource={require("../../../../images/illustration/Light/_003.png")}
            darkSource={require("../../../../images/illustration/Dark/_003.png")}
          />
        </Flex>
        <LText semiBold style={styles.description}>
          <Trans
            secondary
            i18nKey="algorand.claimRewards.flow.steps.starter.title"
            values={{ amount: formattedRewards }}
          />
        </LText>
        <View style={styles.warning}>
          <Alert type="help">
            <Trans i18nKey="algorand.claimRewards.flow.steps.starter.warning" />
          </Alert>
        </View>
      </NavigationScrollView>
      <View style={[styles.footer]}>
        {warning && warning instanceof Error ? (
          <View style={styles.warningSection}>
            <LText
              selectable
              secondary
              semiBold
              style={styles.warningText}
              color="alert"
            >
              <TranslatedError error={warning} />
            </LText>
          </View>
        ) : null}
        <Button
          event="DelegationStartedBtn"
          onPress={onNext}
          title={
            <Trans i18nKey="algorand.claimRewards.flow.steps.starter.cta" />
          }
          type="primary"
        />
      </View>
    </SafeAreaView>
  );
}

const styles = StyleSheet.create({
  root: {
    flex: 1,
  },
  scroll: {
    flex: 1,
  },
  scrollContainer: {
    paddingHorizontal: 16,
    paddingVertical: 32,
  },
  description: {
    fontSize: 16,
    lineHeight: 21,
    textAlign: "center",
    marginTop: 16,
  },
  warning: {
    width: "100%",
    marginVertical: 8,
  },
  warningText: {
    textAlign: "center",
  },
  warningSection: { padding: 16, height: 80 },
  footer: {
    padding: 16,
  },
});


<|start_filename|>src/icons/Apps.js<|end_filename|>
// @flow

import React from "react";
import Svg, { Path } from "react-native-svg";

type Props = {
  size: number,
  color: string,
};

const Apps = ({ size = 20, color = "#142533" }: Props) => (
  <Svg viewBox="0 0 24 24" height={size} width={size} fill="none">
    <Path
      fillRule="evenodd"
      clipRule="evenodd"
      d="M2.5 8.5v-6h6v6h-6ZM1 2a1 1 0 0 1 1-1h7a1 1 0 0 1 1 1v7a1 1 0 0 1-1 1H2a1 1 0 0 1-1-1V2Zm1.5 17.5v-6h6v6h-6ZM1 13a1 1 0 0 1 1-1h7a1 1 0 0 1 1 1v7a1 1 0 0 1-1 1H2a1 1 0 0 1-1-1v-7ZM13.5 2.5v6h6v-6h-6ZM13 1a1 1 0 0 0-1 1v7a1 1 0 0 0 1 1h7a1 1 0 0 0 1-1V2a1 1 0 0 0-1-1h-7Zm.5 18.5v-6h6v6h-6ZM12 13a1 1 0 0 1 1-1h7a1 1 0 0 1 1 1v7a1 1 0 0 1-1 1h-7a1 1 0 0 1-1-1v-7Z"
      fill={color}
    />
  </Svg>
);

export default Apps;


<|start_filename|>src/components/PanAndZoomView.js<|end_filename|>
// @flow

import React, { useRef } from "react";

import {
  PinchGestureHandler,
  PanGestureHandler,
  State,
} from "react-native-gesture-handler";
import { Animated, Platform } from "react-native";

const PanAndZoomView = ({ children }: { children: React$Node }) => {
  // normal number used to store the current scale and update the corresponding animated value
  // after the pinch gesture is finished
  const scaleOffset = useRef(1);
  // animated value used to store the current scale after the pinch gesture is finished
  const animatedScaleOffset = useRef(new Animated.Value(1)).current;
  // animated value to store the scale during the pinch gesture (which always starts at 1)
  const pinchScale = useRef(new Animated.Value(1)).current;
  // binding of the pinchScale value to the pinch gesture scale
  const handlePinch = Animated.event([{ nativeEvent: { scale: pinchScale } }], {
    useNativeDriver: true,
  });
  // actual scale of the image at any given moment (during the pinch gesture and after)
  const scale = Animated.multiply(pinchScale, animatedScaleOffset);

  // normal number used to store the current translate X and update the corresponding animated value
  // after the pan gesture is finished
  const translateXOffset = useRef(0);
  // animated value used to store the current translate X after the pan gesture is finished
  const animatedTranslateXOffset = useRef(new Animated.Value(0)).current;
  // animated value to store the translate X during the pan gesture (which always starts at 0)
  const panTranslateX = useRef(new Animated.Value(0)).current;
  // actual translate X of the image at any given moment (during the pan gesture and after)
  const translateX = Animated.add(
    animatedTranslateXOffset,
    Platform.OS === "ios"
      ? panTranslateX
      : Animated.divide(panTranslateX, scale),
    // on android the scale of the outer view impacts the translation so we need to compensate, not on ios
  );

  // normal number used to store the current translate Y and update the corresponding animated value
  // after the pan gesture is finished
  const translateYOffset = useRef(0);
  // animated value used to store the current translate Y after the pan gesture is finished
  const animatedTranslateYOffset = useRef(new Animated.Value(0)).current;
  // animated value to store the translate Y during the pan gesture (which always starts at 0)
  const panTranslateY = useRef(new Animated.Value(0)).current;
  // actual translate Y of the image at any given moment (during the pan gesture and after)
  const translateY = Animated.add(
    animatedTranslateYOffset,
    Platform.OS === "ios"
      ? panTranslateY
      : Animated.divide(panTranslateY, scale),
    // on android the scale of the outer view impacts the translation so we need to compensate, not on ios
  );

  // binding of the panTranslateX and panTranslateY values to the pan gesture translations
  const handlePan = Animated.event(
    [
      {
        nativeEvent: {
          translationX: panTranslateX,
          translationY: panTranslateY,
        },
      },
    ],
    {
      useNativeDriver: true,
    },
  );

  // we restrict pinch gestures to two fingers and panning gesture to one finger otherwise they
  // conflict with each other and we get weird behaviour on Android
  return (
    // $FlowFixMe
    <PinchGestureHandler
      onGestureEvent={handlePinch}
      minPointers={2}
      maxPointers={2}
      onHandlerStateChange={e => {
        if (e.nativeEvent.state === State.END) {
          // stores the current scale
          scaleOffset.current *= e.nativeEvent.scale;
          animatedScaleOffset.setValue(scaleOffset.current);
          // resets the pinch scale to 1 once the gesture is finished
          pinchScale.setValue(1);
        }
      }}
    >
      {/* each gesture handler can only update 1 view, so we need one for
       scaling and another for panning, this is the scaling view */}
      <Animated.View
        style={[
          {
            transform: [{ scale }],
          },
        ]}
      >
        <PanGestureHandler
          onGestureEvent={handlePan}
          minPointers={1}
          maxPointers={1}
          onHandlerStateChange={e => {
            if (e.nativeEvent.state === State.END) {
              // stores the current translate X
              translateXOffset.current +=
                Platform.OS === "ios"
                  ? e.nativeEvent.translationX
                  : e.nativeEvent.translationX / scaleOffset.current;
              // on android the scale of the outer view impacts the translation so we need to compensate, not on ios
              animatedTranslateXOffset.setValue(translateXOffset.current);
              // resets the pan translate X to 0 once the gesture is finished
              panTranslateX.setValue(0);

              // stores the current translate Y
              translateYOffset.current +=
                Platform.OS === "ios"
                  ? e.nativeEvent.translationY
                  : e.nativeEvent.translationY / scaleOffset.current;
              // on android the scale of the outer view impacts the translation so we need to compensate, not on ios
              animatedTranslateYOffset.setValue(translateYOffset.current);
              // resets the pan translate Y to 0 once the gesture is finished
              panTranslateY.setValue(0);
            }
          }}
        >
          {/* translating view */}
          <Animated.View
            style={[
              {
                transform: [
                  {
                    translateX,
                  },
                  {
                    translateY,
                  },
                ],
              },
            ]}
          >
            {children}
          </Animated.View>
        </PanGestureHandler>
      </Animated.View>
    </PinchGestureHandler>
  );
};

export default PanAndZoomView;


<|start_filename|>src/families/algorand/Rewards/ClaimRewardsFlow/01-Info.js<|end_filename|>
// @flow
import React, { useCallback } from "react";
import { View, StyleSheet, Linking } from "react-native";
import SafeAreaView from "react-native-safe-area-view";
import { Trans } from "react-i18next";
import { useTheme } from "@react-navigation/native";
import { Flex } from "@ledgerhq/native-ui";
import { ScreenName, NavigatorName } from "../../../../const";
import Button from "../../../../components/Button";
import LText from "../../../../components/LText";

import ExternalLink from "../../../../components/ExternalLink";
import BulletList, {
  BulletGreenCheck,
} from "../../../../components/BulletList";
import NavigationScrollView from "../../../../components/NavigationScrollView";
import { urls } from "../../../../config/urls";
import { TrackScreen } from "../../../../analytics";
import Illustration from "../../../../images/illustration/Illustration";

const forceInset = { bottom: "always" };

type RouteParams = {
  accountId: string,
};

type Props = {
  navigation: any,
  route: { params: RouteParams },
};

export default function ClaimRewardsStarted({ navigation, route }: Props) {
  const { colors } = useTheme();
  const onNext = useCallback(() => {
    navigation.pop();
    navigation.navigate(NavigatorName.ReceiveFunds, {
      screen: ScreenName.ReceiveConnectDevice,
      params: route.params,
    });
  }, [navigation, route.params]);

  const howClaimRewardsWorks = useCallback(() => {
    Linking.openURL(urls.algorandStaking);
  }, []);

  return (
    <SafeAreaView
      style={[styles.root, { backgroundColor: colors.background }]}
      forceInset={forceInset}
    >
      <NavigationScrollView
        style={styles.scroll}
        contentContainerStyle={styles.scrollContainer}
      >
        <TrackScreen category="ClaimRewardsFlow" name="Started" />
        <Flex alignItems="center" justifyContent="center" mb={6}>
          <Illustration
            size={200}
            lightSource={require("../../../../images/illustration/Light/_003.png")}
            darkSource={require("../../../../images/illustration/Dark/_003.png")}
          />
        </Flex>
        <LText semiBold style={styles.description}>
          <Trans i18nKey="algorand.claimRewards.flow.steps.info.description" />
        </LText>
        <BulletList
          Bullet={BulletGreenCheck}
          list={[
            <Trans i18nKey="algorand.claimRewards.flow.steps.info.steps.0" />,
            <Trans i18nKey="algorand.claimRewards.flow.steps.info.steps.1" />,
            <Trans i18nKey="algorand.claimRewards.flow.steps.info.steps.2" />,
          ].map(wording => (
            <LText semiBold style={styles.bulletItem} color="black">
              {wording}
            </LText>
          ))}
        />
        <View
          style={[styles.howClaimRewardsWorks, { borderColor: colors.live }]}
        >
          <ExternalLink
            event="AlgorandHowRewardsWork"
            onPress={howClaimRewardsWorks}
            text={
              <Trans i18nKey="algorand.claimRewards.flow.steps.info.howItWorks" />
            }
            ltextProps={{
              secondary: true,
            }}
          />
        </View>
      </NavigationScrollView>
      <View style={[styles.footer]}>
        <Button
          event="ClaimRewardsStartedBtn"
          onPress={onNext}
          title={
            <Trans i18nKey="algorand.claimRewards.flow.steps.starter.cta" />
          }
          type="primary"
        />
      </View>
    </SafeAreaView>
  );
}

const styles = StyleSheet.create({
  root: {
    flex: 1,
  },
  scroll: {
    flex: 1,
  },
  scrollContainer: {
    paddingHorizontal: 32,
    paddingVertical: 32,
    alignItems: "center",
  },
  title: {
    fontSize: 22,
    lineHeight: 33,
    paddingVertical: 16,
  },
  description: {
    fontSize: 16,
    lineHeight: 21,
    textAlign: "center",
    marginVertical: 16,
  },
  bulletItem: {
    fontSize: 14,
  },
  howClaimRewardsWorks: {
    paddingVertical: 8,
    paddingHorizontal: 16,

    flexDirection: "row",
  },
  warning: {
    width: "100%",
    marginTop: 16,
  },
  learnMoreBtn: {
    alignSelf: "flex-start",
    paddingHorizontal: 0,
  },
  footer: {
    padding: 16,
  },
});


<|start_filename|>src/context/AuthPass/index.js<|end_filename|>
// @flow
import React, { PureComponent } from "react";
import { StyleSheet, View, AppState } from "react-native";
import { connect } from "react-redux";
import { withTranslation } from "react-i18next";
import { createStructuredSelector } from "reselect";
import { compose } from "redux";
import { privacySelector } from "../../reducers/settings";
import { SkipLockContext } from "../../components/behaviour/SkipLock";
import { AUTOLOCK_TIMEOUT } from "../../constants";
import type { Privacy } from "../../reducers/settings";
import AuthScreen from "./AuthScreen";
import RequestBiometricAuth from "../../components/RequestBiometricAuth";

const mapStateToProps = createStructuredSelector({
  privacy: privacySelector,
});

type State = {
  isLocked: boolean,
  biometricsError: ?Error,
  appState: string,
  skipLockCount: number,
  setEnabled: (enabled: boolean) => void,
  authModalOpen: boolean,
  mounted: boolean,
};

type OwnProps = {
  children: *,
};

type Props = {
  ...OwnProps,
  t: *,
  privacy: ?Privacy,
};

// as we needs to be resilient to reboots (not showing unlock again after a reboot)
// we need to store this global variable to know if we need to isLocked initially
let wasUnlocked = false;

class AuthPass extends PureComponent<Props, State> {
  setEnabled = (enabled: boolean) => {
    if (this.state.mounted)
      this.setState(prevState => ({
        skipLockCount: prevState.skipLockCount + (enabled ? 1 : -1),
      }));
  };

  state = {
    isLocked: !!this.props.privacy && !wasUnlocked,
    biometricsError: null,
    appState: AppState.currentState || "",

    skipLockCount: 0,
    setEnabled: this.setEnabled,
    authModalOpen: false,
    mounted: false,
  };

  static getDerivedStateFromProps({ privacy }, { isLocked }) {
    if (isLocked && !privacy) {
      return { isLocked: false };
    }
    return null;
  }

  componentDidMount() {
    this.state.mounted = true;
    this.auth();
    AppState.addEventListener("change", this.handleAppStateChange);
  }

  componentWillUnmount() {
    this.state.mounted = false;
  }

  appInBg: number;

  handleAppStateChange = nextAppState => {
    if (
      this.state.appState.match(/inactive|background/) &&
      nextAppState === "active" &&
      this.appInBg + AUTOLOCK_TIMEOUT < Date.now()
    ) {
      this.lock();
      this.appInBg = Date.now();
    } else if (
      nextAppState === "background" ||
      this.state.appState === "active"
    ) {
      this.appInBg = Date.now();
    }
    if (this.state.mounted) this.setState({ appState: nextAppState });
  };

  // auth: try to auth with biometrics and fallback on password
  auth = () => {
    const { privacy } = this.props;
    const { isLocked, authModalOpen } = this.state;
    if (
      isLocked &&
      privacy &&
      privacy.biometricsEnabled &&
      !authModalOpen &&
      this.state.mounted
    ) {
      this.setState({ authModalOpen: true });
    }
  };

  onSuccess = () => {
    if (this.state.mounted) this.setState({ authModalOpen: false });
    this.unlock();
  };

  onError = error => {
    if (this.state.mounted) {
      this.setState({ authModalOpen: false });
      this.setState({
        biometricsError: error,
      });
    }
  };

  // lock the app
  lock = () => {
    if (!this.props.privacy || this.state.skipLockCount) return;

    wasUnlocked = false;
    if (this.state.mounted) {
      this.setState(
        {
          isLocked: true,
          biometricsError: null,
        },
        () => this.auth(),
      );
    }
  };

  // unlock the app
  unlock = () => {
    wasUnlocked = true;
    if (this.state.mounted) {
      this.setState({
        isLocked: false,
        biometricsError: null,
      });
    }
  };

  render() {
    const { children, privacy } = this.props;
    const { isLocked, biometricsError, setEnabled, authModalOpen } = this.state;
    let lockScreen = null;
    if (isLocked && privacy) {
      lockScreen = (
        <View style={styles.container}>
          <AuthScreen
            biometricsError={biometricsError}
            privacy={privacy}
            lock={this.lock}
            unlock={this.unlock}
          />
          <RequestBiometricAuth
            disabled={!authModalOpen}
            onSuccess={this.onSuccess}
            onError={this.onError}
          />
        </View>
      );
    }
    return (
      <SkipLockContext.Provider value={setEnabled}>
        {lockScreen}
        {children}
      </SkipLockContext.Provider>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    position: "absolute",
    top: 0,
    left: 0,
    bottom: 0,
    right: 0,
    zIndex: 10,
  },
});

// $FlowFixMe
const m: React$AbstractComponent<OwnProps> = compose(
  withTranslation(),
  connect(mapStateToProps),
)(AuthPass);

export default m;


<|start_filename|>src/icons/QRcodeZoom.js<|end_filename|>
// @flow
import React, { PureComponent } from "react";
import Svg, { Path, G, Circle } from "react-native-svg";

type Props = {
  size: number,
};

export default class QRcodeZoom extends PureComponent<Props> {
  render() {
    const { size } = this.props;

    return (
      <Svg viewBox="0 0 72 72" width={size || 72} height={size || 72}>
        <G fill="none" fillRule="evenodd">
          <Path
            d="M0 30.857h30.857V0H0v30.857zM6.429 6.43h18v18h-18v-18zM41.143 0v30.857H72V0H41.143zM65.57 24.429h-18v-18h18v18zM0 72h30.857V41.143H0V72zm6.429-24.429h18v18h-18v-18zm5.142 5.143h7.715v7.715H11.57v-7.715zm0-41.143h7.715v7.715H11.57V11.57zm48.858 7.715h-7.715V11.57h7.715v7.715zm6.428 21.857H72v20.571H51.429v-5.143h-5.143V72h-5.143V41.143H56.57v5.143h10.286v-5.143zm0 25.714H72V72h-5.143v-5.143zm-10.286 0h5.143V72h-5.143v-5.143z"
            fill="#D8D8D8"
            fillRule="nonzero"
          />
          <G transform="translate(16 16)">
            <Circle stroke="#D8D8D8" fill="#FFF" cx={20} cy={20} r={19.5} />
            <Path
              d="M16 10a1 1 0 0 1 0 2h-3a1 1 0 0 0-1 1v3a1 1 0 0 1-2 0v-3a3 3 0 0 1 3-3h3zm14 6a1 1 0 0 1-2 0v-3a1 1 0 0 0-1-1h-3a1 1 0 0 1 0-2h3a3 3 0 0 1 3 3v3zm-6 14a1 1 0 0 1 0-2h3a1 1 0 0 0 1-1v-3a1 1 0 0 1 2 0v3a3 3 0 0 1-3 3h-3zm-14-6a1 1 0 0 1 2 0v3a1 1 0 0 0 1 1h3a1 1 0 0 1 0 2h-3a3 3 0 0 1-3-3v-3zm6.2-9h7.6a1.2 1.2 0 0 1 1.2 1.2v7.6a1.2 1.2 0 0 1-1.2 1.2h-7.6a1.2 1.2 0 0 1-1.2-1.2v-7.6a1.2 1.2 0 0 1 1.2-1.2z"
              fill="#bdb3ff"
              fillRule="nonzero"
            />
          </G>
        </G>
      </Svg>
    );
  }
}


<|start_filename|>src/families/polkadot/components/NominationDrawer.js<|end_filename|>
// @flow
import React from "react";
import type { ComponentType } from "react";
import { View, StyleSheet, ScrollView } from "react-native";
import { getAccountCurrency } from "@ledgerhq/live-common/lib/account";
import { getCurrencyColor } from "@ledgerhq/live-common/lib/currencies";
import type { AccountLike } from "@ledgerhq/live-common/lib/types";
// TODO move to component
import { useTheme } from "@react-navigation/native";
import DelegatingContainer from "../../tezos/DelegatingContainer";
import { rgba } from "../../../colors";
import getWindowDimensions from "../../../logic/getWindowDimensions";
import BottomModal from "../../../components/BottomModal";
import Circle from "../../../components/Circle";
import LText from "../../../components/LText";
import CurrencyIcon from "../../../components/CurrencyIcon";
import IconHelp from "../../../icons/Info";

import { normalize } from "../../../helpers/normalizeSize";

const { height } = getWindowDimensions();

type Props = {
  isOpen: boolean,
  onClose: () => void,
  account: AccountLike,
  icon?: React$Node,
  ValidatorImage: ComponentType<{ size: number }>,
  data: FieldType[],
  isNominated?: boolean,
};

export default function NominationDrawer({
  isOpen,
  onClose,
  account,
  ValidatorImage,
  data,
  icon,
  isNominated,
}: Props) {
  const currency = getAccountCurrency(account);
  const color = getCurrencyColor(currency);

  const iconWidth = normalize(64);

  return (
    <BottomModal
      id="InfoModal"
      style={styles.modal}
      isOpened={isOpen}
      onClose={onClose}
    >
      <View style={styles.root}>
        {isNominated ? (
          <DelegatingContainer
            left={
              icon || (
                <Circle size={iconWidth} bg={rgba(color, 0.2)}>
                  <CurrencyIcon
                    size={iconWidth / 2}
                    currency={currency}
                    bg={"rgba(0,0,0,0)"}
                  />
                </Circle>
              )
            }
            right={<ValidatorImage size={iconWidth} />}
          />
        ) : (
          <View style={styles.iconContainer}>
            <ValidatorImage size={iconWidth} />
          </View>
        )}

        <ScrollView
          style={styles.scrollSection}
          showsVerticalScrollIndicator={true}
        >
          {data.map((field, i) => (
            <DataField
              {...field}
              key={"data-" + i}
              isLast={i === data.length - 1}
            />
          ))}
        </ScrollView>
      </View>
    </BottomModal>
  );
}

type FieldType = {
  label: React$Node,
  info?: React$Node,
  infoType?: "info" | "warning",
  Component: React$Node,
};

type DataFieldProps = FieldType & {
  isLast: boolean,
};

function DataField({
  label,
  info,
  infoType,
  Component,
  isLast,
}: DataFieldProps) {
  const { colors } = useTheme();
  return (
    <View
      style={[
        styles.row,
        { borderBottomColor: colors.lightFog },
        isLast ? styles.lastRow : undefined,
      ]}
    >
      <View style={styles.rowWrapper}>
        <LText
          numberOfLines={1}
          semiBold
          style={styles.labelText}
          color="smoke"
        >
          {label}
        </LText>
        <View style={styles.valueWrapper}>{Component}</View>
      </View>
      {info ? (
        <View style={[styles.infoBox]}>
          <IconHelp
            color={infoType === "warning" ? colors.orange : colors.grey}
            size={16}
          />
          <LText
            style={[styles.infoContent]}
            color={infoType === "warning" ? "orange" : "grey"}
          >
            {info}
          </LText>
        </View>
      ) : null}
    </View>
  );
}

export const styles = StyleSheet.create({
  modal: {
    position: "relative",
  },
  root: {
    paddingTop: 8,
  },
  iconContainer: {
    flexDirection: "row",
    alignSelf: "center",
    alignItems: "center",
    paddingTop: 16,
  },
  scrollSection: {
    maxHeight: height - normalize(425),
    paddingHorizontal: 16,
  },
  rowWrapper: {
    flexDirection: "row",
    alignItems: "flex-start",
    justifyContent: "space-between",
  },
  row: {
    borderBottomWidth: 1,
    paddingVertical: 16,
  },
  lastRow: {
    borderBottomWidth: 0,
  },
  labelText: {
    paddingRight: 8,
    fontSize: 14,
  },
  valueWrapper: {
    width: "50%",
    alignItems: "flex-end",
  },
  infoBox: {
    flexDirection: "row",
    alignItems: "center",
    marginTop: 10,
  },
  infoContent: {
    flex: 1,
    marginLeft: 10,
    alignItems: "center",
  },
});


<|start_filename|>src/screens/PairDevices/index.js<|end_filename|>
// @flow
import React, { useReducer, useCallback, useEffect, useRef } from "react";
import { StyleSheet } from "react-native";
import SafeAreaView from "react-native-safe-area-view";
import { useDispatch, useSelector } from "react-redux";
import { timeout, tap } from "rxjs/operators";
import getDeviceInfo from "@ledgerhq/live-common/lib/hw/getDeviceInfo";
import getDeviceName from "@ledgerhq/live-common/lib/hw/getDeviceName";
import { listApps } from "@ledgerhq/live-common/lib/apps/hw";
import type { DeviceModelId } from "@ledgerhq/devices";
import { delay } from "@ledgerhq/live-common/lib/promise";
import type { Device } from "@ledgerhq/live-common/lib/hw/actions/types";
import { useTheme } from "@react-navigation/native";
import logger from "../../logger";
import TransportBLE from "../../react-native-hw-transport-ble";
import { GENUINE_CHECK_TIMEOUT } from "../../constants";
import { addKnownDevice } from "../../actions/ble";
import {
  installAppFirstTime,
  setLastSeenDeviceInfo,
} from "../../actions/settings";
import { hasCompletedOnboardingSelector } from "../../reducers/settings";
import type { DeviceLike } from "../../reducers/ble";
import RequiresBLE from "../../components/RequiresBLE";
import PendingPairing from "./PendingPairing";
import PendingGenuineCheck from "./PendingGenuineCheck";
import Paired from "./Paired";
import Scanning from "./Scanning";
import ScanningTimeout from "./ScanningTimeout";
import RenderError from "./RenderError";

type Props = {
  navigation: any,
  route: { params: RouteParams },
};

type PairDevicesProps = {
  ...Props,
  knownDevices: DeviceLike[],
  hasCompletedOnboarding: boolean,
  addKnownDevice: DeviceLike => void,
  installAppFirstTime: (value: boolean) => void,
};

type RouteParams = {
  onDone?: (device: Device) => void,
};

type BleDevice = {
  id: string,
  name: string,
  modelId: DeviceModelId,
};

export default function PairDevices(props: PairDevicesProps) {
  const { colors } = useTheme();
  return (
    <RequiresBLE>
      <SafeAreaView
        forceInset={forceInset}
        style={[styles.root, { backgroundColor: colors.background }]}
      >
        <PairDevicesInner {...props} />
      </SafeAreaView>
    </RequiresBLE>
  );
}

function PairDevicesInner({ navigation, route }: Props) {
  const hasCompletedOnboarding = useSelector(hasCompletedOnboardingSelector);
  const dispatchRedux = useDispatch();

  const [
    { error, status, device, skipCheck, genuineAskedOnDevice, name },
    dispatch,
  ] = useReducer(reducer, initialState);

  const unmounted = useRef(false);

  useEffect(
    () => () => {
      unmounted.current = true;
    },
    [],
  );

  const onTimeout = useCallback(() => {
    dispatch({ type: "timeout" });
  }, [dispatch]);

  const onRetry = useCallback(() => {
    navigation.setParams({ hasError: false });
    dispatch({ type: "scanning" });
  }, [dispatch, navigation]);

  const onError = useCallback(
    (error: Error) => {
      logger.critical(error);
      navigation.setParams({ hasError: true });
      dispatch({ type: "error", payload: error });
    },
    [dispatch, navigation],
  );

  const onSelect = useCallback(
    async (bleDevice: BleDevice, deviceMeta: *) => {
      const device = {
        deviceName: bleDevice.name,
        deviceId: bleDevice.id,
        modelId: "nanoX",
        wired: false,
      };
      dispatch({ type: "pairing", payload: device });
      try {
        const transport = await TransportBLE.open(bleDevice);
        if (unmounted.current) return;
        try {
          const deviceInfo = await getDeviceInfo(transport);
          if (__DEV__) console.log({ deviceInfo }); // eslint-disable-line no-console
          if (unmounted.current) return;

          dispatch({ type: "genuinecheck", payload: device });

          let appsInstalled;
          await listApps(transport, deviceInfo)
            .pipe(
              timeout(GENUINE_CHECK_TIMEOUT),
              tap(e => {
                if (e.type === "result") {
                  appsInstalled = e.result && e.result.installed.length;
                  if (!hasCompletedOnboarding) {
                    const hasAnyAppInstalled =
                      e.result && e.result.installed.length > 0;

                    if (!hasAnyAppInstalled) {
                      dispatchRedux(installAppFirstTime(false));
                    }
                  }

                  return;
                }
                dispatch({
                  type: "allowManager",
                  payload: e.type === "allow-manager-requested",
                });
              }),
            )
            .toPromise();

          if (unmounted.current) return;

          const name =
            (await getDeviceName(transport)) || device.deviceName || "";
          if (unmounted.current) return;

          dispatchRedux(
            addKnownDevice({
              id: device.deviceId,
              name,
              deviceInfo,
              appsInstalled,
              modelId: deviceMeta?.modelId,
            }),
          );

          dispatchRedux(
            setLastSeenDeviceInfo({
              modelId: device.modelId,
              deviceInfo,
              appsInstalled,
            }),
          );

          if (unmounted.current) return;
          dispatch({ type: "paired", skipCheck: false });
        } finally {
          transport.close();
          await TransportBLE.disconnect(device.deviceId).catch(() => {});
          await delay(500);
        }
      } catch (error) {
        if (unmounted.current) return;
        console.warn(error);
        onError(error);
      }
    },
    [dispatch, dispatchRedux, hasCompletedOnboarding, onError],
  );

  const onBypassGenuine = useCallback(() => {
    navigation.setParams({ hasError: true });
    if (device) {
      dispatchRedux(
        addKnownDevice({
          id: device.deviceId,
          name: name ?? device.deviceName ?? "",
        }),
      );
      dispatch({ type: "paired", skipCheck: true });
    } else {
      dispatch({ type: "scanning" });
    }
  }, [device, dispatchRedux, name, dispatch, navigation]);

  const onDone = useCallback(
    (device: Device) => {
      navigation.goBack();
      route.params?.onDone?.(device);
    },
    [navigation, route],
  );

  if (error) {
    return (
      <RenderError
        status={status}
        error={error}
        onRetry={onRetry}
        onBypassGenuine={onBypassGenuine}
      />
    );
  }

  switch (status) {
    case "scanning":
      return (
        <Scanning
          // $FlowFixMe
          onSelect={onSelect}
          onError={onError}
          onTimeout={onTimeout}
        />
      );
    case "timeout":
      return <ScanningTimeout onRetry={onRetry} />;
    case "pairing":
      return <PendingPairing />;
    case "genuinecheck":
      return (
        <PendingGenuineCheck genuineAskedOnDevice={genuineAskedOnDevice} />
      );
    case "paired":
      return device ? (
        <Paired device={device} genuine={!skipCheck} onContinue={onDone} />
      ) : null;
    default:
      return null;
  }
}

const forceInset = { bottom: "always" };

const initialState: State = {
  status: "scanning",
  device: null,
  name: null,
  error: null,
  skipCheck: false,
  genuineAskedOnDevice: false,
};

function reducer(state, action) {
  switch (action.type) {
    case "timeout":
      return { ...state, status: "timeout" };
    case "retry":
      return { ...state, status: "scanning", error: null, device: null };
    case "error":
      return { ...state, error: action.payload };
    case "pairing":
      return {
        ...state,
        status: "pairing",
        genuineAskedOnDevice: false,
        device: action.payload,
      };
    case "genuinecheck":
      return { ...state, status: "genuinecheck", device: action.payload };
    case "allowManager":
      return { ...state, genuineAskedOnDevice: action.payload };
    case "paired":
      return {
        ...state,
        status: "paired",
        error: null,
        skipCheck: action.skipCheck,
      };
    case "scanning":
      return { ...state, status: "scanning", error: null, device: null };
    default:
      return state;
  }
}

type State = {
  status: Status,
  device: ?Device,
  name: ?string,
  error: ?Error,
  skipCheck: boolean,
  genuineAskedOnDevice: boolean,
};

type Status = "scanning" | "pairing" | "genuinecheck" | "paired" | "timedout";

const styles = StyleSheet.create({
  root: {
    flex: 1,
  },
});


<|start_filename|>src/screens/Swap/History/EmptyState.js<|end_filename|>
// @flow

import React from "react";
import { View, StyleSheet } from "react-native";
import { Trans } from "react-i18next";
import { useTheme } from "@react-navigation/native";

import LText from "../../../components/LText";

const EmptyState = () => {
  const { colors } = useTheme();
  return (
    <View style={[styles.root, { backgroundColor: colors.background }]}>
      <LText secondary style={styles.title}>
        <Trans i18nKey="transfer.swap.history.empty.title" />
      </LText>
      <LText primary style={styles.desc} color="grey">
        <Trans i18nKey="transfer.swap.history.empty.desc" />
      </LText>
    </View>
  );
};

const styles = StyleSheet.create({
  root: {
    flex: 1,
    alignItems: "center",
    justifyContent: "center",
  },
  title: {
    fontSize: 18,
    textAlign: "center",
    marginTop: 24,
    marginBottom: 8,
  },
  desc: {
    fontSize: 13,
    textAlign: "center",
    marginHorizontal: 24,
  },
});

export default EmptyState;


<|start_filename|>src/logic/secp256k1.js<|end_filename|>
import { secp256k1 } from "react-native-fast-crypto";

export async function publicKeyTweakAdd(publicKey, tweak) {
  const pkeyArr = new Uint8Array(Buffer.from(publicKey).toJSON().data);
  const tweakArr = new Uint8Array(Buffer.from(tweak).toJSON().data);
  const r = await secp256k1.publicKeyTweakAdd(pkeyArr, tweakArr, true);
  return r;
}


<|start_filename|>src/screens/Lending/WithdrawFlow/01-Amount.js<|end_filename|>
/* @flow */
import invariant from "invariant";
import { BigNumber } from "bignumber.js";
import useBridgeTransaction from "@ledgerhq/live-common/lib/bridge/useBridgeTransaction";
import React, { useCallback } from "react";
import { useSelector } from "react-redux";
import { makeCompoundSummaryForAccount } from "@ledgerhq/live-common/lib/compound/logic";
import type { TokenCurrency } from "@ledgerhq/live-common/lib/types";
import {
  findCompoundToken,
  formatCurrencyUnit,
} from "@ledgerhq/live-common/lib/currencies";
import { getAccountBridge } from "@ledgerhq/live-common/lib/bridge";
import { getAccountUnit } from "@ledgerhq/live-common/lib/account/helpers";
import { accountScreenSelector } from "../../../reducers/accounts";
import { localeSelector } from "../../../reducers/settings";
import { ScreenName } from "../../../const";

import AmountScreen from "../shared/01-Amount";
import LendingWarnings from "../shared/LendingWarnings";

type Props = {
  navigation: any,
  route: { params: RouteParams },
};

type RouteParams = {
  accountId: string,
  parentId: string,
  currency: TokenCurrency,
};

export default function WithdrawAmount({ navigation, route }: Props) {
  const { account, parentAccount } = useSelector(accountScreenSelector(route));
  const locale = useSelector(localeSelector);
  invariant(
    account && account.type === "TokenAccount",
    "token account required",
  );

  const capabilities = makeCompoundSummaryForAccount(account, parentAccount);
  const max = capabilities?.totalSupplied ?? BigNumber(0);
  const bridge = getAccountBridge(account, parentAccount);
  const ctoken = findCompoundToken(account.token);
  const unit = getAccountUnit(account);
  const tokenUnit = ctoken?.units[0];

  const {
    transaction,
    setTransaction,
    status,
    bridgePending,
    bridgeError,
  } = useBridgeTransaction(() => {
    // $FlowFixMe
    const t = bridge.createTransaction(account);

    const transaction = bridge.updateTransaction(t, {
      recipient: ctoken?.contractAddress || "",
      mode: "compound.withdraw",
      useAllAmount: true,
      amount: max,
      subAccountId: account.id,
      gasPrice: null,
      userGasLimit: null,
    });

    return { account, parentAccount, transaction };
  });

  invariant(transaction, "transaction required");

  const onChangeSendMax = useCallback(
    (useAllAmount: boolean) => {
      setTransaction(
        bridge.updateTransaction(transaction, {
          useAllAmount,
          amount: useAllAmount ? BigNumber(0) : max,
        }),
      );
    },
    [setTransaction, bridge, transaction, max],
  );

  const onContinue = useCallback(() => {
    const formattedAmount =
      status.amount &&
      formatCurrencyUnit(
        transaction.useAllAmount && tokenUnit ? tokenUnit : unit,
        status.amount,
        {
          showAllDigits: false,
          disableRounding: false,
          showCode: true,
          locale,
        },
      );
    navigation.navigate(ScreenName.LendingWithdrawSummary, {
      ...route.params,
      accountId: account.id,
      parentId: parentAccount && parentAccount.id,
      transaction,
      currentNavigation: ScreenName.LendingWithdrawSummary,
      nextNavigation: ScreenName.LendingWithdrawSelectDevice,
      hideTotal: true,
      overrideAmountLabel: formattedAmount,
    });
  }, [
    account.id,
    navigation,
    parentAccount,
    route.params,
    status.amount,
    tokenUnit,
    transaction,
    unit,
  ]);

  return (
    <>
      <LendingWarnings />
      <AmountScreen
        navigation={navigation}
        route={route}
        transaction={transaction}
        setTransaction={setTransaction}
        status={status}
        bridgePending={bridgePending}
        bridgeError={bridgeError}
        max={max}
        onChangeSendMax={onChangeSendMax}
        onContinue={onContinue}
        category={"Lend Withdraw"}
      />
    </>
  );
}


<|start_filename|>src/screens/Settings/General/RegionRow.js<|end_filename|>
/* @flow */
import React from "react";
import { useNavigation } from "@react-navigation/native";
import { Trans } from "react-i18next";
import { useSelector } from "react-redux";
import SettingsRow from "../../../components/SettingsRow";
import LText from "../../../components/LText";
import { ScreenName } from "../../../const";
import { localeSelector } from "../../../reducers/settings";
import regionsByKey from "./regions.json";

export default function RegionRow() {
  const locale = useSelector(localeSelector);
  const { navigate } = useNavigation();
  const region = regionsByKey[locale];
  return (
    <SettingsRow
      event="LanguageSettingsRow"
      title={<Trans i18nKey="settings.display.region" />}
      desc={<Trans i18nKey="settings.display.regionDesc" />}
      arrowRight
      onPress={() => navigate(ScreenName.RegionSettings)}
      alignedTop
    >
      <LText semiBold color="grey">
        {region ? `${region.regionDisplayName} (${locale})` : locale}
      </LText>
    </SettingsRow>
  );
}


<|start_filename|>src/components/ShareLink.js<|end_filename|>
// @flow

import React, { PureComponent } from "react";
import { StyleSheet, Share } from "react-native";
import { Icons, Text } from "@ledgerhq/native-ui";
import Touchable from "./Touchable";
import { withTheme } from "../colors";

type Props = {
  children: React$Node,
  value: string, // String to be shared
  colors: *,
};

class ShareLink extends PureComponent<Props> {
  onPress = async () => {
    const { value } = this.props;
    await Share.share({
      message: value,
    });
  };

  render() {
    const { children } = this.props;

    return (
      <Touchable
        event="ShareLink"
        style={styles.linkContainer}
        onPress={this.onPress}
      >
        <Icons.ShareMedium size={16} color="primary.c80" />
        <Text variant="body" fontWeight="semiBold" color="primary.c80" ml={3}>
          {children}
        </Text>
      </Touchable>
    );
  }
}

export default withTheme(ShareLink);

const styles = StyleSheet.create({
  linkContainer: {
    alignItems: "center",
    justifyContent: "center",
    flexDirection: "row",
  },
});


<|start_filename|>src/screens/SkipSelectDevice.js<|end_filename|>
// @flow
import { useEffect, useState } from "react";
import { useSelector } from "react-redux";
import { NativeModules } from "react-native";
import { discoverDevices } from "@ledgerhq/live-common/lib/hw";
import { lastConnectedDeviceSelector } from "../reducers/settings";
import { knownDevicesSelector } from "../reducers/ble";

type Props = {
  onResult: (device: any) => void,
  route?: { params: any },
};

let usbTimeout;

export default function SkipSelectDevice({ onResult, route }: Props) {
  const lastConnectedDevice = useSelector(lastConnectedDeviceSelector);
  const [hasUSB, setHasUSB] = useState(false);
  const knownDevices = useSelector(knownDevicesSelector);
  const forceSelectDevice = route?.params?.forceSelectDevice;

  useEffect(() => {
    const subscription = discoverDevices(() => true).subscribe(e => {
      setHasUSB(e.id.startsWith("usb|"));
    });
    return () => subscription.unsubscribe();
  }, [knownDevices]);

  useEffect(() => {
    if (
      !forceSelectDevice &&
      knownDevices?.length > 0 &&
      !hasUSB &&
      lastConnectedDevice
    ) {
      // timeout so we have the time to detect usb connection
      usbTimeout = setTimeout(() => {
        NativeModules.BluetoothHelperModule.prompt()
          .then(() => onResult(lastConnectedDevice))
          .catch(() => {
            /* ignore */
          });
      }, 500);
    } else {
      clearTimeout(usbTimeout);
    }
  }, [
    forceSelectDevice,
    hasUSB,
    knownDevices?.length,
    lastConnectedDevice,
    onResult,
  ]);

  return null;
}


<|start_filename|>src/icons/Lock.js<|end_filename|>
// @flow

import React from "react";
import Svg, { Path } from "react-native-svg";

const Lock = ({ size, color }: { size: number, color: string }) => (
  <Svg width={size} height={size} viewBox="0 0 16 18" fill="none">
    <Path
      d="M12.4688 7H11.6562V5.83203C11.6562 3.80078 10.0312 2.15039 8 2.125C5.96875 2.125 4.34375 3.77539 4.34375 5.78125V7H3.53125C2.8457 7 2.3125 7.55859 2.3125 8.21875V13.9062C2.3125 14.5918 2.8457 15.125 3.53125 15.125H12.4688C13.1289 15.125 13.6875 14.5918 13.6875 13.9062V8.21875C13.6875 7.55859 13.1289 7 12.4688 7ZM5.5625 5.78125C5.5625 4.46094 6.6543 3.34375 8 3.34375C9.32031 3.34375 10.4375 4.46094 10.4375 5.78125V7H5.5625V5.78125ZM12.4688 13.9062H3.53125V8.21875H12.4688V13.9062Z"
      fill={color}
    />
  </Svg>
);

export default Lock;


<|start_filename|>src/families/cosmos/DelegationFlow/01-Started.js<|end_filename|>
// @flow
import React, { useCallback } from "react";
import { View, StyleSheet, Linking } from "react-native";
import { Trans } from "react-i18next";
import { useTheme } from "@react-navigation/native";
import { Alert, Button, Flex } from "@ledgerhq/native-ui";
import { ScreenName } from "../../../const";
import LText from "../../../components/LText";

import ExternalLink from "../../../components/ExternalLink";
import BulletList, { BulletGreenCheck } from "../../../components/BulletList";
import NavigationScrollView from "../../../components/NavigationScrollView";
import { urls } from "../../../config/urls";
import { TrackScreen } from "../../../analytics";
import Illustration from "../../../images/illustration/Illustration";
import EarnLight from "../../../images/illustration/Light/_003.png";
import EarnDark from "../../../images/illustration/Dark/_003.png";

type RouteParams = {
  accountId: string,
};

type Props = {
  navigation: any,
  route: { params: RouteParams },
};

export default function DelegationStarted({ navigation, route }: Props) {
  const { colors } = useTheme();
  const onNext = useCallback(() => {
    navigation.navigate(ScreenName.CosmosDelegationValidator, {
      ...route.params,
    });
  }, [navigation, route.params]);

  const howDelegationWorks = useCallback(() => {
    Linking.openURL(urls.cosmosStakingRewards);
  }, []);

  return (
    <View style={[styles.root, { backgroundColor: colors.background }]}>
      <NavigationScrollView
        style={styles.scroll}
        contentContainerStyle={styles.scrollContainer}
      >
        <TrackScreen category="DelegationFlow" name="Started" />
        <Flex alignItems="center" mb={6}>
          <Illustration
            lightSource={EarnLight}
            darkSource={EarnDark}
            size={150}
          />
        </Flex>
        <LText semiBold style={styles.description}>
          <Trans i18nKey="cosmos.delegation.flow.steps.starter.description" />
        </LText>
        <BulletList
          Bullet={BulletGreenCheck}
          list={[
            <Trans i18nKey="cosmos.delegation.flow.steps.starter.steps.0" />,
            <Trans i18nKey="cosmos.delegation.flow.steps.starter.steps.1" />,
            <Trans i18nKey="cosmos.delegation.flow.steps.starter.steps.2" />,
          ].map(wording => (
            <LText
              semiBold
              style={[styles.bulletItem, { color: colors.black }]}
            >
              {wording}
            </LText>
          ))}
        />
        <View style={[styles.howDelegationWorks, { borderColor: colors.live }]}>
          <ExternalLink
            event="DelegationStartedHowDelegationWorks"
            onPress={howDelegationWorks}
            text={<Trans i18nKey="delegation.howDelegationWorks" />}
            ltextProps={{
              secondary: true,
            }}
          />
        </View>
      </NavigationScrollView>
      <View style={[styles.footer, { borderTopColor: colors.lightFog }]}>
        <View style={styles.warning}>
          <Alert
            type="info"
            title={
              <Trans i18nKey="cosmos.delegation.flow.steps.starter.warning.description" />
            }
          />
        </View>
        <Button
          event="DelegationStartedBtn"
          onPress={onNext}
          type="main"
          mt={6}
        >
          <Trans i18nKey="cosmos.delegation.flow.steps.starter.cta" />
        </Button>
      </View>
    </View>
  );
}

const styles = StyleSheet.create({
  root: {
    flex: 1,
  },
  rewards: {
    height: 150,
    width: 150,
  },
  scroll: {
    flex: 1,
  },
  scrollContainer: {
    paddingHorizontal: 32,
    paddingVertical: 32,
    alignItems: "center",
  },
  title: {
    fontSize: 22,
    lineHeight: 33,

    paddingVertical: 16,
  },
  description: {
    fontSize: 16,
    lineHeight: 21,

    textAlign: "center",
    marginBottom: 16,
  },
  bulletItem: {
    fontSize: 14,
  },
  howDelegationWorks: {
    paddingVertical: 8,
    paddingHorizontal: 16,

    flexDirection: "row",
  },
  learnMoreBtn: {
    alignSelf: "flex-start",
    paddingHorizontal: 0,
  },
  footer: {
    padding: 16,
    borderTopWidth: 1,
  },
});


<|start_filename|>src/components/CopyLink.js<|end_filename|>
// @flow

import React, { PureComponent } from "react";
import { StyleSheet } from "react-native";
import Clipboard from "@react-native-community/clipboard";
import { Icons, Text } from "@ledgerhq/native-ui";
import Touchable from "./Touchable";
import { withTheme } from "../colors";

type Props = {
  style?: *,
  children: string | React$Element<*>,
  string: string, // String to be copied
  replacement?: string | React$Element<*>, // String to display in place of children on copy
  colors: *,
};

type State = {
  copied: boolean,
};

class CopyLink extends PureComponent<Props, State> {
  state = {
    copied: false,
  };

  timeout = null;

  onPress = () => {
    const { string } = this.props;

    Clipboard.setString(string);

    this.setState({ copied: true });

    this.timeout = setTimeout(() => {
      this.setState({ copied: false });
    }, 3000);
  };

  render() {
    const { style, children, replacement } = this.props;
    const { copied } = this.state;
    return (
      <Touchable
        event="CopyLink"
        style={[styles.linkContainer, style]}
        onPress={this.onPress}
      >
        <Icons.CopyMedium
          size={16}
          color={copied ? "neutral.c70" : "primary.c80"}
        />
        <Text
          variant="body"
          fontWeight="semiBold"
          color={copied ? "neutral.c70" : "primary.c80"}
          ml={3}
        >
          {copied && replacement ? replacement : children}
        </Text>
      </Touchable>
    );
  }

  componentWillUnmount() {
    if (this.timeout) {
      clearTimeout(this.timeout);
    }
  }
}

export default withTheme(CopyLink);

const styles = StyleSheet.create({
  linkContainer: {
    alignItems: "center",
    justifyContent: "center",
    flexDirection: "row",
  },
  linkText: {
    marginLeft: 6,
  },
});


<|start_filename|>src/screens/DebugEnv.js<|end_filename|>
// @flow

import React, { useState, useCallback } from "react";
import { View, Text } from "react-native";
import Config from "react-native-config";
import { setEnvUnsafe } from "@ledgerhq/live-common/lib/env";
import NavigationScrollView from "../components/NavigationScrollView";
import Button from "../components/Button";
import TextInput from "../components/TextInput";

export default function DebugEnv() {
  const [value, setValue] = useState();
  const [status, setStatus] = useState();

  const onSetEnv = useCallback(() => {
    if (!value) return;

    // Attempt to parse this input
    const match = /([\w]+)=([\w]+)/.exec(value);
    if (!match || match.length !== 3) {
      setStatus("Can't parse input");
    } else if (match[2] === "0") {
      setStatus(`Unsetting ${match[1]}`);
      setEnvUnsafe(match[1], match[2]);
      delete Config[match[1]];
    } else {
      setStatus(`Set value '${match[2]}' for '${match[1]}'`);
      Config[match[1]] = match[2];
      setEnvUnsafe(match[1], match[2]);
    }
  }, [value]);

  return (
    <NavigationScrollView>
      <View style={{ padding: 16, flex: 1 }}>
        <Text>{status}</Text>
        <TextInput
          maxLength={100}
          onChangeText={setValue}
          placeholder={"DEBUG_THEME=1"}
        />
        <Button
          event="DebugEnv"
          type="primary"
          title={"Set env"}
          containerStyle={{ marginBottom: 16 }}
          onPress={onSetEnv}
        />
      </View>
    </NavigationScrollView>
  );
}


<|start_filename|>src/families/cosmos/shared/02-SelectAmount.js<|end_filename|>
// @flow
import invariant from "invariant";
import React, { useCallback, useState, useMemo } from "react";
import { View, StyleSheet, Keyboard, TouchableOpacity } from "react-native";
import { Trans } from "react-i18next";
import { useSelector } from "react-redux";
import { BigNumber } from "bignumber.js";

import type {
  CosmosValidatorItem,
  Transaction,
} from "@ledgerhq/live-common/lib/families/cosmos/types";

import { getAccountBridge } from "@ledgerhq/live-common/lib/bridge";
import { getAccountUnit } from "@ledgerhq/live-common/lib/account";
import { formatCurrencyUnit } from "@ledgerhq/live-common/lib/currencies";

import { useTheme } from "styled-components/native";
import { accountScreenSelector } from "../../../reducers/accounts";
import { localeSelector } from "../../../reducers/settings";
import Button from "../../../components/Button";
import CurrencyInput from "../../../components/CurrencyInput";
import LText from "../../../components/LText";
import Warning from "../../../icons/Warning";
import Check from "../../../icons/Check";
import KeyboardView from "../../../components/KeyboardView";

type RouteParams = {
  accountId: string,
  transaction: Transaction,
  validator: CosmosValidatorItem,
  validatorSrc?: CosmosValidatorItem,
  min?: BigNumber,
  max?: BigNumber,
  value?: BigNumber,
  redelegatedBalance?: BigNumber,
  mode: string,
  nextScreen: string,
};

type Props = {
  navigation: any,
  route: { params: RouteParams },
};

function DelegationAmount({ navigation, route }: Props) {
  const { colors } = useTheme();
  const { account } = useSelector(accountScreenSelector(route));
  const locale = useSelector(localeSelector);

  invariant(
    account && account.cosmosResources && route.params.transaction,
    "account and cosmos transaction required",
  );

  const bridge = getAccountBridge(account, undefined);
  const unit = getAccountUnit(account);

  const initialValue = useMemo(() => route?.params?.value ?? BigNumber(0), [
    route,
  ]);

  const redelegatedBalance = route?.params?.redelegatedBalance ?? BigNumber(0);
  const mode = route?.params?.mode ?? "delegation";

  const [value, setValue] = useState(() => initialValue);

  const initialMax = useMemo(() => route?.params?.max ?? BigNumber(0), [route]);

  const max = useMemo(() => initialMax.minus(value.minus(initialValue)), [
    initialValue,
    initialMax,
    value,
  ]);

  const min = useMemo(() => route?.params?.min ?? BigNumber(0), [route]);

  const onNext = useCallback(() => {
    const tx = route.params.transaction;
    const validators = tx.validators;
    const validatorAddress = route.params.validator.validatorAddress;

    const i = validators.findIndex(
      ({ address }) => address === validatorAddress,
    );

    if (i >= 0) {
      validators[i].amount = value;
    } else {
      validators.push({ address: validatorAddress, amount: value });
    }

    const filteredValidators =
      tx.mode === "delegate"
        ? validators.filter(v => !v.amount.eq(0))
        : validators;

    const transaction = bridge.updateTransaction(tx, {
      validators: filteredValidators,
    });

    navigation.navigate(route.params.nextScreen, {
      ...route.params,
      transaction,
      fromSelectAmount: true,
    });
  }, [navigation, route.params, bridge, value]);

  const [ratioButtons] = useState(
    [0.25, 0.5, 0.75, 1].map(ratio => ({
      label: `${ratio * 100}%`,
      value: initialMax
        .plus(initialValue)
        .multipliedBy(ratio)
        .integerValue(),
    })),
  );

  const error = useMemo(
    () =>
      max.lt(0) ||
      value.lt(min) ||
      (route.params.transaction.mode === "redelegate" && value.eq(0)),
    [value, max, min, route.params.transaction],
  );

  return (
    <View style={[styles.root, { backgroundColor: colors.background.main }]}>
      <KeyboardView>
        <View style={styles.main}>
          <CurrencyInput
            unit={unit}
            value={value}
            onChange={setValue}
            inputStyle={styles.inputStyle}
            hasError={error}
          />
          <View style={styles.ratioButtonContainer}>
            {ratioButtons.map(({ label, value: v }) => (
              <TouchableOpacity
                key={label}
                style={[
                  styles.ratioButton,
                  value.eq(v)
                    ? {
                        backgroundColor: colors.primary.c80,
                      }
                    : { borderColor: colors.neutral.c60 },
                ]}
                onPress={() => {
                  Keyboard.dismiss();
                  setValue(v);
                }}
              >
                <LText
                  style={[styles.ratioLabel]}
                  color={value.eq(v) ? colors.neutral.c100 : colors.neutral.c60}
                >
                  {label}
                </LText>
              </TouchableOpacity>
            ))}
          </View>
        </View>

        <View
          style={[styles.footer, { backgroundColor: colors.background.main }]}
        >
          {error && !value.eq(0) && (
            <View style={styles.labelContainer}>
              <Warning size={16} color={colors.error.c100} />
              <LText style={[styles.assetsRemaining]} color={colors.error.c100}>
                <Trans
                  i18nKey={
                    value.lt(min)
                      ? "cosmos.delegation.flow.steps.amount.minAmount"
                      : "cosmos.delegation.flow.steps.amount.incorrectAmount"
                  }
                  values={{
                    min: formatCurrencyUnit(unit, min, {
                      showCode: true,
                      showAllDigits: true,
                      locale,
                    }),
                    max: formatCurrencyUnit(unit, initialMax, {
                      showCode: true,
                      showAllDigits: true,
                      locale,
                    }),
                  }}
                >
                  <LText semiBold>{""}</LText>
                </Trans>
              </LText>
            </View>
          )}
          {max.isZero() && (
            <View style={styles.labelContainer}>
              <Check size={16} color={colors.success.c100} />
              <LText
                style={[styles.assetsRemaining]}
                color={colors.success.c100}
              >
                <Trans
                  i18nKey={`cosmos.${mode}.flow.steps.amount.allAssetsUsed`}
                />
              </LText>
            </View>
          )}
          {max.gt(0) && !error && (
            <View style={styles.labelContainer}>
              <LText style={styles.assetsRemaining}>
                <Trans
                  i18nKey="cosmos.delegation.flow.steps.amount.assetsRemaining"
                  values={{
                    amount: formatCurrencyUnit(unit, max, {
                      showCode: true,
                      locale,
                    }),
                  }}
                >
                  <LText semiBold>{""}</LText>
                </Trans>
              </LText>
            </View>
          )}
          {!error && redelegatedBalance.gt(0) && (
            <View style={[styles.labelContainer, styles.labelSmall]}>
              <LText style={[styles.assetsRemaining, styles.small]}>
                <Trans
                  i18nKey="cosmos.redelegation.flow.steps.amount.newRedelegatedBalance"
                  values={{
                    amount: formatCurrencyUnit(
                      unit,
                      redelegatedBalance.plus(value),
                      {
                        showCode: true,
                        locale,
                      },
                    ),
                    name: route.params.validator?.name ?? "",
                  }}
                >
                  <LText semiBold>{""}</LText>
                </Trans>
              </LText>
            </View>
          )}
          <Button
            disabled={error}
            event="Cosmos DelegationAmountContinueBtn"
            onPress={onNext}
            title={<Trans i18nKey="cosmos.delegation.flow.steps.amount.cta" />}
            type="primary"
          />
        </View>
      </KeyboardView>
    </View>
  );
}

const styles = StyleSheet.create({
  root: {
    flex: 1,

    padding: 16,
  },
  main: {
    flex: 1,
    alignItems: "center",
    justifyContent: "center",
    minHeight: 150,
  },
  inputStyle: { textAlign: "center", fontSize: 40, fontWeight: "600" },
  ratioButtonContainer: {
    flexDirection: "row",
    justifyContent: "center",
    alignContent: "center",
    height: 36,
    marginTop: 16,
  },
  ratioButton: {
    marginHorizontal: 5,
    width: 60,
    borderWidth: 1,
    borderRadius: 4,

    backgroundColor: "rgba(0,0,0,0)",
    paddingVertical: 8,
  },
  ratioPrimaryButton: {},
  ratioLabel: { textAlign: "center" },
  footer: {
    alignSelf: "stretch",
    padding: 8,
  },
  labelContainer: {
    width: "100%",
    flexDirection: "row",
    alignItems: "center",
    justifyContent: "center",
    paddingBottom: 16,
  },
  labelSmall: {
    paddingBottom: 4,
  },
  assetsRemaining: {
    fontSize: 14,
    textAlign: "center",
    lineHeight: 32,
    paddingHorizontal: 10,
  },
  small: {
    fontSize: 11,
    lineHeight: 16,
  },
});

export default DelegationAmount;


<|start_filename|>src/screens/Swap/History/OperationRow.js<|end_filename|>
// @flow
import React, { useCallback } from "react";
import { TouchableOpacity, StyleSheet, View } from "react-native";
import { useNavigation, useTheme } from "@react-navigation/native";
import Icon from "react-native-vector-icons/dist/Ionicons";

import type { MappedSwapOperation } from "@ledgerhq/live-common/lib/exchange/swap/types";
import {
  getAccountUnit,
  getAccountName,
} from "@ledgerhq/live-common/lib/account/helpers";

import LText from "../../../components/LText";
import CurrencyUnitValue from "../../../components/CurrencyUnitValue";
import { ScreenName } from "../../../const";

import SwapStatusIndicator from "../SwapStatusIndicator";

const OperationRow = ({ item }: { item: MappedSwapOperation }) => {
  const { colors } = useTheme();
  const { swapId, fromAccount, toAccount, fromAmount, toAmount, status } = item;
  const navigation = useNavigation();

  const onOpenOperationDetails = useCallback(() => {
    navigation.navigate(ScreenName.SwapOperationDetails, {
      swapOperation: item,
    });
  }, [item, navigation]);

  return (
    <TouchableOpacity key={swapId} onPress={onOpenOperationDetails}>
      <View
        style={[
          styles.root,
          { backgroundColor: colors.card, borderBottomColor: colors.lightFog },
        ]}
      >
        <SwapStatusIndicator small status={status} />
        <View style={[styles.accountWrapper, { marginLeft: 18 }]}>
          <LText numberOfLines={1} semiBold style={styles.name}>
            {getAccountName(fromAccount)}
          </LText>
          <LText tertiary style={styles.amount}>
            <CurrencyUnitValue
              showCode
              unit={getAccountUnit(fromAccount)}
              value={fromAmount}
            />
          </LText>
        </View>
        <View style={styles.arrow}>
          <Icon name={"ios-arrow-forward"} size={30} color={colors.fog} />
        </View>
        <View style={[styles.accountWrapper, { alignItems: "flex-end" }]}>
          <LText numberOfLines={1} semiBold style={styles.name}>
            {getAccountName(toAccount)}
          </LText>
          <LText tertiary style={styles.amount} color="grey">
            <CurrencyUnitValue
              showCode
              unit={getAccountUnit(toAccount)}
              value={toAmount}
            />
          </LText>
        </View>
      </View>
    </TouchableOpacity>
  );
};

const styles = StyleSheet.create({
  root: {
    flex: 1,
    padding: 16,
    flexDirection: "row",
    borderBottomWidth: 1,
  },
  arrow: {
    flex: 1,
    alignItems: "center",
  },
  name: {
    flexShrink: 1,
    fontSize: 14,
    lineHeight: 19,
    marginBottom: 2,
  },
  amount: {
    fontSize: 13,
    lineHeight: 15,
  },
  accountWrapper: {
    width: "35%",
  },
});

export default OperationRow;


<|start_filename|>src/icons/ConnectNanoXIllustration.js<|end_filename|>
import React from "react";
import Svg, {
  Defs,
  Path,
  Ellipse,
  LinearGradient,
  Stop,
  G,
  Mask,
  Use,
  Rect,
} from "react-native-svg";
import getWindowDimensions from "../logic/getWindowDimensions";

const { width } = getWindowDimensions();
const ConnectNanoXIllustration = props => (
  <Svg
    width={width * 0.7}
    height={((width * 0.7) / 194) * 149}
    viewBox="0 0 194 149"
    {...props}
  >
    <Defs>
      <Path
        id="prefix__a"
        d="M3.53.52l51.082 5.415a1.48 1.48 0 0 1 1.314 1.63L54.6 20.314a1.479 1.479 0 0 1-1.622 1.321h-.002L1.893 16.221A1.482 1.482 0 0 1 .58 14.59L1.906 1.84A1.479 1.479 0 0 1 3.529.519h.001V.52z"
      />
      <Ellipse id="prefix__d" cx={9.11} cy={8.875} rx={4.282} ry={4.317} />
      <Ellipse id="prefix__f" cx={48.258} cy={13.316} rx={4.282} ry={4.317} />
      <LinearGradient
        id="prefix__h"
        x1="50%"
        x2="50%"
        y1="100.845%"
        y2="2.104%"
      >
        <Stop offset="0%" stopColor="#165EDB" />
        <Stop offset="100%" stopColor="#99B9FF" />
      </LinearGradient>
    </Defs>
    <G fill="none" fillRule="evenodd">
      <Path
        fill="#F8B692"
        fillRule="nonzero"
        d="M189.51 126.164c.316 2.342 1.718 4.139 4.205 15.497a2.894 2.894 0 0 1-1.106 2.947c-1.769 1.305-3.563 2.261-5.384 2.87-5.006 1.673-13.485.008-13.508 0-1.182-.449-1.89-2.689-3.241-6.785l-1.165-2.876 2.546-30.397-6.418-37.898 1.546-11.398 3.708-1.622c4.522-.459 7.585.056 9.187 1.544 3.928 3.649 5.092 9.455 6.626 15.698 1.38 5.614 3.79 9.518 4.829 15.887 1.011 6.204-3.107 27.012-1.824 36.533zM160.544 71.45l3.848.487-1.288 9.772c.476 4.566-.2 7.632-2.028 9.197-1.055.903-2.546.514-2.914-.06-.588-.919.681-2.777.166-5.215-.673-3.18-.811-8.305.747-10.544 1.038-1.493 1.528-2.706 1.469-3.637z"
      />
      <Path
        fill="#F29575"
        fillRule="nonzero"
        d="M163.51 82.598c1.428 1.653 2.077 2.683 1.946 3.09-.36 1.119-1.516 1.824-2.485 2.75-.778.743-.502 1.185-1.989 2.625.327-1.246.04-2.367.088-2.975.101-1.291-.1-1.395.049-1.884-.246.412-.997 1.362-2.39.52-.248-.15-.392-.379-.433-.687l5.214-3.439z"
      />
      <Path
        fill="#FFA583"
        fillRule="nonzero"
        d="M162.053 67.048l2.434-1.292-.59 10.787c-.11 4.886-1.293 8.041-3.547 9.465-3.382 2.134-3.05-2.812-4.413-7.583-1.363-4.771-1.294-9.904.756-12.278 1.367-1.584 3.154-1.283 5.36.901z"
      />
      <Path
        fill="#FFC7B1"
        fillRule="nonzero"
        d="M162.053 67.048l2.434-1.292-.59 10.787c-.11 4.886-1.293 8.041-3.547 9.465-3.382 2.134-3.05-2.812-4.413-7.583-1.363-4.771-1.294-9.904.756-12.278 1.367-1.584 3.154-1.283 5.36.901z"
      />
      <Path
        fill="#F29575"
        fillRule="nonzero"
        d="M155.716 74.485l7.291-4.096c-.137 6.578-.29 10.95-.46 13.115-.492-1.998-.394-4.277-.485-5.183-.092-.905-.214-1.405-1.205-2.216.806.062.713.218 1.054 0 .342-.22.09-1.357.007-3.186-2.464 2.48-4.531 3.001-6.202 1.566z"
      />
      <Path
        fill="#FFA583"
        fillRule="nonzero"
        d="M164.42 57.621l2.724-.377-2.985 10.864c-2.753 4.969-5.344 7.222-7.774 6.76-3.644-.693-1.52-3.388-1.18-8.346.34-4.957 1.61-7.545 4.34-9.072 1.821-1.018 3.545-2.644 4.875.171z"
      />
      <Path
        fill="#F29575"
        fillRule="nonzero"
        d="M173.765 68.6c-1.388-2.965 0-1.56.56-.937.847 1.55 1.99 3.163 3.43 4.838-1.734-.623-3.064-1.923-3.99-3.9z"
      />
      <G transform="rotate(-6 593.032 -1102.936)">
        <Mask id="prefix__b" fill="#fff">
          <Use xlinkHref="#prefix__a" />
        </Mask>
        <Use fill="#383838" fillRule="nonzero" xlinkHref="#prefix__a" />
        <G fillRule="nonzero" mask="url(#prefix__b)">
          <Use fill="#000" filter="url(#prefix__c)" xlinkHref="#prefix__d" />
          <Use stroke="#2E2E2E" strokeWidth={0.5} xlinkHref="#prefix__d" />
        </G>
        <Path
          fill="#000"
          fillRule="nonzero"
          d="M53.62 18.702L29.5 57.62a.982.982 0 0 1-1.357.317l-.003-.002-11.611-7.315a.991.991 0 0 1-.313-1.36L40.34 10.334c2.279-3.677 7.095-4.803 10.758-2.514l.023.015c3.671 2.312 4.788 7.171 2.498 10.866z"
          mask="url(#prefix__b)"
        />
        <Path
          fill="#D6D6D6"
          fillRule="nonzero"
          d="M55.423 16.199L37.106 57.677a1.475 1.475 0 0 1-1.949.754l-.004-.001-11.608-5.211a1.486 1.486 0 0 1-.748-1.956l18.32-41.486c1.75-3.96 6.364-5.747 10.308-3.991l.021.01c3.954 1.774 5.732 6.427 3.977 10.403z"
        />
        <G fillRule="nonzero">
          <Use fill="#000" filter="url(#prefix__e)" xlinkHref="#prefix__f" />
          <Use stroke="#D0D0D0" strokeWidth={0.5} xlinkHref="#prefix__f" />
        </G>
      </G>
      <Path
        fill="#F29575"
        fillRule="nonzero"
        d="M178.046 78.4c-1.308 2.187-1.955 4.207-1.94 6.058.024 2.778-.802-1.122-.58-2.901.148-1.187.988-2.239 2.52-3.156z"
      />
      <G fillRule="nonzero">
        <Path
          fill="#C1C1C1"
          d="M167.734 59.544c2.005-.164 2.471 16.758-1.858 22.531-.434.578-2.727 17.877-4.538 26.29-.61 2.83-1.274 2.108-3.183 1.495.003-4.616 2.965-22.13 2.985-24.407.017-1.842.353-15.757 1.285-18.711.933-2.954 1.903-6.608 5.309-7.198z"
        />
        <Path
          fill="#F8B692"
          d="M169.564 138.624c-1.388-5.14-3.706-11.1-6.953-17.878-4.872-10.167-.264-27.532-.055-36.173.208-8.642-.403-24.573 5.178-25.029 3.72-.304 5.991 5.349 6.813 16.958.87 11.863 1.883 19.135 3.038 21.816 1.13 2.624 3.55 8.627 3.42 17.137-.087 5.674-3.9 13.397-11.441 23.17z"
        />
      </G>
      <Path
        fill="#F29575"
        fillRule="nonzero"
        d="M173.833 69.948l.714 6.554c.87 11.863 1.883 19.135 3.038 21.816-.976-4.79-1.866-14.654-2.67-29.593l-1.082 1.223z"
      />
      <Path
        fill="#F8B692"
        fillRule="nonzero"
        d="M60.128 49.305c2.917-1.952 5.547-2.928 7.89-2.928 3.512 0 4.305 3.283 1.904 6.052-2.4 2.77-5.57 6.737-8.642 9.256-2.048 1.68-2.432-2.447-1.152-12.38z"
      />
      <Path
        fill="#FA8B69"
        fillRule="nonzero"
        d="M60.087 49.039c1.655-.835 3.088-1.464 4.298-1.888l-.66 11.952c-.984 1.009-1.833 1.808-2.547 2.398-3.019 2.494-1.9-8.847-1.091-12.462z"
      />
      <Path
        fill="#F8B692"
        fillRule="nonzero"
        d="M5.701 121.704c0-4.204-1.73-9.006-2.788-14.816-1.11-6.099-1.27-12.668-.481-16.752 2.02-10.451 10.29-22.996 10.29-28.104 0-5.11-1.155-12.383 2.019-11.522 3.173.862 3.941 3.508 4.615 5.832.674 2.324 1.827 10.748 0 16.752-1.827 6.003-4.214 10.748-1.442 14.039 1.848 2.193 13.195 3.776 34.041 4.746l7.434 17.333c-.084-.193-1.824 1.937-5.222 6.391-3.398 4.455-8.197 10.41-14.398 17.865l-4.272 8.42c-.093.184-.23.343-.396.464-4.166 3.01-9.414 4.515-15.745 4.515-9.691 0-16.17-1.18-19.356-4.578 0-1.181 5.701-16.381 5.701-20.585z"
      />
      <G transform="translate(17.31 16)">
        <Rect
          width={46.113}
          height={94.421}
          x={0.144}
          fill="#2B2B2B"
          fillRule="nonzero"
          rx={5.97}
        />
        <Rect
          width={42.271}
          height={75.653}
          x={1.797}
          y={8.1}
          fill="#F5F5F5"
          fillRule="nonzero"
          rx={0.578}
        />
        <G filter="url(#prefix__g)" transform="translate(11.664 35.1)">
          <Rect width={23.4} height={23.4} fill="#FFF" rx={11.7} />
          <Path
            fill="url(#prefix__h)"
            fillRule="nonzero"
            d="M4.167 11.278c-1.414.834-2.125 1.248-2.132 1.24A7.45 7.45 0 0 1 7.12.01a.345.345 0 0 1 .361.344l.004 1.654a.383.383 0 0 1-.355.383 5.073 5.073 0 0 0-2.963 8.889z"
            transform="translate(4.205 4.205)"
          />
          <Path
            fill="#bdb3ff"
            d="M7.1 14.586l.86-2.132a.366.366 0 0 1 .477-.202l1.525.617a.366.366 0 0 1 .202.476l-1.118 2.767c-.045.112-.1.218-.161.317a1.645 1.645 0 0 1-2.671.262l-.003-.001-.12-.135a7.397 7.397 0 0 1-1.172-1.81 1.19 1.19 0 0 0 .909.63 1.189 1.189 0 0 0 1.272-.789z"
          />
          <Path
            fill="url(#prefix__h)"
            fillRule="nonzero"
            d="M4.167 11.278c-1.414.834-2.125 1.248-2.132 1.24A7.45 7.45 0 0 1 7.12.01a.345.345 0 0 1 .361.344l.004 1.654a.383.383 0 0 1-.355.383 5.073 5.073 0 0 0-2.963 8.889z"
            transform="rotate(180 9.598 9.598)"
          />
          <Path
            fill="#bdb3ff"
            d="M16.3 8.814l-.86 2.132a.366.366 0 0 1-.477.202l-1.525-.617a.366.366 0 0 1-.202-.476l1.118-2.767c.045-.112.1-.218.161-.317a1.645 1.645 0 0 1 2.671-.262l.003.001.12.135a7.397 7.397 0 0 1 1.172 1.81 1.19 1.19 0 0 0-.909-.63 1.189 1.189 0 0 0-1.272.789z"
          />
        </G>
        <Ellipse
          cx={23.057}
          cy={88.423}
          fill="#3A3A3A"
          fillRule="nonzero"
          rx={3.074}
          ry={3.096}
        />
        <G
          fill="#3A3A3A"
          fillRule="nonzero"
          transform="translate(18.253 3.096)"
        >
          <Ellipse cx={0.769} cy={0.774} rx={1} ry={1} />
          <Rect width={6.148} height={1} x={3.459} y={0.387} rx={0.385} />
        </G>
      </G>
      <Path
        fill="#000"
        fillOpacity={0.05}
        fillRule="nonzero"
        d="M14.056 63.187c0-5.106-1.148-12.377 2.008-11.516 3.156.861 4.303 3.603 4.973 5.926.67 2.323 2.486 11.518 0 19.637-.741 2.42-2.334 5.105-3.634 8.227v-9.765c-2.232-4.935-3.347-9.104-3.347-12.509z"
      />
      <Path
        fill="#F8B692"
        fillRule="nonzero"
        d="M2.967 106.713c-.576-6.263-1.27-12.629-.48-16.7C4.504 79.593 12.77 67.089 12.77 61.996S11.617 49.65 14.79 50.51c3.171.859 3.94 3.497 4.613 5.813.674 2.317 1.826 10.715 0 16.7-1.603 5.255-3.57 9.305-2.236 12.73.794 2.04-1.976 4.163-1.976 5.804-.142 2.197-.046 5.978 0 8.439.081 4.311-11.871 10.544-12.223 6.717zm67.05-19.626c.387.33.66 1.005.82 2.024.241 1.53-.588 3.615-2.804 6.221-2.217 2.606-6.316 5.439-7.345 4.605-1.029-.833-.26-3.13.122-5.215.364-.577 3.433-3.122 9.207-7.635z"
      />
      <Path
        fill="#FA8B69"
        fillRule="nonzero"
        d="M70.54 88.278c-1.21 2.128-3.321 3.796-4.476 4.74-1.155.944-4.516 4.16-5.54 2.034-1.024-2.127-.092-3.618 1.036-6.11.837-1.851 5.09-3.139 7.29-2.696.96.194 1.594 1.451 1.69 2.032z"
      />
      <Path
        fill="#F8B692"
        fillRule="nonzero"
        d="M66.943 74.24c1.05.651 2.341 1.69 3.873 3.115 2.299 2.138 2.035 4.367 1.341 6.505-.694 2.138-5.458 7.774-6.368 8.648-.91.875-3.812 3.79-5.123 2.819-1.31-.972-.527-3.596.288-6.22.543-1.749 1.404-3.66 2.585-5.733V76.57l3.404-2.332z"
      />
      <Path
        fill="#FA8B69"
        fillRule="nonzero"
        d="M63.424 83.333l1.922-3.483a21.83 21.83 0 0 1-1.922 2.225v1.258z"
      />
      <Path
        fill="#000"
        fillOpacity={0.05}
        fillRule="nonzero"
        d="M67.724 64.038c-1.766-1.293-1.59-1.358.525-.194 3.173 1.745 3.654 4.17 2.98 6.012-.673 1.843-3.157 4.006-5.369 5.849-2.212 1.842-6.939 5.207-7.708 3.073-.77-2.133.049-5.237 1.25-7.08.8-1.228 3.575-3.781 8.322-7.66z"
      />
      <Path
        fill="#F8B692"
        fillRule="nonzero"
        d="M63.45 61.856c2.291.337 4.487 1.086 6.587 2.245 3.15 1.738 2.493 4.346 2.005 5.795-.488 1.448-2.18 4.182-4.376 6.017s-6.89 5.186-7.653 3.061c-.764-2.124.477-5.698 1.241-7.05.51-.902 1.241-2.222 2.196-3.96v-6.108z"
      />
      <Path
        fill="#FA8B69"
        fillRule="nonzero"
        d="M63.424 68.048l2.498-3.483c-1.048 1.096-1.881 1.838-2.498 2.225v1.258zm5.078 7.352c-2.344 1.741-4.036 2.967-5.078 3.676 2.978-.967 5.078-1.838 5.957-2.999a6.532 6.532 0 0 0-.88-.677zM26.15 110.42c-.308 2.516-1.442 5.031-4.42 7.74 2.065-1.935 3.44-6.288 3.266-7.74h1.153zm-9.24-29.796c-1.172 4.025-.975 7.444.593 10.255v-4.6c-.296-.642-.395-.671-.592-1.342-.198-1.022-.198-2.46 0-4.313z"
      />
      <G stroke="#bdb3ff" strokeLinecap="round" strokeWidth={1.35}>
        <Path d="M122.845 47.813a9.032 9.032 0 0 0-9.032 9.032" />
        <Path
          d="M122.845 43.297c-7.482 0-13.548 6.066-13.548 13.548"
          opacity={0.5}
        />
        <Path
          d="M123.484 38.516c-10.476 0-18.968 8.492-18.968 18.968"
          opacity={0.1}
        />
      </G>
      <G stroke="#bdb3ff" strokeLinecap="round" strokeWidth={1.35}>
        <Path d="M69.187 19.845a9.032 9.032 0 0 0-9.032-9.032" />
        <Path
          d="M73.703 19.845c0-7.482-6.066-13.548-13.548-13.548"
          opacity={0.5}
        />
        <Path
          d="M78.484 20.484c0-10.476-8.492-18.968-18.968-18.968"
          opacity={0.1}
        />
      </G>
    </G>
  </Svg>
);

export default ConnectNanoXIllustration;


<|start_filename|>src/families/cosmos/DelegationFlow/index.js<|end_filename|>
// @flow
import React, { useMemo } from "react";
import { Platform } from "react-native";
import { useTranslation } from "react-i18next";
import { createStackNavigator } from "@react-navigation/stack";
import { useTheme } from "@react-navigation/native";
import {
  getStackNavigatorConfig,
  defaultNavigationOptions,
} from "../../../navigation/navigatorConfig";
import StepHeader from "../../../components/StepHeader";
import { ScreenName } from "../../../const";
import DelegationStarted from "./01-Started";
import DelegationSelectValidator from "./02-SelectValidator";
import DelegationAmount from "../shared/02-SelectAmount";
import SelectDevice from "../../../screens/SelectDevice";
import ConnectDevice from "../../../screens/ConnectDevice";
import DelegationValidationError from "./04-ValidationError";
import DelegationValidationSuccess from "./04-ValidationSuccess";

const totalSteps = "3";

function DelegationFlow() {
  const { t } = useTranslation();
  const { colors } = useTheme();
  const stackNavigationConfig = useMemo(
    () => getStackNavigatorConfig(colors, true),
    [colors],
  );
  return (
    <Stack.Navigator
      screenOptions={{
        ...stackNavigationConfig,
        gestureEnabled: Platform.OS === "ios",
      }}
    >
      <Stack.Screen
        name={ScreenName.CosmosDelegationStarted}
        component={DelegationStarted}
        options={{
          title: t("cosmos.delegation.stepperHeader.starter"),
        }}
      />
      <Stack.Screen
        name={ScreenName.CosmosDelegationValidator}
        component={DelegationSelectValidator}
        options={{
          headerTitle: () => (
            <StepHeader
              title={t("cosmos.delegation.stepperHeader.validator")}
              subtitle={t("cosmos.delegation.stepperHeader.stepRange", {
                currentStep: "1",
                totalSteps,
              })}
            />
          ),
          headerLeft: () => null,
          headerStyle: {
            ...defaultNavigationOptions.headerStyle,
            elevation: 0,
            shadowOpacity: 0,
            borderBottomWidth: 0,
          },
          gestureEnabled: false,
        }}
      />
      <Stack.Screen
        name={ScreenName.CosmosDelegationAmount}
        component={DelegationAmount}
        options={({ route }) => ({
          headerRight: null,
          headerTitle: () => (
            <StepHeader
              title={
                route.params.validator?.name ??
                route.params.validator.validatorAddress
              }
              subtitle={t("cosmos.delegation.stepperHeader.amountSubTitle")}
            />
          ),
        })}
      />
      <Stack.Screen
        name={ScreenName.CosmosDelegationSelectDevice}
        component={SelectDevice}
        options={{
          headerTitle: () => (
            <StepHeader
              title={t("cosmos.delegation.stepperHeader.selectDevice")}
              subtitle={t("cosmos.delegation.stepperHeader.stepRange", {
                currentStep: "2",
                totalSteps,
              })}
            />
          ),
        }}
      />
      <Stack.Screen
        name={ScreenName.CosmosDelegationConnectDevice}
        component={ConnectDevice}
        options={{
          headerLeft: null,
          gestureEnabled: false,
          headerTitle: () => (
            <StepHeader
              title={t("cosmos.delegation.stepperHeader.connectDevice")}
              subtitle={t("cosmos.delegation.stepperHeader.stepRange", {
                currentStep: "3",
                totalSteps,
              })}
            />
          ),
        }}
      />
      <Stack.Screen
        name={ScreenName.CosmosDelegationValidationError}
        component={DelegationValidationError}
        options={{
          headerShown: false,
          gestureEnabled: false,
        }}
      />
      <Stack.Screen
        name={ScreenName.CosmosDelegationValidationSuccess}
        component={DelegationValidationSuccess}
        options={{
          headerLeft: null,
          headerRight: null,
          headerTitle: "",
          gestureEnabled: false,
        }}
      />
    </Stack.Navigator>
  );
}

const options = {
  headerShown: false,
};

export { DelegationFlow as component, options };

const Stack = createStackNavigator();


<|start_filename|>src/icons/Unlock.js<|end_filename|>
// @flow

import React from "react";
import Svg, { Path } from "react-native-svg";

const Unlock = ({ size, color }: { size: number, color: string }) => (
  <Svg width={size} height={size} viewBox="0 0 16 18" fill="none">
    <Path
      d="M12.4688 8.21875H5.5625V5.83203C5.5625 4.48633 6.62891 3.36914 7.97461 3.34375C9.32031 3.34375 10.4375 4.43555 10.4375 5.78125V6.39062C10.4375 6.74609 10.6914 7 11.0469 7C11.377 7 11.6562 6.74609 11.6562 6.39062V5.83203C11.6562 3.80078 10.0312 2.15039 8 2.125C5.96875 2.125 4.34375 3.77539 4.34375 5.78125V8.21875H3.53125C2.8457 8.21875 2.3125 8.77734 2.3125 9.4375V13.9062C2.3125 14.5918 2.8457 15.125 3.53125 15.125H12.4688C13.1289 15.125 13.6875 14.5918 13.6875 13.9062V9.4375C13.6875 8.77734 13.1289 8.21875 12.4688 8.21875ZM12.4688 13.9062H3.53125V9.4375H12.4688V13.9062Z"
      fill={color}
    />
  </Svg>
);

export default Unlock;


<|start_filename|>src/families/solana/SendRowsFee.js<|end_filename|>
// @flow
import React, { useCallback } from "react";
import { View, StyleSheet, Linking } from "react-native";
import type { AccountLike } from "@ledgerhq/live-common/lib/types";
import { Trans } from "react-i18next";
import type { Transaction } from "@ledgerhq/live-common/lib/families/solana/types";
import {
  getAccountUnit,
  getAccountCurrency,
} from "@ledgerhq/live-common/lib/account";
import { useTheme } from "@react-navigation/native";
import SummaryRow from "../../screens/SendFunds/SummaryRow";
import LText from "../../components/LText";
import CurrencyUnitValue from "../../components/CurrencyUnitValue";
import CounterValue from "../../components/CounterValue";
import ExternalLink from "../../icons/ExternalLink";
import { urls } from "../../config/urls";

type Props = {
  account: AccountLike,
  transaction: Transaction,
};

export default function SolanaFeeRow({ account, transaction }: Props) {
  const { colors } = useTheme();
  const extraInfoFees = useCallback(() => {
    Linking.openURL(urls.solana.supportPage);
  }, []);

  const fees = transaction.feeCalculator?.lamportsPerSignature;
  const unit = getAccountUnit(account);
  const currency = getAccountCurrency(account);

  return (
    <SummaryRow
      onPress={extraInfoFees}
      title={<Trans i18nKey="send.fees.title" />}
      additionalInfo={
        <View>
          <ExternalLink size={12} color={colors.grey} />
        </View>
      }
    >
      <View style={{ alignItems: "flex-end" }}>
        <View style={styles.accountContainer}>
          {fees ? (
            <LText style={styles.valueText}>
              <CurrencyUnitValue unit={unit} value={fees} />
            </LText>
          ) : null}
        </View>
        <LText style={styles.countervalue} color="grey">
          {fees ? (
            <CounterValue before="≈ " value={fees} currency={currency} />
          ) : null}
        </LText>
      </View>
    </SummaryRow>
  );
}

const styles = StyleSheet.create({
  accountContainer: {
    flex: 1,
    flexDirection: "row",
  },
  countervalue: {
    fontSize: 12,
  },
  valueText: {
    fontSize: 16,
  },
});


<|start_filename|>src/polyfill.js<|end_filename|>
/* eslint-disable no-console */

import "@formatjs/intl-getcanonicallocales/polyfill";
import "@formatjs/intl-locale/polyfill";
import "@formatjs/intl-pluralrules/polyfill";
import "@formatjs/intl-pluralrules/locale-data/en";
import "@formatjs/intl-pluralrules/locale-data/fr";
import "@formatjs/intl-pluralrules/locale-data/es";
import "@formatjs/intl-pluralrules/locale-data/ru";
import "@formatjs/intl-pluralrules/locale-data/zh";
import "@formatjs/intl-pluralrules/locale-data/de";
import "@formatjs/intl-pluralrules/locale-data/tr";
import "@formatjs/intl-pluralrules/locale-data/ja";
import "@formatjs/intl-pluralrules/locale-data/ko";
import "@formatjs/intl-numberformat/polyfill";
import "@formatjs/intl-numberformat/locale-data/en";
import "@formatjs/intl-numberformat/locale-data/fr";
import "@formatjs/intl-numberformat/locale-data/es";
import "@formatjs/intl-numberformat/locale-data/ru";
import "@formatjs/intl-numberformat/locale-data/zh";
import "@formatjs/intl-numberformat/locale-data/de";
import "@formatjs/intl-numberformat/locale-data/tr";
import "@formatjs/intl-numberformat/locale-data/ja";
import "@formatjs/intl-numberformat/locale-data/ko";
import "@formatjs/intl-datetimeformat/polyfill";
import "@formatjs/intl-datetimeformat/locale-data/en";
import "@formatjs/intl-datetimeformat/locale-data/fr";
import "@formatjs/intl-datetimeformat/locale-data/es";
import "@formatjs/intl-datetimeformat/locale-data/ru";
import "@formatjs/intl-datetimeformat/locale-data/zh";
import "@formatjs/intl-datetimeformat/locale-data/de";
import "@formatjs/intl-datetimeformat/locale-data/tr";
import "@formatjs/intl-datetimeformat/locale-data/ja";
import "@formatjs/intl-datetimeformat/locale-data/ko";
import "@formatjs/intl-datetimeformat/add-all-tz";

// Fix error when adding Solana account
import "@azure/core-asynciterator-polyfill";

global.Buffer = require("buffer").Buffer;

if (!console.assert) {
  console.assert = () => {};
}

process.browser = true; // for readable-stream/lib/_stream_writable.js
// FIXME shim want to set it to false tho...

if (__DEV__ && process.env.NODE_ENV !== "test") {
  setTimeout(() => {
    // it logs weird things without the timeout...
    try {
      // just for tests
      require("react-native").LogBox.ignoreLogs([
        "Warning: isMounted(...) is deprecated in plain JavaScript React classes. Instead, make sure to clean up subscriptions and pending requests in componentWillUnmount to prevent memory leaks.",
        "Setting a timer for a long period of time, i.e. multiple minutes, is a performance and correctness issue on Android as it keeps the timer module awake, and timers can only be called when the app is in the foreground. See https://github.com/facebook/react-native/issues/12981 for more info.",
        "Warning: componentWillReceiveProps has been renamed",
        "Warning: componentWillUpdate has been renamed",
        "Warning: componentWillMount has been renamed",
      ]);
    } catch (e) {
      console.warn(e);
    }
  }, 100);
}


<|start_filename|>src/families/solana/index.js<|end_filename|>
// @flow
import * as SolanaEditMemo from "./ScreenEditMemo";

export { SolanaEditMemo };


<|start_filename|>src/screens/Lending/modals/InfoModals/TermsStep.js<|end_filename|>
// $flow
import React, { useCallback, useState } from "react";
import {
  View,
  StyleSheet,
  Image,
  Linking,
  TouchableOpacity,
} from "react-native";
import { Trans } from "react-i18next";
import { useNavigation } from "@react-navigation/native";
import { track } from "../../../../analytics";
import BaseInfoModal from "../BaseModal";
import CheckBox from "../../../../components/CheckBox";
import LText from "../../../../components/LText";
import termsImg from "../../../../images/lending-terms.png";
import { ScreenName } from "../../../../const";
import { acceptLendingTerms } from "../../../../logic/terms";
import { urls } from "../../../../config/urls";
import PreventNativeBack from "../../../../components/PreventNativeBack";

type Props = {
  route: { params: { endCallback: () => void } },
};

export default function TermsStep({ route: { params } }: Props) {
  const navigation = useNavigation();
  const [hasAcceptedTerms, setHasAcceptedTerms] = useState(false);

  const onTogleAcceptedTerms = useCallback(
    () => setHasAcceptedTerms(!hasAcceptedTerms),
    [hasAcceptedTerms, setHasAcceptedTerms],
  );

  const onTermsClick = useCallback(() => {
    track("Page Lend TC accepted");
    Linking.openURL(urls.compoundTnC);
  }, []);

  const onNext = useCallback(() => {
    if (hasAcceptedTerms)
      acceptLendingTerms().then(() =>
        navigation.push(ScreenName.LendingInfo1, params),
      );
  }, [hasAcceptedTerms, navigation, params]);

  return (
    <BaseInfoModal
      title={<Trans i18nKey="transfer.lending.terms.title" />}
      description={<Trans i18nKey="transfer.lending.terms.description" />}
      badgeLabel={<Trans i18nKey="transfer.lending.terms.label" />}
      illustration={
        <View style={styles.imageContainer}>
          <Image style={styles.image} resizeMode="contain" source={termsImg} />
        </View>
      }
      disabled={!hasAcceptedTerms}
      onNext={onNext}
    >
      <PreventNativeBack />
      <TouchableOpacity onPress={onTogleAcceptedTerms}>
        <View style={styles.footer}>
          <CheckBox
            onChange={onTogleAcceptedTerms}
            isChecked={hasAcceptedTerms}
          />
          <LText style={styles.switchLabel}>
            <Trans i18nKey="transfer.lending.terms.switchLabel">
              <LText
                onPress={onTermsClick}
                semiBold
                style={styles.conditionsText}
                color="live"
              />
            </Trans>
          </LText>
        </View>
      </TouchableOpacity>
    </BaseInfoModal>
  );
}

const styles = StyleSheet.create({
  switchRow: {
    marginHorizontal: 16,
    flexDirection: "row",
    alignItems: "center",
  },
  switchLabel: {
    marginLeft: 8,
    fontSize: 13,
    paddingRight: 16,
  },
  conditionsText: {
    fontSize: 13,
    textDecorationLine: "underline",
  },
  footer: {
    flexDirection: "row",
    alignContent: "center",
    alignItems: "center",
    justifyContent: "space-between",
    marginTop: 16,
  },
  imageContainer: { width: "100%", height: "100%", paddingHorizontal: 24 },
  image: { width: "100%", height: "100%" },
});


<|start_filename|>scripts/v3check.js<|end_filename|>
const exec = require("child_process").exec;

/* eslint-disable no-console */

const LOG_COMMANDS = false;
const TEST_FILES = false;
const TS_TEST_FILES = [
  // "src/screens/Settings/General/index.tsx",
  "src/components/CurrencyRate.tsx",
];

async function executeAsync(command) {
  return new Promise((resolve, reject) => {
    exec(command, (error, stdout, stderr) => {
      if (error) {
        reject(error);
      } else if (stderr) {
        reject(stderr);
      } else {
        resolve(stdout);
      }
    });
  });
}

async function listTsJsFilesPairs() {
  const jsFilesPath = {};
  const jsTsPairs = [];
  await executeAsync('git ls-files | grep -e ".\\.js$"').then(res =>
    res.split("\n").map(path => (jsFilesPath[path] = true)),
  );
  return executeAsync('git ls-files | grep -e ".\\.ts$" -e ".\\.tsx$"')
    .then(res =>
      res.split("\n").forEach(path => {
        const beforeExtensionPath = path
          .split(".")
          .slice(0, -1)
          .join(".");
        const jsFilePath = `${beforeExtensionPath}.js`;
        if (jsFilesPath[jsFilePath]) {
          jsTsPairs.push({
            tsFilePath: path,
            jsFilePath,
          });
        }
      }),
    )
    .then(() => jsTsPairs);
}

const branchRebrand = "LL-7742";
const branchRelease = "release/v3.0.x";
const mainBranchName = "develop";

async function getFileCreationDate(filePath, firstParent) {
  const command = `git log --format=%aD --first-parent ${firstParent} --no-merges ${filePath} | tail -1`;
  LOG_COMMANDS && console.log(`getFileCreationDate command:\n\t${command}`);
  const rawDate = await executeAsync(command);
  return { date: rawDate ? new Date(rawDate) : null, command };
}

async function listCommits(filePath, sinceDateIsoString) {
  const command = `git log \
    --first-parent \
    ${mainBranchName} \
    ${sinceDateIsoString ? `--since=${sinceDateIsoString}` : ""} \
    --pretty=format:"%aI %H %s" \
    ${filePath}`;
  LOG_COMMANDS && console.log(`listCommits command:\n\t${command}`);
  const rawResult = await executeAsync(command);
  const commitsRaw = rawResult.split("\n").filter(l => !!l);
  return commitsRaw.map(commitRaw => {
    const [dateStr, hashStr, ...rest] = commitRaw.split(" ");
    const message = rest.join(" ");
    return {
      date: new Date(dateStr),
      hashStr,
      message,
      command,
    };
  });
}

function pbcopy(data) {
  const proc = require("child_process").spawn("pbcopy");
  proc.stdin.write(data);
  proc.stdin.end();
}

async function generateMarkdownTodoList() {
  const pairs = await listTsJsFilesPairs();
  let todoList = "";
  const res = await Promise.all(
    pairs
      .filter(
        ({ tsFilePath }) => !TEST_FILES || TS_TEST_FILES.includes(tsFilePath),
      )
      .map(async ({ tsFilePath, jsFilePath }) => {
        const { date: tsFileCreationDate } = await getFileCreationDate(
          tsFilePath,
          branchRebrand,
        ).then(res =>
          res === null ? getFileCreationDate(tsFilePath, branchRelease) : res,
        );
        const commits = tsFileCreationDate
          ? await listCommits(jsFilePath, tsFileCreationDate.toISOString())
          : [];
        return { tsFilePath, jsFilePath, commits };
      }),
  );
  res.forEach(({ jsFilePath, commits }) => {
    if (commits.length === 0) return;
    console.log("jsFilePath", jsFilePath);
    todoList += `- [ ] **${jsFilePath}**\n`;
    commits.forEach(({ message, date, hashStr }) => {
      todoList += `  - [ ] ${date.toDateString()} ${hashStr} ${message}\n`;
    });
    todoList += "\n";
  });
  return todoList;
}

generateMarkdownTodoList().then(pbcopy);


<|start_filename|>src/screens/WalletConnect/Scan.js<|end_filename|>
/* @flow */
import React, { useEffect, useContext } from "react";
import Config from "react-native-config";
import Clipboard from "@react-native-community/clipboard";
import { Trans } from "react-i18next";
import Scanner from "../../components/Scanner";
import { ScreenName } from "../../const";
import { TrackScreen } from "../../analytics";
// $FlowFixMe
import { connect, context, STATUS } from "./Provider";

type Props = {
  navigation: any,
  route: { params: RouteParams },
};

type RouteParams = {
  accountId: string,
};

const ScanWalletConnect = ({ navigation, route }: Props) => {
  const wcContext = useContext(context);

  useEffect(() => {
    let mockTO;
    if (Config.MOCK_SCAN_WALLETCONNECT) {
      mockTO = setTimeout(async () => {
        onResult(await Clipboard.getString());
      }, 2000);
    }
    return () => clearTimeout(mockTO);
  });

  const onResult = (uri: string) => {
    if (wcContext.status !== STATUS.DISCONNECTED) {
      return;
    }
    connect(uri);
    navigation.replace(ScreenName.WalletConnectConnect, {
      uri,
      accountId: route.params.accountId,
    });
  };

  return (
    <>
      <TrackScreen category="WalletConnect" screen="Scan" />
      <Scanner
        navigation={navigation}
        onResult={onResult}
        instruction={<Trans i18nKey="walletconnect.scan" />}
      />
    </>
  );
};

export default ScanWalletConnect;


<|start_filename|>src/screens/DebugLottie.js<|end_filename|>
// @flow

import React, { useMemo, useState } from "react";
import { StyleSheet, ScrollView, View } from "react-native";
import SafeAreaView from "react-native-safe-area-view";
import { useTheme } from "@react-navigation/native";
import Config from "react-native-config";
import Button from "../components/Button";
import LText from "../components/LText";
import Animation from "../components/Animation";
import getDeviceAnimation from "../components/DeviceAction/getDeviceAnimation";
import BottomModal from "../components/BottomModal";
import Touchable from "../components/Touchable";
import Alert from "../components/Alert";
import Check from "../icons/Check";
import { lottieAnimations } from "./Onboarding/shared/infoPagesData";

const forceInset = { bottom: "always" };

const DebugLottie = () => {
  const { colors } = useTheme();

  const keys = useMemo(
    () => [
      "plugAndPinCode",
      "enterPinCode",
      "quitApp",
      "allowManager",
      "openApp",
      "validate",
    ],
    [],
  );
  const onBoardingKeys = useMemo(
    () => [
      "pinCode",
      "recover",
      "confirmWords",
      "numberOfWords",
      "powerOn",
      "powerOnRecovery",
    ],
    [],
  );

  const [modelId, setModelId] = useState(Config.OVERRIDE_MODEL_ID || "nanoS");
  const [wired, setWired] = useState(false);
  const [key, setKey] = useState<any>("plugAndPinCode");
  const [keyModalVisible, setKeyModalVisible] = useState(false);
  const animation = useMemo(() => {
    if (keys.includes(key)) {
      // Normal deviceAction animations
      return getDeviceAnimation({
        device: { modelId, wired: wired && modelId === "nanoX" },
        key,
        theme: "light",
      });
    }
    if (onBoardingKeys.includes(key)) {
      return lottieAnimations[modelId][key].light;
    }
    return null;
    // Onboarding animations
  }, [key, keys, modelId, onBoardingKeys, wired]);

  const animation2 = useMemo(() => {
    if (keys.includes(key)) {
      // Normal deviceAction animations
      return getDeviceAnimation({
        device: { modelId, wired: wired && modelId === "nanoX" },
        key,
        theme: "dark",
      });
    }
    if (onBoardingKeys.includes(key)) {
      return lottieAnimations[modelId][key].dark;
    }
    return null;
    // Onboarding animations
  }, [key, keys, modelId, onBoardingKeys, wired]);

  const allKeys = [...keys, ...onBoardingKeys];
  return (
    <SafeAreaView
      forceInset={forceInset}
      style={[styles.root, { backgroundColor: colors.background }]}
    >
      <Alert type="warning">
        {
          "This is a tool provided as-is for the team to validate lottie animations used in the app."
        }
      </Alert>
      <LText secondary semiBold style={styles.title}>
        {!key ? "Select Animation" : `Showing '${key}'`}
      </LText>
      <View style={{ borderWidth: 1 }}>
        {animation && <Animation source={animation} />}
      </View>
      <View style={{ backgroundColor: "#121212" }}>
        {animation2 && <Animation source={animation2} />}
      </View>
      <View style={styles.select}>
        <Button
          type={modelId === "nanoS" ? "primary" : "secondary"}
          title="nanoS"
          disabled={Config.OVERRIDE_MODEL_ID || key === "pairDevice"}
          onPress={() => {
            setModelId("nanoS");
          }}
        />
        <Button
          type={modelId === "nanoSP" ? "primary" : "secondary"}
          title="nanoSP"
          disabled={Config.OVERRIDE_MODEL_ID || key === "pairDevice"}
          onPress={() => {
            setModelId("nanoSP");
          }}
        />
        <Button
          type={modelId === "nanoX" ? "primary" : "secondary"}
          title="nanoX"
          disabled={Config.OVERRIDE_MODEL_ID}
          onPress={() => {
            setModelId("nanoX");
          }}
        />
        <Button
          type="primary"
          title="blue"
          disabled
          onPress={() => {
            setModelId("blue");
          }}
        />
      </View>
      <Button
        containerStyle={{ marginTop: 8 }}
        type="primary"
        title={`Show ${wired ? "Bluetooth" : "Wired"}`}
        disabled={modelId !== "nanoX" || !keys.includes(key)}
        onPress={() => setWired(wired => !wired)}
      />
      <Button
        containerStyle={{ marginTop: 8 }}
        type="primary"
        title="Animation key"
        onPress={() => setKeyModalVisible(true)}
      />
      <BottomModal isOpened={keyModalVisible} onClose={setKeyModalVisible}>
        <ScrollView style={styles.modal}>
          {allKeys.map((_key, i) => (
            <Touchable
              key={_key + i}
              onPress={() => {
                if (_key === "pairDevice") {
                  setModelId("nanoX");
                  setWired(false);
                }
                setKey(_key);
                setKeyModalVisible(false);
              }}
              style={[styles.button]}
            >
              <LText
                {...(key === _key ? { semiBold: true } : {})}
                style={[styles.buttonLabel]}
              >
                {_key}
              </LText>
              {key === _key && <Check size={16} color={colors.live} />}
            </Touchable>
          ))}
        </ScrollView>
      </BottomModal>
    </SafeAreaView>
  );
};

const styles = StyleSheet.create({
  root: {
    flex: 1,
    padding: 8,
  },
  anim: {
    flex: 1,
  },
  title: {
    margin: 16,
    textAlign: "center",
  },
  select: {
    marginTop: 20,
    flexDirection: "row",
    justifyContent: "space-between",
  },
  button: {
    height: 50,
    margin: 8,
    borderRadius: 4,
    alignItems: "flex-start",
    justifyContent: "space-between",
    padding: 8,
    flexDirection: "row",
  },
  buttonLabel: {
    fontSize: 16,
    textTransform: "capitalize",
  },
  modal: { padding: 8 },
});

export default DebugLottie;


<|start_filename|>src/screens/Lending/ClosedLoans/ClosedLoansRow.js<|end_filename|>
// @flow

import React, { useCallback, useState, useMemo } from "react";
import { TouchableOpacity, View, StyleSheet } from "react-native";
import type { ClosedLoanHistory } from "@ledgerhq/live-common/lib/compound/types";
import {
  getAccountName,
  getAccountCurrency,
} from "@ledgerhq/live-common/lib/account/helpers";
import { Trans } from "react-i18next";
import { useTheme } from "@react-navigation/native";
import { useSelector } from "react-redux";
import LText from "../../../components/LText";
import CurrencyUnitValue from "../../../components/CurrencyUnitValue";
import CurrencyIcon from "../../../components/CurrencyIcon";
import CounterValue from "../../../components/CounterValue";
import DelegationDrawer from "../../../components/DelegationDrawer";
import Circle from "../../../components/Circle";
import Compound, { compoundColor } from "../../../icons/Compound";
import { rgba } from "../../../colors";
import { localeSelector } from "../../../reducers/settings";
import FormatDate from "../../../components/FormatDate";

type RowProps = {
  item: ClosedLoanHistory,
};

export default function ClosedLoansRow({ item }: RowProps) {
  const {
    account,
    parentAccount,
    endDate,
    interestsEarned,
    amountSupplied,
  } = item;
  const { token } = account;
  const { colors } = useTheme();
  const name = getAccountName(parentAccount || account);
  const accountName = getAccountName(account);
  const currency = getAccountCurrency(account);
  const totalRedeemed = amountSupplied.plus(interestsEarned);

  const [isOpened, setIsOpened] = useState(false);

  const onOpenDrawer = useCallback(() => setIsOpened(true), []);
  const onCloseDrawer = useCallback(() => setIsOpened(false), []);

  const locale = useSelector(localeSelector);

  const data = useMemo(
    () => [
      {
        label: (
          <Trans i18nKey="transfer.lending.dashboard.activeAccount.account" />
        ),
        Component: <LText semiBold>{name}</LText>,
      },
      {
        label: (
          <Trans i18nKey="transfer.lending.dashboard.activeAccount.amountRedeemed" />
        ),
        Component: (
          <LText semiBold>
            <CurrencyUnitValue
              unit={token.units[0]}
              value={totalRedeemed}
              showCode
            />
          </LText>
        ),
      },
      {
        label: (
          <Trans i18nKey="transfer.lending.dashboard.activeAccount.interestEarned" />
        ),
        Component: (
          <LText semiBold>
            <CurrencyUnitValue
              unit={token.units[0]}
              value={interestsEarned}
              showCode
            />
          </LText>
        ),
      },
      {
        label: (
          <Trans i18nKey="transfer.lending.dashboard.activeAccount.endDate" />
        ),
        Component: (
          <LText semiBold>
            <FormatDate date={endDate} />
          </LText>
        ),
      },
    ],
    [name, token.units, totalRedeemed, interestsEarned, endDate, locale],
  );

  return (
    <>
      <TouchableOpacity
        style={[styles.row, { backgroundColor: colors.card }]}
        onPress={onOpenDrawer}
      >
        <CurrencyIcon radius={100} currency={token} size={32} />
        <View style={styles.currencySection}>
          <LText
            numberOfLines={1}
            semiBold
            style={styles.subTitle}
            color="grey"
          >
            {name}
          </LText>
          <LText numberOfLines={1} semiBold style={styles.title}>
            {accountName}
          </LText>
        </View>
        <View style={[styles.currencySection, styles.alignEnd]}>
          <LText semiBold>
            <CurrencyUnitValue
              unit={token.units[0]}
              value={amountSupplied}
              showCode
            />
          </LText>
          <LText style={styles.subTitle} color="grey">
            <CounterValue
              currency={token}
              value={amountSupplied}
              disableRounding
              fontSize={3}
              showCode
              alwaysShowSign={false}
            />
          </LText>
        </View>
      </TouchableOpacity>
      <DelegationDrawer
        isOpen={isOpened}
        onClose={onCloseDrawer}
        account={account}
        icon={<CurrencyIcon size={62} radius={62} currency={currency} />}
        ValidatorImage={() => (
          <Circle size={62} bg={rgba(compoundColor, 0.2)}>
            <Compound size={62 * 0.55} />
          </Circle>
        )}
        amount={amountSupplied}
        data={data}
        actions={[]}
        undelegation
      />
    </>
  );
}

const styles = StyleSheet.create({
  row: {
    display: "flex",
    flexDirection: "row",
    alignItems: "center",
    justifyContent: "flex-start",
    padding: 8,
    height: 70,
  },
  currencySection: { paddingHorizontal: 8, flex: 1 },
  alignEnd: {
    alignItems: "flex-end",
  },
  title: {
    lineHeight: 17,
    fontSize: 14,
  },
  subTitle: {
    lineHeight: 15,
    fontSize: 12,
  },
  currencyIconContainer: {
    flexDirection: "row",
    justifyContent: "center",
    alignItems: "center",
  },
});


<|start_filename|>src/components/EditFeeUnit.js<|end_filename|>
/* @flow */
import React, { useState } from "react";
import { FlatList, View, StyleSheet, Keyboard } from "react-native";
import { useNavigation, useRoute } from "@react-navigation/native";
import Icon from "react-native-vector-icons/dist/FontAwesome";
import type { Account } from "@ledgerhq/live-common/lib/types";
import { getAccountBridge } from "@ledgerhq/live-common/lib/bridge";
import { useTranslation } from "react-i18next";
import { BigNumber } from "bignumber.js";
import { ScreenName } from "../const";
import { useFieldByFamily, useEditTxFeeByFamily } from "../families/helpers";
import SettingsRow from "./SettingsRow";
import LText from "./LText";
import CurrencyInput from "./CurrencyInput";
import Touchable from "./Touchable";
import BottomModal from "./BottomModal";
import Button from "./Button";

type Props = {
  account: Account,
  field: string,
};

export default function EditFreeUnit({ account, field }: Props) {
  const { navigate } = useNavigation();
  const route = useRoute();
  const { t } = useTranslation();
  const fieldByFamily = useFieldByFamily(field);

  const [fee, setFee] = useState<BigNumber | null | typeof undefined>(
    fieldByFamily,
  );
  const [isModalOpened, setIsModalOpened] = useState(false);
  const [isValid, setIsValid] = useState(true);
  const [transaction, setTransaction] = useState(route.params?.transaction);

  const feeCustomUnit = transaction.feeCustomUnit || account.unit;
  const editTxFeeByFamily = useEditTxFeeByFamily();

  function onRequestClose() {
    setIsModalOpened(false);
  }

  function onPress() {
    setIsModalOpened(true);
  }

  function keyExtractor(item: any): string {
    return item.code;
  }

  function onChange(fee: ?BigNumber) {
    setFee(fee);
    setIsValid(!(fee && fee.isZero()));
  }

  function updateTransaction(feeCustomUnit: any) {
    const bridge = getAccountBridge(account);
    setTransaction(bridge.updateTransaction(transaction, { feeCustomUnit }));
    onRequestClose();
  }

  function onValidateFees() {
    Keyboard.dismiss();

    navigate(ScreenName.SendSummary, {
      accountId: account.id,
      transaction: editTxFeeByFamily({ account, field, fee }),
    });
  }

  return (
    <>
      <View style={styles.inputContainer}>
        <View style={styles.inputRow}>
          <CurrencyInput
            style={{ flex: 1 }}
            autoFocus
            unit={feeCustomUnit}
            value={fee}
            onChange={onChange}
          />
          <Touchable
            event="EditFeeUnitOpen"
            onPress={onPress}
            style={styles.unitContainer}
          >
            <View style={styles.unitSelectRow}>
              <LText secondary semiBold style={styles.unitStyle}>
                {feeCustomUnit.code}
              </LText>
              <View style={styles.arrowDown}>
                <Icon name="angle-down" size={12} />
              </View>
            </View>
          </Touchable>
        </View>
        <View style={styles.buttonContainer}>
          <Button
            event="EditFeeUnitConfirm"
            type="primary"
            title={t("common.confirm")}
            containerStyle={styles.continueButton}
            onPress={onValidateFees}
            disabled={!isValid}
          />
        </View>
      </View>
      <BottomModal
        id="EditFeeUnitModal"
        isOpened={isModalOpened}
        onClose={onRequestClose}
      >
        <View style={styles.editFeesUnitsModalTitleRow}>
          <LText secondary semiBold style={styles.editFeesUnitModalTitle}>
            {t("send.fees.edit.title")}
          </LText>
        </View>
        <FlatList
          data={account.currency.units}
          keyExtractor={keyExtractor}
          extraData={feeCustomUnit}
          renderItem={({ item }) => (
            <Touchable
              event="EditFeeUnit"
              eventProperties={{ unit: item.code }}
              onPress={() => {
                updateTransaction(item);
              }}
            >
              <SettingsRow
                title={item.code}
                selected={feeCustomUnit === item}
              />
            </Touchable>
          )}
        >
          {account.unit.code}
        </FlatList>
      </BottomModal>
    </>
  );
}

const styles = StyleSheet.create({
  buttonContainer: {
    flex: 1,
    padding: 16,
    justifyContent: "flex-end",
  },
  continueButton: {
    alignSelf: "stretch",
  },
  inputContainer: {
    flex: 1,
    padding: 16,
  },
  inputRow: {
    flexDirection: "row",
  },
  unitContainer: {
    justifyContent: "center",
  },
  unitStyle: {
    marginRight: 8,
  },
  unitSelectRow: {
    flexDirection: "row",
  },
  arrowDown: {
    justifyContent: "center",
  },
  editFeesUnitModalTitle: {
    fontSize: 16,
  },
  editFeesUnitsModalTitleRow: {
    flexDirection: "row",
    marginTop: 16,
    justifyContent: "center",
  },
});


<|start_filename|>src/components/FormatDay.js<|end_filename|>
// @flow
import { Component } from "react";
import { connect } from "react-redux";
import i18next from "i18next";
import { differenceInCalendarDays } from "date-fns";
import compareDate from "../logic/compareDate";
import { localeSelector } from "../reducers/settings";

type Props = {
  day: Date,
  locale: string,
};

class FormatDay extends Component<Props> {
  shouldComponentUpdate({ day: nextDay, locale: nextLocale }: Props) {
    const { day, locale } = this.props;
    const isSameDay = compareDate(day, nextDay);
    const isSameLocale = locale === nextLocale;
    return !isSameDay || !isSameLocale;
  }

  render(): React$Node {
    const { day, locale } = this.props;
    const dayDiff = differenceInCalendarDays(Date.now(), day);
    const suffix =
      dayDiff === 0
        ? ` - ${i18next.t("common.today")}`
        : dayDiff === 1
        ? ` - ${i18next.t("common.yesterday")}`
        : "";

    const formattedDate = new Intl.DateTimeFormat(locale).format(day);
    return `${formattedDate}${suffix}`;
  }
}

const mapStateToProps = state => ({
  locale: localeSelector(state),
});

// $FlowFixMe
export default connect(mapStateToProps, null, null, { forwardRef: true })(
  FormatDay,
);


<|start_filename|>src/components/RootNavigator/PasswordAddFlowNavigator.js<|end_filename|>
// @flow
import React, { useMemo } from "react";
import { createStackNavigator } from "@react-navigation/stack";
import { useTranslation } from "react-i18next";
import { useTheme } from "styled-components/native";
import { ScreenName } from "../../const";
import PasswordAdd from "../../screens/Settings/General/PasswordAdd";
import ConfirmPassword from "../../screens/Settings/General/ConfirmPassword";
import { getStackNavigatorConfig } from "../../navigation/navigatorConfig";

export default function PasswordAddFlowNavigator() {
  const { t } = useTranslation();
  const { colors } = useTheme();
  const stackNavigationConfig = useMemo(
    () => getStackNavigatorConfig(colors, true),
    [colors],
  );
  return (
    <Stack.Navigator screenOptions={stackNavigationConfig}>
      <Stack.Screen
        name={ScreenName.PasswordAdd}
        component={PasswordAdd}
        options={{ title: t("auth.addPassword.title") }}
      />
      <Stack.Screen
        name={ScreenName.ConfirmPassword}
        component={ConfirmPassword}
        options={{
          title: t("auth.confirmPassword.title"),
        }}
      />
    </Stack.Navigator>
  );
}

const Stack = createStackNavigator();


<|start_filename|>src/screens/Onboarding/shared/infoPagesData.js<|end_filename|>
// @flow
import React from "react";
import { Platform } from "react-native";
import { Trans } from "react-i18next";
import type { OnboardingScene } from "../../../components/OnboardingStepperView";

import setupDeviceStartImage from "../assets/getStarted.png";
import pinCodeImage from "../assets/pinCodeImage.png";

import recoveryPhrase from "../assets/recoveryPhrase.png";
import recoverySheet from "../assets/recoverySheet.png";
import hideRecoveryPhrase from "../assets/hideRecoveryPhrase.png";

import pinCodeNanoXLight from "../assets/nanoX/pinCode/light.json";
import pinCodeNanoXDark from "../assets/nanoX/pinCode/dark.json";
import pinCodeNanoSLight from "../assets/nanoS/pinCode/light.json";
import pinCodeNanoSDark from "../assets/nanoS/pinCode/dark.json";
import pinCodeNanoSPLight from "../assets/nanoSP/pinCode/light.json";
import pinCodeNanoSPDark from "../assets/nanoSP/pinCode/dark.json";

import recoverNanoXLight from "../assets/nanoX/recover/light.json";
import recoverNanoXDark from "../assets/nanoX/recover/dark.json";
import recoverNanoSLight from "../assets/nanoS/recover/light.json";
import recoverNanoSDark from "../assets/nanoS/recover/dark.json";
import recoverNanoSPLight from "../assets/nanoSP/recover/light.json";
import recoverNanoSPDark from "../assets/nanoSP/recover/dark.json";

import confirmWordsNanoXLight from "../assets/nanoX/confirmWords/light.json";
import confirmWordsNanoXDark from "../assets/nanoX/confirmWords/dark.json";
import confirmWordsNanoSLight from "../assets/nanoS/confirmWords/light.json";
import confirmWordsNanoSDark from "../assets/nanoS/confirmWords/dark.json";
import confirmWordsNanoSPLight from "../assets/nanoSP/confirmWords/light.json";
import confirmWordsNanoSPDark from "../assets/nanoSP/confirmWords/dark.json";

import numberOfWordsNanoXLight from "../assets/nanoX/numberOfWords/light.json";
import numberOfWordsNanoXDark from "../assets/nanoX/numberOfWords/dark.json";
import numberOfWordsNanoSLight from "../assets/nanoS/numberOfWords/light.json";
import numberOfWordsNanoSDark from "../assets/nanoS/numberOfWords/dark.json";
import numberOfWordsNanoSPLight from "../assets/nanoSP/numberOfWords/light.json";
import numberOfWordsNanoSPDark from "../assets/nanoSP/numberOfWords/dark.json";

import powerOnNanoXLight from "../assets/nanoX/powerOn/light.json";
import powerOnNanoXDark from "../assets/nanoX/powerOn/dark.json";
import powerOnNanoSLight from "../assets/nanoS/powerOn/light.json";
import powerOnNanoSDark from "../assets/nanoS/powerOn/dark.json";
import powerOnNanoSPLight from "../assets/nanoSP/powerOn/light.json";
import powerOnNanoSPDark from "../assets/nanoSP/powerOn/dark.json";

import powerOnRecoveryNanoXLight from "../assets/nanoX/powerOnRecovery/light.json";
import powerOnRecoveryNanoXDark from "../assets/nanoX/powerOnRecovery/dark.json";
import powerOnRecoveryNanoSLight from "../assets/nanoS/powerOnRecovery/light.json";
import powerOnRecoveryNanoSDark from "../assets/nanoS/powerOnRecovery/dark.json";
import powerOnRecoveryNanoSPLight from "../assets/nanoSP/powerOnRecovery/light.json";
import powerOnRecoveryNanoSPDark from "../assets/nanoSP/powerOnRecovery/dark.json";

import importRecoveryPhrase from "../assets/importRecoveryPhrase.png";
import syncCryptos from "../assets/syncCryptos.png";

import onboardingQuizImage from "../assets/onboardingQuiz.png";

import Clock from "../../../icons/Clock";
import Edit from "../../../icons/Edit";
import Check from "../../../icons/Check";
import Warning from "../../../icons/Warning";
import WarningOutline from "../../../icons/WarningOutline";
import EyeCrossed from "../../../icons/EyeCrossed";
import Close from "../../../icons/Close";
import Flower from "../../../icons/Flower";
import ArrowRight from "../../../icons/ArrowRight";
import LText from "../../../components/LText";
import NanoDeviceCancelIcon from "../../../icons/NanoDeviceCancelIcon";
import NanoDeviceCheckIcon from "../../../icons/NanoDeviceCheckIcon";

import { urls } from "../../../config/urls";

export const lottieAnimations = {
  nanoS: {
    pinCode: {
      light: pinCodeNanoSLight,
      dark: pinCodeNanoSDark,
    },
    recover: {
      light: recoverNanoSLight,
      dark: recoverNanoSDark,
    },
    confirmWords: {
      light: confirmWordsNanoSLight,
      dark: confirmWordsNanoSDark,
    },
    numberOfWords: {
      light: numberOfWordsNanoSLight,
      dark: numberOfWordsNanoSDark,
    },
    powerOn: {
      light: powerOnNanoSLight,
      dark: powerOnNanoSDark,
    },
    powerOnRecovery: {
      light: powerOnRecoveryNanoSLight,
      dark: powerOnRecoveryNanoSDark,
    },
    style: {},
  },
  nanoSP: {
    pinCode: {
      light: pinCodeNanoSPLight,
      dark: pinCodeNanoSPDark,
    },
    recover: {
      light: recoverNanoSPLight,
      dark: recoverNanoSPDark,
    },
    confirmWords: {
      light: confirmWordsNanoSPLight,
      dark: confirmWordsNanoSPDark,
    },
    numberOfWords: {
      light: numberOfWordsNanoSPLight,
      dark: numberOfWordsNanoSPDark,
    },
    powerOn: {
      light: powerOnNanoSPLight,
      dark: powerOnNanoSPDark,
    },
    powerOnRecovery: {
      light: powerOnRecoveryNanoSPLight,
      dark: powerOnRecoveryNanoSPDark,
    },
    style: {},
  },
  nanoX: {
    pinCode: {
      light: pinCodeNanoXLight,
      dark: pinCodeNanoXDark,
    },
    recover: {
      light: recoverNanoXLight,
      dark: recoverNanoXDark,
    },
    confirmWords: {
      light: confirmWordsNanoXLight,
      dark: confirmWordsNanoXDark,
    },
    numberOfWords: {
      light: numberOfWordsNanoXLight,
      dark: numberOfWordsNanoXDark,
    },
    powerOn: {
      light: powerOnNanoXLight,
      dark: powerOnNanoXDark,
    },
    powerOnRecovery: {
      light: powerOnRecoveryNanoXLight,
      dark: powerOnRecoveryNanoXDark,
    },
    style: { width: "110%", left: "5%" },
  },
  blue: {
    pinCode: {
      light: undefined,
      dark: undefined,
    },
    recover: {
      light: undefined,
      dark: undefined,
    },
    confirmWords: {
      light: undefined,
      dark: undefined,
    },
    numberOfWords: {
      light: undefined,
      dark: undefined,
    },
    powerOn: {
      light: undefined,
      dark: undefined,
    },
    powerOnRecovery: {
      light: undefined,
      dark: undefined,
    },
    style: {},
  },
};

const recoveryWarningInfoModalProps = [
  {
    Icon: WarningOutline,
    iconColor: "orange",
    title: <Trans i18nKey="onboarding.warning.recoveryPhrase.title" />,
    desc: <Trans i18nKey="onboarding.warning.recoveryPhrase.desc" />,
    link: {
      label: <Trans i18nKey="onboarding.warning.recoveryPhrase.supportLink" />,
      url: urls.supportPage,
    },
  },
];

const pinCodeInfoModalProps = [
  {
    title: (
      <Trans i18nKey="onboarding.stepSetupDevice.pinCodeSetup.infoModal.title" />
    ),
    bullets: [
      {
        Icon: Check,
        label: (
          <Trans i18nKey="onboarding.stepSetupDevice.pinCodeSetup.infoModal.bullets.0.label" />
        ),
        color: "success",
      },
      {
        Icon: Check,
        label: (
          <Trans i18nKey="onboarding.stepSetupDevice.pinCodeSetup.infoModal.bullets.1.label" />
        ),
        color: "success",
      },
      {
        Icon: Check,
        label: (
          <Trans i18nKey="onboarding.stepSetupDevice.pinCodeSetup.infoModal.bullets.2.label" />
        ),
        color: "success",
      },
      {
        Icon: Check,
        label: (
          <Trans i18nKey="onboarding.stepSetupDevice.pinCodeSetup.infoModal.bullets.3.label" />
        ),
        color: "success",
      },
      {
        Icon: Check,
        label: (
          <Trans i18nKey="onboarding.stepSetupDevice.pinCodeSetup.infoModal.bullets.4.label" />
        ),
        color: "success",
      },
      {
        Icon: Close,
        label: (
          <Trans i18nKey="onboarding.stepSetupDevice.pinCodeSetup.infoModal.bullets.5.label" />
        ),
        color: "alert",
      },
      {
        Icon: Close,
        label: (
          <Trans i18nKey="onboarding.stepSetupDevice.pinCodeSetup.infoModal.bullets.6.label" />
        ),
        color: "alert",
      },
      {
        Icon: Close,
        label: (
          <Trans i18nKey="onboarding.stepSetupDevice.pinCodeSetup.infoModal.bullets.7.label" />
        ),
        color: "alert",
      },
    ],
  },
];

const recoveryPhraseInfoModalProps = [
  {
    title: (
      <Trans i18nKey="onboarding.stepSetupDevice.recoveryPhraseSetup.infoModal.title" />
    ),
    desc: (
      <Trans i18nKey="onboarding.stepSetupDevice.recoveryPhraseSetup.infoModal.desc" />
    ),
  },
  {
    desc: (
      <Trans i18nKey="onboarding.stepSetupDevice.recoveryPhraseSetup.infoModal.desc_1" />
    ),
    link: {
      label: (
        <Trans i18nKey="onboarding.stepSetupDevice.recoveryPhraseSetup.infoModal.link" />
      ),
      url: urls.recoveryPhraseInfo,
    },
  },
  {
    title: (
      <Trans i18nKey="onboarding.stepSetupDevice.recoveryPhraseSetup.infoModal.title_1" />
    ),
    bullets: [
      {
        Icon: ArrowRight,
        label: (
          <Trans i18nKey="onboarding.stepSetupDevice.recoveryPhraseSetup.infoModal.bullets.0.label" />
        ),
      },
      {
        Icon: ArrowRight,
        label: (
          <Trans i18nKey="onboarding.stepSetupDevice.recoveryPhraseSetup.infoModal.bullets.1.label" />
        ),
      },
      {
        Icon: ArrowRight,
        label: (
          <Trans i18nKey="onboarding.stepSetupDevice.recoveryPhraseSetup.infoModal.bullets.2.label" />
        ),
      },
    ],
  },
];

const hideRecoveryPhraseInfoModalProps = [
  {
    title: (
      <Trans i18nKey="onboarding.stepSetupDevice.hideRecoveryPhrase.infoModal.title" />
    ),
    bullets: [
      {
        Icon: ArrowRight,
        label: (
          <Trans i18nKey="onboarding.stepSetupDevice.hideRecoveryPhrase.infoModal.bullets.0.label">
            {""}
            <LText bold>{""}</LText>
            {""}
          </Trans>
        ),
      },
      {
        Icon: ArrowRight,
        label: (
          <Trans i18nKey="onboarding.stepSetupDevice.hideRecoveryPhrase.infoModal.bullets.1.label">
            {""}
            <LText bold>{""}</LText>
            {""}
          </Trans>
        ),
      },
      {
        Icon: ArrowRight,
        label: (
          <Trans i18nKey="onboarding.stepSetupDevice.hideRecoveryPhrase.infoModal.bullets.2.label">
            {""}
            <LText bold>{""}</LText>
            {""}
          </Trans>
        ),
      },
      {
        Icon: ArrowRight,
        label: (
          <Trans i18nKey="onboarding.stepSetupDevice.hideRecoveryPhrase.infoModal.bullets.3.label">
            {""}
            <LText bold>{""}</LText>
            {""}
          </Trans>
        ),
      },
      {
        Icon: ArrowRight,
        label: (
          <Trans i18nKey="onboarding.stepSetupDevice.hideRecoveryPhrase.infoModal.bullets.4.label">
            {""}
            <LText bold>{""}</LText>
            {""}
          </Trans>
        ),
      },
    ],
  },
];

const pairNewErrorInfoModalProps = [
  {
    title: <Trans i18nKey="onboarding.stepPairNew.errorInfoModal.title" />,
    desc: <Trans i18nKey="onboarding.stepPairNew.errorInfoModal.desc" />,
  },
  {
    desc: <Trans i18nKey="onboarding.stepPairNew.errorInfoModal.desc_1" />,
  },
  {
    desc: <Trans i18nKey="onboarding.stepPairNew.errorInfoModal.desc_2" />,
  },
  ...(Platform.OS === "android"
    ? [
        {
          title: (
            <Trans i18nKey="onboarding.stepPairNew.errorInfoModal.title_1" />
          ),
          desc: (
            <Trans i18nKey="onboarding.stepPairNew.errorInfoModal.desc_3" />
          ),
        },
        {
          desc: (
            <Trans i18nKey="onboarding.stepPairNew.errorInfoModal.desc_4" />
          ),
          bullets: [
            {
              Icon: ArrowRight,
              label: (
                <Trans i18nKey="onboarding.stepPairNew.errorInfoModal.bullets.0.label" />
              ),
            },
            {
              Icon: ArrowRight,
              label: (
                <Trans i18nKey="onboarding.stepPairNew.errorInfoModal.bullets.1.label" />
              ),
            },
            {
              Icon: ArrowRight,
              label: (
                <Trans i18nKey="onboarding.stepPairNew.errorInfoModal.bullets.2.label" />
              ),
            },
            {
              Icon: ArrowRight,
              label: (
                <Trans i18nKey="onboarding.stepPairNew.errorInfoModal.bullets.3.label" />
              ),
            },
          ],
        },
        {
          desc: (
            <Trans i18nKey="onboarding.stepPairNew.errorInfoModal.desc_5" />
          ),
        },
        {
          title: (
            <Trans i18nKey="onboarding.stepPairNew.errorInfoModal.title_2" />
          ),
          desc: (
            <Trans i18nKey="onboarding.stepPairNew.errorInfoModal.desc_6" />
          ),
        },
      ]
    : []),
];

export const infoModalScenes = {
  recoveryWarningInfoModalProps,
  pinCodeInfoModalProps,
  recoveryPhraseInfoModalProps,
  hideRecoveryPhraseInfoModalProps,
  pairNewErrorInfoModalProps,
};

const pinCodeScenes = (deviceModelId, theme: "dark" | "light") => [
  {
    sceneProps: {
      trackPage: "Pin code step 1",
      image: pinCodeImage,
      title: <Trans i18nKey="onboarding.stepSetupDevice.pinCode.title" />,
      descs: [<Trans i18nKey="onboarding.stepSetupDevice.pinCode.desc" />],
      ctaText: <Trans i18nKey="onboarding.stepSetupDevice.pinCode.cta" />,
      ctaWarningCheckbox: {
        desc: (
          <Trans i18nKey="onboarding.stepSetupDevice.pinCode.checkboxDesc" />
        ),
      },
    },
    sceneInfoKey: "pinCodeInfoModalProps",
    type: "primary",
    id: "pinCode",
  },
  {
    sceneProps: {
      trackPage: "Pin code step 2",
      lottie: lottieAnimations[deviceModelId].pinCode[theme],
      lottieStyle: lottieAnimations[deviceModelId].style,
      bullets: [
        {
          title: (
            <Trans i18nKey="onboarding.stepSetupDevice.pinCodeSetup.bullets.0.title" />
          ),
          label: (
            <>
              <Trans i18nKey="onboarding.stepSetupDevice.pinCodeSetup.bullets.0.label">
                {""}
                <NanoDeviceCheckIcon size={12} />
                <NanoDeviceCancelIcon size={12} />
                {""}
              </Trans>
            </>
          ),
        },
        {
          title: (
            <Trans i18nKey="onboarding.stepSetupDevice.pinCodeSetup.bullets.1.title" />
          ),
          label: (
            <Trans i18nKey="onboarding.stepSetupDevice.pinCodeSetup.bullets.1.label" />
          ),
        },
      ],
      ctaText: <Trans i18nKey="onboarding.stepSetupDevice.pinCodeSetup.cta" />,
    },
    sceneInfoKey: "pinCodeInfoModalProps",
    type: "secondary",
    id: "pinCodeSetup",
  },
];

const getSetupDeviceScenes: (
  deviceModelId: "nanoS" | "nanoSP" | "nanoX" | "blue",
  theme: "dark" | "light",
) => OnboardingScene[] = (deviceModelId, theme) => [
  {
    sceneProps: {
      trackPage: "Get started step 1",
      image: setupDeviceStartImage,
      title: <Trans i18nKey="onboarding.stepSetupDevice.start.title" />,
      bullets: [
        {
          Icon: Clock,
          label: (
            <Trans i18nKey="onboarding.stepSetupDevice.start.bullets.0.label" />
          ),
        },
        {
          Icon: Edit,
          label: (
            <Trans i18nKey="onboarding.stepSetupDevice.start.bullets.1.label" />
          ),
        },
        {
          Icon: Flower,
          label: (
            <Trans i18nKey="onboarding.stepSetupDevice.start.bullets.2.label" />
          ),
        },
      ],
      ctaText: <Trans i18nKey="onboarding.stepSetupDevice.start.cta" />,
      ctaWarningModal: {
        Icon: Warning,
        title: (
          <Trans i18nKey="onboarding.stepSetupDevice.start.warning.title" />
        ),
        desc: <Trans i18nKey="onboarding.stepSetupDevice.start.warning.desc" />,
        ctaText: (
          <Trans i18nKey="onboarding.stepSetupDevice.start.warning.ctaText" />
        ),
      },
    },
    type: "primary",
    id: "start",
  },
  {
    sceneProps: {
      trackPage: "Get started step 2",
      lottie: lottieAnimations[deviceModelId].powerOn[theme],
      lottieStyle: lottieAnimations[deviceModelId].style,
      bullets: [
        {
          title: (
            <Trans i18nKey="onboarding.stepSetupDevice.setup.bullets.0.title" />
          ),
          label: (
            <Trans
              i18nKey={`onboarding.stepSetupDevice.setup.bullets.0.${deviceModelId}.label`}
            />
          ),
        },
        {
          title: (
            <Trans i18nKey="onboarding.stepSetupDevice.setup.bullets.1.title" />
          ),
          label: (
            <Trans i18nKey="onboarding.stepSetupDevice.setup.bullets.1.label" />
          ),
        },
        {
          title: (
            <Trans i18nKey="onboarding.stepSetupDevice.setup.bullets.2.title" />
          ),
          label: (
            <Trans i18nKey="onboarding.stepSetupDevice.setup.bullets.2.label" />
          ),
        },
        {
          title: (
            <Trans i18nKey="onboarding.stepSetupDevice.setup.bullets.3.title" />
          ),
          label: (
            <Trans i18nKey="onboarding.stepSetupDevice.setup.bullets.3.label" />
          ),
        },
      ],
      ctaText: <Trans i18nKey="onboarding.stepSetupDevice.setup.cta" />,
    },
    type: "secondary",
    id: "setup",
  },
  ...pinCodeScenes(deviceModelId, theme),
  {
    sceneProps: {
      trackPage: "Recovery step 1",
      image: recoveryPhrase,
      title: (
        <Trans i18nKey="onboarding.stepSetupDevice.recoveryPhrase.title" />
      ),
      descs: [
        <Trans i18nKey="onboarding.stepSetupDevice.recoveryPhrase.desc" />,
        <Trans i18nKey="onboarding.stepSetupDevice.recoveryPhrase.desc_1" />,
      ],
      ctaText: (
        <Trans i18nKey="onboarding.stepSetupDevice.recoveryPhrase.cta" />
      ),
      ctaWarningCheckbox: {
        desc: (
          <Trans i18nKey="onboarding.stepSetupDevice.recoveryPhrase.checkboxDesc" />
        ),
      },
    },
    sceneInfoKey: "recoveryPhraseInfoModalProps",
    type: "primary",
    id: "recoveryPhrase",
  },
  {
    sceneProps: {
      trackPage: "Recovery step 2",
      image: recoverySheet,
      bullets: [
        {
          title: (
            <Trans i18nKey="onboarding.stepSetupDevice.recoveryPhraseSetup.bullets.0.title" />
          ),
          label: (
            <Trans i18nKey="onboarding.stepSetupDevice.recoveryPhraseSetup.bullets.0.label" />
          ),
        },
        {
          title: (
            <Trans i18nKey="onboarding.stepSetupDevice.recoveryPhraseSetup.bullets.1.title" />
          ),
          labels: [
            <Trans i18nKey="onboarding.stepSetupDevice.recoveryPhraseSetup.bullets.1.label" />,
            <Trans i18nKey="onboarding.stepSetupDevice.recoveryPhraseSetup.bullets.1.label_1" />,
          ],
        },
      ],
      ctaText: (
        <Trans i18nKey="onboarding.stepSetupDevice.recoveryPhraseSetup.cta" />
      ),
    },
    sceneInfoKey: "recoveryPhraseInfoModalProps",
    type: "secondary",
    id: "recoveryPhraseSetup",
  },
  {
    sceneProps: {
      trackPage: "Recovery step 3",
      lottie: lottieAnimations[deviceModelId].recover[theme],
      lottieStyle: lottieAnimations[deviceModelId].style,
      bullets: [
        {
          index: 3,
          title: (
            <Trans i18nKey="onboarding.stepSetupDevice.recoveryPhraseSetup.bullets.2.title" />
          ),
          label: (
            <Trans i18nKey="onboarding.stepSetupDevice.recoveryPhraseSetup.bullets.2.label" />
          ),
        },
        {
          index: 4,
          title: (
            <Trans i18nKey="onboarding.stepSetupDevice.recoveryPhraseSetup.bullets.3.title" />
          ),
          label: "",
        },
      ],
      ctaText: (
        <Trans i18nKey="onboarding.stepSetupDevice.recoveryPhraseSetup.nextStep" />
      ),
    },
    sceneInfoKey: "recoveryPhraseInfoModalProps",
    type: "secondary",
    id: "recoveryPhraseSetup_1",
  },
  {
    sceneProps: {
      trackPage: "Recovery step 4",
      image: hideRecoveryPhrase,
      title: (
        <Trans i18nKey="onboarding.stepSetupDevice.hideRecoveryPhrase.title" />
      ),
      descs: [
        <Trans i18nKey="onboarding.stepSetupDevice.hideRecoveryPhrase.desc" />,
      ],
      bullets: [
        {
          Icon: Warning,
          color: "alert",
          label: (
            <Trans i18nKey="onboarding.stepSetupDevice.hideRecoveryPhrase.bullets.0.label" />
          ),
        },
        {
          Icon: EyeCrossed,
          color: "alert",
          label: (
            <Trans i18nKey="onboarding.stepSetupDevice.hideRecoveryPhrase.bullets.1.label" />
          ),
        },
      ],
      ctaText: (
        <Trans i18nKey="onboarding.stepSetupDevice.hideRecoveryPhrase.cta" />
      ),
      ctaWarningModal: {
        image: onboardingQuizImage,
        title: (
          <Trans i18nKey="onboarding.stepSetupDevice.hideRecoveryPhrase.warning.title" />
        ),
        desc: (
          <Trans i18nKey="onboarding.stepSetupDevice.hideRecoveryPhrase.warning.desc" />
        ),
        ctaText: (
          <Trans i18nKey="onboarding.stepSetupDevice.hideRecoveryPhrase.warning.cta" />
        ),
      },
    },
    sceneInfoKey: "hideRecoveryPhraseInfoModalProps",
    type: "primary",
    id: "hideRecoveryPhrase",
  },
];

const getRecoveryPhraseScenes = (
  deviceModelId: string,
  theme: "dark" | "light",
) => [
  {
    sceneProps: {
      trackPage: "RecoveryPhrase step 1",
      image: importRecoveryPhrase,
      title: (
        <Trans i18nKey="onboarding.stepRecoveryPhrase.importRecoveryPhrase.title" />
      ),
      descs: [
        <Trans i18nKey="onboarding.stepRecoveryPhrase.importRecoveryPhrase.desc" />,
        <Trans i18nKey="onboarding.stepRecoveryPhrase.importRecoveryPhrase.desc_1" />,
      ],
      ctaText: (
        <Trans i18nKey="onboarding.stepRecoveryPhrase.importRecoveryPhrase.cta" />
      ),
      ctaWarningModal: {
        Icon: Warning,
        title: (
          <Trans i18nKey="onboarding.stepRecoveryPhrase.importRecoveryPhrase.warning.title" />
        ),
        desc: (
          <Trans i18nKey="onboarding.stepRecoveryPhrase.importRecoveryPhrase.warning.desc" />
        ),
        ctaText: (
          <Trans i18nKey="onboarding.stepRecoveryPhrase.importRecoveryPhrase.warning.cta" />
        ),
      },
    },
    type: "primary",
    id: "importRecoveryPhrase",
  },
  {
    sceneProps: {
      trackPage: "RecoveryPhrase step 2",
      lottie: lottieAnimations[deviceModelId].powerOnRecovery[theme],
      lottieStyle: lottieAnimations[deviceModelId].style,
      bullets: [
        {
          title: (
            <Trans i18nKey="onboarding.stepRecoveryPhrase.importRecoveryPhrase.bullets.0.title" />
          ),
          label: (
            <Trans
              i18nKey={`onboarding.stepRecoveryPhrase.importRecoveryPhrase.bullets.0.${deviceModelId}.label`}
            />
          ),
        },
        {
          title: (
            <Trans i18nKey="onboarding.stepRecoveryPhrase.importRecoveryPhrase.bullets.1.title" />
          ),
          label: (
            <Trans i18nKey="onboarding.stepRecoveryPhrase.importRecoveryPhrase.bullets.1.label" />
          ),
        },
        {
          title: (
            <Trans i18nKey="onboarding.stepRecoveryPhrase.importRecoveryPhrase.bullets.2.title" />
          ),
          label: (
            <Trans i18nKey="onboarding.stepRecoveryPhrase.importRecoveryPhrase.bullets.2.label" />
          ),
        },
        {
          title: (
            <Trans i18nKey="onboarding.stepRecoveryPhrase.importRecoveryPhrase.bullets.3.title" />
          ),
          label: (
            <Trans i18nKey="onboarding.stepRecoveryPhrase.importRecoveryPhrase.bullets.3.label" />
          ),
        },
      ],
      ctaText: (
        <Trans i18nKey="onboarding.stepRecoveryPhrase.importRecoveryPhrase.nextStep" />
      ),
    },
    type: "secondary",
    id: "importRecoveryPhrase_1",
  },
  ...pinCodeScenes(deviceModelId, theme),
  {
    sceneProps: {
      trackPage: "RecoveryPhrase step 3",
      image: importRecoveryPhrase,
      title: (
        <Trans i18nKey="onboarding.stepRecoveryPhrase.existingRecoveryPhrase.title" />
      ),
      descs: [
        <Trans i18nKey="onboarding.stepRecoveryPhrase.existingRecoveryPhrase.paragraph1" />,
        <Trans i18nKey="onboarding.stepRecoveryPhrase.existingRecoveryPhrase.paragraph2" />,
      ],
      ctaText: (
        <Trans i18nKey="onboarding.stepRecoveryPhrase.existingRecoveryPhrase.nextStep" />
      ),
      ctaWarningCheckbox: {
        desc: (
          <Trans i18nKey="onboarding.stepRecoveryPhrase.existingRecoveryPhrase.checkboxDesc" />
        ),
      },
    },
    sceneInfoKey: "recoveryPhraseInfoModalProps",
    type: "primary",
    id: "existingRecoveryPhrase",
  },
  {
    sceneProps: {
      trackPage: "RecoveryPhrase step 4",
      lottie: lottieAnimations[deviceModelId].numberOfWords[theme],
      lottieStyle: lottieAnimations[deviceModelId].style,
      bullets: [
        {
          title: (
            <Trans i18nKey="onboarding.stepRecoveryPhrase.existingRecoveryPhrase.bullets.0.title" />
          ),
        },
        {
          title: (
            <Trans i18nKey="onboarding.stepRecoveryPhrase.existingRecoveryPhrase.bullets.1.title" />
          ),
          label: (
            <Trans i18nKey="onboarding.stepRecoveryPhrase.existingRecoveryPhrase.bullets.1.label" />
          ),
        },
      ],
      ctaText: (
        <Trans i18nKey="onboarding.stepRecoveryPhrase.existingRecoveryPhrase.nextStep" />
      ),
    },
    sceneInfoKey: "recoveryPhraseInfoModalProps",
    type: "secondary",
    id: "existingRecoveryPhrase_1",
  },
  {
    trackPage: "RecoveryPhrase step 5",
    sceneProps: {
      lottie: lottieAnimations[deviceModelId].confirmWords[theme],
      lottieStyle: lottieAnimations[deviceModelId].style,
      bullets: [
        {
          index: 3,
          title: (
            <Trans i18nKey="onboarding.stepRecoveryPhrase.existingRecoveryPhrase.bullets.2.title" />
          ),
          label: (
            <Trans i18nKey="onboarding.stepRecoveryPhrase.existingRecoveryPhrase.bullets.2.label" />
          ),
        },
        {
          index: 4,
          title: (
            <Trans i18nKey="onboarding.stepRecoveryPhrase.existingRecoveryPhrase.bullets.3.title" />
          ),
          label: (
            <Trans i18nKey="onboarding.stepRecoveryPhrase.existingRecoveryPhrase.bullets.3.label" />
          ),
        },
        {
          index: 5,
          title: (
            <Trans i18nKey="onboarding.stepRecoveryPhrase.existingRecoveryPhrase.bullets.4.title" />
          ),
        },
      ],
      ctaText: (
        <Trans i18nKey="onboarding.stepRecoveryPhrase.existingRecoveryPhrase.nextStep" />
      ),
    },
    sceneInfoKey: "recoveryPhraseInfoModalProps",
    type: "secondary",
    id: "existingRecoveryPhrase_2",
  },
];

const importAccountsScenes = [
  {
    sceneProps: {
      trackPage: "Import accounts step 1",
      image: syncCryptos,
      title: <Trans i18nKey="onboarding.stepImportAccounts.title" />,
      descs: [<Trans i18nKey="onboarding.stepImportAccounts.desc" />],
      ctaText: <Trans i18nKey="onboarding.stepImportAccounts.cta" />,
      bullets: [
        {
          Icon: Clock,
          label: (
            <Trans i18nKey="onboarding.stepImportAccounts.bullets.0.label">
              {""}
              <LText semiBold style={{ color: "#FFF" }} />
              {""}
            </Trans>
          ),
        },
        {
          Icon: Clock,
          label: (
            <Trans i18nKey="onboarding.stepImportAccounts.bullets.1.label" />
          ),
        },
        {
          Icon: Check,
          label: (
            <Trans i18nKey="onboarding.stepImportAccounts.bullets.2.label" />
          ),
        },
      ],
      ctaWarningModal: {
        Icon: Warning,
        title: <Trans i18nKey="onboarding.stepImportAccounts.warning.title" />,
        desc: <Trans i18nKey="onboarding.stepImportAccounts.warning.desc" />,
        ctaText: <Trans i18nKey="onboarding.stepImportAccounts.warning.cta" />,
      },
    },
    type: "primary",
    id: "importRecoveryPhrase",
  },
];

export { getSetupDeviceScenes, getRecoveryPhraseScenes, importAccountsScenes };


<|start_filename|>src/components/RootNavigator/SignTransactionNavigator.js<|end_filename|>
// @flow
import React, { useMemo } from "react";
import { createStackNavigator } from "@react-navigation/stack";
import { useTranslation } from "react-i18next";
import { useTheme } from "styled-components/native";
import { ScreenName } from "../../const";
import SignTransactionSummary from "../../screens/SendFunds/04-Summary";
import SelectDevice from "../../screens/SelectDevice";
import SignTransactionConnectDevice from "../../screens/SignTransaction/02-ConnectDevice";
import SignTransactionValidationError from "../../screens/SignTransaction/03-ValidationError";
import { getStackNavigatorConfig } from "../../navigation/navigatorConfig";
import StepHeader from "../StepHeader";

const totalSteps = "3";

export default function SignTransactionNavigator() {
  const { t } = useTranslation();
  const { colors } = useTheme();
  const stackNavigationConfig = useMemo(
    () => getStackNavigatorConfig(colors, true),
    [colors],
  );

  // @TODO replace with correct error
  const listeners = ({ route }) => ({
    beforeRemove: () =>
      route.params?.onError(
        route.params.error || new Error("Signature interrupted by user"),
      ),
  });

  return (
    <Stack.Navigator screenOptions={stackNavigationConfig}>
      <Stack.Screen
        name={ScreenName.SignTransactionSummary}
        component={SignTransactionSummary}
        options={{
          headerTitle: () => (
            <StepHeader
              title={t("send.stepperHeader.summary")}
              subtitle={t("send.stepperHeader.stepRange", {
                currentStep: "1",
                totalSteps,
              })}
            />
          ),
        }}
        listeners={listeners}
      />
      <Stack.Screen
        name={ScreenName.SignTransactionSelectDevice}
        component={SelectDevice}
        options={{
          headerTitle: () => (
            <StepHeader
              title={t("send.stepperHeader.selectDevice")}
              subtitle={t("send.stepperHeader.stepRange", {
                currentStep: "2",
                totalSteps,
              })}
            />
          ),
        }}
        listeners={listeners}
      />
      <Stack.Screen
        name={ScreenName.SignTransactionConnectDevice}
        component={SignTransactionConnectDevice}
        options={{
          headerTitle: () => (
            <StepHeader
              title={t("send.stepperHeader.connectDevice")}
              subtitle={t("send.stepperHeader.stepRange", {
                currentStep: "2",
                totalSteps,
              })}
            />
          ),
        }}
      />
      <Stack.Screen
        name={ScreenName.SignTransactionValidationError}
        component={SignTransactionValidationError}
        options={{
          headerShown: false,
        }}
        listeners={listeners}
      />
    </Stack.Navigator>
  );
}

const Stack = createStackNavigator();


<|start_filename|>src/screens/Account/Header.js<|end_filename|>
// @flow

import React, { memo } from "react";
import { View, StyleSheet } from "react-native";
import { useSelector } from "react-redux";
import { useGlobalSyncState } from "@ledgerhq/live-common/lib/bridge/react";
import { networkErrorSelector } from "../../reducers/appstate";
import HeaderErrorTitle from "../../components/HeaderErrorTitle";

const Header = () => {
  const { error } = useGlobalSyncState();
  const networkError = useSelector(networkErrorSelector);
  return error ? (
    <View style={styles.root}>
      <HeaderErrorTitle withDescription error={networkError || error} />
    </View>
  ) : null;
};

export default memo<{}>(Header);

const styles = StyleSheet.create({
  root: {
    paddingTop: 16,
  },
});


<|start_filename|>src/families/ethereum/EthereumFeesStrategy.js<|end_filename|>
/* @flow */
import React, { useCallback, useState, useMemo, useEffect } from "react";

import { useFeesStrategy } from "@ledgerhq/live-common/lib/families/ethereum/react";
import { getAccountBridge } from "@ledgerhq/live-common/lib/bridge";

import type { Account, AccountLike } from "@ledgerhq/live-common/lib/types";
import type { Transaction } from "@ledgerhq/live-common/lib/families/ethereum/types";
import { getGasLimit } from "@ledgerhq/live-common/lib/families/ethereum/transaction";
import type { RouteParams } from "../../screens/SendFunds/04-Summary";
import { ScreenName } from "../../const";
import SelectFeesStrategy from "../../components/SelectFeesStrategy";

type Props = {
  account: AccountLike,
  parentAccount: ?Account,
  transaction: Transaction,
  navigation: any,
  route: { params: RouteParams },
  setTransaction: Function,
  ...
};

const getCustomStrategy = transaction => {
  if (transaction.feesStrategy === "custom") {
    return {
      label: "custom",
      forceValueLabel: null,
      amount: transaction.gasPrice,
      displayedAmount: transaction.gasPrice.multipliedBy(
        getGasLimit(transaction),
      ),
      userGasLimit: getGasLimit(transaction),
    };
  }

  return null;
};

export default function EthereumFeesStrategy({
  account,
  parentAccount,
  transaction,
  setTransaction,
  navigation,
  route,
  ...props
}: Props) {
  const defaultStrategies = useFeesStrategy(transaction);
  const [customStrategy, setCustomStrategy] = useState(
    getCustomStrategy(transaction),
  );
  const strategies = useMemo(
    () =>
      customStrategy
        ? [...defaultStrategies, customStrategy]
        : defaultStrategies,
    [defaultStrategies, customStrategy],
  );

  useEffect(() => {
    const newCustomStrategy = getCustomStrategy(transaction);
    if (newCustomStrategy) {
      setCustomStrategy(newCustomStrategy);
    }
  }, [transaction, setCustomStrategy]);

  const onFeesSelected = useCallback(
    ({ amount, label, userGasLimit }) => {
      const bridge = getAccountBridge(account, parentAccount);

      setTransaction(
        bridge.updateTransaction(transaction, {
          gasPrice: amount,
          feesStrategy: label,
          userGasLimit: userGasLimit || transaction.userGasLimit,
        }),
      );
    },
    [setTransaction, account, parentAccount, transaction],
  );

  const openCustomFees = useCallback(() => {
    navigation.navigate(ScreenName.EthereumCustomFees, {
      ...route.params,
      accountId: account.id,
      parentId: parentAccount && parentAccount.id,
      transaction,
    });
  }, [navigation, route.params, account.id, parentAccount, transaction]);

  return (
    <SelectFeesStrategy
      {...props}
      strategies={strategies}
      onStrategySelect={onFeesSelected}
      onCustomFeesPress={openCustomFees}
      account={account}
      parentAccount={parentAccount}
      transaction={transaction}
    />
  );
}


<|start_filename|>src/icons/DeviceIconCheck.js<|end_filename|>
import React from "react";
import Svg, { G, Rect, Path } from "react-native-svg";

const DeviceIconCheck = props => (
  <Svg width={15} height={15} {...props}>
    <G fill="#bdb3ff">
      <Rect width={15} height={15} fillOpacity={0.3} rx={2} />
      <Path d="M9.5 5.5v-1h2v2h-1v1h-1v1h-1v1h-1v1h-2v-1h-1v-1h-1v-2h2v1h2v-1h1v-1h1z" />
    </G>
  </Svg>
);

export default DeviceIconCheck;


<|start_filename|>src/screens/Exchange/Buy.js<|end_filename|>
// @flow

import React, { useCallback, useState } from "react";
import { View, StyleSheet, Platform } from "react-native";
import SafeAreaView from "react-native-safe-area-view";
import { useTranslation } from "react-i18next";
import { useNavigation, useTheme } from "@react-navigation/native";
import { usePlatformApp } from "@ledgerhq/live-common/lib/platform/PlatformAppProvider";
import extraStatusBarPadding from "../../logic/extraStatusBarPadding";
import TrackScreen from "../../analytics/TrackScreen";
import Button from "../../components/Button";
import { ScreenName } from "../../const";
import LText from "../../components/LText";
import BuyOption from "./BuyOption";
import MoonPay from "../../icons/providers/MoonPay";
import Coinify from "../../icons/providers/Coinify";

const forceInset = { bottom: "always" };

type Provider = "moonpay" | "coinify" | null;

export default function Buy() {
  const { t } = useTranslation();
  const navigation = useNavigation();
  const { colors } = useTheme();

  const [provider, setProvider] = useState<Provider>(null);
  const { manifests } = usePlatformApp();

  const navigateToMoonPay = useCallback(() => {
    const manifest = manifests.get("moonpay");
    navigation.navigate(ScreenName.PlatformApp, {
      platform: manifest.id,
      name: manifest.name,
    });
  }, [navigation, manifests]);

  const navigateToCoinify = useCallback(() => {
    navigation.navigate(ScreenName.Coinify);
  }, [navigation]);

  const onContinue = useCallback(() => {
    provider === "moonpay" ? navigateToMoonPay() : navigateToCoinify();
  }, [provider, navigateToMoonPay, navigateToCoinify]);

  const moonPayIcon = <MoonPay size={40} />;
  const coinifyIcon = <Coinify size={40} />;

  return (
    <SafeAreaView
      style={[
        styles.root,
        {
          backgroundColor: colors.card,
          paddingTop: extraStatusBarPadding,
        },
      ]}
      forceInset={forceInset}
    >
      <TrackScreen category="Buy Crypto" />
      <View style={styles.body}>
        <LText semiBold>{t("exchange.buy.title")}</LText>
        <View style={styles.providers}>
          <BuyOption
            name={"MoonPay"}
            icon={moonPayIcon}
            supportedCoinsCount={40}
            onPress={() => setProvider("moonpay")}
            isActive={provider === "moonpay"}
          />
          <BuyOption
            name={"Coinify"}
            icon={coinifyIcon}
            supportedCoinsCount={10}
            onPress={() => setProvider("coinify")}
            isActive={provider === "coinify"}
          />
        </View>
      </View>
      <View
        style={[
          styles.footer,
          {
            ...Platform.select({
              android: {
                borderTopColor: "rgba(20, 37, 51, 0.1)",
                borderTopWidth: 1,
              },
              ios: {
                shadowColor: "rgb(20, 37, 51)",
                shadowRadius: 14,
                shadowOpacity: 0.04,
                shadowOffset: {
                  width: 0,
                  height: -4,
                },
              },
            }),
          },
        ]}
      >
        <Button
          containerStyle={styles.button}
          type={"primary"}
          title={t("common.continue")}
          onPress={() => onContinue()}
          disabled={!provider}
        />
      </View>
    </SafeAreaView>
  );
}

const styles = StyleSheet.create({
  root: {
    flex: 1,
  },
  body: {
    flex: 1,
    display: "flex",
    alignItems: "center",
    justifyContent: "center",
    margin: 16,
  },
  providers: {
    flex: 1,
    marginTop: 8,
  },
  footer: {
    marginTop: 40,
    padding: 16,
  },
  button: {
    alignSelf: "stretch",
    minWidth: "100%",
  },
});


<|start_filename|>android/app/src/main/java/com/ledger/live/BluetoothHelperModule.java<|end_filename|>
package com.ledger.live;

import android.bluetooth.BluetoothAdapter;
import android.content.Intent;
import android.app.Activity;

import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.ActivityEventListener;
import com.facebook.react.bridge.BaseActivityEventListener;

import java.util.Map;
import java.util.HashMap;

public class BluetoothHelperModule extends ReactContextBaseJavaModule {

  private static final int REQUEST_ENABLE_BT = 0;

  private static final String E_ACTIVITY_DOES_NOT_EXIST = "E_ACTIVITY_DOES_NOT_EXIST";
  private static final String E_BLE_CANCELLED = "E_BLE_CANCELLED";

  private Promise blePromise;

  private final ActivityEventListener bleActivityEventListener = new BaseActivityEventListener() {

  @Override
  public void onActivityResult(Activity activity, int requestCode, int resultCode, Intent intent) {
      if (requestCode == REQUEST_ENABLE_BT) {
        if (blePromise != null) {
          if (resultCode == Activity.RESULT_CANCELED) {
            blePromise.reject(E_BLE_CANCELLED, "Ble activation was cancelled");
          } else if (resultCode == Activity.RESULT_OK) {
            blePromise.resolve(true);
          }

          blePromise = null;
        }
      }
    }
  };

  public BluetoothHelperModule(ReactApplicationContext context) {
    super(context);

    // Add the listener for `onActivityResult`
    context.addActivityEventListener(bleActivityEventListener);
  }

  @Override
  public String getName() {
    return "BluetoothHelperModule";
  }

  

  /*
   * check if bluetooth is available.
   */
  @ReactMethod
  public boolean isBluetoothAvailable() {
    BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
    return bluetoothAdapter != null && bluetoothAdapter.isEnabled();
  }

  /*
   * Prompts the user to enable bluetooth if possible.
   */
  @ReactMethod
  public void prompt(Promise promise) {
    boolean isBLEAvailable = this.isBluetoothAvailable();

    if (!isBLEAvailable) {
      // Activity Action: Show a system activity that allows the user to turn on Bluetooth.
      // This system activity will return once Bluetooth has completed turning on, or the user has decided not to turn Bluetooth on.
      // See: https://developer.android.com/reference/android/bluetooth/BluetoothAdapter#ACTION_REQUEST_ENABLE
      Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
      blePromise = promise;

      this.getCurrentActivity().startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
    } else {
      promise.resolve(true);
    }    
  }
}

<|start_filename|>src/screens/Swap/FormSelection/AccountAmountRow.js<|end_filename|>
// @flow
import React from "react";
import { View, StyleSheet, ActivityIndicator } from "react-native";

import { getAccountUnit } from "@ledgerhq/live-common/lib/account";

import { Trans } from "react-i18next";
import { useTheme } from "@react-navigation/native";

import { AmountRequired } from "@ledgerhq/errors";
import { BigNumber } from "bignumber.js";

import type {
  Account,
  TokenAccount,
  TokenCurrency,
  CryptoCurrency,
} from "@ledgerhq/live-common/lib/types";
import type { SwapTransaction } from "@ledgerhq/live-common/lib/exchange/swap/types";
import type { SwapDataType } from "@ledgerhq/live-common/lib/exchange/swap/hooks";
import type { SwapRouteParams } from "..";

import LText from "../../../components/LText";
import AccountSelect from "./AccountSelect";
import CurrencyInput from "../../../components/CurrencyInput";
import CurrencyUnitValue from "../../../components/CurrencyUnitValue";
import TranslatedError from "../../../components/TranslatedError";
import getFontStyle from "../../../components/LText/getFontStyle";
import CounterValue from "../../../components/CounterValue";
import CurrencyTargetSelect from "./CurrencyTargetSelect";

type Props = {
  navigation: *,
  route: { params: SwapRouteParams },
  swap: SwapDataType,
  setFromAccount: (account?: Account | TokenAccount) => void,
  setFromAmount: (amount: BigNumber) => void,
  setToCurrency: (currency?: TokenCurrency | CryptoCurrency) => void,
  useAllAmount: boolean,
  transaction: SwapTransaction,
  bridgePending: boolean,
  rate: any,
  fromAmountError?: Error,
  providers: any,
  provider: any,
};

export default function AccountAmountRow({
  navigation,
  route,
  useAllAmount,
  swap,
  setFromAccount,
  setFromAmount,
  setToCurrency,
  rate,
  bridgePending,
  fromAmountError,
  providers,
  provider,
}: Props) {
  const { colors } = useTheme();
  const {
    from: { account },
    to: { currency: toCurrency },
    rates: { status: ratesStatus },
  } = swap;

  const fromUnit = account && getAccountUnit(account);
  const toUnit = toCurrency?.units[0];

  const hideError =
    bridgePending ||
    (useAllAmount &&
      fromAmountError &&
      fromAmountError instanceof AmountRequired);

  const toValue = rate?.toAmount;

  return (
    <View>
      <View>
        <LText semiBold color="grey" style={styles.label}>
          <Trans i18nKey="transfer.swap.form.from" />
        </LText>
        <View style={styles.root}>
          <AccountSelect
            swap={swap}
            navigation={navigation}
            route={route}
            setFromAccount={setFromAccount}
            providers={providers}
            provider={provider}
          />
          <View style={styles.wrapper}>
            {fromUnit ? (
              <CurrencyInput
                editable={!useAllAmount}
                onChange={setFromAmount}
                unit={fromUnit}
                value={swap.from.amount}
                inputStyle={styles.inputText}
                hasError={!hideError && !!fromAmountError}
                dynamicFontRatio={0.3}
              />
            ) : (
              <LText semiBold color="grey" style={styles.inputText}>
                0
              </LText>
            )}
          </View>
        </View>
        <LText style={[styles.error]} color={"alert"} numberOfLines={2}>
          <TranslatedError
            error={(!hideError && fromAmountError) || undefined}
          />
        </LText>
      </View>
      <View style={[styles.divider, { backgroundColor: colors.fog }]} />
      <View>
        <LText semiBold color="grey" style={styles.label}>
          <Trans i18nKey="transfer.swap.form.to" />
        </LText>
        <View style={styles.root}>
          <CurrencyTargetSelect
            swap={swap}
            navigation={navigation}
            route={route}
            setToCurrency={setToCurrency}
            providers={providers}
            provider={provider}
          />
          <View style={styles.wrapper}>
            {ratesStatus === "loading" ? (
              <ActivityIndicator color={colors.grey} animating />
            ) : toUnit && toCurrency ? (
              <View>
                <LText
                  adjustsFontSizeToFit
                  semiBold
                  color="grey"
                  style={styles.inputText}
                >
                  <CurrencyUnitValue
                    unit={toUnit}
                    value={toValue ?? BigNumber(0)}
                  />
                </LText>
                <LText semiBold color="grey" style={styles.subText}>
                  <CounterValue
                    currency={toCurrency}
                    value={toValue ?? BigNumber(0)}
                  />
                </LText>
              </View>
            ) : (
              <LText semiBold color="grey" style={styles.inputText}>
                0
              </LText>
            )}
          </View>
        </View>
      </View>
    </View>
  );
}

const styles = StyleSheet.create({
  root: {
    flexDirection: "row",
    justifyContent: "space-between",
    alignItems: "center",
    height: 32,
    marginVertical: 10,
  },
  label: {
    fontSize: 12,
    lineHeight: 15,
  },
  divider: {
    width: "100%",
    height: 1,
    marginTop: 8,
    marginBottom: 16,
  },
  wrapper: {
    flexShrink: 0,
    minWidth: "50%",
    alignItems: "flex-end",
    justifyContent: "center",
    height: 32,
  },
  currency: {
    fontSize: 20,
  },
  active: {
    ...getFontStyle({ semiBold: true }),
    fontSize: 30,
  },
  error: {
    fontSize: 14,
    textAlign: "right",
  },
  inputText: {
    textAlign: "right",
    height: 32,
    padding: 0,
  },
  subText: { textAlign: "right", fontSize: 13 },
});


<|start_filename|>src/screens/Swap/FormSelection/SelectAccountScreen.js<|end_filename|>
/* @flow */

import React, { useCallback, useMemo } from "react";
import {
  View,
  StyleSheet,
  FlatList,
  SafeAreaView,
  TouchableOpacity,
} from "react-native";
import { useSelector } from "react-redux";
import { Trans } from "react-i18next";
import { useTheme } from "@react-navigation/native";

import {
  accountWithMandatoryTokens,
  flattenAccounts,
} from "@ledgerhq/live-common/lib/account/helpers";
import { getAccountCurrency } from "@ledgerhq/live-common/lib/account";

import type { SearchResult } from "../../../helpers/formatAccountSearchResults";
import { accountsSelector } from "../../../reducers/accounts";
import { TrackScreen } from "../../../analytics";
import LText from "../../../components/LText";
import FilteredSearchBar from "../../../components/FilteredSearchBar";
import AccountCard from "../../../components/AccountCard";
import KeyboardView from "../../../components/KeyboardView";
import { formatSearchResults } from "../../../helpers/formatAccountSearchResults";
import { NavigatorName, ScreenName } from "../../../const";

import type { SwapRouteParams } from "..";
import AddIcon from "../../../icons/Plus";
import { swapSelectableCurrenciesSelector } from "../../../reducers/settings";

const SEARCH_KEYS = ["name", "unit.code", "token.name", "token.ticker"];

type Props = {
  navigation: any,
  route: { params: SwapRouteParams },
};

export default function SelectAccount({ navigation, route }: Props) {
  const { colors } = useTheme();
  const { swap, target, selectedCurrency, setAccount, provider } = route.params;

  const unfilteredAccounts = useSelector(accountsSelector);
  const selectableCurrencies = useSelector(swapSelectableCurrenciesSelector);

  const accounts = useMemo(
    () =>
      unfilteredAccounts.filter(
        acc => acc && selectableCurrencies.includes(getAccountCurrency(acc).id),
      ),
    [selectableCurrencies, unfilteredAccounts],
  );

  const enhancedAccounts = useMemo(() => {
    if (!selectedCurrency)
      return accounts.map(acc => accountWithMandatoryTokens(acc, []));

    const filteredAccounts = accounts.filter(
      acc =>
        acc.currency.id ===
        (selectedCurrency.type === "TokenCurrency"
          ? selectedCurrency.parentCurrency.id
          : selectedCurrency.id),
    );
    if (selectedCurrency.type === "TokenCurrency") {
      return filteredAccounts.map(acc =>
        accountWithMandatoryTokens(acc, [selectedCurrency]),
      );
    }
    return filteredAccounts;
  }, [accounts, selectedCurrency]);

  const allAccounts = selectedCurrency
    ? flattenAccounts(enhancedAccounts).filter(
        acc =>
          (acc.type === "TokenAccount" ? acc.token : acc.currency).id ===
          selectedCurrency.id,
      )
    : flattenAccounts(enhancedAccounts);

  const keyExtractor = item => item.account.id;
  const isFrom = target === "from";

  const renderItem = useCallback(
    ({ item: result }: { item: SearchResult }) => {
      const { account } = result;
      return (
        <View
          style={
            account.type === "Account"
              ? undefined
              : { ...styles.tokenCardStyle, borderLeftColor: colors.fog }
          }
        >
          <AccountCard
            disabled={!result.match}
            account={account}
            style={styles.card}
            onPress={() => {
              setAccount && setAccount(account);
              navigation.navigate(ScreenName.SwapForm, {
                ...route.params,
                transaction: undefined, // reset transaction after switching source account
                swap: {
                  ...route.params.swap,
                  from: {
                    ...(route.params.swap?.from || {}),
                    account,
                    parentAccount: null,
                  },
                },
              });
            }}
          />
        </View>
      );
    },
    [colors.fog, setAccount, navigation, route.params],
  );

  const elligibleAccountsForSelectedCurrency = allAccounts.filter(account =>
    isFrom ? account.balance.gt(0) : swap.from?.account?.id !== account.id,
  );

  const onAddAccount = useCallback(() => {
    navigation.navigate(NavigatorName.AddAccounts, {
      screen: ScreenName.AddAccountsSelectCrypto,
      params: {
        returnToSwap: true,
        filterCurrencyIds: selectableCurrencies,
        onSuccess: () => {
          navigation.navigate(ScreenName.SwapV2FormSelectAccount, route.params);
        },
        analyticsPropertyFlow: "swap",
      },
    });
  }, [navigation, route.params, selectableCurrencies]);

  const renderList = useCallback(
    items => {
      // $FlowFixMe
      const formatedList = formatSearchResults(items, enhancedAccounts);
      return (
        <FlatList
          data={formatedList}
          renderItem={renderItem}
          keyExtractor={keyExtractor}
          showsVerticalScrollIndicator={false}
          keyboardDismissMode="on-drag"
          ListFooterComponent={() => (
            <TouchableOpacity
              style={styles.buttonContainer}
              onPress={onAddAccount}
            >
              <View
                style={[
                  styles.iconContainer,
                  { backgroundColor: colors.lightLive },
                ]}
              >
                <AddIcon size={14} color={colors.live} />
              </View>
              <LText color="live" semiBold style={styles.label}>
                <Trans i18nKey="transfer.swap.emptyState.CTAButton" />
              </LText>
            </TouchableOpacity>
          )}
        />
      );
    },
    [colors.lightLive, colors.live, enhancedAccounts, onAddAccount, renderItem],
  );

  return (
    <SafeAreaView style={[styles.root, { backgroundColor: colors.background }]}>
      <TrackScreen
        category="Swap Form"
        name={`Edit ${isFrom ? "Source" : "Target"} Account`}
        provider={provider}
      />
      <KeyboardView style={styles.root}>
        <View style={styles.searchContainer}>
          <FilteredSearchBar
            keys={SEARCH_KEYS}
            inputWrapperStyle={[styles.card, styles.searchBarContainer]}
            list={elligibleAccountsForSelectedCurrency}
            renderList={renderList}
            renderEmptySearch={() => (
              <View style={styles.emptyResults}>
                <LText style={styles.label} color="fog">
                  <Trans i18nKey="transfer.receive.noAccount" />
                </LText>
              </View>
            )}
          />
        </View>
      </KeyboardView>
    </SafeAreaView>
  );
}

const styles = StyleSheet.create({
  addAccountButton: {
    flex: 1,
    flexDirection: "row",
    paddingVertical: 16,
    alignItems: "center",
  },
  root: {
    flex: 1,
  },
  tokenCardStyle: {
    marginLeft: 26,
    paddingLeft: 7,
    borderLeftWidth: 1,
  },
  card: {
    paddingHorizontal: 16,
    backgroundColor: "transparent",
  },
  searchContainer: {
    paddingTop: 18,
    flex: 1,
  },
  searchBarContainer: {
    paddingBottom: 8,
  },
  list: {
    paddingTop: 8,
  },
  emptyResults: {
    flex: 1,
    flexDirection: "column",
    alignItems: "center",
    justifyContent: "center",
  },
  label: {
    fontSize: 16,
    lineHeight: 19,
  },
  button: {
    flex: 1,
  },
  iconContainer: {
    borderRadius: 26,
    height: 26,
    width: 26,
    justifyContent: "center",
    alignItems: "center",
    marginRight: 12,
  },
  buttonContainer: {
    paddingTop: 24,
    paddingLeft: 16,
    paddingRight: 16,
    marginBottom: 24,
    flexDirection: "row",
    alignItems: "center",
  },
});


<|start_filename|>src/components/CounterValue.js<|end_filename|>
// @flow
import React, { useState, useCallback, useMemo, useEffect } from "react";
import { BigNumber } from "bignumber.js";
import { useSelector } from "react-redux";
import type { Currency } from "@ledgerhq/live-common/lib/types";
import {
  useCalculate,
  useCountervaluesPolling,
} from "@ledgerhq/live-common/lib/countervalues/react";
import { TouchableOpacity, StyleSheet } from "react-native";
import { Trans } from "react-i18next";
import { useTheme } from "@react-navigation/native";
import { counterValueCurrencySelector } from "../reducers/settings";
import {
  useTrackingPairs,
  addExtraSessionTrackingPair,
} from "../actions/general";
import CurrencyUnitValue from "./CurrencyUnitValue";
import LText from "./LText";
import Circle from "./Circle";
import IconHelp from "../icons/Info";
import BottomModal from "./BottomModal";

type Props = {
  // wich market to query
  currency: Currency,
  // when? if not given: take latest
  date?: Date,
  value: BigNumber | number,
  // display grey placeholder if no value
  withPlaceholder?: boolean,
  placeholderProps?: mixed,
  // as we can't render View inside Text, provide ability to pass
  // wrapper component from outside
  Wrapper?: React$ComponentType<*>,
  subMagnitude?: number,
  joinFragmentsSeparator?: string,
  alwaysShowValue?: boolean,
};

export const NoCountervaluePlaceholder = () => {
  const { colors } = useTheme();
  const [modalOpened, setModalOpened] = useState(false);
  const openModal = useCallback(() => setModalOpened(true), []);
  const closeModal = useCallback(() => setModalOpened(false), []);

  return (
    <TouchableOpacity style={styles.placeholderButton} onPress={openModal}>
      <LText style={styles.placeholderLabel}>-</LText>
      <BottomModal
        isOpened={modalOpened}
        onClose={closeModal}
        style={[styles.modal]}
      >
        <Circle bg={colors.lightLive} size={70}>
          <IconHelp size={30} color={colors.live} />
        </Circle>

        <LText style={styles.modalTitle} semiBold>
          <Trans i18nKey="errors.countervaluesUnavailable.title" />
        </LText>
      </BottomModal>
    </TouchableOpacity>
  );
};

export default function CounterValue({
  value: valueProp,
  date,
  withPlaceholder,
  placeholderProps,
  Wrapper,
  currency,
  ...props
}: Props) {
  const value =
    valueProp instanceof BigNumber ? valueProp.toNumber() : valueProp;
  const counterValueCurrency = useSelector(counterValueCurrencySelector);

  const trackingPairs = useTrackingPairs();
  const cvPolling = useCountervaluesPolling();
  const hasTrackingPair = useMemo(
    () =>
      trackingPairs.some(
        tp => tp.from === currency && tp.to === counterValueCurrency,
      ),
    [counterValueCurrency, currency, trackingPairs],
  );

  useEffect(() => {
    let t;
    if (!hasTrackingPair) {
      addExtraSessionTrackingPair({ from: currency, to: counterValueCurrency });
      t = setTimeout(cvPolling.poll, 2000); // poll after 2s to ensure debounced CV userSettings are effective after this update
    }

    return () => {
      if (t) clearTimeout(t);
    };
  }, [
    counterValueCurrency,
    currency,
    cvPolling,
    cvPolling.poll,
    hasTrackingPair,
    trackingPairs,
  ]);

  const countervalue = useCalculate({
    from: currency,
    to: counterValueCurrency,
    value,
    disableRounding: true,
    date,
  });

  if (typeof countervalue !== "number") {
    return withPlaceholder ? <NoCountervaluePlaceholder /> : null;
  }

  const inner = (
    <CurrencyUnitValue
      {...props}
      currency={currency}
      unit={counterValueCurrency.units[0]}
      value={countervalue}
    />
  );

  if (Wrapper) {
    return <Wrapper>{inner}</Wrapper>;
  }

  return inner;
}

const styles = StyleSheet.create({
  modal: {
    paddingHorizontal: 16,
    paddingTop: 24,
    alignItems: "center",
  },
  modalTitle: {
    marginVertical: 24,
    fontSize: 14,
    lineHeight: 21,
    textAlign: "center",
  },
  placeholderButton: {
    width: 24,
    height: 24,
    alignItems: "center",
    justifyContent: "center",
  },
  placeholderLabel: { fontSize: 16 },
});


<|start_filename|>scripts/v3clean.js<|end_filename|>
const { rm } = require("fs");

const exec = require("child_process").exec;

/* eslint-disable no-console */

async function executeAsync(command) {
  return new Promise((resolve, reject) => {
    exec(command, (error, stdout, stderr) => {
      if (error) {
        reject(error);
      } else if (stderr) {
        reject(stderr);
      } else {
        resolve(stdout);
      }
    });
  });
}

async function listTsJsFilesPairs() {
  const jsFilesPath = {};
  const jsTsPairs = [];
  await executeAsync('git ls-files | grep -e ".\\.js$"').then(res =>
    res.split("\n").map(path => (jsFilesPath[path] = true)),
  );
  return executeAsync('git ls-files | grep -e ".\\.ts$" -e ".\\.tsx$"')
    .then(res =>
      res.split("\n").forEach(path => {
        const beforeExtensionPath = path
          .split(".")
          .slice(0, -1)
          .join(".");
        const jsFilePath = `${beforeExtensionPath}.js`;
        if (jsFilesPath[jsFilePath]) {
          jsTsPairs.push({
            tsFilePath: path,
            jsFilePath,
          });
        }
      }),
    )
    .then(() => jsTsPairs);
}

async function removeJS() {
  const pairs = await listTsJsFilesPairs();

  pairs.forEach(({ jsFilePath }) => {
    rm(jsFilePath);
  });

  return true;
}

removeJS().then(() => console.log("JS files removed"));


<|start_filename|>src/snoopy.js<|end_filename|>
// @flow
/* eslint-disable no-unused-vars */

// The code here is only enabled in development and allows to investigate performance
// https://github.com/jondot/rn-snoopy

// core Snoopy
import Snoopy from "rn-snoopy";
import bars from "rn-snoopy/stream/bars";
import filter from "rn-snoopy/stream/filter";
import buffer from "rn-snoopy/stream/buffer";
import EventEmitter from "react-native/Libraries/vendor/emitter/EventEmitter";

const emitter = new EventEmitter();
const events = Snoopy.stream(emitter);

// Feel free to enable the thing you want to debug.
// Keep commented when you commit.
// Feel free to add more usecases.

// see all new views
// filter({ method: "createView" }, true)(events).subscribe();

// bars()(buffer()(events)).subscribe();


<|start_filename|>src/screens/Settings/General/Region.js<|end_filename|>
/* @flow */
import { connect } from "react-redux";
import upperFirst from "lodash/upperFirst";
import { setLocale } from "../../../actions/settings";
import { localeSelector } from "../../../reducers/settings";
import type { State } from "../../../reducers";
import makeGenericSelectScreen from "../../makeGenericSelectScreen";
import regionByKeys from "./regions.json";

const items = Object.keys(regionByKeys)
  .map(key => {
    const { languageDisplayName, regionDisplayName } = regionByKeys[key];
    const label = `${upperFirst(regionDisplayName)} (${upperFirst(
      languageDisplayName,
    )})`;
    return { value: key, label };
  })
  .sort((a, b) => a.label.localeCompare(b.label));

const mapStateToProps = (state: State) => {
  const selectedKey = localeSelector(state);
  return {
    selectedKey,
    initialSearchQuery:
      items.find(item => item.value === selectedKey)?.label ?? "",
    items,
  };
};

const mapDispatchToProps = {
  onValueChange: ({ value }: *) => setLocale(value),
};

const Screen = makeGenericSelectScreen({
  id: "RegionSettingsSelect",
  itemEventProperties: item => ({
    region: item.value?.split("-")[1] || item.value,
  }),
  keyExtractor: item => item.value,
  formatItem: item => item.label,
  searchable: true,
  searchKeys: ["label", "value"],
});

// $FlowFixMe
export default connect(mapStateToProps, mapDispatchToProps)(Screen);


<|start_filename|>src/families/crypto_org/index.js<|end_filename|>
// @flow
import * as CryptoOrgEditMemo from "./EditMemo";

export { CryptoOrgEditMemo };


<|start_filename|>src/screens/Swap/SwapEntry.js<|end_filename|>
// @flow

import React, { useState, useEffect } from "react";
import Config from "react-native-config";
import { StyleSheet, View } from "react-native";
import { useSelector, useDispatch } from "react-redux";
import SafeAreaView from "react-native-safe-area-view";
import { useTheme, useRoute, useNavigation } from "@react-navigation/native";
import { getProviders } from "@ledgerhq/live-common/lib/exchange/swap";
import { getSwapSelectableCurrencies } from "@ledgerhq/live-common/lib/exchange/swap/logic";
import NotAvailable from "./NotAvailable";
import { swapKYCSelector } from "../../reducers/settings";
import { setSwapSelectableCurrencies } from "../../actions/settings";
import { ScreenName } from "../../const";
import Spinning from "../../components/Spinning";
import BigSpinner from "../../icons/BigSpinner";

export const useProviders = () => {
  const dispatch = useDispatch();
  const [providers, setProviders] = useState();
  const [provider, setProvider] = useState();
  useEffect(() => {
    getProviders().then((providers: any) => {
      let resultProvider;
      const disabledProviders = Config.SWAP_DISABLED_PROVIDERS || "";
      const providersByName = providers.reduce((acc, providerData) => {
        if (!disabledProviders.includes(providerData.provider)) {
          acc[providerData.provider] = providerData;
        }
        return acc;
      }, {});
      // Prio to changelly if both are available
      if ("wyre" in providersByName && "changelly" in providersByName) {
        resultProvider = providersByName.changelly;
      } else {
        resultProvider = providers.find(
          p => !disabledProviders.includes(p.provider),
        );
      }
      // Only set as available currencies from this provider, on swp-agg this changes
      if (resultProvider) {
        dispatch(
          setSwapSelectableCurrencies(
            getSwapSelectableCurrencies([resultProvider]),
          ),
        );
        setProviders([resultProvider]);
        setProvider(resultProvider.provider);
      } else {
        setProviders([]);
      }
    });
  }, [dispatch]);
  return { providers, provider };
};

const SwapEntrypoint = () => {
  const { colors } = useTheme();
  const { replace } = useNavigation();
  const route = useRoute();
  const swapKYC = useSelector(swapKYCSelector);

  const { provider, providers } = useProviders();

  useEffect(() => {
    if (!providers?.length || !provider) return;
    if (provider === "wyre" && swapKYC?.wyre?.status !== "approved") {
      replace(ScreenName.SwapKYC, { provider });
    } else {
      replace(ScreenName.SwapFormOrHistory, {
        providers,
        provider,
        defaultAccount: route?.params?.defaultAccount,
        defaultParentAccount: route?.params?.defaultParentAccount,
      });
    }
  }, [replace, provider, providers, swapKYC, route?.params]);

  return (
    <SafeAreaView style={[styles.root, { backgroundColor: colors.background }]}>
      {!providers ? (
        <View style={styles.loading}>
          <Spinning clockwise>
            <BigSpinner />
          </Spinning>
        </View>
      ) : !provider ? (
        <NotAvailable />
      ) : null}
    </SafeAreaView>
  );
};

const styles = StyleSheet.create({
  root: {
    flex: 1,
  },
  loading: {
    flex: 1,
    alignItems: "center",
    justifyContent: "center",
  },
  selectDevice: {
    fontSize: 15,
    lineHeight: 20,
    marginBottom: 20,
  },
  debugText: {
    marginBottom: 10,
  },
});

export default SwapEntrypoint;


<|start_filename|>src/components/Scanner.js<|end_filename|>
/* @flow */
import React, { useState, useEffect } from "react";
import { StyleSheet, View } from "react-native";
import { useNavigation } from "@react-navigation/native";
import { Camera } from "expo-camera";
import { BarCodeScanner } from "expo-barcode-scanner";
import StyledStatusBar from "./StyledStatusBar";
import CameraScreen from "./CameraScreen";
import FallBackCamera from "../screens/ImportAccounts/FallBackCamera";
import getWindowDimensions from "../logic/getWindowDimensions";

type Props = {
  onResult: Function,
  liveQrCode?: boolean,
  progress?: number,
  instruction?: React$Node | string,
};

const Scanner = ({ onResult, liveQrCode, progress, instruction }: Props) => {
  const [hasPermission, setHasPermission] = useState(null);
  const { width, height } = getWindowDimensions();
  const navigation = useNavigation();

  useEffect(() => {
    (async () => {
      const { status } = await Camera.requestPermissionsAsync();
      setHasPermission(status === "granted");
    })();
  }, []);

  if (hasPermission === null) {
    return <View />;
  }
  if (hasPermission === false) {
    return <FallBackCamera navigation={navigation} />;
  }

  return (
    <View style={styles.container}>
      <StyledStatusBar barStyle="light-content" />
      <Camera
        style={styles.camera}
        type={Camera.Constants.Type.back}
        ratio="16:9"
        onBarCodeScanned={({ data }) => onResult(data)}
        barCodeScannerSettings={{
          barCodeTypes: [BarCodeScanner.Constants.BarCodeType.qr],
        }}
      >
        <CameraScreen
          liveQrCode={liveQrCode}
          progress={progress}
          width={width}
          height={height}
          instruction={instruction}
        />
      </Camera>
    </View>
  );
};

export default Scanner;

const styles = StyleSheet.create({
  container: {
    flex: 1,
  },
  camera: {
    flex: 1,
  },
});


<|start_filename|>src/icons/Planet.js<|end_filename|>
// @flow
import React from "react";
import Svg, { Path, G } from "react-native-svg";

type Props = {
  size: number,
  color: string,
};

export default function Planet({ size = 16, color }: Props) {
  return (
    <Svg width={size} height={size} viewBox="0 0 25 24" fill="none">
      <G>
        <Path
          d="M12.875 20C17.2933 20 20.875 16.4183 20.875 12C20.875 7.58172 17.2933 4 12.875 4C8.45672 4 4.875 7.58172 4.875 12C4.875 16.4183 8.45672 20 12.875 20Z"
          stroke={color}
          stroke-width="1.9"
          stroke-linecap="round"
          stroke-linejoin="round"
        />
        <Path
          d="M8.60927 12.0009C8.60927 9.64447 10.5195 7.73422 12.8759 7.73421M23.6757 5.76562C24.8236 7.75382 20.9189 12.1572 14.9544 15.6008C8.98981 19.0445 3.22407 20.2243 2.07617 18.2361"
          stroke={color}
          stroke-width="1.9"
          stroke-linejoin="round"
        />
        <Path
          d="M2.07623 18.2399C1.37623 17.0299 2.54623 14.9299 4.90623 12.6899M17.4662 5.44994C20.5762 4.51994 22.9762 4.55994 23.6762 5.75994"
          stroke={color}
          stroke-width="1.9"
          stroke-linecap="round"
          stroke-linejoin="round"
        />
      </G>
    </Svg>
  );
}


<|start_filename|>src/families/polkadot/NominateFlow/ValidatorItem.js<|end_filename|>
// @flow
import React, { memo, useCallback, useMemo } from "react";
import { View, StyleSheet } from "react-native";
import { Trans } from "react-i18next";
import { Polkadot as PolkadotIdenticon } from "@polkadot/reactnative-identicon/icons";

import type { PolkadotValidator } from "@ledgerhq/live-common/lib/families/polkadot/types";

import { useTheme } from "styled-components/native";
import CheckBox from "../../../components/CheckBox";
import LText from "../../../components/LText";
import Touchable from "../../../components/Touchable";

type Props = {
  item: PolkadotValidator,
  disabled: boolean,
  onSelect: (item: PolkadotValidator, selected: boolean) => void,
  onClick: (address: string) => void,
  selected: boolean,
};

function Item({ item, selected, disabled, onSelect, onClick }: Props) {
  const { colors } = useTheme();
  const {
    identity,
    address,
    commission,
    nominatorsCount,
    isOversubscribed,
    isElected,
  } = item;

  const onPress = useCallback(() => {
    onSelect(item, selected);
  }, [onSelect, item, selected]);

  const isDisabled = disabled && !selected;

  const formattedCommission = useMemo(
    () => (commission ? `${commission.multipliedBy(100).toNumber()} %` : "-"),
    [commission],
  );
  return (
    <View
      style={[
        styles.wrapper,
        isDisabled ? { backgroundColor: colors.neutral.c40 } : {},
      ]}
    >
      <Touchable
        style={[styles.iconWrapper]}
        onPress={() => onClick(address)}
        event="PolkadotNominateSelectValidatorsOpenExplorer"
      >
        <PolkadotIdenticon
          style={isDisabled ? { backgroundColor: colors.neutral.c40 } : {}}
          address={address}
          size={32}
        />
      </Touchable>

      <View style={styles.nameWrapper}>
        <Touchable
          onPress={() => onClick(address)}
          event="PolkadotNominateSelectValidatorsOpenExplorer"
        >
          <LText
            semiBold
            style={[styles.nameText]}
            color={isDisabled ? colors.neutral.c70 : colors.primary.c90}
            numberOfLines={1}
          >
            {identity || address || ""}
          </LText>
        </Touchable>

        {isElected ? (
          <LText
            style={[styles.valueLabel]}
            color={isOversubscribed ? colors.warning.c100 : colors.neutral.c70}
          >
            {isOversubscribed ? (
              <Trans
                i18nKey="polkadot.nomination.oversubscribed"
                values={{ nominatorsCount }}
              />
            ) : (
              <Trans
                i18nKey="polkadot.nomination.nominatorsCount"
                values={{ nominatorsCount }}
              />
            )}
          </LText>
        ) : (
          <LText style={styles.valueLabel} color={colors.neutral.c70}>
            <Trans i18nKey="polkadot.nomination.waiting" />
          </LText>
        )}
      </View>

      <View style={styles.valueWrapper}>
        <LText semiBold style={styles.valueText} color={colors.neutral.c100}>
          {formattedCommission}
        </LText>

        <LText style={styles.valueLabel} color={colors.neutral.c70}>
          <Trans i18nKey="polkadot.nomination.commission" />
        </LText>
      </View>

      <CheckBox onChange={onPress} isChecked={selected} disabled={isDisabled} />
    </View>
  );
}

const styles = StyleSheet.create({
  wrapper: {
    flexDirection: "row",
    alignItems: "center",
    padding: 16,
  },
  iconWrapper: {
    height: 36,
    width: 36,
    alignItems: "center",
    justifyContent: "center",
    marginRight: 12,
  },
  nameWrapper: {
    flexDirection: "column",
    flex: 1,
    paddingRight: 16,
  },
  nameText: {
    fontSize: 15,
  },
  disabledWrapper: {
    opacity: 0.5,
  },
  valueWrapper: {
    alignItems: "flex-end",
    marginRight: 8,
  },
  valueText: {
    fontSize: 14,
  },
  valueLabel: {
    fontSize: 13,
  },
});

export default memo<Props>(Item);


<|start_filename|>src/components/CounterValuesProvider.js<|end_filename|>
// @flow
import React, { useState, useEffect, useRef } from "react";
import { AppState } from "react-native";
import { useNetInfo } from "@react-native-community/netinfo";
import {
  Countervalues,
  useCountervaluesPolling,
} from "@ledgerhq/live-common/lib/countervalues/react";
import { useUserSettings } from "../actions/general";

export default function CountervaluesProvider({
  children,
  initialState,
}: {
  children: React$Node,
  initialState: *,
}) {
  const userSettings = useUserSettings();

  return (
    <Countervalues userSettings={userSettings} savedState={initialState}>
      <CountervaluesManager>{children}</CountervaluesManager>
    </Countervalues>
  );
}

function CountervaluesManager({ children }: { children: React$Node }) {
  usePollingManager();

  return children;
}

function usePollingManager() {
  const { start, stop } = useCountervaluesPolling();
  const appState = useRef(AppState.currentState ?? "");
  const { isInternetReachable } = useNetInfo();
  const [isActive, setIsActive] = useState(!!appState.current);

  useEffect(() => {
    function handleChange(nextAppState) {
      setIsActive(
        appState.current.match(/inactive|background/) &&
          nextAppState === "active",
      );

      appState.current = nextAppState;
    }

    const sub = AppState.addEventListener("change", handleChange);

    return () => {
      sub.remove();
    };
  }, []);

  useEffect(() => {
    if (!isInternetReachable || !isActive) {
      stop();
      return;
    }
    start();
  }, [isInternetReachable, isActive, start, stop]);
}


<|start_filename|>src/components/RootNavigator/PasswordModifyFlowNavigator.js<|end_filename|>
// @flow
import React, { useMemo } from "react";
import { createStackNavigator } from "@react-navigation/stack";
import { useTranslation } from "react-i18next";
import { useTheme } from "styled-components/native";
import { ScreenName } from "../../const";
import PasswordRemove from "../../screens/Settings/General/PasswordRemove";
import { getStackNavigatorConfig } from "../../navigation/navigatorConfig";

export default function PasswordModifyFlowNavigator() {
  const { t } = useTranslation();
  const { colors } = useTheme();
  const stackNavigationConfig = useMemo(
    () => getStackNavigatorConfig(colors, true),
    [colors],
  );
  return (
    <Stack.Navigator screenOptions={stackNavigationConfig}>
      <Stack.Screen
        name={ScreenName.PasswordRemove}
        component={PasswordRemove}
        options={{
          title: t("auth.confirmPassword.title"),
        }}
      />
    </Stack.Navigator>
  );
}

const Stack = createStackNavigator();


<|start_filename|>src/screens/Lending/EnableFlow/01-SelectAccount.js<|end_filename|>
/* @flow */
import invariant from "invariant";
import { BigNumber } from "bignumber.js";
import React, { useCallback, useState, useEffect } from "react";
import { View, StyleSheet, FlatList, SafeAreaView } from "react-native";
import { useSelector } from "react-redux";
import { Trans } from "react-i18next";
import type {
  Account,
  AccountLike,
  CryptoCurrency,
  TokenCurrency,
} from "@ledgerhq/live-common/lib/types";

import { isAccountEmpty } from "@ledgerhq/live-common/lib/account";
import { formatCurrencyUnit } from "@ledgerhq/live-common/lib/currencies";
import { getAccountCapabilities } from "@ledgerhq/live-common/lib/compound/logic";
import {
  getAccountName,
  getAccountCurrency,
  getAccountUnit,
} from "@ledgerhq/live-common/lib/account/helpers";
import { useTheme } from "@react-navigation/native";
import { subAccountByCurrencyOrderedScreenSelector } from "../../../reducers/accounts";
import { rgba } from "../../../colors";
import { ScreenName, NavigatorName } from "../../../const";
import { TrackScreen } from "../../../analytics";
import LText from "../../../components/LText";
import FilteredSearchBar from "../../../components/FilteredSearchBar";
import KeyboardView from "../../../components/KeyboardView";
import Alert from "../../../components/Alert";
import LendingWarnings from "../shared/LendingWarnings";
import Card from "../../../components/Card";
import CurrencyIcon from "../../../components/CurrencyIcon";
import CheckCircle from "../../../icons/CheckCircle";
import CurrencyUnitValue from "../../../components/CurrencyUnitValue";
import ConfirmationModal from "../../../components/ConfirmationModal";
import Circle from "../../../components/Circle";
import Info from "../../../icons/Info";
import { urls } from "../../../config/urls";
import { localeSelector } from "../../../reducers/settings";

const SEARCH_KEYS = [
  "account.name",
  "account.unit.code",
  "account.token.name",
  "account.token.ticker",
];

type Props = {
  navigation: any,
  route: { params?: { currency: CryptoCurrency | TokenCurrency } },
};

const keyExtractor = item => item.account.id;

function LendingEnableSelectAccount({ route, navigation }: Props) {
  const { colors } = useTheme();
  const locale = useSelector(localeSelector);
  const currency = route?.params?.currency;
  invariant(currency, "currency required");

  let enabledTotalAmount = null;
  const accounts = useSelector(
    subAccountByCurrencyOrderedScreenSelector(route),
  );
  const filteredAccounts = accounts.filter(
    ({ account }) =>
      account.type === "TokenAccount" && !isAccountEmpty(account),
  );

  useEffect(() => {
    if (!filteredAccounts.length) {
      const n = navigation.getParent() || navigation;
      n.replace(NavigatorName.AddAccounts, {
        screen: ScreenName.AddAccountsTokenCurrencyDisclaimer,
        params: { token: currency },
      });
    }
  }, [currency, filteredAccounts, navigation]);

  let accountsWithUnlimitedEnabledAmount = 0;
  filteredAccounts.forEach(({ account }) => {
    const { enabledAmount, enabledAmountIsUnlimited } =
      (account.type === "TokenAccount" && getAccountCapabilities(account)) ||
      {};
    if (enabledAmountIsUnlimited) {
      enabledTotalAmount = Infinity;
      accountsWithUnlimitedEnabledAmount += 1;
      return;
    }
    // no need to calculate enabledTotalAmount
    if (
      enabledAmount &&
      enabledAmount.gt(0) &&
      !accountsWithUnlimitedEnabledAmount
    ) {
      enabledTotalAmount = BigNumber(enabledTotalAmount || 0).plus(
        enabledAmount,
      );
    }
  });

  const formattedEnabledAmount =
    enabledTotalAmount instanceof BigNumber &&
    formatCurrencyUnit(currency.units[0], enabledTotalAmount, {
      showCode: true,
      disableRounding: false,
      locale,
    });

  const [approveInfoModalOpen, setApproveInfoModalOpen] = useState(false);

  const closeApproveInfoModal = useCallback(
    () => setApproveInfoModalOpen(false),
    [],
  );
  const redirectToEnableFlow = useCallback(() => {
    const n = navigation.getParent() || navigation;
    n.push(ScreenName.LendingEnableAmount, { ...approveInfoModalOpen });
    closeApproveInfoModal();
  }, [approveInfoModalOpen, closeApproveInfoModal, navigation]);

  const redirectToSupplyFlow = useCallback(
    params => {
      const n = navigation.getParent() || navigation;
      n.replace(NavigatorName.LendingSupplyFlow, {
        screen: ScreenName.LendingSupplyAmount,
        params,
      });
    },
    [navigation],
  );

  const renderItem = useCallback(
    ({
      item: result,
    }: {
      item: { account: AccountLike, parentAccount?: Account },
    }) => {
      const { account, parentAccount } = result;

      const currency = getAccountCurrency(account);
      const unit = getAccountUnit(account);
      const capabilities =
        account.type === "TokenAccount" && getAccountCapabilities(account);
      const name = getAccountName(parentAccount || account);
      const isEnabled =
        capabilities &&
        ((capabilities.enabledAmount && capabilities.enabledAmount.gt(0)) ||
          capabilities.enabledAmountIsUnlimited);

      return (
        <View
          style={
            account.type === "Account"
              ? undefined
              : [styles.tokenCardStyle, { borderLeftColor: colors.fog }]
          }
        >
          <Card
            onPress={() => {
              isEnabled
                ? redirectToSupplyFlow({
                    accountId: account.id,
                    parentId:
                      account.type !== "Account"
                        ? account.parentId
                        : parentAccount?.id,
                    currency,
                  })
                : setApproveInfoModalOpen({
                    accountId: account.id,
                    parentId:
                      account.type !== "Account"
                        ? account.parentId
                        : parentAccount?.id,
                    currency,
                  });
            }}
            style={[styles.card, styles.cardStyle]}
          >
            <CurrencyIcon size={20} currency={currency} />
            <View style={styles.accountName}>
              <LText
                semiBold
                numberOfLines={1}
                style={[styles.accountNameText, { color: colors.darkBlue }]}
              >
                {name}
              </LText>
            </View>
            <View style={styles.balanceContainer}>
              <LText semiBold style={styles.balanceNumText} color="grey">
                <CurrencyUnitValue
                  showCode
                  unit={unit}
                  value={account.balance}
                />
              </LText>
              {isEnabled && <CheckCircle size={16} color={colors.success} />}
            </View>
          </Card>
        </View>
      );
    },
    [colors.darkBlue, colors.fog, colors.success, redirectToSupplyFlow],
  );
  const renderList = useCallback(
    items => (
      <FlatList
        data={items}
        renderItem={renderItem}
        keyExtractor={keyExtractor}
        showsVerticalScrollIndicator={false}
        keyboardDismissMode="on-drag"
        contentContainerStyle={styles.list}
      />
    ),
    [renderItem],
  );

  const renderEmptySearch = useCallback(
    () => (
      <View style={styles.emptyResults}>
        <LText style={styles.emptyText} color="fog">
          <Trans i18nKey="transfer.receive.noAccount" />
        </LText>
      </View>
    ),
    [],
  );
  return (
    <SafeAreaView style={[styles.root, { backgroundColor: colors.background }]}>
      <TrackScreen
        category="Lend Approve"
        name="Select Account"
        eventProperties={{ currencyName: currency.name }}
      />
      <LendingWarnings />
      <ConfirmationModal
        isOpened={!!approveInfoModalOpen}
        onClose={closeApproveInfoModal}
        onConfirm={redirectToEnableFlow}
        confirmationTitle={
          <Trans i18nKey="transfer.lending.enable.info.title" />
        }
        confirmationDesc={
          <Trans i18nKey="transfer.lending.enable.info.description" />
        }
        confirmButtonText={<Trans i18nKey="transfer.lending.enable.info.cta" />}
        Icon={() => (
          <Circle size={56} bg={rgba(colors.live, 0.2)}>
            <Info size={24} color={colors.live} />
          </Circle>
        )}
        hideRejectButton
      />
      <KeyboardView style={{ flex: 1 }}>
        <View style={styles.searchContainer}>
          <FilteredSearchBar
            list={filteredAccounts}
            inputWrapperStyle={styles.padding}
            renderList={renderList}
            renderEmptySearch={renderEmptySearch}
            keys={SEARCH_KEYS}
          />
        </View>
        <View style={styles.infoSection}>
          <Alert
            type="primary"
            learnMoreUrl={!enabledTotalAmount ? urls.compound : undefined}
          >
            {enabledTotalAmount ? (
              <Trans
                i18nKey={
                  enabledTotalAmount < Infinity
                    ? "transfer.lending.enable.selectAccount.enabledAccountsAmount"
                    : "transfer.lending.enable.selectAccount.enabledAccountsNoLimit"
                }
                values={{
                  number:
                    enabledTotalAmount < Infinity
                      ? filteredAccounts.length
                      : accountsWithUnlimitedEnabledAmount,
                  amount: formattedEnabledAmount,
                  currency: currency.name,
                }}
                count={
                  enabledTotalAmount < Infinity
                    ? filteredAccounts.length
                    : accountsWithUnlimitedEnabledAmount
                }
              />
            ) : (
              <Trans i18nKey="transfer.lending.enable.selectAccount.noEnabledAccounts" />
            )}
          </Alert>
        </View>
      </KeyboardView>
    </SafeAreaView>
  );
}

const styles = StyleSheet.create({
  root: {
    flex: 1,
  },
  tokenCardStyle: {
    marginLeft: 26,
    paddingLeft: 7,
    borderLeftWidth: 1,
  },
  searchContainer: {
    paddingTop: 16,
    flex: 1,
  },
  list: {
    paddingBottom: 32,
  },
  emptyResults: {
    flex: 1,
    flexDirection: "column",
    alignItems: "center",
    justifyContent: "center",
  },
  emptyText: {
    fontSize: 16,
  },
  padding: {
    paddingHorizontal: 16,
  },
  cardStyle: {
    paddingHorizontal: 16,
    backgroundColor: "transparent",
  },
  infoSection: { padding: 16 },
  card: {
    flex: 1,
    flexDirection: "row",
    paddingVertical: 16,
    alignItems: "center",
  },
  accountName: {
    flexGrow: 1,
    flexShrink: 1,
    marginLeft: 8,
  },
  accountNameText: {
    fontSize: 14,
  },
  balanceContainer: {
    flexDirection: "row",
    marginLeft: 16,
    alignItems: "center",
    justifyContent: "flex-end",
  },
  balanceNumText: {
    marginRight: 6,
  },
});

export default LendingEnableSelectAccount;


<|start_filename|>src/icons/providers/MoonPay.js<|end_filename|>
// @flow

import React from "react";
import Svg, { Path } from "react-native-svg";

const MoonPay = ({ size }: { size: number }) => (
  <Svg
    viewBox="0 0 48 48"
    width={size}
    height={size}
    fill="none"
    xmlns="http://www.w3.org/2000/svg"
  >
    <Path
      d="M0.5 10C0.5 4.47715 4.97715 0 10.5 0H38.5C44.0229 0 48.5 4.47715 48.5 10V38C48.5 43.5229 44.0228 48 38.5 48H10.5C4.97715 48 0.5 43.5228 0.5 38V10Z"
      fill="white"
    />
    <Path
      d="M10.1035 26.0021C10.1343 25.8835 10.1584 25.7632 10.1756 25.6419C10.5736 20.8739 13.9471 16.8642 18.5921 15.7799C22.3924 14.8936 25.7965 15.7799 28.698 18.4044C30.5335 20.052 31.7426 22.2839 32.1201 24.7214C32.5098 27.0212 32.1653 29.3853 31.1356 31.4782C30.1059 33.5711 28.4433 35.2866 26.3836 36.3811C25.187 37.0214 23.8797 37.4285 22.5311 37.5807C22.351 37.6024 22.1709 37.6294 22.0016 37.6546H20.3986C20.3464 37.642 20.2959 37.6258 20.2455 37.6186C19.7664 37.5465 19.2801 37.5033 18.8046 37.3988C16.4978 36.8832 14.4161 35.6439 12.8632 33.8617C11.3102 32.0795 10.3673 29.8477 10.172 27.4918C10.1557 27.3703 10.1316 27.25 10.0999 27.1315L10.1035 26.0021Z"
      fill="#7D00FF"
    />
    <Path
      d="M35.2808 10.358L35.6411 10.4337C36.4754 10.5818 37.24 10.9945 37.8216 11.6109C38.4032 12.2273 38.7709 13.0145 38.8704 13.8561C38.9941 14.8646 38.7257 15.8821 38.1207 16.6984C37.5157 17.5147 36.6203 18.0673 35.6194 18.2422C34.617 18.4089 33.5889 18.1841 32.7475 17.6141C31.9062 17.0442 31.3159 16.1728 31.0987 15.18C30.9854 14.6708 30.9738 14.1442 31.0645 13.6305C31.1552 13.1168 31.3465 12.626 31.6273 12.1864C31.908 11.7468 32.2729 11.3669 32.7008 11.0686C33.1287 10.7703 33.6113 10.5594 34.1209 10.4481L34.6018 10.3454L35.2808 10.358Z"
      fill="#7D00FF"
    />
    <Path
      d="M10.5 1H38.5V-1H10.5V1ZM47.5 10V38H49.5V10H47.5ZM38.5 47H10.5V49H38.5V47ZM1.5 38V10H-0.5V38H1.5ZM10.5 47C5.52944 47 1.5 42.9706 1.5 38H-0.5C-0.5 44.0751 4.42487 49 10.5 49V47ZM47.5 38C47.5 42.9706 43.4706 47 38.5 47V49C44.5751 49 49.5 44.0751 49.5 38H47.5ZM38.5 1C43.4706 1 47.5 5.02944 47.5 10H49.5C49.5 3.92487 44.5751 -1 38.5 -1V1ZM10.5 -1C4.42487 -1 -0.5 3.92487 -0.5 10H1.5C1.5 5.02944 5.52944 1 10.5 1V-1Z"
      fill="black"
      fillOpacity="0.06"
    />
  </Svg>
);

export default MoonPay;


<|start_filename|>src/screens/Swap/FormSelection/GenericInputLink.js<|end_filename|>
// @flow
import { useTheme } from "@react-navigation/native";
import React, { useCallback, useState } from "react";
import { Trans } from "react-i18next";
import { View, TouchableOpacity, StyleSheet } from "react-native";

import LText from "../../../components/LText";
import InfoModal from "../../../components/InfoModal";
import InfoIcon from "../../../icons/Info";

type Props = {
  label: React$Node,
  tooltip?: React$Node,
  children: React$Node,
  onEdit?: () => void,
};

export default function GenericInputLink({
  label,
  tooltip,
  children,
  onEdit,
}: Props) {
  const { colors } = useTheme();

  const [tooltipOpen, setTooltipOpen] = useState();

  const openTooltip = useCallback(() => setTooltipOpen(true), []);
  const closeTooltip = useCallback(() => setTooltipOpen(false), []);

  return (
    <View style={styles.root}>
      <TouchableOpacity style={styles.label} onPress={openTooltip}>
        <LText color="grey" style={styles.tooltipText}>
          {label}
        </LText>
        {tooltip ? <InfoIcon size={12} color={colors.grey} /> : null}
      </TouchableOpacity>
      <View style={styles.value}>{children}</View>

      {onEdit ? (
        <TouchableOpacity onPress={onEdit} style={styles.editButton}>
          <LText color="live" semiBold style={styles.editText}>
            <Trans i18nKey="common.edit" />
          </LText>
        </TouchableOpacity>
      ) : null}

      {tooltip ? (
        <InfoModal
          desc={tooltip}
          onClose={closeTooltip}
          isOpened={!!tooltipOpen}
        />
      ) : null}
    </View>
  );
}

const styles = StyleSheet.create({
  root: {
    width: "100%",
    flexDirection: "row",
    justifyContent: "space-between",
    alignItems: "center",
    marginVertical: 10,
  },
  label: {
    flex: 1,
    flexDirection: "row",
    justifyContent: "flex-start",
    alignItems: "center",
  },
  tooltipText: { fontSize: 14, lineHeight: 20, marginRight: 4 },
  value: {
    flexDirection: "row",
    justifyContent: "flex-end",
    alignItems: "center",
  },
  editText: { fontSize: 12, margin: 0 },
  editButton: { paddingLeft: 8, paddingVertical: 4 },
});


<|start_filename|>src/components/RootNavigator/MigrateAccountsFlowNavigator.js<|end_filename|>
// @flow
import React, { useMemo } from "react";
import { Platform } from "react-native";
import { createStackNavigator } from "@react-navigation/stack";
import { Trans } from "react-i18next";
import { useTheme } from "styled-components/native";
import { ScreenName } from "../../const";
import MigrateAccountsOverview from "../../screens/MigrateAccounts/01-Overview";
import MigrateAccountsConnectDevice from "../../screens/MigrateAccounts/02-ConnectDevice";
import MigrateAccountsProgress from "../../screens/MigrateAccounts/03-Progress";
import { getStackNavigatorConfig } from "../../navigation/navigatorConfig";
import StepHeader from "../StepHeader";

const totalSteps = "3";

export default function MigrateAccountsFlowNavigator() {
  const { colors } = useTheme();
  const stackNavigationConfig = useMemo(
    () => getStackNavigatorConfig(colors, true),
    [colors],
  );
  return (
    <Stack.Navigator
      screenOptions={{
        ...stackNavigationConfig,
        gestureEnabled: Platform.OS === "ios",
      }}
    >
      <Stack.Screen
        name={ScreenName.MigrateAccountsOverview}
        component={MigrateAccountsOverview}
        options={{
          headerTitle: () => (
            <StepHeader
              title={<Trans i18nKey="migrateAccounts.overview.headerTitle" />}
              subtitle={
                <Trans
                  i18nKey="send.stepperHeader.stepRange"
                  values={{
                    currentStep: "1",
                    totalSteps,
                  }}
                />
              }
            />
          ),
        }}
      />
      <Stack.Screen
        name={ScreenName.MigrateAccountsConnectDevice}
        component={MigrateAccountsConnectDevice}
        options={{
          headerTitle: () => (
            <StepHeader
              title={
                <Trans i18nKey="migrateAccounts.connectDevice.headerTitle" />
              }
              subtitle={
                <Trans
                  i18nKey="send.stepperHeader.stepRange"
                  values={{
                    currentStep: "2",
                    totalSteps,
                  }}
                />
              }
            />
          ),
        }}
      />
      <Stack.Screen
        name={ScreenName.MigrateAccountsProgress}
        component={MigrateAccountsProgress}
        options={{
          headerTitle: () => (
            <StepHeader
              title={<Trans i18nKey="migrateAccounts.progress.headerTitle" />}
              subtitle={
                <Trans
                  i18nKey="send.stepperHeader.stepRange"
                  values={{
                    currentStep: "3",
                    totalSteps,
                  }}
                />
              }
            />
          ),
        }}
      />
    </Stack.Navigator>
  );
}

const Stack = createStackNavigator();


<|start_filename|>src/icons/QRcode.js<|end_filename|>
// @flow
import React from "react";
import Svg, { Path, G } from "react-native-svg";

type Props = {
  size: number,
};

export default function QRcode({ size = 16 }: Props) {
  return (
    <Svg viewBox="0 0 16 16" width={size} height={size}>
      <G fill="none" fillRule="nonzero">
        <Path stroke="#bdb3ff" d="M1 1h6v6H1zM1 9h6v6H1zM9 1h6v6H9z" />
        <Path fill="#bdb3ff" d="M3 3h2v2H3zM11 3h2v2h-2z" />
        <G fill="#bdb3ff">
          <Path d="M8.5 8.5h3v1h-3zM12.5 10.5h1v2h-1zM12.5 8.5h3v1h-3zM10.5 12.5h3v1h-3zM8.5 12.5h1v3h-1z" />
          <Path d="M8.5 8.5h1v3h-1zM10.5 10.5h1v1h-1zM10.5 14.5h1v1h-1zM12.5 14.5h3v1h-3zM14.5 8.5h1v5h-1z" />
        </G>
        <Path fill="#bdb3ff" d="M3 11h2v2H3z" />
      </G>
    </Svg>
  );
}


<|start_filename|>src/families/polkadot/SimpleOperationFlow/index.js<|end_filename|>
// @flow

import React, { useMemo } from "react";
import { useTranslation } from "react-i18next";
import { createStackNavigator } from "@react-navigation/stack";
import { useTheme } from "@react-navigation/native";
import { ScreenName } from "../../../const";
import { getStackNavigatorConfig } from "../../../navigation/navigatorConfig";
import StepHeader from "../../../components/StepHeader";

import Started from "./01-Started";
import SelectDevice from "../../../screens/SelectDevice";
import ConnectDevice from "../../../screens/ConnectDevice";
import ValidationSuccess from "./03-ValidationSuccess";
import ValidationError from "./03-ValidationError";

const totalSteps = "2";

function SimpleOperationFlow() {
  const { t } = useTranslation();
  const { colors } = useTheme();

  const stackNavigatorConfig = useMemo(
    () => getStackNavigatorConfig(colors, true),
    [colors],
  );

  return (
    <Stack.Navigator screenOptions={stackNavigatorConfig}>
      <Stack.Screen
        name={ScreenName.PolkadotSimpleOperationStarted}
        component={Started}
        options={({ route }) => ({
          title: route.params.mode
            ? t(`polkadot.simpleOperation.modes.${route.params.mode}.title`)
            : t("polkadot.simpleOperation.stepperHeader.info"),
          headerLeft: null,
        })}
      />
      <Stack.Screen
        name={ScreenName.PolkadotSimpleOperationSelectDevice}
        component={SelectDevice}
        options={{
          headerTitle: () => (
            <StepHeader
              title={t("polkadot.simpleOperation.stepperHeader.selectDevice")}
              subtitle={t("polkadot.simpleOperation.stepperHeader.stepRange", {
                currentStep: "1",
                totalSteps,
              })}
            />
          ),
        }}
      />
      <Stack.Screen
        name={ScreenName.PolkadotSimpleOperationConnectDevice}
        component={ConnectDevice}
        options={{
          headerTitle: () => (
            <StepHeader
              title={t("polkadot.simpleOperation.stepperHeader.connectDevice")}
              subtitle={t("polkadot.simpleOperation.stepperHeader.stepRange", {
                currentStep: "2",
                totalSteps,
              })}
            />
          ),
        }}
      />
      <Stack.Screen
        name={ScreenName.PolkadotSimpleOperationValidationSuccess}
        component={ValidationSuccess}
        options={{
          headerTitle: "",
          gestureEnabled: false,
          headerLeft: null,
          headerRight: null,
        }}
      />
      <Stack.Screen
        name={ScreenName.PolkadotSimpleOperationValidationError}
        component={ValidationError}
        options={{ headerShown: false }}
      />
    </Stack.Navigator>
  );
}

const options = {
  headerShown: false,
};

export { SimpleOperationFlow as component, options };

const Stack = createStackNavigator();


<|start_filename|>src/components/FormatDate.js<|end_filename|>
// @flow
import React from "react";
import { useSelector } from "react-redux";

import compareDate from "../logic/compareDate";
import { localeSelector } from "../reducers/settings";

type Props = {
  date: ?Date,
  withHoursMinutes?: boolean,
};

const defaultOptions = {
  year: "numeric",
  month: "numeric",
  day: "numeric",
};
const hoursAndMinutesOptions = {
  hour: "2-digit",
  minute: "2-digit",
};

function FormatDate({ date, withHoursMinutes = false }: Props) {
  const locale = useSelector(localeSelector);

  return date && date.getTime()
    ? new Intl.DateTimeFormat(locale, {
        ...defaultOptions,
        ...(withHoursMinutes ? hoursAndMinutesOptions : {}),
      }).format(date)
    : null;
}

function areEqual(prevProps: Props, nextProps: Props): boolean {
  return compareDate(prevProps.date, nextProps.date);
}

export default React.memo<Props>(FormatDate, areEqual);


<|start_filename|>src/screens/SelectDevice.js<|end_filename|>
// @flow
import React, { useCallback } from "react";
import { StyleSheet } from "react-native";
import { useDispatch as useReduxDispatch } from "react-redux";
import SafeAreaView from "react-native-safe-area-view";
import type { Device } from "@ledgerhq/live-common/lib/hw/actions/types";
import type {
  Transaction,
  TransactionStatus,
} from "@ledgerhq/live-common/lib/types";
import { useTheme } from "@react-navigation/native";
import { TrackScreen } from "../analytics";
import SelectDeviceComp from "../components/SelectDevice";
import NavigationScrollView from "../components/NavigationScrollView";
import { setLastConnectedDevice } from "../actions/settings";
import SkipSelectDevice from "./SkipSelectDevice";

const forceInset = { bottom: "always" };

type Props = {
  navigation: any,
  route: { params: RouteParams, name: string },
};

type RouteParams = {
  accountId: string,
  transaction: Transaction,
  status: TransactionStatus,
  appName?: string,
};

export default function SelectDevice({ navigation, route }: Props) {
  const { colors } = useTheme();
  const dispatchRedux = useReduxDispatch();
  const onNavigate = useCallback(
    device => {
      navigation.navigate(route.name.replace("SelectDevice", "ConnectDevice"), {
        ...route.params,
        device,
      });
    },
    [navigation, route.name, route.params],
  );
  const onSelect = useCallback(
    (device: Device) => {
      dispatchRedux(setLastConnectedDevice(device));
      onNavigate(device);
    },
    [dispatchRedux, onNavigate],
  );

  return (
    <SafeAreaView
      style={[styles.root, { backgroundColor: colors.background }]}
      forceInset={forceInset}
    >
      <NavigationScrollView
        style={styles.scroll}
        contentContainerStyle={styles.scrollContainer}
        keyboardShouldPersistTaps="handled"
      >
        <SkipSelectDevice route={route} onResult={onNavigate} />
        <TrackScreen
          category={route.name.replace("SelectDevice", "")}
          name="SelectDevice"
        />
        <SelectDeviceComp onSelect={onSelect} />
      </NavigationScrollView>
    </SafeAreaView>
  );
}

const styles = StyleSheet.create({
  root: {
    flex: 1,
  },
  scroll: {
    flex: 1,
  },
  scrollContainer: {
    padding: 16,
  },
});


<|start_filename|>src/navigation/styles.js<|end_filename|>
// @flow
import { StyleSheet, Platform } from "react-native";

let headerStyle = {};

if (Platform.OS === "ios") {
  headerStyle = {
    borderBottomWidth: 0,
  };
} else {
  headerStyle = {
    elevation: 0,
  };
}

function Styles() {
  return StyleSheet.create({
    header: {
      ...headerStyle,
      // $FlowFixMe
    },
    headerNoShadow: {
      ...headerStyle,
    },
    transparentHeader: {
      backgroundColor: "transparent",
      shadowOpacity: 0,
      elevation: 0,
    },
  });
}

export default Styles();


<|start_filename|>ios/ledgerlivemobile/RCTBluetoothHelperModule.h<|end_filename|>
//
//  RCTBluetoothHelperModule.h
//  ledgerlivemobile
//
//  Created by jelbaz on 12/07/2021.
//  Copyright © 2021 Ledger SAS. All rights reserved.
//

#ifndef RCTBluetoothHelperModule_h
#define RCTBluetoothHelperModule_h

#import <React/RCTBridgeModule.h>
#import <CoreBluetooth/CoreBluetooth.h>

@interface Delegate : NSObject<CBCentralManagerDelegate>
@property CBCentralManager *centralManager;
@property RCTPromiseResolveBlock resolve;
@property RCTPromiseRejectBlock reject;
- (instancetype)initWithPromise: (RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject;
@end

@interface RCTBluetoothHelperModule : NSObject <RCTBridgeModule>
@property(strong) Delegate * delegate;
@end

#endif /* RCTBluetoothHelperModule_h */


<|start_filename|>src/components/SelectFeesStrategy.js<|end_filename|>
/* @flow */
import React, { useState, useCallback } from "react";
import {
  View,
  StyleSheet,
  FlatList,
  TouchableOpacity,
  SafeAreaView,
} from "react-native";
import { useTranslation } from "react-i18next";
import {
  getMainAccount,
  getAccountUnit,
  getAccountCurrency,
} from "@ledgerhq/live-common/lib/account";
import { useTheme } from "@react-navigation/native";

import type {
  Account,
  AccountLike,
  Transaction,
} from "@ledgerhq/live-common/lib/types";
import LText from "./LText";
import SummaryRow from "../screens/SendFunds/SummaryRow";
import CheckBox from "./CheckBox";
import CounterValue from "./CounterValue";
import CurrencyUnitValue from "./CurrencyUnitValue";

import SectionSeparator from "./SectionSeparator";
import BottomModal from "./BottomModal";
import Info from "../icons/Info";
import NetworkFeeInfo from "./NetworkFeeInfo";

type Props = {
  strategies: any,
  account: AccountLike,
  parentAccount: ?Account,
  transaction: Transaction,
  onStrategySelect: Function,
  onCustomFeesPress: Function,
  forceUnitLabel?: *,
  disabledStrategies?: Array<string>,
};

const CVWrapper = ({ children }: { children: * }) => (
  <LText semiBold color="grey">
    {children}
  </LText>
);

export default function SelectFeesStrategy({
  strategies,
  account,
  parentAccount,
  transaction,
  onStrategySelect,
  onCustomFeesPress,
  forceUnitLabel,
  disabledStrategies,
}: Props) {
  const { t } = useTranslation();

  const { colors } = useTheme();
  const mainAccount = getMainAccount(account, parentAccount);
  const currency = getAccountCurrency(mainAccount);
  const unit = getAccountUnit(mainAccount);
  const { feesStrategy } = transaction;

  const [isNetworkFeeHelpOpened, setNetworkFeeHelpOpened] = useState(false);
  const toggleNetworkFeeHelpModal = useCallback(
    () => setNetworkFeeHelpOpened(!isNetworkFeeHelpOpened),
    [isNetworkFeeHelpOpened],
  );
  const closeNetworkFeeHelpModal = () => setNetworkFeeHelpOpened(false);

  const onPressStrategySelect = useCallback(
    (item: any) => {
      onStrategySelect({
        amount: item.amount,
        label: item.forceValueLabel ?? item.label,
        userGasLimit: item.userGasLimit,
      });
    },
    [onStrategySelect],
  );

  const renderItem = ({ item }) => (
    <TouchableOpacity
      onPress={() => onPressStrategySelect(item)}
      disabled={
        disabledStrategies ? disabledStrategies.includes(item.label) : false
      }
      style={[
        styles.feeButton,
        {
          borderColor:
            feesStrategy === item.label ? colors.live : colors.background,
          backgroundColor:
            feesStrategy === item.label ? colors.lightLive : colors.lightFog,
        },
      ]}
    >
      <View
        style={[
          styles.feeStrategyContainer,
          { opacity: disabledStrategies?.includes(item.label) ? 0.2 : 1 },
        ]}
      >
        <View style={styles.leftBox}>
          <CheckBox
            onChange={() => onPressStrategySelect(item)}
            style={styles.checkbox}
            isChecked={feesStrategy === item.label}
          />
          <LText semiBold style={styles.feeLabel}>
            {t(`fees.speed.${item.label}`)}
          </LText>
        </View>
        <View style={styles.feesAmountContainer}>
          <LText semiBold style={styles.feesAmount}>
            <CurrencyUnitValue
              showCode={!forceUnitLabel}
              unit={item.unit ?? unit}
              value={item.displayedAmount ?? item.amount}
            />
            {forceUnitLabel ? " " : null}
            {forceUnitLabel || null}
          </LText>
          {item.displayedAmount ? (
            <CounterValue
              currency={currency}
              showCode
              value={item.displayedAmount}
              alwaysShowSign={false}
              withPlaceholder
              Wrapper={CVWrapper}
            />
          ) : null}
        </View>
      </View>
    </TouchableOpacity>
  );

  return (
    <>
      <BottomModal
        id="NetworkFee"
        isOpened={isNetworkFeeHelpOpened}
        preventBackdropClick={false}
        onClose={closeNetworkFeeHelpModal}
      >
        <NetworkFeeInfo onClose={closeNetworkFeeHelpModal} />
      </BottomModal>

      <View>
        <SectionSeparator lineColor={colors.lightFog} />
        <SummaryRow
          onPress={toggleNetworkFeeHelpModal}
          title={t("send.summary.fees")}
          additionalInfo={
            <View>
              <Info size={12} color={colors.grey} />
            </View>
          }
        >
          {null}
        </SummaryRow>

        <SafeAreaView style={styles.strategiesContainer}>
          <FlatList
            data={strategies}
            renderItem={renderItem}
            keyExtractor={s => s.label}
            extraData={feesStrategy}
          />
        </SafeAreaView>
        <TouchableOpacity
          style={[
            styles.customizeFeesButton,
            { backgroundColor: colors.lightLive },
          ]}
          onPress={onCustomFeesPress}
        >
          <LText semiBold color="live">
            {t("send.summary.customizeFees")}
          </LText>
        </TouchableOpacity>
      </View>
    </>
  );
}

const styles = StyleSheet.create({
  strategiesContainer: {
    flex: 1,
  },
  leftBox: {
    flex: 1,
    flexDirection: "row",
    alignItems: "center",
  },
  feeStrategyContainer: {
    flex: 1,
    flexDirection: "row",
    justifyContent: "space-between",
    alignItems: "center",
  },
  feesAmountContainer: {
    alignItems: "flex-end",
  },
  feeButton: {
    width: "100%",
    borderRadius: 4,
    borderWidth: 1,
    marginVertical: 4,
    padding: 16,
    flexDirection: "row",
    alignItems: "center",
  },
  feeLabel: { fontSize: 16, textTransform: "capitalize", marginLeft: 10 },
  feesAmount: { fontSize: 15 },
  checkbox: {
    borderRadius: 24,
    width: 20,
    height: 20,
  },
  customizeFeesButton: {
    flex: 1,
    padding: 8,
    borderRadius: 4,
    justifyContent: "center",
    alignItems: "center",
    marginTop: 4,
    marginBottom: 16,
  },
});


<|start_filename|>src/components/AnalyticsConsole.js<|end_filename|>
// @flow
import React, { useState, useCallback, useEffect } from "react";
import { StyleSheet, View, Text } from "react-native";
import { map } from "rxjs/operators";
import useEnv from "@ledgerhq/live-common/lib/hooks/useEnv";
import { trackSubject } from "../analytics/segment";

let id = 0;

const AnalyticsConsole = () => {
  const [items, setItems] = useState([]);
  const filter = useCallback((curr, prev) => {
    if (!prev || !curr.properties) return curr;
    // We repeat a bunch of data over and over again, filter that out unless it changes.
    // Will only work for flat props.
    const keys = Object.keys(curr.properties).filter(
      k => curr?.properties[k] !== prev?.properties[k],
    );
    const properties = keys.reduce(
      (ret, key) => ({ ...ret, [key]: curr.properties[key] }),
      {},
    );
    return { ...curr, properties };
  }, []);

  const addItem = useCallback(item => {
    setItems(currentItems => [...currentItems.slice(-9), item]);
  }, []);

  useEffect(() => {
    trackSubject.pipe(map(item => ({ ...item, id: ++id }))).subscribe(addItem);
  }, [addItem]);
  const render = useEnv("ANALYTICS_CONSOLE");

  return render ? (
    <View style={styles.root} pointerEvents="none">
      {items.map((item, index) => {
        const filteredData = filter(item, items[index - 1]);
        return (
          <View style={styles.item} key={item.id}>
            <Text style={styles.eventName}>{filteredData.event}</Text>
            <Text>
              {filteredData.properties
                ? JSON.stringify(filteredData.properties)
                : null}
            </Text>
          </View>
        );
      })}
    </View>
  ) : null;
};

const styles = StyleSheet.create({
  root: {
    zIndex: 999,
    backgroundColor: "#fffc",
    color: "#000",
    flex: 1,
    position: "absolute",
    opacity: 0.4,
    top: 0,
    left: 0,
    flexDirection: "column-reverse",
  },
  item: {
    padding: 2,
  },
  eventName: {
    fontWeight: "bold",
  },
});

export default AnalyticsConsole;


<|start_filename|>src/components/RootNavigator/LendingSupplyFlowNavigator.js<|end_filename|>
// @flow
import React, { useMemo } from "react";
import { createStackNavigator } from "@react-navigation/stack";
import { useTranslation } from "react-i18next";
import { useTheme } from "styled-components/native";
import { ScreenName } from "../../const";
import LendingSupplyAmount from "../../screens/Lending/SupplyFlow/01-Amount";
import LendingSupplySummary from "../../screens/SendFunds/04-Summary";
import SelectDevice from "../../screens/SelectDevice";
import LendingSupplyConnectDevice from "../../screens/ConnectDevice";
import LendingSupplyValidationSuccess from "../../screens/Lending/SupplyFlow/03-ValidationSuccess";
import LendingSupplyValidationError from "../../screens/Lending/SupplyFlow/03-ValidationError";
import { getStackNavigatorConfig } from "../../navigation/navigatorConfig";
import StepHeader from "../StepHeader";

const totalSteps = "4";

export default function LendingSupplyFlowNavigator() {
  const { t } = useTranslation();
  const { colors } = useTheme();
  const stackNavigationConfig = useMemo(
    () => getStackNavigatorConfig(colors, true),
    [colors],
  );
  return (
    <Stack.Navigator screenOptions={stackNavigationConfig}>
      <Stack.Screen
        name={ScreenName.LendingSupplyAmount}
        component={LendingSupplyAmount}
        options={{
          headerTitle: () => (
            <StepHeader
              title={t("transfer.lending.supply.stepperHeader.amount")}
              subtitle={t("transfer.lending.supply.stepperHeader.stepRange", {
                currentStep: "1",
                totalSteps,
              })}
            />
          ),
        }}
      />
      <Stack.Screen
        name={ScreenName.LendingSupplySummary}
        component={LendingSupplySummary}
        options={{
          headerTitle: () => (
            <StepHeader
              title={t("transfer.lending.supply.stepperHeader.summary")}
              subtitle={t("transfer.lending.supply.stepperHeader.stepRange", {
                currentStep: "2",
                totalSteps,
              })}
            />
          ),
        }}
      />
      <Stack.Screen
        name={ScreenName.LendingSupplySelectDevice}
        component={SelectDevice}
        options={{
          headerTitle: () => (
            <StepHeader
              title={t("transfer.lending.supply.stepperHeader.selectDevice")}
              subtitle={t("transfer.lending.supply.stepperHeader.stepRange", {
                currentStep: "3",
                totalSteps,
              })}
            />
          ),
        }}
      />
      <Stack.Screen
        name={ScreenName.LendingSupplyConnectDevice}
        component={LendingSupplyConnectDevice}
        options={{
          headerTitle: () => (
            <StepHeader
              title={t("transfer.lending.supply.stepperHeader.connectDevice")}
              subtitle={t("transfer.lending.supply.stepperHeader.stepRange", {
                currentStep: "4",
                totalSteps,
              })}
            />
          ),
        }}
      />
      <Stack.Screen
        name={ScreenName.LendingSupplyValidationSuccess}
        component={LendingSupplyValidationSuccess}
        options={{
          headerLeft: null,
          headerShown: false,
          headerRight: null,
          gestureSupplyd: false,
        }}
      />
      <Stack.Screen
        name={ScreenName.LendingSupplyValidationError}
        component={LendingSupplyValidationError}
        options={{
          headerShown: false,
        }}
      />
    </Stack.Navigator>
  );
}

const Stack = createStackNavigator();


<|start_filename|>src/families/polkadot/UnbondFlow/01-Amount.js<|end_filename|>
/* @flow */
import invariant from "invariant";
import { BigNumber } from "bignumber.js";
import useBridgeTransaction from "@ledgerhq/live-common/lib/bridge/useBridgeTransaction";
import React, { useCallback, useState, useEffect } from "react";
import {
  View,
  StyleSheet,
  TouchableWithoutFeedback,
  Switch,
  Keyboard,
  SafeAreaView,
} from "react-native";
import { useSelector } from "react-redux";
import { Trans } from "react-i18next";
import { useTheme } from "@react-navigation/native";

import type { Transaction } from "@ledgerhq/live-common/lib/types";
import { useDebounce } from "@ledgerhq/live-common/lib/hooks/useDebounce";
import {
  getAccountUnit,
  getMainAccount,
} from "@ledgerhq/live-common/lib/account";
import { getAccountBridge } from "@ledgerhq/live-common/lib/bridge";

import { accountScreenSelector } from "../../../reducers/accounts";
import { ScreenName } from "../../../const";
import { TrackScreen } from "../../../analytics";
import LText from "../../../components/LText";
import CurrencyUnitValue from "../../../components/CurrencyUnitValue";
import Button from "../../../components/Button";
import KeyboardView from "../../../components/KeyboardView";
import CurrencyInput from "../../../components/CurrencyInput";
import TranslatedError from "../../../components/TranslatedError";

import { getFirstStatusError, hasStatusError } from "../../helpers";
import FlowErrorBottomModal from "../components/FlowErrorBottomModal";
import SendRowsFee from "../SendRowsFee";

type Props = {
  navigation: any,
  route: { params: RouteParams },
};

type RouteParams = {
  accountId: string,
  transaction: Transaction,
};

export default function PolkadotUnbondAmount({ navigation, route }: Props) {
  const { colors } = useTheme();
  const { account, parentAccount } = useSelector(accountScreenSelector(route));
  invariant(account, "account is required");

  const bridge = getAccountBridge(account, parentAccount);
  const mainAccount = getMainAccount(account, parentAccount);

  const [maxSpendable, setMaxSpendable] = useState(null);

  const {
    transaction,
    setTransaction,
    status,
    bridgePending,
    bridgeError,
  } = useBridgeTransaction(() => {
    const t = bridge.createTransaction(mainAccount);

    const transaction = bridge.updateTransaction(t, {
      mode: "unbond",
    });

    return { account: mainAccount, transaction };
  });

  const debouncedTransaction = useDebounce(transaction, 500);

  useEffect(() => {
    if (!account) return;

    let cancelled = false;
    getAccountBridge(account, parentAccount)
      .estimateMaxSpendable({
        account,
        parentAccount,
        transaction: debouncedTransaction,
      })
      .then(estimate => {
        if (cancelled) return;

        setMaxSpendable(estimate);
      });

    // eslint-disable-next-line consistent-return
    return () => {
      cancelled = true;
    };
  }, [account, parentAccount, debouncedTransaction]);

  const onChange = useCallback(
    amount => {
      if (!amount.isNaN()) {
        setTransaction(bridge.updateTransaction(transaction, { amount }));
      }
    },
    [setTransaction, transaction, bridge],
  );

  const toggleUseAllAmount = useCallback(() => {
    const bridge = getAccountBridge(account, parentAccount);
    if (!transaction) return;

    setTransaction(
      bridge.updateTransaction(transaction, {
        amount: BigNumber(0),
        useAllAmount: !transaction.useAllAmount,
      }),
    );
  }, [setTransaction, account, parentAccount, transaction]);

  const onContinue = useCallback(() => {
    navigation.navigate(ScreenName.PolkadotUnbondSelectDevice, {
      accountId: account.id,
      transaction,
      status,
    });
  }, [account, navigation, transaction, status]);

  const blur = useCallback(() => Keyboard.dismiss(), []);

  if (!account || !transaction) return null;

  const { useAllAmount } = transaction;
  const { amount } = status;
  const unit = getAccountUnit(account);

  const error =
    amount.eq(0) || bridgePending
      ? null
      : getFirstStatusError(status, "errors");
  const warning = getFirstStatusError(status, "warnings");
  const hasErrors = hasStatusError(status);

  return (
    <>
      <TrackScreen category="UnbondFlow" name="Amount" />
      <SafeAreaView
        style={[styles.root, { backgroundColor: colors.background }]}
      >
        <KeyboardView style={styles.container}>
          <TouchableWithoutFeedback onPress={blur}>
            <View style={styles.root}>
              <View style={styles.wrapper}>
                <CurrencyInput
                  editable={!useAllAmount}
                  isActive
                  onChange={onChange}
                  unit={unit}
                  value={amount}
                  renderRight={
                    <LText
                      semiBold
                      style={[styles.currency]}
                      color={warning ? "orange" : error ? "alert" : "grey"}
                    >
                      {unit.code}
                    </LText>
                  }
                  style={styles.inputContainer}
                  inputStyle={[
                    styles.inputStyle,
                    warning && { color: colors.orange },
                    error && { color: colors.alert },
                  ]}
                  hasError={!!error}
                  hasWarning={!!warning}
                />
                <LText
                  style={[styles.fieldStatus]}
                  color={warning ? "orange" : error ? "alert" : "darkBlue"}
                  numberOfLines={2}
                >
                  <TranslatedError error={error || warning} />
                </LText>
              </View>
              <View style={styles.bottomWrapper}>
                <View style={styles.available}>
                  <View style={styles.availableLeft}>
                    <LText>
                      <Trans i18nKey="polkadot.unbond.steps.amount.availableLabel" />
                    </LText>
                    <LText semiBold>
                      {maxSpendable ? (
                        <CurrencyUnitValue
                          showCode
                          unit={unit}
                          value={maxSpendable}
                        />
                      ) : (
                        "-"
                      )}
                    </LText>
                  </View>
                  {typeof useAllAmount === "boolean" ? (
                    <View style={styles.availableRight}>
                      <LText style={styles.maxLabel}>
                        <Trans i18nKey="polkadot.unbond.steps.amount.maxLabel" />
                      </LText>
                      <Switch
                        style={styles.switch}
                        value={useAllAmount}
                        onValueChange={toggleUseAllAmount}
                      />
                    </View>
                  ) : null}
                </View>
                <SendRowsFee
                  account={account}
                  parentAccount={parentAccount}
                  transaction={transaction}
                />
                <View style={styles.continueWrapper}>
                  <Button
                    event="PolkadotUnbondAmountContinue"
                    type="primary"
                    title={
                      <Trans
                        i18nKey={
                          !bridgePending
                            ? "common.continue"
                            : "send.amount.loadingNetwork"
                        }
                      />
                    }
                    onPress={onContinue}
                    disabled={!!hasErrors || bridgePending}
                  />
                </View>
              </View>
            </View>
          </TouchableWithoutFeedback>
        </KeyboardView>
      </SafeAreaView>

      <FlowErrorBottomModal
        navigation={navigation}
        transaction={transaction}
        account={account}
        parentAccount={parentAccount}
        setTransaction={setTransaction}
        bridgeError={bridgeError}
      />
    </>
  );
}

const styles = StyleSheet.create({
  root: {
    flex: 1,
  },
  topContainer: { paddingHorizontal: 32, flexShrink: 1 },
  container: {
    flex: 1,
    paddingTop: 16,
    paddingHorizontal: 16,
    alignItems: "stretch",
    justifyContent: "flex-start",
  },
  available: {
    flexDirection: "row",
    display: "flex",
    flexGrow: 1,
  },
  availableRight: {
    alignItems: "center",
    justifyContent: "flex-end",
    flexDirection: "row",
  },
  availableLeft: {
    justifyContent: "center",
    flexGrow: 1,
  },
  maxLabel: {
    marginRight: 4,
  },
  bottomWrapper: {
    flexGrow: 0,
    alignItems: "stretch",
    justifyContent: "flex-end",
    flexShrink: 1,
  },
  continueWrapper: {
    alignSelf: "stretch",
    alignItems: "stretch",
    justifyContent: "flex-end",
    paddingBottom: 16,
  },
  wrapper: {
    flexGrow: 1,
    flexShrink: 1,
    flexDirection: "column",
    alignContent: "center",
    justifyContent: "center",
  },
  inputContainer: {
    flexBasis: 75,
  },
  inputStyle: {
    flex: 0,
    flexShrink: 1,
    textAlign: "center",
  },
  currency: {
    fontSize: 32,
  },
  fieldStatus: {
    fontSize: 14,
    textAlign: "center",
  },
  switch: {
    opacity: 0.99,
  },
});


<|start_filename|>src/screens/Exchange/CoinifyWidget.js<|end_filename|>
// @flow
import React, { useCallback, useEffect, useRef, useState } from "react";
import { WebView } from "react-native-webview";
import querystring from "querystring";
import { ActivityIndicator, Linking, StyleSheet, View } from "react-native";
import type { Account, AccountLike } from "@ledgerhq/live-common/lib/types";
import {
  getAccountCurrency,
  getMainAccount,
} from "@ledgerhq/live-common/lib/account/helpers";
import type { Device } from "@ledgerhq/live-common/lib/hw/actions/types";
import { createAction } from "@ledgerhq/live-common/lib/hw/actions/app";
import connectApp from "@ledgerhq/live-common/lib/hw/connectApp";
import { getAccountBridge } from "@ledgerhq/live-common/lib/bridge";
import { useTranslation } from "react-i18next";
import { useTheme } from "@react-navigation/native";
import DeviceAction from "../../components/DeviceAction";
import BottomModal from "../../components/BottomModal";
import { renderVerifyAddress } from "../../components/DeviceAction/rendering";
import { getConfig } from "./coinifyConfig";
import { track } from "../../analytics";
import { DevicePart } from "./DevicePart";
import SkipDeviceVerification from "./SkipDeviceVerification";

const action = createAction(connectApp);

type CoinifyWidgetConfig = {
  primaryColor?: string,
  partnerId: string,
  cryptoCurrencies?: string | null,
  address?: string | null,
  targetPage: string,
  confirmMessages?: boolean,
  transferOutMedia?: string,
  transferInMedia?: string,
  confirmMessages?: *,
};

const injectedCode = `
  var originalPostMessage = window.postMessage
  window.postMessage = e => window.ReactNativeWebView.postMessage(JSON.stringify(e))

  document.addEventListener("message", event => {
      originalPostMessage(JSON.parse(event.data), "*");
  });

  window.addEventListener("message", event => {
      originalPostMessage(JSON.parse(event.data), "*");
  });

  function getExternalLink(domNode) {
    let curNode = domNode;

    while (curNode) {
       if (curNode.tagName === "A" && curNode.target === "_blank")
          return curNode;
       else
          curNode = curNode.parentNode;
    }
    return null;
  }

  document.addEventListener('click', event => {
    const externalLink = getExternalLink(event.target)
    if (externalLink) {
      window.postMessage({
        type: "event",
        event: "misc.opened-external-link",
        context: {
          url: externalLink.href
        },
      });
      event.preventDefault();
    }
  }, false);
`;

type Props = {
  account?: AccountLike,
  parentAccount?: ?Account,
  mode: string,
  device?: Device,
  verifyAddress?: boolean,
  skipDevice?: Boolean,
};

let resolvePromise = null;
export default function CoinifyWidget({
  mode,
  account,
  parentAccount,
  device,
  skipDevice,
}: Props) {
  const tradeId = useRef(null);
  const { colors } = useTheme();
  const [requestingAction, setRequestingAction] = useState<
    "none" | "connect" | "verify",
  >("none");
  const [firstLoadDone, setFirstLoadDone] = useState(false);
  const [isOpen, setIsOpen] = useState(false);
  const webView = useRef();

  const currency = account ? getAccountCurrency(account) : null;
  const mainAccount = account ? getMainAccount(account, parentAccount) : null;

  const coinifyConfig = getConfig();

  const widgetConfig: CoinifyWidgetConfig = {
    fontColor: "#142533",
    primaryColor: colors.live,
    partnerId: coinifyConfig.partnerId,
    cryptoCurrencies: currency ? currency.ticker : null,
    address: mainAccount ? mainAccount.freshAddress : null,
    targetPage: mode,
  };

  useEffect(() => {
    if (mode === "buy" && account) {
      track("Coinify Start Buy Widget", {
        currencyName: getAccountCurrency(account).name,
      });
    }
    if (mode === "sell" && account) {
      track("Coinify Start Sell Widget", {
        currencyName: getAccountCurrency(account).name,
      });
    }
    if (mode === "trade-history") {
      track("Coinify Start History Widget");
    }
  }, [account, mode]);

  if (mode === "buy") {
    widgetConfig.transferOutMedia = "blockchain";
    widgetConfig.confirmMessages = true;
  }

  if (mode === "sell") {
    widgetConfig.transferInMedia = "blockchain";
    widgetConfig.confirmMessages = true;
  }

  if (mode === "trade-history") {
    widgetConfig.transferOutMedia = "";
    widgetConfig.transferInMedia = "";
  }

  const handleMessage = useCallback(
    message => {
      const { type, event, context } = JSON.parse(message.nativeEvent.data);

      if (type !== "event") {
        return;
      }
      switch (event) {
        case "misc.opened-external-link":
          if (Linking.canOpenURL(context.url)) {
            Linking.openURL(context.url);
          }
          break;
        case "trade.receive-account-changed":
          if (context.address === mainAccount?.freshAddress) {
            track("Coinify Confirm Buy Start", {
              currencyName: account && getAccountCurrency(account).name,
            });
            setRequestingAction("connect");
            setIsOpen(true);
          } else {
            // TODO this is a problem, it should not occur.
          }
          break;
        case "trade.trade-placed":
          track("Coinify Widget Event Trade Placed", {
            currencyName: account && getAccountCurrency(account).name,
          });
          break;
        case "trade.trade-prepared":
          if (mode === "sell" && currency) {
            setRequestingAction("connect");
            setIsOpen(true);
          }
          break;
        case "trade.trade-created":
          tradeId.current = context.id;
          if (mode === "sell") {
            if (resolvePromise) {
              resolvePromise(context);
              resolvePromise = null;
            }
          }
          if (mode === "buy") {
            webView?.current?.postMessage(
              JSON.stringify({
                type: "event",
                event: "trade.confirm-trade-created",
                context: {
                  confirmed: true,
                  tradeId: tradeId.current,
                },
              }),
            );
            track("Coinify Confirm Buy End", {
              currencyName: account && getAccountCurrency(account).name,
              medium: context?.transferIn?.medium,
            });
          }
          break;
        default:
          break;
      }
    },
    [currency, account, mainAccount, mode],
  );

  const setTransactionId = useCallback(
    txId =>
      new Promise(resolve => {
        resolvePromise = resolve;
        if (webView.current) {
          webView.current.postMessage(
            JSON.stringify({
              type: "event",
              event: "settings.partner-context-changed",
              context: {
                partnerContext: {
                  nonce: txId,
                },
              },
            }),
          );
          webView.current.postMessage(
            JSON.stringify({
              type: "event",
              event: "trade.confirm-trade-prepared",
              context: {
                confirmed: true,
              },
            }),
          );
        }
      }),
    [],
  );

  const settleTrade = useCallback(
    confirmed => {
      setIsOpen(false);
      setRequestingAction("none");
      if (account && webView.current) {
        if (mode === "buy") {
          webView.current.postMessage(
            JSON.stringify({
              type: "event",
              event: "trade.confirm-trade-prepared",
              context: {
                address: mainAccount?.freshAddress,
                confirmed,
              },
            }),
          );
        } else {
          webView.current.postMessage(
            JSON.stringify({
              type: "event",
              event: "trade.confirm-trade-created",
              context: {
                confirmed,
                transferInitiated: true,
                tradeId: tradeId.current,
              },
            }),
          );
          if (confirmed) {
            track("Coinify Confirm Sell End", {
              currencyName: getAccountCurrency(account).name,
            });
          }
        }
      }
    },
    [account, mode, mainAccount?.freshAddress],
  );

  const onResult = useCallback(() => {
    setRequestingAction("verify");
  }, []);

  const tokenCurrency =
    account && account.type === "TokenAccount" ? account.token : null;

  const url = `${coinifyConfig.url}?${querystring.stringify(widgetConfig)}`;

  return (
    <View style={[styles.root]}>
      <WebView
        // $FlowFixMe
        ref={webView}
        startInLoadingState={true}
        renderLoading={() =>
          !firstLoadDone ? (
            <View style={styles.center}>
              <ActivityIndicator size="large" />
            </View>
          ) : null
        }
        originWhitelist={["https://*"]}
        allowsInlineMediaPlayback
        source={{
          uri: url,
        }}
        onLoad={() => setFirstLoadDone(true)}
        injectedJavaScript={injectedCode}
        onMessage={handleMessage}
        mediaPlaybackRequiresUserAction={false}
        scalesPageToFitmediaPlaybackRequiresUserAction
        automaticallyAdjustContentInsets={false}
        scrollEnabled={true}
        style={styles.webview}
        androidHardwareAccelerationDisabled
      />
      <BottomModal id="DeviceActionModal" isOpened={isOpen}>
        <View style={styles.modalContainer}>
          {requestingAction === "connect" && mainAccount ? (
            mode === "buy" && device && !skipDevice ? (
              <DeviceAction
                action={action}
                device={device}
                request={{ account: mainAccount, tokenCurrency }}
                onResult={onResult}
                analyticsPropertyFlow="buy"
              />
            ) : mode === "buy" ? (
              <SkipDeviceVerification
                account={mainAccount}
                settleTrade={settleTrade}
              />
            ) : account ? (
              <DevicePart
                account={account}
                onResult={settleTrade}
                device={device}
                parentAccount={parentAccount}
                getCoinifyContext={setTransactionId}
              />
            ) : null
          ) : requestingAction === "verify" && mainAccount ? (
            <VerifyAddress
              account={mainAccount}
              device={device}
              onResult={settleTrade}
            />
          ) : null}
        </View>
      </BottomModal>
    </View>
  );
}

function VerifyAddress({
  account,
  device,
  onResult,
}: {
  account: Account,
  device: ?Device,
  onResult: (confirmed: boolean, error?: Error) => void,
}) {
  const { dark } = useTheme();
  const { t } = useTranslation();

  const onConfirmAddress = useCallback(async () => {
    try {
      if (!device) return;
      await getAccountBridge(account)
        .receive(account, {
          deviceId: device.deviceId,
          verify: true,
        })
        .toPromise();
      onResult(true);
    } catch (err) {
      onResult(false, err);
    }
  }, [account, device, onResult]);

  useEffect(() => {
    onConfirmAddress();
  }, [onConfirmAddress]);

  if (!device) return null;

  return renderVerifyAddress({
    t,
    currencyName: getAccountCurrency(account).name,
    device,
    address: account.freshAddress,
    theme: dark ? "dark" : "light",
  });
}

const styles = StyleSheet.create({
  root: {
    flex: 1,
  },
  center: {
    flex: 1,
    flexDirection: "column",
    alignItems: "center",
    position: "absolute",
    top: 0,
    left: 0,
    right: 0,
    bottom: 0,
    justifyContent: "center",
  },
  modalContainer: {
    flexDirection: "row",
  },
  webview: {
    flex: 0,
    width: "100%",
    height: "100%",
    /**
     * This is required to prevent a crash when navigating back.
     * The issue is caused by an incompatibility between the
     * react-native-webview and react-native-screens packages.
     * See: https://github.com/react-native-webview/react-native-webview/issues/1069
     * See: https://github.com/software-mansion/react-native-screens/issues/105
     */
    opacity: 0.99,
  },
});


<|start_filename|>src/components/CurrencyInput.js<|end_filename|>
// @flow
import React, { PureComponent } from "react";
import { StyleSheet, View, Dimensions } from "react-native";
import { connect } from "react-redux";
import { BigNumber } from "bignumber.js";

import {
  formatCurrencyUnit,
  sanitizeValueString,
} from "@ledgerhq/live-common/lib/currencies";
import noop from "lodash/noop";
import clamp from "lodash/clamp";

import type { Unit } from "@ledgerhq/live-common/lib/types";

import { localeSelector } from "../reducers/settings";
import getFontStyle from "./LText/getFontStyle";
import { withTheme } from "../colors";
import TextInput from "./FocusedTextInput";

function format(
  unit: Unit,
  value: BigNumber,
  { isFocused, showAllDigits, subMagnitude, locale },
) {
  return formatCurrencyUnit(unit, value, {
    locale,
    useGrouping: !isFocused,
    disableRounding: true,
    showAllDigits: !!showAllDigits && !isFocused,
    subMagnitude: value.isLessThan(1) ? subMagnitude : 0,
  });
}

type Props = {
  isActive: boolean,
  onFocus: boolean => void,
  onChange: BigNumber => void,
  unit: Unit,
  value: ?BigNumber,
  showAllDigits?: boolean,
  subMagnitude: number,
  allowZero: boolean,
  renderLeft?: any,
  renderRight?: any,
  hasError?: boolean,
  hasWarning?: boolean,
  autoFocus?: boolean,
  editable: boolean,
  placeholder?: string,
  style?: *,
  inputStyle?: *,
  colors: *,
  dynamicFontRatio?: number,
  locale: string,
};

type State = {
  isFocused: boolean,
  displayValue: string,
};

class CurrencyInput extends PureComponent<Props, State> {
  static defaultProps = {
    onFocus: noop,
    onChange: noop,
    value: null,
    showAllDigits: false,
    subMagnitude: 0,
    allowZero: false,
    isActive: false,
    hasError: false,
    hasWarning: false,
    autoFocus: false,
    editable: true,
    dynamicFontRatio: 0.75,
  };

  state = {
    isFocused: false,
    displayValue: "",
  };

  componentDidMount() {
    this.setDisplayValue();
  }

  componentDidUpdate(prevProps: Props) {
    const { value, showAllDigits, unit } = this.props;
    const needsToBeReformatted =
      !this.state.isFocused &&
      (value !== prevProps.value ||
        showAllDigits !== prevProps.showAllDigits ||
        unit !== prevProps.unit);

    if (needsToBeReformatted) {
      this.setDisplayValue();
    }
  }

  setDisplayValue = (isFocused: boolean = false) => {
    const {
      value,
      showAllDigits,
      unit,
      subMagnitude,
      allowZero,
      locale,
    } = this.props;
    this.setState({
      isFocused,
      displayValue:
        !value || (value.isZero() && !allowZero)
          ? ""
          : format(unit, value, {
              isFocused,
              showAllDigits,
              subMagnitude,
              locale,
            }),
    });
  };

  handleChange = (v: string) => {
    const { onChange, unit, value, locale } = this.props;
    const r = sanitizeValueString(unit, v, locale);
    const satoshiValue = BigNumber(r.value);

    if (!value || !value.isEqualTo(satoshiValue)) {
      onChange(satoshiValue);
    }
    this.setState({ displayValue: r.display });
  };

  handleBlur = () => {
    this.syncInput({ isFocused: false });
    this.props.onFocus(false);
  };

  handleFocus = () => {
    this.syncInput({ isFocused: true });
    this.props.onFocus(true);
  };

  syncInput = ({ isFocused }: { isFocused: boolean }) => {
    if (isFocused !== this.state.isFocused) {
      this.setDisplayValue(isFocused);
    }
  };

  render() {
    const {
      style,
      inputStyle,
      showAllDigits,
      unit,
      subMagnitude,
      isActive,
      renderLeft,
      renderRight,
      hasError,
      hasWarning,
      autoFocus,
      editable,
      placeholder,
      colors,
      dynamicFontRatio = 0.75,
      locale,
    } = this.props;
    const { displayValue } = this.state;

    // calculating an approximative font size
    const screenWidth = Dimensions.get("window").width * dynamicFontRatio;
    const dynamicFontSize = Math.round(
      clamp(
        Math.sqrt((screenWidth * 32) / displayValue.length),
        8,
        isActive ? 32 : 24,
      ),
    );

    return (
      <View style={[styles.wrapper, style]}>
        {renderLeft}
        <TextInput
          allowFontScaling={false}
          hitSlop={{ top: 20, bottom: 20 }}
          style={[
            styles.input,
            { color: colors.darkBlue },
            hasError
              ? { color: colors.alert }
              : hasWarning
              ? { color: colors.orange }
              : null,
            editable ? {} : { color: colors.grey },
            { fontSize: dynamicFontSize },
            inputStyle,
          ]}
          editable={editable}
          onChangeText={this.handleChange}
          autoCorrect={false}
          value={displayValue}
          autoFocus={autoFocus}
          onFocus={this.handleFocus}
          onBlur={this.handleBlur}
          placeholder={
            placeholder ||
            format(unit, BigNumber(0), {
              isFocused: false,
              showAllDigits,
              subMagnitude,
              locale,
            })
          }
          placeholderTextColor={editable ? colors.darkBlue : colors.grey}
          keyboardType="numeric"
          blurOnSubmit
        />
        {renderRight}
      </View>
    );
  }
}

const styles = StyleSheet.create({
  wrapper: {
    flexDirection: "row",
    alignItems: "center",
    justifyContent: "center",
  },
  input: {
    flex: 1,
    ...getFontStyle(),
    paddingRight: 8,
  },
});

const mapStateToProps = state => ({
  locale: localeSelector(state),
});

export default withTheme(
  connect(mapStateToProps, null, null, { forwardRef: true })(CurrencyInput),
);


<|start_filename|>src/screens/Swap/FormSelection/SelectFeesScreen.js<|end_filename|>
/* @flow */
import React, { useCallback } from "react";
import { StyleSheet, SafeAreaView } from "react-native";

import type {
  Account,
  AccountLikeArray,
} from "@ledgerhq/live-common/lib/types";
import { TrackScreen } from "../../../analytics";
import { ScreenName } from "../../../const";

import type { SwapRouteParams } from "..";
import SendRowsFee from "../../../components/SendRowsFee";
import NavigationScrollView from "../../../components/NavigationScrollView";

type Props = {
  accounts: Account[],
  allAccounts: AccountLikeArray,
  navigation: any,
  route: { params: SwapRouteParams },
};

export default function SelectFees({ navigation, route }: Props) {
  const { transaction } = route.params;
  const { swap, provider, rate } = route.params;
  const isFixed = rate?.tradeMethod === "fixed";
  const { from: { account, parentAccount } = {} } = swap;

  const onSetTransaction = useCallback(
    updatedTransaction => {
      navigation.navigate(ScreenName.SwapForm, {
        ...route.params,
        transaction: updatedTransaction,
      });
    },
    [navigation, route.params],
  );

  return (
    <SafeAreaView style={[styles.root]}>
      <TrackScreen category="Swap Form" name="Edit Fees" provider={provider} />
      <NavigationScrollView contentContainerStyle={styles.scrollView}>
        {account && (
          <SendRowsFee
            setTransaction={onSetTransaction}
            account={account}
            parentAccount={parentAccount}
            transaction={transaction}
            navigation={navigation}
            route={{
              ...route,
              params: {
                ...route.params,
                accountId: account.id,
                parentAccountId: parentAccount?.id,
                currentNavigation: ScreenName.SwapForm,
              },
            }}
            disabledStrategies={isFixed ? ["slow"] : []}
          />
        )}
      </NavigationScrollView>
    </SafeAreaView>
  );
}

const styles = StyleSheet.create({
  root: {
    flex: 1,
  },
  scrollView: {
    paddingHorizontal: 16,
  },
});


<|start_filename|>src/screens/Settings/Developer/index.js<|end_filename|>
// @flow
import { NavigationProp } from "@react-navigation/native";
import { isEnvDefault } from "@ledgerhq/live-common/lib/env";
import React from "react";
import { ScrollView } from "react-native";
import SettingsRow from "../../../components/SettingsRow";
import { ScreenName } from "../../../const";
import { developerFeatures } from "../../../experimental";
import { TrackScreen } from "../../../analytics";
import FeatureRow from "../Experimental/FeatureRow";

export { default as DeveloperCustomManifest } from "./CustomManifest";

export default function DeveloperSettings({
  navigation,
}: {
  navigation: NavigationProp,
}) {
  return (
    <ScrollView>
      <TrackScreen category="Settings" name="Developer" />

      {developerFeatures.map(
        feat =>
          (!feat.shadow || (feat.shadow && !isEnvDefault(feat.name))) && (
            // $FlowFixMe
            <FeatureRow key={feat.name} feature={feat} />
          ),
      )}

      <SettingsRow
        title="Load Platform Manifest"
        onPress={() => navigation.navigate(ScreenName.DeveloperCustomManifest)}
      />
    </ScrollView>
  );
}
