diff --git a/client/less/chat.less b/client/less/chat.less new file mode 100644 index 0000000000..a6f83171ca --- /dev/null +++ b/client/less/chat.less @@ -0,0 +1,24 @@ +.chat-embed-main-title { + display: flex; + flex-grow: 1; + padding-left: 31px; + padding-top: 7px; +} + +.gitter-chat-embed { + z-index: 100; + position: fixed; + + top: 0; + left: 60%; + bottom: 0; + right: 0; + + display: flex; + flex-direction: row; + transition: transform 0.3s cubic-bezier(0.16, 0.22, 0.22, 1.7); +} + +.gitter-chat-embed.is-collapsed:not(.is-loading) { + transform: translateX(110%); +} diff --git a/client/less/main.less b/client/less/main.less index 4340350da7..540aa96c95 100644 --- a/client/less/main.less +++ b/client/less/main.less @@ -951,6 +951,11 @@ code { margin: 0!important; } +// gitter chat +.gitter-chat-embed { + z-index: 20000 !important; +} + //uncomment this to see the dimensions of all elements outlined in red //* { // border-color: red; @@ -1058,3 +1063,5 @@ code { transform: rotate(0deg); } } + +@import "chat.less"; diff --git a/client/main.js b/client/main.js index 892610e1eb..f4e74c5967 100644 --- a/client/main.js +++ b/client/main.js @@ -4,6 +4,105 @@ main.mapShareKey = 'map-shares'; main.ga = window.ga || function() {}; +main = (function(main) { + + // should be set before gitter script loads + ((window.gitter = {}).chat = {}).options = { + disableDefaultChat: true + }; + // wait for sidecar to load + + main.chat = {}; + main.chat.isOpen = false; + main.chat.createHelpChat = function createHelpChat() { + throw new Error('Sidecar chat has not initialized'); + }; + + document.addEventListener('gitter-sidecar-ready', function(e) { + main.chat.GitterChat = e.detail.Chat; + + main.chat.createHelpChat = function(room, helpChatBtnClass, roomTitle) { + roomTitle = roomTitle || 'Waypoint Help'; + + $('body').append( + '' + ); + + main.chat.helpChat = new main.chat.GitterChat({ + room: room, + activationElement: false, + targetElement: $('#chat-embed-help') + }); + + $(helpChatBtnClass).on('click', function() { + // is button already pressed? + // no? open chat + // yes? close chat + var shouldChatBeOpen = !$(this).hasClass('active'); + main.chat.helpChat.toggleChat(shouldChatBeOpen); + if (shouldChatBeOpen) { + $(helpChatBtnClass).addClass('active'); + } + }); + + var helpTitleAdd = false; + $('#chat-embed-help').on('gitter-chat-toggle', function(e) { + var shouldButtonBePressed = !!e.originalEvent.detail.state; + + if (!helpTitleAdd) { + helpTitleAdd = true; + $('#chat-embed-help > .gitter-chat-embed-action-bar').prepend( + '
' + ); + } + + if (shouldButtonBePressed) { + return $(helpChatBtnClass).addClass('active'); + } + return $(helpChatBtnClass).removeClass('active'); + }); + }; + + $('body').append( + '' + ); + + main.chat.mainChat = new main.chat.GitterChat({ + room: 'freecodecamp/freecodecamp', + activationElement: false, + targetElement: $('#chat-embed-main') + }); + + var mainChatTitleAdded = false; + $('#chat-embed-main').on('gitter-chat-toggle', function() { + if (mainChatTitleAdded) { + return null; + } + mainChatTitleAdded = true; + + $('#chat-embed-main > .gitter-chat-embed-action-bar').prepend( + ' ' + ); + }); + + + $('#nav-chat-btn').on('click', function() { + if (!main.chat.isOpen) { + + main.chat.mainChat.toggleChat(true); + } + }); + }); + + return main; +}(main)); + var lastCompleted = typeof lastCompleted !== 'undefined' ? lastCompleted : ''; @@ -38,9 +137,10 @@ function setMapShare(id) { $(document).ready(function() { + var challengeName = typeof challengeName !== 'undefined' ? challengeName : - 'Untitled'; + ''; if (challengeName) { ga('send', 'event', 'Challenge', 'load', challengeName); diff --git a/gulpfile.js b/gulpfile.js index fd5fdda38d..4ae0d93d23 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -79,6 +79,7 @@ var paths = { ], less: './client/less/main.less', + lessFiles: './client/less/*.less', manifest: 'server/manifests/', @@ -392,7 +393,7 @@ var watchDependents = [ ]; gulp.task('watch', watchDependents, function() { - gulp.watch(paths.less, ['less']); + gulp.watch(paths.lessFiles, ['less']); gulp.watch(paths.js, ['js']); gulp.watch(paths.challenges, ['test-challenges']); gulp.watch(paths.js, ['js', 'dependents']); diff --git a/server/middlewares/csp.js b/server/middlewares/csp.js index 74914f56b2..0c812e00c6 100644 --- a/server/middlewares/csp.js +++ b/server/middlewares/csp.js @@ -55,6 +55,7 @@ export default function csp() { return helmet.csp({ defaultSrc: trusted, scriptSrc: [ + 'https://*.gitter.im', '*.optimizely.com', '*.aspnetcdn.com', '*.d3js.org', diff --git a/server/views/coursewares/showBonfire.jade b/server/views/coursewares/showBonfire.jade index 1768bd4274..84bfdb0e29 100644 --- a/server/views/coursewares/showBonfire.jade +++ b/server/views/coursewares/showBonfire.jade @@ -52,7 +52,7 @@ block content label.btn.btn-success#trigger-reset-modal i.fa.fa-refresh | Reset - label.btn.btn-success#trigger-help-modal + label.btn.btn-success#challenge-help-btn i.fa.fa-medkit | Help label.btn.btn-success#trigger-issue-modal @@ -122,7 +122,14 @@ block content a.btn.btn-lg.btn-primary.btn-block(href='#', data-dismiss='modal', aria-hidden='true') Cancel include ../partials/challenge-modals script. - var MDNlinks = !{JSON.stringify(MDNlinks)}; - if (!MDNlinks.length) { - $('#MDN-links').addClass('collapse'); + + document.addEventListener('gitter-sidecar-ready', function(e) { + if (window.main) { + window.main.chat.createHelpChat('freecodecamp/helpbonfires', '#challenge-help-btn', 'Bonfires Help'); } + }); + + var MDNlinks = !{JSON.stringify(MDNlinks)}; + if (!MDNlinks.length) { + $('#MDN-links').addClass('collapse'); + } diff --git a/server/views/coursewares/showHTML.jade b/server/views/coursewares/showHTML.jade index f08d6997dc..e83aa938a8 100644 --- a/server/views/coursewares/showHTML.jade +++ b/server/views/coursewares/showHTML.jade @@ -38,7 +38,11 @@ block content label.btn.btn-success#trigger-reset-modal i.fa.fa-refresh | Reset - label.btn.btn-success#trigger-help-modal + label.btn.btn-success.hidden-sm.hidden-md.hidden-lg + a(href='//gitter.im/freecodecamp/help') + i.fa.fa-medkit + | Help + label.btn.btn-success.hidden-xs#challenge-help-btn i.fa.fa-medkit | Help label.btn.btn-success#trigger-issue-modal @@ -95,3 +99,9 @@ block content else a.btn.btn-lg.btn-primary.btn-block(href='/challenges/next-challenge?id=' + challengeId) Go to my next challenge include ../partials/challenge-modals + script. + document.addEventListener('gitter-sidecar-ready', function(e) { + if (window.main) { + window.main.chat.createHelpChat('freecodecamp/help', '#challenge-help-btn'); + } + }); diff --git a/server/views/coursewares/showJS.jade b/server/views/coursewares/showJS.jade index f4621b5b28..887e44c964 100644 --- a/server/views/coursewares/showJS.jade +++ b/server/views/coursewares/showJS.jade @@ -43,7 +43,7 @@ block content label.btn.btn-success#trigger-reset-modal i.fa.fa-refresh | Reset - label.btn.btn-success#trigger-help-modal + label.btn.btn-success#challenge-help-btn i.fa.fa-medkit | Help label.btn.btn-success#trigger-issue-modal @@ -98,7 +98,12 @@ block content a.animated.fadeIn.btn.btn-lg.btn-primary.btn-block(href='/challenges/next-challenge?id=' + challengeId) Go to my next challenge include ../partials/challenge-modals script. - var MDNlinks = !{JSON.stringify(MDNlinks)}; - if (!MDNlinks.length) { - $('#MDN-links').addClass('collapse'); + var MDNlinks = !{JSON.stringify(MDNlinks)}; + if (!MDNlinks.length) { + $('#MDN-links').addClass('collapse'); + } + document.addEventListener('gitter-sidecar-ready', function(e) { + if (window.main) { + window.main.chat.createHelpChat('freecodecamp/help', '#challenge-help-btn'); } + }); diff --git a/server/views/coursewares/showVideo.jade b/server/views/coursewares/showVideo.jade index bd7f42e5ed..90292e742d 100644 --- a/server/views/coursewares/showVideo.jade +++ b/server/views/coursewares/showVideo.jade @@ -24,7 +24,7 @@ block content var userLoggedIn = true; .button-spacer .btn-group.input-group.btn-group-justified - .btn.btn-success.btn-big#trigger-help-modal + .btn.btn-success.btn-big#challenge-help-btn i.fa.fa-medkit | Get help .btn.btn-success.btn-big#trigger-issue-modal @@ -74,8 +74,14 @@ block content script. $('body').bind('keypress', controlEnterHandler); script. - var challenge_Id = !{JSON.stringify(challengeId)}; - var challenge_Name = !{JSON.stringify(name)}; - var challengeType = !{JSON.stringify(challengeType)}; - var dashedName = !{JSON.stringify(dashedName)}; + var challenge_Id = !{JSON.stringify(challengeId)}; + var challenge_Name = !{JSON.stringify(name)}; + var challengeType = !{JSON.stringify(challengeType)}; + var dashedName = !{JSON.stringify(dashedName)}; + document.addEventListener('gitter-sidecar-ready', function(e) { + if (window.main) { + window.main.chat.createHelpChat('freecodecamp/help', '#challenge-help-btn'); + } + }); include ../partials/challenge-modals + diff --git a/server/views/coursewares/showZiplineOrBasejump.jade b/server/views/coursewares/showZiplineOrBasejump.jade index 174be42e3e..f5e4b666d2 100644 --- a/server/views/coursewares/showZiplineOrBasejump.jade +++ b/server/views/coursewares/showZiplineOrBasejump.jade @@ -24,7 +24,7 @@ block content a.btn.btn-big.btn-primary.btn-block(href='/challenges/next-challenge?id=' + challengeId) Go to my next challenge (ctrl + enter) .button-spacer .btn-group.input-group.btn-group-justified - .btn.btn-success.btn-big#trigger-help-modal + .btn.btn-success.btn-big#challenge-help-btn i.fa.fa-medkit | Help .btn.btn-success.btn-big#trigger-issue-modal @@ -96,3 +96,23 @@ block content script. $('body').on('keypress', controlEnterHandler); include ../partials/challenge-modals + script. + document.addEventListener('gitter-sidecar-ready', function(e) { + var challengeType = !{JSON.stringify(challengeType)}; + var room = 'freecodecamp/help'; + var title; + + if (challengeType === '4') { + room = 'freecodecamp/helpBasejumps'; + title = 'Basejump Help'; + } + + if (challengeType === '3') { + room = 'freecodecamp/helpZiplines'; + title = 'Zipline Help'; + } + + if (window.main) { + window.main.chat.createHelpChat(room, '#challenge-help-btn', title); + } + }); diff --git a/server/views/partials/challenge-modals.jade b/server/views/partials/challenge-modals.jade index 46f6eae7ee..7510fbeef3 100644 --- a/server/views/partials/challenge-modals.jade +++ b/server/views/partials/challenge-modals.jade @@ -10,20 +10,6 @@ a.btn.btn-lg.btn-primary.btn-block#report-issue(name='_csrf', value=_csrf) Create my GitHub issue a.btn.btn-lg.btn-info.btn-block(href='#', data-dismiss='modal', aria-hidden='true') Cancel -#help-modal.modal(tabindex='-1') - .modal-dialog.animated.fadeIn.fast-animation - .modal-content - .modal-header.challenge-list-header Need some help? - a.close.closing-x(href='#', data-dismiss='modal', aria-hidden='true') × - .modal-body.text-center - h3 Remember to use - a(href='//github.com/FreeCodeCamp/freecodecamp/wiki/How-to-get-help-when-you-get-stuck', target='_blank') Read-Search-Ask - | . - h3 If you've already read the errors and searched Google, you should ask for help. - h3 This will take you to our help room. - a.btn.btn-lg.btn-primary.btn-block.close-modal(href='https://gitter.im/FreeCodeCamp/help', target='_blank') Take me to the help room - a.btn.btn-lg.btn-info.btn-block(href='#', data-dismiss='modal', aria-hidden='true') Cancel - #reset-modal.modal(tabindex='-1') .modal-dialog.animated.fadeInUp.fast-animation .modal-content diff --git a/server/views/partials/footer.jade b/server/views/partials/footer.jade index 8bdc9e80b7..025b59a647 100644 --- a/server/views/partials/footer.jade +++ b/server/views/partials/footer.jade @@ -22,3 +22,6 @@ span.sr-only Free Code Camp on Twitter a.ion-locked(href="//github.com/FreeCodeCamp/freecodecamp/wiki/Free-Code-Camp's-Privacy-Policy") span.sr-only Free Code Camp's Privacy Policy + +// scripts should be moved here +script(src="https://sidecar.gitter.im/dist/sidecar.v1.js" async defer) diff --git a/server/views/partials/navbar.jade b/server/views/partials/navbar.jade index 18f3320315..a53dd35892 100644 --- a/server/views/partials/navbar.jade +++ b/server/views/partials/navbar.jade @@ -11,8 +11,10 @@ nav.navbar.navbar-default.navbar-fixed-top.nav-height a.learn-btn(href='#') Learn li a(href='/map') Map - li - a(href='//gitter.im/FreeCodeCamp/FreeCodeCamp', target='_blank') Chat + li.hidden-xs + a#nav-chat-btn(href='#' onclick="return false") Chat + li.visible-xs + a(href="//gitter.im/freecodecamp/freecodecamp" target="_blank") Chat li a(href='/news', target='_blank') News li