mirror of
https://github.com/makeplane/plane
synced 2024-06-14 14:31:34 +00:00
feat: showing un-read notification count
This commit is contained in:
parent
82d03b4882
commit
cc2146a404
@ -39,8 +39,6 @@ export const NotificationCard: React.FC<NotificationCardProps> = (props) => {
|
|||||||
|
|
||||||
const { setToastAlert } = useToast();
|
const { setToastAlert } = useToast();
|
||||||
|
|
||||||
if (notification.data.issue_activity.field === "None") return null;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
key={notification.id}
|
key={notification.id}
|
||||||
@ -88,13 +86,19 @@ export const NotificationCard: React.FC<NotificationCardProps> = (props) => {
|
|||||||
</span>
|
</span>
|
||||||
{notification.data.issue_activity.field !== "comment" &&
|
{notification.data.issue_activity.field !== "comment" &&
|
||||||
notification.data.issue_activity.verb}{" "}
|
notification.data.issue_activity.verb}{" "}
|
||||||
{notification.data.issue_activity.field !== "comment"
|
{notification.data.issue_activity.field === "comment"
|
||||||
? replaceUnderscoreIfSnakeCase(notification.data.issue_activity.field)
|
? "commented"
|
||||||
: "commented"}{" "}
|
: notification.data.issue_activity.field === "None"
|
||||||
{notification.data.issue_activity.field !== "comment" ? "to" : ""}
|
? null
|
||||||
|
: replaceUnderscoreIfSnakeCase(notification.data.issue_activity.field)}{" "}
|
||||||
|
{notification.data.issue_activity.field !== "comment" &&
|
||||||
|
notification.data.issue_activity.field !== "None"
|
||||||
|
? "to"
|
||||||
|
: ""}
|
||||||
<span className="font-semibold text-custom-text-200">
|
<span className="font-semibold text-custom-text-200">
|
||||||
{" "}
|
{" "}
|
||||||
{notification.data.issue_activity.field !== "comment" ? (
|
{notification.data.issue_activity.field !== "None" ? (
|
||||||
|
notification.data.issue_activity.field !== "comment" ? (
|
||||||
notification.data.issue_activity.field === "target_date" ? (
|
notification.data.issue_activity.field === "target_date" ? (
|
||||||
renderShortDateWithYearFormat(notification.data.issue_activity.new_value)
|
renderShortDateWithYearFormat(notification.data.issue_activity.new_value)
|
||||||
) : notification.data.issue_activity.field === "attachment" ? (
|
) : notification.data.issue_activity.field === "attachment" ? (
|
||||||
@ -112,6 +116,9 @@ export const NotificationCard: React.FC<NotificationCardProps> = (props) => {
|
|||||||
: notification.data.issue_activity.issue_comment}
|
: notification.data.issue_activity.issue_comment}
|
||||||
{`"`}
|
{`"`}
|
||||||
</span>
|
</span>
|
||||||
|
)
|
||||||
|
) : (
|
||||||
|
"the issue and assigned it to you."
|
||||||
)}
|
)}
|
||||||
</span>
|
</span>
|
||||||
</p>
|
</p>
|
||||||
|
@ -5,7 +5,7 @@ import Image from "next/image";
|
|||||||
// hooks
|
// hooks
|
||||||
import useTheme from "hooks/use-theme";
|
import useTheme from "hooks/use-theme";
|
||||||
|
|
||||||
import { Popover, Transition, Menu } from "@headlessui/react";
|
import { Popover, Transition } from "@headlessui/react";
|
||||||
|
|
||||||
// hooks
|
// hooks
|
||||||
import useUserNotification from "hooks/use-user-notifications";
|
import useUserNotification from "hooks/use-user-notifications";
|
||||||
@ -14,27 +14,12 @@ import useUserNotification from "hooks/use-user-notifications";
|
|||||||
import { Spinner, Icon } from "components/ui";
|
import { Spinner, Icon } from "components/ui";
|
||||||
import { SnoozeNotificationModal, NotificationCard } from "components/notifications";
|
import { SnoozeNotificationModal, NotificationCard } from "components/notifications";
|
||||||
|
|
||||||
|
// helpers
|
||||||
|
import { getNumberCount } from "helpers/string.helper";
|
||||||
|
|
||||||
// type
|
// type
|
||||||
import type { NotificationType } from "types";
|
import type { NotificationType } from "types";
|
||||||
|
|
||||||
const notificationTabs: Array<{
|
|
||||||
label: string;
|
|
||||||
value: NotificationType;
|
|
||||||
}> = [
|
|
||||||
{
|
|
||||||
label: "My Issues",
|
|
||||||
value: "assigned",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "Created by me",
|
|
||||||
value: "created",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "Subscribed",
|
|
||||||
value: "watching",
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
export const NotificationPopover = () => {
|
export const NotificationPopover = () => {
|
||||||
const {
|
const {
|
||||||
notifications,
|
notifications,
|
||||||
@ -52,11 +37,35 @@ export const NotificationPopover = () => {
|
|||||||
markNotificationArchivedStatus,
|
markNotificationArchivedStatus,
|
||||||
markNotificationReadStatus,
|
markNotificationReadStatus,
|
||||||
markSnoozeNotification,
|
markSnoozeNotification,
|
||||||
|
notificationCount,
|
||||||
|
totalNotificationCount,
|
||||||
} = useUserNotification();
|
} = useUserNotification();
|
||||||
|
|
||||||
// theme context
|
// theme context
|
||||||
const { collapsed: sidebarCollapse } = useTheme();
|
const { collapsed: sidebarCollapse } = useTheme();
|
||||||
|
|
||||||
|
const notificationTabs: Array<{
|
||||||
|
label: string;
|
||||||
|
value: NotificationType;
|
||||||
|
unreadCount?: number;
|
||||||
|
}> = [
|
||||||
|
{
|
||||||
|
label: "My Issues",
|
||||||
|
value: "assigned",
|
||||||
|
unreadCount: notificationCount?.my_issues,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Created by me",
|
||||||
|
value: "created",
|
||||||
|
unreadCount: notificationCount?.created_issues,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Subscribed",
|
||||||
|
value: "watching",
|
||||||
|
unreadCount: notificationCount?.watching_notifications,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<SnoozeNotificationModal
|
<SnoozeNotificationModal
|
||||||
@ -85,6 +94,11 @@ export const NotificationPopover = () => {
|
|||||||
>
|
>
|
||||||
<Icon iconName="notifications" />
|
<Icon iconName="notifications" />
|
||||||
{sidebarCollapse ? null : <span>Notifications</span>}
|
{sidebarCollapse ? null : <span>Notifications</span>}
|
||||||
|
{totalNotificationCount && totalNotificationCount > 0 ? (
|
||||||
|
<span className="ml-auto bg-custom-primary-300 rounded-full text-xs text-white px-1.5">
|
||||||
|
{getNumberCount(totalNotificationCount)}
|
||||||
|
</span>
|
||||||
|
) : null}
|
||||||
</Popover.Button>
|
</Popover.Button>
|
||||||
<Transition
|
<Transition
|
||||||
as={Fragment}
|
as={Fragment}
|
||||||
@ -190,6 +204,11 @@ export const NotificationPopover = () => {
|
|||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
{tab.label}
|
{tab.label}
|
||||||
|
{tab.unreadCount && tab.unreadCount > 0 ? (
|
||||||
|
<span className="ml-3 bg-custom-background-1000/5 rounded-full text-custom-text-100 text-xs px-1.5">
|
||||||
|
{getNumberCount(tab.unreadCount)}
|
||||||
|
</span>
|
||||||
|
) : null}
|
||||||
</button>
|
</button>
|
||||||
))}
|
))}
|
||||||
</nav>
|
</nav>
|
||||||
|
@ -241,3 +241,6 @@ export const USER_WORKSPACE_NOTIFICATIONS_DETAILS = (
|
|||||||
notificationId: string
|
notificationId: string
|
||||||
) =>
|
) =>
|
||||||
`USER_WORKSPACE_NOTIFICATIONS_DETAILS_${workspaceSlug.toUpperCase()}_${notificationId.toUpperCase()}`;
|
`USER_WORKSPACE_NOTIFICATIONS_DETAILS_${workspaceSlug.toUpperCase()}_${notificationId.toUpperCase()}`;
|
||||||
|
|
||||||
|
export const UNREAD_NOTIFICATIONS_COUNT = (workspaceSlug: string) =>
|
||||||
|
`UNREAD_NOTIFICATIONS_COUNT_${workspaceSlug.toUpperCase()}`;
|
||||||
|
@ -134,3 +134,20 @@ export const stripHTML = (html: string) => {
|
|||||||
tmp.innerHTML = html;
|
tmp.innerHTML = html;
|
||||||
return tmp.textContent || tmp.innerText || "";
|
return tmp.textContent || tmp.innerText || "";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description: This function return number count in string if number is more than 100 then it will return 99+
|
||||||
|
* @param {number} number
|
||||||
|
* @return {string}
|
||||||
|
* @example:
|
||||||
|
* const number = 100;
|
||||||
|
* const text = getNumberCount(number);
|
||||||
|
* console.log(text); // 99+
|
||||||
|
*/
|
||||||
|
|
||||||
|
export const getNumberCount = (number: number): string => {
|
||||||
|
if (number > 99) {
|
||||||
|
return "99+";
|
||||||
|
}
|
||||||
|
return number.toString();
|
||||||
|
};
|
||||||
|
@ -9,7 +9,7 @@ import useSWR from "swr";
|
|||||||
import userNotificationServices from "services/notifications.service";
|
import userNotificationServices from "services/notifications.service";
|
||||||
|
|
||||||
// fetch-keys
|
// fetch-keys
|
||||||
import { USER_WORKSPACE_NOTIFICATIONS } from "constants/fetch-keys";
|
import { UNREAD_NOTIFICATIONS_COUNT, USER_WORKSPACE_NOTIFICATIONS } from "constants/fetch-keys";
|
||||||
|
|
||||||
// type
|
// type
|
||||||
import type { NotificationType } from "types";
|
import type { NotificationType } from "types";
|
||||||
@ -46,6 +46,14 @@ const useUserNotification = () => {
|
|||||||
: null
|
: null
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const { data: notificationCount, mutate: mutateNotificationCount } = useSWR(
|
||||||
|
workspaceSlug ? UNREAD_NOTIFICATIONS_COUNT(workspaceSlug.toString()) : null,
|
||||||
|
() =>
|
||||||
|
workspaceSlug
|
||||||
|
? userNotificationServices.getUnreadNotificationsCount(workspaceSlug.toString())
|
||||||
|
: null
|
||||||
|
);
|
||||||
|
|
||||||
const markNotificationReadStatus = async (notificationId: string) => {
|
const markNotificationReadStatus = async (notificationId: string) => {
|
||||||
if (!workspaceSlug) return;
|
if (!workspaceSlug) return;
|
||||||
const isRead =
|
const isRead =
|
||||||
@ -66,6 +74,7 @@ const useUserNotification = () => {
|
|||||||
return prevNotification;
|
return prevNotification;
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
mutateNotificationCount();
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
throw new Error("Something went wrong");
|
throw new Error("Something went wrong");
|
||||||
@ -85,6 +94,7 @@ const useUserNotification = () => {
|
|||||||
return prevNotification;
|
return prevNotification;
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
mutateNotificationCount();
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
throw new Error("Something went wrong");
|
throw new Error("Something went wrong");
|
||||||
@ -164,6 +174,13 @@ const useUserNotification = () => {
|
|||||||
setSelectedNotificationForSnooze,
|
setSelectedNotificationForSnooze,
|
||||||
selectedTab,
|
selectedTab,
|
||||||
setSelectedTab,
|
setSelectedTab,
|
||||||
|
totalNotificationCount: notificationCount
|
||||||
|
? notificationCount.created_issues +
|
||||||
|
notificationCount.watching_notifications +
|
||||||
|
notificationCount.my_issues
|
||||||
|
: null,
|
||||||
|
notificationCount,
|
||||||
|
mutateNotificationCount,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -155,6 +155,18 @@ class UserNotificationsServices extends APIService {
|
|||||||
throw error?.response?.data;
|
throw error?.response?.data;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async getUnreadNotificationsCount(workspaceSlug: string): Promise<{
|
||||||
|
created_issues: number;
|
||||||
|
my_issues: number;
|
||||||
|
watching_notifications: number;
|
||||||
|
}> {
|
||||||
|
return this.get(`/api/workspaces/${workspaceSlug}/users/notifications/unread/`)
|
||||||
|
.then((response) => response?.data)
|
||||||
|
.catch((error) => {
|
||||||
|
throw error?.response?.data;
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const userNotificationServices = new UserNotificationsServices();
|
const userNotificationServices = new UserNotificationsServices();
|
||||||
|
Loading…
Reference in New Issue
Block a user