Merge pull request #11593 from BerkeleyTrue/feat/no-sidecar-map
feat(nav): remove sidecar
This commit is contained in:
@ -1,25 +0,0 @@
|
|||||||
.chat-embed-help-title,
|
|
||||||
.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%);
|
|
||||||
}
|
|
@ -1,137 +0,0 @@
|
|||||||
/*
|
|
||||||
* based off of https://github.com/gitterHQ/sidecar
|
|
||||||
* license: MIT
|
|
||||||
*/
|
|
||||||
.drawer {
|
|
||||||
width:500px;
|
|
||||||
|
|
||||||
z-index: 20000;
|
|
||||||
position: fixed;
|
|
||||||
top: 0;
|
|
||||||
bottom: 0;
|
|
||||||
right: 0;
|
|
||||||
|
|
||||||
display: -webkit-flex;
|
|
||||||
display: flex;
|
|
||||||
-webkit-flex-direction: row;
|
|
||||||
flex-direction: row;
|
|
||||||
background-color: @body-bg;
|
|
||||||
border-left: 1px solid #ddd;
|
|
||||||
box-shadow: -12px 0 18px 0 rgba(50, 50, 50, 0.1);
|
|
||||||
|
|
||||||
transition: transform 0.3s cubic-bezier(0.16, 0.22, 0.22, 1.7);
|
|
||||||
|
|
||||||
&.is-collapsed:not(.is-loading) {
|
|
||||||
-webkit-transform: translateX(110%);
|
|
||||||
transform: translateX(110%);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Add some "extension" so that there isn't a gap
|
|
||||||
* when we translate(via animation) more than 100% */
|
|
||||||
&:after {
|
|
||||||
content: '';
|
|
||||||
|
|
||||||
z-index: -1;
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 100%;
|
|
||||||
bottom: 0;
|
|
||||||
right: -100%;
|
|
||||||
|
|
||||||
background-color: @body-bg;
|
|
||||||
}
|
|
||||||
iframe {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
h2 > a {
|
|
||||||
font-size: 20px;
|
|
||||||
}
|
|
||||||
a > h3 {
|
|
||||||
font-size: 18px;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
p {
|
|
||||||
font-size: 16px;
|
|
||||||
margin-left: 32px !important;
|
|
||||||
}
|
|
||||||
> .challenge-block-time {
|
|
||||||
width: 100%;
|
|
||||||
padding-top: 5px;
|
|
||||||
}
|
|
||||||
@media (max-width: 720px) {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.drawer-action-bar {
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
right: 0;
|
|
||||||
|
|
||||||
display: -webkit-flex;
|
|
||||||
display: flex;
|
|
||||||
justify-content: flex-end;
|
|
||||||
|
|
||||||
padding-bottom: 5px;
|
|
||||||
padding-right:10px;
|
|
||||||
padding-top:5px;
|
|
||||||
z-index: 100;
|
|
||||||
}
|
|
||||||
|
|
||||||
.drawer-action-item {
|
|
||||||
display: -webkit-flex;
|
|
||||||
display: flex;
|
|
||||||
/* main axis */
|
|
||||||
justify-content: center;
|
|
||||||
/* cross axis */
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
width: 40px;
|
|
||||||
height: 40px;
|
|
||||||
|
|
||||||
padding-left: 0;
|
|
||||||
padding-right: 0;
|
|
||||||
|
|
||||||
opacity: 0.65;
|
|
||||||
background: none;
|
|
||||||
background-position: center center;
|
|
||||||
background-repeat: no-repeat;
|
|
||||||
background-size: 22px 22px;
|
|
||||||
border: 0;
|
|
||||||
outline: none;
|
|
||||||
|
|
||||||
cursor: pointer;
|
|
||||||
cursor: hand;
|
|
||||||
|
|
||||||
transition: all 0.2s ease;
|
|
||||||
|
|
||||||
&:hover,
|
|
||||||
&:focus {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:active {
|
|
||||||
filter: hue-rotate(80deg) saturate(150);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.drawer-action-pop-out {
|
|
||||||
margin-right: -4px;
|
|
||||||
background-image: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyMDAgMTcxLjQyOSIgZmlsbD0iIzNhMzEzMyI+PHBhdGggZD0iTTE1Ny4xNDMsMTAzLjU3MXYzNS43MTRjMCw4Ljg1NC0zLjE0NCwxNi40MjYtOS40MzEsMjIuNzEzcy0xMy44NTgsOS40MzEtMjIuNzEyLDkuNDMxSDMyLjE0MyBjLTguODU0LDAtMTYuNDI1LTMuMTQ0LTIyLjcxMi05LjQzMVMwLDE0OC4xNCwwLDEzOS4yODVWNDYuNDI5YzAtOC44NTQsMy4xNDQtMTYuNDI1LDkuNDMxLTIyLjcxMiBjNi4yODctNi4yODcsMTMuODU4LTkuNDMxLDIyLjcxMi05LjQzMWg3OC41NzJjMS4wNDEsMCwxLjg5NiwwLjMzNSwyLjU2NiwxLjAwNGMwLjY3LDAuNjcsMS4wMDQsMS41MjUsMS4wMDQsMi41NjdWMjUgYzAsMS4wNDItMC4zMzQsMS44OTctMS4wMDQsMi41NjdjLTAuNjcsMC42Ny0xLjUyNSwxLjAwNC0yLjU2NiwxLjAwNEgzMi4xNDNjLTQuOTExLDAtOS4xMTUsMS43NDktMTIuNjEyLDUuMjQ2IHMtNS4yNDYsNy43MDEtNS4yNDYsMTIuNjEydjkyLjg1NmMwLDQuOTExLDEuNzQ5LDkuMTE1LDUuMjQ2LDEyLjYxMnM3LjcwMSw1LjI0NSwxMi42MTIsNS4yNDVIMTI1YzQuOTEsMCw5LjExNS0xLjc0OCwxMi42MTEtNS4yNDUgYzMuNDk3LTMuNDk3LDUuMjQ2LTcuNzAxLDUuMjQ2LTEyLjYxMnYtMzUuNzE0YzAtMS4wNDIsMC4zMzQtMS44OTcsMS4wMDQtMi41NjdjMC42Ny0wLjY2OSwxLjUyNS0xLjAwNCwyLjU2Ny0xLjAwNGg3LjE0MyBjMS4wNDIsMCwxLjg5NywwLjMzNSwyLjU2NywxLjAwNEMxNTYuODA5LDEwMS42NzQsMTU3LjE0MywxMDIuNTI5LDE1Ny4xNDMsMTAzLjU3MXogTTIwMCw3LjE0M3Y1Ny4xNDMgYzAsMS45MzUtMC43MDcsMy42MDktMi4xMjEsNS4wMjJjLTEuNDEzLDEuNDE0LTMuMDg4LDIuMTIxLTUuMDIxLDIuMTIxYy0xLjkzNSwwLTMuNjA5LTAuNzA3LTUuMDIyLTIuMTIxbC0xOS42NDQtMTkuNjQzIGwtNzIuNzY3LDcyLjc2OWMtMC43NDQsMC43NDQtMS42LDEuMTE1LTIuNTY3LDEuMTE1cy0xLjgyMy0wLjM3MS0yLjU2Ny0xLjExNUw3Ny41NjcsMTA5LjcxYy0wLjc0NC0wLjc0NC0xLjExNi0xLjYtMS4xMTYtMi41NjcgYzAtMC45NjcsMC4zNzItMS44MjIsMS4xMTYtMi41NjZsNzIuNzY4LTcyLjc2OGwtMTkuNjQ0LTE5LjY0M2MtMS40MTMtMS40MTQtMi4xMi0zLjA4OC0yLjEyLTUuMDIyYzAtMS45MzUsMC43MDctMy42MDksMi4xMi01LjAyMiBDMTMyLjEwNSwwLjcwNywxMzMuNzc5LDAsMTM1LjcxNSwwaDU3LjE0M2MxLjkzNCwwLDMuNjA4LDAuNzA3LDUuMDIxLDIuMTIxQzE5OS4yOTMsMy41MzQsMjAwLDUuMjA4LDIwMCw3LjE0M3oiLz48L3N2Zz4=)
|
|
||||||
}
|
|
||||||
|
|
||||||
.drawer-action-collapse {
|
|
||||||
background-image: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxNzEuNDI5IDE3MS40MjkiIGZpbGw9IiMzYTMxMzMiPjxwYXRoIGQ9Ik0xMjIuNDMzLDEwNi4xMzhsLTE2LjI5NSwxNi4yOTVjLTAuNzQ0LDAuNzQ0LTEuNiwxLjExNi0yLjU2NiwxLjExNmMtMC45NjgsMC0xLjgyMy0wLjM3Mi0yLjU2Ny0xLjExNmwtMTUuMjktMTUuMjkgbC0xNS4yOSwxNS4yOWMtMC43NDQsMC43NDQtMS42LDEuMTE2LTIuNTY3LDEuMTE2cy0xLjgyMy0wLjM3Mi0yLjU2Ny0xLjExNmwtMTYuMjk0LTE2LjI5NWMtMC43NDQtMC43NDQtMS4xMTYtMS42LTEuMTE2LTIuNTY2IGMwLTAuOTY4LDAuMzcyLTEuODIzLDEuMTE2LTIuNTY3bDE1LjI5LTE1LjI5bC0xNS4yOS0xNS4yOWMtMC43NDQtMC43NDQtMS4xMTYtMS42LTEuMTE2LTIuNTY3czAuMzcyLTEuODIzLDEuMTE2LTIuNTY3IEw2NS4yOSw0OC45OTZjMC43NDQtMC43NDQsMS42LTEuMTE2LDIuNTY3LTEuMTE2czEuODIzLDAuMzcyLDIuNTY3LDEuMTE2bDE1LjI5LDE1LjI5bDE1LjI5LTE1LjI5IGMwLjc0NC0wLjc0NCwxLjYtMS4xMTYsMi41NjctMS4xMTZjMC45NjcsMCwxLjgyMiwwLjM3MiwyLjU2NiwxLjExNmwxNi4yOTUsMTYuMjk0YzAuNzQ0LDAuNzQ0LDEuMTE2LDEuNiwxLjExNiwyLjU2NyBzLTAuMzcyLDEuODIzLTEuMTE2LDIuNTY3bC0xNS4yOSwxNS4yOWwxNS4yOSwxNS4yOWMwLjc0NCwwLjc0NCwxLjExNiwxLjYsMS4xMTYsMi41NjcgQzEyMy41NDksMTA0LjUzOSwxMjMuMTc3LDEwNS4zOTQsMTIyLjQzMywxMDYuMTM4eiBNMTQ2LjQyOSw4NS43MTRjMC0xMS4wMTItMi43MTctMjEuMTY4LTguMTQ4LTMwLjQ2OSBzLTEyLjc5Ny0xNi42NjctMjIuMDk4LTIyLjA5OFM5Ni43MjYsMjUsODUuNzE0LDI1cy0yMS4xNjgsMi43MTYtMzAuNDY5LDguMTQ3UzM4LjU3OSw0NS45NDUsMzMuMTQ3LDU1LjI0NlMyNSw3NC43MDMsMjUsODUuNzE0IHMyLjcxNiwyMS4xNjgsOC4xNDcsMzAuNDY5czEyLjc5NywxNi42NjYsMjIuMDk4LDIyLjA5OHMxOS40NTcsOC4xNDgsMzAuNDY5LDguMTQ4czIxLjE2OC0yLjcxNywzMC40NjktOC4xNDggczE2LjY2Ni0xMi43OTcsMjIuMDk4LTIyLjA5OFMxNDYuNDI5LDk2LjcyNiwxNDYuNDI5LDg1LjcxNHogTTE3MS40MjksODUuNzE0YzAsMTUuNTUxLTMuODMyLDI5Ljg5My0xMS40OTYsNDMuMDI0IGMtNy42NjQsMTMuMTMzLTE4LjA2MiwyMy41My0zMS4xOTQsMzEuMTk0Yy0xMy4xMzIsNy42NjQtMjcuNDc0LDExLjQ5Ni00My4wMjQsMTEuNDk2cy0yOS44OTItMy44MzItNDMuMDI0LTExLjQ5NiBjLTEzLjEzMy03LjY2NC0yMy41MzEtMTguMDYyLTMxLjE5NC0zMS4xOTRDMy44MzIsMTE1LjYwNywwLDEwMS4yNjUsMCw4NS43MTRTMy44MzIsNTUuODIyLDExLjQ5Niw0Mi42OSBjNy42NjQtMTMuMTMzLDE4LjA2Mi0yMy41MzEsMzEuMTk0LTMxLjE5NEM1NS44MjIsMy44MzIsNzAuMTY0LDAsODUuNzE0LDBzMjkuODkzLDMuODMyLDQzLjAyNCwxMS40OTYgYzEzLjEzMyw3LjY2NCwyMy41MywxOC4wNjIsMzEuMTk0LDMxLjE5NEMxNjcuNTk3LDU1LjgyMiwxNzEuNDI5LDcwLjE2NCwxNzEuNDI5LDg1LjcxNHoiLz48L3N2Zz4=)
|
|
||||||
}
|
|
||||||
|
|
||||||
.night {
|
|
||||||
.drawer {
|
|
||||||
background-color: @night-body-bg;
|
|
||||||
}
|
|
||||||
.drawer-action-item{
|
|
||||||
filter: brightness(4) saturate(0);
|
|
||||||
-webkit-filter: brightness(4) saturate(0);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1195,11 +1195,9 @@ and (max-width : 400px) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@import "chat.less";
|
@import "code-mirror.less";
|
||||||
@import 'code-mirror.less';
|
|
||||||
@import "challenge.less";
|
@import "challenge.less";
|
||||||
@import "toastr.less";
|
@import "toastr.less";
|
||||||
@import "map.less";
|
@import "map.less";
|
||||||
@import "drawers.less";
|
|
||||||
@import "sk-wave.less";
|
@import "sk-wave.less";
|
||||||
@import "classic-modal.less";
|
@import "classic-modal.less";
|
||||||
|
@ -224,57 +224,10 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.map-aside-body {
|
|
||||||
.map-fixed-header {
|
|
||||||
p {
|
|
||||||
margin-top: 0;
|
|
||||||
font-size: 16px;
|
|
||||||
}
|
|
||||||
.row.map-buttons {
|
|
||||||
float: none;
|
|
||||||
width: 100%;
|
|
||||||
text-align: center;
|
|
||||||
button {
|
|
||||||
width: 300px;
|
|
||||||
}
|
|
||||||
.input-group {
|
|
||||||
width: 300px;
|
|
||||||
padding-top: 5px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.map-buttons:first-of-type {
|
|
||||||
float: none;
|
|
||||||
padding-left: 0;
|
|
||||||
}
|
|
||||||
.map-accordion {
|
|
||||||
top: 160px;
|
|
||||||
h2 {
|
|
||||||
margin: 15px 0;
|
|
||||||
a {
|
|
||||||
padding: 10px 0;
|
|
||||||
padding-left: 50px;
|
|
||||||
padding-right: 20px;
|
|
||||||
font-size: 20px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
h3 > a {
|
|
||||||
font-size: 20px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.night {
|
.night {
|
||||||
.map-fixed-header {
|
.map-fixed-header {
|
||||||
background-color: @night-body-bg;
|
background-color: @night-body-bg;
|
||||||
}
|
}
|
||||||
.map-aside {
|
|
||||||
border-left-color:#222;
|
|
||||||
&-action-item {
|
|
||||||
filter: brightness(2) saturate(0);
|
|
||||||
-webkit-filter: brightness(2) saturate(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#map-filter, .input-group-addon {
|
#map-filter, .input-group-addon {
|
||||||
border-color: #292929;
|
border-color: #292929;
|
||||||
background-color: #666;
|
background-color: #666;
|
||||||
|
130
client/main.js
130
client/main.js
@ -2,136 +2,6 @@ var main = window.main || {};
|
|||||||
|
|
||||||
main.ga = window.ga || function() {};
|
main.ga = window.ga || function() {};
|
||||||
|
|
||||||
main = (function(main, global) {
|
|
||||||
const { Mousetrap } = global;
|
|
||||||
|
|
||||||
// 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) {
|
|
||||||
// room is always in PascalCase
|
|
||||||
roomTitle = room
|
|
||||||
.replace(/([A-Z])/g, ' $1')
|
|
||||||
.replace('Java Script', 'JavaScript');
|
|
||||||
|
|
||||||
$('body').append(
|
|
||||||
'<aside id="chat-embed-help" class="gitter-chat-embed is-collapsed" />'
|
|
||||||
);
|
|
||||||
|
|
||||||
main.chat.helpChat = new main.chat.GitterChat({
|
|
||||||
room: `freecodecamp/${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(
|
|
||||||
'<div class="chat-embed-main-title">' +
|
|
||||||
'<span>' +
|
|
||||||
roomTitle +
|
|
||||||
'</span>' +
|
|
||||||
'</div>'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (shouldButtonBePressed) {
|
|
||||||
return $(helpChatBtnClass).addClass('active');
|
|
||||||
}
|
|
||||||
return $(helpChatBtnClass).removeClass('active');
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
$('body').append(
|
|
||||||
'<aside id="chat-embed-main" class="gitter-chat-embed is-collapsed" />'
|
|
||||||
);
|
|
||||||
|
|
||||||
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;
|
|
||||||
if ($('body').hasClass('night')) {
|
|
||||||
$('#chat-embed-main').addClass('night');
|
|
||||||
}
|
|
||||||
$('#chat-embed-main > .gitter-chat-embed-action-bar').prepend(
|
|
||||||
'<div class="chat-embed-main-title">' +
|
|
||||||
'<span>Free Code Camp\'s Main Chat</span>' +
|
|
||||||
'</div>'
|
|
||||||
);
|
|
||||||
return null;
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
$('#nav-chat-btn').on('click', function(event) {
|
|
||||||
if (!(event.ctrlKey || event.metaKey)) {
|
|
||||||
toggleMainChat();
|
|
||||||
}
|
|
||||||
window.ga('send', 'event', 'Nav', 'clicked', 'Nav chat opened');
|
|
||||||
});
|
|
||||||
|
|
||||||
function showMainChat() {
|
|
||||||
if (!main.chat.isOpen) {
|
|
||||||
main.chat.mainChat.toggleChat(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function collapseMainChat() {
|
|
||||||
$('#chat-embed-main').addClass('is-collapsed');
|
|
||||||
document.activeElement.blur();
|
|
||||||
}
|
|
||||||
|
|
||||||
function toggleMainChat() {
|
|
||||||
var isCollapsed = $('#chat-embed-main').hasClass('is-collapsed');
|
|
||||||
|
|
||||||
if (isCollapsed) {
|
|
||||||
showMainChat();
|
|
||||||
} else {
|
|
||||||
collapseMainChat();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// keyboard shortcuts: open main chat
|
|
||||||
Mousetrap.bind('g c', toggleMainChat);
|
|
||||||
});
|
|
||||||
|
|
||||||
return main;
|
|
||||||
}(main, window));
|
|
||||||
|
|
||||||
$(document).ready(function() {
|
$(document).ready(function() {
|
||||||
|
|
||||||
const { Observable } = window.Rx;
|
const { Observable } = window.Rx;
|
||||||
|
@ -1,178 +0,0 @@
|
|||||||
import { Subject, Observable } from 'rx';
|
|
||||||
import Chat from 'gitter-sidecar';
|
|
||||||
import types from '../../common/app/redux/types';
|
|
||||||
import {
|
|
||||||
openHelpChat,
|
|
||||||
closeHelpChat,
|
|
||||||
toggleMainChat
|
|
||||||
} from '../../common/app/redux/actions';
|
|
||||||
|
|
||||||
export function createHeader(room, title, document) {
|
|
||||||
const type = room === 'freecodecamp' ? 'main' : 'help';
|
|
||||||
const div = document.createElement('div');
|
|
||||||
const span = document.createElement('span');
|
|
||||||
const actionBar = document.querySelector(
|
|
||||||
`#chat-embed-${type}> .gitter-chat-embed-action-bar`
|
|
||||||
);
|
|
||||||
span.appendChild(document.createTextNode(title));
|
|
||||||
div.className = `chat-embed-${type}-title`;
|
|
||||||
div.appendChild(span);
|
|
||||||
actionBar.insertBefore(div, actionBar.firstChild);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function createHelpContainer(document) {
|
|
||||||
const container = document.createElement('aside');
|
|
||||||
container.id = 'chat-embed-help';
|
|
||||||
container.className = 'gitter-chat-embed is-collapsed';
|
|
||||||
document.body.appendChild(container);
|
|
||||||
return container;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function createMainChat(getState, document) {
|
|
||||||
let mainChatTitleAdded = false;
|
|
||||||
const mainChatContainer = document.createElement('aside');
|
|
||||||
mainChatContainer.id = 'chat-embed-main';
|
|
||||||
mainChatContainer.className = 'gitter-chat-embed is-collapsed';
|
|
||||||
document.body.appendChild(mainChatContainer);
|
|
||||||
const mainChat = new Chat({
|
|
||||||
room: 'freecodecamp/freecodecamp',
|
|
||||||
activationElement: false,
|
|
||||||
targetElement: mainChatContainer
|
|
||||||
});
|
|
||||||
|
|
||||||
const toggle$ = Observable.fromEventPattern(
|
|
||||||
h => mainChatContainer.addEventListener('gitter-chat-toggle', h),
|
|
||||||
h => mainChatContainer.removeEventListener('gitter-chat-toggle', h)
|
|
||||||
)
|
|
||||||
.map(e => {
|
|
||||||
const { isMainChatOpen } = getState().app;
|
|
||||||
if (!mainChatTitleAdded) {
|
|
||||||
mainChatTitleAdded = true;
|
|
||||||
createHeader('freecodecamp', 'Free Code Camp\'s Main Chat', document);
|
|
||||||
}
|
|
||||||
if (isMainChatOpen === e.detail.state) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return toggleMainChat();
|
|
||||||
});
|
|
||||||
return {
|
|
||||||
mainChat,
|
|
||||||
toggle$
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// only one help room may be alive at once
|
|
||||||
export function createHelpChat(room, container, proxy, document) {
|
|
||||||
const title = room.replace(/([A-Z])/g, ' $1');
|
|
||||||
let isTitleAdded = false;
|
|
||||||
const chat = new Chat({
|
|
||||||
room: `freecodecamp/${room}`,
|
|
||||||
activationElement: false,
|
|
||||||
targetElement: container
|
|
||||||
});
|
|
||||||
// return subscription to toggle stream
|
|
||||||
// dispose when rooms switch
|
|
||||||
const subscription = Observable.fromEventPattern(
|
|
||||||
h => container.addEventListener('gitter-chat-toggle', h),
|
|
||||||
h => container.removeEventListener('gitter-chat-toggle', h)
|
|
||||||
)
|
|
||||||
.map(e => {
|
|
||||||
if (!isTitleAdded) {
|
|
||||||
isTitleAdded = true;
|
|
||||||
createHeader(room, title, document);
|
|
||||||
}
|
|
||||||
const gitterState = e.detail.state;
|
|
||||||
return gitterState ? openHelpChat() : closeHelpChat();
|
|
||||||
})
|
|
||||||
// use subject proxy to dispatch actions
|
|
||||||
.subscribe(proxy);
|
|
||||||
return { chat, subscription };
|
|
||||||
}
|
|
||||||
|
|
||||||
export const cache = {};
|
|
||||||
export function toggleHelpChat(isOpen, room, proxy, document) {
|
|
||||||
// check is container is already created
|
|
||||||
if (!cache['container']) {
|
|
||||||
cache['container'] = createHelpContainer(document);
|
|
||||||
}
|
|
||||||
const { container } = cache;
|
|
||||||
if (!cache['chat']) {
|
|
||||||
const {
|
|
||||||
chat,
|
|
||||||
subscription
|
|
||||||
} = createHelpChat(room, container, proxy, document);
|
|
||||||
cache.chat = chat;
|
|
||||||
// make sure we clear out old subscription
|
|
||||||
if (cache.subscription && cache.subscription.dispose) {
|
|
||||||
cache.subscription.dispose();
|
|
||||||
}
|
|
||||||
cache.subscription = subscription;
|
|
||||||
cache.currentRoom = room;
|
|
||||||
}
|
|
||||||
// have we switched rooms?
|
|
||||||
if (!cache.currentRoom === room) {
|
|
||||||
// room has changed, if chat object exist, destroy it
|
|
||||||
// and end subscription to toggle
|
|
||||||
try {
|
|
||||||
cache.chat.destroy();
|
|
||||||
cache.subscription.dispose();
|
|
||||||
// chat and subscription may not exist at first so we catch errors here
|
|
||||||
} catch (err) {
|
|
||||||
console.error(err);
|
|
||||||
}
|
|
||||||
// create new chat room and cache
|
|
||||||
const {
|
|
||||||
chat,
|
|
||||||
subscription
|
|
||||||
} = createHelpChat(room, container, proxy, document);
|
|
||||||
cache.chat = chat;
|
|
||||||
cache.subscription = subscription;
|
|
||||||
cache.currentRoom = room;
|
|
||||||
}
|
|
||||||
// all goes well pull chat object from cache
|
|
||||||
const { chat } = cache;
|
|
||||||
chat.toggleChat(isOpen);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function gitterSaga(actions$, getState, { document }) {
|
|
||||||
const helpToggleProxy = new Subject();
|
|
||||||
const {
|
|
||||||
mainChat,
|
|
||||||
toggle$: mainChatToggle$
|
|
||||||
} = createMainChat(getState, document);
|
|
||||||
return Observable.merge(
|
|
||||||
mainChatToggle$,
|
|
||||||
helpToggleProxy,
|
|
||||||
actions$
|
|
||||||
.filter(({ type }) => (
|
|
||||||
type === types.openMainChat ||
|
|
||||||
type === types.closeMainChat ||
|
|
||||||
type === types.toggleMainChat ||
|
|
||||||
type === types.toggleHelpChat
|
|
||||||
))
|
|
||||||
.map(({ type }) => {
|
|
||||||
const state = getState();
|
|
||||||
let shouldBlur = false;
|
|
||||||
if (type === types.toggleHelpChat) {
|
|
||||||
const {
|
|
||||||
app: { isHelpChatOpen },
|
|
||||||
challengesApp: { helpChatRoom }
|
|
||||||
} = state;
|
|
||||||
shouldBlur = !isHelpChatOpen;
|
|
||||||
toggleHelpChat(
|
|
||||||
isHelpChatOpen,
|
|
||||||
helpChatRoom,
|
|
||||||
helpToggleProxy,
|
|
||||||
document
|
|
||||||
);
|
|
||||||
}
|
|
||||||
const { isMainChatOpen } = state.app;
|
|
||||||
mainChat.toggleChat(isMainChatOpen);
|
|
||||||
shouldBlur = !isMainChatOpen;
|
|
||||||
if (!shouldBlur) {
|
|
||||||
document.activeElement.blur();
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
@ -5,7 +5,6 @@ import windowSaga from './window-saga';
|
|||||||
import executeChallengeSaga from './execute-challenge-saga';
|
import executeChallengeSaga from './execute-challenge-saga';
|
||||||
import frameSaga from './frame-saga';
|
import frameSaga from './frame-saga';
|
||||||
import codeStorageSaga from './code-storage-saga';
|
import codeStorageSaga from './code-storage-saga';
|
||||||
import gitterSaga from './gitter-saga';
|
|
||||||
import mouseTrapSaga from './mouse-trap-saga';
|
import mouseTrapSaga from './mouse-trap-saga';
|
||||||
import analyticsSaga from './analytics-saga';
|
import analyticsSaga from './analytics-saga';
|
||||||
import nightModeSaga from './night-mode-saga';
|
import nightModeSaga from './night-mode-saga';
|
||||||
@ -18,7 +17,6 @@ export default [
|
|||||||
executeChallengeSaga,
|
executeChallengeSaga,
|
||||||
frameSaga,
|
frameSaga,
|
||||||
codeStorageSaga,
|
codeStorageSaga,
|
||||||
gitterSaga,
|
|
||||||
mouseTrapSaga,
|
mouseTrapSaga,
|
||||||
analyticsSaga,
|
analyticsSaga,
|
||||||
nightModeSaga
|
nightModeSaga
|
||||||
|
@ -3,12 +3,10 @@ import MouseTrap from 'mousetrap';
|
|||||||
import { push } from 'react-router-redux';
|
import { push } from 'react-router-redux';
|
||||||
import {
|
import {
|
||||||
toggleNightMode,
|
toggleNightMode,
|
||||||
toggleMapDrawer,
|
|
||||||
toggleMainChat,
|
|
||||||
hardGoTo
|
hardGoTo
|
||||||
} from '../../common/app/redux/actions';
|
} from '../../common/app/redux/actions';
|
||||||
|
|
||||||
function bindKey$(key, actionCreator) {
|
function bindKey(key, actionCreator) {
|
||||||
return Observable.fromEventPattern(
|
return Observable.fromEventPattern(
|
||||||
h => MouseTrap.bind(key, h),
|
h => MouseTrap.bind(key, h),
|
||||||
h => MouseTrap.unbind(key, h)
|
h => MouseTrap.unbind(key, h)
|
||||||
@ -27,18 +25,16 @@ const softRedirects = {
|
|||||||
export default function mouseTrapSaga(actions$) {
|
export default function mouseTrapSaga(actions$) {
|
||||||
const traps$ = [
|
const traps$ = [
|
||||||
...Object.keys(softRedirects)
|
...Object.keys(softRedirects)
|
||||||
.map(key => bindKey$(key, () => push(softRedirects[key]))),
|
.map(key => bindKey(key, () => push(softRedirects[key]))),
|
||||||
bindKey$(
|
bindKey(
|
||||||
'g n r',
|
'g n r',
|
||||||
() => hardGoTo('https://github.com/freecodecamp/freecodecamp')
|
() => hardGoTo('https://github.com/freecodecamp/freecodecamp')
|
||||||
),
|
),
|
||||||
bindKey$(
|
bindKey(
|
||||||
'g n w',
|
'g n w',
|
||||||
() => hardGoTo('http://forum.freecodecamp.com')
|
() => hardGoTo('http://forum.freecodecamp.com')
|
||||||
),
|
),
|
||||||
bindKey$('g m', toggleMapDrawer),
|
bindKey('g t n', toggleNightMode)
|
||||||
bindKey$('g t n', toggleNightMode),
|
|
||||||
bindKey$('g c', toggleMainChat)
|
|
||||||
];
|
];
|
||||||
return Observable.merge(traps$).takeUntil(actions$.last());
|
return Observable.merge(traps$).takeUntil(actions$.last());
|
||||||
}
|
}
|
||||||
|
@ -3,13 +3,10 @@ import { Button, Row } from 'react-bootstrap';
|
|||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { createSelector } from 'reselect';
|
import { createSelector } from 'reselect';
|
||||||
|
|
||||||
import MapDrawer from './components/Map-Drawer.jsx';
|
|
||||||
import {
|
import {
|
||||||
fetchUser,
|
fetchUser,
|
||||||
initWindowHeight,
|
initWindowHeight,
|
||||||
updateNavHeight,
|
updateNavHeight,
|
||||||
toggleMapDrawer,
|
|
||||||
toggleMainChat,
|
|
||||||
updateAppLang,
|
updateAppLang,
|
||||||
trackEvent,
|
trackEvent,
|
||||||
loadCurrentChallenge
|
loadCurrentChallenge
|
||||||
@ -26,8 +23,6 @@ const mapDispatchToProps = {
|
|||||||
updateNavHeight,
|
updateNavHeight,
|
||||||
fetchUser,
|
fetchUser,
|
||||||
submitChallenge,
|
submitChallenge,
|
||||||
toggleMapDrawer,
|
|
||||||
toggleMainChat,
|
|
||||||
updateAppLang,
|
updateAppLang,
|
||||||
trackEvent,
|
trackEvent,
|
||||||
loadCurrentChallenge
|
loadCurrentChallenge
|
||||||
@ -37,23 +32,17 @@ const mapStateToProps = createSelector(
|
|||||||
userSelector,
|
userSelector,
|
||||||
state => state.app.isSignInAttempted,
|
state => state.app.isSignInAttempted,
|
||||||
state => state.app.toast,
|
state => state.app.toast,
|
||||||
state => state.app.isMapDrawerOpen,
|
|
||||||
state => state.app.isMapAlreadyLoaded,
|
|
||||||
state => state.challengesApp.toast,
|
state => state.challengesApp.toast,
|
||||||
(
|
(
|
||||||
{ user: { username, points, picture } },
|
{ user: { username, points, picture } },
|
||||||
isSignInAttempted,
|
isSignInAttempted,
|
||||||
toast,
|
toast,
|
||||||
isMapDrawerOpen,
|
|
||||||
isMapAlreadyLoaded,
|
|
||||||
) => ({
|
) => ({
|
||||||
username,
|
username,
|
||||||
points,
|
points,
|
||||||
picture,
|
picture,
|
||||||
toast,
|
toast,
|
||||||
showLoading: !isSignInAttempted,
|
showLoading: !isSignInAttempted,
|
||||||
isMapDrawerOpen,
|
|
||||||
isMapAlreadyLoaded,
|
|
||||||
isSignedIn: !!username
|
isSignedIn: !!username
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
@ -68,10 +57,6 @@ const propTypes = {
|
|||||||
updateNavHeight: PropTypes.func,
|
updateNavHeight: PropTypes.func,
|
||||||
initWindowHeight: PropTypes.func,
|
initWindowHeight: PropTypes.func,
|
||||||
submitChallenge: PropTypes.func,
|
submitChallenge: PropTypes.func,
|
||||||
isMapDrawerOpen: PropTypes.bool,
|
|
||||||
isMapAlreadyLoaded: PropTypes.bool,
|
|
||||||
toggleMapDrawer: PropTypes.func,
|
|
||||||
toggleMainChat: PropTypes.func,
|
|
||||||
fetchUser: PropTypes.func,
|
fetchUser: PropTypes.func,
|
||||||
showLoading: PropTypes.bool,
|
showLoading: PropTypes.bool,
|
||||||
params: PropTypes.object,
|
params: PropTypes.object,
|
||||||
@ -79,7 +64,6 @@ const propTypes = {
|
|||||||
trackEvent: PropTypes.func.isRequired,
|
trackEvent: PropTypes.func.isRequired,
|
||||||
loadCurrentChallenge: PropTypes.func.isRequired
|
loadCurrentChallenge: PropTypes.func.isRequired
|
||||||
};
|
};
|
||||||
const contextTypes = { router: PropTypes.object };
|
|
||||||
|
|
||||||
// export plain class for testing
|
// export plain class for testing
|
||||||
export class FreeCodeCamp extends React.Component {
|
export class FreeCodeCamp extends React.Component {
|
||||||
@ -112,30 +96,19 @@ export class FreeCodeCamp extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { router } = this.context;
|
|
||||||
const {
|
const {
|
||||||
username,
|
username,
|
||||||
points,
|
points,
|
||||||
picture,
|
picture,
|
||||||
updateNavHeight,
|
updateNavHeight,
|
||||||
isMapDrawerOpen,
|
|
||||||
isMapAlreadyLoaded,
|
|
||||||
toggleMapDrawer,
|
|
||||||
toggleMainChat,
|
|
||||||
showLoading,
|
|
||||||
params: { lang },
|
|
||||||
trackEvent,
|
trackEvent,
|
||||||
loadCurrentChallenge
|
loadCurrentChallenge
|
||||||
} = this.props;
|
} = this.props;
|
||||||
const navProps = {
|
const navProps = {
|
||||||
isOnMap: router.isActive(`/${lang}/map`),
|
|
||||||
username,
|
username,
|
||||||
points,
|
points,
|
||||||
picture,
|
picture,
|
||||||
updateNavHeight,
|
updateNavHeight,
|
||||||
toggleMapDrawer,
|
|
||||||
toggleMainChat,
|
|
||||||
showLoading,
|
|
||||||
trackEvent,
|
trackEvent,
|
||||||
loadCurrentChallenge
|
loadCurrentChallenge
|
||||||
};
|
};
|
||||||
@ -146,11 +119,6 @@ export class FreeCodeCamp extends React.Component {
|
|||||||
<Row>
|
<Row>
|
||||||
{ this.props.children }
|
{ this.props.children }
|
||||||
</Row>
|
</Row>
|
||||||
<MapDrawer
|
|
||||||
isAlreadyLoaded={ isMapAlreadyLoaded }
|
|
||||||
isOpen={ isMapDrawerOpen }
|
|
||||||
toggleMapDrawer={ toggleMapDrawer }
|
|
||||||
/>
|
|
||||||
<Toasts />
|
<Toasts />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
@ -158,7 +126,6 @@ export class FreeCodeCamp extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
FreeCodeCamp.displayName = 'FreeCodeCamp';
|
FreeCodeCamp.displayName = 'FreeCodeCamp';
|
||||||
FreeCodeCamp.contextTypes = contextTypes;
|
|
||||||
FreeCodeCamp.propTypes = propTypes;
|
FreeCodeCamp.propTypes = propTypes;
|
||||||
|
|
||||||
export default connect(
|
export default connect(
|
||||||
|
@ -1,49 +0,0 @@
|
|||||||
import React, { PropTypes } from 'react';
|
|
||||||
import classnames from 'classnames';
|
|
||||||
|
|
||||||
export default class Drawer extends React.Component {
|
|
||||||
static displayName = 'Drawer';
|
|
||||||
static propTypes = {
|
|
||||||
children: PropTypes.node,
|
|
||||||
isOpen: PropTypes.bool,
|
|
||||||
closeDrawer: PropTypes.func,
|
|
||||||
closeAria: PropTypes.string,
|
|
||||||
newTabLink: PropTypes.string,
|
|
||||||
newTabAria: PropTypes.string
|
|
||||||
};
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const {
|
|
||||||
isOpen,
|
|
||||||
closeDrawer,
|
|
||||||
closeAria,
|
|
||||||
children,
|
|
||||||
newTabAria,
|
|
||||||
newTabLink
|
|
||||||
} = this.props;
|
|
||||||
const drawerClass = classnames({
|
|
||||||
drawer: true,
|
|
||||||
'is-collapsed': !isOpen
|
|
||||||
});
|
|
||||||
return (
|
|
||||||
<aside className={ drawerClass }>
|
|
||||||
<div className='drawer-action-bar'>
|
|
||||||
<a
|
|
||||||
aria-label={ newTabAria }
|
|
||||||
className='drawer-action-item drawer-action-pop-out'
|
|
||||||
href={ newTabLink }
|
|
||||||
target='_blank'
|
|
||||||
/>
|
|
||||||
<button
|
|
||||||
aria-label={ closeAria }
|
|
||||||
className='drawer-action-item drawer-action-collapse'
|
|
||||||
onClick={ closeDrawer }
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className='drawer-content'>
|
|
||||||
{ children }
|
|
||||||
</div>
|
|
||||||
</aside>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,27 +0,0 @@
|
|||||||
import React, { PropTypes } from 'react';
|
|
||||||
import { Alert } from 'react-bootstrap';
|
|
||||||
|
|
||||||
export default React.createClass({
|
|
||||||
displayName: 'FlashQueue',
|
|
||||||
|
|
||||||
propTypes: {
|
|
||||||
messages: PropTypes.array
|
|
||||||
},
|
|
||||||
|
|
||||||
renderMessages(messages) {
|
|
||||||
return messages.map(() => {
|
|
||||||
return (
|
|
||||||
<Alert />
|
|
||||||
);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const { messages = [] } = this.props;
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
{ this.renderMessages(messages) }
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
@ -1,33 +0,0 @@
|
|||||||
import React, { PropTypes } from 'react';
|
|
||||||
import NoSSR from 'react-no-ssr';
|
|
||||||
|
|
||||||
import Drawer from './Drawer.jsx';
|
|
||||||
import ShowMap from '../routes/challenges/components/map/Map.jsx';
|
|
||||||
|
|
||||||
export default class MapDrawer extends React.Component {
|
|
||||||
static displayName = 'MapDrawer';
|
|
||||||
static propTypes = {
|
|
||||||
isOpen: PropTypes.bool,
|
|
||||||
isAlreadyLoaded: PropTypes.bool,
|
|
||||||
toggleMapDrawer: PropTypes.func
|
|
||||||
};
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const { isOpen, isAlreadyLoaded, toggleMapDrawer } = this.props;
|
|
||||||
return (
|
|
||||||
<Drawer
|
|
||||||
closeAria='close map aside'
|
|
||||||
closeDrawer={ toggleMapDrawer }
|
|
||||||
isOpen={ isOpen }
|
|
||||||
newTabAria='open map in new tab'
|
|
||||||
newTabLink='/map'
|
|
||||||
>
|
|
||||||
<NoSSR>
|
|
||||||
<div>
|
|
||||||
{ isAlreadyLoaded || isOpen ? <ShowMap /> : null }
|
|
||||||
</div>
|
|
||||||
</NoSSR>
|
|
||||||
</Drawer>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@ -11,7 +11,6 @@ import {
|
|||||||
|
|
||||||
import navLinks from './links.json';
|
import navLinks from './links.json';
|
||||||
import AvatarPointsNavItem from './Avatar-Points-Nav-Item.jsx';
|
import AvatarPointsNavItem from './Avatar-Points-Nav-Item.jsx';
|
||||||
import NoPropsPassthrough from '../../utils/No-Props-Passthrough.jsx';
|
|
||||||
|
|
||||||
const fCClogo = 'https://s3.amazonaws.com/freecodecamp/freecodecamp_logo.svg';
|
const fCClogo = 'https://s3.amazonaws.com/freecodecamp/freecodecamp_logo.svg';
|
||||||
|
|
||||||
@ -34,10 +33,7 @@ const propTypes = {
|
|||||||
picture: PropTypes.string,
|
picture: PropTypes.string,
|
||||||
signedIn: PropTypes.bool,
|
signedIn: PropTypes.bool,
|
||||||
username: PropTypes.string,
|
username: PropTypes.string,
|
||||||
isOnMap: PropTypes.bool,
|
|
||||||
updateNavHeight: PropTypes.func,
|
updateNavHeight: PropTypes.func,
|
||||||
toggleMapDrawer: PropTypes.func,
|
|
||||||
toggleMainChat: PropTypes.func,
|
|
||||||
showLoading: PropTypes.bool,
|
showLoading: PropTypes.bool,
|
||||||
trackEvent: PropTypes.func.isRequired,
|
trackEvent: PropTypes.func.isRequired,
|
||||||
loadCurrentChallenge: PropTypes.func.isRequired
|
loadCurrentChallenge: PropTypes.func.isRequired
|
||||||
@ -80,59 +76,6 @@ export default class FCCNav extends React.Component {
|
|||||||
this.props.loadCurrentChallenge();
|
this.props.loadCurrentChallenge();
|
||||||
}
|
}
|
||||||
|
|
||||||
renderMapLink(isOnMap, toggleMapDrawer) {
|
|
||||||
if (isOnMap) {
|
|
||||||
return (
|
|
||||||
<NoPropsPassthrough>
|
|
||||||
<li role='presentation'>
|
|
||||||
<a
|
|
||||||
href='#'
|
|
||||||
onClick={ this.handleMapClickOnMap }
|
|
||||||
>
|
|
||||||
Map
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
</NoPropsPassthrough>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
<LinkContainer
|
|
||||||
eventKey={ 1 }
|
|
||||||
to='/map'
|
|
||||||
>
|
|
||||||
<NavItem
|
|
||||||
onClick={ e => {
|
|
||||||
if (!(e.ctrlKey || e.metaKey)) {
|
|
||||||
e.preventDefault();
|
|
||||||
toggleMapDrawer();
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
target='/map'
|
|
||||||
>
|
|
||||||
Map
|
|
||||||
</NavItem>
|
|
||||||
</LinkContainer>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
renderChat(toggleMainChat) {
|
|
||||||
return (
|
|
||||||
<NavItem
|
|
||||||
eventKey={ 2 }
|
|
||||||
href='//gitter.im/freecodecamp/freecodecamp'
|
|
||||||
onClick={ e => {
|
|
||||||
if (!(e.ctrlKey || e.metaKey)) {
|
|
||||||
e.preventDefault();
|
|
||||||
toggleMainChat();
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
target='_blank'
|
|
||||||
>
|
|
||||||
Chat
|
|
||||||
</NavItem>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
renderLinks() {
|
renderLinks() {
|
||||||
return navLinks.map(({ content, link, react, target }, index) => {
|
return navLinks.map(({ content, link, react, target }, index) => {
|
||||||
if (react) {
|
if (react) {
|
||||||
@ -195,9 +138,6 @@ export default class FCCNav extends React.Component {
|
|||||||
username,
|
username,
|
||||||
points,
|
points,
|
||||||
picture,
|
picture,
|
||||||
isOnMap,
|
|
||||||
toggleMapDrawer,
|
|
||||||
toggleMainChat,
|
|
||||||
showLoading
|
showLoading
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
@ -227,8 +167,6 @@ export default class FCCNav extends React.Component {
|
|||||||
navbar={ true }
|
navbar={ true }
|
||||||
pullRight={ true }
|
pullRight={ true }
|
||||||
>
|
>
|
||||||
{ this.renderMapLink(isOnMap, toggleMapDrawer) }
|
|
||||||
{ this.renderChat(toggleMainChat) }
|
|
||||||
{ this.renderLinks() }
|
{ this.renderLinks() }
|
||||||
{ this.renderSignIn(username, points, picture, showLoading) }
|
{ this.renderSignIn(username, points, picture, showLoading) }
|
||||||
</Nav>
|
</Nav>
|
||||||
|
@ -1,6 +1,14 @@
|
|||||||
[{
|
[{
|
||||||
|
"content": "Map",
|
||||||
|
"link": "/map",
|
||||||
|
"react": true
|
||||||
|
},{
|
||||||
|
"content": "Chat",
|
||||||
|
"link": "https://gitter.im/freecodecamp/home",
|
||||||
|
"target": "_blank"
|
||||||
|
},{
|
||||||
"content": "Forum",
|
"content": "Forum",
|
||||||
"link": "http://forum.freecodecamp.com/",
|
"link": "https://forum.freecodecamp.com/",
|
||||||
"target": "_blank"
|
"target": "_blank"
|
||||||
},{
|
},{
|
||||||
"content": "About",
|
"content": "About",
|
||||||
|
@ -136,63 +136,6 @@ export const doActionOnError = actionCreator => error => Observable.of(
|
|||||||
actionCreator()
|
actionCreator()
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
// drawers
|
|
||||||
export const toggleMapDrawer = createAction(
|
|
||||||
types.toggleMapDrawer,
|
|
||||||
null,
|
|
||||||
() => createEventMeta({
|
|
||||||
category: 'Nav',
|
|
||||||
action: 'toggled',
|
|
||||||
label: 'Map drawer toggled'
|
|
||||||
})
|
|
||||||
);
|
|
||||||
export const closeMapDrawer = createAction(
|
|
||||||
types.closeMapDrawer,
|
|
||||||
null,
|
|
||||||
() => createEventMeta({
|
|
||||||
category: 'Nav',
|
|
||||||
action: 'clicked',
|
|
||||||
label: 'Map drawer closed'
|
|
||||||
})
|
|
||||||
);
|
|
||||||
export const toggleMainChat = createAction(
|
|
||||||
types.toggleMainChat,
|
|
||||||
null,
|
|
||||||
() => createEventMeta({
|
|
||||||
category: 'Nav',
|
|
||||||
action: 'toggled',
|
|
||||||
label: 'Main chat toggled'
|
|
||||||
})
|
|
||||||
);
|
|
||||||
export const toggleHelpChat = createAction(
|
|
||||||
types.toggleHelpChat,
|
|
||||||
null,
|
|
||||||
() => createEventMeta({
|
|
||||||
category: 'Challenge',
|
|
||||||
action: 'toggled',
|
|
||||||
label: 'help chat toggled'
|
|
||||||
})
|
|
||||||
);
|
|
||||||
export const openHelpChat = createAction(
|
|
||||||
types.openHelpChat,
|
|
||||||
null,
|
|
||||||
() => createEventMeta({
|
|
||||||
category: 'Challenge',
|
|
||||||
action: 'opened',
|
|
||||||
label: 'help chat opened'
|
|
||||||
})
|
|
||||||
);
|
|
||||||
export const closeHelpChat = createAction(
|
|
||||||
types.closeHelpChat,
|
|
||||||
null,
|
|
||||||
() => createEventMeta({
|
|
||||||
category: 'Challenge',
|
|
||||||
action: 'closed',
|
|
||||||
label: 'help chat closed'
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
export const toggleNightMode = createAction(
|
export const toggleNightMode = createAction(
|
||||||
types.toggleNightMode,
|
types.toggleNightMode,
|
||||||
// we use this function to avoid hanging onto the eventObject
|
// we use this function to avoid hanging onto the eventObject
|
||||||
|
@ -9,8 +9,6 @@ const initialState = {
|
|||||||
csrfToken: '',
|
csrfToken: '',
|
||||||
windowHeight: 0,
|
windowHeight: 0,
|
||||||
navHeight: 0,
|
navHeight: 0,
|
||||||
isMainChatOpen: false,
|
|
||||||
isHelpChatOpen: false,
|
|
||||||
theme: 'default'
|
theme: 'default'
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -51,31 +49,6 @@ export default handleActions(
|
|||||||
...state,
|
...state,
|
||||||
navHeight
|
navHeight
|
||||||
}),
|
}),
|
||||||
[types.toggleMapDrawer]: state => ({
|
|
||||||
...state,
|
|
||||||
isMapAlreadyLoaded: true,
|
|
||||||
isMapDrawerOpen: !state.isMapDrawerOpen
|
|
||||||
}),
|
|
||||||
[types.closeMapDrawer]: state => ({
|
|
||||||
...state,
|
|
||||||
isMapDrawerOpen: false
|
|
||||||
}),
|
|
||||||
[types.toggleMainChat]: state => ({
|
|
||||||
...state,
|
|
||||||
isMainChatOpen: !state.isMainChatOpen
|
|
||||||
}),
|
|
||||||
[types.toggleHelpChat]: state => ({
|
|
||||||
...state,
|
|
||||||
isHelpChatOpen: !state.isHelpChatOpen
|
|
||||||
}),
|
|
||||||
[types.openHelpChat]: state => ({
|
|
||||||
...state,
|
|
||||||
isHelpChatOpen: true
|
|
||||||
}),
|
|
||||||
[types.closeHelpChat]: state => ({
|
|
||||||
...state,
|
|
||||||
isHelpChatOpen: false
|
|
||||||
}),
|
|
||||||
[types.delayedRedirect]: (state, { payload }) => ({
|
[types.delayedRedirect]: (state, { payload }) => ({
|
||||||
...state,
|
...state,
|
||||||
delayedRedirect: payload
|
delayedRedirect: payload
|
||||||
|
@ -30,20 +30,6 @@ export default createTypes([
|
|||||||
'updateChallengesData',
|
'updateChallengesData',
|
||||||
'updateHikesData',
|
'updateHikesData',
|
||||||
|
|
||||||
// drawers
|
|
||||||
'toggleMapDrawer',
|
|
||||||
'closeMapDrawer',
|
|
||||||
'toggleWikiDrawer',
|
|
||||||
|
|
||||||
// chat
|
|
||||||
'openMainChat',
|
|
||||||
'closeMainChat',
|
|
||||||
'toggleMainChat',
|
|
||||||
|
|
||||||
'openHelpChat',
|
|
||||||
'closeHelpChat',
|
|
||||||
'toggleHelpChat',
|
|
||||||
|
|
||||||
// night mode
|
// night mode
|
||||||
'toggleNightMode',
|
'toggleNightMode',
|
||||||
'updateTheme',
|
'updateTheme',
|
||||||
|
@ -16,13 +16,11 @@ import {
|
|||||||
unlockUntrustedCode
|
unlockUntrustedCode
|
||||||
} from '../../redux/actions';
|
} from '../../redux/actions';
|
||||||
import { makeToast } from '../../../../toasts/redux/actions';
|
import { makeToast } from '../../../../toasts/redux/actions';
|
||||||
import { toggleHelpChat } from '../../../../redux/actions';
|
|
||||||
|
|
||||||
const bindableActions = {
|
const mapDispatchToProps = {
|
||||||
makeToast,
|
makeToast,
|
||||||
executeChallenge,
|
executeChallenge,
|
||||||
updateHint,
|
updateHint,
|
||||||
toggleHelpChat,
|
|
||||||
openBugModal,
|
openBugModal,
|
||||||
unlockUntrustedCode
|
unlockUntrustedCode
|
||||||
};
|
};
|
||||||
@ -34,6 +32,7 @@ const mapStateToProps = createSelector(
|
|||||||
state => state.challengesApp.output,
|
state => state.challengesApp.output,
|
||||||
state => state.challengesApp.hintIndex,
|
state => state.challengesApp.hintIndex,
|
||||||
state => state.challengesApp.isCodeLocked,
|
state => state.challengesApp.isCodeLocked,
|
||||||
|
state => state.challengesApp.helpChatRoom,
|
||||||
(
|
(
|
||||||
{
|
{
|
||||||
challenge: {
|
challenge: {
|
||||||
@ -47,7 +46,8 @@ const mapStateToProps = createSelector(
|
|||||||
tests,
|
tests,
|
||||||
output,
|
output,
|
||||||
hintIndex,
|
hintIndex,
|
||||||
isCodeLocked
|
isCodeLocked,
|
||||||
|
helpChatRoom
|
||||||
) => ({
|
) => ({
|
||||||
title,
|
title,
|
||||||
description,
|
description,
|
||||||
@ -55,7 +55,8 @@ const mapStateToProps = createSelector(
|
|||||||
tests,
|
tests,
|
||||||
output,
|
output,
|
||||||
hint: hints[hintIndex],
|
hint: hints[hintIndex],
|
||||||
isCodeLocked
|
isCodeLocked,
|
||||||
|
helpChatRoom
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -69,17 +70,18 @@ export class SidePanel extends PureComponent {
|
|||||||
static propTypes = {
|
static propTypes = {
|
||||||
description: PropTypes.arrayOf(PropTypes.string),
|
description: PropTypes.arrayOf(PropTypes.string),
|
||||||
height: PropTypes.number,
|
height: PropTypes.number,
|
||||||
|
helpChatRoom: PropTypes.string,
|
||||||
|
hint: PropTypes.string,
|
||||||
|
isCodeLocked: PropTypes.bool,
|
||||||
|
output: PropTypes.string,
|
||||||
tests: PropTypes.arrayOf(PropTypes.object),
|
tests: PropTypes.arrayOf(PropTypes.object),
|
||||||
title: PropTypes.string,
|
title: PropTypes.string,
|
||||||
output: PropTypes.string,
|
|
||||||
hint: PropTypes.string,
|
executeChallenge: PropTypes.func,
|
||||||
updateHint: PropTypes.func,
|
|
||||||
makeToast: PropTypes.func,
|
|
||||||
toggleHelpChat: PropTypes.func,
|
|
||||||
openBugModal: PropTypes.func,
|
openBugModal: PropTypes.func,
|
||||||
|
makeToast: PropTypes.func,
|
||||||
unlockUntrustedCode: PropTypes.func,
|
unlockUntrustedCode: PropTypes.func,
|
||||||
isCodeLocked: PropTypes.bool,
|
updateHint: PropTypes.func
|
||||||
executeChallenge: PropTypes.func
|
|
||||||
};
|
};
|
||||||
|
|
||||||
renderDescription(description = [ 'Happy Coding!' ], descriptionRegex) {
|
renderDescription(description = [ 'Happy Coding!' ], descriptionRegex) {
|
||||||
@ -119,7 +121,7 @@ export class SidePanel extends PureComponent {
|
|||||||
executeChallenge,
|
executeChallenge,
|
||||||
updateHint,
|
updateHint,
|
||||||
makeToast,
|
makeToast,
|
||||||
toggleHelpChat,
|
helpChatRoom,
|
||||||
openBugModal,
|
openBugModal,
|
||||||
isCodeLocked,
|
isCodeLocked,
|
||||||
unlockUntrustedCode
|
unlockUntrustedCode
|
||||||
@ -150,11 +152,11 @@ export class SidePanel extends PureComponent {
|
|||||||
</div>
|
</div>
|
||||||
<ToolPanel
|
<ToolPanel
|
||||||
executeChallenge={ executeChallenge }
|
executeChallenge={ executeChallenge }
|
||||||
|
helpChatRoom={ helpChatRoom }
|
||||||
hint={ hint }
|
hint={ hint }
|
||||||
isCodeLocked={ isCodeLocked }
|
isCodeLocked={ isCodeLocked }
|
||||||
makeToast={ makeToast }
|
makeToast={ makeToast }
|
||||||
openBugModal={ openBugModal }
|
openBugModal={ openBugModal }
|
||||||
toggleHelpChat={ toggleHelpChat }
|
|
||||||
unlockUntrustedCode={ unlockUntrustedCode }
|
unlockUntrustedCode={ unlockUntrustedCode }
|
||||||
updateHint={ updateHint }
|
updateHint={ updateHint }
|
||||||
/>
|
/>
|
||||||
@ -168,5 +170,5 @@ export class SidePanel extends PureComponent {
|
|||||||
|
|
||||||
export default connect(
|
export default connect(
|
||||||
mapStateToProps,
|
mapStateToProps,
|
||||||
bindableActions
|
mapDispatchToProps
|
||||||
)(SidePanel);
|
)(SidePanel);
|
||||||
|
@ -9,25 +9,24 @@ const unlockWarning = (
|
|||||||
</h4>
|
</h4>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const propTypes = {
|
||||||
|
executeChallenge: PropTypes.func.isRequired,
|
||||||
|
helpChatRoom: PropTypes.string,
|
||||||
|
hint: PropTypes.string,
|
||||||
|
isCodeLocked: PropTypes.bool,
|
||||||
|
makeToast: PropTypes.func.isRequired,
|
||||||
|
openBugModal: PropTypes.func.isRequired,
|
||||||
|
unlockUntrustedCode: PropTypes.func.isRequired,
|
||||||
|
updateHint: PropTypes.func.isRequired
|
||||||
|
};
|
||||||
|
|
||||||
export default class ToolPanel extends PureComponent {
|
export default class ToolPanel extends PureComponent {
|
||||||
constructor(...props) {
|
constructor(...props) {
|
||||||
super(...props);
|
super(...props);
|
||||||
this.makeHint = this.makeHint.bind(this);
|
this.makeHint = this.makeHint.bind(this);
|
||||||
this.makeReset = this.makeReset.bind(this);
|
this.makeReset = this.makeReset.bind(this);
|
||||||
}
|
}
|
||||||
static displayName = 'ToolPanel';
|
|
||||||
|
|
||||||
static propTypes = {
|
|
||||||
executeChallenge: PropTypes.func.isRequired,
|
|
||||||
updateHint: PropTypes.func.isRequired,
|
|
||||||
hint: PropTypes.string,
|
|
||||||
isCodeLocked: PropTypes.bool,
|
|
||||||
unlockUntrustedCode: PropTypes.func.isRequired,
|
|
||||||
toggleHelpChat: PropTypes.func.isRequired,
|
|
||||||
openBugModal: PropTypes.func.isRequired,
|
|
||||||
makeToast: PropTypes.func.isRequired
|
|
||||||
};
|
|
||||||
|
|
||||||
makeHint() {
|
makeHint() {
|
||||||
this.props.makeToast({
|
this.props.makeToast({
|
||||||
message: this.props.hint,
|
message: this.props.hint,
|
||||||
@ -93,10 +92,10 @@ export default class ToolPanel extends PureComponent {
|
|||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {
|
const {
|
||||||
|
executeChallenge,
|
||||||
|
helpChatRoom,
|
||||||
hint,
|
hint,
|
||||||
isCodeLocked,
|
isCodeLocked,
|
||||||
executeChallenge,
|
|
||||||
toggleHelpChat,
|
|
||||||
openBugModal,
|
openBugModal,
|
||||||
unlockUntrustedCode
|
unlockUntrustedCode
|
||||||
} = this.props;
|
} = this.props;
|
||||||
@ -126,8 +125,9 @@ export default class ToolPanel extends PureComponent {
|
|||||||
<Button
|
<Button
|
||||||
bsSize='large'
|
bsSize='large'
|
||||||
bsStyle='primary'
|
bsStyle='primary'
|
||||||
componentClass='label'
|
componentClass='a'
|
||||||
onClick={ toggleHelpChat }
|
href={ `https://gitter.im/freecodecamp/${helpChatRoom}` }
|
||||||
|
target='_blank'
|
||||||
>
|
>
|
||||||
Help
|
Help
|
||||||
</Button>
|
</Button>
|
||||||
@ -145,3 +145,6 @@ export default class ToolPanel extends PureComponent {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ToolPanel.displayName = 'ToolPanel';
|
||||||
|
ToolPanel.propTypes = propTypes;
|
||||||
|
@ -9,9 +9,21 @@ import debug from 'debug';
|
|||||||
import { updateCurrentChallenge } from '../../redux/actions';
|
import { updateCurrentChallenge } from '../../redux/actions';
|
||||||
import { makePanelHiddenSelector } from '../../redux/selectors';
|
import { makePanelHiddenSelector } from '../../redux/selectors';
|
||||||
import { userSelector } from '../../../../redux/selectors';
|
import { userSelector } from '../../../../redux/selectors';
|
||||||
import { closeMapDrawer } from '../../../../redux/actions';
|
|
||||||
|
|
||||||
const bindableActions = { closeMapDrawer, updateCurrentChallenge };
|
const propTypes = {
|
||||||
|
block: PropTypes.string,
|
||||||
|
challenge: PropTypes.object,
|
||||||
|
dashedName: PropTypes.string,
|
||||||
|
isComingSoon: PropTypes.bool,
|
||||||
|
isCompleted: PropTypes.bool,
|
||||||
|
isDev: PropTypes.bool,
|
||||||
|
isHidden: PropTypes.bool,
|
||||||
|
isLocked: PropTypes.bool,
|
||||||
|
isRequired: PropTypes.bool,
|
||||||
|
title: PropTypes.string,
|
||||||
|
updateCurrentChallenge: PropTypes.func.isRequired
|
||||||
|
};
|
||||||
|
const mapDispatchToProps = { updateCurrentChallenge };
|
||||||
const makeMapStateToProps = () => createSelector(
|
const makeMapStateToProps = () => createSelector(
|
||||||
userSelector,
|
userSelector,
|
||||||
(_, props) => props.dashedName,
|
(_, props) => props.dashedName,
|
||||||
@ -48,24 +60,8 @@ export class Challenge extends PureComponent {
|
|||||||
super(...args);
|
super(...args);
|
||||||
this.handleChallengeClick = this.handleChallengeClick.bind(this);
|
this.handleChallengeClick = this.handleChallengeClick.bind(this);
|
||||||
}
|
}
|
||||||
static displayName = 'Challenge';
|
|
||||||
static propTypes = {
|
|
||||||
title: PropTypes.string,
|
|
||||||
dashedName: PropTypes.string,
|
|
||||||
block: PropTypes.string,
|
|
||||||
isLocked: PropTypes.bool,
|
|
||||||
isRequired: PropTypes.bool,
|
|
||||||
isComingSoon: PropTypes.bool,
|
|
||||||
isCompleted: PropTypes.bool,
|
|
||||||
isDev: PropTypes.bool,
|
|
||||||
isHidden: PropTypes.bool,
|
|
||||||
challenge: PropTypes.object,
|
|
||||||
updateCurrentChallenge: PropTypes.func.isRequired,
|
|
||||||
closeMapDrawer: PropTypes.func.isRequired
|
|
||||||
};
|
|
||||||
|
|
||||||
handleChallengeClick() {
|
handleChallengeClick() {
|
||||||
this.props.closeMapDrawer();
|
|
||||||
this.props.updateCurrentChallenge(this.props.challenge);
|
this.props.updateCurrentChallenge(this.props.challenge);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -161,4 +157,10 @@ export class Challenge extends PureComponent {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default connect(makeMapStateToProps, bindableActions)(Challenge);
|
Challenge.propTypes = propTypes;
|
||||||
|
Challenge.dispalyName = 'Challenge';
|
||||||
|
|
||||||
|
export default connect(
|
||||||
|
makeMapStateToProps,
|
||||||
|
mapDispatchToProps
|
||||||
|
)(Challenge);
|
||||||
|
@ -15,40 +15,40 @@ import {
|
|||||||
simpleProject,
|
simpleProject,
|
||||||
frontEndProject
|
frontEndProject
|
||||||
} from '../../../../utils/challengeTypes';
|
} from '../../../../utils/challengeTypes';
|
||||||
import { toggleHelpChat } from '../../../../redux/actions';
|
|
||||||
|
|
||||||
const bindableActions = {
|
const propTypes = {
|
||||||
|
isSignedIn: PropTypes.bool,
|
||||||
|
isSimple: PropTypes.bool,
|
||||||
|
isFrontEnd: PropTypes.bool,
|
||||||
|
isSubmitting: PropTypes.bool,
|
||||||
|
helpChatRoom: PropTypes.string.isRequired,
|
||||||
|
openBugModal: PropTypes.func.isRequired,
|
||||||
|
submitChallenge: PropTypes.func.isRequired
|
||||||
|
};
|
||||||
|
const mapDispatchToProps = {
|
||||||
submitChallenge,
|
submitChallenge,
|
||||||
toggleHelpChat,
|
|
||||||
openBugModal
|
openBugModal
|
||||||
};
|
};
|
||||||
const mapStateToProps = createSelector(
|
const mapStateToProps = createSelector(
|
||||||
challengeSelector,
|
challengeSelector,
|
||||||
state => state.app.isSignedIn,
|
state => state.app.isSignedIn,
|
||||||
state => state.challengesApp.isSubmitting,
|
state => state.challengesApp.isSubmitting,
|
||||||
|
state => state.challengesApp.helpChatRoom,
|
||||||
(
|
(
|
||||||
{ challenge: { challengeType = simpleProject } },
|
{ challenge: { challengeType = simpleProject } },
|
||||||
isSignedIn,
|
isSignedIn,
|
||||||
isSubmitting
|
isSubmitting,
|
||||||
|
helpChatRoom,
|
||||||
) => ({
|
) => ({
|
||||||
isSignedIn,
|
isSignedIn,
|
||||||
isSubmitting,
|
isSubmitting,
|
||||||
|
helpChatRoom,
|
||||||
isSimple: challengeType === simpleProject,
|
isSimple: challengeType === simpleProject,
|
||||||
isFrontEnd: challengeType === frontEndProject
|
isFrontEnd: challengeType === frontEndProject
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
export class ToolPanel extends PureComponent {
|
export class ToolPanel extends PureComponent {
|
||||||
static propTypes = {
|
|
||||||
isSignedIn: PropTypes.bool,
|
|
||||||
isSimple: PropTypes.bool,
|
|
||||||
isFrontEnd: PropTypes.bool,
|
|
||||||
isSubmitting: PropTypes.bool,
|
|
||||||
toggleHelpChat: PropTypes.func.isRequired,
|
|
||||||
openBugModal: PropTypes.func.isRequired,
|
|
||||||
submitChallenge: PropTypes.func.isRequired
|
|
||||||
};
|
|
||||||
|
|
||||||
renderSubmitButton(isSignedIn, submitChallenge) {
|
renderSubmitButton(isSignedIn, submitChallenge) {
|
||||||
const buttonCopy = isSignedIn ?
|
const buttonCopy = isSignedIn ?
|
||||||
'Submit and go to my next challenge' :
|
'Submit and go to my next challenge' :
|
||||||
@ -71,8 +71,8 @@ export class ToolPanel extends PureComponent {
|
|||||||
isSimple,
|
isSimple,
|
||||||
isSignedIn,
|
isSignedIn,
|
||||||
isSubmitting,
|
isSubmitting,
|
||||||
|
helpChatRoom,
|
||||||
submitChallenge,
|
submitChallenge,
|
||||||
toggleHelpChat,
|
|
||||||
openBugModal
|
openBugModal
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
@ -89,8 +89,9 @@ export class ToolPanel extends PureComponent {
|
|||||||
<Button
|
<Button
|
||||||
bsStyle='primary'
|
bsStyle='primary'
|
||||||
className='btn-primary-ghost btn-big'
|
className='btn-primary-ghost btn-big'
|
||||||
componentClass='div'
|
componentClass='a'
|
||||||
onClick={ toggleHelpChat }
|
href={ `https://gitter.im/freecodecamp/${helpChatRoom}` }
|
||||||
|
target='_blank'
|
||||||
>
|
>
|
||||||
Help
|
Help
|
||||||
</Button>
|
</Button>
|
||||||
@ -108,7 +109,10 @@ export class ToolPanel extends PureComponent {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ToolPanel.propTypes = propTypes;
|
||||||
|
ToolPanel.displayName = 'ToolPanel';
|
||||||
|
|
||||||
export default connect(
|
export default connect(
|
||||||
mapStateToProps,
|
mapStateToProps,
|
||||||
bindableActions
|
mapDispatchToProps
|
||||||
)(ToolPanel);
|
)(ToolPanel);
|
||||||
|
@ -1,3 +1,2 @@
|
|||||||
// scripts should be moved here
|
// scripts should be moved here
|
||||||
script(src="https://sidecar.gitter.im/dist/sidecar.v1.js" async defer)
|
|
||||||
script(src="https://cdn.optimizely.com/js/999692993.js")
|
script(src="https://cdn.optimizely.com/js/999692993.js")
|
||||||
|
@ -9,12 +9,10 @@ nav.navbar.navbar-default.navbar-fixed-top.nav-height
|
|||||||
ul.nav.navbar-nav.navbar-right.hamburger-dropdown
|
ul.nav.navbar-nav.navbar-right.hamburger-dropdown
|
||||||
li
|
li
|
||||||
a(href='/map') Map
|
a(href='/map') Map
|
||||||
li.hidden-xs
|
|
||||||
a#nav-chat-btn(href='//gitter.im/freecodecamp/freecodecamp' onclick="if (!(event.ctrlKey || event.metaKey)) {return false;}") Chat
|
|
||||||
li.visible-xs
|
|
||||||
a(href="//gitter.im/freecodecamp/freecodecamp" target="_blank") Chat
|
|
||||||
li
|
li
|
||||||
a(href='http://forum.freecodecamp.com', target='_blank') Forum
|
a(href='https://gitter.im/freecodecamp/home' target='_blank') Chat
|
||||||
|
li
|
||||||
|
a(href='https://forum.freecodecamp.com', target='_blank') Forum
|
||||||
li
|
li
|
||||||
a(href='/about') About
|
a(href='/about') About
|
||||||
li
|
li
|
||||||
|
Reference in New Issue
Block a user