fix(errors): surface previously-silent async failures (DP1/DP4/DP6)
Several fire-and-forget promises failed silently: - RoomInput slash-command exe() rejections reset the editor as if they succeeded; now caught and shown via an error toast. - Favourite / low-priority room-tag toggles (setRoomTag/deleteRoomTag) swallowed rejections; now surfaced via the same error toast. - Call-decline sendEvent(RTCDecline) was uncaught; now logged best-effort while the local UI still dismisses. Adds a shared createErrorToast builder mirroring createDownloadToast. /kick and /ban route through rateLimitedActions, whose to() helper swallows non-429 errors, so those two can still resolve on failure — noted in code; the top-level exe() catch covers everything that does reject. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -136,6 +136,7 @@ import { ScheduleMessageModal } from './ScheduleMessageModal';
|
||||
import { ScheduledMessagesTray } from './ScheduledMessagesTray';
|
||||
import { DraftIndicator } from './DraftIndicator';
|
||||
import { scheduledMessagesAtom } from '../../state/scheduledMessages';
|
||||
import { createErrorToast, toastQueueAtom } from '../../state/toast';
|
||||
import { getThreadDraftKey } from '../../state/room/thread';
|
||||
|
||||
const GifPicker = React.lazy(() =>
|
||||
@@ -184,6 +185,7 @@ export const RoomInput = forwardRef<HTMLDivElement, RoomInputProps>(
|
||||
const [scheduleOpen, setScheduleOpen] = useState(false);
|
||||
const [scheduleContent, setScheduleContent] = useState<IContent | null>(null);
|
||||
const setScheduledMessages = useSetAtom(scheduledMessagesAtom);
|
||||
const setToast = useSetAtom(toastQueueAtom);
|
||||
|
||||
const alive = useAlive();
|
||||
// Scope drafts/replies/uploads by thread so a thread composer stays fully
|
||||
@@ -539,7 +541,22 @@ export const RoomInput = forwardRef<HTMLDivElement, RoomInputProps>(
|
||||
} else if (commandName) {
|
||||
const commandContent = commands[commandName as Command];
|
||||
if (commandContent) {
|
||||
commandContent.exe(plainText);
|
||||
// Fire-and-forget by design (the editor resets immediately for UX), but
|
||||
// surface a rejection instead of failing silently. NOTE: /kick and /ban
|
||||
// route through rateLimitedActions (utils/matrix.ts), whose to() helper
|
||||
// swallows non-429 errors, so those two commands can still resolve even
|
||||
// when the underlying kick/ban failed — this catch only covers errors
|
||||
// that actually reject out of exe().
|
||||
commandContent.exe(plainText).catch((err) => {
|
||||
console.error(`Failed to run /${commandName} command:`, err);
|
||||
setToast(
|
||||
createErrorToast(
|
||||
`The /${commandName} command failed. Please try again.`,
|
||||
Icons.Warning,
|
||||
'Command failed',
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
resetEditor(editor);
|
||||
resetEditorHistory(editor);
|
||||
@@ -600,6 +617,7 @@ export const RoomInput = forwardRef<HTMLDivElement, RoomInputProps>(
|
||||
setReplyDraft,
|
||||
isMarkdown,
|
||||
commands,
|
||||
setToast,
|
||||
]);
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user