{"version":3,"sources":["webpack:///./src/components/terms-modal.tsx","webpack:///./src/components/pages/contribution/speak/audio-web.ts","webpack:///../node_modules/react-router-navigation-prompt/es/index.js","webpack:///./src/components/pages/contribution/speak/recording-pill.tsx","webpack:///./src/components/pages/contribution/speak/speak.tsx"],"names":["withLocalization","getString","onAgree","onDisagree","React","Localized","id","elems","termsLink","LocaleLink","to","URLS","TERMS","blank","privacyLink","PRIVACY","Modal","buttons","AudioError","AudioWeb","constructor","this","recorderListeners","start","dataavailable","stop","analyze","bind","isReady","microphone","getMicrophone","Promise","res","rej","deny","error","NotAllowedError","NOT_ALLOWED","NotFoundError","NO_MIC","name","resolve","stream","navigator","mediaDevices","_a","getUserMedia","audio","then","webkitGetUserMedia","mozGetUserMedia","NO_SUPPORT","isMicrophoneSupported","isAudioRecordingSupported","window","MediaRecorder","notSupported","analyzerNode","getByteFrequencyData","frequencyBins","volumeCallback","Math","max","setVolumeCallback","cb","init","audioContext","AudioContext","webkitAudioContext","sourceNode","createMediaStreamSource","volumeNode","createGain","createAnalyser","outputNode","createMediaStreamDestination","channelCount","connect","recorder","fftSize","smoothingTimeConstant","Uint8Array","frequencyBinCount","jsNode","createScriptProcessor","destination","chunks","removeEventListener","e","push","data","addEventListener","onaudioprocess","console","undefined","blob","Blob","type","getAudioFormat","url","URL","createObjectURL","reject","release","track","getTracks","module","exports","modules","installedModules","__webpack_require__","moduleId","i","l","call","m","c","d","getter","o","Object","defineProperty","configurable","enumerable","get","n","__esModule","object","property","prototype","hasOwnProperty","p","s","value","obj","_extends","assign","target","arguments","length","source","key","_createClass","defineProperties","props","descriptor","writable","Constructor","protoProps","staticProps","_react","_react2","default","_reactRouterDom","initState","action","isActive","nextLocation","NavigationPrompt","_React$Component","instance","TypeError","_classCallCheck","_this","self","ReferenceError","_possibleConstructorReturn","__proto__","getPrototypeOf","_prevUserAction","_isMounted","block","onBeforeUnload","onCancel","onConfirm","when","state","unblock","subClass","superClass","create","setPrototypeOf","_inherits","disableNative","setState","history","prevProps","prevState","afterCancel","afterConfirm","result","_this2","_state","allowGoBack","pathname","unlisten","listen","goBack","_this3","beforeCancel","_this4","beforeConfirm","navigateToNextLocation","msg","returnValue","location","renderIfNotActive","createElement","children","Component","withRouter","require","Tooltip","clip","onRerecord","onShare","status","locale","useLocale","isPlaying","setIsPlaying","useState","showSentenceTooltip","setShowSentenceTooltip","audioRef","useRef","toggleIsPlaying","current","nextIsPlaying","trackRecording","play","pause","currentTime","Pill","className","openable","preload","onEnded","ref","src","recording","arrow","open","theme","title","sentence","text","onClick","onMouseEnter","onMouseLeave","StopIcon","PlayOutlineIcon","RedoIcon","ShareIcon","RecordingError","UnsupportedInfo","isIOS","isMobileSafari","BalanceText","SafariColor","rel","href","FirefoxColor","ChromeColor","NoSentencesAvailable","LinkButton","rounded","ArrowRight","initialState","clips","isSubmitted","recordingStatus","rerecordIndex","showPrivacyModal","showDiscardModal","SpeakPage","isUnsupportedPlatform","maxVolume","recordingStartTime","recordingStopTime","handleKeyUprerecording","event","__awaiter","index","code","discardRecording","releaseMicrophone","document","hidden","isRecording","saveRecording","processRecording","info","getRecordingError","newClips","getRecordingIndex","taxonomy","TOO_SHORT","TOO_LONG","TOO_QUIET","updateVolume","volume","rerecord","handleRecordClick","startRecording","err","Date","now","setTimeout","cancelReRecord","handleSkip","api","removeSentences","_b","revokeObjectURL","upload","hasAgreed","addAchievement","addNotification","addUploads","tallyRecording","user","refreshUser","privacyAgreed","account","filter","map","retries","showFirstContributionToast","hasEarnedSessionToast","showFirstStreakToast","challengeEnded","uploadClip","sessionStorage","setItem","JSON","stringify","parse","getItem","removeItem","setInviteContributeAchievement","message","confirm","CheckIcon","resetState","callback","agreeToTerms","updateUser","toggleDiscardModal","displayError","isLoading","returnSpeakError","resetAndGoHome","toLocaleRoute","ROOT","scrollTo","top","behavior","sentenceIds","Boolean","unusedSentences","sentences","includes","shift","slice","SET_COUNT","componentDidMount","componentWillUnmount","findIndex","render","recordingIndex","innerClassName","onRequestClose","ModalButtons","Button","outline","getTrackClass","TextButton","TermsModal","ContributionPage","activeIndex","errorContent","instruction","recordIcon","MicIcon","stopIcon","isFirstSubmit","recordTally","onReset","onSkip","onSubmit","primaryButtons","RecordButton","trackClass","pills","RecordingPill","reportModalProps","reasons","kind","shortcuts","label","mapDispatchToProps","Notifications","actions","addPill","Uploads","add","Sentences","remove","User","refresh","update","localeConnector","selectors","localeSentences"],"mappings":"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","file":"11.js?id=db888b44fdde1cd981f9","sourcesContent":["import * as React from 'react';\nimport {\n  Localized,\n  withLocalization,\n  WithLocalizationProps,\n} from '@fluent/react';\nimport URLS from '../urls';\nimport { LocaleLink } from './locale-helpers';\nimport Modal from './modal/modal';\n\nconst TermsModal = ({\n  getString,\n  onAgree,\n  onDisagree,\n}: { onAgree: () => any; onDisagree: () => any } & WithLocalizationProps) => (\n  <Localized\n    id=\"review-terms\"\n    elems={{\n      termsLink: <LocaleLink to={URLS.TERMS} blank />,\n      privacyLink: <LocaleLink to={URLS.PRIVACY} blank />,\n    }}>\n    <Modal\n      buttons={{\n        [getString('terms-agree')]: onAgree,\n        [getString('terms-disagree')]: onDisagree,\n      }}\n    />\n  </Localized>\n);\nexport default withLocalization(TermsModal);\n","import { getAudioFormat } from '../../../../utility';\n\ninterface BlobEvent extends Event {\n  data: Blob;\n}\n\nexport enum AudioError {\n  NOT_ALLOWED = 'NOT_ALLOWED',\n  NO_MIC = 'NO_MIC',\n  NO_SUPPORT = 'NO_SUPPORT',\n}\n\nexport interface AudioInfo {\n  url: string;\n  blob: Blob;\n}\n\nexport default class AudioWeb {\n  microphone: MediaStream;\n  analyzerNode: AnalyserNode;\n  audioContext: AudioContext;\n  recorder: any;\n  chunks: any[];\n  frequencyBins: Uint8Array;\n  volumeCallback: Function;\n  jsNode: any;\n  recorderListeners: {\n    start: Function | null;\n    dataavailable: Function | null;\n    stop: Function | null;\n  };\n\n  constructor() {\n    this.recorderListeners = {\n      start: null,\n      dataavailable: null,\n      stop: null,\n    };\n    this.analyze = this.analyze.bind(this);\n  }\n\n  private isReady(): boolean {\n    return !!this.microphone;\n  }\n\n  private getMicrophone(): Promise<MediaStream> {\n    return new Promise(function (res: Function, rej: Function) {\n      function deny(error: MediaStreamError) {\n        rej(\n          ({\n            NotAllowedError: AudioError.NOT_ALLOWED,\n            NotFoundError: AudioError.NO_MIC,\n          } as { [errorName: string]: AudioError })[error.name] || error\n        );\n      }\n      function resolve(stream: MediaStream) {\n        res(stream);\n      }\n\n      if (navigator.mediaDevices?.getUserMedia) {\n        navigator.mediaDevices\n          .getUserMedia({ audio: true })\n          .then(resolve, deny);\n      } else if (navigator.getUserMedia) {\n        navigator.getUserMedia({ audio: true }, resolve, deny);\n      } else if (navigator.webkitGetUserMedia) {\n        navigator.webkitGetUserMedia({ audio: true }, resolve, deny);\n      } else if (navigator.mozGetUserMedia) {\n        navigator.mozGetUserMedia({ audio: true }, resolve, deny);\n      } else {\n        // Browser does not support getUserMedia\n        rej(AudioError.NO_SUPPORT);\n      }\n    });\n  }\n\n  // Check all the browser prefixes for microhpone support.\n  isMicrophoneSupported() {\n    return (\n      navigator.mediaDevices?.getUserMedia ||\n      navigator.getUserMedia ||\n      navigator.webkitGetUserMedia ||\n      navigator.mozGetUserMedia\n    );\n  }\n\n  // Check if audio recording is supported\n  isAudioRecordingSupported() {\n    return (\n      typeof window.MediaRecorder !== 'undefined' &&\n      !window.MediaRecorder.notSupported\n    );\n  }\n\n  private analyze() {\n    this.analyzerNode.getByteFrequencyData(this.frequencyBins);\n\n    if (this.volumeCallback) {\n      this.volumeCallback(Math.max(...this.frequencyBins));\n    }\n  }\n\n  setVolumeCallback(cb: Function) {\n    this.volumeCallback = cb;\n  }\n\n  /**\n   * Initialize the recorder, opening the microphone media stream.\n   *\n   * If microphone access is currently denied, the user is asked to grant\n   * access. Since these permission changes take effect only after a reload,\n   * the page is reloaded if the user decides to do so.\n   *\n   */\n  async init() {\n    if (this.isReady()) {\n      return;\n    }\n\n    const microphone = await this.getMicrophone();\n\n    this.microphone = microphone;\n    const audioContext = new (window.AudioContext ||\n      window.webkitAudioContext)();\n    const sourceNode = audioContext.createMediaStreamSource(microphone);\n    const volumeNode = audioContext.createGain();\n    const analyzerNode = audioContext.createAnalyser();\n    const outputNode = audioContext.createMediaStreamDestination();\n\n    // Make sure we're doing mono everywhere.\n    sourceNode.channelCount = 1;\n    volumeNode.channelCount = 1;\n    analyzerNode.channelCount = 1;\n    outputNode.channelCount = 1;\n\n    // Connect the nodes together\n    sourceNode.connect(volumeNode);\n    volumeNode.connect(analyzerNode);\n    analyzerNode.connect(outputNode);\n\n    // and set up the recorder.\n    this.recorder = new window.MediaRecorder(outputNode.stream);\n\n    // Set up the analyzer node, and allocate an array for its data\n    // FFT size 64 gives us 32 bins. But those bins hold frequencies up to\n    // 22kHz or more, and we only care about lower frequencies which is where\n    // most human voice lies, so we use fewer bins.\n    analyzerNode.fftSize = 128;\n    analyzerNode.smoothingTimeConstant = 0.96;\n    this.frequencyBins = new Uint8Array(analyzerNode.frequencyBinCount);\n\n    // Setup jsNode for audio analysis callbacks.\n    // TODO: `createScriptProcessor` is deprecated, and is a heavy solution for\n    //       what it’s doing (checking recording volume). It should be replaced\n    //       with something lighter, or AudioWorklets once they become more\n    //       widely adopted.\n    this.jsNode = audioContext.createScriptProcessor(256, 1, 1);\n    this.jsNode.connect(audioContext.destination);\n\n    this.analyzerNode = analyzerNode;\n    this.audioContext = audioContext;\n  }\n\n  start(): Promise<void> {\n    if (!this.isReady()) {\n      console.error('Cannot record audio before microhphone is ready.');\n      return Promise.resolve();\n    }\n\n    return new Promise<void>((res: Function, rej: Function) => {\n      this.chunks = [];\n      // Remove the old listeners.\n      this.recorder.removeEventListener('start', this.recorderListeners.start);\n      this.recorder.removeEventListener(\n        'dataavailable',\n        this.recorderListeners.dataavailable\n      );\n\n      // Update the stored listeners.\n      this.recorderListeners.start = (e: Event) => res();\n      this.recorderListeners.dataavailable = (e: BlobEvent) => {\n        this.chunks.push(e.data);\n      };\n\n      // Add the new listeners.\n      this.recorder.addEventListener('start', this.recorderListeners.start);\n      this.recorder.addEventListener(\n        'dataavailable',\n        this.recorderListeners.dataavailable\n      );\n\n      // Finally, start it up.\n      // We want to be able to record up to 60s of audio in a single blob.\n      // Without this argument to start(), Chrome will call dataavailable\n      // very frequently.\n      this.jsNode.onaudioprocess = this.analyze;\n      this.recorder.start(20000);\n    });\n  }\n\n  stop(): Promise<AudioInfo> {\n    if (!this.isReady()) {\n      console.error('Cannot stop audio before microphone is ready.');\n      return Promise.reject();\n    }\n\n    return new Promise((res: Function, rej: Function) => {\n      this.jsNode.onaudioprocess = undefined;\n      this.recorder.removeEventListener('stop', this.recorderListeners.stop);\n      this.recorderListeners.stop = (e: Event) => {\n        let blob = new Blob(this.chunks, { type: getAudioFormat() });\n        res({\n          url: URL.createObjectURL(blob),\n          blob: blob,\n        });\n      };\n      this.recorder.addEventListener('stop', this.recorderListeners.stop);\n      this.recorder.stop();\n    });\n  }\n\n  release() {\n    if (this.microphone) {\n      for (const track of this.microphone.getTracks()) {\n        track.stop();\n      }\n    }\n    this.microphone = null;\n  }\n}\n","module.exports =\n/******/ (function(modules) { // webpackBootstrap\n/******/ \t// The module cache\n/******/ \tvar installedModules = {};\n/******/\n/******/ \t// The require function\n/******/ \tfunction __webpack_require__(moduleId) {\n/******/\n/******/ \t\t// Check if module is in cache\n/******/ \t\tif(installedModules[moduleId]) {\n/******/ \t\t\treturn installedModules[moduleId].exports;\n/******/ \t\t}\n/******/ \t\t// Create a new module (and put it into the cache)\n/******/ \t\tvar module = installedModules[moduleId] = {\n/******/ \t\t\ti: moduleId,\n/******/ \t\t\tl: false,\n/******/ \t\t\texports: {}\n/******/ \t\t};\n/******/\n/******/ \t\t// Execute the module function\n/******/ \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n/******/\n/******/ \t\t// Flag the module as loaded\n/******/ \t\tmodule.l = true;\n/******/\n/******/ \t\t// Return the exports of the module\n/******/ \t\treturn module.exports;\n/******/ \t}\n/******/\n/******/\n/******/ \t// expose the modules object (__webpack_modules__)\n/******/ \t__webpack_require__.m = modules;\n/******/\n/******/ \t// expose the module cache\n/******/ \t__webpack_require__.c = installedModules;\n/******/\n/******/ \t// define getter function for harmony exports\n/******/ \t__webpack_require__.d = function(exports, name, getter) {\n/******/ \t\tif(!__webpack_require__.o(exports, name)) {\n/******/ \t\t\tObject.defineProperty(exports, name, {\n/******/ \t\t\t\tconfigurable: false,\n/******/ \t\t\t\tenumerable: true,\n/******/ \t\t\t\tget: getter\n/******/ \t\t\t});\n/******/ \t\t}\n/******/ \t};\n/******/\n/******/ \t// getDefaultExport function for compatibility with non-harmony modules\n/******/ \t__webpack_require__.n = function(module) {\n/******/ \t\tvar getter = module && module.__esModule ?\n/******/ \t\t\tfunction getDefault() { return module['default']; } :\n/******/ \t\t\tfunction getModuleExports() { return module; };\n/******/ \t\t__webpack_require__.d(getter, 'a', getter);\n/******/ \t\treturn getter;\n/******/ \t};\n/******/\n/******/ \t// Object.prototype.hasOwnProperty.call\n/******/ \t__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };\n/******/\n/******/ \t// __webpack_public_path__\n/******/ \t__webpack_require__.p = \"\";\n/******/\n/******/ \t// Load entry module and return exports\n/******/ \treturn __webpack_require__(__webpack_require__.s = 0);\n/******/ })\n/************************************************************************/\n/******/ ([\n/* 0 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nObject.defineProperty(exports, \"__esModule\", {\n  value: true\n});\n\nvar _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };\n\nvar _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();\n\nvar _react = __webpack_require__(1);\n\nvar _react2 = _interopRequireDefault(_react);\n\nvar _reactRouterDom = __webpack_require__(2);\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nfunction _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\"); } return call && (typeof call === \"object\" || typeof call === \"function\") ? call : self; }\n\nfunction _inherits(subClass, superClass) { if (typeof superClass !== \"function\" && superClass !== null) { throw new TypeError(\"Super expression must either be null or a function, not \" + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }\n\nvar initState = {\n  action: null,\n  isActive: false,\n  nextLocation: null\n};\n\n/**\n * A replacement component for the react-router `Prompt`.\n * Allows for more flexible dialogs.\n *\n * @example\n * <NavigationPrompt when={this.props.isDirty}>\n *   {({isActive, onConfirm, onCancel}) => (\n *     <Modal show={isActive}>\n *       <div>\n *         <p>Do you really want to leave?</p>\n *         <button onClick={onCancel}>Cancel</button>\n *         <button onClick={onConfirm}>Ok</button>\n *       </div>\n *     </Modal>\n *   )}\n * </NavigationPrompt>\n */\n\nvar NavigationPrompt = function (_React$Component) {\n  _inherits(NavigationPrompt, _React$Component);\n\n  /*:: _prevUserAction: string; */\n  /*:: _isMounted: bool; */\n\n  function NavigationPrompt(props) {\n    _classCallCheck(this, NavigationPrompt);\n\n    // `_prevUserAction` weirdness because setState()'s callback is not getting invoked.\n    // See: See https://github.com/ZacharyRSmith/react-router-navigation-prompt/pull/9\n    // I don't like making this an instance var,\n    var _this = _possibleConstructorReturn(this, (NavigationPrompt.__proto__ || Object.getPrototypeOf(NavigationPrompt)).call(this, props));\n\n    _this._prevUserAction = '';\n\n    // This component could be used from inside a page, and therefore could be\n    // mounted/unmounted when the route changes.\n    _this._isMounted = true;\n\n    _this.block = _this.block.bind(_this);\n    _this.onBeforeUnload = _this.onBeforeUnload.bind(_this);\n    _this.onCancel = _this.onCancel.bind(_this);\n    _this.onConfirm = _this.onConfirm.bind(_this);\n    _this.when = _this.when.bind(_this);\n\n    _this.state = _extends({}, initState, { unblock: function unblock() {} /* unblock will be set in componentDidMount */ });\n    return _this;\n  }\n\n  _createClass(NavigationPrompt, [{\n    key: 'componentDidMount',\n    value: function componentDidMount() {\n      if (!this.props.disableNative) {\n        window.addEventListener('beforeunload', this.onBeforeUnload);\n      }\n\n      this.setState({ unblock: this.props.history.block(this.block) });\n    }\n  }, {\n    key: 'componentDidUpdate',\n    value: function componentDidUpdate(prevProps, prevState) {\n      if (this._prevUserAction === 'CANCEL' && typeof this.props.afterCancel === 'function') {\n        this.props.afterCancel();\n      } else if (this._prevUserAction === 'CONFIRM' && typeof this.props.afterConfirm === 'function') {\n        this.props.afterConfirm();\n      }\n      this._prevUserAction = '';\n    }\n  }, {\n    key: 'componentWillUnmount',\n    value: function componentWillUnmount() {\n      this._isMounted = false;\n      if (this._prevUserAction === 'CONFIRM' && typeof this.props.afterConfirm === 'function') {\n        this._prevUserAction = '';\n        this.props.afterConfirm();\n      }\n      this.state.unblock();\n      if (!this.props.disableNative) {\n        window.removeEventListener('beforeunload', this.onBeforeUnload);\n      }\n    }\n  }, {\n    key: 'block',\n    value: function block(nextLocation, action) {\n      var result = this.when(nextLocation, action);\n      if (result) {\n        this.setState({\n          action: action,\n          nextLocation: nextLocation,\n          isActive: true\n        });\n      }\n      return !result;\n    }\n  }, {\n    key: 'navigateToNextLocation',\n    value: function navigateToNextLocation(cb) {\n      var _this2 = this;\n\n      var _state = this.state,\n          action = _state.action,\n          nextLocation = _state.nextLocation;\n\n      action = {\n        'POP': this.props.allowGoBack ? 'goBack' : 'push',\n        'PUSH': 'push',\n        'REPLACE': 'replace'\n      }[action || 'PUSH'];\n      if (!nextLocation) nextLocation = { pathname: '/' };\n      var history = this.props.history;\n\n\n      this.state.unblock();\n      this._prevUserAction = 'CONFIRM';\n      if (action === 'goBack') {\n        // Because there is asynchronous time between calling history.goBack()\n        // and history actually changing, we need to set up this temporary callback\n        // -- if we tried to run this synchronously after calling history.goBack(),\n        // then navigateToNextLocation would be triggered again.\n        var unlisten = history.listen(function () {\n          unlisten();\n          if (_this2._isMounted) {\n            // Just in case we unmounted on the route change\n            _this2.setState(_extends({}, initState, {\n              unblock: history.block(_this2.block)\n            }));\n          }\n        });\n        history.goBack();\n      } else {\n        // $FlowFixMe history.replace()'s type expects LocationShape even though it works with Location.\n        history[action](nextLocation); // could unmount at this point\n        if (this._isMounted) {\n          // Just in case we unmounted on the route change\n          this.setState(_extends({}, initState, {\n            unblock: this.props.history.block(this.block)\n          })); // FIXME?  Does history.listen need to be used instead, for async?\n        }\n      }\n    }\n  }, {\n    key: 'onCancel',\n    value: function onCancel() {\n      var _this3 = this;\n\n      (this.props.beforeCancel || function (cb) {\n        cb();\n      })(function () {\n        _this3._prevUserAction = 'CANCEL';\n        _this3.setState(_extends({}, initState));\n      });\n    }\n  }, {\n    key: 'onConfirm',\n    value: function onConfirm() {\n      var _this4 = this;\n\n      (this.props.beforeConfirm || function (cb) {\n        cb();\n      })(function () {\n        _this4.navigateToNextLocation(_this4.props.afterConfirm);\n      });\n    }\n  }, {\n    key: 'onBeforeUnload',\n    value: function onBeforeUnload(e) {\n      if (!this.when()) return;\n      var msg = 'Do you want to leave this site?\\n\\nChanges you made may not be saved.';\n      e.returnValue = msg;\n      return msg;\n    }\n  }, {\n    key: 'when',\n    value: function when(nextLocation, action) {\n      if (typeof this.props.when === 'function') {\n        return this.props.when(this.props.location, nextLocation, action);\n      } else {\n        return this.props.when;\n      }\n    }\n  }, {\n    key: 'render',\n    value: function render() {\n      if (!this.state.isActive && !this.props.renderIfNotActive) return null;\n      return _react2.default.createElement(\n        'div',\n        null,\n        this.props.children({\n          isActive: this.state.isActive,\n          onConfirm: this.onConfirm,\n          onCancel: this.onCancel\n        })\n      );\n    }\n  }]);\n\n  return NavigationPrompt;\n}(_react2.default.Component);\n\nexports.default = (0, _reactRouterDom.withRouter)(NavigationPrompt);\n\n/***/ }),\n/* 1 */\n/***/ (function(module, exports) {\n\nmodule.exports = require(\"react\");\n\n/***/ }),\n/* 2 */\n/***/ (function(module, exports) {\n\nmodule.exports = require(\"react-router-dom\");\n\n/***/ })\n/******/ ]);","import {\n  Localized,\n  withLocalization,\n  WithLocalizationProps,\n} from '@fluent/react';\nimport * as React from 'react';\nimport { useRef, useState } from 'react';\nimport { trackRecording } from '../../../../services/tracker';\nimport { useLocale } from '../../../locale-helpers';\nimport {\n  PlayOutlineIcon,\n  RedoIcon,\n  ShareIcon,\n  StopIcon,\n} from '../../../ui/icons';\nimport { getAudioFormat } from '../../../../utility';\nimport { ContributionPillProps } from '../contribution';\nimport Pill, { PillStatus } from '../pill';\nimport { SentenceRecording } from './sentence-recording';\n\nimport './recording-pill.css';\n\nconst { Tooltip } = require('react-tippy');\n\ninterface Props extends ContributionPillProps, WithLocalizationProps {\n  children?: React.ReactNode;\n  clip: SentenceRecording;\n  onRerecord: () => any;\n  status: PillStatus;\n}\n\nfunction RecordingPill({\n  children,\n  clip,\n  getString,\n  onRerecord,\n  onShare,\n  status,\n  ...props\n}: Props) {\n  const [locale] = useLocale();\n  const [isPlaying, setIsPlaying] = useState(false);\n  const [showSentenceTooltip, setShowSentenceTooltip] = useState(false);\n  const audioRef = useRef(null);\n\n  const toggleIsPlaying = () => {\n    const { current: audio } = audioRef;\n    let nextIsPlaying = !isPlaying;\n    if (nextIsPlaying) {\n      trackRecording('listen', locale);\n      audio.play();\n    } else {\n      audio.pause();\n      audio.currentTime = 0;\n    }\n    setIsPlaying(nextIsPlaying);\n  };\n\n  return (\n    <Pill {...props} className=\"recording\" openable status={status}>\n      {children}\n\n      {!children && status === 'active' && (\n        <Localized id=\"record-cta\">\n          <div className=\"text\" />\n        </Localized>\n      )}\n\n      {!children && status === 'done' && (\n        <>\n          <audio preload=\"auto\" onEnded={toggleIsPlaying} ref={audioRef}>\n            <source src={clip.recording.url} type={getAudioFormat()} />\n          </audio>\n          <Tooltip\n            arrow\n            open={isPlaying || showSentenceTooltip}\n            theme=\"grey-tooltip\"\n            title={clip.sentence.text}>\n            <button\n              className=\"play\"\n              type=\"button\"\n              onClick={toggleIsPlaying}\n              onMouseEnter={() => setShowSentenceTooltip(true)}\n              onMouseLeave={() => setShowSentenceTooltip(false)}>\n              <span className=\"padder\">\n                {isPlaying ? <StopIcon /> : <PlayOutlineIcon />}\n              </span>\n            </button>\n          </Tooltip>\n          {isPlaying ? (\n            <div className=\"placeholder\" />\n          ) : (\n            <>\n              <Tooltip arrow title={getString('review-tooltip')}>\n                <button className=\"redo\" type=\"button\" onClick={onRerecord}>\n                  <span className=\"padder\">\n                    <RedoIcon />\n                  </span>\n                </button>\n              </Tooltip>\n              <Tooltip arrow title={getString('share-clip')}>\n                <button className=\"share\" type=\"button\" onClick={onShare}>\n                  <span className=\"padder\">\n                    <ShareIcon />\n                  </span>\n                </button>\n              </Tooltip>\n            </>\n          )}\n        </>\n      )}\n    </Pill>\n  );\n}\n\nexport default withLocalization(RecordingPill);\n","import {\n  Localized,\n  WithLocalizationProps,\n  withLocalization,\n} from '@fluent/react';\nimport * as React from 'react';\nimport BalanceText from 'react-balance-text';\nimport { connect } from 'react-redux';\nimport { RouteComponentProps, withRouter } from 'react-router';\nconst NavigationPrompt = require('react-router-navigation-prompt').default;\nimport { Locale } from '../../../../stores/locale';\nimport { Notifications } from '../../../../stores/notifications';\nimport { Sentences } from '../../../../stores/sentences';\nimport { Sentence as SentenceType } from 'common';\nimport StateTree from '../../../../stores/tree';\nimport { Uploads } from '../../../../stores/uploads';\nimport { User } from '../../../../stores/user';\nimport API from '../../../../services/api';\nimport { trackRecording, getTrackClass } from '../../../../services/tracker';\nimport URLS from '../../../../urls';\nimport { localeConnector, LocalePropsFromState } from '../../../locale-helpers';\nimport Modal, { ModalButtons } from '../../../modal/modal';\nimport TermsModal from '../../../terms-modal';\nimport {\n  CheckIcon,\n  MicIcon,\n  StopIcon,\n  ArrowRight,\n  FirefoxColor,\n  ChromeColor,\n  SafariColor,\n} from '../../../ui/icons';\nimport { Button, TextButton, LinkButton } from '../../../ui/ui';\nimport { isIOS, isMobileSafari } from '../../../../utility';\nimport ContributionPage, {\n  ContributionPillProps,\n  SET_COUNT,\n} from '../contribution';\nimport {\n  RecordButton,\n  RecordingStatus,\n} from '../../../primary-buttons/primary-buttons';\nimport AudioWeb, { AudioError, AudioInfo } from './audio-web';\nimport RecordingPill from './recording-pill';\nimport { SentenceRecording } from './sentence-recording';\n\nimport './speak.css';\n\nconst MIN_RECORDING_MS = 1000;\nconst MIN_RECORDING_MS_BENCHMARK = 500;\nconst MAX_RECORDING_MS = 16500;\nconst MIN_VOLUME = 8; // Range: [0, 255].\n\nenum RecordingError {\n  TOO_SHORT = 'TOO_SHORT',\n  TOO_LONG = 'TOO_LONG',\n  TOO_QUIET = 'TOO_QUIET',\n}\n\nconst UnsupportedInfo = () => (\n  <div className=\"empty-container\">\n    <div className=\"error-card card-dimensions unsupported\">\n      {isIOS() && !isMobileSafari() ? (\n        <>\n          <BalanceText>\n            <Localized id=\"record-platform-not-supported-ios-non-safari\" />\n          </BalanceText>\n          <SafariColor />\n        </>\n      ) : (\n        <>\n          <BalanceText>\n            <Localized id=\"record-platform-not-supported\" />\n          </BalanceText>\n          <p className=\"desktop\">\n            <Localized id=\"record-platform-not-supported-desktop\">\n              <BalanceText />\n            </Localized>\n          </p>\n          <div>\n            <a\n              rel=\"noopener noreferrer\"\n              target=\"_blank\"\n              href=\"https://www.firefox.com/\"\n              title=\"Firefox\">\n              <FirefoxColor />\n            </a>{' '}\n            <a\n              rel=\"noopener noreferrer\"\n              target=\"_blank\"\n              href=\"https://www.google.com/chrome\"\n              title=\"Chrome\">\n              <ChromeColor />\n            </a>\n          </div>\n        </>\n      )}\n    </div>\n  </div>\n);\n\nconst NoSentencesAvailable = () => (\n  <div className=\"empty-container\">\n    <div className=\"error-card card-dimensions no-sentences-available\">\n      <Localized id=\"speak-empty-state\">\n        <span />\n      </Localized>\n      <LinkButton\n        rounded\n        blank\n        href=\"https://common-voice.github.io/sentence-collector/\">\n        <ArrowRight className=\"speak-sc-icon\" />{' '}\n        <Localized id=\"speak-empty-state-cta\">\n          <span />\n        </Localized>\n      </LinkButton>\n    </div>\n  </div>\n);\n\ninterface PropsFromState {\n  api: API;\n  locale: Locale.State;\n  sentences: SentenceType[];\n  user: User.State;\n  isLoading: boolean;\n}\n\ninterface PropsFromDispatch {\n  addUploads: typeof Uploads.actions.add;\n  addAchievement: typeof Notifications.actions.addAchievement;\n  addNotification: typeof Notifications.actions.addPill;\n  removeSentences: typeof Sentences.actions.remove;\n  tallyRecording: typeof User.actions.tallyRecording;\n  refreshUser: typeof User.actions.refresh;\n  updateUser: typeof User.actions.update;\n}\n\ninterface Props\n  extends LocalePropsFromState,\n    WithLocalizationProps,\n    PropsFromState,\n    PropsFromDispatch,\n    RouteComponentProps<any, any, any> {}\n\ninterface State {\n  clips: SentenceRecording[];\n  isSubmitted: boolean;\n  error?: RecordingError | AudioError;\n  recordingStatus: RecordingStatus;\n  rerecordIndex?: number;\n  showPrivacyModal: boolean;\n  showDiscardModal: boolean;\n}\n\nconst initialState: State = {\n  clips: [],\n  isSubmitted: false,\n  error: null,\n  recordingStatus: null,\n  rerecordIndex: null,\n  showPrivacyModal: false,\n  showDiscardModal: false,\n};\n\nclass SpeakPage extends React.Component<Props, State> {\n  state: State = initialState;\n\n  audio: AudioWeb;\n  isUnsupportedPlatform = false;\n  maxVolume = -1;\n  recordingStartTime = 0;\n  recordingStopTime = 0;\n\n  static getDerivedStateFromProps(props: Props, state: State) {\n    if (state.clips.length > 0) {\n      const sentenceIds = state.clips\n        .map(({ sentence }) => (sentence ? sentence.id : null))\n        .filter(Boolean);\n      const unusedSentences = props.sentences.filter(\n        s => !sentenceIds.includes(s.id)\n      );\n      return {\n        clips: state.clips.map(clip =>\n          clip.sentence\n            ? clip\n            : { recording: null, sentence: unusedSentences.shift() || null }\n        ),\n      };\n    }\n\n    if (props.sentences.length > 0) {\n      return {\n        clips: props.sentences\n          .slice(0, SET_COUNT)\n          .map(sentence => ({ recording: null, sentence })),\n      };\n    }\n\n    return null;\n  }\n\n  componentDidMount() {\n    this.audio = new AudioWeb();\n    this.audio.setVolumeCallback(this.updateVolume.bind(this));\n\n    document.addEventListener('visibilitychange', this.releaseMicrophone);\n    document.addEventListener('keyup', this.handleKeyUprerecording);\n\n    if (\n      !this.audio.isMicrophoneSupported() ||\n      !this.audio.isAudioRecordingSupported()\n    ) {\n      this.isUnsupportedPlatform = true;\n    }\n  }\n\n  async componentWillUnmount() {\n    document.removeEventListener('keyup', this.handleKeyUprerecording);\n\n    document.removeEventListener('visibilitychange', this.releaseMicrophone);\n    if (!this.isRecording) return;\n    await this.audio.stop();\n  }\n\n  private get isRecording() {\n    return this.state.recordingStatus === 'recording';\n  }\n\n  private handleKeyUprerecording = async (event: any) => {\n    let index = null;\n    //for both sets of number keys on a keyboard with shift key\n    if (event.code === 'Digit1' || event.code === 'Numpad1') {\n      index = 0;\n    } else if (event.code === 'Digit2' || event.code === 'Numpad2') {\n      index = 1;\n    } else if (event.code === 'Digit3' || event.code === 'Numpad3') {\n      index = 2;\n    } else if (event.code === 'Digit4' || event.code === 'Numpad4') {\n      index = 3;\n    } else if (event.code === 'Digit5' || event.code === 'Numpad5') {\n      index = 4;\n    }\n\n    if (index !== null) {\n      trackRecording('rerecord', this.props.locale);\n      await this.discardRecording();\n      this.setState({\n        rerecordIndex: index,\n      });\n    }\n  };\n\n  private getRecordingIndex() {\n    return (\n      this.state.rerecordIndex ??\n      this.state.clips.findIndex(({ recording }) => !recording)\n    );\n  }\n\n  private releaseMicrophone = () => {\n    if (!document.hidden) {\n      return;\n    }\n\n    if (this.isRecording) {\n      this.saveRecording();\n    }\n    this.audio.release();\n  };\n\n  private processRecording = (info: AudioInfo) => {\n    const error = this.getRecordingError();\n    if (error) {\n      return this.setState({ error });\n    }\n\n    this.setState(({ clips }) => {\n      const newClips = [...clips];\n      newClips[this.getRecordingIndex()].recording = info;\n      return {\n        clips: newClips,\n        rerecordIndex: null,\n      };\n    });\n\n    trackRecording('record', this.props.locale);\n  };\n\n  private getRecordingError = (): RecordingError => {\n    const length = this.recordingStopTime - this.recordingStartTime;\n    const currentSentence = this.state.clips[this.getRecordingIndex()].sentence;\n    const minClipLength =\n      currentSentence.taxonomy === 'Benchmark'\n        ? MIN_RECORDING_MS_BENCHMARK\n        : MIN_RECORDING_MS;\n\n    if (length < minClipLength) {\n      return RecordingError.TOO_SHORT;\n    }\n    if (length > MAX_RECORDING_MS) {\n      return RecordingError.TOO_LONG;\n    }\n    // If updateVolume was never called, we assume there’s a problem with the\n    // AnalyserNode and skip this error.\n    if (this.maxVolume !== -1 && this.maxVolume < MIN_VOLUME) {\n      return RecordingError.TOO_QUIET;\n    }\n    return null;\n  };\n\n  private updateVolume = (volume: number) => {\n    if (volume > this.maxVolume) {\n      this.maxVolume = volume;\n    }\n  };\n\n  private rerecord = async (i: number) => {\n    trackRecording('rerecord', this.props.locale);\n    await this.discardRecording();\n\n    this.setState({\n      rerecordIndex: i,\n    });\n  };\n\n  private handleRecordClick = async () => {\n    if (this.state.recordingStatus === 'waiting') return;\n    const isRecording = this.isRecording;\n\n    this.setState({ recordingStatus: 'waiting' });\n\n    if (isRecording) {\n      this.saveRecording();\n      return;\n    }\n\n    try {\n      await this.audio.init();\n      await this.startRecording();\n    } catch (err) {\n      if (err in AudioError) {\n        this.setState({ error: err });\n      } else {\n        throw err;\n      }\n    }\n  };\n\n  private startRecording = async () => {\n    try {\n      await this.audio.start();\n      this.maxVolume = -1; // Initialize to -1 in case updateVolume is never called.\n      this.recordingStartTime = Date.now();\n      this.recordingStopTime = 0;\n      this.setState({\n        recordingStatus: 'recording',\n        error: null,\n      });\n    } catch (err) {\n      this.setState({\n        recordingStatus: null,\n      });\n    }\n  };\n\n  private saveRecording = () => {\n    // We noticed that some people hit the Stop button too early, cutting off\n    // the recording prematurely. To compensate, we add a short buffer to the\n    // end of each recording (issue #1648).\n    const RECORD_STOP_DELAY = 500;\n    setTimeout(async () => {\n      const info = await this.audio.stop();\n      this.processRecording(info);\n    }, RECORD_STOP_DELAY);\n    this.recordingStopTime = Date.now();\n    this.setState({\n      recordingStatus: null,\n    });\n  };\n\n  private discardRecording = async () => {\n    if (!this.isRecording) return;\n    await this.audio.stop();\n    this.setState({ recordingStatus: null });\n  };\n\n  private cancelReRecord = async () => {\n    await this.discardRecording();\n    this.setState({ rerecordIndex: null });\n  };\n\n  private handleSkip = async () => {\n    const { api, removeSentences } = this.props;\n    await this.discardRecording();\n    const current = this.getRecordingIndex();\n    const id = this.state.clips[current]?.sentence?.id;\n    removeSentences([id]);\n    this.setState(({ clips }) => {\n      const newClips = [...clips];\n      newClips[current] = { recording: null, sentence: null };\n      if (clips[current]?.recording?.url)\n        URL.revokeObjectURL(clips[current].recording.url);\n      return {\n        clips: newClips,\n        error: null,\n      };\n    });\n  };\n\n  private upload = (hasAgreed: boolean = false) => {\n    const {\n      addAchievement,\n      addNotification,\n      addUploads,\n      api,\n      locale,\n      removeSentences,\n      tallyRecording,\n      user,\n      refreshUser,\n    } = this.props;\n\n    if (!hasAgreed && !(user.privacyAgreed || user.account)) {\n      this.setState({ showPrivacyModal: true });\n      return false;\n    }\n\n    const clips = this.state.clips.filter(clip => clip.recording);\n\n    removeSentences(clips.map(c => c.sentence.id));\n\n    this.setState({ clips: [], isSubmitted: true });\n\n    addUploads([\n      ...clips.map(({ sentence, recording }) => async () => {\n        let retries = 3;\n        while (retries) {\n          try {\n            const {\n              showFirstContributionToast = false,\n              hasEarnedSessionToast = false,\n              showFirstStreakToast = false,\n              challengeEnded = true,\n            } = await api.uploadClip(recording.blob, sentence.id);\n            URL.revokeObjectURL(recording.url);\n            sessionStorage.setItem(\n              'challengeEnded',\n              JSON.stringify(challengeEnded)\n            );\n            sessionStorage.setItem('hasContributed', 'true');\n            if (showFirstContributionToast) {\n              addAchievement(\n                50,\n                \"You're on your way! Congrats on your first contribution.\",\n                'success'\n              );\n            }\n            if (showFirstStreakToast) {\n              addAchievement(\n                50,\n                'You completed a three-day streak! Keep it up.',\n                'success'\n              );\n            }\n            if (\n              !JSON.parse(sessionStorage.getItem('challengeEnded')) &&\n              JSON.parse(sessionStorage.getItem('hasShared')) &&\n              !hasEarnedSessionToast\n            ) {\n              addAchievement(\n                50,\n                \"You're on a roll! You sent an invite and contributed in the same session.\",\n                'success'\n              );\n              sessionStorage.removeItem('hasShared');\n              // Tell back-end user get unexpected achievement: invite + contribute in the same session\n              // Each user can only get once.\n              api.setInviteContributeAchievement();\n            }\n            if (!user.account) {\n              tallyRecording();\n            }\n            retries = 0;\n          } catch (error) {\n            let msg;\n            if (error.message === 'save_clip_error') {\n              msg =\n                'Upload of this clip keeps failing at server, reload the page or try after sometime';\n            } else {\n              msg = 'Upload of this clip keeps failing, keep retrying?';\n            }\n            retries--;\n            await new Promise(resolve => setTimeout(resolve, 1000));\n            if (retries == 0 && confirm(msg)) {\n              retries = 3;\n            }\n          }\n        }\n      }),\n      async () => {\n        trackRecording('submit', locale);\n        refreshUser();\n        addNotification(\n          <>\n            <CheckIcon />{' '}\n            <Localized id=\"clips-uploaded\">\n              <span />\n            </Localized>\n          </>\n        );\n      },\n    ]);\n\n    return true;\n  };\n\n  private resetState = (callback?: any) =>\n    this.setState(initialState, callback);\n\n  private agreeToTerms = async () => {\n    this.setState({ showPrivacyModal: false });\n    this.props.updateUser({ privacyAgreed: true });\n    this.upload(true);\n  };\n\n  private toggleDiscardModal = () => {\n    this.setState({\n      showPrivacyModal: false,\n      showDiscardModal: !this.state.showDiscardModal,\n    });\n  };\n\n  private displayError = () => {\n    return (\n      this.isUnsupportedPlatform ||\n      (!this.props.isLoading && this.state.clips.length == 0)\n    );\n  };\n\n  private returnSpeakError = () => {\n    return this.isUnsupportedPlatform ? (\n      <UnsupportedInfo />\n    ) : (\n      <NoSentencesAvailable />\n    );\n  };\n\n  private resetAndGoHome = () => {\n    const { history, toLocaleRoute } = this.props;\n    this.resetState(() => {\n      history.push(toLocaleRoute(URLS.ROOT));\n      window.scrollTo({\n        top: 0,\n        behavior: 'smooth',\n      });\n    });\n  };\n\n  render() {\n    const { getString, user } = this.props;\n    const {\n      clips,\n      isSubmitted,\n      error,\n      recordingStatus,\n      rerecordIndex,\n      showPrivacyModal,\n      showDiscardModal,\n    } = this.state;\n    const recordingIndex = this.getRecordingIndex();\n\n    return (\n      <>\n        <NavigationPrompt\n          when={clips.filter(clip => clip.recording).length > 0}>\n          {({ onConfirm, onCancel }: any) => (\n            <Modal innerClassName=\"record-abort\" onRequestClose={onCancel}>\n              <Localized id=\"record-abort-title\">\n                <h1 className=\"title\" />\n              </Localized>\n              <Localized id=\"record-abort-text\">\n                <p className=\"text\" />\n              </Localized>\n              <ModalButtons>\n                <Localized id=\"record-abort-submit\">\n                  <Button\n                    outline\n                    rounded\n                    className={getTrackClass('fs', 'exit-submit-clips')}\n                    onClick={() => {\n                      if (this.upload()) onConfirm();\n                    }}\n                  />\n                </Localized>\n                <Localized id=\"record-abort-continue\">\n                  <Button\n                    outline\n                    rounded\n                    className={getTrackClass('fs', 'exit-continue-recording')}\n                    onClick={onCancel}\n                  />\n                </Localized>\n              </ModalButtons>\n              <Localized id=\"record-abort-delete\">\n                <TextButton\n                  className={getTrackClass('fs', 'exit-delete-clips')}\n                  onClick={onConfirm}\n                />\n              </Localized>\n            </Modal>\n          )}\n        </NavigationPrompt>\n        {showPrivacyModal && (\n          <TermsModal\n            onAgree={this.agreeToTerms}\n            onDisagree={this.toggleDiscardModal}\n          />\n        )}\n        {showDiscardModal && (\n          <Localized id=\"review-aborted\">\n            <Modal\n              buttons={{\n                [getString('review-keep-recordings')]: this.toggleDiscardModal,\n                [getString('review-delete-recordings')]: this.resetAndGoHome,\n              }}\n            />\n          </Localized>\n        )}\n        <ContributionPage\n          activeIndex={recordingIndex}\n          errorContent={this.displayError() && this.returnSpeakError()}\n          instruction={props =>\n            error ? (\n              <div className=\"error\">\n                <Localized\n                  id={\n                    {\n                      [RecordingError.TOO_SHORT]: 'record-error-too-short',\n                      [RecordingError.TOO_LONG]: 'record-error-too-long',\n                      [RecordingError.TOO_QUIET]: 'record-error-too-quiet',\n                      [AudioError.NOT_ALLOWED]: 'record-must-allow-microphone',\n                      [AudioError.NO_MIC]: 'record-no-mic-found',\n                      [AudioError.NO_SUPPORT]: 'record-platform-not-supported',\n                    }[error]\n                  }\n                  {...props}\n                />\n              </div>\n            ) : (\n              <Localized\n                id={\n                  this.isRecording\n                    ? 'record-stop-instruction'\n                    : recordingIndex === SET_COUNT - 1\n                    ? 'record-last-instruction'\n                    : ['record-instruction', 'record-again-instruction'][\n                        recordingIndex\n                      ] || 'record-again-instruction2'\n                }\n                elems={{\n                  recordIcon: <MicIcon />,\n                  stopIcon: <StopIcon />,\n                }}\n                {...props}\n              />\n            )\n          }\n          isFirstSubmit={user.recordTally === 0}\n          isPlaying={this.isRecording}\n          isSubmitted={isSubmitted}\n          onReset={() => this.resetState()}\n          onSkip={this.handleSkip}\n          onSubmit={() => this.upload()}\n          primaryButtons={\n            <RecordButton\n              trackClass=\"speak-record\"\n              status={recordingStatus}\n              onClick={this.handleRecordClick}\n            />\n          }\n          pills={clips.map((clip, i) => (props: ContributionPillProps) => (\n            <RecordingPill\n              {...props}\n              clip={clip}\n              status={\n                recordingIndex === i\n                  ? 'active'\n                  : clip.recording\n                  ? 'done'\n                  : 'pending'\n              }\n              onRerecord={() => this.rerecord(i)}>\n              {rerecordIndex === i && (\n                <Localized id=\"record-cancel\">\n                  <TextButton onClick={this.cancelReRecord} />\n                </Localized>\n              )}\n            </RecordingPill>\n          ))}\n          reportModalProps={{\n            reasons: [\n              'offensive-language',\n              'grammar-or-spelling',\n              'different-language',\n              'difficult-pronounce',\n            ],\n            kind: 'sentence',\n            id:\n              recordingIndex == -1 || !clips[recordingIndex].sentence\n                ? null\n                : clips[recordingIndex].sentence.id,\n          }}\n          sentences={clips.map(({ sentence }) => sentence)}\n          shortcuts={[\n            {\n              key: 'shortcut-record-toggle',\n              label: 'shortcut-record-toggle-label',\n              action: this.handleRecordClick,\n            },\n            {\n              key: 'shortcut-rerecord-toggle',\n              label: 'shortcut-rerecord-toggle-label',\n              action: this.handleRecordClick,\n            },\n          ]}\n          type=\"speak\"\n        />\n      </>\n    );\n  }\n}\n\nconst mapStateToProps = (state: StateTree) => {\n  const { sentences, isLoading } = Sentences.selectors.localeSentences(state);\n\n  return {\n    api: state.api,\n    locale: state.locale,\n    sentences,\n    user: state.user,\n    isLoading,\n  };\n};\n\nconst mapDispatchToProps = {\n  addNotification: Notifications.actions.addPill,\n  addAchievement: Notifications.actions.addAchievement,\n  addUploads: Uploads.actions.add,\n  removeSentences: Sentences.actions.remove,\n  tallyRecording: User.actions.tallyRecording,\n  refreshUser: User.actions.refresh,\n  updateUser: User.actions.update,\n};\n\nexport default withRouter(\n  localeConnector(\n    withLocalization(\n      connect<PropsFromState, any>(\n        mapStateToProps,\n        mapDispatchToProps\n      )(SpeakPage)\n    )\n  )\n);\n"],"sourceRoot":""}