This commit is contained in:
Seth Call 2015-06-30 20:25:41 -05:00
parent 4be1117a02
commit bc0add9aa0
60 changed files with 1750 additions and 691 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 263 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 296 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1010 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -21,7 +21,10 @@ context.JK.ClientInit = class ClientInit
this.watchBroadcast()
checkBroadcast: () =>
broadcastActions.load.triggerPromise()
broadcastActions.load.triggerPromise().catch(() ->
false
)
watchBroadcast: () =>
if context.JK.currentUserId

View File

@ -0,0 +1,36 @@
$ = jQuery
context = window
context.JK ||= {}
MIX_MODES = context.JK.MIX_MODES
context.JK.SessionMasterMixDialog = class SessionMasterMixDialog
constructor: (@app) ->
@rest = context.JK.Rest()
@logger = context.JK.logger
@screen = null
@dialogId = 'session-master-mix-dialog'
@dialog = null
@closeBtn = null
initialize:() =>
dialogBindings =
'beforeShow' : @beforeShow
'afterShow' : @afterShow
'afterHide' : @afterHide
@dialog = $('[layout-id="' + @dialogId + '"]')
@app.bindDialog(@dialogId, dialogBindings)
@content = @dialog.find(".dialog-inner")
beforeShow:() =>
@logger.debug("session-master-mix-dlg: beforeShow")
context.jamClient.SetMixerMode(MIX_MODES.MASTER)
afterShow:() =>
@logger.debug("session-master-mix-dlg: afterShow")
afterHide:() =>
context.jamClient.SetMixerMode(MIX_MODES.PERSONAL)

View File

@ -10,4 +10,5 @@
//= require ./react-components/stores/SessionOtherTracksStore
//= require ./react-components/stores/SessionMediaTracksStore
//= require_directory ./react-components/stores
//= require_directory ./react-components/mixins
//= require_directory ./react-components

View File

@ -4,20 +4,29 @@ MixerActions = @MixerActions
@SessionBackingTrack = React.createClass({
mixins: [@MasterPersonalMixersMixin]
propTypes: {
mode: React.PropTypes.bool.isRequired
}
handleMute: (e) ->
e.preventDefault()
mixer = @mixer()
unless mixer?
logger.debug("ignoring mute because no media mixer")
return
muting = $(e.currentTarget).is('.enabled')
MixerActions.mute([this.props.mixers.mixer], muting)
MixerActions.mute([mixer], muting)
render: () ->
# today, all mixers are the same for a remote participant; so just grab the 1st
mixers = @props.mixers
muteMixer = mixers.muteMixer
vuMixer = mixers.vuMixer
mixers = @mixers()
muteMixer = mixers.mixer
muteMixerId = muteMixer?.id
classes = classNames({
@ -65,16 +74,16 @@ MixerActions = @MixerActions
$mute,
'SessionTrackVolumeHover',
() =>
{mixers:@props.mixers}
{mixers:@mixers()}
,
{width:235, positions:['right', 'left'], offsetParent:$root.closest('.screen')})
{width:235, positions:['right', 'left'], offsetParent:$root.closest('.top-parent')})
context.JK.interactReactBubble(
$pan,
'SessionTrackPanHover',
() =>
{mixers:@props.mixers}
{mixers:@mixers()}
,
{width:331, positions:['right', 'left'], offsetParent:$root.closest('.screen')})
{width:331, positions:['right', 'left'], offsetParent:$root.closest('.top-parent')})
})

View File

@ -0,0 +1,75 @@
context = window
@SessionChatMixer= React.createClass({
handleMute: (e) ->
e.preventDefault()
unless @props.mixers.mixer
logger.debug("ignoring mute; no mixer")
return
muting = $(e.currentTarget).is('.enabled')
MixerActions.mute([@props.mixers.mixer], muting)
render: () ->
muteMixer = @props.mixers.muteMixer
vuMixer = @props.mixers.vuMixer
muteMixerId = muteMixer?.id
classes = classNames({
'track-icon-mute': true
'enabled' : !muteMixer?.mute
'muted' : muteMixer?.mute
})
pan = if @props.mixers.mixer? then @props.mixers.mixer.pan else 0
panStyle = {
transform: "rotate(#{pan}deg)"
WebkitTransform: "rotate(#{pan}deg)"
}
`<div className="session-track chat-mixer">
<div className="disabled-track-overlay" />
<div className="session-track-contents">
<div className="name">Session Voice Chat Output</div>
<div className="track-instrument"><img height="45" src="/assets/content/icon_instrument_voice45.png" width="45" /></div>
<div className="track-controls">
<SessionTrackVU orientation="horizontal" lightCount={4} lightWidth={17} lightHeight={3} side="vul" mixers={this.props.mixers} />
<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>
<br className="clearall"/>
</div>
<br className="clearall"/>
</div>
</div>`
componentDidMount: () ->
$root = $(this.getDOMNode())
$mute = $root.find('.track-icon-mute')
$pan = $root.find('.track-icon-pan')
context.JK.interactReactBubble(
$mute,
'SessionTrackVolumeHover',
() =>
{mixers:@props.mixers}
,
{width:235, positions:['right', 'left'], offsetParent:$root.closest('.top-parent')})
context.JK.interactReactBubble(
$pan,
'SessionTrackPanHover',
() =>
{mixers:@props.mixers}
,
{width:331, positions:['right', 'left'], offsetParent:$root.closest('.top-parent')})
})

View File

