diff --git a/.env b/.env index 49cdf2b..b2ab508 100644 --- a/.env +++ b/.env @@ -22,7 +22,7 @@ NEXT_PUBLIC_RTC_APP_ID=689ade491323ae01797818e0 # 启用 mock NEXT_PUBLIC_ENABLE_MOCK=false -NEXT_PUBLIC_APP_URL=https://test.crushlevel.ai +NEXT_PUBLIC_APP_URL=http://localhost:3000 NEXT_PUBLIC_IM_USER_SUFFIX=@u@t NEXT_PUBLIC_IM_AI_SUFFIX=@r@t diff --git a/docs/AiReplySuggestions-Refactor.md b/docs/AiReplySuggestions-Refactor.md new file mode 100644 index 0000000..e0de9e5 --- /dev/null +++ b/docs/AiReplySuggestions-Refactor.md @@ -0,0 +1,238 @@ +# AI 建议回复功能重构说明 + +## 重构日期 +2025-11-17 + +## 最新更新 +2025-11-17 - 优化请求逻辑,确保每次只发送 3 次 API 请求 + +## 问题描述 + +重构前的 AI 建议功能存在以下问题: + +1. **状态管理复杂**:维护了大量状态(`allSuggestions`、`loadedPages`、`batchNo`、`excContentList`、`isPageLoading`、`isRequesting` 等) +2. **页面切换逻辑复杂**:需要跟踪哪些页面已加载,切换时判断是否显示骨架屏 +3. **显示逻辑不清晰**:`isLoading` 只在首次加载时为 true,分页切换时骨架屏显示不正确 +4. **用户体验问题**:会出现 AI 辅助提示为空的情况(如图所示) + +## 重构目标 + +1. **简化状态管理**:只保留必要的状态 +2. **统一交互逻辑**:每次打开建议面板时,重置到第1页并重新获取数据 +3. **清晰的骨架屏显示**:数据未加载完成时始终显示骨架屏 + +## 重构内容 + +### 1. `useAiReplySuggestions` Hook 重构 + +#### 状态简化 + +**重构前:** +```typescript +const [allSuggestions, setAllSuggestions] = useState([]); +const [loadedPages, setLoadedPages] = useState>(new Set([1])); +const [isPageLoading, setIsPageLoading] = useState(false); +const [isRequesting, setIsRequesting] = useState(false); +// ... 其他状态 +``` + +**重构后:** +```typescript +const [pageData, setPageData] = useState>(new Map()); +const [loadingPages, setLoadingPages] = useState>(new Set()); +// ... 其他必要状态 +``` + +#### 核心逻辑变化 + +**重构前:** +- `showSuggestions` 函数会根据多种条件判断是否需要获取数据 +- 首次获取第1页数据,然后静默获取第2、3页 +- 页面切换时复杂的加载状态判断 + +**重构后:** +- **每次调用 `showSuggestions` 都重置到第1页并重新获取所有数据** +- 按页存储数据到 `Map` 中,逻辑更清晰 +- 页面切换时自动检查并加载缺失的页面数据 + +### 2. 数据获取流程优化 + +#### 重构前流程 +1. 获取第1页 → 立即展示 +2. 静默获取第2页 → 更新 `loadedPages` +3. 静默获取第3页 → 更新 `loadedPages` +4. 用户切换页面时检查 `loadedPages` + +#### 重构后流程 +1. 清空旧数据 +2. 获取第1页 → 存入 `pageData.set(1, ...)` +3. 获取第2页 → 存入 `pageData.set(2, ...)` +4. 获取第3页 → 存入 `pageData.set(3, ...)` +5. 用户切换页面时从 `pageData` 读取,无数据则显示骨架屏 + +### 3. 骨架屏显示逻辑 + +**重构前:** +```typescript +const displaySuggestions = isCurrentPageLoaded ? suggestions : Array.from({ length: 3 }, ...); +``` + +**重构后:** +```typescript +const suggestions = isCurrentPageLoading || !currentPageSuggestions + ? Array.from({ length: 3 }, (_, index) => ({ + id: `skeleton-${currentPage}-${index}`, + text: '', + isSkeleton: true + })) + : currentPageSuggestions; +``` + +UI 组件根据 `isSkeleton` 标志渲染骨架屏或真实内容。 + +### 4. UI 组件更新 + +`AiReplySuggestions.tsx` 更新: +- 添加 `isSkeleton` 字段到 `ReplySuggestion` 接口 +- 检查 `suggestions.some(s => s.isSkeleton)` 决定是否显示骨架屏 +- 骨架屏添加 `animate-pulse` 动画效果 + +## 重构优势 + +### 1. **简化的状态管理** +- 使用 `Map` 按页存储数据,结构清晰 +- 移除了 `isRequesting`、`isPageLoading`、`isCurrentPageLoaded` 等冗余状态 +- 只需维护 `loadingPages: Set` 来跟踪正在加载的页面 + +### 2. **智能的数据管理** +- **有新 AI 消息时**:重置到第1页并重新获取数据 +- **没有新消息时**:保留之前的建议内容和当前页面位置 +- 避免了"空白建议"的问题 +- 数据加载状态清晰可见(骨架屏动画) + +### 3. **更可靠的数据加载** +- 智能判断是否需要重新获取数据(基于最后一条 AI 消息 ID) +- 首次打开或数据为空时自动获取 +- 减少了因状态不同步导致的 bug + +### 4. **更好的代码可维护性** +- 代码从 312 行减少到 200 行 +- 逻辑流程更清晰直观 +- 减少了状态依赖和副作用 + +## 使用示例 + +### 场景1:首次打开建议面板(渐进式加载) + +1. **点击建议按钮** → 面板打开,重置到第1页,显示骨架屏 +2. **~500ms 后第1页数据返回** → ✨ **立即展示第1页的3条建议**(用户可以开始浏览) +3. **后台继续获取第2、3页** → 不阻塞用户交互 +4. **点击下一页** → 切换到第2页 + - 如果第2页数据已加载 → 立即显示 + - 如果第2页数据未加载 → 显示骨架屏(通常第2页已在后台加载完成) + +### 场景2:关闭后再次打开(无新消息) + +1. **关闭建议面板** +2. **再次点击建议按钮** → 面板打开 +3. **保留上次的内容和页面位置**(比如之前在第2页,现在仍在第2页) +4. **无需重新加载数据**,立即显示缓存的建议 + +### 场景3:收到新 AI 消息后打开 + +1. **用户发送消息,AI 回复** +2. **点击建议按钮** → 面板打开 +3. **检测到新的 AI 消息** → 重置到第1页,显示骨架屏 +4. **重新获取所有建议数据**(基于最新的对话内容) + +### 场景4:面板已打开时收到新 AI 消息 + +1. **建议面板已打开**(比如用户正在浏览第2页) +2. **用户发送消息,AI 回复** +3. **自动刷新建议数据** → **保持在当前页面**(第2页),不强制切回第1页 +4. **渐进式加载新数据**,用户可以继续浏览当前页 + +## API 返回值变化 + +Hook 返回的接口保持不变,确保向后兼容: + +```typescript +return { + suggestions, // ReplySuggestion[] (包含 isSkeleton 标志) + currentPage, // number + totalPages, // number + isLoading, // boolean (只要有页面在加载就为 true) + isVisible, // boolean + showSuggestions, // () => void + hideSuggestions, // () => void + handlePageChange, // (page: number) => void +}; +``` + +移除的返回值: +- `isPageLoading` +- `isCurrentPageLoaded` +- `refreshSuggestions` + +## 关键优化点 + +### 避免多次请求的设计 + +**问题**:原始实现会触发多次重复请求 + +**解决方案**: +1. **统一的 `fetchAllData` 函数**:一次性顺序获取3页数据,使用局部变量传递 `batchNo` 和 `excContentList` +2. **防重复调用保护**:在 `fetchAllData` 开始时检查 `loadingPages.size > 0`,如果已有加载则跳过 +3. **移除分页独立加载**:删除了 `fetchPageData` 函数和 `handlePageChange` 中的数据获取逻辑 +4. **简化页面切换**:`handlePageChange` 只负责切换 `currentPage`,不触发数据加载 + +### 渐进式加载流程 + +采用**渐进式加载**策略,让用户尽早看到数据,提升体验: + +```typescript +fetchAllData() { + // 1. 检查防重复 + if (loadingPages.size > 0) return; + + // 2. 标记所有页面为加载中 + setLoadingPages(new Set([1, 2, 3])); + + // 3. 获取第1页 → 立即展示 + const response1 = await genSupContentV2({ aiId, excContentList: [] }); + setPageData(new Map([[1, response1]])); // ✨ 立即展示第1页 + setLoadingPages(new Set([2, 3])); // 标记第1页已完成 + + // 4. 获取第2页 → 追加展示 + const response2 = await genSupContentV2({ aiId, batchNo, excContentList: [response1] }); + setPageData(prev => prev.set(2, response2)); // ✨ 追加第2页 + setLoadingPages(new Set([3])); // 标记第2页已完成 + + // 5. 获取第3页 → 追加展示 + const response3 = await genSupContentV2({ aiId, batchNo, excContentList: [response1, response2] }); + setPageData(prev => prev.set(3, response3)); // ✨ 追加第3页 + setLoadingPages(new Set()); // 全部完成 +} +``` + +**关键优势**: +- 🚀 **第1页数据到达后立即展示**,用户无需等待所有数据 +- 📊 **后续页面数据追加展示**,不影响用户浏览第1页 +- ⏱️ **感知加载时间更短**,提升用户体验 +- 🔄 **页面2、3可以并行渲染**,用户切换时自动显示骨架屏 + +## 注意事项 + +1. **精确的请求次数**:每次调用 `fetchAllData` 只会发送 **3 次** API 请求(第1、2、3页) +2. **智能缓存策略**:没有新 AI 消息时,复用已有数据,不发送请求 +3. **网络失败处理**:任一页面加载失败会中断整个流程并清空数据 +4. **Coin 不足处理**:任何页面触发 Coin 不足错误都会关闭整个建议面板 +5. **防重复保护**:通过 `loadingPages.size` 检查防止并发调用 + +## 测试建议 + +1. 测试正常流程:打开建议 → 浏览3页 → 关闭 → 再次打开 +2. 测试网络慢场景:确认骨架屏正确显示 +3. 测试 Coin 不足场景:确认面板正确关闭 +4. 测试新消息场景:发送消息后面板已打开时自动刷新 + diff --git a/src/app/(auth)/login/components/GoogleButton.tsx b/src/app/(auth)/login/components/GoogleButton.tsx index 3789b50..8b6601e 100644 --- a/src/app/(auth)/login/components/GoogleButton.tsx +++ b/src/app/(auth)/login/components/GoogleButton.tsx @@ -15,6 +15,7 @@ const GoogleButton = () => { const searchParams = useSearchParams() const redirect = searchParams.get('redirect'); const [isLoading, setIsLoading] = useState(false) + const buttonRef = useRef(null) const isInitializedRef = useRef(false) // 处理 Google ID Token 回调 @@ -22,7 +23,7 @@ const GoogleButton = () => { try { setIsLoading(true) - // 使用 ID token (JWT) 调用后端登录接口 + // 使用 ID Token (JWT) 调用后端登录接口 const deviceId = tokenManager.getDeviceId() const loginData = { appClient: AppClient.Web, @@ -58,19 +59,25 @@ const GoogleButton = () => { } } - // 加载 Google Identity Services SDK 并初始化 + // 加载 Google Identity Services SDK 并渲染按钮 useEffect(() => { - const loadAndInitGoogleSDK = async () => { + const loadAndInitGoogleButton = async () => { try { - if (isInitializedRef.current) return + if (isInitializedRef.current || !buttonRef.current) return await googleOAuth.loadScript() - // 初始化 Google Identity - if (window.google?.accounts?.id) { - googleOAuth.initGoogleId(handleGoogleResponse) + // 使用 Google 提供的标准按钮,这种方式会自动处理未登录的情况 + if (window.google?.accounts?.id && buttonRef.current) { + googleOAuth.renderButton(buttonRef.current, handleGoogleResponse, { + type: 'standard', + theme: 'outline', + size: 'large', + text: 'continue_with', + width: buttonRef.current.offsetWidth.toString() + }) isInitializedRef.current = true - console.log('Google Identity Services initialized') + console.log('Google Sign-In button rendered') } } catch (error) { console.error('Failed to load Google SDK:', error) @@ -78,49 +85,48 @@ const GoogleButton = () => { } } - loadAndInitGoogleSDK() - }, [handleGoogleResponse]) + loadAndInitGoogleButton() + }, []) - const handleGoogleLogin = async () => { - try { - setIsLoading(true) - - // 保存重定向 URL - if (typeof window !== 'undefined') { - sessionStorage.setItem('login_redirect_url', redirect || '') - } - - // 确保 SDK 已加载并初始化 - if (!window.google?.accounts?.id) { - await googleOAuth.loadScript() - googleOAuth.initGoogleId(handleGoogleResponse) - } - - // 触发 One Tap 登录流程 - if (window.google?.accounts?.id) { - window.google.accounts.id.prompt((notification) => { - if (notification.isNotDisplayed() || notification.isSkippedMoment()) { - console.log('One Tap not displayed:', notification.getNotDisplayedReason() || notification.getSkippedReason()) - // One Tap 无法显示时,使用备选方案 (可选) - setIsLoading(false) - } - }) - } - } catch (error) { - console.error('Google login error:', error) - toast.error("Failed to initialize Google login") - setIsLoading(false) + const handleGoogleLogin = () => { + // 保存重定向 URL + if (typeof window !== 'undefined') { + sessionStorage.setItem('login_redirect_url', redirect || '') + } + + // 如果 Google 按钮已渲染,点击会自动触发 + // 如果未渲染,显示提示 + if (!isInitializedRef.current) { + toast.error("Google login is not ready yet") } } return ( - } - onClick={handleGoogleLogin} - disabled={login.isPending} - > - {login.isPending ? "Signing in..." : "Continue with Google"} - + <> + {/* 隐藏的 Google 标准按钮容器 */} +
+ + {/* 自定义样式的按钮,点击时触发隐藏的 Google 按钮 */} + } + onClick={() => { + handleGoogleLogin() + // 触发隐藏的 Google 按钮 + if (buttonRef.current) { + const googleButton = buttonRef.current.querySelector('div[role="button"]') as HTMLElement + if (googleButton) { + googleButton.click() + } + } + }} + disabled={login.isPending || isLoading} + > + {login.isPending || isLoading ? "Signing in..." : "Continue with Google"} + + ); } diff --git a/src/app/(auth)/login/components/LeftPanel.tsx b/src/app/(auth)/login/components/LeftPanel.tsx index f06dd27..b23886c 100644 --- a/src/app/(auth)/login/components/LeftPanel.tsx +++ b/src/app/(auth)/login/components/LeftPanel.tsx @@ -51,8 +51,16 @@ export function LeftPanel({ scrollBg, images }: LeftPanelProps) { {/* 滚动背景 */} + + {/* 内容层 */}
+ {/* 底部遮罩层 - 铺满背景底部,高度500px */} +
+ {/* 文字内容 - 在图片上方 */}
{ refreshBeforeExpireMinutes: 5 }); + const handleOpenChatProfileDrawer = () => { + setIsChatProfileDrawerOpen(true); + }; const isShowRedDot = hasRedDot(RED_DOT_KEYS.CHAT_BACKGROUND) || hasRedDot(RED_DOT_KEYS.CHAT_BUBBLE); @@ -44,7 +47,7 @@ const ChatPage = () => {
- setIsChatProfileDrawerOpen(true)} /> + {isShowRedDot && }
diff --git a/src/app/(main)/chat/[aiId]/components/ChatCall/ChatCallContainer.tsx b/src/app/(main)/chat/[aiId]/components/ChatCall/ChatCallContainer.tsx index 7c40e2d..4dbe758 100644 --- a/src/app/(main)/chat/[aiId]/components/ChatCall/ChatCallContainer.tsx +++ b/src/app/(main)/chat/[aiId]/components/ChatCall/ChatCallContainer.tsx @@ -374,7 +374,7 @@ const ChatCallContainer = () => { if (newState.isAiThinking) { setTimeout(() => { setSubtitleState({ ...newState }); - // rtc.current?.changeAudioState(false); + rtc.current?.changeAudioState(false); }, 200); return; } diff --git a/src/app/(main)/chat/[aiId]/components/ChatMessageAction/AiReplySuggestions.tsx b/src/app/(main)/chat/[aiId]/components/ChatMessageAction/AiReplySuggestions.tsx index ab1b384..00d794e 100644 --- a/src/app/(main)/chat/[aiId]/components/ChatMessageAction/AiReplySuggestions.tsx +++ b/src/app/(main)/chat/[aiId]/components/ChatMessageAction/AiReplySuggestions.tsx @@ -8,6 +8,7 @@ import Image from 'next/image'; interface ReplySuggestion { id: string; text: string; + isSkeleton?: boolean; } interface AiReplySuggestionsProps { @@ -33,6 +34,8 @@ export const AiReplySuggestions: React.FC = ({ onClose, className }) => { + // 检查是否显示骨架屏:当前页的建议中有骨架屏标记 + const showSkeleton = suggestions.some(s => s.isSkeleton); return (
= ({
{/* 建议列表 */} - {isLoading ? ( - // 骨架屏 - 固定显示2条建议的布局 - [1, 2, 3].map((index) => ( + {showSkeleton ? ( + // 骨架屏 - 固定显示3条建议的布局 + suggestions.map((suggestion) => (
-
+
-
+
)) ) : ( // 实际建议内容 - suggestions.slice(0, 3).map((suggestion) => ( + suggestions.map((suggestion) => (
{ const { nim } = useNimChat(); const { getHistoryMsgActive } = useContext(NimMsgContext); const { aiId } = useChatConfig(); - + // 用于引用ChatMessageUserHeader的ref const userHeaderRef = useRef(null); @@ -35,15 +35,21 @@ const ChatMessageList = () => { } = useMessageState(); // 使用加载状态Hook - const { loadingState, initializeLoading, fetchNextPage, completeInitialLoad } = useMessageLoading( - { - selectedConversationId, - getHistoryMsgActive, - } - ); + const { + loadingState, + initializeLoading, + fetchNextPage, + completeInitialLoad, + } = useMessageLoading({ + selectedConversationId, + getHistoryMsgActive, + }); // 使用滚动Hook - const { containerRef, handleInitialLoadComplete } = useMessageScrolling({ + const { + containerRef, + handleInitialLoadComplete, + } = useMessageScrolling({ messages, selectedConversationId, isInitialLoad: loadingState.isInitialLoad, @@ -79,41 +85,33 @@ const ChatMessageList = () => { // 首次加载完成后的处理 useEffect(() => { if (loadingState.isInitialLoad && (messages.length > 0 || loadingState.apiCallCount > 0)) { - if (process.env.NODE_ENV === "development") { - console.log("📨 检测到首批消息加载完成,准备隐藏骨架屏", { + if (process.env.NODE_ENV === 'development') { + console.log('📨 检测到首批消息加载完成,准备隐藏骨架屏', { messagesLength: messages.length, - apiCallCount: loadingState.apiCallCount, + apiCallCount: loadingState.apiCallCount }); } - + // 延迟处理,确保平滑过渡 setTimeout(() => { completeInitialLoad(messages.length); handleInitialLoadComplete(); - - if (process.env.NODE_ENV === "development") { - console.log("🦴 隐藏骨架屏,显示真实消息"); + + if (process.env.NODE_ENV === 'development') { + console.log('🦴 隐藏骨架屏,显示真实消息'); } }, 200); } - }, [ - messages.length, - loadingState.isInitialLoad, - loadingState.apiCallCount, - completeInitialLoad, - handleInitialLoadComplete, - ]); + }, [messages.length, loadingState.isInitialLoad, loadingState.apiCallCount, completeInitialLoad, handleInitialLoadComplete]); - console.log( - "messages", - messages, - selectedConversationId, - nim?.V2NIMConversationIdUtil.parseConversationTargetId(selectedConversationId || "") - ); + console.log('messages', messages, selectedConversationId, nim?.V2NIMConversationIdUtil.parseConversationTargetId(selectedConversationId || '')); return ( -
-
+
+
{/* 用户信息头部始终显示在顶部 */}
@@ -124,7 +122,7 @@ const ChatMessageList = () => {
)} @@ -134,47 +132,52 @@ const ChatMessageList = () => {
)} - + {/* CrushLevelAction - 使用visibility和opacity控制显示/隐藏,避免图片重复加载 */} -
- + {/* 骨架屏 - 仅在首次访问且无数据且尚未完成加载时显示 */} - {loadingState.showSkeleton && - messages.length === 0 && - loadingState.isInitialLoad && - !loadingState.hasLoadedOnce && } - + {loadingState.showSkeleton && messages.length === 0 && loadingState.isInitialLoad && !loadingState.hasLoadedOnce && ( + + )} + {/* 消息列表 */} {!loadingState.showSkeleton && ( -
+
{/* 持久化开场白 - 始终显示在消息列表顶部 */} - + {/* 普通消息 */} {normalMessages.map((message) => { // 判断是否是用户发送的消息(根据发送者ID) const isUser = !message.senderId.includes(aiId.toString()); - + return (
- +
); })} - + {/* Loading消息,始终显示在最后 */} {loadingMessages.map((message) => (
- +
))}
@@ -182,6 +185,6 @@ const ChatMessageList = () => {
); -}; - -export default ChatMessageList; +} + +export default ChatMessageList; \ No newline at end of file diff --git a/src/app/(main)/contact/contact-page.tsx b/src/app/(main)/contact/contact-page.tsx index db63439..5138a1f 100644 --- a/src/app/(main)/contact/contact-page.tsx +++ b/src/app/(main)/contact/contact-page.tsx @@ -2,16 +2,16 @@ import { Avatar, AvatarImage, AvatarFallback } from "@/components/ui/avatar" import { Button } from "@/components/ui/button" -import { TagBadge } from "@/components/ui/badge" import { InfiniteScrollList } from "@/components/ui/infinite-scroll-list" import RenderContactStatusText from "./components/RenderContactStatusText" import { useHeartbeatRelationListInfinite } from "@/hooks/useIm" import { HeartbeatRelationListOutput } from "@/services/im/types" import { useMemo } from "react" import { useRouter } from "next/navigation" -import { Tag } from "@/components/ui/tag" import AIRelationTag from "@/components/features/AIRelationTag" import Link from "next/link" +import { usePrefetchRoutes } from "@/hooks/useGlobalPrefetchRoutes" +import Image from "next/image" // 联系人数据类型现在使用API返回的数据结构 type ContactItem = HeartbeatRelationListOutput @@ -28,16 +28,6 @@ const ContactCard = ({ contact }: { contact: ContactItem }) => { return currentYear - birthYear; }, [contact.birthday]); - // 获取性别标识 - const getGenderText = (sex?: number) => { - switch (sex) { - case 0: return "Male"; - case 1: return "Female"; - case 2: return "Custom"; - default: return ""; - } - }; - // 跳转到聊天页面 const handleChatClick = () => { if (contact.aiId) { @@ -50,7 +40,7 @@ const ContactCard = ({ contact }: { contact: ContactItem }) => { {/* 用户信息部分 */}
{/* 头像 */} - + @@ -63,7 +53,7 @@ const ContactCard = ({ contact }: { contact: ContactItem }) => {
{/* 名字和标签 */}
- +

{contact.nickname || contact.roleName}

@@ -75,9 +65,11 @@ const ContactCard = ({ contact }: { contact: ContactItem }) => {
{/* 心动值 */}
- Heart @@ -123,6 +115,11 @@ const ContactsPage = () => { const allContacts = useMemo(() => { return data?.pages.flatMap(page => page.datas || []) || []; }, [data]); + const chatRoutes = useMemo( + () => allContacts.slice(0, 20).map((contact) => contact?.aiId ? `/chat/${contact.aiId}` : null), + [allContacts] + ) + usePrefetchRoutes(chatRoutes) // 加载状态骨架屏组件 const ContactSkeleton = () => ( @@ -144,9 +141,11 @@ const ContactsPage = () => { // 空状态组件 const EmptyState = () => (
- Empty

@@ -190,4 +189,4 @@ const ContactsPage = () => { ) } -export default ContactsPage \ No newline at end of file +export default ContactsPage diff --git a/src/app/(main)/create/components/CharacterForm.tsx b/src/app/(main)/create/components/CharacterForm.tsx index c32b85d..7c6e115 100644 --- a/src/app/(main)/create/components/CharacterForm.tsx +++ b/src/app/(main)/create/components/CharacterForm.tsx @@ -125,6 +125,7 @@ export default function CharacterForm() { // 检测名字是否重复 const isExist = await checkNickname({ nickname: nickname.trim(), + isAiCheck: true, }) if (isExist) { form.setError("nickname", { @@ -132,15 +133,15 @@ export default function CharacterForm() { }) return; } - const resp = await checkText({ - content: userProfile, - }) - if (resp) { - form.setError("userProfile", { - message: resp, - }) - return; - } + // const resp = await checkText({ + // content: userProfile, + // }) + // if (resp) { + // form.setError("userProfile", { + // message: resp, + // }) + // return; + // } router.push("/create/dialogue") } catch (error) { console.error(error) diff --git a/src/app/(main)/create/components/DialogueForm.tsx b/src/app/(main)/create/components/DialogueForm.tsx index 3c34b33..f59c012 100644 --- a/src/app/(main)/create/components/DialogueForm.tsx +++ b/src/app/(main)/create/components/DialogueForm.tsx @@ -25,7 +25,7 @@ import { useCheckText } from "@/hooks/auth" const dialogueFormSchema = z.object({ userDialogueStyle: z.string().trim().optional(), // .min(1, "Please enter style").min(10, "Please enter at least 10 characters").max(300, "Please enter less than 300 characters"), - prologue: z.string().trim().min(1, "Please enter opening").min(10, "Please enter at least 10 characters").max(150, "Please enter less than 150 characters"), + prologue: z.string().trim().min(1, "Please enter opening").min(10, "Please enter at least 10 characters").max(250, "Please enter less than 250 characters"), voice: z.object({ content: z.string().optional(), tone: z.number().min(-50).max(100), @@ -38,13 +38,13 @@ const dialogueFormSchema = z.object({ path: ["voice"], }).refine((data) => { if (data.userDialogueStyle) { - if (data.userDialogueStyle.trim().length > 300) { + if (data.userDialogueStyle.trim().length > 400) { return false; } } return true; }, { - message: "Please enter less than 300 characters", + message: "Please enter less than 400 characters", path: ["userDialogueStyle"], }).refine((data) => { if (data.userDialogueStyle) { @@ -170,28 +170,28 @@ export default function DialogueForm() { } setLoading(true) try { - if (data.prologue) { - const resp = await checkText({ - content: data.prologue, - }) - if (resp) { - form.setError("prologue", { - message: resp, - }) - return; - } - } - if (data.userDialogueStyle) { - const resp = await checkText({ - content: data.userDialogueStyle, - }) - if (resp) { - form.setError("userDialogueStyle", { - message: resp, - }) - return; - } - } + // if (data.prologue) { + // const resp = await checkText({ + // content: data.prologue, + // }) + // if (resp) { + // form.setError("prologue", { + // message: resp, + // }) + // return; + // } + // } + // if (data.userDialogueStyle) { + // const resp = await checkText({ + // content: data.userDialogueStyle, + // }) + // if (resp) { + // form.setError("userDialogueStyle", { + // message: resp, + // }) + // return; + // } + // } if (!formData.image?.intro) { const resp = await generateContent({ ptType: PtType.GenAIIntroduction, @@ -323,14 +323,14 @@ export default function DialogueForm() { figure: formData.character?.aiUserExt?.userProfile ?? undefined, } as GenerateContentRequest; const resp = await generateContent(result) - form.setValue("prologue", (resp.content || "").slice(0, 150)) + form.setValue("prologue", resp.content) }} />