diff --git a/src/App.vue b/src/App.vue
index 2b1c114..d8e2ad3 100644
--- a/src/App.vue
+++ b/src/App.vue
@@ -63,7 +63,7 @@ export default {
})
.catch((error) => {
console.log("Error creating client", error);
- if (error.data.errcode ==='M_FORBIDDEN' && this.currentUser.is_guest) {
+ if (error.data && ((error.data.errcode ==='M_FORBIDDEN' && this.currentUser.is_guest) || error.data.errcode ==='M_USER_DEACTIVATED')) {
// Guest account and password don't work. We are in a strange state, probably because
// of server cleanup of accounts or similar. Wipe account and restart...
this.$store.commit("setUser", null);
diff --git a/src/assets/config.json b/src/assets/config.json
index 1d12445..abc13bb 100644
--- a/src/assets/config.json
+++ b/src/assets/config.json
@@ -8,6 +8,7 @@
"languageSupportEmail": "support@guardianproject.info",
"productLink": "letsconvene.im",
"defaultServer": "https://neo.keanu.im",
+ "identityServer_unset": "",
"rtl": false,
"analytics": [
{
diff --git a/src/assets/translations/en.json b/src/assets/translations/en.json
index b7ce9d2..f237d9b 100644
--- a/src/assets/translations/en.json
+++ b/src/assets/translations/en.json
@@ -148,7 +148,15 @@
"login": "Login",
"create_room": "Register & Create Room",
"or": "OR",
- "invalid_message": "Invalid username or password"
+ "invalid_message": "Invalid username or password",
+ "no_supported_flow": "The app can't login to the given server",
+ "terms": "The homeserver requires you to review and accept the following policies:",
+ "accept_terms": "Accept",
+ "email": "You need to verify you email address",
+ "send_verification": "Send verification email",
+ "sent_verification": "An email has been sent to {email}. Please use your regular email client to verify the address.",
+ "resend_verification": "Resend verification email",
+ "email_not_valid": "Email address not valid"
},
"profile": {
"title": "My Profile",
diff --git a/src/components/Chat.vue b/src/components/Chat.vue
index 3ec30c3..db1562e 100644
--- a/src/components/Chat.vue
+++ b/src/components/Chat.vue
@@ -322,6 +322,7 @@ export default {
data() {
return {
+ waitingForRoomObject: false,
events: [],
currentInput: "",
typingMembers: [],
@@ -419,7 +420,6 @@ export default {
computed: {
chatContainer() {
const container = this.$refs.chatContainer;
- console.log("GOT CONTAINER", container);
if (this.useVoiceMode) {
return container.$el;
}
@@ -547,6 +547,7 @@ export default {
this.$matrix.off("Room.timeline", this.onEvent);
this.$matrix.off("RoomMember.typing", this.onUserTyping);
+ this.waitingForRoomObject = false;
this.events = [];
this.timelineWindow = null;
this.typingMembers = [];
@@ -557,27 +558,32 @@ export default {
this.stopRRTimer();
this.lastRR = null;
- if (!this.room) {
- // Public room?
- if (this.roomId && this.roomId.startsWith("#")) {
- this.onRoomNotJoined();
- } else if (this.roomId) {
- this.onRoomNotJoined(); // Private room we are not joined to. What to do? We redirect to join
- // screen, maybe the user has an invite already?
- }
+ if (this.roomId) {
+ this.$matrix.isJoinedToRoom(this.roomId).then(joined => {
+ if (!joined) {
+ this.onRoomNotJoined();
+ } else {
+ if (this.room) {
+ this.onRoomJoined(this.readMarker);
+ } else {
+ this.waitingForRoomObject = true;
+ return; // no room, wait for it (we know we are joined so need to wait for sync to complete)
+ }
+ }
+ });
+ } else {
this.initialLoadDone = true;
return; // no room
}
-
- // Joined?
- if (this.room.hasMembershipState(this.currentUser.user_id, "join")) {
- // Yes, load everything
- this.onRoomJoined(this.readMarker);
- } else {
- this.onRoomNotJoined();
- }
},
},
+ room() {
+ // Were we waiting?
+ if (this.room && this.room.roomId == this.roomId && this.waitingForRoomObject) {
+ this.waitingForRoomObject = false;
+ this.onRoomJoined(this.readMarker);
+ }
+ },
showMessageOperations() {
if (this.showMessageOperations) {
this.$nextTick(() => {
diff --git a/src/components/CreateRoom.vue b/src/components/CreateRoom.vue
index ebf4288..c2324ad 100644
--- a/src/components/CreateRoom.vue
+++ b/src/components/CreateRoom.vue
@@ -39,8 +39,8 @@
background-color="white" v-on:keyup.enter="$refs.topic.focus()" :disabled="step > steps.INITIAL" autofocus
solo @update:error="updateErrorState">
{{ $t("new_room.room_topic") }}
-
+
@@ -71,7 +71,7 @@
{{ roomCreationErrorMsg }}
+ :disabled="isDisabled" ref="create">
{{ status }}
-
-
-
-
-
-
@@ -214,6 +131,7 @@
diff --git a/src/components/InteractiveAuth.vue b/src/components/InteractiveAuth.vue
new file mode 100644
index 0000000..3d50110
--- /dev/null
+++ b/src/components/InteractiveAuth.vue
@@ -0,0 +1,293 @@
+
+
+
+
+
+
+
+
+
+
+ {{ $t("login.email") }}
+
+
+ {{ $t("login.send_verification") }}
+
+
+
+
+
+
+
+
+ {{ $t("login.terms") }}
+
+
+
+
+
+
+
+
+
+
+ {{ $t("login.accept_terms") }}
+
+
+
+
+
+
+
+
+
+
{{ $t("login.sent_verification", { email: this.email }) }}
+
+
+
+ {{ $t("login.resend_verification") }}
+
+
+
+
+
+
+
+
+
+
diff --git a/src/components/Join.vue b/src/components/Join.vue
index e22f171..9d250ef 100644
--- a/src/components/Join.vue
+++ b/src/components/Join.vue
@@ -80,6 +80,8 @@
+
+
{{
roomId && roomId.startsWith("@") ? $t("join.enter_room_user") : $t("join.enter_room")
}}
@@ -133,6 +135,7 @@
diff --git a/src/plugins/utils.js b/src/plugins/utils.js
index ac0fa97..8d0eca5 100644
--- a/src/plugins/utils.js
+++ b/src/plugins/utils.js
@@ -268,7 +268,7 @@ class Util {
resolve(true);
})
.catch(err => {
- console.log("Image send error: ", err);
+ console.log("Send error: ", err);
if (err && err.name == "UnknownDeviceError") {
console.log("Unknown devices. Mark as known before retrying.");
var setAsKnownPromises = [];
@@ -290,7 +290,18 @@ class Util {
Promise.all(setAsKnownPromises)
.then(() => {
// All devices now marked as "known", try to resend
- matrixClient.resendEvent(err.event, matrixClient.getRoom(err.event.getRoomId()))
+ let event = err.event;
+ if (!event) {
+ // Seems event is no longer send in the UnknownDevices error...
+ const room = matrixClient.getRoom(roomId);
+ if (room) {
+ event = room.getLiveTimeline().getEvents().find(e => {
+ // Find the exact match (= object equality)
+ return e.error === err
+ });
+ }
+ }
+ matrixClient.resendEvent(event, matrixClient.getRoom(event.getRoomId()))
.then((result) => {
console.log("Message sent: ", result);
resolve(true);
diff --git a/src/services/matrix.service.js b/src/services/matrix.service.js
index 82e3039..1013b07 100644
--- a/src/services/matrix.service.js
+++ b/src/services/matrix.service.js
@@ -104,35 +104,59 @@ export default {
console.log("create crypto store");
return new LocalStorageCryptoStore(this.$store.getters.storage);
},
- login(user) {
- const tempMatrixClient = sdk.createClient({baseUrl: user.home_server});
+ login(user, registrationFlowHandler) {
+ const tempMatrixClient = sdk.createClient({baseUrl: user.home_server, idBaseUrl: this.$config.identityServer});
var promiseLogin;
const self = this;
if (user.access_token) {
// Logged in on "real" account
promiseLogin = Promise.resolve(user);
- } else if (user.is_guest && !user.user_id) {
+ } else if (user.is_guest && (!user.user_id || user.registration_session)) {
// Generate random username and password. We don't user REAL matrix
// guest accounts because 1. They are not allowed to post media, 2. They
// can not use avatars and 3. They can not seamlessly be upgraded to real accounts.
//
// Instead, we use an ILAG approach, Improved Landing as Guest.
- const user = util.randomUser(this.$config.userIdPrefix);
- const pass = util.randomPass();
+ const userId = user.registration_session ? user.user_id : util.randomUser(this.$config.userIdPrefix);
+ const pass = user.registration_session ? user.password : util.randomPass();
+
+ const extractAndSaveUser = (response) => {
+ var u = Object.assign({}, response);
+ u.home_server = tempMatrixClient.baseUrl; // Don't use deprecated field from response.
+ u.password = pass;
+ u.is_guest = true;
+ this.$store.commit("setUser", u);
+ return u;
+ };
+
promiseLogin = tempMatrixClient
- .register(user, pass, null, {
+ .register(userId, pass, user.registration_session || null, {
type: "m.login.dummy",
initial_device_display_name: this.$config.appName,
})
.then((response) => {
- console.log("Response", response);
- var u = Object.assign({}, response);
- u.home_server = tempMatrixClient.baseUrl; // Don't use deprecated field from response.
- u.password = pass;
- u.is_guest = true;
- this.$store.commit("setUser", u);
- return u;
+ return extractAndSaveUser(response);
+ })
+ .catch(error => {
+ if (registrationFlowHandler && error.httpStatus == 401 && error.data) {
+ const registrationSession = error.data.session;
+
+ // Store user, pass and session, so we can resume if network failure occurs etc.
+ //
+ var u = {};
+ u.user_id = userId;
+ u.home_server = tempMatrixClient.baseUrl; // Don't use deprecated field from response.
+ u.password = pass;
+ u.is_guest = true;
+ u.registration_session = registrationSession;
+ this.$store.commit("setUser", u);
+
+ return registrationFlowHandler(tempMatrixClient, error.data).then((response) => extractAndSaveUser(response));
+ } else {
+ console.error(error);
+ }
+ throw error;
});
} else {
var data = {
@@ -286,11 +310,11 @@ export default {
* Will use a real account, if we have one, otherwise will create
* a random account.
*/
- getLoginPromise() {
+ getLoginPromise(registrationFlowHandler) {
if (this.ready) {
return Promise.resolve(this.currentUser);
}
- return this.$store.dispatch("login", this.currentUser || new User(this.$config.defaultServer, "", "", true));
+ return this.$store.dispatch("login", { user: this.currentUser || new User(this.$config.defaultServer, "", "", true), registrationFlowHandler });
},
addMatrixClientListeners(client) {
@@ -432,10 +456,14 @@ export default {
}
});
Vue.set(this, "rooms", updatedRooms);
- const currentRoom = this.getRoom(this.$store.state.currentRoomId);
- if (this.currentRoom != currentRoom) {
- this.currentRoom = currentRoom;
- }
+
+ const resolvedId = (this.currentRoomId && this.currentRoomId.startsWith("#")) ? this.matrixClient.resolveRoomAlias(this.currentRoomId).then(r => r.room_id) : Promise.resolve(this.currentRoomId);
+ resolvedId.then(roomId => {
+ const currentRoom = this.getRoom(roomId);
+ if (this.currentRoom != currentRoom) {
+ this.currentRoom = currentRoom;
+ }
+ }).catch(ignorederror => {});
},
setCurrentRoomId(roomId) {
@@ -560,6 +588,28 @@ export default {
}
},
+ /**
+ * Returns true if the current user is joined to the given room.
+ * @param roomIdOrAlias
+ * @returns Promise - Whether the user is joined to the room or not
+ */
+ isJoinedToRoom(roomIdOrAlias) {
+ if (roomIdOrAlias && this.matrixClient) {
+ try {
+ const resolvedRoomId = roomIdOrAlias.startsWith("#") ? this.matrixClient.resolveRoomAlias(roomIdOrAlias).then(res => res.room_id) : Promise.resolve(roomIdOrAlias);
+ return resolvedRoomId.then(roomId => {
+ return this.matrixClient.getJoinedRooms().then(rooms => {
+ return rooms.joined_rooms.includes(roomId);
+ });
+ });
+ } catch (ignorederror) {
+ console.error(ignorederror);
+ return Promise.resolve(false);
+ }
+ }
+ return Promise.resolve(false);
+ },
+
isReadOnlyRoom(roomId) {
if (this.matrixClient && roomId) {
const room = this.getRoom(roomId);
diff --git a/src/store/index.js b/src/store/index.js
index 84e27f6..293c7b8 100644
--- a/src/store/index.js
+++ b/src/store/index.js
@@ -101,8 +101,8 @@ export default new Vuex.Store({
}
},
actions: {
- login({ commit }, user) {
- return this._vm.$matrix.login(user).then(
+ login({ commit }, { user, registrationFlowHandler }) {
+ return this._vm.$matrix.login(user, registrationFlowHandler).then(
user => {
commit('loginSuccess', user);
return Promise.resolve(user);