import { i18next, Toast } from '@tencentcloud/uikit-base-component-react'; import { Dialog } from '../../../../baseComp/Dialog'; type Translate = (key: string) => string; // Activation guide for cloud search, picked by current UI language: // Chinese users go to the domestic docs site, others to the international one. const CLOUD_SEARCH_ACTIVATION_DOC_URL_ZH = 'https://cloud.tencent.com/document/product/269/83749#cc18305a-423b-4cb7-9a9e-9d3a8db8fb92'; const CLOUD_SEARCH_ACTIVATION_DOC_URL_EN = 'https://www.tencentcloud.com/document/product/1047/50055#f19e6762-f381-456c-a431-8091c17b56b9'; function getCloudSearchActivationDocURL(): string { return i18next.language?.startsWith('zh') ? CLOUD_SEARCH_ACTIVATION_DOC_URL_ZH : CLOUD_SEARCH_ACTIVATION_DOC_URL_EN; } // SDK error codes observed when a search request has been rate-limited. // The underlying IM SDK reports these in different shapes depending on the // platform/transport, so both numeric and string codes are covered: // - 2996: generic SDK-level frequency control ("超出 SDK 频率控制") // - 'SearchOverLimit': web IM SDK's string error code for search rate limiting // - 60018: cloud message search business rate-limit code const SEARCH_RATE_LIMIT_ERROR_CODES: ReadonlyArray = [ 2996, 'SearchOverLimit', 60018, ]; // SearchUnable: cloud search is not activated for the current SDKAppID. function isSearchUnableError(error: unknown): boolean { const code = (error as { code?: number | string } | null | undefined)?.code; return code === 'SearchUnable'; } function isSearchRateLimitError(error: unknown): boolean { const code = (error as { code?: number | string } | null | undefined)?.code; return code !== undefined && SEARCH_RATE_LIMIT_ERROR_CODES.includes(code); } // 2801: search request timed out on the SDK side. function isTimeoutError(error: unknown): boolean { const code = (error as { code?: number | string } | null | undefined)?.code; return code === 2801; } function openSearchUnableDialog(t: Translate): string { const message = t('Search.cloud_search_not_activated_content'); Dialog.open({ title: t('Search.tip'), // Do not return focus to the previously focused element (e.g. the search // input) on close, otherwise the input's focus handler would re-trigger // the search and reopen this dialog in an infinite loop. finalFocus: false, // The dialog may open while a mouse press is still in progress (the same // press that focused the search input). Its mouseup would then land on // the backdrop and instantly close the dialog, so backdrop-closing is // disabled; use the buttons / X / Esc instead. maskClosable: false, content: (
{message}
{t('Search.view_details')}
), }); return message; } /** * Central error handler for every SearchNew search request. * * SearchUnable errors (cloud search not activated) pop a guidance Dialog. * Rate-limit errors (see SEARCH_RATE_LIMIT_ERROR_CODES) and timeout errors * (2801) pop a Toast and return a friendly message for the inline status * area. Any other error is left as-is (raw SDK message, no Toast) so * unrelated failures keep their existing behavior. */ export function handleSearchError(t: Translate, error: unknown): string { if (isSearchUnableError(error)) { return openSearchUnableDialog(t); } if (isSearchRateLimitError(error)) { const message = t('Search.search_too_frequent'); Toast.error({ message }); return message; } if (isTimeoutError(error)) { const message = t('Search.search_timeout'); Toast.error({ message }); return message; } return (error as Error)?.message ?? ''; }