Skip to content

macOS Delta #1

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

Open
wants to merge 1 commit into
base: macosBase
Choose a base branch
from
Open
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
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @format
* @flow strict-local
*/

import NativeAddressSanitizerCrash from './NativeAddressSanitizerCrash';

module.exports = NativeAddressSanitizerCrash;
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict-local
* @format
*/

import type {TurboModule} from '../TurboModule/RCTExport';

import * as TurboModuleRegistry from '../TurboModule/TurboModuleRegistry';

export interface Spec extends TurboModule {}

export default (TurboModuleRegistry.get<Spec>(
'RCTAddressSanitizerCrash',
): ?Spec);
118 changes: 118 additions & 0 deletions packages/react-native/Libraries/Alert/Alert.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,22 @@ export type Buttons = Array<{
style?: AlertButtonStyle,
...
}>;
// [macOS
export type DefaultInputsArray = Array<{
default?: string,
placeholder?: string,
style?: AlertButtonStyle,
}>;
// macOS]

type Options = {
cancelable?: ?boolean,
userInterfaceStyle?: 'unspecified' | 'light' | 'dark',
onDismiss?: ?() => void,
// [macOS
modal?: ?boolean,
critical?: ?boolean,
// macOS]
...
};

Expand All @@ -56,6 +67,18 @@ class Alert {
undefined,
options,
);
// [macOS
} else if (Platform.OS === 'macos') {
Alert.promptMacOS(
title,
message,
buttons,
'default',
undefined,
options?.modal,
options?.critical,
);
// macOS]
} else if (Platform.OS === 'android') {
const NativeDialogManagerAndroid =
require('../NativeModules/specs/NativeDialogManagerAndroid').default;
Expand Down Expand Up @@ -167,8 +190,103 @@ class Alert {
cb && cb(value);
},
);
// [macOS
} else if (Platform.OS === 'macos') {
const defaultInputs = [{default: defaultValue}];
Alert.promptMacOS(title, message, callbackOrButtons, type, defaultInputs);
}
// macOS]
}

// [macOS
/**
* Create and display a prompt to enter some text.
* @static
* @method promptMacOS
* @param title The dialog's title.
* @param message An optional message that appears above the text
* input.
* @param callbackOrButtons This optional argument should
* be either a single-argument function or an array of buttons. If passed
* a function, it will be called with the prompt's value when the user
* taps 'OK'.
*
* If passed an array of button configurations, each button should include
* a `text` key, as well as optional `onPress` key (see
* example).
* @param type This configures the text input. One of 'plain-text',
* 'secure-text' or 'login-password'.
* @param defaultInputs This optional argument should be an array of couple
* default value - placeholder for the input fields.
* @param modal The alert can be optionally run as an app-modal dialog, instead
* of the default presentation as a sheet.
* @param critical This optional argument should be used when it's needed to
* warn the user about severe consequences of an impending event
* (such as deleting a file).
*
* @example <caption>Example with custom buttons</caption>
*
* AlertMacOS.promptMacOS(
* 'Enter password',
* 'Enter your password to claim your $1.5B in lottery winnings',
* [
* {text: 'Cancel', onPress: () => console.log('Cancel Pressed'), style: 'cancel'},
* {text: 'OK', onPress: password => console.log('OK Pressed, password: ' + password)},
* ],
* 'secure-text'
* );
*
* @example <caption>Example with the default button and a custom callback</caption>
*
* AlertMacOS.prompt(
* 'Update username',
* null,
* text => console.log("Your username is "+text),
* null,
* 'default'
* );
*/
static promptMacOS(
title: ?string,
message?: ?string,
callbackOrButtons?: ?((text: string) => void) | Buttons,
type?: ?AlertType = 'plain-text',
defaultInputs?: DefaultInputsArray,
modal?: ?boolean,
critical?: ?boolean,
): void {
let callbacks: Array<?any> = [];
const buttons = [];
if (typeof callbackOrButtons === 'function') {
callbacks = [callbackOrButtons];
} else if (callbackOrButtons instanceof Array) {
callbackOrButtons.forEach((btn, index) => {
callbacks[index] = btn.onPress;
if (btn.text || index < (callbackOrButtons || []).length - 1) {
const btnDef: {[number]: string} = {};
btnDef[index] = btn.text || '';
buttons.push(btnDef);
}
});
}

RCTAlertManager.alertWithArgs(
{
title: title || undefined,
message: message || undefined,
buttons,
type: type || undefined,
defaultInputs,
modal: modal || undefined,
critical: critical || undefined,
},
(id, value) => {
const cb = callbacks[id];
cb && cb(value);
},
);
}
// macOS]
}

