/** * Copyright (c) Facebook, Inc. and its affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * * @format */ import { FlexColumn, styled, Text, FlexRow, Glyph, LoadingIndicator, colors, Spacer, Button, FlexBox, Checkbox, } from 'flipper'; import React, {Component} from 'react'; import {connect} from 'react-redux'; import {State as Store} from '../reducers'; import { HealthcheckResult, HealthcheckReportCategory, HealthcheckReportItem, HealthcheckReport, startHealthchecks, finishHealthchecks, updateHealthcheckResult, acknowledgeProblems, resetAcknowledgedProblems, HealthcheckStatus, } from '../reducers/healthchecks'; import runHealthchecks, { HealthcheckSettings, HealthcheckEventsHandler, } from '../utils/runHealthchecks'; import {shell} from 'electron'; type StateFromProps = { healthcheckReport: HealthcheckReport; } & HealthcheckSettings; type DispatchFromProps = { acknowledgeProblems: () => void; resetAcknowledgedProblems: () => void; } & HealthcheckEventsHandler; const Container = styled(FlexColumn)({ padding: 20, width: 600, }); const HealthcheckDisplayContainer = styled(FlexRow)({ alignItems: 'center', marginBottom: 5, }); const HealthcheckListContainer = styled(FlexColumn)({ marginBottom: 20, width: 300, }); const Title = styled(Text)({ marginBottom: 18, marginRight: 10, fontWeight: 100, fontSize: '40px', }); const CategoryContainer = styled(FlexColumn)({ marginBottom: 5, marginLeft: 20, marginRight: 20, }); const SideContainer = styled(FlexBox)({ marginBottom: 20, padding: 20, backgroundColor: colors.highlightBackground, border: '1px solid #b3b3b3', width: 250, }); const SideContainerText = styled(Text)({ display: 'block', wordWrap: 'break-word', }); const HealthcheckLabel = styled(Text)({ paddingLeft: 5, }); const SkipReasonLabel = styled(Text)({ paddingLeft: 21, fontStyle: 'italic', }); const CenteredContainer = styled.label({ display: 'flex', alignItems: 'center', }); type OwnProps = { onHide: () => void; }; function CenteredCheckbox(props: { checked: boolean; text: string; onChange: (checked: boolean) => void; }) { const {checked, onChange, text} = props; return ( {text} ); } function HealthcheckIcon(props: {check: HealthcheckResult}) { switch (props.check.status) { case 'IN_PROGRESS': return ; case 'SKIPPED': return ( ); case 'SUCCESS': return ( ); case 'FAILED': return ( ); case 'FAILED_ACKNOWLEDGED': return ( ); default: return ( ); } } function HealthcheckDisplay(props: { category: HealthcheckReportCategory; check: HealthcheckReportItem; onClick?: () => void; }) { return ( {props.check.label} ); } function SideMessageDisplay(props: { isHealthcheckInProgress: boolean; hasProblems: boolean; }) { if (props.isHealthcheckInProgress) { return ( Doctor is running healthchecks... ); } else if (props.hasProblems) { return ( Doctor has discovered problems with your installation. ); } else { return ( All good! Doctor has not discovered any issues with your installation. ); } } function hasProblems(status: HealthcheckStatus) { return ( status === 'FAILED' || status === 'FAILED_ACKNOWLEDGED' || status === 'WARNING' ); } function hasFailedChecks(status: HealthcheckStatus) { return status === 'FAILED' || status === 'FAILED_ACKNOWLEDGED'; } function hasNewFailedChecks(status: HealthcheckStatus) { return status === 'FAILED'; } export type State = { acknowledgeCheckboxVisible: boolean; acknowledgeOnClose?: boolean; }; type Props = OwnProps & StateFromProps & DispatchFromProps; class DoctorSheet extends Component { constructor(props: Props) { super(props); this.state = { acknowledgeCheckboxVisible: false, }; } static getDerivedStateFromProps(props: Props, state: State): State | null { if ( !state.acknowledgeCheckboxVisible && hasFailedChecks(props.healthcheckReport.status) ) { return { ...state, acknowledgeCheckboxVisible: true, acknowledgeOnClose: state.acknowledgeOnClose === undefined ? !hasNewFailedChecks(props.healthcheckReport.status) : state.acknowledgeOnClose, }; } if ( state.acknowledgeCheckboxVisible && !hasFailedChecks(props.healthcheckReport.status) ) { return { ...state, acknowledgeCheckboxVisible: false, }; } return null; } componentWillUnmount(): void { if (this.state.acknowledgeOnClose) { this.props.acknowledgeProblems(); } else { this.props.resetAcknowledgedProblems(); } } onAcknowledgeOnCloseChanged(acknowledge: boolean): void { this.setState(prevState => { return { ...prevState, acknowledgeOnClose: acknowledge, }; }); } openHelpUrl(check: HealthcheckReportItem): void { check.helpUrl && shell.openExternal(check.helpUrl); } async runHealthchecks() { await runHealthchecks(this.props); } render() { return ( Doctor {Object.values(this.props.healthcheckReport.categories).map( (category: HealthcheckReportCategory, categoryIdx: number) => { return ( {category.status !== 'SKIPPED' && ( {category.checks.map((check, checkIdx) => ( this.openHelpUrl(check) : undefined } /> ))} )} {category.status === 'SKIPPED' && ( {category.message} )} ); }, )} {this.state.acknowledgeCheckboxVisible && ( )} ); } } export default connect( ({healthchecks: {healthcheckReport}, settingsState}) => ({ healthcheckReport, enableAndroid: settingsState.enableAndroid, }), { startHealthchecks, finishHealthchecks, updateHealthcheckResult, acknowledgeProblems, resetAcknowledgedProblems, }, )(DoctorSheet);