chore:reduced restrucured files. Refractored code in notifications store

This commit is contained in:
vamsi krishna
2024-12-11 16:24:10 +05:30
parent 2625d26fd3
commit a5f90a3303
15 changed files with 144 additions and 308 deletions

View File

@@ -1,6 +1,6 @@
"use client";
import { FC, useCallback } from "react";
import { observer } from "mobx-react";
import { useParams } from "next/navigation";
// components
@@ -19,8 +19,10 @@ import { cn } from "@/helpers/common.helper";
import { getNumberCount } from "@/helpers/string.helper";
// hooks
import { useWorkspace, useWorkspaceNotifications } from "@/hooks/store";
import { useFlag } from "@/plane-web/hooks/store";
import { NotificationCardListRoot } from "@/plane-web/components/workspace-notifications";
import { NotificationCardListRoot as NotificationCardListRootCe } from "ce/components/workspace-notifications";
import { NotificationCardListRoot as NotificationCardListRootEe } from "ee/components/workspace-notifications";
export const NotificationsSidebarRoot: FC = observer(() => {
const { workspaceSlug } = useParams();
@@ -47,16 +49,18 @@ export const NotificationsSidebarRoot: FC = observer(() => {
[currentNotificationTab, setCurrentNotificationTab]
);
const isFeatureEnabled = useFlag(workspaceSlug.toString(), "INBOX_STACKING");
if (!workspaceSlug || !workspace) return <></>;
return (
<div
className={cn(
"relative border-0 md:border-r border-custom-border-200 z-[10] flex-shrink-0 bg-custom-background-100 h-full transition-all overflow-hidden",
"relative border-0 md:border-r border-custom-border-200 z-[10] flex-shrink-0 bg-custom-background-100 h-full transition-all max-md:overflow-hidden",
currentSelectedNotificationId ? "w-0 md:w-2/6" : "w-full md:w-2/6"
)}
>
<div className="relative w-full h-full overflow-hidden flex flex-col">
<div className="relative w-full h-full flex flex-col">
<Row className="h-[3.75rem] border-b border-custom-border-200 flex">
<NotificationSidebarHeader workspaceSlug={workspaceSlug.toString()} />
</Row>
@@ -100,7 +104,11 @@ export const NotificationsSidebarRoot: FC = observer(() => {
<>
{notificationIds && notificationIds.length > 0 ? (
<ContentWrapper variant={ERowVariant.HUGGING}>
<NotificationCardListRoot workspaceSlug={workspaceSlug.toString()} workspaceId={workspace?.id} />
{isFeatureEnabled ? (
<NotificationCardListRootEe workspaceSlug={workspaceSlug.toString()} workspaceId={workspace?.id} />
) : (
<NotificationCardListRootCe workspaceSlug={workspaceSlug.toString()} workspaceId={workspace?.id} />
)}
</ContentWrapper>
) : (
<div className="relative w-full h-full flex justify-center items-center">

View File

@@ -1,2 +1 @@
export * from "./root";
export * from "./notification-card";

View File

@@ -1,3 +1,5 @@
"use client";
import { FC, useMemo, useState, Fragment } from "react";
import { uniq } from "lodash";
import orderBy from "lodash/orderBy";
@@ -27,25 +29,29 @@ export const NotificationItem: FC<INotificationItem> = observer((props) => {
const [isSnoozeStateModalOpen, setIsSnoozeStateModalOpen] = useState(false);
const [customSnoozeModal, setCustomSnoozeModal] = useState(false);
//hooks
const { groupedNotificationsByIssueId, markNotificationGroupRead, hasInboxIssue, setCurrentSelectedNotificationId } =
useWorkspaceNotifications();
const {
getNotificationsGroupedByIssue,
markBulkNotificationsAsRead,
containsInboxIssue,
setCurrentSelectedNotificationId,
} = useWorkspaceNotifications();
const { getIsIssuePeeked, setPeekIssue } = useIssueDetail();
//derived values
const groupedNotifications = groupedNotificationsByIssueId(workspaceId);
const notificationGroup = groupedNotifications[issueId];
const issue = notificationGroup[0].data?.issue;
const unreadCount = notificationGroup.filter((e) => !e.read_at).length;
const projectId = notificationGroup[0].project;
const groupedNotifications = getNotificationsGroupedByIssue(workspaceId);
const notificationList = groupedNotifications[issueId];
const issue = notificationList[0].data?.issue;
const unreadCount = notificationList.filter((e) => !e.read_at).length;
const projectId = notificationList[0].project;
const authorIds: string[] = uniq(
notificationGroup.map((e) => e.triggered_by).filter((id): id is string => id != undefined && id != null)
notificationList.map((e) => e.triggered_by).filter((id): id is string => id != undefined && id != null)
);
const latestNotificationTime = useMemo(() => {
const latestNotification = orderBy(notificationGroup, (n) => convertToEpoch(n.created_at), "desc")[0];
const latestNotification = orderBy(notificationList, (n) => convertToEpoch(n.created_at), "desc")[0];
if (latestNotification.created_at) return calculateTimeAgo(latestNotification.created_at);
}, [notificationGroup]);
}, [notificationList]);
const { styles, attributes } = usePopper(referenceElement, popperElement, {
placement: "right-start",
@@ -54,18 +60,18 @@ export const NotificationItem: FC<INotificationItem> = observer((props) => {
const handleNotificationIssuePeekOverview = async () => {
if (workspaceSlug && projectId && issueId && !isSnoozeStateModalOpen && !customSnoozeModal) {
setPeekIssue(undefined);
setCurrentSelectedNotificationId(notificationGroup[0].id);
setCurrentSelectedNotificationId(notificationList[0].id);
// make the notification as read
if (unreadCount > 0) {
try {
await markNotificationGroupRead(notificationGroup, workspaceSlug);
await markBulkNotificationsAsRead(notificationList, workspaceSlug);
} catch (error) {
console.error(error);
}
}
if (!hasInboxIssue(notificationGroup)) {
if (!containsInboxIssue(notificationList)) {
if (!getIsIssuePeeked(issueId)) setPeekIssue({ workspaceSlug, projectId, issueId });
}
}
@@ -74,7 +80,7 @@ export const NotificationItem: FC<INotificationItem> = observer((props) => {
// states
const [showPreview, setShowPreview] = useState<boolean>(false);
if (!notificationGroup || !issue || !issue.id || !authorIds || !projectId) return <></>;
if (!notificationList || !issue || !issue.id || !authorIds || !projectId) return <></>;
return (
<Popover as="div" className={""}>
@@ -107,7 +113,7 @@ export const NotificationItem: FC<INotificationItem> = observer((props) => {
workspaceSlug={workspaceSlug}
issueId={issueId}
unreadCount={unreadCount}
notificationGroup={notificationGroup}
notificationList={notificationList}
isSnoozeStateModalOpen={isSnoozeStateModalOpen}
setIsSnoozeStateModalOpen={setIsSnoozeStateModalOpen}
customSnoozeModal={customSnoozeModal}
@@ -147,7 +153,7 @@ export const NotificationItem: FC<INotificationItem> = observer((props) => {
<Popover.Panel {...attributes.popper} className={""}>
<div ref={setPopperElement} className={"absolute z-10"} style={styles.popper}>
<NotificationCardPreview
notificationGroup={notificationGroup}
notificationList={notificationList}
workspaceSlug={workspaceSlug}
projectId={projectId}
issueData={issue}

View File

@@ -15,25 +15,25 @@ import { INotification } from "@/store/notifications/notification";
type TNotificationItemArchiveOption = {
workspaceSlug: string;
notificationGroup: INotification[];
notificationList: INotification[];
issueId: string;
};
export const NotificationItemArchiveOption: FC<TNotificationItemArchiveOption> = observer((props) => {
const { workspaceSlug, notificationGroup, issueId } = props;
const { workspaceSlug, notificationList, issueId } = props;
// hooks
const { captureEvent } = useEventTracker();
const { currentNotificationTab } = useWorkspaceNotifications();
//derived values
const archivedCount = notificationGroup.filter(n=>!!n.archived_at).length;
const archivedCount = notificationList.filter((n) => !!n.archived_at).length;
const { archiveNotificationGroup, unArchiveNotificationGroup } = useWorkspaceNotifications()
const { archiveNotificationList, unArchiveNotificationList } = useWorkspaceNotifications();
const handleNotificationUpdate = async () => {
try {
const request = archivedCount > 0 ? unArchiveNotificationGroup : archiveNotificationGroup;
await request(notificationGroup,workspaceSlug);
const request = archivedCount > 0 ? unArchiveNotificationList : archiveNotificationList;
await request(notificationList, workspaceSlug);
captureEvent(NOTIFICATION_ARCHIVED, {
issue_id: issueId,
tab: currentNotificationTab,

View File

@@ -15,23 +15,23 @@ import { INotification } from "@/store/notifications/notification";
type TNotificationItemReadOption = {
workspaceSlug: string;
notificationGroup: INotification[];
notificationList: INotification[];
issueId: string;
unreadCount: number
unreadCount: number;
};
export const NotificationItemReadOption: FC<TNotificationItemReadOption> = observer((props) => {
const { workspaceSlug, notificationGroup, issueId, unreadCount } = props;
const { workspaceSlug, notificationList, issueId, unreadCount } = props;
// hooks
const { captureEvent } = useEventTracker();
const { currentNotificationTab } = useWorkspaceNotifications();
const { markNotificationGroupRead, markNotificationGroupUnRead } = useWorkspaceNotifications();
const { markBulkNotificationsAsRead, markBulkNotificationsAsUnread } = useWorkspaceNotifications();
const handleNotificationUpdate = async () => {
try {
const request = unreadCount === 0 ? markNotificationGroupUnRead : markNotificationGroupRead;
await request(notificationGroup,workspaceSlug);
const request = unreadCount === 0 ? markBulkNotificationsAsUnread : markBulkNotificationsAsRead;
await request(notificationList, workspaceSlug);
captureEvent(NOTIFICATIONS_READ, {
issue_id: issueId,
tab: currentNotificationTab,

View File

@@ -6,7 +6,6 @@ import { observer } from "mobx-react";
// helpers
import { cn } from "@/helpers/common.helper";
// hooks
import { useNotification } from "@/hooks/store";
import {
NotificationItemReadOption,
NotificationItemArchiveOption,
@@ -16,7 +15,7 @@ import { INotification } from "@/store/notifications/notification";
type TNotificationOption = {
workspaceSlug: string;
notificationGroup: INotification[];
notificationList: INotification[];
issueId: string;
unreadCount: number;
isSnoozeStateModalOpen: boolean;
@@ -28,7 +27,7 @@ type TNotificationOption = {
export const NotificationOption: FC<TNotificationOption> = observer((props) => {
const {
workspaceSlug,
notificationGroup,
notificationList,
issueId,
unreadCount,
isSnoozeStateModalOpen,
@@ -43,7 +42,7 @@ export const NotificationOption: FC<TNotificationOption> = observer((props) => {
{/* read */}
<NotificationItemReadOption
workspaceSlug={workspaceSlug}
notificationGroup={notificationGroup}
notificationList={notificationList}
unreadCount={unreadCount}
issueId={issueId}
/>
@@ -51,14 +50,14 @@ export const NotificationOption: FC<TNotificationOption> = observer((props) => {
{/* archive */}
<NotificationItemArchiveOption
workspaceSlug={workspaceSlug}
notificationGroup={notificationGroup}
notificationList={notificationList}
issueId={issueId}
/>
{/* snooze notification */}
<NotificationItemSnoozeOption
workspaceSlug={workspaceSlug}
notificationGroup={notificationGroup}
notificationList={notificationList}
setIsSnoozeStateModalOpen={setIsSnoozeStateModalOpen}
customSnoozeModal={customSnoozeModal}
setCustomSnoozeModal={setCustomSnoozeModal}

View File

@@ -18,25 +18,24 @@ import { INotification } from "@/store/notifications/notification";
type TNotificationItemSnoozeOption = {
workspaceSlug: string;
notificationGroup: INotification[];
notificationList: INotification[];
setIsSnoozeStateModalOpen: Dispatch<SetStateAction<boolean>>;
customSnoozeModal: boolean;
setCustomSnoozeModal: Dispatch<SetStateAction<boolean>>;
};
export const NotificationItemSnoozeOption: FC<TNotificationItemSnoozeOption> = observer((props) => {
const { workspaceSlug, notificationGroup, setIsSnoozeStateModalOpen, customSnoozeModal, setCustomSnoozeModal } =
props;
const { workspaceSlug, notificationList, setIsSnoozeStateModalOpen, customSnoozeModal, setCustomSnoozeModal } = props;
// hooks
const { isMobile } = usePlatformOS();
const { snoozeNotificationGroup, unSnoozeNotificationGroup } = useWorkspaceNotifications();
const { snoozeNotificationList, unSnoozeNotificationList } = useWorkspaceNotifications();
const snoozedCount = notificationGroup.filter((n) => !!n.snoozed_till).length;
const snoozedCount = notificationList.filter((n) => !!n.snoozed_till).length;
const handleNotificationSnoozeDate = async (snoozeTill: Date | undefined) => {
if (snoozedCount === 0 && snoozeTill) {
try {
await snoozeNotificationGroup(notificationGroup, workspaceSlug, snoozeTill);
await snoozeNotificationList(notificationList, workspaceSlug, snoozeTill);
setToast({
title: "Success!",
message: "Notification(s) snoozed successfully",
@@ -47,7 +46,7 @@ export const NotificationItemSnoozeOption: FC<TNotificationItemSnoozeOption> = o
}
} else {
try {
await unSnoozeNotificationGroup(notificationGroup, workspaceSlug);
await unSnoozeNotificationList(notificationList, workspaceSlug);
setToast({
title: "Success!",
message: "Notification(s) un snoozed successfully",

View File

@@ -30,28 +30,29 @@ type TIssueActivityBlock = {
triggeredBy: IUserLite | undefined;
};
export const acitvityIconsMap: Record<string, ReactElement> = {
state: <LayersIcon width={14} height={14} className="text-custom-text-200" aria-hidden="true" />,
name: <MessageSquare size={14} className="text-custom-text-200" aria-hidden="true" />,
description: <MessageSquare size={14} className="text-custom-text-200" aria-hidden="true" />,
assignees: <Users className="h-3.5 w-3.5 flex-shrink-0 text-custom-text-200" />,
priority: <Signal size={14} className="text-custom-text-200" aria-hidden="true" />,
estimate_point: <Triangle size={14} className="text-custom-text-200" aria-hidden="true" />,
parent: <LayoutPanelTop size={14} className="text-custom-text-200" aria-hidden="true" />,
start_date: <CalendarDays size={14} className="text-custom-text-200" aria-hidden="true" />,
target_date: <CalendarDays size={14} className="text-custom-text-200" aria-hidden="true" />,
cycles: <ContrastIcon className="h-4 w-4 flex-shrink-0 text-custom-text-200" />,
modules: <DiceIcon className="h-4 w-4 flex-shrink-0 text-custom-text-200" />,
labels: <Tag size={14} className="text-custom-text-200" aria-hidden="true" />,
link: <MessageSquare size={14} className="text-custom-text-200" aria-hidden="true" />,
attachment: <Paperclip size={14} className="text-custom-text-200" aria-hidden="true" />,
archived_at: <ArchiveIcon className="h-3.5 w-3.5 text-custom-text-200" aria-hidden="true" />,
inbox: <Intake className="h-4 w-4 flex-shrink-0 text-custom-text-200" />,
type: <ArrowRightLeft className="h-3.5 w-3.5 flex-shrink-0 text-custom-text-200" />,
};
export const IssueActivityBlock: FC<TIssueActivityBlock> = (props) => {
const { ends, children, createdAt, notificationField, triggeredBy } = props;
const acitvityIconsMap: Record<string, ReactElement> = {
state: <LayersIcon width={14} height={14} className="text-custom-text-200" aria-hidden="true" />,
name: <MessageSquare size={14} className="text-custom-text-200" aria-hidden="true" />,
description: <MessageSquare size={14} className="text-custom-text-200" aria-hidden="true" />,
assignees: <Users className="h-3.5 w-3.5 flex-shrink-0 text-custom-text-200" />,
priority: <Signal size={14} className="text-custom-text-200" aria-hidden="true" />,
estimate_point: <Triangle size={14} className="text-custom-text-200" aria-hidden="true" />,
parent: <LayoutPanelTop size={14} className="text-custom-text-200" aria-hidden="true" />,
start_date: <CalendarDays size={14} className="text-custom-text-200" aria-hidden="true" />,
target_date: <CalendarDays size={14} className="text-custom-text-200" aria-hidden="true" />,
cycles: <ContrastIcon className="h-4 w-4 flex-shrink-0 text-custom-text-200" />,
modules: <DiceIcon className="h-4 w-4 flex-shrink-0 text-custom-text-200" />,
labels: <Tag size={14} className="text-custom-text-200" aria-hidden="true" />,
link: <MessageSquare size={14} className="text-custom-text-200" aria-hidden="true" />,
attachment: <Paperclip size={14} className="text-custom-text-200" aria-hidden="true" />,
archived_at: <ArchiveIcon className="h-3.5 w-3.5 text-custom-text-200" aria-hidden="true" />,
inbox: <Intake className="h-4 w-4 flex-shrink-0 text-custom-text-200" />,
type: <ArrowRightLeft className="h-3.5 w-3.5 flex-shrink-0 text-custom-text-200" />,
};
const { isMobile } = usePlatformOS();
return (
<div

View File

@@ -1,3 +1,5 @@
"use client";
import { FC } from "react";
import { TNotification } from "@plane/types";
import { LiteTextReadOnlyEditor } from "@/components/editor";

View File

@@ -1,3 +1,5 @@
"use client";
import { FC } from "react";
import orderBy from "lodash/orderBy";
import { TNotification, TNotificationIssueLite } from "@plane/types";
@@ -6,14 +8,14 @@ import { IssueTypeLogo } from "@/plane-web/components/issue-types";
import { NotificationPreviewActivity } from "@/plane-web/components/workspace-notifications";
import { useIssueType } from "@/plane-web/hooks/store";
export type TNotificationCardPreview = {
notificationGroup: TNotification[];
notificationList: TNotification[];
workspaceSlug: string;
projectId: string;
issueData: TNotificationIssueLite;
};
export const NotificationCardPreview: FC<TNotificationCardPreview> = (props) => {
const { notificationGroup, workspaceSlug, projectId, issueData } = props;
const unreadCount = notificationGroup.filter((e) => !e.read_at).length;
const { notificationList, workspaceSlug, projectId, issueData } = props;
const unreadCount = notificationList.filter((e) => !e.read_at).length;
const issueType = useIssueType(issueData.id);
@@ -36,7 +38,7 @@ export const NotificationCardPreview: FC<TNotificationCardPreview> = (props) =>
<p className="font-medium">{issueData.name}</p>
</div>
<div className="max-h-60 overflow-y-scroll vertical-scrollbar scrollbar-sm">
{orderBy(notificationGroup, (n) => convertToEpoch(n.created_at), "desc")
{orderBy(notificationList, (n) => convertToEpoch(n.created_at), "desc")
.filter((n) => !!n)
.map((notification, index, { length }) => (
<NotificationPreviewActivity

View File

@@ -17,8 +17,9 @@ type TNotificationCardListRoot = {
export const NotificationCardListRoot: FC<TNotificationCardListRoot> = observer((props) => {
const { workspaceSlug, workspaceId } = props;
// hooks
const { loader, paginationInfo, getNotifications, notificationIssueIdsByWorkspaceId } = useWorkspaceNotifications();
const notificationIssueIds = notificationIssueIdsByWorkspaceId(workspaceId);
const { loader, paginationInfo, getNotifications, getIssueIdsSortedByLatestNotification } =
useWorkspaceNotifications();
const notificationIssueIds = getIssueIdsSortedByLatestNotification(workspaceId);
const getNextNotifications = async () => {
try {

View File

@@ -1,31 +0,0 @@
// export * from "./sidebar";
import React, { useMemo } from "react";
import dynamic from "next/dynamic";
import { useParams } from "next/navigation";
import { useFlag } from "@/plane-web/hooks/store";
export const NotificationsSidebarRoot = () => {
const { workspaceSlug } = useParams();
const isFeatureEnabled = useFlag(workspaceSlug.toString(), "INBOX_STACKING");
const NotificationsSideBarRoot = useMemo(
() =>
dynamic(
() =>
isFeatureEnabled
? import(`ee/components/workspace-notifications/sidebar`).then((module) => ({
default: module["NotificationsSidebar"],
}))
: import("ce/components/workspace-notifications/root").then((module) => ({
default: module["NotificationsSidebarRoot"],
})),
{
// TODO: Add loading component
loading: () => <></>,
}
),
[isFeatureEnabled]
);
return <NotificationsSideBarRoot />;
};

View File

@@ -1,106 +0,0 @@
"use client";
import { FC } from "react";
import { observer } from "mobx-react";
import { useParams } from "next/navigation";
// components
import { Header, Row, ERowVariant, EHeaderVariant, ContentWrapper } from "@plane/ui";
import { CountChip } from "@/components/common";
import {
NotificationsLoader,
NotificationEmptyState,
NotificationSidebarHeader,
AppliedFilters,
} from "@/components/workspace-notifications";
// constants
import { NOTIFICATION_TABS } from "@/constants/notification";
// helpers
import { cn } from "@/helpers/common.helper";
import { getNumberCount } from "@/helpers/string.helper";
// hooks
import { useWorkspace, useWorkspaceNotifications } from "@/hooks/store";
// plane web components
import { NotificationCardListRoot } from "@/plane-web/components/workspace-notifications";
export const NotificationsSidebar: FC = observer(() => {
const { workspaceSlug } = useParams();
// hooks
const { getWorkspaceBySlug } = useWorkspace();
const {
currentSelectedNotificationId,
unreadNotificationsCount,
loader,
notificationIssueIdsByWorkspaceId,
currentNotificationTab,
setCurrentNotificationTab,
} = useWorkspaceNotifications();
// derived values
const workspace = workspaceSlug ? getWorkspaceBySlug(workspaceSlug.toString()) : undefined;
const groupedNotificationIssueIds = workspace ? notificationIssueIdsByWorkspaceId(workspace.id) : undefined;
if (!workspaceSlug || !workspace) return <></>;
return (
<div
className={cn(
"relative border-0 md:border-r max-md:overflow-hidden border-custom-border-200 z-[10] flex-shrink-0 bg-custom-background-100 h-full transition-all",
currentSelectedNotificationId ? "w-0 md:w-2/6" : "w-full md:w-2/6"
)}
>
<div className="relative w-full h-full flex flex-col">
<Row className="h-[3.75rem] border-b border-custom-border-200 flex">
<NotificationSidebarHeader workspaceSlug={workspaceSlug.toString()} />
</Row>
<Header variant={EHeaderVariant.SECONDARY} className="justify-start">
{NOTIFICATION_TABS.map((tab) => (
<div
key={tab.value}
className="h-full px-3 relative cursor-pointer"
onClick={() => currentNotificationTab != tab.value && setCurrentNotificationTab(tab.value)}
>
<div
className={cn(
`relative h-full flex justify-center items-center gap-1 text-sm transition-all`,
currentNotificationTab === tab.value
? "text-custom-primary-100"
: "text-custom-text-100 hover:text-custom-text-200"
)}
>
<div className="font-medium">{tab.label}</div>
{tab.count(unreadNotificationsCount) > 0 && (
<CountChip count={getNumberCount(tab.count(unreadNotificationsCount))} />
)}
</div>
{currentNotificationTab === tab.value && (
<div className="border absolute bottom-0 right-0 left-0 rounded-t-md border-custom-primary-100" />
)}
</div>
))}
</Header>
{/* applied filters */}
<AppliedFilters workspaceSlug={workspaceSlug.toString()} />
{/* rendering notifications */}
{loader === "init-loader" ? (
<div className="relative w-full h-full overflow-hidden">
<NotificationsLoader />
</div>
) : (
<>
{groupedNotificationIssueIds && groupedNotificationIssueIds.length > 0 ? (
<ContentWrapper variant={ERowVariant.HUGGING}>
<NotificationCardListRoot workspaceSlug={workspaceSlug.toString()} workspaceId={workspace?.id} />
</ContentWrapper>
) : (
<div className="relative w-full h-full flex justify-center items-center">
<NotificationEmptyState />
</div>
)}
</>
)}
</div>
</div>
);
});

View File

@@ -1,23 +1,23 @@
import { WorkspaceNotificationService } from "@/services/workspace-notification.service";
export class InboxService extends WorkspaceNotificationService {
async markNotificationGroupRead(workspaceSlug: string, payload: { notification_ids: string[] }) {
async markBulkNotificationsAsRead(workspaceSlug: string, payload: { notification_ids: string[] }) {
await this.post(`/api/workspaces/${workspaceSlug}/inbox/read/`, payload);
}
async markNotificationGroupUnRead(workspaceSlug: string, payload: { notification_ids: string[] }) {
async markBulkNotificationsAsUnread(workspaceSlug: string, payload: { notification_ids: string[] }) {
await this.delete(`/api/workspaces/${workspaceSlug}/inbox/read/`, payload);
}
async archiveNotificationGroup(workspaceSlug: string, payload: { notification_ids: string[] }) {
async archiveNotificationList(workspaceSlug: string, payload: { notification_ids: string[] }) {
await this.post(`/api/workspaces/${workspaceSlug}/inbox/archive/`, payload);
}
async unArchiveNotificationGroup(workspaceSlug: string, payload: { notification_ids: string[] }) {
async unArchiveNotificationList(workspaceSlug: string, payload: { notification_ids: string[] }) {
await this.delete(`/api/workspaces/${workspaceSlug}/inbox/archive/`, payload);
}
async updateNotficationGroup(
async updateNotficationList(
workspaceSlug: string,
payload: { notification_ids: string[]; snoozed_till: string | undefined }
) {

View File

@@ -1,5 +1,4 @@
import orderBy from "lodash/orderBy";
import uniqBy from "lodash/uniqBy";
import { runInAction } from "mobx";
import { computedFn } from "mobx-utils";
import { TNotification } from "@plane/types";
@@ -19,39 +18,44 @@ import {
export type TGroupedNotifications = Record<string, TNotification[]>;
export interface IWorkspaceNotificationStore extends IWorkspaceNotificationStoreCore {
groupedNotificationsByIssueId: (workspaceId: string) => Record<string, INotification[]>;
notificationIssueIdsByWorkspaceId: (workspaceId: string) => string[] | undefined;
markNotificationGroupRead: (notificationGroup: INotification[], workspaceSlug: string) => Promise<void>;
markNotificationGroupUnRead: (notificationGroup: INotification[], workspaceSlug: string) => Promise<void>;
archiveNotificationGroup: (notificationGroup: INotification[], workspaceSlug: string) => Promise<void>;
unArchiveNotificationGroup: (notificationGroup: INotification[], workspaceSlug: string) => Promise<void>;
snoozeNotificationGroup: (
notificationGroup: INotification[],
getNotificationsGroupedByIssue: (workspaceId: string) => Record<string, INotification[]>;
getIssueIdsSortedByLatestNotification: (workspaceId: string) => string[] | undefined;
markBulkNotificationsAsRead: (notitificationList: INotification[], workspaceSlug: string) => Promise<void>;
markBulkNotificationsAsUnread: (notitificationList: INotification[], workspaceSlug: string) => Promise<void>;
archiveNotificationList: (notitificationList: INotification[], workspaceSlug: string) => Promise<void>;
unArchiveNotificationList: (notitificationList: INotification[], workspaceSlug: string) => Promise<void>;
snoozeNotificationList: (
notitificationList: INotification[],
workspaceSlug: string,
snoozeTill: Date
snoozeUntil: Date
) => Promise<void>;
unSnoozeNotificationGroup: (notificationGroup: INotification[], workspaceSlug: string) => Promise<void>;
hasInboxIssue: (notificationGroup: INotification[]) => boolean;
unSnoozeNotificationList: (notitificationList: INotification[], workspaceSlug: string) => Promise<void>;
containsInboxIssue: (notitificationList: INotification[]) => boolean;
}
export class WorkspaceNotificationStore extends WorkspaceNotificationStoreCore implements IWorkspaceNotificationStore {
constructor(protected store: RootStore) {
super(store);
}
groupedNotificationsByIssueId = computedFn((workspaceId: string) => {
const filteredNotificationsIds = this.notificationIdsByWorkspaceId(workspaceId);
if (!filteredNotificationsIds) return {};
const groupedNotifications: Record<string, INotification[]> = {};
getNotificationsGroupedByIssue = computedFn((workspaceId: string) => {
const notificationIds = this.notificationIdsByWorkspaceId(workspaceId);
if (!notificationIds) return {};
filteredNotificationsIds.forEach((id) => {
const entityId = this.notifications[id].entity_identifier;
if (!entityId) return;
const existingNotifications = groupedNotifications[entityId] || [];
groupedNotifications[entityId] = uniqBy([...existingNotifications, this.notifications[id]], "id");
});
return notificationIds.reduce<Record<string, INotification[]>>((groupedNotifications, id) => {
const notification = this.notifications[id];
const entityId = notification.entity_identifier;
return groupedNotifications;
if (!entityId) return groupedNotifications;
if (!groupedNotifications[entityId]) {
groupedNotifications[entityId] = [];
}
groupedNotifications[entityId].push(notification);
return groupedNotifications;
}, {});
});
/**
@@ -59,8 +63,8 @@ export class WorkspaceNotificationStore extends WorkspaceNotificationStoreCore i
* @param workspaceId: string
* @returns string[]
*/
notificationIssueIdsByWorkspaceId = computedFn((workspaceId: string) => {
const groupedNotifications: Record<string, INotification[]> = this.groupedNotificationsByIssueId(workspaceId);
getIssueIdsSortedByLatestNotification = computedFn((workspaceId: string) => {
const groupedNotifications: Record<string, INotification[]> = this.getNotificationsGroupedByIssue(workspaceId);
const groupedNotificationIssueIds = orderBy(
Object.keys(groupedNotifications),
@@ -75,21 +79,15 @@ export class WorkspaceNotificationStore extends WorkspaceNotificationStoreCore i
return groupedNotificationIssueIds;
});
/**
* @description Marks a group of notificaitons read
* @param notificationGroup : INotification[]
*/
markNotificationGroupRead = async (notificationGroup: INotification[], workspaceSlug: string) => {
markBulkNotificationsAsRead = async (notificationGroup: INotification[], workspaceSlug: string) => {
const unreadNotificationGroup = notificationGroup.filter((n) => !n.read_at);
if (unreadNotificationGroup.length === 0) return;
try {
this.loader = ENotificationLoader.MUTATION_LOADER;
const notification_ids: string[] = unreadNotificationGroup
.filter((n): n is INotification & { id: string } => !!n.id)
.map((n) => n.id);
const notification_ids: string[] = unreadNotificationGroup.map((n) => n.id);
await inboxService.markNotificationGroupRead(workspaceSlug, {
await inboxService.markBulkNotificationsAsRead(workspaceSlug, {
notification_ids,
});
@@ -107,22 +105,15 @@ export class WorkspaceNotificationStore extends WorkspaceNotificationStoreCore i
}
};
/**
* @description Marks group of notifications unread
* @param notificationGroup : INotification[]
* @param workspaceSlug : string
*/
markNotificationGroupUnRead = async (notificationGroup: INotification[], workspaceSlug: string) => {
markBulkNotificationsAsUnread = async (notificationGroup: INotification[], workspaceSlug: string) => {
const readNotificationGroup = notificationGroup.filter((n) => n.read_at);
if (readNotificationGroup.length === 0) return;
try {
this.loader = ENotificationLoader.MUTATION_LOADER;
const notification_ids: string[] = readNotificationGroup
.filter((n): n is INotification & { id: string } => !!n.id)
.map((n) => n.id);
const notification_ids: string[] = readNotificationGroup.map((n) => n.id);
await inboxService.markNotificationGroupUnRead(workspaceSlug, {
await inboxService.markBulkNotificationsAsUnread(workspaceSlug, {
notification_ids,
});
@@ -140,18 +131,11 @@ export class WorkspaceNotificationStore extends WorkspaceNotificationStoreCore i
}
};
/**
* @description Archives group of notifications
* @param notificationGroup : INotification[]
* @param workspaceSlug : string
*/
archiveNotificationGroup = async (notificationGroup: INotification[], workspaceSlug: string) => {
archiveNotificationList = async (notificationGroup: INotification[], workspaceSlug: string) => {
try {
const notification_ids: string[] = notificationGroup
.filter((n): n is INotification & { id: string } => !!n.id)
.map((n) => n.id);
const notification_ids: string[] = notificationGroup.map((n) => n.id);
await inboxService.archiveNotificationGroup(workspaceSlug, { notification_ids });
await inboxService.archiveNotificationList(workspaceSlug, { notification_ids });
Object.values(notificationGroup).forEach((notification) => {
notification.mutateNotification({ archived_at: new Date().toISOString() });
@@ -162,18 +146,11 @@ export class WorkspaceNotificationStore extends WorkspaceNotificationStoreCore i
}
};
/**
* @description Un archives group of notifications
* @param notificationGroup: INotification[]
* @param workspaceSlug: string
*/
unArchiveNotificationGroup = async (notificationGroup: INotification[], workspaceSlug: string) => {
unArchiveNotificationList = async (notificationGroup: INotification[], workspaceSlug: string) => {
try {
const notification_ids: string[] = notificationGroup
.filter((n): n is INotification & { id: string } => !!n.id)
.map((n) => n.id);
const notification_ids: string[] = notificationGroup.map((n) => n.id);
await inboxService.unArchiveNotificationGroup(workspaceSlug, { notification_ids });
await inboxService.unArchiveNotificationList(workspaceSlug, { notification_ids });
Object.values(notificationGroup).forEach((notification) => {
notification.mutateNotification({ archived_at: undefined });
@@ -184,26 +161,18 @@ export class WorkspaceNotificationStore extends WorkspaceNotificationStoreCore i
}
};
/**
* @description Snoozes group of notifications unitl the provided date and time
* @param notificationGroup : INotification[]
* @param workspaceSlug : string
* @param snoozed_till : Date
*/
snoozeNotificationGroup = async (notificationGroup: INotification[], workspaceSlug: string, snoozeTill: Date) => {
snoozeNotificationList = async (notificationGroup: INotification[], workspaceSlug: string, snoozeUntil: Date) => {
try {
const notification_ids: string[] = notificationGroup
.filter((n): n is INotification & { id: string } => !!n.id)
.map((n) => n.id);
const notification_ids: string[] = notificationGroup.map((n) => n.id);
await inboxService.updateNotficationGroup(workspaceSlug, {
await inboxService.updateNotficationList(workspaceSlug, {
notification_ids,
snoozed_till: snoozeTill.toISOString(),
snoozed_till: snoozeUntil.toISOString(),
});
runInAction(() => {
Object.values(notificationGroup).forEach((notification) => {
notification.mutateNotification({ snoozed_till: snoozeTill.toISOString() });
notification.mutateNotification({ snoozed_till: snoozeUntil.toISOString() });
});
});
} catch (error) {
@@ -212,19 +181,11 @@ export class WorkspaceNotificationStore extends WorkspaceNotificationStoreCore i
}
};
/**
* @description Un Snoozes group of notifications unitl the provided date and time
* @param notificationGroup : INotification[]
* @param workspaceSlug : string
* @param snoozed_till : Date
*/
unSnoozeNotificationGroup = async (notificationGroup: INotification[], workspaceSlug: string) => {
unSnoozeNotificationList = async (notificationGroup: INotification[], workspaceSlug: string) => {
try {
const notification_ids: string[] = notificationGroup
.filter((n): n is INotification & { id: string } => !!n.id)
.map((n) => n.id);
const notification_ids: string[] = notificationGroup.map((n) => n.id);
await inboxService.updateNotficationGroup(workspaceSlug, {
await inboxService.updateNotficationList(workspaceSlug, {
notification_ids,
snoozed_till: undefined,
});
@@ -240,12 +201,7 @@ export class WorkspaceNotificationStore extends WorkspaceNotificationStoreCore i
}
};
/**
* Checks if notification group has an inbox issue
* @param notificationGroup : INotification[]
* @returns boolean
*/
hasInboxIssue = (notificationGroup: INotification[]) => {
containsInboxIssue = (notificationGroup: INotification[]) => {
const inbox_issue = notificationGroup.find((notification) => notification.is_inbox_issue);
if (inbox_issue) return true;
return false;