Merge branch 'develop' into kegan/controller-merging4

pull/448/head
Kegan Dougal 2015-11-30 10:56:14 +00:00
commit 83b3702769
15 changed files with 107 additions and 63 deletions

View File

@ -288,6 +288,7 @@ module.exports = {
fillSpace: function() { fillSpace: function() {
if (!this.refs.messagePanel) return; if (!this.refs.messagePanel) return;
if (this.state.searchResults) return; // TODO: paginate search results
var messageWrapperScroll = this._getScrollNode(); var messageWrapperScroll = this._getScrollNode();
if (messageWrapperScroll.scrollTop < messageWrapperScroll.clientHeight && this.state.room.oldState.paginationToken) { if (messageWrapperScroll.scrollTop < messageWrapperScroll.clientHeight && this.state.room.oldState.paginationToken) {
this.setState({paginating: true}); this.setState({paginating: true});
@ -426,7 +427,7 @@ module.exports = {
onSearch: function(term, scope) { onSearch: function(term, scope) {
var filter; var filter;
if (scope === "Room") { // FIXME: should be enum if (scope === "Room") {
filter = { filter = {
// XXX: it's unintuitive that the filter for searching doesn't have the same shape as the v2 filter API :( // XXX: it's unintuitive that the filter for searching doesn't have the same shape as the v2 filter API :(
rooms: [ rooms: [
@ -443,6 +444,14 @@ module.exports = {
search_term: term, search_term: term,
filter: filter, filter: filter,
order_by: "recent", order_by: "recent",
include_state: true,
groupings: {
group_by: [
{
key: "room_id"
}
]
},
event_context: { event_context: {
before_limit: 1, before_limit: 1,
after_limit: 1, after_limit: 1,
@ -451,9 +460,28 @@ module.exports = {
} }
} }
}).then(function(data) { }).then(function(data) {
// for debugging:
// data.search_categories.room_events.highlights = ["hello", "everybody"];
var highlights;
if (data.search_categories.room_events.highlights &&
data.search_categories.room_events.highlights.length > 0)
{
// postgres on synapse returns us precise details of the
// strings which actually got matched for highlighting.
// for overlapping highlights, favour longer (more specific) terms first
highlights = data.search_categories.room_events.highlights
.sort(function(a, b) { b.length - a.length });
}
else {
// sqlite doesn't, so just try to highlight the literal search term
highlights = [ term ];
}
self.setState({ self.setState({
searchTerm: term, highlights: highlights,
searchResults: data, searchResults: data,
searchScope: scope,
}); });
}, function(error) { }, function(error) {
var ErrorDialog = sdk.getComponent("organisms.ErrorDialog"); var ErrorDialog = sdk.getComponent("organisms.ErrorDialog");
@ -466,39 +494,54 @@ module.exports = {
getEventTiles: function() { getEventTiles: function() {
var DateSeparator = sdk.getComponent('molecules.DateSeparator'); var DateSeparator = sdk.getComponent('molecules.DateSeparator');
var cli = MatrixClientPeg.get();
var ret = []; var ret = [];
var count = 0; var count = 0;
var EventTile = sdk.getComponent('messages.Event'); var EventTile = sdk.getComponent('messages.Event');
var self = this;
if (this.state.searchResults) { if (this.state.searchResults &&
// XXX: this dance is foul, due to the results API not returning sorted results this.state.searchResults.search_categories.room_events.results &&
this.state.searchResults.search_categories.room_events.groups)
{
// XXX: this dance is foul, due to the results API not directly returning sorted results
var results = this.state.searchResults.search_categories.room_events.results; var results = this.state.searchResults.search_categories.room_events.results;
var eventIds = Object.keys(results); var roomIdGroups = this.state.searchResults.search_categories.room_events.groups.room_id;
// XXX: todo: merge overlapping results somehow?
// XXX: why doesn't searching on name work? Object.keys(roomIdGroups)
var resultList = eventIds.map(function(key) { return results[key]; }); // .sort(function(a, b) { b.rank - a.rank }); .sort(function(a, b) { roomIdGroups[a].order - roomIdGroups[b].order }) // WHY NOT RETURN AN ORDERED ARRAY?!?!?!
for (var i = 0; i < resultList.length; i++) { .forEach(function(roomId)
var ts1 = resultList[i].result.origin_server_ts; {
ret.push(<li key={ts1 + "-search"}><DateSeparator ts={ts1}/></li>); // Rank: {resultList[i].rank} // XXX: todo: merge overlapping results somehow?
var mxEv = new Matrix.MatrixEvent(resultList[i].result); // XXX: why doesn't searching on name work?
if (resultList[i].context.events_before[0]) { if (self.state.searchScope === 'All') {
var mxEv2 = new Matrix.MatrixEvent(resultList[i].context.events_before[0]); ret.push(<li key={ roomId }><h1>Room: { cli.getRoom(roomId).name }</h1></li>);
if (EventTile.haveTileForEvent(mxEv2)) { }
ret.push(<li key={mxEv.getId() + "-1"}><EventTile mxEvent={mxEv2} contextual={true} /></li>);
var resultList = roomIdGroups[roomId].results.map(function(eventId) { return results[eventId]; });
for (var i = resultList.length - 1; i >= 0; 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} highlights={self.state.highlights}/></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>);
}
} }
} }
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; return ret;
} }
@ -530,7 +573,7 @@ module.exports = {
var ts0 = this.state.room.timeline[i - 1].getTs(); var ts0 = this.state.room.timeline[i - 1].getTs();
var ts1 = this.state.room.timeline[i].getTs(); var ts1 = this.state.room.timeline[i].getTs();
if (new Date(ts0).toDateString() !== new Date(ts1).toDateString()) { if (new Date(ts0).toDateString() !== new Date(ts1).toDateString()) {
dateSeparator = <DateSeparator key={ts1} ts={ts1}/>; dateSeparator = <li key={ts1}><DateSeparator key={ts1} ts={ts1}/></li>;
continuation = false; continuation = false;
} }
} }

View File

@ -15,8 +15,7 @@ limitations under the License.
*/ */
.mx_MemberAvatar { .mx_MemberAvatar {
/* commenting this out as it breaks on FF seemingly */ position: relative;
/* position: relative; */
} }
.mx_MemberAvatar_initial { .mx_MemberAvatar_initial {

View File

@ -22,7 +22,12 @@ html {
} }
body { body {
font-family: 'Myriad Pro', Helvetica, Arial, Sans-Serif; /* Myriad Pro lacks combining diacritics, so these will fall through
to the next font. Helevetica's diacritics however do not combine
nicely with Myriad Pro (on OSX, at least) and result in a huge
horizontal mess. Arial empirically gets it right, hence prioritising
Arial here. */
font-family: 'Myriad Pro', Arial, Helvetica, Sans-Serif;
font-size: 16px; font-size: 16px;
color: #454545; color: #454545;
border: 0px; border: 0px;

View File

@ -16,4 +16,5 @@ limitations under the License.
.mx_EventAsTextTile { .mx_EventAsTextTile {
opacity: 0.5; opacity: 0.5;
overflow-y: hidden;
} }

View File

@ -27,6 +27,7 @@ limitations under the License.
margin-left: -73px; margin-left: -73px;
margin-top: -4px; margin-top: -4px;
float: left; float: left;
position: relative;
} }
.mx_EventTile_avatar img { .mx_EventTile_avatar img {
@ -44,6 +45,7 @@ limitations under the License.
font-size: 14px; font-size: 14px;
margin-bottom: 4px; margin-bottom: 4px;
display: block; display: block;
overflow-y: hidden;
} }
.mx_EventTile .mx_MessageTimestamp { .mx_EventTile .mx_MessageTimestamp {
@ -63,6 +65,7 @@ limitations under the License.
.mx_MessageTile_content { .mx_MessageTile_content {
display: block; display: block;
margin-right: 100px; margin-right: 100px;
overflow-y: hidden;
} }
/* Various markdown overrides */ /* Various markdown overrides */
@ -87,6 +90,12 @@ limitations under the License.
color: #76cfa6; color: #76cfa6;
} }
.mx_MessageTile_content .markdown-body .hljs {
display: inherit ! important;
}
/* end of overrides */
.mx_MessageTile_searchHighlight { .mx_MessageTile_searchHighlight {
background-color: #76cfa6; background-color: #76cfa6;
color: #fff; color: #fff;

View File

@ -37,8 +37,10 @@ limitations under the License.
} }
.mx_MemberInfo_profileField { .mx_MemberInfo_profileField {
opacity: 0.6; font-color: #999999;
font-size: 14px; font-size: 14px;
position: relative;
background-color: #fff;
} }
.mx_MemberInfo_buttons { .mx_MemberInfo_buttons {

View File

@ -98,10 +98,6 @@ limitations under the License.
opacity: 0.25; opacity: 0.25;
} }
.mx_MemberTile_zalgo {
font-family: Helvetica, Arial, Sans-Serif;
}
.mx_MemberTile:hover .mx_MessageTimestamp { .mx_MemberTile:hover .mx_MessageTimestamp {
display: block; display: block;
} }

View File

@ -59,7 +59,7 @@ limitations under the License.
box-shadow: none; box-shadow: none;
/* needed for FF */ /* needed for FF */
font-family: 'Myriad Pro', Helvetica, Arial, Sans-Serif; font-family: 'Myriad Pro', Arial, Helvetica, 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 */

View File

@ -111,6 +111,7 @@ limitations under the License.
.mx_RoomHeader_nametext { .mx_RoomHeader_nametext {
display: inline-block; display: inline-block;
overflow-y: hidden;
} }
.mx_RoomHeader_settingsButton { .mx_RoomHeader_settingsButton {

View File

@ -13,8 +13,3 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
.mx_SenderProfile_zalgo {
font-family: Helvetica, Arial, Sans-Serif;
display: table-row ! important;
}

View File

@ -45,7 +45,7 @@ limitations under the License.
} }
.mx_MemberList_invite { .mx_MemberList_invite {
font-family: 'Myriad Pro', Helvetica, Arial, Sans-Serif; font-family: 'Myriad Pro', Arial, Helvetica, Sans-Serif;
border-radius: 3px; border-radius: 3px;
border: 1px solid #f0f0f0; border: 1px solid #f0f0f0;
padding: 9px; padding: 9px;

View File

@ -125,7 +125,7 @@ limitations under the License.
clear: both; clear: both;
} }
.mx_RoomView_MessageList > h2 { .mx_RoomView_MessageList h2 {
clear: both; clear: both;
margin-top: 32px; margin-top: 32px;
margin-bottom: 8px; margin-bottom: 8px;

View File

@ -50,12 +50,10 @@ skin['messages.TextualEvent'] = require('matrix-react-sdk/lib/components/views/m
skin['messages.MRoomMemberEvent'] = require('matrix-react-sdk/lib/components/views/messages/MRoomMemberEvent'); skin['messages.MRoomMemberEvent'] = require('matrix-react-sdk/lib/components/views/messages/MRoomMemberEvent');
skin['messages.Event'] = require('matrix-react-sdk/lib/components/views/messages/Event'); skin['messages.Event'] = require('matrix-react-sdk/lib/components/views/messages/Event');
skin['messages.Message'] = require('matrix-react-sdk/lib/components/views/messages/Message'); skin['messages.Message'] = require('matrix-react-sdk/lib/components/views/messages/Message');
skin['messages.MEmoteMessage'] = require('matrix-react-sdk/lib/components/views/messages/MEmoteMessage');
skin['messages.MFileMessage'] = require('matrix-react-sdk/lib/components/views/messages/MFileMessage'); skin['messages.MFileMessage'] = require('matrix-react-sdk/lib/components/views/messages/MFileMessage');
skin['messages.MImageMessage'] = require('matrix-react-sdk/lib/components/views/messages/MImageMessage'); skin['messages.MImageMessage'] = require('matrix-react-sdk/lib/components/views/messages/MImageMessage');
skin['messages.MNoticeMessage'] = require('matrix-react-sdk/lib/components/views/messages/MNoticeMessage');
skin['messages.MTextMessage'] = require('matrix-react-sdk/lib/components/views/messages/MTextMessage');
skin['messages.MVideoMessage'] = require('matrix-react-sdk/lib/components/views/messages/MVideoMessage'); skin['messages.MVideoMessage'] = require('matrix-react-sdk/lib/components/views/messages/MVideoMessage');
skin['messages.TextualMessage'] = require('matrix-react-sdk/lib/components/views/messages/TextualMessage');
skin['messages.UnknownMessage'] = require('matrix-react-sdk/lib/components/views/messages/UnknownMessage'); skin['messages.UnknownMessage'] = require('matrix-react-sdk/lib/components/views/messages/UnknownMessage');
skin['rooms.MemberInfo'] = require('matrix-react-sdk/lib/components/views/rooms/MemberInfo'); skin['rooms.MemberInfo'] = require('matrix-react-sdk/lib/components/views/rooms/MemberInfo');

View File

@ -17,11 +17,6 @@ limitations under the License.
'use strict'; 'use strict';
var React = require('react'); var React = require('react');
var classNames = require("classnames");
// The Lato WOFF doesn't include sensible combining diacritics, so Chrome chokes on rendering them.
// Revert to Arial when this happens, which on OSX works at least.
var zalgo = /[\u0300-\u036f\u1ab0-\u1aff\u1dc0-\u1dff\u20d0-\u20ff\ufe20-\ufe2f]/;
module.exports = React.createClass({ module.exports = React.createClass({
displayName: 'SenderProfile', displayName: 'SenderProfile',
@ -30,18 +25,12 @@ module.exports = React.createClass({
var mxEvent = this.props.mxEvent; var mxEvent = this.props.mxEvent;
var name = mxEvent.sender ? mxEvent.sender.name : mxEvent.getSender(); var name = mxEvent.sender ? mxEvent.sender.name : mxEvent.getSender();
var classes = classNames({
mx_SenderProfile: true,
// taken from https://en.wikipedia.org/wiki/Combining_character
mx_SenderProfile_zalgo: zalgo.test(name),
});
var msgtype = mxEvent.getContent().msgtype; var msgtype = mxEvent.getContent().msgtype;
if (msgtype && msgtype == 'm.emote') { if (msgtype && msgtype == 'm.emote') {
name = ''; // emote message must include the name so don't duplicate it name = ''; // emote message must include the name so don't duplicate it
} }
return ( return (
<span className={classes}> <span className="mx_SenderProfile">
{name} { this.props.aux } {name} { this.props.aux }
</span> </span>
); );

View File

@ -206,7 +206,7 @@ module.exports = React.createClass({
if (this.state.syncState === "ERROR") { if (this.state.syncState === "ERROR") {
statusBar = ( statusBar = (
<div className="mx_RoomView_connectionLostBar"> <div className="mx_RoomView_connectionLostBar">
<img src="img/warning2.png" width="30" height="30" alt="/!\"/> <img src="img/warning2.png" width="30" height="30" alt="/!\ "/>
<div className="mx_RoomView_connectionLostBar_textArea"> <div className="mx_RoomView_connectionLostBar_textArea">
<div className="mx_RoomView_connectionLostBar_title"> <div className="mx_RoomView_connectionLostBar_title">
Connectivity to the server has been lost. Connectivity to the server has been lost.
@ -221,7 +221,7 @@ module.exports = React.createClass({
else if (this.state.hasUnsentMessages) { else if (this.state.hasUnsentMessages) {
statusBar = ( statusBar = (
<div className="mx_RoomView_connectionLostBar"> <div className="mx_RoomView_connectionLostBar">
<img src="img/warning2.png" width="30" height="30" alt="/!\"/> <img src="img/warning2.png" width="30" height="30" alt="/!\ "/>
<div className="mx_RoomView_connectionLostBar_textArea"> <div className="mx_RoomView_connectionLostBar_textArea">
<div className="mx_RoomView_connectionLostBar_title"> <div className="mx_RoomView_connectionLostBar_title">
Some of your messages have not been sent. Some of your messages have not been sent.
@ -291,6 +291,12 @@ module.exports = React.createClass({
</div>; </div>;
} }
var messageComposer;
if (!this.state.searchResults) {
messageComposer =
<MessageComposer room={this.state.room} roomView={this} uploadFile={this.uploadFile} />
}
return ( return (
<div className="mx_RoomView"> <div className="mx_RoomView">
<RoomHeader ref="header" room={this.state.room} editing={this.state.editingRoomSettings} onSearchClick={this.onSearchClick} <RoomHeader ref="header" room={this.state.room} editing={this.state.editingRoomSettings} onSearchClick={this.onSearchClick}
@ -313,10 +319,10 @@ module.exports = React.createClass({
<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> <div className="mx_RoomView_statusAreaBox_line"></div>
{statusBar} { this.state.searchResults ? null : statusBar }
</div> </div>
</div> </div>
<MessageComposer room={this.state.room} roomView={this} uploadFile={this.uploadFile} /> { messageComposer }
</div> </div>
); );
} }