Update view model

This commit is contained in:
Half-Shot 2025-12-30 11:28:49 +00:00
parent 3a76a997ac
commit 74b78bf00c
2 changed files with 61 additions and 9 deletions

View File

@ -2141,7 +2141,15 @@
"select_messages_to_retry": "You can select all or individual messages to retry or delete",
"server_connectivity_lost_description": "Sent messages will be stored until your connection has returned.",
"server_connectivity_lost_title": "Connectivity to the server has been lost.",
"some_messages_not_sent": "Some of your messages have not been sent"
"some_messages_not_sent": "Some of your messages have not been sent",
"message_rejected": {
"title": "Message rejected: %(harm)s",
"can_retry_in": {
"one": "You may attempt to send new messages in 1 second.",
"other": "You may attempt to send new messages in %(count)s seconds."
},
"cannot_retry": "You are not allowed to retry this message"
}
},
"unknown_status_code_for_timeline_jump": "unknown status code",
"unread_notifications_predecessor": {
@ -4264,5 +4272,29 @@
"userInputs": "There should not be any personal or page related data.",
"wordByItself": "A word by itself is easy to guess"
}
},
"safety": {
"harms": {
"generic": "The message was blocked due to harmful content",
"multiple": "This message was blocked due to multiple harms",
"spam": "The message is considered spam.",
"spam.fraud": "The message contains fraudulent content",
"spam.impersonation": "The message is attempting to impersonate someone",
"spam.election_interference": "The message contains election interference content",
"spam.flooding": "You are sending too many messages",
"adult": "The message contains adult content",
"harassment": "Message detected as containing harassment.",
"harassment.trolling": "The message is contains trolling content.",
"harassment.targeted": "The message contains targeted harassment.",
"harassment.hate": "The message contains hateful content.",
"harassment.doxxing": "The message contains doxxing content.",
"violence": "The message contains violent content.",
"child_safety": "The message has contains child safety risks.",
"danger": "The message contains potentially dangerous content.",
"tos": "The message is against the terms of service.",
"tos.hacking": "The message is against the terms of service (hacking).",
"tos.prohibited": "The message is against the terms of service (prohibited content).",
"tos.ban_evasion": "The message is against the terms of service (ban evasion detected)."
}
}
}

View File

@ -19,6 +19,7 @@ import {
type MatrixError,
RoomEvent,
EventStatus,
MatrixSafetyError,
} from "matrix-js-sdk/src/matrix";
import { MatrixClientPeg } from "../../MatrixClientPeg";
@ -50,6 +51,7 @@ export class RoomStatusBarViewModel
private static readonly determineStateForUnreadMessages = (
room: Room,
hasClickedTermsAndConditions: boolean,
isResending: boolean,
): RoomStatusBarViewSnapshot => {
const unsentMessages = room.getPendingEvents().filter((ev) => ev.status === EventStatus.NOT_SENT);
if (unsentMessages.length === 0) {
@ -61,10 +63,11 @@ export class RoomStatusBarViewModel
// The user has just clicked (and we assume accepted) the terms and contitions, so show them the retry buttons
return {
state: RoomStatusBarState.UnsentMessages,
isResending: false,
isResending,
};
}
let resourceLimitError: MatrixError | null = null;
let safetyError: MatrixSafetyError | null = null;
for (const m of unsentMessages) {
if (m.error) {
if (m.error.errcode === "M_CONSENT_NOT_GIVEN") {
@ -77,6 +80,9 @@ export class RoomStatusBarViewModel
if (m.error.errcode === "M_RESOURCE_LIMIT_EXCEEDED") {
resourceLimitError = m.error;
}
if (m.error instanceof MatrixSafetyError) {
safetyError = m.error;
}
}
}
if (resourceLimitError) {
@ -86,6 +92,15 @@ export class RoomStatusBarViewModel
adminContactHref: resourceLimitError.data.admin_contact,
};
}
if (safetyError) {
return {
state: RoomStatusBarState.MessageRejected,
harms: [...safetyError.harms],
serverError: safetyError.error,
isResending,
canRetryInSeconds: safetyError.expiry && Math.ceil((safetyError.expiry.getTime() - Date.now()) / 1000),
};
}
return {
state: RoomStatusBarState.UnsentMessages,
isResending: false,
@ -107,12 +122,6 @@ export class RoomStatusBarViewModel
}
// If we're in the process of resending, don't flicker.
if (isResending) {
return {
state: RoomStatusBarState.UnsentMessages,
isResending,
};
}
const syncState = client.getSyncState();
// Highest priority.
@ -134,10 +143,11 @@ export class RoomStatusBarViewModel
}
// Then check messages.
return this.determineStateForUnreadMessages(room, hasClickedTermsAndConditions);
return this.determineStateForUnreadMessages(room, hasClickedTermsAndConditions, isResending);
};
private readonly client: MatrixClient;
private timeout?: ReturnType<typeof globalThis.setTimeout>;
public constructor(props: Props) {
const client = MatrixClientPeg.safeGet();
@ -159,6 +169,10 @@ export class RoomStatusBarViewModel
private hasClickedTermsAndConditions = false;
private setSnapshot(): void {
if (this.timeout) {
// If we had a timer going, clear it.
clearTimeout(this.timeout);
}
this.snapshot.set(
RoomStatusBarViewModel.computeSnapshot(
this.props.room,
@ -171,6 +185,12 @@ export class RoomStatusBarViewModel
if (this.hasClickedTermsAndConditions && !this.snapshot.current.state) {
this.hasClickedTermsAndConditions = false;
}
if (
this.snapshot.current.state === RoomStatusBarState.MessageRejected &&
this.snapshot.current.canRetryInSeconds
) {
this.timeout = setTimeout(() => this.setSnapshot, this.snapshot.current.canRetryInSeconds * 1000);
}
}
public dispose(): void {