This commit is contained in:
Seth Call 2015-06-20 08:56:52 -05:00
parent 7a803bb682
commit 910b052204
40 changed files with 1090 additions and 134 deletions

View File

@ -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) {

View File

@ -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>&nbsp;');
}*/
$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');
}

View File

@ -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;

View File

@ -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

View File

@ -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

View File

@ -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)
})

View File

@ -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>

View File

@ -10,7 +10,6 @@ ReactCSSTransitionGroup = React.addons.CSSTransitionGroup;
session = sessionMixers.session
mixers = sessionMixers.mixers
logger.debug("SessionOtherTracks: onInputsChanged")
participants = []
if session.inSession()

View File

@ -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"/>

View File

@ -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
})

View File

@ -74,5 +74,4 @@ SessionActions = @SessionActions
};
@app.bindScreen('session2', screenBindings);
})

View File

@ -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)
})

View File

@ -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
})

View File

@ -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
})

View File

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

View File

@ -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')})
})

View File

@ -0,0 +1,13 @@
context = window
@RecordingActions = Reflux.createActions({
initModel: {}
startRecording: {}
stopRecording: {}
startingRecording:{}
startedRecording: {}
stoppingRecording: {}
stoppedRecording: {}
abortedRecording: {}
})

View File

@ -5,4 +5,7 @@ context = window
leaveSession: {}
mixersChanged: {}
allowLeaveSession: {}
syncWithServer: {}
toggleSessionVideo : {}
audioResync: {}
})

View File

@ -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)

View File

@ -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

View File

@ -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)
}
)

View File

@ -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: []}

View File

@ -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({

View File

@ -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")

View File

@ -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;

View File

@ -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;

View File

@ -0,0 +1,2 @@
@import "client/common";

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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
*/

View File

@ -8,9 +8,4 @@ body {
overflow: visible !important;
height:100%;
margin:0 !important;
}
.wrapper {
width:1280px;
margin:0 auto;
}

View File

@ -0,0 +1,6 @@
body.popup {
width:100%;
height:100%;
background-color:#404040;
overflow: hidden !important;
}

View File

@ -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;
}
}

View File

@ -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

View File

@ -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

View File

@ -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'}/

View File

@ -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();

View File

@ -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>

View File

@ -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', {}

View File

@ -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'