Merge branch 'vector' of github.com:matrix-org/matrix-react-sdk into erikj/room_editing

pull/1/head
Erik Johnston 2015-07-20 16:31:02 +01:00
commit 1b6ca2b0ee
21 changed files with 195 additions and 15 deletions

View File

@ -3,7 +3,7 @@
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<title>vector</title> <title>vector</title>
<link href='https://fonts.googleapis.com/css?family=Lato:300,400,700' rel='stylesheet' type='text/css'> <link href='fonts/Lato.css' rel='stylesheet' type='text/css'>
</head> </head>
<body style="height: 100%;"> <body style="height: 100%;">
<audio id="ringbackAudio" loop> <audio id="ringbackAudio" loop>

View File

@ -68,5 +68,15 @@ html {
max-width: 500px; max-width: 500px;
z-index: -100; z-index: -100;
position: relative; position: relative;
top: 100px; top: 200px;
} }
.mx_ErrorDialogTitle {
background-color: #d2322d;
min-height: 16px;
padding: 15px;
border-bottom: 1px solid #e5e5e5;
font-weight: bold;
line-height: 1.4;
color: #fff;
}

View File

@ -22,7 +22,7 @@ limitations under the License.
position: absolute; position: absolute;
width: 200px; width: 200px;
margin-left: -295px; margin-left: -295px;
margin-top: -12px; margin-top: 0px;
z-index: 1000; z-index: 1000;
padding: 6px; padding: 6px;
} }
@ -34,6 +34,17 @@ limitations under the License.
top: 0px; top: 0px;
} }
/*
* a hacky shim to extend the hitmask of the overlay to overlap
* better with the main menu itself
*/
.mx_MemberInfo_shim {
position: absolute;
left: 212px;
width: 40px;
height: 100%;
}
.mx_MemberInfo_avatar { .mx_MemberInfo_avatar {
padding: 6px; padding: 6px;
} }
@ -44,7 +55,6 @@ limitations under the License.
.mx_MemberInfo_field { .mx_MemberInfo_field {
padding: 6px; padding: 6px;
font-weight: bold;
} }
.mx_MemberInfo_button { .mx_MemberInfo_button {

48
skins/base/fonts/Lato.css Normal file
View File

@ -0,0 +1,48 @@
/* latin-ext */
@font-face {
font-family: 'Lato';
font-style: normal;
font-weight: 300;
src: local('Lato Light'), local('Lato-Light'), url(IY9HZVvI1cMoAHxvl0w9LVKPGs1ZzpMvnHX-7fPOuAc.woff2) format('woff2');
unicode-range: U+0100-024F, U+1E00-1EFF, U+20A0-20AB, U+20AD-20CF, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
font-family: 'Lato';
font-style: normal;
font-weight: 300;
src: local('Lato Light'), local('Lato-Light'), url(22JRxvfANxSmnAhzbFH8PgLUuEpTyoUstqEm5AMlJo4.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215, U+E0FF, U+EFFD, U+F000;
}
/* latin-ext */
@font-face {
font-family: 'Lato';
font-style: normal;
font-weight: 400;
src: local('Lato Regular'), local('Lato-Regular'), url(8qcEw_nrk_5HEcCpYdJu8BTbgVql8nDJpwnrE27mub0.woff2) format('woff2');
unicode-range: U+0100-024F, U+1E00-1EFF, U+20A0-20AB, U+20AD-20CF, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
font-family: 'Lato';
font-style: normal;
font-weight: 400;
src: local('Lato Regular'), local('Lato-Regular'), url(MDadn8DQ_3oT6kvnUq_2r_esZW2xOQ-xsNqO47m55DA.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215, U+E0FF, U+EFFD, U+F000;
}
/* latin-ext */
@font-face {
font-family: 'Lato';
font-style: normal;
font-weight: 700;
src: local('Lato Bold'), local('Lato-Bold'), url(rZPI2gHXi8zxUjnybc2ZQFKPGs1ZzpMvnHX-7fPOuAc.woff2) format('woff2');
unicode-range: U+0100-024F, U+1E00-1EFF, U+20A0-20AB, U+20AD-20CF, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
font-family: 'Lato';
font-style: normal;
font-weight: 700;
src: local('Lato Bold'), local('Lato-Bold'), url(MgNNr5y1C_tIEuLEmicLmwLUuEpTyoUstqEm5AMlJo4.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215, U+E0FF, U+EFFD, U+F000;
}

View File

@ -25,6 +25,16 @@ module.exports = React.createClass({
displayName: 'MemberInfo', displayName: 'MemberInfo',
mixins: [MemberInfoController], mixins: [MemberInfoController],
componentDidMount: function() {
var self = this;
var memberInfo = this.getDOMNode();
var memberListScroll = document.getElementsByClassName("mx_MemberList_border")[0];
if (memberListScroll) {
memberInfo.style.top = (memberInfo.parentElement.offsetTop - memberListScroll.scrollTop) + "px";
}
},
getDuration: function(time) { getDuration: function(time) {
if (!time) return; if (!time) return;
var t = parseInt(time / 1000); var t = parseInt(time / 1000);
@ -61,6 +71,7 @@ module.exports = React.createClass({
return ( return (
<div className="mx_MemberInfo"> <div className="mx_MemberInfo">
<img className="mx_MemberInfo_chevron" src="img/chevron-right.png" width="9" height="16" /> <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"> <div className="mx_MemberInfo_avatar">
<img className="mx_MemberInfo_avatarImg" <img className="mx_MemberInfo_avatarImg"
src={ this.props.member ? MatrixClientPeg.get().getAvatarUrlForMember(this.props.member, 128, 128, "crop") : null } src={ this.props.member ? MatrixClientPeg.get().getAvatarUrlForMember(this.props.member, 128, 128, "crop") : null }

View File

@ -0,0 +1,71 @@
/*
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';
/*
* Usage:
* Modal.createDialog(ErrorDialog, {
* title: "some text", (default: "Error")
* description: "some more text",
* button: "Button Text",
* onClose: someFunction,
* focus: true|false (default: true)
* });
*/
var React = require('react');
module.exports = React.createClass({
displayName: 'ErrorDialog',
// can't use getDefaultProps, see Modal.js
componentWillMount: function() {
if (!this.props.title) {
this.props.title = "Error";
}
if (!this.props.description) {
this.props.description = "An error has occurred.";
}
if (!this.props.button) {
this.props.button = "OK";
}
if (this.props.focus === undefined) {
this.props.focus = true;
}
if (!this.props.onClose) {
var self = this;
this.props.onClose = function() {
self.props.onFinished();
};
}
},
render: function() {
return (
<div className="mx_ErrorDialog">
<div className="mx_ErrorDialogTitle">
{this.props.title}
</div>
{this.props.description}<br />
<button onClick={this.props.onClose} autoFocus={this.props.focus}>
{this.props.button}
</button>
</div>
);
}
});

View File

@ -30,6 +30,18 @@ module.exports = React.createClass({
displayName: 'MemberList', displayName: 'MemberList',
mixins: [MemberListController], mixins: [MemberListController],
// FIXME: combine this more nicely with the MemberInfo positioning stuff...
onMemberListScroll: function(ev) {
if (this.refs.memberListScroll) {
var memberListScroll = this.refs.memberListScroll.getDOMNode();
// offset the current MemberInfo bubble
var memberInfo = document.getElementsByClassName("mx_MemberInfo")[0];
if (memberInfo) {
memberInfo.style.top = (memberInfo.parentElement.offsetTop - memberListScroll.scrollTop) + "px";
}
}
},
makeMemberTiles: function() { makeMemberTiles: function() {
var self = this; var self = this;
return Object.keys(self.state.memberDict).map(function(userId) { return Object.keys(self.state.memberDict).map(function(userId) {
@ -71,7 +83,7 @@ module.exports = React.createClass({
<div className="mx_MemberList_chevron"> <div className="mx_MemberList_chevron">
<img src="img/chevron.png" width="24" height="13"/> <img src="img/chevron.png" width="24" height="13"/>
</div> </div>
<div className="mx_MemberList_border"> <div className="mx_MemberList_border" ref="memberListScroll" onScroll={ this.onMemberListScroll }>
<h2>Members</h2> <h2>Members</h2>
<div className="mx_MemberList_wrapper"> <div className="mx_MemberList_wrapper">
{this.makeMemberTiles()} {this.makeMemberTiles()}

View File

@ -179,7 +179,7 @@ module.exports = React.createClass({
<CallView room={this.state.room}/> <CallView room={this.state.room}/>
{ roomEdit } { roomEdit }
</div> </div>
<div ref="messageWrapper" className="mx_RoomView_messagePanel" onScroll={this.onMessageListScroll}> <div ref="messageWrapper" className="mx_RoomView_messagePanel" onScroll={ this.onMessageListScroll }>
<div className="mx_RoomView_messageListWrapper"> <div className="mx_RoomView_messageListWrapper">
<div className="mx_RoomView_MessageList" aria-live="polite"> <div className="mx_RoomView_MessageList" aria-live="polite">
<div className={scrollheader_classes}> <div className={scrollheader_classes}>

View File

@ -25,7 +25,7 @@ var ChangePassword = ComponentBroker.get('molecules/ChangePassword');
var LogoutPrompt = ComponentBroker.get('organisms/LogoutPrompt'); var LogoutPrompt = ComponentBroker.get('organisms/LogoutPrompt');
var Loader = require("react-loader"); var Loader = require("react-loader");
var Modal = require("../../../../src/Modal") var Modal = require("../../../../src/Modal");
module.exports = React.createClass({ module.exports = React.createClass({
displayName: 'UserSettings', displayName: 'UserSettings',

View File

@ -182,7 +182,7 @@ module.exports = React.createClass({
<div className="mx_Login"> <div className="mx_Login">
<div className="mx_Login_box"> <div className="mx_Login_box">
<div className="mx_Login_logo"> <div className="mx_Login_logo">
<img src="img/logo.png" width="249" height="76" alt="vector"/> <img src="img/logo.png" width="249" height="76" alt="vector"/>
</div> </div>
{this.registerContent()} {this.registerContent()}
</div> </div>

View File

@ -54,6 +54,9 @@ limitations under the License.
*/ */
var MatrixClientPeg = require("./MatrixClientPeg"); var MatrixClientPeg = require("./MatrixClientPeg");
var Modal = require("./Modal");
var ComponentBroker = require('./ComponentBroker');
var ErrorDialog = ComponentBroker.get("organisms/ErrorDialog");
var Matrix = require("matrix-js-sdk"); var Matrix = require("matrix-js-sdk");
var dis = require("./dispatcher"); var dis = require("./dispatcher");
@ -154,7 +157,12 @@ dis.register(function(payload) {
console.error("Room %s does not exist.", payload.room_id); console.error("Room %s does not exist.", payload.room_id);
return; return;
} }
if (room.getJoinedMembers().length !== 2) { var members = room.getJoinedMembers();
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
});
console.error( console.error(
"Fail: There are %s joined members in this room, not 2.", "Fail: There are %s joined members in this room, not 2.",
room.getJoinedMembers().length room.getJoinedMembers().length

View File

@ -110,4 +110,5 @@ require('../skins/base/views/molecules/voip/MCallAnswerTile');
require('../skins/base/views/molecules/voip/MCallHangupTile'); require('../skins/base/views/molecules/voip/MCallHangupTile');
require('../skins/base/views/molecules/EventAsTextTile'); require('../skins/base/views/molecules/EventAsTextTile');
require('../skins/base/views/molecules/MemberInfo'); require('../skins/base/views/molecules/MemberInfo');
require('../skins/base/views/organisms/ErrorDialog');
} }

View File

@ -44,6 +44,8 @@ module.exports = {
if (props && props.onFinished) props.onFinished.apply(arguments); if (props && props.onFinished) props.onFinished.apply(arguments);
}; };
// FIXME: If a dialog uses getDefaultProps it clobbers the onFinished
// property set here so you can't close the dialog from a button click!
var dialog = ( var dialog = (
<div className="mx_Dialog_Wrapper"> <div className="mx_Dialog_Wrapper">
<div className="mx_Dialog"> <div className="mx_Dialog">

View File

@ -100,22 +100,18 @@ var commands = {
else { else {
// attempt to join this alias. // attempt to join this alias.
return success( return success(
MatrixClientPeg.get().joinRoom(room_alias).done( MatrixClientPeg.get().joinRoom(room_alias).then(
function(room) { function(room) {
dis.dispatch({ dis.dispatch({
action: 'view_room', action: 'view_room',
room_id: room.roomId room_id: room.roomId
}); });
}, function(err) {
console.error(
"Failed to join room: %s", JSON.stringify(err)
);
}) })
); );
} }
} }
} }
return reject("Usage: /join <room_alias> [NOT IMPLEMENTED]"); return reject("Usage: /join <room_alias>");
}, },
// Kick a user from the room with an optional reason // Kick a user from the room with an optional reason

View File

@ -18,6 +18,9 @@ limitations under the License.
var MatrixClientPeg = require("../../MatrixClientPeg"); var MatrixClientPeg = require("../../MatrixClientPeg");
var SlashCommands = require("../../SlashCommands"); var SlashCommands = require("../../SlashCommands");
var Modal = require("../../Modal");
var ComponentBroker = require('../../ComponentBroker');
var ErrorDialog = ComponentBroker.get("organisms/ErrorDialog");
var dis = require("../../dispatcher"); var dis = require("../../dispatcher");
var KeyCode = { var KeyCode = {
@ -196,10 +199,18 @@ module.exports = {
console.log("Command success."); console.log("Command success.");
}, function(err) { }, function(err) {
console.error("Command failure: %s", err); console.error("Command failure: %s", err);
Modal.createDialog(ErrorDialog, {
title: "Server Error",
description: err.message
});
}); });
} }
else if (cmd.error) { else if (cmd.error) {
console.error(cmd.error); console.error(cmd.error);
Modal.createDialog(ErrorDialog, {
title: "Command Error",
description: cmd.error
});
} }
return; return;
} }