jam-cloud/web/app/assets/javascripts/react-components/stores/SessionStoreModern.es6

1693 lines
60 KiB
JavaScript

const $ = jQuery;
const context = window;
const {
logger
} = context.JK;
const rest = context.JK.Rest();
const {
EVENTS
} = context.JK;
const {
MIX_MODES
} = context.JK;
const {
CLIENT_ROLE
} = context.JK;
const {
JamTrackActions
} = context;
const {
SessionActions
} = context;
const {
RecordingActions
} = context;
const {
NotificationActions
} = context;
const {
VideoActions
} = context;
const {
ConfigureTracksActions
} = context;
const shouldInstallModernSessionStore = !!context.__JK_SKIP_LEGACY_SESSION_STORE__;
context.__JK_SESSION_STORE_MODERN_LOAD_COUNT = (context.__JK_SESSION_STORE_MODERN_LOAD_COUNT || 0) + 1;
if (shouldInstallModernSessionStore) {
// Marker used by legacy SessionStore.js.coffee to avoid registering a second
// Reflux store in modern bundle builds.
context.__JK_USE_MODERN_SESSION_STORE__ = true;
logger.warn("[session-store] modern-load", {
count: context.__JK_SESSION_STORE_MODERN_LOAD_COUNT,
legacy_load_count: context.__JK_SESSION_STORE_LEGACY_LOAD_COUNT || 0
});
if (context.JK.DebugLogCollector && context.JK.DebugLogCollector.push) {
context.JK.DebugLogCollector.push("join-source.session-store.modern-load", {
count: context.__JK_SESSION_STORE_MODERN_LOAD_COUNT,
legacy_load_count: context.__JK_SESSION_STORE_LEGACY_LOAD_COUNT || 0
});
}
context.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,
recordingModel: null,
currentTrackChanges: 0,
isRecording: false,
previousAllTracks: {userTracks: [], backingTracks: [], metronomeTracks: []},
webcamViewer: null,
openBackingTrack: null,
helper: null,
downloadingJamTrack: false,
init() {
logger.warn("[session-store] modern-init");
if (context.JK.DebugLogCollector && context.JK.DebugLogCollector.push) {
context.JK.DebugLogCollector.push("join-source.session-store.modern-init", {});
}
// Register with the app store to get @app
this.listenTo(context.AppStore, this.onAppInit);
this.listenTo(context.RecordingStore, this.onRecordingChanged);
return this.listenTo(context.VideoStore, this.onVideoChanged);
},
onAppInit(app) {
this.app = app;
this.gearUtils = context.JK.GearUtilsInstance;
this.sessionUtils = context.JK.SessionUtils;
this.recordingModel = new context.JK.RecordingModel(this.app, rest, context.jamClientAdapter);
RecordingActions.initModel(this.recordingModel);
return this.helper = new context.SessionHelper(this.app, this.currentSession, this.participantsEverSeen, this.isRecording, this.downloadingJamTrack, (this.enableVstTimeout != null), this.sessionRules, this.subscriptionRules);
},
onSessionJoinedByOther(payload) {
const clientId = payload.client_id;
//parentClientId = context.jamClientAdapter.getParentClientId()
//if parentClientId? && parentClientId != ''
//if parentClientId == clientId
// auto nav to session
if (context.jamClientAdapter.getClientParentChildRole && (context.jamClientAdapter.getClientParentChildRole() === CLIENT_ROLE.CHILD) && (payload.source_user_id === context.JK.currentUserId)) {
logger.debug(`autonav to session ${payload.session_id}`);
return context.SessionActions.navToSession(payload.session_id);
}
},
onNavToSession(sessionId) {
return context.location = '/client#/session/' + sessionId;
},
onMixdownActive(mixdown) {
if ((this.currentSession != null ? this.currentSession.jam_track : undefined) != null) {
this.currentSession.jam_track.mixdown = mixdown;
return this.issueChange();
}
},
onVideoChanged(videoState) {
this.videoState = videoState;
},
issueChange() {
this.helper = new context.SessionHelper(this.app, this.currentSession, this.participantsEverSeen, this.isRecording, this.downloadingJamTrack, (this.enableVstTimeout != null), this.sessionRules, this.subscriptionRules);
return this.trigger(this.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')
//)
if (!this.inSession()) { return; }
// 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");
return SessionActions.leaveSession({location: '/client#/home'});
},
onBroadcastFailure(text) {
logger.debug("SESSION_LIVEBROADCAST_FAIL alert. reason:" + text);
if ((this.currentSession != null) && (this.currentSession.mount != null)) {
return rest.createSourceChange({
mount_id: this.currentSession.mount.id,
source_direction: true,
success: false,
reason: text,
client_id: this.app.clientId
});
} else {
return logger.debug("unable to report source change because no mount seen on session");
}
},
onBroadcastSuccess(text) {
logger.debug("SESSION_LIVEBROADCAST_ACTIVE alert. reason:" + text);
if ((this.currentSession != null) && (this.currentSession.mount != null)) {
return rest.createSourceChange({
mount_id: this.currentSession.mount.id,
source_direction: true,
success: true,
reason: text,
client_id: this.app.clientId
});
} else {
return logger.debug("unable to report source change because no mount seen on session");
}
},
onBroadcastStopped(text) {
logger.debug("SESSION_LIVEBROADCAST_STOPPED alert. reason:" + text);
if ((this.currentSession != null) && (this.currentSession.mount != null)) {
return rest.createSourceChange({
mount_id: this.currentSession.mount.id,
source_direction: false,
success: true,
reason: text,
client_id: this.app.clientId
});
} else {
return logger.debug("unable to report source change because no mount seen on session");
}
},
onShowNativeMetronomeGui() {
return context.jamClientAdapter.SessionShowMetronomeGui();
},
onOpenMetronome() {
const unstable = this.unstableNTPClocks();
if ((this.participants().length > 1) && (unstable.length > 0)) {
const names = unstable.join(", ");
logger.debug("Unstable clocks: ", names, unstable);
return context.JK.Banner.showAlert("Couldn't open metronome", context._.template($('#template-help-metronome-unstable').html(), {names}, { variable: 'data' }));
} else {
const data = {
value: 1,
session_size: this.participants().length,
user_id: context.JK.currentUserId,
user_name: context.JK.currentUserName
};
context.stats.write('web.metronome.open', data);
return rest.openMetronome({id: this.currentSessionId})
.done(response => {
MixerActions.openMetronome();
return this.updateSessionInfo(response, true);
})
.fail(jqXHR => {
return this.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) {
return context.jamClientAdapter.setMetronomeCricketTestState(isCricket);
},
unstableNTPClocks() {
const unstable = [];
// This should be handled in the below loop, actually:
const myState = context.jamClientAdapter.getMyNetworkState();
let map = null;
for (var participant of Array.from(this.participants())) {
var isStable;
var isSelf = participant.client_id === this.app.clientId;
if (isSelf) {
isStable = myState.ntp_stable;
} else {
map = context.jamClientAdapter.getPeerState(participant.client_id);
isStable = map.ntp_stable;
}
if (!isStable) {
var {
name
} = participant.user;
if (isSelf) {
name += " (this computer)";
}
unstable.push(name);
}
}
return unstable;
},
onDownloadingJamTrack(downloading) {
this.downloadingJamTrack = downloading;
return this.issueChange();
},
onToggleSessionVideo() {
if (this.videoState != null ? this.videoState.videoEnabled : undefined) {
logger.debug("toggle session video");
return VideoActions.toggleVideo();
} else {
return 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");
const response = context.jamClientAdapter.SessionAudioResync();
if (response != null) {
return this.app.notify({
"title": "Error",
"text": response,
"icon_url": "/assets/content/icon_alert_big.png"});
}
},
onSyncWithServer() {
return this.refreshCurrentSession(true);
},
onWatchedInputs(inputTracks) {
logger.debug("obtained tracks at start of session");
this.sessionPageEnterDeferred.resolve(inputTracks);
return this.sessionPageEnterDeferred = null;
},
onLog(detail) {
return logger.debug("SessionStore: OnLog", detail);
},
// codeInitiated means the user did not initiate this
onCloseMedia(codeInitiated) {
logger.debug("SessionStore: onCloseMedia", codeInitiated);
if (this.helper.recordedTracks()) {
return this.closeRecording();
} else if (this.helper.jamTracks() || this.downloadingJamTrack) {
return this.closeJamTrack();
} else if (this.helper.backingTrack() && this.helper.backingTrack().path) {
return this.closeBackingTrack();
} else if (this.helper.isMetronomeOpen()) {
return 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 (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.'});
return;
}
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.'});
return;
}
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"
});
});
context.jamClientAdapter.JamTrackStopPlay();
return JamTrackActions.close();
},
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);
return rest.openBackingTrack({id: this.currentSessionId, backing_track_path: result.file})
.done(() => {
const openResult = context.jamClientAdapter.SessionOpenBackingTrackFile(result.file, false);
if (openResult) {
// storing session state in memory, not in response of Session server response. bad.
return 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"
});
return this.closeBackingTrack();
}
})
.fail(jqXHR => {
return this.app.notifyServerError(jqXHR, "Unable to Open Backing Track For Playback");
});
}
},
closeRecording() {
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"
});
});
return context.jamClientAdapter.CloseRecording();
},
closeMetronomeTrack() {
logger.debug("SessionStore: closeMetronomeTrack");
return rest.closeMetronome({id: this.currentSessionId})
.done(() => {
context.jamClientAdapter.SessionCloseMetronome();
return this.refreshCurrentSession(true);
})
.fail(jqXHR => {
return 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 (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
context.jamClientAdapter.SessionStopPlay();
return context.jamClientAdapter.SessionCloseBackingTrackFile('');
},
onMixersChanged(type, text, trackInfo) {
if (!this.inSession()) { return; }
if (text === 'RebuildAudioIoControl') {
if (this.backendMixerAlertThrottleTimer) {
clearTimeout(this.backendMixerAlertThrottleTimer);
}
return this.backendMixerAlertThrottleTimer =
setTimeout(() => {
this.backendMixerAlertThrottleTimer = null;
if (this.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");
this.sessionPageEnterDeferred.resolve(trackInfo.userTracks);
this.sessionPageEnterDeferred = null;
}
return;
}
// wait until we are fully in session before trying to sync tracks to server
if (this.joinDeferred) {
return this.joinDeferred
.done(()=> {
return MixerActions.syncTracks();
});
}
}
, 100);
} else if (text === 'Midi-Track Update') {
logger.debug('midi track sync');
return MixerActions.syncTracks();
} else if ((text === 'RebuildMediaControl') || (text === 'RebuildRemoteUserControl')) {
const {
backingTracks
} = trackInfo;
const previousBackingTracks = this.previousAllTracks.backingTracks;
const {
metronomeTracks
} = trackInfo;
const previousMetronomeTracks = this.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 {
this.refreshCurrentSession(true);
}
return this.previousAllTracks = trackInfo;
} else if (text === 'Global Peer Input Mixer Mode') {
return MixerActions.mixerModeChanged(MIX_MODES.MASTER);
} else if (text === 'Local Peer Stream Mixer Mode') {
return MixerActions.mixerModeChanged(MIX_MODES.PERSONAL);
}
},
onRecordingChanged(details) {
let detail, reason, timeline, title;
logger.debug("SessionStore.onRecordingChanged: " + details.cause);
this.isRecording = details.isRecording;
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 = context.jamClientAdapter.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 {
this.promptUserToSave(details.recordingId, timeline);
}
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) {
return this.findUserBy({clientId})
.done(user=> {
return this.app.notify({
"title": title,
"text": user.name + " " + text,
"icon_url": context.JK.resolveAvatarUrl(user.photo_url)
});
})
.fail(()=> {
return this.app.notify({
"title": title,
"text": 'Someone ' + text,
"icon_url": "/assets/content/icon_alert_big.png"
});
});
},
findUserBy(finder) {
if (finder.clientId) {
let foundParticipant = null;
for (var participant of Array.from(this.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) {
let foundParticipant = null;
for (var participant of Array.from(this.participants())) {
if (participant.user.id === userId) {
foundParticipant = participant;
break;
}
}
return foundParticipant;
},
displayWhoCreatedRecording(clientId) {
if (this.app.clientId !== clientId) { // don't show to creator
return this.findUserBy({clientId})
.done(user => {
return this.app.notify({
"title": "Recording Started",
"text": user.name + " started a recording",
"icon_url": context.JK.resolveAvatarUrl(user.photo_url)
});
})
.fail(() => {
return this.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) {
return rest.getRecording( {id: recordingId} )
.done(recording => {
if (timeline) {
recording.timeline = timeline.global;
}
context.JK.recordingFinishedDialog.setRecording(recording);
return this.app.layout.showDialog('recordingFinished').one(EVENTS.DIALOG_CLOSED, (e, data) => {
if (data.result && data.result.keep) {
return context.JK.prodBubble($('#recording-manager-viewer'), 'file-manager-poke', {}, {positions:['top', 'left', 'right', 'bottom'], offsetParent: $('#session-screen').parent()});
}
});
})
.fail(this.app.ajaxError);
},
onEnterSession(sessionId) {
if (!context.JK.guardAgainstBrowser(this.app)) {
return false;
}
return window.location.href = '/client#/session/' + sessionId;
},
async onJoinSession(sessionId) {
logger.warn("[session-store] modern-onJoinSession", {session_id: sessionId});
// poke ShareDialog
const shareDialog = new JK.ShareDialog(this.app, sessionId, "session");
shareDialog.initialize(context.JK.FacebookHelperInstance);
// initialize webcamViewer
VideoActions.stopVideo();
// double-check that we are connected to the server via websocket
if (!this.ensureConnected()) { return; }
// just make double sure a previous session state is cleared out
this.sessionEnded(true);
// update the session data to be empty
this.updateCurrentSession(null);
// start setting data for this new session
this.currentSessionId = sessionId;
this.startTime = new Date().getTime();
// let's find out the public/private nature of this session,
// so that we can decide whether we need to validate the audio profile more aggressively
try {
const musicSession = await rest.getSessionHistory(this.currentSessionId);
return await this.onJoinSessionDone(musicSession);
} catch (e) {
return logger.error("unable to fetch session history", e);
}
},
async onJoinSessionDone(musicSession) {
const pushJoinAbort = (reason, detail) => {
context.__jkJoinAborts = context.__jkJoinAborts || [];
context.__jkJoinAborts.push({
ts: (new Date()).toISOString(),
reason,
detail,
current_session_id: this.currentSessionId
});
if (context.JK && context.JK.DebugLogCollector && context.JK.DebugLogCollector.push) {
context.JK.DebugLogCollector.push("join-source.session-store.modern-abort", {
reason,
detail,
current_session_id: this.currentSessionId,
stack: (new Error("SessionStoreModern.onJoinSessionDone.abort")).stack
});
}
};
const musicianAccessOnJoin = musicSession.musician_access;
const shouldVerifyNetwork = musicSession.musician_access;
let clientRole = CLIENT_ROLE.PARENT;
if (context.jamClientAdapter.getClientParentChildRole != null) {
clientRole = await context.jamClientAdapter.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();
return;
}
try {
await this.gearUtils.guardAgainstInvalidConfiguration(this.app, shouldVerifyNetwork);
} catch (e) {
pushJoinAbort("guardAgainstInvalidConfiguration", {error: String(e)});
SessionActions.leaveSession.trigger({location: '/client#/home'});
return;
}
const result = await this.sessionUtils.SessionPageEnter();
try {
await this.gearUtils.guardAgainstActiveProfileMissing(this.app, result);
} catch (data) {
const leaveBehavior = {};
if (data && (data.reason === 'handled')) {
if (data.nav === 'BACK') {
leaveBehavior.location = -1;
} else {
leaveBehavior.location = data.nav;
}
} else {
leaveBehavior.location = '/client#/home';
}
pushJoinAbort("guardAgainstActiveProfileMissing", data);
SessionActions.leaveSession.trigger(leaveBehavior);
return;
}
try {
this.userTracks = await this.waitForSessionPageEnterDone();
} catch (data) {
if (data === "timeout") {
context.JK.alertSupportedNeeded('The audio system has not reported your configured tracks in a timely fashion.');
} else if (data === '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: ' + data);
}
pushJoinAbort("waitForSessionPageEnterDone", {error: data});
SessionActions.leaveSession.trigger({location: '/client#/home'});
return;
}
try {
await this.ensureAppropriateProfile(musicianAccessOnJoin);
logger.debug("user has passed all session guards");
await this.joinSession();
} catch (result2) {
if (!result2 || !result2.controlled_location) {
SessionActions.leaveSession.trigger({location: "/client#/home"});
}
}
},
async waitForSessionPageEnterDone() {
this.sessionPageEnterDeferred = $.Deferred();
// see if we already have tracks; if so, we need to run with these
const inputTracks = await context.JK.TrackHelpers.getUserTracks(context.jamClientAdapter);
const isNoInputProfile = await this.gearUtils.isNoInputProfile();
logger.debug("isNoInputProfile", isNoInputProfile);
if ((inputTracks.length > 0) || isNoInputProfile) {
logger.debug("on page enter, tracks are already available");
this.sessionPageEnterDeferred.resolve(inputTracks);
const deferred = this.sessionPageEnterDeferred;
this.sessionPageEnterDeferred = null;
return await deferred;
}
this.sessionPageEnterTimeout = setTimeout(()=> {
if (this.sessionPageEnterTimeout) {
if (this.sessionPageEnterDeferred) {
this.sessionPageEnterDeferred.reject('timeout');
this.sessionPageEnterDeferred = null;
}
return this.sessionPageEnterTimeout = null;
}
}
, 5000);
return await this.sessionPageEnterDeferred;
},
ensureAppropriateProfile(musicianAccess) {
let deferred = new $.Deferred();
if (musicianAccess) {
deferred = context.JK.guardAgainstSinglePlayerProfile(this.app);
} else {
deferred.resolve();
}
return deferred;
},
openBrowserToPayment() {
return 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';
},
async joinSession() {
await context.jamClientAdapter.SessionRegisterCallback("JK.HandleBridgeCallback2");
await context.jamClientAdapter.RegisterRecordingCallbacks("JK.HandleRecordingStartResult", "JK.HandleRecordingStopResult", "JK.HandleRecordingStarted", "JK.HandleRecordingStopped", "JK.HandleRecordingAborted");
await context.jamClientAdapter.SessionSetConnectionStatusRefreshRate(1000);
let clientRole = await context.jamClientAdapter.getClientParentChildRole();
const parentClientId = await context.jamClientAdapter.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
const expectedLatency = await context.jamClientAdapter.FTUEGetExpectedLatency();
this.joinDeferred = rest.joinSession({
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: expectedLatency.latency
});
try {
const response = await this.joinDeferred;
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);
return;
}
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.jamClientAdapter.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}}); }
await this.handleAutoOpenJamTrack();
this.watchBackendStats();
ConfigureTracksActions.reset(true);
return await this.delayEnableVst();
} catch (xhr) {
let leaveBehavior;
this.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."
}
};
return 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."
}
};
return 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});
return 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});
return SessionActions.leaveSession.trigger(leaveBehavior);
} else {
return this.app.notifyServerError(xhr, 'Unable to Join Session');
}
} else {
return this.app.notifyServerError(xhr, 'Unable to Join Session');
}
}
},
delayEnableVst() {
if (this.enableVstTimeout != null) {
clearTimeout(this.enableVstTimeout);
this.enableVstTimeout = null;
}
const isVstLoaded = context.jamClientAdapter.IsVstLoaded();
const hasVstAssignment = context.jamClientAdapter.hasVstAssignment();
if (hasVstAssignment && !isVstLoaded) {
this.enableVstTimeout = setTimeout((() => {
return this.enableVst();
}
), 5000);
return this.issueChange();
}
},
enableVst() {
this.enableVstTimeout = null;
if (this.inSession()) {
ConfigureTracksActions.enableVst();
} else {
logger.debug("no longer in session; not enabling VSTs at this time");
}
return this.issueChange();
},
watchBackendStats() {
return this.backendStatsInterval = window.setInterval((() => (this.updateBackendStats())), 1000);
},
updateBackendStats() {
const connectionStats = window.jamClientAdapter.getConnectionDetail('', false);
const parentConnectionStats = window.jamClientAdapter.getConnectionDetail('', true);
//console.log("CONNECTION STATES", connectionStats)
//console.log("PARENT STATES", parentConnectionStats)
return SessionStatsActions.pushStats(connectionStats, parentConnectionStats);
},
trackChanges(header, payload) {
if (this.currentTrackChanges < payload.track_changes_counter) {
// we don't have the latest info. try and go get it
logger.debug("track_changes_counter = stale. refreshing...");
return this.refreshCurrentSession();
} else {
if (header.type !== 'HEARTBEAT_ACK') {
// don't log if HEARTBEAT_ACK, or you will see this log all the time
return logger.info("track_changes_counter = fresh. skipping refresh...", header, payload);
}
}
},
handleAutoOpenJamTrack() {
const jamTrack = this.sessionUtils.grabAutoOpenJamTrack();
if (jamTrack) {
// give the session to settle just a little (call a timeout of 1 second)
return setTimeout(()=> {
// tell the server we are about to open a jamtrack
return 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);
return JamTrackActions.open(jamTrack);
})
.fail(jqXHR => {
return this.app.notifyServerError(jqXHR, "Unable to Open JamTrack For Playback");
});
}
, 1000);
}
},
inSession() {
return !!this.currentSessionId;
},
alreadyInSession() {
let inSession = false;
return (() => {
const result = [];
for (var participant of Array.from(this.participants())) {
if (participant.user.id === context.JK.currentUserId) {
inSession = true;
break;
} else {
result.push(undefined);
}
}
return result;
})();
},
participants() {
if (this.currentSession) {
return this.currentSession.participants;
} else {
return [];
}
},
refreshCurrentSession(force) {
if (force) { logger.debug("refreshCurrentSession(force=true)"); }
return this.refreshCurrentSessionRest(force);
},
refreshCurrentSessionRest(force) {
if (!this.inSession()) {
logger.debug("refreshCurrentSession skipped: ");
return;
}
if (this.requestingSessionRefresh) {
// if someone asks for a refresh while one is going on, we ask for another to queue up
logger.debug("queueing refresh");
return this.pendingSessionRefresh = true;
} else {
this.requestingSessionRefresh = true;
return rest.getSession(this.currentSessionId)
.done(response => {
try {
return this.updateSessionInfo(response, force);
} catch (e) {
return logger.error("unable to updateSessionInfo in session refresh", e);
}
//setTimeout(() =>
// @updateSessionInfo(response, force)
//, 5000)
})
.fail(jqXHR => {
if (jqXHR.status !== 404) {
return this.app.notifyServerError(jqXHR, "Unable to refresh session data");
} else {
return logger.debug("refreshCurrentSessionRest: could not refresh data for session because it's gone");
}
})
.always(() => {
this.requestingSessionRefresh = false;
if (this.pendingSessionRefresh) {
// and when the request is done, if we have a pending, fire it off again
this.pendingSessionRefresh = false;
return this.refreshCurrentSessionRest(force);
}
});
}
},
onUpdateSession(session) {
return this.updateSessionInfo(session, true);
},
updateSessionInfo(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);
logger.debug('update current session');
return 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);
}
},
leaveSessionRest() {
if (context.JK && context.JK.DebugLogCollector && context.JK.DebugLogCollector.push) {
context.JK.DebugLogCollector.push("leave-source.session-store.modern-leaveSessionRest", {
current_session_id: this.currentSessionId,
client_id: this.app && this.app.clientId,
stack: (new Error("SessionStoreModern.leaveSessionRest")).stack
});
}
return rest.deleteParticipant(this.app.clientId);
},
sendClientParticipantChanges(oldSession, newSession) {
let client_id, i, participant, v;
const joins = [];
const leaves = [];
const leaveJoins = []; // Will hold JamClientParticipants
const oldParticipants = []; // will be set to session.participants if session
const oldParticipantIds = {};
const newParticipants = [];
const newParticipantIds = {};
if (oldSession && oldSession.participants) {
for (var oldParticipant of Array.from(oldSession.participants)) {
oldParticipantIds[oldParticipant.client_id] = oldParticipant;
}
}
if (newSession && newSession.participants) {
for (var newParticipant of Array.from(newSession.participants)) {
newParticipantIds[newParticipant.client_id] = newParticipant;
}
}
for (client_id in newParticipantIds) {
// grow the 'all participants seen' list
participant = newParticipantIds[client_id];
if (!(client_id in this.participantsEverSeen)) {
this.participantsEverSeen[client_id] = participant;
}
if (client_id in 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 in oldParticipantIds) {
participant = oldParticipantIds[client_id];
if (!(client_id in newParticipantIds)) {
// old participant id that's not in new participant ids: Leave
leaves.push(participant);
}
}
for (i in joins) {
v = joins[i];
if (v.client_id !== this.app.clientId) {
this.participantJoined(newSession, v);
}
}
for (i in leaves) {
v = leaves[i];
if (v.client_id !== this.app.clientId) {
this.participantLeft(newSession, v);
}
}
return (() => {
const result = [];
for (i in leaveJoins) {
v = leaveJoins[i];
if (v.client_id !== this.app.clientId) {
logger.debug("participant had a rapid leave/join");
this.participantLeft(newSession, v);
result.push(this.participantJoined(newSession, v));
} else {
result.push(undefined);
}
}
return result;
})();
},
participantJoined(newSession, participant) {
logger.debug("jamClient.ParticipantJoined", participant.client_id);
context.jamClientAdapter.ParticipantJoined(newSession, this.toJamClientParticipant(participant));
return this.currentParticipants[participant.client_id] = {server: participant, client: {audio_established: null}};
},
participantLeft(newSession, participant) {
logger.debug("jamClient.ParticipantLeft", participant.client_id);
context.jamClientAdapter.ParticipantLeft(newSession, this.toJamClientParticipant(participant));
return delete this.currentParticipants[participant.client_id];
},
toJamClientParticipant(participant) {
return {
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() {
return logger.debug("recording registration not hooked up yet");
},
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.jamClientAdapter.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.jamClientAdapter.UpdateSessionInfo != null) {
if (this.currentSession != null) {
context.jamClientAdapter.UpdateSessionInfo(this.currentSession);
} else {
context.jamClientAdapter.UpdateSessionInfo({});
}
}
//logger.debug("session changed")
logger.debug("issue change");
return this.issueChange();
},
ensureConnected() {
if (!context.JK.JamServer.connected) {
const 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);
}
return 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 (context.JK && context.JK.DebugLogCollector && context.JK.DebugLogCollector.push) {
context.JK.DebugLogCollector.push("leave-source.session-store.modern-onLeaveSession", {
behavior: behavior,
current_session_id: this.currentSessionId,
stack: (new Error("SessionStoreModern.onLeaveSession")).stack
});
}
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();
}
this.leaveSession();
return this.sessionUtils.SessionPageLeave();
},
leaveSession() {
if ((this.joinDeferred == null) || ((this.joinDeferred != null ? this.joinDeferred.state() : undefined) === 'resolved')) {
const deferred = new $.Deferred();
return this.recordingModel.stopRecordingIfNeeded()
.always(()=> {
return this.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=" + this.app.clientId);
context.jamClientAdapter.LeaveSession({ sessionID: this.currentSessionId });
this.leaveSessionRest(this.currentSessionId)
.done(function() {
return deferred.resolve(arguments[0], arguments[1], arguments[2]);}.bind(this))
.fail(function() {
return deferred.reject(arguments[0], arguments[1], arguments[2]);
}.bind(this));
// 'unregister' for callbacks
context.jamClientAdapter.SessionRegisterCallback("");
//context.jamClientAdapter.SessionSetAlertCallback("");
context.jamClientAdapter.SessionSetConnectionStatusRefreshRate(0);
this.sessionEnded();
return this.issueChange();
},
selfOpenedJamTracks() {
return this.currentSession && (this.currentSession.jam_track_initiator_id === context.JK.currentUserId);
},
sessionEnded(onJoin) {
// cleanup
context.JK.JamServer.unregisterMessageCallback(context.JK.MessageType.SESSION_JOIN, this.trackChanges);
context.JK.JamServer.unregisterMessageCallback(context.JK.MessageType.SESSION_DEPART, this.trackChanges);
context.JK.JamServer.unregisterMessageCallback(context.JK.MessageType.TRACKS_CHANGED, this.trackChanges);
context.JK.JamServer.unregisterMessageCallback(context.JK.MessageType.HEARTBEAT_ACK, this.trackChanges);
if (this.sessionPageEnterDeferred != null) {
this.sessionPageEnterDeferred.reject('session_over');
this.sessionPageEnterDeferred = null;
}
if (this.backendMixerAlertThrottleTimer) {
clearTimeout(this.backendMixerAlertThrottleTimer);
this.backendMixerAlertThrottleTimer = null;
}
this.userTracks = null;
this.startTime = null;
if (this.backendStatsInterval != null) {
window.clearInterval(this.backendStatsInterval);
this.backendStatsInterval = null;
}
if ((this.joinDeferred != null ? this.joinDeferred.state() : undefined) === 'resolved') {
$(document).trigger(EVENTS.SESSION_ENDED, {session: {id: this.currentSessionId}});
}
this.currentTrackChanges = 0;
this.currentSession = null;
this.joinDeferred = null;
this.isRecording = false;
this.currentSessionId = null;
this.sessionRules = null;
this.subscriptionRules = null;
this.currentParticipants = {};
this.previousAllTracks = {userTracks: [], backingTracks: [], metronomeTracks: []};
this.openBackingTrack = null;
this.shownAudioMediaMixerHelp = false;
this.controlsLockedForJamTrackRecording = false;
this.openBackingTrack = null;
this.downloadingJamTrack = false;
if (!onJoin) { this.sessionUtils.setAutoOpenJamTrack(null); }
JamTrackActions.close();
NotificationActions.sessionEnded();
return $(context.AppStore).triggerHandler('SessionEnded');
},
id() {
return this.currentSessionId;
},
canRecord() {
if (this.subscriptionRules != null) {
console.log("can record? rules:", this.subscriptionRules.can_record_audio );
return this.subscriptionRules.can_record_audio;
} else {
console.log("can record? no rules; allow");
return true;
}
},
canVideo() {
if (this.subscriptionRules != null) {
console.log("can video? rules:", this.subscriptionRules.can_use_video);
return this.subscriptionRules.can_use_video;
} else {
console.log("can video? no rules; allow");
return true;
}
},
getCurrentOrLastSession() {
return this.currentOrLastSession;
},
handleJoinLeaveRequestCallback(data) {
let sessionId;
const op = data["op"];
logger.debug(`client asks ${op} for ${data["id"]}`);
if (op === "join") {
sessionId = data["id"];
if (sessionId !== this.currentSessionId) {
return window.location = "/client#/session/" + sessionId;
} else {
return logger.debug(`dropped ${op} because sessionId ${sessionId} matches currentSessionId`);
}
} else if (op === "leave") {
sessionId = data["id"];
if (sessionId === this.currentSessionId) {
return this.onLeaveSession({location: '/client#/home'});
} else {
return logger.debug(`dropped ${op} because sessionId ${sessionId} does not match currentSessionId ${this.currentSessionId}`);
}
}
}
}
);
} else if (context.JK.DebugLogCollector && context.JK.DebugLogCollector.push) {
context.JK.DebugLogCollector.push("join-source.session-store.modern-skipped", {
reason: "legacy-bundle"
});
}