From 4fe5c468be1fe168c8d16fd11eef8c5ea1e8cabb Mon Sep 17 00:00:00 2001 From: Aaryan Khandelwal Date: Mon, 20 Jan 2025 16:58:01 +0530 Subject: [PATCH 1/2] fix: stickies bugs --- web/core/components/stickies/delete-modal.tsx | 8 +- .../stickies/layout/stickies-list.tsx | 15 +- .../stickies/layout/stickies-loader.tsx | 45 ++++ .../stickies/layout/sticky-dnd-wrapper.tsx | 215 +++++++++--------- .../components/stickies/modal/stickies.tsx | 13 +- web/core/components/stickies/sticky/root.tsx | 8 +- 6 files changed, 174 insertions(+), 130 deletions(-) create mode 100644 web/core/components/stickies/layout/stickies-loader.tsx diff --git a/web/core/components/stickies/delete-modal.tsx b/web/core/components/stickies/delete-modal.tsx index 2d26a53dedc..c810f426841 100644 --- a/web/core/components/stickies/delete-modal.tsx +++ b/web/core/components/stickies/delete-modal.tsx @@ -7,7 +7,7 @@ import { AlertModalCore, TOAST_TYPE, setToast } from "@plane/ui"; interface IStickyDelete { isOpen: boolean; - handleSubmit: () => void; + handleSubmit: () => Promise; handleClose: () => void; } @@ -20,11 +20,11 @@ export const StickyDeleteModal: React.FC = observer((props) => { try { setLoader(true); await handleSubmit(); - } catch (error) { + } catch { setToast({ type: TOAST_TYPE.ERROR, title: "Warning!", - message: "Something went wrong please try again later.", + message: "Something went wrong. Please try again later.", }); } finally { setLoader(false); @@ -38,7 +38,7 @@ export const StickyDeleteModal: React.FC = observer((props) => { isSubmitting={loader} isOpen={isOpen} title="Delete sticky" - content={<>Are you sure you want to delete the sticky? } + content="Are you sure you want to delete the sticky?" /> ); }); diff --git a/web/core/components/stickies/layout/stickies-list.tsx b/web/core/components/stickies/layout/stickies-list.tsx index 4da6efe7b03..c238dbee315 100644 --- a/web/core/components/stickies/layout/stickies-list.tsx +++ b/web/core/components/stickies/layout/stickies-list.tsx @@ -7,18 +7,17 @@ import type { ElementDragPayload } from "@atlaskit/pragmatic-drag-and-drop/eleme import { observer } from "mobx-react"; import { usePathname } from "next/navigation"; import Masonry from "react-masonry-component"; -// plane ui -import { Loader } from "@plane/ui"; // components import { EmptyState } from "@/components/empty-state"; +import { StickiesEmptyState } from "@/components/home/widgets/empty-states/stickies"; // constants import { EmptyStateType } from "@/constants/empty-state"; // hooks import { useSticky } from "@/hooks/use-stickies"; import { useStickyOperations } from "../sticky/use-operations"; +import { StickiesLoader } from "./stickies-loader"; import { StickyDNDWrapper } from "./sticky-dnd-wrapper"; import { getInstructionFromPayload } from "./sticky.helpers"; -import { StickiesEmptyState } from "@/components/home/widgets/empty-states/stickies"; type TStickiesLayout = { workspaceSlug: string; @@ -72,19 +71,13 @@ export const StickiesList = observer((props: TProps) => { }; if (loader === "init-loader") { - return ( -
- - - -
- ); + return ; } if (loader === "loaded" && workspaceStickyIds.length === 0) { return (
- {isStickiesPage ? ( + {isStickiesPage || searchQuery ? ( ( +
+ {Array.from({ length: 4 }).map((_, index) => ( + +
+ + +
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
+
+ + + +
+ +
+
+ ))} +
+); diff --git a/web/core/components/stickies/layout/sticky-dnd-wrapper.tsx b/web/core/components/stickies/layout/sticky-dnd-wrapper.tsx index bc775e06de3..1945b538403 100644 --- a/web/core/components/stickies/layout/sticky-dnd-wrapper.tsx +++ b/web/core/components/stickies/layout/sticky-dnd-wrapper.tsx @@ -15,123 +15,124 @@ import { attachInstruction } from "@atlaskit/pragmatic-drag-and-drop-hitbox/tree import { observer } from "mobx-react"; import { usePathname } from "next/navigation"; import { createRoot } from "react-dom/client"; +// plane types import { InstructionType } from "@plane/types"; +// plane ui import { DropIndicator } from "@plane/ui"; +// plane utils import { cn } from "@plane/utils"; +// components import { StickyNote } from "../sticky"; +// helpers import { getInstructionFromPayload } from "./sticky.helpers"; -// Draggable Sticky Wrapper Component -export const StickyDNDWrapper = observer( - ({ - stickyId, - workspaceSlug, - itemWidth, - isLastChild, - isInFirstRow, - isInLastRow, - handleDrop, - }: { - stickyId: string; - workspaceSlug: string; - itemWidth: string; - isLastChild: boolean; - isInFirstRow: boolean; - isInLastRow: boolean; - handleDrop: (self: DropTargetRecord, source: ElementDragPayload, location: DragLocationHistory) => void; - }) => { - const pathName = usePathname(); - const [isDragging, setIsDragging] = useState(false); - const [instruction, setInstruction] = useState(undefined); - const elementRef = useRef(null); +type Props = { + stickyId: string; + workspaceSlug: string; + itemWidth: string; + isLastChild: boolean; + isInFirstRow: boolean; + isInLastRow: boolean; + handleDrop: (self: DropTargetRecord, source: ElementDragPayload, location: DragLocationHistory) => void; +}; - useEffect(() => { - const element = elementRef.current; - if (!element) return; +export const StickyDNDWrapper = observer((props: Props) => { + const { stickyId, workspaceSlug, itemWidth, isLastChild, isInFirstRow, isInLastRow, handleDrop } = props; + // states + const [isDragging, setIsDragging] = useState(false); + const [instruction, setInstruction] = useState(undefined); + // refs + const elementRef = useRef(null); + // navigation + const pathname = usePathname(); - const initialData = { id: stickyId, type: "sticky" }; + useEffect(() => { + const element = elementRef.current; + if (!element) return; - if (pathName.includes("stickies")) - return combine( - draggable({ - element, - dragHandle: element, - getInitialData: () => initialData, - onDragStart: () => { - setIsDragging(true); - }, - onDrop: () => { - setIsDragging(false); - }, - onGenerateDragPreview: ({ nativeSetDragImage }) => { - setCustomNativeDragPreview({ - getOffset: pointerOutsideOfPreview({ x: "-200px", y: "0px" }), - render: ({ container }) => { - const root = createRoot(container); - root.render( -
-
- -
+ const initialData = { id: stickyId, type: "sticky" }; + + if (pathname.includes("stickies")) + return combine( + draggable({ + element, + dragHandle: element, + getInitialData: () => initialData, + onDragStart: () => { + setIsDragging(true); + }, + onDrop: () => { + setIsDragging(false); + }, + onGenerateDragPreview: ({ nativeSetDragImage }) => { + setCustomNativeDragPreview({ + getOffset: pointerOutsideOfPreview({ x: "-200px", y: "0px" }), + render: ({ container }) => { + const root = createRoot(container); + root.render( +
+
+
- ); - return () => root.unmount(); - }, - nativeSetDragImage, - }); - }, - }), - dropTargetForElements({ - element, - canDrop: ({ source }) => source.data?.type === "sticky", - getData: ({ input, element }) => { - const blockedStates: InstructionType[] = ["make-child"]; - if (!isLastChild) { - blockedStates.push("reorder-below"); - } +
+ ); + return () => root.unmount(); + }, + nativeSetDragImage, + }); + }, + }), + dropTargetForElements({ + element, + canDrop: ({ source }) => source.data?.type === "sticky", + getData: ({ input, element }) => { + const blockedStates: InstructionType[] = ["make-child"]; + if (!isLastChild) { + blockedStates.push("reorder-below"); + } - return attachInstruction(initialData, { - input, - element, - currentLevel: 1, - indentPerLevel: 0, - mode: isLastChild ? "last-in-group" : "standard", - block: blockedStates, - }); - }, - onDrag: ({ self, source, location }) => { - const instruction = getInstructionFromPayload(self, source, location); - setInstruction(instruction); - }, - onDragLeave: () => { - setInstruction(undefined); - }, - onDrop: ({ self, source, location }) => { - setInstruction(undefined); - handleDrop(self, source, location); - }, - }) - ); - }, [stickyId, isDragging]); + return attachInstruction(initialData, { + input, + element, + currentLevel: 1, + indentPerLevel: 0, + mode: isLastChild ? "last-in-group" : "standard", + block: blockedStates, + }); + }, + onDrag: ({ self, source, location }) => { + const instruction = getInstructionFromPayload(self, source, location); + setInstruction(instruction); + }, + onDragLeave: () => { + setInstruction(undefined); + }, + onDrop: ({ self, source, location }) => { + setInstruction(undefined); + handleDrop(self, source, location); + }, + }) + ); + }, [handleDrop, isDragging, isLastChild, pathname, stickyId, workspaceSlug]); - return ( -
- {!isInFirstRow && } -
- -
- {!isInLastRow && } + return ( +
+ {!isInFirstRow && } +
+
- ); - } -); + {!isInLastRow && } +
+ ); +}); diff --git a/web/core/components/stickies/modal/stickies.tsx b/web/core/components/stickies/modal/stickies.tsx index a1203c017f0..832073ddc13 100644 --- a/web/core/components/stickies/modal/stickies.tsx +++ b/web/core/components/stickies/modal/stickies.tsx @@ -1,8 +1,11 @@ import { observer } from "mobx-react"; import { useParams } from "next/navigation"; import { Plus, X } from "lucide-react"; +// plane ui import { RecentStickyIcon } from "@plane/ui"; +// hooks import { useSticky } from "@/hooks/use-stickies"; +// components import { StickiesTruncated } from "../layout/stickies-truncated"; import { useStickyOperations } from "../sticky/use-operations"; import { StickySearch } from "./search"; @@ -13,8 +16,11 @@ type TProps = { export const Stickies = observer((props: TProps) => { const { handleClose } = props; + // navigation const { workspaceSlug } = useParams(); + // store hooks const { creatingSticky, toggleShowNewSticky } = useSticky(); + // sticky operations const { stickyOperations } = useStickyOperations({ workspaceSlug: workspaceSlug?.toString() }); return ( @@ -22,9 +28,9 @@ export const Stickies = observer((props: TProps) => { {/* header */}
{/* Title */} -
- -

Your stickies

+
+ +

Your stickies

{/* actions */}
@@ -50,6 +56,7 @@ export const Stickies = observer((props: TProps) => { {handleClose && (