+
Music
+
+
+
+
+
+
+
+
+
Volume
+
{monitorVolumeLeft}dB
+
+
+
+
+
+ Mute
+
+
+
+
Use this slider to control the volume of all the music in the session in your headphones or speakers.
+
This will not affect the volume for other musicians in the session.
+
To adjust master levels for all musicians for recordings and broadcasts, use Mixer button in the toolbar.
+
+
+
+
+
Chat
+
+
+
+
+
+
+
+
+
Volume
+
{chatVolumeLeft}dB
+
+
+
+
+
+ Mute
+
+
+
+
Use this slider to control the volume of all the voice chat in the session in your headphones or speakers.
+
This will not affect the volume for other musicians in the session.
+
+
+
`
+
+ 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)
+})
\ No newline at end of file
diff --git a/web/app/assets/javascripts/react-components/SessionSettingsBtn.js.jsx.coffee b/web/app/assets/javascripts/react-components/SessionSettingsBtn.js.jsx.coffee
index 604a9194c..228d17f31 100644
--- a/web/app/assets/javascripts/react-components/SessionSettingsBtn.js.jsx.coffee
+++ b/web/app/assets/javascripts/react-components/SessionSettingsBtn.js.jsx.coffee
@@ -2,9 +2,19 @@ context = window
@SessionSettingsBtn = React.createClass({
+ mixins: [Reflux.listenTo(@AppStore,"onAppInit")]
+
+ openSettings: (e) ->
+ e.preventDefault()
+
+ @app.layout.showDialog('session-settings')
+
render: () ->
- `
+ `
SETTINGS
`
+
+ onAppInit: (app) ->
+ @app = app
})
\ No newline at end of file
diff --git a/web/app/assets/javascripts/react-components/SessionShareBtn.js.jsx.coffee b/web/app/assets/javascripts/react-components/SessionShareBtn.js.jsx.coffee
index 7572ec67b..aac4906a0 100644
--- a/web/app/assets/javascripts/react-components/SessionShareBtn.js.jsx.coffee
+++ b/web/app/assets/javascripts/react-components/SessionShareBtn.js.jsx.coffee
@@ -2,9 +2,19 @@ context = window
@SessionShareBtn = React.createClass({
+ mixins: [Reflux.listenTo(@AppStore,"onAppInit")]
+
+ onShare: (e) ->
+ e.preventDefault()
+
+ @app.layout.showDialog('share-dialog')
+
render: () ->
- `
+ `
SHARE
`
+
+ onAppInit: (app) ->
+ @app = app
})
\ No newline at end of file
diff --git a/web/app/assets/javascripts/react-components/SessionVideoBtn.js.jsx.coffee b/web/app/assets/javascripts/react-components/SessionVideoBtn.js.jsx.coffee
index d30b7c42f..8cfd1a344 100644
--- a/web/app/assets/javascripts/react-components/SessionVideoBtn.js.jsx.coffee
+++ b/web/app/assets/javascripts/react-components/SessionVideoBtn.js.jsx.coffee
@@ -1,9 +1,15 @@
context = window
+SessionActions = @SessionActions
@SessionVideoBtn = React.createClass({
+ sessionWebCam: (e) ->
+ e.preventDefault();
+
+ SessionActions.toggleSessionVideo()
+
render: () ->
- `
+ `
VIDEO
`
diff --git a/web/app/assets/javascripts/react-components/SessionVolumeSettingsBtn.js.jsx.coffee b/web/app/assets/javascripts/react-components/SessionVolumeSettingsBtn.js.jsx.coffee
index 7afe17866..0a9ee0b9b 100644
--- a/web/app/assets/javascripts/react-components/SessionVolumeSettingsBtn.js.jsx.coffee
+++ b/web/app/assets/javascripts/react-components/SessionVolumeSettingsBtn.js.jsx.coffee
@@ -2,9 +2,26 @@ context = window
@SessionVolumeSettingsBtn = React.createClass({
+ mixins: [Reflux.listenTo(@MixerStore,"onInputsChanged")]
+
+ onInputsChanged: (sessionMixers) ->
+ this.setState(sessionMixers)
+
render: () ->
`
VOLUME
`
+
+ 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')})
+
})
\ No newline at end of file
diff --git a/web/app/assets/javascripts/react-components/actions/RecordingActions.js.coffee b/web/app/assets/javascripts/react-components/actions/RecordingActions.js.coffee
new file mode 100644
index 000000000..546176110
--- /dev/null
+++ b/web/app/assets/javascripts/react-components/actions/RecordingActions.js.coffee
@@ -0,0 +1,13 @@
+context = window
+
+@RecordingActions = Reflux.createActions({
+ initModel: {}
+ startRecording: {}
+ stopRecording: {}
+ startingRecording:{}
+ startedRecording: {}
+ stoppingRecording: {}
+ stoppedRecording: {}
+ abortedRecording: {}
+
+})
\ No newline at end of file
diff --git a/web/app/assets/javascripts/react-components/actions/SessionActions.js.coffee b/web/app/assets/javascripts/react-components/actions/SessionActions.js.coffee
index 44ea905cf..50d071cf2 100644
--- a/web/app/assets/javascripts/react-components/actions/SessionActions.js.coffee
+++ b/web/app/assets/javascripts/react-components/actions/SessionActions.js.coffee
@@ -5,4 +5,7 @@ context = window
leaveSession: {}
mixersChanged: {}
allowLeaveSession: {}
+ syncWithServer: {}
+ toggleSessionVideo : {}
+ audioResync: {}
})
\ No newline at end of file
diff --git a/web/app/assets/javascripts/react-components/helpers/MixerHelper.js.coffee b/web/app/assets/javascripts/react-components/helpers/MixerHelper.js.coffee
index a1352040c..07107298d 100644
--- a/web/app/assets/javascripts/react-components/helpers/MixerHelper.js.coffee
+++ b/web/app/assets/javascripts/react-components/helpers/MixerHelper.js.coffee
@@ -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)
\ No newline at end of file
+ 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)
\ No newline at end of file
diff --git a/web/app/assets/javascripts/react-components/helpers/SessionHelper.js.coffee b/web/app/assets/javascripts/react-components/helpers/SessionHelper.js.coffee
index d61e3b71b..839d53b45 100644
--- a/web/app/assets/javascripts/react-components/helpers/SessionHelper.js.coffee
+++ b/web/app/assets/javascripts/react-components/helpers/SessionHelper.js.coffee
@@ -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
\ No newline at end of file
diff --git a/web/app/assets/javascripts/react-components/stores/RecordingStore.js.jsx.coffee b/web/app/assets/javascripts/react-components/stores/RecordingStore.js.jsx.coffee
new file mode 100644
index 000000000..d4fb8e447
--- /dev/null
+++ b/web/app/assets/javascripts/react-components/stores/RecordingStore.js.jsx.coffee
@@ -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)
+ }
+)
diff --git a/web/app/assets/javascripts/react-components/stores/SessionStore.js.coffee b/web/app/assets/javascripts/react-components/stores/SessionStore.js.coffee
index 701e90c56..6a9ed99df 100644
--- a/web/app/assets/javascripts/react-components/stores/SessionStore.js.coffee
+++ b/web/app/assets/javascripts/react-components/stores/SessionStore.js.coffee
@@ -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: []}
diff --git a/web/app/assets/javascripts/recordingModel.js b/web/app/assets/javascripts/recordingModel.js
index ebfff5ca8..669321c41 100644
--- a/web/app/assets/javascripts/recordingModel.js
+++ b/web/app/assets/javascripts/recordingModel.js
@@ -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({
diff --git a/web/app/assets/javascripts/webcam_viewer.js.coffee b/web/app/assets/javascripts/webcam_viewer.js.coffee
index ed2b1d772..931fef139 100644
--- a/web/app/assets/javascripts/webcam_viewer.js.coffee
+++ b/web/app/assets/javascripts/webcam_viewer.js.coffee
@@ -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")
diff --git a/web/app/assets/stylesheets/client/content.css.scss b/web/app/assets/stylesheets/client/content.css.scss
index 50d7e4876..852622cf8 100644
--- a/web/app/assets/stylesheets/client/content.css.scss
+++ b/web/app/assets/stylesheets/client/content.css.scss
@@ -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;
diff --git a/web/app/assets/stylesheets/client/react-components/SessionScreen.css.scss b/web/app/assets/stylesheets/client/react-components/SessionScreen.css.scss
index f8de208a1..237d06fbf 100644
--- a/web/app/assets/stylesheets/client/react-components/SessionScreen.css.scss
+++ b/web/app/assets/stylesheets/client/react-components/SessionScreen.css.scss
@@ -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;
diff --git a/web/app/assets/stylesheets/client/react-components/SessionSelfVolumeHover.css.scss b/web/app/assets/stylesheets/client/react-components/SessionSelfVolumeHover.css.scss
new file mode 100644
index 000000000..1d6c5eb35
--- /dev/null
+++ b/web/app/assets/stylesheets/client/react-components/SessionSelfVolumeHover.css.scss
@@ -0,0 +1,2 @@
+@import "client/common";
+
diff --git a/web/app/assets/stylesheets/dialogs/sessionSettingsDialog.css.scss b/web/app/assets/stylesheets/dialogs/sessionSettingsDialog.css.scss
new file mode 100644
index 000000000..13395483d
--- /dev/null
+++ b/web/app/assets/stylesheets/dialogs/sessionSettingsDialog.css.scss
@@ -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;
+ }
+}
\ No newline at end of file
diff --git a/web/app/assets/stylesheets/dialogs/shareDialog.css.scss b/web/app/assets/stylesheets/dialogs/shareDialog.css.scss
index 60bff5e51..541d0489a 100644
--- a/web/app/assets/stylesheets/dialogs/shareDialog.css.scss
+++ b/web/app/assets/stylesheets/dialogs/shareDialog.css.scss
@@ -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;
+ }
}
\ No newline at end of file
diff --git a/web/app/assets/stylesheets/minimal/minimal.css.scss b/web/app/assets/stylesheets/minimal/minimal.css.scss
index f159f3f0d..7ea6d2c7c 100644
--- a/web/app/assets/stylesheets/minimal/minimal.css.scss
+++ b/web/app/assets/stylesheets/minimal/minimal.css.scss
@@ -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
*/
\ No newline at end of file
diff --git a/web/app/assets/stylesheets/minimal/minimal_main.css.scss b/web/app/assets/stylesheets/minimal/minimal_main.css.scss
index 50feaf50f..78ca52f36 100644
--- a/web/app/assets/stylesheets/minimal/minimal_main.css.scss
+++ b/web/app/assets/stylesheets/minimal/minimal_main.css.scss
@@ -8,9 +8,4 @@ body {
overflow: visible !important;
height:100%;
margin:0 !important;
-}
-
-.wrapper {
- width:1280px;
- margin:0 auto;
}
\ No newline at end of file
diff --git a/web/app/assets/stylesheets/minimal/popup.css.scss b/web/app/assets/stylesheets/minimal/popup.css.scss
new file mode 100644
index 000000000..5db8190c4
--- /dev/null
+++ b/web/app/assets/stylesheets/minimal/popup.css.scss
@@ -0,0 +1,6 @@
+body.popup {
+ width:100%;
+ height:100%;
+ background-color:#404040;
+ overflow: hidden !important;
+}
\ No newline at end of file
diff --git a/web/app/assets/stylesheets/minimal/recording_controls.css.scss b/web/app/assets/stylesheets/minimal/recording_controls.css.scss
index 1ba110ca7..b2817b8ee 100644
--- a/web/app/assets/stylesheets/minimal/recording_controls.css.scss
+++ b/web/app/assets/stylesheets/minimal/recording_controls.css.scss
@@ -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;
+ }
}
\ No newline at end of file
diff --git a/web/app/controllers/api_music_notations_controller.rb b/web/app/controllers/api_music_notations_controller.rb
index 25997b24b..90e64435c 100644
--- a/web/app/controllers/api_music_notations_controller.rb
+++ b/web/app/controllers/api_music_notations_controller.rb
@@ -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
diff --git a/web/app/views/api_music_notations/create.rabl b/web/app/views/api_music_notations/create.rabl
index 1df3bde7d..1e9d0b313 100644
--- a/web/app/views/api_music_notations/create.rabl
+++ b/web/app/views/api_music_notations/create.rabl
@@ -1,3 +1,7 @@
object @music_notations
-attribute :id, :file_name
\ No newline at end of file
+attribute :id, :file_name
+
+node do |music_notation|
+ { file_url: "/api/music_notations/#{music_notation.id}" }
+end
\ No newline at end of file
diff --git a/web/app/views/clients/_sessionSettings.html.haml b/web/app/views/clients/_sessionSettings.html.haml
index 3a115260a..abd85514e 100644
--- a/web/app/views/clients/_sessionSettings.html.haml
+++ b/web/app/views/clients/_sessionSettings.html.haml
@@ -1,5 +1,5 @@
-.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'}/
\ No newline at end of file
diff --git a/web/app/views/clients/index.html.erb b/web/app/views/clients/index.html.erb
index 1b6ed5a96..06670850e 100644
--- a/web/app/views/clients/index.html.erb
+++ b/web/app/views/clients/index.html.erb
@@ -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();
diff --git a/web/app/views/dialogs/_shareDialog.html.erb b/web/app/views/dialogs/_shareDialog.html.erb
index 828f07193..589e6b2d3 100644
--- a/web/app/views/dialogs/_shareDialog.html.erb
+++ b/web/app/views/dialogs/_shareDialog.html.erb
@@ -3,9 +3,6 @@
share this
-
-
-
Share to Social Media:
@@ -39,7 +36,7 @@
-
+
Share a Link:
@@ -47,6 +44,11 @@
+
+
+
+
+
<% #render 'shareDialogUnused' %>
diff --git a/web/app/views/popups/recording_controls.html.slim b/web/app/views/popups/recording_controls.html.slim
index 531396f40..5df94974d 100644
--- a/web/app/views/popups/recording_controls.html.slim
+++ b/web/app/views/popups/recording_controls.html.slim
@@ -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)
\ No newline at end of file
+- provide(:page_name, 'recording-start-stop popup')
+= react_component 'PopupRecordingStartStop', {}
\ No newline at end of file
diff --git a/web/config/routes.rb b/web/config/routes.rb
index 72437c717..b51157a0c 100644
--- a/web/config/routes.rb
+++ b/web/config/routes.rb
@@ -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'