Browse Source

add unseen messages counter

romanmatiasko 9 years ago
parent
commit
1445dae792

+ 17 - 9
src/css/_chat.scss

@@ -123,20 +123,28 @@
   color: darken($blue, 10%);
   margin-right: 1em;
   line-height: 50px;
+  height: 50px;
   float: right !important;
   cursor: pointer;
   font-weight: 600;
-  &:hover{
+
+  &:hover {
     color: darken($blue, 5%);
-    .new-message{
-      color: $red;
-    }
   }
-  img{
+  img {
     vertical-align: middle;
   }
-  .new-message{
-    color: $red;
-    padding-right: 1em;
-  }
+}
+
+#chat-counter {
+  position: absolute;
+  margin-left: -.5em;
+  margin-top: -.5em;
+  border-radius: 50%;
+  background: $red;
+  width: 2em;
+  height: 2em;
+  text-align: center;
+  line-height: 2em;
+  color: #fff;
 }

+ 3 - 3
src/css/_game.scss

@@ -49,10 +49,10 @@ span#game-type {
 
 #sounds-label {
   width: 800px;
-  height: 30px;
-  line-height: 30px;
+  height: 50px;
+  line-height: 50px;
   display: block;
-  margin: .5em auto;
+  margin: 0 auto;
 }
 
 #captured-pieces {

+ 1 - 4
src/css/main.scss

@@ -7,12 +7,9 @@
 .center { text-align: center !important; }
 
 @media only screen and (min-width: 760px) and (max-width: 900px) {
-  header, #board-moves-wrapper {
+  header, #board-moves-wrapper, #sounds-label {
     max-width: 760px;
   }
-  #sounds-label {
-    max-width: 530px;
-  }
   #container-wrapper {
     max-width: 900px;
     padding: 1em 10px;

+ 5 - 4
src/js/actions/ChatActions.js

