tahamajs/IE / Projects /front /src /components /threads /ThreadParentBanner.tsx
tahamajs's picture
download
raw
3.47 kB
// src/components/threads/ThreadParentBanner.tsx
import { useState, useEffect } from "react";
import api from "@/lib/api";
import { CornerDownRight, ChevronDown, ChevronUp, Loader2, AlertCircle } from "lucide-react";
import { toast } from "react-toastify";
interface ThreadParentBannerProps {
messageId: number;
threadTitle: string;
}
interface ParentMessage {
body: string;
from: string;
sentAt: string;
}
export function ThreadParentBanner({ messageId, threadTitle }: ThreadParentBannerProps) {
const [parent, setParent] = useState<ParentMessage | null>(null);
const [expanded, setExpanded] = useState(false);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(false);
useEffect(() => {
setLoading(true);
setError(false);
api
.get(`/messages/${messageId}`)
.then((res) => {
setParent(res.data);
setLoading(false);
})
.catch(() => {
setError(true);
setLoading(false);
toast.error("Could not load parent message");
});
}, [messageId]);
if (loading) {
return (
<div className="flex items-center gap-2 border-b border-gray-100 bg-gray-50 px-4 py-2 dark:border-gray-700 dark:bg-gray-800/50">
<Loader2 className="h-4 w-4 animate-spin text-primary" />
<span className="text-xs text-gray-500">Loading parent message…</span>
</div>
);
}
if (error || !parent) {
return (
<div className="flex items-center gap-2 border-b border-gray-100 bg-red-50 px-4 py-2 dark:border-gray-700 dark:bg-red-900/20">
<AlertCircle className="h-4 w-4 text-red-500" />
<span className="text-xs text-red-600 dark:text-red-400">
Could not load parent message
</span>
</div>
);
}
const formattedDate = new Date(parent.sentAt).toLocaleDateString(undefined, {
month: "short",
day: "numeric",
year: "numeric",
});
return (
<div className="border-b border-gray-100 bg-yellow-50/50 dark:border-gray-700 dark:bg-yellow-900/10">
<button
onClick={() => setExpanded(!expanded)}
className="flex w-full items-center gap-2 px-4 py-2 text-left transition hover:bg-yellow-100/50 dark:hover:bg-yellow-900/20"
aria-expanded={expanded}
aria-label={expanded ? "Collapse parent message" : "Expand parent message"}
>
<CornerDownRight className="h-4 w-4 shrink-0 text-yellow-600" />
<div className="min-w-0 flex-1">
<span className="text-xs font-semibold text-yellow-700 dark:text-yellow-400">
{parent.from}
</span>
<span className="ml-2 text-xs text-yellow-600/70 dark:text-yellow-400/70">
{formattedDate}
</span>
<span className="ml-2 text-xs text-gray-400">in thread: {threadTitle}</span>
{!expanded && (
<p className="mt-0.5 truncate text-xs text-gray-500">
{parent.body.substring(0, 80)}…
</p>
)}
</div>
{expanded ? (
<ChevronUp className="h-4 w-4 text-yellow-600" />
) : (
<ChevronDown className="h-4 w-4 text-yellow-600" />
)}
</button>
{expanded && (
<div className="px-4 pb-3">
<p className="whitespace-pre-wrap border-l-2 border-yellow-300 pl-3 text-sm text-gray-700 dark:text-gray-300">
{parent.body}
</p>
</div>
)}
</div>
);
}

Xet Storage Details

Size:
3.47 kB
·
Xet hash:
9da8b20e930efff9a187230999e4cbcb21724c546006b5cd8b6e8d8e8052b92e

Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.