fix: prevent stale tab renders by scoping workflow and loaded models data loading to their respective tokens
This commit is contained in:
@@ -66,6 +66,8 @@ export class ResolverManagerDialog extends ComfyDialog {
|
||||
this._dragging = false;
|
||||
this._dragStart = null;
|
||||
this._analysisProgressToken = null;
|
||||
this._workflowDataLoadToken = null;
|
||||
this._loadedModelsLoadToken = null;
|
||||
this._locateAnimationFrame = null;
|
||||
this._viewportClampFrame = null;
|
||||
this.activeWorkflowRouteKey = this.getActiveWorkflowRouteKey();
|
||||
|
||||
@@ -704,6 +704,8 @@ export const downloadTargetMethods = {
|
||||
this.downloadSubfolders.clear();
|
||||
this.downloadTargetSelections?.clear();
|
||||
this._analysisProgressToken = null;
|
||||
this._workflowDataLoadToken = null;
|
||||
this._loadedModelsLoadToken = null;
|
||||
|
||||
await this.ensureCapabilitiesLoaded();
|
||||
await this.ensureBaseModelsLoaded();
|
||||
|
||||
@@ -116,6 +116,14 @@ export const lifecycleGraphMethods = {
|
||||
async loadWorkflowData(workflow = null, { force = false } = {}) {
|
||||
if (!this.contentElement) return;
|
||||
|
||||
const loadToken = `missing-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
||||
this._workflowDataLoadToken = loadToken;
|
||||
const shouldRenderMissingModels = () => (
|
||||
this.activeTab === 'missing' &&
|
||||
this._workflowDataLoadToken === loadToken &&
|
||||
this.contentElement
|
||||
);
|
||||
|
||||
// Show loading state
|
||||
try {
|
||||
// Use provided workflow, or get current workflow from ComfyUI
|
||||
@@ -124,8 +132,12 @@ export const lifecycleGraphMethods = {
|
||||
}
|
||||
|
||||
if (!workflow) {
|
||||
this._analysisProgressToken = null;
|
||||
this.contentElement.innerHTML = '<p>No workflow loaded. Please load a workflow first.</p>';
|
||||
if (this._workflowDataLoadToken === loadToken) {
|
||||
this._analysisProgressToken = null;
|
||||
}
|
||||
if (shouldRenderMissingModels()) {
|
||||
this.contentElement.innerHTML = '<p>No workflow loaded. Please load a workflow first.</p>';
|
||||
}
|
||||
return;
|
||||
}
|
||||
this.syncWorkflowScopedQueue(workflow);
|
||||
@@ -140,19 +152,23 @@ export const lifecycleGraphMethods = {
|
||||
this.cachedWorkflowSignature === workflowSignature &&
|
||||
this.cachedAnalysisData
|
||||
) {
|
||||
this.displayMissingModels(this.contentElement, this.cachedAnalysisData);
|
||||
this.reconnectActiveDownloads();
|
||||
if (shouldRenderMissingModels()) {
|
||||
this.displayMissingModels(this.contentElement, this.cachedAnalysisData);
|
||||
this.reconnectActiveDownloads();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const analysisId = `an-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
||||
this._analysisProgressToken = analysisId;
|
||||
this.contentElement.innerHTML = this.renderAnalysisProgress({
|
||||
status: 'starting',
|
||||
message: 'Starting analysis...',
|
||||
current: 0,
|
||||
total: 0
|
||||
});
|
||||
if (shouldRenderMissingModels()) {
|
||||
this.contentElement.innerHTML = this.renderAnalysisProgress({
|
||||
status: 'starting',
|
||||
message: 'Starting analysis...',
|
||||
current: 0,
|
||||
total: 0
|
||||
});
|
||||
}
|
||||
|
||||
// Call analyze endpoint
|
||||
const progressPromise = this.pollAnalysisProgress(analysisId, analysisId);
|
||||
@@ -161,7 +177,9 @@ export const lifecycleGraphMethods = {
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ workflow, analysis_id: analysisId })
|
||||
});
|
||||
this._analysisProgressToken = null;
|
||||
if (this._analysisProgressToken === analysisId) {
|
||||
this._analysisProgressToken = null;
|
||||
}
|
||||
await progressPromise;
|
||||
|
||||
if (!response.ok) {
|
||||
@@ -169,18 +187,24 @@ export const lifecycleGraphMethods = {
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
this.cachedWorkflowSignature = workflowSignature;
|
||||
this.cachedAnalysisData = data;
|
||||
this.saveAnalysisCacheForActiveWorkflow();
|
||||
this.displayMissingModels(this.contentElement, data);
|
||||
if (this._workflowDataLoadToken === loadToken) {
|
||||
this.cachedWorkflowSignature = workflowSignature;
|
||||
this.cachedAnalysisData = data;
|
||||
this.saveAnalysisCacheForActiveWorkflow();
|
||||
}
|
||||
if (shouldRenderMissingModels()) {
|
||||
this.displayMissingModels(this.contentElement, data);
|
||||
|
||||
// Reconnect any active downloads to their new progress divs
|
||||
this.reconnectActiveDownloads();
|
||||
// Reconnect any active downloads to their new progress divs
|
||||
this.reconnectActiveDownloads();
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
this._analysisProgressToken = null;
|
||||
if (this._workflowDataLoadToken === loadToken) {
|
||||
this._analysisProgressToken = null;
|
||||
}
|
||||
console.error('Model Resolver: Error loading workflow data:', error);
|
||||
if (this.contentElement) {
|
||||
if (shouldRenderMissingModels()) {
|
||||
this.contentElement.innerHTML = `<p class="mr-error-text">Error: ${error.message}</p>`;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -306,6 +306,9 @@ export const workflowStateMethods = {
|
||||
this.cachedAnalysisData = null;
|
||||
this.cachedLoadedModelsSignature = null;
|
||||
this.cachedLoadedModelsData = null;
|
||||
this._analysisProgressToken = null;
|
||||
this._workflowDataLoadToken = null;
|
||||
this._loadedModelsLoadToken = null;
|
||||
for (const [missingSearchKey, state] of this.searchResultCache.entries()) {
|
||||
if (!this.hasBackgroundSearchJob?.(key, missingSearchKey, state.activeSearchRunId)) {
|
||||
state.activeSearchRunId = null;
|
||||
|
||||
@@ -162,10 +162,18 @@ export const renderFormatMethods = {
|
||||
while (this._analysisProgressToken === token) {
|
||||
try {
|
||||
const response = await api.fetchApi(`/model_resolver/analyze-progress/${analysisId}`);
|
||||
if (response.ok && this.contentElement && this._analysisProgressToken === token) {
|
||||
if (
|
||||
response.ok &&
|
||||
this.contentElement &&
|
||||
this._analysisProgressToken === token &&
|
||||
this.activeTab === 'missing'
|
||||
) {
|
||||
const progress = await response.json();
|
||||
this.contentElement.innerHTML = this.renderAnalysisProgress(progress);
|
||||
if (progress.status === 'completed' || progress.status === 'error') {
|
||||
if (this._analysisProgressToken === token) {
|
||||
this._analysisProgressToken = null;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -685,7 +685,6 @@ export const missingBrowserMethods = {
|
||||
|
||||
// Skip rendering if active tab is not "missing"
|
||||
if (this.activeTab !== 'missing') {
|
||||
container.innerHTML = '';
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -38,6 +38,12 @@ export const tabsLoadedMethods = {
|
||||
|
||||
this.activeTab = nextTab;
|
||||
this.persistActiveTab(this.activeTab);
|
||||
if (this.activeTab !== 'missing') {
|
||||
this._analysisProgressToken = null;
|
||||
}
|
||||
if (this.activeTab !== 'loaded') {
|
||||
this._loadedModelsLoadToken = null;
|
||||
}
|
||||
this.hideTooltip();
|
||||
this.animateTabContentTransition();
|
||||
this.updateTabButtonStates();
|
||||
@@ -67,10 +73,20 @@ export const tabsLoadedMethods = {
|
||||
async loadLoadedModels(workflow = null, { force = false } = {}) {
|
||||
if (!this.contentElement) return;
|
||||
|
||||
const loadToken = `loaded-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
||||
this._loadedModelsLoadToken = loadToken;
|
||||
const shouldRenderLoadedModels = () => (
|
||||
this.activeTab === 'loaded' &&
|
||||
this._loadedModelsLoadToken === loadToken &&
|
||||
this.contentElement
|
||||
);
|
||||
|
||||
try {
|
||||
workflow = workflow || this.getCurrentWorkflow();
|
||||
if (!workflow) {
|
||||
this.contentElement.innerHTML = '<p>No workflow loaded. Please load a workflow first.</p>';
|
||||
if (shouldRenderLoadedModels()) {
|
||||
this.contentElement.innerHTML = '<p>No workflow loaded. Please load a workflow first.</p>';
|
||||
}
|
||||
return;
|
||||
}
|
||||
this.syncWorkflowScopedQueue(workflow);
|
||||
@@ -82,11 +98,15 @@ export const tabsLoadedMethods = {
|
||||
this.cachedLoadedModelsSignature === workflowSignature &&
|
||||
this.cachedLoadedModelsData
|
||||
) {
|
||||
this.displayLoadedModels(this.contentElement, this.cachedLoadedModelsData);
|
||||
if (shouldRenderLoadedModels()) {
|
||||
this.displayLoadedModels(this.contentElement, this.cachedLoadedModelsData);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
this.contentElement.innerHTML = '<p>Loading loaded models...</p>';
|
||||
if (shouldRenderLoadedModels()) {
|
||||
this.contentElement.innerHTML = '<p>Loading loaded models...</p>';
|
||||
}
|
||||
|
||||
const response = await api.fetchApi('/model_resolver/loaded', {
|
||||
method: 'POST',
|
||||
@@ -99,14 +119,18 @@ export const tabsLoadedMethods = {
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
this.cachedLoadedModelsSignature = workflowSignature;
|
||||
this.cachedLoadedModelsData = data;
|
||||
this.saveLoadedModelsCacheForActiveWorkflow();
|
||||
this.displayLoadedModels(this.contentElement, data);
|
||||
if (this._loadedModelsLoadToken === loadToken) {
|
||||
this.cachedLoadedModelsSignature = workflowSignature;
|
||||
this.cachedLoadedModelsData = data;
|
||||
this.saveLoadedModelsCacheForActiveWorkflow();
|
||||
}
|
||||
if (shouldRenderLoadedModels()) {
|
||||
this.displayLoadedModels(this.contentElement, data);
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('Model Resolver: Error loading loaded models:', error);
|
||||
if (this.contentElement) {
|
||||
if (shouldRenderLoadedModels()) {
|
||||
this.contentElement.innerHTML = `<p class="mr-error-text">Error: ${error.message}</p>`;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user