Skip to content

Commit

Permalink
Merge pull request #3311 from tloncorp/lb/merge-mobile-upstream
Browse files Browse the repository at this point in the history
mobile: pull in subsequent work from the old repo
  • Loading branch information
latter-bolden authored Mar 6, 2024
2 parents 37b7578 + b6f2849 commit acd02b9
Show file tree
Hide file tree
Showing 10 changed files with 165 additions and 73 deletions.
1 change: 0 additions & 1 deletion apps/tlon-mobile/android/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
<uses-permission android:name="android.permission.VIBRATE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="com.google.android.gms.permission.AD_ID"/>
<queries>
<intent>
<action android:name="android.intent.action.VIEW"/>
Expand Down
90 changes: 48 additions & 42 deletions apps/tlon-mobile/ios/Landscape.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -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 = (
Expand Down Expand Up @@ -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 = (
Expand Down
1 change: 1 addition & 0 deletions apps/tlon-mobile/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
3 changes: 2 additions & 1 deletion apps/tlon-mobile/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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) {
Expand Down
21 changes: 20 additions & 1 deletion apps/tlon-mobile/src/components/SingletonWebview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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();
Expand Down
18 changes: 11 additions & 7 deletions apps/tlon-mobile/src/contexts/ship.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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);
}
})();
}
Expand Down
10 changes: 8 additions & 2 deletions apps/tlon-mobile/src/screens/TlonLoginScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
35 changes: 18 additions & 17 deletions apps/tlon-mobile/src/utils/string.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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');
});
Loading

0 comments on commit acd02b9

Please sign in to comment.