Merge pull request #270 from matrix-org/rav/issue_1314_federated_rooms
RoomView: Handle joining federated rooms
This commit is contained in:
commit
85277a237a
2 changed files with 106 additions and 82 deletions
|
@ -628,10 +628,13 @@ module.exports = React.createClass({
|
||||||
if (!this.refs.roomView) {
|
if (!this.refs.roomView) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var roomview = this.refs.roomView;
|
var roomview = this.refs.roomView;
|
||||||
|
var roomId = this.refs.roomView.getRoomId();
|
||||||
|
if (!roomId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
var state = roomview.getScrollState();
|
var state = roomview.getScrollState();
|
||||||
this.scrollStateMap[roomview.props.roomId] = state;
|
this.scrollStateMap[roomId] = state;
|
||||||
},
|
},
|
||||||
|
|
||||||
onLoggedIn: function(credentials) {
|
onLoggedIn: function(credentials) {
|
||||||
|
@ -1066,8 +1069,7 @@ module.exports = React.createClass({
|
||||||
page_element = (
|
page_element = (
|
||||||
<RoomView
|
<RoomView
|
||||||
ref="roomView"
|
ref="roomView"
|
||||||
roomId={this.state.currentRoom}
|
roomAddress={this.state.currentRoom || this.state.currentRoomAlias}
|
||||||
roomAlias={this.state.currentRoomAlias}
|
|
||||||
eventId={this.state.initialEventId}
|
eventId={this.state.initialEventId}
|
||||||
thirdPartyInvite={this.state.thirdPartyInvite}
|
thirdPartyInvite={this.state.thirdPartyInvite}
|
||||||
oobData={this.state.roomOobData}
|
oobData={this.state.roomOobData}
|
||||||
|
|
|
@ -54,11 +54,15 @@ module.exports = React.createClass({
|
||||||
propTypes: {
|
propTypes: {
|
||||||
ConferenceHandler: React.PropTypes.any,
|
ConferenceHandler: React.PropTypes.any,
|
||||||
|
|
||||||
roomId: React.PropTypes.string.isRequired,
|
// the ID for this room (or, if we don't know it, an alias for it)
|
||||||
|
//
|
||||||
// if we are referring to this room by a given alias (e.g. in the URL), track it.
|
// XXX: if this is an alias, we will display a 'join' dialogue,
|
||||||
// useful for joining rooms by alias correctly (and fixing https://github.com/vector-im/vector-web/issues/819)
|
// regardless of whether we are already a member, or if the room is
|
||||||
roomAlias: React.PropTypes.string,
|
// peekable. Currently there is a big mess, where at least four
|
||||||
|
// different components (RoomView, MatrixChat, RoomDirectory,
|
||||||
|
// SlashCommands) have logic for turning aliases into rooms, and each
|
||||||
|
// of them do it differently and have different edge cases.
|
||||||
|
roomAddress: React.PropTypes.string.isRequired,
|
||||||
|
|
||||||
// An object representing a third party invite to join this room
|
// An object representing a third party invite to join this room
|
||||||
// Fields:
|
// Fields:
|
||||||
|
@ -93,7 +97,7 @@ module.exports = React.createClass({
|
||||||
},
|
},
|
||||||
|
|
||||||
getInitialState: function() {
|
getInitialState: function() {
|
||||||
var room = this.props.roomId ? MatrixClientPeg.get().getRoom(this.props.roomId) : null;
|
var room = MatrixClientPeg.get().getRoom(this.props.roomAddress);
|
||||||
return {
|
return {
|
||||||
room: room,
|
room: room,
|
||||||
roomLoading: !room,
|
roomLoading: !room,
|
||||||
|
@ -123,7 +127,6 @@ module.exports = React.createClass({
|
||||||
this.dispatcherRef = dis.register(this.onAction);
|
this.dispatcherRef = dis.register(this.onAction);
|
||||||
MatrixClientPeg.get().on("Room", this.onRoom);
|
MatrixClientPeg.get().on("Room", this.onRoom);
|
||||||
MatrixClientPeg.get().on("Room.timeline", this.onRoomTimeline);
|
MatrixClientPeg.get().on("Room.timeline", this.onRoomTimeline);
|
||||||
MatrixClientPeg.get().on("Room.name", this.onRoomName);
|
|
||||||
MatrixClientPeg.get().on("Room.accountData", this.onRoomAccountData);
|
MatrixClientPeg.get().on("Room.accountData", this.onRoomAccountData);
|
||||||
MatrixClientPeg.get().on("RoomState.members", this.onRoomStateMember);
|
MatrixClientPeg.get().on("RoomState.members", this.onRoomStateMember);
|
||||||
// xchat-style tab complete, add a colon if tab
|
// xchat-style tab complete, add a colon if tab
|
||||||
|
@ -146,9 +149,9 @@ module.exports = React.createClass({
|
||||||
// We can /peek though. If it fails then we present the join UI. If it
|
// We can /peek though. If it fails then we present the join UI. If it
|
||||||
// succeeds then great, show the preview (but we still may be able to /join!).
|
// succeeds then great, show the preview (but we still may be able to /join!).
|
||||||
if (!this.state.room) {
|
if (!this.state.room) {
|
||||||
console.log("Attempting to peek into room %s", this.props.roomId);
|
console.log("Attempting to peek into room %s", this.props.roomAddress);
|
||||||
|
|
||||||
MatrixClientPeg.get().peekInRoom(this.props.roomId).then((room) => {
|
MatrixClientPeg.get().peekInRoom(this.props.roomAddress).then((room) => {
|
||||||
this.setState({
|
this.setState({
|
||||||
room: room,
|
room: room,
|
||||||
roomLoading: false,
|
roomLoading: false,
|
||||||
|
@ -200,7 +203,6 @@ module.exports = React.createClass({
|
||||||
if (MatrixClientPeg.get()) {
|
if (MatrixClientPeg.get()) {
|
||||||
MatrixClientPeg.get().removeListener("Room", this.onRoom);
|
MatrixClientPeg.get().removeListener("Room", this.onRoom);
|
||||||
MatrixClientPeg.get().removeListener("Room.timeline", this.onRoomTimeline);
|
MatrixClientPeg.get().removeListener("Room.timeline", this.onRoomTimeline);
|
||||||
MatrixClientPeg.get().removeListener("Room.name", this.onRoomName);
|
|
||||||
MatrixClientPeg.get().removeListener("Room.accountData", this.onRoomAccountData);
|
MatrixClientPeg.get().removeListener("Room.accountData", this.onRoomAccountData);
|
||||||
MatrixClientPeg.get().removeListener("RoomState.members", this.onRoomStateMember);
|
MatrixClientPeg.get().removeListener("RoomState.members", this.onRoomStateMember);
|
||||||
}
|
}
|
||||||
|
@ -233,7 +235,7 @@ module.exports = React.createClass({
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var call = CallHandler.getCallForRoom(payload.room_id);
|
var call = this._getCallForRoom();
|
||||||
var callState;
|
var callState;
|
||||||
|
|
||||||
if (call) {
|
if (call) {
|
||||||
|
@ -256,7 +258,7 @@ module.exports = React.createClass({
|
||||||
},
|
},
|
||||||
|
|
||||||
componentWillReceiveProps: function(newProps) {
|
componentWillReceiveProps: function(newProps) {
|
||||||
if (newProps.roomId != this.props.roomId) {
|
if (newProps.roomAddress != this.props.roomAddress) {
|
||||||
throw new Error("changing room on a RoomView is not supported");
|
throw new Error("changing room on a RoomView is not supported");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -270,7 +272,7 @@ module.exports = React.createClass({
|
||||||
if (this.unmounted) return;
|
if (this.unmounted) return;
|
||||||
|
|
||||||
// ignore events for other rooms
|
// ignore events for other rooms
|
||||||
if (room.roomId != this.props.roomId) return;
|
if (!this.state.room || room.roomId != this.state.room.roomId) return;
|
||||||
|
|
||||||
// ignore anything but real-time updates at the end of the room:
|
// ignore anything but real-time updates at the end of the room:
|
||||||
// updates from pagination will happen when the paginate completes.
|
// updates from pagination will happen when the paginate completes.
|
||||||
|
@ -321,30 +323,18 @@ module.exports = React.createClass({
|
||||||
// set it in our state and start using it (ie. init the timeline)
|
// set it in our state and start using it (ie. init the timeline)
|
||||||
// This will happen if we start off viewing a room we're not joined,
|
// This will happen if we start off viewing a room we're not joined,
|
||||||
// then join it whilst RoomView is looking at that room.
|
// then join it whilst RoomView is looking at that room.
|
||||||
if (room.roomId == this.props.roomId && !this.state.room) {
|
if (!this.state.room && room.roomId == this._joiningRoomId) {
|
||||||
|
this._joiningRoomId = undefined;
|
||||||
this.setState({
|
this.setState({
|
||||||
room: room
|
room: room,
|
||||||
|
joining: false,
|
||||||
});
|
});
|
||||||
this._onRoomLoaded(room);
|
this._onRoomLoaded(room);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
onRoomName: function(room) {
|
|
||||||
// NB don't set state.room here.
|
|
||||||
//
|
|
||||||
// When peeking, this event lands *before* the timeline is correctly
|
|
||||||
// synced; if we set state.room here, the TimelinePanel will be
|
|
||||||
// instantiated, and it will initialise its scroll state, with *no
|
|
||||||
// events*. In short, the scroll state will be all messed up.
|
|
||||||
//
|
|
||||||
// There's no need to set state.room here anyway.
|
|
||||||
if (room.roomId == this.props.roomId) {
|
|
||||||
this.forceUpdate();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
updateTint: function() {
|
updateTint: function() {
|
||||||
var room = MatrixClientPeg.get().getRoom(this.props.roomId);
|
var room = this.state.room;
|
||||||
if (!room) return;
|
if (!room) return;
|
||||||
|
|
||||||
var color_scheme_event = room.getAccountData("org.matrix.room.color_scheme");
|
var color_scheme_event = room.getAccountData("org.matrix.room.color_scheme");
|
||||||
|
@ -367,28 +357,33 @@ module.exports = React.createClass({
|
||||||
},
|
},
|
||||||
|
|
||||||
onRoomStateMember: function(ev, state, member) {
|
onRoomStateMember: function(ev, state, member) {
|
||||||
if (member.roomId === this.props.roomId) {
|
// ignore if we don't have a room yet
|
||||||
// a member state changed in this room, refresh the tab complete list
|
if (!this.state.room) {
|
||||||
this._updateTabCompleteList();
|
|
||||||
|
|
||||||
var room = MatrixClientPeg.get().getRoom(this.props.roomId);
|
|
||||||
if (!room) return;
|
|
||||||
var me = MatrixClientPeg.get().credentials.userId;
|
|
||||||
if (this.state.joining && room.hasMembershipState(me, "join")) {
|
|
||||||
this.setState({
|
|
||||||
joining: false
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!this.props.ConferenceHandler) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (member.roomId !== this.props.roomId ||
|
|
||||||
member.userId !== this.props.ConferenceHandler.getConferenceUserIdForRoom(member.roomId)) {
|
// ignore members in other rooms
|
||||||
|
if (member.roomId !== this.state.room.roomId) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this._updateConfCallNotification();
|
|
||||||
|
// a member state changed in this room, refresh the tab complete list
|
||||||
|
this._updateTabCompleteList();
|
||||||
|
|
||||||
|
// if we are now a member of the room, where we were not before, that
|
||||||
|
// means we have finished joining a room we were previously peeking
|
||||||
|
// into.
|
||||||
|
var me = MatrixClientPeg.get().credentials.userId;
|
||||||
|
if (this.state.joining && this.state.room.hasMembershipState(me, "join")) {
|
||||||
|
this.setState({
|
||||||
|
joining: false
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.props.ConferenceHandler &&
|
||||||
|
member.userId === this.props.ConferenceHandler.getConferenceUserIdForRoom(member.roomId)) {
|
||||||
|
this._updateConfCallNotification();
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
_hasUnsentMessages: function(room) {
|
_hasUnsentMessages: function(room) {
|
||||||
|
@ -403,12 +398,12 @@ module.exports = React.createClass({
|
||||||
},
|
},
|
||||||
|
|
||||||
_updateConfCallNotification: function() {
|
_updateConfCallNotification: function() {
|
||||||
var room = MatrixClientPeg.get().getRoom(this.props.roomId);
|
var room = this.state.room;
|
||||||
if (!room || !this.props.ConferenceHandler) {
|
if (!room || !this.props.ConferenceHandler) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var confMember = room.getMember(
|
var confMember = room.getMember(
|
||||||
this.props.ConferenceHandler.getConferenceUserIdForRoom(this.props.roomId)
|
this.props.ConferenceHandler.getConferenceUserIdForRoom(room.roomId)
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!confMember) {
|
if (!confMember) {
|
||||||
|
@ -427,7 +422,7 @@ module.exports = React.createClass({
|
||||||
},
|
},
|
||||||
|
|
||||||
componentDidMount: function() {
|
componentDidMount: function() {
|
||||||
var call = CallHandler.getCallForRoom(this.props.roomId);
|
var call = this._getCallForRoom();
|
||||||
var callState = call ? call.call_state : "ended";
|
var callState = call ? call.call_state : "ended";
|
||||||
this.setState({
|
this.setState({
|
||||||
callState: callState
|
callState: callState
|
||||||
|
@ -559,25 +554,35 @@ module.exports = React.createClass({
|
||||||
|
|
||||||
display_name_promise.then(() => {
|
display_name_promise.then(() => {
|
||||||
var sign_url = this.props.thirdPartyInvite ? this.props.thirdPartyInvite.inviteSignUrl : undefined;
|
var sign_url = this.props.thirdPartyInvite ? this.props.thirdPartyInvite.inviteSignUrl : undefined;
|
||||||
return MatrixClientPeg.get().joinRoom(this.props.roomAlias || this.props.roomId,
|
return MatrixClientPeg.get().joinRoom(this.props.roomAddress,
|
||||||
{ inviteSignUrl: sign_url } )
|
{ inviteSignUrl: sign_url } )
|
||||||
}).done(function() {
|
}).then(function(resp) {
|
||||||
|
var roomId = resp.roomId;
|
||||||
|
|
||||||
// It is possible that there is no Room yet if state hasn't come down
|
// It is possible that there is no Room yet if state hasn't come down
|
||||||
// from /sync - joinRoom will resolve when the HTTP request to join succeeds,
|
// from /sync - joinRoom will resolve when the HTTP request to join succeeds,
|
||||||
// NOT when it comes down /sync. If there is no room, we'll keep the
|
// NOT when it comes down /sync. If there is no room, we'll keep the
|
||||||
// joining flag set until we see it. Likewise, if our state is not
|
// joining flag set until we see it.
|
||||||
// "join" we'll keep this flag set until it comes down /sync.
|
|
||||||
|
|
||||||
// We'll need to initialise the timeline when joining, but due to
|
// We'll need to initialise the timeline when joining, but due to
|
||||||
// the above, we can't do it here: we do it in onRoom instead,
|
// the above, we can't do it here: we do it in onRoom instead,
|
||||||
// once we have a useable room object.
|
// once we have a useable room object.
|
||||||
var room = MatrixClientPeg.get().getRoom(self.props.roomId);
|
var room = MatrixClientPeg.get().getRoom(roomId);
|
||||||
var me = MatrixClientPeg.get().credentials.userId;
|
if (!room) {
|
||||||
self.setState({
|
// wait for the room to turn up in onRoom.
|
||||||
joining: room ? !room.hasMembershipState(me, "join") : true,
|
self._joiningRoomId = roomId;
|
||||||
room: room
|
} else {
|
||||||
});
|
// we've got a valid room, but that might also just mean that
|
||||||
}, function(error) {
|
// it was peekable (so we had one before anyway). If we are
|
||||||
|
// not yet a member of the room, we will need to wait for that
|
||||||
|
// to happen, in onRoomStateMember.
|
||||||
|
var me = MatrixClientPeg.get().credentials.userId;
|
||||||
|
self.setState({
|
||||||
|
joining: !room.hasMembershipState(me, "join"),
|
||||||
|
room: room
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}).catch(function(error) {
|
||||||
self.setState({
|
self.setState({
|
||||||
joining: false,
|
joining: false,
|
||||||
joinError: error
|
joinError: error
|
||||||
|
@ -612,7 +617,8 @@ module.exports = React.createClass({
|
||||||
description: msg
|
description: msg
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
}).done();
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
joining: true
|
joining: true
|
||||||
});
|
});
|
||||||
|
@ -667,7 +673,7 @@ module.exports = React.createClass({
|
||||||
uploadFile: function(file) {
|
uploadFile: function(file) {
|
||||||
var self = this;
|
var self = this;
|
||||||
ContentMessages.sendContentToRoom(
|
ContentMessages.sendContentToRoom(
|
||||||
file, this.props.roomId, MatrixClientPeg.get()
|
file, this.state.room.roomId, MatrixClientPeg.get()
|
||||||
).done(undefined, function(error) {
|
).done(undefined, function(error) {
|
||||||
var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
||||||
Modal.createDialog(ErrorDialog, {
|
Modal.createDialog(ErrorDialog, {
|
||||||
|
@ -702,7 +708,7 @@ module.exports = React.createClass({
|
||||||
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: [
|
||||||
this.props.roomId
|
this.state.room.roomId
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -910,12 +916,12 @@ module.exports = React.createClass({
|
||||||
onLeaveClick: function() {
|
onLeaveClick: function() {
|
||||||
dis.dispatch({
|
dis.dispatch({
|
||||||
action: 'leave_room',
|
action: 'leave_room',
|
||||||
room_id: this.props.roomId,
|
room_id: this.state.room.roomId,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
onForgetClick: function() {
|
onForgetClick: function() {
|
||||||
MatrixClientPeg.get().forget(this.props.roomId).done(function() {
|
MatrixClientPeg.get().forget(this.state.room.roomId).done(function() {
|
||||||
dis.dispatch({ action: 'view_next_room' });
|
dis.dispatch({ action: 'view_next_room' });
|
||||||
}, function(err) {
|
}, function(err) {
|
||||||
var errCode = err.errcode || "unknown error code";
|
var errCode = err.errcode || "unknown error code";
|
||||||
|
@ -932,7 +938,7 @@ module.exports = React.createClass({
|
||||||
this.setState({
|
this.setState({
|
||||||
rejecting: true
|
rejecting: true
|
||||||
});
|
});
|
||||||
MatrixClientPeg.get().leave(this.props.roomId).done(function() {
|
MatrixClientPeg.get().leave(this.props.roomAddress).done(function() {
|
||||||
dis.dispatch({ action: 'view_next_room' });
|
dis.dispatch({ action: 'view_next_room' });
|
||||||
self.setState({
|
self.setState({
|
||||||
rejecting: false
|
rejecting: false
|
||||||
|
@ -1090,7 +1096,7 @@ module.exports = React.createClass({
|
||||||
},
|
},
|
||||||
|
|
||||||
onMuteAudioClick: function() {
|
onMuteAudioClick: function() {
|
||||||
var call = CallHandler.getCallForRoom(this.props.roomId);
|
var call = this._getCallForRoom();
|
||||||
if (!call) {
|
if (!call) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -1102,7 +1108,7 @@ module.exports = React.createClass({
|
||||||
},
|
},
|
||||||
|
|
||||||
onMuteVideoClick: function() {
|
onMuteVideoClick: function() {
|
||||||
var call = CallHandler.getCallForRoom(this.props.roomId);
|
var call = this._getCallForRoom();
|
||||||
if (!call) {
|
if (!call) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -1142,6 +1148,29 @@ module.exports = React.createClass({
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the ID of the displayed room
|
||||||
|
*
|
||||||
|
* Returns null if the RoomView was instantiated on a room alias and
|
||||||
|
* we haven't yet joined the room.
|
||||||
|
*/
|
||||||
|
getRoomId: function() {
|
||||||
|
if (!this.state.room) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return this.state.room.roomId;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get any current call for this room
|
||||||
|
*/
|
||||||
|
_getCallForRoom: function() {
|
||||||
|
if (!this.state.room) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return CallHandler.getCallForRoom(this.state.room.roomId);
|
||||||
|
},
|
||||||
|
|
||||||
// this has to be a proper method rather than an unnamed function,
|
// this has to be a proper method rather than an unnamed function,
|
||||||
// otherwise react calls it with null on each update.
|
// otherwise react calls it with null on each update.
|
||||||
_gatherTimelinePanelRef: function(r) {
|
_gatherTimelinePanelRef: function(r) {
|
||||||
|
@ -1164,7 +1193,6 @@ module.exports = React.createClass({
|
||||||
var TimelinePanel = sdk.getComponent("structures.TimelinePanel");
|
var TimelinePanel = sdk.getComponent("structures.TimelinePanel");
|
||||||
|
|
||||||
if (!this.state.room) {
|
if (!this.state.room) {
|
||||||
if (this.props.roomId) {
|
|
||||||
if (this.state.roomLoading) {
|
if (this.state.roomLoading) {
|
||||||
return (
|
return (
|
||||||
<div className="mx_RoomView">
|
<div className="mx_RoomView">
|
||||||
|
@ -1201,12 +1229,6 @@ module.exports = React.createClass({
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
else {
|
|
||||||
return (
|
|
||||||
<div />
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var myUserId = MatrixClientPeg.get().credentials.userId;
|
var myUserId = MatrixClientPeg.get().credentials.userId;
|
||||||
|
@ -1248,7 +1270,7 @@ module.exports = React.createClass({
|
||||||
// We have successfully loaded this room, and are not previewing.
|
// We have successfully loaded this room, and are not previewing.
|
||||||
// Display the "normal" room view.
|
// Display the "normal" room view.
|
||||||
|
|
||||||
var call = CallHandler.getCallForRoom(this.props.roomId);
|
var call = this._getCallForRoom();
|
||||||
var inCall = false;
|
var inCall = false;
|
||||||
if (call && (this.state.callState !== 'ended' && this.state.callState !== 'ringing')) {
|
if (call && (this.state.callState !== 'ended' && this.state.callState !== 'ringing')) {
|
||||||
inCall = true;
|
inCall = true;
|
||||||
|
|
Loading…
Reference in a new issue