@ -2,12 +2,21 @@ context = window
@SessionInviteMusiciansBtn = React.createClass({
mixins: [Reflux.listenTo(@AppStore,"onAppInit")]
onAppInit: (app) ->
@app = app
@inviteMusiciansUtil = new JK.InviteMusiciansUtil(@app)
@inviteMusiciansUtil.initialize(JK.FriendSelectorDialogInstance)
openInviteDialog : (e) ->
e.preventDefault()
#friendInput = inviteMusiciansUtil.inviteSessionUpdate('#update-session-invite-musicians', sessionId);
#inviteMusiciansUtil.loadFriends();
#$(friendInput).show();
friendInput = @inviteMusiciansUtil.inviteSessionUpdate('#update-session-invite-musicians', context.SessionStore.currentSessionId)
@inviteMusiciansUtil.loadFriends()
$(friendInput).show()
@app.layout.showDialog('select-invites')
render: () ->
`<a className="session-invite-musicians" onClick={this.openInviteDialog}>

View File

@ -4,20 +4,29 @@ MixerActions = @MixerActions
@SessionJamTrack = React.createClass({
mixins: [@MasterPersonalMixersMixin]
propTypes: {
mode: React.PropTypes.bool.isRequired
}
handleMute: (e) ->
e.preventDefault()
mixer = @mixer()
unless mixer?
logger.debug("ignoring mute because no media mixer")
return
muting = $(e.currentTarget).is('.enabled')
MixerActions.mute([this.props.mixers.mixer], muting)
MixerActions.mute([mixer], muting)
render: () ->
# today, all mixers are the same for a remote participant; so just grab the 1st
mixers = @props.mixers
muteMixer = mixers.muteMixer
vuMixer = mixers.vuMixer
mixers = @mixers()
muteMixer = mixers.mixer
muteMixerId = muteMixer?.id
classes = classNames({
@ -66,16 +75,16 @@ MixerActions = @MixerActions
$mute,
'SessionTrackVolumeHover',
() =>
{mixers:@props.mixers}
{mixers:@mixers()}
,
{width:235, positions:['right', 'left'], offsetParent:$root.closest('.screen')})
{width:235, positions:['right', 'left'], offsetParent:$root.closest('.top-parent')})
context.JK.interactReactBubble(
$pan,
'SessionTrackPanHover',
() =>
{mixers:@props.mixers}
{mixers:@mixers()}
,
{width:331, positions:['right', 'left'], offsetParent:$root.closest('.screen')})
{width:331, positions:['right', 'left'], offsetParent:$root.closest('.top-parent')})
})

View File

@ -0,0 +1,81 @@
context = window
MixerActions = @MixerActions
@SessionJamTrackCategory = React.createClass({
handleMute: (e) ->
e.preventDefault()
muting = $(e.currentTarget).is('.enabled')
MixerActions.mute([this.props.mixers.mixer], muting)
render: () ->
# today, all mixers are the same for a remote participant; so just grab the 1st
mixers = @props.mixers
muteMixer = mixers.muteMixer
vuMixer = mixers.vuMixer
muteMixerId = muteMixer?.id
classes = classNames({
'track-icon-mute': true
'enabled' : !muteMixer?.mute
'muted' : muteMixer?.mute
})
componentClasses = classNames({
"session-track" : true
"jam-track-category" : true
})
pan = mixers.mixer.pan
panStyle = {
transform: "rotate(#{pan}deg)"
WebkitTransform: "rotate(#{pan}deg)"
}
`<div className={componentClasses}>
<div className="session-track-contents">
<div className="jam-track-header">JamTrack:</div>
<div className="name">{this.props.jamTrackName}</div>
<div className="track-controls">
<SessionTrackVU orientation="horizontal" lightCount={4} lightWidth={17} lightHeight={3} side="vul" mixers={mixers} />
<div className="track-buttons">
<div className="track-icon-pan" style={panStyle}/>
<div className={classes} data-control="mute" data-mixer-id={muteMixerId} onClick={this.handleMute}/>
</div>
<br className="clearall"/>
</div>
<br className="clearall"/>
</div>
</div>`
componentDidMount: () ->
$root = $(this.getDOMNode())
$mute = $root.find('.track-icon-mute')
$pan = $root.find('.track-icon-pan')
context.JK.interactReactBubble(
$mute,
'SessionTrackVolumeHover',
() =>
{mixers:@props.mixers}
,
{width:235, positions:['right', 'left'], offsetParent:$root.closest('.top-parent')})
context.JK.interactReactBubble(
$pan,
'SessionTrackPanHover',
() =>
{mixers:@props.mixers}
,
{width:331, positions:['right', 'left'], offsetParent:$root.closest('.top-parent')})
})

View File

@ -0,0 +1,53 @@
context = window
rest = context.JK.Rest()
ReactCSSTransitionGroup = React.addons.CSSTransitionGroup
MIX_MODES = context.JK.MIX_MODES
@SessionMasterCategoryControls = React.createClass({
mixins: [Reflux.listenTo(@SessionMediaTracksStore,"onInputsChanged"), Reflux.listenTo(@AppStore,"onAppInit")]
onInputsChanged: (sessionMixers) ->
mixers = sessionMixers.mixers
inputGroupMixers = mixers.getAudioInputCategoryMixer(MIX_MODES.MASTER)
chatGroupMixers = mixers.getChatCategoryMixer(MIX_MODES.MASTER)
@setState({inputGroupMixers: inputGroupMixers, chatGroupMixers: chatGroupMixers})
render: () ->
categoryControls = []
if @state.inputGroupMixers?
input =
mixers: @state.inputGroupMixers
categoryControls.push(`<SessionMusicMixer key={input.mixers.mixer.id} {...input} />`)
if @state.chatGroupMixers?
input =
mixers: @state.chatGroupMixers
categoryControls.push(`<SessionChatMixer key={input.mixers.mixer.id} {...input} />`)
`<div className="session-category-controls">
<h2>master output</h2>
<div className="session-tracks-scroller">
{categoryControls}
</div>
</div>`
getInitialState:() ->
{inputGroupMixers: null, chatGroupMixers: null}
onAppInit: (app) ->
@app = app
})

View File

@ -0,0 +1,55 @@
context = window
rest = context.JK.Rest()
SessionActions = @SessionActions
ReactCSSTransitionGroup = React.addons.CSSTransitionGroup
MIX_MODES = context.JK.MIX_MODES
EVENTS = context.JK.EVENTS
ChannelGroupIds = context.JK.ChannelGroupIds
@SessionMasterMediaTracks = React.createClass({
mixins: [@SessionMediaTracksMixin, Reflux.listenTo(@SessionMediaTracksStore,"onInputsChanged"), Reflux.listenTo(@AppStore,"onAppInit")]
render: () ->
mediaTracks = []
if this.state.mediaSummary.mediaOpen
if this.state.mediaSummary.backingTrackOpen
for backingTrack in @state.backingTracks
backingTrack.mode = MIX_MODES.MASTER
mediaTracks.push(`<SessionBackingTrack key={backingTrack.track.id} {...backingTrack} />`)
else if this.state.mediaSummary.jamTrackOpen
mediaTracks.push(`<SessionJamTrackCategory key="JamTrackCategory" jamTrackName={this.state.jamTrackName} mixers={this.state.mediaCategoryMixer} mode={MIX_MODES.MASTER} />`)
for jamTrack in @state.jamTracks
jamTrack.mode = MIX_MODES.MASTER
mediaTracks.push(`<SessionJamTrack key={jamTrack.id} {...jamTrack} />`)
else if this.state.mediaSummary.recordingOpen
mediaTracks.push(`<SessionRecordedCategory key="RecordedCategory" recordingName={this.state.recordingName} mixers={this.state.mediaCategoryMixer} mode={MIX_MODES.MASTER} />`)
for recordedTrack in @state.recordedTracks
recordedTrack.mode = MIX_MODES.MASTER
mediaTracks.push(`<SessionRecordedTrack key={recordedTrack.track.id} {...recordedTrack} />`)
else if this.state.mediaSummary.metronomeOpen
@state.metronome.mode = MIX_MODES.MASTER
mediaTracks.push(`<SessionMetronome key={this.state.metronome.id} {...this.state.metronome} />`)
`<div className="session-media-tracks">
<h2>recorded audio</h2>
<div className="session-tracks-scroller">
{mediaTracks}
</div>
</div>`
getInitialState:() ->
{mediaSummary:{mediaOpen: false}, isRecording: false, backingTracks: [], jamTracks: [], recordedTracks: [], metronome: null}
onAppInit: (app) ->
@app = app
})

View File

@ -0,0 +1,13 @@
context = window
MIX_MODES = context.JK.MIX_MODES
@SessionMasterMix = React.createClass({
render: () ->
`<div id="master-tracks">
<SessionMasterMyTracks mode={MIX_MODES.MASTER} />
<SessionMasterOtherTracks mode={MIX_MODES.MASTER} />
<SessionMasterMediaTracks mode={MIX_MODES.MASTER} />
<SessionMasterCategoryControls />
</div>`
})

View File

@ -0,0 +1,40 @@
context = window
ReactCSSTransitionGroup = React.addons.CSSTransitionGroup
MIX_MODES = context.JK.MIX_MODES
logger = context.JK.logger
@SessionMasterMyTracks = React.createClass({
mixins: [@SessionMyTracksMixin, Reflux.listenTo(@SessionMyTracksStore,"onInputsChanged"), Reflux.listenTo(@AppStore,"onAppInit")]
render: () ->
content = null
tracks = []
if this.state.tracks.length > 0
for track in this.state.tracks
track.mode = MIX_MODES.MASTER
tracks.push(`<SessionMyTrack key={track.track.client_track_id} {...track} />`)
if @state.chat
@state.chat.mode = @props.mode
tracks.push(`<SessionMyChat key="chat" {...this.state.chat} />`)
else if this.state.session? && this.state.session.inSession()
logger.debug("no 'my inputs' for master mix")
`<div className="session-my-tracks">
<h2>my live tracks</h2>
<div className="session-tracks-scroller">
{content}
{tracks}
</div>
</div>`
getInitialState:() ->
{tracks:[], session: null}
onAppInit: (app) ->
@app = app
})

View File

@ -0,0 +1,93 @@
context = window
MixerActions = @MixerActions
@SessionMasterOtherTrack = React.createClass({
handleMute: (e) ->
e.preventDefault()
unless @props.mixers.mixer?
logger.debug("ignoring mute; no mixer")
return
muting = $(e.currentTarget).is('.enabled')
MixerActions.mute([@props.mixers.mixer], muting)
render: () ->
muteMixer = @props.mixers.muteMixer
vuMixer = @props.mixers.vuMixer
muteMixerId = muteMixer?.id
classes = classNames({
'track-icon-mute': true
'enabled' : !muteMixer?.mute
'muted' : muteMixer?.mute
})
pan = if @props.mixers.mixer? then @props.mixers.mixer.pan else 0
panStyle = {
transform: "rotate(#{pan}deg)"
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">
<div className="name">{this.props.name}</div>
<div className="track-avatar"><img src={this.props.photoUrl}/></div>
<div className="track-instrument"><img height="45" src={this.props.instrumentIcon} width="45" /></div>
<div className="track-controls">
<SessionTrackVU orientation="horizontal" lightCount={4} lightWidth={17} lightHeight={3} side="vul" mixers={this.props.mixers} />
<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>
<br className="clearall"/>
</div>
<br className="clearall"/>
</div>
</div>`
componentDidMount: () ->
$root = $(this.getDOMNode())
$mute = $root.find('.track-icon-mute')
$pan = $root.find('.track-icon-pan')
context.JK.interactReactBubble(
$mute,
'SessionTrackVolumeHover',
() =>
{mixers:this.props.mixers}
,
{width:235, positions:['right', 'left'], offsetParent:$root.closest('.top-parent')})
context.JK.interactReactBubble(
$pan,
'SessionTrackPanHover',
() =>
{mixers:this.props.mixers}
,
{width:331, positions:['right', 'left'], offsetParent:$root.closest('.top-parent')})
componentWillUpdate: (nextProps, nextState) ->
$root = $(this.getDOMNode())
$mute = $root.find('.track-icon-mute')
$pan = $root.find('.track-icon-pan')
# disable hover effects if there is no mixer
if nextProps.mixers.mixer?
$mute.off("click", false)
$pan.off("click", false)
else
$mute.on("click", false)
$pan.on("click", false)
})

