Merge branch 'develop' into kegan/delete-empty-files

Conflicts:
	src/skins/vector/views/molecules/EventAsTextTile.js
This commit is contained in:
Kegan Dougal 2015-10-27 09:19:08 +00:00
commit c91b642a8b
23 changed files with 353 additions and 143 deletions

View File

@ -22,14 +22,27 @@ into the `vector` directory and run your own server.
Development
===========
You can work on any of the source files within Vector with the setup above,
and your changes will cause an instant rebuild. If you also need to make
changes to the react sdk, you can:
1. Link the react sdk package into the example:
For simple tweaks, you can work on any of the source files within Vector with the
setup above, and your changes will cause an instant rebuild.
However, all serious development on Vector happens on the `develop` branch. This typically
depends on the `develop` snapshot versions of `matrix-react-sdk` and `matrix-js-sdk`
too, which isn't expressed in Vector's `package.json`. To do this, check out
the `develop` branches of these libraries and then use `npm link` to tell Vector
about them:
1. `git clone git@github.com:matrix-org/matrix-react-sdk.git`
2. `cd matrix-react-sdk`
3. `git checkout develop`
4. `npm install`
5. `npm start` (to start the dev rebuilder)
6. `cd ../vector-web`
7. Link the react sdk package into the example:
`npm link path/to/your/react/sdk`
2. Start the development rebuilder in your react SDK directory:
`npm start`
Similarly, you may need to `npm link path/to/your/js/sdk` in your `matrix-react-sdk`
directory.
If you add or remove any components from the Vector skin, you will need to rebuild
the skin's index by running, `npm run reskindex`.

View File

