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 @@ -4,4 +4,13 @@ return `https://comm.app/invite/${secret}`; } -export { inviteLinkUrl }; +function qrCodeLinkUrl(aes256Param: string, ed25519Param: string): string { + const keys = { + aes256: aes256Param, + ed25519: ed25519Param, + }; + const keysString = encodeURIComponent(JSON.stringify(keys)); + return `comm://qr-code/${keysString}`; +} + +export { inviteLinkUrl, 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" /> + + {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,56 @@ +// @flow + +import { useNavigation } from '@react-navigation/native'; +import * as React from 'react'; +import { Linking } from 'react-native'; + +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(); + + React.useEffect(() => { + if (!loggedIn || !currentLink) { + return; + } + + setCurrentLink(null); + + const keys = parseKeys(currentLink); + if (!keys) { + return; + } + + navigate(SecondaryDeviceQRCodeScannerRouteName); + }, [currentLink, loggedIn, navigate]); + + return null; +} + +const urlRegex = /qr-code\/(\S+)$/; +function parseKeys(url: string) { + const match = urlRegex.exec(url); + return match?.[1]; +} + +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: