mirror of
https://github.com/selfxyz/self.git
synced 2026-04-27 03:01:15 -04:00
Migrate Analytics (#951)
* setup analytics adapter for self mobile sdk client and use in app * wrap for context * fix build * yarn types is an alias for build when build just compiles ts * ok unlock * deeper * ok this looks to work * fix license check * make sure it starts with this line * someone didnt commit * fix double analytics bug and builds * lint
This commit is contained in:
@@ -21,6 +21,10 @@ const extraNodeModules = {
|
||||
'@': path.join(__dirname, 'src'),
|
||||
'@selfxyz/common': path.resolve(commonPath, 'dist'),
|
||||
'@selfxyz/mobile-sdk-alpha': path.resolve(sdkAlphaPath, 'dist'),
|
||||
'@selfxyz/mobile-sdk-alpha/constants/analytics': path.resolve(
|
||||
sdkAlphaPath,
|
||||
'dist/esm/constants/analytics.js',
|
||||
),
|
||||
// Main exports
|
||||
'@selfxyz/common/utils': path.resolve(
|
||||
commonPath,
|
||||
|
||||
@@ -7,9 +7,10 @@ import { ActivityIndicator, View } from 'react-native';
|
||||
import { assign, createMachine } from 'xstate';
|
||||
import { useMachine } from '@xstate/react';
|
||||
|
||||
import { ProofEvents } from '@selfxyz/mobile-sdk-alpha/constants/analytics';
|
||||
|
||||
import { HeldPrimaryButton } from '@/components/buttons/PrimaryButtonLongHold';
|
||||
import Description from '@/components/typography/Description';
|
||||
import { ProofEvents } from '@/consts/analytics';
|
||||
import { black } from '@/utils/colors';
|
||||
|
||||
interface HeldPrimaryButtonProveScreenProps {
|
||||
|
||||
@@ -7,16 +7,16 @@ import { Linking } from 'react-native';
|
||||
import { checkVersion } from 'react-native-check-version';
|
||||
import { useNavigation } from '@react-navigation/native';
|
||||
|
||||
import { AppEvents } from '@/consts/analytics';
|
||||
import analytics from '@/utils/analytics';
|
||||
import { registerModalCallbacks } from '@/utils/modalCallbackRegistry';
|
||||
import { useSelfClient } from '@selfxyz/mobile-sdk-alpha';
|
||||
import { AppEvents } from '@selfxyz/mobile-sdk-alpha/constants/analytics';
|
||||
|
||||
const { trackEvent } = analytics();
|
||||
import { registerModalCallbacks } from '@/utils/modalCallbackRegistry';
|
||||
|
||||
export const useAppUpdates = (): [boolean, () => void, boolean] => {
|
||||
const navigation = useNavigation();
|
||||
const [newVersionUrl, setNewVersionUrl] = useState<string | null>(null);
|
||||
const [isModalDismissed, setIsModalDismissed] = useState(false);
|
||||
const selfClient = useSelfClient();
|
||||
|
||||
useEffect(() => {
|
||||
checkVersion().then(version => {
|
||||
@@ -30,13 +30,13 @@ export const useAppUpdates = (): [boolean, () => void, boolean] => {
|
||||
const callbackId = registerModalCallbacks({
|
||||
onButtonPress: async () => {
|
||||
if (newVersionUrl !== null) {
|
||||
trackEvent(AppEvents.UPDATE_STARTED);
|
||||
selfClient.trackEvent(AppEvents.UPDATE_STARTED);
|
||||
await Linking.openURL(newVersionUrl);
|
||||
}
|
||||
},
|
||||
onModalDismiss: () => {
|
||||
setIsModalDismissed(true);
|
||||
trackEvent(AppEvents.UPDATE_MODAL_CLOSED);
|
||||
selfClient.trackEvent(AppEvents.UPDATE_MODAL_CLOSED);
|
||||
},
|
||||
});
|
||||
|
||||
@@ -47,7 +47,7 @@ export const useAppUpdates = (): [boolean, () => void, boolean] => {
|
||||
buttonText: 'Update and restart',
|
||||
callbackId,
|
||||
});
|
||||
trackEvent(AppEvents.UPDATE_MODAL_OPENED);
|
||||
selfClient.trackEvent(AppEvents.UPDATE_MODAL_OPENED);
|
||||
};
|
||||
|
||||
return [newVersionUrl !== null, showAppUpdateModal, isModalDismissed];
|
||||
|
||||
@@ -5,16 +5,15 @@
|
||||
import { useState } from 'react';
|
||||
import { useNavigation } from '@react-navigation/native';
|
||||
|
||||
import { AppEvents } from '@/consts/analytics';
|
||||
import analytics from '@/utils/analytics';
|
||||
import { registerModalCallbacks } from '@/utils/modalCallbackRegistry';
|
||||
import { useSelfClient } from '@selfxyz/mobile-sdk-alpha';
|
||||
import { AppEvents } from '@selfxyz/mobile-sdk-alpha/constants/analytics';
|
||||
|
||||
const { trackEvent } = analytics();
|
||||
import { registerModalCallbacks } from '@/utils/modalCallbackRegistry';
|
||||
|
||||
export const useAppUpdates = (): [boolean, () => void, boolean] => {
|
||||
const navigation = useNavigation();
|
||||
const [isModalDismissed, setIsModalDismissed] = useState(false);
|
||||
|
||||
const { trackEvent } = useSelfClient();
|
||||
const showAppUpdateModal = () => {
|
||||
const callbackId = registerModalCallbacks({
|
||||
onButtonPress: async () => {
|
||||
|
||||
@@ -5,7 +5,8 @@
|
||||
import { useEffect } from 'react';
|
||||
import { Linking, Platform } from 'react-native';
|
||||
|
||||
import { SettingsEvents } from '@/consts/analytics';
|
||||
import { SettingsEvents } from '@selfxyz/mobile-sdk-alpha/constants/analytics';
|
||||
|
||||
import { useModal } from '@/hooks/useModal';
|
||||
import { useNetInfo } from '@/hooks/useNetInfo';
|
||||
import { navigationRef } from '@/navigation';
|
||||
|
||||
@@ -14,7 +14,8 @@ import React, {
|
||||
import ReactNativeBiometrics from 'react-native-biometrics';
|
||||
import Keychain from 'react-native-keychain';
|
||||
|
||||
import { AuthEvents } from '@/consts/analytics';
|
||||
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';
|
||||
|
||||
@@ -16,7 +16,8 @@ import React, {
|
||||
useState,
|
||||
} from 'react';
|
||||
|
||||
import { AuthEvents } from '@/consts/analytics';
|
||||
import { AuthEvents } from '@selfxyz/mobile-sdk-alpha/constants/analytics';
|
||||
|
||||
import type { Mnemonic } from '@/types/mnemonic';
|
||||
import analytics from '@/utils/analytics';
|
||||
|
||||
|
||||
@@ -6,7 +6,8 @@ import type { PropsWithChildren } from 'react';
|
||||
import React, { useEffect } from 'react';
|
||||
import messaging from '@react-native-firebase/messaging';
|
||||
|
||||
import { NotificationEvents } from '@/consts/analytics';
|
||||
import { NotificationEvents } from '@selfxyz/mobile-sdk-alpha/constants/analytics';
|
||||
|
||||
import analytics from '@/utils/analytics';
|
||||
|
||||
const { trackEvent } = analytics();
|
||||
|
||||
@@ -5,10 +5,14 @@
|
||||
import { type PropsWithChildren, useMemo } from 'react';
|
||||
|
||||
import {
|
||||
Adapters,
|
||||
SelfClientProvider as SDKSelfClientProvider,
|
||||
webScannerShim,
|
||||
type WsConn,
|
||||
} from '@selfxyz/mobile-sdk-alpha';
|
||||
import { TrackEventParams } from '@selfxyz/mobile-sdk-alpha';
|
||||
|
||||
import analytics from '@/utils/analytics';
|
||||
|
||||
/**
|
||||
* Provides a configured Self SDK client instance to all descendants.
|
||||
@@ -20,7 +24,7 @@ import {
|
||||
*/
|
||||
export const SelfClientProvider = ({ children }: PropsWithChildren) => {
|
||||
const config = useMemo(() => ({}), []);
|
||||
const adapters = useMemo(
|
||||
const adapters: Partial<Adapters> = useMemo(
|
||||
() => ({
|
||||
scanner: webScannerShim,
|
||||
network: {
|
||||
@@ -71,6 +75,11 @@ export const SelfClientProvider = ({ children }: PropsWithChildren) => {
|
||||
);
|
||||
},
|
||||
},
|
||||
analytics: {
|
||||
trackEvent: (event: string, data?: TrackEventParams) => {
|
||||
analytics().trackEvent(event, data);
|
||||
},
|
||||
},
|
||||
}),
|
||||
[],
|
||||
);
|
||||
|
||||
@@ -7,6 +7,8 @@ import React, { useEffect, useRef } from 'react';
|
||||
import { StyleSheet, View } from 'react-native';
|
||||
import { SystemBars } from 'react-native-edge-to-edge';
|
||||
|
||||
import { PassportEvents } from '@selfxyz/mobile-sdk-alpha/constants/analytics';
|
||||
|
||||
import passportOnboardingAnimation from '@/assets/animations/passport_onboarding.json';
|
||||
import { PrimaryButton } from '@/components/buttons/PrimaryButton';
|
||||
import { SecondaryButton } from '@/components/buttons/SecondaryButton';
|
||||
@@ -15,7 +17,6 @@ import TextsContainer from '@/components/TextsContainer';
|
||||
import Additional from '@/components/typography/Additional';
|
||||
import Description from '@/components/typography/Description';
|
||||
import { DescriptionTitle } from '@/components/typography/DescriptionTitle';
|
||||
import { PassportEvents } from '@/consts/analytics';
|
||||
import useHapticNavigation from '@/hooks/useHapticNavigation';
|
||||
import Scan from '@/images/icons/passport_camera_scan.svg';
|
||||
import { ExpandableBottomLayout } from '@/layouts/ExpandableBottomLayout';
|
||||
|
||||
@@ -30,11 +30,12 @@ import {
|
||||
genMockIdDoc,
|
||||
initPassportDataParsing,
|
||||
} from '@selfxyz/common/utils/passports';
|
||||
import { useSelfClient } from '@selfxyz/mobile-sdk-alpha';
|
||||
import { MockDataEvents } from '@selfxyz/mobile-sdk-alpha/constants/analytics';
|
||||
|
||||
import { PrimaryButton } from '@/components/buttons/PrimaryButton';
|
||||
import ButtonsContainer from '@/components/ButtonsContainer';
|
||||
import { Caption } from '@/components/typography/Caption';
|
||||
import { MockDataEvents } from '@/consts/analytics';
|
||||
import SelfDevCard from '@/images/card-dev.svg';
|
||||
import IdIcon from '@/images/icons/id_icon.svg';
|
||||
import NoteIcon from '@/images/icons/note.svg';
|
||||
@@ -56,8 +57,6 @@ import { extraYPadding } from '@/utils/constants';
|
||||
import { dinot, plexMono } from '@/utils/fonts';
|
||||
import { buttonTap, selectionChange } from '@/utils/haptic';
|
||||
|
||||
const { trackEvent } = analytics();
|
||||
|
||||
const documentTypes = {
|
||||
mock_passport: 'Passport',
|
||||
mock_id_card: 'ID Card',
|
||||
@@ -246,6 +245,7 @@ const FormSection: React.FC<FormSectionProps> = ({
|
||||
};
|
||||
|
||||
const MockDataScreen: React.FC = () => {
|
||||
const { trackEvent } = useSelfClient();
|
||||
const navigation = useNavigation();
|
||||
const [age, setAge] = useState(21);
|
||||
const [expiryYears, setExpiryYears] = useState(5);
|
||||
|
||||
@@ -13,13 +13,13 @@ import { useNavigation } from '@react-navigation/native';
|
||||
import { countryCodes } from '@selfxyz/common/constants';
|
||||
import type { IdDocInput } from '@selfxyz/common/utils';
|
||||
import { genMockIdDocAndInitDataParsing } from '@selfxyz/common/utils/passports';
|
||||
import { MockDataEvents } from '@selfxyz/mobile-sdk-alpha/constants/analytics';
|
||||
|
||||
import { PrimaryButton } from '@/components/buttons/PrimaryButton';
|
||||
import ButtonsContainer from '@/components/ButtonsContainer';
|
||||
import { BodyText } from '@/components/typography/BodyText';
|
||||
import Description from '@/components/typography/Description';
|
||||
import { Title } from '@/components/typography/Title';
|
||||
import { MockDataEvents } from '@/consts/analytics';
|
||||
import { storePassportData } from '@/providers/passportDataProvider';
|
||||
import useUserStore from '@/stores/userStore';
|
||||
import { black, borderColor, white } from '@/utils/colors';
|
||||
|
||||
@@ -8,11 +8,12 @@ import { StyleSheet } from 'react-native';
|
||||
import { YStack } from 'tamagui';
|
||||
import { useNavigation } from '@react-navigation/native';
|
||||
|
||||
import { AppEvents } from '@selfxyz/mobile-sdk-alpha/constants/analytics';
|
||||
|
||||
import warningAnimation from '@/assets/animations/warning.json';
|
||||
import { PrimaryButton } from '@/components/buttons/PrimaryButton';
|
||||
import Caution from '@/components/typography/Caution';
|
||||
import { SubHeader } from '@/components/typography/SubHeader';
|
||||
import { AppEvents } from '@/consts/analytics';
|
||||
import { ExpandableBottomLayout } from '@/layouts/ExpandableBottomLayout';
|
||||
import { useSettingStore } from '@/stores/settingStore';
|
||||
import { black, white } from '@/utils/colors';
|
||||
|
||||
@@ -11,10 +11,12 @@ import {
|
||||
usePreventRemove,
|
||||
} from '@react-navigation/native';
|
||||
|
||||
import { useSelfClient } from '@selfxyz/mobile-sdk-alpha';
|
||||
import { ProofEvents } from '@selfxyz/mobile-sdk-alpha/constants/analytics';
|
||||
|
||||
import { pressedStyle } from '@/components/buttons/pressedStyle';
|
||||
import { BodyText } from '@/components/typography/BodyText';
|
||||
import { Caption } from '@/components/typography/Caption';
|
||||
import { ProofEvents } from '@/consts/analytics';
|
||||
import { useAppUpdates } from '@/hooks/useAppUpdates';
|
||||
import useConnectionModal from '@/hooks/useConnectionModal';
|
||||
import useHapticNavigation from '@/hooks/useHapticNavigation';
|
||||
@@ -23,7 +25,6 @@ import ScanIcon from '@/images/icons/qr_scan.svg';
|
||||
import WarnIcon from '@/images/icons/warning.svg';
|
||||
import { usePassport } from '@/providers/passportDataProvider';
|
||||
import { useSettingStore } from '@/stores/settingStore';
|
||||
import analytics from '@/utils/analytics';
|
||||
import { amber500, black, neutral700, slate800, white } from '@/utils/colors';
|
||||
import { extraYPadding } from '@/utils/constants';
|
||||
|
||||
@@ -38,9 +39,8 @@ const ScanButton = styled(Button, {
|
||||
justifyContent: 'center',
|
||||
});
|
||||
|
||||
const { trackEvent } = analytics();
|
||||
|
||||
const HomeScreen: React.FC = () => {
|
||||
const selfClient = useSelfClient();
|
||||
useConnectionModal();
|
||||
const navigation = useNavigation();
|
||||
const { getAllDocuments } = usePassport();
|
||||
@@ -72,12 +72,12 @@ const HomeScreen: React.FC = () => {
|
||||
|
||||
const goToQRCodeViewFinder = useHapticNavigation('QRCodeViewFinder');
|
||||
const onScanButtonPress = useCallback(() => {
|
||||
trackEvent(ProofEvents.QR_SCAN_REQUESTED, {
|
||||
selfClient.trackEvent(ProofEvents.QR_SCAN_REQUESTED, {
|
||||
from: 'Home',
|
||||
});
|
||||
|
||||
goToQRCodeViewFinder();
|
||||
}, [goToQRCodeViewFinder]);
|
||||
}, [goToQRCodeViewFinder, selfClient]);
|
||||
|
||||
// Prevents back navigation
|
||||
usePreventRemove(true, () => {});
|
||||
|
||||
@@ -8,10 +8,11 @@ import { Gesture, GestureDetector } from 'react-native-gesture-handler';
|
||||
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
||||
import { Anchor, Text, YStack } from 'tamagui';
|
||||
|
||||
import { AppEvents } from '@selfxyz/mobile-sdk-alpha/constants/analytics';
|
||||
|
||||
import AbstractButton from '@/components/buttons/AbstractButton';
|
||||
import { BodyText } from '@/components/typography/BodyText';
|
||||
import { Caption } from '@/components/typography/Caption';
|
||||
import { AppEvents } from '@/consts/analytics';
|
||||
import { privacyUrl, supportedBiometricIdsUrl, termsUrl } from '@/consts/links';
|
||||
import useConnectionModal from '@/hooks/useConnectionModal';
|
||||
import useHapticNavigation from '@/hooks/useHapticNavigation';
|
||||
|
||||
@@ -9,6 +9,7 @@ import { View, XStack, YStack } from 'tamagui';
|
||||
import { useIsFocused, useNavigation } from '@react-navigation/native';
|
||||
|
||||
import { formatDateToYYMMDD } from '@selfxyz/mobile-sdk-alpha';
|
||||
import { PassportEvents } from '@selfxyz/mobile-sdk-alpha/constants/analytics';
|
||||
|
||||
import passportScanAnimation from '@/assets/animations/passport_scan.json';
|
||||
import { SecondaryButton } from '@/components/buttons/SecondaryButton';
|
||||
@@ -17,7 +18,6 @@ import { PassportCamera } from '@/components/native/PassportCamera';
|
||||
import Additional from '@/components/typography/Additional';
|
||||
import Description from '@/components/typography/Description';
|
||||
import { Title } from '@/components/typography/Title';
|
||||
import { PassportEvents } from '@/consts/analytics';
|
||||
import useHapticNavigation from '@/hooks/useHapticNavigation';
|
||||
import Scan from '@/images/icons/passport_camera_scan.svg';
|
||||
import { ExpandableBottomLayout } from '@/layouts/ExpandableBottomLayout';
|
||||
|
||||
@@ -25,6 +25,8 @@ import { CircleHelp } from '@tamagui/lucide-icons';
|
||||
import type { PassportData } from '@selfxyz/common/types';
|
||||
import { getSKIPEM } from '@selfxyz/common/utils/csca';
|
||||
import { initPassportDataParsing } from '@selfxyz/common/utils/passports';
|
||||
import { useSelfClient } from '@selfxyz/mobile-sdk-alpha';
|
||||
import { PassportEvents } from '@selfxyz/mobile-sdk-alpha/constants/analytics';
|
||||
|
||||
import passportVerifyAnimation from '@/assets/animations/passport_verify.json';
|
||||
import { PrimaryButton } from '@/components/buttons/PrimaryButton';
|
||||
@@ -33,7 +35,6 @@ import ButtonsContainer from '@/components/ButtonsContainer';
|
||||
import TextsContainer from '@/components/TextsContainer';
|
||||
import { BodyText } from '@/components/typography/BodyText';
|
||||
import { Title } from '@/components/typography/Title';
|
||||
import { PassportEvents } from '@/consts/analytics';
|
||||
import { useFeedbackAutoHide } from '@/hooks/useFeedbackAutoHide';
|
||||
import useHapticNavigation from '@/hooks/useHapticNavigation';
|
||||
import NFC_IMAGE from '@/images/nfc.png';
|
||||
@@ -41,7 +42,6 @@ import { ExpandableBottomLayout } from '@/layouts/ExpandableBottomLayout';
|
||||
import { useFeedback } from '@/providers/feedbackProvider';
|
||||
import { storePassportData } from '@/providers/passportDataProvider';
|
||||
import useUserStore from '@/stores/userStore';
|
||||
import analytics from '@/utils/analytics';
|
||||
import { black, slate100, slate400, slate500, white } from '@/utils/colors';
|
||||
import { sendFeedbackEmail } from '@/utils/email';
|
||||
import { dinot } from '@/utils/fonts';
|
||||
@@ -55,8 +55,6 @@ import { parseScanResponse, scan } from '@/utils/nfcScanner';
|
||||
import { hasAnyValidRegisteredDocument } from '@/utils/proving/validateDocument';
|
||||
import { sanitizeErrorMessage } from '@/utils/utils';
|
||||
|
||||
const { trackEvent } = analytics();
|
||||
|
||||
const emitter =
|
||||
Platform.OS === 'android'
|
||||
? new NativeEventEmitter(NativeModules.nativeModule)
|
||||
@@ -77,6 +75,7 @@ type PassportNFCScanRoute = RouteProp<
|
||||
>;
|
||||
|
||||
const PassportNFCScanScreen: React.FC = () => {
|
||||
const { trackEvent } = useSelfClient();
|
||||
const navigation = useNavigation();
|
||||
const route = useRoute<PassportNFCScanRoute>();
|
||||
const { showModal } = useFeedback();
|
||||
|
||||
@@ -5,12 +5,13 @@
|
||||
import React from 'react';
|
||||
import { Image } from 'tamagui';
|
||||
|
||||
import { PassportEvents } from '@selfxyz/mobile-sdk-alpha/constants/analytics';
|
||||
|
||||
import { SecondaryButton } from '@/components/buttons/SecondaryButton';
|
||||
import ButtonsContainer from '@/components/ButtonsContainer';
|
||||
import TextsContainer from '@/components/TextsContainer';
|
||||
import { BodyText } from '@/components/typography/BodyText';
|
||||
import { Title } from '@/components/typography/Title';
|
||||
import { PassportEvents } from '@/consts/analytics';
|
||||
import useHapticNavigation from '@/hooks/useHapticNavigation';
|
||||
import NFC_IMAGE from '@/images/nfc.png';
|
||||
import { ExpandableBottomLayout } from '@/layouts/ExpandableBottomLayout';
|
||||
|
||||
@@ -8,6 +8,8 @@ import { StyleSheet } from 'react-native';
|
||||
import { SystemBars } from 'react-native-edge-to-edge';
|
||||
import { useNavigation } from '@react-navigation/native';
|
||||
|
||||
import { PassportEvents } from '@selfxyz/mobile-sdk-alpha/constants/analytics';
|
||||
|
||||
import passportOnboardingAnimation from '@/assets/animations/passport_onboarding.json';
|
||||
import { PrimaryButton } from '@/components/buttons/PrimaryButton';
|
||||
import { SecondaryButton } from '@/components/buttons/SecondaryButton';
|
||||
@@ -16,7 +18,6 @@ import TextsContainer from '@/components/TextsContainer';
|
||||
import Additional from '@/components/typography/Additional';
|
||||
import Description from '@/components/typography/Description';
|
||||
import { Title } from '@/components/typography/Title';
|
||||
import { PassportEvents } from '@/consts/analytics';
|
||||
import useHapticNavigation from '@/hooks/useHapticNavigation';
|
||||
import { ExpandableBottomLayout } from '@/layouts/ExpandableBottomLayout';
|
||||
import { black, slate100, white } from '@/utils/colors';
|
||||
|
||||
@@ -11,14 +11,13 @@ import type { RouteProp } from '@react-navigation/native';
|
||||
|
||||
import { countryCodes } from '@selfxyz/common/constants';
|
||||
import type { PassportData } from '@selfxyz/common/types';
|
||||
import { PassportEvents } from '@selfxyz/mobile-sdk-alpha/constants/analytics';
|
||||
|
||||
import { PrimaryButton } from '@/components/buttons/PrimaryButton';
|
||||
import { SecondaryButton } from '@/components/buttons/SecondaryButton';
|
||||
import { BodyText } from '@/components/typography/BodyText';
|
||||
import { Title } from '@/components/typography/Title';
|
||||
import { PassportEvents } from '@/consts/analytics';
|
||||
import useHapticNavigation from '@/hooks/useHapticNavigation';
|
||||
import LogoSvg from '@/images/logo.svg';
|
||||
import { ExpandableBottomLayout } from '@/layouts/ExpandableBottomLayout';
|
||||
import analytics from '@/utils/analytics';
|
||||
import { black, slate500, white } from '@/utils/colors';
|
||||
|
||||
@@ -8,15 +8,19 @@ import { ActivityIndicator, View } from 'react-native';
|
||||
import type { StaticScreenProps } from '@react-navigation/native';
|
||||
import { usePreventRemove } from '@react-navigation/native';
|
||||
|
||||
import { useSelfClient } from '@selfxyz/mobile-sdk-alpha';
|
||||
import {
|
||||
PassportEvents,
|
||||
ProofEvents,
|
||||
} from '@selfxyz/mobile-sdk-alpha/constants/analytics';
|
||||
|
||||
import successAnimation from '@/assets/animations/loading/success.json';
|
||||
import { PrimaryButton } from '@/components/buttons/PrimaryButton';
|
||||
import Description from '@/components/typography/Description';
|
||||
import { Title } from '@/components/typography/Title';
|
||||
import { PassportEvents, ProofEvents } from '@/consts/analytics';
|
||||
import useHapticNavigation from '@/hooks/useHapticNavigation';
|
||||
import { ExpandableBottomLayout } from '@/layouts/ExpandableBottomLayout';
|
||||
import { styles } from '@/screens/prove/ProofRequestStatusScreen';
|
||||
import analytics from '@/utils/analytics';
|
||||
import { black, white } from '@/utils/colors';
|
||||
import { notificationSuccess } from '@/utils/haptic';
|
||||
import {
|
||||
@@ -27,9 +31,8 @@ import { useProvingStore } from '@/utils/proving/provingMachine';
|
||||
|
||||
type ConfirmBelongingScreenProps = StaticScreenProps<Record<string, never>>;
|
||||
|
||||
const { trackEvent } = analytics();
|
||||
|
||||
const ConfirmBelongingScreen: React.FC<ConfirmBelongingScreenProps> = () => {
|
||||
const { trackEvent } = useSelfClient();
|
||||
const navigate = useHapticNavigation('LoadingScreen', {
|
||||
params: {},
|
||||
});
|
||||
|
||||
@@ -9,6 +9,9 @@ import { SystemBars } from 'react-native-edge-to-edge';
|
||||
import { ScrollView, Spinner } from 'tamagui';
|
||||
import { useIsFocused } from '@react-navigation/native';
|
||||
|
||||
import { useSelfClient } from '@selfxyz/mobile-sdk-alpha';
|
||||
import { ProofEvents } from '@selfxyz/mobile-sdk-alpha/constants/analytics';
|
||||
|
||||
import loadingAnimation from '@/assets/animations/loading/misc.json';
|
||||
import failAnimation from '@/assets/animations/proof_failed.json';
|
||||
import succesAnimation from '@/assets/animations/proof_success.json';
|
||||
@@ -17,13 +20,11 @@ import { BodyText } from '@/components/typography/BodyText';
|
||||
import Description from '@/components/typography/Description';
|
||||
import { typography } from '@/components/typography/styles';
|
||||
import { Title } from '@/components/typography/Title';
|
||||
import { ProofEvents } from '@/consts/analytics';
|
||||
import useHapticNavigation from '@/hooks/useHapticNavigation';
|
||||
import { ExpandableBottomLayout } from '@/layouts/ExpandableBottomLayout';
|
||||
import { ProofStatus } from '@/stores/proof-types';
|
||||
import { useProofHistoryStore } from '@/stores/proofHistoryStore';
|
||||
import { useSelfAppStore } from '@/stores/selfAppStore';
|
||||
import analytics from '@/utils/analytics';
|
||||
import { black, white } from '@/utils/colors';
|
||||
import {
|
||||
buttonTap,
|
||||
@@ -32,9 +33,8 @@ import {
|
||||
} from '@/utils/haptic';
|
||||
import { useProvingStore } from '@/utils/proving/provingMachine';
|
||||
|
||||
const { trackEvent } = analytics();
|
||||
|
||||
const SuccessScreen: React.FC = () => {
|
||||
const { trackEvent } = useSelfClient();
|
||||
const { selfApp, cleanSelfApp } = useSelfAppStore();
|
||||
const appName = selfApp?.appName;
|
||||
const goHome = useHapticNavigation('Home');
|
||||
@@ -121,6 +121,7 @@ const SuccessScreen: React.FC = () => {
|
||||
setAnimationSource(loadingAnimation);
|
||||
}
|
||||
}, [
|
||||
trackEvent,
|
||||
currentState,
|
||||
isFocused,
|
||||
appName,
|
||||
|
||||
@@ -22,27 +22,26 @@ import { Eye, EyeOff } from '@tamagui/lucide-icons';
|
||||
|
||||
import type { SelfAppDisclosureConfig } from '@selfxyz/common/utils/appType';
|
||||
import { formatEndpoint } from '@selfxyz/common/utils/scope';
|
||||
import { useSelfClient } from '@selfxyz/mobile-sdk-alpha';
|
||||
import { ProofEvents } from '@selfxyz/mobile-sdk-alpha/constants/analytics';
|
||||
|
||||
import miscAnimation from '@/assets/animations/loading/misc.json';
|
||||
import { HeldPrimaryButtonProveScreen } from '@/components/buttons/HeldPrimaryButtonProveScreen';
|
||||
import Disclosures from '@/components/Disclosures';
|
||||
import { BodyText } from '@/components/typography/BodyText';
|
||||
import { Caption } from '@/components/typography/Caption';
|
||||
import { ProofEvents } from '@/consts/analytics';
|
||||
import { ExpandableBottomLayout } from '@/layouts/ExpandableBottomLayout';
|
||||
import { setDefaultDocumentTypeIfNeeded } from '@/providers/passportDataProvider';
|
||||
import { ProofStatus } from '@/stores/proof-types';
|
||||
import { useProofHistoryStore } from '@/stores/proofHistoryStore';
|
||||
import { useSelfAppStore } from '@/stores/selfAppStore';
|
||||
import analytics from '@/utils/analytics';
|
||||
import { black, slate300, white } from '@/utils/colors';
|
||||
import { formatUserId } from '@/utils/formatUserId';
|
||||
import { buttonTap } from '@/utils/haptic';
|
||||
import { useProvingStore } from '@/utils/proving/provingMachine';
|
||||
|
||||
const { trackEvent } = analytics();
|
||||
|
||||
const ProveScreen: React.FC = () => {
|
||||
const { trackEvent } = useSelfClient();
|
||||
const { navigate } = useNavigation();
|
||||
const isFocused = useIsFocused();
|
||||
const selectedApp = useSelfAppStore(state => state.selfApp);
|
||||
|
||||
@@ -12,6 +12,9 @@ import {
|
||||
useNavigation,
|
||||
} from '@react-navigation/native';
|
||||
|
||||
import { useSelfClient } from '@selfxyz/mobile-sdk-alpha';
|
||||
import { ProofEvents } from '@selfxyz/mobile-sdk-alpha/constants/analytics';
|
||||
|
||||
import qrScanAnimation from '@/assets/animations/qr_scan.json';
|
||||
import { SecondaryButton } from '@/components/buttons/SecondaryButton';
|
||||
import type { QRCodeScannerViewProps } from '@/components/native/QRCodeScanner';
|
||||
@@ -19,19 +22,16 @@ import { QRCodeScannerView } from '@/components/native/QRCodeScanner';
|
||||
import Additional from '@/components/typography/Additional';
|
||||
import Description from '@/components/typography/Description';
|
||||
import { Title } from '@/components/typography/Title';
|
||||
import { ProofEvents } from '@/consts/analytics';
|
||||
import useConnectionModal from '@/hooks/useConnectionModal';
|
||||
import useHapticNavigation from '@/hooks/useHapticNavigation';
|
||||
import QRScan from '@/images/icons/qr_code.svg';
|
||||
import { ExpandableBottomLayout } from '@/layouts/ExpandableBottomLayout';
|
||||
import { useSelfAppStore } from '@/stores/selfAppStore';
|
||||
import analytics from '@/utils/analytics';
|
||||
import { black, slate800, white } from '@/utils/colors';
|
||||
import { parseAndValidateUrlParams } from '@/utils/deeplinks';
|
||||
|
||||
const { trackEvent } = analytics();
|
||||
|
||||
const QRCodeViewFinderScreen: React.FC = () => {
|
||||
const { trackEvent } = useSelfClient();
|
||||
const { visible: connectionModalVisible } = useConnectionModal();
|
||||
const navigation = useNavigation();
|
||||
const isFocused = useIsFocused();
|
||||
|
||||
@@ -6,12 +6,14 @@ import React, { useCallback, useState } from 'react';
|
||||
import { Separator, View, XStack, YStack } from 'tamagui';
|
||||
import { useNavigation } from '@react-navigation/native';
|
||||
|
||||
import { useSelfClient } from '@selfxyz/mobile-sdk-alpha';
|
||||
import { BackupEvents } from '@selfxyz/mobile-sdk-alpha/constants/analytics';
|
||||
|
||||
import { PrimaryButton } from '@/components/buttons/PrimaryButton';
|
||||
import { SecondaryButton } from '@/components/buttons/SecondaryButton';
|
||||
import { Caption } from '@/components/typography/Caption';
|
||||
import Description from '@/components/typography/Description';
|
||||
import { Title } from '@/components/typography/Title';
|
||||
import { BackupEvents } from '@/consts/analytics';
|
||||
import useHapticNavigation from '@/hooks/useHapticNavigation';
|
||||
import Keyboard from '@/images/icons/keyboard.svg';
|
||||
import RestoreAccountSvg from '@/images/icons/restore_account.svg';
|
||||
@@ -22,14 +24,12 @@ import {
|
||||
reStorePassportDataWithRightCSCA,
|
||||
} from '@/providers/passportDataProvider';
|
||||
import { useSettingStore } from '@/stores/settingStore';
|
||||
import analytics from '@/utils/analytics';
|
||||
import { STORAGE_NAME, useBackupMnemonic } from '@/utils/cloudBackup';
|
||||
import { black, slate500, slate600, white } from '@/utils/colors';
|
||||
import { isUserRegisteredWithAlternativeCSCA } from '@/utils/proving/validateDocument';
|
||||
|
||||
const { trackEvent } = analytics();
|
||||
|
||||
const AccountRecoveryChoiceScreen: React.FC = () => {
|
||||
const { trackEvent } = useSelfClient();
|
||||
const { restoreAccountFromMnemonic } = useAuth();
|
||||
const [restoring, setRestoring] = useState(false);
|
||||
const { cloudBackupEnabled, toggleCloudBackupEnabled, biometricsAvailable } =
|
||||
@@ -85,6 +85,7 @@ const AccountRecoveryChoiceScreen: React.FC = () => {
|
||||
throw new Error('Something wrong happened during cloud recovery');
|
||||
}
|
||||
}, [
|
||||
trackEvent,
|
||||
download,
|
||||
restoreAccountFromMnemonic,
|
||||
cloudBackupEnabled,
|
||||
@@ -94,7 +95,6 @@ const AccountRecoveryChoiceScreen: React.FC = () => {
|
||||
]);
|
||||
|
||||
const handleManualRecoveryPress = useCallback(() => {
|
||||
trackEvent(BackupEvents.MANUAL_RECOVERY_SELECTED);
|
||||
onEnterRecoveryPress();
|
||||
}, [onEnterRecoveryPress]);
|
||||
|
||||
|
||||
@@ -5,11 +5,12 @@
|
||||
import React from 'react';
|
||||
import { View, YStack } from 'tamagui';
|
||||
|
||||
import { BackupEvents } from '@selfxyz/mobile-sdk-alpha/constants/analytics';
|
||||
|
||||
import { PrimaryButton } from '@/components/buttons/PrimaryButton';
|
||||
import { SecondaryButton } from '@/components/buttons/SecondaryButton';
|
||||
import Description from '@/components/typography/Description';
|
||||
import { Title } from '@/components/typography/Title';
|
||||
import { BackupEvents } from '@/consts/analytics';
|
||||
import useHapticNavigation from '@/hooks/useHapticNavigation';
|
||||
import RestoreAccountSvg from '@/images/icons/restore_account.svg';
|
||||
import { ExpandableBottomLayout } from '@/layouts/ExpandableBottomLayout';
|
||||
|
||||
@@ -7,11 +7,12 @@ import React from 'react';
|
||||
import { YStack } from 'tamagui';
|
||||
import { useNavigation } from '@react-navigation/native';
|
||||
|
||||
import { BackupEvents } from '@selfxyz/mobile-sdk-alpha/constants/analytics';
|
||||
|
||||
import proofSuccessAnimation from '@/assets/animations/proof_success.json';
|
||||
import { PrimaryButton } from '@/components/buttons/PrimaryButton';
|
||||
import Description from '@/components/typography/Description';
|
||||
import { Title } from '@/components/typography/Title';
|
||||
import { BackupEvents } from '@/consts/analytics';
|
||||
import { ExpandableBottomLayout } from '@/layouts/ExpandableBottomLayout';
|
||||
import { styles } from '@/screens/prove/ProofRequestStatusScreen';
|
||||
import { black, white } from '@/utils/colors';
|
||||
|
||||
@@ -9,16 +9,17 @@ import { Text, TextArea, View, XStack, YStack } from 'tamagui';
|
||||
import Clipboard from '@react-native-clipboard/clipboard';
|
||||
import { useNavigation } from '@react-navigation/native';
|
||||
|
||||
import { useSelfClient } from '@selfxyz/mobile-sdk-alpha';
|
||||
import { BackupEvents } from '@selfxyz/mobile-sdk-alpha/constants/analytics';
|
||||
|
||||
import { SecondaryButton } from '@/components/buttons/SecondaryButton';
|
||||
import Description from '@/components/typography/Description';
|
||||
import { BackupEvents } from '@/consts/analytics';
|
||||
import Paste from '@/images/icons/paste.svg';
|
||||
import { useAuth } from '@/providers/authProvider';
|
||||
import {
|
||||
loadPassportDataAndSecret,
|
||||
reStorePassportDataWithRightCSCA,
|
||||
} from '@/providers/passportDataProvider';
|
||||
import analytics from '@/utils/analytics';
|
||||
import {
|
||||
black,
|
||||
slate300,
|
||||
@@ -32,7 +33,7 @@ import { isUserRegisteredWithAlternativeCSCA } from '@/utils/proving/validateDoc
|
||||
const RecoverWithPhraseScreen: React.FC = () => {
|
||||
const navigation = useNavigation();
|
||||
const { restoreAccountFromMnemonic } = useAuth();
|
||||
const { trackEvent } = analytics();
|
||||
const { trackEvent } = useSelfClient();
|
||||
const [mnemonic, setMnemonic] = useState<string>();
|
||||
const [restoring, setRestoring] = useState(false);
|
||||
const onPaste = useCallback(async () => {
|
||||
|
||||
@@ -7,26 +7,25 @@ import { YStack } from 'tamagui';
|
||||
import type { StaticScreenProps } from '@react-navigation/native';
|
||||
import { useNavigation } from '@react-navigation/native';
|
||||
|
||||
import { useSelfClient } from '@selfxyz/mobile-sdk-alpha';
|
||||
import { BackupEvents } from '@selfxyz/mobile-sdk-alpha/constants/analytics';
|
||||
|
||||
import BackupDocumentationLink from '@/components/BackupDocumentationLink';
|
||||
import { PrimaryButton } from '@/components/buttons/PrimaryButton';
|
||||
import { SecondaryButton } from '@/components/buttons/SecondaryButton';
|
||||
import { Caption } from '@/components/typography/Caption';
|
||||
import Description from '@/components/typography/Description';
|
||||
import { Title } from '@/components/typography/Title';
|
||||
import { BackupEvents } from '@/consts/analytics';
|
||||
import { useModal } from '@/hooks/useModal';
|
||||
import Cloud from '@/images/icons/logo_cloud_backup.svg';
|
||||
import { ExpandableBottomLayout } from '@/layouts/ExpandableBottomLayout';
|
||||
import type { RootStackParamList } from '@/navigation';
|
||||
import { useAuth } from '@/providers/authProvider';
|
||||
import { useSettingStore } from '@/stores/settingStore';
|
||||
import analytics from '@/utils/analytics';
|
||||
import { STORAGE_NAME, useBackupMnemonic } from '@/utils/cloudBackup';
|
||||
import { black, white } from '@/utils/colors';
|
||||
import { buttonTap, confirmTap } from '@/utils/haptic';
|
||||
|
||||
const { trackEvent } = analytics();
|
||||
|
||||
type NextScreen = keyof Pick<RootStackParamList, 'SaveRecoveryPhrase'>;
|
||||
|
||||
type CloudBackupScreenProps = StaticScreenProps<
|
||||
@@ -39,6 +38,7 @@ type CloudBackupScreenProps = StaticScreenProps<
|
||||
const CloudBackupScreen: React.FC<CloudBackupScreenProps> = ({
|
||||
route: { params },
|
||||
}) => {
|
||||
const { trackEvent } = useSelfClient();
|
||||
const { getOrCreateMnemonic, loginWithBiometrics } = useAuth();
|
||||
const { cloudBackupEnabled, toggleCloudBackupEnabled, biometricsAvailable } =
|
||||
useSettingStore();
|
||||
@@ -95,6 +95,7 @@ const CloudBackupScreen: React.FC<CloudBackupScreenProps> = ({
|
||||
getOrCreateMnemonic,
|
||||
upload,
|
||||
toggleCloudBackupEnabled,
|
||||
trackEvent,
|
||||
]);
|
||||
|
||||
const disableCloudBackups = useCallback(() => {
|
||||
@@ -174,6 +175,7 @@ function BottomButton({
|
||||
cloudBackupEnabled: boolean;
|
||||
nextScreen?: NextScreen;
|
||||
}) {
|
||||
const { trackEvent } = useSelfClient();
|
||||
const navigation = useNavigation();
|
||||
|
||||
const goBack = () => {
|
||||
|
||||
@@ -10,24 +10,24 @@ import { useNavigation } from '@react-navigation/native';
|
||||
import type { NativeStackNavigationProp } from '@react-navigation/native-stack';
|
||||
import { Check, Eraser } from '@tamagui/lucide-icons';
|
||||
|
||||
import { useSelfClient } from '@selfxyz/mobile-sdk-alpha';
|
||||
import { DocumentEvents } from '@selfxyz/mobile-sdk-alpha/constants/analytics';
|
||||
|
||||
import { PrimaryButton } from '@/components/buttons/PrimaryButton';
|
||||
import { SecondaryButton } from '@/components/buttons/SecondaryButton';
|
||||
import ButtonsContainer from '@/components/ButtonsContainer';
|
||||
import { DocumentEvents } from '@/consts/analytics';
|
||||
import type { RootStackParamList } from '@/navigation';
|
||||
import type {
|
||||
DocumentCatalog,
|
||||
DocumentMetadata,
|
||||
} from '@/providers/passportDataProvider';
|
||||
import { usePassport } from '@/providers/passportDataProvider';
|
||||
import analytics from '@/utils/analytics';
|
||||
import { borderColor, textBlack, white } from '@/utils/colors';
|
||||
import { extraYPadding } from '@/utils/constants';
|
||||
import { impactLight } from '@/utils/haptic';
|
||||
|
||||
const { trackEvent } = analytics();
|
||||
|
||||
const PassportDataSelector = () => {
|
||||
const selfClient = useSelfClient();
|
||||
const {
|
||||
loadDocumentCatalog,
|
||||
getAllDocuments,
|
||||
@@ -48,14 +48,15 @@ const PassportDataSelector = () => {
|
||||
const docs = await getAllDocuments();
|
||||
setDocumentCatalog(catalog);
|
||||
setAllDocuments(docs);
|
||||
trackEvent(DocumentEvents.DOCUMENTS_FETCHED, {
|
||||
selfClient.trackEvent(DocumentEvents.DOCUMENTS_FETCHED, {
|
||||
count: catalog.documents.length,
|
||||
});
|
||||
if (catalog.documents.length === 0) {
|
||||
trackEvent(DocumentEvents.NO_DOCUMENTS_FOUND);
|
||||
selfClient.trackEvent(DocumentEvents.NO_DOCUMENTS_FOUND);
|
||||
}
|
||||
setLoading(false);
|
||||
}, [
|
||||
selfClient,
|
||||
loadDocumentCatalog,
|
||||
getAllDocuments,
|
||||
setDocumentCatalog,
|
||||
@@ -73,13 +74,13 @@ const PassportDataSelector = () => {
|
||||
const docs = await getAllDocuments();
|
||||
setDocumentCatalog(catalog);
|
||||
setAllDocuments(docs);
|
||||
trackEvent(DocumentEvents.DOCUMENT_SELECTED);
|
||||
selfClient.trackEvent(DocumentEvents.DOCUMENT_SELECTED);
|
||||
};
|
||||
|
||||
const handleDeleteSpecific = async (documentId: string) => {
|
||||
setLoading(true);
|
||||
await deleteDocument(documentId);
|
||||
trackEvent(DocumentEvents.DOCUMENT_DELETED);
|
||||
selfClient.trackEvent(DocumentEvents.DOCUMENT_DELETED);
|
||||
await loadPassportDataInfo();
|
||||
};
|
||||
|
||||
@@ -269,10 +270,11 @@ const ManageDocumentsScreen: React.FC = () => {
|
||||
const navigation =
|
||||
useNavigation<NativeStackNavigationProp<RootStackParamList>>();
|
||||
const { bottom } = useSafeAreaInsets();
|
||||
const { trackEvent } = useSelfClient();
|
||||
|
||||
useEffect(() => {
|
||||
trackEvent(DocumentEvents.MANAGE_SCREEN_OPENED);
|
||||
}, []);
|
||||
}, [trackEvent]);
|
||||
|
||||
const handleScanDocument = () => {
|
||||
impactLight();
|
||||
|
||||
@@ -8,16 +8,14 @@ import { ScrollView, Separator, XStack, YStack } from 'tamagui';
|
||||
import { useFocusEffect } from '@react-navigation/native';
|
||||
|
||||
import type { PassportMetadata } from '@selfxyz/common/types';
|
||||
import { useSelfClient } from '@selfxyz/mobile-sdk-alpha';
|
||||
import { DocumentEvents } from '@selfxyz/mobile-sdk-alpha/constants/analytics';
|
||||
|
||||
import { Caption } from '@/components/typography/Caption';
|
||||
import { DocumentEvents } from '@/consts/analytics';
|
||||
import { usePassport } from '@/providers/passportDataProvider';
|
||||
import analytics from '@/utils/analytics';
|
||||
import { black, slate200, white } from '@/utils/colors';
|
||||
import { extraYPadding } from '@/utils/constants';
|
||||
|
||||
const { trackEvent } = analytics();
|
||||
|
||||
// TODO clarify if we need more/less keys to be displayed
|
||||
const dataKeysToLabels: Record<
|
||||
keyof Omit<PassportMetadata, 'countryCode' | 'dsc' | 'csca'>,
|
||||
@@ -62,6 +60,7 @@ const InfoRow: React.FC<{
|
||||
);
|
||||
|
||||
const PassportDataInfoScreen: React.FC = () => {
|
||||
const { trackEvent } = useSelfClient();
|
||||
const { getData } = usePassport();
|
||||
const [metadata, setMetadata] = useState<PassportMetadata | null>(null);
|
||||
const { bottom } = useSafeAreaInsets();
|
||||
@@ -80,7 +79,7 @@ const PassportDataInfoScreen: React.FC = () => {
|
||||
|
||||
setMetadata(result.data.passportMetadata!);
|
||||
trackEvent(DocumentEvents.PASSPORT_METADATA_LOADED);
|
||||
}, [metadata, getData]);
|
||||
}, [metadata, getData, trackEvent]);
|
||||
|
||||
useFocusEffect(() => {
|
||||
trackEvent(DocumentEvents.PASSPORT_INFO_OPENED);
|
||||
|
||||
@@ -4,45 +4,10 @@
|
||||
|
||||
import type { JsonMap, JsonValue } from '@segment/analytics-react-native';
|
||||
|
||||
import { TrackEventParams } from '@selfxyz/mobile-sdk-alpha';
|
||||
|
||||
import { createSegmentClient } from '@/Segment';
|
||||
|
||||
/**
|
||||
* Generic reasons:
|
||||
* - network_error: Network connectivity issues
|
||||
* - user_cancelled: User cancelled the operation
|
||||
* - permission_denied: Permission not granted
|
||||
* - invalid_input: Invalid user input
|
||||
* - timeout: Operation timed out
|
||||
* - unknown_error: Unspecified error
|
||||
*
|
||||
* Auth specific:
|
||||
* - invalid_credentials: Invalid login credentials
|
||||
* - biometric_unavailable: Biometric authentication unavailable
|
||||
* - invalid_mnemonic: Invalid mnemonic phrase
|
||||
*
|
||||
* Passport specific:
|
||||
* - invalid_format: Invalid passport format
|
||||
* - expired_passport: Passport is expired
|
||||
* - scan_error: Error during scanning
|
||||
* - nfc_error: NFC read error
|
||||
*
|
||||
* Proof specific:
|
||||
* - verification_failed: Proof verification failed
|
||||
* - session_expired: Session expired
|
||||
* - missing_fields: Required fields missing
|
||||
*
|
||||
* Backup specific:
|
||||
* - backup_not_found: Backup not found
|
||||
* - cloud_service_unavailable: Cloud service unavailable
|
||||
*/
|
||||
|
||||
export interface EventParams {
|
||||
reason?: string | null;
|
||||
duration_seconds?: number;
|
||||
attempt_count?: number;
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
const segmentClient = createSegmentClient();
|
||||
|
||||
function coerceToJsonValue(
|
||||
@@ -102,7 +67,7 @@ function validateParams(
|
||||
): JsonMap | undefined {
|
||||
if (!properties) return undefined;
|
||||
|
||||
const validatedProps = { ...properties } as EventParams;
|
||||
const validatedProps = { ...properties };
|
||||
|
||||
// Ensure duration is formatted as a number with at most 2 decimal places
|
||||
if (validatedProps.duration_seconds !== undefined) {
|
||||
@@ -153,7 +118,7 @@ const analytics = () => {
|
||||
|
||||
return {
|
||||
// Using LiteralCheck will allow constants but not plain string literals
|
||||
trackEvent: (eventName: string, properties?: EventParams) => {
|
||||
trackEvent: (eventName: string, properties?: TrackEventParams) => {
|
||||
_track('event', eventName, properties);
|
||||
},
|
||||
trackScreenView: (
|
||||
|
||||
@@ -25,8 +25,11 @@ import {
|
||||
getPayload,
|
||||
getWSDbRelayerUrl,
|
||||
} from '@selfxyz/common/utils/proving';
|
||||
import {
|
||||
PassportEvents,
|
||||
ProofEvents,
|
||||
} from '@selfxyz/mobile-sdk-alpha/constants/analytics';
|
||||
|
||||
import { PassportEvents, ProofEvents } from '@/consts/analytics';
|
||||
import { navigationRef } from '@/navigation';
|
||||
import { unsafe_getPrivateKey } from '@/providers/authProvider';
|
||||
import {
|
||||
|
||||
@@ -24,8 +24,8 @@ import {
|
||||
import { getLeafDscTree } from '@selfxyz/common/utils/trees';
|
||||
import type { PassportValidationCallbacks } from '@selfxyz/mobile-sdk-alpha';
|
||||
import { isPassportDataValid } from '@selfxyz/mobile-sdk-alpha';
|
||||
import { DocumentEvents } from '@selfxyz/mobile-sdk-alpha/constants/analytics';
|
||||
|
||||
import { DocumentEvents } from '@/consts/analytics';
|
||||
import {
|
||||
getAllDocuments,
|
||||
loadDocumentCatalog,
|
||||
|
||||
@@ -2,11 +2,13 @@
|
||||
// SPDX-License-Identifier: BUSL-1.1
|
||||
// NOTE: Converts to Apache-2.0 on 2029-06-11 per LICENSE.
|
||||
|
||||
import { 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';
|
||||
|
||||
import { useAppUpdates } from '@/hooks/useAppUpdates';
|
||||
import { SelfClientProvider } from '@/providers/selfClientProvider';
|
||||
import { registerModalCallbacks } from '@/utils/modalCallbackRegistry';
|
||||
|
||||
jest.mock('@react-navigation/native', () => ({
|
||||
@@ -28,6 +30,10 @@ jest.mock('@/utils/analytics', () => () => ({
|
||||
const navigate = jest.fn();
|
||||
(useNavigation as jest.Mock).mockReturnValue({ navigate });
|
||||
|
||||
const wrapper = ({ children }: { children: ReactNode }) => (
|
||||
<SelfClientProvider>{children}</SelfClientProvider>
|
||||
);
|
||||
|
||||
describe('useAppUpdates', () => {
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
@@ -39,7 +45,7 @@ describe('useAppUpdates', () => {
|
||||
url: 'u',
|
||||
});
|
||||
|
||||
const { result } = renderHook(() => useAppUpdates());
|
||||
const { result } = renderHook(() => useAppUpdates(), { wrapper });
|
||||
|
||||
// Wait for the async state update to complete
|
||||
await waitFor(() => {
|
||||
@@ -53,7 +59,7 @@ describe('useAppUpdates', () => {
|
||||
url: 'u',
|
||||
});
|
||||
|
||||
const { result } = renderHook(() => useAppUpdates());
|
||||
const { result } = renderHook(() => useAppUpdates(), { wrapper });
|
||||
|
||||
// Wait for the async checkVersion to complete first
|
||||
await waitFor(() => {
|
||||
@@ -13,6 +13,9 @@
|
||||
"../packages/mobile-sdk-alpha/src",
|
||||
"../packages/mobile-sdk-alpha/dist"
|
||||
],
|
||||
"@selfxyz/mobile-sdk-alpha/constants/analytics": [
|
||||
"../packages/mobile-sdk-alpha/dist/esm/constants/analytics.js"
|
||||
],
|
||||
"@/*": ["./src/*"]
|
||||
}
|
||||
},
|
||||
|
||||
@@ -23,6 +23,11 @@
|
||||
"types": "./dist/esm/browser.d.ts",
|
||||
"import": "./dist/esm/browser.js",
|
||||
"require": "./dist/cjs/browser.cjs"
|
||||
},
|
||||
"./constants/analytics": {
|
||||
"types": "./dist/esm/constants/analytics.d.ts",
|
||||
"import": "./dist/esm/constants/analytics.js",
|
||||
"require": "./dist/cjs/constants/analytics.cjs"
|
||||
}
|
||||
},
|
||||
"main": "./dist/cjs/index.cjs",
|
||||
@@ -45,7 +50,7 @@
|
||||
"test": "vitest run",
|
||||
"test:build": "yarn build && yarn test && yarn types && yarn lint",
|
||||
"typecheck": "tsc -p tsconfig.json --noEmit",
|
||||
"types": "tsc -p tsconfig.json --noEmit",
|
||||
"types": "yarn build",
|
||||
"validate:exports": "node ./scripts/validate-exports.mjs",
|
||||
"validate:pkg": "node ./scripts/verify-conditions.mjs"
|
||||
},
|
||||
|
||||
@@ -40,6 +40,7 @@ const distPackageJson = {
|
||||
exports: {
|
||||
'.': './esm/index.js',
|
||||
'./browser': './esm/browser.js',
|
||||
'./constants/analytics': './esm/constants/analytics.js',
|
||||
},
|
||||
};
|
||||
try {
|
||||
|
||||
@@ -3,4 +3,7 @@
|
||||
// NOTE: Converts to Apache-2.0 on 2029-06-11 per LICENSE.
|
||||
|
||||
// Shim configurations for Metro compatibility
|
||||
export const shimConfigs = [{ shimPath: 'browser', targetPath: '../esm/browser.js', name: 'browser' }];
|
||||
export const shimConfigs = [
|
||||
{ shimPath: 'browser', targetPath: '../esm/browser.js', name: 'browser' },
|
||||
{ shimPath: 'constants/analytics', targetPath: '../../esm/constants/analytics.js', name: 'constants/analytics' },
|
||||
];
|
||||
|
||||
@@ -23,6 +23,7 @@ import type {
|
||||
ValidationInput,
|
||||
ValidationResult,
|
||||
} from './types/public';
|
||||
import { TrackEventParams } from './types/public';
|
||||
|
||||
/**
|
||||
* Optional adapter implementations used when a consumer does not provide their
|
||||
@@ -118,9 +119,17 @@ export function createSelfClient({ config, adapters }: { config: Config; adapter
|
||||
};
|
||||
}
|
||||
|
||||
async function trackEvent(event: string, payload?: TrackEventParams): Promise<void> {
|
||||
if (!adapters.analytics) {
|
||||
return;
|
||||
}
|
||||
return adapters.analytics.trackEvent(event, payload);
|
||||
}
|
||||
|
||||
return {
|
||||
scanDocument,
|
||||
validateDocument,
|
||||
trackEvent,
|
||||
checkRegistration,
|
||||
registerDocument,
|
||||
generateProof,
|
||||
|
||||
@@ -37,8 +37,7 @@ export const BackupEvents = {
|
||||
CLOUD_BACKUP_ENABLED_DONE: 'Backup: Cloud Backup Enabled Done',
|
||||
CLOUD_BACKUP_ENABLE_STARTED: 'Backup: Cloud Backup Enable Started',
|
||||
CLOUD_BACKUP_STARTED: 'Backup: Cloud Backup Started',
|
||||
CLOUD_RESTORE_FAILED_PASSPORT_NOT_REGISTERED:
|
||||
'Backup: Cloud Restore Failed: Passport Not Registered',
|
||||
CLOUD_RESTORE_FAILED_PASSPORT_NOT_REGISTERED: 'Backup: Cloud Restore Failed: Passport Not Registered',
|
||||
CLOUD_RESTORE_FAILED_UNKNOWN: 'Backup: Cloud Restore Failed: Unknown Error',
|
||||
CLOUD_RESTORE_SUCCESS: 'Backup: Cloud Restore Success',
|
||||
CREATE_NEW_ACCOUNT: 'Backup: Create New Account',
|
||||
@@ -77,10 +76,8 @@ export const MockDataEvents = {
|
||||
};
|
||||
|
||||
export const NotificationEvents = {
|
||||
BACKGROUND_NOTIFICATION_OPENED:
|
||||
'Notification: Background Notification Opened',
|
||||
COLD_START_NOTIFICATION_OPENED:
|
||||
'Notification: Cold Start Notification Opened',
|
||||
BACKGROUND_NOTIFICATION_OPENED: 'Notification: Background Notification Opened',
|
||||
COLD_START_NOTIFICATION_OPENED: 'Notification: Cold Start Notification Opened',
|
||||
};
|
||||
|
||||
export const PassportEvents = {
|
||||
@@ -52,7 +52,7 @@ export function SelfClientProvider({ config, adapters = {}, children }: PropsWit
|
||||
* @throws If used outside of a {@link SelfClientProvider}.
|
||||
*/
|
||||
export function useSelfClient(): SelfClient {
|
||||
const ctx = useContext(SelfClientContext);
|
||||
if (!ctx) throw new Error('useSelfClient must be used within a SelfClientProvider');
|
||||
return ctx;
|
||||
const client = useContext(SelfClientContext);
|
||||
if (!client) throw new Error('useSelfClient must be used within a SelfClientProvider');
|
||||
return client;
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
// Types
|
||||
export type {
|
||||
Adapters,
|
||||
AnalyticsAdapter,
|
||||
ClockAdapter,
|
||||
Config,
|
||||
CryptoAdapter,
|
||||
@@ -27,6 +28,7 @@ export type {
|
||||
ScannerAdapter,
|
||||
SelfClient,
|
||||
StorageAdapter,
|
||||
TrackEventParams,
|
||||
Unsubscribe,
|
||||
ValidationInput,
|
||||
ValidationResult,
|
||||
|
||||
@@ -32,6 +32,37 @@ export interface MRZInfo {
|
||||
validation: MRZValidation;
|
||||
}
|
||||
|
||||
/** * Generic reasons:
|
||||
* - network_error: Network connectivity issues
|
||||
* - user_cancelled: User cancelled the operation
|
||||
* - permission_denied: Permission not granted
|
||||
* - invalid_input: Invalid user input
|
||||
* - timeout: Operation timed out
|
||||
* - unknown_error: Unspecified error * * Auth specific:
|
||||
* - invalid_credentials: Invalid login credentials
|
||||
* - biometric_unavailable: Biometric authentication unavailable
|
||||
* - invalid_mnemonic: Invalid mnemonic phrase * * Passport specific:
|
||||
* - invalid_format: Invalid passport format
|
||||
* - expired_passport: Passport is expired
|
||||
* - scan_error: Error during scanning
|
||||
* - nfc_error: NFC read error * * Proof specific:
|
||||
* - verification_failed: Proof verification failed
|
||||
* - session_expired: Session expired
|
||||
* - missing_fields: Required fields missing * * Backup specific:
|
||||
* - backup_not_found: Backup not found
|
||||
* - cloud_service_unavailable: Cloud service unavailable
|
||||
* */
|
||||
export interface TrackEventParams {
|
||||
reason?: string | null;
|
||||
duration_seconds?: number;
|
||||
attempt_count?: number;
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
export interface AnalyticsAdapter {
|
||||
trackEvent(event: string, payload?: TrackEventParams): void;
|
||||
}
|
||||
|
||||
export interface ClockAdapter {
|
||||
now(): number;
|
||||
sleep(ms: number, signal?: AbortSignal): Promise<void>;
|
||||
@@ -59,6 +90,7 @@ export interface Adapters {
|
||||
network: NetworkAdapter;
|
||||
clock: ClockAdapter;
|
||||
logger: LoggerAdapter;
|
||||
analytics: AnalyticsAdapter;
|
||||
}
|
||||
|
||||
export interface ProofHandle {
|
||||
@@ -130,6 +162,7 @@ export interface SelfClient {
|
||||
},
|
||||
): Promise<ProofHandle>;
|
||||
extractMRZInfo(mrz: string): MRZInfo;
|
||||
trackEvent(event: string, payload?: TrackEventParams): void;
|
||||
on<E extends SDKEvent>(event: E, cb: (payload: SDKEventMap[E]) => void): Unsubscribe;
|
||||
emit<E extends SDKEvent>(event: E, payload: SDKEventMap[E]): void;
|
||||
}
|
||||
|
||||
@@ -97,6 +97,20 @@ describe('createSelfClient', () => {
|
||||
reason: 'SELF_REG_STATUS_STUB',
|
||||
});
|
||||
});
|
||||
describe('when analytics adapter is given', () => {
|
||||
it('calls that adapter for trackEvent', () => {
|
||||
const trackEvent = vi.fn();
|
||||
const client = createSelfClient({
|
||||
config: {},
|
||||
adapters: { scanner, network, crypto, analytics: { trackEvent } },
|
||||
});
|
||||
|
||||
client.trackEvent('test_event');
|
||||
expect(trackEvent).toHaveBeenCalledWith('test_event', undefined);
|
||||
client.trackEvent('another_event', { foo: 'bar' });
|
||||
expect(trackEvent).toHaveBeenCalledWith('another_event', { foo: 'bar' });
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
const scanner: ScannerAdapter = {
|
||||
|
||||
@@ -4,12 +4,17 @@
|
||||
|
||||
import { defineConfig } from 'tsup';
|
||||
|
||||
const banner = `// SPDX-License-Identifier: BUSL-1.1; Copyright (c) 2025 Social Connect Labs, Inc.; Licensed under BUSL-1.1 (see LICENSE); Apache-2.0 from 2029-06-11`;
|
||||
|
||||
const entry = {
|
||||
index: 'src/index.ts',
|
||||
browser: 'src/browser.ts',
|
||||
'constants/analytics': 'src/constants/analytics.ts',
|
||||
};
|
||||
|
||||
export default defineConfig([
|
||||
{
|
||||
entry: {
|
||||
index: 'src/index.ts',
|
||||
browser: 'src/browser.ts',
|
||||
},
|
||||
entry,
|
||||
format: ['esm'],
|
||||
dts: true,
|
||||
sourcemap: true,
|
||||
@@ -24,14 +29,11 @@ export default defineConfig([
|
||||
options.legalComments = 'eof';
|
||||
},
|
||||
banner: {
|
||||
js: `// SPDX-License-Identifier: BUSL-1.1; Copyright (c) 2025 Social Connect Labs, Inc.; Licensed under BUSL-1.1 (see LICENSE); Apache-2.0 from 2029-06-11`,
|
||||
js: banner,
|
||||
},
|
||||
},
|
||||
{
|
||||
entry: {
|
||||
index: 'src/index.ts',
|
||||
browser: 'src/browser.ts',
|
||||
},
|
||||
entry,
|
||||
format: ['cjs'],
|
||||
dts: false,
|
||||
sourcemap: true,
|
||||
@@ -47,7 +49,7 @@ export default defineConfig([
|
||||
options.legalComments = 'eof';
|
||||
},
|
||||
banner: {
|
||||
js: `// SPDX-License-Identifier: BUSL-1.1; Copyright (c) 2025 Social Connect Labs, Inc.; Licensed under BUSL-1.1 (see LICENSE); Apache-2.0 from 2029-06-11`,
|
||||
js: banner,
|
||||
},
|
||||
},
|
||||
]);
|
||||
|
||||
@@ -7,7 +7,7 @@ const fs = require('fs');
|
||||
const path = require('path');
|
||||
const { glob } = require('glob');
|
||||
|
||||
const LICENSE_HEADER_PATTERN = /SPDX-License-Identifier:/;
|
||||
const LICENSE_HEADER_PATTERN = /^\/\/\s*SPDX-FileCopyrightText:/;
|
||||
const EXTENSIONS = ['**/*.ts', '**/*.tsx', '**/*.js', '**/*.jsx'];
|
||||
|
||||
function checkFile(filePath) {
|
||||
|
||||
@@ -5234,7 +5234,6 @@ __metadata:
|
||||
react-native-logs: "npm:^5.3.0"
|
||||
react-native-nfc-manager: "npm:^3.15.1"
|
||||
react-native-passport-reader: "npm:^1.0.3"
|
||||
react-native-round-flags: "npm:^1.0.4"
|
||||
react-native-safe-area-context: "npm:^5.5.1"
|
||||
react-native-screens: "npm:4.9.0"
|
||||
react-native-sqlite-storage: "npm:^6.0.1"
|
||||
@@ -22585,13 +22584,6 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"react-native-round-flags@npm:^1.0.4":
|
||||
version: 1.0.4
|
||||
resolution: "react-native-round-flags@npm:1.0.4"
|
||||
checksum: 10c0/d09bf2fe9fd16aac3b7eba9034b30cb5a21c6dff9f9f46670cba3b393c0a8b8b37c06fde8292eb1ff492568e89619883f499091b689ddf1e556cf96e2e2ca03a
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"react-native-safe-area-context@npm:^5.5.1":
|
||||
version: 5.5.1
|
||||
resolution: "react-native-safe-area-context@npm:5.5.1"
|
||||
|
||||
Reference in New Issue
Block a user