@ -49,15 +49,25 @@ module.exports = {
var position = {
top: props.top - 20,
right: props.right + 8,
};
var chevron = null;
if (props.left) {
chevron = <img className="mx_ContextualMenu_chevron_left" src="img/chevron-left.png" width="9" height="16" />
position.left = props.left + 8;
} else {
chevron = <img className="mx_ContextualMenu_chevron_right" src="img/chevron-right.png" width="9" height="16" />
position.right = props.right + 8;
}
var className = 'mx_ContextualMenu_wrapper';
// FIXME: If a menu uses getDefaultProps it clobbers the onFinished
// property set here so you can't close the menu from a button click!
var menu = (
<div className="mx_ContextualMenu_wrapper">
<div className={className}>
<div className="mx_ContextualMenu" style={position}>
<img className="mx_ContextualMenu_chevron" src="img/chevron-right.png" width="9" height="16" />
{chevron}
<Element {...props} onFinished={closeMenu}/>
</div>
<div className="mx_ContextualMenu_background" onClick={closeMenu}></div>

View File

@ -33,6 +33,7 @@ module.exports = {
cli.on("Room", this.onRoom);
cli.on("Room.timeline", this.onRoomTimeline);
cli.on("Room.name", this.onRoomName);
cli.on("RoomState.events", this.onRoomStateEvents);
var rooms = this.getRoomList();
this.setState({
@ -66,6 +67,7 @@ module.exports = {
MatrixClientPeg.get().removeListener("Room", this.onRoom);
MatrixClientPeg.get().removeListener("Room.timeline", this.onRoomTimeline);
MatrixClientPeg.get().removeListener("Room.name", this.onRoomName);
MatrixClientPeg.get().removeListener("RoomState.events", this.onRoomStateEvents);
}
},
@ -110,6 +112,11 @@ module.exports = {
this.refreshRoomList();
},
onRoomStateEvents: function(ev, state) {
setTimeout(this.refreshRoomList, 0);
},
refreshRoomList: function() {
var rooms = this.getRoomList();
this.setState({

View File

@ -63,6 +63,7 @@ module.exports = {
MatrixClientPeg.get().removeListener("Room.timeline", this.onRoomTimeline);
MatrixClientPeg.get().removeListener("Room.name", this.onRoomName);
MatrixClientPeg.get().removeListener("RoomMember.typing", this.onRoomMemberTyping);
MatrixClientPeg.get().removeListener("RoomState.members", this.onRoomStateMember);
}
},
@ -356,23 +357,20 @@ module.exports = {
},
getEventTiles: function() {
var tileTypes = {
'm.room.message': sdk.getComponent('molecules.MessageTile'),
'm.room.member' : sdk.getComponent('molecules.EventAsTextTile'),
'm.call.invite' : sdk.getComponent('molecules.EventAsTextTile'),
'm.call.answer' : sdk.getComponent('molecules.EventAsTextTile'),
'm.call.hangup' : sdk.getComponent('molecules.EventAsTextTile'),
'm.room.topic' : sdk.getComponent('molecules.EventAsTextTile'),
};
var DateSeparator = sdk.getComponent('molecules.DateSeparator');
var ret = [];
var count = 0;
var EventTile = sdk.getComponent('molecules.EventTile');
for (var i = this.state.room.timeline.length-1; i >= 0 && count < this.state.messageCap; --i) {
var mxEv = this.state.room.timeline[i];
var TileType = tileTypes[mxEv.getType()];
if (!EventTile.supportsEventType(mxEv.getType())) {
continue;
}
var continuation = false;
var last = false;
var dateSeparator = null;
@ -401,13 +399,12 @@ module.exports = {
if (i === 1) { // n.b. 1, not 0, as the 0th event is an m.room.create and so doesn't show on the timeline
var ts1 = this.state.room.timeline[i].getTs();
dateSeparator = <DateSeparator key={ts1} ts={ts1}/>;
dateSeparator = <li key={ts1}><DateSeparator ts={ts1}/></li>;
continuation = false;
}
if (!TileType) continue;
ret.unshift(
<li key={mxEv.getId()}><TileType mxEvent={mxEv} continuation={continuation} last={last}/></li>
<li key={mxEv.getId()}><EventTile mxEvent={mxEv} continuation={continuation} last={last}/></li>
);
if (dateSeparator) {
ret.unshift(dateSeparator);

View File

@ -67,13 +67,20 @@ a:visited {
padding: 6px;
}
.mx_ContextualMenu_chevron {
.mx_ContextualMenu_chevron_right {
padding: 12px;
position: absolute;
right: -21px;
top: 0px;
}
.mx_ContextualMenu_chevron_left {
padding: 12px;
position: absolute;
left: -21px;
top: 0px;
}
.mx_ContextualMenu_field {
padding: 3px 6px 3px 6px;
cursor: pointer;

View File

@ -14,14 +14,14 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
.mx_MessageTile {
.mx_EventTile {
max-width: 100%;
clear: both;
margin-top: 32px;
margin-left: 56px;
}
.mx_MessageTile_avatar {
.mx_EventTile_avatar {
padding-left: 12px;
padding-right: 12px;
margin-left: -64px;
@ -29,17 +29,17 @@ limitations under the License.
float: left;
}
.mx_MessageTile_avatar img {
.mx_EventTile_avatar img {
background-color: #dbdbdb;
border-radius: 20px;
border: 0px;
}
.mx_MessageTile_continuation {
.mx_EventTile_continuation {
margin-top: 8px ! important;
}
.mx_MessageTile .mx_SenderProfile {
.mx_EventTile .mx_SenderProfile {
color: #454545;
opacity: 0.5;
font-size: 14px;
@ -47,35 +47,35 @@ limitations under the License.
display: block;
}
.mx_MessageTile .mx_MessageTimestamp {
.mx_EventTile .mx_MessageTimestamp {
color: #454545;
opacity: 0.5;
font-size: 14px;
float: right;
}
.mx_MessageTile_content {
.mx_EventTile_content {
padding-right: 100px;
display: block;
}
.mx_MessageTile_notice .mx_MessageTile_content {
.mx_EventTile_notice .mx_MessageTile_content {
opacity: 0.5;
}
.mx_MessageTile_sending {
.mx_EventTile_sending {
color: #ddd;
}
.mx_MessageTile_notSent {
.mx_EventTile_notSent {
color: #f11;
}
.mx_MessageTile_highlight {
.mx_EventTile_highlight {
color: #FF0064;
}
.mx_MessageTile_msgOption {
.mx_EventTile_msgOption {
float: right;
}
@ -83,29 +83,30 @@ limitations under the License.
display: none;
}
.mx_MessageTile_last .mx_MessageTimestamp {
.mx_EventTile_last .mx_MessageTimestamp {
display: block;
}
.mx_MessageTile:hover .mx_MessageTimestamp {
.mx_EventTile:hover .mx_MessageTimestamp {
display: block;
}
.mx_MessageTile_editButton {
.mx_EventTile_editButton {
float: right;
display: none;
border: 0px;
outline: none;
margin-right: 3px;
}
.mx_MessageTile:hover .mx_MessageTile_editButton {
.mx_EventTile:hover .mx_EventTile_editButton {
display: inline-block;
}
.mx_MessageTile.menu .mx_MessageTile_editButton {
.mx_EventTile.menu .mx_EventTile_editButton {
display: inline-block;
}
.mx_MessageTile.menu .mx_MessageTimestamp {
.mx_EventTile.menu .mx_MessageTimestamp {
display: inline-block;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 146 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 146 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 146 B

View File

@ -41,6 +41,7 @@ skin['molecules.ChangeDisplayName'] = require('./views/molecules/ChangeDisplayNa
skin['molecules.ChangePassword'] = require('./views/molecules/ChangePassword');
skin['molecules.DateSeparator'] = require('./views/molecules/DateSeparator');
skin['molecules.EventAsTextTile'] = require('./views/molecules/EventAsTextTile');
skin['molecules.EventTile'] = require('./views/molecules/EventTile');
skin['molecules.MatrixToolbar'] = require('./views/molecules/MatrixToolbar');
skin['molecules.MemberInfo'] = require('./views/molecules/MemberInfo');
skin['molecules.MemberTile'] = require('./views/molecules/MemberTile');

View File

@ -42,7 +42,7 @@ module.exports = React.createClass({
return days[date.getDay()] + ", " + months[date.getMonth()] + " " + (date.getDay()+1) + " " + pad(date.getHours()) + ':' + pad(date.getMinutes());
}
else {
return days[date.getDay()] + ", " + months[date.getMonth()] + " " + (date.getDay()+1) + " " + date().getFullYear() + " " + pad(date.getHours()) + ':' + pad(date.getMinutes());
return days[date.getDay()] + ", " + months[date.getMonth()] + " " + (date.getDay()+1) + " " + date.getFullYear() + " " + pad(date.getHours()) + ':' + pad(date.getMinutes());
}
},

View File

@ -17,6 +17,7 @@ limitations under the License.
'use strict';
var React = require('react');
var MatrixClientPeg = require('matrix-react-sdk/lib/MatrixClientPeg');
var RoomAvatarController = require('matrix-react-sdk/lib/controllers/atoms/RoomAvatar')
@ -24,10 +25,31 @@ module.exports = React.createClass({
displayName: 'RoomAvatar',
mixins: [RoomAvatarController],
getUrlList: function() {
return [
this.roomAvatarUrl(),
this.getOneToOneAvatar(),
this.getFallbackAvatar()
];
},
getFallbackAvatar: function() {
var images = [ '80cef4', '50e2c2', 'f4c371' ];
var total = 0;
for (var i = 0; i < this.props.room.roomId.length; ++i) {
total += this.props.room.roomId.charCodeAt(i);
}
return 'img/' + images[total % images.length] + '.png';
},
render: function() {
var style = {
maxWidth: this.props.width,
maxHeight: this.props.height,
};
return (
<img className="mx_RoomAvatar" src={this.state.imageUrl} onError={this.onError}
width={this.props.width} height={this.props.height}
style={style}
/>
);
}

View File

@ -18,6 +18,7 @@ limitations under the License.
var React = require('react');
var sdk = require('matrix-react-sdk')
var ChangeAvatarController = require('matrix-react-sdk/lib/controllers/molecules/ChangeAvatar')
var Loader = require("react-loader");
@ -28,6 +29,7 @@ module.exports = React.createClass({
mixins: [ChangeAvatarController],
onFileSelected: function(ev) {
this.avatarSet = true;
this.setAvatarFromFile(ev.target.files[0]);
},
@ -38,13 +40,27 @@ module.exports = React.createClass({
},
render: function() {
var RoomAvatar = sdk.getComponent('atoms.RoomAvatar');
var avatarImg;
// Having just set an avatar we just display that since it will take a little
// time to propagate through to the RoomAvatar.
if (this.props.room && !this.avatarSet) {
avatarImg = <RoomAvatar room={this.props.room} width='320' height='240' resizeMethod='scale' />;
} else {
var style = {
maxWidth: 320,
maxHeight: 240,
};
avatarImg = <img src={this.state.avatarUrl} style={style} />;
}
switch (this.state.phase) {
case this.Phases.Display:
case this.Phases.Error:
return (
<div>
<div className="mx_Dialog_content">
<img src={this.state.avatarUrl}/>
{avatarImg}
</div>
<div className="mx_Dialog_content">
Upload new:

View File

@ -24,25 +24,19 @@ var TextForEvent = require('matrix-react-sdk/lib/TextForEvent');
module.exports = React.createClass({
displayName: 'EventAsTextTile',
render: function() {
var MessageTimestamp = sdk.getComponent('atoms.MessageTimestamp');
var MemberAvatar = sdk.getComponent('atoms.MemberAvatar');
statics: {
needsSenderProfile: function() {
return false;
}
},
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 }
</div>
{ timestamp }
<span className="mx_SenderProfile"></span>
<span className="mx_MessageTile_content">
<div className="mx_EventAsTextTile">
{TextForEvent.textForEvent(this.props.mxEvent)}
</span>
</div>
);
},

View File

@ -0,0 +1,133 @@
/*
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 classNames = require("classnames");
var sdk = require('matrix-react-sdk')
var EventTileController = require('matrix-react-sdk/lib/controllers/molecules/EventTile')
var ContextualMenu = require('../../../../ContextualMenu');
var eventTileTypes = {
'm.room.message': 'molecules.MessageTile',
'm.room.member' : 'molecules.EventAsTextTile',
'm.call.invite' : 'molecules.EventAsTextTile',
'm.call.answer' : 'molecules.EventAsTextTile',
'm.call.hangup' : 'molecules.EventAsTextTile',
'm.room.topic' : 'molecules.EventAsTextTile',
};
module.exports = React.createClass({
displayName: 'EventTile',
mixins: [EventTileController],
statics: {
supportsEventType: function(et) {
return eventTileTypes[et] !== undefined;
}
},
getInitialState: function() {
return {menu: false};
},
onEditClicked: function(e) {
var MessageContextMenu = sdk.getComponent('molecules.MessageContextMenu');
var buttonRect = e.target.getBoundingClientRect()
var x = buttonRect.right;
var y = buttonRect.top + (e.target.height / 2);
var self = this;
ContextualMenu.createMenu(MessageContextMenu, {
mxEvent: this.props.mxEvent,
left: x,
top: y,
onFinished: function() {
self.setState({menu: false});
}
});
this.setState({menu: true});
},
render: function() {
var MessageTimestamp = sdk.getComponent('atoms.MessageTimestamp');
var SenderProfile = sdk.getComponent('molecules.SenderProfile');
var MemberAvatar = sdk.getComponent('atoms.MemberAvatar');
var UnknownMessageTile = sdk.getComponent('molecules.UnknownMessageTile');
var content = this.props.mxEvent.getContent();
var msgtype = content.msgtype;
var EventTileType = sdk.getComponent(eventTileTypes[this.props.mxEvent.getType()]);
// This shouldn't happen: the caller should check we support this type
// before trying to instantiate us
if (!EventTileType) {
return null;
}
var classes = classNames({
mx_EventTile: true,
mx_EventTile_sending: ['sending', 'queued'].indexOf(
this.props.mxEvent.status
) !== -1,
mx_EventTile_notSent: this.props.mxEvent.status == 'not_sent',
mx_EventTile_highlight: this.shouldHighlight(),
mx_EventTile_continuation: this.props.continuation,
mx_EventTile_last: this.props.last,
menu: this.state.menu
});
var timestamp = <MessageTimestamp ts={this.props.mxEvent.getTs()} />
var editButton = (
<input
type="image" src="img/edit.png" alt="Edit"
className="mx_EventTile_editButton" onClick={this.onEditClicked}
/>
);
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;
if (!this.props.continuation) {
if (this.props.mxEvent.sender) {
avatar = (
<div className="mx_EventTile_avatar">
<MemberAvatar member={this.props.mxEvent.sender} />
</div>
);
}
if (EventTileType.needsSenderProfile()) {
sender = <SenderProfile mxEvent={this.props.mxEvent} aux={aux} />;
}
}
return (
<div className={classes}>
{ avatar }
{ sender }
<div>
{ timestamp }
{ editButton }
<EventTileType mxEvent={this.props.mxEvent} />
</div>
</div>
);
},
});

View File

@ -52,9 +52,27 @@ module.exports = React.createClass({
if (this.props.onFinished) this.props.onFinished();
},
onRedactClick: function() {
MatrixClientPeg.get().redactEvent(
this.props.mxEvent.getRoomId(), this.props.mxEvent.getId()
).done(function() {
// message should disappear by itself
}, function(e) {
var ErrorDialog = sdk.getComponent("organisms.ErrorDialog");
// display error message stating you couldn't delete this.
var code = e.errcode || e.statusCode;
Modal.createDialog(ErrorDialog, {
title: "Error",
description: "You cannot delete this message. (" + code + ")"
});
});
if (this.props.onFinished) this.props.onFinished();
},
render: function() {
var resendButton;
var viewSourceButton;
var redactButton;
if (this.props.mxEvent.status == 'not_sent') {
resendButton = (
@ -63,6 +81,13 @@ module.exports = React.createClass({
</div>
);
}
else {
redactButton = (
<div className="mx_ContextualMenu_field" onClick={this.onRedactClick}>
Delete
</div>
);
}
viewSourceButton = (
<div className="mx_ContextualMenu_field" onClick={this.onViewSourceClick}>
View Source
@ -72,6 +97,7 @@ module.exports = React.createClass({
return (
<div>
{resendButton}
{redactButton}
{viewSourceButton}
</div>
);

View File

@ -28,28 +28,13 @@ module.exports = React.createClass({
displayName: 'MessageTile',
mixins: [MessageTileController],
onEditClicked: function(e) {
var MessageContextMenu = sdk.getComponent('molecules.MessageContextMenu');
var buttonRect = e.target.getBoundingClientRect()
var x = window.innerWidth - buttonRect.left;
var y = buttonRect.top + (e.target.height / 2);
var self = this;
ContextualMenu.createMenu(MessageContextMenu, {
mxEvent: this.props.mxEvent,
right: x,
top: y,
onFinished: function() {
self.setState({menu: false});
statics: {
needsSenderProfile: function() {
return true;
}
});
this.setState({menu: true});
},
render: function() {
var MessageTimestamp = sdk.getComponent('atoms.MessageTimestamp');
var SenderProfile = sdk.getComponent('molecules.SenderProfile');
var MemberAvatar = sdk.getComponent('atoms.MemberAvatar');
var UnknownMessageTile = sdk.getComponent('molecules.UnknownMessageTile');
var tileTypes = {
@ -66,56 +51,7 @@ module.exports = React.createClass({
if (msgtype && tileTypes[msgtype]) {
TileType = tileTypes[msgtype];
}
var classes = classNames({
mx_MessageTile: true,
mx_MessageTile_sending: ['sending', 'queued'].indexOf(
this.props.mxEvent.status
) !== -1,
mx_MessageTile_notSent: this.props.mxEvent.status == 'not_sent',
mx_MessageTile_highlight: this.shouldHighlight(),
mx_MessageTile_continuation: this.props.continuation,
mx_MessageTile_last: this.props.last,
menu: this.state.menu
});
var timestamp = <MessageTimestamp ts={this.props.mxEvent.getTs()} />
var editButton = (
<input
type="image" src="img/edit.png" alt="Edit"
className="mx_MessageTile_editButton" onClick={this.onEditClicked}
/>
);
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) {
if (this.props.mxEvent.sender) {
avatar = (
<div className="mx_MessageTile_avatar">
<MemberAvatar member={this.props.mxEvent.sender} />
</div>
);
}
sender = <SenderProfile mxEvent={this.props.mxEvent} aux={aux} />;
}
if (this.props.mxEvent.status === "not_sent" && !this.state.resending) {
resend = <button className="mx_MessageTile_msgOption" onClick={this.onResend}>
Resend
</button>;
}
return (
<div className={classes}>
{ avatar }
{ sender }
<div>
{ timestamp }
{ editButton }
<TileType mxEvent={this.props.mxEvent} />
</div>
</div>
);
return <TileType mxEvent={this.props.mxEvent} />;
},
});

View File

@ -18,6 +18,7 @@ limitations under the License.
var React = require('react');
var MatrixClientPeg = require('matrix-react-sdk/lib/MatrixClientPeg');
var sdk = require('matrix-react-sdk');
var RoomSettingsController = require('matrix-react-sdk/lib/controllers/molecules/RoomSettings')
@ -65,6 +66,8 @@ module.exports = React.createClass({
},
render: function() {
var ChangeAvatar = sdk.getComponent('molecules.ChangeAvatar');
var topic = this.props.room.currentState.getStateEvents('m.room.topic', '');
if (topic) topic = topic.getContent().topic;
@ -76,6 +79,8 @@ module.exports = React.createClass({
var power_levels = this.props.room.currentState.getStateEvents('m.room.power_levels', '');
var events_levels = power_levels.events || {};
if (power_levels) {
power_levels = power_levels.getContent();
@ -91,8 +96,7 @@ module.exports = React.createClass({
if (power_levels.kick == undefined) kick_level = 50;
if (power_levels.redact == undefined) redact_level = 50;
var user_levels = power_levels.users || [];
var events_levels = power_levels.events || [];
var user_levels = power_levels.users || {};
var user_id = MatrixClientPeg.get().credentials.userId;
@ -124,7 +128,21 @@ module.exports = React.createClass({
var can_change_levels = false;
}
var banned = this.props.room.getMembersWithMemership("ban");
var room_avatar_level = parseInt(power_levels.state_default || 0);
if (events_levels['m.room.avatar'] !== undefined) {
room_avatar_level = events_levels['m.room.avatar'];
}
var can_set_room_avatar = current_user_level >= room_avatar_level;
var change_avatar;
if (can_set_room_avatar) {
change_avatar = <div>
<h3>Room Icon</h3>
<ChangeAvatar room={this.props.room} />
</div>;
}
var banned = this.props.room.getMembersWithMembership("ban");
return (
<div className="mx_RoomSettings">
@ -207,6 +225,7 @@ module.exports = React.createClass({
);
})}
</div>
{change_avatar}
</div>
);
}

View File

@ -58,15 +58,16 @@ var NotifierView = {
if (ev.getContent().body) msg = ev.getContent().body;
}
var avatarUrl = Avatar.avatarUrlForMember(
var avatarUrl = ev.sender ? Avatar.avatarUrlForMember(
ev.sender, 40, 40, 'crop'
);
) : null;
var notification = new global.Notification(
title,
{
"body": msg,
"icon": avatarUrl
"icon": avatarUrl,
"tag": "vector"
}
);

View File

@ -149,6 +149,11 @@ module.exports = React.createClass({
var innerProgressStyle = {
width: ((this.state.upload.uploadedBytes / this.state.upload.totalBytes) * 100) + '%'
};
var uploadedSize = filesize(this.state.upload.uploadedBytes);
var totalSize = filesize(this.state.upload.totalBytes);
if (uploadedSize.replace(/^.* /,'') === totalSize.replace(/^.* /,'')) {
uploadedSize = uploadedSize.replace(/ .*/, '');
}
statusBar = (
<div className="mx_RoomView_uploadBar">
<div className="mx_RoomView_uploadProgressOuter">
@ -157,7 +162,7 @@ module.exports = React.createClass({
<img className="mx_RoomView_uploadIcon" src="img/fileicon.png" width="40" height="40"/>
<img className="mx_RoomView_uploadCancel" src="img/cancel.png" width="40" height="40"/>
<div className="mx_RoomView_uploadBytes">
{filesize(this.state.upload.uploadedBytes).replace(/ .*/, '')} / {filesize(this.state.upload.totalBytes)}
{ uploadedSize } / { totalSize }
</div>
<div className="mx_RoomView_uploadFilename">Uploading {this.state.upload.fileName}</div>
</div>

View File

@ -26,7 +26,6 @@ var Loader = require("react-loader");
var dis = require('matrix-react-sdk/lib/dispatcher');
module.exports = React.createClass({
displayName: 'MatrixChat',
mixins: [MatrixChatController],
@ -40,6 +39,23 @@ module.exports = React.createClass({
componentDidMount: function() {
window.addEventListener('resize', this.handleResize);
this.handleResize();
matrixLinkify.onUserClick = function(e, userId) {
// this can really go anywhere..
// sprout a MemberInfo context menu.
console.log("Context => %s => %s", userId, e.target);
var MemberInfo = sdk.getComponent('molecules.MemberInfo');
var member = new RoomMember(null, href);
ContextualMenu.createMenu(MemberInfo, {
member: member,
right: window.innerWidth - e.pageX,
top: e.pageY,
onFinished: function() {
console.log("^_^ All done!");
}
});
};
},
componentWillUnmount: function() {

View File

@ -25,10 +25,9 @@ var Loader = require("react-loader");
var RegisterController = require('../../../../controllers/templates/Register')
module.exports = React.createClass({
DEFAULT_HS_URL: 'https://matrix.org',
DEFAULT_IS_URL: 'https://vector.im',
var config = require('../../../../../config.json');
module.exports = React.createClass({
displayName: 'Register',
mixins: [RegisterController],
@ -39,8 +38,8 @@ module.exports = React.createClass({
},
componentWillMount: function() {
this.customHsUrl = this.DEFAULT_HS_URL;
this.customIsUrl = this.DEFAULT_IS_URL;
this.customHsUrl = config.default_hs_url;
this.customIsUrl = config.default_is_url;
},
getRegFormVals: function() {
@ -56,7 +55,7 @@ module.exports = React.createClass({
if (this.state.serverConfigVisible) {
return this.customHsUrl;
} else {
return this.DEFAULT_HS_URL;
return config.default_hs_url;
}
},
@ -64,7 +63,7 @@ module.exports = React.createClass({
if (this.state.serverConfigVisible) {
return this.customIsUrl;
} else {
return this.DEFAULT_IS_URL;
return config.default_is_url;
}
},
@ -173,6 +172,12 @@ module.exports = React.createClass({
case this.FieldErrors.InUse:
strings.push(keys[i]+" is already taken");
break;
case this.FieldErrors.Length:
strings.push(keys[i] + " is not long enough.");
break;
default:
console.error("Unhandled FieldError: %s", bad[keys[i]]);
break;
}
}
var errtxt = strings.join(', ');

View File

@ -20,7 +20,8 @@ module.exports = {
// removed which gives a tree with matrix-react-sdk and vector
// trees smashed together, but this fixes everything being under
// various levels of '.' and '..'
return info.resourcePath.replace(/^[\/\.]*/, '');
// Also, sometimes the resource path is absolute.
return path.relative(process.cwd(), info.resourcePath).replace(/^[\/\.]*/, '');
}
},
resolve: {