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:
+103
@@ -0,0 +1,103 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App;
|
||||
|
||||
final class View
|
||||
{
|
||||
public static function e(?string $value): string
|
||||
{
|
||||
return htmlspecialchars((string) $value, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
|
||||
}
|
||||
|
||||
public static function sourceLabel(string $source): string
|
||||
{
|
||||
return match ($source) {
|
||||
'youtube' => 'YouTube',
|
||||
'reddit' => 'Reddit',
|
||||
'rt' => 'RT',
|
||||
default => ucfirst($source),
|
||||
};
|
||||
}
|
||||
|
||||
public static function sourceBadge(string $source): string
|
||||
{
|
||||
return match ($source) {
|
||||
'youtube' => 'text-bg-danger',
|
||||
'reddit' => 'text-bg-warning',
|
||||
'rt' => 'text-bg-dark',
|
||||
default => 'text-bg-secondary',
|
||||
};
|
||||
}
|
||||
|
||||
public static function sourceColor(string $source): string
|
||||
{
|
||||
return match ($source) {
|
||||
'youtube' => '#ff1f4b',
|
||||
'reddit' => '#ff6a00',
|
||||
'rt' => '#00c389',
|
||||
default => '#6a3dff',
|
||||
};
|
||||
}
|
||||
|
||||
public static function rangeLabel(string $range): string
|
||||
{
|
||||
return match ($range) {
|
||||
'1h' => '1 hour',
|
||||
'2h' => '2 hours',
|
||||
'6h' => '6 hours',
|
||||
'24h' => '24 hours',
|
||||
'7d' => '7 days',
|
||||
default => 'All time',
|
||||
};
|
||||
}
|
||||
|
||||
public static function render(string $template, array $data = []): string
|
||||
{
|
||||
$path = dirname(__DIR__) . '/templates/' . $template . '.php';
|
||||
if (!is_file($path)) {
|
||||
throw new \RuntimeException(sprintf('Template "%s" not found.', $template));
|
||||
}
|
||||
|
||||
extract($data, EXTR_SKIP);
|
||||
ob_start();
|
||||
include $path;
|
||||
|
||||
return (string) ob_get_clean();
|
||||
}
|
||||
|
||||
public static function timeAgo(?string $iso): string
|
||||
{
|
||||
if ($iso === null || $iso === '') {
|
||||
return '';
|
||||
}
|
||||
|
||||
$timestamp = strtotime($iso);
|
||||
if ($timestamp === false) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$diff = time() - $timestamp;
|
||||
if ($diff < 60) {
|
||||
return 'just now';
|
||||
}
|
||||
|
||||
$units = [
|
||||
31536000 => 'y',
|
||||
2592000 => 'mo',
|
||||
604800 => 'w',
|
||||
86400 => 'd',
|
||||
3600 => 'h',
|
||||
60 => 'm',
|
||||
];
|
||||
|
||||
foreach ($units as $seconds => $label) {
|
||||
if ($diff >= $seconds) {
|
||||
return floor($diff / $seconds) . $label . ' ago';
|
||||
}
|
||||
}
|
||||
|
||||
return 'just now';
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user