diff --git a/src/components/structures/FilePanel.js b/src/components/structures/FilePanel.js index 56bc9dee64..74b434cdbf 100644 --- a/src/components/structures/FilePanel.js +++ b/src/components/structures/FilePanel.js @@ -71,6 +71,20 @@ const FilePanel = createReactClass({ return timelineSet; }, + onPaginationRequest(timelineWindow, direction, limit) { + const client = MatrixClientPeg.get(); + const eventIndex = EventIndexPeg.get(); + const roomId = this.props.roomId; + + const room = client.getRoom(roomId); + + if (client.isRoomEncrypted(roomId) && eventIndex !== null) { + return eventIndex.paginateTimelineWindow(room, timelineWindow, direction, limit); + } else { + return timelineWindow.paginate(direction, limit); + } + }, + async updateTimelineSet(roomId: string) { const client = MatrixClientPeg.get(); const room = client.getRoom(roomId); @@ -85,7 +99,8 @@ const FilePanel = createReactClass({ timelineSet = await this.fetchFileEventsServer(room) if (client.isRoomEncrypted(roomId) && eventIndex !== null) { - await eventIndex.populateFileTimeline(room, timelineSet); + const timeline = timelineSet.getLiveTimeline(); + await eventIndex.populateFileTimeline(timelineSet, timeline, room, 1); } this.setState({ timelineSet: timelineSet }); @@ -128,6 +143,7 @@ const FilePanel = createReactClass({ manageReadMarkers={false} timelineSet={this.state.timelineSet} showUrlPreview = {false} + onPaginationRequest={this.onPaginationRequest} tileShape="file_grid" resizeNotifier={this.props.resizeNotifier} empty={_t('There are no visible files in this room')} diff --git a/src/components/structures/TimelinePanel.js b/src/components/structures/TimelinePanel.js index 30b02bfcca..41b1b6f675 100644 --- a/src/components/structures/TimelinePanel.js +++ b/src/components/structures/TimelinePanel.js @@ -94,6 +94,10 @@ const TimelinePanel = createReactClass({ // callback which is called when the read-up-to mark is updated. onReadMarkerUpdated: PropTypes.func, + // callback which is called when we wish to paginate the timeline + // window. + onPaginationRequest: PropTypes.func, + // maximum number of events to show in a timeline timelineCap: PropTypes.number, @@ -338,6 +342,14 @@ const TimelinePanel = createReactClass({ } }, + onPaginationRequest(timelineWindow, direction, size) { + if (this.props.onPaginationRequest) { + return this.props.onPaginationRequest(timelineWindow, direction, size); + } else { + return timelineWindow.paginate(direction, size); + } + }, + // set off a pagination request. onMessageListFillRequest: function(backwards) { if (!this._shouldPaginate()) return Promise.resolve(false); @@ -360,7 +372,7 @@ const TimelinePanel = createReactClass({ debuglog("TimelinePanel: Initiating paginate; backwards:"+backwards); this.setState({[paginatingKey]: true}); - return this._timelineWindow.paginate(dir, PAGINATE_SIZE).then((r) => { + return this.onPaginationRequest(this._timelineWindow, dir, PAGINATE_SIZE).then((r) => { if (this.unmounted) { return; } debuglog("TimelinePanel: paginate complete backwards:"+backwards+"; success:"+r); diff --git a/src/indexing/EventIndex.js b/src/indexing/EventIndex.js index 501e21b29d..7263d8b2c4 100644 --- a/src/indexing/EventIndex.js +++ b/src/indexing/EventIndex.js @@ -418,17 +418,29 @@ export default class EventIndex { return indexManager.searchEventIndex(searchArgs); } - async populateFileTimeline(room, timelineSet) { + async loadFileEvents(room, limit = 10, fromEvent = null, direction = EventTimeline.BACKWARDS) { const client = MatrixClientPeg.get(); const indexManager = PlatformPeg.get().getEventIndexingManager(); + let loadArgs = { + roomId: room.roomId, + limit: limit + } + + if (fromEvent) { + loadArgs.fromEvent = fromEvent; + loadArgs.direction = direction; + } + + let events + // Get our events from the event index. - const events = await indexManager.loadFileEvents( - { - roomId: room.roomId, - limit: 10 - } - ); + try { + events = await indexManager.loadFileEvents(loadArgs); + } catch (e) { + console.log("EventIndex: Error getting file events", e); + return [] + } let eventMapper = client.getEventMapper(); @@ -468,15 +480,82 @@ export default class EventIndex { return matrixEvent; }); + return matrixEvents; + } + + async populateFileTimeline(timelineSet, timeline, room, limit = 10, + fromEvent = null, direction = EventTimeline.BACKWARDS) { + let matrixEvents = await this.loadFileEvents(room, limit, fromEvent, direction); + // Add the events to the live timeline of the file panel. matrixEvents.forEach(e => { if (!timelineSet.eventIdToTimeline(e.getId())) { - const liveTimeline = timelineSet.getLiveTimeline(); - timelineSet.addEventToTimeline(e, liveTimeline, true) + timelineSet.addEventToTimeline(e, timeline, + direction == EventTimeline.BACKWARDS) } }); // Set the pagination token to the oldest event that we retrieved. - timelineSet.getLiveTimeline().setPaginationToken("", EventTimeline.BACKWARDS); + if (matrixEvents.length > 0) { + timeline.setPaginationToken(matrixEvents[matrixEvents.length - 1].getId(), + EventTimeline.BACKWARDS); + return true; + } else { + timeline.setPaginationToken("", EventTimeline.BACKWARDS); + return false; + } + } + + paginateTimelineWindow(room, timelineWindow, direction, limit) { + let tl; + + // TODO this is from the js-sdk, this should probably be exposed to + // us through the js-sdk. + const moveWindowCap = (titmelineWindow, timeline, direction, limit) => { + var count = (direction == EventTimeline.BACKWARDS) ? + timeline.retreat(limit) : timeline.advance(limit); + + if (count) { + timelineWindow._eventCount += count; + var excess = timelineWindow._eventCount - timelineWindow._windowLimit; + + if (excess > 0) { + timelineWindow.unpaginate(3, direction != EventTimeline.BACKWARDS); + } + return true; + } + + return false; + }; + + // TODO these private fields should be somehow exposed in the js-sdk. + if (direction == EventTimeline.BACKWARDS) tl = timelineWindow._start; + else if (direction == EventTimeline.FORWARDS) tl = timelineWindow._end; + + if (!tl) return Promise.resolve(false); + if (tl.pendingPaginate) return tl.pendingPaginate; + + if (moveWindowCap(timelineWindow, tl, direction, limit)) { + return Promise.resolve(true); + } + + const paginationMethod = async (timelineWindow, timeline, room, direction, limit) => { + const timelineSet = timelineWindow._timelineSet; + const token = timeline.timeline.getPaginationToken(direction); + + const ret = await this.populateFileTimeline(timelineSet, timeline.timeline, + room, limit, token, direction); + + moveWindowCap(timelineWindow, timeline, direction, limit) + timeline.pendingPaginate = null; + + return ret; + }; + + const paginationPromise = paginationMethod(timelineWindow, tl, room, + direction, limit); + tl.pendingPaginate = paginationPromise; + + return paginationPromise; } }