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
|
||||
if (plan.deletions.length === 0) {
|
||||
updateStatus('prog-delete', 'Skipped', '#aaa');
|
||||
this.startParallelPhases(plan, activeItems, jobId);
|
||||
this.startParallelPhases(plan, activeItems, jobId, pSheet);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1987,7 +2025,7 @@
|
||||
google.script.run
|
||||
.withSuccessHandler(() => {
|
||||
updateStatus('prog-delete', 'Done', 'green');
|
||||
this.startParallelPhases(plan, activeItems, jobId);
|
||||
this.startParallelPhases(plan, activeItems, jobId, pSheet);
|
||||
})
|
||||
.withFailureHandler(e => {
|
||||
updateStatus('prog-delete', 'Failed', 'red');
|
||||
@ -1998,7 +2036,7 @@
|
||||
.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
|
||||
const updateStatus = (id, status, color) => {
|
||||
const el = document.getElementById(id);
|
||||
@ -2009,7 +2047,6 @@
|
||||
};
|
||||
|
||||
// Phase 2 & 3: Adoptions & Uploads (Parallel)
|
||||
|
||||
const pAdoption = new Promise((resolve, reject) => {
|
||||
if (plan.adoptions.length === 0) {
|
||||
updateStatus('prog-adopt', 'Skipped', '#aaa');
|
||||
@ -2043,34 +2080,36 @@
|
||||
Promise.all([pAdoption, pUpload])
|
||||
.then(() => {
|
||||
// Phase 4: Reorder
|
||||
if (plan.reorders.length === 0 && plan.deletions.length === 0 && plan.adoptions.length === 0 && plan.uploads.length === 0) {
|
||||
// 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.
|
||||
const pReorder = new Promise((resolve, reject) => {
|
||||
if (plan.reorders.length === 0) {
|
||||
updateStatus('prog-reorder', 'Skipped', '#aaa');
|
||||
updateStatus('prog-sheet', 'Running...', 'blue');
|
||||
// Still update sheet? Yes.
|
||||
}
|
||||
resolve();
|
||||
return;
|
||||
}
|
||||
|
||||
if (plan.reorders.length > 0) {
|
||||
updateStatus('prog-reorder', 'Running...', 'blue');
|
||||
google.script.run
|
||||
.withSuccessHandler(() => {
|
||||
updateStatus('prog-reorder', 'Done', 'green');
|
||||
updateStatus('prog-sheet', 'Running...', 'blue');
|
||||
this.runSheetUpdate();
|
||||
resolve();
|
||||
})
|
||||
.withFailureHandler(e => {
|
||||
alert("Reorder Phase Failed: " + e.message);
|
||||
this.finishSave(false);
|
||||
reject(e);
|
||||
})
|
||||
.executeSavePhase(state.sku, 'reorder', plan.reorders, jobId);
|
||||
} else {
|
||||
updateStatus('prog-sheet', 'Running...', 'blue');
|
||||
this.runSheetUpdate();
|
||||
}
|
||||
});
|
||||
|
||||
// Wait for BOTH Reorder and Early Sheet Update
|
||||
Promise.all([pReorder, pSheet])
|
||||
.then(() => {
|
||||
this.finishSave(true);
|
||||
})
|
||||
.catch(e => {
|
||||
// If reorder failed, we already alerted.
|
||||
console.error("Save completion failed", e);
|
||||
this.finishSave(false);
|
||||
});
|
||||
})
|
||||
.catch(e => {
|
||||
alert("Parallel Execution Failed: " + e.message);
|
||||
@ -2079,22 +2118,6 @@
|
||||
});
|
||||
},
|
||||
|
||||
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) {
|
||||
this.stopLogPolling();
|
||||
if (success) {
|
||||
|
||||
@ -119,7 +119,7 @@ export function saveMediaChanges(sku: string, finalState: any[], jobId: string |
|
||||
return logs
|
||||
}
|
||||
|
||||
export function updateSpreadsheetThumbnail(sku: string) {
|
||||
export function updateSpreadsheetThumbnail(sku: string, forcedThumbnailUrl: string | null = null) {
|
||||
const config = new Config()
|
||||
const driveService = new GASDriveService()
|
||||
const shop = new Shop()
|
||||
@ -128,6 +128,31 @@ export function updateSpreadsheetThumbnail(sku: string) {
|
||||
const mediaService = new MediaService(driveService, shopifyMediaService, networkService, config)
|
||||
|
||||
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);
|
||||
|
||||
// Need Shopify ID for accurate state logic?
|
||||
|
||||
Reference in New Issue
Block a user