Jake Vanderwerf
2025-12-23 25be5747a6e462a3d09fc6607b3639b79e4d9374
assets/js/concise/UploadManager.js
@@ -112,7 +112,7 @@
   }
   async init() {
      this.initializeFields();
      // this.initializeFields();
      this.initListeners();
      // Queue integration - handle completion/failure
@@ -165,21 +165,18 @@
   /*******************************************************************************
    * FIELD MANAGEMENT
    *******************************************************************************/
   initializeFields() {
      const fields = document.querySelectorAll(this.selectors.field.field);
      fields.forEach(uploader => this.registerUploader(uploader));
   }
   scanFields(container) {
   scanFields(container, autoUpload) {
      console.log(autoUpload, 'autoUpload');
      const fields = container.querySelectorAll(this.selectors.field.field);
      fields.forEach(uploader => this.registerUploader(uploader));
      fields.forEach(uploader => this.registerUploader(uploader, autoUpload));
   }
   registerUploader(uploader) {
   registerUploader(uploader, autoUpload) {
      const fieldId = this.determineFieldId(uploader);
      const config = this.extractFieldConfig(uploader);
      const config = this.extractFieldConfig(uploader, autoUpload);
      const ui = this.buildFieldUI(uploader);
      console.log(config, 'registering with config');
      // Store field data with Sets for runtime
      const fieldData = {
         id: fieldId,
@@ -206,8 +203,9 @@
      return fieldId;
   }
   extractFieldConfig(fieldElement) {
   extractFieldConfig(fieldElement, autoUpload) {
      return {
         autoUpload: autoUpload,
         destination: fieldElement.dataset.destination || 'meta',
         content: fieldElement.dataset.content || null,
         mode: fieldElement.dataset.mode || 'direct',
@@ -493,14 +491,11 @@
      const fieldWrapper = grid.closest('.field, .upload');
      if (!fieldWrapper) return;
      const movedItems = evt.items && evt.items.length > 0 ? evt.items : [evt.item];
      // Get current order from DOM
      let items = Array.from(grid.querySelectorAll('.item:not(.sortable-ghost):not(.sortable-clone)'))
         .map(upload => upload.dataset.uploadId)
         .filter(id => id);
      console.log('Reordered items:', items);
      // Update hidden input (for form submission)
      let hiddenInput = fieldWrapper.querySelector('input[type="hidden"]');
@@ -508,7 +503,7 @@
         hiddenInput.value = items.join(',');
      }
      // ✅ Update fieldState with new order
      // Update fieldState with new order
      const fieldId = this.getFieldIdFromElement(grid);
      if (fieldId) {
         const fieldData = this.getFieldData(fieldId);
@@ -524,7 +519,7 @@
         // If reordering in preview, the order is implicit by DOM position
         // (we don't store preview order separately)
         this.schedulePersistance(fieldId); // ✅ Persist changes
         this.schedulePersistance(fieldId);
      }
      this.a11y.announce('Item reordered');
@@ -641,6 +636,9 @@
      // Meta field changes
      if (fieldId) {
         const fieldData = this.getFieldData(fieldId);
         if (!fieldData.config.autoUpload) {
            return;
         }
         if (fieldData?.config.destination === 'post_group') {
            this.handleGroupMetaChange(e.target);
         } else {
@@ -761,7 +759,7 @@
      this.refreshSortable(fieldId);
      // Queue for upload if in direct mode
      if (fieldData.config.destination !== 'post_group') {
      if (fieldData.config.autoUpload && fieldData.config.destination !== 'post_group') {
         await this.queueUpload(fieldId);
         this.maybeLockUploads(fieldId);
      }
@@ -1192,7 +1190,7 @@
         popup: `Creating ${posts.length} post${posts.length > 1 ? 's' : ''}...`,
         canMerge: false,
         headers: {
            'action_nonce': jvbSettings.dash
            'action_nonce': window.auth.getNonce('dash')
         },
         append: '_upload',
      };
@@ -1243,7 +1241,7 @@
         title: `Uploading ${uploads.length} file${uploads.length > 1 ? 's' : ''} to server...`,
         popup: `Uploading ${uploads.length} file${uploads.length > 1 ? 's' : ''}...`,
         canMerge: false,
         headers: { 'action_nonce': jvbSettings.dash },
         headers: { 'action_nonce': window.auth.getNonce('dash') },
         append: '_upload'
      };
@@ -1324,7 +1322,7 @@
         data: queueData,
         title: 'Updating meta',
         canMerge: true,
         headers: { 'action_nonce': jvbSettings.dash }
         headers: { 'action_nonce': window.auth.getNonce('dash') }
      };
      try {
@@ -1555,7 +1553,8 @@
      this.refreshSortable(fieldKey);
      // Queue for upload if needed
      if (config.mode === 'direct' && config.destination !== 'post_group') {
      console.log(config);
      if (config.autoUpload && config.mode === 'direct' && config.destination !== 'post_group') {
         await this.queueUpload(fieldKey);
      }
   }
@@ -1671,7 +1670,7 @@
               storedGroup.changes = { ...groupData.changes };
            }
            // ✅ Preserve upload order
            // Preserve upload order
            if (groupData.uploads) {
               storedGroup.uploads = [...groupData.uploads];
            }
@@ -2489,16 +2488,6 @@
      // Can add custom logic here if needed
   }
   getCurrentSelection(fieldId) {
      let selected = [];
      for (let [key, handler] of this.selectionHandlers) {
         if ((fieldId === key || key.includes(fieldId)) && handler.selectedItems.size > 0) {
            selected = selected.concat([...handler.selectedItems]);
         }
      }
      return selected;
   }
   /*******************************************************************************
    * HELPER METHODS
    *******************************************************************************/
@@ -2530,11 +2519,6 @@
    * Save field data to store, converting Sets to Arrays
    */
   async saveFieldData(fieldData) {
      console.log('💾 Saving:', fieldData.id, {
         uploads: fieldData.uploads?.size,
         groups: fieldData.groups?.length
      });
      await this.fieldStore.save({
         ...fieldData,
         timestamp: Date.now()
@@ -2638,27 +2622,6 @@
      return progress[status] || 0;
   }
   getModalType(fieldEl) {
      if (!fieldEl?.element) return null;
      if (fieldEl._cachedModalType !== undefined) {
         return fieldEl._cachedModalType;
      }
      const dialog = fieldEl.element.closest('dialog');
      if (!dialog) {
         fieldEl._cachedModalType = null;
         return null;
      }
      let modalType = null;
      if (dialog.classList.contains('edit')) modalType = 'edit';
      else if (dialog.classList.contains('create')) modalType = 'create';
      else if (dialog.classList.contains('bulkEdit')) modalType = 'bulkEdit';
      else modalType = dialog.className;
      fieldEl._cachedModalType = modalType;
      return modalType;
   }
   createUploadElement(upload, draggable = false) {
      let image = window.getTemplate('uploadItem');
@@ -2734,34 +2697,6 @@
    * PERSISTENCE
    *******************************************************************************/
   /**
    * Normalize field data loaded from IndexedDB
    * Converts Arrays back to Sets, handles missing properties
    */
   normalizeFieldData(fieldData) {
      if (!fieldData) return null;
      // Convert uploads array back to Set
      if (Array.isArray(fieldData.uploads)) {
         fieldData.uploads = new Set(fieldData.uploads);
      } else if (!fieldData.uploads) {
         fieldData.uploads = new Set();
      }
      // Convert groups array, ensure proper structure
      if (!Array.isArray(fieldData.groups)) {
         fieldData.groups = [];
      }
      // Ensure each group has uploads array
      fieldData.groups = fieldData.groups.map(group => ({
         ...group,
         uploads: Array.isArray(group.uploads) ? group.uploads : []
      }));
      return fieldData;
   }
   schedulePersistance(fieldId) {
      const key = `persist_${fieldId}`;
      window.debouncer.schedule(
@@ -2810,6 +2745,56 @@
   }
   /*******************************************************************************
    HELPER to GET UPLOADED FILES
   *******************************************************************************/
   /**
    * Get all files for a form (searches all upload fields within form)
    */
   async getFilesForForm(formElement) {
      const uploadFields = formElement.querySelectorAll('[data-upload-field]');
      const allFiles = [];
      for (const field of uploadFields) {
         const fieldId = this.determineFieldId(field);
         const files = await this.getFilesForField(fieldId);
         allFiles.push(...files);
      }
      return allFiles;
   }
   /**
    * Get all files for a specific field
    */
   async getFilesForField(fieldId) {
      const fieldData = this.getFieldData(fieldId);
      if (!fieldData?.uploads) return [];
      const files = [];
      const uploadsArray = fieldData.uploads instanceof Set
         ? Array.from(fieldData.uploads)
         : fieldData.uploads;
      for (const uploadId of uploadsArray) {
         const upload = this.uploadStore.get(uploadId);
         if (!upload) continue;
         // Get the actual File object from blob data
         const file = await this.getBlobData(uploadId);
         if (file) {
            files.push({
               file: file,
               uploadId: uploadId,
               fieldName: fieldData.config.name,
               meta: upload.meta || {}
            });
         }
      }
      return files;
   }
   /*******************************************************************************
    * RECOVERY & RESTORATION
    *******************************************************************************/
@@ -2844,12 +2829,6 @@
   async checkForStoredUploads() {
      const allFieldStates = this.fieldStore.getAll();
      console.log('Checking for stored uploads...', {
         fieldStates: allFieldStates.length,
         uploadStoreSize: this.uploadStore.data.size
      });
      console.log(this.uploadStore.getAll());
      console.log(this.fieldStore.getAll());
      const pendingFields = allFieldStates.filter(field => {
         if (!field.uploads) return false;
@@ -2866,10 +2845,9 @@
               ['completed', 'processed', 'local_processing', 'processed-original'].includes(upload.status);
         });
      });
      console.log('Found pending fields:', pendingFields.length);
      if (pendingFields.length === 0) return;
      this.showRecoveryNotification(pendingFields);
      await this.showRecoveryNotification(pendingFields);
   }
   async showRecoveryNotification(pendingFields) {
@@ -3154,6 +3132,10 @@
}
// Initialize when DOM is ready
document.addEventListener('DOMContentLoaded', () => {
   window.jvbUploads = new UploadManager();
document.addEventListener('DOMContentLoaded', async function () {
   window.auth.subscribe((event) => {
      if (event === 'auth-loaded') {
         window.jvbUploads = new UploadManager();
      }
   });
});