Optimize Media Matching Workflow
- **Preload Thumbnails**: Implemented image preloading in the media matching wizard to ensure instant rendering of candidate matches. - **Async Linking**: Refactored the linking confirmation to be asynchronous. The UI now optimistically advances to the next match immediately, performing the backend linking in the background. - **Completion Gate**: Added a check to ensure all pending background linking operations verify completion before the wizard closes and reloading occurs. - **Video Support**: Verified that filename-based matching correctly handles video files by extracting filenames from Shopify video URLs.
This commit is contained in:
@ -1678,6 +1678,13 @@
|
|||||||
this.matches = newMatches;
|
this.matches = newMatches;
|
||||||
this.currentMatchIndex = 0;
|
this.currentMatchIndex = 0;
|
||||||
ui.logStatus('info', 'Found ' + newMatches.length + ' potential matches. Starting matching wizard...', 'info');
|
ui.logStatus('info', 'Found ' + newMatches.length + ' potential matches. Starting matching wizard...', 'info');
|
||||||
|
|
||||||
|
// Preload Images
|
||||||
|
newMatches.forEach(function (m) {
|
||||||
|
if (m.drive && m.drive.thumbnail) new Image().src = m.drive.thumbnail;
|
||||||
|
if (m.shopify && m.shopify.thumbnail) new Image().src = m.shopify.thumbnail;
|
||||||
|
});
|
||||||
|
|
||||||
this.startMatching();
|
this.startMatching();
|
||||||
} else {
|
} else {
|
||||||
// No matches, show UI
|
// No matches, show UI
|
||||||
@ -1740,35 +1747,36 @@
|
|||||||
}, 50);
|
}, 50);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
pendingLinks: 0,
|
||||||
|
|
||||||
confirmLink() {
|
confirmLink() {
|
||||||
var match = this.matches[this.currentMatchIndex];
|
var match = this.matches[this.currentMatchIndex];
|
||||||
var _this = this;
|
var _this = this;
|
||||||
|
|
||||||
document.getElementById('btn-match-confirm').disabled = true;
|
// Async Fire & Forget (from UI perspective)
|
||||||
document.getElementById('btn-match-confirm').innerText = "Linking...";
|
this.pendingLinks++;
|
||||||
document.getElementById('btn-match-skip').disabled = true;
|
|
||||||
|
|
||||||
ui.logStatus('link', 'Linking ' + match.drive.filename + '...', 'info');
|
ui.logStatus('link', 'Linking ' + match.drive.filename + '...', 'info');
|
||||||
|
|
||||||
google.script.run
|
google.script.run
|
||||||
.withSuccessHandler(function () {
|
.withSuccessHandler(function () {
|
||||||
ui.logStatus('link', 'Linked ' + match.drive.filename, 'success');
|
ui.logStatus('link', 'Linked ' + match.drive.filename, 'success');
|
||||||
_this.nextMatch();
|
_this.pendingLinks--;
|
||||||
|
_this.checkMatchingDone();
|
||||||
})
|
})
|
||||||
.withFailureHandler(function (e) {
|
.withFailureHandler(function (e) {
|
||||||
alert("Failed to link: " + e.message);
|
ui.logStatus('link', 'Failed to link ' + match.drive.filename + ': ' + e.message, 'error');
|
||||||
document.getElementById('btn-match-confirm').disabled = false;
|
_this.pendingLinks--;
|
||||||
document.getElementById('btn-match-confirm').innerText = "Yes, Link";
|
_this.checkMatchingDone();
|
||||||
document.getElementById('btn-match-skip').disabled = false;
|
|
||||||
})
|
})
|
||||||
.linkDriveFileToShopifyMedia(state.sku, match.drive.id, match.shopify.id);
|
.linkDriveFileToShopifyMedia(state.sku, match.drive.id, match.shopify.id);
|
||||||
|
|
||||||
|
// Move to next immediately
|
||||||
|
this.nextMatch();
|
||||||
},
|
},
|
||||||
|
|
||||||
skipLink() {
|
skipLink() {
|
||||||
document.getElementById('btn-match-skip').innerText = "Skipping...";
|
// No async op needed for skip
|
||||||
document.getElementById('btn-match-skip').disabled = true;
|
this.nextMatch();
|
||||||
document.getElementById('btn-match-confirm').disabled = true;
|
|
||||||
setTimeout(() => this.nextMatch(), 200);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
nextMatch() {
|
nextMatch() {
|
||||||
@ -1776,13 +1784,38 @@
|
|||||||
if (this.currentMatchIndex < this.matches.length) {
|
if (this.currentMatchIndex < this.matches.length) {
|
||||||
this.renderMatch();
|
this.renderMatch();
|
||||||
} else {
|
} else {
|
||||||
// Done
|
this.checkMatchingDone();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
checkMatchingDone() {
|
||||||
|
// 1. Are we visually done?
|
||||||
|
if (this.currentMatchIndex < this.matches.length) return;
|
||||||
|
|
||||||
|
// 2. Are background jobs done?
|
||||||
|
if (this.pendingLinks > 0) {
|
||||||
|
// Show "Waiting" state in Modal
|
||||||
|
document.getElementById('matching-modal').style.display = 'flex'; // Ensure open
|
||||||
|
document.getElementById('btn-match-confirm').disabled = true;
|
||||||
|
document.getElementById('btn-match-skip').disabled = true;
|
||||||
|
|
||||||
|
// Re-purpose the modal content temporarily or overlay?
|
||||||
|
// Let's just update the title/status
|
||||||
|
var title = document.querySelector('#matching-modal h3');
|
||||||
|
if (title) title.innerText = "Finalizing...";
|
||||||
|
|
||||||
|
var p = document.querySelector('#matching-modal p');
|
||||||
|
if (p) p.innerText = "Finishing " + this.pendingLinks + " pending link operations...";
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// All Done
|
||||||
document.getElementById('matching-modal').style.display = 'none';
|
document.getElementById('matching-modal').style.display = 'none';
|
||||||
ui.logStatus('info', 'Matching complete. Reloading...', 'info');
|
ui.logStatus('info', 'Matching complete. Reloading...', 'info');
|
||||||
document.getElementById('loading-ui').style.display = 'block';
|
document.getElementById('loading-ui').style.display = 'block';
|
||||||
// Reload to get fresh state. Since hasRunMatching is true, it shouldn't trigger again.
|
// Reload to get fresh state. Since hasRunMatching is true, it shouldn't trigger again.
|
||||||
this.loadMedia(true);
|
this.loadMedia(true);
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
// Sand Animation (Global Loop)
|
// Sand Animation (Global Loop)
|
||||||
|
|||||||
Reference in New Issue
Block a user