mirror of
https://github.com/selfxyz/self.git
synced 2026-04-05 03:00:53 -04:00
chore: update dev with staging 09/06/25 (#1007)
* update CI
* bump iOS version
* update readme
* update mobile-deploy ci
* bump version iOS
* update workflow to use workload identity federation (#933)
* update workflow to use workload identity federation
* add token permissions
* correct provider name
* chore: incrementing android build version for version 2.6.4 [github action]
---------
Co-authored-by: Self GitHub Actions <action@github.com>
* update ci
* update ci
* update ci
* update ci
* update ci
* fix ci
* fix ci
* fix ci
* remove fastlane use for android
* bump iOS build version
* update CI python script
* iterate on CI
* iterate on CI
* iterate on CI
* Dev (#941)
* SDK Go version (#920)
* feat: helper functions and constant for go-sdk
* feat: formatRevealedDataPacked in go
* chore: refactor
* feat: define struct for selfBackendVerifier
* feat: verify function for selfBackendVerifier
* feat(wip): custom hasher
* feat: SelfVerifierBacked in go
* test(wip): scope and userContextHash is failing
* test: zk proof verified
* fix: MockConfigStore getactionId function
* chore: refactor
* chore: remove abi duplicate files
* chore: move configStore to utils
* chore: modified VcAndDiscloseProof struct
* chore: more review changes
* feat: impl DefaultConfig and InMemoryConfigStore
* chore: refactor and export functions
* fix: module import and README
* chore: remove example folder
* chore: remove pointers from VerificationConfig
* chore: coderabbit review fixes
* chore: more coderabbit review fix
* chore: add license
* fix: convert attestationIdd to int
* chore: remove duplicate code
---------
Co-authored-by: ayman <aymanshaik1015@gmail.com>
* Moving proving Utils to common (#935)
* remove react dom
* moves proving utils to the common
* need to use rn components
* fix imports
* add proving-utils and dedeuplicate entry configs for esm and cjs.
* must wrap in text component
* fix metro bundling
* fix mock import
* fix builds and tests
* please save me
* solution?
* fix test
* Move proving inputs to the common package (#937)
* create ofactTree type to share
* move proving inputs from app to register inputs in common
* missed reexport
* ok
* add some validations as suggested by our ai overlords
* Fix mock passport flow (#942)
* fix dev screens
* add hint
* rename
* fix path
* fix mobile-ci path
* fix: extractMRZ (#938)
* fix: extractMRZ
* yarn nice && yarn types
* fix test: remove unused
* fix mobile ci
* add script
---------
Co-authored-by: Justin Hernandez <transphorm@gmail.com>
* Move Proving attest and cose (#950)
* moved attest and cose utils to common
with cursor converted tests in common to use vitest and converted coseVerify.test to vitest after moving from app to common
what does cryptoLoader do?
* moved away
* get buff
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
---------
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* SELF-253 feat: add user email feedback (#889)
* feat: add sentry feedback
* add sentry feedback to web
* feat: add custom feedback modal & fix freeze on IOS
* yarn nice
* update lock
* feat: show feedback widget on NFC scan issues (#948)
* feat: show feedback widget on NFC scan issues
* fix ref
* clean up
* fix report issue screen
* abstract send user feedback email logic
* fixes
* change text to Report Issue
* sanitize email and track event messge
* remove unnecessary sanitization
* add sanitize error message tests
* fix tests
* save wip. almost done
* fix screen test
* fix screen test
* remove non working test
---------
Co-authored-by: Justin Hernandez <transphorm@gmail.com>
Co-authored-by: Justin Hernandez <justin.hernandez@self.xyz>
* chore: centralize license header checks (#952)
* chore: centralize license header scripts
* chore: run license header checks from root
* add header to other files
* add header to bundle
* add migration script and update check license headers
* convert license to mobile sdk
* migrate license headers
* remove headers from common; convert remaining
* fix headers
* add license header checks
* update unsupported passport screen (#953)
* update unsupported passport screen
* yarn nice
---------
Co-authored-by: Vishalkulkarni45 <109329073+Vishalkulkarni45@users.noreply.github.com>
Co-authored-by: ayman <aymanshaik1015@gmail.com>
Co-authored-by: Aaron DeRuvo <aaron.deruvo@clabs.co>
Co-authored-by: Justin Hernandez <justin.hernandez@self.xyz>
Co-authored-by: Seshanth.S🐺 <35675963+seshanthS@users.noreply.github.com>
Co-authored-by: Justin Hernandez <transphorm@gmail.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* bump version
* bump yarn.lock
* update ci (#966)
* chore: Manually bump and release v2.6.4 (#961)
* update lock files
* bump and build android
* update build artifacts
* show generate mock document button
* update lock
* fix formatting and update failing e2e test
* revert podfile
* fixes
* fix cold start of the app with deeplink
* update ci
* update ci
* Sync MARKETING_VERSION to iOS project files after version bump
* chore: incrementing android build version for version 2.6.4 [github action] (#976)
Co-authored-by: remicolin <98749896+remicolin@users.noreply.github.com>
* chore: add build dependencies step for iOS and Android in mobile deploy workflow
* chore: enhance mobile deploy workflow by adding CMake installation step
* bump android build version
* chore: incrementing android build version for version 2.6.4 [github action] (#985)
Co-authored-by: remicolin <98749896+remicolin@users.noreply.github.com>
* chore: configure Metro bundler for production compatibility in mobile deploy workflow
* chore: incrementing android build version for version 2.6.4 [github action] (#987)
Co-authored-by: remicolin <98749896+remicolin@users.noreply.github.com>
* Revert "chore: configure Metro bundler for production compatibility in mobile deploy workflow"
This reverts commit 60fc1f2580.
* reduce max old space size in mobile-deploy ci
* fix android french id card (#957)
* fix android french id card
* fix common ci cache
* feat: log apdu (#988)
---------
Co-authored-by: Justin Hernandez <transphorm@gmail.com>
Co-authored-by: Seshanth.S🐺 <35675963+seshanthS@users.noreply.github.com>
* unblock ci
* fix merge
* merge fixes
* fix tests
* make ci happy
---------
Co-authored-by: turnoffthiscomputer <colin.remi07@gmail.com>
Co-authored-by: pputman-clabs <99900942+pputman-clabs@users.noreply.github.com>
Co-authored-by: Self GitHub Actions <action@github.com>
Co-authored-by: turnoffthiscomputer <98749896+remicolin@users.noreply.github.com>
Co-authored-by: Vishalkulkarni45 <109329073+Vishalkulkarni45@users.noreply.github.com>
Co-authored-by: ayman <aymanshaik1015@gmail.com>
Co-authored-by: Aaron DeRuvo <aaron.deruvo@clabs.co>
Co-authored-by: Seshanth.S🐺 <35675963+seshanthS@users.noreply.github.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
This commit is contained in:
@@ -13,6 +13,9 @@ const DevFeatureFlagsScreen = lazy(
|
||||
const DevHapticFeedbackScreen = lazy(
|
||||
() => import('@/screens/dev/DevHapticFeedbackScreen'),
|
||||
);
|
||||
const DevPrivateKeyScreen = lazy(
|
||||
() => import('@/screens/dev/DevPrivateKeyScreen'),
|
||||
);
|
||||
const DevSettingsScreen = lazy(() => import('@/screens/dev/DevSettingsScreen'));
|
||||
const CreateMockScreen = lazy(() => import('@/screens/dev/CreateMockScreen'));
|
||||
const CreateMockScreenDeepLink = lazy(
|
||||
@@ -71,6 +74,13 @@ const devScreens = {
|
||||
},
|
||||
} as NativeStackNavigationOptions,
|
||||
},
|
||||
DevPrivateKey: {
|
||||
screen: DevPrivateKeyScreen,
|
||||
options: {
|
||||
...devHeaderOptions,
|
||||
title: 'Private Key',
|
||||
} as NativeStackNavigationOptions,
|
||||
},
|
||||
};
|
||||
|
||||
export default devScreens;
|
||||
@@ -16,7 +16,7 @@ import { createNativeStackNavigator } from '@react-navigation/native-stack';
|
||||
import { DefaultNavBar } from '@/components/NavBar';
|
||||
import AppLayout from '@/layouts/AppLayout';
|
||||
import { getAesopScreens } from '@/navigation/aesop';
|
||||
import devScreens from '@/navigation/dev';
|
||||
import devScreens from '@/navigation/devTools';
|
||||
import documentScreens from '@/navigation/document';
|
||||
import homeScreens from '@/navigation/home';
|
||||
import proveScreens from '@/navigation/prove';
|
||||
|
||||
110
app/src/screens/dev/DevPrivateKeyScreen.tsx
Normal file
110
app/src/screens/dev/DevPrivateKeyScreen.tsx
Normal file
@@ -0,0 +1,110 @@
|
||||
// SPDX-FileCopyrightText: 2025 Social Connect Labs, Inc.
|
||||
// SPDX-License-Identifier: BUSL-1.1
|
||||
// NOTE: Converts to Apache-2.0 on 2029-06-11 per LICENSE.
|
||||
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
import { Button, Text, XStack, YStack } from 'tamagui';
|
||||
import Clipboard from '@react-native-clipboard/clipboard';
|
||||
|
||||
import { unsafe_getPrivateKey } from '@/providers/authProvider';
|
||||
import { black, slate50, slate200, teal500, white } from '@/utils/colors';
|
||||
import { confirmTap } from '@/utils/haptic';
|
||||
|
||||
const DevPrivateKeyScreen: React.FC = () => {
|
||||
const [privateKey, setPrivateKey] = useState<string | null>(
|
||||
'Loading private key…',
|
||||
);
|
||||
const [isPrivateKeyRevealed, setIsPrivateKeyRevealed] = useState(false);
|
||||
const [copied, setCopied] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
unsafe_getPrivateKey().then(key =>
|
||||
setPrivateKey(key || 'No private key found'),
|
||||
);
|
||||
}, []);
|
||||
|
||||
const handleRevealPrivateKey = useCallback(() => {
|
||||
confirmTap();
|
||||
if (!isPrivateKeyRevealed) {
|
||||
setIsPrivateKeyRevealed(true);
|
||||
}
|
||||
if (isPrivateKeyRevealed) {
|
||||
Clipboard.setString(privateKey || '');
|
||||
setCopied(true);
|
||||
setTimeout(() => setCopied(false), 1500);
|
||||
}
|
||||
}, [isPrivateKeyRevealed, privateKey]);
|
||||
|
||||
const getRedactedPrivateKey = useCallback(() => {
|
||||
if (
|
||||
!privateKey ||
|
||||
privateKey === 'Loading private key…' ||
|
||||
privateKey === 'No private key found'
|
||||
) {
|
||||
return privateKey;
|
||||
}
|
||||
|
||||
// If it starts with 0x, show 0x followed by asterisks for the rest
|
||||
if (privateKey.startsWith('0x')) {
|
||||
const restLength = privateKey.length - 2;
|
||||
return '0x' + '*'.repeat(restLength);
|
||||
}
|
||||
|
||||
// Otherwise, show asterisks for the entire length
|
||||
return '*'.repeat(privateKey.length);
|
||||
}, [privateKey]);
|
||||
|
||||
return (
|
||||
<YStack padding="$4">
|
||||
<YStack position="relative" alignItems="stretch" gap={0}>
|
||||
<XStack
|
||||
borderColor={slate200}
|
||||
backgroundColor={slate50}
|
||||
borderWidth="$1"
|
||||
borderBottomWidth={0}
|
||||
borderTopLeftRadius="$5"
|
||||
borderTopRightRadius="$5"
|
||||
gap={12}
|
||||
paddingHorizontal={26}
|
||||
paddingVertical={28}
|
||||
flexWrap="wrap"
|
||||
>
|
||||
<Text>
|
||||
{isPrivateKeyRevealed ? privateKey : getRedactedPrivateKey()}
|
||||
</Text>
|
||||
</XStack>
|
||||
<XStack
|
||||
borderTopColor={slate200}
|
||||
borderTopWidth="$1"
|
||||
justifyContent="center"
|
||||
alignItems="stretch"
|
||||
>
|
||||
<Button
|
||||
unstyled
|
||||
color={isPrivateKeyRevealed ? (copied ? black : white) : black}
|
||||
borderColor={
|
||||
isPrivateKeyRevealed ? (copied ? teal500 : black) : slate200
|
||||
}
|
||||
backgroundColor={
|
||||
isPrivateKeyRevealed ? (copied ? teal500 : black) : slate50
|
||||
}
|
||||
borderWidth="$1"
|
||||
borderTopWidth={0}
|
||||
borderBottomLeftRadius="$5"
|
||||
borderBottomRightRadius="$5"
|
||||
paddingVertical="$2"
|
||||
onPress={handleRevealPrivateKey}
|
||||
width="100%"
|
||||
textAlign="center"
|
||||
>
|
||||
{isPrivateKeyRevealed
|
||||
? `${copied ? 'COPIED' : 'COPY'} TO CLIPBOARD`
|
||||
: 'TAP TO REVEAL'}
|
||||
</Button>
|
||||
</XStack>
|
||||
</YStack>
|
||||
</YStack>
|
||||
);
|
||||
};
|
||||
|
||||
export default DevPrivateKeyScreen;
|
||||
@@ -126,6 +126,7 @@ const items = [
|
||||
'DevSettings',
|
||||
'DevFeatureFlags',
|
||||
'DevHapticFeedback',
|
||||
'DevPrivateKey',
|
||||
'Splash',
|
||||
'Launch',
|
||||
'DocumentOnboarding',
|
||||
@@ -339,7 +340,32 @@ const DevSettingsScreen: React.FC<DevSettingsScreenProps> = ({}) => {
|
||||
title="Debug Shortcuts"
|
||||
description="Jump directly to any screen for testing"
|
||||
>
|
||||
<ScreenSelector />
|
||||
<YStack gap="$2">
|
||||
<Button
|
||||
style={{ backgroundColor: 'white' }}
|
||||
borderColor={slate200}
|
||||
borderRadius="$2"
|
||||
height="$5"
|
||||
padding={0}
|
||||
onPress={() => {
|
||||
navigation.navigate('DevPrivateKey');
|
||||
}}
|
||||
>
|
||||
<XStack
|
||||
width="100%"
|
||||
justifyContent="space-between"
|
||||
paddingVertical="$3"
|
||||
paddingLeft="$4"
|
||||
paddingRight="$1.5"
|
||||
>
|
||||
<Text fontSize="$5" color={slate500} fontFamily={dinot}>
|
||||
View Private Key
|
||||
</Text>
|
||||
<ChevronRight color={slate500} strokeWidth={2.5} />
|
||||
</XStack>
|
||||
</Button>
|
||||
<ScreenSelector />
|
||||
</YStack>
|
||||
</ParameterSection>
|
||||
|
||||
<ParameterSection
|
||||
|
||||
@@ -204,6 +204,18 @@ const DocumentNFCScanScreen: React.FC = () => {
|
||||
// Add timestamp when scan starts
|
||||
scanCancelledRef.current = false;
|
||||
const scanStartTime = Date.now();
|
||||
if (scanTimeoutRef.current) {
|
||||
clearTimeout(scanTimeoutRef.current);
|
||||
scanTimeoutRef.current = null;
|
||||
}
|
||||
scanTimeoutRef.current = setTimeout(() => {
|
||||
scanCancelledRef.current = true;
|
||||
trackEvent(PassportEvents.NFC_SCAN_FAILED, {
|
||||
error: 'timeout',
|
||||
});
|
||||
openErrorModal('Scan timed out. Please try again.');
|
||||
setIsNfcSheetOpen(false);
|
||||
}, 30000);
|
||||
|
||||
// Mark NFC scanning as active to prevent analytics flush interference
|
||||
setNfcScanningActive(true);
|
||||
|
||||
@@ -316,11 +316,9 @@ const ManageDocumentsScreen: React.FC = () => {
|
||||
<PrimaryButton onPress={handleScanDocument}>
|
||||
Scan New ID Document
|
||||
</PrimaryButton>
|
||||
{__DEV__ && (
|
||||
<SecondaryButton onPress={handleGenerateMock}>
|
||||
Generate Mock Document
|
||||
</SecondaryButton>
|
||||
)}
|
||||
<SecondaryButton onPress={handleGenerateMock}>
|
||||
Generate Mock Document
|
||||
</SecondaryButton>
|
||||
</ButtonsContainer>
|
||||
</YStack>
|
||||
</YStack>
|
||||
|
||||
@@ -24,6 +24,11 @@ import {
|
||||
} from '@/providers/passportDataProvider';
|
||||
import { useSettingStore } from '@/stores/settingStore';
|
||||
import { black } from '@/utils/colors';
|
||||
import {
|
||||
getAndClearQueuedUrl,
|
||||
handleUrl,
|
||||
setDeeplinkParentScreen,
|
||||
} from '@/utils/deeplinks';
|
||||
import { impactLight } from '@/utils/haptic';
|
||||
|
||||
const SplashScreen: React.FC = ({}) => {
|
||||
@@ -36,6 +41,7 @@ const SplashScreen: React.FC = ({}) => {
|
||||
const [nextScreen, setNextScreen] = useState<keyof RootStackParamList | null>(
|
||||
null,
|
||||
);
|
||||
const [queuedDeepLink, setQueuedDeepLink] = useState<string | null>(null);
|
||||
const dataLoadInitiatedRef = useRef(false);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -66,9 +72,22 @@ const SplashScreen: React.FC = ({}) => {
|
||||
}
|
||||
|
||||
const hasValid = await hasAnyValidRegisteredDocument(selfClient);
|
||||
setNextScreen(hasValid ? 'Home' : 'Launch');
|
||||
const parentScreen = hasValid ? 'Home' : 'Launch';
|
||||
|
||||
setDeeplinkParentScreen(parentScreen);
|
||||
|
||||
const queuedUrl = getAndClearQueuedUrl();
|
||||
if (queuedUrl) {
|
||||
if (typeof __DEV__ !== 'undefined' && __DEV__) {
|
||||
console.log('Processing queued deeplink:', queuedUrl);
|
||||
}
|
||||
setQueuedDeepLink(queuedUrl);
|
||||
} else {
|
||||
setNextScreen(parentScreen);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`Error in SplashScreen data loading: ${error}`);
|
||||
setDeeplinkParentScreen('Launch');
|
||||
setNextScreen('Launch');
|
||||
}
|
||||
};
|
||||
@@ -83,12 +102,18 @@ const SplashScreen: React.FC = ({}) => {
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (isAnimationFinished && nextScreen) {
|
||||
requestAnimationFrame(() => {
|
||||
navigation.navigate(nextScreen as never);
|
||||
});
|
||||
if (isAnimationFinished) {
|
||||
if (queuedDeepLink) {
|
||||
requestAnimationFrame(() => {
|
||||
handleUrl(queuedDeepLink);
|
||||
});
|
||||
} else if (nextScreen) {
|
||||
requestAnimationFrame(() => {
|
||||
navigation.navigate(nextScreen as never);
|
||||
});
|
||||
}
|
||||
}
|
||||
}, [isAnimationFinished, nextScreen, navigation]);
|
||||
}, [isAnimationFinished, nextScreen, queuedDeepLink, navigation]);
|
||||
|
||||
return (
|
||||
<LottieView
|
||||
|
||||
@@ -72,6 +72,29 @@ const validateAndSanitizeParam = (
|
||||
return decodedValue;
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates a proper navigation stack for deeplink navigation
|
||||
* @param targetScreen - The target screen to navigate to
|
||||
* @param parentScreen - The parent screen that should appear when user goes back (default: 'Home')
|
||||
*/
|
||||
const createDeeplinkNavigationState = (
|
||||
targetScreen: string,
|
||||
parentScreen: string = 'Home',
|
||||
) => ({
|
||||
index: 1, // Current screen index (targetScreen)
|
||||
routes: [{ name: parentScreen }, { name: targetScreen }],
|
||||
});
|
||||
|
||||
// Store the correct parent screen determined by splash screen
|
||||
let correctParentScreen: string = 'Home';
|
||||
|
||||
// Function for splash screen to get and clear the queued initial URL
|
||||
export const getAndClearQueuedUrl = (): string | null => {
|
||||
const url = queuedInitialUrl;
|
||||
queuedInitialUrl = null;
|
||||
return url;
|
||||
};
|
||||
|
||||
export const handleUrl = (uri: string) => {
|
||||
const validatedParams = parseAndValidateUrlParams(uri);
|
||||
const { sessionId, selfApp: selfAppStr, mock_passport } = validatedParams;
|
||||
@@ -81,19 +104,29 @@ export const handleUrl = (uri: string) => {
|
||||
const selfAppJson = JSON.parse(selfAppStr);
|
||||
useSelfAppStore.getState().setSelfApp(selfAppJson);
|
||||
useSelfAppStore.getState().startAppListener(selfAppJson.sessionId);
|
||||
navigationRef.navigate('Prove');
|
||||
|
||||
// Reset navigation stack with correct parent -> ProveScreen
|
||||
navigationRef.reset(
|
||||
createDeeplinkNavigationState('ProveScreen', correctParentScreen),
|
||||
);
|
||||
|
||||
return;
|
||||
} catch (error) {
|
||||
if (typeof __DEV__ !== 'undefined' && __DEV__) {
|
||||
console.error('Error parsing selfApp:', error);
|
||||
}
|
||||
navigationRef.navigate('QRCodeTrouble');
|
||||
navigationRef.reset(
|
||||
createDeeplinkNavigationState('QRCodeTrouble', correctParentScreen),
|
||||
);
|
||||
}
|
||||
} else if (sessionId && typeof sessionId === 'string') {
|
||||
useSelfAppStore.getState().cleanSelfApp();
|
||||
useSelfAppStore.getState().startAppListener(sessionId);
|
||||
navigationRef.navigate('Prove');
|
||||
|
||||
// Reset navigation stack with correct parent -> ProveScreen
|
||||
navigationRef.reset(
|
||||
createDeeplinkNavigationState('ProveScreen', correctParentScreen),
|
||||
);
|
||||
} else if (mock_passport) {
|
||||
try {
|
||||
const data = JSON.parse(mock_passport);
|
||||
@@ -120,12 +153,17 @@ export const handleUrl = (uri: string) => {
|
||||
gender: rawParams.gender,
|
||||
});
|
||||
|
||||
navigationRef.navigate('MockDataDeepLink');
|
||||
// Reset navigation stack with correct parent -> MockDataDeepLink
|
||||
navigationRef.reset(
|
||||
createDeeplinkNavigationState('MockDataDeepLink', correctParentScreen),
|
||||
);
|
||||
} catch (error) {
|
||||
if (typeof __DEV__ !== 'undefined' && __DEV__) {
|
||||
console.error('Error parsing mock_passport data or navigating:', error);
|
||||
}
|
||||
navigationRef.navigate('QRCodeTrouble');
|
||||
navigationRef.reset(
|
||||
createDeeplinkNavigationState('QRCodeTrouble', correctParentScreen),
|
||||
);
|
||||
}
|
||||
} else if (Platform.OS === 'web') {
|
||||
// TODO: web handle links if we need to idk if we do
|
||||
@@ -134,7 +172,9 @@ export const handleUrl = (uri: string) => {
|
||||
if (typeof __DEV__ !== 'undefined' && __DEV__) {
|
||||
console.error('No sessionId or selfApp found in the data');
|
||||
}
|
||||
navigationRef.navigate('QRCodeTrouble');
|
||||
navigationRef.reset(
|
||||
createDeeplinkNavigationState('QRCodeTrouble', correctParentScreen),
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -166,19 +206,29 @@ export const parseAndValidateUrlParams = (uri: string): ValidatedParams => {
|
||||
return validatedParams;
|
||||
};
|
||||
|
||||
export const setupUniversalLinkListenerInNavigation = () => {
|
||||
const handleNavigation = (url: string) => {
|
||||
handleUrl(url);
|
||||
};
|
||||
// Store the initial URL for splash screen to handle after initialization
|
||||
let queuedInitialUrl: string | null = null;
|
||||
|
||||
/**
|
||||
* Sets the correct parent screen for deeplink navigation
|
||||
* This should be called by splash screen after determining the correct screen
|
||||
*/
|
||||
export const setDeeplinkParentScreen = (screen: string) => {
|
||||
correctParentScreen = screen;
|
||||
};
|
||||
|
||||
export const setupUniversalLinkListenerInNavigation = () => {
|
||||
// Get the initial URL and store it for splash screen handling
|
||||
Linking.getInitialURL().then(url => {
|
||||
if (url) {
|
||||
handleNavigation(url);
|
||||
// Store the initial URL instead of handling it immediately
|
||||
queuedInitialUrl = url;
|
||||
}
|
||||
});
|
||||
|
||||
// Handle subsequent URL events normally (when app is already running)
|
||||
const linkingEventListener = Linking.addEventListener('url', ({ url }) => {
|
||||
handleNavigation(url);
|
||||
handleUrl(url);
|
||||
});
|
||||
|
||||
return () => {
|
||||
|
||||
Reference in New Issue
Block a user