/** * Copyright 2018-present Facebook. * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * @format */ import type BugReporter from '../fb-stubs/BugReporter.js'; import type {FlipperDevicePlugin, FlipperPlugin} from '../plugin'; import {toggleBugDialogVisible} from '../reducers/application.js'; import {Component} from 'react'; import {Transition} from 'react-transition-group'; import {connect} from 'react-redux'; import { Button, colors, Link, Input, FlexColumn, FlexRow, Textarea, Text, Glyph, FlexCenter, styled, } from 'flipper'; const Container = styled(FlexColumn)({ padding: 10, }); const Icon = styled(Glyph)({ marginRight: 8, marginLeft: 3, }); const Center = styled(Text)({ textAlign: 'center', lineHeight: '130%', paddingLeft: 20, paddingRight: 20, }); const Title = styled('div')({ fontWeight: '500', marginTop: 8, marginLeft: 2, marginBottom: 8, }); const textareaStyle = { margin: 0, marginBottom: 10, }; const DialogContainer = styled('div')(({state}) => ({ transform: `translateY(${ state === 'entering' || state === 'exiting' ? '-110' : '' }%)`, transition: '.3s transform', width: 400, height: 300, position: 'absolute', left: '50%', marginLeft: -200, top: 38, zIndex: 2, backgroundColor: '#EFEEEF', border: '1px solid #C6C6C6', borderTop: 'none', borderBottomLeftRadius: 2, borderBottomRightRadius: 2, boxShadow: '0 5px 13px rgba(0, 0, 0, 0.2)', })); const TitleInput = styled(Input)({ ...textareaStyle, height: 30, }); const DescriptionTextarea = styled(Textarea)({ ...textareaStyle, flexGrow: 1, }); const SubmitButtonContainer = styled('div')({ marginLeft: 'auto', }); const Footer = styled(FlexRow)({ lineHeight: '24px', }); const CloseDoneButton = styled(Button)({ marginTop: 20, marginLeft: 'auto !important', marginRight: 'auto', }); const InfoBox = styled(FlexRow)({ marginBottom: 20, lineHeight: '130%', }); type State = {| description: string, title: string, submitting: boolean, success: ?number, error: ?string, |}; type Props = {| bugReporter: BugReporter, toggleBugDialogVisible: (visible: boolean) => mixed, activePlugin: ?Class | FlipperDevicePlugin<>>, bugDialogVisible: boolean, |}; class BugReporterDialog extends Component { state = { description: '', title: '', submitting: false, success: null, error: null, }; titleRef: HTMLElement; descriptionRef: HTMLElement; componentDidMount() { document.addEventListener('keydown', this.onKeyDown); } componentWillUnmount() { document.removeEventListener('keydown', this.onKeyDown); } onKeyDown = (e: KeyboardEvent) => { if (this.props.bugDialogVisible && e.key === 'Escape') { this.onCancel(); } }; onDescriptionChange = (e: SyntheticInputEvent) => { this.setState({description: e.target.value}); }; onTitleChange = (e: SyntheticInputEvent) => { this.setState({title: e.target.value}); }; onSubmit = () => { // validate fields const {title, description} = this.state; if (!title) { this.setState({ error: 'Title required.', }); this.titleRef.focus(); return; } if (!description) { this.setState({ error: 'Description required.', }); this.descriptionRef.focus(); return; } this.setState( { error: null, submitting: true, }, () => { // this will be called before the next repaint requestAnimationFrame(() => { // we have to call this again to ensure a repaint has actually happened // as requestAnimationFrame is called BEFORE a repaint, not after which // means we have to queue up twice to actually ensure a repaint has // happened requestAnimationFrame(() => { this.props.bugReporter .report(title, description) .then((id: number) => { this.setState({ submitting: false, success: id, }); }) .catch(err => { this.setState({ error: err.message, submitting: false, }); }); }); }); }, ); }; setTitleRef = (ref: HTMLElement) => { this.titleRef = ref; }; setDescriptionRef = (ref: HTMLElement) => { this.descriptionRef = ref; }; onCancel = () => { this.setState({ error: null, title: '', description: '', }); this.props.toggleBugDialogVisible(false); }; render() { let content; const {title, success, error, description, submitting} = this.state; if (success) { content = (

Bug Report created The bug report{' '} {success} {' '} was successfully created. Thank you for your help making Flipper better!
Close
); } else { content = ( Report a bug... {this.props.activePlugin?.bugs && ( If you bug is related to the{' '} {this.props.activePlugin?.title || this.props.activePlugin?.id}{' '} plugin {this.props.activePlugin?.bugs?.url && ( , you might find useful information about it here:{' '} {this.props.activePlugin?.bugs?.url} )} {this.props.activePlugin?.bugs?.email && ( , you might also want contact{' '} {this.props.activePlugin?.bugs?.email} , the author/oncall of this plugin, directly )} . )}
{error != null && {error}}
); } return ( {state => {content}} ); } } // $FlowFixMe export default connect( ({ plugins: {devicePlugins, clientPlugins}, connections: {selectedPlugin}, application: {bugDialogVisible}, }) => ({ bugDialogVisible, activePlugin: devicePlugins.get(selectedPlugin) || clientPlugins.get(selectedPlugin), }), { toggleBugDialogVisible, }, )(BugReporterDialog);