From 804af341ac4f929329d51e60d5c35fa2ba6391f1 Mon Sep 17 00:00:00 2001
From: Kegan Dougal <kegan@matrix.org>
Date: Thu, 5 Nov 2015 14:52:44 +0000
Subject: [PATCH 1/4] Add a 'connection lost' bar.

---
 src/controllers/organisms/RoomView.js        |  9 ++++++++
 src/skins/vector/css/organisms/RoomView.css  | 24 ++++++++++++++++++++
 src/skins/vector/views/organisms/RoomView.js | 19 +++++++++++++++-
 3 files changed, 51 insertions(+), 1 deletion(-)

diff --git a/src/controllers/organisms/RoomView.js b/src/controllers/organisms/RoomView.js
index f5a8d28f..15d67b15 100644
--- a/src/controllers/organisms/RoomView.js
+++ b/src/controllers/organisms/RoomView.js
@@ -41,6 +41,7 @@ module.exports = {
             draggingFile: false,
             searching: false,
             searchResults: null,
+            syncState: MatrixClientPeg.get().getSyncState()
         }
     },
 
@@ -50,6 +51,7 @@ module.exports = {
         MatrixClientPeg.get().on("Room.name", this.onRoomName);
         MatrixClientPeg.get().on("RoomMember.typing", this.onRoomMemberTyping);
         MatrixClientPeg.get().on("RoomState.members", this.onRoomStateMember);
+        MatrixClientPeg.get().on("sync", this.onSyncStateChange);
         this.atBottom = true;
     },
 
@@ -67,6 +69,7 @@ module.exports = {
             MatrixClientPeg.get().removeListener("Room.name", this.onRoomName);
             MatrixClientPeg.get().removeListener("RoomMember.typing", this.onRoomMemberTyping);
             MatrixClientPeg.get().removeListener("RoomState.members", this.onRoomStateMember);
+            MatrixClientPeg.get().removeListener("sync", this.onSyncStateChange);
         }
     },
 
@@ -102,6 +105,12 @@ module.exports = {
         }
     },
 
+    onSyncStateChange: function(state) {
+        this.setState({
+            syncState: state
+        });
+    },
+
     // MatrixRoom still showing the messages from the old room?
     // Set the key to the room_id. Sadly you can no longer get at
     // the key from inside the component, or we'd check this in code.
diff --git a/src/skins/vector/css/organisms/RoomView.css b/src/skins/vector/css/organisms/RoomView.css
index d564b086..961a7945 100644
--- a/src/skins/vector/css/organisms/RoomView.css
+++ b/src/skins/vector/css/organisms/RoomView.css
@@ -185,6 +185,30 @@ limitations under the License.
     vertical-align: middle;
 }
 
