From bd8f1fe0ccf435cae41384875d3c11ec263c16db Mon Sep 17 00:00:00 2001 From: Leslie Owusu-Appiah Date: Fri, 25 Jun 2021 14:42:23 +0100 Subject: [PATCH] feat(store): use recoil --- .eslintrc.js | 3 +- .prettierrc.js | 2 +- __tests__/Test.test.tsx | 28 ++- __tests__/__snapshots__/Test.test.tsx.snap | 18 +- commitlint.config.js | 4 +- declarations.d.ts | 7 +- index.js | 2 +- jest.config.js | 38 ++-- jest.setup.js | 2 +- metro.config.js | 8 +- package.json | 1 + react-test.ts | 9 - rn-cli.config.js | 14 +- src/@types/bookmarks.d.ts | 12 +- src/@types/env.d.ts | 2 +- src/@types/styled.d.ts | 38 ++-- src/App.tsx | 24 +-- src/AppNavigator.tsx | 101 ++++------ src/components/BrowserWebView.tsx | 100 +++++---- src/components/ButtonIcon.tsx | 64 +++--- .../Navigation/NavigationButtons.tsx | 189 +++++++----------- .../Navigation/NavigationComboInput.tsx | 105 +++++----- src/components/Navigation/index.tsx | 11 +- src/components/Test.tsx | 10 +- src/data/bookmarks.data.ts | 60 +++--- src/protocols/ethereum/index.ts | 118 +++++------ src/store/atoms.ts | 51 +++++ src/store/ethereum.reducer.ts | 98 --------- src/store/index.ts | 11 - src/store/navigation.reducer.ts | 95 --------- src/store/reducers.ts | 15 -- src/store/settings.reducer.ts | 34 ---- src/themes/dark.ts | 36 ++-- src/themes/light.ts | 36 ++-- src/utils/appearance.ts | 38 ++-- src/utils/url.ts | 23 ++- src/views/Browser.tsx | 39 ++-- src/views/Settings.tsx | 133 +++++------- yarn.lock | 53 +++++ 39 files changed, 672 insertions(+), 960 deletions(-) delete mode 100644 react-test.ts create mode 100644 src/store/atoms.ts delete mode 100644 src/store/ethereum.reducer.ts delete mode 100644 src/store/index.ts delete mode 100644 src/store/navigation.reducer.ts delete mode 100644 src/store/reducers.ts delete mode 100644 src/store/settings.reducer.ts diff --git a/.eslintrc.js b/.eslintrc.js index db7e98ee..cd62c780 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -1,7 +1,6 @@ module.exports = { root: true, - extends: '@react-native-community', + extends: ['@react-native-community', 'plugin:jest/recommended'], parser: '@typescript-eslint/parser', plugins: ['@typescript-eslint'], }; - diff --git a/.prettierrc.js b/.prettierrc.js index dc65c364..2e6bc15c 100644 --- a/.prettierrc.js +++ b/.prettierrc.js @@ -4,6 +4,6 @@ module.exports = { singleQuote: true, trailingComma: 'all', bracketSpacing: true, - semi: false + semi: true }; diff --git a/__tests__/Test.test.tsx b/__tests__/Test.test.tsx index 6ce3b6bd..ce94a64b 100644 --- a/__tests__/Test.test.tsx +++ b/__tests__/Test.test.tsx @@ -1,17 +1,15 @@ -import * as React from 'react'; -import * as ReactNative from 'react-native'; +import React from 'react'; import renderer from 'react-test-renderer'; import Test from '../src/components/Test'; -import App from '../App'; +// import App from '../App'; -import { ButtonIcon } from '../src/components/ButtonIcon'; -import IconOptions from 'src/assets/icons/icon-options.svg'; - -import { shallow, mount } from 'enzyme' -import 'jest-enzyme' -import 'jest-styled-components' +// import { ButtonIcon } from '../src/components/ButtonIcon'; +// import IconOptions from 'src/assets/icons/icon-options.svg'; +// import { shallow, mount } from 'enzyme'; +import 'jest-enzyme'; +import 'jest-styled-components'; // const button = { // type: 'options', @@ -20,10 +18,9 @@ import 'jest-styled-components' // accessibilityLabel: 'More Options' // } - // it('icon button renders', () => { // const { type, iconImage, accessibilityLabel, onPress } = button; -// const component = +// const component = // { - const component = - const tree = renderer.create(component).toJSON() - expect(tree).toMatchSnapshot() -}); \ No newline at end of file + const component = ; + const tree = renderer.create(component).toJSON(); + expect(tree).toMatchSnapshot(); +}); diff --git a/__tests__/__snapshots__/Test.test.tsx.snap b/__tests__/__snapshots__/Test.test.tsx.snap index c904124d..367329e6 100644 --- a/__tests__/__snapshots__/Test.test.tsx.snap +++ b/__tests__/__snapshots__/Test.test.tsx.snap @@ -1,15 +1,25 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`Test component renders 1`] = ` - - Testing, testing, 1, 2, 3 - + + Testing, testing, 1, 2, 3 + + `; diff --git a/commitlint.config.js b/commitlint.config.js index 4c335439..84dcb122 100644 --- a/commitlint.config.js +++ b/commitlint.config.js @@ -1,3 +1,3 @@ module.exports = { - extends: ['@commitlint/config-conventional'] -}; \ No newline at end of file + extends: ['@commitlint/config-conventional'], +}; diff --git a/declarations.d.ts b/declarations.d.ts index 64d4b422..870b7922 100644 --- a/declarations.d.ts +++ b/declarations.d.ts @@ -1,5 +1,6 @@ +import React from 'react'; declare module '*.svg' { - import { SvgProps } from 'react-native-svg' - const content: React.FC - export default content + import { SvgProps } from 'react-native-svg'; + const content: React.FC; + export default content; } diff --git a/index.js b/index.js index 127e8994..200879b7 100644 --- a/index.js +++ b/index.js @@ -1,4 +1,4 @@ -import 'react-native-get-random-values' +import 'react-native-get-random-values'; import { AppRegistry } from 'react-native'; import App from './src/App'; diff --git a/jest.config.js b/jest.config.js index 62af9be5..8b81c84f 100644 --- a/jest.config.js +++ b/jest.config.js @@ -1,20 +1,20 @@ module.exports = { - preset: 'react-native', - moduleNameMapper: { - '^@/(.*)$': '/$1', - }, - moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'], - testRegex: '(/__tests__/.*|\\.(test|spec))\\.(ts|tsx|js)$', - testPathIgnorePatterns: ['\\.snap$', '/node_modules/'], - transformIgnorePatterns: ['node_modules/?!(static-container)'], - cacheDirectory: '.jest/cache', - globals: { - 'ts-jest': { - isolatedModules: true, - }, - }, - clearMocks: true, - // setupFiles: ["/jest.setup.js"], - setupFilesAfterEnv: ['./jest.setup.js'], - collectCoverageFrom: ['src/**/*.{ts,tsx}', '!**/node_modules/**'], -}; \ No newline at end of file + preset: 'react-native', + moduleNameMapper: { + '^@/(.*)$': '/$1', + }, + moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'], + testRegex: '(/__tests__/.*|\\.(test|spec))\\.(ts|tsx|js)$', + testPathIgnorePatterns: ['\\.snap$', '/node_modules/'], + transformIgnorePatterns: ['node_modules/?!(static-container)'], + cacheDirectory: '.jest/cache', + globals: { + 'ts-jest': { + isolatedModules: true, + }, + }, + clearMocks: true, + // setupFiles: ["/jest.setup.js"], + setupFilesAfterEnv: ['./jest.setup.js'], + collectCoverageFrom: ['src/**/*.{ts,tsx}', '!**/node_modules/**'], +}; diff --git a/jest.setup.js b/jest.setup.js index af7ce866..1e2fd8a1 100644 --- a/jest.setup.js +++ b/jest.setup.js @@ -16,4 +16,4 @@ jest.mock('react-native/Libraries/Animated/NativeAnimatedHelper'); // React Native Share jest.mock('react-native-share', () => ({ default: jest.fn(), -})); \ No newline at end of file +})); diff --git a/metro.config.js b/metro.config.js index f5608b4e..fdc5964c 100644 --- a/metro.config.js +++ b/metro.config.js @@ -1,4 +1,4 @@ -const { getDefaultConfig } = require('metro-config') +const { getDefaultConfig } = require('metro-config'); module.exports = (async () => { const { @@ -9,8 +9,8 @@ module.exports = (async () => { babelTransformerPath: require.resolve('react-native-svg-transformer'), }, resolver: { - assetExts: assetExts.filter(ext => ext !== 'svg'), + assetExts: assetExts.filter((ext) => ext !== 'svg'), sourceExts: [...sourceExts, 'svg'], }, - } -})() + }; +})(); diff --git a/package.json b/package.json index 1504e313..668eda9b 100644 --- a/package.json +++ b/package.json @@ -57,6 +57,7 @@ "babel-plugin-transform-inline-environment-variables": "^0.4.3", "enzyme": "^3.11.0", "eslint": "^7.22.0", + "eslint-plugin-jest": "^24.3.6", "husky": "^6.0.0", "jest": "^27.0.4", "jest-enzyme": "^7.1.2", diff --git a/react-test.ts b/react-test.ts deleted file mode 100644 index 1bfe1e1e..00000000 --- a/react-test.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { View, Text } from 'react-native'; - -export const Test = () => { - return ( - - Hello humans - - ) -} \ No newline at end of file diff --git a/rn-cli.config.js b/rn-cli.config.js index 35e2ad70..f42e993c 100644 --- a/rn-cli.config.js +++ b/rn-cli.config.js @@ -1,8 +1,8 @@ module.exports = { - getTransformModulePath() { - return require.resolve('react-native-typescript-transformer'); - }, - getSourceExts() { - return ['ts', 'tsx']; - } -}; \ No newline at end of file + getTransformModulePath() { + return require.resolve('react-native-typescript-transformer'); + }, + getSourceExts() { + return ['ts', 'tsx']; + }, +}; diff --git a/src/@types/bookmarks.d.ts b/src/@types/bookmarks.d.ts index 9afeb373..03c135ea 100644 --- a/src/@types/bookmarks.d.ts +++ b/src/@types/bookmarks.d.ts @@ -1,7 +1,7 @@ export type Bookmark = { - "displayName": String - "description": String - "protocol": "http" | "https" | "ipfs" | "hyper" | "dat" - "location": String - "tags": String[] -} \ No newline at end of file + displayName: String; + description: String; + protocol: 'http' | 'https' | 'ipfs' | 'hyper' | 'dat'; + location: String; + tags: String[]; +}; diff --git a/src/@types/env.d.ts b/src/@types/env.d.ts index d93e5d0f..ebdc6924 100644 --- a/src/@types/env.d.ts +++ b/src/@types/env.d.ts @@ -4,4 +4,4 @@ export type EnvType = { ETH_WALLET_ADDRESS: String; ETH_WALLET_SECRET_SEED_PHRASE: String; ETH_WALLET_SECRET_PRIVATE_KEY: String; -} \ No newline at end of file +}; diff --git a/src/@types/styled.d.ts b/src/@types/styled.d.ts index d366b81b..3b636f60 100644 --- a/src/@types/styled.d.ts +++ b/src/@types/styled.d.ts @@ -1,21 +1,21 @@ -import 'styled-components/native' +import 'styled-components/native'; declare module 'styled-components/native' { - export interface DefaultTheme { - colors: { - scheme: string - text: string - background: string - debug: string - }, - ui: { - background: string - foreground: string - icon: string - }, - addressBar: { - background: string - color: string - } - } -} \ No newline at end of file + export interface DefaultTheme { + colors: { + scheme: string; + text: string; + background: string; + debug: string; + }; + ui: { + background: string; + foreground: string; + icon: string; + }; + addressBar: { + background: string; + color: string; + }; + } +} diff --git a/src/App.tsx b/src/App.tsx index 5dcda281..27999e72 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,24 +1,22 @@ -import React from 'react' -import { Provider } from 'react-redux' -import { ThemeProvider } from 'styled-components/native' +import React from 'react'; +import { RecoilRoot } from 'recoil'; +import { ThemeProvider } from 'styled-components/native'; -import store from 'src/store/index' +import AppNavigator from 'src/AppNavigator'; -import AppNavigator from 'src/AppNavigator' +import lightTheme from 'src/themes/light'; +import darkTheme from 'src/themes/dark'; -import lightTheme from 'src/themes/light' -import darkTheme from 'src/themes/dark' - -import { isDarkMode } from 'src/utils/appearance' +import { IsDarkMode } from 'src/utils/appearance'; export default function App() { return ( <> - - + + - + - ) + ); } diff --git a/src/AppNavigator.tsx b/src/AppNavigator.tsx index 21213a2a..5bd4dc2f 100644 --- a/src/AppNavigator.tsx +++ b/src/AppNavigator.tsx @@ -1,75 +1,43 @@ -import React, { useEffect } from 'react' -import { useSelector, useDispatch } from 'react-redux' +import React, { useEffect } from 'react'; +import { useSetRecoilState } from 'recoil'; -import { NavigationContainer } from '@react-navigation/native' -import { createStackNavigator, TransitionPresets } from '@react-navigation/stack' +import { NavigationContainer } from '@react-navigation/native'; +import { + createStackNavigator, + TransitionPresets, +} from '@react-navigation/stack'; -import { useTheme } from 'styled-components/native' +import { env } from 'environments/.env.development'; -import { env } from 'environments/.env.development' +import { ethereumState } from 'src/store/atoms'; -import { AppState } from 'src/store/reducers' -import { - ETH_WALLET_ADDRESS, - ETH_WALLET_BALANCE, - ETH_WALLET_HEX, - ETH_WALLET_ENS -} from 'src/store/ethereum.reducer' +import { Ethereum } from 'src/protocols/ethereum'; -import { Ethereum } from 'src/protocols/ethereum' - -import Browser from 'src/views/Browser' -import Settings from 'src/views/Settings' -import { ethers } from 'ethers' - - -const Stack = createStackNavigator() +import Browser from 'src/views/Browser'; +import Settings from 'src/views/Settings'; +const Stack = createStackNavigator(); export default function AppNavigator() { - - const theme = useTheme() - - const ethereumState = useSelector((state: AppState) => state.ethereum) - const dispatch = useDispatch() + const setEthereumState = useSetRecoilState(ethereumState); useEffect(() => { - - const wallet = new Ethereum() - wallet.walletPrivateKey = `${env.ETH_WALLET_SECRET_PRIVATE_KEY}` - wallet.walletAddress = `${(env.ETH_WALLET_ADDRESS).toLowerCase()}` - - // Store users wallet address - dispatch({ - type: ETH_WALLET_ADDRESS, - ethWalletAddress: wallet.walletAddress - }); - - - (async() => { - - // Set hex and ENS wallet addresses - dispatch({ - type: ETH_WALLET_ENS, - ethWalletEns: wallet.isAddressHex() ? - await wallet.getEnsFromAddress() : - wallet.walletAddress - }); - dispatch({ - type: ETH_WALLET_HEX, - ethWalletHex: wallet.isAddressENS() ? - await wallet.getAddressFromEns() : - wallet.walletAddress - }); - - dispatch({ - type: ETH_WALLET_BALANCE, - ethWalletBalance: await wallet.getBalance() - }); - + const wallet = new Ethereum(); + wallet.walletPrivateKey = `${env.ETH_WALLET_SECRET_PRIVATE_KEY}`; + wallet.walletAddress = `${env.ETH_WALLET_ADDRESS.toLowerCase()}`; + (async () => { + setEthereumState({ + ethWalletAddress: wallet.walletAddress, + ethWalletHex: wallet.isAddressENS() + ? await wallet.getAddressFromEns() + : wallet.walletAddress, + ethWalletEns: wallet.isAddressHex() + ? await wallet.getEnsFromAddress() + : wallet.walletAddress, + ethWalletBalance: await wallet.getBalance(), + }); })(); - }, []); - + }); return ( @@ -77,17 +45,16 @@ export default function AppNavigator() { initialRouteName="Browser" screenOptions={{ cardStyle: { - backgroundColor: 'transparent' + backgroundColor: 'transparent', }, cardOverlayEnabled: true, cardShadowEnabled: true, gestureEnabled: true, - animationEnabled: true, + animationEnabled: true, ...TransitionPresets.ModalPresentationIOS, }} mode="modal" - headerMode="screen" - > + headerMode="screen"> - ) -} \ No newline at end of file + ); +} diff --git a/src/components/BrowserWebView.tsx b/src/components/BrowserWebView.tsx index 43669ec7..61a55eec 100644 --- a/src/components/BrowserWebView.tsx +++ b/src/components/BrowserWebView.tsx @@ -1,19 +1,10 @@ -import React, { useRef, useEffect, useState } from 'react' -import { WebView } from 'react-native-webview' -import { useSelector, useDispatch } from 'react-redux' -import styled from 'styled-components/native' - -import { AppState } from 'src/store/reducers' -import { - WEBVIEW_REF, - WEBVIEW_STATE, - URL_INPUT -} from 'src/store/navigation.reducer' - - +import React, { useRef, useEffect, useState } from 'react'; +import { useRecoilState } from 'recoil'; +import { WebView } from 'react-native-webview'; +import { navigationState } from 'src/store/atoms'; +import styled from 'styled-components/native'; export default function BrowserWebView() { - const config = { detectorTypes: 'all', allowStorage: true, @@ -21,26 +12,30 @@ export default function BrowserWebView() { allowCookies: true, allowLocation: true, allowCaching: true, - defaultSearchEngine: 'duck' - } + defaultSearchEngine: 'duck', + }; - const webViewRef = useRef(null) + const webViewRef = useRef(null); - - const navigation = useSelector((state: AppState) => state.navigation) - const dispatch = useDispatch() + const [refreshing, setRefreshing] = useState(false); - const [ refreshing, setRefreshing ] = useState(false) + const [navState, setNavState] = useRecoilState(navigationState); useEffect(() => { - dispatch({ type: WEBVIEW_REF, webViewRef }); - }, []); + setNavState((previous) => { + return { + ...previous, + webViewRef: webViewRef, + }; + }); + }); const webViewReload = () => { setRefreshing(true); - navigation.webViewRef.current.reload(); - } - + if (navState.webViewRef) { + navState.webViewRef.current?.reload(); + } + }; return ( <> @@ -48,31 +43,36 @@ export default function BrowserWebView() { showsVerticalScrollIndicator={false} showsHorizontalScrollIndicator={false} refreshControl={ - + }> { }} - onNavigationStateChange={(navState: { url: string }) => { - console.log('onNavigationStateChange', navState) - dispatch({ type: URL_INPUT, urlInput: navState.url }) - dispatch({ type: WEBVIEW_STATE, webViewState: navState }) + source={{ uri: navState.urlCurrent }} + onLoadStart={() => {}} + onNavigationStateChange={(currentNavState) => { + console.log('onNavigationStateChange', currentNavState); + setNavState((previous) => { + return { + ...previous, + urlInput: currentNavState.url, + webViewState: currentNavState, + }; + }); }} onLoadEnd={() => { - setRefreshing(false) + setRefreshing(false); }} onContentProcessDidTerminate={(syntheticEvent) => { - const { nativeEvent } = syntheticEvent - console.warn('Content process terminated, reloading', nativeEvent) - // TODO - Show message in UI that the webview crashed - webViewReload() + const { nativeEvent } = syntheticEvent; + console.warn( + 'Content process terminated, reloading', + nativeEvent, + ); + // TODO - Show message in UI that the webview crashed + webViewReload(); }} startInLoadingState domStorageEnabled={config.allowStorage} @@ -85,29 +85,23 @@ export default function BrowserWebView() { - ) + ); } - - const ScrollView = styled.ScrollView.attrs(() => ({ contentContainerStyle: { flex: 1, - } + }, }))` /* Note: experiment with dynamic container heights /*margin-top: 46px;*/ /*margin-bottom: 134px;*/ -` +`; -const RefreshControl = styled.RefreshControl`` +const RefreshControl = styled.RefreshControl``; const SafeAreaView = styled.SafeAreaView` flex: 1; -` - -const WebViewContainer = styled(WebView)`` +`; -const Text = styled.Text` - color: ${props => props.theme.colors.text}; -` +const WebViewContainer = styled(WebView)``; diff --git a/src/components/ButtonIcon.tsx b/src/components/ButtonIcon.tsx index 3be82720..d83afcf1 100644 --- a/src/components/ButtonIcon.tsx +++ b/src/components/ButtonIcon.tsx @@ -1,57 +1,45 @@ -import React from 'react' +import React from 'react'; import { GestureResponderEvent } from 'react-native'; import { SvgProps } from 'react-native-svg'; -import styled, { useTheme } from 'styled-components/native' - +import styled, { useTheme } from 'styled-components/native'; interface IconProps { - type: string -}; + type: string; +} interface ButtonIconProps { - type: string - onPress: (event: GestureResponderEvent) => void - iconImage: React.FC - accessibilityLabel: string -}; - + type: string; + onPress: (event: GestureResponderEvent) => void; + iconImage: React.FC; + accessibilityLabel: string; +} export const ButtonIcon = ({ - type, onPress, iconImage, accessibilityLabel + type, + onPress, + iconImage, + accessibilityLabel, }: ButtonIconProps) => { - const theme = useTheme() - const IconImage = iconImage - return ( - - - - ); + const theme = useTheme(); + const IconImage = iconImage; + return ( + + + + ); }; +const iconSize = 32; -const iconSize = 32 - -const View = styled.View` - margin-left: 16px; - margin-right: 16px; - padding-top: 8px; - padding-bottom: 8px; - flex-direction: row; - justify-content: space-between; -` const Icon = styled.Pressable.attrs({ - hitSlop: 10 + hitSlop: 10, })` height: ${iconSize}px; padding-top: 0; padding-right: 10px; padding-bottom: 0; padding-left: 10px; - ${({ type }: IconProps) => type === 'tabs' && ``} - ${({ type }: IconProps) => type === 'reload' && ``} - ${({ type }: IconProps) => type === 'options' && ``} -` + ${({ type }: IconProps) => type === 'tabs' && ''} + ${({ type }: IconProps) => type === 'reload' && ''} + ${({ type }: IconProps) => type === 'options' && ''} +`; diff --git a/src/components/Navigation/NavigationButtons.tsx b/src/components/Navigation/NavigationButtons.tsx index e6c934ee..92213c80 100644 --- a/src/components/Navigation/NavigationButtons.tsx +++ b/src/components/Navigation/NavigationButtons.tsx @@ -1,105 +1,82 @@ -import React, { useContext } from 'react' -import { SafeAreaView } from 'react-native' -import { useSelector, useDispatch } from 'react-redux' -import { useNavigation } from '@react-navigation/native' -import styled, { useTheme } from 'styled-components/native' +import React from 'react'; +import { SafeAreaView } from 'react-native'; +import { useRecoilState } from 'recoil'; +import { useNavigation } from '@react-navigation/native'; +import styled from 'styled-components/native'; -import { AppState } from 'src/store/reducers' - - -import IconArrowLeft from 'src/assets/icons/icon-arrow-left.svg' -import IconArrowRight from 'src/assets/icons/icon-arrow-right.svg' -import IconOptions from 'src/assets/icons/icon-options.svg' -import IconRefresh from 'src/assets/icons/icon-refresh.svg' -import IconTabs from 'src/assets/icons/icon-tabs.svg' - -import { ButtonIcon } from 'src/components/ButtonIcon' - - -interface IconProps { - type: string -} +import { navigationState } from 'src/store/atoms'; +import IconArrowLeft from 'src/assets/icons/icon-arrow-left.svg'; +import IconArrowRight from 'src/assets/icons/icon-arrow-right.svg'; +import IconOptions from 'src/assets/icons/icon-options.svg'; +import IconRefresh from 'src/assets/icons/icon-refresh.svg'; +import IconTabs from 'src/assets/icons/icon-tabs.svg'; +import { ButtonIcon } from 'src/components/ButtonIcon'; const NavigationButtons = () => { - - const theme = useTheme() - - const screenNavigation = useNavigation() - - const navigation = useSelector((state: AppState) => state.navigation) - const dispatch = useDispatch() - - const webViewReload = () => navigation.webViewRef.current.reload() - // TODO - Make backward/forward inactive based on history - const webViewGoBack = () => navigation.webViewRef.current.goBack() - const webViewGoForward = () => navigation.webViewRef.current.goForward() - const viewSettings = () => screenNavigation.navigate('Settings') - - - const navigationButtons = [ - { - type: 'back', - onPress: () => webViewGoBack(), - iconImage: IconArrowLeft, - accessibilityLabel: 'Go back' - }, - { - type: 'forward', - onPress: () => webViewGoForward(), - iconImage: IconArrowRight, - accessibilityLabel: 'Go forward' - }, - { - type: 'tabs', - onPress: () => { }, - iconImage: IconTabs, - accessibilityLabel: 'List opened web-site tabs' - }, - { - type: 'reload', - onPress: () => webViewReload(), - iconImage: IconRefresh, - accessibilityLabel: 'Reload web-page' - }, - { - type: 'options', - onPress: () => viewSettings(), - iconImage: IconOptions, - accessibilityLabel: 'More Options' - } - ]; - - - - - return ( - - - - { - navigationButtons.map((button) => { - const { type, iconImage, accessibilityLabel, onPress } = button; - return ( - - ) - }) - } - - - - ) -} - - -const iconSize = 32 + const screenNavigation = useNavigation(); + + const [navState] = useRecoilState(navigationState); + + const webViewReload = () => navState.webViewRef.current.reload(); + const webViewGoBack = () => navState.webViewRef.current.goBack(); + const webViewGoForward = () => navState.webViewRef.current.goForward(); + + const viewSettings = () => screenNavigation.navigate('Settings'); + + const navigationButtons = [ + { + type: 'back', + onPress: () => webViewGoBack(), + iconImage: IconArrowLeft, + accessibilityLabel: 'Go back', + }, + { + type: 'forward', + onPress: () => webViewGoForward(), + iconImage: IconArrowRight, + accessibilityLabel: 'Go forward', + }, + { + type: 'tabs', + onPress: () => {}, + iconImage: IconTabs, + accessibilityLabel: 'List opened web-site tabs', + }, + { + type: 'reload', + onPress: () => webViewReload(), + iconImage: IconRefresh, + accessibilityLabel: 'Reload web-page', + }, + { + type: 'options', + onPress: () => viewSettings(), + iconImage: IconOptions, + accessibilityLabel: 'More Options', + }, + ]; + + return ( + + + {navigationButtons.map((button) => { + const { type, iconImage, accessibilityLabel, onPress } = button; + return ( + + ); + })} + + + ); +}; const View = styled.View` margin-left: 16px; @@ -108,20 +85,6 @@ const View = styled.View` padding-bottom: 8px; flex-direction: row; justify-content: space-between; -` -const Icon = styled.Pressable.attrs({ - hitSlop: 10 -})` - height: ${iconSize}px; - padding-top: 0; - padding-right: 10px; - padding-bottom: 0; - padding-left: 10px; - ${({ type }: IconProps) => type === 'tabs' && ``} - ${({ type }: IconProps) => type === 'reload' && ``} - ${({ type }: IconProps) => type === 'options' && ``} -` - - +`; -export default NavigationButtons \ No newline at end of file +export default NavigationButtons; diff --git a/src/components/Navigation/NavigationComboInput.tsx b/src/components/Navigation/NavigationComboInput.tsx index cb8878eb..02799feb 100644 --- a/src/components/Navigation/NavigationComboInput.tsx +++ b/src/components/Navigation/NavigationComboInput.tsx @@ -1,59 +1,65 @@ -import React, { useState, useContext } from 'react' -import { useSelector, useDispatch } from 'react-redux' -import styled, { useTheme } from 'styled-components/native' - -import Share from 'react-native-share' - -import { AppState } from 'src/store/reducers' -import { URL_INPUT, URL_CURRENT } from 'src/store/navigation.reducer' - -import { isDarkMode } from 'src/utils/appearance' -import { upgradeUrl } from 'src/utils/url' - -import { ButtonIcon } from 'src/components/ButtonIcon' -import IconShare from 'src/assets/icons/icon-share.svg' +import React, { useState } from 'react'; +import styled, { useTheme } from 'styled-components/native'; +import Share from 'react-native-share'; +import { IsDarkMode } from 'src/utils/appearance'; +import { upgradeUrl } from 'src/utils/url'; +import IconShare from 'src/assets/icons/icon-share.svg'; +import { useRecoilState } from 'recoil'; +import { navigationState } from 'src/store/atoms'; const AddressTextInput = () => { + const theme = useTheme(); - const theme = useTheme() - - const navigation = useSelector((state: AppState) => state.navigation) - const dispatch = useDispatch() + const [navState, setNavState] = useRecoilState(navigationState); - const [shareVisible, shareVisibility] = useState(true) + const [shareVisible, shareVisibility] = useState(true); const shareCurrentUri = () => { const shareOptions = { title: 'Share', - message: `Sharing: ${navigation.webViewState.title}`, - url: navigation.webViewState.url - } + message: `Sharing: ${navState.webViewState?.title}`, + url: navState.webViewState?.url, + }; Share.open(shareOptions) - .then((resp) => { console.log('Share successful', resp) }) - .catch((err) => { console.log('Share error', err) }) - } + .then((resp) => { + console.log('Share successful', resp); + }) + .catch((err) => { + console.log('Share error', err); + }); + }; return ( { - dispatch({ type: URL_INPUT, urlInput: url }) + setNavState((previous) => { + return { + ...previous, + urlInput: url, + }; + }); }} onSubmitEditing={(event) => { - const urlCurrent = upgradeUrl(event.nativeEvent.text) - dispatch({ type: URL_INPUT, urlInput: urlCurrent }) - dispatch({ type: URL_CURRENT, urlCurrent: urlCurrent }) + const urlCurrent = upgradeUrl(event.nativeEvent.text); + setNavState((previous) => { + return { + ...previous, + urlInput: urlCurrent, + urlCurrent: urlCurrent, + }; + }); }} onFocus={() => { - shareVisibility(false) + shareVisibility(false); }} - onBlur={(event) => { + onBlur={() => { // TODO - Re-instate previous URL on before onSubmitEditing - shareVisibility(true) + shareVisibility(true); }} autoCapitalize="none" autoCompleteType="off" @@ -61,23 +67,20 @@ const AddressTextInput = () => { returnKeyType="go" blurOnSubmit={true} clearButtonMode="while-editing" - keyboardAppearance={isDarkMode() ? 'dark' : 'light'} + keyboardAppearance={IsDarkMode() ? 'dark' : 'light'} // TODO - Change keyboard type to search if URL not detected - keyboardType="web-search" + keyboardType="web-search" selectTextOnFocus={true} textContentType="URL" /> - { - shareVisible && + {shareVisible && ( - } + )} - ) -} - - + ); +}; const AddressBar = styled.View` margin-left: 10px; @@ -85,16 +88,16 @@ const AddressBar = styled.View` margin-top: 10px; margin-bottom: 10px; border-radius: 6px; - background-color: ${props => props.theme.addressBar.background}; -` + background-color: ${(props) => props.theme.addressBar.background}; +`; const URLSearchInput = styled.TextInput` font-size: 18px; text-align: left; padding: 10px; - color: ${props => props.theme.addressBar.color}; -` + color: ${(props) => props.theme.addressBar.color}; +`; const Icon = styled.Pressable.attrs({ - hitSlop: 10 + hitSlop: 10, })` /*border: 1px solid rgba(255,0,0,.5);*/ right: -8px; @@ -104,9 +107,7 @@ const Icon = styled.Pressable.attrs({ margin-right: 8px; padding-bottom: 7.5px; margin-left: 8px; - color: ${props => props.theme.addressBar.color}; -` - - + color: ${(props) => props.theme.addressBar.color}; +`; -export default AddressTextInput \ No newline at end of file +export default AddressTextInput; diff --git a/src/components/Navigation/index.tsx b/src/components/Navigation/index.tsx index ad8b57d8..d9ec0fbe 100644 --- a/src/components/Navigation/index.tsx +++ b/src/components/Navigation/index.tsx @@ -1,8 +1,7 @@ -import React from 'react' - -import NavigationTextInput from 'src/components/Navigation/NavigationComboInput' -import NavigationButtons from 'src/components/Navigation/NavigationButtons' +import React from 'react'; +import NavigationTextInput from 'src/components/Navigation/NavigationComboInput'; +import NavigationButtons from 'src/components/Navigation/NavigationButtons'; export default function Navigation() { return ( @@ -10,5 +9,5 @@ export default function Navigation() { - ) -} \ No newline at end of file + ); +} diff --git a/src/components/Test.tsx b/src/components/Test.tsx index 738bc45d..bfb135ea 100644 --- a/src/components/Test.tsx +++ b/src/components/Test.tsx @@ -1,17 +1,17 @@ -import React from 'react' -import styled from 'styled-components/native' +import React from 'react'; +import styled from 'styled-components/native'; export default function Test() { return ( Testing, testing, 1, 2, 3 - ) + ); } const TestView = styled.View` background-color: rgba(255, 0, 0, 0.25); -` +`; const Text = styled.Text` color: red; -` +`; diff --git a/src/data/bookmarks.data.ts b/src/data/bookmarks.data.ts index 0dc0791a..3062958b 100644 --- a/src/data/bookmarks.data.ts +++ b/src/data/bookmarks.data.ts @@ -1,41 +1,41 @@ -import type { Bookmark } from 'src/@types/bookmarks' +import type { Bookmark } from 'src/@types/bookmarks'; - -export const Bookmarks:Bookmark[] = [ +export const Bookmarks: Bookmark[] = [ { - "displayName": "Foundation", - "description": "Foundation is a platform that aims to build a new creative economy.", - "protocol": "http", - "location": "https://foundation.app/", - "tags": ["ethereum", "nft", "art"] + displayName: 'Foundation', + description: + 'Foundation is a platform that aims to build a new creative economy.', + protocol: 'http', + location: 'https://foundation.app/', + tags: ['ethereum', 'nft', 'art'], }, { - "displayName": "Catalog", - "description": "Catalog is a record press, an auction house, and an open music library.", - "protocol": "http", - "location": "https://beta.catalog.works/leslie", - "tags": ["ethereum", "nft", "music"] + displayName: 'Catalog', + description: + 'Catalog is a record press, an auction house, and an open music library.', + protocol: 'http', + location: 'https://beta.catalog.works/leslie', + tags: ['ethereum', 'nft', 'music'], }, { - "displayName": "Mirror", - "description": "Writing as usual. Publishing like never before.", - "protocol": "http", - "location": "https://mirror.xyz/", - "tags": ["ethereum", "nft", "writing"] + displayName: 'Mirror', + description: 'Writing as usual. Publishing like never before.', + protocol: 'http', + location: 'https://mirror.xyz/', + tags: ['ethereum', 'nft', 'writing'], }, { - "displayName": "Mirror", - "description": "Writing as usual. Publishing like never before.", - "protocol": "http", - "location": "https://web3summit.com/", - "tags": ["ethereum", "nft", "writing"] + displayName: 'Mirror', + description: 'Writing as usual. Publishing like never before.', + protocol: 'http', + location: 'https://web3summit.com/', + tags: ['ethereum', 'nft', 'writing'], }, { - "displayName": "Mirror", - "description": "Writing as usual. Publishing like never before.", - "protocol": "ipfs", - "location": "bafybeidyka6iguedudto3vablngbhyttw32apjk6mo474zfnhlgbivl7me", - "tags": ["ethereum", "nft", "writing"] + displayName: 'Mirror', + description: 'Writing as usual. Publishing like never before.', + protocol: 'ipfs', + location: 'bafybeidyka6iguedudto3vablngbhyttw32apjk6mo474zfnhlgbivl7me', + tags: ['ethereum', 'nft', 'writing'], }, - -] \ No newline at end of file +]; diff --git a/src/protocols/ethereum/index.ts b/src/protocols/ethereum/index.ts index 8a261c09..f2e4dee9 100644 --- a/src/protocols/ethereum/index.ts +++ b/src/protocols/ethereum/index.ts @@ -1,35 +1,28 @@ // import 'react-native-get-random-values' -import '@ethersproject/shims' -import { ethers } from 'ethers' -import type { BigNumber, Wallet } from 'ethers' - -import { env } from 'environments/.env.development' - - - -const provider = - new ethers.providers.FallbackProvider([ - new ethers.providers.EtherscanProvider( - `${env.ETHERSCAN_NETWORK}`, - `${env.ETHERSCAN_API_KEY}` - ), - ]); - +import '@ethersproject/shims'; +import { ethers } from 'ethers'; +import type { BigNumber, Wallet } from 'ethers'; +import { env } from 'environments/.env.development'; +const provider = new ethers.providers.FallbackProvider([ + new ethers.providers.EtherscanProvider( + `${env.ETHERSCAN_NETWORK}`, + `${env.ETHERSCAN_API_KEY}`, + ), +]); export class Ethereum { - - walletPrivateKey: string; + walletPrivateKey: string; walletJSON: string; walletPassword: string; walletAddress: string; constructor( - walletPrivateKey = '', - walletJSON = '', + walletPrivateKey = '', + walletJSON = '', walletPassword = '', - walletAddress = '', + walletAddress = '', ) { this.walletPrivateKey = walletPrivateKey; this.walletJSON = walletJSON; @@ -41,43 +34,45 @@ export class Ethereum { public isAddressENS() { const { walletAddress } = this; return !ethers.utils.isAddress(walletAddress) && - /^[a-zA-Z0-9][a-zA-Z0-9-]{1,61}[a-zA-Z0-9](?:\.[a-zA-Z]{2,})+$/ - .test(walletAddress) ? true : false; + /^[a-zA-Z0-9][a-zA-Z0-9-]{1,61}[a-zA-Z0-9](?:\.[a-zA-Z]{2,})+$/.test( + walletAddress, + ) + ? true + : false; } - public isAddressHex() { const { walletAddress } = this; - if (ethers.utils.isAddress(walletAddress)) - return true; - }; - + if (ethers.utils.isAddress(walletAddress)) { + return true; + } + } public async getEnsFromAddress(): Promise { const { walletAddress } = this; - if (!ethers.utils.isAddress(walletAddress)) - return 'Not a valid address' + if (!ethers.utils.isAddress(walletAddress)) { + return 'Not a valid address'; + } return await provider .lookupAddress(walletAddress) - .then(result => { - const address = result === null ? - `${walletAddress.substr(0,6)}...${walletAddress.slice(-4)}` : - result; - return address + .then((result) => { + const address = + result === null + ? `${walletAddress.substr(0, 6)}...${walletAddress.slice(-4)}` + : result; + return address; }) - .catch(err => `Error: Address doesn't exist. ${err}`); - }; - + .catch((err) => `Error: Address doesn't exist. ${err}`); + } public async getAddressFromEns(): Promise { const { walletAddress } = this; return provider .resolveName(walletAddress) - .then(result => result) - .catch(err => `Error: ${err}`) + .then((result) => result) + .catch((err) => `Error: ${err}`); } - /** * TODO * - Show small amounts @@ -89,27 +84,24 @@ export class Ethereum { return await provider .getBalance(walletAddress) .then((result) => { - const balanceRemainder:BigNumber = result.mod(1e14); + const balanceRemainder: BigNumber = result.mod(1e14); const balanceFormatted = ethers.utils.formatEther( - result.sub(balanceRemainder) + result.sub(balanceRemainder), ); return balanceFormatted; }) .catch((err) => { console.error('Error:', err); - return 'Unknown' + return 'Unknown'; }); - }; - + } public async walletFromMnemonic(): Promise { const { walletPrivateKey } = this; const privateKey = walletPrivateKey; - const walletWithProvider = new ethers - .Wallet(privateKey, provider) - return walletWithProvider - }; - + const walletWithProvider = new ethers.Wallet(privateKey, provider); + return walletWithProvider; + } /** * Warning: this uses crypto and takes around 10 seconds @@ -117,20 +109,16 @@ export class Ethereum { * ! Use Ethers in RN with native crypto? */ public async walletFromEncryptedJSON(): Promise { - const { walletJSON, walletPassword } = this - const json:string = `${JSON.stringify(walletJSON)}` - const password:string = `${walletPassword}` - const wallet = ethers - .Wallet - .fromEncryptedJson(json, password) - .then((wallet:Wallet) => { + const { walletJSON, walletPassword } = this; + const json: string = `${JSON.stringify(walletJSON)}`; + const password: string = `${walletPassword}`; + ethers.Wallet.fromEncryptedJson(json, password) + .then((wallet: Wallet) => { return wallet; }) .catch((err) => { - console.log('Address Failed', err) - return 'Wallet decryption failed' - }) - }; - - -}; \ No newline at end of file + console.log('Address Failed', err); + return 'Wallet decryption failed'; + }); + } +} diff --git a/src/store/atoms.ts b/src/store/atoms.ts new file mode 100644 index 00000000..e1650bd0 --- /dev/null +++ b/src/store/atoms.ts @@ -0,0 +1,51 @@ +import React from 'react'; +import { atom } from 'recoil'; +import type { BigNumber } from 'ethers'; +import type { WebView, WebViewNavigation } from 'react-native-webview'; + +export type EthereumState = { + ethWalletAddress: BigNumber | string | null; + ethWalletHex: string | null; + ethWalletEns: string | null; + ethWalletBalance: string; +}; + +export const ethereumState = atom({ + key: 'ethereumState', + default: { + ethWalletAddress: null, + ethWalletHex: null, + ethWalletEns: null, + ethWalletBalance: '...', + }, +}); + +export type NavigationState = { + urlInput: string; + urlCurrent: string; + webViewRef: React.RefObject | null; + webViewState: null | WebViewNavigation; +}; + +export const navigationState = atom({ + key: 'navigationState', + default: { + urlInput: '', + urlCurrent: 'https://foundation.app/@leslie', + webViewRef: null, + webViewState: null, + }, + // Find a way to use the ref and remove this + dangerouslyAllowMutability: true, +}); + +export type SettingsState = { + settingsRef: null; +}; + +export const settingsState = atom({ + key: 'settingsState', + default: { + settingsRef: null, + }, +}); diff --git a/src/store/ethereum.reducer.ts b/src/store/ethereum.reducer.ts deleted file mode 100644 index bd31c450..00000000 --- a/src/store/ethereum.reducer.ts +++ /dev/null @@ -1,98 +0,0 @@ -import type { BigNumber } from 'ethers' -import type { Dispatch } from 'redux' - -export const ETH_WALLET_ADDRESS = 'ethereum/wallet-address' -export const ETH_WALLET_HEX = 'ethereum/wallet-hex' -export const ETH_WALLET_ENS = 'ethereum/wallet-ens' -export const ETH_WALLET_BALANCE = 'ethereum/wallet-balance' - -type EthereumState = { - ethWalletAddress: BigNumber | string | null - ethWalletHex: string | null - ethWalletEns: string | null - ethWalletBalance: string -} - -const initialState: EthereumState = { - ethWalletAddress: null, - ethWalletHex: null, - ethWalletEns: null, - ethWalletBalance: '...' -} - -type EthereumWalletAddress = { - type: typeof ETH_WALLET_ADDRESS - ethWalletAddress: BigNumber | string | null -} -type EthereumWalletHex = { - type: typeof ETH_WALLET_HEX - ethWalletHex: string | null -} -type EthereumWalletENS = { - type: typeof ETH_WALLET_ENS - ethWalletEns: string | null -} -type EthereumWalletBalance = { - type: typeof ETH_WALLET_BALANCE - ethWalletBalance: string -} - -type EthereumActionTypes = - EthereumWalletAddress | - EthereumWalletHex | - EthereumWalletENS | - EthereumWalletBalance - -export default ( - state: EthereumState = initialState, - action: EthereumActionTypes -) => { - switch (action.type) { - case ETH_WALLET_ADDRESS: { - return { - ...state, - ethWalletAddress: action.ethWalletAddress - } - } - case ETH_WALLET_HEX: { - return { - ...state, - ethWalletHex: action.ethWalletHex - } - } - case ETH_WALLET_ENS: { - return { - ...state, - ethWalletEns: action.ethWalletEns - } - } - case ETH_WALLET_BALANCE: { - return { - ...state, - ethWalletBalance: action.ethWalletBalance - } - } - default: { - return { ...state } - } - } -} - -export const ethWalletAddresss = (address: string) => (dispatch: Dispatch) => { - console.log('ethWalletAddresss dispatch', address) - dispatch({ type: ETH_WALLET_BALANCE, ethWalletAddresss: address }) -} - -export const ethWalletHex = (address: string) => (dispatch: Dispatch) => { - console.log('ethWalletHex dispatch', address) - dispatch({ type: ETH_WALLET_HEX, ethWalletHex: address }) -} -export const ethWalletEns = (address: string) => (dispatch: Dispatch) => { - console.log('ethWalletEns dispatch', address) - dispatch({ type: ETH_WALLET_ENS, ethWalletEns: address }) -} - -export const ethWalletBalance = (balance: string) => (dispatch: Dispatch) => { - console.log('ethWalletBalance dispatch', balance) - dispatch({ type: ETH_WALLET_BALANCE, ethWalletBalance: balance }) -} diff --git a/src/store/index.ts b/src/store/index.ts deleted file mode 100644 index 512d5425..00000000 --- a/src/store/index.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { createStore, applyMiddleware } from 'redux' - -import thunkMiddleware from 'redux-thunk' - -import rootReducer from 'src/store/reducers' - -const middleware = applyMiddleware(thunkMiddleware) - -const store = createStore(rootReducer, middleware) - -export default store diff --git a/src/store/navigation.reducer.ts b/src/store/navigation.reducer.ts deleted file mode 100644 index 370404ab..00000000 --- a/src/store/navigation.reducer.ts +++ /dev/null @@ -1,95 +0,0 @@ -import type React from 'react' -import type { WebView } from 'react-native-webview' -import type { Dispatch } from 'redux' - -export const WEBVIEW_REF = 'browser/webview-ref' -export const WEBVIEW_STATE = 'browser/webview-state' -export const URL_INPUT = 'browser/url-input' -export const URL_CURRENT = 'browser/url-current' - - -type NavigationState = { - urlInput: string - urlCurrent: string - webViewRef: React.RefObject | null - webViewState: null -} - -const initialState: NavigationState = { - urlInput: '', - urlCurrent: 'https://foundation.app/@leslie', - webViewRef: null, - webViewState: null -} - - -type UrlInput = { - type: typeof URL_INPUT - urlInput: string -} -type UrlCurrent = { - type: typeof URL_CURRENT - urlCurrent: string -} -type WebViewRef = { - type: typeof WEBVIEW_REF - webViewRef: React.RefObject | null -} -type WebViewState = { - type: typeof WEBVIEW_STATE - webViewState: null -} - -type NavigationActionTypes = - UrlInput | - UrlCurrent | - WebViewRef | - WebViewState - -export default ( - state: NavigationState = initialState, action: NavigationActionTypes) => { - switch (action.type) { - case WEBVIEW_REF: { - return { - ...state, - webViewRef: action.webViewRef - } - } - case WEBVIEW_STATE: { - return { - ...state, - webViewState: action.webViewState - } - } - case URL_INPUT: { - return { - ...state, - urlInput: action.urlInput - } - } - case URL_CURRENT: { - return { - ...state, - urlCurrent: action.urlCurrent - } - } - default: { - return { ...state } - } - } -} - -export const webViewRef = (ref: any) => (dispatch: Dispatch) => { - console.log('webViewRef dispatch', ref) - dispatch({ type: WEBVIEW_REF, webViewRef: ref }) -} - -export const urlInput = (url: string) => (dispatch: Dispatch) => { - console.log('urlInput dispatch', url) - dispatch({ type: URL_INPUT, urlInput: url }) -} - -export const urlCurrent = (url: string) => (dispatch: Dispatch) => { - console.log('urlCurrent dispatch', url) - dispatch({ type: URL_CURRENT, urlCurrent: url }) -} diff --git a/src/store/reducers.ts b/src/store/reducers.ts deleted file mode 100644 index a4e79d54..00000000 --- a/src/store/reducers.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { combineReducers } from 'redux' - -import navigation from 'src/store/navigation.reducer' -import settings from 'src/store/settings.reducer' -import ethereum from 'src/store/ethereum.reducer' - -const rootReducer = combineReducers({ - navigation, - settings, - ethereum -}) - -export type AppState = ReturnType - -export default rootReducer diff --git a/src/store/settings.reducer.ts b/src/store/settings.reducer.ts deleted file mode 100644 index 1a39bceb..00000000 --- a/src/store/settings.reducer.ts +++ /dev/null @@ -1,34 +0,0 @@ -export const SETTINGS_REF = 'settings/settings-ref' - -type SettingsState = { - settingsRef: null -} - -const initialState: SettingsState = { - settingsRef: null -} - -type SettingsRef = { - type: typeof SETTINGS_REF - settingsOpen: boolean -} - -type SettingsActionTypes = SettingsRef - - -export default ( - state: SettingsState = initialState, - action: SettingsActionTypes -) => { - switch (action.type) { - case SETTINGS_REF: { - return { - ...state, - settingsOpen: action.settingsOpen - } - } - default: { - return { ...state } - } - } -} diff --git a/src/themes/dark.ts b/src/themes/dark.ts index 86404eb2..12b9cda0 100644 --- a/src/themes/dark.ts +++ b/src/themes/dark.ts @@ -1,21 +1,21 @@ -import { DefaultTheme } from 'styled-components/native' +import { DefaultTheme } from 'styled-components/native'; const theme: DefaultTheme = { - colors: { - scheme: 'dark', - text: 'white', - background: 'rgb(24,25,26)', - debug: 'green' - }, - ui: { - background: 'rgba(24,25,26,.95)', - foreground: 'rgba(0,0,0,.75)', - icon: 'rgba(255,255,255,.5)' - }, - addressBar: { - background: 'rgba(255,255,255,.08)', - color: 'rgba(255,255,255,1)' - } -} + colors: { + scheme: 'dark', + text: 'white', + background: 'rgb(24,25,26)', + debug: 'green', + }, + ui: { + background: 'rgba(24,25,26,.95)', + foreground: 'rgba(0,0,0,.75)', + icon: 'rgba(255,255,255,.5)', + }, + addressBar: { + background: 'rgba(255,255,255,.08)', + color: 'rgba(255,255,255,1)', + }, +}; -export default theme \ No newline at end of file +export default theme; diff --git a/src/themes/light.ts b/src/themes/light.ts index 0d0ea799..0f08ecc5 100644 --- a/src/themes/light.ts +++ b/src/themes/light.ts @@ -1,21 +1,21 @@ -import { DefaultTheme } from 'styled-components/native' +import { DefaultTheme } from 'styled-components/native'; const theme: DefaultTheme = { - colors: { - scheme: 'light', - text: 'black', - background: 'rgb(228,229,230)', - debug: 'red' - }, - ui: { - background: 'rgba(228,229,230,.8)', - foreground: 'rgba(255,255,255,.75)', - icon: 'rgba(125,125,125,1)', - }, - addressBar: { - background: 'rgba(0,0,0,.08)', - color: 'rgba(0,0,0,1)' - } -} + colors: { + scheme: 'light', + text: 'black', + background: 'rgb(228,229,230)', + debug: 'red', + }, + ui: { + background: 'rgba(228,229,230,.8)', + foreground: 'rgba(255,255,255,.75)', + icon: 'rgba(125,125,125,1)', + }, + addressBar: { + background: 'rgba(0,0,0,.08)', + color: 'rgba(0,0,0,1)', + }, +}; -export default theme \ No newline at end of file +export default theme; diff --git a/src/utils/appearance.ts b/src/utils/appearance.ts index 27f93645..7c992606 100644 --- a/src/utils/appearance.ts +++ b/src/utils/appearance.ts @@ -1,29 +1,25 @@ -import { useState, useEffect } from 'react' -import { Appearance, useColorScheme } from 'react-native' +import { useState, useEffect } from 'react'; +import { Appearance, useColorScheme } from 'react-native'; +Appearance.getColorScheme(); -Appearance.getColorScheme() +export const IsDarkMode = () => { + const [isDarkMode, setIsDarkMode] = useState(false); - -export const isDarkMode = () => { - - const [isDarkMode, setIsDarkMode] = useState(false) - - const colorScheme = useColorScheme() + const colorScheme = useColorScheme(); useEffect(() => { let mounted = true; - setIsDarkMode(colorScheme === 'dark' ? true : false) - Appearance.addChangeListener(({ colorScheme }) => { + setIsDarkMode(colorScheme === 'dark' ? true : false); + Appearance.addChangeListener(() => { if (mounted) { - setIsDarkMode(colorScheme === 'dark' ? true : false) + setIsDarkMode(colorScheme === 'dark' ? true : false); } - }) - return () => { - mounted = false; - } - }, [isDarkMode]) - - - return isDarkMode -} \ No newline at end of file + }); + return () => { + mounted = false; + }; + }, [isDarkMode, colorScheme]); + + return isDarkMode; +}; diff --git a/src/utils/url.ts b/src/utils/url.ts index ca4cb5fa..ab47864b 100644 --- a/src/utils/url.ts +++ b/src/utils/url.ts @@ -1,12 +1,13 @@ export const upgradeUrl = (uri: string) => { - const expression = /[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&//=]*)/gi; - const regex = new RegExp(expression) - const isURL = uri.match(regex) - if (isURL) { - if (!uri.startsWith('http')) { - return `http://${uri}/` - } - return uri - } - return `https://duck.com/?q=${encodeURI(uri)}` -} \ No newline at end of file + const expression = + /[-a-zA-Z0-9@:%._~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_.~#?&//=]*)/gi; + const regex = new RegExp(expression); + const isURL = uri.match(regex); + if (isURL) { + if (!uri.startsWith('http')) { + return `http://${uri}/`; + } + return uri; + } + return `https://duck.com/?q=${encodeURI(uri)}`; +}; diff --git a/src/views/Browser.tsx b/src/views/Browser.tsx index f66fc0af..72c1da03 100644 --- a/src/views/Browser.tsx +++ b/src/views/Browser.tsx @@ -1,35 +1,26 @@ -import React, { useState } from 'react' -import styled from 'styled-components/native' - -import BrowserWebView from 'src/components/BrowserWebView' -import Navigation from 'src/components/Navigation' - - -const View = styled.View` - flex: 1; - background-color: ${props => props.theme.colors.background}; -` -const SafeAreaView = styled.SafeAreaView` - flex: 1; -` -const KeyboardAvoidingView = styled.KeyboardAvoidingView` - flex: 1; -` +import React from 'react'; +import styled from 'styled-components/native'; +import BrowserWebView from 'src/components/BrowserWebView'; +import Navigation from 'src/components/Navigation'; export default function Browser() { - return ( <> - + - ) -} \ No newline at end of file + ); +} + +const View = styled.View` + flex: 1; + background-color: ${(props) => props.theme.colors.background}; +`; +const KeyboardAvoidingView = styled.KeyboardAvoidingView` + flex: 1; +`; diff --git a/src/views/Settings.tsx b/src/views/Settings.tsx index 97faa51a..6d115f7b 100644 --- a/src/views/Settings.tsx +++ b/src/views/Settings.tsx @@ -1,64 +1,40 @@ -import React, { useState } from 'react' -import { SafeAreaView } from 'react-native' -import { useSelector } from 'react-redux' -import { useNavigation } from '@react-navigation/native' -import styled from 'styled-components/native' -import { BlurView } from '@react-native-community/blur' +import React, { useState } from 'react'; +import { useRecoilState } from 'recoil'; +import { useNavigation } from '@react-navigation/native'; +import styled from 'styled-components/native'; +import { BlurView } from '@react-native-community/blur'; -import { isDarkMode } from 'src/utils/appearance' -import { useTheme } from 'styled-components/native' - -import { AppState } from 'src/store/reducers' - -import { Ethereum } from 'src/protocols/ethereum' - - -import IconCurrencyEthereum from 'src/assets/icons/currencies/currency-ethereum.svg' +import { IsDarkMode } from 'src/utils/appearance'; +import { useTheme } from 'styled-components/native'; +import IconCurrencyEthereum from 'src/assets/icons/currencies/currency-ethereum.svg'; +import { ethereumState } from 'src/store/atoms'; export default function Settings() { + const theme = useTheme(); - const theme = useTheme() - - const ethereumState = useSelector((state: AppState) => state.ethereum) - - const screenNavigation = useNavigation() + const [ethState] = useRecoilState(ethereumState); - const goBack = () => screenNavigation.goBack() + const screenNavigation = useNavigation(); - const [searchWallet, setSearchWallet] = useState('') + const goBack = () => screenNavigation.goBack(); - const wallet = new Ethereum(); + const [searchWallet, setSearchWallet] = useState(''); return ( <> - +
Done - - {ethereumState.ethWalletEns} - - - {ethereumState.ethWalletHex} - + {ethState.ethWalletEns} + {ethState.ethWalletHex} - - {ethereumState.ethWalletBalance} - + {ethState.ethWalletBalance}
@@ -69,93 +45,94 @@ export default function Settings() { onChangeText={(address: string) => { setSearchWallet(address); }} - onSubmitEditing={(event) => { - const search = event.nativeEvent.text + onSubmitEditing={() => { console.log('TODO - Search for wallet information'); }} autoCapitalize="none" autoCompleteType="off" autoCorrect={false} returnKeyType="search" - keyboardAppearance={isDarkMode() ? 'dark' : 'light'} + keyboardAppearance={IsDarkMode() ? 'dark' : 'light'} blurOnSubmit={true} clearButtonMode="always" textContentType="none" // TODO - URL and hex? /> - ) + ); } - +const BlurViewContainer = styled(BlurView)` + position: absolute; + bottom: 0; + right: 0; + left: 0; + top: 0; +`; const Header = styled.View` - background-color: ${props => props.theme.ui.background}; + background-color: ${(props) => props.theme.ui.background}; padding: 20px 0; -` +`; -const CloseButton = styled.Pressable.attrs((props) => ({ - hitSlop: 20 +const CloseButton = styled.Pressable.attrs(() => ({ + hitSlop: 20, }))` position: absolute; top: 24px; right: 24px; -` +`; const CloseButtonText = styled.Text` - color: ${props => props.theme.colors.text}; + color: ${(props) => props.theme.colors.text}; font-size: 18px; -` +`; const WalletInfo = styled.View` margin: 20px 20px 20px 20px; -` +`; -const walletUserIconSize = `80px`; +const walletUserIconSize = '80px'; const WalletUserIcon = styled.View` width: ${walletUserIconSize}; height: ${walletUserIconSize}; border-radius: ${walletUserIconSize}; - background-color: ${props => props.theme.colors.text}; - opacity: .25; + background-color: ${(props) => props.theme.colors.text}; + opacity: 0.25; margin: 0 0 20px 0; -` +`; const WalletAddressTitle = styled.Text` - color: ${props => props.theme.colors.text}; + color: ${(props) => props.theme.colors.text}; font-size: 24px; font-weight: 600; -` +`; const WalletAddressHex = styled.Text` - color: ${props => props.theme.colors.text}; - opacity: .65; + color: ${(props) => props.theme.colors.text}; + opacity: 0.65; font-size: 13.5px; -` +`; const WalletBalance = styled.View` /* border: 1px solid red; */ flex-direction: row; margin: 20px 0 0 0; -` +`; const WalletCurrencyIcon = styled(IconCurrencyEthereum)` /* border: 1px solid red; */ - margin: 3.5px 10px 0 0; - -` + margin: 3.5px 10px 0 0; +`; const WalletAmount = styled.Text` - color: ${props => props.theme.colors.text}; + color: ${(props) => props.theme.colors.text}; font-size: 42px; font-weight: 600; -` - - +`; const Body = styled.View` - background-color: ${props => props.theme.ui.background}; + background-color: ${(props) => props.theme.ui.background}; flex: 1; -` - +`; const SearchWallet = styled.TextInput` font-size: 18px; @@ -166,6 +143,6 @@ const SearchWallet = styled.TextInput` margin-top: 10px; margin-bottom: 10px; border-radius: 6px; - background: ${props => props.theme.ui.foreground}; - color: ${props => props.theme.addressBar.color}; -`; \ No newline at end of file + background: ${(props) => props.theme.ui.foreground}; + color: ${(props) => props.theme.addressBar.color}; +`; diff --git a/yarn.lock b/yarn.lock index f56a9f71..db6662e4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2235,6 +2235,18 @@ eslint-scope "^5.1.1" eslint-utils "^3.0.0" +"@typescript-eslint/experimental-utils@^4.0.1": + version "4.28.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-4.28.0.tgz#13167ed991320684bdc23588135ae62115b30ee0" + integrity sha512-9XD9s7mt3QWMk82GoyUpc/Ji03vz4T5AYlHF9DcoFNfJ/y3UAclRsfGiE2gLfXtyC+JRA3trR7cR296TEb1oiQ== + dependencies: + "@types/json-schema" "^7.0.7" + "@typescript-eslint/scope-manager" "4.28.0" + "@typescript-eslint/types" "4.28.0" + "@typescript-eslint/typescript-estree" "4.28.0" + eslint-scope "^5.1.1" + eslint-utils "^3.0.0" + "@typescript-eslint/parser@^4.19.0", "@typescript-eslint/parser@^4.22.1": version "4.27.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-4.27.0.tgz#85447e573364bce4c46c7f64abaa4985aadf5a94" @@ -2253,11 +2265,24 @@ "@typescript-eslint/types" "4.27.0" "@typescript-eslint/visitor-keys" "4.27.0" +"@typescript-eslint/scope-manager@4.28.0": + version "4.28.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-4.28.0.tgz#6a3009d2ab64a30fc8a1e257a1a320067f36a0ce" + integrity sha512-eCALCeScs5P/EYjwo6se9bdjtrh8ByWjtHzOkC4Tia6QQWtQr3PHovxh3TdYTuFcurkYI4rmFsRFpucADIkseg== + dependencies: + "@typescript-eslint/types" "4.28.0" + "@typescript-eslint/visitor-keys" "4.28.0" + "@typescript-eslint/types@4.27.0": version "4.27.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.27.0.tgz#712b408519ed699baff69086bc59cd2fc13df8d8" integrity sha512-I4ps3SCPFCKclRcvnsVA/7sWzh7naaM/b4pBO2hVxnM3wrU51Lveybdw5WoIktU/V4KfXrTt94V9b065b/0+wA== +"@typescript-eslint/types@4.28.0": + version "4.28.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.28.0.tgz#a33504e1ce7ac51fc39035f5fe6f15079d4dafb0" + integrity sha512-p16xMNKKoiJCVZY5PW/AfILw2xe1LfruTcfAKBj3a+wgNYP5I9ZEKNDOItoRt53p4EiPV6iRSICy8EPanG9ZVA== + "@typescript-eslint/typescript-estree@4.27.0": version "4.27.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-4.27.0.tgz#189a7b9f1d0717d5cccdcc17247692dedf7a09da" @@ -2271,6 +2296,19 @@ semver "^7.3.5" tsutils "^3.21.0" +"@typescript-eslint/typescript-estree@4.28.0": + version "4.28.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-4.28.0.tgz#e66d4e5aa2ede66fec8af434898fe61af10c71cf" + integrity sha512-m19UQTRtxMzKAm8QxfKpvh6OwQSXaW1CdZPoCaQuLwAq7VZMNuhJmZR4g5281s2ECt658sldnJfdpSZZaxUGMQ== + dependencies: + "@typescript-eslint/types" "4.28.0" + "@typescript-eslint/visitor-keys" "4.28.0" + debug "^4.3.1" + globby "^11.0.3" + is-glob "^4.0.1" + semver "^7.3.5" + tsutils "^3.21.0" + "@typescript-eslint/visitor-keys@4.27.0": version "4.27.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-4.27.0.tgz#f56138b993ec822793e7ebcfac6ffdce0a60cb81" @@ -2279,6 +2317,14 @@ "@typescript-eslint/types" "4.27.0" eslint-visitor-keys "^2.0.0" +"@typescript-eslint/visitor-keys@4.28.0": + version "4.28.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-4.28.0.tgz#255c67c966ec294104169a6939d96f91c8a89434" + integrity sha512-PjJyTWwrlrvM5jazxYF5ZPs/nl0kHDZMVbuIcbpawVXaDPelp3+S9zpOz5RmVUfS/fD5l5+ZXNKnWhNYjPzCvw== + dependencies: + "@typescript-eslint/types" "4.28.0" + eslint-visitor-keys "^2.0.0" + JSONStream@^1.0.4: version "1.3.5" resolved "https://registry.yarnpkg.com/JSONStream/-/JSONStream-1.3.5.tgz#3208c1f08d3a4d99261ab64f92302bc15e111ca0" @@ -4141,6 +4187,13 @@ eslint-plugin-jest@22.4.1: resolved "https://registry.yarnpkg.com/eslint-plugin-jest/-/eslint-plugin-jest-22.4.1.tgz#a5fd6f7a2a41388d16f527073b778013c5189a9c" integrity sha512-gcLfn6P2PrFAVx3AobaOzlIEevpAEf9chTpFZz7bYfc7pz8XRv7vuKTIE4hxPKZSha6XWKKplDQ0x9Pq8xX2mg== +eslint-plugin-jest@^24.3.6: + version "24.3.6" + resolved "https://registry.yarnpkg.com/eslint-plugin-jest/-/eslint-plugin-jest-24.3.6.tgz#5f0ca019183c3188c5ad3af8e80b41de6c8e9173" + integrity sha512-WOVH4TIaBLIeCX576rLcOgjNXqP+jNlCiEmRgFTfQtJ52DpwnIQKAVGlGPAN7CZ33bW6eNfHD6s8ZbEUTQubJg== + dependencies: + "@typescript-eslint/experimental-utils" "^4.0.1" + eslint-plugin-prettier@3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-3.1.2.tgz#432e5a667666ab84ce72f945c72f77d996a5c9ba"