LibreChat/client/src/utils/drafts.ts
Danny Avila 9cb5ac63f8
🫧 refactor: Clear Drafts and Surface Error on Expired SSE Stream (#12309)
* refactor: error handling in useResumableSSE for 404 responses

- Added logic to clear drafts from localStorage when a 404 error occurs.
- Integrated errorHandler to notify users of the error condition.
- Introduced comprehensive tests to validate the new behavior, ensuring drafts are cleared and error handling is triggered correctly.C

* feat: add STREAM_EXPIRED error handling and message localization

- Introduced handling for STREAM_EXPIRED errors in useResumableSSE, updating errorHandler to provide relevant feedback.
- Added a new error message for STREAM_EXPIRED in translation files for user notifications.
- Updated tests to ensure proper error handling and message verification for STREAM_EXPIRED scenarios.

* refactor: replace clearDraft with clearAllDrafts utility

- Removed the clearDraft function from useResumableSSE and useSSE hooks, replacing it with the new clearAllDrafts utility for better draft management.
- Updated localStorage interactions to ensure both text and file drafts are cleared consistently for a conversation.
- Enhanced code readability and maintainability by centralizing draft clearing logic.
2026-03-19 14:51:28 -04:00

46 lines
1.6 KiB
TypeScript

import debounce from 'lodash/debounce';
import { Constants, LocalStorageKeys } from 'librechat-data-provider';
export const clearDraft = debounce((id?: string | null) => {
localStorage.removeItem(`${LocalStorageKeys.TEXT_DRAFT}${id ?? ''}`);
}, 2500);
/** Synchronously removes both text and file drafts for a conversation (or NEW_CONVO fallback) */
export const clearAllDrafts = (conversationId?: string | null) => {
const key = conversationId || Constants.NEW_CONVO;
localStorage.removeItem(`${LocalStorageKeys.TEXT_DRAFT}${key}`);
localStorage.removeItem(`${LocalStorageKeys.FILES_DRAFT}${key}`);
};
export const encodeBase64 = (plainText: string): string => {
try {
const textBytes = new TextEncoder().encode(plainText);
return btoa(String.fromCharCode(...textBytes));
} catch {
return '';
}
};
export const decodeBase64 = (base64String: string): string => {
try {
const bytes = atob(base64String);
const uint8Array = new Uint8Array(bytes.length);
for (let i = 0; i < bytes.length; i++) {
uint8Array[i] = bytes.charCodeAt(i);
}
return new TextDecoder().decode(uint8Array);
} catch {
return '';
}
};
export const setDraft = ({ id, value }: { id: string; value?: string }) => {
if (value && value.length > 1) {
localStorage.setItem(`${LocalStorageKeys.TEXT_DRAFT}${id}`, encodeBase64(value));
return;
}
localStorage.removeItem(`${LocalStorageKeys.TEXT_DRAFT}${id}`);
};
export const getDraft = (id?: string): string | null =>
decodeBase64((localStorage.getItem(`${LocalStorageKeys.TEXT_DRAFT}${id ?? ''}`) ?? '') || '');