Compare commits

...

1 Commits

Author SHA1 Message Date
Will Hunt
fa90f14e66 Draft of handling urls 2025-10-16 10:29:11 +01:00
7 changed files with 83 additions and 2 deletions

View File

@@ -524,4 +524,10 @@ export default abstract class BasePlatform {
* @returns {Promise<boolean>} True if the lock was acquired, false otherwise.
*/
public abstract getSessionLock(_onNewInstance: () => Promise<void>): Promise<boolean>;
/**
*
* @param urlPattern
*/
public registerProtocolHandler?(urlPattern: `${string}%s`): Promise<void>
}

View File

@@ -43,6 +43,10 @@ enum Views {
// Another instance of the application has started up. We just show an error page.
LOCK_STOLEN,
// Another instance of the application has opened and this session had a matrixuri, so we
// just show a warning and close.
SENT_URI,
}
export default Views;

View File

@@ -140,6 +140,7 @@ import { ShareFormat, type SharePayload } from "../../dispatcher/payloads/ShareP
import Markdown from "../../Markdown";
import { sanitizeHtmlParams } from "../../Linkify";
import { isOnlyAdmin } from "../../utils/membership";
import mxModuleApi from "../../modules/Api";
// legacy export
export { default as Views } from "../../Views";
@@ -234,6 +235,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
private fontWatcher?: FontWatcher;
private readonly stores: SdkContextClass;
private loadSessionAbortController = new AbortController();
private newUriBroadcastChannel = new BroadcastChannel("io.element.broadcast.new_uri");
private sessionLoadStarted = false;
@@ -283,6 +285,12 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
// object field used for tracking the status info appended to the title tag.
// we don't do it as react state as i'm scared about triggering needless react refreshes.
this.subTitleStatus = "";
this.newUriBroadcastChannel.addEventListener("message", (ev) => {
const { screen, params } = ev.data;
console.log(ev, ev.data);
this.showScreen(screen, params);
});
}
/**
@@ -470,6 +478,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
initSentry(SdkConfig.get("sentry"));
window.addEventListener("resize", this.onWindowResized);
console.log("MatrixCHAT IS LOADING")
// Once we start loading the MatrixClient, we can't stop, even if MatrixChat gets unmounted (as it does
// in React's Strict Mode). So, start loading the session now, but only if this MatrixChat was not previously
@@ -478,8 +487,18 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
this.sessionLoadStarted = true;
const platform = PlatformPeg.get();
if (platform && !platform.checkSessionLockFree()) {
// another instance holds the lock; confirm its theft before proceeding
setTimeout(() => this.setState({ view: Views.CONFIRM_LOCK_THEFT }), 0);
console.log("screenAfterLogin", this.screenAfterLogin);
if (this.screenAfterLogin?.screen.startsWith("matrix")) {
console.log("Got URI req");
// The user has clicked on a matrixuri link, so we need to forward it to them.
this.newUriBroadcastChannel.postMessage({screen: this.screenAfterLogin.screen, params: this.screenAfterLogin.params});
// This will put the application in a final state so the other instance can handle the URI.
setTimeout(() => this.setState({ view: Views.SENT_URI }), 0);
} else {
// another instance holds the lock; confirm its theft before proceeding
setTimeout(() => this.setState({ view: Views.CONFIRM_LOCK_THEFT }), 0);
}
} else {
this.startInitSession();
}
@@ -1752,6 +1771,10 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
}
public showScreen(screen: string, params?: { [key: string]: any }): void {
if (this.state.view === Views.SENT_URI) {
// Ignore any screens once we've reached this state.
return;
}
const cli = MatrixClientPeg.get();
const isLoggedOutOrGuest = !cli || cli.isGuest();
if (!isLoggedOutOrGuest && AUTH_SCREENS.includes(screen)) {
@@ -1817,6 +1840,11 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
}
} else if (screen === "settings") {
dis.fire(Action.ViewUserSettings);
} else if (screen.startsWith("matrix")) {
console.log("matrix:", screen, params);
mxModuleApi.navigation.toMatrixToLink(screen, false).catch((ex) => {
console.log('Failed to handle matrix uri', ex);
})
} else if (screen === "welcome") {
dis.dispatch({
action: "view_welcome_page",
@@ -2098,6 +2126,10 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
}}
/>
);
} else if (this.state.view === Views.SENT_URI) {
view = (
<b>PLACEHOLDER: The URI has been broadcast to the other app.</b>
);
} else if (this.state.view === Views.COMPLETE_SECURITY) {
view = <CompleteSecurity onFinished={this.onCompleteSecurityE2eSetupFinished} />;
} else if (this.state.view === Views.E2E_SETUP) {

View File

@@ -363,6 +363,8 @@ export default class PreferencesUserSettingsTab extends React.Component<IProps,
<SettingsSubsection heading={_t("common|general")} stretchContent>
{this.renderGroup(PreferencesUserSettingsTab.GENERAL_SETTINGS)}
<SettingsFlag name="protocolHandlerRegistered" level={SettingLevel.DEVICE} />
<SettingsFlag name="Electron.showTrayIcon" level={SettingLevel.PLATFORM} hideIfCannotSet />
<SettingsFlag
name="Electron.enableHardwareAcceleration"

View File

@@ -50,6 +50,7 @@ import { SortingAlgorithm } from "../stores/room-list-v3/skip-list/sorters/index
import MediaPreviewConfigController from "./controllers/MediaPreviewConfigController.ts";
import InviteRulesConfigController from "./controllers/InviteRulesConfigController.ts";
import { type ComputedInviteConfig } from "../@types/invite-rules.ts";
import ProtocolHandlerController from "./controllers/ProtocolHandlerController.ts";
export const defaultWatchManager = new WatchManager();
@@ -370,6 +371,7 @@ export interface Settings {
"mediaPreviewConfig": IBaseSetting<MediaPreviewConfig>;
"inviteRules": IBaseSetting<ComputedInviteConfig>;
"Developer.elementCallUrl": IBaseSetting<string>;
protocolHandlerRegistered: IBaseSetting<boolean>;
}
export type SettingKey = keyof Settings;
@@ -1345,6 +1347,11 @@ export const SETTINGS: Settings = {
// Contains room IDs
shouldExportToRageshake: false,
},
"protocolHandlerRegistered": {
default: false,
controller: new ProtocolHandlerController(),
supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS_WITH_CONFIG,
},
/**
* Enable or disable the release announcement feature
*/

View File

@@ -0,0 +1,26 @@
/*
Copyright 2024 New Vector Ltd.
Copyright 2020 The Matrix.org Foundation C.I.C.
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
Please see LICENSE files in the repository root for full details.
*/
import SettingController from "./SettingController";
import PlatformPeg from "../../PlatformPeg";
export default class ProtocolHandlerController extends SettingController {
public constructor() {
super();
}
public get settingDisabled() {
return PlatformPeg.get()?.registerProtocolHandler === undefined;
}
public onChange(): void {
const platform = PlatformPeg.get()!;
const uri = platform.baseUrl;
platform.registerProtocolHandler!(`${uri}#/%s`);
}
}

View File

@@ -277,4 +277,8 @@ export default class WebPlatform extends BasePlatform {
public async getSessionLock(onNewInstance: () => Promise<void>): Promise<boolean> {
return SessionLock.getSessionLock(onNewInstance);
}
public async registerProtocolHandler(urlPattern: `${string}%s`) {
navigator.registerProtocolHandler("matrix", urlPattern);
}
}