Merge remote-tracking branch 'origin/develop' into read_receipts
|
@ -1,3 +1,7 @@
|
||||||
node_modules
|
node_modules
|
||||||
vector/bundle.*
|
vector/bundle.*
|
||||||
lib
|
lib
|
||||||
|
.DS_Store
|
||||||
|
key.pem
|
||||||
|
cert.pem
|
||||||
|
build
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
{
|
||||||
|
"minify": true,
|
||||||
|
"classPrefix": "modernizr_",
|
||||||
|
"options": [
|
||||||
|
"setClasses"
|
||||||
|
],
|
||||||
|
"feature-detects": [
|
||||||
|
"test/css/displaytable",
|
||||||
|
"test/css/flexbox",
|
||||||
|
"test/es5/specification",
|
||||||
|
"test/css/objectfit",
|
||||||
|
"test/storage/localstorage"
|
||||||
|
]
|
||||||
|
}
|
|
@ -28,7 +28,7 @@ setup above, and your changes will cause an instant rebuild.
|
||||||
|
|
||||||
However, all serious development on Vector happens on the `develop` branch. This typically
|
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`
|
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
|
too, which isn't handled by Vector's `package.json`. To get the right dependencies, check out
|
||||||
the `develop` branches of these libraries and then use `npm link` to tell Vector
|
the `develop` branches of these libraries and then use `npm link` to tell Vector
|
||||||
about them:
|
about them:
|
||||||
|
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
"style": "bundle.css",
|
"style": "bundle.css",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"reskindex": "reskindex vector -h src/skins/vector/header",
|
"reskindex": "reskindex vector -h src/skins/vector/header",
|
||||||
|
"build:modernizr": "modernizr -c .modernizr.json -d src/vector/modernizr.js",
|
||||||
"build:css": "catw \"src/skins/vector/css/**/*.css\" -o vector/bundle.css -c uglifycss --no-watch",
|
"build:css": "catw \"src/skins/vector/css/**/*.css\" -o vector/bundle.css -c uglifycss --no-watch",
|
||||||
"build:compile": "babel --source-maps -d lib src",
|
"build:compile": "babel --source-maps -d lib src",
|
||||||
"build:bundle": "NODE_ENV=production webpack -p lib/vector/index.js vector/bundle.js",
|
"build:bundle": "NODE_ENV=production webpack -p lib/vector/index.js vector/bundle.js",
|
||||||
|
@ -27,11 +28,13 @@
|
||||||
"filesize": "^3.1.2",
|
"filesize": "^3.1.2",
|
||||||
"flux": "~2.0.3",
|
"flux": "~2.0.3",
|
||||||
"linkifyjs": "^2.0.0-beta.4",
|
"linkifyjs": "^2.0.0-beta.4",
|
||||||
|
"modernizr": "^3.1.0",
|
||||||
"matrix-js-sdk": "^0.3.0",
|
"matrix-js-sdk": "^0.3.0",
|
||||||
"matrix-react-sdk": "^0.0.2",
|
"matrix-react-sdk": "^0.0.2",
|
||||||
"q": "^1.4.1",
|
"q": "^1.4.1",
|
||||||
"react": "^0.13.3",
|
"react": "^0.13.3",
|
||||||
"react-loader": "^1.4.0"
|
"react-loader": "^1.4.0",
|
||||||
|
"sanitize-html": "^1.11.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"babel": "^5.8.23",
|
"babel": "^5.8.23",
|
||||||
|
|
|
@ -0,0 +1,45 @@
|
||||||
|
/*
|
||||||
|
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 days = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
|
||||||
|
var months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
formatDate: function(date) {
|
||||||
|
// date.toLocaleTimeString is completely system dependent.
|
||||||
|
// just go 24h for now
|
||||||
|
function pad(n) {
|
||||||
|
return (n < 10 ? '0' : '') + n;
|
||||||
|
}
|
||||||
|
|
||||||
|
var now = new Date();
|
||||||
|
if (date.toDateString() === now.toDateString()) {
|
||||||
|
return pad(date.getHours()) + ':' + pad(date.getMinutes());
|
||||||
|
}
|
||||||
|
else if (now.getTime() - date.getTime() < 6 * 24 * 60 * 60 * 1000) {
|
||||||
|
return days[date.getDay()] + " " + pad(date.getHours()) + ':' + pad(date.getMinutes());
|
||||||
|
}
|
||||||
|
else if (now.getFullYear() === date.getFullYear()) {
|
||||||
|
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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -36,11 +36,9 @@ module.exports = {
|
||||||
cli.on("RoomState.events", this.onRoomStateEvents);
|
cli.on("RoomState.events", this.onRoomStateEvents);
|
||||||
cli.on("RoomMember.name", this.onRoomMemberName);
|
cli.on("RoomMember.name", this.onRoomMemberName);
|
||||||
|
|
||||||
var rooms = this.getRoomList();
|
var s = this.getRoomLists();
|
||||||
this.setState({
|
s.activityMap = {};
|
||||||
roomList: rooms,
|
this.setState(s);
|
||||||
activityMap: {}
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
|
|
||||||
componentDidMount: function() {
|
componentDidMount: function() {
|
||||||
|
@ -87,9 +85,7 @@ module.exports = {
|
||||||
onRoomTimeline: function(ev, room, toStartOfTimeline) {
|
onRoomTimeline: function(ev, room, toStartOfTimeline) {
|
||||||
if (toStartOfTimeline) return;
|
if (toStartOfTimeline) return;
|
||||||
|
|
||||||
var newState = {
|
var newState = this.getRoomLists();
|
||||||
roomList: this.getRoomList()
|
|
||||||
};
|
|
||||||
if (
|
if (
|
||||||
room.roomId != this.props.selectedRoom &&
|
room.roomId != this.props.selectedRoom &&
|
||||||
ev.getSender() != MatrixClientPeg.get().credentials.userId)
|
ev.getSender() != MatrixClientPeg.get().credentials.userId)
|
||||||
|
@ -123,18 +119,23 @@ module.exports = {
|
||||||
|
|
||||||
|
|
||||||
refreshRoomList: function() {
|
refreshRoomList: function() {
|
||||||
var rooms = this.getRoomList();
|
this.setState(this.getRoomLists());
|
||||||
this.setState({
|
|
||||||
roomList: rooms
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
|
|
||||||
getRoomList: function() {
|
getRoomLists: function() {
|
||||||
return RoomListSorter.mostRecentActivityFirst(
|
var s = {};
|
||||||
|
var inviteList = [];
|
||||||
|
s.roomList = RoomListSorter.mostRecentActivityFirst(
|
||||||
MatrixClientPeg.get().getRooms().filter(function(room) {
|
MatrixClientPeg.get().getRooms().filter(function(room) {
|
||||||
var me = room.getMember(MatrixClientPeg.get().credentials.userId);
|
var me = room.getMember(MatrixClientPeg.get().credentials.userId);
|
||||||
|
|
||||||
|
if (me && me.membership == "invite") {
|
||||||
|
inviteList.push(room);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
var shouldShowRoom = (
|
var shouldShowRoom = (
|
||||||
me && (me.membership == "join" || me.membership == "invite")
|
me && (me.membership == "join")
|
||||||
);
|
);
|
||||||
// hiding conf rooms only ever toggles shouldShowRoom to false
|
// hiding conf rooms only ever toggles shouldShowRoom to false
|
||||||
if (shouldShowRoom && HIDE_CONFERENCE_CHANS) {
|
if (shouldShowRoom && HIDE_CONFERENCE_CHANS) {
|
||||||
|
@ -153,6 +154,8 @@ module.exports = {
|
||||||
return shouldShowRoom;
|
return shouldShowRoom;
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
s.inviteList = RoomListSorter.mostRecentActivityFirst(inviteList);
|
||||||
|
return s;
|
||||||
},
|
},
|
||||||
|
|
||||||
_recheckCallElement: function(selectedRoomId) {
|
_recheckCallElement: function(selectedRoomId) {
|
||||||
|
@ -174,10 +177,10 @@ module.exports = {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
makeRoomTiles: function() {
|
makeRoomTiles: function(list, isInvite) {
|
||||||
var self = this;
|
var self = this;
|
||||||
var RoomTile = sdk.getComponent("molecules.RoomTile");
|
var RoomTile = sdk.getComponent("molecules.RoomTile");
|
||||||
return this.state.roomList.map(function(room) {
|
return list.map(function(room) {
|
||||||
var selected = room.roomId == self.props.selectedRoom;
|
var selected = room.roomId == self.props.selectedRoom;
|
||||||
return (
|
return (
|
||||||
<RoomTile
|
<RoomTile
|
||||||
|
@ -187,6 +190,7 @@ module.exports = {
|
||||||
selected={selected}
|
selected={selected}
|
||||||
unread={self.state.activityMap[room.roomId] === 1}
|
unread={self.state.activityMap[room.roomId] === 1}
|
||||||
highlight={self.state.activityMap[room.roomId] === 2}
|
highlight={self.state.activityMap[room.roomId] === 2}
|
||||||
|
isInvite={isInvite}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
|
@ -14,6 +14,7 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
var Matrix = require("matrix-js-sdk");
|
||||||
var MatrixClientPeg = require("matrix-react-sdk/lib/MatrixClientPeg");
|
var MatrixClientPeg = require("matrix-react-sdk/lib/MatrixClientPeg");
|
||||||
var React = require("react");
|
var React = require("react");
|
||||||
var q = require("q");
|
var q = require("q");
|
||||||
|
@ -38,6 +39,8 @@ module.exports = {
|
||||||
uploadingRoomSettings: false,
|
uploadingRoomSettings: false,
|
||||||
numUnreadMessages: 0,
|
numUnreadMessages: 0,
|
||||||
draggingFile: false,
|
draggingFile: false,
|
||||||
|
searching: false,
|
||||||
|
searchResults: null,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -361,6 +364,41 @@ module.exports = {
|
||||||
return WhoIsTyping.whoIsTypingString(this.state.room);
|
return WhoIsTyping.whoIsTypingString(this.state.room);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
onSearch: function(term, scope) {
|
||||||
|
var filter;
|
||||||
|
if (scope === "Room") { // FIXME: should be enum
|
||||||
|
filter = {
|
||||||
|
// XXX: it's unintuitive that the filter for searching doesn't have the same shape as the v2 filter API :(
|
||||||
|
rooms: [
|
||||||
|
this.props.roomId
|
||||||
|
]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
var self = this;
|
||||||
|
MatrixClientPeg.get().search({
|
||||||
|
body: {
|
||||||
|
search_categories: {
|
||||||
|
room_events: {
|
||||||
|
search_term: term,
|
||||||
|
filter: filter,
|
||||||
|
event_context: {
|
||||||
|
before_limit: 1,
|
||||||
|
after_limit: 1,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}).then(function(data) {
|
||||||
|
self.setState({
|
||||||
|
searchTerm: term,
|
||||||
|
searchResults: data,
|
||||||
|
});
|
||||||
|
}, function(error) {
|
||||||
|
// TODO: show dialog or something
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
getEventTiles: function() {
|
getEventTiles: function() {
|
||||||
var DateSeparator = sdk.getComponent('molecules.DateSeparator');
|
var DateSeparator = sdk.getComponent('molecules.DateSeparator');
|
||||||
|
|
||||||
|
@ -369,10 +407,40 @@ module.exports = {
|
||||||
|
|
||||||
var EventTile = sdk.getComponent('molecules.EventTile');
|
var EventTile = sdk.getComponent('molecules.EventTile');
|
||||||
|
|
||||||
|
if (this.state.searchResults) {
|
||||||
|
// XXX: this dance is foul, due to the results API not returning sorted results
|
||||||
|
var results = this.state.searchResults.search_categories.room_events.results;
|
||||||
|
var eventIds = Object.keys(results);
|
||||||
|
// XXX: todo: merge overlapping results somehow?
|
||||||
|
// XXX: why doesn't searching on name work?
|
||||||
|
var resultList = eventIds.map(function(key) { return results[key]; }).sort(function(a, b) { b.rank - a.rank });
|
||||||
|
for (var i = 0; i < resultList.length; i++) {
|
||||||
|
var ts1 = resultList[i].result.origin_server_ts;
|
||||||
|
ret.push(<li key={ts1 + "-search"}><DateSeparator ts={ts1}/></li>); // Rank: {resultList[i].rank}
|
||||||
|
var mxEv = new Matrix.MatrixEvent(resultList[i].result);
|
||||||
|
if (resultList[i].context.events_before[0]) {
|
||||||
|
var mxEv2 = new Matrix.MatrixEvent(resultList[i].context.events_before[0]);
|
||||||
|
if (EventTile.haveTileForEvent(mxEv2)) {
|
||||||
|
ret.push(<li key={mxEv.getId() + "-1"}><EventTile mxEvent={mxEv2} contextual={true} /></li>);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (EventTile.haveTileForEvent(mxEv)) {
|
||||||
|
ret.push(<li key={mxEv.getId() + "+0"}><EventTile mxEvent={mxEv} searchTerm={this.state.searchTerm}/></li>);
|
||||||
|
}
|
||||||
|
if (resultList[i].context.events_after[0]) {
|
||||||
|
var mxEv2 = new Matrix.MatrixEvent(resultList[i].context.events_after[0]);
|
||||||
|
if (EventTile.haveTileForEvent(mxEv2)) {
|
||||||
|
ret.push(<li key={mxEv.getId() + "+1"}><EventTile mxEvent={mxEv2} contextual={true} /></li>);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
for (var i = this.state.room.timeline.length-1; i >= 0 && count < this.state.messageCap; --i) {
|
for (var i = this.state.room.timeline.length-1; i >= 0 && count < this.state.messageCap; --i) {
|
||||||
var mxEv = this.state.room.timeline[i];
|
var mxEv = this.state.room.timeline[i];
|
||||||
|
|
||||||
if (!EventTile.supportsEventType(mxEv.getType())) {
|
if (!EventTile.haveTileForEvent(mxEv)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,144 @@
|
||||||
|
/*
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* This has got to be the most fragile piece of CSS ever written.
|
||||||
|
But empirically it works on Chrome/FF/Safari
|
||||||
|
*/
|
||||||
|
|
||||||
|
.mx_ImageView {
|
||||||
|
display: -webkit-flex;
|
||||||
|
display: flex;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
-webkit-align-items: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_ImageView_lhs {
|
||||||
|
-webkit-box-ordinal-group: 1;
|
||||||
|
order: 1;
|
||||||
|
-webkit-flex: 1;
|
||||||
|
flex: 1 1 10%;
|
||||||
|
min-width: 60px;
|
||||||
|
/*
|
||||||
|
background-color: #080;
|
||||||
|
height: 20px;
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_ImageView_content {
|
||||||
|
-webkit-box-ordinal-group: 2;
|
||||||
|
order: 2;
|
||||||
|
/* min-width hack needed for FF */
|
||||||
|
min-width: 0px;
|
||||||
|
height: 90%;
|
||||||
|
-webkit-flex: 15;
|
||||||
|
flex: 15 15 0;
|
||||||
|
|
||||||
|
display: -webkit-flex;
|
||||||
|
display: flex;
|
||||||
|
-webkit-align-items: center;
|
||||||
|
-webkit-justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_ImageView_content img {
|
||||||
|
max-width: 100%;
|
||||||
|
/* XXX: max-height interacts badly with flex on Chrome and doesn't relayout properly until you refresh */
|
||||||
|
max-height: 100%;
|
||||||
|
/* object-fit hack needed for Chrome due to Chrome not relaying out until you refresh */
|
||||||
|
object-fit: contain;
|
||||||
|
/* background-image: url('img/trans.png'); */
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_ImageView_labelWrapper {
|
||||||
|
position: absolute;
|
||||||
|
top: 0px;
|
||||||
|
height: 100%;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_ImageView_label {
|
||||||
|
text-align: left;
|
||||||
|
display: flex;
|
||||||
|
display: -webkit-flex;
|
||||||
|
justify-content: center;
|
||||||
|
-webkit-justify-content: center;
|
||||||
|
flex-direction: column;
|
||||||
|
-webkit-flex-direction: column;
|
||||||
|
padding-left: 60px;
|
||||||
|
padding-right: 60px;
|
||||||
|
min-height: 100%;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_ImageView_name {
|
||||||
|
font-size: 20px;
|
||||||
|
margin-bottom: 6px;
|
||||||
|
pointer-events: all;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_ImageView_metadata {
|
||||||
|
font-size: 16px;
|
||||||
|
opacity: 0.5;
|
||||||
|
pointer-events: all;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_ImageView_download {
|
||||||
|
pointer-events: all;
|
||||||
|
display: table;
|
||||||
|
margin-top: 24px;
|
||||||
|
margin-bottom: 6px;
|
||||||
|
border-radius: 5px;
|
||||||
|
background-color: #454545;
|
||||||
|
font-size: 16px;
|
||||||
|
padding: 9px;
|
||||||
|
border: 1px solid #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_ImageView_size {
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_ImageView_link {
|
||||||
|
color: #fff ! important;
|
||||||
|
text-decoration: none ! important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_ImageView_button {
|
||||||
|
pointer-events: all;
|
||||||
|
font-size: 16px;
|
||||||
|
opacity: 0.5;
|
||||||
|
margin-top: 18px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_ImageView_shim {
|
||||||
|
height: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_ImageView_rhs {
|
||||||
|
-webkit-box-ordinal-group: 3;
|
||||||
|
order: 3;
|
||||||
|
-webkit-flex: 1;
|
||||||
|
flex: 1 1 10%;
|
||||||
|
min-width: 300px;
|
||||||
|
/*
|
||||||
|
background-color: #800;
|
||||||
|
height: 20px;
|
||||||
|
*/
|
||||||
|
}
|
|
@ -17,6 +17,5 @@ limitations under the License.
|
||||||
.mx_MemberAvatar {
|
.mx_MemberAvatar {
|
||||||
z-index: 20;
|
z-index: 20;
|
||||||
border-radius: 20px;
|
border-radius: 20px;
|
||||||
background-color: #dbdbdb;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,7 +22,7 @@ html {
|
||||||
}
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
font-family: 'Lato', Helvetica, Arial, Sans-Serif;
|
font-family: 'Myriad Pro', Helvetica, Arial, Sans-Serif;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
color: #454545;
|
color: #454545;
|
||||||
border: 0px;
|
border: 0px;
|
||||||
|
@ -34,7 +34,7 @@ div.error {
|
||||||
}
|
}
|
||||||
|
|
||||||
h2 {
|
h2 {
|
||||||
color: #80cef4;
|
color: #454545;
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
font-size: 20px;
|
font-size: 20px;
|
||||||
margin-top: 16px;
|
margin-top: 16px;
|
||||||
|
@ -44,7 +44,7 @@ h2 {
|
||||||
a:hover,
|
a:hover,
|
||||||
a:link,
|
a:link,
|
||||||
a:visited {
|
a:visited {
|
||||||
color: #80CEF4;
|
color: #76cfa6;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_ContextualMenu_background {
|
.mx_ContextualMenu_background {
|
||||||
|
@ -58,7 +58,7 @@ a:visited {
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_ContextualMenu {
|
.mx_ContextualMenu {
|
||||||
border: 1px solid #a9dbf4;
|
border: 1px solid #a4a4a4;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
color: #747474;
|
color: #747474;
|
||||||
|
@ -129,13 +129,21 @@ a:visited {
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
position: relative;
|
position: relative;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
max-width: 75%;
|
max-width: 80%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_ImageView {
|
.mx_Dialog_lightbox .mx_Dialog_background {
|
||||||
margin: 6px;
|
opacity: 0.85;
|
||||||
/* hack: flexbox bug? */
|
}
|
||||||
margin-bottom: 4px;
|
|
||||||
|
.mx_Dialog_lightbox .mx_Dialog {
|
||||||
|
border-radius: 0px;
|
||||||
|
background-color: transparent;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
max-width: 100%;
|
||||||
|
max-height: 100%;
|
||||||
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_Dialog_content {
|
.mx_Dialog_content {
|
||||||
|
@ -153,7 +161,7 @@ a:visited {
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
background-color: #80cef4;
|
background-color: #76cfa6;
|
||||||
margin-left: 8px;
|
margin-left: 8px;
|
||||||
margin-right: 8px;
|
margin-right: 8px;
|
||||||
padding-left: 1em;
|
padding-left: 1em;
|
||||||
|
@ -164,7 +172,7 @@ a:visited {
|
||||||
.mx_QuestionDialogTitle {
|
.mx_QuestionDialogTitle {
|
||||||
min-height: 16px;
|
min-height: 16px;
|
||||||
padding: 12px;
|
padding: 12px;
|
||||||
border-bottom: 1px solid #a9dbf4;
|
border-bottom: 1px solid #a4a4a4;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
font-size: 20px;
|
font-size: 20px;
|
||||||
line-height: 1.4;
|
line-height: 1.4;
|
||||||
|
|
|
@ -1,7 +1,4 @@
|
||||||
.mx_RoomDropTarget,
|
.mx_RoomDropTarget,
|
||||||
.mx_RoomList_favourites_label,
|
|
||||||
.mx_RoomList_archive_label,
|
|
||||||
.mx_RoomHeader_search,
|
|
||||||
.mx_RoomSettings_encrypt,
|
.mx_RoomSettings_encrypt,
|
||||||
.mx_CreateRoom_encrypt,
|
.mx_CreateRoom_encrypt,
|
||||||
.mx_RightPanel_filebutton
|
.mx_RightPanel_filebutton
|
||||||
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
/*
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
.mx_EventAsTextTile {
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
|
@ -17,20 +17,19 @@ limitations under the License.
|
||||||
.mx_EventTile {
|
.mx_EventTile {
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
clear: both;
|
clear: both;
|
||||||
margin-top: 32px;
|
margin-top: 24px;
|
||||||
margin-left: 56px;
|
margin-left: 56px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_EventTile_avatar {
|
.mx_EventTile_avatar {
|
||||||
padding-left: 12px;
|
padding-left: 18px;
|
||||||
padding-right: 12px;
|
padding-right: 12px;
|
||||||
margin-left: -64px;
|
margin-left: -64px;
|
||||||
margin-top: -7px;
|
margin-top: -4px;
|
||||||
float: left;
|
float: left;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_EventTile_avatar img {
|
.mx_EventTile_avatar img {
|
||||||
background-color: #dbdbdb;
|
|
||||||
border-radius: 20px;
|
border-radius: 20px;
|
||||||
border: 0px;
|
border: 0px;
|
||||||
}
|
}
|
||||||
|
@ -48,19 +47,30 @@ limitations under the License.
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_EventTile .mx_MessageTimestamp {
|
.mx_EventTile .mx_MessageTimestamp {
|
||||||
color: #454545;
|
color: #acacac;
|
||||||
opacity: 0.5;
|
font-size: 12px;
|
||||||
font-size: 14px;
|
|
||||||
float: right;
|
float: right;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mx_EventTile_line {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
.mx_EventTile_content {
|
.mx_EventTile_content {
|
||||||
padding-right: 100px;
|
padding-right: 100px;
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_EventTile_notice .mx_MessageTile_content {
|
.mx_MessageTile_content {
|
||||||
opacity: 0.5;
|
display: block;
|
||||||
|
margin-right: 100px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_MessageTile_searchHighlight {
|
||||||
|
background-color: #76cfa6;
|
||||||
|
color: #fff;
|
||||||
|
border-radius: 5px;
|
||||||
|
padding: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_EventTile_sending {
|
.mx_EventTile_sending {
|
||||||
|
@ -75,38 +85,41 @@ limitations under the License.
|
||||||
color: #FF0064;
|
color: #FF0064;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mx_EventTile_contextual {
|
||||||
|
opacity: 0.4;
|
||||||
|
}
|
||||||
|
|
||||||
.mx_EventTile_msgOption {
|
.mx_EventTile_msgOption {
|
||||||
float: right;
|
float: right;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_MessageTimestamp {
|
.mx_MessageTimestamp {
|
||||||
display: none;
|
visibility: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_EventTile_last .mx_MessageTimestamp {
|
.mx_EventTile_last .mx_MessageTimestamp {
|
||||||
display: block;
|
visibility: visible;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_EventTile:hover .mx_MessageTimestamp {
|
.mx_EventTile:hover .mx_MessageTimestamp {
|
||||||
display: block;
|
visibility: visible;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_EventTile_editButton {
|
.mx_EventTile_editButton {
|
||||||
float: right;
|
position: absolute;
|
||||||
display: none;
|
right: 1px;
|
||||||
border: 0px;
|
top: 15px;
|
||||||
outline: none;
|
visibility: hidden;
|
||||||
margin-right: 3px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_EventTile:hover .mx_EventTile_editButton {
|
.mx_EventTile:hover .mx_EventTile_editButton {
|
||||||
display: inline-block;
|
visibility: visible;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_EventTile.menu .mx_EventTile_editButton {
|
.mx_EventTile.menu .mx_EventTile_editButton {
|
||||||
display: inline-block;
|
visibility: visible;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_EventTile.menu .mx_MessageTimestamp {
|
.mx_EventTile.menu .mx_MessageTimestamp {
|
||||||
display: inline-block;
|
visibility: visible;
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,12 +23,12 @@ limitations under the License.
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_MImageTile_download {
|
.mx_MImageTile_download {
|
||||||
color: #80cef4;
|
color: #76cfa6;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_MImageTile_download a {
|
.mx_MImageTile_download a {
|
||||||
color: #80cef4;
|
color: #76cfa6;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,5 +15,5 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
.mx_MNoticeTile {
|
.mx_MNoticeTile {
|
||||||
opacity: 0.5;
|
opacity: 0.6;
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,4 +17,3 @@ limitations under the License.
|
||||||
.mx_MTextTile {
|
.mx_MTextTile {
|
||||||
white-space: pre-wrap;
|
white-space: pre-wrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,3 +14,49 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
.mx_MemberInfo {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_MemberInfo h2 {
|
||||||
|
margin-top: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_MemberInfo_cancel {
|
||||||
|
float: right;
|
||||||
|
margin-right: 18px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_MemberInfo_avatar {
|
||||||
|
clear: both;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_MemberInfo_avatar img {
|
||||||
|
border-radius: 48px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_MemberInfo_profileField {
|
||||||
|
opacity: 0.6;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_MemberInfo_buttons {
|
||||||
|
margin-top: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_MemberInfo_field {
|
||||||
|
cursor: pointer;
|
||||||
|
width: 100px;
|
||||||
|
text-align: center;
|
||||||
|
font-size: 12px;
|
||||||
|
background-color: #888;
|
||||||
|
color: #fff;
|
||||||
|
font-weight: bold;
|
||||||
|
border-radius: 20px;
|
||||||
|
padding-left: 6px;
|
||||||
|
padding-right: 6px;
|
||||||
|
padding-top: 4px;
|
||||||
|
padding-bottom: 2px;
|
||||||
|
margin-bottom: 4px;
|
||||||
|
}
|
||||||
|
|
|
@ -16,52 +16,27 @@ limitations under the License.
|
||||||
|
|
||||||
.mx_MemberTile {
|
.mx_MemberTile {
|
||||||
display: table-row;
|
display: table-row;
|
||||||
height: 49px;
|
|
||||||
position: relative;
|
position: relative;
|
||||||
|
color: #454545;
|
||||||
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_MemberTile_avatar {
|
.mx_MemberTile_avatar {
|
||||||
display: table-cell;
|
display: table-cell;
|
||||||
padding-left: 14px;
|
padding-left: 3px;
|
||||||
padding-right: 12px;
|
padding-right: 12px;
|
||||||
padding-top: 3px;
|
padding-top: 2px;
|
||||||
padding-bottom: 3px;
|
padding-bottom: 0px;
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
width: 40px;
|
width: 36px;
|
||||||
height: 40px;
|
height: 36px;
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_MemberTile_inviteTile {
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mx_MemberTile_inviteEditing {
|
|
||||||
display: initial ! important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mx_MemberTile_inviteEditing .mx_MemberTile_avatar {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mx_MemberTile_inviteEditing .mx_MemberTile_name {
|
|
||||||
width: 200px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mx_MemberTile_inviteEditing .mx_MemberTile_name input {
|
|
||||||
border-radius: 3px;
|
|
||||||
border: 1px solid #c7c7c7;
|
|
||||||
font-weight: 300;
|
|
||||||
font-size: 14px;
|
|
||||||
padding: 9px;
|
|
||||||
margin-top: 6px;
|
|
||||||
margin-left: 14px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mx_MemberTile_power {
|
.mx_MemberTile_power {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
width: 48px;
|
width: 44px;
|
||||||
height: 48px;
|
height: 44px;
|
||||||
left: 10px;
|
left: 10px;
|
||||||
top: -1px;
|
top: -1px;
|
||||||
}
|
}
|
||||||
|
@ -79,20 +54,18 @@ limitations under the License.
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_MemberTile_hover {
|
|
||||||
background-color: #f0f0f0;
|
|
||||||
font-size: 12px;
|
|
||||||
color: #747474;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mx_MemberTile_userId {
|
.mx_MemberTile_userId {
|
||||||
font-weight: bold;
|
font-size: 14px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_MemberTile_leave {
|
.mx_MemberTile_presence {
|
||||||
cursor: pointer;
|
font-size: 12px;
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_MemberTile_chevron {
|
||||||
margin-top: 8px;
|
margin-top: 8px;
|
||||||
margin-right: -4px;
|
margin-right: -4px;
|
||||||
margin-left: 6px;
|
margin-left: 6px;
|
||||||
|
@ -113,14 +86,14 @@ limitations under the License.
|
||||||
|
|
||||||
.mx_MemberTile_unavailable .mx_MemberTile_avatar,
|
.mx_MemberTile_unavailable .mx_MemberTile_avatar,
|
||||||
.mx_MemberTile_unavailable .mx_MemberTile_name,
|
.mx_MemberTile_unavailable .mx_MemberTile_name,
|
||||||
.mx_MemberTile_unavailable .mx_MemberTile_nameSpan
|
.mx_MemberTile_unavailable .mx_MemberTile_userId
|
||||||
{
|
{
|
||||||
opacity: 0.66;
|
opacity: 0.66;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_MemberTile_offline .mx_MemberTile_avatar,
|
.mx_MemberTile_offline .mx_MemberTile_avatar,
|
||||||
.mx_MemberTile_offline .mx_MemberTile_name,
|
.mx_MemberTile_offline .mx_MemberTile_name,
|
||||||
.mx_MemberTile_offline .mx_MemberTile_nameSpan
|
.mx_MemberTile_offline .mx_MemberTile_userId
|
||||||
{
|
{
|
||||||
opacity: 0.25;
|
opacity: 0.25;
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,39 +15,37 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
.mx_MessageComposer_wrapper {
|
.mx_MessageComposer_wrapper {
|
||||||
max-width: 720px;
|
max-width: 960px;
|
||||||
height: 50px;
|
height: 70px;
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
margin: auto;
|
margin: auto;
|
||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
border-radius: 25px;
|
border-top: 2px solid #e1dddd;
|
||||||
border: 1px solid #a9dbf4;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_MessageComposer_row {
|
.mx_MessageComposer_row {
|
||||||
display: table-row;
|
display: table-row;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 50px;
|
height: 70px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_MessageComposer .mx_MessageComposer_avatar {
|
.mx_MessageComposer .mx_MessageComposer_avatar {
|
||||||
display: table-cell;
|
display: table-cell;
|
||||||
padding-left: 5px;
|
padding-left: 10px;
|
||||||
padding-right: 10px;
|
padding-right: 20px;
|
||||||
height: 50px;
|
height: 70px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_MessageComposer .mx_MessageComposer_avatar img {
|
.mx_MessageComposer .mx_MessageComposer_avatar img {
|
||||||
margin-top: 5px;
|
margin-top: 18px;
|
||||||
border-radius: 20px;
|
border-radius: 20px;
|
||||||
background-color: #dbdbdb;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_MessageComposer_input {
|
.mx_MessageComposer_input {
|
||||||
display: table-cell;
|
display: table-cell;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
height: 50px;
|
height: 70px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_MessageComposer_input textarea {
|
.mx_MessageComposer_input textarea {
|
||||||
|
@ -64,21 +62,32 @@ limitations under the License.
|
||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
|
|
||||||
/* needed for FF */
|
/* needed for FF */
|
||||||
font-family: 'Lato', Helvetica, Arial, Sans-Serif;
|
font-family: 'Myriad Pro', Helvetica, Arial, Sans-Serif;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* hack for FF as vertical alignment of custom placeholder text is broken */
|
/* hack for FF as vertical alignment of custom placeholder text is broken */
|
||||||
.mx_MessageComposer_input textarea::-moz-placeholder {
|
.mx_MessageComposer_input textarea::-moz-placeholder {
|
||||||
line-height: 100%;
|
line-height: 100%;
|
||||||
|
color: #76cfa6;
|
||||||
|
}
|
||||||
|
.mx_MessageComposer_input textarea::-webkit-input-placeholder {
|
||||||
|
color: #76cfa6;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_MessageComposer_upload {
|
.mx_MessageComposer_upload,
|
||||||
|
.mx_MessageComposer_call {
|
||||||
display: table-cell;
|
display: table-cell;
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
padding-right: 15px;
|
padding-left: 10px;
|
||||||
|
padding-right: 10px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mx_MessageComposer_call {
|
||||||
|
padding-right: 10px;
|
||||||
|
padding-top: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
.mx_MessageComposer_upload img {
|
.mx_MessageComposer_upload img {
|
||||||
margin-top: 5px;
|
margin-top: 5px;
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,10 +18,10 @@ limitations under the License.
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_RoomHeader_wrapper {
|
.mx_RoomHeader_wrapper {
|
||||||
max-width: 720px;
|
max-width: 960px;
|
||||||
margin: auto;
|
margin: auto;
|
||||||
height: 88px;
|
height: 83px;
|
||||||
border-bottom: 1px solid #a8dbf3;
|
border-bottom: 1px solid #eeeeee;
|
||||||
|
|
||||||
display: -webkit-box;
|
display: -webkit-box;
|
||||||
display: -moz-box;
|
display: -moz-box;
|
||||||
|
@ -47,7 +47,7 @@ limitations under the License.
|
||||||
.mx_RoomHeader_textButton {
|
.mx_RoomHeader_textButton {
|
||||||
height: 48px;
|
height: 48px;
|
||||||
margin-top: 18px;
|
margin-top: 18px;
|
||||||
background-color: #80cef4;
|
background-color: #76cfa6;
|
||||||
border-radius: 48px;
|
border-radius: 48px;
|
||||||
margin-right: 8px;
|
margin-right: 8px;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
|
@ -71,11 +71,8 @@ limitations under the License.
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_RoomHeader_rightRow {
|
.mx_RoomHeader_rightRow {
|
||||||
height: 48px;
|
margin-top: 32px;
|
||||||
margin-top: 18px;
|
|
||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
border-radius: 48px;
|
|
||||||
border: 1px solid #a9dbf4;
|
|
||||||
|
|
||||||
-webkit-box-ordinal-group: 3;
|
-webkit-box-ordinal-group: 3;
|
||||||
-moz-box-ordinal-group: 3;
|
-moz-box-ordinal-group: 3;
|
||||||
|
@ -91,8 +88,8 @@ limitations under the License.
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_RoomHeader_simpleHeader {
|
.mx_RoomHeader_simpleHeader {
|
||||||
line-height: 88px;
|
line-height: 83px;
|
||||||
color: #80cef4;
|
color: #76cfa6;
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
font-size: 20px;
|
font-size: 20px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
@ -100,18 +97,39 @@ limitations under the License.
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_RoomHeader_name {
|
.mx_RoomHeader_name {
|
||||||
|
cursor: pointer;
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
height: 28px;
|
height: 28px;
|
||||||
color: #80cef4;
|
color: #454545;
|
||||||
font-weight: 400;
|
font-weight: 800;
|
||||||
font-size: 20px;
|
font-size: 24px;
|
||||||
padding-left: 16px;
|
padding-left: 8px;
|
||||||
padding-right: 16px;
|
padding-right: 16px;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mx_RoomHeader_nametext {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_RoomHeader_settingsButton {
|
||||||
|
display: inline-block;
|
||||||
|
visibility: hidden;
|
||||||
|
position: relative;
|
||||||
|
bottom: 10px;
|
||||||
|
left: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_RoomHeader_name:hover {
|
||||||
|
color: #76cfa6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_RoomHeader_name:hover .mx_RoomHeader_settingsButton {
|
||||||
|
visibility: visible;
|
||||||
|
}
|
||||||
|
|
||||||
.mx_RoomHeader_nameEditing {
|
.mx_RoomHeader_nameEditing {
|
||||||
padding-left: 16px;
|
padding-left: 8px;
|
||||||
padding-right: 16px;
|
padding-right: 16px;
|
||||||
margin-top: -5px;
|
margin-top: -5px;
|
||||||
}
|
}
|
||||||
|
@ -133,9 +151,9 @@ limitations under the License.
|
||||||
vertical-align: bottom;
|
vertical-align: bottom;
|
||||||
float: left;
|
float: left;
|
||||||
max-height: 38px;
|
max-height: 38px;
|
||||||
color: #70b5d7;
|
color: #454545;
|
||||||
font-weight: 300;
|
font-weight: 300;
|
||||||
padding-left: 16px;
|
padding-left: 8px;
|
||||||
padding-right: 16px;
|
padding-right: 16px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
|
@ -153,9 +171,8 @@ limitations under the License.
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_RoomHeader_button {
|
.mx_RoomHeader_button {
|
||||||
height: 48px;
|
|
||||||
display: table-cell;
|
display: table-cell;
|
||||||
vertical-align: middle;
|
vertical-align: top;
|
||||||
padding-left: 8px;
|
padding-left: 8px;
|
||||||
padding-right: 8px;
|
padding-right: 8px;
|
||||||
}
|
}
|
||||||
|
|
|
@ -61,7 +61,7 @@ limitations under the License.
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
background-color: #80cef4;
|
background-color: #76cfa6;
|
||||||
width: auto;
|
width: auto;
|
||||||
margin: auto;
|
margin: auto;
|
||||||
padding: 6px;
|
padding: 6px;
|
||||||
|
|
|
@ -17,24 +17,24 @@ limitations under the License.
|
||||||
.mx_RoomTile {
|
.mx_RoomTile {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
display: table-row;
|
display: table-row;
|
||||||
color: #818794;
|
font-size: 14px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_RoomTile_avatar {
|
.mx_RoomTile_avatar {
|
||||||
display: table-cell;
|
display: table-cell;
|
||||||
padding-right: 10px;
|
background: #eaf5f0;
|
||||||
padding-top: 3px;
|
padding-right: 8px;
|
||||||
padding-bottom: 3px;
|
padding-top: 4px;
|
||||||
padding-left: 10px;
|
padding-bottom: 2px;
|
||||||
|
padding-left: 18px;
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
width: 36px;
|
width: 24px;
|
||||||
height: 36px;
|
height: 24px;
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_RoomTile_avatar img {
|
.mx_RoomTile_avatar img {
|
||||||
border-radius: 20px;
|
border-radius: 20px;
|
||||||
background-color: #dbdbdb;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_RoomTile_name {
|
.mx_RoomTile_name {
|
||||||
|
@ -43,6 +43,13 @@ limitations under the License.
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
padding-right: 16px;
|
padding-right: 16px;
|
||||||
|
color: #454545;
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_RoomTile_invite {
|
||||||
|
opacity: 0.5;
|
||||||
|
font-weight: normal;
|
||||||
}
|
}
|
||||||
|
|
||||||
.collapsed .mx_RoomTile_name {
|
.collapsed .mx_RoomTile_name {
|
||||||
|
@ -63,7 +70,7 @@ limitations under the License.
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_RoomTile_badge {
|
.mx_RoomTile_badge {
|
||||||
background-color: #80cef4;
|
background-color: #76cfa6;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
border-radius: 26px;
|
border-radius: 26px;
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
|
@ -75,6 +82,7 @@ limitations under the License.
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
.mx_RoomTile_badge {
|
.mx_RoomTile_badge {
|
||||||
background-color: #ff0064;
|
background-color: #ff0064;
|
||||||
border: 3px solid #fff;
|
border: 3px solid #fff;
|
||||||
|
@ -85,19 +93,36 @@ limitations under the License.
|
||||||
right: 9px;
|
right: 9px;
|
||||||
bottom: 3px;
|
bottom: 3px;
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
.mx_RoomTile_badge {
|
||||||
|
background-color: #76cfa6;
|
||||||
|
width: 4px;
|
||||||
|
position: absolute;
|
||||||
|
left: 0px;
|
||||||
|
top: 5px;
|
||||||
|
height: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
.mx_RoomTile_unread,
|
.mx_RoomTile_unread,
|
||||||
.mx_RoomTile_highlight,
|
.mx_RoomTile_highlight,
|
||||||
.mx_RoomTile_invited
|
.mx_RoomTile_invited
|
||||||
{
|
{
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
color: #000;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_RoomTile_selected {
|
.mx_RoomTile_selected {
|
||||||
background-color: #f3f8fa;
|
}
|
||||||
color: #80cef4;
|
|
||||||
font-weight: bold;
|
.mx_RoomTile.mx_RoomTile_selected {
|
||||||
|
background: url('img/selected.png');
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-position: right center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_RoomTile_arrow {
|
||||||
|
position: absolute;
|
||||||
|
right: 0px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_RoomTile:hover {
|
.mx_RoomTile:hover {
|
||||||
|
|
|
@ -17,7 +17,7 @@ limitations under the License.
|
||||||
.mx_RoomTooltip {
|
.mx_RoomTooltip {
|
||||||
display: none;
|
display: none;
|
||||||
position: fixed;
|
position: fixed;
|
||||||
border: 1px solid #a9dbf4;
|
border: 1px solid #a4a4a4;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
z-index: 1000;
|
z-index: 1000;
|
||||||
|
|
|
@ -0,0 +1,64 @@
|
||||||
|
/*
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
.mx_SearchBar {
|
||||||
|
padding-top: 5px;
|
||||||
|
padding-bottom: 5px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_SearchBar input {
|
||||||
|
display: inline block;
|
||||||
|
border-radius: 3px;
|
||||||
|
border: 1px solid #f0f0f0;
|
||||||
|
font-size: 16px;
|
||||||
|
padding: 9px;
|
||||||
|
padding-left: 11px;
|
||||||
|
margin-right: 17px;
|
||||||
|
width: auto;
|
||||||
|
flex: 1 1 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_SearchBar_button {
|
||||||
|
display: inline;
|
||||||
|
border: 0px;
|
||||||
|
border-radius: 36px;
|
||||||
|
font-weight: 400;
|
||||||
|
font-size: 16px;
|
||||||
|
color: #fff;
|
||||||
|
background-color: #76cfa6;
|
||||||
|
width: auto;
|
||||||
|
margin: auto;
|
||||||
|
margin-left: 7px;
|
||||||
|
padding-top: 6px;
|
||||||
|
padding-bottom: 4px;
|
||||||
|
padding-left: 24px;
|
||||||
|
padding-right: 24px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_SearchBar_unselected {
|
||||||
|
background-color: #fff;
|
||||||
|
color: #9fddc1;
|
||||||
|
border: #9fddc1 1px solid;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_SearchBar_cancel {
|
||||||
|
padding-left: 14px;
|
||||||
|
padding-right: 14px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
|
@ -16,7 +16,7 @@ limitations under the License.
|
||||||
|
|
||||||
.mx_IncomingCallBox {
|
.mx_IncomingCallBox {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
border: 1px solid #a9dbf4;
|
border: 1px solid #a4a4a4;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
|
|
@ -15,7 +15,7 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
.mx_CreateRoom {
|
.mx_CreateRoom {
|
||||||
width: 720px;
|
width: 960px;
|
||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
margin-right: auto;
|
margin-right: auto;
|
||||||
color: #4a4a4a;
|
color: #4a4a4a;
|
||||||
|
|
|
@ -53,17 +53,20 @@ limitations under the License.
|
||||||
-webkit-order: 3;
|
-webkit-order: 3;
|
||||||
order: 3;
|
order: 3;
|
||||||
|
|
||||||
-webkit-flex: 0 0 170px;
|
-webkit-flex: 0 0 126px;
|
||||||
flex: 0 0 170px;
|
flex: 0 0 126px;
|
||||||
|
|
||||||
border-top: 1px solid #f3f8fa;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_LeftPanel .mx_BottomLeftMenu .mx_RoomTile {
|
.mx_LeftPanel .mx_BottomLeftMenu .mx_RoomTile {
|
||||||
color: #378bb4;
|
color: #454545;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_LeftPanel .mx_BottomLeftMenu .mx_BottomLeftMenu_options {
|
.mx_LeftPanel .mx_BottomLeftMenu .mx_BottomLeftMenu_options {
|
||||||
margin-top: 12px;
|
margin-top: 12px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mx_LeftPanel .mx_BottomLeftMenu img {
|
||||||
|
border-radius: 0px;
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
|
@ -16,8 +16,6 @@ limitations under the License.
|
||||||
|
|
||||||
.mx_MemberList {
|
.mx_MemberList {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
margin-bottom: 100px;
|
|
||||||
padding: 8px;
|
|
||||||
|
|
||||||
-webkit-flex: 1;
|
-webkit-flex: 1;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
|
@ -39,22 +37,47 @@ limitations under the License.
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_MemberList_border {
|
.mx_MemberList_border {
|
||||||
border: 1px solid #a9dbf4;
|
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
border-radius: 8px;
|
|
||||||
background-color: #fff;
|
|
||||||
|
|
||||||
order: 1;
|
order: 1;
|
||||||
-webkit-flex: 1 1 0;
|
-webkit-flex: 1 1 0;
|
||||||
flex: 1 1 0px;
|
flex: 1 1 0px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mx_MemberList_invite {
|
||||||
|
font-family: 'Myriad Pro', Helvetica, Arial, Sans-Serif;
|
||||||
|
border-radius: 3px;
|
||||||
|
border: 1px solid #f0f0f0;
|
||||||
|
padding: 9px;
|
||||||
|
color: #454545;
|
||||||
|
margin-left: 3px;
|
||||||
|
font-size: 16px;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
width: 180px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_MemberList_invite::-moz-placeholder {
|
||||||
|
color: #454545;
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
.mx_MessageList_invite::-webkit-input-placeholder {
|
||||||
|
color: #454545;
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_MemberList_invited h2 {
|
||||||
|
text-transform: uppercase;
|
||||||
|
color: #3d3b39;
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 14px;
|
||||||
|
padding-left: 3px;
|
||||||
|
padding-right: 12px;
|
||||||
|
margin-top: 8px;
|
||||||
|
margin-bottom: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
.mx_MemberList_wrapper {
|
.mx_MemberList_wrapper {
|
||||||
display: table;
|
display: table;
|
||||||
table-layout: fixed;
|
table-layout: fixed;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_MemberList h2 {
|
|
||||||
margin: 14px;
|
|
||||||
}
|
|
||||||
|
|
|
@ -33,32 +33,53 @@ limitations under the License.
|
||||||
-webkit-order: 1;
|
-webkit-order: 1;
|
||||||
order: 1;
|
order: 1;
|
||||||
|
|
||||||
-webkit-flex: 0 0 66px;
|
-webkit-flex: 0 0 83px;
|
||||||
flex: 0 0 66px;
|
flex: 0 0 83px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Fixme - factor this out with the main header **/
|
/** Fixme - factor this out with the main header **/
|
||||||
|
|
||||||
.mx_RightPanel_headerButtonGroup {
|
.mx_RightPanel_headerButtonGroup {
|
||||||
margin-top: 18px;
|
margin-top: 32px;
|
||||||
height: 48px;
|
float: left;
|
||||||
float: right;
|
|
||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
border-radius: 48px;
|
margin-left: -4px;
|
||||||
border: 1px solid #a9dbf4;
|
|
||||||
margin-right: 22px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_RightPanel_headerButton {
|
.mx_RightPanel_headerButton {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
height: 48px;
|
|
||||||
display: table-cell;
|
display: table-cell;
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
padding-left: 8px;
|
padding-left: 15px;
|
||||||
padding-right: 8px;
|
padding-right: 15px;
|
||||||
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_RightPanel .mx_MemberList {
|
.mx_RightPanel_headerButton_highlight {
|
||||||
|
position: absolute;
|
||||||
|
bottom: -2px;
|
||||||
|
left: 10px;
|
||||||
|
width: 25px;
|
||||||
|
height: 4px;
|
||||||
|
background-color: #76cfa6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_RightPanel_headerButton_badge {
|
||||||
|
position: absolute;
|
||||||
|
top: 5px;
|
||||||
|
left: 28px;
|
||||||
|
font-size: 12px;
|
||||||
|
background-color: #76cfa6;
|
||||||
|
color: #fff;
|
||||||
|
font-weight: bold;
|
||||||
|
border-radius: 20px;
|
||||||
|
padding-left: 4px;
|
||||||
|
padding-right: 4px;
|
||||||
|
padding-top: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_RightPanel .mx_MemberList,
|
||||||
|
.mx_RightPanel .mx_MemberInfo {
|
||||||
-webkit-box-ordinal-group: 2;
|
-webkit-box-ordinal-group: 2;
|
||||||
-moz-box-ordinal-group: 2;
|
-moz-box-ordinal-group: 2;
|
||||||
-ms-flex-order: 2;
|
-ms-flex-order: 2;
|
||||||
|
|
|
@ -15,7 +15,7 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
.mx_RoomDirectory {
|
.mx_RoomDirectory {
|
||||||
width: 720px;
|
width: 960px;
|
||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
margin-right: auto;
|
margin-right: auto;
|
||||||
margin-bottom: 12px;
|
margin-bottom: 12px;
|
||||||
|
|
|
@ -15,17 +15,30 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
.mx_RoomList {
|
.mx_RoomList {
|
||||||
|
padding-top: 24px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mx_RoomList_invites,
|
||||||
.mx_RoomList_recents {
|
.mx_RoomList_recents {
|
||||||
margin-top: -12px;
|
|
||||||
display: table;
|
display: table;
|
||||||
table-layout: fixed;
|
table-layout: fixed;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_RoomList h2 {
|
.mx_RoomList_expandButton {
|
||||||
padding-left: 16px;
|
margin-left: 8px;
|
||||||
padding-right: 16px;
|
cursor: pointer;
|
||||||
padding-bottom: 10px;
|
padding-left: 12px;
|
||||||
|
padding-right: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_RoomList h2 {
|
||||||
|
text-transform: uppercase;
|
||||||
|
color: #3d3b39;
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 14px;
|
||||||
|
padding-left: 12px;
|
||||||
|
padding-right: 12px;
|
||||||
|
margin-top: 8px;
|
||||||
|
margin-bottom: 4px;
|
||||||
}
|
}
|
|
@ -36,13 +36,13 @@ limitations under the License.
|
||||||
-webkit-order: 1;
|
-webkit-order: 1;
|
||||||
order: 1;
|
order: 1;
|
||||||
|
|
||||||
-webkit-flex: 0 0 88px;
|
-webkit-flex: 0 0 83px;
|
||||||
flex: 0 0 88px;
|
flex: 0 0 83px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_RoomView_fileDropTarget {
|
.mx_RoomView_fileDropTarget {
|
||||||
min-width: 0px;
|
min-width: 0px;
|
||||||
max-width: 720px;
|
max-width: 960px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
font-size: 20px;
|
font-size: 20px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
@ -61,10 +61,10 @@ limitations under the License.
|
||||||
border-top-right-radius: 10px;
|
border-top-right-radius: 10px;
|
||||||
|
|
||||||
background-color: rgba(255, 255, 255, 0.9);
|
background-color: rgba(255, 255, 255, 0.9);
|
||||||
border: 2px dashed #80cef4;
|
border: 2px #e1dddd solid;
|
||||||
border-bottom: none;
|
border-bottom: none;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 88px;
|
top: 83px;
|
||||||
bottom: 0px;
|
bottom: 0px;
|
||||||
z-index: 3000;
|
z-index: 3000;
|
||||||
}
|
}
|
||||||
|
@ -84,12 +84,12 @@ limitations under the License.
|
||||||
order: 2;
|
order: 2;
|
||||||
|
|
||||||
min-width: 0px;
|
min-width: 0px;
|
||||||
max-width: 720px;
|
max-width: 960px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
margin: auto;
|
margin: auto;
|
||||||
|
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
border-bottom: 1px solid #a8dbf3;
|
border-bottom: 1px solid #eee;
|
||||||
|
|
||||||
-webkit-flex: 0 0 auto;
|
-webkit-flex: 0 0 auto;
|
||||||
flex: 0 0 auto;
|
flex: 0 0 auto;
|
||||||
|
@ -111,7 +111,7 @@ limitations under the License.
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_RoomView_messageListWrapper {
|
.mx_RoomView_messageListWrapper {
|
||||||
max-width: 720px;
|
max-width: 960px;
|
||||||
margin: auto;
|
margin: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -129,8 +129,9 @@ limitations under the License.
|
||||||
clear: both;
|
clear: both;
|
||||||
margin-top: 32px;
|
margin-top: 32px;
|
||||||
margin-bottom: 8px;
|
margin-bottom: 8px;
|
||||||
|
margin-left: 54px;
|
||||||
padding-bottom: 6px;
|
padding-bottom: 6px;
|
||||||
border-bottom: 1px solid #a8dbf3;
|
border-bottom: 1px solid #eee;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_RoomView_invitePrompt {
|
.mx_RoomView_invitePrompt {
|
||||||
|
@ -141,7 +142,7 @@ limitations under the License.
|
||||||
order: 2;
|
order: 2;
|
||||||
|
|
||||||
min-width: 0px;
|
min-width: 0px;
|
||||||
max-width: 720px;
|
max-width: 960px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
margin: auto;
|
margin: auto;
|
||||||
|
|
||||||
|
@ -157,43 +158,44 @@ limitations under the License.
|
||||||
order: 4;
|
order: 4;
|
||||||
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
-webkit-flex: 0 0 58px;
|
-webkit-flex: 0 0 36px;
|
||||||
flex: 0 0 58px;
|
flex: 0 0 36px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_RoomView_statusAreaBox {
|
.mx_RoomView_statusAreaBox {
|
||||||
max-width: 720px;
|
max-width: 960px;
|
||||||
margin: auto;
|
margin: auto;
|
||||||
border-top: 1px solid #a8dbf3;
|
}
|
||||||
|
|
||||||
|
.mx_RoomView_statusAreaBox_line {
|
||||||
|
border-top: 1px solid #eee;
|
||||||
|
margin-left: 54px;
|
||||||
|
height: 1px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_RoomView_unreadMessagesBar {
|
.mx_RoomView_unreadMessagesBar {
|
||||||
margin-top: 13px;
|
color: #ff0064;
|
||||||
color: #fff;
|
|
||||||
font-weight: bold;
|
|
||||||
background-color: #ff0064;
|
|
||||||
border-radius: 30px;
|
|
||||||
height: 30px;
|
|
||||||
line-height: 30px;
|
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
margin-top: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_RoomView_unreadMessagesBar img {
|
.mx_RoomView_unreadMessagesBar img {
|
||||||
padding-left: 22px;
|
padding-left: 10px;
|
||||||
padding-right: 22px;
|
padding-right: 22px;
|
||||||
|
vertical-align: middle;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_RoomView_typingBar {
|
.mx_RoomView_typingBar {
|
||||||
margin-top: 17px;
|
margin-top: 10px;
|
||||||
margin-left: 56px;
|
margin-left: 54px;
|
||||||
color: #818794;
|
color: #4a4a4a;
|
||||||
|
opacity: 0.5;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_RoomView_typingBar img {
|
.mx_RoomView_typingImage {
|
||||||
padding-left: 12px;
|
display: inline;
|
||||||
padding-right: 12px;
|
margin-left: -38px;
|
||||||
margin-left: -64px;
|
margin-top: -4px;
|
||||||
margin-top: -7px;
|
|
||||||
float: left;
|
float: left;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -205,44 +207,46 @@ limitations under the License.
|
||||||
order: 5;
|
order: 5;
|
||||||
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
-webkit-flex: 0 0 63px;
|
-webkit-flex: 0 0 70px;
|
||||||
flex: 0 0 63px;
|
flex: 0 0 70px;
|
||||||
margin-right: 2px;
|
margin-right: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_RoomView_uploadProgressOuter {
|
.mx_RoomView_uploadProgressOuter {
|
||||||
width: 100%;
|
|
||||||
background-color: rgba(169, 219, 244, 0.5);
|
|
||||||
height: 4px;
|
height: 4px;
|
||||||
|
margin-left: 54px;
|
||||||
|
margin-top: -1px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_RoomView_uploadProgressInner {
|
.mx_RoomView_uploadProgressInner {
|
||||||
background-color: #80cef4;
|
background-color: #76cfa6;
|
||||||
height: 4px;
|
height: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_RoomView_uploadFilename {
|
.mx_RoomView_uploadFilename {
|
||||||
margin-top: 15px;
|
margin-top: 5px;
|
||||||
margin-left: 56px;
|
margin-left: 56px;
|
||||||
|
opacity: 0.5;
|
||||||
|
color: #4a4a4a;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_RoomView_uploadIcon {
|
.mx_RoomView_uploadIcon {
|
||||||
float: left;
|
float: left;
|
||||||
margin-top: 6px;
|
margin-top: 1px;
|
||||||
margin-left: 5px;
|
margin-left: 14px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_RoomView_uploadCancel {
|
.mx_RoomView_uploadCancel {
|
||||||
float: right;
|
float: right;
|
||||||
margin-top: 6px;
|
margin-top: 5px;
|
||||||
margin-right: 10px;
|
margin-right: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_RoomView_uploadBytes {
|
.mx_RoomView_uploadBytes {
|
||||||
float: right;
|
float: right;
|
||||||
opacity: 0.5;
|
margin-top: 5px;
|
||||||
margin-top: 15px;
|
margin-right: 30px;
|
||||||
margin-right: 10px;
|
color: #76cfa6;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_RoomView_ongoingConfCallNotification {
|
.mx_RoomView_ongoingConfCallNotification {
|
||||||
|
@ -251,5 +255,5 @@ limitations under the License.
|
||||||
background-color: #ff0064;
|
background-color: #ff0064;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
padding: 6px;
|
padding: 6px 0;
|
||||||
}
|
}
|
|
@ -15,7 +15,7 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
.mx_UserSettings {
|
.mx_UserSettings {
|
||||||
width: 720px;
|
width: 960px;
|
||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
margin-right: auto;
|
margin-right: auto;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,22 @@
|
||||||
|
/*
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
|
||||||
.mx_ViewSource pre {
|
.mx_ViewSource pre {
|
||||||
text-align: left;
|
text-align: left;
|
||||||
|
font-size: 12px;
|
||||||
|
padding: 0.5em 1em 0.5em 1em;
|
||||||
|
word-wrap: break-word;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
.mx_CompatibilityPage {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background-color: #e55;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_CompatibilityPage_box {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
margin: auto;
|
||||||
|
width: 500px;
|
||||||
|
height: 300px;
|
||||||
|
border: 1px solid;
|
||||||
|
padding: 10px;
|
||||||
|
background-color: #fcc;
|
||||||
|
}
|
|
@ -69,6 +69,8 @@ limitations under the License.
|
||||||
-webkit-order: 1;
|
-webkit-order: 1;
|
||||||
order: 1;
|
order: 1;
|
||||||
|
|
||||||
|
background-color: #eaf5f0;
|
||||||
|
|
||||||
-webkit-flex: 0 0 230px;
|
-webkit-flex: 0 0 230px;
|
||||||
flex: 0 0 230px;
|
flex: 0 0 230px;
|
||||||
}
|
}
|
||||||
|
@ -87,7 +89,7 @@ limitations under the License.
|
||||||
|
|
||||||
padding-left: 12px;
|
padding-left: 12px;
|
||||||
padding-right: 12px;
|
padding-right: 12px;
|
||||||
background-color: #f3f8fa;
|
background-color: #fff;
|
||||||
|
|
||||||
-webkit-flex: 1;
|
-webkit-flex: 1;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
|
@ -114,7 +116,6 @@ limitations under the License.
|
||||||
-webkit-order: 3;
|
-webkit-order: 3;
|
||||||
order: 3;
|
order: 3;
|
||||||
|
|
||||||
background-color: #f3f8fa;
|
|
||||||
-webkit-flex: 0 0 230px;
|
-webkit-flex: 0 0 230px;
|
||||||
flex: 0 0 230px;
|
flex: 0 0 230px;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Myriad Pro';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: normal;
|
||||||
|
src: local('Myriad Pro'), local('MyriadPro'), url(MyriadPro-Regular.woff) format('woff');
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Myriad Pro';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 600;
|
||||||
|
src: local('Myriad Pro SemiBold'), local('MyriadPro-SemiBold'), url(MyriadPro-SemiBold.woff) format('woff');
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Myriad Pro';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: bold;
|
||||||
|
src: local('Myriad Pro Bold'), local('MyriadPro-Bold'), url(MyriadPro-Bold.woff) format('woff');
|
||||||
|
}
|
After Width: | Height: | Size: 966 B |
After Width: | Height: | Size: 588 B |
After Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 316 B |
Before Width: | Height: | Size: 179 B After Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 181 B After Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 237 B After Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 581 B |
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 325 B |
Before Width: | Height: | Size: 498 B After Width: | Height: | Size: 460 B |
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 658 B |
After Width: | Height: | Size: 503 B |
After Width: | Height: | Size: 271 B |
Before Width: | Height: | Size: 977 B After Width: | Height: | Size: 729 B |
Before Width: | Height: | Size: 213 B After Width: | Height: | Size: 590 B |
Before Width: | Height: | Size: 854 B After Width: | Height: | Size: 742 B |
After Width: | Height: | Size: 995 B |
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 810 B |
Before Width: | Height: | Size: 1003 B After Width: | Height: | Size: 391 B |
After Width: | Height: | Size: 959 B |
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.6 KiB |
Before Width: | Height: | Size: 704 B After Width: | Height: | Size: 713 B |
|
@ -23,9 +23,6 @@ limitations under the License.
|
||||||
|
|
||||||
var skin = {};
|
var skin = {};
|
||||||
|
|
||||||
skin['atoms.create_room.CreateRoomButton'] = require('./views/atoms/create_room/CreateRoomButton');
|
|
||||||
skin['atoms.create_room.Presets'] = require('./views/atoms/create_room/Presets');
|
|
||||||
skin['atoms.create_room.RoomAlias'] = require('./views/atoms/create_room/RoomAlias');
|
|
||||||
skin['atoms.EditableText'] = require('./views/atoms/EditableText');
|
skin['atoms.EditableText'] = require('./views/atoms/EditableText');
|
||||||
skin['atoms.EnableNotificationsButton'] = require('./views/atoms/EnableNotificationsButton');
|
skin['atoms.EnableNotificationsButton'] = require('./views/atoms/EnableNotificationsButton');
|
||||||
skin['atoms.ImageView'] = require('./views/atoms/ImageView');
|
skin['atoms.ImageView'] = require('./views/atoms/ImageView');
|
||||||
|
@ -33,6 +30,9 @@ skin['atoms.LogoutButton'] = require('./views/atoms/LogoutButton');
|
||||||
skin['atoms.MemberAvatar'] = require('./views/atoms/MemberAvatar');
|
skin['atoms.MemberAvatar'] = require('./views/atoms/MemberAvatar');
|
||||||
skin['atoms.MessageTimestamp'] = require('./views/atoms/MessageTimestamp');
|
skin['atoms.MessageTimestamp'] = require('./views/atoms/MessageTimestamp');
|
||||||
skin['atoms.RoomAvatar'] = require('./views/atoms/RoomAvatar');
|
skin['atoms.RoomAvatar'] = require('./views/atoms/RoomAvatar');
|
||||||
|
skin['atoms.create_room.CreateRoomButton'] = require('./views/atoms/create_room/CreateRoomButton');
|
||||||
|
skin['atoms.create_room.Presets'] = require('./views/atoms/create_room/Presets');
|
||||||
|
skin['atoms.create_room.RoomAlias'] = require('./views/atoms/create_room/RoomAlias');
|
||||||
skin['atoms.voip.VideoFeed'] = require('./views/atoms/voip/VideoFeed');
|
skin['atoms.voip.VideoFeed'] = require('./views/atoms/voip/VideoFeed');
|
||||||
skin['molecules.BottomLeftMenu'] = require('./views/molecules/BottomLeftMenu');
|
skin['molecules.BottomLeftMenu'] = require('./views/molecules/BottomLeftMenu');
|
||||||
skin['molecules.BottomLeftMenuTile'] = require('./views/molecules/BottomLeftMenuTile');
|
skin['molecules.BottomLeftMenuTile'] = require('./views/molecules/BottomLeftMenuTile');
|
||||||
|
@ -42,18 +42,18 @@ skin['molecules.ChangePassword'] = require('./views/molecules/ChangePassword');
|
||||||
skin['molecules.DateSeparator'] = require('./views/molecules/DateSeparator');
|
skin['molecules.DateSeparator'] = require('./views/molecules/DateSeparator');
|
||||||
skin['molecules.EventAsTextTile'] = require('./views/molecules/EventAsTextTile');
|
skin['molecules.EventAsTextTile'] = require('./views/molecules/EventAsTextTile');
|
||||||
skin['molecules.EventTile'] = require('./views/molecules/EventTile');
|
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');
|
|
||||||
skin['molecules.MEmoteTile'] = require('./views/molecules/MEmoteTile');
|
skin['molecules.MEmoteTile'] = require('./views/molecules/MEmoteTile');
|
||||||
skin['molecules.MessageComposer'] = require('./views/molecules/MessageComposer');
|
|
||||||
skin['molecules.MessageContextMenu'] = require('./views/molecules/MessageContextMenu');
|
|
||||||
skin['molecules.MessageTile'] = require('./views/molecules/MessageTile');
|
|
||||||
skin['molecules.MFileTile'] = require('./views/molecules/MFileTile');
|
skin['molecules.MFileTile'] = require('./views/molecules/MFileTile');
|
||||||
skin['molecules.MImageTile'] = require('./views/molecules/MImageTile');
|
skin['molecules.MImageTile'] = require('./views/molecules/MImageTile');
|
||||||
skin['molecules.MNoticeTile'] = require('./views/molecules/MNoticeTile');
|
skin['molecules.MNoticeTile'] = require('./views/molecules/MNoticeTile');
|
||||||
skin['molecules.MRoomMemberTile'] = require('./views/molecules/MRoomMemberTile');
|
skin['molecules.MRoomMemberTile'] = require('./views/molecules/MRoomMemberTile');
|
||||||
skin['molecules.MTextTile'] = require('./views/molecules/MTextTile');
|
skin['molecules.MTextTile'] = require('./views/molecules/MTextTile');
|
||||||
|
skin['molecules.MatrixToolbar'] = require('./views/molecules/MatrixToolbar');
|
||||||
|
skin['molecules.MemberInfo'] = require('./views/molecules/MemberInfo');
|
||||||
|
skin['molecules.MemberTile'] = require('./views/molecules/MemberTile');
|
||||||
|
skin['molecules.MessageComposer'] = require('./views/molecules/MessageComposer');
|
||||||
|
skin['molecules.MessageContextMenu'] = require('./views/molecules/MessageContextMenu');
|
||||||
|
skin['molecules.MessageTile'] = require('./views/molecules/MessageTile');
|
||||||
skin['molecules.ProgressBar'] = require('./views/molecules/ProgressBar');
|
skin['molecules.ProgressBar'] = require('./views/molecules/ProgressBar');
|
||||||
skin['molecules.RoomCreate'] = require('./views/molecules/RoomCreate');
|
skin['molecules.RoomCreate'] = require('./views/molecules/RoomCreate');
|
||||||
skin['molecules.RoomDropTarget'] = require('./views/molecules/RoomDropTarget');
|
skin['molecules.RoomDropTarget'] = require('./views/molecules/RoomDropTarget');
|
||||||
|
@ -61,6 +61,7 @@ skin['molecules.RoomHeader'] = require('./views/molecules/RoomHeader');
|
||||||
skin['molecules.RoomSettings'] = require('./views/molecules/RoomSettings');
|
skin['molecules.RoomSettings'] = require('./views/molecules/RoomSettings');
|
||||||
skin['molecules.RoomTile'] = require('./views/molecules/RoomTile');
|
skin['molecules.RoomTile'] = require('./views/molecules/RoomTile');
|
||||||
skin['molecules.RoomTooltip'] = require('./views/molecules/RoomTooltip');
|
skin['molecules.RoomTooltip'] = require('./views/molecules/RoomTooltip');
|
||||||
|
skin['molecules.SearchBar'] = require('./views/molecules/SearchBar');
|
||||||
skin['molecules.SenderProfile'] = require('./views/molecules/SenderProfile');
|
skin['molecules.SenderProfile'] = require('./views/molecules/SenderProfile');
|
||||||
skin['molecules.ServerConfig'] = require('./views/molecules/ServerConfig');
|
skin['molecules.ServerConfig'] = require('./views/molecules/ServerConfig');
|
||||||
skin['molecules.UnknownMessageTile'] = require('./views/molecules/UnknownMessageTile');
|
skin['molecules.UnknownMessageTile'] = require('./views/molecules/UnknownMessageTile');
|
||||||
|
|
|
@ -18,10 +18,19 @@ limitations under the License.
|
||||||
|
|
||||||
var React = require('react');
|
var React = require('react');
|
||||||
|
|
||||||
|
var MatrixClientPeg = require('matrix-react-sdk/lib/MatrixClientPeg');
|
||||||
|
|
||||||
|
var DateUtils = require('../../../../DateUtils');
|
||||||
|
var filesize = require('filesize');
|
||||||
|
|
||||||
module.exports = React.createClass({
|
module.exports = React.createClass({
|
||||||
displayName: 'ImageView',
|
displayName: 'ImageView',
|
||||||
|
|
||||||
// XXX: keyboard shortcuts for managing dialogs should be done by the modal dialog base class omehow, surely...
|
propTypes: {
|
||||||
|
onFinished: React.PropTypes.func.isRequired
|
||||||
|
},
|
||||||
|
|
||||||
|
// XXX: keyboard shortcuts for managing dialogs should be done by the modal dialog base class somehow, surely...
|
||||||
componentDidMount: function() {
|
componentDidMount: function() {
|
||||||
document.addEventListener("keydown", this.onKeyDown);
|
document.addEventListener("keydown", this.onKeyDown);
|
||||||
},
|
},
|
||||||
|
@ -38,9 +47,28 @@ module.exports = React.createClass({
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
onRedactClick: function() {
|
||||||
|
var self = this;
|
||||||
|
MatrixClientPeg.get().redactEvent(
|
||||||
|
this.props.mxEvent.getRoomId(), this.props.mxEvent.getId()
|
||||||
|
).done(function() {
|
||||||
|
if (self.props.onFinished) self.props.onFinished();
|
||||||
|
}, 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 image. (" + code + ")"
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
render: function() {
|
render: function() {
|
||||||
|
|
||||||
// XXX: can't we just do max-width: 80%, max-height: 80% on the CSS?
|
/*
|
||||||
|
// In theory max-width: 80%, max-height: 80% on the CSS should work
|
||||||
|
// but in practice, it doesn't, so do it manually:
|
||||||
|
|
||||||
var width = this.props.width || 500;
|
var width = this.props.width || 500;
|
||||||
var height = this.props.height || 500;
|
var height = this.props.height || 500;
|
||||||
|
@ -65,9 +93,55 @@ module.exports = React.createClass({
|
||||||
width: displayWidth,
|
width: displayWidth,
|
||||||
height: displayHeight
|
height: displayHeight
|
||||||
};
|
};
|
||||||
|
*/
|
||||||
|
var style, res;
|
||||||
|
|
||||||
|
if (this.props.width && this.props.height) {
|
||||||
|
style = {
|
||||||
|
width: this.props.width,
|
||||||
|
height: this.props.height,
|
||||||
|
};
|
||||||
|
res = ", " + style.width + "x" + style.height + "px";
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<img className="mx_ImageView" src={this.props.src} style={style} />
|
<div className="mx_ImageView">
|
||||||
|
<div className="mx_ImageView_lhs">
|
||||||
|
</div>
|
||||||
|
<div className="mx_ImageView_content">
|
||||||
|
<img src={this.props.src} style={style}/>
|
||||||
|
<div className="mx_ImageView_labelWrapper">
|
||||||
|
<div className="mx_ImageView_label">
|
||||||
|
<div className="mx_ImageView_shim">
|
||||||
|
</div>
|
||||||
|
<div className="mx_ImageView_name">
|
||||||
|
{ this.props.mxEvent.getContent().body }
|
||||||
|
</div>
|
||||||
|
<div className="mx_ImageView_metadata">
|
||||||
|
Uploaded on { DateUtils.formatDate(new Date(this.props.mxEvent.getTs())) } by { this.props.mxEvent.getSender() }
|
||||||
|
</div>
|
||||||
|
<a className="mx_ImageView_link" href={ this.props.src } target="_blank">
|
||||||
|
<div className="mx_ImageView_download">
|
||||||
|
Download this file<br/>
|
||||||
|
<span className="mx_ImageView_size">({ filesize(this.props.mxEvent.getContent().info.size) }{ res })</span>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
<div className="mx_ImageView_button">
|
||||||
|
<a className="mx_ImageView_link" href={ this.props.src } target="_blank">
|
||||||
|
View full screen
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div className="mx_ImageView_button" onClick={this.onRedactClick}>
|
||||||
|
Redact
|
||||||
|
</div>
|
||||||
|
<div className="mx_ImageView_shim">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="mx_ImageView_rhs">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -17,40 +17,16 @@ limitations under the License.
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var React = require('react');
|
var React = require('react');
|
||||||
|
var DateUtils = require('../../../../DateUtils');
|
||||||
var days = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
|
|
||||||
var months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
|
|
||||||
|
|
||||||
module.exports = React.createClass({
|
module.exports = React.createClass({
|
||||||
displayName: 'MessageTimestamp',
|
displayName: 'MessageTimestamp',
|
||||||
|
|
||||||
formatDate: function(date) {
|
|
||||||
// date.toLocaleTimeString is completely system dependent.
|
|
||||||
// just go 24h for now
|
|
||||||
function pad(n) {
|
|
||||||
return (n < 10 ? '0' : '') + n;
|
|
||||||
}
|
|
||||||
|
|
||||||
var now = new Date();
|
|
||||||
if (date.toDateString() === now.toDateString()) {
|
|
||||||
return pad(date.getHours()) + ':' + pad(date.getMinutes());
|
|
||||||
}
|
|
||||||
else if (now.getTime() - date.getTime() < 6 * 24 * 60 * 60 * 1000) {
|
|
||||||
return days[date.getDay()] + " " + pad(date.getHours()) + ':' + pad(date.getMinutes());
|
|
||||||
}
|
|
||||||
else if (now.getFullYear() === date.getFullYear()) {
|
|
||||||
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());
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
render: function() {
|
render: function() {
|
||||||
var date = new Date(this.props.ts);
|
var date = new Date(this.props.ts);
|
||||||
return (
|
return (
|
||||||
<span className="mx_MessageTimestamp">
|
<span className="mx_MessageTimestamp">
|
||||||
{ this.formatDate(date) }
|
{ DateUtils.formatDate(date) }
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
|
@ -33,7 +33,7 @@ module.exports = React.createClass({
|
||||||
},
|
},
|
||||||
|
|
||||||
getFallbackAvatar: function() {
|
getFallbackAvatar: function() {
|
||||||
var images = [ '80cef4', '50e2c2', 'f4c371' ];
|
var images = [ '76cfa6', '50e2c2', 'f4c371' ];
|
||||||
var total = 0;
|
var total = 0;
|
||||||
for (var i = 0; i < this.props.room.roomId.length; ++i) {
|
for (var i = 0; i < this.props.room.roomId.length; ++i) {
|
||||||
total += this.props.room.roomId.charCodeAt(i);
|
total += this.props.room.roomId.charCodeAt(i);
|
||||||
|
|
|
@ -48,7 +48,7 @@ module.exports = React.createClass({
|
||||||
return (
|
return (
|
||||||
<div className="mx_RoomTile" onMouseEnter={this.onMouseEnter} onMouseLeave={this.onMouseLeave} onClick={this.props.onClick}>
|
<div className="mx_RoomTile" onMouseEnter={this.onMouseEnter} onMouseLeave={this.onMouseLeave} onClick={this.props.onClick}>
|
||||||
<div className="mx_RoomTile_avatar">
|
<div className="mx_RoomTile_avatar">
|
||||||
<img src={ this.props.img } width="36" height="36"/>
|
<img src={ this.props.img } width="24" height="24"/>
|
||||||
</div>
|
</div>
|
||||||
{ label }
|
{ label }
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -24,12 +24,15 @@ var sdk = require('matrix-react-sdk')
|
||||||
var EventTileController = require('matrix-react-sdk/lib/controllers/molecules/EventTile')
|
var EventTileController = require('matrix-react-sdk/lib/controllers/molecules/EventTile')
|
||||||
var ContextualMenu = require('../../../../ContextualMenu');
|
var ContextualMenu = require('../../../../ContextualMenu');
|
||||||
|
|
||||||
|
var TextForEvent = require('matrix-react-sdk/lib/TextForEvent');
|
||||||
|
|
||||||
var eventTileTypes = {
|
var eventTileTypes = {
|
||||||
'm.room.message': 'molecules.MessageTile',
|
'm.room.message': 'molecules.MessageTile',
|
||||||
'm.room.member' : 'molecules.EventAsTextTile',
|
'm.room.member' : 'molecules.EventAsTextTile',
|
||||||
'm.call.invite' : 'molecules.EventAsTextTile',
|
'm.call.invite' : 'molecules.EventAsTextTile',
|
||||||
'm.call.answer' : 'molecules.EventAsTextTile',
|
'm.call.answer' : 'molecules.EventAsTextTile',
|
||||||
'm.call.hangup' : 'molecules.EventAsTextTile',
|
'm.call.hangup' : 'molecules.EventAsTextTile',
|
||||||
|
'm.room.name' : 'molecules.EventAsTextTile',
|
||||||
'm.room.topic' : 'molecules.EventAsTextTile',
|
'm.room.topic' : 'molecules.EventAsTextTile',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -38,8 +41,13 @@ module.exports = React.createClass({
|
||||||
mixins: [EventTileController],
|
mixins: [EventTileController],
|
||||||
|
|
||||||
statics: {
|
statics: {
|
||||||
supportsEventType: function(et) {
|
haveTileForEvent: function(e) {
|
||||||
return eventTileTypes[et] !== undefined;
|
if (eventTileTypes[e.getType()] == undefined) return false;
|
||||||
|
if (eventTileTypes[e.getType()] == 'molecules.EventAsTextTile') {
|
||||||
|
return TextForEvent.textForEvent(e) !== '';
|
||||||
|
} else {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -76,7 +84,7 @@ module.exports = React.createClass({
|
||||||
// This shouldn't happen: the caller should check we support this type
|
// This shouldn't happen: the caller should check we support this type
|
||||||
// before trying to instantiate us
|
// before trying to instantiate us
|
||||||
if (!EventTileType) {
|
if (!EventTileType) {
|
||||||
return null;
|
throw new Error("Event type not supported");
|
||||||
}
|
}
|
||||||
|
|
||||||
var classes = classNames({
|
var classes = classNames({
|
||||||
|
@ -88,12 +96,13 @@ module.exports = React.createClass({
|
||||||
mx_EventTile_highlight: this.shouldHighlight(),
|
mx_EventTile_highlight: this.shouldHighlight(),
|
||||||
mx_EventTile_continuation: this.props.continuation,
|
mx_EventTile_continuation: this.props.continuation,
|
||||||
mx_EventTile_last: this.props.last,
|
mx_EventTile_last: this.props.last,
|
||||||
menu: this.state.menu
|
mx_EventTile_contextual: this.props.contextual,
|
||||||
|
menu: this.state.menu,
|
||||||
});
|
});
|
||||||
var timestamp = <MessageTimestamp ts={this.props.mxEvent.getTs()} />
|
var timestamp = <MessageTimestamp ts={this.props.mxEvent.getTs()} />
|
||||||
var editButton = (
|
var editButton = (
|
||||||
<input
|
<input
|
||||||
type="image" src="img/edit.png" alt="Edit"
|
type="image" src="img/edit.png" alt="Edit" width="14" height="14"
|
||||||
className="mx_EventTile_editButton" onClick={this.onEditClicked}
|
className="mx_EventTile_editButton" onClick={this.onEditClicked}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
@ -108,7 +117,7 @@ module.exports = React.createClass({
|
||||||
if (this.props.mxEvent.sender) {
|
if (this.props.mxEvent.sender) {
|
||||||
avatar = (
|
avatar = (
|
||||||
<div className="mx_EventTile_avatar">
|
<div className="mx_EventTile_avatar">
|
||||||
<MemberAvatar member={this.props.mxEvent.sender} />
|
<MemberAvatar member={this.props.mxEvent.sender} width={24} height={24} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -120,10 +129,10 @@ module.exports = React.createClass({
|
||||||
<div className={classes}>
|
<div className={classes}>
|
||||||
{ avatar }
|
{ avatar }
|
||||||
{ sender }
|
{ sender }
|
||||||
<div>
|
<div className="mx_EventTile_line">
|
||||||
{ timestamp }
|
{ timestamp }
|
||||||
{ editButton }
|
{ editButton }
|
||||||
<EventTileType mxEvent={this.props.mxEvent} />
|
<EventTileType mxEvent={this.props.mxEvent} searchTerm={this.props.searchTerm} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -57,8 +57,9 @@ module.exports = React.createClass({
|
||||||
Modal.createDialog(ImageView, {
|
Modal.createDialog(ImageView, {
|
||||||
src: httpUrl,
|
src: httpUrl,
|
||||||
width: content.info.w,
|
width: content.info.w,
|
||||||
height: content.info.h
|
height: content.info.h,
|
||||||
});
|
mxEvent: this.props.mxEvent,
|
||||||
|
}, "mx_Dialog_lightbox");
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -67,7 +68,7 @@ module.exports = React.createClass({
|
||||||
var cli = MatrixClientPeg.get();
|
var cli = MatrixClientPeg.get();
|
||||||
|
|
||||||
var thumbHeight = null;
|
var thumbHeight = null;
|
||||||
if (content.info) thumbHeight = this.thumbHeight(content.info.w, content.info.h, 320, 240);
|
if (content.info) thumbHeight = this.thumbHeight(content.info.w, content.info.h, 480, 360);
|
||||||
|
|
||||||
var imgStyle = {};
|
var imgStyle = {};
|
||||||
if (thumbHeight) imgStyle['height'] = thumbHeight;
|
if (thumbHeight) imgStyle['height'] = thumbHeight;
|
||||||
|
@ -75,7 +76,7 @@ module.exports = React.createClass({
|
||||||
return (
|
return (
|
||||||
<span className="mx_MImageTile">
|
<span className="mx_MImageTile">
|
||||||
<a href={cli.mxcUrlToHttp(content.url)} onClick={ this.onClick }>
|
<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} />
|
<img className="mx_MImageTile_thumbnail" src={cli.mxcUrlToHttp(content.url, 480, 360)} alt={content.body} style={imgStyle} />
|
||||||
</a>
|
</a>
|
||||||
<div className="mx_MImageTile_download">
|
<div className="mx_MImageTile_download">
|
||||||
<a href={cli.mxcUrlToHttp(content.url)} target="_blank">
|
<a href={cli.mxcUrlToHttp(content.url)} target="_blank">
|
||||||
|
|
|
@ -17,18 +17,71 @@ limitations under the License.
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var React = require('react');
|
var React = require('react');
|
||||||
|
var sanitizeHtml = require('sanitize-html');
|
||||||
|
|
||||||
var MNoticeTileController = require('matrix-react-sdk/lib/controllers/molecules/MNoticeTile')
|
var MNoticeTileController = require('matrix-react-sdk/lib/controllers/molecules/MNoticeTile')
|
||||||
|
|
||||||
|
var allowedAttributes = sanitizeHtml.defaults.allowedAttributes;
|
||||||
|
allowedAttributes['font'] = ['color'];
|
||||||
|
var sanitizeHtmlParams = {
|
||||||
|
allowedTags: sanitizeHtml.defaults.allowedTags.concat([ 'font' ]),
|
||||||
|
allowedAttributes: allowedAttributes,
|
||||||
|
};
|
||||||
|
|
||||||
module.exports = React.createClass({
|
module.exports = React.createClass({
|
||||||
displayName: 'MNoticeTile',
|
displayName: 'MNoticeTile',
|
||||||
mixins: [MNoticeTileController],
|
mixins: [MNoticeTileController],
|
||||||
|
|
||||||
|
// FIXME: this entire class is copy-pasted from MTextTile :(
|
||||||
render: function() {
|
render: function() {
|
||||||
var content = this.props.mxEvent.getContent();
|
var content = this.props.mxEvent.getContent();
|
||||||
|
var originalBody = content.body;
|
||||||
|
var body;
|
||||||
|
|
||||||
|
if (this.props.searchTerm) {
|
||||||
|
var lastOffset = 0;
|
||||||
|
var bodyList = [];
|
||||||
|
var k = 0;
|
||||||
|
var offset;
|
||||||
|
|
||||||
|
// XXX: rather than searching for the search term in the body,
|
||||||
|
// we should be looking at the match delimiters returned by the FTS engine
|
||||||
|
if (content.format === "org.matrix.custom.html") {
|
||||||
|
var safeBody = sanitizeHtml(content.formatted_body, sanitizeHtmlParams);
|
||||||
|
var safeSearchTerm = sanitizeHtml(this.props.searchTerm, sanitizeHtmlParams);
|
||||||
|
while ((offset = safeBody.indexOf(safeSearchTerm, lastOffset)) >= 0) {
|
||||||
|
// FIXME: we need to apply the search highlighting to only the text elements of HTML, which means
|
||||||
|
// hooking into the sanitizer parser rather than treating it as a string. Otherwise
|
||||||
|
// the act of highlighting a <b/> or whatever will break the HTML badly.
|
||||||
|
bodyList.push(<span key={ k++ } dangerouslySetInnerHTML={{ __html: safeBody.substring(lastOffset, offset) }} />);
|
||||||
|
bodyList.push(<span key={ k++ } dangerouslySetInnerHTML={{ __html: safeSearchTerm }} className="mx_MessageTile_searchHighlight" />);
|
||||||
|
lastOffset = offset + safeSearchTerm.length;
|
||||||
|
}
|
||||||
|
bodyList.push(<span key={ k++ } dangerouslySetInnerHTML={{ __html: safeBody.substring(lastOffset) }} />);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
while ((offset = originalBody.indexOf(this.props.searchTerm, lastOffset)) >= 0) {
|
||||||
|
bodyList.push(<span key={ k++ } >{ originalBody.substring(lastOffset, offset) }</span>);
|
||||||
|
bodyList.push(<span key={ k++ } className="mx_MessageTile_searchHighlight">{ this.props.searchTerm }</span>);
|
||||||
|
lastOffset = offset + this.props.searchTerm.length;
|
||||||
|
}
|
||||||
|
bodyList.push(<span key={ k++ }>{ originalBody.substring(lastOffset) }</span>);
|
||||||
|
}
|
||||||
|
body = bodyList;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (content.format === "org.matrix.custom.html") {
|
||||||
|
var safeBody = sanitizeHtml(content.formatted_body, sanitizeHtmlParams);
|
||||||
|
body = <span dangerouslySetInnerHTML={{ __html: safeBody }} />;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
body = originalBody;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<span ref="content" className="mx_MNoticeTile mx_MessageTile_content">
|
<span ref="content" className="mx_MNoticeTile mx_MessageTile_content">
|
||||||
{content.body}
|
{ body }
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
|
@ -17,18 +17,71 @@ limitations under the License.
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var React = require('react');
|
var React = require('react');
|
||||||
|
var sanitizeHtml = require('sanitize-html');
|
||||||
|
|
||||||
var MTextTileController = require('matrix-react-sdk/lib/controllers/molecules/MTextTile')
|
var MTextTileController = require('matrix-react-sdk/lib/controllers/molecules/MTextTile')
|
||||||
|
|
||||||
|
var allowedAttributes = sanitizeHtml.defaults.allowedAttributes;
|
||||||
|
allowedAttributes['font'] = ['color'];
|
||||||
|
var sanitizeHtmlParams = {
|
||||||
|
allowedTags: sanitizeHtml.defaults.allowedTags.concat([ 'font' ]),
|
||||||
|
allowedAttributes: allowedAttributes,
|
||||||
|
};
|
||||||
|
|
||||||
module.exports = React.createClass({
|
module.exports = React.createClass({
|
||||||
displayName: 'MTextTile',
|
displayName: 'MTextTile',
|
||||||
mixins: [MTextTileController],
|
mixins: [MTextTileController],
|
||||||
|
|
||||||
|
// FIXME: this entire class is copy-pasted from MTextTile :(
|
||||||
render: function() {
|
render: function() {
|
||||||
var content = this.props.mxEvent.getContent();
|
var content = this.props.mxEvent.getContent();
|
||||||
|
var originalBody = content.body;
|
||||||
|
var body;
|
||||||
|
|
||||||
|
if (this.props.searchTerm) {
|
||||||
|
var lastOffset = 0;
|
||||||
|
var bodyList = [];
|
||||||
|
var k = 0;
|
||||||
|
var offset;
|
||||||
|
|
||||||
|
// XXX: rather than searching for the search term in the body,
|
||||||
|
// we should be looking at the match delimiters returned by the FTS engine
|
||||||
|
if (content.format === "org.matrix.custom.html") {
|
||||||
|
var safeBody = sanitizeHtml(content.formatted_body, sanitizeHtmlParams);
|
||||||
|
var safeSearchTerm = sanitizeHtml(this.props.searchTerm, sanitizeHtmlParams);
|
||||||
|
while ((offset = safeBody.indexOf(safeSearchTerm, lastOffset)) >= 0) {
|
||||||
|
// FIXME: we need to apply the search highlighting to only the text elements of HTML, which means
|
||||||
|
// hooking into the sanitizer parser rather than treating it as a string. Otherwise
|
||||||
|
// the act of highlighting a <b/> or whatever will break the HTML badly.
|
||||||
|
bodyList.push(<span key={ k++ } dangerouslySetInnerHTML={{ __html: safeBody.substring(lastOffset, offset) }} />);
|
||||||
|
bodyList.push(<span key={ k++ } dangerouslySetInnerHTML={{ __html: safeSearchTerm }} className="mx_MessageTile_searchHighlight" />);
|
||||||
|
lastOffset = offset + safeSearchTerm.length;
|
||||||
|
}
|
||||||
|
bodyList.push(<span key={ k++ } dangerouslySetInnerHTML={{ __html: safeBody.substring(lastOffset) }} />);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
while ((offset = originalBody.indexOf(this.props.searchTerm, lastOffset)) >= 0) {
|
||||||
|
bodyList.push(<span key={ k++ } >{ originalBody.substring(lastOffset, offset) }</span>);
|
||||||
|
bodyList.push(<span key={ k++ } className="mx_MessageTile_searchHighlight">{ this.props.searchTerm }</span>);
|
||||||
|
lastOffset = offset + this.props.searchTerm.length;
|
||||||
|
}
|
||||||
|
bodyList.push(<span key={ k++ }>{ originalBody.substring(lastOffset) }</span>);
|
||||||
|
}
|
||||||
|
body = bodyList;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (content.format === "org.matrix.custom.html") {
|
||||||
|
var safeBody = sanitizeHtml(content.formatted_body, sanitizeHtmlParams);
|
||||||
|
body = <span dangerouslySetInnerHTML={{ __html: safeBody }} />;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
body = originalBody;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<span ref="content" className="mx_MTextTile mx_MessageTile_content">
|
<span ref="content" className="mx_MTextTile mx_MessageTile_content">
|
||||||
{content.body}
|
{ body }
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
|
@ -20,19 +20,30 @@ var React = require('react');
|
||||||
var Loader = require("../atoms/Spinner");
|
var Loader = require("../atoms/Spinner");
|
||||||
|
|
||||||
var MatrixClientPeg = require('matrix-react-sdk/lib/MatrixClientPeg');
|
var MatrixClientPeg = require('matrix-react-sdk/lib/MatrixClientPeg');
|
||||||
|
var sdk = require('matrix-react-sdk')
|
||||||
|
var dis = require('matrix-react-sdk/lib/dispatcher');
|
||||||
var MemberInfoController = require('matrix-react-sdk/lib/controllers/molecules/MemberInfo')
|
var MemberInfoController = require('matrix-react-sdk/lib/controllers/molecules/MemberInfo')
|
||||||
|
|
||||||
|
// FIXME: this should probably be an organism, to match with MemberList, not a molecule
|
||||||
|
|
||||||
module.exports = React.createClass({
|
module.exports = React.createClass({
|
||||||
displayName: 'MemberInfo',
|
displayName: 'MemberInfo',
|
||||||
mixins: [MemberInfoController],
|
mixins: [MemberInfoController],
|
||||||
|
|
||||||
|
onCancel: function(e) {
|
||||||
|
dis.dispatch({
|
||||||
|
action: "view_user",
|
||||||
|
member: null
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
render: function() {
|
render: function() {
|
||||||
var interactButton, kickButton, banButton, muteButton, giveModButton, spinner;
|
var interactButton, kickButton, banButton, muteButton, giveModButton, spinner;
|
||||||
if (this.props.member.userId === MatrixClientPeg.get().credentials.userId) {
|
if (this.props.member.userId === MatrixClientPeg.get().credentials.userId) {
|
||||||
interactButton = <div className="mx_ContextualMenu_field" onClick={this.onLeaveClick}>Leave room</div>;
|
interactButton = <div className="mx_MemberInfo_field" onClick={this.onLeaveClick}>Leave room</div>;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
interactButton = <div className="mx_ContextualMenu_field" onClick={this.onChatClick}>Start chat</div>;
|
interactButton = <div className="mx_MemberInfo_field" onClick={this.onChatClick}>Start chat</div>;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.state.creatingRoom) {
|
if (this.state.creatingRoom) {
|
||||||
|
@ -40,30 +51,43 @@ module.exports = React.createClass({
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.state.can.kick) {
|
if (this.state.can.kick) {
|
||||||
kickButton = <div className="mx_ContextualMenu_field" onClick={this.onKick}>
|
kickButton = <div className="mx_MemberInfo_field" onClick={this.onKick}>
|
||||||
Kick
|
Kick
|
||||||
</div>;
|
</div>;
|
||||||
}
|
}
|
||||||
if (this.state.can.ban) {
|
if (this.state.can.ban) {
|
||||||
banButton = <div className="mx_ContextualMenu_field" onClick={this.onBan}>
|
banButton = <div className="mx_MemberInfo_field" onClick={this.onBan}>
|
||||||
Ban
|
Ban
|
||||||
</div>;
|
</div>;
|
||||||
}
|
}
|
||||||
if (this.state.can.mute) {
|
if (this.state.can.mute) {
|
||||||
var muteLabel = this.state.muted ? "Unmute" : "Mute";
|
var muteLabel = this.state.muted ? "Unmute" : "Mute";
|
||||||
muteButton = <div className="mx_ContextualMenu_field" onClick={this.onMuteToggle}>
|
muteButton = <div className="mx_MemberInfo_field" onClick={this.onMuteToggle}>
|
||||||
{muteLabel}
|
{muteLabel}
|
||||||
</div>;
|
</div>;
|
||||||
}
|
}
|
||||||
if (this.state.can.modifyLevel) {
|
if (this.state.can.modifyLevel) {
|
||||||
var giveOpLabel = this.state.isTargetMod ? "Revoke Mod" : "Make Mod";
|
var giveOpLabel = this.state.isTargetMod ? "Revoke Mod" : "Make Mod";
|
||||||
giveModButton = <div className="mx_ContextualMenu_field" onClick={this.onModToggle}>
|
giveModButton = <div className="mx_MemberInfo_field" onClick={this.onModToggle}>
|
||||||
{giveOpLabel}
|
{giveOpLabel}
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var MemberAvatar = sdk.getComponent('atoms.MemberAvatar');
|
||||||
return (
|
return (
|
||||||
<div>
|
<div className="mx_MemberInfo">
|
||||||
|
<img className="mx_MemberInfo_cancel" src="img/cancel-black.png" width="18" height="18" onClick={this.onCancel}/>
|
||||||
|
<div className="mx_MemberInfo_avatar">
|
||||||
|
<MemberAvatar member={this.props.member} width={48} height={48} />
|
||||||
|
</div>
|
||||||
|
<h2>{ this.props.member.name }</h2>
|
||||||
|
<div className="mx_MemberInfo_profileField">
|
||||||
|
{ this.props.member.userId }
|
||||||
|
</div>
|
||||||
|
<div className="mx_MemberInfo_profileField">
|
||||||
|
power: { this.props.member.powerLevelNorm }%
|
||||||
|
</div>
|
||||||
|
<div className="mx_MemberInfo_buttons">
|
||||||
{interactButton}
|
{interactButton}
|
||||||
{muteButton}
|
{muteButton}
|
||||||
{kickButton}
|
{kickButton}
|
||||||
|
@ -71,6 +95,7 @@ module.exports = React.createClass({
|
||||||
{giveModButton}
|
{giveModButton}
|
||||||
{spinner}
|
{spinner}
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -20,7 +20,7 @@ var React = require('react');
|
||||||
|
|
||||||
var MatrixClientPeg = require('matrix-react-sdk/lib/MatrixClientPeg');
|
var MatrixClientPeg = require('matrix-react-sdk/lib/MatrixClientPeg');
|
||||||
var sdk = require('matrix-react-sdk')
|
var sdk = require('matrix-react-sdk')
|
||||||
var ContextualMenu = require('../../../../ContextualMenu');
|
var dis = require('matrix-react-sdk/lib/dispatcher');
|
||||||
var MemberTileController = require('matrix-react-sdk/lib/controllers/molecules/MemberTile')
|
var MemberTileController = require('matrix-react-sdk/lib/controllers/molecules/MemberTile')
|
||||||
|
|
||||||
// The Lato WOFF doesn't include sensible combining diacritics, so Chrome chokes on rendering them.
|
// The Lato WOFF doesn't include sensible combining diacritics, so Chrome chokes on rendering them.
|
||||||
|
@ -58,16 +58,9 @@ module.exports = React.createClass({
|
||||||
},
|
},
|
||||||
|
|
||||||
onClick: function(e) {
|
onClick: function(e) {
|
||||||
var self = this;
|
dis.dispatch({
|
||||||
self.setState({ 'menu': true });
|
action: 'view_user',
|
||||||
var MemberInfo = sdk.getComponent('molecules.MemberInfo');
|
member: this.props.member,
|
||||||
ContextualMenu.createMenu(MemberInfo, {
|
|
||||||
member: self.props.member,
|
|
||||||
right: window.innerWidth - e.pageX,
|
|
||||||
top: e.pageY,
|
|
||||||
onFinished: function() {
|
|
||||||
self.setState({ 'menu': false });
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -119,10 +112,10 @@ module.exports = React.createClass({
|
||||||
var isMyUser = MatrixClientPeg.get().credentials.userId == this.props.member.userId;
|
var isMyUser = MatrixClientPeg.get().credentials.userId == this.props.member.userId;
|
||||||
|
|
||||||
var power;
|
var power;
|
||||||
if (this.props.member && this.props.member.powerLevelNorm > 0) {
|
// if (this.props.member && this.props.member.powerLevelNorm > 0) {
|
||||||
var img = "img/p/p" + Math.floor(20 * this.props.member.powerLevelNorm / 100) + ".png";
|
// 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=""/>;
|
// power = <img src={ img } className="mx_MemberTile_power" width="44" height="44" alt=""/>;
|
||||||
}
|
// }
|
||||||
var presenceClass = "mx_MemberTile_offline";
|
var presenceClass = "mx_MemberTile_offline";
|
||||||
var mainClassName = "mx_MemberTile ";
|
var mainClassName = "mx_MemberTile ";
|
||||||
if (this.props.member.user) {
|
if (this.props.member.user) {
|
||||||
|
@ -134,13 +127,13 @@ module.exports = React.createClass({
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
mainClassName += presenceClass;
|
mainClassName += presenceClass;
|
||||||
if (this.state.hover || this.state.menu) {
|
if (this.state.hover) {
|
||||||
mainClassName += " mx_MemberTile_hover";
|
mainClassName += " mx_MemberTile_hover";
|
||||||
}
|
}
|
||||||
|
|
||||||
var name = this.props.member.name;
|
var name = this.props.member.name;
|
||||||
// if (isMyUser) name += " (me)"; // this does nothing other than introduce line wrapping and pain
|
// if (isMyUser) name += " (me)"; // this does nothing other than introduce line wrapping and pain
|
||||||
var leave = isMyUser ? <img className="mx_MemberTile_leave" src="img/delete.png" width="10" height="10" onClick={this.onLeaveClick}/> : null;
|
//var leave = isMyUser ? <img className="mx_MemberTile_leave" src="img/delete.png" width="10" height="10" onClick={this.onLeaveClick}/> : null;
|
||||||
|
|
||||||
var nameClass = "mx_MemberTile_name";
|
var nameClass = "mx_MemberTile_name";
|
||||||
if (zalgo.test(name)) {
|
if (zalgo.test(name)) {
|
||||||
|
@ -148,7 +141,7 @@ module.exports = React.createClass({
|
||||||
}
|
}
|
||||||
|
|
||||||
var nameEl;
|
var nameEl;
|
||||||
if (this.state.hover || this.state.menu) {
|
if (this.state.hover) {
|
||||||
var presence;
|
var presence;
|
||||||
// FIXME: make presence data update whenever User.presence changes...
|
// FIXME: make presence data update whenever User.presence changes...
|
||||||
var active = this.props.member.user ? ((Date.now() - (this.props.member.user.lastPresenceTs - this.props.member.user.lastActiveAgo)) || -1) : -1;
|
var active = this.props.member.user ? ((Date.now() - (this.props.member.user.lastPresenceTs - this.props.member.user.lastActiveAgo)) || -1) : -1;
|
||||||
|
@ -161,8 +154,8 @@ module.exports = React.createClass({
|
||||||
|
|
||||||
nameEl =
|
nameEl =
|
||||||
<div className="mx_MemberTile_details">
|
<div className="mx_MemberTile_details">
|
||||||
{ leave }
|
<img className="mx_MemberTile_chevron" src="img/member_chevron.png" width="8" height="12"/>
|
||||||
<div className="mx_MemberTile_userId">{ this.props.member.userId }</div>
|
<div className="mx_MemberTile_userId">{ name }</div>
|
||||||
{ presence }
|
{ presence }
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
@ -177,7 +170,7 @@ module.exports = React.createClass({
|
||||||
return (
|
return (
|
||||||
<div className={mainClassName} title={ this.getPowerLabel() } onClick={ this.onClick } onMouseEnter={ this.mouseEnter } onMouseLeave={ this.mouseLeave }>
|
<div className={mainClassName} title={ this.getPowerLabel() } onClick={ this.onClick } onMouseEnter={ this.mouseEnter } onMouseLeave={ this.mouseLeave }>
|
||||||
<div className="mx_MemberTile_avatar">
|
<div className="mx_MemberTile_avatar">
|
||||||
<MemberAvatar member={this.props.member} />
|
<MemberAvatar member={this.props.member} width={36} height={36} />
|
||||||
{ power }
|
{ power }
|
||||||
</div>
|
</div>
|
||||||
{ nameEl }
|
{ nameEl }
|
||||||
|
|
|
@ -22,6 +22,7 @@ var MatrixClientPeg = require('matrix-react-sdk/lib/MatrixClientPeg');
|
||||||
var MessageComposerController = require('matrix-react-sdk/lib/controllers/molecules/MessageComposer')
|
var MessageComposerController = require('matrix-react-sdk/lib/controllers/molecules/MessageComposer')
|
||||||
|
|
||||||
var sdk = require('matrix-react-sdk')
|
var sdk = require('matrix-react-sdk')
|
||||||
|
var dis = require('matrix-react-sdk/lib/dispatcher')
|
||||||
|
|
||||||
module.exports = React.createClass({
|
module.exports = React.createClass({
|
||||||
displayName: 'MessageComposer',
|
displayName: 'MessageComposer',
|
||||||
|
@ -40,6 +41,14 @@ module.exports = React.createClass({
|
||||||
this.refs.uploadInput.getDOMNode().value = null;
|
this.refs.uploadInput.getDOMNode().value = null;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
onCallClick: function(ev) {
|
||||||
|
dis.dispatch({
|
||||||
|
action: 'place_call',
|
||||||
|
type: ev.shiftKey ? "screensharing" : "video",
|
||||||
|
room_id: this.props.room.roomId
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
render: function() {
|
render: function() {
|
||||||
var me = this.props.room.getMember(MatrixClientPeg.get().credentials.userId);
|
var me = this.props.room.getMember(MatrixClientPeg.get().credentials.userId);
|
||||||
var uploadInputStyle = {display: 'none'};
|
var uploadInputStyle = {display: 'none'};
|
||||||
|
@ -49,15 +58,18 @@ module.exports = React.createClass({
|
||||||
<div className="mx_MessageComposer_wrapper">
|
<div className="mx_MessageComposer_wrapper">
|
||||||
<div className="mx_MessageComposer_row">
|
<div className="mx_MessageComposer_row">
|
||||||
<div className="mx_MessageComposer_avatar">
|
<div className="mx_MessageComposer_avatar">
|
||||||
<MemberAvatar member={me} />
|
<MemberAvatar member={me} width={24} height={24} />
|
||||||
</div>
|
</div>
|
||||||
<div className="mx_MessageComposer_input">
|
<div className="mx_MessageComposer_input">
|
||||||
<textarea ref="textarea" onKeyDown={this.onKeyDown} placeholder="Type a message" />
|
<textarea ref="textarea" onKeyDown={this.onKeyDown} placeholder="Type a message..." />
|
||||||
</div>
|
</div>
|
||||||
<div className="mx_MessageComposer_upload" onClick={this.onUploadClick}>
|
<div className="mx_MessageComposer_upload" onClick={this.onUploadClick}>
|
||||||
<img src="img/upload.png" width="32" height="32"/>
|
<img src="img/upload.png" width="17" height="22"/>
|
||||||
<input type="file" style={uploadInputStyle} ref="uploadInput" onChange={this.onUploadFileSelected} />
|
<input type="file" style={uploadInputStyle} ref="uploadInput" onChange={this.onUploadFileSelected} />
|
||||||
</div>
|
</div>
|
||||||
|
<div className="mx_MessageComposer_call" onClick={this.onCallClick}>
|
||||||
|
<img src="img/call.png" width="28" height="20"/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -84,7 +84,7 @@ module.exports = React.createClass({
|
||||||
else {
|
else {
|
||||||
redactButton = (
|
redactButton = (
|
||||||
<div className="mx_ContextualMenu_field" onClick={this.onRedactClick}>
|
<div className="mx_ContextualMenu_field" onClick={this.onRedactClick}>
|
||||||
Delete
|
Redact
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,6 +50,6 @@ module.exports = React.createClass({
|
||||||
TileType = tileTypes[msgtype];
|
TileType = tileTypes[msgtype];
|
||||||
}
|
}
|
||||||
|
|
||||||
return <TileType mxEvent={this.props.mxEvent} />;
|
return <TileType mxEvent={this.props.mxEvent} searchTerm={this.props.searchTerm} />;
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
@ -59,7 +59,6 @@ module.exports = React.createClass({
|
||||||
var topic = this.props.room.currentState.getStateEvents('m.room.topic', '');
|
var topic = this.props.room.currentState.getStateEvents('m.room.topic', '');
|
||||||
|
|
||||||
var call_buttons;
|
var call_buttons;
|
||||||
var zoom_button;
|
|
||||||
if (this.state && this.state.call_state != 'ended') {
|
if (this.state && this.state.call_state != 'ended') {
|
||||||
//var muteVideoButton;
|
//var muteVideoButton;
|
||||||
var activeCall = (
|
var activeCall = (
|
||||||
|
@ -111,16 +110,15 @@ module.exports = React.createClass({
|
||||||
cancel_button = <div className="mx_RoomHeader_textButton" onClick={this.props.onCancelClick}>Cancel</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>
|
save_button = <div className="mx_RoomHeader_textButton" onClick={this.props.onSaveClick}>Save Changes</div>
|
||||||
} else {
|
} else {
|
||||||
|
// <EditableText label={this.props.room.name} initialValue={actual_name} placeHolder="Name" onValueChanged={this.onNameChange} />
|
||||||
name =
|
name =
|
||||||
<div className="mx_RoomHeader_name">
|
<div className="mx_RoomHeader_name" onClick={this.props.onSettingsClick}>
|
||||||
<EditableText label={this.props.room.name} initialValue={actual_name} placeHolder="Name" onValueChanged={this.onNameChange} />
|
<div className="mx_RoomHeader_nametext">{ this.props.room.name }</div>
|
||||||
|
<div className="mx_RoomHeader_settingsButton">
|
||||||
|
<img src="img/settings.png" width="12" height="12"/>
|
||||||
|
</div>
|
||||||
</div>
|
</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" title={topic.getContent().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"/>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var roomAvatar = null;
|
var roomAvatar = null;
|
||||||
|
@ -130,13 +128,24 @@ module.exports = React.createClass({
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (activeCall && activeCall.type == "video") {
|
var zoom_button, video_button, voice_button;
|
||||||
|
if (activeCall) {
|
||||||
|
if (activeCall.type == "video") {
|
||||||
zoom_button = (
|
zoom_button = (
|
||||||
<div className="mx_RoomHeader_button" onClick={this.onFullscreenClick}>
|
<div className="mx_RoomHeader_button" onClick={this.onFullscreenClick}>
|
||||||
<img src="img/zoom.png" title="Fullscreen" alt="Fullscreen" width="32" height="32" style={{ 'marginTop': '3px' }}/>
|
<img src="img/zoom.png" title="Fullscreen" alt="Fullscreen" width="32" height="32" style={{ 'marginTop': '-5px' }}/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
video_button =
|
||||||
|
<div className="mx_RoomHeader_button mx_RoomHeader_video" onClick={activeCall && activeCall.type === "video" ? this.onMuteVideoClick : this.onVideoClick}>
|
||||||
|
<img src="img/video.png" title="Video call" alt="Video call" width="32" height="32" style={{ 'marginTop': '-8px' }}/>
|
||||||
|
</div>;
|
||||||
|
voice_button =
|
||||||
|
<div className="mx_RoomHeader_button mx_RoomHeader_voice" onClick={activeCall ? this.onMuteAudioClick : this.onVoiceClick}>
|
||||||
|
<img src="img/voip.png" title="VoIP call" alt="VoIP call" width="32" height="32" style={{ 'marginTop': '-8px' }}/>
|
||||||
|
</div>;
|
||||||
|
}
|
||||||
|
|
||||||
header =
|
header =
|
||||||
<div className="mx_RoomHeader_wrapper">
|
<div className="mx_RoomHeader_wrapper">
|
||||||
|
@ -153,16 +162,11 @@ module.exports = React.createClass({
|
||||||
{cancel_button}
|
{cancel_button}
|
||||||
{save_button}
|
{save_button}
|
||||||
<div className="mx_RoomHeader_rightRow">
|
<div className="mx_RoomHeader_rightRow">
|
||||||
{ settings_button }
|
{ video_button }
|
||||||
|
{ voice_button }
|
||||||
{ zoom_button }
|
{ zoom_button }
|
||||||
<div className="mx_RoomHeader_button mx_RoomHeader_search">
|
<div className="mx_RoomHeader_button">
|
||||||
<img src="img/search.png" title="Search" alt="Search" width="32" height="32"/>
|
<img src="img/search.png" title="Search" alt="Search" width="21" height="19" onClick={this.props.onSearchClick}/>
|
||||||
</div>
|
|
||||||
<div className="mx_RoomHeader_button mx_RoomHeader_video" onClick={activeCall && activeCall.type === "video" ? this.onMuteVideoClick : this.onVideoClick}>
|
|
||||||
<img src="img/video.png" title="Video call" alt="Video call" width="32" height="32"/>
|
|
||||||
</div>
|
|
||||||
<div className="mx_RoomHeader_button mx_RoomHeader_voice" onClick={activeCall ? this.onMuteAudioClick : this.onVoiceClick}>
|
|
||||||
<img src="img/voip.png" title="VoIP call" alt="VoIP call" width="32" height="32"/>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -50,7 +50,16 @@ module.exports = React.createClass({
|
||||||
'mx_RoomTile_highlight': this.props.highlight,
|
'mx_RoomTile_highlight': this.props.highlight,
|
||||||
'mx_RoomTile_invited': this.props.room.currentState.members[myUserId].membership == 'invite'
|
'mx_RoomTile_invited': this.props.room.currentState.members[myUserId].membership == 'invite'
|
||||||
});
|
});
|
||||||
var name = this.props.room.name.replace(":", ":\u200b");
|
|
||||||
|
var name;
|
||||||
|
if (this.props.isInvite) {
|
||||||
|
name = this.props.room.getMember(MatrixClientPeg.get().credentials.userId).events.member.getSender();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
name = this.props.room.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
name = name.replace(":", ":\u200b"); // add a zero-width space to allow linewrapping after the colon
|
||||||
var badge;
|
var badge;
|
||||||
if (this.props.highlight) {
|
if (this.props.highlight) {
|
||||||
badge = <div className="mx_RoomTile_badge"/>;
|
badge = <div className="mx_RoomTile_badge"/>;
|
||||||
|
@ -73,7 +82,8 @@ module.exports = React.createClass({
|
||||||
|
|
||||||
var label;
|
var label;
|
||||||
if (!this.props.collapsed) {
|
if (!this.props.collapsed) {
|
||||||
label = <div className="mx_RoomTile_name">{name}</div>;
|
var className = 'mx_RoomTile_name' + (this.props.isInvite ? ' mx_RoomTile_invite' : '');
|
||||||
|
label = <div className={ className }>{name}</div>;
|
||||||
}
|
}
|
||||||
else if (this.state.hover) {
|
else if (this.state.hover) {
|
||||||
var RoomTooltip = sdk.getComponent("molecules.RoomTooltip");
|
var RoomTooltip = sdk.getComponent("molecules.RoomTooltip");
|
||||||
|
@ -84,7 +94,7 @@ module.exports = React.createClass({
|
||||||
return (
|
return (
|
||||||
<div className={classes} onClick={this.onClick} onMouseEnter={this.onMouseEnter} onMouseLeave={this.onMouseLeave}>
|
<div className={classes} onClick={this.onClick} onMouseEnter={this.onMouseEnter} onMouseLeave={this.onMouseLeave}>
|
||||||
<div className="mx_RoomTile_avatar">
|
<div className="mx_RoomTile_avatar">
|
||||||
<RoomAvatar room={this.props.room} />
|
<RoomAvatar room={this.props.room} width="24" height="24" />
|
||||||
{ badge }
|
{ badge }
|
||||||
</div>
|
</div>
|
||||||
{ label }
|
{ label }
|
||||||
|
|
|
@ -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('matrix-react-sdk/lib/MatrixClientPeg');
|
||||||
|
var sdk = require('matrix-react-sdk');
|
||||||
|
|
||||||
|
module.exports = React.createClass({
|
||||||
|
displayName: 'SearchBar',
|
||||||
|
|
||||||
|
getInitialState: function() {
|
||||||
|
return ({
|
||||||
|
scope: 'Room'
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
onThisRoomClick: function() {
|
||||||
|
this.setState({ scope: 'Room' });
|
||||||
|
},
|
||||||
|
|
||||||
|
onAllRoomsClick: function() {
|
||||||
|
this.setState({ scope: 'All' });
|
||||||
|
},
|
||||||
|
|
||||||
|
onSearchChange: function(e) {
|
||||||
|
if (e.keyCode === 13) { // on enter...
|
||||||
|
this.props.onSearch(this.refs.search_term.getDOMNode().value, this.state.scope);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
render: function() {
|
||||||
|
return (
|
||||||
|
<div className="mx_SearchBar">
|
||||||
|
<input ref="search_term" className="mx_SearchBar_input" type="text" autoFocus={true} placeholder="Search..." onKeyDown={this.onSearchChange}/>
|
||||||
|
<div className={"mx_SearchBar_button" + (this.state.scope !== 'Room' ? " mx_SearchBar_unselected" : "")} onClick={this.onThisRoomClick}>This Room</div>
|
||||||
|
<div className={"mx_SearchBar_button" + (this.state.scope !== 'All' ? " mx_SearchBar_unselected" : "")} onClick={this.onAllRoomsClick}>All Rooms</div>
|
||||||
|
<img className="mx_SearchBar_cancel" src="img/cancel-black.png" width="18" height="18" onClick={this.props.onCancelClick} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
|
@ -40,7 +40,8 @@ module.exports = React.createClass({
|
||||||
classes += " collapsed";
|
classes += " collapsed";
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
collapseButton = <img className="mx_LeftPanel_hideButton" onClick={ this.onHideClick } src="img/hide.png" width="12" height="20" alt="<"/>
|
// Hide the collapse button until we work out how to display it in the new skin
|
||||||
|
// collapseButton = <img className="mx_LeftPanel_hideButton" onClick={ this.onHideClick } src="img/hide.png" width="12" height="20" alt="<"/>
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -30,7 +30,6 @@ module.exports = React.createClass({
|
||||||
mixins: [MemberListController],
|
mixins: [MemberListController],
|
||||||
|
|
||||||
getInitialState: function() {
|
getInitialState: function() {
|
||||||
return { editing: false };
|
|
||||||
},
|
},
|
||||||
|
|
||||||
memberSort: function(userIdA, userIdB) {
|
memberSort: function(userIdA, userIdB) {
|
||||||
|
@ -71,43 +70,21 @@ module.exports = React.createClass({
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
onPopulateInvite: function(inputText, shouldSubmit) {
|
onPopulateInvite: function(e) {
|
||||||
// reset back to placeholder
|
this.onInvite(this.refs.invite.getDOMNode().value);
|
||||||
this.refs.invite.setValue("Invite", false, true);
|
e.preventDefault();
|
||||||
this.setState({ editing: false });
|
|
||||||
if (!shouldSubmit) {
|
|
||||||
return; // enter key wasn't pressed
|
|
||||||
}
|
|
||||||
this.onInvite(inputText);
|
|
||||||
},
|
|
||||||
|
|
||||||
onClickInvite: function(ev) {
|
|
||||||
this.setState({ editing: true });
|
|
||||||
this.refs.invite.onClickDiv();
|
|
||||||
ev.stopPropagation();
|
|
||||||
ev.preventDefault();
|
|
||||||
},
|
},
|
||||||
|
|
||||||
inviteTile: function() {
|
inviteTile: function() {
|
||||||
var classes = classNames({
|
|
||||||
mx_MemberTile: true,
|
|
||||||
mx_MemberTile_inviteTile: true,
|
|
||||||
mx_MemberTile_inviteEditing: this.state.editing,
|
|
||||||
});
|
|
||||||
|
|
||||||
var EditableText = sdk.getComponent("atoms.EditableText");
|
|
||||||
if (this.state.inviting) {
|
if (this.state.inviting) {
|
||||||
return (
|
return (
|
||||||
<Loader />
|
<Loader />
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
return (
|
return (
|
||||||
<div className={ classes } onClick={ this.onClickInvite } >
|
<form onSubmit={this.onPopulateInvite}>
|
||||||
<div className="mx_MemberTile_avatar"><img src="img/create-big.png" width="40" height="40" alt=""/></div>
|
<input className="mx_MemberList_invite" ref="invite" placeholder="Invite another user"/>
|
||||||
<div className="mx_MemberTile_name">
|
</form>
|
||||||
<EditableText ref="invite" label="Invite" placeHolder="@user:domain.com" initialValue="" onValueChanged={this.onPopulateInvite}/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -117,7 +94,7 @@ module.exports = React.createClass({
|
||||||
var invitedMemberTiles = this.makeMemberTiles('invite');
|
var invitedMemberTiles = this.makeMemberTiles('invite');
|
||||||
if (invitedMemberTiles.length > 0) {
|
if (invitedMemberTiles.length > 0) {
|
||||||
invitedSection = (
|
invitedSection = (
|
||||||
<div>
|
<div className="mx_MemberList_invited">
|
||||||
<h2>Invited</h2>
|
<h2>Invited</h2>
|
||||||
<div className="mx_MemberList_wrapper">
|
<div className="mx_MemberList_wrapper">
|
||||||
{invitedMemberTiles}
|
{invitedMemberTiles}
|
||||||
|
@ -127,18 +104,14 @@ module.exports = React.createClass({
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<div className="mx_MemberList">
|
<div className="mx_MemberList">
|
||||||
<div className="mx_MemberList_chevron">
|
|
||||||
<img src="img/chevron.png" width="24" height="13"/>
|
|
||||||
</div>
|
|
||||||
<div className="mx_MemberList_border">
|
<div className="mx_MemberList_border">
|
||||||
|
{this.inviteTile()}
|
||||||
<div>
|
<div>
|
||||||
<h2>Members</h2>
|
|
||||||
<div className="mx_MemberList_wrapper">
|
<div className="mx_MemberList_wrapper">
|
||||||
{this.makeMemberTiles('join')}
|
{this.makeMemberTiles('join')}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{invitedSection}
|
{invitedSection}
|
||||||
{this.inviteTile()}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -19,6 +19,7 @@ limitations under the License.
|
||||||
var React = require('react');
|
var React = require('react');
|
||||||
var sdk = require('matrix-react-sdk')
|
var sdk = require('matrix-react-sdk')
|
||||||
var dis = require('matrix-react-sdk/lib/dispatcher');
|
var dis = require('matrix-react-sdk/lib/dispatcher');
|
||||||
|
var MatrixClientPeg = require("matrix-react-sdk/lib/MatrixClientPeg");
|
||||||
|
|
||||||
module.exports = React.createClass({
|
module.exports = React.createClass({
|
||||||
displayName: 'RightPanel',
|
displayName: 'RightPanel',
|
||||||
|
@ -26,6 +27,20 @@ module.exports = React.createClass({
|
||||||
Phase : {
|
Phase : {
|
||||||
MemberList: 'MemberList',
|
MemberList: 'MemberList',
|
||||||
FileList: 'FileList',
|
FileList: 'FileList',
|
||||||
|
MemberInfo: 'MemberInfo',
|
||||||
|
},
|
||||||
|
|
||||||
|
componentWillMount: function() {
|
||||||
|
this.dispatcherRef = dis.register(this.onAction);
|
||||||
|
var cli = MatrixClientPeg.get();
|
||||||
|
cli.on("RoomState.members", this.onRoomStateMember);
|
||||||
|
},
|
||||||
|
|
||||||
|
componentWillUnmount: function() {
|
||||||
|
dis.unregister(this.dispatcherRef);
|
||||||
|
if (MatrixClientPeg.get()) {
|
||||||
|
MatrixClientPeg.get().removeListener("RoomState.members", this.onRoomStateMember);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
getInitialState: function() {
|
getInitialState: function() {
|
||||||
|
@ -48,25 +63,85 @@ module.exports = React.createClass({
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
onRoomStateMember: function(ev, state, member) {
|
||||||
|
// redraw the badge on the membership list
|
||||||
|
if (this.state.phase == this.Phase.MemberList && member.roomId === this.props.roomId) {
|
||||||
|
this.forceUpdate();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
onAction: function(payload) {
|
||||||
|
if (payload.action === "view_user") {
|
||||||
|
if (payload.member) {
|
||||||
|
this.setState({
|
||||||
|
phase: this.Phase.MemberInfo,
|
||||||
|
member: payload.member,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.setState({
|
||||||
|
phase: this.Phase.MemberList
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (payload.action === "view_room") {
|
||||||
|
if (this.state.phase === this.Phase.MemberInfo) {
|
||||||
|
this.setState({
|
||||||
|
phase: this.Phase.MemberList
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
render: function() {
|
render: function() {
|
||||||
var MemberList = sdk.getComponent('organisms.MemberList');
|
var MemberList = sdk.getComponent('organisms.MemberList');
|
||||||
var buttonGroup;
|
var buttonGroup;
|
||||||
var panel;
|
var panel;
|
||||||
|
|
||||||
|
var filesHighlight;
|
||||||
|
var membersHighlight;
|
||||||
|
if (!this.props.collapsed) {
|
||||||
|
if (this.state.phase == this.Phase.MemberList || this.state.phase === this.Phase.MemberInfo) {
|
||||||
|
membersHighlight = <div className="mx_RightPanel_headerButton_highlight"></div>;
|
||||||
|
}
|
||||||
|
else if (this.state.phase == this.Phase.FileList) {
|
||||||
|
filesHighlight = <div className="mx_RightPanel_headerButton_highlight"></div>;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var membersBadge;
|
||||||
|
if ((this.state.phase == this.Phase.MemberList || this.state.phase === this.Phase.MemberInfo) && this.props.roomId) {
|
||||||
|
var cli = MatrixClientPeg.get();
|
||||||
|
var room = cli.getRoom(this.props.roomId);
|
||||||
|
if (room) {
|
||||||
|
membersBadge = <div className="mx_RightPanel_headerButton_badge">{ room.getJoinedMembers().length }</div>;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (this.props.roomId) {
|
if (this.props.roomId) {
|
||||||
buttonGroup =
|
buttonGroup =
|
||||||
<div className="mx_RightPanel_headerButtonGroup">
|
<div className="mx_RightPanel_headerButtonGroup">
|
||||||
<div className="mx_RightPanel_headerButton mx_RightPanel_filebutton">
|
|
||||||
<img src="img/file.png" width="32" height="32" title="Files" alt="Files"/>
|
|
||||||
</div>
|
|
||||||
<div className="mx_RightPanel_headerButton" onClick={ this.onMemberListButtonClick }>
|
<div className="mx_RightPanel_headerButton" onClick={ this.onMemberListButtonClick }>
|
||||||
<img src="img/members.png" width="32" height="32" title="Members" alt="Members"/>
|
<img src="img/members.png" width="17" height="22" title="Members" alt="Members"/>
|
||||||
|
{ membersBadge }
|
||||||
|
{ membersHighlight }
|
||||||
|
</div>
|
||||||
|
<div className="mx_RightPanel_headerButton mx_RightPanel_filebutton">
|
||||||
|
<img src="img/files.png" width="17" height="22" title="Files" alt="Files"/>
|
||||||
|
{ filesHighlight }
|
||||||
</div>
|
</div>
|
||||||
</div>;
|
</div>;
|
||||||
|
|
||||||
if (!this.props.collapsed && this.state.phase == this.Phase.MemberList) {
|
if (!this.props.collapsed) {
|
||||||
|
if(this.state.phase == this.Phase.MemberList) {
|
||||||
panel = <MemberList roomId={this.props.roomId} key={this.props.roomId} />
|
panel = <MemberList roomId={this.props.roomId} key={this.props.roomId} />
|
||||||
}
|
}
|
||||||
|
else if(this.state.phase == this.Phase.MemberInfo) {
|
||||||
|
var MemberInfo = sdk.getComponent('molecules.MemberInfo');
|
||||||
|
panel = <MemberInfo roomId={this.props.roomId} member={this.state.member} key={this.props.roomId} />
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var classes = "mx_RightPanel";
|
var classes = "mx_RightPanel";
|
||||||
|
|
|
@ -69,6 +69,7 @@ module.exports = React.createClass({
|
||||||
});
|
});
|
||||||
}, function(err) {
|
}, function(err) {
|
||||||
console.error("Failed to join room: %s", JSON.stringify(err));
|
console.error("Failed to join room: %s", JSON.stringify(err));
|
||||||
|
var ErrorDialog = sdk.getComponent("organisms.ErrorDialog");
|
||||||
Modal.createDialog(ErrorDialog, {
|
Modal.createDialog(ErrorDialog, {
|
||||||
title: "Failed to join room",
|
title: "Failed to join room",
|
||||||
description: err.message
|
description: err.message
|
||||||
|
|
|
@ -41,22 +41,38 @@ module.exports = React.createClass({
|
||||||
callElement = <CallView className="mx_MatrixChat_callView"/>
|
callElement = <CallView className="mx_MatrixChat_callView"/>
|
||||||
}
|
}
|
||||||
|
|
||||||
var recentsLabel = this.props.collapsed ?
|
var expandButton = this.props.collapsed ?
|
||||||
<img style={{cursor: 'pointer'}} onClick={ this.onShowClick } src="img/menu.png" width="27" height="20" alt=">"/> :
|
<img className="mx_RoomList_expandButton" onClick={ this.onShowClick } src="img/menu.png" width="20" alt=">"/> :
|
||||||
"Recents";
|
null;
|
||||||
|
|
||||||
|
var invitesLabel = this.props.collapsed ? null : "Invites";
|
||||||
|
var recentsLabel = this.props.collapsed ? null : "Recent";
|
||||||
|
|
||||||
|
var invites;
|
||||||
|
if (this.state.inviteList.length) {
|
||||||
|
invites = <div>
|
||||||
|
<h2 className="mx_RoomList_invitesLabel">{ invitesLabel }</h2>
|
||||||
|
<div className="mx_RoomList_invites">
|
||||||
|
{this.makeRoomTiles(this.state.inviteList, true)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="mx_RoomList" onScroll={this._repositionTooltip}>
|
<div className="mx_RoomList" onScroll={this._repositionTooltip}>
|
||||||
{callElement}
|
{ expandButton }
|
||||||
<h2 className="mx_RoomList_favourites_label">Favourites</h2>
|
{ callElement }
|
||||||
|
<h2 className="mx_RoomList_favouritesLabel">Favourites</h2>
|
||||||
<RoomDropTarget text="Drop here to favourite"/>
|
<RoomDropTarget text="Drop here to favourite"/>
|
||||||
|
|
||||||
<h2 className="mx_RoomList_recents_label">{ recentsLabel }</h2>
|
{ invites }
|
||||||
|
|
||||||
|
<h2 className="mx_RoomList_recentsLabel">{ recentsLabel }</h2>
|
||||||
<div className="mx_RoomList_recents">
|
<div className="mx_RoomList_recents">
|
||||||
{this.makeRoomTiles()}
|
{this.makeRoomTiles(this.state.roomList, false)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h2 className="mx_RoomList_archive_label">Archive</h2>
|
<h2 className="mx_RoomList_archiveLabel">Archive</h2>
|
||||||
<RoomDropTarget text="Drop here to archive"/>
|
<RoomDropTarget text="Drop here to archive"/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -63,6 +63,29 @@ module.exports = React.createClass({
|
||||||
this.setState(this.getInitialState());
|
this.setState(this.getInitialState());
|
||||||
},
|
},
|
||||||
|
|
||||||
|
onRejectButtonClicked: function(ev) {
|
||||||
|
var self = this;
|
||||||
|
this.setState({
|
||||||
|
rejecting: true
|
||||||
|
});
|
||||||
|
MatrixClientPeg.get().leave(this.props.roomId).done(function() {
|
||||||
|
dis.dispatch({ action: 'view_next_room' });
|
||||||
|
self.setState({
|
||||||
|
rejecting: false
|
||||||
|
});
|
||||||
|
}, function(err) {
|
||||||
|
console.error("Failed to reject invite: %s", err);
|
||||||
|
self.setState({
|
||||||
|
rejecting: false,
|
||||||
|
rejectError: err
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
onSearchClick: function() {
|
||||||
|
this.setState({ searching: true });
|
||||||
|
},
|
||||||
|
|
||||||
onConferenceNotificationClick: function() {
|
onConferenceNotificationClick: function() {
|
||||||
dis.dispatch({
|
dis.dispatch({
|
||||||
action: 'place_call',
|
action: 'place_call',
|
||||||
|
@ -89,6 +112,7 @@ module.exports = React.createClass({
|
||||||
var MessageComposer = sdk.getComponent('molecules.MessageComposer');
|
var MessageComposer = sdk.getComponent('molecules.MessageComposer');
|
||||||
var CallView = sdk.getComponent("molecules.voip.CallView");
|
var CallView = sdk.getComponent("molecules.voip.CallView");
|
||||||
var RoomSettings = sdk.getComponent("molecules.RoomSettings");
|
var RoomSettings = sdk.getComponent("molecules.RoomSettings");
|
||||||
|
var SearchBar = sdk.getComponent("molecules.SearchBar");
|
||||||
|
|
||||||
if (!this.state.room) {
|
if (!this.state.room) {
|
||||||
if (this.props.roomId) {
|
if (this.props.roomId) {
|
||||||
|
@ -106,7 +130,7 @@ module.exports = React.createClass({
|
||||||
|
|
||||||
var myUserId = MatrixClientPeg.get().credentials.userId;
|
var myUserId = MatrixClientPeg.get().credentials.userId;
|
||||||
if (this.state.room.currentState.members[myUserId].membership == 'invite') {
|
if (this.state.room.currentState.members[myUserId].membership == 'invite') {
|
||||||
if (this.state.joining) {
|
if (this.state.joining || this.state.rejecting) {
|
||||||
return (
|
return (
|
||||||
<div className="mx_RoomView">
|
<div className="mx_RoomView">
|
||||||
<Loader />
|
<Loader />
|
||||||
|
@ -116,6 +140,7 @@ module.exports = React.createClass({
|
||||||
var inviteEvent = this.state.room.currentState.members[myUserId].events.member.event;
|
var inviteEvent = this.state.room.currentState.members[myUserId].events.member.event;
|
||||||
// XXX: Leaving this intentionally basic for now because invites are about to change totally
|
// XXX: Leaving this intentionally basic for now because invites are about to change totally
|
||||||
var joinErrorText = this.state.joinError ? "Failed to join room!" : "";
|
var joinErrorText = this.state.joinError ? "Failed to join room!" : "";
|
||||||
|
var rejectErrorText = this.state.rejectError ? "Failed to reject invite!" : "";
|
||||||
return (
|
return (
|
||||||
<div className="mx_RoomView">
|
<div className="mx_RoomView">
|
||||||
<RoomHeader ref="header" room={this.state.room} simpleHeader="Room invite"/>
|
<RoomHeader ref="header" room={this.state.room} simpleHeader="Room invite"/>
|
||||||
|
@ -123,7 +148,9 @@ module.exports = React.createClass({
|
||||||
<div>{inviteEvent.user_id} has invited you to a room</div>
|
<div>{inviteEvent.user_id} has invited you to a room</div>
|
||||||
<br/>
|
<br/>
|
||||||
<button ref="joinButton" onClick={this.onJoinButtonClicked}>Join</button>
|
<button ref="joinButton" onClick={this.onJoinButtonClicked}>Join</button>
|
||||||
|
<button onClick={this.onRejectButtonClicked}>Reject</button>
|
||||||
<div className="error">{joinErrorText}</div>
|
<div className="error">{joinErrorText}</div>
|
||||||
|
<div className="error">{rejectErrorText}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -159,8 +186,8 @@ module.exports = React.createClass({
|
||||||
<div className="mx_RoomView_uploadProgressOuter">
|
<div className="mx_RoomView_uploadProgressOuter">
|
||||||
<div className="mx_RoomView_uploadProgressInner" style={innerProgressStyle}></div>
|
<div className="mx_RoomView_uploadProgressInner" style={innerProgressStyle}></div>
|
||||||
</div>
|
</div>
|
||||||
<img className="mx_RoomView_uploadIcon" src="img/fileicon.png" width="40" height="40"/>
|
<img className="mx_RoomView_uploadIcon" src="img/fileicon.png" width="17" height="22"/>
|
||||||
<img className="mx_RoomView_uploadCancel" src="img/cancel.png" width="40" height="40"/>
|
<img className="mx_RoomView_uploadCancel" src="img/cancel.png" width="18" height="18"/>
|
||||||
<div className="mx_RoomView_uploadBytes">
|
<div className="mx_RoomView_uploadBytes">
|
||||||
{ uploadedSize } / { totalSize }
|
{ uploadedSize } / { totalSize }
|
||||||
</div>
|
</div>
|
||||||
|
@ -175,7 +202,7 @@ module.exports = React.createClass({
|
||||||
if (unreadMsgs) {
|
if (unreadMsgs) {
|
||||||
statusBar = (
|
statusBar = (
|
||||||
<div className="mx_RoomView_unreadMessagesBar" onClick={ this.scrollToBottom }>
|
<div className="mx_RoomView_unreadMessagesBar" onClick={ this.scrollToBottom }>
|
||||||
<img src="img/newmessages.png" width="10" height="12" alt=""/>
|
<img src="img/newmessages.png" width="24" height="24" alt=""/>
|
||||||
{unreadMsgs}
|
{unreadMsgs}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -183,19 +210,22 @@ module.exports = React.createClass({
|
||||||
else if (typingString) {
|
else if (typingString) {
|
||||||
statusBar = (
|
statusBar = (
|
||||||
<div className="mx_RoomView_typingBar">
|
<div className="mx_RoomView_typingBar">
|
||||||
<img src="img/typing.png" width="40" height="40" alt=""/>
|
<div className="mx_RoomView_typingImage">...</div>
|
||||||
{typingString}
|
{typingString}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var roomEdit = null;
|
var aux = null;
|
||||||
if (this.state.editingRoomSettings) {
|
if (this.state.editingRoomSettings) {
|
||||||
roomEdit = <RoomSettings ref="room_settings" onSaveClick={this.onSaveClick} room={this.state.room} />;
|
aux = <RoomSettings ref="room_settings" onSaveClick={this.onSaveClick} room={this.state.room} />;
|
||||||
}
|
}
|
||||||
if (this.state.uploadingRoomSettings) {
|
else if (this.state.uploadingRoomSettings) {
|
||||||
roomEdit = <Loader/>;
|
aux = <Loader/>;
|
||||||
|
}
|
||||||
|
else if (this.state.searching) {
|
||||||
|
aux = <SearchBar ref="search_bar" onCancelClick={this.onCancelClick} onSearch={this.onSearch}/>;
|
||||||
}
|
}
|
||||||
|
|
||||||
var conferenceCallNotification = null;
|
var conferenceCallNotification = null;
|
||||||
|
@ -211,7 +241,7 @@ module.exports = React.createClass({
|
||||||
if (this.state.draggingFile) {
|
if (this.state.draggingFile) {
|
||||||
fileDropTarget = <div className="mx_RoomView_fileDropTarget">
|
fileDropTarget = <div className="mx_RoomView_fileDropTarget">
|
||||||
<div className="mx_RoomView_fileDropTargetLabel">
|
<div className="mx_RoomView_fileDropTargetLabel">
|
||||||
<img src="img/upload-big.png" width="46" height="61" alt="Drop File Here"/><br/>
|
<img src="img/upload-big.png" width="43" height="57" alt="Drop File Here"/><br/>
|
||||||
Drop File Here
|
Drop File Here
|
||||||
</div>
|
</div>
|
||||||
</div>;
|
</div>;
|
||||||
|
@ -219,12 +249,12 @@ module.exports = React.createClass({
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="mx_RoomView">
|
<div className="mx_RoomView">
|
||||||
<RoomHeader ref="header" room={this.state.room} editing={this.state.editingRoomSettings}
|
<RoomHeader ref="header" room={this.state.room} editing={this.state.editingRoomSettings} onSearchClick={this.onSearchClick}
|
||||||
onSettingsClick={this.onSettingsClick} onSaveClick={this.onSaveClick} onCancelClick={this.onCancelClick} />
|
onSettingsClick={this.onSettingsClick} onSaveClick={this.onSaveClick} onCancelClick={this.onCancelClick} />
|
||||||
<div className="mx_RoomView_auxPanel">
|
<div className="mx_RoomView_auxPanel">
|
||||||
<CallView room={this.state.room}/>
|
<CallView room={this.state.room}/>
|
||||||
{ conferenceCallNotification }
|
{ conferenceCallNotification }
|
||||||
{ roomEdit }
|
{ aux }
|
||||||
</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">
|
||||||
|
@ -238,6 +268,7 @@ module.exports = React.createClass({
|
||||||
</div>
|
</div>
|
||||||
<div className="mx_RoomView_statusArea">
|
<div className="mx_RoomView_statusArea">
|
||||||
<div className="mx_RoomView_statusAreaBox">
|
<div className="mx_RoomView_statusAreaBox">
|
||||||
|
<div className="mx_RoomView_statusAreaBox_line"></div>
|
||||||
{statusBar}
|
{statusBar}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -21,6 +21,26 @@ var React = require('react');
|
||||||
module.exports = React.createClass({
|
module.exports = React.createClass({
|
||||||
displayName: 'ViewSource',
|
displayName: 'ViewSource',
|
||||||
|
|
||||||
|
propTypes: {
|
||||||
|
onFinished: React.PropTypes.func.isRequired
|
||||||
|
},
|
||||||
|
|
||||||
|
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() {
|
render: function() {
|
||||||
return (
|
return (
|
||||||
<div className="mx_ViewSource">
|
<div className="mx_ViewSource">
|
||||||
|
|
|
@ -0,0 +1,62 @@
|
||||||
|
/*
|
||||||
|
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');
|
||||||
|
|
||||||
|
module.exports = React.createClass({
|
||||||
|
displayName: 'CompatibilityPage',
|
||||||
|
propTypes: {
|
||||||
|
onAccept: React.PropTypes.func
|
||||||
|
},
|
||||||
|
|
||||||
|
getDefaultProps: function() {
|
||||||
|
return {
|
||||||
|
onAccept: function() {} // NOP
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
onAccept: function() {
|
||||||
|
this.props.onAccept();
|
||||||
|
},
|
||||||
|
|
||||||
|
render: function() {
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="mx_CompatibilityPage">
|
||||||
|
<div className="mx_CompatibilityPage_box">
|
||||||
|
<p>Sorry, your browser is <b>not</b> able to run Vector.</p>
|
||||||
|
<p>
|
||||||
|
Buttons and images may appear out of place, communication may
|
||||||
|
not be possible and all manner of chaos may be unleashed.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Please install <a href={"https://www.google.com/chrome"}>Chrome</a> for
|
||||||
|
the best experience.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Though if you like taking risks with your life, you can still try it
|
||||||
|
out by clicking that you understand the risks involved.
|
||||||
|
</p>
|
||||||
|
<button onClick={this.onAccept}>
|
||||||
|
I understand the risks and wish to continue
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
|
@ -16,6 +16,7 @@ limitations under the License.
|
||||||
|
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
|
var RunModernizrTests = require("./modernizr"); // this side-effects a global
|
||||||
var React = require("react");
|
var React = require("react");
|
||||||
var sdk = require("matrix-react-sdk");
|
var sdk = require("matrix-react-sdk");
|
||||||
sdk.loadSkin(require('../skins/vector/skindex'));
|
sdk.loadSkin(require('../skins/vector/skindex'));
|
||||||
|
@ -25,6 +26,34 @@ var qs = require("querystring");
|
||||||
|
|
||||||
var lastLocationHashSet = null;
|
var lastLocationHashSet = null;
|
||||||
|
|
||||||
|
function checkBrowserFeatures(featureList) {
|
||||||
|
if (!window.Modernizr) {
|
||||||
|
console.error("Cannot check features - Modernizr global is missing.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
var featureComplete = true;
|
||||||
|
for (var i = 0; i < featureList.length; i++) {
|
||||||
|
if (window.Modernizr[featureList[i]] === undefined) {
|
||||||
|
console.error(
|
||||||
|
"Looked for feature '%s' but Modernizr has no results for this. " +
|
||||||
|
"Has it been configured correctly?", featureList[i]
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (window.Modernizr[featureList[i]] === false) {
|
||||||
|
console.error("Browser missing feature: '%s'", featureList[i]);
|
||||||
|
// toggle flag rather than return early so we log all missing features
|
||||||
|
// rather than just the first.
|
||||||
|
featureComplete = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return featureComplete;
|
||||||
|
}
|
||||||
|
|
||||||
|
var validBrowser = checkBrowserFeatures([
|
||||||
|
"displaytable", "flexbox", "es5object", "es5function", "localstorage",
|
||||||
|
"objectfit"
|
||||||
|
]);
|
||||||
|
|
||||||
// We want to support some name / value pairs in the fragment
|
// We want to support some name / value pairs in the fragment
|
||||||
// so we're re-using query string like format
|
// so we're re-using query string like format
|
||||||
|
@ -84,14 +113,11 @@ var makeRegistrationUrl = function() {
|
||||||
'#/register';
|
'#/register';
|
||||||
}
|
}
|
||||||
|
|
||||||
var MatrixChat = sdk.getComponent('pages.MatrixChat');
|
|
||||||
window.matrixChat = React.render(
|
|
||||||
<MatrixChat onNewScreen={onNewScreen} registrationUrl={makeRegistrationUrl()} />,
|
|
||||||
document.getElementById('matrixchat')
|
|
||||||
);
|
|
||||||
|
|
||||||
window.addEventListener('hashchange', onHashChange);
|
window.addEventListener('hashchange', onHashChange);
|
||||||
window.onload = function() {
|
window.onload = function() {
|
||||||
|
if (!validBrowser) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
routeUrl(window.location);
|
routeUrl(window.location);
|
||||||
loaded = true;
|
loaded = true;
|
||||||
if (lastLoadedScreen) {
|
if (lastLoadedScreen) {
|
||||||
|
@ -100,3 +126,28 @@ window.onload = function() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function loadApp() {
|
||||||
|
if (validBrowser) {
|
||||||
|
var MatrixChat = sdk.getComponent('pages.MatrixChat');
|
||||||
|
window.matrixChat = React.render(
|
||||||
|
<MatrixChat onNewScreen={onNewScreen} registrationUrl={makeRegistrationUrl()} />,
|
||||||
|
document.getElementById('matrixchat')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
console.error("Browser is missing required features.");
|
||||||
|
// take to a different landing page to AWOOOOOGA at the user
|
||||||
|
var CompatibilityPage = require("../skins/vector/views/pages/CompatibilityPage");
|
||||||
|
window.matrixChat = React.render(
|
||||||
|
<CompatibilityPage onAccept={function() {
|
||||||
|
validBrowser = true;
|
||||||
|
console.log("User accepts the compatibility risks.");
|
||||||
|
loadApp();
|
||||||
|
window.onload(); // still do the same code paths for compatible clients
|
||||||
|
}} />,
|
||||||
|
document.getElementById('matrixchat')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
loadApp();
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<title>Vector</title>
|
<title>Vector</title>
|
||||||
<link href='fonts/Lato.css' rel='stylesheet' type='text/css'>
|
<link href='fonts/MyriadPro.css' rel='stylesheet' type='text/css'>
|
||||||
<link rel="apple-touch-icon" sizes="57x57" href="/icons/apple-touch-icon-57x57.png">
|
<link rel="apple-touch-icon" sizes="57x57" href="/icons/apple-touch-icon-57x57.png">
|
||||||
<link rel="apple-touch-icon" sizes="60x60" href="/icons/apple-touch-icon-60x60.png">
|
<link rel="apple-touch-icon" sizes="60x60" href="/icons/apple-touch-icon-60x60.png">
|
||||||
<link rel="apple-touch-icon" sizes="72x72" href="/icons/apple-touch-icon-72x72.png">
|
<link rel="apple-touch-icon" sizes="72x72" href="/icons/apple-touch-icon-72x72.png">
|
||||||
|
|