Changeset View
Changeset View
Standalone View
Standalone View
native/navigation/deep-links-context-provider.react.js
Show All 9 Lines | import { | ||||
verifyInviteLinkActionTypes, | verifyInviteLinkActionTypes, | ||||
} from 'lib/actions/link-actions.js'; | } from 'lib/actions/link-actions.js'; | ||||
import { | import { | ||||
parseInstallReferrerFromInviteLinkURL, | parseInstallReferrerFromInviteLinkURL, | ||||
parseDataFromDeepLink, | parseDataFromDeepLink, | ||||
type ParsedDeepLinkData, | type ParsedDeepLinkData, | ||||
} from 'lib/facts/links.js'; | } from 'lib/facts/links.js'; | ||||
import { isLoggedIn } from 'lib/selectors/user-selectors.js'; | import { isLoggedIn } from 'lib/selectors/user-selectors.js'; | ||||
import { getKeyserverOverrideForAnInviteLink } from 'lib/shared/invite-links.js'; | |||||
import type { KeyserverOverride } from 'lib/shared/invite-links.js'; | |||||
import type { SetState } from 'lib/types/hook-types.js'; | import type { SetState } from 'lib/types/hook-types.js'; | ||||
import { useDispatchActionPromise } from 'lib/utils/redux-promise-utils.js'; | import { useDispatchActionPromise } from 'lib/utils/redux-promise-utils.js'; | ||||
import { | import { | ||||
InviteLinkModalRouteName, | InviteLinkModalRouteName, | ||||
SecondaryDeviceQRCodeScannerRouteName, | SecondaryDeviceQRCodeScannerRouteName, | ||||
} from './route-names.js'; | } from './route-names.js'; | ||||
import { useSelector } from '../redux/redux-utils.js'; | import { useSelector } from '../redux/redux-utils.js'; | ||||
▲ Show 20 Lines • Show All 48 Lines • ▼ Show 20 Lines | const checkInstallReferrer = React.useCallback(async () => { | ||||
} | } | ||||
const linkSecret = parseInstallReferrerFromInviteLinkURL(installReferrer); | const linkSecret = parseInstallReferrerFromInviteLinkURL(installReferrer); | ||||
if (linkSecret) { | if (linkSecret) { | ||||
setCurrentLink(linkSecret); | setCurrentLink(linkSecret); | ||||
} | } | ||||
}, []); | }, []); | ||||
useOnFirstLaunchEffect('ANDROID_REFERRER', checkInstallReferrer); | useOnFirstLaunchEffect('ANDROID_REFERRER', checkInstallReferrer); | ||||
const [keyserverOverride, setKeyserverOverride] = | |||||
React.useState<?KeyserverOverride>(undefined); | |||||
const inviteLinkSecret = React.useRef<?string>(null); | |||||
const loggedIn = useSelector(isLoggedIn); | const loggedIn = useSelector(isLoggedIn); | ||||
const dispatchActionPromise = useDispatchActionPromise(); | const dispatchActionPromise = useDispatchActionPromise(); | ||||
const validateLink = useVerifyInviteLink(); | const validateLink = useVerifyInviteLink(keyserverOverride); | ||||
const navigation = useNavigation(); | const navigation = useNavigation(); | ||||
React.useEffect(() => { | React.useEffect(() => { | ||||
void (async () => { | void (async () => { | ||||
if (!loggedIn || !currentLink) { | if (!loggedIn || !currentLink) { | ||||
return; | return; | ||||
} | } | ||||
// We're setting this to null so that we ensure that each link click | // We're setting this to null so that we ensure that each link click | ||||
// results in at most one validation and navigation. | // results in at most one validation and navigation. | ||||
setCurrentLink(null); | setCurrentLink(null); | ||||
setKeyserverOverride(undefined); | |||||
inviteLinkSecret.current = null; | |||||
const parsedData: ParsedDeepLinkData = parseDataFromDeepLink(currentLink); | const parsedData: ParsedDeepLinkData = parseDataFromDeepLink(currentLink); | ||||
if (!parsedData) { | if (!parsedData) { | ||||
return; | return; | ||||
} | } | ||||
if (parsedData.type === 'invite-link') { | if (parsedData.type === 'invite-link') { | ||||
const { secret } = parsedData.data; | const { secret } = parsedData.data; | ||||
inviteLinkSecret.current = secret; | |||||
try { | |||||
const newKeyserverOverride = | |||||
await getKeyserverOverrideForAnInviteLink(secret); | |||||
setKeyserverOverride(newKeyserverOverride); | |||||
} catch (e) { | |||||
console.log('Error while downloading an invite link blob', e); | |||||
navigation.navigate<'InviteLinkModal'>({ | |||||
name: InviteLinkModalRouteName, | |||||
params: { | |||||
invitationDetails: { | |||||
status: 'invalid', | |||||
}, | |||||
secret, | |||||
}, | |||||
}); | |||||
} | |||||
} else if (parsedData.type === 'qr-code') { | |||||
navigation.navigate(SecondaryDeviceQRCodeScannerRouteName); | |||||
} | |||||
})(); | |||||
}, [currentLink, loggedIn, navigation]); | |||||
React.useEffect(() => { | |||||
const secret = inviteLinkSecret.current; | |||||
if (keyserverOverride === undefined || !secret) { | |||||
return; | |||||
} | |||||
setKeyserverOverride(undefined); | |||||
void (async () => { | |||||
let result; | |||||
try { | |||||
const validateLinkPromise = validateLink({ secret }); | const validateLinkPromise = validateLink({ secret }); | ||||
void dispatchActionPromise( | void dispatchActionPromise( | ||||
verifyInviteLinkActionTypes, | verifyInviteLinkActionTypes, | ||||
validateLinkPromise, | validateLinkPromise, | ||||
); | ); | ||||
const result = await validateLinkPromise; | result = await validateLinkPromise; | ||||
if (result.status === 'already_joined') { | if (result.status === 'already_joined') { | ||||
return; | return; | ||||
} | } | ||||
} catch (e) { | |||||
console.log(e); | |||||
result = { | |||||
status: 'invalid', | |||||
}; | |||||
} | |||||
navigation.navigate<'InviteLinkModal'>({ | navigation.navigate<'InviteLinkModal'>({ | ||||
name: InviteLinkModalRouteName, | name: InviteLinkModalRouteName, | ||||
params: { | params: { | ||||
invitationDetails: result, | invitationDetails: result, | ||||
secret, | secret, | ||||
}, | }, | ||||
}); | }); | ||||
} else if (parsedData.type === 'qr-code') { | |||||
navigation.navigate(SecondaryDeviceQRCodeScannerRouteName); | |||||
} | |||||
})(); | })(); | ||||
}, [currentLink, dispatchActionPromise, loggedIn, navigation, validateLink]); | }, [dispatchActionPromise, keyserverOverride, navigation, validateLink]); | ||||
const contextValue = React.useMemo( | const contextValue = React.useMemo( | ||||
() => ({ | () => ({ | ||||
setCurrentLinkUrl: setCurrentLink, | setCurrentLinkUrl: setCurrentLink, | ||||
}), | }), | ||||
[], | [], | ||||
); | ); | ||||
return ( | return ( | ||||
<DeepLinksContext.Provider value={contextValue}> | <DeepLinksContext.Provider value={contextValue}> | ||||
{children} | {children} | ||||
</DeepLinksContext.Provider> | </DeepLinksContext.Provider> | ||||
); | ); | ||||
} | } | ||||
export { DeepLinksContext, DeepLinksContextProvider }; | export { DeepLinksContext, DeepLinksContextProvider }; |