diff --git a/src/GuestAccess.js b/src/GuestAccess.js
new file mode 100644
index 0000000000..ef48d23ded
--- /dev/null
+++ b/src/GuestAccess.js
@@ -0,0 +1,51 @@
+/*
+Copyright 2015 OpenMarket Ltd
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+const IS_GUEST_KEY = "matrix-is-guest";
+
+class GuestAccess {
+
+ constructor(localStorage) {
+ this.localStorage = localStorage;
+ try {
+ this._isGuest = localStorage.getItem(IS_GUEST_KEY) === "true";
+ }
+ catch (e) {} // don't care
+ }
+
+ setPeekedRoom(roomId) {
+ // we purposefully do not persist this to local storage as peeking is
+ // entirely transient.
+ this._peekedRoomId = roomId;
+ }
+
+ getPeekedRoom() {
+ return this._peekedRoomId;
+ }
+
+ isGuest() {
+ return this._isGuest;
+ }
+
+ markAsGuest(isGuest) {
+ try {
+ this.localStorage.setItem(IS_GUEST_KEY, JSON.stringify(isGuest));
+ } catch (e) {} // ignore. If they don't do LS, they'll just get a new account.
+ this._isGuest = isGuest;
+ this._peekedRoomId = null;
+ }
+}
+
+module.exports = GuestAccess;
diff --git a/src/MatrixClientPeg.js b/src/MatrixClientPeg.js
index 4a83ed09d9..dbb3dbf83e 100644
--- a/src/MatrixClientPeg.js
+++ b/src/MatrixClientPeg.js
@@ -18,6 +18,7 @@ limitations under the License.
// A thing that holds your Matrix Client
var Matrix = require("matrix-js-sdk");
+var GuestAccess = require("./GuestAccess");
var matrixClient = null;
@@ -33,7 +34,7 @@ function deviceId() {
return id;
}
-function createClient(hs_url, is_url, user_id, access_token) {
+function createClient(hs_url, is_url, user_id, access_token, guestAccess) {
var opts = {
baseUrl: hs_url,
idBaseUrl: is_url,
@@ -47,6 +48,15 @@ function createClient(hs_url, is_url, user_id, access_token) {
}
matrixClient = Matrix.createClient(opts);
+ if (guestAccess) {
+ console.log("Guest: %s", guestAccess.isGuest());
+ matrixClient.setGuest(guestAccess.isGuest());
+ var peekedRoomId = guestAccess.getPeekedRoom();
+ if (peekedRoomId) {
+ console.log("Peeking in room %s", peekedRoomId);
+ matrixClient.peekInRoom(peekedRoomId);
+ }
+ }
}
if (localStorage) {
@@ -54,12 +64,18 @@ if (localStorage) {
var is_url = localStorage.getItem("mx_is_url") || 'https://matrix.org';
var access_token = localStorage.getItem("mx_access_token");
var user_id = localStorage.getItem("mx_user_id");
+ var guestAccess = new GuestAccess(localStorage);
if (access_token && user_id && hs_url) {
- createClient(hs_url, is_url, user_id, access_token);
+ createClient(hs_url, is_url, user_id, access_token, guestAccess);
}
}
class MatrixClient {
+
+ constructor(guestAccess) {
+ this.guestAccess = guestAccess;
+ }
+
get() {
return matrixClient;
}
@@ -97,7 +113,7 @@ class MatrixClient {
}
}
- replaceUsingAccessToken(hs_url, is_url, user_id, access_token) {
+ replaceUsingAccessToken(hs_url, is_url, user_id, access_token, isGuest) {
if (localStorage) {
try {
localStorage.clear();
@@ -105,7 +121,8 @@ class MatrixClient {
console.warn("Error using local storage");
}
}
- createClient(hs_url, is_url, user_id, access_token);
+ this.guestAccess.markAsGuest(Boolean(isGuest));
+ createClient(hs_url, is_url, user_id, access_token, this.guestAccess);
if (localStorage) {
try {
localStorage.setItem("mx_hs_url", hs_url);
@@ -122,6 +139,6 @@ class MatrixClient {
}
if (!global.mxMatrixClient) {
- global.mxMatrixClient = new MatrixClient();
+ global.mxMatrixClient = new MatrixClient(new GuestAccess(localStorage));
}
module.exports = global.mxMatrixClient;
diff --git a/src/Presence.js b/src/Presence.js
index 5c9d6945a3..4152d7a487 100644
--- a/src/Presence.js
+++ b/src/Presence.js
@@ -73,6 +73,11 @@ class Presence {
}
var old_state = this.state;
this.state = newState;
+
+ if (MatrixClientPeg.get().isGuest()) {
+ return; // don't try to set presence when a guest; it won't work.
+ }
+
var self = this;
MatrixClientPeg.get().setPresence(this.state).done(function() {
console.log("Presence: %s", newState);
diff --git a/src/Signup.js b/src/Signup.js
index 74c4ad5f19..42468959fe 100644
--- a/src/Signup.js
+++ b/src/Signup.js
@@ -69,6 +69,10 @@ class Register extends Signup {
this.params.idSid = idSid;
}
+ setGuestAccessToken(token) {
+ this.guestAccessToken = token;
+ }
+
getStep() {
return this._step;
}
@@ -126,7 +130,8 @@ class Register extends Signup {
}
return MatrixClientPeg.get().register(
- this.username, this.password, this.params.sessionId, authDict, bindEmail
+ this.username, this.password, this.params.sessionId, authDict, bindEmail,
+ this.guestAccessToken
).then(function(result) {
self.credentials = result;
self.setStep("COMPLETE");
diff --git a/src/UserSettingsStore.js b/src/UserSettingsStore.js
index 1b1e8810a9..45aca1f0dc 100644
--- a/src/UserSettingsStore.js
+++ b/src/UserSettingsStore.js
@@ -15,7 +15,7 @@ limitations under the License.
*/
'use strict';
-
+var q = require("q");
var MatrixClientPeg = require("./MatrixClientPeg");
var Notifier = require("./Notifier");
@@ -35,6 +35,11 @@ module.exports = {
},
loadThreePids: function() {
+ if (MatrixClientPeg.get().isGuest()) {
+ return q({
+ threepids: []
+ }); // guests can't poke 3pid endpoint
+ }
return MatrixClientPeg.get().getThreePids();
},
diff --git a/src/components/structures/MatrixChat.js b/src/components/structures/MatrixChat.js
index d732a54922..320dad09b3 100644
--- a/src/components/structures/MatrixChat.js
+++ b/src/components/structures/MatrixChat.js
@@ -42,6 +42,7 @@ module.exports = React.createClass({
ConferenceHandler: React.PropTypes.any,
onNewScreen: React.PropTypes.func,
registrationUrl: React.PropTypes.string,
+ enableGuest: React.PropTypes.bool,
startingQueryParams: React.PropTypes.object
},
@@ -83,8 +84,21 @@ module.exports = React.createClass({
},
componentDidMount: function() {
+ this._autoRegisterAsGuest = false;
+ if (this.props.enableGuest) {
+ if (!this.props.config || !this.props.config.default_hs_url) {
+ console.error("Cannot enable guest access: No supplied config prop for HS/IS URLs");
+ }
+ else {
+ this._autoRegisterAsGuest = true;
+ }
+ }
+
this.dispatcherRef = dis.register(this.onAction);
if (this.state.logged_in) {
+ // Don't auto-register as a guest. This applies if you refresh the page on a
+ // logged in client THEN hit the Sign Out button.
+ this._autoRegisterAsGuest = false;
this.startMatrixClient();
}
this.focusComposer = false;
@@ -93,8 +107,11 @@ module.exports = React.createClass({
this.scrollStateMap = {};
document.addEventListener("keydown", this.onKeyDown);
window.addEventListener("focus", this.onFocus);
+
if (this.state.logged_in) {
this.notifyNewScreen('');
+ } else if (this._autoRegisterAsGuest) {
+ this._registerAsGuest();
} else {
this.notifyNewScreen('login');
}
@@ -126,6 +143,34 @@ module.exports = React.createClass({
}
},
+ _registerAsGuest: function() {
+ var self = this;
+ var config = this.props.config;
+ console.log("Doing guest login on %s", config.default_hs_url);
+ MatrixClientPeg.replaceUsingUrls(
+ config.default_hs_url, config.default_is_url
+ );
+ MatrixClientPeg.get().registerGuest().done(function(creds) {
+ console.log("Registered as guest: %s", creds.user_id);
+ self._setAutoRegisterAsGuest(false);
+ self.onLoggedIn({
+ userId: creds.user_id,
+ accessToken: creds.access_token,
+ homeserverUrl: config.default_hs_url,
+ identityServerUrl: config.default_is_url,
+ guest: true
+ });
+ }, function(err) {
+ console.error(err.data);
+ self._setAutoRegisterAsGuest(false);
+ });
+ },
+
+ _setAutoRegisterAsGuest: function(shouldAutoRegister) {
+ this._autoRegisterAsGuest = shouldAutoRegister;
+ this.forceUpdate();
+ },
+
onAction: function(payload) {
var roomIndexDelta = 1;
@@ -180,6 +225,14 @@ module.exports = React.createClass({
screen: 'post_registration'
});
break;
+ case 'start_upgrade_registration':
+ this.replaceState({
+ screen: "register",
+ upgradeUsername: MatrixClientPeg.get().getUserIdLocalpart(),
+ guestAccessToken: MatrixClientPeg.get().getAccessToken()
+ });
+ this.notifyNewScreen('register');
+ break;
case 'token_login':
if (this.state.logged_in) return;
@@ -382,10 +435,11 @@ module.exports = React.createClass({
},
onLoggedIn: function(credentials) {
- console.log("onLoggedIn => %s", credentials.userId);
+ credentials.guest = Boolean(credentials.guest);
+ console.log("onLoggedIn => %s (guest=%s)", credentials.userId, credentials.guest);
MatrixClientPeg.replaceUsingAccessToken(
credentials.homeserverUrl, credentials.identityServerUrl,
- credentials.userId, credentials.accessToken
+ credentials.userId, credentials.accessToken, credentials.guest
);
this.setState({
screen: undefined,
@@ -715,12 +769,20 @@ module.exports = React.createClass({
);
}
- } else if (this.state.logged_in) {
+ } else if (this.state.logged_in || (!this.state.logged_in && this._autoRegisterAsGuest)) {
var Spinner = sdk.getComponent('elements.Spinner');
+ var logoutLink;
+ if (this.state.logged_in) {
+ logoutLink = (
+
+ Logout
+
+ );
+ }
return (
);
} else if (this.state.screen == 'register') {
@@ -730,6 +792,9 @@ module.exports = React.createClass({
sessionId={this.state.register_session_id}
idSid={this.state.register_id_sid}
email={this.props.startingQueryParams.email}
+ username={this.state.upgradeUsername}
+ disableUsernameChanges={Boolean(this.state.upgradeUsername)}
+ guestAccessToken={this.state.guestAccessToken}
hsUrl={this.props.config.default_hs_url}
isUrl={this.props.config.default_is_url}
registrationUrl={this.props.registrationUrl}
diff --git a/src/components/structures/RoomView.js b/src/components/structures/RoomView.js
index 5cffefad26..c77bd99e38 100644
--- a/src/components/structures/RoomView.js
+++ b/src/components/structures/RoomView.js
@@ -97,6 +97,24 @@ module.exports = React.createClass({
this.forceUpdate();
}
});
+ // if this is an unknown room then we're in one of three states:
+ // - This is a room we can peek into (search engine) (we can /peek)
+ // - This is a room we can publicly join or were invited to. (we can /join)
+ // - This is a room we cannot join at all. (no action can help us)
+ // 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
+ // succeeds then great, show the preview (but we still may be able to /join!).
+ if (!this.state.room) {
+ console.log("Attempting to peek into room %s", this.props.roomId);
+ MatrixClientPeg.get().peekInRoom(this.props.roomId).done(function() {
+ // we don't need to do anything - JS SDK will emit Room events
+ // which will update the UI.
+ }, function(err) {
+ console.error("Failed to peek into room: %s", err);
+ });
+ }
+
+
},
componentWillUnmount: function() {
@@ -422,6 +440,12 @@ module.exports = React.createClass({
joining: false,
joinError: error
});
+ var msg = error.message ? error.message : JSON.stringify(error);
+ var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
+ Modal.createDialog(ErrorDialog, {
+ title: "Failed to join room",
+ description: msg
+ });
});
this.setState({
joining: true
@@ -712,7 +736,7 @@ module.exports = React.createClass({
return ret;
},
- uploadNewState: function(new_name, new_topic, new_join_rule, new_history_visibility, new_power_levels) {
+ uploadNewState: function(newVals) {
var old_name = this.state.room.name;
var old_topic = this.state.room.currentState.getStateEvents('m.room.topic', '');
@@ -738,46 +762,54 @@ module.exports = React.createClass({
var deferreds = [];
- if (old_name != new_name && new_name != undefined && new_name) {
+ if (old_name != newVals.name && newVals.name != undefined && newVals.name) {
deferreds.push(
- MatrixClientPeg.get().setRoomName(this.state.room.roomId, new_name)
+ MatrixClientPeg.get().setRoomName(this.state.room.roomId, newVals.name)
);
}
- if (old_topic != new_topic && new_topic != undefined) {
+ if (old_topic != newVals.topic && newVals.topic != undefined) {
deferreds.push(
- MatrixClientPeg.get().setRoomTopic(this.state.room.roomId, new_topic)
+ MatrixClientPeg.get().setRoomTopic(this.state.room.roomId, newVals.topic)
);
}
- if (old_join_rule != new_join_rule && new_join_rule != undefined) {
+ if (old_join_rule != newVals.join_rule && newVals.join_rule != undefined) {
deferreds.push(
MatrixClientPeg.get().sendStateEvent(
this.state.room.roomId, "m.room.join_rules", {
- join_rule: new_join_rule,
+ join_rule: newVals.join_rule,
}, ""
)
);
}
- if (old_history_visibility != new_history_visibility && new_history_visibility != undefined) {
+ if (old_history_visibility != newVals.history_visibility &&
+ newVals.history_visibility != undefined) {
deferreds.push(
MatrixClientPeg.get().sendStateEvent(
this.state.room.roomId, "m.room.history_visibility", {
- history_visibility: new_history_visibility,
+ history_visibility: newVals.history_visibility,
}, ""
)
);
}
- if (new_power_levels) {
+ if (newVals.power_levels) {
deferreds.push(
MatrixClientPeg.get().sendStateEvent(
- this.state.room.roomId, "m.room.power_levels", new_power_levels, ""
+ this.state.room.roomId, "m.room.power_levels", newVals.power_levels, ""
)
);
}
+ deferreds.push(
+ MatrixClientPeg.get().setGuestAccess(this.state.room.roomId, {
+ allowRead: newVals.guest_read,
+ allowJoin: newVals.guest_join
+ })
+ );
+
if (deferreds.length) {
var self = this;
q.all(deferreds).fail(function(err) {
@@ -862,19 +894,15 @@ module.exports = React.createClass({
uploadingRoomSettings: true,
});
- var new_name = this.refs.header.getRoomName();
- var new_topic = this.refs.room_settings.getTopic();
- var new_join_rule = this.refs.room_settings.getJoinRules();
- var new_history_visibility = this.refs.room_settings.getHistoryVisibility();
- var new_power_levels = this.refs.room_settings.getPowerLevels();
-
- this.uploadNewState(
- new_name,
- new_topic,
- new_join_rule,
- new_history_visibility,
- new_power_levels
- );
+ this.uploadNewState({
+ name: this.refs.header.getRoomName(),
+ topic: this.refs.room_settings.getTopic(),
+ join_rule: this.refs.room_settings.getJoinRules(),
+ history_visibility: this.refs.room_settings.getHistoryVisibility(),
+ power_levels: this.refs.room_settings.getPowerLevels(),
+ guest_join: this.refs.room_settings.canGuestsJoin(),
+ guest_read: this.refs.room_settings.canGuestsRead()
+ });
},
onCancelClick: function() {
diff --git a/src/components/structures/UserSettings.js b/src/components/structures/UserSettings.js
index c1550f9b6b..ddf4229170 100644
--- a/src/components/structures/UserSettings.js
+++ b/src/components/structures/UserSettings.js
@@ -135,6 +135,12 @@ module.exports = React.createClass({
});
},
+ onUpgradeClicked: function() {
+ dis.dispatch({
+ action: "start_upgrade_registration"
+ });
+ },
+
onLogoutPromptCancel: function() {
this.logoutModal.closeDialog();
},
@@ -164,6 +170,28 @@ module.exports = React.createClass({
this.state.avatarUrl ? MatrixClientPeg.get().mxcUrlToHttp(this.state.avatarUrl) : null
);
+ var accountJsx;
+
+ if (MatrixClientPeg.get().isGuest()) {
+ accountJsx = (
+
+ Upgrade (It's free!)
+
+ );
+ }
+ else {
+ accountJsx = (
+
+ );
+ }
+
return (
@@ -213,14 +241,7 @@ module.exports = React.createClass({
Account
-
+ {accountJsx}
diff --git a/src/components/structures/login/Registration.js b/src/components/structures/login/Registration.js
index 7f6e408fef..f89d65d740 100644
--- a/src/components/structures/login/Registration.js
+++ b/src/components/structures/login/Registration.js
@@ -19,7 +19,6 @@ limitations under the License.
var React = require('react');
var sdk = require('../../../index');
-var MatrixClientPeg = require('../../../MatrixClientPeg');
var dis = require('../../../dispatcher');
var Signup = require("../../../Signup");
var ServerConfig = require("../../views/login/ServerConfig");
@@ -40,6 +39,9 @@ module.exports = React.createClass({
hsUrl: React.PropTypes.string,
isUrl: React.PropTypes.string,
email: React.PropTypes.string,
+ username: React.PropTypes.string,
+ guestAccessToken: React.PropTypes.string,
+ disableUsernameChanges: React.PropTypes.bool,
// registration shouldn't know or care how login is done.
onLoginClick: React.PropTypes.func.isRequired
},
@@ -63,6 +65,7 @@ module.exports = React.createClass({
this.registerLogic.setSessionId(this.props.sessionId);
this.registerLogic.setRegistrationUrl(this.props.registrationUrl);
this.registerLogic.setIdSid(this.props.idSid);
+ this.registerLogic.setGuestAccessToken(this.props.guestAccessToken);
this.registerLogic.recheckState();
},
@@ -186,7 +189,9 @@ module.exports = React.createClass({
registerStep = (
diff --git a/src/components/views/login/RegistrationForm.js b/src/components/views/login/RegistrationForm.js
index bc461dd1bb..534464a4ae 100644
--- a/src/components/views/login/RegistrationForm.js
+++ b/src/components/views/login/RegistrationForm.js
@@ -30,6 +30,7 @@ module.exports = React.createClass({
defaultUsername: React.PropTypes.string,
showEmail: React.PropTypes.bool,
minPasswordLength: React.PropTypes.number,
+ disableUsernameChanges: React.PropTypes.bool,
onError: React.PropTypes.func,
onRegisterClick: React.PropTypes.func // onRegisterClick(Object) => ?Promise
},
@@ -109,7 +110,8 @@ module.exports = React.createClass({
{emailSection}
+ placeholder="User name" defaultValue={this.state.username}
+ disabled={this.props.disableUsernameChanges} />
diff --git a/src/components/views/rooms/RoomSettings.js b/src/components/views/rooms/RoomSettings.js
index 211ecbd71a..9e07385d65 100644
--- a/src/components/views/rooms/RoomSettings.js
+++ b/src/components/views/rooms/RoomSettings.js
@@ -31,6 +31,14 @@ module.exports = React.createClass({
};
},
+ canGuestsJoin: function() {
+ return this.refs.guests_join.checked;
+ },
+
+ canGuestsRead: function() {
+ return this.refs.guests_read.checked;
+ },
+
getTopic: function() {
return this.refs.topic.value;
},
@@ -83,6 +91,10 @@ module.exports = React.createClass({
if (history_visibility) history_visibility = history_visibility.getContent().history_visibility;
var power_levels = this.props.room.currentState.getStateEvents('m.room.power_levels', '');
+ var guest_access = this.props.room.currentState.getStateEvents('m.room.guest_access', '');
+ if (guest_access) {
+ guest_access = guest_access.getContent().guest_access;
+ }
var events_levels = power_levels.events || {};
@@ -154,6 +166,14 @@ module.exports = React.createClass({
+
+
Power levels