vrg-archive/src/components/post.jsx
hiina c5e7bf307a implement the thing
seems to work pretty okay.
2025-04-11 17:13:08 -06:00

147 lines
4.7 KiB
JavaScript

import React from "npm:react";
import DOMPurify from "npm:dompurify";
// thanks to gemini 2.5 for this slop.
// Helper function to format Unix timestamp (seconds)
function formatTimestamp(unixTimestamp) {
if (!unixTimestamp) return "Invalid Date";
try {
const date = new Date(unixTimestamp * 1000);
// Simple format, adjust as needed (e.g., locale, relative time)
return date.toLocaleString(); // Example: "4/10/2025, 10:01:10 PM"
} catch (e) {
console.error("Error formatting timestamp:", unixTimestamp, e);
return "Invalid Date";
}
}
// Helper function to format file size
function formatFileSize(bytes) {
if (bytes === null || typeof bytes !== "number" || isNaN(bytes) || bytes < 0)
return "";
if (bytes === 0) return "0 Bytes";
const k = 1024;
const sizes = ["Bytes", "KB", "MB", "GB", "TB"];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + " " + sizes[i];
}
function FormatComment({ text }) {
if (!text) {
return null;
}
let p = DOMPurify.sanitize(text);
return <span dangerouslySetInnerHTML={{ __html: p }}></span>;
}
// --- The Main Post Component ---
export function Post({ postData }) {
// Destructure with default values for safety
const {
post_num,
thread_num,
thread_title = null, // Only present if it's the OP row from the join
is_op = 0,
timestamp = null,
name = "Anonymous",
trip = null,
capcode = null,
comment = "",
media_filename = null,
media_w = null,
media_h = null,
media_size = null,
media_hash = null, // MD5 hash
media_link = null, // Direct link (may need construction)
thumb_link = null, // Thumbnail link (may need construction)
media_spoiler = 0, // Treat as boolean/int
} = postData || {}; // Handle case where postData might be null/undefined
if (!post_num) {
return <div className="post error">Error: Invalid post data provided.</div>;
}
const isOp = !!is_op; // Convert 0/1 to boolean
const postClass = `post ${isOp ? "op" : "reply"}`;
const postId = `p${post_num}`;
const postInfoId = `pi${post_num}`;
const messageId = `m${post_num}`;
const fileId = `f${post_num}`;
const hasMedia = !!media_filename;
// Construct media links if they are null but filename/hash exists (example for 4cdn)
// Adjust logic based on how links were stored or need to be generated
const effectiveMediaLink =
media_link || (hasMedia ? `https://i.4cdn.org/vg/${media_filename}` : null); // Example construction
const effectiveThumbLink =
thumb_link ||
(hasMedia
? `https://i.4cdn.org/vg/${media_filename.replace(/(\.\w+)$/, "s$1")}`
: null); // Example construction for 's' thumbnail
return (
<div id={postId} className={postClass}>
{/* Post Info Bar */}
<div className="postInfo desktop" id={postInfoId}>
<span className="nameBlock">
<span className="name">{name}</span>
{trip && <span className="trip"> {trip}</span>}
{capcode && capcode !== "N" && (
<span className="capcode"> ## {capcode}</span>
)}{" "}
</span>
<span
className="dateTime"
data-utc={timestamp}
title={formatTimestamp(timestamp)}
>
{formatTimestamp(timestamp)} {/* Display formatted date/time */}
</span>
<span className="postNum desktop">
<a href={`#${postId}`} title="Link to this post">
No. {post_num}
</a>
</span>
</div>
{/* File/Media Section */}
{hasMedia && (
<div className="file" id={fileId}>
<div className="fileText">
File:{" "}
<a href="/observable.jpg" target="_blank" rel="noopener noreferrer">
{media_filename}
</a>
{" ("}
{formatFileSize(media_size)}
{media_w && media_h && `, ${media_w}x${media_h}`}
{")"}
</div>
<a
className="fileThumb"
href="/observable.jpg"
target="_blank"
rel="noopener noreferrer"
>
{/* Basic spoiler handling: maybe blur or hide image based on state */}
<img
src="/observable.png"
alt={formatFileSize(media_size)} // Alt text with file size
data-md5={media_hash || ""} // Store md5 if available
style={{ maxWidth: "250px", maxHeight: "250px" }} // Example sizing
loading="lazy"
/>
</a>
</div>
)}
{/* Post Message/Comment */}
<blockquote className="postMessage" id={messageId}>
<FormatComment text={comment} />
</blockquote>
</div>
);
}