Compare commits

..

3 Commits

Author SHA1 Message Date
David Baker
d4d83f644a operator precedence 2015-08-05 17:59:04 +01:00
David Baker
6fc0498a57 refer to the appropriate js-sdk branch 2015-08-05 14:51:30 +01:00
David Baker
05201e7d34 Hacks for a conference calling PoC 2015-08-05 14:45:10 +01:00
62 changed files with 318 additions and 775 deletions

View File

@@ -1,9 +0,0 @@
Vector is written mainly by the Vector team, building upon the Matrix React
SDK. Vector also welcomes external contributions. Third party contributors
include:
* Nolan Darilek (https://github.com/ndarilek)
Accessibility and semantic markup contributions
* https://github.com/neko259
Improved scrollbar CSS

View File

@@ -1,20 +0,0 @@
Changes in vector v0.1.1 (2015-08-10)
======================================
* Support logging in with an email address
* Use the Vector identity server
* Fix a bug where the client was not stopped properly on logout
* Fix bugs where field values would be forgotten if login or registration failed
* Improve URL bar navigation
* Add explanatory help text on advanced server options
* Fix a bug which caused execptions on malformed VoIP invitations
* Remove superfluous scrollbars on Firefox
* Numerous CSS fixes
* Improved accessibility
* Support command-click / middle click to open image in a new tab
* Improved room directory
* Fix display of text with many combining unicode points
Changes in vector v0.1.0 (2015-08-10)
======================================
Initial release

View File

@@ -1,4 +0,0 @@
Contributing code to Vector
===========================
Vector follows the same pattern as https://github.com/matrix-org/synapse/blob/master/CONTRIBUTING.rst

View File

@@ -24,8 +24,6 @@ var React = require("react");
// maps cannot pass through two stages).
var MatrixReactSdk = require("../../src/index");
var lastLocationHashSet = null;
// Here, we do some crude URL analysis to allow
// deep-linking. We only support registration
// deep-links in this example.
@@ -48,10 +46,6 @@ function routeUrl(location) {
}
function onHashChange(ev) {
if (decodeURIComponent(window.location.hash) == lastLocationHashSet) {
// we just set this: no need to route it!
return;
}
routeUrl(window.location);
}
@@ -61,9 +55,7 @@ var loaded = false;
// so a web page can update the URL bar appropriately.
var onNewScreen = function(screen) {
if (!loaded) return;
var hash = '#/' + screen;
lastLocationHashSet = hash;
window.location.hash = hash;
window.location.hash = '#/'+screen;
}
// We use this to work out what URL the SDK should

View File

@@ -10,6 +10,7 @@
"license": "Apache-2.0",
"devDependencies": {
"browserify": "^10.2.3",
"envify": "^3.4.0",
"http-server": "^0.8.0",
"matrix-react-sdk": "../../",
"parallelshell": "^1.2.0",
@@ -18,7 +19,7 @@
"watchify": "^3.2.1"
},
"scripts": {
"build": "NODE_ENV=production browserify --ignore olm -t reactify index.js | uglifyjs -c -m -o bundle.js",
"build": "browserify --ignore olm -t [ envify --NODE_ENV production ] -t reactify index.js | uglifyjs -c -m -o bundle.js",
"start": "parallelshell \"watchify --ignore olm -v -d -t reactify index.js -o bundle.js\" \"http-server\""
}
}

View File

@@ -25,7 +25,7 @@
"classnames": "^2.1.2",
"filesize": "^3.1.2",
"flux": "^2.0.3",
"matrix-js-sdk": "git://github.com/matrix-org/matrix-js-sdk.git#develop",
"matrix-js-sdk": "git://github.com/matrix-org/matrix-js-sdk.git#no-trickle-ice",
"q": "^1.4.1",
"react": "^0.13.3",
"react-loader": "^1.4.0",

View File

@@ -14,24 +14,6 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
.mx_MImageTile_thumbnail {
/*
background-color: #fff;
border: 2px solid #fff;
border-radius: 1px;
*/
.mx_MImageTile {
}
.mx_MImageTile_download {
color: #80cef4;
cursor: pointer;
}
.mx_MImageTile_download a {
color: #80cef4;
text-decoration: none;
}
.mx_MImageTile_download img {
padding-right: 8px;
}

View File

@@ -25,11 +25,4 @@ limitations under the License.
.mx_MatrixToolbar button {
margin-left: 12px;
}
.mx_MatrixToolbar_close {
float: right;
margin-top: 3px;
margin-right: 12px;
cursor: pointer;
}

View File

@@ -31,6 +31,12 @@ limitations under the License.
position: relative;
}
.mx_MemberTile_avatarImg {
z-index: 20;
border-radius: 20px;
background-color: #dbdbdb;
}
.mx_MemberTile_inviteEditing {
display: initial ! important;
}

View File

@@ -40,7 +40,6 @@ limitations under the License.
-webkit-order: 1;
order: 1;
-webkit-flex: 1;
flex: 1;
}
@@ -60,15 +59,9 @@ limitations under the License.
-webkit-order: 2;
order: 2;
cursor: pointer;
/*
-webkit-flex: 0 0 90px;
flex: 0 0 90px;
*/
padding-left: 12px;
padding-right: 12px;
}
}
.mx_RoomHeader_rightRow {
height: 48px;
@@ -95,7 +88,7 @@ limitations under the License.
color: #80cef4;
font-weight: 400;
font-size: 20px;
overflow: hidden;
overflow: scroll;
text-overflow: ellipsis;
}
@@ -134,8 +127,7 @@ limitations under the License.
font-weight: 300;
padding-left: 16px;
padding-right: 16px;
overflow: hidden;
text-overflow: ellipsis;
overflow-y: scroll;
}
.mx_RoomHeader_avatar {

View File

@@ -40,7 +40,6 @@ limitations under the License.
order: 1;
overflow-y: scroll;
-webkit-flex: 1 1 0;
flex: 1 1 0;
}

View File

@@ -40,13 +40,12 @@ limitations under the License.
.mx_MemberList_border {
border: 1px solid #a9dbf4;
overflow-y: auto;
overflow-y: scroll;
border-radius: 8px;
padding: 20px 14px 14px 24px;
background-color: #fff;
order: 1;
-webkit-flex: 1 1 0;
flex: 1 1 0px;
}

View File

