From 553c53e7e858a5f0e8f5975bfcc033335874ea7f Mon Sep 17 00:00:00 2001
From: Richard Lewis <rick@matrix.org>
Date: Wed, 17 May 2017 12:33:58 +0100
Subject: [PATCH 01/30] Add apps menu icons

---
 .../views/rooms/_MessageComposer.scss         |  6 +++--
 src/skins/vector/img/icons-apps-active.svg    | 24 +++++++++++++++++++
 src/skins/vector/img/icons-apps.svg           | 14 +++++++++++
 3 files changed, 42 insertions(+), 2 deletions(-)
 create mode 100644 src/skins/vector/img/icons-apps-active.svg
 create mode 100644 src/skins/vector/img/icons-apps.svg

diff --git a/src/skins/vector/css/matrix-react-sdk/views/rooms/_MessageComposer.scss b/src/skins/vector/css/matrix-react-sdk/views/rooms/_MessageComposer.scss
index 525cc1f6..ac497227 100644
--- a/src/skins/vector/css/matrix-react-sdk/views/rooms/_MessageComposer.scss
+++ b/src/skins/vector/css/matrix-react-sdk/views/rooms/_MessageComposer.scss
@@ -128,7 +128,8 @@ limitations under the License.
 .mx_MessageComposer_upload,
 .mx_MessageComposer_hangup,
 .mx_MessageComposer_voicecall,