@@ -2,16 +2,17 @@ const ChatConstants = require('../constants/ChatConstants');
 const AppDispatcher = require('../dispatcher/AppDispatcher');
 
 const ChatActions = {
-  toggleChat() {
+  toggleVisibility() {
     AppDispatcher.handleViewAction({
-      actionType: ChatConstants.TOGGLE_CHAT
+      actionType: ChatConstants.TOGGLE_VISIBILITY
     });
   },
-  submitMessage(message, className) {
+  submitMessage(message, className, received) {
     AppDispatcher.handleViewAction({
       actionType: ChatConstants.SUBMIT_MESSAGE,
       message: message,
-      className: className
+      className: className,
+      received: received
     });
   }
 };

+ 11 - 4
src/js/components/Chat.js

@@ -26,12 +26,12 @@ const Chat = React.createClass({
   },
   componentDidMount() {
     this.props.io.on('receive-message', data => {
-      ChatActions.submitMessage(data.message, data.color + ' left');
+      ChatActions.submitMessage(data.message, data.color + ' left', true);
       this._maybePlaySound();
     });
     ChatStore.on('change', this._onChatStoreChange);
     
-    if (window.innerWidth > 1399) ChatActions.toggleChat();
+    if (window.innerWidth > 1399) ChatActions.toggleVisibility();
   },
   componentWillUnmount() {
     ChatStore.off('change', this._onChatStoreChange);
@@ -40,14 +40,17 @@ const Chat = React.createClass({
     return (
       <div id="chat-wrapper"
            style={this.state.isChatHidden ? {display: 'none'} : null}>
+        
         <h4>Chat</h4>
         <a className="close"
-           onClick={ChatActions.toggleChat}>
+           onClick={ChatActions.toggleVisibility}>
           x
         </a>
+        
         <audio preload="auto" ref="msgSnd">
           <source src="/snd/message.mp3" />
         </audio>
+        
         <ul id="chat-list" ref="chat">
           {this.state.messages.map((message, i) => (
             <li key={i} className={message.get('className')}>
@@ -55,10 +58,13 @@ const Chat = React.createClass({
             </li>
           )).toArray()}
         </ul>
+        
         <span>Write your message:</span>
+        
         <form id="chat-form"
               onSubmit={this._submitMessage}>
           <input type="text"
+                 ref="message"
                  className={this.props.color}
                  required
                  value={this.state.message}
@@ -79,12 +85,13 @@ const Chat = React.createClass({
     const message = this.state.message;
 
     if (!isOpponentAvailable) {
+      this.refs.message.getDOMNode().blur();
       this.props.openModal('info', 'Sorry, your opponent is not connected. ' +
         'You canโ€˜t send messages.');
       return;
     }
 
-    ChatActions.submitMessage(message, color + ' right');
+    ChatActions.submitMessage(message, color + ' right', false);
     this.setState({message: ''});
 
     io.emit('send-message', {

+ 12 - 24
src/js/components/GameHeader.js

@@ -4,6 +4,7 @@ const React = require('react/addons');
 const Clock = require('./Clock');
 const ChatStore = require('../stores/ChatStore');
 const ChatActions = require('../actions/ChatActions');
+const omit = require('lodash.omit');
 
 const GameHeader = React.createClass({
   
@@ -18,26 +19,17 @@ const GameHeader = React.createClass({
   mixins: [React.addons.PureRenderMixin],
 
   getInitialState() {
-    return {
-      isChatHidden: ChatStore.getState().isChatHidden,
-      newMessage: false
-    };
+    return omit(ChatStore.getState(), 'messages');
   },
   componentDidMount() {
-    const io = this.props.io;
-
-    io.on('receive-message', () => {
-      if (this.state.isChatHidden) {
-        this.setState({newMessage: true});
-      }
-    });
-    ChatStore.on('change', this._onChatStoreChange);
+    ChatStore.on('change', this._onChatChange);
   },
   componentWillUnmount() {
-    ChatStore.off('change', this._onChatStoreChange);
+    ChatStore.off('change', this._onChatChange);
   },
   render() {
     const {io, params, gameOver, isOpponentAvailable} = this.props;
+    const unseenCount = this.state.unseenCount;
 
     return (
       <header className="clearfix">
@@ -65,9 +57,11 @@ const GameHeader = React.createClass({
         :null}
 
         <a id="chat-icon"
-           onClick={this._toggleChat}>
-          {this.state.newMessage ?
-            <span className="new-message">You have a new message!</span>
+           onClick={ChatActions.toggleVisibility}>
+          {unseenCount ?
+            <span id="chat-counter">
+              {unseenCount < 9 ? unseenCount : '9+'}
+            </span>
           :null}
           <img src="/img/chat.svg"
                width="50"
@@ -77,14 +71,8 @@ const GameHeader = React.createClass({
       </header>
     );
   },
-  _onChatStoreChange() {
-    this.setState({
-      isChatHidden: ChatStore.getState().isChatHidden
-    });
-  },
-  _toggleChat() {
-    this.setState({newMessage: false});
-    ChatActions.toggleChat();
+  _onChatChange() {
+    this.setState(omit(ChatStore.getState(), 'messages'));
   },
   _onResign() {
     const {io, params, color} = this.props;

+ 1 - 1
src/js/constants/ChatConstants.js

@@ -1,6 +1,6 @@
 const keyMirror = require('react/lib/keyMirror');
 
 module.exports = keyMirror({
-  TOGGLE_CHAT: null,
+  TOGGLE_VISIBILITY: null,
   SUBMIT_MESSAGE: null
 });

+ 22 - 8
src/js/stores/ChatStore.js

@@ -8,31 +8,45 @@ const {List, Map} = Immutable;
 const CHANGE_EVENT = 'change';
   
 var _messages = List();
+var _unseenCount = 0;
 var _isChatHidden = true;
 
 const ChatStore = Object.assign({}, EventEmitter.prototype, {
   getState() {
     return {
       messages: _messages,
+      unseenCount: _unseenCount,
       isChatHidden: _isChatHidden
     };
   }
 });
 
+function toggleVisibility() {
+  _isChatHidden = !_isChatHidden;
+  _unseenCount = 0;
+}
+
+function submitMessage(message, className, received) {
+  _messages = _messages.push(Map({
+    message: message,
+    className: className
+  }));
+
+  if (received && _isChatHidden) {
+    _unseenCount += 1;
+  }
+}
+
 AppDispatcher.register(payload => {
-  let action = payload.action;
+  const action = payload.action;
 
   switch (action.actionType) {
-
-    case ChatConstants.TOGGLE_CHAT:
-      _isChatHidden = !_isChatHidden;
+    case ChatConstants.TOGGLE_VISIBILITY:
+      toggleVisibility();
       break;
 
     case ChatConstants.SUBMIT_MESSAGE:
-      _messages = _messages.push(Map({
-        message: action.message,
-        className: action.className
-      }));
+      submitMessage(action.message, action.className, action.received);
       break;
 
     default:

+ 6 - 6
src/js/stores/GameStore.js

@@ -122,8 +122,8 @@ function gameOver(options) {
 }
 
 AppDispatcher.register(payload => {
-  var action = payload.action;
-  var emitEvent = true;
+  const action = payload.action;
+  let emitEvent = true;
 
   switch (action.actionType) {
     case GameConstants.MAKE_MOVE:
@@ -131,16 +131,16 @@ AppDispatcher.register(payload => {
         action.from, action.to, action.capture, action.emitMove);
       break;
 
-    case GameConstants.REMATCH:
-      setInitialState();
+    case GameConstants.CHANGE_PROMOTION:
+      _promotion = action.promotion;
       break;
 
     case GameConstants.GAME_OVER:
       gameOver(action.options);
       break;
 
-    case GameConstants.CHANGE_PROMOTION:
-      _promotion = action.promotion;
+    case GameConstants.REMATCH:
+      setInitialState();
       break;
 
     default: