WIP: try to support non-guest room peek. Rename visibility permissions in room settings a bit, and fix how they are persisted.

pull/21833/head
Matthew Hodgson 2016-01-18 17:39:23 +00:00
parent cc3d27bd4b
commit 491ba94230
4 changed files with 132 additions and 41 deletions

View File

@ -65,6 +65,7 @@ module.exports = React.createClass({
collapse_rhs: false, collapse_rhs: false,
ready: false, ready: false,
width: 10000, width: 10000,
autoPeek: true, // by default, we peek into rooms when we try to join them
}; };
if (s.logged_in) { if (s.logged_in) {
if (MatrixClientPeg.get().getRooms().length) { if (MatrixClientPeg.get().getRooms().length) {
@ -304,6 +305,9 @@ module.exports = React.createClass({
}); });
break; break;
case 'view_room': case 'view_room':
// by default we autoPeek rooms, unless we were called explicitly with
// autoPeek=false by something like RoomDirectory who has already peeked
this.setState({ autoPeek : payload.auto_peek === false ? false : true });
this._viewRoom(payload.room_id, payload.show_settings); this._viewRoom(payload.room_id, payload.show_settings);
break; break;
case 'view_prev_room': case 'view_prev_room':
@ -787,6 +791,7 @@ module.exports = React.createClass({
<RoomView <RoomView
ref="roomView" ref="roomView"
roomId={this.state.currentRoom} roomId={this.state.currentRoom}
autoPeek={this.state.autoPeek}
key={this.state.currentRoom} key={this.state.currentRoom}
ConferenceHandler={this.props.ConferenceHandler} /> ConferenceHandler={this.props.ConferenceHandler} />
); );

View File

@ -57,7 +57,9 @@ if (DEBUG_SCROLL) {
module.exports = React.createClass({ module.exports = React.createClass({
displayName: 'RoomView', displayName: 'RoomView',
propTypes: { propTypes: {
ConferenceHandler: React.PropTypes.any ConferenceHandler: React.PropTypes.any,
roomId: React.PropTypes.string,
autoPeek: React.PropTypes.bool, // should we try to peek the room on mount, or has whoever invoked us already initiated a peek?
}, },
/* properties in RoomView objects include: /* properties in RoomView objects include:
@ -78,7 +80,9 @@ module.exports = React.createClass({
syncState: MatrixClientPeg.get().getSyncState(), syncState: MatrixClientPeg.get().getSyncState(),
hasUnsentMessages: this._hasUnsentMessages(room), hasUnsentMessages: this._hasUnsentMessages(room),
callState: null, callState: null,
autoPeekDone: false, // track whether our autoPeek (if any) has completed)
guestsCanJoin: false, guestsCanJoin: false,
canPeek: false,
readMarkerEventId: room ? room.getEventReadUpTo(MatrixClientPeg.get().credentials.userId) : null, readMarkerEventId: room ? room.getEventReadUpTo(MatrixClientPeg.get().credentials.userId) : null,
readMarkerGhostEventId: undefined readMarkerGhostEventId: undefined
} }
@ -86,6 +90,7 @@ module.exports = React.createClass({
componentWillMount: function() { componentWillMount: function() {
this.dispatcherRef = dis.register(this.onAction); this.dispatcherRef = dis.register(this.onAction);
MatrixClientPeg.get().on("Room", this.onNewRoom);
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.name", this.onRoomName);
MatrixClientPeg.get().on("Room.accountData", this.onRoomAccountData); MatrixClientPeg.get().on("Room.accountData", this.onRoomAccountData);
@ -110,9 +115,13 @@ module.exports = React.createClass({
// We can't try to /join because this may implicitly accept invites (!) // We can't try to /join because this may implicitly accept invites (!)
// 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 && this.props.autoPeek) {
console.log("Attempting to peek into room %s", this.props.roomId); console.log("Attempting to peek into room %s", this.props.roomId);
MatrixClientPeg.get().peekInRoom(this.props.roomId).done(() => { MatrixClientPeg.get().peekInRoom(this.props.roomId).done(() => {
this.setState({
autoPeekDone: true;
});
// we don't need to do anything - JS SDK will emit Room events // we don't need to do anything - JS SDK will emit Room events
// which will update the UI. We *do* however need to know if we // which will update the UI. We *do* however need to know if we
// can join the room so we can fiddle with the UI appropriately. // can join the room so we can fiddle with the UI appropriately.
@ -120,15 +129,20 @@ module.exports = React.createClass({
if (!peekedRoom) { if (!peekedRoom) {
return; return;
} }
var guestAccessEvent = peekedRoom.currentState.getStateEvents("m.room.guest_access", ""); var guestAccessEvent = peekedRoom.currentState.getStateEvents("m.room.guest_access", "");
if (!guestAccessEvent) { if (guestAccessEvent && guestAccessEvent.getContent().guest_access === "can_join") {
return;
}
if (guestAccessEvent.getContent().guest_access === "can_join") {
this.setState({ this.setState({
guestsCanJoin: true guestsCanJoin: true
}); });
} }
var historyVisibility = peekedRoom.currentState.getStateEvents("m.room.history_visibility", "");
if (historyVisibility && historyVisibility.getContent().history_visibility === "world_readable") {
this.setState({
canPeek: true
});
}
}, function(err) { }, function(err) {
console.error("Failed to peek into room: %s", err); console.error("Failed to peek into room: %s", err);
}); });
@ -155,6 +169,7 @@ module.exports = React.createClass({
} }
dis.unregister(this.dispatcherRef); dis.unregister(this.dispatcherRef);
if (MatrixClientPeg.get()) { if (MatrixClientPeg.get()) {
MatrixClientPeg.get().removeListener("Room", this.onNewRoom);
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.name", this.onRoomName);
MatrixClientPeg.get().removeListener("Room.accountData", this.onRoomAccountData); MatrixClientPeg.get().removeListener("Room.accountData", this.onRoomAccountData);
@ -278,6 +293,14 @@ module.exports = React.createClass({
}); });
}, },
onNewRoom: function(room) {
if (room.roomId == this.props.roomId) {
this.setState({
room: room
});
}
},
onRoomName: function(room) { onRoomName: function(room) {
if (room.roomId == this.props.roomId) { if (room.roomId == this.props.roomId) {
this.setState({ this.setState({
@ -929,9 +952,10 @@ module.exports = React.createClass({
); );
} }
var visibilityDeferred;
if (old_history_visibility != newVals.history_visibility && if (old_history_visibility != newVals.history_visibility &&
newVals.history_visibility != undefined) { newVals.history_visibility != undefined) {
deferreds.push( visibilityDeferred =
MatrixClientPeg.get().sendStateEvent( MatrixClientPeg.get().sendStateEvent(
this.state.room.roomId, "m.room.history_visibility", { this.state.room.roomId, "m.room.history_visibility", {
history_visibility: newVals.history_visibility, history_visibility: newVals.history_visibility,
@ -940,6 +964,27 @@ module.exports = React.createClass({
); );
} }
if (old_guest_read != newVals.guest_read ||
old_guest_join != newVals.guest_join)
{
var guestDeferred =
MatrixClientPeg.get().setGuestAccess(this.state.room.roomId, {
allowRead: newVals.guest_read,
allowJoin: newVals.guest_join
});
if (visibilityDeferred) {
visibilityDeferred = visibilityDeferred.then(guestDeferred);
}
else {
visibilityDeferred = guestDeferred;
}
}
if (visibilityDeferred) {
deferreds.push(visibilityDeferred);
}
if (newVals.power_levels) { if (newVals.power_levels) {
deferreds.push( deferreds.push(
MatrixClientPeg.get().sendStateEvent( MatrixClientPeg.get().sendStateEvent(
@ -1033,17 +1078,6 @@ module.exports = React.createClass({
); );
} }
if (old_guest_read != newVals.guest_read ||
old_guest_join != newVals.guest_join)
{
deferreds.push(
MatrixClientPeg.get().setGuestAccess(this.state.room.roomId, {
allowRead: newVals.guest_read,
allowJoin: newVals.guest_join
})
);
}
if (deferreds.length) { if (deferreds.length) {
var self = this; var self = this;
q.allSettled(deferreds).then( q.allSettled(deferreds).then(
@ -1391,12 +1425,28 @@ module.exports = React.createClass({
if (!this.state.room) { if (!this.state.room) {
if (this.props.roomId) { if (this.props.roomId) {
return ( if (this.props.autoPeek && !this.state.autoPeekDone) {
<div> return (
<button onClick={this.onJoinButtonClicked}>Join Room</button> <div className="mx_RoomView">
</div> <Loader />
); </div>
} else { );
}
else {
var joinErrorText = this.state.joinError ? "Failed to join room!" : "";
return (
<div className="mx_RoomView">
<RoomHeader ref="header" room={this.state.room} simpleHeader="Join room"/>
<div className="mx_RoomView_auxPanel">
<RoomPreviewBar onJoinClick={ this.onJoinButtonClicked }
canJoin={ true } canPreview={ false }/>
<div className="error">{joinErrorText}</div>
</div>
</div>
);
}
}
else {
return ( return (
<div /> <div />
); );
@ -1417,16 +1467,21 @@ module.exports = React.createClass({
var inviteEvent = myMember.events.member; var inviteEvent = myMember.events.member;
var inviterName = inviteEvent.sender ? inviteEvent.sender.name : inviteEvent.getSender(); var inviterName = inviteEvent.sender ? inviteEvent.sender.name : inviteEvent.getSender();
// XXX: Leaving this intentionally basic for now because invites are about to change totally // XXX: Leaving this intentionally basic for now because invites are about to change totally
// FIXME: This comment is now outdated - what do we need to fix? ^
var joinErrorText = this.state.joinError ? "Failed to join room!" : ""; var joinErrorText = this.state.joinError ? "Failed to join room!" : "";
var rejectErrorText = this.state.rejectError ? "Failed to reject invite!" : ""; var rejectErrorText = this.state.rejectError ? "Failed to reject invite!" : "";
// We deliberately don't try to peek into invites, even if we have permission to peek
// as they could be a spam vector.
// XXX: in future we could give the option of a 'Preview' button which lets them view anyway.
return ( return (
<div className="mx_RoomView"> <div className="mx_RoomView">
<RoomHeader ref="header" room={this.state.room} simpleHeader="Room invite"/> <RoomHeader ref="header" room={this.state.room} simpleHeader="Room invite"/>
<div className="mx_RoomView_invitePrompt"> <div className="mx_RoomView_auxPanel">
<div>{inviterName} has invited you to a room</div> <RoomPreviewBar onJoinClick={ this.onJoinButtonClicked }
<br/> onRejectClick={ this.onRejectButtonClicked }
<button ref="joinButton" onClick={this.onJoinButtonClicked}>Join</button> inviterName={ inviterName }
<button onClick={this.onRejectButtonClicked}>Reject</button> canJoin={ true } canPreview={ false }/>
<div className="error">{joinErrorText}</div> <div className="error">{joinErrorText}</div>
<div className="error">{rejectErrorText}</div> <div className="error">{rejectErrorText}</div>
</div> </div>
@ -1544,6 +1599,12 @@ module.exports = React.createClass({
<RoomPreviewBar onJoinClick={this.onJoinButtonClicked} canJoin={true} /> <RoomPreviewBar onJoinClick={this.onJoinButtonClicked} canJoin={true} />
); );
} }
else if (this.state.canPeek &&
(!myMember || myMember.membership !== "join")) {
aux = (
<RoomPreviewBar onJoinClick={this.onJoinButtonClicked} canJoin={true} />
);
}
var conferenceCallNotification = null; var conferenceCallNotification = null;
if (this.state.displayConfCallNotification) { if (this.state.displayConfCallNotification) {

View File

@ -23,33 +23,57 @@ module.exports = React.createClass({
propTypes: { propTypes: {
onJoinClick: React.PropTypes.func, onJoinClick: React.PropTypes.func,
canJoin: React.PropTypes.bool onRejectClick: React.PropTypes.func,
inviterName: React.PropTypes.string,
canJoin: React.PropTypes.bool,
canPreview: React.PropTypes.bool,
}, },
getDefaultProps: function() { getDefaultProps: function() {
return { return {
onJoinClick: function() {}, onJoinClick: function() {},
canJoin: false canJoin: false,
canPreview: true,
}; };
}, },
render: function() { render: function() {
var joinBlock; var joinBlock, previewBlock;
if (this.props.canJoin) { if (this.props.inviter) {
joinBlock = (
<div>
<div className="mx_RoomPreviewBar_invite_text">
You have been invited to join this room by { this.props.inviterName }
</div>
<div className="mx_RoomPreviewBar_join_text">
Would you like to <a onClick={ this.props.onJoinClick }>accept</a> or
<a onClick={ this.props.onRejectClick }>decline</a> this invitation?
</div>
</div>
);
}
else if (this.props.canJoin) {
joinBlock = ( joinBlock = (
<div className="mx_RoomPreviewBar_join_text"> <div className="mx_RoomPreviewBar_join_text">
Would you like to <a onClick={this.props.onJoinClick}>join</a> this room? Would you like to <a onClick={ this.props.onJoinClick }>join</a> this room?
</div>
);
}
if (this.props.canPreview) {
previewBlock = (
<div className="mx_RoomPreviewBar_preview_text">
This is a preview of this room. Room interactions have been disabled.
</div> </div>
); );
} }
return ( return (
<div className="mx_RoomPreviewBar"> <div className="mx_RoomPreviewBar">
<div className="mx_RoomPreviewBar_preview_text"> { previewBlock }
This is a preview of this room. Room interactions have been disabled. { joinBlock }
</div>
{joinBlock}
</div> </div>
); );
} }

View File

@ -660,12 +660,13 @@ module.exports = React.createClass({
} }
</div> </div>
// FIXME: disable guests_read if the user hasn't turned on shared history
return ( return (
<div className="mx_RoomSettings"> <div className="mx_RoomSettings">
<label><input type="checkbox" ref="is_private" defaultChecked={join_rule != "public"}/> Make this room private</label> <br/> <label><input type="checkbox" ref="is_private" defaultChecked={join_rule != "public"}/> Make this room private</label> <br/>
<label><input type="checkbox" ref="share_history" defaultChecked={history_visibility == "shared"}/> Share message history with new users</label> <br/> <label><input type="checkbox" ref="share_history" defaultChecked={history_visibility === "shared" || history_visibility === "world_readable"}/> Share message history with new participants</label> <br/>
<label><input type="checkbox" ref="guests_read" defaultChecked={history_visibility === "world_readable"}/> Allow guests to read messages in this room</label> <br/> <label><input type="checkbox" ref="guests_join" defaultChecked={guest_access === "can_join"}/> Let guests join this room</label> <br/>
<label><input type="checkbox" ref="guests_join" defaultChecked={guest_access === "can_join"}/> Allow guests to join this room</label> <br/> <label><input type="checkbox" ref="guests_read" defaultChecked={history_visibility === "world_readable"}/> Let users read message history without joining</label> <br/>
<label className="mx_RoomSettings_encrypt"><input type="checkbox" /> Encrypt room</label> <label className="mx_RoomSettings_encrypt"><input type="checkbox" /> Encrypt room</label>
{ tags_section } { tags_section }