-.mx_MessageComposer_videocall {
+.mx_MessageComposer_videocall,
+.mx_MessageComposer_apps {
     /*display: table-cell;*/
     /*vertical-align: middle;*/
     /*padding-left: 10px;*/
@@ -140,7 +141,8 @@ limitations under the License.
 .mx_MessageComposer_upload object,
 .mx_MessageComposer_hangup object,
 .mx_MessageComposer_voicecall object,
-.mx_MessageComposer_videocall object {
+.mx_MessageComposer_videocall object,
+.mx_MessageComposer_apps object {
     pointer-events: none;
 }
 
diff --git a/src/skins/vector/img/icons-apps-active.svg b/src/skins/vector/img/icons-apps-active.svg
new file mode 100644
index 00000000..ea222d05
--- /dev/null
+++ b/src/skins/vector/img/icons-apps-active.svg
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 15.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="35px"
+	 height="35px" viewBox="0 0 35 35" enable-background="new 0 0 35 35" xml:space="preserve">
+<g id="Layer_1">
+	<path id="Oval-109-Copy" fill="#76CFA6" enable-background="new    " d="M17.5,35C27.165,35,35,27.165,35,17.5S27.165,0,17.5,0
+		S0,7.835,0,17.5S7.835,35,17.5,35z"/>
+	<g id="Icon">
+		<g>
+			<path fill="none" stroke="#FFFFFF" d="M7.5,12.5h5v-5h-5V12.5z M15,27.5h5v-5h-5V27.5z M7.5,27.5h5v-5h-5V27.5z M7.5,20h5v-5h-5
+				V20z M15,20h5v-5h-5V20z M22.5,7.5v5h5v-5H22.5z M15,12.5h5v-5h-5V12.5z M22.5,20h5v-5h-5V20z M22.5,27.5h5v-5h-5V27.5z"/>
+		</g>
+	</g>
+</g>
+<g id="Layer_2">
+	<g id="Icon_1_" opacity="0.15">
+		<g>
+			<path fill="none" stroke="#76CFA6" d="M7.5,12.5h5v-5h-5V12.5z M15,27.5h5v-5h-5V27.5z M7.5,27.5h5v-5h-5V27.5z M7.5,20h5v-5h-5
+				V20z M15,20h5v-5h-5V20z M22.5,7.5v5h5v-5H22.5z M15,12.5h5v-5h-5V12.5z M22.5,20h5v-5h-5V20z M22.5,27.5h5v-5h-5V27.5z"/>
+		</g>
+	</g>
+</g>
+</svg>
diff --git a/src/skins/vector/img/icons-apps.svg b/src/skins/vector/img/icons-apps.svg
new file mode 100644
index 00000000..affd8e64
--- /dev/null
+++ b/src/skins/vector/img/icons-apps.svg
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 15.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+	 width="35px" height="35px" viewBox="0 0 35 35" enable-background="new 0 0 35 35" xml:space="preserve">
+<path id="Oval-109-Copy" opacity="0.15" fill="#76CFA6" enable-background="new    " d="M17.5,35C27.165,35,35,27.165,35,17.5
+	S27.165,0,17.5,0S0,7.835,0,17.5S7.835,35,17.5,35z"/>
+<g id="Icon">
+	<g>
+		<path fill="none" stroke="#76CFA6" d="M7.5,12.5h5v-5h-5V12.5z M15,27.5h5v-5h-5V27.5z M7.5,27.5h5v-5h-5V27.5z M7.5,20h5v-5h-5
+			V20z M15,20h5v-5h-5V20z M22.5,7.5v5h5v-5H22.5z M15,12.5h5v-5h-5V12.5z M22.5,20h5v-5h-5V20z M22.5,27.5h5v-5h-5V27.5z"/>
+	</g>
+</g>
+</svg>

From 7ea6157b6749d08e0917f6a5338a6ac2e62ef608 Mon Sep 17 00:00:00 2001
From: Richard Lewis <rick@matrix.org>
Date: Wed, 17 May 2017 21:16:15 +0100
Subject: [PATCH 02/30] App drawer styling

---
 .../css/matrix-react-sdk/views/rooms/_MessageComposer.scss   | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/src/skins/vector/css/matrix-react-sdk/views/rooms/_MessageComposer.scss b/src/skins/vector/css/matrix-react-sdk/views/rooms/_MessageComposer.scss
index ac497227..b7de8dcd 100644
--- a/src/skins/vector/css/matrix-react-sdk/views/rooms/_MessageComposer.scss
+++ b/src/skins/vector/css/matrix-react-sdk/views/rooms/_MessageComposer.scss
@@ -202,3 +202,8 @@ limitations under the License.
     padding: 4px 4px 4px 0;
     opacity: 0.8;
 }
+
+.mx_AppsDrawer {
+    height: 50px;
+    width: 100%;
+}

From 7a63cfd71746c5626f2ee0ae286939a0421ebcb6 Mon Sep 17 00:00:00 2001
From: Richard Lewis <rick@matrix.org>
Date: Wed, 17 May 2017 23:17:53 +0100
Subject: [PATCH 03/30] Add stylesheet for apps

---
 src/skins/vector/css/_components.scss         |  1 +
 .../views/rooms/_AppsDrawer.scss              | 22 +++++++++++++++++++
 2 files changed, 23 insertions(+)
 create mode 100644 src/skins/vector/css/matrix-react-sdk/views/rooms/_AppsDrawer.scss

diff --git a/src/skins/vector/css/_components.scss b/src/skins/vector/css/_components.scss
index c22fbc06..7d78404d 100644
--- a/src/skins/vector/css/_components.scss
+++ b/src/skins/vector/css/_components.scss
@@ -51,6 +51,7 @@
 @import "./matrix-react-sdk/views/rooms/_SearchableEntityList.scss";
 @import "./matrix-react-sdk/views/rooms/_TabCompleteBar.scss";
 @import "./matrix-react-sdk/views/rooms/_TopUnreadMessagesBar.scss";
+@import "./matrix-react-sdk/views/rooms/_AppsDrawer.scss";
 @import "./matrix-react-sdk/views/settings/_DevicesPanel.scss";
 @import "./matrix-react-sdk/views/settings/_IntegrationsManager.scss";
 @import "./matrix-react-sdk/views/voip/_CallView.scss";
diff --git a/src/skins/vector/css/matrix-react-sdk/views/rooms/_AppsDrawer.scss b/src/skins/vector/css/matrix-react-sdk/views/rooms/_AppsDrawer.scss
new file mode 100644
index 00000000..7b20e4db
--- /dev/null
+++ b/src/skins/vector/css/matrix-react-sdk/views/rooms/_AppsDrawer.scss
@@ -0,0 +1,22 @@
+/*
+Copyright 2015, 2016 OpenMarket Ltd
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+.mx_AddWidget_button {
+    order: 2;
+    cursor: pointer;
+    padding-left: 12px;
+    padding-right: 12px;
+}

From 8afc9f9a09d9efd00d22dd3dfa3d929383880d68 Mon Sep 17 00:00:00 2001
From: Richard Lewis <rick@matrix.org>
Date: Fri, 19 May 2017 17:17:48 +0100
Subject: [PATCH 04/30] Add widget iframes and styling

---
 .../views/rooms/_AppsDrawer.scss              | 44 +++++++++++++++++++
 .../views/rooms/_MessageComposer.scss         |  5 ---
 2 files changed, 44 insertions(+), 5 deletions(-)

diff --git a/src/skins/vector/css/matrix-react-sdk/views/rooms/_AppsDrawer.scss b/src/skins/vector/css/matrix-react-sdk/views/rooms/_AppsDrawer.scss
index 7b20e4db..23c4ca48 100644
--- a/src/skins/vector/css/matrix-react-sdk/views/rooms/_AppsDrawer.scss
+++ b/src/skins/vector/css/matrix-react-sdk/views/rooms/_AppsDrawer.scss
@@ -14,9 +14,53 @@ See the License for the specific language governing permissions and
 limitations under the License.
 */
 
+.mx_AppsDrawer {
+}
+
+.mx_AppsContainer {
+    display: flex;
+    flex-direction: row;
+    align-items: center;
+}
+
 .mx_AddWidget_button {
     order: 2;
     cursor: pointer;
     padding-left: 12px;
     padding-right: 12px;
+    margin: 0 20px 20px 20px;
+    color: $accent-color;
+}
+
+.mx_SetAppURLDialog_input {
+    border-radius: 3px;
+    border: 1px solid $input-border-color;
+    padding: 9px;
+    color: $primary-fg-color;
+    background-color: $primary-bg-color;
+    font-size: 15px;
+}
+
+.mx_AppTile {
+    width: 48%;
+    margin: 2px 5px;
+    border: 1px solid $greyed-fg-color;
+    border-radius: 2px;
+    height: 200px;
+    // display: inline-block;
+}
+
+.mx_AppTileMenuBar {
+    // height: 15px;
+    margin: 0;
+    padding: 2px 10px;
+    // background-color: $e2e-verified-color;
+    border-bottom: 1px solid $greyed-fg-color;
+}
+
+.mx_AppTileBody iframe {
+    width: 100%;
+    height: 175px;
+    overflow: hidden;
+    border: none;
 }
diff --git a/src/skins/vector/css/matrix-react-sdk/views/rooms/_MessageComposer.scss b/src/skins/vector/css/matrix-react-sdk/views/rooms/_MessageComposer.scss
index b7de8dcd..ac497227 100644
--- a/src/skins/vector/css/matrix-react-sdk/views/rooms/_MessageComposer.scss
+++ b/src/skins/vector/css/matrix-react-sdk/views/rooms/_MessageComposer.scss
@@ -202,8 +202,3 @@ limitations under the License.
     padding: 4px 4px 4px 0;
     opacity: 0.8;
 }
-
-.mx_AppsDrawer {
-    height: 50px;
-    width: 100%;
-}

From 998739a7dcc0c98b29fa19e016c5f825761a36ac Mon Sep 17 00:00:00 2001
From: Richard Lewis <rick@matrix.org>
Date: Mon, 22 May 2017 18:00:40 +0100
Subject: [PATCH 05/30] Edit icon and styling

---
 .../views/rooms/_AppsDrawer.scss              | 33 ++++++++++++++-----
 src/skins/vector/img/edit.svg                 | 13 ++++++++
 2 files changed, 38 insertions(+), 8 deletions(-)
 create mode 100644 src/skins/vector/img/edit.svg

diff --git a/src/skins/vector/css/matrix-react-sdk/views/rooms/_AppsDrawer.scss b/src/skins/vector/css/matrix-react-sdk/views/rooms/_AppsDrawer.scss
index 23c4ca48..bf838b85 100644
--- a/src/skins/vector/css/matrix-react-sdk/views/rooms/_AppsDrawer.scss
+++ b/src/skins/vector/css/matrix-react-sdk/views/rooms/_AppsDrawer.scss
@@ -26,25 +26,26 @@ limitations under the License.
 .mx_AddWidget_button {
     order: 2;
     cursor: pointer;
-    padding-left: 12px;
     padding-right: 12px;
-    margin: 0 20px 20px 20px;
+    padding: 0;
+    margin: 0 0 5px 0;
     color: $accent-color;
+    font-size: 12px;
 }
 
 .mx_SetAppURLDialog_input {
     border-radius: 3px;
     border: 1px solid $input-border-color;
     padding: 9px;
-    color: $primary-fg-color;
+    color: $primary-hairline-color;
     background-color: $primary-bg-color;
     font-size: 15px;
 }
 
 .mx_AppTile {
-    width: 48%;
-    margin: 2px 5px;
-    border: 1px solid $greyed-fg-color;
+    width: 50%;
+    margin: 0 5px 2px 0;
+    border: 1px solid $primary-hairline-color;
     border-radius: 2px;
     height: 200px;
     // display: inline-block;
@@ -55,12 +56,28 @@ limitations under the License.
     margin: 0;
     padding: 2px 10px;
     // background-color: $e2e-verified-color;
-    border-bottom: 1px solid $greyed-fg-color;
+    border-bottom: 1px solid $primary-hairline-color;
+    font-size: 10px;
+}
+
+.mx_AppTileMenuBarWidgets {
+    float: right;
+}
+.mx_AppTileMenuBarWidget {
+    // pointer-events: none;
+    cursor: pointer;
 }
 
 .mx_AppTileBody iframe {
     width: 100%;
-    height: 175px;
+    height: 181px;
     overflow: hidden;
     border: none;
 }
+
+.mx_CloseAppWidget {
+}
+
+.mx_AppTileMenuBarWidgetPadding {
+    margin-right: 5px;
+}
diff --git a/src/skins/vector/img/edit.svg b/src/skins/vector/img/edit.svg
new file mode 100644
index 00000000..a0be3454
--- /dev/null
+++ b/src/skins/vector/img/edit.svg
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 16.2.1, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+	 width="512px" height="512px" viewBox="0 0 512 512" enable-background="new 0 0 512 512" xml:space="preserve">
+<g>
+	
+		<rect x="178.846" y="92.087" transform="matrix(-0.7071 -0.7071 0.7071 -0.7071 224.3476 631.1498)" width="128.085" height="354.049"/>
+	<path d="M471.723,88.393l-48.115-48.114c-11.723-11.724-31.558-10.896-44.304,1.85l-45.202,45.203l90.569,90.568l45.202-45.202
+		C482.616,119.952,483.445,100.116,471.723,88.393z"/>
+	<polygon points="64.021,363.252 32,480 148.737,447.979 	"/>
+</g>
+</svg>

From ac9075a82a24234f8b6698f4070549b0d45abcd1 Mon Sep 17 00:00:00 2001
From: Richard Lewis <rick@matrix.org>
Date: Tue, 30 May 2017 10:47:29 +0100
Subject: [PATCH 06/30] Increase panel height

---
 .../vector/css/matrix-react-sdk/views/rooms/_AppsDrawer.scss  | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/skins/vector/css/matrix-react-sdk/views/rooms/_AppsDrawer.scss b/src/skins/vector/css/matrix-react-sdk/views/rooms/_AppsDrawer.scss
index bf838b85..ae39e7f7 100644
--- a/src/skins/vector/css/matrix-react-sdk/views/rooms/_AppsDrawer.scss
+++ b/src/skins/vector/css/matrix-react-sdk/views/rooms/_AppsDrawer.scss
@@ -47,7 +47,7 @@ limitations under the License.
     margin: 0 5px 2px 0;
     border: 1px solid $primary-hairline-color;
     border-radius: 2px;
-    height: 200px;
+    height: 350px;
     // display: inline-block;
 }
 
