Commit 854818e0 authored by Javokhir's avatar Javokhir
Browse files

feat(example): replace demo screen with a real MyID verification flow

Credentials are entered at runtime instead of hardcoded so the example can be
shared without leaking a client hash, and the result view renders the returned
portrait to prove the base64 payload decodes.
parent 1b21f2dd
import { useState } from 'react';
import { Button, StyleSheet, Text, View } from 'react-native';
import { isMyIdError, start } from 'myid-sdk';
// Replace with a sessionId issued by your backend and the hashes from MyID.
const SESSION_ID = '';
const CLIENT_HASH = '';
const CLIENT_HASH_ID = '';
import {
ActivityIndicator,
Image,
Pressable,
ScrollView,
StyleSheet,
Text,
TextInput,
View,
} from 'react-native';
import {
isMyIdError,
start,
type MyIdEnvironment,
type MyIdResult,
} from 'myid-sdk';
/**
* Credentials are entered at runtime rather than hardcoded so the demo can be
* shared without leaking a client hash. sessionId comes from your own backend.
*/
export default function App() {
const [status, setStatus] = useState('Idle');
const [sessionId, setSessionId] = useState('');
const [clientHash, setClientHash] = useState('');
const [clientHashId, setClientHashId] = useState('');
const [environment, setEnvironment] = useState<MyIdEnvironment>('DEBUG');
const [busy, setBusy] = useState(false);
const [result, setResult] = useState<MyIdResult | null>(null);
const [error, setError] = useState<string | null>(null);
const onPress = async () => {
setStatus('Starting...');
const onStart = async () => {
setBusy(true);
setResult(null);
setError(null);
try {
const result = await start({
config: {
sessionId: SESSION_ID,
clientHash: CLIENT_HASH,
clientHashId: CLIENT_HASH_ID,
environment: 'DEBUG',
},
const value = await start({
config: { sessionId, clientHash, clientHashId, environment },
iosAppearance: { colorPrimary: '#2F6FED', buttonCornerRadius: 12 },
});
setStatus(`Code ${result.code}`);
} catch (error) {
setStatus(
isMyIdError(error) ? `${error.code}: ${error.message}` : 'Unknown error'
setResult(value);
} catch (caught) {
setError(
isMyIdError(caught)
? `[${caught.code}] ${caught.message}`
: String(caught)
);
} finally {
setBusy(false);
}
};
const canStart =
!busy && sessionId !== '' && clientHash !== '' && clientHashId !== '';
return (
<View style={styles.container}>
<Text style={styles.status}>{status}</Text>
<Button title="Start MyID" onPress={onPress} />
<ScrollView contentContainerStyle={styles.container}>
<Text style={styles.title}>MyID SDK</Text>
<Field label="Session ID" value={sessionId} onChangeText={setSessionId} />
<Field
label="Client hash"
value={clientHash}
onChangeText={setClientHash}
/>
<Field
label="Client hash ID"
value={clientHashId}
onChangeText={setClientHashId}
/>
<View style={styles.row}>
{(['DEBUG', 'PRODUCTION'] as const).map((value) => (
<Pressable
key={value}
onPress={() => setEnvironment(value)}
style={[styles.chip, environment === value && styles.chipActive]}
>
<Text
style={[
styles.chipText,
environment === value && styles.chipTextActive,
]}
>
{value}
</Text>
</Pressable>
))}
</View>
<Pressable
onPress={onStart}
disabled={!canStart}
style={[styles.button, !canStart && styles.buttonDisabled]}
>
{busy ? (
<ActivityIndicator color="#fff" />
) : (
<Text style={styles.buttonText}>Start verification</Text>
)}
</Pressable>
{error !== null && <Text style={styles.error}>{error}</Text>}
{result !== null && (
<View style={styles.result}>
<Text style={styles.resultCode}>Result code: {result.code}</Text>
{result.base64 !== undefined && (
<Image
style={styles.portrait}
source={{ uri: `data:image/jpeg;base64,${result.base64}` }}
/>
)}
</View>
)}
</ScrollView>
);
}
function Field({
label,
value,
onChangeText,
}: {
label: string;
value: string;
onChangeText: (next: string) => void;
}) {
return (
<View style={styles.field}>
<Text style={styles.label}>{label}</Text>
<TextInput
style={styles.input}
value={value}
onChangeText={onChangeText}
autoCapitalize="none"
autoCorrect={false}
/>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
gap: 16,
container: { padding: 24, gap: 16, paddingTop: 72 },
title: { fontSize: 28, fontWeight: '700', marginBottom: 8 },
field: { gap: 6 },
label: { fontSize: 13, color: '#555' },
input: {
borderWidth: 1,
borderColor: '#ccc',
borderRadius: 8,
paddingHorizontal: 12,
paddingVertical: 10,
fontSize: 15,
},
row: { flexDirection: 'row', gap: 8 },
chip: {
paddingHorizontal: 14,
paddingVertical: 8,
borderRadius: 999,
borderWidth: 1,
borderColor: '#ccc',
},
status: {
fontSize: 16,
chipActive: { backgroundColor: '#2F6FED', borderColor: '#2F6FED' },
chipText: { color: '#333', fontSize: 13 },
chipTextActive: { color: '#fff' },
button: {
backgroundColor: '#2F6FED',
borderRadius: 10,
paddingVertical: 14,
alignItems: 'center',
marginTop: 8,
},
buttonDisabled: { backgroundColor: '#9db8f0' },
buttonText: { color: '#fff', fontSize: 16, fontWeight: '600' },
error: { color: '#c0392b', fontSize: 14 },
result: { gap: 12, alignItems: 'center' },
resultCode: { fontSize: 16, fontWeight: '600' },
portrait: { width: 180, height: 180, borderRadius: 12 },
});
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment