Jake Vanderwerf
5 days ago a9b3b28d001941921aa70d37fdc87c758a163a44
src/feed/view.js
@@ -7,9 +7,10 @@
      this.error = window.jvbError;
      this.cache = new window.jvbCache('feed');
      this.templates = window.jvbTemplates;
      this.isFirstLoad = true;
      this.config = {
         source: '',
         contextId: '',
         context: '',
         highlight: null,
         gallery: false,
@@ -17,35 +18,23 @@
         ... this.container.dataset
      };
      this.init();
   }
   init() {
      this.initElements();
      this.defineTemplates();
      this.initListeners();
      this.initFilters();
      if ('requestIdleCallback' in window) {
         requestIdleCallback(() => {
            this.initStore();
            this.initTaxonomies();
      this.initStore();
      this.initTaxonomies().then(r => {});
            this.processCachedFilters();
            this.processURLFilters();
            this.updateFilterUI();
            this.initGallery();
         }, { timeout: 2000 });
      } else {
         setTimeout(() => {
            this.initStore();
            this.initTaxonomies();
            this.processCachedFilters();
            this.processURLFilters();
            this.updateFilterUI();
            this.initGallery();
         }, 100);
      }
      this.processCachedFilters();
      this.processURLFilters();
      this.updateFilterUI();
      this.initGallery();
   }
   initElements() {
@@ -53,10 +42,13 @@
         filterTrigger: '[data-filter]',
         filters: {
            actions:    '.filter-actions .toggle-text',
            container:  '.filters',
            container:  '.all-filters',
            showing: '.all-filters summary .current',
            content:    '[data-filter="content"]',
            ordering:   '.ordering',
            orderby:    '[data-filter="orderby"]',
            order:      '[data-filter="order"]',
            orderWrap:  '.order-direction',
            match:      '[data-filter="match"]',
            favourites: '[data-filter="favourites"]',
            taxonomy:   '[data-filter^="taxonomy"]',
@@ -66,31 +58,49 @@
         buttons: {
            loadMore:      'button.load-more',
            remove:        '.remove-term',
            clearFilters:  'button.clear-filters'
            clearFilters:  'button.clear-filters',
            refresh:    'button[data-action="refresh"]'
         }
      };
      this.ui = window.uiFromSelectors(this.selectors, this.container);
      this.ui.buttons.refresh = document.querySelector(this.selectors.buttons.refresh);
      //Add content and taxonomies
      this.ui.content = this.ui.filters.container.querySelectorAll('[name="content"]');
      if (this.ui.content.length === 0) this.ui.content = false;
      this.ui.taxonomies = this.ui.filters.container.querySelectorAll('[data-taxonomy]');
      if (this.ui.taxonomies.length === 0) this.ui.content = false;
      this.ui.orderbyWrap = this.ui.filters.container.querySelector('[data-for-order]');
      if (this.ui.orderbyWrap.length === 0) this.ui.content = false;
      this.ui.order = this.ui.filters.container.querySelectorAll('[data-filter="order"]');
      if (this.ui.order.length === 0) this.ui.content = false;
      this.ui.orderby = this.ui.filters.container.querySelectorAll('[data-filter="orderby"]');
      if (this.ui.orderby.length === 0) this.ui.content = false;
      let getAll = ['content','orderby','order','taxonomy'];
      getAll.forEach(item => {
         let items = this.ui.filters.container.querySelectorAll(this.selectors.filters[item]);
         this.ui[item] = Array.from(items);
      });
      this.contentTypes = (this.ui.content)
         ? Array.from(this.ui.content).map(c => c.value)
      this.contentTypes = (this.ui.content.length > 0)
         ? this.ui.content.map(c => c.value)
         : [this.container.dataset.content];
      this.taxonomies = (this.ui.taxonomies?.length > 0)
         ? Array.from(this.ui.taxonomies).map(t => t.dataset.taxonomy)
         : [];
   }
   /**
    *
    * @param {string} item
    */
   getChecked(item) {
      if (!['content', 'orderby','order'].includes(item)) {
         console.log('Invalid item to check: ', item);
      }
      let items = this.ui[item];
      if (!items) {
         return;
      }
      let checked = items.filter(i => i.checked);
      if (item === 'content' && checked.length > 0) {
         this.updateContentFor(checked[0].value);
      }
      return checked.length === 0 ? items[0].value : checked[0].value;
   }
   initListeners() {
      this.popStateHandler = this.handlePopState.bind(this);
      this.clickHandler    = this.handleClick.bind(this);
@@ -104,15 +114,16 @@
   initFilters() {
      this.allowedFilters = ['content', 'order', 'orderby', 'favourites', 'match'];
      let defaults = {
         content: this.contentTypes[0],
         orderby: 'date',
         order:      'desc',
         content: this.getChecked('content'),
         orderby: this.getChecked('orderby'),
         order:      this.getChecked('order'),
         page:    1,
      };
      if (this.config.context) defaults.context = this.config.context;
      if (this.config.source) defaults.source = this.config.source;
      if (this.config.contextId) defaults.contextId = this.config.contextId;
      this.filters = defaults;
      this.defaults = {...defaults};
   }
   updateFilterUI() {
@@ -171,6 +182,18 @@
      if (remove) {
         this.removeSelectedTerm(remove);
      }
      let refresh = window.targetCheck(e, this.selectors.buttons.refresh);
      if (refresh) {
         this.store.clearCache();
         this.store.fetch();
      }
      let orderbyButton = window.targetCheck(e, '[data-filter="orderby"]');
      if (orderbyButton && orderbyButton.value === 'random' && orderbyButton.checked) {
         // Already selected random, just re-render to trigger new shuffle
         this.renderItems();
      }
   }
   nextPage() {
@@ -229,7 +252,7 @@
   }
   getFieldId(taxonomy) {
      return this.selector.getFieldId(Array.from(this.ui.taxonomies).filter(tax => tax.dataset.taxonomy === taxonomy)[0]??null);
      return this.selector.getFieldId(this.ui.taxonomies.filter(tax => tax.dataset.taxonomy === taxonomy)[0]??null);
   }
   removeSelectedTerm(button) {
      const termId = parseInt(button.dataset.id);
@@ -264,6 +287,8 @@
         this.ui.taxonomies,
         this.ui.orderby
      ];
      this.ui.filters.showing.textContent = this.ui.content.filter(c => c.value === content)[0].dataset.label;
      checkIt.forEach(check => {
         if (!check) return;
         check.forEach(button => {
@@ -276,19 +301,19 @@
      });
   }
   updateOrderOptions(order) {
      if (this.ui.orderbyWrap) {
         let options = this.ui.orderbyWrap.dataset.forOrder.split(',')??[];
         this.ui.orderbyWrap.hidden = !options.includes(order);
      if (this.ui.filters.orderWrap) {
         let options = this.ui.filters.orderWrap.dataset.forOrder.split(',')??[];
         this.ui.filters.orderWrap.hidden = !options.includes(order);
      }
   }
   updateFilterControls() {
      const isHidden = Object.keys(this.taxFilters).length === 0;
      const keys = Object.keys(this.taxFilters);
      if (this.ui.buttons.clearFilters) {
         this.ui.buttons.clearFilters.hidden = isHidden;
         this.ui.buttons.clearFilters.hidden = keys.length === 0;
      }
      if (this.ui.filters.actions) {
         this.ui.filters.actions.hidden = isHidden;
         this.ui.filters.actions.hidden = keys.length <= 1;
      }
   }
@@ -320,7 +345,7 @@
      this.updateFilterControls();
   }
   getTaxonomyIcon(taxonomy) {
      let iconButton = Array.from(this.ui.taxonomies)
      let iconButton = this.ui.taxonomies
         .find(t => t.dataset.taxonomy === taxonomy);
      return iconButton?.dataset.icon.trim() || 'tag';
   }
@@ -335,7 +360,7 @@
   processCachedFilters() {
      Object.keys(this.filters).forEach(filter => {
         let cached = this.cache.get(`${this.config.source}_${this.config.context}_${filter}`);
         let cached = this.cache.get(`${this.config.contextId}_${this.config.context}_${filter}`);
         if (cached && cached !== this.filters[filter]) {
            this.filters[filter] = cached;
         }
@@ -397,7 +422,7 @@
   saveToCacheFilters() {
      Object.keys(this.store.filters).forEach(filter => {
         const cacheKey = `${this.config.source}_${this.config.context}_${filter}`;
         const cacheKey = `${this.config.contextId}_${this.config.context}_${filter}`;
         if (this.store.filters[filter] !== this.defaults[filter]) {
            this.cache.set(cacheKey, this.store.filters[filter]);
@@ -406,7 +431,7 @@
         }
      });
      const taxCacheKey = `${this.config.source}_${this.config.context}_taxonomy`;
      const taxCacheKey = `${this.config.contextId}_${this.config.context}_taxonomy`;
      if (Object.keys(this.taxFilters).length > 0) {
         this.cache.set(taxCacheKey, this.taxFilters);
      } else {
@@ -425,6 +450,12 @@
      }
   }
   initStore() {
      let extraOrderby = this.ui.orderby.filter(v => !['date','date_modified','title','random'].includes(v.value));
      let extraIndexes = [];
      extraOrderby.forEach(orderby =>{
         extraIndexes.push({name:orderby.value, keyPath: orderby.value});
      });
      const store = window.jvbStore.register(
         'feed',
         {
@@ -435,21 +466,33 @@
               { name: 'content', keyPath: 'content'},
               { name: 'taxonomy', keyPath: 'taxonomy'},
               { name: 'user', keyPath: 'user'},
               { name: 'date', keyPath: 'modified'},
               { name: 'title', keyPath: 'title'}
               { name: 'date', keyPath: 'date'},
               { name: 'modified', keyPath: 'modified'},
               { name: 'title', keyPath: 'title'},
               ... extraIndexes
            ],
            filters: this.filters,
            TTL: 6 * 60 * 60 * 1000, //6 hours
            showLoading: true,
            required: 'content',
         }
         },
         2
      );
      this.store = store.feed;
      this.store.subscribe((event, data) => {
         switch (event) {
            case 'data-loaded':
               this.renderItems();
               if (this.isFirstLoad) {
                  //We rendered the first page in php already
                  this.isFirstLoad = false;
                  return;
               }
               // if (this.store.filters.page === 1) {
               //    return;
               // }
               this.renderItems(data.items);
               this.ui.buttons.loadMore.hidden = true;
               if (this.store.lastResponse && this.store.lastResponse?.has_more) {
                  this.ui.buttons.loadMore.hidden = !this.store.lastResponse?.has_more??true;
@@ -463,8 +506,8 @@
      return this.store.filters.page === 1;
   }
   renderItems() {
      let items = this.store.getFiltered();
   renderItems(items = null) {
      items = items??this.store.getFiltered();
      if (this.isFirstPage()) {
         window.removeChildren(this.ui.grid);
      }
@@ -495,6 +538,10 @@
   }
   createItemElement(item) {
      if (typeof item !== 'object') {
         item = this.store.get(item);
         if (!item) return;
      }
      return this.templates.create(`feedItem${window.uppercaseFirst(item.content)}`, item);
   }
   splitIDs(value) {
@@ -566,14 +613,16 @@
         let link = termItem.querySelector('a');
         if (!link) continue;
         let title = window.decodeHTMLEntities(term.title);
         [
            link.href,
            link.title,
            link.textContent
         ] = [
            term.url,
            `See more ${term.title}`,
            term.title
            `See more ${title}`,
            title
         ];
         element.append(termItem);
      }
@@ -591,7 +640,7 @@
      element.textContent = window.formatTimeAgo(value, 'F Y');
   }
   formatField(element, value) {
      element.textContent = value;
      element.textContent = window.decodeHTMLEntities(value);
   }
   addTimelineElements(item, template) {
@@ -608,10 +657,10 @@
      ];
      if (afterEl) {
         afterEl.textContent = `After ${item.fields.number} Tx`;
         afterEl.textContent = `After ${item.number} Tx`;
      }
      if (number) {
         number.textContent = item.fields.number;
         number.textContent = item.number;
      }
      if (started) {
         this.formatTimeField(started, item.fields.timeline[0]['post_date']);
@@ -640,8 +689,8 @@
         setup({el, refs, manyRefs, data}) {
            el.dataset.id = data.id;
            el.dataset.taxonomy = data.taxonomy;
            if (refs.icon) refs.icon.className = `icon icon=${data.icon}`;
            if (refs.span) refs.span.textContent = data.name;
            if (refs.icon) refs.icon.className = `icon icon-${data.icon}`;
            if (refs.span) refs.span.textContent = window.decodeHTMLEntities(data.name);
         }
      });
      T.define('emptyState');
@@ -659,7 +708,6 @@
               if (manyRefs.fields) {
                  for (let field of manyRefs.fields) {
                     if (isTimeline && ['timeline','number'].includes(field.dataset.field)) continue;
                     const value = Object.hasOwn(data.fields, field.dataset.field)? data.fields[field.dataset.field] : false;
                     if (!value) {
                        field.remove();