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:
committed by
Facebook Github Bot
parent
b625efee3d
commit
aab004aa15
@@ -14,6 +14,7 @@ import {
|
|||||||
setActiveSheet,
|
setActiveSheet,
|
||||||
ActiveSheet,
|
ActiveSheet,
|
||||||
ACTIVE_SHEET_DOCTOR,
|
ACTIVE_SHEET_DOCTOR,
|
||||||
|
ACTIVE_SHEET_SETTINGS,
|
||||||
} from '../reducers/application';
|
} from '../reducers/application';
|
||||||
import {State as Store} from '../reducers/index';
|
import {State as Store} from '../reducers/index';
|
||||||
import {ButtonGroup, Button} from 'flipper';
|
import {ButtonGroup, Button} from 'flipper';
|
||||||
@@ -23,23 +24,24 @@ import runHealthchecks, {
|
|||||||
HealthcheckEventsHandler,
|
HealthcheckEventsHandler,
|
||||||
} from '../utils/runHealthchecks';
|
} from '../utils/runHealthchecks';
|
||||||
import {
|
import {
|
||||||
HealthcheckResult,
|
|
||||||
updateHealthcheckResult,
|
updateHealthcheckResult,
|
||||||
startHealthchecks,
|
startHealthchecks,
|
||||||
finishHealthchecks,
|
finishHealthchecks,
|
||||||
|
HealthcheckReport,
|
||||||
|
HealthcheckResult,
|
||||||
} from '../reducers/healthchecks';
|
} from '../reducers/healthchecks';
|
||||||
|
|
||||||
import {reportUsage} from '../utils/metrics';
|
import {reportUsage} from '../utils/metrics';
|
||||||
|
|
||||||
type StateFromProps = {
|
type StateFromProps = {
|
||||||
healthcheckResult: HealthcheckResult;
|
healthcheckReport: HealthcheckReport;
|
||||||
} & HealthcheckSettings;
|
} & HealthcheckSettings;
|
||||||
|
|
||||||
type DispatchFromProps = {
|
type DispatchFromProps = {
|
||||||
setActiveSheet: (payload: ActiveSheet) => void;
|
setActiveSheet: (payload: ActiveSheet) => void;
|
||||||
} & HealthcheckEventsHandler;
|
} & HealthcheckEventsHandler;
|
||||||
|
|
||||||
type State = {visible: boolean};
|
type State = {visible: boolean; message: string; showSettingsButton: boolean};
|
||||||
|
|
||||||
type Props = DispatchFromProps & StateFromProps;
|
type Props = DispatchFromProps & StateFromProps;
|
||||||
class DoctorBar extends Component<Props, State> {
|
class DoctorBar extends Component<Props, State> {
|
||||||
@@ -47,18 +49,41 @@ class DoctorBar extends Component<Props, State> {
|
|||||||
super(props);
|
super(props);
|
||||||
this.state = {
|
this.state = {
|
||||||
visible: false,
|
visible: false,
|
||||||
|
message: '',
|
||||||
|
showSettingsButton: false,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
this.showMessageIfChecksFailed();
|
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() {
|
async showMessageIfChecksFailed() {
|
||||||
await runHealthchecks(this.props);
|
await runHealthchecks(this.props);
|
||||||
if (
|
const result = this.props.healthcheckReport.result;
|
||||||
this.props.healthcheckResult.status === 'FAILED' ||
|
if (hasProblems(result)) {
|
||||||
this.props.healthcheckResult.status === 'WARNING'
|
if (result.isAcknowledged) {
|
||||||
) {
|
|
||||||
if (this.props.healthcheckResult.isAcknowledged) {
|
|
||||||
reportUsage('doctor:warning:suppressed');
|
reportUsage('doctor:warning:suppressed');
|
||||||
} else {
|
} else {
|
||||||
this.setVisible(true);
|
this.setVisible(true);
|
||||||
@@ -76,18 +101,28 @@ class DoctorBar extends Component<Props, State> {
|
|||||||
<ButtonGroup>
|
<ButtonGroup>
|
||||||
<Button
|
<Button
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
|
reportUsage('doctor:report:opened:fromWarningBar');
|
||||||
this.props.setActiveSheet(ACTIVE_SHEET_DOCTOR);
|
this.props.setActiveSheet(ACTIVE_SHEET_DOCTOR);
|
||||||
this.setVisible(false);
|
this.setVisible(false);
|
||||||
}}>
|
}}>
|
||||||
Show Problems
|
Show Problems
|
||||||
</Button>
|
</Button>
|
||||||
|
{this.state.showSettingsButton && (
|
||||||
|
<Button
|
||||||
|
onClick={() => {
|
||||||
|
reportUsage('settings:opened:fromWarningBar');
|
||||||
|
this.props.setActiveSheet(ACTIVE_SHEET_SETTINGS);
|
||||||
|
}}>
|
||||||
|
Show Settings
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
<Button onClick={() => this.setVisible(false)}>
|
<Button onClick={() => this.setVisible(false)}>
|
||||||
Dismiss
|
Dismiss
|
||||||
</Button>
|
</Button>
|
||||||
</ButtonGroup>
|
</ButtonGroup>
|
||||||
</ButtonSection>
|
</ButtonSection>
|
||||||
<FlexColumn style={{flexGrow: 1}}>
|
<FlexColumn style={{flexGrow: 1}}>
|
||||||
Doctor has discovered problems with your installation
|
{this.state.message}
|
||||||
</FlexColumn>
|
</FlexColumn>
|
||||||
</FlexRow>
|
</FlexRow>
|
||||||
</WarningContainer>
|
</WarningContainer>
|
||||||
@@ -107,13 +142,12 @@ class DoctorBar extends Component<Props, State> {
|
|||||||
|
|
||||||
export default connect<StateFromProps, DispatchFromProps, {}, Store>(
|
export default connect<StateFromProps, DispatchFromProps, {}, Store>(
|
||||||
({
|
({
|
||||||
settingsState: {enableAndroid},
|
settingsState: {enableAndroid, enableIOS},
|
||||||
healthchecks: {
|
healthchecks: {healthcheckReport},
|
||||||
healthcheckReport: {result},
|
|
||||||
},
|
|
||||||
}) => ({
|
}) => ({
|
||||||
enableAndroid,
|
enableAndroid,
|
||||||
healthcheckResult: result,
|
enableIOS,
|
||||||
|
healthcheckReport,
|
||||||
}),
|
}),
|
||||||
{
|
{
|
||||||
setActiveSheet,
|
setActiveSheet,
|
||||||
@@ -149,3 +183,7 @@ const ButtonSection = styled(FlexColumn)({
|
|||||||
flexShrink: 0,
|
flexShrink: 0,
|
||||||
flexGrow: 0,
|
flexGrow: 0,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function hasProblems(result: HealthcheckResult) {
|
||||||
|
return result.status === 'WARNING' || result.status === 'FAILED';
|
||||||
|
}
|
||||||
|
|||||||
@@ -201,7 +201,7 @@ function ResultMessage(props: {result: HealthcheckResult}) {
|
|||||||
return (
|
return (
|
||||||
<p>
|
<p>
|
||||||
Doctor has discovered problems with your installation. Please click to
|
Doctor has discovered problems with your installation. Please click to
|
||||||
each item to get details.
|
an item to get its details.
|
||||||
</p>
|
</p>
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
@@ -404,9 +404,13 @@ class DoctorSheet extends Component<Props, State> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default connect<StateFromProps, DispatchFromProps, OwnProps, Store>(
|
export default connect<StateFromProps, DispatchFromProps, OwnProps, Store>(
|
||||||
({healthchecks: {healthcheckReport}, settingsState}) => ({
|
({
|
||||||
|
healthchecks: {healthcheckReport},
|
||||||
|
settingsState: {enableAndroid, enableIOS},
|
||||||
|
}) => ({
|
||||||
healthcheckReport,
|
healthcheckReport,
|
||||||
enableAndroid: settingsState.enableAndroid,
|
enableAndroid,
|
||||||
|
enableIOS,
|
||||||
}),
|
}),
|
||||||
{
|
{
|
||||||
startHealthchecks,
|
startHealthchecks,
|
||||||
|
|||||||
@@ -24,6 +24,10 @@ import {FilePathConfigField, ConfigText} from './settings/configFields';
|
|||||||
import isEqual from 'lodash.isequal';
|
import isEqual from 'lodash.isequal';
|
||||||
import restartFlipper from '../utils/restartFlipper';
|
import restartFlipper from '../utils/restartFlipper';
|
||||||
import LauncherSettingsPanel from '../fb-stubs/LauncherSettingsPanel';
|
import LauncherSettingsPanel from '../fb-stubs/LauncherSettingsPanel';
|
||||||
|
import {reportUsage} from '../utils/metrics';
|
||||||
|
import os from 'os';
|
||||||
|
|
||||||
|
const platform = os.platform();
|
||||||
|
|
||||||
const Container = styled(FlexColumn)({
|
const Container = styled(FlexColumn)({
|
||||||
padding: 20,
|
padding: 20,
|
||||||
@@ -64,6 +68,10 @@ class SettingsSheet extends Component<Props, State> {
|
|||||||
updatedLauncherSettings: {...this.props.launcherSettings},
|
updatedLauncherSettings: {...this.props.launcherSettings},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
reportUsage('settings:opened');
|
||||||
|
}
|
||||||
|
|
||||||
applyChanges = async () => {
|
applyChanges = async () => {
|
||||||
this.props.updateSettings(this.state.updatedSettings);
|
this.props.updateSettings(this.state.updatedSettings);
|
||||||
this.props.updateLauncherSettings(this.state.updatedLauncherSettings);
|
this.props.updateLauncherSettings(this.state.updatedLauncherSettings);
|
||||||
@@ -104,15 +112,26 @@ class SettingsSheet extends Component<Props, State> {
|
|||||||
</ToggledSection>
|
</ToggledSection>
|
||||||
<ToggledSection
|
<ToggledSection
|
||||||
label="iOS Developer"
|
label="iOS Developer"
|
||||||
toggled={this.props.isXcodeDetected}
|
toggled={
|
||||||
frozen>
|
this.state.updatedSettings.enableIOS && platform === 'darwin'
|
||||||
|
}
|
||||||
|
frozen={platform !== 'darwin'}
|
||||||
|
onChange={v => {
|
||||||
|
this.setState({
|
||||||
|
updatedSettings: {...this.state.updatedSettings, enableIOS: v},
|
||||||
|
});
|
||||||
|
}}>
|
||||||
{' '}
|
{' '}
|
||||||
<ConfigText
|
{platform === 'darwin' && (
|
||||||
content={
|
<ConfigText
|
||||||
'Use xcode-select to enable or switch between xcode versions'
|
content={'Use "xcode-select" to switch between Xcode versions'}
|
||||||
}
|
/>
|
||||||
frozen
|
)}
|
||||||
/>
|
{platform !== 'darwin' && (
|
||||||
|
<ConfigText
|
||||||
|
content={'iOS development is only supported on MacOS'}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</ToggledSection>
|
</ToggledSection>
|
||||||
<LauncherSettingsPanel
|
<LauncherSettingsPanel
|
||||||
isPrefetchingEnabled={this.state.updatedSettings.enablePrefetching}
|
isPrefetchingEnabled={this.state.updatedSettings.enablePrefetching}
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ import isProduction from '../utils/isProduction';
|
|||||||
import {clipboard} from 'electron';
|
import {clipboard} from 'electron';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import {State} from 'src/reducers';
|
import {State} from 'src/reducers';
|
||||||
|
import {reportUsage} from '../utils/metrics';
|
||||||
|
|
||||||
const AppTitleBar = styled(FlexRow)<{focused?: boolean}>(({focused}) => ({
|
const AppTitleBar = styled(FlexRow)<{focused?: boolean}>(({focused}) => ({
|
||||||
background: focused
|
background: focused
|
||||||
@@ -175,7 +176,10 @@ class TitleBar extends React.Component<Props, StateFromProps> {
|
|||||||
icon="settings"
|
icon="settings"
|
||||||
title="Settings"
|
title="Settings"
|
||||||
compact={true}
|
compact={true}
|
||||||
onClick={() => this.props.setActiveSheet(ACTIVE_SHEET_SETTINGS)}
|
onClick={() => {
|
||||||
|
this.props.setActiveSheet(ACTIVE_SHEET_SETTINGS);
|
||||||
|
reportUsage('settings:opened:fromTitleBar');
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
{config.bugReportButtonVisible && (
|
{config.bugReportButtonVisible && (
|
||||||
<Button
|
<Button
|
||||||
@@ -189,7 +193,10 @@ class TitleBar extends React.Component<Props, StateFromProps> {
|
|||||||
icon="first-aid"
|
icon="first-aid"
|
||||||
title="Doctor"
|
title="Doctor"
|
||||||
compact={true}
|
compact={true}
|
||||||
onClick={() => this.props.setActiveSheet(ACTIVE_SHEET_DOCTOR)}
|
onClick={() => {
|
||||||
|
this.props.setActiveSheet(ACTIVE_SHEET_DOCTOR);
|
||||||
|
reportUsage('doctor:report:opened:fromTitleBar');
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
<ButtonGroup>
|
<ButtonGroup>
|
||||||
<Button
|
<Button
|
||||||
|
|||||||
@@ -255,6 +255,9 @@ export default (store: Store, logger: Logger) => {
|
|||||||
if (process.platform !== 'darwin') {
|
if (process.platform !== 'darwin') {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (!store.getState().settingsState.enableIOS) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
isXcodeDetected()
|
isXcodeDetected()
|
||||||
.then(isDetected => {
|
.then(isDetected => {
|
||||||
store.dispatch(setXcodeDetected(isDetected));
|
store.dispatch(setXcodeDetected(isDetected));
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ export enum Tristate {
|
|||||||
export type Settings = {
|
export type Settings = {
|
||||||
androidHome: string;
|
androidHome: string;
|
||||||
enableAndroid: boolean;
|
enableAndroid: boolean;
|
||||||
|
enableIOS: boolean;
|
||||||
/**
|
/**
|
||||||
* If unset, this will assume the value of the GK setting.
|
* If unset, this will assume the value of the GK setting.
|
||||||
* Note that this setting has no effect in the open source version
|
* 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 = {
|
const initialState: Settings = {
|
||||||
androidHome: getDefaultAndroidSdkPath(),
|
androidHome: getDefaultAndroidSdkPath(),
|
||||||
enableAndroid: true,
|
enableAndroid: true,
|
||||||
|
enableIOS: os.platform() === 'darwin',
|
||||||
enablePrefetching: Tristate.Unset,
|
enablePrefetching: Tristate.Unset,
|
||||||
jsApps: {
|
jsApps: {
|
||||||
webAppLauncher: {
|
webAppLauncher: {
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ export type HealthcheckEventsHandler = {
|
|||||||
|
|
||||||
export type HealthcheckSettings = {
|
export type HealthcheckSettings = {
|
||||||
enableAndroid: boolean;
|
enableAndroid: boolean;
|
||||||
|
enableIOS: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type HealthcheckOptions = HealthcheckEventsHandler & HealthcheckSettings;
|
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',
|
'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);
|
options.startHealthchecks(healthchecks);
|
||||||
const environmentInfo = await getEnvInfo();
|
const environmentInfo = await getEnvInfo();
|
||||||
let hasProblems = false;
|
let hasProblems = false;
|
||||||
|
|||||||
Reference in New Issue
Block a user