mirror of
https://github.com/selfxyz/self.git
synced 2026-01-09 06:38:09 -05:00
chore: tighten types across mobile surface areas (#1209)
* chore(app): tighten types across app workspace * fixes * save wip * save wip linting * fix any types * fix types * fix: import forwardRef directly from react - Changed from React.forwardRef to forwardRef import - Resolves linting warning about named exports * save typing and linting updates * cr feedback. final pass * more cr feedback * pipeline fixes
This commit is contained in:
12
.github/workflows/circuits-build.yml
vendored
12
.github/workflows/circuits-build.yml
vendored
@@ -11,20 +11,20 @@ on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
circuit-type:
|
||||
description: 'Circuits to build (comma-separated: register, register_id, register_aadhaar, disclose, dsc). Leave empty to build all.'
|
||||
description: "Circuits to build (comma-separated: register, register_id, register_aadhaar, disclose, dsc). Leave empty to build all."
|
||||
required: false
|
||||
type: string
|
||||
default: ''
|
||||
default: ""
|
||||
circuit-name:
|
||||
description: 'Circuit names to build (comma-separated: register_sha256_sha224_sha224_ecdsa_secp224r1, dsc_sha256_rsa_65537_4096). Cannot be used with circuit-type.'
|
||||
description: "Circuit names to build (comma-separated: register_sha256_sha224_sha224_ecdsa_secp224r1, dsc_sha256_rsa_65537_4096). Cannot be used with circuit-type."
|
||||
required: false
|
||||
type: string
|
||||
default: ''
|
||||
default: ""
|
||||
run-id:
|
||||
description: 'Run ID to download artifacts .'
|
||||
description: "Run ID to download artifacts ."
|
||||
required: false
|
||||
type: string
|
||||
default: ''
|
||||
default: ""
|
||||
|
||||
concurrency:
|
||||
group: circuits-build-${{ github.workflow }}-${{ github.ref }}
|
||||
|
||||
@@ -173,6 +173,16 @@ module.exports = {
|
||||
'@typescript-eslint/ban-ts-comment': 'off',
|
||||
'no-empty': 'off',
|
||||
|
||||
// TypeScript Import Rules
|
||||
|
||||
'@typescript-eslint/consistent-type-imports': [
|
||||
'error',
|
||||
{
|
||||
prefer: 'type-imports',
|
||||
disallowTypeAnnotations: false,
|
||||
},
|
||||
],
|
||||
|
||||
// Override rules conflicting with TypeScript union formatting
|
||||
|
||||
'@typescript-eslint/indent': 'off',
|
||||
|
||||
@@ -170,8 +170,9 @@
|
||||
"@testing-library/react-native": "^13.3.3",
|
||||
"@tsconfig/react-native": "^3.0.6",
|
||||
"@types/add": "^2",
|
||||
"@types/bn.js": "^5.2.0",
|
||||
"@types/dompurify": "^3.2.0",
|
||||
"@types/elliptic": "^6",
|
||||
"@types/elliptic": "^6.4.18",
|
||||
"@types/jest": "^29.5.14",
|
||||
"@types/node": "^22.18.3",
|
||||
"@types/node-forge": "^1.3.14",
|
||||
|
||||
@@ -20,70 +20,124 @@ function transformProjectToAliasImports(project, appRootPath) {
|
||||
|
||||
// Handle import declarations
|
||||
for (const declaration of sourceFile.getImportDeclarations()) {
|
||||
const spec = declaration.getModuleSpecifierValue();
|
||||
|
||||
// Skip existing alias imports
|
||||
if (spec.startsWith('@/') || spec.startsWith('@tests/')) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Handle relative imports
|
||||
if (!spec.startsWith('./') && !spec.startsWith('../')) continue;
|
||||
const abs = path.resolve(dir, spec);
|
||||
let baseDir = null;
|
||||
let baseAlias = null;
|
||||
|
||||
// Determine containment safely using path.relative to avoid startsWith false positives
|
||||
const relFromSrc = path.relative(srcDir, abs);
|
||||
if (!relFromSrc.startsWith('..') && !path.isAbsolute(relFromSrc)) {
|
||||
baseDir = srcDir;
|
||||
baseAlias = '@';
|
||||
} else {
|
||||
const relFromTests = path.relative(testsDir, abs);
|
||||
if (!relFromTests.startsWith('..') && !path.isAbsolute(relFromTests)) {
|
||||
baseDir = testsDir;
|
||||
baseAlias = '@tests';
|
||||
} else {
|
||||
try {
|
||||
// Skip if no module specifier or not a string literal
|
||||
const moduleSpecifier = declaration.getModuleSpecifier();
|
||||
if (
|
||||
!moduleSpecifier ||
|
||||
moduleSpecifier.getKind() !== SyntaxKind.StringLiteral
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
const newSpec = determineAliasStrategy(dir, abs, baseDir, baseAlias);
|
||||
declaration.setModuleSpecifier(newSpec);
|
||||
const spec = declaration.getModuleSpecifierValue();
|
||||
|
||||
// Skip existing alias imports
|
||||
if (spec.startsWith('@/') || spec.startsWith('@tests/')) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Handle relative imports
|
||||
if (!spec.startsWith('./') && !spec.startsWith('../')) continue;
|
||||
const abs = path.resolve(dir, spec);
|
||||
let baseDir = null;
|
||||
let baseAlias = null;
|
||||
|
||||
// Determine containment safely using path.relative to avoid startsWith false positives
|
||||
const relFromSrc = path.relative(srcDir, abs);
|
||||
if (!relFromSrc.startsWith('..') && !path.isAbsolute(relFromSrc)) {
|
||||
baseDir = srcDir;
|
||||
baseAlias = '@';
|
||||
} else {
|
||||
const relFromTests = path.relative(testsDir, abs);
|
||||
if (
|
||||
!relFromTests.startsWith('..') &&
|
||||
!path.isAbsolute(relFromTests)
|
||||
) {
|
||||
baseDir = testsDir;
|
||||
baseAlias = '@tests';
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
const newSpec = determineAliasStrategy(dir, abs, baseDir, baseAlias);
|
||||
declaration.setModuleSpecifier(newSpec);
|
||||
} catch (error) {
|
||||
// Skip declarations that can't be processed (e.g., type-only imports with issues)
|
||||
const msg = error instanceof Error ? error.message : String(error);
|
||||
console.warn(
|
||||
`Skipping import declaration in ${sourceFile.getFilePath()}: ${msg}`,
|
||||
);
|
||||
try {
|
||||
console.debug('Import declaration text:', declaration.getText());
|
||||
} catch {}
|
||||
if (error && typeof error === 'object' && 'stack' in error) {
|
||||
console.debug('Error stack:', error.stack);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Handle export declarations like: export * from '../x' or export {A} from '../x'
|
||||
for (const declaration of sourceFile.getExportDeclarations()) {
|
||||
const spec = declaration.getModuleSpecifierValue();
|
||||
if (!spec) continue;
|
||||
|
||||
// Skip existing alias exports
|
||||
if (spec.startsWith('@/') || spec.startsWith('@tests/')) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Handle relative exports
|
||||
if (!spec.startsWith('./') && !spec.startsWith('../')) continue;
|
||||
const abs = path.resolve(dir, spec);
|
||||
let baseDir = null;
|
||||
let baseAlias = null;
|
||||
|
||||
const relFromSrc = path.relative(srcDir, abs);
|
||||
if (!relFromSrc.startsWith('..') && !path.isAbsolute(relFromSrc)) {
|
||||
baseDir = srcDir;
|
||||
baseAlias = '@';
|
||||
} else {
|
||||
const relFromTests = path.relative(testsDir, abs);
|
||||
if (!relFromTests.startsWith('..') && !path.isAbsolute(relFromTests)) {
|
||||
baseDir = testsDir;
|
||||
baseAlias = '@tests';
|
||||
} else {
|
||||
try {
|
||||
// Skip if no module specifier or not a string literal
|
||||
const moduleSpecifier = declaration.getModuleSpecifier();
|
||||
if (
|
||||
!moduleSpecifier ||
|
||||
moduleSpecifier.getKind() !== SyntaxKind.StringLiteral
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
const newSpec = determineAliasStrategy(dir, abs, baseDir, baseAlias);
|
||||
declaration.setModuleSpecifier(newSpec);
|
||||
const spec = declaration.getModuleSpecifierValue();
|
||||
if (!spec) continue;
|
||||
|
||||
// Skip existing alias exports
|
||||
if (spec.startsWith('@/') || spec.startsWith('@tests/')) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Handle relative exports
|
||||
if (!spec.startsWith('./') && !spec.startsWith('../')) continue;
|
||||
const abs = path.resolve(dir, spec);
|
||||
let baseDir = null;
|
||||
let baseAlias = null;
|
||||
|
||||
const relFromSrc = path.relative(srcDir, abs);
|
||||
if (!relFromSrc.startsWith('..') && !path.isAbsolute(relFromSrc)) {
|
||||
baseDir = srcDir;
|
||||
baseAlias = '@';
|
||||
} else {
|
||||
const relFromTests = path.relative(testsDir, abs);
|
||||
if (
|
||||
!relFromTests.startsWith('..') &&
|
||||
!path.isAbsolute(relFromTests)
|
||||
) {
|
||||
baseDir = testsDir;
|
||||
baseAlias = '@tests';
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
const newSpec = determineAliasStrategy(dir, abs, baseDir, baseAlias);
|
||||
declaration.setModuleSpecifier(newSpec);
|
||||
} catch (error) {
|
||||
// Skip declarations that can't be processed
|
||||
const msg = error instanceof Error ? error.message : String(error);
|
||||
console.warn(
|
||||
`Skipping export declaration in ${sourceFile.getFilePath()}: ${msg}`,
|
||||
);
|
||||
try {
|
||||
console.debug('Export declaration text:', declaration.getText());
|
||||
} catch {}
|
||||
if (error && typeof error === 'object' && 'stack' in error) {
|
||||
console.debug('Error stack:', error.stack);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Handle require() calls
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
import type { LottieViewProps } from 'lottie-react-native';
|
||||
import LottieView from 'lottie-react-native';
|
||||
import React, { useEffect, useRef } from 'react';
|
||||
import React, { forwardRef, useEffect, useRef } from 'react';
|
||||
|
||||
/**
|
||||
* Wrapper around LottieView that fixes iOS native module initialization timing.
|
||||
@@ -20,7 +20,7 @@ import React, { useEffect, useRef } from 'react';
|
||||
* @example
|
||||
* <DelayedLottieView autoPlay loop source={animation} style={styles.animation} />
|
||||
*/
|
||||
export const DelayedLottieView = React.forwardRef<LottieView, LottieViewProps>(
|
||||
export const DelayedLottieView = forwardRef<LottieView, LottieViewProps>(
|
||||
(props, forwardedRef) => {
|
||||
const internalRef = useRef<LottieView>(null);
|
||||
const ref = (forwardedRef as React.RefObject<LottieView>) || internalRef;
|
||||
|
||||
@@ -3,7 +3,11 @@
|
||||
// NOTE: Converts to Apache-2.0 on 2029-06-11 per LICENSE.
|
||||
|
||||
import React from 'react';
|
||||
import type { GestureResponderEvent, ViewStyle } from 'react-native';
|
||||
import type {
|
||||
GestureResponderEvent,
|
||||
LayoutChangeEvent,
|
||||
ViewStyle,
|
||||
} from 'react-native';
|
||||
import { Platform, StyleSheet } from 'react-native';
|
||||
import type { ViewProps } from 'tamagui';
|
||||
import { Button, Text } from 'tamagui';
|
||||
@@ -16,6 +20,7 @@ export interface ButtonProps extends ViewProps {
|
||||
children: React.ReactNode;
|
||||
animatedComponent?: React.ReactNode;
|
||||
trackEvent?: string;
|
||||
onLayout?: (event: LayoutChangeEvent) => void;
|
||||
}
|
||||
|
||||
interface AbstractButtonProps extends ButtonProps {
|
||||
|
||||
@@ -93,7 +93,6 @@ export function HeldPrimaryButton({
|
||||
{...props}
|
||||
onPressIn={onPressIn}
|
||||
onPressOut={onPressOut}
|
||||
// @ts-expect-error actually it is there
|
||||
onLayout={getButtonSize}
|
||||
animatedComponent={renderAnimatedComponent()}
|
||||
>
|
||||
|
||||
@@ -83,7 +83,6 @@ export function HeldPrimaryButton({
|
||||
{...props}
|
||||
onPressIn={onPressIn}
|
||||
onPressOut={onPressOut}
|
||||
// @ts-expect-error actually it is there
|
||||
onLayout={getButtonSize}
|
||||
animatedComponent={renderAnimatedComponent()}
|
||||
>
|
||||
|
||||
@@ -2,17 +2,12 @@
|
||||
// SPDX-License-Identifier: BUSL-1.1
|
||||
// NOTE: Converts to Apache-2.0 on 2029-06-11 per LICENSE.
|
||||
|
||||
import React, { forwardRef } from 'react';
|
||||
import type { StyleProp, ViewStyle } from 'react-native';
|
||||
import type { ComponentProps } from 'react';
|
||||
import React from 'react';
|
||||
import { SvgXml as RNSvgXml } from 'react-native-svg';
|
||||
|
||||
type Props = {
|
||||
xml: string;
|
||||
width?: number;
|
||||
height?: number;
|
||||
style?: StyleProp<ViewStyle>;
|
||||
};
|
||||
type Props = ComponentProps<typeof RNSvgXml>;
|
||||
|
||||
export const SvgXml = forwardRef<any, Props>((p, _ref) => <RNSvgXml {...p} />);
|
||||
export const SvgXml: React.FC<Props> = props => <RNSvgXml {...props} />;
|
||||
SvgXml.displayName = 'SvgXml';
|
||||
export default SvgXml;
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// SPDX-License-Identifier: BUSL-1.1
|
||||
// NOTE: Converts to Apache-2.0 on 2029-06-11 per LICENSE.
|
||||
|
||||
import DOMPurify from 'dompurify';
|
||||
import createDOMPurify from 'dompurify';
|
||||
import {
|
||||
createElement,
|
||||
type CSSProperties,
|
||||
@@ -20,7 +20,7 @@ type Props = {
|
||||
export const SvgXml = forwardRef<HTMLDivElement, Props>(
|
||||
({ xml, width, height, style, ...props }, ref) => {
|
||||
// Initialize DOMPurify for web browser environment
|
||||
const purify = DOMPurify(window);
|
||||
const purify = createDOMPurify(window);
|
||||
const safe = purify.sanitize(xml, {
|
||||
USE_PROFILES: { svg: true, svgFilters: true },
|
||||
});
|
||||
|
||||
@@ -2,7 +2,8 @@
|
||||
// SPDX-License-Identifier: BUSL-1.1
|
||||
// NOTE: Converts to Apache-2.0 on 2029-06-11 per LICENSE.
|
||||
|
||||
import React, { type FC } from 'react';
|
||||
import type { FC } from 'react';
|
||||
import React from 'react';
|
||||
import { Dimensions } from 'react-native';
|
||||
import { Separator, Text, XStack, YStack } from 'tamagui';
|
||||
|
||||
|
||||
@@ -6,7 +6,8 @@ import React, { useCallback } from 'react';
|
||||
import type { NativeSyntheticEvent, StyleProp, ViewStyle } from 'react-native';
|
||||
import { PixelRatio, Platform, requireNativeComponent } from 'react-native';
|
||||
|
||||
import { type SelfClient, useSelfClient } from '@selfxyz/mobile-sdk-alpha';
|
||||
import type { SelfClient } from '@selfxyz/mobile-sdk-alpha';
|
||||
import { useSelfClient } from '@selfxyz/mobile-sdk-alpha';
|
||||
|
||||
import { RCTFragment } from '@/components/native/RCTFragment';
|
||||
|
||||
@@ -69,9 +70,13 @@ export const PassportCamera: React.FC<PassportCameraProps> = ({
|
||||
if (!isMounted) {
|
||||
return;
|
||||
}
|
||||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||
const { error, errorMessage, stackTrace } = event.nativeEvent;
|
||||
const {
|
||||
error: nativeError,
|
||||
errorMessage,
|
||||
stackTrace,
|
||||
} = event.nativeEvent;
|
||||
const e = new Error(errorMessage);
|
||||
e.name = nativeError;
|
||||
e.stack = stackTrace;
|
||||
onPassportRead(e);
|
||||
},
|
||||
|
||||
@@ -4,7 +4,8 @@
|
||||
|
||||
import React, { useCallback, useEffect } from 'react';
|
||||
|
||||
import { type SelfClient, useSelfClient } from '@selfxyz/mobile-sdk-alpha';
|
||||
import type { SelfClient } from '@selfxyz/mobile-sdk-alpha';
|
||||
import { useSelfClient } from '@selfxyz/mobile-sdk-alpha';
|
||||
|
||||
// TODO: Web find a lightweight ocr or mrz scanner.
|
||||
|
||||
|
||||
@@ -53,9 +53,13 @@ export const QRCodeScannerView: React.FC<QRCodeScannerViewProps> = ({
|
||||
if (!isMounted) {
|
||||
return;
|
||||
}
|
||||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||
const { error, errorMessage, stackTrace } = event.nativeEvent;
|
||||
const {
|
||||
error: nativeError,
|
||||
errorMessage,
|
||||
stackTrace,
|
||||
} = event.nativeEvent;
|
||||
const e = new Error(errorMessage);
|
||||
e.name = nativeError;
|
||||
e.stack = stackTrace;
|
||||
onQRData(e);
|
||||
},
|
||||
|
||||
@@ -6,14 +6,17 @@ import { useEffect, useState } from 'react';
|
||||
import { Linking } from 'react-native';
|
||||
import { checkVersion } from 'react-native-check-version';
|
||||
import { useNavigation } from '@react-navigation/native';
|
||||
import type { NativeStackNavigationProp } from '@react-navigation/native-stack';
|
||||
|
||||
import { useSelfClient } from '@selfxyz/mobile-sdk-alpha';
|
||||
import { AppEvents } from '@selfxyz/mobile-sdk-alpha/constants/analytics';
|
||||
|
||||
import type { RootStackParamList } from '@/navigation';
|
||||
import { registerModalCallbacks } from '@/utils/modalCallbackRegistry';
|
||||
|
||||
export const useAppUpdates = (): [boolean, () => void, boolean] => {
|
||||
const navigation = useNavigation();
|
||||
const navigation =
|
||||
useNavigation<NativeStackNavigationProp<RootStackParamList>>();
|
||||
const [newVersionUrl, setNewVersionUrl] = useState<string | null>(null);
|
||||
const [isModalDismissed, setIsModalDismissed] = useState(false);
|
||||
const selfClient = useSelfClient();
|
||||
|
||||
@@ -4,14 +4,17 @@
|
||||
|
||||
import { useState } from 'react';
|
||||
import { useNavigation } from '@react-navigation/native';
|
||||
import type { NativeStackNavigationProp } from '@react-navigation/native-stack';
|
||||
|
||||
import { useSelfClient } from '@selfxyz/mobile-sdk-alpha';
|
||||
import { AppEvents } from '@selfxyz/mobile-sdk-alpha/constants/analytics';
|
||||
|
||||
import type { RootStackParamList } from '@/navigation';
|
||||
import { registerModalCallbacks } from '@/utils/modalCallbackRegistry';
|
||||
|
||||
export const useAppUpdates = (): [boolean, () => void, boolean] => {
|
||||
const navigation = useNavigation();
|
||||
const navigation =
|
||||
useNavigation<NativeStackNavigationProp<RootStackParamList>>();
|
||||
const [isModalDismissed, setIsModalDismissed] = useState(false);
|
||||
const { trackEvent } = useSelfClient();
|
||||
const showAppUpdateModal = () => {
|
||||
|
||||
@@ -4,7 +4,9 @@
|
||||
|
||||
import { useCallback, useRef, useState } from 'react';
|
||||
import { useNavigation } from '@react-navigation/native';
|
||||
import type { NativeStackNavigationProp } from '@react-navigation/native-stack';
|
||||
|
||||
import type { RootStackParamList } from '@/navigation';
|
||||
import type { ModalParams } from '@/screens/app/ModalScreen';
|
||||
import {
|
||||
getModalCallbacks,
|
||||
@@ -14,7 +16,8 @@ import {
|
||||
|
||||
export const useModal = (params: ModalParams) => {
|
||||
const [visible, setVisible] = useState(false);
|
||||
const navigation = useNavigation();
|
||||
const navigation =
|
||||
useNavigation<NativeStackNavigationProp<RootStackParamList>>();
|
||||
const callbackIdRef = useRef<number>();
|
||||
|
||||
const showModal = useCallback(() => {
|
||||
|
||||
@@ -2,7 +2,8 @@
|
||||
// SPDX-License-Identifier: BUSL-1.1
|
||||
// NOTE: Converts to Apache-2.0 on 2029-06-11 per LICENSE.
|
||||
|
||||
import React, { createElement, forwardRef } from 'react';
|
||||
import type React from 'react';
|
||||
import { createElement, forwardRef } from 'react';
|
||||
|
||||
type BlurViewProps = React.HTMLAttributes<HTMLDivElement> & {
|
||||
blurType?: string;
|
||||
|
||||
@@ -2,7 +2,8 @@
|
||||
// SPDX-License-Identifier: BUSL-1.1
|
||||
// NOTE: Converts to Apache-2.0 on 2029-06-11 per LICENSE.
|
||||
|
||||
import React, { createElement, forwardRef } from 'react';
|
||||
import type React from 'react';
|
||||
import { createElement, forwardRef } from 'react';
|
||||
|
||||
export const Circle = forwardRef<
|
||||
SVGCircleElement,
|
||||
|
||||
@@ -11,6 +11,7 @@ import type { DocumentCategory } from '@selfxyz/common/utils/types';
|
||||
import DeferredLinkingInfoScreen from '@/screens/app/DeferredLinkingInfoScreen';
|
||||
import LaunchScreen from '@/screens/app/LaunchScreen';
|
||||
import LoadingScreen from '@/screens/app/LoadingScreen';
|
||||
import type { ModalNavigationParams } from '@/screens/app/ModalScreen';
|
||||
import ModalScreen from '@/screens/app/ModalScreen';
|
||||
import SplashScreen from '@/screens/app/SplashScreen';
|
||||
|
||||
@@ -40,6 +41,7 @@ const appScreens = {
|
||||
animation: 'fade',
|
||||
contentStyle: { backgroundColor: 'transparent' },
|
||||
} as NativeStackNavigationOptions,
|
||||
params: {} as ModalNavigationParams,
|
||||
},
|
||||
DeferredLinkingInfo: {
|
||||
screen: DeferredLinkingInfoScreen,
|
||||
|
||||
@@ -10,6 +10,7 @@ import {
|
||||
createNavigationContainerRef,
|
||||
createStaticNavigation,
|
||||
} from '@react-navigation/native';
|
||||
import type { NativeStackScreenProps } from '@react-navigation/native-stack';
|
||||
import { createNativeStackNavigator } from '@react-navigation/native-stack';
|
||||
|
||||
import { useSelfClient } from '@selfxyz/mobile-sdk-alpha';
|
||||
@@ -47,13 +48,39 @@ const AppNavigation = createNativeStackNavigator({
|
||||
screens: navigationScreens,
|
||||
});
|
||||
|
||||
export type RootStackParamList = StaticParamList<typeof AppNavigation>;
|
||||
type BaseRootStackParamList = StaticParamList<typeof AppNavigation>;
|
||||
|
||||
// Explicitly declare route params that are not inferred from initialParams
|
||||
export type RootStackParamList = Omit<
|
||||
BaseRootStackParamList,
|
||||
'ComingSoon' | 'IDPicker' | 'AadhaarUpload' | 'AadhaarUploadError'
|
||||
> & {
|
||||
ComingSoon: {
|
||||
countryCode?: string;
|
||||
documentCategory?: string;
|
||||
};
|
||||
IDPicker: {
|
||||
countryCode: string;
|
||||
documentTypes: string[];
|
||||
};
|
||||
AadhaarUpload: {
|
||||
countryCode: string;
|
||||
};
|
||||
AadhaarUploadError: {
|
||||
errorType: string;
|
||||
};
|
||||
};
|
||||
|
||||
export type RootStackScreenProps<T extends keyof RootStackParamList> =
|
||||
NativeStackScreenProps<RootStackParamList, T>;
|
||||
|
||||
// Create a ref that we can use to access the navigation state
|
||||
export const navigationRef = createNavigationContainerRef<RootStackParamList>();
|
||||
|
||||
declare global {
|
||||
namespace ReactNavigation {
|
||||
// Allow React Navigation helpers to infer route params from our stack
|
||||
// Use interface merging to avoid duplicate identifier errors
|
||||
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
|
||||
interface RootParamList extends RootStackParamList {}
|
||||
}
|
||||
|
||||
@@ -12,17 +12,18 @@ import React, {
|
||||
useState,
|
||||
} from 'react';
|
||||
import ReactNativeBiometrics from 'react-native-biometrics';
|
||||
import Keychain, { GetOptions, SetOptions } from 'react-native-keychain';
|
||||
import type { GetOptions, SetOptions } from 'react-native-keychain';
|
||||
import Keychain from 'react-native-keychain';
|
||||
|
||||
import { AuthEvents } from '@selfxyz/mobile-sdk-alpha/constants/analytics';
|
||||
|
||||
import { useSettingStore } from '@/stores/settingStore';
|
||||
import type { Mnemonic } from '@/types/mnemonic';
|
||||
import analytics from '@/utils/analytics';
|
||||
import type { GetSecureOptions } from '@/utils/keychainSecurity';
|
||||
import {
|
||||
createKeychainOptions,
|
||||
detectSecurityCapabilities,
|
||||
GetSecureOptions,
|
||||
} from '@/utils/keychainSecurity';
|
||||
|
||||
const { trackEvent } = analytics();
|
||||
|
||||
@@ -2,20 +2,24 @@
|
||||
// SPDX-License-Identifier: BUSL-1.1
|
||||
// NOTE: Converts to Apache-2.0 on 2029-06-11 per LICENSE.
|
||||
|
||||
import { type PropsWithChildren, useMemo } from 'react';
|
||||
import type { PropsWithChildren } from 'react';
|
||||
import { useMemo } from 'react';
|
||||
import { Platform } from 'react-native';
|
||||
|
||||
import {
|
||||
import type {
|
||||
Adapters,
|
||||
TrackEventParams,
|
||||
WsConn,
|
||||
} from '@selfxyz/mobile-sdk-alpha';
|
||||
import {
|
||||
createListenersMap,
|
||||
reactNativeScannerAdapter,
|
||||
SdkEvents,
|
||||
SelfClientProvider as SDKSelfClientProvider,
|
||||
type TrackEventParams,
|
||||
webNFCScannerShim,
|
||||
type WsConn,
|
||||
} from '@selfxyz/mobile-sdk-alpha';
|
||||
|
||||
import type { RootStackParamList } from '@/navigation';
|
||||
import { navigationRef } from '@/navigation';
|
||||
import { unsafe_getPrivateKey } from '@/providers/authProvider';
|
||||
import { selfClientDocumentsAdapter } from '@/providers/passportDataProvider';
|
||||
@@ -24,7 +28,6 @@ import { useSettingStore } from '@/stores/settingStore';
|
||||
import analytics from '@/utils/analytics';
|
||||
|
||||
type GlobalCrypto = { crypto?: { subtle?: Crypto['subtle'] } };
|
||||
|
||||
/**
|
||||
* Provides a configured Self SDK client instance to all descendants.
|
||||
*
|
||||
@@ -33,6 +36,25 @@ type GlobalCrypto = { crypto?: { subtle?: Crypto['subtle'] } };
|
||||
* - `fetch`/`WebSocket` for network communication
|
||||
* - Web Crypto hashing with a stub signer
|
||||
*/
|
||||
function navigateIfReady<RouteName extends keyof RootStackParamList>(
|
||||
route: RouteName,
|
||||
...args: undefined extends RootStackParamList[RouteName]
|
||||
? [params?: RootStackParamList[RouteName]]
|
||||
: [params: RootStackParamList[RouteName]]
|
||||
): void {
|
||||
if (navigationRef.isReady()) {
|
||||
const params = args[0];
|
||||
if (params !== undefined) {
|
||||
(navigationRef.navigate as (r: RouteName, p: typeof params) => void)(
|
||||
route,
|
||||
params,
|
||||
);
|
||||
} else {
|
||||
navigationRef.navigate(route as never);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const SelfClientProvider = ({ children }: PropsWithChildren) => {
|
||||
const config = useMemo(() => ({}), []);
|
||||
const adapters: Adapters = useMemo(
|
||||
@@ -134,13 +156,17 @@ export const SelfClientProvider = ({ children }: PropsWithChildren) => {
|
||||
|
||||
addListener(
|
||||
SdkEvents.PROVING_PASSPORT_NOT_SUPPORTED,
|
||||
({ countryCode, documentCategory }) => {
|
||||
if (navigationRef.isReady()) {
|
||||
navigationRef.navigate('ComingSoon', {
|
||||
countryCode,
|
||||
documentCategory,
|
||||
} as any);
|
||||
}
|
||||
({
|
||||
countryCode,
|
||||
documentCategory,
|
||||
}: {
|
||||
countryCode: string | null;
|
||||
documentCategory: string | null;
|
||||
}) => {
|
||||
navigateIfReady('ComingSoon', {
|
||||
countryCode: countryCode ?? undefined,
|
||||
documentCategory: documentCategory ?? undefined,
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
@@ -207,17 +233,19 @@ export const SelfClientProvider = ({ children }: PropsWithChildren) => {
|
||||
}
|
||||
});
|
||||
addListener(SdkEvents.PROVING_AADHAAR_UPLOAD_FAILURE, ({ errorType }) => {
|
||||
if (navigationRef.isReady()) {
|
||||
// @ts-expect-error
|
||||
navigationRef.navigate('AadhaarUploadError', { errorType });
|
||||
}
|
||||
navigateIfReady('AadhaarUploadError', { errorType });
|
||||
});
|
||||
|
||||
addListener(
|
||||
SdkEvents.DOCUMENT_COUNTRY_SELECTED,
|
||||
({ countryCode, documentTypes }) => {
|
||||
({
|
||||
countryCode,
|
||||
documentTypes,
|
||||
}: {
|
||||
countryCode: string;
|
||||
documentTypes: string[];
|
||||
}) => {
|
||||
if (navigationRef.isReady()) {
|
||||
// @ts-expect-error
|
||||
navigationRef.navigate('IDPicker', { countryCode, documentTypes });
|
||||
}
|
||||
},
|
||||
@@ -234,10 +262,14 @@ export const SelfClientProvider = ({ children }: PropsWithChildren) => {
|
||||
navigationRef.navigate('DocumentOnboarding');
|
||||
break;
|
||||
case 'a':
|
||||
navigationRef.navigate('AadhaarUpload', { countryCode } as never);
|
||||
if (countryCode) {
|
||||
navigationRef.navigate('AadhaarUpload', { countryCode });
|
||||
}
|
||||
break;
|
||||
default:
|
||||
navigationRef.navigate('ComingSoon', { countryCode } as never);
|
||||
if (countryCode) {
|
||||
navigationRef.navigate('ComingSoon', { countryCode });
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
import React, { useCallback, useState } from 'react';
|
||||
import { Separator, View, XStack, YStack } from 'tamagui';
|
||||
import { useNavigation } from '@react-navigation/native';
|
||||
import type { NativeStackNavigationProp } from '@react-navigation/native-stack';
|
||||
|
||||
import { isUserRegisteredWithAlternativeCSCA } from '@selfxyz/common/utils/passports/validate';
|
||||
import { useSelfClient } from '@selfxyz/mobile-sdk-alpha';
|
||||
@@ -19,6 +20,7 @@ import useHapticNavigation from '@/hooks/useHapticNavigation';
|
||||
import Keyboard from '@/images/icons/keyboard.svg';
|
||||
import RestoreAccountSvg from '@/images/icons/restore_account.svg';
|
||||
import { ExpandableBottomLayout } from '@/layouts/ExpandableBottomLayout';
|
||||
import type { RootStackParamList } from '@/navigation';
|
||||
import { useAuth } from '@/providers/authProvider';
|
||||
import {
|
||||
loadPassportDataAndSecret,
|
||||
@@ -37,7 +39,8 @@ const AccountRecoveryChoiceScreen: React.FC = () => {
|
||||
const { cloudBackupEnabled, toggleCloudBackupEnabled, biometricsAvailable } =
|
||||
useSettingStore();
|
||||
const { download } = useBackupMnemonic();
|
||||
const navigation = useNavigation();
|
||||
const navigation =
|
||||
useNavigation<NativeStackNavigationProp<RootStackParamList>>();
|
||||
|
||||
const onRestoreFromCloudNext = useHapticNavigation('AccountVerifiedSuccess');
|
||||
const onEnterRecoveryPress = useHapticNavigation('RecoverWithPhrase');
|
||||
|
||||
@@ -8,6 +8,7 @@ import { Keyboard, StyleSheet } from 'react-native';
|
||||
import { Text, TextArea, View, XStack, YStack } from 'tamagui';
|
||||
import Clipboard from '@react-native-clipboard/clipboard';
|
||||
import { useNavigation } from '@react-navigation/native';
|
||||
import type { NativeStackNavigationProp } from '@react-navigation/native-stack';
|
||||
|
||||
import { isUserRegisteredWithAlternativeCSCA } from '@selfxyz/common/utils/passports/validate';
|
||||
import { useSelfClient } from '@selfxyz/mobile-sdk-alpha';
|
||||
@@ -16,6 +17,7 @@ import { BackupEvents } from '@selfxyz/mobile-sdk-alpha/constants/analytics';
|
||||
import { SecondaryButton } from '@/components/buttons/SecondaryButton';
|
||||
import Description from '@/components/typography/Description';
|
||||
import Paste from '@/images/icons/paste.svg';
|
||||
import type { RootStackParamList } from '@/navigation';
|
||||
import { useAuth } from '@/providers/authProvider';
|
||||
import {
|
||||
loadPassportDataAndSecret,
|
||||
@@ -31,7 +33,8 @@ import {
|
||||
} from '@/utils/colors';
|
||||
|
||||
const RecoverWithPhraseScreen: React.FC = () => {
|
||||
const navigation = useNavigation();
|
||||
const navigation =
|
||||
useNavigation<NativeStackNavigationProp<RootStackParamList>>();
|
||||
const selfClient = useSelfClient();
|
||||
const { useProtocolStore } = selfClient;
|
||||
const { restoreAccountFromMnemonic } = useAuth();
|
||||
|
||||
@@ -6,6 +6,7 @@ import React, { useCallback, useMemo, useState } from 'react';
|
||||
import { YStack } from 'tamagui';
|
||||
import type { StaticScreenProps } from '@react-navigation/native';
|
||||
import { useNavigation } from '@react-navigation/native';
|
||||
import type { NativeStackNavigationProp } from '@react-navigation/native-stack';
|
||||
|
||||
import { useSelfClient } from '@selfxyz/mobile-sdk-alpha';
|
||||
import { BackupEvents } from '@selfxyz/mobile-sdk-alpha/constants/analytics';
|
||||
@@ -181,7 +182,8 @@ function BottomButton({
|
||||
nextScreen?: NextScreen;
|
||||
}) {
|
||||
const { trackEvent } = useSelfClient();
|
||||
const navigation = useNavigation();
|
||||
const navigation =
|
||||
useNavigation<NativeStackNavigationProp<RootStackParamList>>();
|
||||
|
||||
const goBack = () => {
|
||||
confirmTap();
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// SPDX-License-Identifier: BUSL-1.1
|
||||
// NOTE: Converts to Apache-2.0 on 2029-06-11 per LICENSE.
|
||||
|
||||
import LottieView from 'lottie-react-native';
|
||||
import type LottieView from 'lottie-react-native';
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
import { StyleSheet } from 'react-native';
|
||||
import type { StaticScreenProps } from '@react-navigation/native';
|
||||
@@ -10,7 +10,7 @@ import { useFocusEffect, useIsFocused } from '@react-navigation/native';
|
||||
|
||||
import type { DocumentCategory } from '@selfxyz/common/utils/types';
|
||||
import { loadSelectedDocument, useSelfClient } from '@selfxyz/mobile-sdk-alpha';
|
||||
import { ProvingStateType } from '@selfxyz/mobile-sdk-alpha/browser';
|
||||
import type { ProvingStateType } from '@selfxyz/mobile-sdk-alpha/browser';
|
||||
|
||||
import failAnimation from '@/assets/animations/loading/fail.json';
|
||||
import proveLoadingAnimation from '@/assets/animations/loading/prove.json';
|
||||
@@ -98,7 +98,7 @@ const LoadingScreen: React.FC<LoadingScreenProps> = ({ route }) => {
|
||||
} else {
|
||||
await init(selfClient, 'dsc', true);
|
||||
}
|
||||
} catch (_error) {
|
||||
} catch {
|
||||
console.error('Error loading selected document:');
|
||||
await init(selfClient, 'dsc', true);
|
||||
} finally {
|
||||
|
||||
@@ -19,6 +19,7 @@ import {
|
||||
YStack,
|
||||
} from 'tamagui';
|
||||
import { useNavigation } from '@react-navigation/native';
|
||||
import type { NativeStackNavigationProp } from '@react-navigation/native-stack';
|
||||
import { ChevronDown, Minus, Plus, X } from '@tamagui/lucide-icons';
|
||||
|
||||
import { countryCodes } from '@selfxyz/common/constants';
|
||||
@@ -37,6 +38,7 @@ import { useMockDataForm } from '@/hooks/useMockDataForm';
|
||||
import SelfDevCard from '@/images/card-dev.svg';
|
||||
import IdIcon from '@/images/icons/id_icon.svg';
|
||||
import NoteIcon from '@/images/icons/note.svg';
|
||||
import type { RootStackParamList } from '@/navigation';
|
||||
import { storePassportData } from '@/providers/passportDataProvider';
|
||||
import {
|
||||
black,
|
||||
@@ -159,7 +161,8 @@ const FormSection: React.FC<FormSectionProps> = ({
|
||||
|
||||
const CreateMockScreen: React.FC = () => {
|
||||
const { trackEvent } = useSelfClient();
|
||||
const navigation = useNavigation();
|
||||
const navigation =
|
||||
useNavigation<NativeStackNavigationProp<RootStackParamList>>();
|
||||
const {
|
||||
age,
|
||||
setAge,
|
||||
|
||||
@@ -8,6 +8,7 @@ import { ActivityIndicator, View } from 'react-native';
|
||||
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
||||
import { ScrollView, Text, XStack, YStack } from 'tamagui';
|
||||
import { useNavigation } from '@react-navigation/native';
|
||||
import type { NativeStackNavigationProp } from '@react-navigation/native-stack';
|
||||
|
||||
import { countryCodes } from '@selfxyz/common/constants';
|
||||
import { getCountryISO2 } from '@selfxyz/common/constants/countries';
|
||||
@@ -20,13 +21,15 @@ import ButtonsContainer from '@/components/ButtonsContainer';
|
||||
import { BodyText } from '@/components/typography/BodyText';
|
||||
import Description from '@/components/typography/Description';
|
||||
import { Title } from '@/components/typography/Title';
|
||||
import type { RootStackParamList } from '@/navigation';
|
||||
import { storePassportData } from '@/providers/passportDataProvider';
|
||||
import useUserStore from '@/stores/userStore';
|
||||
import { black, borderColor, white } from '@/utils/colors';
|
||||
import { extraYPadding } from '@/utils/constants';
|
||||
|
||||
const CreateMockScreenDeepLink: React.FC = () => {
|
||||
const navigation = useNavigation();
|
||||
const navigation =
|
||||
useNavigation<NativeStackNavigationProp<RootStackParamList>>();
|
||||
|
||||
const [selectedCountry, setSelectedCountry] = useState('USA');
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// SPDX-License-Identifier: BUSL-1.1
|
||||
// NOTE: Converts to Apache-2.0 on 2029-06-11 per LICENSE.
|
||||
|
||||
import React, { useCallback, useEffect, useState } from 'react';
|
||||
import React, { useCallback, useEffect, useRef, useState } from 'react';
|
||||
import {
|
||||
Button,
|
||||
Input,
|
||||
@@ -44,6 +44,7 @@ const DevFeatureFlagsScreen: React.FC = () => {
|
||||
const [debounceTimers, setDebounceTimers] = useState<
|
||||
Record<string, NodeJS.Timeout>
|
||||
>({});
|
||||
const debounceTimersRef = useRef<Record<string, NodeJS.Timeout>>({});
|
||||
|
||||
const loadFeatureFlags = useCallback(async () => {
|
||||
try {
|
||||
@@ -186,17 +187,19 @@ const DevFeatureFlagsScreen: React.FC = () => {
|
||||
loadFeatureFlags();
|
||||
}, [loadFeatureFlags]);
|
||||
|
||||
useEffect(() => {
|
||||
debounceTimersRef.current = debounceTimers;
|
||||
}, [debounceTimers]);
|
||||
|
||||
// Cleanup debounce timers on unmount
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
Object.values(debounceTimers).forEach(timer => {
|
||||
Object.values(debounceTimersRef.current).forEach(timer => {
|
||||
if (timer) {
|
||||
clearTimeout(timer);
|
||||
}
|
||||
});
|
||||
};
|
||||
// only clean up on unmount
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
const hasLocalOverrides = featureFlags.some(
|
||||
|
||||
@@ -2,12 +2,12 @@
|
||||
// SPDX-License-Identifier: BUSL-1.1
|
||||
// NOTE: Converts to Apache-2.0 on 2029-06-11 per LICENSE.
|
||||
|
||||
import LottieView from 'lottie-react-native';
|
||||
import type LottieView from 'lottie-react-native';
|
||||
import React, { useEffect, useMemo, useState } from 'react';
|
||||
import { Adapt, Button, Select, Sheet, Text, XStack, YStack } from 'tamagui';
|
||||
import { Check, ChevronDown } from '@tamagui/lucide-icons';
|
||||
|
||||
import {
|
||||
import type {
|
||||
provingMachineCircuitType,
|
||||
ProvingStateType,
|
||||
} from '@selfxyz/mobile-sdk-alpha';
|
||||
@@ -58,20 +58,22 @@ const DevLoadingScreen: React.FC = () => {
|
||||
const [canCloseApp, setCanCloseApp] = useState(false);
|
||||
const [shouldLoopAnimation, setShouldLoopAnimation] = useState(true);
|
||||
|
||||
const terminalStates: ProvingStateType[] = [
|
||||
'completed',
|
||||
'error',
|
||||
'failure',
|
||||
'passport_not_supported',
|
||||
'account_recovery_choice',
|
||||
'passport_data_not_found',
|
||||
];
|
||||
const terminalStates = useMemo<ProvingStateType[]>(
|
||||
() => [
|
||||
'completed',
|
||||
'error',
|
||||
'failure',
|
||||
'passport_not_supported',
|
||||
'account_recovery_choice',
|
||||
'passport_data_not_found',
|
||||
],
|
||||
[],
|
||||
);
|
||||
|
||||
const safeToCloseStates: ProvingStateType[] = [
|
||||
'proving',
|
||||
'post_proving',
|
||||
'completed',
|
||||
];
|
||||
const safeToCloseStates = useMemo<ProvingStateType[]>(
|
||||
() => ['proving', 'post_proving', 'completed'],
|
||||
[],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
const { actionText, actionSubText, estimatedTime, statusBarProgress } =
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
// NOTE: Converts to Apache-2.0 on 2029-06-11 per LICENSE.
|
||||
|
||||
import React, { useCallback, useEffect, useState } from 'react';
|
||||
import type { ImageSourcePropType } from 'react-native';
|
||||
import { Linking } from 'react-native';
|
||||
import { Image, XStack, YStack } from 'tamagui';
|
||||
import { useNavigation } from '@react-navigation/native';
|
||||
@@ -32,6 +33,7 @@ const AadhaarUploadScreen: React.FC = () => {
|
||||
useNavigation<NativeStackNavigationProp<RootStackParamList>>();
|
||||
const { trackEvent } = useSelfClient();
|
||||
const [isProcessing, setIsProcessing] = useState(false);
|
||||
const aadhaarImageSource: ImageSourcePropType = AadhaarImage;
|
||||
|
||||
const { showModal: showPermissionModal } = useModal({
|
||||
titleText: 'Photo Library Access Required',
|
||||
@@ -117,16 +119,16 @@ const AadhaarUploadScreen: React.FC = () => {
|
||||
errorMessage.includes('Failed to process') ||
|
||||
errorMessage.includes('Invalid')
|
||||
) {
|
||||
(navigation.navigate as any)('AadhaarUploadError', {
|
||||
errorType: 'general' as const,
|
||||
});
|
||||
navigation.navigate('AadhaarUploadError', {
|
||||
errorType: 'general',
|
||||
} as never);
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle any other errors by showing error screen
|
||||
(navigation.navigate as any)('AadhaarUploadError', {
|
||||
errorType: 'general' as const,
|
||||
});
|
||||
navigation.navigate('AadhaarUploadError', {
|
||||
errorType: 'general',
|
||||
} as never);
|
||||
} finally {
|
||||
setIsProcessing(false);
|
||||
}
|
||||
@@ -152,7 +154,7 @@ const AadhaarUploadScreen: React.FC = () => {
|
||||
paddingVertical={20}
|
||||
>
|
||||
<Image
|
||||
source={AadhaarImage as any}
|
||||
source={aadhaarImageSource}
|
||||
width="100%"
|
||||
height="100%"
|
||||
objectFit="contain"
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
import React from 'react';
|
||||
import { YStack } from 'tamagui';
|
||||
import { useNavigation } from '@react-navigation/native';
|
||||
import type { NativeStackNavigationProp } from '@react-navigation/native-stack';
|
||||
|
||||
import { useSelfClient } from '@selfxyz/mobile-sdk-alpha';
|
||||
import { AadhaarEvents } from '@selfxyz/mobile-sdk-alpha/constants/analytics';
|
||||
@@ -13,12 +14,14 @@ import { PrimaryButton } from '@/components/buttons/PrimaryButton';
|
||||
import { BodyText } from '@/components/typography/BodyText';
|
||||
import BlueCheckIcon from '@/images/blue_check.svg';
|
||||
import { useSafeAreaInsets } from '@/mocks/react-native-safe-area-context';
|
||||
import type { RootStackParamList } from '@/navigation';
|
||||
import { black, slate100, slate200, slate500, white } from '@/utils/colors';
|
||||
import { extraYPadding } from '@/utils/constants';
|
||||
|
||||
const AadhaarUploadedSuccessScreen: React.FC = () => {
|
||||
const { bottom } = useSafeAreaInsets();
|
||||
const navigation = useNavigation();
|
||||
const navigation =
|
||||
useNavigation<NativeStackNavigationProp<RootStackParamList>>();
|
||||
const { trackEvent } = useSelfClient();
|
||||
|
||||
return (
|
||||
|
||||
@@ -8,7 +8,7 @@ import { Button, Text, XStack, YStack, ZStack } from 'tamagui';
|
||||
import { BlurView } from '@react-native-community/blur';
|
||||
import { useNavigation } from '@react-navigation/native';
|
||||
|
||||
import { DocumentCatalog, IDDocument } from '@selfxyz/common/utils/types';
|
||||
import type { DocumentCatalog, IDDocument } from '@selfxyz/common/utils/types';
|
||||
|
||||
import IdCardLayout from '@/components/homeScreen/idCard';
|
||||
import { usePassport } from '@/providers/passportDataProvider';
|
||||
|
||||
@@ -6,6 +6,7 @@ import React, { useState } from 'react';
|
||||
import { Platform, ScrollView } from 'react-native';
|
||||
import { Input, YStack } from 'tamagui';
|
||||
import { useNavigation } from '@react-navigation/native';
|
||||
import type { NativeStackNavigationProp } from '@react-navigation/native-stack';
|
||||
|
||||
import { useSelfClient } from '@selfxyz/mobile-sdk-alpha';
|
||||
|
||||
@@ -16,6 +17,7 @@ import { BodyText } from '@/components/typography/BodyText';
|
||||
import Description from '@/components/typography/Description';
|
||||
import { Title } from '@/components/typography/Title';
|
||||
import { ExpandableBottomLayout } from '@/layouts/ExpandableBottomLayout';
|
||||
import type { RootStackParamList } from '@/navigation';
|
||||
import { white } from '@/utils/colors';
|
||||
|
||||
type NFCParams = {
|
||||
@@ -90,7 +92,8 @@ const NFC_METHODS = [
|
||||
];
|
||||
|
||||
const DocumentNFCMethodSelectionScreen: React.FC = () => {
|
||||
const navigation = useNavigation();
|
||||
const navigation =
|
||||
useNavigation<NativeStackNavigationProp<RootStackParamList>>();
|
||||
const [selectedMethod, setSelectedMethod] = useState('standard');
|
||||
const [canValue, setCanValue] = useState('');
|
||||
const [error, setError] = useState('');
|
||||
@@ -130,6 +133,7 @@ const DocumentNFCMethodSelectionScreen: React.FC = () => {
|
||||
if (selectedMethod === 'can') {
|
||||
params.canNumber = canValue;
|
||||
}
|
||||
// Type assertion needed because static navigation doesn't infer optional params
|
||||
navigation.navigate('DocumentNFCScan', params as never);
|
||||
};
|
||||
|
||||
|
||||
@@ -28,6 +28,7 @@ import {
|
||||
useNavigation,
|
||||
useRoute,
|
||||
} from '@react-navigation/native';
|
||||
import type { NativeStackNavigationProp } from '@react-navigation/native-stack';
|
||||
import { CircleHelp } from '@tamagui/lucide-icons';
|
||||
|
||||
import type { PassportData } from '@selfxyz/common/types';
|
||||
@@ -48,6 +49,7 @@ import { useFeedbackAutoHide } from '@/hooks/useFeedbackAutoHide';
|
||||
import useHapticNavigation from '@/hooks/useHapticNavigation';
|
||||
import NFC_IMAGE from '@/images/nfc.png';
|
||||
import { ExpandableBottomLayout } from '@/layouts/ExpandableBottomLayout';
|
||||
import type { RootStackParamList } from '@/navigation';
|
||||
import { useFeedback } from '@/providers/feedbackProvider';
|
||||
import { storePassportData } from '@/providers/passportDataProvider';
|
||||
import { logNFCEvent } from '@/Sentry';
|
||||
@@ -92,7 +94,8 @@ const DocumentNFCScanScreen: React.FC = () => {
|
||||
const selfClient = useSelfClient();
|
||||
const { trackEvent, useMRZStore } = selfClient;
|
||||
|
||||
const navigation = useNavigation();
|
||||
const navigation =
|
||||
useNavigation<NativeStackNavigationProp<RootStackParamList>>();
|
||||
const route = useRoute<DocumentNFCScanRoute>();
|
||||
const { showModal } = useFeedback();
|
||||
useFeedbackAutoHide();
|
||||
@@ -460,12 +463,6 @@ const DocumentNFCScanScreen: React.FC = () => {
|
||||
}
|
||||
};
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const _cancelScanIfRunning = useCallback(async () => {
|
||||
// // TODO: cancel if scanning
|
||||
// setIsNfcSheetOpen(false);
|
||||
}, []);
|
||||
|
||||
useFocusEffect(
|
||||
useCallback(() => {
|
||||
logNFCEvent('info', 'screen_focus', { ...baseContext, stage: 'focus' });
|
||||
|
||||
@@ -79,7 +79,7 @@ const ConfirmBelongingScreen: React.FC<ConfirmBelongingScreenProps> = () => {
|
||||
};
|
||||
}
|
||||
setDocumentMetadata(metadata);
|
||||
} catch (_error) {
|
||||
} catch {
|
||||
// setting defaults on error
|
||||
setDocumentMetadata({
|
||||
documentCategory: 'passport',
|
||||
|
||||
@@ -11,14 +11,17 @@ import {
|
||||
useNavigation,
|
||||
usePreventRemove,
|
||||
} from '@react-navigation/native';
|
||||
import type { NativeStackNavigationProp } from '@react-navigation/native-stack';
|
||||
|
||||
import { DocumentCatalog, IDDocument } from '@selfxyz/common/utils/types';
|
||||
import { DocumentMetadata, useSelfClient } from '@selfxyz/mobile-sdk-alpha';
|
||||
import type { DocumentCatalog, IDDocument } from '@selfxyz/common/utils/types';
|
||||
import type { DocumentMetadata } from '@selfxyz/mobile-sdk-alpha';
|
||||
import { useSelfClient } from '@selfxyz/mobile-sdk-alpha';
|
||||
import { DocumentEvents } from '@selfxyz/mobile-sdk-alpha/constants/analytics';
|
||||
|
||||
import IdCardLayout from '@/components/homeScreen/idCard';
|
||||
import { useAppUpdates } from '@/hooks/useAppUpdates';
|
||||
import useConnectionModal from '@/hooks/useConnectionModal';
|
||||
import type { RootStackParamList } from '@/navigation';
|
||||
import { usePassport } from '@/providers/passportDataProvider';
|
||||
import useUserStore from '@/stores/userStore';
|
||||
import { slate50 } from '@/utils/colors';
|
||||
@@ -27,7 +30,8 @@ import { extraYPadding } from '@/utils/constants';
|
||||
const HomeScreen: React.FC = () => {
|
||||
const selfClient = useSelfClient();
|
||||
useConnectionModal();
|
||||
const navigation = useNavigation();
|
||||
const navigation =
|
||||
useNavigation<NativeStackNavigationProp<RootStackParamList>>();
|
||||
const { setIdDetailsDocumentId } = useUserStore();
|
||||
const { getAllDocuments, loadDocumentCatalog } = usePassport();
|
||||
const [isNewVersionAvailable, showAppUpdateModal, isModalDismissed] =
|
||||
|
||||
@@ -11,9 +11,11 @@ import {
|
||||
} from 'react-native';
|
||||
import { Card, Image, Text, View, XStack, YStack } from 'tamagui';
|
||||
import { useNavigation } from '@react-navigation/native';
|
||||
import type { NativeStackNavigationProp } from '@react-navigation/native-stack';
|
||||
import { CheckSquare2, Wallet, XCircle } from '@tamagui/lucide-icons';
|
||||
|
||||
import { BodyText } from '@/components/typography/BodyText';
|
||||
import type { RootStackParamList } from '@/navigation';
|
||||
import { useProofHistoryStore } from '@/stores/proofHistoryStore';
|
||||
import type { ProofHistory } from '@/stores/proofTypes';
|
||||
import { ProofStatus } from '@/stores/proofTypes';
|
||||
@@ -62,7 +64,8 @@ export const ProofHistoryList: React.FC<ProofHistoryListProps> = ({
|
||||
hasMore,
|
||||
} = useProofHistoryStore();
|
||||
const [refreshing, setRefreshing] = useState(false);
|
||||
const navigation = useNavigation();
|
||||
const navigation =
|
||||
useNavigation<NativeStackNavigationProp<RootStackParamList>>();
|
||||
|
||||
useEffect(() => {
|
||||
initDatabase();
|
||||
|
||||
@@ -12,9 +12,11 @@ import {
|
||||
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
||||
import { Card, Image, Text, View, XStack, YStack } from 'tamagui';
|
||||
import { useNavigation } from '@react-navigation/native';
|
||||
import type { NativeStackNavigationProp } from '@react-navigation/native-stack';
|
||||
import { CheckSquare2, Wallet, XCircle } from '@tamagui/lucide-icons';
|
||||
|
||||
import { BodyText } from '@/components/typography/BodyText';
|
||||
import type { RootStackParamList } from '@/navigation';
|
||||
import { useProofHistoryStore } from '@/stores/proofHistoryStore';
|
||||
import type { ProofHistory } from '@/stores/proofTypes';
|
||||
import { ProofStatus } from '@/stores/proofTypes';
|
||||
@@ -57,7 +59,8 @@ const ProofHistoryScreen: React.FC = () => {
|
||||
hasMore,
|
||||
} = useProofHistoryStore();
|
||||
const [refreshing, setRefreshing] = useState(false);
|
||||
const navigation = useNavigation();
|
||||
const navigation =
|
||||
useNavigation<NativeStackNavigationProp<RootStackParamList>>();
|
||||
const { bottom } = useSafeAreaInsets();
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
import React from 'react';
|
||||
import { YStack } from 'tamagui';
|
||||
import { useNavigation } from '@react-navigation/native';
|
||||
import type { NativeStackNavigationProp } from '@react-navigation/native-stack';
|
||||
|
||||
import { BackupEvents } from '@selfxyz/mobile-sdk-alpha/constants/analytics';
|
||||
|
||||
@@ -14,12 +15,14 @@ import { DelayedLottieView } from '@/components/DelayedLottieView';
|
||||
import Description from '@/components/typography/Description';
|
||||
import { Title } from '@/components/typography/Title';
|
||||
import { ExpandableBottomLayout } from '@/layouts/ExpandableBottomLayout';
|
||||
import type { RootStackParamList } from '@/navigation';
|
||||
import { styles } from '@/screens/verification/ProofRequestStatusScreen';
|
||||
import { black, white } from '@/utils/colors';
|
||||
import { buttonTap } from '@/utils/haptic';
|
||||
|
||||
const AccountVerifiedSuccessScreen: React.FC = ({}) => {
|
||||
const navigation = useNavigation();
|
||||
const navigation =
|
||||
useNavigation<NativeStackNavigationProp<RootStackParamList>>();
|
||||
|
||||
return (
|
||||
<ExpandableBottomLayout.Layout backgroundColor={white}>
|
||||
|
||||
@@ -6,6 +6,7 @@ import React, { useEffect } from 'react';
|
||||
import { StyleSheet } from 'react-native';
|
||||
import { YStack } from 'tamagui';
|
||||
import { useNavigation } from '@react-navigation/native';
|
||||
import type { NativeStackNavigationProp } from '@react-navigation/native-stack';
|
||||
|
||||
import { AppEvents } from '@selfxyz/mobile-sdk-alpha/constants/analytics';
|
||||
|
||||
@@ -15,12 +16,14 @@ import { DelayedLottieView } from '@/components/DelayedLottieView';
|
||||
import Caution from '@/components/typography/Caution';
|
||||
import { SubHeader } from '@/components/typography/SubHeader';
|
||||
import { ExpandableBottomLayout } from '@/layouts/ExpandableBottomLayout';
|
||||
import type { RootStackParamList } from '@/navigation';
|
||||
import { useSettingStore } from '@/stores/settingStore';
|
||||
import { black, white } from '@/utils/colors';
|
||||
import { confirmTap, notificationWarning } from '@/utils/haptic';
|
||||
|
||||
const DisclaimerScreen: React.FC = () => {
|
||||
const navigation = useNavigation();
|
||||
const navigation =
|
||||
useNavigation<NativeStackNavigationProp<RootStackParamList>>();
|
||||
const { dismissPrivacyNote } = useSettingStore();
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
@@ -2,7 +2,8 @@
|
||||
// SPDX-License-Identifier: BUSL-1.1
|
||||
// NOTE: Converts to Apache-2.0 on 2029-06-11 per LICENSE.
|
||||
|
||||
import LottieView, { type LottieViewProps } from 'lottie-react-native';
|
||||
import type { LottieViewProps } from 'lottie-react-native';
|
||||
import LottieView from 'lottie-react-native';
|
||||
import React, { useCallback, useEffect, useRef, useState } from 'react';
|
||||
import { Linking, StyleSheet, View } from 'react-native';
|
||||
import { SystemBars } from 'react-native-edge-to-edge';
|
||||
|
||||
@@ -18,6 +18,7 @@ import type {
|
||||
import { ScrollView, StyleSheet, TouchableOpacity } from 'react-native';
|
||||
import { Image, Text, View, XStack, YStack } from 'tamagui';
|
||||
import { useIsFocused, useNavigation } from '@react-navigation/native';
|
||||
import type { NativeStackNavigationProp } from '@react-navigation/native-stack';
|
||||
import { Eye, EyeOff } from '@tamagui/lucide-icons';
|
||||
|
||||
import type { SelfAppDisclosureConfig } from '@selfxyz/common/utils/appType';
|
||||
@@ -31,6 +32,7 @@ import Disclosures from '@/components/Disclosures';
|
||||
import { BodyText } from '@/components/typography/BodyText';
|
||||
import { Caption } from '@/components/typography/Caption';
|
||||
import { ExpandableBottomLayout } from '@/layouts/ExpandableBottomLayout';
|
||||
import type { RootStackParamList } from '@/navigation';
|
||||
import {
|
||||
setDefaultDocumentTypeIfNeeded,
|
||||
usePassport,
|
||||
@@ -44,7 +46,8 @@ import { buttonTap } from '@/utils/haptic';
|
||||
const ProveScreen: React.FC = () => {
|
||||
const selfClient = useSelfClient();
|
||||
const { trackEvent } = selfClient;
|
||||
const { navigate } = useNavigation();
|
||||
const { navigate } =
|
||||
useNavigation<NativeStackNavigationProp<RootStackParamList>>();
|
||||
const isFocused = useIsFocused();
|
||||
const { useProvingStore, useSelfAppStore } = selfClient;
|
||||
const selectedApp = useSelfAppStore(state => state.selfApp);
|
||||
|
||||
@@ -11,6 +11,7 @@ import {
|
||||
useIsFocused,
|
||||
useNavigation,
|
||||
} from '@react-navigation/native';
|
||||
import type { NativeStackNavigationProp } from '@react-navigation/native-stack';
|
||||
|
||||
import { useSelfClient } from '@selfxyz/mobile-sdk-alpha';
|
||||
import { ProofEvents } from '@selfxyz/mobile-sdk-alpha/constants/analytics';
|
||||
@@ -25,6 +26,7 @@ import useConnectionModal from '@/hooks/useConnectionModal';
|
||||
import useHapticNavigation from '@/hooks/useHapticNavigation';
|
||||
import QRScan from '@/images/icons/qr_code.svg';
|
||||
import { ExpandableBottomLayout } from '@/layouts/ExpandableBottomLayout';
|
||||
import type { RootStackParamList } from '@/navigation';
|
||||
import { black, slate800, white } from '@/utils/colors';
|
||||
import { parseAndValidateUrlParams } from '@/utils/deeplinks';
|
||||
|
||||
@@ -32,7 +34,8 @@ const QRCodeViewFinderScreen: React.FC = () => {
|
||||
const selfClient = useSelfClient();
|
||||
const { trackEvent } = selfClient;
|
||||
const { visible: connectionModalVisible } = useConnectionModal();
|
||||
const navigation = useNavigation();
|
||||
const navigation =
|
||||
useNavigation<NativeStackNavigationProp<RootStackParamList>>();
|
||||
const isFocused = useIsFocused();
|
||||
const [doneScanningQR, setDoneScanningQR] = useState(false);
|
||||
const navigateToProve = useHapticNavigation('Prove');
|
||||
|
||||
@@ -90,9 +90,9 @@ export const useSettingStore = create<SettingsState>()(
|
||||
storage: createJSONStorage(() => AsyncStorage),
|
||||
onRehydrateStorage: () => undefined,
|
||||
partialize: state => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const { hideNetworkModal, setHideNetworkModal, ...persistedState } =
|
||||
state;
|
||||
const persistedState = { ...state };
|
||||
delete (persistedState as Partial<SettingsState>).hideNetworkModal;
|
||||
delete (persistedState as Partial<SettingsState>).setHideNetworkModal;
|
||||
return persistedState;
|
||||
},
|
||||
},
|
||||
|
||||
8
app/src/types/elliptic.d.ts
vendored
8
app/src/types/elliptic.d.ts
vendored
@@ -1,8 +0,0 @@
|
||||
// SPDX-FileCopyrightText: 2025 Social Connect Labs, Inc.
|
||||
// SPDX-License-Identifier: BUSL-1.1
|
||||
// NOTE: Converts to Apache-2.0 on 2029-06-11 per LICENSE.
|
||||
|
||||
declare module 'elliptic' {
|
||||
const elliptic: any;
|
||||
export = elliptic;
|
||||
}
|
||||
@@ -2,12 +2,13 @@
|
||||
// SPDX-License-Identifier: BUSL-1.1
|
||||
// NOTE: Converts to Apache-2.0 on 2029-06-11 per LICENSE.
|
||||
|
||||
import { AppState, type AppStateStatus } from 'react-native';
|
||||
import type { AppStateStatus } from 'react-native';
|
||||
import { AppState } from 'react-native';
|
||||
import { ENABLE_DEBUG_LOGS, MIXPANEL_NFC_PROJECT_TOKEN } from '@env';
|
||||
import NetInfo from '@react-native-community/netinfo';
|
||||
import type { JsonMap, JsonValue } from '@segment/analytics-react-native';
|
||||
|
||||
import { TrackEventParams } from '@selfxyz/mobile-sdk-alpha';
|
||||
import type { TrackEventParams } from '@selfxyz/mobile-sdk-alpha';
|
||||
|
||||
import { createSegmentClient } from '@/Segment';
|
||||
import { PassportReader } from '@/utils/passportReader';
|
||||
@@ -186,25 +187,37 @@ const flushMixpanelEvents = async () => {
|
||||
if (__DEV__) console.log('[Mixpanel] flush skipped - NFC scanning active');
|
||||
return;
|
||||
}
|
||||
|
||||
// Ensure we don't drop events if the native reader isn't available
|
||||
if (!PassportReader?.trackEvent) {
|
||||
if (__DEV__)
|
||||
console.warn('[Mixpanel] flush skipped - NFC module unavailable');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
if (__DEV__) console.log('[Mixpanel] flush');
|
||||
// Send any queued events before flushing
|
||||
while (eventQueue.length > 0) {
|
||||
const evt = eventQueue.shift()!;
|
||||
if (PassportReader.trackEvent) {
|
||||
try {
|
||||
await Promise.resolve(
|
||||
PassportReader.trackEvent(evt.name, evt.properties),
|
||||
);
|
||||
} catch (trackErr) {
|
||||
// Put the event back and abort; we'll retry on the next flush
|
||||
eventQueue.unshift(evt);
|
||||
throw trackErr;
|
||||
}
|
||||
}
|
||||
if (PassportReader.flush) await Promise.resolve(PassportReader.flush());
|
||||
if (PassportReader.flush) {
|
||||
await Promise.resolve(PassportReader.flush());
|
||||
}
|
||||
// Only reset event count after successful send/flush
|
||||
eventCount = 0;
|
||||
} catch (err) {
|
||||
if (__DEV__) console.warn('Mixpanel flush failed', err);
|
||||
// re-queue on failure
|
||||
if (typeof err !== 'undefined') {
|
||||
// no-op, events are already queued if failure happened before flush
|
||||
}
|
||||
// Events have been re-queued on failure, so they're not lost
|
||||
}
|
||||
};
|
||||
|
||||
@@ -277,7 +290,7 @@ export const trackNfcEvent = async (
|
||||
}
|
||||
|
||||
try {
|
||||
if (PassportReader.trackEvent) {
|
||||
if (PassportReader && PassportReader.trackEvent) {
|
||||
await Promise.resolve(PassportReader.trackEvent(name, properties));
|
||||
}
|
||||
eventCount++;
|
||||
|
||||
@@ -7,7 +7,7 @@ import { Linking, Platform } from 'react-native';
|
||||
|
||||
import { countries } from '@selfxyz/common/constants/countries';
|
||||
import type { IdDocInput } from '@selfxyz/common/utils';
|
||||
import { SelfClient } from '@selfxyz/mobile-sdk-alpha';
|
||||
import type { SelfClient } from '@selfxyz/mobile-sdk-alpha';
|
||||
|
||||
import { navigationRef } from '@/navigation';
|
||||
import useUserStore from '@/stores/userStore';
|
||||
|
||||
@@ -2,13 +2,14 @@
|
||||
// SPDX-License-Identifier: BUSL-1.1
|
||||
// NOTE: Converts to Apache-2.0 on 2029-06-11 per LICENSE.
|
||||
|
||||
import Keychain, {
|
||||
type ACCESS_CONTROL,
|
||||
type ACCESSIBLE,
|
||||
import type {
|
||||
ACCESS_CONTROL,
|
||||
ACCESSIBLE,
|
||||
GetOptions,
|
||||
type SECURITY_LEVEL,
|
||||
SECURITY_LEVEL,
|
||||
SetOptions,
|
||||
} from 'react-native-keychain';
|
||||
import Keychain from 'react-native-keychain';
|
||||
|
||||
/**
|
||||
* Security configuration for keychain operations
|
||||
@@ -45,7 +46,7 @@ export async function checkBiometricsAvailable(): Promise<boolean> {
|
||||
const rnBiometrics = new ReactNativeBiometrics();
|
||||
const { available } = await rnBiometrics.isSensorAvailable();
|
||||
return available;
|
||||
} catch (_error) {
|
||||
} catch {
|
||||
console.log('Biometrics not available');
|
||||
return false;
|
||||
}
|
||||
@@ -64,7 +65,7 @@ export async function checkPasscodeAvailable(): Promise<boolean> {
|
||||
// Clean up test entry
|
||||
await Keychain.resetGenericPassword({ service: testService });
|
||||
return true;
|
||||
} catch (_error) {
|
||||
} catch {
|
||||
console.log('Device passcode not available');
|
||||
return false;
|
||||
}
|
||||
@@ -187,7 +188,7 @@ export async function getMaxSecurityLevel(): Promise<SECURITY_LEVEL> {
|
||||
// Try to get the device's security level
|
||||
const securityLevel = await Keychain.getSecurityLevel();
|
||||
return securityLevel || Keychain.SECURITY_LEVEL.ANY;
|
||||
} catch (_error) {
|
||||
} catch {
|
||||
console.log('Could not determine security level, defaulting to ANY');
|
||||
return Keychain.SECURITY_LEVEL.ANY;
|
||||
}
|
||||
|
||||
@@ -17,13 +17,18 @@ type Locale = {
|
||||
|
||||
export function getLocales(): Locale[] {
|
||||
return navigator.languages.map(lang => {
|
||||
const locale = new Intl.Locale(lang);
|
||||
type LocaleWithTextInfo = Intl.Locale & {
|
||||
textInfo?: {
|
||||
direction?: string;
|
||||
};
|
||||
};
|
||||
|
||||
const locale = new Intl.Locale(lang) as LocaleWithTextInfo;
|
||||
return {
|
||||
languageCode: locale.language,
|
||||
countryCode: locale.region ?? '',
|
||||
scriptCode: locale.script,
|
||||
languageTag: lang,
|
||||
// @ts-expect-error this not in type but appears to be in browsers
|
||||
isRTL: locale.textInfo?.direction === 'rtl',
|
||||
};
|
||||
});
|
||||
|
||||
@@ -2,31 +2,24 @@
|
||||
// SPDX-License-Identifier: BUSL-1.1
|
||||
// NOTE: Converts to Apache-2.0 on 2029-06-11 per LICENSE.
|
||||
|
||||
import type { configLoggerType } from 'react-native-logs';
|
||||
import { logger } from 'react-native-logs';
|
||||
import {
|
||||
type configLoggerType,
|
||||
type defLvlType,
|
||||
logger,
|
||||
type transportFunctionType,
|
||||
} from 'react-native-logs';
|
||||
|
||||
import { interceptConsole } from '@/utils/logger/consoleInterceptor';
|
||||
import { lokiTransport } from '@/utils/logger/lokiTransport';
|
||||
import { setupNativeLoggerBridge } from '@/utils/logger/nativeLoggerBridge';
|
||||
|
||||
export {
|
||||
AppLogger,
|
||||
AuthLogger,
|
||||
BackupLogger,
|
||||
DocumentLogger,
|
||||
Logger,
|
||||
MockDataLogger,
|
||||
NfcLogger,
|
||||
NotificationLogger,
|
||||
PassportLogger,
|
||||
ProofLogger,
|
||||
SettingsLogger,
|
||||
};
|
||||
|
||||
const defaultConfig: configLoggerType<any, any> = {
|
||||
const defaultConfig: configLoggerType<
|
||||
transportFunctionType<object> | transportFunctionType<object>[],
|
||||
defLvlType
|
||||
> = {
|
||||
enabled: __DEV__ ? false : true,
|
||||
severity: __DEV__ ? 'debug' : 'warn', //TODO configure this using remote-config
|
||||
transport: [lokiTransport],
|
||||
transport: [lokiTransport as unknown as transportFunctionType<object>],
|
||||
transportOptions: {
|
||||
colors: {
|
||||
info: 'blueBright',
|
||||
@@ -42,6 +35,9 @@ const defaultConfig: configLoggerType<any, any> = {
|
||||
|
||||
const Logger = logger.createLogger(defaultConfig);
|
||||
|
||||
type RootLogger = typeof Logger;
|
||||
type LoggerExtension = ReturnType<RootLogger['extend']>;
|
||||
|
||||
// loggers based on src/consts/analytics.ts
|
||||
const AppLogger = Logger.extend('APP');
|
||||
const NotificationLogger = Logger.extend('NOTIFICATION');
|
||||
@@ -60,7 +56,7 @@ const NfcLogger = Logger.extend('NFC');
|
||||
interceptConsole(AppLogger);
|
||||
|
||||
// Define log levels
|
||||
export const logLevels = {
|
||||
const logLevels = {
|
||||
debug: 0,
|
||||
info: 1,
|
||||
warn: 2,
|
||||
@@ -70,3 +66,20 @@ export const logLevels = {
|
||||
// Initialize native logger bridge after all loggers are defined
|
||||
// This avoids module cycle by injecting dependencies instead of importing them
|
||||
setupNativeLoggerBridge({ AppLogger, NfcLogger, Logger });
|
||||
|
||||
export type { LoggerExtension, RootLogger };
|
||||
|
||||
export {
|
||||
AppLogger,
|
||||
AuthLogger,
|
||||
BackupLogger,
|
||||
DocumentLogger,
|
||||
Logger,
|
||||
MockDataLogger,
|
||||
NfcLogger,
|
||||
NotificationLogger,
|
||||
PassportLogger,
|
||||
ProofLogger,
|
||||
SettingsLogger,
|
||||
logLevels,
|
||||
};
|
||||
|
||||
@@ -11,28 +11,35 @@ const originalConsole = {
|
||||
debug: console.debug,
|
||||
};
|
||||
|
||||
const interceptConsole = (appLogger: any) => {
|
||||
console.log = (...args: any[]) => {
|
||||
type LoggerMethods = {
|
||||
debug: (...args: unknown[]) => void;
|
||||
info: (...args: unknown[]) => void;
|
||||
warn: (...args: unknown[]) => void;
|
||||
error: (...args: unknown[]) => void;
|
||||
};
|
||||
|
||||
const interceptConsole = (appLogger: LoggerMethods) => {
|
||||
console.log = (...args: unknown[]) => {
|
||||
appLogger.info(...args);
|
||||
originalConsole.log(...args);
|
||||
};
|
||||
|
||||
console.info = (...args: any[]) => {
|
||||
console.info = (...args: unknown[]) => {
|
||||
appLogger.info(...args);
|
||||
originalConsole.info(...args);
|
||||
};
|
||||
|
||||
console.warn = (...args: any[]) => {
|
||||
console.warn = (...args: unknown[]) => {
|
||||
appLogger.warn(...args);
|
||||
originalConsole.warn(...args);
|
||||
};
|
||||
|
||||
console.error = (...args: any[]) => {
|
||||
console.error = (...args: unknown[]) => {
|
||||
appLogger.error(...args);
|
||||
originalConsole.error(...args);
|
||||
};
|
||||
|
||||
console.debug = (...args: any[]) => {
|
||||
console.debug = (...args: unknown[]) => {
|
||||
appLogger.debug(...args);
|
||||
originalConsole.debug(...args);
|
||||
};
|
||||
|
||||
@@ -135,7 +135,7 @@ registerDocumentChangeCallback((isMock: boolean) => {
|
||||
isCurrentPassportMockFlag = isMock;
|
||||
});
|
||||
|
||||
export const cleanupLokiTransport = () => {
|
||||
const cleanupLokiTransport = () => {
|
||||
try {
|
||||
appStateSubscription.remove?.();
|
||||
} catch {}
|
||||
@@ -143,7 +143,7 @@ export const cleanupLokiTransport = () => {
|
||||
};
|
||||
|
||||
// Export flush function for manual flushing if needed
|
||||
export const flushLokiTransport = () => {
|
||||
const flushLokiTransport = () => {
|
||||
if (batch.length > 0) {
|
||||
sendBatch([...batch], 'default');
|
||||
batch = [];
|
||||
@@ -165,8 +165,10 @@ const appStateSubscription = AppState.addEventListener(
|
||||
handleAppStateChange,
|
||||
);
|
||||
|
||||
type LokiTransportOptions = Record<string, never>;
|
||||
|
||||
// Create react-native-logs transport function
|
||||
export const lokiTransport: transportFunctionType<any> = props => {
|
||||
const lokiTransport: transportFunctionType<LokiTransportOptions> = props => {
|
||||
const { msg, rawMsg, level, extension } = props;
|
||||
|
||||
if (isCurrentPassportMockFlag) {
|
||||
@@ -189,7 +191,12 @@ export const lokiTransport: transportFunctionType<any> = props => {
|
||||
}
|
||||
|
||||
// Create the log object
|
||||
const logObject: any = {
|
||||
const logObject: {
|
||||
level: string;
|
||||
message: string;
|
||||
timestamp: string;
|
||||
data?: unknown;
|
||||
} = {
|
||||
level: level.text,
|
||||
message: actualMessage,
|
||||
timestamp,
|
||||
@@ -209,3 +216,10 @@ export const lokiTransport: transportFunctionType<any> = props => {
|
||||
|
||||
addToBatch(entry, namespace);
|
||||
};
|
||||
|
||||
export {
|
||||
type LokiTransportOptions,
|
||||
cleanupLokiTransport,
|
||||
flushLokiTransport,
|
||||
lokiTransport,
|
||||
};
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
|
||||
import { NativeEventEmitter, NativeModules, Platform } from 'react-native';
|
||||
|
||||
import type { LoggerExtension, RootLogger } from '@/utils/logger';
|
||||
|
||||
// Remove direct imports to avoid module cycle
|
||||
// Dependencies will be injected via setupNativeLoggerBridge
|
||||
|
||||
@@ -11,21 +13,21 @@ interface NativeLogEvent {
|
||||
level: 'debug' | 'info' | 'warn' | 'error';
|
||||
category: string;
|
||||
message: string;
|
||||
data?: any;
|
||||
data?: unknown;
|
||||
}
|
||||
|
||||
let eventEmitter: NativeEventEmitter | null = null;
|
||||
let isInitialized = false;
|
||||
let injectedLoggers: {
|
||||
AppLogger: any;
|
||||
NfcLogger: any;
|
||||
Logger: any;
|
||||
AppLogger: LoggerExtension;
|
||||
NfcLogger: LoggerExtension;
|
||||
Logger: RootLogger;
|
||||
} | null = null;
|
||||
|
||||
const setupNativeLoggerBridge = (loggers: {
|
||||
AppLogger: any;
|
||||
NfcLogger: any;
|
||||
Logger: any;
|
||||
AppLogger: LoggerExtension;
|
||||
NfcLogger: LoggerExtension;
|
||||
Logger: RootLogger;
|
||||
}) => {
|
||||
if (isInitialized) return;
|
||||
|
||||
@@ -71,7 +73,7 @@ const handleNativeLogEvent = (event: NativeLogEvent) => {
|
||||
const { level, category, message, data } = event;
|
||||
|
||||
// Route to appropriate logger based on category
|
||||
let logger: any;
|
||||
let logger: LoggerExtension;
|
||||
switch (category.toLowerCase()) {
|
||||
case 'nfc':
|
||||
logger = injectedLoggers.NfcLogger;
|
||||
|
||||
@@ -10,25 +10,11 @@ import type { NFCScanContext } from '@selfxyz/mobile-sdk-alpha';
|
||||
|
||||
import { logNFCEvent } from '@/Sentry';
|
||||
import {
|
||||
PassportReader,
|
||||
type AndroidScanResponse,
|
||||
reset,
|
||||
scan as scanDocument,
|
||||
} from '@/utils/passportReader';
|
||||
|
||||
interface AndroidScanResponse {
|
||||
mrz: string;
|
||||
eContent: string;
|
||||
encryptedDigest: string;
|
||||
_photo: string;
|
||||
_digestAlgorithm: string;
|
||||
_signerInfoDigestAlgorithm: string;
|
||||
_digestEncryptionAlgorithm: string;
|
||||
_LDSVersion: string;
|
||||
_unicodeVersion: string;
|
||||
encapContent: string;
|
||||
documentSigningCertificate: string;
|
||||
dataGroupHashes: string;
|
||||
}
|
||||
import { PassportReader } from '@/utils/passportReader';
|
||||
|
||||
interface Inputs {
|
||||
passportNumber: string;
|
||||
@@ -44,6 +30,11 @@ interface Inputs {
|
||||
userId?: string;
|
||||
}
|
||||
|
||||
interface DataGroupHash {
|
||||
sodHash?: string;
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
export const parseScanResponse = (response: unknown) => {
|
||||
return Platform.OS === 'android'
|
||||
? handleResponseAndroid(response as AndroidScanResponse)
|
||||
@@ -108,7 +99,39 @@ const scanIOS = async (
|
||||
inputs: Inputs,
|
||||
context: Omit<NFCScanContext, 'stage'>,
|
||||
) => {
|
||||
if (!PassportReader?.scanPassport) {
|
||||
// Prefer direct native scanPassport when available (tests stub this directly)
|
||||
const iosReader = PassportReader as unknown as {
|
||||
scanPassport?: (
|
||||
passportNumber: string,
|
||||
dateOfBirth: string,
|
||||
dateOfExpiry: string,
|
||||
canNumber: string,
|
||||
useCan: boolean,
|
||||
skipPACE: boolean,
|
||||
skipCA: boolean,
|
||||
extendedMode: boolean,
|
||||
usePacePolling: boolean,
|
||||
sessionId: string,
|
||||
) => Promise<unknown>;
|
||||
} | null;
|
||||
|
||||
if (iosReader?.scanPassport) {
|
||||
return await iosReader.scanPassport(
|
||||
inputs.passportNumber,
|
||||
inputs.dateOfBirth,
|
||||
inputs.dateOfExpiry,
|
||||
inputs.canNumber ?? '',
|
||||
inputs.useCan ?? false,
|
||||
inputs.skipPACE ?? false,
|
||||
inputs.skipCA ?? false,
|
||||
inputs.extendedMode ?? false,
|
||||
inputs.usePacePolling ?? false,
|
||||
inputs.sessionId,
|
||||
);
|
||||
}
|
||||
|
||||
// Fallback to normalized cross-platform scan
|
||||
if (!scanDocument) {
|
||||
console.warn(
|
||||
'iOS passport scanner is not available - native module failed to load',
|
||||
);
|
||||
@@ -123,34 +146,56 @@ const scanIOS = async (
|
||||
);
|
||||
}
|
||||
|
||||
return await Promise.resolve(
|
||||
PassportReader.scanPassport(
|
||||
inputs.passportNumber,
|
||||
inputs.dateOfBirth,
|
||||
inputs.dateOfExpiry,
|
||||
inputs.canNumber ?? '',
|
||||
inputs.useCan ?? false,
|
||||
inputs.skipPACE ?? false,
|
||||
inputs.skipCA ?? false,
|
||||
inputs.extendedMode ?? false,
|
||||
inputs.usePacePolling ?? false,
|
||||
inputs.sessionId,
|
||||
),
|
||||
);
|
||||
return await scanDocument({
|
||||
documentNumber: inputs.passportNumber,
|
||||
dateOfBirth: inputs.dateOfBirth,
|
||||
dateOfExpiry: inputs.dateOfExpiry,
|
||||
canNumber: inputs.canNumber ?? '',
|
||||
useCan: inputs.useCan ?? false,
|
||||
skipPACE: inputs.skipPACE ?? false,
|
||||
skipCA: inputs.skipCA ?? false,
|
||||
extendedMode: inputs.extendedMode ?? false,
|
||||
usePacePolling: inputs.usePacePolling ?? false,
|
||||
sessionId: inputs.sessionId,
|
||||
});
|
||||
};
|
||||
|
||||
const handleResponseIOS = (response: unknown) => {
|
||||
const parsed = JSON.parse(String(response));
|
||||
const dgHashesObj = JSON.parse(parsed?.dataGroupHashes);
|
||||
const dg1HashString = dgHashesObj?.DG1?.sodHash;
|
||||
const dg1Hash = Array.from(Buffer.from(dg1HashString, 'hex'));
|
||||
const dg2HashString = dgHashesObj?.DG2?.sodHash;
|
||||
const dg2Hash = Array.from(Buffer.from(dg2HashString, 'hex'));
|
||||
// Support string or object response
|
||||
const parsed: Record<string, unknown> =
|
||||
typeof response === 'string'
|
||||
? (JSON.parse(response) as Record<string, unknown>)
|
||||
: ((response as Record<string, unknown>) ?? {});
|
||||
|
||||
const eContentBase64 = parsed?.eContentBase64;
|
||||
const signedAttributes = parsed?.signedAttributes;
|
||||
const mrz = parsed?.passportMRZ;
|
||||
const signatureBase64 = parsed?.signatureBase64;
|
||||
const dgHashesObj = JSON.parse(
|
||||
String(parsed?.dataGroupHashes ?? '{}'),
|
||||
) as Record<string, DataGroupHash>;
|
||||
const dg1HashString = dgHashesObj?.DG1?.sodHash as string | undefined;
|
||||
const dg2HashString = dgHashesObj?.DG2?.sodHash as string | undefined;
|
||||
|
||||
const mrz = parsed?.passportMRZ as string | undefined;
|
||||
if (!mrz || typeof mrz !== 'string') {
|
||||
throw new Error('Invalid iOS NFC response: missing passportMRZ');
|
||||
}
|
||||
|
||||
const isHex = (s: string) => /^[0-9a-fA-F]*$/.test(s);
|
||||
if (dg1HashString !== undefined && !isHex(dg1HashString)) {
|
||||
throw new Error('Invalid DG1 sodHash hex string');
|
||||
}
|
||||
if (dg2HashString !== undefined && !isHex(String(dg2HashString))) {
|
||||
throw new Error('Invalid DG2 sodHash hex string');
|
||||
}
|
||||
|
||||
const dg1Hash = dg1HashString
|
||||
? Array.from(Buffer.from(dg1HashString, 'hex'))
|
||||
: [];
|
||||
const dg2Hash = dg2HashString
|
||||
? Array.from(Buffer.from(String(dg2HashString), 'hex'))
|
||||
: [];
|
||||
|
||||
const eContentBase64 = parsed?.eContentBase64 as string | undefined;
|
||||
const signedAttributes = parsed?.signedAttributes as string | undefined;
|
||||
const signatureBase64 = parsed?.signatureBase64 as string | undefined;
|
||||
// const _dataGroupsPresent = parsed?.dataGroupsPresent;
|
||||
// const _placeOfBirth = parsed?.placeOfBirth;
|
||||
// const _activeAuthenticationPassed = parsed?.activeAuthenticationPassed;
|
||||
@@ -160,25 +205,32 @@ const handleResponseIOS = (response: unknown) => {
|
||||
// const passportPhoto = parsed?.passportPhoto;
|
||||
// const _encapsulatedContentDigestAlgorithm =
|
||||
// parsed?.encapsulatedContentDigestAlgorithm;
|
||||
const documentSigningCertificate = parsed?.documentSigningCertificate;
|
||||
const pem = JSON.parse(documentSigningCertificate).PEM.replace(/\n/g, '');
|
||||
const eContentArray = Array.from(Buffer.from(signedAttributes, 'base64'));
|
||||
const documentSigningCertificate = parsed?.documentSigningCertificate as
|
||||
| string
|
||||
| undefined;
|
||||
const pem = JSON.parse(String(documentSigningCertificate)).PEM.replace(
|
||||
/\n/g,
|
||||
'',
|
||||
);
|
||||
const eContentArray = Array.from(
|
||||
Buffer.from(String(signedAttributes ?? ''), 'base64'),
|
||||
);
|
||||
const signedEContentArray = eContentArray.map(byte =>
|
||||
byte > 127 ? byte - 256 : byte,
|
||||
);
|
||||
|
||||
const concatenatedDataHashesArray = Array.from(
|
||||
Buffer.from(eContentBase64, 'base64'),
|
||||
Buffer.from(String(eContentBase64 ?? ''), 'base64'),
|
||||
);
|
||||
const concatenatedDataHashesArraySigned = concatenatedDataHashesArray.map(
|
||||
byte => (byte > 127 ? byte - 256 : byte),
|
||||
);
|
||||
|
||||
const encryptedDigestArray = Array.from(
|
||||
Buffer.from(signatureBase64, 'base64'),
|
||||
Buffer.from(String(signatureBase64 ?? ''), 'base64'),
|
||||
).map(byte => (byte > 127 ? byte - 256 : byte));
|
||||
|
||||
const document_type = mrz.length === 88 ? 'passport' : 'id_card';
|
||||
const document_type = String(mrz).length === 88 ? 'passport' : 'id_card';
|
||||
|
||||
return {
|
||||
mrz,
|
||||
@@ -222,7 +274,7 @@ const handleResponseAndroid = (response: AndroidScanResponse): PassportData => {
|
||||
'-----END CERTIFICATE-----';
|
||||
|
||||
const dgPresents = Object.keys(dgHashesObj)
|
||||
.map(key => parseInt(key)) // eslint-disable-line radix
|
||||
.map(key => parseInt(key, 10))
|
||||
.filter(num => !isNaN(num))
|
||||
.sort((a, b) => a - b);
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ export interface RemoteMessage {
|
||||
title?: string;
|
||||
body?: string;
|
||||
};
|
||||
[key: string]: any;
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
export const API_URL = 'https://notification.self.xyz';
|
||||
|
||||
@@ -3,12 +3,10 @@
|
||||
// NOTE: Converts to Apache-2.0 on 2029-06-11 per LICENSE.
|
||||
|
||||
import { PermissionsAndroid, Platform } from 'react-native';
|
||||
import type { FirebaseMessagingTypes } from '@react-native-firebase/messaging';
|
||||
import messaging from '@react-native-firebase/messaging';
|
||||
|
||||
import type {
|
||||
DeviceTokenRegistration,
|
||||
RemoteMessage,
|
||||
} from '@/utils/notifications/notificationService.shared';
|
||||
import type { DeviceTokenRegistration } from '@/utils/notifications/notificationService.shared';
|
||||
import {
|
||||
API_URL,
|
||||
API_URL_STAGING,
|
||||
@@ -121,13 +119,13 @@ export async function requestNotificationPermission(): Promise<boolean> {
|
||||
|
||||
export function setupNotifications(): () => void {
|
||||
messaging().setBackgroundMessageHandler(
|
||||
async (remoteMessage: RemoteMessage) => {
|
||||
async (remoteMessage: FirebaseMessagingTypes.RemoteMessage) => {
|
||||
log('Message handled in the background!', remoteMessage);
|
||||
},
|
||||
);
|
||||
|
||||
const unsubscribeForeground = messaging().onMessage(
|
||||
async (remoteMessage: RemoteMessage) => {
|
||||
async (remoteMessage: FirebaseMessagingTypes.RemoteMessage) => {
|
||||
log('Foreground message received:', remoteMessage);
|
||||
},
|
||||
);
|
||||
|
||||
@@ -15,78 +15,151 @@ type ScanOptions = {
|
||||
extendedMode?: boolean;
|
||||
usePacePolling?: boolean;
|
||||
sessionId?: string;
|
||||
quality?: number;
|
||||
};
|
||||
|
||||
export interface AndroidScanResponse {
|
||||
mrz: string;
|
||||
eContent: string;
|
||||
encryptedDigest: string;
|
||||
photo: {
|
||||
base64: string;
|
||||
};
|
||||
digestAlgorithm: string;
|
||||
signerInfoDigestAlgorithm: string;
|
||||
digestEncryptionAlgorithm: string;
|
||||
LDSVersion: string;
|
||||
unicodeVersion: string;
|
||||
encapContent: string;
|
||||
documentSigningCertificate: string;
|
||||
dataGroupHashes: string;
|
||||
}
|
||||
|
||||
type AndroidPassportReaderModule = {
|
||||
configure?: (token: string, enableDebug?: boolean) => void;
|
||||
trackEvent?: (name: string, properties?: Record<string, unknown>) => void;
|
||||
flush?: () => void | Promise<void>;
|
||||
reset?: () => void;
|
||||
scan?: (options: ScanOptions) => Promise<AndroidScanResponse>;
|
||||
};
|
||||
|
||||
type IOSPassportReaderModule = {
|
||||
configure?: (token: string, enableDebug?: boolean) => void;
|
||||
trackEvent?: (name: string, properties?: Record<string, unknown>) => void;
|
||||
flush?: () => void | Promise<void>;
|
||||
scanPassport?: (
|
||||
passportNumber: string,
|
||||
dateOfBirth: string,
|
||||
dateOfExpiry: string,
|
||||
canNumber: string,
|
||||
useCan: boolean,
|
||||
skipPACE: boolean,
|
||||
skipCA: boolean,
|
||||
extendedMode: boolean,
|
||||
usePacePolling: boolean,
|
||||
sessionId: string,
|
||||
) => Promise<string | Record<string, unknown>>;
|
||||
};
|
||||
|
||||
type PassportReaderModule =
|
||||
| AndroidPassportReaderModule
|
||||
| IOSPassportReaderModule;
|
||||
|
||||
// Platform-specific PassportReader implementation
|
||||
let PassportReader: any;
|
||||
let reset: any;
|
||||
let scan: ((options: ScanOptions) => Promise<any>) | null;
|
||||
let PassportReader: PassportReaderModule | null = null;
|
||||
let scan: ((options: ScanOptions) => Promise<unknown>) | null = null;
|
||||
let resetImpl: (() => void) | undefined;
|
||||
|
||||
if (Platform.OS === 'android') {
|
||||
// Android uses the react-native-passport-reader package
|
||||
try {
|
||||
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
||||
const AndroidPassportReader = require('react-native-passport-reader');
|
||||
const AndroidPassportReader = NativeModules.RNPassportReader as
|
||||
| AndroidPassportReaderModule
|
||||
| undefined;
|
||||
|
||||
if (AndroidPassportReader) {
|
||||
PassportReader = AndroidPassportReader;
|
||||
reset = AndroidPassportReader.reset;
|
||||
scan = AndroidPassportReader.scan;
|
||||
} catch (error) {
|
||||
console.warn('Failed to load Android PassportReader:', error);
|
||||
PassportReader = null;
|
||||
reset = null;
|
||||
scan = null;
|
||||
}
|
||||
} else if (Platform.OS === 'ios') {
|
||||
// iOS uses the native PassportReader module directly
|
||||
PassportReader = NativeModules.PassportReader || null;
|
||||
|
||||
// iOS doesn't have reset function
|
||||
reset = null;
|
||||
|
||||
// iOS uses scanPassport method with different signature
|
||||
scan = PassportReader?.scanPassport
|
||||
? async (options: ScanOptions) => {
|
||||
resetImpl = () => AndroidPassportReader.reset?.();
|
||||
if (AndroidPassportReader.scan) {
|
||||
const androidScan = AndroidPassportReader.scan.bind(
|
||||
AndroidPassportReader,
|
||||
);
|
||||
scan = async options => {
|
||||
const {
|
||||
documentNumber,
|
||||
dateOfBirth,
|
||||
dateOfExpiry,
|
||||
canNumber = '',
|
||||
useCan = false,
|
||||
skipPACE = false,
|
||||
skipCA = false,
|
||||
extendedMode = false,
|
||||
usePacePolling = true,
|
||||
sessionId = '',
|
||||
quality = 1,
|
||||
} = options;
|
||||
|
||||
const result = await PassportReader.scanPassport(
|
||||
return androidScan({
|
||||
documentNumber,
|
||||
dateOfBirth,
|
||||
dateOfExpiry,
|
||||
canNumber,
|
||||
useCan,
|
||||
skipPACE,
|
||||
skipCA,
|
||||
extendedMode,
|
||||
usePacePolling,
|
||||
sessionId,
|
||||
);
|
||||
// iOS native returns a JSON string; normalize to object.
|
||||
try {
|
||||
return typeof result === 'string' ? JSON.parse(result) : result;
|
||||
} catch {
|
||||
return result;
|
||||
}
|
||||
quality,
|
||||
});
|
||||
};
|
||||
}
|
||||
} else {
|
||||
console.warn('Failed to load Android PassportReader: module not found');
|
||||
}
|
||||
} else if (Platform.OS === 'ios') {
|
||||
// iOS uses the native PassportReader module directly
|
||||
const IOSPassportReader = NativeModules.PassportReader as
|
||||
| IOSPassportReaderModule
|
||||
| undefined;
|
||||
|
||||
PassportReader = IOSPassportReader ?? null;
|
||||
|
||||
// iOS uses scanPassport method with different signature
|
||||
if (IOSPassportReader?.scanPassport) {
|
||||
const scanPassport = IOSPassportReader.scanPassport.bind(IOSPassportReader);
|
||||
scan = async options => {
|
||||
const {
|
||||
documentNumber,
|
||||
dateOfBirth,
|
||||
dateOfExpiry,
|
||||
canNumber = '',
|
||||
useCan = false,
|
||||
skipPACE = false,
|
||||
skipCA = false,
|
||||
extendedMode = false,
|
||||
usePacePolling = true,
|
||||
sessionId = '',
|
||||
} = options;
|
||||
|
||||
const result = await scanPassport(
|
||||
documentNumber,
|
||||
dateOfBirth,
|
||||
dateOfExpiry,
|
||||
canNumber,
|
||||
useCan,
|
||||
skipPACE,
|
||||
skipCA,
|
||||
extendedMode,
|
||||
usePacePolling,
|
||||
sessionId,
|
||||
);
|
||||
// iOS native returns a JSON string; normalize to object.
|
||||
try {
|
||||
return typeof result === 'string' ? JSON.parse(result) : result;
|
||||
} catch {
|
||||
return result;
|
||||
}
|
||||
: null;
|
||||
};
|
||||
}
|
||||
} else {
|
||||
// Unsupported platform
|
||||
console.warn('PassportReader: Unsupported platform');
|
||||
PassportReader = null;
|
||||
reset = null;
|
||||
scan = null;
|
||||
}
|
||||
|
||||
const reset = () => {
|
||||
resetImpl?.();
|
||||
};
|
||||
|
||||
export type { ScanOptions };
|
||||
export { PassportReader, reset, scan };
|
||||
export default PassportReader;
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// SPDX-License-Identifier: BUSL-1.1
|
||||
// NOTE: Converts to Apache-2.0 on 2029-06-11 per LICENSE.
|
||||
|
||||
import {
|
||||
import type {
|
||||
provingMachineCircuitType,
|
||||
ProvingStateType,
|
||||
} from '@selfxyz/mobile-sdk-alpha';
|
||||
|
||||
@@ -46,7 +46,7 @@ export async function checkAndUpdateRegistrationStates(
|
||||
const logValidationError = (
|
||||
error: string,
|
||||
data?: PassportData,
|
||||
additionalContext?: Record<string, any>,
|
||||
additionalContext?: Record<string, unknown>,
|
||||
) => {
|
||||
anyFailureReported = true;
|
||||
trackEvent(DocumentEvents.VALIDATE_DOCUMENT_FAILED, {
|
||||
@@ -176,25 +176,33 @@ export async function checkAndUpdateRegistrationStates(
|
||||
|
||||
// UNUSED?
|
||||
|
||||
interface MigratedPassportData extends Omit<PassportData, 'documentType'> {
|
||||
documentType?: string;
|
||||
}
|
||||
type MigratedPassportData = Omit<PassportData, 'documentCategory' | 'mock'> & {
|
||||
documentCategory?: PassportData['documentCategory'];
|
||||
mock?: PassportData['mock'];
|
||||
};
|
||||
|
||||
export function migratePassportData(passportData: PassportData): PassportData {
|
||||
const migratedData: MigratedPassportData = { ...passportData };
|
||||
if (!('documentCategory' in migratedData) || !('mock' in migratedData)) {
|
||||
const documentType = (migratedData as any).documentType;
|
||||
if (documentType) {
|
||||
(migratedData as any).mock = documentType.startsWith('mock');
|
||||
(migratedData as any).documentCategory = documentType.includes('passport')
|
||||
? 'passport'
|
||||
: 'id_card';
|
||||
} else {
|
||||
(migratedData as any).documentType = 'passport';
|
||||
(migratedData as any).documentCategory = 'passport';
|
||||
(migratedData as any).mock = false;
|
||||
}
|
||||
// console.log('Migrated passport data:', migratedData);
|
||||
}
|
||||
return migratedData as PassportData;
|
||||
|
||||
const existingDocumentType = migratedData.documentType;
|
||||
const inferredMock =
|
||||
migratedData.mock ?? existingDocumentType?.startsWith('mock');
|
||||
const inferredCategory =
|
||||
migratedData.documentCategory ??
|
||||
(existingDocumentType?.includes('passport') ? 'passport' : 'id_card');
|
||||
|
||||
const normalizedDocumentType = existingDocumentType ?? 'passport';
|
||||
const normalizedMock = inferredMock ?? false;
|
||||
const normalizedCategory =
|
||||
inferredCategory ??
|
||||
(normalizedDocumentType.includes('passport') ? 'passport' : 'id_card');
|
||||
|
||||
const normalizedData: PassportData = {
|
||||
...migratedData,
|
||||
documentType: normalizedDocumentType,
|
||||
mock: normalizedMock,
|
||||
documentCategory: normalizedCategory,
|
||||
};
|
||||
|
||||
return normalizedData;
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// SPDX-License-Identifier: BUSL-1.1
|
||||
// NOTE: Converts to Apache-2.0 on 2029-06-11 per LICENSE.
|
||||
|
||||
import { ReactNode } from 'react';
|
||||
import type { ReactNode } from 'react';
|
||||
import { checkVersion } from 'react-native-check-version';
|
||||
import { useNavigation } from '@react-navigation/native';
|
||||
import { act, renderHook, waitFor } from '@testing-library/react-native';
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
import { Linking } from 'react-native';
|
||||
|
||||
import { SelfClient } from '@selfxyz/mobile-sdk-alpha';
|
||||
import type { SelfClient } from '@selfxyz/mobile-sdk-alpha';
|
||||
|
||||
jest.mock('@/navigation', () => ({
|
||||
navigationRef: {
|
||||
|
||||
@@ -16,9 +16,11 @@
|
||||
"../packages/mobile-sdk-alpha/dist"
|
||||
],
|
||||
"@selfxyz/mobile-sdk-alpha/onboarding/*": [
|
||||
"../packages/mobile-sdk-alpha/src/flows/onboarding/*",
|
||||
"../packages/mobile-sdk-alpha/dist/esm/flows/onboarding/*"
|
||||
],
|
||||
"@selfxyz/mobile-sdk-alpha/disclosing/*": [
|
||||
"../packages/mobile-sdk-alpha/src/flows/disclosing/*",
|
||||
"../packages/mobile-sdk-alpha/dist/esm/flows/disclosing/*"
|
||||
],
|
||||
"@/*": ["./src/*"]
|
||||
|
||||
@@ -70,6 +70,14 @@ module.exports = {
|
||||
'@typescript-eslint/no-require-imports': 'error',
|
||||
'@typescript-eslint/no-empty-object-type': 'error',
|
||||
'@typescript-eslint/no-unused-vars': ['error', { argsIgnorePattern: '^_', varsIgnorePattern: '^_' }],
|
||||
// TypeScript Import Rules
|
||||
'@typescript-eslint/consistent-type-imports': [
|
||||
'error',
|
||||
{
|
||||
prefer: 'type-imports',
|
||||
disallowTypeAnnotations: false,
|
||||
},
|
||||
],
|
||||
// Add prettier rule to show prettier errors as ESLint errors
|
||||
'prettier/prettier': ['warn', {}, { usePrettierrc: true }],
|
||||
// Disable prop-types for TypeScript files since we use TypeScript types
|
||||
|
||||
@@ -6,12 +6,13 @@ import { defaultConfig } from './config/defaults';
|
||||
import { mergeConfig } from './config/merge';
|
||||
import { notImplemented } from './errors';
|
||||
import { extractMRZInfo as parseMRZInfo } from './processing/mrz';
|
||||
import { ProofContext } from './proving/internal/logging';
|
||||
import type { ProofContext } from './proving/internal/logging';
|
||||
import { useProvingStore } from './proving/provingMachine';
|
||||
import { useMRZStore } from './stores/mrzStore';
|
||||
import { useProtocolStore } from './stores/protocolStore';
|
||||
import { useSelfAppStore } from './stores/selfAppStore';
|
||||
import { SDKEvent, SDKEventMap, SdkEvents } from './types/events';
|
||||
import type { SDKEvent, SDKEventMap } from './types/events';
|
||||
import { SdkEvents } from './types/events';
|
||||
import type {
|
||||
Adapters,
|
||||
Config,
|
||||
@@ -21,9 +22,9 @@ import type {
|
||||
NFCScanOpts,
|
||||
NFCScanResult,
|
||||
SelfClient,
|
||||
TrackEventParams,
|
||||
Unsubscribe,
|
||||
} from './types/public';
|
||||
import { TrackEventParams } from './types/public';
|
||||
/**
|
||||
* Optional adapter implementations used when a consumer does not provide their
|
||||
* own. These defaults are intentionally minimal no-ops suitable for tests and
|
||||
|
||||
@@ -7,7 +7,7 @@ import type { DimensionValue, NativeSyntheticEvent, ViewProps, ViewStyle } from
|
||||
import { NativeModules, PixelRatio, Platform, requireNativeComponent, StyleSheet, View } from 'react-native';
|
||||
|
||||
import { extractMRZInfo, formatDateToYYMMDD } from '../mrz';
|
||||
import { MRZInfo } from '../types/public';
|
||||
import type { MRZInfo } from '../types/public';
|
||||
import { RCTFragment } from './RCTFragment';
|
||||
|
||||
interface SelfMRZScannerViewProps extends ViewProps {
|
||||
|
||||
@@ -9,7 +9,7 @@ import { View } from 'tamagui';
|
||||
import { getSKIPEM, initPassportDataParsing } from '@selfxyz/common';
|
||||
|
||||
import { useSelfClient } from '../../context';
|
||||
import { NFCScanResult } from '../../types/public';
|
||||
import type { NFCScanResult } from '../../types/public';
|
||||
import type { ScreenProps } from '../../types/ui';
|
||||
|
||||
export const NFCScannerScreen = ({ onSuccess, onFailure }: ScreenProps) => {
|
||||
|
||||
@@ -2,10 +2,11 @@
|
||||
// SPDX-License-Identifier: BUSL-1.1
|
||||
// NOTE: Converts to Apache-2.0 on 2029-06-11 per LICENSE.
|
||||
|
||||
import { createContext, type PropsWithChildren, useContext, useMemo } from 'react';
|
||||
import type { PropsWithChildren } from 'react';
|
||||
import { createContext, useContext, useMemo } from 'react';
|
||||
|
||||
import { createSelfClient } from './client';
|
||||
import { SdkEvents } from './types/events';
|
||||
import type { SdkEvents } from './types/events';
|
||||
import type { Adapters, Config, SelfClient } from './types/public';
|
||||
|
||||
/**
|
||||
|
||||
@@ -19,7 +19,7 @@ import {
|
||||
} from '@selfxyz/common';
|
||||
|
||||
import { extractNameFromMRZ } from '../processing/mrz';
|
||||
import { SelfClient } from '../types/public';
|
||||
import type { SelfClient } from '../types/public';
|
||||
|
||||
export async function clearPassportData(selfClient: SelfClient) {
|
||||
const catalog = await selfClient.loadDocumentCatalog();
|
||||
|
||||
@@ -2,16 +2,18 @@
|
||||
// SPDX-License-Identifier: BUSL-1.1
|
||||
// NOTE: Converts to Apache-2.0 on 2029-06-11 per LICENSE.
|
||||
|
||||
import { RefObject, useCallback } from 'react';
|
||||
import type { RefObject } from 'react';
|
||||
import { useCallback } from 'react';
|
||||
import { Platform } from 'react-native';
|
||||
|
||||
import { PassportEvents } from '../../constants/analytics';
|
||||
import { useSelfClient } from '../../context';
|
||||
import { checkScannedInfo, formatDateToYYMMDD } from '../../processing/mrz';
|
||||
import { SdkEvents } from '../../types/events';
|
||||
import { MRZInfo } from '../../types/public';
|
||||
import type { MRZInfo } from '../../types/public';
|
||||
|
||||
export { MRZScannerView, MRZScannerViewProps } from '../../components/MRZScannerView';
|
||||
export type { MRZScannerViewProps } from '../../components/MRZScannerView';
|
||||
export { MRZScannerView } from '../../components/MRZScannerView';
|
||||
|
||||
export function mrzReadInstructions() {
|
||||
return 'Lay your document flat and position the machine readable text in the viewfinder';
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
import { create } from 'zustand';
|
||||
|
||||
import { MRZInfo } from '../types/public';
|
||||
import type { MRZInfo } from '../types/public';
|
||||
|
||||
type MRZNeededForNFC = Pick<MRZInfo, 'documentNumber' | 'dateOfBirth' | 'dateOfExpiry'>;
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// SPDX-License-Identifier: BUSL-1.1
|
||||
// NOTE: Converts to Apache-2.0 on 2029-06-11 per LICENSE.
|
||||
|
||||
import { DocumentCategory } from '@selfxyz/common';
|
||||
import type { DocumentCategory } from '@selfxyz/common';
|
||||
|
||||
import type { NFCScanContext, ProofContext } from '../proving/internal/logging';
|
||||
import type { LogLevel, Progress } from './public';
|
||||
|
||||
@@ -2,16 +2,16 @@
|
||||
// SPDX-License-Identifier: BUSL-1.1
|
||||
// NOTE: Converts to Apache-2.0 on 2029-06-11 per LICENSE.
|
||||
|
||||
import { create } from 'zustand';
|
||||
import type { create } from 'zustand';
|
||||
|
||||
import type { DocumentCatalog, IDDocument, PassportData } from '@selfxyz/common';
|
||||
|
||||
import type { ProofContext } from '../proving/internal/logging';
|
||||
import { ProvingState } from '../proving/provingMachine';
|
||||
import { MRZState } from '../stores/mrzStore';
|
||||
import { ProtocolState } from '../stores/protocolStore';
|
||||
import { SelfAppState } from '../stores/selfAppStore';
|
||||
import { SDKEvent, SDKEventMap } from './events';
|
||||
import type { ProvingState } from '../proving/provingMachine';
|
||||
import type { MRZState } from '../stores/mrzStore';
|
||||
import type { ProtocolState } from '../stores/protocolStore';
|
||||
import type { SelfAppState } from '../stores/selfAppStore';
|
||||
import type { SDKEvent, SDKEventMap } from './events';
|
||||
|
||||
export type { PassportValidationCallbacks } from '../validation/document';
|
||||
export type { DocumentCatalog, IDDocument, PassportData };
|
||||
|
||||
@@ -6,7 +6,7 @@ import { describe, expect, it, vi } from 'vitest';
|
||||
|
||||
import type { CryptoAdapter, DocumentsAdapter, NetworkAdapter, NFCScannerAdapter } from '../src';
|
||||
import { createListenersMap, createSelfClient, SdkEvents } from '../src/index';
|
||||
import { AuthAdapter } from '../src/types/public';
|
||||
import type { AuthAdapter } from '../src/types/public';
|
||||
|
||||
describe('createSelfClient', () => {
|
||||
// Test eager validation during client creation
|
||||
|
||||
@@ -7,7 +7,8 @@ import { describe, expect, it } from 'vitest';
|
||||
import type { DocumentCatalog } from '@selfxyz/common/types';
|
||||
import type { PassportData } from '@selfxyz/common/types/passport';
|
||||
|
||||
import { createSelfClient, defaultConfig, DocumentsAdapter, loadSelectedDocument, SelfClient } from '../../src';
|
||||
import type { DocumentsAdapter, SelfClient } from '../../src';
|
||||
import { createSelfClient, defaultConfig, loadSelectedDocument } from '../../src';
|
||||
|
||||
const createMockSelfClientWithDocumentsAdapter = (documentsAdapter: DocumentsAdapter): SelfClient => {
|
||||
return createSelfClient({
|
||||
|
||||
@@ -9,7 +9,7 @@ import { beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
import { PassportEvents } from '../../../src/constants/analytics';
|
||||
import { useReadMRZ } from '../../../src/flows/onboarding/read-mrz';
|
||||
import { SdkEvents } from '../../../src/types/events';
|
||||
import { MRZInfo } from '../../../src/types/public';
|
||||
import type { MRZInfo } from '../../../src/types/public';
|
||||
|
||||
import { renderHook } from '@testing-library/react';
|
||||
|
||||
|
||||
@@ -9,7 +9,8 @@
|
||||
|
||||
import { describe, expect, it } from 'vitest';
|
||||
|
||||
import { handleStatusCode, parseStatusMessage, type StatusMessage } from '../../../src/proving/internal/statusHandlers';
|
||||
import type { StatusMessage } from '../../../src/proving/internal/statusHandlers';
|
||||
import { handleStatusCode, parseStatusMessage } from '../../../src/proving/internal/statusHandlers';
|
||||
|
||||
describe('parseStatusMessage', () => {
|
||||
it('parses valid JSON string', () => {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// SPDX-License-Identifier: BUSL-1.1
|
||||
// NOTE: Converts to Apache-2.0 on 2029-06-11 per LICENSE.
|
||||
|
||||
import { SelfClient } from '../../src';
|
||||
import type { SelfClient } from '../../src';
|
||||
import { useProvingStore } from '../../src/proving/provingMachine';
|
||||
import { useProtocolStore } from '../../src/stores/protocolStore';
|
||||
import { useSelfAppStore } from '../../src/stores/selfAppStore';
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// SPDX-License-Identifier: BUSL-1.1
|
||||
// NOTE: Converts to Apache-2.0 on 2029-06-11 per LICENSE.
|
||||
|
||||
import { SelfClient } from '../../src';
|
||||
import type { SelfClient } from '../../src';
|
||||
import { ProofEvents } from '../../src/constants/analytics';
|
||||
import * as documentUtils from '../../src/documents/utils';
|
||||
import { useProvingStore } from '../../src/proving/provingMachine';
|
||||
|
||||
@@ -7,7 +7,8 @@ import { afterEach, describe, expect, it, vi } from 'vitest';
|
||||
|
||||
import type { PassportData } from '@selfxyz/common/types';
|
||||
|
||||
import { SdkEvents, SelfClient } from '../../src';
|
||||
import type { SelfClient } from '../../src';
|
||||
import { SdkEvents } from '../../src';
|
||||
import * as documentsUtils from '../../src/documents/utils';
|
||||
import { useProvingStore } from '../../src/proving/provingMachine';
|
||||
|
||||
|
||||
@@ -34,5 +34,13 @@ module.exports = {
|
||||
'@typescript-eslint/no-unused-vars': ['error', { argsIgnorePattern: '^_', varsIgnorePattern: '^_' }],
|
||||
'@typescript-eslint/no-require-imports': 'off',
|
||||
'prettier/prettier': ['warn', {}, { usePrettierrc: true }],
|
||||
// TypeScript Import Rules
|
||||
'@typescript-eslint/consistent-type-imports': [
|
||||
'error',
|
||||
{
|
||||
prefer: 'type-imports',
|
||||
disallowTypeAnnotations: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
@@ -8,7 +8,8 @@ import type { DocumentCatalog, DocumentMetadata, IDDocument } from '@selfxyz/com
|
||||
import { loadSelectedDocument, useSelfClient } from '@selfxyz/mobile-sdk-alpha';
|
||||
|
||||
import HomeScreen from './src/screens/HomeScreen';
|
||||
import { screenMap, type ScreenContext, type ScreenRoute } from './src/screens';
|
||||
import type { ScreenContext, ScreenRoute } from './src/screens';
|
||||
import { screenMap } from './src/screens';
|
||||
import SelfClientProvider from './src/providers/SelfClientProvider';
|
||||
|
||||
type SelectedDocumentState = {
|
||||
|
||||
@@ -5,7 +5,8 @@
|
||||
import React, { useMemo } from 'react';
|
||||
import { StyleSheet, Text, View } from 'react-native';
|
||||
|
||||
import { buildValidationRows, humanizeDocumentType, type NormalizedMRZResult } from '../utils/camera';
|
||||
import type { NormalizedMRZResult } from '../utils/camera';
|
||||
import { buildValidationRows, humanizeDocumentType } from '../utils/camera';
|
||||
|
||||
interface Props {
|
||||
result: NormalizedMRZResult;
|
||||
|
||||
@@ -3,7 +3,8 @@
|
||||
// NOTE: Converts to Apache-2.0 on 2029-06-11 per LICENSE.
|
||||
|
||||
import React from 'react';
|
||||
import { ScrollView, ScrollViewProps, StyleSheet } from 'react-native';
|
||||
import type { ScrollViewProps } from 'react-native';
|
||||
import { ScrollView, StyleSheet } from 'react-native';
|
||||
import { SafeAreaView } from 'react-native-safe-area-context';
|
||||
|
||||
type Props = ScrollViewProps & {
|
||||
|
||||
@@ -3,7 +3,8 @@
|
||||
// NOTE: Converts to Apache-2.0 on 2029-06-11 per LICENSE.
|
||||
|
||||
import React from 'react';
|
||||
import { StyleSheet, View, type ViewStyle } from 'react-native';
|
||||
import type { ViewStyle } from 'react-native';
|
||||
import { StyleSheet, View } from 'react-native';
|
||||
|
||||
import SafeAreaScrollView from './SafeAreaScrollView';
|
||||
import StandardHeader from './StandardHeader';
|
||||
|
||||
@@ -8,7 +8,8 @@ import { AccessibilityInfo, PermissionsAndroid, Platform } from 'react-native';
|
||||
import type { MRZInfo } from '@selfxyz/mobile-sdk-alpha';
|
||||
import { useReadMRZ } from '@selfxyz/mobile-sdk-alpha/onboarding/read-mrz';
|
||||
|
||||
import { normalizeMRZPayload, type NormalizedMRZResult } from '../utils/camera';
|
||||
import type { NormalizedMRZResult } from '../utils/camera';
|
||||
import { normalizeMRZPayload } from '../utils/camera';
|
||||
|
||||
type PermissionState = 'loading' | 'granted' | 'denied';
|
||||
type ScanState = 'idle' | 'scanning' | 'success' | 'error';
|
||||
|
||||
@@ -8,7 +8,8 @@ import { StyleSheet, Text, View } from 'react-native';
|
||||
import Logo from '../assets/images/logo.svg';
|
||||
import SafeAreaScrollView from '../components/SafeAreaScrollView';
|
||||
import MenuButton from '../components/MenuButton';
|
||||
import { orderedSectionEntries, type ScreenContext } from './index';
|
||||
import type { ScreenContext } from './index';
|
||||
import { orderedSectionEntries } from './index';
|
||||
|
||||
type Props = {
|
||||
screenContext: ScreenContext;
|
||||
|
||||
@@ -2,7 +2,8 @@
|
||||
// SPDX-License-Identifier: BUSL-1.1
|
||||
// NOTE: Converts to Apache-2.0 on 2029-06-11 per LICENSE.
|
||||
|
||||
import { type MRZInfo, type MRZValidation, extractMRZInfo } from '@selfxyz/mobile-sdk-alpha';
|
||||
import type { MRZInfo, MRZValidation } from '@selfxyz/mobile-sdk-alpha';
|
||||
import { extractMRZInfo } from '@selfxyz/mobile-sdk-alpha';
|
||||
|
||||
export type MRZPayload = MRZInfo | (MRZInfo & { rawMRZ?: string; raw?: string; mrzString?: string }) | string;
|
||||
|
||||
|
||||
@@ -4,7 +4,8 @@ import userEvent from '@testing-library/user-event';
|
||||
import { describe, expect, it, vi } from 'vitest';
|
||||
|
||||
import HomeScreen from '../../src/screens/HomeScreen';
|
||||
import { orderedSectionEntries, type ScreenContext } from '../../src/screens';
|
||||
import type { ScreenContext } from '../../src/screens';
|
||||
import { orderedSectionEntries } from '../../src/screens';
|
||||
|
||||
describe('HomeScreen', () => {
|
||||
const createContext = (): ScreenContext => ({
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { describe, expect, it, vi } from 'vitest';
|
||||
|
||||
import { orderedSectionEntries, screenDescriptors, screenMap, type ScreenContext } from '../../src/screens';
|
||||
import type { ScreenContext } from '../../src/screens';
|
||||
import { orderedSectionEntries, screenDescriptors, screenMap } from '../../src/screens';
|
||||
|
||||
describe('screen descriptor index', () => {
|
||||
const createContext = (): ScreenContext => ({
|
||||
|
||||
@@ -7346,8 +7346,9 @@ __metadata:
|
||||
"@testing-library/react-native": "npm:^13.3.3"
|
||||
"@tsconfig/react-native": "npm:^3.0.6"
|
||||
"@types/add": "npm:^2"
|
||||
"@types/bn.js": "npm:^5.2.0"
|
||||
"@types/dompurify": "npm:^3.2.0"
|
||||
"@types/elliptic": "npm:^6"
|
||||
"@types/elliptic": "npm:^6.4.18"
|
||||
"@types/jest": "npm:^29.5.14"
|
||||
"@types/node": "npm:^22.18.3"
|
||||
"@types/node-forge": "npm:^1.3.14"
|
||||
@@ -11506,7 +11507,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@types/bn.js@npm:*, @types/bn.js@npm:^5.1.0":
|
||||
"@types/bn.js@npm:*, @types/bn.js@npm:^5.1.0, @types/bn.js@npm:^5.2.0":
|
||||
version: 5.2.0
|
||||
resolution: "@types/bn.js@npm:5.2.0"
|
||||
dependencies:
|
||||
@@ -11629,7 +11630,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@types/elliptic@npm:^6":
|
||||
"@types/elliptic@npm:^6.4.18":
|
||||
version: 6.4.18
|
||||
resolution: "@types/elliptic@npm:6.4.18"
|
||||
dependencies:
|
||||
|
||||
Reference in New Issue
Block a user