Merge branch 'develop' into wmwragg/direct-chat-sublist

This commit is contained in:
wmwragg 2016-08-30 11:22:31 +01:00
commit 7b7a77bad0
19 changed files with 334 additions and 85 deletions

6
.travis.yml Normal file
View File

@ -0,0 +1,6 @@
language: node_js
node_js:
- 6 # node v6, to match jenkins
install:
- npm install
- (cd node_modules/matrix-react-sdk && npm run build)

View File

@ -1,3 +1,40 @@
Changes in [0.7.5-r1](https://github.com/vector-im/vector-web/releases/tag/v0.7.5-r1) (2016-08-28)
==================================================================================================
[Full Changelog](https://github.com/vector-im/vector-web/compare/v0.7.5...v0.7.5-r1)
* Correctly pin deps :(
Changes in [0.7.5](https://github.com/vector-im/vector-web/releases/tag/v0.7.5) (2016-08-28)
============================================================================================
[Full Changelog](https://github.com/vector-im/vector-web/compare/v0.7.4-r1...v0.7.5)
* re-add leave button in RoomSettings
* add /user URLs
* recognise matrix.to links and other vector links
* fix linkify dependency
* fix avatar clicking in MemberInfo
* fix RoomTagContextMenu so it works on historical rooms
* warn people to put their Matrix HS on a separate domain to Vector
* fix zalgos again
* Add .travis.yml
[\#2007](https://github.com/vector-im/vector-web/pull/2007)
* add fancy changelog dialog
[\#1972](https://github.com/vector-im/vector-web/pull/1972)
* Update autocomplete design
[\#1978](https://github.com/vector-im/vector-web/pull/1978)
* Update encryption info in README
[\#2001](https://github.com/vector-im/vector-web/pull/2001)
* Added event/info message avatars back in
[\#2000](https://github.com/vector-im/vector-web/pull/2000)
* Wmwragg/chat message presentation
[\#1987](https://github.com/vector-im/vector-web/pull/1987)
* Make the notification slider work
[\#1982](https://github.com/vector-im/vector-web/pull/1982)
* Use cpx to copy olm.js, and add watcher
[\#1966](https://github.com/vector-im/vector-web/pull/1966)
* Make up a device display name
[\#1959](https://github.com/vector-im/vector-web/pull/1959)
Changes in [0.7.4-r1](https://github.com/vector-im/vector-web/releases/tag/v0.7.4-r1) (2016-08-12) Changes in [0.7.4-r1](https://github.com/vector-im/vector-web/releases/tag/v0.7.4-r1) (2016-08-12)
================================================================================================== ==================================================================================================
[Full Changelog](https://github.com/vector-im/vector-web/compare/v0.7.4...v0.7.4-r1) [Full Changelog](https://github.com/vector-im/vector-web/compare/v0.7.4...v0.7.4-r1)

View File

@ -20,6 +20,19 @@ of Vector:
as desired. See below for details. as desired. See below for details.
1. Enter the URL into your browser and log into vector! 1. Enter the URL into your browser and log into vector!
Important Security Note
=======================
We do not recommend running Vector from the same domain name as your Matrix
homeserver. The reason is the risk of XSS (cross-site-scripting) vulnerabilities
that could occur if someone caused Vector to load and render malicious user generated
content from a Matrix API which then had trusted access to Vector (or other apps) due
to sharing the same domain.
We have put some coarse mitigations into place to try to protect against this situation,
but it's still not good practice to do it in the first place.
See https://github.com/vector-im/vector-web/issues/1977 for more details.
Building From Source Building From Source
==================== ====================
@ -75,7 +88,9 @@ nativefier https://vector.im/beta/
``` ```
krisa has a dedicated electron project at https://github.com/krisak/vector-electron-desktop krisa has a dedicated electron project at https://github.com/krisak/vector-electron-desktop
(although you should swap out the 'vector' folder for the latest vector tarball you want to run) (although you should swap out the 'vector' folder for the latest vector tarball you want to run.
Get a tarball from https://vector.im/packages or build your own - see Building From Source
above).
There's also a (much) older electron distribution at https://github.com/stevenhammerton/vector-desktop There's also a (much) older electron distribution at https://github.com/stevenhammerton/vector-desktop
@ -216,20 +231,13 @@ day-to-day use; it is experimental and should be considered only as a
proof-of-concept. See https://matrix.org/jira/browse/SPEC-162 for an overview proof-of-concept. See https://matrix.org/jira/browse/SPEC-162 for an overview
of the current progress. of the current progress.
Vector is built with support for end-to-end encryption by default. To enable the (very experimental) support, check the 'End-to-End Encryption'
box in the 'Labs' section of the user settings (note that the labs are disabled
To enable encryption for a room, type on http://vector.im/beta: you will need to use http://vector.im/develop or your
own deployment of vector). The Room Settings dialog will then show an
``` 'Encryption' setting; rooms for which you are an administrator will offer you
/encrypt on the option of enabling encryption. Any messages sent in that room will then be
``` encrypted.
in the message bar in that room. Vector will then generate a set of keys, and
encrypt all outgoing messages in that room. (Note that other people in that
room will send messages in the clear unless they also `/encrypt on`.)
Note that historical encrypted messages cannot currently be decoded - history Note that historical encrypted messages cannot currently be decoded - history
is therefore lost when the page is reloaded. is therefore lost when the page is reloaded.
There is currently no visual indication of whether encryption is enabled for a
room.

View File

@ -4,12 +4,15 @@ set -e
export NVM_DIR="/home/jenkins/.nvm" export NVM_DIR="/home/jenkins/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh" [ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh"
nvm use 4 nvm use 6
set -x 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.
npm install matrix-react-sdk matrix-js-sdk
# we may be using a dev branch of react-sdk, in which case we need to build it # we may be using a dev branch of react-sdk, in which case we need to build it
(cd node_modules/matrix-react-sdk && npm run build) (cd node_modules/matrix-react-sdk && npm run build)

View File

@ -1,6 +1,6 @@
{ {
"name": "vector-web", "name": "vector-web",
"version": "0.7.4-r1", "version": "0.7.5-r1",
"description": "Vector webapp", "description": "Vector webapp",
"author": "matrix.org", "author": "matrix.org",
"repository": { "repository": {
@ -46,9 +46,9 @@
"gemini-scrollbar": "matrix-org/gemini-scrollbar#b302279", "gemini-scrollbar": "matrix-org/gemini-scrollbar#b302279",
"gfm.css": "^1.1.1", "gfm.css": "^1.1.1",
"highlight.js": "^9.0.0", "highlight.js": "^9.0.0",
"linkifyjs": "^2.0.0-beta.4", "linkifyjs": "2.0.0-beta.4",
"matrix-js-sdk": "matrix-org/matrix-js-sdk#develop", "matrix-js-sdk": "0.5.6",
"matrix-react-sdk": "matrix-org/matrix-react-sdk#develop", "matrix-react-sdk": "0.6.5",
"modernizr": "^3.1.0", "modernizr": "^3.1.0",
"q": "^1.4.1", "q": "^1.4.1",
"react": "^15.2.1", "react": "^15.2.1",

View File

@ -37,6 +37,7 @@ module.exports.components['structures.ViewSource'] = require('./components/struc
module.exports.components['views.context_menus.MessageContextMenu'] = require('./components/views/context_menus/MessageContextMenu'); module.exports.components['views.context_menus.MessageContextMenu'] = require('./components/views/context_menus/MessageContextMenu');
module.exports.components['views.context_menus.NotificationStateContextMenu'] = require('./components/views/context_menus/NotificationStateContextMenu'); module.exports.components['views.context_menus.NotificationStateContextMenu'] = require('./components/views/context_menus/NotificationStateContextMenu');
module.exports.components['views.context_menus.RoomTagContextMenu'] = require('./components/views/context_menus/RoomTagContextMenu'); module.exports.components['views.context_menus.RoomTagContextMenu'] = require('./components/views/context_menus/RoomTagContextMenu');
module.exports.components['views.dialogs.ChangelogDialog'] = require('./components/views/dialogs/ChangelogDialog');
module.exports.components['views.elements.ImageView'] = require('./components/views/elements/ImageView'); module.exports.components['views.elements.ImageView'] = require('./components/views/elements/ImageView');
module.exports.components['views.elements.Spinner'] = require('./components/views/elements/Spinner'); module.exports.components['views.elements.Spinner'] = require('./components/views/elements/Spinner');
module.exports.components['views.globals.GuestWarningBar'] = require('./components/views/globals/GuestWarningBar'); module.exports.components['views.globals.GuestWarningBar'] = require('./components/views/globals/GuestWarningBar');

View File

@ -17,7 +17,8 @@ limitations under the License.
'use strict'; 'use strict';
var React = require('react'); var React = require('react');
var sdk = require('matrix-react-sdk') var sdk = require('matrix-react-sdk');
var Matrix = require("matrix-js-sdk");
var dis = require('matrix-react-sdk/lib/dispatcher'); 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');
@ -45,9 +46,18 @@ module.exports = React.createClass({
}, },
getInitialState: function() { getInitialState: function() {
if (this.props.userId) {
var member = new Matrix.RoomMember(null, this.props.userId);
return {
phase: this.Phase.MemberInfo,
member: member,
}
}
else {
return { return {
phase: this.Phase.MemberList phase: this.Phase.MemberList
} }
}
}, },
onMemberListButtonClick: function() { onMemberListButtonClick: function() {
@ -97,7 +107,7 @@ module.exports = React.createClass({
}); });
} }
} }
if (payload.action === "view_room") { else if (payload.action === "view_room") {
if (this.state.phase === this.Phase.MemberInfo) { if (this.state.phase === this.Phase.MemberInfo) {
this.setState({ this.setState({
phase: this.Phase.MemberList phase: this.Phase.MemberList
@ -145,15 +155,15 @@ module.exports = React.createClass({
{ filesHighlight } { filesHighlight }
</div> </div>
</div>; </div>;
}
if (!this.props.collapsed) { if (!this.props.collapsed) {
if(this.state.phase == this.Phase.MemberList) { if(this.props.roomId && this.state.phase == this.Phase.MemberList) {
panel = <MemberList roomId={this.props.roomId} key={this.props.roomId} /> panel = <MemberList roomId={this.props.roomId} key={this.props.roomId} />
} }
else if(this.state.phase == this.Phase.MemberInfo) { else if(this.state.phase == this.Phase.MemberInfo) {
var MemberInfo = sdk.getComponent('rooms.MemberInfo'); var MemberInfo = sdk.getComponent('rooms.MemberInfo');
panel = <MemberInfo roomId={this.props.roomId} member={this.state.member} key={this.props.roomId} /> panel = <MemberInfo member={this.state.member} key={this.props.roomId || this.props.userId} />
}
} }
} }

View File

@ -456,7 +456,7 @@ var RoomSubList = React.createClass({
// is run with historical room tag data, after that there should only be undefined // is run with historical room tag data, after that there should only be undefined
// in the list at a time anyway. // in the list at a time anyway.
for (let i = 0; i < list.length; i++) { for (let i = 0; i < list.length; i++) {
if (list[i].tags[self.props.tagName].order === undefined) { if (list[i].tags[self.props.tagName] && list[i].tags[self.props.tagName].order === undefined) {
MatrixClientPeg.get().setRoomTag(list[i].roomId, self.props.tagName, {order: (order + 1.0) / 2.0}).finally(function() { MatrixClientPeg.get().setRoomTag(list[i].roomId, self.props.tagName, {order: (order + 1.0) / 2.0}).finally(function() {
// Do any final stuff here // Do any final stuff here
}).fail(function(err) { }).fail(function(err) {

View File

@ -133,12 +133,11 @@ module.exports = React.createClass({
} }
} }
// XXX: this should be https://matrix.to.
// XXX: if we use room ID, we should also include a server where the event can be found (other than in the domain of the event ID) // XXX: if we use room ID, we should also include a server where the event can be found (other than in the domain of the event ID)
permalinkButton = ( permalinkButton = (
<div className="mx_MessageContextMenu_field"> <div className="mx_MessageContextMenu_field">
<a href={ "#/room/" + this.props.mxEvent.getRoomId() +"/"+ this.props.mxEvent.getId() } <a href={ "https://matrix.to/#/" + this.props.mxEvent.getRoomId() +"/"+ this.props.mxEvent.getId() }
onClick={ this.onPermalinkClick }>Permalink</a> target="_blank" onClick={ this.onPermalinkClick }>Permalink</a>
</div> </div>
); );

View File

@ -126,6 +126,25 @@ module.exports = React.createClass({
}; };
}, },
_onClickForget: function() {
// FIXME: duplicated with RoomSettings (and dead code in RoomView)
MatrixClientPeg.get().forget(this.props.room.roomId).done(function() {
dis.dispatch({ action: 'view_next_room' });
}, function(err) {
var errCode = err.errcode || "unknown error code";
var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
Modal.createDialog(ErrorDialog, {
title: "Error",
description: `Failed to forget room (${errCode})`
});
});
// Close the context menu
if (this.props.onFinished) {
this.props.onFinished();
};
},
render: function() { render: function() {
var myUserId = MatrixClientPeg.get().credentials.userId; var myUserId = MatrixClientPeg.get().credentials.userId;
var myMember = this.props.room.getMember(myUserId); var myMember = this.props.room.getMember(myUserId);
@ -148,6 +167,17 @@ module.exports = React.createClass({
'mx_RoomTagContextMenu_fieldDisabled': false, 'mx_RoomTagContextMenu_fieldDisabled': false,
}); });
if (myMember && myMember.membership === "leave") {
return (
<div>
<div className={ leaveClasses } onClick={ this._onClickForget } >
<img className="mx_RoomTagContextMenu_icon" src="img/icon_context_delete.svg" width="15" height="15" />
Forget
</div>
</div>
);
}
return ( return (
<div> <div>
<div className={ favouriteClasses } onClick={this._onClickFavourite} > <div className={ favouriteClasses } onClick={this._onClickFavourite} >

View File

@ -0,0 +1,82 @@
/*
Copyright 2016 Aviral Dasgupta
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import React from 'react';
import sdk from 'matrix-react-sdk';
import request from 'browser-request';
const REPOS = ['vector-im/vector-web', 'matrix-org/matrix-react-sdk', 'matrix-org/matrix-js-sdk'];
export default class ChangelogDialog extends React.Component {
constructor(props) {
super(props);
this.state = {};
}
componentDidMount() {
const version = this.props.newVersion.split('-');
const version2 = this.props.version.split('-');
if(version == null || version2 == null) return;
for(let i=0; i<REPOS.length; i++) {
const oldVersion = version2[2*i+1];
const newVersion = version[2*i+1];
request(`https://api.github.com/repos/${REPOS[i]}/compare/${oldVersion}...${newVersion}`, (a, b, body) => {
if(body == null) return;
this.setState({[REPOS[i]]: JSON.parse(body).commits});
});
}
}
render() {
const Spinner = sdk.getComponent('views.elements.Spinner');
const QuestionDialog = sdk.getComponent('dialogs.QuestionDialog');
const logs = REPOS.map(repo => {
if (this.state[repo] == null) return <Spinner key={repo} />;
return (
<div key={repo}>
<h2>{repo}</h2>
{this.state[repo].map(commit =>
<div key={commit.commit.url}><a href={commit.commit.url}>{commit.commit.message}</a></div>
)}
</div>
)
});
const content = (
<div className="mx_ChangelogDialog_content">
{this.props.version == null || this.props.newVersion == null ? <h2>Unavailable</h2> : logs}
</div>
);
return (
<QuestionDialog
title="Changelog"
description={content}
button="Update"
onFinished={this.props.onFinished}
/>
)
}
}
ChangelogDialog.propTypes = {
version: React.PropTypes.string.isRequired,
newVersion: React.PropTypes.string.isRequired,
onFinished: React.PropTypes.func.isRequired,
};

View File

@ -17,20 +17,36 @@ limitations under the License.
'use strict'; 'use strict';
var React = require('react'); var React = require('react');
var sdk = require('matrix-react-sdk') var sdk = require('matrix-react-sdk');
import Modal from 'matrix-react-sdk/lib/Modal';
module.exports = React.createClass({ export default function NewVersionBar(props) {
displayName: 'NewVersionBar', const onChangelogClicked = () => {
const ChangelogDialog = sdk.getComponent('dialogs.ChangelogDialog');
Modal.createDialog(ChangelogDialog, {
version: props.version,
newVersion: props.newVersion,
onFinished: (update) => {
if(update) {
window.location.reload();
}
}
});
};
render: function() {
return ( return (
<div className="mx_MatrixToolbar"> <div className="mx_MatrixToolbar">
<img className="mx_MatrixToolbar_warning" src="img/warning.svg" width="24" height="23" alt="/!\"/> <img className="mx_MatrixToolbar_warning" src="img/warning.svg" width="24" height="23" alt="/!\"/>
<div> <div className="mx_MatrixToolbar_content">
A new version of Vector is available. Refresh your browser. A new version of Vector is available. Refresh your browser.
</div> </div>
<button className="mx_MatrixToolbar_action" onClick={onChangelogClicked}>Changelog</button>
</div> </div>
); );
} }
});
NewVersionBar.propTypes = {
version: React.PropTypes.string.isRequired,
newVersion: React.PropTypes.string.isRequired,
};

View File

@ -251,7 +251,7 @@ input[type=text]:focus, textarea:focus {
background-color: #fff; background-color: #fff;
} }
.emojione { .mx_emojione {
height: 1em; height: 1em;
vertical-align: middle; vertical-align: middle;
} }
@ -267,7 +267,7 @@ input[type=text]:focus, textarea:focus {
} }
/** green button with rounded corners */ /** green button with rounded corners */
.textButton { .mx_textButton {
color: #fff; color: #fff;
background-color: #76cfa6; background-color: #76cfa6;
border-radius: 17px; border-radius: 17px;

View File

@ -4,7 +4,7 @@
z-index: 1000; z-index: 1000;
width: 100%; width: 100%;
border: 1px solid #e5e5e5; border: 1px solid #e5e5e5;
background: rgba(255, 255, 255, 0.9); background: white;
border-bottom: none; border-bottom: none;
border-radius: 4px 4px 0 0; border-radius: 4px 4px 0 0;
max-height: 50vh; max-height: 50vh;
@ -12,56 +12,68 @@
} }
.mx_Autocomplete_ProviderSection { .mx_Autocomplete_ProviderSection {
padding: 12px;
border-bottom: 1px solid #e5e5e5; border-bottom: 1px solid #e5e5e5;
} }
.mx_Autocomplete_ProviderSection * { .mx_Autocomplete_Completion_container_pill {
padding: 2px; margin: 12px;
border-radius: 4px; display: flex;
} }
.mx_Autocomplete_Completion { /* a "block" completion takes up a whole line */
.mx_Autocomplete_Completion_block {
height: 34px;
display: flex;
padding: 0 12px;
user-select: none; user-select: none;
cursor: pointer; cursor: pointer;
transition: 0.3s all ease;
display: flex;
align-items: center; align-items: center;
color: #4a4a4a;
} }
.mx_Autocomplete_Completion.selected * { .mx_Autocomplete_Completion_block * {
transition: 0.3s all ease; margin: 0 3px;
}
.mx_Autocomplete_Completion_pill {
border-radius: 17px;
height: 34px;
display: flex;
user-select: none;
cursor: pointer;
align-items: center;
color: #4a4a4a;
}
.mx_Autocomplete_Completion_pill * {
margin: 0 3px;
}
/* container for pill-style completions */
.mx_Autocomplete_Completion_container_pill {
margin: 12px;
display: flex;
flex-flow: wrap;
} }
.mx_Autocomplete_Completion.selected { .mx_Autocomplete_Completion.selected {
background: #76cfa6; background: #f6f6f6;
color: white;
outline: none; outline: none;
} }
.mx_Autocomplete_Completion.selected * {
color: white !important;
}
.mx_Autocomplete_provider_name { .mx_Autocomplete_provider_name {
color: #76cfa6; margin: 12px;
font-weight: 600; color: #454545;
font-weight: 400;
opacity: 0.4;
} }
.autocomplete-enter { /* styling for common completion elements */
opacity: 0.01; .mx_Autocomplete_Completion_subtitle {
font-style: italic;
flex: 1;
} }
.autocomplete-enter.autocomplete-enter-active { .mx_Autocomplete_Completion_description {
opacity: 1; color: gray;
transition: opacity 300ms ease-in;
}
.autocomplete-leave {
opacity: 1;
}
.autocomplete-leave.autocomplete-leave-active {
opacity: 0.01;
transition: opacity 300ms ease-in;
} }

View File

@ -34,6 +34,11 @@ limitations under the License.
z-index: 2; z-index: 2;
} }
.mx_EventTile.mx_EventTile_info .mx_EventTile_avatar {
top: 8px;
left: 44px;
}
.mx_EventTile_continuation { .mx_EventTile_continuation {
padding-top: 0px ! important; padding-top: 0px ! important;
} }

View File

@ -63,6 +63,7 @@ limitations under the License.
align-items: center; align-items: center;
overflow: auto; overflow: auto;
font-size: 14px; font-size: 14px;
margin-right: 6px;
} }
.mx_MessageComposer_input_rte { .mx_MessageComposer_input_rte {
border-top: 2px solid #76cfa6; /* placeholder RTE indicator */ border-top: 2px solid #76cfa6; /* placeholder RTE indicator */

View File

@ -19,6 +19,21 @@ limitations under the License.
margin-bottom: 20px; margin-bottom: 20px;
} }
.mx_RoomSettings_leaveButton {
height: 36px;
background-color: #76cfa6;
border-radius: 36px;
margin-right: 8px;
color: #fff;
line-height: 34px;
text-align: center;
float: right;
cursor: pointer;
padding-left: 12px;
padding-right: 12px;
margin-right: 32px;
}
.mx_RoomSettings_powerLevels { .mx_RoomSettings_powerLevels {
display: table; display: table;
} }

View File

@ -0,0 +1,20 @@
/*
Copyright 2016 Aviral Dasgupta
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
.mx_ChangelogDialog_content {
max-height: 300px;
overflow: auto;
}

View File

@ -54,3 +54,7 @@ limitations under the License.
float: right; float: right;
margin-right: 10px; margin-right: 10px;
} }
.mx_MatrixToolbar_action {
margin-right: 16px;
}