ui revamp on ble
24
.github/workflows/android-custom-build.yml
vendored
@@ -47,28 +47,9 @@ jobs:
|
||||
- name: Install npm dependencies
|
||||
run: |
|
||||
npm install
|
||||
|
||||
- name: Create .env.local file
|
||||
run: |
|
||||
echo "${{ secrets.ENV_FILE }}" > .env.local > android/local.properties
|
||||
|
||||
- name: Setup branch and env
|
||||
run: |
|
||||
# Strip git ref prefix from version
|
||||
echo "BRANCH_NAME=$(echo ${{ github.ref }} | sed -e 's,.*/\(.*\),\1,')" >> $GITHUB_ENV
|
||||
echo "GPG_TTY=$(tty)" >> $GITHUB_ENV
|
||||
|
||||
- name: Setup branch and GPG public key
|
||||
run: |
|
||||
# Strip git ref prefix from version
|
||||
|
||||
echo ${{ env.BRANCH_NAME }}
|
||||
|
||||
echo ${{ env.GPG_TTY }}
|
||||
sudo apt-get --yes install gnupg2
|
||||
gpg2 --import ./.github/keys/mosipgpgkey_pub.gpg
|
||||
gpg2 --quiet --batch --passphrase=${{secrets.gpg_secret}} --allow-secret-key-import --import ./.github/keys/mosipgpgkey_sec.gpg
|
||||
|
||||
- name: Build App Newlogic Release
|
||||
run: |
|
||||
cd android
|
||||
@@ -76,11 +57,10 @@ jobs:
|
||||
./gradlew :app:assembleNewlogicRelease
|
||||
env:
|
||||
MIMOTO_HOST: ${{ github.event.inputs.backendServiceUrl }}
|
||||
FIREBASE_SECRET: ${{ secrets.GPG_SECRET }}
|
||||
APPLICATION_THEME: ${{ github.event.inputs.theme }}
|
||||
FIREBASE_SECRET: ${{ secrets.FIREBASE_SECRET }}
|
||||
- name: Upload Artifact
|
||||
uses: actions/upload-artifact@v3.1.1
|
||||
with:
|
||||
name: apk-output
|
||||
path: android/app/build/outputs/apk/newlogic/release/
|
||||
retention-days: 10
|
||||
retention-days: 5
|
||||
|
||||
5
.github/workflows/clear_artifacts.yml
vendored
@@ -1,4 +1,7 @@
|
||||
name: 'Delete old artifacts'
|
||||
on:
|
||||
workflow_dispatch:
|
||||
name: 'Delete old artifacts'
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
@@ -8,5 +11,5 @@ jobs:
|
||||
steps:
|
||||
- uses: kolpav/purge-artifacts-action@v1
|
||||
with:
|
||||
token: ${{ secrets. access_token }}
|
||||
token: ${{ secrets.ACTION_PAT }}
|
||||
expire-in: 2days # Setting this to 0 will delete all artifacts
|
||||
|
Before Width: | Height: | Size: 129 KiB After Width: | Height: | Size: 5.6 KiB |
|
Before Width: | Height: | Size: 129 KiB After Width: | Height: | Size: 3.2 KiB |
|
Before Width: | Height: | Size: 129 KiB After Width: | Height: | Size: 9.2 KiB |
|
Before Width: | Height: | Size: 129 KiB After Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 129 KiB After Width: | Height: | Size: 22 KiB |
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<resources>
|
||||
<color name="splashscreen_background">#ffffff</color>
|
||||
<color name="splashscreen_background">#F59B4B</color>
|
||||
<color name="iconBackground">#FFFFFF</color>
|
||||
<color name="colorPrimary">#023c69</color>
|
||||
<color name="colorPrimaryDark">#ffffff</color>
|
||||
|
||||
@@ -4,6 +4,6 @@
|
||||
<string name="app_name_mosip">MOSIP Resident App - Mosip/Inji</string>
|
||||
<string name="app_name_newlogic">MOSIP Resident App - Newlogic</string>
|
||||
<string name="app_name_ph">MOSIP Resident App - PH</string>
|
||||
<string name="expo_splash_screen_resize_mode" translatable="false">contain</string>
|
||||
<string name="expo_splash_screen_resize_mode" translatable="false">cover</string>
|
||||
<string name="expo_splash_screen_status_bar_translucent" translatable="false">false</string>
|
||||
</resources>
|
||||
@@ -6,7 +6,7 @@ export default {
|
||||
icon: './assets/icon.png',
|
||||
splash: {
|
||||
image: './assets/splash.png',
|
||||
resizeMode: 'contain',
|
||||
resizeMode: 'cover',
|
||||
backgroundColor: '#ffffff',
|
||||
},
|
||||
updates: {
|
||||
|
||||
BIN
assets/Secure-Sharing.png
Normal file
|
After Width: | Height: | Size: 72 KiB |
BIN
assets/Secure-Sharing2.png
Normal file
|
After Width: | Height: | Size: 214 KiB |
BIN
assets/biometric-unlock-icon.png
Normal file
|
After Width: | Height: | Size: 1.8 KiB |
BIN
assets/digital-identity-icon.png
Normal file
|
After Width: | Height: | Size: 3.2 KiB |
BIN
assets/features-walkaround-icon.png
Normal file
|
After Width: | Height: | Size: 603 B |
BIN
assets/help-icon.png
Normal file
|
After Width: | Height: | Size: 2.4 KiB |
BIN
assets/inji-home-logo.png
Normal file
|
After Width: | Height: | Size: 2.8 KiB |
BIN
assets/inji-logo-white.png
Normal file
|
After Width: | Height: | Size: 7.6 KiB |
BIN
assets/inji_small_logo.png
Normal file
|
After Width: | Height: | Size: 4.3 KiB |
BIN
assets/intro-scanner.png
Normal file
|
After Width: | Height: | Size: 70 KiB |
BIN
assets/intro-wallet-binding.png
Normal file
|
After Width: | Height: | Size: 96 KiB |
BIN
assets/legal-notices-icon.png
Normal file
|
After Width: | Height: | Size: 307 B |
BIN
assets/lock-icon.png
Normal file
|
After Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 5.8 KiB After Width: | Height: | Size: 607 B |
BIN
assets/progressing-logo.png
Normal file
|
After Width: | Height: | Size: 2.3 KiB |
|
Before Width: | Height: | Size: 39 KiB After Width: | Height: | Size: 22 KiB |
@@ -14,6 +14,7 @@ export const ActivityLogText: React.FC<{ activity: ActivityLog }> = (props) => {
|
||||
<TextItem
|
||||
label={getActionLabel(activity, i18n.language)}
|
||||
text={`${activity.vcLabel} ${t(activity.type)}`}
|
||||
topDivider
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,23 +1,18 @@
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { TextItem } from './ui/TextItem';
|
||||
import { Text } from './ui';
|
||||
|
||||
export const DeviceInfoList: React.FC<DeviceInfoProps> = (props) => {
|
||||
const { t } = useTranslation('DeviceInfoList');
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<TextItem
|
||||
divider
|
||||
label={props.of === 'receiver' ? t('requestedBy') : t('sentBy')}
|
||||
text={props.deviceInfo.deviceName}
|
||||
/>
|
||||
<Text>{props.deviceInfo.deviceName}</Text>
|
||||
</React.Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
interface DeviceInfoProps {
|
||||
of: 'sender' | 'receiver';
|
||||
deviceInfo: DeviceInfo;
|
||||
}
|
||||
|
||||
|
||||
@@ -11,23 +11,26 @@ export const EditableListItem: React.FC<EditableListItemProps> = (props) => {
|
||||
const [newValue, setNewValue] = useState(props.value);
|
||||
|
||||
return (
|
||||
<ListItem
|
||||
bottomDivider
|
||||
onPress={() => setIsEditing(true)}
|
||||
style={{ display: props.display }}>
|
||||
<ListItem bottomDivider topDivider onPress={() => setIsEditing(true)}>
|
||||
<Icon
|
||||
name={props.Icon}
|
||||
type="antdesign"
|
||||
size={20}
|
||||
style={Theme.Styles.profileIconBg}
|
||||
containerStyle={Theme.Styles.settingsIconBg}
|
||||
type={props.IconType}
|
||||
size={22}
|
||||
color={Theme.Colors.Icon}
|
||||
/>
|
||||
<ListItem.Content>
|
||||
<ListItem.Title>
|
||||
<Text color={Theme.Colors.profileLabel}>{props.label}</Text>
|
||||
<Text weight="semibold" color={Theme.Colors.profileLabel}>
|
||||
{props.label}
|
||||
</Text>
|
||||
</ListItem.Title>
|
||||
</ListItem.Content>
|
||||
<Text color={Theme.Colors.profileValue}>{props.value}</Text>
|
||||
<Icon
|
||||
name="chevron-right"
|
||||
size={21}
|
||||
color={Theme.Colors.profileLanguageValue}
|
||||
/>
|
||||
<Overlay
|
||||
overlayStyle={{ padding: 24, elevation: 6 }}
|
||||
isVisible={isEditing}
|
||||
@@ -67,6 +70,7 @@ interface EditableListItemProps {
|
||||
label: string;
|
||||
value: string;
|
||||
Icon: string;
|
||||
IconType?: string;
|
||||
onEdit: (newValue: string) => void;
|
||||
display?: 'none' | 'flex';
|
||||
}
|
||||
|
||||
74
components/KebabPopUp.tsx
Normal file
@@ -0,0 +1,74 @@
|
||||
import React from 'react';
|
||||
import { BottomSheet, Icon, ListItem } from 'react-native-elements';
|
||||
import { Theme } from '../components/ui/styleUtils';
|
||||
import { Centered, Column, Row, Text } from '../components/ui';
|
||||
import { WalletBinding } from '../screens/Home/MyVcs/WalletBinding';
|
||||
import { Pressable } from 'react-native';
|
||||
import { useKebabPopUp } from './KebabPopUpController';
|
||||
import { ActorRefFrom } from 'xstate';
|
||||
import { vcItemMachine } from '../machines/vcItem';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { HistoryTab } from '../screens/Home/MyVcs/HistoryTab';
|
||||
|
||||
export const KebabPopUp: React.FC<KebabPopUpProps> = (props) => {
|
||||
const controller = useKebabPopUp(props);
|
||||
const { t } = useTranslation('HomeScreenKebabPopUp');
|
||||
return (
|
||||
<Column>
|
||||
<Icon
|
||||
name={props.iconName}
|
||||
type={props.iconType}
|
||||
color={Theme.Colors.GrayIcon}
|
||||
/>
|
||||
<BottomSheet
|
||||
isVisible={props.isVisible}
|
||||
containerStyle={Theme.KebabPopUpStyles.kebabPopUp}>
|
||||
<Row style={Theme.KebabPopUpStyles.kebabHeaderStyle}>
|
||||
<Text weight="bold">{t('title')}</Text>
|
||||
<Icon
|
||||
name="close"
|
||||
onPress={props.onDismiss}
|
||||
color={Theme.Colors.Details}
|
||||
size={25}
|
||||
/>
|
||||
</Row>
|
||||
<Column>
|
||||
<ListItem bottomDivider>
|
||||
<ListItem.Content>
|
||||
<ListItem.Title>
|
||||
<Pressable onPress={controller.PIN_CARD}>
|
||||
<Text size="small" weight="bold">
|
||||
{props.vcKey.split(':')[4] == 'true'
|
||||
? t('unPinCard')
|
||||
: t('pinCard')}
|
||||
</Text>
|
||||
</Pressable>
|
||||
</ListItem.Title>
|
||||
</ListItem.Content>
|
||||
</ListItem>
|
||||
|
||||
<WalletBinding
|
||||
label={t('offlineAuthenticationDisabled!')}
|
||||
content={t('offlineAuthDisabledMessage')}
|
||||
service={props.service}
|
||||
/>
|
||||
|
||||
<HistoryTab
|
||||
service={props.service}
|
||||
label={t('ActivityLog')}
|
||||
vcKey={props.vcKey}
|
||||
/>
|
||||
</Column>
|
||||
</BottomSheet>
|
||||
</Column>
|
||||
);
|
||||
};
|
||||
|
||||
export interface KebabPopUpProps {
|
||||
iconName: string;
|
||||
iconType?: string;
|
||||
vcKey: string;
|
||||
isVisible: boolean;
|
||||
onDismiss: () => void;
|
||||
service: ActorRefFrom<typeof vcItemMachine>;
|
||||
}
|
||||
75
components/KebabPopUpController.tsx
Normal file
@@ -0,0 +1,75 @@
|
||||
import { useSelector } from '@xstate/react';
|
||||
import { ActorRefFrom } from 'xstate';
|
||||
import {
|
||||
selectKebabPopUpWalletBindingInProgress,
|
||||
selectKebabPopUp,
|
||||
selectKebabPopUpAcceptingBindingOtp,
|
||||
selectKebabPopUpBindingWarning,
|
||||
selectEmptyWalletBindingId,
|
||||
selectIsPinned,
|
||||
selectOtpError,
|
||||
selectShowWalletBindingError,
|
||||
selectWalletBindingError,
|
||||
VcItemEvents,
|
||||
vcItemMachine,
|
||||
selectShowActivities,
|
||||
} from '../machines/vcItem';
|
||||
import { selectActivities } from '../machines/activityLog';
|
||||
import { GlobalContext } from '../shared/GlobalContext';
|
||||
import { useContext } from 'react';
|
||||
|
||||
export function useKebabPopUp(props) {
|
||||
const service = props.service as ActorRefFrom<typeof vcItemMachine>;
|
||||
const PIN_CARD = () => service.send(VcItemEvents.PIN_CARD());
|
||||
const KEBAB_POPUP = () => service.send(VcItemEvents.KEBAB_POPUP());
|
||||
const ADD_WALLET_BINDING_ID = () =>
|
||||
service.send(VcItemEvents.ADD_WALLET_BINDING_ID());
|
||||
const CONFIRM = () => service.send(VcItemEvents.CONFIRM());
|
||||
const DISMISS = () => service.send(VcItemEvents.DISMISS());
|
||||
const CANCEL = () => service.send(VcItemEvents.CANCEL());
|
||||
const SHOW_ACTIVITY = () => service.send(VcItemEvents.SHOW_ACTIVITY());
|
||||
const INPUT_OTP = (otp: string) => service.send(VcItemEvents.INPUT_OTP(otp));
|
||||
const isPinned = useSelector(service, selectIsPinned);
|
||||
const isBindingWarning = useSelector(service, selectKebabPopUpBindingWarning);
|
||||
const isAcceptingOtpInput = useSelector(
|
||||
service,
|
||||
selectKebabPopUpAcceptingBindingOtp
|
||||
);
|
||||
const isWalletBindingError = useSelector(
|
||||
service,
|
||||
selectShowWalletBindingError
|
||||
);
|
||||
const otpError = useSelector(service, selectOtpError);
|
||||
const walletBindingError = useSelector(service, selectWalletBindingError);
|
||||
const WalletBindingInProgress = useSelector(
|
||||
service,
|
||||
selectKebabPopUpWalletBindingInProgress
|
||||
);
|
||||
const emptyWalletBindingId = useSelector(service, selectEmptyWalletBindingId);
|
||||
const isKebabPopUp = useSelector(service, selectKebabPopUp);
|
||||
const isShowActivities = useSelector(service, selectShowActivities);
|
||||
const { appService } = useContext(GlobalContext);
|
||||
const activityLogService = appService.children.get('activityLog');
|
||||
|
||||
return {
|
||||
isPinned,
|
||||
PIN_CARD,
|
||||
KEBAB_POPUP,
|
||||
ADD_WALLET_BINDING_ID,
|
||||
CONFIRM,
|
||||
DISMISS,
|
||||
CANCEL,
|
||||
INPUT_OTP,
|
||||
SHOW_ACTIVITY,
|
||||
isBindingWarning,
|
||||
isAcceptingOtpInput,
|
||||
isWalletBindingError,
|
||||
walletBindingError,
|
||||
otpError,
|
||||
WalletBindingInProgress,
|
||||
emptyWalletBindingId,
|
||||
isKebabPopUp,
|
||||
isShowActivities,
|
||||
activities: useSelector(activityLogService, selectActivities),
|
||||
};
|
||||
}
|
||||
@@ -14,14 +14,33 @@ export const MessageOverlay: React.FC<MessageOverlayProps> = (props) => {
|
||||
overlayStyle={Theme.MessageOverlayStyles.overlay}
|
||||
onShow={props.onShow}
|
||||
onBackdropPress={props.onBackdropPress}>
|
||||
<Column width={Dimensions.get('screen').width * 0.8}>
|
||||
<Column padding="24">
|
||||
<Column
|
||||
width={Dimensions.get('screen').width * 0.8}
|
||||
style={
|
||||
!props.progress
|
||||
? Theme.MessageOverlayStyles.popupOverLay
|
||||
: { height: 100 }
|
||||
}>
|
||||
<Column padding="21" crossAlign="center">
|
||||
{props.title && (
|
||||
<Text weight="semibold" margin="0 0 12 0">
|
||||
<Text
|
||||
align="center"
|
||||
weight="bold"
|
||||
margin="0 0 10 0"
|
||||
color={Theme.Colors.Details}>
|
||||
{props.title}
|
||||
</Text>
|
||||
)}
|
||||
{props.message && <Text margin="0 0 12 0">{props.message}</Text>}
|
||||
{props.message && (
|
||||
<Text
|
||||
align="center"
|
||||
weight="semibold"
|
||||
size="small"
|
||||
margin="10 0 12 0"
|
||||
color={Theme.Colors.Details}>
|
||||
{props.message}
|
||||
</Text>
|
||||
)}
|
||||
{props.progress && <Progress progress={props.progress} />}
|
||||
{props.hint && (
|
||||
<Text
|
||||
@@ -35,6 +54,7 @@ export const MessageOverlay: React.FC<MessageOverlayProps> = (props) => {
|
||||
</Column>
|
||||
{!props.children && props.onCancel ? (
|
||||
<Button
|
||||
type="gradient"
|
||||
title={t('cancel')}
|
||||
onPress={props.onCancel}
|
||||
styles={Theme.MessageOverlayStyles.button}
|
||||
@@ -60,6 +80,7 @@ export interface MessageOverlayProps {
|
||||
title?: string;
|
||||
message?: string;
|
||||
progress?: boolean | number;
|
||||
requester?: boolean;
|
||||
hint?: string;
|
||||
onCancel?: () => void;
|
||||
onBackdropPress?: () => void;
|
||||
|
||||
@@ -22,6 +22,7 @@ export const PinInput: React.FC<PinInputProps> = (props) => {
|
||||
selectTextOnFocus
|
||||
keyboardType="numeric"
|
||||
maxLength={1}
|
||||
secureTextEntry
|
||||
selectionColor={Theme.Colors.inputSelection}
|
||||
style={Theme.PinInputStyle.input}
|
||||
key={index}
|
||||
|
||||
69
components/ProgressingModal.tsx
Normal file
@@ -0,0 +1,69 @@
|
||||
import React, { useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Button, Centered, Column, Text } from './ui';
|
||||
import { Modal } from './ui/Modal';
|
||||
import { Image, View } from 'react-native';
|
||||
import { Theme } from './ui/styleUtils';
|
||||
import PaginationDot from 'react-native-animated-pagination-dot';
|
||||
|
||||
export const ProgressingModal: React.FC<ProgressingModalProps> = (props) => {
|
||||
const { t } = useTranslation('ScanScreen');
|
||||
|
||||
let n = 0;
|
||||
const [curPage, setCurPage] = useState(n);
|
||||
|
||||
const highLightDot = () => setCurPage(n + 1);
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<Modal
|
||||
isVisible={props.isVisible}
|
||||
headerLeft={t(props.title)}
|
||||
onDismiss={props.onCancel}
|
||||
headerLabel={props.label}
|
||||
headerElevation={3}
|
||||
requester={props.requester}>
|
||||
<Centered crossAlign="center" fill>
|
||||
<Column margin="24 0" align="space-around">
|
||||
<Image
|
||||
source={Theme.InjiProgressingLogo}
|
||||
height={2}
|
||||
width={2}
|
||||
style={{ marginBottom: 15, marginLeft: -6 }}
|
||||
/>
|
||||
<PaginationDot
|
||||
activeDotColor={'black'}
|
||||
curPage={curPage}
|
||||
maxPage={3}
|
||||
/>
|
||||
</Column>
|
||||
|
||||
<Column style={{ display: props.timeoutHint ? 'flex' : 'none' }}>
|
||||
<Column style={Theme.SelectVcOverlayStyles.timeoutHintContainer}>
|
||||
<Text
|
||||
align="center"
|
||||
color={Theme.Colors.TimoutText}
|
||||
style={Theme.TextStyles.bold}>
|
||||
{t('ScanScreen:status.sharing.timeoutHint')}
|
||||
</Text>
|
||||
<Button
|
||||
type="clear"
|
||||
title={t('common:cancel')}
|
||||
onPress={props.onCancel}
|
||||
/>
|
||||
</Column>
|
||||
</Column>
|
||||
</Centered>
|
||||
</Modal>
|
||||
</React.Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
export interface ProgressingModalProps {
|
||||
isVisible: boolean;
|
||||
title?: string;
|
||||
label?: string;
|
||||
timeoutHint?: string;
|
||||
onCancel?: () => void;
|
||||
requester?: boolean;
|
||||
}
|
||||
@@ -58,11 +58,6 @@ export const QrScanner: React.FC<QrScannerProps> = (props) => {
|
||||
|
||||
return (
|
||||
<View>
|
||||
{props.title && (
|
||||
<Text align="center" margin="16 0" color={Theme.Colors.Details}>
|
||||
{props.title}
|
||||
</Text>
|
||||
)}
|
||||
<View style={Theme.Styles.scannerContainer}>
|
||||
<Camera
|
||||
style={Theme.Styles.scanner}
|
||||
@@ -73,23 +68,15 @@ export const QrScanner: React.FC<QrScannerProps> = (props) => {
|
||||
type={type}
|
||||
/>
|
||||
</View>
|
||||
<Column margin="24 0">
|
||||
<TouchableOpacity
|
||||
style={Theme.Styles.flipIconButton}
|
||||
onPress={() => {
|
||||
setType(
|
||||
type === Camera.Constants.Type.back
|
||||
? Camera.Constants.Type.front
|
||||
: Camera.Constants.Type.back
|
||||
);
|
||||
}}>
|
||||
<Icon
|
||||
name="flip-camera-ios"
|
||||
color={Theme.Colors.flipCameraIcon}
|
||||
size={64}
|
||||
/>
|
||||
</TouchableOpacity>
|
||||
</Column>
|
||||
{props.title && (
|
||||
<Text
|
||||
align="center"
|
||||
weight="bold"
|
||||
style={Theme.TextStyles.base}
|
||||
margin="20 57 0 57">
|
||||
{props.title}
|
||||
</Text>
|
||||
)}
|
||||
</View>
|
||||
);
|
||||
|
||||
|
||||
212
components/SingleVcItem.tsx
Normal file
@@ -0,0 +1,212 @@
|
||||
import React, { useContext, useRef } from 'react';
|
||||
import { useInterpret, useSelector } from '@xstate/react';
|
||||
import { Image, ImageBackground, Pressable } from 'react-native';
|
||||
import { CheckBox, Icon } from 'react-native-elements';
|
||||
import { ActorRefFrom } from 'xstate';
|
||||
import {
|
||||
createVcItemMachine,
|
||||
selectVerifiableCredential,
|
||||
selectGeneratedOn,
|
||||
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
|
||||
numLines={4}
|
||||
color={Theme.Colors.Details}
|
||||
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>
|
||||
);
|
||||
}
|
||||
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().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 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)}
|
||||
style={
|
||||
props.selected
|
||||
? Theme.Styles.selectedVc
|
||||
: Theme.Styles.closeCardBgContainer
|
||||
}>
|
||||
<ImageBackground
|
||||
source={!verifiableCredential ? null : Theme.CloseCard}
|
||||
resizeMode="stretch"
|
||||
borderRadius={4}
|
||||
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" style={{ alignItems: 'flex-start' }}>
|
||||
{getDetails(t('fullName'), fullName, verifiableCredential)}
|
||||
{getDetails(t('uin'), 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 '';
|
||||
}
|
||||
}
|
||||
@@ -9,7 +9,18 @@ import { Button, Column, Row, Text } from './ui';
|
||||
import { Theme } from './ui/styleUtils';
|
||||
import { TextItem } from './ui/TextItem';
|
||||
import { VcItemTags } from './VcItemTags';
|
||||
import VerifiedIcon from './VerifiedIcon';
|
||||
import QRCode from 'react-native-qrcode-svg';
|
||||
|
||||
const VerifiedIcon: React.FC = () => {
|
||||
return (
|
||||
<Icon
|
||||
name="check-circle"
|
||||
color={Theme.Colors.VerifiedIcon}
|
||||
size={14}
|
||||
containerStyle={{ marginStart: 4, bottom: 1 }}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export const VcDetails: React.FC<VcDetailsProps> = (props) => {
|
||||
const { t, i18n } = useTranslation('VcDetails');
|
||||
@@ -28,33 +39,29 @@ export const VcDetails: React.FC<VcDetailsProps> = (props) => {
|
||||
borderRadius={10}
|
||||
style={Theme.Styles.openCardBgContainer}
|
||||
source={Theme.OpenCard}>
|
||||
<Row style={Theme.Styles.openDetailsHeader}>
|
||||
<Column margin={'0 0 0 10'}>
|
||||
<Text
|
||||
weight="bold"
|
||||
size="smaller"
|
||||
color={Theme.Colors.DetailsLabel}>
|
||||
{t('idType')}
|
||||
</Text>
|
||||
<Text weight="bold" size="smaller" color={Theme.Colors.Details}>
|
||||
{t('nationalCard')}
|
||||
</Text>
|
||||
<Row align="space-between">
|
||||
<Column align="space-evenly" crossAlign="center">
|
||||
<Image
|
||||
source={
|
||||
props.vc?.credential.biometrics?.face
|
||||
? { uri: props.vc?.credential.biometrics.face }
|
||||
: Theme.ProfileIcon
|
||||
}
|
||||
style={Theme.Styles.openCardImage}
|
||||
/>
|
||||
<Column margin="20 0 0 0">
|
||||
<Image source={Theme.MosipLogo} style={Theme.Styles.logo} />
|
||||
</Column>
|
||||
<Column margin="20 0 0 0">
|
||||
<QRCode
|
||||
size={90}
|
||||
value={String(props.vc.credential)}
|
||||
backgroundColor={Theme.Colors.QRCodeBackgroundColor}
|
||||
/>
|
||||
</Column>
|
||||
</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>
|
||||
<Column align="space-evenly">
|
||||
<Column>
|
||||
<Text
|
||||
weight="bold"
|
||||
size="smaller"
|
||||
@@ -70,173 +77,190 @@ export const VcDetails: React.FC<VcDetailsProps> = (props) => {
|
||||
)}
|
||||
</Text>
|
||||
</Column>
|
||||
<Row>
|
||||
<Column>
|
||||
<Column>
|
||||
<Text
|
||||
weight="bold"
|
||||
size="smaller"
|
||||
color={Theme.Colors.DetailsLabel}>
|
||||
{t('idType')}
|
||||
</Text>
|
||||
<Text
|
||||
weight="bold"
|
||||
size="smaller"
|
||||
color={Theme.Colors.Details}>
|
||||
{t('nationalCard')}
|
||||
</Text>
|
||||
</Column>
|
||||
{uin ? (
|
||||
<Column margin="20 0 0 0">
|
||||
<Text
|
||||
weight="bold"
|
||||
size="smaller"
|
||||
color={Theme.Colors.DetailsLabel}>
|
||||
{t('uin')}
|
||||
</Text>
|
||||
<Text
|
||||
weight="semibold"
|
||||
size="smaller"
|
||||
color={Theme.Colors.Details}>
|
||||
{uin}
|
||||
</Text>
|
||||
</Column>
|
||||
) : null}
|
||||
|
||||
{uin ? (
|
||||
<Column fill style={Theme.Styles.labelPart}>
|
||||
<Text
|
||||
weight="bold"
|
||||
size="smaller"
|
||||
color={Theme.Colors.DetailsLabel}>
|
||||
{t('uin')}
|
||||
</Text>
|
||||
<Text
|
||||
weight="semibold"
|
||||
size="smaller"
|
||||
color={Theme.Colors.Details}>
|
||||
{uin}
|
||||
</Text>
|
||||
{vid ? (
|
||||
<Column margin="20 0 0 0">
|
||||
<Text
|
||||
weight="bold"
|
||||
size="smaller"
|
||||
color={Theme.Colors.DetailsLabel}>
|
||||
{t('vid')}
|
||||
</Text>
|
||||
<Text
|
||||
weight="semibold"
|
||||
size="smaller"
|
||||
color={Theme.Colors.Details}>
|
||||
{vid}
|
||||
</Text>
|
||||
</Column>
|
||||
) : null}
|
||||
<Column margin="20 0 0 0">
|
||||
<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>
|
||||
) : null}
|
||||
|
||||
{vid ? (
|
||||
<Column fill style={Theme.Styles.labelPart}>
|
||||
<Text
|
||||
weight="bold"
|
||||
size="smaller"
|
||||
color={Theme.Colors.DetailsLabel}>
|
||||
{t('vid')}
|
||||
</Text>
|
||||
<Text
|
||||
weight="semibold"
|
||||
size="smaller"
|
||||
color={Theme.Colors.Details}>
|
||||
{vid}
|
||||
</Text>
|
||||
<Column margin="0 0 0 40">
|
||||
<Column>
|
||||
<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 margin="20 0 0 0">
|
||||
<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 margin="20 0 0 0">
|
||||
<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 margin="20 0 0 0">
|
||||
<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>
|
||||
) : null}
|
||||
|
||||
<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>
|
||||
<Row>
|
||||
<Text
|
||||
style={
|
||||
props.vc?.verifiableCredential.credentialSubject.email
|
||||
.length > 25
|
||||
? { flex: 1 }
|
||||
: { flex: 0 }
|
||||
}
|
||||
weight="semibold"
|
||||
size="smaller"
|
||||
color={Theme.Colors.Details}>
|
||||
{getLocalizedField(
|
||||
props.vc?.verifiableCredential.credentialSubject.email
|
||||
)}
|
||||
</Text>
|
||||
</Row>
|
||||
</Column>
|
||||
|
||||
<Column fill style={Theme.Styles.labelPart}>
|
||||
<Text
|
||||
weight="bold"
|
||||
size="smaller"
|
||||
color={Theme.Colors.DetailsLabel}>
|
||||
{t('address')}
|
||||
</Text>
|
||||
<Row>
|
||||
<Text
|
||||
style={{ flex: 1 }}
|
||||
weight="semibold"
|
||||
size="smaller"
|
||||
color={Theme.Colors.Details}>
|
||||
{getFullAddress(
|
||||
props.vc?.verifiableCredential.credentialSubject
|
||||
)}
|
||||
</Text>
|
||||
</Row>
|
||||
</Column>
|
||||
</Row>
|
||||
</Column>
|
||||
</Row>
|
||||
<View style={Theme.Styles.hrLine}></View>
|
||||
<Column>
|
||||
<Column fill style={Theme.Styles.labelPart}>
|
||||
<Text
|
||||
weight="bold"
|
||||
size="smaller"
|
||||
color={Theme.Colors.DetailsLabel}>
|
||||
{t('email')}
|
||||
</Text>
|
||||
<Row>
|
||||
<Text
|
||||
style={
|
||||
props.vc?.verifiableCredential.credentialSubject.email
|
||||
.length > 25
|
||||
? { flex: 1 }
|
||||
: { flex: 0 }
|
||||
}
|
||||
weight="semibold"
|
||||
size="smaller"
|
||||
color={Theme.Colors.Details}>
|
||||
{getLocalizedField(
|
||||
props.vc?.verifiableCredential.credentialSubject.email
|
||||
)}
|
||||
</Text>
|
||||
</Row>
|
||||
</Column>
|
||||
|
||||
<Column style={Theme.Styles.labelPart}>
|
||||
<Text
|
||||
weight="bold"
|
||||
size="smaller"
|
||||
color={Theme.Colors.DetailsLabel}>
|
||||
{t('address')}
|
||||
</Text>
|
||||
<Row>
|
||||
<Text
|
||||
style={{ flex: 1 }}
|
||||
weight="semibold"
|
||||
size="smaller"
|
||||
color={Theme.Colors.Details}>
|
||||
{getFullAddress(
|
||||
props.vc?.verifiableCredential.credentialSubject
|
||||
)}
|
||||
</Text>
|
||||
</Row>
|
||||
</Column>
|
||||
</Column>
|
||||
<VcItemTags tag={props.vc?.tag} />
|
||||
</ImageBackground>
|
||||
|
||||
|
||||
@@ -1,12 +1,6 @@
|
||||
import React, { useContext, useRef } from 'react';
|
||||
import React, { useContext, useRef, useState } from 'react';
|
||||
import { useInterpret, useSelector } from '@xstate/react';
|
||||
import {
|
||||
Pressable,
|
||||
Image,
|
||||
ImageBackground,
|
||||
Dimensions,
|
||||
View,
|
||||
} from 'react-native';
|
||||
import { Pressable, Image, ImageBackground, Dimensions } from 'react-native';
|
||||
import { CheckBox, Icon } from 'react-native-elements';
|
||||
import { ActorRefFrom } from 'xstate';
|
||||
import {
|
||||
@@ -17,14 +11,16 @@ import {
|
||||
selectContext,
|
||||
selectTag,
|
||||
selectEmptyWalletBindingId,
|
||||
selectKebabPopUp,
|
||||
VcItemEvents,
|
||||
} 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';
|
||||
import { LocalizedField } from '../types/vc';
|
||||
import { VcItemTags } from './VcItemTags';
|
||||
import { KebabPopUp } from './KebabPopUp';
|
||||
import VerifiedIcon from './VerifiedIcon';
|
||||
|
||||
const getDetails = (arg1, arg2, verifiableCredential) => {
|
||||
@@ -126,7 +122,9 @@ export const VcItem: React.FC<VcItemProps> = (props) => {
|
||||
const context = useSelector(service, selectContext);
|
||||
const verifiableCredential = useSelector(service, selectVerifiableCredential);
|
||||
const emptyWalletBindingId = useSelector(service, selectEmptyWalletBindingId);
|
||||
|
||||
const isKebabPopUp = useSelector(service, selectKebabPopUp);
|
||||
const KEBAB_POPUP = () => service.send(VcItemEvents.KEBAB_POPUP());
|
||||
const DISMISS = () => service.send(VcItemEvents.DISMISS());
|
||||
//Assigning the UIN and VID from the VC details to display the idtype label
|
||||
const uin = verifiableCredential?.credentialSubject.UIN;
|
||||
const vid = verifiableCredential?.credentialSubject.VID;
|
||||
@@ -135,16 +133,24 @@ export const VcItem: React.FC<VcItemProps> = (props) => {
|
||||
const fullName = !verifiableCredential
|
||||
? ''
|
||||
: getLocalizedField(verifiableCredential.credentialSubject.fullName);
|
||||
const isvalid = !verifiableCredential ? '' : t('valid');
|
||||
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;
|
||||
|
||||
const tag = useSelector(service, selectTag);
|
||||
|
||||
return (
|
||||
<Pressable
|
||||
onPress={() => props.onPress(service)}
|
||||
disabled={!verifiableCredential}
|
||||
style={
|
||||
props.selected
|
||||
? Theme.Styles.selectedBindedVc
|
||||
? Theme.Styles.selectedVc
|
||||
: Theme.Styles.closeCardBgContainer
|
||||
}>
|
||||
<ImageBackground
|
||||
@@ -156,140 +162,124 @@ export const VcItem: React.FC<VcItemProps> = (props) => {
|
||||
? Theme.Styles.vertloadingContainer
|
||||
: Theme.Styles.backgroundImageContainer
|
||||
}>
|
||||
<Row style={Theme.Styles.homeCloseCardDetailsHeader}>
|
||||
<Column>
|
||||
<Text
|
||||
color={
|
||||
!verifiableCredential
|
||||
? Theme.Colors.LoadingDetailsLabel
|
||||
: Theme.Colors.DetailsLabel
|
||||
}
|
||||
weight="bold"
|
||||
size="smaller">
|
||||
{t('idType')}
|
||||
</Text>
|
||||
<Text
|
||||
weight="bold"
|
||||
color={Theme.Colors.Details}
|
||||
size="smaller"
|
||||
style={
|
||||
!verifiableCredential
|
||||
? Theme.Styles.loadingTitle
|
||||
: Theme.Styles.subtitle
|
||||
}>
|
||||
{t('nationalCard')}
|
||||
</Text>
|
||||
</Column>
|
||||
<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 25" style={{ alignItems: 'flex-start' }}>
|
||||
{getDetails(t('fullName'), fullName, verifiableCredential)}
|
||||
{!verifiableCredential
|
||||
? getDetails(t('id'), uin || vid, verifiableCredential)
|
||||
: null}
|
||||
{uin ? getDetails(t('uin'), uin, verifiableCredential) : null}
|
||||
{vid ? getDetails(t('vid'), vid, verifiableCredential) : null}
|
||||
{getDetails(t('generatedOn'), generatedOn, verifiableCredential)}
|
||||
{getDetails(t('status'), t('valid'), verifiableCredential)}
|
||||
</Column>
|
||||
</Column>
|
||||
|
||||
{!verifiableCredential && (
|
||||
<RotatingIcon name="sync" color={Theme.Colors.rotatingIcon} />
|
||||
)}
|
||||
</Row>
|
||||
<VcItemTags tag={tag} />
|
||||
</ImageBackground>
|
||||
{props.activeTab !== 'receivedVcsTab' &&
|
||||
props.activeTab != 'sharingVcScreen' && (
|
||||
<Row>
|
||||
{emptyWalletBindingId ? (
|
||||
<Row
|
||||
width={Dimensions.get('screen').width * 0.8}
|
||||
align="space-between"
|
||||
crossAlign="center">
|
||||
<Row crossAlign="center" style={{ flex: 1 }}>
|
||||
{verifiableCredential && <WalletUnverified />}
|
||||
<Text
|
||||
color={Theme.Colors.Details}
|
||||
weight="semibold"
|
||||
size="small"
|
||||
margin="10 33 10 10"
|
||||
style={
|
||||
!verifiableCredential
|
||||
? Theme.Styles.loadingTitle
|
||||
: Theme.Styles.statusLabel
|
||||
}
|
||||
children={t('offlineAuthDisabledHeader')}></Text>
|
||||
</Row>
|
||||
|
||||
<Pressable
|
||||
onPress={() =>
|
||||
verifiableCredential ? props.onPress(service) : null
|
||||
}>
|
||||
<Column>
|
||||
<Row align="space-between">
|
||||
<Row>
|
||||
<ImageBackground
|
||||
source={
|
||||
!verifiableCredential
|
||||
? Theme.ProfileIcon
|
||||
: { uri: context.credential.biometrics.face }
|
||||
}
|
||||
style={Theme.Styles.closeCardImage}>
|
||||
{props.iconName && (
|
||||
<Icon
|
||||
name="dots-three-horizontal"
|
||||
type="entypo"
|
||||
color={Theme.Colors.GrayIcon}
|
||||
name={props.iconName}
|
||||
type={props.iconType}
|
||||
color={Theme.Colors.Icon}
|
||||
style={{ marginLeft: -80 }}
|
||||
/>
|
||||
</Pressable>
|
||||
</Row>
|
||||
) : (
|
||||
<Row
|
||||
width={Dimensions.get('screen').width * 0.8}
|
||||
align="space-between"
|
||||
crossAlign="center">
|
||||
<Row crossAlign="center" style={{ flex: 1 }}>
|
||||
<WalletVerified />
|
||||
)}
|
||||
</ImageBackground>
|
||||
<Column margin="0 0 0 10">
|
||||
{getDetails(t('fullName'), fullName, verifiableCredential)}
|
||||
|
||||
<Column margin="10 0 0 0">
|
||||
<Text
|
||||
color={Theme.Colors.statusLabel}
|
||||
color={
|
||||
!verifiableCredential
|
||||
? Theme.Colors.LoadingDetailsLabel
|
||||
: Theme.Colors.DetailsLabel
|
||||
}
|
||||
weight="semibold"
|
||||
size="smaller"
|
||||
margin="10 10 10 10"
|
||||
align="left">
|
||||
{t('idType')}
|
||||
</Text>
|
||||
<Text
|
||||
weight="regular"
|
||||
color={Theme.Colors.Details}
|
||||
size="smaller"
|
||||
style={
|
||||
!verifiableCredential
|
||||
? Theme.Styles.loadingTitle
|
||||
: Theme.Styles.subtitle
|
||||
}
|
||||
children={t('profileAuthenticated')}></Text>
|
||||
</Row>
|
||||
}>
|
||||
{t('nationalCard')}
|
||||
</Text>
|
||||
</Column>
|
||||
</Column>
|
||||
</Row>
|
||||
|
||||
{props.showOnlyBindedVc ? null : (
|
||||
<Pressable onPress={() => props.onPress(service)}>
|
||||
<Icon
|
||||
name="dots-three-horizontal"
|
||||
type="entypo"
|
||||
color={Theme.Colors.GrayIcon}
|
||||
/>
|
||||
</Pressable>
|
||||
)}
|
||||
</Row>
|
||||
)}
|
||||
<Column>{verifiableCredential ? selectableOrCheck : null}</Column>
|
||||
</Row>
|
||||
|
||||
<Row
|
||||
align="space-between"
|
||||
margin="5 0 0 0"
|
||||
style={
|
||||
!verifiableCredential ? Theme.Styles.loadingContainer : null
|
||||
}>
|
||||
<Column>
|
||||
{uin ? getDetails(t('uin'), uin, verifiableCredential) : null}
|
||||
{vid ? getDetails(t('vid'), vid, verifiableCredential) : null}
|
||||
{!verifiableCredential
|
||||
? getDetails(t('id'), uin || vid, verifiableCredential)
|
||||
: null}
|
||||
{getDetails(t('generatedOn'), generatedOn, verifiableCredential)}
|
||||
</Column>
|
||||
<Column>
|
||||
{verifiableCredential
|
||||
? getDetails(t('status'), isvalid, verifiableCredential)
|
||||
: null}
|
||||
</Column>
|
||||
<Column style={{ display: verifiableCredential ? 'flex' : 'none' }}>
|
||||
<Image
|
||||
source={Theme.MosipLogo}
|
||||
style={Theme.Styles.logo}
|
||||
resizeMethod="auto"
|
||||
/>
|
||||
</Column>
|
||||
</Row>
|
||||
</Column>
|
||||
<VcItemTags tag={tag} />
|
||||
</ImageBackground>
|
||||
<Row
|
||||
width={Dimensions.get('screen').width * 0.8}
|
||||
align="space-between"
|
||||
crossAlign="center">
|
||||
<Row crossAlign="center" style={{ flex: 1 }}>
|
||||
{verifiableCredential &&
|
||||
(emptyWalletBindingId ? <WalletUnverified /> : <WalletVerified />)}
|
||||
<Text
|
||||
color={Theme.Colors.Details}
|
||||
weight="semibold"
|
||||
size="small"
|
||||
margin="10 33 10 10"
|
||||
style={
|
||||
!verifiableCredential
|
||||
? Theme.Styles.loadingTitle
|
||||
: Theme.Styles.subtitle
|
||||
}
|
||||
children={
|
||||
emptyWalletBindingId
|
||||
? t('offlineAuthDisabledHeader')
|
||||
: t('profileAuthenticated')
|
||||
}></Text>
|
||||
</Row>
|
||||
{verifiableCredential && (
|
||||
<Pressable onPress={KEBAB_POPUP}>
|
||||
<KebabPopUp
|
||||
vcKey={props.vcKey}
|
||||
iconName="dots-three-horizontal"
|
||||
iconType="entypo"
|
||||
isVisible={isKebabPopUp}
|
||||
onDismiss={DISMISS}
|
||||
service={service}
|
||||
/>
|
||||
</Pressable>
|
||||
)}
|
||||
</Row>
|
||||
</Pressable>
|
||||
);
|
||||
};
|
||||
@@ -302,7 +292,8 @@ interface VcItemProps {
|
||||
showOnlyBindedVc?: boolean;
|
||||
onPress?: (vcRef?: ActorRefFrom<typeof vcItemMachine>) => void;
|
||||
onShow?: (vcRef?: ActorRefFrom<typeof vcItemMachine>) => void;
|
||||
activeTab?: string;
|
||||
iconName?: string;
|
||||
iconType?: string;
|
||||
}
|
||||
|
||||
function getLocalizedField(rawField: string | LocalizedField) {
|
||||
|
||||
@@ -8,7 +8,7 @@ import { Text } from './Text';
|
||||
import { Theme, Spacing } from './styleUtils';
|
||||
|
||||
export const Button: React.FC<ButtonProps> = (props) => {
|
||||
const type = props.type || 'solid';
|
||||
const type = props.type || 'solid' || 'radius' || 'gradient';
|
||||
const buttonStyle: StyleProp<ViewStyle> = [
|
||||
props.fill ? Theme.ButtonStyles.fill : null,
|
||||
Theme.ButtonStyles[type],
|
||||
@@ -16,9 +16,14 @@ export const Button: React.FC<ButtonProps> = (props) => {
|
||||
];
|
||||
|
||||
const containerStyle: StyleProp<ViewStyle> = [
|
||||
Theme.ButtonStyles.container,
|
||||
!(type === 'gradient') ? Theme.ButtonStyles.container : null,
|
||||
props.disabled ? Theme.ButtonStyles.disabled : null,
|
||||
props.margin ? Theme.spacing('margin', props.margin) : null,
|
||||
type === 'gradient'
|
||||
? props.isVcThere
|
||||
? Theme.ButtonStyles.float
|
||||
: Theme.ButtonStyles.gradient
|
||||
: null,
|
||||
props.styles,
|
||||
];
|
||||
|
||||
@@ -28,7 +33,7 @@ export const Button: React.FC<ButtonProps> = (props) => {
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
return !(type === 'gradient') ? (
|
||||
<RNEButton
|
||||
buttonStyle={buttonStyle}
|
||||
containerStyle={[
|
||||
@@ -44,6 +49,8 @@ export const Button: React.FC<ButtonProps> = (props) => {
|
||||
color={
|
||||
type === 'solid' || type === 'addId' || type === 'radius'
|
||||
? Theme.Colors.whiteText
|
||||
: type === 'plain'
|
||||
? Theme.Colors.plainText
|
||||
: Theme.Colors.AddIdBtnTxt
|
||||
}>
|
||||
{props.title}
|
||||
@@ -54,6 +61,36 @@ export const Button: React.FC<ButtonProps> = (props) => {
|
||||
onPress={handleOnPress}
|
||||
loading={props.loading}
|
||||
/>
|
||||
) : (
|
||||
<RNEButton
|
||||
ViewComponent={require('react-native-linear-gradient').default}
|
||||
linearGradientProps={{
|
||||
colors: !props.disabled
|
||||
? Theme.Colors.GradientColors
|
||||
: Theme.Colors.DisabledColors,
|
||||
}}
|
||||
containerStyle={
|
||||
props.isVcThere ? containerStyle : Theme.ButtonStyles.gradient
|
||||
}
|
||||
type={props.type}
|
||||
raised={props.raised}
|
||||
title={
|
||||
<Text
|
||||
weight="semibold"
|
||||
style={Theme.TextStyles.bold}
|
||||
color={
|
||||
type === 'solid' || type === 'gradient' || type === 'radius'
|
||||
? Theme.Colors.whiteText
|
||||
: Theme.Colors.DownloadIdBtnTxt
|
||||
}>
|
||||
{props.title}
|
||||
</Text>
|
||||
}
|
||||
buttonStyle={!props.isVcThere ? { height: 45 } : { height: 39 }}
|
||||
icon={props.icon}
|
||||
onPress={handleOnPress}
|
||||
loading={props.loading}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -62,10 +99,12 @@ interface ButtonProps {
|
||||
disabled?: boolean;
|
||||
margin?: Spacing;
|
||||
type?: RNEButtonProps['type'];
|
||||
isVcThere?: boolean;
|
||||
onPress?: RNEButtonProps['onPress'];
|
||||
fill?: boolean;
|
||||
raised?: boolean;
|
||||
loading?: boolean;
|
||||
icon?: RNEButtonProps['icon'];
|
||||
styles?: StyleProp<ViewStyle>;
|
||||
colors?: (string | number)[];
|
||||
}
|
||||
|
||||
@@ -2,9 +2,13 @@ import React from 'react';
|
||||
import { I18nManager, Modal as RNModal, View } from 'react-native';
|
||||
import { Icon } from 'react-native-elements';
|
||||
import { Column, Row, Text } from '.';
|
||||
import { useSendVcScreen } from '../../screens/Scan/SendVcScreenController';
|
||||
import { DeviceInfoList } from '../DeviceInfoList';
|
||||
import { ElevationLevel, Theme } from './styleUtils';
|
||||
|
||||
export const Modal: React.FC<ModalProps> = (props) => {
|
||||
const controller = useSendVcScreen();
|
||||
|
||||
return (
|
||||
<RNModal
|
||||
animationType="slide"
|
||||
@@ -19,8 +23,8 @@ export const Modal: React.FC<ModalProps> = (props) => {
|
||||
flex: 1,
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
marginHorizontal: 21,
|
||||
marginVertical: 16,
|
||||
marginHorizontal: 18,
|
||||
marginVertical: 15,
|
||||
}}>
|
||||
{props.headerRight ? (
|
||||
<Icon
|
||||
@@ -34,17 +38,45 @@ export const Modal: React.FC<ModalProps> = (props) => {
|
||||
name="arrow-left"
|
||||
type="material-community"
|
||||
onPress={props.onDismiss}
|
||||
color={Theme.Colors.Details}
|
||||
containerStyle={Theme.Styles.backArrowContainer}
|
||||
color={Theme.Colors.Icon}
|
||||
/>
|
||||
) : null}
|
||||
<Row fill align="center" margin={'5 30 0 0'}>
|
||||
<Text weight="semibold">{props.headerTitle}</Text>
|
||||
<Row
|
||||
fill
|
||||
align={props.headerLeft ? 'flex-start' : 'center'}
|
||||
margin={'16 0 0 0'}>
|
||||
<Column>
|
||||
<Text style={Theme.TextStyles.header}>
|
||||
{props.headerTitle || props.headerLeft}
|
||||
</Text>
|
||||
{!props.requester ? (
|
||||
<Text
|
||||
weight="semibold"
|
||||
style={Theme.TextStyles.small}
|
||||
color={
|
||||
props.headerLabelColor
|
||||
? props.headerLabelColor
|
||||
: Theme.Colors.profileLanguageValue
|
||||
}>
|
||||
{props.headerLabel}
|
||||
</Text>
|
||||
) : (
|
||||
<Text
|
||||
weight="semibold"
|
||||
style={Theme.TextStyles.small}
|
||||
color={Theme.Colors.IconBg}>
|
||||
<DeviceInfoList deviceInfo={controller.receiverInfo} />
|
||||
</Text>
|
||||
)}
|
||||
</Column>
|
||||
</Row>
|
||||
{props.headerRight || props.arrowLeft || (
|
||||
<Icon
|
||||
name="close"
|
||||
onPress={props.onDismiss}
|
||||
color={Theme.Colors.Icon}
|
||||
color={Theme.Colors.Details}
|
||||
size={27}
|
||||
/>
|
||||
)}
|
||||
</View>
|
||||
@@ -57,10 +89,14 @@ export const Modal: React.FC<ModalProps> = (props) => {
|
||||
|
||||
export interface ModalProps {
|
||||
isVisible: boolean;
|
||||
onDismiss: () => void;
|
||||
requester?: boolean;
|
||||
onDismiss?: () => void;
|
||||
headerTitle?: string;
|
||||
headerElevation?: ElevationLevel;
|
||||
headerLabel?: string;
|
||||
headerLabelColor?: string;
|
||||
headerRight?: React.ReactElement;
|
||||
headerLeft?: React.ReactElement;
|
||||
arrowLeft?: React.ReactElement;
|
||||
onShow?: () => void;
|
||||
}
|
||||
|
||||
69
components/ui/SetupPicker.tsx
Normal file
@@ -0,0 +1,69 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { Dimensions } from 'react-native';
|
||||
import { Icon, ListItem } from 'react-native-elements';
|
||||
import { Column } from './Layout';
|
||||
import { Text } from './Text';
|
||||
import { Theme } from './styleUtils';
|
||||
|
||||
interface Picker extends React.VFC<PickerProps<unknown>> {
|
||||
<T>(props: PickerProps<T>): ReturnType<React.FC>;
|
||||
}
|
||||
|
||||
export const SetupPicker: Picker = (props: PickerProps<unknown>) => {
|
||||
const [isContentVisible, setIsContentVisible] = useState(false);
|
||||
const [selectedIndex, setSelectedIndex] = useState(-1);
|
||||
|
||||
useEffect(() => {
|
||||
setSelectedIndex(
|
||||
props.items.findIndex(({ value }) => value === props.selectedValue)
|
||||
);
|
||||
}, [props.selectedValue]);
|
||||
|
||||
const toggleContent = () => setIsContentVisible(!isContentVisible);
|
||||
|
||||
const selectItem = (index: number) => {
|
||||
setSelectedIndex(index);
|
||||
props.onValueChange(props.items[index].value, index);
|
||||
toggleContent();
|
||||
};
|
||||
|
||||
return (
|
||||
<Column
|
||||
width={Dimensions.get('window').width * 0.8}
|
||||
backgroundColor={Theme.Colors.whiteBackgroundColor}>
|
||||
{props.items.map((item, index) => (
|
||||
<ListItem
|
||||
bottomDivider
|
||||
topDivider={index !== 0}
|
||||
onPress={() => selectItem(index)}
|
||||
key={index}>
|
||||
<ListItem.Content>
|
||||
<ListItem.Title>
|
||||
<Text
|
||||
color={selectedIndex === index ? Theme.Colors.Icon : null}
|
||||
weight={selectedIndex === index ? 'semibold' : 'regular'}>
|
||||
{item.label}
|
||||
</Text>
|
||||
</ListItem.Title>
|
||||
</ListItem.Content>
|
||||
{selectedIndex === index ? (
|
||||
<Icon name="radio-button-checked" color={Theme.Colors.Icon} />
|
||||
) : (
|
||||
<Icon name="radio-button-unchecked" color={Theme.Colors.GrayIcon} />
|
||||
)}
|
||||
</ListItem>
|
||||
))}
|
||||
</Column>
|
||||
);
|
||||
};
|
||||
|
||||
interface PickerProps<T> {
|
||||
items: PickerItem<T>[];
|
||||
selectedValue: T;
|
||||
onValueChange: (value: T, index: number) => void;
|
||||
}
|
||||
|
||||
interface PickerItem<T> {
|
||||
label: string;
|
||||
value: T;
|
||||
}
|
||||
@@ -10,10 +10,17 @@ export const TextItem: React.FC<TextItemProps> = (props) => {
|
||||
pX={24}
|
||||
pY={props.label ? 16 : 12}
|
||||
style={{
|
||||
borderBottomColor: Theme.Colors.borderBottomColor,
|
||||
borderBottomWidth: props.divider ? 1 : 0,
|
||||
borderColor: Theme.Colors.borderBottomColor,
|
||||
borderBottomWidth: props.divider ? 2 : 0,
|
||||
borderTopWidth: props.topDivider ? 2 : 0,
|
||||
alignItems: 'flex-start',
|
||||
}}>
|
||||
<Text
|
||||
color={Theme.Colors.textValue}
|
||||
weight={props.label ? 'semibold' : 'regular'}
|
||||
style={{ textAlign: 'left' }}>
|
||||
{props.text}
|
||||
</Text>
|
||||
{props.label && (
|
||||
<Text
|
||||
size="smaller"
|
||||
@@ -23,12 +30,6 @@ export const TextItem: React.FC<TextItemProps> = (props) => {
|
||||
{props.label}
|
||||
</Text>
|
||||
)}
|
||||
<Text
|
||||
color={Theme.Colors.textValue}
|
||||
weight={props.label ? 'semibold' : 'regular'}
|
||||
style={{ textAlign: 'left' }}>
|
||||
{props.text}
|
||||
</Text>
|
||||
</Column>
|
||||
);
|
||||
};
|
||||
@@ -37,5 +38,6 @@ interface TextItemProps {
|
||||
text: string;
|
||||
label?: string;
|
||||
divider?: boolean;
|
||||
topDivider?: boolean;
|
||||
margin?: string;
|
||||
}
|
||||
|
||||
@@ -3,23 +3,35 @@ import { Dimensions, StyleSheet, ViewStyle } from 'react-native';
|
||||
import { Spacing } from '../styleUtils';
|
||||
|
||||
const Colors = {
|
||||
Black: '#231F20',
|
||||
Grey: '#B0B0B0',
|
||||
Black: '#000000',
|
||||
Zambezi: '#5F5F5F',
|
||||
Grey: '#C7C7C7',
|
||||
Grey5: '#E0E0E0',
|
||||
Grey6: '#F2F2F2',
|
||||
Gray40: '#666666',
|
||||
Gray9: '#171717',
|
||||
DimGray: '#737373',
|
||||
Orange: '#F2811D',
|
||||
LightGrey: '#f7f5f0',
|
||||
LightGrey: '#F5F5F5',
|
||||
ShadeOfGrey: '#6F6F6F',
|
||||
White: '#FFFFFF',
|
||||
Red: '#EB5757',
|
||||
Green: '#219653',
|
||||
Red: '#D52929',
|
||||
Green: '#4B9D20',
|
||||
Transparent: 'transparent',
|
||||
Warning: '#f0ad4e',
|
||||
LightOrange: '#fce7e3',
|
||||
GrayText: '#6F6F6F',
|
||||
dorColor: '#CBCBCB',
|
||||
plainText: '#FFD6A7',
|
||||
walletbindingLabel: '#000000',
|
||||
walletbindingContent: '#666666',
|
||||
LightOrange: '#FDF1E6',
|
||||
GradientColors: ['#F59B4B', '#E86E04'],
|
||||
DisabledColors: ['#C7C7C7', '#C7C7C7'],
|
||||
TimeoutHintBoxColor: '#FFF7E5',
|
||||
TimoutText: '#8B6105',
|
||||
};
|
||||
|
||||
export type ElevationLevel = 0 | 1 | 2 | 3 | 4 | 5;
|
||||
export type ElevationLevel = 0 | 1 | 2 | 3 | 4 | 5 | 6;
|
||||
|
||||
export const DefaultTheme = {
|
||||
Colors: {
|
||||
@@ -29,10 +41,11 @@ export const DefaultTheme = {
|
||||
LoadingDetailsLabel: Colors.Orange,
|
||||
AddIdBtnBg: Colors.Orange,
|
||||
AddIdBtnTxt: Colors.Orange,
|
||||
ClearAddIdBtnBg: Colors.Transparent,
|
||||
DownloadIdBtnTxt: Colors.White,
|
||||
Loading: Colors.Orange,
|
||||
noUinText: Colors.Orange,
|
||||
IconBg: Colors.Orange,
|
||||
popUp: Colors.Green,
|
||||
Icon: Colors.Orange,
|
||||
GrayIcon: Colors.Grey,
|
||||
borderBottomColor: Colors.Grey6,
|
||||
@@ -51,6 +64,7 @@ export const DefaultTheme = {
|
||||
loadingLabel: Colors.Grey6,
|
||||
textLabel: Colors.Grey,
|
||||
textValue: Colors.Black,
|
||||
requesterName: Colors.Red,
|
||||
errorMessage: Colors.Red,
|
||||
QRCodeBackgroundColor: Colors.LightGrey,
|
||||
ReceiveVcModalBackgroundColor: Colors.LightGrey,
|
||||
@@ -59,15 +73,26 @@ export const DefaultTheme = {
|
||||
whiteText: Colors.White,
|
||||
flipCameraIcon: Colors.Black,
|
||||
IdInputModalBorder: Colors.Grey,
|
||||
RetrieveIdLabel: Colors.ShadeOfGrey,
|
||||
inputSelection: Colors.Orange,
|
||||
checkCircleIcon: Colors.White,
|
||||
OnboardingCircleIcon: Colors.White,
|
||||
OnboardingCloseIcon: Colors.White,
|
||||
WarningIcon: Colors.Warning,
|
||||
DefaultToggle: Colors.LightOrange,
|
||||
ProfileIconBg: Colors.LightOrange,
|
||||
Cursor: Colors.Orange,
|
||||
version: Colors.DimGray,
|
||||
poweredByBLE: Colors.Gray9,
|
||||
GrayText: Colors.GrayText,
|
||||
gradientBtn: ['#F59B4B', '#E86E04'],
|
||||
dotColor: Colors.dorColor,
|
||||
plainText: Colors.plainText,
|
||||
IconBackground: Colors.LightOrange,
|
||||
GradientColors: Colors.GradientColors,
|
||||
DisabledColors: Colors.DisabledColors,
|
||||
getVidColor: Colors.Zambezi,
|
||||
TimeoutHintBoxColor: Colors.TimeoutHintBoxColor,
|
||||
TimoutText: Colors.TimoutText,
|
||||
walletbindingLabel: Colors.Black,
|
||||
walletbindingContent: Colors.Gray40,
|
||||
},
|
||||
Styles: StyleSheet.create({
|
||||
title: {
|
||||
@@ -110,6 +135,31 @@ export const DefaultTheme = {
|
||||
backgroundColor: Colors.Grey6,
|
||||
borderRadius: 4,
|
||||
},
|
||||
bottomTabIconStyle: {
|
||||
padding: 4,
|
||||
width: 36,
|
||||
height: 36,
|
||||
borderRadius: 6,
|
||||
backgroundColor: Colors.LightOrange,
|
||||
},
|
||||
popUp: {
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-between',
|
||||
backgroundColor: Colors.Green,
|
||||
height: 39,
|
||||
position: 'relative',
|
||||
paddingHorizontal: 12,
|
||||
},
|
||||
homeScreenContainer: {
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
borderRadius: 10,
|
||||
backgroundColor: '#fff',
|
||||
shadowColor: '#000',
|
||||
shadowOpacity: 0.4,
|
||||
elevation: 5,
|
||||
padding: 10,
|
||||
},
|
||||
vertloadingContainer: {
|
||||
flex: 1,
|
||||
backgroundColor: Colors.Grey6,
|
||||
@@ -120,10 +170,11 @@ export const DefaultTheme = {
|
||||
flex: 1,
|
||||
justifyContent: 'flex-start',
|
||||
},
|
||||
logoContainer: {
|
||||
closecardMosipLogo: {
|
||||
flex: 1,
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'flex-end',
|
||||
alignSelf: 'flex-end',
|
||||
marginLeft: 300,
|
||||
},
|
||||
closeCardBgContainer: {
|
||||
@@ -136,7 +187,7 @@ export const DefaultTheme = {
|
||||
shadowRadius: 3,
|
||||
elevation: 4,
|
||||
},
|
||||
selectedBindedVc: {
|
||||
selectedVc: {
|
||||
borderRadius: 10,
|
||||
margin: 5,
|
||||
borderWidth: 2,
|
||||
@@ -159,7 +210,7 @@ export const DefaultTheme = {
|
||||
width: 100,
|
||||
},
|
||||
bottomButtonsContainer: {
|
||||
height: 120,
|
||||
height: 'auto',
|
||||
borderTopLeftRadius: 27,
|
||||
borderTopRightRadius: 27,
|
||||
padding: 6,
|
||||
@@ -217,12 +268,11 @@ export const DefaultTheme = {
|
||||
justifyContent: 'space-between',
|
||||
},
|
||||
logo: {
|
||||
height: 36,
|
||||
width: 30,
|
||||
height: 46,
|
||||
width: 40,
|
||||
},
|
||||
homeCloseCardDetailsHeader: {
|
||||
flex: 1,
|
||||
justifyContent: 'space-between',
|
||||
},
|
||||
details: {
|
||||
width: 290,
|
||||
@@ -233,11 +283,27 @@ export const DefaultTheme = {
|
||||
flex: 1,
|
||||
padding: 10,
|
||||
},
|
||||
profileIconBg: {
|
||||
padding: 8,
|
||||
width: 40,
|
||||
height: 40,
|
||||
borderRadius: 6,
|
||||
IconContainer: {
|
||||
padding: 6,
|
||||
width: 36,
|
||||
marginRight: 4,
|
||||
marginLeft: 10,
|
||||
height: 36,
|
||||
borderRadius: 10,
|
||||
backgroundColor: Colors.LightOrange,
|
||||
},
|
||||
settingsIconBg: {
|
||||
padding: 6,
|
||||
width: 36,
|
||||
marginRight: 4,
|
||||
height: 36,
|
||||
backgroundColor: Colors.Transparent,
|
||||
},
|
||||
backArrowContainer: {
|
||||
padding: 6,
|
||||
width: 36,
|
||||
height: 36,
|
||||
borderRadius: 10,
|
||||
backgroundColor: Colors.LightOrange,
|
||||
},
|
||||
domainVerifiyIcon: {
|
||||
@@ -261,23 +327,22 @@ export const DefaultTheme = {
|
||||
borderRadius: 5,
|
||||
},
|
||||
scannerContainer: {
|
||||
borderWidth: 4,
|
||||
borderColor: Colors.Black,
|
||||
borderRadius: 32,
|
||||
justifyContent: 'center',
|
||||
height: 300,
|
||||
width: 300,
|
||||
alignSelf: 'center',
|
||||
height: 330,
|
||||
width: 320,
|
||||
overflow: 'hidden',
|
||||
marginLeft: 18,
|
||||
marginTop: -65,
|
||||
},
|
||||
scanner: {
|
||||
height: 400,
|
||||
width: '100%',
|
||||
margin: 'auto',
|
||||
},
|
||||
flipIconButton: {
|
||||
alignSelf: 'center',
|
||||
alignItems: 'center',
|
||||
photoConsentLabel: {
|
||||
backgroundColor: Colors.White,
|
||||
padding: 0,
|
||||
borderWidth: 0,
|
||||
},
|
||||
tabIndicator: {
|
||||
backgroundColor: Colors.Orange,
|
||||
@@ -292,53 +357,85 @@ export const DefaultTheme = {
|
||||
detailsText: {
|
||||
fontWeight: 'bold',
|
||||
fontSize: 15,
|
||||
fontFamily: 'Poppins_700Bold',
|
||||
fontFamily: 'Inter_700Bold',
|
||||
},
|
||||
getId: {
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
marginTop: 10,
|
||||
marginVertical: 6,
|
||||
},
|
||||
placeholder: {
|
||||
fontFamily: 'Poppins_400Regular',
|
||||
fontFamily: 'Inter_600SemiBold',
|
||||
},
|
||||
hrLine: {
|
||||
borderBottomColor: 'black',
|
||||
borderBottomWidth: 1,
|
||||
marginTop: 10,
|
||||
},
|
||||
}),
|
||||
PinInputStyle: StyleSheet.create({
|
||||
input: {
|
||||
borderBottomWidth: 1,
|
||||
borderBottomWidth: 3,
|
||||
borderColor: Colors.Grey,
|
||||
color: Colors.Black,
|
||||
flex: 1,
|
||||
fontFamily: 'Poppins_600SemiBold',
|
||||
fontSize: 18,
|
||||
fontWeight: '600',
|
||||
fontSize: 33,
|
||||
fontFamily: 'Inter_600SemiBold',
|
||||
height: 40,
|
||||
lineHeight: 28,
|
||||
margin: 8,
|
||||
textAlign: 'center',
|
||||
},
|
||||
onEnteringPin: {
|
||||
borderBottomWidth: 3,
|
||||
borderColor: Colors.Orange,
|
||||
color: Colors.Black,
|
||||
flex: 1,
|
||||
fontFamily: 'Inter_700Bold',
|
||||
fontSize: 29,
|
||||
height: 40,
|
||||
margin: 8,
|
||||
textAlign: 'center',
|
||||
},
|
||||
}),
|
||||
TextStyles: StyleSheet.create({
|
||||
header: {
|
||||
color: Colors.Black,
|
||||
fontFamily: 'Inter_700Bold',
|
||||
fontSize: 18,
|
||||
lineHeight: 22,
|
||||
paddingTop: 4,
|
||||
},
|
||||
retrieveIdLabel: {
|
||||
color: Colors.ShadeOfGrey,
|
||||
fontFamily: 'Inter_600SemiBold',
|
||||
lineHeight: 18,
|
||||
},
|
||||
error: {
|
||||
color: Colors.Red,
|
||||
fontFamily: 'Inter_600SemiBold',
|
||||
fontSize: 12,
|
||||
},
|
||||
base: {
|
||||
color: Colors.Black,
|
||||
fontSize: 18,
|
||||
lineHeight: 28,
|
||||
fontSize: 16,
|
||||
lineHeight: 18,
|
||||
},
|
||||
regular: {
|
||||
fontFamily: 'Poppins_400Regular',
|
||||
fontFamily: 'Inter_400Regular',
|
||||
},
|
||||
semibold: {
|
||||
fontFamily: 'Poppins_600SemiBold',
|
||||
fontFamily: 'Inter_600SemiBold',
|
||||
},
|
||||
bold: {
|
||||
fontFamily: 'Poppins_700Bold',
|
||||
fontFamily: 'Inter_700Bold',
|
||||
},
|
||||
small: {
|
||||
fontSize: 14,
|
||||
fontSize: 13,
|
||||
lineHeight: 21,
|
||||
},
|
||||
smaller: {
|
||||
fontSize: 12,
|
||||
fontSize: 11,
|
||||
lineHeight: 18,
|
||||
},
|
||||
}),
|
||||
@@ -395,15 +492,30 @@ export const DefaultTheme = {
|
||||
borderColor: Colors.Orange,
|
||||
},
|
||||
container: {
|
||||
minHeight: 48,
|
||||
height: 45,
|
||||
flexDirection: 'row',
|
||||
},
|
||||
disabled: {
|
||||
opacity: 0.5,
|
||||
backgroundColor: Colors.Grey,
|
||||
},
|
||||
addId: {
|
||||
backgroundColor: Colors.Orange,
|
||||
},
|
||||
gradient: {
|
||||
borderRadius: 9,
|
||||
width: '88%',
|
||||
alignSelf: 'center',
|
||||
margin: 4,
|
||||
},
|
||||
float: {
|
||||
borderRadius: 9,
|
||||
width: '34%',
|
||||
alignSelf: 'center',
|
||||
fontSize: 10,
|
||||
elevation: 5,
|
||||
position: 'absolute',
|
||||
bottom: 24,
|
||||
},
|
||||
clearAddIdBtnBg: {
|
||||
backgroundColor: Colors.Transparent,
|
||||
},
|
||||
@@ -429,6 +541,24 @@ export const DefaultTheme = {
|
||||
backgroundColor: Colors.White,
|
||||
padding: 0,
|
||||
},
|
||||
consentCheckContainer: {
|
||||
backgroundColor: Colors.White,
|
||||
borderWidth: 0,
|
||||
marginTop: -15,
|
||||
fontFamily: 'Inter_600SemiBold',
|
||||
padding: 0,
|
||||
},
|
||||
timeoutHintContainer: {
|
||||
backgroundColor: Colors.TimeoutHintBoxColor,
|
||||
margin: 21,
|
||||
paddingHorizontal: 14,
|
||||
paddingVertical: 12,
|
||||
borderRadius: 12,
|
||||
},
|
||||
sharedSuccessfully: {
|
||||
flex: 1,
|
||||
backgroundColor: Colors.White,
|
||||
},
|
||||
}),
|
||||
AppMetaDataStyles: StyleSheet.create({
|
||||
buttonContainer: {
|
||||
@@ -497,12 +627,33 @@ export const DefaultTheme = {
|
||||
height: Dimensions.get('screen').height,
|
||||
},
|
||||
}),
|
||||
KebabPopUpStyles: StyleSheet.create({
|
||||
kebabPopUp: {
|
||||
marginHorizontal: 4,
|
||||
},
|
||||
kebabHeaderStyle: {
|
||||
backgroundColor: 'white',
|
||||
borderTopLeftRadius: 24,
|
||||
borderTopRightRadius: 24,
|
||||
justifyContent: 'space-between',
|
||||
fontFamily: 'Inter_700Bold',
|
||||
paddingRight: 15,
|
||||
paddingLeft: 130,
|
||||
paddingTop: 18,
|
||||
},
|
||||
}),
|
||||
MessageOverlayStyles: StyleSheet.create({
|
||||
overlay: {
|
||||
elevation: 5,
|
||||
backgroundColor: Colors.White,
|
||||
padding: 0,
|
||||
},
|
||||
popupOverLay: {
|
||||
height: 260,
|
||||
backgroundColor: Colors.White,
|
||||
borderRadius: 15,
|
||||
margin: -13.5,
|
||||
},
|
||||
button: {
|
||||
borderTopLeftRadius: 0,
|
||||
borderTopRightRadius: 0,
|
||||
@@ -664,7 +815,7 @@ export const DefaultTheme = {
|
||||
sliderTitle: {
|
||||
color: Colors.White,
|
||||
marginBottom: 20,
|
||||
fontFamily: 'Poppins_700Bold',
|
||||
fontFamily: 'Inter_700Bold',
|
||||
},
|
||||
text: {
|
||||
color: Colors.White,
|
||||
@@ -684,14 +835,21 @@ export const DefaultTheme = {
|
||||
top: 40,
|
||||
zIndex: 1,
|
||||
},
|
||||
bottomContainer: {
|
||||
padding: 20,
|
||||
borderTopLeftRadius: 30,
|
||||
borderTopRightRadius: 30,
|
||||
marginTop: -185,
|
||||
paddingBottom: 100,
|
||||
},
|
||||
}),
|
||||
claimsContainer: StyleSheet.create({
|
||||
container: {
|
||||
backgroundColor: Colors.Transparent,
|
||||
},
|
||||
}),
|
||||
OpenCard: require('../../../assets/ID-open.png'),
|
||||
CloseCard: require('../../../assets/ID-closed.png'),
|
||||
OpenCard: '',
|
||||
CloseCard: '',
|
||||
ProfileIcon: require('../../../assets/placeholder-photo.png'),
|
||||
MosipSplashLogo: require('../../../assets/icon.png'),
|
||||
MosipLogo: require('../../../assets/mosip-logo.png'),
|
||||
@@ -699,6 +857,16 @@ export const DefaultTheme = {
|
||||
WarningLogo: require('../../../assets/warningLogo.png'),
|
||||
OtpLogo: require('../../../assets/otp-mobile-logo.png'),
|
||||
SuccessLogo: require('../../../assets/success-logo.png'),
|
||||
DigitalIdentityLogo: require('../../../assets/digital-identity-icon.png'),
|
||||
InjiLogoWhite: require('../../../assets/inji-logo-white.png'),
|
||||
InjiProgressingLogo: require('../../../assets/progressing-logo.png'),
|
||||
LockIcon: require('../../../assets/lock-icon.png'),
|
||||
InjiHomeLogo: require('../../../assets/inji-home-logo.png'),
|
||||
HelpIcon: require('../../../assets/help-icon.png'),
|
||||
sharingIntro: require('../../../assets/Secure-Sharing.png'),
|
||||
walletIntro: require('../../../assets/intro-wallet-binding.png'),
|
||||
IntroScanner: require('../../../assets/intro-scanner.png'),
|
||||
injiSmallLogo: require('../../../assets/inji_small_logo.png'),
|
||||
|
||||
elevation(level: ElevationLevel): ViewStyle {
|
||||
// https://ethercreative.github.io/react-native-shadow-generator/
|
||||
|
||||
@@ -18,6 +18,7 @@ const Colors = {
|
||||
Purple2: '#AEA7FF',
|
||||
Transparent: 'transparent',
|
||||
Warning: '#f0ad4e',
|
||||
GrayText: '#6F6F6F',
|
||||
};
|
||||
|
||||
export type ElevationLevel = 0 | 1 | 2 | 3 | 4 | 5;
|
||||
@@ -66,9 +67,8 @@ export const PurpleTheme = {
|
||||
OnboardingCircleIcon: Colors.White,
|
||||
OnboardingCloseIcon: Colors.White,
|
||||
WarningIcon: Colors.Warning,
|
||||
Cursor: Colors.Purple,
|
||||
version: Colors.DimGray,
|
||||
poweredByBLE: Colors.Gray9,
|
||||
GrayText: Colors.GrayText,
|
||||
gradientBtn: ['#F59B4B', '#E86E04'],
|
||||
},
|
||||
Styles: StyleSheet.create({
|
||||
title: {
|
||||
@@ -186,13 +186,16 @@ export const PurpleTheme = {
|
||||
justifyContent: 'space-between',
|
||||
},
|
||||
logo: {
|
||||
height: 36,
|
||||
width: 36,
|
||||
height: 46,
|
||||
width: 40,
|
||||
},
|
||||
homeCloseCardDetailsHeader: {
|
||||
flex: 1,
|
||||
justifyContent: 'space-between',
|
||||
},
|
||||
closecardMosipLogo: {
|
||||
alignSelf: 'flex-end',
|
||||
},
|
||||
details: {
|
||||
width: 290,
|
||||
marginLeft: 110,
|
||||
@@ -243,7 +246,7 @@ export const PurpleTheme = {
|
||||
detailsText: {
|
||||
fontWeight: 'bold',
|
||||
fontSize: 15,
|
||||
fontFamily: 'Poppins_700Bold',
|
||||
fontFamily: 'Inter_700Bold',
|
||||
},
|
||||
getId: {
|
||||
justifyContent: 'center',
|
||||
@@ -251,7 +254,17 @@ export const PurpleTheme = {
|
||||
marginTop: 10,
|
||||
},
|
||||
placeholder: {
|
||||
fontFamily: 'Poppins_400Regular',
|
||||
fontFamily: 'Inter_400Regular',
|
||||
},
|
||||
hrLine: {
|
||||
borderBottomColor: 'black',
|
||||
borderBottomWidth: 1,
|
||||
marginTop: 10,
|
||||
},
|
||||
hrLine: {
|
||||
borderBottomColor: 'black',
|
||||
borderBottomWidth: 1,
|
||||
marginTop: 10,
|
||||
},
|
||||
}),
|
||||
PinInputStyle: StyleSheet.create({
|
||||
@@ -260,7 +273,7 @@ export const PurpleTheme = {
|
||||
borderColor: Colors.Grey,
|
||||
color: Colors.Black,
|
||||
flex: 1,
|
||||
fontFamily: 'Poppins_600SemiBold',
|
||||
fontFamily: 'Inter_600SemiBold',
|
||||
fontSize: 18,
|
||||
fontWeight: '600',
|
||||
height: 40,
|
||||
@@ -276,13 +289,13 @@ export const PurpleTheme = {
|
||||
lineHeight: 28,
|
||||
},
|
||||
regular: {
|
||||
fontFamily: 'Poppins_400Regular',
|
||||
fontFamily: 'Inter_400Regular',
|
||||
},
|
||||
semibold: {
|
||||
fontFamily: 'Poppins_600SemiBold',
|
||||
fontFamily: 'Inter_600SemiBold',
|
||||
},
|
||||
bold: {
|
||||
fontFamily: 'Poppins_700Bold',
|
||||
fontFamily: 'Inter_700Bold',
|
||||
},
|
||||
small: {
|
||||
fontSize: 14,
|
||||
@@ -362,6 +375,12 @@ export const PurpleTheme = {
|
||||
borderRadius: 10,
|
||||
backgroundColor: Colors.Purple,
|
||||
},
|
||||
gradient: {
|
||||
borderRadius: 10,
|
||||
},
|
||||
gradient: {
|
||||
borderRadius: 10,
|
||||
},
|
||||
}),
|
||||
OIDCAuthStyles: StyleSheet.create({
|
||||
viewContainer: {
|
||||
@@ -603,7 +622,7 @@ export const PurpleTheme = {
|
||||
sliderTitle: {
|
||||
color: Colors.White,
|
||||
marginBottom: 20,
|
||||
fontFamily: 'Poppins_700Bold',
|
||||
fontFamily: 'Inter_700Bold',
|
||||
},
|
||||
text: {
|
||||
color: Colors.White,
|
||||
|
||||
12
i18n.ts
@@ -18,7 +18,7 @@ export const SUPPORTED_LANGUAGES = {
|
||||
ar: 'عربى',
|
||||
hi: 'हिंदी',
|
||||
kn: 'ಕನ್ನಡ',
|
||||
ta: 'தமிழ்',
|
||||
ta: 'ತಮಿಳು',
|
||||
};
|
||||
|
||||
i18next
|
||||
@@ -32,14 +32,18 @@ i18next
|
||||
})
|
||||
.then(async () => {
|
||||
const language = await AsyncStorage.getItem('language');
|
||||
if (language !== i18next.language) {
|
||||
i18next.changeLanguage(language);
|
||||
if (Object.keys(SUPPORTED_LANGUAGES).includes(i18next.language)) {
|
||||
if (language !== i18next.language) {
|
||||
i18next.changeLanguage(language);
|
||||
}
|
||||
} else {
|
||||
i18next.changeLanguage('en');
|
||||
}
|
||||
});
|
||||
|
||||
export default i18next;
|
||||
|
||||
function getLanguageCode(code: string) {
|
||||
export function getLanguageCode(code: string) {
|
||||
const [language] = code.split('-');
|
||||
return language;
|
||||
}
|
||||
|
||||
|
Before Width: | Height: | Size: 33 KiB After Width: | Height: | Size: 22 KiB |
155
locales/en.json
@@ -43,30 +43,55 @@
|
||||
"gender": "Gender",
|
||||
"dateOfBirth": "Date of birth",
|
||||
"phoneNumber": "Phone number",
|
||||
"email": "Email",
|
||||
"email": "Email Address",
|
||||
"address": "Address",
|
||||
"reasonForSharing": "Reason for sharing",
|
||||
"idType": "ID type",
|
||||
"id": "Id",
|
||||
"nationalCard": "National Card",
|
||||
"uin": "UIN",
|
||||
"enableVerification": "Activate",
|
||||
"profileAuthenticated": "Activated for online login",
|
||||
"offlineAuthDisabledHeader": "Activation pending for online login",
|
||||
"offlineAuthDisabledMessage": "Please click the button below to activate this credential to be used for online login.",
|
||||
"vid": "VID",
|
||||
"enabledAuthPopUp":"Credentials are enabled for online authentication.",
|
||||
"verificationEnabledSuccess": "Activated for online login",
|
||||
"goback": "GO BACK",
|
||||
"BindingWarning": "If you have enabled verification for this credential on another wallet, it will get overridden. Do you want to proceed?",
|
||||
"yes_confirm": "Yes, I Confirm",
|
||||
"no": "No",
|
||||
"Alert": "Alert",
|
||||
"ok": "Okay"
|
||||
"ok": "Okay",
|
||||
"downloading": "Downloading Your Card",
|
||||
"enableVerification": "Enable Verification",
|
||||
"profileAuthenticated": "Profile is authenticated!",
|
||||
"offlineAuthDisabledHeader": "Offline Authentication disabled!",
|
||||
"offlineAuthDisabledMessage": "Click here to enable this credentials to be used for offline authentication."
|
||||
},
|
||||
"HomeScreenKebabPopUp": {
|
||||
"title": "More Options",
|
||||
"unPinCard": "Unpin Card",
|
||||
"pinCard" : "Pin Card",
|
||||
"offlineAuthenticationDisabled!": "Offline authentication disabled!",
|
||||
"offlineAuthDisabledMessage": "Click here to enable this credentials to be used for offline authentication.",
|
||||
"viewActivityLog": "View activity log",
|
||||
"removeFromWallet": "Remove from wallet",
|
||||
"revokeId": "Revoke ID",
|
||||
"revokeMessage": "Revoke the virtual ID for this profile",
|
||||
"ActivityLog":"View Activity Log"
|
||||
},
|
||||
"WalletBinding": {
|
||||
"inProgress" : "In Progress",
|
||||
"profileAuthenticated": "Profile is authenticated!"
|
||||
},
|
||||
"BindingVcWarningOverlay": {
|
||||
"alert": "Please Confirm",
|
||||
"BindingWarning": "If you have enabled verification for this credential on another wallet, it will get overridden. Do you want to proceed?",
|
||||
"yesConfirm": "Yes, I confirm",
|
||||
"no" : "No"
|
||||
},
|
||||
"AuthScreen": {
|
||||
"header": "Would you like to use biometrics to unlock the application?",
|
||||
"useBiometrics": "Use biometrics",
|
||||
"usePasscode": "I'd rather use a passcode",
|
||||
"header": "Select App Unlock Method",
|
||||
"Description": "Would you like to use biometrics to unlock the application?",
|
||||
"useBiometrics": "Use Biometrics",
|
||||
"usePasscode": "Use Passcode",
|
||||
"errors": {
|
||||
"unavailable": "Device does not support Biometrics",
|
||||
"unenrolled": "To use Biometrics, please enroll your biometrics in your device settings",
|
||||
@@ -76,8 +101,8 @@
|
||||
},
|
||||
"BiometricScreen": {
|
||||
"unlock": "Unlock with biometrics"
|
||||
},
|
||||
"HistoryTab": {
|
||||
},
|
||||
"HistoryScreen": {
|
||||
"noHistory": "No history available yet",
|
||||
"downloaded": "downloaded",
|
||||
"shared": "shared",
|
||||
@@ -89,7 +114,16 @@
|
||||
"receivedVcsTab": "Received\n{{vcLabel}}",
|
||||
"historyTab": "History"
|
||||
},
|
||||
"SettingScreen": {
|
||||
"header": "Settings",
|
||||
"bioUnlock": "Bio Unlock",
|
||||
"language": "Language",
|
||||
"aboutInji": "About Inji",
|
||||
"injiTourGuide": "Inji Tour Guide",
|
||||
"logout": "Logout"
|
||||
},
|
||||
"AddVcModal": {
|
||||
"inputIdHeader":"Retrieve your ID",
|
||||
"requestingCredential": "Requesting credential...",
|
||||
"errors": {
|
||||
"input": {
|
||||
@@ -138,35 +172,45 @@
|
||||
"qstnMarkToolTip": "Application ID is available in the acknowledgement received after enrolment."
|
||||
},
|
||||
"IdInputModal": {
|
||||
"header": "Enter your UIN/VID to download your {{vcLabel}}",
|
||||
"generateVc": "Generate My {{vcLabel}}",
|
||||
"enterId": "Enter your {{idType}}",
|
||||
"noUIN/VID": "Don't have your UIN/VID? Get it here",
|
||||
"header": "Enter the MOSIP-provided UIN or VID of the {{vcLabel}} you wish to retrieve",
|
||||
"title": "Retrieve your ID",
|
||||
"guideLabel": "Select ID type and enter the MOSIP provided UIN or VID of the ID you wish to retrieve",
|
||||
"generateVc": "Generate {{vcLabel}}",
|
||||
"downloadID": "Download ID",
|
||||
"enterId": "Enter {{idType}}",
|
||||
"noUIN/VID": "Don't have UIN/VID?",
|
||||
"getItHere": "Get it now",
|
||||
"requestingOTP": "Requesting OTP..."
|
||||
},
|
||||
"OtpVerificationModal": {
|
||||
"enterOtp": "Enter the 6-digit verification code we sent you",
|
||||
"header": "OTP Verification"
|
||||
"title": "OTP Verification",
|
||||
"otpSentMessage": "We've sent the 6 digit code to your registered mobile number!",
|
||||
"resendTheCode": "You can resend the code in ",
|
||||
"resendCode": "Resend Code"
|
||||
},
|
||||
"MyVcsTab": {
|
||||
"addVcButton": "Add {{vcLabel}}",
|
||||
"generateVc": "Generate your {{vcLabel}}",
|
||||
"generateVcDescription": "Tap on \"Add {{vcLabel}}\" below to download your {{vcLabel}}"
|
||||
"downloadID": "Download ID",
|
||||
"bringYourDigitalID": "Bring Your Digital ID",
|
||||
"generateVcDescription": "To download your {{vcLabel}} tap Download {{vcLabel}} below",
|
||||
"downloadingYourId": "Downloading your ID, this can take upto 5 minutes"
|
||||
},
|
||||
"OnboardingOverlay": {
|
||||
"stepOneTitle": "Welcome!",
|
||||
"stepOneText": "Keep your digital credential with you at all times. To get started, add {{vcLabel}} to your profile.",
|
||||
"stepTwoTitle": "{{vcLabel}} management",
|
||||
"stepTwoText": "Once generated, {{vcLabel}} are safely stored on your mobile and can be renamed or shared at any time.",
|
||||
"stepThreeTitle": "Easy sharing",
|
||||
"stepThreeText": "Share and receive {{vcLabel}} switfly using your phone camera to scan QR codes.",
|
||||
"stepThreeButton": "Get started and add {{vcLabel}}"
|
||||
"stepOneTitle": "Secure Sharing!",
|
||||
"stepOneText": "Share and receive {{vcLabel}} switfly using your phone camera to scan QR codes",
|
||||
"stepTwoTitle": "Trusted Digital Wallet",
|
||||
"stepTwoText": "Keep your digital credential with you at all times",
|
||||
"stepThreeTitle": "Quick Access",
|
||||
"stepThreeText": "Once generated, {{vcLabel}} are safely stored on your mobile.",
|
||||
"stepThreeButton": "Get Started",
|
||||
"skip": "Skip",
|
||||
"next": "Next"
|
||||
},
|
||||
"ReceivedVcsTab": {
|
||||
"noReceivedVcsTitle": "No {{vcLabel}} available yet",
|
||||
"noReceivedVcsText": "Tap on Request below to receive {{vcLabel}}"
|
||||
},
|
||||
"ViewVcModal": {
|
||||
"title": "ID Details",
|
||||
"cancel": "Cancel",
|
||||
"lock": "Lock",
|
||||
"unlock": "Unlock",
|
||||
@@ -177,7 +221,6 @@
|
||||
"requestingOtp": "Requesting OTP...",
|
||||
"editTag": "Rename",
|
||||
"redirecting": "Redirecting...",
|
||||
"inProgress": "Loading...",
|
||||
"success": {
|
||||
"unlocked": "{{vcLabel}} successfully unlocked",
|
||||
"locked": "{{vcLabel}} successfully locked",
|
||||
@@ -188,11 +231,13 @@
|
||||
"home": "Home",
|
||||
"scan": "Scan",
|
||||
"request": "Request",
|
||||
"history": "History",
|
||||
"settings": "Settings"
|
||||
},
|
||||
"PasscodeScreen": {
|
||||
"header": "Set a passcode to secure your application",
|
||||
"confirmPasscode": "Confirm your passcode",
|
||||
"header": "Set Passcode",
|
||||
"description": "Lorem Ipsum is simply dummy text of the printing and typesetting industry",
|
||||
"confirmPasscode": "Confirm passcode",
|
||||
"enterPasscode": "Enter your passcode"
|
||||
},
|
||||
"AppMetaData": {
|
||||
@@ -201,14 +246,16 @@
|
||||
"useBle": "Powered by BLE",
|
||||
"useGoogleNearby": "Powered by GoogleNearby"
|
||||
},
|
||||
"ProfileScreen": {
|
||||
"name": "Name",
|
||||
"SettingScreen": {
|
||||
"header": "Settings",
|
||||
"profile": "Profile",
|
||||
"vcLabel": "VC Label",
|
||||
"language": "Language",
|
||||
"bioUnlock": "Unlock with biometrics",
|
||||
"authFactorUnlock": "Unlock auth factor",
|
||||
"AppMetaData": "About Inji",
|
||||
"logout": "Logout",
|
||||
"credits": "Credits and legal notices",
|
||||
"featuresWalkAround":"Feature Walkaround",
|
||||
"logout": "Log-out",
|
||||
"revokeLabel": "Revoke VID",
|
||||
"revokeHeader": "REVOKE VID",
|
||||
"revokingVids": "You are about to revoke ({{count}}) VIDs.",
|
||||
@@ -299,7 +346,8 @@
|
||||
"gotoSettings": "Go to settings"
|
||||
},
|
||||
"ScanScreen": {
|
||||
"header": "Scan QR Code",
|
||||
"scanningGuide": "Hold the phone study and scan the QR code",
|
||||
"requester": "Requester",
|
||||
"noShareableVcs": "No shareable {{vcLabel}} are available.",
|
||||
"sharingVc": "Sharing {{vcLabel}}",
|
||||
"bluetoothStateAndroid": "Please turn on bluetooth from quick settings to support local sharing",
|
||||
@@ -317,7 +365,9 @@
|
||||
}
|
||||
},
|
||||
"status": {
|
||||
"connecting": "Connecting...",
|
||||
"inProgress": "In Progress",
|
||||
"establishingConnection": "Establishing Connection",
|
||||
"sharingInProgress": "Sharing in Progress",
|
||||
"connectingTimeout": "It's taking a while to establish the connection. Is the other device open for connections?",
|
||||
"exchangingDeviceInfo": "Exchanging device info...",
|
||||
"exchangingDeviceInfoTimeout": "It's taking a while to exchange device info. You may have to reconnect.",
|
||||
@@ -331,8 +381,9 @@
|
||||
"timeoutHint": "It's taking longer than expected to share. There could be a problem with the connection."
|
||||
},
|
||||
"accepted": {
|
||||
"title": "Success!",
|
||||
"message": "Your {{vcLabel}} has been successfully shared with {{receiver}}"
|
||||
"title": "ID shared successfully",
|
||||
"message": "Your ID has been successfully shared with ",
|
||||
"gotohome": "Go to home"
|
||||
},
|
||||
"rejected": {
|
||||
"title": "Notice",
|
||||
@@ -351,11 +402,22 @@
|
||||
"verifyAndShare": "Verify Identity & Share"
|
||||
},
|
||||
"SendVcScreen": {
|
||||
"reasonForSharing": "Reason for sharing (optional)",
|
||||
"acceptRequest": "Share",
|
||||
"acceptRequestAndVerify": "Share with Selfie",
|
||||
"reject": "Reject",
|
||||
"consentToPhotoVerification": "I give consent to have my photo taken for authentication"
|
||||
"reasonForSharing": "Reason for sharing",
|
||||
"approveRequest": "Approve request and choose {{vcLabel}}",
|
||||
"approveRequestAndVerify": "Approve request and verify",
|
||||
"consentToPhotoVerification": "I give consent to have my photo taken for authentication",
|
||||
"pleaseSelectAnId": "Please select an ID",
|
||||
"reject": "Reject"
|
||||
},
|
||||
"Credits": {
|
||||
"header": "Credits and legal notices",
|
||||
"back": "Back",
|
||||
"reasonForSharing": "Reason for sharing",
|
||||
"approveRequest": "Approve request and choose {{vcLabel}}",
|
||||
"approveRequestAndVerify": "Approve request and verify",
|
||||
"consentToPhotoVerification": "I give consent to have my photo taken for authentication",
|
||||
"pleaseSelectAnId": "Please select an ID",
|
||||
"reject": "Reject"
|
||||
},
|
||||
"VerifyIdentityOverlay": {
|
||||
"status": {
|
||||
@@ -374,6 +436,11 @@
|
||||
"getStarted": "Get started",
|
||||
"unlockApp": "Unlock application"
|
||||
},
|
||||
"SetupLanguage": {
|
||||
"header": "Choose Language",
|
||||
"description": "Lorem Ipsum is simply dummy text of the printing and typesetting industry",
|
||||
"save": "Save Preference"
|
||||
},
|
||||
"common": {
|
||||
"cancel": "Cancel",
|
||||
"save": "Save",
|
||||
@@ -390,4 +457,4 @@
|
||||
"genericError": "Something went wrong. Please try again after some time!"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -89,6 +89,14 @@
|
||||
"receivedVcsTab": "Nakuhang\n{{vcLabel}}",
|
||||
"historyTab": "Pangyayari"
|
||||
},
|
||||
"SettingScreen": {
|
||||
"header": "Mga setting",
|
||||
"profile": "Profile",
|
||||
"vcLabel": "Label ng Vc",
|
||||
"language": "Wika",
|
||||
"bioUnlock": "Bio Unlock",
|
||||
"logout": "Log out"
|
||||
},
|
||||
"AddVcModal": {
|
||||
"requestingCredential": "Humihiling ng kredensyal...",
|
||||
"errors": {
|
||||
@@ -147,9 +155,9 @@
|
||||
"header": "Pag-verify ng OTP"
|
||||
},
|
||||
"MyVcsTab": {
|
||||
"addVcButton": "Magdagdag ng {{vcLabel}}",
|
||||
"generateVc": "Gumawa ng iyong {{vcLabel}}",
|
||||
"generateVcDescription": "Pindutin ang \"Magdagdag ng {{vcLabel}}\" sa ibaba upang makuha ang iyong {{vcLabel}}"
|
||||
"downloadID": "I-download ang ID",
|
||||
"bringYourDigitalID": "Dalhin ang iyong digital identity",
|
||||
"generateVcDescription": "I-tap ang \"Download {{vcLabel}}\" sa ibaba para i-download ang iyong {{vcLabel}}"
|
||||
},
|
||||
"OnboardingOverlay": {
|
||||
"stepOneTitle": "Mabuhay!",
|
||||
@@ -186,7 +194,8 @@
|
||||
"home": "Bahay",
|
||||
"scan": "Scan",
|
||||
"request": "Request",
|
||||
"settings": "mga setting"
|
||||
"settings": "mga setting",
|
||||
"history": "Kasaysayan"
|
||||
},
|
||||
"PasscodeScreen": {
|
||||
"header": "Magtakda ng passcode upang masigurado ang iyong aplikasyon",
|
||||
@@ -284,7 +293,8 @@
|
||||
"gotoSettings": "Pumunta sa setting"
|
||||
},
|
||||
"ScanScreen": {
|
||||
"header": "I-scan ang QR Code",
|
||||
"scanningGuide": "Hawakan ang telepono at i-scan ang QR code",
|
||||
"requester": "Humihiling",
|
||||
"noShareableVcs": "Walang magagamit na maibabahaging {{vcLabel}}.",
|
||||
"bluetoothStateAndroid": "Mangyaring i-on ang bluetooth mula sa mga mabilisang setting upang suportahan ang lokal na pagbabahagi",
|
||||
"bluetoothStateIos": "Mangyaring i-on ang bluetooth mula sa control center upang suportahan ang lokal na pagbabahagi",
|
||||
@@ -355,6 +365,11 @@
|
||||
"getStarted": "Magsimula",
|
||||
"unlockApp": "Buksan ang aplikasyon"
|
||||
},
|
||||
"SetupLanguage": {
|
||||
"header": "Piliin ang Wika",
|
||||
"description": "Lorem Ipsum is simply dummy text of the printing and typesetting industry",
|
||||
"save": "I-save ang Kagustuhan"
|
||||
},
|
||||
"common": {
|
||||
"cancel": "Kanselahin",
|
||||
"save": "I-save",
|
||||
|
||||
@@ -149,9 +149,9 @@
|
||||
"header": "ओटीपी सत्यापन"
|
||||
},
|
||||
"MyVcsTab": {
|
||||
"addVcButton": "{{vcLabel}} जोड़ें",
|
||||
"generateVc": "अपना {{vcLabel}}जेनरेट करें",
|
||||
"generateVcDescription": "अपना {{vcLabel}} डाउनलोड करने के लिए नीचे \"जोड़ें {{vcLabel}}\" पर टैप करें"
|
||||
"downloadID": "डाउनलोड आईडी",
|
||||
"bringYourDigitalID": "अपनी डिजिटल पहचान लाओ",
|
||||
"generateVcDescription": "अपना {{vcLabel}} डाउनलोड करने के लिए नीचे डाउनलोड {{vcLabel}} पर टैप करें"
|
||||
},
|
||||
"OnboardingOverlay": {
|
||||
"stepOneTitle": "स्वागत है!",
|
||||
@@ -185,10 +185,11 @@
|
||||
}
|
||||
},
|
||||
"MainLayout": {
|
||||
"home": "घर",
|
||||
"home": "होम",
|
||||
"scan": "स्कैन",
|
||||
"request": "अनुरोध",
|
||||
"settings": "सेटिंग्स"
|
||||
"settings": "सेटिंग्स",
|
||||
"history": "इतिहास"
|
||||
},
|
||||
"PasscodeScreen": {
|
||||
"header": "अपना आवेदन सुरक्षित करने के लिए पासकोड सेट करें",
|
||||
@@ -201,8 +202,9 @@
|
||||
"useBle": "Powered by BLE",
|
||||
"useGoogleNearby": "Powered by GoogleNearby"
|
||||
},
|
||||
"ProfileScreen": {
|
||||
"name": "नाम",
|
||||
"SettingScreen": {
|
||||
"header": "समायोजन",
|
||||
"profile": "प्रोफ़ाइल",
|
||||
"vcLabel": "वीसी लेबल",
|
||||
"language": "भाषा",
|
||||
"bioUnlock": "बायोमेट्रिक्स से अनलॉक करें",
|
||||
@@ -287,7 +289,8 @@
|
||||
"gotoSettings": "सेटिंग्स में जाओ"
|
||||
},
|
||||
"ScanScreen": {
|
||||
"header": "क्यूआर कोड स्कैन करे",
|
||||
"scanningGuide": "फोन को पकड़ें और क्यूआर कोड को स्कैन करें",
|
||||
"requester": "अनुरोधकर्ता",
|
||||
"noShareableVcs": "कोई साझा करने योग्य {{vcLabel}} उपलब्ध नहीं है।",
|
||||
"bluetoothStateAndroid": "स्थानीय साझाकरण का समर्थन करने के लिए कृपया त्वरित सेटिंग से ब्लूटूथ चालू करें",
|
||||
"bluetoothStateIos": " स्थानीय साझाकरण का समर्थन करने के लिए कृपया नियंत्रण केंद्र से ब्लूटूथ चालू करें",
|
||||
@@ -374,6 +377,11 @@
|
||||
"getStarted": "आरंभ करें",
|
||||
"unlockApp": "एप्लिकेशन अनलॉक करें"
|
||||
},
|
||||
"SetupLanguage": {
|
||||
"header": "भाषा चुनें",
|
||||
"description": "Lorem Ipsum is simply dummy text of the printing and typesetting industry",
|
||||
"save": "वरीयता सहेजें"
|
||||
},
|
||||
"common": {
|
||||
"cancel": "रद्द करें",
|
||||
"save": "सहेजें",
|
||||
|
||||
@@ -124,6 +124,7 @@ export type ActivityLogType =
|
||||
| 'VC_RECEIVED_NOT_SAVED'
|
||||
| 'VC_DELETED'
|
||||
| 'VC_DOWNLOADED'
|
||||
| 'VC_UPDATED'
|
||||
| 'VC_REVOKED'
|
||||
| 'VC_SHARED_WITH_VERIFICATION_CONSENT'
|
||||
| 'VC_RECEIVED_WITH_PRESENCE_VERIFIED'
|
||||
|
||||
@@ -17,7 +17,6 @@ import * as BLERequest from './openIdBle/request';
|
||||
import * as BLEScan from './openIdBle/scan';
|
||||
import { createScanMachine, scanMachine } from './scan';
|
||||
import { createRevokeMachine, revokeVidsMachine } from './revoke';
|
||||
|
||||
import { pure, respond } from 'xstate/lib/actions';
|
||||
import { AppServices } from '../shared/GlobalContext';
|
||||
import { request } from '../shared/request';
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import { init } from 'mosip-inji-face-sdk';
|
||||
import { ContextFrom, EventFrom, send, StateFrom } from 'xstate';
|
||||
import { assign, ContextFrom, EventFrom, send, StateFrom } from 'xstate';
|
||||
import { createModel } from 'xstate/lib/model';
|
||||
import { downloadModel } from '../shared/commonprops/commonProps';
|
||||
import getAllConfigurations, {
|
||||
downloadModel,
|
||||
} from '../shared/commonprops/commonProps';
|
||||
import { AppServices } from '../shared/GlobalContext';
|
||||
import { StoreEvents, StoreResponseEvent } from './store';
|
||||
|
||||
@@ -11,6 +13,7 @@ const model = createModel(
|
||||
passcode: '',
|
||||
biometrics: '',
|
||||
canUseBiometrics: false,
|
||||
selectLanguage: false,
|
||||
},
|
||||
{
|
||||
events: {
|
||||
@@ -19,6 +22,8 @@ const model = createModel(
|
||||
LOGOUT: () => ({}),
|
||||
LOGIN: () => ({}),
|
||||
STORE_RESPONSE: (response?: unknown) => ({ response }),
|
||||
SELECT: () => ({}),
|
||||
NEXT: () => ({}),
|
||||
},
|
||||
}
|
||||
);
|
||||
@@ -60,21 +65,36 @@ export const authMachine = model.createMachine(
|
||||
},
|
||||
checkingAuth: {
|
||||
always: [
|
||||
{ cond: 'hasLanguageset', target: 'languagesetup' },
|
||||
{ cond: 'hasPasscodeSet', target: 'unauthorized' },
|
||||
{ cond: 'hasBiometricSet', target: 'unauthorized' },
|
||||
{ target: 'settingUp' },
|
||||
],
|
||||
},
|
||||
languagesetup: {
|
||||
on: {
|
||||
SELECT: {
|
||||
target: 'introSlider',
|
||||
},
|
||||
},
|
||||
},
|
||||
introSlider: {
|
||||
on: {
|
||||
NEXT: {
|
||||
target: 'settingUp',
|
||||
},
|
||||
},
|
||||
},
|
||||
settingUp: {
|
||||
on: {
|
||||
SETUP_PASSCODE: {
|
||||
target: 'authorized',
|
||||
actions: ['setPasscode', 'storeContext'],
|
||||
actions: ['setPasscode', 'storeContext', 'setLanguage'],
|
||||
},
|
||||
SETUP_BIOMETRICS: {
|
||||
// Note! dont authorized yet we need to setup passcode too as discuss
|
||||
// target: 'authorized',
|
||||
actions: ['setBiometrics', 'storeContext'],
|
||||
actions: ['setBiometrics', 'storeContext', 'setLanguage'],
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -127,11 +147,25 @@ export const authMachine = model.createMachine(
|
||||
setBiometrics: model.assign({
|
||||
biometrics: (_, event: SetupBiometricsEvent) => event.biometrics,
|
||||
}),
|
||||
|
||||
setLanguage: assign({
|
||||
selectLanguage: (context) => !context.selectLanguage,
|
||||
}),
|
||||
},
|
||||
|
||||
services: {
|
||||
downloadFaceSdkModel: () => () => {
|
||||
downloadModel();
|
||||
downloadFaceSdkModel: () => async () => {
|
||||
var injiProp = null;
|
||||
try {
|
||||
var injiProp = await getAllConfigurations();
|
||||
const resp: string =
|
||||
injiProp != null ? injiProp.faceSdkModelUrl : null;
|
||||
if (resp != null) {
|
||||
init(resp, false);
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
@@ -144,6 +178,9 @@ export const authMachine = model.createMachine(
|
||||
hasBiometricSet: (context) => {
|
||||
return context.biometrics !== '' && context.passcode !== '';
|
||||
},
|
||||
hasLanguageset: (context) => {
|
||||
return !context.selectLanguage;
|
||||
},
|
||||
},
|
||||
}
|
||||
);
|
||||
@@ -180,3 +217,10 @@ export function selectUnauthorized(state: State) {
|
||||
export function selectSettingUp(state: State) {
|
||||
return state.matches('settingUp');
|
||||
}
|
||||
|
||||
export function selectLanguagesetup(state: State) {
|
||||
return state.matches('languagesetup');
|
||||
}
|
||||
export function selectIntroSlider(state: State) {
|
||||
return state.matches('introSlider');
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ export interface Typegen0 {
|
||||
requestStoredContext: 'xstate.init';
|
||||
setBiometrics: 'SETUP_BIOMETRICS';
|
||||
setContext: 'STORE_RESPONSE';
|
||||
setLanguage: 'SETUP_BIOMETRICS' | 'SETUP_PASSCODE';
|
||||
setPasscode: 'SETUP_PASSCODE';
|
||||
storeContext:
|
||||
| 'SETUP_BIOMETRICS'
|
||||
@@ -35,6 +36,7 @@ export interface Typegen0 {
|
||||
'eventsCausingGuards': {
|
||||
hasBiometricSet: '';
|
||||
hasData: 'STORE_RESPONSE';
|
||||
hasLanguageset: '';
|
||||
hasPasscodeSet: '';
|
||||
};
|
||||
'eventsCausingServices': {
|
||||
@@ -44,6 +46,8 @@ export interface Typegen0 {
|
||||
| 'authorized'
|
||||
| 'checkingAuth'
|
||||
| 'init'
|
||||
| 'introSlider'
|
||||
| 'languagesetup'
|
||||
| 'savingDefaults'
|
||||
| 'settingUp'
|
||||
| 'unauthorized';
|
||||
|
||||
@@ -6,6 +6,7 @@ import { EventFrom, Receiver, sendParent, send, sendUpdate } from 'xstate';
|
||||
import { createModel } from 'xstate/lib/model';
|
||||
import { generateSecureRandom } from 'react-native-securerandom';
|
||||
import { log } from 'xstate/lib/actions';
|
||||
import { VC_ITEM_STORE_KEY } from '../shared/constants';
|
||||
|
||||
const ENCRYPTION_ID = 'c7c22a6c-9759-4605-ac88-46f4041d863d';
|
||||
|
||||
@@ -21,6 +22,7 @@ const model = createModel(
|
||||
SET: (key: string, value: unknown) => ({ key, value }),
|
||||
APPEND: (key: string, value: unknown) => ({ key, value }),
|
||||
PREPEND: (key: string, value: unknown) => ({ key, value }),
|
||||
UPDATE: (key: string, value: string) => ({ key, value }),
|
||||
REMOVE: (key: string, value: string) => ({ key, value }),
|
||||
REMOVE_ITEMS: (key: string, values: string[]) => ({ key, values }),
|
||||
CLEAR: () => ({}),
|
||||
@@ -116,6 +118,9 @@ export const storeMachine =
|
||||
PREPEND: {
|
||||
actions: 'forwardStoreRequest',
|
||||
},
|
||||
UPDATE: {
|
||||
actions: 'forwardStoreRequest',
|
||||
},
|
||||
REMOVE: {
|
||||
actions: 'forwardStoreRequest',
|
||||
},
|
||||
@@ -205,6 +210,16 @@ export const storeMachine =
|
||||
response = event.value;
|
||||
break;
|
||||
}
|
||||
case 'UPDATE': {
|
||||
await updateItem(
|
||||
event.key,
|
||||
event.value,
|
||||
context.encryptionKey
|
||||
);
|
||||
|
||||
response = event.value;
|
||||
break;
|
||||
}
|
||||
case 'REMOVE': {
|
||||
await removeItem(
|
||||
event.key,
|
||||
@@ -341,7 +356,30 @@ export async function prependItem(
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
export async function updateItem(
|
||||
key: string,
|
||||
value: string,
|
||||
encryptionKey: string
|
||||
) {
|
||||
try {
|
||||
const list = await getItem(key, [], encryptionKey);
|
||||
const newList = [
|
||||
value,
|
||||
...list.map((item) => {
|
||||
const vc = item.split(':');
|
||||
if (vc[3] !== value.split(':')[3]) {
|
||||
vc[4] = 'false';
|
||||
return vc.join(':');
|
||||
}
|
||||
}),
|
||||
].filter((value) => value != undefined && value !== null);
|
||||
|
||||
await setItem(key, newList, encryptionKey);
|
||||
} catch (e) {
|
||||
console.error('error prependItem:', e);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
export async function removeItem(
|
||||
key: string,
|
||||
value: string,
|
||||
|
||||
@@ -36,7 +36,8 @@ export interface Typegen0 {
|
||||
| 'PREPEND'
|
||||
| 'REMOVE'
|
||||
| 'REMOVE_ITEMS'
|
||||
| 'SET';
|
||||
| 'SET'
|
||||
| 'UPDATE';
|
||||
notifyParent:
|
||||
| 'KEY_RECEIVED'
|
||||
| 'done.invoke.store.resettingStorage:invocation[0]';
|
||||
|
||||
@@ -26,8 +26,10 @@ const model = createModel(
|
||||
STORE_RESPONSE: (response: unknown) => ({ response }),
|
||||
STORE_ERROR: (error: Error) => ({ error }),
|
||||
VC_ADDED: (vcKey: string) => ({ vcKey }),
|
||||
VC_UPDATED: (vcKey: string) => ({ vcKey }),
|
||||
VC_RECEIVED: (vcKey: string) => ({ vcKey }),
|
||||
VC_DOWNLOADED: (vc: VC) => ({ vc }),
|
||||
VC_UPDATE: (vc: VC) => ({ vc }),
|
||||
REFRESH_MY_VCS: () => ({}),
|
||||
REFRESH_MY_VCS_TWO: (vc: VC) => ({ vc }),
|
||||
REFRESH_RECEIVED_VCS: () => ({}),
|
||||
@@ -133,9 +135,15 @@ export const vcMachine =
|
||||
VC_ADDED: {
|
||||
actions: 'prependToMyVcs',
|
||||
},
|
||||
VC_UPDATED: {
|
||||
actions: ['updateMyVcs', 'setUpdateVc'],
|
||||
},
|
||||
VC_DOWNLOADED: {
|
||||
actions: 'setDownloadedVc',
|
||||
},
|
||||
VC_UPDATE: {
|
||||
actions: 'setVcUpdate',
|
||||
},
|
||||
VC_RECEIVED: [
|
||||
{
|
||||
actions: 'moveExistingVcToTop',
|
||||
@@ -181,10 +189,35 @@ export const vcMachine =
|
||||
context.vcs[VC_ITEM_STORE_KEY(event.vc)] = event.vc;
|
||||
},
|
||||
|
||||
setVcUpdate: (context, event) => {
|
||||
context.vcs[VC_ITEM_STORE_KEY(event.vc)] = event.vc;
|
||||
},
|
||||
|
||||
setUpdateVc: send(
|
||||
(_context, event) => {
|
||||
return StoreEvents.UPDATE(MY_VCS_STORE_KEY, event.vcKey);
|
||||
},
|
||||
{ to: (context) => context.serviceRefs.store }
|
||||
),
|
||||
|
||||
prependToMyVcs: model.assign({
|
||||
myVcs: (context, event) => [event.vcKey, ...context.myVcs],
|
||||
}),
|
||||
|
||||
updateMyVcs: model.assign({
|
||||
myVcs: (context, event) =>
|
||||
[
|
||||
event.vcKey,
|
||||
...context.myVcs.map((value) => {
|
||||
const vc = value.split(':');
|
||||
if (vc[3] !== event.vcKey.split(':')[3]) {
|
||||
vc[4] = 'false';
|
||||
return vc.join(':');
|
||||
}
|
||||
}),
|
||||
].filter((value) => value != undefined),
|
||||
}),
|
||||
|
||||
prependToReceivedVcs: model.assign({
|
||||
receivedVcs: (context, event) => [
|
||||
event.vcKey,
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
// This file was automatically generated. Edits will be overwritten
|
||||
|
||||
export interface Typegen0 {
|
||||
'@@xstate/typegen': true;
|
||||
'internalEvents': {
|
||||
'xstate.init': { type: 'xstate.init' };
|
||||
};
|
||||
'invokeSrcNameMap': {};
|
||||
'missingImplementations': {
|
||||
actions: never;
|
||||
delays: never;
|
||||
guards: never;
|
||||
services: never;
|
||||
};
|
||||
'eventsCausingActions': {
|
||||
getReceivedVcsResponse: 'GET_RECEIVED_VCS';
|
||||
getVcItemResponse: 'GET_VC_ITEM';
|
||||
loadMyVcs: 'REFRESH_MY_VCS' | 'xstate.init';
|
||||
loadReceivedVcs: 'REFRESH_RECEIVED_VCS' | 'STORE_RESPONSE';
|
||||
moveExistingVcToTop: 'VC_RECEIVED';
|
||||
prependToMyVcs: 'VC_ADDED';
|
||||
prependToReceivedVcs: 'VC_RECEIVED';
|
||||
setDownloadedVc: 'VC_DOWNLOADED';
|
||||
setMyVcs: 'STORE_RESPONSE';
|
||||
setReceivedVcs: 'STORE_RESPONSE';
|
||||
setUpdateVc: 'VC_UPDATED';
|
||||
setVcUpdate: 'VC_UPDATE';
|
||||
updateMyVcs: 'VC_UPDATED';
|
||||
};
|
||||
'eventsCausingDelays': {};
|
||||
'eventsCausingGuards': {
|
||||
hasExistingReceivedVc: 'VC_RECEIVED';
|
||||
};
|
||||
'eventsCausingServices': {};
|
||||
'matchesStates':
|
||||
| 'init'
|
||||
| 'init.myVcs'
|
||||
| 'init.receivedVcs'
|
||||
| 'ready'
|
||||
| 'ready.myVcs'
|
||||
| 'ready.myVcs.idle'
|
||||
| 'ready.myVcs.refreshing'
|
||||
| 'ready.receivedVcs'
|
||||
| 'ready.receivedVcs.idle'
|
||||
| 'ready.receivedVcs.refreshing'
|
||||
| {
|
||||
init?: 'myVcs' | 'receivedVcs';
|
||||
ready?:
|
||||
| 'myVcs'
|
||||
| 'receivedVcs'
|
||||
| {
|
||||
myVcs?: 'idle' | 'refreshing';
|
||||
receivedVcs?: 'idle' | 'refreshing';
|
||||
};
|
||||
};
|
||||
'tags': never;
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ import {
|
||||
import getAllConfigurations, {
|
||||
DownloadProps,
|
||||
} from '../shared/commonprops/commonProps';
|
||||
import i18n from '../i18n';
|
||||
import { VcEvents } from './vc';
|
||||
|
||||
const model = createModel(
|
||||
{
|
||||
@@ -38,6 +38,7 @@ const model = createModel(
|
||||
verifiableCredential: null as VerifiableCredential,
|
||||
requestId: '',
|
||||
isVerified: false,
|
||||
isPinned: false,
|
||||
lastVerifiedOn: null,
|
||||
locked: false,
|
||||
otp: '',
|
||||
@@ -75,6 +76,9 @@ const model = createModel(
|
||||
ADD_WALLET_BINDING_ID: () => ({}),
|
||||
CANCEL: () => ({}),
|
||||
CONFIRM: () => ({}),
|
||||
PIN_CARD: () => ({}),
|
||||
KEBAB_POPUP: () => ({}),
|
||||
SHOW_ACTIVITY: () => ({}),
|
||||
},
|
||||
}
|
||||
);
|
||||
@@ -213,6 +217,150 @@ export const vcItemMachine =
|
||||
ADD_WALLET_BINDING_ID: {
|
||||
target: 'showBindingWarning',
|
||||
},
|
||||
PIN_CARD: {
|
||||
target: 'pinCard',
|
||||
actions: 'setPinCard',
|
||||
},
|
||||
KEBAB_POPUP: {
|
||||
target: 'kebabPopUp',
|
||||
},
|
||||
|
||||
DISMISS: {
|
||||
target: 'checkingVc',
|
||||
},
|
||||
},
|
||||
},
|
||||
pinCard: {
|
||||
entry: 'storeContext',
|
||||
on: {
|
||||
STORE_RESPONSE: {
|
||||
actions: 'sendVcUpdated',
|
||||
target: 'idle',
|
||||
},
|
||||
},
|
||||
},
|
||||
kebabPopUp: {
|
||||
on: {
|
||||
DISMISS: {
|
||||
target: 'idle',
|
||||
},
|
||||
ADD_WALLET_BINDING_ID: {
|
||||
target: '#vc-item.kebabPopUp.showBindingWarning',
|
||||
},
|
||||
PIN_CARD: {
|
||||
target: '#vc-item.pinCard',
|
||||
actions: 'setPinCard',
|
||||
},
|
||||
SHOW_ACTIVITY: {
|
||||
target: '#vc-item.kebabPopUp.showActivities',
|
||||
},
|
||||
},
|
||||
initial: 'idle',
|
||||
states: {
|
||||
idle: {},
|
||||
showBindingWarning: {
|
||||
on: {
|
||||
CONFIRM: {
|
||||
target: '#vc-item.kebabPopUp.requestingBindingOtp',
|
||||
},
|
||||
CANCEL: {
|
||||
target: '#vc-item.kebabPopUp',
|
||||
},
|
||||
},
|
||||
},
|
||||
requestingBindingOtp: {
|
||||
invoke: {
|
||||
src: 'requestBindingOtp',
|
||||
onDone: [
|
||||
{
|
||||
target: '#vc-item.kebabPopUp.acceptingBindingOtp',
|
||||
},
|
||||
],
|
||||
onError: [
|
||||
{
|
||||
actions: 'setWalletBindingError',
|
||||
target: '#vc-item.kebabPopUp.showingWalletBindingError',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
showingWalletBindingError: {
|
||||
on: {
|
||||
CANCEL: {
|
||||
target: '#vc-item.kebabPopUp',
|
||||
actions: 'setWalletBindingErrorEmpty',
|
||||
},
|
||||
},
|
||||
},
|
||||
acceptingBindingOtp: {
|
||||
entry: ['clearOtp'],
|
||||
on: {
|
||||
INPUT_OTP: {
|
||||
target: '#vc-item.kebabPopUp.addKeyPair',
|
||||
actions: ['setOtp'],
|
||||
},
|
||||
DISMISS: {
|
||||
target: '#vc-item.kebabPopUp',
|
||||
actions: ['clearOtp', 'clearTransactionId'],
|
||||
},
|
||||
},
|
||||
},
|
||||
addKeyPair: {
|
||||
invoke: {
|
||||
src: 'generateKeyPair',
|
||||
onDone: {
|
||||
target: '#vc-item.kebabPopUp.addingWalletBindingId',
|
||||
actions: ['setPublicKey', 'setPrivateKey'],
|
||||
},
|
||||
onError: [
|
||||
{
|
||||
actions: 'setWalletBindingError',
|
||||
target: '#vc-item.kebabPopUp.showingWalletBindingError',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
addingWalletBindingId: {
|
||||
invoke: {
|
||||
src: 'addWalletBindnigId',
|
||||
onDone: [
|
||||
{
|
||||
target: '#vc-item.kebabPopUp.updatingPrivateKey',
|
||||
actions: ['setWalletBindingId'],
|
||||
},
|
||||
],
|
||||
onError: [
|
||||
{
|
||||
actions: 'setWalletBindingError',
|
||||
target: '#vc-item.kebabPopUp.showingWalletBindingError',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
updatingPrivateKey: {
|
||||
invoke: {
|
||||
src: 'updatePrivateKey',
|
||||
onDone: {
|
||||
actions: [
|
||||
'storeContext',
|
||||
'updatePrivateKey',
|
||||
'updateVc',
|
||||
'setWalletBindingErrorEmpty',
|
||||
'logWalletBindingSuccess',
|
||||
],
|
||||
target: '#vc-item.kebabPopUp',
|
||||
},
|
||||
onError: {
|
||||
actions: 'setWalletBindingError',
|
||||
target: '#vc-item.kebabPopUp.showingWalletBindingError',
|
||||
},
|
||||
},
|
||||
},
|
||||
showActivities: {
|
||||
on: {
|
||||
DISMISS: '#vc-item.kebabPopUp',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
editingTag: {
|
||||
@@ -479,7 +627,6 @@ export const vcItemMachine =
|
||||
invoke: {
|
||||
src: 'updatePrivateKey',
|
||||
onDone: {
|
||||
target: 'idle',
|
||||
actions: [
|
||||
'storeContext',
|
||||
'updatePrivateKey',
|
||||
@@ -487,6 +634,7 @@ export const vcItemMachine =
|
||||
'setWalletBindingErrorEmpty',
|
||||
'logWalletBindingSuccess',
|
||||
],
|
||||
target: 'idle',
|
||||
},
|
||||
onError: {
|
||||
actions: ['setWalletBindingError', 'logWalletBindingFailure'],
|
||||
@@ -526,6 +674,21 @@ export const vcItemMachine =
|
||||
event.data as WalletBindingResponse,
|
||||
}),
|
||||
|
||||
setPinCard: assign((context) => {
|
||||
return {
|
||||
...context,
|
||||
isPinned: !context.isPinned,
|
||||
};
|
||||
}),
|
||||
|
||||
sendVcUpdated: send(
|
||||
(_context, event) =>
|
||||
VcEvents.VC_UPDATED(VC_ITEM_STORE_KEY(event.response) as string),
|
||||
{
|
||||
to: (context) => context.serviceRefs.vc,
|
||||
}
|
||||
),
|
||||
|
||||
updateVc: send(
|
||||
(context) => {
|
||||
const { serviceRefs, ...vc } = context;
|
||||
@@ -739,7 +902,7 @@ export const vcItemMachine =
|
||||
authFactorType: 'WLA',
|
||||
format: 'jwt',
|
||||
individualId: context.id,
|
||||
transactionId: context.bindingTransactionId,
|
||||
transactionId: context.transactionId,
|
||||
publicKey: context.publicKey,
|
||||
challengeList: [
|
||||
{
|
||||
@@ -852,6 +1015,7 @@ export const vcItemMachine =
|
||||
tag: '',
|
||||
requestId: context.requestId,
|
||||
isVerified: false,
|
||||
isPinned: context.isPinned,
|
||||
lastVerifiedOn: null,
|
||||
locked: context.locked,
|
||||
walletBindingResponse: null,
|
||||
@@ -1005,6 +1169,9 @@ export function selectIsOtpError(state: State) {
|
||||
export function selectOtpError(state: State) {
|
||||
return state.context.otpError;
|
||||
}
|
||||
export function selectIsPinned(state: State) {
|
||||
return state.context.isPinned;
|
||||
}
|
||||
|
||||
export function selectIsLockingVc(state: State) {
|
||||
return state.matches('lockingVc');
|
||||
@@ -1026,25 +1193,21 @@ export function selectIsAcceptingRevokeInput(state: State) {
|
||||
return state.matches('acceptingRevokeInput');
|
||||
}
|
||||
|
||||
export function selectIsRequestBindingOtp(state: State) {
|
||||
return state.matches('requestingBindingOtp');
|
||||
}
|
||||
|
||||
export function selectWalletBindingId(state: State) {
|
||||
return state.context.walletBindingResponse;
|
||||
}
|
||||
|
||||
export function selectEmptyWalletBindingId(state: State) {
|
||||
var val = state.context.walletBindingResponse
|
||||
? state.context.walletBindingResponse.walletBindingId
|
||||
: undefined;
|
||||
return val === undefined || val == null || val.length <= 0 ? true : false;
|
||||
return val == undefined || val == null || val.length <= 0 ? true : false;
|
||||
}
|
||||
|
||||
export function selectWalletBindingError(state: State) {
|
||||
return state.context.walletBindingError;
|
||||
}
|
||||
|
||||
export function selectRequestBindingOtp(state: State) {
|
||||
return state.matches('requestingBindingOtp');
|
||||
}
|
||||
|
||||
export function selectAcceptingBindingOtp(state: State) {
|
||||
return state.matches('acceptingBindingOtp');
|
||||
}
|
||||
@@ -1053,7 +1216,7 @@ export function selectShowWalletBindingError(state: State) {
|
||||
return state.matches('showingWalletBindingError');
|
||||
}
|
||||
|
||||
export function isWalletBindingInProgress(state: State) {
|
||||
export function selectWalletBindingInProgress(state: State) {
|
||||
return state.matches('requestingBindingOtp') ||
|
||||
state.matches('addingWalletBindingId') ||
|
||||
state.matches('addKeyPair') ||
|
||||
@@ -1062,6 +1225,38 @@ export function isWalletBindingInProgress(state: State) {
|
||||
: false;
|
||||
}
|
||||
|
||||
export function isShowingBindingWarning(state: State) {
|
||||
export function selectBindingWarning(state: State) {
|
||||
return state.matches('showBindingWarning');
|
||||
}
|
||||
export function selectKebabPopUp(state: State) {
|
||||
return state.matches('kebabPopUp');
|
||||
}
|
||||
|
||||
export function selectKebabPopUpRequestBindingOtp(state: State) {
|
||||
return state.matches('kebabPopUp.requestingBindingOtp');
|
||||
}
|
||||
|
||||
export function selectKebabPopUpAcceptingBindingOtp(state: State) {
|
||||
return state.matches('kebabPopUp.acceptingBindingOtp');
|
||||
}
|
||||
|
||||
export function selectKebabPopUpShowWalletBindingError(state: State) {
|
||||
return state.matches('kebabPopUp.showingWalletBindingError');
|
||||
}
|
||||
|
||||
export function selectKebabPopUpWalletBindingInProgress(state: State) {
|
||||
return state.matches('kebabPopUp.requestingBindingOtp') ||
|
||||
state.matches('kebabPopUp.addingWalletBindingId') ||
|
||||
state.matches('kebabPopUp.addKeyPair') ||
|
||||
state.matches('kebabPopUp.updatingPrivateKey')
|
||||
? true
|
||||
: false;
|
||||
}
|
||||
|
||||
export function selectKebabPopUpBindingWarning(state: State) {
|
||||
return state.matches('kebabPopUp.showBindingWarning');
|
||||
}
|
||||
|
||||
export function selectShowActivities(state: State) {
|
||||
return state.matches('kebabPopUp.showActivities');
|
||||
}
|
||||
|
||||
@@ -29,6 +29,26 @@ export interface Typegen0 {
|
||||
data: unknown;
|
||||
__tip: 'See the XState TS docs to learn how to strongly type this.';
|
||||
};
|
||||
'done.invoke.vc-item.kebabPopUp.addKeyPair:invocation[0]': {
|
||||
type: 'done.invoke.vc-item.kebabPopUp.addKeyPair:invocation[0]';
|
||||
data: unknown;
|
||||
__tip: 'See the XState TS docs to learn how to strongly type this.';
|
||||
};
|
||||
'done.invoke.vc-item.kebabPopUp.addingWalletBindingId:invocation[0]': {
|
||||
type: 'done.invoke.vc-item.kebabPopUp.addingWalletBindingId:invocation[0]';
|
||||
data: unknown;
|
||||
__tip: 'See the XState TS docs to learn how to strongly type this.';
|
||||
};
|
||||
'done.invoke.vc-item.kebabPopUp.requestingBindingOtp:invocation[0]': {
|
||||
type: 'done.invoke.vc-item.kebabPopUp.requestingBindingOtp:invocation[0]';
|
||||
data: unknown;
|
||||
__tip: 'See the XState TS docs to learn how to strongly type this.';
|
||||
};
|
||||
'done.invoke.vc-item.kebabPopUp.updatingPrivateKey:invocation[0]': {
|
||||
type: 'done.invoke.vc-item.kebabPopUp.updatingPrivateKey:invocation[0]';
|
||||
data: unknown;
|
||||
__tip: 'See the XState TS docs to learn how to strongly type this.';
|
||||
};
|
||||
'done.invoke.vc-item.requestingBindingOtp:invocation[0]': {
|
||||
type: 'done.invoke.vc-item.requestingBindingOtp:invocation[0]';
|
||||
data: unknown;
|
||||
@@ -75,6 +95,26 @@ export interface Typegen0 {
|
||||
type: 'error.platform.vc-item.addingWalletBindingId:invocation[0]';
|
||||
data: unknown;
|
||||
};
|
||||
'error.platform.vc-item.checkingServerData.verifyingDownloadLimitExpiry:invocation[0]': {
|
||||
type: 'error.platform.vc-item.checkingServerData.verifyingDownloadLimitExpiry:invocation[0]';
|
||||
data: unknown;
|
||||
};
|
||||
'error.platform.vc-item.kebabPopUp.addKeyPair:invocation[0]': {
|
||||
type: 'error.platform.vc-item.kebabPopUp.addKeyPair:invocation[0]';
|
||||
data: unknown;
|
||||
};
|
||||
'error.platform.vc-item.kebabPopUp.addingWalletBindingId:invocation[0]': {
|
||||
type: 'error.platform.vc-item.kebabPopUp.addingWalletBindingId:invocation[0]';
|
||||
data: unknown;
|
||||
};
|
||||
'error.platform.vc-item.kebabPopUp.requestingBindingOtp:invocation[0]': {
|
||||
type: 'error.platform.vc-item.kebabPopUp.requestingBindingOtp:invocation[0]';
|
||||
data: unknown;
|
||||
};
|
||||
'error.platform.vc-item.kebabPopUp.updatingPrivateKey:invocation[0]': {
|
||||
type: 'error.platform.vc-item.kebabPopUp.updatingPrivateKey:invocation[0]';
|
||||
data: unknown;
|
||||
};
|
||||
'error.platform.vc-item.requestingBindingOtp:invocation[0]': {
|
||||
type: 'error.platform.vc-item.requestingBindingOtp:invocation[0]';
|
||||
data: unknown;
|
||||
@@ -98,16 +138,24 @@ export interface Typegen0 {
|
||||
'xstate.init': { type: 'xstate.init' };
|
||||
};
|
||||
'invokeSrcNameMap': {
|
||||
addWalletBindnigId: 'done.invoke.vc-item.addingWalletBindingId:invocation[0]';
|
||||
addWalletBindnigId:
|
||||
| 'done.invoke.vc-item.addingWalletBindingId:invocation[0]'
|
||||
| 'done.invoke.vc-item.kebabPopUp.addingWalletBindingId:invocation[0]';
|
||||
checkDownloadExpiryLimit: 'done.invoke.vc-item.checkingServerData.verifyingDownloadLimitExpiry:invocation[0]';
|
||||
checkStatus: 'done.invoke.checkStatus';
|
||||
downloadCredential: 'done.invoke.downloadCredential';
|
||||
generateKeyPair: 'done.invoke.vc-item.addKeyPair:invocation[0]';
|
||||
requestBindingOtp: 'done.invoke.vc-item.requestingBindingOtp:invocation[0]';
|
||||
generateKeyPair:
|
||||
| 'done.invoke.vc-item.addKeyPair:invocation[0]'
|
||||
| 'done.invoke.vc-item.kebabPopUp.addKeyPair:invocation[0]';
|
||||
requestBindingOtp:
|
||||
| 'done.invoke.vc-item.kebabPopUp.requestingBindingOtp:invocation[0]'
|
||||
| 'done.invoke.vc-item.requestingBindingOtp:invocation[0]';
|
||||
requestLock: 'done.invoke.vc-item.requestingLock:invocation[0]';
|
||||
requestOtp: 'done.invoke.vc-item.requestingOtp:invocation[0]';
|
||||
requestRevoke: 'done.invoke.vc-item.requestingRevoke:invocation[0]';
|
||||
updatePrivateKey: 'done.invoke.vc-item.updatingPrivateKey:invocation[0]';
|
||||
updatePrivateKey:
|
||||
| 'done.invoke.vc-item.kebabPopUp.updatingPrivateKey:invocation[0]'
|
||||
| 'done.invoke.vc-item.updatingPrivateKey:invocation[0]';
|
||||
verifyCredential: 'done.invoke.vc-item.verifyingCredential:invocation[0]';
|
||||
};
|
||||
'missingImplementations': {
|
||||
@@ -123,6 +171,7 @@ export interface Typegen0 {
|
||||
| 'DISMISS'
|
||||
| 'REVOKE_VC'
|
||||
| 'STORE_RESPONSE'
|
||||
| 'done.invoke.vc-item.kebabPopUp.requestingBindingOtp:invocation[0]'
|
||||
| 'done.invoke.vc-item.requestingBindingOtp:invocation[0]'
|
||||
| 'done.invoke.vc-item.requestingOtp:invocation[0]'
|
||||
| 'done.invoke.vc-item.updatingPrivateKey:invocation[0]'
|
||||
@@ -141,22 +190,37 @@ export interface Typegen0 {
|
||||
incrementDownloadCounter: 'POLL';
|
||||
logDownloaded: 'CREDENTIAL_DOWNLOADED';
|
||||
logRevoked: 'STORE_RESPONSE';
|
||||
logWalletBindingFailure:
|
||||
| 'error.platform.vc-item.addKeyPair:invocation[0]'
|
||||
| 'error.platform.vc-item.addingWalletBindingId:invocation[0]'
|
||||
| 'error.platform.vc-item.requestingBindingOtp:invocation[0]'
|
||||
| 'error.platform.vc-item.updatingPrivateKey:invocation[0]';
|
||||
logWalletBindingSuccess:
|
||||
| 'done.invoke.vc-item.kebabPopUp.updatingPrivateKey:invocation[0]'
|
||||
| 'done.invoke.vc-item.updatingPrivateKey:invocation[0]';
|
||||
markVcValid: 'done.invoke.vc-item.verifyingCredential:invocation[0]';
|
||||
requestStoredContext: 'GET_VC_RESPONSE' | 'REFRESH';
|
||||
requestVcContext: 'xstate.init';
|
||||
requestVcContext: 'DISMISS' | 'xstate.init';
|
||||
revokeVID: 'done.invoke.vc-item.requestingRevoke:invocation[0]';
|
||||
sendVcUpdated: 'STORE_RESPONSE';
|
||||
setCredential:
|
||||
| 'CREDENTIAL_DOWNLOADED'
|
||||
| 'GET_VC_RESPONSE'
|
||||
| 'STORE_RESPONSE';
|
||||
setDownloadInterval: 'done.invoke.vc-item.checkingServerData.verifyingDownloadLimitExpiry:invocation[0]';
|
||||
setLock: 'done.invoke.vc-item.requestingLock:invocation[0]';
|
||||
setMaxDownloadCount: 'done.invoke.vc-item.checkingServerData.verifyingDownloadLimitExpiry:invocation[0]';
|
||||
setOtp: 'INPUT_OTP';
|
||||
setOtpError:
|
||||
| 'error.platform.vc-item.requestingLock:invocation[0]'
|
||||
| 'error.platform.vc-item.requestingRevoke:invocation[0]';
|
||||
setPrivateKey: 'done.invoke.vc-item.addKeyPair:invocation[0]';
|
||||
setPublicKey: 'done.invoke.vc-item.addKeyPair:invocation[0]';
|
||||
setPinCard: 'PIN_CARD';
|
||||
setPrivateKey:
|
||||
| 'done.invoke.vc-item.addKeyPair:invocation[0]'
|
||||
| 'done.invoke.vc-item.kebabPopUp.addKeyPair:invocation[0]';
|
||||
setPublicKey:
|
||||
| 'done.invoke.vc-item.addKeyPair:invocation[0]'
|
||||
| 'done.invoke.vc-item.kebabPopUp.addKeyPair:invocation[0]';
|
||||
setRevoke: 'done.invoke.vc-item.requestingRevoke:invocation[0]';
|
||||
setTag: 'SAVE_TAG';
|
||||
setTransactionId:
|
||||
@@ -168,22 +232,34 @@ export interface Typegen0 {
|
||||
setWalletBindingError:
|
||||
| 'error.platform.vc-item.addKeyPair:invocation[0]'
|
||||
| 'error.platform.vc-item.addingWalletBindingId:invocation[0]'
|
||||
| 'error.platform.vc-item.kebabPopUp.addKeyPair:invocation[0]'
|
||||
| 'error.platform.vc-item.kebabPopUp.addingWalletBindingId:invocation[0]'
|
||||
| 'error.platform.vc-item.kebabPopUp.requestingBindingOtp:invocation[0]'
|
||||
| 'error.platform.vc-item.kebabPopUp.updatingPrivateKey:invocation[0]'
|
||||
| 'error.platform.vc-item.requestingBindingOtp:invocation[0]'
|
||||
| 'error.platform.vc-item.updatingPrivateKey:invocation[0]';
|
||||
setWalletBindingErrorEmpty:
|
||||
| 'CANCEL'
|
||||
| 'done.invoke.vc-item.kebabPopUp.updatingPrivateKey:invocation[0]'
|
||||
| 'done.invoke.vc-item.updatingPrivateKey:invocation[0]';
|
||||
setWalletBindingId: 'done.invoke.vc-item.addingWalletBindingId:invocation[0]';
|
||||
setWalletBindingId:
|
||||
| 'done.invoke.vc-item.addingWalletBindingId:invocation[0]'
|
||||
| 'done.invoke.vc-item.kebabPopUp.addingWalletBindingId:invocation[0]';
|
||||
storeContext:
|
||||
| 'CREDENTIAL_DOWNLOADED'
|
||||
| 'PIN_CARD'
|
||||
| 'done.invoke.vc-item.kebabPopUp.updatingPrivateKey:invocation[0]'
|
||||
| 'done.invoke.vc-item.updatingPrivateKey:invocation[0]'
|
||||
| 'done.invoke.vc-item.verifyingCredential:invocation[0]';
|
||||
storeLock: 'done.invoke.vc-item.requestingLock:invocation[0]';
|
||||
storeTag: 'SAVE_TAG';
|
||||
updatePrivateKey: 'done.invoke.vc-item.updatingPrivateKey:invocation[0]';
|
||||
updatePrivateKey:
|
||||
| 'done.invoke.vc-item.kebabPopUp.updatingPrivateKey:invocation[0]'
|
||||
| 'done.invoke.vc-item.updatingPrivateKey:invocation[0]';
|
||||
updateVc:
|
||||
| 'CREDENTIAL_DOWNLOADED'
|
||||
| 'STORE_RESPONSE'
|
||||
| 'done.invoke.vc-item.kebabPopUp.updatingPrivateKey:invocation[0]'
|
||||
| 'done.invoke.vc-item.updatingPrivateKey:invocation[0]'
|
||||
| 'done.invoke.vc-item.verifyingCredential:invocation[0]';
|
||||
};
|
||||
@@ -194,16 +270,22 @@ export interface Typegen0 {
|
||||
isVcValid: '';
|
||||
};
|
||||
'eventsCausingServices': {
|
||||
addWalletBindnigId: 'done.invoke.vc-item.addKeyPair:invocation[0]';
|
||||
addWalletBindnigId:
|
||||
| 'done.invoke.vc-item.addKeyPair:invocation[0]'
|
||||
| 'done.invoke.vc-item.kebabPopUp.addKeyPair:invocation[0]';
|
||||
checkDownloadExpiryLimit: 'STORE_RESPONSE';
|
||||
checkStatus: 'done.invoke.vc-item.checkingServerData.verifyingDownloadLimitExpiry:invocation[0]';
|
||||
checkStatus:
|
||||
| 'done.invoke.vc-item.checkingServerData.verifyingDownloadLimitExpiry:invocation[0]'
|
||||
| 'error.platform.vc-item.checkingServerData.verifyingDownloadLimitExpiry:invocation[0]';
|
||||
downloadCredential: 'DOWNLOAD_READY';
|
||||
generateKeyPair: 'INPUT_OTP';
|
||||
requestBindingOtp: 'CONFIRM';
|
||||
requestLock: 'INPUT_OTP';
|
||||
requestOtp: 'LOCK_VC';
|
||||
requestRevoke: 'INPUT_OTP';
|
||||
updatePrivateKey: 'done.invoke.vc-item.addingWalletBindingId:invocation[0]';
|
||||
updatePrivateKey:
|
||||
| 'done.invoke.vc-item.addingWalletBindingId:invocation[0]'
|
||||
| 'done.invoke.vc-item.kebabPopUp.addingWalletBindingId:invocation[0]';
|
||||
verifyCredential: '' | 'VERIFY';
|
||||
};
|
||||
'matchesStates':
|
||||
@@ -224,8 +306,19 @@ export interface Typegen0 {
|
||||
| 'invalid'
|
||||
| 'invalid.backend'
|
||||
| 'invalid.otp'
|
||||
| 'kebabPopUp'
|
||||
| 'kebabPopUp.acceptingBindingOtp'
|
||||
| 'kebabPopUp.addKeyPair'
|
||||
| 'kebabPopUp.addingWalletBindingId'
|
||||
| 'kebabPopUp.idle'
|
||||
| 'kebabPopUp.requestingBindingOtp'
|
||||
| 'kebabPopUp.showActivities'
|
||||
| 'kebabPopUp.showBindingWarning'
|
||||
| 'kebabPopUp.showingWalletBindingError'
|
||||
| 'kebabPopUp.updatingPrivateKey'
|
||||
| 'lockingVc'
|
||||
| 'loggingRevoke'
|
||||
| 'pinCard'
|
||||
| 'requestingBindingOtp'
|
||||
| 'requestingLock'
|
||||
| 'requestingOtp'
|
||||
@@ -242,6 +335,16 @@ export interface Typegen0 {
|
||||
| 'downloadingCredential'
|
||||
| 'verifyingDownloadLimitExpiry';
|
||||
invalid?: 'backend' | 'otp';
|
||||
kebabPopUp?:
|
||||
| 'acceptingBindingOtp'
|
||||
| 'addKeyPair'
|
||||
| 'addingWalletBindingId'
|
||||
| 'idle'
|
||||
| 'requestingBindingOtp'
|
||||
| 'showActivities'
|
||||
| 'showBindingWarning'
|
||||
| 'showingWalletBindingError'
|
||||
| 'updatingPrivateKey';
|
||||
};
|
||||
'tags': never;
|
||||
}
|
||||
|
||||
1627
package-lock.json
generated
@@ -18,6 +18,7 @@
|
||||
"@digitalbazaar/rsa-signature-2018": "digitalbazaar/rsa-signature-2018#initial",
|
||||
"@digitalbazaar/rsa-verification-key-2018": "digitalbazaar/rsa-verification-key-2018#initial",
|
||||
"@digitalcredentials/vc": "^1.1.2",
|
||||
"@expo-google-fonts/inter": "^0.2.3",
|
||||
"@expo-google-fonts/poppins": "^0.2.0",
|
||||
"@expo/metro-config": "^0.3.12",
|
||||
"@idpass/smartshare-react-native": "0.2.3-beta.2",
|
||||
@@ -49,15 +50,18 @@
|
||||
"react": "17.0.1",
|
||||
"react-i18next": "^11.16.6",
|
||||
"react-native": "0.64.4",
|
||||
"react-native-animated-pagination-dot": "^0.4.0",
|
||||
"react-native-app-intro-slider": "^4.0.4",
|
||||
"react-native-biometrics-changed": "^1.1.8",
|
||||
"react-native-bluetooth-state-manager": "^1.3.2",
|
||||
"react-native-cli": "^2.0.1",
|
||||
"react-native-device-info": "^8.4.8",
|
||||
"react-native-dotenv": "^3.3.1",
|
||||
"react-native-elements": "^3.4.2",
|
||||
"react-native-fs": "^2.20.0",
|
||||
"react-native-gesture-handler": "~2.1.0",
|
||||
"react-native-keychain": "^8.0.0",
|
||||
"react-native-linear-gradient": "^2.6.2",
|
||||
"react-native-location-enabler": "^4.1.0",
|
||||
"react-native-openid4vp-ble": "github:mosip/tuvali#v0.3.10",
|
||||
"react-native-permissions": "^3.6.0",
|
||||
|
||||
@@ -9,19 +9,24 @@ import { WelcomeScreen } from '../screens/WelcomeScreen';
|
||||
import { PasscodeScreen } from '../screens/PasscodeScreen';
|
||||
import { MainLayout } from '../screens/MainLayout';
|
||||
import { NotificationsScreen } from '../screens/NotificationsScreen';
|
||||
import { Image } from 'react-native';
|
||||
import { SetupLanguageScreen } from '../screens/SetupLanguageScreen';
|
||||
import { IntroSlidersScreen } from '../screens/Home/IntroSlidersScreen';
|
||||
|
||||
export const baseRoutes: Screen[] = [
|
||||
{
|
||||
name: 'Language',
|
||||
component: SetupLanguageScreen,
|
||||
},
|
||||
{
|
||||
name: 'IntroSliders',
|
||||
component: IntroSlidersScreen,
|
||||
options: {
|
||||
headerShown: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'Welcome',
|
||||
component: WelcomeScreen,
|
||||
options: {
|
||||
headerLeft: () =>
|
||||
React.createElement(Image, {
|
||||
source: require('../assets/idpass-logo.png'),
|
||||
style: { width: 124, height: 27, resizeMode: 'contain' },
|
||||
}),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'Auth',
|
||||
@@ -52,6 +57,8 @@ export const authRoutes: Screen[] = [
|
||||
];
|
||||
|
||||
export type RootStackParamList = {
|
||||
Language: undefined;
|
||||
IntroSliders: undefined;
|
||||
Welcome: undefined;
|
||||
Auth: undefined;
|
||||
Passcode: {
|
||||
|
||||
@@ -3,11 +3,12 @@ import {
|
||||
BottomTabNavigationOptions,
|
||||
BottomTabScreenProps,
|
||||
} from '@react-navigation/bottom-tabs';
|
||||
import { Image } from 'react-native';
|
||||
import { HomeScreen } from '../screens/Home/HomeScreen';
|
||||
import { ProfileScreen } from '../screens/Profile/ProfileScreen';
|
||||
import { RootStackParamList } from './index';
|
||||
import { RequestLayout } from '../screens/Request/RequestLayout';
|
||||
import { ScanLayout } from '../screens/Scan/ScanLayout';
|
||||
import { HistoryScreen } from '../screens/History/HistoryScreen';
|
||||
import i18n from '../i18n';
|
||||
import { Platform } from 'react-native';
|
||||
import { isGoogleNearbyEnabled } from '../lib/smartshare';
|
||||
@@ -17,7 +18,12 @@ const home: TabScreen = {
|
||||
component: HomeScreen,
|
||||
icon: 'home',
|
||||
options: {
|
||||
title: i18n.t('MainLayout:home'),
|
||||
headerTitle: '',
|
||||
headerLeft: () =>
|
||||
React.createElement(Image, {
|
||||
source: require('../assets/inji-home-logo.png'),
|
||||
style: { width: 124, height: 27, resizeMode: 'contain' },
|
||||
}),
|
||||
},
|
||||
};
|
||||
const scan: TabScreen = {
|
||||
@@ -38,12 +44,13 @@ const request: TabScreen = {
|
||||
headerShown: false,
|
||||
},
|
||||
};
|
||||
const settings: TabScreen = {
|
||||
name: 'Settings',
|
||||
component: ProfileScreen,
|
||||
icon: 'settings',
|
||||
const history: TabScreen = {
|
||||
name: 'History',
|
||||
component: HistoryScreen,
|
||||
icon: 'history',
|
||||
options: {
|
||||
title: i18n.t('MainLayout:Settings'),
|
||||
title: i18n.t('MainLayout:history'),
|
||||
headerRight: null,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -55,7 +62,7 @@ if (Platform.OS !== 'ios' || isGoogleNearbyEnabled) {
|
||||
mainRoutes.push(request);
|
||||
}
|
||||
|
||||
mainRoutes.push(settings);
|
||||
mainRoutes.push(history);
|
||||
|
||||
export type MainBottomTabParamList = {
|
||||
Home: {
|
||||
@@ -63,7 +70,7 @@ export type MainBottomTabParamList = {
|
||||
};
|
||||
Scan: undefined;
|
||||
Request: undefined;
|
||||
Settings: undefined;
|
||||
History: undefined;
|
||||
};
|
||||
|
||||
export interface TabScreen {
|
||||
|
||||
@@ -6,8 +6,6 @@ import {
|
||||
} from '@react-navigation/native-stack';
|
||||
import { authRoutes, baseRoutes } from '../routes';
|
||||
import { useAppLayout } from './AppLayoutController';
|
||||
import { Icon } from 'react-native-elements';
|
||||
import { Theme } from '../components/ui/styleUtils';
|
||||
import { StatusBar } from 'react-native';
|
||||
|
||||
const { Navigator, Screen } = createNativeStackNavigator();
|
||||
@@ -25,7 +23,15 @@ export const AppLayout: React.FC = () => {
|
||||
return (
|
||||
<NavigationContainer>
|
||||
<StatusBar animated={true} barStyle="dark-content" />
|
||||
<Navigator initialRouteName={baseRoutes[0].name} screenOptions={options}>
|
||||
<Navigator
|
||||
initialRouteName={
|
||||
controller.isLanguagesetup
|
||||
? baseRoutes[0].name
|
||||
: controller.isUnAuthorized
|
||||
? baseRoutes[2].name
|
||||
: baseRoutes[1].name
|
||||
}
|
||||
screenOptions={options}>
|
||||
{baseRoutes.map((route) => (
|
||||
<Screen key={route.name} {...route} />
|
||||
))}
|
||||
|
||||
@@ -1,13 +1,19 @@
|
||||
import { useSelector } from '@xstate/react';
|
||||
import { useContext } from 'react';
|
||||
import { selectAuthorized } from '../machines/auth';
|
||||
import {
|
||||
selectAuthorized,
|
||||
selectLanguagesetup,
|
||||
selectUnauthorized,
|
||||
} from '../machines/auth';
|
||||
import { GlobalContext } from '../shared/GlobalContext';
|
||||
|
||||
export function useAppLayout() {
|
||||
const { appService } = useContext(GlobalContext);
|
||||
const authService = appService.children.get('auth');
|
||||
|
||||
const isLanguagesetup = useSelector(authService, selectLanguagesetup);
|
||||
return {
|
||||
isAuthorized: useSelector(authService, selectAuthorized),
|
||||
isUnAuthorized: useSelector(authService, selectUnauthorized),
|
||||
isLanguagesetup,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ import React from 'react';
|
||||
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 { Button, Column, Text } from '../components/ui';
|
||||
import { Theme } from '../components/ui/styleUtils';
|
||||
import { RootRouteProps } from '../routes';
|
||||
import { useAuthScreen } from './AuthScreenController';
|
||||
@@ -15,21 +15,29 @@ export const AuthScreen: React.FC<RootRouteProps> = (props) => {
|
||||
<Column
|
||||
fill
|
||||
padding={[32, 32, 32, 32]}
|
||||
backgroundColor={Theme.Colors.whiteBackgroundColor}>
|
||||
backgroundColor={Theme.Colors.whiteBackgroundColor}
|
||||
align="space-between">
|
||||
<MessageOverlay
|
||||
isVisible={controller.alertMsg != ''}
|
||||
onBackdropPress={controller.hideAlert}
|
||||
title={controller.alertMsg}
|
||||
/>
|
||||
<Column>
|
||||
<Text align="center">{t('header')}</Text>
|
||||
<Icon name="fingerprint" size={80} color={Theme.Colors.Icon} />
|
||||
<Column margin="20 0 0 0">
|
||||
<Text weight="semibold" align="center">
|
||||
{t('header')}
|
||||
</Text>
|
||||
<Text align="center" color={Theme.Colors.GrayText}>
|
||||
{t('Description')}
|
||||
</Text>
|
||||
</Column>
|
||||
</Column>
|
||||
<Centered fill>
|
||||
<Icon name="fingerprint" size={180} color={Theme.Colors.Icon} />
|
||||
</Centered>
|
||||
|
||||
<Column>
|
||||
<Button
|
||||
title={t('useBiometrics')}
|
||||
type="radius"
|
||||
margin="0 0 8 0"
|
||||
disabled={!controller.isBiometricsAvailable}
|
||||
onPress={controller.useBiometrics}
|
||||
|
||||
@@ -2,21 +2,21 @@ import React from 'react';
|
||||
import { RefreshControl } from 'react-native';
|
||||
import { Icon } from 'react-native-elements';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { Centered, Column, Text } from '../../components/ui';
|
||||
import { useHistoryTab } from './HistoryTabController';
|
||||
import { HomeScreenTabProps } from './HomeScreen';
|
||||
import { useHistoryTab } from './HistoryScreenController';
|
||||
import { ActivityLogText } from '../../components/ActivityLogText';
|
||||
import { MainRouteProps } from '../../routes/main';
|
||||
import { Theme } from '../../components/ui/styleUtils';
|
||||
|
||||
export const HistoryTab: React.FC<HomeScreenTabProps> = (props) => {
|
||||
const { t } = useTranslation('HistoryTab');
|
||||
export const HistoryScreen: React.FC<MainRouteProps> = () => {
|
||||
const { t } = useTranslation('HistoryScreen');
|
||||
const controller = useHistoryTab();
|
||||
|
||||
return (
|
||||
<Column fill style={{ display: props.isVisible ? 'flex' : 'none' }}>
|
||||
<Column fill backgroundColor={Theme.Colors.whiteBackgroundColor}>
|
||||
<Column
|
||||
scroll
|
||||
padding="32 0"
|
||||
padding="7 0"
|
||||
refreshControl={
|
||||
<RefreshControl
|
||||
refreshing={controller.isRefreshing}
|
||||
@@ -3,7 +3,6 @@ import { Tab } from 'react-native-elements';
|
||||
import { Column, Text } from '../../components/ui';
|
||||
import { Theme } from '../../components/ui/styleUtils';
|
||||
import { HomeRouteProps } from '../../routes/main';
|
||||
import { HistoryTab } from './HistoryTab';
|
||||
import { MyVcsTab } from './MyVcsTab';
|
||||
import { ReceivedVcsTab } from './ReceivedVcsTab';
|
||||
import { ViewVcModal } from './ViewVcModal';
|
||||
@@ -20,14 +19,6 @@ export const HomeScreen: React.FC<HomeRouteProps> = (props) => {
|
||||
return (
|
||||
<React.Fragment>
|
||||
<Column fill backgroundColor={Theme.Colors.lightGreyBackgroundColor}>
|
||||
<Tab
|
||||
value={controller.activeTab}
|
||||
onChange={controller.SELECT_TAB}
|
||||
indicatorStyle={Theme.Styles.tabIndicator}>
|
||||
{TabItem(t('myVcsTab', { vcLabel: controller.vcLabel.plural }))}
|
||||
{TabItem(t('receivedVcsTab', { vcLabel: controller.vcLabel.plural }))}
|
||||
{TabItem(t('historyTab'))}
|
||||
</Tab>
|
||||
{controller.haveTabsLoaded && (
|
||||
<Column fill>
|
||||
<MyVcsTab
|
||||
@@ -40,11 +31,6 @@ export const HomeScreen: React.FC<HomeRouteProps> = (props) => {
|
||||
service={controller.tabRefs.receivedVcs}
|
||||
vcItemActor={controller.selectedVc}
|
||||
/>
|
||||
<HistoryTab
|
||||
isVisible={controller.activeTab === 2}
|
||||
vcItemActor={controller.selectedVc}
|
||||
service={controller.tabRefs.history}
|
||||
/>
|
||||
</Column>
|
||||
)}
|
||||
</Column>
|
||||
|
||||
@@ -9,10 +9,6 @@ import {
|
||||
import { createModel } from 'xstate/lib/model';
|
||||
import { vcItemMachine } from '../../machines/vcItem';
|
||||
import { AppServices } from '../../shared/GlobalContext';
|
||||
import {
|
||||
createHistoryTabMachine,
|
||||
HistoryTabMachine,
|
||||
} from './HistoryTabMachine';
|
||||
import { createMyVcsTabMachine, MyVcsTabMachine } from './MyVcsTabMachine';
|
||||
import {
|
||||
createReceivedVcsTabMachine,
|
||||
@@ -25,7 +21,6 @@ const model = createModel(
|
||||
tabRefs: {
|
||||
myVcs: {} as ActorRefFrom<typeof MyVcsTabMachine>,
|
||||
receivedVcs: {} as ActorRefFrom<typeof ReceivedVcsTabMachine>,
|
||||
history: {} as ActorRefFrom<typeof HistoryTabMachine>,
|
||||
},
|
||||
selectedVc: null as ActorRefFrom<typeof vcItemMachine>,
|
||||
activeTab: 0,
|
||||
@@ -45,14 +40,12 @@ const model = createModel(
|
||||
|
||||
const MY_VCS_TAB_REF_ID = 'myVcsTab';
|
||||
const RECEIVED_VCS_TAB_REF_ID = 'receivedVcsTab';
|
||||
const HISTORY_TAB_REF_ID = 'historyTab';
|
||||
|
||||
export const HomeScreenEvents = model.events;
|
||||
|
||||
export type TabRef =
|
||||
| ActorRefFrom<typeof MyVcsTabMachine>
|
||||
| ActorRefFrom<typeof ReceivedVcsTabMachine>
|
||||
| ActorRefFrom<typeof HistoryTabMachine>;
|
||||
| ActorRefFrom<typeof ReceivedVcsTabMachine>;
|
||||
|
||||
export const HomeScreenMachine = model.createMachine(
|
||||
{
|
||||
@@ -143,10 +136,6 @@ export const HomeScreenMachine = model.createMachine(
|
||||
createReceivedVcsTabMachine(context.serviceRefs),
|
||||
RECEIVED_VCS_TAB_REF_ID
|
||||
),
|
||||
history: spawn(
|
||||
createHistoryTabMachine(context.serviceRefs),
|
||||
HISTORY_TAB_REF_ID
|
||||
),
|
||||
}),
|
||||
}),
|
||||
|
||||
|
||||
146
screens/Home/IntroSlidersScreen.tsx
Normal file
@@ -0,0 +1,146 @@
|
||||
import React, { useRef, useContext } from 'react';
|
||||
import AppIntroSlider from 'react-native-app-intro-slider';
|
||||
import { Dimensions, Image, StatusBar, View } from 'react-native';
|
||||
import { Centered, Column, Row, Text, Button } from '../../components/ui';
|
||||
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';
|
||||
import { RootRouteProps } from '../../routes';
|
||||
import { useWelcomeScreen } from '../WelcomeScreenController';
|
||||
import LinearGradient from 'react-native-linear-gradient';
|
||||
|
||||
export const IntroSlidersScreen: React.FC<RootRouteProps> = (props) => {
|
||||
const slider = useRef<AppIntroSlider>();
|
||||
|
||||
const { t } = useTranslation('OnboardingOverlay');
|
||||
const { appService } = useContext(GlobalContext);
|
||||
const settingsService = appService.children.get('settings');
|
||||
const vcLabel = useSelector(settingsService, selectVcLabel);
|
||||
const controller = useWelcomeScreen(props);
|
||||
|
||||
const slides = [
|
||||
{
|
||||
key: 'one',
|
||||
title: t('stepOneTitle'),
|
||||
text: t('stepOneText', { vcLabel: vcLabel.plural }),
|
||||
image: Theme.sharingIntro,
|
||||
},
|
||||
{
|
||||
key: 'two',
|
||||
title: t('stepTwoTitle', { vcLabel: vcLabel.singular }),
|
||||
text: t('stepTwoText', { vcLabel: vcLabel.plural }),
|
||||
image: Theme.walletIntro,
|
||||
},
|
||||
{
|
||||
key: 'three',
|
||||
title: t('stepThreeTitle'),
|
||||
text: t('stepThreeText', { vcLabel: vcLabel.plural }),
|
||||
image: Theme.IntroScanner,
|
||||
},
|
||||
];
|
||||
|
||||
const renderItem = ({ item }) => {
|
||||
return (
|
||||
<LinearGradient colors={Theme.Colors.gradientBtn}>
|
||||
<Centered>
|
||||
<Row crossAlign="center">
|
||||
<Column
|
||||
style={{
|
||||
flex: 3,
|
||||
alignItems: 'flex-end',
|
||||
marginRight: 75,
|
||||
}}>
|
||||
<Image
|
||||
style={{ marginTop: 50, marginBottom: 30 }}
|
||||
source={Theme.injiSmallLogo}
|
||||
/>
|
||||
</Column>
|
||||
|
||||
<Column
|
||||
style={{
|
||||
flex: 1,
|
||||
alignItems: 'flex-end',
|
||||
}}>
|
||||
<Button
|
||||
type="plain"
|
||||
title={t('skip')}
|
||||
onPress={controller.NEXT}
|
||||
/>
|
||||
</Column>
|
||||
</Row>
|
||||
<Image source={item.image} />
|
||||
<Column
|
||||
style={Theme.OnboardingOverlayStyles.bottomContainer}
|
||||
crossAlign="center"
|
||||
backgroundColor={Theme.Colors.whiteText}
|
||||
width={Dimensions.get('screen').width}>
|
||||
<Text weight="semibold" margin="0 0 18 0">
|
||||
{item.title}
|
||||
</Text>
|
||||
<Text margin="0 0 150 0" color={Theme.Colors.GrayText}>
|
||||
{item.text}
|
||||
</Text>
|
||||
{item.footer}
|
||||
</Column>
|
||||
</Centered>
|
||||
</LinearGradient>
|
||||
);
|
||||
};
|
||||
|
||||
const renderNextButton = () => {
|
||||
return (
|
||||
<View>
|
||||
<LinearGradient
|
||||
colors={Theme.Colors.gradientBtn}
|
||||
style={{ borderRadius: 10, height: 50, marginTop: -10 }}>
|
||||
<Text
|
||||
weight="semibold"
|
||||
align="center"
|
||||
color="#FFFFFF"
|
||||
margin="10 0 0 0">
|
||||
{t('next')}
|
||||
</Text>
|
||||
</LinearGradient>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
const renderDoneButton = () => {
|
||||
return (
|
||||
<View>
|
||||
<LinearGradient
|
||||
colors={Theme.Colors.gradientBtn}
|
||||
style={{ borderRadius: 10, height: 50, marginTop: -10 }}>
|
||||
<Text
|
||||
weight="semibold"
|
||||
align="center"
|
||||
color="#FFFFFF"
|
||||
margin="10 0 0 0">
|
||||
{t('stepThreeButton')}
|
||||
</Text>
|
||||
</LinearGradient>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
return (
|
||||
<View style={{ flex: 1 }}>
|
||||
<StatusBar translucent={true} backgroundColor="transparent" />
|
||||
<AppIntroSlider
|
||||
data={slides}
|
||||
renderDoneButton={renderDoneButton}
|
||||
renderNextButton={renderNextButton}
|
||||
bottomButton
|
||||
ref={slider}
|
||||
activeDotStyle={{
|
||||
backgroundColor: Theme.Colors.Icon,
|
||||
width: 30,
|
||||
marginBottom: 90,
|
||||
}}
|
||||
dotStyle={{ backgroundColor: Theme.Colors.dotColor, marginBottom: 90 }}
|
||||
renderItem={renderItem}
|
||||
onDone={() => controller.NEXT()}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
@@ -18,6 +18,7 @@ export const AddVcModal: React.FC<AddVcModalProps> = (props) => {
|
||||
}
|
||||
onDismiss={controller.DISMISS}
|
||||
onPress={props.onPress}
|
||||
headerTitle={t('inputIdHeader')}
|
||||
/>
|
||||
|
||||
<OtpVerificationModal
|
||||
|
||||
@@ -23,6 +23,7 @@ const model = createModel(
|
||||
otpError: '',
|
||||
transactionId: '',
|
||||
requestId: '',
|
||||
isPinned: false,
|
||||
},
|
||||
{
|
||||
events: {
|
||||
@@ -99,7 +100,7 @@ export const AddVcModalMachine =
|
||||
},
|
||||
],
|
||||
SELECT_ID_TYPE: {
|
||||
actions: ['setIdType', 'clearId'],
|
||||
actions: ['clearIdError', 'setIdType', 'clearId'],
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -134,7 +135,7 @@ export const AddVcModalMachine =
|
||||
},
|
||||
],
|
||||
SELECT_ID_TYPE: {
|
||||
actions: ['setIdType', 'clearId'],
|
||||
actions: ['clearIdError', 'setIdType', 'clearId'],
|
||||
target: 'idle',
|
||||
},
|
||||
},
|
||||
|
||||
@@ -38,7 +38,7 @@ export interface Typegen0 {
|
||||
};
|
||||
'eventsCausingActions': {
|
||||
clearId: 'SELECT_ID_TYPE';
|
||||
clearIdError: 'INPUT_ID' | 'VALIDATE_INPUT';
|
||||
clearIdError: 'INPUT_ID' | 'SELECT_ID_TYPE' | 'VALIDATE_INPUT';
|
||||
clearOtp:
|
||||
| 'DISMISS'
|
||||
| 'done.invoke.AddVcModal.acceptingIdInput.requestingOtp:invocation[0]'
|
||||
|
||||
@@ -8,7 +8,7 @@ import { Theme } from '../../../components/ui/styleUtils';
|
||||
export const BindingVcWarningOverlay: React.FC<QrLoginWarningProps> = (
|
||||
props
|
||||
) => {
|
||||
const { t } = useTranslation('VcDetails');
|
||||
const { t } = useTranslation('BindingVcWarningOverlay');
|
||||
|
||||
return (
|
||||
<Overlay
|
||||
@@ -18,8 +18,9 @@ export const BindingVcWarningOverlay: React.FC<QrLoginWarningProps> = (
|
||||
align="space-between"
|
||||
crossAlign="center"
|
||||
padding={'10'}
|
||||
width={Dimensions.get('screen').width * 0.8}>
|
||||
<Row align="center" crossAlign="center" margin={'0 80 0 0'}>
|
||||
width={Dimensions.get('screen').width * 0.8}
|
||||
height={Dimensions.get('screen').height * 0}>
|
||||
<Row align="center" crossAlign="center" margin={'0 80 -10 0'}>
|
||||
<Image source={Theme.WarningLogo} resizeMethod="auto" />
|
||||
<Text
|
||||
margin={'0 0 0 -80'}
|
||||
@@ -29,18 +30,22 @@ export const BindingVcWarningOverlay: React.FC<QrLoginWarningProps> = (
|
||||
</Text>
|
||||
</Row>
|
||||
|
||||
<Text size="regular" weight="bold">
|
||||
{t('Alert')}
|
||||
</Text>
|
||||
<Column crossAlign="center" margin="0 0 30 0">
|
||||
<Text weight="semibold">{t('alert')}</Text>
|
||||
|
||||
<Text align="center" size="smaller">
|
||||
{t('BindingWarning')}
|
||||
</Text>
|
||||
<Text
|
||||
align="center"
|
||||
size="small"
|
||||
weight="semibold"
|
||||
color={Theme.Colors.GrayText}>
|
||||
{t('BindingWarning')}
|
||||
</Text>
|
||||
</Column>
|
||||
|
||||
<Button
|
||||
margin={'10 0 0 0'}
|
||||
type="radius"
|
||||
title={t('yes_confirm')}
|
||||
margin={'30 0 0 0'}
|
||||
type="gradient"
|
||||
title={t('yesConfirm')}
|
||||
onPress={props.onConfirm}
|
||||
/>
|
||||
|
||||
|
||||
65
screens/Home/MyVcs/HistoryTab.tsx
Normal file
@@ -0,0 +1,65 @@
|
||||
import React from 'react';
|
||||
import { Icon, ListItem } from 'react-native-elements';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Modal } from '../../../components/ui/Modal';
|
||||
import { Centered, Column, Text } from '../../../components/ui';
|
||||
import { ActivityLogText } from '../../../components/ActivityLogText';
|
||||
import { ActorRefFrom } from 'xstate';
|
||||
import { vcItemMachine } from '../../../machines/vcItem';
|
||||
import { useKebabPopUp } from '../../../components/KebabPopUpController';
|
||||
import { Theme } from '../../../components/ui/styleUtils';
|
||||
|
||||
export const HistoryTab: React.FC<HistoryTabProps> = (props) => {
|
||||
const { t } = useTranslation('HistoryTab');
|
||||
const controller = useKebabPopUp(props);
|
||||
console.log(controller.isShowActivities, 'log from activities');
|
||||
return (
|
||||
<ListItem bottomDivider onPress={controller.SHOW_ACTIVITY}>
|
||||
<ListItem.Content>
|
||||
<ListItem.Title>
|
||||
<Text
|
||||
size="small"
|
||||
weight="semibold"
|
||||
color={Theme.Colors.walletbindingLabel}>
|
||||
{props.label}
|
||||
</Text>
|
||||
</ListItem.Title>
|
||||
</ListItem.Content>
|
||||
<Modal
|
||||
headerLabel={props.vcKey.split(':')[2]}
|
||||
isVisible={controller.isShowActivities}
|
||||
onDismiss={controller.DISMISS}>
|
||||
<Column fill>
|
||||
{controller.activities.map((activity) => {
|
||||
if (activity._vcKey == props.vcKey) {
|
||||
return (
|
||||
<ActivityLogText
|
||||
key={`${activity.timestamp}-${activity._vcKey}`}
|
||||
activity={activity}
|
||||
/>
|
||||
);
|
||||
}
|
||||
})}
|
||||
{controller.activities.length === 0 && (
|
||||
<Centered fill>
|
||||
<Icon
|
||||
style={{ marginBottom: 20 }}
|
||||
size={40}
|
||||
name="sentiment-dissatisfied"
|
||||
/>
|
||||
<Text align="center" weight="semibold" margin="0 0 4 0">
|
||||
{t('noHistory')}
|
||||
</Text>
|
||||
</Centered>
|
||||
)}
|
||||
</Column>
|
||||
</Modal>
|
||||
</ListItem>
|
||||
);
|
||||
};
|
||||
|
||||
export interface HistoryTabProps {
|
||||
label: string;
|
||||
vcKey: string;
|
||||
service: ActorRefFrom<typeof vcItemMachine>;
|
||||
}
|
||||
@@ -39,20 +39,26 @@ export const IdInputModal: React.FC<IdInputModalProps> = (props) => {
|
||||
<Modal
|
||||
onDismiss={dismissInput}
|
||||
isVisible={props.isVisible}
|
||||
onShow={setIndividualID}>
|
||||
onShow={setIndividualID}
|
||||
headerTitle={t('title')}
|
||||
headerElevation={2}>
|
||||
<KeyboardAvoidingView
|
||||
style={{ flex: 1 }}
|
||||
behavior={Platform.OS === 'ios' ? 'padding' : 'height'}>
|
||||
<Column fill align="space-between" pY={32} pX={24}>
|
||||
<Text align="center">
|
||||
{t('header', { vcLabel: controller.vcLabel.singular })}
|
||||
</Text>
|
||||
<Column>
|
||||
<Row crossAlign="flex-end">
|
||||
<Text
|
||||
align="left"
|
||||
size="regular"
|
||||
style={Theme.TextStyles.retrieveIdLabel}>
|
||||
{t('guideLabel', { vcLabel: controller.vcLabel.singular })}
|
||||
</Text>
|
||||
<Row crossAlign="flex-end" style={{ marginTop: 20 }}>
|
||||
<Column
|
||||
width="33%"
|
||||
style={{
|
||||
borderBottomWidth: 1,
|
||||
marginBottom: 2,
|
||||
borderColor:
|
||||
Platform.OS === 'ios'
|
||||
? 'transparent'
|
||||
@@ -69,49 +75,51 @@ export const IdInputModal: React.FC<IdInputModalProps> = (props) => {
|
||||
</Column>
|
||||
<Column fill>
|
||||
<Input
|
||||
inputContainerStyle={
|
||||
controller.id ? Theme.Styles.VidInputBottom : null
|
||||
}
|
||||
placeholder={!controller.id ? inputLabel : ''}
|
||||
label={controller.id ? inputLabel : ''}
|
||||
labelStyle={{
|
||||
color: controller.isInvalid
|
||||
? Theme.Colors.errorMessage
|
||||
: Theme.Colors.textValue,
|
||||
textAlign: 'left',
|
||||
}}
|
||||
inputStyle={{
|
||||
textAlign: I18nManager.isRTL ? 'right' : 'left',
|
||||
fontWeight: '700',
|
||||
}}
|
||||
selectionColor={Theme.Colors.Cursor}
|
||||
value={controller.id}
|
||||
keyboardType="number-pad"
|
||||
rightIcon={
|
||||
controller.isInvalid ? (
|
||||
<Icon
|
||||
name="error"
|
||||
size={18}
|
||||
color={Theme.Colors.errorMessage}
|
||||
/>
|
||||
) : null
|
||||
}
|
||||
errorStyle={{ color: Theme.Colors.errorMessage }}
|
||||
errorStyle={Theme.TextStyles.error}
|
||||
errorMessage={controller.idError}
|
||||
onChangeText={controller.INPUT_ID}
|
||||
ref={setIdInputRef}
|
||||
/>
|
||||
</Column>
|
||||
</Row>
|
||||
</Column>
|
||||
<Column>
|
||||
<Button
|
||||
title={t('generateVc', { vcLabel: controller.vcLabel.singular })}
|
||||
type="gradient"
|
||||
title={t('downloadID')}
|
||||
disabled={!controller.id}
|
||||
margin="24 0 0 0"
|
||||
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>
|
||||
<Row style={Theme.Styles.getId}>
|
||||
<Text
|
||||
color={Theme.Colors.getVidColor}
|
||||
weight="semibold"
|
||||
size="small">
|
||||
{t('noUIN/VID')}
|
||||
</Text>
|
||||
<TouchableOpacity activeOpacity={1} onPress={props.onPress}>
|
||||
<Text
|
||||
color={Theme.Colors.AddIdBtnBg}
|
||||
weight="semibold"
|
||||
size="small">
|
||||
{t('getItHere')}
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
</Row>
|
||||
)}
|
||||
</Column>
|
||||
</Column>
|
||||
|
||||
@@ -1,44 +0,0 @@
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { PinInput } from '../../../components/PinInput';
|
||||
import { Column, Text } from '../../../components/ui';
|
||||
import { ModalProps, Modal } from '../../../components/ui/Modal';
|
||||
import { Theme } from '../../../components/ui/styleUtils';
|
||||
import { Image } from 'react-native';
|
||||
import { Icon } from 'react-native-elements';
|
||||
|
||||
export const OtpVerification: React.FC<OtpVerificationModalProps> = (props) => {
|
||||
const { t } = useTranslation('OtpVerificationModal');
|
||||
|
||||
return (
|
||||
<Modal
|
||||
isVisible={props.isVisible}
|
||||
onDismiss={props.onDismiss}
|
||||
headerElevation={2}
|
||||
headerTitle={t('header')}
|
||||
headerRight={<Icon name={''} />}>
|
||||
<Column
|
||||
fill
|
||||
padding="32"
|
||||
backgroundColor={Theme.Colors.lightGreyBackgroundColor}>
|
||||
<Column fill align="space-between" crossAlign="center">
|
||||
<Text align="center">{t('enterOtp')}</Text>
|
||||
<Image source={Theme.OtpLogo} resizeMethod="auto" />
|
||||
<Text
|
||||
align="center"
|
||||
color={Theme.Colors.errorMessage}
|
||||
margin="16 0 0 0">
|
||||
{props.error}
|
||||
</Text>
|
||||
<PinInput length={6} onDone={props.onInputDone} />
|
||||
</Column>
|
||||
<Column fill></Column>
|
||||
</Column>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
interface OtpVerificationModalProps extends ModalProps {
|
||||
onInputDone: (otp: string) => void;
|
||||
error?: string;
|
||||
}
|
||||
@@ -1,3 +1,6 @@
|
||||
{
|
||||
"enterOtp": "Enter the 6-digit verification code we sent you"
|
||||
"title": "OTP Verification",
|
||||
"otpSentMessage": "We've sent the 6 digit code to your registered mobile number!",
|
||||
"resendTheCode": "You can resend the code in ",
|
||||
"resendCode": "Resend Code"
|
||||
}
|
||||
@@ -1,10 +1,10 @@
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
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 { ModalProps, Modal } from '../../../components/ui/Modal';
|
||||
import { Theme } from '../../../components/ui/styleUtils';
|
||||
import { Image, TouchableOpacity } from 'react-native';
|
||||
|
||||
export const OtpVerificationModal: React.FC<OtpVerificationModalProps> = (
|
||||
props
|
||||
@@ -13,10 +13,29 @@ export const OtpVerificationModal: React.FC<OtpVerificationModalProps> = (
|
||||
|
||||
return (
|
||||
<Modal isVisible={props.isVisible} onDismiss={props.onDismiss}>
|
||||
<Column fill padding="32">
|
||||
<Icon name="lock" color={Theme.Colors.Icon} size={60} />
|
||||
<Column fill align="space-between">
|
||||
<Text align="center">{t('enterOtp')}</Text>
|
||||
<Column
|
||||
fill
|
||||
padding="32"
|
||||
backgroundColor={Theme.Colors.whiteBackgroundColor}>
|
||||
<Column fill align="space-between" crossAlign="center">
|
||||
<Column crossAlign="center">
|
||||
<Image source={Theme.OtpLogo} resizeMethod="auto" />
|
||||
<Text
|
||||
margin="24 0 6 0"
|
||||
weight="bold"
|
||||
style={Theme.TextStyles.header}>
|
||||
{t('title')}
|
||||
</Text>
|
||||
<Text
|
||||
margin="0 24 15 24"
|
||||
color={Theme.Colors.RetrieveIdLabel}
|
||||
weight="semibold"
|
||||
size="small"
|
||||
align="center">
|
||||
{t('otpSentMessage')}
|
||||
</Text>
|
||||
</Column>
|
||||
|
||||
<Text
|
||||
align="center"
|
||||
color={Theme.Colors.errorMessage}
|
||||
@@ -24,6 +43,20 @@ export const OtpVerificationModal: React.FC<OtpVerificationModalProps> = (
|
||||
{props.error}
|
||||
</Text>
|
||||
<PinInput length={6} onDone={props.onInputDone} />
|
||||
|
||||
<Text
|
||||
margin="36 0 0 0"
|
||||
color={Theme.Colors.RetrieveIdLabel}
|
||||
weight="semibold"
|
||||
size="small">
|
||||
{t('resendTheCode')}
|
||||
</Text>
|
||||
|
||||
<TouchableOpacity activeOpacity={1}>
|
||||
<Text color={Theme.Colors.AddIdBtnBg} weight="bold" size="regular">
|
||||
{t('resendCode')}
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
</Column>
|
||||
<Column fill></Column>
|
||||
</Column>
|
||||
|
||||
102
screens/Home/MyVcs/WalletBinding.tsx
Normal file
@@ -0,0 +1,102 @@
|
||||
import React from 'react';
|
||||
import { Icon, ListItem } from 'react-native-elements';
|
||||
import { Row, Text } from '../../../components/ui';
|
||||
import { Theme } from '../../../components/ui/styleUtils';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { BindingVcWarningOverlay } from './BindingVcWarningOverlay';
|
||||
import { OtpVerificationModal } from './OtpVerificationModal';
|
||||
import { MessageOverlay } from '../../../components/MessageOverlay';
|
||||
import { useKebabPopUp } from '../../../components/KebabPopUpController';
|
||||
import { Dimensions } from 'react-native';
|
||||
import { ActorRefFrom } from 'xstate';
|
||||
import { vcItemMachine } from '../../../machines/vcItem';
|
||||
|
||||
export const WalletBinding: React.FC<WalletBindingProps> = (props) => {
|
||||
const controller = useKebabPopUp(props);
|
||||
|
||||
const WalletVerified: React.FC = () => {
|
||||
return (
|
||||
<Icon
|
||||
name="verified-user"
|
||||
color={Theme.Colors.VerifiedIcon}
|
||||
size={28}
|
||||
containerStyle={{ marginStart: 4, bottom: 1 }}
|
||||
/>
|
||||
);
|
||||
};
|
||||
const { t } = useTranslation('WalletBinding');
|
||||
|
||||
return controller.emptyWalletBindingId ? (
|
||||
<ListItem bottomDivider onPress={controller.ADD_WALLET_BINDING_ID}>
|
||||
{props.Icon && (
|
||||
<Icon
|
||||
name={props.Icon}
|
||||
type="font-awesome"
|
||||
size={20}
|
||||
style={Theme.Styles.profileIconBg}
|
||||
color={Theme.Colors.Icon}
|
||||
/>
|
||||
)}
|
||||
<ListItem.Content>
|
||||
<ListItem.Title>
|
||||
<Text weight="bold" size="small">
|
||||
{props.label}
|
||||
</Text>
|
||||
</ListItem.Title>
|
||||
<Text
|
||||
weight="semibold"
|
||||
color={Theme.Colors.walletbindingContent}
|
||||
size="smaller">
|
||||
{props.content}
|
||||
</Text>
|
||||
</ListItem.Content>
|
||||
|
||||
<BindingVcWarningOverlay
|
||||
isVisible={controller.isBindingWarning}
|
||||
onConfirm={controller.CONFIRM}
|
||||
onCancel={controller.CANCEL}
|
||||
/>
|
||||
|
||||
<OtpVerificationModal
|
||||
isVisible={controller.isAcceptingOtpInput}
|
||||
onDismiss={controller.DISMISS}
|
||||
onInputDone={controller.INPUT_OTP}
|
||||
error={controller.otpError}
|
||||
/>
|
||||
<MessageOverlay
|
||||
isVisible={controller.isWalletBindingError}
|
||||
title={controller.walletBindingError}
|
||||
onCancel={controller.CANCEL}
|
||||
/>
|
||||
<MessageOverlay
|
||||
isVisible={controller.WalletBindingInProgress}
|
||||
title={t('inProgress')}
|
||||
progress
|
||||
/>
|
||||
</ListItem>
|
||||
) : (
|
||||
<ListItem bottomDivider>
|
||||
<Row
|
||||
width={Dimensions.get('screen').width * 0.8}
|
||||
align="space-between"
|
||||
crossAlign="center">
|
||||
<Row crossAlign="center" style={{ flex: 1 }}>
|
||||
<WalletVerified />
|
||||
<Text
|
||||
color={Theme.Colors.Details}
|
||||
weight="bold"
|
||||
size="small"
|
||||
margin="10 10 10 10"
|
||||
children={t('profileAuthenticated')}></Text>
|
||||
</Row>
|
||||
</Row>
|
||||
</ListItem>
|
||||
);
|
||||
};
|
||||
|
||||
interface WalletBindingProps {
|
||||
label: string;
|
||||
content?: string;
|
||||
Icon?: string;
|
||||
service: ActorRefFrom<typeof vcItemMachine>;
|
||||
}
|
||||
86
screens/Home/MyVcs/WalletBindingController.ts
Normal file
@@ -0,0 +1,86 @@
|
||||
import { useSelector, useInterpret } from '@xstate/react';
|
||||
import { useContext, useRef, useState } from 'react';
|
||||
import { GlobalContext } from '../../../shared/GlobalContext';
|
||||
import { selectMyVcs, VcEvents } from '../../../machines/vc';
|
||||
import {
|
||||
createVcItemMachine,
|
||||
isShowingBindingWarning,
|
||||
selectAcceptingBindingOtp,
|
||||
isWalletBindingInProgress,
|
||||
VcItemEvents,
|
||||
selectIsAcceptingOtpInput,
|
||||
selectOtpError,
|
||||
selectShowWalletBindingError,
|
||||
selectWalletBindingError,
|
||||
} from '../../../machines/vcItem';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { ActorRefFrom } from 'xstate';
|
||||
|
||||
export function useWalletBinding(props) {
|
||||
const { t } = useTranslation('ProfileScreen');
|
||||
const { appService } = useContext(GlobalContext);
|
||||
|
||||
const machine = useRef(
|
||||
createVcItemMachine(
|
||||
appService.getSnapshot().context.serviceRefs,
|
||||
props.vcKey
|
||||
)
|
||||
);
|
||||
|
||||
const bindingService = useInterpret(machine.current, { devTools: __DEV__ });
|
||||
|
||||
const vcService = appService.children.get('vc');
|
||||
|
||||
const vcKeys = useSelector(vcService, selectMyVcs);
|
||||
|
||||
const otpError = useSelector(bindingService, selectOtpError);
|
||||
const [isRevoking, setRevoking] = useState(false);
|
||||
const [isAuthenticating, setAuthenticating] = useState(false);
|
||||
const [isViewing, setIsViewing] = useState(false);
|
||||
const [isBindingWarning, setisBindingWarning] = useState(false);
|
||||
const [toastVisible, setToastVisible] = useState(false);
|
||||
const [message, setMessage] = useState('');
|
||||
const [selectedIndex, setSelectedIndex] = useState<number>(null);
|
||||
const [selectedVidKeys, setSelectedVidKeys] = useState<string[]>([]);
|
||||
|
||||
const selectVcItem = (index: number, vcKey: string) => {
|
||||
return () => {
|
||||
setSelectedIndex(index);
|
||||
};
|
||||
};
|
||||
|
||||
const WalletBindingInProgress = useSelector(
|
||||
bindingService,
|
||||
isWalletBindingInProgress
|
||||
);
|
||||
|
||||
return {
|
||||
isBindingWarning,
|
||||
setisBindingWarning,
|
||||
otpError,
|
||||
message,
|
||||
toastVisible,
|
||||
WalletBindingInProgress,
|
||||
|
||||
isAcceptingOtpInput: useSelector(bindingService, selectIsAcceptingOtpInput),
|
||||
isAcceptingBindingOtp: useSelector(
|
||||
bindingService,
|
||||
selectAcceptingBindingOtp
|
||||
),
|
||||
isBindingError: useSelector(bindingService, selectShowWalletBindingError),
|
||||
walletBindingError: useSelector(bindingService, selectWalletBindingError),
|
||||
|
||||
DISMISS: () => bindingService.send(VcItemEvents.DISMISS()),
|
||||
|
||||
CONFIRM: () => bindingService.send(VcItemEvents.CONFIRM()),
|
||||
|
||||
CANCEL: () => bindingService.send(VcItemEvents.CANCEL()),
|
||||
|
||||
REFRESH: () => vcService.send(VcEvents.REFRESH_MY_VCS()),
|
||||
setAuthenticating,
|
||||
selectVcItem,
|
||||
setIsViewing,
|
||||
setRevoking,
|
||||
};
|
||||
}
|
||||
@@ -1,17 +1,15 @@
|
||||
import React from 'react';
|
||||
import { Button, Column, Text, Centered } from '../../components/ui';
|
||||
import { Icon } from 'react-native-elements';
|
||||
import { Button, Column, Row, Text } from '../../components/ui';
|
||||
import { Theme } from '../../components/ui/styleUtils';
|
||||
import { RefreshControl } from 'react-native';
|
||||
import { RefreshControl, Image, View } 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 { VcItem } from '../../components/VcItem';
|
||||
import { GET_INDIVIDUAL_ID } from '../../shared/constants';
|
||||
import { Icon } from 'react-native-elements';
|
||||
|
||||
export const MyVcsTab: React.FC<HomeScreenTabProps> = (props) => {
|
||||
const { t } = useTranslation('MyVcsTab');
|
||||
@@ -26,68 +24,114 @@ export const MyVcsTab: React.FC<HomeScreenTabProps> = (props) => {
|
||||
GET_INDIVIDUAL_ID('');
|
||||
};
|
||||
|
||||
{
|
||||
controller.isRequestSuccessful
|
||||
? setTimeout(() => {
|
||||
controller.DISMISS();
|
||||
}, 6000)
|
||||
: null;
|
||||
}
|
||||
|
||||
const DownloadingIdPopUp: React.FC = () => {
|
||||
return (
|
||||
<View
|
||||
style={{ display: controller.isRequestSuccessful ? 'flex' : 'none' }}>
|
||||
<Row style={Theme.Styles.popUp}>
|
||||
<Text color={Theme.Colors.whiteText} weight="semibold" size="smaller">
|
||||
{t('downloadingYourId')}
|
||||
</Text>
|
||||
<Icon
|
||||
name="close"
|
||||
onPress={() => {
|
||||
controller.DISMISS();
|
||||
clearIndividualId();
|
||||
}}
|
||||
color={Theme.Colors.whiteText}
|
||||
size={19}
|
||||
/>
|
||||
</Row>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<Column fill style={{ display: props.isVisible ? 'flex' : 'none' }}>
|
||||
<Column fill pY={32} pX={24}>
|
||||
<DownloadingIdPopUp />
|
||||
<Column fill pY={10} pX={18}>
|
||||
{controller.vcKeys.length > 0 && (
|
||||
<React.Fragment>
|
||||
<Column
|
||||
scroll
|
||||
margin="0 0 20 0"
|
||||
backgroundColor={Theme.Colors.lightGreyBackgroundColor}
|
||||
refreshControl={
|
||||
<RefreshControl
|
||||
refreshing={controller.isRefreshingVcs}
|
||||
onRefresh={controller.REFRESH}
|
||||
/>
|
||||
}>
|
||||
{controller.vcKeys.map((vcKey, index) => (
|
||||
<VcItem
|
||||
key={`${vcKey}-${index}`}
|
||||
vcKey={vcKey}
|
||||
margin="0 2 8 2"
|
||||
onPress={controller.VIEW_VC}
|
||||
/>
|
||||
))}
|
||||
</Column>
|
||||
<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}
|
||||
/>
|
||||
{controller.vcKeys.map((vcKey, index) => {
|
||||
if (vcKey.split(':')[4] === 'true') {
|
||||
return (
|
||||
<VcItem
|
||||
key={`${vcKey}-${index}`}
|
||||
vcKey={vcKey}
|
||||
margin="0 2 8 2"
|
||||
onPress={controller.VIEW_VC}
|
||||
iconName="pushpin"
|
||||
iconType="antdesign"
|
||||
/>
|
||||
);
|
||||
}
|
||||
})}
|
||||
{controller.vcKeys.map((vcKey, index) => {
|
||||
if (vcKey.split(':')[4] === 'false') {
|
||||
return (
|
||||
<VcItem
|
||||
key={`${vcKey}-${index}`}
|
||||
vcKey={vcKey}
|
||||
margin="0 2 8 2"
|
||||
onPress={controller.VIEW_VC}
|
||||
/>
|
||||
);
|
||||
}
|
||||
})}
|
||||
</Column>
|
||||
<Button
|
||||
type="gradient"
|
||||
isVcThere
|
||||
disabled={controller.isRefreshingVcs}
|
||||
title={t('downloadID')}
|
||||
onPress={controller.DOWNLOAD_ID}
|
||||
/>
|
||||
</React.Fragment>
|
||||
)}
|
||||
{controller.vcKeys.length === 0 && (
|
||||
<React.Fragment>
|
||||
<Centered fill>
|
||||
<Text weight="semibold" margin="0 0 8 0">
|
||||
{t('generateVc', { vcLabel: controller.vcLabel.plural })}
|
||||
<Column fill style={Theme.Styles.homeScreenContainer}>
|
||||
<Image source={Theme.DigitalIdentityLogo} />
|
||||
<Text weight="bold" margin="33 0 6 0" lineHeight={1}>
|
||||
{t('bringYourDigitalID', {
|
||||
vcLabel: controller.vcLabel.plural,
|
||||
})}
|
||||
</Text>
|
||||
<Text color={Theme.Colors.textLabel} align="center">
|
||||
<Text
|
||||
style={Theme.TextStyles.bold}
|
||||
color={Theme.Colors.textLabel}
|
||||
align="center"
|
||||
margin="0 12 30 12">
|
||||
{t('generateVcDescription', {
|
||||
vcLabel: controller.vcLabel.singular,
|
||||
})}
|
||||
</Text>
|
||||
<Icon
|
||||
name="arrow-downward"
|
||||
containerStyle={{ marginTop: 20 }}
|
||||
color={Theme.Colors.Icon}
|
||||
<Button
|
||||
type="gradient"
|
||||
disabled={controller.isRefreshingVcs}
|
||||
title={t('downloadID')}
|
||||
onPress={controller.DOWNLOAD_ID}
|
||||
/>
|
||||
</Centered>
|
||||
|
||||
<Button
|
||||
type="addId"
|
||||
disabled={controller.isRefreshingVcs}
|
||||
title={t('addVcButton', {
|
||||
vcLabel: controller.vcLabel.singular,
|
||||
})}
|
||||
onPress={controller.ADD_VC}
|
||||
/>
|
||||
</Column>
|
||||
</React.Fragment>
|
||||
)}
|
||||
</Column>
|
||||
@@ -100,20 +144,6 @@ export const MyVcsTab: React.FC<HomeScreenTabProps> = (props) => {
|
||||
{controller.GetVcModalService && (
|
||||
<GetVcModal service={controller.GetVcModalService} />
|
||||
)}
|
||||
|
||||
{controller.isRequestSuccessful && (
|
||||
<DownloadingVcModal
|
||||
isVisible={controller.isRequestSuccessful}
|
||||
onDismiss={controller.DISMISS}
|
||||
onShow={clearIndividualId}
|
||||
/>
|
||||
)}
|
||||
|
||||
<OnboardingOverlay
|
||||
isVisible={controller.isOnboarding}
|
||||
onDone={controller.ONBOARDING_DONE}
|
||||
onAddVc={controller.ADD_VC}
|
||||
/>
|
||||
</React.Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -39,7 +39,7 @@ export function useMyVcsTab(props: HomeScreenTabProps) {
|
||||
|
||||
DISMISS: () => service.send(MyVcsTabEvents.DISMISS()),
|
||||
|
||||
ADD_VC: () => service.send(MyVcsTabEvents.ADD_VC()),
|
||||
DOWNLOAD_ID: () => service.send(MyVcsTabEvents.ADD_VC()),
|
||||
|
||||
GET_VC: () => service.send(MyVcsTabEvents.GET_VC()),
|
||||
|
||||
|
||||
@@ -18,9 +18,9 @@ export interface Typegen0 {
|
||||
'invokeSrcNameMap': {};
|
||||
'missingImplementations': {
|
||||
actions: never;
|
||||
services: never;
|
||||
guards: never;
|
||||
delays: never;
|
||||
guards: never;
|
||||
services: 'AddVcModal' | 'GetVcModal';
|
||||
};
|
||||
'eventsCausingActions': {
|
||||
completeOnboarding: 'ADD_VC' | 'ONBOARDING_DONE';
|
||||
@@ -29,11 +29,14 @@ export interface Typegen0 {
|
||||
storeVcItem: 'done.invoke.AddVcModal';
|
||||
viewVcFromParent: 'VIEW_VC';
|
||||
};
|
||||
'eventsCausingServices': {};
|
||||
'eventsCausingDelays': {};
|
||||
'eventsCausingGuards': {
|
||||
isOnboardingDone: 'STORE_RESPONSE';
|
||||
};
|
||||
'eventsCausingDelays': {};
|
||||
'eventsCausingServices': {
|
||||
AddVcModal: 'ADD_VC' | 'done.invoke.GetVcModal';
|
||||
GetVcModal: 'GET_VC';
|
||||
};
|
||||
'matchesStates':
|
||||
| 'addingVc'
|
||||
| 'addingVc.addVcSuccessful'
|
||||
|
||||
@@ -10,7 +10,7 @@ import { OIDcAuthenticationModal } from '../../components/OIDcAuth';
|
||||
import { useViewVcModal, ViewVcModalProps } from './ViewVcModalController';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { VcDetails } from '../../components/VcDetails';
|
||||
import { OtpVerification } from './MyVcs/OtpVerification';
|
||||
import { OtpVerificationModal } from './MyVcs/OtpVerificationModal';
|
||||
import { BindingVcWarningOverlay } from './MyVcs/BindingVcWarningOverlay';
|
||||
|
||||
export const ViewVcModal: React.FC<ViewVcModalProps> = (props) => {
|
||||
@@ -22,12 +22,12 @@ export const ViewVcModal: React.FC<ViewVcModalProps> = (props) => {
|
||||
idType: 'VID',
|
||||
label: t('revoke'),
|
||||
icon: 'close-circle-outline',
|
||||
onPress: () => controller.CONFIRM_REVOKE_VC(),
|
||||
onPress: controller.CONFIRM_REVOKE_VC,
|
||||
},
|
||||
{
|
||||
label: t('editTag'),
|
||||
icon: 'pencil',
|
||||
onPress: () => controller.EDIT_TAG(),
|
||||
onPress: controller.EDIT_TAG,
|
||||
},
|
||||
];
|
||||
|
||||
@@ -35,19 +35,8 @@ export const ViewVcModal: React.FC<ViewVcModalProps> = (props) => {
|
||||
<Modal
|
||||
isVisible={props.isVisible}
|
||||
onDismiss={props.onDismiss}
|
||||
headerTitle={
|
||||
controller.vc.verifiableCredential.credentialSubject.UIN
|
||||
? controller.vc.verifiableCredential.credentialSubject.UIN
|
||||
: controller.vc.verifiableCredential.credentialSubject.VID
|
||||
}
|
||||
headerElevation={2}
|
||||
headerRight={
|
||||
<DropdownIcon
|
||||
icon="dots-vertical"
|
||||
idType={controller.vc.idType}
|
||||
items={DATA}
|
||||
/>
|
||||
}>
|
||||
headerTitle={t('title')}
|
||||
headerElevation={2}>
|
||||
<Column scroll>
|
||||
<Column fill>
|
||||
<VcDetails
|
||||
@@ -87,7 +76,7 @@ export const ViewVcModal: React.FC<ViewVcModalProps> = (props) => {
|
||||
)}
|
||||
|
||||
{controller.isAcceptingOtpInput && (
|
||||
<OtpVerification
|
||||
<OtpVerificationModal
|
||||
isVisible={controller.isAcceptingOtpInput}
|
||||
onDismiss={controller.DISMISS}
|
||||
onInputDone={controller.inputOtp}
|
||||
@@ -96,7 +85,7 @@ export const ViewVcModal: React.FC<ViewVcModalProps> = (props) => {
|
||||
)}
|
||||
|
||||
{controller.isAcceptingBindingOtp && (
|
||||
<OtpVerification
|
||||
<OtpVerificationModal
|
||||
isVisible={controller.isAcceptingBindingOtp}
|
||||
onDismiss={controller.DISMISS}
|
||||
onInputDone={controller.inputOtp}
|
||||
|
||||
@@ -17,12 +17,12 @@ import {
|
||||
VcItemEvents,
|
||||
vcItemMachine,
|
||||
selectWalletBindingError,
|
||||
selectIsRequestBindingOtp,
|
||||
selectRequestBindingOtp,
|
||||
selectAcceptingBindingOtp,
|
||||
selectEmptyWalletBindingId,
|
||||
isWalletBindingInProgress,
|
||||
selectWalletBindingInProgress,
|
||||
selectShowWalletBindingError,
|
||||
isShowingBindingWarning,
|
||||
selectBindingWarning,
|
||||
} from '../../machines/vcItem';
|
||||
import { selectPasscode } from '../../machines/auth';
|
||||
import { biometricsMachine, selectIsSuccess } from '../../machines/biometrics';
|
||||
@@ -130,7 +130,7 @@ export function useViewVcModal({
|
||||
selectIsAcceptingRevokeInput
|
||||
),
|
||||
storedPasscode: useSelector(authService, selectPasscode),
|
||||
isBindingOtp: useSelector(vcItemActor, selectIsRequestBindingOtp),
|
||||
isBindingOtp: useSelector(vcItemActor, selectRequestBindingOtp),
|
||||
isAcceptingBindingOtp: useSelector(vcItemActor, selectAcceptingBindingOtp),
|
||||
walletBindingError: useSelector(vcItemActor, selectWalletBindingError),
|
||||
isWalletBindingPending: useSelector(
|
||||
@@ -139,10 +139,10 @@ export function useViewVcModal({
|
||||
),
|
||||
isWalletBindingInProgress: useSelector(
|
||||
vcItemActor,
|
||||
isWalletBindingInProgress
|
||||
selectWalletBindingInProgress
|
||||
),
|
||||
isBindingError: useSelector(vcItemActor, selectShowWalletBindingError),
|
||||
isBindingWarning: useSelector(vcItemActor, isShowingBindingWarning),
|
||||
isBindingWarning: useSelector(vcItemActor, selectBindingWarning),
|
||||
|
||||
CONFIRM_REVOKE_VC: () => {
|
||||
setRevoking(true);
|
||||
|
||||
@@ -8,6 +8,9 @@ import { mainRoutes } from '../routes/main';
|
||||
import { RootRouteProps } from '../routes';
|
||||
import { Theme } from '../components/ui/styleUtils';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Row } from '../components/ui';
|
||||
import { Image, Pressable } from 'react-native';
|
||||
import { SettingScreen } from './Settings/SettingScreen';
|
||||
|
||||
const { Navigator, Screen } = createBottomTabNavigator();
|
||||
|
||||
@@ -15,17 +18,63 @@ export const MainLayout: React.FC<RootRouteProps> = () => {
|
||||
const { t } = useTranslation('MainLayout');
|
||||
|
||||
const options: BottomTabNavigationOptions = {
|
||||
headerLeft: () => <Icon name="notifications" color={Theme.Colors.Icon} />,
|
||||
headerLeftContainerStyle: { paddingStart: 16 },
|
||||
headerRightContainerStyle: { paddingEnd: 16 },
|
||||
headerTitleAlign: 'center',
|
||||
tabBarShowLabel: false,
|
||||
headerRight: () => (
|
||||
<Row align="space-between">
|
||||
<Pressable
|
||||
onPress={() => {
|
||||
console.log('Help Page');
|
||||
}}>
|
||||
<Image
|
||||
source={require('../assets/help-icon.png')}
|
||||
style={{ width: 36, height: 36 }}
|
||||
/>
|
||||
</Pressable>
|
||||
|
||||
<SettingScreen
|
||||
triggerComponent={
|
||||
<Icon
|
||||
name="settings"
|
||||
type="simple-line-icon"
|
||||
size={21}
|
||||
style={Theme.Styles.IconContainer}
|
||||
color={Theme.Colors.Icon}
|
||||
/>
|
||||
}
|
||||
navigation={undefined}
|
||||
route={undefined}
|
||||
/>
|
||||
</Row>
|
||||
),
|
||||
headerTitleStyle: {
|
||||
fontFamily: 'Inter_600SemiBold',
|
||||
fontSize: 30,
|
||||
margin: 8,
|
||||
},
|
||||
headerRightContainerStyle: { paddingEnd: 13 },
|
||||
headerLeftContainerStyle: { paddingEnd: 13 },
|
||||
tabBarShowLabel: true,
|
||||
tabBarLabelStyle: {
|
||||
fontSize: 12,
|
||||
color: Theme.Colors.IconBg,
|
||||
fontFamily: 'Inter_600SemiBold',
|
||||
fontSize: 30,
|
||||
margin: 8,
|
||||
},
|
||||
headerRightContainerStyle: { paddingEnd: 13 },
|
||||
headerLeftContainerStyle: { paddingEnd: 13 },
|
||||
tabBarShowLabel: true,
|
||||
tabBarActiveTintColor: Theme.Colors.IconBg,
|
||||
tabBarStyle: {
|
||||
height: 86,
|
||||
paddingHorizontal: 36,
|
||||
height: 82,
|
||||
paddingHorizontal: 10,
|
||||
},
|
||||
tabBarLabelStyle: {
|
||||
fontSize: 12,
|
||||
fontFamily: 'Inter_600SemiBold',
|
||||
},
|
||||
tabBarItemStyle: {
|
||||
height: 86,
|
||||
height: 83,
|
||||
padding: 11,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -38,12 +87,12 @@ export const MainLayout: React.FC<RootRouteProps> = () => {
|
||||
component={route.component}
|
||||
options={{
|
||||
...route.options,
|
||||
title: t(route.name.toLowerCase()).toUpperCase(),
|
||||
title: t(route.name),
|
||||
tabBarIcon: ({ focused }) => (
|
||||
<Icon
|
||||
name={route.icon}
|
||||
color={focused ? Theme.Colors.IconBg : Theme.Colors.Icon}
|
||||
reverse={focused}
|
||||
color={focused ? Theme.Colors.Icon : Theme.Colors.GrayIcon}
|
||||
style={focused ? Theme.Styles.bottomTabIconStyle : null}
|
||||
/>
|
||||
),
|
||||
}}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Icon } from 'react-native-elements';
|
||||
import { Image } from 'react-native';
|
||||
import { MAX_PIN, PasscodeVerify } from '../components/PasscodeVerify';
|
||||
import { PinInput } from '../components/PinInput';
|
||||
import { Column, Text } from '../components/ui';
|
||||
@@ -15,12 +15,27 @@ export const PasscodeScreen: React.FC<PasscodeRouteProps> = (props) => {
|
||||
const passcodeSetup =
|
||||
controller.passcode === '' ? (
|
||||
<React.Fragment>
|
||||
<Text align="center">{t('header')}</Text>
|
||||
<Column>
|
||||
<Text align="center" style={Theme.TextStyles.header}>
|
||||
{t('header')}
|
||||
</Text>
|
||||
<Text align="center" weight="semibold" color={Theme.Colors.GrayText}>
|
||||
{t('description')}
|
||||
</Text>
|
||||
</Column>
|
||||
|
||||
<PinInput length={MAX_PIN} onDone={controller.setPasscode} />
|
||||
</React.Fragment>
|
||||
) : (
|
||||
<React.Fragment>
|
||||
<Text align="center">{t('confirmPasscode')}</Text>
|
||||
<Column>
|
||||
<Text align="center" style={Theme.TextStyles.header}>
|
||||
{t('confirmPasscode')}
|
||||
</Text>
|
||||
<Text align="center" weight="semibold" color={Theme.Colors.GrayText}>
|
||||
{t('description')}
|
||||
</Text>
|
||||
</Column>
|
||||
<PasscodeVerify
|
||||
onSuccess={controller.SETUP_PASSCODE}
|
||||
onError={controller.setError}
|
||||
@@ -34,9 +49,9 @@ export const PasscodeScreen: React.FC<PasscodeRouteProps> = (props) => {
|
||||
fill
|
||||
padding="32"
|
||||
backgroundColor={Theme.Colors.whiteBackgroundColor}>
|
||||
<Icon name="lock" color={Theme.Colors.Icon} size={60} />
|
||||
<Image source={Theme.LockIcon} style={{ alignSelf: 'center' }} />
|
||||
{props.route.params?.setup ? (
|
||||
<Column fill align="space-between" width="100%">
|
||||
<Column fill align="space-around" width="100%">
|
||||
{passcodeSetup}
|
||||
</Column>
|
||||
) : (
|
||||
|
||||
@@ -77,8 +77,8 @@ export const ProfileScreen: React.FC<MainRouteProps> = (props) => {
|
||||
<Icon
|
||||
name="fingerprint"
|
||||
type="fontawesome"
|
||||
size={20}
|
||||
style={Theme.Styles.profileIconBg}
|
||||
size={25}
|
||||
style={Theme.Styles.IconContainer}
|
||||
color={Theme.Colors.Icon}
|
||||
/>
|
||||
<ListItem.Content>
|
||||
@@ -105,7 +105,7 @@ export const ProfileScreen: React.FC<MainRouteProps> = (props) => {
|
||||
name="unlock"
|
||||
size={20}
|
||||
type="antdesign"
|
||||
style={Theme.Styles.profileIconBg}
|
||||
style={Theme.Styles.IconContainer}
|
||||
color={Theme.Colors.Icon}
|
||||
/>
|
||||
<ListItem.Content>
|
||||
@@ -125,7 +125,7 @@ export const ProfileScreen: React.FC<MainRouteProps> = (props) => {
|
||||
name="logout"
|
||||
type="fontawesome"
|
||||
size={20}
|
||||
style={Theme.Styles.profileIconBg}
|
||||
style={Theme.Styles.IconContainer}
|
||||
color={Theme.Colors.Icon}
|
||||
/>
|
||||
<ListItem.Content>
|
||||
|
||||
@@ -5,11 +5,11 @@ import { Icon } from 'react-native-elements';
|
||||
|
||||
import { Theme } from '../../components/ui/styleUtils';
|
||||
import { SendVcScreen } from './SendVcScreen';
|
||||
import { MessageOverlay } from '../../components/MessageOverlay';
|
||||
import { useScanLayout } from './ScanLayoutController';
|
||||
import { ScanScreen } from './ScanScreen';
|
||||
import { I18nManager, Platform } from 'react-native';
|
||||
import { Message } from '../../components/Message';
|
||||
import { ProgressingModal } from '../../components/ProgressingModal';
|
||||
import { MessageOverlay } from '../../components/MessageOverlay';
|
||||
|
||||
const ScanStack = createNativeStackNavigator();
|
||||
|
||||
@@ -22,14 +22,21 @@ export const ScanLayout: React.FC = () => {
|
||||
<ScanStack.Navigator
|
||||
initialRouteName="ScanScreen"
|
||||
screenOptions={{
|
||||
headerTitleAlign: 'center',
|
||||
headerLeft: () =>
|
||||
I18nManager.isRTL && Platform.OS !== 'ios' ? (
|
||||
<LanguageSelector
|
||||
triggerComponent={
|
||||
<Icon name="language" color={Theme.Colors.Icon} />
|
||||
}
|
||||
/>
|
||||
) : null,
|
||||
}}>
|
||||
{!controller.isDone && (
|
||||
<ScanStack.Screen
|
||||
name="SendVcScreen"
|
||||
component={SendVcScreen}
|
||||
options={{
|
||||
title: t('sharingVc', {
|
||||
title: t('requester', {
|
||||
vcLabel: controller.vcLabel.singular,
|
||||
}),
|
||||
headerBackVisible: false,
|
||||
@@ -40,23 +47,29 @@ export const ScanLayout: React.FC = () => {
|
||||
name="ScanScreen"
|
||||
component={ScanScreen}
|
||||
options={{
|
||||
title: t('MainLayout:scan').toUpperCase(),
|
||||
headerTitleStyle: { fontSize: 30, fontFamily: 'Inter_600SemiBold' },
|
||||
title: t('MainLayout:scan'),
|
||||
}}
|
||||
/>
|
||||
</ScanStack.Navigator>
|
||||
|
||||
<MessageOverlay
|
||||
<ProgressingModal
|
||||
isVisible={controller.statusOverlay != null}
|
||||
title={controller.statusOverlay?.title}
|
||||
message={controller.statusOverlay?.message}
|
||||
hint={controller.statusOverlay?.hint}
|
||||
timeoutHint={controller.statusOverlay?.hint}
|
||||
isVisible={controller.statusOverlay != null}
|
||||
title={controller.statusOverlay?.title}
|
||||
timeoutHint={controller.statusOverlay?.hint}
|
||||
label={controller.statusOverlay?.message}
|
||||
onCancel={controller.statusOverlay?.onCancel}
|
||||
progress={controller.statusOverlay?.progress}
|
||||
onBackdropPress={controller.statusOverlay?.onBackdropPress}
|
||||
requester={controller.statusOverlay?.requester}
|
||||
/>
|
||||
|
||||
{controller.isDisconnected && (
|
||||
<Message
|
||||
<MessageOverlay
|
||||
isVisible={controller.isDisconnected}
|
||||
title={t('RequestScreen:status.disconnected.title')}
|
||||
message={t('RequestScreen:status.disconnected.message')}
|
||||
onBackdropPress={controller.DISMISS}
|
||||
|
||||
@@ -93,16 +93,24 @@ export function useScanLayout() {
|
||||
const onCancel = () => scanService.send(ScanEvents.CANCEL());
|
||||
let statusOverlay: Pick<
|
||||
MessageOverlayProps,
|
||||
'title' | 'message' | 'hint' | 'onCancel' | 'progress' | 'onBackdropPress'
|
||||
| 'title'
|
||||
| 'message'
|
||||
| 'hint'
|
||||
| 'onCancel'
|
||||
| 'progress'
|
||||
| 'onBackdropPress'
|
||||
| 'requester'
|
||||
> = null;
|
||||
if (isConnecting) {
|
||||
statusOverlay = {
|
||||
message: t('status.connecting'),
|
||||
title: t('status.inProgress'),
|
||||
message: t('status.establishingConnection'),
|
||||
progress: true,
|
||||
};
|
||||
} else if (isConnectingTimeout) {
|
||||
statusOverlay = {
|
||||
message: t('status.connecting'),
|
||||
title: t('status.sharingInProgress'),
|
||||
requester: t('status.sharingInProgress'),
|
||||
hint: t('status.connectingTimeout'),
|
||||
onCancel,
|
||||
progress: true,
|
||||
|
||||
@@ -93,13 +93,11 @@ export const ScanScreen: React.FC = () => {
|
||||
<Column
|
||||
fill
|
||||
padding="24 0"
|
||||
backgroundColor={Theme.Colors.lightGreyBackgroundColor}>
|
||||
backgroundColor={Theme.Colors.whiteBackgroundColor}>
|
||||
<Centered
|
||||
fill
|
||||
align="space-evenly"
|
||||
backgroundColor={Theme.Colors.lightGreyBackgroundColor}>
|
||||
<Text align="center">{t('header')}</Text>
|
||||
|
||||
backgroundColor={Theme.Colors.whiteBackgroundColor}>
|
||||
{controller.isLocationDisabled || controller.isLocationDenied ? (
|
||||
<Column padding="24" fill align="space-between">
|
||||
<Centered fill>
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
import React, { useContext, useEffect, useRef } from 'react';
|
||||
import { CheckBox, Input } from 'react-native-elements';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useFocusEffect } from '@react-navigation/native';
|
||||
import { DeviceInfoList } from '../../components/DeviceInfoList';
|
||||
import { Button, Column, Row } from '../../components/ui';
|
||||
import { Button, Column, Row, Text } from '../../components/ui';
|
||||
import { Theme } from '../../components/ui/styleUtils';
|
||||
import { MessageOverlay } from '../../components/MessageOverlay';
|
||||
import { useSendVcScreen } from './SendVcScreenController';
|
||||
@@ -55,23 +53,50 @@ export const SendVcScreen: React.FC = () => {
|
||||
return (
|
||||
<React.Fragment>
|
||||
<Column fill backgroundColor={Theme.Colors.lightGreyBackgroundColor}>
|
||||
<Column padding="16 0" scroll>
|
||||
<DeviceInfoList of="receiver" deviceInfo={controller.receiverInfo} />
|
||||
|
||||
<Column padding="24">
|
||||
<Column>
|
||||
<Column
|
||||
padding="24 19 14 19"
|
||||
backgroundColor={Theme.Colors.whiteBackgroundColor}
|
||||
style={{ position: 'relative' }}>
|
||||
<Input
|
||||
value={controller.reason ? controller.reason : ''}
|
||||
placeholder={!controller.reason ? reasonLabel : ''}
|
||||
label={controller.reason ? reasonLabel : ''}
|
||||
labelStyle={{ textAlign: 'left' }}
|
||||
onChangeText={controller.UPDATE_REASON}
|
||||
containerStyle={{ marginBottom: 24 }}
|
||||
containerStyle={{ marginBottom: 6 }}
|
||||
inputStyle={{ textAlign: I18nManager.isRTL ? 'right' : 'left' }}
|
||||
selectionColor={Theme.Colors.Cursor}
|
||||
/>
|
||||
|
||||
<CheckBox
|
||||
containerStyle={Theme.SelectVcOverlayStyles.consentCheckContainer}
|
||||
title={t('consentToPhotoVerification')}
|
||||
checked={controller.selectedVc.shouldVerifyPresence}
|
||||
onPress={controller.TOGGLE_USER_CONSENT}
|
||||
/>
|
||||
</Column>
|
||||
<Column>
|
||||
{controller.vcKeys.map((vcKey, index) => (
|
||||
<Text
|
||||
margin="15 0 13 24"
|
||||
weight="bold"
|
||||
color={Theme.Colors.textValue}
|
||||
style={{ position: 'relative' }}>
|
||||
{t('pleaseSelectAnId')}
|
||||
</Text>
|
||||
</Column>
|
||||
<Column scroll>
|
||||
{controller.vcKeys.length === 1 && (
|
||||
<SingleVcItem
|
||||
key={controller.vcKeys[0]}
|
||||
vcKey={controller.vcKeys[0]}
|
||||
margin="0 2 8 2"
|
||||
onPress={controller.SELECT_VC_ITEM(0)}
|
||||
selectable
|
||||
selected={0 === controller.selectedIndex}
|
||||
/>
|
||||
)}
|
||||
|
||||
{controller.vcKeys.length > 1 &&
|
||||
controller.vcKeys.map((vcKey, index) => (
|
||||
<VcItem
|
||||
key={vcKey}
|
||||
vcKey={vcKey}
|
||||
@@ -79,27 +104,24 @@ export const SendVcScreen: React.FC = () => {
|
||||
onPress={controller.SELECT_VC_ITEM(index)}
|
||||
selectable
|
||||
selected={index === controller.selectedIndex}
|
||||
activeTab={'sharingVcScreen'}
|
||||
/>
|
||||
))}
|
||||
</Column>
|
||||
</Column>
|
||||
<Column
|
||||
backgroundColor={Theme.Colors.whiteBackgroundColor}
|
||||
padding="16 24"
|
||||
margin="2 0 0 0"
|
||||
elevation={2}>
|
||||
|
||||
<Column padding="12 0" elevation={2}>
|
||||
<Button
|
||||
title={t('acceptRequest')}
|
||||
margin="12 0 12 0"
|
||||
type="gradient"
|
||||
title={t('approveRequest', {
|
||||
vcLabel: controller.vcLabel.singular,
|
||||
})}
|
||||
disabled={controller.selectedIndex == null}
|
||||
onPress={controller.ACCEPT_REQUEST}
|
||||
/>
|
||||
{!controller.selectedVc.shouldVerifyPresence && (
|
||||
<Button
|
||||
type="outline"
|
||||
title={t('acceptRequestAndVerify')}
|
||||
margin="12 0 12 0"
|
||||
type="gradient"
|
||||
title={t('approveRequestAndVerify')}
|
||||
styles={{ marginTop: '12' }}
|
||||
disabled={controller.selectedIndex == null}
|
||||
onPress={controller.VERIFY_AND_ACCEPT_REQUEST}
|
||||
/>
|
||||
|
||||
57
screens/Scan/SuccessfullySharedModal.tsx
Normal file
@@ -0,0 +1,57 @@
|
||||
import React, { useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Theme } from '../../components/ui/styleUtils';
|
||||
import { Modal } from '../../components/ui/Modal';
|
||||
import { Image } from 'react-native';
|
||||
import { Centered, Column, Text } from '../../components/ui';
|
||||
import { DeviceInfoList } from '../../components/DeviceInfoList';
|
||||
import { Button } from '../../components/ui';
|
||||
import { useScanLayout } from './ScanLayoutController';
|
||||
import { useSendVcScreen } from './SendVcScreenController';
|
||||
|
||||
export const SharingSuccessModal: React.FC<SharingSuccessModalProps> = (
|
||||
props
|
||||
) => {
|
||||
const { t } = useTranslation('ScanScreen');
|
||||
const controller = useScanLayout();
|
||||
const controller1 = useSendVcScreen();
|
||||
|
||||
const [showSuccessMessage, setIsShowSuccessMessage] = useState(false);
|
||||
const toggle = () => setIsShowSuccessMessage(!showSuccessMessage);
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<Modal isVisible={controller.isDone}>
|
||||
<Column
|
||||
margin="64 0"
|
||||
crossAlign="center"
|
||||
style={Theme.SelectVcOverlayStyles.sharedSuccessfully}>
|
||||
<Image source={Theme.SuccessLogo} height={22} width={22} />
|
||||
<Text style={Theme.TextStyles.bold}>
|
||||
{t('ScanScreen:status.accepted.title')}
|
||||
</Text>
|
||||
<Text
|
||||
align="center"
|
||||
style={Theme.TextStyles.bold}
|
||||
color={Theme.Colors.profileValue}>
|
||||
{t('ScanScreen:status.accepted.message')}
|
||||
</Text>
|
||||
<Text style={Theme.TextStyles.bold}>
|
||||
<DeviceInfoList deviceInfo={controller1.receiverInfo} />
|
||||
</Text>
|
||||
</Column>
|
||||
<Column margin="0 0 0">
|
||||
<Button
|
||||
type="gradient"
|
||||
title={t('ScanScreen:status.accepted.gotohome')}
|
||||
onPress={controller.DISMISS}
|
||||
/>
|
||||
</Column>
|
||||
</Modal>
|
||||
</React.Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
interface SharingSuccessModalProps {
|
||||
isVisible: boolean;
|
||||
}
|
||||
@@ -1,14 +1,13 @@
|
||||
import React, { useState } from 'react';
|
||||
import Markdown from 'react-native-simple-markdown';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { I18nManager, Image, SafeAreaView, View } from 'react-native';
|
||||
import { I18nManager, SafeAreaView, View } from 'react-native';
|
||||
import { Divider, Icon, ListItem, Overlay } from 'react-native-elements';
|
||||
|
||||
import { Button, Text, Row, Column } from '../../components/ui';
|
||||
import { Button, Text, Row } from '../../components/ui';
|
||||
import { Theme } from '../../components/ui/styleUtils';
|
||||
import appMetaData from '../../AppMetaData.md';
|
||||
import { getVersion } from 'react-native-device-info';
|
||||
import { isBLEEnabled } from '../../lib/smartshare';
|
||||
|
||||
export const AppMetaData: React.FC<AppMetaDataProps> = (props) => {
|
||||
const { t } = useTranslation('AppMetaData');
|
||||
4
screens/Settings/Credits.strings.json
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"header": "Credits and legal notices",
|
||||
"back": "Back"
|
||||
}
|
||||