diff --git a/apps/tlon-mobile/android/app/src/main/AndroidManifest.xml b/apps/tlon-mobile/android/app/src/main/AndroidManifest.xml index 9f185e80eb..980f571091 100644 --- a/apps/tlon-mobile/android/app/src/main/AndroidManifest.xml +++ b/apps/tlon-mobile/android/app/src/main/AndroidManifest.xml @@ -4,7 +4,6 @@ - diff --git a/apps/tlon-mobile/ios/Landscape.xcodeproj/project.pbxproj b/apps/tlon-mobile/ios/Landscape.xcodeproj/project.pbxproj index 67812cbc7b..a5e0dbc119 100644 --- a/apps/tlon-mobile/ios/Landscape.xcodeproj/project.pbxproj +++ b/apps/tlon-mobile/ios/Landscape.xcodeproj/project.pbxproj @@ -1174,27 +1174,30 @@ "${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon-Samples/ReactCommon_Samples.framework/Headers/platform/ios", "${PODS_CONFIGURATION_BUILD_DIR}/React-NativeModulesApple/React_NativeModulesApple.framework/Headers", "${PODS_CONFIGURATION_BUILD_DIR}/React-graphics/React_graphics.framework/Headers/react/renderer/graphics/platform/ios", - "${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon/ReactCommon.framework/Headers", - "${PODS_CONFIGURATION_BUILD_DIR}/React-Fabric/React_Fabric.framework/Headers/react/renderer/components/view/platform/cxx", - "${PODS_CONFIGURATION_BUILD_DIR}/React-graphics/React_graphics.framework/Headers", - "${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon/ReactCommon.framework/Headers", - "${PODS_CONFIGURATION_BUILD_DIR}/React-Fabric/React_Fabric.framework/Headers/react/renderer/components/view/platform/cxx", - "${PODS_CONFIGURATION_BUILD_DIR}/React-graphics/React_graphics.framework/Headers", - "${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon/ReactCommon.framework/Headers", - "${PODS_CONFIGURATION_BUILD_DIR}/React-Fabric/React_Fabric.framework/Headers/react/renderer/components/view/platform/cxx", - "${PODS_CONFIGURATION_BUILD_DIR}/React-graphics/React_graphics.framework/Headers", - "${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon/ReactCommon.framework/Headers", - "${PODS_CONFIGURATION_BUILD_DIR}/React-Fabric/React_Fabric.framework/Headers/react/renderer/components/view/platform/cxx", - "${PODS_CONFIGURATION_BUILD_DIR}/React-graphics/React_graphics.framework/Headers", - "${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon/ReactCommon.framework/Headers", - "${PODS_CONFIGURATION_BUILD_DIR}/React-Fabric/React_Fabric.framework/Headers/react/renderer/components/view/platform/cxx", - "${PODS_CONFIGURATION_BUILD_DIR}/React-graphics/React_graphics.framework/Headers", - "${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon/ReactCommon.framework/Headers", - "${PODS_CONFIGURATION_BUILD_DIR}/React-Fabric/React_Fabric.framework/Headers/react/renderer/components/view/platform/cxx", - "${PODS_CONFIGURATION_BUILD_DIR}/React-graphics/React_graphics.framework/Headers", - "${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon/ReactCommon.framework/Headers", - "${PODS_CONFIGURATION_BUILD_DIR}/React-Fabric/React_Fabric.framework/Headers/react/renderer/components/view/platform/cxx", - "${PODS_CONFIGURATION_BUILD_DIR}/React-graphics/React_graphics.framework/Headers", + " ${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon/ReactCommon.framework/Headers", + " ${PODS_CONFIGURATION_BUILD_DIR}/React-Fabric/React_Fabric.framework/Headers/react/renderer/components/view/platform/cxx", + " ${PODS_CONFIGURATION_BUILD_DIR}/React-graphics/React_graphics.framework/Headers", + " ${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon/ReactCommon.framework/Headers", + " ${PODS_CONFIGURATION_BUILD_DIR}/React-Fabric/React_Fabric.framework/Headers/react/renderer/components/view/platform/cxx", + " ${PODS_CONFIGURATION_BUILD_DIR}/React-graphics/React_graphics.framework/Headers", + " ${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon/ReactCommon.framework/Headers", + " ${PODS_CONFIGURATION_BUILD_DIR}/React-Fabric/React_Fabric.framework/Headers/react/renderer/components/view/platform/cxx", + " ${PODS_CONFIGURATION_BUILD_DIR}/React-graphics/React_graphics.framework/Headers", + " ${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon/ReactCommon.framework/Headers", + " ${PODS_CONFIGURATION_BUILD_DIR}/React-Fabric/React_Fabric.framework/Headers/react/renderer/components/view/platform/cxx", + " ${PODS_CONFIGURATION_BUILD_DIR}/React-graphics/React_graphics.framework/Headers", + " ${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon/ReactCommon.framework/Headers", + " ${PODS_CONFIGURATION_BUILD_DIR}/React-Fabric/React_Fabric.framework/Headers/react/renderer/components/view/platform/cxx", + " ${PODS_CONFIGURATION_BUILD_DIR}/React-graphics/React_graphics.framework/Headers", + " ${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon/ReactCommon.framework/Headers", + " ${PODS_CONFIGURATION_BUILD_DIR}/React-Fabric/React_Fabric.framework/Headers/react/renderer/components/view/platform/cxx", + " ${PODS_CONFIGURATION_BUILD_DIR}/React-graphics/React_graphics.framework/Headers", + " ${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon/ReactCommon.framework/Headers", + " ${PODS_CONFIGURATION_BUILD_DIR}/React-Fabric/React_Fabric.framework/Headers/react/renderer/components/view/platform/cxx", + " ${PODS_CONFIGURATION_BUILD_DIR}/React-graphics/React_graphics.framework/Headers", + " ${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon/ReactCommon.framework/Headers", + " ${PODS_CONFIGURATION_BUILD_DIR}/React-Fabric/React_Fabric.framework/Headers/react/renderer/components/view/platform/cxx", + " ${PODS_CONFIGURATION_BUILD_DIR}/React-graphics/React_graphics.framework/Headers", ); IPHONEOS_DEPLOYMENT_TARGET = 16.0; LD_RUNPATH_SEARCH_PATHS = ( @@ -1265,27 +1268,30 @@ "${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon-Samples/ReactCommon_Samples.framework/Headers/platform/ios", "${PODS_CONFIGURATION_BUILD_DIR}/React-NativeModulesApple/React_NativeModulesApple.framework/Headers", "${PODS_CONFIGURATION_BUILD_DIR}/React-graphics/React_graphics.framework/Headers/react/renderer/graphics/platform/ios", - "${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon/ReactCommon.framework/Headers", - "${PODS_CONFIGURATION_BUILD_DIR}/React-Fabric/React_Fabric.framework/Headers/react/renderer/components/view/platform/cxx", - "${PODS_CONFIGURATION_BUILD_DIR}/React-graphics/React_graphics.framework/Headers", - "${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon/ReactCommon.framework/Headers", - "${PODS_CONFIGURATION_BUILD_DIR}/React-Fabric/React_Fabric.framework/Headers/react/renderer/components/view/platform/cxx", - "${PODS_CONFIGURATION_BUILD_DIR}/React-graphics/React_graphics.framework/Headers", - "${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon/ReactCommon.framework/Headers", - "${PODS_CONFIGURATION_BUILD_DIR}/React-Fabric/React_Fabric.framework/Headers/react/renderer/components/view/platform/cxx", - "${PODS_CONFIGURATION_BUILD_DIR}/React-graphics/React_graphics.framework/Headers", - "${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon/ReactCommon.framework/Headers", - "${PODS_CONFIGURATION_BUILD_DIR}/React-Fabric/React_Fabric.framework/Headers/react/renderer/components/view/platform/cxx", - "${PODS_CONFIGURATION_BUILD_DIR}/React-graphics/React_graphics.framework/Headers", - "${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon/ReactCommon.framework/Headers", - "${PODS_CONFIGURATION_BUILD_DIR}/React-Fabric/React_Fabric.framework/Headers/react/renderer/components/view/platform/cxx", - "${PODS_CONFIGURATION_BUILD_DIR}/React-graphics/React_graphics.framework/Headers", - "${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon/ReactCommon.framework/Headers", - "${PODS_CONFIGURATION_BUILD_DIR}/React-Fabric/React_Fabric.framework/Headers/react/renderer/components/view/platform/cxx", - "${PODS_CONFIGURATION_BUILD_DIR}/React-graphics/React_graphics.framework/Headers", - "${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon/ReactCommon.framework/Headers", - "${PODS_CONFIGURATION_BUILD_DIR}/React-Fabric/React_Fabric.framework/Headers/react/renderer/components/view/platform/cxx", - "${PODS_CONFIGURATION_BUILD_DIR}/React-graphics/React_graphics.framework/Headers", + " ${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon/ReactCommon.framework/Headers", + " ${PODS_CONFIGURATION_BUILD_DIR}/React-Fabric/React_Fabric.framework/Headers/react/renderer/components/view/platform/cxx", + " ${PODS_CONFIGURATION_BUILD_DIR}/React-graphics/React_graphics.framework/Headers", + " ${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon/ReactCommon.framework/Headers", + " ${PODS_CONFIGURATION_BUILD_DIR}/React-Fabric/React_Fabric.framework/Headers/react/renderer/components/view/platform/cxx", + " ${PODS_CONFIGURATION_BUILD_DIR}/React-graphics/React_graphics.framework/Headers", + " ${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon/ReactCommon.framework/Headers", + " ${PODS_CONFIGURATION_BUILD_DIR}/React-Fabric/React_Fabric.framework/Headers/react/renderer/components/view/platform/cxx", + " ${PODS_CONFIGURATION_BUILD_DIR}/React-graphics/React_graphics.framework/Headers", + " ${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon/ReactCommon.framework/Headers", + " ${PODS_CONFIGURATION_BUILD_DIR}/React-Fabric/React_Fabric.framework/Headers/react/renderer/components/view/platform/cxx", + " ${PODS_CONFIGURATION_BUILD_DIR}/React-graphics/React_graphics.framework/Headers", + " ${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon/ReactCommon.framework/Headers", + " ${PODS_CONFIGURATION_BUILD_DIR}/React-Fabric/React_Fabric.framework/Headers/react/renderer/components/view/platform/cxx", + " ${PODS_CONFIGURATION_BUILD_DIR}/React-graphics/React_graphics.framework/Headers", + " ${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon/ReactCommon.framework/Headers", + " ${PODS_CONFIGURATION_BUILD_DIR}/React-Fabric/React_Fabric.framework/Headers/react/renderer/components/view/platform/cxx", + " ${PODS_CONFIGURATION_BUILD_DIR}/React-graphics/React_graphics.framework/Headers", + " ${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon/ReactCommon.framework/Headers", + " ${PODS_CONFIGURATION_BUILD_DIR}/React-Fabric/React_Fabric.framework/Headers/react/renderer/components/view/platform/cxx", + " ${PODS_CONFIGURATION_BUILD_DIR}/React-graphics/React_graphics.framework/Headers", + " ${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon/ReactCommon.framework/Headers", + " ${PODS_CONFIGURATION_BUILD_DIR}/React-Fabric/React_Fabric.framework/Headers/react/renderer/components/view/platform/cxx", + " ${PODS_CONFIGURATION_BUILD_DIR}/React-graphics/React_graphics.framework/Headers", ); IPHONEOS_DEPLOYMENT_TARGET = 16.0; LD_RUNPATH_SEARCH_PATHS = ( diff --git a/apps/tlon-mobile/package.json b/apps/tlon-mobile/package.json index 0083e4e235..3112f1ae71 100644 --- a/apps/tlon-mobile/package.json +++ b/apps/tlon-mobile/package.json @@ -72,6 +72,7 @@ "react-native-screens": "~3.29.0", "react-native-storage": "^1.0.1", "react-native-svg": "^14.1.0", + "react-native-url-polyfill": "^2.0.0", "react-native-webview": "13.6.4", "realm": "^12.6.0", "tailwind-rn": "^4.2.0" diff --git a/apps/tlon-mobile/src/App.tsx b/apps/tlon-mobile/src/App.tsx index 4ba3add014..49507f8823 100644 --- a/apps/tlon-mobile/src/App.tsx +++ b/apps/tlon-mobile/src/App.tsx @@ -42,6 +42,7 @@ import { TlonLoginScreen } from './screens/TlonLoginScreen'; import { WelcomeScreen } from './screens/WelcomeScreen'; import type { OnboardingStackParamList } from './types'; import { posthogAsync, trackError } from './utils/posthog'; +import { getPathFromWer } from './utils/string'; type Props = { wer?: string; @@ -58,7 +59,7 @@ const App = ({ wer: initialWer }: Props) => { const { wer, lure, priorityToken, clearDeepLink } = useDeepLink(); const navigation = useNavigation(); const screenOptions = useScreenOptions(); - const gotoPath = wer ?? initialWer; + const gotoPath = initialWer ? getPathFromWer(initialWer) : wer; useEffect(() => { if (isAuthenticated) { diff --git a/apps/tlon-mobile/src/components/SingletonWebview.tsx b/apps/tlon-mobile/src/components/SingletonWebview.tsx index 35f4588a9d..a4949ea038 100644 --- a/apps/tlon-mobile/src/components/SingletonWebview.tsx +++ b/apps/tlon-mobile/src/components/SingletonWebview.tsx @@ -10,6 +10,7 @@ import { addNotificationResponseReceivedListener } from 'expo-notifications'; import { useCallback, useEffect, useRef, useState } from 'react'; import { Alert, Linking, useColorScheme } from 'react-native'; import { useSafeAreaInsets } from 'react-native-safe-area-context'; +import { URL } from 'react-native-url-polyfill'; import { WebView } from 'react-native-webview'; import { useTailwind } from 'tailwind-rn'; @@ -40,7 +41,7 @@ const createUri = (shipUrl: string, path?: string) => export const SingletonWebview = () => { const tailwind = useTailwind(); - const { shipUrl = '', ship, clearShip } = useShip(); + const { shipUrl = '', ship, clearShip, setShip } = useShip(); const webViewProps = useWebView(); const colorScheme = useColorScheme(); const safeAreaInsets = useSafeAreaInsets(); @@ -221,6 +222,24 @@ export const SingletonWebview = () => { setTimeout(() => setAppLoaded(true), 10_000); }} onShouldStartLoadWithRequest={({ url }) => { + const parsedUrl = new URL(url); + const parsedShipUrl = new URL(shipUrl); + const redirectedToHttps = + parsedUrl.protocol === 'https:' && + parsedShipUrl.protocol === 'http:' && + parsedUrl.host === parsedShipUrl.host && + parsedUrl.pathname.startsWith('/apps/groups'); + + // Allow redirect to HTTPS + if (redirectedToHttps) { + setShip({ + ship, + shipUrl: parsedUrl.origin, + }); + + return true; + } + // Clear ship info if webview is redirecting to login page if (url.includes(`${shipUrl}/~/login`)) { clearShip(); diff --git a/apps/tlon-mobile/src/contexts/ship.tsx b/apps/tlon-mobile/src/contexts/ship.tsx index 136554749b..817c1e96fc 100644 --- a/apps/tlon-mobile/src/contexts/ship.tsx +++ b/apps/tlon-mobile/src/contexts/ship.tsx @@ -6,6 +6,7 @@ import { NativeModules } from 'react-native'; import { configureApi } from '../lib/api'; import storage from '../lib/storage'; +import { transformShipURL } from '../utils/string'; const { UrbitModule } = NativeModules; @@ -57,36 +58,39 @@ export const ShipProvider = ({ children }: { children: ReactNode }) => { return; } + // The passed shipUrl should already be normalized, but defensively ensure it is + const normalizedShipUrl = transformShipURL(shipUrl); + // Save to React Native stoage - storage.save({ key: 'store', data: { ship, shipUrl } }); + storage.save({ key: 'store', data: { ship, shipUrl: normalizedShipUrl } }); // Save context state - setShipInfo({ ship, shipUrl }); + setShipInfo({ ship, shipUrl: normalizedShipUrl }); // Configure API - configureApi(ship, shipUrl); + configureApi(ship, normalizedShipUrl); // Configure analytics crashlytics().setAttribute( 'isHosted', - shipUrl.includes('.tlon.network') ? 'true' : 'false' + normalizedShipUrl.includes('.tlon.network') ? 'true' : 'false' ); // If cookie was passed in, use it, otherwise fetch from ship if (authCookie) { // Save to native storage - UrbitModule.setUrbit(ship, shipUrl, authCookie); + UrbitModule.setUrbit(ship, normalizedShipUrl, authCookie); } else { // Run this in the background (async () => { // Fetch the root ship URL and parse headers - const response = await fetch(shipUrl, { + const response = await fetch(normalizedShipUrl, { credentials: 'include', }); const fetchedAuthCookie = response.headers.get('set-cookie'); if (fetchedAuthCookie) { // Save to native storage - UrbitModule.setUrbit(ship, shipUrl, fetchedAuthCookie); + UrbitModule.setUrbit(ship, normalizedShipUrl, fetchedAuthCookie); } })(); } diff --git a/apps/tlon-mobile/src/screens/TlonLoginScreen.tsx b/apps/tlon-mobile/src/screens/TlonLoginScreen.tsx index 1498d2da5c..22d6d467aa 100644 --- a/apps/tlon-mobile/src/screens/TlonLoginScreen.tsx +++ b/apps/tlon-mobile/src/screens/TlonLoginScreen.tsx @@ -99,8 +99,14 @@ export const TlonLoginScreen = ({ navigation }: Props) => { user, }); } - } catch (err) { - setRemoteError((err as Error).message); + } catch (err: any) { + if ('name' in err && err.name === 'AbortError') { + setRemoteError( + 'Sorry, we could not connect to the server. Please try again later.' + ); + } else { + setRemoteError((err as Error).message); + } } setIsSubmitting(false); diff --git a/apps/tlon-mobile/src/utils/string.test.ts b/apps/tlon-mobile/src/utils/string.test.ts index 6df699050f..9c16862553 100644 --- a/apps/tlon-mobile/src/utils/string.test.ts +++ b/apps/tlon-mobile/src/utils/string.test.ts @@ -3,58 +3,59 @@ import { expect, test } from 'vitest'; import { formatPhoneNumber, transformShipURL } from './string'; test('formats phone number without country code', () => { - expect(formatPhoneNumber('5555555555'), '(555) 555-5555'); - expect(formatPhoneNumber('555-555-5555'), '(555) 555-5555'); + expect(formatPhoneNumber('5555555555')).toBe('(555) 555-5555'); + expect(formatPhoneNumber('555-555-5555')).toBe('(555) 555-5555'); }); test('formats phone number with country code', () => { - expect(formatPhoneNumber('15555555555'), '+1 (555) 555-5555'); - expect(formatPhoneNumber('+15555555555'), '+1 (555) 555-5555'); + expect(formatPhoneNumber('15555555555')).toBe('+1 (555) 555-5555'); + expect(formatPhoneNumber('+15555555555')).toBe('+1 (555) 555-5555'); }); test('skips when given unknown format', () => { - expect(formatPhoneNumber('1234'), '1234'); + expect(formatPhoneNumber('1234')).toBe('1234'); }); test('transforms ship url without a protocol', () => { - expect( - transformShipURL('sampel-palnet.tlon.network'), + expect(transformShipURL('sampel-palnet.tlon.network')).toBe( 'https://sampel-palnet.tlon.network' ); }); test('transforms ship url with a path', () => { - expect( - transformShipURL('sampel-palnet.tlon.network/apps/groups'), + expect(transformShipURL('sampel-palnet.tlon.network/apps/groups')).toBe( 'https://sampel-palnet.tlon.network' ); }); test('transforms ship url with a protocol and extra whitespace', () => { - expect( - transformShipURL('https://sampel-palnet.tlon.network '), + expect(transformShipURL('https://sampel-palnet.tlon.network ')).toBe( + 'https://sampel-palnet.tlon.network' + ); +}); + +test('transforms ship url with trailing slash', () => { + expect(transformShipURL('https://sampel-palnet.tlon.network/')).toBe( 'https://sampel-palnet.tlon.network' ); }); test('if a ship url is already valid, it is returned as is', () => { - expect( - transformShipURL('https://sampel-palnet.tlon.network'), + expect(transformShipURL('https://sampel-palnet.tlon.network')).toBe( 'https://sampel-palnet.tlon.network' ); }); test('if a ship url is an ip address with a port, it is returned as is', () => { - expect( - transformShipURL('http://192.168.0.1:8443'), + expect(transformShipURL('http://192.168.0.1:8443')).toBe( 'http://192.168.0.1:8443' ); }); test('if a ship url is an ip address without a port, it is returned as is', () => { - expect(transformShipURL('http://192.168.0.1'), 'http://192.168.0.1'); + expect(transformShipURL('http://192.168.0.1')).toBe('http://192.168.0.1'); }); test('if a ship url is http://localhost, it is returned as is', () => { - expect(transformShipURL('http://localhost'), 'http://localhost'); + expect(transformShipURL('http://localhost')).toBe('http://localhost'); }); diff --git a/apps/tlon-mobile/src/utils/string.ts b/apps/tlon-mobile/src/utils/string.ts index 6a0ce8b513..8ac2292f4f 100644 --- a/apps/tlon-mobile/src/utils/string.ts +++ b/apps/tlon-mobile/src/utils/string.ts @@ -47,7 +47,7 @@ export const formatPhoneNumber = (phoneNumber: string) => { export const transformShipURL = (shipUrl: string) => { // this definition is duplicated here because importing it from src/constants will cause vitest to // attempt to import react-native libraries, which fill fail - const SHIP_URL_REGEX = /^https?:\/\/([\w-]+\.)+[\w-]+(:\d+)?(?=\/?$)/; + const SHIP_URL_REGEX = /^https?:\/\/([\w-]+\.)+[\w-]+(:\d+)?$/; const shipUrlMatch = shipUrl.match(SHIP_URL_REGEX); let shipUrlToUse = shipUrl; @@ -70,8 +70,51 @@ export const transformShipURL = (shipUrl: string) => { '$1' ); + // Remove trailing slashes + transformed = transformed.replace(/\/+$/, ''); + shipUrlToUse = transformed; } return shipUrlToUse; }; + +/** + * This function is used to determine the path to navigate to + * when the app is opened from a notification. + * @param wer The path from the backend + * @returns path to navigate to + * This is necessary because the backend sends a path to the app + * that is not always the correct path to navigate to (e.g. when + * there's a post in an "all notifications" channel, the path + * will be to a thread starting with the post, but we want to + * navigate to the post itself in the main chat). + * This same logic exists in the web app in Notification.tsx + */ +export const getPathFromWer = (wer: string): string => { + const pathParts = wer.split('/'); + // if not going to a specific post, return the path + if (pathParts.length < 10) { + return wer; + } + + const path = wer; + const parts = path.split('/'); + const isChatMsg = parts.includes('chat'); + const index = 8; + const post = parts[index + 1]; + const reply = parts[index + 2]; + + // all replies should go to the post and scroll to the reply + if (reply) { + return `${parts.slice(0, index + 2).join('/')}?reply=${reply}`; + } + + // chat messages should go to the channel and scroll to the message + if (isChatMsg) { + return `${parts.slice(0, index).join('/')}?msg=${post}`; + } + + // all other posts should go to the post + return path; +}; diff --git a/package-lock.json b/package-lock.json index f38610b1a3..6b3a2e10be 100644 --- a/package-lock.json +++ b/package-lock.json @@ -68,6 +68,7 @@ "react-native-screens": "~3.29.0", "react-native-storage": "^1.0.1", "react-native-svg": "^14.1.0", + "react-native-url-polyfill": "^2.0.0", "react-native-webview": "13.6.4", "realm": "^12.6.0", "tailwind-rn": "^4.2.0" @@ -769,7 +770,7 @@ } }, "apps/tlon-web": { - "version": "5.5.0", + "version": "5.7.0", "dependencies": { "@aws-sdk/client-s3": "^3.190.0", "@aws-sdk/s3-request-presigner": "^3.190.0", @@ -32665,6 +32666,17 @@ "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz", "integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==" }, + "node_modules/react-native-url-polyfill": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/react-native-url-polyfill/-/react-native-url-polyfill-2.0.0.tgz", + "integrity": "sha512-My330Do7/DvKnEvwQc0WdcBnFPploYKp9CYlefDXzIdEaA+PAhDYllkvGeEroEzvc4Kzzj2O4yVdz8v6fjRvhA==", + "dependencies": { + "whatwg-url-without-unicode": "8.0.0-3" + }, + "peerDependencies": { + "react-native": "*" + } + }, "node_modules/react-native-web": { "version": "0.19.10", "resolved": "https://registry.npmjs.org/react-native-web/-/react-native-web-0.19.10.tgz",