960 lines
35 KiB
CoffeeScript
960 lines
35 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)
|
|
# this check ensures we attempt to listen if this component is created in a popup
|
|
reactContext = if window.opener? then window.opener else window
|
|
# make sure this is actually us opening the window, not someone else (by checking for MixerStore)
|
|
if window.opener?
|
|
try
|
|
m = window.opener.MixerStore
|
|
catch e
|
|
reactContext = window
|
|
|
|
if true # /iPhone|iPad|iPod|android/i.test(navigator.userAgent)
|
|
# iPad or iPhone
|
|
reactContext = window
|
|
|
|
AppActions = reactContext.AppActions
|
|
JamTrackPlayerActions = reactContext.JamTrackPlayerActions
|
|
JamTrackPlayerStore = reactContext.JamTrackPlayerStore
|
|
UserStore = reactContext.UserStore
|
|
|
|
mixins.push(Reflux.listenTo(JamTrackPlayerStore, 'onJamTrackPlayerStoreChanged'))
|
|
mixins.push(Reflux.listenTo(UserStore, 'onUserChanged'))
|
|
|
|
|
|
@PopupJamTrackPlayer = React.createClass({
|
|
|
|
mixins: mixins
|
|
|
|
computeWeight: (jam_track_track) ->
|
|
weight = switch
|
|
when jam_track_track.track_type == 'Master' then 0
|
|
when jam_track_track.track_type == 'Click' then 10000
|
|
else
|
|
jam_track_track.position
|
|
|
|
onJamTrackPlayerStoreChanged: (changes) ->
|
|
#logger.debug("PopupMediaControls: jamtrack changed", changes)
|
|
@setState({jamTrackState: changes})
|
|
|
|
onUserChanged: (changes) ->
|
|
@setState({user: changes.user})
|
|
|
|
showMetronome: (e) ->
|
|
e.preventDefault()
|
|
|
|
SessionActions.showNativeMetronomeGui()
|
|
|
|
getInitialState: () ->
|
|
state = {}
|
|
state.jamTrackState = JamTrackPlayerStore.getState()
|
|
state.showCustomMixes = true
|
|
state.showMyMixes = true
|
|
state.user = UserStore.getState().user
|
|
return state
|
|
|
|
close: () ->
|
|
window.close()
|
|
|
|
help: (e) ->
|
|
e.preventDefault()
|
|
|
|
AppActions.openExternalUrl($(e.target).attr('href'))
|
|
|
|
render: () ->
|
|
closeLinkText = null
|
|
header = null
|
|
extraControls = null
|
|
|
|
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
|
|
selectedStem = jamTrack?.activeStem
|
|
|
|
if selectedMixdown?
|
|
jamTrackTypeHeader = 'Custom Mix'
|
|
|
|
disabled = true
|
|
if selectedMixdown.client_state?
|
|
switch selectedMixdown.client_state
|
|
when 'download_fail'
|
|
customMixName = `<h5>{selectedMixdown.name}<img src="/assets/content/icon-mix-fail@2X.png"/></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 selectedStem?
|
|
if selectedStem.instrument
|
|
instrumentId = selectedStem.instrument.id
|
|
instrumentDescription = selectedStem.instrument.description
|
|
part = "(#{selectedStem.part})" if selectedStem.part? && selectedStem.part != instrumentDescription
|
|
part = '' unless part
|
|
|
|
jamTrackTypeHeader = 'Track'
|
|
|
|
trackName = "#{instrumentDescription} #{part}"
|
|
|
|
disabled = true
|
|
if selectedStem.client_state?
|
|
switch selectedStem.client_state
|
|
when 'downloading'
|
|
customMixName = `<h5>Loading {trackName}... <img src="/assets/shared/spinner.gif"/></h5>`
|
|
when 'download_fail'
|
|
customMixName = `<h5>{trackName}<img src="/assets/content/icon-mix-fail@2X.png"/></h5>`
|
|
when 'ready'
|
|
customMixName = `<h5>{trackName}</h5>`
|
|
disabled = false
|
|
else
|
|
customMixName = `<h5>{trackName}<img src="/assets/content/icon-mix-fail@2X.png"/></h5>`
|
|
|
|
else
|
|
if jamTrack?.client_state == 'downloading'
|
|
downloader = `<img src="/assets/shared/spinner.gif"/>`
|
|
else if jamTrack?.client_state == 'download_fail'
|
|
downloader = `<img src="/assets/content/icon-mix-fail@2X.png"/>`
|
|
|
|
jamTrackTypeHeader = `<span>Full JamTrack {downloader}</span>`
|
|
|
|
header = `
|
|
<div className="header">
|
|
<h3>{mediaType}: {mediaName}</h3>
|
|
<h4>{jamTrackTypeHeader}</h4>
|
|
{customMixName}
|
|
</div>`
|
|
|
|
myMixes = null
|
|
if @state.showMyMixes
|
|
|
|
if jamTrack?
|
|
myMixdowns = []
|
|
|
|
boundPlayClick = this.jamTrackPlay.bind(this, jamTrack);
|
|
boundDownloadClick = this.jamTrackDownload.bind(this, jamTrack);
|
|
|
|
active = jamTrack.last_mixdown_id == null && jamTrack.last_stem_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}/>
|
|
<a href={this.createJamTrackUrl(jamTrack)} target="_blank" onClick={boundDownloadClick}><img
|
|
src="/assets/content/icon_download@2X.png" className="mixdown-download"/></a>
|
|
<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);
|
|
|
|
boundDownloadNotReadyClick = this.downloadNotReady.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}/>`
|
|
|
|
# create hidden objects to deal with alginment issues using a table
|
|
if !editIcon
|
|
editIcon = `<img src="/assets/content/icon-delete@2X.png" style={{visibility:'hidden'}}
|
|
className="mixdown-edit" onClick={false}/>`
|
|
|
|
download = `<a onClick={boundDownloadReadyClick} href={this.downloadMixdownUrl(mixdown)} target="_blank"><img
|
|
src="/assets/content/icon_download@2X.png" className="mixdown-download"/></a>`
|
|
|
|
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>`
|
|
|
|
active = jamTrack.last_stem_id?
|
|
trackOptions = []
|
|
|
|
jamTrack.tracks.sort((a, b) =>
|
|
aWeight = @computeWeight(a)
|
|
bWeight = @computeWeight(b)
|
|
return aWeight - bWeight
|
|
)
|
|
|
|
|
|
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>`)
|
|
|
|
boundStemActivateClick = this.activateStem.bind(this)
|
|
boundStemPlayClick = this.downloadStem.bind(this)
|
|
boundStemChange = this.stemChanged.bind(this)
|
|
|
|
myMixdowns.push `
|
|
<div key={track.id} className={classNames({'stem-track' : true, 'mixdown-display': true, 'active' : active})}>
|
|
<div className="mixdown-name">
|
|
<select className="active-stem-select" name="active-stem-select" onChange={boundStemChange}
|
|
defaultValue={jamTrack.last_stem_id}>
|
|
<option key="null" value="">or select a track</option>
|
|
{trackOptions}
|
|
</select>
|
|
</div>
|
|
<div className="mixdown-actions">
|
|
<img src="/assets/content/icon_open@2X.png" className="mixdown-play" onClick={boundStemActivateClick}/>
|
|
<a href={this.createStemUrl(jamTrack.id, jamTrack.last_stem_id)} target="_blank"
|
|
onClick={boundStemPlayClick}><img src="/assets/content/icon_download@2X.png"
|
|
className="mixdown-download"/></a>
|
|
<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>`
|
|
else
|
|
# nothing
|
|
|
|
mixControls = null
|
|
|
|
if @state.showCustomMixes
|
|
|
|
tracks = []
|
|
if jamTrack?
|
|
for track in jamTrack.tracks
|
|
if track.track_type == 'Track' || track.track_type == 'Click'
|
|
if track.track_type == 'Click'
|
|
instrumentId = track.instrument.id
|
|
instrumentDescription = 'Clicktrack'
|
|
part = ''
|
|
else if track.instrument
|
|
instrumentId = track.instrument.id
|
|
instrumentDescription = track.instrument.description
|
|
if track.part? && track.part != instrumentDescription
|
|
part = "(#{track.part})"
|
|
else
|
|
part = ''
|
|
|
|
|
|
tracks.push(`
|
|
<tr className="stem">
|
|
<td><img src={context.JK.getInstrumentIcon24(instrumentId)}
|
|
className="instrument-icon"/> {instrumentDescription} {part}</td>
|
|
<td className="mute"><input type="checkbox" className="stem-mute" data-stem-id={track.id}
|
|
defaultChecked={track.track_type == 'Click'}/></td>
|
|
</tr>`)
|
|
|
|
if jamTrack?.jmep?.Events? && jamTrack.jmep.Events[0].metronome?
|
|
# tap-in detected; show user tap-in option
|
|
tracks.push(`
|
|
<tr className="stem">
|
|
<td><img src={context.JK.getInstrumentIcon24('computer')} className="instrument-icon"/> Count-in</td>
|
|
<td className="mute"><input type="checkbox" className="stem-mute" data-stem-id="count-in"/></td>
|
|
</tr>`)
|
|
|
|
stems = `<div key="stems" className="stems">
|
|
<table>
|
|
<thead>
|
|
<col align="left"/>
|
|
<col align="right"/>
|
|
<tr>
|
|
<th>TRACKS</th>
|
|
<th className="mute">mute</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{tracks}
|
|
</tbody>
|
|
</table>
|
|
</div>`
|
|
|
|
|
|
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})
|
|
|
|
mixControls = `
|
|
<div key="create-mix" className="create-mix">
|
|
<p>Mute or unmute any tracks 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>
|
|
{stems}
|
|
<div className="field">
|
|
<label>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>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>`
|
|
|
|
|
|
if @state.showMyMixes
|
|
showMyMixesText = `<a className="show-hide-my-mixes" onClick={this.toggleMyMixes}>hide my mixes
|
|
<div className="details-arrow arrow-up"/>
|
|
</a>`
|
|
else
|
|
showMyMixesText = `<a className="show-hide-my-mixes" onClick={this.toggleMyMixes}>show my mixes
|
|
<div className="details-arrow arrow-down"/>
|
|
</a>`
|
|
|
|
if @state.showCustomMixes
|
|
showMixControlsText = `<a className="show-hide-mix-controls" onClick={this.toggleCustomMixes}>hide mix controls
|
|
<div className="details-arrow arrow-up"/>
|
|
</a>`
|
|
else
|
|
showMixControlsText = `<a className="show-hide-mix-controls" 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={false}>
|
|
{mixControls}
|
|
</ReactCSSTransitionGroup>
|
|
</div>`
|
|
|
|
|
|
if helpLink?
|
|
helpButton = `<a className="help-link button-grey" href={helpLink} target="_blank">HELP</a>`
|
|
|
|
`<div className="jamtrack-player-main">
|
|
<div className="jamtrack-player-controls">
|
|
<div className="media-header">
|
|
jamtracks web player
|
|
</div>
|
|
<div className="media-controls-popup">
|
|
{header}
|
|
<BrowserMediaControls disabled={this.disableLoading}/>
|
|
{extraControls}
|
|
<div className="actions">
|
|
{helpButton}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div className="helpful-resources">
|
|
<div className="helpful-header">
|
|
helpful resources
|
|
</div>
|
|
<div className="helpful-section">
|
|
<a href="https://jamkazam.desk.com/customer/portal/articles/2166273-playing-your-jamtracks-in-a-browser" target="_blank">read a web player help article</a><br/>
|
|
that explains how to use all the features of this web player
|
|
</div>
|
|
<div className="helpful-section">
|
|
<a href="/client#/jamtrack" target="_blank">go to JamTracks homepage</a><br/>
|
|
where you can access all of your JamTracks, search for more JamTracks, and more
|
|
</div>
|
|
<div className="helpful-section">
|
|
<a href="https://itunes.apple.com/us/app/jamtracks/id1060927816?mt=8" target="_blank">download our free iOS app</a><br/>
|
|
that you can use to play with your JamTracks on your iPhone, iPad, or iPod Touch
|
|
</div>
|
|
<div className="helpful-section">
|
|
<a href="/downloads" target="_blank">download our free Mac or Windows app</a><br/>
|
|
that gives you more powerful features to do more amazing things with your JamTracks
|
|
</div>
|
|
<div className="helpful-section">
|
|
<a href="https://jamkazam.desk.com/customer/en/portal/articles/2126113-playing-your-jamtracks-in-our-free-windows-or-mac-app" target="_blank">review a list of help articles on the Mac/Win app</a><br/>
|
|
to understand all the things you can do with our free desktop app
|
|
</div>
|
|
<div className="helpful-section">
|
|
<a onClick={this.seeAllByArtist} target="_blank">see more JamTracks by this artist</a><br/>
|
|
to check out other songs you might like
|
|
</div>
|
|
<div className="helpful-section">
|
|
<a href="https://s3.amazonaws.com/jamkazam-public/public/lists/all-jamkazam-jamtracks.pdf" target="_blank">download our complete JamTracks catalog</a><br/>
|
|
to browse or search through all the music we have in a convenient PDF file
|
|
</div>
|
|
</div>
|
|
</div>`
|
|
|
|
seeAllByArtist: (e) ->
|
|
e.preventDefault()
|
|
jamTrack = @state.jamTrackState?.jamTrack
|
|
|
|
console.log("seeAllByArtist context", jamTrack)
|
|
window.open('/client?artist=' + encodeURIComponent(jamTrack.original_artist) + '#/jamtrack/search')
|
|
|
|
windowUnloaded: () ->
|
|
JamTrackPlayerActions.windowUnloaded() unless window.DontAutoCloseMedia
|
|
|
|
toggleMyMixes: (e) ->
|
|
e.preventDefault()
|
|
@setState({showMyMixes: !@state.showMyMixes})
|
|
|
|
toggleCustomMixes: (e) ->
|
|
e.preventDefault()
|
|
|
|
|
|
@setState({showCustomMixes: !@state.showCustomMixes})
|
|
|
|
downloadNotReady: (mixdown, e) ->
|
|
alert("This mix is not yet ready to download.")
|
|
e.preventDefault()
|
|
|
|
verificationCheck: () ->
|
|
if @state.user?.email_needs_verification
|
|
alert("Check your email and click the verification link. Refresh this page when done, and try again.")
|
|
|
|
return @state.user?.email_needs_verification
|
|
|
|
downloadMixdownReady: (mixdown, e) ->
|
|
if @verificationCheck()
|
|
e.preventDefault()
|
|
return
|
|
|
|
if mixdown.myPackage?.signing_state == 'SIGNED'
|
|
if /iPhone|iPad|iPod/i.test(navigator.userAgent)
|
|
# fall through
|
|
else
|
|
e.preventDefault()
|
|
new window.Fingerprint2().get((result, components) => (
|
|
iframe = document.createElement("iframe")
|
|
iframe.src = @downloadMixdownUrl(mixdown, result)
|
|
iframe.style.display = "none"
|
|
document.body.appendChild(iframe);
|
|
))
|
|
else
|
|
alert("The mix is not yet ready to download")
|
|
|
|
downloadMixdownUrl: (mixdown, result) ->
|
|
window.location.protocol + '//' + window.location.host + "/api/mixdowns/#{mixdown.id}/download.mp3?file_type=mp3&sample_rate=48&download=1&mark=#{result}"
|
|
|
|
activateStem: (e) ->
|
|
e.preventDefault()
|
|
|
|
return if @verificationCheck()
|
|
|
|
$select = $(this.getDOMNode()).find('.active-stem-select')
|
|
|
|
selectedTrackId = $select.val()
|
|
|
|
if !selectedTrackId? || selectedTrackId == ''
|
|
alert("You must pick a track from the dropdown in order to play it.")
|
|
else
|
|
@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
|
|
JamTrackPlayerActions.openStem(selectedTrackId)
|
|
|
|
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?
|
|
window.open('/client#/redeemComplete')
|
|
else
|
|
window.open('/client#/redeemSignup')
|
|
else
|
|
window.open('/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")
|
|
window.open('/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)
|
|
))
|
|
|
|
downloadStem: (e) ->
|
|
if @verificationCheck()
|
|
e.preventDefault()
|
|
return
|
|
|
|
$select = $(this.getDOMNode()).find('.active-stem-select')
|
|
|
|
selectedTrackId = $select.val()
|
|
|
|
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
|
|
|
|
if !selectedTrackId? || selectedTrackId == ''
|
|
e.preventDefault()
|
|
alert("You must select a track in order to download and also click the folder icon.")
|
|
else
|
|
if /iPhone|iPad|iPod/i.test(navigator.userAgent)
|
|
if @state.jamTrackState.jamTrack?.last_stem_id
|
|
# fall through
|
|
else
|
|
e.preventDefault()
|
|
alert("You must select a track in order to download and also click the folder icon.")
|
|
else
|
|
e.preventDefault()
|
|
|
|
try
|
|
new window.Fingerprint2().get((result, components) => (
|
|
iframe = document.createElement("iframe")
|
|
iframe.src = @createStemUrl(@state.jamTrackState.jamTrack.id, selectedTrackId, result)
|
|
iframe.style.display = "none"
|
|
document.body.appendChild(iframe);
|
|
))
|
|
catch error
|
|
logger.error("not working: ", error)
|
|
alert("Unable to download. Please try a different browser.")
|
|
|
|
|
|
createStemUrl: (jamTrackId, stemId, result) ->
|
|
window.location.protocol + '//' + window.location.host + "/api/jamtracks/#{jamTrackId}/stems/#{stemId}/download.mp3?file_type=mp3&download=1&mark=#{result}"
|
|
|
|
stemChanged: () ->
|
|
|
|
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
|
|
JamTrackPlayerActions.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
|
|
|
|
JamTrackPlayerActions.activateNoMixdown(jamtrack)
|
|
|
|
jamTrackDownload: (jamTrack, e) ->
|
|
if @verificationCheck()
|
|
e.preventDefault()
|
|
return
|
|
|
|
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
|
|
|
|
if /iPhone|iPad|iPod/i.test(navigator.userAgent)
|
|
# fall through
|
|
|
|
else
|
|
e.preventDefault()
|
|
|
|
new window.Fingerprint2().get((result, components) => (
|
|
iframe = document.createElement("iframe")
|
|
iframe.src = @createJamTrackUrl(jamTrack, result)
|
|
iframe.style.display = "none"
|
|
document.body.appendChild(iframe);
|
|
))
|
|
|
|
createJamTrackUrl: (jamTrack, result) ->
|
|
window.location.protocol + '//' + window.location.host + "/api/jamtracks/#{jamTrack.id}/stems/master/download.mp3?file_type=mp3&download=1&mark=#{result}"
|
|
|
|
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)
|
|
JamTrackPlayerActions.editMixdown({id: mixdown.id, name: newValue})
|
|
@setState({editingMixdownId: null})
|
|
|
|
mixdownDelete: (mixdown) ->
|
|
if @state.editingMixdownId?
|
|
@setState({editingMixdownId: null})
|
|
return
|
|
|
|
if confirm("Delete this custom mix?")
|
|
JamTrackPlayerActions.deleteMixdown(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)
|
|
JamTrackPlayerActions.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 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)
|
|
|
|
count_in = false
|
|
|
|
# get mute state of all tracks
|
|
$mutes = $(@getDOMNode()).find('.stems .stem .stem-mute')
|
|
|
|
tracks = []
|
|
$mutes.each((i, mute) =>
|
|
$mute = $(mute)
|
|
stemId = $mute.attr('data-stem-id')
|
|
muted = $mute.is(':checked')
|
|
|
|
if stemId == 'count-in'
|
|
count_in = !muted
|
|
else
|
|
tracks.push({id: stemId, mute: muted})
|
|
)
|
|
|
|
mixdown = {
|
|
jamTrackID: @state.jamTrackState.jamTrack.id,
|
|
name: name,
|
|
settings: {speed: speed, pitch: pitch, "count-in": count_in, tracks: tracks}
|
|
}
|
|
|
|
JamTrackPlayerActions.createMixdown(mixdown, @createMixdownDone, @createMixdownFail)
|
|
|
|
@setState({creatingMixdown: true, createMixdownErrors: null})
|
|
|
|
createMixdownDone: (created) ->
|
|
logger.debug("created (within PopupMediaControls)", created)
|
|
@setState({creatingMixdown: false, showCustomMixes: true, 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})
|
|
|
|
fetchJamTrackInfo: () ->
|
|
rest.getJamTrack({plan_code: gon.jamtrack_id})
|
|
.done((response) =>
|
|
JamTrackPlayerActions.open(response, false);
|
|
@fetchUserInfo()
|
|
)
|
|
.fail((jqXHR) =>
|
|
alert("Unable to fetch JamTrack information. Try logging in.")
|
|
)
|
|
|
|
fetchUserInfo: () ->
|
|
rest.getUserDetail()
|
|
.done((response) =>
|
|
rest.postUserEvent({name: 'jamtrack_web_player_open'})
|
|
context.stats.write('web.jamtrack_web_player.open', {
|
|
value: 1,
|
|
user_id: context.JK.currentUserId,
|
|
user_name: context.JK.currentUserName
|
|
})
|
|
|
|
if !response.first_opened_jamtrack_web_player
|
|
setTimeout((() =>
|
|
logger.debug("first time user")
|
|
rest.userOpenedJamTrackWebPlayer()
|
|
$root = $(@getDOMNode())
|
|
#context.JK.prodBubble($root.find('.create-mix-btn'), 'first-time-jamtrack-web-player', {}, {positions:['left'], offsetParent: $root})
|
|
), 1500)
|
|
)
|
|
|
|
|
|
|
|
componentDidMount: () ->
|
|
$(window).unload(@windowUnloaded)
|
|
|
|
@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)
|
|
|
|
@fetchJamTrackInfo()
|
|
|
|
componentDidUpdate: () ->
|
|
@resizeWindow()
|
|
setTimeout(@resizeWindow, 1000)
|
|
|
|
resizeWindow: () =>
|
|
return
|
|
$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(450, height + offset)
|
|
|
|
computeDisableLoading: (state) ->
|
|
@disableLoading = false
|
|
|
|
return unless nextState?
|
|
|
|
selectedMixdown = state?.jamTrackState?.jamTrack?.activeMixdown
|
|
|
|
mixdownDownloading = false
|
|
if selectedMixdown?
|
|
switch selectedMixdown.client_state
|
|
when 'downloading'
|
|
mixdownDownloading = true
|
|
|
|
selectedStem = state?.jamTrackState?.jamTrack?.activeStem
|
|
|
|
stemDownloading = false
|
|
if selectedStem?
|
|
switch selectedStem.client_state
|
|
when 'downloading'
|
|
stemDownloading = true
|
|
|
|
@disableLoading = state?.jamTrackState?.jamTrack?.client_state == 'downloading' || mixdownDownloading || stemDownloading
|
|
|
|
componentWillMount: () ->
|
|
@computeDisableLoading(@state)
|
|
|
|
componentWillUpdate: (nextProps, nextState) ->
|
|
@computeDisableLoading(nextState)
|
|
|
|
|
|
}) |