Viewing File: /home/markqprx/iniasli.pro/client/comments/comment-list/comment-list-item.tsx

import React, {Fragment, memo, useContext, useState} from 'react';
import {SiteConfigContext} from '@common/core/settings/site-config-context';
import {Link} from 'react-router-dom';
import {Comment} from '@common/comments/comment';
import {useAuth} from '@common/auth/use-auth';
import {UserAvatar} from '@common/ui/images/user-avatar';
import {Button} from '@common/ui/buttons/button';
import {Trans} from '@common/i18n/trans';
import {NewCommentForm} from '@common/comments/new-comment-form';
import {User} from '@common/auth/user';
import {Commentable} from '@common/comments/commentable';
import {useDeleteComments} from '@common/comments/requests/use-delete-comments';
import {DialogTrigger} from '@common/ui/overlays/dialog/dialog-trigger';
import {queryClient} from '@common/http/query-client';
import {ConfirmationDialog} from '@common/ui/overlays/dialog/confirmation-dialog';
import {FormattedDuration} from '@common/i18n/formatted-duration';
import {useIsMobileMediaQuery} from '@common/utils/hooks/is-mobile-media-query';
import {ThumbButtons} from '@common/votes/thumb-buttons';
import {ReplyIcon} from '@common/icons/material/Reply';
import {MoreVertIcon} from '@common/icons/material/MoreVert';
import {
  Menu,
  MenuItem,
  MenuTrigger,
} from '@common/ui/navigation/menu/menu-trigger';
import {FormattedRelativeTime} from '@common/i18n/formatted-relative-time';
import {useSubmitReport} from '@common/reports/requests/use-submit-report';

interface CommentListItemProps {
  comment: Comment;
  commentable: Commentable;
  canDelete?: boolean;
}
export function CommentListItem({
  comment,
  commentable,
  // user can delete comment if they have created it, or they have relevant permissions on commentable
  canDelete,
}: CommentListItemProps) {
  const isMobile = useIsMobileMediaQuery();
  const {user, hasPermission} = useAuth();
  const [replyFormVisible, setReplyFormVisible] = useState(false);
  const showReplyButton =
    user != null &&
    !comment.deleted &&
    !isMobile &&
    comment.depth < 5 &&
    hasPermission('comments.create');

  return (
    <div
      style={{paddingLeft: `${comment.depth * 20}px`}}
      onClick={() => {
        if (isMobile) {
          setReplyFormVisible(!replyFormVisible);
        }
      }}
    >
      <div className="group flex min-h-70 items-start gap-24 py-18">
        <UserAvatar user={comment.user} size={isMobile ? 'lg' : 'xl'} circle />
        <div className="flex-auto text-sm">
          <div className="mb-4 flex items-center gap-8">
            {comment.user && <UserDisplayName user={comment.user} />}
            <time className="text-xs text-muted">
              <FormattedRelativeTime date={comment.created_at} />
            </time>
            {comment.position ? (
              <Position commentable={commentable} position={comment.position} />
            ) : null}
          </div>
          <div className="whitespace-pre-line">
            {comment.deleted ? (
              <span className="italic text-muted">
                <Trans message="[COMMENT DELETED]" />
              </span>
            ) : (
              comment.content
            )}
          </div>
          {!comment.deleted && (
            <div className="-ml-8 mt-10 flex items-center gap-8">
              {showReplyButton && (
                <Button
                  sizeClassName="text-sm px-8 py-4"
                  startIcon={<ReplyIcon />}
                  onClick={() => setReplyFormVisible(!replyFormVisible)}
                >
                  <Trans message="Reply" />
                </Button>
              )}
              <ThumbButtons model={comment} showUpvotesOnly />
              <CommentOptionsTrigger
                comment={comment}
                canDelete={canDelete}
                user={user}
              />
            </div>
          )}
        </div>
      </div>
      {replyFormVisible ? (
        <NewCommentForm
          className={!comment?.depth ? 'pl-20' : undefined}
          commentable={commentable}
          inReplyTo={comment}
          autoFocus
          onSuccess={() => {
            setReplyFormVisible(false);
          }}
        />
      ) : null}
    </div>
  );
}

interface PositionProps {
  commentable: Commentable;
  position: number;
}
const Position = memo(({commentable, position}: PositionProps) => {
  if (!commentable.duration) return null;
  const seconds = (position / 100) * (commentable.duration / 1000);
  return (
    <span className="text-xs text-muted">
      <Trans
        message="at :position"
        values={{
          position: <FormattedDuration seconds={seconds} />,
        }}
      />
    </span>
  );
});

interface DeleteCommentsButtonProps {
  comment: Comment;
  canDelete?: boolean;
  user: User | null;
}
export function CommentOptionsTrigger({
  comment,
  canDelete,
  user,
}: DeleteCommentsButtonProps) {
  const deleteComments = useDeleteComments();
  const reportComment = useSubmitReport(comment);

  const [isDeleteDialogOpen, setIsDeleteDialogOpen] = useState(false);
  const showDeleteButton =
    (comment.user_id === user?.id || canDelete) && !comment.deleted;

  const handleReport = () => {
    reportComment.mutate({});
  };

  const handleDelete = (isConfirmed: boolean) => {
    setIsDeleteDialogOpen(false);
    if (isConfirmed) {
      deleteComments.mutate(
        {commentIds: [comment.id]},
        {
          onSuccess: () => {
            queryClient.invalidateQueries({queryKey: ['comment']});
          },
        },
      );
    }
  };

  return (
    <Fragment>
      <MenuTrigger>
        <Button startIcon={<MoreVertIcon />} sizeClassName="text-sm px-8 py-4">
          <Trans message="More" />
        </Button>
        <Menu>
          <MenuItem value="report" onSelected={() => handleReport()}>
            <Trans message="Report comment" />
          </MenuItem>
          {showDeleteButton && (
            <MenuItem
              value="delete"
              onSelected={() => setIsDeleteDialogOpen(true)}
            >
              <Trans message="Delete" />
            </MenuItem>
          )}
        </Menu>
      </MenuTrigger>
      <DialogTrigger
        type="modal"
        isOpen={isDeleteDialogOpen}
        onClose={isConfirmed => handleDelete(isConfirmed)}
      >
        <ConfirmationDialog
          isDanger
          title={<Trans message="Delete comment?" />}
          body={
            <Trans message="Are you sure you want to delete this comment?" />
          }
          confirm={<Trans message="Delete" />}
        />
      </DialogTrigger>
    </Fragment>
  );
}

interface UserDisplayNameProps {
  user: User;
}
function UserDisplayName({user}: UserDisplayNameProps) {
  const {auth} = useContext(SiteConfigContext);
  if (auth.getUserProfileLink) {
    return (
      <Link
        to={auth.getUserProfileLink(user)}
        className="text-base font-medium hover:underline"
      >
        {user.display_name}
      </Link>
    );
  }
  return <div className="text-base font-medium">{user.display_name}</div>;
}
Back to Directory File Manager