Post.jsx 9.49 KiB
import React, { useEffect } from "react";
import { useInView } from "react-intersection-observer";
import classNames from "classnames";
import { format, isToday } from "date-fns";
import Chip from "components/Chip";
import { DropdownMenu, DropdownMenuItem } from "components/dropdown-menu";
import PostScore from "components/posts/PostScore";
import Thumbs from "components/Thumbs";
const Post = ({
className,
datetime,
author,
type,
ranking,
content,
modified,
seen,
archived,
state,
dimIfArchived = true,
currentUser,
supportThreshold,
canThumb,
onLike,
onDislike,
onHide,
onBanUser,
onUnbanUser,
onAnnounceProcedureProposal,
onAcceptProcedureProposal,
onRejectProcedureProposal,
onRejectProcedureProposalByChairman,
onEdit,
onArchive,
onSeen,
}) => {
const { ref, inView } = useInView({
threshold: 0.8,
trackVisibility: true,
delay: 1500,
skip: seen,
triggerOnce: true,
});
useEffect(() => {
if (!seen && inView && onSeen) {
onSeen();
}
});
const wrapperClassName = classNames(
"flex items-start p-4 lg:p-2 lg:py-3 lg:-mx-2 transition duration-500",
{
"bg-yellow-100 bg-opacity-50": !seen,
"opacity-25 hover:opacity-100": dimIfArchived && !!archived,
},
className
);
const labels = [];
if (type === "post") {
labels.push(
<Chip
key="type__post"
condensed
color="grey-125"
className="text-grey-300"
>
Příspěvek
</Chip>
);
}
if (type === "procedure-proposal") {
labels.push(
<Chip key="type__procedure-proposal" condensed color="cyan-200">
Návrh postupu
</Chip>
);
labels.push(
{
pending: (
<Chip
key="state__pending"
condensed
color="grey-500"
title="Návrh čekající na zpracování"
>
Čeká na zpracování
</Chip>
),
announced: (
<Chip
key="state__announced"
condensed
color="blue-300"
title="Návrh k hlasování"
>
K hlasování
</Chip>
),
accepted: (
<Chip
key="state__accepted"
condensed
color="green-400"
title="Schválený návrh"
>
Schválený
</Chip>
),
rejected: (
<Chip
key="state__rejected"
condensed
color="red-600"
title="Zamítnutý návrh"
>
Zamítnutý
</Chip>
),
"rejected-by-chairman": (
<Chip
key="state__rejected-by-chairmen"
condensed
color="red-600"
title="Návrh zamítnutý předsedajícím"
>
Zamítnutý předs.
</Chip>
),
}[state]
);
}
if (archived) {
labels.push(
<Chip
key="isArchived"
condensed
color="grey-125"
className="text-grey-300"
>
Archivovaný
</Chip>
);
}
const isChairman = currentUser && currentUser.role === "chairman";
const showAnnounceAction =
isChairman && type === "procedure-proposal" && state === "pending";
const showAcceptAction =
isChairman && type === "procedure-proposal" && state === "announced";
const showRejectAction =
isChairman && type === "procedure-proposal" && state === "announced";
const showRejectByChairmanAction =
isChairman &&
type === "procedure-proposal" &&
["announced", "pending"].includes(state);
const showEditAction =
isChairman ||
(currentUser && currentUser.id === author.id && !currentUser.isBanned);
const showBanAction = isChairman && !author.isBanned;
const showUnbanAction = isChairman && author.isBanned;
const showHideAction = isChairman && !archived;
const showArchiveAction = isChairman && !archived;
// Show actions dropdown if any of actions is available.
const showActions = [
showAnnounceAction,
showAcceptAction,
showRejectAction,
showRejectByChairmanAction,
showEditAction,
showBanAction,
showUnbanAction,
showHideAction,
showArchiveAction,
].some((item) => !!item);
const htmlContent = {
__html: content,
};
const thumbsVisible = !archived && (type === "post" || state === "announced");
return (
<div className={wrapperClassName} ref={ref}>
<img
src={`https://a.pirati.cz/piratar/200/${author.username}.jpg`}
className="w-8 h-8 lg:w-14 lg:h-14 rounded mr-3 object-cover"
alt={author.name}
/>
{/* overflow-hidden to avoid spammers damaging the UI */}
<div className="flex-1 overflow-hidden">
<div className="mb-1">
<div className="flex justify-between items-start xl:items-center">
<div className="flex flex-col xl:flex-row xl:items-center">
<div className="flex flex-col xl:flex-row xl:items-center">
<span className="font-bold">{author.name}</span>
<div className="mt-1 xl:mt-0 xl:ml-2 leading-tight">
<span className="text-grey-200 text-xs sm:text-sm">
{author.group}
</span>
<span className="text-grey-200 ml-1 text-xs sm:text-sm">
@{" "}
{format(
datetime,
isToday(datetime) ? "H:mm" : "dd. MM. H:mm"
)}
{modified && (
<span className="text-grey-200 text-xs block md:inline md:ml-2">
(upraveno)
</span>
)}
</span>
</div>
</div>
<div className="hidden lg:flex flex-row flex-wrap lg:flex-no-wrap lg:items-center mt-1 xl:mt-0 xl:ml-2 space-x-2">
{labels}
</div>
</div>
<div className="flex items-center">
{thumbsVisible && (
<Thumbs
likes={ranking.likes}
dislikes={ranking.dislikes}
readOnly={!canThumb}
onLike={onLike}
onDislike={onDislike}
myVote={ranking.myVote}
/>
)}
<PostScore
className="ml-2"
score={ranking.score}
hasDislikes={ranking.dislikes > 0}
rankingReadonly={!thumbsVisible}
supportThreshold={supportThreshold}
/>
{showActions && (
<DropdownMenu right className="pl-4">
{showAnnounceAction && (
<DropdownMenuItem
onClick={onAnnounceProcedureProposal}
icon="ico--bullhorn"
title="Vyhlásit procedurální návrh"
/>
)}
{showAcceptAction && (
<DropdownMenuItem
onClick={onAcceptProcedureProposal}
icon="ico--thumbs-up"
title="Schválit procedurální návrh"
/>
)}
{showRejectAction && (
<DropdownMenuItem
onClick={onRejectProcedureProposal}
icon="ico--thumbs-down"
title="Zamítnout procedurální návrh"
/>
)}
{showRejectByChairmanAction && (
<DropdownMenuItem
onClick={onRejectProcedureProposalByChairman}
icon="ico--thumbs-down"
title="Zamítnout procedurální návrh předsedajícím"
/>
)}
{showEditAction && (
<DropdownMenuItem
onClick={onEdit}
icon="ico--pencil"
title="Upravit příspěvek"
/>
)}
{showBanAction && (
<DropdownMenuItem
onClick={onBanUser}
icon="ico--lock"
title="Zablokovat uživatele"
/>
)}
{showUnbanAction && (
<DropdownMenuItem
onClick={onUnbanUser}
icon="ico--lock-open"
title="Odblokovat uživatele"
/>
)}
{showHideAction && (
<DropdownMenuItem
onClick={onHide}
icon="ico--eye-off"
title="Skrýt příspěvek"
/>
)}
{showArchiveAction && (
<DropdownMenuItem
onClick={onArchive}
icon="ico--drawer"
title="Archivovat příspěvek"
/>
)}
</DropdownMenu>
)}
</div>
</div>
</div>
<div className="flex lg:hidden flex-row flex-wrap my-2 space-x-2">
{labels}
</div>
<div
className="text-sm lg:text-base text-black leading-normal content-block overflow-x-scroll"
dangerouslySetInnerHTML={htmlContent}
></div>
</div>
</div>
);
};
export default Post;