forked from github/plane
feat: mark all as read (#1982)
This commit is contained in:
parent
47abe9db5e
commit
a1acd2772e
@ -1,7 +1,7 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
|
|
||||||
// components
|
// components
|
||||||
import { Icon, Tooltip } from "components/ui";
|
import { CustomMenu, Icon, Tooltip } from "components/ui";
|
||||||
// helpers
|
// helpers
|
||||||
import { getNumberCount } from "helpers/string.helper";
|
import { getNumberCount } from "helpers/string.helper";
|
||||||
|
|
||||||
@ -21,6 +21,7 @@ type NotificationHeaderProps = {
|
|||||||
setArchived: React.Dispatch<React.SetStateAction<boolean>>;
|
setArchived: React.Dispatch<React.SetStateAction<boolean>>;
|
||||||
setReadNotification: React.Dispatch<React.SetStateAction<boolean>>;
|
setReadNotification: React.Dispatch<React.SetStateAction<boolean>>;
|
||||||
setSelectedTab: React.Dispatch<React.SetStateAction<NotificationType>>;
|
setSelectedTab: React.Dispatch<React.SetStateAction<NotificationType>>;
|
||||||
|
markAllNotificationsAsRead: () => Promise<void>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const NotificationHeader: React.FC<NotificationHeaderProps> = (props) => {
|
export const NotificationHeader: React.FC<NotificationHeaderProps> = (props) => {
|
||||||
@ -37,6 +38,7 @@ export const NotificationHeader: React.FC<NotificationHeaderProps> = (props) =>
|
|||||||
setArchived,
|
setArchived,
|
||||||
setReadNotification,
|
setReadNotification,
|
||||||
setSelectedTab,
|
setSelectedTab,
|
||||||
|
markAllNotificationsAsRead,
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
const notificationTabs: Array<{
|
const notificationTabs: Array<{
|
||||||
@ -88,33 +90,51 @@ export const NotificationHeader: React.FC<NotificationHeaderProps> = (props) =>
|
|||||||
<Icon iconName="filter_list" />
|
<Icon iconName="filter_list" />
|
||||||
</button>
|
</button>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
<Tooltip tooltipContent="Snoozed notifications">
|
<CustomMenu
|
||||||
<button
|
customButton={
|
||||||
type="button"
|
<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={() => {
|
onClick={() => {
|
||||||
setArchived(false);
|
setArchived(false);
|
||||||
setReadNotification(false);
|
setReadNotification(false);
|
||||||
setSnoozed((prev) => !prev);
|
setSnoozed((prev) => !prev);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Icon iconName="schedule" />
|
<div className="flex items-center gap-2">
|
||||||
</button>
|
<Icon iconName="schedule" />
|
||||||
</Tooltip>
|
Show snoozed
|
||||||
<Tooltip tooltipContent="Archived notifications">
|
</div>
|
||||||
<button
|
</CustomMenu.MenuItem>
|
||||||
type="button"
|
<CustomMenu.MenuItem
|
||||||
|
renderAs="button"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setSnoozed(false);
|
setSnoozed(false);
|
||||||
setReadNotification(false);
|
setReadNotification(false);
|
||||||
setArchived((prev) => !prev);
|
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>
|
</button>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
<button type="button" onClick={() => closePopover()}>
|
|
||||||
<Icon iconName="close" />
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="border-b border-custom-border-300 w-full px-5 mt-5">
|
<div className="border-b border-custom-border-300 w-full px-5 mt-5">
|
||||||
|
@ -51,6 +51,7 @@ export const NotificationPopover = () => {
|
|||||||
hasMore,
|
hasMore,
|
||||||
isRefreshing,
|
isRefreshing,
|
||||||
setFetchNotifications,
|
setFetchNotifications,
|
||||||
|
markAllNotificationsAsRead,
|
||||||
} = useUserNotification();
|
} = useUserNotification();
|
||||||
|
|
||||||
// theme context
|
// theme context
|
||||||
@ -112,7 +113,7 @@ export const NotificationPopover = () => {
|
|||||||
leaveFrom="opacity-100 translate-y-0"
|
leaveFrom="opacity-100 translate-y-0"
|
||||||
leaveTo="opacity-0 translate-y-1"
|
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
|
<NotificationHeader
|
||||||
notificationCount={notificationCount}
|
notificationCount={notificationCount}
|
||||||
notificationMutate={notificationMutate}
|
notificationMutate={notificationMutate}
|
||||||
@ -126,6 +127,7 @@ export const NotificationPopover = () => {
|
|||||||
setArchived={setArchived}
|
setArchived={setArchived}
|
||||||
setReadNotification={setReadNotification}
|
setReadNotification={setReadNotification}
|
||||||
setSelectedTab={setSelectedTab}
|
setSelectedTab={setSelectedTab}
|
||||||
|
markAllNotificationsAsRead={markAllNotificationsAsRead}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{notifications ? (
|
{notifications ? (
|
||||||
|
@ -9,11 +9,14 @@ import useSWRInfinite from "swr/infinite";
|
|||||||
// services
|
// services
|
||||||
import userNotificationServices from "services/notifications.service";
|
import userNotificationServices from "services/notifications.service";
|
||||||
|
|
||||||
|
// hooks
|
||||||
|
import useToast from "./use-toast";
|
||||||
|
|
||||||
// fetch-keys
|
// fetch-keys
|
||||||
import { UNREAD_NOTIFICATIONS_COUNT, getPaginatedNotificationKey } from "constants/fetch-keys";
|
import { UNREAD_NOTIFICATIONS_COUNT, getPaginatedNotificationKey } from "constants/fetch-keys";
|
||||||
|
|
||||||
// type
|
// type
|
||||||
import type { NotificationType, NotificationCount } from "types";
|
import type { NotificationType, NotificationCount, IMarkAllAsReadPayload } from "types";
|
||||||
|
|
||||||
const PER_PAGE = 30;
|
const PER_PAGE = 30;
|
||||||
|
|
||||||
@ -21,6 +24,8 @@ const useUserNotification = () => {
|
|||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { workspaceSlug } = router.query;
|
const { workspaceSlug } = router.query;
|
||||||
|
|
||||||
|
const { setToastAlert } = useToast();
|
||||||
|
|
||||||
const [snoozed, setSnoozed] = useState<boolean>(false);
|
const [snoozed, setSnoozed] = useState<boolean>(false);
|
||||||
const [archived, setArchived] = useState<boolean>(false);
|
const [archived, setArchived] = useState<boolean>(false);
|
||||||
const [readNotification, setReadNotification] = 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 {
|
return {
|
||||||
notifications,
|
notifications,
|
||||||
notificationMutate,
|
notificationMutate,
|
||||||
@ -304,6 +332,7 @@ const useUserNotification = () => {
|
|||||||
isRefreshing,
|
isRefreshing,
|
||||||
setFetchNotifications,
|
setFetchNotifications,
|
||||||
markNotificationAsRead,
|
markNotificationAsRead,
|
||||||
|
markAllNotificationsAsRead,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -9,6 +9,7 @@ import type {
|
|||||||
INotificationParams,
|
INotificationParams,
|
||||||
NotificationCount,
|
NotificationCount,
|
||||||
PaginatedUserNotification,
|
PaginatedUserNotification,
|
||||||
|
IMarkAllAsReadPayload,
|
||||||
} from "types";
|
} from "types";
|
||||||
|
|
||||||
class UserNotificationsServices extends APIService {
|
class UserNotificationsServices extends APIService {
|
||||||
@ -172,6 +173,19 @@ class UserNotificationsServices extends APIService {
|
|||||||
throw error?.response?.data;
|
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();
|
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;
|
my_issues: number;
|
||||||
watching_issues: number;
|
watching_issues: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export interface IMarkAllAsReadPayload {
|
||||||
|
archived?: boolean;
|
||||||
|
snoozed?: boolean;
|
||||||
|
type?: NotificationType;
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user