feat(media): Optimize Media Manager loading performance
Significant performance improvements to the 'Loading media...' phase: - Reduced client-server round trips by consolidating the initial handshake (diagnostics + media fetch) into a single backend call: getMediaManagerInitialState. - Implemented batched Google Drive metadata retrieval in GASDriveService using the Advanced Drive API, eliminating per-file property fetching calls. - Switched to HtmlService templates in showMediaManager to pass initial SKU/Title data directly, enabling the UI shell to appear instantly upon opening. - Updated documentation (ARCHITECTURE.md, MEMORY.md) to clarify Webpack global assignment requirements for GAS functions. - Verified with comprehensive updates to unit and integration tests.
This commit is contained in:
@ -99,4 +99,55 @@ export class GASDriveService implements IDriveService {
|
||||
return {}
|
||||
}
|
||||
}
|
||||
|
||||
getFilesWithProperties(folderId: string): { file: GoogleAppsScript.Drive.File, properties: { [key: string]: string } }[] {
|
||||
if (typeof Drive === 'undefined') {
|
||||
return this.getFiles(folderId).map(f => ({ file: f, properties: {} }))
|
||||
}
|
||||
|
||||
try {
|
||||
const drive = Drive as any
|
||||
const isV3 = !!drive.Files.create
|
||||
const query = `'${folderId}' in parents and trashed = false`
|
||||
const fields = isV3 ? 'nextPageToken, files(id, name, mimeType, appProperties)' : 'nextPageToken, items(id, title, mimeType, properties)'
|
||||
|
||||
const results: { file: GoogleAppsScript.Drive.File, properties: { [key: string]: string } }[] = []
|
||||
let pageToken: string | null = null
|
||||
|
||||
do {
|
||||
const response = drive.Files.list({ q: query, fields: fields, pageToken: pageToken, supportsAllDrives: true, includeItemsFromAllDrives: true })
|
||||
|
||||
const items = isV3 ? response.files : response.items
|
||||
|
||||
if (items) {
|
||||
items.forEach((item: any) => {
|
||||
const file = DriveApp.getFileById(item.id)
|
||||
const props: { [key: string]: string } = {}
|
||||
|
||||
if (isV3) {
|
||||
if (item.appProperties) {
|
||||
Object.assign(props, item.appProperties)
|
||||
}
|
||||
} else {
|
||||
if (item.properties) {
|
||||
item.properties.forEach((p: any) => {
|
||||
if (p.visibility === 'PRIVATE') {
|
||||
props[p.key] = p.value
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
results.push({ file: file, properties: props })
|
||||
})
|
||||
}
|
||||
pageToken = response.nextPageToken
|
||||
} while (pageToken)
|
||||
|
||||
return results
|
||||
} catch (e) {
|
||||
console.error(`Failed to get files with properties for folder ${folderId}`, e)
|
||||
return this.getFiles(folderId).map(f => ({ file: f, properties: {} }))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -100,7 +100,7 @@ export class MediaService {
|
||||
// We need strict file list.
|
||||
// Optimization: getFiles() usually returns limited info.
|
||||
// We might need to iterate and pull props if getFiles() doesn't include appProperties (DriveApp doesn't).
|
||||
const driveFiles = this.driveService.getFiles(folder.getId())
|
||||
const driveFiles = this.driveService.getFilesWithProperties(folder.getId())
|
||||
|
||||
// 2. Get Shopify Media
|
||||
let shopifyMedia: any[] = []
|
||||
@ -118,26 +118,17 @@ export class MediaService {
|
||||
const sidecarFileIds = new Set<string>();
|
||||
|
||||
// Map of Drive Files (Enriched)
|
||||
const driveFileStats = driveFiles.map(f => {
|
||||
let shopifyId = null
|
||||
let galleryOrder = 9999
|
||||
let type = 'media';
|
||||
let customThumbnailId = null;
|
||||
let parentVideoId = null;
|
||||
const driveFileStats = driveFiles.map(d => {
|
||||
const f = d.file
|
||||
const props = d.properties
|
||||
let shopifyId = props['shopify_media_id'] || null
|
||||
let galleryOrder = props['gallery_order'] ? parseInt(props['gallery_order']) : 9999
|
||||
let type = props['type'] || 'media';
|
||||
let customThumbnailId = props['custom_thumbnail_id'] || null;
|
||||
let parentVideoId = props['parent_video_id'] || null;
|
||||
|
||||
try {
|
||||
const props = this.driveService.getFileProperties(f.getId())
|
||||
if (props['shopify_media_id']) shopifyId = props['shopify_media_id']
|
||||
if (props['gallery_order']) galleryOrder = parseInt(props['gallery_order'])
|
||||
if (props['type']) type = props['type'];
|
||||
if (props['custom_thumbnail_id']) customThumbnailId = props['custom_thumbnail_id'];
|
||||
if (props['parent_video_id']) parentVideoId = props['parent_video_id'];
|
||||
console.log(`[DEBUG] File ${f.getName()} Props:`, JSON.stringify(props));
|
||||
|
||||
console.log(`[DEBUG] File ${f.getName()} Props:`, JSON.stringify(props));
|
||||
|
||||
} catch (e) {
|
||||
console.warn(`Failed to get properties for ${f.getName()}`)
|
||||
}
|
||||
return { file: f, shopifyId, galleryOrder, type, customThumbnailId, parentVideoId }
|
||||
})
|
||||
|
||||
@ -610,5 +601,20 @@ export class MediaService {
|
||||
|
||||
return logs
|
||||
}
|
||||
getInitialState(sku: string, shopifyProductId: string): { diagnostics: any, media: any[] } {
|
||||
// 1. Diagnostics (Reusing the existing method logic but avoiding redundant setup)
|
||||
const diagnostics = this.getDiagnostics(sku, shopifyProductId);
|
||||
|
||||
// 2. Unified Media State
|
||||
// If diagnostics succeeded in finding the folder, we should probably pass that info
|
||||
// to getUnifiedMediaState to avoid re-fetching the folder, but for now
|
||||
// let's just call the method to keep it clean.
|
||||
const media = this.getUnifiedMediaState(sku, shopifyProductId);
|
||||
|
||||
return {
|
||||
diagnostics,
|
||||
media
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -127,4 +127,12 @@ export class MockDriveService implements IDriveService {
|
||||
return {}
|
||||
}
|
||||
}
|
||||
|
||||
getFilesWithProperties(folderId: string): { file: GoogleAppsScript.Drive.File, properties: { [key: string]: string } }[] {
|
||||
const files = this.getFiles(folderId)
|
||||
return files.map(f => ({
|
||||
file: f,
|
||||
properties: (f as any)._properties || {}
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user