From a11516a984c222f7e307b1695cbf82cb92a248d3 Mon Sep 17 00:00:00 2001 From: David Baker <dave@matrix.org> Date: Fri, 16 Sep 2016 17:33:28 +0100 Subject: [PATCH] Make publicrooms use the new paginating API Also do filtering on the server WIP: This breaks the network dropdown --- src/components/structures/RoomDirectory.js | 110 +++++++++++------- .../vector-web/structures/RoomDirectory.css | 5 + 2 files changed, 72 insertions(+), 43 deletions(-) diff --git a/src/components/structures/RoomDirectory.js b/src/components/structures/RoomDirectory.js index 3d82499687..7a54f6b54a 100644 --- a/src/components/structures/RoomDirectory.js +++ b/src/components/structures/RoomDirectory.js @@ -29,6 +29,7 @@ var linkify = require('linkifyjs'); var linkifyString = require('linkifyjs/string'); var linkifyMatrix = require('matrix-react-sdk/lib/linkify-matrix'); var sanitizeHtml = require('sanitize-html'); +var q = require('q'); linkifyMatrix(linkify); @@ -50,7 +51,6 @@ module.exports = React.createClass({ getInitialState: function() { return { publicRooms: [], - roomAlias: '', loading: true, filterByNetwork: null, } @@ -64,6 +64,8 @@ module.exports = React.createClass({ this.networkPatterns[network] = new RegExp(this.props.config.networkPatterns[network]); } } + this.nextBatch = null; + this.filterString = null; // dis.dispatch({ // action: 'ui_opacity', @@ -73,7 +75,7 @@ module.exports = React.createClass({ }, componentDidMount: function() { - this.getPublicRooms(); + this.refreshRoomList(); }, componentWillUnmount: function() { @@ -84,24 +86,34 @@ module.exports = React.createClass({ // }); }, - getPublicRooms: function() { - var self = this; - MatrixClientPeg.get().publicRooms(function (err, data) { - if (err) { - self.setState({ loading: false }); - console.error("Failed to get publicRooms: %s", JSON.stringify(err)); - var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); - Modal.createDialog(ErrorDialog, { - title: "Failed to get public room list", - description: err.message - }); - } - else { - self.setState({ - publicRooms: data.chunk, - loading: false, - }); - } + refreshRoomList: function() { + this.nextBatch = null; + this.setState({ + publicRooms: [], + }); + this.getMoreRooms().done(); + }, + + getMoreRooms: function() { + const opts = {limit: 20}; + if (this.nextBatch) opts.since = this.nextBatch; + if (this.filterString) opts.filter = { generic_search_term: this.filterString } ; + return MatrixClientPeg.get().publicRooms(opts).then((data) => { + this.nextBatch = data.next_batch; + this.setState((s) => { + s.publicRooms.push(...data.chunk); + s.loading = false; + return s; + }); + return Boolean(data.next_batch); + }, (err) => { + this.setState({ loading: false }); + console.error("Failed to get publicRooms: %s", JSON.stringify(err)); + var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); + Modal.createDialog(ErrorDialog, { + title: "Failed to get public room list", + description: err.message + }); }); }, @@ -142,10 +154,10 @@ module.exports = React.createClass({ return MatrixClientPeg.get().deleteAlias(alias); }).done(() => { modal.close(); - this.getPublicRooms(); + this.refreshRoomList(); }, function(err) { modal.close(); - this.getPublicRooms(); + this.refreshRoomList(); Modal.createDialog(ErrorDialog, { title: "Failed to "+step, description: err.toString() @@ -170,6 +182,23 @@ module.exports = React.createClass({ }); }, + onFillRequest: function(backwards) { + if (backwards || !this.nextBatch) return q(false); + + return this.getMoreRooms(); + }, + + onFilterChange: function(ev) { + const alias = ev.target.value; + + if (ev.key == "Enter") { + this.showRoomAlias(alias); + } else { + this.filterString = alias || null; + this.refreshRoomList(); + } + }, + showRoomAlias: function(alias) { this.showRoom(null, alias); }, @@ -214,23 +243,17 @@ module.exports = React.createClass({ dis.dispatch(payload); }, - getRows: function(filter) { + getRows: function() { var BaseAvatar = sdk.getComponent('avatars.BaseAvatar'); if (!this.state.publicRooms) return []; var rooms = this.state.publicRooms.filter((a) => { - // FIXME: if incrementally typing, keep narrowing down the search set - // incrementally rather than starting over each time. if (this.state.filterByNetwork) { if (!this._isRoomInNetwork(a, this.state.filterByNetwork)) return false; } - return (((a.name && a.name.toLowerCase().search(filter.toLowerCase()) >= 0) || - (a.aliases && a.aliases[0].toLowerCase().search(filter.toLowerCase()) >= 0)) && - a.num_joined_members > 0); - }).sort(function(a,b) { - return a.num_joined_members - b.num_joined_members; + return true; }); var rows = []; var self = this; @@ -259,7 +282,7 @@ module.exports = React.createClass({ var topic = rooms[i].topic || ''; topic = linkifyString(sanitizeHtml(topic)); - rows.unshift( + rows.push( <tr key={ rooms[i].room_id } onClick={self.onRoomClicked.bind(self, rooms[i])} // cancel onMouseDown otherwise shift-clicking highlights text @@ -289,14 +312,6 @@ module.exports = React.createClass({ return rows; }, - onKeyUp: function(ev) { - this.forceUpdate(); - this.setState({ roomAlias : this.refs.roomAlias.value }) - if (ev.key == "Enter") { - this.showRoomAlias(this.refs.roomAlias.value); - } - }, - /** * Terrible temporary function that guess what network a public room * entry is in, until synapse is able to tell us @@ -323,21 +338,30 @@ module.exports = React.createClass({ const SimpleRoomHeader = sdk.getComponent('rooms.SimpleRoomHeader'); const NetworkDropdown = sdk.getComponent('directory.NetworkDropdown'); + const ScrollPanel = sdk.getComponent("structures.ScrollPanel"); return ( <div className="mx_RoomDirectory"> <SimpleRoomHeader title="Directory" /> <div className="mx_RoomDirectory_list"> <div className="mx_RoomDirectory_listheader"> - <input ref="roomAlias" placeholder="Join a room (e.g. #foo:domain.com)" className="mx_RoomDirectory_input" size="64" onKeyUp={ this.onKeyUp }/> + <input type="text" placeholder="Find a room by keyword or room ID (#foo:matrix.org)" + className="mx_RoomDirectory_input" size="64" onChange={this.onFilterChange} + /> <NetworkDropdown config={this.props.config} onNetworkChange={this.onNetworkChange} /> </div> - <GeminiScrollbar className="mx_RoomDirectory_tableWrapper"> + <ScrollPanel + className="mx_RoomDirectory_tableWrapper" + onFillRequest={ this.onFillRequest } + stickyBottom={false} + startAtBottom={false} + onResize={function(){}} + > <table ref="directory_table" className="mx_RoomDirectory_table"> <tbody> - { this.getRows(this.state.roomAlias) } + { this.getRows() } </tbody> </table> - </GeminiScrollbar> + </ScrollPanel> </div> </div> ); diff --git a/src/skins/vector/css/vector-web/structures/RoomDirectory.css b/src/skins/vector/css/vector-web/structures/RoomDirectory.css index b4a2394593..17954e1c98 100644 --- a/src/skins/vector/css/vector-web/structures/RoomDirectory.css +++ b/src/skins/vector/css/vector-web/structures/RoomDirectory.css @@ -46,6 +46,11 @@ limitations under the License. -webkit-flex-direction: column; } +.mx_RoomDirectory_list .mx_RoomView_messageListWrapper { + justify-content: flex-start; + -webkit-justify-content: flex-start; +} + .mx_RoomDirectory_listheader { display: table; width: 100%;