Replace newTranslatableError
with UserFriendlyError
(#10440
* Introduce UserFriendlyError * Replace newTranslatableError with UserFriendlyError * Remove ITranslatableError * Fix up some strict lints * Document when we/why we can remove * Update matrix-web-i18n Includes changes to find `new UserFriendlyError`, see https://github.com/matrix-org/matrix-web-i18n/pull/6 * Include room ID in error * Translate fallback error * Translate better * Update i18n strings * Better re-use * Minor comment fixes
This commit is contained in:
parent
567248d5c5
commit
ff1468b6d3
10 changed files with 285 additions and 99 deletions
|
@ -30,7 +30,7 @@ import { SlashCommand as SlashCommandEvent } from "@matrix-org/analytics-events/
|
|||
|
||||
import { MatrixClientPeg } from "./MatrixClientPeg";
|
||||
import dis from "./dispatcher/dispatcher";
|
||||
import { _t, _td, ITranslatableError, newTranslatableError } from "./languageHandler";
|
||||
import { _t, _td, UserFriendlyError } from "./languageHandler";
|
||||
import Modal from "./Modal";
|
||||
import MultiInviter from "./utils/MultiInviter";
|
||||
import { Linkify, topicToHtml } from "./HtmlUtils";
|
||||
|
@ -110,7 +110,7 @@ export const CommandCategories = {
|
|||
other: _td("Other"),
|
||||
};
|
||||
|
||||
export type RunResult = XOR<{ error: Error | ITranslatableError }, { promise: Promise<IContent | undefined> }>;
|
||||
export type RunResult = XOR<{ error: Error }, { promise: Promise<IContent | undefined> }>;
|
||||
|
||||
type RunFn = (this: Command, roomId: string, args?: string) => RunResult;
|
||||
|
||||
|
@ -163,14 +163,15 @@ export class Command {
|
|||
public run(roomId: string, threadId: string | null, args?: string): RunResult {
|
||||
// if it has no runFn then its an ignored/nop command (autocomplete only) e.g `/me`
|
||||
if (!this.runFn) {
|
||||
return reject(newTranslatableError("Command error: Unable to handle slash command."));
|
||||
return reject(new UserFriendlyError("Command error: Unable to handle slash command."));
|
||||
}
|
||||
|
||||
const renderingType = threadId ? TimelineRenderingType.Thread : TimelineRenderingType.Room;
|
||||
if (this.renderingTypes && !this.renderingTypes?.includes(renderingType)) {
|
||||
return reject(
|
||||
newTranslatableError("Command error: Unable to find rendering type (%(renderingType)s)", {
|
||||
new UserFriendlyError("Command error: Unable to find rendering type (%(renderingType)s)", {
|
||||
renderingType,
|
||||
cause: undefined,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
@ -310,7 +311,7 @@ export const Commands = [
|
|||
const room = cli.getRoom(roomId);
|
||||
if (!room?.currentState.mayClientSendStateEvent("m.room.tombstone", cli)) {
|
||||
return reject(
|
||||
newTranslatableError("You do not have the required permissions to use this command."),
|
||||
new UserFriendlyError("You do not have the required permissions to use this command."),
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -345,10 +346,10 @@ export const Commands = [
|
|||
(async (): Promise<void> => {
|
||||
const unixTimestamp = Date.parse(args);
|
||||
if (!unixTimestamp) {
|
||||
throw newTranslatableError(
|
||||
throw new UserFriendlyError(
|
||||
"We were unable to understand the given date (%(inputDate)s). " +
|
||||
"Try using the format YYYY-MM-DD.",
|
||||
{ inputDate: args },
|
||||
{ inputDate: args, cause: undefined },
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -496,7 +497,10 @@ export const Commands = [
|
|||
const room = cli.getRoom(roomId);
|
||||
if (!room) {
|
||||
return reject(
|
||||
newTranslatableError("Failed to get room topic: Unable to find room (%(roomId)s", { roomId }),
|
||||
new UserFriendlyError("Failed to get room topic: Unable to find room (%(roomId)s", {
|
||||
roomId,
|
||||
cause: undefined,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -576,13 +580,13 @@ export const Commands = [
|
|||
setToDefaultIdentityServer();
|
||||
return;
|
||||
}
|
||||
throw newTranslatableError(
|
||||
throw new UserFriendlyError(
|
||||
"Use an identity server to invite by email. Manage in Settings.",
|
||||
);
|
||||
});
|
||||
} else {
|
||||
return reject(
|
||||
newTranslatableError("Use an identity server to invite by email. Manage in Settings."),
|
||||
new UserFriendlyError("Use an identity server to invite by email. Manage in Settings."),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -594,7 +598,15 @@ export const Commands = [
|
|||
})
|
||||
.then(() => {
|
||||
if (inviter.getCompletionState(address) !== "invited") {
|
||||
throw new Error(inviter.getErrorText(address));
|
||||
const errorStringFromInviterUtility = inviter.getErrorText(address);
|
||||
if (errorStringFromInviterUtility) {
|
||||
throw new Error(errorStringFromInviterUtility);
|
||||
} else {
|
||||
throw new UserFriendlyError(
|
||||
"User (%(user)s) did not end up as invited to %(roomId)s but no error was given from the inviter utility",
|
||||
{ user: address, roomId, cause: undefined },
|
||||
);
|
||||
}
|
||||
}
|
||||
}),
|
||||
);
|
||||
|
@ -743,7 +755,12 @@ export const Commands = [
|
|||
return room.getCanonicalAlias() === roomAlias || room.getAltAliases().includes(roomAlias);
|
||||
})?.roomId;
|
||||
if (!targetRoomId) {
|
||||
return reject(newTranslatableError("Unrecognised room address: %(roomAlias)s", { roomAlias }));
|
||||
return reject(
|
||||
new UserFriendlyError("Unrecognised room address: %(roomAlias)s", {
|
||||
roomAlias,
|
||||
cause: undefined,
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -898,7 +915,10 @@ export const Commands = [
|
|||
const room = cli.getRoom(roomId);
|
||||
if (!room) {
|
||||
return reject(
|
||||
newTranslatableError("Command failed: Unable to find room (%(roomId)s", { roomId }),
|
||||
new UserFriendlyError("Command failed: Unable to find room (%(roomId)s", {
|
||||
roomId,
|
||||
cause: undefined,
|
||||
}),
|
||||
);
|
||||
}
|
||||
const member = room.getMember(userId);
|
||||
|
@ -906,7 +926,7 @@ export const Commands = [
|
|||
!member?.membership ||
|
||||
getEffectiveMembership(member.membership) === EffectiveMembership.Leave
|
||||
) {
|
||||
return reject(newTranslatableError("Could not find user in room"));
|
||||
return reject(new UserFriendlyError("Could not find user in room"));
|
||||
}
|
||||
const powerLevelEvent = room.currentState.getStateEvents("m.room.power_levels", "");
|
||||
return success(cli.setPowerLevel(roomId, userId, powerLevel, powerLevelEvent));
|
||||
|
@ -940,13 +960,16 @@ export const Commands = [
|
|||
const room = cli.getRoom(roomId);
|
||||
if (!room) {
|
||||
return reject(
|
||||
newTranslatableError("Command failed: Unable to find room (%(roomId)s", { roomId }),
|
||||
new UserFriendlyError("Command failed: Unable to find room (%(roomId)s", {
|
||||
roomId,
|
||||
cause: undefined,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
const powerLevelEvent = room.currentState.getStateEvents("m.room.power_levels", "");
|
||||
if (!powerLevelEvent?.getContent().users[args]) {
|
||||
return reject(newTranslatableError("Could not find user in room"));
|
||||
return reject(new UserFriendlyError("Could not find user in room"));
|
||||
}
|
||||
return success(cli.setPowerLevel(roomId, args, undefined, powerLevelEvent));
|
||||
}
|
||||
|
@ -975,7 +998,7 @@ export const Commands = [
|
|||
!isCurrentLocalRoom(),
|
||||
runFn: function (roomId, widgetUrl) {
|
||||
if (!widgetUrl) {
|
||||
return reject(newTranslatableError("Please supply a widget URL or embed code"));
|
||||
return reject(new UserFriendlyError("Please supply a widget URL or embed code"));
|
||||
}
|
||||
|
||||
// Try and parse out a widget URL from iframes
|
||||
|
@ -988,14 +1011,14 @@ export const Commands = [
|
|||
if (iframe.tagName.toLowerCase() === "iframe" && iframe.attrs) {
|
||||
const srcAttr = iframe.attrs.find((a) => a.name === "src");
|
||||
logger.log("Pulling URL out of iframe (embed code)");
|
||||
if (!srcAttr) return reject(newTranslatableError("iframe has no src attribute"));
|
||||
if (!srcAttr) return reject(new UserFriendlyError("iframe has no src attribute"));
|
||||
widgetUrl = srcAttr.value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!widgetUrl.startsWith("https://") && !widgetUrl.startsWith("http://")) {
|
||||
return reject(newTranslatableError("Please supply a https:// or http:// widget URL"));
|
||||
return reject(new UserFriendlyError("Please supply a https:// or http:// widget URL"));
|
||||
}
|
||||
if (WidgetUtils.canUserModifyWidgets(roomId)) {
|
||||
const userId = MatrixClientPeg.get().getUserId();
|
||||
|
@ -1017,7 +1040,7 @@ export const Commands = [
|
|||
|
||||
return success(WidgetUtils.setRoomWidget(roomId, widgetId, type, widgetUrl, name, data));
|
||||
} else {
|
||||
return reject(newTranslatableError("You cannot modify widgets in this room."));
|
||||
return reject(new UserFriendlyError("You cannot modify widgets in this room."));
|
||||
}
|
||||
},
|
||||
category: CommandCategories.admin,
|
||||
|
@ -1041,18 +1064,22 @@ export const Commands = [
|
|||
(async (): Promise<void> => {
|
||||
const device = cli.getStoredDevice(userId, deviceId);
|
||||
if (!device) {
|
||||
throw newTranslatableError("Unknown (user, session) pair: (%(userId)s, %(deviceId)s)", {
|
||||
userId,
|
||||
deviceId,
|
||||
});
|
||||
throw new UserFriendlyError(
|
||||
"Unknown (user, session) pair: (%(userId)s, %(deviceId)s)",
|
||||
{
|
||||
userId,
|
||||
deviceId,
|
||||
cause: undefined,
|
||||
},
|
||||
);
|
||||
}
|
||||
const deviceTrust = await cli.checkDeviceTrust(userId, deviceId);
|
||||
|
||||
if (deviceTrust.isVerified()) {
|
||||
if (device.getFingerprint() === fingerprint) {
|
||||
throw newTranslatableError("Session already verified!");
|
||||
throw new UserFriendlyError("Session already verified!");
|
||||
} else {
|
||||
throw newTranslatableError(
|
||||
throw new UserFriendlyError(
|
||||
"WARNING: session already verified, but keys do NOT MATCH!",
|
||||
);
|
||||
}
|
||||
|
@ -1060,7 +1087,7 @@ export const Commands = [
|
|||
|
||||
if (device.getFingerprint() !== fingerprint) {
|
||||
const fprint = device.getFingerprint();
|
||||
throw newTranslatableError(
|
||||
throw new UserFriendlyError(
|
||||
"WARNING: KEY VERIFICATION FAILED! The signing key for %(userId)s and session" +
|
||||
' %(deviceId)s is "%(fprint)s" which does not match the provided key ' +
|
||||
'"%(fingerprint)s". This could mean your communications are being intercepted!',
|
||||
|
@ -1069,6 +1096,7 @@ export const Commands = [
|
|||
userId,
|
||||
deviceId,
|
||||
fingerprint,
|
||||
cause: undefined,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
@ -1217,7 +1245,7 @@ export const Commands = [
|
|||
return success(
|
||||
(async (): Promise<void> => {
|
||||
const room = await VoipUserMapper.sharedInstance().getVirtualRoomForRoom(roomId);
|
||||
if (!room) throw newTranslatableError("No virtual room for this room");
|
||||
if (!room) throw new UserFriendlyError("No virtual room for this room");
|
||||
dis.dispatch<ViewRoomPayload>({
|
||||
action: Action.ViewRoom,
|
||||
room_id: room.roomId,
|
||||
|
@ -1245,7 +1273,7 @@ export const Commands = [
|
|||
if (isPhoneNumber) {
|
||||
const results = await LegacyCallHandler.instance.pstnLookup(userId);
|
||||
if (!results || results.length === 0 || !results[0].userid) {
|
||||
throw newTranslatableError("Unable to find Matrix ID for phone number");
|
||||
throw new UserFriendlyError("Unable to find Matrix ID for phone number");
|
||||
}
|
||||
userId = results[0].userid;
|
||||
}
|
||||
|
@ -1308,7 +1336,7 @@ export const Commands = [
|
|||
runFn: function (roomId, args) {
|
||||
const call = LegacyCallHandler.instance.getCallForRoom(roomId);
|
||||
if (!call) {
|
||||
return reject(newTranslatableError("No active call in this room"));
|
||||
return reject(new UserFriendlyError("No active call in this room"));
|
||||
}
|
||||
call.setRemoteOnHold(true);
|
||||
return success();
|
||||
|
@ -1323,7 +1351,7 @@ export const Commands = [
|
|||
runFn: function (roomId, args) {
|
||||
const call = LegacyCallHandler.instance.getCallForRoom(roomId);
|
||||
if (!call) {
|
||||
return reject(newTranslatableError("No active call in this room"));
|
||||
return reject(new UserFriendlyError("No active call in this room"));
|
||||
}
|
||||
call.setRemoteOnHold(false);
|
||||
return success();
|
||||
|
@ -1337,7 +1365,7 @@ export const Commands = [
|
|||
isEnabled: () => !isCurrentLocalRoom(),
|
||||
runFn: function (roomId, args) {
|
||||
const room = MatrixClientPeg.get().getRoom(roomId);
|
||||
if (!room) return reject(newTranslatableError("Could not find room"));
|
||||
if (!room) return reject(new UserFriendlyError("Could not find room"));
|
||||
return success(guessAndSetDMRoom(room, true));
|
||||
},
|
||||
renderingTypes: [TimelineRenderingType.Room],
|
||||
|
@ -1349,7 +1377,7 @@ export const Commands = [
|
|||
isEnabled: () => !isCurrentLocalRoom(),
|
||||
runFn: function (roomId, args) {
|
||||
const room = MatrixClientPeg.get().getRoom(roomId);
|
||||
if (!room) return reject(newTranslatableError("Could not find room"));
|
||||
if (!room) return reject(new UserFriendlyError("Could not find room"));
|
||||
return success(guessAndSetDMRoom(room, false));
|
||||
},
|
||||
renderingTypes: [TimelineRenderingType.Room],
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue