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 { useState } from 'react';
import { Button, StyleSheet, Text, View } from 'react-native'; import {
import { isMyIdError, start } from 'myid-sdk'; ActivityIndicator,
Image,
// Replace with a sessionId issued by your backend and the hashes from MyID. Pressable,
const SESSION_ID = ''; ScrollView,
const CLIENT_HASH = ''; StyleSheet,
const CLIENT_HASH_ID = ''; 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() { 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 () => { const onStart = async () => {
setStatus('Starting...'); setBusy(true);
setResult(null);
setError(null);
try { try {
const result = await start({ const value = await start({
config: { config: { sessionId, clientHash, clientHashId, environment },
sessionId: SESSION_ID, iosAppearance: { colorPrimary: '#2F6FED', buttonCornerRadius: 12 },
clientHash: CLIENT_HASH,
clientHashId: CLIENT_HASH_ID,
environment: 'DEBUG',
},
}); });
setStatus(`Code ${result.code}`); setResult(value);
} catch (error) { } catch (caught) {
setStatus( setError(
isMyIdError(error) ? `${error.code}: ${error.message}` : 'Unknown error' isMyIdError(caught)
? `[${caught.code}] ${caught.message}`
: String(caught)
); );
} finally {
setBusy(false);
} }
}; };
const canStart =
!busy && sessionId !== '' && clientHash !== '' && clientHashId !== '';
return ( return (
<View style={styles.container}> <ScrollView contentContainerStyle={styles.container}>
<Text style={styles.status}>{status}</Text> <Text style={styles.title}>MyID SDK</Text>
<Button title="Start MyID" onPress={onPress} />
<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> </View>
); );
} }
const styles = StyleSheet.create({ const styles = StyleSheet.create({
container: { container: { padding: 24, gap: 16, paddingTop: 72 },
flex: 1, title: { fontSize: 28, fontWeight: '700', marginBottom: 8 },
alignItems: 'center', field: { gap: 6 },
justifyContent: 'center', label: { fontSize: 13, color: '#555' },
gap: 16, 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: { chipActive: { backgroundColor: '#2F6FED', borderColor: '#2F6FED' },
fontSize: 16, 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