jaeswift-website/js/radar.js

169 lines
6.6 KiB
JavaScript

/* ─── RADAR: Live Intelligence Feed ─────────────── */
(function() {
'use strict';
const API = '/api/radar';
const REFRESH_INTERVAL = 15 * 60 * 1000; // 15 minutes
let currentSource = 'all';
let allItems = [];
let refreshTimer = null;
// ─── Time Ago ──────────────────────────────────
function timeAgo(dateStr) {
if (!dateStr) return '';
const now = new Date();
const then = new Date(dateStr);
const diff = Math.floor((now - then) / 1000);
if (diff < 60) return diff + 's ago';
if (diff < 3600) return Math.floor(diff / 60) + 'm ago';
if (diff < 86400) return Math.floor(diff / 3600) + 'h ago';
if (diff < 604800) return Math.floor(diff / 86400) + 'd ago';
return Math.floor(diff / 604800) + 'w ago';
}
// ─── Extract Domain ────────────────────────────
function extractDomain(url) {
try {
const u = new URL(url);
return u.hostname.replace('www.', '');
} catch(e) {
return '';
}
}
// ─── Escape HTML ───────────────────────────────
function esc(s) {
const d = document.createElement('div');
d.textContent = s;
return d.innerHTML;
}
// ─── Render Feed ───────────────────────────────
function renderFeed(items) {
const feed = document.getElementById('radarFeed');
if (!items || items.length === 0) {
feed.innerHTML = '<div class="radar-empty">NO SIGNALS DETECTED ON CURRENT FREQUENCY</div>';
return;
}
let html = '';
items.forEach(item => {
const domain = extractDomain(item.url);
const ago = timeAgo(item.published);
const sourceClass = 'source-' + item.source_id;
const sourceLabel = item.source || 'UNKNOWN';
html += '<div class="radar-item">';
html += ' <div class="radar-item-time">' + esc(ago) + '</div>';
html += ' <div class="radar-item-source ' + sourceClass + '">' + esc(sourceLabel) + '</div>';
html += ' <div class="radar-item-content">';
html += ' <a href="' + esc(item.url) + '" class="radar-item-title" target="_blank" rel="noopener">' + esc(item.title) + '</a>';
html += ' <div class="radar-item-meta">';
if (domain) {
html += ' <span class="radar-item-domain">' + esc(domain) + '</span>';
}
if (item.comments_url) {
html += ' <a href="' + esc(item.comments_url) + '" class="radar-item-comments" target="_blank" rel="noopener">COMMENTS</a>';
}
html += ' </div>';
html += ' </div>';
html += '</div>';
});
feed.innerHTML = html;
}
// ─── Filter & Search ───────────────────────────
function applyFilters() {
const q = document.getElementById('radarSearch').value.trim().toLowerCase();
let filtered = allItems;
if (currentSource !== 'all') {
filtered = filtered.filter(i => i.source_id === currentSource);
}
if (q) {
filtered = filtered.filter(i =>
(i.title || '').toLowerCase().includes(q) ||
(i.summary || '').toLowerCase().includes(q)
);
}
document.getElementById('statTotal').textContent = filtered.length;
renderFeed(filtered);
}
// ─── Fetch Data ────────────────────────────────
async function fetchRadar(forceRefresh) {
const feed = document.getElementById('radarFeed');
feed.innerHTML = '<div class="radar-loading">SCANNING FREQUENCIES...</div>';
try {
if (forceRefresh) {
await fetch(API + '/refresh', { method: 'POST' });
}
const res = await fetch(API);
const data = await res.json();
allItems = data.items || [];
document.getElementById('statTotal').textContent = allItems.length;
// Format last updated
if (data.last_updated) {
const d = new Date(data.last_updated);
const pad = n => String(n).padStart(2, '0');
document.getElementById('statUpdated').textContent =
pad(d.getHours()) + ':' + pad(d.getMinutes()) + ':' + pad(d.getSeconds()) + ' UTC';
}
applyFilters();
} catch(err) {
console.error('RADAR fetch error:', err);
feed.innerHTML = '<div class="radar-empty">⚠ SIGNAL LOST — UNABLE TO REACH FEED API</div>';
}
}
// ─── Event Listeners ───────────────────────────
function init() {
// Source filters
document.querySelectorAll('.radar-filter').forEach(btn => {
btn.addEventListener('click', function(e) {
e.preventDefault();
document.querySelectorAll('.radar-filter').forEach(b => b.classList.remove('active'));
this.classList.add('active');
currentSource = this.dataset.source;
applyFilters();
});
});
// Search
let searchTimeout;
document.getElementById('radarSearch').addEventListener('input', function() {
clearTimeout(searchTimeout);
searchTimeout = setTimeout(applyFilters, 200);
});
// Refresh button
document.getElementById('radarRefresh').addEventListener('click', function(e) {
e.preventDefault();
this.classList.add('spinning');
fetchRadar(true).then(() => {
setTimeout(() => this.classList.remove('spinning'), 500);
});
});
// Initial fetch
fetchRadar(false);
// Auto-refresh every 15 min
refreshTimer = setInterval(() => fetchRadar(false), REFRESH_INTERVAL);
}
// ─── Boot ──────────────────────────────────────
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', init);
} else {
init();
}
})();