Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[menu-bar] Add support for auth deeplink #100

Merged
merged 2 commits into from
Nov 3, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
- Added support for installing apps directly from Finder. ([#56](https://github.com/expo/orbit/pull/56) by [@gabrieldonadel](https://github.com/gabrieldonadel))
- Added local HTTP server to circumvent deep-link limitations. ([#52](https://github.com/expo/orbit/pull/52), [#53](https://github.com/expo/orbit/pull/53), [#54](https://github.com/expo/orbit/pull/54), [#55](https://github.com/expo/orbit/pull/55) by [@gabrieldonadel](https://github.com/gabrieldonadel))
- Added Projects section to the menu bar. ([#46](https://github.com/expo/orbit/pull/46), [#59](https://github.com/expo/orbit/pull/59), [#83](https://github.com/expo/orbit/pull/83), [#95](https://github.com/expo/orbit/pull/95) by [@gabrieldonadel](https://github.com/gabrieldonadel))
- Added support for login to Expo. ([#41](https://github.com/expo/orbit/pull/41), [#43](https://github.com/expo/orbit/pull/43), [#44](https://github.com/expo/orbit/pull/44), [#45](https://github.com/expo/orbit/pull/45), [#62](https://github.com/expo/orbit/pull/62), [#67](https://github.com/expo/orbit/pull/67), [#89](https://github.com/expo/orbit/pull/89) by [@gabrieldonadel](https://github.com/gabrieldonadel))
- Added support for login to Expo. ([#41](https://github.com/expo/orbit/pull/41), [#43](https://github.com/expo/orbit/pull/43), [#44](https://github.com/expo/orbit/pull/44), [#45](https://github.com/expo/orbit/pull/45), [#62](https://github.com/expo/orbit/pull/62), [#67](https://github.com/expo/orbit/pull/67), [#89](https://github.com/expo/orbit/pull/89), [#100](https://github.com/expo/orbit/pull/100) by [@gabrieldonadel](https://github.com/gabrieldonadel))
- Focus simulator/emulator window when launching an app. ([#75](https://github.com/expo/orbit/pull/75) by [@gabrieldonadel](https://github.com/gabrieldonadel))
- Add support for running iOS internal distribution apps on real devices. ([#79](https://github.com/expo/orbit/pull/79) by [@gabrieldonadel](https://github.com/gabrieldonadel))
- Add support for opening snack projects on real iOS devices. ([#92](https://github.com/expo/orbit/pull/92) by [@gabrieldonadel](https://github.com/gabrieldonadel))
Expand Down
12 changes: 12 additions & 0 deletions apps/menu-bar/src/modules/Storage.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import { MMKV } from 'react-native-mmkv';

import { apolloClient } from '../api/ApolloClient';

export const userPreferencesStorageKey = 'user-preferences';
export const sessionSecretStorageKey = 'sessionSecret';

export type UserPreferences = {
launchOnLogin: boolean;
Expand Down Expand Up @@ -57,3 +60,12 @@ export const resetStorage = () => {
};

export const storage = new MMKV();

export function saveSessionSecret(sessionSecret: string) {
storage.set(sessionSecretStorageKey, sessionSecret);
}

export function resetApolloStore() {
apolloClient.resetStore();
storage.delete('apollo-cache-persist');
}
13 changes: 8 additions & 5 deletions apps/menu-bar/src/popover/Core.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ import {
} from '../modules/Storage';
import { getDeviceId, getDeviceOS, isVirtualDevice } from '../utils/device';
import { MenuBarStatus } from '../utils/helpers';
import { getPlatformFromURI } from '../utils/parseUrl';
import { getPlatformFromURI, handleAuthUrl } from '../utils/parseUrl';

type Props = {
isDevWindow: boolean;
Expand Down Expand Up @@ -248,12 +248,15 @@ function Core(props: Props) {
if (!props.isDevWindow) {
const urlWithoutProtocol = url.substring(url.indexOf('://') + 3);
const isSnackUrl = url.includes('exp.host/');
const isAuthUrl = urlWithoutProtocol.startsWith('auth?');

if (isSnackUrl) {
return handleSnackUrl(`exp://${urlWithoutProtocol}`);
if (isAuthUrl) {
handleAuthUrl(url);
} else if (isSnackUrl) {
handleSnackUrl(`exp://${urlWithoutProtocol}`);
} else {
installAppFromURI(`https://${urlWithoutProtocol}`);
}

installAppFromURI(`https://${urlWithoutProtocol}`);
}
},
[props.isDevWindow, installAppFromURI, handleSnackUrl]
Expand Down
13 changes: 13 additions & 0 deletions apps/menu-bar/src/utils/parseUrl.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,16 @@
import { saveSessionSecret } from '../modules/Storage';

export const getPlatformFromURI = (url: string): 'android' | 'ios' => {
return url.endsWith('.apk') ? 'android' : 'ios';
};

export function handleAuthUrl(url: string) {
const resultURL = new URL(url);
const sessionSecret = resultURL.searchParams.get('session_secret');

if (!sessionSecret) {
throw new Error('session_secret is missing in auth redirect query');
}

saveSessionSecret(sessionSecret);
}
9 changes: 2 additions & 7 deletions apps/menu-bar/src/windows/DebugMenu.tsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,17 @@
import { StyleSheet, TouchableOpacity } from 'react-native';

import { apolloClient } from '../api/ApolloClient';
import { View, Text } from '../components';
import NativeColorPalette from '../components/NativeColorPalette';
import MenuBarModule from '../modules/MenuBarModule';
import { resetStorage, storage } from '../modules/Storage';
import { resetApolloStore, resetStorage } from '../modules/Storage';

const DebugMenu = () => {
return (
<View flex="1" px="medium" pb="medium" padding="2" gap="1.5">
<TouchableOpacity onPress={resetStorage}>
<Text color="warning">Reset storage</Text>
</TouchableOpacity>
<TouchableOpacity
onPress={() => {
apolloClient.resetStore();
storage.delete('apollo-cache-persist');
}}>
<TouchableOpacity onPress={resetApolloStore}>
<Text color="warning">Clear Apollo Store</Text>
</TouchableOpacity>
<NativeColorPalette />
Expand Down
27 changes: 20 additions & 7 deletions apps/menu-bar/src/windows/Settings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,11 @@ import SparkleModule from '../modules/SparkleModule';
import {
UserPreferences,
getUserPreferences,
saveSessionSecret,
saveUserPreferences,
storage,
sessionSecretStorageKey,
resetApolloStore,
} from '../modules/Storage';
import WebAuthenticationSessionModule, {
WebBrowserResultType,
Expand All @@ -35,9 +38,19 @@ const osList: { label: string; key: keyof UserPreferences }[] = [
const Settings = () => {
const theme = useCurrentTheme();
const [hasSessionSecret, setHasSessionSecret] = useState(
Boolean(storage.getString('sessionSecret'))
Boolean(storage.getString(sessionSecretStorageKey))
);

useEffect(() => {
const listener = storage.addOnValueChangedListener((key) => {
if (key === sessionSecretStorageKey) {
setHasSessionSecret(Boolean(storage.getString(sessionSecretStorageKey)));
}
});

return listener.remove;
}, []);

const [userPreferences, setUserPreferences] = useState<UserPreferences>(getUserPreferences());
const [customSdkPathEnabled, setCustomSdkPathEnabled] = useState(
Boolean(getUserPreferences().customSdkPath)
Expand All @@ -48,6 +61,7 @@ const Settings = () => {
fetchPolicy: 'cache-and-network',
skip: !hasSessionSecret,
});

const currentUser = data?.meUserActor;

useEffect(() => {
Expand Down Expand Up @@ -119,14 +133,13 @@ const Settings = () => {
throw new Error('session_secret is missing in auth redirect query');
}

storage.set('sessionSecret', sessionSecret);
setHasSessionSecret(true);
saveSessionSecret(sessionSecret);
}
};

const handleLogout = async () => {
storage.delete('sessionSecret');
setHasSessionSecret(false);
const handleLogout = () => {
storage.delete(sessionSecretStorageKey);
resetApolloStore();
};

const toggleOS = async (key: keyof UserPreferences, value: boolean) => {
Expand Down Expand Up @@ -162,7 +175,7 @@ const Settings = () => {
</Row>
) : null}

<Button title="Logout" onPress={handleLogout} style={styles.button} />
<Button title="Log Out" onPress={handleLogout} style={styles.button} />
{__DEV__ ? (
<TouchableOpacity
onPress={() => WindowsNavigator.open('DebugMenu')}
Expand Down