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"; // mobx store import { useMobxStore } from "lib/mobx/store-provider"; // components import { DeleteArchivedIssueModal, DeleteIssueModal, IssueActivity, IssueUpdateStatus, PeekOverviewIssueDetails, PeekOverviewProperties, } from "components/issues"; // hooks import useOutsideClickDetector from "hooks/use-outside-click-detector"; // ui import { Button, CenterPanelIcon, CustomSelect, FullScreenPanelIcon, SidePanelIcon, Spinner } from "@plane/ui"; // types import { IIssue, IIssueLink, ILinkDetails } from "types"; interface IIssueView { workspaceSlug: string; projectId: string; issueId: string; issue: IIssue | null; 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; const router = useRouter(); const { peekIssueId } = router.query; const { user: { currentUser }, issueDetail: { fetchIssueSubscription, getIssueActivity, getIssueReactions, getIssueSubscription, setPeekId }, } = useMobxStore(); const [peekMode, setPeekMode] = useState("side-peek"); const [deleteIssueModal, setDeleteIssueModal] = useState(false); const [isSubmitting, setIsSubmitting] = useState<"submitting" | "submitted" | "saved">("saved"); // ref const issuePeekOverviewRef = useRef(null); const updateRoutePeekId = () => { if (issueId != peekIssueId) { setPeekId(issueId); const { query } = router; router.push({ pathname: router.pathname, query: { ...query, peekIssueId: issueId, peekProjectId: projectId }, }); } }; const removeRoutePeekId = () => { const { query } = router; if (query.peekIssueId) { setPeekId(null); 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 fetchIssueSubscription(workspaceSlug, projectId, issueId); } } ); const issueReactions = getIssueReactions || []; const issueActivity = getIssueActivity; const issueSubscription = getIssueSubscription || []; const currentMode = PEEK_OPTIONS.find((m) => m.key === peekMode); useOutsideClickDetector(issuePeekOverviewRef, () => removeRoutePeekId()); return ( <> {issue && !isArchived && ( setDeleteIssueModal(false)} data={issue} onSubmit={handleDeleteIssue} /> )} {issue && isArchived && ( setDeleteIssueModal(false)} onSubmit={handleDeleteIssue} /> )}
{children && (
{children}
)} {issueId === peekIssueId && (
{/* header */}
{currentMode && (
setPeekMode(val)} customButton={ } > {PEEK_OPTIONS.map((mode) => (
{mode.title}
))}
)}
{issue?.created_by !== currentUser?.id && !issue?.assignees.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} />
)} ) )}
)}
); });