diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 16ffca3..3e30e82 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -2,6 +2,7 @@ + NFCReaderUsageDescription Enable Keycard + NSCameraUsageDescription + $(PRODUCT_NAME) needs access to your Camera. CFBundleDevelopmentRegion en CFBundleDisplayName diff --git a/package-lock.json b/package-lock.json index 2a28876..54420c4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,7 +13,8 @@ "react-native": "0.75.3", "react-native-modal": "^13.0.1", "react-native-status-keycard": "git+https://github.com/status-im/react-native-status-keycard.git#refs/tags/v2.6.0", - "react-native-vector-icons": "^10.1.0" + "react-native-vector-icons": "^10.1.0", + "react-native-vision-camera": "^4.5.3" }, "devDependencies": { "@babel/core": "^7.20.0", @@ -4775,9 +4776,9 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "22.5.5", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.5.5.tgz", - "integrity": "sha512-Xjs4y5UPO/CLdzpgR6GirZJx36yScjh73+2NlLlkFRSoQN8B0DpfXPdZGnvVmLRLOsqDpOfTNv7D9trgGhmOIA==", + "version": "22.7.4", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.7.4.tgz", + "integrity": "sha512-y+NPi1rFzDs1NdQHHToqeiX2TIS79SWEAw9GYhkkx8bD0ChpfqC+n2j5OXOCpzfojBEBt6DnEnnG9MY0zk1XLg==", "license": "MIT", "dependencies": { "undici-types": "~6.19.2" @@ -4800,9 +4801,9 @@ "license": "MIT" }, "node_modules/@types/react": { - "version": "18.3.8", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.8.tgz", - "integrity": "sha512-syBUrW3/XpnW4WJ41Pft+I+aPoDVbrBVQGEnbD7NijDGlVC+8gV/XKRY+7vMDlfPpbwYt0l1vd/Sj8bJGMbs9Q==", + "version": "18.3.10", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.10.tgz", + "integrity": "sha512-02sAAlBnP39JgXwkAq3PeU9DVaaGpZyF3MGcC0MKgQVkZor5IiiDAipVaxQHtDJAmO4GIy/rVBy/LzVj76Cyqg==", "devOptional": true, "license": "MIT", "dependencies": { @@ -5729,9 +5730,9 @@ } }, "node_modules/browserslist": { - "version": "4.23.3", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.3.tgz", - "integrity": "sha512-btwCFJVjI4YWDNfau8RhZ+B1Q/VLoUITrm3RlP6y1tYGWIOa+InuYiRGXUBXo8nA1qKmHMyLB/iVQg5TT4eFoA==", + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.0.tgz", + "integrity": "sha512-Rmb62sR1Zpjql25eSanFGEhAxcFwfA1K0GuQcLoaJBAcENegrQut3hYdhXFF1obQfiDyqIW/cLM5HSJ/9k884A==", "funding": [ { "type": "opencollective", @@ -5748,8 +5749,8 @@ ], "license": "MIT", "dependencies": { - "caniuse-lite": "^1.0.30001646", - "electron-to-chromium": "^1.5.4", + "caniuse-lite": "^1.0.30001663", + "electron-to-chromium": "^1.5.28", "node-releases": "^2.0.18", "update-browserslist-db": "^1.1.0" }, @@ -5880,9 +5881,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001663", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001663.tgz", - "integrity": "sha512-o9C3X27GLKbLeTYZ6HBOLU1tsAcBZsLis28wrVzddShCS16RujjHp9GDHKZqrB3meE0YjhawvMFsGb/igqiPzA==", + "version": "1.0.30001664", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001664.tgz", + "integrity": "sha512-AmE7k4dXiNKQipgn7a2xg558IRqPN3jMQY/rOsbxDhrd0tyChwbITBfiwtnqz8bi2M5mIWbxAYBvk7W7QBUS2g==", "funding": [ { "type": "opencollective", @@ -6652,9 +6653,9 @@ "license": "MIT" }, "node_modules/electron-to-chromium": { - "version": "1.5.27", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.27.tgz", - "integrity": "sha512-o37j1vZqCoEgBuWWXLHQgTN/KDKe7zwpiY5CPeq2RvUqOyJw9xnrULzZAEVQ5p4h+zjMk7hgtOoPdnLxr7m/jw==", + "version": "1.5.29", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.29.tgz", + "integrity": "sha512-PF8n2AlIhCKXQ+gTpiJi0VhcHDb69kYX4MtCiivctc2QD3XuNZ/XIOlbGzt7WAjjEev0TtaH6Cu3arZExm5DOw==", "license": "ISC" }, "node_modules/emittery": { @@ -7192,9 +7193,9 @@ } }, "node_modules/eslint-plugin-react": { - "version": "7.36.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.36.1.tgz", - "integrity": "sha512-/qwbqNXZoq+VP30s1d4Nc1C5GTxjJQjk4Jzs4Wq2qzxFM7dSmuG2UkIjg2USMLh3A/aVcUNrK7v0J5U1XEGGwA==", + "version": "7.37.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.37.0.tgz", + "integrity": "sha512-IHBePmfWH5lKhJnJ7WB1V+v/GolbB0rjS8XYVCSQCZKaQCAUhMoVoOEn1Ef8Z8Wf0a7l8KTJvuZg5/e4qrZ6nA==", "dev": true, "license": "MIT", "dependencies": { @@ -8047,9 +8048,9 @@ "license": "MIT" }, "node_modules/flow-parser": { - "version": "0.246.0", - "resolved": "https://registry.npmjs.org/flow-parser/-/flow-parser-0.246.0.tgz", - "integrity": "sha512-WHRizzSrWFTcKo7cVcbP3wzZVhzsoYxoWqbnH4z+JXGqrjVmnsld6kBZWVlB200PwD5ur8r+HV3KUDxv3cHhOQ==", + "version": "0.247.1", + "resolved": "https://registry.npmjs.org/flow-parser/-/flow-parser-0.247.1.tgz", + "integrity": "sha512-DHwcm06fWbn2Z6uFD3NaBZ5lMOoABIQ4asrVA80IWvYjjT5WdbghkUOL1wIcbLcagnFTdCZYOlSNnKNp/xnRZQ==", "license": "MIT", "engines": { "node": ">=0.4.0" @@ -13056,6 +13057,30 @@ "node": ">=10" } }, + "node_modules/react-native-vision-camera": { + "version": "4.5.3", + "resolved": "https://registry.npmjs.org/react-native-vision-camera/-/react-native-vision-camera-4.5.3.tgz", + "integrity": "sha512-wulpS6ZY6SwuYMDD8csIFMFJ6lXeQq4bJvaAhwEnr0R9cZ53jDUs0/SFZva6UFEPIw64h0IdrrzuVTEufhDSgQ==", + "license": "MIT", + "peerDependencies": { + "@shopify/react-native-skia": "*", + "react": "*", + "react-native": "*", + "react-native-reanimated": "*", + "react-native-worklets-core": "*" + }, + "peerDependenciesMeta": { + "@shopify/react-native-skia": { + "optional": true + }, + "react-native-reanimated": { + "optional": true + }, + "react-native-worklets-core": { + "optional": true + } + } + }, "node_modules/react-native/node_modules/@jest/types": { "version": "26.6.2", "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz", @@ -14224,9 +14249,9 @@ } }, "node_modules/terser": { - "version": "5.33.0", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.33.0.tgz", - "integrity": "sha512-JuPVaB7s1gdFKPKTelwUyRq5Sid2A3Gko2S0PncwdBq7kN9Ti9HPWDQ06MPsEDGsZeVESjKEnyGy68quBk1w6g==", + "version": "5.34.1", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.34.1.tgz", + "integrity": "sha512-FsJZ7iZLd/BXkz+4xrRTGJ26o/6VTjQytUk8b8OxkwcD2I+79VPJlz7qss1+zE7h8GNIScFqXcDyJ/KqBYZFVA==", "license": "BSD-2-Clause", "dependencies": { "@jridgewell/source-map": "^0.3.3", @@ -14640,9 +14665,9 @@ } }, "node_modules/update-browserslist-db": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.0.tgz", - "integrity": "sha512-EdRAaAyk2cUE1wOf2DkEhzxqOQvFOoRJFNS6NeyJ01Gp2beMRpBAINjM2iDXE3KCuKhwnvHIQCJm6ThL2Z+HzQ==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.1.tgz", + "integrity": "sha512-R8UzCaa9Az+38REPiJ1tXlImTJXlVfgHZsglwBD/k6nj76ctsH1E3q4doGrukiLQd3sGQYu56r5+lo5r94l29A==", "funding": [ { "type": "opencollective", @@ -14659,8 +14684,8 @@ ], "license": "MIT", "dependencies": { - "escalade": "^3.1.2", - "picocolors": "^1.0.1" + "escalade": "^3.2.0", + "picocolors": "^1.1.0" }, "bin": { "update-browserslist-db": "cli.js" diff --git a/package.json b/package.json index 977587c..3e8c860 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,8 @@ "react-native": "0.75.3", "react-native-modal": "^13.0.1", "react-native-status-keycard": "git+https://github.com/status-im/react-native-status-keycard.git#refs/tags/v2.6.0", - "react-native-vector-icons": "^10.1.0" + "react-native-vector-icons": "^10.1.0", + "react-native-vision-camera": "^4.5.3" }, "devDependencies": { "@babel/core": "^7.20.0", diff --git a/src/Main.tsx b/src/Main.tsx index 724bc0d..77ba145 100644 --- a/src/Main.tsx +++ b/src/Main.tsx @@ -5,23 +5,26 @@ import AsyncStorage from '@react-native-async-storage/async-storage'; import DiscoveryScreen from './components/steps/DiscoveryScreen'; import InitializationScreen from './components/steps/InitializationScreen'; import MnemonicScreen from './components/steps/MnemonicScreen'; -import AuthenticationScreen from './components/steps/AuthenticationScreen'; +import HomeScreen from './components/steps/HomeScreen'; import FactoryResetScreen from './components/steps/FactoryResetScreen'; import NFCModal from './NFCModal'; //@ts-ignore import Keycard from "react-native-status-keycard"; +import Dialpad from './components/Dialpad'; enum Step { Discovery, Initialization, Loading, Authentication, + Home, FactoryReset, } const Main = () => { const PIN_MAX_RETRY = 3; + const WALLET_DERIVATION_PATH = "m/84'/0'/0'/0/0"; const [isModalVisible, setIsModalVisible] = useState(false); const [step, setStep] = useState(Step.Discovery); @@ -32,6 +35,9 @@ const Main = () => { const pinRef = useRef(""); const mnemonicRef = useRef(""); const isListeningCard = useRef(false); + const walletKey = useRef(""); + const sessionId = useRef("") + const challenge = useRef("") const getPairings = async () => { const pairingJSON = await AsyncStorage.getItem("pairings"); @@ -97,10 +103,10 @@ const Main = () => { break; case Step.Loading: await Keycard.saveMnemonic(mnemonicRef.current, pinRef.current); - setStep(Step.Authentication); - break; + //fallthrough case Step.Authentication: - setStep(Step.Discovery); + walletKey.current = await Keycard.exportKeyWithPath(pinRef.current, WALLET_DERIVATION_PATH); + setStep(Step.Home); break; case Step.FactoryReset: await Keycard.factoryReset(); @@ -195,6 +201,16 @@ const Main = () => { return connectCard(); } + const authenticate = (pin: string) => { + pinRef.current = pin + connectCard(); + return true; + } + + const login = (sessionId: string, challenge: string) => { + + } + const cancel = () => { setStep(Step.Discovery); } @@ -208,8 +224,9 @@ const Main = () => { {step == Step.Discovery && } {step == Step.Initialization && } {step == Step.Loading && } - {step == Step.Authentication && {} } onCancelFunc={cancel}>} - {step == Step.FactoryReset && } + {step == Step.Authentication && } + {step == Step.Home && } + {step == Step.FactoryReset && } ); diff --git a/src/components/steps/AuthenticationScreen.tsx b/src/components/steps/AuthenticationScreen.tsx deleted file mode 100644 index f08759f..0000000 --- a/src/components/steps/AuthenticationScreen.tsx +++ /dev/null @@ -1,41 +0,0 @@ -import {FC } from "react"; -import { StyleSheet, Text, View } from "react-native"; -import Button from "../Button"; - -type AuthenticationScreenProps = { - pinRetryCounter: number; - onPressFunc: () => void; - onCancelFunc: () => void; -}; - -const AuthenticationScreen: FC = props => { - const {pinRetryCounter, onPressFunc, onCancelFunc} = props; - - return ( - - - Success - Work in progress... - - - - )}; - -const styles = StyleSheet.create({ - heading: { - textAlign: 'center', - fontSize: 30, - fontFamily: 'Inconsolata Medium', - color: '#199515', - marginTop: '50%' - }, - prompt: { - textAlign: 'center', - fontSize: 18, - fontFamily: 'Inconsolata Regular', - color: 'white', - marginTop: '5%' - } -}); - -export default AuthenticationScreen; \ No newline at end of file diff --git a/src/components/steps/HomeScreen.tsx b/src/components/steps/HomeScreen.tsx new file mode 100644 index 0000000..1aa1606 --- /dev/null +++ b/src/components/steps/HomeScreen.tsx @@ -0,0 +1,62 @@ +import {FC, useEffect } from "react"; +import { StyleSheet, Text, View } from "react-native"; +import Button from "../Button"; +import { useCameraDevice, useCameraPermission, useCodeScanner } from "react-native-vision-camera"; + +type HomeScreenProps = { + walletKey: string; + onPressFunc: (sessionId: string, challenge: string) => void; + onCancelFunc: () => void; +}; + +const HomeScreen: FC = props => { + const {walletKey, onPressFunc, onCancelFunc} = props; + const cameraDevice = useCameraDevice('back') + const { hasPermission, requestPermission } = useCameraPermission(); + const codeScanner = useCodeScanner({ + codeTypes: ['qr'], + onCodeScanned: (codes) => { + console.log(`Scanned ${codes.length} codes!`) + } + }); + + const walletAddress = () => { + return walletKey; + } + + useEffect(() => { + if (!hasPermission) { + if (!requestPermission()) { + onCancelFunc(); + }; + } + }); + + return ( + + + Success + {walletAddress()} + + + + )}; + +const styles = StyleSheet.create({ + heading: { + textAlign: 'center', + fontSize: 30, + fontFamily: 'Inconsolata Medium', + color: '#199515', + marginTop: '50%' + }, + prompt: { + textAlign: 'center', + fontSize: 18, + fontFamily: 'Inconsolata Regular', + color: 'white', + marginTop: '5%' + } +}); + +export default HomeScreen; \ No newline at end of file