11"use client" ;
22
3- import { useState , FormEvent , useEffect , useMemo } from "react" ;
3+ import { useState , FormEvent , useEffect , useRef , useCallback , useMemo } from "react" ;
44// import useSWR from "swr";
55// import {
66// getQuestionExample,
@@ -9,9 +9,10 @@ import { useState, FormEvent, useEffect, useMemo } from "react";
99// import { getLanguageName } from "../pagesList";
1010import { useEmbedContext } from "@/terminal/embedContext" ;
1111import { DynamicMarkdownSection , PagePath } from "@/lib/docs" ;
12- import { useRouter } from "next/navigation" ;
12+ import { usePathname , useRouter } from "next/navigation" ;
1313import { ChatStreamEvent } from "@/api/chat/route" ;
1414import { useStreamingChatContext } from "@/(docs)/streamingChatContext" ;
15+ import { revalidateChatAction } from "@/actions/revalidateChat" ;
1516
1617interface ChatFormProps {
1718 path : PagePath ;
@@ -30,6 +31,37 @@ export function ChatForm({ path, sectionContent, close }: ChatFormProps) {
3031 const router = useRouter ( ) ;
3132 const streamingChatContext = useStreamingChatContext ( ) ;
3233
34+ const pathname = usePathname ( ) ;
35+ const pendingRouterPushTarget = useRef < null | string > ( null ) ;
36+ const pendingRouterPushResolver = useRef < null | ( ( ) => void ) > ( null ) ;
37+ // router.pushの完了を待つ関数。pathnameの変化でページ遷移の完了を検知し、解決する。
38+ const asyncRouterPush = useCallback (
39+ ( url : string , options ?: { scroll ?: boolean } ) => {
40+ if ( pendingRouterPushTarget . current ) {
41+ console . error (
42+ "Already navigating to" ,
43+ pendingRouterPushTarget . current ,
44+ "can't navigate to" ,
45+ url
46+ ) ;
47+ return ;
48+ }
49+ pendingRouterPushTarget . current = url ;
50+ return new Promise < void > ( ( resolve ) => {
51+ pendingRouterPushResolver . current = resolve ;
52+ router . push ( url , options ) ;
53+ } ) ;
54+ } ,
55+ [ router ]
56+ ) ;
57+ useEffect ( ( ) => {
58+ if ( pendingRouterPushTarget . current === pathname ) {
59+ pendingRouterPushResolver . current ?.( ) ;
60+ pendingRouterPushTarget . current = null ;
61+ pendingRouterPushResolver . current = null ;
62+ }
63+ } , [ pathname ] ) ;
64+
3365 const exampleData = useMemo (
3466 ( ) =>
3567 sectionContent
@@ -97,6 +129,7 @@ export function ChatForm({ path, sectionContent, close }: ChatFormProps) {
97129 const reader = response . body ! . getReader ( ) ;
98130 const decoder = new TextDecoder ( ) ;
99131 let buffer = "" ;
132+ let chatId : string | null = null ;
100133 let navigated = false ;
101134
102135 // ストリームを非同期で読み続ける(ナビゲーション後もバックグラウンドで継続)
@@ -117,11 +150,16 @@ export function ChatForm({ path, sectionContent, close }: ChatFormProps) {
117150 const event = JSON . parse ( line ) as ChatStreamEvent ;
118151
119152 if ( event . type === "chat" ) {
153+ // revalidateChatは/api/chatの中では呼ばず、別のServerActionとして呼び出す
154+ await revalidateChatAction ( event . chatId , path ) ;
155+ chatId = event . chatId ;
120156 streamingChatContext . startStreaming ( event . chatId ) ;
121157 document . getElementById ( event . sectionId ) ?. scrollIntoView ( {
122158 behavior : "smooth" ,
123159 } ) ;
124- router . push ( `/chat/${ event . chatId } ` , { scroll : false } ) ;
160+ await asyncRouterPush ( `/chat/${ event . chatId } ` , {
161+ scroll : false ,
162+ } ) ;
125163 router . refresh ( ) ;
126164 navigated = true ;
127165 setIsLoading ( false ) ;
@@ -130,14 +168,21 @@ export function ChatForm({ path, sectionContent, close }: ChatFormProps) {
130168 } else if ( event . type === "chunk" ) {
131169 streamingChatContext . appendChunk ( event . text ) ;
132170 } else if ( event . type === "done" ) {
171+ if ( chatId ) {
172+ await revalidateChatAction ( chatId , path ) ;
173+ }
133174 streamingChatContext . finishStreaming ( ) ;
134175 router . refresh ( ) ;
135176 } else if ( event . type === "error" ) {
136177 if ( ! navigated ) {
137178 setErrorMessage ( event . message ) ;
138179 setIsLoading ( false ) ;
139180 }
181+ if ( chatId ) {
182+ await revalidateChatAction ( chatId , path ) ;
183+ }
140184 streamingChatContext . finishStreaming ( ) ;
185+ router . refresh ( ) ;
141186 }
142187 } catch {
143188 // ignore JSON parse errors
0 commit comments