Merge branch '575-add-config-option-to-use-short-dm-links-or-not' into 'dev'

Improved BaseURL and DM link handling

See merge request keanuapp/keanuapp-weblite!271
This commit is contained in:
N Pex 2023-12-01 08:20:03 +00:00
commit 1ddedac0ef
9 changed files with 373 additions and 322 deletions

View file

@ -7,7 +7,11 @@
}, },
"languageSupportEmail": "support@guardianproject.info", "languageSupportEmail": "support@guardianproject.info",
"productLink": "letsconvene.im", "productLink": "letsconvene.im",
"defaultServer": "https://neo.keanu.im", "defaultBaseUrl": "https://neo.keanu.im",
"matrixDomainPartMapping": {
},
"useFullyQualifiedDMLinks": true,
"defaultMatrixDomainPart": "neo.keanu.im",
"identityServer_unset": "", "identityServer_unset": "",
"registrationToken_unset": "", "registrationToken_unset": "",
"rtl": false, "rtl": false,

View file

@ -463,7 +463,7 @@ export default {
.getUniqueAliasForRoomName( .getUniqueAliasForRoomName(
this.$matrix.matrixClient, this.$matrix.matrixClient,
this.roomName, this.roomName,
this.$matrix.currentUserHomeServer this.$matrix.currentUserMXDomain
) )
.then((alias) => { .then((alias) => {
createRoomOptions.room_alias_name = alias; createRoomOptions.room_alias_name = alias;

View file

@ -53,7 +53,7 @@ import * as sdk from "matrix-js-sdk";
import logoMixin from "./logoMixin"; import logoMixin from "./logoMixin";
import InteractiveAuth from './InteractiveAuth.vue'; import InteractiveAuth from './InteractiveAuth.vue';
import CopyLink from "./CopyLink.vue" import CopyLink from "./CopyLink.vue"
import utils from "../plugins/utils"; import util from "../plugins/utils";
export default { export default {
name: "GetLink", name: "GetLink",
@ -91,7 +91,7 @@ export default {
methods: { methods: {
defaultData() { defaultData() {
return { return {
user: new User(this.$config.defaultServer, "", utils.randomPass()), user: new User("", util.randomPass()),
isValid: false, isValid: false,
loading: false, loading: false,
message: "", message: "",
@ -128,10 +128,10 @@ export default {
var user = Object.assign({}, this.user); 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) { if (prefix.length == 0) {
prefix = this.$config.userIdPrefix; prefix = this.$config.userIdPrefix;
user.user_id = utils.randomUser(prefix); user.user_id = util.randomUser(prefix);
} else { } else {
// We first try with a username that is just a processed version of the display name. // 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. // If it is already taken, try again with random characters appended.
@ -139,8 +139,6 @@ export default {
prefix = prefix + "-"; prefix = prefix + "-";
} }
user.normalize();
this.loading = true; this.loading = true;
this.loadLoginFlows().then(() => { this.loadLoginFlows().then(() => {
@ -160,7 +158,7 @@ export default {
this.hasError = true; this.hasError = true;
} else if (error.data && error.data.errcode === 'M_USER_IN_USE') { } else if (error.data && error.data.errcode === 'M_USER_IN_USE') {
// Try again with (other/new) random chars appended // 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( return this.$store.dispatch("createUser", { user, registrationFlowHandler: this.$refs.interactiveAuth.registrationFlowHandler }).then(
(ignoreduser) => { (ignoreduser) => {
this.$matrix.setUserDisplayName(userDisplayName); this.$matrix.setUserDisplayName(userDisplayName);
@ -191,28 +189,29 @@ export default {
}, },
loadLoginFlows() { loadLoginFlows() {
var user = Object.assign({}, this.user); var user = Object.assign({}, this.user);
user.normalize(); util.getMatrixBaseUrl(user, this.$config)
const server = user.home_server || this.$config.defaultServer; .then((baseUrl) => {
if (server !== this.currentLoginServer) { if (baseUrl !== this.currentLoginServer) {
this.currentLoginServer = server; this.currentLoginServer = baseUrl;
this.loadingLoginFlows = true; this.loadingLoginFlows = true;
const matrixClient = sdk.createClient({ baseUrl: server }); const matrixClient = sdk.createClient({ baseUrl: baseUrl });
return matrixClient.loginFlows().then((response) => { return matrixClient.loginFlows().then((response) => {
console.log("FLOWS", response.flows); console.log("FLOWS", response.flows);
this.loginFlows = response.flows.filter(this.supportedLoginFlow); this.loginFlows = response.flows.filter(this.supportedLoginFlow);
this.loadingLoginFlows = false; this.loadingLoginFlows = false;
if (this.loginFlows.length == 0) { if (this.loginFlows.length == 0) {
this.message = this.$t('login.no_supported_flow') this.message = this.$t('login.no_supported_flow')
this.hasError = true; this.hasError = true;
} else {
this.message = "";
this.hasError = false;
}
});
} else { } else {
this.message = ""; return Promise.resolve();
this.hasError = false;
} }
}); })
} else {
return Promise.resolve();
}
}, },
supportedLoginFlow(flow) { supportedLoginFlow(flow) {
return ["m.login.password"].includes(flow.type); return ["m.login.password"].includes(flow.type);

View file

@ -128,7 +128,7 @@ export default {
}, },
data() { data() {
return { return {
user: new User(this.$config.defaultServer, "", ""), user: new User("", ""),
isValid: true, isValid: true,
loading: false, loading: false,
message: "", 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. // 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); var user = Object.assign({}, this.user);
user.normalize();
this.loading = true; this.loading = true;
this.$store.dispatch("login", { user }).then( this.$store.dispatch("login", { user }).then(
@ -231,35 +230,36 @@ export default {
}, },
onUsernameBlur() { onUsernameBlur() {
var user = Object.assign({}, this.user); var user = Object.assign({}, this.user);
user.normalize(); util.getMatrixBaseUrl(user, this.$config)
const server = user.home_server || this.$config.defaultServer; .then((baseUrl) => {
if (server !== this.currentLoginServer) { if (baseUrl !== this.currentLoginServer) {
this.showPasswordField = false; this.showPasswordField = false;
this.currentLoginServer = server; this.currentLoginServer = baseUrl;
this.loadingLoginFlows = true; this.loadingLoginFlows = true;
const matrixClient = sdk.createClient({baseUrl: server}); const matrixClient = sdk.createClient({ baseUrl: baseUrl });
matrixClient.loginFlows().then((response) => { matrixClient.loginFlows().then((response) => {
console.log("FLOWS", response.flows); console.log("FLOWS", response.flows);
this.loginFlows = response.flows.filter(this.supportedLoginFlow); this.loginFlows = response.flows.filter(this.supportedLoginFlow);
this.loadingLoginFlows = false; this.loadingLoginFlows = false;
if (this.loginFlows.length == 0) { if (this.loginFlows.length == 0) {
this.message = this.$t('login.no_supported_flow') this.message = this.$t('login.no_supported_flow')
this.hasError = true; this.hasError = true;
} else { } else {
this.message = ""; this.message = "";
this.hasError = false; this.hasError = false;
this.showPasswordField = this.loginFlows.some(f => f.type == "m.login.password"); this.showPasswordField = this.loginFlows.some(f => f.type == "m.login.password");
if (this.showPasswordField) { if (this.showPasswordField) {
this.$nextTick(() => { this.$nextTick(() => {
this.$refs.password.focus(); this.$refs.password.focus();
}); });
} }
}
});
} }
}); });
}
}, },
supportedLoginFlow(flow) { supportedLoginFlow(flow) {
return ["m.login.password"].includes(flow.type); return ["m.login.password"].includes(flow.type);

View file

@ -1,19 +1,10 @@
export default class User { export default class User {
constructor(home_server, user_id, password, is_guest) { constructor(user_id, password, is_guest) {
this.home_server = home_server;
this.user_id = user_id; this.user_id = user_id;
this.password = password; this.password = password;
this.is_guest = is_guest || false 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) { static localPart(user_id) {
if (user_id && user_id.startsWith('@') && user_id.includes(':')) { if (user_id && user_id.startsWith('@') && user_id.includes(':')) {
const parts = user_id.split(":"); const parts = user_id.split(":");
@ -22,19 +13,11 @@ export default class User {
return user_id; return user_id;
} }
static serverName(user_id) { static domainPart(user_id) {
if (user_id && user_id.startsWith('@') && user_id.includes(':')) { if (user_id && user_id.startsWith('@') && user_id.includes(':')) {
const parts = user_id.split(":"); const parts = user_id.split(":");
return parts[1]; return parts[1];
} }
return user_id; return undefined;
}
// 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];
} }
} }

View file

@ -2,6 +2,8 @@ import axios from 'axios';
import * as ContentHelpers from "matrix-js-sdk/lib/content-helpers"; import * as ContentHelpers from "matrix-js-sdk/lib/content-helpers";
import dataUriToBuffer from "data-uri-to-buffer"; import dataUriToBuffer from "data-uri-to-buffer";
import ImageResize from "image-resize"; 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_DELETION_NOTICE = "im.keanu.room_deletion_notice";
export const STATE_EVENT_ROOM_DELETED = "im.keanu.room_deleted"; export const STATE_EVENT_ROOM_DELETED = "im.keanu.room_deleted";
@ -867,10 +869,10 @@ class Util {
return undefined; return undefined;
} }
getUniqueAliasForRoomName(matrixClient, roomName, homeServer, iterationCount) { getUniqueAliasForRoomName(matrixClient, roomName, defaultMatrixDomainPart, iterationCount) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
var preferredAlias = roomName.replace(/\s/g, "").toLowerCase(); var preferredAlias = roomName.replace(/\s/g, "").toLowerCase();
var tryAlias = "#" + preferredAlias + ":" + homeServer; var tryAlias = "#" + preferredAlias + ":" + defaultMatrixDomainPart;
matrixClient.getRoomIdForAlias(tryAlias) matrixClient.getRoomIdForAlias(tryAlias)
.then(ignoredid => { .then(ignoredid => {
// We got a response, this means the tryAlias already exists. // We got a response, this means the tryAlias already exists.
@ -885,7 +887,7 @@ class Util {
roomName = roomName.substring(0, roomName.length - 5); roomName = roomName.substring(0, roomName.length - 5);
} }
const randomChars = this.randomString(4, "abcdefghijklmnopqrstuvwxyz0123456789"); 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 => { .catch(err => {
if (err.errcode == 'M_NOT_FOUND') { if (err.errcode == 'M_NOT_FOUND') {
@ -921,6 +923,34 @@ class Util {
console.log("Failed to fetch attachment: ", err); 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(); export default new Util();

View file

@ -130,9 +130,9 @@ router.beforeEach((to, from, next) => {
if (roomId && !roomId.startsWith("@")) { if (roomId && !roomId.startsWith("@")) {
// Not a full username. Assume local name on this server. // Not a full username. Assume local name on this server.
return router.app.$config.promise.then((config) => { return router.app.$config.promise.then((config) => {
const user = new User(config.defaultServer, roomId, ""); const domain = config.homeServer;
user.normalize(); if (!domain) throw new Error("No domain part for user invite!");
roomId = "@" + roomId + ":" + User.serverDomain(user.home_server); roomId = "@" + roomId + ":" + domain;
router.app.$matrix.setCurrentRoomId(roomId); router.app.$matrix.setCurrentRoomId(roomId);
}).catch(err => console.error(err)).finally(() => next()); }).catch(err => console.error(err)).finally(() => next());
} else { } else {
@ -186,7 +186,7 @@ router.getRoomLink = function (alias, roomId, roomName, mode) {
router.getDMLink = function (user, config) { router.getDMLink = function (user, config) {
let userId = user.user_id; 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 // Using default server, don't include it in the link
userId = User.localPart(user.user_id); userId = User.localPart(user.user_id);
} }

View file

@ -15,8 +15,16 @@ export default {
Vue.set(config, key, json[key]); Vue.set(config, key, json[key]);
} }
// If default server is not set, default to current server address // If default server is not set, default to current server address
if (!json.defaultServer) { if (!json.defaultBaseUrl) {
Vue.set(config, "defaultServer", defaultServerFromLocation); 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); Vue.set(config, "loaded", true);
@ -26,6 +34,24 @@ export default {
} }
return config; 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; Vue.prototype.$config = config;
} }
} }

View file

@ -68,8 +68,8 @@ export default {
return null; return null;
}, },
currentUserHomeServer() { currentUserMXDomain() {
return this.$config.homeServer ? this.$config.homeServer : User.serverName(this.currentUserId); return User.domainPart(this.currentUserId) || this.$config.defaultMatrixDomainPart;
}, },
currentRoomId() { currentRoomId() {
@ -92,7 +92,7 @@ export default {
return this.rooms.filter((room) => { return this.rooms.filter((room) => {
return room.selfMembership === "join" || room.selfMembership === "invite"; return room.selfMembership === "join" || room.selfMembership === "invite";
}); });
} },
}, },
watch: { watch: {
@ -110,8 +110,8 @@ export default {
} else { } else {
this.currentRoomIsReadOnlyForUser = false; this.currentRoomIsReadOnlyForUser = false;
} }
} },
} },
}, },
methods: { methods: {
@ -120,83 +120,88 @@ export default {
return new LocalStorageCryptoStore(this.$store.getters.storage); return new LocalStorageCryptoStore(this.$store.getters.storage);
}, },
login(user, registrationFlowHandler, createUser = false) { login(user, registrationFlowHandler, createUser = false) {
const tempMatrixClient = sdk.createClient({baseUrl: user.home_server, idBaseUrl: this.$config.identityServer}); return util.getMatrixBaseUrl(user, this.$config).then((baseUrl) => {
var promiseLogin; const tempMatrixClient = sdk.createClient({
baseUrl: baseUrl,
const self = this; idBaseUrl: this.$config.identityServer,
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;
}); });
} var promiseLogin;
return promiseLogin.then((user) => { const self = this;
return self.getMatrixClient(user); 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); const matrixStore = new sdk.MemoryStore(this.$store.getters.storage);
var opts = { return util.getMatrixBaseUrl(user, this.$config).then((baseUrl) => {
baseUrl: user.home_server, var opts = {
userId: user.user_id, baseUrl: baseUrl,
store: matrixStore, userId: user.user_id,
deviceId: user.device_id, store: matrixStore,
accessToken: user.access_token, deviceId: user.device_id,
timelineSupport: true, accessToken: user.access_token,
unstableClientRelationAggregation: true, timelineSupport: true,
//useAuthorizationHeader: true unstableClientRelationAggregation: true,
}; //useAuthorizationHeader: true
this.matrixClient = sdk.createClient(opts); };
// if (user.is_guest) { this.matrixClient = sdk.createClient(opts);
// this.matrixClient.setGuest(true); // if (user.is_guest) {
// } // this.matrixClient.setGuest(true);
return this.matrixClient // }
.initCrypto() return this.matrixClient
.then(() => { .initCrypto()
console.log("Crypto initialized"); .then(() => {
console.log("Crypto initialized");
this.addMatrixClientListeners(this.matrixClient); this.addMatrixClientListeners(this.matrixClient);
this.matrixClient.startClient(); this.matrixClient.startClient();
return this.matrixClient; return this.matrixClient;
}) })
.then((matrixClient) => { .then((matrixClient) => {
if (matrixClient.isInitialSyncComplete()) { if (matrixClient.isInitialSyncComplete()) {
console.log("Initial sync done already!"); console.log("Initial sync done already!");
return matrixClient; return matrixClient;
} else { } else {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
matrixClient.once("sync", function (state, ignoredprevState, ignoredres) { matrixClient.once("sync", function (state, ignoredprevState, ignoredres) {
console.log(state); // state will be 'PREPARED' when the client is ready to use console.log(state); // state will be 'PREPARED' when the client is ready to use
if (state == "PREPARED") { if (state == "PREPARED") {
resolve(matrixClient); resolve(matrixClient);
} else if (state == "ERROR") { } else if (state == "ERROR") {
reject("Error syncing"); reject("Error syncing");
} }
});
}); });
}); }
} })
}) .then(() => {
.then(() => { // Ready to use! Start by loading rooms.
// Ready to use! Start by loading rooms. this.initClient();
this.initClient(); return user;
return user; });
}); });
}, },
/** /**
@ -329,7 +336,10 @@ export default {
if (this.ready) { if (this.ready) {
return Promise.resolve(this.currentUser); 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) { addMatrixClientListeners(client) {
@ -376,12 +386,12 @@ export default {
break; break;
case "m.room.power_levels": case "m.room.power_levels":
{ {
if (this.currentRoom && event.getRoomId() == this.currentRoom.roomId) { if (this.currentRoom && event.getRoomId() == this.currentRoom.roomId) {
this.currentRoomIsReadOnlyForUser = this.isReadOnlyRoomForUser(event.getRoomId(), this.currentUserId); this.currentRoomIsReadOnlyForUser = this.isReadOnlyRoomForUser(event.getRoomId(), this.currentUserId);
}
} }
break; }
break;
case STATE_EVENT_ROOM_DELETED: case STATE_EVENT_ROOM_DELETED:
{ {
@ -394,7 +404,7 @@ export default {
if (event.getSender() !== this.currentUserId) { if (event.getSender() !== this.currentUserId) {
this.leaveRoomAndNavigate(room.roomId).then(() => { this.leaveRoomAndNavigate(room.roomId).then(() => {
this.matrixClient.store.removeRoom(room.roomId); this.matrixClient.store.removeRoom(room.roomId);
}) });
} }
} }
} }
@ -406,13 +416,16 @@ export default {
onRoom(room) { onRoom(room) {
if (room.selfMembership === "invite") { if (room.selfMembership === "invite") {
this.matrixClient.getRoomTags(room.roomId).then(reply => { this.matrixClient
if (Object.keys(reply.tags).includes("m.server_notice")) { .getRoomTags(room.roomId)
Vue.set(room, "isServiceNoticeRoom", true); .then((reply) => {
} if (Object.keys(reply.tags).includes("m.server_notice")) {
}).catch((error => { Vue.set(room, "isServiceNoticeRoom", true);
console.error(error); }
})) })
.catch((error) => {
console.error(error);
});
} }
this.reloadRooms(); this.reloadRooms();
this.updateNotificationCount(); this.updateNotificationCount();
@ -477,13 +490,18 @@ export default {
}); });
Vue.set(this, "rooms", updatedRooms); 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); const resolvedId =
resolvedId.then(roomId => { this.currentRoomId && this.currentRoomId.startsWith("#")
const currentRoom = this.getRoom(roomId); ? this.matrixClient.resolveRoomAlias(this.currentRoomId).then((r) => r.room_id)
if (this.currentRoom != currentRoom) { : Promise.resolve(this.currentRoomId);
this.currentRoom = currentRoom; resolvedId
} .then((roomId) => {
}).catch(ignorederror => {}); const currentRoom = this.getRoom(roomId);
if (this.currentRoom != currentRoom) {
this.currentRoom = currentRoom;
}
})
.catch((ignorederror) => {});
}, },
setCurrentRoomId(roomId) { setCurrentRoomId(roomId) {
@ -566,22 +584,14 @@ export default {
*/ */
leaveRoomAndNavigate(roomId) { leaveRoomAndNavigate(roomId) {
const joinedRooms = this.joinedRooms; const joinedRooms = this.joinedRooms;
const isLastRoomWeAreJoinedTo = ( const isLastRoomWeAreJoinedTo = joinedRooms && joinedRooms.length == 1 && joinedRooms[0].roomId == roomId;
joinedRooms && return this.leaveRoom(roomId).then(() => {
joinedRooms.length == 1 && if (isLastRoomWeAreJoinedTo) {
joinedRooms[0].roomId == roomId this.$navigation.push({ name: "Goodbye" }, -1);
); } else {
return this.leaveRoom(roomId) this.$navigation.push({ name: "Home", params: { roomId: null } }, -1);
.then(() => { }
if (isLastRoomWeAreJoinedTo) { });
this.$navigation.push({ name: "Goodbye" }, -1);
} else {
this.$navigation.push(
{ name: "Home", params: { roomId: null } },
-1
);
}
})
}, },
kickUser(roomId, userId) { kickUser(roomId, userId) {
@ -641,9 +651,11 @@ export default {
isJoinedToRoom(roomIdOrAlias) { isJoinedToRoom(roomIdOrAlias) {
if (roomIdOrAlias && this.matrixClient) { if (roomIdOrAlias && this.matrixClient) {
try { try {
const resolvedRoomId = roomIdOrAlias.startsWith("#") ? this.matrixClient.resolveRoomAlias(roomIdOrAlias).then(res => res.room_id) : Promise.resolve(roomIdOrAlias); const resolvedRoomId = roomIdOrAlias.startsWith("#")
return resolvedRoomId.then(roomId => { ? this.matrixClient.resolveRoomAlias(roomIdOrAlias).then((res) => res.room_id)
return this.matrixClient.getJoinedRooms().then(rooms => { : Promise.resolve(roomIdOrAlias);
return resolvedRoomId.then((roomId) => {
return this.matrixClient.getJoinedRooms().then((rooms) => {
return rooms.joined_rooms.includes(roomId); return rooms.joined_rooms.includes(roomId);
}); });
}); });
@ -661,7 +673,7 @@ export default {
if (room && room.currentState) { if (room && room.currentState) {
const powerLevelEvent = room.currentState.getStateEvents("m.room.power_levels", ""); const powerLevelEvent = room.currentState.getStateEvents("m.room.power_levels", "");
if (powerLevelEvent) { 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) { if (this.matrixClient && roomId && userId) {
const room = this.getRoom(roomId); const room = this.getRoom(roomId);
if (room && room.currentState) { if (room && room.currentState) {
return !room.currentState.maySendMessage(userId) return !room.currentState.maySendMessage(userId);
} }
} }
return false; return false;
@ -686,11 +698,7 @@ export default {
if (powerLevelEvent) { if (powerLevelEvent) {
let content = powerLevelEvent.getContent(); let content = powerLevelEvent.getContent();
content.events_default = readOnly ? 50 : 0; content.events_default = readOnly ? 50 : 0;
this.matrixClient.sendStateEvent( this.matrixClient.sendStateEvent(room.roomId, "m.room.power_levels", content);
room.roomId,
"m.room.power_levels",
content
);
} }
} }
} }
@ -759,11 +767,7 @@ export default {
}); });
}) })
.then(() => { .then(() => {
return this.matrixClient.sendStateEvent( return this.matrixClient.sendStateEvent(roomId, STATE_EVENT_ROOM_DELETED, { status: "deleted" });
roomId,
STATE_EVENT_ROOM_DELETED,
{ status: "deleted" }
);
}) })
.then(() => { .then(() => {
//console.log("Purge: create timeline"); //console.log("Purge: create timeline");
@ -849,11 +853,9 @@ export default {
return kickFirstMember(allMembers); return kickFirstMember(allMembers);
}) })
.then(() => { .then(() => {
return withRetry(() => this.matrixClient.sendStateEvent( return withRetry(() =>
roomId, this.matrixClient.sendStateEvent(roomId, STATE_EVENT_ROOM_DELETED, { status: "deleted" })
STATE_EVENT_ROOM_DELETED, );
{ status: "deleted" }
));
}) })
.then(() => { .then(() => {
statusCallback(null); statusCallback(null);
@ -970,7 +972,7 @@ export default {
return false; return false;
}, },
/** /**
* Return true if this room is a direct room with one other user. NOTE: this currently * 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. * only checks number of members, not any is_direct flag.
* @param { } room * @param { } room
@ -979,7 +981,7 @@ export default {
// TODO - Use the is_direct accountData flag (m.direct). WE (as the client) // TODO - Use the is_direct accountData flag (m.direct). WE (as the client)
// apprently need to set this... // apprently need to set this...
if (room && room.getJoinRule() == "invite" && room.getMembers().length == 2) { if (room && room.getJoinRule() == "invite" && room.getMembers().length == 2) {
return true; return true;
} }
return false; return false;
}, },
@ -998,7 +1000,10 @@ export default {
setUserDisplayName(name) { setUserDisplayName(name) {
if (this.matrixClient) { 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 { } else {
return Promise.reject("No matrix client"); return Promise.reject("No matrix client");
} }
@ -1043,71 +1048,75 @@ export default {
* @returns A MatrixClient that can be used for public queries * @returns A MatrixClient that can be used for public queries
*/ */
getPublicQueryMatrixClient() { getPublicQueryMatrixClient() {
var clientPromise;
if (this.matrixClient) { if (this.matrixClient) {
clientPromise = this.getMatrixClient().then(() => { return this.getMatrixClient().then(() => {
return this.matrixClient; return this.matrixClient;
}); });
} else { } else {
const tempMatrixClient = sdk.createClient({baseUrl: this.$config.defaultServer});
var tempUserString = this.$store.state.tempuser; var tempUserString = this.$store.state.tempuser;
var tempUser = null; var tempUser = null;
if (tempUserString) { if (tempUserString) {
tempUser = JSON.parse(tempUserString); tempUser = JSON.parse(tempUserString);
} }
return util.getMatrixBaseUrl(tempUser, this.$config).then((baseUrl) => {
const tempMatrixClient = sdk.createClient({ baseUrl: baseUrl });
// Need to create an account? var clientPromise;
//
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;
});
}
// Get an access token // Need to create an account?
clientPromise = clientPromise.then((user) => { //
var data = { if (tempUser) {
user: User.localPart(user.user_id), clientPromise = Promise.resolve(tempUser);
password: user.password, } else {
type: "m.login.password", const user = util.randomUser(this.$config.userIdPrefix);
initial_device_display_name: this.$config.appName, const pass = util.randomPass();
}; clientPromise = tempMatrixClient
if (user.device_id) { .register(user, pass, null, {
data.device_id = user.device_id; 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 // Get an access token
// clientPromise = clientPromise.then((user) => {
// Create a slimmed down client, without crypto. This one is var data = {
// Only used to get public room info from. user: User.localPart(user.user_id),
clientPromise = clientPromise.then((user) => { password: user.password,
var opts = { type: "m.login.password",
baseUrl: this.$config.defaultServer, initial_device_display_name: this.$config.appName,
userId: user.user_id, };
accessToken: user.access_token, if (user.device_id) {
timelineSupport: false, data.device_id = user.device_id;
}; }
var matrixClient = sdk.createClient(opts); return tempMatrixClient.login("m.login.password", data);
matrixClient.startClient(); });
return matrixClient;
// 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) { getPublicUserInfo(userId) {