@@ -32,7 +32,6 @@ limitations under the License.
}
.mx_RoomDirectory_list {
-webkit-flex: 1;
flex: 1;
display: -webkit-box;
@@ -58,7 +57,6 @@ limitations under the License.
.mx_RoomDirectory_tableWrapper {
overflow-y: scroll;
-webkit-flex: 1 1 0;
flex: 1 1 0;
}

View File

@@ -40,42 +40,6 @@ limitations under the License.
flex: 0 0 88px;
}
.mx_RoomView_fileDropTarget {
min-width: 0px;
max-width: 720px;
width: 100%;
font-size: 20px;
text-align: center;
pointer-events: none;
padding-left: 12px;
padding-right: 12px;
margin-left: -12px;
-webkit-border-top-left-radius: 10px;
-webkit-border-top-right-radius: 10px;
-moz-border-radius-topleft: 10px;
-moz-border-radius-topright: 10px;
border-top-left-radius: 10px;
border-top-right-radius: 10px;
background-color: rgba(255, 255, 255, 0.9);
border: 2px dashed #80cef4;
border-bottom: none;
position: absolute;
top: 88px;
bottom: 0px;
z-index: 3000;
}
.mx_RoomView_fileDropTargetLabel {
top: 50%;
width: 100%;
margin-top: -50px;
position: absolute;
}
.mx_RoomView_auxPanel {
-webkit-box-ordinal-group: 2;
-moz-box-ordinal-group: 2;
@@ -88,9 +52,9 @@ limitations under the License.
width: 100%;
margin: auto;
overflow: auto;
border-bottom: 1px solid #a8dbf3;
overflow: scroll;
-webkit-flex: 0 0 auto;
flex: 0 0 auto;
}
@@ -119,8 +83,6 @@ limitations under the License.
.mx_RoomView_MessageList {
width: 100%;
list-style-type: none;
padding: 0px;
}
.mx_RoomView_MessageList h2 {
@@ -165,22 +127,6 @@ limitations under the License.
border-top: 1px solid #a8dbf3;
}
.mx_RoomView_unreadMessagesBar {
margin-top: 13px;
color: #fff;
font-weight: bold;
background-color: #ff0064;
border-radius: 30px;
height: 30px;
line-height: 30px;
cursor: pointer;
}
.mx_RoomView_unreadMessagesBar img {
padding-left: 22px;
padding-right: 22px;
}
.mx_RoomView_typingBar {
margin-top: 17px;
margin-left: 56px;

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 213 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

View File

@@ -24,23 +24,6 @@ module.exports = React.createClass({
displayName: 'ImageView',
mixins: [ImageViewController],
// XXX: keyboard shortcuts for managing dialogs should be done by the modal dialog base class omehow, surely...
componentDidMount: function() {
document.addEventListener("keydown", this.onKeyDown);
},
componentWillUnmount: function() {
document.removeEventListener("keydown", this.onKeyDown);
},
onKeyDown: function(ev) {
if (ev.keyCode == 27) { // escape
ev.stopPropagation();
ev.preventDefault();
this.props.onFinished();
}
},
render: function() {
// XXX: can't we just do max-width: 80%, max-height: 80% on the CSS?

View File

@@ -1,34 +0,0 @@
/*
Copyright 2015 OpenMarket Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
'use strict';
var React = require('react');
var MemberAvatarController = require("../../../../src/controllers/atoms/MemberAvatar");
module.exports = React.createClass({
displayName: 'MemberAvatar',
mixins: [MemberAvatarController],
render: function() {
return (
<img className="mx_MemberAvatar" src={this.state.imageUrl}
onError={this.onError}
width={this.props.width} height={this.props.height} />
);
}
});

View File

@@ -22,7 +22,6 @@ var MatrixClientPeg = require("../../../../src/MatrixClientPeg");
var EventAsTextTileController = require("../../../../src/controllers/molecules/EventAsTextTile");
var ComponentBroker = require('../../../../src/ComponentBroker');
var MessageTimestamp = ComponentBroker.get('atoms/MessageTimestamp');
var MemberAvatar = ComponentBroker.get('atoms/MemberAvatar');
var TextForEvent = require("../../../../src/TextForEvent");
module.exports = React.createClass({
@@ -31,14 +30,11 @@ module.exports = React.createClass({
render: function() {
var text = TextForEvent.textForEvent(this.props.mxEvent);
if (text == null || text.length == 0) return null;
var timestamp = this.props.last ? <MessageTimestamp ts={this.props.mxEvent.getTs()} /> : null;
var avatar = this.props.mxEvent.sender ? <MemberAvatar member={this.props.mxEvent.sender} /> : null;
return (
<div className="mx_MessageTile mx_MessageTile_notice">
<div className="mx_MessageTile_avatar">
{ avatar }
<img src={ this.props.mxEvent.sender ? MatrixClientPeg.get().getAvatarUrlForMember(this.props.mxEvent.sender, 40, 40, "crop") : null } width="40" height="40" alt=""/>
</div>
{ timestamp }
<span className="mx_SenderProfile"></span>

View File

@@ -1,53 +0,0 @@
/*
Copyright 2015 OpenMarket Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
'use strict';
var React = require('react');
var MatrixClientPeg = require("../../../../src/MatrixClientPeg");
var HealthDemoTileController = require("../../../../src/controllers/molecules/HealthDemoTile");
var ComponentBroker = require('../../../../src/ComponentBroker');
var MessageTimestamp = ComponentBroker.get('atoms/MessageTimestamp');
var MemberAvatar = ComponentBroker.get('atoms/MemberAvatar');
var TextForEvent = require("../../../../src/TextForEvent");
module.exports = React.createClass({
displayName: 'HealthDemoTile',
mixins: [HealthDemoTileController],
render: function() {
var ts = this.props.mxEvent.getContent().ts;
var date = new Date(ts*1000);
var timeStr = date.toLocaleDateString() + ' ' + date.toLocaleTimeString();
//var timestamp = <MessageTimestamp ts={this.props.mxEvent.getContent().ts} />;
var imgStyle = { float: 'left' };
return (
<div className="mx_HealthDemoTile">
<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAYAAACM/rhtAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3wgSDys3IE3P1AAAA2lJREFUWMPNmMuLFUcUxn+n03ONzkRNxOhERQQfgxhXBhNiFB+ICxdZCSJkJbVS0LUIbgyBgLMQRAr8N1xIRCVmsogSETQZBYOPGZPAEIcRR8c7HjenoWyqr/dO9SX3QN/ue+r19VffOVXVkGBepC1fikkNAAX4BpgPXHOqL+sEmKU0dqoAB4BLwEXgZN0sZjX0cRCYZ30doWbLatDf2sC90IssNWb/X4ABiHWloq97RoNe5EOgUXJv6xmApekt7Ks6AyUV4PqIb0NJAj3HYJ8X2VhX4k4FuC7iawCbCnAFkwXQTgFniSkmBrAP2BzUHfAiV4EXXuSYU+0IZJaYYtZEij8otBmsNDtsKRz2Iis7AZmSqHNgoKL4My/ysT3vKpWd7SSIUjS4ytiK2TJgeQXAvV7ky3b1mAJwZQngX0DTnpcDS71IPzBYarcAONoui6kAc3ueBa4DM/Z/APg0wl5hO73IF+0EY5YQwSsCBqcM4OtSjtxT0c2gBc97gzH3ImJpYSfwpsUmVoCfnerNCINTwAjwElhkviFga9D+jrUpyg97kbWBLEJrWjCN5Bb+PwD7SgxUMbjFqd42FiQA+BT40wIEYHcQKAC/AneBM/Z/MfBti6EmgBuZ6fCTIMm+7/ouGKCwSeCFTXNMowCjTnUYGGtTTXnx0zT6hyIMFi/QHwy2y4s0gIUhQKc67UV+aTHgqN0PAT/aLrxVGF8Fmrnp5gRwIdIgs7X1OLDffKuBjcBHQb3/7D5SMdi4XTjVa15kr2mxCuAM8I9T1dxy0VO7qnT3eQCwYfoqACrwtw0+6UUmgCWlLt7p36k+A57VmWZGg2hrWH7rDyLucVA3Ns1jTnV8Lmt+uwDHA3H32bljIEjSofDL0/ym0N9c9obtAhwLQIjpZ17A4JOg7vWInm51dcPqVCeARxXFsyWA9yIAf5/rMSBrR6hmt1pk/XCKXwUpBWDaqf7RNQaDt75pg5dtyqnOlgCG03wp5WySd1D3twoGH4TnD6f6yot8b6lnBjifcsrLOgj5SeBhpOheGYBTfQCcAk471X9TTnZ5h/WvhAcis/sVL9SMyKTrx86fIr77dNE6BXi5pwE61ekIoJ5isDzNz53qbN3fpVOCBOAcsN3W4uE6PxQlW/B9ZdCLDNX5ma3roLtlbwGzvhTlwr9lXgAAAABJRU5ErkJggg==" style={imgStyle} />
<div>
<span className="mx_HealthDemoTile_ts">{timeStr}</span>
<span className="mx_HealthDemoTile_content">
{this.props.mxEvent.getContent().bpm} bpm
</span>
</div>
</div>
);
},
});

View File

@@ -32,12 +32,9 @@ module.exports = React.createClass({
return (
<span className="mx_MFileTile">
<div className="mx_MImageTile_download">
<a href={cli.mxcUrlToHttp(content.url)} target="_blank">
<img src="img/download.png" width="10" height="12"/>
Download {this.presentableTextForFile(content)}
</a>
</div>
<a href={cli.mxcUrlToHttp(content.url)} target="_blank">
{this.presentableTextForFile(content)}
</a>
</span>
);
},

View File

@@ -17,7 +17,6 @@ limitations under the License.
'use strict';
var React = require('react');
var filesize = require('filesize');
var MImageTileController = require("../../../../src/controllers/molecules/MImageTile");
@@ -79,15 +78,9 @@ module.exports = React.createClass({
return (
<span className="mx_MImageTile">
<a href={cli.mxcUrlToHttp(content.url)} onClick={ this.onClick }>
<img className="mx_MImageTile_thumbnail" src={cli.mxcUrlToHttp(content.url, 320, 240)} alt={content.body} style={imgStyle} />
<a href={cli.mxcUrlToHttp(content.url)} onClick={this.onClick}>
<img src={cli.mxcUrlToHttp(content.url, 320, 240)} alt={content.body} style={imgStyle} />
</a>
<div className="mx_MImageTile_download">
<a href={cli.mxcUrlToHttp(content.url)} target="_blank">
<img src="img/download.png" width="10" height="12"/>
Download {content.body} ({ filesize(content.info.size) })
</a>
</div>
</span>
);
},

View File

@@ -24,7 +24,6 @@ var MatrixClientPeg = require("../../../../src/MatrixClientPeg");
var ComponentBroker = require('../../../../src/ComponentBroker');
var TextForEvent = require('../../../../src/TextForEvent');
var MessageTimestamp = ComponentBroker.get('atoms/MessageTimestamp');
var MemberAvatar = ComponentBroker.get('atoms/MemberAvatar');
module.exports = React.createClass({
displayName: 'MRoomMemberTile',
@@ -42,7 +41,7 @@ module.exports = React.createClass({
return (
<div className="mx_MessageTile mx_MessageTile_notice">
<div className="mx_MessageTile_avatar">
<MemberAvatar member={this.props.mxEvent.sender} />
<img src={ this.props.mxEvent.target ? MatrixClientPeg.get().getAvatarUrlForMember(this.props.mxEvent.target, 40, 40, "crop") : null } width="40" height="40" alt=""/>
</div>
{ timestamp }
<span className="mx_SenderProfile"></span>

View File

@@ -24,21 +24,15 @@ var LogoutButton = ComponentBroker.get("atoms/LogoutButton");
var EnableNotificationsButton = ComponentBroker.get("atoms/EnableNotificationsButton");
var MatrixToolbarController = require("../../../../src/controllers/molecules/MatrixToolbar");
var Notifier = ComponentBroker.get('organisms/Notifier');
module.exports = React.createClass({
displayName: 'MatrixToolbar',
mixins: [MatrixToolbarController],
hideToolbar: function() {
Notifier.setToolbarHidden(true);
},
render: function() {
return (
<div className="mx_MatrixToolbar">
You are not receiving desktop notifications. <EnableNotificationsButton />
<div className="mx_MatrixToolbar_close"><img src="img/close-white.png" width="16" height="16" onClick={ this.hideToolbar } /></div>
</div>
);
}

View File

@@ -20,8 +20,6 @@ var React = require('react');
var MatrixClientPeg = require("../../../../src/MatrixClientPeg");
var MemberInfoController = require("../../../../src/controllers/molecules/MemberInfo");
var ComponentBroker = require('../../../../src/ComponentBroker');
var MemberAvatar = ComponentBroker.get('atoms/MemberAvatar');
module.exports = React.createClass({
displayName: 'MemberInfo',
@@ -60,6 +58,11 @@ module.exports = React.createClass({
},
render: function() {
var power;
if (this.props.member) {
var img = "img/p/p" + Math.floor(20 * this.props.member.powerLevelNorm / 100) + ".png";
power = <img src={ img } className="mx_MemberTile_power" width="48" height="48" alt=""/>;
}
var activeAgo = "unknown";
if (this.state.active >= 0) {
activeAgo = this.getDuration(this.state.active);
@@ -98,7 +101,9 @@ module.exports = React.createClass({
<img className="mx_MemberInfo_chevron" src="img/chevron-right.png" width="9" height="16" />
<div className="mx_MemberInfo_shim"></div>
<div className="mx_MemberInfo_avatar">
<MemberAvatar member={this.props.member} width={128} height={128} />
<img className="mx_MemberInfo_avatarImg"
src={ this.props.member ? MatrixClientPeg.get().getAvatarUrlForMember(this.props.member, 128, 128, "crop") : null }
width="128" height="128" alt=""/>
</div>
<div className="mx_MemberInfo_field">{this.props.member.userId}</div>
{opLabel}

View File

@@ -24,7 +24,6 @@ var Modal = require("../../../../src/Modal");
var MemberTileController = require("../../../../src/controllers/molecules/MemberTile");
var MemberInfo = ComponentBroker.get('molecules/MemberInfo');
var ErrorDialog = ComponentBroker.get("organisms/ErrorDialog");
var MemberAvatar = ComponentBroker.get('atoms/MemberAvatar');
// The Lato WOFF doesn't include sensible combining diacritics, so Chrome chokes on rendering them.
// Revert to Arial when this happens, which on OSX works at least.
@@ -51,7 +50,7 @@ module.exports = React.createClass({
var isMyUser = MatrixClientPeg.get().credentials.userId == this.props.member.userId;
var power;
if (this.props.member && this.props.member.powerLevelNorm > 0) {
if (this.props.member) {
var img = "img/p/p" + Math.floor(20 * this.props.member.powerLevelNorm / 100) + ".png";
power = <img src={ img } className="mx_MemberTile_power" width="48" height="48" alt=""/>;
}
@@ -96,8 +95,10 @@ module.exports = React.createClass({
return (
<div className={mainClassName} onMouseEnter={ this.mouseEnter } onMouseLeave={ this.mouseLeave }>
<div className="mx_MemberTile_avatar">
<MemberAvatar member={this.props.member} />
{ power }
<img className="mx_MemberTile_avatarImg"
src={ this.props.member ? MatrixClientPeg.get().getAvatarUrlForMember(this.props.member, 40, 40, "crop") : null }
width="40" height="40" alt=""/>
{ power }
</div>
{ nameEl }
</div>

View File

@@ -22,9 +22,6 @@ var MatrixClientPeg = require("../../../../src/MatrixClientPeg");
var MessageComposerController = require("../../../../src/controllers/molecules/MessageComposer");
var ContentMessages = require("../../../../src/ContentMessages");
var ComponentBroker = require('../../../../src/ComponentBroker');
var MemberAvatar = ComponentBroker.get('atoms/MemberAvatar');
module.exports = React.createClass({
displayName: 'MessageComposer',
mixins: [MessageComposerController],
@@ -50,7 +47,7 @@ module.exports = React.createClass({
<div className="mx_MessageComposer_wrapper">
<div className="mx_MessageComposer_row">
<div className="mx_MessageComposer_avatar">
<MemberAvatar member={me} />
<img src={ MatrixClientPeg.get().getAvatarUrlForMember(me, 40, 40, "crop") } width="40" height="40" alt=""/>
</div>
<div className="mx_MessageComposer_input">
<textarea ref="textarea" onKeyDown={this.onKeyDown} placeholder="Type a message" />

View File

@@ -25,7 +25,6 @@ var ComponentBroker = require('../../../../src/ComponentBroker');
var MessageTimestamp = ComponentBroker.get('atoms/MessageTimestamp');
var SenderProfile = ComponentBroker.get('molecules/SenderProfile');
var MemberAvatar = ComponentBroker.get('atoms/MemberAvatar');
var UnknownMessageTile = ComponentBroker.get('molecules/UnknownMessageTile');
@@ -61,20 +60,14 @@ module.exports = React.createClass({
mx_MessageTile_last: this.props.last,
});
var timestamp = <MessageTimestamp ts={this.props.mxEvent.getTs()} />
var aux = null;
if (msgtype === 'm.image') aux = "sent an image";
else if (msgtype === 'm.video') aux = "sent a video";
else if (msgtype === 'm.file') aux = "uploaded a file";
var avatar, sender, resend;
if (!this.props.continuation) {
avatar = (
<div className="mx_MessageTile_avatar">
<MemberAvatar member={this.props.mxEvent.sender} />
<img src={ this.props.mxEvent.sender ? MatrixClientPeg.get().getAvatarUrlForMember(this.props.mxEvent.sender, 40, 40, "crop") : null } width="40" height="40" alt=""/>
</div>
);
sender = <SenderProfile mxEvent={this.props.mxEvent} aux={aux} />;
sender = <SenderProfile mxEvent={this.props.mxEvent} />;
}
if (this.props.mxEvent.status === "not_sent" && !this.state.resending) {
resend = <button className="mx_MessageTile_msgOption" onClick={this.onResend}>

View File

@@ -22,7 +22,6 @@ var ComponentBroker = require('../../../../src/ComponentBroker');
var MatrixClientPeg = require("../../../../src/MatrixClientPeg");
var RoomHeaderController = require("../../../../src/controllers/molecules/RoomHeader");
var EditableText = ComponentBroker.get("atoms/EditableText");
var RoomAvatar = ComponentBroker.get('atoms/RoomAvatar');
module.exports = React.createClass({
displayName: 'RoomHeader',
@@ -68,7 +67,6 @@ module.exports = React.createClass({
var name = null;
var topic_el = null;
var cancel_button = null;
var save_button = null;
var settings_button = null;
var actual_name = this.props.room.currentState.getStateEvents('m.room.name', '');
@@ -79,14 +77,12 @@ module.exports = React.createClass({
<input className="mx_RoomHeader_nameInput" type="text" defaultValue={actual_name} placeholder="Name" ref="name_edit"/>
</div>
// if (topic) topic_el = <div className="mx_RoomHeader_topic"><textarea>{ topic.getContent().topic }</textarea></div>
cancel_button = <div className="mx_RoomHeader_textButton" onClick={this.props.onCancelClick}>Cancel</div>
save_button = <div className="mx_RoomHeader_textButton" onClick={this.props.onSaveClick}>Save Changes</div>
} else {
name =
<div className="mx_RoomHeader_name">
<EditableText label={this.props.room.name} initialValue={actual_name} placeHolder="Name" onValueChanged={this.onNameChange} />
</div>
if (topic) topic_el = <div className="mx_RoomHeader_topic" title={topic.getContent().topic}>{ topic.getContent().topic }</div>;
if (topic) topic_el = <div className="mx_RoomHeader_topic">{ topic.getContent().topic }</div>;
settings_button = (
<div className="mx_RoomHeader_button" onClick={this.props.onSettingsClick}>
<img src="img/settings.png" width="32" height="32"/>
@@ -94,18 +90,11 @@ module.exports = React.createClass({
);
}
var roomAvatar = null;
if (this.props.room) {
roomAvatar = (
<RoomAvatar room={this.props.room} />
);
}
header =
<div className="mx_RoomHeader_wrapper">
<div className="mx_RoomHeader_leftRow">
<div className="mx_RoomHeader_avatar">
{ roomAvatar }
<img src={ MatrixClientPeg.get().getAvatarUrlForRoom(this.props.room, 48, 48, "crop") } width="48" height="48" alt=""/>
</div>
<div className="mx_RoomHeader_info">
{ name }
@@ -113,8 +102,6 @@ module.exports = React.createClass({
</div>
</div>
{callButtons}
{cancel_button}
{save_button}
<div className="mx_RoomHeader_rightRow">
{ settings_button }
<div className="mx_RoomHeader_button mx_RoomHeader_search">

View File

@@ -207,6 +207,12 @@ module.exports = React.createClass({
);
})}
</div>
<div className="mx_RoomSettings_buttons">
<div className="mx_RoomSettings_button" onClick={this.props.onSaveClick}>
Save this room
</div>
</div>
</div>
);
}

View File

@@ -23,9 +23,6 @@ var RoomTileController = require("../../../../src/controllers/molecules/RoomTile
var MatrixClientPeg = require("../../../../src/MatrixClientPeg");
var ComponentBroker = require('../../../../src/ComponentBroker');
var RoomAvatar = ComponentBroker.get('atoms/RoomAvatar');
module.exports = React.createClass({
displayName: 'RoomTile',
mixins: [RoomTileController],
@@ -60,10 +57,7 @@ module.exports = React.createClass({
*/
return (
<div className={classes} onClick={this.onClick}>
<div className="mx_RoomTile_avatar">
<RoomAvatar room={this.props.room} />
{ badge }
</div>
<div className="mx_RoomTile_avatar"><img src={ MatrixClientPeg.get().getAvatarUrlForRoom(this.props.room, 40, 40, "crop") } width="40" height="40" alt=""/>{ badge }</div>
<div className="mx_RoomTile_name">{name}</div>
</div>
);

View File

@@ -45,7 +45,7 @@ module.exports = React.createClass({
}
return (
<span className={classes}>
{name} { this.props.aux }
{name}
</span>
);
},

View File

@@ -0,0 +1,50 @@
/*
Copyright 2015 OpenMarket Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
'use strict';
var React = require('react');
var MatrixClientPeg = require("../../../../../src/MatrixClientPeg");
var ComponentBroker = require('../../../../../src/ComponentBroker');
var MCallAnswerTileController = require("../../../../../src/controllers/molecules/voip/MCallAnswerTile");
var MessageTimestamp = ComponentBroker.get('atoms/MessageTimestamp');
module.exports = React.createClass({
displayName: 'MCallAnswerTile',
mixins: [MCallAnswerTileController],
getAnswerText: function(event) {
var senderName = event.sender ? event.sender.name : "Someone";
return senderName + " answered the call.";
},
render: function() {
// XXX: for now, just cheekily borrow the css from message tile...
return (
<div className="mx_MessageTile mx_MessageTile_notice">
<div className="mx_MessageTile_avatar">
<img src={ this.props.mxEvent.sender ? MatrixClientPeg.get().getAvatarUrlForMember(this.props.mxEvent.sender, 40, 40, "crop") : null } width="40" height="40" alt=""/>
</div>
<MessageTimestamp ts={this.props.mxEvent.getTs()} />
<span className="mx_SenderProfile"></span>
<span className="mx_MessageTile_content">
{this.getAnswerText(this.props.mxEvent)}
</span>
</div>
);
},
});

View File

@@ -0,0 +1,50 @@
/*
Copyright 2015 OpenMarket Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
'use strict';
var React = require('react');
var MatrixClientPeg = require("../../../../../src/MatrixClientPeg");
var ComponentBroker = require('../../../../../src/ComponentBroker');
var MCallHangupTileController = require("../../../../../src/controllers/molecules/voip/MCallHangupTile");
var MessageTimestamp = ComponentBroker.get('atoms/MessageTimestamp');
module.exports = React.createClass({
displayName: 'MCallHangupTile',
mixins: [MCallHangupTileController],
getHangupText: function(event) {
var senderName = event.sender ? event.sender.name : "Someone";
return senderName + " ended the call.";
},
render: function() {
// XXX: for now, just cheekily borrow the css from message tile...
return (
<div className="mx_MessageTile mx_MessageTile_notice">
<div className="mx_MessageTile_avatar">
<img src={ this.props.mxEvent.sender ? MatrixClientPeg.get().getAvatarUrlForMember(this.props.mxEvent.sender, 40, 40, "crop") : null } width="40" height="40" alt=""/>
</div>
<MessageTimestamp ts={this.props.mxEvent.getTs()} />
<span className="mx_SenderProfile"></span>
<span className="mx_MessageTile_content">
{this.getHangupText(this.props.mxEvent)}
</span>
</div>
);
},
});

View File

@@ -0,0 +1,56 @@
/*
Copyright 2015 OpenMarket Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
'use strict';
var React = require('react');
var MatrixClientPeg = require("../../../../../src/MatrixClientPeg");
var ComponentBroker = require('../../../../../src/ComponentBroker');
var MCallInviteTileController = require("../../../../../src/controllers/molecules/voip/MCallInviteTile");
var MessageTimestamp = ComponentBroker.get('atoms/MessageTimestamp');
module.exports = React.createClass({
displayName: 'MCallInviteTile',
mixins: [MCallInviteTileController],
getInviteText: function(event) {
var senderName = event.sender ? event.sender.name : "Someone";
// FIXME: Find a better way to determine this from the event?
var type = "voice";
if (event.getContent().offer &&
event.getContent().offer.sdp.indexOf('m=video') !== -1) {
type = "video";
}
return senderName + " placed a " + type + " call.";
},
render: function() {
// XXX: for now, just cheekily borrow the css from message tile...
return (
<div className="mx_MessageTile mx_MessageTile_notice">
<div className="mx_MessageTile_avatar">
<img src={ this.props.mxEvent.sender ? MatrixClientPeg.get().getAvatarUrlForMember(this.props.mxEvent.sender, 40, 40, "crop") : null } width="40" height="40" alt=""/>
</div>
<MessageTimestamp ts={this.props.mxEvent.getTs()} />
<span className="mx_SenderProfile"></span>
<span className="mx_MessageTile_content">
{this.getInviteText(this.props.mxEvent)}
</span>
</div>
);
},
});

View File

@@ -29,12 +29,12 @@ module.exports = React.createClass({
render: function() {
return (
<aside className="mx_LeftPanel">
<div className="mx_LeftPanel">
<img className="mx_LeftPanel_hideButton" src="img/hide.png" width="32" height="32" alt="<"/>
<IncomingCallBox />
<RoomList selectedRoom={this.props.selectedRoom} />
<BottomLeftMenu />
</aside>
</div>
);
}
});

View File

@@ -23,8 +23,6 @@ var TextForEvent = require("../../../../src/TextForEvent");
var extend = require("../../../../src/extend");
var dis = require("../../../../src/dispatcher");
var Avatar = require("../../../../src/Avatar");
var NotifierView = {
notificationMessageForEvent: function(ev) {
@@ -59,15 +57,11 @@ var NotifierView = {
if (ev.getContent().body) msg = ev.getContent().body;
}
var avatarUrl = Avatar.avatarUrlForMember(
ev.sender, 40, 40, 'crop'
);
var notification = new global.Notification(
title,
{
"body": msg,
"icon": avatarUrl
"icon": MatrixClientPeg.get().getAvatarUrlForMember(ev.sender)
}
);

View File

@@ -66,12 +66,12 @@ module.exports = React.createClass({
}
return (
<aside className="mx_RightPanel">
<div className="mx_RightPanel">
<div className="mx_RightPanel_header">
{ buttonGroup }
</div>
{ panel }
</aside>
</div>
);
}
});

View File

@@ -64,21 +64,11 @@ module.exports = React.createClass({
);
},
onCancelClick: function() {
this.setState(this.getInitialState());
},
getUnreadMessagesString: function() {
if (!this.state.numUnreadMessages) {
return "";
}
return this.state.numUnreadMessages + " new messages";
},
scrollToBottom: function() {
if (!this.refs.messageWrapper) return;
var messageWrapper = this.refs.messageWrapper.getDOMNode();
messageWrapper.scrollTop = messageWrapper.scrollHeight;
return this.state.numUnreadMessages + " unread messages";
},
render: function() {
@@ -144,8 +134,8 @@ module.exports = React.createClass({
// set when you've scrolled up
if (unreadMsgs) {
statusBar = (
<div className="mx_RoomView_unreadMessagesBar" onClick={ this.scrollToBottom }>
<img src="img/newmessages.png" width="10" height="12" alt=""/>
<div className="mx_RoomView_typingBar">
<img src="img/typing.png" width="40" height="40" alt=""/>
{unreadMsgs}
</div>
);
@@ -161,39 +151,30 @@ module.exports = React.createClass({
}
var roomEdit = null;
if (this.state.editingRoomSettings) {
roomEdit = <RoomSettings ref="room_settings" onSaveClick={this.onSaveClick} room={this.state.room} />;
}
if (this.state.uploadingRoomSettings) {
roomEdit = <Loader/>;
}
var fileDropTarget = null;
if (this.state.draggingFile) {
fileDropTarget = <div className="mx_RoomView_fileDropTarget">
<div className="mx_RoomView_fileDropTargetLabel">
<img src="img/upload-big.png" width="46" height="61" alt="Drop File Here"/><br/>
Drop File Here
</div>
</div>;
}
return (
<div className="mx_RoomView">
<RoomHeader ref="header" room={this.state.room} editing={this.state.editingRoomSettings}
onSettingsClick={this.onSettingsClick} onSaveClick={this.onSaveClick} onCancelClick={this.onCancelClick} />
onSettingsClick={this.onSettingsClick}/>
<div className="mx_RoomView_auxPanel">
<CallView room={this.state.room}/>
{ roomEdit }
</div>
<div ref="messageWrapper" className="mx_RoomView_messagePanel" onScroll={ this.onMessageListScroll }>
<div className="mx_RoomView_messageListWrapper">
{ fileDropTarget }
<ol className="mx_RoomView_MessageList" aria-live="polite">
<li className={scrollheader_classes}>
</li>
<div className="mx_RoomView_MessageList" aria-live="polite">
<div className={scrollheader_classes}>
</div>
{this.getEventTiles()}
</ol>
</div>
</div>
</div>
<div className="mx_RoomView_statusArea">

View File

@@ -75,15 +75,15 @@ module.exports = React.createClass({
break;
}
if (Notifier.supportsDesktopNotifications() && !Notifier.isEnabled() && !Notifier.isToolbarHidden()) {
if (Notifier.supportsDesktopNotifications() && !Notifier.isEnabled()) {
return (
<div className="mx_MatrixChat_wrapper">
<MatrixToolbar />
<div className="mx_MatrixChat mx_MatrixChat_toolbarShowing">
<LeftPanel selectedRoom={this.state.currentRoom} />
<main className="mx_MatrixChat_middlePanel">
<div className="mx_MatrixChat_middlePanel">
{page_element}
</main>
</div>
{right_panel}
</div>
</div>
@@ -93,9 +93,9 @@ module.exports = React.createClass({
return (
<div className="mx_MatrixChat">
<LeftPanel selectedRoom={this.state.currentRoom} />
<main className="mx_MatrixChat_middlePanel">
<div className="mx_MatrixChat_middlePanel">
{page_element}
</main>
</div>
{right_panel}
</div>
);

View File

@@ -30,7 +30,7 @@ var ServerConfig = ComponentBroker.get("molecules/ServerConfig");
module.exports = React.createClass({
DEFAULT_HS_URL: 'https://matrix.org',
DEFAULT_IS_URL: 'https://vector.im',
DEFAULT_IS_URL: 'https://matrix.org',
displayName: 'Login',
mixins: [LoginController],

View File

@@ -28,7 +28,7 @@ var ServerConfig = ComponentBroker.get("molecules/ServerConfig");
module.exports = React.createClass({
DEFAULT_HS_URL: 'https://matrix.org',
DEFAULT_IS_URL: 'https://vector.im',
DEFAULT_IS_URL: 'https://matrix.org',
displayName: 'Register',
mixins: [RegisterController],

View File

@@ -1,50 +0,0 @@
/*
Copyright 2015 OpenMarket Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
'use strict';
var MatrixClientPeg = require('./MatrixClientPeg');
module.exports = {
avatarUrlForMember: function(member, width, height, resizeMethod) {
var url = MatrixClientPeg.get().getAvatarUrlForMember(
member, width, height, resizeMethod, false
);
if (url === null) {
url = this.defaultAvatarUrlForString(member.userId);
}
return url;
},
defaultAvatarUrlForString: function(s) {
var total = 0;
for (var i = 0; i < s.length; ++i) {
total += s.charCodeAt(i);
}
switch (total % 3) {
case 0:
return "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAIAAAADnC86AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAADRJREFUeNrszQENADAIACB9QjNbxSKP4eagAFnTseHFErFYLBaLxWKxWCwWi8Vi8cX4CzAABSwCRWJw31gAAAAASUVORK5CYII=";
break;
case 1:
return "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAIAAAADnC86AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAADRJREFUeNrszQENADAIACB9chOaxgCP4eagAFk9seHFErFYLBaLxWKxWCwWi8Vi8cX4CzAAtKMCks/JG8MAAAAASUVORK5CYII=";
break;
case 2:
return "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAIAAAADnC86AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAADRJREFUeNrszQENADAIACB9YzNayQCP4eagADldseHFErFYLBaLxWKxWCwWi8Vi8cX4CzAAyiACeHwPiu4AAAAASUVORK5CYII=";
break;
}
}
}

View File

@@ -59,6 +59,7 @@ var ComponentBroker = require('./ComponentBroker');
var ErrorDialog = ComponentBroker.get("organisms/ErrorDialog");
var Matrix = require("matrix-js-sdk");
var dis = require("./dispatcher");
var q = require("q");
var calls = {
//room_id: MatrixCall
@@ -150,6 +151,29 @@ function _setCallState(call, roomId, status) {
});
}
function findOrMakeVertoRoom() {
var VERTO_USERID = '@verto_1234:matrix.org';
var defer = q.defer();
var allRooms = MatrixClientPeg.get().getRooms();
for (var i = 0; i < Object.keys(allRooms).length; ++i) {
var r = allRooms[i];
var membs = r.getJoinedMembers();
if (membs.length == 2 && (membs[0].userId == VERTO_USERID || membs[0].userId == VERTO_USERID)) {
defer.resolve(r);
return defer.promise;
}
}
MatrixClientPeg.get().createRoom({
invite: [ VERTO_USERID ]
}).done(function(result) {
defer.resolve(MatrixClientPeg.get().getRoom(result.room_id));
});
return defer.promise;
}
dis.register(function(payload) {
switch (payload.action) {
case 'place_call':
@@ -162,7 +186,29 @@ dis.register(function(payload) {
return;
}
var members = room.getJoinedMembers();
if (members.length !== 2) {
if (members.length > 2) {
var vertoRoom = findOrMakeVertoRoom().done(function(r) {
console.log("Place %s conference call in %s", payload.type, payload.room_id);
var call = Matrix.createNewMatrixCall(
MatrixClientPeg.get(), r.roomId
);
_setCallListeners(call);
_setCallState(call, call.roomId, "ringback");
if (payload.type === 'voice') {
call.placeVoiceCall();
}
else if (payload.type === 'video') {
call.placeVideoCall(
payload.remote_element,
payload.local_element
);
}
else {
console.error("Unknown call type: %s", payload.type);
}
});
return;
} else if (members.length !== 2) {
var text = members.length === 1 ? "yourself." : "more than 2 people.";
Modal.createDialog(ErrorDialog, {
description: "You cannot place a call with " + text
@@ -227,4 +273,4 @@ module.exports = {
getCall: function(roomId) {
return calls[roomId] || null;
}
};
};

View File

@@ -75,6 +75,7 @@ require('../skins/base/views/molecules/MNoticeTile');
require('../skins/base/views/molecules/MEmoteTile');
require('../skins/base/views/molecules/MImageTile');
require('../skins/base/views/molecules/MFileTile');
require('../skins/base/views/molecules/MRoomMemberTile');
require('../skins/base/views/molecules/RoomHeader');
require('../skins/base/views/molecules/MessageComposer');
require('../skins/base/views/molecules/ProgressBar');
@@ -102,14 +103,14 @@ require('../skins/base/views/molecules/RoomDropTarget');
require('../skins/base/views/molecules/BottomLeftMenu');
require('../skins/base/views/molecules/DateSeparator');
require('../skins/base/views/atoms/voip/VideoFeed');
require('../skins/base/views/atoms/MemberAvatar');
require('../skins/base/views/atoms/RoomAvatar');
require('../skins/base/views/atoms/ImageView');
require('../skins/base/views/molecules/voip/VideoView');
require('../skins/base/views/molecules/voip/CallView');
require('../skins/base/views/molecules/voip/IncomingCallBox');
require('../skins/base/views/molecules/voip/MCallInviteTile');
require('../skins/base/views/molecules/voip/MCallAnswerTile');
require('../skins/base/views/molecules/voip/MCallHangupTile');
require('../skins/base/views/molecules/EventAsTextTile');
require('../skins/base/views/molecules/HealthDemoTile');
require('../skins/base/views/molecules/MemberInfo');
require('../skins/base/views/organisms/ErrorDialog');
require('../skins/base/views/organisms/QuestionDialog');

View File

@@ -1,22 +0,0 @@
var MatrixClientPeg = require('./MatrixClientPeg');
module.exports = {
/**
* Given a room object, return the canonical alias for it
* if there is one. Otherwise return null;
*/
getCanonicalAliasForRoom: function(room) {
var aliasEvents = room.currentState.getStateEvents(
"m.room.aliases"
);
// Canonical aliases aren't implemented yet, so just return the first
for (var j = 0; j < aliasEvents.length; j++) {
var aliases = aliasEvents[j].getContent().aliases;
if (aliases && aliases.length) {
return aliases[0];
}
}
return null;
}
}

View File

@@ -67,34 +67,10 @@ function textForMessageEvent(ev) {
return message;
};
function textForCallAnswerEvent(event) {
var senderName = event.sender ? event.sender.name : "Someone";
return senderName + " answered the call.";
};
function textForCallHangupEvent(event) {
var senderName = event.sender ? event.sender.name : "Someone";
return senderName + " ended the call.";
};
function textForCallInviteEvent(event) {
var senderName = event.sender ? event.sender.name : "Someone";
// FIXME: Find a better way to determine this from the event?
var type = "voice";
if (event.getContent().offer && event.getContent().offer.sdp &&
event.getContent().offer.sdp.indexOf('m=video') !== -1) {
type = "video";
}
return senderName + " placed a " + type + " call.";
};
var handlers = {
'm.room.message': textForMessageEvent,
'm.room.topic': textForTopicEvent,
'm.room.member': textForMemberEvent,
'm.call.invite': textForCallInviteEvent,
'm.call.answer': textForCallAnswerEvent,
'm.call.hangup': textForCallHangupEvent,
'm.room.member': textForMemberEvent
};
module.exports = {

View File

@@ -1,64 +0,0 @@
/*
Copyright 2015 OpenMarket Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
'use strict';
var Avatar = require('../../Avatar');
var React = require('react');
module.exports = {
propTypes: {
member: React.PropTypes.object.isRequired,
width: React.PropTypes.number,
height: React.PropTypes.number,
resizeMethod: React.PropTypes.string,
},
getDefaultProps: function() {
return {
width: 40,
height: 40,
resizeMethod: 'crop'
}
},
defaultAvatarUrl: function(member) {
return Avatar.defaultAvatarUrlForString(
member.userId
);
},
onError: function(ev) {
// don't tightloop if the browser can't load a data url
if (ev.target.src == this.defaultAvatarUrl(this.props.member)) {
return;
}
this.setState({
imageUrl: this.defaultAvatarUrl(this.props.member)
});
},
getInitialState: function() {
return {
imageUrl: Avatar.avatarUrlForMember(
this.props.member,
this.props.width, this.props.height,
this.props.resizeMethod
)
};
}
};

View File

@@ -1,64 +0,0 @@
/*
Copyright 2015 OpenMarket Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
'use strict';
var MatrixClientPeg = require('../../MatrixClientPeg');
var Avatar = require('../../Avatar');
module.exports = {
getDefaultProps: function() {
return {
width: 40,
height: 40,
resizeMethod: 'crop'
}
},
avatarUrlForRoom: function(room) {
var url = MatrixClientPeg.get().getAvatarUrlForRoom(
room,
this.props.width, this.props.height, this.props.resizeMethod,
false
);
if (url === null) {
url = this.defaultAvatarUrl(room);
}
return url;
},
defaultAvatarUrl: function(room) {
return Avatar.defaultAvatarUrlForString(
this.props.room.roomId
);
},
onError: function(ev) {
// don't tightloop if the browser can't load a data url
if (ev.target.src == this.defaultAvatarUrl(this.props.room)) {
return;
}
this.setState({
imageUrl: this.defaultAvatarUrl(this.props.room)
});
},
getInitialState: function() {
return {
imageUrl: this.avatarUrlForRoom(this.props.room)
};
}
};

View File

@@ -293,7 +293,7 @@ module.exports = {
};
var canAffectUser = them.powerLevel < me.powerLevel;
if (!canAffectUser) {
//console.log("Cannot affect user: %s >= %s", them.powerLevel, me.powerLevel);
console.log("Cannot affect user: %s >= %s", them.powerLevel, me.powerLevel);
return can;
}
var editPowerLevel = (

View File

@@ -182,12 +182,12 @@ module.exports = {
var self = this;
setTimeout(function() {
if (self.refs.textarea && self.refs.textarea.getDOMNode().value != '') {
if (self.refs.textarea.getDOMNode().value != '') {
self.onTypingActivity();
} else {
self.onFinishedTyping();
}
}, 10); // XXX: what is this 10ms setTimeout doing? Looks hacky :(
}, 10);
},
onEnter: function(ev) {

View File

@@ -14,9 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
.mx_MemberAvatar {
z-index: 20;
border-radius: 20px;
background-color: #dbdbdb;
}
'use strict';
module.exports = {
};

View File

@@ -14,25 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
.mx_HealthDemoTile {
border: 1px solid black;
width: 200px;
height: 70px;
margin: 5px;
background-color: #ddd;
padding: 5px;
margin-top: 20px;
}
.mx_HealthDemoTile .mx_HealthDemoTile_ts {
display: block;
text-align: right;
font-size: 85%;
}
.mx_HealthDemoTile_content {
text-align: right;
font-size: 200%;
font-weight: bold;
}
'use strict';
module.exports = {
};

View File

@@ -16,19 +16,5 @@ limitations under the License.
'use strict';
var React = require('react');
var RoomAvatarController = require("../../../../src/controllers/atoms/RoomAvatar");
module.exports = React.createClass({
displayName: 'RoomAvatar',
mixins: [RoomAvatarController],
render: function() {
return (
<img className="mx_RoomAvatar" src={this.state.imageUrl} onError={this.onError}
width={this.props.width} height={this.props.height}
/>
);
}
});
module.exports = {
};

View File

@@ -27,14 +27,10 @@ var dis = require("../../dispatcher");
* }
*/
// XXX: This isn't an organism surely in the atomic sense of the word
// what on earth is it doing here?!
module.exports = {
start: function() {
this.boundOnRoomTimeline = this.onRoomTimeline.bind(this);
MatrixClientPeg.get().on('Room.timeline', this.boundOnRoomTimeline);
this.state = { 'toolbarHidden' : false };
},
stop: function() {
@@ -86,8 +82,6 @@ module.exports = {
value: false
});
}
this.setToolbarHidden(false);
},
isEnabled: function() {
@@ -100,18 +94,6 @@ module.exports = {
return enabled === 'true';
},
setToolbarHidden: function(hidden) {
this.state.toolbarHidden = hidden;
dis.dispatch({
action: "notifier_enabled",
value: this.isEnabled()
});
},
isToolbarHidden: function() {
return this.state.toolbarHidden;
},
onRoomTimeline: function(ev, room, toStartOfTimeline) {
if (toStartOfTimeline) return;
if (ev.sender && ev.sender.userId == MatrixClientPeg.get().credentials.userId) return;

View File

@@ -73,11 +73,13 @@ module.exports = {
if (actions && actions.tweaks && actions.tweaks.highlight) {
hl = 2;
}
// obviously this won't deep copy but this shouldn't be necessary
var amap = this.state.activityMap;
amap[room.roomId] = Math.max(amap[room.roomId] || 0, hl);
if (actions.notify) {
// obviously this won't deep copy but this shouldn't be necessary
var amap = this.state.activityMap;
amap[room.roomId] = Math.max(amap[room.roomId] || 0, hl);
newState.activityMap = amap;
newState.activityMap = amap;
}
}
this.setState(newState);
},

View File

@@ -36,12 +36,11 @@ var Notifier = ComponentBroker.get('organisms/Notifier');
var tileTypes = {
'm.room.message': ComponentBroker.get('molecules/MessageTile'),
'm.room.member' : ComponentBroker.get('molecules/EventAsTextTile'),
'm.call.invite' : ComponentBroker.get('molecules/EventAsTextTile'),
'm.call.answer' : ComponentBroker.get('molecules/EventAsTextTile'),
'm.call.hangup' : ComponentBroker.get('molecules/EventAsTextTile'),
'm.room.topic' : ComponentBroker.get('molecules/EventAsTextTile'),
'org.matrix.demo.health' : ComponentBroker.get('molecules/HealthDemoTile'),
'm.room.member': ComponentBroker.get('molecules/MRoomMemberTile'),
'm.call.invite': ComponentBroker.get('molecules/voip/MCallInviteTile'),
'm.call.answer': ComponentBroker.get('molecules/voip/MCallAnswerTile'),
'm.call.hangup': ComponentBroker.get('molecules/voip/MCallHangupTile'),
'm.room.topic': ComponentBroker.get('molecules/EventAsTextTile'),
};
var DateSeparator = ComponentBroker.get('molecules/DateSeparator');
@@ -53,8 +52,7 @@ module.exports = {
messageCap: INITIAL_SIZE,
editingRoomSettings: false,
uploadingRoomSettings: false,
numUnreadMessages: 0,
draggingFile: false,
numUnreadMessages: 0
}
},
@@ -71,8 +69,6 @@ module.exports = {
var messageWrapper = this.refs.messageWrapper.getDOMNode();
messageWrapper.removeEventListener('drop', this.onDrop);
messageWrapper.removeEventListener('dragover', this.onDragOver);
messageWrapper.removeEventListener('dragleave', this.onDragLeaveOrEnd);
messageWrapper.removeEventListener('dragend', this.onDragLeaveOrEnd);
}
dis.unregister(this.dispatcherRef);
if (MatrixClientPeg.get()) {
@@ -177,8 +173,6 @@ module.exports = {
messageWrapper.addEventListener('drop', this.onDrop);
messageWrapper.addEventListener('dragover', this.onDragOver);
messageWrapper.addEventListener('dragleave', this.onDragLeaveOrEnd);
messageWrapper.addEventListener('dragend', this.onDragLeaveOrEnd);
messageWrapper.scrollTop = messageWrapper.scrollHeight;
@@ -207,7 +201,6 @@ module.exports = {
},
fillSpace: function() {
if (!this.refs.messageWrapper) return;
var messageWrapper = this.refs.messageWrapper.getDOMNode();
if (messageWrapper.scrollTop < messageWrapper.clientHeight && this.state.room.oldState.paginationToken) {
this.setState({paginating: true});
@@ -278,7 +271,6 @@ module.exports = {
var items = ev.dataTransfer.items;
if (items.length == 1) {
if (items[0].kind == 'file') {
this.setState({ draggingFile : true });
ev.dataTransfer.dropEffect = 'copy';
}
}
@@ -287,19 +279,12 @@ module.exports = {
onDrop: function(ev) {
ev.stopPropagation();
ev.preventDefault();
this.setState({ draggingFile : false });
var files = ev.dataTransfer.files;
if (files.length == 1) {
this.uploadFile(files[0]);
}
},
onDragLeaveOrEnd: function(ev) {
ev.stopPropagation();
ev.preventDefault();
this.setState({ draggingFile : false });
},
uploadFile: function(file) {
this.setState({
upload: {
@@ -367,8 +352,7 @@ module.exports = {
}
if (!TileType) continue;
ret.unshift(
// XXX: don't wrap everything in a needless li - make the TileType a li if we must :(
<li key={mxEv.getId()}><TileType mxEvent={mxEv} continuation={continuation} last={last}/></li>
<TileType key={mxEv.getId()} mxEvent={mxEv} continuation={continuation} last={last}/>
);
if (dateSeparator) {
ret.unshift(dateSeparator);

View File

@@ -23,11 +23,9 @@ var MatrixClientPeg = require("../../MatrixClientPeg");
var RoomListSorter = require("../../RoomListSorter");
var Presence = require("../../Presence");
var dis = require("../../dispatcher");
var q = require("q");
var ComponentBroker = require('../../ComponentBroker');
var Notifier = ComponentBroker.get('organisms/Notifier');
var MatrixTools = require('../../MatrixTools');
module.exports = {
PageTypes: {
@@ -138,20 +136,7 @@ module.exports = {
currentRoom: payload.room_id,
page_type: this.PageTypes.RoomView,
});
if (this.sdkReady) {
// if the SDK is not ready yet, remember what room
// we're supposed to be on but don't notify about
// the new screen yet (we won't be showing it yet)
// The normal case where this happens is navigating
// to the room in the URL bar on page load.
var presentedId = payload.room_id;
var room = MatrixClientPeg.get().getRoom(payload.room_id);
if (room) {
var theAlias = MatrixTools.getCanonicalAliasForRoom(room);
if (theAlias) presentedId = theAlias;
}
this.notifyNewScreen('room/'+presentedId);
}
this.notifyNewScreen('room/'+payload.room_id);
break;
case 'view_prev_room':
roomIndexDelta = -1;
@@ -167,26 +152,11 @@ module.exports = {
}
}
roomIndex = (roomIndex + roomIndexDelta) % allRooms.length;
if (roomIndex < 0) roomIndex = allRooms.length - 1;
this.focusComposer = true;
this.setState({
currentRoom: allRooms[roomIndex].roomId
});
this.notifyNewScreen('room/'+allRooms[roomIndex].roomId);
break;
case 'view_indexed_room':
var allRooms = RoomListSorter.mostRecentActivityFirst(
MatrixClientPeg.get().getRooms()
);
var roomIndex = payload.roomIndex;
if (allRooms[roomIndex]) {
this.focusComposer = true;
this.setState({
currentRoom: allRooms[roomIndex].roomId
});
this.notifyNewScreen('room/'+allRooms[roomIndex].roomId);
}
break;
case 'view_user_settings':
this.setState({
page_type: this.PageTypes.UserSettings,
@@ -221,30 +191,20 @@ module.exports = {
var cli = MatrixClientPeg.get();
var self = this;
cli.on('syncComplete', function() {
self.sdkReady = true;
if (!self.state.currentRoom) {
var firstRoom = null;
if (cli.getRooms() && cli.getRooms().length) {
firstRoom = RoomListSorter.mostRecentActivityFirst(
cli.getRooms()
)[0].roomId;
self.setState({ready: true, currentRoom: firstRoom, page_type: self.PageTypes.RoomView});
self.setState({ready: true, currentRoom: firstRoom});
self.notifyNewScreen('room/'+firstRoom);
} else {
self.setState({ready: true, page_type: self.PageTypes.RoomDirectory});
self.setState({ready: true});
}
} else {
self.setState({ready: true, currentRoom: self.state.currentRoom});
self.setState({ready: true});
}
// we notifyNewScreen now because now the room will actually be displayed,
// and (mostly) now we can get the correct alias.
var presentedId = self.state.currentRoom;
var room = MatrixClientPeg.get().getRoom(self.state.currentRoom);
if (room) {
var theAlias = MatrixTools.getCanonicalAliasForRoom(room);
if (theAlias) presentedId = theAlias;
}
self.notifyNewScreen('room/'+presentedId);
dis.dispatch({action: 'focus_composer'});
});
cli.on('Call.incoming', function(call) {
@@ -260,25 +220,14 @@ module.exports = {
onKeyDown: function(ev) {
if (ev.altKey) {
if (ev.ctrlKey && ev.keyCode > 48 && ev.keyCode < 58) {
dis.dispatch({
action: 'view_indexed_room',
roomIndex: ev.keyCode - 49,
});
ev.stopPropagation();
ev.preventDefault();
return;
}
switch (ev.keyCode) {
case 38:
dis.dispatch({action: 'view_prev_room'});
ev.stopPropagation();
ev.preventDefault();
break;
case 40:
dis.dispatch({action: 'view_next_room'});
ev.stopPropagation();
ev.preventDefault();
break;
}
}
@@ -300,26 +249,10 @@ module.exports = {
params: params
});
} else if (screen.indexOf('room/') == 0) {
var roomString = screen.split('/')[1];
var defer = q.defer();
if (roomString[0] == '#') {
var self = this;
MatrixClientPeg.get().getRoomIdForAlias(roomString).done(function(result) {
if (self.sdkReady) self.setState({ready: true});
defer.resolve(result.room_id);
}, function() {
if (self.sdkReady) self.setState({ready: true});
defer.resolve(null);
});
this.setState({ready: false});
} else {
defer.resolve(roomString);
}
defer.promise.done(function(roomId) {
dis.dispatch({
action: 'view_room',
room_id: roomId
});
var roomId = screen.split('/')[1];
dis.dispatch({
action: 'view_room',
room_id: roomId
});
}
},

View File

@@ -71,17 +71,10 @@ module.exports = {
var formVals = this.getFormVals();
var loginParams = {
password: formVals.password
};
if (formVals.username.indexOf('@') > 0) {
loginParams.medium = 'email';
loginParams.address = formVals.username;
} else {
loginParams.user = formVals.username;
}
MatrixClientPeg.get().login('m.login.password', loginParams).done(function(data) {
MatrixClientPeg.get().login('m.login.password', {
'user': formVals.username,
'password': formVals.password
}).done(function(data) {
MatrixClientPeg.replaceUsingAccessToken(
self.state.hs_url, self.state.is_url,
data.user_id, data.access_token
@@ -91,11 +84,7 @@ module.exports = {
}
}, function(error) {
self.setStep("stage_m.login.password");
if (error.httpStatus == 400 && loginParams.medium) {
self.setState({errorText: 'This Home Server does not support login using email address.'});
} else {
self.setState({errorText: 'Login failed.'});
}
self.setState({errorText: 'Login failed.'});
});
},