View File

@ -0,0 +1,66 @@
context = window
ReactCSSTransitionGroup = React.addons.CSSTransitionGroup
@SessionMasterOtherTracks = React.createClass({
mixins: [Reflux.listenTo(@SessionOtherTracksStore,"onInputsChanged"), Reflux.listenTo(@AppStore,"onAppInit")]
onInputsChanged: (sessionMixers) ->
session = sessionMixers.session
mixers = sessionMixers.mixers
noAudioUsers = mixers.noAudioUsers
tracks = []
if session.inSession()
for participant in session.otherParticipants()
name = participant.user.name;
firstTrack = participant.tracks[0]
photoUrl = context.JK.resolveAvatarUrl(participant.user.photo_url)
for track in participant.tracks
mixerData = mixers.findMixerForTrack(participant.client_id, track, false, @props.mode)
instrumentIcon = context.JK.getInstrumentIcon45(firstTrack.instrument_id)
trackState = {
participant: participant,
track: track,
mixers: mixerData,
name: name,
instrumentIcon: instrumentIcon,
photoUrl: photoUrl,
hasMixer: mixerData.mixer? ,
noAudio: noAudioUsers[participant.client_id]
}
tracks.push(trackState)
# todo: sessionModel.setAudioEstablished
this.setState(tracks: tracks, session: session)
render: () ->
tracks = []
for track in @state.tracks
tracks.push(`<SessionMasterOtherTrack key={track.track.client_track_id} {...track} />`)
`<div className="session-other-tracks">
<h2>other live tracks</h2>
<div className="session-tracks-scroller">
{tracks}
</div>
</div>`
getInitialState:() ->
{tracks:[], session: null}
onAppInit: (app) ->
@app = app
})

