From 46d681c6b825d21b3f698d793c4e630c687d90ad Mon Sep 17 00:00:00 2001
From: Jake Vanderwerf <get@jakevanderwerf.ca>
Date: Thu, 21 May 2026 21:41:53 +0000
Subject: [PATCH] =Major CustomBlocks.php overhaul, expanding block support and customization from the editor. theme.json should now be updated on new themes to set brand colours, etc. Also note: major change to .col vs .row alignment: simplifying it to .top .bottom vs the confusion of the differences for .col/.row .start and .a-start

---
 inc/blocks/FeedBlock.php |  741 ++++++++++++++++++++++++++++++++++----------------------
 1 files changed, 452 insertions(+), 289 deletions(-)

diff --git a/inc/blocks/FeedBlock.php b/inc/blocks/FeedBlock.php
index 83f8b1d..c6b1f0b 100644
--- a/inc/blocks/FeedBlock.php
+++ b/inc/blocks/FeedBlock.php
@@ -1,343 +1,506 @@
 <?php
 namespace JVBase\blocks;
 
-use JVBase\managers\CacheManager;
+use JVBase\managers\Cache;
+use JVBase\registrar\Registrar;
+use JVBase\base\Site;
 use JVBase\forms\TaxonomySelector;
 use WP_Block;
 
 if (!defined('ABSPATH')) {
-    exit; // Exit if accessed directly
+	exit;
 }
 
 class FeedBlock
 {
-    protected CacheManager $cache;
-    protected array $config;
-    protected string $path = JVB_DIR.'/build/feed';
+	protected Cache $cache;
+	protected array $config;
+	protected string $path = JVB_DIR.'/build/feed';
 
-    public function __construct()
-    {
-        $this->cache = CacheManager::for('feed',WEEK_IN_SECONDS);
-        add_action('init', [$this, 'registerBlock']);
-    }
+	public function __construct()
+	{
+		// Initialize cache with connections
+		$this->cache = Cache::for('feed_block', WEEK_IN_SECONDS);
+//		if (JVB_TESTING) {
+//			$this->cache->flush();
+//		}
 
-    public function registerBlock()
-    {
-        register_block_type($this->path, [
-            'render_callback' => [$this, 'render']
-        ]);
-    }
+		add_action('init', [$this, 'registerBlock']);
+	}
 
-    protected function buildParams(array $attributes):array
-    {
+	public function registerBlock()
+	{
+		register_block_type($this->path, [
+			'render_callback' => [$this, 'render']
+		]);
+	}
 
-        if (!jvbCheck('inheritQuery', $attributes)) {
-            return [
-                'title'     => $attributes['title'],
-                'content'   => $attributes['contentTypes'],
-                'taxonomies'=> $this->getTaxonomies($attributes['contentTypes'])
-            ];
-        }
+	protected function buildParams(array $attributes): array
+	{
+		if (!jvbCheck('inheritQuery', $attributes)) {
+			return [
+				'title' => $attributes['title'],
+				'content' => $attributes['contentTypes'],
+				'taxonomies' => $this->getTaxonomies($attributes['contentTypes'])
+			];
+		}
+		$config = [
+			'is_gallery'    => false,
+			'content'       => '',
+			'taxonomies'    => []
+		];
+		$type = get_queried_object();
 
-        if (isJVBUserType()) {
-            return $this->buildProfileConfig();
-        } elseif (isJVBContentTax()) {
-            return $this->buildShopConfig();
-        } elseif (is_tax()) {
-            return $this->buildTaxonomyConfig();
-        }
+		if (is_post_type_archive() || is_singular()) {
+			$content = is_singular() ? jvbNoBase($type->post_type) : jvbNoBase($type->name);
 
-        return [];
-    }
+			$registrar = Registrar::getInstance($content)??false;
+			if ($registrar) {
+				$config = array_merge($config, $registrar->getConfig('feed'));
+			} else {
+				$config['content'] = $content;
+				$config['icon'] = jvbDefaultIcon();
+			}
+			if (is_singular()) {
+				$config['source'] = $type->ID;
+			}
 
-    protected function buildProfileConfig():array
-    {
-        $obj = get_queried_object();
-        $content = jvbGetUserContentTypes($obj->ID);
-        return [
-            'is_gallery'    => true,
-            'content'       => $content,
-            'context'       => jvbNoBase($obj->post_type),
-            'taxonomies'    => $this->getTaxonomies($content)
-        ];
-    }
+			$config['taxonomies'] = $this->getTaxonomies([$content]);
+		} elseif (is_tax()) {
+			$content = jvbNoBase($type->taxonomy);
+			$registrar = Registrar::getInstance($content)??false;
+			if ($registrar) {
+				$config['content'] = $registrar->registrar->for;
+				$config['context'] = $content;
+				$config['taxonomies'] = $this->getTaxonomies($registrar->registrar->for);
+				if (!empty($registrar->getConfig('feed'))){
+					$config = array_merge($config, $registrar->getConfig('feed'));
+				}
+			}
+			$config['source'] = $type->term_id;
+		}
 
-    protected function buildShopConfig():array
-    {
-        $type = jvbNoBase(get_queried_object()->taxonomy);
-        $content = jvbContentTaxContent($type);
+		if (!is_array($config['content'])) {
+			$config['content'] = [$config['content']];
+		}
 
-        $context = [
-            'content'   => $content,
-            'taxonomies' => $this->getTaxonomies($content),
-            'context'   => $type,
-        ];
-        unset($context['taxonomies'][array_search($type, $context['taxonomies'])]);
-        return $context;
-    }
+		return $config;
+	}
 
-    protected function buildTaxonomyConfig():array
-    {
-        $type = jvbNoBase(get_queried_object()->taxonomy);
-        $content = jvbContentTaxContent($type);
-        return [
-            'content'   => $content,
-            'taxonomies' => $this->getTaxonomies($content),
-            'context'   => $type
-        ];
-    }
+	/**
+	 * Get taxonomies for given content types
 
-    protected function getTaxonomies(array $content):array
-    {
-        global $jvb_taxonomy_for;
-        $taxonomies = [];
-        foreach ($jvb_taxonomy_for as $taxonomy => $for) {
-            if (array_intersect($for, $content)) {
-                $taxonomies[] = $taxonomy;
-            }
-        }
-        return $taxonomies;
-    }
+	 */
+	protected function getTaxonomies(array $content): array
+	{
 
-    public function render(array $attributes, string $content, WP_Block $block)
-    {
-        $this->config = $this->buildParams($attributes);
+		$taxonomies = [];
 
+		foreach ($content as $contentType) {
+			$registrar = Registrar::getInstance($contentType);
+			if (!$registrar) {
+				continue;
+			}
+			$contentTaxonomies = $registrar->registrar->taxonomies;
+			$contentTaxonomies = array_filter($contentTaxonomies, function($taxonomy) {
+				return Registrar::getInstance($taxonomy)?->hasFeature('show_feed');
+			});
+			$taxonomies = array_merge($taxonomies, $contentTaxonomies);
+		}
+
+		return array_unique($taxonomies);
+	}
+
+	public function render(array $attributes, string $content, WP_Block $block)
+	{
+
+		$this->config = $this->buildParams($attributes);
 		return $this->cache->remember(
-			$this->config,
+			$this->cache->generateKey($this->config),
 			function() {
 				return $this->renderBlock();
 			}
 		);
-    }
+	}
+	protected function getContext():string|bool
+	{
+		return array_key_exists('context', $this->config)?$this->config['context']:false;
+	}
+	protected function getIDS():array|bool
+	{
+		return (array_key_exists('ids', $this->config) && !empty($this->config['ids'])) ? $this->config['ids'] : false;
+	}
+	protected function getClasses():array|bool
+	{
+		return array_key_exists('classes', $this->config) && !empty($this->config['classes']) ? $this->config['classes'] : false;
+	}
+	protected function getSource():string|bool
+	{
+		return array_key_exists('source', $this->config) ? $this->config['source'] : false;
+	}
 
-    protected function renderBlock():string
-    {
-        $work = isJVBUserType() ? ' id="work"' : '';
+	protected function getIcon():string|bool
+	{
+		return array_key_exists('icon', $this->config) ? $this->config['icon'] : false;
+	}
+	protected function isGallery():bool
+	{
+		return (array_key_exists('is_gallery', $this->config) && $this->config['is_gallery']);
+	}
+	protected function getContent():array|bool
+	{
+		return (array_key_exists('content', $this->config) && !empty ($this->config['content'])) ? $this->config['content'] : false;
+	}
 
-        ob_start();
-        ?>
-        <section<?= $work ?> class="feed-block"
-                             data-source="<?= get_queried_object_id(); ?>"
-            <?= (array_key_exists('context', $this->config)) ? ' data-context="'.$this->config['context'].'"' : '' ?>
-            <?= (array_key_exists('is_gallery', $this->config)) ? ' data-gallery="true"' : ''?>>
-            <?php
-            $this->renderFilters();
-            $this->renderGrid();
-            $this->renderLoader();
-            $this->renderTemplates();
-            ?>
-        </section>
-        <?php
-        return ob_get_clean();
-    }
+	protected function renderBlock(): string
+	{
+		if (is_post_type_archive(BASE.'directory')) {
+			return '';
+		}
+		$ids = ($this->getIDS()) ? ' id="'.implode(' ',$this->getIDS()).'"' : '';
+		$classes = ($this->getClasses()) ? ' class="'.implode(' ',$this->getClasses()).'"' : '';
+		$source = ($this->getSource()) ? ' data-source="'.$this->getSource().'"' : '';
+		$context = ($this->getContext()) ? ' data-context="'.$this->getContext().'"' : '';
+		$icons = ($this->getIcon()) ? ' data-icon="'.$this->getIcon().'"' : ' data-icon="'.jvbLogoIcon().'"';
+		$gallery = $this->isGallery() ? ' data-gallery' : '';
+		$content = ($this->getContent()) ? ' data-content="'.implode(',',$this->getContent()).'"' : '';
+		ob_start();
+		?>
+		<section<?= $ids.$classes ?> class="feed-block"<?= $content.$source.$context.$gallery.$icons ?>>
+			<?php
+			$this->renderFilters();
+			$this->renderGrid();
+			$this->renderLoader();
+			$this->renderTemplates();
+			echo TaxonomySelector::outputSelectorModal();
+			?>
+		</section>
+		<footer><button data-action="refresh" data-ignore><?=jvbIcon('arrows-clockwise')?><span>Hard Refresh</span></span></button></footer>
+		<?php
+		return ob_get_clean();
+	}
 
-    protected function renderFilters():void
-    {
-        if (empty($this->config)) {
-            return;
-        }
-        $many = count($this->config['content']) > 1;
-        global $jvb_everything;
-        global $jvb_taxonomy_for;
-        ?>
-        <form class="feed-filters" data-save="feed">
-            <details>
-            <summary class="row btw">
-                <span class="label">SHOWING: </span>
-                <?php
-                $labels = [];
-                foreach ($this->config['content'] as $i => $type) :
-                    $checked = $i === 0 ? ' checked' : '';
+	protected function renderFilters(): void
+	{
+		if (empty($this->config)) {
+			return;
+		}
 
-                    $label = $jvb_everything[$type]['plural'];
-                    ?>
+		$feedContent = $this->getFeedContent();
+		$hasMany = count($this->getContent()) > 1;
+		?>
+		<form class="filters" data-save="feed-<?=$this->getContext()?>">
+			<?php if ($hasMany) {
+				//If we have multiple content, only show the content first
+				?>
+				<details class="col left">
+				<summary class="row x-btw">
+					<span class="label">SHOWING: </span>
+					<?php
+					$labels = [];
+					foreach ($this->getContent() as $i => $type) :
 
-                    <input type="radio"
-                           id="filter-<?= esc_attr($type) ?>"
-                           class="btn"
-                           name="content"
-                           value="<?= esc_attr($type) ?>"
-                        <?= $checked ?>>
-                    <label for="filter-<?= esc_attr($type) ?>" title="Show <?= $label ?>" class="row">
-                        <?= jvbIcon($type, ['title'=> $label]) ?>
-                        <span class="screen-reader-text"><?= $label ?></span>
-                    </label>
-
-                <?php
-                $labels['filter-'.$type] = $label;
-                endforeach;
-                ?>
-                <ul class="filter-label">
-                <?php
-                $i = 0;
-                foreach ($labels as $id =>$label) {
-                    $active = $i === 0 ? ' class="active"' : '';
-                    ?>
-                    <li id="<?=$id?>"<?=$active?>>
-                        <?=$label?>
-                    </li>
-                    <?php
-                    $i++;
-                }
-                ?>
-                </ul>
+						$checked = $i === 0 ? ' checked' : '';
+						$label = $feedContent[$type]['plural'] ?? ucfirst($type);
+						?>
+						<input type="radio"
+							   id="filter-<?= esc_attr($type) ?>"
+							   class="btn"
+							   name="content"
+							   data-filter="content"
+							   value="<?= esc_attr($type) ?>"
+							<?= $checked ?>>
+						<label for="filter-<?= esc_attr($type) ?>" title="Show <?= $label ?>" class="row">
+							<?= jvbIcon($feedContent[$type]['icon']) ?>
+							<span class="screen-reader-text"><?= $label ?></span>
+						</label>
+						<?php
+						$labels['filter-'.$type] = $label;
+					endforeach;
+					?>
+					<ul class="filter-label">
+						<?php
+						$i = 0;
+						foreach ($labels as $id => $label) {
+							$active = $i === 0 ? ' class="active"' : '';
+							?>
+							<li id="<?= $id ?>"<?= $active ?>>
+								<?= $label ?>
+							</li>
+							<?php
+							$i++;
+						}
+						?>
+					</ul>
+			<?php } ?>
 
 
-                <?php if (is_user_logged_in()) : ?>
-                    <input type="checkbox" id="favourites" class="btn" name="favourites" value="on">
-                    <label for="favourites" title="Show Favourites" class="row">
-                        <?= jvbIcon('heart', ['title'    =>'Favourites']) ?>
-                        <span class="screen-reader-text">Show Favourites Only</span>
-                    </label>
-                <?php endif; ?>
+					<?php if (Site::has('favourites') && is_user_logged_in()) : ?>
+						<input type="checkbox" id="favourites" class="btn" name="favourites" value="on"
+							   data-filter="favourites">
+						<label for="favourites" title="Show Favourites" class="row">
+							<?= jvbIcon('heart').jvbIcon('heart', ['style' => 'fill']) ?>
+							<span class="screen-reader-text">Show Favourites Only</span>
+						</label>
+					<?php endif; ?>
+				<?php if ($hasMany) { ?>
+				</summary>
+				<?php }
+				if (!empty ($this->config['taxonomies'])) {
+				?>
+					<div class="filters">
+						<div class="filter-group row left">
+							<span class="label">FILTER BY:</span>
 
-                <?php if ($many) {
-                    echo '</summary>';
-                } ?>
+							<?php
+							foreach ($this->config['taxonomies'] as $tax) :
+								$registrar = Registrar::getInstance($tax)??false;
+								if (!$registrar) continue;
 
-                <div class="filters">
-                    <div class="filter-group">
-                        <span class="label">FILTER BY:</span>
+								$contentForTax = $registrar->registrar->for;
+								$hidden = empty($contentForTax) ? ' hidden' : '';
 
-                        <?php
-                        foreach ($jvb_taxonomy_for as $tax => $items) :
-                            $hidden = !in_array($tax, $this->config['taxonomies']) ? ' hidden' : '';
-                            if (in_array($tax, $this->config['taxonomies'])) {
-                                $tax = new TaxonomySelector(
-                                    'feed-'.$tax,
-                                    $tax,
-                                    [
-										'update' => '.selected-items-section .selected-items',
-                                        'types' => $items,
-										'hidden'=> $hidden
-                                    ]
-                                );
-                                echo  $tax->render();
-                            }
+								$taxSelector = new TaxonomySelector(
+									'feed-'.$tax,
+									$tax,
+									[
+										'icon'			=> $registrar->getIcon()??jvbLogoIcon(),
+										'update' 		=> '.selected-items-section .selected-items',
+										'types' 		=> $contentForTax,
+										'autocomplete'	=> false,
+										'hidden' 		=> $hidden,
+										'output'		=> 'minimal'
+									]
+								);
+								echo $taxSelector->render();
+							endforeach;
+							?>
+						</div>
+						<div class="selected-items-section">
+							<div class="selected-items row"></div>
+							<div class="filter-actions row">
+								<?= str_replace('class="toggle-text"', 'class="toggle-text" hidden', jvbRenderToggleTextField('match', 'Match', 'Filters', 'ALL', 'ANY', false, ['filter' => 'match'])) ?>
+								<button type="button" class="clear-filters row" hidden>
+									<?= jvbIcon('x') ?>
+									Clear All Filters
+								</button>
+							</div>
+						</div>
+					</div>
+				<?php } ?>
+					<div class="row x-btw nowrap">
+						<div class="order-by filter-group row left w-full">
+							<span class="label">ORDER BY:</span>
+							<?php
+							//TODO: Get content types that can be sorted alphabetically
+							?>
+							<input type="radio" id="order-title" class="btn" name="orderby" value="title" data-for="artist,shop" data-filter="orderby" hidden>
+							<label for="order-title" title="Order by Name" class="row">
+								<?= jvbIcon('alphabetical') ?>
+								<span class="label">Name</span>
+							</label>
 
-                        endforeach; ?>
-                    </div>
-                    <div class="selected-items-section">
-                        <div class="selected-items row"></div>
-                        <div class="filter-actions row">
-                            <?= jvbRenderToggleTextField('match', 'Match', 'Filters', 'ALL', 'ANY') ?>
-                            <button type="button" class="clear-filters row">
-                                <?= jvbIcon('x', ['title'    => 'Clear']) ?>
-                                Clear All Filters
-                            </button>
-                        </div>
-                    </div>
-                </div>
+							<input type="radio" id="order-date" class="btn" name="orderby" value="date" data-filter="orderby" checked>
+							<label for="order-date" title="Order by Date Created" class="row">
+								<?= jvbIcon('calendar', ['title' => 'Date']) ?>
+								<span class="label">Date Created</span>
+							</label>
 
-                <div class="filter-group">
-                    <div class="order-by">
-                        <span class="label">ORDER BY:</span>
-                        <input type="radio" id="order-title" class="btn" name="orderby" value="title" data-for="artist,shop" hidden>
-                        <label for="order-title" title="Order by Name" class="row">
-                            <?= jvbIcon('alphabetical') ?>
-                            <span class="label">Name</span>
-                        </label>
+							<input type="radio" id="order-modified" class="btn" name="orderby" value="modified" data-filter="orderby">
+							<label for="order-modified" title="Order by Date Modified" class="row">
+								<?= jvbIcon('clock-clockwise') ?>
+								<span class="label">Date Modified</span>
+							</label>
 
-                        <input type="radio" id="order-date" class="btn" name="orderby" value="date" checked>
-                        <label for="order-date" title="Order by Date" class="row">
-                            <?= jvbIcon('calendar', ['title'=>'Date']) ?>
-                            <span class="label">Date</span>
-                        </label>
+							<?php
+								$custom = [];
+								foreach ($this->getContent() as $content) {
+									$registrar = Registrar::getInstance($content)??false;
 
-                        <input type="radio" id="order-random" class="btn" name="orderby" value="random">
-                        <label for="order-random" title="Random Order" class="row">
-                            <?= jvbIcon('shuffle') ?>
-                            <span class="label">Random</span>
-                        </label>
-                    </div>
+									if ($registrar && !empty($registrar->config('feed')->getCustomOrder())) {
+										$custom = array_merge_recursive($custom, $registrar->config('feed')->getCustomOrder());
+									}
+								}
+								foreach ($custom as $slug => $conf) {
+									?>
+									<input type="radio" id="order-<?=$slug?>" class="btn" name="orderby" value="<?=$slug?>" data-for="<?=$conf['for']?>" data-filter="orderby">
+									<label for="order-<?=$slug?>" title="<?= $conf['label']?>" class="row">
+										<?= jvbIcon($conf['icon']) ?>
+										<span class="label"><?=$conf['label']?></span>
+									</label>
+									<?php
+								}
+								$custom = implode(',', array_keys($custom));
+							?>
+							<input type="radio" id="order-random" class="btn" name="orderby" value="random" data-filter="orderby">
+							<label for="order-random" title="Random Order" class="row">
+								<?= jvbIcon('shuffle') ?>
+								<span class="label">Random</span>
+							</label>
 
-                    <div class="order-direction radio-group-label" data-for-order="date,title">
-                        <span class="label">ORDER:</span>
-                        <input type="radio" id="order-desc" class="btn" name="order" value="desc" checked>
-                        <label for="order-desc" title="Newest First" class="row">
-                            <?= jvbIcon('sort-descending') ?>
-                        </label>
+						</div>
 
-                        <input type="radio" id="order-asc" class="btn" name="order" value="asc">
-                        <label for="order-asc" title="Oldest First" class="row">
-                            <?= jvbIcon('sort-ascending') ?>
-                        </label>
-                    </div>
-                </div>
-            </details>
-        </form>
-        <?php
-    }
+						<div class="order-direction filter-group row left w-full" data-for-order="date,modified,title<?= $custom === '' ? '' : ','.$custom?>">
+							<span class="label">ORDER:</span>
+							<input type="radio" id="order-desc" class="btn" name="order" value="desc" data-filter="order" checked>
+							<label for="order-desc" title="Sort Descending (A-Z, 1-10)" class="row">
+								<?= jvbIcon('sort-descending') ?>
+								<span class="label" >DESC (A-Z)</span>
+							</label>
 
-    protected function renderGrid():void
-    {
-        ?>
-        <div class="item-grid"></div>
-        <?php
-    }
+							<input type="radio" id="order-asc" class="btn" name="order" value="asc" data-filter="order">
+							<label for="order-asc" title="Sort Ascending (Z-A, 10-1)" class="row">
+								<?= jvbIcon('sort-ascending') ?>
+								<span class="label" >ASC (Z-A)</span>
+							</label>
+						</div>
+					</div>
+			<?php if ($hasMany) { ?>
+				</details>
+			<?php } ?>
+		</form>
+		<?php
+	}
 
-    protected function renderLoader():void
-    {
-        ?>
-        <button type="button" class="load-more">
-            <?= jvbIcon('arrow-elbow-left-down', ['title'    =>'More']) ?>
-            Show Me More
-            <?= jvbIcon('arrow-elbow-right-down', ['title'=> 'More']) ?>
-        </button>
+	protected function renderGrid(): void
+	{
+		?>
+		<div class="item-grid">
+			<?php
+			$total = count($this->getContent()) - 1;
+			for ($i = 1; $i <= 36; $i++) {
+				$rand = rand(0, $total);
+				$config = Registrar::getInstance($this->getContent()[$rand]);
+				$icon = jvbIcon($config->getIcon??jvbLogoIcon());
+				?>
+				<div class="placeholder"><?=apply_filters('jvbFeedPlaceholder', $icon) ?></div>
+				<?php
+			}
+			?>
+		</div>
+		<?php
+	}
 
-        <?= jvbLoadingScreen() ?>
-        <?php
-        if (array_key_exists('is_gallery', $this->config)) {
-            jvbRenderGallery();
-        }
-    }
+	protected function renderLoader(): void
+	{
+		?>
+		<button type="button" class="load-more">
+			<?= jvbIcon('arrow-elbow-left-down') ?>
+			Show Me More
+			<?= jvbIcon('arrow-elbow-right-down') ?>
+		</button>
 
-    protected function renderTemplates():void
-    {
-        echo '<template class="feed-item">
-        <details class="item feed" data-umami-event="view_feed">
-            <summary class="row btw">
-                <span class="handle">DETAILS</span>
-                <button class="favourite" title="Add to favourites" onclick="toggleFavourite(this)">
-                    '.jvbIcon('heart')
-               .jvbIcon('heart', ['style'=>'fill']).'
-                </button>
-                <div class="feed-images">
-                    <a>
-                        <img width="300px" height="300px" loading="lazy" decoding="async">
-                    </a>
-                </div>
-            </summary>
+		<?= jvbLoadingScreen() ?>
+		<?php
+		if (array_key_exists('is_gallery', $this->config)) {
+			jvbRenderGallery();
+		}
+	}
 
-            <div class="item-info">
-                <h3><a></a></h3>
-                <div class="item">
-                    <span class="label"></span>
-                    <a></a>
-                    <p></p>
-                </div>
-                <div class="item-list">
-                    <span class="label"></span>
-                        <ul>
-                            <li>
-                                <a></a>
-                            </li>
-                        </ul>
-                    </div>
-                </div>
-            </div>
-        </details>
-    </template>';
+	protected function renderTemplates(): void
+	{
+		if ($this->getContent()) {
+			foreach ($this->getContent() as $content) {
+				echo $this->getDefaultTemplate($content);
+			}
+		}
 
-        echo '<template class="emptyState">
-            <div class="feed-empty-state">
-                <h3>NOTHING HERE...</h3>
+		echo '<template class="feedTerm"><button class="remove-term">'.jvbIcon(jvbDefaultIcon()).'<span></span>'.jvbIcon('x').'</button></template>';
+		echo '<template class="emptyState">'.apply_filters('jvbFeedEmptyState', '<div class="empty-state">
+                <h3>'.jvbIcon($this->getIcon()).'NOTHING HERE'.jvbIcon($this->getIcon()).'</h3>
                 <p>Try tweaking those filters a bit.</p>
-                <p>Edmonton\'s got talent - let\'s find it.</p>
-            </div>
-        </template>';
+            </div>', $this->config). '</template>';
+		echo '<template class="placeholderTemplate"><div class="placeholder">'.apply_filters('jvbFeedPlaceholder', jvbIcon(jvbLogoIcon())).'</div></template>';
+	}
 
-        echo '<template class="placeholderTemplate"><div class="placeholder"></div></template>';
-    }
+	protected function getFavouritesButton(string $content):string
+	{
+		$registrar = Registrar::getInstance($content);
+		if (!$registrar || !Site::has('favourites') || !$registrar->hasFeature('favouritable')) {
+			return '';
+		}
+		return '<button class="favourite" type="button" title="Add to favourites" data-action="favourite">
+			'.jvbIcon('heart')
+			.jvbIcon('heart', ['style'=>'fill']).'
+		</button>';
+	}
+	protected function getUpvotesButton(string $content):string
+	{
+		$registrar = Registrar::getInstance($content);
+		if (!Site::has('karma') || !$registrar || !$registrar->hasFeature('karma')){
+			return '';
+		}
+		return '<div class="karma row">
+			<button type="button" class="vote" data-action="upvote">
+				'.jvbIcon('arrow-fat-up')
+				.jvbIcon('arrow-fat-up', ['style'=>'fill']).
+			'</button>
+			<button type="button" class="vote" data-action="downvote">
+				'.jvbIcon('arrow-fat-down')
+				.jvbIcon('arrow-fat-down', ['style'=>'fill']).
+			'</button>
+			<span class="score"></span>
+		</div>';
+	}
+	protected function getDefaultTemplate(string $content): string
+	{
+		$config = Registrar::getInstance($content)->getConfig('feed');
+		$allFields = Registrar::getFieldsFor($content);
+		$images = $config['images']??['post_thumbnail'];
+		$fields = $config['fields']??['post_title','post_date','post_excerpt'];
+		$fields = array_filter($fields, function($field) use($images) {
+			return !in_array($field, $images);
+		});
+		$fields = array_filter($allFields, function($field) use($fields) {
+			return in_array($field, $fields);
+		}, ARRAY_FILTER_USE_KEY);
+		$template = '<div class="feed item col '.$content.'">'.$this->getFavouritesButton($content).$this->getUpvotesButton($content);
+
+		//Add all defined images, but allow for filtering
+		$imageTemplate = '<a>';
+		foreach ($images as $image) {
+			$imageTemplate .= '<img data-field="'.$image.'" width="300px" height="300px" sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 800px" loading="lazy" decoding="async">';
+		}
+		$imageTemplate .= '</a>';
+		$template .= '<div class="images">'.apply_filters('jvbFeedImages', $imageTemplate, $content, $images).'</div>';
+
+		//Output default fields, but allow for filtering
+		$template .= '<details>
+		<summary>'.apply_filters('jvbFeedItemSummary', jvbIcon('dots-three'), $content).'</summary>';
+
+		$fieldsTemplate = '';
+
+		foreach ($fields as $fieldName => $config) {
+			$fieldsTemplate .= apply_filters('jvbFeedItemField', $this->defaultFieldTemplate($config['type'], $fieldName), $content, $fieldName, $config['type']);
+		}
+		$template .= '<div class="item-info">'.apply_filters('jvbFeedItemFields', $fieldsTemplate, $content, $fields).'</div>';
+		$template .= '</details></div>';
+
+		return '<template class="feedItem'.ucfirst($content).'">'.apply_filters('jvbFeedItem', $template, $content).'</template>';
+	}
+
+	protected function defaultFieldTemplate(string $fieldType, string $fieldName):string
+	{
+		$data = ' data-field="'.$fieldName.'"';
+		switch ($fieldName) {
+			case 'post_title':
+				return '<h3'.$data.'></h3>';
+			case 'post_date':
+			case 'post_modified':
+				return '<time'.$data.'></time>';
+		}
+		return match($fieldType) {
+			'upload'	=> '<img'.$data.' width="300px" height="300px" sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 800px" loading="lazy" decoding="async">',
+			'taxonomy'	=> '<ul'.$data.'><li><a><i></i></a></li></ul>',
+			default	=>  '<p'.$data.'></p>',
+		};
+	}
+	/**
+	 * Get feed content using Features instead of get_option
+	 * Returns array of slug => config for types that show in feed
+	 */
+	public function getFeedContent(): array
+	{
+		return JVB()->routes('feed')->getFeedTypesConfig();
+	}
 }

--
Gitblit v1.10.0