Merge branch 'develop' into matthew/preview_urls

This commit is contained in:
Matthew Hodgson 2016-04-03 01:10:33 +01:00
commit f195d2eb24
6 changed files with 70 additions and 25 deletions

View file

@ -99,7 +99,10 @@ module.exports = function (config) {
path.resolve('./test'), path.resolve('./test'),
], ],
query: { query: {
presets: ['react', 'es2015'] // we're using react 5, for consistency with
// the release build, which doesn't use the
// presets.
// presets: ['react', 'es2015'],
}, },
}, },
], ],

View file

@ -30,7 +30,7 @@
"highlight.js": "^8.9.1", "highlight.js": "^8.9.1",
"linkifyjs": "^2.0.0-beta.4", "linkifyjs": "^2.0.0-beta.4",
"marked": "^0.3.5", "marked": "^0.3.5",
"matrix-js-sdk": "^0.5.1", "matrix-js-sdk": "matrix-org/matrix-js-sdk#develop",
"optimist": "^0.6.1", "optimist": "^0.6.1",
"q": "^1.4.1", "q": "^1.4.1",
"react": "^0.14.2", "react": "^0.14.2",
@ -40,13 +40,15 @@
"velocity-animate": "^1.2.3", "velocity-animate": "^1.2.3",
"velocity-ui-pack": "^1.2.2" "velocity-ui-pack": "^1.2.2"
}, },
"//babelversion": [
"brief experiments with babel6 seems to show that it generates source ",
"maps which confuse chrome and make setting breakpoints tricky. So ",
"let's stick with v5 for now."
],
"devDependencies": { "devDependencies": {
"babel": "^5.8.23", "babel": "^5.8.23",
"babel-core": "^6.7.4", "babel-core": "^5.8.38",
"babel-loader": "^6.2.4", "babel-loader": "^5.4.0",
"babel-preset-es2015": "^6.6.0",
"babel-preset-react": "^6.5.0",
"babel-runtime": "^6.6.1",
"expect": "^1.16.0", "expect": "^1.16.0",
"json-loader": "^0.5.3", "json-loader": "^0.5.3",
"karma": "^0.13.22", "karma": "^0.13.22",

View file

@ -27,7 +27,7 @@ var sanitizeHtmlParams = {
'del', // for markdown 'del', // for markdown
// deliberately no h1/h2 to stop people shouting. // deliberately no h1/h2 to stop people shouting.
'h3', 'h4', 'h5', 'h6', 'blockquote', 'p', 'a', 'ul', 'ol', 'h3', 'h4', 'h5', 'h6', 'blockquote', 'p', 'a', 'ul', 'ol',
'nl', 'li', 'b', 'i', 'strong', 'em', 'strike', 'code', 'hr', 'br', 'div', 'nl', 'li', 'b', 'i', 'u', 'strong', 'em', 'strike', 'code', 'hr', 'br', 'div',
'table', 'thead', 'caption', 'tbody', 'tr', 'th', 'td', 'pre' 'table', 'thead', 'caption', 'tbody', 'tr', 'th', 'td', 'pre'
], ],
allowedAttributes: { allowedAttributes: {

View file

@ -448,6 +448,20 @@ module.exports = React.createClass({
}); });
}, },
onMemberAvatarClick: function () {
var avatarUrl = this.props.member.user.avatarUrl;
if(!avatarUrl) return;
var httpUrl = MatrixClientPeg.get().mxcUrlToHttp(avatarUrl);
var ImageView = sdk.getComponent("elements.ImageView");
var params = {
src: httpUrl,
name: this.props.member.name
};
Modal.createDialog(ImageView, params, "mx_Dialog_lightbox");
},
render: function() { render: function() {
var startChat, kickButton, banButton, muteButton, giveModButton, spinner; var startChat, kickButton, banButton, muteButton, giveModButton, spinner;
if (this.props.member.userId !== MatrixClientPeg.get().credentials.userId) { if (this.props.member.userId !== MatrixClientPeg.get().credentials.userId) {
@ -508,7 +522,7 @@ module.exports = React.createClass({
<div className="mx_MemberInfo"> <div className="mx_MemberInfo">
<img className="mx_MemberInfo_cancel" src="img/cancel.svg" width="18" height="18" onClick={this.onCancel}/> <img className="mx_MemberInfo_cancel" src="img/cancel.svg" width="18" height="18" onClick={this.onCancel}/>
<div className="mx_MemberInfo_avatar"> <div className="mx_MemberInfo_avatar">
<MemberAvatar member={this.props.member} width={48} height={48} /> <MemberAvatar onClick={this.onMemberAvatarClick} member={this.props.member} width={48} height={48} />
</div> </div>
<h2>{ this.props.member.name }</h2> <h2>{ this.props.member.name }</h2>

View file

@ -17,6 +17,7 @@ var React = require('react');
var CallHandler = require('../../../CallHandler'); var CallHandler = require('../../../CallHandler');
var MatrixClientPeg = require('../../../MatrixClientPeg'); var MatrixClientPeg = require('../../../MatrixClientPeg');
var Modal = require('../../../Modal');
var sdk = require('../../../index'); var sdk = require('../../../index');
var dis = require('../../../dispatcher'); var dis = require('../../../dispatcher');
@ -47,11 +48,40 @@ module.exports = React.createClass({
onUploadFileSelected: function(ev) { onUploadFileSelected: function(ev) {
var files = ev.target.files; var files = ev.target.files;
// MessageComposer shouldn't have to rely on its parent passing in a callback to upload a file
if (files && files.length > 0) { var QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
this.props.uploadFile(files[0]); var TintableSvg = sdk.getComponent("elements.TintableSvg");
var fileList = [];
for(var i=0; i<files.length; i++) {
fileList.push(<li>
<TintableSvg src="img/files.svg" width="16" height="16" /> {files[i].name}
</li>);
} }
this.refs.uploadInput.value = null;
Modal.createDialog(QuestionDialog, {
title: "Upload Files",
description: (
<div>
<p>Are you sure you want upload the following files?</p>
<ul style={{listStyle: 'none', textAlign: 'left'}}>
{fileList}
</ul>
</div>
),
onFinished: (shouldUpload) => {
if(shouldUpload) {
// MessageComposer shouldn't have to rely on its parent passing in a callback to upload a file
if (files) {
for(var i=0; i<files.length; i++) {
this.props.uploadFile(files[i]);
}
}
}
this.refs.uploadInput.value = null;
}
});
}, },
onHangupClick: function() { onHangupClick: function() {
@ -130,6 +160,7 @@ module.exports = React.createClass({
<TintableSvg src="img/upload.svg" width="19" height="24"/> <TintableSvg src="img/upload.svg" width="19" height="24"/>
<input ref="uploadInput" type="file" <input ref="uploadInput" type="file"
style={uploadInputStyle} style={uploadInputStyle}
multiple
onChange={this.onUploadFileSelected} /> onChange={this.onUploadFileSelected} />
</div> </div>
); );

View file

@ -91,11 +91,13 @@ describe('MessagePanel', function () {
var tiles = TestUtils.scryRenderedComponentsWithType( var tiles = TestUtils.scryRenderedComponentsWithType(
mp, sdk.getComponent('rooms.EventTile')); mp, sdk.getComponent('rooms.EventTile'));
var tileContainers = tiles.map(function (t) {
return ReactDOM.findDOMNode(t).parentNode;
});
// find the <li> which wraps the read marker // find the <li> which wraps the read marker
var rm = TestUtils.findRenderedDOMComponentWithClass(mp, 'mx_RoomView_myReadMarker_container'); var rm = TestUtils.findRenderedDOMComponentWithClass(mp, 'mx_RoomView_myReadMarker_container');
var eventContainer = ReactDOM.findDOMNode(tiles[4]).parentNode; expect(rm.previousSibling).toEqual(tileContainers[4]);
expect(rm.previousSibling).toEqual(eventContainer);
// now move the RM // now move the RM
mp = ReactDOM.render( mp = ReactDOM.render(
@ -108,18 +110,11 @@ describe('MessagePanel', function () {
expect(found.length).toEqual(2); expect(found.length).toEqual(2);
// the first should be the ghost // the first should be the ghost
var ghost = found[0]; expect(found[0].previousSibling).toEqual(tileContainers[4]);
eventContainer = ReactDOM.findDOMNode(tiles[4]).parentNode; var hr = found[0].children[0];
expect(ghost.previousSibling).toEqual(eventContainer);
var hr = ghost.children[0];
// the first should be the ghost
eventContainer = ReactDOM.findDOMNode(tiles[4]).parentNode;
expect(found[0].previousSibling).toEqual(eventContainer);
// the second should be the real thing // the second should be the real thing
eventContainer = ReactDOM.findDOMNode(tiles[4]).parentNode; expect(found[1].previousSibling).toEqual(tileContainers[6]);
expect(ghost.previousSibling).toEqual(eventContainer);
// advance the clock, and then let the browser run an animation frame, // advance the clock, and then let the browser run an animation frame,
// to let the animation start // to let the animation start