View File

@ -8,40 +8,26 @@ ChannelGroupIds = context.JK.ChannelGroupIds
@SessionMediaTracks = React.createClass({
mixins: [Reflux.listenTo(@SessionMediaTracksStore,"onInputsChanged"), Reflux.listenTo(@AppStore,"onAppInit")]
mixins: [@SessionMediaTracksMixin, Reflux.listenTo(@SessionMediaTracksStore,"onInputsChanged"), Reflux.listenTo(@AppStore,"onAppInit")]
onInputsChanged: (sessionMixers) ->
session = sessionMixers.session
mixers = sessionMixers.mixers
backingTracks = mixers.backingTracks
jamTracks = mixers.jamTracks
recordedTracks = mixers.recordedTracks
metronome = mixers.metronome
state =
isRecording: session.isRecording
mediaSummary: mixers.mediaSummary
backingTracks: backingTracks
jamTracks: jamTracks
recordedTracks: recordedTracks
metronome: metronome
inputsChangedProcessed: (state) ->
if state.mediaSummary.mediaOpen
if !@state.childWindow?
logger.debug("OPENING CHILD WINDOW")
childWindow = window.open("/popups/media-controls", 'Media Controls', 'scrollbars=yes,toolbar=no,status=no,height=155,width=350')
childWindow.PopupProps = state
state.childWindow = childWindow
else
if @state.childWindow?
@state.childWindow.DontAutoCloseMedia = true
@state.childWindow.close()
state.childWindow = null
if !state.metronomeFlickerTimeout? # if the metronomeFlickerTimeout is active, we don't consider closing the childWindow
@checkCloseWindow()
state.childWindow = null
@setState(state)
checkCloseWindow: () ->
if @state.childWindow?
logger.debug("CLOSING CHILD WINDOW")
@state.childWindow.DontAutoCloseMedia = true
@state.childWindow.close()
closeAudio: (e) ->
@ -252,16 +238,22 @@ ChannelGroupIds = context.JK.ChannelGroupIds
</a>`
for backingTrack in @state.backingTracks
mediaTracks.push(`<SessionBackingTrack key={backingTrack.track.id} {...backingTrack} />`)
for jamTrack in @state.jamTracks
mediaTracks.push(`<SessionJamTrack key={jamTrack.id} {...jamTrack} />`)
for recordedTrack in @state.recordedTracks
mediaTracks.push(`<SessionRecordedTrack key={recordedTrack.track.id} {...recordedTrack} />`)
if @state.metronome
if this.state.mediaSummary.backingTrackOpen
for backingTrack in @state.backingTracks
backingTrack.mode = MIX_MODES.PERSONAL
mediaTracks.push(`<SessionBackingTrack key={backingTrack.track.id} {...backingTrack} />`)
else if this.state.mediaSummary.jamTrackOpen
mediaTracks.push(`<SessionJamTrackCategory key="JamTrackCategory" jamTrackName={this.state.jamTrackName} mixers={this.state.mediaCategoryMixer} mode={MIX_MODES.PERSONAL} />`)
for jamTrack in @state.jamTracks
jamTrack.mode = MIX_MODES.PERSONAL
mediaTracks.push(`<SessionJamTrack key={jamTrack.id} {...jamTrack} />`)
else if this.state.mediaSummary.recordingOpen
mediaTracks.push(`<SessionRecordedCategory key="RecordedCategory" recordingName={this.state.recordingName} mixers={this.state.mediaCategoryMixer} mode={MIX_MODES.PERSONAL} />`)
for recordedTrack in @state.recordedTracks
recordedTrack.mode = MIX_MODES.PERSONAL
mediaTracks.push(`<SessionRecordedTrack key={recordedTrack.track.id} {...recordedTrack} />`)
else if this.state.mediaSummary.metronomeOpen
@state.metronome.mode = MIX_MODES.PERSONAL
mediaTracks.push(`<SessionMetronome key={this.state.metronome.id} {...this.state.metronome} />`)
contents = closeOptions
@ -311,6 +303,7 @@ ChannelGroupIds = context.JK.ChannelGroupIds
# kick off the download JamTrack process
@state.downloadJamTrack.init()
@checkCloseWindow() if !@state.mediaSummary.mediaOpen && !@state.metronomeFlickerTimeout?
})

View File

@ -68,7 +68,7 @@ MixerActions = @MixerActions
() =>
{mixers:@props.mixers}
,
{width:235, positions:['right', 'left'], offsetParent:$root.closest('.screen')})
{width:235, positions:['right', 'left'], offsetParent:$root.closest('.top-parent')})
context.JK.interactReactBubble(
$pan,
@ -76,6 +76,6 @@ MixerActions = @MixerActions
() =>
{mixers:@props.mixers}
,
{width:331, positions:['right', 'left'], offsetParent:$root.closest('.screen')})
{width:331, positions:['right', 'left'], offsetParent:$root.closest('.top-parent')})
})

View File

@ -2,8 +2,12 @@ context = window
@SessionMixerBtn = React.createClass({
openDialog: (e) ->
e.preventDefault()
context.JK.app.layout.showDialog('session-master-mix-dialog')
render: () ->
`<a className="session-mixer button-grey left">
`<a className="session-mixer button-grey left" onClick={this.openDialog}>
<img src="/assets/content/icon_settings_sm.png" align="texttop" height="12" width="12"/>
MIXER
</a>`

View File

@ -0,0 +1,75 @@
context = window
@SessionMusicMixer= React.createClass({
handleMute: (e) ->
e.preventDefault()
unless @props.mixers.mixer
logger.debug("ignoring mute; no mixer")
return
muting = $(e.currentTarget).is('.enabled')
MixerActions.mute([@props.mixers.mixer], muting)
render: () ->
muteMixer = @props.mixers.muteMixer
vuMixer = @props.mixers.vuMixer
muteMixerId = muteMixer?.id
classes = classNames({
'track-icon-mute': true
'enabled' : !muteMixer?.mute
'muted' : muteMixer?.mute
})
pan = if @props.mixers.mixer? then @props.mixers.mixer.pan else 0
panStyle = {
transform: "rotate(#{pan}deg)"
WebkitTransform: "rotate(#{pan}deg)"
}
`<div className="session-track music-mixer">
<div className="disabled-track-overlay" />
<div className="session-track-contents">
<div className="name">Session Music Output</div>
<div className="track-instrument"><img height="45" src="/assets/content/icon_instrument_voice45.png" width="45" /></div>
<div className="track-controls">
<SessionTrackVU orientation="horizontal" lightCount={4} lightWidth={17} lightHeight={3} side="vul" mixers={this.props.mixers} />
<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>
<br className="clearall"/>
</div>
<br className="clearall"/>
</div>
</div>`
componentDidMount: () ->
$root = $(this.getDOMNode())
$mute = $root.find('.track-icon-mute')
$pan = $root.find('.track-icon-pan')
context.JK.interactReactBubble(
$mute,
'SessionTrackVolumeHover',
() =>
{mixers:@props.mixers}
,
{width:235, positions:['right', 'left'], offsetParent:$root.closest('.top-parent')})
context.JK.interactReactBubble(
$pan,
'SessionTrackPanHover',
() =>
{mixers:@props.mixers}
,
{width:331, positions:['right', 'left'], offsetParent:$root.closest('.top-parent')})
})

View File

@ -0,0 +1,69 @@
context = window
MixerActions = @MixerActions
@SessionMyChat = React.createClass({
mixins: [@MasterPersonalMixersMixin]
handleMute: (e) ->
e.preventDefault()
mixers = @mixers()
unless mixers.mixer
logger.debug("ignoring mute; no mixer")
return
muting = $(e.currentTarget).is('.enabled')
MixerActions.mute([mixers.mixer, mixers.oppositeMixer], muting)
render: () ->
mixers = @mixers()
muteMixer = mixers.muteMixer
vuMixer = mixers.vuMixer
muteMixerId = muteMixer?.id
classes = classNames({
'track-icon-mute': true
'enabled' : !muteMixer?.mute
'muted' : muteMixer?.mute
})
# <div className="track-icon-equalizer" />
`<div className="session-track my-track">
<div className="disabled-track-overlay" />
<div className="session-track-contents">
<div className="name">{this.props.name}</div>
<div className="track-avatar"><img src={this.props.photoUrl}/></div>
<div className="track-instrument"><img height="45" src='/assets/content/icon_instrument_chat45.png' width="45" /></div>
<div className="track-controls">
<SessionTrackVU orientation="horizontal" lightCount={4} lightWidth={17} lightHeight={3} side="vul" mixers={mixers} />
<div className="track-buttons">
<div className={classes} data-control="mute" data-mixer-id={muteMixerId} onClick={this.handleMute}/>
</div>
<br className="clearall"/>
</div>
<br className="clearall"/>
</div>
</div>`
componentDidMount: () ->
$root = $(this.getDOMNode())
$mute = $root.find('.track-icon-mute')
context.JK.interactReactBubble(
$mute,
'SessionTrackVolumeHover',
() =>
{mixers:@mixers()}
,
{width:235, positions:['right', 'left'], offsetParent:$root.closest('.top-parent')})
})

View File

@ -70,17 +70,17 @@ MixerActions = @MixerActions
$mute,
'SessionTrackVolumeHover',
() =>
{mixers:this.props.mixers, mixerFinder: this.props.mixerFinder}
{mixers:this.props.mixers}
,
{width:235, positions:['right', 'left'], offsetParent:$root.closest('.screen')})
{width:235, positions:['right', 'left'], offsetParent:$root.closest('.top-parent')})
context.JK.interactReactBubble(
$pan,
'SessionTrackPanHover',
() =>
{mixers:this.props.mixers, mixerFinder: this.props.mixerFinder}
{mixers:this.props.mixers}
,
{width:331, positions:['right', 'left'], offsetParent:$root.closest('.screen')})
{width:331, positions:['right', 'left'], offsetParent:$root.closest('.top-parent')})
componentWillUpdate: (nextProps, nextState) ->
$root = $(this.getDOMNode())

