mirror of
https://github.com/vector-im/element-web.git
synced 2026-05-05 20:26:19 +02:00
misc updates
This commit is contained in:
parent
74b78bf00c
commit
16eb46fb17
@ -110,7 +110,6 @@ WithLocalRoomRetry.args = {
|
||||
export const WithMessageRejected = Template.bind({});
|
||||
WithMessageRejected.args = {
|
||||
state: RoomStatusBarState.MessageRejected,
|
||||
onResendAllClick: undefined,
|
||||
harms: ["org.matrix.msc4387.harassment"],
|
||||
};
|
||||
|
||||
@ -123,6 +122,7 @@ WithMessageRejectedCanRetryInTime.args = {
|
||||
onResendAllClick: undefined,
|
||||
canRetryInSeconds: 5,
|
||||
harms: [],
|
||||
isResending: false,
|
||||
};
|
||||
|
||||
/**
|
||||
@ -132,6 +132,7 @@ export const WithMessageRejectedCanRetry = Template.bind({});
|
||||
WithMessageRejectedCanRetry.args = {
|
||||
state: RoomStatusBarState.MessageRejected,
|
||||
harms: [],
|
||||
isResending: false,
|
||||
};
|
||||
|
||||
/**
|
||||
@ -151,15 +152,7 @@ export const WithMessageRejectedWithKnownHarm = Template.bind({});
|
||||
WithMessageRejectedWithKnownHarm.args = {
|
||||
state: RoomStatusBarState.MessageRejected,
|
||||
harms: ["org.matrix.msc4387.spam"],
|
||||
};
|
||||
|
||||
/**
|
||||
* Rendered when a message was rejected by the server, and we use the generic message.
|
||||
*/
|
||||
export const WithMessageRejectedWithUnknownHarm = Template.bind({});
|
||||
WithMessageRejectedWithUnknownHarm.args = {
|
||||
state: RoomStatusBarState.MessageRejected,
|
||||
harms: ["any.old.harm"],
|
||||
isResending: false,
|
||||
};
|
||||
|
||||
/**
|
||||
@ -170,4 +163,5 @@ WithMessageRejectedWithServerMessage.args = {
|
||||
state: RoomStatusBarState.MessageRejected,
|
||||
harms: ["any.old.harm"],
|
||||
serverError: "OurServer rejects this content",
|
||||
isResending: false,
|
||||
};
|
||||
|
||||
@ -18,22 +18,22 @@ export interface RoomStatusBarViewActions {
|
||||
/**
|
||||
* Called when the user clicks on the 'resend all' button in the 'unsent messages' bar.
|
||||
*/
|
||||
onResendAllClick?: () => void;
|
||||
onResendAllClick: () => void;
|
||||
|
||||
/**
|
||||
* Called when the user clicks on the 'cancel all' button in the 'unsent messages' bar.
|
||||
*/
|
||||
onDeleteAllClick?: () => void;
|
||||
onDeleteAllClick: () => void;
|
||||
|
||||
/**
|
||||
* Called when the user clicks on the 'Retry' button in the 'failed to start chat' bar.
|
||||
*/
|
||||
onRetryRoomCreationClick?: () => void;
|
||||
onRetryRoomCreationClick: () => void;
|
||||
|
||||
/**
|
||||
* Called when the user clicks on the 'Review Terms and Conditions' button.
|
||||
*/
|
||||
onTermsAndConditionsClicked?: () => void;
|
||||
onTermsAndConditionsClicked: () => void;
|
||||
}
|
||||
|
||||
export enum RoomStatusBarState {
|
||||
@ -72,13 +72,20 @@ export interface RoomStatusBarLocalRoomError {
|
||||
state: RoomStatusBarState.LocalRoomFailed;
|
||||
}
|
||||
|
||||
export interface RoomStatusBarMessageRejected {
|
||||
export interface RoomStatusBarMessageRejectedRetryable {
|
||||
state: RoomStatusBarState.MessageRejected;
|
||||
canRetryInSeconds?: number;
|
||||
isResending: boolean;
|
||||
harms: string[];
|
||||
serverError?: string;
|
||||
}
|
||||
export interface RoomStatusBarMessageRejectedUnretryable {
|
||||
state: RoomStatusBarState.MessageRejected;
|
||||
harms: string[];
|
||||
serverError?: string;
|
||||
}
|
||||
|
||||
type RoomStatusBarMessageRejected = RoomStatusBarMessageRejectedRetryable | RoomStatusBarMessageRejectedUnretryable;
|
||||
|
||||
export type RoomStatusBarViewSnapshot =
|
||||
| RoomStatusBarNoConnection
|
||||
@ -197,10 +204,14 @@ function RoomStatusBarViewMessageRejected({
|
||||
);
|
||||
|
||||
let subtitleText: string;
|
||||
if (onResendAllClick) {
|
||||
subtitleText = _t("room|status_bar|select_messages_to_retry");
|
||||
} else if (!onResendAllClick && snapshot.canRetryInSeconds !== undefined) {
|
||||
subtitleText = _t("room|status_bar|message_rejected|can_retry_in", { count: snapshot.canRetryInSeconds });
|
||||
const canRetry = "isResending" in snapshot;
|
||||
const isResending = "isResending" in snapshot && snapshot.isResending;
|
||||
if (canRetry) {
|
||||
if (snapshot.canRetryInSeconds !== undefined) {
|
||||
subtitleText = _t("room|status_bar|message_rejected|can_retry_in", { count: snapshot.canRetryInSeconds });
|
||||
} else {
|
||||
subtitleText = _t("room|status_bar|select_messages_to_retry");
|
||||
}
|
||||
} else {
|
||||
subtitleText = _t("room|status_bar|message_rejected|cannot_retry");
|
||||
}
|
||||
@ -210,30 +221,25 @@ function RoomStatusBarViewMessageRejected({
|
||||
role="status"
|
||||
type="critical"
|
||||
actions={
|
||||
snapshot.isResending ? (
|
||||
isResending ? (
|
||||
<InlineSpinner />
|
||||
) : (
|
||||
<>
|
||||
{onDeleteAllClick && (
|
||||
<Button
|
||||
size="sm"
|
||||
kind="destructive"
|
||||
Icon={DeleteIcon}
|
||||
disabled={snapshot.isResending}
|
||||
onClick={deleteAllClick}
|
||||
>
|
||||
{_t("room|status_bar|delete_all")}
|
||||
</Button>
|
||||
)}
|
||||
{(onResendAllClick || snapshot.canRetryInSeconds) && (
|
||||
<Button
|
||||
size="sm"
|
||||
kind="destructive"
|
||||
Icon={DeleteIcon}
|
||||
disabled={isResending}
|
||||
onClick={deleteAllClick}
|
||||
>
|
||||
{_t("room|status_bar|delete_all")}
|
||||
</Button>
|
||||
{canRetry && (
|
||||
<Button
|
||||
size="sm"
|
||||
kind="secondary"
|
||||
Icon={RestartIcon}
|
||||
disabled={
|
||||
snapshot.isResending ||
|
||||
!!(snapshot.canRetryInSeconds && snapshot.canRetryInSeconds > 0)
|
||||
}
|
||||
disabled={!!(snapshot.canRetryInSeconds && snapshot.canRetryInSeconds > 0)}
|
||||
onClick={resendClick}
|
||||
className={styles.container}
|
||||
>
|
||||
@ -412,29 +418,18 @@ export function RoomStatusBarView({ vm }: Readonly<RoomStatusBarViewProps>): JSX
|
||||
<InlineSpinner />
|
||||
) : (
|
||||
<>
|
||||
{vm.onDeleteAllClick && (
|
||||
<Button
|
||||
size="sm"
|
||||
kind="destructive"
|
||||
Icon={DeleteIcon}
|
||||
disabled={snapshot.isResending}
|
||||
onClick={deleteAllClick}
|
||||
>
|
||||
{_t("room|status_bar|delete_all")}
|
||||
</Button>
|
||||
)}
|
||||
{vm.onResendAllClick && (
|
||||
<Button
|
||||
size="sm"
|
||||
kind="secondary"
|
||||
Icon={RestartIcon}
|
||||
disabled={snapshot.isResending}
|
||||
onClick={resendClick}
|
||||
className={styles.container}
|
||||
>
|
||||
{_t("room|status_bar|retry_all")}
|
||||
</Button>
|
||||
)}
|
||||
<Button size="sm" kind="destructive" Icon={DeleteIcon} onClick={deleteAllClick}>
|
||||
{_t("room|status_bar|delete_all")}
|
||||
</Button>
|
||||
<Button
|
||||
size="sm"
|
||||
kind="secondary"
|
||||
Icon={RestartIcon}
|
||||
onClick={resendClick}
|
||||
className={styles.container}
|
||||
>
|
||||
{_t("room|status_bar|retry_all")}
|
||||
</Button>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
@ -24,67 +24,95 @@ test.describe("Safety error rendering", () => {
|
||||
},
|
||||
});
|
||||
|
||||
test("should show a safety rejection of a message with no harms", { tag: ["@screenshot"] }, async ({ page, app, room, user }) => {
|
||||
await page.route("**/_matrix/client/v3/**/send/**", async (route) => {
|
||||
await route.fulfill({ json: {errcode: MatrixSafetyErrorCode.name, error: "Server provided error"}, status: 400 });
|
||||
});
|
||||
await app.viewRoomById(room.roomId);
|
||||
const composer = app.getComposerField();
|
||||
await composer.fill("Hello!");
|
||||
await composer.press("Enter");
|
||||
const statusBar = page.getByRole("alert", { name: new RegExp(/.*Message rejected.*/)});
|
||||
await expect(statusBar).toMatchScreenshot("message-no-harms.png");
|
||||
});
|
||||
test(
|
||||
"should show a safety rejection of a message with no harms",
|
||||
{ tag: ["@screenshot"] },
|
||||
async ({ page, app, room, user }) => {
|
||||
await page.route("**/_matrix/client/v3/**/send/**", async (route) => {
|
||||
await route.fulfill({
|
||||
json: { errcode: MatrixSafetyErrorCode.name, error: "Server provided error" },
|
||||
status: 400,
|
||||
});
|
||||
});
|
||||
await app.viewRoomById(room.roomId);
|
||||
const composer = app.getComposerField();
|
||||
await composer.fill("Hello!");
|
||||
await composer.press("Enter");
|
||||
const statusBar = page.getByRole("status", { name: new RegExp(/.*Message rejected.*/) });
|
||||
await expect(statusBar).toMatchScreenshot("message-no-harms.png");
|
||||
},
|
||||
);
|
||||
|
||||
test("should show a safety rejection of a message with only unknown harms", { tag: ["@screenshot"] }, async ({ page, app, room, user }) => {
|
||||
await page.route("**/_matrix/client/v3/**/send/**", async (route) => {
|
||||
await route.fulfill({ json: {errcode: MatrixSafetyErrorCode.name, error: "Server provided error", harms: ["org.example.unknown-harm"]}, status: 400 });
|
||||
});
|
||||
await app.viewRoomById(room.roomId);
|
||||
const composer = app.getComposerField();
|
||||
await composer.fill("Hello!");
|
||||
await composer.press("Enter");
|
||||
const statusBar = page.getByRole("alert", { name: new RegExp(/.*Message rejected.*/)});
|
||||
await expect(statusBar).toMatchScreenshot("message-no-harms.png");
|
||||
});
|
||||
test(
|
||||
"should show a safety rejection of a message with only unknown harms",
|
||||
{ tag: ["@screenshot"] },
|
||||
async ({ page, app, room, user }) => {
|
||||
await page.route("**/_matrix/client/v3/**/send/**", async (route) => {
|
||||
await route.fulfill({
|
||||
json: {
|
||||
errcode: MatrixSafetyErrorCode.name,
|
||||
error: "Server provided error",
|
||||
harms: ["org.example.unknown-harm"],
|
||||
},
|
||||
status: 400,
|
||||
});
|
||||
});
|
||||
await app.viewRoomById(room.roomId);
|
||||
const composer = app.getComposerField();
|
||||
await composer.fill("Hello!");
|
||||
await composer.press("Enter");
|
||||
const statusBar = page.getByRole("status", { name: new RegExp(/.*Message rejected.*/) });
|
||||
await expect(statusBar).toMatchScreenshot("message-no-harms.png");
|
||||
},
|
||||
);
|
||||
|
||||
test("should show a simple rejection of a message with spam harm", { tag: ["@screenshot"] }, async ({ page, app, room, user }) => {
|
||||
await page.route("**/_matrix/client/v3/**/send/**", async (route) => {
|
||||
await route.fulfill({ json: {errcode: MatrixSafetyErrorCode.name, error: "Ignored error", harms: ["org.matrix.msc4387.spam"]}, status: 400 });
|
||||
});
|
||||
await app.viewRoomById(room.roomId);
|
||||
const composer = app.getComposerField();
|
||||
await composer.fill("Hello!");
|
||||
await composer.press("Enter");
|
||||
const statusBar = page.getByRole("alert", { name: new RegExp(/.*Message rejected.*/)});
|
||||
await expect(statusBar).toMatchScreenshot("message-spam.png");
|
||||
});
|
||||
test("should show a simple rejection of a message with spam harm with expiry", { tag: ["@screenshot"] }, async ({ page, app, room, user }) => {
|
||||
await page.route("**/_matrix/client/v3/**/send/**", async (route) => {
|
||||
await route.fulfill({ json: {errcode: MatrixSafetyErrorCode.name, error: "Ignored error", harms: ["org.matrix.msc4387.spam"], expiry: Date.now() + 1000}, status: 400 });
|
||||
});
|
||||
await app.viewRoomById(room.roomId);
|
||||
const composer = app.getComposerField();
|
||||
await composer.fill("Hello!");
|
||||
await composer.press("Enter");
|
||||
const statusBar = page.getByRole("alert", { name: new RegExp(/.*Message rejected.*/)});
|
||||
await expect(statusBar).toMatchScreenshot("message-spam-expiry.png");
|
||||
// Permit a retry
|
||||
await page.unroute("**/_matrix/client/v3/**/send/**");
|
||||
await statusBar.getByRole("button", { name: "Retry"}).click({ timeout: 1100 });
|
||||
await expect(statusBar).not.toBeVisible();
|
||||
});
|
||||
test("should show a simple rejection of an upload", { }, async ({ page, app, room, user }) => {
|
||||
await page.route("**/_matrix/media/v3/upload**", async (route) => {
|
||||
await route.fulfill({ json: {errcode: MatrixSafetyErrorCode.name, error: "Ignored error", harms: ["org.matrix.msc4387.spam"], expiry: Date.now() + 1000}, status: 400 });
|
||||
});
|
||||
await app.viewRoomById(room.roomId);
|
||||
// Start waiting for file chooser before clicking. Note no await.
|
||||
const fileChooserPromise = page.waitForEvent('filechooser');
|
||||
await page.getByRole("button", { name: "Attachment"}).click();
|
||||
const fileChooser = await fileChooserPromise;
|
||||
await fileChooser.setFiles("playwright/sample-files/riot.png");
|
||||
await page.getByRole("button", { name: "Upload"}).click();
|
||||
await expect(page.getByRole("dialog", { name: "Upload Failed" })).toBeVisible();
|
||||
});
|
||||
});
|
||||
test(
|
||||
"should show a simple rejection of a message with spam harm",
|
||||
{ tag: ["@screenshot"] },
|
||||
async ({ page, app, room, user }) => {
|
||||
await page.route("**/_matrix/client/v3/**/send/**", async (route) => {
|
||||
await route.fulfill({
|
||||
json: {
|
||||
errcode: MatrixSafetyErrorCode.name,
|
||||
error: "Ignored error",
|
||||
harms: ["org.matrix.msc4387.spam"],
|
||||
},
|
||||
status: 400,
|
||||
});
|
||||
});
|
||||
await app.viewRoomById(room.roomId);
|
||||
const composer = app.getComposerField();
|
||||
await composer.fill("Hello!");
|
||||
await composer.press("Enter");
|
||||
const statusBar = page.getByRole("status", { name: new RegExp(/.*Message rejected.*/) });
|
||||
await expect(statusBar).toMatchScreenshot("message-spam.png");
|
||||
},
|
||||
);
|
||||
test(
|
||||
"should show a simple rejection of a message with spam harm with expiry",
|
||||
{ tag: ["@screenshot"] },
|
||||
async ({ page, app, room, user }) => {
|
||||
await page.route("**/_matrix/client/v3/**/send/**", async (route) => {
|
||||
await route.fulfill({
|
||||
json: {
|
||||
errcode: MatrixSafetyErrorCode.name,
|
||||
error: "Ignored error",
|
||||
harms: ["org.matrix.msc4387.spam"],
|
||||
expiry: Date.now() + 1000,
|
||||
},
|
||||
status: 400,
|
||||
});
|
||||
});
|
||||
await app.viewRoomById(room.roomId);
|
||||
const composer = app.getComposerField();
|
||||
await composer.fill("Hello!");
|
||||
await composer.press("Enter");
|
||||
const statusBar = page.getByRole("status", { name: new RegExp(/.*Message rejected.*/) });
|
||||
await expect(statusBar).toMatchScreenshot("message-spam-expiry.png");
|
||||
// Permit a retry
|
||||
await page.unroute("**/_matrix/client/v3/**/send/**");
|
||||
await statusBar.getByRole("button", { name: "Retry all" }).click({ timeout: 1500 });
|
||||
await expect(statusBar).not.toBeVisible();
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 13 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 17 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 14 KiB |
@ -93,12 +93,18 @@ export class RoomStatusBarViewModel
|
||||
};
|
||||
}
|
||||
if (safetyError) {
|
||||
const canRetry = !!safetyError.expiry;
|
||||
return {
|
||||
state: RoomStatusBarState.MessageRejected,
|
||||
harms: [...safetyError.harms],
|
||||
serverError: safetyError.error,
|
||||
isResending,
|
||||
canRetryInSeconds: safetyError.expiry && Math.ceil((safetyError.expiry.getTime() - Date.now()) / 1000),
|
||||
...(canRetry
|
||||
? {
|
||||
isResending,
|
||||
canRetryInSeconds:
|
||||
safetyError.expiry && Math.ceil((safetyError.expiry.getTime() - Date.now()) / 1000),
|
||||
}
|
||||
: undefined),
|
||||
};
|
||||
}
|
||||
return {
|
||||
@ -187,9 +193,10 @@ export class RoomStatusBarViewModel
|
||||
}
|
||||
if (
|
||||
this.snapshot.current.state === RoomStatusBarState.MessageRejected &&
|
||||
"canRetryInSeconds" in this.snapshot.current &&
|
||||
this.snapshot.current.canRetryInSeconds
|
||||
) {
|
||||
this.timeout = setTimeout(() => this.setSnapshot, this.snapshot.current.canRetryInSeconds * 1000);
|
||||
this.timeout = setTimeout(() => this.setSnapshot(), this.snapshot.current.canRetryInSeconds * 1000);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user