diff --git a/.gitignore b/.gitignore
index c5826b78d83481138ca667d2df9bab86ccc51731..cba50a69e7b571542e46221b7c4c3fc3eaa28c92 100644
--- a/.gitignore
+++ b/.gitignore
@@ -5,6 +5,7 @@
 /key.pem
 /lib
 /node_modules
+/electron/node_modules
 /packages/
 /webapp
 /.npmrc
diff --git a/CHANGELOG.md b/CHANGELOG.md
index a2ed3149787153e456692c50f79e962e77afdb48..ea63ee66a029e3558b0264816b04928a9d1354c6 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,24 @@
+Changes in [0.9.9](https://github.com/vector-im/riot-web/releases/tag/v0.9.9) (2017-04-25)
+==========================================================================================
+[Full Changelog](https://github.com/vector-im/riot-web/compare/v0.9.9-rc.2...v0.9.9)
+
+ * No changes
+
+
+Changes in [0.9.9-rc.2](https://github.com/vector-im/riot-web/releases/tag/v0.9.9-rc.2) (2017-04-24)
+====================================================================================================
+[Full Changelog](https://github.com/vector-im/riot-web/compare/v0.9.9-rc.1...v0.9.9-rc.2)
+
+ * Fix bug where links to Riot would fail to open.
+
+
+Changes in [0.9.9-rc.1](https://github.com/vector-im/riot-web/releases/tag/v0.9.9-rc.1) (2017-04-21)
+====================================================================================================
+[Full Changelog](https://github.com/vector-im/riot-web/compare/v0.9.8...v0.9.9-rc.1)
+
+ * Update js-sdk and matrix-react-sdk to fix registration without a captcha (https://github.com/vector-im/riot-web/issues/3621)
+
+
 Changes in [0.9.8](https://github.com/vector-im/riot-web/releases/tag/v0.9.8) (2017-04-12)
 ==========================================================================================
 [Full Changelog](https://github.com/vector-im/riot-web/compare/v0.9.8-rc.3...v0.9.8)
diff --git a/README.md b/README.md
index 2d7ab81b8ad382c1d576e0e4fe3f626c80e737dc..55463a376e35fcd94520cda4c3624538ec6181ef 100644
--- a/README.md
+++ b/README.md
@@ -135,7 +135,7 @@ To run as a desktop app:
 
    ```
    npm install electron
-   node_modules/.bin/electron .
+   npm run electron
    ```
 
 To build packages, use electron-builder. This is configured to output:
diff --git a/config.sample.json b/config.sample.json
index a65646ac77473100256e9364a7431e0a9dca7c7f..3c513f7ab2cf14ea165a76a76b9980fe2898cf2f 100644
--- a/config.sample.json
+++ b/config.sample.json
@@ -4,7 +4,7 @@
     "brand": "Riot",
     "integrations_ui_url": "https://scalar.vector.im/",
     "integrations_rest_url": "https://scalar.vector.im/api",
-    "bug_report_endpoint_url": "https://vector.im/bugs",
+    "bug_report_endpoint_url": "https://riot.im/bugreports/submit",
     "enableLabs": true,
     "roomDirectory": {
         "servers": [
diff --git a/electron/img/riot.ico b/electron/img/riot.ico
deleted file mode 100644
index 8b681ffba3c1fbc6af4ad68fc41449ce017d9b53..0000000000000000000000000000000000000000
Binary files a/electron/img/riot.ico and /dev/null differ
diff --git a/electron/img/riot.png b/electron/img/riot.png
deleted file mode 100644
index fe13aa99c3aac79efe1ce0e4dd549656f7e0451f..0000000000000000000000000000000000000000
Binary files a/electron/img/riot.png and /dev/null differ
diff --git a/electron/build/icon.icns b/electron_app/build/icon.icns
similarity index 100%
rename from electron/build/icon.icns
rename to electron_app/build/icon.icns
diff --git a/electron/build/icon.ico b/electron_app/build/icon.ico
similarity index 100%
rename from electron/build/icon.ico
rename to electron_app/build/icon.ico
diff --git a/electron/build/icons/128x128.png b/electron_app/build/icons/128x128.png
similarity index 100%
rename from electron/build/icons/128x128.png
rename to electron_app/build/icons/128x128.png
diff --git a/electron/build/icons/16x16.png b/electron_app/build/icons/16x16.png
similarity index 100%
rename from electron/build/icons/16x16.png
rename to electron_app/build/icons/16x16.png
diff --git a/electron/build/icons/24x24.png b/electron_app/build/icons/24x24.png
similarity index 100%
rename from electron/build/icons/24x24.png
rename to electron_app/build/icons/24x24.png
diff --git a/electron/build/icons/256x256.png b/electron_app/build/icons/256x256.png
similarity index 100%
rename from electron/build/icons/256x256.png
rename to electron_app/build/icons/256x256.png
diff --git a/electron/build/icons/48x48.png b/electron_app/build/icons/48x48.png
similarity index 100%
rename from electron/build/icons/48x48.png
rename to electron_app/build/icons/48x48.png
diff --git a/electron/build/icons/512x512.png b/electron_app/build/icons/512x512.png
similarity index 100%
rename from electron/build/icons/512x512.png
rename to electron_app/build/icons/512x512.png
diff --git a/electron/build/icons/64x64.png b/electron_app/build/icons/64x64.png
similarity index 100%
rename from electron/build/icons/64x64.png
rename to electron_app/build/icons/64x64.png
diff --git a/electron/build/icons/96x96.png b/electron_app/build/icons/96x96.png
similarity index 100%
rename from electron/build/icons/96x96.png
rename to electron_app/build/icons/96x96.png
diff --git a/electron/build/install-spinner.gif b/electron_app/build/install-spinner.gif
similarity index 100%
rename from electron/build/install-spinner.gif
rename to electron_app/build/install-spinner.gif
diff --git a/electron_app/img/riot.ico b/electron_app/img/riot.ico
new file mode 100644
index 0000000000000000000000000000000000000000..8f8ff94eea677e966568bc2a264d1c0fe708dcf4
Binary files /dev/null and b/electron_app/img/riot.ico differ
diff --git a/electron_app/img/riot.png b/electron_app/img/riot.png
new file mode 100644
index 0000000000000000000000000000000000000000..85e9f8ca74a6cd79a8a1fed0ea06be3d11ffc46c
Binary files /dev/null and b/electron_app/img/riot.png differ
diff --git a/electron_app/package.json b/electron_app/package.json
new file mode 100644
index 0000000000000000000000000000000000000000..99651cc1c5ed654ef103994762651f1737c82dab
--- /dev/null
+++ b/electron_app/package.json
@@ -0,0 +1,11 @@
+{
+  "name": "riot-web",
+  "productName": "Riot",
+  "main": "src/electron-main.js",
+  "version": "0.9.9",
+  "description": "A feature-rich client for Matrix.org",
+  "author": "Vector Creations Ltd.",
+  "dependencies": {
+    "electron-window-state": "^4.1.0"
+  }
+}
diff --git a/electron/riot.im/README b/electron_app/riot.im/README
similarity index 100%
rename from electron/riot.im/README
rename to electron_app/riot.im/README
diff --git a/electron/riot.im/config.json b/electron_app/riot.im/config.json
similarity index 100%
rename from electron/riot.im/config.json
rename to electron_app/riot.im/config.json
diff --git a/electron/src/electron-main.js b/electron_app/src/electron-main.js
similarity index 95%
rename from electron/src/electron-main.js
rename to electron_app/src/electron-main.js
index a1fc9c1a6dfd51f35d2d979aea6df53786db8334..32e305d8d98b69666e819dd369bf1ba8476f194c 100644
--- a/electron/src/electron-main.js
+++ b/electron_app/src/electron-main.js
@@ -30,6 +30,8 @@ const tray = require('./tray');
 
 const VectorMenu = require('./vectormenu');
 
+const windowStateKeeper = require('electron-window-state');
+
 let vectorConfig = {};
 try {
     vectorConfig = require('../../webapp/config.json');
@@ -187,11 +189,21 @@ electron.app.on('ready', () => {
         process.platform == 'win32' ? 'ico' : 'png'
     );
 
+    // Load the previous window state with fallback to defaults
+    let mainWindowState = windowStateKeeper({
+        defaultWidth: 1024,
+        defaultHeight: 768,
+    });
+
     mainWindow = new electron.BrowserWindow({
         icon: icon_path,
-        width: 1024, height: 768,
         show: false,
         autoHideMenuBar: true,
+
+        x: mainWindowState.x,
+        y: mainWindowState.y,
+        width: mainWindowState.width,
+        height: mainWindowState.height,
     });
     mainWindow.loadURL(`file://${__dirname}/../../webapp/index.html`);
     electron.Menu.setApplicationMenu(VectorMenu);
@@ -230,6 +242,8 @@ electron.app.on('ready', () => {
             onLinkContextMenu(ev, params);
         }
     });
+
+    mainWindowState.manage(mainWindow);
 });
 
 electron.app.on('window-all-closed', () => {
diff --git a/electron/src/squirrelhooks.js b/electron_app/src/squirrelhooks.js
similarity index 100%
rename from electron/src/squirrelhooks.js
rename to electron_app/src/squirrelhooks.js
diff --git a/electron/src/tray.js b/electron_app/src/tray.js
similarity index 100%
rename from electron/src/tray.js
rename to electron_app/src/tray.js
diff --git a/electron/src/vectormenu.js b/electron_app/src/vectormenu.js
similarity index 100%
rename from electron/src/vectormenu.js
rename to electron_app/src/vectormenu.js
diff --git a/package.json b/package.json
index a25e17955f1834ecf3456c7d70daa45bc723aa22..884811af93c0ba5e06997b2b9832738bd33a3e6b 100644
--- a/package.json
+++ b/package.json
@@ -1,8 +1,8 @@
 {
   "name": "riot-web",
   "productName": "Riot",
-  "main": "electron/src/electron-main.js",
-  "version": "0.9.8",
+  "main": "electron_app/src/electron-main.js",
+  "version": "0.9.9",
   "description": "A feature-rich client for Matrix.org",
   "author": "Vector Creations Ltd.",
   "repository": {
@@ -33,13 +33,15 @@
     "build:bundle": "cross-env NODE_ENV=production webpack -p --progress",
     "build:bundle:dev": "webpack --optimize-occurence-order --progress",
     "build:electron": "npm run clean && npm run build && build -wml --ia32 --x64",
-    "build": "node scripts/babelcheck.js && npm run build:res && npm run build:bundle",
-    "build:dev": "node scripts/babelcheck.js && npm run build:res && npm run build:bundle:dev",
+    "build": "npm run build:res && npm run build:bundle",
+    "build:dev": "npm run build:res && npm run build:bundle:dev",
     "dist": "scripts/package.sh",
+    "install:electron": "install-app-deps",
+    "electron": "npm run install:electron && electron .",
     "start:res": "node scripts/copy-res.js -w",
     "start:js": "webpack-dev-server --output-filename=bundles/_dev_/[name].js --output-chunk-file=bundles/_dev_/[name].js -w --progress",
     "start:js:prod": "cross-env NODE_ENV=production webpack-dev-server -w --progress",
-    "start": "node scripts/babelcheck.js && parallelshell \"npm run start:res\" \"npm run start:js\"",
+    "start": "parallelshell \"npm run start:res\" \"npm run start:js\"",
     "start:prod": "parallelshell \"npm run start:res\" \"npm run start:js:prod\"",
     "lint": "eslint src/",
     "lintall": "eslint src/ test/",
@@ -145,10 +147,12 @@
     "dereference": true,
     "//files": "We bundle everything, so we only need to include webapp/",
     "files": [
-      "electron/src/**",
-      "electron/img/**",
-      "webapp/**",
-      "package.json"
+      "node_modules/**",
+      "src/**",
+      "img/**"
+    ],
+    "extraResources": [
+      "webapp/**/*"
     ],
     "linux": {
       "target": "deb",
@@ -159,10 +163,11 @@
     },
     "win": {
       "target": "squirrel"
+    },
+    "directories": {
+      "buildResources": "electron_app/build",
+      "output": "electron_app/dist",
+      "app": "electron_app"
     }
-  },
-  "directories": {
-    "buildResources": "electron/build",
-    "output": "electron/dist"
   }
 }
diff --git a/release.sh b/release.sh
index e8c68b9024b17a8008bd8f145672434fc30d9b2b..c245456051b4d620a9ae0ba8ca1655fe506c539b 100755
--- a/release.sh
+++ b/release.sh
@@ -1,12 +1,25 @@
-#!/bin/sh
+#!/bin/bash
 #
 # Script to perform a release of vector-web.
 #
-# Requires github-changelog-generator; to install, do 
+# Requires github-changelog-generator; to install, do
 #   pip install git+https://github.com/matrix-org/github-changelog-generator.git
 
 set -e
 
 cd `dirname $0`
 
+
+# bump Electron's package.json first
+release="${1#v}"
+tag="v${release}"
+echo "electron npm version"
+
+cd electron_app
+npm version --no-git-tag-version "$release"
+git commit package.json -m "$tag"
+
+
+cd ..
+
 exec ./node_modules/matrix-js-sdk/release.sh -z "$@"
diff --git a/scripts/babelcheck.js b/scripts/babelcheck.js
deleted file mode 100644
index 89f5cac554703c023b082b6a00cf018fb5914e3a..0000000000000000000000000000000000000000
--- a/scripts/babelcheck.js
+++ /dev/null
@@ -1,26 +0,0 @@
-#!/usr/bin/env node
-
-var exec = require('child_process').exec;
-
-// Makes sure the babel executable in the path is babel 6 (or greater), not
-// babel 5, which it is if you upgrade from an older version of react-sdk and
-// run 'npm install' since the package has changed to babel-cli, so 'babel'
-// remains installed and the executable in node_modules/.bin remains as babel
-// 5.
-
-// This script is duplicated from matrix-react-sdk because it can't reliably
-// be pulled in from react-sdk while npm install is failing, as it will do
-// if the environment is in the erroneous state this script checks for.
-
-exec("babel -V", function (error, stdout, stderr) {
-    if ((error && error.code) || parseInt(stdout.substr(0,1), 10) < 6) {
-        console.log("\033[31m\033[1m"+
-            '*****************************************\n'+
-            '* vector-web has moved to babel 6       *\n'+
-            '* Please "rm -rf node_modules && npm i" *\n'+
-            '* then restore links as appropriate     *\n'+
-            '*****************************************\n'+
-        "\033[91m");
-        process.exit(1);
-    }
-});
diff --git a/scripts/electron-package.sh b/scripts/electron-package.sh
index 87e353f7462d2761b4b19a202fa21a7fa6751e70..a5718df8324a1d70d9f45dc0d6807720a2a7d182 100755
--- a/scripts/electron-package.sh
+++ b/scripts/electron-package.sh
@@ -90,8 +90,8 @@ npm run build:electron
 
 popd
 
-distdir="$builddir/electron/dist"
-pubdir="$projdir/electron/pub"
+distdir="$builddir/electron_app/dist"
+pubdir="$projdir/electron_app/pub"
 rm -r "$pubdir" || true
 mkdir -p "$pubdir"
 
@@ -120,11 +120,11 @@ cp $distdir/win/*.nupkg "$pubdir/update/win32/x64/"
 cp $distdir/win/RELEASES "$pubdir/update/win32/x64/"
 
 # Move the debs to the main project dir's dist folder
-rm -r "$projdir/electron/dist" || true
-mkdir -p "$projdir/electron/dist"
-cp $distdir/*.deb "$projdir/electron/dist/"
+rm -r "$projdir/electron_app/dist" || true
+mkdir -p "$projdir/electron_app/dist"
+cp $distdir/*.deb "$projdir/electron_app/dist/"
 
 rm -rf "$builddir"
 
 echo "Riot Desktop is ready to go in $pubdir: this directory can be hosted on your web server."
-echo "deb archives are in electron/dist/ - these should be added into your debian repository"
+echo "deb archives are in electron_app/dist/ - these should be added into your debian repository"
diff --git a/scripts/issues-burndown.pl b/scripts/issues-burndown.pl
old mode 100644
new mode 100755
index 7d0f3409c072ad3a7a5d12087dec34af1e73b1a4..67c05673df5ff378d28d50b5fff891dcd0ef829a
--- a/scripts/issues-burndown.pl
+++ b/scripts/issues-burndown.pl
@@ -4,16 +4,24 @@ use warnings;
 use strict;
 
 use Net::GitHub;
-use DateTime;
-use DateTime::Format::ISO8601;
+use Time::Moment;
+use Term::ReadPassword;
+
+# This version of the script emits the cumulative number of bugs, split into open & closed
+# suitable for drawing the 'top' and 'bottom' of a burndown graph.
+#
+# N.B. this doesn't take into account issues changing priority over time, but only their most recent priority.
+#
+# If you want instead the number of open issues on a given day, then look at issues-no-state.pl
 
 my $gh = Net::GitHub->new(
-    login => 'ara4n', pass => 'secret'
+    login => 'ara4n', pass => read_password("github password: "),
 );
 
 $gh->set_default_user_repo('vector-im', 'vector-web'); 
 
-my @issues = $gh->issue->repos_issues({ state => 'all', milestone => 3 });
+#my @issues = $gh->issue->repos_issues({ state => 'all', milestone => 3 });
+my @issues = $gh->issue->repos_issues({ state => 'all' });
 while ($gh->issue->has_next_page) {
     push @issues, $gh->issue->next_page;
 }
@@ -30,11 +38,11 @@ while ($gh->issue->has_next_page) {
 
 my $days = {};
 my $schema = {};
-my $now = DateTime->now();
+my $now = Time::Moment->now;
 
 foreach my $issue (@issues) {
     next if ($issue->{pull_request});
-    
+
     # use Data::Dumper;
     # print STDERR Dumper($issue);
 
@@ -56,10 +64,10 @@ foreach my $issue (@issues) {
     my $priority = &$extract_labels(qw(p1 p2 p3 p4 p5)) || "unprioritised";
     my $severity = &$extract_labels(qw(minor major critical cosmetic network)) || "no-severity";
 
-    my $start = DateTime::Format::ISO8601->parse_datetime($issue->{created_at});
+    my $start = Time::Moment->from_string($issue->{created_at});
 
     do {
-        my $ymd = $start->ymd();
+        my $ymd = $start->strftime('%F');
 
         $days->{ $ymd }->{ 'created' }->{ $type }->{ $priority }->{ $severity }->{ total }++;
         $schema->{ 'created' }->{ $type }->{ $priority }->{ $severity }->{ total }++;
@@ -68,13 +76,14 @@ foreach my $issue (@issues) {
             $schema->{ 'created' }->{ $type }->{ $priority }->{ $severity }->{ $_ }++;
         }
 
-        $start = $start->add(days => 1);
-    } while (DateTime->compare($start, $now) < 0);
+        $start = $start->plus_days(1);
+        # print STDERR "^";
+    } while ($start->compare($now) < 0);
 
     if ($state eq 'closed') {
-        my $end = DateTime::Format::ISO8601->parse_datetime($issue->{closed_at});
+        my $end = Time::Moment->from_string($issue->{closed_at});
         do {
-            my $ymd = $end->ymd();
+            my $ymd = $end->strftime('%F');
 
             $days->{ $ymd }->{ 'resolved' }->{ $type }->{ $priority }->{ $severity }->{ total }++;
             $schema->{ 'resolved' }->{ $type }->{ $priority }->{ $severity }->{ total }++;
@@ -83,9 +92,12 @@ foreach my $issue (@issues) {
                 $schema->{ 'resolved' }->{ $type }->{ $priority }->{ $severity }->{ $_ }++;
             }
 
-            $end = $end->add(days => 1);
-        } while (DateTime->compare($end, $now) < 0);
+            $end = $end->plus_days(1);
+        } while ($end->compare($now) < 0);
+        # print STDERR "v";
     }
+
+    # print STDERR "\n";
 }
 
 print "day,";
diff --git a/scripts/issues-no-state.pl b/scripts/issues-no-state.pl
index da12fb225adca758a16b5ae733b2bb59762a9019..9b07ed271f6bb67a0d674eab05fa6fdefc201510 100755
--- a/scripts/issues-no-state.pl
+++ b/scripts/issues-no-state.pl
@@ -6,14 +6,22 @@ use strict;
 use Net::GitHub;
 use DateTime;
 use DateTime::Format::ISO8601;
+use Term::ReadPassword;
+
+# This version of the script emits the total number of bugs open on a given day,
+# split by various tags.
+#
+# If you want instead the cumulative number of open & closed issues on a given day,
+# then look at issues-burndown.pl
 
 my $gh = Net::GitHub->new(
-    login => 'ara4n', pass => 'secret'
+    login => 'ara4n', pass => read_password("github password: "),
 );
 
 $gh->set_default_user_repo('vector-im', 'vector-web'); 
 
-my @issues = $gh->issue->repos_issues({ state => 'all', milestone => 3 });
+#my @issues = $gh->issue->repos_issues({ state => 'all', milestone => 3 });
+my @issues = $gh->issue->repos_issues({ state => 'all' });
 while ($gh->issue->has_next_page) {
     push @issues, $gh->issue->next_page;
 }
diff --git a/scripts/make-icons.sh b/scripts/make-icons.sh
index c77064abdd509ecca93eaf63538c8c8263a38b24..19e48895df46adc5135ef5435aa09d7e5c288de3 100755
--- a/scripts/make-icons.sh
+++ b/scripts/make-icons.sh
@@ -52,7 +52,7 @@ cp "$tmpdir/256.png" "$tmpdir/Riot.iconset/icon_256x256.png"
 cp "$tmpdir/512.png" "$tmpdir/Riot.iconset/icon_256x256@2x.png"
 cp "$tmpdir/512.png" "$tmpdir/Riot.iconset/icon_512x512.png"
 cp "$tmpdir/1024.png" "$tmpdir/Riot.iconset/icon_512x512@2x.png"
-iconutil -c icns -o electron/build/icon.icns "$tmpdir/Riot.iconset"
+iconutil -c icns -o electron_app/build/icon.icns "$tmpdir/Riot.iconset"
 
 cp "$tmpdir/36.png" "res/vector-icons/android-chrome-36x36.png"
 cp "$tmpdir/48.png" "res/vector-icons/android-chrome-48x48.png"
@@ -79,15 +79,17 @@ cp "$tmpdir/144.png" "res/vector-icons/mstile-144x144.png"
 cp "$tmpdir/150.png" "res/vector-icons/mstile-150x150.png"
 cp "$tmpdir/310.png" "res/vector-icons/mstile-310x310.png"
 cp "$tmpdir/310x150.png" "res/vector-icons/mstile-310x150.png"
+cp "$tmpdir/180.png" "electron_app/img/riot.png"
 
 convert "$tmpdir/16.png" "$tmpdir/32.png" "$tmpdir/64.png" "$tmpdir/128.png"  "$tmpdir/256.png" "res/vector-icons/favicon.ico"
 
-cp "res/vector-icons/favicon.ico" "electron/build/icon.ico"
+cp "res/vector-icons/favicon.ico" "electron_app/build/icon.ico"
+cp "res/vector-icons/favicon.ico" "electron_app/img/riot.ico"
 
 # https://github.com/electron-userland/electron-builder/blob/3f97b86993d4ea5172e562b182230a194de0f621/src/targets/LinuxTargetHelper.ts#L127
 for i in 24 96 16 48 64 128 256 512
 do
-    cp "$tmpdir/$i.png" "electron/build/icons/${i}x${i}.png"
+    cp "$tmpdir/$i.png" "electron_app/build/icons/${i}x${i}.png"
 done
 
 rm -r "$tmpdir"
diff --git a/scripts/package.sh b/scripts/package.sh
index 5c1fdd5e363791f07c6d18cbc1ff93d7c645fc06..b3bc00bf03a95786b41dac8d391f8a08cb9eb425 100755
--- a/scripts/package.sh
+++ b/scripts/package.sh
@@ -1,4 +1,4 @@
-#!/bin/sh
+#!/bin/bash
 
 set -e
 
@@ -22,7 +22,14 @@ cp config.sample.json webapp/
 
 mkdir -p dist
 cp -r webapp vector-$version
-echo $version > vector-$version/version
+
+# if $version looks like semver with leading v, strip it before writing to file
+if [[ ${version} =~ ^v[[:digit:]]+\.[[:digit:]]+\.[[:digit:]]+(-.+)?$ ]]; then
+    echo ${version:1} > vector-$version/version
+else
+    echo ${version} > vector-$version/version
+fi
+
 tar chvzf dist/vector-$version.tar.gz vector-$version
 rm -r vector-$version
 
diff --git a/src/components/structures/BottomLeftMenu.js b/src/components/structures/BottomLeftMenu.js
index f378cac628c693fcec8f692db1533f7ca1a3ee66..63dfac60d88c6bfd3c39a3cc55bdc0796d1a83a7 100644
--- a/src/components/structures/BottomLeftMenu.js
+++ b/src/components/structures/BottomLeftMenu.js
@@ -1,5 +1,6 @@
 /*
 Copyright 2015, 2016 OpenMarket Ltd
+Copyright 2017 Vector Creations Ltd
 
 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
@@ -14,13 +15,8 @@ See the License for the specific language governing permissions and
 limitations under the License.
 */
 
-'use strict';
-
-var React = require('react');
-var ReactDOM = require('react-dom');
-var sdk = require('matrix-react-sdk')
-var dis = require('matrix-react-sdk/lib/dispatcher');
-var AccessibleButton = require('matrix-react-sdk/lib/components/views/elements/AccessibleButton');
+import React from 'react';
+import sdk from 'matrix-react-sdk';
 
 module.exports = React.createClass({
     displayName: 'BottomLeftMenu',
@@ -30,121 +26,28 @@ module.exports = React.createClass({
         teamToken: React.PropTypes.string,
     },
 
-    getInitialState: function() {
-        return({
-            directoryHover : false,
-            roomsHover : false,
-            homeHover: false,
-            peopleHover : false,
-            settingsHover : false,
-        });
-    },
-
-    // Room events
-    onDirectoryClick: function() {
-        dis.dispatch({ action: 'view_room_directory' });
-    },
-
-    onDirectoryMouseEnter: function() {
-        this.setState({ directoryHover: true });
-    },
-
-    onDirectoryMouseLeave: function() {
-        this.setState({ directoryHover: false });
-    },
-
-    onRoomsClick: function() {
-        dis.dispatch({ action: 'view_create_room' });
-    },
-
-    onRoomsMouseEnter: function() {
-        this.setState({ roomsHover: true });
-    },
-
-    onRoomsMouseLeave: function() {
-        this.setState({ roomsHover: false });
-    },
-
-    // Home button events
-    onHomeClick: function() {
-        dis.dispatch({ action: 'view_home_page' });
-    },
-
-    onHomeMouseEnter: function() {
-        this.setState({ homeHover: true });
-    },
-
-    onHomeMouseLeave: function() {
-        this.setState({ homeHover: false });
-    },
-
-    // People events
-    onPeopleClick: function() {
-        dis.dispatch({ action: 'view_create_chat' });
-    },
-
-    onPeopleMouseEnter: function() {
-        this.setState({ peopleHover: true });
-    },
-
-    onPeopleMouseLeave: function() {
-        this.setState({ peopleHover: false });
-    },
-
-    // Settings events
-    onSettingsClick: function() {
-        dis.dispatch({ action: 'view_user_settings' });
-    },
-
-    onSettingsMouseEnter: function() {
-        this.setState({ settingsHover: true });
-    },
-
-    onSettingsMouseLeave: function() {
-        this.setState({ settingsHover: false });
-    },
-
-    // Get the label/tooltip to show
-    getLabel: function(label, show) {
-        if (show) {
-            var RoomTooltip = sdk.getComponent("rooms.RoomTooltip");
-            return <RoomTooltip className="mx_BottomLeftMenu_tooltip" label={label} />;
-        }
-    },
-
     render: function() {
-        var TintableSvg = sdk.getComponent('elements.TintableSvg');
+        const HomeButton = sdk.getComponent('elements.HomeButton');
+        const StartChatButton = sdk.getComponent('elements.StartChatButton');
+        const RoomDirectoryButton = sdk.getComponent('elements.RoomDirectoryButton');
+        const CreateRoomButton = sdk.getComponent('elements.CreateRoomButton');
+        const SettingsButton = sdk.getComponent('elements.SettingsButton');
 
         var homeButton;
         if (this.props.teamToken) {
-            homeButton = (
-                <AccessibleButton className="mx_BottomLeftMenu_homePage" onClick={ this.onHomeClick } onMouseEnter={ this.onHomeMouseEnter } onMouseLeave={ this.onHomeMouseLeave } >
-                    <TintableSvg src="img/icons-home.svg" width="25" height="25" />
-                    { this.getLabel("Welcome page", this.state.homeHover) }
-                </AccessibleButton>
-            );
+            homeButton = <HomeButton tooltip={true} />;
         }
 
         return (
             <div className="mx_BottomLeftMenu">
                 <div className="mx_BottomLeftMenu_options">
                     { homeButton }
-                    <AccessibleButton className="mx_BottomLeftMenu_people" onClick={ this.onPeopleClick } onMouseEnter={ this.onPeopleMouseEnter } onMouseLeave={ this.onPeopleMouseLeave } >
-                        <TintableSvg src="img/icons-people.svg" width="25" height="25" />
-                        { this.getLabel("Start chat", this.state.peopleHover) }
-                    </AccessibleButton>
-                    <AccessibleButton className="mx_BottomLeftMenu_directory" onClick={ this.onDirectoryClick } onMouseEnter={ this.onDirectoryMouseEnter } onMouseLeave={ this.onDirectoryMouseLeave } >
-                        <TintableSvg src="img/icons-directory.svg" width="25" height="25"/>
-                        { this.getLabel("Room directory", this.state.directoryHover) }
-                    </AccessibleButton>
-                    <AccessibleButton className="mx_BottomLeftMenu_createRoom" onClick={ this.onRoomsClick } onMouseEnter={ this.onRoomsMouseEnter } onMouseLeave={ this.onRoomsMouseLeave } >
-                        <TintableSvg src="img/icons-create-room.svg" width="25" height="25" />
-                        { this.getLabel("Create new room", this.state.roomsHover) }
-                    </AccessibleButton>
-                    <AccessibleButton className="mx_BottomLeftMenu_settings" onClick={ this.onSettingsClick } onMouseEnter={ this.onSettingsMouseEnter } onMouseLeave={ this.onSettingsMouseLeave } >
-                        <TintableSvg src="img/icons-settings.svg" width="25" height="25" />
-                        { this.getLabel("Settings", this.state.settingsHover) }
-                    </AccessibleButton>
+                    <StartChatButton tooltip={true} />
+                    <RoomDirectoryButton tooltip={true} />
+                    <CreateRoomButton tooltip={true} />
+                    <span className="mx_BottomLeftMenu_settings">
+                        <SettingsButton tooltip={true} />
+                    </span>
                 </div>
             </div>
         );
diff --git a/src/components/structures/RoomDirectory.js b/src/components/structures/RoomDirectory.js
index cdf8d19e55c7b5dc2ce6590ce95360f7ee740004..9104695938f10538efdb51c2f0900396d66931c7 100644
--- a/src/components/structures/RoomDirectory.js
+++ b/src/components/structures/RoomDirectory.js
@@ -204,7 +204,7 @@ module.exports = React.createClass({
                 }).done(() => {
                     modal.close();
                     this.refreshRoomList();
-                }, function(err) {
+                }, (err) => {
                     modal.close();
                     this.refreshRoomList();
                     console.error("Failed to " + step + ": " + err);
diff --git a/src/components/structures/RoomSubList.js b/src/components/structures/RoomSubList.js
index 577dac9c8f5c2c8ea675055cff48926552883650..ab6c4422d19ceeb176761c67e12e190e84ddec0d 100644
--- a/src/components/structures/RoomSubList.js
+++ b/src/components/structures/RoomSubList.js
@@ -1,4 +1,5 @@
 /*
+Copyright 2017 Vector Creations Ltd
 Copyright 2015, 2016 OpenMarket Ltd
 
 Licensed under the Apache License, Version 2.0 (the "License");
@@ -29,6 +30,7 @@ var FormattingUtils = require('matrix-react-sdk/lib/utils/FormattingUtils');
 var AccessibleButton = require('matrix-react-sdk/lib/components/views/elements/AccessibleButton');
 var ConstantTimeDispatcher = require('matrix-react-sdk/lib/ConstantTimeDispatcher');
 var RoomSubListHeader = require('./RoomSubListHeader.js');
+import Modal from 'matrix-react-sdk/lib/Modal';
 
 // turn this on for drag & drop console debugging galore
 var debug = false;
@@ -82,6 +84,8 @@ var RoomSubList = React.createClass({
         incomingCall: React.PropTypes.object,
         onShowMoreRooms: React.PropTypes.func,
         searchFilter: React.PropTypes.string,
+        emptyContent: React.PropTypes.node, // content shown if the list is empty
+        headerItems: React.PropTypes.node, // content shown in the sublist header
     },
 
     getInitialState: function() {
@@ -468,16 +472,15 @@ var RoomSubList = React.createClass({
 
     render: function() {
         var connectDropTarget = this.props.connectDropTarget;
-        var RoomDropTarget = sdk.getComponent('rooms.RoomDropTarget');
         var TruncatedList = sdk.getComponent('elements.TruncatedList');
 
         var label = this.props.collapsed ? null : this.props.label;
 
-        //console.log("render: " + JSON.stringify(this.state.sortedList));
-
-        var target;
-        if (this.state.sortedList.length == 0 && this.props.editable) {
-            target = <RoomDropTarget label={ 'Drop here to ' + this.props.verb }/>;
+        let content;
+        if (this.state.sortedList.length == 0) {
+            content = this.props.emptyContent;
+        } else {
+            content = this.makeRoomTiles();
         }
 
         var roomCount = this.props.list.length > 0 ? this.props.list.length : '';
@@ -497,8 +500,7 @@ var RoomSubList = React.createClass({
             if (!this.state.hidden) {
                 subList = <TruncatedList className={ classes } truncateAt={this.state.truncateAt}
                                          createOverflowElement={this._createOverflowTile} >
-                                { target }
-                                { this.makeRoomTiles() }
+                                { content }
                           </TruncatedList>;
             }
             else {
@@ -520,6 +522,7 @@ var RoomSubList = React.createClass({
                         roomNotificationCount={ this.roomNotificationCount() }
                         onClick={ this.onClick }
                         onHeaderClick={ this.props.onHeaderClick }
+                        headerItems={this.props.headerItems}
                     />
                     { subList }
                 </div>
@@ -541,6 +544,7 @@ var RoomSubList = React.createClass({
                             roomNotificationCount={ this.roomNotificationCount() }
                             onClick={ this.onClick }
                             onHeaderClick={ this.props.onHeaderClick }
+                            headerItems={this.props.headerItems}
                         />
                      : undefined }
                     { (this.props.showSpinner && !this.state.hidden) ? <Loader /> : undefined }
diff --git a/src/components/structures/RoomSubListHeader.js b/src/components/structures/RoomSubListHeader.js
index ad9aff5f704452c0682493a7dcec7545ff1fa668..74094ae0bab1d0b44fc58388a1d914db5eb1ad8e 100644
--- a/src/components/structures/RoomSubListHeader.js
+++ b/src/components/structures/RoomSubListHeader.js
@@ -14,16 +14,11 @@ See the License for the specific language governing permissions and
 limitations under the License.
 */
 
-'use strict';
-
-var React = require('react');
-var ReactDOM = require('react-dom');
-var classNames = require('classnames');
-var sdk = require('matrix-react-sdk')
-var FormattingUtils = require('matrix-react-sdk/lib/utils/FormattingUtils');
-var RoomNotifs = require('matrix-react-sdk/lib/RoomNotifs');
-var AccessibleButton = require('matrix-react-sdk/lib/components/views/elements/AccessibleButton');
-var ConstantTimeDispatcher = require('matrix-react-sdk/lib/ConstantTimeDispatcher');
+import React from 'react';
+import classNames from 'classnames';
+import sdk from 'matrix-react-sdk';
+import { formatCount } from 'matrix-react-sdk/lib/utils/FormattingUtils';
+import AccessibleButton from 'matrix-react-sdk/lib/components/views/elements/AccessibleButton';
 
 module.exports = React.createClass({
     displayName: 'RoomSubListHeader',
@@ -42,6 +37,7 @@ module.exports = React.createClass({
         hidden: React.PropTypes.bool,
         onClick: React.PropTypes.func,
         onHeaderClick: React.PropTypes.func,
+        headerItems: React.PropTypes.node, // content shown in the sublist header
     },
 
     getDefaultProps: function() {
@@ -63,35 +59,34 @@ module.exports = React.createClass({
     // },
 
     render: function() {
-        var TintableSvg = sdk.getComponent("elements.TintableSvg");
+        const TintableSvg = sdk.getComponent("elements.TintableSvg");
 
-        var subListNotifications = this.props.roomNotificationCount;
-        var subListNotifCount = subListNotifications[0];
-        var subListNotifHighlight = subListNotifications[1];
+        const subListNotifications = this.props.roomNotificationCount;
+        const subListNotifCount = subListNotifications[0];
+        const subListNotifHighlight = subListNotifications[1];
 
-        var chevronClasses = classNames({
+        const chevronClasses = classNames({
             'mx_RoomSubList_chevron': true,
             'mx_RoomSubList_chevronRight': this.props.hidden,
             'mx_RoomSubList_chevronDown': !this.props.hidden,
         });
 
-        var badgeClasses = classNames({
+        const badgeClasses = classNames({
             'mx_RoomSubList_badge': true,
             'mx_RoomSubList_badgeHighlight': subListNotifHighlight,
         });
 
-        var badge;
+        let badge;
         if (subListNotifCount > 0) {
-            badge = <div className={badgeClasses}>{ FormattingUtils.formatCount(subListNotifCount) }</div>;
-        }
-        else if (subListNotifHighlight) {
+            badge = <div className={badgeClasses}>{ formatCount(subListNotifCount) }</div>;
+        } else if (subListNotifHighlight) {
             badge = <div className={badgeClasses}>!</div>;   
         }
 
         // When collapsed, allow a long hover on the header to show user
         // the full tag name and room count
-        var title;
-        var roomCount = this.props.roomCount;
+        let title;
+        const roomCount = this.props.roomCount;
         if (this.props.collapsed) {
             title = this.props.label;
             if (roomCount !== '') {
@@ -99,9 +94,9 @@ module.exports = React.createClass({
             }
         }
 
-        var incomingCall;
+        let incomingCall;
         if (this.props.isIncomingCallRoom) {
-            var IncomingCallBox = sdk.getComponent("voip.IncomingCallBox");
+            const IncomingCallBox = sdk.getComponent("voip.IncomingCallBox");
             incomingCall = <IncomingCallBox className="mx_RoomSubList_incomingCall" incomingCall={ this.props.incomingCall }/>;
         }
 
@@ -109,6 +104,7 @@ module.exports = React.createClass({
             <div className="mx_RoomSubList_labelContainer" title={ title } ref="header">
                 <AccessibleButton onClick={ this.props.onClick } className="mx_RoomSubList_label" tabIndex="0">
                     { this.props.collapsed ? '' : this.props.label }
+                    {this.props.headerItems}
                     <div className="mx_RoomSubList_roomCount">{ roomCount }</div>
                     <div className={chevronClasses}></div>
                     { badge }
diff --git a/src/skins/vector/css/_components.scss b/src/skins/vector/css/_components.scss
index 54f6c79519487d9b2153ea8fa1ba54925bd7f55a..a0864817fe8d8ce4f25659e6199fec8a7eeac5a3 100644
--- a/src/skins/vector/css/_components.scss
+++ b/src/skins/vector/css/_components.scss
@@ -27,6 +27,7 @@
 @import "./matrix-react-sdk/views/elements/_MemberEventListSummary.scss";
 @import "./matrix-react-sdk/views/elements/_ProgressBar.scss";
 @import "./matrix-react-sdk/views/elements/_RichText.scss";
+@import "./matrix-react-sdk/views/elements/_RoleButton.scss";
 @import "./matrix-react-sdk/views/login/_InteractiveAuthEntryComponents.scss";
 @import "./matrix-react-sdk/views/login/_ServerConfig.scss";
 @import "./matrix-react-sdk/views/messages/_MEmoteBody.scss";
diff --git a/src/skins/vector/css/matrix-react-sdk/structures/_RoomView.scss b/src/skins/vector/css/matrix-react-sdk/structures/_RoomView.scss
index 1286193974da7a49f4029833d3ed56d6245955a7..e251ecd14cb3efd0e367488a5d0b0c475708c521 100644
--- a/src/skins/vector/css/matrix-react-sdk/structures/_RoomView.scss
+++ b/src/skins/vector/css/matrix-react-sdk/structures/_RoomView.scss
@@ -160,7 +160,8 @@ hr.mx_RoomView_myReadMarker {
     border-bottom: solid 1px $accent-color;
     margin-top: 0px;
     position: relative;
-    top: 5px;
+    top: -1px;
+    z-index: 1;
 }
 
 .mx_RoomView_statusArea {
diff --git a/src/skins/vector/css/matrix-react-sdk/views/elements/_MemberEventListSummary.scss b/src/skins/vector/css/matrix-react-sdk/views/elements/_MemberEventListSummary.scss
index 1858865980dfd576c568f76469c63b534b3162d1..1969bc2db06f172cfc1fdbf33bf8c21c7b48c036 100644
--- a/src/skins/vector/css/matrix-react-sdk/views/elements/_MemberEventListSummary.scss
+++ b/src/skins/vector/css/matrix-react-sdk/views/elements/_MemberEventListSummary.scss
@@ -20,6 +20,7 @@ limitations under the License.
 
 .mx_TextualEvent.mx_MemberEventListSummary_summary {
     font-size: 14px;
+    display: inline-flex;
 }
 
 .mx_MemberEventListSummary_avatars {
diff --git a/src/skins/vector/css/matrix-react-sdk/views/elements/_RoleButton.scss b/src/skins/vector/css/matrix-react-sdk/views/elements/_RoleButton.scss
new file mode 100644
index 0000000000000000000000000000000000000000..094e0b9b1b7d94c1ac472bcd8b8fa1dd002aa34c
--- /dev/null
+++ b/src/skins/vector/css/matrix-react-sdk/views/elements/_RoleButton.scss
@@ -0,0 +1,33 @@
+/*
+Copyright 2107 Vector Creations 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_RoleButton {
+    margin-left: 4px;
+    margin-right: 4px;
+    cursor: pointer;
+    display: inline-block;
+}
+
+.mx_RoleButton object {
+    pointer-events: none;
+}
+
+.mx_RoleButton_tooltip {
+    display: inline-block;
+    position: relative;
+    top: -25px;
+    left: 6px;
+}
diff --git a/src/skins/vector/css/matrix-react-sdk/views/rooms/_RoomList.scss b/src/skins/vector/css/matrix-react-sdk/views/rooms/_RoomList.scss
index 110dcd5b6b0947300f44a1b44ca28089f2421705..35787ca0c48d0022153b348c60cd336d0049415a 100644
--- a/src/skins/vector/css/matrix-react-sdk/views/rooms/_RoomList.scss
+++ b/src/skins/vector/css/matrix-react-sdk/views/rooms/_RoomList.scss
@@ -1,5 +1,6 @@
 /*
 Copyright 2015, 2016 OpenMarket Ltd
+Copyright 2107 Vector Creations Ltd
 
 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
@@ -37,3 +38,25 @@ limitations under the License.
 .mx_RoomList_scrollbar .gm-scrollbar.-vertical {
     z-index: 6;
 }
+
+.mx_RoomList_emptySubListTip {
+    font-size: 13px;
+    margin-left: 18px;
+    margin-right: 18px;
+    margin-top: 8px;
+    margin-bottom: 7px;
+    padding: 5px;
+    border: 1px dashed $accent-color;
+    color: $primary-fg-color;
+    background-color: $droptarget-bg-color;
+    border-radius: 4px;
+}
+
+.mx_RoomList_emptySubListTip .mx_RoleButton {
+    vertical-align: -3px;
+}
+
+.mx_RoomList_headerButtons {
+    position: absolute;
+    right: 60px;
+}
diff --git a/src/skins/vector/css/vector-web/structures/_LeftPanel.scss b/src/skins/vector/css/vector-web/structures/_LeftPanel.scss
index d3bbce1b19466b7f6f713495e334f9f6cedc594d..f171591cd60813d6e6685c331038715c3d842858 100644
--- a/src/skins/vector/css/vector-web/structures/_LeftPanel.scss
+++ b/src/skins/vector/css/vector-web/structures/_LeftPanel.scss
@@ -64,43 +64,25 @@ limitations under the License.
     pointer-events: none;
 }
 
-.mx_LeftPanel .mx_BottomLeftMenu_homePage,
-.mx_LeftPanel .mx_BottomLeftMenu_directory,
-.mx_LeftPanel .mx_BottomLeftMenu_createRoom,
-.mx_LeftPanel .mx_BottomLeftMenu_people,
-.mx_LeftPanel .mx_BottomLeftMenu_settings {
-    display: inline-block;
-    cursor: pointer;
-}
-
-.collapsed .mx_BottomLeftMenu_homePage,
-.collapsed .mx_BottomLeftMenu_directory,
-.collapsed .mx_BottomLeftMenu_createRoom,
-.collapsed .mx_BottomLeftMenu_people,
-.collapsed .mx_BottomLeftMenu_settings {
+.collapsed .mx_RoleButton {
     margin-right: 0px ! important;
     padding-top: 3px ! important;
     padding-bottom: 3px ! important;
 }
 
-.mx_LeftPanel .mx_BottomLeftMenu_homePage,
-.mx_LeftPanel .mx_BottomLeftMenu_directory,
-.mx_LeftPanel .mx_BottomLeftMenu_createRoom,
-.mx_LeftPanel .mx_BottomLeftMenu_people {
+.mx_BottomLeftMenu_options .mx_RoleButton {
+    margin-left: 0px;
     margin-right: 10px;
 }
 
-.mx_LeftPanel .mx_BottomLeftMenu_settings {
+.mx_BottomLeftMenu_options .mx_BottomLeftMenu_settings {
     float: right;
 }
 
-.mx_LeftPanel.collapsed .mx_BottomLeftMenu_settings {
-    float: none;
+.mx_BottomLeftMenu_options .mx_BottomLeftMenu_settings .mx_RoleButton {
+    margin-right: 0px;
 }
 
-.mx_LeftPanel .mx_BottomLeftMenu_tooltip {
-    display: inline-block;
-    position: relative;
-    top: -25px;
-    left: 6px;
+.mx_LeftPanel.collapsed .mx_BottomLeftMenu_settings {
+    float: none;
 }
diff --git a/src/vector/platform/ElectronPlatform.js b/src/vector/platform/ElectronPlatform.js
index 9c857e3524c76efff04e4cd1bd0bea091d33054f..82ef0b516843cbea300e68413f8141f70caf140e 100644
--- a/src/vector/platform/ElectronPlatform.js
+++ b/src/vector/platform/ElectronPlatform.js
@@ -20,13 +20,11 @@ limitations under the License.
 import VectorBasePlatform from './VectorBasePlatform';
 import dis from 'matrix-react-sdk/lib/dispatcher';
 import q from 'q';
-
-const electron = require('electron');
-const remote = electron.remote;
+import electron, {remote} from 'electron';
 
 remote.autoUpdater.on('update-downloaded', onUpdateDownloaded);
 
-function onUpdateDownloaded(ev, releaseNotes, ver, date, updateURL) {
+function onUpdateDownloaded(ev: Event, releaseNotes: string, ver: string, date: Date, updateURL: string) {
     dis.dispatch({
         action: 'new_version',
         currentVersion: remote.app.getVersion(),
@@ -35,7 +33,7 @@ function onUpdateDownloaded(ev, releaseNotes, ver, date, updateURL) {
     });
 }
 
-function platformFriendlyName() {
+function platformFriendlyName(): string {
     console.log(window.process);
     switch (window.process.platform) {
         case 'darwin':
@@ -72,11 +70,11 @@ export default class ElectronPlatform extends VectorBasePlatform {
         }
     }
 
-    supportsNotifications() : boolean {
+    supportsNotifications(): boolean {
         return true;
     }
 
-    maySendNotifications() : boolean {
+    maySendNotifications(): boolean {
         return true;
     }
 
@@ -100,7 +98,7 @@ export default class ElectronPlatform extends VectorBasePlatform {
                 icon: avatarUrl,
                 tag: 'vector',
                 silent: true, // we play our own sounds
-            }
+            },
         );
 
         notification.onclick = function() {
@@ -123,7 +121,7 @@ export default class ElectronPlatform extends VectorBasePlatform {
         notif.close();
     }
 
-    getAppVersion() {
+    getAppVersion(): Promise<string> {
         return q(remote.app.getVersion());
     }
 
@@ -140,15 +138,15 @@ export default class ElectronPlatform extends VectorBasePlatform {
         electron.ipcRenderer.send('install_update');
     }
 
-    getDefaultDeviceDisplayName() {
+    getDefaultDeviceDisplayName(): string {
         return 'Riot Desktop on ' + platformFriendlyName();
     }
 
-    screenCaptureErrorString() {
+    screenCaptureErrorString(): ?string {
         return null;
     }
 
-    requestNotificationPermission() : Promise {
+    requestNotificationPermission(): Promise<string> {
         return q('granted');
     }
 
diff --git a/src/vector/platform/VectorBasePlatform.js b/src/vector/platform/VectorBasePlatform.js
index 5240f3f5836000839bed3c11b1ed3af10dc5cf46..1466b76ae37c57dff2cd9e210ec48f354b03da6e 100644
--- a/src/vector/platform/VectorBasePlatform.js
+++ b/src/vector/platform/VectorBasePlatform.js
@@ -44,7 +44,7 @@ export default class VectorBasePlatform extends BasePlatform {
      * Get a sensible default display name for the
      * device Vector is running on
      */
-    getDefaultDeviceDisplayName() {
+    getDefaultDeviceDisplayName(): string {
         return "Unknown device";
     }
 }
diff --git a/src/vector/platform/WebPlatform.js b/src/vector/platform/WebPlatform.js
index 5dc55052972a5d8b6f6f9d1ff3230bf4f853d008..72ca19f06c2256e6beb40c0325493ac6cf3cdcfe 100644
--- a/src/vector/platform/WebPlatform.js
+++ b/src/vector/platform/WebPlatform.js
@@ -52,7 +52,7 @@ export default class WebPlatform extends VectorBasePlatform {
             }
 
             this.favicon.badge(notif, {
-                bgColor: bgColor
+                bgColor: bgColor,
             });
         } catch (e) {
             console.warn(`Failed to set badge count: ${e.message}`);
@@ -75,7 +75,7 @@ export default class WebPlatform extends VectorBasePlatform {
      * Returns true if the platform supports displaying
      * notifications, otherwise false.
      */
-    supportsNotifications() : boolean {
+    supportsNotifications(): boolean {
         return Boolean(global.Notification);
     }
 
@@ -83,8 +83,8 @@ export default class WebPlatform extends VectorBasePlatform {
      * Returns true if the application currently has permission
      * to display notifications. Otherwise false.
      */
-    maySendNotifications() : boolean {
-        return global.Notification.permission == 'granted';
+    maySendNotifications(): boolean {
+        return global.Notification.permission === 'granted';
     }
 
     /**
@@ -94,7 +94,7 @@ export default class WebPlatform extends VectorBasePlatform {
      * that is 'granted' if the user allowed the request or
      * 'denied' otherwise.
      */
-    requestNotificationPermission() : Promise {
+    requestNotificationPermission(): Promise<string> {
         // annoyingly, the latest spec says this returns a
         // promise, but this is only supported in Chrome 46
         // and Firefox 47, so adapt the callback API.
@@ -113,13 +113,13 @@ export default class WebPlatform extends VectorBasePlatform {
                 icon: avatarUrl,
                 tag: "vector",
                 silent: true, // we play our own sounds
-            }
+            },
         );
 
         notification.onclick = function() {
             dis.dispatch({
                 action: 'view_room',
-                room_id: room.roomId
+                room_id: room.roomId,
             });
             global.focus();
             notification.close();
@@ -132,7 +132,7 @@ export default class WebPlatform extends VectorBasePlatform {
         }, 5 * 1000);
     }
 
-    _getVersion() {
+    _getVersion(): Promise<string> {
         const deferred = q.defer();
 
         // We add a cachebuster to the request to make sure that we know about
@@ -148,19 +148,19 @@ export default class WebPlatform extends VectorBasePlatform {
             },
             (err, response, body) => {
                 if (err || response.status < 200 || response.status >= 300) {
-                    if (err == null) err = { status: response.status };
+                    if (err === null) err = { status: response.status };
                     deferred.reject(err);
                     return;
                 }
 
                 const ver = body.trim();
                 deferred.resolve(ver);
-            }
+            },
         );
         return deferred.promise;
     }
 
-    getAppVersion() {
+    getAppVersion(): Promise<string> {
         if (this.runningVersion !== null) {
             return q(this.runningVersion);
         }
@@ -169,9 +169,9 @@ export default class WebPlatform extends VectorBasePlatform {
 
     pollForUpdate() {
         this._getVersion().done((ver) => {
-            if (this.runningVersion == null) {
+            if (this.runningVersion === null) {
                 this.runningVersion = ver;
-            } else if (this.runningVersion != ver) {
+            } else if (this.runningVersion !== ver) {
                 dis.dispatch({
                     action: 'new_version',
                     currentVersion: this.runningVersion,
@@ -187,19 +187,18 @@ export default class WebPlatform extends VectorBasePlatform {
         window.location.reload();
     }
 
-    getDefaultDeviceDisplayName() {
+    getDefaultDeviceDisplayName(): string {
         // strip query-string and fragment from uri
-        let u = url.parse(window.location.href);
+        const u = url.parse(window.location.href);
         u.search = "";
         u.hash = "";
-        let app_name = u.format();
+        const appName = u.format();
 
-        let ua = new UAParser();
-        return app_name + " via " + ua.getBrowser().name +
-            " on " + ua.getOS().name;
+        const ua = new UAParser();
+        return `${appName} via ${ua.getBrowser().name} on ${ua.getOS().name}`;
     }
 
-    screenCaptureErrorString() {
+    screenCaptureErrorString(): ?string {
         // it won't work at all if you're not on HTTPS so whine whine whine
         if (!global.window || global.window.location.protocol !== "https:") {
             return "You need to be using HTTPS to place a screen-sharing call.";
diff --git a/src/vector/submit-rageshake.js b/src/vector/submit-rageshake.js
index ef6fbabe29f926ad3a0c4fdb04d1e631f6255944..45b427e89994d26ca3b6107a70a600becc8be53c 100644
--- a/src/vector/submit-rageshake.js
+++ b/src/vector/submit-rageshake.js
@@ -17,6 +17,7 @@ limitations under the License.
 import pako from 'pako';
 import q from "q";
 
+import MatrixClientPeg from 'matrix-react-sdk/lib/MatrixClientPeg';
 import PlatformPeg from 'matrix-react-sdk/lib/PlatformPeg';
 
 import rageshake from './rageshake'
@@ -64,6 +65,8 @@ export default async function sendBugReport(bugReportEndpoint, opts) {
         userAgent = window.navigator.userAgent;
     }
 
+    const client = MatrixClientPeg.get();
+
     console.log("Sending bug report.");
 
     const body = new FormData();
@@ -72,6 +75,11 @@ export default async function sendBugReport(bugReportEndpoint, opts) {
     body.append('version', version);
     body.append('user_agent', userAgent);
 
+    if (client) {
+        body.append('user_id', client.credentials.userId);
+        body.append('device_id', client.deviceId);
+    }
+
     if (opts.sendLogs) {
         progressCallback("Collecting logs");
         const logs = await rageshake.getLogsForReport();