*wip
This commit is contained in:
parent
7a803bb682
commit
910b052204
|
|
@ -198,50 +198,56 @@
|
|||
|
||||
function loggedIn(header, payload) {
|
||||
|
||||
server.signedIn = true;
|
||||
server.clientID = payload.client_id;
|
||||
server.publicIP = payload.public_ip;
|
||||
// reason for setTimeout:
|
||||
// loggedIn causes an absolute ton of initialization to happen, and errors sometimes happen
|
||||
// but because loggedIn(header,payload) is a callback from a websocket, the browser doesn't show a stack trace...
|
||||
|
||||
if (context.jamClient !== undefined) {
|
||||
context.jamClient.connected = true;
|
||||
context.jamClient.clientID = server.clientID;
|
||||
}
|
||||
setTimeout(function() {
|
||||
server.signedIn = true;
|
||||
server.clientID = payload.client_id;
|
||||
server.publicIP = payload.public_ip;
|
||||
|
||||
clearConnectTimeout();
|
||||
if (context.jamClient !== undefined) {
|
||||
context.jamClient.connected = true;
|
||||
context.jamClient.clientID = server.clientID;
|
||||
}
|
||||
|
||||
heartbeatStateReset();
|
||||
clearConnectTimeout();
|
||||
|
||||
app.clientId = payload.client_id;
|
||||
heartbeatStateReset();
|
||||
|
||||
if(isClientMode()) {
|
||||
// tell the backend that we have logged in
|
||||
context.jamClient.OnLoggedIn(payload.user_id, payload.token); // ACTS AS CONTINUATION
|
||||
$.cookie('client_id', payload.client_id);
|
||||
}
|
||||
app.clientId = payload.client_id;
|
||||
|
||||
// this has to be after context.jamclient.OnLoggedIn, because it hangs in scenarios
|
||||
// where there is no device on startup for the current profile.
|
||||
// So, in that case, it's possible that a reconnect loop will attempt, but we *do not want*
|
||||
// it to go through unless we've passed through .OnLoggedIn
|
||||
server.connected = true;
|
||||
server.reconnecting = false;
|
||||
server.connecting = false;
|
||||
initialConnectAttempt = false;
|
||||
if (isClientMode()) {
|
||||
// tell the backend that we have logged in
|
||||
context.jamClient.OnLoggedIn(payload.user_id, payload.token); // ACTS AS CONTINUATION
|
||||
$.cookie('client_id', payload.client_id);
|
||||
}
|
||||
|
||||
heartbeatMS = payload.heartbeat_interval * 1000;
|
||||
connection_expire_time = payload.connection_expire_time * 1000;
|
||||
logger.info("loggedIn(): clientId=" + app.clientId + " heartbeat=" + payload.heartbeat_interval + "s expire_time=" + payload.connection_expire_time + 's');
|
||||
heartbeatInterval = context.setInterval(_heartbeat, heartbeatMS);
|
||||
heartbeatAckCheckInterval = context.setInterval(_heartbeatAckCheck, 1000);
|
||||
lastHeartbeatAckTime = new Date(new Date().getTime() + heartbeatMS); // add a little forgiveness to server for initial heartbeat
|
||||
connectDeferred.resolve();
|
||||
$self.triggerHandler(EVENTS.CONNECTION_UP)
|
||||
// this has to be after context.jamclient.OnLoggedIn, because it hangs in scenarios
|
||||
// where there is no device on startup for the current profile.
|
||||
// So, in that case, it's possible that a reconnect loop will attempt, but we *do not want*
|
||||
// it to go through unless we've passed through .OnLoggedIn
|
||||
server.connected = true;
|
||||
server.reconnecting = false;
|
||||
server.connecting = false;
|
||||
initialConnectAttempt = false;
|
||||
|
||||
activeElementEvent('afterConnect', payload);
|
||||
heartbeatMS = payload.heartbeat_interval * 1000;
|
||||
connection_expire_time = payload.connection_expire_time * 1000;
|
||||
logger.info("loggedIn(): clientId=" + app.clientId + " heartbeat=" + payload.heartbeat_interval + "s expire_time=" + payload.connection_expire_time + 's');
|
||||
heartbeatInterval = context.setInterval(_heartbeat, heartbeatMS);
|
||||
heartbeatAckCheckInterval = context.setInterval(_heartbeatAckCheck, 1000);
|
||||
lastHeartbeatAckTime = new Date(new Date().getTime() + heartbeatMS); // add a little forgiveness to server for initial heartbeat
|
||||
connectDeferred.resolve();
|
||||
$self.triggerHandler(EVENTS.CONNECTION_UP)
|
||||
|
||||
if(payload.client_update && context.JK.ClientUpdateInstance) {
|
||||
context.JK.ClientUpdateInstance.runCheck(payload.client_update.product, payload.client_update.version, payload.client_update.uri, payload.client_update.size)
|
||||
}
|
||||
activeElementEvent('afterConnect', payload);
|
||||
|
||||
if (payload.client_update && context.JK.ClientUpdateInstance) {
|
||||
context.JK.ClientUpdateInstance.runCheck(payload.client_update.product, payload.client_update.version, payload.client_update.uri, payload.client_update.size)
|
||||
}
|
||||
}, 0)
|
||||
}
|
||||
|
||||
function heartbeatAck(header, payload) {
|
||||
|
|
|
|||
|
|
@ -6,11 +6,12 @@
|
|||
var gearUtils = context.JK.GearUtilsInstance;
|
||||
var $dialog;
|
||||
var $screen = $('#session-settings');
|
||||
var $selectedFilenames = $screen.find('#selected-filenames');
|
||||
var $uploadSpinner = $screen.find('.upload-spinner');
|
||||
var $selectedFilenames = $('#settings-selected-filenames');
|
||||
//var $selectedFilenames = $screen.find('#selected-filenames');
|
||||
var $uploadSpinner = $screen.find('.spinner-small');
|
||||
//var $selectedFilenames = $('#settings-selected-filenames');
|
||||
var $inputFiles = $screen.find('#session-select-files');
|
||||
var $btnSelectFiles = $screen.find('.btn-select-files');
|
||||
var $inputBox = $screen.find('.inputbox')
|
||||
var rest = new JK.Rest();
|
||||
var sessionId;
|
||||
|
||||
|
|
@ -21,7 +22,7 @@
|
|||
context.JK.GenreSelectorHelper.render('#session-settings-genre');
|
||||
$dialog = $('[layout-id="session-settings"]');
|
||||
|
||||
var currentSession = sessionScreen.getCurrentSession();
|
||||
var currentSession = context.SessionStore.currentSession;
|
||||
sessionId = currentSession.id;
|
||||
|
||||
// id
|
||||
|
|
@ -65,13 +66,21 @@
|
|||
$('#session-settings-fan-access').val('listen-chat-band');
|
||||
}
|
||||
|
||||
// notation files
|
||||
/**
|
||||
// notation files in the account screen. ugh.
|
||||
$selectedFilenames.empty();
|
||||
for (var i=0; i < currentSession.music_notations.length; i++) {
|
||||
var notation = currentSession.music_notations[i];
|
||||
$selectedFilenames.append('<a href="' + notation.file_url + '" rel="external">' + notation.file_name + '</a> ');
|
||||
}*/
|
||||
|
||||
$inputBox.empty();
|
||||
for (var i=0; i < currentSession.music_notations.length; i++) {
|
||||
var notation = currentSession.music_notations[i];
|
||||
addNotation(notation)
|
||||
}
|
||||
|
||||
|
||||
context.JK.dropdown($('#session-settings-language'));
|
||||
context.JK.dropdown($('#session-settings-musician-access'));
|
||||
context.JK.dropdown($('#session-settings-fan-access'));
|
||||
|
|
@ -81,6 +90,29 @@
|
|||
$('#session-settings-fan-access').easyDropDown(easyDropDownState)
|
||||
}
|
||||
|
||||
function addNotation(notation) {
|
||||
|
||||
var $notation = $('<div class="notation-entry"><div>' + notation.file_name + '</div><a href="#" data-id="' + notation.id + '">X</a></div>')
|
||||
$notation.find('a').on('click', function(e) {
|
||||
|
||||
if($(this).attr('data-deleting')) {
|
||||
// ignore duplicate delete attempts
|
||||
return false;
|
||||
}
|
||||
|
||||
$(this).attr('data-deleting', true)
|
||||
var $notationEntry = $(this).closest('.notation-entry').find('div').text('deleting...')
|
||||
|
||||
rest.deleteMusicNotation({id: notation.id})
|
||||
.done(function() {
|
||||
$notation.remove()
|
||||
})
|
||||
.fail(app.ajaxError)
|
||||
return false;
|
||||
})
|
||||
$inputBox.append($notation);
|
||||
}
|
||||
|
||||
function saveSettings(evt) {
|
||||
|
||||
var data = {};
|
||||
|
|
@ -111,11 +143,7 @@
|
|||
data.fan_access = false;
|
||||
data.fan_chat = false;
|
||||
}
|
||||
else if (fanAccess == 'listen-chat-each') {
|
||||
data.fan_access = true;
|
||||
data.fan_chat = false;
|
||||
}
|
||||
else if (fanAccess == 'listen-chat-band') {
|
||||
else if (fanAccess == 'listen-chat') {
|
||||
data.fan_access = true;
|
||||
data.fan_chat = true;
|
||||
}
|
||||
|
|
@ -177,7 +205,7 @@
|
|||
}
|
||||
})
|
||||
.always(function() {
|
||||
$btnSelectFiles.text('SELECT FILES...').data('uploading', null)
|
||||
$btnSelectFiles.text('ADD FILES...').data('uploading', null)
|
||||
$uploadSpinner.hide();
|
||||
});
|
||||
}
|
||||
|
|
@ -203,10 +231,9 @@
|
|||
else {
|
||||
// upload as soon as user picks their files.
|
||||
uploadNotations($inputFiles.get(0).files)
|
||||
.done(function() {
|
||||
context._.each(fileNames, function(fileName) {
|
||||
var $text = $('<span>').text(fileName);
|
||||
$selectedFilenames.append($text);
|
||||
.done(function(response) {
|
||||
context._.each(response, function(notation) {
|
||||
addNotation(notation)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
|
@ -225,7 +252,7 @@
|
|||
|
||||
function settingsSaved(response) {
|
||||
// No response returned from this call. 204.
|
||||
sessionScreen.refreshCurrentSession(true);
|
||||
context.SessionActions.syncWithServer()
|
||||
app.layout.closeDialog('session-settings');
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -95,6 +95,14 @@
|
|||
});
|
||||
}
|
||||
|
||||
|
||||
function deleteMusicNotation(options) {
|
||||
return $.ajax({
|
||||
type: "DELETE",
|
||||
url: "/api/music_notations/" +options.id
|
||||
});
|
||||
}
|
||||
|
||||
function legacyJoinSession(options) {
|
||||
var sessionId = options["session_id"];
|
||||
delete options["session_id"];
|
||||
|
|
@ -1808,6 +1816,7 @@
|
|||
this.createScheduledSession = createScheduledSession;
|
||||
this.uploadMusicNotations = uploadMusicNotations;
|
||||
this.getMusicNotation = getMusicNotation;
|
||||
this.deleteMusicNotation = deleteMusicNotation;
|
||||
this.getBroadcastNotification = getBroadcastNotification;
|
||||
this.quietBroadcastNotification = quietBroadcastNotification;
|
||||
this.legacyJoinSession = legacyJoinSession;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,20 @@
|
|||
//= require bugsnag
|
||||
//= require bind-polyfill
|
||||
//= require jquery
|
||||
//= require jquery.monkeypatch
|
||||
//= require jquery_ujs
|
||||
//= require jquery.bt
|
||||
//= require jquery.icheck
|
||||
//= require jquery.easydropdown
|
||||
//= require classnames
|
||||
//= require reflux
|
||||
//= require AAC_underscore
|
||||
//= require AAA_Log
|
||||
//= require globals
|
||||
//= require jam_rest
|
||||
//= require ga
|
||||
//= require utils
|
||||
//= require react
|
||||
//= require react_ujs
|
||||
//= require react-init
|
||||
//= require react-components
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
//= require_directory ./react-components/helpers
|
||||
//= require_directory ./react-components/actions
|
||||
//= require ./react-components/stores/AppStore
|
||||
//= require ./react-components/stores/RecordingStore
|
||||
//= require ./react-components/stores/SessionStore
|
||||
//= require ./react-components/stores/MixerStore
|
||||
//= require ./react-components/stores/SessionMyTracksStore
|
||||
|
|
|
|||
|
|
@ -0,0 +1,131 @@
|
|||
context = window
|
||||
|
||||
mixins = []
|
||||
|
||||
# this check ensures we attempt to listen if this component is created in a popup
|
||||
if window.opener
|
||||
mixins.push(Reflux.listenTo(window.opener.RecordingStore,"onRecordingStateChanged"))
|
||||
|
||||
@PopupRecordingStartStop = React.createClass({
|
||||
|
||||
# this comes from the parent window
|
||||
mixins: mixins
|
||||
|
||||
onRecordingStateChanged: (recordingState) ->
|
||||
this.setState(isRecording: recordingState.isRecording, recordedOnce: this.state.recordedOnce || recordingState.isRecording)
|
||||
|
||||
startStopRecording: () ->
|
||||
if this.state.isRecording
|
||||
window.opener.RecordingActions.stopRecording()
|
||||
else
|
||||
window.opener.RecordingActions.startRecording()
|
||||
|
||||
onNoteShowHide: () ->
|
||||
this.setState(showNote: !this.state.showNote)
|
||||
|
||||
getInitialState: () ->
|
||||
{isRecording: window.ParentIsRecording, showNote: true, recordedOnce: false}
|
||||
|
||||
render: () ->
|
||||
|
||||
recordingVerb = if this.state.isRecording then 'Stop' else 'Start'
|
||||
|
||||
recordingBtnClasses = classNames({
|
||||
"currently-recording" : this.state.isRecording,
|
||||
"control" : true
|
||||
})
|
||||
|
||||
noteJSX = `<div className="important-note">
|
||||
<h5>
|
||||
Important Note
|
||||
</h5>
|
||||
<div className="contents">
|
||||
While playing in your session, you are listening to your own personal mix. This recording will use the master mix,
|
||||
which may sound very different. To hear and adjust your master mix settings, click the MIXER button in the session toolbar.
|
||||
</div>
|
||||
</div>`
|
||||
|
||||
recordingJSX = `<div className="recording-options">
|
||||
<div className="field">
|
||||
<input type="radio" name="recording-input-type" id="recording-input-both" defaultChecked="checked" />
|
||||
<label htmlFor="recording-input-both">Record both video and audio</label>
|
||||
<div className="clearall"></div>
|
||||
</div>
|
||||
<div className="field">
|
||||
<input type="radio" name="recording-input-type" id="recording-input-audio" />
|
||||
<label htmlFor="recording-input-audio">Record audio only</label>
|
||||
<div className="clearall"></div>
|
||||
</div>
|
||||
</div>`
|
||||
|
||||
if this.state.showNote
|
||||
noteText = 'hide note'
|
||||
else
|
||||
noteText = 'show note'
|
||||
|
||||
noteShowHideJSX = `<a href="#" className="note-show-hide" onClick={this.onNoteShowHide}>{noteText}</a>`
|
||||
|
||||
note = null
|
||||
recordingOptions = null
|
||||
noteShowHide = null
|
||||
|
||||
if this.state.showNote && !this.state.isRecording && !this.state.recordedOnce
|
||||
# should we show the note itself? Only if not recording, too
|
||||
note = noteJSX
|
||||
|
||||
if !this.state.isRecording && !this.state.recordedOnce
|
||||
noteShowHide = noteShowHideJSX
|
||||
|
||||
if gon.global.video_available == "full"
|
||||
recordingOptions = recordingJSX
|
||||
|
||||
|
||||
`<div className="recording-start-stop">
|
||||
<div className="control-holder">
|
||||
<a className={recordingBtnClasses} onClick={this.startStopRecording}>
|
||||
<span className="helper" />
|
||||
<img src="/assets/content/recordbutton-off.png" width="20" height="20" />
|
||||
<span id="recording-status">{recordingVerb} Recording</span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
{recordingOptions}
|
||||
|
||||
{note}
|
||||
|
||||
{noteShowHide}
|
||||
|
||||
</div>`
|
||||
|
||||
componentDidMount: () ->
|
||||
$root = jQuery(this.getDOMNode())
|
||||
|
||||
$recordingType = $root.find('input[type="radio"]')
|
||||
context.JK.checkbox($recordingType)
|
||||
|
||||
@resizeWindow()
|
||||
|
||||
# this is necessary due to whatever the client's rendering behavior is.
|
||||
setTimeout(@resizeWindow, 300)
|
||||
|
||||
componentDidUpdate: () ->
|
||||
@resizeWindow()
|
||||
|
||||
resizeWindow: () =>
|
||||
$container = $('#minimal-container')
|
||||
width = $container.width()
|
||||
height = $container.height()
|
||||
|
||||
# there is 20px or so of unused space at the top of the page. can't figure out why it's there. (above #minimal-container)
|
||||
mysteryTopMargin = 20
|
||||
|
||||
# deal with chrome in real browsers
|
||||
offset = (window.outerHeight - window.innerHeight) + mysteryTopMargin
|
||||
|
||||
# handle native client chrome that the above outer-inner doesn't catch
|
||||
#if navigator.userAgent.indexOf('JamKazam') > -1
|
||||
|
||||
#offset += 25
|
||||
|
||||
window.resizeTo(width, height + offset)
|
||||
})
|
||||
|
|
@ -35,6 +35,8 @@ MixerActions = @MixerActions
|
|||
WebkitTransform: "rotate(#{pan}deg)"
|
||||
}
|
||||
|
||||
# <div className="track-icon-equalizer" />
|
||||
|
||||
`<div className="session-track my-track">
|
||||
<div className="disabled-track-overlay" />
|
||||
<div className="session-track-contents">
|
||||
|
|
@ -46,7 +48,6 @@ MixerActions = @MixerActions
|
|||
<div className="track-buttons">
|
||||
<div className={classes} data-control="mute" data-mixer-id={muteMixerId} onClick={this.handleMute}/>
|
||||
<div className="track-icon-pan" style={panStyle}/>
|
||||
<div className="track-icon-equalizer" />
|
||||
</div>
|
||||
<br className="clearall"/>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@ ReactCSSTransitionGroup = React.addons.CSSTransitionGroup;
|
|||
session = sessionMixers.session
|
||||
mixers = sessionMixers.mixers
|
||||
|
||||
logger.debug("SessionOtherTracks: onInputsChanged")
|
||||
participants = []
|
||||
|
||||
if session.inSession()
|
||||
|
|
|
|||
|
|
@ -2,17 +2,32 @@ context = window
|
|||
|
||||
@SessionRecordBtn = React.createClass({
|
||||
|
||||
datAss: () ->
|
||||
alert("dat ass")
|
||||
mixins: [Reflux.listenTo(@MixerStore,"onSessionMixerChange")]
|
||||
|
||||
onSessionMixerChange: (sessionMixers) ->
|
||||
|
||||
this.setState({isRecording: sessionMixers.session.isRecording})
|
||||
|
||||
getInitialState: () ->
|
||||
{childWindow: null, isRecording: false}
|
||||
|
||||
openRecording: () ->
|
||||
recording = window.open("/popups/session/#{1}/recording-controls", 'Recording', 'scrollbars=yes,toolbar=no,status=no,height=315,width=350')
|
||||
|
||||
if this.state.childWindow?
|
||||
this.state.childWindow.close()
|
||||
|
||||
window.callMe = @datAss
|
||||
$(recording).on('load', ()->
|
||||
alert("loaded")
|
||||
recording.bling()
|
||||
childWindow = window.open("/popups/recording-controls", 'Recording', 'scrollbars=yes,toolbar=no,status=no,height=315,width=350')
|
||||
childWindow.ParentRecordingStore = context.RecordingStore
|
||||
childWindow.ParentIsRecording = this.state.isRecording
|
||||
|
||||
###
|
||||
$(childWindow).on('load', ()=>
|
||||
childWindow.focus()
|
||||
)
|
||||
###
|
||||
|
||||
this.setState({childWindow: childWindow})
|
||||
|
||||
render: () ->
|
||||
`<a className="session-record button-grey left" onClick={this.openRecording}>
|
||||
<img src="/assets/content/icon_settings_sm.png" align="texttop" height="12" width="12"/>
|
||||
|
|
|
|||
|
|
@ -2,9 +2,19 @@ context = window
|
|||
|
||||
@SessionResyncBtn = React.createClass({
|
||||
|
||||
mixins: [Reflux.listenTo(@AppStore,"onAppInit")]
|
||||
|
||||
resync: (e) ->
|
||||
e.preventDefault()
|
||||
|
||||
SessionActions.audioResync()
|
||||
|
||||
render: () ->
|
||||
`<a className="session-resync button-grey resync left">
|
||||
`<a className="session-resync button-grey resync left" onClick={this.resync}>
|
||||
<img src="/assets/content/icon_resync.png" align="texttop" height="12" width="12"/>
|
||||
RESYNC
|
||||
</a>`
|
||||
|
||||
onAppInit: (app) ->
|
||||
@app = app
|
||||
})
|
||||
|
|
@ -74,5 +74,4 @@ SessionActions = @SessionActions
|
|||
};
|
||||
|
||||
@app.bindScreen('session2', screenBindings);
|
||||
|
||||
})
|
||||
|
|
|
|||
|
|
@ -0,0 +1,121 @@
|
|||
context = window
|
||||
|
||||
MixerActions = @MixerActions
|
||||
|
||||
@SessionSelfVolumeHover = React.createClass({
|
||||
|
||||
|
||||
getInitialState: () ->
|
||||
{mixers: this.props.mixers}
|
||||
|
||||
handleMute: (e) ->
|
||||
e.preventDefault()
|
||||
|
||||
muting = $(e.currentTarget).is('.enabled')
|
||||
|
||||
MixerActions.mute([this.props.mixers.mixer], muting)
|
||||
|
||||
handleMuteCheckbox: (e) ->
|
||||
muting = $(e.target).is(':checked')
|
||||
|
||||
MixerActions.mute([this.props.mixers.mixer, this.props.mixers.oppositeMixer], muting)
|
||||
|
||||
render: () ->
|
||||
|
||||
monitorMuteMixer = this.props.inputGroupMixers.muteMixer
|
||||
monitorMuteMixerId = monitorMuteMixer?.id
|
||||
monitorVolumeLeft = this.props.inputGroupMixers.mixer?.volume_left
|
||||
monitorMuteClasses = classNames({
|
||||
'track-icon-mute': true
|
||||
'enabled' : !monitorMuteMixer?.mute
|
||||
'muted' : monitorMuteMixer?.mute
|
||||
})
|
||||
|
||||
chatMuteMixer = this.props.chatGroupMixers.muteMixer
|
||||
chatMuteMixerId = chatMuteMixer?.id
|
||||
chatVolumeLeft = this.props.chatGroupMixers.mixer?.volume_left
|
||||
chatMuteClasses = classNames({
|
||||
'track-icon-mute': true
|
||||
'enabled' : !chatMuteMixer?.mute
|
||||
'muted' : chatMuteMixer?.mute
|
||||
})
|
||||
|
||||
`<div id="self-volume-hover" >
|
||||
<div className="monitor-mixer mixer-holder">
|
||||
<h3>Music</h3>
|
||||
<div className="session-track track">
|
||||
<div className="track-vu-left">
|
||||
<SessionTrackVU orientation="vertical" lightCount={13} lightWidth={3} lightHeight={17} side="vul" mixers={this.props.inputGroupMixers} />
|
||||
</div>
|
||||
<div className="track-vu-right">
|
||||
<SessionTrackVU orientation="vertical" lightCount={13} lightWidth={3} lightHeight={17} side="vur" mixers={this.props.inputGroupMixers} />
|
||||
</div>
|
||||
<div className="track-label">
|
||||
<div>Volume</div>
|
||||
<div>{monitorVolumeLeft}dB</div>
|
||||
</div>
|
||||
<SessionTrackGain mixers={this.props.inputGroupMixers} />
|
||||
<div className={monitorMuteClasses} data-control="mute" data-mixer-id={monitorMuteMixerId} onClick={this.handleMute}/>
|
||||
|
||||
<input type="checkbox" name="mute"/>
|
||||
<label for="mute">Mute</label>
|
||||
</div>
|
||||
|
||||
<div className="textual-help">
|
||||
<p>Use this slider to control the volume of all the music in the session in your headphones or speakers.</p>
|
||||
<p>This will not affect the volume for other musicians in the session.</p>
|
||||
<p>To adjust master levels for all musicians for recordings and broadcasts, use Mixer button in the toolbar.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="chat-mixer mixer-holder">
|
||||
<h3>Chat</h3>
|
||||
<div className="session-track track">
|
||||
<div className="track-vu-left">
|
||||
<SessionTrackVU orientation="vertical" lightCount={13} lightWidth={3} lightHeight={17} side="vul" mixers={this.props.chatGroupMixers} />
|
||||
</div>
|
||||
<div className="track-vu-right">
|
||||
<SessionTrackVU orientation="vertical" lightCount={13} lightWidth={3} lightHeight={17} side="vur" mixers={this.props.chatGroupMixers} />
|
||||
</div>
|
||||
<div className="track-label">
|
||||
<div>Volume</div>
|
||||
<div>{chatVolumeLeft}dB</div>
|
||||
</div>
|
||||
<SessionTrackGain mixers={this.props.chatGroupMixers} />
|
||||
<div className={chatMuteClasses} data-control="mute" data-mixer-id={chatMuteMixerId} onClick={this.handleMute}/>
|
||||
|
||||
<input type="checkbox" name="mute"/>
|
||||
<label for="mute">Mute</label>
|
||||
</div>
|
||||
|
||||
<div className="textual-help">
|
||||
<p>Use this slider to control the volume of all the voice chat in the session in your headphones or speakers.</p>
|
||||
<p>This will not affect the volume for other musicians in the session.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>`
|
||||
|
||||
componentDidMount: () ->
|
||||
$root = jQuery(this.getDOMNode())
|
||||
|
||||
# initialize icheck
|
||||
$checkbox = $root.find('input')
|
||||
context.JK.checkbox($checkbox)
|
||||
$checkbox.on('ifChanged', this.handleMuteCheckbox);
|
||||
|
||||
#if this.props.mixers.muteMixer.mute
|
||||
# $checkbox.iCheck('check').attr('checked', true)
|
||||
#else
|
||||
# $checkbox.iCheck('uncheck').attr('checked', false)
|
||||
|
||||
componentWillUpdate: (nextProps, nextState) ->
|
||||
$root = jQuery(this.getDOMNode())
|
||||
|
||||
# re-initialize icheck
|
||||
$checkbox = $root.find('input')
|
||||
|
||||
#if nextState.mixers.muteMixer?.mute
|
||||
# $checkbox.iCheck('check').attr('checked', true)
|
||||
#else
|
||||
# $checkbox.iCheck('uncheck').attr('checked', false)
|
||||
})
|
||||
|
|
@ -2,9 +2,19 @@ context = window
|
|||
|
||||
@SessionSettingsBtn = React.createClass({
|
||||
|
||||
mixins: [Reflux.listenTo(@AppStore,"onAppInit")]
|
||||
|
||||
openSettings: (e) ->
|
||||
e.preventDefault()
|
||||
|
||||
@app.layout.showDialog('session-settings')
|
||||
|
||||
render: () ->
|
||||
`<a className="session-settings button-grey left">
|
||||
`<a className="session-settings button-grey left" onClick={this.openSettings}>
|
||||
<img src="/assets/content/icon_settings_sm.png" align="texttop" height="12" width="12"/>
|
||||
SETTINGS
|
||||
</a>`
|
||||
|
||||
onAppInit: (app) ->
|
||||
@app = app
|
||||
})
|
||||
|
|
@ -2,9 +2,19 @@ context = window
|
|||
|
||||
@SessionShareBtn = React.createClass({
|
||||
|
||||
mixins: [Reflux.listenTo(@AppStore,"onAppInit")]
|
||||
|
||||
onShare: (e) ->
|
||||
e.preventDefault()
|
||||
|
||||
@app.layout.showDialog('share-dialog')
|
||||
|
||||
render: () ->
|
||||
`<a className="session-share button-grey left">
|
||||
`<a className="session-share button-grey left" onClick={this.onShare}>
|
||||
<img src="/assets/content/icon_share.png" align="texttop" height="12" width="12"/>
|
||||
SHARE
|
||||
</a>`
|
||||
|
||||
onAppInit: (app) ->
|
||||
@app = app
|
||||
})
|
||||
|
|
@ -1,9 +1,15 @@
|
|||
context = window
|
||||
SessionActions = @SessionActions
|
||||
|
||||
@SessionVideoBtn = React.createClass({
|
||||
|
||||
sessionWebCam: (e) ->
|
||||
e.preventDefault();
|
||||
|
||||
SessionActions.toggleSessionVideo()
|
||||
|
||||
render: () ->
|
||||
`<a className="session-share button-grey-toggle left">
|
||||
`<a className="session-share button-grey-toggle left" onClick={this.sessionWebCam}>
|
||||
<img src="/assets/content/icon_cam.png" align="texttop" height="12" width="12"/>
|
||||
VIDEO
|
||||
</a>`
|
||||
|
|
|
|||
|
|
@ -2,9 +2,26 @@ context = window
|
|||
|
||||
@SessionVolumeSettingsBtn = React.createClass({
|
||||
|
||||
mixins: [Reflux.listenTo(@MixerStore,"onInputsChanged")]
|
||||
|
||||
onInputsChanged: (sessionMixers) ->
|
||||
this.setState(sessionMixers)
|
||||
|
||||
render: () ->
|
||||
`<a className="session-volume-settings button-grey left">
|
||||
<img src="/assets/content/icon_settings_sm.png" align="texttop" height="12" width="12"/>
|
||||
VOLUME
|
||||
</a>`
|
||||
|
||||
componentDidMount: () ->
|
||||
$root = $(this.getDOMNode())
|
||||
|
||||
context.JK.interactReactBubble(
|
||||
$root,
|
||||
'SessionSelfVolumeHover',
|
||||
() =>
|
||||
{inputGroupMixers: this.state.mixers.getAudioInputChatGroupMixer(), chatGroupMixers: this.state.mixers.getChatGroupMixer()}
|
||||
,
|
||||
{width:470, positions:['right', 'bottom', 'left'], offsetParent:$root.closest('.screen')})
|
||||
|
||||
})
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
context = window
|
||||
|
||||
@RecordingActions = Reflux.createActions({
|
||||
initModel: {}
|
||||
startRecording: {}
|
||||
stopRecording: {}
|
||||
startingRecording:{}
|
||||
startedRecording: {}
|
||||
stoppingRecording: {}
|
||||
stoppedRecording: {}
|
||||
abortedRecording: {}
|
||||
|
||||
})
|
||||
|
|
@ -5,4 +5,7 @@ context = window
|
|||
leaveSession: {}
|
||||
mixersChanged: {}
|
||||
allowLeaveSession: {}
|
||||
syncWithServer: {}
|
||||
toggleSessionVideo : {}
|
||||
audioResync: {}
|
||||
})
|
||||
|
|
@ -461,4 +461,28 @@ MIX_MODES = context.JK.MIX_MODES;
|
|||
context.JK.VuHelpers.updateVU2('vur', mixer, value)
|
||||
|
||||
getTrackInfo: () ->
|
||||
context.JK.TrackHelpers.getTrackInfo(context.jamClient, @masterMixers)
|
||||
context.JK.TrackHelpers.getTrackInfo(context.jamClient, @masterMixers)
|
||||
|
||||
getGroupMixer: (groupId, mode) ->
|
||||
mixers = @mixersForGroupId(groupId, MIX_MODES.PERSONAL)
|
||||
|
||||
if mixers.length == 0
|
||||
logger.warn("could not find mixer with group ID: " + groupId + ', mode:' + mode)
|
||||
return {}
|
||||
else
|
||||
mixer = mixers[0]
|
||||
{
|
||||
mixer: mixer,
|
||||
muteMixer : mixer,
|
||||
vuMixer: mixer,
|
||||
oppositeMixer: mixer
|
||||
}
|
||||
|
||||
console.log("M MIXERS", @masterMixers)
|
||||
console.log("P MIXERS", @personalMixers)
|
||||
|
||||
getAudioInputChatGroupMixer: () ->
|
||||
@getGroupMixer(ChannelGroupIds.AudioInputMusicGroup, MIX_MODES.PERSONAL)
|
||||
|
||||
getChatGroupMixer: () ->
|
||||
@getGroupMixer(ChannelGroupIds.AudioInputChatGroup, MIX_MODES.PERSONAL)
|
||||
|
|
@ -2,9 +2,10 @@ context = window
|
|||
|
||||
@SessionHelper = class SessionHelper
|
||||
|
||||
constructor: (app, session) ->
|
||||
constructor: (app, session, isRecording) ->
|
||||
@app = app
|
||||
@session = session
|
||||
@isRecording = isRecording
|
||||
|
||||
inSession: () ->
|
||||
@session?
|
||||
|
|
@ -87,4 +88,4 @@ context = window
|
|||
found
|
||||
|
||||
id: () ->
|
||||
@session.id
|
||||
@session.id
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
$ = jQuery
|
||||
context = window
|
||||
logger = context.JK.logger
|
||||
|
||||
@RecordingStore = Reflux.createStore(
|
||||
{
|
||||
listenables: @RecordingActions
|
||||
|
||||
init: ->
|
||||
# Register with the app store to get @app
|
||||
this.listenTo(context.AppStore, this.onAppInit)
|
||||
|
||||
onAppInit: (app) ->
|
||||
@app = app
|
||||
|
||||
onInitModel: (recordingModel) ->
|
||||
@recordingModel = recordingModel
|
||||
this.trigger({isRecording: @recordingModel.isRecording()})
|
||||
|
||||
onStartRecording: () ->
|
||||
@recordingModel.startRecording()
|
||||
|
||||
onStopRecording: () ->
|
||||
@recordingModel.stopRecording()
|
||||
|
||||
onStartingRecording: (details) ->
|
||||
details.cause = 'starting'
|
||||
this.trigger(details)
|
||||
|
||||
onStartedRecording: (details) ->
|
||||
details.cause = 'started'
|
||||
this.trigger(details)
|
||||
|
||||
onStoppingRecording: (details) ->
|
||||
details.cause = 'stopping'
|
||||
this.trigger(details)
|
||||
|
||||
onStoppedRecording: (details) ->
|
||||
details.cause = 'stopped'
|
||||
this.trigger(details)
|
||||
|
||||
onAbortedRecording: (details) ->
|
||||
details.cause = 'aborted'
|
||||
this.trigger(details)
|
||||
}
|
||||
)
|
||||
|
|
@ -7,6 +7,7 @@ MIX_MODES = context.JK.MIX_MODES
|
|||
|
||||
|
||||
SessionActions = @SessionActions
|
||||
RecordingActions = @RecordingActions
|
||||
|
||||
@SessionStore = Reflux.createStore(
|
||||
{
|
||||
|
|
@ -26,19 +27,43 @@ SessionActions = @SessionActions
|
|||
sessionPageEnterDeferred: null
|
||||
gearUtils: null
|
||||
sessionUtils: null
|
||||
joinDeffered: null
|
||||
joinDeferred: null
|
||||
recordingModel: null
|
||||
currentTrackChanges: 0
|
||||
isRecording: false
|
||||
previousAllTracks: {userTracks: [], backingTracks: [], metronomeTracks: []}
|
||||
webcamViewer: null
|
||||
|
||||
init: ->
|
||||
# Register with the app store to get @app
|
||||
this.listenTo(context.AppStore, this.onAppInit)
|
||||
this.listenTo(context.RecordingStore, this.onRecordingChanged)
|
||||
|
||||
if gon.global.video_available && gon.global.video_available!="none"
|
||||
@webcamViewer.init()
|
||||
@webcamViewer.setVideoOff()
|
||||
|
||||
onAppInit: (@app) ->
|
||||
@gearUtils = context.JK.GearUtilsInstance
|
||||
@sessionUtils = context.JK.SessionUtils
|
||||
@recordingModel = new context.JK.RecordingModel(@app, this, rest, context.jamClient);
|
||||
@recordingModel = new context.JK.RecordingModel(@app, rest, context.jamClient);
|
||||
RecordingActions.initModel(@recordingModel)
|
||||
|
||||
onToggleSessionVideo: () ->
|
||||
logger.debug("toggle session video")
|
||||
@webcamViewer.toggleWebcam() if @webcamViewer?
|
||||
|
||||
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"})
|
||||
|
||||
onSyncWithServer: () ->
|
||||
@refreshCurrentSession(true)
|
||||
|
||||
onWatchedInputs: (inputTracks) ->
|
||||
|
||||
|
|
@ -103,8 +128,176 @@ SessionActions = @SessionActions
|
|||
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)
|
||||
|
||||
this.trigger(new context.SessionHelper(@app, @currentSession, @isRecording))
|
||||
|
||||
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();
|
||||
|
||||
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-screen2').parent()})
|
||||
)
|
||||
)
|
||||
.fail(@app.ajaxError)
|
||||
|
||||
onJoinSession: (sessionId) ->
|
||||
|
||||
# poke ShareDialog
|
||||
shareDialog = new JK.ShareDialog(@app, sessionId, "session");
|
||||
shareDialog.initialize(context.JK.FacebookHelperInstance);
|
||||
|
||||
# initialize webcamViewer
|
||||
if gon.global.video_available && gon.global.video_available != "none"
|
||||
@webcamViewer.beforeShow()
|
||||
|
|
@ -215,7 +408,7 @@ SessionActions = @SessionActions
|
|||
|
||||
joinSession: () ->
|
||||
context.jamClient.SessionRegisterCallback("JK.HandleBridgeCallback2");
|
||||
#context.jamClient.RegisterRecordingCallbacks("JK.HandleRecordingStartResult", "JK.HandleRecordingStopResult", "JK.HandleRecordingStarted", "JK.HandleRecordingStopped", "JK.HandleRecordingAborted");
|
||||
context.jamClient.RegisterRecordingCallbacks("JK.HandleRecordingStartResult", "JK.HandleRecordingStopResult", "JK.HandleRecordingStarted", "JK.HandleRecordingStopped", "JK.HandleRecordingAborted");
|
||||
context.jamClient.SessionSetConnectionStatusRefreshRate(1000);
|
||||
#context.JK.HelpBubbleHelper.jamtrackGuideSession($screen.find('li.open-a-jamtrack'), $screen)
|
||||
|
||||
|
|
@ -233,10 +426,11 @@ SessionActions = @SessionActions
|
|||
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(response.id)
|
||||
@leaveSessionRest(@currentSessionId)
|
||||
return
|
||||
|
||||
logger.debug("calling jamClient.JoinSession");
|
||||
|
|
@ -248,9 +442,9 @@ SessionActions = @SessionActions
|
|||
else
|
||||
context.JK.GA.trackSessionMusicians(context.JK.GA.SessionCreationTypes.join);
|
||||
|
||||
@recordingModel.reset(response.id);
|
||||
@recordingModel.reset(@currentSessionId);
|
||||
|
||||
context.jamClient.JoinSession({sessionID: response.id});
|
||||
context.jamClient.JoinSession({sessionID: @currentSessionId});
|
||||
|
||||
@refreshCurrentSession(true);
|
||||
|
||||
|
|
@ -474,7 +668,7 @@ SessionActions = @SessionActions
|
|||
|
||||
console.log("SESSION CHANGED", sessionData)
|
||||
|
||||
this.trigger(new context.SessionHelper(@app, @currentSession))
|
||||
this.trigger(new context.SessionHelper(@app, @currentSession, @isRecording))
|
||||
|
||||
ensureConnected: () ->
|
||||
unless context.JK.JamServer.connected
|
||||
|
|
@ -546,7 +740,10 @@ SessionActions = @SessionActions
|
|||
|
||||
@sessionEnded()
|
||||
|
||||
this.trigger(new context.SessionHelper(@app, @currentSession))
|
||||
this.trigger(new context.SessionHelper(@app, @currentSession, @isRecording))
|
||||
|
||||
selfOpenedJamTracks: () ->
|
||||
@currentSession && (@currentSession.jam_track_initiator_id == context.JK.currentUserId)
|
||||
|
||||
sessionEnded: () ->
|
||||
# cleanup
|
||||
|
|
@ -567,12 +764,13 @@ SessionActions = @SessionActions
|
|||
@userTracks = null;
|
||||
@startTime = null;
|
||||
|
||||
if @joinDeffered?.state() == 'resolved'
|
||||
if @joinDeferred?.state() == 'resolved'
|
||||
$(document).trigger(EVENTS.SESSION_ENDED, {session: {id: @currentSessionId}})
|
||||
|
||||
@currentTrackChanges = 0
|
||||
@currentSession = null
|
||||
@joinDeferred = null
|
||||
@isRecording = false
|
||||
@currentSessionId = null
|
||||
@currentParticipants = {}
|
||||
@previousAllTracks = {userTracks: [], backingTracks: [], metronomeTracks: []}
|
||||
|
|
|
|||
|
|
@ -61,6 +61,7 @@
|
|||
}
|
||||
|
||||
|
||||
|
||||
function groupTracksToClient(recording) {
|
||||
// group N tracks to the same client Id
|
||||
var groupedTracks = {};
|
||||
|
|
@ -85,6 +86,8 @@
|
|||
currentlyRecording = true;
|
||||
stoppingRecording = false;
|
||||
|
||||
context.RecordingActions.startingRecording({isRecording: false})
|
||||
|
||||
currentRecording = rest.startRecording({"music_session_id": sessionId})
|
||||
.done(function(recording) {
|
||||
currentRecordingId = recording.id;
|
||||
|
|
@ -95,8 +98,10 @@
|
|||
jamClient.StartRecording(recording["id"], groupedTracks);
|
||||
})
|
||||
.fail(function(jqXHR) {
|
||||
$self.triggerHandler('startedRecording', { clientId: app.clientId, reason: 'rest', detail: arguments });
|
||||
var details = { clientId: app.clientId, reason: 'rest', detail: arguments, isRecording: false }
|
||||
$self.triggerHandler('startedRecording', details);
|
||||
currentlyRecording = false;
|
||||
context.RecordingActions.startedRecording(details);
|
||||
})
|
||||
|
||||
|
||||
|
|
@ -117,6 +122,7 @@
|
|||
waitingOnStopTimer = setTimeout(timeoutTransitionToStop, 5000);
|
||||
|
||||
$self.triggerHandler('stoppingRecording', {reason: reason, detail: detail});
|
||||
context.RecordingActions.stoppingRecording({reason: reason, detail: detail, isRecording:true})
|
||||
|
||||
// this path assumes that the currentRecording info has, or can be, retrieved
|
||||
// failure for currentRecording is handled elsewhere
|
||||
|
|
@ -146,7 +152,9 @@
|
|||
else {
|
||||
logger.error("unable to stop recording %o", arguments);
|
||||
transitionToStopped();
|
||||
$self.triggerHandler('stoppedRecording', {'recordingId': recording.id, 'reason' : 'rest', 'details' : arguments});
|
||||
var details = {'recordingId': recording.id, 'reason' : 'rest', 'details' : arguments, isRecording: false}
|
||||
$self.triggerHandler('stoppedRecording', details);
|
||||
context.RecordingActions.stoppedRecording(details)
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
@ -169,7 +177,9 @@
|
|||
|
||||
if(!waitingOnClientStop && !waitingOnServerStop) {
|
||||
transitionToStopped();
|
||||
$self.triggerHandler('stoppedRecording', {recordingId: recordingId, reason: errorReason, detail: errorDetail});
|
||||
var details = {recordingId: recordingId, reason: errorReason, detail: errorDetail, isRecording: false}
|
||||
$self.triggerHandler('stoppedRecording', details)
|
||||
context.RecordingActions.stoppedRecording(details)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -199,12 +209,16 @@
|
|||
|
||||
|
||||
if(success) {
|
||||
$self.triggerHandler('startedRecording', {clientId: app.clientId})
|
||||
var details = {clientId: app.clientId, isRecording:true}
|
||||
$self.triggerHandler('startedRecording', details)
|
||||
context.RecordingActions.startedRecording(details)
|
||||
}
|
||||
else {
|
||||
currentlyRecording = false;
|
||||
logger.error("unable to start the recording %o, %o", reason, detail);
|
||||
$self.triggerHandler('startedRecording', { clientId: app.clientId, reason: reason, detail: detail});
|
||||
var details = { clientId: app.clientId, reason: reason, detail: detail, isRecording: false}
|
||||
$self.triggerHandler('startedRecording', details);
|
||||
context.RecordingActions.startedRecording(details)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -222,7 +236,9 @@
|
|||
else {
|
||||
transitionToStopped();
|
||||
logger.error("backend unable to stop the recording %o, %o", reason, detail);
|
||||
$self.triggerHandler('stoppedRecording', {recordingId: recordingId, reason: reason, detail : detail});
|
||||
var details = {recordingId: recordingId, reason: reason, detail : detail, isRecording: false}
|
||||
$self.triggerHandler('stoppedRecording', details);
|
||||
context.RecordingActions.stoppedRecording(details)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -243,9 +259,14 @@
|
|||
currentOrLastRecordingId = recording.id;
|
||||
});
|
||||
|
||||
$self.triggerHandler('startingRecording', {recordingId: recordingId});
|
||||
var details = {recordingId: recordingId, isRecording: false}
|
||||
$self.triggerHandler('startingRecording', details);
|
||||
this.RecordingActions.startingRecording(details)
|
||||
currentlyRecording = true;
|
||||
$self.triggerHandler('startedRecording', {clientId: clientId, recordingId: recordingId});
|
||||
|
||||
details = {clientId: clientId, recordingId: recordingId, isRecording: true}
|
||||
$self.triggerHandler('startedRecording', details);
|
||||
this.RecordingActions.startedRecording(details)
|
||||
}
|
||||
|
||||
function handleRecordingStopped(recordingId, result) {
|
||||
|
|
@ -254,7 +275,10 @@
|
|||
var detail = result.detail;
|
||||
|
||||
|
||||
$self.triggerHandler('stoppingRecording', {recordingId: recordingId, reason: reason, detail: detail });
|
||||
var details = {recordingId: recordingId, reason: reason, detail: detail, isRecording: true }
|
||||
$self.triggerHandler('stoppingRecording', details);
|
||||
context.RecordingActions.stoppingRecording(details)
|
||||
|
||||
// the backend says the recording must be stopped.
|
||||
// tell the server to stop it too
|
||||
rest.stopRecording({
|
||||
|
|
@ -266,18 +290,26 @@
|
|||
.fail(function(jqXHR, textStatus, errorMessage) {
|
||||
if(jqXHR.status == 422) {
|
||||
logger.debug("recording already stopped %o", arguments);
|
||||
$self.triggerHandler('stoppedRecording', {recordingId: recordingId, reason: reason, detail: detail});
|
||||
var details = {recordingId: recordingId, reason: reason, detail: detail, isRecording: false}
|
||||
$self.triggerHandler('stoppedRecording', details);
|
||||
context.RecordingActions.stoppedRecording(details)
|
||||
}
|
||||
else if(jqXHR.status == 404) {
|
||||
logger.debug("recording is already deleted %o", arguments);
|
||||
$self.triggerHandler('stoppedRecording', {recordingId: recordingId, reason: reason, detail: detail});
|
||||
var details = {recordingId: recordingId, reason: reason, detail: detail, isRecording: false}
|
||||
$self.triggerHandler('stoppedRecording', details);
|
||||
context.RecordingActions.stoppedRecording(details)
|
||||
}
|
||||
else {
|
||||
$self.triggerHandler('stoppedRecording', {recordingId: recordingId, reason: textStatus, detail: errorMessage});
|
||||
var details = {recordingId: recordingId, reason: textStatus, detail: errorMessage, isRecording: false}
|
||||
$self.triggerHandler('stoppedRecording', details);
|
||||
context.RecordingActions.stoppedRecording(details)
|
||||
}
|
||||
})
|
||||
.done(function() {
|
||||
$self.triggerHandler('stoppedRecording', {recordingId: recordingId, reason: reason, detail: detail});
|
||||
var details = {recordingId: recordingId, reason: reason, detail: detail, isRecording: false}
|
||||
$self.triggerHandler('stoppedRecording', details);
|
||||
context.RecordingActions.stoppedRecording(details)
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -288,7 +320,9 @@
|
|||
|
||||
stoppingRecording = false;
|
||||
|
||||
$self.triggerHandler('abortedRecording', {recordingId: recordingId, reason: reason, detail: detail });
|
||||
var details = {recordingId: recordingId, reason: reason, detail: detail, isRecording: false }
|
||||
$self.triggerHandler('abortedRecording', details);
|
||||
context.RecordingActions.abortedRecording(details)
|
||||
// the backend says the recording must be stopped.
|
||||
// tell the server to stop it too
|
||||
rest.stopRecording({
|
||||
|
|
|
|||
|
|
@ -14,6 +14,10 @@ context.JK.WebcamViewer = class WebcamViewer
|
|||
@resolution=null
|
||||
|
||||
init: (root) =>
|
||||
|
||||
# the session usage of webcamViewer does not actually pass in anything
|
||||
root = $() unless root?
|
||||
|
||||
@root = root
|
||||
@toggleBtn = @root.find(".webcam-test-btn")
|
||||
@webcamSelect = @root.find(".webcam-select-container select")
|
||||
|
|
|
|||
|
|
@ -217,7 +217,7 @@
|
|||
|
||||
.content-wrapper, .dialog, .dialog-inner, .ftue-inner {
|
||||
|
||||
select, textarea, input[type=text], input[type=password], div.friendbox {
|
||||
select, textarea, input[type=text], input[type=password], div.friendbox, div.inputbox {
|
||||
background-color:#c5c5c5;
|
||||
border:none;
|
||||
-webkit-box-shadow: inset 2px 2px 3px 0px #888;
|
||||
|
|
|
|||
|
|
@ -106,7 +106,7 @@
|
|||
}
|
||||
|
||||
.react-holder {
|
||||
&.SessionTrackVolumeHover {
|
||||
&.SessionTrackVolumeHover, &.SessionSelfVolumeHover {
|
||||
height:331px;
|
||||
width:235px;
|
||||
|
||||
|
|
@ -182,6 +182,72 @@
|
|||
left:34px
|
||||
}
|
||||
}
|
||||
|
||||
#self-volume-hover {
|
||||
h3 {
|
||||
font-size:16px;
|
||||
font-weight:bold;
|
||||
margin-bottom:10px;
|
||||
}
|
||||
|
||||
.monitor-mixer {
|
||||
float:left;
|
||||
width:235px;
|
||||
@include border_box_sizing;
|
||||
padding: 15px 0 15px 0;
|
||||
|
||||
h3 {
|
||||
margin-left:36px;
|
||||
}
|
||||
|
||||
.textual-help {
|
||||
border-right:1px solid $ColorTextTypical;
|
||||
float:right;
|
||||
padding-right:25px !important;
|
||||
|
||||
p:nth-child(1) {
|
||||
margin-top:0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.chat-mixer {
|
||||
float:left;
|
||||
width:235px;
|
||||
@include border-box-sizing;
|
||||
padding: 15px 0 15px 0;
|
||||
|
||||
h3 {
|
||||
margin-left:41px;
|
||||
}
|
||||
}
|
||||
|
||||
.mixer-holder {
|
||||
|
||||
.session-track {
|
||||
margin-top:0;
|
||||
}
|
||||
|
||||
.textual-help {
|
||||
margin-top:0;
|
||||
padding-right:10px;
|
||||
|
||||
p:nth-child(1) {
|
||||
margin-top:0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
&.SessionTrackVolumeHover {
|
||||
|
||||
}
|
||||
|
||||
&.SessionSelfVolumeHover {
|
||||
width:470px ! important;
|
||||
height:360px ! important;
|
||||
}
|
||||
|
||||
&.SessionTrackPanHover {
|
||||
width:331px;
|
||||
height:197px;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,2 @@
|
|||
@import "client/common";
|
||||
|
||||
|
|
@ -0,0 +1,67 @@
|
|||
@import "client/common";
|
||||
|
||||
#session-settings {
|
||||
|
||||
width:500px;
|
||||
|
||||
.dropdown-wrapper {
|
||||
width:100%;
|
||||
}
|
||||
|
||||
input, textarea {
|
||||
width:100%;
|
||||
@include border_box_sizing;
|
||||
}
|
||||
|
||||
.btn-select-files {
|
||||
position:absolute;
|
||||
width: 90px;
|
||||
top: 2px;
|
||||
}
|
||||
|
||||
.notation-selector {
|
||||
position:absolute;
|
||||
right:0
|
||||
}
|
||||
|
||||
.notation-files {
|
||||
position:relative;
|
||||
}
|
||||
|
||||
.inputbox {
|
||||
height:60px;
|
||||
padding:5px;
|
||||
}
|
||||
|
||||
.notation-file {
|
||||
|
||||
}
|
||||
|
||||
.input-holder {
|
||||
width:350px;
|
||||
}
|
||||
|
||||
.notation-entry {
|
||||
div {
|
||||
display:block;
|
||||
width:90%;
|
||||
overflow:hidden;
|
||||
white-space: nowrap;
|
||||
float: left;
|
||||
color:black;
|
||||
}
|
||||
a {
|
||||
float:right;
|
||||
color:black;
|
||||
}
|
||||
}
|
||||
|
||||
#session-settings-dialog-submit {
|
||||
margin-right:1px;
|
||||
}
|
||||
|
||||
.spinner-small {
|
||||
position: absolute;
|
||||
top: 20px;
|
||||
}
|
||||
}
|
||||
|
|
@ -2,6 +2,10 @@
|
|||
|
||||
width:500px;
|
||||
|
||||
.dialog-inner {
|
||||
padding-bottom:6px;
|
||||
}
|
||||
|
||||
.button-orange {
|
||||
margin:0 2px 0 0;
|
||||
}
|
||||
|
|
@ -272,6 +276,9 @@
|
|||
}
|
||||
|
||||
.share-link {
|
||||
|
||||
height:75px;
|
||||
|
||||
h3 {
|
||||
margin-bottom:20px;
|
||||
}
|
||||
|
|
@ -292,4 +299,9 @@
|
|||
text-align: center;
|
||||
margin: 125px auto;
|
||||
}
|
||||
|
||||
.actions {
|
||||
text-align:center;
|
||||
margin-top:20px;
|
||||
}
|
||||
}
|
||||
|
|
@ -5,6 +5,8 @@
|
|||
*= require client/screen_common
|
||||
*= require client/content
|
||||
*= require client/ftue
|
||||
*= require icheck/minimal/minimal
|
||||
*= require minimal/popup
|
||||
*= require minimal/recording_controls
|
||||
*= require minimal/minimal_main
|
||||
*/
|
||||
|
|
@ -8,9 +8,4 @@ body {
|
|||
overflow: visible !important;
|
||||
height:100%;
|
||||
margin:0 !important;
|
||||
}
|
||||
|
||||
.wrapper {
|
||||
width:1280px;
|
||||
margin:0 auto;
|
||||
}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
body.popup {
|
||||
width:100%;
|
||||
height:100%;
|
||||
background-color:#404040;
|
||||
overflow: hidden !important;
|
||||
}
|
||||
|
|
@ -1,6 +1,95 @@
|
|||
body.recording-controls {
|
||||
width:100%;
|
||||
height:100%;
|
||||
background-color:#404040;
|
||||
overflow: hidden !important;
|
||||
@import "client/common";
|
||||
|
||||
body.recording-start-stop {
|
||||
|
||||
position:relative;
|
||||
color: $ColorTextTypical;
|
||||
|
||||
#minimal-container {
|
||||
padding-bottom:20px;
|
||||
}
|
||||
|
||||
.recording-start-stop {
|
||||
padding-left:44px;
|
||||
}
|
||||
|
||||
.control-holder {
|
||||
width:100%;
|
||||
margin: 1em 0;
|
||||
}
|
||||
|
||||
.helper {
|
||||
display: inline-block;
|
||||
height: 100%;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.control {
|
||||
width:231px;
|
||||
height:34px;
|
||||
@include border_box_sizing;
|
||||
margin-top:15px;
|
||||
padding:3px;
|
||||
background-color:#242323;
|
||||
text-align:center;
|
||||
font-size:13px;
|
||||
border-radius:5px;
|
||||
vertical-align:middle;
|
||||
color:#ccc;
|
||||
}
|
||||
|
||||
|
||||
.control img {
|
||||
vertical-align:middle;
|
||||
margin-right:5px;
|
||||
}
|
||||
|
||||
.control span {
|
||||
vertical-align:middle;
|
||||
}
|
||||
|
||||
.iradio_minimal {
|
||||
float:left;
|
||||
margin-right:5px;
|
||||
}
|
||||
|
||||
label {
|
||||
padding-top:2px;
|
||||
}
|
||||
|
||||
.field {
|
||||
height:18px;
|
||||
&:nth-child(1) {
|
||||
|
||||
}
|
||||
&:nth-child(2) {
|
||||
margin-top:9px;
|
||||
}
|
||||
}
|
||||
|
||||
.note-show-hide {
|
||||
font-size:11px;
|
||||
}
|
||||
|
||||
h5 {
|
||||
text-decoration:underline;
|
||||
margin-bottom:5px;
|
||||
}
|
||||
|
||||
.important-note {
|
||||
margin-top:30px;
|
||||
line-height:150%;
|
||||
font-size:12px;
|
||||
width:260px;
|
||||
}
|
||||
|
||||
a.note-show-hide {
|
||||
margin-top:5px;
|
||||
text-decoration:underline;
|
||||
font-size:11px;
|
||||
}
|
||||
|
||||
.currently-recording {
|
||||
background-color: $ColorRecordingBackground;
|
||||
}
|
||||
}
|
||||
|
|
@ -25,14 +25,28 @@ class ApiMusicNotationsController < ApiController
|
|||
def download
|
||||
@music_notation = MusicNotation.find(params[:id])
|
||||
|
||||
unless @music_notation.music_session.nil? || @music_notation.music_session.can_join?(current_user, true)
|
||||
unless @music_notation.music_session.can_join?(current_user, true)
|
||||
render :text => "Permission denied", status:403
|
||||
return
|
||||
end
|
||||
|
||||
if '_blank'==params[:target]
|
||||
redirect_to @music_notation.sign_url
|
||||
else
|
||||
render :text => @music_notation.sign_url
|
||||
end
|
||||
end
|
||||
|
||||
def delete
|
||||
@music_notation = MusicNotation.find(params[:id])
|
||||
|
||||
unless @music_notation.music_session.can_join?(current_user, true)
|
||||
render :text => "Permission denied", status:403
|
||||
return
|
||||
end
|
||||
|
||||
@music_notation.destroy
|
||||
|
||||
render :json => {}, status: 204
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,3 +1,7 @@
|
|||
object @music_notations
|
||||
|
||||
attribute :id, :file_name
|
||||
attribute :id, :file_name
|
||||
|
||||
node do |music_notation|
|
||||
{ file_url: "/api/music_notations/#{music_notation.id}" }
|
||||
end
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
<!-- Session Settings Dialog -->
|
||||
.dialog{:layout => 'dialog', 'layout-id' => 'session-settings', :id => 'session-settings', :width => '600px', :height => '800px'}
|
||||
.dialog{:layout => 'dialog', 'layout-id' => 'session-settings', :id => 'session-settings'}
|
||||
|
||||
.content-head
|
||||
= image_tag "content/icon_settings_lg.png", :width => 18, :height => 18, :class => "content-icon"
|
||||
|
|
@ -10,7 +10,7 @@
|
|||
|
||||
%input{:type => "hidden", :name => "id", :id => "session-settings-id"}
|
||||
|
||||
.left.mr35
|
||||
.left.input-holder
|
||||
|
||||
.left.ib
|
||||
Genre:
|
||||
|
|
@ -53,21 +53,19 @@
|
|||
|
||||
.clearall.left.w25.ib.mb10
|
||||
Notation Files:
|
||||
.right.w75.ib.mb10
|
||||
.w40.left
|
||||
.selected-files-section
|
||||
%div{:id => "settings-selected-filenames"}
|
||||
.right.ib.mb10
|
||||
%a.button-orange.btn-select-files SELECT FILES...
|
||||
.right.w75.ib.mb10.notation-files
|
||||
.notation-selector
|
||||
%a.button-orange.btn-select-files ADD FILES...
|
||||
%input.hidden{:type => "file", :id => "session-select-files", :value => "Select Files...", :accept => ".pdf, .png, .jpg, .jpeg, .gif, .xml, .mxl, .txt"}
|
||||
.spinner-small.upload-spinner
|
||||
|
||||
.clearall.right.mt10
|
||||
%a.button-orange{:href => 'https://jamkazam.desk.com', :rel => 'external'} HELP
|
||||
.spinner-small.hidden
|
||||
|
||||
.inputbox
|
||||
|
||||
|
||||
.clearall.right.mt20
|
||||
%a.button-grey{'layout-action' => "close"} CANCEL
|
||||
%a.button-orange{:id => "session-settings-dialog-submit"} UPDATE SETTINGS
|
||||
|
||||
.clearall
|
||||
|
||||
%br/
|
||||
%br{:clear => 'all'}/
|
||||
|
|
@ -153,6 +153,7 @@
|
|||
|
||||
var recordingFinishedDialog = new JK.RecordingFinishedDialog(JK.app);
|
||||
recordingFinishedDialog.initialize();
|
||||
JK.recordingFinishedDialog = recordingFinishedDialog
|
||||
|
||||
var localRecordingsDialog = new JK.LocalRecordingsDialog(JK.app);
|
||||
localRecordingsDialog.initialize();
|
||||
|
|
|
|||
|
|
@ -3,9 +3,6 @@
|
|||
<div class="content-head"><h1>share this <span id="shareType"></span></h1></div>
|
||||
<div class="dialog-inner">
|
||||
|
||||
<div class="right"><a class="button-orange" layout-action="close">X CLOSE</a></div>
|
||||
<br clear="both" />
|
||||
|
||||
<div class="share-to-social-media border-bottom">
|
||||
|
||||
<h3 class="mb5">Share to Social Media:</h3>
|
||||
|
|
@ -39,7 +36,7 @@
|
|||
<br clear="both" />
|
||||
</div>
|
||||
|
||||
<div class="share-link">
|
||||
<div class="share-link border-bottom">
|
||||
<h3>Share a Link:</h3>
|
||||
<div class="link-contents">
|
||||
</div>
|
||||
|
|
@ -47,6 +44,11 @@
|
|||
<div class="right"><a id="btn-share-copy" class="button-orange">COPY LINK</a></div>
|
||||
</div>
|
||||
|
||||
<div class="actions"><a class="button-orange" layout-action="close">X CLOSE</a></div>
|
||||
<br clear="both" />
|
||||
|
||||
|
||||
|
||||
<!-- contains syndication widget code -->
|
||||
<% #render 'shareDialogUnused' %>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,11 +1,2 @@
|
|||
- provide(:page_name, 'recording-controls')
|
||||
.recording-controls
|
||||
| IM HERE
|
||||
|
||||
javascript:
|
||||
window.bling = function () {
|
||||
alert("bling!!")
|
||||
window.opener.callMe()
|
||||
}
|
||||
|
||||
console.log("window.opener", window.opener)
|
||||
- provide(:page_name, 'recording-start-stop popup')
|
||||
= react_component 'PopupRecordingStartStop', {}
|
||||
|
|
@ -136,7 +136,7 @@ SampleApp::Application.routes.draw do
|
|||
match '/extras/settings', to: 'extras#settings'
|
||||
|
||||
scope '/popups' do
|
||||
match '/session/:id/recording-controls', to: 'popups#recording_controls'
|
||||
match '/recording-controls', to: 'popups#recording_controls'
|
||||
end
|
||||
|
||||
scope '/corp' do
|
||||
|
|
@ -219,6 +219,8 @@ SampleApp::Application.routes.draw do
|
|||
# Music notations
|
||||
match '/music_notations' => 'api_music_notations#create', :via => :post
|
||||
match '/music_notations/:id' => 'api_music_notations#download', :via => :get, :as => :download_music_notation
|
||||
match '/music_notations/:id' => 'api_music_notations#delete', :via => :delete, :as => :delete_music_notation
|
||||
|
||||
|
||||
# Backing track_show
|
||||
match '/backing_tracks' => 'api_backing_tracks#index', :via => :get, :as => 'api_backing_tracks_list'
|
||||
|
|
|
|||
Loading…
Reference in New Issue