mirror of
https://github.com/makeplane/plane
synced 2024-06-14 14:31:34 +00:00
feat: mark all as read (#1982)
This commit is contained in:
parent
47abe9db5e
commit
a1acd2772e
@ -1,7 +1,7 @@
|
||||
import React from "react";
|
||||
|
||||
// components
|
||||
import { Icon, Tooltip } from "components/ui";
|
||||
import { CustomMenu, Icon, Tooltip } from "components/ui";
|
||||
// helpers
|
||||
import { getNumberCount } from "helpers/string.helper";
|
||||
|
||||
@ -21,6 +21,7 @@ type NotificationHeaderProps = {
|
||||
setArchived: React.Dispatch<React.SetStateAction<boolean>>;
|
||||
setReadNotification: React.Dispatch<React.SetStateAction<boolean>>;
|
||||
setSelectedTab: React.Dispatch<React.SetStateAction<NotificationType>>;
|
||||
markAllNotificationsAsRead: () => Promise<void>;
|
||||
};
|
||||
|
||||
export const NotificationHeader: React.FC<NotificationHeaderProps> = (props) => {
|
||||
@ -37,6 +38,7 @@ export const NotificationHeader: React.FC<NotificationHeaderProps> = (props) =>
|
||||
setArchived,
|
||||
setReadNotification,
|
||||
setSelectedTab,
|
||||
markAllNotificationsAsRead,
|
||||
} = props;
|
||||
|
||||
const notificationTabs: Array<{
|
||||
@ -88,33 +90,51 @@ export const NotificationHeader: React.FC<NotificationHeaderProps> = (props) =>
|
||||
<Icon iconName="filter_list" />
|
||||
</button>
|
||||
</Tooltip>
|
||||
<Tooltip tooltipContent="Snoozed notifications">
|
||||
<button
|
||||
type="button"
|
||||
<CustomMenu
|
||||
customButton={
|
||||
<div className="grid place-items-center ">
|
||||
<Icon iconName="more_vert" />
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<CustomMenu.MenuItem renderAs="button" onClick={markAllNotificationsAsRead}>
|
||||
<div className="flex items-center gap-2">
|
||||
<Icon iconName="done_all" />
|
||||
Mark all as read
|
||||
</div>
|
||||
</CustomMenu.MenuItem>
|
||||
<CustomMenu.MenuItem
|
||||
renderAs="button"
|
||||
onClick={() => {
|
||||
setArchived(false);
|
||||
setReadNotification(false);
|
||||
setSnoozed((prev) => !prev);
|
||||
}}
|
||||
>
|
||||
<Icon iconName="schedule" />
|
||||
</button>
|
||||
</Tooltip>
|
||||
<Tooltip tooltipContent="Archived notifications">
|
||||
<button
|
||||
type="button"
|
||||
<div className="flex items-center gap-2">
|
||||
<Icon iconName="schedule" />
|
||||
Show snoozed
|
||||
</div>
|
||||
</CustomMenu.MenuItem>
|
||||
<CustomMenu.MenuItem
|
||||
renderAs="button"
|
||||
onClick={() => {
|
||||
setSnoozed(false);
|
||||
setReadNotification(false);
|
||||
setArchived((prev) => !prev);
|
||||
}}
|
||||
>
|
||||
<Icon iconName="archive" />
|
||||
<div className="flex items-center gap-2">
|
||||
<Icon iconName="archive" />
|
||||
Show archived
|
||||
</div>
|
||||
</CustomMenu.MenuItem>
|
||||
</CustomMenu>
|
||||
<Tooltip tooltipContent="Close">
|
||||
<button type="button" onClick={() => closePopover()}>
|
||||
<Icon iconName="close" />
|
||||
</button>
|
||||
</Tooltip>
|
||||
<button type="button" onClick={() => closePopover()}>
|
||||
<Icon iconName="close" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="border-b border-custom-border-300 w-full px-5 mt-5">
|
||||
|
@ -51,6 +51,7 @@ export const NotificationPopover = () => {
|
||||
hasMore,
|
||||
isRefreshing,
|
||||
setFetchNotifications,
|
||||
markAllNotificationsAsRead,
|
||||
} = useUserNotification();
|
||||
|
||||
// theme context
|
||||
@ -112,7 +113,7 @@ export const NotificationPopover = () => {
|
||||
leaveFrom="opacity-100 translate-y-0"
|
||||
leaveTo="opacity-0 translate-y-1"
|
||||
>
|
||||
<Popover.Panel className="absolute bg-custom-background-100 flex flex-col left-0 md:left-full ml-8 z-10 -top-44 md:w-[36rem] w-[20rem] h-[75vh] border border-custom-border-300 shadow-lg rounded-xl">
|
||||
<Popover.Panel className="absolute bg-custom-background-100 flex flex-col left-0 md:left-full ml-8 z-10 -top-36 md:w-[36rem] w-[20rem] h-[50vh] border border-custom-border-300 shadow-lg rounded-xl">
|
||||
<NotificationHeader
|
||||
notificationCount={notificationCount}
|
||||
notificationMutate={notificationMutate}
|
||||
@ -126,6 +127,7 @@ export const NotificationPopover = () => {
|
||||
setArchived={setArchived}
|
||||
setReadNotification={setReadNotification}
|
||||
setSelectedTab={setSelectedTab}
|
||||
markAllNotificationsAsRead={markAllNotificationsAsRead}
|
||||
/>
|
||||
|
||||
{notifications ? (
|
||||
|
@ -9,11 +9,14 @@ import useSWRInfinite from "swr/infinite";
|
||||
// services
|
||||
import userNotificationServices from "services/notifications.service";
|
||||
|
||||
// hooks
|
||||
import useToast from "./use-toast";
|
||||
|
||||
// fetch-keys
|
||||
import { UNREAD_NOTIFICATIONS_COUNT, getPaginatedNotificationKey } from "constants/fetch-keys";
|
||||
|
||||
// type
|
||||
import type { NotificationType, NotificationCount } from "types";
|
||||
import type { NotificationType, NotificationCount, IMarkAllAsReadPayload } from "types";
|
||||
|
||||
const PER_PAGE = 30;
|
||||
|
||||
@ -21,6 +24,8 @@ const useUserNotification = () => {
|
||||
const router = useRouter();
|
||||
const { workspaceSlug } = router.query;
|
||||
|
||||
const { setToastAlert } = useToast();
|
||||
|
||||
const [snoozed, setSnoozed] = useState<boolean>(false);
|
||||
const [archived, setArchived] = useState<boolean>(false);
|
||||
const [readNotification, setReadNotification] = useState<boolean>(false);
|
||||
@ -274,6 +279,29 @@ const useUserNotification = () => {
|
||||
}
|
||||
};
|
||||
|
||||
const markAllNotificationsAsRead = async () => {
|
||||
if (!workspaceSlug) return;
|
||||
|
||||
let markAsReadParams: IMarkAllAsReadPayload;
|
||||
|
||||
if (snoozed) markAsReadParams = { archived: false, snoozed: true };
|
||||
else if (archived) markAsReadParams = { archived: true, snoozed: false };
|
||||
else markAsReadParams = { archived: false, snoozed: false, type: selectedTab };
|
||||
|
||||
await userNotificationServices
|
||||
.markAllNotificationsAsRead(workspaceSlug.toString(), markAsReadParams)
|
||||
.catch(() => {
|
||||
setToastAlert({
|
||||
type: "error",
|
||||
title: "Error!",
|
||||
message: "Something went wrong. Please try again.",
|
||||
});
|
||||
})
|
||||
.finally(() => {
|
||||
notificationMutate();
|
||||
});
|
||||
};
|
||||
|
||||
return {
|
||||
notifications,
|
||||
notificationMutate,
|
||||
@ -304,6 +332,7 @@ const useUserNotification = () => {
|
||||
isRefreshing,
|
||||
setFetchNotifications,
|
||||
markNotificationAsRead,
|
||||
markAllNotificationsAsRead,
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -9,6 +9,7 @@ import type {
|
||||
INotificationParams,
|
||||
NotificationCount,
|
||||
PaginatedUserNotification,
|
||||
IMarkAllAsReadPayload,
|
||||
} from "types";
|
||||
|
||||
class UserNotificationsServices extends APIService {
|
||||
@ -172,6 +173,19 @@ class UserNotificationsServices extends APIService {
|
||||
throw error?.response?.data;
|
||||
});
|
||||
}
|
||||
|
||||
async markAllNotificationsAsRead(
|
||||
workspaceSlug: string,
|
||||
payload: IMarkAllAsReadPayload
|
||||
): Promise<any> {
|
||||
return this.post(`/api/workspaces/${workspaceSlug}/users/notifications/mark-all-read/`, {
|
||||
...payload,
|
||||
})
|
||||
.then((response) => response?.data)
|
||||
.catch((error) => {
|
||||
throw error?.response?.data;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const userNotificationServices = new UserNotificationsServices();
|
||||
|
6
apps/app/types/notifications.d.ts
vendored
6
apps/app/types/notifications.d.ts
vendored
@ -71,3 +71,9 @@ export type NotificationCount = {
|
||||
my_issues: number;
|
||||
watching_issues: number;
|
||||
};
|
||||
|
||||
export interface IMarkAllAsReadPayload {
|
||||
archived?: boolean;
|
||||
snoozed?: boolean;
|
||||
type?: NotificationType;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user