fix: load cluster names before saving to cache
- Changed order: loadClusterNames now runs BEFORE setClusterResults - Removed condition that skipped loading names for 'Person_X' clusters - Now always loads custom names from trace profiles (DB is source of truth) - Fixes rename persistence - custom names are now correctly saved to cache
This commit is contained in:
15
src/store.ts
15
src/store.ts
@@ -335,20 +335,7 @@ export async function pollClusterResults(fileUuid: string, onDone?: (results: an
|
|||||||
export async function loadClusterNames(fileUuid: string, clusters: any[]) {
|
export async function loadClusterNames(fileUuid: string, clusters: any[]) {
|
||||||
console.log('[loadClusterNames] loading names for', clusters.length, 'clusters')
|
console.log('[loadClusterNames] loading names for', clusters.length, 'clusters')
|
||||||
const promises = clusters.map(async (c) => {
|
const promises = clusters.map(async (c) => {
|
||||||
// Only load name from DB if cluster has no meaningful name
|
// Always load name from trace profile (DB is source of truth for custom names)
|
||||||
// "Person_X" names from clustering are considered meaningful
|
|
||||||
// Only replace "Unassigned", empty, or auto-generated trace names
|
|
||||||
const currentName = c.name || ''
|
|
||||||
const isAutoGenerated = currentName === 'Unassigned' ||
|
|
||||||
currentName === '' ||
|
|
||||||
currentName.startsWith('Face Trace ') ||
|
|
||||||
currentName.startsWith('Trace ')
|
|
||||||
|
|
||||||
if (!isAutoGenerated) {
|
|
||||||
// Cluster already has a meaningful name (e.g., Person_X from clustering or custom name), don't overwrite
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const repTid = c.representative_trace !== undefined && c.representative_trace !== null ? c.representative_trace : c.trace_ids?.[0]
|
const repTid = c.representative_trace !== undefined && c.representative_trace !== null ? c.representative_trace : c.trace_ids?.[0]
|
||||||
if (repTid != null) {
|
if (repTid != null) {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -296,10 +296,10 @@
|
|||||||
<!-- Assign modal -->
|
<!-- Assign modal -->
|
||||||
<!-- Face detail modal -->
|
<!-- Face detail modal -->
|
||||||
<!-- Side navigation arrows - outside overlay for proper z-index stacking -->
|
<!-- Side navigation arrows - outside overlay for proper z-index stacking -->
|
||||||
<button v-if="faceDetailModal.show && canFaceDetailPrev" class="ms-fs-nav-arrow ms-fs-nav-prev" @click="goFaceDetailPrev" title="Previous FS (←)" :style="{ zIndex: faceDetailZIndex + 100 }">
|
<button v-if="faceDetailModal.show && canFaceDetailPrev" class="ms-fs-nav-arrow ms-fs-nav-prev" @click="() => { navClickCount++; console.log('[nav] PREV CLICKED'); goFaceDetailPrev(); }" title="Previous FS (←)" :style="{ zIndex: faceDetailZIndex + 100, pointerEvents: 'auto' }">
|
||||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none"><polyline points="15 18 9 12 15 6" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>
|
<svg width="24" height="24" viewBox="0 0 24 24" fill="none"><polyline points="15 18 9 12 15 6" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>
|
||||||
</button>
|
</button>
|
||||||
<button v-if="faceDetailModal.show && canFaceDetailNext" class="ms-fs-nav-arrow ms-fs-nav-next" @click="goFaceDetailNext" title="Next FS (→)" :style="{ zIndex: faceDetailZIndex + 100 }">
|
<button v-if="faceDetailModal.show && canFaceDetailNext" class="ms-fs-nav-arrow ms-fs-nav-next" @click="() => { navClickCount++; console.log('[nav] NEXT CLICKED'); goFaceDetailNext(); }" title="Next FS (→)" :style="{ zIndex: faceDetailZIndex + 100, pointerEvents: 'auto' }">
|
||||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none"><polyline points="9 18 15 12 9 6" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>
|
<svg width="24" height="24" viewBox="0 0 24 24" fill="none"><polyline points="9 18 15 12 9 6" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>
|
||||||
</button>
|
</button>
|
||||||
<div v-if="faceDetailModal.show" class="ms-modal-overlay show" :style="{ zIndex: faceDetailZIndex }" @click.self="faceDetailModal.show = false">
|
<div v-if="faceDetailModal.show" class="ms-modal-overlay show" :style="{ zIndex: faceDetailZIndex }" @click.self="faceDetailModal.show = false">
|
||||||
@@ -326,7 +326,7 @@
|
|||||||
<div class="ms-face-detail-info">
|
<div class="ms-face-detail-info">
|
||||||
<div class="ms-face-detail-title">
|
<div class="ms-face-detail-title">
|
||||||
<span class="ms-face-trace-id">{{ faceDetailModal.candidate?.file_uuid }}:FS#{{ faceDetailModal.candidate?.trace_id }}</span>
|
<span class="ms-face-trace-id">{{ faceDetailModal.candidate?.file_uuid }}:FS#{{ faceDetailModal.candidate?.trace_id }}</span>
|
||||||
<span class="ms-nav-debug" style="font-size:10px;color:#888;margin-left:8px;">nav:{{ faceDetailNavSource || 'null' }} idx:{{ faceDetailNavIndex }} list:{{ faceDetailNavList.length }}</span>
|
<span class="ms-nav-debug" style="font-size:10px;color:#888;margin-left:8px;">nav:{{ faceDetailNavSource || 'null' }} idx:{{ faceDetailNavIndex }} list:{{ faceDetailNavList.length }} clicks:{{ navClickCount }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="ms-face-detail-row">
|
<div class="ms-face-detail-row">
|
||||||
<span class="ms-face-detail-value">F{{ faceDetailModal.candidate?.start_frame }}–[F{{ faceDetailModal.candidate?.frame_number }}]–F{{ faceDetailModal.candidate?.end_frame }} {{ faceDetailModal.candidate?.frame_count }} Frames</span>
|
<span class="ms-face-detail-value">F{{ faceDetailModal.candidate?.start_frame }}–[F{{ faceDetailModal.candidate?.frame_number }}]–F{{ faceDetailModal.candidate?.end_frame }} {{ faceDetailModal.candidate?.frame_count }} Frames</span>
|
||||||
@@ -1003,6 +1003,7 @@ const traceProfileJsonTitle = ref('')
|
|||||||
const faceDetailModal = ref({ show: false, candidate: null as any, newIdentityName: '' })
|
const faceDetailModal = ref({ show: false, candidate: null as any, newIdentityName: '' })
|
||||||
const faceDetailNavSource = ref<'unassigned' | 'cluster' | null>(null)
|
const faceDetailNavSource = ref<'unassigned' | 'cluster' | null>(null)
|
||||||
const faceDetailNavIndex = ref(-1)
|
const faceDetailNavIndex = ref(-1)
|
||||||
|
const navClickCount = ref(0)
|
||||||
const regeneratingThumbnail = ref(false)
|
const regeneratingThumbnail = ref(false)
|
||||||
const clusterDetailModal = ref({ show: false, cluster: null as any, selectedTrace: null as any })
|
const clusterDetailModal = ref({ show: false, cluster: null as any, selectedTrace: null as any })
|
||||||
const clusterVideoTraces = ref<any[]>([])
|
const clusterVideoTraces = ref<any[]>([])
|
||||||
@@ -2127,14 +2128,14 @@ async function loadClusterResults() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (groups.length) {
|
if (groups.length) {
|
||||||
|
// Load trace profiles for names FIRST (DB is source of truth for custom names)
|
||||||
|
await loadClusterNames(fileUuid, groups)
|
||||||
|
|
||||||
console.log('[loadClusterResults] Calling setClusterResults with', groups.length, 'groups')
|
console.log('[loadClusterResults] Calling setClusterResults with', groups.length, 'groups')
|
||||||
console.log('[loadClusterResults] First group trace_ids:', groups[0]?.trace_ids)
|
console.log('[loadClusterResults] First group trace_ids:', groups[0]?.trace_ids)
|
||||||
setClusterResults(fileUuid, groups)
|
setClusterResults(fileUuid, groups)
|
||||||
console.log('[loadClusterResults] After setClusterResults, cache:', clusterResultsCache.value[fileUuid]?.length)
|
console.log('[loadClusterResults] After setClusterResults, cache:', clusterResultsCache.value[fileUuid]?.length)
|
||||||
|
|
||||||
// Load trace profiles for names in background (non-blocking)
|
|
||||||
loadClusterNames(fileUuid, groups).catch(() => {})
|
|
||||||
|
|
||||||
// Load thumbnails for cluster representatives immediately.
|
// Load thumbnails for cluster representatives immediately.
|
||||||
// rep 資料只需 file_uuid + trace_id,皆已存在 cluster-results payload 中,
|
// rep 資料只需 file_uuid + trace_id,皆已存在 cluster-results payload 中,
|
||||||
// 不需等待慢速的 get_unassigned_traces(~5s/page)。
|
// 不需等待慢速的 get_unassigned_traces(~5s/page)。
|
||||||
|
|||||||
Reference in New Issue
Block a user