[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:
Prateek Shourya 2024-05-31 15:10:38 +05:30 committed by GitHub
parent bf4f97d7f6
commit 0a105a1c21
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 143 additions and 113 deletions

View File

@ -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(

View File

@ -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

View File

@ -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">

View File

@ -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>
))} ))}

View File

@ -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>

View File

@ -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;
}; };

View File

@ -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]);
} }
}; };
} }