Merge pull request #62 from matrix-org/kegan/archived-rooms

Show archived rooms and implement forgetting
This commit is contained in:
Kegsay 2015-12-18 17:36:36 +00:00
commit e57d0601b6
3 changed files with 119 additions and 17 deletions

View file

@ -875,7 +875,19 @@ module.exports = React.createClass({
action: 'leave_room',
room_id: this.props.roomId,
});
this.props.onFinished();
},
onForgetClick: function() {
MatrixClientPeg.get().forget(this.props.roomId).done(function() {
dis.dispatch({ action: 'view_next_room' });
}, function(err) {
var errCode = err.errcode || "unknown error code";
var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
Modal.createDialog(ErrorDialog, {
title: "Error",
description: `Failed to forget room (${errCode})`
});
});
},
onRejectButtonClicked: function(ev) {
@ -1267,16 +1279,23 @@ module.exports = React.createClass({
}
var messageComposer, searchInfo;
if (!this.state.searchResults) {
var canSpeak = (
// joined and not showing search results
myMember && (myMember.membership == 'join') && !this.state.searchResults
);
if (canSpeak) {
messageComposer =
<MessageComposer room={this.state.room} roomView={this} uploadFile={this.uploadFile} callState={this.state.callState} />
}
else {
// TODO: Why aren't we storing the term/scope/count in this format
// in this.state if this is what RoomHeader desires?
if (this.state.searchResults) {
searchInfo = {
searchTerm : this.state.searchTerm,
searchScope : this.state.searchScope,
searchCount : this.state.searchCount,
}
};
}
var call = CallHandler.getCallForRoom(this.props.roomId);
@ -1323,8 +1342,18 @@ module.exports = React.createClass({
return (
<div className={ "mx_RoomView" + (inCall ? " mx_RoomView_inCall" : "") }>
<RoomHeader ref="header" room={this.state.room} searchInfo={searchInfo} editing={this.state.editingRoomSettings} onSearchClick={this.onSearchClick}
onSettingsClick={this.onSettingsClick} onSaveClick={this.onSaveClick} onCancelClick={this.onCancelClick} onLeaveClick={this.onLeaveClick} />
<RoomHeader ref="header" room={this.state.room} searchInfo={searchInfo}
editing={this.state.editingRoomSettings}
onSearchClick={this.onSearchClick}
onSettingsClick={this.onSettingsClick}
onSaveClick={this.onSaveClick}
onCancelClick={this.onCancelClick}
onForgetClick={
(myMember && myMember.membership === "leave") ? this.onForgetClick : null
}
onLeaveClick={
(myMember && myMember.membership === "join") ? this.onLeaveClick : null
} />
{ fileDropTarget }
<div className="mx_RoomView_auxPanel">
<CallView ref="callView" room={this.state.room} ConferenceHandler={this.props.ConferenceHandler}/>

View file

@ -131,7 +131,17 @@ module.exports = React.createClass({
if (this.props.onLeaveClick) {
leave_button =
<div className="mx_RoomHeader_button mx_RoomHeader_leaveButton">
<img src="img/leave.svg" title="Leave room" alt="Leave room" width="26" height="20" onClick={this.props.onLeaveClick}/>
<img src="img/leave.svg" title="Leave room" alt="Leave room"
width="26" height="20" onClick={this.props.onLeaveClick}/>
</div>;
}
var forget_button;
if (this.props.onForgetClick) {
forget_button =
<div className="mx_RoomHeader_button mx_RoomHeader_leaveButton">
<img src="img/leave.svg" title="Forget room" alt="Forget room"
width="26" height="20" onClick={this.props.onForgetClick}/>
</div>;
}
@ -149,6 +159,7 @@ module.exports = React.createClass({
{cancel_button}
{save_button}
<div className="mx_RoomHeader_rightRow">
{ forget_button }
{ leave_button }
<div className="mx_RoomHeader_button">
<img src="img/search.svg" title="Search" alt="Search" width="21" height="19" onClick={this.props.onSearchClick}/>

View file

@ -39,6 +39,7 @@ module.exports = React.createClass({
getInitialState: function() {
return {
activityMap: null,
isLoadingLeftRooms: false,
lists: {},
incomingCall: null,
}
@ -47,6 +48,7 @@ module.exports = React.createClass({
componentWillMount: function() {
var cli = MatrixClientPeg.get();
cli.on("Room", this.onRoom);
cli.on("deleteRoom", this.onDeleteRoom);
cli.on("Room.timeline", this.onRoomTimeline);
cli.on("Room.name", this.onRoomName);
cli.on("Room.tags", this.onRoomTags);
@ -104,7 +106,26 @@ module.exports = React.createClass({
},
onRoom: function(room) {
this.refreshRoomList();
this._delayedRefreshRoomList();
},
onDeleteRoom: function(roomId) {
this._delayedRefreshRoomList();
},
onArchivedHeaderClick: function(isHidden) {
if (!isHidden) {
var self = this;
this.setState({ isLoadingLeftRooms: true });
// we don't care about the response since it comes down via "Room"
// events.
MatrixClientPeg.get().syncLeftRooms().catch(function(err) {
console.error("Failed to sync left rooms: %s", err);
console.error(err);
}).finally(function() {
self.setState({ isLoadingLeftRooms: false });
});
}
},
onRoomTimeline: function(ev, room, toStartOfTimeline) {
@ -143,22 +164,57 @@ module.exports = React.createClass({
},
onRoomName: function(room) {
this.refreshRoomList();
this._delayedRefreshRoomList();
},
onRoomTags: function(event, room) {
this.refreshRoomList();
this._delayedRefreshRoomList();
},
onRoomStateEvents: function(ev, state) {
setTimeout(this.refreshRoomList, 0);
this._delayedRefreshRoomList();
},
onRoomMemberName: function(ev, member) {
setTimeout(this.refreshRoomList, 0);
this._delayedRefreshRoomList();
},
_delayedRefreshRoomList: function() {
// There can be 1000s of JS SDK events when rooms are initially synced;
// we don't want to do lots of work rendering until things have settled.
// Therefore, keep a 1s refresh buffer which will refresh the room list
// at MOST once every 1s to prevent thrashing.
var MAX_REFRESH_INTERVAL_MS = 1000;
var self = this;
if (!self._lastRefreshRoomListTs) {
self.refreshRoomList(); // first refresh evar
}
else {
var timeWaitedMs = Date.now() - self._lastRefreshRoomListTs;
if (timeWaitedMs > MAX_REFRESH_INTERVAL_MS) {
clearTimeout(self._refreshRoomListTimerId);
self._refreshRoomListTimerId = null;
self.refreshRoomList(); // refreshed more than MAX_REFRESH_INTERVAL_MS ago
}
else {
// refreshed less than MAX_REFRESH_INTERVAL_MS ago, wait the difference
// if we aren't already waiting. If we are waiting then NOP, it will
// fire soon, promise!
if (!self._refreshRoomListTimerId) {
self._refreshRoomListTimerId = setTimeout(function() {
self.refreshRoomList();
}, 10 + MAX_REFRESH_INTERVAL_MS - timeWaitedMs); // 10 is a buffer amount
}
}
}
},
refreshRoomList: function() {
// console.log("DEBUG: Refresh room list delta=%s ms",
// (!this._lastRefreshRoomListTs ? "-" : (Date.now() - this._lastRefreshRoomListTs))
// );
// TODO: rather than bluntly regenerating and re-sorting everything
// every time we see any kind of room change from the JS SDK
// we could do incremental updates on our copy of the state
@ -166,6 +222,7 @@ module.exports = React.createClass({
// us re-rendering all the sublists every time anything changes anywhere
// in the state of the client.
this.setState(this.getRoomLists());
this._lastRefreshRoomListTs = Date.now();
},
getRoomLists: function() {
@ -184,9 +241,12 @@ module.exports = React.createClass({
if (me && me.membership == "invite") {
s.lists["im.vector.fake.invite"].push(room);
}
else if (me && me.membership === "leave") {
s.lists["im.vector.fake.archived"].push(room);
}
else {
var shouldShowRoom = (
me && (me.membership == "join")
me && (me.membership == "join" || me.membership === "ban")
);
// hiding conf rooms only ever toggles shouldShowRoom to false
@ -355,7 +415,6 @@ module.exports = React.createClass({
verb="demote"
editable={ true }
order="recent"
bottommost={ self.state.lists['im.vector.fake.archived'].length === 0 }
activityMap={ self.state.activityMap }
selectedRoom={ self.props.selectedRoom }
incomingCall={ self.state.incomingCall }
@ -365,11 +424,14 @@ module.exports = React.createClass({
label="Historical"
editable={ false }
order="recent"
bottommost={ true }
activityMap={ self.state.activityMap }
selectedRoom={ self.props.selectedRoom }
incomingCall={ self.state.incomingCall }
collapsed={ self.props.collapsed } />
collapsed={ self.props.collapsed }
alwaysShowHeader={ true }
startAsHidden={ true }
showSpinner={ self.state.isLoadingLeftRooms }
onHeaderClick= { self.onArchivedHeaderClick }
incomingCall={ self.state.incomingCall } />
</div>
</GeminiScrollbar>
);