mirror of
https://github.com/makeplane/plane
synced 2024-06-14 14:31:34 +00:00
[WEB-1325] chore: refactor inbox issue store to avoid data loss. (#4640)
* [WEB-1325] chore: refactor inbox issue store to avoid data loss. * chore: inbox store improvement.
This commit is contained in:
parent
bf4f97d7f6
commit
0a105a1c21
@ -52,7 +52,7 @@ export const InboxIssueActionsHeader: FC<TInboxIssueActionsHeader> = observer((p
|
|||||||
const [declineIssueModal, setDeclineIssueModal] = useState(false);
|
const [declineIssueModal, setDeclineIssueModal] = useState(false);
|
||||||
const [deleteIssueModal, setDeleteIssueModal] = useState(false);
|
const [deleteIssueModal, setDeleteIssueModal] = useState(false);
|
||||||
// store
|
// store
|
||||||
const { currentTab, deleteInboxIssue, inboxIssuesArray } = useProjectInbox();
|
const { currentTab, deleteInboxIssue, inboxIssueIds } = useProjectInbox();
|
||||||
const { data: currentUser } = useUser();
|
const { data: currentUser } = useUser();
|
||||||
const {
|
const {
|
||||||
membership: { currentProjectRole },
|
membership: { currentProjectRole },
|
||||||
@ -76,11 +76,11 @@ export const InboxIssueActionsHeader: FC<TInboxIssueActionsHeader> = observer((p
|
|||||||
|
|
||||||
const redirectIssue = (): string | undefined => {
|
const redirectIssue = (): string | undefined => {
|
||||||
let nextOrPreviousIssueId: string | undefined = undefined;
|
let nextOrPreviousIssueId: string | undefined = undefined;
|
||||||
const currentIssueIndex = inboxIssuesArray.findIndex((i) => i.issue.id === currentInboxIssueId);
|
const currentIssueIndex = inboxIssueIds.findIndex((id) => id === currentInboxIssueId);
|
||||||
if (inboxIssuesArray[currentIssueIndex + 1])
|
if (inboxIssueIds[currentIssueIndex + 1])
|
||||||
nextOrPreviousIssueId = inboxIssuesArray[currentIssueIndex + 1].issue.id;
|
nextOrPreviousIssueId = inboxIssueIds[currentIssueIndex + 1];
|
||||||
else if (inboxIssuesArray[currentIssueIndex - 1])
|
else if (inboxIssueIds[currentIssueIndex - 1])
|
||||||
nextOrPreviousIssueId = inboxIssuesArray[currentIssueIndex - 1].issue.id;
|
nextOrPreviousIssueId = inboxIssueIds[currentIssueIndex - 1];
|
||||||
else nextOrPreviousIssueId = undefined;
|
else nextOrPreviousIssueId = undefined;
|
||||||
return nextOrPreviousIssueId;
|
return nextOrPreviousIssueId;
|
||||||
};
|
};
|
||||||
@ -134,22 +134,22 @@ export const InboxIssueActionsHeader: FC<TInboxIssueActionsHeader> = observer((p
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
const currentIssueIndex = inboxIssuesArray.findIndex((issue) => issue.issue.id === currentInboxIssueId) ?? 0;
|
const currentIssueIndex = inboxIssueIds.findIndex((issueId) => issueId === currentInboxIssueId) ?? 0;
|
||||||
|
|
||||||
const handleInboxIssueNavigation = useCallback(
|
const handleInboxIssueNavigation = useCallback(
|
||||||
(direction: "next" | "prev") => {
|
(direction: "next" | "prev") => {
|
||||||
if (!inboxIssuesArray || !currentInboxIssueId) return;
|
if (!inboxIssueIds || !currentInboxIssueId) return;
|
||||||
const activeElement = document.activeElement as HTMLElement;
|
const activeElement = document.activeElement as HTMLElement;
|
||||||
if (activeElement && (activeElement.classList.contains("tiptap") || activeElement.id === "title-input")) return;
|
if (activeElement && (activeElement.classList.contains("tiptap") || activeElement.id === "title-input")) return;
|
||||||
const nextIssueIndex =
|
const nextIssueIndex =
|
||||||
direction === "next"
|
direction === "next"
|
||||||
? (currentIssueIndex + 1) % inboxIssuesArray.length
|
? (currentIssueIndex + 1) % inboxIssueIds.length
|
||||||
: (currentIssueIndex - 1 + inboxIssuesArray.length) % inboxIssuesArray.length;
|
: (currentIssueIndex - 1 + inboxIssueIds.length) % inboxIssueIds.length;
|
||||||
const nextIssueId = inboxIssuesArray[nextIssueIndex].issue.id;
|
const nextIssueId = inboxIssueIds[nextIssueIndex];
|
||||||
if (!nextIssueId) return;
|
if (!nextIssueId) return;
|
||||||
router.push(`/${workspaceSlug}/projects/${projectId}/inbox?inboxIssueId=${nextIssueId}`);
|
router.push(`/${workspaceSlug}/projects/${projectId}/inbox?inboxIssueId=${nextIssueId}`);
|
||||||
},
|
},
|
||||||
[currentInboxIssueId, currentIssueIndex, inboxIssuesArray, projectId, router, workspaceSlug]
|
[currentInboxIssueId, currentIssueIndex, inboxIssueIds, projectId, router, workspaceSlug]
|
||||||
);
|
);
|
||||||
|
|
||||||
const onKeyDown = useCallback(
|
const onKeyDown = useCallback(
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { FC, useState } from "react";
|
import { FC, useEffect, useState } from "react";
|
||||||
import { observer } from "mobx-react";
|
import { observer } from "mobx-react";
|
||||||
|
import { useRouter } from "next/router";
|
||||||
import useSWR from "swr";
|
import useSWR from "swr";
|
||||||
import { InboxIssueActionsHeader, InboxIssueMainContent } from "@/components/inbox";
|
import { InboxIssueActionsHeader, InboxIssueMainContent } from "@/components/inbox";
|
||||||
import { EUserProjectRoles } from "@/constants/project";
|
import { EUserProjectRoles } from "@/constants/project";
|
||||||
@ -15,14 +16,25 @@ type TInboxContentRoot = {
|
|||||||
|
|
||||||
export const InboxContentRoot: FC<TInboxContentRoot> = observer((props) => {
|
export const InboxContentRoot: FC<TInboxContentRoot> = observer((props) => {
|
||||||
const { workspaceSlug, projectId, inboxIssueId, isMobileSidebar, setIsMobileSidebar } = props;
|
const { workspaceSlug, projectId, inboxIssueId, isMobileSidebar, setIsMobileSidebar } = props;
|
||||||
|
/// router
|
||||||
|
const router = useRouter();
|
||||||
// states
|
// states
|
||||||
const [isSubmitting, setIsSubmitting] = useState<"submitting" | "submitted" | "saved">("saved");
|
const [isSubmitting, setIsSubmitting] = useState<"submitting" | "submitted" | "saved">("saved");
|
||||||
// hooks
|
// hooks
|
||||||
const { fetchInboxIssueById, getIssueInboxByIssueId } = useProjectInbox();
|
const { currentTab, fetchInboxIssueById, getIssueInboxByIssueId, getIsIssueAvailable } = useProjectInbox();
|
||||||
const inboxIssue = getIssueInboxByIssueId(inboxIssueId);
|
const inboxIssue = getIssueInboxByIssueId(inboxIssueId);
|
||||||
const {
|
const {
|
||||||
membership: { currentProjectRole },
|
membership: { currentProjectRole },
|
||||||
} = useUser();
|
} = useUser();
|
||||||
|
// derived values
|
||||||
|
const isIssueAvailable = getIsIssueAvailable(inboxIssueId?.toString() || "");
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!isIssueAvailable && inboxIssueId) {
|
||||||
|
router.replace(`/${workspaceSlug}/projects/${projectId}/inbox?currentTab=${currentTab}`);
|
||||||
|
}
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [isIssueAvailable]);
|
||||||
|
|
||||||
useSWR(
|
useSWR(
|
||||||
workspaceSlug && projectId && inboxIssueId
|
workspaceSlug && projectId && inboxIssueId
|
||||||
|
@ -12,31 +12,30 @@ import { renderFormattedDate } from "@/helpers/date-time.helper";
|
|||||||
// hooks
|
// hooks
|
||||||
import { useLabel, useMember, useProjectInbox } from "@/hooks/store";
|
import { useLabel, useMember, useProjectInbox } from "@/hooks/store";
|
||||||
import { usePlatformOS } from "@/hooks/use-platform-os";
|
import { usePlatformOS } from "@/hooks/use-platform-os";
|
||||||
// store
|
|
||||||
import { IInboxIssueStore } from "@/store/inbox/inbox-issue.store";
|
|
||||||
|
|
||||||
type InboxIssueListItemProps = {
|
type InboxIssueListItemProps = {
|
||||||
workspaceSlug: string;
|
workspaceSlug: string;
|
||||||
projectId: string;
|
projectId: string;
|
||||||
projectIdentifier?: string;
|
projectIdentifier?: string;
|
||||||
inboxIssue: IInboxIssueStore;
|
inboxIssueId: string;
|
||||||
setIsMobileSidebar: (value: boolean) => void;
|
setIsMobileSidebar: (value: boolean) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const InboxIssueListItem: FC<InboxIssueListItemProps> = observer((props) => {
|
export const InboxIssueListItem: FC<InboxIssueListItemProps> = observer((props) => {
|
||||||
const { workspaceSlug, projectId, inboxIssue, projectIdentifier, setIsMobileSidebar } = props;
|
const { workspaceSlug, projectId, inboxIssueId, projectIdentifier, setIsMobileSidebar } = props;
|
||||||
// router
|
// router
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { inboxIssueId } = router.query;
|
const { inboxIssueId: selectedInboxIssueId } = router.query;
|
||||||
// store
|
// store
|
||||||
const { currentTab } = useProjectInbox();
|
const { currentTab, getIssueInboxByIssueId } = useProjectInbox();
|
||||||
const { projectLabels } = useLabel();
|
const { projectLabels } = useLabel();
|
||||||
const { isMobile } = usePlatformOS();
|
const { isMobile } = usePlatformOS();
|
||||||
const { getUserDetails } = useMember();
|
const { getUserDetails } = useMember();
|
||||||
const issue = inboxIssue.issue;
|
const inboxIssue = getIssueInboxByIssueId(inboxIssueId);
|
||||||
|
const issue = inboxIssue?.issue;
|
||||||
|
|
||||||
const handleIssueRedirection = (event: MouseEvent, currentIssueId: string | undefined) => {
|
const handleIssueRedirection = (event: MouseEvent, currentIssueId: string | undefined) => {
|
||||||
if (inboxIssueId === currentIssueId) event.preventDefault();
|
if (selectedInboxIssueId === currentIssueId) event.preventDefault();
|
||||||
setIsMobileSidebar(false);
|
setIsMobileSidebar(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -55,7 +54,7 @@ export const InboxIssueListItem: FC<InboxIssueListItemProps> = observer((props)
|
|||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
`flex flex-col gap-2 relative border border-t-transparent border-l-transparent border-r-transparent border-b-custom-border-200 p-4 hover:bg-custom-primary/5 cursor-pointer transition-all`,
|
`flex flex-col gap-2 relative border border-t-transparent border-l-transparent border-r-transparent border-b-custom-border-200 p-4 hover:bg-custom-primary/5 cursor-pointer transition-all`,
|
||||||
{ "border-custom-primary-100 border": inboxIssueId === issue.id }
|
{ "border-custom-primary-100 border": selectedInboxIssueId === issue.id }
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<div className="space-y-1">
|
<div className="space-y-1">
|
||||||
|
@ -2,30 +2,28 @@ import { FC, Fragment } from "react";
|
|||||||
import { observer } from "mobx-react";
|
import { observer } from "mobx-react";
|
||||||
// components
|
// components
|
||||||
import { InboxIssueListItem } from "@/components/inbox";
|
import { InboxIssueListItem } from "@/components/inbox";
|
||||||
// store
|
|
||||||
import { IInboxIssueStore } from "@/store/inbox/inbox-issue.store";
|
|
||||||
|
|
||||||
export type InboxIssueListProps = {
|
export type InboxIssueListProps = {
|
||||||
workspaceSlug: string;
|
workspaceSlug: string;
|
||||||
projectId: string;
|
projectId: string;
|
||||||
projectIdentifier?: string;
|
projectIdentifier?: string;
|
||||||
inboxIssues: IInboxIssueStore[];
|
inboxIssueIds: string[];
|
||||||
setIsMobileSidebar: (value: boolean) => void;
|
setIsMobileSidebar: (value: boolean) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const InboxIssueList: FC<InboxIssueListProps> = observer((props) => {
|
export const InboxIssueList: FC<InboxIssueListProps> = observer((props) => {
|
||||||
const { workspaceSlug, projectId, projectIdentifier, inboxIssues, setIsMobileSidebar } = props;
|
const { workspaceSlug, projectId, projectIdentifier, inboxIssueIds, setIsMobileSidebar } = props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{inboxIssues.map((inboxIssue) => (
|
{inboxIssueIds.map((inboxIssueId) => (
|
||||||
<Fragment key={inboxIssue.id}>
|
<Fragment key={inboxIssueId}>
|
||||||
<InboxIssueListItem
|
<InboxIssueListItem
|
||||||
setIsMobileSidebar={setIsMobileSidebar}
|
setIsMobileSidebar={setIsMobileSidebar}
|
||||||
workspaceSlug={workspaceSlug}
|
workspaceSlug={workspaceSlug}
|
||||||
projectId={projectId}
|
projectId={projectId}
|
||||||
projectIdentifier={projectIdentifier}
|
projectIdentifier={projectIdentifier}
|
||||||
inboxIssue={inboxIssue}
|
inboxIssueId={inboxIssueId}
|
||||||
/>
|
/>
|
||||||
</Fragment>
|
</Fragment>
|
||||||
))}
|
))}
|
||||||
|
@ -44,7 +44,7 @@ export const InboxSidebar: FC<IInboxSidebarProps> = observer((props) => {
|
|||||||
currentTab,
|
currentTab,
|
||||||
handleCurrentTab,
|
handleCurrentTab,
|
||||||
loader,
|
loader,
|
||||||
inboxIssuesArray,
|
inboxIssueIds,
|
||||||
inboxIssuePaginationInfo,
|
inboxIssuePaginationInfo,
|
||||||
fetchInboxPaginationIssues,
|
fetchInboxPaginationIssues,
|
||||||
getAppliedFiltersCount,
|
getAppliedFiltersCount,
|
||||||
@ -56,13 +56,9 @@ export const InboxSidebar: FC<IInboxSidebarProps> = observer((props) => {
|
|||||||
if (!workspaceSlug || !projectId) return;
|
if (!workspaceSlug || !projectId) return;
|
||||||
fetchInboxPaginationIssues(workspaceSlug.toString(), projectId.toString());
|
fetchInboxPaginationIssues(workspaceSlug.toString(), projectId.toString());
|
||||||
}, [workspaceSlug, projectId, fetchInboxPaginationIssues]);
|
}, [workspaceSlug, projectId, fetchInboxPaginationIssues]);
|
||||||
|
|
||||||
// page observer
|
// page observer
|
||||||
useIntersectionObserver({
|
useIntersectionObserver(containerRef, elementRef, fetchNextPages, "20%");
|
||||||
containerRef,
|
|
||||||
elementRef,
|
|
||||||
callback: fetchNextPages,
|
|
||||||
rootMargin: "20%",
|
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="bg-custom-background-100 flex-shrink-0 w-full h-full border-r border-custom-border-300 ">
|
<div className="bg-custom-background-100 flex-shrink-0 w-full h-full border-r border-custom-border-300 ">
|
||||||
@ -108,13 +104,13 @@ export const InboxSidebar: FC<IInboxSidebarProps> = observer((props) => {
|
|||||||
className="w-full h-full overflow-hidden overflow-y-auto vertical-scrollbar scrollbar-md"
|
className="w-full h-full overflow-hidden overflow-y-auto vertical-scrollbar scrollbar-md"
|
||||||
ref={containerRef}
|
ref={containerRef}
|
||||||
>
|
>
|
||||||
{inboxIssuesArray.length > 0 ? (
|
{inboxIssueIds.length > 0 ? (
|
||||||
<InboxIssueList
|
<InboxIssueList
|
||||||
setIsMobileSidebar={setIsMobileSidebar}
|
setIsMobileSidebar={setIsMobileSidebar}
|
||||||
workspaceSlug={workspaceSlug}
|
workspaceSlug={workspaceSlug}
|
||||||
projectId={projectId}
|
projectId={projectId}
|
||||||
projectIdentifier={currentProjectDetails?.identifier}
|
projectIdentifier={currentProjectDetails?.identifier}
|
||||||
inboxIssues={inboxIssuesArray}
|
inboxIssueIds={inboxIssueIds}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<div className="flex items-center justify-center h-full w-full">
|
<div className="flex items-center justify-center h-full w-full">
|
||||||
@ -130,15 +126,14 @@ export const InboxSidebar: FC<IInboxSidebarProps> = observer((props) => {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
{inboxIssuePaginationInfo?.next_page_results && (
|
||||||
<div ref={elementRef}>
|
<div ref={elementRef}>
|
||||||
{inboxIssuePaginationInfo?.next_page_results && (
|
|
||||||
<Loader className="mx-auto w-full space-y-4 py-4 px-2">
|
<Loader className="mx-auto w-full space-y-4 py-4 px-2">
|
||||||
<Loader.Item height="64px" width="w-100" />
|
<Loader.Item height="64px" width="w-100" />
|
||||||
<Loader.Item height="64px" width="w-100" />
|
<Loader.Item height="64px" width="w-100" />
|
||||||
</Loader>
|
</Loader>
|
||||||
)}
|
</div>
|
||||||
</div>
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { RefObject, useState, useEffect } from "react";
|
import { RefObject, useEffect } from "react";
|
||||||
|
|
||||||
export type UseIntersectionObserverProps = {
|
export type UseIntersectionObserverProps = {
|
||||||
containerRef: RefObject<HTMLDivElement>;
|
containerRef: RefObject<HTMLDivElement>;
|
||||||
@ -7,18 +7,19 @@ export type UseIntersectionObserverProps = {
|
|||||||
rootMargin?: string;
|
rootMargin?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useIntersectionObserver = (props: UseIntersectionObserverProps) => {
|
export const useIntersectionObserver = (
|
||||||
const { containerRef, elementRef, callback, rootMargin = "0px" } = props;
|
containerRef: RefObject<HTMLDivElement>,
|
||||||
const [isVisible, setVisibility] = useState(false);
|
elementRef: RefObject<HTMLDivElement>,
|
||||||
|
callback: () => void,
|
||||||
|
rootMargin?: string
|
||||||
|
) => {
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (elementRef.current) {
|
if (elementRef.current) {
|
||||||
const observer = new IntersectionObserver(
|
const observer = new IntersectionObserver(
|
||||||
([entry]) => {
|
(entries) => {
|
||||||
if (entry.isIntersecting) {
|
if (entries[entries.length - 1].isIntersecting) {
|
||||||
callback();
|
callback();
|
||||||
}
|
}
|
||||||
setVisibility(entry.isIntersecting);
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
root: containerRef.current,
|
root: containerRef.current,
|
||||||
@ -37,6 +38,4 @@ export const useIntersectionObserver = (props: UseIntersectionObserverProps) =>
|
|||||||
// fix this eslint warning with caution
|
// fix this eslint warning with caution
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [rootMargin, callback, elementRef.current, containerRef.current]);
|
}, [rootMargin, callback, elementRef.current, containerRef.current]);
|
||||||
|
|
||||||
return isVisible;
|
|
||||||
};
|
};
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import { uniq, update } from "lodash";
|
||||||
import isEmpty from "lodash/isEmpty";
|
import isEmpty from "lodash/isEmpty";
|
||||||
import omit from "lodash/omit";
|
import omit from "lodash/omit";
|
||||||
import orderBy from "lodash/orderBy";
|
import orderBy from "lodash/orderBy";
|
||||||
@ -38,11 +39,13 @@ export interface IProjectInboxStore {
|
|||||||
inboxSorting: Partial<TInboxIssueSorting>;
|
inboxSorting: Partial<TInboxIssueSorting>;
|
||||||
inboxIssuePaginationInfo: TInboxIssuePaginationInfo | undefined;
|
inboxIssuePaginationInfo: TInboxIssuePaginationInfo | undefined;
|
||||||
inboxIssues: Record<string, IInboxIssueStore>; // issue_id -> IInboxIssueStore
|
inboxIssues: Record<string, IInboxIssueStore>; // issue_id -> IInboxIssueStore
|
||||||
|
inboxIssueIds: string[];
|
||||||
// computed
|
// computed
|
||||||
getAppliedFiltersCount: number;
|
getAppliedFiltersCount: number;
|
||||||
inboxIssuesArray: IInboxIssueStore[];
|
// computed functions
|
||||||
// helper actions
|
|
||||||
getIssueInboxByIssueId: (issueId: string) => IInboxIssueStore;
|
getIssueInboxByIssueId: (issueId: string) => IInboxIssueStore;
|
||||||
|
getIsIssueAvailable: (inboxIssueId: string) => boolean;
|
||||||
|
// helper actions
|
||||||
inboxIssueSorting: (issues: IInboxIssueStore[]) => IInboxIssueStore[];
|
inboxIssueSorting: (issues: IInboxIssueStore[]) => IInboxIssueStore[];
|
||||||
inboxIssueQueryParams: (
|
inboxIssueQueryParams: (
|
||||||
inboxFilters: Partial<TInboxIssueFilter>,
|
inboxFilters: Partial<TInboxIssueFilter>,
|
||||||
@ -50,6 +53,7 @@ export interface IProjectInboxStore {
|
|||||||
pagePerCount: number,
|
pagePerCount: number,
|
||||||
paginationCursor: string
|
paginationCursor: string
|
||||||
) => Partial<Record<keyof TInboxIssueFilter, string>>;
|
) => Partial<Record<keyof TInboxIssueFilter, string>>;
|
||||||
|
createOrUpdateInboxIssue: (inboxIssues: TInboxIssue[], workspaceSlug: string, projectId: string) => void;
|
||||||
// actions
|
// actions
|
||||||
handleCurrentTab: (tab: TInboxIssueCurrentTab) => void;
|
handleCurrentTab: (tab: TInboxIssueCurrentTab) => void;
|
||||||
handleInboxIssueFilters: <T extends keyof TInboxIssueFilter>(key: T, value: TInboxIssueFilter[T]) => void; // if user sends me undefined, I will remove the value from the filter key
|
handleInboxIssueFilters: <T extends keyof TInboxIssueFilter>(key: T, value: TInboxIssueFilter[T]) => void; // if user sends me undefined, I will remove the value from the filter key
|
||||||
@ -82,6 +86,7 @@ export class ProjectInboxStore implements IProjectInboxStore {
|
|||||||
};
|
};
|
||||||
inboxIssuePaginationInfo: TInboxIssuePaginationInfo | undefined = undefined;
|
inboxIssuePaginationInfo: TInboxIssuePaginationInfo | undefined = undefined;
|
||||||
inboxIssues: Record<string, IInboxIssueStore> = {};
|
inboxIssues: Record<string, IInboxIssueStore> = {};
|
||||||
|
inboxIssueIds: string[] = [];
|
||||||
// services
|
// services
|
||||||
inboxIssueService;
|
inboxIssueService;
|
||||||
|
|
||||||
@ -95,9 +100,9 @@ export class ProjectInboxStore implements IProjectInboxStore {
|
|||||||
inboxSorting: observable,
|
inboxSorting: observable,
|
||||||
inboxIssuePaginationInfo: observable,
|
inboxIssuePaginationInfo: observable,
|
||||||
inboxIssues: observable,
|
inboxIssues: observable,
|
||||||
|
inboxIssueIds: observable,
|
||||||
// computed
|
// computed
|
||||||
getAppliedFiltersCount: computed,
|
getAppliedFiltersCount: computed,
|
||||||
inboxIssuesArray: computed,
|
|
||||||
// actions
|
// actions
|
||||||
handleInboxIssueFilters: action,
|
handleInboxIssueFilters: action,
|
||||||
handleInboxIssueSorting: action,
|
handleInboxIssueSorting: action,
|
||||||
@ -122,19 +127,13 @@ export class ProjectInboxStore implements IProjectInboxStore {
|
|||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
get inboxIssuesArray() {
|
|
||||||
let appliedFilters =
|
|
||||||
this.currentTab === EInboxIssueCurrentTab.OPEN
|
|
||||||
? [EInboxIssueStatus.PENDING, EInboxIssueStatus.SNOOZED]
|
|
||||||
: [EInboxIssueStatus.ACCEPTED, EInboxIssueStatus.DECLINED, EInboxIssueStatus.DUPLICATE];
|
|
||||||
appliedFilters = appliedFilters.filter((filter) => this.inboxFilters?.status?.includes(filter));
|
|
||||||
return this.inboxIssueSorting(
|
|
||||||
Object.values(this.inboxIssues || {}).filter((inbox) => appliedFilters.includes(inbox.status))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
getIssueInboxByIssueId = computedFn((issueId: string) => this.inboxIssues?.[issueId]);
|
getIssueInboxByIssueId = computedFn((issueId: string) => this.inboxIssues?.[issueId]);
|
||||||
|
|
||||||
|
getIsIssueAvailable = computedFn((inboxIssueId: string) => {
|
||||||
|
if (!this.inboxIssueIds) return true;
|
||||||
|
return this.inboxIssueIds.includes(inboxIssueId);
|
||||||
|
});
|
||||||
|
|
||||||
// helpers
|
// helpers
|
||||||
inboxIssueSorting = (issues: IInboxIssueStore[]) => {
|
inboxIssueSorting = (issues: IInboxIssueStore[]) => {
|
||||||
let inboxIssues: IInboxIssueStore[] = issues;
|
let inboxIssues: IInboxIssueStore[] = issues;
|
||||||
@ -210,32 +209,55 @@ export class ProjectInboxStore implements IProjectInboxStore {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
createOrUpdateInboxIssue = (inboxIssues: TInboxIssue[], workspaceSlug: string, projectId: string) => {
|
||||||
|
if (inboxIssues && inboxIssues.length > 0) {
|
||||||
|
inboxIssues.forEach((inbox: TInboxIssue) => {
|
||||||
|
const inboxIssueDetail = this.getIssueInboxByIssueId(inbox?.issue?.id);
|
||||||
|
if (inboxIssueDetail)
|
||||||
|
update(this.inboxIssues, [inbox?.issue?.id], (existingInboxIssue) => ({
|
||||||
|
...existingInboxIssue,
|
||||||
|
...inbox,
|
||||||
|
issue: {
|
||||||
|
...existingInboxIssue?.issue,
|
||||||
|
...inbox?.issue,
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
else
|
||||||
|
set(this.inboxIssues, [inbox?.issue?.id], new InboxIssueStore(workspaceSlug, projectId, inbox, this.store));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// actions
|
// actions
|
||||||
handleCurrentTab = (tab: TInboxIssueCurrentTab) => {
|
handleCurrentTab = (tab: TInboxIssueCurrentTab) => {
|
||||||
set(this, "currentTab", tab);
|
runInAction(() => {
|
||||||
set(this, "inboxFilters", undefined);
|
set(this, "currentTab", tab);
|
||||||
set(this, ["inboxSorting", "order_by"], "issue__created_at");
|
set(this, "inboxFilters", undefined);
|
||||||
set(this, ["inboxSorting", "sort_by"], "desc");
|
set(this, ["inboxSorting", "order_by"], "issue__created_at");
|
||||||
set(this, ["inboxIssues"], {});
|
set(this, ["inboxSorting", "sort_by"], "desc");
|
||||||
set(this, ["inboxIssuePaginationInfo"], undefined);
|
set(this, ["inboxIssueIds"], []);
|
||||||
if (tab === "closed") set(this, ["inboxFilters", "status"], [-1, 1, 2]);
|
set(this, ["inboxIssuePaginationInfo"], undefined);
|
||||||
else set(this, ["inboxFilters", "status"], [-2]);
|
if (tab === "closed") set(this, ["inboxFilters", "status"], [-1, 1, 2]);
|
||||||
|
else set(this, ["inboxFilters", "status"], [-2]);
|
||||||
|
});
|
||||||
const { workspaceSlug, projectId } = this.store.router;
|
const { workspaceSlug, projectId } = this.store.router;
|
||||||
if (workspaceSlug && projectId) this.fetchInboxIssues(workspaceSlug, projectId, "filter-loading");
|
if (workspaceSlug && projectId) this.fetchInboxIssues(workspaceSlug, projectId, "filter-loading");
|
||||||
};
|
};
|
||||||
|
|
||||||
handleInboxIssueFilters = <T extends keyof TInboxIssueFilter>(key: T, value: TInboxIssueFilter[T]) => {
|
handleInboxIssueFilters = <T extends keyof TInboxIssueFilter>(key: T, value: TInboxIssueFilter[T]) => {
|
||||||
set(this.inboxFilters, key, value);
|
runInAction(() => {
|
||||||
set(this, ["inboxIssues"], {});
|
set(this.inboxFilters, key, value);
|
||||||
set(this, ["inboxIssuePaginationInfo"], undefined);
|
set(this, ["inboxIssuePaginationInfo"], undefined);
|
||||||
|
});
|
||||||
const { workspaceSlug, projectId } = this.store.router;
|
const { workspaceSlug, projectId } = this.store.router;
|
||||||
if (workspaceSlug && projectId) this.fetchInboxIssues(workspaceSlug, projectId, "filter-loading");
|
if (workspaceSlug && projectId) this.fetchInboxIssues(workspaceSlug, projectId, "filter-loading");
|
||||||
};
|
};
|
||||||
|
|
||||||
handleInboxIssueSorting = <T extends keyof TInboxIssueSorting>(key: T, value: TInboxIssueSorting[T]) => {
|
handleInboxIssueSorting = <T extends keyof TInboxIssueSorting>(key: T, value: TInboxIssueSorting[T]) => {
|
||||||
set(this.inboxSorting, key, value);
|
runInAction(() => {
|
||||||
set(this, ["inboxIssues"], {});
|
set(this.inboxSorting, key, value);
|
||||||
set(this, ["inboxIssuePaginationInfo"], undefined);
|
set(this, ["inboxIssuePaginationInfo"], undefined);
|
||||||
|
});
|
||||||
const { workspaceSlug, projectId } = this.store.router;
|
const { workspaceSlug, projectId } = this.store.router;
|
||||||
if (workspaceSlug && projectId) this.fetchInboxIssues(workspaceSlug, projectId, "filter-loading");
|
if (workspaceSlug && projectId) this.fetchInboxIssues(workspaceSlug, projectId, "filter-loading");
|
||||||
};
|
};
|
||||||
@ -248,11 +270,14 @@ export class ProjectInboxStore implements IProjectInboxStore {
|
|||||||
fetchInboxIssues = async (workspaceSlug: string, projectId: string, loadingType: TLoader = undefined) => {
|
fetchInboxIssues = async (workspaceSlug: string, projectId: string, loadingType: TLoader = undefined) => {
|
||||||
try {
|
try {
|
||||||
if (this.currentInboxProjectId != projectId) {
|
if (this.currentInboxProjectId != projectId) {
|
||||||
set(this, ["currentInboxProjectId"], projectId);
|
runInAction(() => {
|
||||||
set(this, ["inboxIssues"], {});
|
set(this, ["currentInboxProjectId"], projectId);
|
||||||
set(this, ["inboxIssuePaginationInfo"], undefined);
|
set(this, ["inboxIssues"], {});
|
||||||
|
set(this, ["inboxIssueIds"], []);
|
||||||
|
set(this, ["inboxIssuePaginationInfo"], undefined);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
if (Object.keys(this.inboxIssues).length === 0) this.loader = "init-loading";
|
if (Object.keys(this.inboxIssueIds).length === 0) this.loader = "init-loading";
|
||||||
else this.loader = "mutation-loading";
|
else this.loader = "mutation-loading";
|
||||||
if (loadingType) this.loader = loadingType;
|
if (loadingType) this.loader = loadingType;
|
||||||
|
|
||||||
@ -267,15 +292,11 @@ export class ProjectInboxStore implements IProjectInboxStore {
|
|||||||
runInAction(() => {
|
runInAction(() => {
|
||||||
this.loader = undefined;
|
this.loader = undefined;
|
||||||
set(this, "inboxIssuePaginationInfo", paginationInfo);
|
set(this, "inboxIssuePaginationInfo", paginationInfo);
|
||||||
if (results && results.length > 0)
|
if (results) {
|
||||||
results.forEach((value: TInboxIssue) => {
|
const issueIds = results.map((value) => value?.issue?.id);
|
||||||
if (this.getIssueInboxByIssueId(value?.issue?.id) === undefined)
|
set(this, ["inboxIssueIds"], issueIds);
|
||||||
set(
|
this.createOrUpdateInboxIssue(results, workspaceSlug, projectId);
|
||||||
this.inboxIssues,
|
}
|
||||||
[value?.issue?.id],
|
|
||||||
new InboxIssueStore(workspaceSlug, projectId, value, this.store)
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error fetching the inbox issues", error);
|
console.error("Error fetching the inbox issues", error);
|
||||||
@ -299,7 +320,7 @@ export class ProjectInboxStore implements IProjectInboxStore {
|
|||||||
this.inboxIssuePaginationInfo &&
|
this.inboxIssuePaginationInfo &&
|
||||||
(!this.inboxIssuePaginationInfo?.total_results ||
|
(!this.inboxIssuePaginationInfo?.total_results ||
|
||||||
(this.inboxIssuePaginationInfo?.total_results &&
|
(this.inboxIssuePaginationInfo?.total_results &&
|
||||||
this.inboxIssuesArray.length < this.inboxIssuePaginationInfo?.total_results))
|
this.inboxIssueIds.length < this.inboxIssuePaginationInfo?.total_results))
|
||||||
) {
|
) {
|
||||||
this.loader = "pagination-loading";
|
this.loader = "pagination-loading";
|
||||||
|
|
||||||
@ -314,15 +335,11 @@ export class ProjectInboxStore implements IProjectInboxStore {
|
|||||||
runInAction(() => {
|
runInAction(() => {
|
||||||
this.loader = undefined;
|
this.loader = undefined;
|
||||||
set(this, "inboxIssuePaginationInfo", paginationInfo);
|
set(this, "inboxIssuePaginationInfo", paginationInfo);
|
||||||
if (results && results.length > 0)
|
if (results && results.length > 0) {
|
||||||
results.forEach((value: TInboxIssue) => {
|
const issueIds = results.map((value) => value?.issue?.id);
|
||||||
if (this.getIssueInboxByIssueId(value?.issue?.id) === undefined)
|
update(this, ["inboxIssueIds"], (ids) => uniq([...ids, ...issueIds]));
|
||||||
set(
|
this.createOrUpdateInboxIssue(results, workspaceSlug, projectId);
|
||||||
this.inboxIssues,
|
}
|
||||||
[value?.issue?.id],
|
|
||||||
new InboxIssueStore(workspaceSlug, projectId, value, this.store)
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
} else set(this, ["inboxIssuePaginationInfo", "next_page_results"], false);
|
} else set(this, ["inboxIssuePaginationInfo", "next_page_results"], false);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@ -357,14 +374,16 @@ export class ProjectInboxStore implements IProjectInboxStore {
|
|||||||
set(this.inboxIssues, [issueId], new InboxIssueStore(workspaceSlug, projectId, inboxIssue, this.store));
|
set(this.inboxIssues, [issueId], new InboxIssueStore(workspaceSlug, projectId, inboxIssue, this.store));
|
||||||
set(this, "loader", undefined);
|
set(this, "loader", undefined);
|
||||||
});
|
});
|
||||||
// fetching reactions
|
await Promise.all([
|
||||||
await this.store.issue.issueDetail.fetchReactions(workspaceSlug, projectId, issueId);
|
// fetching reactions
|
||||||
// fetching activity
|
this.store.issue.issueDetail.fetchReactions(workspaceSlug, projectId, issueId),
|
||||||
await this.store.issue.issueDetail.fetchActivities(workspaceSlug, projectId, issueId);
|
// fetching activity
|
||||||
// fetching comments
|
this.store.issue.issueDetail.fetchActivities(workspaceSlug, projectId, issueId),
|
||||||
await this.store.issue.issueDetail.fetchComments(workspaceSlug, projectId, issueId);
|
// fetching comments
|
||||||
// fetching attachments
|
this.store.issue.issueDetail.fetchComments(workspaceSlug, projectId, issueId),
|
||||||
await this.store.issue.issueDetail.fetchAttachments(workspaceSlug, projectId, issueId);
|
// fetching attachments
|
||||||
|
this.store.issue.issueDetail.fetchAttachments(workspaceSlug, projectId, issueId),
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
return inboxIssue;
|
return inboxIssue;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@ -385,6 +404,7 @@ export class ProjectInboxStore implements IProjectInboxStore {
|
|||||||
const inboxIssueResponse = await this.inboxIssueService.create(workspaceSlug, projectId, data);
|
const inboxIssueResponse = await this.inboxIssueService.create(workspaceSlug, projectId, data);
|
||||||
if (inboxIssueResponse)
|
if (inboxIssueResponse)
|
||||||
runInAction(() => {
|
runInAction(() => {
|
||||||
|
update(this, ["inboxIssueIds"], (ids) => [...ids, inboxIssueResponse?.issue?.id]);
|
||||||
set(
|
set(
|
||||||
this.inboxIssues,
|
this.inboxIssues,
|
||||||
[inboxIssueResponse?.issue?.id],
|
[inboxIssueResponse?.issue?.id],
|
||||||
@ -419,11 +439,18 @@ export class ProjectInboxStore implements IProjectInboxStore {
|
|||||||
(this.inboxIssuePaginationInfo?.total_results || 0) - 1
|
(this.inboxIssuePaginationInfo?.total_results || 0) - 1
|
||||||
);
|
);
|
||||||
set(this, "inboxIssues", omit(this.inboxIssues, inboxIssueId));
|
set(this, "inboxIssues", omit(this.inboxIssues, inboxIssueId));
|
||||||
|
set(
|
||||||
|
this,
|
||||||
|
["inboxIssueIds"],
|
||||||
|
this.inboxIssueIds.filter((id) => id !== inboxIssueId)
|
||||||
|
);
|
||||||
});
|
});
|
||||||
await this.inboxIssueService.destroy(workspaceSlug, projectId, inboxIssueId);
|
await this.inboxIssueService.destroy(workspaceSlug, projectId, inboxIssueId);
|
||||||
} catch {
|
} catch {
|
||||||
console.error("Error removing the inbox issue");
|
console.error("Error removing the inbox issue");
|
||||||
set(this.inboxIssues, [inboxIssueId], currentIssue);
|
set(this.inboxIssues, [inboxIssueId], currentIssue);
|
||||||
|
set(this, ["inboxIssuePaginationInfo", "total_results"], (this.inboxIssuePaginationInfo?.total_results || 0) + 1);
|
||||||
|
set(this, ["inboxIssueIds"], [...this.inboxIssueIds, inboxIssueId]);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user