mirror of
https://github.com/mosip/inji-wallet.git
synced 2026-01-08 21:18:14 -05:00
Merge pull request #493 from MonobikashDas/qa-develop
merged latest develop code
This commit is contained in:
@@ -102,7 +102,7 @@ apply from: new File(["node", "--print", "require.resolve('react-native/package.
|
||||
* Upload all the APKs to the Play Store and people will download
|
||||
* the correct one based on the CPU architecture of their device.
|
||||
*/
|
||||
def enableSeparateBuildPerCPUArchitecture = false
|
||||
def enableSeparateBuildPerCPUArchitecture = true
|
||||
|
||||
/**
|
||||
* Run Proguard to shrink the Java bytecode in release builds.
|
||||
@@ -172,7 +172,7 @@ android {
|
||||
abi {
|
||||
reset()
|
||||
enable enableSeparateBuildPerCPUArchitecture
|
||||
universalApk false // If true, also generate a universal APK
|
||||
universalApk true // If true, also generate a universal APK
|
||||
include "armeabi-v7a", "x86", "arm64-v8a", "x86_64"
|
||||
}
|
||||
}
|
||||
@@ -240,9 +240,10 @@ android {
|
||||
}
|
||||
|
||||
android.applicationVariants.all { variant ->
|
||||
variant.outputs.all {
|
||||
variant.outputs.all { output ->
|
||||
def datetime = new Date().format('yyyyMMdd_HHmm')
|
||||
outputFileName = "${defaultConfig.applicationId}-${variant.versionName}_${datetime}.apk"
|
||||
def architecture = output.getFilter(com.android.build.OutputFile.ABI) ?: "universal"
|
||||
outputFileName = "${defaultConfig.applicationId}-${variant.versionName}_${datetime}_${architecture}.apk"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React, { useState } from 'react';
|
||||
import { Dimensions } from 'react-native';
|
||||
import { Dimensions, I18nManager } from 'react-native';
|
||||
import { Icon, ListItem, Overlay, Input } from 'react-native-elements';
|
||||
import { Text, Column, Row, Button } from './ui';
|
||||
import { Theme } from './ui/styleUtils';
|
||||
@@ -31,7 +31,14 @@ export const EditableListItem: React.FC<EditableListItemProps> = (props) => {
|
||||
onBackdropPress={dismiss}>
|
||||
<Column width={Dimensions.get('screen').width * 0.8}>
|
||||
<Text>{t('editLabel', { label: props.label })}</Text>
|
||||
<Input autoFocus value={newValue} onChangeText={setNewValue} />
|
||||
<Input
|
||||
autoFocus
|
||||
value={newValue}
|
||||
onChangeText={setNewValue}
|
||||
inputStyle={{
|
||||
textAlign: I18nManager.isRTL ? 'right' : 'left',
|
||||
}}
|
||||
/>
|
||||
<Row>
|
||||
<Button fill type="clear" title={t('cancel')} onPress={dismiss} />
|
||||
<Button fill title={t('save')} onPress={edit} />
|
||||
|
||||
@@ -75,7 +75,7 @@ const getDetails = (arg1, arg2, verifiableCredential) => {
|
||||
}
|
||||
return (
|
||||
<Column>
|
||||
<Text color={Theme.Colors.DetailsLabel} size="smaller" align="left">
|
||||
<Text color={Theme.Colors.DetailsLabel} size="smaller">
|
||||
{arg1}
|
||||
</Text>
|
||||
<Text
|
||||
|
||||
@@ -43,8 +43,7 @@ export const VcDetails: React.FC<VcDetailsProps> = (props) => {
|
||||
<Text
|
||||
weight="bold"
|
||||
size="smaller"
|
||||
color={Theme.Colors.DetailsLabel}
|
||||
align="left">
|
||||
color={Theme.Colors.DetailsLabel}>
|
||||
{t('idType')}
|
||||
</Text>
|
||||
<Text weight="bold" size="smaller" color={Theme.Colors.Details}>
|
||||
@@ -69,14 +68,12 @@ export const VcDetails: React.FC<VcDetailsProps> = (props) => {
|
||||
<Text
|
||||
weight="bold"
|
||||
size="smaller"
|
||||
align="left"
|
||||
color={Theme.Colors.DetailsLabel}>
|
||||
{t('fullName')}
|
||||
</Text>
|
||||
<Text
|
||||
weight="semibold"
|
||||
size="smaller"
|
||||
align="left"
|
||||
color={Theme.Colors.Details}>
|
||||
{getLocalizedField(
|
||||
props.vc?.verifiableCredential.credentialSubject.fullName
|
||||
@@ -144,7 +141,6 @@ export const VcDetails: React.FC<VcDetailsProps> = (props) => {
|
||||
<Text
|
||||
weight="semibold"
|
||||
size="smaller"
|
||||
align="left"
|
||||
color={Theme.Colors.Details}>
|
||||
{t('valid')}
|
||||
</Text>
|
||||
@@ -222,8 +218,7 @@ export const VcDetails: React.FC<VcDetailsProps> = (props) => {
|
||||
}
|
||||
weight="semibold"
|
||||
size="smaller"
|
||||
color={Theme.Colors.Details}
|
||||
align="left">
|
||||
color={Theme.Colors.Details}>
|
||||
{getLocalizedField(
|
||||
props.vc?.verifiableCredential.credentialSubject.email
|
||||
)}
|
||||
@@ -243,8 +238,7 @@ export const VcDetails: React.FC<VcDetailsProps> = (props) => {
|
||||
style={{ flex: 1 }}
|
||||
weight="semibold"
|
||||
size="smaller"
|
||||
color={Theme.Colors.Details}
|
||||
align="left">
|
||||
color={Theme.Colors.Details}>
|
||||
{getFullAddress(
|
||||
props.vc?.verifiableCredential.credentialSubject
|
||||
)}
|
||||
|
||||
@@ -38,7 +38,6 @@ const getDetails = (arg1, arg2, verifiableCredential) => {
|
||||
<Text
|
||||
weight="bold"
|
||||
size="smaller"
|
||||
align="left"
|
||||
color={
|
||||
!verifiableCredential
|
||||
? Theme.Colors.LoadingDetailsLabel
|
||||
@@ -73,8 +72,7 @@ const getDetails = (arg1, arg2, verifiableCredential) => {
|
||||
: Theme.Colors.DetailsLabel
|
||||
}
|
||||
weight="bold"
|
||||
size="smaller"
|
||||
align="left">
|
||||
size="smaller">
|
||||
{arg1}
|
||||
</Text>
|
||||
<Text
|
||||
@@ -82,7 +80,6 @@ const getDetails = (arg1, arg2, verifiableCredential) => {
|
||||
color={Theme.Colors.Details}
|
||||
weight="bold"
|
||||
size="smaller"
|
||||
align="left"
|
||||
style={
|
||||
!verifiableCredential
|
||||
? Theme.Styles.loadingTitle
|
||||
@@ -180,8 +177,7 @@ export const VcItem: React.FC<VcItemProps> = (props) => {
|
||||
: Theme.Colors.DetailsLabel
|
||||
}
|
||||
weight="bold"
|
||||
size="smaller"
|
||||
align="left">
|
||||
size="smaller">
|
||||
{t('idType')}
|
||||
</Text>
|
||||
<Text
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React from 'react';
|
||||
import { Modal as RNModal, View } from 'react-native';
|
||||
import { I18nManager, Modal as RNModal, View } from 'react-native';
|
||||
import { Icon } from 'react-native-elements';
|
||||
import { Column, Row, Text } from '.';
|
||||
import { ElevationLevel, Theme } from './styleUtils';
|
||||
@@ -24,7 +24,7 @@ export const Modal: React.FC<ModalProps> = (props) => {
|
||||
}}>
|
||||
{props.headerRight ? (
|
||||
<Icon
|
||||
name="chevron-left"
|
||||
name={I18nManager.isRTL ? 'chevron-right' : 'chevron-left'}
|
||||
onPress={props.onDismiss}
|
||||
color={Theme.Colors.Icon}
|
||||
/>
|
||||
|
||||
@@ -9,7 +9,7 @@ export const Text: React.FC<TextProps> = (props: TextProps) => {
|
||||
Theme.TextStyles.base,
|
||||
Theme.TextStyles[weight],
|
||||
props.color ? { color: props.color } : null,
|
||||
props.align ? { textAlign: props.align } : null,
|
||||
props.align ? { textAlign: props.align } : { textAlign: 'left' },
|
||||
props.margin ? Theme.spacing('margin', props.margin) : null,
|
||||
props.size ? Theme.TextStyles[props.size] : null,
|
||||
props.style ? props.style : null,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React from 'react';
|
||||
import { Modal as RNModal } from 'react-native';
|
||||
import { I18nManager, Modal as RNModal } from 'react-native';
|
||||
import { Icon } from 'react-native-elements';
|
||||
import { Column, Row } from '.';
|
||||
import { Theme, ElevationLevel } from './styleUtils';
|
||||
@@ -15,7 +15,7 @@ export const Modal: React.FC<ModalProps> = (props) => {
|
||||
<Row padding="16 32" elevation={props.headerElevation}>
|
||||
{props.headerRight ? (
|
||||
<Icon
|
||||
name="chevron-left"
|
||||
name={I18nManager.isRTL ? 'chevron-right' : 'chevron-left'}
|
||||
onPress={props.onDismiss}
|
||||
color={Theme.Colors.Icon}
|
||||
/>
|
||||
|
||||
@@ -652,6 +652,11 @@ export const DefaultTheme = {
|
||||
zIndex: 1,
|
||||
},
|
||||
}),
|
||||
claimsContainer: StyleSheet.create({
|
||||
container: {
|
||||
backgroundColor: Colors.Transparent,
|
||||
},
|
||||
}),
|
||||
OpenCard: require('../../../assets/ID-open.png'),
|
||||
CloseCard: require('../../../assets/ID-closed.png'),
|
||||
ProfileIcon: require('../../../assets/placeholder-photo.png'),
|
||||
|
||||
@@ -596,6 +596,11 @@ export const PurpleTheme = {
|
||||
borderTopRightRadius: 0,
|
||||
},
|
||||
}),
|
||||
claimsContainer: StyleSheet.create({
|
||||
container: {
|
||||
backgroundColor: Colors.Transparent,
|
||||
},
|
||||
}),
|
||||
OpenCard: require('../../../purpleAassets/bg_cart_one.png'),
|
||||
CloseCard: require('../../../purpleAassets/cart_unsel.png'),
|
||||
ProfileIcon: require('../../../purpleAassets/profile_icon_unsel.png'),
|
||||
|
||||
356
lib/react-native-elements/tooltip/Tooltip.tsx
Normal file
356
lib/react-native-elements/tooltip/Tooltip.tsx
Normal file
@@ -0,0 +1,356 @@
|
||||
import React, { Fragment } from 'react';
|
||||
import {
|
||||
TouchableOpacity,
|
||||
Modal,
|
||||
View,
|
||||
StatusBar,
|
||||
I18nManager,
|
||||
ViewStyle,
|
||||
FlexStyle,
|
||||
StyleProp,
|
||||
StyleSheet,
|
||||
ColorValue,
|
||||
Platform,
|
||||
Keyboard,
|
||||
} from 'react-native';
|
||||
import { withTheme } from 'react-native-elements/dist/config';
|
||||
import { ThemeProps } from 'react-native-elements/dist/config';
|
||||
import {
|
||||
ScreenWidth,
|
||||
ScreenHeight,
|
||||
isIOS,
|
||||
} from 'react-native-elements/dist/helpers';
|
||||
import Triangle from './Triangle';
|
||||
import getTooltipCoordinate, {
|
||||
getElementVisibleWidth,
|
||||
} from './getTooltipCoordinate';
|
||||
|
||||
export type TooltipProps = {
|
||||
withPointer?: boolean;
|
||||
popover?: React.ReactElement<{}>;
|
||||
toggleOnPress?: boolean;
|
||||
toggleAction?: string | 'onPress' | 'onLongPress';
|
||||
height?: FlexStyle['height'];
|
||||
width?: FlexStyle['width'];
|
||||
containerStyle?: StyleProp<ViewStyle>;
|
||||
pointerColor?: ColorValue;
|
||||
onClose?(): void;
|
||||
onOpen?(): void;
|
||||
overlayColor?: ColorValue;
|
||||
withOverlay?: boolean;
|
||||
backgroundColor?: ColorValue;
|
||||
highlightColor?: ColorValue;
|
||||
skipAndroidStatusBar?: boolean;
|
||||
ModalComponent?: typeof React.Component;
|
||||
closeOnlyOnBackdropPress?: boolean;
|
||||
} & typeof defaultProps;
|
||||
|
||||
const defaultProps = {
|
||||
withOverlay: true,
|
||||
overlayColor: 'rgba(250, 250, 250, 0.70)',
|
||||
highlightColor: 'transparent',
|
||||
withPointer: true,
|
||||
toggleOnPress: true,
|
||||
toggleAction: 'onPress',
|
||||
height: 40,
|
||||
width: 150,
|
||||
containerStyle: {},
|
||||
backgroundColor: '#617080',
|
||||
onClose: () => {},
|
||||
onOpen: () => {},
|
||||
skipAndroidStatusBar: false,
|
||||
ModalComponent: Modal,
|
||||
closeOnlyOnBackdropPress: false,
|
||||
};
|
||||
|
||||
type TooltipState = {
|
||||
isVisible: boolean;
|
||||
yOffset: number;
|
||||
xOffset: number;
|
||||
elementWidth: number;
|
||||
elementHeight: number;
|
||||
};
|
||||
|
||||
class Tooltip extends React.Component<
|
||||
TooltipProps & Partial<ThemeProps<TooltipProps>>,
|
||||
TooltipState
|
||||
> {
|
||||
static defaultProps = defaultProps;
|
||||
_isMounted: boolean = false;
|
||||
state = {
|
||||
isVisible: false,
|
||||
yOffset: 0,
|
||||
xOffset: 0,
|
||||
elementWidth: 0,
|
||||
elementHeight: 0,
|
||||
};
|
||||
renderedElement?: View | null;
|
||||
|
||||
toggleTooltip = () => {
|
||||
const { onClose } = this.props;
|
||||
Keyboard.dismiss();
|
||||
setTimeout(() => {
|
||||
this.getElementPosition();
|
||||
this._isMounted &&
|
||||
this.setState((prevState) => {
|
||||
if (prevState.isVisible) {
|
||||
onClose && onClose();
|
||||
}
|
||||
return { isVisible: !prevState.isVisible };
|
||||
});
|
||||
}, 100);
|
||||
};
|
||||
|
||||
wrapWithPress = (
|
||||
toggleOnPress: TooltipProps['toggleOnPress'],
|
||||
toggleAction: TooltipProps['toggleAction'],
|
||||
children: React.ReactNode
|
||||
) => {
|
||||
if (toggleOnPress) {
|
||||
return (
|
||||
<TouchableOpacity
|
||||
{...{ [toggleAction]: this.toggleTooltip }}
|
||||
delayLongPress={250}
|
||||
activeOpacity={1}>
|
||||
{children}
|
||||
</TouchableOpacity>
|
||||
);
|
||||
}
|
||||
return children;
|
||||
};
|
||||
|
||||
containerStyle = (withOverlay: boolean, overlayColor: string): ViewStyle => {
|
||||
return {
|
||||
backgroundColor: withOverlay ? overlayColor : 'transparent',
|
||||
flex: 1,
|
||||
};
|
||||
};
|
||||
|
||||
getTooltipStyle = () => {
|
||||
const { yOffset, xOffset, elementHeight, elementWidth } = this.state;
|
||||
const { height, backgroundColor, width, withPointer, containerStyle } =
|
||||
this.props;
|
||||
const { x, y } = getTooltipCoordinate(
|
||||
xOffset,
|
||||
yOffset,
|
||||
elementWidth,
|
||||
elementHeight,
|
||||
ScreenWidth,
|
||||
ScreenHeight,
|
||||
width,
|
||||
height,
|
||||
withPointer
|
||||
);
|
||||
|
||||
return StyleSheet.flatten([
|
||||
{
|
||||
position: 'absolute',
|
||||
[I18nManager.isRTL ? 'right' : 'left']: x,
|
||||
top: y,
|
||||
width,
|
||||
height,
|
||||
backgroundColor,
|
||||
// default styles
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
flex: 1,
|
||||
borderRadius: 10,
|
||||
padding: 10,
|
||||
},
|
||||
containerStyle,
|
||||
]);
|
||||
};
|
||||
|
||||
renderPointer = (tooltipY: FlexStyle['top']) => {
|
||||
const { yOffset, xOffset, elementHeight, elementWidth } = this.state;
|
||||
const { backgroundColor, pointerColor } = this.props;
|
||||
const pastMiddleLine = yOffset > (tooltipY || 0);
|
||||
|
||||
return (
|
||||
<View
|
||||
style={{
|
||||
position: 'absolute',
|
||||
top: pastMiddleLine ? yOffset - 13 : yOffset + elementHeight - 2,
|
||||
[I18nManager.isRTL ? 'right' : 'left']:
|
||||
xOffset +
|
||||
getElementVisibleWidth(elementWidth, xOffset, ScreenWidth) / 2 -
|
||||
7.5,
|
||||
}}>
|
||||
<Triangle
|
||||
style={{ borderBottomColor: pointerColor || backgroundColor }}
|
||||
isDown={pastMiddleLine}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
getTooltipHighlightedButtonStyle = (): ViewStyle => {
|
||||
const { highlightColor } = this.props;
|
||||
const { yOffset, xOffset, elementWidth, elementHeight } = this.state;
|
||||
|
||||
return {
|
||||
position: 'absolute',
|
||||
top: yOffset,
|
||||
[I18nManager.isRTL ? 'right' : 'left']: xOffset,
|
||||
backgroundColor: highlightColor,
|
||||
overflow: 'visible',
|
||||
width: elementWidth,
|
||||
height: elementHeight,
|
||||
};
|
||||
};
|
||||
|
||||
renderTouchableHighlightedButton = () => {
|
||||
const TooltipHighlightedButtonStyle =
|
||||
this.getTooltipHighlightedButtonStyle();
|
||||
|
||||
return (
|
||||
<TouchableOpacity
|
||||
testID="tooltipTouchableHighlightedButton"
|
||||
onPress={() => this.toggleTooltip()}
|
||||
style={TooltipHighlightedButtonStyle}>
|
||||
{this.props.children}
|
||||
</TouchableOpacity>
|
||||
);
|
||||
};
|
||||
|
||||
renderStaticHighlightedButton = () => {
|
||||
const TooltipHighlightedButtonStyle =
|
||||
this.getTooltipHighlightedButtonStyle();
|
||||
|
||||
return (
|
||||
<View style={TooltipHighlightedButtonStyle}>{this.props.children}</View>
|
||||
);
|
||||
};
|
||||
|
||||
renderHighlightedButton = () => {
|
||||
const { closeOnlyOnBackdropPress } = this.props;
|
||||
if (closeOnlyOnBackdropPress) {
|
||||
return this.renderTouchableHighlightedButton();
|
||||
} else {
|
||||
return this.renderStaticHighlightedButton();
|
||||
}
|
||||
};
|
||||
|
||||
renderContent = (withTooltip: boolean) => {
|
||||
const { popover, withPointer, toggleOnPress, toggleAction } = this.props;
|
||||
if (!withTooltip) {
|
||||
return this.wrapWithPress(
|
||||
toggleOnPress,
|
||||
toggleAction,
|
||||
this.props.children
|
||||
);
|
||||
}
|
||||
|
||||
const tooltipStyle = this.getTooltipStyle() as ViewStyle;
|
||||
|
||||
return (
|
||||
<View>
|
||||
{this.renderHighlightedButton()}
|
||||
{withPointer && this.renderPointer(tooltipStyle.top)}
|
||||
<View style={tooltipStyle} testID="tooltipPopoverContainer">
|
||||
{popover}
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
this._isMounted = true;
|
||||
// wait to compute onLayout values.
|
||||
requestAnimationFrame(this.getElementPosition);
|
||||
}
|
||||
componentWillUnmount() {
|
||||
this._isMounted = false;
|
||||
}
|
||||
|
||||
getElementPosition = () => {
|
||||
const { skipAndroidStatusBar } = this.props;
|
||||
this.renderedElement &&
|
||||
this.renderedElement.measure(
|
||||
(
|
||||
_frameOffsetX,
|
||||
_frameOffsetY,
|
||||
width,
|
||||
height,
|
||||
pageOffsetX,
|
||||
pageOffsetY
|
||||
) => {
|
||||
this._isMounted &&
|
||||
this.setState({
|
||||
xOffset: pageOffsetX,
|
||||
yOffset:
|
||||
isIOS || skipAndroidStatusBar
|
||||
? pageOffsetY
|
||||
: pageOffsetY -
|
||||
Platform.select({
|
||||
android: StatusBar.currentHeight,
|
||||
ios: 20,
|
||||
default: 0,
|
||||
}),
|
||||
elementWidth: width,
|
||||
elementHeight: height,
|
||||
});
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
renderStaticModalContent = () => {
|
||||
const { withOverlay, overlayColor } = this.props;
|
||||
return (
|
||||
<Fragment>
|
||||
<TouchableOpacity
|
||||
style={this.containerStyle(withOverlay, overlayColor)}
|
||||
onPress={this.toggleTooltip}
|
||||
activeOpacity={1}
|
||||
/>
|
||||
<View>{this.renderContent(true)}</View>
|
||||
</Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
renderTogglingModalContent = () => {
|
||||
const { withOverlay, overlayColor } = this.props;
|
||||
return (
|
||||
<TouchableOpacity
|
||||
style={this.containerStyle(withOverlay, overlayColor)}
|
||||
onPress={this.toggleTooltip}
|
||||
activeOpacity={1}>
|
||||
{this.renderContent(true)}
|
||||
</TouchableOpacity>
|
||||
);
|
||||
};
|
||||
|
||||
renderModalContent = () => {
|
||||
const { closeOnlyOnBackdropPress } = this.props;
|
||||
if (closeOnlyOnBackdropPress) {
|
||||
return this.renderStaticModalContent();
|
||||
} else {
|
||||
return this.renderTogglingModalContent();
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const { isVisible } = this.state;
|
||||
const { onOpen, ModalComponent } = this.props;
|
||||
return (
|
||||
<View
|
||||
collapsable={false}
|
||||
ref={(e) => {
|
||||
this.renderedElement = e;
|
||||
}}>
|
||||
{this.renderContent(false)}
|
||||
<ModalComponent
|
||||
animationType="fade"
|
||||
visible={isVisible}
|
||||
transparent
|
||||
onShow={onOpen}>
|
||||
{this.renderModalContent()}
|
||||
</ModalComponent>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export { Tooltip };
|
||||
export default withTheme(Tooltip, 'Tooltip');
|
||||
40
lib/react-native-elements/tooltip/Triangle.tsx
Normal file
40
lib/react-native-elements/tooltip/Triangle.tsx
Normal file
@@ -0,0 +1,40 @@
|
||||
import React from 'react';
|
||||
import { View, StyleSheet, StyleProp, ViewStyle } from 'react-native';
|
||||
|
||||
type TriangleProps = {
|
||||
style?: StyleProp<ViewStyle>;
|
||||
isDown?: boolean;
|
||||
};
|
||||
|
||||
const Triangle: React.FunctionComponent<TriangleProps> = ({
|
||||
style,
|
||||
isDown,
|
||||
}) => (
|
||||
<View
|
||||
style={StyleSheet.flatten([
|
||||
styles.triangle,
|
||||
style,
|
||||
isDown ? styles.down : {},
|
||||
])}
|
||||
/>
|
||||
);
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
down: {
|
||||
transform: [{ rotate: '180deg' }],
|
||||
},
|
||||
triangle: {
|
||||
width: 0,
|
||||
height: 0,
|
||||
backgroundColor: 'transparent',
|
||||
borderStyle: 'solid',
|
||||
borderLeftWidth: 8,
|
||||
borderRightWidth: 8,
|
||||
borderBottomWidth: 15,
|
||||
borderLeftColor: 'transparent',
|
||||
borderRightColor: 'transparent',
|
||||
borderBottomColor: 'white',
|
||||
},
|
||||
});
|
||||
|
||||
export default Triangle;
|
||||
142
lib/react-native-elements/tooltip/getTooltipCoordinate.tsx
Normal file
142
lib/react-native-elements/tooltip/getTooltipCoordinate.tsx
Normal file
@@ -0,0 +1,142 @@
|
||||
const getArea = (a: number, b: number) => a * b;
|
||||
|
||||
const getPointDistance = (a: number[], b: number[]) =>
|
||||
Math.sqrt(Math.pow(a[0] - b[0], 2) + Math.pow(a[1] - b[1], 2));
|
||||
|
||||
export const getElementVisibleWidth = (
|
||||
elementWidth: number,
|
||||
xOffset: number,
|
||||
ScreenWidth: number
|
||||
) => {
|
||||
// Element is fully visible OR scrolled right
|
||||
if (xOffset >= 0) {
|
||||
return xOffset + elementWidth <= ScreenWidth // is element fully visible?
|
||||
? elementWidth // element is fully visible;
|
||||
: ScreenWidth - xOffset; // calculate visible width of scrolled element
|
||||
}
|
||||
// Element is scrolled LEFT
|
||||
return elementWidth - xOffset; // calculate visible width of scrolled element
|
||||
};
|
||||
|
||||
/*
|
||||
type Coord = {
|
||||
x: number,
|
||||
y: number,
|
||||
};
|
||||
|
||||
~Tooltip coordinate system:~
|
||||
The tooltip coordinates are based on the element which it is wrapping.
|
||||
We take the x and y coordinates of the element and find the best position
|
||||
to place the tooltip. To find the best position we look for the side with the
|
||||
most space. In order to find the side with the most space we divide the the
|
||||
surroundings in four quadrants and check for the one with biggest area.
|
||||
Once we know the quandrant with the biggest area it place the tooltip in that
|
||||
direction.
|
||||
|
||||
To find the areas we first get 5 coordinate points. The center and the other 4 extreme points
|
||||
which together make a perfect cross shape.
|
||||
|
||||
Once we know the coordinates we can get the length of the vertices which form each quadrant.
|
||||
Since they are squares we only need two.
|
||||
*/
|
||||
const getTooltipCoordinate = (
|
||||
x: number,
|
||||
y: number,
|
||||
width: number,
|
||||
height: number,
|
||||
ScreenWidth: number,
|
||||
ScreenHeight: number,
|
||||
tooltipWidth: number,
|
||||
tooltipHeight: number,
|
||||
withPointer: boolean
|
||||
) => {
|
||||
// The following are point coordinates: [x, y]
|
||||
const center = [
|
||||
x + getElementVisibleWidth(width, x, ScreenWidth) / 2,
|
||||
y + height / 2,
|
||||
];
|
||||
const pOne = [center[0], 0];
|
||||
const pTwo = [ScreenWidth, center[1]];
|
||||
const pThree = [center[0], ScreenHeight];
|
||||
const pFour = [0, center[1]];
|
||||
// vertices
|
||||
const vOne = getPointDistance(center, pOne);
|
||||
const vTwo = getPointDistance(center, pTwo);
|
||||
const vThree = getPointDistance(center, pThree);
|
||||
const vFour = getPointDistance(center, pFour);
|
||||
// Quadrant areas.
|
||||
// type Areas = {
|
||||
// area: number,
|
||||
// id: number,
|
||||
// };
|
||||
const areas = [
|
||||
getArea(vOne, vFour),
|
||||
getArea(vOne, vTwo),
|
||||
getArea(vTwo, vThree),
|
||||
getArea(vThree, vFour),
|
||||
].map((each, index) => ({ area: each, id: index }));
|
||||
const sortedArea = areas.sort((a, b) => b.area - a.area);
|
||||
// deslocated points
|
||||
const dX = 0.001;
|
||||
const dY = height / 2;
|
||||
// Deslocate the coordinates in the direction of the quadrant.
|
||||
const directionCorrection = [
|
||||
[-1, -1],
|
||||
[1, -1],
|
||||
[1, 1],
|
||||
[-1, 1],
|
||||
];
|
||||
const deslocateReferencePoint = [
|
||||
[-tooltipWidth, -tooltipHeight],
|
||||
[0, -tooltipHeight],
|
||||
[0, 0],
|
||||
[-tooltipWidth, 0],
|
||||
];
|
||||
// current quadrant index
|
||||
const qIndex = sortedArea[0].id;
|
||||
const getWithPointerOffsetY = () =>
|
||||
withPointer ? 10 * directionCorrection[qIndex][1] : 0;
|
||||
const getWithPointerOffsetX = () =>
|
||||
withPointer ? center[0] - 18 * directionCorrection[qIndex][0] : center[0];
|
||||
const newX =
|
||||
getWithPointerOffsetX() +
|
||||
(dX * directionCorrection[qIndex][0] + deslocateReferencePoint[qIndex][0]);
|
||||
return {
|
||||
x: constraintX(newX, qIndex, center[0], ScreenWidth, tooltipWidth),
|
||||
y:
|
||||
center[1] +
|
||||
(dY * directionCorrection[qIndex][1] +
|
||||
deslocateReferencePoint[qIndex][1]) +
|
||||
getWithPointerOffsetY(),
|
||||
};
|
||||
};
|
||||
|
||||
const constraintX = (
|
||||
newX: number,
|
||||
qIndex: number,
|
||||
x: number,
|
||||
ScreenWidth: number,
|
||||
tooltipWidth: number
|
||||
) => {
|
||||
switch (qIndex) {
|
||||
// 0 and 3 are the left side quadrants.
|
||||
case 0:
|
||||
case 3: {
|
||||
const maxWidth = newX > ScreenWidth ? ScreenWidth - 10 : newX;
|
||||
return newX < 1 ? 10 : maxWidth;
|
||||
}
|
||||
// 1 and 2 are the right side quadrants
|
||||
case 1:
|
||||
case 2: {
|
||||
const leftOverSpace = ScreenWidth - newX;
|
||||
return leftOverSpace >= tooltipWidth
|
||||
? newX
|
||||
: newX - (tooltipWidth - leftOverSpace + 10);
|
||||
}
|
||||
default: {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export default getTooltipCoordinate;
|
||||
@@ -282,7 +282,7 @@
|
||||
},
|
||||
"rejected": {
|
||||
"title": "تنويه",
|
||||
"message": "تم رفض {{vcLabel}} من قِبل {{Receiver}}"
|
||||
"message": "تم رفض {{vcLabel}} من قِبل {{receiver}}"
|
||||
}
|
||||
},
|
||||
"consentToPhotoVerification": "أوافق على التقاط صورتي للمصادقة"
|
||||
|
||||
@@ -19,15 +19,15 @@
|
||||
"deviceRefNumber": "Device reference number",
|
||||
"name": "Name"
|
||||
},
|
||||
"PasscodeVerify": {
|
||||
"passcodeMismatchError": "Passcode did not match."
|
||||
},
|
||||
"FaceScanner": {},
|
||||
"OIDcAuth": {
|
||||
"title": "OIDC Authentication",
|
||||
"text": "To be replaced with the OIDC provider UI",
|
||||
"verify": "Verify"
|
||||
},
|
||||
"PasscodeVerify": {
|
||||
"passcodeMismatchError": "Passcode did not match."
|
||||
},
|
||||
"QrScanner": {
|
||||
"missingPermissionText": "This app uses the camera to scan the QR code of another device.",
|
||||
"allowCameraButton": "Allow access to camera"
|
||||
@@ -48,18 +48,11 @@
|
||||
"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",
|
||||
"verificationEnabledSuccess": "Activated for online login",
|
||||
"goback": "GO BACK",
|
||||
"BindingWarning": "You have already activated online login for this credential on another device. You will no longer be able to use that device for login if you activate it again on this device.",
|
||||
"yes_confirm": "Yes, I Confirm",
|
||||
"no": "No",
|
||||
"Alert": "Alert",
|
||||
"ok": "Okay"
|
||||
"enableVerification": "Enable Verification",
|
||||
"profileAuthenticated": "Profile is authenticated!",
|
||||
"offlineAuthDisabledHeader": "Offline Authentication disabled!",
|
||||
"offlineAuthDisabledMessage": "Click 'Enable Authentication' to enable this credentials to be used for offline authentication."
|
||||
},
|
||||
"AuthScreen": {
|
||||
"header": "Would you like to use biometrics to unlock the application?",
|
||||
@@ -123,8 +116,7 @@
|
||||
"requestingOTP": "Requesting OTP..."
|
||||
},
|
||||
"OtpVerificationModal": {
|
||||
"enterOtp": "Enter the 6-digit verification code we sent you",
|
||||
"header": "OTP Verification"
|
||||
"enterOtp": "Enter the 6-digit verification code we sent you"
|
||||
},
|
||||
"MyVcsTab": {
|
||||
"addVcButton": "Add {{vcLabel}}",
|
||||
@@ -155,7 +147,6 @@
|
||||
"requestingOtp": "Requesting OTP...",
|
||||
"editTag": "Rename",
|
||||
"redirecting": "Redirecting...",
|
||||
"inProgress": "Loading...",
|
||||
"success": {
|
||||
"unlocked": "{{vcLabel}} successfully unlocked",
|
||||
"locked": "{{vcLabel}} successfully locked",
|
||||
@@ -193,26 +184,6 @@
|
||||
"revokeSuccessful": "VID successfully revoked",
|
||||
"version": "Version"
|
||||
},
|
||||
"QrScreen": {
|
||||
"title": "QR Login",
|
||||
"alignQr": "Align the QR code within the frame to scan",
|
||||
"confirmation": "Confirmation",
|
||||
"checkDomain": "Also, check that there is a lock icon on the address bar.",
|
||||
"domainHead": "https://",
|
||||
"selectId": "Select ID",
|
||||
"noBindedVc": "No Binded {{vcLabel}} Available to Verify",
|
||||
"back": "Go Back",
|
||||
"confirm": "Confirm",
|
||||
"verify": "Verify",
|
||||
"faceAuth": "Face Authentication",
|
||||
"consent": "Consent",
|
||||
"loading": "Loading...",
|
||||
"domainWarning": "Please confirm the domain of the website you are scanning the QR code from is as below",
|
||||
"access": " is requesting access to",
|
||||
"status": "Status",
|
||||
"successMessage": "You Have Successfully Logged Into ",
|
||||
"okay": "Okay"
|
||||
},
|
||||
"ReceiveVcScreen": {
|
||||
"header": "{{vcLabel}} details",
|
||||
"save": "Save {{vcLabel}}",
|
||||
@@ -276,21 +247,7 @@
|
||||
"invalid": "Invalid QR Code",
|
||||
"offline": "Please connect to the internet to scan QR codes using Online sharing mode",
|
||||
"sent": "{{ vcLabel }} has been sent...",
|
||||
"sentHint": "Waiting for receiver to save or discard your {{ vcLabel }}"
|
||||
}
|
||||
},
|
||||
"SelectVcOverlay": {
|
||||
"header": "Share {{vcLabel}}",
|
||||
"chooseVc": "Choose the {{vcLabel}} you'd like to share with",
|
||||
"share": "Share",
|
||||
"verifyAndShare": "Verify Identity & Share"
|
||||
},
|
||||
"SendVcScreen": {
|
||||
"reasonForSharing": "Reason for sharing (optional)",
|
||||
"acceptRequest": "Accept request and choose {{vcLabel}}",
|
||||
"acceptRequestAndVerify": "Accept request and verify",
|
||||
"reject": "Reject",
|
||||
"status": {
|
||||
"sentHint": "Waiting for receiver to save or discard your {{ vcLabel }}",
|
||||
"sharing": {
|
||||
"title": "Sharing...",
|
||||
"hint": "Please wait for the receiving device to accept or reject the share.",
|
||||
@@ -304,7 +261,19 @@
|
||||
"title": "Notice",
|
||||
"message": "Your {{vcLabel}} was discarded by {{receiver}}"
|
||||
}
|
||||
},
|
||||
}
|
||||
},
|
||||
"SelectVcOverlay": {
|
||||
"header": "Share {{vcLabel}}",
|
||||
"chooseVc": "Choose the {{vcLabel}} you'd like to share with",
|
||||
"share": "Share",
|
||||
"verifyAndShare": "Verify Identity & Share"
|
||||
},
|
||||
"SendVcScreen": {
|
||||
"reasonForSharing": "Reason for sharing (optional)",
|
||||
"acceptRequest": "Accept request and choose {{vcLabel}}",
|
||||
"acceptRequestAndVerify": "Accept request and verify",
|
||||
"reject": "Reject",
|
||||
"consentToPhotoVerification": "I give consent to have my photo taken for authentication"
|
||||
},
|
||||
"VerifyIdentityOverlay": {
|
||||
|
||||
@@ -15,7 +15,6 @@ import { linkTransactionResponse, VC } from '../types/vc';
|
||||
import { request } from '../shared/request';
|
||||
import { getJwt } from '../shared/cryptoutil/cryptoUtil';
|
||||
import { getPrivateKey } from '../shared/keystore/SecureKeystore';
|
||||
import getAllConfigurations from '../shared/commonprops/commonProps';
|
||||
|
||||
const model = createModel(
|
||||
{
|
||||
@@ -37,6 +36,7 @@ const model = createModel(
|
||||
domainName: '',
|
||||
consentClaims: ['name', 'picture'],
|
||||
isSharing: {},
|
||||
linkedTransactionId: '',
|
||||
},
|
||||
{
|
||||
events: {
|
||||
@@ -99,7 +99,7 @@ export const qrLoginMachine =
|
||||
'expandLinkTransResp',
|
||||
'setClaims',
|
||||
],
|
||||
target: 'WarningDomainName',
|
||||
target: 'loadMyVcs',
|
||||
},
|
||||
],
|
||||
onError: [
|
||||
@@ -110,26 +110,6 @@ export const qrLoginMachine =
|
||||
],
|
||||
},
|
||||
},
|
||||
WarningDomainName: {
|
||||
invoke: {
|
||||
src: 'domainNameConfig',
|
||||
onDone: {
|
||||
actions: 'setDomainName',
|
||||
target: 'showWarning',
|
||||
},
|
||||
},
|
||||
},
|
||||
showWarning: {
|
||||
on: {
|
||||
CONFIRM: {
|
||||
target: 'loadMyVcs',
|
||||
},
|
||||
DISMISS: {
|
||||
actions: 'forwardToParent',
|
||||
target: 'waitingForData',
|
||||
},
|
||||
},
|
||||
},
|
||||
ShowError: {
|
||||
on: {
|
||||
DISMISS: {
|
||||
@@ -164,7 +144,7 @@ export const qrLoginMachine =
|
||||
faceAuth: {
|
||||
on: {
|
||||
FACE_VALID: {
|
||||
target: 'requestConsent',
|
||||
target: 'sendingAuthenticate',
|
||||
},
|
||||
FACE_INVALID: {
|
||||
target: 'invalidIdentity',
|
||||
@@ -184,6 +164,21 @@ export const qrLoginMachine =
|
||||
},
|
||||
},
|
||||
},
|
||||
sendingAuthenticate: {
|
||||
invoke: {
|
||||
src: 'sendAuthenticate',
|
||||
onDone: {
|
||||
target: 'requestConsent',
|
||||
actions: 'setLinkedTransactionId',
|
||||
},
|
||||
onError: [
|
||||
{
|
||||
actions: 'SetErrorMessage',
|
||||
target: 'ShowError',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
requestConsent: {
|
||||
on: {
|
||||
CONFIRM: {
|
||||
@@ -234,10 +229,6 @@ export const qrLoginMachine =
|
||||
linkCode: (context, event) => event.value,
|
||||
}),
|
||||
|
||||
setDomainName: assign({
|
||||
domainName: (context, event) => event.data,
|
||||
}),
|
||||
|
||||
loadMyVcs: send(StoreEvents.GET(MY_VCS_STORE_KEY), {
|
||||
to: (context) => context.serviceRefs.store,
|
||||
}),
|
||||
@@ -312,6 +303,9 @@ export const qrLoginMachine =
|
||||
return { ...context.isSharing };
|
||||
},
|
||||
}),
|
||||
setLinkedTransactionId: assign({
|
||||
linkedTransactionId: (context, event) => event.data as string,
|
||||
}),
|
||||
},
|
||||
services: {
|
||||
linkTransaction: async (context) => {
|
||||
@@ -328,12 +322,7 @@ export const qrLoginMachine =
|
||||
return response.response;
|
||||
},
|
||||
|
||||
domainNameConfig: async (context) => {
|
||||
var response = await getAllConfigurations();
|
||||
return response.warningDomainName;
|
||||
},
|
||||
|
||||
sendConsent: async (context) => {
|
||||
sendAuthenticate: async (context) => {
|
||||
var privateKey = await getPrivateKey(
|
||||
context.selectedVc.walletBindingResponse?.walletBindingId
|
||||
);
|
||||
@@ -345,7 +334,7 @@ export const qrLoginMachine =
|
||||
walletBindingResponse?.thumbprint
|
||||
);
|
||||
|
||||
const resp = await request(
|
||||
const response = await request(
|
||||
'POST',
|
||||
'/v1/idp/linked-authorization/authenticate',
|
||||
{
|
||||
@@ -363,16 +352,17 @@ export const qrLoginMachine =
|
||||
},
|
||||
}
|
||||
);
|
||||
return response.response.linkedTransactionId;
|
||||
},
|
||||
|
||||
const trnId = resp.response.linkedTransactionId;
|
||||
|
||||
sendConsent: async (context) => {
|
||||
const response = await request(
|
||||
'POST',
|
||||
'/v1/idp/linked-authorization/consent',
|
||||
{
|
||||
requestTime: String(new Date().toISOString()),
|
||||
request: {
|
||||
linkedTransactionId: trnId,
|
||||
linkedTransactionId: context.linkedTransactionId,
|
||||
acceptedClaims: context.essentialClaims.concat(
|
||||
context.selectedVoluntaryClaims
|
||||
),
|
||||
@@ -406,10 +396,6 @@ export function selectDomainName(state: State) {
|
||||
return state.context.domainName;
|
||||
}
|
||||
|
||||
export function selectIsShowWarning(state: State) {
|
||||
return state.matches('showWarning');
|
||||
}
|
||||
|
||||
export function selectIsLinkTransaction(state: State) {
|
||||
return state.matches('linkTransaction');
|
||||
}
|
||||
@@ -437,6 +423,9 @@ export function selectIsShowError(state: State) {
|
||||
export function selectIsRequestConsent(state: State) {
|
||||
return state.matches('requestConsent');
|
||||
}
|
||||
export function selectIsSendingAuthenticate(state: State) {
|
||||
return state.matches('sendingAuthenticate');
|
||||
}
|
||||
|
||||
export function selectIsSendingConsent(state: State) {
|
||||
return state.matches('sendingConsent');
|
||||
@@ -453,6 +442,9 @@ export function selectSelectedVc(state: State) {
|
||||
export function selectLinkTransactionResponse(state: State) {
|
||||
return state.context.linkTransactionResponse;
|
||||
}
|
||||
export function selectEssentialClaims(state: State) {
|
||||
return state.context.essentialClaims;
|
||||
}
|
||||
|
||||
export function selectVoluntaryClaims(state: State) {
|
||||
return state.context.voluntaryClaims;
|
||||
|
||||
@@ -3,20 +3,24 @@
|
||||
export interface Typegen0 {
|
||||
'@@xstate/typegen': true;
|
||||
'internalEvents': {
|
||||
'done.invoke.QrLogin.WarningDomainName:invocation[0]': {
|
||||
type: 'done.invoke.QrLogin.WarningDomainName:invocation[0]';
|
||||
data: unknown;
|
||||
__tip: 'See the XState TS docs to learn how to strongly type this.';
|
||||
};
|
||||
'done.invoke.QrLogin.linkTransaction:invocation[0]': {
|
||||
type: 'done.invoke.QrLogin.linkTransaction:invocation[0]';
|
||||
data: unknown;
|
||||
__tip: 'See the XState TS docs to learn how to strongly type this.';
|
||||
};
|
||||
'done.invoke.QrLogin.sendingAuthenticate:invocation[0]': {
|
||||
type: 'done.invoke.QrLogin.sendingAuthenticate:invocation[0]';
|
||||
data: unknown;
|
||||
__tip: 'See the XState TS docs to learn how to strongly type this.';
|
||||
};
|
||||
'error.platform.QrLogin.linkTransaction:invocation[0]': {
|
||||
type: 'error.platform.QrLogin.linkTransaction:invocation[0]';
|
||||
data: unknown;
|
||||
};
|
||||
'error.platform.QrLogin.sendingAuthenticate:invocation[0]': {
|
||||
type: 'error.platform.QrLogin.sendingAuthenticate:invocation[0]';
|
||||
data: unknown;
|
||||
};
|
||||
'error.platform.QrLogin.sendingConsent:invocation[0]': {
|
||||
type: 'error.platform.QrLogin.sendingConsent:invocation[0]';
|
||||
data: unknown;
|
||||
@@ -24,8 +28,8 @@ export interface Typegen0 {
|
||||
'xstate.init': { type: 'xstate.init' };
|
||||
};
|
||||
'invokeSrcNameMap': {
|
||||
domainNameConfig: 'done.invoke.QrLogin.WarningDomainName:invocation[0]';
|
||||
linkTransaction: 'done.invoke.QrLogin.linkTransaction:invocation[0]';
|
||||
sendAuthenticate: 'done.invoke.QrLogin.sendingAuthenticate:invocation[0]';
|
||||
sendConsent: 'done.invoke.QrLogin.sendingConsent:invocation[0]';
|
||||
};
|
||||
'missingImplementations': {
|
||||
@@ -37,13 +41,16 @@ export interface Typegen0 {
|
||||
'eventsCausingActions': {
|
||||
SetErrorMessage:
|
||||
| 'error.platform.QrLogin.linkTransaction:invocation[0]'
|
||||
| 'error.platform.QrLogin.sendingAuthenticate:invocation[0]'
|
||||
| 'error.platform.QrLogin.sendingConsent:invocation[0]';
|
||||
expandLinkTransResp: 'done.invoke.QrLogin.linkTransaction:invocation[0]';
|
||||
forwardToParent: 'DISMISS';
|
||||
loadMyVcs: 'CONFIRM';
|
||||
loadMyVcs: 'done.invoke.QrLogin.linkTransaction:invocation[0]';
|
||||
resetLinkTransactionId: 'GET';
|
||||
resetSelectedVoluntaryClaims: 'GET';
|
||||
setClaims: 'done.invoke.QrLogin.linkTransaction:invocation[0]';
|
||||
setConsentClaims: 'TOGGLE_CONSENT_CLAIM';
|
||||
setDomainName: 'done.invoke.QrLogin.WarningDomainName:invocation[0]';
|
||||
setLinkedTransactionId: 'done.invoke.QrLogin.sendingAuthenticate:invocation[0]';
|
||||
setMyVcs: 'STORE_RESPONSE';
|
||||
setScanData: 'GET';
|
||||
setSelectedVc: 'SELECT_VC';
|
||||
@@ -52,21 +59,20 @@ export interface Typegen0 {
|
||||
'eventsCausingDelays': {};
|
||||
'eventsCausingGuards': {};
|
||||
'eventsCausingServices': {
|
||||
domainNameConfig: 'done.invoke.QrLogin.linkTransaction:invocation[0]';
|
||||
linkTransaction: 'GET';
|
||||
sendAuthenticate: 'FACE_VALID';
|
||||
sendConsent: 'CONFIRM';
|
||||
};
|
||||
'matchesStates':
|
||||
| 'ShowError'
|
||||
| 'WarningDomainName'
|
||||
| 'done'
|
||||
| 'faceAuth'
|
||||
| 'invalidIdentity'
|
||||
| 'linkTransaction'
|
||||
| 'loadMyVcs'
|
||||
| 'requestConsent'
|
||||
| 'sendingAuthenticate'
|
||||
| 'sendingConsent'
|
||||
| 'showWarning'
|
||||
| 'showvcList'
|
||||
| 'success'
|
||||
| 'waitingForData';
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -35,8 +35,7 @@ export interface Typegen0 {
|
||||
sendDisconnect: 'done.invoke.request.cancelling:invocation[0]';
|
||||
sendVcResponse:
|
||||
| 'done.invoke.request.reviewing.accepted:invocation[0]'
|
||||
| 'done.invoke.request.reviewing.rejected:invocation[0]'
|
||||
| 'done.invoke.request.reviewing:invocation[0]';
|
||||
| 'done.invoke.request.reviewing.rejected:invocation[0]';
|
||||
verifyVp: 'done.invoke.request.reviewing.verifyingVp:invocation[0]';
|
||||
};
|
||||
'missingImplementations': {
|
||||
@@ -88,6 +87,7 @@ export interface Typegen0 {
|
||||
| 'done.invoke.request.reviewing.verifyingVp:invocation[0]';
|
||||
requestReceiverInfo: 'CONNECTED';
|
||||
setIncomingVc: 'VC_RECEIVED';
|
||||
setPairId: 'CONNECTED';
|
||||
setReceiveLogTypeDiscarded: 'CANCEL' | 'REJECT';
|
||||
setReceiveLogTypeRegular: 'ACCEPT';
|
||||
setReceiveLogTypeUnverified: 'FACE_INVALID';
|
||||
@@ -123,7 +123,7 @@ export interface Typegen0 {
|
||||
receiveVc: 'EXCHANGE_DONE';
|
||||
requestBluetooth: 'BLUETOOTH_DISABLED';
|
||||
sendDisconnect: 'CANCEL';
|
||||
sendVcResponse: 'CANCEL' | 'REJECT' | 'STORE_RESPONSE' | 'VC_RECEIVED';
|
||||
sendVcResponse: 'CANCEL' | 'REJECT' | 'STORE_RESPONSE';
|
||||
verifyVp: never;
|
||||
};
|
||||
'matchesStates':
|
||||
|
||||
120
machines/scan.ts
120
machines/scan.ts
@@ -348,14 +348,6 @@ export const scanMachine =
|
||||
sent: {
|
||||
description:
|
||||
'VC data has been shared and the receiver should now be viewing it',
|
||||
on: {
|
||||
VC_ACCEPTED: {
|
||||
target: '#scan.reviewing.accepted',
|
||||
},
|
||||
VC_REJECTED: {
|
||||
target: '#scan.reviewing.rejected',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
on: {
|
||||
@@ -363,8 +355,13 @@ export const scanMachine =
|
||||
target: '#scan.findingConnection',
|
||||
},
|
||||
VC_SENT: {
|
||||
target: '#scan.reviewing.sendingVc.sent',
|
||||
internal: true,
|
||||
target: '.sent',
|
||||
},
|
||||
VC_ACCEPTED: {
|
||||
target: '#scan.reviewing.accepted',
|
||||
},
|
||||
VC_REJECTED: {
|
||||
target: '#scan.reviewing.rejected',
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -762,8 +759,11 @@ export const scanMachine =
|
||||
|
||||
monitorCancellation: (context) => async (callback) => {
|
||||
if (context.sharingProtocol === 'ONLINE') {
|
||||
await onlineSubscribe('disconnect', null, () =>
|
||||
callback({ type: 'DISCONNECT' })
|
||||
await onlineSubscribe(
|
||||
'disconnect',
|
||||
null,
|
||||
() => callback({ type: 'DISCONNECT' }),
|
||||
{ pairId: context.scannedQrParams.cid }
|
||||
);
|
||||
}
|
||||
},
|
||||
@@ -782,6 +782,7 @@ export const scanMachine =
|
||||
|
||||
discoverDevice: (context) => (callback) => {
|
||||
if (context.sharingProtocol === 'OFFLINE') {
|
||||
GoogleNearbyMessages.disconnect();
|
||||
IdpassSmartshare.createConnection('discoverer', () => {
|
||||
callback({ type: 'CONNECTED' });
|
||||
});
|
||||
@@ -791,26 +792,39 @@ export const scanMachine =
|
||||
console.log('\n\n[scan] GNM_ERROR\n\n', kind, message)
|
||||
);
|
||||
|
||||
await GoogleNearbyMessages.connect({
|
||||
apiKey: GNM_API_KEY,
|
||||
discoveryMediums: ['ble'],
|
||||
discoveryModes: ['scan', 'broadcast'],
|
||||
});
|
||||
try {
|
||||
IdpassSmartshare.destroyConnection();
|
||||
} catch (e) {
|
||||
/*pass*/
|
||||
}
|
||||
await GoogleNearbyMessages.connect(
|
||||
Platform.select({
|
||||
ios: {
|
||||
apiKey: GNM_API_KEY,
|
||||
},
|
||||
default: {},
|
||||
})
|
||||
);
|
||||
console.log('[scan] GNM connected!');
|
||||
|
||||
await onlineSubscribe('pairing:response', async (response) => {
|
||||
await GoogleNearbyMessages.unpublish();
|
||||
if (response === 'ok') {
|
||||
callback({ type: 'CONNECTED' });
|
||||
}
|
||||
});
|
||||
await onlineSubscribe(
|
||||
'pairing:response',
|
||||
async (response) => {
|
||||
await GoogleNearbyMessages.unpublish();
|
||||
if (response === context.scannedQrParams.cid) {
|
||||
callback({ type: 'CONNECTED' });
|
||||
}
|
||||
},
|
||||
null,
|
||||
{ pairId: context.scannedQrParams.cid }
|
||||
);
|
||||
|
||||
const pairingEvent: PairingEvent = {
|
||||
type: 'pairing',
|
||||
data: context.scannedQrParams,
|
||||
};
|
||||
|
||||
await onlineSend(pairingEvent);
|
||||
await onlineSend(pairingEvent, context.scannedQrParams.cid);
|
||||
})();
|
||||
}
|
||||
},
|
||||
@@ -839,10 +853,12 @@ export const scanMachine =
|
||||
async (receiverInfo) => {
|
||||
await GoogleNearbyMessages.unpublish();
|
||||
callback({ type: 'EXCHANGE_DONE', receiverInfo });
|
||||
}
|
||||
},
|
||||
null,
|
||||
{ pairId: context.scannedQrParams.cid }
|
||||
);
|
||||
|
||||
await onlineSend(event);
|
||||
await onlineSend(event, context.scannedQrParams.cid);
|
||||
})();
|
||||
}
|
||||
},
|
||||
@@ -880,16 +896,24 @@ export const scanMachine =
|
||||
});
|
||||
return () => subscription?.remove();
|
||||
} else {
|
||||
sendVc(vc, statusCallback, () => callback({ type: 'DISCONNECT' }));
|
||||
sendVc(
|
||||
vc,
|
||||
statusCallback,
|
||||
() => callback({ type: 'DISCONNECT' }),
|
||||
context.scannedQrParams.cid
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
sendDisconnect: (context) => () => {
|
||||
if (context.sharingProtocol === 'ONLINE') {
|
||||
onlineSend({
|
||||
type: 'disconnect',
|
||||
data: 'rejected',
|
||||
});
|
||||
onlineSend(
|
||||
{
|
||||
type: 'disconnect',
|
||||
data: 'rejected',
|
||||
},
|
||||
context.scannedQrParams.cid
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
@@ -953,7 +977,7 @@ export const scanMachine =
|
||||
|
||||
delays: {
|
||||
CLEAR_DELAY: 250,
|
||||
CANCEL_TIMEOUT: 3000,
|
||||
CANCEL_TIMEOUT: 5000,
|
||||
CONNECTION_TIMEOUT: (context) => {
|
||||
return (context.sharingProtocol === 'ONLINE' ? 15 : 5) * 1000;
|
||||
},
|
||||
@@ -1091,7 +1115,8 @@ export function selectIsDisconnected(state: State) {
|
||||
async function sendVc(
|
||||
vc: VC,
|
||||
callback: (status: SendVcStatus) => void,
|
||||
disconnectCallback: () => void
|
||||
disconnectCallback: () => void,
|
||||
pairId: string
|
||||
) {
|
||||
const rawData = JSON.stringify(vc);
|
||||
const chunks = chunkString(rawData, GNM_MESSAGE_LIMIT);
|
||||
@@ -1116,17 +1141,20 @@ async function sendVc(
|
||||
if (typeof status === 'number' && chunk < event.data.vcChunk.total) {
|
||||
chunk += 1;
|
||||
await GoogleNearbyMessages.unpublish();
|
||||
await onlineSend({
|
||||
type: 'send-vc',
|
||||
data: {
|
||||
isChunked: true,
|
||||
vcChunk: {
|
||||
total: chunks.length,
|
||||
chunk,
|
||||
rawData: chunks[chunk],
|
||||
await onlineSend(
|
||||
{
|
||||
type: 'send-vc',
|
||||
data: {
|
||||
isChunked: true,
|
||||
vcChunk: {
|
||||
total: chunks.length,
|
||||
chunk,
|
||||
rawData: chunks[chunk],
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
pairId
|
||||
);
|
||||
} else if (typeof status === 'string') {
|
||||
if (status === 'ACCEPTED' || status === 'REJECTED') {
|
||||
GoogleNearbyMessages.unsubscribe();
|
||||
@@ -1135,16 +1163,16 @@ async function sendVc(
|
||||
}
|
||||
},
|
||||
disconnectCallback,
|
||||
{ keepAlive: true }
|
||||
{ keepAlive: true, pairId }
|
||||
);
|
||||
await onlineSend(event);
|
||||
await onlineSend(event, pairId);
|
||||
} else {
|
||||
const event: SendVcEvent = {
|
||||
type: 'send-vc',
|
||||
data: { isChunked: false, vc },
|
||||
};
|
||||
await onlineSubscribe(SendVcResponseType, callback);
|
||||
await onlineSend(event);
|
||||
await onlineSubscribe(SendVcResponseType, callback, null, { pairId });
|
||||
await onlineSend(event, pairId);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -137,7 +137,6 @@ export interface Typegen0 {
|
||||
SHARING_TIMEOUT:
|
||||
| 'ACCEPT_REQUEST'
|
||||
| 'FACE_VALID'
|
||||
| 'VC_SENT'
|
||||
| 'done.invoke.scan.reviewing.creatingVp:invocation[0]';
|
||||
};
|
||||
'eventsCausingGuards': {
|
||||
@@ -165,7 +164,6 @@ export interface Typegen0 {
|
||||
sendVc:
|
||||
| 'ACCEPT_REQUEST'
|
||||
| 'FACE_VALID'
|
||||
| 'VC_SENT'
|
||||
| 'done.invoke.scan.reviewing.creatingVp:invocation[0]';
|
||||
};
|
||||
'matchesStates':
|
||||
|
||||
@@ -15,7 +15,6 @@ import { verifyCredential } from '../shared/vcjs/verifyCredential';
|
||||
import { log } from 'xstate/lib/actions';
|
||||
import {
|
||||
generateKeys,
|
||||
getJwt,
|
||||
WalletBindingResponse,
|
||||
} from '../shared/cryptoutil/cryptoUtil';
|
||||
import { KeyPair } from 'react-native-rsa-native';
|
||||
@@ -73,7 +72,6 @@ const model = createModel(
|
||||
REFRESH: () => ({}),
|
||||
REVOKE_VC: () => ({}),
|
||||
ADD_WALLET_BINDING_ID: () => ({}),
|
||||
BINDING_DONE: () => ({}),
|
||||
CANCEL: () => ({}),
|
||||
CONFIRM: () => ({}),
|
||||
},
|
||||
@@ -480,8 +478,13 @@ export const vcItemMachine =
|
||||
invoke: {
|
||||
src: 'updatePrivateKey',
|
||||
onDone: {
|
||||
target: 'showBindingStatus',
|
||||
actions: ['updatePrivateKey', 'updateVc'],
|
||||
target: 'idle',
|
||||
actions: [
|
||||
'storeContext',
|
||||
'updatePrivateKey',
|
||||
'updateVc',
|
||||
'setWalletBindingErrorEmpty',
|
||||
],
|
||||
},
|
||||
onError: {
|
||||
actions: 'setWalletBindingError',
|
||||
@@ -489,15 +492,6 @@ export const vcItemMachine =
|
||||
},
|
||||
},
|
||||
},
|
||||
showBindingStatus: {
|
||||
entry: 'storeContext',
|
||||
on: {
|
||||
BINDING_DONE: {
|
||||
target: 'idle',
|
||||
actions: 'setWalletBindingErrorEmpty',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -1022,10 +1016,6 @@ export function selectAcceptingBindingOtp(state: State) {
|
||||
return state.matches('acceptingBindingOtp');
|
||||
}
|
||||
|
||||
export function selectShowBindingStatus(state: State) {
|
||||
return state.matches('showBindingStatus');
|
||||
}
|
||||
|
||||
export function selectShowWalletBindingError(state: State) {
|
||||
return state.matches('showingWalletBindingError');
|
||||
}
|
||||
|
||||
@@ -119,23 +119,23 @@ export interface Typegen0 {
|
||||
'eventsCausingActions': {
|
||||
clearOtp:
|
||||
| ''
|
||||
| 'BINDING_DONE'
|
||||
| 'CANCEL'
|
||||
| 'DISMISS'
|
||||
| 'REVOKE_VC'
|
||||
| 'STORE_RESPONSE'
|
||||
| 'done.invoke.vc-item.requestingBindingOtp:invocation[0]'
|
||||
| 'done.invoke.vc-item.requestingOtp:invocation[0]'
|
||||
| 'done.invoke.vc-item.updatingPrivateKey:invocation[0]'
|
||||
| 'done.invoke.vc-item.verifyingCredential:invocation[0]'
|
||||
| 'error.platform.vc-item.requestingLock:invocation[0]'
|
||||
| 'error.platform.vc-item.requestingRevoke:invocation[0]'
|
||||
| 'error.platform.vc-item.verifyingCredential:invocation[0]';
|
||||
clearTransactionId:
|
||||
| ''
|
||||
| 'BINDING_DONE'
|
||||
| 'CANCEL'
|
||||
| 'DISMISS'
|
||||
| 'STORE_RESPONSE'
|
||||
| 'done.invoke.vc-item.updatingPrivateKey:invocation[0]'
|
||||
| 'done.invoke.vc-item.verifyingCredential:invocation[0]'
|
||||
| 'error.platform.vc-item.verifyingCredential:invocation[0]';
|
||||
incrementDownloadCounter: 'POLL';
|
||||
@@ -170,7 +170,9 @@ export interface Typegen0 {
|
||||
| 'error.platform.vc-item.addingWalletBindingId:invocation[0]'
|
||||
| 'error.platform.vc-item.requestingBindingOtp:invocation[0]'
|
||||
| 'error.platform.vc-item.updatingPrivateKey:invocation[0]';
|
||||
setWalletBindingErrorEmpty: 'BINDING_DONE' | 'CANCEL';
|
||||
setWalletBindingErrorEmpty:
|
||||
| 'CANCEL'
|
||||
| 'done.invoke.vc-item.updatingPrivateKey:invocation[0]';
|
||||
setWalletBindingId: 'done.invoke.vc-item.addingWalletBindingId:invocation[0]';
|
||||
storeContext:
|
||||
| 'CREDENTIAL_DOWNLOADED'
|
||||
@@ -229,7 +231,6 @@ export interface Typegen0 {
|
||||
| 'requestingOtp'
|
||||
| 'requestingRevoke'
|
||||
| 'revokingVc'
|
||||
| 'showBindingStatus'
|
||||
| 'showBindingWarning'
|
||||
| 'showingWalletBindingError'
|
||||
| 'storingTag'
|
||||
|
||||
@@ -1,69 +0,0 @@
|
||||
import { useSelector } from '@xstate/react';
|
||||
import { useContext } from 'react';
|
||||
import { ActorRefFrom } from 'xstate';
|
||||
import {
|
||||
selectIsRefreshingMyVcs,
|
||||
selectMyVcs,
|
||||
VcEvents,
|
||||
} from '../../../machines/vc';
|
||||
import { GlobalContext } from '../../../shared/GlobalContext';
|
||||
import NetInfo from '@react-native-community/netinfo';
|
||||
import { selectVcLabel } from '../../../machines/settings';
|
||||
import {
|
||||
selectAcceptingBindingOtp,
|
||||
selectIsRequestBindingOtp,
|
||||
selectOtpError,
|
||||
selectShowBindingStatus,
|
||||
selectWalletBindingError,
|
||||
selectWalletBindingId,
|
||||
VcItemEvents,
|
||||
vcItemMachine,
|
||||
} from '../../../machines/vcItem';
|
||||
import { ModalProps } from '../../../components/ui/Modal';
|
||||
|
||||
export function useBindVcStatus(props: BindVcProps) {
|
||||
const { appService } = useContext(GlobalContext);
|
||||
const settingsService = appService.children.get('settings');
|
||||
const vcService = appService.children.get('vc');
|
||||
|
||||
const netInfoFetch = (otp: string) => {
|
||||
NetInfo.fetch().then((state) => {
|
||||
if (state.isConnected) {
|
||||
vcService.send(VcItemEvents.INPUT_OTP(otp));
|
||||
} else {
|
||||
vcService.send(VcItemEvents.DISMISS());
|
||||
showToast('Request network failed');
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
return {
|
||||
vcKeys: useSelector(vcService, selectMyVcs),
|
||||
vcLabel: useSelector(settingsService, selectVcLabel),
|
||||
|
||||
isRefreshingVcs: useSelector(vcService, selectIsRefreshingMyVcs),
|
||||
isBindingOtp: useSelector(vcService, selectIsRequestBindingOtp),
|
||||
walletAddress: useSelector(vcService, selectWalletBindingId),
|
||||
isAcceptingBindingOtp: useSelector(vcService, selectAcceptingBindingOtp),
|
||||
showBindingStatus: useSelector(vcService, selectShowBindingStatus),
|
||||
walletBindingError: useSelector(vcService, selectWalletBindingError),
|
||||
|
||||
inputOtp: (otp: string) => {
|
||||
netInfoFetch(otp);
|
||||
},
|
||||
otpError: useSelector(vcService, selectOtpError),
|
||||
BINDING_DONE: () => vcService.send(VcItemEvents.BINDING_DONE()),
|
||||
INPUT_OTP: (otp: string) => vcService.send(VcItemEvents.INPUT_OTP(otp)),
|
||||
DISMISS: () => vcService.send(VcItemEvents.DISMISS()),
|
||||
|
||||
REFRESH: () => vcService.send(VcEvents.REFRESH_MY_VCS()),
|
||||
};
|
||||
}
|
||||
function showToast(arg0: string) {
|
||||
throw new Error('Function not implemented.');
|
||||
}
|
||||
|
||||
export interface BindVcProps extends ModalProps {
|
||||
bindingError: string;
|
||||
onDone: () => void;
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Column, Text, Button, Row } from '../../../components/ui';
|
||||
import { Theme } from '../../../components/ui/styleUtils';
|
||||
import { useBindVcStatus, BindVcProps } from './BindVcController';
|
||||
import { Image } from 'react-native';
|
||||
import { Modal } from '../../../components/ui/Modal';
|
||||
import { Icon } from 'react-native-elements';
|
||||
|
||||
export const BindStatus: React.FC<BindVcProps> = (props) => {
|
||||
const controller = useBindVcStatus(props);
|
||||
const { t, i18n } = useTranslation('VcDetails');
|
||||
var message: string = controller.walletBindingError;
|
||||
|
||||
return (
|
||||
<Modal
|
||||
isVisible={props.isVisible}
|
||||
onDismiss={props.onDismiss}
|
||||
headerElevation={2}
|
||||
headerTitle={t('status')}
|
||||
headerRight={<Icon name={''} />}>
|
||||
<Column fill align="space-around" crossAlign="center" padding={'10'}>
|
||||
<Column crossAlign="center">
|
||||
{!controller.walletBindingError ? (
|
||||
<Image source={Theme.SuccessLogo} resizeMethod="auto" />
|
||||
) : (
|
||||
<Row align="center" crossAlign="center" margin={'0 80 0 0'}>
|
||||
<Image source={Theme.WarningLogo} resizeMethod="auto" />
|
||||
<Text
|
||||
margin={'0 0 0 -80'}
|
||||
color={Theme.Colors.whiteText}
|
||||
weight="bold">
|
||||
!
|
||||
</Text>
|
||||
</Row>
|
||||
)}
|
||||
{controller.walletBindingError ? (
|
||||
<Text
|
||||
align="center"
|
||||
color={Theme.Colors.errorMessage}
|
||||
margin="16 0 0 0">
|
||||
{{ message }}
|
||||
</Text>
|
||||
) : (
|
||||
<Text align="center" margin="16 0 0 0">
|
||||
{t('verificationEnabledSuccess')}
|
||||
</Text>
|
||||
)}
|
||||
</Column>
|
||||
|
||||
<Button title={t('ok')} onPress={props.onDone} type="radius" />
|
||||
</Column>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
@@ -1,7 +1,7 @@
|
||||
import React from 'react';
|
||||
import { Dimensions } from 'react-native';
|
||||
import { Icon, Input, Tooltip } from 'react-native-elements';
|
||||
import { Button, Column, Row, Text } from '../../../components/ui';
|
||||
import { Dimensions, I18nManager } from 'react-native';
|
||||
import { Icon, Input } from 'react-native-elements';
|
||||
import { Button, Centered, Column, Row, Text } from '../../../components/ui';
|
||||
import { Modal } from '../../../components/ui/Modal';
|
||||
import { Theme } from '../../../components/ui/styleUtils';
|
||||
import {
|
||||
@@ -11,6 +11,7 @@ import {
|
||||
import { KeyboardAvoidingView, Platform } from 'react-native';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { MessageOverlay } from '../../../components/MessageOverlay';
|
||||
import Tooltip from '../../../lib/react-native-elements/tooltip/Tooltip';
|
||||
|
||||
export const GetIdInputModal: React.FC<GetIdInputModalProps> = (props) => {
|
||||
const { t } = useTranslation('GetIdInputModal');
|
||||
@@ -39,6 +40,10 @@ export const GetIdInputModal: React.FC<GetIdInputModalProps> = (props) => {
|
||||
color: controller.isInvalid
|
||||
? Theme.Colors.errorMessage
|
||||
: Theme.Colors.textValue,
|
||||
textAlign: 'left',
|
||||
}}
|
||||
inputStyle={{
|
||||
textAlign: I18nManager.isRTL ? 'right' : 'left',
|
||||
}}
|
||||
style={Theme.Styles.placeholder}
|
||||
value={controller.id}
|
||||
@@ -59,25 +64,27 @@ export const GetIdInputModal: React.FC<GetIdInputModalProps> = (props) => {
|
||||
skipAndroidStatusBar={true}
|
||||
onOpen={controller.ACTIVATE_ICON_COLOR}
|
||||
onClose={controller.DEACTIVATE_ICON_COLOR}>
|
||||
{controller.isInvalid ? (
|
||||
<Icon
|
||||
name="error"
|
||||
size={18}
|
||||
color={
|
||||
!controller.iconColor
|
||||
? Theme.Colors.errorMessage
|
||||
: Theme.Colors.Icon
|
||||
}
|
||||
/>
|
||||
) : (
|
||||
<Icon
|
||||
name={'help'}
|
||||
size={18}
|
||||
color={
|
||||
!controller.iconColor ? null : Theme.Colors.Icon
|
||||
}
|
||||
/>
|
||||
)}
|
||||
<Centered width={32} fill>
|
||||
{controller.isInvalid ? (
|
||||
<Icon
|
||||
name="error"
|
||||
size={18}
|
||||
color={
|
||||
!controller.iconColor
|
||||
? Theme.Colors.errorMessage
|
||||
: Theme.Colors.Icon
|
||||
}
|
||||
/>
|
||||
) : (
|
||||
<Icon
|
||||
name={'help'}
|
||||
size={18}
|
||||
color={
|
||||
!controller.iconColor ? null : Theme.Colors.Icon
|
||||
}
|
||||
/>
|
||||
)}
|
||||
</Centered>
|
||||
</Tooltip>
|
||||
}
|
||||
errorStyle={{ color: Theme.Colors.errorMessage }}
|
||||
|
||||
@@ -75,6 +75,7 @@ export const IdInputModal: React.FC<IdInputModalProps> = (props) => {
|
||||
color: controller.isInvalid
|
||||
? Theme.Colors.errorMessage
|
||||
: Theme.Colors.textValue,
|
||||
textAlign: 'left',
|
||||
}}
|
||||
inputStyle={{
|
||||
textAlign: I18nManager.isRTL ? 'right' : 'left',
|
||||
|
||||
@@ -11,7 +11,6 @@ import { useViewVcModal, ViewVcModalProps } from './ViewVcModalController';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { VcDetails } from '../../components/VcDetails';
|
||||
import { OtpVerification } from './MyVcs/OtpVerification';
|
||||
import { BindStatus } from './MyVcs/BindVcStatus';
|
||||
import { BindingVcWarningOverlay } from './MyVcs/BindingVcWarningOverlay';
|
||||
|
||||
export const ViewVcModal: React.FC<ViewVcModalProps> = (props) => {
|
||||
@@ -103,15 +102,6 @@ export const ViewVcModal: React.FC<ViewVcModalProps> = (props) => {
|
||||
/>
|
||||
)}
|
||||
|
||||
{controller.showBindingStatus && (
|
||||
<BindStatus
|
||||
isVisible={controller.showBindingStatus}
|
||||
bindingError={controller.walletBindingError}
|
||||
onDismiss={controller.BINDING_DONE}
|
||||
onDone={controller.BINDING_DONE}
|
||||
/>
|
||||
)}
|
||||
|
||||
<BindingVcWarningOverlay
|
||||
isVisible={controller.isBindingWarning}
|
||||
onConfirm={controller.CONFIRM}
|
||||
|
||||
@@ -16,11 +16,9 @@ import {
|
||||
selectVc,
|
||||
VcItemEvents,
|
||||
vcItemMachine,
|
||||
selectWalletBindingId,
|
||||
selectWalletBindingError,
|
||||
selectIsRequestBindingOtp,
|
||||
selectAcceptingBindingOtp,
|
||||
selectShowBindingStatus,
|
||||
selectEmptyWalletBindingId,
|
||||
isWalletBindingInProgress,
|
||||
selectShowWalletBindingError,
|
||||
@@ -134,7 +132,6 @@ export function useViewVcModal({
|
||||
storedPasscode: useSelector(authService, selectPasscode),
|
||||
isBindingOtp: useSelector(vcItemActor, selectIsRequestBindingOtp),
|
||||
isAcceptingBindingOtp: useSelector(vcItemActor, selectAcceptingBindingOtp),
|
||||
showBindingStatus: useSelector(vcItemActor, selectShowBindingStatus),
|
||||
walletBindingError: useSelector(vcItemActor, selectWalletBindingError),
|
||||
isWalletBindingPending: useSelector(
|
||||
vcItemActor,
|
||||
@@ -174,7 +171,6 @@ export function useViewVcModal({
|
||||
EDIT_TAG: () => vcItemActor.send(VcItemEvents.EDIT_TAG()),
|
||||
SAVE_TAG: (tag: string) => vcItemActor.send(VcItemEvents.SAVE_TAG(tag)),
|
||||
DISMISS: () => vcItemActor.send(VcItemEvents.DISMISS()),
|
||||
BINDING_DONE: () => vcItemActor.send(VcItemEvents.BINDING_DONE()),
|
||||
LOCK_VC: () => vcItemActor.send(VcItemEvents.LOCK_VC()),
|
||||
INPUT_OTP: (otp: string) => vcItemActor.send(VcItemEvents.INPUT_OTP(otp)),
|
||||
CANCEL: () => vcItemActor.send(VcItemEvents.CANCEL()),
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React, { useState } from 'react';
|
||||
import Markdown from 'react-native-simple-markdown';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Image, SafeAreaView, View } from 'react-native';
|
||||
import { I18nManager, Image, SafeAreaView, View } from 'react-native';
|
||||
import { Divider, Icon, ListItem, Overlay } from 'react-native-elements';
|
||||
|
||||
import { Button, Text, Row, Column } from '../../components/ui';
|
||||
@@ -69,7 +69,14 @@ export const Credits: React.FC<CreditsProps> = (props) => {
|
||||
<View style={Theme.CreditsStyles.buttonContainer}>
|
||||
<Button
|
||||
type="clear"
|
||||
icon={<Icon name="chevron-left" color={Theme.Colors.Icon} />}
|
||||
icon={
|
||||
<Icon
|
||||
name={
|
||||
I18nManager.isRTL ? 'chevron-right' : 'chevron-left'
|
||||
}
|
||||
color={Theme.Colors.Icon}
|
||||
/>
|
||||
}
|
||||
title=""
|
||||
onPress={() => setIsViewing(false)}
|
||||
/>
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
import React from 'react';
|
||||
import { Dimensions, RefreshControl, SafeAreaView, View } from 'react-native';
|
||||
import {
|
||||
Dimensions,
|
||||
I18nManager,
|
||||
RefreshControl,
|
||||
SafeAreaView,
|
||||
View,
|
||||
} from 'react-native';
|
||||
import { Divider, Icon, ListItem, Overlay } from 'react-native-elements';
|
||||
import { Button, Column, Centered, Row, Text } from '../../components/ui';
|
||||
import { VidItem } from '../../components/VidItem';
|
||||
@@ -40,7 +46,14 @@ export const Revoke: React.FC<RevokeScreenProps> = (props) => {
|
||||
<View style={Theme.RevokeStyles.buttonContainer}>
|
||||
<Button
|
||||
type="clear"
|
||||
icon={<Icon name="chevron-left" color={Theme.Colors.Icon} />}
|
||||
icon={
|
||||
<Icon
|
||||
name={
|
||||
I18nManager.isRTL ? 'chevron-right' : 'chevron-left'
|
||||
}
|
||||
color={Theme.Colors.Icon}
|
||||
/>
|
||||
}
|
||||
title=""
|
||||
onPress={() => controller.setIsViewing(false)}
|
||||
/>
|
||||
|
||||
@@ -19,7 +19,7 @@ export const MyBindedVcs: React.FC<MyBindedVcsProps> = (props) => {
|
||||
headerTitle={t('selectId')}
|
||||
headerElevation={5}
|
||||
onDismiss={() => {
|
||||
controller.setQrLogin(false), controller.DISMISS();
|
||||
controller.DISMISS();
|
||||
}}>
|
||||
<React.Fragment>
|
||||
<Column fill style={{ display: props.isVisible ? 'flex' : 'none' }}>
|
||||
|
||||
@@ -15,7 +15,7 @@ export const QrConsent: React.FC<QrConsentProps> = (props) => {
|
||||
|
||||
return (
|
||||
<Modal
|
||||
isVisible={controller.isRequestConsent}
|
||||
isVisible={props.isVisible}
|
||||
arrowLeft={<Icon name={''} />}
|
||||
headerTitle={t('consent')}
|
||||
headerElevation={5}
|
||||
@@ -23,80 +23,86 @@ export const QrConsent: React.FC<QrConsentProps> = (props) => {
|
||||
<Column
|
||||
fill
|
||||
align="space-between"
|
||||
padding="0 24 0 24"
|
||||
padding="24 24 0 24"
|
||||
style={{ display: props.isVisible ? 'flex' : 'none' }}
|
||||
backgroundColor={Theme.Colors.lightGreyBackgroundColor}>
|
||||
<Column>
|
||||
{controller.linkTransactionResponse && (
|
||||
<Row margin={'0 0 0 9'} crossAlign="center" align="center">
|
||||
<Image
|
||||
source={controller.logoUrl ? { uri: controller.logoUrl } : null}
|
||||
style={{ width: 65, height: 65 }}
|
||||
/>
|
||||
</Row>
|
||||
)}
|
||||
<Text
|
||||
style={Theme.TextStyles.small}
|
||||
weight="bold"
|
||||
margin={'10 0 0 0'}>
|
||||
{controller.clientName} {t('access')}
|
||||
</Text>
|
||||
</Column>
|
||||
<ScrollView>
|
||||
<Column
|
||||
align="space-evenly"
|
||||
crossAlign="center"
|
||||
margin={'15 0 0 0'}
|
||||
style={Theme.Styles.consentPageTop}
|
||||
elevation={3}>
|
||||
{controller.linkTransactionResponse && (
|
||||
<Row margin={'0 0 0 6'} crossAlign="center" align="center">
|
||||
<Image
|
||||
source={Theme.MosipLogo}
|
||||
style={{ width: 60, height: 70 }}
|
||||
/>
|
||||
<View style={Theme.Styles.consentDottedLine}></View>
|
||||
<Image
|
||||
source={
|
||||
controller.logoUrl ? { uri: controller.logoUrl } : null
|
||||
}
|
||||
style={{ width: 65, height: 65 }}
|
||||
/>
|
||||
<Column>
|
||||
{
|
||||
<Text
|
||||
style={Theme.TextStyles.small}
|
||||
weight="bold"
|
||||
margin={'10 0 0 0'}>
|
||||
{t('essentialClaims')}
|
||||
</Text>
|
||||
}
|
||||
|
||||
{controller.essentialClaims.map((claim, index) => (
|
||||
<Row key={index} align={'space-between'} margin={'10 0 0 20'}>
|
||||
<Text
|
||||
color={Theme.Colors.profileLabel}
|
||||
style={Theme.TextStyles.base}>
|
||||
{t(claim[0].toUpperCase() + claim.slice(1))}
|
||||
</Text>
|
||||
<Text
|
||||
color={Theme.Colors.GrayIcon}
|
||||
style={Theme.TextStyles.small}
|
||||
weight="bold"
|
||||
margin={'10 0 10 6'}>
|
||||
{t('required')}
|
||||
</Text>
|
||||
</Row>
|
||||
)}
|
||||
<Text
|
||||
style={Theme.TextStyles.small}
|
||||
weight="bold"
|
||||
margin={'0 0 10 6'}>
|
||||
{controller.clientName} {t('access')}
|
||||
</Text>
|
||||
))}
|
||||
</Column>
|
||||
|
||||
<Column scroll padding="10 0 0 0">
|
||||
<ListItem bottomDivider>
|
||||
<ListItem.Content>
|
||||
<ListItem.Title>
|
||||
<Text
|
||||
color={Theme.Colors.profileLabel}
|
||||
style={Theme.TextStyles.base}>
|
||||
{t('Name and Picture')}
|
||||
</Text>
|
||||
</ListItem.Title>
|
||||
</ListItem.Content>
|
||||
<Switch value={true} color={Theme.Colors.ProfileIconBg} />
|
||||
</ListItem>
|
||||
{controller.claims.map((claim, index) => {
|
||||
if (claim == 'name' || claim == 'picture') {
|
||||
return null;
|
||||
} else {
|
||||
return (
|
||||
<ListItem key={index} bottomDivider>
|
||||
<ListItem.Content>
|
||||
<ListItem.Title>
|
||||
<Text color={Theme.Colors.profileLabel}>
|
||||
{t(claim[0].toUpperCase() + claim.slice(1))}
|
||||
</Text>
|
||||
</ListItem.Title>
|
||||
</ListItem.Content>
|
||||
<Column>
|
||||
{
|
||||
<Text
|
||||
style={Theme.TextStyles.small}
|
||||
weight="bold"
|
||||
margin={'10 0 0 0'}>
|
||||
{t('voluntaryClaims')}
|
||||
</Text>
|
||||
}
|
||||
|
||||
<Switch
|
||||
value={controller.isShare[claim]}
|
||||
onValueChange={() =>
|
||||
controller.SELECT_CONSENT(
|
||||
controller.isShare[claim],
|
||||
claim
|
||||
)
|
||||
}
|
||||
color={Theme.Colors.Icon}
|
||||
/>
|
||||
</ListItem>
|
||||
);
|
||||
}
|
||||
})}
|
||||
{controller.voluntaryClaims.map((claim, index) => (
|
||||
<ListItem
|
||||
key={index}
|
||||
bottomDivider
|
||||
containerStyle={Theme.claimsContainer.container}>
|
||||
<ListItem.Content>
|
||||
<ListItem.Title>
|
||||
<Text color={Theme.Colors.profileLabel}>
|
||||
{t(claim[0].toUpperCase() + claim.slice(1))}
|
||||
</Text>
|
||||
</ListItem.Title>
|
||||
</ListItem.Content>
|
||||
|
||||
<Switch
|
||||
value={controller.isShare[claim]}
|
||||
onValueChange={() =>
|
||||
controller.SELECT_CONSENT(controller.isShare[claim], claim)
|
||||
}
|
||||
color={Theme.Colors.Icon}
|
||||
/>
|
||||
</ListItem>
|
||||
))}
|
||||
</Column>
|
||||
</ScrollView>
|
||||
<Column
|
||||
@@ -106,13 +112,13 @@ export const QrConsent: React.FC<QrConsentProps> = (props) => {
|
||||
<Button
|
||||
margin={'6 10 0 10'}
|
||||
styles={Theme.ButtonStyles.radius}
|
||||
title={'Confirm'}
|
||||
title={t('allow')}
|
||||
onPress={props.onConfirm}
|
||||
/>
|
||||
<Button
|
||||
margin={'10 10 0 10'}
|
||||
type="clear"
|
||||
title={'Cancel'}
|
||||
title={t('cancel')}
|
||||
onPress={props.onCancel}
|
||||
/>
|
||||
</Column>
|
||||
|
||||
@@ -6,7 +6,6 @@ import { Modal } from '../../components/ui/Modal';
|
||||
import { VerifyIdentityOverlay } from '../VerifyIdentityOverlay';
|
||||
import { MessageOverlay } from '../../components/MessageOverlay';
|
||||
import { MyBindedVcs } from './MyBindedVcs';
|
||||
import { QrLoginWarning } from './QrLoginWarning';
|
||||
import { QrLoginSuccess } from './QrLoginSuccessMessage';
|
||||
import { QrConsent } from './QrConsent';
|
||||
import { QrLoginRef } from '../../machines/QrLoginMachine';
|
||||
@@ -23,10 +22,8 @@ export const QrLogin: React.FC<QrLoginProps> = (props) => {
|
||||
headerTitle={t('title')}
|
||||
headerRight={<Icon name={''} />}>
|
||||
<Column fill>
|
||||
<QrLoginWarning
|
||||
isVisible={controller.isShowWarning}
|
||||
onConfirm={controller.CONFIRM}
|
||||
onCancel={controller.DISMISS}
|
||||
<MyBindedVcs
|
||||
isVisible={controller.isShowingVcList}
|
||||
service={props.service}
|
||||
/>
|
||||
|
||||
@@ -35,7 +32,8 @@ export const QrLogin: React.FC<QrLoginProps> = (props) => {
|
||||
controller.isWaitingForData ||
|
||||
controller.isLoadingMyVcs ||
|
||||
controller.isLinkTransaction ||
|
||||
controller.isSendingConsent
|
||||
controller.isSendingConsent ||
|
||||
controller.isSendingAuthenticate
|
||||
}
|
||||
title={t('loading')}
|
||||
progress
|
||||
@@ -47,11 +45,6 @@ export const QrLogin: React.FC<QrLoginProps> = (props) => {
|
||||
onCancel={controller.DISMISS}
|
||||
/>
|
||||
|
||||
<MyBindedVcs
|
||||
isVisible={controller.isShowingVcList}
|
||||
service={props.service}
|
||||
/>
|
||||
|
||||
<VerifyIdentityOverlay
|
||||
isVisible={controller.isVerifyingIdentity}
|
||||
vc={controller.selectedVc}
|
||||
|
||||
@@ -14,15 +14,15 @@ import {
|
||||
selectIsSharing,
|
||||
selectIsShowError,
|
||||
selectIsShowingVcList,
|
||||
selectIsShowWarning,
|
||||
selectIsVerifyingSuccesful,
|
||||
selectIsWaitingForData,
|
||||
selectLinkTransactionResponse,
|
||||
selectMyVcs,
|
||||
selectLogoUrl,
|
||||
selectDomainName,
|
||||
selectSelectedVc,
|
||||
selectVoluntaryClaims,
|
||||
selectIsSendingAuthenticate,
|
||||
selectEssentialClaims,
|
||||
} from '../../machines/QrLoginMachine';
|
||||
import { selectVcLabel } from '../../machines/settings';
|
||||
import { selectBindedVcs } from '../../machines/vc';
|
||||
@@ -61,7 +61,8 @@ export function useQrLogin({ service }: QrLoginProps) {
|
||||
),
|
||||
domainName: useSelector(service, selectDomainName),
|
||||
logoUrl: useSelector(service, selectLogoUrl),
|
||||
claims: useSelector(service, selectVoluntaryClaims),
|
||||
essentialClaims: useSelector(service, selectEssentialClaims),
|
||||
voluntaryClaims: useSelector(service, selectVoluntaryClaims),
|
||||
clientName: useSelector(service, selectClientName),
|
||||
error: useSelector(service, selectErrorMessage),
|
||||
|
||||
@@ -71,12 +72,12 @@ export function useQrLogin({ service }: QrLoginProps) {
|
||||
SELECT_VC,
|
||||
SELECT_CONSENT,
|
||||
isWaitingForData: useSelector(service, selectIsWaitingForData),
|
||||
isShowWarning: useSelector(service, selectIsShowWarning),
|
||||
isShowingVcList: useSelector(service, selectIsShowingVcList),
|
||||
isLinkTransaction: useSelector(service, selectIsLinkTransaction),
|
||||
isLoadingMyVcs: useSelector(service, selectIsloadMyVcs),
|
||||
isRequestConsent: useSelector(service, selectIsRequestConsent),
|
||||
isShowingError: useSelector(service, selectIsShowError),
|
||||
isSendingAuthenticate: useSelector(service, selectIsSendingAuthenticate),
|
||||
isSendingConsent: useSelector(service, selectIsSendingConsent),
|
||||
isVerifyingIdentity: useSelector(service, selectIsisVerifyingIdentity),
|
||||
isInvalidIdentity: useSelector(service, selectIsInvalidIdentity),
|
||||
|
||||
@@ -75,6 +75,11 @@ export const ReceiveVcScreen: React.FC = () => {
|
||||
onPress={controller.DISMISS}
|
||||
margin={[0, 8, 0, 0]}
|
||||
/>
|
||||
<Button
|
||||
fill
|
||||
title={t('common:tryAgain')}
|
||||
onPress={controller.RETRY_VERIFICATION}
|
||||
/>
|
||||
</Row>
|
||||
</MessageOverlay>
|
||||
</React.Fragment>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React from 'react';
|
||||
import { TFunction, useTranslation } from 'react-i18next';
|
||||
import { Switch } from 'react-native-elements';
|
||||
import { Platform } from 'react-native';
|
||||
import { I18nManager, Platform } from 'react-native';
|
||||
import QRCode from 'react-native-qrcode-svg';
|
||||
|
||||
import { Centered, Button, Row, Column, Text } from '../../components/ui';
|
||||
|
||||
@@ -63,11 +63,12 @@ export const ScanLayout: React.FC = () => {
|
||||
|
||||
<MessageOverlay
|
||||
isVisible={controller.statusOverlay != null}
|
||||
title={controller.statusOverlay?.title}
|
||||
message={controller.statusOverlay?.message}
|
||||
hint={controller.statusOverlay?.hint}
|
||||
onCancel={controller.statusOverlay?.onCancel}
|
||||
progress={!controller.isInvalid}
|
||||
onBackdropPress={controller.DISMISS_INVALID}
|
||||
progress={controller.statusOverlay?.progress}
|
||||
onBackdropPress={controller.statusOverlay?.onBackdropPress}
|
||||
/>
|
||||
|
||||
{controller.isDisconnected && (
|
||||
|
||||
@@ -19,6 +19,11 @@ import {
|
||||
selectIsOffline,
|
||||
selectIsSent,
|
||||
selectIsDisconnected,
|
||||
selectIsRejected,
|
||||
selectIsAccepted,
|
||||
selectIsSendingVc,
|
||||
selectIsSendingVcTimeout,
|
||||
selectReceiverInfo,
|
||||
} from '../../machines/scan';
|
||||
import { selectVcLabel } from '../../machines/settings';
|
||||
import { MainBottomTabParamList } from '../../routes/main';
|
||||
@@ -55,6 +60,11 @@ export function useScanLayout() {
|
||||
locationError.button = t('errors.locationDenied.button');
|
||||
}
|
||||
|
||||
const DISMISS = () => scanService.send(ScanEvents.DISMISS());
|
||||
const CANCEL = () => scanService.send(ScanEvents.CANCEL());
|
||||
|
||||
const receiverInfo = useSelector(scanService, selectReceiverInfo);
|
||||
|
||||
const isInvalid = useSelector(scanService, selectIsInvalid);
|
||||
const isConnecting = useSelector(scanService, selectIsConnecting);
|
||||
const isConnectingTimeout = useSelector(
|
||||
@@ -69,48 +79,89 @@ export function useScanLayout() {
|
||||
scanService,
|
||||
selectIsExchangingDeviceInfoTimeout
|
||||
);
|
||||
const isOffline = useSelector(scanService, selectIsOffline);
|
||||
const isAccepted = useSelector(scanService, selectIsAccepted);
|
||||
const isRejected = useSelector(scanService, selectIsRejected);
|
||||
const isSent = useSelector(scanService, selectIsSent);
|
||||
const isOffline = useSelector(scanService, selectIsOffline);
|
||||
const isSendingVc = useSelector(scanService, selectIsSendingVc);
|
||||
const isSendingVcTimeout = useSelector(scanService, selectIsSendingVcTimeout);
|
||||
|
||||
const vcLabel = useSelector(settingsService, selectVcLabel);
|
||||
|
||||
const onCancel = () => scanService.send(ScanEvents.CANCEL());
|
||||
let statusOverlay: Pick<
|
||||
MessageOverlayProps,
|
||||
'message' | 'hint' | 'onCancel'
|
||||
'title' | 'message' | 'hint' | 'onCancel' | 'progress' | 'onBackdropPress'
|
||||
> = null;
|
||||
if (isConnecting) {
|
||||
statusOverlay = {
|
||||
message: t('status.connecting'),
|
||||
progress: true,
|
||||
};
|
||||
} else if (isConnectingTimeout) {
|
||||
statusOverlay = {
|
||||
message: t('status.connecting'),
|
||||
hint: t('status.connectingTimeout'),
|
||||
onCancel,
|
||||
progress: true,
|
||||
};
|
||||
} else if (isExchangingDeviceInfo) {
|
||||
statusOverlay = {
|
||||
message: t('status.exchangingDeviceInfo'),
|
||||
progress: true,
|
||||
};
|
||||
} else if (isExchangingDeviceInfoTimeout) {
|
||||
statusOverlay = {
|
||||
message: t('status.exchangingDeviceInfo'),
|
||||
hint: t('status.exchangingDeviceInfoTimeout'),
|
||||
onCancel,
|
||||
progress: true,
|
||||
};
|
||||
} else if (isSent) {
|
||||
statusOverlay = {
|
||||
message: t('status.sent', { vcLabel: vcLabel.singular }),
|
||||
hint: t('status.sentHint', { vcLabel: vcLabel.singular }),
|
||||
};
|
||||
} else if (isSendingVc) {
|
||||
statusOverlay = {
|
||||
title: t('status.sharing.title'),
|
||||
hint: t('status.sharing.hint'),
|
||||
progress: true,
|
||||
};
|
||||
} else if (isSendingVcTimeout) {
|
||||
statusOverlay = {
|
||||
title: t('status.sharing.title'),
|
||||
hint: t('status.sharing.timeoutHint'),
|
||||
onCancel: CANCEL,
|
||||
progress: true,
|
||||
};
|
||||
} else if (isAccepted) {
|
||||
statusOverlay = {
|
||||
title: t('status.accepted.title'),
|
||||
message: t('status.accepted.message', {
|
||||
vcLabel: vcLabel.singular,
|
||||
receiver: receiverInfo.deviceName,
|
||||
}),
|
||||
onBackdropPress: DISMISS,
|
||||
};
|
||||
} else if (isRejected) {
|
||||
statusOverlay = {
|
||||
title: t('status.rejected.title'),
|
||||
message: t('status.rejected.message', {
|
||||
vcLabel: vcLabel.singular,
|
||||
receiver: receiverInfo.deviceName,
|
||||
}),
|
||||
onBackdropPress: DISMISS,
|
||||
};
|
||||
} else if (isInvalid) {
|
||||
statusOverlay = {
|
||||
message: t('status.invalid'),
|
||||
onBackdropPress: DISMISS,
|
||||
};
|
||||
} else if (isOffline) {
|
||||
statusOverlay = {
|
||||
message: t('status.offline'),
|
||||
onBackdropPress: DISMISS,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -154,8 +205,6 @@ export function useScanLayout() {
|
||||
isDisconnected: useSelector(scanService, selectIsDisconnected),
|
||||
statusOverlay,
|
||||
|
||||
DISMISS: () => scanService.send(ScanEvents.DISMISS()),
|
||||
DISMISS_INVALID: () =>
|
||||
isInvalid ? scanService.send(ScanEvents.DISMISS()) : null,
|
||||
DISMISS,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -20,6 +20,19 @@
|
||||
"invalid": "Invalid QR Code",
|
||||
"offline": "Please connect to the internet to scan QR codes using Online sharing mode",
|
||||
"sent": "{{ vcLabel }} has been sent...",
|
||||
"sentHint": "Waiting for receiver to save or discard your {{ vcLabel }}"
|
||||
"sentHint": "Waiting for receiver to save or discard your {{ vcLabel }}",
|
||||
"sharing": {
|
||||
"title": "Sharing...",
|
||||
"hint": "Please wait for the receiving device to accept or reject the share.",
|
||||
"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}}"
|
||||
},
|
||||
"rejected": {
|
||||
"title": "Notice",
|
||||
"message": "Your {{vcLabel}} was discarded by {{receiver}}"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,20 +3,5 @@
|
||||
"acceptRequest": "Accept request and choose {{vcLabel}}",
|
||||
"acceptRequestAndVerify": "Accept request and verify",
|
||||
"reject": "Reject",
|
||||
"status": {
|
||||
"sharing": {
|
||||
"title": "Sharing...",
|
||||
"hint": "Please wait for the receiving device to accept or reject the share.",
|
||||
"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}}"
|
||||
},
|
||||
"rejected": {
|
||||
"title": "Notice",
|
||||
"message": "Your {{vcLabel}} was discarded by {{receiver}}"
|
||||
}
|
||||
},
|
||||
"consentToPhotoVerification": "I give consent to have my photo taken for authentication"
|
||||
}
|
||||
@@ -121,46 +121,6 @@ export const SendVcScreen: React.FC = () => {
|
||||
/>
|
||||
</Row>
|
||||
</MessageOverlay>
|
||||
|
||||
<MessageOverlay
|
||||
isVisible={controller.isSendingVc}
|
||||
title={t('status.sharing.title')}
|
||||
hint={
|
||||
controller.isSendingVcTimeout
|
||||
? t('status.sharing.timeoutHint')
|
||||
: t('status.sharing.hint')
|
||||
}
|
||||
onCancel={controller.isSendingVcTimeout ? controller.CANCEL : null}
|
||||
progress
|
||||
/>
|
||||
|
||||
<MessageOverlay
|
||||
isVisible={controller.status != null}
|
||||
title={controller.status?.title}
|
||||
hint={controller.status?.hint}
|
||||
onCancel={controller.status?.onCancel}
|
||||
progress
|
||||
/>
|
||||
|
||||
<MessageOverlay
|
||||
isVisible={controller.isAccepted}
|
||||
title={t('status.accepted.title')}
|
||||
message={t('status.accepted.message', {
|
||||
vcLabel: controller.vcLabel.singular,
|
||||
receiver: controller.receiverInfo.deviceName,
|
||||
})}
|
||||
onBackdropPress={controller.DISMISS}
|
||||
/>
|
||||
|
||||
<MessageOverlay
|
||||
isVisible={controller.isRejected}
|
||||
title={t('status.rejected.title')}
|
||||
message={t('status.rejected.message', {
|
||||
vcLabel: controller.vcLabel.singular,
|
||||
receiver: controller.receiverInfo.deviceName,
|
||||
})}
|
||||
onBackdropPress={controller.DISMISS}
|
||||
/>
|
||||
</React.Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,18 +1,12 @@
|
||||
import { useSelector } from '@xstate/react';
|
||||
import { useContext, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { ActorRefFrom } from 'xstate';
|
||||
import { MessageOverlayProps } from '../../components/MessageOverlay';
|
||||
import {
|
||||
ScanEvents,
|
||||
selectIsAccepted,
|
||||
selectReason,
|
||||
selectReceiverInfo,
|
||||
selectIsRejected,
|
||||
selectIsSelectingVc,
|
||||
selectIsSendingVc,
|
||||
selectVcName,
|
||||
selectIsSendingVcTimeout,
|
||||
selectIsVerifyingIdentity,
|
||||
selectIsInvalidIdentity,
|
||||
selectSelectedVc,
|
||||
@@ -29,24 +23,8 @@ export function useSendVcScreen() {
|
||||
const settingsService = appService.children.get('settings');
|
||||
const vcService = appService.children.get('vc');
|
||||
|
||||
const { t } = useTranslation('SendVcScreen');
|
||||
const isSendingVc = useSelector(scanService, selectIsSendingVc);
|
||||
const isSendingVcTimeout = useSelector(scanService, selectIsSendingVcTimeout);
|
||||
const CANCEL = () => scanService.send(ScanEvents.CANCEL());
|
||||
|
||||
let status: Pick<MessageOverlayProps, 'title' | 'hint' | 'onCancel'> = null;
|
||||
if (isSendingVc) {
|
||||
status = {
|
||||
title: t('status.sharing.title'),
|
||||
};
|
||||
} else if (isSendingVcTimeout) {
|
||||
status = {
|
||||
title: t('status.sharing.title'),
|
||||
hint: t('status.sharing.timeoutHint'),
|
||||
onCancel: CANCEL,
|
||||
};
|
||||
}
|
||||
|
||||
const [selectedIndex, setSelectedIndex] = useState<number>(null);
|
||||
|
||||
return {
|
||||
@@ -60,7 +38,6 @@ export function useSendVcScreen() {
|
||||
scanService.send(ScanEvents.SELECT_VC(vcData));
|
||||
},
|
||||
|
||||
status,
|
||||
receiverInfo: useSelector(scanService, selectReceiverInfo),
|
||||
reason: useSelector(scanService, selectReason),
|
||||
vcName: useSelector(scanService, selectVcName),
|
||||
@@ -69,10 +46,6 @@ export function useSendVcScreen() {
|
||||
selectedVc: useSelector(scanService, selectSelectedVc),
|
||||
|
||||
isSelectingVc: useSelector(scanService, selectIsSelectingVc),
|
||||
isSendingVc,
|
||||
isSendingVcTimeout,
|
||||
isAccepted: useSelector(scanService, selectIsAccepted),
|
||||
isRejected: useSelector(scanService, selectIsRejected),
|
||||
isVerifyingIdentity: useSelector(scanService, selectIsVerifyingIdentity),
|
||||
isInvalidIdentity: useSelector(scanService, selectIsInvalidIdentity),
|
||||
isCancelling: useSelector(scanService, selectIsCancelling),
|
||||
|
||||
@@ -5,24 +5,30 @@
|
||||
// } from '@idpass/smartshare-react-native';
|
||||
import Smartshare from '@idpass/smartshare-react-native';
|
||||
import { ConnectionParams } from '@idpass/smartshare-react-native/lib/typescript/IdpassSmartshare';
|
||||
import { getDeviceNameSync } from 'react-native-device-info';
|
||||
const { IdpassSmartshare, GoogleNearbyMessages } = Smartshare;
|
||||
|
||||
import { DeviceInfo } from '../components/DeviceInfoList';
|
||||
import { VC } from '../types/vc';
|
||||
|
||||
export function onlineSubscribe<T extends SmartshareEventType>(
|
||||
export async function onlineSubscribe<T extends SmartshareEventType>(
|
||||
eventType: T,
|
||||
callback: (data: SmartshareEventData<T>) => void,
|
||||
disconectCallback?: (data: SmartshareEventData<T>) => void,
|
||||
config?: { keepAlive: boolean }
|
||||
config?: { keepAlive?: boolean; pairId?: string }
|
||||
) {
|
||||
return GoogleNearbyMessages.subscribe(
|
||||
(foundMessage) => {
|
||||
if (__DEV__) {
|
||||
console.log('\n[request] MESSAGE_FOUND', foundMessage.slice(0, 100));
|
||||
console.log(
|
||||
`[${getDeviceNameSync()}] MESSAGE_FOUND`,
|
||||
foundMessage.slice(0, 100)
|
||||
);
|
||||
}
|
||||
const response = SmartshareEvent.fromString<T>(foundMessage);
|
||||
if (response.type === 'disconnect') {
|
||||
if (response.pairId !== config?.pairId) {
|
||||
return;
|
||||
} else if (response.type === 'disconnect') {
|
||||
GoogleNearbyMessages.unsubscribe();
|
||||
disconectCallback(response.data);
|
||||
} else if (response.type === eventType) {
|
||||
@@ -32,20 +38,25 @@ export function onlineSubscribe<T extends SmartshareEventType>(
|
||||
},
|
||||
(lostMessage) => {
|
||||
if (__DEV__) {
|
||||
console.log('\n[request] MESSAGE_LOST', lostMessage.slice(0, 100));
|
||||
console.log(
|
||||
`[${getDeviceNameSync()}] MESSAGE_LOST`,
|
||||
lostMessage.slice(0, 100)
|
||||
);
|
||||
}
|
||||
}
|
||||
).catch((error: Error) => {
|
||||
if (error.message.includes('existing callback is already subscribed')) {
|
||||
console.log('Existing callback found. Unsubscribing then retrying...');
|
||||
console.log(
|
||||
`${getDeviceNameSync()} Existing callback found for ${eventType}. Unsubscribing then retrying...`
|
||||
);
|
||||
return onlineSubscribe(eventType, callback, disconectCallback, config);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export function onlineSend(event: SmartshareEvents) {
|
||||
export async function onlineSend(event: SmartshareEvents, pairId: string) {
|
||||
return GoogleNearbyMessages.publish(
|
||||
new SmartshareEvent(event.type, event.data).toString()
|
||||
new SmartshareEvent(event.type, event.data, pairId).toString()
|
||||
);
|
||||
}
|
||||
|
||||
@@ -71,17 +82,27 @@ export function offlineSend(event: SmartshareEvents, callback: () => void) {
|
||||
}
|
||||
|
||||
class SmartshareEvent<T extends SmartshareEventType> {
|
||||
constructor(public type: T | string, public data: SmartshareEventData<T>) {}
|
||||
constructor(
|
||||
public type: T | string,
|
||||
public data: SmartshareEventData<T>,
|
||||
public pairId = ''
|
||||
) {}
|
||||
|
||||
static fromString<T extends SmartshareEventType>(json: string) {
|
||||
const [type, data] = json.split('\n');
|
||||
return new SmartshareEvent<T>(type, data ? JSON.parse(data) : undefined);
|
||||
const [pairId, type, data] = json.split('\n');
|
||||
return new SmartshareEvent<T>(
|
||||
type,
|
||||
data ? JSON.parse(data) : undefined,
|
||||
pairId
|
||||
);
|
||||
}
|
||||
|
||||
toString() {
|
||||
return this.data != null
|
||||
? this.type + '\n' + JSON.stringify(this.data)
|
||||
: this.type;
|
||||
const message =
|
||||
this.data != null
|
||||
? this.type + '\n' + JSON.stringify(this.data)
|
||||
: this.type;
|
||||
return [this.pairId, message].join('\n');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -92,7 +113,7 @@ export interface PairingEvent {
|
||||
|
||||
export interface PairingResponseEvent {
|
||||
type: 'pairing:response';
|
||||
data: 'ok';
|
||||
data: string;
|
||||
}
|
||||
|
||||
export interface ExchangeReceiverInfoEvent {
|
||||
|
||||
Reference in New Issue
Block a user