From bc0add9aa02f070a79d17ac469503b22475b5e61 Mon Sep 17 00:00:00 2001 From: Seth Call Date: Tue, 30 Jun 2015 20:25:41 -0500 Subject: [PATCH] * wip --- .../content/bkg_slider_gain_horiz_24.png | Bin 0 -> 263 bytes web/app/assets/images/content/icon_email.png | Bin 0 -> 296 bytes .../assets/images/content/icon_equalizer.png | Bin 0 -> 1010 bytes .../images/content/icon_instrument_chat45.png | Bin 0 -> 2296 bytes web/app/assets/images/content/icon_pan.png | Bin 0 -> 1125 bytes .../assets/javascripts/client_init.js.coffee | 5 +- .../dialog/sessionMasterMixDialog.js.coffee | 36 ++ .../assets/javascripts/react-components.js | 1 + .../SessionBackingTrack.js.jsx.coffee | 29 +- .../SessionChatMixer.js.jsx.coffee | 75 ++++ .../SessionInviteMusiciansBtn.js.jsx.coffee | 15 +- .../SessionJamTrack.js.jsx.coffee | 29 +- .../SessionJamTrackCategory.js.jsx.coffee | 81 ++++ ...essionMasterCategoryControls.js.jsx.coffee | 53 +++ .../SessionMasterMediaTracks.js.jsx.coffee | 55 +++ .../SessionMasterMix.js.jsx.coffee | 13 + .../SessionMasterMyTracks.js.jsx.coffee | 40 ++ .../SessionMasterOtherTrack.js.jsx.coffee | 93 ++++ .../SessionMasterOtherTracks.js.jsx.coffee | 66 +++ .../SessionMediaTracks.js.jsx.coffee | 63 ++- .../SessionMetronome.js.jsx.coffee | 4 +- .../SessionMixerBtn.js.jsx.coffee | 6 +- .../SessionMusicMixer.js.jsx.coffee | 75 ++++ .../SessionMyChat.js.jsx.coffee | 69 +++ .../SessionMyTrack.js.jsx.coffee | 8 +- .../SessionMyTracks.js.jsx.coffee | 48 +- .../SessionOtherTrack.js.jsx.coffee | 6 +- .../SessionOtherTracks.js.jsx.coffee | 19 +- .../SessionRecordedCategory.js.jsx.coffee | 84 ++++ .../SessionRecordedTrack.js.jsx.coffee | 30 +- .../SessionScreen.js.jsx.coffee | 9 +- .../SessionSelfVolumeHover.js.jsx.coffee | 2 +- .../SessionTrackGain.js.jsx.coffee | 24 +- .../SessionTrackPan.js.jsx.coffee | 9 +- .../SessionTrackPanHover.js.jsx.coffee | 12 +- .../SessionTrackVU.js.jsx.coffee | 18 +- .../SessionTrackVolumeHover.js.jsx.coffee | 31 +- .../helpers/MixerHelper.js.coffee | 141 ++++-- .../MasterPersonalMixersMixin.js.coffee | 17 + .../mixins/SessionMediaTracksMixin.js.coffee | 51 +++ .../mixins/SessionMyTracksMixin.js.coffee | 45 ++ .../mixins/SessionOtherTracksMixin.js.coffee | 6 + .../stores/MixerStore.js.coffee | 30 +- .../stores/SessionStore.js.coffee | 2 +- .../javascripts/scheduled_session.js.erb | 2 +- web/app/assets/javascripts/session.js | 2 +- web/app/assets/javascripts/utils.js | 19 +- web/app/assets/javascripts/vuHelpers.js | 79 +--- .../react-components/MediaControls.scss.scss | 2 +- .../react-components/SessionScreen.css.scss | 421 +----------------- .../react-components/SessionTrack.css.scss | 393 ++++++++++++++++ .../stylesheets/client/session.css.scss | 2 +- .../dialogs/inviteMusiciansDialog.css.scss | 18 + .../dialogs/sessionMasterMixDialog.css.scss | 61 +++ .../views/clients/_inviteMusicians.html.erb | 21 +- web/app/views/clients/_session.html.slim | 2 +- web/app/views/clients/_session2.html.slim | 2 +- web/app/views/clients/index.html.erb | 3 + web/app/views/dialogs/_dialogs.html.haml | 3 +- .../dialogs/_sessionMasterMixDialog.html.slim | 11 + 60 files changed, 1750 insertions(+), 691 deletions(-) create mode 100644 web/app/assets/images/content/bkg_slider_gain_horiz_24.png create mode 100644 web/app/assets/images/content/icon_email.png create mode 100644 web/app/assets/images/content/icon_equalizer.png create mode 100644 web/app/assets/images/content/icon_instrument_chat45.png create mode 100644 web/app/assets/images/content/icon_pan.png create mode 100644 web/app/assets/javascripts/dialog/sessionMasterMixDialog.js.coffee create mode 100644 web/app/assets/javascripts/react-components/SessionChatMixer.js.jsx.coffee create mode 100644 web/app/assets/javascripts/react-components/SessionJamTrackCategory.js.jsx.coffee create mode 100644 web/app/assets/javascripts/react-components/SessionMasterCategoryControls.js.jsx.coffee create mode 100644 web/app/assets/javascripts/react-components/SessionMasterMediaTracks.js.jsx.coffee create mode 100644 web/app/assets/javascripts/react-components/SessionMasterMix.js.jsx.coffee create mode 100644 web/app/assets/javascripts/react-components/SessionMasterMyTracks.js.jsx.coffee create mode 100644 web/app/assets/javascripts/react-components/SessionMasterOtherTrack.js.jsx.coffee create mode 100644 web/app/assets/javascripts/react-components/SessionMasterOtherTracks.js.jsx.coffee create mode 100644 web/app/assets/javascripts/react-components/SessionMusicMixer.js.jsx.coffee create mode 100644 web/app/assets/javascripts/react-components/SessionMyChat.js.jsx.coffee create mode 100644 web/app/assets/javascripts/react-components/SessionRecordedCategory.js.jsx.coffee create mode 100644 web/app/assets/javascripts/react-components/mixins/MasterPersonalMixersMixin.js.coffee create mode 100644 web/app/assets/javascripts/react-components/mixins/SessionMediaTracksMixin.js.coffee create mode 100644 web/app/assets/javascripts/react-components/mixins/SessionMyTracksMixin.js.coffee create mode 100644 web/app/assets/javascripts/react-components/mixins/SessionOtherTracksMixin.js.coffee create mode 100644 web/app/assets/stylesheets/client/react-components/SessionTrack.css.scss create mode 100644 web/app/assets/stylesheets/dialogs/inviteMusiciansDialog.css.scss create mode 100644 web/app/assets/stylesheets/dialogs/sessionMasterMixDialog.css.scss create mode 100644 web/app/views/dialogs/_sessionMasterMixDialog.html.slim diff --git a/web/app/assets/images/content/bkg_slider_gain_horiz_24.png b/web/app/assets/images/content/bkg_slider_gain_horiz_24.png new file mode 100644 index 0000000000000000000000000000000000000000..aad8fb7b9e3336e25c04072b0ef639b0db9d61d1 GIT binary patch literal 263 zcmeAS@N?(olHy`uVBq!ia0vp^>_9BR!3HGFE|gW!U_%O?XxI14-? ziy0WWg+Z8+Vb&Z8pdfpRr>`sfeKvk3MWGhkz)Yaf6i*k&5RLO^FB)Uj(MB|?X_`?sk7(Q-1n4Rjxar>mdKI;Vst E0PCGz>i_@% literal 0 HcmV?d00001 diff --git a/web/app/assets/images/content/icon_email.png b/web/app/assets/images/content/icon_email.png new file mode 100644 index 0000000000000000000000000000000000000000..a3163f217b341d24f5468101bccabcafca8cd67a GIT binary patch literal 296 zcmeAS@N?(olHy`uVBq!ia0vp^Vn8g)!3HFq6y&A?DYhhUcNd2LAh=-f^2tCE&H|6f zVg?4j!ywFfJby(BP>{XE)7O>#KAWrEak-aXPsoTkYfgTMyRGJW_w`KMSNm0NJ=gBgTe~DWM4f%gtwI literal 0 HcmV?d00001 diff --git a/web/app/assets/images/content/icon_equalizer.png b/web/app/assets/images/content/icon_equalizer.png new file mode 100644 index 0000000000000000000000000000000000000000..9c63c8dcc3d44de4749a973b3fe577c3a05043fe GIT binary patch literal 1010 zcmVVGd000McNliru-v$g5IxUnu(Psbv03B&m zSad^gZEa<4bN~PV002XBWnpw>WFU8GbZ8()Nlj2>E@cM*00UM@L_t(I%axVSPa9Vd z$3N@c^}>P)l7)@=Wtnv>yKYD$a%h@ML5&1Oq?bl&#I-pAm*$pArGEg2^uQGfaY)6T zgKs{h2#QiE$Os!Y6&OqW5Ee+rUhG}_^^n>sf#%0ZBfZ1uGxO&AW}Xx_48cd55Q3JL z7Jhv4gg;)sWOa2FRaGy=xcCHf9EVUS#PswuV`F1Dj&mjElYrG~6(bx5pr@w?S(e`q zv~3&Pw%>c@STy<;*xQS)>j)vP2i9sezPNLT!NEalwc52nMNx=EA^>!BbkN@3ekImb z&)n41#N6B*v$L}(ih>Y=i;D{?l}e*JkH>>y80_rqpzAt5pYM90<2VF^K{`7->FMny z7!2Y#4igg-jpkRYRs4QGKEIztB7qQszP`SzK-;z{7K?lqk0Z-6s;V+DFo3S>{4z7c zPfwqsC<>0_(BI#Ws;aE4tZ;sQj$s&7DizA*a>JFuyLV}8Yva3z582(_1>mdu_erPI z=(_&4DxZ(P-@G9ji%}>P$fVO89~}`2g$Rek#N%-!NkRyLGC4WP@bEA~2=^${alVfR~J)LQ~dDwaYINcPIq@V-#>c9;lTl=Qi*|Ex6s^fZbl+xG8rx`3)3_)41+K4-6NSyGCVZIx1*yd zxm>Q%SRn+dR0_+oh(sd%{^A8UBM}B}-=@RoqpPcn#l=OCBuvx9>-FOGdRbapVsmqo z=H_Nxbs_40aBx5-od)2~^>sF0za|=u5{t#qG>uF;jjF0-v)RU@-rn9u(==37y*w$B zBvCGxSzBAfaU3>NDK<7X@caFIJv4-6S?ulYp{i=59ky+=x4YXIL*?CR&@_#Og$0(E zmoZHfx7&>@%Z!hYlgs5eIXOX56i!c1DHICGvdrP(AugBedZ5eYBA?H5e0+>7%k1y( zlh5Y~1Ooi^>J_C@2~E?mEQ@3^i4cNfvH1TL)}i%`XJ==aJ39md0Wz6PLqJ)U`T5zi z#`&Yym*Y5W{re`WFU8GbZ8()Nlj2>E@cM*00>-3L_t(o!_ApnP}5f) z$3OWeAwezz0wENkY%BxB#DbSRs4zyU)#?HbbiH&HXDnK0 zY-JzXRUdSFV=CxKSF222uI3_w5{%lQkw_Xhgd`-{hbmiys8!n5UFjEJmSFke8Q7ettfGP^rks$RIvG9zhTYg+knOyXooaq04He{n90_T)9GT zZ!f`M@M)Jt^fb^ZDJjgFHH&$AJw-)DWM*a}lgS7MgZO+td_EuHa2P=lh>3|oCX*o+ zi}8BB*zIQU8jcl=$l$6b*4QQuLW9QDDXtY|s`Q{rw+O!Fi(MTv1dg!%9kKPqQ zfN*4E27^JaUcE{~Ljwwh0=-_(v}w~YnoRWe_C7guen9~{ckDnlWeR_*M*P~XeX=-Z1HHw3dfL1D%Y}>XCjaEzbp+i(vRpIq|c^>i& z4A9uv$i#^gDJv@@J3E_(^XEsbKMYzXld=B2_gJ`aA^$vnoXX0|=LY;hfk1%Pwl;Ed za#*ln0f^=>`z;L(4U7lX*Vj{5SI3MQGkA5;qT$f#>FF$4vV=Ph2S<+{eWqe&Eamt6 zIdb?gZnqnQ!9Y?{(*K}m&z?@Qm^5h;gM)+Ix^)YYNHmn- zy02X$7!0CPsgNi#GU)H`$LVweAdyO$n3za%ax%-7En~*anK+$JKHa~c#>U2|gu{e{ z1m>2MP*zrkR;wj5Gn3}#X7*H8($Ud@$K%1-+snYf07|72$;8A&yk756jghHTQc_aF zs@1DWOG^X5?RGO&qoJ*>Eh^9|m5Sxdm!r{WkV>Th=yW*Vc@vMvgU{z9 zF)(j#>S%6YRS#bjmpu;FDPKjlqo~N50sUa#d{kz zAeYMt1Of~W4kDAu5W=AlB9RC|5b*o`R8&+D8yh>KHBzh96crUk1$ts)V#KlH;$k!! z4HBsoi9~|m??FMdDrlum7%ej5)7J}7kMJ|_< zot;g5Lc$~eye~@0C;5IM6iNTNcaNMGUqmbx(`B_HT(VdQ27?q97UFO?B2JV@BxBlz zKivF2>_@+?zfYe&O<`dne!rjg_I8AmCr?r`cP`b{)wDD>M+|9$gM-98o!}& zrsKwq8yJm7v|258yPeL?PK5scejE-5cDo&u$wW&_OXN1l?T!j`YilbOi)Dl*ygbxxGCq(B0kL7>&kZP4t~R zclg`KAM@`E7ua1{iAW^khx&Sg!63upjKkr;!0wojOHV zS65VOjzA!Q*<`|Eu`p@UB$^r<`QnQ&=gr#AAMN1>m6i1o_i4{VbLPz9wbx$5Y&KI< zQ#1O#$9R%bsbuw()_|j^y`*rf5cw^z?MzdG}r3 zc;gMMRx6u6{E$nRE{z^leou25t=>?l)3NrQcPK6{rlqBYty{KWu~;I9!m(Psq_h;B zPB-F3J3Bk6`R=<>VO1&>Z@&2^OJ0AS*w|Q(A3H`>RTXx-{n7Dzte|x|9V=I^99axq zzdj7Qq_h;hUXKV^P*z5IdO95)9US`nb55K%!N9=4e2eSJOW&z% S$2t}O0000VGd000McNliru-v$g5IUWGPWFU8GbZ8()Nlj2>E@cM*00YTML_t(I%axShOH_Fn z#y{trIpfS|DQc@Tjx*3GQ>I|Sw&6hFwq_8)Mw-iZw`P!R7cCC7H;wMr(&BA()8b!H z3)XnylD24wEnbL-phF_#fIm{SwK#L;$9eanoK=*ieQv(+{lfEp-{*PXN2v+~gpUvr zOw+{c_0rbX##c>E`2BvIPA96WlFeq>+S+1vc9v&PpVHso&%1Z;u-on16e!z-B0@Ty zruFh=!r?GwWo7Ivq$mp6Y!*$^a`#hHQ$%jxW@2Ijhr{vVvMLaez~RHftMAp~WrM0C+ycHEv6$l7bfq-}` zDkKt#11YRh97U;B9qBbfBZP-&YeT8udnCJ zGiLzk>+54>c^O^T`4qTZE=EU38G8O4fUhrJL_L50Jf9sp#QXQ}86FziH3FXk5n*6p z0DxdHh#CroP*s(9JkH9>%C524#&!{#&9>*wX0tIjH%BIup|G$Jo7?RMV10cZ(=_+A zYgrZ}BO@#=EfI^w_AQuBr?D&xUDvU_dGiLh$3tOZA$GeR%d&E=Zl6E*^dKKy-cJ%u z(@+!z%d$`>CnpgR{C+>BrKMPwwYM!@N7wazf0xN*sIIO?*L5~GH&LHOqik$!aPZ(k zLZJ|;RO)}5(scGL0I^sMb?oI!Mn^^fxZ2S{QBe`uZ1$7LlF1~WpFBx(b29+ZXcQGi zp|7uxR4PR<7$g#j$rhUVsGY?{X3TU(gPWNzJp!5|&iu5sti9eh3?hGFo-&71r&IEcgH_%{zN%Oac2 z(%IR`_cw0fa=CIkEiWyRN~Orp&&TcYpsFeWi;IhNMPnO!Y~XnnG8pd9;LCdkuOf2qO`P> r*RNkQK0eN$FJ6#HByhQ0dp7GIcQCt}YaVoO00000NkvXXu0mjfPR$X& literal 0 HcmV?d00001 diff --git a/web/app/assets/javascripts/client_init.js.coffee b/web/app/assets/javascripts/client_init.js.coffee index bc20a8015..659e5a24d 100644 --- a/web/app/assets/javascripts/client_init.js.coffee +++ b/web/app/assets/javascripts/client_init.js.coffee @@ -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 diff --git a/web/app/assets/javascripts/dialog/sessionMasterMixDialog.js.coffee b/web/app/assets/javascripts/dialog/sessionMasterMixDialog.js.coffee new file mode 100644 index 000000000..781031b95 --- /dev/null +++ b/web/app/assets/javascripts/dialog/sessionMasterMixDialog.js.coffee @@ -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) + + diff --git a/web/app/assets/javascripts/react-components.js b/web/app/assets/javascripts/react-components.js index 880aa84c4..aa19d1274 100644 --- a/web/app/assets/javascripts/react-components.js +++ b/web/app/assets/javascripts/react-components.js @@ -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 \ No newline at end of file diff --git a/web/app/assets/javascripts/react-components/SessionBackingTrack.js.jsx.coffee b/web/app/assets/javascripts/react-components/SessionBackingTrack.js.jsx.coffee index 536f13e20..dafee8601 100644 --- a/web/app/assets/javascripts/react-components/SessionBackingTrack.js.jsx.coffee +++ b/web/app/assets/javascripts/react-components/SessionBackingTrack.js.jsx.coffee @@ -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')}) }) diff --git a/web/app/assets/javascripts/react-components/SessionChatMixer.js.jsx.coffee b/web/app/assets/javascripts/react-components/SessionChatMixer.js.jsx.coffee new file mode 100644 index 000000000..bd59eb726 --- /dev/null +++ b/web/app/assets/javascripts/react-components/SessionChatMixer.js.jsx.coffee @@ -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)" + } + + `
+
+
+
Session Voice Chat Output
+
+
+ +
+
+
+
+
+
+
+
+
` + + 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')}) + + +}) \ No newline at end of file diff --git a/web/app/assets/javascripts/react-components/SessionInviteMusiciansBtn.js.jsx.coffee b/web/app/assets/javascripts/react-components/SessionInviteMusiciansBtn.js.jsx.coffee index f1c0934f7..d5ffb91fd 100644 --- a/web/app/assets/javascripts/react-components/SessionInviteMusiciansBtn.js.jsx.coffee +++ b/web/app/assets/javascripts/react-components/SessionInviteMusiciansBtn.js.jsx.coffee @@ -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: () -> ` diff --git a/web/app/assets/javascripts/react-components/SessionJamTrack.js.jsx.coffee b/web/app/assets/javascripts/react-components/SessionJamTrack.js.jsx.coffee index 740b3edb4..2e8ddecab 100644 --- a/web/app/assets/javascripts/react-components/SessionJamTrack.js.jsx.coffee +++ b/web/app/assets/javascripts/react-components/SessionJamTrack.js.jsx.coffee @@ -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')}) }) diff --git a/web/app/assets/javascripts/react-components/SessionJamTrackCategory.js.jsx.coffee b/web/app/assets/javascripts/react-components/SessionJamTrackCategory.js.jsx.coffee new file mode 100644 index 000000000..630782717 --- /dev/null +++ b/web/app/assets/javascripts/react-components/SessionJamTrackCategory.js.jsx.coffee @@ -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)" + } + + `
+
+
JamTrack:
+
{this.props.jamTrackName}
+
+ +
+
+
+
+
+
+ +
+
+
` + + 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')}) + +}) \ No newline at end of file diff --git a/web/app/assets/javascripts/react-components/SessionMasterCategoryControls.js.jsx.coffee b/web/app/assets/javascripts/react-components/SessionMasterCategoryControls.js.jsx.coffee new file mode 100644 index 000000000..760495ef0 --- /dev/null +++ b/web/app/assets/javascripts/react-components/SessionMasterCategoryControls.js.jsx.coffee @@ -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(``) + + if @state.chatGroupMixers? + input = + mixers: @state.chatGroupMixers + + categoryControls.push(``) + + + `
+

master output

+
+ {categoryControls} +
+
` + + + getInitialState:() -> + {inputGroupMixers: null, chatGroupMixers: null} + + + onAppInit: (app) -> + @app = app + + + + + +}) diff --git a/web/app/assets/javascripts/react-components/SessionMasterMediaTracks.js.jsx.coffee b/web/app/assets/javascripts/react-components/SessionMasterMediaTracks.js.jsx.coffee new file mode 100644 index 000000000..1b9a6aae2 --- /dev/null +++ b/web/app/assets/javascripts/react-components/SessionMasterMediaTracks.js.jsx.coffee @@ -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(``) + else if this.state.mediaSummary.jamTrackOpen + mediaTracks.push(``) + for jamTrack in @state.jamTracks + jamTrack.mode = MIX_MODES.MASTER + mediaTracks.push(``) + else if this.state.mediaSummary.recordingOpen + mediaTracks.push(``) + for recordedTrack in @state.recordedTracks + recordedTrack.mode = MIX_MODES.MASTER + mediaTracks.push(``) + else if this.state.mediaSummary.metronomeOpen + @state.metronome.mode = MIX_MODES.MASTER + mediaTracks.push(``) + + `
+

recorded audio

+
+ {mediaTracks} +
+
` + + + getInitialState:() -> + {mediaSummary:{mediaOpen: false}, isRecording: false, backingTracks: [], jamTracks: [], recordedTracks: [], metronome: null} + + onAppInit: (app) -> + @app = app + + + + + +}) diff --git a/web/app/assets/javascripts/react-components/SessionMasterMix.js.jsx.coffee b/web/app/assets/javascripts/react-components/SessionMasterMix.js.jsx.coffee new file mode 100644 index 000000000..51a9be63a --- /dev/null +++ b/web/app/assets/javascripts/react-components/SessionMasterMix.js.jsx.coffee @@ -0,0 +1,13 @@ +context = window +MIX_MODES = context.JK.MIX_MODES + +@SessionMasterMix = React.createClass({ + + render: () -> + `
+ + + + +
` +}) \ No newline at end of file diff --git a/web/app/assets/javascripts/react-components/SessionMasterMyTracks.js.jsx.coffee b/web/app/assets/javascripts/react-components/SessionMasterMyTracks.js.jsx.coffee new file mode 100644 index 000000000..44f879eed --- /dev/null +++ b/web/app/assets/javascripts/react-components/SessionMasterMyTracks.js.jsx.coffee @@ -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(``) + + if @state.chat + @state.chat.mode = @props.mode + tracks.push(``) + + else if this.state.session? && this.state.session.inSession() + logger.debug("no 'my inputs' for master mix") + + `
+

my live tracks

+
+ {content} + {tracks} +
+
` + + getInitialState:() -> + {tracks:[], session: null} + + onAppInit: (app) -> + @app = app +}) diff --git a/web/app/assets/javascripts/react-components/SessionMasterOtherTrack.js.jsx.coffee b/web/app/assets/javascripts/react-components/SessionMasterOtherTrack.js.jsx.coffee new file mode 100644 index 000000000..7edfb02c9 --- /dev/null +++ b/web/app/assets/javascripts/react-components/SessionMasterOtherTrack.js.jsx.coffee @@ -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)" + } + + #
+ + `
+
+
+
{this.props.name}
+
+
+
+ +
+
+
+
+
+
+ + +
+
+
` + + 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) +}) diff --git a/web/app/assets/javascripts/react-components/SessionMasterOtherTracks.js.jsx.coffee b/web/app/assets/javascripts/react-components/SessionMasterOtherTracks.js.jsx.coffee new file mode 100644 index 000000000..a441c6679 --- /dev/null +++ b/web/app/assets/javascripts/react-components/SessionMasterOtherTracks.js.jsx.coffee @@ -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(``) + + `
+

other live tracks

+
+ {tracks} +
+
` + + getInitialState:() -> + {tracks:[], session: null} + + onAppInit: (app) -> + @app = app +}) diff --git a/web/app/assets/javascripts/react-components/SessionMediaTracks.js.jsx.coffee b/web/app/assets/javascripts/react-components/SessionMediaTracks.js.jsx.coffee index 8199ed192..c70ad933c 100644 --- a/web/app/assets/javascripts/react-components/SessionMediaTracks.js.jsx.coffee +++ b/web/app/assets/javascripts/react-components/SessionMediaTracks.js.jsx.coffee @@ -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
` - for backingTrack in @state.backingTracks - mediaTracks.push(``) - - for jamTrack in @state.jamTracks - mediaTracks.push(``) - - for recordedTrack in @state.recordedTracks - mediaTracks.push(``) - - if @state.metronome + if this.state.mediaSummary.backingTrackOpen + for backingTrack in @state.backingTracks + backingTrack.mode = MIX_MODES.PERSONAL + mediaTracks.push(``) + else if this.state.mediaSummary.jamTrackOpen + mediaTracks.push(``) + for jamTrack in @state.jamTracks + jamTrack.mode = MIX_MODES.PERSONAL + mediaTracks.push(``) + else if this.state.mediaSummary.recordingOpen + mediaTracks.push(``) + for recordedTrack in @state.recordedTracks + recordedTrack.mode = MIX_MODES.PERSONAL + mediaTracks.push(``) + else if this.state.mediaSummary.metronomeOpen + @state.metronome.mode = MIX_MODES.PERSONAL mediaTracks.push(``) 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? }) diff --git a/web/app/assets/javascripts/react-components/SessionMetronome.js.jsx.coffee b/web/app/assets/javascripts/react-components/SessionMetronome.js.jsx.coffee index 18fae8b0f..cdbf7a8fe 100644 --- a/web/app/assets/javascripts/react-components/SessionMetronome.js.jsx.coffee +++ b/web/app/assets/javascripts/react-components/SessionMetronome.js.jsx.coffee @@ -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')}) }) diff --git a/web/app/assets/javascripts/react-components/SessionMixerBtn.js.jsx.coffee b/web/app/assets/javascripts/react-components/SessionMixerBtn.js.jsx.coffee index b4efeece2..c3f404fce 100644 --- a/web/app/assets/javascripts/react-components/SessionMixerBtn.js.jsx.coffee +++ b/web/app/assets/javascripts/react-components/SessionMixerBtn.js.jsx.coffee @@ -2,8 +2,12 @@ context = window @SessionMixerBtn = React.createClass({ + openDialog: (e) -> + e.preventDefault() + context.JK.app.layout.showDialog('session-master-mix-dialog') + render: () -> - ` + ` MIXER ` diff --git a/web/app/assets/javascripts/react-components/SessionMusicMixer.js.jsx.coffee b/web/app/assets/javascripts/react-components/SessionMusicMixer.js.jsx.coffee new file mode 100644 index 000000000..17881b241 --- /dev/null +++ b/web/app/assets/javascripts/react-components/SessionMusicMixer.js.jsx.coffee @@ -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)" + } + + `
+
+
+
Session Music Output
+
+
+ +
+
+
+
+
+
+
+
+
` + + 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')}) + + +}) \ No newline at end of file diff --git a/web/app/assets/javascripts/react-components/SessionMyChat.js.jsx.coffee b/web/app/assets/javascripts/react-components/SessionMyChat.js.jsx.coffee new file mode 100644 index 000000000..ad551efd4 --- /dev/null +++ b/web/app/assets/javascripts/react-components/SessionMyChat.js.jsx.coffee @@ -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 + }) + + + #
+ + `
+
+
+
{this.props.name}
+
+
+
+ +
+
+
+
+
+ + +
+
+
` + + 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')}) +}) diff --git a/web/app/assets/javascripts/react-components/SessionMyTrack.js.jsx.coffee b/web/app/assets/javascripts/react-components/SessionMyTrack.js.jsx.coffee index c120b08e4..23789c318 100644 --- a/web/app/assets/javascripts/react-components/SessionMyTrack.js.jsx.coffee +++ b/web/app/assets/javascripts/react-components/SessionMyTrack.js.jsx.coffee @@ -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()) diff --git a/web/app/assets/javascripts/react-components/SessionMyTracks.js.jsx.coffee b/web/app/assets/javascripts/react-components/SessionMyTracks.js.jsx.coffee index 1d6b8ea26..3f98515e5 100644 --- a/web/app/assets/javascripts/react-components/SessionMyTracks.js.jsx.coffee +++ b/web/app/assets/javascripts/react-components/SessionMyTracks.js.jsx.coffee @@ -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(``) - else if this.state.session? && this.state.session.inSession() + if @state.chat + @state.chat.mode = @props.mode + tracks.push(``) + + else if @state.session? && @state.session.inSession() content = `

