From 963eeb0a8c2d7fb3c5d0bd64c2fb47aa39784906 Mon Sep 17 00:00:00 2001 From: Dakshesh Jain Date: Thu, 17 Aug 2023 13:57:21 +0530 Subject: [PATCH] refactor: only using runInAction for assignment refactor: - using single types instead of lite version, - isLoading logic for label store --- .../components/core/filters/filters-list.tsx | 4 +- apps/app/components/issues/select/label.tsx | 4 +- .../labels/create-update-label-inline.tsx | 4 +- .../components/labels/delete-label-modal.tsx | 4 +- .../components/labels/labels-list-modal.tsx | 6 +- .../components/labels/single-label-group.tsx | 14 +-- apps/app/components/labels/single-label.tsx | 8 +- .../components/tiptap/bubble-menu/index.tsx | 24 +++--- .../projects/[projectId]/settings/labels.tsx | 12 +-- apps/app/store/label.ts | 85 +++++++++++-------- apps/app/types/index.d.ts | 2 +- apps/app/types/issues.d.ts | 30 ------- apps/app/types/labels.d.ts | 22 +++++ 13 files changed, 111 insertions(+), 108 deletions(-) create mode 100644 apps/app/types/labels.d.ts diff --git a/apps/app/components/core/filters/filters-list.tsx b/apps/app/components/core/filters/filters-list.tsx index 038441d18..1de569ea2 100644 --- a/apps/app/components/core/filters/filters-list.tsx +++ b/apps/app/components/core/filters/filters-list.tsx @@ -10,7 +10,7 @@ import { replaceUnderscoreIfSnakeCase } from "helpers/string.helper"; // helpers import { renderShortDateWithYearFormat } from "helpers/date-time.helper"; // types -import { IIssueFilterOptions, IState, IUserLite, TStateGroups, LabelLite } from "types"; +import { IIssueFilterOptions, IState, IUserLite, TStateGroups, IIssueLabels } from "types"; // constants import { STATE_GROUP_COLORS } from "constants/state"; @@ -18,7 +18,7 @@ type Props = { filters: Partial; setFilters: (updatedFilter: Partial) => void; clearAllFilters: (...args: any) => void; - labels: LabelLite[] | undefined; + labels: IIssueLabels[] | undefined; members: IUserLite[] | undefined; states: IState[] | undefined; }; diff --git a/apps/app/components/issues/select/label.tsx b/apps/app/components/issues/select/label.tsx index 5a333e69b..9e18508ba 100644 --- a/apps/app/components/issues/select/label.tsx +++ b/apps/app/components/issues/select/label.tsx @@ -18,7 +18,7 @@ import { TagIcon, } from "@heroicons/react/24/outline"; // types -import type { LabelLite } from "types"; +import type { IIssueLabels } from "types"; type Props = { setIsOpen: React.Dispatch>; @@ -42,7 +42,7 @@ export const IssueLabelSelect: React.FC = observer( if (workspaceSlug && projectId) loadLabels(workspaceSlug.toString(), projectId); }, [workspaceSlug, projectId, loadLabels]); - const filteredOptions: LabelLite[] = labels?.filter((l) => + const filteredOptions: IIssueLabels[] = labels?.filter((l) => l.name.toLowerCase().includes(query.toLowerCase()) ); diff --git a/apps/app/components/labels/create-update-label-inline.tsx b/apps/app/components/labels/create-update-label-inline.tsx index 6bc61bc0b..a1284abee 100644 --- a/apps/app/components/labels/create-update-label-inline.tsx +++ b/apps/app/components/labels/create-update-label-inline.tsx @@ -19,7 +19,7 @@ import { Input, PrimaryButton, SecondaryButton } from "components/ui"; // icons import { ChevronDownIcon } from "@heroicons/react/24/outline"; // types -import { IIssueLabels, LabelLite } from "types"; +import { IIssueLabels } from "types"; // fetch-keys import { getRandomLabelColor, LABEL_COLOR_OPTIONS } from "constants/label"; @@ -27,7 +27,7 @@ type Props = { labelForm: boolean; setLabelForm: React.Dispatch>; isUpdating: boolean; - labelToUpdate: LabelLite | null; + labelToUpdate: IIssueLabels | null; onClose?: () => void; }; diff --git a/apps/app/components/labels/delete-label-modal.tsx b/apps/app/components/labels/delete-label-modal.tsx index f6bc7861e..2bfe4db96 100644 --- a/apps/app/components/labels/delete-label-modal.tsx +++ b/apps/app/components/labels/delete-label-modal.tsx @@ -15,12 +15,12 @@ import useToast from "hooks/use-toast"; // ui import { DangerButton, SecondaryButton } from "components/ui"; // types -import type { ICurrentUserResponse, LabelLite } from "types"; +import type { ICurrentUserResponse, IIssueLabels } from "types"; type Props = { isOpen: boolean; onClose: () => void; - data: LabelLite | null; + data: IIssueLabels | null; user: ICurrentUserResponse | undefined; }; diff --git a/apps/app/components/labels/labels-list-modal.tsx b/apps/app/components/labels/labels-list-modal.tsx index e255d3e32..348c65664 100644 --- a/apps/app/components/labels/labels-list-modal.tsx +++ b/apps/app/components/labels/labels-list-modal.tsx @@ -11,12 +11,12 @@ import { Combobox, Dialog, Transition } from "@headlessui/react"; // icons import { RectangleStackIcon, MagnifyingGlassIcon } from "@heroicons/react/24/outline"; // types -import { ICurrentUserResponse, LabelLite } from "types"; +import { ICurrentUserResponse, IIssueLabels } from "types"; type Props = { isOpen: boolean; handleClose: () => void; - parent: LabelLite | undefined; + parent: IIssueLabels | undefined; user: ICurrentUserResponse | undefined; }; @@ -37,7 +37,7 @@ export const LabelsListModal: React.FC = observer( setQuery(""); }; - const addChildLabel = async (label: LabelLite) => { + const addChildLabel = async (label: IIssueLabels) => { if (!workspaceSlug || !projectId || !user) return; updateLabel( diff --git a/apps/app/components/labels/single-label-group.tsx b/apps/app/components/labels/single-label-group.tsx index 2d8eecd2b..f0c7346c0 100644 --- a/apps/app/components/labels/single-label-group.tsx +++ b/apps/app/components/labels/single-label-group.tsx @@ -20,14 +20,14 @@ import { TrashIcon, } from "@heroicons/react/24/outline"; // types -import { ICurrentUserResponse, LabelLite } from "types"; +import { ICurrentUserResponse, IIssueLabels } from "types"; type Props = { - label: LabelLite; - labelChildren: LabelLite[]; - addLabelToGroup: (parentLabel: LabelLite) => void; - editLabel: (label: LabelLite) => void; - handleLabelDelete: (label: LabelLite) => void; + label: IIssueLabels; + labelChildren: IIssueLabels[]; + addLabelToGroup: (parentLabel: IIssueLabels) => void; + editLabel: (label: IIssueLabels) => void; + handleLabelDelete: (label: IIssueLabels) => void; user: ICurrentUserResponse | undefined; }; @@ -39,7 +39,7 @@ export const SingleLabelGroup: React.FC = observer( const { label: labelStore } = useMobxStore(); const { updateLabel } = labelStore; - const removeFromGroup = (label: LabelLite) => { + const removeFromGroup = (label: IIssueLabels) => { if (!workspaceSlug || !projectId || !user) return; updateLabel( diff --git a/apps/app/components/labels/single-label.tsx b/apps/app/components/labels/single-label.tsx index f376c29d7..15fcf896d 100644 --- a/apps/app/components/labels/single-label.tsx +++ b/apps/app/components/labels/single-label.tsx @@ -3,14 +3,14 @@ import React from "react"; // ui import { CustomMenu } from "components/ui"; // types -import { LabelLite } from "types"; +import { IIssueLabels } from "types"; //icons import { RectangleGroupIcon, PencilIcon, TrashIcon } from "@heroicons/react/24/outline"; type Props = { - label: LabelLite; - addLabelToGroup: (parentLabel: LabelLite) => void; - editLabel: (label: LabelLite) => void; + label: IIssueLabels; + addLabelToGroup: (parentLabel: IIssueLabels) => void; + editLabel: (label: IIssueLabels) => void; handleLabelDelete: () => void; }; diff --git a/apps/app/components/tiptap/bubble-menu/index.tsx b/apps/app/components/tiptap/bubble-menu/index.tsx index 259b5ecea..339553309 100644 --- a/apps/app/components/tiptap/bubble-menu/index.tsx +++ b/apps/app/components/tiptap/bubble-menu/index.tsx @@ -19,32 +19,32 @@ export const EditorBubbleMenu: FC = (props) => { const items: BubbleMenuItem[] = [ { name: "bold", - isActive: () => props.editor.isActive("bold"), - command: () => props.editor.chain().focus().toggleBold().run(), + isActive: () => props?.editor?.isActive("bold")!, + command: () => props?.editor?.chain().focus().toggleBold().run()!, icon: BoldIcon, }, { name: "italic", - isActive: () => props.editor.isActive("italic"), - command: () => props.editor.chain().focus().toggleItalic().run(), + isActive: () => props?.editor?.isActive("italic")!, + command: () => props?.editor?.chain().focus().toggleItalic().run()!, icon: ItalicIcon, }, { name: "underline", - isActive: () => props.editor.isActive("underline"), - command: () => props.editor.chain().focus().toggleUnderline().run(), + isActive: () => props?.editor?.isActive("underline")!, + command: () => props?.editor?.chain().focus().toggleUnderline().run()!, icon: UnderlineIcon, }, { name: "strike", - isActive: () => props.editor.isActive("strike"), - command: () => props.editor.chain().focus().toggleStrike().run(), + isActive: () => props?.editor?.isActive("strike")!, + command: () => props?.editor?.chain().focus().toggleStrike().run()!, icon: StrikethroughIcon, }, { name: "code", - isActive: () => props.editor.isActive("code"), - command: () => props.editor.chain().focus().toggleCode().run(), + isActive: () => props?.editor?.isActive("code")!, + command: () => props?.editor?.chain().focus().toggleCode().run()!, icon: CodeIcon, }, ]; @@ -78,7 +78,7 @@ export const EditorBubbleMenu: FC = (props) => { className="flex w-fit divide-x divide-custom-border-300 rounded border border-custom-border-300 bg-custom-background-100 shadow-xl" > { setIsNodeSelectorOpen(!isNodeSelectorOpen); @@ -86,7 +86,7 @@ export const EditorBubbleMenu: FC = (props) => { }} /> { setIsLinkSelectorOpen(!isLinkSelectorOpen); diff --git a/apps/app/pages/[workspaceSlug]/projects/[projectId]/settings/labels.tsx b/apps/app/pages/[workspaceSlug]/projects/[projectId]/settings/labels.tsx index f7ee6ee81..8a2c59090 100644 --- a/apps/app/pages/[workspaceSlug]/projects/[projectId]/settings/labels.tsx +++ b/apps/app/pages/[workspaceSlug]/projects/[projectId]/settings/labels.tsx @@ -31,7 +31,7 @@ import { PlusIcon } from "@heroicons/react/24/outline"; // images import emptyLabel from "public/empty-state/label.svg"; // types -import { LabelLite } from "types"; +import { IIssueLabels } from "types"; import type { NextPage } from "next"; // fetch-keys import { PROJECT_DETAILS } from "constants/fetch-keys"; @@ -44,14 +44,14 @@ const LabelsSettings: NextPage = () => { // edit label const [isUpdating, setIsUpdating] = useState(false); - const [labelToUpdate, setLabelToUpdate] = useState(null); + const [labelToUpdate, setLabelToUpdate] = useState(null); // labels list modal const [labelsListModal, setLabelsListModal] = useState(false); - const [parentLabel, setParentLabel] = useState(undefined); + const [parentLabel, setParentLabel] = useState(undefined); // delete label - const [selectDeleteLabel, setSelectDeleteLabel] = useState(null); + const [selectDeleteLabel, setSelectDeleteLabel] = useState(null); const router = useRouter(); const { workspaceSlug, projectId } = router.query; @@ -79,12 +79,12 @@ const LabelsSettings: NextPage = () => { setLabelForm(true); }; - const addLabelToGroup = (parentLabel: LabelLite) => { + const addLabelToGroup = (parentLabel: IIssueLabels) => { setLabelsListModal(true); setParentLabel(parentLabel); }; - const editLabel = (label: LabelLite) => { + const editLabel = (label: IIssueLabels) => { setLabelForm(true); setIsUpdating(true); setLabelToUpdate(label); diff --git a/apps/app/store/label.ts b/apps/app/store/label.ts index d13882cd8..143f43aae 100644 --- a/apps/app/store/label.ts +++ b/apps/app/store/label.ts @@ -5,10 +5,10 @@ import { action, observable, runInAction, makeAutoObservable } from "mobx"; import issueService from "services/issues.service"; // types -import type { IIssueLabels, LabelLite, ICurrentUserResponse, LabelForm } from "types"; +import type { IIssueLabels, ICurrentUserResponse, LabelForm } from "types"; class LabelStore { - labels: LabelLite[] = []; + labels: IIssueLabels[] = []; isLabelsLoading: boolean = false; rootStore: any | null = null; @@ -30,24 +30,29 @@ class LabelStore { */ loadLabels = async (workspaceSlug: string, projectId: string) => { - this.isLabelsLoading = true; + this.isLabelsLoading = this.labels.length === 0; try { const labelsResponse: IIssueLabels[] = await issueService.getIssueLabels( workspaceSlug, projectId ); + + const _labels = [...(labelsResponse || [])].map((label) => ({ + id: label.id, + name: label.name, + description: label.description, + color: label.color, + parent: label.parent, + })); + runInAction(() => { - this.labels = labelsResponse.map((label) => ({ - id: label.id, - name: label.name, - description: label.description, - color: label.color, - parent: label.parent, - })); + this.labels = _labels; this.isLabelsLoading = false; }); } catch (error) { - this.isLabelsLoading = false; + runInAction(() => { + this.isLabelsLoading = false; + }); console.error("Fetching labels error", error); } }; @@ -59,11 +64,11 @@ class LabelStore { /** * For provided query, this function returns all labels that contain query in their name from the labels store. * @param query - query string - * @returns {LabelLite[]} array of labels that contain query in their name + * @returns {IIssueLabels[]} array of labels that contain query in their name * @example * getFilteredLabels("labe") // [{ id: "1", name: "label1", description: "", color: "", parent: null }] */ - getFilteredLabels = (query: string): LabelLite[] => + getFilteredLabels = (query: string): IIssueLabels[] => this.labels.filter((label) => label.name.includes(query)); createLabel = async ( @@ -80,18 +85,19 @@ class LabelStore { user ); + const _label = [ + ...this.labels, + { + id: labelResponse.id, + name: labelResponse.name, + description: labelResponse.description, + color: labelResponse.color, + parent: labelResponse.parent, + }, + ].sort((a, b) => a.name.localeCompare(b.name)); + runInAction(() => { - this.labels = [ - ...this.labels, - { - id: labelResponse.id, - name: labelResponse.name, - description: labelResponse.description, - color: labelResponse.color, - parent: labelResponse.parent, - }, - ]; - this.labels.sort((a, b) => a.name.localeCompare(b.name)); + this.labels = _label; }); return labelResponse; } catch (error) { @@ -116,18 +122,20 @@ class LabelStore { user ); - const _labels = [...this.labels].map((label) => { - if (label.id === labelId) { - return { - id: labelResponse.id, - name: labelResponse.name, - description: labelResponse.description, - color: labelResponse.color, - parent: labelResponse.parent, - }; - } - return label; - }); + const _labels = [...this.labels] + .map((label) => { + if (label.id === labelId) { + return { + id: labelResponse.id, + name: labelResponse.name, + description: labelResponse.description, + color: labelResponse.color, + parent: labelResponse.parent, + }; + } + return label; + }) + .sort((a, b) => a.name.localeCompare(b.name)); runInAction(() => { this.labels = _labels; @@ -146,8 +154,11 @@ class LabelStore { ) => { try { issueService.deleteIssueLabel(workspaceSlug, projectId, labelId, user); + + const _labels = [...this.labels].filter((label) => label.id !== labelId); + runInAction(() => { - this.labels = this.labels.filter((label) => label.id !== labelId); + this.labels = _labels; }); } catch (error) { console.error("Deleting label error", error); diff --git a/apps/app/types/index.d.ts b/apps/app/types/index.d.ts index 8c9592917..e27962cd3 100644 --- a/apps/app/types/index.d.ts +++ b/apps/app/types/index.d.ts @@ -18,7 +18,7 @@ export * from "./calendar"; export * from "./notifications"; export * from "./waitlist"; export * from "./reaction"; - +export * from "./labels"; export type NestedKeyOf = { [Key in keyof ObjectType & (string | number)]: ObjectType[Key] extends object diff --git a/apps/app/types/issues.d.ts b/apps/app/types/issues.d.ts index 4f89e67a6..720c468d9 100644 --- a/apps/app/types/issues.d.ts +++ b/apps/app/types/issues.d.ts @@ -148,36 +148,6 @@ export type IssuePriorities = { user: string; }; -export interface IIssueLabels { - id: string; - created_at: Date; - updated_at: Date; - name: string; - description: string; - color: string; - created_by: string; - updated_by: string; - project: string; - project_detail: IProjectLite; - workspace: string; - workspace_detail: IWorkspaceLite; - parent: string | null; -} - -export interface LabelForm { - name: string; - description: string; - color: string; - parent: string | null; -} - -/** - * @description Issue label's lite version - */ -export interface LabelLite extends LabelForm { - id: string; -} - export interface IIssueActivity { actor: string; actor_detail: IUserLite; diff --git a/apps/app/types/labels.d.ts b/apps/app/types/labels.d.ts new file mode 100644 index 000000000..77118bc02 --- /dev/null +++ b/apps/app/types/labels.d.ts @@ -0,0 +1,22 @@ +export interface IIssueLabels { + id: string; + created_at?: Date; + updated_at?: Date; + name: string; + description: string; + color: string; + created_by?: string; + updated_by?: string; + project?: string; + project_detail?: IProjectLite; + workspace?: string; + workspace_detail?: IWorkspaceLite; + parent: string | null; +} + +export interface LabelForm { + name: string; + description: string; + color: string; + parent: string | null; +}