Skip to content

Commit

Permalink
[menu-bar] Automatically open popover when the user tries to reopen t…
Browse files Browse the repository at this point in the history
…he app from the Dock or Spotlight (#109)

* [menu-bar] Refactor useListDevices to use React Context

* [menu-bar] Open popover by default

* [menu-bar] Automatically open popover when the user tries to reopen the app from the Dock or Spotlight

* Add changelog entry
  • Loading branch information
gabrieldonadel authored Nov 28, 2023
1 parent 8cec4d4 commit 137f70e
Show file tree
Hide file tree
Showing 6 changed files with 72 additions and 26 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@

### 🎉 New features

- Automatically open popover when the user tries to reopen the app from the Dock or Spotlight. ([#109](https://github.com/expo/orbit/pull/109) by [@gabrieldonadel](https://github.com/gabrieldonadel))

### 🐛 Bug fixes

- Fix installing EAS builds from cold start. ([#108](https://github.com/expo/orbit/pull/108) by [@gabrieldonadel](https://github.com/gabrieldonadel))
Expand Down
9 changes: 9 additions & 0 deletions apps/menu-bar/macos/ExpoMenuBar-macOS/AppDelegate.mm
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,15 @@ - (void)applicationWillFinishLaunching:(NSNotification *)__unused aNotification
andEventID:kAEGetURL];
}

// 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];
}

return YES;
}

- (void)getUrlEventHandler:(NSAppleEventDescriptor *)event withReplyEvent:(NSAppleEventDescriptor *)replyEvent
{
[self openPopover];
Expand Down
5 changes: 4 additions & 1 deletion apps/menu-bar/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import AutoResizerRootView from './components/AutoResizerRootView';
import { SAFE_AREA_FACTOR } from './hooks/useSafeDisplayDimensions';
import { PersistGate } from './modules/PersistGate';
import Popover from './popover';
import { DevicesProvider } from './providers/DevicesProvider';
import { ThemeProvider } from './providers/ThemeProvider';

type Props = {
Expand All @@ -19,7 +20,9 @@ function App(props: Props) {
maxRelativeHeight={SAFE_AREA_FACTOR}>
<PersistGate>
<ThemeProvider themePreference="no-preference">
<Popover isDevWindow={props.isDevWindow} />
<DevicesProvider>
<Popover isDevWindow={props.isDevWindow} />
</DevicesProvider>
</ThemeProvider>
</PersistGate>
</AutoResizerRootView>
Expand Down
7 changes: 6 additions & 1 deletion apps/menu-bar/src/popover/Core.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ import DeviceItem, { DEVICE_ITEM_HEIGHT } from '../components/DeviceItem';
import { useDeepLinking } from '../hooks/useDeepLinking';
import { useDeviceAudioPreferences } from '../hooks/useDeviceAudioPreferences';
import { useGetPinnedApps } from '../hooks/useGetPinnedApps';
import { useListDevices } from '../hooks/useListDevices';
import { usePopoverFocusEffect } from '../hooks/usePopoverFocus';
import { useSafeDisplayDimensions } from '../hooks/useSafeDisplayDimensions';
import { useFileHandler } from '../modules/FileHandlerModule';
Expand All @@ -29,6 +28,7 @@ import {
getSelectedDevicesIds,
saveSelectedDevicesIds,
} from '../modules/Storage';
import { useListDevices } from '../providers/DevicesProvider';
import { getDeviceId, getDeviceOS, isVirtualDevice } from '../utils/device';
import { MenuBarStatus } from '../utils/helpers';
import { getPlatformFromURI, handleAuthUrl } from '../utils/parseUrl';
Expand All @@ -51,6 +51,11 @@ function Core(props: Props) {
const [progress, setProgress] = useState(0);

const { devicesPerPlatform, numberOfDevices, sections, refetch } = useListDevices();
usePopoverFocusEffect(
useCallback(() => {
refetch();
}, [refetch])
);
const { emulatorWithoutAudio } = useDeviceAudioPreferences();

// TODO: Extract into a hook
Expand Down
9 changes: 9 additions & 0 deletions apps/menu-bar/src/popover/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ import { ErrorBoundary, FallbackProps } from './ErrorBoundary';
import Footer from './Footer';
import { Text, View } from '../components';
import { useSafeDisplayDimensions } from '../hooks/useSafeDisplayDimensions';
import MenuBarModule from '../modules/MenuBarModule';
import { storage } from '../modules/Storage';
import { useListDevices } from '../providers/DevicesProvider';
import { WindowsNavigator } from '../windows';
import { hasSeenOnboardingStorageKey } from '../windows/Onboarding';

Expand All @@ -15,6 +17,7 @@ type Props = {

function Popover(props: Props) {
const { height } = useSafeDisplayDimensions();
const { hasInitialized } = useListDevices();

useEffect(() => {
const hasSeenOnboarding = storage.getBoolean(hasSeenOnboardingStorageKey);
Expand All @@ -23,6 +26,12 @@ function Popover(props: Props) {
}
}, []);

useEffect(() => {
if (hasInitialized && storage.getBoolean(hasSeenOnboardingStorageKey)) {
MenuBarModule.openPopover();
}
}, [hasInitialized]);

return (
<View
style={{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,39 @@
import { CliCommands } from 'common-types';
import { useCallback, useEffect, useState } from 'react';
import { DeviceEventEmitter } from 'react-native';
import React, { useCallback, useEffect, useState } from 'react';

import { listDevicesAsync } from '../commands/listDevicesAsync';
import { getUserPreferences } from '../modules/Storage';
import { DevicesPerPlatform, getDeviceId, getSectionsFromDeviceList } from '../utils/device';

export const useListDevices = () => {
type ListDevicesContext = {
devicesPerPlatform: DevicesPerPlatform;
sections: ReturnType<typeof getSectionsFromDeviceList>;
numberOfDevices: number;
hasInitialized: boolean;
error?: Error;
refetch: () => Promise<void>;
};

const defaultValues: ListDevicesContext = {
devicesPerPlatform: {
android: { devices: new Map() },
ios: { devices: new Map() },
},
sections: [],
numberOfDevices: 0,
hasInitialized: false,
refetch: async () => {},
};

const DevicesContext = React.createContext<ListDevicesContext>(defaultValues);
export const useListDevices = () => React.useContext(DevicesContext);

export function DevicesProvider({ children }: { children: React.ReactNode }) {
const [devicesPerPlatform, setDevicesPerPlatform] = useState<DevicesPerPlatform>({
android: { devices: new Map() },
ios: { devices: new Map() },
});
const [loading, setLoading] = useState(false);
const [hasInitialized, setHasInitialized] = useState(false);
const [error, setError] = useState<Error>();

const sections = getSectionsFromDeviceList(devicesPerPlatform);
Expand All @@ -23,7 +45,6 @@ export const useListDevices = () => {
showAndroidEmulators: showAndroid,
} = getUserPreferences();

setLoading(true);
try {
const devicesList = await listDevicesAsync({ platform: 'all' });
const iosDevices = new Map<
Expand Down Expand Up @@ -61,28 +82,25 @@ export const useListDevices = () => {
});
} catch (err) {
setError(err as Error);
} finally {
setLoading(false);
}
}, []);

useEffect(() => {
const listener = DeviceEventEmitter.addListener('popoverFocused', () => {
updateDevicesList();
});
updateDevicesList();

return () => {
listener.remove();
};
updateDevicesList().then(() => setHasInitialized(true));
}, [updateDevicesList]);

return {
devicesPerPlatform,
sections,
numberOfDevices: devicesPerPlatform.android.devices.size + devicesPerPlatform.ios.devices.size,
loading,
error,
refetch: updateDevicesList,
};
};
return (
<DevicesContext.Provider
value={{
devicesPerPlatform,
sections,
numberOfDevices:
devicesPerPlatform.android.devices.size + devicesPerPlatform.ios.devices.size,
hasInitialized,
error,
refetch: updateDevicesList,
}}>
{children}
</DevicesContext.Provider>
);
}

0 comments on commit 137f70e

Please sign in to comment.