{"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","TermsModal","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","_a","navigator","mediaDevices","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","__rest","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","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=eceba37a66967b23733b","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":""}