Files
comv3/status/index.php
T
Ty Clifford 6ab3058a92 v3
2026-05-20 21:08:23 -04:00

233 lines
9.8 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
/**
* status/index.php — Updates log: paginated list view
*
* URLs:
* /status/ → page 1
* /status/?page=2 → page 2
* /status/?id=<uid> → redirects to /status/post.php?id=<uid>
*/
require_once __DIR__ . '/config.php';
// ── Redirect bare ?id= to the single-post page ───────────────────────────────
if (isset($_GET['id']) && trim($_GET['id']) !== '') {
$safe_id = rawurlencode(trim($_GET['id']));
header('Location: ' . STATUS_BASE_URL . '/post.php?id=' . $safe_id, true, 301);
exit;
}
// ── Load data ─────────────────────────────────────────────────────────────────
function status_load_posts(): array {
if (!file_exists(STATUS_JSON_PATH)) return [];
$raw = json_decode(file_get_contents(STATUS_JSON_PATH), true);
return is_array($raw) ? $raw : [];
}
function status_relative_time(string $iso): string {
try {
$dt = new DateTime($iso, new DateTimeZone('UTC'));
$now = new DateTime('now', new DateTimeZone('UTC'));
$diff = $now->getTimestamp() - $dt->getTimestamp();
if ($diff < 60) return 'just now';
if ($diff < 3600) return floor($diff / 60) . 'm ago';
if ($diff < 86400) return floor($diff / 3600) . 'h ago';
if ($diff < 604800) return floor($diff / 86400) . 'd ago';
if ($diff < 2592000) return floor($diff / 604800) . 'w ago';
return $dt->format('M j, Y');
} catch (Exception $e) { return ''; }
}
function status_abs_date(string $iso): string {
try {
$dt = new DateTime($iso, new DateTimeZone('UTC'));
return $dt->format('F j, Y · g:i A') . ' UTC';
} catch (Exception $e) { return $iso; }
}
function status_iso(string $iso): string {
try {
$dt = new DateTime($iso, new DateTimeZone('UTC'));
return $dt->format(DateTime::ATOM);
} catch (Exception $e) { return $iso; }
}
$all_posts = status_load_posts();
$total = count($all_posts);
$per_page = max(1, (int) STATUS_PER_PAGE);
$total_pages = max(1, (int) ceil($total / $per_page));
$page = max(1, min($total_pages, (int) ($_GET['page'] ?? 1)));
$offset = ($page - 1) * $per_page;
$posts = array_slice($all_posts, $offset, $per_page);
$page_title = STATUS_PAGE_TITLE . ($page > 1 ? ' — Page ' . $page : '');
$canonical = STATUS_BASE_URL . '/' . ($page > 1 ? '?page=' . $page : '');
?>
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title><?= htmlspecialchars($page_title) ?> — <?= htmlspecialchars(STATUS_SITE_NAME) ?></title>
<meta name="description" content="<?= htmlspecialchars(STATUS_PAGE_SUBTITLE) ?>">
<link rel="canonical" href="<?= htmlspecialchars($canonical) ?>">
<meta property="og:title" content="<?= htmlspecialchars(STATUS_PAGE_TITLE) ?>">
<meta property="og:type" content="website">
<meta property="og:url" content="<?= htmlspecialchars($canonical) ?>">
<meta property="og:site_name" content="<?= htmlspecialchars(STATUS_SITE_NAME) ?>">
<meta property="og:description" content="<?= htmlspecialchars(STATUS_PAGE_SUBTITLE) ?>">
<link rel="shortcut icon" href="<?= STATUS_FAVICON_ICO ?>">
<link rel="apple-touch-icon" href="<?= STATUS_FAVICON_JPG ?>">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=Share+Tech+Mono&display=swap" rel="stylesheet">
<?php require __DIR__ . '/shared_styles.php'; ?>
</head>
<body>
<div class="page" role="main">
<!-- ── Top bar ─────────────────────────────────────────────────────────── -->
<header class="topbar">
<a class="back-link" href="<?= htmlspecialchars(STATUS_HOME_URL) ?>">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none"
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
<line x1="19" y1="12" x2="5" y2="12"/>
<polyline points="12 19 5 12 12 5"/>
</svg>
<?= htmlspecialchars(STATUS_HOME_LABEL) ?>
</a>
<span class="topbar-badge">
<?= $total ?> update<?= $total !== 1 ? 's' : '' ?>
</span>
</header>
<!-- ── Page heading ────────────────────────────────────────────────────── -->
<div class="page-heading">
<p class="eyebrow">Status &amp; Updates</p>
<h1><?= htmlspecialchars(STATUS_PAGE_TITLE) ?></h1>
<p class="page-subtitle"><?= htmlspecialchars(STATUS_PAGE_SUBTITLE) ?></p>
</div>
<!-- ── Post list ───────────────────────────────────────────────────────── -->
<div class="card">
<?php if (empty($posts)): ?>
<p class="empty-state">No updates yet.</p>
<?php else: ?>
<?php foreach ($posts as $i => $post):
$post_id = htmlspecialchars($post['id'] ?? '');
$post_text = htmlspecialchars($post['text'] ?? '');
$post_date = $post['date'] ?? '';
$post_link = $post['link'] ?? '';
$post_link_label = htmlspecialchars($post['link_label'] ?? $post_link);
$post_tags = (array)($post['tags'] ?? []);
$post_url = htmlspecialchars(STATUS_BASE_URL . '/post.php?id=' . rawurlencode($post['id'] ?? ''));
$is_last = ($i === count($posts) - 1);
?>
<article class="post-item<?= $is_last ? ' post-item-last' : '' ?>"
id="post-<?= $post_id ?>">
<div class="post-meta">
<a class="post-uid" href="<?= $post_url ?>"
title="Permalink to this update">#<?= $post_id ?></a>
<?php if ($post_date): ?>
<time class="post-time" datetime="<?= htmlspecialchars(status_iso($post_date)) ?>"
title="<?= htmlspecialchars(status_abs_date($post_date)) ?>">
<?= htmlspecialchars(status_relative_time($post_date)) ?>
</time>
<span class="post-abs-date"><?= htmlspecialchars(status_abs_date($post_date)) ?></span>
<?php endif; ?>
<?php foreach ($post_tags as $tag): ?>
<a class="post-tag"
href="<?= htmlspecialchars(STATUS_BASE_URL . '/?tag=' . rawurlencode($tag)) ?>"
title="Filter by <?= htmlspecialchars($tag) ?>"><?= htmlspecialchars($tag) ?></a>
<?php endforeach; ?>
</div>
<p class="post-text"><?= $post_text ?></p>
<?php if ($post_link): ?>
<a class="post-link"
href="<?= htmlspecialchars($post_link) ?>"
<?= str_starts_with($post_link, 'http') ? 'target="_blank" rel="noopener noreferrer"' : '' ?>>
<?= $post_link_label ?>
</a>
<?php endif; ?>
<div class="post-footer">
<a class="post-permalink" href="<?= $post_url ?>">
<svg width="11" height="11" viewBox="0 0 24 24" fill="none"
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
<path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/>
<path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/>
</svg>
Permalink
</a>
</div>
</article>
<?php endforeach; ?>
<?php endif; ?>
</div><!-- /card -->
<!-- ── Pagination ──────────────────────────────────────────────────────── -->
<?php if ($total_pages > 1): ?>
<nav class="pagination" aria-label="Page navigation">
<div class="pagination-info">
<?php
$from = $offset + 1;
$to = min($offset + $per_page, $total);
echo "Showing $from$to of $total";
?>
</div>
<div class="pagination-btns">
<!-- Prev -->
<?php if ($page > 1): ?>
<a class="page-btn" href="?page=<?= $page - 1 ?>" rel="prev" aria-label="Previous page"></a>
<?php else: ?>
<span class="page-btn" aria-disabled="true"></span>
<?php endif; ?>
<!-- Page numbers with sliding window -->
<?php
$lo = max(1, $page - 2);
$hi = min($total_pages, $page + 2);
if ($lo > 1): ?><a class="page-btn" href="?page=1">1</a><?php
if ($lo > 2): ?><span class="page-btn page-ellipsis" aria-hidden="true">…</span><?php endif;
endif;
for ($p = $lo; $p <= $hi; $p++):
if ($p === $page): ?><span class="page-btn active" aria-current="page"><?= $p ?></span><?php
else: ?><a class="page-btn" href="?page=<?= $p ?>"><?= $p ?></a><?php
endif;
endfor;
if ($hi < $total_pages):
if ($hi < $total_pages - 1): ?><span class="page-btn page-ellipsis" aria-hidden="true">…</span><?php endif;
?><a class="page-btn" href="?page=<?= $total_pages ?>"><?= $total_pages ?></a><?php
endif;
?>
<!-- Next -->
<?php if ($page < $total_pages): ?>
<a class="page-btn" href="?page=<?= $page + 1 ?>" rel="next" aria-label="Next page"></a>
<?php else: ?>
<span class="page-btn" aria-disabled="true"></span>
<?php endif; ?>
</div>
</nav>
<?php endif; ?>
<footer class="page-foot">
<span class="foot-note"><?= htmlspecialchars(STATUS_SITE_NAME) ?> · status log</span>
<a class="foot-link" href="<?= htmlspecialchars(STATUS_HOME_URL) ?>">← Back to home</a>
</footer>
</div><!-- /page -->
</body>
</html>