Allow disabling iOS development in Settings

Summary:
Currently Android development can be disabled in Settings, but iOS development not. Because of this Doctor always shows warnings to Android-only developers who has no iOS SDK installed. This change makes it possible to disable "iOS development" option in the same way as we already had for Android.

Additionally I changed Doctor warning to show more specific message if only iOS or only Android checks are failed with suggestion to disable the failing platform if it is not required.

Reviewed By: jknoxville

Differential Revision: D19538070

fbshipit-source-id: 234d2c6cf21083f9d6aebd63418aed7f9a78e922
This commit is contained in:
Anton Nikolaev
2020-01-23 13:35:12 -08:00
committed by Facebook Github Bot
parent b625efee3d
commit aab004aa15
7 changed files with 109 additions and 27 deletions

View File

@@ -14,6 +14,7 @@ import {
setActiveSheet,
ActiveSheet,
ACTIVE_SHEET_DOCTOR,
ACTIVE_SHEET_SETTINGS,
} from '../reducers/application';
import {State as Store} from '../reducers/index';
import {ButtonGroup, Button} from 'flipper';
@@ -23,23 +24,24 @@ import runHealthchecks, {
HealthcheckEventsHandler,
} from '../utils/runHealthchecks';
import {
HealthcheckResult,
updateHealthcheckResult,
startHealthchecks,
finishHealthchecks,
HealthcheckReport,
HealthcheckResult,
} from '../reducers/healthchecks';
import {reportUsage} from '../utils/metrics';
type StateFromProps = {
healthcheckResult: HealthcheckResult;
healthcheckReport: HealthcheckReport;
} & HealthcheckSettings;
type DispatchFromProps = {
setActiveSheet: (payload: ActiveSheet) => void;
} & HealthcheckEventsHandler;
type State = {visible: boolean};
type State = {visible: boolean; message: string; showSettingsButton: boolean};
type Props = DispatchFromProps & StateFromProps;
class DoctorBar extends Component<Props, State> {
@@ -47,18 +49,41 @@ class DoctorBar extends Component<Props, State> {
super(props);
this.state = {
visible: false,
message: '',
showSettingsButton: false,
};
}
componentDidMount() {
this.showMessageIfChecksFailed();
}
static getDerivedStateFromProps(props: Props, state: State): State | null {
const failedCategories = Object.values(
props.healthcheckReport.categories,
).filter(cat => hasProblems(cat.result));
if (failedCategories.length == 1) {
const failedCat = failedCategories[0];
if (failedCat.key === 'ios' || failedCat.key === 'android') {
return {
...state,
message: `Doctor has discovered problems with your ${failedCat.label} setup. If you are not interested in ${failedCat.label} development you can disable it in Settings.`,
showSettingsButton: true,
};
}
}
if (failedCategories.length) {
return {
...state,
message: 'Doctor has discovered problems with your installation.',
showSettingsButton: false,
};
}
return null;
}
async showMessageIfChecksFailed() {
await runHealthchecks(this.props);
if (
this.props.healthcheckResult.status === 'FAILED' ||
this.props.healthcheckResult.status === 'WARNING'
) {
if (this.props.healthcheckResult.isAcknowledged) {
const result = this.props.healthcheckReport.result;
if (hasProblems(result)) {
if (result.isAcknowledged) {
reportUsage('doctor:warning:suppressed');
} else {
this.setVisible(true);
@@ -76,18 +101,28 @@ class DoctorBar extends Component<Props, State> {
<ButtonGroup>
<Button
onClick={() => {
reportUsage('doctor:report:opened:fromWarningBar');
this.props.setActiveSheet(ACTIVE_SHEET_DOCTOR);
this.setVisible(false);
}}>
Show Problems
</Button>
{this.state.showSettingsButton && (
<Button
onClick={() => {
reportUsage('settings:opened:fromWarningBar');
this.props.setActiveSheet(ACTIVE_SHEET_SETTINGS);
}}>
Show Settings
</Button>
)}
<Button onClick={() => this.setVisible(false)}>
Dismiss
</Button>
</ButtonGroup>
</ButtonSection>
<FlexColumn style={{flexGrow: 1}}>
Doctor has discovered problems with your installation
{this.state.message}
</FlexColumn>
</FlexRow>
</WarningContainer>
@@ -107,13 +142,12 @@ class DoctorBar extends Component<Props, State> {
export default connect<StateFromProps, DispatchFromProps, {}, Store>(
({
settingsState: {enableAndroid},
healthchecks: {
healthcheckReport: {result},
},
settingsState: {enableAndroid, enableIOS},
healthchecks: {healthcheckReport},
}) => ({
enableAndroid,
healthcheckResult: result,
enableIOS,
healthcheckReport,
}),
{
setActiveSheet,
@@ -149,3 +183,7 @@ const ButtonSection = styled(FlexColumn)({
flexShrink: 0,
flexGrow: 0,
});
function hasProblems(result: HealthcheckResult) {
return result.status === 'WARNING' || result.status === 'FAILED';
}

View File

@@ -201,7 +201,7 @@ function ResultMessage(props: {result: HealthcheckResult}) {
return (
<p>
Doctor has discovered problems with your installation. Please click to
each item to get details.
an item to get its details.
</p>
);
} else {
@@ -404,9 +404,13 @@ class DoctorSheet extends Component<Props, State> {
}
export default connect<StateFromProps, DispatchFromProps, OwnProps, Store>(
({healthchecks: {healthcheckReport}, settingsState}) => ({
({
healthchecks: {healthcheckReport},
settingsState: {enableAndroid, enableIOS},
}) => ({
healthcheckReport,
enableAndroid: settingsState.enableAndroid,
enableAndroid,
enableIOS,
}),
{
startHealthchecks,

View File

@@ -24,6 +24,10 @@ import {FilePathConfigField, ConfigText} from './settings/configFields';
import isEqual from 'lodash.isequal';
import restartFlipper from '../utils/restartFlipper';
import LauncherSettingsPanel from '../fb-stubs/LauncherSettingsPanel';
import {reportUsage} from '../utils/metrics';
import os from 'os';
const platform = os.platform();
const Container = styled(FlexColumn)({
padding: 20,
@@ -64,6 +68,10 @@ class SettingsSheet extends Component<Props, State> {
updatedLauncherSettings: {...this.props.launcherSettings},
};
componentDidMount() {
reportUsage('settings:opened');
}
applyChanges = async () => {
this.props.updateSettings(this.state.updatedSettings);
this.props.updateLauncherSettings(this.state.updatedLauncherSettings);
@@ -104,15 +112,26 @@ class SettingsSheet extends Component<Props, State> {
</ToggledSection>
<ToggledSection
label="iOS Developer"
toggled={this.props.isXcodeDetected}
frozen>
toggled={
this.state.updatedSettings.enableIOS && platform === 'darwin'
}
frozen={platform !== 'darwin'}
onChange={v => {
this.setState({
updatedSettings: {...this.state.updatedSettings, enableIOS: v},
});
}}>
{' '}
<ConfigText
content={
'Use xcode-select to enable or switch between xcode versions'
}
frozen
/>
{platform === 'darwin' && (
<ConfigText
content={'Use "xcode-select" to switch between Xcode versions'}
/>
)}
{platform !== 'darwin' && (
<ConfigText
content={'iOS development is only supported on MacOS'}
/>
)}
</ToggledSection>
<LauncherSettingsPanel
isPrefetchingEnabled={this.state.updatedSettings.enablePrefetching}

View File

@@ -42,6 +42,7 @@ import isProduction from '../utils/isProduction';
import {clipboard} from 'electron';
import React from 'react';
import {State} from 'src/reducers';
import {reportUsage} from '../utils/metrics';
const AppTitleBar = styled(FlexRow)<{focused?: boolean}>(({focused}) => ({
background: focused
@@ -175,7 +176,10 @@ class TitleBar extends React.Component<Props, StateFromProps> {
icon="settings"
title="Settings"
compact={true}
onClick={() => this.props.setActiveSheet(ACTIVE_SHEET_SETTINGS)}
onClick={() => {
this.props.setActiveSheet(ACTIVE_SHEET_SETTINGS);
reportUsage('settings:opened:fromTitleBar');
}}
/>
{config.bugReportButtonVisible && (
<Button
@@ -189,7 +193,10 @@ class TitleBar extends React.Component<Props, StateFromProps> {
icon="first-aid"
title="Doctor"
compact={true}
onClick={() => this.props.setActiveSheet(ACTIVE_SHEET_DOCTOR)}
onClick={() => {
this.props.setActiveSheet(ACTIVE_SHEET_DOCTOR);
reportUsage('doctor:report:opened:fromTitleBar');
}}
/>
<ButtonGroup>
<Button

View File

@@ -255,6 +255,9 @@ export default (store: Store, logger: Logger) => {
if (process.platform !== 'darwin') {
return;
}
if (!store.getState().settingsState.enableIOS) {
return;
}
isXcodeDetected()
.then(isDetected => {
store.dispatch(setXcodeDetected(isDetected));

View File

@@ -20,6 +20,7 @@ export enum Tristate {
export type Settings = {
androidHome: string;
enableAndroid: boolean;
enableIOS: boolean;
/**
* If unset, this will assume the value of the GK setting.
* Note that this setting has no effect in the open source version
@@ -47,6 +48,7 @@ export const DEFAULT_ANDROID_SDK_PATH = getDefaultAndroidSdkPath();
const initialState: Settings = {
androidHome: getDefaultAndroidSdkPath(),
enableAndroid: true,
enableIOS: os.platform() === 'darwin',
enablePrefetching: Tristate.Unset,
jsApps: {
webAppLauncher: {

View File

@@ -26,6 +26,7 @@ export type HealthcheckEventsHandler = {
export type HealthcheckSettings = {
enableAndroid: boolean;
enableIOS: boolean;
};
export type HealthcheckOptions = HealthcheckEventsHandler & HealthcheckSettings;
@@ -40,6 +41,14 @@ async function launchHealthchecks(options: HealthcheckOptions): Promise<void> {
'Healthcheck is skipped, because "Android Development" option is disabled in the Flipper settings',
};
}
if (!options.enableIOS) {
healthchecks.ios = {
label: healthchecks.ios.label,
isSkipped: true,
skipReason:
'Healthcheck is skipped, because "iOS Development" option is disabled in the Flipper settings',
};
}
options.startHealthchecks(healthchecks);
const environmentInfo = await getEnvInfo();
let hasProblems = false;