merged with latest develop
1
.github/workflows/android.yml
vendored
@@ -5,6 +5,7 @@ on:
|
||||
branches:
|
||||
- main
|
||||
- develop
|
||||
- demobranch
|
||||
tags:
|
||||
- '*'
|
||||
pull_request:
|
||||
|
||||
BIN
assets/ID-closed.png
Normal file
|
After Width: | Height: | Size: 103 KiB |
BIN
assets/ID-open.png
Normal file
|
After Width: | Height: | Size: 197 KiB |
BIN
assets/img1.png
Normal file
|
After Width: | Height: | Size: 216 KiB |
BIN
assets/img11.png
Normal file
|
After Width: | Height: | Size: 35 KiB |
BIN
assets/img12.png
Normal file
|
After Width: | Height: | Size: 36 KiB |
BIN
assets/img13.png
Normal file
|
After Width: | Height: | Size: 33 KiB |
BIN
assets/img2.png
Normal file
|
After Width: | Height: | Size: 210 KiB |
BIN
assets/img3.png
Normal file
|
After Width: | Height: | Size: 248 KiB |
BIN
assets/img4.png
Normal file
|
After Width: | Height: | Size: 82 KiB |
BIN
assets/img5.png
Normal file
|
After Width: | Height: | Size: 20 KiB |
BIN
assets/img6.png
Normal file
|
After Width: | Height: | Size: 89 KiB |
BIN
assets/img7.png
Normal file
|
After Width: | Height: | Size: 4.9 KiB |
BIN
assets/img8.png
Normal file
|
After Width: | Height: | Size: 9.9 KiB |
BIN
assets/img9.png
Normal file
|
After Width: | Height: | Size: 9.0 KiB |
@@ -12,12 +12,6 @@ export const DeviceInfoList: React.FC<DeviceInfoProps> = (props) => {
|
||||
label={props.of === 'receiver' ? t('requestedBy') : t('sentBy')}
|
||||
text={props.deviceInfo.deviceName}
|
||||
/>
|
||||
<TextItem divider label={t('name')} text={props.deviceInfo.name} />
|
||||
<TextItem
|
||||
divider
|
||||
label={t('deviceRefNumber')}
|
||||
text={props.deviceInfo.deviceId}
|
||||
/>
|
||||
</React.Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -4,7 +4,7 @@ import { Popable } from 'react-native-popable';
|
||||
import { Text } from 'react-native-elements';
|
||||
import Icon from 'react-native-vector-icons/MaterialCommunityIcons';
|
||||
import { Row } from './ui';
|
||||
import { Colors } from './ui/styleUtils';
|
||||
import { Theme } from './ui/styleUtils';
|
||||
|
||||
export const DropdownIcon: React.FC<DropdownProps> = (props) => {
|
||||
const popover = useRef(null);
|
||||
@@ -21,7 +21,7 @@ export const DropdownIcon: React.FC<DropdownProps> = (props) => {
|
||||
padding: 8,
|
||||
paddingTop: 4,
|
||||
paddingBottom: 4,
|
||||
borderBottomColor: Colors.Grey6,
|
||||
borderBottomColor: Theme.Colors.borderBottomColor,
|
||||
borderBottomWidth: 1,
|
||||
}}>
|
||||
<Pressable
|
||||
@@ -30,7 +30,7 @@ export const DropdownIcon: React.FC<DropdownProps> = (props) => {
|
||||
<Row>
|
||||
<Icon
|
||||
name={item.icon}
|
||||
color={Colors.Orange}
|
||||
color={Theme.Colors.Icon}
|
||||
size={20}
|
||||
style={{ marginRight: 8 }}
|
||||
/>
|
||||
@@ -46,7 +46,7 @@ export const DropdownIcon: React.FC<DropdownProps> = (props) => {
|
||||
<Popable
|
||||
position="bottom"
|
||||
ref={popover}
|
||||
backgroundColor={Colors.White}
|
||||
backgroundColor={Theme.Colors.whiteBackgroundColor}
|
||||
style={{ top: 10, left: -20, minWidth: 120, elevation: 1 }}
|
||||
content={
|
||||
<View>
|
||||
@@ -57,7 +57,7 @@ export const DropdownIcon: React.FC<DropdownProps> = (props) => {
|
||||
/>
|
||||
</View>
|
||||
}>
|
||||
<Icon name={props.icon} color={Colors.Orange} size={24} />
|
||||
<Icon name={props.icon} color={Theme.Colors.Icon} size={24} />
|
||||
</Popable>
|
||||
</View>
|
||||
);
|
||||
|
||||
@@ -2,7 +2,7 @@ import React, { useState } from 'react';
|
||||
import { Dimensions } from 'react-native';
|
||||
import { ListItem, Overlay, Input } from 'react-native-elements';
|
||||
import { Text, Column, Row, Button } from './ui';
|
||||
import { Colors } from './ui/styleUtils';
|
||||
import { Theme } from './ui/styleUtils';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
export const EditableListItem: React.FC<EditableListItemProps> = (props) => {
|
||||
@@ -14,10 +14,10 @@ export const EditableListItem: React.FC<EditableListItemProps> = (props) => {
|
||||
<ListItem bottomDivider onPress={() => setIsEditing(true)}>
|
||||
<ListItem.Content>
|
||||
<ListItem.Title>
|
||||
<Text>{props.label}</Text>
|
||||
<Text color={Theme.Colors.profileLabel}>{props.label}</Text>
|
||||
</ListItem.Title>
|
||||
</ListItem.Content>
|
||||
<Text color={Colors.Grey}>{props.value}</Text>
|
||||
<Text color={Theme.Colors.profileValue}>{props.value}</Text>
|
||||
<Overlay
|
||||
overlayStyle={{ padding: 24, elevation: 6 }}
|
||||
isVisible={isEditing}
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
import React from 'react';
|
||||
import { View, Image } from 'react-native';
|
||||
import { Theme } from './ui/styleUtils';
|
||||
|
||||
export const Logo: React.FC<LogoProps> = (props) => {
|
||||
return (
|
||||
<View>
|
||||
<Image
|
||||
style={{ resizeMode: 'contain', ...props }}
|
||||
source={require('../assets/mosip-logo.png')}
|
||||
source={Theme.MosipLogo}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
|
||||
@@ -2,12 +2,12 @@ import React from 'react';
|
||||
import { Dimensions, StyleSheet } from 'react-native';
|
||||
import { Overlay, LinearProgress } from 'react-native-elements';
|
||||
import { Column, Text } from './ui';
|
||||
import { Colors, elevation } from './ui/styleUtils';
|
||||
import { Theme } from './ui/styleUtils';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
overlay: {
|
||||
...elevation(5),
|
||||
backgroundColor: Colors.White,
|
||||
...Theme.elevation(5),
|
||||
backgroundColor: Theme.Colors.whiteBackgroundColor,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -16,7 +16,8 @@ export const MessageOverlay: React.FC<MessageOverlayProps> = (props) => {
|
||||
<Overlay
|
||||
isVisible={props.isVisible}
|
||||
overlayStyle={styles.overlay}
|
||||
onBackdropPress={props.onBackdropPress}>
|
||||
onBackdropPress={props.onBackdropPress}
|
||||
onShow={props.onShow}>
|
||||
<Column padding="24" width={Dimensions.get('screen').width * 0.8}>
|
||||
{props.title && (
|
||||
<Text weight="semibold" margin="0 0 12 0">
|
||||
@@ -25,7 +26,10 @@ export const MessageOverlay: React.FC<MessageOverlayProps> = (props) => {
|
||||
)}
|
||||
{props.message && <Text margin="0 0 12 0">{props.message}</Text>}
|
||||
{props.hasProgress && (
|
||||
<LinearProgress variant="indeterminate" color={Colors.Orange} />
|
||||
<LinearProgress
|
||||
variant="indeterminate"
|
||||
color={Theme.Colors.Loading}
|
||||
/>
|
||||
)}
|
||||
</Column>
|
||||
</Overlay>
|
||||
@@ -38,4 +42,5 @@ interface MessageOverlayProps {
|
||||
message?: string;
|
||||
hasProgress?: boolean;
|
||||
onBackdropPress?: () => void;
|
||||
onShow?: () => void;
|
||||
}
|
||||
|
||||
308
components/NewVcDetails.tsx
Normal file
@@ -0,0 +1,308 @@
|
||||
import { formatDistanceToNow } from 'date-fns';
|
||||
import React from 'react';
|
||||
import * as DateFnsLocale from '../lib/date-fns/locale';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Image, ImageBackground } from 'react-native';
|
||||
import { Icon, ListItem } from 'react-native-elements';
|
||||
import { VC, CredentialSubject } from '../types/vc';
|
||||
import { Column, Row, Text } from './ui';
|
||||
import { Theme } from './ui/styleUtils';
|
||||
import { TextItem } from './ui/TextItem';
|
||||
import { useReceiveVcModal } from '../screens/Request/ReceiveVcModalController';
|
||||
|
||||
const VerifiedIcon: React.FC = () => {
|
||||
return (
|
||||
<Icon
|
||||
name="check-circle"
|
||||
color={Theme.Colors.VerifiedIcon}
|
||||
size={14}
|
||||
containerStyle={{ marginStart: 4, bottom: 1 }}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export const NewVcDetails: React.FC<VcDetailsProps> = (props) => {
|
||||
const { t, i18n } = useTranslation('VcDetails');
|
||||
const controller = useReceiveVcModal();
|
||||
|
||||
return (
|
||||
<ImageBackground
|
||||
style={{
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
}}
|
||||
source={Theme.OpenCard}>
|
||||
<Row style={Theme.Styles.successTag}>
|
||||
<Icon
|
||||
name="check-circle"
|
||||
color={Theme.Colors.checkCircleIcon}
|
||||
size={40}
|
||||
/>
|
||||
<Text margin="0 10" color={Theme.Colors.whiteText}>
|
||||
{controller.vcLabel.singular} Received
|
||||
</Text>
|
||||
</Row>
|
||||
|
||||
<Column>
|
||||
<Column style={Theme.Styles.closeDetailsHeader}>
|
||||
<Column>
|
||||
<Text
|
||||
weight="bold"
|
||||
size="smaller"
|
||||
color={Theme.Colors.DetailsLabel}>
|
||||
{t('fullName')}
|
||||
</Text>
|
||||
<Text weight="bold" size="smaller" color={Theme.Colors.Details}>
|
||||
{getLocalizedField(
|
||||
props.vc?.verifiableCredential.credentialSubject.fullName
|
||||
)}
|
||||
</Text>
|
||||
</Column>
|
||||
<Image source={Theme.MosipLogo} style={Theme.Styles.logo} />
|
||||
</Column>
|
||||
|
||||
<Row style={Theme.Styles.openDetailsContainer}>
|
||||
<ListItem bottomDivider>
|
||||
<ListItem.Content>
|
||||
<ListItem.Subtitle>{t('photo')}</ListItem.Subtitle>
|
||||
<ListItem.Content>
|
||||
<Image
|
||||
source={
|
||||
props.vc?.credential.biometrics?.face
|
||||
? { uri: props.vc?.credential.biometrics.face }
|
||||
: Theme.ProfileIcon
|
||||
}
|
||||
style={Theme.Styles.openCardImage}
|
||||
/>
|
||||
</ListItem.Content>
|
||||
</ListItem.Content>
|
||||
</ListItem>
|
||||
|
||||
<Column style={Theme.Styles.details}>
|
||||
<Column style={Theme.Styles.labelPart}>
|
||||
<Column
|
||||
fill
|
||||
padding="12 16"
|
||||
margin="0 16 0 0"
|
||||
style={Theme.Styles.labelPart}>
|
||||
<Text
|
||||
size="smaller"
|
||||
color={Theme.Colors.DetailsLabel}
|
||||
style={{ textTransform: 'uppercase', fontWeight: 'bold' }}>
|
||||
{props.vc?.idType}
|
||||
</Text>
|
||||
<Text
|
||||
weight="semibold"
|
||||
size="smaller"
|
||||
color={Theme.Colors.Details}>
|
||||
{props.vc?.id}
|
||||
</Text>
|
||||
</Column>
|
||||
<Column
|
||||
fill
|
||||
padding="12 16"
|
||||
margin="0 16 0 0"
|
||||
style={Theme.Styles.labelPart}>
|
||||
<Text
|
||||
weight="bold"
|
||||
size="smaller"
|
||||
color={Theme.Colors.DetailsLabel}>
|
||||
{t('generatedOn')}
|
||||
</Text>
|
||||
<Text
|
||||
weight="semibold"
|
||||
size="smaller"
|
||||
color={Theme.Colors.Details}>
|
||||
{new Date(props.vc?.generatedOn).toLocaleDateString()}
|
||||
</Text>
|
||||
</Column>
|
||||
<Column fill padding="12 16" style={Theme.Styles.labelPart}>
|
||||
<Text
|
||||
weight="bold"
|
||||
size="smaller"
|
||||
color={Theme.Colors.DetailsLabel}>
|
||||
{t('status')}
|
||||
</Text>
|
||||
<Row>
|
||||
<Text
|
||||
weight="semibold"
|
||||
size="smaller"
|
||||
color={Theme.Colors.Details}>
|
||||
{t('valid')}
|
||||
</Text>
|
||||
{props.vc?.isVerified && <VerifiedIcon />}
|
||||
</Row>
|
||||
</Column>
|
||||
</Column>
|
||||
|
||||
<Column
|
||||
fill
|
||||
padding="12 16"
|
||||
margin="0 16 0 0"
|
||||
style={Theme.Styles.labelPart}>
|
||||
<Text
|
||||
weight="bold"
|
||||
size="smaller"
|
||||
color={Theme.Colors.DetailsLabel}>
|
||||
{t('gender')}
|
||||
</Text>
|
||||
<Text
|
||||
weight="semibold"
|
||||
size="smaller"
|
||||
color={Theme.Colors.Details}>
|
||||
{getLocalizedField(
|
||||
props.vc?.verifiableCredential.credentialSubject.gender
|
||||
)}
|
||||
</Text>
|
||||
</Column>
|
||||
|
||||
<Column
|
||||
fill
|
||||
padding="12 16"
|
||||
margin="0 16 0 0"
|
||||
style={Theme.Styles.labelPart}>
|
||||
<Text
|
||||
weight="bold"
|
||||
size="smaller"
|
||||
color={Theme.Colors.DetailsLabel}>
|
||||
{t('dateOfBirth')}
|
||||
</Text>
|
||||
<Text
|
||||
weight="semibold"
|
||||
size="smaller"
|
||||
color={Theme.Colors.Details}>
|
||||
{new Date(
|
||||
getLocalizedField(
|
||||
props.vc?.verifiableCredential.credentialSubject.dateOfBirth
|
||||
)
|
||||
).toLocaleDateString()}
|
||||
</Text>
|
||||
</Column>
|
||||
|
||||
<Column
|
||||
fill
|
||||
padding="12 16"
|
||||
margin="0 16 0 0"
|
||||
style={Theme.Styles.labelPart}>
|
||||
<Text
|
||||
weight="bold"
|
||||
size="smaller"
|
||||
color={Theme.Colors.DetailsLabel}>
|
||||
{t('phoneNumber')}
|
||||
</Text>
|
||||
<Text
|
||||
weight="semibold"
|
||||
size="smaller"
|
||||
color={Theme.Colors.Details}>
|
||||
{getLocalizedField(
|
||||
props.vc?.verifiableCredential.credentialSubject.phone
|
||||
)}
|
||||
</Text>
|
||||
</Column>
|
||||
|
||||
<Column
|
||||
fill
|
||||
padding="12 16"
|
||||
margin="0 16 0 0"
|
||||
style={Theme.Styles.labelPart}>
|
||||
<Text
|
||||
weight="bold"
|
||||
size="smaller"
|
||||
color={Theme.Colors.DetailsLabel}>
|
||||
{t('email')}
|
||||
</Text>
|
||||
<Text
|
||||
weight="semibold"
|
||||
size="smaller"
|
||||
color={Theme.Colors.Details}>
|
||||
{getLocalizedField(
|
||||
props.vc?.verifiableCredential.credentialSubject.email
|
||||
)}
|
||||
</Text>
|
||||
</Column>
|
||||
|
||||
<Column
|
||||
fill
|
||||
padding="12 16"
|
||||
margin="0 16 0 0"
|
||||
style={Theme.Styles.labelPart}>
|
||||
<Text
|
||||
weight="bold"
|
||||
size="smaller"
|
||||
color={Theme.Colors.DetailsLabel}>
|
||||
{t('address')}
|
||||
</Text>
|
||||
<Text
|
||||
weight="semibold"
|
||||
size="smaller"
|
||||
color={Theme.Colors.Details}>
|
||||
{getFullAddress(
|
||||
props.vc?.verifiableCredential.credentialSubject
|
||||
)}
|
||||
</Text>
|
||||
</Column>
|
||||
|
||||
{props.vc?.reason?.length > 0 && (
|
||||
<Text margin="24 24 16 24" weight="semibold">
|
||||
{t('reasonForSharing')}
|
||||
</Text>
|
||||
)}
|
||||
{props.vc?.reason?.map((reason, index) => (
|
||||
<TextItem
|
||||
key={index}
|
||||
divider
|
||||
label={formatDistanceToNow(reason.timestamp, {
|
||||
addSuffix: true,
|
||||
locale: DateFnsLocale[i18n.language],
|
||||
})}
|
||||
text={reason.message}
|
||||
/>
|
||||
))}
|
||||
</Column>
|
||||
</Row>
|
||||
</Column>
|
||||
</ImageBackground>
|
||||
);
|
||||
};
|
||||
|
||||
interface VcDetailsProps {
|
||||
vc: VC;
|
||||
}
|
||||
|
||||
interface LocalizedField {
|
||||
language: string;
|
||||
value: string;
|
||||
}
|
||||
|
||||
function getFullAddress(credential: CredentialSubject) {
|
||||
if (!credential) {
|
||||
return '';
|
||||
}
|
||||
|
||||
const fields = [
|
||||
'addressLine1',
|
||||
'addressLine2',
|
||||
'addressLine3',
|
||||
'city',
|
||||
'province',
|
||||
'region',
|
||||
];
|
||||
|
||||
return fields
|
||||
.map((field) => getLocalizedField(credential[field]))
|
||||
.concat(credential.postalCode)
|
||||
.filter(Boolean)
|
||||
.join(', ');
|
||||
}
|
||||
|
||||
function getLocalizedField(rawField: string | LocalizedField) {
|
||||
if (typeof rawField === 'string') {
|
||||
return rawField;
|
||||
}
|
||||
try {
|
||||
const locales: LocalizedField[] = JSON.parse(JSON.stringify(rawField));
|
||||
return locales[0].value;
|
||||
} catch (e) {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,7 @@ import { Dimensions, Modal as RNModal, StyleSheet } from 'react-native';
|
||||
import { Icon } from 'react-native-elements';
|
||||
import { PasscodeVerify } from '../components/PasscodeVerify';
|
||||
import { Column, Text } from '../components/ui';
|
||||
import { Colors } from '../components/ui/styleUtils';
|
||||
import { Theme } from '../components/ui/styleUtils';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
modal: {
|
||||
@@ -19,8 +19,11 @@ export const Passcode: React.FC<PasscodeProps> = (props) => {
|
||||
style={styles.modal}
|
||||
visible={true}
|
||||
onRequestClose={props.onDismiss}>
|
||||
<Column fill padding="32" backgroundColor={Colors.White}>
|
||||
<Icon name="lock" color={Colors.Orange} size={60} />
|
||||
<Column
|
||||
fill
|
||||
padding="32"
|
||||
backgroundColor={Theme.Colors.whiteBackgroundColor}>
|
||||
<Icon name="lock" color={Theme.Colors.Icon} size={60} />
|
||||
<Column fill align="space-between" width="100%">
|
||||
<Text align="center">{props.message || 'Enter your passcode'}</Text>
|
||||
<PasscodeVerify
|
||||
@@ -30,7 +33,7 @@ export const Passcode: React.FC<PasscodeProps> = (props) => {
|
||||
/>
|
||||
</Column>
|
||||
<Column fill>
|
||||
<Text align="center" color={Colors.Red}>
|
||||
<Text align="center" color={Theme.Colors.errorMessage}>
|
||||
{props.error}
|
||||
</Text>
|
||||
</Column>
|
||||
|
||||
@@ -1,24 +1,8 @@
|
||||
import React, { useEffect } from 'react';
|
||||
import { StyleSheet, TextInput } from 'react-native';
|
||||
import { TextInput } from 'react-native';
|
||||
import { usePinInput } from '../machines/pinInput';
|
||||
import { Row } from './ui';
|
||||
import { Colors } from './ui/styleUtils';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
input: {
|
||||
borderBottomWidth: 1,
|
||||
borderColor: Colors.Grey,
|
||||
color: Colors.Black,
|
||||
flex: 1,
|
||||
fontFamily: 'Poppins_600SemiBold',
|
||||
fontSize: 18,
|
||||
fontWeight: '600',
|
||||
height: 40,
|
||||
lineHeight: 28,
|
||||
margin: 8,
|
||||
textAlign: 'center',
|
||||
},
|
||||
});
|
||||
import { Theme } from './ui/styleUtils';
|
||||
|
||||
export const PinInput: React.FC<PinInputProps> = (props) => {
|
||||
const { state, send, events } = usePinInput(props.length);
|
||||
@@ -38,8 +22,8 @@ export const PinInput: React.FC<PinInputProps> = (props) => {
|
||||
selectTextOnFocus
|
||||
keyboardType="numeric"
|
||||
maxLength={1}
|
||||
selectionColor={Colors.Orange}
|
||||
style={styles.input}
|
||||
selectionColor={Theme.Colors.inputSelection}
|
||||
style={Theme.PinInputStyle.input}
|
||||
key={index}
|
||||
ref={input}
|
||||
value={values[index]}
|
||||
|
||||
@@ -2,35 +2,14 @@ import React, { useContext, useEffect, useState } from 'react';
|
||||
import Icon from 'react-native-vector-icons/MaterialIcons';
|
||||
import { Camera } from 'expo-camera';
|
||||
import { BarCodeEvent, BarCodeScanner } from 'expo-barcode-scanner';
|
||||
import { Linking, StyleSheet, TouchableOpacity, View } from 'react-native';
|
||||
import { Colors } from './ui/styleUtils';
|
||||
import { Linking, TouchableOpacity, View } from 'react-native';
|
||||
import { Theme } from './ui/styleUtils';
|
||||
import { Column, Button, Text } from './ui';
|
||||
import { GlobalContext } from '../shared/GlobalContext';
|
||||
import { useSelector } from '@xstate/react';
|
||||
import { selectIsActive } from '../machines/app';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
scannerContainer: {
|
||||
borderWidth: 4,
|
||||
borderColor: Colors.Black,
|
||||
borderRadius: 32,
|
||||
justifyContent: 'center',
|
||||
height: 300,
|
||||
width: 300,
|
||||
overflow: 'hidden',
|
||||
},
|
||||
scanner: {
|
||||
height: 400,
|
||||
width: '100%',
|
||||
margin: 'auto',
|
||||
},
|
||||
flipIconButton: {
|
||||
alignSelf: 'center',
|
||||
alignItems: 'center',
|
||||
},
|
||||
});
|
||||
|
||||
export const QrScanner: React.FC<QrScannerProps> = (props) => {
|
||||
const { t } = useTranslation('QrScanner');
|
||||
const { appService } = useContext(GlobalContext);
|
||||
@@ -67,7 +46,7 @@ export const QrScanner: React.FC<QrScannerProps> = (props) => {
|
||||
if (hasPermission === false) {
|
||||
return (
|
||||
<Column fill align="space-between">
|
||||
<Text align="center" color={Colors.Red}>
|
||||
<Text align="center" color={Theme.Colors.errorMessage}>
|
||||
{t('missingPermissionText')}
|
||||
</Text>
|
||||
<Button title={t('allowCameraButton')} onPress={openSettings} />
|
||||
@@ -77,9 +56,9 @@ export const QrScanner: React.FC<QrScannerProps> = (props) => {
|
||||
|
||||
return (
|
||||
<View>
|
||||
<View style={styles.scannerContainer}>
|
||||
<View style={Theme.Styles.scannerContainer}>
|
||||
<Camera
|
||||
style={styles.scanner}
|
||||
style={Theme.Styles.scanner}
|
||||
barCodeScannerSettings={{
|
||||
barcodeTypes: [BarCodeScanner.Constants.BarCodeType.qr],
|
||||
}}
|
||||
@@ -89,7 +68,7 @@ export const QrScanner: React.FC<QrScannerProps> = (props) => {
|
||||
</View>
|
||||
<Column margin="24 0">
|
||||
<TouchableOpacity
|
||||
style={styles.flipIconButton}
|
||||
style={Theme.Styles.flipIconButton}
|
||||
onPress={() => {
|
||||
setType(
|
||||
type === Camera.Constants.Type.back
|
||||
@@ -97,7 +76,11 @@ export const QrScanner: React.FC<QrScannerProps> = (props) => {
|
||||
: Camera.Constants.Type.back
|
||||
);
|
||||
}}>
|
||||
<Icon name="flip-camera-ios" color={Colors.Black} size={64} />
|
||||
<Icon
|
||||
name="flip-camera-ios"
|
||||
color={Theme.Colors.flipCameraIcon}
|
||||
size={64}
|
||||
/>
|
||||
</TouchableOpacity>
|
||||
</Column>
|
||||
</View>
|
||||
|
||||
205
components/SingleVcItem.tsx
Normal file
@@ -0,0 +1,205 @@
|
||||
import React, { useContext, useRef } from 'react';
|
||||
import { useInterpret, useSelector } from '@xstate/react';
|
||||
import { Image, ImageBackground } from 'react-native';
|
||||
import { CheckBox, Icon } from 'react-native-elements';
|
||||
import { ActorRefFrom } from 'xstate';
|
||||
import {
|
||||
createVcItemMachine,
|
||||
selectVerifiableCredential,
|
||||
selectGeneratedOn,
|
||||
selectTag,
|
||||
selectId,
|
||||
vcItemMachine,
|
||||
selectContext,
|
||||
} from '../machines/vcItem';
|
||||
import { Column, Row, Text } from './ui';
|
||||
import { Theme } from './ui/styleUtils';
|
||||
import { GlobalContext } from '../shared/GlobalContext';
|
||||
import { RotatingIcon } from './RotatingIcon';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
const VerifiedIcon: React.FC = () => {
|
||||
return (
|
||||
<Icon
|
||||
name="check-circle"
|
||||
color={Theme.Colors.VerifiedIcon}
|
||||
size={14}
|
||||
containerStyle={{ marginStart: 4, bottom: 1 }}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const getDetails = (arg1, arg2, verifiableCredential) => {
|
||||
if (arg1 === 'Full Name') {
|
||||
return (
|
||||
<Column>
|
||||
<Text color={Theme.Colors.DetailsLabel} size="smaller">
|
||||
{arg1}
|
||||
</Text>
|
||||
<Text
|
||||
color={Theme.Colors.Details}
|
||||
numLines={1}
|
||||
weight="bold"
|
||||
size="smaller"
|
||||
style={
|
||||
!verifiableCredential
|
||||
? Theme.Styles.loadingTitle
|
||||
: Theme.Styles.subtitle
|
||||
}>
|
||||
{!verifiableCredential ? '' : arg2}
|
||||
</Text>
|
||||
</Column>
|
||||
);
|
||||
}
|
||||
if (arg1 === 'Status') {
|
||||
return (
|
||||
<Column>
|
||||
<Text size="smaller" color={Theme.Colors.DetailsLabel}>
|
||||
{arg1}
|
||||
</Text>
|
||||
<Row>
|
||||
<Text
|
||||
weight="bold"
|
||||
color={Theme.Colors.Details}
|
||||
size="smaller"
|
||||
style={
|
||||
!verifiableCredential
|
||||
? Theme.Styles.loadingTitle
|
||||
: Theme.Styles.subtitle
|
||||
}>
|
||||
{!verifiableCredential ? '' : arg2}
|
||||
</Text>
|
||||
{!verifiableCredential ? null : <VerifiedIcon />}
|
||||
</Row>
|
||||
</Column>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<Column>
|
||||
<Text color={Theme.Colors.DetailsLabel} size="smaller">
|
||||
{arg1}
|
||||
</Text>
|
||||
<Text
|
||||
numLines={1}
|
||||
color={Theme.Colors.Details}
|
||||
weight="bold"
|
||||
size="smaller"
|
||||
style={
|
||||
!verifiableCredential
|
||||
? Theme.Styles.loadingTitle
|
||||
: Theme.Styles.subtitle
|
||||
}>
|
||||
{!verifiableCredential ? '' : arg2}
|
||||
</Text>
|
||||
</Column>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export const SingleVcItem: React.FC<VcItemProps> = (props) => {
|
||||
const { appService } = useContext(GlobalContext);
|
||||
const { t } = useTranslation('VcDetails');
|
||||
|
||||
const machine = useRef(
|
||||
createVcItemMachine(appService.getSnapshot().serviceRefs, props.vcKey)
|
||||
);
|
||||
|
||||
const service = useInterpret(machine.current);
|
||||
const context = useSelector(service, selectContext);
|
||||
const verifiableCredential = useSelector(service, selectVerifiableCredential);
|
||||
const uin = useSelector(service, selectId);
|
||||
const tag = useSelector(service, selectTag);
|
||||
const generatedOn = useSelector(service, selectGeneratedOn);
|
||||
const fullName = !verifiableCredential
|
||||
? ''
|
||||
: getLocalizedField(verifiableCredential.credentialSubject.fullName);
|
||||
|
||||
const selectableOrCheck = props.selectable ? (
|
||||
<CheckBox
|
||||
checked={props.selected}
|
||||
checkedIcon={<Icon name="radio-button-checked" />}
|
||||
uncheckedIcon={<Icon name="radio-button-unchecked" />}
|
||||
onPress={() => props.onPress(service)}
|
||||
/>
|
||||
) : null;
|
||||
|
||||
return (
|
||||
<Column onShow={props.onShow(service)}>
|
||||
<ImageBackground
|
||||
source={!verifiableCredential ? null : Theme.CloseCard}
|
||||
resizeMode="stretch"
|
||||
style={
|
||||
!verifiableCredential
|
||||
? Theme.Styles.vertloadingContainer
|
||||
: Theme.Styles.backgroundImageContainer
|
||||
}>
|
||||
<Row style={Theme.Styles.homeCloseCardDetailsHeader}>
|
||||
<Image
|
||||
source={Theme.MosipLogo}
|
||||
style={Theme.Styles.logo}
|
||||
resizeMethod="auto"
|
||||
/>
|
||||
</Row>
|
||||
<Row
|
||||
crossAlign="center"
|
||||
margin="5 0 0 0"
|
||||
style={!verifiableCredential ? Theme.Styles.loadingContainer : null}>
|
||||
<Column
|
||||
style={
|
||||
!verifiableCredential
|
||||
? Theme.Styles.loadingContainer
|
||||
: Theme.Styles.closeDetails
|
||||
}>
|
||||
<Image
|
||||
source={
|
||||
!verifiableCredential
|
||||
? Theme.ProfileIcon
|
||||
: { uri: context.credential.biometrics.face }
|
||||
}
|
||||
style={Theme.Styles.closeCardImage}
|
||||
/>
|
||||
|
||||
<Column margin="0 0 0 10">
|
||||
{getDetails(t('fullName'), fullName, verifiableCredential)}
|
||||
{getDetails(t('uin'), tag || uin, verifiableCredential)}
|
||||
{getDetails(t('generatedOn'), generatedOn, verifiableCredential)}
|
||||
{getDetails(t('status'), t('valid'), verifiableCredential)}
|
||||
</Column>
|
||||
</Column>
|
||||
|
||||
{verifiableCredential ? (
|
||||
selectableOrCheck
|
||||
) : (
|
||||
<RotatingIcon name="sync" color={Theme.Colors.rotatingIcon} />
|
||||
)}
|
||||
</Row>
|
||||
</ImageBackground>
|
||||
</Column>
|
||||
);
|
||||
};
|
||||
|
||||
interface VcItemProps {
|
||||
vcKey: string;
|
||||
margin?: string;
|
||||
selectable?: boolean;
|
||||
selected?: boolean;
|
||||
onPress?: (vcRef?: ActorRefFrom<typeof vcItemMachine>) => void;
|
||||
onShow?: (vcRef?: ActorRefFrom<typeof vcItemMachine>) => void;
|
||||
}
|
||||
|
||||
interface LocalizedField {
|
||||
language: string;
|
||||
value: string;
|
||||
}
|
||||
|
||||
function getLocalizedField(rawField: string | LocalizedField) {
|
||||
if (typeof rawField === 'string') {
|
||||
return rawField;
|
||||
}
|
||||
try {
|
||||
const locales: LocalizedField[] = JSON.parse(JSON.stringify(rawField));
|
||||
return locales[0].value;
|
||||
} catch (e) {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
21
components/SuccesfullyReceived.tsx
Normal file
@@ -0,0 +1,21 @@
|
||||
import React from 'react';
|
||||
import { Overlay } from 'react-native-elements';
|
||||
|
||||
export const SuccesfullyReceived: React.FC<MessageOverlayProps> = (props) => {
|
||||
return (
|
||||
<Overlay
|
||||
isVisible={props.isVisible}
|
||||
onBackdropPress={props.onBackdropPress}
|
||||
onShow={props.onShow}></Overlay>
|
||||
);
|
||||
};
|
||||
|
||||
interface MessageOverlayProps {
|
||||
isVisible: boolean;
|
||||
img?: string;
|
||||
title?: string;
|
||||
message?: string;
|
||||
hasProgress?: boolean;
|
||||
onBackdropPress?: () => void;
|
||||
onShow?: () => void;
|
||||
}
|
||||
@@ -2,13 +2,13 @@ import React, { useState } from 'react';
|
||||
import { Dimensions, StyleSheet } from 'react-native';
|
||||
import { Overlay, Input } from 'react-native-elements';
|
||||
import { Button, Column, Row, Text } from './ui';
|
||||
import { Colors, elevation } from './ui/styleUtils';
|
||||
import { Theme } from './ui/styleUtils';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
overlay: {
|
||||
...elevation(5),
|
||||
backgroundColor: Colors.White,
|
||||
...Theme.elevation(5),
|
||||
backgroundColor: Theme.Colors.overlayBackgroundColor,
|
||||
padding: 0,
|
||||
},
|
||||
});
|
||||
|
||||
230
components/UpdatedVcDetails.tsx
Normal file
@@ -0,0 +1,230 @@
|
||||
import { formatDistanceToNow } from 'date-fns';
|
||||
import React from 'react';
|
||||
import * as DateFnsLocale from '../lib/date-fns/locale';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Image, ImageBackground } from 'react-native';
|
||||
import { Icon } from 'react-native-elements';
|
||||
import { VC, CredentialSubject } from '../types/vc';
|
||||
import { Column, Row, Text } from './ui';
|
||||
import { Theme } from './ui/styleUtils';
|
||||
import { TextItem } from './ui/TextItem';
|
||||
|
||||
const VerifiedIcon: React.FC = () => {
|
||||
return (
|
||||
<Icon
|
||||
name="check-circle"
|
||||
color={Theme.Colors.VerifiedIcon}
|
||||
size={14}
|
||||
containerStyle={{ marginStart: 4, bottom: 1 }}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export const UpdatedVcDetails: React.FC<VcDetailsProps> = (props) => {
|
||||
const { t, i18n } = useTranslation('VcDetails');
|
||||
|
||||
return (
|
||||
<ImageBackground
|
||||
borderRadius={10}
|
||||
style={Theme.Styles.openCardBgContainer}
|
||||
source={Theme.OpenCard}>
|
||||
<Row style={Theme.Styles.openDetailsHeader}>
|
||||
<Column>
|
||||
<Text weight="bold" size="smaller" color={Theme.Colors.DetailsLabel}>
|
||||
{t('fullName')}
|
||||
</Text>
|
||||
<Text weight="bold" size="smaller" color={Theme.Colors.Details}>
|
||||
{getLocalizedField(
|
||||
props.vc?.verifiableCredential.credentialSubject.fullName
|
||||
)}
|
||||
</Text>
|
||||
</Column>
|
||||
<Image source={Theme.MosipLogo} style={Theme.Styles.logo} />
|
||||
</Row>
|
||||
|
||||
<Row style={Theme.Styles.openDetailsContainer}>
|
||||
<Image
|
||||
source={
|
||||
props.vc?.credential.biometrics?.face
|
||||
? { uri: props.vc?.credential.biometrics.face }
|
||||
: Theme.ProfileIcon
|
||||
}
|
||||
style={Theme.Styles.openCardImage}
|
||||
/>
|
||||
|
||||
<Column style={Theme.Styles.labelPartContainer}>
|
||||
<Column fill>
|
||||
<Text
|
||||
weight="bold"
|
||||
size="smaller"
|
||||
color={Theme.Colors.DetailsLabel}>
|
||||
{props.vc?.idType}
|
||||
</Text>
|
||||
<Text weight="semibold" size="smaller" color={Theme.Colors.Details}>
|
||||
{props.vc?.id}
|
||||
</Text>
|
||||
</Column>
|
||||
|
||||
<Column fill style={Theme.Styles.labelPart}>
|
||||
<Text
|
||||
weight="bold"
|
||||
size="smaller"
|
||||
color={Theme.Colors.DetailsLabel}>
|
||||
{t('generatedOn')}
|
||||
</Text>
|
||||
<Text weight="semibold" size="smaller" color={Theme.Colors.Details}>
|
||||
{new Date(props.vc?.generatedOn).toLocaleDateString()}
|
||||
</Text>
|
||||
</Column>
|
||||
|
||||
<Column fill style={Theme.Styles.labelPart}>
|
||||
<Text
|
||||
weight="bold"
|
||||
size="smaller"
|
||||
color={Theme.Colors.DetailsLabel}>
|
||||
{t('status')}
|
||||
</Text>
|
||||
<Row>
|
||||
<Text
|
||||
weight="semibold"
|
||||
size="smaller"
|
||||
color={Theme.Colors.Details}>
|
||||
{t('valid')}
|
||||
</Text>
|
||||
{props.vc?.isVerified && <VerifiedIcon />}
|
||||
</Row>
|
||||
</Column>
|
||||
|
||||
<Column fill style={Theme.Styles.labelPart}>
|
||||
<Text
|
||||
weight="bold"
|
||||
size="smaller"
|
||||
color={Theme.Colors.DetailsLabel}>
|
||||
{t('gender')}
|
||||
</Text>
|
||||
<Text weight="semibold" size="smaller" color={Theme.Colors.Details}>
|
||||
{getLocalizedField(
|
||||
props.vc?.verifiableCredential.credentialSubject.gender
|
||||
)}
|
||||
</Text>
|
||||
</Column>
|
||||
|
||||
<Column fill style={Theme.Styles.labelPart}>
|
||||
<Text
|
||||
weight="bold"
|
||||
size="smaller"
|
||||
color={Theme.Colors.DetailsLabel}>
|
||||
{t('dateOfBirth')}
|
||||
</Text>
|
||||
<Text weight="semibold" size="smaller" color={Theme.Colors.Details}>
|
||||
{new Date(
|
||||
getLocalizedField(
|
||||
props.vc?.verifiableCredential.credentialSubject.dateOfBirth
|
||||
)
|
||||
).toLocaleDateString()}
|
||||
</Text>
|
||||
</Column>
|
||||
|
||||
<Column fill style={Theme.Styles.labelPart}>
|
||||
<Text
|
||||
weight="bold"
|
||||
size="smaller"
|
||||
color={Theme.Colors.DetailsLabel}>
|
||||
{t('phoneNumber')}
|
||||
</Text>
|
||||
<Text weight="semibold" size="smaller" color={Theme.Colors.Details}>
|
||||
{getLocalizedField(
|
||||
props.vc?.verifiableCredential.credentialSubject.phone
|
||||
)}
|
||||
</Text>
|
||||
</Column>
|
||||
|
||||
<Column fill style={Theme.Styles.labelPart}>
|
||||
<Text
|
||||
weight="bold"
|
||||
size="smaller"
|
||||
color={Theme.Colors.DetailsLabel}>
|
||||
{t('email')}
|
||||
</Text>
|
||||
<Text weight="semibold" size="smaller" color={Theme.Colors.Details}>
|
||||
{getLocalizedField(
|
||||
props.vc?.verifiableCredential.credentialSubject.email
|
||||
)}
|
||||
</Text>
|
||||
</Column>
|
||||
|
||||
<Column fill style={Theme.Styles.labelPart}>
|
||||
<Text
|
||||
weight="bold"
|
||||
size="smaller"
|
||||
color={Theme.Colors.DetailsLabel}>
|
||||
{t('address')}
|
||||
</Text>
|
||||
<Text weight="semibold" size="smaller" color={Theme.Colors.Details}>
|
||||
{getFullAddress(props.vc?.verifiableCredential.credentialSubject)}
|
||||
</Text>
|
||||
</Column>
|
||||
|
||||
{props.vc?.reason?.length > 0 && (
|
||||
<Text margin="24 24 16 24" weight="semibold">
|
||||
{t('reasonForSharing')}
|
||||
</Text>
|
||||
)}
|
||||
{props.vc?.reason?.map((reason, index) => (
|
||||
<TextItem
|
||||
key={index}
|
||||
divider
|
||||
label={formatDistanceToNow(reason.timestamp, {
|
||||
addSuffix: true,
|
||||
locale: DateFnsLocale[i18n.language],
|
||||
})}
|
||||
text={reason.message}
|
||||
/>
|
||||
))}
|
||||
</Column>
|
||||
</Row>
|
||||
</ImageBackground>
|
||||
);
|
||||
};
|
||||
|
||||
interface VcDetailsProps {
|
||||
vc: VC;
|
||||
}
|
||||
|
||||
interface LocalizedField {
|
||||
language: string;
|
||||
value: string;
|
||||
}
|
||||
|
||||
function getFullAddress(credential: CredentialSubject) {
|
||||
if (!credential) {
|
||||
return '';
|
||||
}
|
||||
|
||||
const fields = [
|
||||
'addressLine1',
|
||||
'addressLine2',
|
||||
'addressLine3',
|
||||
'city',
|
||||
'province',
|
||||
'region',
|
||||
];
|
||||
|
||||
return fields
|
||||
.map((field) => getLocalizedField(credential[field]))
|
||||
.concat(credential.postalCode)
|
||||
.filter(Boolean)
|
||||
.join(', ');
|
||||
}
|
||||
|
||||
function getLocalizedField(rawField: string | LocalizedField) {
|
||||
if (typeof rawField === 'string') {
|
||||
return rawField;
|
||||
}
|
||||
try {
|
||||
const locales: LocalizedField[] = JSON.parse(JSON.stringify(rawField));
|
||||
return locales[0].value;
|
||||
} catch (e) {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
210
components/UpdatedVcItem.tsx
Normal file
@@ -0,0 +1,210 @@
|
||||
import React, { useContext, useRef } from 'react';
|
||||
import { useInterpret, useSelector } from '@xstate/react';
|
||||
import { Pressable, Image, ImageBackground } from 'react-native';
|
||||
import { CheckBox, Icon } from 'react-native-elements';
|
||||
import { ActorRefFrom } from 'xstate';
|
||||
import {
|
||||
createVcItemMachine,
|
||||
selectVerifiableCredential,
|
||||
selectGeneratedOn,
|
||||
selectTag,
|
||||
selectId,
|
||||
vcItemMachine,
|
||||
selectContext,
|
||||
} from '../machines/vcItem';
|
||||
import { Column, Row, Text } from './ui';
|
||||
import { Theme } from './ui/styleUtils';
|
||||
import { RotatingIcon } from './RotatingIcon';
|
||||
import { GlobalContext } from '../shared/GlobalContext';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
const VerifiedIcon: React.FC = () => {
|
||||
return (
|
||||
<Icon
|
||||
name="check-circle"
|
||||
color={Theme.Colors.VerifiedIcon}
|
||||
size={14}
|
||||
containerStyle={{ marginStart: 4, bottom: 1 }}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const getDetails = (arg1, arg2, verifiableCredential) => {
|
||||
if (arg1 === 'Full Name') {
|
||||
return (
|
||||
<Column>
|
||||
<Text color={Theme.Colors.DetailsLabel} size="smaller">
|
||||
{arg1}
|
||||
</Text>
|
||||
<Text
|
||||
color={Theme.Colors.Details}
|
||||
numLines={1}
|
||||
weight="bold"
|
||||
size="smaller"
|
||||
style={
|
||||
!verifiableCredential
|
||||
? Theme.Styles.loadingTitle
|
||||
: Theme.Styles.subtitle
|
||||
}>
|
||||
{!verifiableCredential ? '' : arg2}
|
||||
</Text>
|
||||
</Column>
|
||||
);
|
||||
}
|
||||
if (arg1 === 'Status') {
|
||||
return (
|
||||
<Column>
|
||||
<Text size="smaller" color={Theme.Colors.DetailsLabel}>
|
||||
{arg1}
|
||||
</Text>
|
||||
<Row>
|
||||
<Text
|
||||
weight="bold"
|
||||
color={Theme.Colors.Details}
|
||||
size="smaller"
|
||||
style={
|
||||
!verifiableCredential
|
||||
? Theme.Styles.loadingTitle
|
||||
: Theme.Styles.subtitle
|
||||
}>
|
||||
{!verifiableCredential ? '' : arg2}
|
||||
</Text>
|
||||
{!verifiableCredential ? null : <VerifiedIcon />}
|
||||
</Row>
|
||||
</Column>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<Column>
|
||||
<Text color={Theme.Colors.DetailsLabel} size="smaller">
|
||||
{arg1}
|
||||
</Text>
|
||||
<Text
|
||||
numLines={1}
|
||||
color={Theme.Colors.Details}
|
||||
weight="bold"
|
||||
size="smaller"
|
||||
style={
|
||||
!verifiableCredential
|
||||
? Theme.Styles.loadingTitle
|
||||
: Theme.Styles.subtitle
|
||||
}>
|
||||
{!verifiableCredential ? '' : arg2}
|
||||
</Text>
|
||||
</Column>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export const UpdatedVcItem: React.FC<VcItemProps> = (props) => {
|
||||
const { appService } = useContext(GlobalContext);
|
||||
const { t } = useTranslation('VcDetails');
|
||||
const machine = useRef(
|
||||
createVcItemMachine(
|
||||
appService.getSnapshot().context.serviceRefs,
|
||||
props.vcKey
|
||||
)
|
||||
);
|
||||
|
||||
const service = useInterpret(machine.current);
|
||||
const context = useSelector(service, selectContext);
|
||||
const verifiableCredential = useSelector(service, selectVerifiableCredential);
|
||||
const uin = useSelector(service, selectId);
|
||||
const tag = useSelector(service, selectTag);
|
||||
const generatedOn = useSelector(service, selectGeneratedOn);
|
||||
const fullName = !verifiableCredential
|
||||
? ''
|
||||
: getLocalizedField(verifiableCredential.credentialSubject.fullName);
|
||||
|
||||
const selectableOrCheck = props.selectable ? (
|
||||
<CheckBox
|
||||
checked={props.selected}
|
||||
checkedIcon={<Icon name="radio-button-checked" />}
|
||||
uncheckedIcon={<Icon name="radio-button-unchecked" />}
|
||||
onPress={() => props.onPress(service)}
|
||||
/>
|
||||
) : null;
|
||||
|
||||
return (
|
||||
<Pressable
|
||||
onPress={() => props.onPress(service)}
|
||||
disabled={!verifiableCredential}
|
||||
style={Theme.Styles.closeCardBgContainer}>
|
||||
<ImageBackground
|
||||
source={!verifiableCredential ? null : Theme.CloseCard}
|
||||
resizeMode="stretch"
|
||||
style={
|
||||
!verifiableCredential
|
||||
? Theme.Styles.vertloadingContainer
|
||||
: Theme.Styles.backgroundImageContainer
|
||||
}>
|
||||
<Row style={Theme.Styles.homeCloseCardDetailsHeader}>
|
||||
<Image
|
||||
source={Theme.MosipLogo}
|
||||
style={Theme.Styles.logo}
|
||||
resizeMethod="auto"
|
||||
/>
|
||||
</Row>
|
||||
<Row
|
||||
crossAlign="center"
|
||||
margin="5 0 0 0"
|
||||
style={!verifiableCredential ? Theme.Styles.loadingContainer : null}>
|
||||
<Column
|
||||
style={
|
||||
!verifiableCredential
|
||||
? Theme.Styles.loadingContainer
|
||||
: Theme.Styles.closeDetails
|
||||
}>
|
||||
<Image
|
||||
source={
|
||||
!verifiableCredential
|
||||
? Theme.ProfileIcon
|
||||
: { uri: context.credential.biometrics.face }
|
||||
}
|
||||
style={Theme.Styles.closeCardImage}
|
||||
/>
|
||||
|
||||
<Column margin="0 0 0 10">
|
||||
{getDetails(t('fullName'), fullName, verifiableCredential)}
|
||||
{getDetails(t('uin'), tag || uin, verifiableCredential)}
|
||||
{getDetails(t('generatedOn'), generatedOn, verifiableCredential)}
|
||||
{getDetails(t('status'), t('valid'), verifiableCredential)}
|
||||
</Column>
|
||||
</Column>
|
||||
|
||||
{verifiableCredential ? (
|
||||
selectableOrCheck
|
||||
) : (
|
||||
<RotatingIcon name="sync" color={Theme.Colors.rotatingIcon} />
|
||||
)}
|
||||
</Row>
|
||||
</ImageBackground>
|
||||
</Pressable>
|
||||
);
|
||||
};
|
||||
|
||||
interface VcItemProps {
|
||||
vcKey: string;
|
||||
margin?: string;
|
||||
selectable?: boolean;
|
||||
selected?: boolean;
|
||||
onPress?: (vcRef?: ActorRefFrom<typeof vcItemMachine>) => void;
|
||||
onShow?: (vcRef?: ActorRefFrom<typeof vcItemMachine>) => void;
|
||||
}
|
||||
|
||||
interface LocalizedField {
|
||||
language: string;
|
||||
value: string;
|
||||
}
|
||||
|
||||
function getLocalizedField(rawField: string | LocalizedField) {
|
||||
if (typeof rawField === 'string') {
|
||||
return rawField;
|
||||
}
|
||||
try {
|
||||
const locales: LocalizedField[] = JSON.parse(JSON.stringify(rawField));
|
||||
return locales[0].value;
|
||||
} catch (e) {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
@@ -6,14 +6,14 @@ import { Image } from 'react-native';
|
||||
import { Icon, ListItem } from 'react-native-elements';
|
||||
import { VC, CredentialSubject } from '../types/vc';
|
||||
import { Column, Row, Text } from './ui';
|
||||
import { Colors } from './ui/styleUtils';
|
||||
import { Theme } from './ui/styleUtils';
|
||||
import { TextItem } from './ui/TextItem';
|
||||
|
||||
const VerifiedIcon: React.FC = () => {
|
||||
return (
|
||||
<Icon
|
||||
name="check-circle"
|
||||
color={Colors.Green}
|
||||
color={Theme.Colors.VerifiedIcon}
|
||||
size={14}
|
||||
containerStyle={{ marginStart: 4, bottom: 1 }}
|
||||
/>
|
||||
@@ -27,7 +27,7 @@ export const VcDetails: React.FC<VcDetailsProps> = (props) => {
|
||||
<Column>
|
||||
<Row pY={16} pX={8} align="space-between">
|
||||
<Column fill elevation={1} pY={12} pX={16} margin="0 8">
|
||||
<Text size="smaller" color={Colors.Grey}>
|
||||
<Text size="smaller" color={Theme.Colors.textLabel}>
|
||||
{t('generatedOn')}
|
||||
</Text>
|
||||
<Text weight="bold" size="smaller">
|
||||
@@ -37,7 +37,7 @@ export const VcDetails: React.FC<VcDetailsProps> = (props) => {
|
||||
<Column fill elevation={1} pY={12} pX={16} margin="0 8">
|
||||
<Text
|
||||
size="smaller"
|
||||
color={Colors.Grey}
|
||||
color={Theme.Colors.textLabel}
|
||||
style={{ textTransform: 'uppercase' }}>
|
||||
{props.vc?.idType}
|
||||
</Text>
|
||||
@@ -46,7 +46,7 @@ export const VcDetails: React.FC<VcDetailsProps> = (props) => {
|
||||
</Text>
|
||||
</Column>
|
||||
<Column fill elevation={1} pY={12} pX={16} margin="0 8">
|
||||
<Text size="smaller" color={Colors.Grey}>
|
||||
<Text size="smaller" color={Theme.Colors.textLabel}>
|
||||
{t('status')}
|
||||
</Text>
|
||||
<Row>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React, { useContext, useRef } from 'react';
|
||||
import { useInterpret, useSelector } from '@xstate/react';
|
||||
import { Pressable, StyleSheet } from 'react-native';
|
||||
import { Pressable } from 'react-native';
|
||||
import { CheckBox, Icon } from 'react-native-elements';
|
||||
import { ActorRefFrom } from 'xstate';
|
||||
import {
|
||||
@@ -12,36 +12,10 @@ import {
|
||||
vcItemMachine,
|
||||
} from '../machines/vcItem';
|
||||
import { Column, Row, Text } from './ui';
|
||||
import { Colors } from './ui/styleUtils';
|
||||
import { Theme } from './ui/styleUtils';
|
||||
import { RotatingIcon } from './RotatingIcon';
|
||||
import { GlobalContext } from '../shared/GlobalContext';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
title: {
|
||||
color: Colors.Black,
|
||||
backgroundColor: 'transparent',
|
||||
},
|
||||
loadingTitle: {
|
||||
color: 'transparent',
|
||||
backgroundColor: Colors.Grey5,
|
||||
borderRadius: 4,
|
||||
},
|
||||
subtitle: {
|
||||
backgroundColor: 'transparent',
|
||||
},
|
||||
loadingSubtitle: {
|
||||
backgroundColor: Colors.Grey,
|
||||
borderRadius: 4,
|
||||
},
|
||||
container: {
|
||||
backgroundColor: Colors.White,
|
||||
},
|
||||
loadingContainer: {
|
||||
backgroundColor: Colors.Grey6,
|
||||
borderRadius: 4,
|
||||
},
|
||||
});
|
||||
|
||||
export const VcItem: React.FC<VcItemProps> = (props) => {
|
||||
const { appService } = useContext(GlobalContext);
|
||||
const machine = useRef(
|
||||
@@ -75,15 +49,25 @@ export const VcItem: React.FC<VcItemProps> = (props) => {
|
||||
elevation={!verifiableCredential ? 0 : 2}
|
||||
crossAlign="center"
|
||||
margin={props.margin}
|
||||
backgroundColor={!verifiableCredential ? Colors.Grey6 : Colors.White}
|
||||
backgroundColor={
|
||||
!verifiableCredential
|
||||
? Theme.Colors.loadingLabel
|
||||
: Theme.Colors.whiteBackgroundColor
|
||||
}
|
||||
padding={[16, 16]}
|
||||
style={
|
||||
!verifiableCredential ? styles.loadingContainer : styles.container
|
||||
!verifiableCredential
|
||||
? Theme.VcItemStyles.loadingContainer
|
||||
: Theme.VcItemStyles.container
|
||||
}>
|
||||
<Column fill margin="0 24 0 0">
|
||||
<Text
|
||||
weight="semibold"
|
||||
style={!verifiableCredential ? styles.loadingTitle : styles.title}
|
||||
style={
|
||||
!verifiableCredential
|
||||
? Theme.VcItemStyles.loadingTitle
|
||||
: Theme.VcItemStyles.title
|
||||
}
|
||||
margin="0 0 6 0">
|
||||
{!verifiableCredential ? '' : tag || uin}
|
||||
</Text>
|
||||
@@ -91,21 +75,25 @@ export const VcItem: React.FC<VcItemProps> = (props) => {
|
||||
size="smaller"
|
||||
numLines={1}
|
||||
style={
|
||||
!verifiableCredential ? styles.loadingSubtitle : styles.subtitle
|
||||
!verifiableCredential
|
||||
? Theme.VcItemStyles.loadingSubtitle
|
||||
: Theme.VcItemStyles.subtitle
|
||||
}>
|
||||
{!verifiableCredential
|
||||
? ''
|
||||
: getLocalizedField(
|
||||
verifiableCredential.credentialSubject.fullName
|
||||
verifiableCredential.verifiableCredential.credentialSubject
|
||||
.fullName
|
||||
) +
|
||||
' · ' +
|
||||
generatedOn}
|
||||
</Text>
|
||||
</Column>
|
||||
|
||||
{verifiableCredential ? (
|
||||
selectableOrCheck
|
||||
) : (
|
||||
<RotatingIcon name="sync" color={Colors.Grey5} />
|
||||
<RotatingIcon name="sync" color={Theme.Colors.rotatingIcon} />
|
||||
)}
|
||||
</Row>
|
||||
</Pressable>
|
||||
|
||||
BIN
components/ui/.Modal.tsx.swp
Normal file
BIN
components/ui/.Modal_BASE_1602.tsx.swp
Normal file
BIN
components/ui/.Modal_LOCAL_1602.tsx.swp
Normal file
BIN
components/ui/.Modal_REMOTE_1602.tsx.swp
Normal file
@@ -3,46 +3,21 @@ import {
|
||||
Button as RNEButton,
|
||||
ButtonProps as RNEButtonProps,
|
||||
} from 'react-native-elements';
|
||||
import {
|
||||
GestureResponderEvent,
|
||||
StyleProp,
|
||||
StyleSheet,
|
||||
ViewStyle,
|
||||
} from 'react-native';
|
||||
import { GestureResponderEvent, StyleProp, ViewStyle } from 'react-native';
|
||||
import { Text } from './Text';
|
||||
import { Colors, Spacing, spacing } from './styleUtils';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
fill: {
|
||||
flex: 1,
|
||||
},
|
||||
solid: {
|
||||
backgroundColor: Colors.Orange,
|
||||
},
|
||||
clear: {
|
||||
backgroundColor: 'transparent',
|
||||
},
|
||||
outline: {
|
||||
backgroundColor: 'transparent',
|
||||
borderColor: Colors.Orange,
|
||||
},
|
||||
container: {
|
||||
minHeight: 48,
|
||||
flexDirection: 'row',
|
||||
},
|
||||
disabled: {
|
||||
opacity: 0.5,
|
||||
},
|
||||
});
|
||||
import { Theme } from './styleUtils';
|
||||
|
||||
export const Button: React.FC<ButtonProps> = (props) => {
|
||||
const type = props.type || 'solid';
|
||||
const buttonStyle: StyleProp<ViewStyle> = [styles.fill, styles[type]];
|
||||
const buttonStyle: StyleProp<ViewStyle> = [
|
||||
Theme.ButtonStyles.fill,
|
||||
Theme.ButtonStyles[type],
|
||||
];
|
||||
|
||||
const containerStyle: StyleProp<ViewStyle> = [
|
||||
styles.container,
|
||||
props.disabled ? styles.disabled : null,
|
||||
props.margin ? spacing('margin', props.margin) : null,
|
||||
Theme.ButtonStyles.container,
|
||||
props.disabled ? Theme.ButtonStyles.disabled : null,
|
||||
props.margin ? Theme.spacing('margin', props.margin) : null,
|
||||
];
|
||||
|
||||
const handleOnPress = (event: GestureResponderEvent) => {
|
||||
@@ -54,14 +29,21 @@ export const Button: React.FC<ButtonProps> = (props) => {
|
||||
return (
|
||||
<RNEButton
|
||||
buttonStyle={buttonStyle}
|
||||
containerStyle={[props.fill ? styles.fill : null, containerStyle]}
|
||||
containerStyle={[
|
||||
props.fill ? Theme.ButtonStyles.fill : null,
|
||||
containerStyle,
|
||||
]}
|
||||
type={props.type}
|
||||
raised={props.raised}
|
||||
title={
|
||||
<Text
|
||||
weight="semibold"
|
||||
align="center"
|
||||
color={type === 'solid' ? Colors.White : Colors.Orange}>
|
||||
color={
|
||||
type === 'solid' || type === 'addId'
|
||||
? Theme.Colors.whiteText
|
||||
: Theme.Colors.AddIdBtnTxt
|
||||
}>
|
||||
{props.title}
|
||||
</Text>
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ import {
|
||||
RefreshControlProps,
|
||||
SafeAreaView,
|
||||
} from 'react-native';
|
||||
import { elevation, ElevationLevel, Spacing, spacing } from './styleUtils';
|
||||
import { Theme, ElevationLevel, Spacing } from './styleUtils';
|
||||
|
||||
function createLayout(
|
||||
direction: FlexStyle['flexDirection'],
|
||||
@@ -31,14 +31,14 @@ function createLayout(
|
||||
const styles: StyleProp<ViewStyle> = [
|
||||
layoutStyles.base,
|
||||
props.fill ? layoutStyles.fill : null,
|
||||
props.padding ? spacing('padding', props.padding) : null,
|
||||
props.margin ? spacing('margin', props.margin) : null,
|
||||
props.padding ? Theme.spacing('padding', props.padding) : null,
|
||||
props.margin ? Theme.spacing('margin', props.margin) : null,
|
||||
props.backgroundColor ? { backgroundColor: props.backgroundColor } : null,
|
||||
props.width ? { width: props.width } : null,
|
||||
props.height ? { height: props.height } : null,
|
||||
props.align ? { justifyContent: props.align } : null,
|
||||
props.crossAlign ? { alignItems: props.crossAlign } : null,
|
||||
props.elevation ? elevation(props.elevation) : null,
|
||||
props.elevation ? Theme.elevation(props.elevation) : null,
|
||||
props.style ? props.style : null,
|
||||
props.pY ? { paddingVertical: props.pY } : null,
|
||||
props.pX ? { paddingHorizontal: props.pX } : null,
|
||||
|
||||
@@ -2,7 +2,7 @@ import React from 'react';
|
||||
import { Dimensions, Modal as RNModal, StyleSheet, View } from 'react-native';
|
||||
import { Icon } from 'react-native-elements';
|
||||
import { Column, Row, Text } from '.';
|
||||
import { Colors, ElevationLevel } from './styleUtils';
|
||||
import { ElevationLevel, Theme } from './styleUtils';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
modal: {
|
||||
@@ -17,6 +17,7 @@ export const Modal: React.FC<ModalProps> = (props) => {
|
||||
animationType="slide"
|
||||
style={styles.modal}
|
||||
visible={props.isVisible}
|
||||
onShow={props.onShow}
|
||||
onRequestClose={props.onDismiss}>
|
||||
<Column fill safe>
|
||||
<Row elevation={props.headerElevation}>
|
||||
@@ -31,7 +32,7 @@ export const Modal: React.FC<ModalProps> = (props) => {
|
||||
<Icon
|
||||
name="chevron-left"
|
||||
onPress={props.onDismiss}
|
||||
color={Colors.Orange}
|
||||
color={Theme.Colors.Icon}
|
||||
/>
|
||||
) : null}
|
||||
<Row fill align="center">
|
||||
@@ -41,7 +42,7 @@ export const Modal: React.FC<ModalProps> = (props) => {
|
||||
<Icon
|
||||
name="close"
|
||||
onPress={props.onDismiss}
|
||||
color={Colors.Orange}
|
||||
color={Theme.Colors.Icon}
|
||||
/>
|
||||
)}
|
||||
</View>
|
||||
@@ -58,4 +59,5 @@ export interface ModalProps {
|
||||
headerTitle?: string;
|
||||
headerElevation?: ElevationLevel;
|
||||
headerRight?: React.ReactElement;
|
||||
onShow?: () => void;
|
||||
}
|
||||
|
||||
70
components/ui/Modal_BACKUP_1602.tsx
Normal file
@@ -0,0 +1,70 @@
|
||||
import React from 'react';
|
||||
import { Dimensions, Modal as RNModal, StyleSheet, View } from 'react-native';
|
||||
import { Icon } from 'react-native-elements';
|
||||
import { Column, Row, Text } from '.';
|
||||
import { Theme, ElevationLevel } from './styleUtils';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
modal: {
|
||||
width: Dimensions.get('screen').width,
|
||||
height: Dimensions.get('screen').height,
|
||||
},
|
||||
});
|
||||
|
||||
export const Modal: React.FC<ModalProps> = (props) => {
|
||||
return (
|
||||
<RNModal
|
||||
animationType="slide"
|
||||
style={styles.modal}
|
||||
visible={props.isVisible}
|
||||
onShow={props.onShow}
|
||||
onRequestClose={props.onDismiss}>
|
||||
<Column fill>
|
||||
<Row elevation={props.headerElevation}>
|
||||
<View style={{ marginHorizontal: 16, marginVertical: 16 }}>
|
||||
{props.headerRight ? (
|
||||
<Icon
|
||||
name="chevron-left"
|
||||
onPress={props.onDismiss}
|
||||
color={Theme.Colors.Icon}
|
||||
/>
|
||||
) : null}
|
||||
<Row fill align="center">
|
||||
<Text weight="semibold">{props.headerTitle}</Text>
|
||||
</Row>
|
||||
{props.headerRight || (
|
||||
<Icon
|
||||
name="close"
|
||||
onPress={props.onDismiss}
|
||||
color={Theme.Colors.Icon}
|
||||
/>
|
||||
)}
|
||||
</View>
|
||||
</Row>
|
||||
<Row padding="16 32" elevation={props.headerElevation}>
|
||||
{props.headerRight ? (
|
||||
<Icon
|
||||
name="chevron-left"
|
||||
onPress={props.onDismiss}
|
||||
color={Theme.Colors.Icon}
|
||||
/>
|
||||
) : null}
|
||||
|
||||
<Row fill align="center">
|
||||
<Text weight="semibold">{props.headerTitle}</Text>
|
||||
</Row>
|
||||
</Row>
|
||||
{props.children}
|
||||
</Column>
|
||||
</RNModal>
|
||||
);
|
||||
};
|
||||
|
||||
export interface ModalProps {
|
||||
isVisible: boolean;
|
||||
onDismiss: () => void;
|
||||
headerTitle?: string;
|
||||
headerElevation?: ElevationLevel;
|
||||
headerRight?: React.ReactElement;
|
||||
onShow?: () => void;
|
||||
}
|
||||
53
components/ui/Modal_BASE_1602.tsx
Normal file
@@ -0,0 +1,53 @@
|
||||
import React from 'react';
|
||||
import { Dimensions, Modal as RNModal, StyleSheet } from 'react-native';
|
||||
import { Icon } from 'react-native-elements';
|
||||
import { Column, Row, Text } from '.';
|
||||
import { Theme, ElevationLevel } from './styleUtils';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
modal: {
|
||||
width: Dimensions.get('screen').width,
|
||||
height: Dimensions.get('screen').height,
|
||||
},
|
||||
});
|
||||
|
||||
export const Modal: React.FC<ModalProps> = (props) => {
|
||||
return (
|
||||
<RNModal
|
||||
animationType="slide"
|
||||
style={styles.modal}
|
||||
visible={props.isVisible}
|
||||
onRequestClose={props.onDismiss}>
|
||||
<Column fill>
|
||||
<Row padding="16 32" elevation={props.headerElevation}>
|
||||
{props.headerRight ? (
|
||||
<Icon
|
||||
name="chevron-left"
|
||||
onPress={props.onDismiss}
|
||||
color={Theme.Colors.Icon}
|
||||
/>
|
||||
) : null}
|
||||
<Row fill align="center">
|
||||
<Text weight="semibold">{props.headerTitle}</Text>
|
||||
</Row>
|
||||
{props.headerRight || (
|
||||
<Icon
|
||||
name="close"
|
||||
onPress={props.onDismiss}
|
||||
color={Theme.Colors.Icon}
|
||||
/>
|
||||
)}
|
||||
</Row>
|
||||
{props.children}
|
||||
</Column>
|
||||
</RNModal>
|
||||
);
|
||||
};
|
||||
|
||||
export interface ModalProps {
|
||||
isVisible: boolean;
|
||||
onDismiss: () => void;
|
||||
headerTitle?: string;
|
||||
headerElevation?: ElevationLevel;
|
||||
headerRight?: React.ReactElement;
|
||||
}
|
||||
61
components/ui/Modal_LOCAL_1602.tsx
Normal file
@@ -0,0 +1,61 @@
|
||||
import React from 'react';
|
||||
import { Dimensions, Modal as RNModal, StyleSheet, View } from 'react-native';
|
||||
import { Icon } from 'react-native-elements';
|
||||
import { Column, Row, Text } from '.';
|
||||
import { Theme, ElevationLevel } from './styleUtils';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
modal: {
|
||||
width: Dimensions.get('screen').width,
|
||||
height: Dimensions.get('screen').height,
|
||||
},
|
||||
});
|
||||
|
||||
export const Modal: React.FC<ModalProps> = (props) => {
|
||||
return (
|
||||
<RNModal
|
||||
animationType="slide"
|
||||
style={styles.modal}
|
||||
visible={props.isVisible}
|
||||
onRequestClose={props.onDismiss}>
|
||||
<Column fill>
|
||||
<Row elevation={props.headerElevation}>
|
||||
<View
|
||||
style={{
|
||||
flex: 1,
|
||||
flexDirection: 'row',
|
||||
marginHorizontal: 16,
|
||||
marginVertical: 16,
|
||||
}}>
|
||||
{props.headerRight ? (
|
||||
<Icon
|
||||
name="chevron-left"
|
||||
onPress={props.onDismiss}
|
||||
color={Theme.Colors.Icon}
|
||||
/>
|
||||
) : null}
|
||||
<Row fill align="center">
|
||||
<Text weight="semibold">{props.headerTitle}</Text>
|
||||
</Row>
|
||||
{props.headerRight || (
|
||||
<Icon
|
||||
name="close"
|
||||
onPress={props.onDismiss}
|
||||
color={Theme.Colors.Icon}
|
||||
/>
|
||||
)}
|
||||
</View>
|
||||
</Row>
|
||||
{props.children}
|
||||
</Column>
|
||||
</RNModal>
|
||||
);
|
||||
};
|
||||
|
||||
export interface ModalProps {
|
||||
isVisible: boolean;
|
||||
onDismiss: () => void;
|
||||
headerTitle?: string;
|
||||
headerElevation?: ElevationLevel;
|
||||
headerRight?: React.ReactElement;
|
||||
}
|
||||
49
components/ui/Modal_REMOTE_1602.tsx
Normal file
@@ -0,0 +1,49 @@
|
||||
import React from 'react';
|
||||
import { Dimensions, Modal as RNModal, StyleSheet } from 'react-native';
|
||||
import { Icon } from 'react-native-elements';
|
||||
import { Column, Row, Text } from '.';
|
||||
import { Theme, ElevationLevel } from './styleUtils';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
modal: {
|
||||
width: Dimensions.get('screen').width,
|
||||
height: Dimensions.get('screen').height,
|
||||
},
|
||||
});
|
||||
|
||||
export const Modal: React.FC<ModalProps> = (props) => {
|
||||
return (
|
||||
<RNModal
|
||||
animationType="slide"
|
||||
style={styles.modal}
|
||||
visible={props.isVisible}
|
||||
onShow={props.onShow}
|
||||
onRequestClose={props.onDismiss}>
|
||||
<Column fill>
|
||||
<Row padding="16 32" elevation={props.headerElevation}>
|
||||
{props.headerRight ? (
|
||||
<Icon
|
||||
name="chevron-left"
|
||||
onPress={props.onDismiss}
|
||||
color={Theme.Colors.Icon}
|
||||
/>
|
||||
) : null}
|
||||
|
||||
<Row fill align="center">
|
||||
<Text weight="semibold">{props.headerTitle}</Text>
|
||||
</Row>
|
||||
</Row>
|
||||
{props.children}
|
||||
</Column>
|
||||
</RNModal>
|
||||
);
|
||||
};
|
||||
|
||||
export interface ModalProps {
|
||||
isVisible: boolean;
|
||||
onDismiss: () => void;
|
||||
headerTitle?: string;
|
||||
headerElevation?: ElevationLevel;
|
||||
headerRight?: React.ReactElement;
|
||||
onShow?: () => void;
|
||||
}
|
||||
@@ -1,42 +1,17 @@
|
||||
import React from 'react';
|
||||
import { StyleProp, TextStyle, StyleSheet, Text as RNText } from 'react-native';
|
||||
import { Colors, Spacing, spacing } from './styleUtils';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
base: {
|
||||
color: Colors.Black,
|
||||
fontSize: 18,
|
||||
lineHeight: 28,
|
||||
},
|
||||
regular: {
|
||||
fontFamily: 'Poppins_400Regular',
|
||||
},
|
||||
semibold: {
|
||||
fontFamily: 'Poppins_600SemiBold',
|
||||
},
|
||||
bold: {
|
||||
fontFamily: 'Poppins_700Bold',
|
||||
},
|
||||
small: {
|
||||
fontSize: 14,
|
||||
lineHeight: 21,
|
||||
},
|
||||
smaller: {
|
||||
fontSize: 12,
|
||||
lineHeight: 18,
|
||||
},
|
||||
});
|
||||
import { StyleProp, TextStyle, Text as RNText } from 'react-native';
|
||||
import { Theme, Spacing } from './styleUtils';
|
||||
|
||||
export const Text: React.FC<TextProps> = (props: TextProps) => {
|
||||
const weight = props.weight || 'regular';
|
||||
|
||||
const textStyles: StyleProp<TextStyle> = [
|
||||
styles.base,
|
||||
styles[weight],
|
||||
Theme.TextStyles.base,
|
||||
Theme.TextStyles[weight],
|
||||
props.color ? { color: props.color } : null,
|
||||
props.align ? { textAlign: props.align } : null,
|
||||
props.margin ? spacing('margin', props.margin) : null,
|
||||
props.size ? styles[props.size] : null,
|
||||
props.margin ? Theme.spacing('margin', props.margin) : null,
|
||||
props.size ? Theme.TextStyles[props.size] : null,
|
||||
props.style ? props.style : null,
|
||||
];
|
||||
|
||||
|
||||
@@ -1,24 +1,26 @@
|
||||
import React from 'react';
|
||||
import { Column, Text } from '.';
|
||||
import { Colors } from './styleUtils';
|
||||
import { Theme } from './styleUtils';
|
||||
|
||||
export const TextItem: React.FC<TextItemProps> = (props) => {
|
||||
return (
|
||||
<Column
|
||||
backgroundColor={Colors.White}
|
||||
backgroundColor={Theme.Colors.whiteBackgroundColor}
|
||||
margin={props.margin}
|
||||
pX={24}
|
||||
pY={props.label ? 16 : 12}
|
||||
style={{
|
||||
borderBottomColor: Colors.Grey6,
|
||||
borderBottomColor: Theme.Colors.borderBottomColor,
|
||||
borderBottomWidth: props.divider ? 1 : 0,
|
||||
}}>
|
||||
{props.label && (
|
||||
<Text size="smaller" color={Colors.Grey} weight="semibold">
|
||||
<Text size="smaller" color={Theme.Colors.textLabel} weight="semibold">
|
||||
{props.label}
|
||||
</Text>
|
||||
)}
|
||||
<Text color={Colors.Black} weight={props.label ? 'semibold' : 'regular'}>
|
||||
<Text
|
||||
color={Theme.Colors.textValue}
|
||||
weight={props.label ? 'semibold' : 'regular'}>
|
||||
{props.text}
|
||||
</Text>
|
||||
</Column>
|
||||
|
||||
@@ -1,30 +1,16 @@
|
||||
import React from 'react';
|
||||
import { StyleSheet } from 'react-native';
|
||||
import { View } from 'react-native';
|
||||
import { Text } from './Text';
|
||||
import { Colors } from './styleUtils';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
toastContainer: {
|
||||
backgroundColor: Colors.Orange,
|
||||
position: 'absolute',
|
||||
alignSelf: 'center',
|
||||
top: 80,
|
||||
borderRadius: 4,
|
||||
},
|
||||
messageContainer: {
|
||||
fontSize: 12,
|
||||
},
|
||||
});
|
||||
import { Theme } from './styleUtils';
|
||||
|
||||
export const ToastItem: React.FC<ToastProps> = (props) => {
|
||||
return (
|
||||
<View style={styles.toastContainer}>
|
||||
<View style={Theme.ToastItemStyles.toastContainer}>
|
||||
<Text
|
||||
align="center"
|
||||
margin="8 16"
|
||||
color={Colors.White}
|
||||
style={styles.messageContainer}>
|
||||
color={Theme.Colors.ToastItemText}
|
||||
style={Theme.ToastItemStyles.messageContainer}>
|
||||
{props.message}
|
||||
</Text>
|
||||
</View>
|
||||
|
||||
43
components/ui/UpdatedModal.tsx
Normal file
@@ -0,0 +1,43 @@
|
||||
import React from 'react';
|
||||
import { Dimensions, Modal as RNModal, StyleSheet } from 'react-native';
|
||||
import { Icon } from 'react-native-elements';
|
||||
import { Column, Row } from '.';
|
||||
import { Theme, ElevationLevel } from './styleUtils';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
modal: {
|
||||
width: Dimensions.get('screen').width,
|
||||
height: Dimensions.get('screen').height,
|
||||
},
|
||||
});
|
||||
|
||||
export const Modal: React.FC<ModalProps> = (props) => {
|
||||
return (
|
||||
<RNModal
|
||||
animationType="slide"
|
||||
style={styles.modal}
|
||||
visible={props.isVisible}
|
||||
onRequestClose={props.onDismiss}>
|
||||
<Column fill>
|
||||
<Row padding="16 32" elevation={props.headerElevation}>
|
||||
{props.headerRight ? (
|
||||
<Icon
|
||||
name="chevron-left"
|
||||
onPress={props.onDismiss}
|
||||
color={Theme.Colors.Icon}
|
||||
/>
|
||||
) : null}
|
||||
</Row>
|
||||
{props.children}
|
||||
</Column>
|
||||
</RNModal>
|
||||
);
|
||||
};
|
||||
|
||||
export interface ModalProps {
|
||||
isVisible: boolean;
|
||||
onDismiss: () => void;
|
||||
headerTitle?: string;
|
||||
headerElevation?: ElevationLevel;
|
||||
headerRight?: React.ReactElement;
|
||||
}
|
||||
@@ -1,62 +1,12 @@
|
||||
import { ViewStyle } from 'react-native';
|
||||
import { DefaultTheme } from './themes/DefaultTheme';
|
||||
import { PurpleTheme } from './themes/PurpleTheme';
|
||||
|
||||
export const Colors = {
|
||||
Black: '#231F20',
|
||||
Grey: '#B0B0B0',
|
||||
Grey5: '#E0E0E0',
|
||||
Grey6: '#F2F2F2',
|
||||
Orange: '#F2811D',
|
||||
LightGrey: '#FAF9FF',
|
||||
White: '#FFFFFF',
|
||||
Red: '#EB5757',
|
||||
Green: '#219653',
|
||||
};
|
||||
// To change the theme, CSS theme file has to import and assign it to Theme in line no 6
|
||||
|
||||
export const Theme = DefaultTheme;
|
||||
|
||||
type SpacingXY = [number, number];
|
||||
type SpacingFull = [number, number, number, number];
|
||||
export type Spacing = SpacingXY | SpacingFull | string;
|
||||
|
||||
export function spacing(type: 'margin' | 'padding', values: Spacing) {
|
||||
if (Array.isArray(values) && values.length === 2) {
|
||||
return {
|
||||
[`${type}Horizontal`]: values[0],
|
||||
[`${type}Vertical`]: values[1],
|
||||
};
|
||||
}
|
||||
|
||||
const [top, end, bottom, start] =
|
||||
typeof values === 'string' ? values.split(' ').map(Number) : values;
|
||||
|
||||
return {
|
||||
[`${type}Top`]: top,
|
||||
[`${type}End`]: end != null ? end : top,
|
||||
[`${type}Bottom`]: bottom != null ? bottom : top,
|
||||
[`${type}Start`]: start != null ? start : end != null ? end : top,
|
||||
};
|
||||
}
|
||||
|
||||
export type ElevationLevel = 0 | 1 | 2 | 3 | 4 | 5;
|
||||
|
||||
export function elevation(level: ElevationLevel): ViewStyle {
|
||||
// https://ethercreative.github.io/react-native-shadow-generator/
|
||||
|
||||
if (level === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const index = level - 1;
|
||||
|
||||
return {
|
||||
shadowColor: Colors.Black,
|
||||
shadowOffset: {
|
||||
width: 0,
|
||||
height: [1, 1, 1, 2, 2][index],
|
||||
},
|
||||
shadowOpacity: [0.18, 0.2, 0.22, 0.23, 0.25][index],
|
||||
shadowRadius: [1.0, 1.41, 2.22, 2.62, 3.84][index],
|
||||
elevation: level,
|
||||
zIndex: level,
|
||||
borderRadius: 4,
|
||||
backgroundColor: Colors.White,
|
||||
};
|
||||
}
|
||||
|
||||
413
components/ui/themes/DefaultTheme.ts
Normal file
@@ -0,0 +1,413 @@
|
||||
import { StyleSheet, ViewStyle } from 'react-native';
|
||||
import { Spacing } from '../styleUtils';
|
||||
|
||||
const Colors = {
|
||||
Black: '#231F20',
|
||||
Grey: '#B0B0B0',
|
||||
Grey5: '#E0E0E0',
|
||||
Grey6: '#F2F2F2',
|
||||
Orange: '#F2811D',
|
||||
LightGrey: '#FAF9FF',
|
||||
White: '#FFFFFF',
|
||||
Red: '#EB5757',
|
||||
Green: '#219653',
|
||||
Transparent: 'transparent',
|
||||
};
|
||||
|
||||
export type ElevationLevel = 0 | 1 | 2 | 3 | 4 | 5;
|
||||
|
||||
export const DefaultTheme = {
|
||||
Colors: {
|
||||
TabItemText: Colors.Orange,
|
||||
Details: Colors.Black,
|
||||
DetailsLabel: Colors.Orange,
|
||||
AddIdBtnBg: Colors.Orange,
|
||||
AddIdBtnTxt: Colors.Orange,
|
||||
ClearAddIdBtnBg: Colors.Transparent,
|
||||
Loading: Colors.Orange,
|
||||
noUinText: Colors.Orange,
|
||||
IconBg: Colors.Orange,
|
||||
Icon: Colors.Orange,
|
||||
borderBottomColor: Colors.Grey6,
|
||||
whiteBackgroundColor: Colors.White,
|
||||
lightGreyBackgroundColor: Colors.LightGrey,
|
||||
profileLanguageValue: Colors.Grey,
|
||||
profileVersion: Colors.Grey,
|
||||
profileAuthFactorUnlock: Colors.Grey,
|
||||
profileLabel: Colors.Black,
|
||||
profileValue: Colors.Grey,
|
||||
overlayBackgroundColor: Colors.White,
|
||||
rotatingIcon: Colors.Grey5,
|
||||
loadingLabel: Colors.Grey6,
|
||||
textLabel: Colors.Grey,
|
||||
textValue: Colors.Black,
|
||||
errorMessage: Colors.Red,
|
||||
QRCodeBackgroundColor: Colors.LightGrey,
|
||||
ReceiveVcModalBackgroundColor: Colors.LightGrey,
|
||||
ToastItemText: Colors.White,
|
||||
VerifiedIcon: Colors.Green,
|
||||
whiteText: Colors.White,
|
||||
flipCameraIcon: Colors.Black,
|
||||
IdInputModalBorder: Colors.Grey,
|
||||
inputSelection: Colors.Orange,
|
||||
checkCircleIcon: Colors.White,
|
||||
OnboardingCircleIcon: Colors.White,
|
||||
OnboardingCloseIcon: Colors.White,
|
||||
},
|
||||
Styles: StyleSheet.create({
|
||||
title: {
|
||||
color: Colors.Black,
|
||||
backgroundColor: Colors.Transparent,
|
||||
},
|
||||
loadingTitle: {
|
||||
color: Colors.Transparent,
|
||||
backgroundColor: Colors.Grey,
|
||||
borderRadius: 4,
|
||||
},
|
||||
subtitle: {
|
||||
backgroundColor: Colors.Transparent,
|
||||
},
|
||||
loadingSubtitle: {
|
||||
backgroundColor: Colors.Grey,
|
||||
borderRadius: 4,
|
||||
},
|
||||
closeDetails: {
|
||||
flex: 1,
|
||||
flexDirection: 'row',
|
||||
paddingRight: 90,
|
||||
},
|
||||
loadingContainer: {
|
||||
flex: 1,
|
||||
flexDirection: 'row',
|
||||
backgroundColor: Colors.Grey6,
|
||||
borderRadius: 4,
|
||||
},
|
||||
vertloadingContainer: {
|
||||
flex: 1,
|
||||
backgroundColor: Colors.Grey6,
|
||||
borderRadius: 4,
|
||||
padding: 5,
|
||||
},
|
||||
closeDetailsContainer: {
|
||||
flex: 1,
|
||||
justifyContent: 'flex-start',
|
||||
},
|
||||
logoContainer: {
|
||||
flex: 1,
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'flex-end',
|
||||
marginLeft: 300,
|
||||
},
|
||||
closeCardBgContainer: {
|
||||
borderRadius: 10,
|
||||
margin: 8,
|
||||
backgroundColor: '#fff',
|
||||
shadowColor: '#000',
|
||||
shadowOffset: { width: -1, height: 1 },
|
||||
shadowOpacity: 0.4,
|
||||
shadowRadius: 3,
|
||||
elevation: 4,
|
||||
},
|
||||
labelPartContainer: {
|
||||
marginLeft: 16,
|
||||
},
|
||||
labelPart: {
|
||||
marginTop: 10,
|
||||
},
|
||||
openCardBgContainer: {
|
||||
borderRadius: 10,
|
||||
margin: 8,
|
||||
backgroundColor: '#fff',
|
||||
shadowColor: '#000',
|
||||
shadowOffset: { width: -1, height: 1 },
|
||||
shadowOpacity: 0.4,
|
||||
shadowRadius: 3,
|
||||
elevation: 4,
|
||||
padding: 10,
|
||||
},
|
||||
backgroundImageContainer: {
|
||||
flex: 1,
|
||||
padding: 10,
|
||||
},
|
||||
successTag: {
|
||||
backgroundColor: Colors.Green,
|
||||
height: 43,
|
||||
flex: 1,
|
||||
alignItems: 'center',
|
||||
paddingLeft: 6,
|
||||
},
|
||||
closeDetailsHeader: {
|
||||
flex: 1,
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'center',
|
||||
paddingLeft: 18,
|
||||
margin: 6,
|
||||
},
|
||||
openDetailsHeader: {
|
||||
flex: 1,
|
||||
justifyContent: 'space-between',
|
||||
},
|
||||
logo: {
|
||||
height: 30,
|
||||
width: 30,
|
||||
},
|
||||
homeCloseCardDetailsHeader: {
|
||||
flex: 1,
|
||||
justifyContent: 'flex-end',
|
||||
},
|
||||
details: {
|
||||
width: 290,
|
||||
marginLeft: 110,
|
||||
marginTop: 0,
|
||||
},
|
||||
openDetailsContainer: {
|
||||
flex: 1,
|
||||
justifyContent: 'space-between',
|
||||
padding: 10,
|
||||
},
|
||||
closeCardImage: {
|
||||
width: 100,
|
||||
height: 100,
|
||||
borderRadius: 5,
|
||||
},
|
||||
openCardImage: {
|
||||
width: 100,
|
||||
height: 100,
|
||||
borderRadius: 5,
|
||||
},
|
||||
scannerContainer: {
|
||||
borderWidth: 4,
|
||||
borderColor: Colors.Black,
|
||||
borderRadius: 32,
|
||||
justifyContent: 'center',
|
||||
height: 300,
|
||||
width: 300,
|
||||
overflow: 'hidden',
|
||||
},
|
||||
scanner: {
|
||||
height: 400,
|
||||
width: '100%',
|
||||
margin: 'auto',
|
||||
},
|
||||
flipIconButton: {
|
||||
alignSelf: 'center',
|
||||
alignItems: 'center',
|
||||
},
|
||||
tabIndicator: {
|
||||
backgroundColor: Colors.Orange,
|
||||
},
|
||||
tabContainer: {
|
||||
backgroundColor: Colors.Transparent,
|
||||
justifyContent: 'center',
|
||||
},
|
||||
tabView: {
|
||||
flex: 1,
|
||||
},
|
||||
detailsText: {
|
||||
fontWeight: 'bold',
|
||||
fontSize: 15,
|
||||
fontFamily: 'Poppins_700Bold',
|
||||
},
|
||||
getId: {
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
marginTop: 10,
|
||||
},
|
||||
placeholder: {
|
||||
fontFamily: 'Poppins_400Regular',
|
||||
},
|
||||
}),
|
||||
PinInputStyle: StyleSheet.create({
|
||||
input: {
|
||||
borderBottomWidth: 1,
|
||||
borderColor: Colors.Grey,
|
||||
color: Colors.Black,
|
||||
flex: 1,
|
||||
fontFamily: 'Poppins_600SemiBold',
|
||||
fontSize: 18,
|
||||
fontWeight: '600',
|
||||
height: 40,
|
||||
lineHeight: 28,
|
||||
margin: 8,
|
||||
textAlign: 'center',
|
||||
},
|
||||
}),
|
||||
TextStyles: StyleSheet.create({
|
||||
base: {
|
||||
color: Colors.Black,
|
||||
fontSize: 18,
|
||||
lineHeight: 28,
|
||||
},
|
||||
regular: {
|
||||
fontFamily: 'Poppins_400Regular',
|
||||
},
|
||||
semibold: {
|
||||
fontFamily: 'Poppins_600SemiBold',
|
||||
},
|
||||
bold: {
|
||||
fontFamily: 'Poppins_700Bold',
|
||||
},
|
||||
small: {
|
||||
fontSize: 14,
|
||||
lineHeight: 21,
|
||||
},
|
||||
smaller: {
|
||||
fontSize: 12,
|
||||
lineHeight: 18,
|
||||
},
|
||||
}),
|
||||
VcItemStyles: StyleSheet.create({
|
||||
title: {
|
||||
color: Colors.Black,
|
||||
backgroundColor: 'transparent',
|
||||
},
|
||||
loadingTitle: {
|
||||
color: 'transparent',
|
||||
backgroundColor: Colors.Grey5,
|
||||
borderRadius: 4,
|
||||
},
|
||||
subtitle: {
|
||||
backgroundColor: 'transparent',
|
||||
},
|
||||
loadingSubtitle: {
|
||||
backgroundColor: Colors.Grey,
|
||||
borderRadius: 4,
|
||||
},
|
||||
container: {
|
||||
backgroundColor: Colors.White,
|
||||
},
|
||||
loadingContainer: {
|
||||
backgroundColor: Colors.Grey6,
|
||||
borderRadius: 4,
|
||||
},
|
||||
}),
|
||||
ToastItemStyles: StyleSheet.create({
|
||||
toastContainer: {
|
||||
backgroundColor: Colors.Orange,
|
||||
position: 'absolute',
|
||||
alignSelf: 'center',
|
||||
top: 80,
|
||||
borderRadius: 4,
|
||||
},
|
||||
messageContainer: {
|
||||
fontSize: 12,
|
||||
},
|
||||
}),
|
||||
ButtonStyles: StyleSheet.create({
|
||||
fill: {
|
||||
flex: 1,
|
||||
},
|
||||
solid: {
|
||||
backgroundColor: Colors.Orange,
|
||||
},
|
||||
clear: {
|
||||
backgroundColor: Colors.Transparent,
|
||||
},
|
||||
outline: {
|
||||
backgroundColor: Colors.Transparent,
|
||||
borderColor: Colors.Orange,
|
||||
},
|
||||
container: {
|
||||
minHeight: 48,
|
||||
flexDirection: 'row',
|
||||
},
|
||||
disabled: {
|
||||
opacity: 0.5,
|
||||
},
|
||||
addId: {
|
||||
backgroundColor: Colors.Orange,
|
||||
},
|
||||
clearAddIdBtnBg: {
|
||||
backgroundColor: Colors.Transparent,
|
||||
},
|
||||
}),
|
||||
OnboardingOverlayStyles: StyleSheet.create({
|
||||
overlay: {
|
||||
padding: 24,
|
||||
bottom: 86,
|
||||
backgroundColor: 'transparent',
|
||||
shadowColor: 'transparent',
|
||||
},
|
||||
slide: {
|
||||
width: '100%',
|
||||
padding: 20,
|
||||
},
|
||||
slider: {
|
||||
backgroundColor: Colors.Orange,
|
||||
minHeight: 300,
|
||||
width: '100%',
|
||||
margin: 0,
|
||||
borderRadius: 4,
|
||||
},
|
||||
appSlider: {},
|
||||
sliderTitle: {
|
||||
color: Colors.White,
|
||||
marginBottom: 20,
|
||||
fontFamily: 'Poppins_700Bold',
|
||||
},
|
||||
text: {
|
||||
color: Colors.White,
|
||||
},
|
||||
paginationContainer: {
|
||||
margin: 10,
|
||||
},
|
||||
paginationDots: {
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
marginBottom: 20,
|
||||
},
|
||||
closeIcon: {
|
||||
alignItems: 'flex-end',
|
||||
end: 16,
|
||||
top: 40,
|
||||
zIndex: 1,
|
||||
},
|
||||
}),
|
||||
OpenCard: require('../../../assets/ID-open.png'),
|
||||
CloseCard: require('../../../assets/ID-closed.png'),
|
||||
ProfileIcon: require('../../../assets/placeholder-photo.png'),
|
||||
MosipLogo: require('../../../assets/mosip-logo.png'),
|
||||
elevation(level: ElevationLevel): ViewStyle {
|
||||
// https://ethercreative.github.io/react-native-shadow-generator/
|
||||
|
||||
if (level === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const index = level - 1;
|
||||
|
||||
return {
|
||||
shadowColor: Colors.Black,
|
||||
shadowOffset: {
|
||||
width: 0,
|
||||
height: [1, 1, 1, 2, 2][index],
|
||||
},
|
||||
shadowOpacity: [0.18, 0.2, 0.22, 0.23, 0.25][index],
|
||||
shadowRadius: [1.0, 1.41, 2.22, 2.62, 3.84][index],
|
||||
elevation: level,
|
||||
zIndex: level,
|
||||
borderRadius: 4,
|
||||
backgroundColor: Colors.White,
|
||||
};
|
||||
},
|
||||
spacing(type: 'margin' | 'padding', values: Spacing) {
|
||||
if (Array.isArray(values) && values.length === 2) {
|
||||
return {
|
||||
[`${type}Horizontal`]: values[0],
|
||||
[`${type}Vertical`]: values[1],
|
||||
};
|
||||
}
|
||||
|
||||
const [top, end, bottom, start] =
|
||||
typeof values === 'string' ? values.split(' ').map(Number) : values;
|
||||
|
||||
return {
|
||||
[`${type}Top`]: top,
|
||||
[`${type}End`]: end != null ? end : top,
|
||||
[`${type}Bottom`]: bottom != null ? bottom : top,
|
||||
[`${type}Start`]: start != null ? start : end != null ? end : top,
|
||||
};
|
||||
},
|
||||
};
|
||||
453
components/ui/themes/PurpleTheme.ts
Normal file
@@ -0,0 +1,453 @@
|
||||
import { StyleSheet, ViewStyle } from 'react-native';
|
||||
import { Spacing } from '../styleUtils';
|
||||
|
||||
const Colors = {
|
||||
Black: '#231F20',
|
||||
Grey: '#B0B0B0',
|
||||
Grey5: '#E0E0E0',
|
||||
Grey6: '#F2F2F2',
|
||||
Orange: '#F2811D',
|
||||
LightGrey: '#FAF9FF',
|
||||
White: '#FFFFFF',
|
||||
Red: '#EB5757',
|
||||
Green: '#219653',
|
||||
Purple: '#70308C',
|
||||
Transparent: 'transparent',
|
||||
};
|
||||
|
||||
export type ElevationLevel = 0 | 1 | 2 | 3 | 4 | 5;
|
||||
|
||||
export const PurpleTheme = {
|
||||
Colors: {
|
||||
TabItemText: Colors.Purple,
|
||||
Details: Colors.White,
|
||||
DetailsLabel: Colors.White,
|
||||
AddIdBtnBg: Colors.Purple,
|
||||
AddIdBtnTxt: Colors.Purple,
|
||||
ClearAddIdBtnBg: 'transparent',
|
||||
noUinText: Colors.Purple,
|
||||
IconBg: Colors.Purple,
|
||||
Icon: Colors.Purple,
|
||||
Loading: Colors.Purple,
|
||||
borderBottomColor: Colors.Grey6,
|
||||
whiteBackgroundColor: Colors.White,
|
||||
lightGreyBackgroundColor: Colors.LightGrey,
|
||||
profileLanguageValue: Colors.Grey,
|
||||
profileVersion: Colors.Grey,
|
||||
profileAuthFactorUnlock: Colors.Grey,
|
||||
profileLabel: Colors.Black,
|
||||
profileValue: Colors.Grey,
|
||||
overlayBackgroundColor: Colors.White,
|
||||
rotatingIcon: Colors.Grey5,
|
||||
loadingLabel: Colors.Grey6,
|
||||
textLabel: Colors.Grey,
|
||||
textValue: Colors.Black,
|
||||
errorMessage: Colors.Red,
|
||||
QRCodeBackgroundColor: Colors.LightGrey,
|
||||
ReceiveVcModalBackgroundColor: Colors.LightGrey,
|
||||
ToastItemText: Colors.White,
|
||||
VerifiedIcon: Colors.Green,
|
||||
whiteText: Colors.White,
|
||||
flipCameraIcon: Colors.Black,
|
||||
IdInputModalBorder: Colors.Grey,
|
||||
inputSelection: Colors.Orange,
|
||||
checkCircleIcon: Colors.White,
|
||||
OnboardingCircleIcon: Colors.White,
|
||||
OnboardingCloseIcon: Colors.White,
|
||||
},
|
||||
Styles: StyleSheet.create({
|
||||
title: {
|
||||
color: '#231F20',
|
||||
backgroundColor: 'transparent',
|
||||
},
|
||||
loadingTitle: {
|
||||
color: 'transparent',
|
||||
backgroundColor: '#B0B0B0',
|
||||
borderRadius: 4,
|
||||
},
|
||||
subtitle: {
|
||||
backgroundColor: 'transparent',
|
||||
},
|
||||
loadingSubtitle: {
|
||||
backgroundColor: '#B0B0B0',
|
||||
borderRadius: 4,
|
||||
},
|
||||
closeDetails: {
|
||||
flex: 1,
|
||||
flexDirection: 'row',
|
||||
paddingRight: 90,
|
||||
},
|
||||
loadingContainer: {
|
||||
flex: 1,
|
||||
flexDirection: 'row',
|
||||
backgroundColor: '#F2F2F2',
|
||||
borderRadius: 4,
|
||||
margin: 5,
|
||||
},
|
||||
vertloadingContainer: {
|
||||
flex: 1,
|
||||
backgroundColor: '#F2F2F2',
|
||||
borderRadius: 4,
|
||||
margin: 5,
|
||||
},
|
||||
closeDetailsContainer: {
|
||||
flex: 1,
|
||||
justifyContent: 'flex-start',
|
||||
},
|
||||
logoContainer: {
|
||||
flex: 1,
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'flex-end',
|
||||
marginLeft: 300,
|
||||
},
|
||||
closeCardBgContainer: {
|
||||
marginBottom: 10,
|
||||
backgroundColor: '#fff',
|
||||
shadowColor: '#000',
|
||||
shadowOffset: { width: -1, height: 1 },
|
||||
shadowOpacity: 0.4,
|
||||
shadowRadius: 3,
|
||||
elevation: 4,
|
||||
},
|
||||
labelPartContainer: {
|
||||
marginLeft: 16,
|
||||
},
|
||||
labelPart: {
|
||||
marginTop: 10,
|
||||
},
|
||||
openCardBgContainer: {
|
||||
margin: 8,
|
||||
shadowColor: '#000',
|
||||
shadowOffset: { width: -1, height: 1 },
|
||||
shadowOpacity: 1,
|
||||
shadowRadius: 3,
|
||||
elevation: 5,
|
||||
padding: 10,
|
||||
},
|
||||
backgroundImageContainer: {
|
||||
flex: 1,
|
||||
padding: 10,
|
||||
},
|
||||
successTag: {
|
||||
backgroundColor: '#219653',
|
||||
height: 43,
|
||||
flex: 1,
|
||||
alignItems: 'center',
|
||||
paddingLeft: 6,
|
||||
},
|
||||
closeDetailsHeader: {
|
||||
flex: 1,
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'center',
|
||||
paddingLeft: 18,
|
||||
margin: 6,
|
||||
},
|
||||
openDetailsHeader: {
|
||||
flex: 1,
|
||||
justifyContent: 'space-between',
|
||||
},
|
||||
homeCloseCardDetailsHeader: {
|
||||
flex: 1,
|
||||
justifyContent: 'space-between',
|
||||
},
|
||||
logo: {
|
||||
height: 30,
|
||||
width: 30,
|
||||
},
|
||||
openDetailsContainer: {
|
||||
flex: 1,
|
||||
justifyContent: 'space-between',
|
||||
padding: 10,
|
||||
},
|
||||
closeCardImage: {
|
||||
width: 100,
|
||||
height: 100,
|
||||
borderRadius: 5,
|
||||
},
|
||||
details: {
|
||||
width: 290,
|
||||
marginLeft: 110,
|
||||
marginTop: 0,
|
||||
},
|
||||
openCardImage: {
|
||||
width: 100,
|
||||
height: 100,
|
||||
borderRadius: 5,
|
||||
},
|
||||
scannerContainer: {
|
||||
borderWidth: 4,
|
||||
borderColor: '#231F20',
|
||||
borderRadius: 32,
|
||||
justifyContent: 'center',
|
||||
height: 300,
|
||||
width: 300,
|
||||
overflow: 'hidden',
|
||||
},
|
||||
scanner: {
|
||||
height: 400,
|
||||
width: '100%',
|
||||
margin: 'auto',
|
||||
},
|
||||
flipIconButton: {
|
||||
alignSelf: 'center',
|
||||
alignItems: 'center',
|
||||
},
|
||||
tabIndicator: {
|
||||
backgroundColor: '#70308C',
|
||||
},
|
||||
tabContainer: {
|
||||
backgroundColor: 'transparent',
|
||||
justifyContent: 'center',
|
||||
},
|
||||
tabView: {
|
||||
flex: 1,
|
||||
},
|
||||
detailsText: {
|
||||
fontWeight: 'bold',
|
||||
fontSize: 15,
|
||||
fontFamily: 'Poppins_700Bold',
|
||||
},
|
||||
getId: {
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
marginTop: 10,
|
||||
},
|
||||
placeholder: {
|
||||
fontFamily: 'Poppins_400Regular',
|
||||
},
|
||||
overlay: {
|
||||
padding: 24,
|
||||
bottom: 86,
|
||||
backgroundColor: 'transparent',
|
||||
shadowColor: 'transparent',
|
||||
},
|
||||
slide: {
|
||||
width: '100%',
|
||||
padding: 20,
|
||||
},
|
||||
slider: {
|
||||
backgroundColor: '#70308C',
|
||||
minHeight: 280,
|
||||
width: '100%',
|
||||
margin: 0,
|
||||
borderRadius: 4,
|
||||
},
|
||||
appSlider: {},
|
||||
sliderTitle: {
|
||||
color: '#FFFFFF',
|
||||
marginBottom: 20,
|
||||
fontFamily: 'Poppins_700Bold',
|
||||
},
|
||||
text: {
|
||||
color: '#FFFFFF',
|
||||
},
|
||||
paginationContainer: {
|
||||
margin: 10,
|
||||
},
|
||||
paginationDots: {
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
marginBottom: 20,
|
||||
},
|
||||
closeIcon: {
|
||||
alignItems: 'flex-end',
|
||||
end: 16,
|
||||
top: 40,
|
||||
zIndex: 1,
|
||||
},
|
||||
}),
|
||||
PinInputStyle: StyleSheet.create({
|
||||
input: {
|
||||
borderBottomWidth: 1,
|
||||
borderColor: Colors.Grey,
|
||||
color: Colors.Black,
|
||||
flex: 1,
|
||||
fontFamily: 'Poppins_600SemiBold',
|
||||
fontSize: 18,
|
||||
fontWeight: '600',
|
||||
height: 40,
|
||||
lineHeight: 28,
|
||||
margin: 8,
|
||||
textAlign: 'center',
|
||||
},
|
||||
}),
|
||||
TextStyles: StyleSheet.create({
|
||||
base: {
|
||||
color: Colors.Black,
|
||||
fontSize: 18,
|
||||
lineHeight: 28,
|
||||
},
|
||||
regular: {
|
||||
fontFamily: 'Poppins_400Regular',
|
||||
},
|
||||
semibold: {
|
||||
fontFamily: 'Poppins_600SemiBold',
|
||||
},
|
||||
bold: {
|
||||
fontFamily: 'Poppins_700Bold',
|
||||
},
|
||||
small: {
|
||||
fontSize: 14,
|
||||
lineHeight: 21,
|
||||
},
|
||||
smaller: {
|
||||
fontSize: 12,
|
||||
lineHeight: 18,
|
||||
},
|
||||
}),
|
||||
VcItemStyles: StyleSheet.create({
|
||||
title: {
|
||||
color: Colors.Black,
|
||||
backgroundColor: 'transparent',
|
||||
},
|
||||
loadingTitle: {
|
||||
color: 'transparent',
|
||||
backgroundColor: Colors.Grey5,
|
||||
borderRadius: 4,
|
||||
},
|
||||
subtitle: {
|
||||
backgroundColor: 'transparent',
|
||||
},
|
||||
loadingSubtitle: {
|
||||
backgroundColor: Colors.Grey,
|
||||
borderRadius: 4,
|
||||
},
|
||||
container: {
|
||||
backgroundColor: Colors.White,
|
||||
},
|
||||
loadingContainer: {
|
||||
backgroundColor: Colors.Grey6,
|
||||
borderRadius: 4,
|
||||
},
|
||||
}),
|
||||
ToastItemStyles: StyleSheet.create({
|
||||
toastContainer: {
|
||||
backgroundColor: Colors.Orange,
|
||||
position: 'absolute',
|
||||
alignSelf: 'center',
|
||||
top: 80,
|
||||
borderRadius: 4,
|
||||
},
|
||||
messageContainer: {
|
||||
fontSize: 12,
|
||||
},
|
||||
}),
|
||||
ButtonStyles: StyleSheet.create({
|
||||
fill: {
|
||||
flex: 1,
|
||||
},
|
||||
solid: {
|
||||
backgroundColor: Colors.Purple,
|
||||
},
|
||||
clear: {
|
||||
backgroundColor: Colors.Transparent,
|
||||
},
|
||||
outline: {
|
||||
backgroundColor: Colors.Transparent,
|
||||
borderColor: Colors.Purple,
|
||||
},
|
||||
container: {
|
||||
minHeight: 48,
|
||||
flexDirection: 'row',
|
||||
},
|
||||
disabled: {
|
||||
opacity: 0.5,
|
||||
},
|
||||
addId: {
|
||||
backgroundColor: Colors.Purple,
|
||||
},
|
||||
clearAddIdBtnBg: {
|
||||
backgroundColor: Colors.Transparent,
|
||||
},
|
||||
}),
|
||||
OnboardingOverlayStyles: StyleSheet.create({
|
||||
overlay: {
|
||||
padding: 24,
|
||||
bottom: 86,
|
||||
backgroundColor: 'transparent',
|
||||
shadowColor: 'transparent',
|
||||
},
|
||||
slide: {
|
||||
width: '100%',
|
||||
padding: 20,
|
||||
},
|
||||
slider: {
|
||||
backgroundColor: Colors.Purple,
|
||||
minHeight: 300,
|
||||
width: '100%',
|
||||
margin: 0,
|
||||
borderRadius: 4,
|
||||
},
|
||||
appSlider: {},
|
||||
sliderTitle: {
|
||||
color: Colors.White,
|
||||
marginBottom: 20,
|
||||
fontFamily: 'Poppins_700Bold',
|
||||
},
|
||||
text: {
|
||||
color: Colors.White,
|
||||
},
|
||||
paginationContainer: {
|
||||
margin: 10,
|
||||
},
|
||||
paginationDots: {
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
marginBottom: 20,
|
||||
},
|
||||
closeIcon: {
|
||||
alignItems: 'flex-end',
|
||||
end: 16,
|
||||
top: 40,
|
||||
zIndex: 1,
|
||||
},
|
||||
}),
|
||||
OpenCard: require('../../../purpleAassets/bg_cart_one.png'),
|
||||
CloseCard: require('../../../purpleAassets/cart_unsel.png'),
|
||||
ProfileIcon: require('../../../purpleAassets/profile_icon_unsel.png'),
|
||||
MosipLogo: require('../../../purpleAassets/logo.png'),
|
||||
elevation(level: ElevationLevel): ViewStyle {
|
||||
// https://ethercreative.github.io/react-native-shadow-generator/
|
||||
|
||||
if (level === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const index = level - 1;
|
||||
|
||||
return {
|
||||
shadowColor: Colors.Black,
|
||||
shadowOffset: {
|
||||
width: 0,
|
||||
height: [1, 1, 1, 2, 2][index],
|
||||
},
|
||||
shadowOpacity: [0.18, 0.2, 0.22, 0.23, 0.25][index],
|
||||
shadowRadius: [1.0, 1.41, 2.22, 2.62, 3.84][index],
|
||||
elevation: level,
|
||||
zIndex: level,
|
||||
borderRadius: 4,
|
||||
backgroundColor: Colors.White,
|
||||
};
|
||||
},
|
||||
spacing(type: 'margin' | 'padding', values: Spacing) {
|
||||
if (Array.isArray(values) && values.length === 2) {
|
||||
return {
|
||||
[`${type}Horizontal`]: values[0],
|
||||
[`${type}Vertical`]: values[1],
|
||||
};
|
||||
}
|
||||
|
||||
const [top, end, bottom, start] =
|
||||
typeof values === 'string' ? values.split(' ').map(Number) : values;
|
||||
|
||||
return {
|
||||
[`${type}Top`]: top,
|
||||
[`${type}End`]: end != null ? end : top,
|
||||
[`${type}Bottom`]: bottom != null ? bottom : top,
|
||||
[`${type}Start`]: start != null ? start : end != null ? end : top,
|
||||
};
|
||||
},
|
||||
};
|
||||
209
components/ui/themes/Readme.md
Normal file
@@ -0,0 +1,209 @@
|
||||
# CSS Themes
|
||||
|
||||
We can customize the application Theme by adding a new file under components/ui/themes and import that file in components/ui/styleUtils.ts and assign that file to Theme variable
|
||||
|
||||
```
|
||||
components/ui/styleUtils.ts
|
||||
|
||||
eg:-
|
||||
import { PurpleTheme } from './PurpleTheme';
|
||||
|
||||
export const Theme = PurpleTheme;
|
||||
```
|
||||
|
||||
## **Logo and Background Images :**
|
||||
|
||||
To change the mosip logo
|
||||
|
||||
```
|
||||
MosipLogo = require(path of logo you want to use, in string format)
|
||||
|
||||
*logo can be png or svg
|
||||
|
||||
eg:-
|
||||
MosipLogo = require('../../assets/mosip-logo.png')
|
||||
```
|
||||
|
||||
To change the profile logo which is available as a demo while loading the vc details
|
||||
|
||||
```
|
||||
ProfileIcon = require(path of logo you want to use, in string format)
|
||||
|
||||
*logo can be png or svg
|
||||
|
||||
eg:-
|
||||
ProfileIcon: require('../../assets/placeholder-photo.png')
|
||||
```
|
||||
|
||||
To change the close card details background
|
||||
|
||||
```
|
||||
CloseCard = require(path of image you want to use, in string format)
|
||||
|
||||
*Image can be png or svg
|
||||
-width: 363 pixels
|
||||
-height: 236 pixels
|
||||
|
||||
eg:-
|
||||
CloseCard: require('../../assets/ID-closed.png')
|
||||
```
|
||||
|
||||
To change the OpenCard card details background
|
||||
|
||||
```
|
||||
OpenCard = require(path of image you want to use, in string format)
|
||||
|
||||
*Image can be png or svg
|
||||
-width: 363 pixels
|
||||
-height: 623 pixels
|
||||
|
||||
eg:-
|
||||
OpenCard: require('../../assets/ID-open.png')
|
||||
```
|
||||
|
||||
## **Colors :**
|
||||
|
||||
To change the color of TabItemText
|
||||
|
||||

|
||||
|
||||
```
|
||||
export const DefaultTheme = {
|
||||
Colors: {
|
||||
TabItemText: colors.Orange,
|
||||
...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
To change the color of Details Label Text
|
||||
|
||||

|
||||
|
||||
```
|
||||
export const DefaultTheme = {
|
||||
Colors: {
|
||||
DetailsLabel: colors.Orange,
|
||||
...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
To change the color of Details Text
|
||||
|
||||

|
||||
|
||||
```
|
||||
export const DefaultTheme = {
|
||||
Colors: {
|
||||
Details: colors.Orange,
|
||||
...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
To change the color of AddId Button Text and Background
|
||||
|
||||

|
||||
|
||||
```
|
||||
export const DefaultTheme = {
|
||||
Colors: {
|
||||
AddIdBtnBg: colors.Orange,
|
||||
AddIdBtnTxt: colors.Orange,
|
||||
...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
To change the color of Icons
|
||||
|
||||

|
||||
|
||||
```
|
||||
export const DefaultTheme = {
|
||||
Colors: {
|
||||
Icon: colors.Orange,
|
||||
...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
To change the Background Color of Icons
|
||||
|
||||

|
||||
|
||||
```
|
||||
export const DefaultTheme = {
|
||||
Colors: {
|
||||
IconBg: colors.Orange,
|
||||
...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
To change the Color of Loading Transition
|
||||
|
||||

|
||||
|
||||
```
|
||||
export const DefaultTheme = {
|
||||
Colors: {
|
||||
Loading: colors.Orange,
|
||||
...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
To change the Color of noUinText
|
||||
|
||||

|
||||
|
||||
```
|
||||
export const DefaultTheme = {
|
||||
Colors: {
|
||||
noUinText: colors.Orange,
|
||||
...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
To change the Color of profileLabel
|
||||
|
||||

|
||||
|
||||
```
|
||||
export const DefaultTheme = {
|
||||
Colors: {
|
||||
profileLabel: colors.Black,
|
||||
...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
To change the Color of profileValue and profileAuthFactorUnlock
|
||||
|
||||

|
||||
|
||||
```
|
||||
export const DefaultTheme = {
|
||||
Colors: {
|
||||
profileAuthFactorUnlock: colors.Grey,
|
||||
profileValue: colors.Grey,
|
||||
...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
To change the Color of profileVersion
|
||||
|
||||

|
||||
|
||||
```
|
||||
export const DefaultTheme = {
|
||||
Colors: {
|
||||
profileVersion: colors.Grey,
|
||||
...
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -14,6 +14,7 @@
|
||||
},
|
||||
"VcDetails": {
|
||||
"generatedOn": "Generated on",
|
||||
"uin":"UIN",
|
||||
"status": "Status",
|
||||
"valid": "Valid",
|
||||
"photo": "Photo",
|
||||
@@ -67,6 +68,22 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"GetVcModal": {
|
||||
"retrievingId": "Retrieving UID/VID...",
|
||||
"applicationProcessing": "Your application is still being processed. Please try again after a few days",
|
||||
"errors": {
|
||||
"input": {
|
||||
"empty": "The input cannot be empty",
|
||||
"invalidFormat": "The input format is incorrect"
|
||||
},
|
||||
"backend": {
|
||||
"invalidOtp": "OTP is invalid",
|
||||
"applicationProcessing": "Your application is still being processed. Please try again after a few days",
|
||||
"invalidAid": "AID is not ready",
|
||||
"timeOut": "Requesting Time Out"
|
||||
}
|
||||
}
|
||||
},
|
||||
"DownloadingVcModal": {
|
||||
"header": "Downloading your {{vcLabel}}",
|
||||
"bodyText": "This may take some time, we will notify you when your {{vcLabel}} has been downloaded and is available",
|
||||
@@ -75,7 +92,13 @@
|
||||
"IdInputModal": {
|
||||
"header": "Enter the MOSIP-provided UIN or VID of the {{vcLabel}} you wish to retrieve",
|
||||
"generateVc": "Generate {{vcLabel}}",
|
||||
"enterId": "Enter your {{idType}}"
|
||||
"enterId": "Enter your {{idType}}",
|
||||
"noUIN/VID": "Don't have your UIN/VID? Get it here"
|
||||
},
|
||||
"GetIdInputModal": {
|
||||
"header": "To retrieve your UIN or VID, enter your Application {{vcLabel}} number",
|
||||
"getUIN": "Get UIN/VID",
|
||||
"applicationId": "Application {{vcLabel}} number"
|
||||
},
|
||||
"OtpVerificationModal": {
|
||||
"enterOtp": "Enter the 6-digit verification code we sent you"
|
||||
|
||||
@@ -49,6 +49,7 @@ const model = createModel(
|
||||
REJECT: () => ({}),
|
||||
CANCEL: () => ({}),
|
||||
DISMISS: () => ({}),
|
||||
GOBACK: () => ({}),
|
||||
VC_RECEIVED: (vc: VC) => ({ vc }),
|
||||
CONNECTED: () => ({}),
|
||||
DISCONNECT: () => ({}),
|
||||
@@ -241,6 +242,7 @@ export const requestMachine = model.createMachine(
|
||||
},
|
||||
on: {
|
||||
DISMISS: 'navigatingToHome',
|
||||
GOBACK: '#clearingConnection',
|
||||
},
|
||||
},
|
||||
rejected: {
|
||||
@@ -255,6 +257,7 @@ export const requestMachine = model.createMachine(
|
||||
},
|
||||
},
|
||||
navigatingToHome: {},
|
||||
navigatingToTimeBasedRequest: {},
|
||||
},
|
||||
exit: ['disconnect'],
|
||||
},
|
||||
|
||||
@@ -559,6 +559,10 @@ export function selectVerifiableCredential(state: State) {
|
||||
return state.context.verifiableCredential;
|
||||
}
|
||||
|
||||
export function selectContext(state: State) {
|
||||
return state.context;
|
||||
}
|
||||
|
||||
export function selectIsEditingTag(state: State) {
|
||||
return state.matches('editingTag');
|
||||
}
|
||||
|
||||
11
package-lock.json
generated
@@ -59,6 +59,7 @@
|
||||
"react-native-securerandom": "^1.0.0",
|
||||
"react-native-simple-markdown": "^1.1.0",
|
||||
"react-native-svg": "12.1.1",
|
||||
"react-native-swipe-gestures": "^1.0.5",
|
||||
"react-native-system-setting": "^1.7.6",
|
||||
"react-native-uuid": "^2.0.1",
|
||||
"react-native-vector-icons": "^8.1.0",
|
||||
@@ -21223,6 +21224,11 @@
|
||||
"react-native": ">=0.50.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-native-swipe-gestures": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/react-native-swipe-gestures/-/react-native-swipe-gestures-1.0.5.tgz",
|
||||
"integrity": "sha512-Ns7Bn9H/Tyw278+5SQx9oAblDZ7JixyzeOczcBK8dipQk2pD7Djkcfnf1nB/8RErAmMLL9iXgW0QHqiII8AhKw=="
|
||||
},
|
||||
"node_modules/react-native-system-setting": {
|
||||
"version": "1.7.6",
|
||||
"resolved": "https://registry.npmjs.org/react-native-system-setting/-/react-native-system-setting-1.7.6.tgz",
|
||||
@@ -44044,6 +44050,11 @@
|
||||
"css-tree": "^1.0.0-alpha.39"
|
||||
}
|
||||
},
|
||||
"react-native-swipe-gestures": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/react-native-swipe-gestures/-/react-native-swipe-gestures-1.0.5.tgz",
|
||||
"integrity": "sha512-Ns7Bn9H/Tyw278+5SQx9oAblDZ7JixyzeOczcBK8dipQk2pD7Djkcfnf1nB/8RErAmMLL9iXgW0QHqiII8AhKw=="
|
||||
},
|
||||
"react-native-system-setting": {
|
||||
"version": "1.7.6",
|
||||
"resolved": "https://registry.npmjs.org/react-native-system-setting/-/react-native-system-setting-1.7.6.tgz",
|
||||
|
||||
@@ -63,6 +63,7 @@
|
||||
"react-native-securerandom": "^1.0.0",
|
||||
"react-native-simple-markdown": "^1.1.0",
|
||||
"react-native-svg": "12.1.1",
|
||||
"react-native-swipe-gestures": "^1.0.5",
|
||||
"react-native-system-setting": "^1.7.6",
|
||||
"react-native-uuid": "^2.0.1",
|
||||
"react-native-vector-icons": "^8.1.0",
|
||||
@@ -90,7 +91,6 @@
|
||||
"name": "mosip-resident-app",
|
||||
"version": "1.0.0",
|
||||
"lint-staged": {
|
||||
"*.{ts,tsx}": "eslint --cache --fix",
|
||||
"*.{ts,tsx,js,css,md}": "prettier --write",
|
||||
"*.strings.json": "node scripts/compile-strings.js"
|
||||
},
|
||||
|
||||
BIN
purpleAassets/_cart_sel.png
Normal file
|
After Width: | Height: | Size: 179 KiB |
BIN
purpleAassets/bg_cart_one.png
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
purpleAassets/cart_unsel.png
Normal file
|
After Width: | Height: | Size: 24 KiB |
BIN
purpleAassets/idpass-logo.png
Normal file
|
After Width: | Height: | Size: 15 KiB |
BIN
purpleAassets/logo.png
Normal file
|
After Width: | Height: | Size: 14 KiB |
BIN
purpleAassets/profile_icon_unsel.png
Normal file
|
After Width: | Height: | Size: 2.2 KiB |
BIN
purpleAassets/splash.png
Normal file
|
After Width: | Height: | Size: 39 KiB |
@@ -7,6 +7,7 @@ import { HomeScreen } from '../screens/Home/HomeScreen';
|
||||
import { ProfileScreen } from '../screens/Profile/ProfileScreen';
|
||||
import { ScanScreen } from '../screens/Scan/ScanScreen';
|
||||
import { RootStackParamList } from './index';
|
||||
import { TimerBasedRequestScreen } from '../screens/Request/TimerBasedRequestScreen';
|
||||
import { RequestLayout } from '../screens/Request/RequestLayout';
|
||||
|
||||
export const mainRoutes: TabScreen[] = [
|
||||
@@ -31,6 +32,11 @@ export const mainRoutes: TabScreen[] = [
|
||||
headerShown: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'TimerBasedRequest',
|
||||
component: TimerBasedRequestScreen,
|
||||
icon: 'timer',
|
||||
},
|
||||
{
|
||||
name: 'Profile',
|
||||
component: ProfileScreen,
|
||||
@@ -44,6 +50,7 @@ export type MainBottomTabParamList = {
|
||||
};
|
||||
Scan: undefined;
|
||||
Request: undefined;
|
||||
TimerBasedRequest: undefined;
|
||||
Profile: undefined;
|
||||
};
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ import { LanguageSelector } from '../components/LanguageSelector';
|
||||
import { authRoutes, baseRoutes } from '../routes';
|
||||
import { useAppLayout } from './AppLayoutController';
|
||||
import { Icon } from 'react-native-elements';
|
||||
import { Colors } from '../components/ui/styleUtils';
|
||||
import { Theme } from '../components/ui/styleUtils';
|
||||
import { StatusBar } from 'react-native';
|
||||
|
||||
const { Navigator, Screen } = createNativeStackNavigator();
|
||||
@@ -22,7 +22,7 @@ export const AppLayout: React.FC = () => {
|
||||
headerShadowVisible: false,
|
||||
headerRight: () => (
|
||||
<LanguageSelector
|
||||
triggerComponent={<Icon name="language" color={Colors.Orange} />}
|
||||
triggerComponent={<Icon name="language" color={Theme.Colors.Icon} />}
|
||||
/>
|
||||
),
|
||||
headerBackVisible: false,
|
||||
|
||||
@@ -3,7 +3,7 @@ import { useTranslation } from 'react-i18next';
|
||||
import { Icon } from 'react-native-elements';
|
||||
import { MessageOverlay } from '../components/MessageOverlay';
|
||||
import { Button, Centered, Column, Text } from '../components/ui';
|
||||
import { Colors } from '../components/ui/styleUtils';
|
||||
import { Theme } from '../components/ui/styleUtils';
|
||||
import { RootRouteProps } from '../routes';
|
||||
import { useAuthScreen } from './AuthScreenController';
|
||||
|
||||
@@ -12,7 +12,10 @@ export const AuthScreen: React.FC<RootRouteProps> = (props) => {
|
||||
const controller = useAuthScreen(props);
|
||||
|
||||
return (
|
||||
<Column fill padding={[32, 32, 32, 32]} backgroundColor={Colors.White}>
|
||||
<Column
|
||||
fill
|
||||
padding={[32, 32, 32, 32]}
|
||||
backgroundColor={Theme.Colors.whiteBackgroundColor}>
|
||||
<MessageOverlay
|
||||
isVisible={controller.alertMsg != ''}
|
||||
onBackdropPress={controller.hideAlert}
|
||||
@@ -22,7 +25,7 @@ export const AuthScreen: React.FC<RootRouteProps> = (props) => {
|
||||
<Text align="center">{t('header')}</Text>
|
||||
</Column>
|
||||
<Centered fill>
|
||||
<Icon name="fingerprint" size={180} color={Colors.Orange} />
|
||||
<Icon name="fingerprint" size={180} color={Theme.Colors.Icon} />
|
||||
</Centered>
|
||||
<Column>
|
||||
<Button
|
||||
|
||||
@@ -3,7 +3,7 @@ import { useTranslation } from 'react-i18next';
|
||||
import { TouchableOpacity } from 'react-native';
|
||||
import { Icon } from 'react-native-elements';
|
||||
import { Button, Centered, Column } from '../components/ui';
|
||||
import { Colors } from '../components/ui/styleUtils';
|
||||
import { Theme } from '../components/ui/styleUtils';
|
||||
import { RootRouteProps } from '../routes';
|
||||
import { useBiometricScreen } from './BiometricScreenController';
|
||||
import { Passcode } from '../components/Passcode';
|
||||
@@ -13,10 +13,14 @@ export const BiometricScreen: React.FC<RootRouteProps> = (props) => {
|
||||
const controller = useBiometricScreen(props);
|
||||
|
||||
return (
|
||||
<Column fill pY={32} pX={32} backgroundColor={Colors.White}>
|
||||
<Column
|
||||
fill
|
||||
pY={32}
|
||||
pX={32}
|
||||
backgroundColor={Theme.Colors.whiteBackgroundColor}>
|
||||
<Centered fill>
|
||||
<TouchableOpacity onPress={controller.useBiometrics}>
|
||||
<Icon name="fingerprint" size={180} color={Colors.Orange} />
|
||||
<Icon name="fingerprint" size={180} color={Theme.Colors.Icon} />
|
||||
</TouchableOpacity>
|
||||
</Centered>
|
||||
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import React from 'react';
|
||||
import { StyleSheet } from 'react-native';
|
||||
import { Tab } from 'react-native-elements';
|
||||
import { Column, Text } from '../../components/ui';
|
||||
import { Colors } from '../../components/ui/styleUtils';
|
||||
import { Theme } from '../../components/ui/styleUtils';
|
||||
import { HomeRouteProps } from '../../routes/main';
|
||||
import { HistoryTab } from './HistoryTab';
|
||||
import { MyVcsTab } from './MyVcsTab';
|
||||
@@ -11,19 +10,8 @@ import { ViewVcModal } from './ViewVcModal';
|
||||
import { useHomeScreen } from './HomeScreenController';
|
||||
import { TabRef } from './HomeScreenMachine';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
tabIndicator: {
|
||||
backgroundColor: Colors.Orange,
|
||||
},
|
||||
tabContainer: {
|
||||
backgroundColor: 'transparent',
|
||||
justifyContent: 'center',
|
||||
},
|
||||
tabView: {
|
||||
flex: 1,
|
||||
},
|
||||
});
|
||||
import { ActorRefFrom } from 'xstate';
|
||||
import { vcItemMachine } from '../../machines/vcItem';
|
||||
|
||||
export const HomeScreen: React.FC<HomeRouteProps> = (props) => {
|
||||
const { t } = useTranslation('HomeScreen');
|
||||
@@ -31,11 +19,11 @@ export const HomeScreen: React.FC<HomeRouteProps> = (props) => {
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<Column fill backgroundColor={Colors.LightGrey}>
|
||||
<Column fill backgroundColor={Theme.Colors.lightGreyBackgroundColor}>
|
||||
<Tab
|
||||
value={controller.activeTab}
|
||||
onChange={controller.SELECT_TAB}
|
||||
indicatorStyle={styles.tabIndicator}>
|
||||
indicatorStyle={Theme.Styles.tabIndicator}>
|
||||
{TabItem(t('myVcsTab', { vcLabel: controller.vcLabel.plural }))}
|
||||
{TabItem(t('receivedVcsTab', { vcLabel: controller.vcLabel.plural }))}
|
||||
{TabItem(t('historyTab'))}
|
||||
@@ -45,13 +33,17 @@ export const HomeScreen: React.FC<HomeRouteProps> = (props) => {
|
||||
<MyVcsTab
|
||||
isVisible={controller.activeTab === 0}
|
||||
service={controller.tabRefs.myVcs}
|
||||
vcItemActor={controller.selectedVc}
|
||||
onSwipe={() => props.navigation.navigate('TimerBasedRequest')}
|
||||
/>
|
||||
<ReceivedVcsTab
|
||||
isVisible={controller.activeTab === 1}
|
||||
service={controller.tabRefs.receivedVcs}
|
||||
vcItemActor={controller.selectedVc}
|
||||
/>
|
||||
<HistoryTab
|
||||
isVisible={controller.activeTab === 2}
|
||||
vcItemActor={controller.selectedVc}
|
||||
service={controller.tabRefs.history}
|
||||
/>
|
||||
</Column>
|
||||
@@ -71,9 +63,9 @@ export const HomeScreen: React.FC<HomeRouteProps> = (props) => {
|
||||
function TabItem(title: string) {
|
||||
return (
|
||||
<Tab.Item
|
||||
containerStyle={styles.tabContainer}
|
||||
containerStyle={Theme.Styles.tabContainer}
|
||||
title={
|
||||
<Text align="center" color={Colors.Orange}>
|
||||
<Text align="center" color={Theme.Colors.TabItemText}>
|
||||
{title}
|
||||
</Text>
|
||||
}
|
||||
@@ -84,4 +76,6 @@ function TabItem(title: string) {
|
||||
export interface HomeScreenTabProps {
|
||||
isVisible: boolean;
|
||||
service: TabRef;
|
||||
onSwipe?: () => void;
|
||||
vcItemActor: ActorRefFrom<typeof vcItemMachine>;
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ export const AddVcModal: React.FC<AddVcModalProps> = (props) => {
|
||||
!controller.isAcceptingOtpInput && !controller.isRequestingCredential
|
||||
}
|
||||
onDismiss={controller.DISMISS}
|
||||
onPress={props.onPress}
|
||||
/>
|
||||
|
||||
<OtpVerificationModal
|
||||
|
||||
@@ -26,4 +26,5 @@ export function useAddVcModal({ service }: AddVcModalProps) {
|
||||
|
||||
export interface AddVcModalProps {
|
||||
service: ActorRefFrom<typeof AddVcModalMachine>;
|
||||
onPress?: () => void;
|
||||
}
|
||||
|
||||
@@ -49,6 +49,11 @@ export const AddVcModalMachine =
|
||||
},
|
||||
id: 'AddVcModal',
|
||||
initial: 'acceptingIdInput',
|
||||
on: {
|
||||
INPUT_ID: {
|
||||
actions: 'setId',
|
||||
},
|
||||
},
|
||||
states: {
|
||||
acceptingIdInput: {
|
||||
entry: ['setTransactionId', 'clearOtp'],
|
||||
@@ -281,10 +286,13 @@ export const AddVcModalMachine =
|
||||
services: {
|
||||
requestOtp: async (context) => {
|
||||
return request('POST', '/req/otp', {
|
||||
id: 'mosip.identity.otp.internal',
|
||||
individualId: context.id,
|
||||
individualIdType: context.idType,
|
||||
otpChannel: ['EMAIL', 'PHONE'],
|
||||
metadata: {},
|
||||
otpChannel: ['PHONE', 'EMAIL'],
|
||||
requestTime: String(new Date().toISOString()),
|
||||
transactionID: context.transactionId,
|
||||
version: '1.0',
|
||||
});
|
||||
},
|
||||
|
||||
|
||||
@@ -9,7 +9,10 @@ export const DownloadingVcModal: React.FC<ModalProps> = (props) => {
|
||||
const controller = useDownloadingVcModal();
|
||||
|
||||
return (
|
||||
<Modal isVisible={props.isVisible} onDismiss={props.onDismiss}>
|
||||
<Modal
|
||||
isVisible={props.isVisible}
|
||||
onDismiss={props.onDismiss}
|
||||
onShow={props.onShow}>
|
||||
<Column fill pY={32} pX={24} align="space-between">
|
||||
<Column fill>
|
||||
<Text weight="semibold" align="center">
|
||||
|
||||
64
screens/Home/MyVcs/GetIdInputModal.tsx
Normal file
@@ -0,0 +1,64 @@
|
||||
import React from 'react';
|
||||
import { Icon, Input } from 'react-native-elements';
|
||||
import { Button, Column, Row, Text } from '../../../components/ui';
|
||||
import { Modal } from '../../../components/ui/Modal';
|
||||
import { Theme } from '../../../components/ui/styleUtils';
|
||||
import {
|
||||
GetIdInputModalProps,
|
||||
useGetIdInputModal,
|
||||
} from './GetIdInputModalController';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
export const GetIdInputModal: React.FC<GetIdInputModalProps> = (props) => {
|
||||
const { t } = useTranslation('GetIdInputModal');
|
||||
const controller = useGetIdInputModal(props);
|
||||
|
||||
const inputLabel = t('applicationId', {
|
||||
vcLabel: controller.vcLabel.singular,
|
||||
});
|
||||
|
||||
return (
|
||||
<Modal onDismiss={props.onDismiss} isVisible={props.isVisible}>
|
||||
<Column fill align="space-between" padding="32 24">
|
||||
<Text align="center">
|
||||
{t('header', { vcLabel: controller.vcLabel.singular })}
|
||||
</Text>
|
||||
<Column>
|
||||
<Row crossAlign="flex-end">
|
||||
<Column fill>
|
||||
<Input
|
||||
placeholder={!controller.id ? inputLabel : ''}
|
||||
label={controller.id ? inputLabel : ''}
|
||||
labelStyle={{
|
||||
color: controller.isInvalid
|
||||
? Theme.Colors.errorMessage
|
||||
: Theme.Colors.textValue,
|
||||
}}
|
||||
style={Theme.Styles.placeholder}
|
||||
value={controller.id}
|
||||
keyboardType="number-pad"
|
||||
rightIcon={
|
||||
controller.isInvalid ? (
|
||||
<Icon name="error" size={18} color="red" />
|
||||
) : (
|
||||
<Icon name="help" size={18} />
|
||||
)
|
||||
}
|
||||
errorStyle={{ color: Theme.Colors.errorMessage }}
|
||||
errorMessage={controller.idError}
|
||||
onChangeText={controller.INPUT_ID}
|
||||
ref={(node) => !controller.idInputRef && controller.READY(node)}
|
||||
/>
|
||||
</Column>
|
||||
</Row>
|
||||
<Button
|
||||
title={t('getUIN', { vcLabel: controller.vcLabel.singular })}
|
||||
margin="24 0 0 0"
|
||||
onPress={controller.VALIDATE_INPUT}
|
||||
loading={controller.isRequestingOtp}
|
||||
/>
|
||||
</Column>
|
||||
</Column>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
46
screens/Home/MyVcs/GetIdInputModalController.ts
Normal file
@@ -0,0 +1,46 @@
|
||||
import { useContext } from 'react';
|
||||
import { useSelector } from '@xstate/react';
|
||||
import { selectVcLabel } from '../../../machines/settings';
|
||||
import { GlobalContext } from '../../../shared/GlobalContext';
|
||||
import { ActorRefFrom } from 'xstate';
|
||||
import { TextInput } from 'react-native';
|
||||
import { ModalProps } from '../../../components/ui/Modal';
|
||||
import {
|
||||
GetVcModalEvents,
|
||||
GetVcModalMachine,
|
||||
selectIsAcceptingOtpInput,
|
||||
selectIsInvalid,
|
||||
selectIsRequestingOtp,
|
||||
selectOtpError,
|
||||
selectId,
|
||||
selectIdError,
|
||||
selectIdInputRef,
|
||||
} from './GetVcModalMachine';
|
||||
|
||||
export function useGetIdInputModal({ service }: GetIdInputModalProps) {
|
||||
const { appService } = useContext(GlobalContext);
|
||||
const settingsService = appService.children.get('settings');
|
||||
|
||||
return {
|
||||
id: useSelector(service, selectId),
|
||||
idInputRef: useSelector(service, selectIdInputRef),
|
||||
vcLabel: useSelector(settingsService, selectVcLabel),
|
||||
idError: useSelector(service, selectIdError),
|
||||
otpError: useSelector(service, selectOtpError),
|
||||
|
||||
isInvalid: useSelector(service, selectIsInvalid),
|
||||
isAcceptingOtpInput: useSelector(service, selectIsAcceptingOtpInput),
|
||||
isRequestingOtp: useSelector(service, selectIsRequestingOtp),
|
||||
|
||||
INPUT_ID: (id: string) => service.send(GetVcModalEvents.INPUT_ID(id)),
|
||||
VALIDATE_INPUT: () => service.send(GetVcModalEvents.VALIDATE_INPUT()),
|
||||
INPUT_OTP: (otp: string) => service.send(GetVcModalEvents.INPUT_OTP(otp)),
|
||||
READY: (input: TextInput) => service.send(GetVcModalEvents.READY(input)),
|
||||
DISMISS: () => service.send(GetVcModalEvents.DISMISS()),
|
||||
};
|
||||
}
|
||||
|
||||
export interface GetIdInputModalProps extends ModalProps {
|
||||
service: ActorRefFrom<typeof GetVcModalMachine>;
|
||||
onPress?: () => void;
|
||||
}
|
||||
34
screens/Home/MyVcs/GetVcModal.tsx
Normal file
@@ -0,0 +1,34 @@
|
||||
import React from 'react';
|
||||
import { MessageOverlay } from '../../../components/MessageOverlay';
|
||||
import { useGetVcModal, GetVcModalProps } from './GetVcModalController';
|
||||
import { OtpVerificationModal } from './OtpVerificationModal';
|
||||
import { GetIdInputModal } from './GetIdInputModal';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
export const GetVcModal: React.FC<GetVcModalProps> = (props) => {
|
||||
const { t } = useTranslation('GetVcModal');
|
||||
const controller = useGetVcModal(props);
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<GetIdInputModal
|
||||
service={props.service}
|
||||
isVisible={true}
|
||||
onDismiss={controller.DISMISS}
|
||||
/>
|
||||
|
||||
<OtpVerificationModal
|
||||
isVisible={controller.isAcceptingOtpInput}
|
||||
onDismiss={controller.DISMISS}
|
||||
onInputDone={controller.INPUT_OTP}
|
||||
error={controller.otpError}
|
||||
/>
|
||||
|
||||
<MessageOverlay
|
||||
isVisible={controller.isRequestingCredential}
|
||||
title={t('retrievingId')}
|
||||
hasProgress
|
||||
/>
|
||||
</React.Fragment>
|
||||
);
|
||||
};
|
||||
30
screens/Home/MyVcs/GetVcModalController.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
import { useSelector } from '@xstate/react';
|
||||
import { ActorRefFrom } from 'xstate';
|
||||
import {
|
||||
GetVcModalEvents,
|
||||
GetVcModalMachine,
|
||||
selectIsAcceptingOtpInput,
|
||||
selectIsRequestingCredential,
|
||||
selectOtpError,
|
||||
selectIsAcceptingIdInput,
|
||||
} from './GetVcModalMachine';
|
||||
|
||||
export function useGetVcModal({ service }: GetVcModalProps) {
|
||||
return {
|
||||
isRequestingCredential: useSelector(service, selectIsRequestingCredential),
|
||||
|
||||
otpError: useSelector(service, selectOtpError),
|
||||
|
||||
isAcceptingUinInput: useSelector(service, selectIsAcceptingIdInput),
|
||||
isAcceptingOtpInput: useSelector(service, selectIsAcceptingOtpInput),
|
||||
|
||||
INPUT_OTP: (otp: string) => service.send(GetVcModalEvents.INPUT_OTP(otp)),
|
||||
|
||||
DISMISS: () => service.send(GetVcModalEvents.DISMISS()),
|
||||
};
|
||||
}
|
||||
|
||||
export interface GetVcModalProps {
|
||||
service: ActorRefFrom<typeof GetVcModalMachine>;
|
||||
onPress?: () => void;
|
||||
}
|
||||
334
screens/Home/MyVcs/GetVcModalMachine.ts
Normal file
@@ -0,0 +1,334 @@
|
||||
import { TextInput } from 'react-native';
|
||||
import {
|
||||
assign,
|
||||
ErrorPlatformEvent,
|
||||
EventFrom,
|
||||
DoneInvokeEvent,
|
||||
sendParent,
|
||||
StateFrom,
|
||||
} from 'xstate';
|
||||
import { createModel } from 'xstate/lib/model';
|
||||
import { BackendResponseError, request } from '../../../shared/request';
|
||||
import i18n from '../../../i18n';
|
||||
import { AddVcModalMachine } from './AddVcModalMachine';
|
||||
import { GET_INDIVIDUAL_ID } from '../../../shared/constants';
|
||||
|
||||
const model = createModel(
|
||||
{
|
||||
idInputRef: null as TextInput,
|
||||
id: '',
|
||||
idError: '',
|
||||
otp: '',
|
||||
otpError: '',
|
||||
transactionId: '',
|
||||
child: null,
|
||||
},
|
||||
{
|
||||
events: {
|
||||
INPUT_ID: (id: string) => ({ id }),
|
||||
INPUT_OTP: (otp: string) => ({ otp }),
|
||||
VALIDATE_INPUT: () => ({}),
|
||||
READY: (idInputRef: TextInput) => ({ idInputRef }),
|
||||
DISMISS: () => ({}),
|
||||
GOT_ID: (id: string) => ({ id }),
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
export const GetVcModalEvents = model.events;
|
||||
|
||||
export const GetVcModalMachine =
|
||||
/** @xstate-layout N4IgpgJg5mDOIC5QEEIQGoGMCyB7CAhgDYB0BmmYADgC4CWAdlAJITMNUCuNJATmAwhhejKAGIASgFFkAEQCaiUFVyw69XAyUgAHogC0ARgAsATgDMJAEynjhgBzmArE-v2AbDYA0IAJ4GrAHZ3EkCnKxMrc0NDc1NA+ycAXySfVAwcfGIyCmp6JlZ2Lh4AM1xMTjUmMR1YGgIaMDISxt4ACkMABk6ASjF0rDxCUnJKWlFCjm4SMoqqqG0VNQ0tJF0DWM6QwOjXQydjYyDbH38EIycQ+M6PI6t7TsNTdxS0tEGskdzxgrYpnjoECIYDEzAAcgAFACqABUAPrMWSLVTqOiabR6c6GdxOQIkRydcJRQnudweU6IezGEjGezxKxRQJPTamV4gAaZYY5Mb5Fh-YokQHAsToZAAGURyBhUgRkNhyOWaNWoExRisLmsuPMgSCgUC8XM9gpCCCThI5huNkNpme2qc5jZHKG2VGeQm-OmQpBAGUpGKpABheGIuEw+QQqQK1Hotaq-b2QwkTqmVzBTqBQnmdXGoyGKyheI6xnmYzmMmGR3vTku768yYCxgAN2IgNBcuDSLWS2jyvWWM6ZdCA6c6eMYQtuJz8WshuxHhcVkzDtS7Krzq+PPdRU9DGbRFboolsilMvB0JhUZWGIMpcs+1METHycSnWMxsfSdcZfM2pLFsNlYZOu3Jur824AruLYQGIvr+kGCKyKG4aRl2KJXrGGyEp0JCmPeOKJP+ZbGu4pg4XSOp0q+jgJKyK5Op8IE-Hy4F8GAACOnBwLyADyNBUGIECaE0Ta4AA1k09Fcq6TH1tM-AcVxoi8VQCAiZgDRKgA2p0AC6l5Kte-ZuNYpJ6par6XIExr2DYNI2bigRmDqJKAR8Um1lu-ysQpdRKXxYjCLwuC8CQVBEA0ZS8AAtiQkk1puYFefJnG+Uwymqbu5QaZo2l6ahioxiqBjxPYJAxFseZpnY0TGmYIROA43QPAOpkvHRa4MdJdYejQYiyMw3rYAN3r6YVfb6MY7jUtiWYllY7jYpE7jWWEZVPPcTILQ1FbtUBnUeWlfHgW255wtxMIQqNvZxnE1Iph4i4Wo8LXGqYhJJs1RwPj+32udWG6gVAynHf1g3DVdhkXDsSYJJ0VhmAyDi0rVGopqYVLww8jkmMubx7VyyWKUwAb8EIDD0MQAlCYKmXibFHUE+xKW8iTkACBTRAZY2WUrLlEMYVi2LYfc+xmDaJhklYxpdBaoSixE7gDjcxjJLtbnZITqVQKzZMcwFvBBSFYURcFMVxaQmss6T7N0MQXM81pun80VWKHImMS0sccPuHqUt+IgMTBGVDKLvquLUcYKQrgw+BwNo5uMd1LH8IIwiiM743YvqJBTQ+GZPDstJvv75zzZYYQKz9pYPG1ePqwDMk9TM5SVOn+U9pDsR5kOA4pjirjzU4OZl6E4QzbdhpbH9wFdZ5DZAmAGdxqW1JRM4Nrat0JimMaTJkfSwTGDcWY7XX-2J3PO57oCJBgFFtBnMoaEGQL+g7Hi9g6p49oq735jSzZT8jgEw+1worSOatz6z0Sg2SC+4IDN2ig0JeBhgj5hcIaT++wnhUiNCXEseILQ2TcGEDMP5T6rnxvFQGskILXwQQAI3IOJQQKDzgkUTJmDwOJFY4gsMaEcpEGoPBMGObU7hojT32glZiXkmxQTYW-DwJAMEkOwejZGJdJpAO1HSQ4U1QFSPcjI2h3lmZ+SoIo8qH9nC0gahYBIpJiLahwg+H25FXxxFrpQ+uF8YHcCsWQnOCN84GiLjmUs2wx5mGEdjIx1CmLA3+FYzwpEggPEmgaMueCzj6jxJ7A4r4VbagfPEi2TMiba2tuTW2RArGTXzA4cWJIdieGlo8UqEQDgYPTAQspJBBIMEXu3dCLt9B0ksIrRwtgDi4RtO00scsDiHATCWDJZTAlBGCXnR4YSqQ5kEaPLphgdjpntFSKOSQgA */
|
||||
model.createMachine(
|
||||
{
|
||||
tsTypes: {} as import('./GetVcModalMachine.typegen').Typegen0,
|
||||
schema: {
|
||||
context: model.initialContext,
|
||||
events: {} as EventFrom<typeof model>,
|
||||
},
|
||||
id: 'GetVcModal',
|
||||
initial: 'acceptingIdInput',
|
||||
states: {
|
||||
acceptingIdInput: {
|
||||
entry: ['setTransactionId', 'clearOtp'],
|
||||
initial: 'rendering',
|
||||
states: {
|
||||
rendering: {
|
||||
on: {
|
||||
READY: {
|
||||
actions: 'setIdInputRef',
|
||||
target: 'focusing',
|
||||
},
|
||||
},
|
||||
},
|
||||
focusing: {
|
||||
after: {
|
||||
'100': {
|
||||
description:
|
||||
'Small delay to properly show the keyboard when focusing input.',
|
||||
target: 'idle',
|
||||
},
|
||||
},
|
||||
},
|
||||
idle: {
|
||||
entry: 'focusInput',
|
||||
invoke: {
|
||||
id: 'AddVcModal',
|
||||
src: AddVcModalMachine,
|
||||
},
|
||||
on: {
|
||||
INPUT_ID: {
|
||||
actions: 'setId',
|
||||
},
|
||||
VALIDATE_INPUT: [
|
||||
{
|
||||
cond: 'isEmptyId',
|
||||
target: '#GetVcModal.acceptingIdInput.invalid.empty',
|
||||
},
|
||||
{
|
||||
cond: 'isWrongIdFormat',
|
||||
target: '#GetVcModal.acceptingIdInput.invalid.format',
|
||||
},
|
||||
{
|
||||
target: 'requestingOtp',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
invalid: {
|
||||
entry: 'focusInput',
|
||||
states: {
|
||||
empty: {
|
||||
entry: 'setIdErrorEmpty',
|
||||
},
|
||||
format: {
|
||||
entry: 'setIdErrorWrongFormat',
|
||||
},
|
||||
backend: {},
|
||||
},
|
||||
on: {
|
||||
INPUT_ID: {
|
||||
actions: ['setId', 'clearIdError'],
|
||||
target: 'idle',
|
||||
},
|
||||
VALIDATE_INPUT: [
|
||||
{
|
||||
cond: 'isEmptyId',
|
||||
target: '.empty',
|
||||
},
|
||||
{
|
||||
cond: 'isWrongIdFormat',
|
||||
target: '.format',
|
||||
},
|
||||
{
|
||||
target: 'requestingOtp',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
requestingOtp: {
|
||||
invoke: {
|
||||
src: 'requestOtp',
|
||||
onDone: [
|
||||
{
|
||||
target: '#GetVcModal.acceptingOtpInput',
|
||||
},
|
||||
],
|
||||
onError: [
|
||||
{
|
||||
actions: 'setIdBackendError',
|
||||
target: '#GetVcModal.acceptingIdInput.invalid.backend',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
on: {
|
||||
DISMISS: {
|
||||
actions: 'forwardToParent',
|
||||
},
|
||||
},
|
||||
},
|
||||
acceptingOtpInput: {
|
||||
entry: 'clearOtp',
|
||||
on: {
|
||||
INPUT_OTP: {
|
||||
actions: 'setOtp',
|
||||
target: 'requestingUinVid',
|
||||
},
|
||||
DISMISS: {
|
||||
target: '#GetVcModal.acceptingIdInput.idle',
|
||||
},
|
||||
},
|
||||
},
|
||||
requestingUinVid: {
|
||||
invoke: {
|
||||
src: 'requestingUinVid',
|
||||
onDone: [
|
||||
{
|
||||
actions: ['setIndividualId'],
|
||||
target: 'done',
|
||||
},
|
||||
],
|
||||
onError: [
|
||||
{
|
||||
actions: 'setIdBackendError',
|
||||
cond: 'isIdInvalid',
|
||||
target: '#GetVcModal.acceptingIdInput.invalid.backend',
|
||||
},
|
||||
{
|
||||
actions: 'setOtpError',
|
||||
target: 'acceptingOtpInput',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
done: {
|
||||
type: 'final',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
actions: {
|
||||
forwardToParent: sendParent('DISMISS'),
|
||||
|
||||
setId: model.assign({
|
||||
id: (_context, event) => event.id,
|
||||
}),
|
||||
|
||||
setOtp: model.assign({
|
||||
otp: (_context, event) => event.otp,
|
||||
}),
|
||||
|
||||
setTransactionId: assign({
|
||||
transactionId: () => String(new Date().valueOf()).substring(3, 13),
|
||||
}),
|
||||
|
||||
setIndividualId: (_context, event) =>
|
||||
GET_INDIVIDUAL_ID((event as DoneInvokeEvent<string>).data),
|
||||
|
||||
setIdBackendError: assign({
|
||||
idError: (context, event) => {
|
||||
if ((event as ErrorPlatformEvent).data == 'IDA-MLC-001') {
|
||||
return i18n.t('errors.backend.timeOut', { ns: 'GetVcModal' });
|
||||
}
|
||||
|
||||
const message = (event as ErrorPlatformEvent).data.message;
|
||||
const ID_ERRORS_MAP = {
|
||||
'AID is not ready': 'applicationProcessing',
|
||||
};
|
||||
return ID_ERRORS_MAP[message]
|
||||
? i18n.t(`errors.backend.${ID_ERRORS_MAP[message]}`, {
|
||||
ns: 'GetVcModal',
|
||||
})
|
||||
: message;
|
||||
},
|
||||
}),
|
||||
|
||||
clearIdError: model.assign({ idError: '' }),
|
||||
|
||||
setIdErrorEmpty: model.assign({
|
||||
idError: () => i18n.t('errors.input.empty', { ns: 'GetVcModal' }),
|
||||
}),
|
||||
|
||||
setIdErrorWrongFormat: model.assign({
|
||||
idError: () =>
|
||||
i18n.t('errors.input.invalidFormat', { ns: 'GetVcModal' }),
|
||||
}),
|
||||
|
||||
setOtpError: assign({
|
||||
otpError: (_context, event) => {
|
||||
const message = (event as ErrorPlatformEvent).data.message;
|
||||
const OTP_ERRORS_MAP = {
|
||||
'OTP is invalid': 'invalidOtp',
|
||||
};
|
||||
return OTP_ERRORS_MAP[message]
|
||||
? i18n.t(`errors.backend.${OTP_ERRORS_MAP[message]}`, {
|
||||
ns: 'GetVcModal',
|
||||
})
|
||||
: message;
|
||||
},
|
||||
}),
|
||||
|
||||
setIdInputRef: model.assign({
|
||||
idInputRef: (_context, event) => event.idInputRef,
|
||||
}),
|
||||
|
||||
clearOtp: assign({ otp: '' }),
|
||||
|
||||
focusInput: (context) => context.idInputRef.focus(),
|
||||
},
|
||||
|
||||
services: {
|
||||
requestOtp: async (context) => {
|
||||
return await request('POST', '/req/individualId/otp', {
|
||||
id: 'mosip.identity.otp.internal',
|
||||
aid: context.id,
|
||||
metadata: {},
|
||||
otpChannel: ['EMAIL', 'PHONE'],
|
||||
requestTime: String(new Date().toISOString()),
|
||||
transactionID: context.transactionId,
|
||||
version: '1.0',
|
||||
});
|
||||
},
|
||||
|
||||
requestingUinVid: async (context) => {
|
||||
const response = await request('POST', '/aid/get-individual-id', {
|
||||
aid: context.id,
|
||||
otp: context.otp,
|
||||
transactionID: context.transactionId,
|
||||
});
|
||||
return response.response.individualId;
|
||||
},
|
||||
},
|
||||
|
||||
guards: {
|
||||
isEmptyId: ({ id }) => !id || !id.length,
|
||||
|
||||
isWrongIdFormat: ({ id }) => !/^\d{14,29}$/.test(id),
|
||||
|
||||
isIdInvalid: (_context, event: unknown) =>
|
||||
['RES-SER-449', 'IDA-MLC-001'].includes(
|
||||
(event as BackendResponseError).name
|
||||
),
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
type State = StateFrom<typeof GetVcModalMachine>;
|
||||
|
||||
export function selectId(state: State) {
|
||||
return state.context.id;
|
||||
}
|
||||
|
||||
export function selectIdInputRef(state: State) {
|
||||
return state.context.idInputRef;
|
||||
}
|
||||
|
||||
export function selectIdError(state: State) {
|
||||
return state.context.idError;
|
||||
}
|
||||
|
||||
export function selectOtpError(state: State) {
|
||||
return state.context.otpError;
|
||||
}
|
||||
|
||||
export function selectIsAcceptingIdInput(state: State) {
|
||||
return state.matches('acceptingIdInput');
|
||||
}
|
||||
|
||||
export function selectIsInvalid(state: State) {
|
||||
return state.matches('acceptingIdInput.invalid');
|
||||
}
|
||||
|
||||
export function selectIsAcceptingOtpInput(state: State) {
|
||||
return state.matches('acceptingOtpInput');
|
||||
}
|
||||
|
||||
export function selectIsRequestingOtp(state: State) {
|
||||
return state.matches('acceptingIdInput.requestingOtp');
|
||||
}
|
||||
|
||||
export function selectIsRequestingCredential(state: State) {
|
||||
return state.matches('requestingUinVid');
|
||||
}
|
||||
102
screens/Home/MyVcs/GetVcModalMachine.typegen.ts
Normal file
@@ -0,0 +1,102 @@
|
||||
// This file was automatically generated. Edits will be overwritten
|
||||
|
||||
export interface Typegen0 {
|
||||
'@@xstate/typegen': true;
|
||||
'internalEvents': {
|
||||
'done.invoke.GetVcModal.acceptingIdInput.requestingOtp:invocation[0]': {
|
||||
type: 'done.invoke.GetVcModal.acceptingIdInput.requestingOtp:invocation[0]';
|
||||
data: unknown;
|
||||
__tip: 'See the XState TS docs to learn how to strongly type this.';
|
||||
};
|
||||
'done.invoke.GetVcModal.requestingUinVid:invocation[0]': {
|
||||
type: 'done.invoke.GetVcModal.requestingUinVid:invocation[0]';
|
||||
data: unknown;
|
||||
__tip: 'See the XState TS docs to learn how to strongly type this.';
|
||||
};
|
||||
'error.platform.GetVcModal.acceptingIdInput.requestingOtp:invocation[0]': {
|
||||
type: 'error.platform.GetVcModal.acceptingIdInput.requestingOtp:invocation[0]';
|
||||
data: unknown;
|
||||
};
|
||||
'error.platform.GetVcModal.requestingUinVid:invocation[0]': {
|
||||
type: 'error.platform.GetVcModal.requestingUinVid:invocation[0]';
|
||||
data: unknown;
|
||||
};
|
||||
'xstate.after(100)#GetVcModal.acceptingIdInput.focusing': {
|
||||
type: 'xstate.after(100)#GetVcModal.acceptingIdInput.focusing';
|
||||
};
|
||||
'xstate.init': { type: 'xstate.init' };
|
||||
};
|
||||
'invokeSrcNameMap': {
|
||||
requestOtp: 'done.invoke.GetVcModal.acceptingIdInput.requestingOtp:invocation[0]';
|
||||
requestingUinVid: 'done.invoke.GetVcModal.requestingUinVid:invocation[0]';
|
||||
};
|
||||
'missingImplementations': {
|
||||
actions: never;
|
||||
services: never;
|
||||
guards: never;
|
||||
delays: never;
|
||||
};
|
||||
'eventsCausingActions': {
|
||||
clearIdError: 'INPUT_ID';
|
||||
clearOtp:
|
||||
| 'DISMISS'
|
||||
| 'done.invoke.GetVcModal.acceptingIdInput.requestingOtp:invocation[0]'
|
||||
| 'error.platform.GetVcModal.requestingUinVid:invocation[0]'
|
||||
| 'xstate.init';
|
||||
focusInput:
|
||||
| 'DISMISS'
|
||||
| 'INPUT_ID'
|
||||
| 'VALIDATE_INPUT'
|
||||
| 'error.platform.GetVcModal.acceptingIdInput.requestingOtp:invocation[0]'
|
||||
| 'error.platform.GetVcModal.requestingUinVid:invocation[0]'
|
||||
| 'xstate.after(100)#GetVcModal.acceptingIdInput.focusing';
|
||||
forwardToParent: 'DISMISS';
|
||||
setId: 'INPUT_ID';
|
||||
setIdBackendError:
|
||||
| 'error.platform.GetVcModal.acceptingIdInput.requestingOtp:invocation[0]'
|
||||
| 'error.platform.GetVcModal.requestingUinVid:invocation[0]';
|
||||
setIdErrorEmpty: 'VALIDATE_INPUT';
|
||||
setIdErrorWrongFormat: 'VALIDATE_INPUT';
|
||||
setIdInputRef: 'READY';
|
||||
setIndividualId: 'done.invoke.GetVcModal.requestingUinVid:invocation[0]';
|
||||
setOtp: 'INPUT_OTP';
|
||||
setOtpError: 'error.platform.GetVcModal.requestingUinVid:invocation[0]';
|
||||
setTransactionId:
|
||||
| 'DISMISS'
|
||||
| 'error.platform.GetVcModal.requestingUinVid:invocation[0]'
|
||||
| 'xstate.init';
|
||||
};
|
||||
'eventsCausingServices': {
|
||||
requestOtp: 'VALIDATE_INPUT';
|
||||
requestingUinVid: 'INPUT_OTP';
|
||||
};
|
||||
'eventsCausingGuards': {
|
||||
isEmptyId: 'VALIDATE_INPUT';
|
||||
isIdInvalid: 'error.platform.GetVcModal.requestingUinVid:invocation[0]';
|
||||
isWrongIdFormat: 'VALIDATE_INPUT';
|
||||
};
|
||||
'eventsCausingDelays': {};
|
||||
'matchesStates':
|
||||
| 'acceptingIdInput'
|
||||
| 'acceptingIdInput.focusing'
|
||||
| 'acceptingIdInput.idle'
|
||||
| 'acceptingIdInput.invalid'
|
||||
| 'acceptingIdInput.invalid.backend'
|
||||
| 'acceptingIdInput.invalid.empty'
|
||||
| 'acceptingIdInput.invalid.format'
|
||||
| 'acceptingIdInput.rendering'
|
||||
| 'acceptingIdInput.requestingOtp'
|
||||
| 'acceptingOtpInput'
|
||||
| 'done'
|
||||
| 'requestingUinVid'
|
||||
| {
|
||||
acceptingIdInput?:
|
||||
| 'focusing'
|
||||
| 'idle'
|
||||
| 'invalid'
|
||||
| 'rendering'
|
||||
| 'requestingOtp'
|
||||
| { invalid?: 'backend' | 'empty' | 'format' };
|
||||
};
|
||||
'tags': never;
|
||||
}
|
||||
@@ -3,19 +3,34 @@ import { Icon, Input } from 'react-native-elements';
|
||||
import { Picker } from '@react-native-picker/picker';
|
||||
import { Button, Column, Row, Text } from '../../../components/ui';
|
||||
import { Modal } from '../../../components/ui/Modal';
|
||||
import { Colors } from '../../../components/ui/styleUtils';
|
||||
import { Theme } from '../../../components/ui/styleUtils';
|
||||
import { IdInputModalProps, useIdInputModal } from './IdInputModalController';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { KeyboardAvoidingView, Platform } from 'react-native';
|
||||
import { TouchableOpacity } from 'react-native';
|
||||
import { individualId } from '../../../shared/constants';
|
||||
import { GET_INDIVIDUAL_ID } from '../../../shared/constants';
|
||||
|
||||
export const IdInputModal: React.FC<IdInputModalProps> = (props) => {
|
||||
const { t } = useTranslation('IdInputModal');
|
||||
const controller = useIdInputModal(props);
|
||||
|
||||
const setIndividualID = () => {
|
||||
controller.INPUT_ID(individualId);
|
||||
};
|
||||
|
||||
const dismissInput = () => {
|
||||
props.onDismiss();
|
||||
GET_INDIVIDUAL_ID('');
|
||||
};
|
||||
|
||||
const inputLabel = t('enterId', { idType: controller.idType });
|
||||
|
||||
return (
|
||||
<Modal onDismiss={props.onDismiss} isVisible={props.isVisible}>
|
||||
<Modal
|
||||
onDismiss={dismissInput}
|
||||
isVisible={props.isVisible}
|
||||
onShow={setIndividualID}>
|
||||
<KeyboardAvoidingView
|
||||
style={{ flex: 1 }}
|
||||
behavior={Platform.OS === 'ios' ? 'padding' : 'height'}>
|
||||
@@ -30,7 +45,9 @@ export const IdInputModal: React.FC<IdInputModalProps> = (props) => {
|
||||
style={{
|
||||
borderBottomWidth: 1,
|
||||
borderColor:
|
||||
Platform.OS === 'ios' ? 'transparent' : Colors.Grey,
|
||||
Platform.OS === 'ios'
|
||||
? 'transparent'
|
||||
: Theme.Colors.IdInputModalBorder,
|
||||
bottom: Platform.OS === 'ios' ? 50 : 24,
|
||||
height: Platform.OS === 'ios' ? 100 : 'auto',
|
||||
}}>
|
||||
@@ -46,7 +63,9 @@ export const IdInputModal: React.FC<IdInputModalProps> = (props) => {
|
||||
placeholder={!controller.id ? inputLabel : ''}
|
||||
label={controller.id ? inputLabel : ''}
|
||||
labelStyle={{
|
||||
color: controller.isInvalid ? Colors.Red : Colors.Black,
|
||||
color: controller.isInvalid
|
||||
? Theme.Colors.errorMessage
|
||||
: Theme.Colors.textValue,
|
||||
}}
|
||||
value={controller.id}
|
||||
keyboardType="number-pad"
|
||||
@@ -55,7 +74,7 @@ export const IdInputModal: React.FC<IdInputModalProps> = (props) => {
|
||||
<Icon name="error" size={18} color="red" />
|
||||
) : null
|
||||
}
|
||||
errorStyle={{ color: Colors.Red }}
|
||||
errorStyle={{ color: Theme.Colors.errorMessage }}
|
||||
errorMessage={controller.idError}
|
||||
onChangeText={controller.INPUT_ID}
|
||||
ref={(node) =>
|
||||
@@ -70,6 +89,14 @@ export const IdInputModal: React.FC<IdInputModalProps> = (props) => {
|
||||
onPress={controller.VALIDATE_INPUT}
|
||||
loading={controller.isRequestingOtp}
|
||||
/>
|
||||
{!controller.id && (
|
||||
<TouchableOpacity
|
||||
activeOpacity={1}
|
||||
onPress={props.onPress}
|
||||
style={Theme.Styles.getId}>
|
||||
<Text color={Theme.Colors.AddIdBtnBg}>{t('noUIN/VID')}</Text>
|
||||
</TouchableOpacity>
|
||||
)}
|
||||
</Column>
|
||||
</Column>
|
||||
</KeyboardAvoidingView>
|
||||
|
||||
@@ -47,4 +47,5 @@ export function useIdInputModal({ service }: IdInputModalProps) {
|
||||
|
||||
export interface IdInputModalProps extends ModalProps {
|
||||
service: ActorRefFrom<typeof AddVcModalMachine>;
|
||||
onPress?: () => void;
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ import { Icon } from 'react-native-elements';
|
||||
import { PinInput } from '../../../components/PinInput';
|
||||
import { Column, Text } from '../../../components/ui';
|
||||
import { Modal, ModalProps } from '../../../components/ui/Modal';
|
||||
import { Colors } from '../../../components/ui/styleUtils';
|
||||
import { Theme } from '../../../components/ui/styleUtils';
|
||||
|
||||
export const OtpVerificationModal: React.FC<OtpVerificationModalProps> = (
|
||||
props
|
||||
@@ -14,10 +14,13 @@ export const OtpVerificationModal: React.FC<OtpVerificationModalProps> = (
|
||||
return (
|
||||
<Modal isVisible={props.isVisible} onDismiss={props.onDismiss}>
|
||||
<Column fill padding="32">
|
||||
<Icon name="lock" color={Colors.Orange} size={60} />
|
||||
<Icon name="lock" color={Theme.Colors.Icon} size={60} />
|
||||
<Column fill align="space-between">
|
||||
<Text align="center">{t('enterOtp')}</Text>
|
||||
<Text align="center" color={Colors.Red} margin="16 0 0 0">
|
||||
<Text
|
||||
align="center"
|
||||
color={Theme.Colors.errorMessage}
|
||||
margin="16 0 0 0">
|
||||
{props.error}
|
||||
</Text>
|
||||
<PinInput length={6} onDone={props.onInputDone} />
|
||||
|
||||
@@ -1,20 +1,32 @@
|
||||
import React from 'react';
|
||||
import { Button, Column, Text, Centered } from '../../components/ui';
|
||||
import { VcItem } from '../../components/VcItem';
|
||||
import { Icon } from 'react-native-elements';
|
||||
import { Colors } from '../../components/ui/styleUtils';
|
||||
import { Theme } from '../../components/ui/styleUtils';
|
||||
import { RefreshControl } from 'react-native';
|
||||
import { useMyVcsTab } from './MyVcsTabController';
|
||||
import { HomeScreenTabProps } from './HomeScreen';
|
||||
import { AddVcModal } from './MyVcs/AddVcModal';
|
||||
import { GetVcModal } from './MyVcs/GetVcModal';
|
||||
import { DownloadingVcModal } from './MyVcs/DownloadingVcModal';
|
||||
import { OnboardingOverlay } from './OnboardingOverlay';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import GestureRecognizer from 'react-native-swipe-gestures';
|
||||
import { UpdatedVcItem } from '../../components/UpdatedVcItem';
|
||||
import { GET_INDIVIDUAL_ID } from '../../shared/constants';
|
||||
|
||||
export const MyVcsTab: React.FC<HomeScreenTabProps> = (props) => {
|
||||
const { t } = useTranslation('MyVcsTab');
|
||||
const controller = useMyVcsTab(props);
|
||||
|
||||
const getId = () => {
|
||||
controller.DISMISS();
|
||||
controller.GET_VC();
|
||||
};
|
||||
|
||||
const clearIndividualId = () => {
|
||||
GET_INDIVIDUAL_ID('');
|
||||
};
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<Column fill style={{ display: props.isVisible ? 'flex' : 'none' }}>
|
||||
@@ -23,6 +35,7 @@ export const MyVcsTab: React.FC<HomeScreenTabProps> = (props) => {
|
||||
<React.Fragment>
|
||||
<Column
|
||||
scroll
|
||||
margin="0 0 20 0"
|
||||
refreshControl={
|
||||
<RefreshControl
|
||||
refreshing={controller.isRefreshingVcs}
|
||||
@@ -30,23 +43,25 @@ export const MyVcsTab: React.FC<HomeScreenTabProps> = (props) => {
|
||||
/>
|
||||
}>
|
||||
{controller.vcKeys.map((vcKey) => (
|
||||
<VcItem
|
||||
<UpdatedVcItem
|
||||
key={vcKey}
|
||||
vcKey={vcKey}
|
||||
margin="0 2 8 2"
|
||||
onPress={controller.VIEW_VC}
|
||||
/>
|
||||
))}
|
||||
</Column>
|
||||
<Column elevation={2} margin="0 2">
|
||||
<Button
|
||||
type="clear"
|
||||
disabled={controller.isRefreshingVcs}
|
||||
title={t('addVcButton', {
|
||||
vcLabel: controller.vcLabel.singular,
|
||||
})}
|
||||
onPress={controller.ADD_VC}
|
||||
/>
|
||||
<GestureRecognizer onSwipeLeft={props.onSwipe}>
|
||||
<Column elevation={2} margin="10 2 0 2">
|
||||
<Button
|
||||
type="clear"
|
||||
disabled={controller.isRefreshingVcs}
|
||||
title={t('addVcButton', {
|
||||
vcLabel: controller.vcLabel.singular,
|
||||
})}
|
||||
onPress={controller.ADD_VC}
|
||||
/>
|
||||
</Column>
|
||||
</GestureRecognizer>
|
||||
</Column>
|
||||
</React.Fragment>
|
||||
)}
|
||||
@@ -56,7 +71,7 @@ export const MyVcsTab: React.FC<HomeScreenTabProps> = (props) => {
|
||||
<Text weight="semibold" margin="0 0 8 0">
|
||||
{t('generateVc', { vcLabel: controller.vcLabel.plural })}
|
||||
</Text>
|
||||
<Text color={Colors.Grey} align="center">
|
||||
<Text color={Theme.Colors.textLabel} align="center">
|
||||
{t('generateVcDescription', {
|
||||
vcLabel: controller.vcLabel.singular,
|
||||
})}
|
||||
@@ -64,28 +79,38 @@ export const MyVcsTab: React.FC<HomeScreenTabProps> = (props) => {
|
||||
<Icon
|
||||
name="arrow-downward"
|
||||
containerStyle={{ marginTop: 20 }}
|
||||
color={Colors.Orange}
|
||||
color={Theme.Colors.Icon}
|
||||
/>
|
||||
</Centered>
|
||||
<Button
|
||||
disabled={controller.isRefreshingVcs}
|
||||
title={t('addVcButton', {
|
||||
vcLabel: controller.vcLabel.singular,
|
||||
})}
|
||||
onPress={controller.ADD_VC}
|
||||
/>
|
||||
|
||||
<GestureRecognizer onSwipeLeft={props.onSwipe}>
|
||||
<Button
|
||||
type="addId"
|
||||
disabled={controller.isRefreshingVcs}
|
||||
title={t('addVcButton', {
|
||||
vcLabel: controller.vcLabel.singular,
|
||||
})}
|
||||
onPress={controller.ADD_VC}
|
||||
/>
|
||||
</GestureRecognizer>
|
||||
</React.Fragment>
|
||||
)}
|
||||
</Column>
|
||||
</Column>
|
||||
|
||||
{controller.AddVcModalService && (
|
||||
<AddVcModal service={controller.AddVcModalService} />
|
||||
<AddVcModal service={controller.AddVcModalService} onPress={getId} />
|
||||
)}
|
||||
|
||||
{controller.GetVcModalService && (
|
||||
<GetVcModal service={controller.GetVcModalService} />
|
||||
)}
|
||||
|
||||
{controller.isRequestSuccessful && (
|
||||
<DownloadingVcModal
|
||||
isVisible={controller.isRequestSuccessful}
|
||||
onDismiss={controller.DISMISS}
|
||||
onShow={clearIndividualId}
|
||||
/>
|
||||
)}
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ import {
|
||||
selectMyVcs,
|
||||
VcEvents,
|
||||
} from '../../machines/vc';
|
||||
import { vcItemMachine } from '../../machines/vcItem';
|
||||
import { vcItemMachine } from '../../machines/vcItem';
|
||||
import { GlobalContext } from '../../shared/GlobalContext';
|
||||
import { HomeScreenTabProps } from './HomeScreen';
|
||||
import {
|
||||
@@ -16,6 +16,7 @@ import {
|
||||
selectAddVcModal,
|
||||
selectIsOnboarding,
|
||||
selectIsRequestSuccessful,
|
||||
selectGetVcModal,
|
||||
} from './MyVcsTabMachine';
|
||||
|
||||
export function useMyVcsTab(props: HomeScreenTabProps) {
|
||||
@@ -27,6 +28,7 @@ export function useMyVcsTab(props: HomeScreenTabProps) {
|
||||
return {
|
||||
service,
|
||||
AddVcModalService: useSelector(service, selectAddVcModal),
|
||||
GetVcModalService: useSelector(service, selectGetVcModal),
|
||||
|
||||
vcKeys: useSelector(vcService, selectMyVcs),
|
||||
vcLabel: useSelector(settingsService, selectVcLabel),
|
||||
@@ -39,9 +41,11 @@ export function useMyVcsTab(props: HomeScreenTabProps) {
|
||||
|
||||
ADD_VC: () => service.send(MyVcsTabEvents.ADD_VC()),
|
||||
|
||||
GET_VC: () => service.send(MyVcsTabEvents.GET_VC()),
|
||||
|
||||
REFRESH: () => vcService.send(VcEvents.REFRESH_MY_VCS()),
|
||||
|
||||
VIEW_VC: (vcRef: ActorRefFrom<typeof vcItemMachine>) => {
|
||||
VIEW_VC: (vcRef: ActorRefFrom<typeof vcItemMachine>) => {
|
||||
return service.send(MyVcsTabEvents.VIEW_VC(vcRef));
|
||||
},
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@ import {
|
||||
ONBOARDING_STATUS_STORE_KEY,
|
||||
} from '../../shared/constants';
|
||||
import { AddVcModalMachine } from './MyVcs/AddVcModalMachine';
|
||||
import { GetVcModalMachine } from './MyVcs/GetVcModalMachine';
|
||||
|
||||
const model = createModel(
|
||||
{
|
||||
@@ -30,6 +31,7 @@ const model = createModel(
|
||||
DISMISS: () => ({}),
|
||||
STORE_RESPONSE: (response?: unknown) => ({ response }),
|
||||
ADD_VC: () => ({}),
|
||||
GET_VC: () => ({}),
|
||||
ONBOARDING_DONE: () => ({}),
|
||||
},
|
||||
}
|
||||
@@ -75,6 +77,7 @@ export const MyVcsTabMachine = model.createMachine(
|
||||
on: {
|
||||
ADD_VC: 'addingVc',
|
||||
VIEW_VC: 'viewingVc',
|
||||
GET_VC: 'gettingVc',
|
||||
},
|
||||
},
|
||||
viewingVc: {
|
||||
@@ -111,6 +114,20 @@ export const MyVcsTabMachine = model.createMachine(
|
||||
},
|
||||
},
|
||||
},
|
||||
gettingVc: {
|
||||
invoke: {
|
||||
id: 'GetVcModal',
|
||||
src: GetVcModalMachine,
|
||||
onDone: 'addingVc',
|
||||
},
|
||||
on: {
|
||||
DISMISS: 'idle',
|
||||
},
|
||||
initial: 'waitingForvcKey',
|
||||
states: {
|
||||
waitingForvcKey: {},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -168,6 +185,10 @@ export function selectAddVcModal(state: State) {
|
||||
return state.children.AddVcModal as ActorRefFrom<typeof AddVcModalMachine>;
|
||||
}
|
||||
|
||||
export function selectGetVcModal(state: State) {
|
||||
return state.children.GetVcModal as ActorRefFrom<typeof GetVcModalMachine>;
|
||||
}
|
||||
|
||||
export function selectIsOnboarding(state: State) {
|
||||
return state.matches('onboarding');
|
||||
}
|
||||
|
||||
@@ -1,58 +1,14 @@
|
||||
import React, { useRef, useContext } from 'react';
|
||||
import AppIntroSlider from 'react-native-app-intro-slider';
|
||||
import { SafeAreaView, ScrollView, StyleSheet, View } from 'react-native';
|
||||
import { SafeAreaView, ScrollView, View } from 'react-native';
|
||||
import { Icon, Overlay } from 'react-native-elements';
|
||||
import { Button, Column, Text } from '../../components/ui';
|
||||
import { Colors } from '../../components/ui/styleUtils';
|
||||
import { Theme } from '../../components/ui/styleUtils';
|
||||
import { useSelector } from '@xstate/react';
|
||||
import { GlobalContext } from '../../shared/GlobalContext';
|
||||
import { selectVcLabel } from '../../machines/settings';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
overlay: {
|
||||
padding: 24,
|
||||
bottom: 86,
|
||||
backgroundColor: 'transparent',
|
||||
shadowColor: 'transparent',
|
||||
},
|
||||
slide: {
|
||||
width: '100%',
|
||||
padding: 20,
|
||||
},
|
||||
slider: {
|
||||
backgroundColor: Colors.Orange,
|
||||
minHeight: 300,
|
||||
width: '100%',
|
||||
margin: 0,
|
||||
borderRadius: 4,
|
||||
},
|
||||
appSlider: {},
|
||||
title: {
|
||||
color: Colors.White,
|
||||
marginBottom: 20,
|
||||
fontFamily: 'Poppins_700Bold',
|
||||
},
|
||||
text: {
|
||||
color: Colors.White,
|
||||
},
|
||||
paginationContainer: {
|
||||
margin: 10,
|
||||
},
|
||||
paginationDots: {
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
marginBottom: 20,
|
||||
},
|
||||
closeIcon: {
|
||||
alignItems: 'flex-end',
|
||||
end: 16,
|
||||
top: 40,
|
||||
zIndex: 1,
|
||||
},
|
||||
});
|
||||
|
||||
export const OnboardingOverlay: React.FC<OnboardingProps> = (props) => {
|
||||
const slider = useRef<AppIntroSlider>();
|
||||
|
||||
@@ -90,10 +46,12 @@ export const OnboardingOverlay: React.FC<OnboardingProps> = (props) => {
|
||||
|
||||
const renderItem = ({ item }) => {
|
||||
return (
|
||||
<View style={styles.slide}>
|
||||
<View style={Theme.OnboardingOverlayStyles.slide}>
|
||||
<ScrollView showsVerticalScrollIndicator={true}>
|
||||
<Text style={styles.title}>{item.title}</Text>
|
||||
<Text style={styles.text}>{item.text}</Text>
|
||||
<Text style={Theme.OnboardingOverlayStyles.sliderTitle}>
|
||||
{item.title}
|
||||
</Text>
|
||||
<Text style={Theme.OnboardingOverlayStyles.text}>{item.text}</Text>
|
||||
{item.footer}
|
||||
</ScrollView>
|
||||
</View>
|
||||
@@ -102,14 +60,14 @@ export const OnboardingOverlay: React.FC<OnboardingProps> = (props) => {
|
||||
|
||||
const renderPagination = (activeIndex: number) => {
|
||||
return (
|
||||
<View style={styles.paginationContainer}>
|
||||
<View style={Theme.OnboardingOverlayStyles.paginationContainer}>
|
||||
<SafeAreaView>
|
||||
<View style={styles.paginationDots}>
|
||||
<View style={Theme.OnboardingOverlayStyles.paginationDots}>
|
||||
{slides.length > 1 &&
|
||||
slides.map((_, i) => (
|
||||
<Icon
|
||||
key={i}
|
||||
color={Colors.White}
|
||||
color={Theme.Colors.OnboardingCircleIcon}
|
||||
size={10}
|
||||
name="circle"
|
||||
style={{ opacity: i === activeIndex ? 1 : 0.6, margin: 2 }}
|
||||
@@ -124,21 +82,21 @@ export const OnboardingOverlay: React.FC<OnboardingProps> = (props) => {
|
||||
return (
|
||||
<Overlay
|
||||
isVisible={props.isVisible}
|
||||
overlayStyle={styles.overlay}
|
||||
overlayStyle={Theme.OnboardingOverlayStyles.overlay}
|
||||
transparent
|
||||
onBackdropPress={props.onDone}>
|
||||
<Column fill align="flex-end">
|
||||
<Icon
|
||||
name="close"
|
||||
color={Colors.White}
|
||||
color={Theme.Colors.OnboardingCloseIcon}
|
||||
onPress={props.onDone}
|
||||
containerStyle={styles.closeIcon}
|
||||
containerStyle={Theme.OnboardingOverlayStyles.closeIcon}
|
||||
/>
|
||||
<View style={styles.slider}>
|
||||
<View style={Theme.OnboardingOverlayStyles.slider}>
|
||||
<AppIntroSlider
|
||||
renderItem={renderItem}
|
||||
data={slides}
|
||||
style={styles.appSlider}
|
||||
style={Theme.OnboardingOverlayStyles.appSlider}
|
||||
ref={slider}
|
||||
renderPagination={renderPagination}
|
||||
/>
|
||||
|
||||
@@ -3,10 +3,10 @@ import { useTranslation } from 'react-i18next';
|
||||
import { RefreshControl } from 'react-native';
|
||||
import { Icon } from 'react-native-elements';
|
||||
import { Centered, Column, Text } from '../../components/ui';
|
||||
import { Colors } from '../../components/ui/styleUtils';
|
||||
import { VcItem } from '../../components/VcItem';
|
||||
import { Theme } from '../../components/ui/styleUtils';
|
||||
import { HomeScreenTabProps } from './HomeScreen';
|
||||
import { useReceivedVcsTab } from './ReceivedVcsTabController';
|
||||
import { UpdatedVcItem } from '../../components/UpdatedVcItem';
|
||||
|
||||
export const ReceivedVcsTab: React.FC<HomeScreenTabProps> = (props) => {
|
||||
const { t } = useTranslation('ReceivedVcsTab');
|
||||
@@ -24,7 +24,7 @@ export const ReceivedVcsTab: React.FC<HomeScreenTabProps> = (props) => {
|
||||
/>
|
||||
}>
|
||||
{controller.vcKeys.map((vcKey) => (
|
||||
<VcItem
|
||||
<UpdatedVcItem
|
||||
key={vcKey}
|
||||
vcKey={vcKey}
|
||||
margin="0 2 8 2"
|
||||
@@ -44,7 +44,7 @@ export const ReceivedVcsTab: React.FC<HomeScreenTabProps> = (props) => {
|
||||
vcLabel: controller.vcLabel.plural,
|
||||
})}
|
||||
</Text>
|
||||
<Text align="center" color={Colors.Grey}>
|
||||
<Text align="center" color={Theme.Colors.textLabel}>
|
||||
{t('noReceivedVcsText', {
|
||||
vcLabel: controller.vcLabel.singular,
|
||||
})}
|
||||
|
||||
@@ -3,14 +3,14 @@ import { Icon } from 'react-native-elements';
|
||||
import { TextEditOverlay } from '../../components/TextEditOverlay';
|
||||
import { Column } from '../../components/ui';
|
||||
import { Modal } from '../../components/ui/Modal';
|
||||
import { Colors } from '../../components/ui/styleUtils';
|
||||
import { VcDetails } from '../../components/VcDetails';
|
||||
import { Theme } from '../../components/ui/styleUtils';
|
||||
import { MessageOverlay } from '../../components/MessageOverlay';
|
||||
import { ToastItem } from '../../components/ui/ToastItem';
|
||||
import { Passcode } from '../../components/Passcode';
|
||||
import { OtpVerificationModal } from './MyVcs/OtpVerificationModal';
|
||||
import { useViewVcModal, ViewVcModalProps } from './ViewVcModalController';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { UpdatedVcDetails } from '../../components/UpdatedVcDetails';
|
||||
|
||||
export const ViewVcModal: React.FC<ViewVcModalProps> = (props) => {
|
||||
const { t } = useTranslation('ViewVcModal');
|
||||
@@ -23,11 +23,15 @@ export const ViewVcModal: React.FC<ViewVcModalProps> = (props) => {
|
||||
headerTitle={controller.vc.tag || controller.vc.id}
|
||||
headerElevation={2}
|
||||
headerRight={
|
||||
<Icon name="edit" onPress={controller.EDIT_TAG} color={Colors.Orange} />
|
||||
<Icon
|
||||
name="edit"
|
||||
onPress={controller.EDIT_TAG}
|
||||
color={Theme.Colors.Icon}
|
||||
/>
|
||||
}>
|
||||
<Column scroll backgroundColor={Colors.LightGrey}>
|
||||
<Column>
|
||||
<VcDetails vc={controller.vc} />
|
||||
<Column scroll>
|
||||
<Column fill>
|
||||
<UpdatedVcDetails vc={controller.vc} />
|
||||
</Column>
|
||||
</Column>
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ import { Icon } from 'react-native-elements';
|
||||
import { mainRoutes } from '../routes/main';
|
||||
import { RootRouteProps } from '../routes';
|
||||
import { LanguageSelector } from '../components/LanguageSelector';
|
||||
import { Colors } from '../components/ui/styleUtils';
|
||||
import { Theme } from '../components/ui/styleUtils';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
const { Navigator, Screen } = createBottomTabNavigator();
|
||||
@@ -16,10 +16,11 @@ export const MainLayout: React.FC<RootRouteProps> = () => {
|
||||
const { t } = useTranslation('MainLayout');
|
||||
|
||||
const options: BottomTabNavigationOptions = {
|
||||
headerLeft: () => <Icon name="notifications" color={Theme.Colors.Icon} />,
|
||||
headerLeftContainerStyle: { paddingStart: 16 },
|
||||
headerRight: () => (
|
||||
<LanguageSelector
|
||||
triggerComponent={<Icon name="language" color={Colors.Orange} />}
|
||||
triggerComponent={<Icon name="language" color={Theme.Colors.Icon} />}
|
||||
/>
|
||||
),
|
||||
headerRightContainerStyle: { paddingEnd: 16 },
|
||||
@@ -47,7 +48,7 @@ export const MainLayout: React.FC<RootRouteProps> = () => {
|
||||
tabBarIcon: ({ focused }) => (
|
||||
<Icon
|
||||
name={route.icon}
|
||||
color={focused ? Colors.Orange : Colors.Grey}
|
||||
color={focused ? Theme.Colors.IconBg : Theme.Colors.Icon}
|
||||
reverse={focused}
|
||||
/>
|
||||
),
|
||||
|
||||
@@ -4,7 +4,7 @@ import { Icon } from 'react-native-elements';
|
||||
import { MAX_PIN, PasscodeVerify } from '../components/PasscodeVerify';
|
||||
import { PinInput } from '../components/PinInput';
|
||||
import { Column, Text } from '../components/ui';
|
||||
import { Colors } from '../components/ui/styleUtils';
|
||||
import { Theme } from '../components/ui/styleUtils';
|
||||
import { PasscodeRouteProps } from '../routes';
|
||||
import { usePasscodeScreen } from './PasscodeScreenController';
|
||||
|
||||
@@ -30,8 +30,11 @@ export const PasscodeScreen: React.FC<PasscodeRouteProps> = (props) => {
|
||||
);
|
||||
|
||||
return (
|
||||
<Column fill padding="32" backgroundColor={Colors.White}>
|
||||
<Icon name="lock" color={Colors.Orange} size={60} />
|
||||
<Column
|
||||
fill
|
||||
padding="32"
|
||||
backgroundColor={Theme.Colors.whiteBackgroundColor}>
|
||||
<Icon name="lock" color={Theme.Colors.Icon} size={60} />
|
||||
{props.route.params?.setup ? (
|
||||
<Column fill align="space-between" width="100%">
|
||||
{passcodeSetup}
|
||||
@@ -48,7 +51,7 @@ export const PasscodeScreen: React.FC<PasscodeRouteProps> = (props) => {
|
||||
)}
|
||||
|
||||
<Column fill>
|
||||
<Text align="center" color={Colors.Red}>
|
||||
<Text align="center" color={Theme.Colors.errorMessage}>
|
||||
{controller.error}
|
||||
</Text>
|
||||
</Column>
|
||||
|
||||
@@ -4,7 +4,7 @@ import { Dimensions, Image, StyleSheet, View } from 'react-native';
|
||||
import { Divider, Icon, ListItem, Overlay } from 'react-native-elements';
|
||||
import Markdown from 'react-native-simple-markdown';
|
||||
import { Button, Text, Row } from '../../components/ui';
|
||||
import { Colors } from '../../components/ui/styleUtils';
|
||||
import { Theme } from '../../components/ui/styleUtils';
|
||||
import creditsContent from '../../Credits.md';
|
||||
|
||||
export const Credits: React.FC<CreditsProps> = (props) => {
|
||||
@@ -57,7 +57,7 @@ export const Credits: React.FC<CreditsProps> = (props) => {
|
||||
<ListItem bottomDivider onPress={() => setIsViewing(true)}>
|
||||
<ListItem.Content>
|
||||
<ListItem.Title>
|
||||
<Text>{props.label}</Text>
|
||||
<Text color={props.color}>{props.label}</Text>
|
||||
</ListItem.Title>
|
||||
</ListItem.Content>
|
||||
<Overlay
|
||||
@@ -69,7 +69,7 @@ export const Credits: React.FC<CreditsProps> = (props) => {
|
||||
<View style={styles.buttonContainer}>
|
||||
<Button
|
||||
type="clear"
|
||||
icon={<Icon name="chevron-left" color={Colors.Orange} />}
|
||||
icon={<Icon name="chevron-left" color={Theme.Colors.Icon} />}
|
||||
title=""
|
||||
onPress={() => setIsViewing(false)}
|
||||
/>
|
||||
@@ -90,4 +90,5 @@ export const Credits: React.FC<CreditsProps> = (props) => {
|
||||
|
||||
interface CreditsProps {
|
||||
label: string;
|
||||
color?: string;
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ import { View } from 'react-native';
|
||||
import { getVersion } from 'react-native-device-info';
|
||||
import { ListItem, Switch } from 'react-native-elements';
|
||||
import { Column, Text } from '../../components/ui';
|
||||
import { Colors } from '../../components/ui/styleUtils';
|
||||
import { Theme } from '../../components/ui/styleUtils';
|
||||
import { MainRouteProps } from '../../routes/main';
|
||||
import { EditableListItem } from '../../components/EditableListItem';
|
||||
import { MessageOverlay } from '../../components/MessageOverlay';
|
||||
@@ -25,7 +25,7 @@ const LanguageSetting: React.FC = () => {
|
||||
<Text>{t('language')}</Text>
|
||||
</ListItem.Title>
|
||||
</ListItem.Content>
|
||||
<Text margin="0 12 0 0" color={Colors.Grey}>
|
||||
<Text margin="0 12 0 0" color={Theme.Colors.profileLanguageValue}>
|
||||
{SUPPORTED_LANGUAGES[i18next.language]}
|
||||
</Text>
|
||||
</ListItem>
|
||||
@@ -38,7 +38,10 @@ export const ProfileScreen: React.FC<MainRouteProps> = (props) => {
|
||||
const { t } = useTranslation('ProfileScreen');
|
||||
const controller = useProfileScreen(props);
|
||||
return (
|
||||
<Column fill padding="24 0" backgroundColor={Colors.LightGrey}>
|
||||
<Column
|
||||
fill
|
||||
padding="24 0"
|
||||
backgroundColor={Theme.Colors.lightGreyBackgroundColor}>
|
||||
<MessageOverlay
|
||||
isVisible={controller.alertMsg != ''}
|
||||
onBackdropPress={controller.hideAlert}
|
||||
@@ -58,27 +61,29 @@ export const ProfileScreen: React.FC<MainRouteProps> = (props) => {
|
||||
<ListItem bottomDivider disabled={!controller.canUseBiometrics}>
|
||||
<ListItem.Content>
|
||||
<ListItem.Title>
|
||||
<Text>{t('bioUnlock')}</Text>
|
||||
<Text color={Theme.Colors.profileLabel}>{t('bioUnlock')}</Text>
|
||||
</ListItem.Title>
|
||||
</ListItem.Content>
|
||||
<Switch
|
||||
value={controller.isBiometricUnlockEnabled}
|
||||
onValueChange={controller.useBiometrics}
|
||||
color={Colors.Orange}
|
||||
color={Theme.Colors.profileValue}
|
||||
/>
|
||||
</ListItem>
|
||||
<ListItem bottomDivider disabled>
|
||||
<ListItem.Content>
|
||||
<ListItem.Title>
|
||||
<Text color={Colors.Grey}>{t('authFactorUnlock')}</Text>
|
||||
<Text color={Theme.Colors.profileAuthFactorUnlock}>
|
||||
{t('authFactorUnlock')}
|
||||
</Text>
|
||||
</ListItem.Title>
|
||||
</ListItem.Content>
|
||||
</ListItem>
|
||||
<Credits label={t('credits')} />
|
||||
<Credits label={t('credits')} color={Theme.Colors.profileLabel} />
|
||||
<ListItem bottomDivider onPress={controller.LOGOUT}>
|
||||
<ListItem.Content>
|
||||
<ListItem.Title>
|
||||
<Text>{t('logout')}</Text>
|
||||
<Text color={Theme.Colors.profileLabel}>{t('logout')}</Text>
|
||||
</ListItem.Title>
|
||||
</ListItem.Content>
|
||||
</ListItem>
|
||||
@@ -87,7 +92,7 @@ export const ProfileScreen: React.FC<MainRouteProps> = (props) => {
|
||||
margin="32 0 0 0"
|
||||
align="center"
|
||||
size="smaller"
|
||||
color={Colors.Grey}>
|
||||
color={Theme.Colors.profileVersion}>
|
||||
Version: {getVersion()}
|
||||
</Text>
|
||||
{controller.backendInfo.application.name !== '' ? (
|
||||
@@ -96,7 +101,7 @@ export const ProfileScreen: React.FC<MainRouteProps> = (props) => {
|
||||
weight="semibold"
|
||||
align="center"
|
||||
size="smaller"
|
||||
color={Colors.Grey}>
|
||||
color={Theme.Colors.profileValue}>
|
||||
{controller.backendInfo.application.name}:{' '}
|
||||
{controller.backendInfo.application.version}
|
||||
</Text>
|
||||
@@ -104,7 +109,7 @@ export const ProfileScreen: React.FC<MainRouteProps> = (props) => {
|
||||
weight="semibold"
|
||||
align="center"
|
||||
size="smaller"
|
||||
color={Colors.Grey}>
|
||||
color={Theme.Colors.profileValue}>
|
||||
MOSIP: {controller.backendInfo.config['mosip.host']}
|
||||
</Text>
|
||||
</View>
|
||||
|
||||
0
screens/Request/ReceiveVcModal.tsx
Normal file
@@ -1,8 +1,10 @@
|
||||
import React from 'react';
|
||||
import QRCode from 'react-native-qrcode-svg';
|
||||
import { Button, Centered, Column, Row, Text } from '../../components/ui';
|
||||
import { Colors } from '../../components/ui/styleUtils';
|
||||
|
||||
import { Centered, Button, Row, Column, Text } from '../../components/ui';
|
||||
import { Theme } from '../../components/ui/styleUtils';
|
||||
import { MainRouteProps } from '../../routes/main';
|
||||
import { ReceiveVcModal } from './ReceiveVcModal';
|
||||
import { MessageOverlay } from '../../components/MessageOverlay';
|
||||
import { useRequestScreen } from './RequestScreenController';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Switch } from 'react-native-elements';
|
||||
@@ -13,11 +15,14 @@ export const RequestScreen: React.FC = () => {
|
||||
const controller = useRequestScreen();
|
||||
|
||||
return (
|
||||
<Column fill padding="24" backgroundColor={Colors.LightGrey}>
|
||||
<Column
|
||||
fill
|
||||
padding="24"
|
||||
backgroundColor={Theme.Colors.lightGreyBackgroundColor}>
|
||||
<Column>
|
||||
{controller.isBluetoothDenied ? (
|
||||
<React.Fragment>
|
||||
<Text color={Colors.Red} align="center">
|
||||
<Text color={Theme.Colors.errorMessage} align="center">
|
||||
{t('bluetoothDenied', { vcLabel: controller.vcLabel.singular })}
|
||||
</Text>
|
||||
<Button
|
||||
@@ -38,7 +43,7 @@ export const RequestScreen: React.FC = () => {
|
||||
<QRCode
|
||||
size={200}
|
||||
value={controller.connectionParams}
|
||||
backgroundColor={Colors.LightGrey}
|
||||
backgroundColor={Theme.Colors.QRCodeBackgroundColor}
|
||||
/>
|
||||
) : null}
|
||||
</Centered>
|
||||
|
||||
@@ -78,5 +78,6 @@ export function useRequestScreen() {
|
||||
SWITCH_PROTOCOL: (value: boolean) =>
|
||||
requestService.send(RequestEvents.SWITCH_PROTOCOL(value)),
|
||||
GOTO_SETTINGS: () => requestService.send(RequestEvents.GOTO_SETTINGS()),
|
||||
GOBACK: () => requestService.send(RequestEvents.GOBACK()),
|
||||
};
|
||||
}
|
||||
|
||||
35
screens/Request/TimerBasedReceiveVcModal.tsx
Normal file
@@ -0,0 +1,35 @@
|
||||
import React from 'react';
|
||||
import { Column } from '../../components/ui';
|
||||
import { Theme } from '../../components/ui/styleUtils';
|
||||
import { Modal, ModalProps } from '../../components/ui/Modal';
|
||||
import { useReceiveVcModal } from './ReceiveVcModalController';
|
||||
import { NewVcDetails } from '../../components/NewVcDetails';
|
||||
|
||||
export const TimerBasedReceiveVcModal: React.FC<ReceveVcModalProps> = (
|
||||
props
|
||||
) => {
|
||||
const controller = useReceiveVcModal();
|
||||
|
||||
return (
|
||||
<Modal
|
||||
{...props}
|
||||
onShow={() =>
|
||||
setTimeout(() => {
|
||||
props.onAccept();
|
||||
}, 5000)
|
||||
}>
|
||||
<Column
|
||||
scroll
|
||||
padding="0 0 48 0"
|
||||
backgroundColor={Theme.Colors.lightGreyBackgroundColor}>
|
||||
<NewVcDetails vc={controller.incomingVc} />
|
||||
</Column>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
interface ReceveVcModalProps extends ModalProps {
|
||||
onAccept: () => void;
|
||||
onReject: () => void;
|
||||
onShow: () => void;
|
||||
}
|
||||
93
screens/Request/TimerBasedRequestScreen.tsx
Normal file
@@ -0,0 +1,93 @@
|
||||
import React from 'react';
|
||||
import QRCode from 'react-native-qrcode-svg';
|
||||
import { Centered, Column, Text } from '../../components/ui';
|
||||
import { Theme } from '../../components/ui/styleUtils';
|
||||
import { MainRouteProps } from '../../routes/main';
|
||||
import { TimerBasedReceiveVcModal } from './TimerBasedReceiveVcModal';
|
||||
import { MessageOverlay } from '../../components/MessageOverlay';
|
||||
import { useRequestScreen } from './RequestScreenController';
|
||||
import { SuccesfullyReceived } from '../../components/SuccesfullyReceived';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useIsFocused } from '@react-navigation/native';
|
||||
|
||||
export const TimerBasedRequestScreen: React.FC<MainRouteProps> = (props) => {
|
||||
const controller = useRequestScreen(props);
|
||||
const { t } = useTranslation('RequestScreen');
|
||||
const isFocused = useIsFocused();
|
||||
|
||||
return (
|
||||
<Column
|
||||
fill
|
||||
padding="98 24 24 24"
|
||||
backgroundColor={Theme.Colors.lightGreyBackgroundColor}>
|
||||
<Column>
|
||||
{controller.isBluetoothDenied ? (
|
||||
<Text color={Theme.Colors.errorMessage} align="center">
|
||||
{t('bluetoothDenied', { vcLabel: controller.vcLabel.singular })}
|
||||
</Text>
|
||||
) : (
|
||||
controller.isWaitingForConnection && (
|
||||
<Text align="center">
|
||||
{t('showQrCode', { vcLabel: controller.vcLabel.singular })}
|
||||
</Text>
|
||||
)
|
||||
)}
|
||||
</Column>
|
||||
|
||||
<Centered fill>
|
||||
{controller.isWaitingForConnection &&
|
||||
controller.connectionParams !== '' ? (
|
||||
<QRCode
|
||||
size={200}
|
||||
value={controller.connectionParams}
|
||||
backgroundColor={Theme.Colors.QRCodeBackgroundColor}
|
||||
/>
|
||||
) : null}
|
||||
</Centered>
|
||||
|
||||
{controller.statusMessage !== '' && (
|
||||
<Column elevation={1} padding="16 24">
|
||||
<Text>{controller.statusMessage}</Text>
|
||||
</Column>
|
||||
)}
|
||||
|
||||
{isFocused && (
|
||||
<TimerBasedReceiveVcModal
|
||||
isVisible={controller.isReviewing}
|
||||
onDismiss={controller.REJECT}
|
||||
onAccept={controller.ACCEPT}
|
||||
onReject={controller.REJECT}
|
||||
onShow={controller.ACCEPT}
|
||||
headerTitle={``}
|
||||
/>
|
||||
)}
|
||||
|
||||
{isFocused && (
|
||||
<SuccesfullyReceived
|
||||
img="true"
|
||||
isVisible={controller.isAccepted}
|
||||
onBackdropPress={controller.DISMISS}
|
||||
onShow={controller.GOBACK}
|
||||
/>
|
||||
)}
|
||||
|
||||
{isFocused && (
|
||||
<MessageOverlay
|
||||
isVisible={controller.isRejected}
|
||||
title="Notice"
|
||||
message={`You rejected ${controller.senderInfo.deviceName}'s ${controller.vcLabel.singular}'`}
|
||||
onBackdropPress={controller.DISMISS}
|
||||
/>
|
||||
)}
|
||||
|
||||
{isFocused && (
|
||||
<MessageOverlay
|
||||
isVisible={controller.isDisconnected}
|
||||
title={t('Rejected')}
|
||||
message={t('The request to share ID was rejected')}
|
||||
onBackdropPress={controller.DISMISS}
|
||||
/>
|
||||
)}
|
||||
</Column>
|
||||
);
|
||||
};
|
||||
@@ -1,26 +1,29 @@
|
||||
import React from 'react';
|
||||
import { QrScanner } from '../../components/QrScanner';
|
||||
import { Button, Column, Text } from '../../components/ui';
|
||||
import { Colors } from '../../components/ui/styleUtils';
|
||||
import { Theme } from '../../components/ui/styleUtils';
|
||||
import { MainRouteProps } from '../../routes/main';
|
||||
import { MessageOverlay } from '../../components/MessageOverlay';
|
||||
import { SendVcModal } from './SendVcModal';
|
||||
import { useScanScreen } from './ScanScreenController';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { UpdatedSendVcModal } from './UpdatedSendVcModal';
|
||||
|
||||
export const ScanScreen: React.FC<MainRouteProps> = (props) => {
|
||||
const { t } = useTranslation('ScanScreen');
|
||||
const controller = useScanScreen(props);
|
||||
|
||||
return (
|
||||
<Column fill padding="98 24 24 24" backgroundColor={Colors.LightGrey}>
|
||||
<Column
|
||||
fill
|
||||
padding="98 24 24 24"
|
||||
backgroundColor={Theme.Colors.lightGreyBackgroundColor}>
|
||||
<Text align="center">{t('header')}</Text>
|
||||
|
||||
{controller.isLocationDisabled ||
|
||||
controller.isLocationDenied ||
|
||||
controller.isFlightMode ? (
|
||||
<Column fill align="space-between">
|
||||
<Text align="center" margin="16 0" color={Colors.Red}>
|
||||
<Text align="center" margin="16 0" color={Theme.Colors.errorMessage}>
|
||||
{controller.locationError.message}
|
||||
</Text>
|
||||
<Button
|
||||
@@ -37,7 +40,7 @@ export const ScanScreen: React.FC<MainRouteProps> = (props) => {
|
||||
</Column>
|
||||
)
|
||||
) : (
|
||||
<Text align="center" margin="16 0" color={Colors.Red}>
|
||||
<Text align="center" margin="16 0" color={Theme.Colors.errorMessage}>
|
||||
{t('noShareableVcs', { vcLabel: controller.vcLabel.plural })}
|
||||
</Text>
|
||||
)}
|
||||
@@ -49,7 +52,7 @@ export const ScanScreen: React.FC<MainRouteProps> = (props) => {
|
||||
onBackdropPress={controller.DISMISS_INVALID}
|
||||
/>
|
||||
|
||||
<SendVcModal
|
||||
<UpdatedSendVcModal
|
||||
isVisible={controller.isReviewing}
|
||||
onDismiss={controller.DISMISS}
|
||||
headerElevation={2}
|
||||
|
||||
@@ -3,7 +3,7 @@ import { useTranslation } from 'react-i18next';
|
||||
import { Dimensions, StyleSheet } from 'react-native';
|
||||
import { Overlay } from 'react-native-elements/dist/overlay/Overlay';
|
||||
import { Button, Column, Row, Text } from '../../components/ui';
|
||||
import { Colors, elevation } from '../../components/ui/styleUtils';
|
||||
import { Theme } from '../../components/ui/styleUtils';
|
||||
import { VcItem } from '../../components/VcItem';
|
||||
import {
|
||||
SelectVcOverlayProps,
|
||||
@@ -12,8 +12,8 @@ import {
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
overlay: {
|
||||
...elevation(5),
|
||||
backgroundColor: Colors.White,
|
||||
...Theme.elevation(5),
|
||||
backgroundColor: Theme.Colors.overlayBackgroundColor,
|
||||
padding: 0,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -2,7 +2,7 @@ import React from 'react';
|
||||
import { Input } from 'react-native-elements';
|
||||
import { DeviceInfoList } from '../../components/DeviceInfoList';
|
||||
import { Button, Column } from '../../components/ui';
|
||||
import { Colors } from '../../components/ui/styleUtils';
|
||||
import { Theme } from '../../components/ui/styleUtils';
|
||||
import { SelectVcOverlay } from './SelectVcOverlay';
|
||||
import { MessageOverlay } from '../../components/MessageOverlay';
|
||||
import { Modal, ModalProps } from '../../components/ui/Modal';
|
||||
@@ -17,7 +17,7 @@ export const SendVcModal: React.FC<SendVcModalProps> = (props) => {
|
||||
|
||||
return (
|
||||
<Modal {...props}>
|
||||
<Column fill backgroundColor={Colors.LightGrey}>
|
||||
<Column fill backgroundColor={Theme.Colors.lightGreyBackgroundColor}>
|
||||
<Column padding="16 0" scroll>
|
||||
<DeviceInfoList of="receiver" deviceInfo={controller.receiverInfo} />
|
||||
<Column padding="24">
|
||||
@@ -30,7 +30,7 @@ export const SendVcModal: React.FC<SendVcModalProps> = (props) => {
|
||||
</Column>
|
||||
</Column>
|
||||
<Column
|
||||
backgroundColor={Colors.White}
|
||||
backgroundColor={Theme.Colors.whiteBackgroundColor}
|
||||
padding="16 24"
|
||||
margin="2 0 0 0"
|
||||
elevation={2}>
|
||||
|
||||
121
screens/Scan/UpdatedSendVcModal.tsx
Normal file
@@ -0,0 +1,121 @@
|
||||
import React from 'react';
|
||||
import { Input } from 'react-native-elements';
|
||||
import { DeviceInfoList } from '../../components/DeviceInfoList';
|
||||
import { Button, Column } from '../../components/ui';
|
||||
import { Theme } from '../../components/ui/styleUtils';
|
||||
import { MessageOverlay } from '../../components/MessageOverlay';
|
||||
import { Modal, ModalProps } from '../../components/ui/Modal';
|
||||
import { useSendVcModal } from './SendVcModalController';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { UpdatedVcItem } from '../../components/UpdatedVcItem';
|
||||
import { useSelectVcOverlay } from './SelectVcOverlayController';
|
||||
import { SingleVcItem } from '../../components/SingleVcItem';
|
||||
|
||||
export const UpdatedSendVcModal: React.FC<SendVcModalProps> = (props) => {
|
||||
const { t } = useTranslation('UpdatedSendVcModal');
|
||||
const controller = useSendVcModal();
|
||||
|
||||
const onShare = () => {
|
||||
controller.ACCEPT_REQUEST();
|
||||
controller2.onSelect();
|
||||
};
|
||||
|
||||
const details = {
|
||||
isVisible: controller.isSelectingVc,
|
||||
receiverName: controller.receiverInfo.deviceName,
|
||||
onSelect: controller.SELECT_VC,
|
||||
onCancel: controller.CANCEL,
|
||||
vcKeys: controller.vcKeys,
|
||||
};
|
||||
|
||||
const controller2 = useSelectVcOverlay(details);
|
||||
|
||||
const reasonLabel = t('Reason For Sharing');
|
||||
|
||||
return (
|
||||
<Modal {...props}>
|
||||
<Column fill backgroundColor={Theme.Colors.lightGreyBackgroundColor}>
|
||||
<Column padding="16 0" scroll>
|
||||
<DeviceInfoList of="receiver" deviceInfo={controller.receiverInfo} />
|
||||
<Column padding="24">
|
||||
<Input
|
||||
placeholder={!controller.reason ? reasonLabel : ''}
|
||||
label={controller.reason ? reasonLabel : ''}
|
||||
onChangeText={controller.UPDATE_REASON}
|
||||
containerStyle={{ marginBottom: 24 }}
|
||||
/>
|
||||
</Column>
|
||||
<Column>
|
||||
{controller.vcKeys.length === 1 && (
|
||||
<SingleVcItem
|
||||
key={controller.vcKeys[0]}
|
||||
vcKey={controller.vcKeys[0]}
|
||||
margin="0 2 8 2"
|
||||
onShow={controller2.selectVcItem(0)}
|
||||
selectable
|
||||
selected={0 === controller2.selectedIndex}
|
||||
/>
|
||||
)}
|
||||
|
||||
{controller.vcKeys.length > 1 &&
|
||||
controller.vcKeys.map((vcKey, index) => (
|
||||
<UpdatedVcItem
|
||||
key={vcKey}
|
||||
vcKey={vcKey}
|
||||
margin="0 2 8 2"
|
||||
onPress={controller2.selectVcItem(index)}
|
||||
selectable
|
||||
selected={index === controller2.selectedIndex}
|
||||
/>
|
||||
))}
|
||||
</Column>
|
||||
</Column>
|
||||
<Column
|
||||
backgroundColor={Theme.Colors.whiteBackgroundColor}
|
||||
padding="16 24"
|
||||
margin="2 0 0 0"
|
||||
elevation={2}>
|
||||
<Button
|
||||
title={t('AcceptRequest', { vcLabel: controller.vcLabel.singular })}
|
||||
margin="12 0 12 0"
|
||||
disabled={controller2.selectedIndex == null}
|
||||
onPress={onShare}
|
||||
/>
|
||||
<Button
|
||||
type="clear"
|
||||
title={t('Reject')}
|
||||
onPress={controller.CANCEL}
|
||||
/>
|
||||
</Column>
|
||||
</Column>
|
||||
|
||||
<MessageOverlay
|
||||
isVisible={controller.isSendingVc}
|
||||
title={t('Sharing..')}
|
||||
hasProgress
|
||||
/>
|
||||
|
||||
<MessageOverlay
|
||||
isVisible={controller.isAccepted}
|
||||
title={t(controller.vcLabel.singular, 'Sent succesfully')}
|
||||
message={t('statusAccepted.message', {
|
||||
vcLabel: controller.vcLabel.singular,
|
||||
receiver: controller.receiverInfo.deviceName,
|
||||
})}
|
||||
onShow={props.onDismiss}
|
||||
/>
|
||||
|
||||
<MessageOverlay
|
||||
isVisible={controller.isRejected}
|
||||
title={t('statusRejected.title')}
|
||||
message={t('statusRejected.message', {
|
||||
vcLabel: controller.vcLabel.singular,
|
||||
receiver: controller.receiverInfo.deviceName,
|
||||
})}
|
||||
onBackdropPress={props.onDismiss}
|
||||
/>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
type SendVcModalProps = ModalProps;
|
||||
@@ -2,7 +2,7 @@ import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Logo } from '../components/Logo';
|
||||
import { Button, Centered, Column, Text } from '../components/ui';
|
||||
import { Colors } from '../components/ui/styleUtils';
|
||||
import { Theme } from '../components/ui/styleUtils';
|
||||
import { RootRouteProps } from '../routes';
|
||||
import { useWelcomeScreen } from './WelcomeScreenController';
|
||||
|
||||
@@ -11,7 +11,10 @@ export const WelcomeScreen: React.FC<RootRouteProps> = (props) => {
|
||||
const controller = useWelcomeScreen(props);
|
||||
|
||||
return (
|
||||
<Column fill padding="32 32 0" backgroundColor={Colors.White}>
|
||||
<Column
|
||||
fill
|
||||
padding="32 32 0"
|
||||
backgroundColor={Theme.Colors.whiteBackgroundColor}>
|
||||
<Centered fill>
|
||||
<Logo height={182} />
|
||||
<Text margin="16 0 0 0">{t('title')}</Text>
|
||||
|
||||