WIP: try to support non-guest room peek. Rename visibility permissions in room settings a bit, and fix how they are persisted.
parent
cc3d27bd4b
commit
491ba94230
|
@ -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} />
|
||||||
);
|
);
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 }
|
||||||
|
|
Loading…
Reference in New Issue