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:
Aaron DeRuvo
2025-08-26 15:40:14 +02:00
committed by GitHub
parent cf75bfa0ba
commit 4367780cd6
48 changed files with 221 additions and 157 deletions

View File

@@ -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,

View File

@@ -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 {

View File

@@ -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];

View File

@@ -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 () => {

View File

@@ -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';

View File

@@ -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';

View File

@@ -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';

View File

@@ -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();

View File

@@ -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);
},
},
}),
[],
);

View File

@@ -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';

View File

@@ -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);

View File

@@ -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';

View File

@@ -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';

View File

@@ -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, () => {});

View File

@@ -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';

View File

@@ -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';

View File

@@ -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();

View File

@@ -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';

View File

@@ -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';

View File

@@ -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';

View File

@@ -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: {},
});

View File

@@ -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,

View File

@@ -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);

View File

@@ -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();

View File

@@ -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]);

View File

@@ -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';

View File

@@ -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';

View File

@@ -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 () => {

View File

@@ -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 = () => {

View File

@@ -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();

View File

@@ -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);

View File

@@ -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: (

View File

@@ -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 {

View File

@@ -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,

View File

@@ -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(() => {

View File

@@ -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/*"]
}
},

View File

@@ -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"
},

View File

@@ -40,6 +40,7 @@ const distPackageJson = {
exports: {
'.': './esm/index.js',
'./browser': './esm/browser.js',
'./constants/analytics': './esm/constants/analytics.js',
},
};
try {

View File

@@ -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' },
];

View File

@@ -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,

View File

@@ -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 = {

View File

@@ -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;
}

View File

@@ -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,

View File

@@ -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;
}

View File

@@ -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 = {

View File

@@ -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,
},
},
]);

View File

@@ -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) {

View File

@@ -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"