context = window logger = context.JK.logger ReactCSSTransitionGroup = React.addons.CSSTransitionGroup; 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: () -> 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 = `
Creating mixdown...
` else customMixName = `
{selectedMixdown.name}
` else if SessionStore.downloadingJamTrack downloader = `` jamTrackTypeHeader = `Full JamTrack {downloader}` header = `

{mediaType}: {mediaName}

{jamTrackTypeHeader}

{customMixName}
` 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 `
Full JamTrack
` 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 = `` when 'QUIET' action = `` when 'QUEUED' action = `` when 'QUEUED_TIMEOUT' action = `` when 'SIGNING' action = `` when 'SIGNING_TIMEOUT' action = `` when 'SIGNED' action = `` when 'ERROR' action = `` else action = `` if editing mixdownName = `` editIcon = `` else mixdownName = mixdown.name editIcon = `` download = `` myMixdowns.push `
{mixdownName}
{action} {download} {editIcon}
` 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(``) myMixdowns.push `
` myMixes = `
{myMixdowns}
` 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 = `

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.

CREATE MIX {errorHtml}
` else mixControls = `

To create a custom mix, you must open the Full JamTrack in the My Mixes section above.

` 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 = `

My Mixes {showMyMixesText}

{myMixes}

Create Custom Mix {showMixControlsText}

{mixControls}
` 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 = `

{mediaType}: {mediaName}

{jamTrackTypeHeader}

{customMixName}
` else if @state.media.mediaSummary.backingTrackOpen mediaType = "Audio File" mediaName = context.JK.getNameOfFile(@state.media.backingTracks[0].shortFilename) closeLinkText = 'CLOSE AUDIO FILE' header = `

{mediaType}: {mediaName}

` extraControls = `

` else if @state.media.mediaSummary.metronomeOpen mediaType = "Metronome" closeLinkText = 'CLOSE METRONOME' header = `

Metronome

` extraControls = `` else mediaType = "" if helpLink? helpButton = `HELP` `
{header} {extraControls}
{helpButton} {closeLinkText}
` windowUnloaded: () -> logger.debug('PopupMediaControls: window uploaded') @unloaded = true window.unloaded = true SessionActions.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() new window.Fingerprint2().get((result, components) => ( redirectTo = "/api/jamtracks/#{jamTrack.id}/stems/master/download.mp3?file_type=mp3&download=1&mark=#{result}" redirectTo = URI.escape(redirectTo) AppActions.openExternalUrl(window.location.protocol + '//' + window.location.host + "/signin?redirect-to=#{redirectTo}") )) stemChanged:() -> stemDownload: (e) -> e.preventDefault() return if @verificationCheck() $select = $(this.getDOMNode()).find('.active-stem-select') 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 = URI.escape(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 = URI.escape(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) @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: () -> console.log("THIS UNLOADED", @unloaded) 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) })