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:
Ben Miller
2025-12-31 08:43:04 -07:00
parent 55a89a0802
commit ad67dd9ab5

View File

@ -1678,6 +1678,13 @@
this.matches = newMatches;
this.currentMatchIndex = 0;
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();
} else {
// No matches, show UI
@ -1740,35 +1747,36 @@
}, 50);
},
pendingLinks: 0,
confirmLink() {
var match = this.matches[this.currentMatchIndex];
var _this = this;
document.getElementById('btn-match-confirm').disabled = true;
document.getElementById('btn-match-confirm').innerText = "Linking...";
document.getElementById('btn-match-skip').disabled = true;
// Async Fire & Forget (from UI perspective)
this.pendingLinks++;
ui.logStatus('link', 'Linking ' + match.drive.filename + '...', 'info');
google.script.run
.withSuccessHandler(function () {
ui.logStatus('link', 'Linked ' + match.drive.filename, 'success');
_this.nextMatch();
_this.pendingLinks--;
_this.checkMatchingDone();
})
.withFailureHandler(function (e) {
alert("Failed to link: " + e.message);
document.getElementById('btn-match-confirm').disabled = false;
document.getElementById('btn-match-confirm').innerText = "Yes, Link";
document.getElementById('btn-match-skip').disabled = false;
ui.logStatus('link', 'Failed to link ' + match.drive.filename + ': ' + e.message, 'error');
_this.pendingLinks--;
_this.checkMatchingDone();
})
.linkDriveFileToShopifyMedia(state.sku, match.drive.id, match.shopify.id);
// Move to next immediately
this.nextMatch();
},
skipLink() {
document.getElementById('btn-match-skip').innerText = "Skipping...";
document.getElementById('btn-match-skip').disabled = true;
document.getElementById('btn-match-confirm').disabled = true;
setTimeout(() => this.nextMatch(), 200);
// No async op needed for skip
this.nextMatch();
},
nextMatch() {
@ -1776,13 +1784,38 @@
if (this.currentMatchIndex < this.matches.length) {
this.renderMatch();
} 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';
ui.logStatus('info', 'Matching complete. Reloading...', 'info');
document.getElementById('loading-ui').style.display = 'block';
// Reload to get fresh state. Since hasRunMatching is true, it shouldn't trigger again.
this.loadMedia(true);
}
},
// Sand Animation (Global Loop)