View File

@ -1,51 +1,27 @@
context = window
MIX_MODES = context.JK.MIX_MODES
ReactCSSTransitionGroup = React.addons.CSSTransitionGroup;
@SessionMyTracks = React.createClass({
mixins: [Reflux.listenTo(@SessionMyTracksStore,"onInputsChanged"), Reflux.listenTo(@AppStore,"onAppInit")]
onInputsChanged: (sessionMixers) ->
session = sessionMixers.session
mixers = sessionMixers.mixers
tracks = []
if session.inSession()
participant = session.getParticipant(@app.clientId)
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)
# todo: sessionModel.setAudioEstablished
instrumentIcon = context.JK.getInstrumentIcon45(track.instrument_id);
photoUrl = context.JK.resolveAvatarUrl(participant.user.photo_url);
tracks.push({track: track, mixerFinder: mixerFinder, mixers: mixerData, name: name, instrumentIcon: instrumentIcon, photoUrl: photoUrl, clientId: participant.client_id})
# TODO: also deal with chat
else
logger.debug("SessionMyTracks: unable to find participant")
this.setState(tracks: tracks, session:session)
mixins: [@SessionMyTracksMixin, Reflux.listenTo(@SessionMyTracksStore,"onInputsChanged"), Reflux.listenTo(@AppStore,"onAppInit")]
render: () ->
content = null
tracks = []
if this.state.tracks.length > 0
for track in this.state.tracks
if @state.tracks.length > 0
for track in @state.tracks
track.mode = MIX_MODES.PERSONAL
tracks.push(`<SessionMyTrack key={track.track.client_track_id} {...track} />`)
else if this.state.session? && this.state.session.inSession()
if @state.chat
@state.chat.mode = @props.mode
tracks.push(`<SessionMyChat key="chat" {...this.state.chat} />`)
else if @state.session? && @state.session.inSession()
content = `<div className="session-mytracks-notracks">
<p className="notice">
You have not set up any inputs for your instrument or vocals.
@ -67,7 +43,7 @@ ReactCSSTransitionGroup = React.addons.CSSTransitionGroup;
</div>`
getInitialState:() ->
{tracks:[], session: null}
{tracks:[], session: null, chat:null}
onAppInit: (app) ->
@app = app

View File

@ -81,8 +81,7 @@ MixerActions = @MixerActions
'SessionTrackVolumeHover',
() =>
mixers = if this.props.tracks.length > 0 then this.props.tracks[0].mixers else {}
mixerFinder = if this.props.tracks.length > 0 then this.props.tracks[0].mixerFinder else null
{mixers:mixers, mixerFinder: mixerFinder}
{mixers:mixers}
,
{width:235, positions:['right', 'left'], offsetParent:$root.closest('.screen')})
@ -91,8 +90,7 @@ MixerActions = @MixerActions
'SessionTrackPanHover',
() =>
mixers = if this.props.tracks.length > 0 then this.props.tracks[0].mixers else {}
mixerFinder = if this.props.tracks.length > 0 then this.props.tracks[0].mixerFinder else null
{mixers:mixers, mixerFinder: mixerFinder}
{mixers:mixers}
,
{width:331, positions:['right', 'left'], offsetParent:$root.closest('.screen')})