@@ -70,7 +70,7 @@ limitations under the License.
 
 .mx_AppTileBody iframe {
     width: 100%;
-    height: 181px;
+    height: 331px;
     overflow: hidden;
     border: none;
 }

From 113533ad6148bcc6d22178293cf9e2dc1cfa5717 Mon Sep 17 00:00:00 2001
From: Richard Lewis <rick@matrix.org>
Date: Wed, 31 May 2017 10:08:23 +0100
Subject: [PATCH 07/30] Add styling for full-width widgets

---
 .../css/matrix-react-sdk/views/rooms/_AppsDrawer.scss    | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/src/skins/vector/css/matrix-react-sdk/views/rooms/_AppsDrawer.scss b/src/skins/vector/css/matrix-react-sdk/views/rooms/_AppsDrawer.scss
index ae39e7f7..0d8ada36 100644
--- a/src/skins/vector/css/matrix-react-sdk/views/rooms/_AppsDrawer.scss
+++ b/src/skins/vector/css/matrix-react-sdk/views/rooms/_AppsDrawer.scss
@@ -51,6 +51,15 @@ limitations under the License.
     // display: inline-block;
 }
 
+.mx_AppTileFullWidth {
+    width: 100%;
+    margin: 0 5px 2px 0;
+    border: 1px solid $primary-hairline-color;
+    border-radius: 2px;
+    height: 350px;
+    // display: inline-block;
+}
+
 .mx_AppTileMenuBar {
     // height: 15px;
     margin: 0;

From 0ef800073bac036ed797ff10d6911ff2d39271e4 Mon Sep 17 00:00:00 2001
From: Richard Lewis <rick@matrix.org>
Date: Mon, 5 Jun 2017 18:21:52 +0100
Subject: [PATCH 08/30] App drawer styling

---
 .../matrix-react-sdk/views/rooms/_AppsDrawer.scss    | 12 ++++++++----
 1 file changed, 8 insertions(+), 4 deletions(-)

diff --git a/src/skins/vector/css/matrix-react-sdk/views/rooms/_AppsDrawer.scss b/src/skins/vector/css/matrix-react-sdk/views/rooms/_AppsDrawer.scss
index 0d8ada36..db217573 100644
--- a/src/skins/vector/css/matrix-react-sdk/views/rooms/_AppsDrawer.scss
+++ b/src/skins/vector/css/matrix-react-sdk/views/rooms/_AppsDrawer.scss
@@ -47,16 +47,17 @@ limitations under the License.
     margin: 0 5px 2px 0;
     border: 1px solid $primary-hairline-color;
     border-radius: 2px;
-    height: 350px;
+    // height: 350px;
     // display: inline-block;
 }
 
 .mx_AppTileFullWidth {
     width: 100%;
-    margin: 0 5px 2px 0;
+    margin: 0;
+    padding: 0;
     border: 1px solid $primary-hairline-color;
     border-radius: 2px;
-    height: 350px;
+    // height: 350px;
     // display: inline-block;
 }
 
@@ -79,9 +80,12 @@ limitations under the License.
 
 .mx_AppTileBody iframe {
     width: 100%;
-    height: 331px;
+    height: 350px;
     overflow: hidden;
     border: none;
+    padding: 0;
+    margin: 0;
+    display: block;
 }
 
 .mx_CloseAppWidget {

From 76880e0de7a7e982283432d141c69acd5520f5e2 Mon Sep 17 00:00:00 2001
From: Richard Lewis <rick@matrix.org>
Date: Tue, 6 Jun 2017 23:46:27 +0100
Subject: [PATCH 09/30] App icon tile styling

---
 .../views/rooms/_AppsDrawer.scss                | 17 +++++++++++++++++
 1 file changed, 17 insertions(+)

diff --git a/src/skins/vector/css/matrix-react-sdk/views/rooms/_AppsDrawer.scss b/src/skins/vector/css/matrix-react-sdk/views/rooms/_AppsDrawer.scss
index db217573..840fd963 100644
--- a/src/skins/vector/css/matrix-react-sdk/views/rooms/_AppsDrawer.scss
+++ b/src/skins/vector/css/matrix-react-sdk/views/rooms/_AppsDrawer.scss
@@ -94,3 +94,20 @@ limitations under the License.
 .mx_AppTileMenuBarWidgetPadding {
     margin-right: 5px;
 }
+
+.mx_AppIconTile {
+    box-shadow: 0 4px 8px 0 rgba(0,0,0,0.2);
+    transition: 0.3s;
+}
+
+.mx_AppIconTile:hover {
+    box-shadow: 0 8px 16px 0 rgba(0,0,0,0.2);
+}
+
+.mx_AppIconTile_content {
+    padding: 2px 16px;
+}
+
+.mx_AppIconTile_image {
+    width: 100%;
+}

From 9302c60b47699d42a5dcf08a7b7cca4a564be9f8 Mon Sep 17 00:00:00 2001
From: Richard Lewis <rick@matrix.org>
Date: Wed, 7 Jun 2017 10:55:35 +0100
Subject: [PATCH 10/30] App icon styling

---
 .../views/rooms/_AppsDrawer.scss              | 34 +++++++++++++++++--
 1 file changed, 32 insertions(+), 2 deletions(-)

diff --git a/src/skins/vector/css/matrix-react-sdk/views/rooms/_AppsDrawer.scss b/src/skins/vector/css/matrix-react-sdk/views/rooms/_AppsDrawer.scss
index 840fd963..cc473048 100644
--- a/src/skins/vector/css/matrix-react-sdk/views/rooms/_AppsDrawer.scss
+++ b/src/skins/vector/css/matrix-react-sdk/views/rooms/_AppsDrawer.scss
@@ -96,18 +96,48 @@ limitations under the License.
 }
 
 .mx_AppIconTile {
+    background-color: #666;
+    border: 1px solid #666;
+    width: 200px;
     box-shadow: 0 4px 8px 0 rgba(0,0,0,0.2);
     transition: 0.3s;
+    border-radius: 3px;
+    margin: 5px;
+    display: inline-block;
 }
 
 .mx_AppIconTile:hover {
-    box-shadow: 0 8px 16px 0 rgba(0,0,0,0.2);
+    border: 1px solid $accent-color;
+    box-shadow: 0 0 10px 5px rgba(200,200,200,0.5);
 }
 
 .mx_AppIconTile_content {
     padding: 2px 16px;
+    height: 60px;
+    overflow: hidden;
+}
+
+.mx_AppIconTile_content h4 {
+    margin-top: 5px;
+    margin-bottom: 2px;
+}
+
+.mx_AppIconTile_content p {
+    margin-top: 0;
+    margin-bottom: 5px;
+    font-size: smaller;
 }
 
 .mx_AppIconTile_image {
-    width: 100%;
+    width: 75%;
+    margin: 10px;
+    max-height: 100px;
+}
+
+.mx_AppIconTile_imageContainer {
+    text-align: center;
+    width: 100%;
+    background-color: white;
+    border-radius: 3px 3px 0 0;
+    max-height: 155px;
 }

From 272d36995bb3886bbc6ddcb932d184bd2fdc0485 Mon Sep 17 00:00:00 2001
From: Richard Lewis <rick@matrix.org>
Date: Wed, 7 Jun 2017 15:28:58 +0100
Subject: [PATCH 11/30] Fix active styling

