jam-cloud/web/app/assets/javascripts/react-components/stores/SessionStore.js.coffee

2337 lines
91 KiB
CoffeeScript

$ = jQuery
context = window
logger = context.JK.logger
rest = context.JK.Rest()
EVENTS = context.JK.EVENTS
MIX_MODES = context.JK.MIX_MODES
CLIENT_ROLE = context.JK.CLIENT_ROLE
JamTrackActions = @JamTrackActions
SessionActions = @SessionActions
RecordingActions = @RecordingActions
NotificationActions = @NotificationActions
VideoActions = @VideoActions
ConfigureTracksActions = @ConfigureTracksActions
@SessionStore = Reflux.createStore(
{
listenables: SessionActions
userTracks: null # comes from the backend
currentSessionId: null
currentSession: null
currentOrLastSession: null
sessionRules: null
subscriptionRules: null
startTime: null
currentParticipants: {}
participantsEverSeen: {}
users: {} # // User info for session participants
requestingSessionRefresh: false
pendingSessionRefresh: false
sessionPageEnterTimeout: null
sessionPageEnterDeferred: null
gearUtils: null
sessionUtils: null
joinDeferred: null
isJoinDeferredResolved: false;
recordingModel: null
currentTrackChanges: 0
isRecording: false
previousAllTracks: {userTracks: [], backingTracks: [], metronomeTracks: []}
webcamViewer: null
openBackingTrack: null
helper: null
downloadingJamTrack: false
init: ->
# Register with the app store to get @app
this.listenTo(context.AppStore, this.onAppInit)
this.listenTo(context.RecordingStore, this.onRecordingChanged)
this.listenTo(context.VideoStore, this.onVideoChanged)
onAppInit: (@app) ->
@gearUtils = context.JK.GearUtilsInstance
@sessionUtils = context.JK.SessionUtils
@recordingModel = new context.JK.RecordingModel(@app, rest, context.jamClient);
RecordingActions.initModel(@recordingModel)
@helper = new context.SessionHelper(@app, @currentSession, @participantsEverSeen, @isRecording, @downloadingJamTrack, @enableVstTimeout?, @sessionRules, @subscriptionRules, @recordingOwnerId)
# onSessionJoinedByOther: (payload) ->
# clientId = payload.client_id
# #parentClientId = context.jamClient.getParentClientId()
# #if parentClientId? && parentClientId != ''
# #if parentClientId == clientId
# # auto nav to session
# if context.jamClient.getClientParentChildRole && context.jamClient.getClientParentChildRole() == CLIENT_ROLE.CHILD && payload.source_user_id == context.JK.currentUserId
# logger.debug("autonav to session #{payload.session_id}")
# context.SessionActions.navToSession(payload.session_id)
onSessionJoinedByOther: `async function(payload) {
const clientId = payload.client_id;
//parentClientId = context.jamClient.getParentClientId()
//if parentClientId? && parentClientId != ''
//if parentClientId == clientId
// auto nav to session
if (await context.jamClient.getClientParentChildRole() === CLIENT_ROLE.CHILD && (payload.source_user_id === context.JK.currentUserId)) {
logger.debug('autonav to session '+payload.session_id);
context.SessionActions.navToSession(payload.session_id);
}
}`
onNavToSession: (sessionId) ->
context.location = '/client#/session/' + sessionId
onMixdownActive: (mixdown) ->
if @currentSession?.jam_track?
@currentSession.jam_track.mixdown = mixdown
@issueChange()
onVideoChanged: (@videoState) ->
issueChange: () ->
@helper = new context.SessionHelper(@app, @currentSession, @participantsEverSeen, @isRecording, @downloadingJamTrack, @enableVstTimeout?, @sessionRules, @subscriptionRules, @recordingOwnerId)
this.trigger(@helper)
onWindowBackgrounded: () ->
#@app.user()
#.done((userProfile) =>
#if userProfile.show_whats_next &&
# window.location.pathname.indexOf(gon.client_path) == 0 &&
# !@app.layout.isDialogShowing('getting-started')
# @app.layout.showDialog('getting-started')
#)
return unless @inSession()
# the window was closed; just attempt to nav to home, which will cause all the right REST calls to happen
logger.debug("leaving session because window was closed")
SessionActions.leaveSession({location: '/client#/home'})
onBroadcastFailure: (text) ->
logger.debug("SESSION_LIVEBROADCAST_FAIL alert. reason:" + text);
if @currentSession? && @currentSession.mount?
rest.createSourceChange({
mount_id: @currentSession.mount.id,
source_direction: true,
success: false,
reason: text,
client_id: @app.clientId
})
else
logger.debug("unable to report source change because no mount seen on session")
onBroadcastSuccess: (text) ->
logger.debug("SESSION_LIVEBROADCAST_ACTIVE alert. reason:" + text);
if @currentSession? && @currentSession.mount?
rest.createSourceChange({
mount_id: @currentSession.mount.id,
source_direction: true,
success: true,
reason: text,
client_id: @app.clientId
})
else
logger.debug("unable to report source change because no mount seen on session")
onBroadcastStopped: (text) ->
logger.debug("SESSION_LIVEBROADCAST_STOPPED alert. reason:" + text);
if @currentSession? && @currentSession.mount?
rest.createSourceChange({
mount_id: @currentSession.mount.id,
source_direction: false,
success: true,
reason: text,
client_id: @app.clientId
})
else
logger.debug("unable to report source change because no mount seen on session")
# onShowNativeMetronomeGui: () ->
# context.jamClient.SessionShowMetronomeGui()
onShowNativeMetronomeGui: `async function() {
await context.jamClient.SessionShowMetronomeGui();
}`
onOpenMetronome: () ->
unstable = @unstableNTPClocks()
if @participants().length > 1 && unstable.length > 0
names = unstable.join(", ")
logger.debug("Unstable clocks: ", names, unstable)
context.JK.Banner.showAlert("Couldn't open metronome", context._.template($('#template-help-metronome-unstable').html(), {names: names}, { variable: 'data' }));
else
data =
value: 1
session_size: @participants().length
user_id: context.JK.currentUserId
user_name: context.JK.currentUserName
context.stats.write('web.metronome.open', data)
rest.openMetronome({id: @currentSessionId})
.done((response) =>
MixerActions.openMetronome()
@updateSessionInfo(response, true)
)
.fail((jqXHR) =>
@app.notify({
"title": "Couldn't open metronome",
"text": "Couldn't inform the server to open metronome. msg=" + jqXHR.responseText,
"icon_url": "/assets/content/icon_alert_big.png"
})
)
# onMetronomeCricketChange: (isCricket) ->
# context.jamClient.setMetronomeCricketTestState(isCricket);
onMetronomeCricketChange: `async function(isCricket) {
await context.jamClient.setMetronomeCricketTestState(isCricket);
}`
# unstableNTPClocks: () ->
# unstable = []
# # This should be handled in the below loop, actually:
# myState = context.jamClient.getMyNetworkState()
# map = null
# for participant in @participants()
# isSelf = participant.client_id == @app.clientId
# if isSelf
# isStable = myState.ntp_stable
# else
# map = context.jamClient.getPeerState(participant.client_id)
# isStable = map.ntp_stable
# if !isStable
# name = participant.user.name
# if isSelf
# name += " (this computer)"
# unstable.push(name)
# unstable
unstableNTPClocks: `async function() {
const unstable = [];
// This should be handled in the below loop, actually:
const myState = await context.jamClient.getMyNetworkState();
let map = null;
for (let participant of Array.from(this.participants())) {
var isStable;
const isSelf = participant.client_id === this.app.clientId;
if (isSelf) {
isStable = myState.ntp_stable;
} else {
map = await context.jamClient.getPeerState(participant.client_id);
isStable = map.ntp_stable;
}
if (!isStable) {
let {
name
} = participant.user;
if (isSelf) {
name += " (this computer)";
}
unstable.push(name);
}
}
return unstable;
}`
onDownloadingJamTrack: (downloading) ->
@downloadingJamTrack = downloading
@issueChange()
onToggleSessionVideo: () ->
if @videoState?.videoEnabled
logger.debug("toggle session video")
VideoActions.toggleVideo()
else
context.JK.Banner.showAlert({
title: "Video Is Disabled",
html: "To re-enable video, you must go your video settings in your account settings and enable video.",
})
# onAudioResync: () ->
# logger.debug("audio resyncing")
# response = context.jamClient.SessionAudioResync()
# if response?
# @app.notify({
# "title": "Error",
# "text": response,
# "icon_url": "/assets/content/icon_alert_big.png"})
onAudioResync: `async function() {
logger.debug("audio resyncing");
try{
const response = await context.jamClient.SessionAudioResync();
if(!response.process_status === 'Success') {
this.app.notify({
"title": "Error",
"text": 'Error in audio resyncing',
"icon_url": "/assets/content/icon_alert_big.png"});
}
}catch(e){
this.app.notify({
"title": "Error",
"text": e.message,
"icon_url": "/assets/content/icon_alert_big.png"});
}
}`
onSyncWithServer: () ->
@refreshCurrentSession(true)
onWatchedInputs: (inputTracks) ->
logger.debug("obtained tracks at start of session")
@sessionPageEnterDeferred.resolve(inputTracks);
@sessionPageEnterDeferred = null
onLog: (detail) ->
logger.debug("SessionStore: OnLog", detail)
# codeInitiated means the user did not initiate this
# onCloseMedia: (codeInitiated) ->
# logger.debug("SessionStore: onCloseMedia", codeInitiated)
# if @helper.recordedTracks()
# @closeRecording()
# else if @helper.jamTracks() || @downloadingJamTrack
# @closeJamTrack()
# else if @helper.backingTrack() && @helper.backingTrack().path
# @closeBackingTrack()
# else if @helper.isMetronomeOpen()
# @closeMetronomeTrack()
# else
# logger.error("don't know how to close open media", @helper) unless codeInitiated
onCloseMedia: `async function(codeInitiated) {
logger.debug("SessionStore: onCloseMedia", codeInitiated);
if (this.helper.recordedTracks()) {
await this.closeRecording();
} else if (this.helper.jamTracks() || this.downloadingJamTrack) {
await this.closeJamTrack();
} else if (this.helper.backingTrack() && this.helper.backingTrack().path) {
await this.closeBackingTrack();
} else if (this.helper.isMetronomeOpen()) {
await this.closeMetronomeTrack();
} else {
if (!codeInitiated) { return logger.error("don't know how to close open media", this.helper); }
}
}`
# closeJamTrack: () ->
# logger.debug("closing jam track");
# if @isRecording
# logger.debug("can't close jamtrack while recording")
# @app.notify({title: 'Can Not Close JamTrack', text: 'A JamTrack can not be closed while recording.'})
# return
# unless @selfOpenedJamTracks()
# logger.debug("can't close jamtrack if not the opener")
# @app.notify({title: 'Can Not Close JamTrack', text: 'Only the person who opened the JamTrack can close it.'})
# return
# rest.closeJamTrack({id: @currentSessionId})
# .done(() =>
# @downloadingJamTrack = false
# @refreshCurrentSession(true)
# )
# .fail((jqXHR) =>
# @app.notify({
# "title": "Couldn't Close JamTrack",
# "text": "Couldn't inform the server to close JamTrack. msg=" + jqXHR.responseText,
# "icon_url": "/assets/content/icon_alert_big.png"
# })
# )
# context.jamClient.JamTrackStopPlay()
# JamTrackActions.close()
closeJamTrack: `async function() {
logger.debug("closing jam track");
if (this.isRecording) {
logger.debug("can't close jamtrack while recording");
this.app.notify({title: 'Can Not Close JamTrack', text: 'A JamTrack can not be closed while recording.'});
}
if (!this.selfOpenedJamTracks()) {
logger.debug("can't close jamtrack if not the opener");
this.app.notify({title: 'Can Not Close JamTrack', text: 'Only the person who opened the JamTrack can close it.'});
}
rest.closeJamTrack({id: this.currentSessionId})
.done(() => {
this.downloadingJamTrack = false;
return this.refreshCurrentSession(true);
})
.fail(jqXHR => {
return this.app.notify({
"title": "Couldn't Close JamTrack",
"text": "Couldn't inform the server to close JamTrack. msg=" + jqXHR.responseText,
"icon_url": "/assets/content/icon_alert_big.png"
});
});
await context.jamClient.JamTrackStopPlay();
JamTrackActions.close();
}`
# onOpenBackingTrack: (result) ->
# unless @inSession()
# logger.debug("ignoring backing track selected callback (not in session)")
# return
# if result.success
# logger.debug("backing track selected: " + result.file);
# rest.openBackingTrack({id: @currentSessionId, backing_track_path: result.file})
# .done(() =>
# openResult = context.jamClient.SessionOpenBackingTrackFile(result.file, false);
# if openResult
# # storing session state in memory, not in response of Session server response. bad.
# @openBackingTrack = result.file
# else
# @app.notify({
# "title": "Couldn't Open Backing Track",
# "text": "Is the file a valid audio file?",
# "icon_url": "/assets/content/icon_alert_big.png"
# });
# @closeBackingTrack()
# )
# .fail((jqXHR) =>
# @app.notifyServerError(jqXHR, "Unable to Open Backing Track For Playback");
# )
onOpenBackingTrack: `async function(result) {
console.log('onOpenBackingTrack', result)
if (!this.inSession()) {
logger.debug("ignoring backing track selected callback (not in session)");
return;
}
if (result.success) {
logger.debug("backing track selected: " + result.file);
try{
await rest.openBackingTrack({id: this.currentSessionId, backing_track_path: result.file});
const openResult = await context.jamClient.SessionOpenBackingTrackFile(result.file, false);
console.log('openResult', openResult)
if (openResult) {
// storing session state in memory, not in response of Session server response. bad.
this.openBackingTrack = result.file;
} else {
this.app.notify({
"title": "Couldn't Open Backing Track",
"text": "Is the file a valid audio file?",
"icon_url": "/assets/content/icon_alert_big.png"
});
await this.closeBackingTrack();
}
}catch(jqXHR){
this.app.notifyServerError(jqXHR, "Unable to Open Backing Track For Playback");
}
}
}`
# closeRecording: () ->
# logger.debug("closing recording");
# rest.stopPlayClaimedRecording({id: @currentSessionId, claimed_recording_id: @currentSession.claimed_recording.id})
# .done((response) =>
# #sessionModel.refreshCurrentSession(true);
# # update session info
# @onUpdateSession(response)
# )
# .fail((jqXHR) =>
# @app.notify({
# "title": "Couldn't Stop Recording Playback",
# "text": "Couldn't inform the server to stop playback. msg=" + jqXHR.responseText,
# "icon_url": "/assets/content/icon_alert_big.png"
# })
# )
# context.jamClient.CloseRecording()
closeRecording: `async function() {
logger.debug("closing recording");
rest.stopPlayClaimedRecording({id: this.currentSessionId, claimed_recording_id: this.currentSession.claimed_recording.id})
.done(response => {
//sessionModel.refreshCurrentSession(true);
// update session info
return this.onUpdateSession(response);
})
.fail(jqXHR => {
return this.app.notify({
"title": "Couldn't Stop Recording Playback",
"text": "Couldn't inform the server to stop playback. msg=" + jqXHR.responseText,
"icon_url": "/assets/content/icon_alert_big.png"
});
});
await context.jamClient.CloseRecording();
}`
# closeMetronomeTrack:() ->
# logger.debug("SessionStore: closeMetronomeTrack")
# rest.closeMetronome({id: @currentSessionId})
# .done(() =>
# context.jamClient.SessionCloseMetronome()
# @refreshCurrentSession(true)
# )
# .fail((jqXHR) =>
# @app.notify({
# "title": "Couldn't Close MetronomeTrack",
# "text": "Couldn't inform the server to close MetronomeTrack. msg=" + jqXHR.responseText,
# "icon_url": "/assets/content/icon_alert_big.png"
# })
# )
closeMetronomeTrack: `async function() {
logger.debug("SessionStore: closeMetronomeTrack");
try{
await rest.closeMetronome({id: this.currentSessionId})
await context.jamClient.SessionCloseMetronome();
this.refreshCurrentSession(true);
}catch(jqXHR){
this.app.notify({
"title": "Couldn't Close MetronomeTrack",
"text": "Couldn't inform the server to close MetronomeTrack. msg=" + jqXHR.responseText,
"icon_url": "/assets/content/icon_alert_big.png"
});
}
}`
# closeBackingTrack: () ->
# if @isRecording
# logger.debug("can't close backing track while recording")
# return
# rest.closeBackingTrack({id: @currentSessionId})
# .done(() =>
# )
# .fail(() =>
# @app.notify({
# "title": "Couldn't Close Backing Track",
# "text": "Couldn't inform the server to close Backing Track. msg=" + jqXHR.responseText,
# "icon_url": "/assets/content/icon_alert_big.png"
# });
# )
# # '' closes all open backing tracks
# context.jamClient.SessionStopPlay();
# context.jamClient.SessionCloseBackingTrackFile('');
closeBackingTrack: `async function() {
if (this.isRecording) {
logger.debug("can't close backing track while recording");
return;
}
rest.closeBackingTrack({id: this.currentSessionId})
.done(() => {
})
.fail(() => {
return this.app.notify({
"title": "Couldn't Close Backing Track",
"text": "Couldn't inform the server to close Backing Track. msg=" + jqXHR.responseText,
"icon_url": "/assets/content/icon_alert_big.png"
});
});
// '' closes all open backing tracks
await context.jamClient.SessionStopPlay();
await context.jamClient.SessionCloseBackingTrackFile('');
}`
onMixersChanged: (type, text, trackInfo) ->
return unless @inSession()
if text == 'RebuildAudioIoControl'
if @backendMixerAlertThrottleTimer
clearTimeout(@backendMixerAlertThrottleTimer)
@backendMixerAlertThrottleTimer =
setTimeout(() =>
@backendMixerAlertThrottleTimer = null
if @sessionPageEnterDeferred
# this means we are still waiting for the BACKEND_MIXER_CHANGE that indicates we have user tracks built-out/ready
# we will get at least one BACKEND_MIXER_CHANGE that corresponds to the backend doing a 'audio pause', which won't matter much
# so we need to check that we actaully have userTracks before considering ourselves done
if trackInfo.userTracks.length > 0
logger.debug("obtained tracks at start of session")
@sessionPageEnterDeferred.resolve(trackInfo.userTracks)
@sessionPageEnterDeferred = null
return
# wait until we are fully in session before trying to sync tracks to server
if @joinDeferred
@joinDeferred
.then(()=>
MixerActions.syncTracks()
)
, 100)
else if text == 'Midi-Track Update'
logger.debug('midi track sync')
MixerActions.syncTracks()
else if text == 'RebuildMediaControl' || text == 'RebuildRemoteUserControl'
backingTracks = trackInfo.backingTracks
previousBackingTracks = @previousAllTracks.backingTracks
metronomeTracks = trackInfo.metronomeTracks
previousMetronomeTracks = @previousAllTracks.metronomeTracks
# the way we know if backing tracks changes, or recordings are opened, is via this event.
# but we want to report to the user when backing tracks change; so we need to detect change on our own
if !(previousBackingTracks.length == 0 && backingTracks.length == 0) && previousBackingTracks != backingTracks
logger.debug("backing tracks changed", previousBackingTracks, backingTracks)
MixerActions.syncTracks()
else if !(previousMetronomeTracks.length == 0 && metronomeTracks.length == 0) && previousMetronomeTracks != metronomeTracks
#logger.debug("metronome state changed ", previousMetronomeTracks, metronomeTracks)
MixerActions.syncTracks()
else
@refreshCurrentSession(true)
@previousAllTracks = trackInfo
else if text == 'Global Peer Input Mixer Mode'
MixerActions.mixerModeChanged(MIX_MODES.MASTER)
else if text == 'Local Peer Stream Mixer Mode'
MixerActions.mixerModeChanged(MIX_MODES.PERSONAL)
# onRecordingChanged: (details) ->
# logger.debug("SessionStore.onRecordingChanged: " + details.cause)
# @isRecording = details.isRecording
# switch details.cause
# when 'started'
# if details.reason
# reason = details.reason;
# detail = details.detail;
# title = "Could Not Start Recording";
# switch reason
# when 'client-no-response'
# @notifyWithUserInfo(title, 'did not respond to the start signal.', detail)
# when 'empty-recording-id'
# @app.notifyAlert(title, "No recording ID specified.")
# when 'missing-client'
# @notifyWithUserInfo(title, 'could not be signalled to start recording.', detail)
# when 'already-recording'
# @app.notifyAlert(title, 'Already recording. If this appears incorrect, try restarting JamKazam.')
# when 'recording-engine-unspecified'
# @notifyWithUserInfo(title, 'had a problem writing recording data to disk.', detail)
# when 'recording-engine-create-directory'
# @notifyWithUserInfo(title, 'had a problem creating a recording folder.', detail)
# when 'recording-engine-create-file'
# @notifyWithUserInfo(title, 'had a problem creating a recording file.', detail)
# when 'recording-engine-sample-rate'
# @notifyWithUserInfo(title, 'had a problem recording at the specified sample rate.', detail)
# when 'rest'
# jqXHR = detail[0];
# @app.notifyServerError(jqXHR);
# else
# @notifyWithUserInfo(title, 'Error Reason: ' + reason)
# else
# @displayWhoCreatedRecording(details.clientId)
# when 'stopped'
# if @selfOpenedJamTracks()
# timeline = context.jamClient.GetJamTrackTimeline();
# rest.addRecordingTimeline(details.recordingId, timeline)
# .fail(()=>
# @app.notify({
# title: "Unable to Add JamTrack Volume Data",
# text: "The volume of the JamTrack will not be correct in the recorded mix."
# }, null, true)
# )
# if details.reason
# logger.warn("Recording Discarded: ", details)
# reason = details.reason
# detail = details.detail
# title = "Recording Discarded"
# switch reason
# when 'client-no-response'
# @notifyWithUserInfo(title, 'did not respond to the stop signal.', detail)
# when 'missing-client'
# @notifyWithUserInfo(title, 'could not be signalled to stop recording.', detail)
# when 'empty-recording-id'
# @app.notifyAlert(title, "No recording ID specified.")
# when 'wrong-recording-id'
# @app.notifyAlert(title, "Wrong recording ID specified.")
# when 'not-recording'
# @app.notifyAlert(title, "Not currently recording.")
# when 'already-stopping'
# @app.notifyAlert(title, "Already stopping the current recording.")
# when 'start-before-stop'
# @notifyWithUserInfo(title, 'asked that we start a new recording; cancelling the current one.', detail)
# else
# @app.notifyAlert(title, "Error reason: " + reason)
# else
# @promptUserToSave(details.recordingId, timeline);
# when 'abortedRecording'
# reason = details.reason
# detail = details.detail
# title = "Recording Cancelled"
# switch reason
# when 'client-no-response'
# @notifyWithUserInfo(title, 'did not respond to the start signal.', detail)
# when 'missing-client'
# @notifyWithUserInfo(title, 'could not be signalled to start recording.', detail)
# when 'populate-recording-info'
# @notifyWithUserInfo(title, 'could not synchronize with the server.', detail)
# when 'recording-engine-unspecified'
# @notifyWithUserInfo(title, 'had a problem writing recording data to disk.', detail)
# when 'recording-engine-create-directory'
# @notifyWithUserInfo(title, 'had a problem creating a recording folder.', detail)
# when 'recording-engine-create-file'
# @notifyWithUserInfo(title, 'had a problem creating a recording file.', detail)
# when 'recording-engine-sample-rate'
# @notifyWithUserInfo(title, 'had a problem recording at the specified sample rate.', detail)
# else
# @app.notifyAlert(title, "Error reason: " + reason)
# @issueChange()
# onRecordingChanged: `async function(details) {
# let detail, reason, timeline, title;
# logger.debug("SessionStore.onRecordingChanged: " + details.cause);
# console.log("_DEBUG_ SessionStore.onRecordingChanged: " + JSON.stringify(details));
# this.isRecording = details.isRecording;
# this.recordingClinetId = details.clientId;
# switch (details.cause) {
# case 'started':
# if (details.reason) {
# ({
# reason
# } = details);
# ({
# detail
# } = details);
# title = "Could Not Start Recording";
# switch (reason) {
# case 'client-no-response':
# this.notifyWithUserInfo(title, 'did not respond to the start signal.', detail);
# break;
# case 'empty-recording-id':
# this.app.notifyAlert(title, "No recording ID specified.");
# break;
# case 'missing-client':
# this.notifyWithUserInfo(title, 'could not be signalled to start recording.', detail);
# break;
# case 'already-recording':
# this.app.notifyAlert(title, 'Already recording. If this appears incorrect, try restarting JamKazam.');
# break;
# case 'recording-engine-unspecified':
# this.notifyWithUserInfo(title, 'had a problem writing recording data to disk.', detail);
# break;
# case 'recording-engine-create-directory':
# this.notifyWithUserInfo(title, 'had a problem creating a recording folder.', detail);
# break;
# case 'recording-engine-create-file':
# this.notifyWithUserInfo(title, 'had a problem creating a recording file.', detail);
# break;
# case 'recording-engine-sample-rate':
# this.notifyWithUserInfo(title, 'had a problem recording at the specified sample rate.', detail);
# break;
# case 'rest':
# var jqXHR = detail[0];
# this.app.notifyServerError(jqXHR);
# break;
# default:
# this.notifyWithUserInfo(title, 'Error Reason: ' + reason);
# }
# } else {
# this.displayWhoCreatedRecording(details.clientId);
# }
# break;
# case 'stopped':
# if (this.selfOpenedJamTracks()) {
# timeline = await context.jamClient.GetJamTrackTimeline();
# rest.addRecordingTimeline(details.recordingId, timeline)
# .fail(()=> {
# return this.app.notify({
# title: "Unable to Add JamTrack Volume Data",
# text: "The volume of the JamTrack will not be correct in the recorded mix."
# }, null, true);
# });
# }
# if (details.reason) {
# logger.warn("Recording Discarded: ", details);
# ({
# reason
# } = details);
# ({
# detail
# } = details);
# title = "Recording Discarded";
# switch (reason) {
# case 'client-no-response':
# this.notifyWithUserInfo(title, 'did not respond to the stop signal.', detail);
# break;
# case 'missing-client':
# this.notifyWithUserInfo(title, 'could not be signalled to stop recording.', detail);
# break;
# case 'empty-recording-id':
# this.app.notifyAlert(title, "No recording ID specified.");
# break;
# case 'wrong-recording-id':
# this.app.notifyAlert(title, "Wrong recording ID specified.");
# break;
# case 'not-recording':
# this.app.notifyAlert(title, "Not currently recording.");
# break;
# case 'already-stopping':
# this.app.notifyAlert(title, "Already stopping the current recording.");
# break;
# case 'start-before-stop':
# this.notifyWithUserInfo(title, 'asked that we start a new recording; cancelling the current one.', detail);
# break;
# default:
# this.app.notifyAlert(title, "Error reason: " + reason);
# }
# } else {
# logger.debug('backend opens recording files directory.')
# }
# break;
# case 'abortedRecording':
# ({
# reason
# } = details);
# ({
# detail
# } = details);
# title = "Recording Cancelled";
# switch (reason) {
# case 'client-no-response':
# this.notifyWithUserInfo(title, 'did not respond to the start signal.', detail);
# break;
# case 'missing-client':
# this.notifyWithUserInfo(title, 'could not be signalled to start recording.', detail);
# break;
# case 'populate-recording-info':
# this.notifyWithUserInfo(title, 'could not synchronize with the server.', detail);
# break;
# case 'recording-engine-unspecified':
# this.notifyWithUserInfo(title, 'had a problem writing recording data to disk.', detail);
# break;
# case 'recording-engine-create-directory':
# this.notifyWithUserInfo(title, 'had a problem creating a recording folder.', detail);
# break;
# case 'recording-engine-create-file':
# this.notifyWithUserInfo(title, 'had a problem creating a recording file.', detail);
# break;
# case 'recording-engine-sample-rate':
# this.notifyWithUserInfo(title, 'had a problem recording at the specified sample rate.', detail);
# break;
# default:
# this.app.notifyAlert(title, "Error reason: " + reason);
# }
# break;
# }
# return this.issueChange();
# }`
onRecordingChanged: `async function(details) {
let detail, reason, timeline, title;
logger.debug("SessionStore.onRecordingChanged: " + details.cause);
//console.log("_DEBUG_* SessionStore.onRecordingChanged: " + JSON.stringify(details));
this.isRecording = details.isRecording;
this.recordingClinetId = details.clientId;
let recordingState = await this.recordingModel.getCurrentRecordingState();
this.recordingOwnerId = recordingState.recordingOwnerId;
switch (details.cause) {
case 'started':
if (details.reason) {
({
reason
} = details);
({
detail
} = details);
title = "Could Not Start Recording";
switch (reason) {
case 'client-no-response':
this.notifyWithUserInfo(title, 'did not respond to the start signal.', detail);
break;
case 'empty-recording-id':
this.app.notifyAlert(title, "No recording ID specified.");
break;
case 'missing-client':
this.notifyWithUserInfo(title, 'could not be signalled to start recording.', detail);
break;
case 'already-recording':
this.app.notifyAlert(title, 'Already recording. If this appears incorrect, try restarting JamKazam.');
break;
case 'recording-engine-unspecified':
this.notifyWithUserInfo(title, 'had a problem writing recording data to disk.', detail);
break;
case 'recording-engine-create-directory':
this.notifyWithUserInfo(title, 'had a problem creating a recording folder.', detail);
break;
case 'recording-engine-create-file':
this.notifyWithUserInfo(title, 'had a problem creating a recording file.', detail);
break;
case 'recording-engine-sample-rate':
this.notifyWithUserInfo(title, 'had a problem recording at the specified sample rate.', detail);
break;
case 'rest':
var jqXHR = detail[0];
this.app.notifyServerError(jqXHR);
break;
default:
this.notifyWithUserInfo(title, 'Error Reason: ' + reason);
}
} else {
this.displayWhoCreatedRecording(details.clientId);
}
break;
case 'stopped':
if (this.selfOpenedJamTracks()) {
timeline = await context.jamClient.GetJamTrackTimeline();
rest.addRecordingTimeline(details.recordingId, timeline)
.fail(()=> {
return this.app.notify({
title: "Unable to Add JamTrack Volume Data",
text: "The volume of the JamTrack will not be correct in the recorded mix."
}, null, true);
});
}
if (details.reason) {
logger.warn("Recording Discarded: ", details);
({
reason
} = details);
({
detail
} = details);
title = "Recording Discarded";
switch (reason) {
case 'client-no-response':
this.notifyWithUserInfo(title, 'did not respond to the stop signal.', detail);
break;
case 'missing-client':
this.notifyWithUserInfo(title, 'could not be signalled to stop recording.', detail);
break;
case 'empty-recording-id':
this.app.notifyAlert(title, "No recording ID specified.");
break;
case 'wrong-recording-id':
this.app.notifyAlert(title, "Wrong recording ID specified.");
break;
case 'not-recording':
this.app.notifyAlert(title, "Not currently recording.");
break;
case 'already-stopping':
this.app.notifyAlert(title, "Already stopping the current recording.");
break;
case 'start-before-stop':
this.notifyWithUserInfo(title, 'asked that we start a new recording; cancelling the current one.', detail);
break;
default:
this.app.notifyAlert(title, "Error reason: " + reason);
}
} else {
logger.debug('backend opens recording files directory.')
}
break;
case 'abortedRecording':
({
reason
} = details);
({
detail
} = details);
title = "Recording Cancelled";
switch (reason) {
case 'client-no-response':
this.notifyWithUserInfo(title, 'did not respond to the start signal.', detail);
break;
case 'missing-client':
this.notifyWithUserInfo(title, 'could not be signalled to start recording.', detail);
break;
case 'populate-recording-info':
this.notifyWithUserInfo(title, 'could not synchronize with the server.', detail);
break;
case 'recording-engine-unspecified':
this.notifyWithUserInfo(title, 'had a problem writing recording data to disk.', detail);
break;
case 'recording-engine-create-directory':
this.notifyWithUserInfo(title, 'had a problem creating a recording folder.', detail);
break;
case 'recording-engine-create-file':
this.notifyWithUserInfo(title, 'had a problem creating a recording file.', detail);
break;
case 'recording-engine-sample-rate':
this.notifyWithUserInfo(title, 'had a problem recording at the specified sample rate.', detail);
break;
default:
this.app.notifyAlert(title, "Error reason: " + reason);
}
break;
}
return this.issueChange();
}`
notifyWithUserInfo: (title , text, clientId) ->
@findUserBy({clientId: clientId})
.done((user)=>
@app.notify({
"title": title,
"text": user.name + " " + text,
"icon_url": context.JK.resolveAvatarUrl(user.photo_url)
});
)
.fail(()=>
@app.notify({
"title": title,
"text": 'Someone ' + text,
"icon_url": "/assets/content/icon_alert_big.png"
})
)
findUserBy: (finder) ->
if finder.clientId
foundParticipant = null
for participant in @participants()
if participant.client_id == finder.clientId
foundParticipant = participant
break
if foundParticipant
return $.Deferred().resolve(foundParticipant.user).promise();
# TODO: find it via some REST API if not found?
return $.Deferred().reject().promise();
findParticipantByUserId: (userId) ->
foundParticipant = null
for participant in @participants()
if participant.user.id == userId
foundParticipant = participant
break
foundParticipant
displayWhoCreatedRecording: (clientId) ->
if @app.clientId != clientId # don't show to creator
@findUserBy({clientId: clientId})
.done((user) =>
@app.notify({
"title": "Recording Started",
"text": user.name + " started a recording",
"icon_url": context.JK.resolveAvatarUrl(user.photo_url)
})
)
.fail(() =>
@app.notify({
"title": "Recording Started",
"text": "Oops! Can't determine who started this recording",
"icon_url": "/assets/content/icon_alert_big.png"
})
)
promptUserToSave: (recordingId, timeline) ->
rest.getRecording( {id: recordingId} )
.done((recording) =>
if timeline
recording.timeline = timeline.global
context.JK.recordingFinishedDialog.setRecording(recording)
@app.layout.showDialog('recordingFinished').one(EVENTS.DIALOG_CLOSED, (e, data) =>
if data.result && data.result.keep
context.JK.prodBubble($('#recording-manager-viewer'), 'file-manager-poke', {}, {positions:['top', 'left', 'right', 'bottom'], offsetParent: $('#session-screen').parent()})
)
)
.fail(@app.ajaxError)
onEnterSession: (sessionId) ->
if !context.JK.guardAgainstBrowser(@app)
return false;
window.location.href = '/client#/session/' + sessionId
onJoinSession: `async function(sessionId) {
if(!context.JK.guardAgainstBrowser(this.app))
return false;
const shareDialog = new JK.ShareDialog(this.app, sessionId, "session");
shareDialog.initialize(context.JK.FacebookHelperInstance);
VideoActions.stopVideo();
if (!this.ensureConnected()) { return; }
this.sessionEnded(true);
this.updateCurrentSession(null);
this.currentSessionId = sessionId;
this.startTime = new Date().getTime();
try{
const musicSession = await rest.getSessionHistoryPromise(this.currentSessionId)
await this.onJoinSessionDone(musicSession)
}catch(e){
return logger.error("unable to fetch session history", e);
}
}`
onJoinSessionDone: `async function(musicSession){
const musicianAccessOnJoin = musicSession.musician_access;
const shouldVerifyNetwork = musicSession.musician_access;
let clientRole = CLIENT_ROLE.PARENT;
clientRole = await context.jamClient.getClientParentChildRole();
if (clientRole === CLIENT_ROLE.CHILD) {
logger.debug("client is configured to act as child. skipping all checks. assuming 0 tracks");
this.userTracks = [];
await this.joinSession();
}
try{
await this.gearUtils.guardAgainstInvalidConfiguration(this.app, shouldVerifyNetwork)
const result = await this.sessionUtils.SessionPageEnter();
try{
await this.gearUtils.guardAgainstActiveProfileMissing(this.app, result)
try{
this.userTracks = await this.waitForSessionPageEnterDone()
try{
await this.ensureAppropriateProfile(musicianAccessOnJoin)
logger.debug("user has passed all session guards")
await this.joinSession()
}catch(error){
if (!error.controlled_location) {
SessionActions.leaveSession.trigger({location: "/client#/home"});
}
}
}catch(error){
if (error === "timeout") {
context.JK.alertSupportedNeeded('The audio system has not reported your configured tracks in a timely fashion.');
} else if (error === 'session_over') {
// do nothing; session ended before we got the user track info. just bail
logger.debug("session is over; bailing");
} else {
context.JK.alertSupportedNeeded('Unable to determine configured tracks due to reason: ' + error);
}
SessionActions.leaveSession.trigger({location: '/client#/home'});
}
}catch(error){
const leaveBehavior = {};
if (error && (error.reason === 'handled')) {
if (error.nav === 'BACK') {
leaveBehavior.location = -1;
} else {
leaveBehavior.location = error.nav;
}
} else {
leaveBehavior.location = '/client#/home';
}
SessionActions.leaveSession.trigger(leaveBehavior);
}
}catch(error){
SessionActions.leaveSession.trigger({location: '/client#/home'});
}
}`
# waitForSessionPageEnterDone: () ->
# @sessionPageEnterDeferred = $.Deferred()
# # see if we already have tracks; if so, we need to run with these
# inputTracks = context.JK.TrackHelpers.getUserTracks(context.jamClient)
# logger.debug("isNoInputProfile", @gearUtils.isNoInputProfile())
# if inputTracks.length > 0 || @gearUtils.isNoInputProfile()
# logger.debug("on page enter, tracks are already available")
# @sessionPageEnterDeferred.resolve(inputTracks)
# deferred = @sessionPageEnterDeferred
# @sessionPageEnterDeferred = null
# return deferred
# @sessionPageEnterTimeout = setTimeout(()=>
# if @sessionPageEnterTimeout
# if @sessionPageEnterDeferred
# @sessionPageEnterDeferred.reject('timeout')
# @sessionPageEnterDeferred = null
# @sessionPageEnterTimeout = null
# , 5000)
# @sessionPageEnterDeferred
# waitForSessionPageEnterDone: `function() {
# const gearUtils = this.gearUtils;
# return new Promise(async function(resolve, reject) {
# //see if we already have tracks; if so, we need to run with these
# const inputTracks = await context.JK.TrackHelpers.getUserTracks(context.jamClient);
# logger.debug("isNoInputProfile", await gearUtils.isNoInputProfile());
# if ((inputTracks.length > 0) || await gearUtils.isNoInputProfile()) {
# logger.debug("on page enter, tracks are already available");
# resolve(inputTracks);
# }else{
# this.sessionPageEnterTimeout = setTimeout(()=> {
# if (this.sessionPageEnterTimeout) {
# reject('timeout');
# this.sessionPageEnterTimeout = null;
# }
# }, 5000);
# }
# })
# }`
waitForSessionPageEnterDone: `async function () {
sessionPageEnterDeferred = $.Deferred();
// see if we already have tracks; if so, we need to run with these
var inputTracks = await context.JK.TrackHelpers.getUserTracks(context.jamClient);
logger.debug("isNoInputProfile", this.gearUtils.isNoInputProfile())
if(inputTracks.length > 0 || this.gearUtils.isNoInputProfile() ) {
logger.debug("on page enter, tracks are already available")
sessionPageEnterDeferred.resolve(inputTracks);
var deferred = sessionPageEnterDeferred;
sessionPageEnterDeferred = null;
return deferred;
}
sessionPageEnterTimeout = setTimeout(function() {
if(sessionPageEnterTimeout) {
if(sessionPageEnterDeferred) {
sessionPageEnterDeferred.reject('timeout');
sessionPageEnterDeferred = null;
}
sessionPageEnterTimeout = null;
}
}, 5000);
return sessionPageEnterDeferred;
}`
# ensureAppropriateProfile: (musicianAccess) ->
# deferred = new $.Deferred();
# if musicianAccess
# deferred = context.JK.guardAgainstSinglePlayerProfile(@app)
# else
# deferred.resolve();
# deferred
ensureAppropriateProfile: `function(musicianAccess) {
const app = this.app
return new Promise(async function(resolve, reject){
if (musicianAccess) {
try{
await context.JK.guardAgainstSinglePlayerProfile(app);
resolve();
}catch(error){
reject(error)
}
} else {
resolve();
}
})
}`
openBrowserToPayment: () ->
context.JK.popExternalLink("/client#/account/subscription", true)
openBrowserToPlanComparison: () ->
context.JK.popExternalLink("https://jamkazam.freshdesk.com/support/solutions/articles/66000122535-what-are-jamkazam-s-free-vs-premium-features-")
return 'noclose'
# joinSession: () ->
# context.jamClient.SessionRegisterCallback("JK.HandleBridgeCallback2");
# context.jamClient.RegisterRecordingCallbacks("JK.HandleRecordingStartResult", "JK.HandleRecordingStopResult", "JK.HandleRecordingStarted", "JK.HandleRecordingStopped", "JK.HandleRecordingAborted");
# context.jamClient.SessionSetConnectionStatusRefreshRate(1000);
# clientRole = context.jamClient.getClientParentChildRole();
# parentClientId = context.jamClient.getParentClientId();
# logger.debug("role when joining session: #{clientRole}, parent client id #{parentClientId}")
# #context.JK.HelpBubbleHelper.jamtrackGuideSession($screen.find('li.open-a-jamtrack'), $screen)
# if clientRole == 0
# clientRole = 'child'
# else if clientRole == 1
# clientRole = 'parent'
# if clientRole == '' || !clientRole
# clientRole = null
# # subscribe to events from the recording model
# @recordingRegistration()
# # tell the server we want to join
# @joinDeferred = rest.joinSession({
# client_id: @app.clientId,
# ip_address: context.JK.JamServer.publicIP,
# as_musician: true,
# tracks: @userTracks,
# session_id: @currentSessionId,
# client_role: clientRole,
# parent_client_id: parentClientId
# audio_latency: context.jamClient.FTUEGetExpectedLatency().latency
# })
# .done((response) =>
# unless @inSession()
# # the user has left the session before they got joined. We need to issue a leave again to the server to make sure they are out
# logger.debug("user left before fully joined to session. telling server again that they have left")
# @leaveSessionRest(@currentSessionId)
# return
# @updateSessionInfo(response, true)
# @issueChange()
# logger.debug("calling jamClient.JoinSession");
# # on temporary disconnect scenarios, a user may already be in a session when they enter this path
# # so we avoid double counting
# unless @alreadyInSession()
# if @participants().length == 1
# context.JK.GA.trackSessionMusicians(context.JK.GA.SessionCreationTypes.create);
# else
# context.JK.GA.trackSessionMusicians(context.JK.GA.SessionCreationTypes.join);
# @recordingModel.reset(@currentSessionId);
# joinSessionMsg = {sessionID: @currentSessionId, music_session_id_int: response.music_session_id_int}
# context.jamClient.JoinSession(joinSessionMsg);
# #@refreshCurrentSession(true);
# context.JK.JamServer.registerMessageCallback(context.JK.MessageType.SESSION_JOIN, @trackChanges);
# context.JK.JamServer.registerMessageCallback(context.JK.MessageType.SESSION_DEPART, @trackChanges);
# context.JK.JamServer.registerMessageCallback(context.JK.MessageType.TRACKS_CHANGED, @trackChanges);
# context.JK.JamServer.registerMessageCallback(context.JK.MessageType.HEARTBEAT_ACK, @trackChanges);
# $(document).trigger(EVENTS.SESSION_STARTED, {session: {id: @currentSessionId, lesson_session: response.lesson_session}}) if document
# @handleAutoOpenJamTrack()
# @watchBackendStats()
# ConfigureTracksActions.reset(true)
# @delayEnableVst()
# )
# .fail((xhr) =>
# @updateCurrentSession(null)
# if xhr.status == 404
# # we tried to join the session, but it's already gone. kick user back to join session screen
# leaveBehavior =
# location: "/client#/findSession"
# notify:
# title: "Unable to Join Session",
# text: " The session you attempted to join is over."
# SessionActions.leaveSession.trigger(leaveBehavior)
# else if xhr.status == 422
# response = JSON.parse(xhr.responseText);
# if response["errors"] && response["errors"]["tracks"] && response["errors"]["tracks"][0] == "Please select at least one track"
# @app.notifyAlert("No Inputs Configured", $('<span>You will need to reconfigure your audio device.</span>'))
# else if response["errors"] && response["errors"]["music_session"] && response["errors"]["music_session"][0] == ["is currently recording"]
# leaveBehavior =
# location: "/client#/findSession"
# notify:
# title: "Unable to Join Session"
# text: "The session is currently recording."
# SessionActions.leaveSession.trigger(leaveBehavior)
# else if response["errors"] && response["errors"]["remaining_session_play_time"]
# leaveBehavior =
# location: "/client#/findSession"
# buttons = []
# buttons.push({name: 'CLOSE', buttonStyle: 'button-grey'})
# buttons.push({name: 'COMPARE PLANS', buttonStyle: 'button-grey', click: (() => (@openBrowserToPlanComparison()))})
# buttons.push({
# name: 'UPGRADE PLAN',
# buttonStyle: 'button-orange',
# click: (() => (@openBrowserToPayment()))
# })
# context.JK.Banner.show({
# title: "Out of Time For This Session",
# html: context._.template($('#template-no-remaining-session-play-time').html(), {}, { variable: 'data' }),
# buttons: buttons});
# SessionActions.leaveSession.trigger(leaveBehavior)
# else if response["errors"] && response["errors"]["remaining_month_play_time"]
# leaveBehavior =
# location: "/client#/findSession"
# buttons = []
# buttons.push({name: 'CLOSE', buttonStyle: 'button-grey'})
# buttons.push({name: 'COMPARE PLANS', buttonStyle: 'button-grey', click: (() => (@openBrowserToPlanComparison()))})
# buttons.push({
# name: 'UPGRADE PLAN',
# buttonStyle: 'button-orange',
# click: (() => (@openBrowserToPayment()))
# })
# context.JK.Banner.show({
# title: "Out of Time for the Month",
# html: context._.template($('#template-no-remaining-month-play-time').html(), {}, { variable: 'data' }),
# buttons: buttons});
# SessionActions.leaveSession.trigger(leaveBehavior)
# else
# @app.notifyServerError(xhr, 'Unable to Join Session');
# else
# @app.notifyServerError(xhr, 'Unable to Join Session');
# )
joinSession: `async function() {
await context.jamClient.SessionRegisterCallback("JK.HandleBridgeCallback2");
await context.jamClient.RegisterRecordingCallbacks("JK.HandleRecordingStartResult", "JK.HandleRecordingStopResult", "JK.HandleRecordingStarted", "JK.HandleRecordingStopped", "JK.HandleRecordingAborted");
await context.jamClient.SessionSetConnectionStatusRefreshRate(1000);
let clientRole = await context.jamClient.getClientParentChildRole();
const parentClientId = await context.jamClient.getParentClientId();
logger.debug('role when joining session: '+clientRole+', parent client id '+parentClientId);
//context.JK.HelpBubbleHelper.jamtrackGuideSession($screen.find('li.open-a-jamtrack'), $screen)
if (clientRole === 0) {
clientRole = 'child';
} else if (clientRole === 1) {
clientRole = 'parent';
}
if ((clientRole === '') || !clientRole) {
clientRole = null;
}
// subscribe to events from the recording model
this.recordingRegistration();
// tell the server we want to join
this.joinDeferred = rest.joinSessionPromise({
client_id: this.app.clientId,
ip_address: context.JK.JamServer.publicIP,
as_musician: true,
tracks: this.userTracks,
session_id: this.currentSessionId,
client_role: clientRole,
parent_client_id: parentClientId,
audio_latency: await context.jamClient.FTUEGetExpectedLatency().latency
}).then(async (response) => {
this.isJoinDeferredResolved = true
if (!this.inSession()) {
// the user has left the session before they got joined. We need to issue a leave again to the server to make sure they are out
logger.debug("user left before fully joined to session. telling server again that they have left");
this.leaveSessionRest(this.currentSessionId);
}
this.updateSessionInfo(response, true);
this.issueChange();
logger.debug("calling jamClient.JoinSession");
// on temporary disconnect scenarios, a user may already be in a session when they enter this path
// so we avoid double counting
if (!this.alreadyInSession()) {
if (this.participants().length === 1) {
context.JK.GA.trackSessionMusicians(context.JK.GA.SessionCreationTypes.create);
} else {
context.JK.GA.trackSessionMusicians(context.JK.GA.SessionCreationTypes.join);
}
}
this.recordingModel.reset(this.currentSessionId);
const joinSessionMsg = {sessionID: this.currentSessionId, music_session_id_int: response.music_session_id_int};
await context.jamClient.JoinSession(joinSessionMsg);
//@refreshCurrentSession(true);
context.JK.JamServer.registerMessageCallback(context.JK.MessageType.SESSION_JOIN, this.trackChanges);
context.JK.JamServer.registerMessageCallback(context.JK.MessageType.SESSION_DEPART, this.trackChanges);
context.JK.JamServer.registerMessageCallback(context.JK.MessageType.TRACKS_CHANGED, this.trackChanges);
context.JK.JamServer.registerMessageCallback(context.JK.MessageType.HEARTBEAT_ACK, this.trackChanges);
if (document) { $(document).trigger(EVENTS.SESSION_STARTED, {session: {id: this.currentSessionId, lesson_session: response.lesson_session}}); }
this.handleAutoOpenJamTrack();
this.watchBackendStats();
ConfigureTracksActions.reset(true);
this.delayEnableVst();
logger.debug("completed session join")
}).catch((xhr) => {
let leaveBehavior;
this.updateCurrentSession(null);
if (xhr.status === 404) {
// we tried to join the session, but it is already gone. kick user back to join session screen
leaveBehavior = {
location: "/client#/findSession",
notify: {
title: "Unable to Join Session",
text: " The session you attempted to join is over."
}
};
SessionActions.leaveSession.trigger(leaveBehavior);
} else if (xhr.status === 422) {
let buttons;
const response = JSON.parse(xhr.responseText);
if (response["errors"] && response["errors"]["tracks"] && (response["errors"]["tracks"][0] === "Please select at least one track")) {
return this.app.notifyAlert("No Inputs Configured", $('<span>You will need to reconfigure your audio device.</span>'));
} else if (response["errors"] && response["errors"]["music_session"] && (response["errors"]["music_session"][0] === ["is currently recording"])) {
leaveBehavior = {
location: "/client#/findSession",
notify: {
title: "Unable to Join Session",
text: "The session is currently recording."
}
};
SessionActions.leaveSession.trigger(leaveBehavior);
} else if (response["errors"] && response["errors"]["remaining_session_play_time"]) {
leaveBehavior =
{location: "/client#/findSession"};
buttons = [];
buttons.push({name: 'CLOSE', buttonStyle: 'button-grey'});
buttons.push({name: 'COMPARE PLANS', buttonStyle: 'button-grey', click: (() => (this.openBrowserToPlanComparison()))});
buttons.push({
name: 'UPGRADE PLAN',
buttonStyle: 'button-orange',
click: (() => (this.openBrowserToPayment()))
});
context.JK.Banner.show({
title: "Out of Time For This Session",
html: context._.template($('#template-no-remaining-session-play-time').html(), {}, { variable: 'data' }),
buttons});
SessionActions.leaveSession.trigger(leaveBehavior);
} else if (response["errors"] && response["errors"]["remaining_month_play_time"]) {
leaveBehavior =
{location: "/client#/findSession"};
buttons = [];
buttons.push({name: 'CLOSE', buttonStyle: 'button-grey'});
buttons.push({name: 'COMPARE PLANS', buttonStyle: 'button-grey', click: (() => (this.openBrowserToPlanComparison()))});
buttons.push({
name: 'UPGRADE PLAN',
buttonStyle: 'button-orange',
click: (() => (this.openBrowserToPayment()))
});
context.JK.Banner.show({
title: "Out of Time for the Month",
html: context._.template($('#template-no-remaining-month-play-time').html(), {}, { variable: 'data' }),
buttons});
SessionActions.leaveSession.trigger(leaveBehavior);
} else {
this.app.notifyServerError(xhr, 'Unable to Join Session');
}
} else {
this.app.notifyServerError(xhr, 'Unable to Join Session');
}
})
}`
# delayEnableVst: () ->
# if @enableVstTimeout?
# clearTimeout(@enableVstTimeout)
# @enableVstTimeout = null
# isVstLoaded = context.jamClient.IsVstLoaded()
# hasVstAssignment = context.jamClient.hasVstAssignment()
# if hasVstAssignment && !isVstLoaded
# @enableVstTimeout = setTimeout((() =>
# @enableVst()
# ), 5000)
# @issueChange()
delayEnableVst: `async function() {
if (this.enableVstTimeout != null) {
clearTimeout(this.enableVstTimeout);
this.enableVstTimeout = null;
}
const isVstLoaded = await context.jamClient.IsVstLoaded();
const hasVstAssignment = await context.jamClient.hasVstAssignment();
if (hasVstAssignment && !isVstLoaded) {
this.enableVstTimeout = setTimeout((() => {
return this.enableVst();
}
), 5000);
this.issueChange();
}
}`
enableVst: () ->
@enableVstTimeout = null
if @inSession()
ConfigureTracksActions.enableVst()
else
logger.debug("no longer in session; not enabling VSTs at this time")
@issueChange()
watchBackendStats: () ->
@backendStatsInterval = window.setInterval((() => (@updateBackendStats())), 1000)
# updateBackendStats: () ->
# connectionStats = window.jamClient.getConnectionDetail('', false)
# parentConnectionStats = window.jamClient.getConnectionDetail('', true)
# #console.log("CONNECTION STATES", connectionStats)
# #console.log("PARENT STATES", parentConnectionStats)
# SessionStatsActions.pushStats(connectionStats, parentConnectionStats)
updateBackendStats: `async function() {
//const clientId = await context.jamClient.clientID();
//const parentClientId = await context.jamClient.getParentClientId();
const clientId = context.JK.app.clientId
const connectionStats = await window.jamClient.getConnectionDetail('', false);
const parentConnectionStats = await window.jamClient.getConnectionDetail('', true);
SessionStatsActions.pushStats(connectionStats, parentConnectionStats);
}`
trackChanges: (header, payload) ->
if @currentTrackChanges < payload.track_changes_counter
# we don't have the latest info. try and go get it
logger.debug("track_changes_counter = stale. refreshing...")
@refreshCurrentSession();
else
if header.type != 'HEARTBEAT_ACK'
# don't log if HEARTBEAT_ACK, or you will see this log all the time
logger.info("track_changes_counter = fresh. skipping refresh...", header, payload)
# handleAutoOpenJamTrack: () ->
# jamTrack = @sessionUtils.grabAutoOpenJamTrack();
# if jamTrack
# # give the session to settle just a little (call a timeout of 1 second)
# setTimeout(()=>
# # tell the server we are about to open a jamtrack
# rest.openJamTrack({id: @currentSessionId, jam_track_id: jamTrack.id})
# .done((response) =>
# logger.debug("jamtrack opened")
# # now actually load the jamtrack
# context.SessionActions.updateSession.trigger(response);
# # context.JK.CurrentSessionModel.updateSession(response);
# # loadJamTrack(jamTrack);
# JamTrackActions.open(jamTrack)
# )
# .fail((jqXHR) =>
# @app.notifyServerError(jqXHR, "Unable to Open JamTrack For Playback")
# )
# , 1000)
handleAutoOpenJamTrack: `async function() {
const jamTrack = this.sessionUtils.grabAutoOpenJamTrack();
if (jamTrack) {
// give the session to settle just a little (call a timeout of 1 second)
setTimeout(()=> {
// tell the server we are about to open a jamtrack
rest.openJamTrack({id: this.currentSessionId, jam_track_id: jamTrack.id})
.done(response => {
logger.debug("jamtrack opened");
// now actually load the jamtrack
context.SessionActions.updateSession.trigger(response);
// context.JK.CurrentSessionModel.updateSession(response);
// loadJamTrack(jamTrack);
JamTrackActions.open(jamTrack);
})
.fail(jqXHR => {
this.app.notifyServerError(jqXHR, "Unable to Open JamTrack For Playback");
});
}
, 1000);
}
}`
inSession: () ->
!!@currentSessionId
alreadyInSession: () ->
inSession = false
for participant in @participants()
if participant.user.id == context.JK.currentUserId
inSession = true
break
participants: () ->
if @currentSession
@currentSession.participants;
else
[]
refreshCurrentSession: (force) ->
logger.debug("refreshCurrentSession(force=true)") if force
@refreshCurrentSessionRest(force)
refreshCurrentSessionRest: (force) ->
unless @inSession()
logger.debug("refreshCurrentSession skipped: ")
return
if @requestingSessionRefresh
# if someone asks for a refresh while one is going on, we ask for another to queue up
logger.debug("queueing refresh")
@pendingSessionRefresh = true;
else
@requestingSessionRefresh = true
rest.getSession(@currentSessionId)
.done((response) =>
try
@updateSessionInfo(response, force)
catch e
logger.error("unable to updateSessionInfo in session refresh", e)
#setTimeout(() =>
# @updateSessionInfo(response, force)
#, 5000)
)
.fail((jqXHR) =>
if jqXHR.status != 404
@app.notifyServerError(jqXHR, "Unable to refresh session data")
else
logger.debug("refreshCurrentSessionRest: could not refresh data for session because it's gone")
)
.always(() =>
@requestingSessionRefresh = false
if @pendingSessionRefresh
# and when the request is done, if we have a pending, fire it off again
@pendingSessionRefresh = false
@refreshCurrentSessionRest(force)
)
onUpdateSession: (session) ->
@updateSessionInfo(session, true)
# updateSessionInfo: (session, force) ->
# console.log("_DEBUG_* SessionStore#updateSessionInfo", session)
# if force == true || @currentTrackChanges < session.track_changes_counter
# logger.debug("updating current track changes from %o to %o", @currentTrackChanges, session.track_changes_counter)
# @currentTrackChanges = session.track_changes_counter
# @sendClientParticipantChanges(@currentSession, session)
# console.log("_DEBUG_* recordingState", recordingState);
# logger.debug('update current session')
# @updateCurrentSession(session);
# #if(callback != null) {
# # callback();
# #}
# else
# logger.info("ignoring refresh because we already have current: " + @currentTrackChanges + ", seen: " + session.track_changes_counter);
# updateSessionInfo: `function(session, force) {
# console.log("_DEBUG_* SessionStore#updateSessionInfo", session);
# if ((force === true) || (this.currentTrackChanges < session.track_changes_counter)) {
# logger.debug("updating current track changes from %o to %o", this.currentTrackChanges, session.track_changes_counter);
# this.currentTrackChanges = session.track_changes_counter;
# this.sendClientParticipantChanges(this.currentSession, session);
# this.recordingModel.getRecordingState().then(
# (recordingState) => {
# if (recordingState) {
# //merge the recording state with the session
# console.log("_DEBUG_* SessionStore getRecordingState -> recordingState", recordingState);
# session = {...session, ...recordingState};
# }
# }
# ).finally(() => {
# console.log("_DEBUG_* SessionStore final -> recordingState", session);
# logger.debug('update current session');
# this.updateCurrentSession(session);
# //if(callback != null) {
# // callback();
# //}
# })
# } else {
# return logger.info("ignoring refresh because we already have current: " + this.currentTrackChanges + ", seen: " + session.track_changes_counter);
# }
# }`
updateSessionInfo: `function (session, force) {
if ((force === true) || (this.currentTrackChanges < session.track_changes_counter)) {
logger.debug("updating current track changes from %o to %o", this.currentTrackChanges, session.track_changes_counter);
this.currentTrackChanges = session.track_changes_counter;
this.sendClientParticipantChanges(this.currentSession, session);
this.recordingModel.getCurrentRecordingState().then((recordingState) => {
session = { ...session, ...recordingState };
logger.debug('update current session');
}).finally(() => {
//console.log("_DEBUG_* SessionStore#updateSessionInfo sessionState", session);
this.updateCurrentSession(session);
});
} else {
return logger.info("ignoring refresh because we already have current: " + this.currentTrackChanges + ", seen: " + session.track_changes_counter);
}
}`
leaveSessionRest: () ->
#alert('leaveSessionRest')
rest.deleteParticipant(@app.clientId);
sendClientParticipantChanges: (oldSession, newSession) ->
joins = []
leaves = []
leaveJoins = []; # Will hold JamClientParticipants
oldParticipants = []; # will be set to session.participants if session
oldParticipantIds = {};
newParticipants = [];
newParticipantIds = {};
if oldSession && oldSession.participants
for oldParticipant in oldSession.participants
oldParticipantIds[oldParticipant.client_id] = oldParticipant
if newSession && newSession.participants
for newParticipant in newSession.participants
newParticipantIds[newParticipant.client_id] = newParticipant
for client_id, participant of newParticipantIds
# grow the 'all participants seen' list
unless (client_id of @participantsEverSeen)
@participantsEverSeen[client_id] = participant;
if client_id of oldParticipantIds
# if the participant is here now, and here before, there is still a chance we missed a
# very fast leave/join. So check if joined_session_at is different
if oldParticipantIds[client_id].joined_session_at != participant.joined_session_at
leaveJoins.push(participant)
else
# new participant id that's not in old participant ids: Join
joins.push(participant);
for client_id, participant of oldParticipantIds
unless (client_id of newParticipantIds)
# old participant id that's not in new participant ids: Leave
leaves.push(participant);
for i, v of joins
if v.client_id != @app.clientId
@participantJoined(newSession, v)
for i,v of leaves
if v.client_id != @app.clientId
@participantLeft(newSession, v)
for i,v of leaveJoins
if v.client_id != @app.clientId
logger.debug("participant had a rapid leave/join")
@participantLeft(newSession, v)
@participantJoined(newSession, v)
# participantJoined: (newSession, participant) ->
# logger.debug("jamClient.ParticipantJoined", participant.client_id)
# context.jamClient.ParticipantJoined(newSession, @toJamClientParticipant(participant));
# @currentParticipants[participant.client_id] = {server: participant, client: {audio_established: null}}
participantJoined: `async function(newSession, participant) {
logger.debug("jamClient.ParticipantJoined", participant.client_id);
await context.jamClient.ParticipantJoined(newSession, this.toJamClientParticipant(participant));
this.currentParticipants[participant.client_id] = {server: participant, client: {audio_established: null}};
}`
# participantLeft: (newSession, participant) ->
# logger.debug("jamClient.ParticipantLeft", participant.client_id)
# context.jamClient.ParticipantLeft(newSession, @toJamClientParticipant(participant));
# delete @currentParticipants[participant.client_id]
participantLeft: `async function(newSession, participant) {
logger.debug("jamClient.ParticipantLeft", participant.client_id);
await context.jamClient.ParticipantLeft(newSession, this.toJamClientParticipant(participant));
delete this.currentParticipants[participant.client_id];
}`
toJamClientParticipant: (participant) ->
{
userID: "",
clientID: participant.client_id,
client_id_int: participant.client_id_int,
tcpPort: 0,
udpPort: 0,
localIPAddress: participant.ip_address, # ?
globalIPAddress: participant.ip_address, # ?
latency: 0,
natType: ""
}
recordingRegistration: () ->
logger.debug("recording registration not hooked up yet")
# updateCurrentSession: (sessionData) ->
# if sessionData != null
# @currentOrLastSession = sessionData
# if sessionData.session_rules
# @sessionRules = sessionData.session_rules
# # TESTING:
# #@sessionRules.remaining_session_play_time = 60 * 15 + 15 # 15 minutes and 15 seconds
# # compute timestamp due time
# if @sessionRules.remaining_session_play_time?
# until_time = new Date()
# until_time = new Date(until_time.getTime() + @sessionRules.remaining_session_play_time * 1000)
# console.log("subscription: session has remaining play time", until_time)
# @sessionRules.remaining_session_until = until_time
# if sessionData.subscription
# # for the backend - it looks here
# #sessionData.subscription = sessionData.subscription_rules
# # let the backend know
# #context.jamClient.applySubscriptionPolicy()
# @subscriptionRules = sessionData.subscription
# # TESTING:
# #@subscriptionRules.remaining_month_play_time = 60 * 15 + 15 # 15 minutes and 15 seconds
# if @subscriptionRules.remaining_month_play_time?
# until_time = new Date()
# until_time = new Date(until_time.getTime() + @subscriptionRules.remaining_month_play_time * 1000)
# #until_time.setSeconds(until_time.getSeconds() + @subscriptionRules.remaining_month_play_time)
# console.log("subscription: month has remaining play time", until_time)
# @subscriptionRules.remaining_month_until = until_time
# @currentSession = sessionData
# if context.jamClient.UpdateSessionInfo?
# if @currentSession?
# context.jamClient.UpdateSessionInfo(@currentSession)
# else
# context.jamClient.UpdateSessionInfo({})
# #logger.debug("session changed")
# logger.debug("issue change")
# @issueChange()
updateCurrentSession: `async function(sessionData) {
//console.log("_DEBUG_* SessionStore#updateCurrentSession", sessionData)
if (sessionData !== null) {
let until_time;
this.currentOrLastSession = sessionData;
if (sessionData.session_rules) {
this.sessionRules = sessionData.session_rules;
// TESTING:
//@sessionRules.remaining_session_play_time = 60 * 15 + 15 # 15 minutes and 15 seconds
// compute timestamp due time
if (this.sessionRules.remaining_session_play_time != null) {
until_time = new Date();
until_time = new Date(until_time.getTime() + (this.sessionRules.remaining_session_play_time * 1000));
console.log("subscription: session has remaining play time", until_time);
this.sessionRules.remaining_session_until = until_time;
}
}
if (sessionData.subscription) {
// for the backend - it looks here
//sessionData.subscription = sessionData.subscription_rules
// let the backend know
//context.jamClient.applySubscriptionPolicy()
this.subscriptionRules = sessionData.subscription;
// TESTING:
//@subscriptionRules.remaining_month_play_time = 60 * 15 + 15 # 15 minutes and 15 seconds
if (this.subscriptionRules.remaining_month_play_time != null) {
until_time = new Date();
until_time = new Date(until_time.getTime() + (this.subscriptionRules.remaining_month_play_time * 1000));
//until_time.setSeconds(until_time.getSeconds() + @subscriptionRules.remaining_month_play_time)
console.log("subscription: month has remaining play time", until_time);
this.subscriptionRules.remaining_month_until = until_time;
}
}
}
this.currentSession = sessionData;
//if (context.jamClient.UpdateSessionInfo != null) {
if (this.currentSession != null) {
await context.jamClient.UpdateSessionInfo(this.currentSession);
} else {
await context.jamClient.UpdateSessionInfo({});
}
//}
//logger.debug("session changed")
logger.debug("issue change");
return this.issueChange();
}`
ensureConnected: () ->
unless context.JK.JamServer.connected
leaveBehavior =
location: '/client#/home'
notify:
title: "Not Connected"
text: 'To create or join a session, you must be connected to the server.'
SessionActions.leaveSession.trigger(leaveBehavior)
context.JK.JamServer.connected
# called by anyone wanting to leave the session with a certain behavior
# onLeaveSession: (behavior) ->
# logger.debug("attempting to leave session", behavior)
# if behavior.notify
# @app.layout.notify(behavior.notify)
# SessionActions.allowLeaveSession.trigger()
# if behavior.location
# if jQuery.isNumeric(behavior.location)
# window.history.go(behavior.location)
# else
# window.location = behavior.location
# else if behavior.hash
# window.location.hash = behavior.hash
# else
# logger.warn("no location specified in leaveSession action", behavior)
# window.location = '/client#/home'
# #VideoActions.stopVideo()
# if @currentSession?.lesson_session?
# isTeacher = context.JK.currentUserId == @currentSession.lesson_session.teacher_id
# tempSession = @currentSession
# rest.ratingDecision({
# as_student: !isTeacher,
# teacher_id: @currentSession.lesson_session.teacher_id,
# student_id: @currentSession.lesson_session.teacher_id
# }).done(((decision) =>
# showDialog = !decision.rating || showDialog = decision.lesson_count % 6 == 0
# if showDialog
# if isTeacher
# @app.layout.showDialog('rate-user-dialog', {d1: 'student_' + tempSession.lesson_session.student_id})
# else
# @app.layout.showDialog('rate-user-dialog', {d1: 'teacher_' + tempSession.lesson_session.teacher_id})
# else
# unless @rateSessionDialog?
# @rateSessionDialog = new context.JK.RateSessionDialog(context.JK.app);
# @rateSessionDialog.initialize();
# @rateSessionDialog.showDialog();
# ))
# else
# unless @rateSessionDialog?
# @rateSessionDialog = new context.JK.RateSessionDialog(context.JK.app);
# @rateSessionDialog.initialize();
# @rateSessionDialog.showDialog();
# alert('calling leaveSession')
# @leaveSession()
# @sessionUtils.SessionPageLeave()
onLeaveSession: `async function(behavior) {
logger.debug("attempting to leave session", behavior);
if (behavior.notify) {
this.app.layout.notify(behavior.notify);
}
SessionActions.allowLeaveSession.trigger();
if (behavior.location) {
if (jQuery.isNumeric(behavior.location)) {
window.history.go(behavior.location);
} else {
window.location = behavior.location;
}
} else if (behavior.hash) {
window.location.hash = behavior.hash;
} else {
logger.warn("no location specified in leaveSession action", behavior);
window.location = '/client#/home';
}
//VideoActions.stopVideo()
if ((this.currentSession != null ? this.currentSession.lesson_session : undefined) != null) {
const isTeacher = context.JK.currentUserId === this.currentSession.lesson_session.teacher_id;
const tempSession = this.currentSession;
rest.ratingDecision({
as_student: !isTeacher,
teacher_id: this.currentSession.lesson_session.teacher_id,
student_id: this.currentSession.lesson_session.teacher_id
}).done((decision => {
var showDialog = !decision.rating || (showDialog = (decision.lesson_count % 6) === 0);
if (showDialog) {
if (isTeacher) {
return this.app.layout.showDialog('rate-user-dialog', {d1: 'student_' + tempSession.lesson_session.student_id});
} else {
return this.app.layout.showDialog('rate-user-dialog', {d1: 'teacher_' + tempSession.lesson_session.teacher_id});
}
} else {
if (this.rateSessionDialog == null) {
this.rateSessionDialog = new context.JK.RateSessionDialog(context.JK.app);
this.rateSessionDialog.initialize();
return this.rateSessionDialog.showDialog();
}
}
}));
} else {
if (this.rateSessionDialog == null) {
this.rateSessionDialog = new context.JK.RateSessionDialog(context.JK.app);
this.rateSessionDialog.initialize();
}
this.rateSessionDialog.showDialog();
}
await this.leaveSession();
await this.sessionUtils.SessionPageLeave();
}`
# leaveSession: () ->
# #if !@joinDeferred? || @joinDeferred?.state() == 'resolved'
# if !@joinDeferred?
# deferred = new $.Deferred()
# @recordingModel.stopRecordingIfNeeded()
# .always(()=>
# @performLeaveSession(deferred)
# )
leaveSession: `async function() {
if ((this.joinDeferred == null) || this.isJoinDeferredResolved) {
const deferred = new $.Deferred();
const self = this;
/*Promise.resolve(this.recordingModel.stopRecordingIfNeeded())
.then(async function() {
await self.performLeaveSession(deferred)
});*/
await self.performLeaveSession(deferred);
}
}`
# performLeaveSession: (deferred) ->
# logger.debug("SessionModel.leaveCurrentSession()")
# # TODO - sessionChanged will be called with currentSession = null\
# # leave the session right away without waiting on REST. Why? If you can't contact the server, or if it takes a long
# # time, for that entire duration you'll still be sending voice data to the other users.
# # this may be bad if someone decides to badmouth others in the left-session during this time
# logger.debug("performLeaveSession: calling jamClient.LeaveSession for clientId=" + @app.clientId)
# context.jamClient.LeaveSession({ sessionID: @currentSessionId })
# @leaveSessionRest(@currentSessionId)
# .done(=>
# deferred.resolve(arguments[0], arguments[1], arguments[2]))
# .fail(=>
# deferred.reject(arguments[0], arguments[1], arguments[2]);
# )
# # 'unregister' for callbacks
# context.jamClient.SessionRegisterCallback("");
# #context.jamClient.SessionSetAlertCallback("");
# context.jamClient.SessionSetConnectionStatusRefreshRate(0);
# @sessionEnded()
# @issueChange()
performLeaveSession: `async function(deferred) {
logger.debug("SessionModel.leaveCurrentSession()");
logger.debug("performLeaveSession: calling jamClient.LeaveSession for clientId=" + this.app.clientId);
logger.debug('before context.jamClient.LeaveSession')
await context.jamClient.LeaveSession({ sessionID: this.currentSessionId });
logger.debug('before leaveSessionRest')
this.leaveSessionRest(this.currentSessionId)
.done(function() {
session = this.getCurrentOrLastSession();
context.SessionStore.updateSessionInfo(session, true)
logger.debug('leaveSessionRest done')
deferred.resolve(arguments[0], arguments[1], arguments[2]);}.bind(this))
.fail(function() {
logger.debug('leaveSessionRest fail')
deferred.reject(arguments[0], arguments[1], arguments[2]);
}.bind(this));
// 'unregister' for callbacks
await context.jamClient.SessionRegisterCallback("");
//context.jamClient.SessionSetAlertCallback("");
await context.jamClient.SessionSetConnectionStatusRefreshRate(0);
this.sessionEnded();
this.issueChange();
}`
selfOpenedJamTracks: () ->
@currentSession && (@currentSession.jam_track_initiator_id == context.JK.currentUserId)
sessionEnded: (onJoin) ->
# cleanup
context.JK.JamServer.unregisterMessageCallback(context.JK.MessageType.SESSION_JOIN, @trackChanges);
context.JK.JamServer.unregisterMessageCallback(context.JK.MessageType.SESSION_DEPART, @trackChanges);
context.JK.JamServer.unregisterMessageCallback(context.JK.MessageType.TRACKS_CHANGED, @trackChanges);
context.JK.JamServer.unregisterMessageCallback(context.JK.MessageType.HEARTBEAT_ACK, @trackChanges);
if @sessionPageEnterDeferred?
@sessionPageEnterDeferred.reject('session_over')
@sessionPageEnterDeferred = null
if @backendMixerAlertThrottleTimer
clearTimeout(@backendMixerAlertThrottleTimer)
@backendMixerAlertThrottleTimer = null
@userTracks = null;
@startTime = null;
if @backendStatsInterval?
window.clearInterval(@backendStatsInterval)
@backendStatsInterval = null
#if @joinDeferred?.state() == 'resolved'
if @joinDeferred?
$(document).trigger(EVENTS.SESSION_ENDED, {session: {id: @currentSessionId}})
@currentTrackChanges = 0
@currentSession = null
@joinDeferred = null
@isJoinDeferredResolved = false
@isRecording = false
@currentSessionId = null
@sessionRules = null
@subscriptionRules = null
@currentParticipants = {}
@previousAllTracks = {userTracks: [], backingTracks: [], metronomeTracks: []}
@openBackingTrack = null
@shownAudioMediaMixerHelp = false
@controlsLockedForJamTrackRecording = false
@openBackingTrack = null
@downloadingJamTrack = false
@sessionUtils.setAutoOpenJamTrack(null) unless onJoin
JamTrackActions.close()
NotificationActions.sessionEnded()
$(context.AppStore).triggerHandler('SessionEnded')
id: () ->
@currentSessionId
canRecord: () ->
if @subscriptionRules?
console.log("can record? rules:", @subscriptionRules.can_record_audio )
return @subscriptionRules.can_record_audio
else
console.log("can record? no rules; allow")
return true
canVideo: () ->
if @subscriptionRules?
console.log("can video? rules:", @subscriptionRules.can_use_video)
return @subscriptionRules.can_use_video
else
console.log("can video? no rules; allow")
return true
getCurrentOrLastSession: () ->
@currentOrLastSession
handleJoinLeaveRequestCallback: (data) ->
op = data["op"]
logger.debug("client asks #{op} for #{data["id"]}")
if op == "join"
sessionId = data["id"]
if sessionId != @currentSessionId
window.location = "/client#/session/" + sessionId
else
logger.debug("dropped #{op} because sessionId #{sessionId} matches currentSessionId")
else if op == "leave"
sessionId = data["id"]
if sessionId == @currentSessionId
@onLeaveSession({location: '/client#/home'})
else
logger.debug("dropped #{op} because sessionId #{sessionId} does not match currentSessionId #{@currentSessionId}")
}
)