Merge branch 'main' of https://github.com/status-im/keycard-exit
This commit is contained in:
commit
b5d00c8d69
|
@ -8,6 +8,7 @@
|
||||||
"name": "KeycardExit",
|
"name": "KeycardExit",
|
||||||
"version": "0.0.1",
|
"version": "0.0.1",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@react-native-async-storage/async-storage": "^2.0.0",
|
||||||
"react": "18.3.1",
|
"react": "18.3.1",
|
||||||
"react-native": "0.75.2",
|
"react-native": "0.75.2",
|
||||||
"react-native-modal": "^13.0.1",
|
"react-native-modal": "^13.0.1",
|
||||||
|
@ -2935,6 +2936,17 @@
|
||||||
"node": ">= 8"
|
"node": ">= 8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@react-native-async-storage/async-storage": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@react-native-async-storage/async-storage/-/async-storage-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-af6H9JjfL6G/PktBfUivvexoiFKQTJGQCtSWxMdivLzNIY94mu9DdiY0JqCSg/LyPCLGKhHPUlRQhNvpu3/KVA==",
|
||||||
|
"dependencies": {
|
||||||
|
"merge-options": "^3.0.4"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react-native": "^0.0.0-0 || >=0.65 <1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@react-native-community/cli": {
|
"node_modules/@react-native-community/cli": {
|
||||||
"version": "14.0.0",
|
"version": "14.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/@react-native-community/cli/-/cli-14.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/@react-native-community/cli/-/cli-14.0.0.tgz",
|
||||||
|
@ -8298,6 +8310,14 @@
|
||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/is-plain-obj": {
|
||||||
|
"version": "2.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz",
|
||||||
|
"integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/is-plain-object": {
|
"node_modules/is-plain-object": {
|
||||||
"version": "2.0.4",
|
"version": "2.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz",
|
||||||
|
@ -10688,6 +10708,17 @@
|
||||||
"resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-5.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-5.2.1.tgz",
|
||||||
"integrity": "sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q=="
|
"integrity": "sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q=="
|
||||||
},
|
},
|
||||||
|
"node_modules/merge-options": {
|
||||||
|
"version": "3.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/merge-options/-/merge-options-3.0.4.tgz",
|
||||||
|
"integrity": "sha512-2Sug1+knBjkaMsMgf1ctR1Ujx+Ayku4EdJN4Z+C2+JzoeF7A3OZ9KM2GY0CpQS51NR61LTurMJrRKPhSs3ZRTQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"is-plain-obj": "^2.1.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/merge-stream": {
|
"node_modules/merge-stream": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz",
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
"test": "jest"
|
"test": "jest"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@react-native-async-storage/async-storage": "^2.0.0",
|
||||||
"react": "18.3.1",
|
"react": "18.3.1",
|
||||||
"react-native": "0.75.2",
|
"react-native": "0.75.2",
|
||||||
"react-native-modal": "^13.0.1",
|
"react-native-modal": "^13.0.1",
|
||||||
|
|
78
src/Main.tsx
78
src/Main.tsx
|
@ -1,14 +1,16 @@
|
||||||
import React, { useEffect, useRef, useState } from 'react';
|
import React, { useEffect, useRef, useState } from 'react';
|
||||||
import { Platform, SafeAreaView, StyleSheet, useColorScheme, DeviceEventEmitter } from 'react-native';
|
import { SafeAreaView, StyleSheet, useColorScheme, DeviceEventEmitter } from 'react-native';
|
||||||
import { Colors } from 'react-native/Libraries/NewAppScreen';
|
import { Colors } from 'react-native/Libraries/NewAppScreen';
|
||||||
|
import AsyncStorage from '@react-native-async-storage/async-storage';
|
||||||
|
|
||||||
import DiscoveryScreen from './components/steps/DiscoveryScreen';
|
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 NFCModal from './NFCModal';
|
||||||
|
|
||||||
//@ts-ignore
|
//@ts-ignore
|
||||||
import Keycard from "react-native-status-keycard";
|
import Keycard from "react-native-status-keycard";
|
||||||
import InitializationScreen from './components/steps/InitializationScreen';
|
|
||||||
import NFCModal from './NFCModal';
|
|
||||||
import MnemonicScreen from './components/steps/MnemonicScreen';
|
|
||||||
import AuthenticationScreen from './components/steps/AuthenticationScreen';
|
|
||||||
|
|
||||||
enum Step {
|
enum Step {
|
||||||
Discovery,
|
Discovery,
|
||||||
|
@ -26,11 +28,45 @@ const Main = () => {
|
||||||
const stepRef = useRef(step);
|
const stepRef = useRef(step);
|
||||||
const pinRef = useRef("");
|
const pinRef = useRef("");
|
||||||
const mnemonicRef = useRef("");
|
const mnemonicRef = useRef("");
|
||||||
|
const isListeningCard = useRef(false);
|
||||||
|
|
||||||
|
const getPairings = async () => {
|
||||||
|
const pairingJSON = await AsyncStorage.getItem("pairings");
|
||||||
|
return pairingJSON ? JSON.parse(pairingJSON) : {};
|
||||||
|
}
|
||||||
|
|
||||||
|
const addPairing = async (instanceUID, pairing) => {
|
||||||
|
const pairings = await getPairings();
|
||||||
|
pairings[instanceUID] = {pairing: pairing};
|
||||||
|
return AsyncStorage.setItem("pairings", JSON.stringify(pairings));
|
||||||
|
}
|
||||||
|
|
||||||
|
const isCardLost = (err) => {
|
||||||
|
return (err == "Tag was lost.") || err.includes("NFCError:100");
|
||||||
|
}
|
||||||
|
|
||||||
|
const wrongPINCounter = (err) => {
|
||||||
|
const matches = /Unexpected error SW, 0x63C(\d+)|wrongPIN\(retryCounter: (\d+)\)/.exec(err)
|
||||||
|
|
||||||
|
if (matches && matches.length == 2) {
|
||||||
|
return parseInt(matches[1])
|
||||||
|
}
|
||||||
|
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
const keycardConnectHandler = async () => {
|
const keycardConnectHandler = async () => {
|
||||||
|
if (!isListeningCard.current) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const appInfo = await Keycard.getApplicationInfo();
|
const appInfo = await Keycard.getApplicationInfo();
|
||||||
|
|
||||||
|
if (appInfo["new-pairing"]) {
|
||||||
|
await addPairing(appInfo["instance-uid"], appInfo["new-pairing"]);
|
||||||
|
}
|
||||||
|
|
||||||
switch (stepRef.current) {
|
switch (stepRef.current) {
|
||||||
case Step.Discovery:
|
case Step.Discovery:
|
||||||
if (appInfo["initialized?"]) {
|
if (appInfo["initialized?"]) {
|
||||||
|
@ -58,22 +94,39 @@ const Main = () => {
|
||||||
setStep(Step.Discovery);
|
setStep(Step.Discovery);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
} catch (err: any) {
|
||||||
if (pinRef.current) {
|
if (isCardLost(err.message)) {
|
||||||
await Keycard.unpair(pinRef.current);
|
console.log("connection to card lost");
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
} catch (err) {
|
|
||||||
|
const pinRetryCounter = wrongPINCounter(err.message);
|
||||||
|
|
||||||
|
if (pinRetryCounter !== null) {
|
||||||
|
//TODO: better handling
|
||||||
|
console.log("wrong PIN. Retry counter: " + pinRetryCounter);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
console.log(err);
|
console.log(err);
|
||||||
}
|
}
|
||||||
|
|
||||||
await Keycard.stopNFC("");
|
await Keycard.stopNFC("");
|
||||||
setIsModalVisible(false);
|
setIsModalVisible(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
stepRef.current = step;
|
stepRef.current = step;
|
||||||
|
isListeningCard.current = isModalVisible;
|
||||||
|
|
||||||
if (!didMount.current) {
|
if (!didMount.current) {
|
||||||
didMount.current = true;
|
didMount.current = true;
|
||||||
|
|
||||||
|
const loadPairing = async () => {
|
||||||
|
await Keycard.setPairings(await getPairings());
|
||||||
|
};
|
||||||
|
|
||||||
|
loadPairing().catch(console.log);
|
||||||
DeviceEventEmitter.addListener("keyCardOnConnected", keycardConnectHandler);
|
DeviceEventEmitter.addListener("keyCardOnConnected", keycardConnectHandler);
|
||||||
DeviceEventEmitter.addListener("keyCardOnDisconnected", () => console.log("keycard disconnected"));
|
DeviceEventEmitter.addListener("keyCardOnDisconnected", () => console.log("keycard disconnected"));
|
||||||
DeviceEventEmitter.addListener("keyCardOnNFCEnabled", () => console.log("nfc enabled"));
|
DeviceEventEmitter.addListener("keyCardOnNFCEnabled", () => console.log("nfc enabled"));
|
||||||
|
@ -92,9 +145,7 @@ const Main = () => {
|
||||||
|
|
||||||
await Keycard.startNFC("Tap your Keycard");
|
await Keycard.startNFC("Tap your Keycard");
|
||||||
|
|
||||||
if (Platform.OS === 'android') {
|
setIsModalVisible(true);
|
||||||
setIsModalVisible(true);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const initPin = async (p: string) => {
|
const initPin = async (p: string) => {
|
||||||
|
@ -103,9 +154,10 @@ const Main = () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const loadMnemonic = (mnemonic: string, p?: string) => {
|
const loadMnemonic = (mnemonic: string, p?: string) => {
|
||||||
if(p) {
|
if (p) {
|
||||||
pinRef.current = p;
|
pinRef.current = p;
|
||||||
}
|
}
|
||||||
|
|
||||||
mnemonicRef.current = mnemonic;
|
mnemonicRef.current = mnemonic;
|
||||||
|
|
||||||
return connectCard();
|
return connectCard();
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import React, {FC, useEffect, useState } from "react";
|
import React, {FC, useEffect, useState } from "react";
|
||||||
import {StyleSheet, Text, View } from "react-native";
|
import {Platform, StyleSheet, Text, View } from "react-native";
|
||||||
import Modal from "react-native-modal/dist/modal";
|
import Modal from "react-native-modal/dist/modal";
|
||||||
|
|
||||||
type NFCModalProps = {
|
type NFCModalProps = {
|
||||||
|
@ -11,7 +11,7 @@ const NFCModal: FC<NFCModalProps> = props => {
|
||||||
const {isVisible, onChangeFunc} = props;
|
const {isVisible, onChangeFunc} = props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal isVisible={isVisible} onSwipeComplete={() => onChangeFunc(!isVisible)} swipeDirection={['up', 'left', 'right', 'down']} style={modalStyle.modalContainer}>
|
<Modal isVisible={(Platform.OS === 'android') && isVisible} onSwipeComplete={() => onChangeFunc(!isVisible)} swipeDirection={['up', 'left', 'right', 'down']} style={modalStyle.modalContainer}>
|
||||||
<View style={modalStyle.container}>
|
<View style={modalStyle.container}>
|
||||||
<Text style={modalStyle.header}>Ready to Scan</Text>
|
<Text style={modalStyle.header}>Ready to Scan</Text>
|
||||||
<Text style={modalStyle.prompt}>Tap your Keycard</Text>
|
<Text style={modalStyle.prompt}>Tap your Keycard</Text>
|
||||||
|
|
Loading…
Reference in New Issue