Fix slash commands not being enabled in certain cases (#11090)

* Fix slash commands not being enabled in certain cases

* Fix import cycle
This commit is contained in:
Michael Telatynski 2023-06-14 13:49:18 +01:00 committed by GitHub
parent 9c48487d85
commit 6486255f54
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 16 additions and 10 deletions

View file

@ -69,6 +69,7 @@ import { htmlSerializeFromMdIfNeeded } from "./editor/serialize";
import { leaveRoomBehaviour } from "./utils/leave-behaviour"; import { leaveRoomBehaviour } from "./utils/leave-behaviour";
import { isLocalRoom } from "./utils/localRoom/isLocalRoom"; import { isLocalRoom } from "./utils/localRoom/isLocalRoom";
import { SdkContextClass } from "./contexts/SDKContext"; import { SdkContextClass } from "./contexts/SDKContext";
import { MatrixClientPeg } from "./MatrixClientPeg";
// XXX: workaround for https://github.com/microsoft/TypeScript/issues/31816 // XXX: workaround for https://github.com/microsoft/TypeScript/issues/31816
interface HTMLInputEvent extends Event { interface HTMLInputEvent extends Event {
@ -122,7 +123,7 @@ interface ICommandOpts {
runFn?: RunFn; runFn?: RunFn;
category: string; category: string;
hideCompletionAfterSpace?: boolean; hideCompletionAfterSpace?: boolean;
isEnabled?(matrixClient?: MatrixClient): boolean; isEnabled?(matrixClient: MatrixClient | null): boolean;
renderingTypes?: TimelineRenderingType[]; renderingTypes?: TimelineRenderingType[];
} }
@ -136,7 +137,7 @@ export class Command {
public readonly hideCompletionAfterSpace: boolean; public readonly hideCompletionAfterSpace: boolean;
public readonly renderingTypes?: TimelineRenderingType[]; public readonly renderingTypes?: TimelineRenderingType[];
public readonly analyticsName?: SlashCommandEvent["command"]; public readonly analyticsName?: SlashCommandEvent["command"];
private readonly _isEnabled?: (matrixClient?: MatrixClient) => boolean; private readonly _isEnabled?: (matrixClient: MatrixClient | null) => boolean;
public constructor(opts: ICommandOpts) { public constructor(opts: ICommandOpts) {
this.command = opts.command; this.command = opts.command;
@ -189,7 +190,7 @@ export class Command {
return _t("Usage") + ": " + this.getCommandWithArgs(); return _t("Usage") + ": " + this.getCommandWithArgs();
} }
public isEnabled(cli?: MatrixClient): boolean { public isEnabled(cli: MatrixClient | null): boolean {
return this._isEnabled?.(cli) ?? true; return this._isEnabled?.(cli) ?? true;
} }
} }
@ -206,7 +207,7 @@ function successSync(value: any): RunResult {
return success(Promise.resolve(value)); return success(Promise.resolve(value));
} }
const isCurrentLocalRoom = (cli?: MatrixClient): boolean => { const isCurrentLocalRoom = (cli: MatrixClient | null): boolean => {
const roomId = SdkContextClass.instance.roomViewStore.getRoomId(); const roomId = SdkContextClass.instance.roomViewStore.getRoomId();
if (!roomId) return false; if (!roomId) return false;
const room = cli?.getRoom(roomId); const room = cli?.getRoom(roomId);
@ -214,7 +215,7 @@ const isCurrentLocalRoom = (cli?: MatrixClient): boolean => {
return isLocalRoom(room); return isLocalRoom(room);
}; };
const canAffectPowerlevels = (cli?: MatrixClient): boolean => { const canAffectPowerlevels = (cli: MatrixClient | null): boolean => {
const roomId = SdkContextClass.instance.roomViewStore.getRoomId(); const roomId = SdkContextClass.instance.roomViewStore.getRoomId();
if (!cli || !roomId) return false; if (!cli || !roomId) return false;
const room = cli?.getRoom(roomId); const room = cli?.getRoom(roomId);
@ -1425,7 +1426,7 @@ interface ICmd {
export function getCommand(input: string): ICmd { export function getCommand(input: string): ICmd {
const { cmd, args } = parseCommandString(input); const { cmd, args } = parseCommandString(input);
if (cmd && CommandMap.has(cmd) && CommandMap.get(cmd)!.isEnabled()) { if (cmd && CommandMap.has(cmd) && CommandMap.get(cmd)!.isEnabled(MatrixClientPeg.get())) {
return { return {
cmd: CommandMap.get(cmd), cmd: CommandMap.get(cmd),
args, args,

View file

@ -27,6 +27,7 @@ import { TextualCompletion } from "./Components";
import { ICompletion, ISelectionRange } from "./Autocompleter"; import { ICompletion, ISelectionRange } from "./Autocompleter";
import { Command, Commands, CommandMap } from "../SlashCommands"; import { Command, Commands, CommandMap } from "../SlashCommands";
import { TimelineRenderingType } from "../contexts/RoomContext"; import { TimelineRenderingType } from "../contexts/RoomContext";
import { MatrixClientPeg } from "../MatrixClientPeg";
const COMMAND_RE = /(^\/\w*)(?: .*)?/g; const COMMAND_RE = /(^\/\w*)(?: .*)?/g;
@ -51,12 +52,14 @@ export default class CommandProvider extends AutocompleteProvider {
const { command, range } = this.getCurrentCommand(query, selection); const { command, range } = this.getCurrentCommand(query, selection);
if (!command) return []; if (!command) return [];
const cli = MatrixClientPeg.get();
let matches: Command[] = []; let matches: Command[] = [];
// check if the full match differs from the first word (i.e. returns false if the command has args) // check if the full match differs from the first word (i.e. returns false if the command has args)
if (command[0] !== command[1]) { if (command[0] !== command[1]) {
// The input looks like a command with arguments, perform exact match // The input looks like a command with arguments, perform exact match
const name = command[1].slice(1); // strip leading `/` const name = command[1].slice(1); // strip leading `/`
if (CommandMap.has(name) && CommandMap.get(name)!.isEnabled()) { if (CommandMap.has(name) && CommandMap.get(name)!.isEnabled(cli)) {
// some commands, namely `me` don't suit having the usage shown whilst typing their arguments // some commands, namely `me` don't suit having the usage shown whilst typing their arguments
if (CommandMap.get(name)!.hideCompletionAfterSpace) return []; if (CommandMap.get(name)!.hideCompletionAfterSpace) return [];
matches = [CommandMap.get(name)!]; matches = [CommandMap.get(name)!];
@ -75,7 +78,7 @@ export default class CommandProvider extends AutocompleteProvider {
return matches return matches
.filter((cmd) => { .filter((cmd) => {
const display = !cmd.renderingTypes || cmd.renderingTypes.includes(this.renderingType); const display = !cmd.renderingTypes || cmd.renderingTypes.includes(this.renderingType);
return cmd.isEnabled() && display; return cmd.isEnabled(cli) && display;
}) })
.map((result) => { .map((result) => {
let completion = result.getCommand() + " "; let completion = result.getCommand() + " ";

View file

@ -19,6 +19,7 @@ import React from "react";
import { _t } from "../../../languageHandler"; import { _t } from "../../../languageHandler";
import { Command, CommandCategories, Commands } from "../../../SlashCommands"; import { Command, CommandCategories, Commands } from "../../../SlashCommands";
import InfoDialog from "./InfoDialog"; import InfoDialog from "./InfoDialog";
import { MatrixClientPeg } from "../../../MatrixClientPeg";
interface IProps { interface IProps {
onFinished(): void; onFinished(): void;
@ -27,7 +28,7 @@ interface IProps {
const SlashCommandHelpDialog: React.FC<IProps> = ({ onFinished }) => { const SlashCommandHelpDialog: React.FC<IProps> = ({ onFinished }) => {
const categories: Record<string, Command[]> = {}; const categories: Record<string, Command[]> = {};
Commands.forEach((cmd) => { Commands.forEach((cmd) => {
if (!cmd.isEnabled()) return; if (!cmd.isEnabled(MatrixClientPeg.get())) return;
if (!categories[cmd.category]) { if (!categories[cmd.category]) {
categories[cmd.category] = []; categories[cmd.category] = [];
} }

View file

@ -51,6 +51,7 @@ import { ALTERNATE_KEY_NAME, KeyBindingAction } from "../../../accessibility/Key
import { _t } from "../../../languageHandler"; import { _t } from "../../../languageHandler";
import { linkify } from "../../../linkify-matrix"; import { linkify } from "../../../linkify-matrix";
import { SdkContextClass } from "../../../contexts/SDKContext"; import { SdkContextClass } from "../../../contexts/SDKContext";
import { MatrixClientPeg } from "../../../MatrixClientPeg";
// matches emoticons which follow the start of a line or whitespace // matches emoticons which follow the start of a line or whitespace
const REGEX_EMOTICON_WHITESPACE = new RegExp("(?:^|\\s)(" + EMOTICON_REGEX.source + ")\\s|:^$"); const REGEX_EMOTICON_WHITESPACE = new RegExp("(?:^|\\s)(" + EMOTICON_REGEX.source + ")\\s|:^$");
@ -268,7 +269,7 @@ export default class BasicMessageEditor extends React.Component<IProps, IState>
if (isTyping && this.props.model.parts[0].type === "command") { if (isTyping && this.props.model.parts[0].type === "command") {
const { cmd } = parseCommandString(this.props.model.parts[0].text); const { cmd } = parseCommandString(this.props.model.parts[0].text);
const command = CommandMap.get(cmd!); const command = CommandMap.get(cmd!);
if (!command?.isEnabled() || command.category !== CommandCategories.messages) { if (!command?.isEnabled(MatrixClientPeg.get()) || command.category !== CommandCategories.messages) {
isTyping = false; isTyping = false;
} }
} }