import StarIcon from '@atlaskit/icon/glyph/star'; import StarFilledIcon from '@atlaskit/icon/glyph/star-filled'; import React, { Component } from 'react'; import { connect } from 'react-redux'; import { Dialog } from '../../base/dialog'; import { translate } from '../../base/i18n'; import JitsiMeetJS from '../../base/lib-jitsi-meet'; import { cancelFeedback, submitFeedback } from '../actions'; declare var interfaceConfig: Object; const scoreAnimationClass = interfaceConfig.ENABLE_FEEDBACK_ANIMATION ? 'shake-rotate' : ''; /** * The scores to display for selecting. The score is the index in the array and * the value of the index is a translation key used for display in the dialog. * * @types {string[]} */ const SCORES = [ 'feedback.veryBad', 'feedback.bad', 'feedback.average', 'feedback.good', 'feedback.veryGood' ]; /** * A React {@code Component} for displaying a dialog to rate the current * conference quality, write a message describing the experience, and submit * the feedback. * * @extends Component */ class FeedbackDialog extends Component { /** * {@code FeedbackDialog} component's property types. * * @static */ static propTypes = { /** * The cached feedback message, if any, that was set when closing a * previous instance of {@code FeedbackDialog}. */ _message: React.PropTypes.string, /** * The cached feedback score, if any, that was set when closing a * previous instance of {@code FeedbackDialog}. */ _score: React.PropTypes.number, /** * The JitsiConference that is being rated. The conference is passed in * because feedback can occur after a conference has been left, so * references to it may no longer exist in redux. * * @type {JitsiConference} */ conference: React.PropTypes.object, /** * Invoked to signal feedback submission or canceling. */ dispatch: React.PropTypes.func, /** * Callback invoked when {@code FeedbackDialog} is unmounted. */ onClose: React.PropTypes.func, /** * Invoked to obtain translated strings. */ t: React.PropTypes.func }; /** * Initializes a new {@code FeedbackDialog} instance. * * @param {Object} props - The read-only React {@code Component} props with * which the new instance is to be initialized. */ constructor(props) { super(props); const { _message, _score } = this.props; this.state = { /** * The currently entered feedback message. * * @type {string} */ message: _message, /** * The score selection index which is currently being hovered. The * value -1 is used as a sentinel value to match store behavior of * using -1 for no score having been selected. * * @type {number} */ mousedOverScore: -1, /** * The currently selected score selection index. The score will not * be 0 indexed so subtract one to map with SCORES. * * @type {number} */ score: _score > -1 ? _score - 1 : _score }; /** * An array of objects with click handlers for each of the scores listed * in SCORES. This pattern is used for binding event handlers only once * for each score selection icon. * * @type {Object[]} */ this._scoreClickConfigurations = SCORES.map((textKey, index) => { return { _onClick: () => this._onScoreSelect(index), _onMouseOver: () => this._onScoreMouseOver(index) }; }); // Bind event handlers so they are only bound once for every instance. this._onCancel = this._onCancel.bind(this); this._onMessageChange = this._onMessageChange.bind(this); this._onScoreContainerMouseLeave = this._onScoreContainerMouseLeave.bind(this); this._onSubmit = this._onSubmit.bind(this); } /** * Emits an analytics event to notify feedback has been opened. * * @inheritdoc */ componentDidMount() { JitsiMeetJS.analytics.sendEvent('feedback.open'); } /** * Invokes the onClose callback, if defined, to notify of the close event. * * @inheritdoc */ componentWillUnmount() { if (this.props.onClose) { this.props.onClose(); } } /** * Implements React's {@link Component#render()}. * * @inheritdoc * @returns {ReactElement} */ render() { const { message, mousedOverScore, score } = this.state; const scoreToDisplayAsSelected = mousedOverScore > -1 ? mousedOverScore : score; const scoreIcons = this._scoreClickConfigurations.map( (config, index) => { const isFilled = index <= scoreToDisplayAsSelected; const activeClass = isFilled ? 'active' : ''; const className = `star-btn ${scoreAnimationClass} ${activeClass}`; return ( { isFilled ? : } ); }); const { t } = this.props; return (

{ t(SCORES[scoreToDisplayAsSelected]) }

{ scoreIcons }