jam-cloud/web/app/assets/javascripts/react-components/PopupMediaControls.js.jsx.c...

356 lines
13 KiB
CoffeeScript

context = window
logger = context.JK.logger
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
SessionActions = window.opener.SessionActions
MediaPlaybackStore = window.opener.MediaPlaybackStore
MixerActions = window.opener.MixerActions
JamTrackMixdownActions = window.opener.JamTrackMixdownActions
JamTrackMixdownStore = window.opener.JamTrackMixdownStore
JamTrackMixdown = window.opener.JamTrackMixdown
MixerStore = window.opener.MixerStore
mixins.push(Reflux.listenTo(MixerStore, 'onMixersChanged'))
mixins.push(Reflux.listenTo(MediaPlaybackStore, 'onMediaStateChanged'))
mixins.push(Reflux.listenTo(JamTrackMixdownStore, 'onJamTrackMixdownChanged'))
mixins.push(Reflux.listenTo(JamTrackStore, 'onJamTrackChanged'))
@PopupMediaControls = React.createClass({
mixins: mixins
onMixersChanged: (sessionMixers) ->
session = sessionMixers.session
mixers = sessionMixers.mixers
# 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()
@setState(media: state)
onMediaStateChanged: (changes) ->
if changes.currentTimeChanged && @root?
@setState({time: changes.time})
onJamTrackMixdownChanged: (changes) ->
@setState({mixdown: changes})
onJamTrackChanged: (changes) ->
@setState({jamTrack: changes})
showMetronome: (e) ->
e.preventDefault()
SessionActions.showNativeMetronomeGui()
getInitialState: () ->
{media: @props.media, time: '0:00', mixdown: @props.mixdown, jamTrack: @props.jamTrack, creatingMixdown: false, createMixdownErrors: null}
close: () ->
window.close()
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} ({this.state.time})</h3>`
else if @state.jamTrack?
jamTrack = @state.jamTrack
mediaType = "JamTrack"
mediaName = @state.jamTrack.name
closeLinkText = 'CLOSE JAMTRACK'
selectedMixdown = null
if jamTrack.last_mixdown_id
selectedMixdowns = jamTrack.mixdowns.filter((mixdown) -> jamTrack.last_mixdown_id == mixdown.id)
selectedMixdown = selectedMixdowns[0] if selectedMixdowns.length > 0
if selectedMixdown?
jamTrackTypeHeader = 'Custom Mix'
customMixName = `<h5>selectedMixdown.name</h5>`
else
jamTrackTypeHeader = 'Full JamTrack'
header = `
<div className="header">
<h3>{mediaType}: {mediaName} ({this.state.time})</h3>
<h4>{jamTrackTypeHeader}</h4>
{customMixName}
</div>`
myMixes = null
if @state.showMyMixes
myMixdowns = []
for mixdown in jamTrack.mixdowns
myMixdowns.push << `
<div key={mixdown.id} className="mixdown">
{mixdown.name}
</div>`
myMixes = `<div 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'})
console.log("errorHtml", errorHtml)
createMixClasses = classNames({'button-orange' : true, 'create-mix-btn' : true, 'disabled' : @state.creatingMixdown})
mixControls = `
<div 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. Please note that changing the tempo or pitch of the JamTrack may take a long time, and won't be ready right away.</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>`
if @state.showMyMixes
showMyMixesText = 'hide my mixes'
else
showMyMixesText = 'show my mixes'
if @state.showCustomMixes
showMixControlsText = 'hide mix controls'
else
showMixControlsText = 'show mix controls'
extraControls = `
<div className="extra-controls">
<h4>My Mixes <a onClick={this.toggleMyMixes}>{showMyMixesText}</a></h4>
{myMixes}
<h4 className="custom-mix-header">Create Custom Mix <a onClick={this.toggleCustomMixes}>{showMixControlsText}</a></h4>
{mixControls}
</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} ({this.state.time})</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 = ""
`<div className="media-controls-popup">
{header}
<MediaControls />
{extraControls}
<a className="close-link button-orange" onClick={this.close}>{closeLinkText}</a>
</div>`
windowUnloaded: () ->
SessionActions.closeMedia(false) unless window.DontAutoCloseMedia
toggleMyMixes: (e) ->
e.preventDefault()
@setState({showMyMixes: !@state.showMyMixes})
toggleCustomMixes: (e) ->
e.preventDefault()
@setState({showCustomMixes: !@state.showCustomMixes})
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()
logger.debug("NAME", name)
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
if pitch == '' || pitch.indexOf('separator') > -1
pitch = undefined
mixdown = {jamTrackID: @state.jamTrack.id, name: name, settings: {speed:speed, pitch: pitch}}
package_settings = {file_type: 'ogg', encrypt_type: 'jkz'}
JamTrackMixdownActions.create(mixdown, package_settings, @createMixdownDone, @createMixdownFail)
@setState({creatingMixdown: true, createMixdownErrors: null})
createMixdownDone: (created) ->
logger.debug("created (within PopupMediaControls)", created)
@setState({creatingMixdown: false})
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)
@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()
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)
})