diff --git a/app/components/chat/Chat.client.tsx b/app/components/chat/Chat.client.tsx
index dff7598..390f2ff 100644
--- a/app/components/chat/Chat.client.tsx
+++ b/app/components/chat/Chat.client.tsx
@@ -12,6 +12,7 @@ import { fileModificationsToHTML } from '~/utils/diff';
import { cubicEasingFn } from '~/utils/easings';
import { createScopedLogger, renderLogger } from '~/utils/logger';
import { BaseChat } from './BaseChat';
+import { SubscriptionDialog } from '~/components/auth/SubscriptionDialog';
const toastAnimation = cssTransition({
enter: 'animated fadeInRight',
@@ -24,10 +25,28 @@ export function Chat() {
renderLogger.trace('Chat');
const { ready, initialMessages, storeMessageHistory } = useChatHistory();
+ const [isSubscriptionDialogOpen, setIsSubscriptionDialogOpen] = useState(false);
+
+ const handleError = (error: Error) => {
+ console.error('Chat error:', error);
+ if (error.message === 'Insufficient token balance') {
+ toast.error('代币余额不足,请购买更多代币或升级订阅计划', {
+ onClick: () => setIsSubscriptionDialogOpen(true),
+ });
+ } else {
+ toast.error('发生错误,请稍后重试');
+ }
+ };
return (
<>
- {ready && }
+ {ready && (
+
+ )}
{
return (
@@ -48,23 +67,27 @@ export function Chat() {
return ;
}
}
-
return undefined;
}}
position="bottom-right"
pauseOnFocusLoss
transition={toastAnimation}
/>
+ setIsSubscriptionDialogOpen(false)}
+ />
>
);
}
-interface ChatProps {
+interface ChatImplProps {
initialMessages: Message[];
- storeMessageHistory: (messages: Message[]) => Promise;
+ storeMessageHistory: (messages: Message[]) => void | Promise;
+ onError: (error: Error) => void;
}
-export const ChatImpl = memo(({ initialMessages, storeMessageHistory }: ChatProps) => {
+const ChatImpl = memo(function ChatImpl({ initialMessages, storeMessageHistory, onError }: ChatImplProps) {
useShortcuts();
const textareaRef = useRef(null);
@@ -75,7 +98,7 @@ export const ChatImpl = memo(({ initialMessages, storeMessageHistory }: ChatProp
const [animationScope, animate] = useAnimate();
- const { messages, isLoading, input, handleInputChange, setInput, stop, append } = useChat({
+ const { messages, isLoading, input, handleInputChange, setInput, stop, append, reload, error } = useChat({
api: '/api/chat',
onError: (error) => {
logger.error('Request failed\n\n', error);
@@ -85,6 +108,15 @@ export const ChatImpl = memo(({ initialMessages, storeMessageHistory }: ChatProp
logger.debug('Finished streaming');
},
initialMessages,
+ onResponse(response) {
+ if (response.status === 401) {
+ // 处理未授权错误
+ onError(new Error('Unauthorized'));
+ } else if (response.status === 402) {
+ // 处理代币不足错误
+ onError(new Error('Insufficient token balance'));
+ }
+ },
});
const { enhancingPrompt, promptEnhanced, enhancePrompt, resetEnhancer } = usePromptEnhancer();
@@ -100,9 +132,12 @@ export const ChatImpl = memo(({ initialMessages, storeMessageHistory }: ChatProp
parseMessages(messages, isLoading);
if (messages.length > initialMessages.length) {
- storeMessageHistory(messages).catch((error) => toast.error(error.message));
+ const result = storeMessageHistory(messages);
+ if (result instanceof Promise) {
+ result.catch((error: Error) => toast.error(error.message));
+ }
}
- }, [messages, isLoading, parseMessages]);
+ }, [messages, isLoading, parseMessages, initialMessages.length, storeMessageHistory]);
const scrollTextArea = () => {
const textarea = textareaRef.current;
@@ -198,6 +233,12 @@ export const ChatImpl = memo(({ initialMessages, storeMessageHistory }: ChatProp
const [messageRef, scrollRef] = useSnapScroll();
+ useEffect(() => {
+ if (error) {
+ onError(error);
+ }
+ }, [error, onError]);
+
return (