diff --git a/src/Preferences.js b/src/Preferences.js index d6d36f6..cdd1446 100644 --- a/src/Preferences.js +++ b/src/Preferences.js @@ -25,6 +25,7 @@ export class Preferences extends EventEmitter { // used to differentiate web from native if a client supports both this.platform = null; this.homeservers = null; + this.preferredWebInstances = {}; const prefsStr = localStorage.getItem("preferred_client"); if (prefsStr) { @@ -36,6 +37,10 @@ export class Preferences extends EventEmitter { if (serversStr) { this.homeservers = JSON.parse(serversStr); } + const preferredWebInstancesStr = localStorage.getItem("preferred_web_instances"); + if (preferredWebInstancesStr) { + this.preferredWebInstances = JSON.parse(preferredWebInstancesStr); + } } setClient(id, platform) { @@ -54,12 +59,24 @@ export class Preferences extends EventEmitter { } } + setPreferredWebInstance(client_id, instance_url) { + this.preferredWebInstances[client_id] = instance_url; + this._localStorage.setItem("preferred_web_instances", JSON.stringify(this.preferredWebInstances)); + this.emit("canClear"); + } + + getPreferredWebInstance(client_id) { + return this.preferredWebInstances[client_id]; + } + clear() { this._localStorage.removeItem("preferred_client"); this._localStorage.removeItem("consented_servers"); + this._localStorage.removeItem("preferred_web_instances"); this.clientId = null; this.platform = null; this.homeservers = null; + this.preferredWebInstances = {}; } get canClear() { diff --git a/src/open/ClientViewModel.js b/src/open/ClientViewModel.js index 9a0543a..7059186 100644 --- a/src/open/ClientViewModel.js +++ b/src/open/ClientViewModel.js @@ -59,11 +59,11 @@ export class ClientViewModel extends ViewModel { if (this._proposedPlatform === this._nativePlatform) { deepLinkLabel = "Open in app"; } else { - deepLinkLabel = `Open on ${this._client.getPreferredWebInstance(this._link)}`; + deepLinkLabel = `Open on ${this.preferredWebInstance}`; } } const actions = []; - const proposedDeepLink = this._client.getDeepLink(this._proposedPlatform, this._link); + const proposedDeepLink = this._client.getDeepLink(this._proposedPlatform, this._link, this.preferredWebInstance); if (proposedDeepLink) { actions.push({ label: deepLinkLabel, @@ -83,8 +83,8 @@ export class ClientViewModel extends ViewModel { // show only if there is a preferred instance, and if we don't already link to it in the first button if (hasPreferredWebInstance && this._webPlatform && this._proposedPlatform !== this._webPlatform) { actions.push({ - label: `Open on ${this._client.getPreferredWebInstance(this._link)}`, - url: this._client.getDeepLink(this._webPlatform, this._link), + label: `Open on ${this.preferredWebInstance}`, + url: this._client.getDeepLink(this._webPlatform, this._link, this.preferredWebInstance), kind: "open-in-web", activated: () => {} // don't persist this choice as we don't persist the preferred web instance, it's in the url }); @@ -108,10 +108,10 @@ export class ClientViewModel extends ViewModel { actions.push(...nativeActions); } if (this._webPlatform) { - const webDeepLink = this._client.getDeepLink(this._webPlatform, this._link); + const webDeepLink = this._client.getDeepLink(this._webPlatform, this._link, this.preferredWebInstance); if (webDeepLink) { const webLabel = this.hasPreferredWebInstance ? - `Open on ${this._client.getPreferredWebInstance(this._link)}` : + `Open on ${this.preferredWebInstance}` : `Continue in your browser`; actions.push({ label: webLabel, @@ -128,14 +128,22 @@ export class ClientViewModel extends ViewModel { return actions; } - get hasPreferredWebInstance() { + get preferredWebInstance() { // also check there is a web platform that matches the platforms the user is on (mobile or desktop web) - return this._webPlatform && typeof this._client.getPreferredWebInstance(this._link) === "string"; + if (!this._webPlatform) return undefined; + return ( + this.preferences.getPreferredWebInstance(this._client.id) + || this._client.getPreferredWebInstance(this._link) + ); + } + + get hasPreferredWebInstance() { + return typeof this.preferredWebInstance === "string"; } get hostedByBannerLabel() { - const preferredWebInstance = this._client.getPreferredWebInstance(this._link); - if (this._webPlatform && preferredWebInstance) { + if (this.hasPreferredWebInstance) { + const preferredWebInstance = this.preferredWebInstance; let label = preferredWebInstance; const subDomainIdx = preferredWebInstance.lastIndexOf(".", preferredWebInstance.lastIndexOf(".") - 1); if (subDomainIdx !== -1) { @@ -188,7 +196,7 @@ export class ClientViewModel extends ViewModel { get showDeepLinkInInstall() { // we can assume this._nativePlatform as this._clientCanIntercept already checks it - return this._clientCanIntercept && !!this._client.getDeepLink(this._nativePlatform, this._link); + return this._clientCanIntercept && !!this._client.getDeepLink(this._nativePlatform, this._link, this.preferredWebInstance); } get availableOnPlatformNames() { diff --git a/src/open/clients/Element.js b/src/open/clients/Element.js index 06ca2fe..e28b0ac 100644 --- a/src/open/clients/Element.js +++ b/src/open/clients/Element.js @@ -55,8 +55,9 @@ export class Element { get homepage() { return "https://element.io"; } get author() { return "Element"; } getMaturity(platform) { return Maturity.Stable; } + get supportsCustomInstances() { return true; } - getDeepLink(platform, link) { + getDeepLink(platform, link, preferredWebInstance) { let fragmentPath; switch (link.kind) { case LinkKind.User: @@ -82,8 +83,8 @@ export class Element { let instanceHost = trustedWebInstances[0]; // we use app.element.io which iOS will intercept, but it likely won't intercept any other trusted instances // so only use a preferred web instance for true web links. - if (isWebPlatform && trustedWebInstances.includes(link.webInstances[this.id])) { - instanceHost = link.webInstances[this.id]; + if (isWebPlatform && preferredWebInstance) { + instanceHost = preferredWebInstance; } return `https://${instanceHost}/#/${fragmentPath}`; } else if (platform === Platform.Linux || platform === Platform.Windows || platform === Platform.macOS) {