From bde59d33d04dbc4dbd1a76cc1edf5c20ba33f112 Mon Sep 17 00:00:00 2001 From: Evan Kaloudis Date: Thu, 13 Apr 2023 16:41:13 -0400 Subject: [PATCH 1/3] Satscard --- Navigation.ts | 4 + locales/en.json | 10 + package.json | 4 +- views/Settings/Satscard.tsx | 586 +++++ views/Settings/Settings.tsx | 32 + yarn.lock | 4814 ++++++++++++++++++----------------- 6 files changed, 3180 insertions(+), 2270 deletions(-) create mode 100644 views/Settings/Satscard.tsx diff --git a/Navigation.ts b/Navigation.ts index eff4cf4c5c..690baa47a2 100644 --- a/Navigation.ts +++ b/Navigation.ts @@ -50,6 +50,7 @@ import PointOfSaleRecon from './views/Settings/PointOfSaleRecon'; import PointOfSaleReconExport from './views/Settings/PointOfSaleReconExport'; import PaymentsSettings from './views/Settings/PaymentsSettings'; import InvoicesSettings from './views/Settings/InvoicesSettings'; +import Satscard from './views/Settings/Satscard'; // Routing import Routing from './views/Routing/Routing'; @@ -247,6 +248,9 @@ const AppScenes = { PointOfSaleReconExport: { screen: PointOfSaleReconExport }, + Satscard: { + screen: Satscard + }, PaymentsSettings: { screen: PaymentsSettings }, diff --git a/locales/en.json b/locales/en.json index 5eaaff79c7..95c17839bb 100644 --- a/locales/en.json +++ b/locales/en.json @@ -53,6 +53,8 @@ "general.false": "False", "general.force": "Force", "general.proceed": "Proceed", + "general.id": "ID", + "general.version": "Version", "general.fiatFetchError": "Error fetching exchange rates", "components.CollapsedQr.show": "Show QR", "components.CollapsedQr.hide": "Hide QR", @@ -118,6 +120,14 @@ "views.Routing.RoutingEvent.title": "You received", "views.Routing.SetFees": "Your node fees", "views.Routing.channelFees": "Edit channel fees", + "views.Satscard.load": "Load Satscard", + "views.Satscard.unseal": "Unseal", + "views.Satscard.birthHeight": "Birth height", + "views.Satscard.totalSlots": "Total slots", + "views.Satscard.activeSlot": "Active slot", + "views.Satscard.slotStatus": "Slot status", + "views.Satscard.generateAddress": "Generate address", + "views.Satscard.tapsignerNotSupported": "Tapsigner not supported", "views.Settings.AddEditNode.certificateButton": "Certificate Install Instructions", "views.Settings.AddEditNode.nodeConfig": "Node Configuration", "views.Settings.AddEditNode.connectionStringClipboard": "Detected the following connection string in your clipboard", diff --git a/package.json b/package.json index 97c2d724f3..0b59927797 100644 --- a/package.json +++ b/package.json @@ -29,7 +29,7 @@ "@react-native-picker/picker": "2.4.8", "@react-navigation/bottom-tabs": "5.11.11", "@react-navigation/native": "6.0.16", - "@remobile/react-native-qrcode-local-image": "github:BlueWallet/react-native-qrcode-local-image#b8baa79", + "@remobile/react-native-qrcode-local-image": "github:BlueWallet/react-native-qrcode-local-image#master", "@tradle/react-native-http": "2.0.1", "@types/react-native-snap-carousel": "3.8.5", "assert": "1.5.0", @@ -42,6 +42,7 @@ "browserify-zlib": "0.1.4", "bs58check": "2.1.2", "buffer": "5.6.0", + "cktap-protocol-react-native": "git+https://github.com/bithyve/cktap-protocol-react-native.git#main", "console-browserify": "1.2.0", "constants-browserify": "1.0.0", "create-hash": "1.2.0", @@ -49,6 +50,7 @@ "dns.js": "1.0.1", "domain-browser": "1.2.0", "elliptic": "6.5.4", + "eslint-plugin-ft-flow": "2.0.3", "events": "1.1.1", "fast-sha256": "1.3.0", "hash.js": "1.1.7", diff --git a/views/Settings/Satscard.tsx b/views/Settings/Satscard.tsx new file mode 100644 index 0000000000..49c856c884 --- /dev/null +++ b/views/Settings/Satscard.tsx @@ -0,0 +1,586 @@ +import React from 'react'; +import { + Platform, + ScrollView, + StyleSheet, + Text, + TouchableOpacity, + View +} from 'react-native'; +import { ButtonGroup, Header, Icon } from 'react-native-elements'; +import { inject, observer } from 'mobx-react'; + +import { localeString } from './../../utils/LocaleUtils'; +import { themeColor } from './../../utils/ThemeUtils'; + +import TextInput from '../../components/TextInput'; + +import Button from '../../components/Button'; +import CollapsedQR from '../../components/CollapsedQR'; +import KeyValue from '../../components/KeyValue'; +import { ErrorMessage } from '../../components/SuccessErrorMessage'; + +import { CKTapCard } from 'cktap-protocol-react-native'; + +import ModalStore from '../../stores/ModalStore'; +import SettingsStore from './../../stores/SettingsStore'; + +interface SatscardProps { + navigation: any; + ModalStore: ModalStore; + SettingsStore: SettingsStore; +} + +interface SatscardState { + selectedIndex: number; + card: CKTapCard; + cardStatus: any; + pubkey: string; + privkey: string; + tapsignerError: boolean; + error: string; + slotStatus: string; + cvv: string; + selectedSlot: number; + selectedSlotIndex: number; + selectedSlotStatus: string; +} + +interface CardStatus { + card_ident: string; + active_slot: number; + birth_height: number; + is_tapsigner: boolean; + applet_version: string; + num_slots: number; +} + +@inject('ModalStore', 'SettingsStore') +@observer +export default class Satscard extends React.Component< + SatscardProps, + SatscardState +> { + constructor(props) { + super(props); + const card = new CKTapCard(); + this.state = { + cvv: '', + selectedIndex: 0, + selectedSlot: 0, + card, + cardStatus: null, + pubkey: '', + privkey: '', + slotStatus: '', + tapsignerError: false, + error: '' + }; + } + + renderSeparator = () => ( + + ); + + render() { + const { navigation, ModalStore } = this.props; + const { + cvv, + card, + cardStatus, + pubkey, + privkey, + slotStatus, + selectedIndex, + tapsignerError, + error, + selectedSlot, + selectedSlotIndex, + selectedSlotStatus + } = this.state; + + const BackButton = () => ( + + navigation.navigate('Settings', { + refresh: true + }) + } + color={themeColor('text')} + underlayColor="transparent" + /> + ); + + const infoButton = () => ( + + Info + + ); + + const addressButton = () => ( + + Address + + ); + + const slotButton = (index: number) => ( + + {index.toString()} + + ); + + const ClearButton = () => ( + + this.setState({ + pubkey: '', + cardStatus: null, + error: '' + }) + } + color={themeColor('text')} + underlayColor="transparent" + /> + ); + + const buttons = [{ element: infoButton }, { element: addressButton }]; + const slotButtons = + cardStatus && cardStatus.num_slots + ? [...Array(cardStatus.num_slots)].map((_, i) => { + return slotButton(i); + }) + : null; + + const awaitNfc = () => { + // enable NFC + if (Platform.OS === 'android') { + ModalStore.toggleAndroidNfcModal(true); + } + + this.setState({ + error: '' + }); + }; + + return ( + +
} + centerComponent={{ + text: 'Satscard', + style: { + color: themeColor('text'), + fontFamily: 'Lato-Regular' + } + }} + rightComponent={cardStatus ? : null} + backgroundColor={themeColor('background')} + containerStyle={{ + borderBottomWidth: 0 + }} + /> + + {tapsignerError && ( + + )} + {error && ( + this.setState({ error: '' })} + > + + + )} + {cardStatus && ( + { + this.setState({ selectedIndex }); + }} + selectedIndex={selectedIndex} + buttons={buttons} + selectedButtonStyle={{ + backgroundColor: themeColor('highlight'), + borderRadius: 12 + }} + containerStyle={{ + backgroundColor: themeColor('secondary'), + borderRadius: 12, + borderColor: themeColor('secondary') + }} + innerBorderStyle={{ + color: themeColor('secondary') + }} + /> + )} + {selectedIndex === 0 && cardStatus && ( + <> + {false && ( + + )} + + + + + + + { + if (text.length === 7) return; + this.setState({ + cvv: text + }); + }} + /> +