356 lines
13 KiB
CoffeeScript
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)
|
|
}) |