+.mx_RoomView_connectionLostBar {
+    margin-top: 5px;
+}
+
+.mx_RoomView_connectionLostBar img {
+    padding-left: 10px;
+    padding-right: 22px;
+    vertical-align: middle;
+    float: left;
+}
+
+.mx_RoomView_connectionLostBar_textArea {
+    float: left;
+}
+
+.mx_RoomView_connectionLostBar_title {
+    color: #f00;
+}
+
+.mx_RoomView_connectionLostBar_desc {
+    color: #ddd;
+    font-size: 12px;
+}
+
 .mx_RoomView_typingBar {
     margin-top: 10px;
     margin-left: 54px;
diff --git a/src/skins/vector/views/organisms/RoomView.js b/src/skins/vector/views/organisms/RoomView.js
index 4f15ea61..86b200cf 100644
--- a/src/skins/vector/views/organisms/RoomView.js
+++ b/src/skins/vector/views/organisms/RoomView.js
@@ -197,9 +197,26 @@ module.exports = React.createClass({
             } else {
                 var typingString = this.getWhoIsTypingString();
                 var unreadMsgs = this.getUnreadMessagesString();
+                // no conn bar trumps unread count since you can't get unread messages
+                // without a connection! (technically may already have some but meh)
+                if (this.state.syncState === "ERROR") {
+                    statusBar = (
+                        <div className="mx_RoomView_connectionLostBar">
+                            <img src="img/cancel.png" width="10" height="12" alt=""/>
+                            <div className="mx_RoomView_connectionLostBar_textArea">
+                                <span className="mx_RoomView_connectionLostBar_title">
+                                Internet connection has been lost.
+                                </span>
+                                <div className="mx_RoomView_connectionLostBar_desc">
+                                Sent messages will be stored until your connection has resumed.
+                                </div>
+                            </div>
+                        </div>
+                    );
+                }
                 // unread count trumps who is typing since the unread count is only
                 // set when you've scrolled up
-                if (unreadMsgs) {
+                else if (unreadMsgs) {
                     statusBar = (
                         <div className="mx_RoomView_unreadMessagesBar" onClick={ this.scrollToBottom }>
                             <img src="img/newmessages.png" width="24" height="24" alt=""/>

From 3a8c263e8e1927caf1aa4609fe08c0f93156853d Mon Sep 17 00:00:00 2001
From: Kegan Dougal <kegan@matrix.org>
Date: Thu, 5 Nov 2015 15:59:03 +0000
Subject: [PATCH 2/4] Add resending bar (and resend all option)

Factor out resend logic which was in the context menu into a separate
Resend file (it shouldn't be in the skin, but it also isn't really
suitable for a controller given 2 different views invoke it..)
---
 src/Resend.js                                 | 24 +++++++++++++++
 src/controllers/organisms/RoomView.js         | 30 +++++++++++++++++--
 src/skins/vector/css/organisms/RoomView.css   |  4 +++
 .../views/molecules/MessageContextMenu.js     | 16 ++--------
 src/skins/vector/views/organisms/RoomView.js  | 20 +++++++++++++
 5 files changed, 78 insertions(+), 16 deletions(-)
 create mode 100644 src/Resend.js

diff --git a/src/Resend.js b/src/Resend.js
new file mode 100644
index 00000000..52b7c936
--- /dev/null
+++ b/src/Resend.js
@@ -0,0 +1,24 @@
+var MatrixClientPeg = require('matrix-react-sdk/lib/MatrixClientPeg');
+var dis = require('matrix-react-sdk/lib/dispatcher');
+
+module.exports = {
+    resend: function(event) {
+        MatrixClientPeg.get().resendEvent(
+            event, MatrixClientPeg.get().getRoom(event.getRoomId())
+        ).done(function() {
+            dis.dispatch({
+                action: 'message_sent',
+                event: event
+            });
+        }, function() {
+            dis.dispatch({
+                action: 'message_send_failed',
+                event: event
+            });
+        });
+        dis.dispatch({
+            action: 'message_resend_started',
+            event: event
+        });
+    },
+};
\ No newline at end of file
diff --git a/src/controllers/organisms/RoomView.js b/src/controllers/organisms/RoomView.js
index 15d67b15..1324e21f 100644
--- a/src/controllers/organisms/RoomView.js
+++ b/src/controllers/organisms/RoomView.js
@@ -24,6 +24,7 @@ var Modal = require("matrix-react-sdk/lib/Modal");
 var sdk = require('matrix-react-sdk/lib/index');
 var CallHandler = require('matrix-react-sdk/lib/CallHandler');
 var VectorConferenceHandler = require('../../modules/VectorConferenceHandler');
+var Resend = require("../../Resend");
 
 var dis = require("matrix-react-sdk/lib/dispatcher");
 
@@ -32,8 +33,9 @@ var INITIAL_SIZE = 20;
 
 module.exports = {
     getInitialState: function() {
+        var room = this.props.roomId ? MatrixClientPeg.get().getRoom(this.props.roomId) : null;
         return {
-            room: this.props.roomId ? MatrixClientPeg.get().getRoom(this.props.roomId) : null,
+            room: room,
             messageCap: INITIAL_SIZE,
             editingRoomSettings: false,
             uploadingRoomSettings: false,
@@ -41,7 +43,8 @@ module.exports = {
             draggingFile: false,
             searching: false,
             searchResults: null,
-            syncState: MatrixClientPeg.get().getSyncState()
+            syncState: MatrixClientPeg.get().getSyncState(),
+            hasUnsentMessages: this._hasUnsentMessages(room)
         }
     },
 
@@ -77,6 +80,9 @@ module.exports = {
         switch (payload.action) {
             case 'message_send_failed':
             case 'message_sent':
+                this.setState({
+                    hasUnsentMessages: this._hasUnsentMessages(this.state.room)
+                });
             case 'message_resend_started':
                 this.setState({
                     room: MatrixClientPeg.get().getRoom(this.props.roomId)
@@ -182,6 +188,19 @@ module.exports = {
         this._updateConfCallNotification();
     },
 
+    _hasUnsentMessages: function(room) {
+        return this._getUnsentMessages(room).length > 0;
+    },
+
+    _getUnsentMessages: function(room) {
+        if (!room) { return []; }
+        // TODO: It would be nice if the JS SDK provided nicer constant-time
+        // constructs rather than O(N) (N=num msgs) on this.
+        return room.timeline.filter(function(ev) {
+            return ev.status === Matrix.EventStatus.NOT_SENT;
+        });
+    },
+
     _updateConfCallNotification: function() {
         var room = MatrixClientPeg.get().getRoom(this.props.roomId);
         if (!room) return;
@@ -274,6 +293,13 @@ module.exports = {
         return false;
     },
 
+    onResendAllClick: function() {
+        var eventsToResend = this._getUnsentMessages(this.state.room);
+        eventsToResend.forEach(function(event) {
+            Resend.resend(event);
+        });
+    },
+
     onJoinButtonClicked: function(ev) {
         var self = this;
         MatrixClientPeg.get().joinRoom(this.props.roomId).then(function() {
diff --git a/src/skins/vector/css/organisms/RoomView.css b/src/skins/vector/css/organisms/RoomView.css
index 961a7945..0379a2aa 100644
--- a/src/skins/vector/css/organisms/RoomView.css
+++ b/src/skins/vector/css/organisms/RoomView.css
@@ -209,6 +209,10 @@ limitations under the License.
     font-size: 12px;
 }
 
+.mx_RoomView_resend_link {
+    cursor: pointer;
+}
+
 .mx_RoomView_typingBar {
     margin-top: 10px;
     margin-left: 54px;
diff --git a/src/skins/vector/views/molecules/MessageContextMenu.js b/src/skins/vector/views/molecules/MessageContextMenu.js
index 995c2c4b..b36d4ccb 100644
--- a/src/skins/vector/views/molecules/MessageContextMenu.js
+++ b/src/skins/vector/views/molecules/MessageContextMenu.js
@@ -22,25 +22,13 @@ var MatrixClientPeg = require('matrix-react-sdk/lib/MatrixClientPeg');
 var dis = require('matrix-react-sdk/lib/dispatcher');
 var sdk = require('matrix-react-sdk')
 var Modal = require('matrix-react-sdk/lib/Modal');
+var Resend = require("../../../../Resend");
 
 module.exports = React.createClass({
     displayName: 'MessageContextMenu',
 
     onResendClick: function() {
-        MatrixClientPeg.get().resendEvent(
-            this.props.mxEvent, MatrixClientPeg.get().getRoom(
-                this.props.mxEvent.getRoomId()
-            )
-        ).done(function() {
-            dis.dispatch({
-                action: 'message_sent'
-            });
-        }, function() {
-            dis.dispatch({
-                action: 'message_send_failed'
-            });
-        });
-        dis.dispatch({action: 'message_resend_started'});
+        Resend.resend(this.props.mxEvent);
         if (this.props.onFinished) this.props.onFinished();
     },
 
diff --git a/src/skins/vector/views/organisms/RoomView.js b/src/skins/vector/views/organisms/RoomView.js
index 86b200cf..1eda7b7f 100644
--- a/src/skins/vector/views/organisms/RoomView.js
+++ b/src/skins/vector/views/organisms/RoomView.js
@@ -199,6 +199,8 @@ module.exports = React.createClass({
                 var unreadMsgs = this.getUnreadMessagesString();
                 // no conn bar trumps unread count since you can't get unread messages
                 // without a connection! (technically may already have some but meh)
+                // It also trumps the "some not sent" msg since you can't resend without
+                // a connection!
                 if (this.state.syncState === "ERROR") {
                     statusBar = (
                         <div className="mx_RoomView_connectionLostBar">
@@ -214,6 +216,24 @@ module.exports = React.createClass({
                         </div>
                     );
                 }
+                else if (this.state.hasUnsentMessages) {
+                    statusBar = (
+                        <div className="mx_RoomView_connectionLostBar">
+                            <img src="img/cancel.png" width="10" height="12" alt=""/>
+                            <div className="mx_RoomView_connectionLostBar_textArea">
+                                <span className="mx_RoomView_connectionLostBar_title">
+                                Some of your messages have not been sent.
+                                </span>
+                                <div className="mx_RoomView_connectionLostBar_desc">
+                                    <span className="mx_RoomView_resend_link"
+                                        onClick={ this.onResendAllClick } >
+                                    Resend all now
+                                    </span> or select individual messages to re-send.
+                                </div>
+                            </div>
+                        </div>
+                    );
+                }
                 // unread count trumps who is typing since the unread count is only
                 // set when you've scrolled up
                 else if (unreadMsgs) {

From 615879ffddde97b3d3aa4343ebffaa8686f4f8fd Mon Sep 17 00:00:00 2001
From: Matthew Hodgson <matthew@matrix.org>
Date: Mon, 9 Nov 2015 00:13:25 +0000
Subject: [PATCH 3/4] skin to match CSS (spacing will need to be fixed once
 matthew/ordered-roomlist lands)

---
 src/skins/vector/css/molecules/EventTile.css |  2 +-
 src/skins/vector/css/organisms/RoomView.css  | 21 +++++++++--------
 src/skins/vector/views/organisms/RoomView.js | 24 ++++++++++----------
 3 files changed, 24 insertions(+), 23 deletions(-)

diff --git a/src/skins/vector/css/molecules/EventTile.css b/src/skins/vector/css/molecules/EventTile.css
index eb59711e..f9c8551b 100644
--- a/src/skins/vector/css/molecules/EventTile.css
+++ b/src/skins/vector/css/molecules/EventTile.css
@@ -78,7 +78,7 @@ limitations under the License.
 }
 
 .mx_EventTile_notSent {
-    color: #f11;
+    color: #ddd;
 }
 
 .mx_EventTile_highlight {
diff --git a/src/skins/vector/css/organisms/RoomView.css b/src/skins/vector/css/organisms/RoomView.css
index 0379a2aa..508e3457 100644
--- a/src/skins/vector/css/organisms/RoomView.css
+++ b/src/skins/vector/css/organisms/RoomView.css
@@ -158,13 +158,14 @@ limitations under the License.
     order: 4;
 
     width: 100%;
-    -webkit-flex: 0 0 36px;
-    flex: 0 0 36px;
+    -webkit-flex: 0 0 auto;
+    flex: 0 0 auto;
 }
 
 .mx_RoomView_statusAreaBox {
     max-width: 960px;
     margin: auto;
+    min-height: 36px;
 }
 
 .mx_RoomView_statusAreaBox_line {
@@ -186,7 +187,8 @@ limitations under the License.
 }
 
 .mx_RoomView_connectionLostBar {
-    margin-top: 5px;
+    margin-top: 19px;
+    height: 58px;
 }
 
 .mx_RoomView_connectionLostBar img {
@@ -196,20 +198,19 @@ limitations under the License.
     float: left;
 }
 
-.mx_RoomView_connectionLostBar_textArea {
-    float: left;
-}
-
 .mx_RoomView_connectionLostBar_title {
-    color: #f00;
+    color: #ff0064;
 }
 
 .mx_RoomView_connectionLostBar_desc {
-    color: #ddd;
-    font-size: 12px;
+    color: #454545;
+    font-size: 14px;
+    opacity: 0.5;
 }
 
 .mx_RoomView_resend_link {
+    color: #454545 ! important;
+    text-decoration: underline ! important;
     cursor: pointer;
 }
 
diff --git a/src/skins/vector/views/organisms/RoomView.js b/src/skins/vector/views/organisms/RoomView.js
index 1eda7b7f..c07a890c 100644
--- a/src/skins/vector/views/organisms/RoomView.js
+++ b/src/skins/vector/views/organisms/RoomView.js
@@ -204,13 +204,13 @@ module.exports = React.createClass({
                 if (this.state.syncState === "ERROR") {
                     statusBar = (
                         <div className="mx_RoomView_connectionLostBar">
-                            <img src="img/cancel.png" width="10" height="12" alt=""/>
+                            <img src="img/warning2.png" width="30" height="30" alt="/!\"/>
                             <div className="mx_RoomView_connectionLostBar_textArea">
-                                <span className="mx_RoomView_connectionLostBar_title">
-                                Internet connection has been lost.
-                                </span>
+                                <div className="mx_RoomView_connectionLostBar_title">
+                                    Connectivity to the server has been lost.
+                                </div>
                                 <div className="mx_RoomView_connectionLostBar_desc">
-                                Sent messages will be stored until your connection has resumed.
+                                    Sent messages will be stored until your connection has returned.
                                 </div>
                             </div>
                         </div>
@@ -219,16 +219,16 @@ module.exports = React.createClass({
                 else if (this.state.hasUnsentMessages) {
                     statusBar = (
                         <div className="mx_RoomView_connectionLostBar">
-                            <img src="img/cancel.png" width="10" height="12" alt=""/>
+                            <img src="img/warning2.png" width="30" height="30" alt="/!\"/>
                             <div className="mx_RoomView_connectionLostBar_textArea">
-                                <span className="mx_RoomView_connectionLostBar_title">
-                                Some of your messages have not been sent.
-                                </span>
+                                <div className="mx_RoomView_connectionLostBar_title">
+                                    Some of your messages have not been sent.
+                                </div>
                                 <div className="mx_RoomView_connectionLostBar_desc">
-                                    <span className="mx_RoomView_resend_link"
-                                        onClick={ this.onResendAllClick } >
+                                    <a className="mx_RoomView_resend_link"
+                                        onClick={ this.onResendAllClick }>
                                     Resend all now
-                                    </span> or select individual messages to re-send.
+                                    </a> or select individual messages to re-send.
                                 </div>
                             </div>
                         </div>

From eaa2f94327ef60c3fe622f2837a6ae14ee1d8344 Mon Sep 17 00:00:00 2001
From: Matthew Hodgson <matthew@matrix.org>
Date: Mon, 9 Nov 2015 00:13:40 +0000
Subject: [PATCH 4/4] warning png

---
 src/skins/vector/img/warning2.png | Bin 0 -> 1420 bytes
 1 file changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 src/skins/vector/img/warning2.png

diff --git a/src/skins/vector/img/warning2.png b/src/skins/vector/img/warning2.png
new file mode 100644
index 0000000000000000000000000000000000000000..db0fd4a897de34f13f949afd4f3c73a6c48f3b4e
GIT binary patch
literal 1420
zcmeAS@N?(olHy`uVBq!ia0vp^av;pX1SGcvS$+jlk|nMYCBgY=CFO}lsSJ)O`AMk?
zp1FzXsX?iUDV2pMQ*9U+m^Cs(B1$5BeXNr6bM+EIYV;~{3xK*A7;Nk-3KEmEQ%e+*
zQqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn
zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxOgGuU%v{0TQqR!T
z+}y-mN5ROz&{W^RSl`${*T~q)#K6kLNC66zfVLH-q*(>IxIyg#@@$ndN=gc>^!3Zj
z%k|2Q_413-^$jg8E%gnI^o@*kfhu&1EAvVcD|GXUm0>2hq!uR^WfqiV=I1GZOiWD5
zFD<cE0=g99h1>$Tv3bSNU;+l1ennz|zM-B0$V)JVzP|XC=H|jx7ncO3BHWAB;Np<V
zf>iyW)Z+ZoqGVvir744~DzI`cN=+=uFAB-e&w+(vKt_H^esM;Afr7I$IMft0d=ry1
z^FV@{U|qhxR-SpqC5d^-sh%#jN<ee;GBZ=GTpW#Djhx&Z&0Snv3=LgP9UTqLot({$
z%-k%T%?&NgVR~KilS^|`^GaZPQxJNcaOwpmh};68%`T}$nPsUdZbkXI3Sf_0W#V>=
z1y1vzdQ)(_#S*7peV}9XLD7p8-7q0w8Uiuli5JL$C;!wuV45!iCT{(x|F0Ps7=L-X
zIEGZ*dNa*d$2m~s_<!BA+KyQpB{`W@#h5wSnq5_rwnpS6Wh$F;h&3Gv3hH$22)N(U
z@m_DK=&1+FE2o)Uy%LZ+HRzS8*VOX+?72w=_ddL{{XX};_47N`=l3p3FzEQvAyGJa
ze}mn|#jE6R>SoMzdN8xUwIh7pTfGyvAAD)s&8vFh%JCDgmwn`zK21^9%rB|YEW#l1
z$EpyYChZuxj8-xE2}P2hCOh@oY9IdJvM{`F<3F+8JZ7fXc3)KI=U=((=JUy-zU^z~
zRK53@I7^~RKKf8su{zuK`YySvj~~2RJ!z5B+i#V3ezq=_XU>@W)MNVL=6j(JGrp?n
z<?T@4nJwwpFR<k~<J|6xtJr@f3apox<#xJo_jF*jii+NhyDdMq6+C>>Ubf@n;VF)s
zit7I71I~VM-hTGrca1ftdfz^JFp0bNT|-*?%{V9h-3%pR%VZfemhaSxzssQ3!*YL8
z0AJ1>;oA6YH&?xb*}H^%C+#qr5fbt0K!KRx+ADSIA|K@^vuu79^d!|`R@);PrCIAD
zqm+ezec;M|qLs6)ReHPKwx55tY!OnvbvD>+_Tt(T(G&d>kDU58^B&K9`I8|gR*kC<
z%vzWD$VR|bpnJdSl5HF_U+%X(CVg6As)4TEZtWEpy1waH6hCl1aaFz5$x$tf>tc-N
j2bcf*ZI3#|u}Uyh-(Gi7sw~YCR6csT`njxgN@xNAYH|*X

literal 0
HcmV?d00001