Add main chat toggle
This commit is contained in:
61
client/sagas/gitter-saga.js
Normal file
61
client/sagas/gitter-saga.js
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
import { Observable } from 'rx';
|
||||||
|
import Chat from 'gitter-sidecar';
|
||||||
|
import types from '../../common/app/redux/types';
|
||||||
|
|
||||||
|
function createHeader(document) {
|
||||||
|
const div = document.createElement('div');
|
||||||
|
const span = document.createElement('span');
|
||||||
|
const actionBar = document.querySelector(
|
||||||
|
'#chat-embed-main > .gitter-chat-embed-action-bar'
|
||||||
|
);
|
||||||
|
span.appendChild(document.createTextNode('Free Code Camp\'s Main Chat'));
|
||||||
|
div.className = 'chat-embed-main-title';
|
||||||
|
div.appendChild(span);
|
||||||
|
actionBar.insertBefore(div, actionBar.firstChild);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function gitterSaga(actions$, 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 mainChatToggle$ = 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(document);
|
||||||
|
}
|
||||||
|
if (isMainChatOpen === e.detail.state) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return { type: types.toggleMainChat };
|
||||||
|
});
|
||||||
|
return Observable.merge(
|
||||||
|
mainChatToggle$,
|
||||||
|
actions$
|
||||||
|
.filter(({ type }) => (
|
||||||
|
type === types.openMainChat ||
|
||||||
|
type === types.closeMainChat ||
|
||||||
|
type === types.toggleMainChat
|
||||||
|
))
|
||||||
|
.map(() => {
|
||||||
|
const { isMainChatOpen } = getState().app;
|
||||||
|
mainChat.toggleChat(isMainChatOpen);
|
||||||
|
if (!isMainChatOpen) {
|
||||||
|
document.activeElement.blur();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
@ -6,6 +6,7 @@ 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';
|
||||||
|
|
||||||
export default [
|
export default [
|
||||||
errSaga,
|
errSaga,
|
||||||
@ -15,5 +16,6 @@ export default [
|
|||||||
windowSaga,
|
windowSaga,
|
||||||
executeChallengeSaga,
|
executeChallengeSaga,
|
||||||
frameSaga,
|
frameSaga,
|
||||||
codeStorageSaga
|
codeStorageSaga,
|
||||||
|
gitterSaga
|
||||||
];
|
];
|
||||||
|
@ -11,7 +11,8 @@ import {
|
|||||||
fetchUser,
|
fetchUser,
|
||||||
initWindowHeight,
|
initWindowHeight,
|
||||||
updateNavHeight,
|
updateNavHeight,
|
||||||
toggleMapDrawer
|
toggleMapDrawer,
|
||||||
|
toggleMainChat
|
||||||
} from './redux/actions';
|
} from './redux/actions';
|
||||||
|
|
||||||
import { submitChallenge } from './routes/challenges/redux/actions';
|
import { submitChallenge } from './routes/challenges/redux/actions';
|
||||||
@ -53,7 +54,8 @@ const bindableActions = {
|
|||||||
updateNavHeight,
|
updateNavHeight,
|
||||||
fetchUser,
|
fetchUser,
|
||||||
submitChallenge,
|
submitChallenge,
|
||||||
toggleMapDrawer
|
toggleMapDrawer,
|
||||||
|
toggleMainChat
|
||||||
};
|
};
|
||||||
|
|
||||||
const fetchContainerOptions = {
|
const fetchContainerOptions = {
|
||||||
@ -79,7 +81,8 @@ export class FreeCodeCamp extends React.Component {
|
|||||||
submitChallenge: PropTypes.func,
|
submitChallenge: PropTypes.func,
|
||||||
isMapDrawerOpen: PropTypes.bool,
|
isMapDrawerOpen: PropTypes.bool,
|
||||||
isMapAlreadyLoaded: PropTypes.bool,
|
isMapAlreadyLoaded: PropTypes.bool,
|
||||||
toggleMapDrawer: PropTypes.func
|
toggleMapDrawer: PropTypes.func,
|
||||||
|
toggleMainChat: PropTypes.func
|
||||||
};
|
};
|
||||||
|
|
||||||
componentWillReceiveProps({
|
componentWillReceiveProps({
|
||||||
@ -141,14 +144,16 @@ export class FreeCodeCamp extends React.Component {
|
|||||||
updateNavHeight,
|
updateNavHeight,
|
||||||
isMapDrawerOpen,
|
isMapDrawerOpen,
|
||||||
isMapAlreadyLoaded,
|
isMapAlreadyLoaded,
|
||||||
toggleMapDrawer
|
toggleMapDrawer,
|
||||||
|
toggleMainChat
|
||||||
} = this.props;
|
} = this.props;
|
||||||
const navProps = {
|
const navProps = {
|
||||||
username,
|
username,
|
||||||
points,
|
points,
|
||||||
picture,
|
picture,
|
||||||
updateNavHeight,
|
updateNavHeight,
|
||||||
toggleMapDrawer
|
toggleMapDrawer,
|
||||||
|
toggleMainChat
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -40,7 +40,8 @@ export default class extends React.Component {
|
|||||||
signedIn: PropTypes.bool,
|
signedIn: PropTypes.bool,
|
||||||
username: PropTypes.string,
|
username: PropTypes.string,
|
||||||
updateNavHeight: PropTypes.func,
|
updateNavHeight: PropTypes.func,
|
||||||
toggleMapDrawer: PropTypes.func
|
toggleMapDrawer: PropTypes.func,
|
||||||
|
toggleMainChat: PropTypes.func
|
||||||
};
|
};
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
@ -75,6 +76,19 @@ export default class extends React.Component {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
renderChat(toggleMainChat) {
|
||||||
|
return (
|
||||||
|
<NavItem
|
||||||
|
eventKey={ 2 }
|
||||||
|
href='//gitter.im/freecodecamp/freecodecamp'
|
||||||
|
onClick={ 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) {
|
||||||
@ -144,7 +158,8 @@ export default class extends React.Component {
|
|||||||
username,
|
username,
|
||||||
points,
|
points,
|
||||||
picture,
|
picture,
|
||||||
toggleMapDrawer
|
toggleMapDrawer,
|
||||||
|
toggleMainChat
|
||||||
} = this.props;
|
} = this.props;
|
||||||
const { router } = this.context;
|
const { router } = this.context;
|
||||||
const isOnMap = router.isActive('/map');
|
const isOnMap = router.isActive('/map');
|
||||||
@ -161,6 +176,7 @@ export default class extends React.Component {
|
|||||||
navbar={ true }
|
navbar={ true }
|
||||||
pullRight={ true }>
|
pullRight={ true }>
|
||||||
{ this.renderMapLink(isOnMap, toggleMapDrawer) }
|
{ this.renderMapLink(isOnMap, toggleMapDrawer) }
|
||||||
|
{ this.renderChat(toggleMainChat) }
|
||||||
{ this.renderLinks() }
|
{ this.renderLinks() }
|
||||||
{ this.renderPoints(username, points) }
|
{ this.renderPoints(username, points) }
|
||||||
{ this.renderSignin(username, picture) }
|
{ this.renderSignin(username, picture) }
|
||||||
|
@ -1,8 +1,4 @@
|
|||||||
[{
|
[{
|
||||||
"content": "Chat",
|
|
||||||
"link": "//gitter.im/FreeCodeCamp/FreeCodeCamp",
|
|
||||||
"target": "_blank"
|
|
||||||
},{
|
|
||||||
"content": "Forum",
|
"content": "Forum",
|
||||||
"link": "http://forum.freecodecamp.com/",
|
"link": "http://forum.freecodecamp.com/",
|
||||||
"target": "_blank"
|
"target": "_blank"
|
||||||
|
@ -53,3 +53,14 @@ export const toggleMapDrawer = createAction(
|
|||||||
types.toggleMapDrawer,
|
types.toggleMapDrawer,
|
||||||
e => e.preventDefault()
|
e => e.preventDefault()
|
||||||
);
|
);
|
||||||
|
export const toggleWikiDrawer = createAction(types.toggleWikiDrawer);
|
||||||
|
|
||||||
|
// chat
|
||||||
|
export const toggleMainChat = createAction(
|
||||||
|
types.toggleMainChat,
|
||||||
|
e => {
|
||||||
|
if (!(e.ctrlKey || e.metaKey)) {
|
||||||
|
e.preventDefault();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
@ -9,7 +9,8 @@ const initialState = {
|
|||||||
isSignedIn: false,
|
isSignedIn: false,
|
||||||
csrfToken: '',
|
csrfToken: '',
|
||||||
windowHeight: 0,
|
windowHeight: 0,
|
||||||
navHeight: 0
|
navHeight: 0,
|
||||||
|
isMainChatOpen: false
|
||||||
};
|
};
|
||||||
|
|
||||||
export default handleActions(
|
export default handleActions(
|
||||||
@ -46,6 +47,10 @@ export default handleActions(
|
|||||||
...state,
|
...state,
|
||||||
isMapAlreadyLoaded: true,
|
isMapAlreadyLoaded: true,
|
||||||
isMapDrawerOpen: !state.isMapDrawerOpen
|
isMapDrawerOpen: !state.isMapDrawerOpen
|
||||||
|
}),
|
||||||
|
[types.toggleMainChat]: state => ({
|
||||||
|
...state,
|
||||||
|
isMainChatOpen: !state.isMainChatOpen
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
initialState
|
initialState
|
||||||
|
@ -22,5 +22,11 @@ export default createTypes([
|
|||||||
'updateHikesData',
|
'updateHikesData',
|
||||||
|
|
||||||
// drawers
|
// drawers
|
||||||
'toggleMapDrawer'
|
'toggleMapDrawer',
|
||||||
|
'toggleWikiDrawer',
|
||||||
|
|
||||||
|
// main chat
|
||||||
|
'openMainChat',
|
||||||
|
'closeMainChat',
|
||||||
|
'toggleMainChat'
|
||||||
], 'app');
|
], 'app');
|
||||||
|
@ -50,6 +50,7 @@
|
|||||||
"express-validator": "^2.18.0",
|
"express-validator": "^2.18.0",
|
||||||
"fetchr": "~0.5.12",
|
"fetchr": "~0.5.12",
|
||||||
"frameguard": "^2.0.0",
|
"frameguard": "^2.0.0",
|
||||||
|
"gitter-sidecar": "^1.2.3",
|
||||||
"helmet": "^2.0.0",
|
"helmet": "^2.0.0",
|
||||||
"helmet-csp": "^1.0.3",
|
"helmet-csp": "^1.0.3",
|
||||||
"history": "^2.0.0",
|
"history": "^2.0.0",
|
||||||
|
Reference in New Issue
Block a user