module.exports = Alert;
5 changes: 5 additions & 0 deletions packages/react-native/Libraries/Alert/NativeAlertManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,11 @@ export type Args = {|
preferredButtonKey?: string,
keyboardType?: string,
userInterfaceStyle?: string,
// [macOS
defaultInputs?: Array<Object>,
modal?: ?boolean,
critical?: ?boolean,
// macOS]
|};

export interface Spec extends TurboModule {
Expand Down
16 changes: 16 additions & 0 deletions packages/react-native/Libraries/Alert/RCTAlertManager.macos.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @format
* @flow strict-local
*/

// [macOS]

/* $FlowFixMe allow macOS to share iOS file */
const alertWithArgs = require('./RCTAlertManager.ios');

module.exports = alertWithArgs;
8 changes: 7 additions & 1 deletion packages/react-native/Libraries/Animated/AnimatedMock.js
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,13 @@ const spring = function (
return {
...emptyAnimation,
start: mockAnimationStart((callback?: ?EndCallback): void => {
anyValue.setValue(config.toValue);
// [macOS setValue can't handle AnimatedNodes
if (config.toValue instanceof AnimatedNode) {
anyValue.setValue(config.toValue.__getValue());
} else {
// macOS]
anyValue.setValue(config.toValue);
}
callback?.({finished: true});
}),
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -600,7 +600,9 @@ export default {
nativeEventEmitter = new NativeEventEmitter(
// T88715063: NativeEventEmitter only used this parameter on iOS. Now it uses it on all platforms, so this code was modified automatically to preserve its behavior
// If you want to use the native module on other platforms, please remove this condition and test its behavior
Platform.OS !== 'ios' ? null : NativeAnimatedModule,
Platform.OS !== 'ios' && Platform.OS !== 'macos' // [macOS] Also use this parameter on macOS
? null
: NativeAnimatedModule,
);
}
return nativeEventEmitter;
Expand Down
17 changes: 10 additions & 7 deletions packages/react-native/Libraries/AppDelegate/RCTAppDelegate.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
*/

#import <React/RCTBridgeDelegate.h>
#import <UIKit/UIKit.h>
#import <React/RCTUIKit.h> // [macOS]

@class RCTBridge;
@protocol RCTBridgeDelegate;
Expand Down Expand Up @@ -51,10 +51,13 @@
(const facebook::react::ObjCTurboModule::InitParams &)params
* - (id<RCTTurboModule>)getModuleInstanceFromClass:(Class)moduleClass
*/
#if !TARGET_OS_OSX // [macOS]
@interface RCTAppDelegate : UIResponder <UIApplicationDelegate, UISceneDelegate, RCTBridgeDelegate>

#else // [macOS
@interface RCTAppDelegate : NSResponder <NSApplicationDelegate, RCTBridgeDelegate>
#endif // macOS]
/// The window object, used to render the UViewControllers
@property (nonatomic, strong) UIWindow *window;
@property (nonatomic, strong) RCTPlatformWindow *window; // [macOS]
@property (nonatomic, strong) RCTBridge *bridge;
@property (nonatomic, strong) NSString *moduleName;
@property (nonatomic, strong) NSDictionary *initialProps;
Expand Down Expand Up @@ -83,9 +86,9 @@
*
* @returns: a UIView properly configured with a bridge for React Native.
*/
- (UIView *)createRootViewWithBridge:(RCTBridge *)bridge
moduleName:(NSString *)moduleName
initProps:(NSDictionary *)initProps;
- (RCTPlatformView *)createRootViewWithBridge:(RCTBridge *)bridge // [macOS]
moduleName:(NSString *)moduleName
initProps:(NSDictionary *)initProps;

/**
* It creates the RootViewController.
Expand All @@ -104,7 +107,7 @@
*
* @return: void
*/
- (void)setRootView:(UIView *)rootView toRootViewController:(UIViewController *)rootViewController;
- (void)setRootView:(RCTPlatformView *)rootView toRootViewController:(UIViewController *)rootViewController; // [macOS]

/// This method controls whether the App will use RuntimeScheduler. Only applicable in the legacy architecture.
///
Expand Down
44 changes: 40 additions & 4 deletions packages/react-native/Libraries/AppDelegate/RCTAppDelegate.mm
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,15 @@ - (instancetype)init
}
#endif

#if !TARGET_OS_OSX // [macOS]
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
#else // [macOS
- (void)applicationDidFinishLaunching:(NSNotification *)notification
{
NSApplication *application = [notification object];
NSDictionary *launchOptions = [notification userInfo];
#endif // macOS]
BOOL enableTM = NO;
BOOL enableBridgeless = NO;
#if RCT_NEW_ARCH_ENABLED
Expand All @@ -87,7 +94,7 @@ - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(

RCTAppSetupPrepareApp(application, enableTM);

UIView *rootView;
RCTPlatformView *rootView; // [macOS]

if (enableBridgeless) {
#if RCT_NEW_ARCH_ENABLED
Expand Down Expand Up @@ -125,6 +132,7 @@ - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(
NSDictionary *initProps = [self prepareInitialProps];
rootView = [self createRootViewWithBridge:self.bridge moduleName:self.moduleName initProps:initProps];
}
#if !TARGET_OS_OSX // [macOS]
self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
UIViewController *rootViewController = [self createRootViewController];
[self setRootView:rootView toRootViewController:rootViewController];
Expand All @@ -133,6 +141,21 @@ - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(
[self.window makeKeyAndVisible];

return YES;
#else // [macOS
NSRect frame = NSMakeRect(0,0,1024,768);
self.window = [[NSWindow alloc] initWithContentRect:NSZeroRect
styleMask:NSWindowStyleMaskTitled | NSWindowStyleMaskResizable | NSWindowStyleMaskClosable | NSWindowStyleMaskMiniaturizable
backing:NSBackingStoreBuffered
defer:NO];
self.window.title = self.moduleName;
self.window.autorecalculatesKeyViewLoop = YES;
NSViewController *rootViewController = [NSViewController new];
rootViewController.view = rootView;
rootView.frame = frame;
self.window.contentViewController = rootViewController;
[self.window makeKeyAndOrderFront:self];
[self.window center];
#endif // macOS]
}

- (NSURL *)sourceURLForBridge:(RCTBridge *)bridge
Expand Down Expand Up @@ -160,27 +183,38 @@ - (RCTBridge *)createBridgeWithDelegate:(id<RCTBridgeDelegate>)delegate launchOp
return [[RCTBridge alloc] initWithDelegate:delegate launchOptions:launchOptions];
}

- (UIView *)createRootViewWithBridge:(RCTBridge *)bridge
- (RCTPlatformView *)createRootViewWithBridge:(RCTBridge *)bridge // [macOS]
moduleName:(NSString *)moduleName
initProps:(NSDictionary *)initProps
{
BOOL enableFabric = NO;
#if RCT_NEW_ARCH_ENABLED
enableFabric = self.fabricEnabled;
#endif
UIView *rootView = RCTAppSetupDefaultRootView(bridge, moduleName, initProps, enableFabric);
RCTPlatformView *rootView = RCTAppSetupDefaultRootView(bridge, moduleName, initProps, enableFabric); // [macOS]

#if !TARGET_OS_OSX // [macOS]
rootView.backgroundColor = [UIColor systemBackgroundColor];
#else // [macOS
rootView.layer.backgroundColor = [[NSColor windowBackgroundColor] CGColor];
#endif // macOS]

return rootView;
}

#if !TARGET_OS_OSX // [macOS]
- (UIViewController *)createRootViewController
{
return [UIViewController new];
}
#else // [macOS
- (NSViewController *)createRootViewController
{
return [NSViewController new];
}
#endif // macOS]

- (void)setRootView:(UIView *)rootView toRootViewController:(UIViewController *)rootViewController
- (void)setRootView:(RCTPlatformView *)rootView toRootViewController:(UIViewController *)rootViewController // [macOS]
{
rootViewController.view = rootView;
}
Expand All @@ -191,13 +225,15 @@ - (BOOL)runtimeSchedulerEnabled
}

#pragma mark - UISceneDelegate
#if !TARGET_OS_OSX // [macOS]
- (void)windowScene:(UIWindowScene *)windowScene
didUpdateCoordinateSpace:(id<UICoordinateSpace>)previousCoordinateSpace
interfaceOrientation:(UIInterfaceOrientation)previousInterfaceOrientation
traitCollection:(UITraitCollection *)previousTraitCollection API_AVAILABLE(ios(13.0))
{
[[NSNotificationCenter defaultCenter] postNotificationName:RCTRootViewFrameDidChangeNotification object:self];
}
#endif // [macOS]

#pragma mark - RCTCxxBridgeDelegate
- (std::unique_ptr<facebook::react::JSExecutorFactory>)jsExecutorFactoryForBridge:(RCTBridge *)bridge
Expand Down
Loading