From d7dbe7fee362d587dfc334135d9581b6216a4295 Mon Sep 17 00:00:00 2001
From: Jake Vanderwerf <get@jakevanderwerf.ca>
Date: Sun, 23 Nov 2025 04:13:56 +0000
Subject: [PATCH] =Timeline block, and feed block updated. DataStore.js refactored to not block rendering
---
inc/managers/IconsManager.php | 301 +++++++++++++++++++++++++++++++++++++++++++------
1 files changed, 262 insertions(+), 39 deletions(-)
diff --git a/inc/managers/IconsManager.php b/inc/managers/IconsManager.php
index d3710d0..8278763 100644
--- a/inc/managers/IconsManager.php
+++ b/inc/managers/IconsManager.php
@@ -17,6 +17,7 @@
protected array $customIcons = [];
protected array $usedIcons = [];
protected array $map = [];
+ protected const MAX_VERSIONS = 5;
/**
* Get singleton instance
@@ -31,6 +32,7 @@
private function __construct()
{
$this->cache = CacheManager::for('icons', WEEK_IN_SECONDS);
+
$this->style = (array_key_exists('icons', JVB_SITE) && in_array(JVB_SITE['icons'], $this->styles))
? JVB_SITE['icons']
: 'regular';
@@ -44,36 +46,35 @@
]);
- $this->usedIcons = get_option(BASE.'used_icons', []);
+ $this->usedIcons = get_option(BASE.'usedIcons', []);
$this->includeIcons();
+ // Track custom icons for CSS generation
+ $this->trackCustomIcons();
// Register hooks only once
$this->registerHooks();
}
/**
- * Include icons via filter (for JS usage, etc.)
+ * Ensure custom icons are tracked for CSS generation
*/
- protected function includeIcons():array
+ protected function trackCustomIcons(): void
{
- $icons = get_option(BASE.'includeIcons');
-// $icons = false;
- if (!$icons) {
- $icons = $this->addIncludeIcons();
- }
- $include = apply_filters('jvbIncludeIcons', []);
- $add = array_filter($include, function($addIt) use ($icons) {
- return !in_array($addIt, $icons);
- });
- error_log('Adding icons: '.print_r($add, true));
- if (!empty($add)) {
- $this->addIncludeIcons($add);
+ if (empty($this->customIcons)) {
+ return;
}
- return $icons;
+ foreach ($this->customIcons as $name => $path) {
+ $this->trackIconUsage($name, $this->style);
+ }
}
- protected function addIncludeIcons(array $add = []) {
+ /**
+ * Include icons via filter (for JS usage, etc.)
+ */
+ protected function includeIcons():void
+ {
$icons = get_option(BASE.'includeIcons');
+
if (!$icons) {
$icons = [
'check-circle',
@@ -120,6 +121,7 @@
'file-txt',
'file-xls',
];
+
$check = [JVB_CONTENT, JVB_TAXONOMY, JVB_USER];
foreach ($check as $constant) {
foreach ($constant as $key => $value) {
@@ -129,20 +131,77 @@
}
}
$icons = apply_filters('jvbIncludeIcons', $icons);
-
+ $icons = $this->maybePrefixIcons($icons);
update_option(BASE.'includeIcons', $icons);
- update_option(BASE.'icons_needs_update', true);
}
- $add = apply_filters('jvbIncludeIcons', $add);
- $add = array_filter($add, function($addIt) use ($icons) {
- return !in_array($addIt, $icons);
- });
- if (!empty($add)) {
- $icons = array_merge($add, $icons);
- update_option(BASE.'usedIcons', $icons);
- update_option(BASE.'icons_needs_update', true);
+
+ // Ensure icons are in the correct format (handle legacy data)
+ if (!$this->isIconsArrayPrefixed($icons)) {
+ $icons = $this->maybePrefixIcons($icons);
+ update_option(BASE.'includeIcons', $icons);
}
- return $icons;
+
+ $additional = apply_filters('jvbIncludeIcons', []);
+ if (!empty($additional)) {
+ $additional = $this->maybePrefixIcons($additional);
+ $merged = $this->mergeUsedIcons($icons, $additional);
+
+ if ($icons != $merged) {
+ update_option(BASE.'includeIcons', $merged);
+ $icons = $merged;
+ }
+ }
+
+ foreach ($icons as $style => $theIcons) {
+ foreach($theIcons as $icon) {
+ $this->trackIconUsage($icon, $style);
+ }
+ }
+ }
+
+ /**
+ * Check if icons array is in the prefixed format [style => [icons]]
+ */
+ protected function isIconsArrayPrefixed(array $icons): bool
+ {
+ if (empty($icons)) {
+ return true;
+ }
+
+ // Check if first key is a valid style name
+ $first_key = array_key_first($icons);
+ if (!in_array($first_key, $this->styles)) {
+ return false;
+ }
+
+ // Check if first value is an array
+ return is_array($icons[$first_key]);
+ }
+
+ protected function maybePrefixIcons(array $icons):array
+ {
+ $out = [];
+ foreach ($icons as $icon) {
+ if (is_array($icon) && array_key_exists('style', $icon)) {
+ if (!array_key_exists($icon['style'], $out)) {
+ $out[$icon['style']] = [];
+ }
+ if (!in_array($icon['name'], $out[$icon['style']])) {
+ $out[$icon['style']][] = $icon['name'];
+ }
+ } elseif(is_array($icon)) {
+ $icon = $icon['name'];
+ }
+ if (!is_array($icon)) {
+ if (!array_key_exists($this->style, $out)) {
+ $out[$this->style] = [];
+ }
+ if (!in_array($icon, $out[$this->style])){
+ $out[$this->style][] = $icon;
+ }
+ }
+ }
+ return $out;
}
protected function addMap():void
@@ -173,7 +232,8 @@
*/
protected function registerHooks(): void
{
- add_action('init', [$this, 'checkCSS']);
+ add_action('init', [$this, 'includeIcons'], 1);
+ add_action('init', [$this, 'checkCSS'], 10);
add_action('wp_enqueue_scripts', [$this, 'enqueueIconStyles']);
add_action('admin_enqueue_scripts', [$this, 'enqueueIconStyles']);
}
@@ -192,7 +252,15 @@
{
error_log('[IconsManager]:regenerateCSS');
$css = $this->generateIconCSS();
- $css_path = JVB_DIR.'/assets/css/icons.css';
+ $css_path = JVB_CHILD_DIR.'/assets/css/';
+ if (!file_exists($css_path)) {
+ wp_mkdir_p($css_path);
+ }
+ $css_path .= '/icons.css';
+
+
+ // Archive current version before overwriting
+ $this->archiveCurrentVersion($css);
if (file_put_contents($css_path, $css) !== false) {
CacheManager::updateTimestamp('icons');
@@ -228,24 +296,23 @@
*/
public function getIcon(string $name, array $options = []): string
{
- if (!array_key_exists('style', $options)) {
- $options['style'] = $this->style;
- }
+ $style = array_key_exists('style', $options) ? $options['style'] :$this->style;
$name = (array_key_exists($name, $this->map)) ? $this->map[$name] : $name;
// Validate icon exists
- if (!$this->iconExists($name, $options['style'])) {
+ if (!$this->iconExists($name, $style)) {
error_log('[IconsManager] Icon not found: ' . $name);
return '';
}
- $style = $options['style'] ?? $this->style;
+
// Track icon usage
$this->trackIconUsage($name, $style);
+ $styleClass = ($style !== $this->style) ? '-'.substr($style, 0,2) : '';
// Build classes
- $classes = ['icon', 'icon-' . $name];
+ $classes = ['icon', 'icon-' . $name.$styleClass];
if (!empty($options['class'])) {
$classes[] = $options['class'];
}
@@ -264,13 +331,24 @@
*/
protected function trackIconUsage(string $name, string $style): void
{
+ $needsUpdate = false;
+
if (!array_key_exists($style, $this->usedIcons)) {
$this->usedIcons[$style] = [];
+ $needsUpdate = true;
}
if (!in_array($name, $this->usedIcons[$style])) {
$this->usedIcons[$style][] = $name;
- update_option(BASE.'used_icons', $this->usedIcons);
+ $needsUpdate = true;
+ }
+
+ if ($needsUpdate) {
+ // Merge with existing option to never lose icons
+ $existing = get_option(BASE.'usedIcons', []);
+ $merged = $this->mergeUsedIcons($existing, $this->usedIcons);
+ update_option(BASE.'usedIcons', $merged);
+
// Flag for regeneration on next init
update_option(BASE.'icons_needs_update', true);
@@ -357,7 +435,7 @@
wp_enqueue_style(
'jvb-icons',
- JVB_URL.'assets/css/icons.css',
+ JVB_CHILD_URL.'assets/css/icons.css',
[],
$timestamp
);
@@ -369,10 +447,11 @@
protected function generateIconCSS(): string
{
$css = '';
+ $this->mergeUsedIcons();
+
foreach ($this->usedIcons as $style => $icons) {
$styleClass = ($style !== $this->style) ? '-'.substr($style, 0,2) : '';
foreach ($icons as $icon) {
-
$svg = $this->getEncodedSVG($icon, $style);
if ($svg !== '') {
$css .= ".icon-{$icon}{$styleClass}{";
@@ -384,6 +463,34 @@
return $this->minifyCss($css);
}
+ protected function mergeUsedIcons(array|bool $oldIcons = true, array|bool $newIcons = true):array
+ {
+ $set = false;
+ if ($oldIcons === true) {
+ $oldIcons = $this->usedIcons;
+ $set = true;
+ }
+ if ($newIcons === true) {
+ $history = $this->getVersionHistory();
+ $newIcons = (count($history) > 0) ? $history[0]['iconList'] : [];
+ }
+ foreach ($newIcons as $style => $icons) {
+ if (!isset($oldIcons[$style])) {
+ //Style doesn't exist in previous set, add the whole thing
+ $oldIcons[$style] = $icons;
+ } else {
+ $oldIcons[$style] = array_unique(
+ array_merge($oldIcons[$style], $icons)
+ );
+ }
+ }
+ if ($set) {
+ $this->usedIcons = $oldIcons;
+ update_option(BASE.'usedIcons', $oldIcons);
+ }
+ return $oldIcons;
+ }
+
protected function minifyCSS(string $css): string
{
// Remove comments
@@ -428,7 +535,123 @@
public function clearIconCache(): void
{
delete_option(BASE . 'icon_usage_list'); // Clear DB option
+ delete_option(BASE.'usedIcons');
+ delete_option(BASE.'includeIcons');
+ delete_option(BASE.'iconMap');
$this->cache->delete('icon_styles_css');
CacheManager::updateTimestamp('icons');
}
+
+ protected function archiveCurrentVersion(string $css): void
+ {
+ $history = $this->getVersionHistory();
+
+ $icon_count = 0;
+ foreach ($this->usedIcons as $style => $icons) {
+ $icon_count += count($icons);
+ }
+
+ $newEntry = [
+ 'css' => $css,
+ 'iconList' => $this->usedIcons,
+ 'timestamp' => time(),
+ 'icon_count' => $icon_count,
+ 'size' => strlen($css),
+ 'size_formatted' => size_format(strlen($css), 2)
+ ];
+
+ array_unshift($history, $newEntry);
+
+ if (count($history) > self::MAX_VERSIONS) {
+ $history = array_slice($history, 0, self::MAX_VERSIONS);
+ }
+
+ update_option(BASE.'icon_css_history', $history);
+ }
+
+ public function getVersionHistory(): array
+ {
+ return get_option(BASE.'icon_css_history', []);
+ }
+
+ public function restoreVersion(int $timestamp): bool
+ {
+ $history = $this->getVersionHistory();
+
+ foreach ($history as $entry) {
+ if ($entry['timestamp'] === $timestamp) {
+ $css_path = JVB_DIR . '/assets/css/icons.css';
+
+ // Archive current before restoring
+ $current_css = file_get_contents($css_path);
+ if ($current_css !== false) {
+ $this->archiveCurrentVersion($current_css);
+ }
+
+ // Restore the version
+ if (file_put_contents($css_path, $entry['css']) !== false) {
+ $this->usedIcons = $entry['iconList'];
+ update_option(BASE.'usedIcons', $this->usedIcons);
+ CacheManager::updateTimestamp('icons');
+ return true;
+ }
+
+ return false;
+ }
+ }
+
+ error_log("[IconsManager] Version {$timestamp} not found in history");
+ return false;
+ }
+
+ public function forceRefresh(): void
+ {
+ $this->clearIconCache();
+ update_option(BASE.'icons_needs_update', true);
+ CacheManager::updateTimestamp('icons');
+ }
+
+ public function mergeVersions(array $timestamps): bool
+ {
+ if (empty($timestamps)) {
+ return false;
+ }
+
+ $history = get_option(BASE.'icon_css_history', []);
+ $merged_icons = [];
+ // Collect icons from selected versions
+ foreach ($history as $entry) {
+ if (in_array($entry['timestamp'], $timestamps)) {
+ foreach ($entry['iconList'] as $style => $icons) {
+ if (!isset($merged_icons[$style])) {
+ $merged_icons[$style] = [];
+ }
+ // Merge and keep unique
+ $merged_icons[$style] = array_unique(
+ array_merge($merged_icons[$style], $icons)
+ );
+ }
+ }
+ }
+
+ if (empty($merged_icons)) {
+ error_log('[IconsManager] No icons found in selected versions');
+ return false;
+ }
+
+ // Archive current version
+ $current_css = file_get_contents(JVB_DIR . '/assets/css/icons.css');
+ if ($current_css !== false) {
+ $this->archiveCurrentVersion($current_css);
+ }
+
+ // Update used icons and regenerate
+ $this->usedIcons = $merged_icons;
+ update_option(BASE.'usedIcons', $this->usedIcons);
+
+ // Force regeneration
+ $this->regenerateCSS();
+
+ return true;
+ }
}
--
Gitblit v1.10.0