View File

@ -6,7 +6,6 @@ ReactCSSTransitionGroup = React.addons.CSSTransitionGroup
mixins: [Reflux.listenTo(@SessionOtherTracksStore,"onInputsChanged"), Reflux.listenTo(@AppStore,"onAppInit")]
onInputsChanged: (sessionMixers) ->
session = sessionMixers.session
mixers = sessionMixers.mixers
noAudioUsers = mixers.noAudioUsers
@ -25,23 +24,31 @@ ReactCSSTransitionGroup = React.addons.CSSTransitionGroup
for track in participant.tracks
# try to find mixer info for this track
mixerFinder = [participant.client_id, track, false] # so that other callers can re-find their mixer data
mixerFinder = [participant.client_id, track, false] # so that other callers can re-find their mixer data
mixerData = mixers.findMixerForTrack(participant.client_id, track, false)
mixerData = mixers.findMixerForTrack(participant.client_id, track, false, @props.mode)
if mixerData.mixer?
hasMixer = true
tracks.push(track: track, mixers: mixerData, mixerFinder: mixerFinder)
# todo: sessionModel.setAudioEstablished
# todo: sessionModel.setAudioEstablished
instrumentIcon = context.JK.getInstrumentIcon45(firstTrack.instrument_id)
photoUrl = context.JK.resolveAvatarUrl(participant.user.photo_url)
participantState = {participant:participant, tracks: tracks, name: name, instrumentIcon: instrumentIcon, photoUrl: photoUrl, hasMixer: hasMixer, noAudio: noAudioUsers[participant.client_id]}
participantState = {
participant: participant,
tracks: tracks,
name: name,
instrumentIcon: instrumentIcon,
photoUrl: photoUrl,
hasMixer: hasMixer,
noAudio: noAudioUsers[participant.client_id]
}
participants.push(participantState)
this.setState(participants:participants, session:session)
this.setState(participants: participants, session: session)
render: () ->

View File

