Initial commit: RSS feed dashboard aggregating YouTube, Reddit and RT

- PHP 8 + PDO SQLite cache with on-demand TTL refresh and dedup by link hash
- laminas-feed + Guzzle for RSS/Atom parsing and HTTP
- Bootstrap 5 UI with source and time-range filters, AJAX load-more
- User cron-free deployment under Apache/php-fpm; .htaccess hardening
This commit is contained in:
Harwinder Sekhon
2026-10-05 17:07:48 -04:00
commit ad4dc6b984
21 changed files with 2732 additions and 0 deletions
+49
View File
@@ -0,0 +1,49 @@
<?php
declare(strict_types=1);
use App\View;
/** @var array<int, array<string, mixed>> $items */
foreach ($items as $item):
$source = (string) $item['source'];
$color = View::sourceColor($source);
$link = View::e((string) $item['link']);
$publishedAt = $item['published_at'] ?? null;
?>
<div class="col-12 col-md-6 col-xl-4">
<article class="card h-100 feed-card" style="--src: <?= View::e($color) ?>">
<?php if (!empty($item['thumbnail'])): ?>
<a href="<?= $link ?>" target="_blank" rel="noopener noreferrer" class="feed-thumb-link">
<img src="<?= View::e((string) $item['thumbnail']) ?>"
class="card-img-top feed-thumb"
alt="" loading="lazy" decoding="async" referrerpolicy="no-referrer">
<span class="thumb-tag"><?= View::e(View::sourceLabel($source)) ?></span>
</a>
<?php endif; ?>
<div class="card-body d-flex flex-column">
<div class="d-flex align-items-center gap-2 mb-2">
<?php if (empty($item['thumbnail'])): ?>
<span class="source-chip"><?= View::e(View::sourceLabel($source)) ?></span>
<?php endif; ?>
<small class="feed-name text-truncate mono"><?= View::e((string) ($item['feed_label'] ?? '')) ?></small>
</div>
<h2 class="card-title mb-2">
<a href="<?= $link ?>" target="_blank" rel="noopener noreferrer"><?= View::e((string) $item['title']) ?></a>
</h2>
<?php if (!empty($item['description'])): ?>
<p class="card-text flex-grow-1 mb-2"><?= View::e((string) $item['description']) ?></p>
<?php endif; ?>
<div class="card-footer-meta mt-auto d-flex justify-content-between align-items-center pt-3">
<span class="author text-truncate me-2"><?= View::e((string) ($item['author'] ?? '')) ?></span>
<?php if ($publishedAt): ?>
<time datetime="<?= View::e((string) $publishedAt) ?>"
data-timestamp="<?= View::e((string) $publishedAt) ?>"
class="timestamp mono flex-shrink-0"><?= View::e(View::timeAgo((string) $publishedAt)) ?></time>
<?php endif; ?>
</div>
</div>
</article>
</div>
<?php endforeach; ?>