diff --git a/.babelrc b/.babelrc index 8c7b66269d..6ba0e0dae0 100644 --- a/.babelrc +++ b/.babelrc @@ -1,4 +1,4 @@ { "presets": ["react", "es2015", "es2016"], - "plugins": ["transform-class-properties", "transform-object-rest-spread", "transform-async-to-generator", "transform-runtime", "add-module-exports"] + "plugins": ["transform-class-properties", "transform-object-rest-spread", "transform-async-to-bluebird", "transform-runtime", "add-module-exports"] } diff --git a/.gitignore b/.gitignore index 060ca6e934..2ad05012a0 100644 --- a/.gitignore +++ b/.gitignore @@ -14,6 +14,6 @@ npm-debug.log electron/dist electron/pub -/.idea +**/.idea /config.json /src/component-index.js diff --git a/.travis.yml b/.travis.yml index e020ba7d15..94ed745cd8 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. @@ -5,8 +12,9 @@ node_js: # - 4.x is still in LTS (until April 2018), but some of our deps (notably # extract-zip) don't work with it # - 5.x has been EOLed for nearly a year. - # - 6.x is the current 'LTS' version - # - 7.x is the current 'current' version (until October 2017) + # - 6.x is the active 'LTS' version + # - 7.x is no longer supported + # - 8.x is the current 'current' version (until October 2017) # # see: https://github.com/nodejs/LTS/ # @@ -16,6 +24,8 @@ 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. diff --git a/CHANGELOG.md b/CHANGELOG.md index cdba055144..a78d26e0bc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,168 @@ +Changes in [0.12.1](https://github.com/vector-im/riot-web/releases/tag/v0.12.1) (2017-08-23) +============================================================================================ +[Full Changelog](https://github.com/vector-im/riot-web/compare/v0.12.1-rc.1...v0.12.1) + + * [No changes] + +Changes in [0.12.1-rc.1](https://github.com/vector-im/riot-web/releases/tag/v0.12.1-rc.1) (2017-08-22) +====================================================================================================== +[Full Changelog](https://github.com/vector-im/riot-web/compare/v0.12.0-rc.2...v0.12.1-rc.1) + + * Update from Weblate. + [\#4832](https://github.com/vector-im/riot-web/pull/4832) + * Misc styling fixes. + [\#4826](https://github.com/vector-im/riot-web/pull/4826) + * Show / Hide apps icons + [\#4774](https://github.com/vector-im/riot-web/pull/4774) + +Changes in [0.12.0-rc.1](https://github.com/vector-im/riot-web/releases/tag/v0.12.0-rc.1) (2017-08-16) +====================================================================================================== +[Full Changelog](https://github.com/vector-im/riot-web/compare/v0.11.4...v0.12.0-rc.1) + + * Update from Weblate. + [\#4797](https://github.com/vector-im/riot-web/pull/4797) + * move focus-via-up/down cursors to LeftPanel + [\#4777](https://github.com/vector-im/riot-web/pull/4777) + * Remove userId property on RightPanel + [\#4775](https://github.com/vector-im/riot-web/pull/4775) + * Make member device info buttons fluid and stackable with flexbox + [\#4776](https://github.com/vector-im/riot-web/pull/4776) + * un-i18n Modal Analytics + [\#4688](https://github.com/vector-im/riot-web/pull/4688) + * Quote using innerText + [\#4773](https://github.com/vector-im/riot-web/pull/4773) + * Karma tweaks for riot-web + [\#4765](https://github.com/vector-im/riot-web/pull/4765) + * Fix typo with scripts/fetch-develop-deps.sh in Building From Source + [\#4764](https://github.com/vector-im/riot-web/pull/4764) + * Adjust CSS for optional avatars in pills + [\#4757](https://github.com/vector-im/riot-web/pull/4757) + * Fix crypto on develop + [\#4754](https://github.com/vector-im/riot-web/pull/4754) + * Fix signing key url in readme + [\#4464](https://github.com/vector-im/riot-web/pull/4464) + * update gitignore to allow .idea directory to exist in subdirs + [\#4749](https://github.com/vector-im/riot-web/pull/4749) + * tweak compact theme + [\#4665](https://github.com/vector-im/riot-web/pull/4665) + * Update draft-js from 0.10.1 to 0.11.0-alpha + [\#4740](https://github.com/vector-im/riot-web/pull/4740) + * electron support for mouse forward/back buttons in Windows + [\#4739](https://github.com/vector-im/riot-web/pull/4739) + * Update draft-js from 0.8.1 to 0.10.1 + [\#4730](https://github.com/vector-im/riot-web/pull/4730) + * Make pills, emoji translucent when sending + [\#4693](https://github.com/vector-im/riot-web/pull/4693) + * Widget permissions styling and icon + [\#4690](https://github.com/vector-im/riot-web/pull/4690) + * CSS required for composer autoscroll + [\#4682](https://github.com/vector-im/riot-web/pull/4682) + * CSS for group edit UI + [\#4608](https://github.com/vector-im/riot-web/pull/4608) + * Fix a couple of minor errors in the room list + [\#4671](https://github.com/vector-im/riot-web/pull/4671) + * Styling for beta testing icon. + [\#4584](https://github.com/vector-im/riot-web/pull/4584) + * Increase the timeout for clearing indexeddbs + [\#4650](https://github.com/vector-im/riot-web/pull/4650) + * Make some adjustments to mx_UserPill and mx_RoomPill + [\#4597](https://github.com/vector-im/riot-web/pull/4597) + * Apply CSS to
tags to distinguish them from each other
+ [\#4639](https://github.com/vector-im/riot-web/pull/4639)
+ * Use `catch` instead of `fail` to handle room tag error
+ [\#4643](https://github.com/vector-im/riot-web/pull/4643)
+ * CSS for decorated matrix.to links in the composer
+ [\#4583](https://github.com/vector-im/riot-web/pull/4583)
+ * Deflake the joining test
+ [\#4579](https://github.com/vector-im/riot-web/pull/4579)
+ * Bump react to 15.6 to fix build problems
+ [\#4577](https://github.com/vector-im/riot-web/pull/4577)
+ * Improve AppTile menu bar button styling.
+ [\#4567](https://github.com/vector-im/riot-web/pull/4567)
+ * Transform `async` functions to bluebird promises
+ [\#4572](https://github.com/vector-im/riot-web/pull/4572)
+ * use flushAllExpected in joining test
+ [\#4570](https://github.com/vector-im/riot-web/pull/4570)
+ * Switch riot-web to bluebird
+ [\#4565](https://github.com/vector-im/riot-web/pull/4565)
+ * loading tests: wait for login component
+ [\#4564](https://github.com/vector-im/riot-web/pull/4564)
+ * Remove CSS for the MessageComposerInputOld
+ [\#4568](https://github.com/vector-im/riot-web/pull/4568)
+ * Implement the focus_room_filter action
+ [\#4560](https://github.com/vector-im/riot-web/pull/4560)
+ * CSS for Rooms in Group View
+ [\#4530](https://github.com/vector-im/riot-web/pull/4530)
+ * more HomePage tweaks
+ [\#4557](https://github.com/vector-im/riot-web/pull/4557)
+ * Give HomePage an unmounted guard
+ [\#4556](https://github.com/vector-im/riot-web/pull/4556)
+ * Take RTE out of labs
+ [\#4500](https://github.com/vector-im/riot-web/pull/4500)
+ * CSS for Groups page
+ [\#4468](https://github.com/vector-im/riot-web/pull/4468)
+ * CSS for GroupView
+ [\#4442](https://github.com/vector-im/riot-web/pull/4442)
+ * remove unused class
+ [\#4525](https://github.com/vector-im/riot-web/pull/4525)
+ * Fix long words causing MessageComposer to widen
+ [\#4466](https://github.com/vector-im/riot-web/pull/4466)
+ * Add visual bell animation for RTE
+ [\#4516](https://github.com/vector-im/riot-web/pull/4516)
+ * Truncate auto-complete pills properly
+ [\#4502](https://github.com/vector-im/riot-web/pull/4502)
+ * Use chrome headless instead of phantomjs
+ [\#4512](https://github.com/vector-im/riot-web/pull/4512)
+ * Use external mock-request
+ [\#4489](https://github.com/vector-im/riot-web/pull/4489)
+ * fix Quote not closing contextual menu
+ [\#4443](https://github.com/vector-im/riot-web/pull/4443)
+ * Apply white-space: pre-wrap to mx_MEmoteBody
+ [\#4470](https://github.com/vector-im/riot-web/pull/4470)
+ * Add some style improvements to autocompletions
+ [\#4456](https://github.com/vector-im/riot-web/pull/4456)
+ * Styling for apps / widgets
+ [\#4447](https://github.com/vector-im/riot-web/pull/4447)
+ * Attempt to flush the rageshake logs on close
+ [\#4400](https://github.com/vector-im/riot-web/pull/4400)
+ * Update from Weblate.
+ [\#4401](https://github.com/vector-im/riot-web/pull/4401)
+ * improve update polling electron and provide a manual check for updates
+ button
+ [\#4176](https://github.com/vector-im/riot-web/pull/4176)
+ * Fix load failure in firefox when indexedDB is disabled
+ [\#4395](https://github.com/vector-im/riot-web/pull/4395)
+ * Change missed 'Redact' to 'Remove' in ImageView.
+ [\#4362](https://github.com/vector-im/riot-web/pull/4362)
+ * explicit convert to nativeImage to stabilise trayIcon on Windows [Electron]
+ [\#4355](https://github.com/vector-im/riot-web/pull/4355)
+ * Use _tJsx for PasswordNagBar (because it has )
+ [\#4373](https://github.com/vector-im/riot-web/pull/4373)
+ * Clean up some log outputs from the integ tests
+ [\#4376](https://github.com/vector-im/riot-web/pull/4376)
+ * CSS for redeisng of password warning
+ [\#4367](https://github.com/vector-im/riot-web/pull/4367)
+ * Give _t to PasswordNagBar, add CSS for UserSettings password warning
+ [\#4366](https://github.com/vector-im/riot-web/pull/4366)
+ * Update from Weblate.
+ [\#4361](https://github.com/vector-im/riot-web/pull/4361)
+ * Update from Weblate.
+ [\#4360](https://github.com/vector-im/riot-web/pull/4360)
+ * Test 'return-to-app' functionality
+ [\#4352](https://github.com/vector-im/riot-web/pull/4352)
+ * Update from Weblate.
+ [\#4354](https://github.com/vector-im/riot-web/pull/4354)
+ * onLoadCompleted is now onTokenLoginCompleted
+ [\#4335](https://github.com/vector-im/riot-web/pull/4335)
+ * Tweak tests to match updates to matrixchat
+ [\#4325](https://github.com/vector-im/riot-web/pull/4325)
+ * Update from Weblate.
+ [\#4346](https://github.com/vector-im/riot-web/pull/4346)
+ * change dispatcher forward_event signature
+ [\#4337](https://github.com/vector-im/riot-web/pull/4337)
+ * Add border on hover for code blocks
+ [\#4259](https://github.com/vector-im/riot-web/pull/4259)
+
Changes in [0.11.4](https://github.com/vector-im/riot-web/releases/tag/v0.11.4) (2017-06-22)
============================================================================================
[Full Changelog](https://github.com/vector-im/riot-web/compare/v0.11.3...v0.11.4)
@@ -5,7 +170,7 @@ Changes in [0.11.4](https://github.com/vector-im/riot-web/releases/tag/v0.11.4)
* Update matrix-js-sdk and react-sdk to fix a regression where the
background indexedb worker was disabled, failures to open indexeddb
causing the app to fail to start, a race when starting that could break
- switching to rooms, and the inability to to invite user with mixed case
+ switching to rooms, and the inability to invite users with mixed case
usernames.
Changes in [0.11.3](https://github.com/vector-im/riot-web/releases/tag/v0.11.3) (2017-06-20)
diff --git a/README.md b/README.md
index 89f2148f5e..271382030e 100644
--- a/README.md
+++ b/README.md
@@ -22,7 +22,7 @@ released version of Riot:
1. Enter the URL into your browser and log into Riot!
Releases are signed by PGP, and can be checked against the public key
-at https://riot.im/packages/keys/riot-master.asc
+at https://riot.im/packages/keys/riot.asc
Note that Chrome does not allow microphone or webcam access for sites served
over http (except localhost), so for working VoIP you will need to serve Riot
@@ -62,7 +62,7 @@ to build.
1. If you're using the `develop` branch, install the develop versions of the
dependencies, as the released ones will be too old:
```
- scripts/fetch-develop-deps.sh
+ scripts/fetch-develop.deps.sh
```
Whenever you git pull on riot-web you will also probably need to force an update
to these dependencies - the simplest way is to re-run the script, but you can also
@@ -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
@@ -282,19 +281,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).
[
](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
===============
diff --git a/electron_app/package.json b/electron_app/package.json
index 2c6e62f2f5..0a3b092e77 100644
--- a/electron_app/package.json
+++ b/electron_app/package.json
@@ -2,7 +2,7 @@
"name": "riot-web",
"productName": "Riot",
"main": "src/electron-main.js",
- "version": "0.11.4",
+ "version": "0.12.1",
"description": "A feature-rich client for Matrix.org",
"author": "Vector Creations Ltd.",
"dependencies": {
diff --git a/electron_app/src/electron-main.js b/electron_app/src/electron-main.js
index 3491ce0fa3..ce5ac38413 100644
--- a/electron_app/src/electron-main.js
+++ b/electron_app/src/electron-main.js
@@ -29,6 +29,7 @@ const AutoLaunch = require('auto-launch');
const tray = require('./tray');
const vectorMenu = require('./vectormenu');
const webContentsHandler = require('./webcontents-handler');
+const updater = require('./updater');
const windowStateKeeper = require('electron-window-state');
@@ -46,69 +47,9 @@ try {
// Continue with the defaults (ie. an empty config)
}
-const UPDATE_POLL_INTERVAL_MS = 60 * 60 * 1000;
-const INITIAL_UPDATE_DELAY_MS = 30 * 1000;
-
let mainWindow = null;
-let appQuitting = false;
+global.appQuitting = false;
-function installUpdate() {
- // for some reason, quitAndInstall does not fire the
- // before-quit event, so we need to set the flag here.
- appQuitting = true;
- electron.autoUpdater.quitAndInstall();
-}
-
-function pollForUpdates() {
- try {
- electron.autoUpdater.checkForUpdates();
- } catch (e) {
- console.log('Couldn\'t check for update', e);
- }
-}
-
-function startAutoUpdate(updateBaseUrl) {
- if (updateBaseUrl.slice(-1) !== '/') {
- updateBaseUrl = updateBaseUrl + '/';
- }
- try {
- // For reasons best known to Squirrel, the way it checks for updates
- // is completely different between macOS and windows. On macOS, it
- // hits a URL that either gives it a 200 with some json or
- // 204 No Content. On windows it takes a base path and looks for
- // files under that path.
- if (process.platform === 'darwin') {
- // include the current version in the URL we hit. Electron doesn't add
- // it anywhere (apart from the User-Agent) so it's up to us. We could
- // (and previously did) just use the User-Agent, but this doesn't
- // rely on NSURLConnection setting the User-Agent to what we expect,
- // and also acts as a convenient cache-buster to ensure that when the
- // app updates it always gets a fresh value to avoid update-looping.
- electron.autoUpdater.setFeedURL(
- `${updateBaseUrl}macos/?localVersion=${encodeURIComponent(electron.app.getVersion())}`);
-
- } else if (process.platform === 'win32') {
- electron.autoUpdater.setFeedURL(`${updateBaseUrl}win32/${process.arch}/`);
- } else {
- // Squirrel / electron only supports auto-update on these two platforms.
- // I'm not even going to try to guess which feed style they'd use if they
- // implemented it on Linux, or if it would be different again.
- console.log('Auto update not supported on this platform');
- }
- // We check for updates ourselves rather than using 'updater' because we need to
- // do it in the main process (and we don't really need to check every 10 minutes:
- // every hour should be just fine for a desktop app)
- // However, we still let the main window listen for the update events.
- // We also wait a short time before checking for updates the first time because
- // of squirrel on windows and it taking a small amount of time to release a
- // lock file.
- setTimeout(pollForUpdates, INITIAL_UPDATE_DELAY_MS);
- setInterval(pollForUpdates, UPDATE_POLL_INTERVAL_MS);
- } catch (err) {
- // will fail if running in debug mode
- console.log('Couldn\'t enable update checking', err);
- }
-}
// handle uncaught errors otherwise it displays
// stack traces in popup dialogs, which is terrible (which
@@ -120,8 +61,6 @@ process.on('uncaughtException', function(error) {
console.log('Unhandled exception', error);
});
-electron.ipcMain.on('install_update', installUpdate);
-
let focusHandlerAttached = false;
electron.ipcMain.on('setBadgeCount', function(ev, count) {
electron.app.setBadgeCount(count);
@@ -233,7 +172,7 @@ electron.app.on('ready', () => {
if (vectorConfig.update_base_url) {
console.log(`Starting auto update with base URL: ${vectorConfig.update_base_url}`);
- startAutoUpdate(vectorConfig.update_base_url);
+ updater.start(vectorConfig.update_base_url);
} else {
console.log('No update_base_url is defined: auto update is disabled');
}
@@ -246,7 +185,7 @@ electron.app.on('ready', () => {
defaultHeight: 768,
});
- mainWindow = new electron.BrowserWindow({
+ mainWindow = global.mainWindow = new electron.BrowserWindow({
icon: iconPath,
show: false,
autoHideMenuBar: true,
@@ -264,7 +203,7 @@ electron.app.on('ready', () => {
mainWindow.hide();
// Create trayIcon icon
- tray.create(mainWindow, {
+ tray.create({
icon_path: iconPath,
brand: vectorConfig.brand || 'Riot',
});
@@ -276,10 +215,10 @@ electron.app.on('ready', () => {
}
mainWindow.on('closed', () => {
- mainWindow = null;
+ mainWindow = global.mainWindow = null;
});
mainWindow.on('close', (e) => {
- if (!appQuitting && (tray.hasTray() || process.platform === 'darwin')) {
+ if (!global.appQuitting && (tray.hasTray() || process.platform === 'darwin')) {
// On Mac, closing the window just hides it
// (this is generally how single-window Mac apps
// behave, eg. Mail.app)
@@ -289,6 +228,17 @@ electron.app.on('ready', () => {
}
});
+ if (process.platform === 'win32') {
+ // Handle forward/backward mouse buttons in Windows
+ mainWindow.on('app-command', (e, cmd) => {
+ if (cmd === 'browser-backward' && mainWindow.webContents.canGoBack()) {
+ mainWindow.webContents.goBack();
+ } else if (cmd === 'browser-forward' && mainWindow.webContents.canGoForward()) {
+ mainWindow.webContents.goForward();
+ }
+ });
+ }
+
webContentsHandler(mainWindow.webContents);
mainWindowState.manage(mainWindow);
});
@@ -302,7 +252,10 @@ electron.app.on('activate', () => {
});
electron.app.on('before-quit', () => {
- appQuitting = true;
+ global.appQuitting = true;
+ if (mainWindow) {
+ mainWindow.webContents.send('before-quit');
+ }
});
// Set the App User Model ID to match what the squirrel
diff --git a/electron_app/src/tray.js b/electron_app/src/tray.js
index 039e7133fa..bd07d7d433 100644
--- a/electron_app/src/tray.js
+++ b/electron_app/src/tray.js
@@ -26,17 +26,17 @@ exports.hasTray = function hasTray() {
return (trayIcon !== null);
};
-exports.create = function(win, config) {
+exports.create = function(config) {
// no trays on darwin
if (process.platform === 'darwin' || trayIcon) return;
const toggleWin = function() {
- if (win.isVisible() && !win.isMinimized()) {
- win.hide();
+ if (global.mainWindow.isVisible() && !global.mainWindow.isMinimized()) {
+ global.mainWindow.hide();
} else {
- if (win.isMinimized()) win.restore();
- if (!win.isVisible()) win.show();
- win.focus();
+ if (global.mainWindow.isMinimized()) global.mainWindow.restore();
+ if (!global.mainWindow.isVisible()) global.mainWindow.show();
+ global.mainWindow.focus();
}
};
@@ -54,41 +54,46 @@ exports.create = function(win, config) {
},
]);
- trayIcon = new Tray(config.icon_path);
+ const defaultIcon = nativeImage.createFromPath(config.icon_path);
+
+ trayIcon = new Tray(defaultIcon);
trayIcon.setToolTip(config.brand);
trayIcon.setContextMenu(contextMenu);
trayIcon.on('click', toggleWin);
let lastFavicon = null;
- win.webContents.on('page-favicon-updated', async function(ev, favicons) {
- let newFavicon = config.icon_path;
- if (favicons && favicons.length > 0 && favicons[0].startsWith('data:')) {
- newFavicon = favicons[0];
+ global.mainWindow.webContents.on('page-favicon-updated', async function(ev, favicons) {
+ if (!favicons || favicons.length <= 0 || !favicons[0].startsWith('data:')) {
+ if (lastFavicon !== null) {
+ win.setIcon(defaultIcon);
+ trayIcon.setImage(defaultIcon);
+ lastFavicon = null;
+ }
+ return;
}
// No need to change, shortcut
- if (newFavicon === lastFavicon) return;
- lastFavicon = newFavicon;
+ if (favicons[0] === lastFavicon) return;
+ lastFavicon = favicons[0];
- // if its not default we have to construct into nativeImage
- if (newFavicon !== config.icon_path) {
- newFavicon = nativeImage.createFromDataURL(favicons[0]);
+ let newFavicon = nativeImage.createFromDataURL(favicons[0]);
- if (process.platform === 'win32') {
- try {
- const icoPath = path.join(app.getPath('temp'), 'win32_riot_icon.ico')
- const icoBuf = await pngToIco(newFavicon.toPNG());
- fs.writeFileSync(icoPath, icoBuf);
- newFavicon = icoPath;
- } catch (e) {console.error(e);}
+ // Windows likes ico's too much.
+ if (process.platform === 'win32') {
+ try {
+ const icoPath = path.join(app.getPath('temp'), 'win32_riot_icon.ico');
+ fs.writeFileSync(icoPath, await pngToIco(newFavicon.toPNG()));
+ newFavicon = nativeImage.createFromPath(icoPath);
+ } catch (e) {
+ console.error("Failed to make win32 ico", e);
}
}
trayIcon.setImage(newFavicon);
- win.setIcon(newFavicon);
+ global.mainWindow.setIcon(newFavicon);
});
- win.webContents.on('page-title-updated', function(ev, title) {
+ global.mainWindow.webContents.on('page-title-updated', function(ev, title) {
trayIcon.setToolTip(title);
});
};
diff --git a/electron_app/src/updater.js b/electron_app/src/updater.js
new file mode 100644
index 0000000000..49fa4e0419
--- /dev/null
+++ b/electron_app/src/updater.js
@@ -0,0 +1,84 @@
+const { app, autoUpdater, ipcMain } = require('electron');
+
+const UPDATE_POLL_INTERVAL_MS = 60 * 60 * 1000;
+const INITIAL_UPDATE_DELAY_MS = 30 * 1000;
+
+function installUpdate() {
+ // for some reason, quitAndInstall does not fire the
+ // before-quit event, so we need to set the flag here.
+ global.appQuitting = true;
+ autoUpdater.quitAndInstall();
+}
+
+function pollForUpdates() {
+ try {
+ autoUpdater.checkForUpdates();
+ } catch (e) {
+ console.log('Couldn\'t check for update', e);
+ }
+}
+
+module.exports = {};
+module.exports.start = function startAutoUpdate(updateBaseUrl) {
+ if (updateBaseUrl.slice(-1) !== '/') {
+ updateBaseUrl = updateBaseUrl + '/';
+ }
+ try {
+ let url;
+ // For reasons best known to Squirrel, the way it checks for updates
+ // is completely different between macOS and windows. On macOS, it
+ // hits a URL that either gives it a 200 with some json or
+ // 204 No Content. On windows it takes a base path and looks for
+ // files under that path.
+ if (process.platform === 'darwin') {
+ // include the current version in the URL we hit. Electron doesn't add
+ // it anywhere (apart from the User-Agent) so it's up to us. We could
+ // (and previously did) just use the User-Agent, but this doesn't
+ // rely on NSURLConnection setting the User-Agent to what we expect,
+ // and also acts as a convenient cache-buster to ensure that when the
+ // app updates it always gets a fresh value to avoid update-looping.
+ url = `${updateBaseUrl}macos/?localVersion=${encodeURIComponent(app.getVersion())}`;
+
+ } else if (process.platform === 'win32') {
+ url = `${updateBaseUrl}win32/${process.arch}/`;
+ } else {
+ // Squirrel / electron only supports auto-update on these two platforms.
+ // I'm not even going to try to guess which feed style they'd use if they
+ // implemented it on Linux, or if it would be different again.
+ console.log('Auto update not supported on this platform');
+ }
+
+ if (url) {
+ autoUpdater.setFeedURL(url);
+ // We check for updates ourselves rather than using 'updater' because we need to
+ // do it in the main process (and we don't really need to check every 10 minutes:
+ // every hour should be just fine for a desktop app)
+ // However, we still let the main window listen for the update events.
+ // We also wait a short time before checking for updates the first time because
+ // of squirrel on windows and it taking a small amount of time to release a
+ // lock file.
+ setTimeout(pollForUpdates, INITIAL_UPDATE_DELAY_MS);
+ setInterval(pollForUpdates, UPDATE_POLL_INTERVAL_MS);
+ }
+ } catch (err) {
+ // will fail if running in debug mode
+ console.log('Couldn\'t enable update checking', err);
+ }
+}
+
+ipcMain.on('install_update', installUpdate);
+ipcMain.on('check_updates', pollForUpdates);
+
+function ipcChannelSendUpdateStatus(status) {
+ if (global.mainWindow) {
+ global.mainWindow.webContents.send('check_updates', status);
+ }
+}
+
+autoUpdater.on('update-available', function() {
+ ipcChannelSendUpdateStatus(true);
+}).on('update-not-available', function() {
+ ipcChannelSendUpdateStatus(false);
+}).on('error', function(error) {
+ ipcChannelSendUpdateStatus(error.message);
+});
diff --git a/karma.conf.js b/karma.conf.js
index 1e04366313..3b415b1ae6 100644
--- a/karma.conf.js
+++ b/karma.conf.js
@@ -84,13 +84,23 @@ module.exports = function (config) {
// available preprocessors:
// https://npmjs.org/browse/keyword/karma-preprocessor
preprocessors: {
- '{src,test}/**/*.js': ['webpack'],
+ '{src,test}/**/*.js': ['webpack', 'sourcemap'],
},
// test results reporter to use
- // possible values: 'dots', 'progress'
// available reporters: https://npmjs.org/browse/keyword/karma-reporter
- reporters: ['progress', 'junit'],
+ reporters: ['logcapture', 'spec', 'junit', 'summary'],
+
+ specReporter: {
+ suppressErrorSummary: false, // do print error summary
+ suppressFailed: false, // do print information about failed tests
+ suppressPassed: false, // do print information about passed tests
+ showSpecTiming: true, // print the time elapsed for each spec
+ },
+
+ client: {
+ captureLogs: true,
+ },
// web server port
port: 9876,
@@ -113,8 +123,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 f51290061d..44b1dc5948 100644
--- a/package.json
+++ b/package.json
@@ -2,7 +2,7 @@
"name": "riot-web",
"productName": "Riot",
"main": "electron_app/src/electron-main.js",
- "version": "0.11.4",
+ "version": "0.12.1",
"description": "A feature-rich client for Matrix.org",
"author": "Vector Creations Ltd.",
"repository": {
@@ -31,8 +31,8 @@
"build:res": "node scripts/copy-res.js",
"build:modernizr": "modernizr -c .modernizr.json -d src/vector/modernizr.js",
"build:compile": "npm run reskindex && babel --source-maps -d lib src",
- "build:bundle": "cross-env NODE_ENV=production webpack -p --progress",
- "build:bundle:dev": "webpack --optimize-occurence-order --progress",
+ "build:bundle": "cross-env NODE_ENV=production webpack -p --progress --bail",
+ "build:bundle:dev": "webpack --optimize-occurence-order --progress --bail",
"build:electron": "npm run clean && npm run build && npm run install:electron && build -wml --ia32 --x64",
"build": "npm run reskindex && npm run build:res && npm run build:bundle",
"build:dev": "npm run reskindex && npm run build:res && npm run build:bundle:dev",
@@ -48,15 +48,16 @@
"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",
"test-multi": "karma start"
},
"dependencies": {
"babel-polyfill": "^6.5.0",
"babel-runtime": "^6.11.6",
+ "bluebird": "^3.5.0",
"browser-request": "^0.3.3",
"classnames": "^2.1.2",
- "draft-js": "^0.8.1",
+ "draft-js": "^0.11.0-alpha",
"extract-text-webpack-plugin": "^0.9.1",
"favico.js": "^0.3.10",
"filesize": "3.5.6",
@@ -65,15 +66,14 @@
"gfm.css": "^1.1.1",
"highlight.js": "^9.0.0",
"linkifyjs": "^2.1.3",
- "matrix-js-sdk": "0.7.13",
- "matrix-react-sdk": "0.9.7",
+ "matrix-js-sdk": "0.8.1",
+ "matrix-react-sdk": "0.10.1",
"modernizr": "^3.1.0",
"pako": "^1.0.5",
- "q": "^1.4.1",
- "react": "^15.4.0",
+ "react": "^15.6.0",
"react-dnd": "^2.1.4",
"react-dnd-html5-backend": "^2.1.2",
- "react-dom": "^15.4.0",
+ "react-dom": "^15.6.0",
"react-gemini-scrollbar": "matrix-org/react-gemini-scrollbar#5e97aef",
"sanitize-html": "^1.11.1",
"text-encoding-utf-8": "^1.0.1",
@@ -88,7 +88,7 @@
"babel-eslint": "^6.1.0",
"babel-loader": "^6.2.5",
"babel-plugin-add-module-exports": "^0.2.1",
- "babel-plugin-transform-async-to-generator": "^6.16.0",
+ "babel-plugin-transform-async-to-bluebird": "^1.1.1",
"babel-plugin-transform-class-properties": "^6.16.0",
"babel-plugin-transform-object-rest-spread": "^6.16.0",
"babel-plugin-transform-runtime": "^6.15.0",
@@ -114,18 +114,22 @@
"fs-extra": "^0.30.0",
"html-webpack-plugin": "^2.24.0",
"json-loader": "^0.5.3",
- "karma": "^0.13.22",
+ "karma": "^1.7.0",
"karma-chrome-launcher": "^0.2.3",
"karma-cli": "^0.1.2",
"karma-junit-reporter": "^0.4.1",
+ "karma-logcapture-reporter": "0.0.1",
"karma-mocha": "^0.2.2",
- "karma-phantomjs-launcher": "^1.0.0",
+ "karma-sourcemap-loader": "^0.3.7",
+ "karma-spec-reporter": "0.0.31",
+ "karma-summary-reporter": "^1.3.3",
"karma-webpack": "^1.7.0",
+ "matrix-mock-request": "^1.2.0",
+ "matrix-react-test-utils": "^0.2.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",
@@ -135,7 +139,7 @@
"postcss-simple-vars": "^3.0.0",
"postcss-strip-inline-comments": "^0.1.5",
"react-addons-perf": "^15.4.0",
- "react-addons-test-utils": "^15.4.0",
+ "react-addons-test-utils": "^15.6.0",
"rimraf": "^2.4.3",
"source-map-loader": "^0.1.5",
"webpack": "^1.12.14",
diff --git a/release.sh b/release.sh
index 8ae307f7e2..136750181e 100755
--- a/release.sh
+++ b/release.sh
@@ -11,7 +11,7 @@ cd `dirname $0`
for i in matrix-js-sdk matrix-react-sdk
do
- depver=`cat package.json | jq -r .dependencies.\"$i\"`
+ depver=`cat package.json | jq -r .dependencies[\"$i\"]`
latestver=`npm show $i version`
if [ "$depver" != "$latestver" ]
then
diff --git a/scripts/copy-res.js b/scripts/copy-res.js
index e8f6684d21..fa52492e00 100755
--- a/scripts/copy-res.js
+++ b/scripts/copy-res.js
@@ -9,24 +9,27 @@
// This could readily be automated, but it's nice to explicitly
// control when we languages are available.
const INCLUDE_LANGS = [
+ {'value': 'da', 'label': 'Dansk'},
+ {'value': 'de_DE', 'label': 'Deutsch'},
{'value': 'en_EN', 'label': 'English'},
{'value': 'en_US', 'label': 'English (US)'},
- {'value': 'da', 'label': 'Dansk'},
{'value': 'el', 'label': 'Ελληνικά'},
{'value': 'eo', 'label': 'Esperanto'},
- {'value': 'nl', 'label': 'Nederlands'},
- {'value': 'de_DE', 'label': 'Deutsch'},
+ {'value': 'es', 'label': 'Español'},
+ {'value': 'eu', 'label': 'Euskal'},
{'value': 'fr', 'label': 'Français'},
{'value': 'hu', 'label': 'Magyar'},
{'value': 'ko', 'label': '한국어'},
+ {'value': 'lv', 'label': 'Latviešu'},
{'value': 'nb_NO', 'label': 'Norwegian Bokmål'},
+ {'value': 'nl', 'label': 'Nederlands'},
{'value': 'pl', 'label': 'Polski'},
{'value': 'pt', 'label': 'Português'},
{'value': 'pt_BR', 'label': 'Português do Brasil'},
{'value': 'ru', 'label': 'Русский'},
{'value': 'sv', 'label': 'Svenska'},
- {'value': 'es', 'label': 'Español'},
{'value': 'th', 'label': 'ไทย'},
+ {'value': 'te', 'label': 'తెలుగు'},
{'value': 'tr', 'label': 'Türk'},
{'value': 'zh_Hans', 'label': '简体中文'}, // simplified chinese
{'value': 'zh_Hant', 'label': '繁體中文'}, // traditional chinese
diff --git a/scripts/deploy.py b/scripts/deploy.py
index c96b46e81f..cc350e4c9a 100755
--- a/scripts/deploy.py
+++ b/scripts/deploy.py
@@ -63,7 +63,8 @@ class Deployer:
self.packages_path = "."
self.bundles_path = None
self.should_clean = False
- self.config_location = None
+ # filename -> symlink path e.g 'config.localhost.json' => '../localhost/config.json'
+ self.config_locations = {}
self.verify_signature = True
def deploy(self, tarball, extract_path):
@@ -95,11 +96,12 @@ class Deployer:
print ("Extracted into: %s" % extracted_dir)
- if self.config_location:
- create_relative_symlink(
- target=self.config_location,
- linkname=os.path.join(extracted_dir, 'config.json')
- )
+ if self.config_locations:
+ for config_filename, config_loc in self.config_locations.iteritems():
+ create_relative_symlink(
+ target=config_loc,
+ linkname=os.path.join(extracted_dir, config_filename)
+ )
if self.bundles_path:
extracted_bundles = os.path.join(extracted_dir, 'bundles')
@@ -178,6 +180,8 @@ if __name__ == "__main__":
deployer.packages_path = args.packages_dir
deployer.bundles_path = args.bundles_dir
deployer.should_clean = args.clean
- deployer.config_location = args.config
+ deployer.config_locations = {
+ "config.json": args.config,
+ }
deployer.deploy(args.tarball, args.extract_path)
diff --git a/scripts/fetch-develop.deps.sh b/scripts/fetch-develop.deps.sh
index 4fa1a4a22c..e2d40341a0 100755
--- a/scripts/fetch-develop.deps.sh
+++ b/scripts/fetch-develop.deps.sh
@@ -49,42 +49,47 @@ function dodep() {
[ "$curbranch" != 'develop' ] && clone $org $repo develop
} || return $?
- (
- cd $repo
- echo "$repo set to branch "`git rev-parse --abbrev-ref HEAD`
- )
+ echo "$repo set to branch "`git -C "$repo" rev-parse --abbrev-ref HEAD`
mkdir -p node_modules
rm -r "node_modules/$repo" 2>/dev/null || true
ln -sv "../$repo" node_modules/
+
+ (
+ cd $repo
+ npm install
+ )
}
+##############################
+
echo -en 'travis_fold:start:matrix-js-sdk\r'
echo 'Setting up matrix-js-sdk'
dodep matrix-org matrix-js-sdk
-(
- cd node_modules/matrix-js-sdk
- npm install
-)
echo -en 'travis_fold:end:matrix-js-sdk\r'
+##############################
+
echo -en 'travis_fold:start:matrix-react-sdk\r'
echo 'Setting up matrix-react-sdk'
dodep matrix-org matrix-react-sdk
-mkdir -p node_modules/matrix-react-sdk/node_modules
+# replace the version of js-sdk that got pulled into react-sdk with a symlink
+# to our version. Make sure to do this *after* doing 'npm i' in react-sdk,
+# otherwise npm helpfully moves another-json from matrix-js-sdk/node_modules
+# into matrix-react-sdk/node_modules.
+#
+# (note this matches the instructions in the README.)
+rm -r node_modules/matrix-react-sdk/node_modules/matrix-js-sdk
ln -s ../../matrix-js-sdk node_modules/matrix-react-sdk/node_modules/
-(
- cd node_modules/matrix-react-sdk
- npm install
-)
-
echo -en 'travis_fold:end:matrix-react-sdk\r'
+##############################
+
# Link the reskindex binary in place: if we used npm link,
# npm would do this for us, but we don't because we'd have
# to define the npm prefix somewhere so it could put the
diff --git a/scripts/jenkins.sh b/scripts/jenkins.sh
index 4f2e940564..7b5b4c8e2e 100755
--- a/scripts/jenkins.sh
+++ b/scripts/jenkins.sh
@@ -8,8 +8,11 @@ nvm use 6
set -x
-# check out corresponding branches of dependencies
-`dirname $0`/fetch-develop.deps.sh
+# check out corresponding branches of dependencies.
+#
+# clone the deps with depth 1: we know we will only ever need that one
+# commit.
+`dirname $0`/fetch-develop.deps.sh --depth 1
npm install
diff --git a/scripts/redeploy.py b/scripts/redeploy.py
index 598f6c5265..e10a48c008 100755
--- a/scripts/redeploy.py
+++ b/scripts/redeploy.py
@@ -13,6 +13,7 @@
from __future__ import print_function
import json, requests, tarfile, argparse, os, errno
import time
+import traceback
from urlparse import urljoin
from flask import Flask, jsonify, request, abort
@@ -124,6 +125,7 @@ def fetch_jenkins_build(job_name, build_num):
try:
extracted_dir = deploy_tarball(tar_gz_url, build_dir)
except DeployException as e:
+ traceback.print_exc()
abort(400, e.message)
create_symlink(source=extracted_dir, linkname=arg_symlink)
@@ -185,10 +187,16 @@ if __name__ == "__main__":
to the /vector directory INSIDE the tarball."
)
)
+
+ def _raise(ex):
+ raise ex
+
+ # --config config.json=../../config.json --config config.localhost.json=./localhost.json
parser.add_argument(
- "--config", dest="config", help=(
- "Write a symlink to config.json in the extracted tarball. \
- To this location."
+ "--config", action="append", dest="configs",
+ type=lambda kv: kv.split("=", 1) if "=" in kv else _raise(Exception("Missing =")), help=(
+ "A list of configs to symlink into the extracted tarball. \
+ For example, --config config.json=../config.json config2.json=../test/config.json"
)
)
parser.add_argument(
@@ -212,7 +220,8 @@ if __name__ == "__main__":
deployer = Deployer()
deployer.bundles_path = args.bundles_dir
deployer.should_clean = args.clean
- deployer.config_location = args.config
+ deployer.config_locations = dict(args.configs) if args.configs else {}
+
# we don't pgp-sign jenkins artifacts; instead we rely on HTTPS access to
# the jenkins server (and the jenkins server not being compromised and/or
@@ -225,13 +234,13 @@ if __name__ == "__main__":
deploy_tarball(args.tarball_uri, build_dir)
else:
print(
- "Listening on port %s. Extracting to %s%s. Symlinking to %s. Jenkins URL: %s. Config location: %s" %
+ "Listening on port %s. Extracting to %s%s. Symlinking to %s. Jenkins URL: %s. Config locations: %s" %
(args.port,
arg_extract_path,
" (clean after)" if deployer.should_clean else "",
arg_symlink,
arg_jenkins_url,
- deployer.config_location,
+ deployer.config_locations,
)
)
app.run(host="0.0.0.0", port=args.port, debug=True)
diff --git a/src/VectorConferenceHandler.js b/src/VectorConferenceHandler.js
index f34a7b732b..933f59937e 100644
--- a/src/VectorConferenceHandler.js
+++ b/src/VectorConferenceHandler.js
@@ -16,7 +16,7 @@ limitations under the License.
"use strict";
-var q = require("q");
+import Promise from 'bluebird';
var Matrix = require("matrix-js-sdk");
var Room = Matrix.Room;
var CallHandler = require('matrix-react-sdk/lib/CallHandler');
@@ -53,11 +53,11 @@ ConferenceCall.prototype._joinConferenceUser = function() {
// Make sure the conference user is in the group chat room
var groupRoom = this.client.getRoom(this.groupRoomId);
if (!groupRoom) {
- return q.reject("Bad group room ID");
+ return Promise.reject("Bad group room ID");
}
var member = groupRoom.getMember(this.confUserId);
if (member && member.membership === "join") {
- return q();
+ return Promise.resolve();
}
return this.client.invite(this.groupRoomId, this.confUserId);
};
@@ -75,7 +75,7 @@ ConferenceCall.prototype._getConferenceUserRoom = function() {
}
}
if (confRoom) {
- return q(confRoom);
+ return Promise.resolve(confRoom);
}
return this.client.createRoom({
preset: "private_chat",
diff --git a/src/components/structures/HomePage.js b/src/components/structures/HomePage.js
index 2311cc1f30..bdba55eb0e 100644
--- a/src/components/structures/HomePage.js
+++ b/src/components/structures/HomePage.js
@@ -52,6 +52,8 @@ module.exports = React.createClass({
},
componentWillMount: function() {
+ this._unmounted = false;
+
if (this.props.teamToken && this.props.teamServerUrl) {
this.setState({
iframeSrc: `${this.props.teamServerUrl}/static/${this.props.teamToken}/home.html`
@@ -67,9 +69,14 @@ module.exports = React.createClass({
request(
{ method: "GET", url: src },
(err, response, body) => {
+ if (this._unmounted) {
+ return;
+ }
+
if (err || response.status < 200 || response.status >= 300) {
- console.log(err);
- this.setState({ page: "Couldn't load home page" });
+ console.warn(`Error loading home page: ${err}`);
+ this.setState({ page: _t("Couldn't load home page") });
+ return;
}
body = body.replace(/_t\(['"]([\s\S]*?)['"]\)/mg, (match, g1)=>this.translate(g1));
@@ -79,6 +86,10 @@ module.exports = React.createClass({
}
},
+ componentWillUnmount: function() {
+ this._unmounted = true;
+ },
+
render: function() {
if (this.state.iframeSrc) {
return (
diff --git a/src/components/structures/LeftPanel.js b/src/components/structures/LeftPanel.js
index 77338404fa..4539df1ffa 100644
--- a/src/components/structures/LeftPanel.js
+++ b/src/components/structures/LeftPanel.js
@@ -16,17 +16,16 @@ limitations under the License.
'use strict';
-var React = require('react');
-var DragDropContext = require('react-dnd').DragDropContext;
-var HTML5Backend = require('react-dnd-html5-backend');
-var sdk = require('matrix-react-sdk')
-var dis = require('matrix-react-sdk/lib/dispatcher');
+import React from 'react';
+import { DragDropContext } from 'react-dnd';
+import HTML5Backend from 'react-dnd-html5-backend';
+import KeyCode from 'matrix-react-sdk/lib/KeyCode';
+import sdk from 'matrix-react-sdk';
+import dis from 'matrix-react-sdk/lib/dispatcher';
import MatrixClientPeg from 'matrix-react-sdk/lib/MatrixClientPeg';
-
-var VectorConferenceHandler = require('../../VectorConferenceHandler');
-var CallHandler = require("matrix-react-sdk/lib/CallHandler");
-
+import CallHandler from 'matrix-react-sdk/lib/CallHandler';
import AccessibleButton from 'matrix-react-sdk/lib/components/views/elements/AccessibleButton';
+import VectorConferenceHandler from '../../VectorConferenceHandler';
var LeftPanel = React.createClass({
displayName: 'LeftPanel',
@@ -42,6 +41,10 @@ var LeftPanel = React.createClass({
};
},
+ componentWillMount: function() {
+ this.focusedElement = null;
+ },
+
componentDidMount: function() {
this.dispatcherRef = dis.register(this.onAction);
},
@@ -64,6 +67,91 @@ var LeftPanel = React.createClass({
}
},
+ _onFocus: function(ev) {
+ this.focusedElement = ev.target;
+ },
+
+ _onBlur: function(ev) {
+ this.focusedElement = null;
+ },
+
+ _onKeyDown: function(ev) {
+ if (!this.focusedElement) return;
+ let handled = false;
+
+ switch (ev.keyCode) {
+ case KeyCode.UP:
+ this._onMoveFocus(true);
+ handled = true;
+ break;
+ case KeyCode.DOWN:
+ this._onMoveFocus(false);
+ handled = true;
+ break;
+ }
+
+ if (handled) {
+ ev.stopPropagation();
+ ev.preventDefault();
+ }
+ },
+
+ _onMoveFocus: function(up) {
+ var element = this.focusedElement;
+
+ // unclear why this isn't needed
+ // var descending = (up == this.focusDirection) ? this.focusDescending : !this.focusDescending;
+ // this.focusDirection = up;
+
+ var descending = false; // are we currently descending or ascending through the DOM tree?
+ var classes;
+
+ do {
+ var child = up ? element.lastElementChild : element.firstElementChild;
+ var sibling = up ? element.previousElementSibling : element.nextElementSibling;
+
+ if (descending) {
+ if (child) {
+ element = child;
+ }
+ else if (sibling) {
+ element = sibling;
+ }
+ else {
+ descending = false;
+ element = element.parentElement;
+ }
+ }
+ else {
+ if (sibling) {
+ element = sibling;
+ descending = true;
+ }
+ else {
+ element = element.parentElement;
+ }
+ }
+
+ if (element) {
+ classes = element.classList;
+ if (classes.contains("mx_LeftPanel")) { // we hit the top
+ element = up ? element.lastElementChild : element.firstElementChild;
+ descending = true;
+ }
+ }
+
+ } while(element && !(
+ classes.contains("mx_RoomTile") ||
+ classes.contains("mx_SearchBox_search") ||
+ classes.contains("mx_RoomSubList_ellipsis")));
+
+ if (element) {
+ element.focus();
+ this.focusedElement = element;
+ this.focusedDescending = descending;
+ }
+ },
+
_recheckCallElement: function(selectedRoomId) {
// if we aren't viewing a room with an ongoing call, but there is an
// active call, show the call element - we need to do this to make
@@ -126,7 +214,8 @@ var LeftPanel = React.createClass({
}
return (
-