@ -0,0 +1,84 @@
context = window
MixerActions = @MixerActions
@SessionRecordedCategory = React.createClass({
propTypes: {
mode: React.PropTypes.bool.isRequired
}
handleMute: (e) ->
e.preventDefault()
muting = $(e.currentTarget).is('.enabled')
MixerActions.mute([this.props.mixers.mixer], muting)
render: () ->
# today, all mixers are the same for a remote participant; so just grab the 1st
mixers = @props.mixers
muteMixer = mixers.muteMixer
vuMixer = mixers.vuMixer
muteMixerId = muteMixer?.id
classes = classNames({
'track-icon-mute': true
'enabled' : !muteMixer?.mute
'muted' : muteMixer?.mute
})
componentClasses = classNames({
"session-track" : true
"recorded-category" : true
})
pan = mixers.mixer.pan
panStyle = {
transform: "rotate(#{pan}deg)"
WebkitTransform: "rotate(#{pan}deg)"
}
`<div className={componentClasses}>
<div className="session-track-contents">
<div className="name">{this.props.recordingName}</div>
<div className="track-controls">
<SessionTrackVU orientation="horizontal" lightCount={4} lightWidth={17} lightHeight={3} side="vul" mixers={mixers} />
<div className="track-buttons">
<div className="track-icon-pan" style={panStyle}/>
<div className={classes} data-control="mute" data-mixer-id={muteMixerId} onClick={this.handleMute}/>
</div>
<br className="clearall"/>
</div>
<br className="clearall"/>
</div>
</div>`
componentDidMount: () ->
$root = $(this.getDOMNode())
$mute = $root.find('.track-icon-mute')
$pan = $root.find('.track-icon-pan')
context.JK.interactReactBubble(
$mute,
'SessionTrackVolumeHover',
() =>
{mixers:@props.mixers}
,
{width:235, positions:['right', 'left'], offsetParent:$root.closest('.top-parent')})
context.JK.interactReactBubble(
$pan,
'SessionTrackPanHover',
() =>
{mixers:@props.mixers}
,
{width:331, positions:['right', 'left'], offsetParent:$root.closest('.top-parent')})
})

View File

@ -4,20 +4,29 @@ MixerActions = @MixerActions
@SessionRecordedTrack = React.createClass({
mixins: [@MasterPersonalMixersMixin]
propTypes: {
mode: React.PropTypes.bool.isRequired
}
handleMute: (e) ->
e.preventDefault()
mixer = @mixer()
unless mixer?
logger.debug("ignoring mute because no media mixer")
return
muting = $(e.currentTarget).is('.enabled')
MixerActions.mute([this.props.mixers.mixer], muting)
MixerActions.mute([mixer], muting)
render: () ->
# today, all mixers are the same for a remote participant; so just grab the 1st
mixers = @props.mixers
muteMixer = mixers.muteMixer
vuMixer = mixers.vuMixer
mixers = @mixers()
muteMixer = mixers.mixer
muteMixerId = muteMixer?.id
classes = classNames({
@ -57,7 +66,6 @@ MixerActions = @MixerActions
componentDidMount: () ->
$root = $(this.getDOMNode())
$mute = $root.find('.track-icon-mute')
$pan = $root.find('.track-icon-pan')
@ -66,16 +74,16 @@ MixerActions = @MixerActions
$mute,
'SessionTrackVolumeHover',
() =>
{mixers:@props.mixers}
{mixers:@mixers()}
,
{width:235, positions:['right', 'left'], offsetParent:$root.closest('.screen')})
{width:235, positions:['right', 'left'], offsetParent:$root.closest('.top-parent')})
context.JK.interactReactBubble(
$pan,
'SessionTrackPanHover',
() =>
{mixers:@props.mixers}
{mixers:@mixers()}
,
{width:331, positions:['right', 'left'], offsetParent:$root.closest('.screen')})
{width:331, positions:['right', 'left'], offsetParent:$root.closest('.top-parent')})
})

View File

@ -1,4 +1,5 @@
context = window
MIX_MODES = context.JK.MIX_MODES
SessionActions = @SessionActions
@ -19,9 +20,9 @@ SessionActions = @SessionActions
<SessionLeaveBtn />
</div>
<div className="tracks" id="new-tracks">
<SessionMyTracks />
<SessionOtherTracks />
<SessionMediaTracks />
<SessionMyTracks mode={MIX_MODES.PERSONAL} />
<SessionOtherTracks mode={MIX_MODES.PERSONAL} />
<SessionMediaTracks mode={MIX_MODES.PERSONAL} />
<SessionNotifications />
</div>
</div>`
@ -73,5 +74,5 @@ SessionActions = @SessionActions
'beforeDisconnect' : @beforeDisconnect,
};
@app.bindScreen('session2', screenBindings);
@app.bindScreen('session', screenBindings);
})

View File

@ -13,7 +13,7 @@ MixerActions = @MixerActions
inputGroupMixers = mixers.getAudioInputCategoryMixer(MIX_MODES.PERSONAL)
chatGroupMixers = mixers.getChatCategoryMixer( MIX_MODES.PERSONAL)
this.setState({inputGroupMixers: inputGroupMixers, chatGroupMixers: chatGroupMixers})
@setState({inputGroupMixers: inputGroupMixers, chatGroupMixers: chatGroupMixers})
getInitialState: () ->
{inputGroupMixers: @props.inputGroupMixers, chatGroupMixers: @props.chatGroupMixers}

View File

@ -5,20 +5,22 @@ logger = context.JK.logger
getInitialState: () ->
{
mixers: this.props.mixers,
behaviors: this.props.behaviors || {}
mixers: @props.mixers,
behaviors: @props.behaviors || {}
}
faderChanged: (e, data) ->
$target = $(this)
groupId = $target.data('groupId')
mixerIds = [this.state.mixers.mixer.id]
mixers = [@state.mixers.mixer]
MixerActions.faderChanged(data, mixerIds, groupId)
MixerActions.faderChanged(data, mixers, groupId)
render: () ->
mixerId = this.state.mixers?.mixer?.id
`<div className="track-gain">
<div className="fader vertical" data-control="fader" data-fader-id={this.state.mixers.mixer.id} data-orientation="vertical">
<div className="fader vertical" data-control="fader" data-fader-id={mixerId} data-orientation="vertical">
<div className="handle" data-control="fader-handle">
<img src="/assets/content/slider_gain_vertical.png" width="28" height="11"/>
</div>
@ -30,20 +32,20 @@ logger = context.JK.logger
if !$root.is('.track-gain')
logger.error("unknown root node")
$fader = $root.attr('data-mixer-id', this.state.mixers.mixer.id).data('groupId', this.state.mixers.mixer.groupId).data('mixer', this.state.mixers.mixer).data('opposite-mixer', this.state.mixers.oppositeMixer)
$fader = $root.attr('data-mixer-id', @state.mixers.mixer.id).data('groupId', @state.mixers.mixer.groupId).data('mixer', @state.mixers.mixer).data('opposite-mixer', @state.mixers.oppositeMixer)
if this.state.behaviors.mediaControlsDisabled
$fader.data('media-controls-disabled', true).data('media-track-opener', this.state.behaviors.mediaTrackOpener) # this we be applied later to the fader handle $element
if @state.behaviors.mediaControlsDisabled
$fader.data('media-controls-disabled', true).data('media-track-opener', @state.behaviors.mediaTrackOpener) # this we be applied later to the fader handle $element
$fader.data('showHelpAboutMediaMixers', this.state.behaviors.showHelpAboutMediaMixers)
$fader.data('showHelpAboutMediaMixers', @state.behaviors.showHelpAboutMediaMixers)
context.JK.FaderHelpers.renderFader2($fader, {faderType: 'vertical'});
# Initialize gain position
MixerActions.initGain(this.state.mixers.mixer)
MixerActions.initGain(@state.mixers.mixer)
# watch for fader change events
$fader.on('fader_change', this.faderChanged);
$fader.on('fader_change', @faderChanged);
})

