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
SessionActions.log({msg: "isOpener=" + state.media.mediaSummary.isOpener})
SessionActions.log({msg: "metronomeOpen=" + 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 = `
{mediaType}: {mediaName}
`
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 = `
{selectedMixdown.name}
`
when 'keying_timeout'
customMixName = `
{selectedMixdown.name}
`
when 'download_fail'
customMixName = `
{selectedMixdown.name}
`
when 'keying'
customMixName = `
Loading selected mix...
`
when 'downloading'
customMixName = `
Loading selected mix...
`
when 'ready'
customMixName = `
{selectedMixdown.name}
`
disabled = false
else
if selectedMixdown.myPackage
customMixName = `
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.
`
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)
})