Skip to content

Commit

Permalink
Changes for TV support (#77)
Browse files Browse the repository at this point in the history
- New user pref "Show experimental features" (false by default)
- New user prefs to hide/show different simulator types
- Checkbox in settings UI for the experimental features
- New row in menu bar UI to switch between different simulator lists
- Awareness of `osType=tvOS` in `common-types` and `eas-shared`
- Other changes:
  - Boolean user preferences are now always defined -- defaults in `Storage.ts`
  - Allow showing debug menu even if we are not logged in
  • Loading branch information
douglowder committed Oct 14, 2023
1 parent a8a3190 commit 6056712
Show file tree
Hide file tree
Showing 9 changed files with 138 additions and 21 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

### 🎉 New features

- Add ability to show/hide different types of simulators, and add experimental TV support. ([#77](https://github.com/expo/orbit/pull/77) by [@douglowder](https://github.com/douglowder))
- Add support for opening tarballs with multiple apps. ([#73](https://github.com/expo/orbit/pull/73) by [@gabrieldonadel](https://github.com/gabrieldonadel))
- Improve feedback to the user when an error occurs. ([#64](https://github.com/expo/orbit/pull/64) by [@gabrieldonadel](https://github.com/gabrieldonadel))
- Added drag and drop support for installing apps. ([#57](https://github.com/expo/orbit/pull/57) by [@gabrieldonadel](https://github.com/gabrieldonadel))
Expand Down
7 changes: 0 additions & 7 deletions apps/menu-bar/.prettierrc.js

This file was deleted.

22 changes: 20 additions & 2 deletions apps/menu-bar/src/hooks/useListDevices.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@ import { useCallback, useEffect, useState } from 'react';
import { DeviceEventEmitter } from 'react-native';

import { listDevicesAsync } from '../commands/listDevicesAsync';
import { UserPreferences } from '../modules/Storage';
import { getSectionsFromDeviceList } from '../utils/device';

export const useListDevices = () => {
export const useListDevices = (userPreferences: UserPreferences) => {
const [devicesPerPlatform, setDevicesPerPlatform] = useState<DevicesPerPlatform>({
android: { devices: [] },
ios: { devices: [] },
Expand All @@ -19,13 +20,30 @@ export const useListDevices = () => {
setLoading(true);
try {
const devicesList = await listDevicesAsync({ platform: 'all' });
const showIos = userPreferences.showIosSimulators;
const showTvos =
userPreferences.showExperimentalFeatures && userPreferences.showTvosSimulators;
const showAndroid = userPreferences.showAndroidEmulators;
if (!showIos) {
devicesList.ios.devices = devicesList.ios.devices.filter(
(device) => device.osType !== 'iOS'
);
}
if (!showTvos) {
devicesList.ios.devices = devicesList.ios.devices.filter(
(device) => device.osType !== 'tvOS'
);
}
if (!showAndroid) {
devicesList.android.devices = [];
}
setDevicesPerPlatform(devicesList);
} catch (err) {
setError(err as Error);
} finally {
setLoading(false);
}
}, []);
}, [userPreferences]);

useEffect(() => {
const listener = DeviceEventEmitter.addListener('popoverFocused', () => {
Expand Down
22 changes: 18 additions & 4 deletions apps/menu-bar/src/modules/Storage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,28 @@ import { MMKV } from 'react-native-mmkv';
const userPreferencesStorageKey = 'user-preferences';

export type UserPreferences = {
launchOnLogin?: boolean;
emulatorWithoutAudio?: boolean;
launchOnLogin: boolean;
emulatorWithoutAudio: boolean;
customSdkPath?: string;
showExperimentalFeatures: boolean;
showIosSimulators: boolean;
showTvosSimulators: boolean;
showAndroidEmulators: boolean;
};

export const defaultUserPreferences: UserPreferences = {
launchOnLogin: false,
emulatorWithoutAudio: false,
showExperimentalFeatures: false,
showIosSimulators: true,
showTvosSimulators: false,
showAndroidEmulators: true,
};

export const getUserPreferences = async () => {
const value = await AsyncStorage.getItem(userPreferencesStorageKey);
return JSON.parse(value ?? '{}') as UserPreferences;
const stringValue = await AsyncStorage.getItem(userPreferencesStorageKey);
const value = (stringValue ? JSON.parse(stringValue) : defaultUserPreferences) as UserPreferences;
return value;
};

export const saveUserPreferences = async (preferences: UserPreferences) => {
Expand Down
69 changes: 67 additions & 2 deletions apps/menu-bar/src/popover/Core.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import { bootDeviceAsync } from '../commands/bootDeviceAsync';
import { downloadBuildAsync } from '../commands/downloadBuildAsync';
import { installAndLaunchAppAsync } from '../commands/installAndLaunchAppAsync';
import { launchSnackAsync } from '../commands/launchSnackAsync';
import { Spacer, Text, View } from '../components';
import { Checkbox, Spacer, Text, View } from '../components';
import DeviceItem, { DEVICE_ITEM_HEIGHT } from '../components/DeviceItem';
import ProgressIndicator from '../components/ProgressIndicator';
import { useDeepLinking } from '../hooks/useDeepLinking';
Expand All @@ -31,6 +31,10 @@ import {
SelectedDevicesIds,
getSelectedDevicesIds,
saveSelectedDevicesIds,
UserPreferences,
defaultUserPreferences,
getUserPreferences,
saveUserPreferences,
} from '../modules/Storage';
import { openProjectsSelectorURL } from '../utils/constants';
import { getDeviceId, getDeviceOS, isVirtualDevice } from '../utils/device';
Expand All @@ -54,14 +58,23 @@ function Core(props: Props) {
android: undefined,
ios: undefined,
});
const [userPreferences, setUserPreferences] = useState<UserPreferences>(defaultUserPreferences);

useEffect(() => {
getUserPreferences().then((value) => {
saveUserPreferences(value);
setUserPreferences(value);
});
}, []);

const { apps } = useGetPinnedApps();
const showProjectsSection = Boolean(apps?.length);

const [status, setStatus] = useState(Status.LISTENING);
const [progress, setProgress] = useState(0);

const { devicesPerPlatform, numberOfDevices, sections, refetch } = useListDevices();
const { devicesPerPlatform, numberOfDevices, sections, refetch } =
useListDevices(userPreferences);
const { emulatorWithoutAudio } = useDeviceAudioPreferences();
const theme = useExpoTheme();

Expand Down Expand Up @@ -113,6 +126,36 @@ function Core(props: Props) {
[emulatorWithoutAudio]
);

const onPressShowIosSimulators = async (value: boolean) => {
const newPreferences = {
...userPreferences,
showIosSimulators: value,
};
saveUserPreferences(newPreferences).then(() => {
setUserPreferences(newPreferences);
});
};

const onPressShowTvosSimulators = async (value: boolean) => {
const newPreferences = {
...userPreferences,
showTvosSimulators: value,
};
saveUserPreferences(newPreferences).then(() => {
setUserPreferences(newPreferences);
});
};

const onPressShowAndroidEmulators = async (value: boolean) => {
const newPreferences = {
...userPreferences,
showAndroidEmulators: value,
};
saveUserPreferences(newPreferences).then(() => {
setUserPreferences(newPreferences);
});
};

// @TODO: create another hook
const handleSnackUrl = useCallback(
async (url: string) => {
Expand Down Expand Up @@ -277,6 +320,28 @@ function Core(props: Props) {
</View>
) : null}
</View>
<View px="medium" style={{ flexDirection: 'row' }}>
<Text size="small" weight="normal">
Devices to show:
</Text>
<Checkbox
value={userPreferences.showIosSimulators}
onValueChange={onPressShowIosSimulators}
label="iOS"
/>
{userPreferences.showExperimentalFeatures ? (
<Checkbox
value={userPreferences.showTvosSimulators}
onValueChange={onPressShowTvosSimulators}
label="tvOS"
/>
) : null}
<Checkbox
value={userPreferences.showAndroidEmulators}
onValueChange={onPressShowAndroidEmulators}
label="Android"
/>
</View>
{apps?.length ? <ProjectsSection apps={apps} /> : null}
<View shrink="1" pt="tiny">
<SectionList
Expand Down
5 changes: 4 additions & 1 deletion apps/menu-bar/src/utils/device.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,14 @@ export type BaseDevice = {
);

export function getDeviceOS(device: Device): 'android' | 'ios' {
if (device.osType === 'tvOS') {
return 'ios';
}
return device.osType.toLowerCase() as 'android' | 'ios';
}

export function getDeviceId(device: Device): string {
return device.osType === 'iOS' ? device.udid : device.name;
return device.osType === 'iOS' || device.osType === 'tvOS' ? device.udid : device.name;
}

export function getSectionsFromDeviceList(
Expand Down
25 changes: 24 additions & 1 deletion apps/menu-bar/src/windows/Settings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import MenuBarModule from '../modules/MenuBarModule';
import SparkleModule from '../modules/SparkleModule';
import {
UserPreferences,
defaultUserPreferences,
getUserPreferences,
saveUserPreferences,
storage,
Expand All @@ -30,7 +31,7 @@ const Settings = () => {
Boolean(storage.getString('sessionSecret'))
);

const [userPreferences, setUserPreferences] = useState<UserPreferences>({});
const [userPreferences, setUserPreferences] = useState<UserPreferences>(defaultUserPreferences);
const [customSdkPathEnabled, setCustomSdkPathEnabled] = useState(false);
const [automaticallyChecksForUpdates, setAutomaticallyChecksForUpdates] = useState(false);

Expand Down Expand Up @@ -78,6 +79,14 @@ const Settings = () => {
SparkleModule.setAutomaticallyChecksForUpdates(value);
};

const onPressSetShowExperimentalFeatures = async (value: boolean) => {
setUserPreferences((prev) => {
const newPreferences = { ...prev, showExperimentalFeatures: value };
saveUserPreferences(newPreferences);
return newPreferences;
});
};

const onPressEmulatorWithoutAudio = async (value: boolean) => {
setUserPreferences((prev) => {
const newPreferences = { ...prev, emulatorWithoutAudio: value };
Expand Down Expand Up @@ -167,6 +176,13 @@ const Settings = () => {
color="primary"
/>
<Button title="Log In" onPress={() => handleAuthentication('login')} />
{__DEV__ ? (
<TouchableOpacity
onPress={() => WindowsNavigator.open('DebugMenu')}
style={[styles.debugButton, getStylesForColor('primary', theme)?.touchableStyle]}>
<SystemIconView systemIconName="ladybug" />
</TouchableOpacity>
) : null}
</Row>
)}
</View>
Expand Down Expand Up @@ -207,6 +223,13 @@ const Settings = () => {
label="Custom Android SDK root location"
/>
</Row>
<Row mb="2" align="center">
<Checkbox
value={userPreferences.showExperimentalFeatures}
onValueChange={onPressSetShowExperimentalFeatures}
label="Show experimental features (requires restart)"
/>
</Row>
<PathInput
editable={customSdkPathEnabled}
onChangeText={(text) => {
Expand Down
2 changes: 1 addition & 1 deletion packages/common-types/src/devices.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export interface IosSimulator {
runtime: string;
osVersion: string;
windowName: string;
osType: 'iOS';
osType: 'iOS' | 'tvOS';
state: 'Booted' | 'Shutdown';
isAvailable: boolean;
name: string;
Expand Down
6 changes: 3 additions & 3 deletions packages/eas-shared/src/run/ios/simulator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ export async function getAvailableIosSimulatorsListAsync(query?: string): Promis
// Create an array [tvOS, 13, 4]
const [osType, ...osVersionComponents] = runtimeSuffix.split('-');

if (osType === 'iOS') {
if (osType === 'iOS' || osType === 'tvOS') {
// Join the end components [13, 4] -> '13.4'
const osVersion = osVersionComponents.join('.');
const sims = info.devices[runtime];
Expand All @@ -56,8 +56,8 @@ export async function getAvailableIosSimulatorsListAsync(query?: string): Promis
...device,
runtime,
osVersion,
windowName: `${device.name} (${osVersion})`,
osType: 'iOS' as const,
windowName: `${device.osType} ${device.name} (${osVersion})`,
osType,
state: device.state as 'Booted' | 'Shutdown',
deviceType: 'simulator',
lastBootedAt: device.lastBootedAt ? new Date(device.lastBootedAt).getTime() : undefined,
Expand Down

0 comments on commit 6056712

Please sign in to comment.