
diff --git a/web/app/assets/javascripts/react-components/SessionTrackPanHover.js.jsx.coffee b/web/app/assets/javascripts/react-components/SessionTrackPanHover.js.jsx.coffee
index d0b26da67..8ab86a572 100644
--- a/web/app/assets/javascripts/react-components/SessionTrackPanHover.js.jsx.coffee
+++ b/web/app/assets/javascripts/react-components/SessionTrackPanHover.js.jsx.coffee
@@ -8,7 +8,7 @@ MixerActions = @MixerActions
onInputsChanged: (sessionMixers) ->
mixers = sessionMixers.mixers
- newMixers = mixers.findMixerForTrack.apply(mixers, this.props.mixerFinder)
+ newMixers = mixers.refreshMixer(@state.mixers)
this.setState({mixers: newMixers})
@@ -18,8 +18,6 @@ MixerActions = @MixerActions
render: () ->
-
-
`
@@ -34,4 +32,12 @@ MixerActions = @MixerActions
`
+ componentWillUpdate: (nextProps, nextState) ->
+ $root = jQuery(this.getDOMNode())
+
+ # if the mixers go dead, whack our selves out of existence
+ unless nextState.mixers?
+ $container = $root.closest('.react-holder')
+ $container.data('bt').btOff()
+ return
})
\ No newline at end of file
diff --git a/web/app/assets/javascripts/react-components/SessionTrackVU.js.jsx.coffee b/web/app/assets/javascripts/react-components/SessionTrackVU.js.jsx.coffee
index a9ad12cd5..788dc46fc 100644
--- a/web/app/assets/javascripts/react-components/SessionTrackVU.js.jsx.coffee
+++ b/web/app/assets/javascripts/react-components/SessionTrackVU.js.jsx.coffee
@@ -1,4 +1,5 @@
context = window
+ptrCount = 0
@SessionTrackVU = React.createClass({
@@ -44,28 +45,27 @@ context = window
`
getInitialState: () ->
- {registered: null}
+ {registered: null, ptr: ptrCount++}
- registerVU: (mixerId) ->
+ registerVU: (mixer) ->
- return if @state.registered? || !mixerId?
+ return if @state.registered? || !mixer?
$root = $(this.getDOMNode())
- context.JK.VuHelpers.registerVU('single', mixerId, @render, @props.orientation == 'horizontal', @props.lightCount, $root.find('td'))
+ context.JK.VuHelpers.registerVU('single', mixer, @state.ptr, @props.orientation == 'horizontal', @props.lightCount, $root.find('td'))
- @setState(registered: {mixerId: mixerId, ptr: @render})
+ @setState(registered: {mixer: mixer, ptr: @state.ptr})
componentWillReceiveProps: (nextProps) ->
- @registerVU(nextProps.mixers.mixer?.id)
+ @registerVU(nextProps.mixers?.vuMixer)
componentDidMount: () ->
- @registerVU(@props.mixers.mixer?.id)
+ @registerVU(@props.mixers?.vuMixer)
componentWillUnmount: () ->
- console.log("UNMOUNTING")
if @state.registered?
- context.JK.VuHelpers.unregisterVU(@state.registered.mixerId, @state.registered.ptr)
+ context.JK.VuHelpers.unregisterVU(@state.registered.mixer, @state.registered.ptr)
})
\ No newline at end of file
diff --git a/web/app/assets/javascripts/react-components/SessionTrackVolumeHover.js.jsx.coffee b/web/app/assets/javascripts/react-components/SessionTrackVolumeHover.js.jsx.coffee
index 503b215a3..4321984ef 100644
--- a/web/app/assets/javascripts/react-components/SessionTrackVolumeHover.js.jsx.coffee
+++ b/web/app/assets/javascripts/react-components/SessionTrackVolumeHover.js.jsx.coffee
@@ -1,15 +1,16 @@
context = window
-
+ChannelGroupIds = context.JK.ChannelGroupIds
MixerActions = @MixerActions
@SessionTrackVolumeHover = React.createClass({
+ btElement: null
mixins: [Reflux.listenTo(@SessionMyTracksStore,"onInputsChanged")]
onInputsChanged: (sessionMixers) ->
mixers = sessionMixers.mixers
- newMixers = mixers.findMixerForTrack.apply(mixers, this.props.mixerFinder)
+ newMixers = mixers.refreshMixer(@state.mixers)
this.setState({mixers: newMixers})
@@ -21,19 +22,27 @@ MixerActions = @MixerActions
muting = $(e.currentTarget).is('.enabled')
- MixerActions.mute([this.state.mixers.mixer, this.state.mixers.oppositeMixer], muting)
+ if @state.mixers.mixer.group_id == ChannelGroupIds.AudioInputMusicGroup || @state.mixers.mixer.group_id == ChannelGroupIds.AudioInputChatGroup
+ MixerActions.mute([this.state.mixers.mixer, this.state.mixers.oppositeMixer], muting)
+ else
+ MixerActions.mute([this.state.mixers.mixer], muting)
+
handleMuteCheckbox: (e) ->
muting = $(e.target).is(':checked')
- MixerActions.mute([this.state.mixers.mixer, this.state.mixers.oppositeMixer], muting)
+ if @state.mixers.mixer.group_id == ChannelGroupIds.AudioInputMusicGroup || @state.mixers.mixer.group_id == ChannelGroupIds.AudioInputChatGroup
+ MixerActions.mute([this.state.mixers.mixer, this.state.mixers.oppositeMixer], muting)
+ else
+ MixerActions.mute([this.state.mixers.mixer], muting)
+
render: () ->
- muteMixer = this.state.mixers.muteMixer
+ muteMixer = this.state.mixers?.muteMixer
muteMixerId = muteMixer?.id
- volume_left = this.state.mixers.mixer?.volume_left
+ volume_left = this.state.mixers?.mixer?.volume_left
classes = classNames({
'track-icon-mute': true
@@ -76,7 +85,7 @@ MixerActions = @MixerActions
context.JK.checkbox($checkbox)
$checkbox.on('ifChanged', this.handleMuteCheckbox);
- if this.state.mixers.muteMixer.mute
+ if @state.mixers.muteMixer.mute
$checkbox.iCheck('check').attr('checked', true)
else
$checkbox.iCheck('uncheck').attr('checked', false)
@@ -84,10 +93,16 @@ MixerActions = @MixerActions
componentWillUpdate: (nextProps, nextState) ->
$root = jQuery(this.getDOMNode())
+ # if the mixers go dead, whack our selves out of existence
+ unless nextState.mixers?
+ $container = $root.closest('.react-holder')
+ $container.data('bt').btOff()
+ return
+
# re-initialize icheck
$checkbox = $root.find('input')
- if nextState.mixers.muteMixer?.mute
+ if nextState.mixers?.muteMixer?.mute
$checkbox.iCheck('check').attr('checked', true)
else
$checkbox.iCheck('uncheck').attr('checked', false)
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 11c83bf08..e180a4662 100644
--- a/web/app/assets/javascripts/react-components/helpers/MixerHelper.js.coffee
+++ b/web/app/assets/javascripts/react-components/helpers/MixerHelper.js.coffee
@@ -8,6 +8,8 @@ MIX_MODES = context.JK.MIX_MODES;
@MixerHelper = class MixerHelper
constructor: (@session, @masterMixers, @personalMixers, @metro, @noAudioUsers, @mixMode) ->
+ @mixMode = MIX_MODES.PERSONAL # TODO - remove mixMode from MixerHelper? Or at least stop using it in most functions
+ @app = @session.app
@mixersByResourceId = {}
@mixersByTrackId = {}
@allMixers = {}
@@ -48,6 +50,7 @@ MIX_MODES = context.JK.MIX_MODES;
mixerPair.personal = personalMixer;
@groupTypes()
+ @chatMixer = @resolveChatMixer()
groupTypes: () ->
localMediaMixers = @mixersForGroupIds(@mediaTrackGroups, MIX_MODES.MASTER)
@@ -256,7 +259,7 @@ MIX_MODES = context.JK.MIX_MODES;
photoUrl: "/assets/content/icon_recording.png"
showLoop: isOpener && !@session.isPlayingRecording()
track: serverBackingTrack
- mixers: {mixer: mixer, oppositeMixer: oppositeMixer, vuMixer: mixer, muteMixer: mixer}
+ mixers: @mediaMixers(mixer, isOpener)
backingTracks.push(data)
@@ -320,15 +323,13 @@ MIX_MODES = context.JK.MIX_MODES;
part = oneOfTheTracks.part
part = '' unless name?
- oppositeMixer = @getMixerByResourceId(mixer.rid, MIX_MODES.PERSONAL)
-
data =
name: jamTrackName
part: part
isOpener: isOpener
instrumentIcon: instrumentIcon
track: oneOfTheTracks
- mixers: {mixer: mixer, oppositeMixer: oppositeMixer, vuMixer: mixer, muteMixer: mixer}
+ mixers: @mediaMixers(mixer, isOpener)
_jamTracks.push(data)
@@ -341,7 +342,7 @@ MIX_MODES = context.JK.MIX_MODES;
serverRecordedTracks = @session.recordedTracks()
- isOpener = @recordingTrackMixers[0].group_id == ChannelGroupIds.MediaTrackGroup;
+ isOpener = @recordingTrackMixers[0].group_id == ChannelGroupIds.MediaTrackGroup
# using the server's info in conjuction with the client's, draw the recording tracks
if serverRecordedTracks
@@ -380,16 +381,14 @@ MIX_MODES = context.JK.MIX_MODES;
instrumentIcon = context.JK.getInstrumentIcon24(oneOfTheTracks.instrument_id)
userName = oneOfTheTracks.user.name
userName = oneOfTheTracks.user.first_name + ' ' + oneOfTheTracks.user.last_name unless userName?
- oppositeMixer = @getMixerByResourceId(mixer.rid, MIX_MODES.PERSONAL)
- isOpener = mixer.group_id == ChannelGroupIds.MediaTrackGroup
data =
recordingName: recordingName
isOpener: isOpener
userName: userName
instrumentIcon: instrumentIcon
track: oneOfTheTracks
- mixers: {mixer: mixer, oppositeMixer: oppositeMixer, vuMixer: mixer, muteMixer: mixer}
+ mixers: @mediaMixers(mixer, isOpener)
recordedTracks.push(data)
@@ -412,6 +411,56 @@ MIX_MODES = context.JK.MIX_MODES;
metronome
+ resolveChatMixer: () ->
+ masterChatMixers = @mixersForGroupId(ChannelGroupIds.AudioInputChatGroup, MIX_MODES.MASTER);
+
+ return null if masterChatMixers.length == 0
+
+ personalChatMixers = @mixersForGroupId(ChannelGroupIds.AudioInputChatGroup, MIX_MODES.PERSONAL);
+
+ if personalChatMixers.length == 0
+ logger.warn("unable to find personal mixer for voice chat");
+ return null
+
+
+ masterChatMixer = masterChatMixers[0];
+ personalChatMixer = personalChatMixers[0];
+
+ {
+ master: {
+ mixer: masterChatMixer
+ muteMixer: masterChatMixer
+ vuMixer: masterChatMixer
+ oppositeMixer: personalChatMixer
+ }
+ personal: {
+ mixer: personalChatMixer
+ muteMixer: personalChatMixer
+ vuMixer: personalChatMixer
+ oppositeMixer: masterChatMixer
+ }
+ }
+
+ # supply the master mixer of a media track, and this function will harvest out the rest
+ mediaMixers:(masterMixer, isOpener) ->
+ personalMixer = if isOpener then @getMixerByResourceId(masterMixer.rid, MIX_MODES.PERSONAL) else null
+ personalVuMixer = if isOpener then personalMixer else masterMixer
+ {
+ isOpener: isOpener
+
+ master: {
+ mixer: masterMixer
+ muteMixer: masterMixer
+ vuMixer: masterMixer
+ }
+ personal: {
+ mixer: personalMixer
+ muteMixer: personalMixer
+ vuMixer: personalVuMixer
+ }
+ }
+
+
mixersForGroupIds: (groupIds, mixMode) ->
foundMixers = []
mixers = if mixMode == MIX_MODES.MASTER then @masterMixers else @personalMixers;
@@ -434,7 +483,6 @@ MIX_MODES = context.JK.MIX_MODES;
getMixer: (mixerId, mode) ->
mode = @mixMode unless mode?
-
@allMixers[(if mode then 'M' else 'P') + mixerId]
getMixerByTrackId: (trackId, mode) ->
@@ -483,15 +531,16 @@ MIX_MODES = context.JK.MIX_MODES;
return mixerPair.personal
- findMixerForTrack: (client_id, track, myTrack) ->
+ findMixerForTrack: (client_id, track, myTrack, mode = MIX_MODES.PERSONAL) ->
mixer = null # what is the best mixer for this track/client ID?
oppositeMixer = null # what is the corresponding mixer in the opposite mode?
vuMixer = null
muteMixer = null
+
if myTrack
# when it's your track, look it up by the backend resource ID
- mixer = @getMixerByTrackId(track.client_track_id, @mixMode)
+ mixer = @getMixerByTrackId(track.client_track_id, mode)
vuMixer = mixer
muteMixer = mixer
@@ -501,9 +550,9 @@ MIX_MODES = context.JK.MIX_MODES;
if mixer
# find the matching AudioInputMusicGroup for the opposite mode
- oppositeMixer = @getMixerByTrackId(track.client_track_id, !@mixMode)
+ oppositeMixer = @getMixerByTrackId(track.client_track_id, !mode)
- if @mixMode == MIX_MODES.PERSONAL
+ if mode == MIX_MODES.PERSONAL
muteMixer = oppositeMixer; # make the master mixer the mute mixer
# sanity checks
@@ -514,7 +563,7 @@ MIX_MODES = context.JK.MIX_MODES;
else
logger.debug("local track is not present: ", track, @allMixers)
else
- switch @mixMode
+ switch mode
when MIX_MODES.MASTER
# when it's a remote track and in master mode, we should find the PeerAudioInputMusicGroup
@@ -575,17 +624,10 @@ MIX_MODES = context.JK.MIX_MODES;
mixer = @getMixer(mixerId, mode)
mixer.mute = muting
- faderChanged: (data, mixerIds, groupId) ->
- # media tracks are the only controls that sometimes set two mixers right now
- hasMasterAndPersonalControls = mixerIds.length == 2
-
- for mixerId, i in mixerIds
+ faderChanged: (data, mixers, groupId) ->
+ for mixer in mixers
broadcast = !(data.dragging) # If fader is still dragging, don't broadcast
- mode = undefined
- if hasMasterAndPersonalControls
- mode = if i == 0 then MIX_MODES.MASTER else MIX_MODES.PERSONAL
-
- mixer = @fillTrackVolumeObject(mixerId, mode, broadcast)
+ mixer = @fillTrackVolumeObject(mixer.id, mixer.mode, broadcast)
@setMixerVolume(mixer, data.percentage)
@@ -602,12 +644,11 @@ MIX_MODES = context.JK.MIX_MODES;
context.JK.FaderHelpers.setFaderValue(mixer.id, gainPercent)
context.JK.FaderHelpers.showFader(mixer.id)
- panChanged: (data, mixerIds, groupId) ->
+ panChanged: (data, mixers, groupId) ->
# media tracks are the only controls that sometimes set two mixers right now
- for mixerId, i in mixerIds
+ for mixer in mixers
broadcast = !(data.dragging) # If fader is still dragging, don't broadcast
- mode = undefined
- mixer = @fillTrackVolumeObject(mixerId, mode, broadcast)
+ mixer = @fillTrackVolumeObject(mixer.id, mixer.mode, broadcast)
@setMixerPan(mixer, data.percentage)
@@ -626,7 +667,6 @@ MIX_MODES = context.JK.MIX_MODES;
context.jamClient.SessionSetControlState(mixer.id, mixer.mode);
loopChanged: (mixer, shouldLoop) ->
- console.log("mixer, shouldLoop", mixer, shouldLoop)
@fillTrackVolumeObject(mixer.id, mixer.mode)
context.trackVolumeObject.loop = shouldLoop
@@ -701,11 +741,8 @@ MIX_MODES = context.JK.MIX_MODES;
@currentMixerRangeMax = mixer.range_high;
mixer
- updateVU: (mixerId, leftValue, leftClipping, rightValue, rightClipping) ->
- mixer = @getMixer(mixerId, @mixMode)
- unless mixer
- # try again, in the opposite mode (awful that this is necessary)
- mixer = @getMixer(mixerId, !@mixMode)
+ updateVU: (mixerId, mode, leftValue, leftClipping, rightValue, rightClipping) ->
+ mixer = @getMixer(mixerId, mode)
if mixer
context.JK.VuHelpers.updateVU3(mixer, leftValue, leftClipping, rightValue, rightClipping)
@@ -733,7 +770,7 @@ MIX_MODES = context.JK.MIX_MODES;
if mixers.length == 0
logger.warn("could not find mixer with group ID: " + groupId + ', mode:' + mode)
- return {}
+ return null
found = null
for mixer in mixers
@@ -743,7 +780,7 @@ MIX_MODES = context.JK.MIX_MODES;
unless found?
logger.warn("could not find mixer with categoryId: " + categoryId)
- return {}
+ return null
else
{
mixer: found,
@@ -756,4 +793,34 @@ MIX_MODES = context.JK.MIX_MODES;
@getGroupMixer(CategoryGroupIds.AudioInputMusic, mode)
getChatCategoryMixer: (mode) ->
- @getGroupMixer(CategoryGroupIds.AudioInputChat, mode)
\ No newline at end of file
+ @getGroupMixer(CategoryGroupIds.AudioInputChat, mode)
+
+ getMediaCategoryMixer: (mode) ->
+ @getGroupMixer(CategoryGroupIds.MediaTrack, mode)
+
+ getUserMediaCategoryMixer: (mode) ->
+ @getGroupMixer(CategoryGroupIds.UserMedia, mode)
+
+
+ refreshMixer: (mixers) ->
+ return null unless mixers? && mixers.mixer?
+
+ mixer = @getMixer(mixers.mixer.id, mixers.mixer.mode)
+
+ if mixer?
+ oppositeMixer = if mixers.oppositeMixer then @getMixer(mixers.oppositeMixer.id, mixers.oppositeMixer.mode) else null
+ {
+ mixer: mixer
+ vuMixer: @getMixer(mixers.vuMixer.id, mixers.vuMixer.mode)
+ muteMixer: @getMixer(mixers.muteMixer.id, mixers.muteMixer.mode)
+ oppositeMixer: oppositeMixer
+ }
+ else
+ return null
+
+
+ recordingName: () ->
+ @session.recordingName()
+
+ jamTrackName: () ->
+ @session.jamTrackName()
diff --git a/web/app/assets/javascripts/react-components/mixins/MasterPersonalMixersMixin.js.coffee b/web/app/assets/javascripts/react-components/mixins/MasterPersonalMixersMixin.js.coffee
new file mode 100644
index 000000000..553aa3a5e
--- /dev/null
+++ b/web/app/assets/javascripts/react-components/mixins/MasterPersonalMixersMixin.js.coffee
@@ -0,0 +1,17 @@
+context = window
+MIX_MODES = context.JK.MIX_MODES
+
+@MasterPersonalMixersMixin = {
+
+ mixer: () ->
+ if @props.mode == MIX_MODES.MASTER
+ @props.mixers['master'].mixer
+ else
+ @props.mixers['personal'].mixer
+
+ mixers: () ->
+ if @props.mode == MIX_MODES.MASTER
+ @props.mixers['master']
+ else
+ @props.mixers['personal']
+}
\ No newline at end of file
diff --git a/web/app/assets/javascripts/react-components/mixins/SessionMediaTracksMixin.js.coffee b/web/app/assets/javascripts/react-components/mixins/SessionMediaTracksMixin.js.coffee
new file mode 100644
index 000000000..9517dff6f
--- /dev/null
+++ b/web/app/assets/javascripts/react-components/mixins/SessionMediaTracksMixin.js.coffee
@@ -0,0 +1,51 @@
+context = window
+MIX_MODES = context.JK.MIX_MODES
+logger = context.JK.logger
+
+@SessionMediaTracksMixin = {
+
+ metronomeTrulyGoneCheck: () ->
+
+ logger.debug("metronome is completely gone")
+ @setState({metronomeFlickerTimeout: null})
+
+ onInputsChanged: (sessionMixers) ->
+
+ session = sessionMixers.session
+ mixers = sessionMixers.mixers
+
+ # the backend delete/adds the metronome rapidly when the user hits play. this is custom code to deal with that
+
+ metronomeFlickerTimeout = @state.metronomeFlickerTimeout
+
+ if mixers.metronome?
+ if metronomeFlickerTimeout?
+ logger.debug("canceling metronome flicker timeout because metronome mixer reappeared")
+ clearTimeout(metronomeFlickerTimeout)
+ metronomeFlickerTimeout = null
+ else
+ if @state.metronomeIsShowing
+ logger.debug("setting metronome flicker timeout")
+ clearTimeout(metronomeFlickerTimeout) if metronomeFlickerTimeout?
+ metronomeFlickerTimeout = setTimeout(@metronomeTrulyGoneCheck, 1000)
+
+ metronomeIsShowing = mixers.metronome?
+
+ state =
+ isRecording: session.isRecording
+ mediaSummary: mixers.mediaSummary
+ backingTracks: mixers.backingTracks
+ jamTracks: mixers.jamTracks
+ recordedTracks: mixers.recordedTracks
+ metronome: mixers.metronome
+ mediaCategoryMixer: mixers.getMediaCategoryMixer(@props.mode)
+ recordingName: mixers.recordingName()
+ jamTrackName: mixers.jamTrackName()
+ metronomeIsShowing: metronomeIsShowing
+ metronomeFlickerTimeout: metronomeFlickerTimeout
+
+ @inputsChangedProcessed(state) if @inputsChangedProcessed?
+
+ @setState(state)
+
+}
\ No newline at end of file
diff --git a/web/app/assets/javascripts/react-components/mixins/SessionMyTracksMixin.js.coffee b/web/app/assets/javascripts/react-components/mixins/SessionMyTracksMixin.js.coffee
new file mode 100644
index 000000000..937102629
--- /dev/null
+++ b/web/app/assets/javascripts/react-components/mixins/SessionMyTracksMixin.js.coffee
@@ -0,0 +1,45 @@
+context = window
+
+@SessionMyTracksMixin = {
+
+ onInputsChanged: (sessionMixers) ->
+
+
+ session = sessionMixers.session
+ mixers = sessionMixers.mixers
+
+ tracks = []
+
+ if session.inSession()
+ participant = session.getParticipant(@app.clientId)
+ photoUrl = context.JK.resolveAvatarUrl(participant.user.photo_url);
+
+ chatMixer = mixers.chatMixer
+ chat = null
+ if chatMixer
+ chat =
+ mixers: chatMixer
+ mode: @props.mode
+ photoUrl: photoUrl
+
+
+ if participant
+ name = participant.user.name;
+
+ for track in participant.tracks
+ # try to find mixer info for this track
+ mixerFinder = [participant.client_id, track, true] # so that other callers can re-find their mixer data
+ mixerData = mixers.findMixerForTrack(participant.client_id, track, true, @props.mode)
+
+ # todo: sessionModel.setAudioEstablished
+
+ instrumentIcon = context.JK.getInstrumentIcon45(track.instrument_id);
+
+ tracks.push({track: track, mixerFinder: mixerFinder, mixers: mixerData, name: name, instrumentIcon: instrumentIcon, photoUrl: photoUrl, clientId: participant.client_id})
+
+
+ else
+ logger.debug("SessionMyTracks: unable to find participant")
+
+ this.setState(tracks: tracks, session:session, chat: chat)
+}
\ No newline at end of file
diff --git a/web/app/assets/javascripts/react-components/mixins/SessionOtherTracksMixin.js.coffee b/web/app/assets/javascripts/react-components/mixins/SessionOtherTracksMixin.js.coffee
new file mode 100644
index 000000000..adda6eac3
--- /dev/null
+++ b/web/app/assets/javascripts/react-components/mixins/SessionOtherTracksMixin.js.coffee
@@ -0,0 +1,6 @@
+context = window
+
+@SessionOtherTracksMixin = {
+
+
+}
\ No newline at end of file
diff --git a/web/app/assets/javascripts/react-components/stores/MixerStore.js.coffee b/web/app/assets/javascripts/react-components/stores/MixerStore.js.coffee
index 70c97763a..fb274cdda 100644
--- a/web/app/assets/javascripts/react-components/stores/MixerStore.js.coffee
+++ b/web/app/assets/javascripts/react-components/stores/MixerStore.js.coffee
@@ -84,15 +84,16 @@ rest = context.JK.Rest()
vuVal = 0.0;
if eventName == "vu"
mixerId = vuInfo[1];
- leftValue = vuInfo[2];
- leftClipping = vuInfo[3];
- rightValue = vuInfo[4];
- rightClipping = vuInfo[5];
+ mode = vuInfo[2];
+ leftValue = vuInfo[3];
+ leftClipping = vuInfo[4];
+ rightValue = vuInfo[5];
+ rightClipping = vuInfo[6];
# TODO - no guarantee range will be -80 to 20. Get from the
# GetControlState for this mixer which returns min/max
# value is a DB value from -80 to 20. Convert to float from 0.0-1.0
- @mixers.updateVU(mixerId, (leftValue + 80) / 80, leftClipping, (rightValue + 80) / 80, rightClipping)
+ @mixers.updateVU(mixerId, mode, (leftValue + 80) / 80, leftClipping, (rightValue + 80) / 80, rightClipping)
#@mixers.updateVU(mixerId + "_vur", (rightValue + 80) / 80, rightClipping)
@@ -132,14 +133,14 @@ rest = context.JK.Rest()
# simulate a state change to cause a UI redraw
@issueChange()
- onFaderChanged: (data, mixerIds, groupId) ->
+ onFaderChanged: (data, mixers, groupId) ->
- @mixers.faderChanged(data, mixerIds, groupId)
+ @mixers.faderChanged(data, mixers, groupId)
@issueChange()
- onPanChanged: (data, mixerIds, groupId) ->
- @mixers.panChanged(data, mixerIds, groupId)
+ onPanChanged: (data, mixers, groupId) ->
+ @mixers.panChanged(data, mixers, groupId)
@issueChange()
@@ -187,13 +188,9 @@ rest = context.JK.Rest()
onMixersChanged: (type, text) ->
- console.log("MixerStore: onMixersChanged")
-
@masterMixers = context.jamClient.SessionGetAllControlState(true);
@personalMixers = context.jamClient.SessionGetAllControlState(false);
- console.log("masterMixers", @masterMixers)
- console.log("personalMixers", @personalMixers)
@mixers = new context.MixerHelper(@session, @masterMixers, @personalMixers, @metro, @noAudioUsers, @mixers?.mixMode || MIX_MODES.PERSONAL)
SessionActions.mixersChanged.trigger(type, text, @mixers.getTrackInfo())
@@ -201,9 +198,10 @@ rest = context.JK.Rest()
@issueChange()
onMixerModeChanged: (mode) ->
-
- @mixers = new context.MixerHelper(@session, @masterMixers, @personalMixers, @metro, @noAudioUsers, mode)
- @issueChange()
+ if mode == MIX_MODES.MASTER
+ @app.layout.showDialog('session-master-mix-dialog') unless @app.layout.isDialogShowing('session-master-mix-dialog')
+ else
+ @app.layout.closeDialog('session-master-mix-dialog') if @app.layout.isDialogShowing('session-master-mix-dialog')
onSyncTracks: () ->
logger.debug("MixerStore: onSyncTracks")
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 1742ab26e..7c81247fd 100644
--- a/web/app/assets/javascripts/react-components/stores/SessionStore.js.coffee
+++ b/web/app/assets/javascripts/react-components/stores/SessionStore.js.coffee
@@ -925,7 +925,7 @@ NotificationActions = @NotificationActions
@currentSession = sessionData
- console.log("SESSION CHANGED", sessionData)
+ #logger.debug("session changed")
@issueChange()
diff --git a/web/app/assets/javascripts/scheduled_session.js.erb b/web/app/assets/javascripts/scheduled_session.js.erb
index 021c90bb7..11891e938 100644
--- a/web/app/assets/javascripts/scheduled_session.js.erb
+++ b/web/app/assets/javascripts/scheduled_session.js.erb
@@ -720,7 +720,7 @@
// we redirect to the session screen, which handles the REST call to POST /participants.
logger.debug("joining session screen: " + sessionId)
- context.location = '/client#/session2/' + sessionId;
+ context.location = '/client#/session/' + sessionId;
};
if (createSessionSettings.createType == '<%= MusicSession::CREATE_TYPE_START_SCHEDULED%>') {
diff --git a/web/app/assets/javascripts/session.js b/web/app/assets/javascripts/session.js
index 4f8ed92c6..30f2d19f4 100644
--- a/web/app/assets/javascripts/session.js
+++ b/web/app/assets/javascripts/session.js
@@ -3286,7 +3286,7 @@
'beforeLeave' : beforeLeave,
'beforeDisconnect' : beforeDisconnect,
};
- app.bindScreen('session', screenBindings);
+ //app.bindScreen('session', screenBindings);
$recordingManagerViewer = $('#recording-manager-viewer');
$screen = $('#session-screen');
diff --git a/web/app/assets/javascripts/utils.js b/web/app/assets/javascripts/utils.js
index 5332f9b88..3f328f7ce 100644
--- a/web/app/assets/javascripts/utils.js
+++ b/web/app/assets/javascripts/utils.js
@@ -223,7 +223,15 @@
reactHovers.btOff();
})
reactHovers = []
+ var reactElement = null
+ var reactDomNode = null;
+ function cleanupReact() {
+ if(reactDomNode) {
+ logger.debug()
+ React.unmountComponentAtNode(reactDomNode)
+ }
+ }
function waitForBubbleHover($bubble) {
$bubble.hoverIntent({
over: function() {
@@ -239,7 +247,7 @@
var timeout = null;
-
+ options.postHide = cleanupReact;
options.trigger = 'none'
options.clickAnywhereToClose = true
options.closeWhenOthersOpen = true
@@ -248,11 +256,12 @@
if(!reactElementName) {
throw "unknown react element" + reactElementName
}
- var element = React.createElement(reactElement, reactPropsCallback());
-
+ reactElement= React.createElement(reactElement, reactPropsCallback());
var $container = $(container)
-
- React.render(element, $container.find('.react-holder').get(0))
+ reactDomNode = $container.find('.react-holder').get(0)
+ $(reactDomNode).data('bt', $element)
+ console.log("reactDomNode", reactDomNode)
+ React.render(reactElement, reactDomNode)
}
options.postShow = function(container) {
diff --git a/web/app/assets/javascripts/vuHelpers.js b/web/app/assets/javascripts/vuHelpers.js
index d49fde42c..dea7b6c0e 100644
--- a/web/app/assets/javascripts/vuHelpers.js
+++ b/web/app/assets/javascripts/vuHelpers.js
@@ -97,14 +97,20 @@
},
+ createQualifiedId: function(mixer) {
+ return (mixer.mode ? 'M' : 'P') + mixer.id
+ },
+
// type can be 'single' or 'double', meaning how the VU is represented (one set of lights, two)
// mixerId is the ID of the mixer
// and someFunction is used to make the registration (equality check).
- registerVU: function(type, mixerId, someFunction, horizontal, lightCount, leftLights, rightLights) {
- var registrations = this.registeredMixers[mixerId]
+ registerVU: function(type, mixer, someFunction, horizontal, lightCount, leftLights, rightLights) {
+
+ var fqId = this.createQualifiedId(mixer)
+ var registrations = this.registeredMixers[fqId]
if (!registrations) {
registrations = []
- this.registeredMixers[mixerId] = registrations
+ this.registeredMixers[fqId] = registrations
}
if(type == 'single') {
@@ -117,22 +123,26 @@
},
- unregisterVU: function(mixerId, someFunction) {
- var registrations = this.registeredMixers[mixerId]
+ unregisterVU: function(mixer, someFunction) {
+ var fqId = this.createQualifiedId(mixer)
+ var registrations = this.registeredMixers[fqId]
if (!registrations || registrations.length == 0) {
- logger.debug("no registration found for: " + type + ":" + mixerId)
+ logger.debug("no registration found for:" + fqId, registrations, this.registeredMixers)
return
}
+ else {
+ logger.debug("unregistering " + fqId + ", " + registrations.length)
+ }
var origLength = registrations.length
registrations = registrations.filter(function(element) {
- someFunction !== element.ptr
+ return someFunction !== element.ptr
})
- this.registeredMixers[mixerId] = registrations
+ this.registeredMixers[fqId] = registrations
if(origLength == registrations.length) {
- logger.warn("did not find anything to unregister for: " + type + ':' + mixerId)
+ logger.warn("did not find anything to unregister for: " + fqId)
}
},
@@ -169,7 +179,9 @@
// sentMixerId ends with vul or vur
updateVU3: function(mixer, leftValue, leftClipping, rightValue, rightClipping) {
- var registrations = this.registeredMixers[mixer.id]
+ var fqId = this.createQualifiedId(mixer)
+
+ var registrations = this.registeredMixers[fqId]
if (registrations) {
var j;
for(j = 0; j < registrations.length; j++) {
@@ -196,53 +208,6 @@
}
},
- /**
- * Given a selector representing a container for a VU meter and
- * a value between 0.0 and 1.0, light the appropriate lights.
- */
- updateVU2: function (side, mixer, value) {
- // There are 13 VU lights. Figure out how many to
- // light based on the incoming value.
-
- var $selector = $('.' + side + '-' + mixer.id)
-
- $selector.each(function() {
- var $table = $(this)
- var horizontal = $table.is('.horizontal')
-
- var lightCount = Number($table.attr('data-light-count'))
-
- var i = 0;
- var state = 'on';
- var lights = Math.round(value * lightCount);
- var redSwitch = Math.round(lightCount * 0.6666667);
-
- var $light = null;
- var colorClass = 'vu-green-';
- var thisLightSelector = null;
-
- // Remove all light classes from all lights
- var allLightsSelector = $table.find('td');
- $(allLightsSelector).removeClass('vu-green-off vu-green-on vu-red-off vu-red-on');
-
- // Set the lights
- for (i = 0; i < lightCount; i++) {
- colorClass = 'vu-green-';
- state = 'on';
- if (i >= redSwitch) {
- colorClass = 'vu-red-';
- }
- if (i >= lights) {
- state = 'off';
- }
-
- var lightIndex = horizontal ? i : lightCount - i - 1;
- allLightsSelector.eq(lightIndex).addClass(colorClass + state);
- }
- })
-
- }
-
};
})(window, jQuery);
\ No newline at end of file
diff --git a/web/app/assets/stylesheets/client/react-components/MediaControls.scss.scss b/web/app/assets/stylesheets/client/react-components/MediaControls.scss.scss
index 5bf94e719..94e75da6c 100644
--- a/web/app/assets/stylesheets/client/react-components/MediaControls.scss.scss
+++ b/web/app/assets/stylesheets/client/react-components/MediaControls.scss.scss
@@ -154,7 +154,7 @@
.jam-track-get-ready, .media-seeking {
display:none;
position:absolute;
- top:-29px;
+ top:20px;
margin-left:-50px;
width:100px;
vertical-align:middle;
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 7674ab62e..d7e8bd558 100644
--- a/web/app/assets/stylesheets/client/react-components/SessionScreen.css.scss
+++ b/web/app/assets/stylesheets/client/react-components/SessionScreen.css.scss
@@ -73,7 +73,7 @@
position: absolute;
top: 90px;
padding: 0 15px;
- box-sizing: border-box;
+ @include border_box_sizing;
bottom: 0;
left: 0;
right: 0;
@@ -92,301 +92,6 @@
margin-top:20px;
}
- .session-track {
- float:left;
- margin: 10px 0;
- color: $ColorTextTypical;
- background-color: #242323;
- border-radius: 6px;
- min-height: 76px;
- max-width: 210px;
- position:relative;
- @include border_box_sizing;
-
- .session-track-contents {
- padding: 6px 6px 6px 10px;
- }
- .disabled-track-overlay {
- width: 0;
- height: 0;
- position: absolute;
- background-color:#555;
- border-radius: 6px;
- }
-
- &.no-mixer, &.no-audio {
- .disabled-track-overlay {
- width: 100%;
- height: 100%;
- opacity:0.5;
- }
- }
-
-
- // media overrides
- &.backing-track, &.recorded-track, &.jam-track, &.metronome {
- width:210px;
- table.vu {
- float: right;
- margin-top: 30px;
- margin-right: 4px;
- }
- .track-controls {
- float:right;
- }
- .track-buttons {
- float:right;
- }
- .track-icon-pan {
- float:right;
- margin-right:20px;
- }
- .track-icon-mute{
- float:right;
- }
- }
-
- &.metronome {
- .track-instrument {
- float:left;
- margin-left:0;
- margin-right: 8px;
- margin-top: -3px;
- }
- .track-controls {
- margin-left:0;
- }
- }
-
- &.recorded-track, &.jam-track {
- height:56px;
- min-height:56px;
- .track-buttons {
- margin-top:2px;
- }
- .track-controls {
- margin-left:0;
- }
- table.vu {
- margin-top:10px;
- }
- .track-instrument {
- float: left;
- margin: -2px 7px 0 0;
- }
- }
- }
-
-
-
- .react-holder {
- &.SessionTrackVolumeHover, &.SessionSelfVolumeHover {
- height:331px;
- width:235px;
-
- .session-track {
- float:left;
- background-color: #242323;
- border-radius: 4px;
- display: inline-block;
- height: 300px;
- margin-right: 14px;
- position: relative;
- width: 70px;
- margin-top:19px;
- margin-left:24px;
- }
-
- .track-icon-mute {
- float:none;
- position: absolute;
- top: 246px;
- left: 29px;
- }
-
- .track-gain {
- position:absolute;
- width:28px;
- height:209px;
- top:32px;
- left:23px;
- }
-
- .fader {
- height:209px;
- }
-
- .handle {
- bottom:0%;
- display:none;
- }
-
- .textual-help {
- float:left;
- width:100px;
- }
-
- p {
- font-size:12px;
- padding:0;
- margin:16px 0 0;
- line-height:125%;
-
- &:nth-child(1) {
- margin-top:19px;
- }
- }
-
- .icheckbox_minimal {
- position:absolute;
- top: 271px;
- left: 12px;
- }
-
- input {
- position:absolute;
- top: 271px;
- left: 12px;
- }
-
- label {
- @include labelFont;
- position:absolute;
- top:273px;
- 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;
- padding:15px;
- @include border_box_sizing;
-
- .session-pan {
- .textual-help {
- float:left;
- width:100px;
- }
- }
-
- p {
- font-size:12px;
- padding:0;
- line-height:125%;
- }
- .track-pan {
- background-color: #242323;
- border-radius: 4px;
- display: inline-block;
- height: 70px;
- position: relative;
- width: 300px;
- margin-top:15px;
- }
- .fader {
- position:absolute;
- width:205px;
- height:24px;
- top:34px;
- left:44px;
- background-image: url('/assets/content/bkg_slider_gain_horiz_24.png');
- }
- .handle {
- display:none;
-
- img {
- position:absolute;
- left:-5px;
- }
- }
- .left-label {
- @include labelFont;
- position:absolute;
- left:13px;
- top:40px;
- }
- .right-label {
- @include labelFont;
- position:absolute;
- right:12px;
- top:40px;
- }
- .floater {
- width:20px;
- text-align:center;
- top:-22px;
- left:-8px;
- @include labelFont;
- position:absolute;
- }
- }
- }
.when-empty {
margin-top:20px;
@@ -395,109 +100,6 @@
overflow:hidden;
}
- .session-track {
-
- .name {
- width: 100%;
- margin-bottom: 6px;
- @include labelFont;
- }
-
- .track-avatar {
- float: left;
- padding: 1px;
- width: 44px;
- height: 44px;
- background-color: #ed3618;
- -webkit-border-radius: 22px;
- -moz-border-radius: 22px;
- border-radius: 22px;
-
- img {
- width: 44px;
- height: 44px;
- -webkit-border-radius: 22px;
- -moz-border-radius: 22px;
- border-radius: 22px;
- }
- }
-
- .track-instrument {
- float: left;
- padding: 1px;
- margin-left: 5px;
- }
- }
-
- table.vu {
- float: left;
-
- td {
- border: 3px solid #242323;
- }
- }
-
- .track-controls {
- margin-top: 2px;
- margin-left: 10px;
- float:left
- }
-
- .track-buttons {
- margin-top:22px;
- padding:0 0 0 3px;
- }
-
- .track-icon-mute {
- float:left;
- position:relative;
- top:0;
- left:0;
- }
-
- .track-icon-pan {
- float:left;
- cursor: pointer;
- width: 20px;
- height: 20px;
- background-image:url('/assets/content/icon_pan.png');
- background-repeat:no-repeat;
- text-align: center;
- margin-left:10px;
- //-webkit-transform: rotate(7deg); /* Chrome, Safari, Opera */
- //transform: rotate(7deg);
- }
-
- .track-icon-equalizer {
- float:left;
- cursor: pointer;
- width: 20px;
- height: 20px;
- background-image:url('/assets/content/icon_equalizer.png');
- background-repeat:no-repeat;
- text-align: center;
- margin-left:7px;
- }
-
- .session-track-list-enter {
- opacity: 0.01;
- transition: opacity .5s ease-in;
-
- &.session-track-list-enter-active {
- opacity: 1;
- }
- }
-
- .session-track-list-leave {
- opacity:1;
- transition: opacity .5s ease-in;
-
- &.session-track-list-leave-active {
- opacity: 0.01;
- }
- }
-
-
.session-track-settings {
height:20px;
cursor:pointer;
@@ -607,4 +209,23 @@
@include border_box_sizing;
padding:6px;
}
-}
\ No newline at end of file
+}
+
+
+.session-track-list-enter {
+ opacity: 0.01;
+ transition: opacity .5s ease-in;
+
+ &.session-track-list-enter-active {
+ opacity: 1;
+ }
+}
+
+.session-track-list-leave {
+ opacity:1;
+ transition: opacity .5s ease-in;
+
+ &.session-track-list-leave-active {
+ opacity: 0.01;
+ }
+}
diff --git a/web/app/assets/stylesheets/client/react-components/SessionTrack.css.scss b/web/app/assets/stylesheets/client/react-components/SessionTrack.css.scss
new file mode 100644
index 000000000..e19ca940b
--- /dev/null
+++ b/web/app/assets/stylesheets/client/react-components/SessionTrack.css.scss
@@ -0,0 +1,393 @@
+@import "client/common";
+
+.session-track {
+ float:left;
+ margin: 10px 0;
+ color: $ColorTextTypical;
+ background-color: #242323;
+ border-radius: 6px;
+ min-height: 76px;
+ max-width: 210px;
+ position:relative;
+ @include border_box_sizing;
+
+ .name {
+ width: 100%;
+ margin-bottom: 6px;
+ @include labelFont;
+ }
+
+ .track-avatar {
+ float: left;
+ padding: 1px;
+ width: 44px;
+ height: 44px;
+ background-color: #ed3618;
+ -webkit-border-radius: 22px;
+ -moz-border-radius: 22px;
+ border-radius: 22px;
+
+ img {
+ width: 44px;
+ height: 44px;
+ -webkit-border-radius: 22px;
+ -moz-border-radius: 22px;
+ border-radius: 22px;
+ }
+ }
+
+ .track-instrument {
+ float: left;
+ padding: 1px;
+ margin-left: 5px;
+ }
+
+ table.vu {
+ float: left;
+
+ td {
+ border: 3px solid #242323;
+ }
+ }
+
+ .session-track-contents {
+ padding: 6px 6px 6px 10px;
+ }
+ .disabled-track-overlay {
+ width: 0;
+ height: 0;
+ position: absolute;
+ background-color:#555;
+ border-radius: 6px;
+ }
+
+ &.no-mixer, &.no-audio {
+ .disabled-track-overlay {
+ width: 100%;
+ height: 100%;
+ opacity:0.5;
+ }
+ }
+
+
+ .track-controls {
+ margin-top: 2px;
+ margin-left: 10px;
+ float:left
+ }
+
+ .track-buttons {
+ margin-top:22px;
+ padding:0 0 0 3px;
+ }
+
+ .track-icon-mute {
+ float:left;
+ position:relative;
+ top:0;
+ left:0;
+ }
+
+ .track-icon-pan {
+ float:left;
+ cursor: pointer;
+ width: 20px;
+ height: 20px;
+ background-image:url('/assets/content/icon_pan.png');
+ background-repeat:no-repeat;
+ text-align: center;
+ margin-left:10px;
+ //-webkit-transform: rotate(7deg); /* Chrome, Safari, Opera */
+ //transform: rotate(7deg);
+ }
+
+ .track-icon-equalizer {
+ float:left;
+ cursor: pointer;
+ width: 20px;
+ height: 20px;
+ background-image:url('/assets/content/icon_equalizer.png');
+ background-repeat:no-repeat;
+ text-align: center;
+ margin-left:7px;
+ }
+
+
+ // media overrides
+ &.backing-track, &.recorded-track, &.jam-track, &.metronome, &.recorded-category, &.jam-track-category {
+ width:210px;
+ table.vu {
+ float: right;
+ margin-top: 30px;
+ margin-right: 4px;
+ }
+ .track-controls {
+ float:right;
+ }
+ .track-buttons {
+ float:right;
+ }
+ .track-icon-pan {
+ float:right;
+ margin-right:20px;
+ }
+ .track-icon-mute{
+ float:right;
+ }
+ }
+
+ &.metronome {
+ .track-instrument {
+ float:left;
+ margin-left:0;
+ margin-right: 8px;
+ margin-top: -3px;
+ }
+ .track-controls {
+ margin-left:0;
+ }
+ }
+
+ &.recorded-track, &.jam-track, &.recorded-category, &.jam-track-category {
+ height:56px;
+ min-height:56px;
+ .track-buttons {
+ margin-top:2px;
+ }
+ .track-controls {
+ margin-left:0;
+ }
+ table.vu {
+ margin-top:10px;
+ }
+ .track-instrument {
+ float: left;
+ margin: -2px 7px 0 0;
+ }
+ }
+
+ &.recorded-category, &.jam-track-category {
+ height:auto !important;
+ }
+
+ &.jam-track-category {
+ .jam-track-header {
+ position:absolute;
+ @include labelFont;
+
+ }
+ .name {
+ width:auto;
+ margin-left:60px;
+ }
+ }
+}
+
+.react-holder {
+ &.SessionTrackVolumeHover, &.SessionSelfVolumeHover {
+ height:331px;
+ width:235px;
+
+ .session-track {
+ float:left;
+ background-color: #242323;
+ border-radius: 4px;
+ display: inline-block;
+ height: 300px;
+ margin-right: 14px;
+ position: relative;
+ width: 70px;
+ margin-top:19px;
+ margin-left:24px;
+ }
+
+ .track-icon-mute {
+ float:none;
+ position: absolute;
+ top: 246px;
+ left: 29px;
+ }
+
+ .track-gain {
+ position:absolute;
+ width:28px;
+ height:209px;
+ top:32px;
+ left:23px;
+ }
+
+ .fader {
+ height:209px;
+ }
+
+ .handle {
+ bottom:0%;
+ display:none;
+ }
+
+ .textual-help {
+ float:left;
+ width:100px;
+ }
+
+ p {
+ font-size:12px !important;
+ padding:0;
+ margin:16px 0 0 !important;
+ line-height:125% !important;
+ &:nth-child(1) {
+ margin-top: 19px !important;
+ }
+ }
+
+ .icheckbox_minimal {
+ position:absolute;
+ top: 271px;
+ left: 12px;
+ }
+
+ input {
+ position:absolute;
+ top: 271px;
+ left: 12px;
+ }
+
+ label {
+ @include labelFont;
+ position:absolute;
+ top:273px;
+ 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 !important;
+ }
+ }
+ }
+
+ .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;
+ padding:15px;
+ @include border_box_sizing;
+
+ .session-pan {
+ .textual-help {
+ float:left;
+ width:100px;
+ }
+ }
+
+ p {
+ font-size:12px !important;
+ padding:0 !important;
+ line-height:125% !important;
+ margin:0 !important;
+ }
+ .track-pan {
+ background-color: #242323;
+ border-radius: 4px;
+ display: inline-block;
+ height: 70px;
+ position: relative;
+ width: 300px;
+ margin-top:15px;
+ }
+ .fader {
+ position:absolute;
+ width:205px;
+ height:24px;
+ top:34px;
+ left:44px;
+ background-image: url('/assets/content/bkg_slider_gain_horiz_24.png');
+ }
+ .handle {
+ display:none;
+
+ img {
+ position:absolute;
+ left:-5px;
+ }
+ }
+ .left-label {
+ @include labelFont;
+ position:absolute;
+ left:13px;
+ top:40px;
+ }
+ .right-label {
+ @include labelFont;
+ position:absolute;
+ right:12px;
+ top:40px;
+ }
+ .floater {
+ width:20px;
+ text-align:center;
+ top:-22px;
+ left:-8px;
+ @include labelFont;
+ position:absolute;
+ }
+ }
+}
diff --git a/web/app/assets/stylesheets/client/session.css.scss b/web/app/assets/stylesheets/client/session.css.scss
index 213a4d726..f1d2cdc8f 100644
--- a/web/app/assets/stylesheets/client/session.css.scss
+++ b/web/app/assets/stylesheets/client/session.css.scss
@@ -1,6 +1,6 @@
@import "client/common";
-[layout-id="session"] {
+[layout-id="session_old"] {
.resync {
margin-left:15px;
diff --git a/web/app/assets/stylesheets/dialogs/inviteMusiciansDialog.css.scss b/web/app/assets/stylesheets/dialogs/inviteMusiciansDialog.css.scss
new file mode 100644
index 000000000..4f0041966
--- /dev/null
+++ b/web/app/assets/stylesheets/dialogs/inviteMusiciansDialog.css.scss
@@ -0,0 +1,18 @@
+#invite-musician-friends-dialog {
+ width:450px;
+
+ p.instructions {
+ margin-bottom:30px;
+ }
+
+ .actions {
+ text-align:center;
+ }
+
+ #btn-cancel-invites {
+
+ }
+ #btn-save-invites {
+
+ }
+}
\ No newline at end of file
diff --git a/web/app/assets/stylesheets/dialogs/sessionMasterMixDialog.css.scss b/web/app/assets/stylesheets/dialogs/sessionMasterMixDialog.css.scss
new file mode 100644
index 000000000..13420ad7b
--- /dev/null
+++ b/web/app/assets/stylesheets/dialogs/sessionMasterMixDialog.css.scss
@@ -0,0 +1,61 @@
+@import "client/common";
+
+#session-master-mix-dialog {
+ width:1100px;
+ height:466px;
+
+ #master-tracks {
+ position: absolute;
+ bottom: 50px;
+ top: 120px;
+ width: 100%;
+ padding-right: 20px;
+ @include border_box_sizing;
+ }
+ .dialog-inner {
+ padding: 10px 20px;
+ width:100%;
+
+ p.notice {
+ width:800px;
+ line-height:125%;
+ }
+
+ h2 {
+ font-size:24px;
+ }
+ }
+
+ .session-my-tracks, .session-other-tracks, .session-media-tracks, .session-category-controls {
+ @include border_box_sizing;
+ float: left;
+ width: 25%;
+ padding: 15px;
+ height: 100%;
+ margin-bottom: 15px;
+ color:$ColorTextTypical;
+ overflow:hidden;
+ position:relative;
+ }
+
+ .session-tracks-scroller {
+ overflow-x: hidden;
+ overflow-y: auto;
+ width: 100%;
+
+ padding: 0 15px 0 0;
+ @include border_box_sizing;
+ left: 0;
+ right: 0;
+ bottom:0;
+ position:absolute;
+ top: 40px;
+ }
+
+ .close-button {
+ position:absolute;
+ margin:0 0 0 -30px;
+ bottom:10px;
+ left: 50%;
+ }
+}
\ No newline at end of file
diff --git a/web/app/views/clients/_inviteMusicians.html.erb b/web/app/views/clients/_inviteMusicians.html.erb
index 24bdba3bf..36f6aeede 100644
--- a/web/app/views/clients/_inviteMusicians.html.erb
+++ b/web/app/views/clients/_inviteMusicians.html.erb
@@ -1,14 +1,23 @@
-
+
+
+ <%= image_tag "content/icon_email.png", {:height => 21, :width => 22, :class => 'content-icon'} %>
+
invite musicians
+
+
+
+ Start typing a name, or click the Choose Friends button to invite your JamKazam friends to join your session.
+
-
-
diff --git a/web/app/views/clients/_session.html.slim b/web/app/views/clients/_session.html.slim
index bf3c37af6..7c0d574ae 100644
--- a/web/app/views/clients/_session.html.slim
+++ b/web/app/views/clients/_session.html.slim
@@ -1,4 +1,4 @@
-#session-screen.screen.secondary[layout="screen" layout-id="session" layout-arg="id"]
+#session-screen.screen.secondary[layout="screen" layout-id="session_old" layout-arg="id"]
.content-head
.content-icon
= image_tag "shared/icon_session.png", {:height => 19, :width => 19}
diff --git a/web/app/views/clients/_session2.html.slim b/web/app/views/clients/_session2.html.slim
index 10522a150..738d44624 100644
--- a/web/app/views/clients/_session2.html.slim
+++ b/web/app/views/clients/_session2.html.slim
@@ -1,4 +1,4 @@
-#session-screen2.screen.secondary[layout="screen" layout-id="session2" layout-arg="id"]
+#session-screen2.screen.top-parent.secondary[layout="screen" layout-id="session" layout-arg="id"]
.content-head
.content-icon
= image_tag "shared/icon_session.png", {:height => 19, :width => 19}
diff --git a/web/app/views/clients/index.html.erb b/web/app/views/clients/index.html.erb
index 06670850e..3e9c8d1d2 100644
--- a/web/app/views/clients/index.html.erb
+++ b/web/app/views/clients/index.html.erb
@@ -329,6 +329,9 @@
var singlePlayerProfileGuardDialog = new JK.SinglePlayerProfileGuardDialog(JK.app);
singlePlayerProfileGuardDialog.initialize();
+ var sessionMasterMixDialog = new JK.SessionMasterMixDialog(JK.app);
+ sessionMasterMixDialog.initialize();
+
var signinDialog = new JK.SigninDialog(JK.app);
signinDialog.initialize();
JK.SigninPage(); // initialize signin helper
diff --git a/web/app/views/dialogs/_dialogs.html.haml b/web/app/views/dialogs/_dialogs.html.haml
index 8c1a2d817..42d5da153 100644
--- a/web/app/views/dialogs/_dialogs.html.haml
+++ b/web/app/views/dialogs/_dialogs.html.haml
@@ -37,4 +37,5 @@
= render 'dialogs/openBackingTrackDialog'
= render 'dialogs/loginRequiredDialog'
= render 'dialogs/jamtrackPaymentHistoryDialog'
-= render 'dialogs/singlePlayerProfileGuard'
\ No newline at end of file
+= render 'dialogs/singlePlayerProfileGuard'
+= render 'dialogs/sessionMasterMixDialog'
\ No newline at end of file
diff --git a/web/app/views/dialogs/_sessionMasterMixDialog.html.slim b/web/app/views/dialogs/_sessionMasterMixDialog.html.slim
new file mode 100644
index 000000000..f2ca1e4a9
--- /dev/null
+++ b/web/app/views/dialogs/_sessionMasterMixDialog.html.slim
@@ -0,0 +1,11 @@
+.dialog.dialog-overlay-sm.top-parent layout='dialog' layout-id='session-master-mix-dialog' id='session-master-mix-dialog'
+ .content-head
+ = image_tag "shared/icon_session.png", {:height => 19, :width => 19, :class => 'content-icon'}
+ h1 session master mix
+ .dialog-inner
+ p.notice
+ | The master mix is the audio used for session recordings and live broadcasts. Changes to the master mix are global.
+ | The master mix does not include voice chat or the metronome. Any user in the session may use the volume and pan controls
+ | below to make adjustments to the master mix.
+ = react_component 'SessionMasterMix', {}
+ a.button-orange.close-button layout-action="close" CLOSE