Merge branch 'develop' into luke/rts-welcome-pages

This commit is contained in:
Luke Barnard 2017-02-07 12:41:52 +01:00 committed by GitHub
commit fa552ad5af
20 changed files with 402 additions and 144 deletions

View file

@ -1,3 +1,106 @@
Changes in [0.9.7](https://github.com/vector-im/riot-web/releases/tag/v0.9.7) (2017-02-04)
==========================================================================================
[Full Changelog](https://github.com/vector-im/riot-web/compare/v0.9.7-rc.3...v0.9.7)
* Update to matrix-js-sdk 0.7.5 (no changes from 0.7.5-rc.3)
* Update to matrix-react-sdk 0.8.6 (no changes from 0.8.6-rc.3)
Changes in [0.9.7-rc.3](https://github.com/vector-im/riot-web/releases/tag/v0.9.7-rc.3) (2017-02-03)
====================================================================================================
[Full Changelog](https://github.com/vector-im/riot-web/compare/v0.9.7-rc.2...v0.9.7-rc.3)
* Update to latest Olm to fix key import/export and use of megolm sessions
created on more recent versions
* Update to latest matrix-react-sdk and matrix-js-sdk to fix e2e device
handling
Changes in [0.9.7-rc.2](https://github.com/vector-im/riot-web/releases/tag/v0.9.7-rc.2) (2017-02-03)
====================================================================================================
[Full Changelog](https://github.com/vector-im/riot-web/compare/v0.9.7-rc.1...v0.9.7-rc.2)
* Update matrix-js-sdk to get new device change
notifications interface for more reliable e2e crypto
Changes in [0.9.7-rc.1](https://github.com/vector-im/riot-web/releases/tag/v0.9.7-rc.1) (2017-02-03)
====================================================================================================
[Full Changelog](https://github.com/vector-im/riot-web/compare/v0.9.6...v0.9.7-rc.1)
* Better user interface for screen readers and keyboard navigation
[\#2946](https://github.com/vector-im/riot-web/pull/2946)
* Allow mxc: URLs for icons in the NetworkDropdown
[\#3118](https://github.com/vector-im/riot-web/pull/3118)
* make TopRightMenu more intuitive
[\#3117](https://github.com/vector-im/riot-web/pull/3117)
* Handle icons with width > height
[\#3110](https://github.com/vector-im/riot-web/pull/3110)
* Fix jenkins build
[\#3105](https://github.com/vector-im/riot-web/pull/3105)
* Add CSS for a support box in login
[\#3081](https://github.com/vector-im/riot-web/pull/3081)
* Allow a custom login logo to be displayed on login
[\#3082](https://github.com/vector-im/riot-web/pull/3082)
* Fix the width of input fields within login/reg box
[\#3080](https://github.com/vector-im/riot-web/pull/3080)
* Set BaseAvatar_image bg colour = #fff
[\#3057](https://github.com/vector-im/riot-web/pull/3057)
* only recalculate favicon if it changes
[\#3067](https://github.com/vector-im/riot-web/pull/3067)
* CSS tweak for email address lookup
[\#3064](https://github.com/vector-im/riot-web/pull/3064)
* Glue the dialog to rageshake: honour sendLogs flag.
[\#3061](https://github.com/vector-im/riot-web/pull/3061)
* Don't use hash-named directory for dev server
[\#3049](https://github.com/vector-im/riot-web/pull/3049)
* Implement bug reporting logic
[\#3000](https://github.com/vector-im/riot-web/pull/3000)
* Add css for bug report dialog
[\#3045](https://github.com/vector-im/riot-web/pull/3045)
* Increase the max-height of the expanded status bar
[\#3043](https://github.com/vector-im/riot-web/pull/3043)
* Hopefully, fix intermittent test failure
[\#3040](https://github.com/vector-im/riot-web/pull/3040)
* CSS for 'searching known users'
[\#2971](https://github.com/vector-im/riot-web/pull/2971)
* Animate status bar max-height and margin-top
[\#2981](https://github.com/vector-im/riot-web/pull/2981)
* Add eslint config
[\#3032](https://github.com/vector-im/riot-web/pull/3032)
* Re-position typing avatars relative to "is typing"
[\#3030](https://github.com/vector-im/riot-web/pull/3030)
* CSS for avatars that appear when users are typing
[\#2998](https://github.com/vector-im/riot-web/pull/2998)
* Add StartupWMClass
[\#3001](https://github.com/vector-im/riot-web/pull/3001)
* Fix link to image for event options menu
[\#3002](https://github.com/vector-im/riot-web/pull/3002)
* Make riot desktop single instance
[\#2999](https://github.com/vector-im/riot-web/pull/2999)
* Add electron tray icon
[\#2997](https://github.com/vector-im/riot-web/pull/2997)
* Fixes to electron desktop notifs
[\#2994](https://github.com/vector-im/riot-web/pull/2994)
* Auto-hide the electron menu bar
[\#2975](https://github.com/vector-im/riot-web/pull/2975)
* A couple of tweaks to the karma config
[\#2987](https://github.com/vector-im/riot-web/pull/2987)
* Deploy script
[\#2974](https://github.com/vector-im/riot-web/pull/2974)
* Use the postcss-webpack-loader
[\#2990](https://github.com/vector-im/riot-web/pull/2990)
* Switch CSS to using postcss, and implement a dark theme.
[\#2973](https://github.com/vector-im/riot-web/pull/2973)
* Update redeploy script to keep old bundles
[\#2969](https://github.com/vector-im/riot-web/pull/2969)
* Include current version in update check explicitly
[\#2967](https://github.com/vector-im/riot-web/pull/2967)
* Add another layer of directory to webpack chunks
[\#2966](https://github.com/vector-im/riot-web/pull/2966)
* Fix links to fonts and images from CSS
[\#2965](https://github.com/vector-im/riot-web/pull/2965)
* Put parent build hash in webpack output filenames
[\#2961](https://github.com/vector-im/riot-web/pull/2961)
* update README to point to new names/locations
[\#2846](https://github.com/vector-im/riot-web/pull/2846)
Changes in [0.9.6](https://github.com/vector-im/riot-web/releases/tag/v0.9.6) (2017-01-16) Changes in [0.9.6](https://github.com/vector-im/riot-web/releases/tag/v0.9.6) (2017-01-16)
========================================================================================== ==========================================================================================
[Full Changelog](https://github.com/vector-im/riot-web/compare/v0.9.6-rc.1...v0.9.6) [Full Changelog](https://github.com/vector-im/riot-web/compare/v0.9.6-rc.1...v0.9.6)

View file

@ -2,13 +2,14 @@
var path = require('path'); var path = require('path');
var webpack = require('webpack'); var webpack = require('webpack');
var webpack_config = require('./webpack.config');
/* /*
* We use webpack to build our tests. It's a pain to have to wait for webpack * We use webpack to build our tests. It's a pain to have to wait for webpack
* to build everything; however it's the easiest way to load our dependencies * to build everything; however it's the easiest way to load our dependencies
* from node_modules. * from node_modules.
* *
* If you run karma in multi-run mode (with `npm run test:multi`), it will watch * If you run karma in multi-run mode (with `npm run test-multi`), it will watch
* the tests for changes, and webpack will rebuild using a cache. This is much quicker * the tests for changes, and webpack will rebuild using a cache. This is much quicker
* than a clean rebuild. * than a clean rebuild.
*/ */
@ -19,8 +20,41 @@ var testFile = process.env.KARMA_TEST_FILE || 'test/all-tests.js';
process.env.PHANTOMJS_BIN = 'node_modules/.bin/phantomjs'; process.env.PHANTOMJS_BIN = 'node_modules/.bin/phantomjs';
process.env.Q_DEBUG = 1; process.env.Q_DEBUG = 1;
/* the webpack config is based on the real one, to (a) try to simulate the
* deployed environment as closely as possible, and (b) to avoid a shedload of
* cut-and-paste.
*/
// find out if we're shipping olm, and where it is, if so.
const olm_entry = webpack_config.entry['olm'];
// remove the default entries - karma provides its own (via the 'files' and
// 'preprocessors' config below)
delete webpack_config['entry'];
// add ./test as a search path for js
webpack_config.module.loaders.unshift({
test: /\.js$/, loader: "babel",
include: [path.resolve('./src'), path.resolve('./test')],
});
// disable parsing for sinon, because it
// tries to do voodoo with 'require' which upsets
// webpack (https://github.com/webpack/webpack/issues/304)
webpack_config.module.noParse.push(/sinon\/pkg\/sinon\.js$/);
// ?
webpack_config.resolve.alias['sinon'] = 'sinon/pkg/sinon.js';
webpack_config.resolve.root = [
path.resolve('./src'),
path.resolve('./test'),
];
webpack_config.devtool = 'inline-source-map';
module.exports = function (config) { module.exports = function (config) {
config.set({ const myconfig = {
// frameworks to use // frameworks to use
// available frameworks: https://npmjs.org/browse/keyword/karma-adapter // available frameworks: https://npmjs.org/browse/keyword/karma-adapter
frameworks: ['mocha'], frameworks: ['mocha'],
@ -51,7 +85,7 @@ module.exports = function (config) {
// available preprocessors: // available preprocessors:
// https://npmjs.org/browse/keyword/karma-preprocessor // https://npmjs.org/browse/keyword/karma-preprocessor
preprocessors: { preprocessors: {
'test/**/*.js': ['webpack', 'sourcemap'] '{src,test}/**/*.js': ['webpack'],
}, },
// test results reporter to use // test results reporter to use
@ -94,59 +128,20 @@ module.exports = function (config) {
outputDir: 'karma-reports', outputDir: 'karma-reports',
}, },
webpack: { webpack: webpack_config,
module: {
preLoaders: [
// use the source-map-loader for javascript. This means
// that we have a better chance of seeing line numbers from
// the pre-babeled source.
{ test: /\.js$/, loader: "source-map-loader" },
],
loaders: [
{ test: /\.json$/, loader: "json" },
{
test: /\.js$/, loader: "babel",
include: [path.resolve('./src'),
path.resolve('./test'),
]
},
],
noParse: [
// don't parse the languages within highlight.js. They
// cause stack overflows
// (https://github.com/webpack/webpack/issues/1721), and
// there is no need for webpack to parse them - they can
// just be included as-is.
/highlight\.js\/lib\/languages/,
// also disable parsing for sinon, because it webpackMiddleware: {
// tries to do voodoo with 'require' which upsets stats: {
// webpack (https://github.com/webpack/webpack/issues/304) // don't fill the console up with a mahoosive list of modules
/sinon\/pkg\/sinon\.js$/, chunks: false,
],
}, },
resolve: {
alias: {
// alias any requires to the react module to the one in our path, otherwise
// we tend to get the react source included twice when using npm link.
react: path.resolve('./node_modules/react'),
// same goes for js-sdk
"matrix-js-sdk": path.resolve('./node_modules/matrix-js-sdk'),
sinon: 'sinon/pkg/sinon.js',
},
root: [
path.resolve('./src'),
path.resolve('./test'),
],
},
plugins: [
// olm may not be installed, so avoid webpack warnings by
// ignoring it.
new webpack.IgnorePlugin(/^olm$/),
],
devtool: 'inline-source-map',
}, },
}); };
// include the olm loader if we have it.
if (olm_entry) {
myconfig.files.unshift(olm_entry);
}
config.set(myconfig);
}; };

View file

@ -2,7 +2,7 @@
"name": "riot-web", "name": "riot-web",
"productName": "Riot", "productName": "Riot",
"main": "electron/src/electron-main.js", "main": "electron/src/electron-main.js",
"version": "0.9.6", "version": "0.9.7",
"description": "A feature-rich client for Matrix.org", "description": "A feature-rich client for Matrix.org",
"author": "Vector Creations Ltd.", "author": "Vector Creations Ltd.",
"repository": { "repository": {
@ -46,7 +46,7 @@
"clean": "rimraf lib webapp electron/dist", "clean": "rimraf lib webapp electron/dist",
"prepublish": "npm run build:compile", "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 PhantomJS --colors=false",
"test:multi": "karma start" "test-multi": "karma start"
}, },
"dependencies": { "dependencies": {
"babel-polyfill": "^6.5.0", "babel-polyfill": "^6.5.0",
@ -111,7 +111,6 @@
"karma-junit-reporter": "^0.4.1", "karma-junit-reporter": "^0.4.1",
"karma-mocha": "^0.2.2", "karma-mocha": "^0.2.2",
"karma-phantomjs-launcher": "^1.0.0", "karma-phantomjs-launcher": "^1.0.0",
"karma-sourcemap-loader": "^0.3.7",
"karma-webpack": "^1.7.0", "karma-webpack": "^1.7.0",
"minimist": "^1.2.0", "minimist": "^1.2.0",
"mkdirp": "^0.5.1", "mkdirp": "^0.5.1",
@ -134,7 +133,7 @@
"webpack-dev-server": "^1.16.2" "webpack-dev-server": "^1.16.2"
}, },
"optionalDependencies": { "optionalDependencies": {
"olm": "https://matrix.org/packages/npm/olm/olm-2.1.0.tgz" "olm": "https://matrix.org/packages/npm/olm/olm-2.2.1.tgz"
}, },
"build": { "build": {
"appId": "im.riot.app", "appId": "im.riot.app",

View file

@ -11,13 +11,18 @@ set -x
npm install npm install
# apparently npm 3.10.3 on node 6.4.0 doesn't upgrade #develop target with npm install unless explicitly asked. # apparently npm 3.10.3 on node 6.4.0 doesn't upgrade #develop target with npm install unless explicitly asked.
npm install matrix-react-sdk matrix-js-sdk npm install matrix-react-sdk matrix-js-sdk olm
# install olm. A naive 'npm i ./olm/olm-*.tgz' fails because it uses the url # install olm. A naive 'npm i ./olm/olm-*.tgz' fails because it uses the url
# from our package.json (or even matrix-js-sdk's) in preference. # from our package.json (or even matrix-js-sdk's) in preference.
tar -C olm -xz < olm/olm-*.tgz #
rm -r node_modules/olm # disabled for now, to avoid the annoying scenario of a release doing something
cp -r olm/package node_modules/olm # different to /develop. Instead, add it to the 'npm install' list above.
# -- rav 2016/02/03
#tar -C olm -xz < olm/olm-*.tgz
#rm -r node_modules/olm
#cp -r olm/package node_modules/olm
# we may be using dev branches of js-sdk and react-sdk, in which case we need to build them # we may be using dev branches of js-sdk and react-sdk, in which case we need to build them
(cd node_modules/matrix-js-sdk && npm install) (cd node_modules/matrix-js-sdk && npm install)

111
scripts/rageshake.go Normal file
View file

@ -0,0 +1,111 @@
// Run a web server capable of dumping bug reports sent by Riot.
// Requires Go 1.5+
// Usage: go run rageshake.go PORT
// Example: go run rageshake.go 8080
package main
import (
"bytes"
"compress/gzip"
"encoding/json"
"fmt"
"io/ioutil"
"log"
"net/http"
"os"
"path/filepath"
"strconv"
"time"
)
var maxPayloadSize = 1024 * 1024 * 55 // 55 MB
type LogEntry struct {
ID string `json:"id"`
Lines string `json:"lines"`
}
type Payload struct {
Text string `json:"text"`
Version string `json:"version"`
UserAgent string `json:"user_agent"`
Logs []LogEntry `json:"logs"`
}
func respond(code int, w http.ResponseWriter) {
w.WriteHeader(code)
w.Write([]byte("{}"))
}
func gzipAndSave(data []byte, fpath string) error {
fpath = filepath.Join("bugs", fpath)
if _, err := os.Stat(fpath); err == nil {
return fmt.Errorf("file already exists") // the user can just retry
}
var b bytes.Buffer
gz := gzip.NewWriter(&b)
if _, err := gz.Write(data); err != nil {
return err
}
if err := gz.Flush(); err != nil {
return err
}
if err := gz.Close(); err != nil {
return err
}
if err := ioutil.WriteFile(fpath, b.Bytes(), 0644); err != nil {
return err
}
return nil
}
func main() {
http.HandleFunc("/api/submit", func(w http.ResponseWriter, req *http.Request) {
if req.Method != "POST" && req.Method != "OPTIONS" {
respond(405, w)
return
}
// Set CORS
w.Header().Set("Access-Control-Allow-Origin", "*")
w.Header().Set("Access-Control-Allow-Methods", "POST, OPTIONS")
w.Header().Set("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept")
if req.Method == "OPTIONS" {
respond(200, w)
return
}
if length, err := strconv.Atoi(req.Header.Get("Content-Length")); err != nil || length > maxPayloadSize {
respond(413, w)
return
}
var p Payload
if err := json.NewDecoder(req.Body).Decode(&p); err != nil {
respond(400, w)
return
}
// Dump bug report to disk as form:
// "bugreport-20170115-112233.log.gz" => user text, version, user agent, # logs
// "bugreport-20170115-112233-0.log.gz" => most recent log
// "bugreport-20170115-112233-1.log.gz" => ...
// "bugreport-20170115-112233-N.log.gz" => oldest log
t := time.Now().UTC()
prefix := t.Format("bugreport-20060102-150405")
summary := fmt.Sprintf(
"%s\n\nNumber of logs: %d\nVersion: %s\nUser-Agent: %s\n", p.Text, len(p.Logs), p.Version, p.UserAgent,
)
if err := gzipAndSave([]byte(summary), prefix+".log.gz"); err != nil {
respond(500, w)
return
}
for i, log := range p.Logs {
if err := gzipAndSave([]byte(log.Lines), fmt.Sprintf("%s-%d.log.gz", prefix, i)); err != nil {
respond(500, w)
return // TODO: Rollback?
}
}
respond(200, w)
})
port := os.Args[1]
log.Fatal(http.ListenAndServe(":"+port, nil))
}

View file

@ -20,6 +20,7 @@ var React = require('react');
var ReactDOM = require('react-dom'); var ReactDOM = require('react-dom');
var sdk = require('matrix-react-sdk') var sdk = require('matrix-react-sdk')
var dis = require('matrix-react-sdk/lib/dispatcher'); var dis = require('matrix-react-sdk/lib/dispatcher');
var AccessibleButton = require('matrix-react-sdk/lib/components/views/elements/AccessibleButton');
module.exports = React.createClass({ module.exports = React.createClass({
displayName: 'BottomLeftMenu', displayName: 'BottomLeftMenu',
@ -121,10 +122,10 @@ module.exports = React.createClass({
var homeButton; var homeButton;
if (this.state.teamToken) { if (this.state.teamToken) {
homeButton = ( homeButton = (
<div className="mx_BottomLeftMenu_homePage" onClick={ this.onHomeClick } onMouseEnter={ this.onHomeMouseEnter } onMouseLeave={ this.onHomeMouseLeave } > <AccessibleButton className="mx_BottomLeftMenu_homePage" onClick={ this.onHomeClick } onMouseEnter={ this.onHomeMouseEnter } onMouseLeave={ this.onHomeMouseLeave } >
<TintableSvg src="img/icons-home.svg" width="25" height="25" /> <TintableSvg src="img/icons-home.svg" width="25" height="25" />
{ this.getLabel("Welcome page", this.state.homeHover) } { this.getLabel("Welcome page", this.state.homeHover) }
</div> </AccessibleButton>
); );
} }
@ -132,22 +133,22 @@ module.exports = React.createClass({
<div className="mx_BottomLeftMenu"> <div className="mx_BottomLeftMenu">
<div className="mx_BottomLeftMenu_options"> <div className="mx_BottomLeftMenu_options">
{ homeButton } { homeButton }
<div className="mx_BottomLeftMenu_people" onClick={ this.onPeopleClick } onMouseEnter={ this.onPeopleMouseEnter } onMouseLeave={ this.onPeopleMouseLeave } > <AccessibleButton className="mx_BottomLeftMenu_people" onClick={ this.onPeopleClick } onMouseEnter={ this.onPeopleMouseEnter } onMouseLeave={ this.onPeopleMouseLeave } >
<TintableSvg src="img/icons-people.svg" width="25" height="25" /> <TintableSvg src="img/icons-people.svg" width="25" height="25" />
{ this.getLabel("Start chat", this.state.peopleHover) } { this.getLabel("Start chat", this.state.peopleHover) }
</div> </AccessibleButton>
<div className="mx_BottomLeftMenu_directory" onClick={ this.onDirectoryClick } onMouseEnter={ this.onDirectoryMouseEnter } onMouseLeave={ this.onDirectoryMouseLeave } > <AccessibleButton className="mx_BottomLeftMenu_directory" onClick={ this.onDirectoryClick } onMouseEnter={ this.onDirectoryMouseEnter } onMouseLeave={ this.onDirectoryMouseLeave } >
<TintableSvg src="img/icons-directory.svg" width="25" height="25"/> <TintableSvg src="img/icons-directory.svg" width="25" height="25"/>
{ this.getLabel("Room directory", this.state.directoryHover) } { this.getLabel("Room directory", this.state.directoryHover) }
</div> </AccessibleButton>
<div className="mx_BottomLeftMenu_createRoom" onClick={ this.onRoomsClick } onMouseEnter={ this.onRoomsMouseEnter } onMouseLeave={ this.onRoomsMouseLeave } > <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" /> <TintableSvg src="img/icons-create-room.svg" width="25" height="25" />
{ this.getLabel("Create new room", this.state.roomsHover) } { this.getLabel("Create new room", this.state.roomsHover) }
</div> </AccessibleButton>
<div className="mx_BottomLeftMenu_settings" onClick={ this.onSettingsClick } onMouseEnter={ this.onSettingsMouseEnter } onMouseLeave={ this.onSettingsMouseLeave } > <AccessibleButton className="mx_BottomLeftMenu_settings" onClick={ this.onSettingsClick } onMouseEnter={ this.onSettingsMouseEnter } onMouseLeave={ this.onSettingsMouseLeave } >
<TintableSvg src="img/icons-settings.svg" width="25" height="25" /> <TintableSvg src="img/icons-settings.svg" width="25" height="25" />
{ this.getLabel("Settings", this.state.settingsHover) } { this.getLabel("Settings", this.state.settingsHover) }
</div> </AccessibleButton>
</div> </div>
</div> </div>
); );

View file

@ -23,6 +23,7 @@ var dis = require('matrix-react-sdk/lib/dispatcher');
var MatrixClientPeg = require("matrix-react-sdk/lib/MatrixClientPeg"); var MatrixClientPeg = require("matrix-react-sdk/lib/MatrixClientPeg");
var rate_limited_func = require('matrix-react-sdk/lib/ratelimitedfunc'); var rate_limited_func = require('matrix-react-sdk/lib/ratelimitedfunc');
var Modal = require('matrix-react-sdk/lib/Modal'); var Modal = require('matrix-react-sdk/lib/Modal');
var AccessibleButton = require('matrix-react-sdk/lib/components/views/elements/AccessibleButton');
module.exports = React.createClass({ module.exports = React.createClass({
displayName: 'RightPanel', displayName: 'RightPanel',
@ -69,45 +70,21 @@ module.exports = React.createClass({
}, },
onMemberListButtonClick: function() { onMemberListButtonClick: function() {
if (this.props.collapsed || this.state.phase !== this.Phase.MemberList) { this.setState({ phase: this.Phase.MemberList });
this.setState({ phase: this.Phase.MemberList });
dis.dispatch({
action: 'show_right_panel',
});
}
else {
dis.dispatch({
action: 'hide_right_panel',
});
}
}, },
onFileListButtonClick: function() { onFileListButtonClick: function() {
if (this.props.collapsed || this.state.phase !== this.Phase.FilePanel) { this.setState({ phase: this.Phase.FilePanel });
this.setState({ phase: this.Phase.FilePanel });
dis.dispatch({
action: 'show_right_panel',
});
}
else {
dis.dispatch({
action: 'hide_right_panel',
});
}
}, },
onNotificationListButtonClick: function() { onNotificationListButtonClick: function() {
if (this.props.collapsed || this.state.phase !== this.Phase.NotificationPanel) { this.setState({ phase: this.Phase.NotificationPanel });
this.setState({ phase: this.Phase.NotificationPanel }); },
dis.dispatch({
action: 'show_right_panel', onCollapseClick: function() {
}); dis.dispatch({
} action: 'hide_right_panel',
else { });
dis.dispatch({
action: 'hide_right_panel',
});
}
}, },
onInviteButtonClick: function() { onInviteButtonClick: function() {
@ -207,12 +184,12 @@ module.exports = React.createClass({
if (user_is_in_room) { if (user_is_in_room) {
inviteGroup = inviteGroup =
<div className="mx_RightPanel_invite" onClick={ this.onInviteButtonClick } > <AccessibleButton className="mx_RightPanel_invite" onClick={ this.onInviteButtonClick } >
<div className="mx_RightPanel_icon" > <div className="mx_RightPanel_icon" >
<TintableSvg src="img/icon-invite-people.svg" width="35" height="35" /> <TintableSvg src="img/icon-invite-people.svg" width="35" height="35" />
</div> </div>
<div className="mx_RightPanel_message">Invite to this room</div> <div className="mx_RightPanel_message">Invite to this room</div>
</div>; </AccessibleButton>;
} }
} }
@ -220,20 +197,28 @@ module.exports = React.createClass({
if (this.props.roomId) { if (this.props.roomId) {
buttonGroup = buttonGroup =
<div className="mx_RightPanel_headerButtonGroup"> <div className="mx_RightPanel_headerButtonGroup">
<div className="mx_RightPanel_headerButton" title="Members" onClick={ this.onMemberListButtonClick }> <AccessibleButton className="mx_RightPanel_headerButton"
title="Members" onClick={ this.onMemberListButtonClick }>
<div className="mx_RightPanel_headerButton_badge">{ membersBadge ? membersBadge : <span>&nbsp;</span>}</div> <div className="mx_RightPanel_headerButton_badge">{ membersBadge ? membersBadge : <span>&nbsp;</span>}</div>
<TintableSvg src="img/icons-people.svg" width="25" height="25"/> <TintableSvg src="img/icons-people.svg" width="25" height="25"/>
{ membersHighlight } { membersHighlight }
</div> </AccessibleButton>
<div className="mx_RightPanel_headerButton mx_RightPanel_filebutton" title="Files" onClick={ this.onFileListButtonClick }> <AccessibleButton
className="mx_RightPanel_headerButton mx_RightPanel_filebutton"
title="Files" onClick={ this.onFileListButtonClick }>
<div className="mx_RightPanel_headerButton_badge">&nbsp;</div> <div className="mx_RightPanel_headerButton_badge">&nbsp;</div>
<TintableSvg src="img/icons-files.svg" width="25" height="25"/> <TintableSvg src="img/icons-files.svg" width="25" height="25"/>
{ filesHighlight } { filesHighlight }
</div> </AccessibleButton>
<div className="mx_RightPanel_headerButton mx_RightPanel_notificationbutton" title="Notifications" onClick={ this.onNotificationListButtonClick }> <AccessibleButton
className="mx_RightPanel_headerButton mx_RightPanel_notificationbutton"
title="Notifications" onClick={ this.onNotificationListButtonClick }>
<div className="mx_RightPanel_headerButton_badge">&nbsp;</div> <div className="mx_RightPanel_headerButton_badge">&nbsp;</div>
<TintableSvg src="img/icons-notifications.svg" width="25" height="25"/> <TintableSvg src="img/icons-notifications.svg" width="25" height="25"/>
{ notificationsHighlight } { notificationsHighlight }
</AccessibleButton>
<div className="mx_RightPanel_headerButton mx_RightPanel_collapsebutton" title="Hide panel" onClick={ this.onCollapseClick }>
<TintableSvg src="img/minimise.svg" width="10" height="16"/>
</div> </div>
</div>; </div>;
} }
@ -276,4 +261,3 @@ module.exports = React.createClass({
); );
} }
}); });

View file

@ -26,6 +26,7 @@ var Unread = require('matrix-react-sdk/lib/Unread');
var MatrixClientPeg = require('matrix-react-sdk/lib/MatrixClientPeg'); var MatrixClientPeg = require('matrix-react-sdk/lib/MatrixClientPeg');
var RoomNotifs = require('matrix-react-sdk/lib/RoomNotifs'); var RoomNotifs = require('matrix-react-sdk/lib/RoomNotifs');
var FormattingUtils = require('matrix-react-sdk/lib/utils/FormattingUtils'); var FormattingUtils = require('matrix-react-sdk/lib/utils/FormattingUtils');
var AccessibleButton = require('matrix-react-sdk/lib/components/views/elements/AccessibleButton');
// turn this on for drop & drag console debugging galore // turn this on for drop & drag console debugging galore
var debug = false; var debug = false;
@ -417,15 +418,17 @@ var RoomSubList = React.createClass({
} }
} }
var tabindex = this.props.searchFilter === "" ? "0" : "-1";
return ( return (
<div className="mx_RoomSubList_labelContainer" title={ title } ref="header"> <div className="mx_RoomSubList_labelContainer" title={ title } ref="header">
<div onClick={ this.onClick } className="mx_RoomSubList_label"> <AccessibleButton onClick={ this.onClick } className="mx_RoomSubList_label" tabIndex={tabindex}>
{ this.props.collapsed ? '' : this.props.label } { this.props.collapsed ? '' : this.props.label }
<div className="mx_RoomSubList_roomCount">{ roomCount }</div> <div className="mx_RoomSubList_roomCount">{ roomCount }</div>
<div className={chevronClasses}></div> <div className={chevronClasses}></div>
{ badge } { badge }
{ incomingCall } { incomingCall }
</div> </AccessibleButton>
</div> </div>
); );
}, },
@ -447,11 +450,11 @@ var RoomSubList = React.createClass({
}); });
return ( return (
<div className="mx_RoomSubList_ellipsis" onClick={this._showFullMemberList}> <AccessibleButton className="mx_RoomSubList_ellipsis" onClick={this._showFullMemberList}>
<div className="mx_RoomSubList_line"></div> <div className="mx_RoomSubList_line"></div>
<div className="mx_RoomSubList_more">more</div> <div className="mx_RoomSubList_more">more</div>
<div className={ badgeClasses }>{ content }</div> <div className={ badgeClasses }>{ content }</div>
</div> </AccessibleButton>
); );
}, },

View file

@ -20,6 +20,7 @@ var React = require('react');
var sdk = require('matrix-react-sdk') var sdk = require('matrix-react-sdk')
var dis = require('matrix-react-sdk/lib/dispatcher'); var dis = require('matrix-react-sdk/lib/dispatcher');
var rate_limited_func = require('matrix-react-sdk/lib/ratelimitedfunc'); var rate_limited_func = require('matrix-react-sdk/lib/ratelimitedfunc');
var AccessibleButton = require('matrix-react-sdk/lib/components/views/elements/AccessibleButton');
module.exports = React.createClass({ module.exports = React.createClass({
displayName: 'SearchBox', displayName: 'SearchBox',
@ -35,6 +36,25 @@ module.exports = React.createClass({
}; };
}, },
componentDidMount: function() {
this.dispatcherRef = dis.register(this.onAction);
},
componentWillUnmount: function() {
dis.unregister(this.dispatcherRef);
},
onAction: function(payload) {
switch (payload.action) {
// Clear up the text field when a room is selected.
case 'view_room':
if (this.refs.search) {
this._clearSearch();
}
break;
}
},
onChange: function() { onChange: function() {
if (!this.refs.search) return; if (!this.refs.search) return;
this.setState({ searchTerm: this.refs.search.value }); this.setState({ searchTerm: this.refs.search.value });
@ -61,35 +81,42 @@ module.exports = React.createClass({
} }
}, },
_clearSearch: function() {
this.refs.search.value = "";
this.onChange();
},
render: function() { render: function() {
var TintableSvg = sdk.getComponent('elements.TintableSvg'); var TintableSvg = sdk.getComponent('elements.TintableSvg');
var collapseTabIndex = this.refs.search && this.refs.search.value !== "" ? "-1" : "0";
var toggleCollapse; var toggleCollapse;
if (this.props.collapsed) { if (this.props.collapsed) {
toggleCollapse = toggleCollapse =
<div className="mx_SearchBox_maximise" onClick={ this.onToggleCollapse.bind(this, true) }> <AccessibleButton className="mx_SearchBox_maximise" tabIndex={collapseTabIndex} onClick={ this.onToggleCollapse.bind(this, true) }>
<TintableSvg src="img/maximise.svg" width="10" height="16" alt="&lt;"/> <TintableSvg src="img/maximise.svg" width="10" height="16" alt="Expand panel"/>
</div> </AccessibleButton>
} }
else { else {
toggleCollapse = toggleCollapse =
<div className="mx_SearchBox_minimise" onClick={ this.onToggleCollapse.bind(this, false) }> <AccessibleButton className="mx_SearchBox_minimise" tabIndex={collapseTabIndex} onClick={ this.onToggleCollapse.bind(this, false) }>
<TintableSvg src="img/minimise.svg" width="10" height="16" alt="&lt;"/> <TintableSvg src="img/minimise.svg" width="10" height="16" alt="Collapse panel"/>
</div> </AccessibleButton>
} }
var searchControls; var searchControls;
if (!this.props.collapsed) { if (!this.props.collapsed) {
searchControls = [ searchControls = [
this.state.searchTerm.length > 0 ? this.state.searchTerm.length > 0 ?
<div key="button" <AccessibleButton key="button"
className="mx_SearchBox_closeButton" className="mx_SearchBox_closeButton"
onClick={ ()=>{ this.refs.search.value = ""; this.onChange(); } }> onClick={ ()=>{ this._clearSearch(); } }>
<TintableSvg <TintableSvg
className="mx_SearchBox_searchButton" className="mx_SearchBox_searchButton"
src="img/icons-close.svg" width="24" height="24" src="img/icons-close.svg" width="24" height="24"
/> />
</div> </AccessibleButton>
: :
<TintableSvg <TintableSvg
key="button" key="button"

View file

@ -181,7 +181,10 @@ export default class NetworkDropdown extends React.Component {
span_class = 'mx_NetworkDropdown_menu_network'; span_class = 'mx_NetworkDropdown_menu_network';
} else { } else {
key = server + '_inst_' + instance.instance_id; key = server + '_inst_' + instance.instance_id;
icon = <img src={instance.icon || DEFAULT_ICON_URL} />; const imgUrl = instance.icon ?
MatrixClientPeg.get().mxcUrlToHttp(instance.icon, 25, 25, 'crop', true) :
DEFAULT_ICON_URL;
icon = <img src={imgUrl} />;
name = instance.desc; name = instance.desc;
span_class = 'mx_NetworkDropdown_menu_network'; span_class = 'mx_NetworkDropdown_menu_network';
} }

View file

@ -22,6 +22,7 @@ var MatrixClientPeg = require('matrix-react-sdk/lib/MatrixClientPeg');
var DateUtils = require('matrix-react-sdk/lib/DateUtils'); var DateUtils = require('matrix-react-sdk/lib/DateUtils');
var filesize = require('filesize'); var filesize = require('filesize');
var AccessibleButton = require('matrix-react-sdk/lib/components/views/elements/AccessibleButton');
module.exports = React.createClass({ module.exports = React.createClass({
displayName: 'ImageView', displayName: 'ImageView',
@ -162,7 +163,7 @@ module.exports = React.createClass({
<img src={this.props.src} style={style}/> <img src={this.props.src} style={style}/>
<div className="mx_ImageView_labelWrapper"> <div className="mx_ImageView_labelWrapper">
<div className="mx_ImageView_label"> <div className="mx_ImageView_label">
<img className="mx_ImageView_cancel" src="img/cancel-white.svg" width="18" height="18" alt="Close" onClick={ this.props.onFinished }/> <AccessibleButton className="mx_ImageView_cancel" onClick={ this.props.onFinished }><img src="img/cancel-white.svg" width="18" height="18" alt="Close"/></AccessibleButton>
<div className="mx_ImageView_shim"> <div className="mx_ImageView_shim">
</div> </div>
<div className="mx_ImageView_name"> <div className="mx_ImageView_name">

View file

@ -19,6 +19,7 @@ limitations under the License.
var React = require('react'); var React = require('react');
var Notifier = require("matrix-react-sdk/lib/Notifier"); var Notifier = require("matrix-react-sdk/lib/Notifier");
var sdk = require('matrix-react-sdk') var sdk = require('matrix-react-sdk')
var AccessibleButton = require('matrix-react-sdk/lib/components/views/elements/AccessibleButton');
module.exports = React.createClass({ module.exports = React.createClass({
displayName: 'MatrixToolbar', displayName: 'MatrixToolbar',
@ -38,7 +39,7 @@ module.exports = React.createClass({
<div className="mx_MatrixToolbar_content"> <div className="mx_MatrixToolbar_content">
You are not receiving desktop notifications. <a className="mx_MatrixToolbar_link" onClick={ this.onClick }>Enable them now</a> You are not receiving desktop notifications. <a className="mx_MatrixToolbar_link" onClick={ this.onClick }>Enable them now</a>
</div> </div>
<div className="mx_MatrixToolbar_close"><img src="img/cancel.svg" width="18" height="18" onClick={ this.hideToolbar } /></div> <AccessibleButton className="mx_MatrixToolbar_close" onClick={ this.hideToolbar } ><img src="img/cancel.svg" width="18" height="18" /></AccessibleButton>
</div> </div>
); );
} }

View file

@ -20,6 +20,7 @@ var React = require('react');
var MatrixClientPeg = require('matrix-react-sdk/lib/MatrixClientPeg'); var MatrixClientPeg = require('matrix-react-sdk/lib/MatrixClientPeg');
var sdk = require('matrix-react-sdk'); var sdk = require('matrix-react-sdk');
var classNames = require('classnames'); var classNames = require('classnames');
var AccessibleButton = require('matrix-react-sdk/lib/components/views/elements/AccessibleButton');
module.exports = React.createClass({ module.exports = React.createClass({
displayName: 'SearchBar', displayName: 'SearchBar',
@ -57,12 +58,12 @@ module.exports = React.createClass({
var allRoomsClasses = classNames({ mx_SearchBar_button : true, mx_SearchBar_unselected : this.state.scope !== 'All' }); var allRoomsClasses = classNames({ mx_SearchBar_button : true, mx_SearchBar_unselected : this.state.scope !== 'All' });
return ( return (
<div className="mx_SearchBar"> <div className="mx_SearchBar">
<input ref="search_term" className="mx_SearchBar_input" type="text" autoFocus={true} placeholder="Search..." onKeyDown={this.onSearchChange}/> <input ref="search_term" className="mx_SearchBar_input" type="text" autoFocus={true} placeholder="Search..." onKeyDown={this.onSearchChange}/>
<div className={ searchButtonClasses } onClick={this.onSearch}><img src="img/search-button.svg" width="37" height="37" alt="Search"/></div> <AccessibleButton className={ searchButtonClasses } onClick={this.onSearch}><img src="img/search-button.svg" width="37" height="37" alt="Search"/></AccessibleButton>
<div className={ thisRoomClasses } onClick={this.onThisRoomClick}>This Room</div> <AccessibleButton className={ thisRoomClasses } onClick={this.onThisRoomClick}>This Room</AccessibleButton>
<div className={ allRoomsClasses } onClick={this.onAllRoomsClick}>All Rooms</div> <AccessibleButton className={ allRoomsClasses } onClick={this.onAllRoomsClick}>All Rooms</AccessibleButton>
<img className="mx_SearchBar_cancel" src="img/cancel.svg" width="18" height="18" onClick={this.props.onCancelClick} /> <AccessibleButton className="mx_SearchBar_cancel" onClick={this.props.onCancelClick}><img src="img/cancel.svg" width="18" height="18" /></AccessibleButton>
</div> </div>
); );
} }

View file

@ -107,6 +107,16 @@ limitations under the License.
justify-content: flex-start; justify-content: flex-start;
} }
.mx_RoomView_empty {
flex: 1 1 auto;
font-size: 13px;
padding-left: 3em;
padding-right: 3em;
margin-right: 20px;
margin-top: 33%;
text-align: center;
}
.mx_RoomView_MessageList { .mx_RoomView_MessageList {
width: 100%; width: 100%;
list-style-type: none; list-style-type: none;

View file

@ -46,7 +46,7 @@ limitations under the License.
} }
.mx_Login_logo img { .mx_Login_logo img {
height: 100% max-height: 100%
} }
.mx_Login_support { .mx_Login_support {

View file

@ -54,7 +54,7 @@ limitations under the License.
margin-right: 8px; margin-right: 8px;
padding-left: 0.5em; padding-left: 0.5em;
padding-right: 0.5em; padding-right: 0.5em;
width: 70px; width: 85px;
outline: none; outline: none;
cursor: pointer; cursor: pointer;
color: $accent-color; color: $accent-color;

View file

@ -34,14 +34,15 @@ limitations under the License.
.mx_RightPanel_headerButtonGroup { .mx_RightPanel_headerButtonGroup {
margin-top: 6px; margin-top: 6px;
float: left; display: flex;
width: 100%;
background-color: $primary-bg-color; background-color: $primary-bg-color;
margin-left: 0px; margin-left: 0px;
} }
.mx_RightPanel_headerButton { .mx_RightPanel_headerButton {
cursor: pointer; cursor: pointer;
display: table-cell; flex: 0;
vertical-align: top; vertical-align: top;
padding-left: 4px; padding-left: 4px;
padding-right: 5px; padding-right: 5px;
@ -69,6 +70,12 @@ limitations under the License.
padding-bottom: 2px; padding-bottom: 2px;
} }
.mx_RightPanel_collapsebutton {
flex: 1;
text-align: right;
margin-top: 20px;
}
.mx_RightPanel .mx_MemberList, .mx_RightPanel .mx_MemberList,
.mx_RightPanel .mx_MemberInfo, .mx_RightPanel .mx_MemberInfo,
.mx_RightPanel_blank { .mx_RightPanel_blank {

View file

@ -3,7 +3,7 @@
<svg version="1.1" id="Layer_1" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns" <svg version="1.1" id="Layer_1" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns"
xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="-11 13 24 24" xml:space="preserve"> xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="-11 13 24 24" xml:space="preserve">
<style type="text/css"> <style type="text/css">
.st1{fill:none;stroke:#FFFFFF;stroke-width:2;stroke-linecap:round;} .st1{fill:none;stroke-width:2;stroke-linecap:round;}
</style> </style>
<title>icons_create_room</title> <title>icons_create_room</title>
<desc>Created with sketchtool.</desc> <desc>Created with sketchtool.</desc>
@ -13,8 +13,8 @@
<g id="Room-list_x2F_Footer" transform="translate(0.000000, 708.000000)" sketch:type="MSShapeGroup"> <g id="Room-list_x2F_Footer" transform="translate(0.000000, 708.000000)" sketch:type="MSShapeGroup">
<g id="icons_create_room" transform="translate(20.000000, 18.000000)"> <g id="icons_create_room" transform="translate(20.000000, 18.000000)">
<circle id="Oval-1-Copy-7" fill="#76CFA6" cx="1" cy="25" r="12"/> <circle id="Oval-1-Copy-7" fill="#76CFA6" cx="1" cy="25" r="12"/>
<path id="Line" class="st1" d="M-2.5,28.5l7.1-7.1"/> <path id="Line" class="st1" stroke="#FFFFFF" d="M-2.5,28.5l7.1-7.1"/>
<path id="Line_1_" class="st1" d="M-2.5,21.5l7.1,7.1"/> <path id="Line_1_" class="st1" stroke="#FFFFFF" d="M-2.5,21.5l7.1,7.1"/>
</g> </g>
</g> </g>
</g> </g>

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

View file

@ -193,6 +193,7 @@ describe('loading:', function () {
expect(req.data.password).toEqual('pass'); expect(req.data.password).toEqual('pass');
}).respond(200, { }).respond(200, {
user_id: '@user:id', user_id: '@user:id',
device_id: 'DEVICE_ID',
access_token: 'access_token', access_token: 'access_token',
}); });
login.onPasswordLogin("user", "pass") login.onPasswordLogin("user", "pass")
@ -393,6 +394,7 @@ describe('loading:', function () {
expect(req.data.token).toEqual("secretToken"); expect(req.data.token).toEqual("secretToken");
}).respond(200, { }).respond(200, {
user_id: "@user:localhost", user_id: "@user:localhost",
device_id: 'DEVICE_ID',
access_token: "access_token", access_token: "access_token",
}); });

View file

@ -132,6 +132,11 @@ module.exports = {
devServer: { devServer: {
// serve unwebpacked assets from webapp. // serve unwebpacked assets from webapp.
contentBase: './webapp', contentBase: './webapp',
stats: {
// don't fill the console up with a mahoosive list of modules
chunks: false,
},
}, },
}; };