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, reportSeen = true, onLike, onDislike, onHide, onBanUser, onUnbanUser, onInviteUser, onAnnounceProcedureProposal, onAcceptProcedureProposal, onRejectProcedureProposal, onRejectProcedureProposalByChairman, onEdit, onArchive, onSeen, ...props }) => { const { ref, inView } = useInView({ threshold: 0.8, trackVisibility: true, delay: 1000, skip: !reportSeen, triggerOnce: true, }); useEffect(() => { if (inView && onSeen) { onSeen(); } }, [inView, 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" aria-label="Návrh čekající na zpracování" > Čeká na zpracování </Chip> ), announced: ( <Chip key="state__announced" condensed color="blue-300" aria-label="Návrh k hlasování" > K hlasování </Chip> ), accepted: ( <Chip key="state__accepted" condensed color="green-400" aria-label="Schválený návrh" > Schválený </Chip> ), rejected: ( <Chip key="state__rejected" condensed color="red-600" aria-label="Zamítnutý návrh" > Zamítnutý </Chip> ), "rejected-by-chairman": ( <Chip key="state__rejected-by-chairmen" condensed color="red-600" aria-label="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 showInviteAction = isChairman; 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, showInviteAction, showHideAction, showArchiveAction, ].some((item) => !!item); const htmlContent = { __html: content, }; const thumbsVisible = !archived && (type === "post" || state === "announced"); return ( <div className={wrapperClassName} ref={ref} {...props}> <img src={`https://a.pirati.cz/piratar/200/${author.username}.jpg`} className="w-8 h-8 lg:w-14 lg:h-14 mr-3 rounded object-cover" alt={author.name} /> <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" postType={type} ranking={ranking} rankingReadonly={!thumbsVisible} supportThreshold={supportThreshold} /> {showActions && ( <DropdownMenu right className="pl-4 static"> {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" /> )} {showInviteAction && ( <DropdownMenuItem onClick={onInviteUser} icon="ico--phone" title="Pozvat uživatele do Jitsi" /> )} {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-auto overflow-y-hidden mt-1" dangerouslySetInnerHTML={htmlContent} ></div> </div> </div> ); }; export default React.memo(Post);