diff --git a/apps/mobile/src/components/icon/index.ts b/apps/mobile/src/components/icon/index.ts index be2c3e8ba3..8c7079aa63 100644 --- a/apps/mobile/src/components/icon/index.ts +++ b/apps/mobile/src/components/icon/index.ts @@ -33,3 +33,13 @@ export * from './plus'; export * from './home-filled'; export * from './home-outlined'; export * from './setting-outlined'; + +export * from './msg-send'; +export * from './msg-receive'; +export * from './msg-claim-reward'; +export * from './msg-delegate'; +export * from './msg-undelegate'; +export * from './msg-redelegate'; +export * from './msg-cancel-undelegate'; +export * from './msg-swap'; +export * from './msg-vote'; diff --git a/apps/mobile/src/components/icon/msg-cancel-undelegate.tsx b/apps/mobile/src/components/icon/msg-cancel-undelegate.tsx new file mode 100644 index 0000000000..592e76010b --- /dev/null +++ b/apps/mobile/src/components/icon/msg-cancel-undelegate.tsx @@ -0,0 +1,30 @@ +import React, {FunctionComponent} from 'react'; +import {IconProps} from './types'; +import {ClipPath, Defs, G, Path, Rect, Svg} from 'react-native-svg'; + +export const MessageCancelUndelegateIcon: FunctionComponent = ({ + size, + color, +}) => { + return ( + + + + + + + + + + + + ); +}; diff --git a/apps/mobile/src/components/icon/msg-claim-reward.tsx b/apps/mobile/src/components/icon/msg-claim-reward.tsx new file mode 100644 index 0000000000..84e3307103 --- /dev/null +++ b/apps/mobile/src/components/icon/msg-claim-reward.tsx @@ -0,0 +1,20 @@ +import React, {FunctionComponent} from 'react'; +import {IconProps} from './types'; +import {Path, Svg} from 'react-native-svg'; + +export const MessageClaimRewardIcon: FunctionComponent = ({ + size, + color, +}) => { + return ( + + + + ); +}; diff --git a/apps/mobile/src/components/icon/msg-delegate.tsx b/apps/mobile/src/components/icon/msg-delegate.tsx new file mode 100644 index 0000000000..361b745939 --- /dev/null +++ b/apps/mobile/src/components/icon/msg-delegate.tsx @@ -0,0 +1,47 @@ +import React, {FunctionComponent} from 'react'; +import {IconProps} from './types'; +import {ClipPath, Defs, G, Path, Rect, Svg} from 'react-native-svg'; + +export const MessageDelegateIcon: FunctionComponent = ({ + size, + color, +}) => { + return ( + + + + + + + + + + + + + + + + + + ); +}; diff --git a/apps/mobile/src/components/icon/msg-receive.tsx b/apps/mobile/src/components/icon/msg-receive.tsx new file mode 100644 index 0000000000..b59a221af6 --- /dev/null +++ b/apps/mobile/src/components/icon/msg-receive.tsx @@ -0,0 +1,19 @@ +import React, {FunctionComponent} from 'react'; +import {IconProps} from './types'; +import {Path, Svg} from 'react-native-svg'; + +export const MessageReceiveIcon: FunctionComponent = ({ + size, + color, +}) => { + return ( + + + + ); +}; diff --git a/apps/mobile/src/components/icon/msg-redelegate.tsx b/apps/mobile/src/components/icon/msg-redelegate.tsx new file mode 100644 index 0000000000..dd6ce4cce3 --- /dev/null +++ b/apps/mobile/src/components/icon/msg-redelegate.tsx @@ -0,0 +1,49 @@ +import React, {FunctionComponent} from 'react'; +import {IconProps} from './types'; +import {ClipPath, Defs, G, Path, Rect, Svg} from 'react-native-svg'; + +export const MessageRedelegateIcon: FunctionComponent = ({ + size, + color, +}) => { + return ( + + + + + + + + + + + + + + + + + + ); +}; diff --git a/apps/mobile/src/components/icon/msg-send.tsx b/apps/mobile/src/components/icon/msg-send.tsx new file mode 100644 index 0000000000..c471cde5c8 --- /dev/null +++ b/apps/mobile/src/components/icon/msg-send.tsx @@ -0,0 +1,19 @@ +import React, {FunctionComponent} from 'react'; +import {IconProps} from './types'; +import {Path, Svg} from 'react-native-svg'; + +export const MessageSendIcon: FunctionComponent = ({ + size, + color, +}) => { + return ( + + + + ); +}; diff --git a/apps/mobile/src/components/icon/msg-swap.tsx b/apps/mobile/src/components/icon/msg-swap.tsx new file mode 100644 index 0000000000..4c2a673446 --- /dev/null +++ b/apps/mobile/src/components/icon/msg-swap.tsx @@ -0,0 +1,19 @@ +import React, {FunctionComponent} from 'react'; +import {IconProps} from './types'; +import {Path, Svg} from 'react-native-svg'; + +export const MessageSwapIcon: FunctionComponent = ({ + size, + color, +}) => { + return ( + + + + ); +}; diff --git a/apps/mobile/src/components/icon/msg-undelegate.tsx b/apps/mobile/src/components/icon/msg-undelegate.tsx new file mode 100644 index 0000000000..327a350a54 --- /dev/null +++ b/apps/mobile/src/components/icon/msg-undelegate.tsx @@ -0,0 +1,49 @@ +import React, {FunctionComponent} from 'react'; +import {IconProps} from './types'; +import {ClipPath, Defs, G, Path, Rect, Svg} from 'react-native-svg'; + +export const MessageUndelegateIcon: FunctionComponent = ({ + size, + color, +}) => { + return ( + + + + + + + + + + + + + + + + + + ); +}; diff --git a/apps/mobile/src/components/icon/msg-vote.tsx b/apps/mobile/src/components/icon/msg-vote.tsx new file mode 100644 index 0000000000..ee5f261c96 --- /dev/null +++ b/apps/mobile/src/components/icon/msg-vote.tsx @@ -0,0 +1,43 @@ +import React, {FunctionComponent} from 'react'; +import {IconProps} from './types'; +import {ClipPath, Defs, G, Path, Rect, Svg} from 'react-native-svg'; + +export const MessageVoteIcon: FunctionComponent = ({ + size, + color, +}) => { + return ( + + + + + + + + + + + + + + + + ); +}; diff --git a/apps/mobile/src/components/pageHeader/header.tsx b/apps/mobile/src/components/pageHeader/header.tsx index 58ce22c2e1..9342488171 100644 --- a/apps/mobile/src/components/pageHeader/header.tsx +++ b/apps/mobile/src/components/pageHeader/header.tsx @@ -114,7 +114,7 @@ const DefaultScreenHeaderTitle: FunctionComponent = ({ ); }; -const DefaultScreenHeaderLeft: FunctionComponent = () => { +export const DefaultScreenHeaderLeft: FunctionComponent = () => { const style = useStyle(); const nav = useNavigation(); const isSettingIntro = diff --git a/apps/mobile/src/hooks/use-buy.ts b/apps/mobile/src/hooks/use-buy.ts new file mode 100644 index 0000000000..7b19a0b61f --- /dev/null +++ b/apps/mobile/src/hooks/use-buy.ts @@ -0,0 +1,163 @@ +import {AppCurrency, ChainInfo} from '@keplr-wallet/types'; +import {useStore} from '../stores'; +import {useEffect, useState} from 'react'; +import {FiatOnRampServiceInfo} from '../config.ui.ts'; +import {simpleFetch} from '@keplr-wallet/simple-fetch'; + +export const useBuy = (selectedTokenInfo?: { + chainId: string; + currency: AppCurrency; +}) => { + const {accountStore, chainStore} = useStore(); + const [fiatOnRampServiceInfos, setFiatOnRampServiceInfos] = useState< + FiatOnRampServiceInfo[] + >([]); + + useEffect(() => { + (async () => { + const response = await simpleFetch<{list: FiatOnRampServiceInfo[]}>( + 'https://raw.githubusercontent.com/chainapsis/keplr-fiat-on-off-ramp-registry/main/fiat-on-off-ramp-list.json', + ); + + setFiatOnRampServiceInfos(response.data.list); + })(); + }, []); + + const buySupportServiceInfos = fiatOnRampServiceInfos.map(serviceInfo => { + const buySupportCoinDenoms = [ + ...new Set( + selectedTokenInfo + ? Object.entries(serviceInfo.buySupportCoinDenomsByChainId) + .filter( + ([chainId, coinDenoms]) => + chainId === selectedTokenInfo.chainId && + coinDenoms?.some(coinDenom => + coinDenom === 'USDC' + ? selectedTokenInfo.currency.coinDenom.includes('USDC') + : coinDenom === selectedTokenInfo.currency.coinDenom, + ), + ) + .map(([_, coinDenoms]) => coinDenoms) + .flat() + : Object.values(serviceInfo.buySupportCoinDenomsByChainId).flat(), + ), + ]; + const buySupportChainAccounts = (() => { + const res: { + chainInfo: ChainInfo; + bech32Address: string; + }[] = []; + + for (const chainId of Object.keys( + serviceInfo.buySupportCoinDenomsByChainId, + )) { + if (chainStore.hasChain(chainId)) { + if ( + !selectedTokenInfo || + (selectedTokenInfo && selectedTokenInfo.chainId === chainId) + ) { + res.push({ + chainInfo: chainStore.getChain(chainId), + bech32Address: accountStore.getAccount(chainId).bech32Address, + }); + } + } + } + + return res; + })(); + + const selectedCoinDenom = selectedTokenInfo + ? buySupportCoinDenoms.find(coinDenom => + coinDenom === 'USDC' + ? selectedTokenInfo.currency.coinDenom.includes('USDC') + : coinDenom === selectedTokenInfo.currency.coinDenom, + ) + : undefined; + const selectedChainName = selectedTokenInfo + ? buySupportChainAccounts.find( + chainAccount => + chainAccount.chainInfo.chainId === selectedTokenInfo.chainId, + )?.chainInfo.chainName + : undefined; + + const buyUrlParams = (() => { + if (buySupportCoinDenoms.length === 0) { + return undefined; + } + + switch (serviceInfo.serviceId) { + case 'moonpay': + return { + apiKey: + process.env['KEPLR_EXT_MOONPAY_API_KEY'] ?? serviceInfo.apiKey, + showWalletAddressForm: 'true', + walletAddresses: encodeURIComponent( + JSON.stringify( + buySupportChainAccounts.reduce( + (acc, cur) => ({ + ...acc, + [( + cur.chainInfo.stakeCurrency || cur.chainInfo.currencies[0] + ).coinDenom.toLowerCase()]: cur.bech32Address, + }), + {}, + ), + ), + ), + defaultCurrencyCode: selectedCoinDenom ?? buySupportCoinDenoms[0], + }; + case 'transak': + return { + apiKey: + process.env['KEPLR_EXT_TRANSAK_API_KEY'] ?? serviceInfo.apiKey, + hideMenu: 'true', + walletAddressesData: encodeURIComponent( + JSON.stringify({ + coins: buySupportChainAccounts.reduce( + (acc, cur) => ({ + ...acc, + [( + cur.chainInfo.stakeCurrency || cur.chainInfo.currencies[0] + ).coinDenom]: { + address: cur.bech32Address, + }, + }), + {}, + ), + }), + ), + cryptoCurrencyList: buySupportCoinDenoms, + defaultCryptoCurrency: selectedCoinDenom ?? buySupportCoinDenoms[0], + }; + case 'kado': + return { + apiKey: process.env['KEPLR_EXT_KADO_API_KEY'] ?? serviceInfo.apiKey, + product: 'BUY', + networkList: buySupportChainAccounts.map(chainAccount => + chainAccount.chainInfo.chainName.toUpperCase(), + ), + cryptoList: buySupportCoinDenoms, + onRevCurrency: selectedCoinDenom ?? buySupportCoinDenoms[0], + network: + selectedChainName ?? + buySupportChainAccounts[0].chainInfo.chainName.toUpperCase(), + }; + default: + return; + } + })(); + const buyUrl = buyUrlParams + ? `${serviceInfo.buyOrigin}?${Object.entries(buyUrlParams) + .map(paramKeyValue => paramKeyValue.join('=')) + .join('&')}` + : undefined; + + return { + ...serviceInfo, + buyUrl, + }; + }); + + return buySupportServiceInfos; +}; diff --git a/apps/mobile/src/navigation.tsx b/apps/mobile/src/navigation.tsx index fc0ea0d363..b6c569cf56 100644 --- a/apps/mobile/src/navigation.tsx +++ b/apps/mobile/src/navigation.tsx @@ -124,6 +124,7 @@ import {Text} from 'react-native'; import {ActivitiesScreen} from './screen/activities'; import {DocumentFillIcon} from './components/icon/document-fill.tsx'; import {DocumentOutlinedIcon} from './components/icon/document-outliend.tsx'; +import {TokenDetailScreen} from './screen/token-detail'; type DefaultRegisterParams = { hideBackButton?: boolean; @@ -343,8 +344,11 @@ export type RootStackParamList = { initialGasAdjustment?: string; tempSwitchAmount?: string; }; - Activities: undefined; + TokenDetail: { + chainId: string; + coinMinimalDenom: string; + }; }; export type StakeNavigation = { @@ -1320,6 +1324,7 @@ export const AppNavigation: FunctionComponent = observer(() => { }} component={IBCSwapDestinationSelectAssetScreen} /> + diff --git a/apps/mobile/src/screen/activities/messages.tsx b/apps/mobile/src/screen/activities/messages.tsx index 683cbc2042..7f2a1ea2df 100644 --- a/apps/mobile/src/screen/activities/messages.tsx +++ b/apps/mobile/src/screen/activities/messages.tsx @@ -78,7 +78,7 @@ export const RenderMessages: FunctionComponent<{ {msgsPerDaily.map((msgs, i) => { return ( - + + { @@ -110,8 +112,8 @@ export const MsgItemBase: FunctionComponent<{ } @@ -88,20 +87,3 @@ export const MsgRelationCancelUndelegate: FunctionComponent<{ /> ); }); - -export const CancelRedelegateIcon: FunctionComponent = ({ - size, - color, -}) => { - return ( - - - - ); -}; diff --git a/apps/mobile/src/screen/activities/msg-items/delegate.tsx b/apps/mobile/src/screen/activities/msg-items/delegate.tsx index dcfceca7cf..a8faf04b64 100644 --- a/apps/mobile/src/screen/activities/msg-items/delegate.tsx +++ b/apps/mobile/src/screen/activities/msg-items/delegate.tsx @@ -5,8 +5,8 @@ import {useStore} from '../../../stores'; import {CoinPretty} from '@keplr-wallet/unit'; import {Staking} from '@keplr-wallet/stores'; import {MsgItemBase} from './base.tsx'; -import {IconProps} from '../../../components/icon/types.ts'; -import {Path, Svg} from 'react-native-svg'; +import {MessageDelegateIcon} from '../../../components/icon'; +import {useStyle} from '../../../styles'; export const MsgRelationDelegate: FunctionComponent<{ msg: MsgHistory; @@ -16,6 +16,7 @@ export const MsgRelationDelegate: FunctionComponent<{ }> = observer(({msg, prices, targetDenom, isInAllActivitiesPage}) => { const {chainStore, queriesStore} = useStore(); + const style = useStyle(); const chainInfo = chainStore.getChain(msg.chainId); const amountPretty = useMemo(() => { @@ -68,7 +69,12 @@ export const MsgRelationDelegate: FunctionComponent<{ return ( } + logo={ + + } chainId={msg.chainId} title="Stake" paragraph={`To ${moniker}`} @@ -80,18 +86,3 @@ export const MsgRelationDelegate: FunctionComponent<{ /> ); }); - -export const DelegateIcon: FunctionComponent = ({size}) => { - return ( - - - - - ); -}; diff --git a/apps/mobile/src/screen/activities/msg-items/ibc-receive.tsx b/apps/mobile/src/screen/activities/msg-items/ibc-receive.tsx index 30badd9265..63b504bd54 100644 --- a/apps/mobile/src/screen/activities/msg-items/ibc-receive.tsx +++ b/apps/mobile/src/screen/activities/msg-items/ibc-receive.tsx @@ -7,8 +7,8 @@ import {CoinPretty} from '@keplr-wallet/unit'; import {Buffer} from 'buffer'; import {Bech32Address} from '@keplr-wallet/cosmos'; import {MsgItemBase} from './base.tsx'; -import {ArrowDownLeftIcon} from './receive.tsx'; import {useStyle} from '../../../styles'; +import {MessageReceiveIcon} from '../../../components/icon'; export const MsgRelationIBCSendReceive: FunctionComponent<{ msg: MsgHistory; @@ -58,8 +58,8 @@ export const MsgRelationIBCSendReceive: FunctionComponent<{ return ( } diff --git a/apps/mobile/src/screen/activities/msg-items/ibc-send-refunded.tsx b/apps/mobile/src/screen/activities/msg-items/ibc-send-refunded.tsx index 73885a3ec9..c598371920 100644 --- a/apps/mobile/src/screen/activities/msg-items/ibc-send-refunded.tsx +++ b/apps/mobile/src/screen/activities/msg-items/ibc-send-refunded.tsx @@ -4,9 +4,9 @@ import {useStore} from '../../../stores'; import {isValidCoinStr, parseCoinStr} from '@keplr-wallet/common'; import {CoinPretty} from '@keplr-wallet/unit'; import {MsgItemBase} from './base.tsx'; -import {ArrowDownLeftIcon} from './receive.tsx'; import {useStyle} from '../../../styles'; import {MsgHistory} from '../types.ts'; +import {MessageReceiveIcon} from '../../../components/icon'; export const MsgRelationIBCSendRefunded: FunctionComponent<{ msg: MsgHistory; @@ -39,8 +39,8 @@ export const MsgRelationIBCSendRefunded: FunctionComponent<{ return ( } diff --git a/apps/mobile/src/screen/activities/msg-items/ibc-send.tsx b/apps/mobile/src/screen/activities/msg-items/ibc-send.tsx index 2d6a0e0a80..0c383967ca 100644 --- a/apps/mobile/src/screen/activities/msg-items/ibc-send.tsx +++ b/apps/mobile/src/screen/activities/msg-items/ibc-send.tsx @@ -6,8 +6,8 @@ import {CoinPretty} from '@keplr-wallet/unit'; import {Bech32Address} from '@keplr-wallet/cosmos'; import {Buffer} from 'buffer'; import {MsgItemBase} from './base.tsx'; -import {ArrowUpRightIcon} from './send.tsx'; import {useStyle} from '../../../styles'; +import {MessageSendIcon} from '../../../components/icon'; export const MsgRelationIBCSend: FunctionComponent<{ msg: MsgHistory; @@ -72,7 +72,7 @@ export const MsgRelationIBCSend: FunctionComponent<{ return ( + } chainId={msg.chainId} title="Send" diff --git a/apps/mobile/src/screen/activities/msg-items/ibc-swap-receive.tsx b/apps/mobile/src/screen/activities/msg-items/ibc-swap-receive.tsx index 9d3a608bea..086d2b9262 100644 --- a/apps/mobile/src/screen/activities/msg-items/ibc-swap-receive.tsx +++ b/apps/mobile/src/screen/activities/msg-items/ibc-swap-receive.tsx @@ -1,4 +1,4 @@ -import {FunctionComponent, useMemo} from 'react'; +import React, {FunctionComponent, useMemo} from 'react'; import {observer} from 'mobx-react-lite'; import {MsgHistory} from '../types.ts'; import {useStore} from '../../../stores'; @@ -8,7 +8,7 @@ import {CoinPretty} from '@keplr-wallet/unit'; import {ChainInfo} from '@keplr-wallet/types'; import {Buffer} from 'buffer'; import {MsgItemBase} from './base.tsx'; -import {ArrowRightLeftIcon} from './ibc-swap.tsx'; +import {MessageSwapIcon} from '../../../components/icon'; export const MsgRelationIBCSwapReceive: FunctionComponent<{ msg: MsgHistory; @@ -206,10 +206,7 @@ export const MsgRelationIBCSwapReceive: FunctionComponent<{ return ( + } chainId={msg.chainId} title="Swap Completed" diff --git a/apps/mobile/src/screen/activities/msg-items/ibc-swap-refunded.tsx b/apps/mobile/src/screen/activities/msg-items/ibc-swap-refunded.tsx index 4f291577db..9d4af96e7f 100644 --- a/apps/mobile/src/screen/activities/msg-items/ibc-swap-refunded.tsx +++ b/apps/mobile/src/screen/activities/msg-items/ibc-swap-refunded.tsx @@ -1,12 +1,12 @@ -import {FunctionComponent, useMemo} from 'react'; +import React, {FunctionComponent, useMemo} from 'react'; import {observer} from 'mobx-react-lite'; import {useStore} from '../../../stores'; import {isValidCoinStr, parseCoinStr} from '@keplr-wallet/common'; import {CoinPretty} from '@keplr-wallet/unit'; import {MsgItemBase} from './base.tsx'; -import {ArrowDownLeftIcon} from './receive.tsx'; import {useStyle} from '../../../styles'; import {MsgHistory} from '../types.ts'; +import {MessageReceiveIcon} from '../../../components/icon'; export const MsgRelationIBCSwapRefunded: FunctionComponent<{ msg: MsgHistory; @@ -39,8 +39,8 @@ export const MsgRelationIBCSwapRefunded: FunctionComponent<{ return ( } diff --git a/apps/mobile/src/screen/activities/msg-items/ibc-swap.tsx b/apps/mobile/src/screen/activities/msg-items/ibc-swap.tsx index 93a3e92e32..11c1259139 100644 --- a/apps/mobile/src/screen/activities/msg-items/ibc-swap.tsx +++ b/apps/mobile/src/screen/activities/msg-items/ibc-swap.tsx @@ -7,9 +7,8 @@ import {CoinPretty} from '@keplr-wallet/unit'; import {ChainInfo} from '@keplr-wallet/types'; import {Buffer} from 'buffer'; import {MsgItemBase} from './base.tsx'; -import {IconProps} from '../../../components/icon/types.ts'; -import {Path, Svg} from 'react-native-svg'; import {useStyle} from '../../../styles'; +import {MessageSwapIcon} from '../../../components/icon'; export const MsgRelationIBCSwap: FunctionComponent<{ msg: MsgHistory; @@ -161,10 +160,7 @@ export const MsgRelationIBCSwap: FunctionComponent<{ return ( + } chainId={msg.chainId} title="Swap" @@ -192,20 +188,3 @@ export const MsgRelationIBCSwap: FunctionComponent<{ /> ); }); - -export const ArrowRightLeftIcon: FunctionComponent = ({ - size, - color, -}) => { - return ( - - - - ); -}; diff --git a/apps/mobile/src/screen/activities/msg-items/logo.tsx b/apps/mobile/src/screen/activities/msg-items/logo.tsx index f55061f78e..fc6481dc13 100644 --- a/apps/mobile/src/screen/activities/msg-items/logo.tsx +++ b/apps/mobile/src/screen/activities/msg-items/logo.tsx @@ -10,9 +10,11 @@ export const ItemLogo: FunctionComponent<{ return ( diff --git a/apps/mobile/src/screen/activities/msg-items/merged-claim-rewards.tsx b/apps/mobile/src/screen/activities/msg-items/merged-claim-rewards.tsx index 6f1135b742..bfd485c74e 100644 --- a/apps/mobile/src/screen/activities/msg-items/merged-claim-rewards.tsx +++ b/apps/mobile/src/screen/activities/msg-items/merged-claim-rewards.tsx @@ -18,6 +18,7 @@ import {Text} from 'react-native'; import {Gutter} from '../../../components/gutter'; import {VerticalCollapseTransition} from '../../../components/transition'; import {ItemLogo} from './logo.tsx'; +import {MessageClaimRewardIcon} from '../../../components/icon'; export const MsgRelationMergedClaimRewards: FunctionComponent<{ msg: MsgHistory; @@ -27,6 +28,7 @@ export const MsgRelationMergedClaimRewards: FunctionComponent<{ }> = observer(({msg, prices, targetDenom, isInAllActivitiesPage}) => { const {chainStore} = useStore(); + const style = useStyle(); const chainInfo = chainStore.getChain(msg.chainId); const amountPretty = useMemo(() => { @@ -75,7 +77,12 @@ export const MsgRelationMergedClaimRewards: FunctionComponent<{ return ( } + logo={ + + } chainId={msg.chainId} title="Claim Reward" amount={amountPretty} diff --git a/apps/mobile/src/screen/activities/msg-items/receive.tsx b/apps/mobile/src/screen/activities/msg-items/receive.tsx index 4dc71cf3cc..7cda8b171d 100644 --- a/apps/mobile/src/screen/activities/msg-items/receive.tsx +++ b/apps/mobile/src/screen/activities/msg-items/receive.tsx @@ -2,12 +2,11 @@ import React, {FunctionComponent, useMemo} from 'react'; import {observer} from 'mobx-react-lite'; import {MsgItemBase} from './base.tsx'; import {useStore} from '../../../stores'; -import {IconProps} from '../../../components/icon/types.ts'; -import {Path, Svg} from 'react-native-svg'; import {useStyle} from '../../../styles'; import {CoinPretty} from '@keplr-wallet/unit'; import {Bech32Address} from '@keplr-wallet/cosmos'; import {MsgHistory} from '../types.ts'; +import {MessageReceiveIcon} from '../../../components/icon'; export const MsgRelationReceive: FunctionComponent<{ msg: MsgHistory; @@ -48,8 +47,8 @@ export const MsgRelationReceive: FunctionComponent<{ return ( } @@ -68,20 +67,3 @@ export const MsgRelationReceive: FunctionComponent<{ /> ); }); - -export const ArrowDownLeftIcon: FunctionComponent = ({ - size, - color, -}) => { - return ( - - - - ); -}; diff --git a/apps/mobile/src/screen/activities/msg-items/redelegate.tsx b/apps/mobile/src/screen/activities/msg-items/redelegate.tsx index 784d421e0c..ba66cab80a 100644 --- a/apps/mobile/src/screen/activities/msg-items/redelegate.tsx +++ b/apps/mobile/src/screen/activities/msg-items/redelegate.tsx @@ -5,9 +5,8 @@ import {MsgHistory} from '../types.ts'; import {CoinPretty} from '@keplr-wallet/unit'; import {Staking} from '@keplr-wallet/stores'; import {MsgItemBase} from './base.tsx'; -import {IconProps} from '../../../components/icon/types.ts'; -import {Path, Svg} from 'react-native-svg'; import {useStyle} from '../../../styles'; +import {MessageRedelegateIcon} from '../../../components/icon'; export const MsgRelationRedelegate: FunctionComponent<{ msg: MsgHistory; @@ -95,7 +94,10 @@ export const MsgRelationRedelegate: FunctionComponent<{ return ( + } chainId={msg.chainId} title="Switch Validator" @@ -113,17 +115,3 @@ export const MsgRelationRedelegate: FunctionComponent<{ /> ); }); - -export const RedelegateIcon: FunctionComponent = ({size, color}) => { - return ( - - - - ); -}; diff --git a/apps/mobile/src/screen/activities/msg-items/send.tsx b/apps/mobile/src/screen/activities/msg-items/send.tsx index bf0e06d5ab..11a5e16202 100644 --- a/apps/mobile/src/screen/activities/msg-items/send.tsx +++ b/apps/mobile/src/screen/activities/msg-items/send.tsx @@ -5,9 +5,8 @@ import {useStore} from '../../../stores'; import {CoinPretty} from '@keplr-wallet/unit'; import {Bech32Address} from '@keplr-wallet/cosmos'; import {MsgItemBase} from './base.tsx'; -import {IconProps} from '../../../components/icon/types.ts'; -import {Path, Svg} from 'react-native-svg'; import {useStyle} from '../../../styles'; +import {MessageSendIcon} from '../../../components/icon'; export const MsgRelationSend: FunctionComponent<{ msg: MsgHistory; @@ -48,7 +47,7 @@ export const MsgRelationSend: FunctionComponent<{ return ( + } chainId={msg.chainId} title="Send" @@ -65,20 +64,3 @@ export const MsgRelationSend: FunctionComponent<{ /> ); }); - -export const ArrowUpRightIcon: FunctionComponent = ({ - size, - color, -}) => { - return ( - - - - ); -}; diff --git a/apps/mobile/src/screen/activities/msg-items/undelegate.tsx b/apps/mobile/src/screen/activities/msg-items/undelegate.tsx index 3e89778aa5..c52431c4d6 100644 --- a/apps/mobile/src/screen/activities/msg-items/undelegate.tsx +++ b/apps/mobile/src/screen/activities/msg-items/undelegate.tsx @@ -5,8 +5,8 @@ import {useStore} from '../../../stores'; import {CoinPretty} from '@keplr-wallet/unit'; import {Staking} from '@keplr-wallet/stores'; import {MsgItemBase} from './base.tsx'; -import {IconProps} from '../../../components/icon/types.ts'; -import {Path, Svg} from 'react-native-svg'; +import {MessageUndelegateIcon} from '../../../components/icon'; +import {useStyle} from '../../../styles'; export const MsgRelationUndelegate: FunctionComponent<{ msg: MsgHistory; @@ -16,6 +16,8 @@ export const MsgRelationUndelegate: FunctionComponent<{ }> = observer(({msg, prices, targetDenom, isInAllActivitiesPage}) => { const {chainStore, queriesStore} = useStore(); + const style = useStyle(); + const chainInfo = chainStore.getChain(msg.chainId); const amountPretty = useMemo(() => { @@ -68,7 +70,12 @@ export const MsgRelationUndelegate: FunctionComponent<{ return ( } + logo={ + + } chainId={msg.chainId} title="Unstake" paragraph={`From ${moniker}`} @@ -80,19 +87,3 @@ export const MsgRelationUndelegate: FunctionComponent<{ /> ); }); - -export const UndelegateIcon: FunctionComponent = ({size}) => { - return ( - - - - - ); -}; diff --git a/apps/mobile/src/screen/activities/msg-items/vote.tsx b/apps/mobile/src/screen/activities/msg-items/vote.tsx index b7c943b36e..595b2a8de3 100644 --- a/apps/mobile/src/screen/activities/msg-items/vote.tsx +++ b/apps/mobile/src/screen/activities/msg-items/vote.tsx @@ -2,8 +2,8 @@ import React, {FunctionComponent, useMemo} from 'react'; import {observer} from 'mobx-react-lite'; import {MsgHistory} from '../types.ts'; import {MsgItemBase} from './base.tsx'; -import {G, Defs, Path, Rect, Svg, ClipPath} from 'react-native-svg'; import {useStyle} from '../../../styles'; +import {MessageVoteIcon} from '../../../components/icon'; export const MsgRelationVote: FunctionComponent<{ msg: MsgHistory; @@ -55,7 +55,9 @@ export const MsgRelationVote: FunctionComponent<{ return ( } + logo={ + + } chainId={msg.chainId} title="Vote" paragraph={`#${proposal.proposalId}`} @@ -68,48 +70,3 @@ export const MsgRelationVote: FunctionComponent<{ /> ); }); - -export const VoteIcon: FunctionComponent = () => { - return ( - - - - - - - - - - - - - - ); -}; diff --git a/apps/mobile/src/screen/home/available.tsx b/apps/mobile/src/screen/home/available.tsx index 7944189e27..3987913e61 100644 --- a/apps/mobile/src/screen/home/available.tsx +++ b/apps/mobile/src/screen/home/available.tsx @@ -18,7 +18,7 @@ import {TokenFoundModal} from './components/token-found-modal'; import {LookingForChains} from './components/looking-for-chains'; import {Gutter} from '../../components/gutter'; import * as ExpoImage from 'expo-image'; -import {StackActions, useNavigation} from '@react-navigation/native'; +import {useNavigation} from '@react-navigation/native'; import {FormattedMessage, useIntl} from 'react-intl'; import {Toggle} from '../../components/toggle'; import { @@ -262,12 +262,13 @@ export const AvailableTabView: FunctionComponent<{ setIsSecretErrorModalOpen(true); }} onClick={() => { - navigation.dispatch({ - ...StackActions.push('Send', { + navigation.navigate({ + name: 'TokenDetail', + params: { chainId: viewToken.chainInfo.chainId, coinMinimalDenom: viewToken.token.currency.coinMinimalDenom, - }), + }, }); }} showPrice24HChange={ diff --git a/apps/mobile/src/screen/home/components/deposit-modal/buy-modal.tsx b/apps/mobile/src/screen/home/components/deposit-modal/buy-modal.tsx index 05148f5061..bfdad61ced 100644 --- a/apps/mobile/src/screen/home/components/deposit-modal/buy-modal.tsx +++ b/apps/mobile/src/screen/home/components/deposit-modal/buy-modal.tsx @@ -1,13 +1,7 @@ import {observer} from 'mobx-react-lite'; -import React, {FunctionComponent, useEffect, useState} from 'react'; +import React, {FunctionComponent} from 'react'; import {StyleSheet, Text} from 'react-native'; -import { - FiatOnRampServiceInfo, - FiatOnRampServiceInfos, -} from '../../../../config.ui'; -import {simpleFetch} from '@keplr-wallet/simple-fetch'; -import {useStore} from '../../../../stores'; -import {ChainInfo} from '@keplr-wallet/types'; +import {FiatOnRampServiceInfo} from '../../../../config.ui'; import {useStyle} from '../../../../styles'; import {RectButton} from 'react-native-gesture-handler'; import {Box} from '../../../../components/box'; @@ -18,190 +12,49 @@ import {Gutter} from '../../../../components/gutter'; import {Button} from '../../../../components/button'; import {StackNavProp} from '../../../../navigation'; import {FormattedMessage, useIntl} from 'react-intl'; -import {Column, Columns} from '../../../../components/column'; import {ArrowLeftIcon} from '../../../../components/icon'; import {IconButton} from '../../../../components/icon-button'; import {useSafeAreaInsets} from 'react-native-safe-area-context'; export const BuyModal = observer<{ - setCurrentScene: (key: string) => void; - setIsOpen: (isOpen: boolean) => void; navigation: StackNavProp; -}>(({setCurrentScene, navigation, setIsOpen}) => { - const {accountStore, chainStore} = useStore(); + buySupportServiceInfos: (FiatOnRampServiceInfo & {buyUrl?: string})[]; + setIsOpen: (isOpen: boolean) => void; + setCurrentScene?: (key: string) => void; +}>(({setCurrentScene, navigation, setIsOpen, buySupportServiceInfos}) => { const style = useStyle(); const intl = useIntl(); const safeAreaInsets = useSafeAreaInsets(); - const [fiatOnRampServiceInfos, setFiatOnRampServiceInfos] = useState( - FiatOnRampServiceInfos, - ); - useEffect(() => { - (async () => { - const response = await simpleFetch<{list: FiatOnRampServiceInfo[]}>( - 'https://raw.githubusercontent.com/chainapsis/keplr-fiat-on-off-ramp-registry/main/fiat-on-off-ramp-list.json', - ); - - setFiatOnRampServiceInfos(response.data.list); - })(); - }, []); - - const buySupportServiceInfos = fiatOnRampServiceInfos.map(serviceInfo => { - const buySupportChainIds = Object.keys( - serviceInfo.buySupportCoinDenomsByChainId, - ); - - const buySupportDefaultChainInfo = (() => { - if ( - buySupportChainIds.length > 0 && - chainStore.hasChain(buySupportChainIds[0]) - ) { - return chainStore.getChain(buySupportChainIds[0]); - } - })(); - - const buySupportChainAccounts = (() => { - const res: { - chainInfo: ChainInfo; - bech32Address: string; - }[] = []; - - for (const chainId of buySupportChainIds) { - if (chainStore.hasChain(chainId)) { - res.push({ - chainInfo: chainStore.getChain(chainId), - bech32Address: accountStore.getAccount(chainId).bech32Address, - }); - } - } - - return res; - })(); - - const buyUrlParams = (() => { - switch (serviceInfo.serviceId) { - case 'moonpay': - return { - apiKey: - process.env['KEPLR_EXT_MOONPAY_API_KEY'] ?? serviceInfo.apiKey, - showWalletAddressForm: 'true', - walletAddresses: encodeURIComponent( - JSON.stringify( - buySupportChainAccounts.reduce( - (acc, cur) => ({ - ...acc, - [( - cur.chainInfo.stakeCurrency || cur.chainInfo.currencies[0] - ).coinDenom.toLowerCase()]: cur.bech32Address, - }), - {}, - ), - ), - ), - ...(buySupportDefaultChainInfo && { - defaultCurrencyCode: ( - buySupportDefaultChainInfo.stakeCurrency || - buySupportDefaultChainInfo.currencies[0] - ).coinDenom.toLowerCase(), - }), - }; - case 'transak': - return { - apiKey: - process.env['KEPLR_EXT_TRANSAK_API_KEY'] ?? serviceInfo.apiKey, - hideMenu: 'true', - walletAddressesData: encodeURIComponent( - JSON.stringify({ - coins: buySupportChainAccounts.reduce( - (acc, cur) => ({ - ...acc, - [( - cur.chainInfo.stakeCurrency || cur.chainInfo.currencies[0] - ).coinDenom]: { - address: cur.bech32Address, - }, - }), - {}, - ), - }), - ), - cryptoCurrencyList: buySupportChainAccounts - .map( - chainAccount => - ( - chainAccount.chainInfo.stakeCurrency || - chainAccount.chainInfo.currencies[0] - ).coinDenom, - ) - .join(','), - ...(buySupportDefaultChainInfo && { - defaultCryptoCurrency: ( - buySupportDefaultChainInfo.stakeCurrency || - buySupportDefaultChainInfo.currencies[0] - ).coinDenom, - }), - }; - case 'kado': - return { - apiKey: process.env['KEPLR_EXT_KADO_API_KEY'] ?? serviceInfo.apiKey, - product: 'BUY', - networkList: buySupportChainAccounts.map(chainAccount => - chainAccount.chainInfo.chainName.toUpperCase(), - ), - cryptoList: [ - ...new Set( - Object.values(serviceInfo.buySupportCoinDenomsByChainId).flat(), - ), - ], - ...(buySupportDefaultChainInfo && { - onRevCurrency: - serviceInfo.buySupportCoinDenomsByChainId[ - buySupportDefaultChainInfo.chainId - ]?.[0] ?? 'USDC', - network: buySupportDefaultChainInfo.chainName.toUpperCase(), - }), - }; - default: - return; - } - })(); - const buyUrl = buyUrlParams - ? `${serviceInfo.buyOrigin}?${Object.entries(buyUrlParams) - .map(paramKeyValue => paramKeyValue.join('=')) - .join('&')}` - : undefined; - - return { - ...serviceInfo, - buyUrl, - }; - }); - return ( - - { - setCurrentScene('List'); - }} - icon={color => } - containerStyle={style.flatten(['width-32', 'height-32'])} - /> - - - - - - - - - + + {setCurrentScene ? ( + + { + setCurrentScene('List'); + }} + icon={color => } + containerStyle={style.flatten(['width-32', 'height-32'])} + /> + + ) : null} + + + + + {buySupportServiceInfos.map(serviceInfo => { + if (serviceInfo.buyUrl === undefined) { + return null; + } + return ( (''); const [currentScene, setCurrentScene] = useState('List'); + const buySupportServiceInfos = useBuy(); + return ( ); diff --git a/apps/mobile/src/screen/token-detail/components/address-chip.tsx b/apps/mobile/src/screen/token-detail/components/address-chip.tsx new file mode 100644 index 0000000000..c8556031f9 --- /dev/null +++ b/apps/mobile/src/screen/token-detail/components/address-chip.tsx @@ -0,0 +1,70 @@ +import React, {FunctionComponent, useState} from 'react'; +import {observer} from 'mobx-react-lite'; +import {useStore} from '../../../stores'; +import {RectButton} from '../../../components/rect-button'; +import {Text} from 'react-native'; +import {useStyle} from '../../../styles'; +import {Bech32Address} from '@keplr-wallet/cosmos'; +import {CopyOutlineIcon} from '../../../components/icon'; +import {Gutter} from '../../../components/gutter'; +import {XAxis} from '../../../components/axis'; +import * as Clipboard from 'expo-clipboard'; +import LottieView from 'lottie-react-native'; + +export const AddressChip: FunctionComponent<{ + chainId: string; + + // modal 안에서는 색상 문제로 안보여서 + // modal 안에서는 배경색을 바꿈 + inModal?: boolean; +}> = observer(({chainId, inModal}) => { + const {accountStore} = useStore(); + const style = useStyle(); + + const account = accountStore.getAccount(chainId); + + const [hasCopied, setHasCopied] = useState(false); + + return ( + { + await Clipboard.setStringAsync(account.bech32Address); + setHasCopied(true); + + setTimeout(() => { + setHasCopied(false); + }, 1000); + }}> + + + {Bech32Address.shortenAddress(account.bech32Address, 16)} + + + + {hasCopied ? ( + + ) : ( + + )} + + + ); +}); diff --git a/apps/mobile/src/screen/token-detail/components/circle-button.tsx b/apps/mobile/src/screen/token-detail/components/circle-button.tsx new file mode 100644 index 0000000000..6d213aa5a0 --- /dev/null +++ b/apps/mobile/src/screen/token-detail/components/circle-button.tsx @@ -0,0 +1,38 @@ +import React, {FunctionComponent} from 'react'; +import {Text} from 'react-native'; +import {useStyle} from '../../../styles'; +import {Gutter} from '../../../components/gutter'; +import {YAxis} from '../../../components/axis'; +import {Box} from '../../../components/box'; +import {TouchableWithoutFeedback} from 'react-native-gesture-handler'; + +export const CircleButton: FunctionComponent<{ + onClick: () => void; + icon: React.ReactElement; + text: string; + disabled?: boolean; +}> = ({onClick, icon, text, disabled}) => { + const style = useStyle(); + + return ( + + + + {icon} + + + + + {text} + + + ); +}; diff --git a/apps/mobile/src/screen/token-detail/components/qr-chip.tsx b/apps/mobile/src/screen/token-detail/components/qr-chip.tsx new file mode 100644 index 0000000000..00ec3fd4a5 --- /dev/null +++ b/apps/mobile/src/screen/token-detail/components/qr-chip.tsx @@ -0,0 +1,18 @@ +import React, {FunctionComponent} from 'react'; +import {useStyle} from '../../../styles'; +import {QRCodeIcon} from '../../../components/icon'; +import {TouchableWithoutFeedback} from 'react-native-gesture-handler'; + +export const QRCodeChip: FunctionComponent<{ + onClick: () => void; +}> = ({onClick}) => { + const style = useStyle(); + + return ( + + + + ); +}; diff --git a/apps/mobile/src/screen/token-detail/components/receive-modal.tsx b/apps/mobile/src/screen/token-detail/components/receive-modal.tsx new file mode 100644 index 0000000000..4ad8cfe31b --- /dev/null +++ b/apps/mobile/src/screen/token-detail/components/receive-modal.tsx @@ -0,0 +1,83 @@ +import React from 'react'; +import {registerCardModal} from '../../../components/modal/card'; +import {observer} from 'mobx-react-lite'; +import {Box} from '../../../components/box'; +import {Text} from 'react-native'; +import {useStyle} from '../../../styles'; +import {useStore} from '../../../stores'; +import {ChainImageFallback} from '../../../components/image'; +import {XAxis} from '../../../components/axis'; +import {Gutter} from '../../../components/gutter'; +import QRCode from 'react-native-qrcode-svg'; +import {AddressChip} from './address-chip.tsx'; +import {Button} from '../../../components/button'; + +export const ReceiveModal = registerCardModal<{ + chainId: string; + setIsOpen: (isOpen: boolean) => void; +}>( + observer(({chainId, setIsOpen}) => { + const {chainStore, accountStore} = useStore(); + const style = useStyle(); + + const chainInfo = chainStore.getChain(chainId); + const account = accountStore.getAccount(chainId); + + return ( + + + Copy Address + + + + + + + + + + {chainInfo.chainName} + + + + + + + + + + + + + + + +