From dd70e4a576e9ec74c95afb3cdb5f3a1e4f5e7c4d Mon Sep 17 00:00:00 2001 From: N Pex Date: Fri, 1 Dec 2023 08:20:03 +0000 Subject: [PATCH] Improved BaseURL and DM link handling --- src/assets/config.json | 6 +- src/components/CreateRoom.vue | 2 +- src/components/GetLink.vue | 51 ++-- src/components/Login.vue | 54 ++-- src/models/user.js | 23 +- src/plugins/utils.js | 36 ++- src/router/index.js | 8 +- src/services/config.service.js | 30 +- src/services/matrix.service.js | 485 +++++++++++++++++---------------- 9 files changed, 373 insertions(+), 322 deletions(-) diff --git a/src/assets/config.json b/src/assets/config.json index 74739ee..f5cb5d7 100644 --- a/src/assets/config.json +++ b/src/assets/config.json @@ -7,7 +7,11 @@ }, "languageSupportEmail": "support@guardianproject.info", "productLink": "letsconvene.im", - "defaultServer": "https://neo.keanu.im", + "defaultBaseUrl": "https://neo.keanu.im", + "matrixDomainPartMapping": { + }, + "useFullyQualifiedDMLinks": true, + "defaultMatrixDomainPart": "neo.keanu.im", "identityServer_unset": "", "registrationToken_unset": "", "rtl": false, diff --git a/src/components/CreateRoom.vue b/src/components/CreateRoom.vue index 64e9818..7825678 100644 --- a/src/components/CreateRoom.vue +++ b/src/components/CreateRoom.vue @@ -463,7 +463,7 @@ export default { .getUniqueAliasForRoomName( this.$matrix.matrixClient, this.roomName, - this.$matrix.currentUserHomeServer + this.$matrix.currentUserMXDomain ) .then((alias) => { createRoomOptions.room_alias_name = alias; diff --git a/src/components/GetLink.vue b/src/components/GetLink.vue index 7a2175b..48574c3 100644 --- a/src/components/GetLink.vue +++ b/src/components/GetLink.vue @@ -53,7 +53,7 @@ import * as sdk from "matrix-js-sdk"; import logoMixin from "./logoMixin"; import InteractiveAuth from './InteractiveAuth.vue'; import CopyLink from "./CopyLink.vue" -import utils from "../plugins/utils"; +import util from "../plugins/utils"; export default { name: "GetLink", @@ -91,7 +91,7 @@ export default { methods: { defaultData() { return { - user: new User(this.$config.defaultServer, "", utils.randomPass()), + user: new User("", util.randomPass()), isValid: false, loading: false, message: "", @@ -128,10 +128,10 @@ export default { var user = Object.assign({}, this.user); - let prefix = userDisplayName.toLowerCase().replaceAll(" ", "-").replaceAll(utils.invalidUserIdChars(), ""); + let prefix = userDisplayName.toLowerCase().replaceAll(" ", "-").replaceAll(util.invalidUserIdChars(), ""); if (prefix.length == 0) { prefix = this.$config.userIdPrefix; - user.user_id = utils.randomUser(prefix); + user.user_id = util.randomUser(prefix); } else { // We first try with a username that is just a processed version of the display name. // If it is already taken, try again with random characters appended. @@ -139,8 +139,6 @@ export default { prefix = prefix + "-"; } - user.normalize(); - this.loading = true; this.loadLoginFlows().then(() => { @@ -160,7 +158,7 @@ export default { this.hasError = true; } else if (error.data && error.data.errcode === 'M_USER_IN_USE') { // Try again with (other/new) random chars appended - user.user_id = utils.randomUser(prefix); + user.user_id = util.randomUser(prefix); return this.$store.dispatch("createUser", { user, registrationFlowHandler: this.$refs.interactiveAuth.registrationFlowHandler }).then( (ignoreduser) => { this.$matrix.setUserDisplayName(userDisplayName); @@ -191,28 +189,29 @@ export default { }, loadLoginFlows() { var user = Object.assign({}, this.user); - user.normalize(); - const server = user.home_server || this.$config.defaultServer; - if (server !== this.currentLoginServer) { - this.currentLoginServer = server; - this.loadingLoginFlows = true; + util.getMatrixBaseUrl(user, this.$config) + .then((baseUrl) => { + if (baseUrl !== this.currentLoginServer) { + this.currentLoginServer = baseUrl; + this.loadingLoginFlows = true; - const matrixClient = sdk.createClient({ baseUrl: server }); - return matrixClient.loginFlows().then((response) => { - console.log("FLOWS", response.flows); - this.loginFlows = response.flows.filter(this.supportedLoginFlow); - this.loadingLoginFlows = false; - if (this.loginFlows.length == 0) { - this.message = this.$t('login.no_supported_flow') - this.hasError = true; + const matrixClient = sdk.createClient({ baseUrl: baseUrl }); + return matrixClient.loginFlows().then((response) => { + console.log("FLOWS", response.flows); + this.loginFlows = response.flows.filter(this.supportedLoginFlow); + this.loadingLoginFlows = false; + if (this.loginFlows.length == 0) { + this.message = this.$t('login.no_supported_flow') + this.hasError = true; + } else { + this.message = ""; + this.hasError = false; + } + }); } else { - this.message = ""; - this.hasError = false; + return Promise.resolve(); } - }); - } else { - return Promise.resolve(); - } + }) }, supportedLoginFlow(flow) { return ["m.login.password"].includes(flow.type); diff --git a/src/components/Login.vue b/src/components/Login.vue index b8056c8..94a6742 100644 --- a/src/components/Login.vue +++ b/src/components/Login.vue @@ -128,7 +128,7 @@ export default { }, data() { return { - user: new User(this.$config.defaultServer, "", ""), + user: new User("", ""), isValid: true, loading: false, message: "", @@ -184,7 +184,6 @@ export default { // Is it a full matrix user id? Modify a copy, so that the UI will still show the full ID. var user = Object.assign({}, this.user); - user.normalize(); this.loading = true; this.$store.dispatch("login", { user }).then( @@ -231,35 +230,36 @@ export default { }, onUsernameBlur() { var user = Object.assign({}, this.user); - user.normalize(); - const server = user.home_server || this.$config.defaultServer; - if (server !== this.currentLoginServer) { - - this.showPasswordField = false; + util.getMatrixBaseUrl(user, this.$config) + .then((baseUrl) => { + if (baseUrl !== this.currentLoginServer) { - this.currentLoginServer = server; - this.loadingLoginFlows = true; + this.showPasswordField = false; - const matrixClient = sdk.createClient({baseUrl: server}); - matrixClient.loginFlows().then((response) => { - console.log("FLOWS", response.flows); - this.loginFlows = response.flows.filter(this.supportedLoginFlow); - this.loadingLoginFlows = false; - if (this.loginFlows.length == 0) { - this.message = this.$t('login.no_supported_flow') - this.hasError = true; - } else { - this.message = ""; - this.hasError = false; - this.showPasswordField = this.loginFlows.some(f => f.type == "m.login.password"); - if (this.showPasswordField) { - this.$nextTick(() => { - this.$refs.password.focus(); - }); - } + this.currentLoginServer = baseUrl; + this.loadingLoginFlows = true; + + const matrixClient = sdk.createClient({ baseUrl: baseUrl }); + matrixClient.loginFlows().then((response) => { + console.log("FLOWS", response.flows); + this.loginFlows = response.flows.filter(this.supportedLoginFlow); + this.loadingLoginFlows = false; + if (this.loginFlows.length == 0) { + this.message = this.$t('login.no_supported_flow') + this.hasError = true; + } else { + this.message = ""; + this.hasError = false; + this.showPasswordField = this.loginFlows.some(f => f.type == "m.login.password"); + if (this.showPasswordField) { + this.$nextTick(() => { + this.$refs.password.focus(); + }); + } + } + }); } }); - } }, supportedLoginFlow(flow) { return ["m.login.password"].includes(flow.type); diff --git a/src/models/user.js b/src/models/user.js index 9b46687..a9f1870 100644 --- a/src/models/user.js +++ b/src/models/user.js @@ -1,19 +1,10 @@ export default class User { - constructor(home_server, user_id, password, is_guest) { - this.home_server = home_server; + constructor(user_id, password, is_guest) { this.user_id = user_id; this.password = password; this.is_guest = is_guest || false } - normalize = function () { - if (this.user_id.startsWith('@') && this.user_id.includes(':')) { - const parts = this.user_id.split(":"); - this.user_id = parts[0].substring(1); - this.home_server = "https://" + parts[1]; - } - }; - static localPart(user_id) { if (user_id && user_id.startsWith('@') && user_id.includes(':')) { const parts = user_id.split(":"); @@ -22,19 +13,11 @@ export default class User { return user_id; } - static serverName(user_id) { + static domainPart(user_id) { if (user_id && user_id.startsWith('@') && user_id.includes(':')) { const parts = user_id.split(":"); return parts[1]; } - return user_id; - } - - // Get the domain out of the home_server, so if that one is e.g. - // "https://yourdomain.com:8008" then we return "yourdomain.com" - static serverDomain(home_server) { - const parts = home_server.split("://"); - const serverAndPort = parts[parts.length - 1].split(/:|\//); - return serverAndPort[0]; + return undefined; } } \ No newline at end of file diff --git a/src/plugins/utils.js b/src/plugins/utils.js index 939fd16..f5dcf15 100644 --- a/src/plugins/utils.js +++ b/src/plugins/utils.js @@ -2,6 +2,8 @@ import axios from 'axios'; import * as ContentHelpers from "matrix-js-sdk/lib/content-helpers"; import dataUriToBuffer from "data-uri-to-buffer"; import ImageResize from "image-resize"; +import { AutoDiscovery } from 'matrix-js-sdk'; +import User from '../models/user'; export const STATE_EVENT_ROOM_DELETION_NOTICE = "im.keanu.room_deletion_notice"; export const STATE_EVENT_ROOM_DELETED = "im.keanu.room_deleted"; @@ -867,10 +869,10 @@ class Util { return undefined; } - getUniqueAliasForRoomName(matrixClient, roomName, homeServer, iterationCount) { + getUniqueAliasForRoomName(matrixClient, roomName, defaultMatrixDomainPart, iterationCount) { return new Promise((resolve, reject) => { var preferredAlias = roomName.replace(/\s/g, "").toLowerCase(); - var tryAlias = "#" + preferredAlias + ":" + homeServer; + var tryAlias = "#" + preferredAlias + ":" + defaultMatrixDomainPart; matrixClient.getRoomIdForAlias(tryAlias) .then(ignoredid => { // We got a response, this means the tryAlias already exists. @@ -885,7 +887,7 @@ class Util { roomName = roomName.substring(0, roomName.length - 5); } const randomChars = this.randomString(4, "abcdefghijklmnopqrstuvwxyz0123456789"); - resolve(this.getUniqueAliasForRoomName(matrixClient, roomName + "-" + randomChars, homeServer, (iterationCount || 0) + 1)) + resolve(this.getUniqueAliasForRoomName(matrixClient, roomName + "-" + randomChars, defaultMatrixDomainPart, (iterationCount || 0) + 1)) }) .catch(err => { if (err.errcode == 'M_NOT_FOUND') { @@ -921,6 +923,34 @@ class Util { console.log("Failed to fetch attachment: ", err); }); } + + getMatrixBaseUrl(user, config) { + if (user) { + const domain = User.domainPart(user.user_id); + if (domain) { + const endpoint = config.getMatrixDomainPartMapping(domain); + if (endpoint) { + console.log("Mapped to", endpoint); + return Promise.resolve(endpoint); + } + return AutoDiscovery.findClientConfig(domain) + .then((clientConfig) => { + const hs = clientConfig['m.homeserver']; + if (hs && !hs.error && hs.base_url) { + console.log("Use home server returned from well-known", hs.base_url); + return hs.base_url; + } + console.log("Fallback to default server"); + return config.defaultBaseUrl; + }) + .catch((err) => { + console.error("Failed well-known lookup", err); + return config.defaultBaseUrl; + }); + } + } + return Promise.resolve(config.defaultBaseUrl); + } } export default new Util(); diff --git a/src/router/index.js b/src/router/index.js index b70280b..903e4c2 100644 --- a/src/router/index.js +++ b/src/router/index.js @@ -130,9 +130,9 @@ router.beforeEach((to, from, next) => { if (roomId && !roomId.startsWith("@")) { // Not a full username. Assume local name on this server. return router.app.$config.promise.then((config) => { - const user = new User(config.defaultServer, roomId, ""); - user.normalize(); - roomId = "@" + roomId + ":" + User.serverDomain(user.home_server); + const domain = config.homeServer; + if (!domain) throw new Error("No domain part for user invite!"); + roomId = "@" + roomId + ":" + domain; router.app.$matrix.setCurrentRoomId(roomId); }).catch(err => console.error(err)).finally(() => next()); } else { @@ -186,7 +186,7 @@ router.getRoomLink = function (alias, roomId, roomName, mode) { router.getDMLink = function (user, config) { let userId = user.user_id; - if (user.home_server === config.defaultServer) { + if (User.domainPart(userId) === config.homeServer && !config.useFullyQualifiedDMLinks) { // Using default server, don't include it in the link userId = User.localPart(user.user_id); } diff --git a/src/services/config.service.js b/src/services/config.service.js index 08d5ddc..56dd7aa 100644 --- a/src/services/config.service.js +++ b/src/services/config.service.js @@ -15,8 +15,16 @@ export default { Vue.set(config, key, json[key]); } // If default server is not set, default to current server address - if (!json.defaultServer) { - Vue.set(config, "defaultServer", defaultServerFromLocation); + if (!json.defaultBaseUrl) { + if (json.defaultServer) { + // TODO - Only to migrate old values (defaultServer was renamed defaultBaseUrl), can be removed later... + Vue.set(config, "defaultBaseUrl", defaultServerFromLocation); + } else { + Vue.set(config, "defaultBaseUrl", json.defaultServer); + } + } + if (json.useFullyQualifiedDMLinks == undefined) { + Vue.set(config, "useFullyQualifiedDMLinks", true); // Default to true } Vue.set(config, "loaded", true); @@ -26,6 +34,24 @@ export default { } return config; }); + + /** + * If there is an explicit mapping for this MX domain in the config file, return the endpoint URL that it maps to. + * @param {*} domain + * @returns + */ + config.getMatrixDomainPartMapping = (domain) => { + console.log("Get domain endpoint mapping for", domain); + if (config.matrixDomainPartMapping && config.matrixDomainPartMapping[domain]) { + const mapping = config.matrixDomainPartMapping[domain]; + if (Array.isArray(mapping)) { + return mapping[0]; //TODO - Use the first one for now, but maybe rotate somehow? + } + return mapping; + } + return undefined; + } + Vue.prototype.$config = config; } } diff --git a/src/services/matrix.service.js b/src/services/matrix.service.js index f342881..e163a35 100644 --- a/src/services/matrix.service.js +++ b/src/services/matrix.service.js @@ -68,8 +68,8 @@ export default { return null; }, - currentUserHomeServer() { - return this.$config.homeServer ? this.$config.homeServer : User.serverName(this.currentUserId); + currentUserMXDomain() { + return User.domainPart(this.currentUserId) || this.$config.defaultMatrixDomainPart; }, currentRoomId() { @@ -92,7 +92,7 @@ export default { return this.rooms.filter((room) => { return room.selfMembership === "join" || room.selfMembership === "invite"; }); - } + }, }, watch: { @@ -110,8 +110,8 @@ export default { } else { this.currentRoomIsReadOnlyForUser = false; } - } - } + }, + }, }, methods: { @@ -120,83 +120,88 @@ export default { return new LocalStorageCryptoStore(this.$store.getters.storage); }, login(user, registrationFlowHandler, createUser = false) { - 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 (createUser || (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 userId = (createUser || user.registration_session) ? user.user_id : util.randomUser(this.$config.userIdPrefix); - const pass = (createUser || 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(userId, pass, user.registration_session || null, { - type: "m.login.dummy", - initial_device_display_name: this.$config.appName, - }) - .then((response) => { - 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 = { - user: User.localPart(user.user_id), - password: user.password, - type: "m.login.password", - initial_device_display_name: this.$config.appName, - }; - if (user.device_id) { - data.device_id = user.device_id; - } - promiseLogin = tempMatrixClient.login("m.login.password", data).then((response) => { - var u = Object.assign({}, response); - if (user.is_guest) { - // Copy over needed properties - u = Object.assign(user, response); - } - u.home_server = tempMatrixClient.baseUrl; // Don't use deprecated field from response. - this.$store.commit("setUser", u); - return u; + return util.getMatrixBaseUrl(user, this.$config).then((baseUrl) => { + const tempMatrixClient = sdk.createClient({ + baseUrl: baseUrl, + idBaseUrl: this.$config.identityServer, }); - } + var promiseLogin; - return promiseLogin.then((user) => { - return self.getMatrixClient(user); + const self = this; + if (user.access_token) { + // Logged in on "real" account + promiseLogin = Promise.resolve(user); + } else if (createUser || (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 userId = + createUser || user.registration_session ? user.user_id : util.randomUser(this.$config.userIdPrefix); + const pass = createUser || user.registration_session ? user.password : util.randomPass(); + + const extractAndSaveUser = (response) => { + var u = Object.assign({}, response); + u.password = pass; + u.is_guest = true; + this.$store.commit("setUser", u); + return u; + }; + + promiseLogin = tempMatrixClient + .register(userId, pass, user.registration_session || null, { + type: "m.login.dummy", + initial_device_display_name: this.$config.appName, + }) + .then((response) => { + 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.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 = { + user: User.localPart(user.user_id), + password: user.password, + type: "m.login.password", + initial_device_display_name: this.$config.appName, + }; + if (user.device_id) { + data.device_id = user.device_id; + } + promiseLogin = tempMatrixClient.login("m.login.password", data).then((response) => { + var u = Object.assign({}, response); + if (user.is_guest) { + // Copy over needed properties + u = Object.assign(user, response); + } + this.$store.commit("setUser", u); + return u; + }); + } + + return promiseLogin.then((user) => { + return self.getMatrixClient(user); + }); }); }, @@ -271,52 +276,54 @@ export default { const matrixStore = new sdk.MemoryStore(this.$store.getters.storage); - var opts = { - baseUrl: user.home_server, - userId: user.user_id, - store: matrixStore, - deviceId: user.device_id, - accessToken: user.access_token, - timelineSupport: true, - unstableClientRelationAggregation: true, - //useAuthorizationHeader: true - }; - this.matrixClient = sdk.createClient(opts); - // if (user.is_guest) { - // this.matrixClient.setGuest(true); - // } - return this.matrixClient - .initCrypto() - .then(() => { - console.log("Crypto initialized"); + return util.getMatrixBaseUrl(user, this.$config).then((baseUrl) => { + var opts = { + baseUrl: baseUrl, + userId: user.user_id, + store: matrixStore, + deviceId: user.device_id, + accessToken: user.access_token, + timelineSupport: true, + unstableClientRelationAggregation: true, + //useAuthorizationHeader: true + }; + this.matrixClient = sdk.createClient(opts); + // if (user.is_guest) { + // this.matrixClient.setGuest(true); + // } + return this.matrixClient + .initCrypto() + .then(() => { + console.log("Crypto initialized"); - this.addMatrixClientListeners(this.matrixClient); + this.addMatrixClientListeners(this.matrixClient); - this.matrixClient.startClient(); - return this.matrixClient; - }) - .then((matrixClient) => { - if (matrixClient.isInitialSyncComplete()) { - console.log("Initial sync done already!"); - return matrixClient; - } else { - return new Promise((resolve, reject) => { - matrixClient.once("sync", function (state, ignoredprevState, ignoredres) { - console.log(state); // state will be 'PREPARED' when the client is ready to use - if (state == "PREPARED") { - resolve(matrixClient); - } else if (state == "ERROR") { - reject("Error syncing"); - } + this.matrixClient.startClient(); + return this.matrixClient; + }) + .then((matrixClient) => { + if (matrixClient.isInitialSyncComplete()) { + console.log("Initial sync done already!"); + return matrixClient; + } else { + return new Promise((resolve, reject) => { + matrixClient.once("sync", function (state, ignoredprevState, ignoredres) { + console.log(state); // state will be 'PREPARED' when the client is ready to use + if (state == "PREPARED") { + resolve(matrixClient); + } else if (state == "ERROR") { + reject("Error syncing"); + } + }); }); - }); - } - }) - .then(() => { - // Ready to use! Start by loading rooms. - this.initClient(); - return user; - }); + } + }) + .then(() => { + // Ready to use! Start by loading rooms. + this.initClient(); + return user; + }); + }); }, /** @@ -329,7 +336,10 @@ export default { if (this.ready) { return Promise.resolve(this.currentUser); } - return this.$store.dispatch("login", { user: this.currentUser || new User(this.$config.defaultServer, "", "", true), registrationFlowHandler }); + return this.$store.dispatch("login", { + user: this.currentUser || new User("", "", true), + registrationFlowHandler, + }); }, addMatrixClientListeners(client) { @@ -376,13 +386,13 @@ export default { break; case "m.room.power_levels": - { - if (this.currentRoom && event.getRoomId() == this.currentRoom.roomId) { - this.currentRoomIsReadOnlyForUser = this.isReadOnlyRoomForUser(event.getRoomId(), this.currentUserId); - } + { + if (this.currentRoom && event.getRoomId() == this.currentRoom.roomId) { + this.currentRoomIsReadOnlyForUser = this.isReadOnlyRoomForUser(event.getRoomId(), this.currentUserId); } - break; - + } + break; + case STATE_EVENT_ROOM_DELETED: { const room = this.matrixClient.getRoom(event.getRoomId()); @@ -394,7 +404,7 @@ export default { if (event.getSender() !== this.currentUserId) { this.leaveRoomAndNavigate(room.roomId).then(() => { this.matrixClient.store.removeRoom(room.roomId); - }) + }); } } } @@ -406,13 +416,16 @@ export default { onRoom(room) { if (room.selfMembership === "invite") { - this.matrixClient.getRoomTags(room.roomId).then(reply => { - if (Object.keys(reply.tags).includes("m.server_notice")) { - Vue.set(room, "isServiceNoticeRoom", true); - } - }).catch((error => { - console.error(error); - })) + this.matrixClient + .getRoomTags(room.roomId) + .then((reply) => { + if (Object.keys(reply.tags).includes("m.server_notice")) { + Vue.set(room, "isServiceNoticeRoom", true); + } + }) + .catch((error) => { + console.error(error); + }); } this.reloadRooms(); this.updateNotificationCount(); @@ -476,14 +489,19 @@ export default { } }); Vue.set(this, "rooms", updatedRooms); - - 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 => {}); + + 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) { @@ -562,26 +580,18 @@ export default { /** * Leave the room, and if this is the last room we are in, navigate to the "goodbye" page. * Otherwise, navigate to home. - * @param roomId + * @param roomId */ leaveRoomAndNavigate(roomId) { const joinedRooms = this.joinedRooms; - const isLastRoomWeAreJoinedTo = ( - joinedRooms && - joinedRooms.length == 1 && - joinedRooms[0].roomId == roomId - ); - return this.leaveRoom(roomId) - .then(() => { - if (isLastRoomWeAreJoinedTo) { - this.$navigation.push({ name: "Goodbye" }, -1); - } else { - this.$navigation.push( - { name: "Home", params: { roomId: null } }, - -1 - ); - } - }) + const isLastRoomWeAreJoinedTo = joinedRooms && joinedRooms.length == 1 && joinedRooms[0].roomId == roomId; + return this.leaveRoom(roomId).then(() => { + if (isLastRoomWeAreJoinedTo) { + this.$navigation.push({ name: "Goodbye" }, -1); + } else { + this.$navigation.push({ name: "Home", params: { roomId: null } }, -1); + } + }); }, kickUser(roomId, userId) { @@ -635,18 +645,20 @@ export default { /** * Returns true if the current user is joined to the given room. - * @param roomIdOrAlias + * @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 => { + 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); @@ -661,7 +673,7 @@ export default { if (room && room.currentState) { const powerLevelEvent = room.currentState.getStateEvents("m.room.power_levels", ""); if (powerLevelEvent) { - return powerLevelEvent.getContent().events_default > 0 + return powerLevelEvent.getContent().events_default > 0; } } } @@ -672,7 +684,7 @@ export default { if (this.matrixClient && roomId && userId) { const room = this.getRoom(roomId); if (room && room.currentState) { - return !room.currentState.maySendMessage(userId) + return !room.currentState.maySendMessage(userId); } } return false; @@ -686,11 +698,7 @@ export default { if (powerLevelEvent) { let content = powerLevelEvent.getContent(); content.events_default = readOnly ? 50 : 0; - this.matrixClient.sendStateEvent( - room.roomId, - "m.room.power_levels", - content - ); + this.matrixClient.sendStateEvent(room.roomId, "m.room.power_levels", content); } } } @@ -759,11 +767,7 @@ export default { }); }) .then(() => { - return this.matrixClient.sendStateEvent( - roomId, - STATE_EVENT_ROOM_DELETED, - { status: "deleted" } - ); + return this.matrixClient.sendStateEvent(roomId, STATE_EVENT_ROOM_DELETED, { status: "deleted" }); }) .then(() => { //console.log("Purge: create timeline"); @@ -849,11 +853,9 @@ export default { return kickFirstMember(allMembers); }) .then(() => { - return withRetry(() => this.matrixClient.sendStateEvent( - roomId, - STATE_EVENT_ROOM_DELETED, - { status: "deleted" } - )); + return withRetry(() => + this.matrixClient.sendStateEvent(roomId, STATE_EVENT_ROOM_DELETED, { status: "deleted" }) + ); }) .then(() => { statusCallback(null); @@ -970,7 +972,7 @@ export default { return false; }, - /** + /** * Return true if this room is a direct room with one other user. NOTE: this currently * only checks number of members, not any is_direct flag. * @param { } room @@ -979,7 +981,7 @@ export default { // TODO - Use the is_direct accountData flag (m.direct). WE (as the client) // apprently need to set this... if (room && room.getJoinRule() == "invite" && room.getMembers().length == 2) { - return true; + return true; } return false; }, @@ -998,7 +1000,10 @@ export default { setUserDisplayName(name) { if (this.matrixClient) { - return this.matrixClient.setDisplayName(name || this.user.userId).then(() => this.userDisplayName = name).catch(err => console.err("Failed to set display name", err)); + return this.matrixClient + .setDisplayName(name || this.user.userId) + .then(() => (this.userDisplayName = name)) + .catch((err) => console.err("Failed to set display name", err)); } else { return Promise.reject("No matrix client"); } @@ -1043,71 +1048,75 @@ export default { * @returns A MatrixClient that can be used for public queries */ getPublicQueryMatrixClient() { - var clientPromise; if (this.matrixClient) { - clientPromise = this.getMatrixClient().then(() => { + return this.getMatrixClient().then(() => { return this.matrixClient; }); } else { - const tempMatrixClient = sdk.createClient({baseUrl: this.$config.defaultServer}); var tempUserString = this.$store.state.tempuser; var tempUser = null; if (tempUserString) { tempUser = JSON.parse(tempUserString); } + return util.getMatrixBaseUrl(tempUser, this.$config).then((baseUrl) => { + const tempMatrixClient = sdk.createClient({ baseUrl: baseUrl }); - // Need to create an account? - // - if (tempUser) { - clientPromise = Promise.resolve(tempUser); - } else { - const user = util.randomUser(this.$config.userIdPrefix); - const pass = util.randomPass(); - clientPromise = tempMatrixClient - .register(user, pass, null, { - type: "m.login.dummy", - initial_device_display_name: this.$config.appName, - }) - .then((response) => { - console.log("Response", response); - response.password = pass; - response.is_guest = true; - this.$store.commit("setTempUser", response); - return response; - }); - } + var clientPromise; - // Get an access token - clientPromise = clientPromise.then((user) => { - var data = { - user: User.localPart(user.user_id), - password: user.password, - type: "m.login.password", - initial_device_display_name: this.$config.appName, - }; - if (user.device_id) { - data.device_id = user.device_id; + // Need to create an account? + // + if (tempUser) { + clientPromise = Promise.resolve(tempUser); + } else { + const user = util.randomUser(this.$config.userIdPrefix); + const pass = util.randomPass(); + clientPromise = tempMatrixClient + .register(user, pass, null, { + type: "m.login.dummy", + initial_device_display_name: this.$config.appName, + }) + .then((response) => { + console.log("Response", response); + response.password = pass; + response.is_guest = true; + this.$store.commit("setTempUser", response); + return response; + }); } - return tempMatrixClient.login("m.login.password", data); - }); - // Then login - // - // Create a slimmed down client, without crypto. This one is - // Only used to get public room info from. - clientPromise = clientPromise.then((user) => { - var opts = { - baseUrl: this.$config.defaultServer, - userId: user.user_id, - accessToken: user.access_token, - timelineSupport: false, - }; - var matrixClient = sdk.createClient(opts); - matrixClient.startClient(); - return matrixClient; + // Get an access token + clientPromise = clientPromise.then((user) => { + var data = { + user: User.localPart(user.user_id), + password: user.password, + type: "m.login.password", + initial_device_display_name: this.$config.appName, + }; + if (user.device_id) { + data.device_id = user.device_id; + } + return tempMatrixClient.login("m.login.password", data); + }); + + // Then login + // + // Create a slimmed down client, without crypto. This one is + // Only used to get public room info from. + clientPromise = clientPromise.then((user) => { + var opts = { + baseUrl: baseUrl, + userId: user.user_id, + accessToken: user.access_token, + timelineSupport: false, + }; + var matrixClient = sdk.createClient(opts); + matrixClient.startClient(); + return matrixClient; + }); + + return clientPromise; }); } - return clientPromise; }, getPublicUserInfo(userId) {