You have not set up any inputs for your instrument or vocals. @@ -67,7 +43,7 @@ ReactCSSTransitionGroup = React.addons.CSSTransitionGroup;

` getInitialState:() -> - {tracks:[], session: null} + {tracks:[], session: null, chat:null} onAppInit: (app) -> @app = app diff --git a/web/app/assets/javascripts/react-components/SessionOtherTrack.js.jsx.coffee b/web/app/assets/javascripts/react-components/SessionOtherTrack.js.jsx.coffee index 2129481da..87477c028 100644 --- a/web/app/assets/javascripts/react-components/SessionOtherTrack.js.jsx.coffee +++ b/web/app/assets/javascripts/react-components/SessionOtherTrack.js.jsx.coffee @@ -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')}) diff --git a/web/app/assets/javascripts/react-components/SessionOtherTracks.js.jsx.coffee b/web/app/assets/javascripts/react-components/SessionOtherTracks.js.jsx.coffee index 9e6815212..1c74d4a77 100644 --- a/web/app/assets/javascripts/react-components/SessionOtherTracks.js.jsx.coffee +++ b/web/app/assets/javascripts/react-components/SessionOtherTracks.js.jsx.coffee @@ -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: () -> diff --git a/web/app/assets/javascripts/react-components/SessionRecordedCategory.js.jsx.coffee b/web/app/assets/javascripts/react-components/SessionRecordedCategory.js.jsx.coffee new file mode 100644 index 000000000..b7305d56b --- /dev/null +++ b/web/app/assets/javascripts/react-components/SessionRecordedCategory.js.jsx.coffee @@ -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)" + } + + `
+
+
{this.props.recordingName}
+
+ +
+
+
+
+
+
+ +
+
+
` + + 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')}) + +}) \ No newline at end of file diff --git a/web/app/assets/javascripts/react-components/SessionRecordedTrack.js.jsx.coffee b/web/app/assets/javascripts/react-components/SessionRecordedTrack.js.jsx.coffee index 0ff654dd2..6a72d1919 100644 --- a/web/app/assets/javascripts/react-components/SessionRecordedTrack.js.jsx.coffee +++ b/web/app/assets/javascripts/react-components/SessionRecordedTrack.js.jsx.coffee @@ -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')}) }) diff --git a/web/app/assets/javascripts/react-components/SessionScreen.js.jsx.coffee b/web/app/assets/javascripts/react-components/SessionScreen.js.jsx.coffee index 5f807a5c5..38587d094 100644 --- a/web/app/assets/javascripts/react-components/SessionScreen.js.jsx.coffee +++ b/web/app/assets/javascripts/react-components/SessionScreen.js.jsx.coffee @@ -1,4 +1,5 @@ context = window +MIX_MODES = context.JK.MIX_MODES SessionActions = @SessionActions @@ -19,9 +20,9 @@ SessionActions = @SessionActions
- - - + + +
` @@ -73,5 +74,5 @@ SessionActions = @SessionActions 'beforeDisconnect' : @beforeDisconnect, }; - @app.bindScreen('session2', screenBindings); + @app.bindScreen('session', screenBindings); }) diff --git a/web/app/assets/javascripts/react-components/SessionSelfVolumeHover.js.jsx.coffee b/web/app/assets/javascripts/react-components/SessionSelfVolumeHover.js.jsx.coffee index cfe67e8d7..dfaefafa4 100644 --- a/web/app/assets/javascripts/react-components/SessionSelfVolumeHover.js.jsx.coffee +++ b/web/app/assets/javascripts/react-components/SessionSelfVolumeHover.js.jsx.coffee @@ -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} diff --git a/web/app/assets/javascripts/react-components/SessionTrackGain.js.jsx.coffee b/web/app/assets/javascripts/react-components/SessionTrackGain.js.jsx.coffee index 99ea912e8..e5dfa7f58 100644 --- a/web/app/assets/javascripts/react-components/SessionTrackGain.js.jsx.coffee +++ b/web/app/assets/javascripts/react-components/SessionTrackGain.js.jsx.coffee @@ -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 + `
-
+
@@ -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); }) \ No newline at end of file diff --git a/web/app/assets/javascripts/react-components/SessionTrackPan.js.jsx.coffee b/web/app/assets/javascripts/react-components/SessionTrackPan.js.jsx.coffee index 72a9f9c3d..0bfae1005 100644 --- a/web/app/assets/javascripts/react-components/SessionTrackPan.js.jsx.coffee +++ b/web/app/assets/javascripts/react-components/SessionTrackPan.js.jsx.coffee @@ -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 + `
Left
Right
-
+
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. +


-
- INVITE -
-
- CANCEL  + + + +
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