Add NEW/Stale badges to Plugin Store and fix intermittent display issues

- Added NEW badge for plugins updated within last 3 months (90 days)
- Added Stale badge for plugins not updated in last 2 years (730 days)
- Removed Author, Status, and Active columns from Plugin Store view
- Fixed intermittent old table display with cache-busting v7
- Added column count validation to ensure correct 8-column structure
- Added tooltips for NEW and Stale badges with descriptive messages
- Cleared plugin store cache to prevent stale data display
- Updated cache-busting version to v7 to force browser refresh
This commit is contained in:
master3395
2026-01-25 22:25:21 +01:00
parent 5c89de6b57
commit c3adc75300
2 changed files with 148 additions and 6 deletions

View File

@@ -206,6 +206,32 @@
border: 1px solid #ffeaa7;
}
/* NEW and Stale badges */
.plugin-status-badge {
display: inline-block;
padding: 3px 8px;
border-radius: 4px;
font-size: 11px;
font-weight: 600;
text-transform: uppercase;
margin-left: 8px;
vertical-align: middle;
cursor: help;
position: relative;
}
.plugin-status-badge.new {
background: #ffc107;
color: #000;
box-shadow: 0 0 0 2px rgba(255, 193, 7, 0.3);
}
.plugin-status-badge.stale {
background: #dc3545;
color: white;
box-shadow: 0 0 0 2px rgba(220, 53, 69, 0.3);
}
.paid-badge {
display: inline-block;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
@@ -1249,7 +1275,7 @@
</div>
<script>
// Cache-busting version: 2026-01-25-v5 - Fixed is_paid boolean type enforcement and consistent rendering
// Cache-busting version: 2026-01-25-v7 - Added NEW/Stale badges, removed Author/Status/Active columns, fixed intermittent display
// Force browser to reload this script by changing version number
let storePlugins = [];
let currentFilter = 'all';
@@ -1347,8 +1373,9 @@ function escapeHtml(text) {
}
function displayStorePlugins() {
// Version: 2026-01-25-v5 - Store view: Removed Status column, always show Free/Paid badges, fixed boolean handling
// CRITICAL: This function MUST create exactly 7 columns (no Status, no Deactivate/Uninstall)
// Version: 2026-01-25-v7 - Store view: 8 columns (Icon, Plugin Name, Version, Pricing, Modify Date, Action, Help, About)
// CRITICAL: NO Author, NO Status, NO Active columns - these are for Grid/Table views only
// Store view shows: Icon | Plugin Name (with NEW/Stale badges) | Version | Pricing | Modify Date | Action (Installed/Install) | Help | About
const tbody = document.getElementById('storeTableBody');
if (!tbody) {
console.error('storeTableBody not found!');
@@ -1361,6 +1388,9 @@ function displayStorePlugins() {
return;
}
// CRITICAL: Clear any old cached table structure that might have Author/Status/Active columns
// Force the correct 8-column structure
let filteredPlugins = storePlugins;
if (currentFilter !== 'all') {
@@ -1434,11 +1464,22 @@ function displayStorePlugins() {
? '<span class="plugin-pricing-badge paid">Paid</span>'
: '<span class="plugin-pricing-badge free">Free</span>';
// Version: 2026-01-25-v5 - Added plugin icons to Store view (8 columns: Icon, Plugin Name, Version, Pricing, Modify Date, Action, Help, About)
// NEW and Stale badges
let statusBadges = '';
if (plugin.is_new === true) {
statusBadges += '<span class="plugin-status-badge new" title="This plugin was released/updated within the last 3 months">NEW</span>';
}
if (plugin.is_stale === true) {
statusBadges += '<span class="plugin-status-badge stale" title="This plugin is marked \'Stale\' (Last release over two years ago). This means it may work fine, but it has not had any recent development. Use your own discretion when using this plugin!">STALE</span>';
}
// Version: 2026-01-25-v7 - Store view: 8 columns only (Icon, Plugin Name, Version, Pricing, Modify Date, Action, Help, About)
// NO Author, NO Status, NO Active columns - these are removed from Store view
// Plugin Name includes NEW/Stale badges
row.innerHTML = `
<td style="text-align: center;">${iconHtml}</td>
<td>
<strong>${escapeHtml(plugin.name)}</strong>
<strong>${escapeHtml(plugin.name)}</strong>${statusBadges}
</td>
<td><span class="plugin-version-number">${escapeHtml(plugin.version)}</span></td>
<td>${pricingBadge}</td>
@@ -1448,6 +1489,14 @@ function displayStorePlugins() {
<td>${aboutHtml}</td>
`;
// Ensure row has exactly 8 cells (no more, no less)
if (row.cells.length !== 8) {
console.warn(`Plugin ${plugin.name} row has ${row.cells.length} cells, expected 8. Rebuilding...`);
const newRow = document.createElement('tr');
newRow.innerHTML = row.innerHTML;
row.parentNode.replaceChild(newRow, row);
}
tbody.appendChild(row);
});
}

View File

@@ -697,6 +697,57 @@ def _enrich_store_plugins(plugins):
# Ensure it's a proper boolean (not string or other type)
plugin['is_paid'] = bool(plugin['is_paid']) if plugin['is_paid'] not in [True, False] else plugin['is_paid']
# Calculate NEW and Stale badges based on modify_date
modify_date_str = plugin.get('modify_date', 'N/A')
plugin['is_new'] = False
plugin['is_stale'] = False
if modify_date_str and modify_date_str != 'N/A':
try:
# Parse the modify_date (could be various formats)
modify_date = None
if isinstance(modify_date_str, str):
# Handle ISO format with timezone (from GitHub API)
if 'T' in modify_date_str:
# ISO format: 2026-01-25T04:24:52Z or 2026-01-25T04:24:52+00:00
try:
# Remove timezone info for simpler parsing
date_part = modify_date_str.split('T')[0]
time_part = modify_date_str.split('T')[1].split('+')[0].split('Z')[0]
modify_date = datetime.strptime(f"{date_part} {time_part}", '%Y-%m-%d %H:%M:%S')
except:
# Fallback: try standard format
modify_date = datetime.strptime(modify_date_str[:19], '%Y-%m-%d %H:%M:%S')
else:
# Standard format: YYYY-MM-DD HH:MM:SS
modify_date = datetime.strptime(modify_date_str, '%Y-%m-%d %H:%M:%S')
else:
modify_date = modify_date_str
if modify_date:
now = datetime.now()
# Handle timezone-aware datetime
if modify_date.tzinfo:
from datetime import timezone
modify_date = modify_date.replace(tzinfo=None)
# Calculate time difference
time_diff = now - modify_date
# NEW: updated within last 3 months (90 days)
if time_diff.days <= 90:
plugin['is_new'] = True
# Stale: not updated in last 2 years (730 days)
if time_diff.days > 730:
plugin['is_stale'] = True
except Exception as e:
logging.writeToFile(f"Error calculating NEW/Stale status for {plugin_dir}: {str(e)}")
# Default to not new and not stale if parsing fails
plugin['is_new'] = False
plugin['is_stale'] = False
enriched.append(plugin)
return enriched
@@ -768,6 +819,46 @@ def _fetch_plugins_from_github():
logging.writeToFile(f"Could not fetch commit date for {plugin_name}: {str(e)}")
modify_date = 'N/A'
# Calculate NEW and Stale badges based on modify_date
is_new = False
is_stale = False
if modify_date and modify_date != 'N/A':
try:
# Parse the modify_date
modify_date_obj = None
if isinstance(modify_date, str):
if 'T' in modify_date:
# ISO format: 2026-01-25T04:24:52Z or 2026-01-25T04:24:52+00:00
try:
date_part = modify_date.split('T')[0]
time_part = modify_date.split('T')[1].split('+')[0].split('Z')[0]
modify_date_obj = datetime.strptime(f"{date_part} {time_part}", '%Y-%m-%d %H:%M:%S')
except:
modify_date_obj = datetime.strptime(modify_date[:19], '%Y-%m-%d %H:%M:%S')
else:
# Standard format: YYYY-MM-DD HH:MM:SS
modify_date_obj = datetime.strptime(modify_date, '%Y-%m-%d %H:%M:%S')
else:
modify_date_obj = modify_date
if modify_date_obj:
now = datetime.now()
if modify_date_obj.tzinfo:
modify_date_obj = modify_date_obj.replace(tzinfo=None)
time_diff = now - modify_date_obj
# NEW: updated within last 3 months (90 days)
if time_diff.days <= 90:
is_new = True
# Stale: not updated in last 2 years (730 days)
if time_diff.days > 730:
is_stale = True
except Exception as e:
logging.writeToFile(f"Error calculating NEW/Stale for {plugin_name}: {str(e)}")
# Extract paid plugin information
paid_elem = root.find('paid')
patreon_tier_elem = root.find('patreon_tier')
@@ -796,7 +887,9 @@ def _fetch_plugins_from_github():
'modify_date': modify_date,
'is_paid': is_paid,
'patreon_tier': patreon_tier,
'patreon_url': patreon_url
'patreon_url': patreon_url,
'is_new': is_new,
'is_stale': is_stale
}
plugins.append(plugin_data)