import { FC, ReactNode, useRef, useState } from "react"; import { useRouter } from "next/router"; import { observer } from "mobx-react-lite"; import useSWR from "swr"; import { MoveRight, MoveDiagonal, Bell, Link2, Trash2 } from "lucide-react"; // hooks import { useIssueDetail, useUser } from "hooks/store"; // components import { DeleteArchivedIssueModal, DeleteIssueModal, IssueActivity, IssueUpdateStatus, PeekOverviewIssueDetails, PeekOverviewProperties, } from "components/issues"; // ui import { Button, CenterPanelIcon, CustomSelect, FullScreenPanelIcon, SidePanelIcon, Spinner } from "@plane/ui"; // types import { TIssue, IIssueLink, ILinkDetails } from "@plane/types"; import useOutsideClickDetector from "hooks/use-outside-click-detector"; interface IIssueView { workspaceSlug: string; projectId: string; issueId: string; issue: TIssue | undefined; isLoading?: boolean; isArchived?: boolean; handleCopyText: (e: React.MouseEvent) => void; redirectToIssueDetail: () => void; issueUpdate: (issue: Partial) => void; issueReactionCreate: (reaction: string) => void; issueReactionRemove: (reaction: string) => void; issueCommentCreate: (comment: any) => void; issueCommentUpdate: (comment: any) => void; issueCommentRemove: (commentId: string) => void; issueCommentReactionCreate: (commentId: string, reaction: string) => void; issueCommentReactionRemove: (commentId: string, reaction: string) => void; issueSubscriptionCreate: () => void; issueSubscriptionRemove: () => void; issueLinkCreate: (formData: IIssueLink) => Promise; issueLinkUpdate: (formData: IIssueLink, linkId: string) => Promise; issueLinkDelete: (linkId: string) => Promise; handleDeleteIssue: () => Promise; children: ReactNode; disableUserActions?: boolean; showCommentAccessSpecifier?: boolean; } type TPeekModes = "side-peek" | "modal" | "full-screen"; const PEEK_OPTIONS: { key: TPeekModes; icon: any; title: string }[] = [ { key: "side-peek", icon: SidePanelIcon, title: "Side Peek", }, { key: "modal", icon: CenterPanelIcon, title: "Modal", }, { key: "full-screen", icon: FullScreenPanelIcon, title: "Full Screen", }, ]; export const IssueView: FC = observer((props) => { const { workspaceSlug, projectId, issueId, issue, isLoading, isArchived, handleCopyText, redirectToIssueDetail, issueUpdate, issueReactionCreate, issueReactionRemove, issueCommentCreate, issueCommentUpdate, issueCommentRemove, issueCommentReactionCreate, issueCommentReactionRemove, issueSubscriptionCreate, issueSubscriptionRemove, issueLinkCreate, issueLinkUpdate, issueLinkDelete, handleDeleteIssue, children, disableUserActions = false, showCommentAccessSpecifier = false, } = props; // states const [peekMode, setPeekMode] = useState("side-peek"); const [isSubmitting, setIsSubmitting] = useState<"submitting" | "submitted" | "saved">("saved"); // ref const issuePeekOverviewRef = useRef(null); // router const router = useRouter(); const { peekIssueId } = router.query; // store hooks const { fetchSubscriptions, activity, reaction, subscription, setIssueId, isAnyModalOpen, isDeleteIssueModalOpen, toggleDeleteIssueModal, } = useIssueDetail(); const { currentUser } = useUser(); const updateRoutePeekId = () => { if (issueId != peekIssueId) { setIssueId(issueId); const { query } = router; router.push({ pathname: router.pathname, query: { ...query, peekIssueId: issueId, peekProjectId: projectId }, }); } }; const removeRoutePeekId = () => { const { query } = router; if (query.peekIssueId) { setIssueId(undefined); delete query.peekIssueId; delete query.peekProjectId; router.push({ pathname: router.pathname, query: { ...query }, }); } }; useSWR( workspaceSlug && projectId && issueId && peekIssueId && issueId === peekIssueId ? `ISSUE_PEEK_OVERVIEW_SUBSCRIPTION_${workspaceSlug}_${projectId}_${peekIssueId}` : null, async () => { if (workspaceSlug && projectId && issueId && peekIssueId && issueId === peekIssueId) { await fetchSubscriptions(workspaceSlug, projectId, issueId); } } ); const issueReactions = reaction.getReactionsByIssueId(issueId) || []; const issueActivity = activity.getActivitiesByIssueId(issueId); const issueSubscription = subscription.getSubscriptionByIssueId(issueId) || {}; const currentMode = PEEK_OPTIONS.find((m) => m.key === peekMode); useOutsideClickDetector(issuePeekOverviewRef, () => !isAnyModalOpen && removeRoutePeekId()); return ( <> {issue && !isArchived && ( toggleDeleteIssueModal(false)} data={issue} onSubmit={handleDeleteIssue} /> )} {issue && isArchived && ( toggleDeleteIssueModal(false)} onSubmit={handleDeleteIssue} /> )}
{children && (
{children}
)} {issueId === peekIssueId && (
{/* header */}
{currentMode && (
setPeekMode(val)} customButton={ } > {PEEK_OPTIONS.map((mode) => (
{mode.title}
))}
)}
{issue?.created_by !== currentUser?.id && !issue?.assignee_ids.includes(currentUser?.id ?? "") && !router.pathname.includes("[archivedIssueId]") && ( )} {!disableUserActions && ( )}
{/* content */}
{isLoading && !issue ? (
) : ( issue && ( <> {["side-peek", "modal"].includes(peekMode) ? (
{isArchived && (
)} setIsSubmitting(value)} isSubmitting={isSubmitting} workspaceSlug={workspaceSlug} issue={issue} issueUpdate={issueUpdate} issueReactions={issueReactions} user={currentUser} issueReactionCreate={issueReactionCreate} issueReactionRemove={issueReactionRemove} />
) : (
setIsSubmitting(value)} isSubmitting={isSubmitting} workspaceSlug={workspaceSlug} issue={issue} issueReactions={issueReactions} issueUpdate={issueUpdate} user={currentUser} issueReactionCreate={issueReactionCreate} issueReactionRemove={issueReactionRemove} />
)} ) )}
)}
); });