---
 .../views/rooms/_AppsDrawer.scss                 | 16 ++++++++++++----
 1 file changed, 12 insertions(+), 4 deletions(-)

diff --git a/src/skins/vector/css/matrix-react-sdk/views/rooms/_AppsDrawer.scss b/src/skins/vector/css/matrix-react-sdk/views/rooms/_AppsDrawer.scss
index cc473048..798c9af1 100644
--- a/src/skins/vector/css/matrix-react-sdk/views/rooms/_AppsDrawer.scss
+++ b/src/skins/vector/css/matrix-react-sdk/views/rooms/_AppsDrawer.scss
@@ -96,8 +96,8 @@ limitations under the License.
 }
 
 .mx_AppIconTile {
-    background-color: #666;
-    border: 1px solid #666;
+    background-color: $lightbox-bg-color;
+    border: 1px solid rgba(0, 0, 0, 0);
     width: 200px;
     box-shadow: 0 4px 8px 0 rgba(0,0,0,0.2);
     transition: 0.3s;
@@ -106,6 +106,11 @@ limitations under the License.
     display: inline-block;
 }
 
+.mx_AppIconTile.mx_AppIconTile_active {
+    color: $accent-color;
+    border-color: $accent-color;
+}
+
 .mx_AppIconTile:hover {
     border: 1px solid $accent-color;
     box-shadow: 0 0 10px 5px rgba(200,200,200,0.5);
@@ -129,9 +134,12 @@ limitations under the License.
 }
 
 .mx_AppIconTile_image {
+    padding: 10px;
     width: 75%;
-    margin: 10px;
-    max-height: 100px;
+    max-width:100px;
+    max-height:100px;
+    width: auto;
+    height: auto;
 }
 
 .mx_AppIconTile_imageContainer {

From 3588ce7bc518177c0759893a229170ae68df8c14 Mon Sep 17 00:00:00 2001
From: Richard Lewis <rick@matrix.org>
Date: Mon, 19 Jun 2017 11:35:20 +0100
Subject: [PATCH 12/30] Fix app tile styling

---
 .../vector/css/matrix-react-sdk/views/rooms/_AppsDrawer.scss | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/src/skins/vector/css/matrix-react-sdk/views/rooms/_AppsDrawer.scss b/src/skins/vector/css/matrix-react-sdk/views/rooms/_AppsDrawer.scss
index 798c9af1..dceff2a3 100644
--- a/src/skins/vector/css/matrix-react-sdk/views/rooms/_AppsDrawer.scss
+++ b/src/skins/vector/css/matrix-react-sdk/views/rooms/_AppsDrawer.scss
@@ -147,5 +147,8 @@ limitations under the License.
     width: 100%;
     background-color: white;
     border-radius: 3px 3px 0 0;
-    max-height: 155px;
+    height: 155px;
+    display: flex;
+    justify-content: center;
+    align-items: center;
 }

From db70272902b607b19cb5ddcf5ff468788df55983 Mon Sep 17 00:00:00 2001
From: Richard Lewis <rick@matrix.org>
Date: Mon, 19 Jun 2017 12:05:57 +0100
Subject: [PATCH 13/30] Fix custom widget form styling

---
 .../vector/css/matrix-react-sdk/views/rooms/_AppsDrawer.scss | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/src/skins/vector/css/matrix-react-sdk/views/rooms/_AppsDrawer.scss b/src/skins/vector/css/matrix-react-sdk/views/rooms/_AppsDrawer.scss
index dceff2a3..0fcabac1 100644
--- a/src/skins/vector/css/matrix-react-sdk/views/rooms/_AppsDrawer.scss
+++ b/src/skins/vector/css/matrix-react-sdk/views/rooms/_AppsDrawer.scss
@@ -152,3 +152,8 @@ limitations under the License.
     justify-content: center;
     align-items: center;
 }
+
+form.mx_Custom_Widget_Form div {
+    margin-top: 10px;
+    margin-bottom: 10px;
+}

From f36c1ca2072a61ac9dffbe64cc848ac1b558be60 Mon Sep 17 00:00:00 2001
From: Michael Telatynski <7t3chguy@googlemail.com>
Date: Tue, 27 Jun 2017 14:51:18 +0100
Subject: [PATCH 14/30] fix Quote not closing contextual menu

also use `this.closeMenu();` over the explicit direct call as the wrapper exists and is a little clearer
---
 .../views/context_menus/MessageContextMenu.js       | 13 +++++++------
 1 file changed, 7 insertions(+), 6 deletions(-)

