@@ -532,7 +534,7 @@
-
☁️
+
☁️
Drop files to Upload
@@ -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"
]
}