View File

@ -12,15 +12,18 @@ logger = context.JK.logger
panChanged: (e, data) ->
$target = $(this)
groupId = $target.data('groupId')
mixerIds = [this.state.mixers.mixer.id]
mixers = [@state.mixers.mixer]
MixerActions.panChanged(data, mixerIds, groupId)
MixerActions.panChanged(data, mixers, groupId)
render: () ->
mixerId = this.state.mixers?.mixer?.id
`<div className="track-pan">
<div className="left-label">Left</div>
<div className="right-label">Right</div>
<div className="fader horizontal" data-control="fader" data-fader-id={this.state.mixers.mixer.id} data-orientation="horizontal">
<div className="fader horizontal" data-control="fader" data-fader-id={mixerId} data-orientation="horizontal">
<div className="handle" data-control="fader-handle">
<div className="floater"></div>
<img src="/assets/content/slider_gain_horiz.png" width="11" height="28"/>

View File

@ -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: () ->
`<div className="track-pan-hover">
<div className="textual-help">
<p>
@ -34,4 +32,12 @@ MixerActions = @MixerActions
</div>
</div>`
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
})

View File

@ -1,4 +1,5 @@
context = window
ptrCount = 0
@SessionTrackVU = React.createClass({
@ -44,28 +45,27 @@ context = window
</table>`
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)
})

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -0,0 +1,6 @@
context = window
@SessionOtherTracksMixin = {
}

View File

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

View File

@ -925,7 +925,7 @@ NotificationActions = @NotificationActions
@currentSession = sessionData
console.log("SESSION CHANGED", sessionData)
#logger.debug("session changed")
@issueChange()

View File

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

View File

@ -3286,7 +3286,7 @@
'beforeLeave' : beforeLeave,
'beforeDisconnect' : beforeDisconnect,
};
app.bindScreen('session', screenBindings);
//app.bindScreen('session', screenBindings);
$recordingManagerViewer = $('#recording-manager-viewer');
$screen = $('#session-screen');

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,6 +1,6 @@
@import "client/common";
[layout-id="session"] {
[layout-id="session_old"] {
.resync {
margin-left:15px;

View File

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

View File

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

View File

@ -1,14 +1,23 @@
<!-- Session Update Invite Musicians Dialog -->
<div class="dialog invitemusicians-overlay" layout="dialog" layout-id="select-invites" style="min-height:180px;">
<div id="invite-musician-friends-dialog" class="dialog invitemusicians-overlay" layout="dialog" layout-id="select-invites" style="min-height:180px;">
<div class="content-head">
<%= image_tag "content/icon_email.png", {:height => 21, :width => 22, :class => 'content-icon'} %>
<h1>invite musicians</h1>
</div>
<div class="dialog-inner">
<p class="instructions">
Start typing a name, or click the Choose Friends button to invite your JamKazam friends to join your session.
</p>
<div class="invitemusicians-inner" id="update-session-invite-musicians">
</div>
<br clear="all" />
<div class="right">
<a id="btn-save-invites" layout-action="close" class="button-orange">INVITE</a>
</div>
<div class="right">
<a id="btn-cancel-invites" layout-action="close" class="button-grey">CANCEL</a>&nbsp;
<div class="actions">
<a id="btn-cancel-invites" layout-action="close" class="button-grey">CANCEL</a>
<a id="btn-save-invites" layout-action="close" class="button-orange">INVITE</a>
</div>
</div>
</div>
<!-- invite musician friend selector template -->

View File

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

View File

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

View File

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

View File

@ -37,4 +37,5 @@
= render 'dialogs/openBackingTrackDialog'
= render 'dialogs/loginRequiredDialog'
= render 'dialogs/jamtrackPaymentHistoryDialog'
= render 'dialogs/singlePlayerProfileGuard'
= render 'dialogs/singlePlayerProfileGuard'
= render 'dialogs/sessionMasterMixDialog'

View File

@ -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.&nbsp;
| The master mix does not include voice chat or the metronome. Any user in the session may use the volume and pan controls&nbsp;
| below to make adjustments to the master mix.
= react_component 'SessionMasterMix', {}
a.button-orange.close-button layout-action="close" CLOSE