Thanks to visit codestin.com
Credit goes to github.com

Skip to content
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
- Upgrade to `expo` SDK 53 and react-native `0.79`. ([#254](https://github.com/expo/orbit/pull/254) by [@gabrieldonadel](https://github.com/gabrieldonadel))
- Update ESLint, `eslint-config-universe` and setups, bump TypeScript versions. ([#261](https://github.com/expo/orbit/pull/261) by [@Simek](https://github.com/Simek))
- Add macOS build script. ([#262](https://github.com/expo/orbit/pull/262) by [@gabrieldonadel](https://github.com/gabrieldonadel))
- Simplify `AppDelegate.m` logic in to align with Expo's template. ([#263](https://github.com/expo/orbit/pull/263) by [@gabrieldonadel](https://github.com/gabrieldonadel))

## 2.0.3 — 2025-05-16

Expand Down
9 changes: 1 addition & 8 deletions apps/menu-bar/macos/ExpoMenuBar-macOS/AppDelegate.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,15 @@

#import "Expo_Orbit-Swift.h"

@class RCTBridge;

@interface AppDelegate : RCTAppDelegate <NSUserNotificationCenterDelegate>
{
NSStatusItem *statusItem;
NSPopover *popover;
SwifterWrapper *httpServer;
PopoverManager *popoverManager;
}

@property(nonatomic, strong, readonly) NSPopover *popover;
#if RCT_DEV
@property (nonatomic, strong) NSWindowController *devWindowController;
#endif
- (void)openPopover;
- (void)closePopover;
- (void)setPopoverContentSize:(NSSize)size;
- (IBAction)showHelp:(id)sender;

@end
110 changes: 5 additions & 105 deletions apps/menu-bar/macos/ExpoMenuBar-macOS/AppDelegate.m
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,10 @@

#import <React/RCTBridge.h>
#import <React/RCTBundleURLProvider.h>
#import <React/RCTRootView.h>
#import <React/RCTLinkingManager.h>

#import "DevViewController.h"
#import "WindowNavigator.h"
#import "Expo_Orbit-Swift.h"
#import "DragDropStatusItemView.h"

@implementation AppDelegate

Expand All @@ -25,27 +22,14 @@ - (void)applicationDidFinishLaunching:(NSNotification *)notification {

- (void)loadReactNativeWindow:(NSDictionary *)launchOptions
{
statusItem = [[NSStatusBar systemStatusBar] statusItemWithLength:NSSquareStatusItemLength];
DragDropStatusItemView *dragDropView = [[DragDropStatusItemView alloc] initWithFrame:NSMakeRect(0, 0, 22, 22)];
dragDropView.openPopoverAction = ^{
[self openPopover];
};
[statusItem.button addSubview:dragDropView];
[statusItem.button setTarget:self];
[statusItem.button sendActionOn:NSEventMaskRightMouseUp | NSEventMaskLeftMouseUp];
[statusItem.button setAction:@selector(onPressStatusItem:)];

RCTPlatformView *rootView = [self.rootViewFactory viewWithModuleName:self.moduleName
initialProperties:self.initialProps
launchOptions:launchOptions];
NSViewController *rootViewController = [[NSViewController alloc] init];
rootViewController.view = rootView;

popover = [[NSPopover alloc] init];
popover.contentSize = NSMakeSize(380, 450);
popover.contentViewController = rootViewController;
popover.behavior = NSPopoverBehaviorTransient;
[self addPopoverObservers];
popoverManager = [PopoverManager initializeSharedWithDelegate:self];
[popoverManager setContentViewController:rootViewController];

#ifdef SHOW_DEV_WINDOW
#if RCT_DEV
Expand All @@ -72,92 +56,11 @@ - (void)customizeRootView:(RCTUIView *)rootView

- (BOOL)application:(NSApplication *)_ openFile:(NSString *)filename
{
[self openPopover];

[popoverManager openPopover];
[NSNotificationCenter.defaultCenter postNotificationName:@"ExpoOrbit_OnOpenFile" object:filename];
return YES;
}

- (NSMenu *)createContextMenu {
NSMenu *menu = [[NSMenu alloc] initWithTitle:@"My Menu"];

[menu addItemWithTitle:@"Settings..." action:@selector(settingsAction:) keyEquivalent:@""];
[menu addItemWithTitle:@"Quit" action:@selector(quitAction:) keyEquivalent:@"q"];

return menu;
}


- (void)quitAction:(id)sender {
exit(0);
}


- (void)settingsAction:(id)sender {
WindowNavigator *windowNavigator = [WindowNavigator shared];
[windowNavigator openWindow:@"Settings" options:@{
@"windowStyle": @{
@"titlebarAppearsTransparent": @YES,
@"height": @(580.0),
@"width": @(500.0)
}
}];
}

- (void)openPopover {
[popover showRelativeToRect:statusItem.button.bounds
ofView:statusItem.button
preferredEdge:NSMinYEdge];
[popover.contentViewController.view.window makeKeyWindow];
[self.bridge enqueueJSCall:@"RCTDeviceEventEmitter.emit"
args:@[@"popoverFocused", @{
@"screenSize": @{
@"height": @([[NSScreen mainScreen] frame].size.height),
@"width": @([[NSScreen mainScreen] frame].size.width)
}
}]];
}

- (void)closePopover {
[popover close];
}

- (void)setPopoverContentSize:(NSSize)size {
[popover setContentSize:size];
[popover.contentViewController.view setFrameSize:size];
}

- (void)addPopoverObservers {
NSNotificationCenter *notificationCenter = NSNotificationCenter.defaultCenter;
__weak typeof(self) weakSelf = self;

[notificationCenter addObserverForName:@"ExpoOrbit_OpenPopover" object:nil queue:nil usingBlock:^(NSNotification * _Nonnull notification) {
[weakSelf openPopover];
}];
[notificationCenter addObserverForName:@"ExpoOrbit_ClosePopover" object:nil queue:nil usingBlock:^(NSNotification * _Nonnull notification) {
[weakSelf closePopover];
}];
}

- (void)onPressStatusItem:(id)sender {
NSEvent *event = [NSApp currentEvent];
if (event.type == NSEventTypeRightMouseUp) {
NSMenu *contextMenu = [self createContextMenu];
[statusItem popUpStatusItemMenu:contextMenu];
return;
}

if (popover.isShown) {
[self closePopover];
} else {
[self openPopover];
}
}

- (void)applicationWillTerminate:(NSNotification *)aNotification {
// Insert code here to tear down your application
}

- (void)applicationWillFinishLaunching:(NSNotification *)__unused aNotification
{
[NSUserNotificationCenter defaultUserNotificationCenter].delegate = self;
Expand All @@ -171,21 +74,18 @@ - (void)applicationWillFinishLaunching:(NSNotification *)__unused aNotification
// Called when the user tries to reopen the app from the Dock or Spotlight
- (BOOL)applicationShouldHandleReopen:(NSApplication *)sender hasVisibleWindows:(BOOL)visibleWindows {
if (!visibleWindows) {
[self openPopover];
[popoverManager openPopover];
}

return YES;
}

- (void)getUrlEventHandler:(NSAppleEventDescriptor *)event withReplyEvent:(NSAppleEventDescriptor *)replyEvent
{
[self openPopover];
[popoverManager openPopover];
[RCTLinkingManager getUrlEventHandler:event withReplyEvent:replyEvent];
}

- (NSPopover *)popover {
return popover;
}

#pragma mark - RCTBridgeDelegate Methods

Expand Down
6 changes: 2 additions & 4 deletions apps/menu-bar/macos/ExpoMenuBar-macOS/AutoResizerRootView.m
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
#import "AutoResizerRootView.h"
#import "AppDelegate.h"

#import "Expo_Orbit-Swift.h"

const CGFloat minimumViewSize = 40.0;

Expand Down Expand Up @@ -30,8 +29,7 @@ - (void)layout
CGFloat newHeight = frameHeight <= maxHeight ? frameHeight : maxHeight;

dispatch_async(dispatch_get_main_queue(), ^{
AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
[appDelegate setPopoverContentSize:CGSizeMake(self.frame.size.width, newHeight)];
[PopoverManager.shared setPopoverContentSize:CGSizeMake(self.frame.size.width, newHeight)];
});
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,2 @@
//
// Use this file to import your target's public headers that you would like to expose to Swift.
//

#import "DragDropStatusItemView.h"
#import "WindowNavigator.h"
145 changes: 145 additions & 0 deletions apps/menu-bar/macos/ExpoMenuBar-macOS/PopoverManager.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
import Cocoa
import React_RCTAppDelegate

class PopoverManager: NSObject {
@objc public static private(set) var shared: PopoverManager!

@objc public var delegate: RCTAppDelegate
private var statusItem: NSStatusItem!
@objc var popover: NSPopover!

@objc public static func initializeShared(delegate: RCTAppDelegate) -> PopoverManager {
if shared == nil {
shared = PopoverManager(delegate: delegate)
}
return shared
}

init(delegate: RCTAppDelegate) {
self.delegate = delegate
super.init()

self.setupPopover()
self.setupStatusItem()
}

private func setupStatusItem() {
statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.squareLength)

let dragDropView = DragDropStatusItemView(frame: NSRect(x: 0, y: 0, width: 22, height: 22))!
dragDropView.openPopoverAction = { [weak self] in
self?.openPopover()
}

let button = statusItem.button!
button.addSubview(dragDropView)
button.target = self
button.sendAction(on: .init([.leftMouseUp, .rightMouseUp]))
button.action = #selector(handleStatusItemClick(_:))
}

private func setupPopover() {
popover = NSPopover()
popover.contentSize = NSSize(width: 380, height: 450)
popover.behavior = .transient
setupObservers()
}

private func setupObservers() {
NotificationCenter.default.addObserver(
self,
selector: #selector(openPopover),
name: Notification.Name("ExpoOrbit_OpenPopover"),
object: nil
)

NotificationCenter.default.addObserver(
self,
selector: #selector(closePopover),
name: Notification.Name("ExpoOrbit_ClosePopover"),
object: nil
)
}

// MARK: - Event Handling

@objc private func handleStatusItemClick(_ sender: Any?) {
guard let event = NSApp.currentEvent else { return }

if event.type == .rightMouseUp {
showContextMenu()
} else {
popover.isShown ? closePopover() : openPopover()
}
}

private func showContextMenu() {
let menu = NSMenu()

let settingsItem = NSMenuItem(
title: "Settings...",
action: #selector(settingsAction),
keyEquivalent: ""
)
settingsItem.target = self

let quitItem = NSMenuItem(
title: "Quit",
action: #selector(quitAction(_:)),
keyEquivalent: "q"
)
quitItem.target = self

menu.addItem(settingsItem)
menu.addItem(quitItem)
statusItem.popUpMenu(menu)
}

// MARK: - Actions

@objc private func settingsAction() {
WindowNavigator.shared().openWindow(
"Settings",
options: [
"windowStyle": ["titlebarAppearsTransparent": true, "height": 580.0, "width": 500.0]
])
}

@objc private func quitAction(_ sender: Any?) {
NSApp.terminate(nil)
}

// MARK: - Public Interface

@objc func setContentViewController(_ viewController: NSViewController) {
popover.contentViewController = viewController
}

// MARK: - Popover Management
@objc func openPopover() {
guard let button = statusItem.button else { return }

popover.show(
relativeTo: button.bounds,
of: button,
preferredEdge: .minY)
popover.contentViewController?.view.window?.makeKey()

let screenSize: [String: Any] = [
"height": NSScreen.main?.frame.height ?? 0,
"width": NSScreen.main?.frame.width ?? 0
]

delegate.bridge?.enqueueJSCall(
"RCTDeviceEventEmitter.emit", args: ["popoverFocused", ["screenSize": screenSize]])
}

@objc func closePopover() {
popover.close()
}

@objc func setPopoverContentSize(_ size: NSSize) {
popover.contentSize = size
popover.contentViewController?.view.frame.size = size
}
}
Loading