{"version":3,"sources":["webpack:///./src/components/pages/contribution/listen/listen.tsx"],"names":["VoteButton","_a","kind","props","__rest","React","Object","assign","type","className","getTrackClass","join","ThumbsUpIcon","ThumbsDownIcon","Localized","id","initialState","clips","hasPlayed","hasPlayedSome","isPlaying","isSubmitted","ListenPage","constructor","audioRef","state","play","this","stop","current","setState","clearInterval","playedSomeInterval","setInterval","audio","pause","currentTime","trackListening","locale","vote","isValid","showFirstContributionToast","hasEarnedSessionToast","addAchievement","api","showFirstStreakToast","challengeEnded","clipIndex","getClipIndex","sessionStorage","setItem","JSON","stringify","parse","getItem","removeItem","setInviteContributeAchievement","SET_COUNT","map","clip","i","voteYes","voteNo","handleSkip","removeClip","slice","reset","length","componentWillUnmount","findIndex","render","activeClip","src","audioSrc","preload","onEnded","ref","ContributionPage","activeIndex","errorContent","isLoading","LinkButton","rounded","to","URLS","SPEAK","MicIcon","instruction","elems","playIcon","OldPlayIcon","onReset","onSkip","primaryButtons","onClick","disabled","PlayButton","trackClass","pills","isVoted","isActive","Pill","status","VolumeIcon","CheckIcon","CrossIcon","reportModalProps","reasons","sentences","sentence","shortcuts","key","label","action","mapDispatchToProps","Clips","actions","remove","Notifications","connect","selectors","localeClips"],"mappings":"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","file":"15.js?id=30a7863615ec06b42f5f","sourcesContent":["import { Localized } from '@fluent/react';\nimport * as React from 'react';\nimport { connect } from 'react-redux';\nimport { Clip as ClipType } from 'common';\nimport { trackListening, getTrackClass } from '../../../../services/tracker';\nimport { Clips } from '../../../../stores/clips';\nimport { Locale } from '../../../../stores/locale';\nimport StateTree from '../../../../stores/tree';\nimport API from '../../../../services/api';\nimport URLS from '../../../../urls';\nimport {\n  CheckIcon,\n  CrossIcon,\n  MicIcon,\n  OldPlayIcon,\n  ThumbsDownIcon,\n  ThumbsUpIcon,\n  VolumeIcon,\n} from '../../../ui/icons';\nimport { LinkButton } from '../../../ui/ui';\nimport ContributionPage, {\n  ContributionPillProps,\n  SET_COUNT,\n} from '../contribution';\nimport { Notifications } from '../../../../stores/notifications';\nimport { PlayButton } from '../../../primary-buttons/primary-buttons';\nimport Pill from '../pill';\n\nimport './listen.css';\nimport { User } from '@sentry/types';\n\nconst VOTE_NO_PLAY_MS = 3000; // Threshold when to allow voting no\n\nconst VoteButton = ({\n  kind,\n  ...props\n}: { kind: 'yes' | 'no' } & React.ButtonHTMLAttributes<any>) => (\n  <button\n    type=\"button\"\n    className={['vote-button', kind, getTrackClass('fs', `vote-${kind}`)].join(\n      ' '\n    )}\n    {...props}>\n    {kind === 'yes' && <ThumbsUpIcon />}\n    {kind === 'no' && <ThumbsDownIcon />}\n    <Localized id={'vote-' + kind}>\n      <span />\n    </Localized>\n  </button>\n);\n\ninterface PropsFromState {\n  api: API;\n  clips: ClipType[];\n  isLoading: boolean;\n  locale: Locale.State;\n  showFirstContributionToast: boolean;\n  hasEarnedSessionToast: boolean;\n  showFirstStreakToast: boolean;\n  challengeEnded: boolean;\n}\n\ninterface PropsFromDispatch {\n  removeClip: typeof Clips.actions.remove;\n  vote: typeof Clips.actions.vote;\n  addAchievement: typeof Notifications.actions.addAchievement;\n}\n\ninterface Props extends PropsFromState, PropsFromDispatch {}\n\ninterface State {\n  clips: (ClipType & { isValid?: boolean })[];\n  hasPlayed: boolean;\n  hasPlayedSome: boolean;\n  isPlaying: boolean;\n  isSubmitted: boolean;\n}\n\nconst initialState: State = {\n  clips: [],\n  hasPlayed: false,\n  hasPlayedSome: false,\n  isPlaying: false,\n  isSubmitted: false,\n};\n\nclass ListenPage extends React.Component<Props, State> {\n  audioRef = React.createRef<HTMLAudioElement>();\n  playedSomeInterval: any;\n\n  state: State = initialState;\n\n  static getDerivedStateFromProps(props: Props, state: State) {\n    if (state.clips.length > 0) return null;\n\n    if (props.clips.length > 0) {\n      return {\n        clips: props.clips\n          .slice(0, SET_COUNT)\n          .map(clip => ({ ...clip, isValid: null })),\n      };\n    }\n\n    return null;\n  }\n\n  componentWillUnmount() {\n    clearInterval(this.playedSomeInterval);\n    // this.audioPlayer.close();\n  }\n\n  private getClipIndex() {\n    return this.state.clips.findIndex(clip => clip.isValid === null);\n  }\n\n  private play = () => {\n    if (this.state.isPlaying) {\n      this.stop();\n      return;\n    }\n\n    this.audioRef.current.play();\n    this.setState({ isPlaying: true });\n    clearInterval(this.playedSomeInterval);\n    this.playedSomeInterval = setInterval(\n      () => this.setState({ hasPlayedSome: true }),\n      VOTE_NO_PLAY_MS\n    );\n  };\n\n  private stop = () => {\n    const audio = this.audioRef.current;\n    audio.pause();\n    audio.currentTime = 0;\n    clearInterval(this.playedSomeInterval);\n    this.setState({ isPlaying: false });\n  };\n\n  private hasPlayed = () => {\n    this.setState({ hasPlayed: true, isPlaying: false });\n    trackListening('listen', this.props.locale);\n  };\n\n  private vote = (isValid: boolean) => {\n    const { clips } = this.state;\n\n    const {\n      showFirstContributionToast,\n      hasEarnedSessionToast,\n      addAchievement,\n      api,\n      showFirstStreakToast,\n      challengeEnded,\n    } = this.props;\n    const clipIndex = this.getClipIndex();\n\n    this.stop();\n    this.props.vote(isValid, this.state.clips[this.getClipIndex()].id);\n\n    sessionStorage.setItem('challengeEnded', JSON.stringify(challengeEnded));\n    sessionStorage.setItem('hasContributed', 'true');\n\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    this.setState({\n      hasPlayed: false,\n      hasPlayedSome: false,\n      isPlaying: false,\n      isSubmitted: clipIndex === SET_COUNT - 1,\n      clips: clips.map((clip, i) =>\n        i === clipIndex ? { ...clip, isValid } : clip\n      ),\n    });\n  };\n\n  private voteYes = () => {\n    if (!this.state.hasPlayed) {\n      return;\n    }\n    this.vote(true);\n    trackListening('vote-yes', this.props.locale);\n  };\n\n  private voteNo = () => {\n    const { hasPlayed, hasPlayedSome } = this.state;\n    if (!hasPlayed && !hasPlayedSome) {\n      return;\n    }\n    this.vote(false);\n    trackListening('vote-no', this.props.locale);\n  };\n\n  private handleSkip = () => {\n    const { removeClip } = this.props;\n    const { clips } = this.state;\n    this.stop();\n    removeClip(clips[this.getClipIndex()].id);\n    this.setState({\n      clips: clips.map((clip, i) =>\n        this.getClipIndex() === i\n          ? { ...this.props.clips.slice(SET_COUNT)[0], isValid: null }\n          : clip\n      ),\n      hasPlayed: false,\n      hasPlayedSome: false,\n    });\n  };\n\n  private reset = () => this.setState(initialState);\n\n  render() {\n    const {\n      clips,\n      hasPlayed,\n      hasPlayedSome,\n      isPlaying,\n      isSubmitted,\n    } = this.state;\n    const clipIndex = this.getClipIndex();\n    const activeClip = clips[clipIndex];\n    return (\n      <>\n        <audio\n          {...(activeClip && { src: activeClip.audioSrc })}\n          preload=\"auto\"\n          onEnded={this.hasPlayed}\n          ref={this.audioRef}\n        />\n        <ContributionPage\n          activeIndex={clipIndex}\n          errorContent={\n            !this.props.isLoading &&\n            (clips.length === 0 || !activeClip) && (\n              <div className=\"empty-container\">\n                <div className=\"error-card card-dimensions\">\n                  <Localized id=\"listen-empty-state\">\n                    <span />\n                  </Localized>\n                  <LinkButton\n                    rounded\n                    to={URLS.SPEAK}\n                    className=\"record-instead\">\n                    <MicIcon />{' '}\n                    <Localized id=\"record-button-label\">\n                      <span />\n                    </Localized>\n                  </LinkButton>\n                </div>\n              </div>\n            )\n          }\n          instruction={props =>\n            activeClip &&\n            !isPlaying &&\n            !hasPlayedSome &&\n            !hasPlayed && (\n              <Localized\n                id={\n                  clipIndex === SET_COUNT - 1\n                    ? 'listen-last-time-instruction'\n                    : [\n                        'listen-instruction',\n                        'listen-again-instruction',\n                        'listen-3rd-time-instruction',\n                      ][clipIndex] || 'listen-again-instruction'\n                }\n                elems={{ playIcon: <OldPlayIcon /> }}\n                {...props}\n              />\n            )\n          }\n          isPlaying={isPlaying}\n          isSubmitted={isSubmitted}\n          onReset={this.reset}\n          onSkip={this.handleSkip}\n          primaryButtons={\n            <>\n              <VoteButton\n                kind=\"yes\"\n                onClick={this.voteYes}\n                disabled={!hasPlayed}\n              />\n              <PlayButton\n                isPlaying={isPlaying}\n                onClick={this.play}\n                trackClass=\"play-clip\"\n              />\n              <VoteButton\n                kind=\"no\"\n                onClick={this.voteNo}\n                disabled={!hasPlayed && !hasPlayedSome}\n              />\n            </>\n          }\n          pills={clips.map(\n            ({ isValid }, i) => (props: ContributionPillProps) => {\n              const isVoted = isValid !== null;\n              const isActive = clipIndex === i;\n              return (\n                <Pill\n                  className={isVoted ? (isValid ? 'valid' : 'invalid') : ''}\n                  onClick={null}\n                  status={isActive ? 'active' : isVoted ? 'done' : 'pending'}\n                  {...props}>\n                  {isActive ? (\n                    <VolumeIcon />\n                  ) : isVoted ? (\n                    isValid ? (\n                      <CheckIcon />\n                    ) : (\n                      <CrossIcon />\n                    )\n                  ) : null}\n                </Pill>\n              );\n            }\n          )}\n          reportModalProps={{\n            reasons: [\n              'offensive-speech',\n              'grammar-or-spelling',\n              'different-language',\n            ],\n            kind: 'clip',\n            id: activeClip ? activeClip.id : null,\n          }}\n          sentences={clips.map(clip => clip.sentence)}\n          shortcuts={[\n            {\n              key: 'shortcut-play-toggle',\n              label: 'shortcut-play-toggle-label',\n              action: this.play,\n            },\n            {\n              key: 'shortcut-vote-yes',\n              label: 'vote-yes',\n              action: this.voteYes,\n            },\n            {\n              key: 'shortcut-vote-no',\n              label: 'vote-no',\n              action: this.voteNo,\n            },\n          ]}\n          type=\"listen\"\n        />\n      </>\n    );\n  }\n}\n\nconst mapStateToProps = (state: StateTree) => {\n  const {\n    clips,\n    isLoading,\n    showFirstContributionToast,\n    hasEarnedSessionToast,\n    showFirstStreakToast,\n    challengeEnded,\n  } = Clips.selectors.localeClips(state);\n  const { api } = state;\n  return {\n    clips,\n    isLoading,\n    showFirstContributionToast,\n    hasEarnedSessionToast,\n    showFirstStreakToast,\n    challengeEnded,\n    api,\n    locale: state.locale,\n  };\n};\n\nconst mapDispatchToProps = {\n  removeClip: Clips.actions.remove,\n  vote: Clips.actions.vote,\n  addAchievement: Notifications.actions.addAchievement,\n};\n\nexport default connect<PropsFromState, any>(\n  mapStateToProps,\n  mapDispatchToProps\n)(ListenPage);\n"],"sourceRoot":""}