diff --git a/src/MediaManager.html b/src/MediaManager.html index c2af61b..ed14e2e 100644 --- a/src/MediaManager.html +++ b/src/MediaManager.html @@ -362,9 +362,9 @@ Loading... ... - - - + + + @@ -385,8 +385,8 @@ style="flex: 1; font-size: 13px;"> Your Computer - - + + @@ -404,41 +404,43 @@
Initializing...
-
-
-
-

Gallery (0)

- + + +
+
+
+

Gallery (0)

+ +
+
+ + +
-
- - + + + + +
+ +
+ +
+ +
- - - - -
- -
- -
- - -
-
-
+
@@ -532,7 +534,7 @@ @@ -603,16 +605,16 @@ this.items.forEach(function (i) { if (i._deleted) actions.push({ type: 'delete', name: i.filename || 'Item' }); - }); + }); this.items.forEach(function (i) { if (i._deleted) return; if (!initialIds.has(i.id)) { actions.push({ type: 'upload', name: i.filename || 'New Item' }); } else if (i.status === 'drive_only') { - actions.push({ type: 'sync_upload', name: i.filename || 'Item' }); - } - }); + actions.push({ type: 'sync_upload', name: i.filename || 'Item' }); + } + }); // 3. Reorders var activeItems = this.items.filter(function (i) { return !i._deleted; }); @@ -627,14 +629,14 @@ var orderChanged = false; if (initialCommon.length === currentCommon.length) { for (var k = 0; k < initialCommon.length; k++) { - if (initialCommon[k].id !== currentCommon[k].id) { - orderChanged = true; - break; - } + if (initialCommon[k].id !== currentCommon[k].id) { + orderChanged = true; + break; } - } else { - // If lengths differ despite logic, assume change or weird state } + } else { + // If lengths differ despite logic, assume change or weird state + } if (orderChanged) { actions.push({ type: 'reorder', name: 'Reorder Gallery' }); @@ -698,6 +700,44 @@ this.saveBtn.innerText = enable ? "Save Changes" : "No Changes"; }; + UI.prototype.showPhotoSession = function (url) { + var uiEl = document.getElementById('photos-session-ui'); + var link = document.getElementById('photos-session-link'); + var status = document.getElementById('photos-session-status'); + + uiEl.style.display = 'block'; + link.href = url; + // We also open it automatically in a popup + const width = 1200; + const height = 800; + const left = (screen.width - width) / 2; + const top = (screen.height - height) / 2; + + // Attempt popup + const popup = window.open(url, 'googlePhotos', `width=${width},height=${height},top=${top},left=${left}`); + + if (popup) { + link.innerText = "Re-open Popup ↗"; + link.onclick = function (e) { + e.preventDefault(); + window.open(url, 'googlePhotos', `width=${width},height=${height},top=${top},left=${left}`); + } + } else { + link.innerText = "Open Google Photos ↗"; + link.onclick = null; // Default href behavior + } + + status.innerText = "Waiting for selection in popup..."; + }; + + UI.prototype.closePhotoSession = function () { + document.getElementById('photos-session-ui').style.display = 'none'; + }; + + UI.prototype.updatePhotoStatus = function (msg) { + document.getElementById('photos-session-status').innerText = msg; + }; + UI.prototype.render = function (items) { this.grid.innerHTML = ''; var _this = this; // Capture 'this' for callbacks @@ -720,15 +760,15 @@ filter: '.deleted-item', ghostClass: 'sortable-ghost', dragClass: 'sortable-drag', - onEnd: function () { - var newOrderIds = Array.from(_this.grid.children).map(function (el) { return el.dataset.id; }); - var newItems = newOrderIds.map(function (id) { - return state.items.find(function (i) { return i.id === id; }); - }).filter(Boolean); - state.items = newItems; - state.checkDirty(); - } - }); + onEnd: function () { + var newOrderIds = Array.from(_this.grid.children).map(function (el) { return el.dataset.id; }); + var newItems = newOrderIds.map(function (id) { + return state.items.find(function (i) { return i.id === id; }); + }).filter(Boolean); + state.items = newItems; + state.checkDirty(); + } + }); }; UI.prototype.setLoadingState = function (isLoading) { @@ -847,14 +887,14 @@ vid.src = previewUrlParam; vid.style.display = 'block'; vid.play().catch(console.warn); - } else { + } else { // Fallback if URL missing console.warn("Missing contentUrl for Shopify video"); } } } else { - img.src = item.thumbnail; - img.style.display = 'block'; + img.src = item.thumbnail; + img.style.display = 'block'; } modal.style.display = 'flex'; @@ -874,23 +914,23 @@ if (plan.actions.length === 0) { container.innerHTML = '
No pending changes.
'; } else { - var html = plan.actions.map(function (a, i) { - var icon = '•'; - if (a.type === 'delete') icon = '🗑️'; - if (a.type === 'upload') icon = '📤'; - if (a.type === 'sync_upload') icon = '☁️'; - if (a.type === 'reorder') icon = '🔢'; + var html = plan.actions.map(function (a, i) { + var icon = '•'; + if (a.type === 'delete') icon = '🗑️'; + if (a.type === 'upload') icon = '📤'; + if (a.type === 'sync_upload') icon = '☁️'; + if (a.type === 'reorder') icon = '🔢'; - var label = ""; - if (a.type === 'delete') label = 'Delete ' + a.name + ''; - if (a.type === 'upload') label = 'Upload New ' + a.name + ''; - if (a.type === 'sync_upload') label = 'Sync Drive File ' + a.name + ''; - if (a.type === 'reorder') label = 'Update Order'; + var label = ""; + if (a.type === 'delete') label = 'Delete ' + a.name + ''; + if (a.type === 'upload') label = 'Upload New ' + a.name + ''; + if (a.type === 'sync_upload') label = 'Sync Drive File ' + a.name + ''; + if (a.type === 'reorder') label = 'Update Order'; - return '
' + (i + 1) + '. ' + icon + ' ' + label + '
'; - }).join(''); - container.innerHTML = html; - } + return '
' + (i + 1) + '. ' + icon + ' ' + label + '
'; + }).join(''); + container.innerHTML = html; + } document.getElementById('details-modal').style.display = 'flex'; }; @@ -900,260 +940,242 @@ document.getElementById('details-modal').style.display = 'none'; }; - UI.prototype.showPhotoSession = function (url) { - var uiEl = document.getElementById('photos-session-ui'); - var link = document.getElementById('photos-session-link'); - var status = document.getElementById('photos-session-status'); - - uiEl.style.display = 'block'; - link.href = url; - link.style.display = 'block'; - status.innerText = "Waiting for selection..."; - }; - - UI.prototype.closePhotoSession = function () { - document.getElementById('photos-session-ui').style.display = 'none'; - }; - - UI.prototype.updatePhotoStatus = function (msg) { - document.getElementById('photos-session-status').innerText = msg; - }; - var ui = new UI(); window.ui = ui; - /** - * Data Controller - */ - var controller = { - init() { - // Start polling for SKU selection - setInterval(() => this.checkSku(), 2000); - this.checkSku(); - }, + /** + * Data Controller + */ + var controller = { + init() { + // Start polling for SKU selection + setInterval(() => this.checkSku(), 2000); + this.checkSku(); + }, - checkSku() { - google.script.run - .withSuccessHandler(info => { - // Info is now { sku, title } or null - const sku = info ? info.sku : null; + checkSku() { + google.script.run + .withSuccessHandler(info => { + // Info is now { sku, title } or null + const sku = info ? info.sku : null; - if (sku && sku !== state.sku) { - state.setSku(info); // Pass whole object - this.loadMedia(); - } else if (!sku && !state.sku) { - // If we don't have a SKU and haven't shown error yet - if (document.getElementById('error-ui').style.display !== 'flex') { - this.loadMedia(); - } - } - }) - .getSelectedProductInfo(); - }, - - loadMedia(preserveLogs = false) { - // Resolve SKU/Title - prefer state, fallback to DOM - let sku = state.sku; - let title = state.title; - - if (!sku) { - const domSku = document.getElementById('current-sku').innerText; - if (domSku && domSku !== '...') sku = domSku; - } - - // CHECK FOR MISSING SKU - if (!sku || sku === '...') { - console.warn("No SKU found. Showing error."); - document.getElementById('loading-ui').style.display = 'none'; - document.getElementById('main-ui').style.display = 'none'; - document.getElementById('error-ui').style.display = 'flex'; - return; - } - - if (!title) { - const domTitle = document.getElementById('current-title').innerText; - if (domTitle && domTitle !== 'Loading...') title = domTitle; - } - - // Show Main UI immediately so logs are visible - document.getElementById('loading-ui').style.display = 'none'; - document.getElementById('main-ui').style.display = 'block'; - - // Set Inline Loading State - ui.setLoadingState(true); - - // Reset State (this calls ui.updateSku) - state.setSku({ sku, title }); - - if (!preserveLogs) { - document.getElementById('status-log-container').innerHTML = ''; + if (sku && sku !== state.sku) { + state.setSku(info); // Pass whole object + this.loadMedia(); + } else if (!sku && !state.sku) { + // If we don't have a SKU and haven't shown error yet + if (document.getElementById('error-ui').style.display !== 'flex') { + this.loadMedia(); } - ui.toggleLogBtn.style.display = 'inline-block'; - ui.toggleLog(true); // Force Show Log to see progress + } + }) + .getSelectedProductInfo(); + }, - // 1. Run Diagnostics - // 1. Run Diagnostics - ui.logStatus('init', 'Initializing access...', 'info'); + loadMedia(preserveLogs = false) { + // Resolve SKU/Title - prefer state, fallback to DOM + let sku = state.sku; + let title = state.title; - google.script.run - .withSuccessHandler(function (diagnostics) { - // Drive Status - if (diagnostics.drive.status === 'ok') { - ui.logStatus('drive', `Drive Folder: ok (${diagnostics.drive.fileCount} files) Open Folder ↗`, 'success'); - ui.setDriveLink(diagnostics.drive.folderUrl); - } else { - ui.logStatus('drive', `Drive Check Failed: ${diagnostics.drive.error}`, 'error'); - } + if (!sku) { + const domSku = document.getElementById('current-sku').innerText; + if (domSku && domSku !== '...') sku = domSku; + } - // Capture Token - if (diagnostics.token) state.token = diagnostics.token; + // CHECK FOR MISSING SKU + if (!sku || sku === '...') { + console.warn("No SKU found. Showing error."); + document.getElementById('loading-ui').style.display = 'none'; + document.getElementById('main-ui').style.display = 'none'; + document.getElementById('error-ui').style.display = 'flex'; + return; + } - // Shopify Status - if (diagnostics.shopify.status === 'ok') { - ui.logStatus('shopify', `Shopify Product: ok (${diagnostics.shopify.mediaCount} media) (ID: ${diagnostics.shopify.id}) Open Admin ↗`, 'success'); - ui.setShopifyLink(diagnostics.shopify.adminUrl); - } else if (diagnostics.shopify.status === 'skipped') { - ui.logStatus('shopify', 'Shopify Product: Not linked/Found', 'info'); + if (!title) { + const domTitle = document.getElementById('current-title').innerText; + if (domTitle && domTitle !== 'Loading...') title = domTitle; + } + + // Show Main UI immediately so logs are visible + document.getElementById('loading-ui').style.display = 'none'; + document.getElementById('main-ui').style.display = 'block'; + + // Set Inline Loading State + ui.setLoadingState(true); + + // Reset State (this calls ui.updateSku) + state.setSku({ sku, title }); + + if (!preserveLogs) { + document.getElementById('status-log-container').innerHTML = ''; + } + ui.toggleLogBtn.style.display = 'inline-block'; + ui.toggleLog(true); // Force Show Log to see progress + + // 1. Run Diagnostics + // 1. Run Diagnostics + ui.logStatus('init', 'Initializing access...', 'info'); + + google.script.run + .withSuccessHandler(function (diagnostics) { + // Drive Status + if (diagnostics.drive.status === 'ok') { + ui.logStatus('drive', `Drive Folder: ok (${diagnostics.drive.fileCount} files) Open Folder ↗`, 'success'); + ui.setDriveLink(diagnostics.drive.folderUrl); + } else { + ui.logStatus('drive', `Drive Check Failed: ${diagnostics.drive.error}`, 'error'); + } + + // Capture Token + if (diagnostics.token) state.token = diagnostics.token; + + // Shopify Status + if (diagnostics.shopify.status === 'ok') { + ui.logStatus('shopify', `Shopify Product: ok (${diagnostics.shopify.mediaCount} media) (ID: ${diagnostics.shopify.id}) Open Admin ↗`, 'success'); + ui.setShopifyLink(diagnostics.shopify.adminUrl); + } else if (diagnostics.shopify.status === 'skipped') { + ui.logStatus('shopify', 'Shopify Product: Not linked/Found', 'info'); + } else { + ui.logStatus('shopify', `Shopify Check Failed: ${diagnostics.shopify.error}`, 'error'); + } + + ui.logStatus('fetch', 'Fetching full media state (this may take a moment)...', 'info'); + + // 2. Load Full Media + google.script.run + .withSuccessHandler(function (items) { + // Normalize items + const normalized = items.map(i => ({ + ...i, + id: i.id || Math.random().toString(36).substr(2, 9), + status: i.source || 'drive_only', // Fix: Use source as status + source: i.source, + _deleted: false // Init soft delete flag + })); + + state.setItems(normalized); + + if (!controller.hasRunMatching) { + controller.hasRunMatching = true; + controller.checkMatches(normalized); } else { - ui.logStatus('shopify', `Shopify Check Failed: ${diagnostics.shopify.error}`, 'error'); + controller.showGallery(); } - ui.logStatus('fetch', 'Fetching full media state (this may take a moment)...', 'info'); - - // 2. Load Full Media - google.script.run - .withSuccessHandler(function (items) { - // Normalize items - const normalized = items.map(i => ({ - ...i, - id: i.id || Math.random().toString(36).substr(2, 9), - status: i.source || 'drive_only', // Fix: Use source as status - source: i.source, - _deleted: false // Init soft delete flag - })); - - state.setItems(normalized); - - if (!controller.hasRunMatching) { - controller.hasRunMatching = true; - controller.checkMatches(normalized); - } else { - controller.showGallery(); - } - - }) - .withFailureHandler(function (err) { - ui.logStatus('fatal', `Failed to load media: ${err.message}`, 'error'); - }) - .getMediaForSku(sku); - }) - .withFailureHandler(function (err) { - ui.logStatus('fatal', `Diagnostics failed: ${err.message}`, 'error'); - }) - .getMediaDiagnostics(sku, ""); - }, - - saveChanges() { - ui.toggleSave(false); - ui.saveBtn.innerText = "Saving..."; - - ui.saveBtn.innerText = "Saving..."; - - // Filter out deleted items so they are actually removed - const activeItems = state.items.filter(i => !i._deleted); - - // Send final state array to backend - google.script.run - .withSuccessHandler((logs) => { - ui.saveBtn.innerText = "Saved!"; - - // Verify logs is an array (backward compatibility check) - if (Array.isArray(logs)) { - document.getElementById('status-log-container').innerHTML = ''; - logs.forEach(l => ui.logStatus('save', l, 'info')); - ui.toggleLog(true); // Force show - } else { - // Fallback for old backend - alert("Changes Saved & Synced!"); - } - - // Reload to get fresh IDs/State, preserving the save logs - setTimeout(() => this.loadMedia(true), 1500); + .withFailureHandler(function (err) { + ui.logStatus('fatal', `Failed to load media: ${err.message}`, 'error'); }) - .withFailureHandler(e => { + .getMediaForSku(sku); + + }) + .withFailureHandler(function (err) { + ui.logStatus('fatal', `Diagnostics failed: ${err.message}`, 'error'); + }) + .getMediaDiagnostics(sku, ""); + }, + + saveChanges() { + ui.toggleSave(false); + ui.saveBtn.innerText = "Saving..."; + + ui.saveBtn.innerText = "Saving..."; + + // Filter out deleted items so they are actually removed + const activeItems = state.items.filter(i => !i._deleted); + + // Send final state array to backend + google.script.run + .withSuccessHandler((logs) => { + ui.saveBtn.innerText = "Saved!"; + + // Verify logs is an array (backward compatibility check) + if (Array.isArray(logs)) { + document.getElementById('status-log-container').innerHTML = ''; + logs.forEach(l => ui.logStatus('save', l, 'info')); + ui.toggleLog(true); // Force show + } else { + // Fallback for old backend + alert("Changes Saved & Synced!"); + } + + // Reload to get fresh IDs/State, preserving the save logs + setTimeout(() => this.loadMedia(true), 1500); + }) + .withFailureHandler(e => { alert(`Save Failed: ${e.message}`); ui.toggleSave(true); }) - .saveMediaChanges(state.sku, activeItems); - }, + .saveMediaChanges(state.sku, activeItems); + }, - handleFiles(fileList) { - Array.from(fileList).forEach(file => { - const reader = new FileReader(); - reader.onload = (e) => { - const data = e.target.result.split(',')[1]; // Base64 + handleFiles(fileList) { + Array.from(fileList).forEach(file => { + const reader = new FileReader(); + reader.onload = (e) => { + const data = e.target.result.split(',')[1]; // Base64 - google.script.run - .withSuccessHandler(() => { - this.loadMedia(); - }) - .saveFileToDrive(state.sku, file.name, file.type, data); - }; - reader.readAsDataURL(file); - }); - }, + google.script.run + .withSuccessHandler(() => { + this.loadMedia(); + }) + .saveFileToDrive(state.sku, file.name, file.type, data); + }; + reader.readAsDataURL(file); + }); + }, - // --- Picker --- - openPicker() { - if (!pickerApiLoaded) return alert("API Loading..."); - google.script.run.withSuccessHandler(c => createPicker(c)).getPickerConfig(); - }, + // --- Picker --- + // --- Picker --- + openPicker() { + if (!pickerApiLoaded) return alert("API Loading..."); + google.script.run.withSuccessHandler(c => createPicker(c)).getPickerConfig(); + }, - importFromPicker(fileId, mime, name, url) { - google.script.run - .withSuccessHandler(() => this.loadMedia()) - .importFromPicker(state.sku, fileId, mime, name, url); - }, + importFromPicker(fileId, mime, name, url) { + google.script.run + .withSuccessHandler(() => this.loadMedia()) + .importFromPicker(state.sku, fileId, mime, name, url); + }, - // --- Photos --- - startPhotoSession() { - ui.updatePhotoStatus("Starting session..."); - google.script.run - .withSuccessHandler(session => { - ui.showPhotoSession(session.pickerUri); - this.pollPhotoSession(session.id); - }) - .createPhotoSession(); - }, + // --- Photos (Popup Flow) --- + startPhotoSession() { + ui.updatePhotoStatus("Starting session..."); + google.script.run + .withSuccessHandler(session => { + ui.showPhotoSession(session.pickerUri); + this.pollPhotoSession(session.id); + }) + .createPhotoSession(); + }, - pollPhotoSession(sessionId) { - let processing = false; - const check = () => { - if (processing) return; - google.script.run - .withSuccessHandler(res => { - if (res.status === 'complete') { - processing = true; - ui.updatePhotoStatus("Importing photos..."); - controller.processPhotoItems(res.mediaItems); - } else if (res.status === 'error') { - ui.updatePhotoStatus("Error: " + res.message); - } else { - setTimeout(check, 2000); - } - }) - .checkPhotoSession(sessionId); - }; - check(); - }, + pollPhotoSession(sessionId) { + let processing = false; + const check = () => { + if (processing) return; + google.script.run + .withSuccessHandler(res => { + if (res.status === 'complete') { + processing = true; + ui.updatePhotoStatus("Importing photos..."); + controller.processPhotoItems(res.mediaItems); + } else if (res.status === 'error') { + ui.updatePhotoStatus("Error: " + res.message); + } else { + setTimeout(check, 2000); + } + }) + .checkPhotoSession(sessionId); + }; + check(); + }, - processPhotoItems(items) { - let done = 0; - items.forEach(item => { - const url = (item.mediaFile && item.mediaFile.baseUrl) ? item.mediaFile.baseUrl : item.baseUrl; - google.script.run - .withSuccessHandler(() => { + processPhotoItems(items) { + let done = 0; + items.forEach(item => { + const url = (item.mediaFile && item.mediaFile.baseUrl) ? item.mediaFile.baseUrl : item.baseUrl; + google.script.run + .withSuccessHandler(() => { done++; if (done === items.length) { ui.updatePhotoStatus("Done!"); @@ -1161,233 +1183,239 @@ setTimeout(() => ui.closePhotoSession(), 2000); } }) - .importFromPicker(state.sku, null, item.mimeType, item.filename, url); - }); - }, + .importFromPicker(state.sku, null, item.mimeType, item.filename, url); + }); + }, - // --- Compatibility / Matching Logic --- - matches: [], - currentMatchIndex: 0, - hasRunMatching: false, + // --- Legacy Photos Session (Removed in favor of Embedded Picker) --- + // startPhotoSession() { ... } - checkMatches(items) { - // Filter candidates - var driveOnly = items.filter(function (i) { return i.status === 'drive_only'; }); - var shopifyOnly = items.filter(function (i) { return i.source === 'shopify_only'; }); // source check is safer for shopify items + // --- Compatibility / Matching Logic --- - var newMatches = []; + matches: [], + currentMatchIndex: 0, + hasRunMatching: false, - driveOnly.forEach(function (d) { - // Find match by filename - // Note: Backend might return "Orphaned Media" if extraction failed, ignore those. - if (!d.filename || d.filename === 'Orphaned Media') return; + checkMatches(items) { + // Filter candidates + var driveOnly = items.filter(function (i) { return i.status === 'drive_only'; }); + var shopifyOnly = items.filter(function (i) { return i.source === 'shopify_only'; }); // source check is safer for shopify items - var match = shopifyOnly.find(function (s) { - return s.filename === d.filename; // Exact match - }); + var newMatches = []; - if (match) { - newMatches.push({ drive: d, shopify: match }); - } - }); + driveOnly.forEach(function (d) { + // Find match by filename + // Note: Backend might return "Orphaned Media" if extraction failed, ignore those. + if (!d.filename || d.filename === 'Orphaned Media') return; - if (newMatches.length > 0) { - this.matches = newMatches; - this.currentMatchIndex = 0; - ui.logStatus('info', 'Found ' + newMatches.length + ' potential matches. Starting matching wizard...', 'info'); - this.startMatching(); - } else { - // No matches, show UI - this.showGallery(); - } - }, + var match = shopifyOnly.find(function (s) { + return s.filename === d.filename; // Exact match + }); - startMatching() { - document.getElementById('loading-ui').style.display = 'none'; - document.getElementById('main-ui').style.display = 'none'; - document.getElementById('matching-modal').style.display = 'flex'; - this.renderMatch(); - }, + if (match) { + newMatches.push({ drive: d, shopify: match }); + } + }); - renderMatch() { - var match = this.matches[this.currentMatchIndex]; + if (newMatches.length > 0) { + this.matches = newMatches; + this.currentMatchIndex = 0; + ui.logStatus('info', 'Found ' + newMatches.length + ' potential matches. Starting matching wizard...', 'info'); + this.startMatching(); + } else { + // No matches, show UI + this.showGallery(); + } + }, - // Reset Buttons - var btnConfirm = document.getElementById('btn-match-confirm'); - var btnSkip = document.getElementById('btn-match-skip'); - if (btnConfirm) { - btnConfirm.disabled = false; - btnConfirm.innerText = "Yes, Link"; - } - if (btnSkip) { - btnSkip.disabled = false; - btnSkip.innerText = "No, Skip"; - } + startMatching() { + document.getElementById('loading-ui').style.display = 'none'; + document.getElementById('main-ui').style.display = 'none'; + document.getElementById('matching-modal').style.display = 'flex'; + this.renderMatch(); + }, - var dImg = document.getElementById('match-drive-img'); - var sImg = document.getElementById('match-shopify-img'); + renderMatch() { + var match = this.matches[this.currentMatchIndex]; - // Reset visual state safely - dImg.style.transition = 'none'; - dImg.style.opacity = '0'; - sImg.style.transition = 'none'; - sImg.style.opacity = '0'; + // Reset Buttons + var btnConfirm = document.getElementById('btn-match-confirm'); + var btnSkip = document.getElementById('btn-match-skip'); + if (btnConfirm) { + btnConfirm.disabled = false; + btnConfirm.innerText = "Yes, Link"; + } + if (btnSkip) { + btnSkip.disabled = false; + btnSkip.innerText = "No, Skip"; + } - // Clear source to blank pixel to ensure old image is gone - var blank = "data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs="; - dImg.src = blank; - sImg.src = blank; + var dImg = document.getElementById('match-drive-img'); + var sImg = document.getElementById('match-shopify-img'); - document.getElementById('match-drive-name').innerText = match.drive.filename; - document.getElementById('match-shopify-name').innerText = match.shopify.filename; + // Reset visual state safely + dImg.style.transition = 'none'; + dImg.style.opacity = '0'; + sImg.style.transition = 'none'; + sImg.style.opacity = '0'; - document.getElementById('match-index').innerText = this.currentMatchIndex + 1; - document.getElementById('match-total').innerText = this.matches.length; + // Clear source to blank pixel to ensure old image is gone + var blank = "data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs="; + dImg.src = blank; + sImg.src = blank; - // Load new images - setTimeout(function () { - dImg.style.transition = 'opacity 0.3s ease'; - sImg.style.transition = 'opacity 0.3s ease'; + document.getElementById('match-drive-name').innerText = match.drive.filename; + document.getElementById('match-shopify-name').innerText = match.shopify.filename; - dImg.onload = function () { dImg.style.opacity = '1'; }; - sImg.onload = function () { sImg.style.opacity = '1'; }; + document.getElementById('match-index').innerText = this.currentMatchIndex + 1; + document.getElementById('match-total').innerText = this.matches.length; - dImg.src = match.drive.thumbnail; - sImg.src = match.shopify.thumbnail; - }, 50); - }, + // Load new images + setTimeout(function () { + dImg.style.transition = 'opacity 0.3s ease'; + sImg.style.transition = 'opacity 0.3s ease'; - confirmLink() { - var match = this.matches[this.currentMatchIndex]; - var _this = this; + dImg.onload = function () { dImg.style.opacity = '1'; }; + sImg.onload = function () { sImg.style.opacity = '1'; }; - document.getElementById('btn-match-confirm').disabled = true; - document.getElementById('btn-match-confirm').innerText = "Linking..."; - document.getElementById('btn-match-skip').disabled = true; + dImg.src = match.drive.thumbnail; + sImg.src = match.shopify.thumbnail; + }, 50); + }, - // ui.logStatus('link', 'Linking ' + match.drive.filename + '...', 'info'); + confirmLink() { + var match = this.matches[this.currentMatchIndex]; + var _this = this; - google.script.run - .withSuccessHandler(function () { - // ui.logStatus('link', 'Linked ' + match.drive.filename, 'success'); - _this.nextMatch(); - }) - .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; - }) - .linkDriveFileToShopifyMedia(state.sku, match.drive.id, match.shopify.id); - }, + document.getElementById('btn-match-confirm').disabled = true; + document.getElementById('btn-match-confirm').innerText = "Linking..."; + document.getElementById('btn-match-skip').disabled = true; - 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); - }, + // ui.logStatus('link', 'Linking ' + match.drive.filename + '...', 'info'); - nextMatch() { - this.currentMatchIndex++; - if (this.currentMatchIndex < this.matches.length) { - this.renderMatch(); - } else { - // 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); - } - }, + google.script.run + .withSuccessHandler(function () { + // ui.logStatus('link', 'Linked ' + match.drive.filename, 'success'); + _this.nextMatch(); + }) + .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; + }) + .linkDriveFileToShopifyMedia(state.sku, match.drive.id, match.shopify.id); + }, - showGallery() { - document.getElementById('loading-ui').style.display = 'none'; - document.getElementById('main-ui').style.display = 'block'; - ui.logStatus('done', 'Finished loading.', 'success'); - setTimeout(function () { ui.toggleLog(false); }, 1000); - } - }; + 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); + }, - // --- Google Picker API --- - let pickerApiLoaded = false; - window.onApiLoad = function () { gapi.load('picker', () => { pickerApiLoaded = true; }); }; + nextMatch() { + this.currentMatchIndex++; + if (this.currentMatchIndex < this.matches.length) { + this.renderMatch(); + } else { + // 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); + } + }, - function createPicker(config) { - const view = new google.picker.DocsView(google.picker.ViewId.DOCS) - .setMimeTypes("image/png,image/jpeg,image/jpg,video/mp4") - .setIncludeFolders(true) - .setSelectFolderEnabled(false); - const photosView = new google.picker.PhotosView(); + showGallery() { + document.getElementById('loading-ui').style.display = 'none'; + document.getElementById('main-ui').style.display = 'block'; + ui.logStatus('done', 'Finished loading.', 'success'); + setTimeout(function () { ui.toggleLog(false); }, 1000); + } + }; - new google.picker.PickerBuilder() - .addView(view) - .addView(photosView) - .setOAuthToken(config.token) - .setDeveloperKey(config.apiKey) - .setCallback(data => { - if (data.action == google.picker.Action.PICKED) { - const doc = data.docs[0]; - const url = (doc.thumbnails && doc.thumbnails.length > 0) ? doc.thumbnails[doc.thumbnails.length - 1].url : null; - controller.importFromPicker(doc.id, doc.mimeType, doc.name, url); - } - }) - .build() - .setVisible(true); + // --- Google Picker API --- + let pickerApiLoaded = false; + window.onApiLoad = function () { gapi.load('picker', () => { pickerApiLoaded = true; }); }; + + function createPicker(config) { + const view = new google.picker.DocsView(google.picker.ViewId.DOCS) + .setMimeTypes("image/png,image/jpeg,image/jpg,video/mp4") + .setIncludeFolders(true) + .setSelectFolderEnabled(false); + + const builder = new google.picker.PickerBuilder(); + + builder.addView(view) + .setOAuthToken(config.token) + .setDeveloperKey(config.apiKey) + .setOrigin(window.location.protocol + '//' + window.location.host) + .setCallback(data => { + if (data.action == google.picker.Action.PICKED) { + const doc = data.docs[0]; + const isDrive = doc.serviceId === 'docs'; + + // Drive File (Always, since we removed Photos view) + controller.importFromPicker(doc.id, doc.mimeType, doc.name, null); + } + }) + .build() + .setVisible(true); } - // Init - try { - if (!window.state || !window.ui || !window.controller) { - throw new Error("Core components failed to initialize. Check console for SyntaxError."); - } - controller.init(); - window.controller = controller; // Re-assert global access - } catch (e) { - alert("Init Failed: " + e.message); - console.error(e); - } + // Init + try { + if (!window.state || !window.ui || !window.controller) { + throw new Error("Core components failed to initialize. Check console for SyntaxError."); + } + controller.init(); + window.controller = controller; // Re-assert global access + } catch (e) { + alert("Init Failed: " + e.message); + console.error(e); + } - // Drag & Drop Handlers (Global) - const dropOverlay = document.getElementById('drop-overlay'); - let dragCounter = 0; + // Drag & Drop Handlers (Global) + const dropOverlay = document.getElementById('drop-overlay'); + let dragCounter = 0; - // Check if the drag involves files - function isFileDrag(e) { - return e.dataTransfer.types && Array.from(e.dataTransfer.types).includes('Files'); - } + // Check if the drag involves files + function isFileDrag(e) { + return e.dataTransfer.types && Array.from(e.dataTransfer.types).includes('Files'); + } - document.addEventListener('dragenter', (e) => { - if (!isFileDrag(e)) return; - e.preventDefault(); - dragCounter++; - dropOverlay.style.display = 'flex'; - }); + document.addEventListener('dragenter', (e) => { + if (!isFileDrag(e)) return; + e.preventDefault(); + dragCounter++; + dropOverlay.style.display = 'flex'; + }); - document.addEventListener('dragleave', (e) => { - if (!isFileDrag(e)) return; - e.preventDefault(); - dragCounter--; - if (dragCounter === 0) { - dropOverlay.style.display = 'none'; - } - }); + document.addEventListener('dragleave', (e) => { + if (!isFileDrag(e)) return; + e.preventDefault(); + dragCounter--; + if (dragCounter === 0) { + dropOverlay.style.display = 'none'; + } + }); - document.addEventListener('dragover', (e) => { - if (!isFileDrag(e)) return; - e.preventDefault(); - }); + document.addEventListener('dragover', (e) => { + if (!isFileDrag(e)) return; + e.preventDefault(); + }); - document.addEventListener('drop', (e) => { - if (!isFileDrag(e)) return; - e.preventDefault(); - dragCounter = 0; - dropOverlay.style.display = 'none'; - if (e.dataTransfer && e.dataTransfer.files.length > 0) { - controller.handleFiles(e.dataTransfer.files); - } - }); + document.addEventListener('drop', (e) => { + if (!isFileDrag(e)) return; + e.preventDefault(); + dragCounter = 0; + dropOverlay.style.display = 'none'; + if (e.dataTransfer && e.dataTransfer.files.length > 0) { + controller.handleFiles(e.dataTransfer.files); + } + }); diff --git a/src/appsscript.json b/src/appsscript.json index da78956..fb2f929 100644 --- a/src/appsscript.json +++ b/src/appsscript.json @@ -18,6 +18,7 @@ "https://www.googleapis.com/auth/script.scriptapp", "https://www.googleapis.com/auth/drive", "https://www.googleapis.com/auth/userinfo.email", - "https://www.googleapis.com/auth/photospicker.mediaitems.readonly" + "https://www.googleapis.com/auth/photospicker.mediaitems.readonly", + "https://www.googleapis.com/auth/drive.photos.readonly" ] }