diff --git a/keyserver/.well-known/apple-app-site-association b/keyserver/.well-known/apple-app-site-association --- a/keyserver/.well-known/apple-app-site-association +++ b/keyserver/.well-known/apple-app-site-association @@ -10,12 +10,15 @@ "components": [ { "/": "/invite/*" + }, + { + "/": "/qr-code/*" } ] }, { "appID": "H98Y8MH53M.app.comm", - "paths": [ "/invite/*" ] + "paths": [ "/invite/*", "/qr-code/*" ] } ] } diff --git a/lib/facts/links.js b/lib/facts/links.js --- a/lib/facts/links.js +++ b/lib/facts/links.js @@ -1,7 +1,23 @@ // @flow +const inviteLinkUrlPrefix = 'https://comm.app/invite/'; function inviteLinkUrl(secret: string): string { - return `https://comm.app/invite/${secret}`; + return `${inviteLinkUrlPrefix}${secret}`; } -export { inviteLinkUrl }; +const qrCodeLinkUrlPrefix = 'comm://qr-code/'; +function qrCodeLinkUrl(aes256Param: string, ed25519Param: string): string { + const keys = { + aes256: aes256Param, + ed25519: ed25519Param, + }; + const keysString = encodeURIComponent(JSON.stringify(keys)); + return `${qrCodeLinkUrlPrefix}${keysString}`; +} + +export { + inviteLinkUrlPrefix, + inviteLinkUrl, + qrCodeLinkUrlPrefix, + qrCodeLinkUrl, +}; diff --git a/native/android/app/src/main/AndroidManifest.xml b/native/android/app/src/main/AndroidManifest.xml --- a/native/android/app/src/main/AndroidManifest.xml +++ b/native/android/app/src/main/AndroidManifest.xml @@ -68,6 +68,11 @@ android:host="comm.app" android:pathPrefix="/invite" /> + currentLink?.startsWith(inviteLinkUrlPrefix), + [currentLink], + ); React.useEffect(() => { (async () => { - if (!loggedIn || !currentLink) { + if (!loggedIn || !currentLink || !isCurrentLinkInviteLink) { return; } // We're setting this to null so that we ensure that each link click @@ -107,7 +112,14 @@ }, }); })(); - }, [currentLink, dispatchActionPromise, loggedIn, navigation, validateLink]); + }, [ + currentLink, + dispatchActionPromise, + loggedIn, + isCurrentLinkInviteLink, + navigation, + validateLink, + ]); const contextValue = React.useMemo( () => ({ diff --git a/native/navigation/navigation-handler.react.js b/native/navigation/navigation-handler.react.js --- a/native/navigation/navigation-handler.react.js +++ b/native/navigation/navigation-handler.react.js @@ -11,6 +11,7 @@ import { useIsAppLoggedIn } from './nav-selectors.js'; import { NavContext, type NavAction } from './navigation-context.js'; import PolicyAcknowledgmentHandler from './policy-acknowledgment-handler.react.js'; +import QRCodeLinkHandler from './qr-code-link-handler.react.js'; import ThreadScreenTracker from './thread-screen-tracker.react.js'; import DevTools from '../redux/dev-tools.react.js'; import { useSelector } from '../redux/redux-utils.js'; @@ -43,6 +44,7 @@ + {devTools} > ); diff --git a/native/navigation/qr-code-link-handler.react.js b/native/navigation/qr-code-link-handler.react.js new file mode 100644 --- /dev/null +++ b/native/navigation/qr-code-link-handler.react.js @@ -0,0 +1,50 @@ +// @flow + +import { useNavigation } from '@react-navigation/native'; +import * as React from 'react'; +import { Linking } from 'react-native'; + +import { qrCodeLinkUrlPrefix } from 'lib/facts/links.js'; +import { isLoggedIn } from 'lib/selectors/user-selectors.js'; + +import { SecondaryDeviceQRCodeScannerRouteName } from './route-names.js'; +import { useSelector } from '../redux/redux-utils.js'; + +function QRCodeLinkHandler(): null { + const [currentLink, setCurrentLink] = React.useState(null); + + React.useEffect(() => { + const subscription = Linking.addEventListener('url', ({ url }) => + setCurrentLink(url), + ); + (async () => { + const initialURL = await Linking.getInitialURL(); + if (initialURL) { + setCurrentLink(initialURL); + } + })(); + + return () => subscription.remove(); + }, []); + + const loggedIn = useSelector(isLoggedIn); + const { navigate } = useNavigation(); + const isQRCodeLink = React.useMemo( + () => currentLink?.startsWith(qrCodeLinkUrlPrefix), + [currentLink], + ); + + React.useEffect(() => { + if (!loggedIn || !currentLink || !isQRCodeLink) { + return; + } + + setCurrentLink(null); + + navigate(SecondaryDeviceQRCodeScannerRouteName); + }, [currentLink, loggedIn, isQRCodeLink, navigate]); + + return null; +} + +export default QRCodeLinkHandler; diff --git a/native/profile/secondary-device-qr-code-scanner.react.js b/native/profile/secondary-device-qr-code-scanner.react.js --- a/native/profile/secondary-device-qr-code-scanner.react.js +++ b/native/profile/secondary-device-qr-code-scanner.react.js @@ -36,10 +36,14 @@ }, [navigation]); const onConnect = React.useCallback((barCodeEvent: BarCodeEvent) => { - const { type, data } = barCodeEvent; + const { data } = barCodeEvent; + const extractKeysFromURLRegex = /comm:\/\/qr-code\/(.*)/; + const keysMatch = data.match(extractKeysFromURLRegex)?.[1] ?? ''; + const keys = JSON.parse(decodeURIComponent(keysMatch)); + Alert.alert( 'Scan successful', - `Bar code with type ${type} and data ${data} has been scanned!`, + `QR code contains the following keys: ${JSON.stringify(keys)}`, [{ text: 'OK' }], ); }, []); diff --git a/native/qr-code/qr-code-screen.react.js b/native/qr-code/qr-code-screen.react.js --- a/native/qr-code/qr-code-screen.react.js +++ b/native/qr-code/qr-code-screen.react.js @@ -4,6 +4,8 @@ import { View, Text } from 'react-native'; import QRCode from 'react-native-qrcode-svg'; +import { qrCodeLinkUrl } from 'lib/facts/links.js'; + import type { QRCodeSignInNavigationProp } from './qr-code-sign-in-navigator.react.js'; import type { NavigationRoute } from '../navigation/route-names.js'; import { useStyles } from '../themes/colors.js'; @@ -13,6 +15,8 @@ +route: NavigationRoute<'QRCodeScreen'>, }; +const qrCodeValue = qrCodeLinkUrl('random_aes256_key', 'device_ed25519_key'); + // eslint-disable-next-line no-unused-vars function QRCodeScreen(props: QRCodeScreenProps): React.Node { const styles = useStyles(unboundStyles); @@ -22,7 +26,7 @@ Open the Comm app on your phone and scan the QR code below - + How to find the scanner: diff --git a/web/account/qr-code-login.react.js b/web/account/qr-code-login.react.js --- a/web/account/qr-code-login.react.js +++ b/web/account/qr-code-login.react.js @@ -3,8 +3,12 @@ import { QRCodeSVG } from 'qrcode.react'; import * as React from 'react'; +import { qrCodeLinkUrl } from 'lib/facts/links.js'; + import css from './qr-code-login.css'; +const qrCodeValue = qrCodeLinkUrl('random_aes256_key', 'device_ed25519_key'); + function QrCodeLogin(): React.Node { return ( @@ -12,12 +16,7 @@ Open the Comm app on your phone and scan the QR code below - + How to find the scanner: