Optimize Media Manager sheet update trigger
- Update mediaHandlers.ts to accept an optional forcedThumbnailUrl in updateSpreadsheetThumbnail, enabling updates without re-fetching backend state. - Update MediaManager.html execution plan to trigger the sheet update immediately (optimistically) using the predicted first item from the plan, running in parallel with other execution phases. - Ensure the execution flow waits for both the sheet update and other operations to complete before finishing.
This commit is contained in:
@ -1975,10 +1975,48 @@
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Optimistic Sheet Update (Immediate Parallel Execution)
|
||||||
|
const pSheet = new Promise(resolve => {
|
||||||
|
const first = activeItems[0];
|
||||||
|
let url = null;
|
||||||
|
if (first) {
|
||||||
|
// Determine best URL based on item type
|
||||||
|
// Note: MediaHandlers logic prioritizes Shopify URL if present.
|
||||||
|
// We mimic that logic here.
|
||||||
|
const isShopify = (first.source === 'shopify_only' || first.source === 'synced') && first.thumbnail;
|
||||||
|
|
||||||
|
if (isShopify) {
|
||||||
|
url = first.thumbnail;
|
||||||
|
} else if (first.id) {
|
||||||
|
// Drive Item - Construct URL compatible with server logic
|
||||||
|
url = "https://drive.google.com/thumbnail?id=" + first.id + "&sz=w400";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!url) {
|
||||||
|
updateStatus('prog-sheet', 'Skipped', '#aaa');
|
||||||
|
resolve();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
updateStatus('prog-sheet', 'Running...', 'blue');
|
||||||
|
google.script.run
|
||||||
|
.withSuccessHandler(() => {
|
||||||
|
updateStatus('prog-sheet', 'Done', 'green');
|
||||||
|
resolve();
|
||||||
|
})
|
||||||
|
.withFailureHandler(e => {
|
||||||
|
console.warn("Sheet update failed", e);
|
||||||
|
updateStatus('prog-sheet', 'Failed (Warn)', 'orange');
|
||||||
|
resolve(); // Don't block completion
|
||||||
|
})
|
||||||
|
.updateSpreadsheetThumbnail(state.sku, url);
|
||||||
|
});
|
||||||
|
|
||||||
// Phase 1: Deletions
|
// Phase 1: Deletions
|
||||||
if (plan.deletions.length === 0) {
|
if (plan.deletions.length === 0) {
|
||||||
updateStatus('prog-delete', 'Skipped', '#aaa');
|
updateStatus('prog-delete', 'Skipped', '#aaa');
|
||||||
this.startParallelPhases(plan, activeItems, jobId);
|
this.startParallelPhases(plan, activeItems, jobId, pSheet);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1987,7 +2025,7 @@
|
|||||||
google.script.run
|
google.script.run
|
||||||
.withSuccessHandler(() => {
|
.withSuccessHandler(() => {
|
||||||
updateStatus('prog-delete', 'Done', 'green');
|
updateStatus('prog-delete', 'Done', 'green');
|
||||||
this.startParallelPhases(plan, activeItems, jobId);
|
this.startParallelPhases(plan, activeItems, jobId, pSheet);
|
||||||
})
|
})
|
||||||
.withFailureHandler(e => {
|
.withFailureHandler(e => {
|
||||||
updateStatus('prog-delete', 'Failed', 'red');
|
updateStatus('prog-delete', 'Failed', 'red');
|
||||||
@ -1998,7 +2036,7 @@
|
|||||||
.executeSavePhase(state.sku, 'deletions', plan.deletions, jobId);
|
.executeSavePhase(state.sku, 'deletions', plan.deletions, jobId);
|
||||||
},
|
},
|
||||||
|
|
||||||
startParallelPhases(plan, activeItems, jobId) {
|
startParallelPhases(plan, activeItems, jobId, pSheet) {
|
||||||
// Helper function defined at the top to avoid hoisting issues
|
// Helper function defined at the top to avoid hoisting issues
|
||||||
const updateStatus = (id, status, color) => {
|
const updateStatus = (id, status, color) => {
|
||||||
const el = document.getElementById(id);
|
const el = document.getElementById(id);
|
||||||
@ -2009,7 +2047,6 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Phase 2 & 3: Adoptions & Uploads (Parallel)
|
// Phase 2 & 3: Adoptions & Uploads (Parallel)
|
||||||
|
|
||||||
const pAdoption = new Promise((resolve, reject) => {
|
const pAdoption = new Promise((resolve, reject) => {
|
||||||
if (plan.adoptions.length === 0) {
|
if (plan.adoptions.length === 0) {
|
||||||
updateStatus('prog-adopt', 'Skipped', '#aaa');
|
updateStatus('prog-adopt', 'Skipped', '#aaa');
|
||||||
@ -2043,58 +2080,44 @@
|
|||||||
Promise.all([pAdoption, pUpload])
|
Promise.all([pAdoption, pUpload])
|
||||||
.then(() => {
|
.then(() => {
|
||||||
// Phase 4: Reorder
|
// Phase 4: Reorder
|
||||||
if (plan.reorders.length === 0 && plan.deletions.length === 0 && plan.adoptions.length === 0 && plan.uploads.length === 0) {
|
const pReorder = new Promise((resolve, reject) => {
|
||||||
// Nothing to reorder usually means no changes, but if we just changed metadata?
|
|
||||||
// Reorder always runs to ensure "0001" suffixes are correct if we deleted something.
|
|
||||||
// But if plan.reorders is empty, likely no items left.
|
|
||||||
if (plan.reorders.length === 0) {
|
if (plan.reorders.length === 0) {
|
||||||
updateStatus('prog-reorder', 'Skipped', '#aaa');
|
updateStatus('prog-reorder', 'Skipped', '#aaa');
|
||||||
updateStatus('prog-sheet', 'Running...', 'blue');
|
resolve();
|
||||||
// Still update sheet? Yes.
|
return;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (plan.reorders.length > 0) {
|
updateStatus('prog-reorder', 'Running...', 'blue');
|
||||||
updateStatus('prog-reorder', 'Running...', 'blue');
|
google.script.run
|
||||||
google.script.run
|
.withSuccessHandler(() => {
|
||||||
.withSuccessHandler(() => {
|
updateStatus('prog-reorder', 'Done', 'green');
|
||||||
updateStatus('prog-reorder', 'Done', 'green');
|
resolve();
|
||||||
updateStatus('prog-sheet', 'Running...', 'blue');
|
})
|
||||||
this.runSheetUpdate();
|
.withFailureHandler(e => {
|
||||||
})
|
alert("Reorder Phase Failed: " + e.message);
|
||||||
.withFailureHandler(e => {
|
reject(e);
|
||||||
alert("Reorder Phase Failed: " + e.message);
|
})
|
||||||
this.finishSave(false);
|
.executeSavePhase(state.sku, 'reorder', plan.reorders, jobId);
|
||||||
})
|
});
|
||||||
.executeSavePhase(state.sku, 'reorder', plan.reorders, jobId);
|
|
||||||
} else {
|
// Wait for BOTH Reorder and Early Sheet Update
|
||||||
updateStatus('prog-sheet', 'Running...', 'blue');
|
Promise.all([pReorder, pSheet])
|
||||||
this.runSheetUpdate();
|
.then(() => {
|
||||||
}
|
this.finishSave(true);
|
||||||
|
})
|
||||||
|
.catch(e => {
|
||||||
|
// If reorder failed, we already alerted.
|
||||||
|
console.error("Save completion failed", e);
|
||||||
|
this.finishSave(false);
|
||||||
|
});
|
||||||
})
|
})
|
||||||
.catch(e => {
|
.catch(e => {
|
||||||
alert("Parallel Execution Failed: " + e.message);
|
alert("Parallel Execution Failed: " + e.message);
|
||||||
this.stopLogPolling();
|
this.stopLogPolling();
|
||||||
ui.setSavingState(false);
|
ui.setSavingState(false);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
runSheetUpdate() {
|
|
||||||
google.script.run
|
|
||||||
.withSuccessHandler(() => {
|
|
||||||
const el = document.getElementById('prog-sheet');
|
|
||||||
if (el) { el.style.color = 'green'; el.innerText = el.innerText.split(':')[0] + ': Done'; }
|
|
||||||
this.finishSave(true);
|
|
||||||
})
|
|
||||||
.withFailureHandler(e => {
|
|
||||||
console.error("Sheet update failed", e);
|
|
||||||
const el = document.getElementById('prog-sheet');
|
|
||||||
if (el) { el.style.color = 'orange'; el.innerText = el.innerText.split(':')[0] + ': Done (Sheet Warn)'; }
|
|
||||||
this.finishSave(true);
|
|
||||||
})
|
|
||||||
.updateSpreadsheetThumbnail(state.sku);
|
|
||||||
},
|
|
||||||
|
|
||||||
finishSave(success) {
|
finishSave(success) {
|
||||||
this.stopLogPolling();
|
this.stopLogPolling();
|
||||||
if (success) {
|
if (success) {
|
||||||
|
|||||||
@ -119,7 +119,7 @@ export function saveMediaChanges(sku: string, finalState: any[], jobId: string |
|
|||||||
return logs
|
return logs
|
||||||
}
|
}
|
||||||
|
|
||||||
export function updateSpreadsheetThumbnail(sku: string) {
|
export function updateSpreadsheetThumbnail(sku: string, forcedThumbnailUrl: string | null = null) {
|
||||||
const config = new Config()
|
const config = new Config()
|
||||||
const driveService = new GASDriveService()
|
const driveService = new GASDriveService()
|
||||||
const shop = new Shop()
|
const shop = new Shop()
|
||||||
@ -128,6 +128,31 @@ export function updateSpreadsheetThumbnail(sku: string) {
|
|||||||
const mediaService = new MediaService(driveService, shopifyMediaService, networkService, config)
|
const mediaService = new MediaService(driveService, shopifyMediaService, networkService, config)
|
||||||
|
|
||||||
const ss = new GASSpreadsheetService();
|
const ss = new GASSpreadsheetService();
|
||||||
|
|
||||||
|
// Optimization: If forced URL provided (optimistic update), skip state calculation
|
||||||
|
if (forcedThumbnailUrl) {
|
||||||
|
try {
|
||||||
|
const row = ss.getRowNumberByColumnValue("product_inventory", "sku", sku);
|
||||||
|
if (row) {
|
||||||
|
const thumbUrl = forcedThumbnailUrl;
|
||||||
|
try {
|
||||||
|
const image = SpreadsheetApp.newCellImage()
|
||||||
|
.setSourceUrl(thumbUrl)
|
||||||
|
.setAltTextTitle(sku)
|
||||||
|
.setAltTextDescription(`Thumbnail for ${sku}`)
|
||||||
|
.build();
|
||||||
|
ss.setCellValueByColumnName("product_inventory", row, "thumbnail", image);
|
||||||
|
} catch (builderErr) {
|
||||||
|
ss.setCellValueByColumnName("product_inventory", row, "thumbnail", `=IMAGE("${thumbUrl}")`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
} catch (e) {
|
||||||
|
console.warn("Failed to update sheet thumbnail (forced)", e);
|
||||||
|
throw new Error("Sheet Update Failed: " + e.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const product = new Product(sku);
|
const product = new Product(sku);
|
||||||
|
|
||||||
// Need Shopify ID for accurate state logic?
|
// Need Shopify ID for accurate state logic?
|
||||||
|
|||||||
Reference in New Issue
Block a user