diff --git a/src/components/views/context_menus/MessageContextMenu.js b/src/components/views/context_menus/MessageContextMenu.js
index bc82778a..1c287738 100644
--- a/src/components/views/context_menus/MessageContextMenu.js
+++ b/src/components/views/context_menus/MessageContextMenu.js
@@ -67,7 +67,7 @@ module.exports = React.createClass({
 
     onResendClick: function() {
         Resend.resend(this.props.mxEvent);
-        if (this.props.onFinished) this.props.onFinished();
+        this.closeMenu();
     },
 
     onViewSourceClick: function() {
@@ -75,7 +75,7 @@ module.exports = React.createClass({
         Modal.createDialog(ViewSource, {
             content: this.props.mxEvent.event,
         }, 'mx_Dialog_viewsource');
-        if (this.props.onFinished) this.props.onFinished();
+        this.closeMenu();
     },
 
     onViewClearSourceClick: function() {
@@ -84,7 +84,7 @@ module.exports = React.createClass({
             // FIXME: _clearEvent is private
             content: this.props.mxEvent._clearEvent,
         }, 'mx_Dialog_viewsource');
-        if (this.props.onFinished) this.props.onFinished();
+        this.closeMenu();
     },
 
     onRedactClick: function() {
@@ -106,12 +106,12 @@ module.exports = React.createClass({
                 }).done();
             },
         }, 'mx_Dialog_confirmredact');
-        if (this.props.onFinished) this.props.onFinished();
+        this.closeMenu();
     },
 
     onCancelSendClick: function() {
         Resend.removeFromQueue(this.props.mxEvent);
-        if (this.props.onFinished) this.props.onFinished();
+        this.closeMenu();
     },
 
     onForwardClick: function() {
@@ -130,7 +130,7 @@ module.exports = React.createClass({
         if (this.props.eventTileOps) {
             this.props.eventTileOps.unhideWidget();
         }
-        if (this.props.onFinished) this.props.onFinished();
+        this.closeMenu();
     },
 
     onQuoteClick: function() {
@@ -139,6 +139,7 @@ module.exports = React.createClass({
             action: 'quote',
             event: this.props.mxEvent,
         });
+        this.closeMenu();
     },
 
     render: function() {

From 2ea69624af7d3e226996c77e065a50ad5df81b4d Mon Sep 17 00:00:00 2001
From: Luke Barnard <lukeb@openmarket.com>
Date: Wed, 28 Jun 2017 16:36:32 +0100
Subject: [PATCH 15/30] Add some style improvements to autocompletions

 - Fix https://github.com/vector-im/riot-web/issues/2230 by adding text-overflow: ellipsis to pill spans
 - Add padding to pills
 - Make sure to only apply horizontal margin of pill children at one level of the DOM tree
---
 .../views/rooms/_Autocomplete.scss                 | 14 +++++++++++++-
 1 file changed, 13 insertions(+), 1 deletion(-)

diff --git a/src/skins/vector/css/matrix-react-sdk/views/rooms/_Autocomplete.scss b/src/skins/vector/css/matrix-react-sdk/views/rooms/_Autocomplete.scss
index 062dd0ba..704b3aa9 100644
--- a/src/skins/vector/css/matrix-react-sdk/views/rooms/_Autocomplete.scss
+++ b/src/skins/vector/css/matrix-react-sdk/views/rooms/_Autocomplete.scss
@@ -38,6 +38,7 @@
 .mx_Autocomplete_Completion_pill {
     border-radius: 17px;
     height: 34px;
+    padding: 0px 5px;
     display: flex;
     user-select: none;
     cursor: pointer;
@@ -45,10 +46,21 @@
     color: $primary-fg-color;
 }
 
-.mx_Autocomplete_Completion_pill * {
+.mx_Autocomplete_Completion_pill > * {
     margin: 0 3px;
 }
 
+.mx_Autocomplete_provider_name,
+.mx_Autocomplete_Completion_title,
+.mx_Autocomplete_Completion_subtitle,
+.mx_Autocomplete_Completion_description {
+    /* Ellipsis for long names/subtitles/descriptions*/
+    max-width: 150px;
+    white-space: nowrap;
+    overflow: hidden;
+    text-overflow: ellipsis;
+}
+
 /* container for pill-style completions */
 .mx_Autocomplete_Completion_container_pill {
     margin: 12px;

From 042e7acca51d71cb6fb872899d0c9d0b85141ad2 Mon Sep 17 00:00:00 2001
From: Luke Barnard <lukeb@openmarket.com>
Date: Fri, 30 Jun 2017 16:03:56 +0100
Subject: [PATCH 16/30] Fix long words causing MessageComposer to widen

Use `word-break: break-word` to split long words.

Fixes https://github.com/vector-im/riot-web/issues/4414
---
 .../css/matrix-react-sdk/views/rooms/_MessageComposer.scss       | 1 +
 1 file changed, 1 insertion(+)

diff --git a/src/skins/vector/css/matrix-react-sdk/views/rooms/_MessageComposer.scss b/src/skins/vector/css/matrix-react-sdk/views/rooms/_MessageComposer.scss
index 85c0e2c7..db2f6c08 100644
--- a/src/skins/vector/css/matrix-react-sdk/views/rooms/_MessageComposer.scss
+++ b/src/skins/vector/css/matrix-react-sdk/views/rooms/_MessageComposer.scss
@@ -87,6 +87,7 @@ limitations under the License.
     flex: 1;
     max-height: 120px;
     overflow: auto;
+    word-break: break-word;
 }
 
 .mx_MessageComposer_input blockquote {

From 6fcf504a4b4c0374613224c328830137610e7a83 Mon Sep 17 00:00:00 2001
From: Luke Barnard <lukeb@openmarket.com>
Date: Fri, 30 Jun 2017 17:41:55 +0100
Subject: [PATCH 17/30] Apply white-space: pre-wrap to mx_MEmoteBody

as we do with MTextBody.

So that people can do multi-line emotes...
---
 .../css/matrix-react-sdk/views/messages/_MEmoteBody.scss      | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/src/skins/vector/css/matrix-react-sdk/views/messages/_MEmoteBody.scss b/src/skins/vector/css/matrix-react-sdk/views/messages/_MEmoteBody.scss
index e614aca7..cf722e5a 100644
--- a/src/skins/vector/css/matrix-react-sdk/views/messages/_MEmoteBody.scss
+++ b/src/skins/vector/css/matrix-react-sdk/views/messages/_MEmoteBody.scss
@@ -14,6 +14,10 @@ See the License for the specific language governing permissions and
 limitations under the License.
 */
 
+.mx_MEmoteBody {
+    white-space: pre-wrap;
+}
+
 .mx_MEmoteBody_sender {
     cursor: pointer;
 }

From 39ff11fe8242a56ee97eeeb4a4523d46d98f6370 Mon Sep 17 00:00:00 2001
From: Michael Telatynski <7t3chguy@googlemail.com>
Date: Mon, 3 Jul 2017 18:13:56 +0100
Subject: [PATCH 18/30] preinstall phantomjs to try get rid of boom error

---
 .travis.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.travis.yml b/.travis.yml
index e020ba7d..0656064e 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -19,4 +19,4 @@ node_js:
 install:
     # clone the deps with depth 1: we know we will only ever need that one
     # commit.
-    - scripts/fetch-develop.deps.sh --depth 1 && npm install
+    - scripts/fetch-develop.deps.sh --depth 1 && npm i phantomjs-prebuilt && npm install

From bd1196716a9f5d34b837f3e9b5778e2856587f5e Mon Sep 17 00:00:00 2001
From: Richard van der Hoff <richard@matrix.org>
Date: Tue, 4 Jul 2017 15:25:09 +0100
Subject: [PATCH 19/30] Use external mock-request

mock-request is now factored out to matrix-mock-request; use it
---
 package.json              |   1 +
 test/app-tests/joining.js |   2 +-
 test/app-tests/loading.js |   2 +-
 test/mock-request.js      | 336 --------------------------------------
 4 files changed, 3 insertions(+), 338 deletions(-)
 delete mode 100644 test/mock-request.js

diff --git a/package.json b/package.json
index f5129006..87b004ce 100644
--- a/package.json
+++ b/package.json
@@ -121,6 +121,7 @@
     "karma-mocha": "^0.2.2",
     "karma-phantomjs-launcher": "^1.0.0",
     "karma-webpack": "^1.7.0",
+    "matrix-mock-request": "^0.1.3",
     "minimist": "^1.2.0",
     "mkdirp": "^0.5.1",
     "mocha": "^2.4.5",
diff --git a/test/app-tests/joining.js b/test/app-tests/joining.js
index d9670125..11fd3d48 100644
--- a/test/app-tests/joining.js
+++ b/test/app-tests/joining.js
@@ -36,7 +36,7 @@ var expect = require('expect');
 var q = require('q');
 
 var test_utils = require('../test-utils');
-var MockHttpBackend = require('../mock-request');
+var MockHttpBackend = require('matrix-mock-request');
 
 var HS_URL='http://localhost';
 var IS_URL='http://localhost';
diff --git a/test/app-tests/loading.js b/test/app-tests/loading.js
index 54e98760..d01836a3 100644
--- a/test/app-tests/loading.js
+++ b/test/app-tests/loading.js
@@ -33,7 +33,7 @@ import {VIEWS} from 'matrix-react-sdk/lib/components/structures/MatrixChat';
 import dis from 'matrix-react-sdk/lib/dispatcher';
 
 import * as test_utils from '../test-utils';
-import MockHttpBackend from '../mock-request';
+import MockHttpBackend from 'matrix-mock-request';
 import {parseQs, parseQsFromFragment} from '../../src/vector/url_utils';
 
 var DEFAULT_HS_URL='http://my_server';
diff --git a/test/mock-request.js b/test/mock-request.js
deleted file mode 100644
index 64ac6c06..00000000
--- a/test/mock-request.js
+++ /dev/null
@@ -1,336 +0,0 @@
-"use strict";
-const q = require("q");
-import expect from 'expect';
-
-/**
- * Construct a mock HTTP backend, heavily inspired by Angular.js.
- * @constructor
- */
-function HttpBackend() {
-    this.requests = [];
-    this.expectedRequests = [];
-    const self = this;
-    // the request function dependency that the SDK needs.
-    this.requestFn = function(opts, callback) {
-        const req = new Request(opts, callback);
-        console.log(`${Date.now()} HTTP backend received request: ${req}`);
-        self.requests.push(req);
-
-        const abort = function() {
-            const idx = self.requests.indexOf(req);
-            if (idx >= 0) {
-                console.log("Aborting HTTP request: %s %s", opts.method,
-                            opts.uri);
-                self.requests.splice(idx, 1);
-                req.callback("aborted");
-            }
-        };
-
-        return {
-            abort: abort,
-        };
-    };
-
-    // very simplistic mapping from the whatwg fetch interface onto the request
-    // interface, so we can use the same mock backend for both.
-    this.fetchFn = function(input, init) {
-        init = init || {};
-        const requestOpts = {
-            uri: input,
-            method: init.method || 'GET',
-            body: init.body,
-        };
-
-        return new Promise((resolve, reject) => {
-            function callback(err, response, body) {
-                if (err) {
-                    reject(err);
-                }
-                resolve({
-                    ok: response.statusCode >= 200 && response.statusCode < 300,
-                    json: () => body,
-                });
-            };
-
-            const req = new Request(requestOpts, callback);
-            console.log(`HTTP backend received request: ${req}`);
-            self.requests.push(req);
-        });
-    };
-}
-HttpBackend.prototype = {
-    /**
-     * Respond to all of the requests (flush the queue).
-     * @param {string} path The path to flush (optional) default: all.
-     * @param {integer} numToFlush The number of things to flush (optional), default: all.
-     * @param {integer=} waitTime  The time (in ms) to wait for a request to happen.
-     *    default: 100
-     *
-     * @return {Promise} resolves when there is nothing left to flush, with the
-     *    number of requests flushed
-     */
-    flush: function(path, numToFlush, waitTime) {
-        const defer = q.defer();
-        const self = this;
-        let flushed = 0;
-        if (waitTime === undefined) {
-            waitTime = 100;
-        }
-
-        function log(msg) {
-            console.log(`${Date.now()} flush[${path || ''}]: ${msg}`);
-        }
-
-        log("HTTP backend flushing... (path=" + path
-            + " numToFlush=" + numToFlush
-            + " waitTime=" + waitTime
-            + ")",
-        );
-        const endTime =  waitTime + Date.now();
-
-        const tryFlush = function() {
-            // if there's more real requests and more expected requests, flush 'em.
-            log(`  trying to flush => reqs=[${self.requests}] ` +
-                `expected=[${self.expectedRequests}]`,
-            );
-            if (self._takeFromQueue(path)) {
-                // try again on the next tick.
-                flushed += 1;
-                if (numToFlush && flushed === numToFlush) {
-                    log(`Flushed assigned amount: ${numToFlush}`);
-                    defer.resolve(flushed);
-                } else {
-                    log(`  flushed. Trying for more.`);
-                    setTimeout(tryFlush, 0);
-                }
-            } else if (flushed === 0 && Date.now() < endTime) {
-                // we may not have made the request yet, wait a generous amount of
-                // time before giving up.
-                log(`  nothing to flush yet; waiting for requests.`);
-                setTimeout(tryFlush, 5);
-            } else {
-                if (flushed === 0) {
-                    log("nothing to flush; giving up");
-                } else {
-                    log(`no more flushes after flushing ${flushed} requests`);
-                }
-                defer.resolve(flushed);
-            }
-        };
-
-        setTimeout(tryFlush, 0);
-
-        return defer.promise;
-    },
-
-    /**
-     * Attempts to resolve requests/expected requests.
-     * @param {string} path The path to flush (optional) default: all.
-     * @return {boolean} true if something was resolved.
-     */
-    _takeFromQueue: function(path) {
-        let req = null;
-        let i;
-        let j;
-        let matchingReq = null;
-        let expectedReq = null;
-        let testResponse = null;
-        for (i = 0; i < this.requests.length; i++) {
-            req = this.requests[i];
-            for (j = 0; j < this.expectedRequests.length; j++) {
-                expectedReq = this.expectedRequests[j];
-                if (path && path !== expectedReq.path) {
-                    continue;
-                }
-                if (expectedReq.method === req.method &&
-                        req.path.indexOf(expectedReq.path) !== -1) {
-                    if (!expectedReq.data || (JSON.stringify(expectedReq.data) ===
-                            JSON.stringify(req.data))) {
-                        matchingReq = expectedReq;
-                        this.expectedRequests.splice(j, 1);
-                        break;
-                    }
-                }
-            }
-
-            if (matchingReq) {
-                // remove from request queue
-                this.requests.splice(i, 1);
-                i--;
-
-                for (j = 0; j < matchingReq.checks.length; j++) {
-                    matchingReq.checks[j](req);
-                }
-                testResponse = matchingReq.response;
-                console.log(`${Date.now()}    responding to ${matchingReq.path}`);
-                let body = testResponse.body;
-                if (Object.prototype.toString.call(body) == "[object Function]") {
-                    body = body(req.path, req.data);
-                }
-                req.callback(
-                    testResponse.err, testResponse.response, body,
-                );
-                matchingReq = null;
-            }
-        }
-        if (testResponse) {  // flushed something
-            return true;
-        }
-        return false;
-    },
-
-    /**
-     * Makes sure that the SDK hasn't sent any more requests to the backend.
-     */
-    verifyNoOutstandingRequests: function() {
-        const firstOutstandingReq = this.requests[0] || {};
-        expect(this.requests.length).toEqual(0,
-            "Expected no more HTTP requests but received request to " +
-            firstOutstandingReq.path,
-        );
-    },
-
-    /**
-     * Makes sure that the test doesn't have any unresolved requests.
-     */
-    verifyNoOutstandingExpectation: function() {
-        const firstOutstandingExpectation = this.expectedRequests[0] || {};
-        expect(this.expectedRequests.length).toEqual(0,
-            "Expected to see HTTP request for " + firstOutstandingExpectation.path,
-        );
-    },
-
-    /**
-     * Create an expected request.
-     * @param {string} method The HTTP method
-     * @param {string} path The path (which can be partial)
-     * @param {Object} data The expected data.
-     * @return {Request} An expected request.
-     */
-    when: function(method, path, data) {
-        const pendingReq = new ExpectedRequest(method, path, data);
-        this.expectedRequests.push(pendingReq);
-        return pendingReq;
-    },
-};
-
-/**
- * Represents the expectation of a request.
- *
- * <p>Includes the conditions to be matched against, the checks to be made,
- * and the response to be returned.
- *
- * @constructor
- * @param {string} method
- * @param {string} path
- * @param {object?} data
- */
-function ExpectedRequest(method, path, data) {
-    this.method = method;
-    this.path = path;
-    this.data = data;
-    this.response = null;
-    this.checks = [];
-}
-
-ExpectedRequest.prototype = {
-    toString: function() {
-        return this.method + " " + this.path
-    },
-
-    /**
-     * Execute a check when this request has been satisfied.
-     * @param {Function} fn The function to execute.
-     * @return {Request} for chaining calls.
-     */
-    check: function(fn) {
-        this.checks.push(fn);
-        return this;
-    },
-
-    /**
-     * Respond with the given data when this request is satisfied.
-     * @param {Number} code The HTTP status code.
-     * @param {Object|Function} data The HTTP JSON body. If this is a function,
-     * it will be invoked when the JSON body is required (which should be returned).
-     */
-    respond: function(code, data) {
-        this.response = {
-            response: {
-                statusCode: code,
-                headers: {},
-            },
-            body: data,
-            err: null,
-        };
-    },
-
-    /**
-     * Fail with an Error when this request is satisfied.
-     * @param {Number} code The HTTP status code.
-     * @param {Error} err The error to throw (e.g. Network Error)
-     */
-    fail: function(code, err) {
-        this.response = {
-            response: {
-                statusCode: code,
-                headers: {},
-            },
-            body: null,
-            err: err,
-        };
-    },
-};
-
-/**
- * Represents a request made by the app.
- *
- * @constructor
- * @param {object} opts opts passed to request()
- * @param {function} callback
- */
-function Request(opts, callback) {
-    this.opts = opts;
-    this.callback = callback;
-
-    Object.defineProperty(this, 'method', {
-        get: function() {
-            return opts.method;
-        },
-    });
-
-    Object.defineProperty(this, 'path', {
-        get: function() {
-            return opts.uri;
-        },
-    });
-
-    Object.defineProperty(this, 'data', {
-        get: function() {
-            return opts.body;
-        },
-    });
-
-    Object.defineProperty(this, 'queryParams', {
-        get: function() {
-            return opts.qs;
-        },
-    });
-
-    Object.defineProperty(this, 'headers', {
-        get: function() {
-            return opts.headers || {};
-        },
-    });
-}
-
-Request.prototype = {
-    toString: function() {
-        return this.method + " " + this.path;
-    },
-};
-
-/**
- * The HttpBackend class.
- */
-module.exports = HttpBackend;

From 48889b51b005319c83d0d3295172df4abc3ae91d Mon Sep 17 00:00:00 2001
From: Richard van der Hoff <richard@matrix.org>
Date: Tue, 4 Jul 2017 16:38:19 +0100
Subject: [PATCH 20/30] Bump to matrix-mock-request 1.0.0

- for consistency with js-sdk updates
---
 package.json | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/package.json b/package.json
index 87b004ce..3d7e6935 100644
--- a/package.json
+++ b/package.json
@@ -121,7 +121,7 @@
     "karma-mocha": "^0.2.2",
     "karma-phantomjs-launcher": "^1.0.0",
     "karma-webpack": "^1.7.0",
-    "matrix-mock-request": "^0.1.3",
+    "matrix-mock-request": "^1.0.0",
     "minimist": "^1.2.0",
     "mkdirp": "^0.5.1",
     "mocha": "^2.4.5",

From 1e569e67b17b4a6519d29776b39c3944a8ba8cab Mon Sep 17 00:00:00 2001
From: Luke Barnard <lukeb@openmarket.com>
Date: Tue, 4 Jul 2017 16:40:15 +0100
Subject: [PATCH 21/30] Do not truncate autocomplete provider names

---
 .../vector/css/matrix-react-sdk/views/rooms/_Autocomplete.scss   | 1 -
 1 file changed, 1 deletion(-)

diff --git a/src/skins/vector/css/matrix-react-sdk/views/rooms/_Autocomplete.scss b/src/skins/vector/css/matrix-react-sdk/views/rooms/_Autocomplete.scss
index 704b3aa9..9beb0a2c 100644
--- a/src/skins/vector/css/matrix-react-sdk/views/rooms/_Autocomplete.scss
+++ b/src/skins/vector/css/matrix-react-sdk/views/rooms/_Autocomplete.scss
@@ -50,7 +50,6 @@
     margin: 0 3px;
 }
 
-.mx_Autocomplete_provider_name,
 .mx_Autocomplete_Completion_title,
 .mx_Autocomplete_Completion_subtitle,
 .mx_Autocomplete_Completion_description {

From b4b33ae06f8051fbef6317937c91bb5a4561f107 Mon Sep 17 00:00:00 2001
From: Luke Barnard <lukeb@openmarket.com>
Date: Tue, 4 Jul 2017 16:49:11 +0100
Subject: [PATCH 22/30] Add CSS class to truncate certain autocompletions

---
 .../views/rooms/_Autocomplete.scss             | 18 ++++++++++--------
 1 file changed, 10 insertions(+), 8 deletions(-)

diff --git a/src/skins/vector/css/matrix-react-sdk/views/rooms/_Autocomplete.scss b/src/skins/vector/css/matrix-react-sdk/views/rooms/_Autocomplete.scss
index 9beb0a2c..6bf3f3b6 100644
--- a/src/skins/vector/css/matrix-react-sdk/views/rooms/_Autocomplete.scss
+++ b/src/skins/vector/css/matrix-react-sdk/views/rooms/_Autocomplete.scss
@@ -50,14 +50,16 @@
     margin: 0 3px;
 }
 
-.mx_Autocomplete_Completion_title,
-.mx_Autocomplete_Completion_subtitle,
-.mx_Autocomplete_Completion_description {
-    /* Ellipsis for long names/subtitles/descriptions*/
-    max-width: 150px;
-    white-space: nowrap;
-    overflow: hidden;
-    text-overflow: ellipsis;
+.mx_Autocomplete_Completion_container_truncate {
+    .mx_Autocomplete_Completion_title,
+    .mx_Autocomplete_Completion_subtitle,
+    .mx_Autocomplete_Completion_description {
+        /* Ellipsis for long names/subtitles/descriptions*/
+        max-width: 150px;
+        white-space: nowrap;
+        overflow: hidden;
+        text-overflow: ellipsis;
+    }
 }
 
 /* container for pill-style completions */

From 38b1ca9b901f2a9a1069d40e3d24561570428177 Mon Sep 17 00:00:00 2001
From: Richard van der Hoff <richard@matrix.org>
Date: Wed, 5 Jul 2017 09:35:51 +0100
Subject: [PATCH 23/30] Use headless chrome instead of phantomjs for tests

---
 karma.conf.js | 15 +++++++++++++++
 package.json  |  2 +-
 2 files changed, 16 insertions(+), 1 deletion(-)

diff --git a/karma.conf.js b/karma.conf.js
index 1e043663..d834987e 100644
--- a/karma.conf.js
+++ b/karma.conf.js
@@ -113,8 +113,23 @@ module.exports = function (config) {
         browsers: [
             'Chrome',
             //'PhantomJS',
+            //'ChromeHeadless'
         ],
 
+        customLaunchers: {
+            'ChromeHeadless': {
+                base: 'Chrome',
+                flags: [
+                    // '--no-sandbox',
+                    // See https://chromium.googlesource.com/chromium/src/+/lkgr/headless/README.md
+                    '--headless',
+                    '--disable-gpu',
+                    // Without a remote debugging port, Google Chrome exits immediately.
+                    '--remote-debugging-port=9222',
+                ],
+            }
+        },
+
         // Continuous Integration mode
         // if true, Karma captures browsers, runs the tests and exits
         // singleRun: false,
diff --git a/package.json b/package.json
index 3d7e6935..dc1058c9 100644
--- a/package.json
+++ b/package.json
@@ -48,7 +48,7 @@
     "lintall": "eslint src/ test/",
     "clean": "rimraf lib webapp electron_app/dist",
     "prepublish": "npm run build:compile",
-    "test": "karma start --single-run=true --autoWatch=false --browsers PhantomJS --colors=false",
+    "test": "karma start --single-run=true --autoWatch=false --browsers ChromeHeadless --colors=false",
     "test-multi": "karma start"
   },
   "dependencies": {

From 9969d6095d46488c0c56663da68b6d9e0ccaac68 Mon Sep 17 00:00:00 2001
From: Richard van der Hoff <richard@matrix.org>
Date: Wed, 5 Jul 2017 09:43:40 +0100
Subject: [PATCH 24/30] Remove phantomjs; add chrome

---
 .travis.yml  | 4 +++-
 package.json | 2 --
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/.travis.yml b/.travis.yml
index 0656064e..55744222 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -16,7 +16,9 @@ node_js:
     - 6.3
     - 6
     - 7
+addons:
+    chrome: stable
 install:
     # clone the deps with depth 1: we know we will only ever need that one
     # commit.
-    - scripts/fetch-develop.deps.sh --depth 1 && npm i phantomjs-prebuilt && npm install
+    - scripts/fetch-develop.deps.sh --depth 1 && npm install
diff --git a/package.json b/package.json
index dc1058c9..83d9bd16 100644
--- a/package.json
+++ b/package.json
@@ -119,14 +119,12 @@
     "karma-cli": "^0.1.2",
     "karma-junit-reporter": "^0.4.1",
     "karma-mocha": "^0.2.2",
-    "karma-phantomjs-launcher": "^1.0.0",
     "karma-webpack": "^1.7.0",
     "matrix-mock-request": "^1.0.0",
     "minimist": "^1.2.0",
     "mkdirp": "^0.5.1",
     "mocha": "^2.4.5",
     "parallelshell": "^1.2.0",
-    "phantomjs-prebuilt": "^2.1.7",
     "postcss-extend": "^1.0.5",
     "postcss-import": "^9.0.0",
     "postcss-loader": "^1.2.2",

From cfe9e762ad36556561c43a471b76de748a2ceb8d Mon Sep 17 00:00:00 2001
From: Richard van der Hoff <richard@matrix.org>
Date: Wed, 5 Jul 2017 10:01:13 +0100
Subject: [PATCH 25/30] Use trusty dist

---
 .travis.yml | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/.travis.yml b/.travis.yml
index 55744222..409fcd75 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,3 +1,10 @@
+# we need trusty for the chrome addon
+dist: trusty
+
+# we don't need sudo, so can run in a container, which makes startup much
+# quicker.
+sudo: false
+
 language: node_js
 node_js:
     # make sure we work with a range of node versions.

From 60703bdcfcc3b7d6a996936ac00003383825646e Mon Sep 17 00:00:00 2001
From: Richard van der Hoff <richard@matrix.org>
Date: Wed, 5 Jul 2017 10:54:10 +0100
Subject: [PATCH 26/30] Log errors thrown by getThirdpartyProtocols

- so that it appears in rageshakes and test logs.
---
 src/components/structures/RoomDirectory.js | 1 +
 1 file changed, 1 insertion(+)

diff --git a/src/components/structures/RoomDirectory.js b/src/components/structures/RoomDirectory.js
index 64a53d33..e7d68c39 100644
--- a/src/components/structures/RoomDirectory.js
+++ b/src/components/structures/RoomDirectory.js
@@ -73,6 +73,7 @@ module.exports = React.createClass({
             this.protocols = response;
             this.setState({protocolsLoading: false});
         }, (err) => {
+            console.warn(`error loading thirdparty protocols: ${err}`);
             this.setState({protocolsLoading: false});
             if (MatrixClientPeg.get().isGuest()) {
                 // Guests currently aren't allowed to use this API, so

From ba0073ca71af36e0f6ed671e35c4235b03286f88 Mon Sep 17 00:00:00 2001
From: Richard van der Hoff <richard@matrix.org>
Date: Wed, 5 Jul 2017 11:45:23 +0100
Subject: [PATCH 27/30] README: Add notes on running tests

---
 README.md | 35 +++++++++++++++++++++++++----------
 1 file changed, 25 insertions(+), 10 deletions(-)

diff --git a/README.md b/README.md
index 89f2148f..3334ad4e 100644
--- a/README.md
+++ b/README.md
@@ -282,19 +282,34 @@ If any of these steps error with, `file table overflow`, you are probably on a m
 which has a very low limit on max open files. Run `ulimit -Sn 1024` and try again.
 You'll need to do this in each new terminal you open before building Riot.
 
-How to add a new translation?
-=============================
+Running the tests
+-----------------
+
+There are a number of application-level tests in the `tests` directory; these
+are designed to run in a browser instance under the control of
+[karma](https://karma-runner.github.io). To run them:
+
+* Make sure you have Chrome installed (a recent version, like 59)
+* Make sure you have `matrix-js-sdk` and `matrix-react-sdk` installed and
+  built, as above
+* `npm run test`
+
+The above will run the tests under Chrome in a `headless` mode.
+
+You can also tell karma to run the tests in a loop (every time the source
+changes), in an instance of Chrome on your desktop, with `npm run
+test-multi`. This also gives you the option of running the tests in 'debug'
+mode, which is useful for stepping through the tests in the developer tools.
+
+Translations
+============
+
+To add a new translation, head to the [translating doc](docs/translating.md).
+
+For a developer guide, see the [translating dev doc](docs/translating-dev.md).
 
 [<img src="https://translate.riot.im/widgets/riot-web/-/multi-auto.svg" alt="translationsstatus" width="340">](https://translate.riot.im/engage/riot-web/?utm_source=widget)
 
-
-Head to the [translating doc](docs/translating.md)
-
-Adding Strings to the translations (Developer Guide)
-====================================================
-
-Head to the [translating dev doc](docs/translating-dev.md)
-
 Triaging issues
 ===============
 

From a14bc9a9b1642b674b22afff10bc570d740c7b0d Mon Sep 17 00:00:00 2001
From: Richard van der Hoff <richard@matrix.org>
Date: Wed, 5 Jul 2017 11:47:38 +0100
Subject: [PATCH 28/30] README: minor fixes

---
 README.md | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/README.md b/README.md
index 3334ad4e..d4b778b9 100644
--- a/README.md
+++ b/README.md
@@ -81,7 +81,7 @@ to build.
    npm run build
    ```
    However, we recommend setting up a proper development environment (see "Setting
-   up a development environment" below) if you want to run your own copy of the
+   up a dev environment" below) if you want to run your own copy of the
    `develop` branch, as it makes it much easier to keep these dependencies
    up-to-date.  Or just use https://riot.im/develop - the continuous integration
    release of the develop branch.
@@ -253,7 +253,6 @@ Finally, build and start Riot itself:
 1. `rm -r node_modules/matrix-react-sdk; ln -s ../../matrix-react-sdk node_modules/`
 1. `npm start`
 1. Wait a few seconds for the initial build to finish; you should see something like:
-
     ```
     Hash: b0af76309dd56d7275c8
     Version: webpack 1.12.14

From 07e8d4ef69a24704caaa0ef2e17d77993ec64dfe Mon Sep 17 00:00:00 2001
From: Luke Barnard <lukeb@openmarket.com>
Date: Wed, 5 Jul 2017 18:13:23 +0100
Subject: [PATCH 29/30] Add visual bell animation for RTE

---
 .../matrix-react-sdk/views/rooms/_MessageComposer.scss | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/src/skins/vector/css/matrix-react-sdk/views/rooms/_MessageComposer.scss b/src/skins/vector/css/matrix-react-sdk/views/rooms/_MessageComposer.scss
index 49111afd..89ae2d67 100644
--- a/src/skins/vector/css/matrix-react-sdk/views/rooms/_MessageComposer.scss
+++ b/src/skins/vector/css/matrix-react-sdk/views/rooms/_MessageComposer.scss
@@ -78,6 +78,16 @@ limitations under the License.
     margin-right: 6px;
 }
 
+@keyframes visualbell
+{
+    from { background-color: #faa }
+    to { background-color: $primary-bg-color }
+}
+
+.mx_MessageComposer_input_error {
+    animation: 0.2s visualbell;
+}
+
 .mx_MessageComposer_input_empty .public-DraftEditorPlaceholder-root {
     display: none;
 }

From a8d5f7d5ebd7eae3cff68688ea68a35f522fdaac Mon Sep 17 00:00:00 2001
From: Michael Telatynski <7t3chguy@gmail.com>
Date: Thu, 6 Jul 2017 13:53:17 +0100
Subject: [PATCH 30/30] remove unused class

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
---
 .../css/matrix-react-sdk/views/rooms/_MessageComposer.scss   | 5 -----
 1 file changed, 5 deletions(-)

diff --git a/src/skins/vector/css/matrix-react-sdk/views/rooms/_MessageComposer.scss b/src/skins/vector/css/matrix-react-sdk/views/rooms/_MessageComposer.scss
index a4b26cc4..6c2216dd 100644
--- a/src/skins/vector/css/matrix-react-sdk/views/rooms/_MessageComposer.scss
+++ b/src/skins/vector/css/matrix-react-sdk/views/rooms/_MessageComposer.scss
@@ -194,11 +194,6 @@ limitations under the License.
     cursor: pointer;
 }
 
-.mx_MessageComposer_format_button_disabled {
-    cursor: not-allowed;
-    opacity: 0.5;
-}
-
 .mx_MessageComposer_formatbar_cancel {
     margin-right: 22px;
 }