mirror of
https://github.com/selfxyz/self.git
synced 2026-04-27 03:01:15 -04:00
Merge branch 'dev' into android-release
This commit is contained in:
187
app/App.tsx
187
app/App.tsx
@@ -1,23 +1,9 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import {
|
||||
SafeAreaView,
|
||||
ScrollView,
|
||||
StatusBar,
|
||||
StyleSheet,
|
||||
useColorScheme,
|
||||
NativeModules,
|
||||
DeviceEventEmitter,
|
||||
TextInput,
|
||||
Platform,
|
||||
} from 'react-native';
|
||||
|
||||
import {
|
||||
Colors,
|
||||
DebugInstructions,
|
||||
Header,
|
||||
LearnMoreLinks,
|
||||
ReloadInstructions,
|
||||
} from 'react-native/Libraries/NewAppScreen';
|
||||
import Toast, { BaseToast, ErrorToast, SuccessToast, ToastProps } from 'react-native-toast-message';
|
||||
// @ts-ignore
|
||||
import PassportReader from 'react-native-passport-reader';
|
||||
@@ -28,7 +14,7 @@ import {
|
||||
DEFAULT_DOE,
|
||||
DEFAULT_ADDRESS,
|
||||
} from '@env';
|
||||
import { DataHash, PassportData, mockPassportData } from '../common/src/utils/types';
|
||||
import { DataHash, PassportData } from '../common/src/utils/types';
|
||||
import { AWS_ENDPOINT } from '../common/src/constants/constants';
|
||||
import {
|
||||
hash,
|
||||
@@ -45,7 +31,7 @@ import {
|
||||
import { samplePassportData } from '../common/src/utils/passportDataStatic';
|
||||
|
||||
import "@ethersproject/shims"
|
||||
import { ethers } from "ethers";
|
||||
import { ethers, ZeroAddress } from "ethers";
|
||||
import axios from 'axios';
|
||||
import groth16ExportSolidityCallData from './utils/snarkjs';
|
||||
import contractAddresses from "./deployments/addresses.json"
|
||||
@@ -59,8 +45,6 @@ global.Buffer = Buffer;
|
||||
|
||||
console.log('DEFAULT_PNUMBER', DEFAULT_PNUMBER);
|
||||
|
||||
const SKIP_SCAN = false;
|
||||
|
||||
const attributeToPosition = {
|
||||
issuing_state: [2, 5],
|
||||
name: [5, 44],
|
||||
@@ -72,23 +56,19 @@ const attributeToPosition = {
|
||||
}
|
||||
|
||||
function App(): JSX.Element {
|
||||
|
||||
|
||||
const isDarkMode = useColorScheme() === 'dark';
|
||||
const [passportNumber, setPassportNumber] = useState(DEFAULT_PNUMBER ?? "");
|
||||
const [dateOfBirth, setDateOfBirth] = useState(DEFAULT_DOB ?? '');
|
||||
const [dateOfExpiry, setDateOfExpiry] = useState(DEFAULT_DOE ?? '');
|
||||
const [address, setAddress] = useState(DEFAULT_ADDRESS ?? '');
|
||||
const [passportData, setPassportData] = useState(samplePassportData);
|
||||
const [step, setStep] = useState(Steps.MRZ_SCAN);
|
||||
const [testResult, setTestResult] = useState<any>(null);
|
||||
const [address, setAddress] = useState<string>(ethers.ZeroAddress);
|
||||
const [passportData, setPassportData] = useState<PassportData>(samplePassportData as PassportData);
|
||||
const [step, setStep] = useState<number>(Steps.MRZ_SCAN);
|
||||
const [error, setError] = useState<any>(null);
|
||||
const [generatingProof, setGeneratingProof] = useState<boolean>(false);
|
||||
const [proofTime, setProofTime] = useState<number>(0);
|
||||
const [totalTime, setTotalTime] = useState<number>(0);
|
||||
const [proof, setProof] = useState<{ proof: string, inputs: string } | null>(null);
|
||||
const [minting, setMinting] = useState<boolean>(false);
|
||||
const [mintText, setMintText] = useState<string | null>(null);
|
||||
const [mintText, setMintText] = useState<string>("");
|
||||
|
||||
const [disclosure, setDisclosure] = useState({
|
||||
issuing_state: false,
|
||||
name: false,
|
||||
@@ -115,7 +95,7 @@ function App(): JSX.Element {
|
||||
setDateOfBirth(birthDate);
|
||||
setDateOfExpiry(expiryDate);
|
||||
setStep(Steps.MRZ_SCAN_COMPLETED);
|
||||
} catch (error) {
|
||||
} catch (error: any) {
|
||||
console.error('Invalid MRZ format:', error.message);
|
||||
}
|
||||
})
|
||||
@@ -133,19 +113,6 @@ function App(): JSX.Element {
|
||||
});
|
||||
};
|
||||
|
||||
const backgroundStyle = {
|
||||
backgroundColor: Colors.white,
|
||||
flex: 1
|
||||
};
|
||||
|
||||
const inputStyle = StyleSheet.create({
|
||||
inputField: {
|
||||
minHeight: 45, // Set a minimum height that fits the text
|
||||
// Add other styles as needed to match your design
|
||||
},
|
||||
// Include any other styles you want to apply to the input component
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
const logEventListener = DeviceEventEmitter.addListener('LOG_EVENT', e => {
|
||||
console.log(e);
|
||||
@@ -160,21 +127,23 @@ function App(): JSX.Element {
|
||||
if (Platform.OS !== 'android') {
|
||||
NativeModules.Prover.runInitAction() // for mopro, ios only rn
|
||||
}
|
||||
if (SKIP_SCAN && passportData === null) {
|
||||
setPassportData(samplePassportData as PassportData);
|
||||
setStep(Steps.NFC_SCAN_COMPLETED);
|
||||
}
|
||||
}, []);
|
||||
|
||||
async function handleResponseIOS(response: any) {
|
||||
const parsed = JSON.parse(response);
|
||||
|
||||
const eContentBase64 = parsed.eContentBase64; // this is what we call concatenatedDataHashes in our world
|
||||
const signedAttributes = parsed.signedAttributes; // this is what we call eContent in our world
|
||||
const eContentBase64 = parsed.eContentBase64; // this is what we call concatenatedDataHashes in android world
|
||||
const signedAttributes = parsed.signedAttributes; // this is what we call eContent in android world
|
||||
const signatureAlgorithm = parsed.signatureAlgorithm;
|
||||
const mrz = parsed.passportMRZ;
|
||||
const dataGroupHashes = parsed.dataGroupHashes;
|
||||
const signatureBase64 = parsed.signatureBase64;
|
||||
console.log('dataGroupsPresent', parsed.dataGroupsPresent)
|
||||
console.log('placeOfBirth', parsed.placeOfBirth)
|
||||
console.log('activeAuthenticationPassed', parsed.activeAuthenticationPassed)
|
||||
console.log('isPACESupported', parsed.isPACESupported)
|
||||
console.log('isChipAuthenticationSupported', parsed.isChipAuthenticationSupported)
|
||||
console.log('residenceAddress', parsed.residenceAddress)
|
||||
console.log('passportPhoto', parsed.passportPhoto.substring(0, 100) + '...')
|
||||
|
||||
console.log('parsed.documentSigningCertificate', parsed.documentSigningCertificate)
|
||||
const pem = JSON.parse(parsed.documentSigningCertificate).PEM.replace(/\\\\n/g, '\n')
|
||||
@@ -192,17 +161,6 @@ function App(): JSX.Element {
|
||||
const concatenatedDataHashesArray = Array.from(Buffer.from(eContentBase64, 'base64'));
|
||||
const concatenatedDataHashesArraySigned = concatenatedDataHashesArray.map(byte => byte > 127 ? byte - 256 : byte);
|
||||
|
||||
const dgHashes = JSON.parse(dataGroupHashes);
|
||||
console.log('dgHashes', dgHashes)
|
||||
|
||||
const dataGroupHashesArray = Object.keys(dgHashes)
|
||||
.map(key => {
|
||||
const dgNumber = parseInt(key.replace('DG', ''));
|
||||
const hashArray = hexStringToSignedIntArray(dgHashes[key].computedHash);
|
||||
return [dgNumber, hashArray];
|
||||
})
|
||||
.sort((a, b) => (a[0] as number) - (b[0] as number));
|
||||
|
||||
const encryptedDigestArray = Array.from(Buffer.from(signatureBase64, 'base64')).map(byte => byte > 127 ? byte - 256 : byte);
|
||||
|
||||
const passportData = {
|
||||
@@ -214,6 +172,7 @@ function App(): JSX.Element {
|
||||
dataGroupHashes: concatenatedDataHashesArraySigned,
|
||||
eContent: signedEContentArray,
|
||||
encryptedDigest: encryptedDigestArray,
|
||||
photoBase64: "data:image/jpeg;base64," + parsed.passportPhoto,
|
||||
};
|
||||
|
||||
console.log('mrz', passportData.mrz);
|
||||
@@ -224,7 +183,7 @@ function App(): JSX.Element {
|
||||
console.log('encryptedDigest', passportData.encryptedDigest);
|
||||
|
||||
setPassportData(passportData);
|
||||
setStep('scanCompleted');
|
||||
setStep(Steps.NFC_SCAN_COMPLETED);
|
||||
}
|
||||
|
||||
async function handleResponseAndroid(response: any) {
|
||||
@@ -237,6 +196,7 @@ function App(): JSX.Element {
|
||||
dataGroupHashes,
|
||||
eContent,
|
||||
encryptedDigest,
|
||||
photo
|
||||
} = response;
|
||||
|
||||
const passportData: PassportData = {
|
||||
@@ -250,6 +210,7 @@ function App(): JSX.Element {
|
||||
dataGroupHashes: dataHashesObjToArray(JSON.parse(dataGroupHashes)),
|
||||
eContent: JSON.parse(eContent),
|
||||
encryptedDigest: JSON.parse(encryptedDigest),
|
||||
photoBase64: photo.base64,
|
||||
};
|
||||
|
||||
console.log('mrz', passportData.mrz);
|
||||
@@ -258,6 +219,7 @@ function App(): JSX.Element {
|
||||
console.log('dataGroupHashes', passportData.dataGroupHashes);
|
||||
console.log('eContent', passportData.eContent);
|
||||
console.log('encryptedDigest', passportData.encryptedDigest);
|
||||
console.log("photoBase64", passportData.photoBase64.substring(0, 100) + '...')
|
||||
|
||||
setPassportData(passportData);
|
||||
setStep(Steps.NFC_SCAN_COMPLETED);
|
||||
@@ -290,7 +252,7 @@ function App(): JSX.Element {
|
||||
dateOfBirth: dateOfBirth,
|
||||
dateOfExpiry: dateOfExpiry,
|
||||
});
|
||||
console.log('response', response);
|
||||
// console.log('response', response);
|
||||
console.log('scanned');
|
||||
handleResponseAndroid(response);
|
||||
} catch (e: any) {
|
||||
@@ -391,7 +353,6 @@ function App(): JSX.Element {
|
||||
}
|
||||
const end = Date.now();
|
||||
console.log('Total proof time from frontend:', end - start);
|
||||
setTotalTime(end - start);
|
||||
};
|
||||
|
||||
async function proveAndroid(inputs: any, path: string) {
|
||||
@@ -449,7 +410,7 @@ function App(): JSX.Element {
|
||||
|
||||
// setProofTime(response.duration);
|
||||
setGeneratingProof(false)
|
||||
setStep('proofGenerated');
|
||||
setStep(Steps.PROOF_GENERATED);
|
||||
} catch (err: any) {
|
||||
console.log('err', err);
|
||||
setError(
|
||||
@@ -540,87 +501,37 @@ function App(): JSX.Element {
|
||||
};
|
||||
|
||||
return (
|
||||
<YStack f={1}>
|
||||
<SafeAreaView style={backgroundStyle}>
|
||||
<StatusBar
|
||||
barStyle={isDarkMode ? 'light-content' : 'dark-content'}
|
||||
backgroundColor={Colors.red}
|
||||
<YStack f={1} bg="white" h="100%" w="100%">
|
||||
<YStack h="100%" w="100%">
|
||||
<MainScreen
|
||||
onStartCameraScan={startCameraScan}
|
||||
nfcScan={scan}
|
||||
passportData={passportData}
|
||||
disclosure={disclosure}
|
||||
handleDisclosureChange={handleDisclosureChange}
|
||||
address={address}
|
||||
setAddress={setAddress}
|
||||
generatingProof={generatingProof}
|
||||
handleProve={handleProve}
|
||||
step={step}
|
||||
mintText={mintText}
|
||||
proof={proof}
|
||||
proofTime={proofTime}
|
||||
handleMint={handleMint}
|
||||
setStep={setStep}
|
||||
passportNumber={passportNumber}
|
||||
setPassportNumber={setPassportNumber}
|
||||
dateOfBirth={dateOfBirth}
|
||||
setDateOfBirth={setDateOfBirth}
|
||||
dateOfExpiry={dateOfExpiry}
|
||||
setDateOfExpiry={setDateOfExpiry}
|
||||
/>
|
||||
<ScrollView
|
||||
contentInsetAdjustmentBehavior="automatic"
|
||||
style={{
|
||||
backgroundColor: isDarkMode ? Colors.black : Colors.black,
|
||||
}}
|
||||
contentContainerStyle={{ flexGrow: 1 }}
|
||||
>
|
||||
<YStack style={styles.view}>
|
||||
<MainScreen
|
||||
onStartCameraScan={startCameraScan}
|
||||
nfcScan={scan}
|
||||
passportData={passportData}
|
||||
disclosure={disclosure}
|
||||
handleDisclosureChange={handleDisclosureChange}
|
||||
address={address}
|
||||
setAddress={setAddress}
|
||||
generatingProof={generatingProof}
|
||||
handleProve={handleProve}
|
||||
step={step}
|
||||
mintText={mintText}
|
||||
proof={proof}
|
||||
proofTime={proofTime}
|
||||
handleMint={handleMint}
|
||||
totalTime={totalTime}
|
||||
setStep={setStep}
|
||||
passportNumber={passportNumber}
|
||||
setPassportNumber={setPassportNumber}
|
||||
dateOfBirth={dateOfBirth}
|
||||
setDateOfBirth={setDateOfBirth}
|
||||
dateOfExpiry={dateOfExpiry}
|
||||
setDateOfExpiry={setDateOfExpiry}
|
||||
/>
|
||||
</YStack>
|
||||
</ScrollView>
|
||||
</SafeAreaView>
|
||||
</YStack>
|
||||
<Toast config={toastConfig} />
|
||||
</YStack>
|
||||
);
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
view: {
|
||||
flex: 1,
|
||||
},
|
||||
sectionContainer: {
|
||||
marginTop: 32,
|
||||
paddingHorizontal: 24,
|
||||
},
|
||||
sectionTitle: {
|
||||
fontSize: 24,
|
||||
fontWeight: '600',
|
||||
},
|
||||
sectionDescription: {
|
||||
marginTop: 8,
|
||||
fontSize: 18,
|
||||
fontWeight: '400',
|
||||
},
|
||||
highlight: {
|
||||
fontWeight: '700',
|
||||
},
|
||||
header: {
|
||||
fontSize: 22,
|
||||
fontWeight: 'bold',
|
||||
textAlign: 'center',
|
||||
marginTop: 20,
|
||||
},
|
||||
testSection: {
|
||||
backgroundColor: '#f2f2f2', // different background color
|
||||
padding: 10,
|
||||
borderTopWidth: 1,
|
||||
borderTopColor: '#dcdcdc', // adding a border top with a light color
|
||||
marginTop: 15,
|
||||
},
|
||||
});
|
||||
|
||||
export default App;
|
||||
|
||||
|
||||
|
||||
@@ -35,6 +35,12 @@ You might need to set the rust-toolchain rust version as global default. Example
|
||||
rustup default 1.67.0
|
||||
```
|
||||
|
||||
For macOs users you might also need to set-up the path to sdk:
|
||||
in /app/android create local.properties
|
||||
|
||||
Add the following line:
|
||||
sdk.dir=/Users/<user>/Library/Android/sdk or any relevant path to your sdk
|
||||
|
||||
This you modify the circuits, you might have to modify `ark-circom-passport/src/passport.rs` too.
|
||||
|
||||
#### Build the iOS native module
|
||||
|
||||
@@ -498,87 +498,84 @@ class RNPassportReaderModule(private val reactContext: ReactApplicationContext)
|
||||
}
|
||||
|
||||
override fun onPostExecute(result: Exception?) {
|
||||
if (scanPromise == null) return
|
||||
if (scanPromise == null) return
|
||||
|
||||
if (result != null) {
|
||||
// Log.w(TAG, exceptionStack(result))
|
||||
if (result is IOException) {
|
||||
scanPromise?.reject("E_SCAN_FAILED_DISCONNECT", "Lost connection to chip on card")
|
||||
} else {
|
||||
scanPromise?.reject("E_SCAN_FAILED", result)
|
||||
}
|
||||
if (result != null) {
|
||||
// Log.w(TAG, exceptionStack(result))
|
||||
if (result is IOException) {
|
||||
scanPromise?.reject("E_SCAN_FAILED_DISCONNECT", "Lost connection to chip on card")
|
||||
} else {
|
||||
scanPromise?.reject("E_SCAN_FAILED", result)
|
||||
}
|
||||
|
||||
resetState()
|
||||
return
|
||||
}
|
||||
resetState()
|
||||
return
|
||||
}
|
||||
|
||||
val mrzInfo = dg1File.mrzInfo
|
||||
val mrzInfo = dg1File.mrzInfo
|
||||
|
||||
// var quality = 100
|
||||
// if (opts?.hasKey("quality") == true) {
|
||||
// quality = (opts?.getDouble("quality") ?: 1.0 * 100).toInt()
|
||||
// }
|
||||
val gson = Gson()
|
||||
val gson = Gson()
|
||||
|
||||
val signedDataField = SODFile::class.java.getDeclaredField("signedData")
|
||||
signedDataField.isAccessible = true
|
||||
|
||||
// val signedData = signedDataField.get(sodFile) as SignedData
|
||||
|
||||
val eContentAsn1InputStream = ASN1InputStream(sodFile.eContent.inputStream())
|
||||
// val eContentDecomposed: ASN1Primitive = eContentAsn1InputStream.readObject()
|
||||
|
||||
val passport = Arguments.createMap()
|
||||
passport.putString("mrz", mrzInfo.toString())
|
||||
passport.putString("signatureAlgorithm", sodFile.docSigningCertificate.sigAlgName) // this one is new
|
||||
|
||||
val publicKey = sodFile.docSigningCertificate.publicKey
|
||||
if (publicKey is RSAPublicKey) {
|
||||
passport.putString("modulus", publicKey.modulus.toString())
|
||||
} else if (publicKey is ECPublicKey) {
|
||||
// Handle the elliptic curve public key case
|
||||
val signedDataField = SODFile::class.java.getDeclaredField("signedData")
|
||||
signedDataField.isAccessible = true
|
||||
|
||||
// val w = publicKey.getW()
|
||||
// passport.putString("publicKeyW", w.toString())
|
||||
// val signedData = signedDataField.get(sodFile) as SignedData
|
||||
|
||||
// val ecParams = publicKey.getParams()
|
||||
// passport.putInt("cofactor", ecParams.getCofactor())
|
||||
// passport.putString("curve", ecParams.getCurve().toString())
|
||||
// passport.putString("generator", ecParams.getGenerator().toString())
|
||||
// passport.putString("order", ecParams.getOrder().toString())
|
||||
// if (ecParams is ECNamedCurveSpec) {
|
||||
// passport.putString("curveName", ecParams.getName())
|
||||
// }
|
||||
val eContentAsn1InputStream = ASN1InputStream(sodFile.eContent.inputStream())
|
||||
// val eContentDecomposed: ASN1Primitive = eContentAsn1InputStream.readObject()
|
||||
|
||||
val passport = Arguments.createMap()
|
||||
passport.putString("mrz", mrzInfo.toString())
|
||||
passport.putString("signatureAlgorithm", sodFile.docSigningCertificate.sigAlgName) // this one is new
|
||||
|
||||
val publicKey = sodFile.docSigningCertificate.publicKey
|
||||
if (publicKey is RSAPublicKey) {
|
||||
passport.putString("modulus", publicKey.modulus.toString())
|
||||
} else if (publicKey is ECPublicKey) {
|
||||
// Handle the elliptic curve public key case
|
||||
|
||||
// val w = publicKey.getW()
|
||||
// passport.putString("publicKeyW", w.toString())
|
||||
|
||||
// val ecParams = publicKey.getParams()
|
||||
// passport.putInt("cofactor", ecParams.getCofactor())
|
||||
// passport.putString("curve", ecParams.getCurve().toString())
|
||||
// passport.putString("generator", ecParams.getGenerator().toString())
|
||||
// passport.putString("order", ecParams.getOrder().toString())
|
||||
// if (ecParams is ECNamedCurveSpec) {
|
||||
// passport.putString("curveName", ecParams.getName())
|
||||
// }
|
||||
|
||||
// Old one, probably wrong:
|
||||
// passport.putString("curveName", (publicKey.parameters as ECNamedCurveSpec).name)
|
||||
// passport.putString("curveName", (publicKey.parameters.algorithm)) or maybe this
|
||||
passport.putString("publicKeyQ", publicKey.q.toString())
|
||||
}
|
||||
|
||||
// Old one, probably wrong:
|
||||
// passport.putString("curveName", (publicKey.parameters as ECNamedCurveSpec).name)
|
||||
// passport.putString("curveName", (publicKey.parameters.algorithm)) or maybe this
|
||||
passport.putString("publicKeyQ", publicKey.q.toString())
|
||||
}
|
||||
|
||||
passport.putString("dataGroupHashes", gson.toJson(sodFile.dataGroupHashes))
|
||||
passport.putString("eContent", gson.toJson(sodFile.eContent))
|
||||
passport.putString("encryptedDigest", gson.toJson(sodFile.encryptedDigest))
|
||||
|
||||
|
||||
// Another way to get signing time is to get into signedData.signerInfos, then search for the ICO identifier 1.2.840.113549.1.9.5
|
||||
// passport.putString("signerInfos", gson.toJson(signedData.signerInfos))
|
||||
|
||||
// Log.d(TAG, "signedData.digestAlgorithms: ${gson.toJson(signedData.digestAlgorithms)}")
|
||||
// Log.d(TAG, "signedData.signerInfos: ${gson.toJson(signedData.signerInfos)}")
|
||||
// Log.d(TAG, "signedData.certificates: ${gson.toJson(signedData.certificates)}")
|
||||
|
||||
// val base64 = bitmap?.let { toBase64(it, quality) }
|
||||
// val photo = Arguments.createMap()
|
||||
// photo.putString("base64", base64 ?: "")
|
||||
// photo.putInt("width", bitmap?.width ?: 0)
|
||||
// photo.putInt("height", bitmap?.height ?: 0)
|
||||
// passport.putMap("photo", photo)
|
||||
// passport.putString("dg2File", gson.toJson(dg2File))
|
||||
|
||||
scanPromise?.resolve(passport)
|
||||
resetState()
|
||||
}
|
||||
passport.putString("dataGroupHashes", gson.toJson(sodFile.dataGroupHashes))
|
||||
passport.putString("eContent", gson.toJson(sodFile.eContent))
|
||||
passport.putString("encryptedDigest", gson.toJson(sodFile.encryptedDigest))
|
||||
|
||||
|
||||
// Another way to get signing time is to get into signedData.signerInfos, then search for the ICO identifier 1.2.840.113549.1.9.5
|
||||
// passport.putString("signerInfos", gson.toJson(signedData.signerInfos))
|
||||
|
||||
// Log.d(TAG, "signedData.digestAlgorithms: ${gson.toJson(signedData.digestAlgorithms)}")
|
||||
// Log.d(TAG, "signedData.signerInfos: ${gson.toJson(signedData.signerInfos)}")
|
||||
// Log.d(TAG, "signedData.certificates: ${gson.toJson(signedData.certificates)}")
|
||||
|
||||
var quality = 100
|
||||
val base64 = bitmap?.let { toBase64(it, quality) }
|
||||
val photo = Arguments.createMap()
|
||||
photo.putString("base64", base64 ?: "")
|
||||
photo.putInt("width", bitmap?.width ?: 0)
|
||||
photo.putInt("height", bitmap?.height ?: 0)
|
||||
passport.putMap("photo", photo)
|
||||
// passport.putString("dg2File", gson.toJson(dg2File))
|
||||
|
||||
scanPromise?.resolve(passport)
|
||||
resetState()
|
||||
}
|
||||
}
|
||||
|
||||
private fun convertDate(input: String?): String? {
|
||||
|
||||
BIN
app/assets/fonts/Luciole-Bold-Italic.ttf
Normal file
BIN
app/assets/fonts/Luciole-Bold-Italic.ttf
Normal file
Binary file not shown.
BIN
app/assets/fonts/Luciole-Bold.ttf
Normal file
BIN
app/assets/fonts/Luciole-Bold.ttf
Normal file
Binary file not shown.
BIN
app/assets/fonts/Luciole-Regular-Italic.ttf
Normal file
BIN
app/assets/fonts/Luciole-Regular-Italic.ttf
Normal file
Binary file not shown.
BIN
app/assets/fonts/Luciole-Regular.ttf
Normal file
BIN
app/assets/fonts/Luciole-Regular.ttf
Normal file
Binary file not shown.
14
app/index.js
14
app/index.js
@@ -2,17 +2,23 @@
|
||||
* @format
|
||||
*/
|
||||
|
||||
import { AppRegistry } from 'react-native';
|
||||
import { AppRegistry, LogBox } from 'react-native';
|
||||
import App from './App';
|
||||
import { name as appName } from './app.json';
|
||||
import { TamaguiProvider } from 'tamagui';
|
||||
import { createTamagui, createTokens } from 'tamagui';
|
||||
import { createTamagui } from 'tamagui';
|
||||
import { config } from '@tamagui/config/v2-native'
|
||||
import myAppConfig from './tamagui.config';
|
||||
|
||||
|
||||
const tamaguiConfig = createTamagui(config)
|
||||
|
||||
|
||||
LogBox.ignoreLogs([
|
||||
/bad setState/,
|
||||
])
|
||||
|
||||
const Root = () => (
|
||||
<TamaguiProvider config={tamaguiConfig}>
|
||||
<TamaguiProvider config={myAppConfig}>
|
||||
<App />
|
||||
</TamaguiProvider>
|
||||
);
|
||||
|
||||
@@ -118,6 +118,14 @@ class PassportReader: NSObject{
|
||||
ret["phoneNumber"] = passport.phoneNumber
|
||||
ret["personalNumber"] = passport.personalNumber
|
||||
|
||||
let passportPhotoData = passport.passportPhoto // [UInt8]
|
||||
if let passportPhotoData = passport.passportPhoto {
|
||||
let data = Data(passportPhotoData)
|
||||
let base64String = data.base64EncodedString()
|
||||
|
||||
ret["passportPhoto"] = base64String
|
||||
}
|
||||
|
||||
// documentSigningCertificate
|
||||
// countrySigningCertificate
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@ target 'ProofOfPassport' do
|
||||
flags = get_default_flags()
|
||||
|
||||
use_frameworks!
|
||||
pod 'NFCPassportReader', git: 'https://github.com/0xturboblitz/NFCPassportReader.git', commit: 'd36952eeaa2025ff1a9c9abbc244bd5ff53eb0f9'
|
||||
pod 'NFCPassportReader', git: 'https://github.com/0xturboblitz/NFCPassportReader.git', commit: '310ecb519655d9ed8b1afc5eb490b2f51a4d3595'
|
||||
pod 'MoproKit', :path => './MoproKit'
|
||||
|
||||
use_react_native!(
|
||||
|
||||
@@ -403,7 +403,7 @@ DEPENDENCIES:
|
||||
- FBReactNativeSpec (from `../node_modules/react-native/React/FBReactNativeSpec`)
|
||||
- glog (from `../node_modules/react-native/third-party-podspecs/glog.podspec`)
|
||||
- MoproKit (from `./MoproKit`)
|
||||
- NFCPassportReader (from `https://github.com/0xturboblitz/NFCPassportReader.git`, commit `d36952eeaa2025ff1a9c9abbc244bd5ff53eb0f9`)
|
||||
- NFCPassportReader (from `https://github.com/0xturboblitz/NFCPassportReader.git`, commit `310ecb519655d9ed8b1afc5eb490b2f51a4d3595`)
|
||||
- RCT-Folly (from `../node_modules/react-native/third-party-podspecs/RCT-Folly.podspec`)
|
||||
- RCTRequired (from `../node_modules/react-native/Libraries/RCTRequired`)
|
||||
- RCTTypeSafety (from `../node_modules/react-native/Libraries/TypeSafety`)
|
||||
@@ -460,7 +460,7 @@ EXTERNAL SOURCES:
|
||||
MoproKit:
|
||||
:path: "./MoproKit"
|
||||
NFCPassportReader:
|
||||
:commit: d36952eeaa2025ff1a9c9abbc244bd5ff53eb0f9
|
||||
:commit: 310ecb519655d9ed8b1afc5eb490b2f51a4d3595
|
||||
:git: https://github.com/0xturboblitz/NFCPassportReader.git
|
||||
RCT-Folly:
|
||||
:podspec: "../node_modules/react-native/third-party-podspecs/RCT-Folly.podspec"
|
||||
@@ -533,7 +533,7 @@ EXTERNAL SOURCES:
|
||||
|
||||
CHECKOUT OPTIONS:
|
||||
NFCPassportReader:
|
||||
:commit: d36952eeaa2025ff1a9c9abbc244bd5ff53eb0f9
|
||||
:commit: 310ecb519655d9ed8b1afc5eb490b2f51a4d3595
|
||||
:git: https://github.com/0xturboblitz/NFCPassportReader.git
|
||||
|
||||
SPEC CHECKSUMS:
|
||||
@@ -555,13 +555,13 @@ SPEC CHECKSUMS:
|
||||
React-Core: 8293312ad137ea82fd2c29deb163dbc24aa4e00e
|
||||
React-CoreModules: 32fab1d62416849a3b6dac6feff9d54e5ddc2d1e
|
||||
React-cxxreact: 55d0f7cb6b4cc09ba9190797f1da87182d1a2fb6
|
||||
React-debug: 7e61555c8158126c6cd98c3154381ad3821aaaca
|
||||
React-debug: 878f0c4026b30a6240f7a15f8612efcf5d8c3df9
|
||||
React-jsc: 0db8e8cc2074d979c37ffa7b8d7c914833960497
|
||||
React-jsi: 58677ff4848ceb6aeb9118fe03448a843ea5e16a
|
||||
React-jsiexecutor: 2c15ba1bace70177492368d5180b564f165870fd
|
||||
React-jsinspector: b511447170f561157547bc0bef3f169663860be7
|
||||
React-logger: c5b527272d5f22eaa09bb3c3a690fee8f237ae95
|
||||
React-NativeModulesApple: 0438665fc7473be6edc496e823e6ea0b0537b46c
|
||||
React-NativeModulesApple: 3a49a4bc38b979b804525816b781eb6612dba5fa
|
||||
React-perflogger: 6bd153e776e6beed54c56b0847e1220a3ff92ba5
|
||||
React-RCTActionSheet: c0b62af44e610e69d9a2049a682f5dba4e9dff17
|
||||
React-RCTAnimation: fe7005136b58f58871cab2f70732343b6e330d30
|
||||
@@ -575,13 +575,13 @@ SPEC CHECKSUMS:
|
||||
React-RCTVibration: ea3a68a49873a54ced927c90923fc6932baf344a
|
||||
React-rncore: 9672a017af4a7da7495d911f0b690cbcae9dd18d
|
||||
React-runtimeexecutor: 369ae9bb3f83b65201c0c8f7d50b72280b5a1dbc
|
||||
React-runtimescheduler: ec1066a4f2d1152eb1bc3fb61d69376b3bc0dde0
|
||||
React-utils: d55ba834beb39f01b0b470ae43478c0a3a024abe
|
||||
ReactCommon: 68e3a815fbb69af3bb4196e04c6ae7abb306e7a8
|
||||
React-runtimescheduler: 116fb55732ddfd96298350528cf13ceaf94759c8
|
||||
React-utils: a8681f0d721ff080373ae9e4afb1f380707b55f9
|
||||
ReactCommon: df6a7f5665621529ee01b89fb0c3c93eb014f276
|
||||
RNSVG: 07dbd870b0dcdecc99b3a202fa37c8ca163caec2
|
||||
SocketRocket: f32cd54efbe0f095c4d7594881e52619cfe80b17
|
||||
Yoga: 8796b55dba14d7004f980b54bcc9833ee45b28ce
|
||||
|
||||
PODFILE CHECKSUM: d401e6efe0c933985349c8c115c7edca8fef3182
|
||||
PODFILE CHECKSUM: 7568291077da8ee6387464cd1a7e01559a46ab1f
|
||||
|
||||
COCOAPODS: 1.14.3
|
||||
COCOAPODS: 1.15.0
|
||||
|
||||
@@ -490,8 +490,10 @@
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = ProofOfPassport/ProofOfPassport.entitlements;
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEVELOPMENT_TEAM = 5B29R5LYHQ;
|
||||
DEVELOPMENT_TEAM = X53ZR86F22;
|
||||
ENABLE_BITCODE = NO;
|
||||
INFOPLIST_FILE = ProofOfPassport/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
|
||||
@@ -512,8 +514,9 @@
|
||||
"-ObjC",
|
||||
"-lc++",
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.warroom.proofofpassport;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.warroom.proofofpassportdev;
|
||||
PRODUCT_NAME = ProofOfPassport;
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
SWIFT_VERSION = 5.0;
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
@@ -528,7 +531,7 @@
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = ProofOfPassport/ProofOfPassport.entitlements;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEVELOPMENT_TEAM = 5B29R5LYHQ;
|
||||
DEVELOPMENT_TEAM = X53ZR86F22;
|
||||
INFOPLIST_FILE = ProofOfPassport/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
@@ -547,7 +550,7 @@
|
||||
"-ObjC",
|
||||
"-lc++",
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.warroom.proofofpassport;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.warroom.proofofpassportdev;
|
||||
PRODUCT_NAME = ProofOfPassport;
|
||||
SWIFT_VERSION = 5.0;
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
|
||||
7
app/ios/ProofOfPassport.xcodeproj/project.xcworkspace/contents.xcworkspacedata
generated
Normal file
7
app/ios/ProofOfPassport.xcodeproj/project.xcworkspace/contents.xcworkspacedata
generated
Normal file
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Workspace
|
||||
version = "1.0">
|
||||
<FileRef
|
||||
location = "self:">
|
||||
</FileRef>
|
||||
</Workspace>
|
||||
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>IDEDidComputeMac32BitWarning</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -12,23 +12,26 @@
|
||||
"dependencies": {
|
||||
"@babel/plugin-transform-private-methods": "^7.23.3",
|
||||
"@ethersproject/shims": "^5.7.0",
|
||||
"@tamagui/config": "^1.84.0",
|
||||
"@tamagui/lucide-icons": "^1.84.0",
|
||||
"@tamagui/config": "^1.89.20",
|
||||
"@tamagui/core": "^1.89.20",
|
||||
"@tamagui/lucide-icons": "^1.89.20",
|
||||
"@tamagui/types": "^1.89.20",
|
||||
"axios": "^1.6.3",
|
||||
"body-parser": "^1.20.2",
|
||||
"buffer": "^6.0.3",
|
||||
"crypto-js": "^4.1.1",
|
||||
"ethers": "^6.9.1",
|
||||
"ethers": "^6.11.0",
|
||||
"express": "^4.18.2",
|
||||
"js-sha256": "^0.9.0",
|
||||
"node-forge": "^1.3.1",
|
||||
"pvutils": "^1.1.3",
|
||||
"react": "18.2.0",
|
||||
"react-native": "0.72.3",
|
||||
"react-native-canvas": "^0.1.39",
|
||||
"react-native-passport-reader": "^1.0.3",
|
||||
"react-native-svg": "13.4.0",
|
||||
"react-native-toast-message": "^2.2.0",
|
||||
"tamagui": "^1.84.0"
|
||||
"tamagui": "^1.89.20"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.20.0",
|
||||
|
||||
7
app/react-native.config.js
Normal file
7
app/react-native.config.js
Normal file
@@ -0,0 +1,7 @@
|
||||
module.exports = {
|
||||
project: {
|
||||
ios: {},
|
||||
android: {},
|
||||
},
|
||||
assets: ['./assets/fonts'],
|
||||
};
|
||||
@@ -1,40 +1,70 @@
|
||||
import React from 'react';
|
||||
import { Text, YStack, XStack, Card, H3, Image } from 'tamagui';
|
||||
import { ChevronRight } from '@tamagui/lucide-icons';
|
||||
import { Platform } from 'react-native';
|
||||
|
||||
const AppCard = ({ title, description, colorOfTheText, background, id, onTouchStart, eleva }) => {
|
||||
|
||||
interface AppCardProps {
|
||||
title: string;
|
||||
description: string;
|
||||
colorOfTheText: string;
|
||||
background: string | undefined;
|
||||
id: string | number;
|
||||
onTouchStart?: () => void;
|
||||
selected?: boolean;
|
||||
}
|
||||
|
||||
const AppCard: React.FC<AppCardProps> = ({
|
||||
title,
|
||||
description,
|
||||
colorOfTheText,
|
||||
background,
|
||||
id,
|
||||
onTouchStart,
|
||||
selected
|
||||
}) => {
|
||||
return (
|
||||
<Card
|
||||
key={id}
|
||||
<XStack
|
||||
overflow="hidden"
|
||||
elevation={selected ? "$3" : "$3"}
|
||||
borderRadius="$10"
|
||||
elevation={eleva}
|
||||
onTouchStart={onTouchStart}
|
||||
borderColor={(selected) ? "#3185FC" : ((Platform.OS === 'ios') ? "white" : "transparent")}
|
||||
borderWidth={(selected) ? 3 : 3}
|
||||
shadowColor={selected ? "#3185FC" : "black"}
|
||||
>
|
||||
<XStack
|
||||
<Card
|
||||
key={id}
|
||||
elevation={0}
|
||||
onTouchStart={onTouchStart}
|
||||
>
|
||||
<Card.Header w="100%">
|
||||
<XStack w="100%" ai="center" py="$1" >
|
||||
<YStack>
|
||||
<H3 color={colorOfTheText} selectable={false} >{title}</H3>
|
||||
<Text theme="alt2" color={colorOfTheText} selectable={false}>{description}</Text>
|
||||
</YStack>
|
||||
<XStack flex={1} />
|
||||
<ChevronRight size="$4" color={colorOfTheText} />
|
||||
</XStack>
|
||||
</Card.Header>
|
||||
{background && (
|
||||
<Card.Background>
|
||||
<Image
|
||||
flex={1}
|
||||
borderRadius="$10"
|
||||
source={{
|
||||
uri: background
|
||||
}}
|
||||
/>
|
||||
</Card.Background>
|
||||
)}
|
||||
</XStack>
|
||||
</Card >
|
||||
<XStack w="100%"
|
||||
>
|
||||
<Card.Header w="100%">
|
||||
|
||||
<XStack ai="center" py="$1">
|
||||
<YStack width={250}>
|
||||
<H3 color={colorOfTheText} selectable={false} >{title}</H3>
|
||||
<Text mt="$1" theme="alt2" color={colorOfTheText} selectable={false}>{description}</Text>
|
||||
</YStack>
|
||||
<XStack flex={1} />
|
||||
<ChevronRight size="$4" color={selected ? "#3185FC" : colorOfTheText} minWidth="$4" />
|
||||
</XStack>
|
||||
</Card.Header>
|
||||
{(
|
||||
<Card.Background
|
||||
>
|
||||
{background &&
|
||||
<Image
|
||||
flex={1}
|
||||
source={{
|
||||
uri: background
|
||||
}}
|
||||
/>}
|
||||
</Card.Background>
|
||||
)}
|
||||
</XStack>
|
||||
</Card >
|
||||
</XStack>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
import { styled, Text,View } from '@tamagui/core';
|
||||
|
||||
// Correctly capitalized component name
|
||||
const CircleContainer = styled(View, {
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
display: 'flex',
|
||||
borderRadius: 2,
|
||||
borderWidth: 2,
|
||||
borderColor: '#000',
|
||||
width: 30,
|
||||
height: 30,});
|
||||
|
||||
// Usage
|
||||
const CircleText = () => (
|
||||
<CircleContainer>
|
||||
<Text>1</Text>
|
||||
</CircleContainer>
|
||||
);
|
||||
|
||||
export default CircleText;
|
||||
74
app/src/components/ProofGrid.tsx
Normal file
74
app/src/components/ProofGrid.tsx
Normal file
@@ -0,0 +1,74 @@
|
||||
import React, { useMemo } from 'react';
|
||||
import { Svg, Rect } from 'react-native-svg';
|
||||
import { YStack } from 'tamagui';
|
||||
|
||||
interface ProofGridProps {
|
||||
proof: { proof: string; inputs: string } | null;
|
||||
}
|
||||
|
||||
const ProofGrid: React.FC<ProofGridProps> = ({ proof }) => {
|
||||
const gridSize = 8;
|
||||
const pixelSize = 15;
|
||||
|
||||
const sumAndScaleDigits = useMemo(() => (values: string[]) => {
|
||||
const sum = values.reduce((acc, val) => acc + BigInt(val), BigInt(0));
|
||||
const digits = sum.toString().split('').map(Number);
|
||||
return digits.map(digit => Math.round(digit * (256 / 9)));
|
||||
}, []);
|
||||
|
||||
// Prepare the RGB values
|
||||
const { rValues, gValues, bValues } = useMemo(() => {
|
||||
if (!proof) {
|
||||
return { rValues: [], gValues: [], bValues: [] };
|
||||
}
|
||||
|
||||
const parsedProof = JSON.parse(proof.proof);
|
||||
return {
|
||||
rValues: sumAndScaleDigits(parsedProof.a),
|
||||
gValues: sumAndScaleDigits(parsedProof.b.flat()),
|
||||
bValues: sumAndScaleDigits(parsedProof.c)
|
||||
};
|
||||
}, [proof, sumAndScaleDigits]);
|
||||
|
||||
// Generate the grid data
|
||||
const gridData = useMemo(() =>
|
||||
Array.from({ length: gridSize }, (_, rowIndex) =>
|
||||
Array.from({ length: gridSize }, (_, colIndex) => {
|
||||
const index = rowIndex * gridSize + colIndex;
|
||||
const r = index < rValues.length ? rValues[index] : 0;
|
||||
const g = index < gValues.length ? gValues[index] : 0;
|
||||
const b = index < bValues.length ? bValues[index] : 0;
|
||||
return `rgb(${r}, ${g}, ${b})`;
|
||||
})
|
||||
)
|
||||
, [gridSize, rValues, gValues, bValues]);
|
||||
|
||||
// Render the grid using SVG and Rect
|
||||
return (
|
||||
<YStack
|
||||
width={gridSize * pixelSize}
|
||||
borderRadius={40}
|
||||
overflow="hidden"
|
||||
elevation="$4"
|
||||
style={{ backdropFilter: 'blur(10px)' }}
|
||||
>
|
||||
<Svg height={gridSize * pixelSize} width={gridSize * pixelSize} >
|
||||
{gridData.map((row, i) =>
|
||||
row.map((fill, j) => (
|
||||
<Rect
|
||||
key={`${i}-${j}`}
|
||||
x={j * pixelSize}
|
||||
y={i * pixelSize}
|
||||
width={pixelSize}
|
||||
height={pixelSize}
|
||||
fill={fill}
|
||||
/>
|
||||
))
|
||||
)}
|
||||
</Svg>
|
||||
</YStack>
|
||||
|
||||
);
|
||||
};
|
||||
|
||||
export default ProofGrid;
|
||||
BIN
app/src/images/nfc.png
Normal file
BIN
app/src/images/nfc.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 131 KiB |
@@ -3,18 +3,32 @@ import ZUPASS from '../images/zupass.png';
|
||||
import GITCOIN from '../images/gitcoin.png';
|
||||
import { YStack } from 'tamagui';
|
||||
import AppCard from '../components/AppCard';
|
||||
import { App, gitcoin, soulbond, zuzalu } from '../utils/AppClass';
|
||||
const AppScreen = ({ selectedApp, setSelectedApp }) => {
|
||||
import { App, gitcoin, soulbound, zuzalu } from '../utils/AppClass';
|
||||
import { Steps } from '../utils/utils';
|
||||
|
||||
interface AppScreenProps {
|
||||
selectedApp: App | null;
|
||||
setSelectedApp: (app: App | null) => void;
|
||||
step: number;
|
||||
setStep: (step: number) => void;
|
||||
}
|
||||
|
||||
const AppScreen: React.FC<AppScreenProps> = ({ selectedApp, setSelectedApp, step, setStep }) => {
|
||||
|
||||
const handleCardSelect = (app: App) => {
|
||||
setSelectedApp(app);
|
||||
if (selectedApp != app) {
|
||||
setSelectedApp(app);
|
||||
if (step >= Steps.NFC_SCAN_COMPLETED) {
|
||||
setStep(Steps.NFC_SCAN_COMPLETED);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const cardsData = [
|
||||
{
|
||||
app: zuzalu,
|
||||
title: 'Add to Zupasss',
|
||||
description: 'And prove your identity at in person',
|
||||
description: 'Prove your identity at in person events',
|
||||
background: ZUPASS,
|
||||
colorOfTheText: 'white',
|
||||
},
|
||||
@@ -26,15 +40,15 @@ const AppScreen = ({ selectedApp, setSelectedApp }) => {
|
||||
colorOfTheText: 'white',
|
||||
},
|
||||
{
|
||||
app: soulbond,
|
||||
app: soulbound,
|
||||
title: 'Mint SBT',
|
||||
description: 'And prove your identity at in person events',
|
||||
description: 'And prove you\'re a human',
|
||||
colorOfTheText: 'black',
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<YStack gap="$5" w="100%" p="$5">
|
||||
<YStack flex={1} gap="$5" px="$5" jc="center" alignItems='center' >
|
||||
|
||||
{cardsData.map(card => (
|
||||
<AppCard
|
||||
@@ -45,7 +59,7 @@ const AppScreen = ({ selectedApp, setSelectedApp }) => {
|
||||
background={card.background}
|
||||
id={card.app.id}
|
||||
onTouchStart={() => handleCardSelect(card.app)}
|
||||
eleva={selectedApp && selectedApp.id === card.app.id ? "$0" : "$12"}
|
||||
selected={selectedApp && selectedApp.id === card.app.id ? true : false}
|
||||
/>
|
||||
))}
|
||||
|
||||
|
||||
@@ -1,40 +1,70 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { YStack, XStack, Text, Button, SizableText, Tabs, styled, Dialog, Adapt, Sheet, Label, Fieldset, Input, Switch, ThemeableStack } from 'tamagui'
|
||||
import { Scan, UserCheck, HelpCircle, XCircle, IterationCw, LayoutGrid, Sparkles } from '@tamagui/lucide-icons';
|
||||
import { YStack, XStack, Text, Button, Tabs, styled, Dialog, Adapt, Sheet, Label, Fieldset, Input, Switch, ThemeableStack, Separator, H3, H2, Image } from 'tamagui'
|
||||
import { Scan, UserCheck, HelpCircle, IterationCw, LayoutGrid, VenetianMask, Cog, CheckCircle2 } from '@tamagui/lucide-icons';
|
||||
import ScanScreen from './ScanScreen';
|
||||
import ProveScreen from './ProveScreen';
|
||||
import { Steps } from '../utils/utils';
|
||||
import AppScreen from './AppScreen';
|
||||
import { App } from '../utils/AppClass';
|
||||
const MainScreen = (
|
||||
{ onStartCameraScan,
|
||||
nfcScan,
|
||||
passportData,
|
||||
disclosure,
|
||||
handleDisclosureChange,
|
||||
address,
|
||||
setAddress,
|
||||
generatingProof,
|
||||
handleProve,
|
||||
step,
|
||||
mintText,
|
||||
proof,
|
||||
proofTime,
|
||||
handleMint,
|
||||
totalTime,
|
||||
setStep,
|
||||
passportNumber,
|
||||
setPassportNumber,
|
||||
dateOfBirth,
|
||||
setDateOfBirth,
|
||||
dateOfExpiry,
|
||||
setDateOfExpiry
|
||||
}
|
||||
) => {
|
||||
import { Platform } from 'react-native';
|
||||
import { Keyboard } from 'react-native';
|
||||
import NFC_IMAGE from '../images/nfc.png'
|
||||
|
||||
|
||||
interface MainScreenProps {
|
||||
onStartCameraScan: () => void;
|
||||
nfcScan: () => void;
|
||||
passportData: any;
|
||||
disclosure: { [key: string]: boolean };
|
||||
handleDisclosureChange: (field: string) => void;
|
||||
address: string;
|
||||
setAddress: (address: string) => void;
|
||||
generatingProof: boolean;
|
||||
handleProve: () => void;
|
||||
step: number;
|
||||
mintText: string;
|
||||
proof: any;
|
||||
proofTime: number;
|
||||
handleMint: () => void;
|
||||
setStep: (step: number) => void;
|
||||
passportNumber: string;
|
||||
setPassportNumber: (number: string) => void;
|
||||
dateOfBirth: string;
|
||||
setDateOfBirth: (date: string) => void;
|
||||
dateOfExpiry: string;
|
||||
setDateOfExpiry: (date: string) => void;
|
||||
}
|
||||
|
||||
const MainScreen: React.FC<MainScreenProps> = ({
|
||||
onStartCameraScan,
|
||||
nfcScan,
|
||||
passportData,
|
||||
disclosure,
|
||||
handleDisclosureChange,
|
||||
address,
|
||||
setAddress,
|
||||
generatingProof,
|
||||
handleProve,
|
||||
step,
|
||||
mintText,
|
||||
proof,
|
||||
proofTime,
|
||||
handleMint,
|
||||
setStep,
|
||||
passportNumber,
|
||||
setPassportNumber,
|
||||
dateOfBirth,
|
||||
setDateOfBirth,
|
||||
dateOfExpiry,
|
||||
setDateOfExpiry
|
||||
}) => {
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const [ens, setEns] = useState<string>('');
|
||||
const [selectedTab, setSelectedTab] = useState("scan");
|
||||
const [selectedApp, setSelectedApp] = useState<App | null>(null);
|
||||
const [brokenCamera, setBrokenCamera] = useState(false);
|
||||
const [open, setOpen] = useState(false)
|
||||
const [hideData, setHideData] = useState(false);
|
||||
const [open, setOpen] = useState(true)
|
||||
const AppCard = styled(ThemeableStack, {
|
||||
hoverTheme: true,
|
||||
pressTheme: true,
|
||||
@@ -56,31 +86,206 @@ const MainScreen = (
|
||||
setDateOfExpiry("");
|
||||
|
||||
}
|
||||
const handleHideData = () => {
|
||||
setHideData(!hideData);
|
||||
}
|
||||
const handleNFCScan = () => {
|
||||
if ((Platform.OS === 'ios')) {
|
||||
console.log('ios');
|
||||
nfcScan();
|
||||
}
|
||||
else {
|
||||
console.log('android :)');
|
||||
setIsOpen(true);
|
||||
nfcScan();
|
||||
}
|
||||
}
|
||||
useEffect(() => {
|
||||
// Check if length of each field is correct and move to step MRZ_SCAN_COMPLETED if so
|
||||
if (passportNumber?.length === 9 && (dateOfBirth?.length === 6 && dateOfExpiry?.length === 6)) {
|
||||
setStep(Steps.MRZ_SCAN_COMPLETED);
|
||||
}
|
||||
}, [passportNumber, dateOfBirth, dateOfExpiry]);
|
||||
|
||||
return (
|
||||
<YStack f={1} ai="center" jc="space-between" bc="#fff">
|
||||
useEffect(() => {
|
||||
let timeoutId: ReturnType<typeof setTimeout>;
|
||||
if (step >= Steps.NFC_SCAN_COMPLETED) {
|
||||
// Set the timeout and store its ID
|
||||
timeoutId = setTimeout(() => {
|
||||
setIsOpen(false);
|
||||
}, 700);
|
||||
}
|
||||
return () => {
|
||||
if (timeoutId) {
|
||||
clearTimeout(timeoutId);
|
||||
}
|
||||
};
|
||||
}, [step]);
|
||||
|
||||
<YStack w="100%">
|
||||
<XStack w="100%" jc="space-between" ai="center" ph="$4" pv="$2" bc="#fff" p="$3">
|
||||
<XStack></XStack>
|
||||
<Text>
|
||||
{selectedTab === "scan" ? "Scan" : (selectedTab === "app" ? "App" : "Prove")}
|
||||
// Keyboard management
|
||||
|
||||
const [keyboardVisible, setKeyboardVisible] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
const showSubscription = Keyboard.addListener('keyboardDidShow', () => {
|
||||
setKeyboardVisible(true);
|
||||
});
|
||||
const hideSubscription = Keyboard.addListener('keyboardDidHide', () => {
|
||||
setKeyboardVisible(false);
|
||||
});
|
||||
|
||||
return () => {
|
||||
showSubscription.remove();
|
||||
hideSubscription.remove();
|
||||
};
|
||||
}, []);
|
||||
|
||||
|
||||
return (
|
||||
<YStack f={1} bc="white" mt={Platform.OS === 'ios' ? "$8" : "$0"} mb={Platform.OS === 'ios' ? "$3" : "$0"}>
|
||||
|
||||
<YStack >
|
||||
<XStack jc="space-between" ai="center" p="$2" px="$3">
|
||||
<Dialog
|
||||
modal
|
||||
>
|
||||
<Dialog.Trigger >
|
||||
<YStack p="$2" pr="$7">
|
||||
<Cog />
|
||||
</YStack>
|
||||
</Dialog.Trigger>
|
||||
|
||||
<Adapt when="sm" platform="touch">
|
||||
<Sheet animation="medium" zIndex={200000} modal dismissOnSnapToBottom>
|
||||
<Sheet.Frame padding="$4" gap="$4">
|
||||
<Adapt.Contents />
|
||||
</Sheet.Frame>
|
||||
<Sheet.Overlay
|
||||
animation="lazy"
|
||||
enterStyle={{ opacity: 0 }}
|
||||
exitStyle={{ opacity: 0 }}
|
||||
/>
|
||||
</Sheet>
|
||||
</Adapt>
|
||||
|
||||
<Dialog.Portal>
|
||||
<Dialog.Overlay
|
||||
key="overlay"
|
||||
animation="quick"
|
||||
opacity={0.5}
|
||||
enterStyle={{ opacity: 0 }}
|
||||
exitStyle={{ opacity: 0 }}
|
||||
/>
|
||||
|
||||
<Dialog.Content
|
||||
bordered
|
||||
elevate
|
||||
key="content"
|
||||
animateOnly={['transform', 'opacity']}
|
||||
animation={[
|
||||
'quick',
|
||||
{
|
||||
opacity: {
|
||||
overshootClamping: true,
|
||||
},
|
||||
},
|
||||
]}
|
||||
enterStyle={{ x: 0, y: -20, opacity: 0, scale: 0.9 }}
|
||||
exitStyle={{ x: 0, y: 10, opacity: 0, scale: 0.95 }}
|
||||
|
||||
>
|
||||
<YStack f={1} gap="$2">
|
||||
<XStack gap="$2" >
|
||||
<Dialog.Title>Settings</Dialog.Title>
|
||||
<Cog mt="$1" alignSelf='center' size="$2" />
|
||||
|
||||
</XStack>
|
||||
|
||||
|
||||
|
||||
<Fieldset mt="$2" horizontal>
|
||||
<Label width={225} justifyContent="flex-end" htmlFor="restart" fow="bold">
|
||||
Private mode
|
||||
</Label>
|
||||
<Switch size="$4" checked={hideData} onCheckedChange={handleHideData}>
|
||||
<Switch.Thumb animation="bouncy" backgroundColor="white" />
|
||||
</Switch>
|
||||
</Fieldset>
|
||||
|
||||
<Fieldset mt="$1" horizontal>
|
||||
<Label width={225} justifyContent="flex-end" htmlFor="name" fow="bold">
|
||||
Broken camera
|
||||
</Label>
|
||||
<Switch size="$4" checked={brokenCamera} onCheckedChange={setBrokenCamera}>
|
||||
<Switch.Thumb animation="bouncy" backgroundColor="white" />
|
||||
</Switch>
|
||||
</Fieldset>
|
||||
{
|
||||
brokenCamera &&
|
||||
<YStack pl="$3" gap="$3" mt="$4">
|
||||
<Fieldset gap="$4" horizontal>
|
||||
<Label width={160} justifyContent="flex-end" fontSize={13}>
|
||||
Passport Number
|
||||
</Label>
|
||||
<Input borderColor={passportNumber?.length === 9 ? "green" : "unset"} flex={1} id="passport_number" onChangeText={(text) => setPassportNumber(text.toUpperCase())} value={passportNumber} keyboardType="default" />
|
||||
</Fieldset>
|
||||
<Fieldset gap="$4" horizontal>
|
||||
<Label width={160} justifyContent="flex-end" fontSize={13}>
|
||||
Date of birth (yymmdd)
|
||||
</Label>
|
||||
<Input borderColor={dateOfBirth?.length === 6 ? "green" : "unset"} flex={1} id="date_of_birth" onChangeText={setDateOfBirth} value={dateOfBirth} keyboardType="numeric" />
|
||||
</Fieldset>
|
||||
<Fieldset gap="$4" horizontal>
|
||||
<Label width={160} justifyContent="flex-end" fontSize={13}>
|
||||
Date of expiry (yymmdd)
|
||||
</Label>
|
||||
<Input borderColor={dateOfExpiry?.length === 6 ? "green" : "unset"} flex={1} id="date_of_expiry" onChangeText={setDateOfExpiry} value={dateOfExpiry} keyboardType="numeric" />
|
||||
</Fieldset>
|
||||
</YStack>
|
||||
}
|
||||
<Fieldset gap="$4" mt="$3" horizontal>
|
||||
<Label width={200} justifyContent="flex-end" htmlFor="restart" fow="bold">
|
||||
Restart to step 1
|
||||
</Label>
|
||||
<Button size="$4" m="$2" onPress={handleRestart}>
|
||||
<IterationCw />
|
||||
</Button>
|
||||
</Fieldset>
|
||||
|
||||
<Fieldset gap="$4" mt="$2" horizontal>
|
||||
<Label width={200} justifyContent="flex-end" htmlFor="skip" fow="bold">
|
||||
Use mock passport data
|
||||
</Label>
|
||||
<Button size="$4" m="$2" onPress={handleSkip}>
|
||||
<VenetianMask />
|
||||
</Button>
|
||||
</Fieldset>
|
||||
<YStack flex={1}>
|
||||
<YStack flex={1} />
|
||||
<Dialog.Close mb="$6" displayWhenAdapted alignSelf='center' asChild >
|
||||
<Button>
|
||||
<Text w="80%" textAlign='center' fow="bold">Close</Text>
|
||||
</Button>
|
||||
</Dialog.Close>
|
||||
</YStack>
|
||||
</YStack>
|
||||
|
||||
</Dialog.Content>
|
||||
</Dialog.Portal>
|
||||
</Dialog>
|
||||
|
||||
|
||||
|
||||
<Text fow="bold">
|
||||
{selectedTab === "scan" ? "Scan" : (selectedTab === "app" ? "Apps" : "Prove")}
|
||||
</Text>
|
||||
|
||||
<Dialog
|
||||
modal
|
||||
onOpenChange={(open) => {
|
||||
setOpen(open)
|
||||
}}
|
||||
>
|
||||
<Dialog.Trigger asChild p="$2">
|
||||
<HelpCircle />
|
||||
<Dialog.Trigger>
|
||||
<YStack p="$2" pl="$8">
|
||||
<HelpCircle />
|
||||
</YStack>
|
||||
</Dialog.Trigger>
|
||||
|
||||
<Adapt when="sm" platform="touch">
|
||||
@@ -122,160 +327,167 @@ const MainScreen = (
|
||||
exitStyle={{ x: 0, y: 10, opacity: 0, scale: 0.95 }}
|
||||
gap="$4"
|
||||
>
|
||||
<XStack space >
|
||||
<Dialog.Title>Settings</Dialog.Title>
|
||||
|
||||
</XStack>
|
||||
|
||||
<Fieldset gap="$4" mt="$2" horizontal>
|
||||
<Label width={160} justifyContent="flex-end" htmlFor="name" fow="bold">
|
||||
Restart to step 1
|
||||
</Label>
|
||||
<Button size="$4" m="$2" onPress={handleRestart}>
|
||||
<IterationCw />
|
||||
</Button>
|
||||
</Fieldset>
|
||||
<Fieldset gap="$4" mt="$2" horizontal>
|
||||
<Label width={160} justifyContent="flex-end" htmlFor="name" fow="bold">
|
||||
Skip to App selection
|
||||
</Label>
|
||||
<Button size="$4" m="$2" onPress={handleSkip}>
|
||||
<Sparkles />
|
||||
</Button>
|
||||
</Fieldset>
|
||||
|
||||
<Fieldset gap="$4" mt="$2" horizontal>
|
||||
<Label width={160} justifyContent="flex-end" htmlFor="name" fow="bold">
|
||||
Broken camera
|
||||
</Label>
|
||||
<Switch size="$4" checked={brokenCamera} onCheckedChange={setBrokenCamera}>
|
||||
<Switch.Thumb animation="bouncy" backgroundColor="white" color />
|
||||
</Switch>
|
||||
</Fieldset>
|
||||
{
|
||||
brokenCamera &&
|
||||
<YStack space pl="$3">
|
||||
<Fieldset gap="$4" horizontal>
|
||||
<Label width={160} justifyContent="flex-end" htmlFor="name">
|
||||
Passport Number
|
||||
</Label>
|
||||
<Input borderColor={passportNumber?.length === 9 ? "green" : "unset"} flex={1} id="passport_number" onChangeText={(text) => setPassportNumber(text.toUpperCase())} value={passportNumber} keyboardType="default" />
|
||||
</Fieldset>
|
||||
<Fieldset gap="$4" mt="$2" horizontal>
|
||||
<Label width={160} justifyContent="flex-end" htmlFor="name">
|
||||
Date of birth (yymmdd)
|
||||
</Label>
|
||||
<Input borderColor={dateOfBirth?.length === 6 ? "green" : "unset"} flex={1} id="date_of_birth" onChangeText={setDateOfBirth} value={dateOfBirth} keyboardType="numeric" />
|
||||
</Fieldset>
|
||||
<Fieldset gap="$4" mt="$2" horizontal>
|
||||
<Label width={160} justifyContent="flex-end" htmlFor="name">
|
||||
Date of expiry (yymmdd)
|
||||
</Label>
|
||||
<Input borderColor={dateOfExpiry?.length === 6 ? "green" : "unset"} flex={1} id="date_of_expiry" onChangeText={setDateOfExpiry} value={dateOfExpiry} keyboardType="numeric" />
|
||||
</Fieldset>
|
||||
</YStack>
|
||||
}
|
||||
<YStack flex={1}>
|
||||
<XStack gap="$2">
|
||||
<Dialog.Title>Help</Dialog.Title>
|
||||
<HelpCircle mt="$1" alignSelf='center' size="$2" />
|
||||
</XStack>
|
||||
<H3 fontFamily="Luciole" mt="$3">How to scan your passport ?</H3>
|
||||
<YStack>
|
||||
<Text>1. Find the location of the NFC chip of your passport.</Text>
|
||||
<Text>If you are struggling <Text color="#3185FC">this post</Text> will help you to find it.</Text>
|
||||
<Text mt="$2">2. Find where is the NFC lector of your phone.</Text>
|
||||
<Text mt="$2">3. Keep both part pressed together when this app ask for it.</Text>
|
||||
</YStack>
|
||||
<H3 mt="$3">Security and User data Privacy</H3>
|
||||
<YStack gap="$2">
|
||||
<Text>This app gerates ZK proofs to ...</Text>
|
||||
</YStack>
|
||||
<H3 mt="$3">What are ZK proofs ?</H3>
|
||||
<YStack gap="$2">
|
||||
<Text>Zero Knowledge proofs are .....</Text>
|
||||
</YStack>
|
||||
|
||||
<H3 mt="$3">Contacts</H3>
|
||||
<YStack gap="$2">
|
||||
<Text>telegram</Text>
|
||||
</YStack>
|
||||
|
||||
<H3 mt="$3">Credits</H3>
|
||||
<YStack >
|
||||
<Text>Ethereum Foundation</Text>
|
||||
<Text>turboblitz.eth</Text>
|
||||
<Text>???.eth</Text>
|
||||
</YStack>
|
||||
|
||||
<YStack flex={1}></YStack>
|
||||
<Dialog.Close mb="$4" displayWhenAdapted asChild alignSelf='center'>
|
||||
<XCircle size="$3" />
|
||||
<Dialog.Close displayWhenAdapted alignSelf='center' asChild >
|
||||
<Button>
|
||||
<Text w="80%" textAlign='center' fow="bold">Close</Text>
|
||||
</Button>
|
||||
</Dialog.Close>
|
||||
<YStack flex={1}></YStack>
|
||||
|
||||
|
||||
</YStack>
|
||||
|
||||
</Dialog.Content>
|
||||
</Dialog.Portal>
|
||||
</Dialog>
|
||||
|
||||
</XStack>
|
||||
<YStack w="100%" h={2} backgroundColor="#DCDCDC" opacity={0.16}></YStack>
|
||||
<Sheet open={isOpen} onOpenChange={setIsOpen} modal dismissOnOverlayPress={false} disableDrag animation="medium" snapPoints={[40]}>
|
||||
<Sheet.Overlay />
|
||||
<Sheet.Handle />
|
||||
<Sheet.Frame>
|
||||
<YStack gap="$5" f={1} pt="$3">
|
||||
<H2 textAlign='center' color="gray">Ready to scan</H2>
|
||||
{step >= Steps.NFC_SCAN_COMPLETED ?
|
||||
<CheckCircle2
|
||||
size="$8"
|
||||
alignSelf='center'
|
||||
color="#3185FC"
|
||||
animation="quick"
|
||||
/> :
|
||||
<Image
|
||||
h="$8"
|
||||
w="$8"
|
||||
alignSelf='center'
|
||||
borderRadius={1000}
|
||||
source={{
|
||||
uri: NFC_IMAGE
|
||||
}}
|
||||
/>
|
||||
}
|
||||
<Text textAlign='center'>Hold your device near the NFC tag.</Text>
|
||||
<Button my="$2" w="$20" alignSelf='center' onPress={() => setIsOpen(false)}>
|
||||
<Text textAlign='center' fow="bold"> Cancel</Text>
|
||||
</Button>
|
||||
</YStack>
|
||||
</Sheet.Frame>
|
||||
</Sheet>
|
||||
<Separator />
|
||||
</YStack>
|
||||
<Tabs f={1} orientation="horizontal" flexDirection="column" defaultValue="scan" onValueChange={setSelectedTab}>
|
||||
<Tabs.Content value="scan" f={1}>
|
||||
<ScanScreen
|
||||
onStartCameraScan={onStartCameraScan}
|
||||
handleNFCScan={handleNFCScan}
|
||||
step={step} />
|
||||
</Tabs.Content>
|
||||
|
||||
<Tabs.Content value="app" f={1}>
|
||||
<AppScreen
|
||||
selectedApp={selectedApp}
|
||||
setSelectedApp={setSelectedApp}
|
||||
step={step}
|
||||
setStep={setStep}
|
||||
/>
|
||||
</Tabs.Content>
|
||||
|
||||
<Tabs f={1} defaultValue="scan" orientation='horizontal' dir='ltr' shadowColor="black" onValueChange={(newValue) => setSelectedTab(newValue)}>
|
||||
<YStack ai="center" jc="space-between" bc="" >
|
||||
<XStack flexGrow={0} ai="center" />
|
||||
|
||||
<Tabs.Content value="scan">
|
||||
<ScanScreen
|
||||
onStartCameraScan={onStartCameraScan}
|
||||
nfcScan={nfcScan}
|
||||
step={step} />
|
||||
</Tabs.Content>
|
||||
<Tabs.Content value="app">
|
||||
<AppScreen
|
||||
selectedApp={selectedApp}
|
||||
setSelectedApp={setSelectedApp}
|
||||
/>
|
||||
</Tabs.Content>
|
||||
<Tabs.Content value="generate">
|
||||
<ProveScreen
|
||||
passportData={passportData}
|
||||
disclosure={disclosure}
|
||||
selectedApp={selectedApp}
|
||||
handleDisclosureChange={handleDisclosureChange}
|
||||
address={address}
|
||||
setAddress={setAddress}
|
||||
generatingProof={generatingProof}
|
||||
handleProve={handleProve}
|
||||
step={step}
|
||||
mintText={mintText}
|
||||
proof={proof}
|
||||
proofTime={proofTime}
|
||||
handleMint={handleMint}
|
||||
totalTime={totalTime}
|
||||
setStep={setStep}
|
||||
/>
|
||||
</Tabs.Content>
|
||||
|
||||
<YStack w="100%" backgroundColor="white">
|
||||
<YStack w="100%" h={2} backgroundColor="#DCDCDC" opacity={0.16}></YStack>
|
||||
|
||||
<Tabs.List w="100%" pt="$4" pb="$3">
|
||||
<Tabs.Tab unstyled value="scan" w="33%" backgroundColor="transparent" >
|
||||
<Tabs.Content value="generate" f={1}>
|
||||
<ProveScreen
|
||||
passportData={passportData}
|
||||
disclosure={disclosure}
|
||||
selectedApp={selectedApp}
|
||||
handleDisclosureChange={handleDisclosureChange}
|
||||
address={address}
|
||||
setAddress={setAddress}
|
||||
generatingProof={generatingProof}
|
||||
handleProve={handleProve}
|
||||
step={step}
|
||||
mintText={mintText}
|
||||
proof={proof}
|
||||
proofTime={proofTime}
|
||||
handleMint={handleMint}
|
||||
hideData={hideData}
|
||||
ens={ens}
|
||||
setEns={setEns} />
|
||||
</Tabs.Content>
|
||||
<Separator />
|
||||
{(!keyboardVisible || Platform.OS == "ios") &&
|
||||
<Tabs.List separator={<Separator vertical />} pt="$4" pb="$3">
|
||||
<Tabs.Tab value="scan" unstyled w="33%">
|
||||
<YStack ai="center">
|
||||
<Scan color={selectedTab === "scan" ? '#3185FC' : 'black'} />
|
||||
<Text color={selectedTab === "scan" ? '#3185FC' : 'black'}>Scan</Text>
|
||||
</YStack>
|
||||
</Tabs.Tab>
|
||||
{step >= Steps.NFC_SCAN_COMPLETED ?
|
||||
<Tabs.Tab value="app" unstyled w="33%">
|
||||
<YStack ai="center">
|
||||
<Scan color={selectedTab === "scan" ? '#3185FC' : 'black'} />
|
||||
<SizableText color={selectedTab === "scan" ? '#3185FC' : 'black'}>Scan</SizableText>
|
||||
<LayoutGrid color={selectedTab === "app" ? '#3185FC' : 'black'} />
|
||||
<Text color={selectedTab === "app" ? '#3185FC' : 'black'}>Apps</Text>
|
||||
</YStack>
|
||||
</Tabs.Tab>
|
||||
:
|
||||
<Tabs.Tab value="scan" unstyled w="33%">
|
||||
<YStack ai="center">
|
||||
<LayoutGrid color="#bcbcbc" />
|
||||
<Text color="#bcbcbc">Apps</Text>
|
||||
</YStack>
|
||||
</Tabs.Tab>
|
||||
|
||||
{step < Steps.NFC_SCAN_COMPLETED ?
|
||||
<Tabs.Tab unstyled value="scan" w="33%" backgroundColor="transparent" >
|
||||
<YStack ai="center">
|
||||
<LayoutGrid color="#eeeeee" />
|
||||
<SizableText color="#eeeeee">App</SizableText>
|
||||
</YStack>
|
||||
</Tabs.Tab>
|
||||
:
|
||||
<Tabs.Tab unstyled value="app" w="33%" backgroundColor="transparent" >
|
||||
<YStack ai="center">
|
||||
<LayoutGrid color={selectedTab === "app" ? '#3185FC' : 'black'} />
|
||||
<SizableText color={selectedTab === "app" ? '#3185FC' : 'black'}>App</SizableText>
|
||||
</YStack>
|
||||
</Tabs.Tab>
|
||||
}
|
||||
|
||||
|
||||
|
||||
{selectedApp === null ?
|
||||
<Tabs.Tab unstyled value={step < Steps.NFC_SCAN_COMPLETED ? "scan" : "app"} w="33%" backgroundColor="transparent">
|
||||
<YStack ai="center">
|
||||
<UserCheck color="#eeeeee" />
|
||||
<SizableText color="#eeeeee">Prove</SizableText>
|
||||
</YStack>
|
||||
</Tabs.Tab>
|
||||
:
|
||||
<Tabs.Tab unstyled value="generate" w="33%" backgroundColor="transparent">
|
||||
}
|
||||
{
|
||||
(step >= Steps.NFC_SCAN_COMPLETED) && (selectedApp != null) ?
|
||||
<Tabs.Tab value="generate" unstyled w="33%">
|
||||
<YStack ai="center">
|
||||
<UserCheck color={selectedTab === "generate" ? '#3185FC' : 'black'} />
|
||||
<SizableText color={selectedTab === "generate" ? '#3185FC' : 'black'}>Prove</SizableText>
|
||||
<Text color={selectedTab === "generate" ? '#3185FC' : 'black'}>Prove</Text>
|
||||
</YStack>
|
||||
</Tabs.Tab>
|
||||
}
|
||||
</Tabs.List>
|
||||
</YStack>
|
||||
</YStack>
|
||||
</Tabs >
|
||||
:
|
||||
<Tabs.Tab value={step >= Steps.NFC_SCAN_COMPLETED ? "app" : "scan"} unstyled w="33%">
|
||||
<YStack ai="center">
|
||||
<UserCheck color="#bcbcbc" />
|
||||
<Text color="#bcbcbc">Prove</Text>
|
||||
</YStack>
|
||||
</Tabs.Tab>
|
||||
}
|
||||
</Tabs.List>
|
||||
|
||||
}
|
||||
</Tabs>
|
||||
</YStack >
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,16 +1,39 @@
|
||||
import React, { useState } from 'react';
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { NativeModules } from 'react-native';
|
||||
import { YStack, XStack, Text, Checkbox, Input, Button, Spinner, SizableText, Image } from 'tamagui';
|
||||
import { Check } from '@tamagui/lucide-icons';
|
||||
import { YStack, XStack, Text, Checkbox, Input, Button, Spinner, Image } from 'tamagui';
|
||||
import { Check, LayoutGrid, Scan } from '@tamagui/lucide-icons';
|
||||
import { getFirstName, formatDuration } from '../../utils/utils';
|
||||
import { attributeToPosition } from '../../../common/src/constants/constants';
|
||||
import { Steps } from '../utils/utils';
|
||||
import USER from '../images/user.png'
|
||||
import ProofGrid from '../components/ProofGrid';
|
||||
import { App } from '../utils/AppClass';
|
||||
import { Keyboard, Platform } from 'react-native';
|
||||
const { ethers } = require('ethers');
|
||||
|
||||
const fileName = "passport.arkzkey"
|
||||
const path = "/data/user/0/com.proofofpassport/files/" + fileName
|
||||
|
||||
const ProveScreen = ({
|
||||
interface ProveScreenProps {
|
||||
selectedApp: App | null;
|
||||
passportData: any;
|
||||
disclosure: { [key: string]: boolean };
|
||||
handleDisclosureChange: (field: string) => void;
|
||||
address: string;
|
||||
setAddress: (address: string) => void;
|
||||
generatingProof: boolean;
|
||||
handleProve: () => void;
|
||||
handleMint: () => void;
|
||||
step: number;
|
||||
mintText: string;
|
||||
proof: { proof: string, inputs: string } | null;
|
||||
proofTime: number;
|
||||
hideData: boolean;
|
||||
ens: string;
|
||||
setEns: (ens: string) => void;
|
||||
}
|
||||
|
||||
const ProveScreen: React.FC<ProveScreenProps> = ({
|
||||
passportData,
|
||||
disclosure,
|
||||
selectedApp,
|
||||
@@ -20,12 +43,13 @@ const ProveScreen = ({
|
||||
generatingProof,
|
||||
handleProve,
|
||||
step,
|
||||
setStep,
|
||||
mintText,
|
||||
proof,
|
||||
proofTime,
|
||||
handleMint,
|
||||
totalTime
|
||||
hideData,
|
||||
ens,
|
||||
setEns
|
||||
}) => {
|
||||
const [downloadingFile, setDownloadingFile] = useState(false);
|
||||
const [zkeyLoaded, setZkeyLoaded] = useState(false);
|
||||
@@ -51,61 +75,121 @@ const ProveScreen = ({
|
||||
}
|
||||
};
|
||||
|
||||
const maskString = (input: string): string => {
|
||||
if (input.length <= 5) {
|
||||
return input.charAt(0) + '*'.repeat(input.length - 1);
|
||||
} else {
|
||||
return input.charAt(0) + input.charAt(1) + '*'.repeat(input.length - 2);
|
||||
}
|
||||
}
|
||||
|
||||
const [inputValue, setInputValue] = useState('');
|
||||
const provider = new ethers.JsonRpcProvider(`https://eth-mainnet.g.alchemy.com/v2/lpOn3k6Fezetn1e5QF-iEsn-J0C6oGE0`);
|
||||
|
||||
useEffect(() => {
|
||||
if (ens != '' && inputValue == '') {
|
||||
setInputValue(ens);
|
||||
|
||||
}
|
||||
else if (address != ethers.ZeroAddress && inputValue == '') {
|
||||
setInputValue(address);
|
||||
}
|
||||
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
const resolveENS = async () => {
|
||||
if (inputValue != ens) {
|
||||
if (inputValue.endsWith('.eth')) {
|
||||
try {
|
||||
const resolvedAddress = await provider.resolveName(inputValue);
|
||||
if (resolvedAddress) {
|
||||
console.log("new address settled:" + resolvedAddress);
|
||||
setAddress(resolvedAddress);
|
||||
setEns(inputValue);
|
||||
if (hideData) {
|
||||
console.log(maskString(address));
|
||||
// setInputValue(maskString(address));
|
||||
}
|
||||
else {
|
||||
// setInputValue(address);
|
||||
}
|
||||
} else {
|
||||
console.error("Could not resolve ENS name.");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error resolving ENS name:", error);
|
||||
}
|
||||
}
|
||||
else if (inputValue.length === 42 && inputValue.startsWith('0x')) {
|
||||
setAddress(inputValue);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
resolveENS();
|
||||
}, [inputValue]);
|
||||
|
||||
|
||||
|
||||
|
||||
// Keyboard management
|
||||
|
||||
const [keyboardVisible, setKeyboardVisible] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
const showSubscription = Keyboard.addListener('keyboardDidShow', () => {
|
||||
setKeyboardVisible(true);
|
||||
});
|
||||
const hideSubscription = Keyboard.addListener('keyboardDidHide', () => {
|
||||
setKeyboardVisible(false);
|
||||
});
|
||||
|
||||
return () => {
|
||||
showSubscription.remove();
|
||||
hideSubscription.remove();
|
||||
};
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<YStack space="$4" p="$4" >
|
||||
{
|
||||
step < Steps.PROOF_GENERATED ? (
|
||||
<YStack >
|
||||
<YStack w="100%" ai="center">
|
||||
<Image
|
||||
w="$12"
|
||||
h="$12"
|
||||
flex={1}
|
||||
<YStack px="$4" f={1}>
|
||||
{(step >= Steps.NFC_SCAN_COMPLETED && selectedApp != null) ?
|
||||
(step < Steps.PROOF_GENERATED ? (
|
||||
<YStack flex={1} m="$2" gap="$2">
|
||||
<XStack flex={1} />
|
||||
<YStack alignSelf='center' mt="$1">
|
||||
{hideData ? <Image
|
||||
w="$13"
|
||||
h="$15"
|
||||
borderRadius="$10"
|
||||
source={{
|
||||
uri: USER
|
||||
uri: USER,
|
||||
}}
|
||||
/>
|
||||
/> :
|
||||
<Image
|
||||
w="$13"
|
||||
h="$15"
|
||||
borderRadius="$10"
|
||||
source={{
|
||||
uri: passportData.photoBase64 ?? USER,
|
||||
}}
|
||||
/>
|
||||
|
||||
}
|
||||
</YStack>
|
||||
<YStack mt="$12">
|
||||
<SizableText mb="$1">Hi {getFirstName(passportData.mrz)},</SizableText>
|
||||
<Text mb="$2">{selectedApp.name} is asking for the following information:</Text>
|
||||
{Object.keys(selectedApp.disclosure).map((key) => {
|
||||
const keyy = key as keyof typeof disclosure;
|
||||
const indexes = attributeToPosition[keyy];
|
||||
const keyFormatted = keyy.replace(/_/g, ' ').split(' ').map(word => word.charAt(0).toUpperCase() + word.slice(1)).join(' ');
|
||||
const mrzAttribute = passportData.mrz.slice(indexes[0], indexes[1]);
|
||||
const mrzAttributeFormatted = mrzAttribute.replace(/</g, ' ');
|
||||
<XStack f={1} />
|
||||
|
||||
return (
|
||||
<XStack key={key} m="$2" w="$full" gap="$2">
|
||||
|
||||
<Checkbox
|
||||
value={key}
|
||||
checked={disclosure[keyy]}
|
||||
onCheckedChange={() => handleDisclosureChange(keyy)}
|
||||
aria-label={keyFormatted}
|
||||
size="$5"
|
||||
>
|
||||
<Checkbox.Indicator >
|
||||
<Check />
|
||||
</Checkbox.Indicator>
|
||||
</Checkbox>
|
||||
|
||||
<Text fontWeight="bold">{keyFormatted}: </Text>
|
||||
<Text>{mrzAttributeFormatted}</Text>
|
||||
</XStack>
|
||||
);
|
||||
})}
|
||||
</YStack>
|
||||
<Text mt="$3">Enter your address or ens</Text>
|
||||
<Text mt="$8" fontWeight="bold">Hi {hideData ? maskString(getFirstName(passportData.mrz)) : getFirstName(passportData.mrz)},</Text>
|
||||
<Text mt="$2">Enter your address or ens:</Text>
|
||||
<Input
|
||||
size="md"
|
||||
fontSize={13}
|
||||
mt="$3"
|
||||
placeholder="Your Address or ens name"
|
||||
value={address}
|
||||
onChangeText={setAddress}
|
||||
value={inputValue}
|
||||
onChangeText={setInputValue}
|
||||
autoCorrect={false}
|
||||
autoCapitalize='none'
|
||||
borderColor={address != ethers.ZeroAddress ? "#3185FC" : "unset"}
|
||||
/>
|
||||
|
||||
{downloadingFile ? (
|
||||
@@ -125,34 +209,90 @@ const ProveScreen = ({
|
||||
</Button>
|
||||
)}
|
||||
|
||||
<Button borderRadius={100} onPress={() => {handleProve(path)}} mt="$6" backgroundColor="#3185FC" >
|
||||
{(!keyboardVisible || Platform.OS == "ios") && <YStack mt="$6" f={1}>
|
||||
<Text h="$3">{selectedApp?.disclosurephrase}</Text>
|
||||
<YStack mt="$1">
|
||||
{selectedApp && Object.keys(selectedApp.disclosure).map((key) => {
|
||||
const key_ = key as string;
|
||||
const indexes = attributeToPosition[key_];
|
||||
const keyFormatted = key_.replace(/_/g, ' ').split(' ').map((word: string) => word.charAt(0).toUpperCase() + word.slice(1)).join(' ');
|
||||
const mrzAttribute = passportData.mrz.slice(indexes[0], indexes[1] + 1);
|
||||
const mrzAttributeFormatted = mrzAttribute;
|
||||
|
||||
return (
|
||||
<XStack key={key} m="$2" gap="$4">
|
||||
<Checkbox
|
||||
value={key}
|
||||
checked={disclosure[key_]}
|
||||
onCheckedChange={() => handleDisclosureChange(key_)}
|
||||
aria-label={keyFormatted}
|
||||
size="$5"
|
||||
>
|
||||
<Checkbox.Indicator >
|
||||
<Check />
|
||||
</Checkbox.Indicator>
|
||||
</Checkbox>
|
||||
<Text fontWeight="bold">{keyFormatted}: </Text>
|
||||
<Text>{hideData ? maskString(mrzAttributeFormatted) : mrzAttributeFormatted}</Text>
|
||||
</XStack>
|
||||
);
|
||||
})}
|
||||
</YStack>
|
||||
</YStack>}
|
||||
<XStack f={1} />
|
||||
<XStack f={1} />
|
||||
<XStack f={1} />
|
||||
{(!keyboardVisible || Platform.OS == "ios") && <Button disabled={address == ethers.ZeroAddress} borderRadius={100} onPress={handleProve} mt="$8" backgroundColor={address == ethers.ZeroAddress ? "#cecece" : "#3185FC"} alignSelf='center' >
|
||||
{generatingProof ? (
|
||||
<XStack ai="center">
|
||||
<Spinner />
|
||||
<Text color="white" marginLeft="$2" fow="bold">Generating zk proof</Text>
|
||||
<Text color="white" marginLeft="$2" fow="bold" >Generating ZK proof</Text>
|
||||
</XStack>
|
||||
) : (
|
||||
<Text color="white" fow="bold">Generate zk proof</Text>
|
||||
<Text color="white" fow="bold">Generate ZK proof</Text>
|
||||
)}
|
||||
</Button>
|
||||
</Button>}
|
||||
<Text fontSize={10} color={generatingProof ? "gray" : "white"} alignSelf='center'>This operation can take up to 2 mn</Text>
|
||||
<Text fontSize={9} color={generatingProof ? "gray" : "white"} pb="$2" alignSelf='center'>The application may freeze during this time (hard work)</Text>
|
||||
</YStack>
|
||||
|
||||
) : (
|
||||
<YStack m="$2">
|
||||
<Text>Zero-knowledge proof generated</Text>
|
||||
<YStack flex={1} m="$2" justifyContent='center' alignItems='center' gap="$5">
|
||||
<XStack flex={1} />
|
||||
<ProofGrid proof={proof} />
|
||||
|
||||
<Text fontWeight="bold">Proof:</Text>
|
||||
<Text>{JSON.stringify(proof)}</Text>
|
||||
<YStack>
|
||||
<Text fontWeight="bold" fontSize="$6" mt="$6">Congrats 🎉</Text>
|
||||
<Text fontWeight="bold" fontSize="$5">You just generated this Zero Knowledge proof !</Text>
|
||||
<Text color="gray" fontSize="$5" mt="$1" fow="bold" textAlign='left'>You can now share this proof with the selected app.</Text>
|
||||
|
||||
<Text fontWeight="bold">Proof Duration: {formatDuration(proofTime)}</Text>
|
||||
<Text fontWeight="bold">Total Duration: {formatDuration(totalTime)}</Text>
|
||||
<Text color="gray" mt="$3">Proof generation duration: {formatDuration(proofTime)}</Text>
|
||||
|
||||
<Button borderRadius={100} onPress={handleMint} marginTop="$4" mb="$4" backgroundColor="#3185FC">
|
||||
<Text color="white" fow="bold">Mint Proof of Passport</Text>
|
||||
</YStack>
|
||||
|
||||
<XStack flex={1} />
|
||||
|
||||
|
||||
|
||||
{mintText && <Text color="gray">{mintText}</Text>}
|
||||
|
||||
<Button borderRadius={100} onPress={handleMint} marginTop="$4" mb="$8" backgroundColor="#3185FC">
|
||||
<Text color="white" fow="bold" >{selectedApp?.mintphrase}</Text>
|
||||
</Button>
|
||||
|
||||
{mintText && <Text>{mintText}</Text>}
|
||||
</YStack>
|
||||
)
|
||||
) :
|
||||
(
|
||||
<YStack flex={1} justifyContent='center' alignItems='center'>
|
||||
<Text fontSize={17} textAlign='center' fow="bold">Please scan your passport and select an app to generate ZK proof</Text>
|
||||
<XStack mt="$8" gap="$7">
|
||||
<Scan size="$4" color={step < Steps.NFC_SCAN_COMPLETED ? "black" : "#3185FC"} />
|
||||
<LayoutGrid size="$4" color={selectedApp == null ? "black" : "#3185FC"} />
|
||||
</XStack>
|
||||
</YStack>
|
||||
|
||||
)
|
||||
}
|
||||
</YStack >
|
||||
);
|
||||
|
||||
@@ -1,72 +1,45 @@
|
||||
import React from 'react';
|
||||
import { YStack, Text, Spinner, Circle, ZStack, XStack, SizableText } from 'tamagui'; // Ensure correct import paths based on your project setup
|
||||
import { YStack, Text, Spinner, XStack } from 'tamagui';
|
||||
import { Steps } from '../utils/utils';
|
||||
const ScanScreen = ({ onStartCameraScan, nfcScan, step }) => {
|
||||
|
||||
interface ScanScreenProps {
|
||||
onStartCameraScan: () => void;
|
||||
handleNFCScan: () => void;
|
||||
step: number;
|
||||
}
|
||||
|
||||
const ScanScreen: React.FC<ScanScreenProps> = ({ onStartCameraScan, handleNFCScan, step }) => {
|
||||
|
||||
return (
|
||||
<YStack >
|
||||
<ZStack alignSelf='center' maxWidth={50} maxHeight={50} width={50} flex={1} space="$0">
|
||||
<Circle
|
||||
alignSelf='center'
|
||||
h={22}
|
||||
w={22}
|
||||
borderWidth={1.6}
|
||||
p={0}
|
||||
/>
|
||||
<SizableText
|
||||
alignSelf='center'
|
||||
h={22}
|
||||
w={22}
|
||||
y={-1}
|
||||
x={7}
|
||||
color="black"
|
||||
fow="bold"
|
||||
>1</SizableText>
|
||||
</ZStack>
|
||||
<Text textAlign='center' mt="$-3" px="$4" fow={step === Steps.MRZ_SCAN ? "bold" : "normal"} >Scan the machine readable zone on the main page of your passport</Text>
|
||||
<YStack f={1} p="$5" gap="$1" px="$5" justifyContent='center'>
|
||||
<XStack
|
||||
jc="center"
|
||||
borderColor="black"
|
||||
borderWidth={1.5}
|
||||
borderRadius="$10"
|
||||
f={0}
|
||||
w="$1"
|
||||
h="$1"
|
||||
alignSelf='center'>
|
||||
<Text fontSize={12} alignSelf='center' fow="bold">1</Text>
|
||||
</XStack>
|
||||
<Text textAlign='center' mt="$2" fow={step === Steps.MRZ_SCAN ? "bold" : "normal"} >Scan the machine readable zone on the main page of your passport</Text>
|
||||
|
||||
<XStack
|
||||
mt="$9"
|
||||
jc="center"
|
||||
borderColor="black"
|
||||
borderWidth={1.5}
|
||||
borderRadius={10}
|
||||
f={0}
|
||||
w="$1"
|
||||
h="$1"
|
||||
alignSelf='center'>
|
||||
<Text fontSize={12} alignSelf='center' fow="bold">2</Text>
|
||||
</XStack>
|
||||
<Text textAlign='center' mt="$2" fow={(step === Steps.MRZ_SCAN_COMPLETED) || (step === Steps.NFC_SCANNING) ? "bold" : "normal"}>Hold your passport against your device to read the biometric chip</Text>
|
||||
|
||||
<ZStack alignSelf='center' mt="$8" maxWidth={50} maxHeight={50} width={50} flex={1} space="$0">
|
||||
<Circle
|
||||
alignSelf='center'
|
||||
h={22}
|
||||
w={22}
|
||||
borderWidth={1.6}
|
||||
p={0}
|
||||
/>
|
||||
<SizableText
|
||||
alignSelf='center'
|
||||
h={22}
|
||||
w={22}
|
||||
y={-1}
|
||||
x={7}
|
||||
color="black"
|
||||
fow="bold"
|
||||
>2</SizableText>
|
||||
</ZStack>
|
||||
<Text textAlign='center' mt="$-3" px="$4" fow={(step === Steps.MRZ_SCAN_COMPLETED) || (step === Steps.NFC_SCANNING) ? "bold" : "normal"}>Hold your passport against your device to read the biometric chip</Text>
|
||||
|
||||
<ZStack alignSelf='center' mt="$8" maxWidth={50} maxHeight={50} width={50} flex={1} space="$0">
|
||||
<Circle
|
||||
alignSelf='center'
|
||||
h={22}
|
||||
w={22}
|
||||
borderWidth={1.6}
|
||||
p={0}
|
||||
/>
|
||||
<SizableText
|
||||
alignSelf='center'
|
||||
h={22}
|
||||
w={22}
|
||||
y={-1}
|
||||
x={7}
|
||||
color="black"
|
||||
fow="bold"
|
||||
>3</SizableText>
|
||||
</ZStack>
|
||||
<Text textAlign='center' mt="$-3" px="$4" fow={step >= Steps.NFC_SCAN_COMPLETED ? "bold" : "normal"}>Select App</Text>
|
||||
|
||||
<YStack w="100%" ai="center">
|
||||
<YStack ai="center">
|
||||
{
|
||||
step < Steps.NFC_SCAN_COMPLETED
|
||||
? (
|
||||
@@ -79,18 +52,18 @@ const ScanScreen = ({ onStartCameraScan, nfcScan, step }) => {
|
||||
</YStack>
|
||||
: (
|
||||
<YStack mt="$10">
|
||||
<Text size="$4" br="$2" color="#3185FC" onPress={nfcScan}>
|
||||
<Text size="$4" br="$2" color="#3185FC" onPress={handleNFCScan}>
|
||||
{step === Steps.NFC_SCANNING ? "Scanning" : "Scan passport with NFC"}
|
||||
</Text>
|
||||
<Spinner mt="$4" color={step === Steps.NFC_SCANNING ? "#3185FC" : "transparent"} />
|
||||
</YStack>
|
||||
)
|
||||
)
|
||||
: <XStack />
|
||||
:
|
||||
<XStack />
|
||||
}
|
||||
</YStack>
|
||||
|
||||
</YStack>
|
||||
</YStack >
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
|
||||
// src/tamagui.config.js
|
||||
|
||||
import { createTamagui } from 'tamagui';
|
||||
|
||||
export const tamaguiConfig = createTamagui({
|
||||
// Your basic Tamagui configuration goes here
|
||||
// You can start with an empty config and extend it as needed
|
||||
});
|
||||
@@ -1,5 +1,3 @@
|
||||
// AppClass.ts
|
||||
|
||||
type Disclosure = {
|
||||
[key: string]: boolean;
|
||||
};
|
||||
@@ -8,15 +6,18 @@ export class App {
|
||||
id: string;
|
||||
name: string;
|
||||
disclosure: Disclosure;
|
||||
mintphrase: string;
|
||||
disclosurephrase: string;
|
||||
|
||||
constructor(id: string, name: string, disclosure: Disclosure) {
|
||||
constructor(id: string, name: string, disclosure: Disclosure, mintphrase: string, disclosurephrase: string) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
this.disclosure = disclosure;
|
||||
this.disclosurephrase = disclosurephrase;
|
||||
this.mintphrase = mintphrase;
|
||||
}
|
||||
}
|
||||
|
||||
// Create instances of the App class
|
||||
export const gitcoin = new App("gitcoin", "Gitcoin", {});
|
||||
export const soulbond = new App("soulbond", "Soulbond token", { nationality: false, expiry_date: false });
|
||||
export const zuzalu = new App("zuzalu", "Zupass", { date_of_birth: false });
|
||||
export const gitcoin = new App("gitcoin", "Gitcoin", {}, "Add to Gitcoin passport", "Gitcoin passport doesn't require disclosure of any data.");
|
||||
export const soulbound = new App("soulbound", "Soulbound token", { nationality: true, expiry_date: false }, "Mint Soulbound token", "Choose the information you want to disclose and mint your SBT.");
|
||||
export const zuzalu = new App("zuzalu", "Zupass", { date_of_birth: false }, "Add to Zupass", "Zupass requires the following information:");
|
||||
|
||||
15
app/tamagui.config.ts
Normal file
15
app/tamagui.config.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import { config } from '@tamagui/config/v3'
|
||||
import { createTamagui } from 'tamagui' // or '@tamagui/core'
|
||||
|
||||
const appConfig = createTamagui(config)
|
||||
|
||||
export type AppConfig = typeof appConfig
|
||||
|
||||
declare module 'tamagui' {
|
||||
// or '@tamagui/core'
|
||||
// overrides TamaguiCustomConfig so your custom types
|
||||
// work everywhere you import `tamagui`
|
||||
interface TamaguiCustomConfig extends AppConfig { }
|
||||
}
|
||||
|
||||
export default appConfig
|
||||
5203
app/yarn.lock
5203
app/yarn.lock
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user