831 lines
31 KiB
CoffeeScript
831 lines
31 KiB
CoffeeScript
context = window
|
|
logger = context.JK.logger
|
|
ReactCSSTransitionGroup = React.addons.CSSTransitionGroup;
|
|
rest = context.JK.Rest()
|
|
|
|
mixins = []
|
|
|
|
|
|
# make sure this is actually us opening the window, not someone else (by checking for MixerStore)
|
|
|
|
accessOpener = false
|
|
if window.opener?
|
|
try
|
|
m = window.opener.MixerStore
|
|
accessOpener = true
|
|
catch e
|
|
|
|
|
|
if accessOpener
|
|
AppActions = window.opener.AppActions
|
|
SessionActions = window.opener.SessionActions
|
|
MixerActions = window.opener.MixerActions
|
|
MixerStore = window.opener.MixerStore
|
|
JamTrackActions = window.opener.JamTrackActions
|
|
JamTrackMixdownActions = window.opener.JamTrackMixdownActions
|
|
#JamTrackMixdownStore = window.opener.JamTrackMixdownStore
|
|
JamTrackMixdown = window.opener.JamTrackMixdown
|
|
JamTrackStore = window.opener.JamTrackStore
|
|
MixerStore = window.opener.MixerStore
|
|
SessionStore = window.opener.SessionStore
|
|
UserStore = window.opener.UserStore
|
|
|
|
mixins.push(Reflux.listenTo(MixerStore, 'onMixersChanged'))
|
|
mixins.push(Reflux.listenTo(JamTrackStore, 'onJamTrackChanged'))
|
|
mixins.push(Reflux.listenTo(UserStore, 'onUserChanged'))
|
|
|
|
@PopupMediaControls = React.createClass({
|
|
|
|
mixins: mixins
|
|
|
|
updateFromMixerHelper: (mixers, session) ->
|
|
|
|
|
|
# the backend delete/adds the metronome rapidly when the user hits play. this is custom code to deal with that
|
|
|
|
state =
|
|
isRecording: session.isRecording
|
|
mediaSummary: mixers.mediaSummary
|
|
backingTracks: mixers.backingTracks
|
|
jamTracks: mixers.jamTracks
|
|
recordedTracks: mixers.recordedTracks
|
|
metronome: mixers.metronome
|
|
recordingName: mixers.recordingName()
|
|
jamTrackName: mixers.jamTrackName()
|
|
jamTrackMixdown: session.jamTrackMixdown()
|
|
|
|
return {media: state, downloadingJamTrack: session.downloadingJamTrack}
|
|
|
|
onMixersChanged: (sessionMixers) ->
|
|
session = sessionMixers.session
|
|
mixers = sessionMixers.mixers
|
|
|
|
if @unloaded
|
|
#console.log("PopupMediaControls unloaded. ignore onMixersChnaged")
|
|
return
|
|
if window.closed
|
|
return
|
|
@setState(@updateFromMixerHelper(mixers, session))
|
|
|
|
|
|
onMediaStateChanged: (changes) ->
|
|
if @unloaded
|
|
#console.log("PopupMediaControls unloaded. ignore onMixersChnaged")
|
|
return
|
|
|
|
if window.closed
|
|
return
|
|
|
|
if changes.currentTimeChanged && @root?
|
|
@setState({time: changes.time})
|
|
|
|
onJamTrackChanged: (changes) ->
|
|
if @unloaded
|
|
#console.log("PopupMediaControls unloaded. ignore onMixersChnaged")
|
|
return
|
|
|
|
if window.closed
|
|
return
|
|
|
|
logger.debug("PopupMediaControls: jamtrack changed", changes)
|
|
@setState({jamTrackState: changes})
|
|
|
|
onUserChanged: (changes) ->
|
|
@setState({user: changes.user})
|
|
|
|
showMetronome: (e) ->
|
|
e.preventDefault()
|
|
|
|
SessionActions.showNativeMetronomeGui()
|
|
|
|
getInitialState: () ->
|
|
|
|
if accessOpener
|
|
|
|
state = @updateFromMixerHelper(MixerStore.mixers, MixerStore.session)
|
|
state.jamTrackState = JamTrackStore.getState()
|
|
state.user = UserStore.getState().user
|
|
return state
|
|
else
|
|
|
|
return {
|
|
media: @props.media,
|
|
mixdown: @props.mixdown,
|
|
jamTrackState: @props.jamTrackState,
|
|
creatingMixdown: false,
|
|
createMixdownErrors: null,
|
|
editingMixdownId: null,
|
|
downloadingJamTrack: @props.downloadingJamTrack,
|
|
jamTrackMixdown: {}
|
|
}
|
|
|
|
close: (e) ->
|
|
if e?
|
|
e.preventDefault()
|
|
|
|
if !@state.media.mediaSummary.isOpener && !@state.media.mediaSummary.metronomeOpen
|
|
return
|
|
|
|
SessionActions.log({msg: "PopupMediaControls close event trigger"})
|
|
window.close()
|
|
|
|
help: (e) ->
|
|
e.preventDefault()
|
|
|
|
AppActions.openExternalUrl($(e.target).attr('href'))
|
|
|
|
render: () ->
|
|
|
|
closeLinkText = null
|
|
header = null
|
|
extraControls = null
|
|
|
|
# give the users options to close it
|
|
if @state.media.mediaSummary.recordingOpen
|
|
mediaType = "Recording"
|
|
mediaName = @state.media.recordedTracks[0].recordingName
|
|
closeLinkText = 'close recording'
|
|
header = `<h3>{mediaType}: {mediaName}</h3>`
|
|
else if @state.media.mediaSummary.jamTrackOpen || @state.jamTrackState.jamTrack?
|
|
if @state.media.mediaSummary.isOpener || @state.jamTrackState.jamTrack?
|
|
# if you opened the JamTrack, then you get all the good info
|
|
jamTrack = @state.jamTrackState.jamTrack
|
|
mediaType = "JamTrack"
|
|
mediaName = jamTrack.name
|
|
closeLinkText = 'CLOSE JAMTRACK'
|
|
helpLink = 'https://jamkazam.desk.com/customer/portal/articles/2138903-using-custom-mixes-to-slow-tempo-change-pitch'
|
|
|
|
selectedMixdown = jamTrack.activeMixdown
|
|
|
|
|
|
if selectedMixdown?
|
|
jamTrackTypeHeader = 'Custom Mix'
|
|
|
|
disabled = true
|
|
if selectedMixdown.client_state?
|
|
switch selectedMixdown.client_state
|
|
when 'cant_open'
|
|
customMixName = `<h5>{selectedMixdown.name}<img src="/assets/content/icon-mix-fail@2X.png" /></h5>`
|
|
when 'keying_timeout'
|
|
customMixName = `<h5>{selectedMixdown.name}<img src="/assets/content/icon-mix-fail@2X.png" /></h5>`
|
|
when 'download_fail'
|
|
customMixName = `<h5>{selectedMixdown.name}<img src="/assets/content/icon-mix-fail@2X.png" /></h5>`
|
|
when 'keying'
|
|
customMixName = `<h5>Loading selected mix... <img src="/assets/shared/spinner.gif" /></h5>`
|
|
when 'downloading'
|
|
customMixName = `<h5>Loading selected mix... <img src="/assets/shared/spinner.gif" /></h5>`
|
|
when 'ready'
|
|
customMixName = `<h5>{selectedMixdown.name}</h5>`
|
|
disabled = false
|
|
else
|
|
if selectedMixdown.myPackage
|
|
customMixName = `<h5>Creating mixdown... <img src="/assets/shared/spinner.gif" /></h5>`
|
|
else
|
|
customMixName = `<h5>{selectedMixdown.name}<img src="/assets/content/icon-mix-fail@2X.png" /></h5>`
|
|
|
|
else
|
|
if SessionStore.downloadingJamTrack
|
|
downloader = `<img src="/assets/shared/spinner.gif" />`
|
|
|
|
jamTrackTypeHeader = `<span>Full JamTrack {downloader}</span>`
|
|
|
|
header = `
|
|
<div className="header">
|
|
<h3>{mediaType}: {mediaName}</h3>
|
|
<h4>{jamTrackTypeHeader}</h4>
|
|
{customMixName}
|
|
</div>`
|
|
|
|
myMixes = null
|
|
if @state.showMyMixes
|
|
myMixdowns = []
|
|
|
|
boundPlayClick = this.jamTrackPlay.bind(this, jamTrack);
|
|
boundDownloadClick = this.jamTrackDownload.bind(this, jamTrack);
|
|
|
|
active = jamTrack.last_mixdown_id == null
|
|
|
|
myMixdowns.push `
|
|
<div key="full-track" className={classNames({'full-track': true, 'mixdown-display': true, 'active' : active})}>
|
|
<div className="mixdown-name">
|
|
Full JamTrack
|
|
</div>
|
|
<div className="mixdown-actions">
|
|
<img src="/assets/content/icon_open@2X.png" className="mixdown-play" onClick={boundPlayClick}/>
|
|
<img src="/assets/content/icon_download@2X.png" className="mixdown-download" onClick={boundDownloadClick}/>
|
|
<img src ="/assets/content/icon-delete@2X.png" style={{visibility:'hidden'}} className="mixdown-edit" onClick={false} />
|
|
<img src ="/assets/content/icon-delete@2X.png" style={{visibility:'hidden'}} className="mixdown-delete" onClick={false} />
|
|
</div>
|
|
</div>`
|
|
|
|
for mixdown in jamTrack.mixdowns
|
|
boundPlayClick = this.mixdownPlay.bind(this, mixdown);
|
|
boundEditClick = this.mixdownEdit.bind(this, mixdown);
|
|
boundSaveClick = this.mixdownSave.bind(this, mixdown);
|
|
boundDeleteClick = this.mixdownDelete.bind(this, mixdown);
|
|
boundErrorClick = this.mixdownError.bind(this, mixdown);
|
|
boundEditKeydown = this.onEditKeydown.bind(this, mixdown);
|
|
boundDownloadReadyClick = this.downloadMixdownReady.bind(this, mixdown)
|
|
|
|
mixdown_package = mixdown.myPackage
|
|
|
|
active = mixdown.id == jamTrack.last_mixdown_id
|
|
|
|
editing = mixdown.id == @state.editingMixdownId
|
|
|
|
# if there is a package, check it's state; otherwise let the user enqueue it
|
|
if mixdown_package
|
|
switch mixdown_package.signing_state
|
|
when 'QUIET_TIMEOUT'
|
|
action = `<img src="/assets/content/icon-mix-fail@2X.png" className="mixdown-play" onClick={boundErrorClick}/>`
|
|
when 'QUIET'
|
|
action = `<img src="/assets/shared/spinner.gif" className="mixdown-play"/>`
|
|
when 'QUEUED'
|
|
action = `<img src="/assets/shared/spinner.gif" className="mixdown-play"/>`
|
|
when 'QUEUED_TIMEOUT'
|
|
action = `<img src="/assets/content/icon-mix-fail@2X.png" className="mixdown-play" onClick={boundErrorClick}/>`
|
|
when 'SIGNING'
|
|
action = `<img src="/assets/shared/spinner.gif" className="mixdown-play"/>`
|
|
when 'SIGNING_TIMEOUT'
|
|
action = `<img src="/assets/content/icon-mix-fail@2X.png" className="mixdown-play" onClick={boundErrorClick}/>`
|
|
when 'SIGNED'
|
|
action = `<img src="/assets/content/icon_open@2X.png" className="mixdown-play" onClick={boundPlayClick}/>`
|
|
when 'ERROR'
|
|
action = `<img src="/assets/content/icon-mix-fail@2X.png" className="mixdown-play" onClick={boundErrorClick}/>`
|
|
else
|
|
action = `<img src="/assets/content/icon-mix-fail@2X.png" className="mixdown-play" onClick={boundErrorClick}/>`
|
|
|
|
if editing
|
|
mixdownName = `<input className="edit-name" type="text" defaultValue={mixdown.name} onKeyDown={boundEditKeydown} />`
|
|
editIcon = `<img src="/assets/content/icon-save@2X.png" className="mixdown-edit" onClick={boundSaveClick}/>`
|
|
else
|
|
mixdownName = mixdown.name
|
|
editIcon = `<img src="/assets/content/icon-edit@2X.png" className="mixdown-edit" onClick={boundEditClick}/>`
|
|
|
|
download = `<img src="/assets/content/icon_download@2X.png" className="mixdown-download" onClick={boundDownloadReadyClick} />`
|
|
|
|
myMixdowns.push `
|
|
<div key={mixdown.id} className={classNames({'mixdown-display': true, 'active' : active})}>
|
|
<div className="mixdown-name">
|
|
{mixdownName}
|
|
</div>
|
|
<div className="mixdown-actions">
|
|
{action}
|
|
{download}
|
|
{editIcon}
|
|
|
|
<img src ="/assets/content/icon-delete@2X.png" className="mixdown-delete" onClick={boundDeleteClick} />
|
|
</div>
|
|
</div>`
|
|
|
|
trackOptions = []
|
|
for track in jamTrack.tracks
|
|
if track.track_type == 'Track' || track.track_type == 'Click'
|
|
|
|
if track.instrument
|
|
instrumentId = track.instrument.id
|
|
instrumentDescription = track.instrument.description
|
|
if track.part? && track.part != instrumentDescription
|
|
part = "(#{track.part})"
|
|
else
|
|
part = ''
|
|
trackOptions.push(`<option key={track.id} value={track.id}>{instrumentDescription} {part}</option>`)
|
|
|
|
myMixdowns.push `
|
|
<div key={track.id} className={classNames({'stem-track' : true, 'mixdown-display': true})}>
|
|
<div className="mixdown-name">
|
|
<select className="active-stem-select" name="active-stem-select" onChange={this.stemChanged}>
|
|
<option key="null" value="">or select a track</option>
|
|
{trackOptions}
|
|
</select>
|
|
</div>
|
|
<div className="mixdown-actions">
|
|
<img src="/assets/content/icon_open@2X.png" style={{visibility:'hidden'}} className="mixdown-play"/>
|
|
<img src="/assets/content/icon_download@2X.png" className="mixdown-download" onClick={this.stemDownload}/>
|
|
<img src ="/assets/content/icon-delete@2X.png" style={{visibility:'hidden'}} className="mixdown-edit" onClick={false} />
|
|
<img src ="/assets/content/icon-delete@2X.png" style={{visibility:'hidden'}} className="mixdown-delete" onClick={false} />
|
|
</div>
|
|
</div>`
|
|
|
|
myMixes = `<div key="my-mixes" className="my-mixes">{myMixdowns}</div>`
|
|
|
|
mixControls = null
|
|
if @state.showCustomMixes
|
|
|
|
nameClassData = {field: true}
|
|
if @state.createMixdownErrors?
|
|
|
|
errorHtml = context.JK.reactErrors(@state.createMixdownErrors, {name: 'Mix Name', settings: 'Settings', jam_track: 'JamTrack'})
|
|
|
|
createMixClasses = classNames({'button-orange' : true, 'create-mix-btn' : true, 'disabled' : @state.creatingMixdown})
|
|
|
|
if !selectedMixdown?
|
|
mixControls = `
|
|
<div key="create-mix" className="create-mix">
|
|
<p>Use the JamTrack controls on the session screen to set levels, mute/unmute, or pan any of the parts of the JamTrack as you like. You can also use the controls below to adjust the tempo or pitch of the JamTrack. Then give your custom mix a name, and click the Create Mix button.</p>
|
|
<div className="field">
|
|
<label>Change Tempo:</label>
|
|
<select name="mix-speed">
|
|
<option value="">No change</option>
|
|
<option value="separator-1">------------</option>
|
|
<option value="-5">Slower by 5%</option>
|
|
<option value="-10">Slower by 10%</option>
|
|
<option value="-15">Slower by 15%</option>
|
|
<option value="-20">Slower by 20%</option>
|
|
<option value="-25">Slower by 25%</option>
|
|
<option value="-30">Slower by 30%</option>
|
|
<option value="-35">Slower by 35%</option>
|
|
<option value="-40">Slower by 40%</option>
|
|
<option value="-45">Slower by 45%</option>
|
|
<option value="-50">Slower by 50%</option>
|
|
<option value="-60">Slower by 60%</option>
|
|
<option value="-70">Slower by 70%</option>
|
|
<option value="-80">Slower by 80%</option>
|
|
<option value="separator-2">------------</option>
|
|
<option value="5">Faster by 5%</option>
|
|
<option value="10">Faster by 10%</option>
|
|
<option value="15">Faster by 15%</option>
|
|
<option value="20">Faster by 20%</option>
|
|
<option value="30">Faster by 30%</option>
|
|
<option value="40">Faster by 40%</option>
|
|
<option value="50">Faster by 50%</option>
|
|
</select>
|
|
</div>
|
|
<div className="field">
|
|
<label>Change Pitch:</label>
|
|
<select name="mix-pitch">
|
|
<option value="">No change</option>
|
|
<option value="separator-1">---------------</option>
|
|
<option value="-1">Down 1 Semitone</option>
|
|
<option value="-2">Down 2 Semitones</option>
|
|
<option value="-3">Down 3 Semitones</option>
|
|
<option value="-4">Down 4 Semitones</option>
|
|
<option value="-5">Down 5 Semitones</option>
|
|
<option value="-6">Down 6 Semitones</option>
|
|
<option value="-7">Down 7 Semitones</option>
|
|
<option value="-8">Down 8 Semitones</option>
|
|
<option value="-9">Down 9 Semitones</option>
|
|
<option value="-10">Down 10 Semitones</option>
|
|
<option value="-11">Down 11 Semitones</option>
|
|
<option value="-12">Down 12 Semitones</option>
|
|
<option value="separator-2">---------------</option>
|
|
<option value="1">Up 1 Semitone</option>
|
|
<option value="2">Up 2 Semitones</option>
|
|
<option value="3">Up 3 Semitones</option>
|
|
<option value="4">Up 4 Semitones</option>
|
|
<option value="5">Up 5 Semitones</option>
|
|
<option value="6">Up 6 Semitones</option>
|
|
<option value="7">Up 7 Semitones</option>
|
|
<option value="8">Up 8 Semitones</option>
|
|
<option value="9">Up 9 Semitones</option>
|
|
<option value="10">Up 10 Semitones</option>
|
|
<option value="11">Up 11 Semitones</option>
|
|
<option value="12">Up 12 Semitones</option>
|
|
</select>
|
|
</div>
|
|
<div className={classNames(nameClassData)}>
|
|
<label>Mix Name:</label>
|
|
<input type="text" name="mix-name"/>
|
|
</div>
|
|
<div className="field">
|
|
<a className={createMixClasses} onClick={this.createMix}>CREATE MIX</a>
|
|
{errorHtml}
|
|
</div>
|
|
<div className="clearall"/>
|
|
|
|
</div>`
|
|
else
|
|
|
|
mixControls =
|
|
`<div key="no-full-mix-open" className="create-mix not-active">
|
|
<p>To create a custom mix, you must open the Full JamTrack in the My Mixes section above.</p>
|
|
</div>`
|
|
|
|
if @state.showMyMixes
|
|
showMyMixesText = `<a onClick={this.toggleMyMixes}>hide my mixes <div className="details-arrow arrow-up" /></a>`
|
|
else
|
|
showMyMixesText = `<a onClick={this.toggleMyMixes}>show my mixes <div className="details-arrow arrow-down" /></a>`
|
|
|
|
if @state.showCustomMixes
|
|
showMixControlsText = `<a onClick={this.toggleCustomMixes}>hide mix controls <div className="details-arrow arrow-up" /></a>`
|
|
else
|
|
showMixControlsText = `<a onClick={this.toggleCustomMixes}>show mix controls <div className="details-arrow arrow-down" /></a>`
|
|
|
|
|
|
extraControls = `
|
|
<div className="extra-controls">
|
|
<h4>My Mixes {showMyMixesText}</h4>
|
|
<ReactCSSTransitionGroup transitionName="session-track-list" transitionAppear={true}>
|
|
{myMixes}
|
|
</ReactCSSTransitionGroup>
|
|
<h4 className="custom-mix-header">Create Custom Mix {showMixControlsText}</h4>
|
|
<ReactCSSTransitionGroup transitionName="session-track-list" transitionAppear={true}>
|
|
{mixControls}
|
|
</ReactCSSTransitionGroup>
|
|
</div>`
|
|
else
|
|
|
|
mediaType = "JamTrack"
|
|
mediaName = @state.media.jamTrackName
|
|
closeLinkText = 'CLOSE JAMTRACK'
|
|
|
|
# implies we have a mixdown
|
|
if @state.media.jamTrackMixdown.id?
|
|
jamTrackTypeHeader = 'Custom Mix'
|
|
else
|
|
jamTrackTypeHeader = 'Full JamTrack'
|
|
|
|
header = `
|
|
<div className="header">
|
|
<h3>{mediaType}: {mediaName}</h3>
|
|
<h4>{jamTrackTypeHeader}</h4>
|
|
{customMixName}
|
|
</div>`
|
|
|
|
|
|
|
|
else if @state.media.mediaSummary.backingTrackOpen
|
|
mediaType = "Audio File"
|
|
mediaName = context.JK.getNameOfFile(@state.media.backingTracks[0].shortFilename)
|
|
closeLinkText = 'CLOSE AUDIO FILE'
|
|
header = `<h3>{mediaType}: {mediaName}</h3>`
|
|
extraControls =
|
|
`<div>
|
|
<div className="field">
|
|
<input type="checkbox" name="loop" /><label htmlFor="loop">Loop audio file playback</label>
|
|
</div>
|
|
<br className="clearall"/>
|
|
</div>`
|
|
else if @state.media.mediaSummary.metronomeOpen
|
|
mediaType = "Metronome"
|
|
closeLinkText = 'CLOSE METRONOME'
|
|
header = `<h3>Metronome</h3>`
|
|
extraControls =
|
|
`<div>
|
|
<a className="display-metronome" onClick={this.showMetronome}>Display visual metronome</a>
|
|
</div>`
|
|
else
|
|
mediaType = ""
|
|
|
|
if helpLink?
|
|
helpButton = `<a className="help-link button-grey" href={helpLink} onClick={this.help}>HELP</a>`
|
|
|
|
if @state.media.mediaSummary.isOpener || @state.media.mediaSummary.metronomeOpen
|
|
enabledClass = 'close-link button-orange'
|
|
else
|
|
enabledClass = 'close-link button-grey'
|
|
closeLinkText = 'ONLY OPENER CAN CLOSE'
|
|
|
|
`<div className="media-controls-popup">
|
|
{header}
|
|
<MediaControls unloaded={this.unloaded} disabled={this.state.downloadingJamTrack || this.disableLoading}/>
|
|
{extraControls}
|
|
<div className="actions">
|
|
{helpButton}
|
|
<a className={enabledClass} onClick={this.close}>{closeLinkText}</a>
|
|
</div>
|
|
</div>`
|
|
|
|
|
|
windowUnloaded: (event) ->
|
|
logger.debug('PopupMediaControls: window uploaded')
|
|
SessionActions.log({msg: 'PopupMediaControls: window uploaded', event: event})
|
|
@unloaded = true
|
|
window.unloaded = true
|
|
|
|
@props.windowUnloaded()
|
|
|
|
MixerActions.closeMedia(false) unless window.DontAutoCloseMedia
|
|
|
|
toggleMyMixes: (e) ->
|
|
e.preventDefault()
|
|
@setState({showMyMixes: !@state.showMyMixes})
|
|
|
|
toggleCustomMixes: (e) ->
|
|
e.preventDefault()
|
|
|
|
@setState({showCustomMixes: !@state.showCustomMixes})
|
|
|
|
mixdownPlay: (mixdown, e) ->
|
|
@setState({editingMixdownId: null})
|
|
|
|
e.preventDefault()
|
|
|
|
if @disableLoading
|
|
alert('Certain actions are disabled while a track is being loaded.')
|
|
return
|
|
|
|
# make this package the active one
|
|
JamTrackMixdownActions.openMixdown(mixdown)
|
|
|
|
jamTrackPlay: (jamtrack, e) ->
|
|
e.preventDefault()
|
|
# user wants to select the full track
|
|
|
|
if @disableLoading
|
|
alert('Certain actions are disabled while a track is being loaded.')
|
|
return
|
|
|
|
JamTrackActions.activateNoMixdown(jamtrack)
|
|
|
|
verificationCheck: () ->
|
|
if @state.user?.email_needs_verification
|
|
alert("Check your email and click the verification link. Close and re-open this JamTrack, and try again.")
|
|
|
|
return @state.user?.email_needs_verification
|
|
|
|
jamTrackDownload: (jamTrack, e) ->
|
|
e.preventDefault()
|
|
|
|
return if @verificationCheck()
|
|
|
|
if !@state.jamTrackState.jamTrack.can_download
|
|
e.preventDefault()
|
|
if confirm("You have not purchased the rights to download a JamTrack. Add them to your shopping cart?")
|
|
@addUpgradeToCart(@state.jamTrackState.jamTrack)
|
|
return
|
|
|
|
new window.Fingerprint2().get((result, components) => (
|
|
redirectTo = "/api/jamtracks/#{jamTrack.id}/stems/master/download.mp3?file_type=mp3&download=1&mark=#{result}"
|
|
redirectTo = encodeURIComponent(redirectTo)
|
|
AppActions.openExternalUrl(window.location.protocol + '//' + window.location.host + "/signin?redirect-to=#{redirectTo}")
|
|
))
|
|
|
|
addUpgradeToCart: (jamtrack) ->
|
|
console.log("adding upgrade to cart")
|
|
|
|
params = {id: jamtrack.id, variant: 'download'}
|
|
|
|
rest.addJamtrackToShoppingCart(params).done((response) =>
|
|
console.log("added item to shopping cart. fast_redeem? " + response.fast_redeem)
|
|
if response.fast_reedem
|
|
if context.JK.currentUserId?
|
|
AppActions.openExternalUrl(window.location.protocol + '//' + window.location.host + '/client#/redeemComplete')
|
|
else
|
|
AppActions.openExternalUrl(window.location.protocol + '//' + window.location.host + '/client#/redeemSignup')
|
|
else
|
|
AppActions.openExternalUrl(window.location.protocol + '//' + window.location.host + '/client#/shoppingCart')
|
|
|
|
).fail(((jqxhr) =>
|
|
|
|
handled = false
|
|
if jqxhr.status == 422
|
|
body = JSON.parse(jqxhr.responseText)
|
|
if body.errors?.cart_id?[0] == 'has already been taken'
|
|
console.log("already taken, just show shopping cart")
|
|
AppActions.openExternalUrl(window.location.protocol + '//' + window.location.host + '/client#/shoppingCart')
|
|
return
|
|
else if body.errors && body.errors.base
|
|
handled = true
|
|
alert("You can not have a mix of free and non-free items in your shopping cart.\n\nIf you want to add this new item to your shopping cart, then clear out all current items by clicking on the shopping cart icon and clicking 'delete' next to each item.")
|
|
if !handled
|
|
alert("Error adding to shoppig cart. " + jqxhr.responseText)
|
|
))
|
|
|
|
stemChanged:() ->
|
|
stemDownload: (e) ->
|
|
e.preventDefault()
|
|
|
|
return if @verificationCheck()
|
|
|
|
$select = $(this.getDOMNode()).find('.active-stem-select')
|
|
|
|
if !@state.jamTrackState.jamTrack.can_download
|
|
e.preventDefault()
|
|
if confirm("You have not purchased the rights to download a JamTrack. Add them to your shopping cart?")
|
|
@addUpgradeToCart(@state.jamTrackState.jamTrack)
|
|
return
|
|
|
|
selectedTrackId = $select.val()
|
|
|
|
if !selectedTrackId? || selectedTrackId == ''
|
|
alert("You must select a track in order to download.")
|
|
else
|
|
e.preventDefault()
|
|
|
|
new window.Fingerprint2().get((result, components) => (
|
|
redirectTo = "/api/jamtracks/#{@state.jamTrackState.jamTrack.id}/stems/#{selectedTrackId}/download.mp3?file_type=mp3&download=1&mark=#{result}"
|
|
redirectTo = encodeURIComponent(redirectTo)
|
|
AppActions.openExternalUrl(window.location.protocol + '//' + window.location.host + "/signin?redirect-to=#{redirectTo}")
|
|
))
|
|
|
|
|
|
onEditKeydown: (mixdown, e) ->
|
|
logger.debug("on edit keydown", e)
|
|
if e.keyCode == 13 # enter
|
|
@mixdownSave(mixdown, e)
|
|
else if e.keyCode == 27 # esc
|
|
@setState({editingMixdownId: null})
|
|
|
|
mixdownEdit: (mixdown) ->
|
|
@setState({editingMixdownId: mixdown.id})
|
|
|
|
mixdownSave: (mixdown, e) ->
|
|
e.preventDefault()
|
|
$input = $(this.getDOMNode()).find('input.edit-name')
|
|
newValue = $input.val()
|
|
logger.debug("editing mixdown name to be: " + newValue)
|
|
JamTrackMixdownActions.editMixdown({id: mixdown.id, name: newValue})
|
|
@setState({editingMixdownId: null})
|
|
|
|
mixdownDelete: (mixdown) ->
|
|
if @state.editingMixdownId?
|
|
@setState({editingMixdownId:null})
|
|
return
|
|
|
|
if confirm("Delete this custom mix?")
|
|
JamTrackMixdownActions.deleteMixdown(mixdown)
|
|
|
|
downloadMixdownReady: (mixdown, e) ->
|
|
e.preventDefault()
|
|
|
|
return if @verificationCheck()
|
|
|
|
# find the mp3 package for browser state check
|
|
for mixdown_package in mixdown.packages
|
|
if mixdown_package.file_type == 'mp3'
|
|
browserPackage = mixdown_package
|
|
break
|
|
|
|
if browserPackage?.signing_state == 'SIGNED'
|
|
new window.Fingerprint2().get((result, components) => (
|
|
redirectTo = "/api/mixdowns/#{mixdown.id}/download.mp3?file_type=mp3&sample_rate=48&download=1&mark=#{result}"
|
|
redirectTo = encodeURIComponent(redirectTo)
|
|
AppActions.openExternalUrl(window.location.protocol + '//' + window.location.host + "/signin?redirect-to=#{redirectTo}")
|
|
))
|
|
else
|
|
JamTrackMixdownActions.openDownloader(mixdown)
|
|
|
|
mixdownError: (mixdown) ->
|
|
|
|
myPackage = mixdown.myPackage
|
|
|
|
if myPackage?
|
|
switch myPackage.signing_state
|
|
when 'QUIET_TIMEOUT'
|
|
action = 'Custom mix never got created. Retry?'
|
|
when 'QUEUED_TIMEOUT'
|
|
action = 'Custom mix was never built. Retry?'
|
|
when 'SIGNING_TIMEOUT'
|
|
action = 'Custom mix took took long to build. Retry?'
|
|
when 'ERROR'
|
|
action = 'Custom mix failed to build. Retry?'
|
|
else
|
|
action = 'Custom mix never got created. Retry?'
|
|
|
|
return unless action?
|
|
|
|
if confirm(action)
|
|
JamTrackMixdownActions.enqueueMixdown(mixdown, @enqueueDone)
|
|
|
|
enqueueDone: (enqueued) ->
|
|
@promptEstimate(enqueued)
|
|
|
|
promptEstimate: (enqueued) ->
|
|
time = enqueued.queue_time
|
|
|
|
if time == 0
|
|
alert("Your custom mix will take about 1 minute to be created.")
|
|
else
|
|
guess = Math.ceil(time / 60.0)
|
|
if guess == 1
|
|
msg = '1 minute'
|
|
else
|
|
msg = "#{guess} minutes"
|
|
alert("Your custom mix will take about #{msg} to be created.")
|
|
|
|
|
|
createMix: (e) ->
|
|
e.preventDefault()
|
|
|
|
return if @state.creatingMix
|
|
|
|
$root = $(@getDOMNode())
|
|
|
|
name = $root.find('input[name="mix-name"]').val()
|
|
speed = $root.find('select[name="mix-speed"]').val()
|
|
pitch = $root.find('select[name="mix-pitch"]').val()
|
|
|
|
if @state.jamTrackState.jamTrack?.activeMixdown?
|
|
@setState({createMixdownErrors: {errors: {'Full JamTrack': ['must be selected']}}})
|
|
return
|
|
|
|
if name == null || name == ''
|
|
@setState({createMixdownErrors: {errors: {'Mix Name': ["can't be blank"]}}})
|
|
return
|
|
|
|
# sanitize junk out of speed/pitch
|
|
if speed == '' || speed.indexOf('separator') > -1
|
|
speed = undefined
|
|
else
|
|
speed = parseInt(speed)
|
|
if pitch == '' || pitch.indexOf('separator') > -1
|
|
pitch = undefined
|
|
else
|
|
pitch = parseInt(pitch)
|
|
|
|
mixdown = {jamTrackID: @state.jamTrackState.jamTrack.id, name: name, settings: {speed:speed, pitch: pitch}}
|
|
|
|
JamTrackMixdownActions.createMixdown(mixdown, @createMixdownDone, @createMixdownFail)
|
|
|
|
@setState({creatingMixdown: true, createMixdownErrors: null})
|
|
|
|
createMixdownDone: (created) ->
|
|
logger.debug("created (within PopupMediaControls)", created)
|
|
# automatically close the create custom mix area
|
|
@setState({creatingMixdown: false, showCustomMixes: false, showMyMixes: true})
|
|
|
|
@promptEstimate(created)
|
|
|
|
createMixdownFail: (jqXHR) ->
|
|
logger.debug("create mixdown fail (within PopupMediaControls)", jqXHR.status)
|
|
@setState({creatingMixdown: false})
|
|
if jqXHR.status == 422
|
|
response = JSON.parse(jqXHR.responseText)
|
|
logger.warn("failed to create mixdown", response, jqXHR.responseText)
|
|
|
|
@setState({createMixdownErrors: response})
|
|
|
|
|
|
componentDidMount: () ->
|
|
|
|
$(window).unload(@windowUnloaded)
|
|
|
|
$(window).on('beforeunload', (event) =>
|
|
# Cancel the event as stated by the standard.
|
|
#event.preventDefault()
|
|
#Chrome requires returnValue to be set.
|
|
#event.returnValue = ''
|
|
#SessionActions.log({msg:"beforeunload", event: event})
|
|
)
|
|
|
|
@root = jQuery(this.getDOMNode())
|
|
|
|
$loop = @root.find('input[name="loop"]')
|
|
context.JK.checkbox($loop)
|
|
|
|
$loop.on('ifChecked', () =>
|
|
# it doesn't matter if you do personal or master, because backend just syncs both
|
|
MixerActions.loopChanged(@state.media.backingTracks[0].mixers.personal.mixer, true)
|
|
)
|
|
$loop.on('ifUnchecked', () =>
|
|
# it doesn't matter if you do personal or master, because backend just syncs both
|
|
MixerActions.loopChanged(@state.media.backingTracks[0].mixers.personal.mixer, false)
|
|
)
|
|
|
|
@resizeWindow()
|
|
|
|
# this is necessary due to whatever the client's rendering behavior is.
|
|
setTimeout(@resizeWindow, 300)
|
|
|
|
componentDidUpdate: () ->
|
|
@resizeWindow()
|
|
setTimeout(@resizeWindow, 1000)
|
|
|
|
shouldComponentUpdate: () ->
|
|
return !@unloaded
|
|
|
|
resizeWindow: () =>
|
|
$container = $('#minimal-container')
|
|
width = $container.width()
|
|
height = $container.height()
|
|
|
|
# there is 20px or so of unused space at the top of the page. can't figure out why it's there. (above #minimal-container)
|
|
#mysteryTopMargin = 20
|
|
mysteryTopMargin = 0
|
|
# deal with chrome in real browsers
|
|
offset = (window.outerHeight - window.innerHeight) + mysteryTopMargin
|
|
|
|
# handle native client chrome that the above outer-inner doesn't catch
|
|
#if navigator.userAgent.indexOf('JamKazam') > -1
|
|
|
|
#offset += 25
|
|
|
|
window.resizeTo(width, height + offset)
|
|
|
|
computeDisableLoading: (state) ->
|
|
@disableLoading = false
|
|
|
|
return unless nextState?
|
|
|
|
selectedMixdown = state?.jamTrackState?.jamTrack?.activeMixdown
|
|
|
|
mixdownDownloading = false
|
|
if selectedMixdown?
|
|
switch selectedMixdown.client_state
|
|
when 'keying'
|
|
mixdownDownloading = true
|
|
when 'downloading'
|
|
mixdownDownloading = true
|
|
|
|
|
|
@disableLoading = SessionStore.downloadingJamTrack || mixdownDownloading
|
|
|
|
componentWillMount: () ->
|
|
@computeDisableLoading(@state)
|
|
|
|
componentWillUpdate: (nextProps, nextState) ->
|
|
@computeDisableLoading(nextState)
|
|
|
|
|
|
}) |