├── app ├── assets │ ├── images │ │ ├── icon.png │ │ ├── binance.png │ │ ├── bitcoin.png │ │ ├── ethereum.png │ │ ├── favicon.png │ │ ├── polygon.png │ │ ├── splash.png │ │ ├── wallet.png │ │ └── adaptive-icon.png │ ├── fonts │ │ ├── IBMPlexMono-Bold.ttf │ │ ├── IBMPlexSans-Bold.ttf │ │ ├── IBMPlexMono-Light.ttf │ │ ├── IBMPlexMono-Medium.ttf │ │ ├── IBMPlexMono-Regular.ttf │ │ ├── IBMPlexSans-Light.ttf │ │ ├── IBMPlexSans-Medium.ttf │ │ ├── IBMPlexSans-Regular.ttf │ │ └── IBMPlexSans-SemiBold.ttf │ └── vectors │ │ └── index.tsx ├── hooks │ ├── useColorScheme.ts │ └── useCachedResources.ts ├── constants │ ├── Layout.ts │ ├── Colors.ts │ ├── Dummies.ts │ └── Assets.ts ├── screens │ ├── Exchange.tsx │ ├── Profile.tsx │ ├── Home.tsx │ ├── Trade.tsx │ ├── Onboarding.tsx │ ├── Receive.tsx │ ├── Send.tsx │ └── Swap.tsx ├── components │ ├── __tests__ │ │ └── StyledText-test.js │ ├── AppStatusBar.tsx │ ├── TouchableText.tsx │ ├── ModalHeaderRight.tsx │ ├── StyledText.tsx │ ├── Themed.tsx │ ├── HomeTabs.tsx │ ├── TokenSelector.tsx │ ├── TradingModalHeader.tsx │ ├── WalletTokens.tsx │ ├── WalletTransactions.tsx │ └── HeroBar.tsx ├── atoms │ └── index.ts ├── navigation │ ├── LinkingConfiguration.ts │ └── index.tsx ├── helpers │ └── index.ts └── styles │ └── index.ts ├── .gitignore ├── babel.config.js ├── .expo-shared └── assets.json ├── tsconfig.json ├── App.tsx ├── README.md ├── app.json ├── LICENSE ├── package.json └── types.tsx /app/assets/images/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eliezerbasubi/B-Wallet/HEAD/app/assets/images/icon.png -------------------------------------------------------------------------------- /app/assets/images/binance.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eliezerbasubi/B-Wallet/HEAD/app/assets/images/binance.png -------------------------------------------------------------------------------- /app/assets/images/bitcoin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eliezerbasubi/B-Wallet/HEAD/app/assets/images/bitcoin.png -------------------------------------------------------------------------------- /app/assets/images/ethereum.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eliezerbasubi/B-Wallet/HEAD/app/assets/images/ethereum.png -------------------------------------------------------------------------------- /app/assets/images/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eliezerbasubi/B-Wallet/HEAD/app/assets/images/favicon.png -------------------------------------------------------------------------------- /app/assets/images/polygon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eliezerbasubi/B-Wallet/HEAD/app/assets/images/polygon.png -------------------------------------------------------------------------------- /app/assets/images/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eliezerbasubi/B-Wallet/HEAD/app/assets/images/splash.png -------------------------------------------------------------------------------- /app/assets/images/wallet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eliezerbasubi/B-Wallet/HEAD/app/assets/images/wallet.png -------------------------------------------------------------------------------- /app/assets/images/adaptive-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eliezerbasubi/B-Wallet/HEAD/app/assets/images/adaptive-icon.png -------------------------------------------------------------------------------- /app/assets/fonts/IBMPlexMono-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eliezerbasubi/B-Wallet/HEAD/app/assets/fonts/IBMPlexMono-Bold.ttf -------------------------------------------------------------------------------- /app/assets/fonts/IBMPlexSans-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eliezerbasubi/B-Wallet/HEAD/app/assets/fonts/IBMPlexSans-Bold.ttf -------------------------------------------------------------------------------- /app/assets/fonts/IBMPlexMono-Light.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eliezerbasubi/B-Wallet/HEAD/app/assets/fonts/IBMPlexMono-Light.ttf -------------------------------------------------------------------------------- /app/assets/fonts/IBMPlexMono-Medium.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eliezerbasubi/B-Wallet/HEAD/app/assets/fonts/IBMPlexMono-Medium.ttf -------------------------------------------------------------------------------- /app/assets/fonts/IBMPlexMono-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eliezerbasubi/B-Wallet/HEAD/app/assets/fonts/IBMPlexMono-Regular.ttf -------------------------------------------------------------------------------- /app/assets/fonts/IBMPlexSans-Light.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eliezerbasubi/B-Wallet/HEAD/app/assets/fonts/IBMPlexSans-Light.ttf -------------------------------------------------------------------------------- /app/assets/fonts/IBMPlexSans-Medium.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eliezerbasubi/B-Wallet/HEAD/app/assets/fonts/IBMPlexSans-Medium.ttf -------------------------------------------------------------------------------- /app/assets/fonts/IBMPlexSans-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eliezerbasubi/B-Wallet/HEAD/app/assets/fonts/IBMPlexSans-Regular.ttf -------------------------------------------------------------------------------- /app/assets/fonts/IBMPlexSans-SemiBold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eliezerbasubi/B-Wallet/HEAD/app/assets/fonts/IBMPlexSans-SemiBold.ttf -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | .expo/ 3 | dist/ 4 | npm-debug.* 5 | *.jks 6 | *.p8 7 | *.p12 8 | *.key 9 | *.mobileprovision 10 | *.orig.* 11 | web-build/ 12 | 13 | # macOS 14 | .DS_Store 15 | -------------------------------------------------------------------------------- /app/hooks/useColorScheme.ts: -------------------------------------------------------------------------------- 1 | import { 2 | ColorSchemeName, 3 | useColorScheme as _useColorScheme, 4 | } from "react-native"; 5 | 6 | export default function useColorScheme(): NonNullable { 7 | return _useColorScheme() as NonNullable; 8 | } 9 | -------------------------------------------------------------------------------- /app/constants/Layout.ts: -------------------------------------------------------------------------------- 1 | import { Dimensions } from 'react-native'; 2 | 3 | const width = Dimensions.get('window').width; 4 | const height = Dimensions.get('window').height; 5 | 6 | export default { 7 | window: { 8 | width, 9 | height, 10 | }, 11 | isSmallDevice: width < 375, 12 | }; 13 | -------------------------------------------------------------------------------- /app/screens/Exchange.tsx: -------------------------------------------------------------------------------- 1 | import { View, Text, ViewStyle } from "react-native"; 2 | import React from "react"; 3 | 4 | const Exchange = ({ style }: { style?: ViewStyle }) => { 5 | return ( 6 | 7 | Exchange 8 | 9 | ); 10 | }; 11 | 12 | export default Exchange; 13 | -------------------------------------------------------------------------------- /app/screens/Profile.tsx: -------------------------------------------------------------------------------- 1 | import { StyleSheet, Text, View } from "react-native"; 2 | import React from "react"; 3 | 4 | const Profile = () => { 5 | return ( 6 | 7 | Profile 8 | 9 | ); 10 | }; 11 | 12 | export default Profile; 13 | 14 | const styles = StyleSheet.create({}); 15 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = function (api) { 2 | api.cache(true); 3 | return { 4 | presets: ["babel-preset-expo"], 5 | plugins: [ 6 | [ 7 | "module-resolver", 8 | { 9 | alias: { 10 | root: "./app", 11 | }, 12 | }, 13 | ], 14 | ], 15 | }; 16 | }; 17 | -------------------------------------------------------------------------------- /.expo-shared/assets.json: -------------------------------------------------------------------------------- 1 | { 2 | "e997a5256149a4b76e6bfd6cbf519c5e5a0f1d278a3d8fa1253022b03c90473b": true, 3 | "af683c96e0ffd2cf81287651c9433fa44debc1220ca7cb431fe482747f34a505": true, 4 | "12bb71342c6255bbf50437ec8f4441c083f47cdb74bd89160c15e4f43e52a1cb": true, 5 | "40b842e832070c58deac6aa9e08fa459302ee3f9da492c7e77d93d2fbf4a56fd": true 6 | } 7 | -------------------------------------------------------------------------------- /app/components/__tests__/StyledText-test.js: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import renderer from 'react-test-renderer'; 3 | 4 | import { MonoText } from '../StyledText'; 5 | 6 | it(`renders correctly`, () => { 7 | const tree = renderer.create(Snapshot test!).toJSON(); 8 | 9 | expect(tree).toMatchSnapshot(); 10 | }); 11 | -------------------------------------------------------------------------------- /app/atoms/index.ts: -------------------------------------------------------------------------------- 1 | import { atom } from "recoil"; 2 | import { IToken } from "types"; 3 | import { TOKENS } from "../constants/Dummies"; 4 | 5 | export const TradingTabState = atom({ 6 | key: "tradingTabState", 7 | default: 0, 8 | }); 9 | 10 | export const CurrentTokenState = atom({ 11 | key: "currentTokenState", 12 | default: TOKENS.Bitcoin, 13 | }); 14 | -------------------------------------------------------------------------------- /app/components/AppStatusBar.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { useIsFocused } from "@react-navigation/native"; 3 | import { StatusBar, StatusBarProps } from "expo-status-bar"; 4 | 5 | const AppStatusBar = (props: StatusBarProps) => { 6 | const isFocused = useIsFocused(); 7 | 8 | return isFocused ? : null; 9 | }; 10 | 11 | export default AppStatusBar; 12 | -------------------------------------------------------------------------------- /app/screens/Home.tsx: -------------------------------------------------------------------------------- 1 | import { View } from "react-native"; 2 | import React from "react"; 3 | import HeroBar from "../components/HeroBar"; 4 | import HomeTabs from "../components/HomeTabs"; 5 | import Layout from "../constants/Layout"; 6 | 7 | const Home = () => { 8 | return ( 9 | 10 | 11 | 12 | 13 | ); 14 | }; 15 | 16 | export default Home; 17 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "expo/tsconfig.base", 3 | "compilerOptions": { 4 | "jsx": "react-native", 5 | "strict": true, 6 | "esModuleInterop": true, 7 | "lib": ["DOM", "ESNext"], 8 | "moduleResolution": "node", 9 | "noEmit": true, 10 | "resolveJsonModule": true, 11 | "skipLibCheck": true, 12 | "target": "ESNext", 13 | "baseUrl": ".", 14 | "allowJs": true, 15 | "paths": { 16 | "app": ["./app/"] 17 | } 18 | }, 19 | "exclude": ["node_modules"], 20 | "include": ["./app/**/*.tsx", "./app/**/*.ts"] 21 | } 22 | -------------------------------------------------------------------------------- /app/components/TouchableText.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | View, 3 | Text, 4 | TouchableOpacity, 5 | ViewStyle, 6 | TextStyle, 7 | } from "react-native"; 8 | import React from "react"; 9 | 10 | const TouchableText = ({ 11 | style, 12 | textStyle, 13 | text, 14 | onPress, 15 | }: { 16 | text: string; 17 | style?: ViewStyle; 18 | textStyle?: TextStyle; 19 | onPress?: () => void; 20 | }) => { 21 | return ( 22 | 23 | {text} 24 | 25 | ); 26 | }; 27 | 28 | export default TouchableText; 29 | -------------------------------------------------------------------------------- /app/screens/Trade.tsx: -------------------------------------------------------------------------------- 1 | import { View, Text, Animated } from "react-native"; 2 | import React from "react"; 3 | import { useRecoilValue } from "recoil"; 4 | import { TradingTabState } from "../atoms"; 5 | import Swap from "./Swap"; 6 | import Exchange from "./Exchange"; 7 | 8 | const Trade = () => { 9 | const tradingTab = useRecoilValue(TradingTabState); 10 | 11 | return ( 12 | 13 | 14 | 15 | 16 | ); 17 | }; 18 | 19 | export default Trade; 20 | -------------------------------------------------------------------------------- /app/components/ModalHeaderRight.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { useNavigation } from "@react-navigation/native"; 3 | import TouchableText from "./TouchableText"; 4 | import { FONTS, SIZES } from "../constants/Assets"; 5 | import COLORS from "../constants/Colors"; 6 | 7 | const ModalHeaderRight = ({ text }: { text?: string }) => { 8 | const navigation = useNavigation(); 9 | 10 | return ( 11 | navigation.goBack()} 19 | /> 20 | ); 21 | }; 22 | 23 | export default ModalHeaderRight; 24 | -------------------------------------------------------------------------------- /App.tsx: -------------------------------------------------------------------------------- 1 | import { StatusBar } from "expo-status-bar"; 2 | import { useColorScheme } from "react-native"; 3 | import { SafeAreaProvider } from "react-native-safe-area-context"; 4 | import { RecoilRoot } from "recoil"; 5 | import useCachedResources from "./app/hooks/useCachedResources"; 6 | import Navigation from "./app/navigation"; 7 | 8 | export default function App() { 9 | const isLoadingComplete = useCachedResources(); 10 | const colorScheme = useColorScheme(); 11 | 12 | if (!isLoadingComplete) { 13 | return null; 14 | } 15 | 16 | return ( 17 | 18 | 19 | 20 | 21 | 22 | 23 | ); 24 | } 25 | -------------------------------------------------------------------------------- /app/constants/Colors.ts: -------------------------------------------------------------------------------- 1 | const tintColorLight = "#2f95dc"; 2 | const tintColorDark = "#fff"; 3 | 4 | export const COLOR_SCHEME = { 5 | light: { 6 | text: "#000", 7 | background: "#fff", 8 | tint: tintColorLight, 9 | tabIconDefault: "#ccc", 10 | tabIconSelected: tintColorLight, 11 | }, 12 | dark: { 13 | text: "#fff", 14 | background: "#000", 15 | tint: tintColorDark, 16 | tabIconDefault: "#ccc", 17 | tabIconSelected: tintColorDark, 18 | }, 19 | }; 20 | 21 | export default { 22 | primary: "#1d3557", 23 | secondary: "#7DFFF3", 24 | tertiary: "#9E80FF", 25 | white: "#fff", 26 | black50: "rgba(0,0,0,0.5)", 27 | green: "green", 28 | red: "#e63946", 29 | gray: "#f6f6f6", 30 | gray10: "#DADADA", 31 | }; 32 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # B-Wallet 3 | 4 | A digital asset wallet that helps store, sell, buy and earn digital tokens built with React Native 5 | 6 | ![B-Wallet - Pages](https://user-images.githubusercontent.com/24823152/165055461-aaddbb7f-782e-4eb2-b7de-d935d05fa65a.png) 7 | 8 | 9 | 10 | ### Technology Stack & Dependencies 11 | - React Native Expo 12 | - `Typescript` for type definitions 13 | - `react-native-tab-view` for custom tabs and animated tab views 14 | - `react-native-qrcode-svg` for generating QR code based on user address 15 | 16 | ### Install Dependencies 17 | ``` 18 | $ npm install 19 | ``` 20 | or 21 | ``` 22 | $ yarn install 23 | ``` 24 | 25 | ### Run project 26 | ``` 27 | $ npm start 28 | ``` 29 | 30 | or 31 | 32 | ``` 33 | $ yarn start 34 | ``` 35 | -------------------------------------------------------------------------------- /app.json: -------------------------------------------------------------------------------- 1 | { 2 | "expo": { 3 | "name": "B-Wallet", 4 | "slug": "B-Wallet", 5 | "version": "1.0.0", 6 | "orientation": "portrait", 7 | "icon": "./app/assets/images/icon.png", 8 | "scheme": "myapp", 9 | "userInterfaceStyle": "automatic", 10 | "splash": { 11 | "image": "./app/assets/images/splash.png", 12 | "resizeMode": "contain", 13 | "backgroundColor": "#ffffff" 14 | }, 15 | "updates": { 16 | "fallbackToCacheTimeout": 0 17 | }, 18 | "assetBundlePatterns": ["**/*"], 19 | "ios": { 20 | "supportsTablet": true 21 | }, 22 | "android": { 23 | "adaptiveIcon": { 24 | "foregroundImage": "./app/assets/images/adaptive-icon.png", 25 | "backgroundColor": "#ffffff" 26 | } 27 | }, 28 | "web": { 29 | "favicon": "./app/assets/images/favicon.png" 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /app/navigation/LinkingConfiguration.ts: -------------------------------------------------------------------------------- 1 | import { LinkingOptions } from "@react-navigation/native"; 2 | import * as Linking from "expo-linking"; 3 | 4 | import { RootStackParamList } from "../../types"; 5 | 6 | const linking: LinkingOptions = { 7 | prefixes: [Linking.makeUrl("/")], 8 | config: { 9 | screens: { 10 | Root: { 11 | screens: { 12 | HomeTab: { 13 | screens: { 14 | Home: "home", 15 | Send: "send", 16 | }, 17 | }, 18 | TradeTab: { 19 | screens: { 20 | Trade: "trade", 21 | Exchange: "exchange", 22 | }, 23 | }, 24 | ProfileTab: { 25 | screens: { 26 | Profile: "profile", 27 | }, 28 | }, 29 | }, 30 | }, 31 | Onboarding: "Onboarding", 32 | Modal: "modal", 33 | }, 34 | }, 35 | }; 36 | 37 | export default linking; 38 | -------------------------------------------------------------------------------- /app/helpers/index.ts: -------------------------------------------------------------------------------- 1 | import { DateTimeFormat, TChains } from "types"; 2 | import { TOKENS } from "../constants/Dummies"; 3 | 4 | export const truncate = (str: string) => { 5 | if (!str) { 6 | return ""; 7 | } 8 | return `${str.substring(0, 4)}...${str.substring( 9 | str.length - 4, 10 | str.length 11 | )}`; 12 | }; 13 | 14 | export const dateFormat = (date?: string) => { 15 | const format: DateTimeFormat = { 16 | month: "short", 17 | day: "numeric", 18 | year: "2-digit", 19 | }; 20 | 21 | if (date) { 22 | return new Date(date).toLocaleDateString("en-US", format); 23 | } 24 | 25 | return new Date().toLocaleDateString("en-US", format); 26 | }; 27 | 28 | export const convertTokenToDollars = (price: number, token: TChains) => { 29 | if (!token || !price) { 30 | return Number(0).toFixed(2).toLocaleString(); 31 | } 32 | const chain = TOKENS[token]; 33 | 34 | const total = price * chain.priceUSD; 35 | return new Intl.NumberFormat().format(total); 36 | }; 37 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Eli W. Basubi 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /app/assets/vectors/index.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | Feather, 3 | FontAwesome, 4 | Ionicons, 5 | MaterialCommunityIcons, 6 | } from "@expo/vector-icons"; 7 | import { StyleProp, TextStyle } from "react-native"; 8 | 9 | type TVector = "feather" | "fontawesome" | "ionicons" | "materialCI"; 10 | 11 | interface IProps { 12 | size?: number; 13 | style?: StyleProp; 14 | name?: any; 15 | color?: string; 16 | as?: TVector; 17 | onPress?: () => void; 18 | } 19 | 20 | const Vector = ({ 21 | size, 22 | style, 23 | name, 24 | color, 25 | as, 26 | onPress, 27 | }: IProps): JSX.Element => { 28 | const props = { 29 | name, 30 | size, 31 | style, 32 | color, 33 | onPress, 34 | }; 35 | 36 | if (as === "feather") { 37 | return ; 38 | } 39 | if (as === "ionicons") { 40 | return ; 41 | } 42 | 43 | if (as === "materialCI") { 44 | return ( 45 | 51 | ); 52 | } 53 | return ; 54 | }; 55 | 56 | export default Vector; 57 | -------------------------------------------------------------------------------- /app/components/StyledText.tsx: -------------------------------------------------------------------------------- 1 | import { FONTS } from "app/constants/Assets"; 2 | import { Text, TextProps } from "./Themed"; 3 | 4 | export const RegularText = (props: TextProps) => { 5 | return ( 6 | 7 | ); 8 | }; 9 | 10 | export const LightText = (props: TextProps) => { 11 | return ; 12 | }; 13 | 14 | export const BoldText = (props: TextProps) => { 15 | return ; 16 | }; 17 | 18 | export const MediumText = (props: TextProps) => { 19 | return ( 20 | 21 | ); 22 | }; 23 | 24 | export const SemiText = (props: TextProps) => { 25 | return ( 26 | 27 | ); 28 | }; 29 | 30 | export const MonoText = (props: TextProps) => { 31 | return ( 32 | 33 | ); 34 | }; 35 | 36 | export const LightMonoText = (props: TextProps) => { 37 | return ( 38 | 39 | ); 40 | }; 41 | -------------------------------------------------------------------------------- /app/components/Themed.tsx: -------------------------------------------------------------------------------- 1 | import { Text as DefaultText, View as DefaultView } from "react-native"; 2 | 3 | import { COLOR_SCHEME } from "../constants/Colors"; 4 | import useColorScheme from "../hooks/useColorScheme"; 5 | 6 | export const useThemeColor = ( 7 | props: { light?: string; dark?: string }, 8 | colorName: keyof typeof COLOR_SCHEME.light & keyof typeof COLOR_SCHEME.dark 9 | ) => { 10 | const theme = useColorScheme(); 11 | const colorFromProps = props[theme]; 12 | 13 | if (colorFromProps) { 14 | return colorFromProps; 15 | } 16 | return COLOR_SCHEME[theme][colorName]; 17 | }; 18 | 19 | type ThemeProps = { 20 | lightColor?: string; 21 | darkColor?: string; 22 | }; 23 | 24 | export type TextProps = ThemeProps & DefaultText["props"]; 25 | export type ViewProps = ThemeProps & DefaultView["props"]; 26 | 27 | export const Text = (props: TextProps) => { 28 | const { style, lightColor, darkColor, ...otherProps } = props; 29 | const color = useThemeColor({ light: lightColor, dark: darkColor }, "text"); 30 | 31 | return ; 32 | }; 33 | 34 | export const View = (props: ViewProps) => { 35 | const { style, lightColor, darkColor, ...otherProps } = props; 36 | const backgroundColor = useThemeColor( 37 | { light: lightColor, dark: darkColor }, 38 | "background" 39 | ); 40 | 41 | return ; 42 | }; 43 | -------------------------------------------------------------------------------- /app/hooks/useCachedResources.ts: -------------------------------------------------------------------------------- 1 | import { FontAwesome } from "@expo/vector-icons"; 2 | import * as Font from "expo-font"; 3 | import * as SplashScreen from "expo-splash-screen"; 4 | import { useEffect, useState } from "react"; 5 | 6 | export default function useCachedResources() { 7 | const [isLoadingComplete, setLoadingComplete] = useState(false); 8 | 9 | useEffect(() => { 10 | async function loadResourcesAndDataAsync() { 11 | try { 12 | SplashScreen.preventAutoHideAsync(); 13 | 14 | // Load fonts 15 | await Font.loadAsync({ 16 | ...FontAwesome.font, 17 | IBMPlexSansLight: require("../assets/fonts/IBMPlexSans-Light.ttf"), 18 | IBMPlexSansRegular: require("../assets/fonts/IBMPlexSans-Regular.ttf"), 19 | IBMPlexSansMedium: require("../assets/fonts/IBMPlexSans-Medium.ttf"), 20 | IBMPlexSansSemiBold: require("../assets/fonts/IBMPlexSans-SemiBold.ttf"), 21 | IBMPlexSansBold: require("../assets/fonts/IBMPlexSans-Bold.ttf"), 22 | IBMPlexMonoLight: require("../assets/fonts/IBMPlexMono-Light.ttf"), 23 | IBMPlexMonoRegular: require("../assets/fonts/IBMPlexMono-Regular.ttf"), 24 | IBMPlexMonoMedium: require("../assets/fonts/IBMPlexMono-Medium.ttf"), 25 | IBMPlexMonoBold: require("../assets/fonts/IBMPlexMono-Bold.ttf"), 26 | }); 27 | } catch (e) { 28 | // We might want to provide this error information to an error reporting service 29 | console.warn(e); 30 | } finally { 31 | setLoadingComplete(true); 32 | SplashScreen.hideAsync(); 33 | } 34 | } 35 | 36 | loadResourcesAndDataAsync(); 37 | }, []); 38 | 39 | return isLoadingComplete; 40 | } 41 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "b-wallet", 3 | "version": "1.0.0", 4 | "main": "node_modules/expo/AppEntry.js", 5 | "scripts": { 6 | "start": "expo start", 7 | "android": "expo start --android", 8 | "ios": "expo start --ios", 9 | "web": "expo start --web", 10 | "eject": "expo eject", 11 | "test": "jest --watchAll" 12 | }, 13 | "jest": { 14 | "preset": "jest-expo" 15 | }, 16 | "dependencies": { 17 | "@expo/vector-icons": "^12.0.0", 18 | "@react-navigation/bottom-tabs": "^6.0.5", 19 | "@react-navigation/native": "^6.0.2", 20 | "@react-navigation/native-stack": "^6.1.0", 21 | "expo": "~44.0.0", 22 | "expo-asset": "~8.4.4", 23 | "expo-constants": "~13.0.0", 24 | "expo-font": "~10.0.4", 25 | "expo-linking": "~3.0.0", 26 | "expo-splash-screen": "~0.14.0", 27 | "expo-status-bar": "~1.2.0", 28 | "expo-web-browser": "~10.1.0", 29 | "react": "17.0.1", 30 | "react-dom": "17.0.1", 31 | "react-native": "0.64.3", 32 | "react-native-dropdown-picker": "^5.4.0", 33 | "react-native-pager-view": "5.4.9", 34 | "react-native-qrcode-svg": "^6.1.2", 35 | "react-native-safe-area-context": "3.3.2", 36 | "react-native-screens": "~3.10.1", 37 | "react-native-svg": "^12.3.0", 38 | "react-native-tab-view": "^3.1.1", 39 | "react-native-web": "0.17.1", 40 | "recoil": "^0.7.2" 41 | }, 42 | "devDependencies": { 43 | "@babel/core": "^7.12.9", 44 | "@types/react": "~17.0.21", 45 | "@types/react-native": "~0.64.12", 46 | "jest": "^26.6.3", 47 | "jest-expo": "~44.0.1", 48 | "react-test-renderer": "17.0.1", 49 | "typescript": "~4.3.5" 50 | }, 51 | "private": true, 52 | "resolutions": { 53 | "@types/react": "17.0.44" 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /app/constants/Dummies.ts: -------------------------------------------------------------------------------- 1 | import { IToken, ITransaction, IUser, TChains } from "types"; 2 | import { IMAGES } from "./Assets"; 3 | 4 | export const USER_DATA: IUser = { 5 | username: "@satoshi", 6 | amount: 2, 7 | token: "Bitcoin", 8 | address: "0xE21603B45E2675fFeB9f20EED098e268219508CB", 9 | }; 10 | 11 | /** 12 | * The currency price in USD is based on the price grabbed on Google on 16 Apr 2022 at around 6PM 13 | */ 14 | export const TOKENS: Record = { 15 | Bitcoin: { 16 | id: 1, 17 | name: "Bitcoin", 18 | symbol: "BTC", 19 | icon: IMAGES.Bitcoin, 20 | rate: 2.45, 21 | priceUSD: 40455.1, 22 | status: "I", 23 | balance: 0, 24 | defaultSwapChain: "Binance", 25 | }, 26 | Ethereum: { 27 | id: 2, 28 | name: "Ethereum", 29 | symbol: "ETH", 30 | icon: IMAGES.Ethereum, 31 | rate: 5.5, 32 | priceUSD: 3050.78, 33 | status: "D", 34 | balance: 0, 35 | defaultSwapChain: "Bitcoin", 36 | }, 37 | Binance: { 38 | id: 3, 39 | name: "Binance", 40 | symbol: "BSC", 41 | icon: IMAGES.Binance, 42 | rate: 3.45, 43 | priceUSD: 416.58, 44 | status: "I", 45 | balance: 0, 46 | defaultSwapChain: "Polygon", 47 | }, 48 | Polygon: { 49 | id: 4, 50 | name: "Polygon", 51 | symbol: "MATIC", 52 | icon: IMAGES.Polygon, 53 | rate: 1.3, 54 | priceUSD: 1.39, 55 | status: "D", 56 | balance: 0, 57 | defaultSwapChain: "Ethereum", 58 | }, 59 | }; 60 | 61 | export const TRANSACTIONS: ITransaction[] = [ 62 | { 63 | id: 1, 64 | type: "stake", 65 | amount: 0, 66 | token: "Binance", 67 | date: "2022/04/16", 68 | }, 69 | { 70 | id: 2, 71 | type: "swap", 72 | amount: 100, 73 | from: "Ethereum", 74 | to: "Polygon", 75 | date: "2022/03/30", 76 | }, 77 | { 78 | id: 12, 79 | type: "buy", 80 | amount: 100, 81 | token: "Polygon", 82 | date: "2021/03/30", 83 | }, 84 | ]; 85 | -------------------------------------------------------------------------------- /app/constants/Assets.ts: -------------------------------------------------------------------------------- 1 | import { IWalletTab } from "types"; 2 | 3 | const Wallet = require("../assets/images/wallet.png"); 4 | const Bitcoin = require("../assets/images/bitcoin.png"); 5 | const Ethereum = require("../assets/images/ethereum.png"); 6 | const Binance = require("../assets/images/binance.png"); 7 | const Polygon = require("../assets/images/polygon.png"); 8 | 9 | export const IMAGES = { 10 | Wallet, 11 | Bitcoin, 12 | Ethereum, 13 | Binance, 14 | Polygon, 15 | }; 16 | 17 | const elevationNone = { 18 | shadowColor: "#000", 19 | shadowOffset: { 20 | height: 4, 21 | width: 0, 22 | }, 23 | shadowOpacity: 0.3, 24 | shadowRadius: 4.65, 25 | }; 26 | 27 | export const SHADOWS = { 28 | shadow8: { 29 | ...elevationNone, 30 | elevation: 8, 31 | }, 32 | shadow: { 33 | shadowColor: "#000", 34 | shadowOffset: { 35 | height: 0, 36 | width: 0, 37 | }, 38 | shadowOpacity: 0.2, 39 | shadowRadius: 4.65, 40 | elevation: 4, 41 | }, 42 | elevation0: { 43 | ...elevationNone, 44 | }, 45 | }; 46 | 47 | export const SIZES = { 48 | p50: 50, 49 | p40: 40, 50 | p30: 30, 51 | p20: 20, 52 | p15: 15, 53 | p6: 6, 54 | base: 8, 55 | small: 12, 56 | font: 14, 57 | medium: 16, 58 | large: 18, 59 | extraLarge: 24, 60 | half: "50%", 61 | full: "100%", 62 | }; 63 | 64 | export const FONTS = { 65 | light: "IBMPlexSansLight", 66 | regular: "IBMPlexSansRegular", 67 | medium: "IBMPlexSansMedium", 68 | semibold: "IBMPlexSansSemiBold", 69 | bold: "IBMPlexSansBold", 70 | monoLight: "IBMPlexMonoLight", 71 | monoRegular: "IBMPlexMonoRegular", 72 | monoMedium: "IBMPlexMonoMedium", 73 | monoBold: "IBMPlexMonoBold", 74 | }; 75 | 76 | export const Opacity = { 77 | opacity2: "rgba(0,0,0, 0.2)", 78 | }; 79 | 80 | export const WALLET_TOP_TABS: IWalletTab[] = [ 81 | { title: "Send", id: 1, route: "Send" }, 82 | { title: "Receive", id: 2, route: "Receive" }, 83 | { title: "Trade", id: 3, route: "Trade" }, 84 | ]; 85 | -------------------------------------------------------------------------------- /app/screens/Onboarding.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { Image, Text, TouchableOpacity, View } from "react-native"; 3 | import { SafeAreaView } from "react-native-safe-area-context"; 4 | import Colors from "../constants/Colors"; 5 | import AppStatusBar from "../components/AppStatusBar"; 6 | import { FONTS, IMAGES, SIZES } from "../constants/Assets"; 7 | import { useNavigation } from "@react-navigation/native"; 8 | import Vector from "../assets/vectors"; 9 | import styles from "../styles"; 10 | 11 | const Onboarding = () => { 12 | const navigation = useNavigation(); 13 | 14 | return ( 15 | 16 | 17 | 18 | 23 | 24 | 25 | 33 | Secure your coins and NFTs with ease. 34 | 35 | 44 | Buy, Sell, Earn digital assets and easily secure your funds. 45 | 46 | 47 | 48 | navigation.navigate("Root")}> 49 | 50 | 57 | Let's get started 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | ); 67 | }; 68 | 69 | export default Onboarding; 70 | -------------------------------------------------------------------------------- /app/styles/index.ts: -------------------------------------------------------------------------------- 1 | import { FONTS, SHADOWS, SIZES } from "../constants/Assets"; 2 | import { StyleSheet } from "react-native"; 3 | import COLORS from "../constants/Colors"; 4 | 5 | const styles = StyleSheet.create({ 6 | container: { 7 | backgroundColor: COLORS.primary, 8 | color: COLORS.white, 9 | flex: 1, 10 | }, 11 | startButton: { 12 | display: "flex", 13 | alignItems: "center", 14 | justifyContent: "space-between", 15 | flexDirection: "row", 16 | backgroundColor: COLORS.secondary, 17 | borderRadius: 30, 18 | width: "100%", 19 | padding: 10, 20 | paddingHorizontal: 25, 21 | }, 22 | startIcon: { 23 | height: 35, 24 | width: 35, 25 | borderRadius: 40, 26 | display: "flex", 27 | alignItems: "center", 28 | justifyContent: "center", 29 | backgroundColor: COLORS.tertiary, 30 | }, 31 | primaryColor: { 32 | backgroundColor: COLORS.primary, 33 | color: COLORS.white, 34 | }, 35 | secondaryColor: { 36 | backgroundColor: COLORS.secondary, 37 | color: COLORS.primary, 38 | }, 39 | tertiaryColor: { 40 | backgroundColor: COLORS.tertiary, 41 | color: COLORS.primary, 42 | }, 43 | cardWrapper: { 44 | flexDirection: "row", 45 | justifyContent: "space-between", 46 | alignItems: "center", 47 | marginBottom: 20, 48 | }, 49 | leadingIcon: { 50 | width: 50, 51 | height: 50, 52 | borderRadius: 70, 53 | justifyContent: "center", 54 | alignItems: "center", 55 | marginLeft: -SIZES.p20, 56 | ...SHADOWS.elevation0, 57 | }, 58 | cardMainWrapper: { 59 | margin: SIZES.p20, 60 | backgroundColor: COLORS.white, 61 | borderRadius: SIZES.small, 62 | borderWidth: 1, 63 | borderColor: COLORS.gray10, 64 | }, 65 | primaryButtonView: { 66 | backgroundColor: COLORS.primary, 67 | marginHorizontal: SIZES.p20, 68 | padding: SIZES.p15, 69 | borderRadius: SIZES.small, 70 | }, 71 | primaryButtonText: { 72 | textAlign: "center", 73 | color: COLORS.white, 74 | fontFamily: FONTS.semibold, 75 | fontSize: SIZES.font, 76 | }, 77 | headerStyle: { 78 | backgroundColor: COLORS.primary, 79 | }, 80 | selectorStyle: { borderColor: COLORS.gray10 }, 81 | dropdownContainerStyle: { 82 | width: SIZES.full, 83 | marginTop: 5, 84 | borderColor: COLORS.gray10, 85 | }, 86 | }); 87 | 88 | export default styles; 89 | -------------------------------------------------------------------------------- /app/components/HomeTabs.tsx: -------------------------------------------------------------------------------- 1 | import { View, Text } from "react-native"; 2 | import React, { useState } from "react"; 3 | import { TabView, SceneMap, TabBar } from "react-native-tab-view"; 4 | 5 | import { SIZES } from "../constants/Assets"; 6 | import styles from "../styles"; 7 | import Layout from "../constants/Layout"; 8 | import WalletAssets from "./WalletTokens"; 9 | import WalletTransactions from "./WalletTransactions"; 10 | import Colors from "../constants/Colors"; 11 | 12 | const ROUTES = [ 13 | { key: "tokens", title: "Tokens" }, 14 | { key: "transactions", title: "Transactions" }, 15 | ]; 16 | 17 | const renderScene = SceneMap({ 18 | tokens: WalletAssets, 19 | transactions: WalletTransactions, 20 | }); 21 | 22 | const HomeTabs = () => { 23 | const [index, setIndex] = useState(0); 24 | 25 | return ( 26 | 37 | ( 43 | ( 62 | 68 | {title} 69 | 70 | )} 71 | /> 72 | )} 73 | /> 74 | 75 | ); 76 | }; 77 | 78 | export default HomeTabs; 79 | -------------------------------------------------------------------------------- /types.tsx: -------------------------------------------------------------------------------- 1 | /** 2 | * Learn more about using TypeScript with React Navigation: 3 | * https://reactnavigation.org/docs/typescript/ 4 | */ 5 | 6 | import { BottomTabScreenProps } from "@react-navigation/bottom-tabs"; 7 | import { 8 | CompositeScreenProps, 9 | NavigatorScreenParams, 10 | } from "@react-navigation/native"; 11 | import { NativeStackScreenProps } from "@react-navigation/native-stack"; 12 | import { ImageSourcePropType } from "react-native"; 13 | 14 | declare global { 15 | namespace ReactNavigation { 16 | interface RootParamList extends RootStackParamList {} 17 | } 18 | } 19 | 20 | export type RootStackParamList = { 21 | Root: NavigatorScreenParams | undefined; 22 | Home: undefined; 23 | Onboarding: undefined; 24 | Modal: undefined; 25 | Exchange: undefined; 26 | Trade: undefined; 27 | Send: undefined; 28 | Receive: undefined; 29 | }; 30 | 31 | export type RootStackScreenProps = 32 | NativeStackScreenProps; 33 | 34 | export type RootTabParamList = { 35 | HomeTab: undefined; 36 | TradeTab: undefined; 37 | ProfileTab: undefined; 38 | Exchange: undefined; 39 | Modal: undefined; 40 | }; 41 | 42 | export type RootTabScreenProps = 43 | CompositeScreenProps< 44 | BottomTabScreenProps, 45 | NativeStackScreenProps 46 | >; 47 | 48 | export interface IUser { 49 | username: string; 50 | amount: number; 51 | address: string; 52 | token: TChains; 53 | } 54 | 55 | export type TChains = "Bitcoin" | "Ethereum" | "Binance" | "Polygon"; 56 | export type TSymbol = "BTC" | "ETH" | "BSC" | "MATIC"; 57 | export type TActivity = "swap" | "buy" | "send" | "stake" | "approve"; 58 | 59 | export interface IToken { 60 | id?: number; 61 | name: TChains; 62 | symbol: TSymbol; 63 | icon: ImageSourcePropType; 64 | rate?: number; 65 | priceUSD: number; 66 | balance?: number; 67 | status?: "I" | "D"; 68 | defaultSwapChain?: TChains; 69 | } 70 | 71 | export interface ITransaction { 72 | id?: number; 73 | type: TActivity; 74 | from?: TChains | string; 75 | to?: TChains | string; 76 | amount?: number; 77 | token?: TChains; 78 | date: string; 79 | } 80 | 81 | export interface DateTimeFormat { 82 | month: "short"; 83 | day: "numeric"; 84 | year: "2-digit"; 85 | } 86 | 87 | export interface IWalletTab { 88 | title: string; 89 | id: number; 90 | route: any; 91 | } 92 | -------------------------------------------------------------------------------- /app/components/TokenSelector.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | Image, 3 | TouchableWithoutFeedback, 4 | StyleProp, 5 | ViewStyle, 6 | } from "react-native"; 7 | import React, { useState } from "react"; 8 | import DropDownPicker from "react-native-dropdown-picker"; 9 | import { useRecoilState } from "recoil"; 10 | import { TChains } from "types"; 11 | import { TOKENS } from "../constants/Dummies"; 12 | import { SHADOWS, FONTS, SIZES } from "../constants/Assets"; 13 | import Colors from "../constants/Colors"; 14 | import { CurrentTokenState } from "../atoms"; 15 | 16 | const TOKEN_ITEMS = Object.values(TOKENS).map((token) => ({ 17 | value: token.name as string, 18 | label: token.name as string, 19 | icon: () => ( 20 | 21 | ), 22 | })); 23 | interface IProps { 24 | defaultValue?: TChains; 25 | style?: StyleProp; 26 | dropDownContainerStyle?: StyleProp; 27 | onChange?: (value: TChains) => void; 28 | } 29 | 30 | const TokenSelector = ({ 31 | style, 32 | defaultValue, 33 | dropDownContainerStyle, 34 | onChange, 35 | }: IProps) => { 36 | const [currentToken, setCurrentToken] = useRecoilState(CurrentTokenState); 37 | const [open, setOpen] = useState(false); 38 | const [value, setValue] = useState(defaultValue || currentToken.name); 39 | const [items, setItems] = useState(TOKEN_ITEMS); 40 | 41 | const onChangeChain = (chain: TChains) => { 42 | if (onChange) { 43 | onChange(chain); 44 | return; 45 | } 46 | setCurrentToken(TOKENS[chain]); 47 | }; 48 | 49 | return ( 50 | setOpen(false)}> 51 | setOpen(false) }} 60 | placeholder="Select a Token" 61 | containerStyle={{ zIndex: 10 }} 62 | disableBorderRadius={false} 63 | labelStyle={{ 64 | fontFamily: FONTS.regular, 65 | fontSize: SIZES.font, 66 | overflow: "hidden", 67 | }} 68 | labelProps={{ numberOfLines: 1 }} 69 | dropDownContainerStyle={dropDownContainerStyle} 70 | style={style} 71 | arrowIconStyle={{ opacity: 0.5 }} 72 | onChangeValue={(value) => onChangeChain?.(value as TChains)} 73 | /> 74 | 75 | ); 76 | }; 77 | 78 | TokenSelector.defaultProps = { 79 | style: { 80 | width: "50%", 81 | borderRadius: 100, 82 | paddingRight: SIZES.p20, 83 | backgroundColor: Colors.gray, 84 | ...SHADOWS.shadow8, 85 | }, 86 | dropDownContainerStyle: { 87 | width: "50%", 88 | borderWidth: 0, 89 | marginTop: 5, 90 | }, 91 | }; 92 | 93 | export default TokenSelector; 94 | -------------------------------------------------------------------------------- /app/screens/Receive.tsx: -------------------------------------------------------------------------------- 1 | import { View, Text, StyleSheet } from "react-native"; 2 | import React from "react"; 3 | import QRCode from "react-native-qrcode-svg"; 4 | import { useRecoilValue } from "recoil"; 5 | import { USER_DATA } from "../constants/Dummies"; 6 | import { FONTS, SIZES } from "../constants/Assets"; 7 | import COLORS from "../constants/Colors"; 8 | import TokenSelector from "../components/TokenSelector"; 9 | import { CurrentTokenState } from "../atoms"; 10 | import Vector from "../assets/vectors"; 11 | import styles from "../styles"; 12 | 13 | const Receive = () => { 14 | const currentToken = useRecoilValue(CurrentTokenState); 15 | 16 | return ( 17 | 18 | 29 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | {USER_DATA.address} 40 | 41 | 42 | 43 | 44 | 45 | Send only {currentToken.name} ({currentToken.symbol}) to this address. 46 | 47 | 48 | Sending any other coins may result in permanent loss. 49 | 50 | 51 | 52 | 60 | 61 | 62 | 63 | 64 | 70 | 71 | 72 | 73 | ); 74 | }; 75 | 76 | const innerStyles = StyleSheet.create({ 77 | iconWrapper: { 78 | height: 50, 79 | width: 50, 80 | borderRadius: SIZES.extraLarge, 81 | backgroundColor: COLORS.primary, 82 | alignItems: "center", 83 | justifyContent: "center", 84 | marginHorizontal: SIZES.large, 85 | }, 86 | icon: { 87 | padding: SIZES.font, 88 | color: COLORS.white, 89 | }, 90 | }); 91 | 92 | export default Receive; 93 | -------------------------------------------------------------------------------- /app/components/TradingModalHeader.tsx: -------------------------------------------------------------------------------- 1 | import { View, Text, TouchableOpacity, Animated } from "react-native"; 2 | import React from "react"; 3 | import { useRecoilState } from "recoil"; 4 | import COLORS from "../constants/Colors"; 5 | import { FONTS, Opacity, SIZES } from "../constants/Assets"; 6 | import { TradingTabState } from "../atoms"; 7 | import { useNavigation } from "@react-navigation/native"; 8 | 9 | const ROUTES = [ 10 | { title: "Swap", key: "trade" }, 11 | { title: "Exchange", key: "exchange" }, 12 | ]; 13 | 14 | const TradingModalHeader = () => { 15 | const navigation = useNavigation(); 16 | const [tabIndex, setTabIndex] = useRecoilState(TradingTabState); 17 | 18 | return ( 19 | 27 | 34 | 42 | 52 | {ROUTES.map(({ key, title }, position) => ( 53 | setTabIndex(position)} 56 | style={{ 57 | backgroundColor: 58 | position === tabIndex ? COLORS.primary : "transparent", 59 | justifyContent: "center", 60 | alignItems: "center", 61 | marginRight: position === 0 ? SIZES.base : 0, 62 | padding: SIZES.p6, 63 | width: 90, 64 | borderRadius: SIZES.p6, 65 | }} 66 | > 67 | 75 | {title} 76 | 77 | 78 | ))} 79 | 80 | 81 | navigation.goBack()}> 82 | 90 | Done 91 | 92 | 93 | 94 | 95 | ); 96 | }; 97 | 98 | export default TradingModalHeader; 99 | -------------------------------------------------------------------------------- /app/screens/Send.tsx: -------------------------------------------------------------------------------- 1 | import { View, Text, TextInput } from "react-native"; 2 | import React from "react"; 3 | import { useRecoilValue } from "recoil"; 4 | import { FONTS, SIZES } from "../constants/Assets"; 5 | import COLORS from "../constants/Colors"; 6 | import styles from "../styles"; 7 | import Vector from "../assets/vectors"; 8 | import TokenSelector from "../components/TokenSelector"; 9 | import { CurrentTokenState } from "../atoms"; 10 | import TouchableText from "../components/TouchableText"; 11 | 12 | const Send = () => { 13 | const currentToken = useRecoilValue(CurrentTokenState); 14 | 15 | return ( 16 | 17 | 18 | 22 | 23 | 31 | 32 | Balance {currentToken.balance} {currentToken.symbol} 33 | 34 | 35 | 43 | 44 | 45 | 52 | 58 | 59 | 60 | 68 | Send to 69 | 70 | 81 | 88 | 89 | 90 | 91 | 96 | 97 | ); 98 | }; 99 | 100 | export default Send; 101 | -------------------------------------------------------------------------------- /app/components/WalletTokens.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | View, 3 | Text, 4 | FlatList, 5 | Image, 6 | Pressable, 7 | ViewStyle, 8 | } from "react-native"; 9 | import React from "react"; 10 | import { useRecoilValue } from "recoil"; 11 | import { TOKENS } from "../constants/Dummies"; 12 | import { IToken } from "types"; 13 | import { FONTS, SHADOWS, SIZES } from "../constants/Assets"; 14 | import Colors from "../constants/Colors"; 15 | import { convertTokenToDollars } from "../helpers"; 16 | import { CurrentTokenState } from "../atoms"; 17 | 18 | interface IProps { 19 | showDetails?: boolean; 20 | onPress?: () => void; 21 | } 22 | 23 | const TokenCard = ({ 24 | item, 25 | showDetails = true, 26 | onPress, 27 | }: { item: IToken } & IProps) => { 28 | const hasDecreased = item.status === "D"; 29 | 30 | return ( 31 | 40 | 52 | 53 | 54 | 55 | 63 | {item.name} 64 | 65 | {showDetails && ( 66 | 67 | 75 | ${convertTokenToDollars(1, item.name)} 76 | 77 | 84 | {hasDecreased ? "-" : "+"} 85 | {item.rate}% 86 | 87 | 88 | )} 89 | 90 | {showDetails && ( 91 | 92 | 100 | {item.balance} {item.symbol} 101 | 102 | 103 | )} 104 | 105 | ); 106 | }; 107 | 108 | const WalletTokens = ({ showDetails, onPress }: IProps) => { 109 | const currentToken = useRecoilValue(CurrentTokenState); 110 | 111 | const tokens = Object.values(TOKENS).reduce((acc, token) => { 112 | if (token.name === currentToken.name) { 113 | return [token, ...acc]; 114 | } 115 | return [...acc, token]; 116 | }, [] as IToken[]); 117 | 118 | return ( 119 | ( 122 | 123 | )} 124 | showsVerticalScrollIndicator={false} 125 | keyExtractor={({ id }) => `${id}`} 126 | contentContainerStyle={{ paddingBottom: SIZES.p50 }} 127 | /> 128 | ); 129 | }; 130 | 131 | export default WalletTokens; 132 | -------------------------------------------------------------------------------- /app/screens/Swap.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | View, 3 | Text, 4 | TextInput, 5 | TouchableOpacity, 6 | Pressable, 7 | ViewStyle, 8 | } from "react-native"; 9 | import React, { useState } from "react"; 10 | import { SIZES, FONTS } from "../constants/Assets"; 11 | import COLORS from "../constants/Colors"; 12 | import TokenSelector from "../components/TokenSelector"; 13 | import Vector from "../assets/vectors"; 14 | import { convertTokenToDollars } from "../helpers"; 15 | import { TChains } from "types"; 16 | import { useRecoilValue } from "recoil"; 17 | import { CurrentTokenState } from "../atoms"; 18 | import styles from "../styles"; 19 | import TouchableText from "../components/TouchableText"; 20 | 21 | const SwapItem = ({ defaultChain }: { defaultChain?: TChains }) => { 22 | const [value, setValue] = useState(""); 23 | const [selectedToken, setSelectedToken] = useState(null); 24 | 25 | return ( 26 | 32 | 42 | 43 | setValue(text)} 53 | /> 54 | 55 | 70 | 71 | 79 | 85 | 86 | ${convertTokenToDollars(+value, selectedToken as TChains)} 87 | 88 | 89 | 90 | ); 91 | }; 92 | 93 | const Swap = ({ style }: { style?: ViewStyle }) => { 94 | const currentToken = useRecoilValue(CurrentTokenState); 95 | 96 | return ( 97 | 98 | 99 | 100 | 101 | 102 | 109 | 124 | 130 | 131 | 132 | 133 | 140 | 141 | 142 | 147 | 148 | ); 149 | }; 150 | 151 | export default Swap; 152 | -------------------------------------------------------------------------------- /app/components/WalletTransactions.tsx: -------------------------------------------------------------------------------- 1 | import { View, Text, FlatList, Image, TextProps } from "react-native"; 2 | import React from "react"; 3 | import { TOKENS, TRANSACTIONS } from "../constants/Dummies"; 4 | import styles from "../styles"; 5 | import { ITransaction, TActivity, TChains } from "types"; 6 | import { FONTS, SHADOWS, SIZES } from "../constants/Assets"; 7 | import Vector from "../assets/vectors"; 8 | import Colors from "../constants/Colors"; 9 | import { dateFormat, convertTokenToDollars } from "../helpers"; 10 | 11 | interface IProps { 12 | item: ITransaction; 13 | } 14 | 15 | const TransactionIcon = ({ 16 | type, 17 | token, 18 | destination, 19 | }: { 20 | type: TActivity; 21 | token?: TChains; 22 | destination?: TChains; 23 | }) => { 24 | if (type === "swap" && token && destination) { 25 | return ( 26 | 27 | 28 | 35 | 36 | ); 37 | } 38 | 39 | if (["stake", "buy", "send"].includes(type) && token) { 40 | const hasDecreased = ["send", "stake"].includes(type); 41 | 42 | return ( 43 | 44 | 45 | 54 | 60 | 61 | 62 | ); 63 | } 64 | 65 | return ( 66 | 72 | ); 73 | }; 74 | 75 | const TransactionTitle = ({ 76 | type, 77 | amount, 78 | token, 79 | destination, 80 | style, 81 | }: { 82 | type: TActivity; 83 | amount: number; 84 | token?: TChains; 85 | destination?: TChains; 86 | } & TextProps) => { 87 | if (type === "swap" && amount && destination && token) { 88 | return ( 89 | 90 | Swap {Number(amount).toFixed(2)} {TOKENS[token].symbol} for{" "} 91 | {Number(TOKENS[destination]?.balance)} {TOKENS[destination].symbol} 92 | 93 | ); 94 | } 95 | 96 | return {type}; 97 | }; 98 | 99 | const TransactionCard = ({ item }: IProps) => { 100 | return ( 101 | 102 | 114 | 119 | 120 | 121 | 133 | 134 | {dateFormat(item.date)} 135 | 136 | 137 | {!["approve", "swap"].includes(item.type) && item.token && ( 138 | 139 | 146 | {item.amount} {TOKENS[item.token]?.symbol} 147 | 148 | 154 | ${convertTokenToDollars(item.amount || 0, item.token)} 155 | 156 | 157 | )} 158 | 159 | ); 160 | }; 161 | 162 | const WalletTransactions = () => { 163 | return ( 164 | `${id}`} 169 | contentContainerStyle={{ paddingBottom: SIZES.p50 }} 170 | /> 171 | ); 172 | }; 173 | 174 | export default WalletTransactions; 175 | -------------------------------------------------------------------------------- /app/navigation/index.tsx: -------------------------------------------------------------------------------- 1 | import { createBottomTabNavigator } from "@react-navigation/bottom-tabs"; 2 | import { 3 | NavigationContainer, 4 | DefaultTheme, 5 | DarkTheme, 6 | } from "@react-navigation/native"; 7 | import { createNativeStackNavigator } from "@react-navigation/native-stack"; 8 | import * as React from "react"; 9 | import { Feather } from "@expo/vector-icons"; 10 | import { ColorSchemeName, TouchableOpacity } from "react-native"; 11 | 12 | import Colors from "../constants/Colors"; 13 | import useColorScheme from "../hooks/useColorScheme"; 14 | import Home from "../screens/Home"; 15 | import Onboarding from "../screens/Onboarding"; 16 | import Profile from "../screens/Profile"; 17 | import { RootStackParamList, RootTabParamList } from "../../types"; 18 | import LinkingConfiguration from "./LinkingConfiguration"; 19 | import Vector from "../assets/vectors"; 20 | import { SHADOWS } from "../constants/Assets"; 21 | import { COLOR_SCHEME } from "../constants/Colors"; 22 | import Trade from "../screens/Trade"; 23 | import TradingModalHeader from "../components/TradingModalHeader"; 24 | import Send from "../screens/Send"; 25 | import styles from "../styles"; 26 | import ModalHeaderRight from "../components/ModalHeaderRight"; 27 | import Receive from "../screens/Receive"; 28 | 29 | export default function Navigation({ 30 | colorScheme, 31 | }: { 32 | colorScheme: ColorSchemeName; 33 | }) { 34 | return ( 35 | 39 | 40 | 41 | ); 42 | } 43 | 44 | const Stack = createNativeStackNavigator(); 45 | 46 | const RootNavigator = () => { 47 | return ( 48 | 52 | 57 | 62 | 63 | , 68 | }} 69 | > 70 | 71 | 72 | , 79 | }} 80 | > 81 | 82 | 83 | 84 | 85 | ); 86 | }; 87 | 88 | const BottomTab = createBottomTabNavigator(); 89 | 90 | const BottomTabNavigator = () => { 91 | const colorScheme = useColorScheme(); 92 | 93 | return ( 94 | 100 | ({ 104 | title: "Home", 105 | headerShown: false, 106 | tabBarShowLabel: false, 107 | tabBarIcon: ({ color }) => , 108 | })} 109 | /> 110 | ({ 114 | title: "Trade", 115 | headerShown: false, 116 | tabBarShowLabel: false, 117 | tabBarIcon: () => ( 118 | navigation.navigate("Trade")} 120 | style={{ 121 | top: -20, 122 | height: 70, 123 | width: 70, 124 | justifyContent: "center", 125 | alignItems: "center", 126 | borderRadius: 50, 127 | backgroundColor: Colors.primary, 128 | ...SHADOWS.shadow8, 129 | }} 130 | > 131 | 142 | 143 | ), 144 | })} 145 | /> 146 | , 152 | }} 153 | /> 154 | 155 | ); 156 | }; 157 | 158 | function TabBarIcon(props: { 159 | name: React.ComponentProps["name"]; 160 | color: string; 161 | }) { 162 | return ( 163 | 164 | ); 165 | } 166 | -------------------------------------------------------------------------------- /app/components/HeroBar.tsx: -------------------------------------------------------------------------------- 1 | import { View, Text, TouchableOpacity } from "react-native"; 2 | import React, { useState } from "react"; 3 | import { SafeAreaView } from "react-native-safe-area-context"; 4 | import { useNavigation } from "@react-navigation/native"; 5 | import { 6 | FONTS, 7 | Opacity, 8 | SHADOWS, 9 | SIZES, 10 | WALLET_TOP_TABS, 11 | } from "../constants/Assets"; 12 | import styles from "../styles"; 13 | import Vector from "../assets/vectors"; 14 | import { USER_DATA } from "../constants/Dummies"; 15 | import { convertTokenToDollars, truncate } from "../helpers"; 16 | import Colors from "../constants/Colors"; 17 | import TokenSelector from "./TokenSelector"; 18 | import { useRecoilValue } from "recoil"; 19 | import { CurrentTokenState } from "../atoms"; 20 | 21 | const HeroBar = () => { 22 | const [showAmount, setShowAmount] = useState(true); 23 | const currentToken = useRecoilValue(CurrentTokenState); 24 | 25 | const navigation = useNavigation(); 26 | 27 | const onToggleAmount = () => { 28 | setShowAmount((state) => !state); 29 | }; 30 | 31 | return ( 32 | 33 | 41 | 42 | 50 | 51 | 52 | 62 | 68 | 69 | 70 | 71 | 78 | 85 | {USER_DATA.username} 86 | 87 | 88 | 96 | 107 | {showAmount 108 | ? `US ${convertTokenToDollars( 109 | USER_DATA.amount, 110 | currentToken.name 111 | )}` 112 | : "******"} 113 | 114 | 115 | 116 | 122 | 123 | 124 | 125 | 133 | 134 | {truncate(USER_DATA.address)} 135 | 136 | 137 | 138 | 139 | 152 | {WALLET_TOP_TABS.map((tab, index) => ( 153 | navigation.navigate(tab.route)} 165 | > 166 | 173 | {tab.title} 174 | 175 | 176 | ))} 177 | 178 | 179 | 180 | 181 | ); 182 | }; 183 | 184 | export default HeroBar; 185 | --------------------------------------------------------------------------------