fix(workspace): reset doc state on navigation (bbox / analysis state leak)
Quand l'utilisateur naviguait d'un doc à un autre via /docs/:id, Vue réutilisait l'instance du composant — onMounted ne se redéclenchait pas, laissant l'analyse précédente (et donc les bbox du StructureViewer) visibles tant que le nouveau fetch n'était pas terminé. - DocWorkspacePage: watch props.id pour recharger le doc + :key sur les onglets (forçe un remount propre, reset l'état interne du StructureViewer comme selectedPage/hiddenTypes). - DocInspectTab / DocAskTab: watch props.docId avec immediate:true, reset l'état au début du load + guard de race condition (ignore les réponses pour un docId obsolète).
This commit is contained in:
parent
ed50756ccd
commit
3c80d8baf0
3 changed files with 36 additions and 12 deletions
|
|
@ -40,7 +40,7 @@
|
|||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, onMounted } from 'vue'
|
||||
import { ref, computed, watch } from 'vue'
|
||||
import { RouterLink } from 'vue-router'
|
||||
import type { Analysis, Page } from '../shared/types'
|
||||
import { fetchDocumentAnalyses } from '../features/analysis/api'
|
||||
|
|
@ -73,13 +73,18 @@ const visitedBySelfRef = computed<Map<string, number>>(() => new Map())
|
|||
async function loadAnalysis(): Promise<void> {
|
||||
loading.value = true
|
||||
error.value = null
|
||||
analysis.value = null
|
||||
focusedSelfRef.value = null
|
||||
const requestedId = props.docId
|
||||
try {
|
||||
const analyses = await fetchDocumentAnalyses(props.docId)
|
||||
const analyses = await fetchDocumentAnalyses(requestedId)
|
||||
if (requestedId !== props.docId) return
|
||||
analysis.value = analyses.find((a) => a.status === 'COMPLETED') ?? null
|
||||
} catch (e) {
|
||||
if (requestedId !== props.docId) return
|
||||
error.value = (e as Error).message || 'Failed to load analysis'
|
||||
} finally {
|
||||
loading.value = false
|
||||
if (requestedId === props.docId) loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -91,7 +96,7 @@ function onSectionFocus(sectionRef: string): void {
|
|||
}
|
||||
}
|
||||
|
||||
onMounted(loadAnalysis)
|
||||
watch(() => props.docId, loadAnalysis, { immediate: true })
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@
|
|||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { ref, watch } from 'vue'
|
||||
import { RouterLink } from 'vue-router'
|
||||
import type { Analysis } from '../shared/types'
|
||||
import { fetchDocumentAnalyses } from '../features/analysis/api'
|
||||
|
|
@ -45,17 +45,21 @@ const analysis = ref<Analysis | null>(null)
|
|||
async function load(): Promise<void> {
|
||||
loading.value = true
|
||||
error.value = null
|
||||
analysis.value = null
|
||||
const requestedId = props.docId
|
||||
try {
|
||||
const analyses = await fetchDocumentAnalyses(props.docId)
|
||||
const analyses = await fetchDocumentAnalyses(requestedId)
|
||||
if (requestedId !== props.docId) return
|
||||
analysis.value = analyses.find((a) => a.status === 'COMPLETED') ?? null
|
||||
} catch (e) {
|
||||
if (requestedId !== props.docId) return
|
||||
error.value = (e as Error).message || 'Failed to load analysis'
|
||||
} finally {
|
||||
loading.value = false
|
||||
if (requestedId === props.docId) loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(load)
|
||||
watch(() => props.docId, load, { immediate: true })
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
|
|
|||
|
|
@ -35,16 +35,19 @@
|
|||
</div>
|
||||
|
||||
<!-- Tab content — lazy loaded (#216) -->
|
||||
<!-- :key on docId forces a clean remount when navigating to a different doc,
|
||||
preventing stale state (bbox, selectedPage, etc.) from leaking. -->
|
||||
<div class="tab-content" role="tabpanel" data-e2e="tab-content">
|
||||
<Suspense>
|
||||
<DocChunksTab
|
||||
v-if="activeMode === 'chunks'"
|
||||
:key="id"
|
||||
:doc-id="id"
|
||||
:available-stores="doc.stores ?? []"
|
||||
:store-links="doc.storeLinks"
|
||||
/>
|
||||
<DocInspectTab v-else-if="activeMode === 'inspect'" :doc-id="id" />
|
||||
<DocAskTab v-else-if="activeMode === 'ask'" :doc-id="id" />
|
||||
<DocInspectTab v-else-if="activeMode === 'inspect'" :key="id" :doc-id="id" />
|
||||
<DocAskTab v-else-if="activeMode === 'ask'" :key="id" :doc-id="id" />
|
||||
</Suspense>
|
||||
</div>
|
||||
</template>
|
||||
|
|
@ -106,12 +109,17 @@ function switchMode(m: DocMode): void {
|
|||
async function loadDoc(): Promise<void> {
|
||||
loadingDoc.value = true
|
||||
docError.value = null
|
||||
doc.value = null
|
||||
const requestedId = props.id
|
||||
try {
|
||||
doc.value = await fetchDocument(props.id)
|
||||
const fetched = await fetchDocument(requestedId)
|
||||
if (requestedId !== props.id) return
|
||||
doc.value = fetched
|
||||
} catch (e) {
|
||||
if (requestedId !== props.id) return
|
||||
docError.value = (e as Error).message || 'Failed to load document'
|
||||
} finally {
|
||||
loadingDoc.value = false
|
||||
if (requestedId === props.id) loadingDoc.value = false
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -140,6 +148,13 @@ watch(
|
|||
if (resolved) activeMode.value = resolved
|
||||
},
|
||||
)
|
||||
|
||||
watch(
|
||||
() => props.id,
|
||||
(newId, oldId) => {
|
||||
if (newId !== oldId) loadDoc()
|
||||
},
|
||||
)
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
|
|
|||
Loading…
Reference in a new issue