Skip to content

Commit

Permalink
[menu-bar] Migrate WebAuthenticationSession module to expo-modules an…
Browse files Browse the repository at this point in the history
…d add electron support (#177)

* [menu-bar][electron] Add support for WebAuthenticationSession module

* [menu-bar] Migrate WebAuthenticationSession module to expo-modules

* Fix deeplinks on windows

* Remove objective-c code

* Add changelog entry

* Apply suggestions from code review

Co-authored-by: Alan Hughes <30924086+alanjhughes@users.noreply.github.com>

* No longer extend ObservableObject

---------

Co-authored-by: Alan Hughes <30924086+alanjhughes@users.noreply.github.com>
  • Loading branch information
gabrieldonadel and alanjhughes authored Feb 16, 2024
1 parent d0a2d20 commit 56aae30
Show file tree
Hide file tree
Showing 17 changed files with 168 additions and 80 deletions.
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
### 🎉 New features

- Add support for launching Expo updates. ([#134](https://github.com/expo/orbit/pull/134), [#137](https://github.com/expo/orbit/pull/137), [#138](https://github.com/expo/orbit/pull/138), [#144](https://github.com/expo/orbit/pull/144), [#148](https://github.com/expo/orbit/pull/148) by [@gabrieldonadel](https://github.com/gabrieldonadel))
- Add experimental support for Windows and Linux. ([#152](https://github.com/expo/orbit/pull/152), [#157](https://github.com/expo/orbit/pull/157), [#158](https://github.com/expo/orbit/pull/158), [#160](https://github.com/expo/orbit/pull/160), [#161](https://github.com/expo/orbit/pull/161), [#165](https://github.com/expo/orbit/pull/165), [#170](https://github.com/expo/orbit/pull/170), [#171](https://github.com/expo/orbit/pull/171), [#172](https://github.com/expo/orbit/pull/172), [#173](https://github.com/expo/orbit/pull/173), [#174](https://github.com/expo/orbit/pull/174), [#175](https://github.com/expo/orbit/pull/175) by [@gabrieldonadel](https://github.com/gabrieldonadel))
- Add experimental support for Windows and Linux. ([#152](https://github.com/expo/orbit/pull/152), [#157](https://github.com/expo/orbit/pull/157), [#158](https://github.com/expo/orbit/pull/158), [#160](https://github.com/expo/orbit/pull/160), [#161](https://github.com/expo/orbit/pull/161), [#165](https://github.com/expo/orbit/pull/165), [#170](https://github.com/expo/orbit/pull/170), [#171](https://github.com/expo/orbit/pull/171), [#172](https://github.com/expo/orbit/pull/172), [#173](https://github.com/expo/orbit/pull/173), [#174](https://github.com/expo/orbit/pull/174), [#175](https://github.com/expo/orbit/pull/175), [#177](https://github.com/expo/orbit/pull/177) by [@gabrieldonadel](https://github.com/gabrieldonadel))

### 🐛 Bug fixes

Expand Down
2 changes: 2 additions & 0 deletions apps/menu-bar/electron/modules/mainRegistry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import WindowManager from './WindowManager/main';
import FilePickerModule from '../../modules/file-picker/electron/main';
import MenuBarModule from '../../modules/menu-bar/electron/main';
import RudderModule from '../../modules/rudder/electron/main';
import WebAuthenticationSession from '../../modules/web-authentication-session/electron/main';

export const MainModules: Registry = [
MenuBarModule,
Expand All @@ -16,4 +17,5 @@ export const MainModules: Registry = [
RudderModule,
FilePickerModule,
AlertModule,
WebAuthenticationSession,
];

This file was deleted.

53 changes: 0 additions & 53 deletions apps/menu-bar/macos/ExpoMenuBar-macOS/WebAuthenticationSession.m

This file was deleted.

6 changes: 0 additions & 6 deletions apps/menu-bar/macos/ExpoMenuBar.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
77818F2A6E488E8B68D317B2 /* ExpoModulesProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = B02E22FAC6ED05060D31BB51 /* ExpoModulesProvider.swift */; };
C032EDB72A1FE6F300FBC597 /* orbit-cli-arm64 in Resources */ = {isa = PBXBuildFile; fileRef = C032EDB62A1FE6F300FBC597 /* orbit-cli-arm64 */; };
C051E6B62A83577800C6D615 /* orbit-cli-x64 in Resources */ = {isa = PBXBuildFile; fileRef = C051E6B52A83577800C6D615 /* orbit-cli-x64 */; };
C051E6BB2A991C4200C6D615 /* WebAuthenticationSession.m in Sources */ = {isa = PBXBuildFile; fileRef = C051E6BA2A991C4200C6D615 /* WebAuthenticationSession.m */; };
C051EC0E2AA227C600C6D615 /* SwifterWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = C051EC0D2AA227C600C6D615 /* SwifterWrapper.swift */; };
C0523ECC2A550983003371AF /* WindowManager.m in Sources */ = {isa = PBXBuildFile; fileRef = C0523ECB2A550983003371AF /* WindowManager.m */; };
C0523ED02A55980D003371AF /* WindowWithDeallocCallback.m in Sources */ = {isa = PBXBuildFile; fileRef = C0523ECF2A55980D003371AF /* WindowWithDeallocCallback.m */; };
Expand Down Expand Up @@ -71,8 +70,6 @@
C051E6B32A83408800C6D615 /* ExpoMenuBar-macOSRelease.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = "ExpoMenuBar-macOSRelease.entitlements"; sourceTree = "<group>"; };
C051E6B42A8340AF00C6D615 /* AutoLauncherRelease.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = AutoLauncherRelease.entitlements; sourceTree = "<group>"; };
C051E6B52A83577800C6D615 /* orbit-cli-x64 */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.executable"; name = "orbit-cli-x64"; path = "../../cli/orbit-cli-x64"; sourceTree = "<group>"; };
C051E6B92A991C0A00C6D615 /* WebAuthenticationSession.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = WebAuthenticationSession.h; sourceTree = "<group>"; };
C051E6BA2A991C4200C6D615 /* WebAuthenticationSession.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = WebAuthenticationSession.m; sourceTree = "<group>"; };
C051EC0C2AA227C600C6D615 /* ExpoMenuBar-macOS-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "ExpoMenuBar-macOS-Bridging-Header.h"; sourceTree = "<group>"; };
C051EC0D2AA227C600C6D615 /* SwifterWrapper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwifterWrapper.swift; sourceTree = "<group>"; };
C0523ECB2A550983003371AF /* WindowManager.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = WindowManager.m; sourceTree = "<group>"; };
Expand Down Expand Up @@ -199,8 +196,6 @@
C0523ECF2A55980D003371AF /* WindowWithDeallocCallback.m */,
C08E65332A5D04910079E3A9 /* WindowNavigator.m */,
C08E65352A5D0A580079E3A9 /* WindowNavigator.h */,
C051E6B92A991C0A00C6D615 /* WebAuthenticationSession.h */,
C051E6BA2A991C4200C6D615 /* WebAuthenticationSession.m */,
C051EC0D2AA227C600C6D615 /* SwifterWrapper.swift */,
C051EC0C2AA227C600C6D615 /* ExpoMenuBar-macOS-Bridging-Header.h */,
C06B8F892AAA9024009F2BB5 /* FileHandler.m */,
Expand Down Expand Up @@ -506,7 +501,6 @@
C0C820022AB8E5EE003D75AF /* SparkleModule.m in Sources */,
C06B8F8D2AAA9058009F2BB5 /* FileHandlerManager.m in Sources */,
C051EC0E2AA227C600C6D615 /* SwifterWrapper.swift in Sources */,
C051E6BB2A991C4200C6D615 /* WebAuthenticationSession.m in Sources */,
C0523ECC2A550983003371AF /* WindowManager.m in Sources */,
514201582437B4B40078DB4F /* main.m in Sources */,
5142014D2437B4B30078DB4F /* AppDelegate.m in Sources */,
Expand Down
6 changes: 6 additions & 0 deletions apps/menu-bar/macos/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1093,6 +1093,8 @@ PODS:
- SocketRocket (0.7.0)
- Sparkle (2.5.0)
- Swifter (1.5.0)
- WebAuthenticationSession (1.0.0):
- ExpoModulesCore
- Yoga (1.14.0)

DEPENDENCIES:
Expand Down Expand Up @@ -1161,6 +1163,7 @@ DEPENDENCIES:
- RNSVG (from `../../../node_modules/react-native-svg`)
- Sparkle (~> 2.5.0)
- Swifter (~> 1.5.0)
- WebAuthenticationSession (from `../modules/web-authentication-session/ios`)
- Yoga (from `../../../node_modules/react-native-macos/ReactCommon/yoga`)

SPEC REPOS:
Expand Down Expand Up @@ -1296,6 +1299,8 @@ EXTERNAL SOURCES:
:path: "../modules/rudder/ios"
RNSVG:
:path: "../../../node_modules/react-native-svg"
WebAuthenticationSession:
:path: "../modules/web-authentication-session/ios"
Yoga:
:path: "../../../node_modules/react-native-macos/ReactCommon/yoga"

Expand Down Expand Up @@ -1368,6 +1373,7 @@ SPEC CHECKSUMS:
SocketRocket: abac6f5de4d4d62d24e11868d7a2f427e0ef940d
Sparkle: 913cef92f73e08d0ae47a36ee20e523913e5f9e2
Swifter: e71dd674404923d7f03ebb03f3f222d1c570bc8e
WebAuthenticationSession: e8a569e2aa0032a4d2491ad7a8ca7104ac270233
Yoga: 46a35a1f0734aa6295ec21cf8ef599ab65cbbf07

PODFILE CHECKSUM: b99b8d2f5c2bf6a9433cff98783c664f2c1bbfb5
Expand Down
52 changes: 52 additions & 0 deletions apps/menu-bar/modules/web-authentication-session/electron/main.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { BrowserWindow } from 'electron';

import {
WebBrowserResult,
WebBrowserResultType,
WebAuthenticationSessionModuleType,
} from '../src/WebAuthenticationSession.types';

async function openAuthSessionAsync(urlString: string): Promise<WebBrowserResult> {
const url = new URL(urlString);
const window = new BrowserWindow({
width: 860,
height: 740,
});
window.menuBarVisible = false;
window.loadURL(urlString);

return new Promise((resolve, reject) => {
function handleRedirect(
event: Electron.Event<
Electron.WebContentsWillRedirectEventParams | Electron.WebContentsWillNavigateEventParams
>
) {
if (event.isSameDocument || url.origin === new URL(event.url).origin) {
return;
}

event.preventDefault();
window.close();

resolve({ type: WebBrowserResultType.SUCCESS, url: event.url });
}

window.webContents.on('will-redirect', handleRedirect);
window.webContents.on('will-navigate', handleRedirect);

window.on('closed', () => {
resolve({ type: WebBrowserResultType.CANCEL });
});

window.webContents.on('render-process-gone', (event, details) => {
reject(new Error(details.reason));
});
});
}

const WebAuthenticationSession: WebAuthenticationSessionModuleType & { name: string } = {
name: 'WebAuthenticationSession',
openAuthSessionAsync,
};

export default WebAuthenticationSession;
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"platforms": ["apple", "web"],
"ios": {
"modules": ["WebAuthenticationSessionModule"]
}
}
8 changes: 8 additions & 0 deletions apps/menu-bar/modules/web-authentication-session/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { WebBrowserResultType } from './src/WebAuthenticationSession.types';
import WebAuthenticationSessionModule from './src/WebAuthenticationSessionModule';

export async function openAuthSessionAsync(url: string) {
return await WebAuthenticationSessionModule.openAuthSessionAsync(url);
}

export { WebBrowserResultType };
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import AuthenticationServices

public class WebAuthContextProvider: NSObject, ASWebAuthenticationPresentationContextProviding {
public func presentationAnchor(for session: ASWebAuthenticationSession) -> ASPresentationAnchor {
var anchor: ASPresentationAnchor?

DispatchQueue.main.sync {
anchor = NSApp.mainWindow
}

return anchor ?? ASPresentationAnchor()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
Pod::Spec.new do |s|
s.name = 'WebAuthenticationSession'
s.version = '1.0.0'
s.summary = 'A sample project summary'
s.description = 'A sample project description'
s.author = ''
s.homepage = 'https://docs.expo.dev/modules/'
s.platform = :osx, '10.15'
s.source = { git: '' }
s.static_framework = true

s.dependency 'ExpoModulesCore'

# Swift/Objective-C compatibility
s.pod_target_xcconfig = {
'DEFINES_MODULE' => 'YES',
'SWIFT_COMPILATION_MODE' => 'wholemodule'
}

s.source_files = "**/*.{h,m,mm,swift,hpp,cpp}"
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import ExpoModulesCore
import AuthenticationServices

public class WebAuthenticationSessionModule: Module {
var authContextProvider = WebAuthContextProvider()
var authSession: ASWebAuthenticationSession?

public func definition() -> ModuleDefinition {
Name("WebAuthenticationSession")

AsyncFunction("openAuthSessionAsync") { (urlString: String, promise: Promise) in
guard let url = URL(string: urlString) else {
promise.reject("INVALID_URL", "Invalid URL provided")
return
}

authSession = ASWebAuthenticationSession(url: url, callbackURLScheme: "expo-orbit") { callbackURL, error in
if let error {
promise.reject("AUTH_SESSION_ERROR", error.localizedDescription)
} else {
if let callbackURL {
promise.resolve(["type": "success", "url": callbackURL.absoluteString])
} else {
promise.resolve(["type": "cancel"])
}
}
}

if #available(macOS 10.15, *) {
authSession?.presentationContextProvider = authContextProvider
}
authSession?.start()
}
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import { NativeModules } from 'react-native';

export enum WebBrowserResultType {
CANCEL = 'cancel',
SUCCESS = 'success',
Expand All @@ -14,11 +12,6 @@ export type WebBrowserResult =
url: string;
};

type WebAuthenticationSessionModuleType = {
export type WebAuthenticationSessionModuleType = {
openAuthSessionAsync: (url: string) => Promise<WebBrowserResult>;
};

const WebAuthenticationSessionModule: WebAuthenticationSessionModuleType =
NativeModules.WebAuthenticationSession;

export default WebAuthenticationSessionModule;
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { requireNativeModule } from 'expo-modules-core';

import { WebAuthenticationSessionModuleType } from './WebAuthenticationSession.types';

export default requireNativeModule<WebAuthenticationSessionModuleType>('WebAuthenticationSession');
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { requireElectronModule } from 'react-native-electron-modules/build/requireElectronModule';

import { WebAuthenticationSessionModuleType } from './WebAuthenticationSession.types';

export default requireElectronModule<WebAuthenticationSessionModuleType>(
'WebAuthenticationSession'
);
10 changes: 6 additions & 4 deletions apps/menu-bar/src/utils/parseUrl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,23 +26,25 @@ export function identifyAndParseDeeplinkURL(deeplinkURLString: string): {
*/
const urlWithoutProtocol = deeplinkURLString.replace(/^[^:]+:\/\//, '');
const deeplinkURL = new URL(deeplinkURLString, 'http://expo.dev');
// On web the pathname starts with '///' instead of '/'
const pathname = deeplinkURL.pathname.replace('///', '/');

if (deeplinkURL.pathname.startsWith('/auth')) {
if (pathname.startsWith('/auth')) {
return { urlType: URLType.AUTH, url: deeplinkURLString };
}
if (deeplinkURL.pathname.startsWith('/update')) {
if (pathname.startsWith('/update')) {
return {
urlType: URLType.EXPO_UPDATE,
url: getUrlFromSearchParams(deeplinkURL.searchParams),
};
}
if (deeplinkURL.pathname.startsWith('/download')) {
if (pathname.startsWith('/download')) {
return {
urlType: URLType.EXPO_BUILD,
url: getUrlFromSearchParams(deeplinkURL.searchParams),
};
}
if (deeplinkURL.pathname.startsWith('/snack')) {
if (pathname.startsWith('/snack')) {
return {
urlType: URLType.SNACK,
url: getUrlFromSearchParams(deeplinkURL.searchParams),
Expand Down
9 changes: 5 additions & 4 deletions apps/menu-bar/src/windows/Settings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ import React, { Fragment, useEffect, useState } from 'react';
import { StyleSheet, TouchableOpacity, Platform } from 'react-native';

import { WindowsNavigator } from './index';
import {
openAuthSessionAsync,
WebBrowserResultType,
} from '../../modules/web-authentication-session';
import { withApolloProvider } from '../api/ApolloClient';
import { Checkbox, View, Row, Text, Divider } from '../components';
import { Avatar } from '../components/Avatar';
Expand All @@ -24,9 +28,6 @@ import {
sessionSecretStorageKey,
resetApolloStore,
} from '../modules/Storage';
import WebAuthenticationSessionModule, {
WebBrowserResultType,
} from '../modules/WebAuthenticationSessionModule';
import { getCurrentUserDisplayName } from '../utils/helpers';
import { addOpacity } from '../utils/theme';
import { useCurrentTheme } from '../utils/useExpoTheme';
Expand Down Expand Up @@ -141,7 +142,7 @@ const Settings = () => {
const authSessionURL = `${
Config.website.origin
}/${type}?confirm_account=1&app_redirect_uri=${encodeURIComponent(redirectBase)}`;
const result = await WebAuthenticationSessionModule.openAuthSessionAsync(authSessionURL);
const result = await openAuthSessionAsync(authSessionURL);

if (result.type === WebBrowserResultType.SUCCESS) {
const resultURL = new URL(result.url);
Expand Down

0 comments on commit 56aae30

Please sign in to comment.