Merge branch 'preview' of github.com:makeplane/plane into develop

This commit is contained in:
sriram veeraghanta 2024-02-09 16:22:41 +05:30
commit 99975d0ba0
4 changed files with 360 additions and 235 deletions

View File

@ -1,7 +1,7 @@
import React from "react"; import React, { useEffect, useRef } from "react";
import Image from "next/image"; import Image from "next/image";
import { useRouter } from "next/router"; import { useRouter } from "next/router";
import { ArchiveRestore, Clock, MessageSquare, User2 } from "lucide-react"; import { ArchiveRestore, Clock, MessageSquare, MoreVertical, User2 } from "lucide-react";
import Link from "next/link"; import Link from "next/link";
// hooks // hooks
import useToast from "hooks/use-toast"; import useToast from "hooks/use-toast";
@ -14,6 +14,7 @@ import { replaceUnderscoreIfSnakeCase, truncateText, stripAndTruncateHTML } from
import { calculateTimeAgo, renderFormattedTime, renderFormattedDate } from "helpers/date-time.helper"; import { calculateTimeAgo, renderFormattedTime, renderFormattedDate } from "helpers/date-time.helper";
// type // type
import type { IUserNotification } from "@plane/types"; import type { IUserNotification } from "@plane/types";
import { Menu } from "@headlessui/react";
type NotificationCardProps = { type NotificationCardProps = {
notification: IUserNotification; notification: IUserNotification;
@ -40,8 +41,73 @@ export const NotificationCard: React.FC<NotificationCardProps> = (props) => {
const router = useRouter(); const router = useRouter();
const { workspaceSlug } = router.query; const { workspaceSlug } = router.query;
// states
const [showSnoozeOptions, setshowSnoozeOptions] = React.useState(false);
// toast alert
const { setToastAlert } = useToast(); const { setToastAlert } = useToast();
// refs
const snoozeRef = useRef<HTMLDivElement | null>(null);
const moreOptions = [
{
id: 1,
name: notification.read_at ? "Mark as unread" : "Mark as read",
icon: <MessageSquare className="h-3.5 w-3.5 text-custom-text-300" />,
onClick: () => {
markNotificationReadStatusToggle(notification.id).then(() => {
setToastAlert({
title: notification.read_at ? "Notification marked as read" : "Notification marked as unread",
type: "success",
});
});
},
},
{
id: 2,
name: notification.archived_at ? "Unarchive" : "Archive",
icon: notification.archived_at ? (
<ArchiveRestore className="h-3.5 w-3.5 text-custom-text-300" />
) : (
<ArchiveIcon className="h-3.5 w-3.5 text-custom-text-300" />
),
onClick: () => {
markNotificationArchivedStatus(notification.id).then(() => {
setToastAlert({
title: notification.archived_at ? "Notification un-archived" : "Notification archived",
type: "success",
});
});
},
},
];
const snoozeOptionOnClick = (date: Date | null) => {
if (!date) {
setSelectedNotificationForSnooze(notification.id);
return;
}
markSnoozeNotification(notification.id, date).then(() => {
setToastAlert({
title: `Notification snoozed till ${renderFormattedDate(date)}`,
type: "success",
});
});
};
// close snooze options on outside click
useEffect(() => {
const handleClickOutside = (event: any) => {
if (snoozeRef.current && !snoozeRef.current?.contains(event.target)) {
setshowSnoozeOptions(false);
}
};
document.addEventListener("mousedown", handleClickOutside, true);
document.addEventListener("touchend", handleClickOutside, true);
return () => {
document.removeEventListener("mousedown", handleClickOutside, true);
document.removeEventListener("touchend", handleClickOutside, true);
};
}, []);
if (isSnoozedTabOpen && new Date(notification.snoozed_till!) < new Date()) return null; if (isSnoozedTabOpen && new Date(notification.snoozed_till!) < new Date()) return null;
@ -87,57 +153,136 @@ export const NotificationCard: React.FC<NotificationCardProps> = (props) => {
)} )}
</div> </div>
<div className="w-full space-y-2.5 overflow-hidden"> <div className="w-full space-y-2.5 overflow-hidden">
{!notification.message ? ( <div className="flex items-start">
<div className="w-full break-words text-sm"> {!notification.message ? (
<span className="font-semibold"> <div className="w-full break-words text-sm">
{notification.triggered_by_details.is_bot <span className="font-semibold">
? notification.triggered_by_details.first_name {notification.triggered_by_details.is_bot
: notification.triggered_by_details.display_name}{" "} ? notification.triggered_by_details.first_name
</span> : notification.triggered_by_details.display_name}{" "}
{notification.data.issue_activity.field !== "comment" && notification.data.issue_activity.verb}{" "} </span>
{notification.data.issue_activity.field === "comment" {notification.data.issue_activity.field !== "comment" && notification.data.issue_activity.verb}{" "}
? "commented" {notification.data.issue_activity.field === "comment"
: notification.data.issue_activity.field === "None" ? "commented"
? null : notification.data.issue_activity.field === "None"
: replaceUnderscoreIfSnakeCase(notification.data.issue_activity.field)}{" "} ? null
{notification.data.issue_activity.field !== "comment" && notification.data.issue_activity.field !== "None" : replaceUnderscoreIfSnakeCase(notification.data.issue_activity.field)}{" "}
? "to" {notification.data.issue_activity.field !== "comment" && notification.data.issue_activity.field !== "None"
: ""} ? "to"
<span className="font-semibold"> : ""}
{" "} <span className="font-semibold">
{notification.data.issue_activity.field !== "None" ? ( {" "}
notification.data.issue_activity.field !== "comment" ? ( {notification.data.issue_activity.field !== "None" ? (
notification.data.issue_activity.field === "target_date" ? ( notification.data.issue_activity.field !== "comment" ? (
renderFormattedDate(notification.data.issue_activity.new_value) notification.data.issue_activity.field === "target_date" ? (
) : notification.data.issue_activity.field === "attachment" ? ( renderFormattedDate(notification.data.issue_activity.new_value)
"the issue" ) : notification.data.issue_activity.field === "attachment" ? (
) : notification.data.issue_activity.field === "description" ? ( "the issue"
stripAndTruncateHTML(notification.data.issue_activity.new_value, 55) ) : notification.data.issue_activity.field === "description" ? (
stripAndTruncateHTML(notification.data.issue_activity.new_value, 55)
) : (
notification.data.issue_activity.new_value
)
) : ( ) : (
notification.data.issue_activity.new_value <span>
{`"`}
{notification.data.issue_activity.new_value.length > 55
? notification?.data?.issue_activity?.issue_comment?.slice(0, 50) + "..."
: notification.data.issue_activity.issue_comment}
{`"`}
</span>
) )
) : ( ) : (
<span> "the issue and assigned it to you."
{`"`} )}
{notification.data.issue_activity.new_value.length > 55 </span>
? notification?.data?.issue_activity?.issue_comment?.slice(0, 50) + "..." </div>
: notification.data.issue_activity.issue_comment} ) : (
{`"`} <div className="w-full break-words text-sm">
</span> <span className="semi-bold">{notification.message}</span>
) </div>
) : ( )}
"the issue and assigned it to you." <div className="flex md:hidden items-start">
<Menu as="div" className={" w-min text-left"}>
{({ open }) => (
<>
<Menu.Button as={React.Fragment}>
<button
onClick={(e) => e.stopPropagation()}
className="flex w-full items-center gap-x-2 rounded p-0.5 text-sm"
>
<MoreVertical className="h-3.5 w-3.5 text-custom-text-300" />
</button>
</Menu.Button>
{open && (
<Menu.Items className={"absolute right-0 z-10"} static>
<div
className={
"my-1 overflow-y-scroll rounded-md border-[0.5px] border-custom-border-300 bg-custom-background-100 px-2 py-2.5 text-xs shadow-custom-shadow-rg focus:outline-none min-w-[12rem] whitespace-nowrap"
}
>
{moreOptions.map((item) => (
<Menu.Item as="div">
{({ close }) => (
<button
onClick={(e) => {
e.stopPropagation();
e.preventDefault();
item.onClick();
close();
}}
className="flex gap-x-2 items-center p-1.5"
>
{item.icon}
{item.name}
</button>
)}
</Menu.Item>
))}
<Menu.Item as="div">
<div
onClick={(e) => {
e.stopPropagation();
e.preventDefault();
setshowSnoozeOptions(true);
}}
className="flex gap-x-2 items-center p-1.5"
>
<Clock className="h-3.5 w-3.5 text-custom-text-300" />
Snooze
</div>
</Menu.Item>
</div>
</Menu.Items>
)}
</>
)} )}
</span> </Menu>
{showSnoozeOptions && (
<div
ref={snoozeRef}
className="absolute right-36 z-20 my-1 top-24 overflow-y-scroll rounded-md border-[0.5px] border-custom-border-300 bg-custom-background-100 px-2 py-2.5 text-xs shadow-custom-shadow-rg focus:outline-none min-w-[12rem] whitespace-nowrap"
>
{snoozeOptions.map((item) => (
<p
className="p-1.5"
onClick={(e) => {
e.stopPropagation();
e.preventDefault();
setshowSnoozeOptions(false);
snoozeOptionOnClick(item.value);
}}
>
{item.label}
</p>
))}
</div>
)}
</div> </div>
) : ( </div>
<div className="w-full break-words text-sm">
<span className="semi-bold">{notification.message}</span>
</div>
)}
<div className="flex justify-between gap-2 text-xs"> <div className="flex justify-between gap-2 text-xs">
<p className="text-custom-text-300"> <p className="text-custom-text-300 line-clamp-1">
{truncateText( {truncateText(
`${notification.data.issue.identifier}-${notification.data.issue.sequence_id} ${notification.data.issue.name}`, `${notification.data.issue.identifier}-${notification.data.issue.sequence_id} ${notification.data.issue.name}`,
50 50
@ -152,43 +297,12 @@ export const NotificationCard: React.FC<NotificationCardProps> = (props) => {
</span> </span>
</p> </p>
) : ( ) : (
<p className="flex-shrink-0 text-custom-text-300">{calculateTimeAgo(notification.created_at)}</p> <p className="flex-shrink-0 text-custom-text-300 mt-auto">{calculateTimeAgo(notification.created_at)}</p>
)} )}
</div> </div>
</div> </div>
<div className="absolute right-3 top-3 hidden gap-x-3 py-1 group-hover:flex"> <div className="absolute right-3 top-3 hidden gap-x-3 py-1 md:group-hover:flex">
{[ {moreOptions.map((item) => (
{
id: 1,
name: notification.read_at ? "Mark as unread" : "Mark as read",
icon: <MessageSquare className="h-3.5 w-3.5 text-custom-text-300" />,
onClick: () => {
markNotificationReadStatusToggle(notification.id).then(() => {
setToastAlert({
title: notification.read_at ? "Notification marked as read" : "Notification marked as unread",
type: "success",
});
});
},
},
{
id: 2,
name: notification.archived_at ? "Unarchive" : "Archive",
icon: notification.archived_at ? (
<ArchiveRestore className="h-3.5 w-3.5 text-custom-text-300" />
) : (
<ArchiveIcon className="h-3.5 w-3.5 text-custom-text-300" />
),
onClick: () => {
markNotificationArchivedStatus(notification.id).then(() => {
setToastAlert({
title: notification.archived_at ? "Notification un-archived" : "Notification archived",
type: "success",
});
});
},
},
].map((item) => (
<Tooltip tooltipContent={item.name}> <Tooltip tooltipContent={item.name}>
<button <button
type="button" type="button"
@ -221,18 +335,7 @@ export const NotificationCard: React.FC<NotificationCardProps> = (props) => {
onClick={(e) => { onClick={(e) => {
e.stopPropagation(); e.stopPropagation();
e.preventDefault(); e.preventDefault();
snoozeOptionOnClick(item.value);
if (!item.value) {
setSelectedNotificationForSnooze(notification.id);
return;
}
markSnoozeNotification(notification.id, item.value).then(() => {
setToastAlert({
title: `Notification snoozed till ${renderFormattedDate(item.value)}`,
type: "success",
});
});
}} }}
> >
{item.label} {item.label}

View File

@ -1,5 +1,7 @@
import React from "react"; import React from "react";
import { ArrowLeft, CheckCheck, Clock, ListFilter, MoreVertical, RefreshCw, X } from "lucide-react"; import { ArrowLeft, CheckCheck, Clock, ListFilter, MoreVertical, RefreshCw, X } from "lucide-react";
// components
import { SidebarHamburgerToggle } from "components/core/sidebar/sidebar-menu-hamburger-toggle";
// ui // ui
import { ArchiveIcon, CustomMenu, Tooltip } from "@plane/ui"; import { ArchiveIcon, CustomMenu, Tooltip } from "@plane/ui";
// helpers // helpers
@ -65,7 +67,11 @@ export const NotificationHeader: React.FC<NotificationHeaderProps> = (props) =>
return ( return (
<> <>
<div className="flex items-center justify-between px-5 pt-5"> <div className="flex items-center justify-between px-5 pt-5">
<h2 className="mb-2 text-xl font-semibold">Notifications</h2> <div className="flex items-center gap-x-2 ">
<SidebarHamburgerToggle />
<h2 className="md:text-xl md:font-semibold">Notifications</h2>
</div>
<div className="flex items-center justify-center gap-x-4 text-custom-text-200"> <div className="flex items-center justify-center gap-x-4 text-custom-text-200">
<Tooltip tooltipContent="Refresh"> <Tooltip tooltipContent="Refresh">
<button <button
@ -128,11 +134,13 @@ export const NotificationHeader: React.FC<NotificationHeaderProps> = (props) =>
</div> </div>
</CustomMenu.MenuItem> </CustomMenu.MenuItem>
</CustomMenu> </CustomMenu>
<Tooltip tooltipContent="Close"> <div className="hidden md:block">
<button type="button" onClick={() => closePopover()}> <Tooltip tooltipContent="Close">
<X className="h-3.5 w-3.5" /> <button type="button" onClick={() => closePopover()}>
</button> <X className="h-3.5 w-3.5" />
</Tooltip> </button>
</Tooltip>
</div>
</div> </div>
</div> </div>
<div className="mt-5 w-full border-b border-custom-border-300 px-5"> <div className="mt-5 w-full border-b border-custom-border-300 px-5">

View File

@ -5,6 +5,7 @@ import { observer } from "mobx-react-lite";
// hooks // hooks
import { useApplication } from "hooks/store"; import { useApplication } from "hooks/store";
import useUserNotification from "hooks/use-user-notifications"; import useUserNotification from "hooks/use-user-notifications";
import useOutsideClickDetector from "hooks/use-outside-click-detector";
// components // components
import { EmptyState } from "components/common"; import { EmptyState } from "components/common";
import { SnoozeNotificationModal, NotificationCard, NotificationHeader } from "components/notifications"; import { SnoozeNotificationModal, NotificationCard, NotificationHeader } from "components/notifications";
@ -15,8 +16,12 @@ import emptyNotification from "public/empty-state/notification.svg";
import { getNumberCount } from "helpers/string.helper"; import { getNumberCount } from "helpers/string.helper";
export const NotificationPopover = observer(() => { export const NotificationPopover = observer(() => {
// states
const [isActive, setIsActive] = React.useState(false);
// store hooks // store hooks
const { theme: themeStore } = useApplication(); const { theme: themeStore } = useApplication();
// refs
const notificationPopoverRef = React.useRef<HTMLDivElement | null>(null);
const { const {
notifications, notifications,
@ -44,8 +49,11 @@ export const NotificationPopover = observer(() => {
setFetchNotifications, setFetchNotifications,
markAllNotificationsAsRead, markAllNotificationsAsRead,
} = useUserNotification(); } = useUserNotification();
const isSidebarCollapsed = themeStore.sidebarCollapsed; const isSidebarCollapsed = themeStore.sidebarCollapsed;
useOutsideClickDetector(notificationPopoverRef, () => {
// if snooze modal is open, then don't close the popover
if (selectedNotificationForSnooze === null) setIsActive(false);
});
return ( return (
<> <>
@ -54,141 +62,142 @@ export const NotificationPopover = observer(() => {
onClose={() => setSelectedNotificationForSnooze(null)} onClose={() => setSelectedNotificationForSnooze(null)}
onSubmit={markSnoozeNotification} onSubmit={markSnoozeNotification}
notification={notifications?.find((notification) => notification.id === selectedNotificationForSnooze) || null} notification={notifications?.find((notification) => notification.id === selectedNotificationForSnooze) || null}
onSuccess={() => { onSuccess={() => setSelectedNotificationForSnooze(null)}
setSelectedNotificationForSnooze(null);
}}
/> />
<Popover className="relative w-full"> <Popover ref={notificationPopoverRef} className="md:relative w-full">
{({ open: isActive, close: closePopover }) => { <>
if (isActive) setFetchNotifications(true); <Tooltip tooltipContent="Notifications" position="right" className="ml-2" disabled={!isSidebarCollapsed}>
<button
className={`group relative flex w-full items-center gap-2.5 rounded-md px-3 py-2 text-sm font-medium outline-none ${
isActive
? "bg-custom-primary-100/10 text-custom-primary-100"
: "text-custom-sidebar-text-200 hover:bg-custom-sidebar-background-80"
} ${isSidebarCollapsed ? "justify-center" : ""}`}
onClick={() => {
if (window.innerWidth < 768) themeStore.toggleSidebar();
if (!isActive) setFetchNotifications(true);
setIsActive(!isActive);
}}
>
<Bell className="h-4 w-4" />
{isSidebarCollapsed ? null : <span>Notifications</span>}
{totalNotificationCount && totalNotificationCount > 0 ? (
isSidebarCollapsed ? (
<span className="absolute right-3.5 top-2 h-2 w-2 rounded-full bg-custom-primary-300" />
) : (
<span className="ml-auto rounded-full bg-custom-primary-300 px-1.5 text-xs text-white">
{getNumberCount(totalNotificationCount)}
</span>
)
) : null}
</button>
</Tooltip>
<Transition
show={isActive}
as={Fragment}
enter="transition ease-out duration-200"
enterFrom="opacity-0 translate-y-1"
enterTo="opacity-100 translate-y-0"
leave="transition ease-in duration-150"
leaveFrom="opacity-100 translate-y-0"
leaveTo="opacity-0 translate-y-1"
>
<Popover.Panel
className="absolute top-0 left-[280px] md:-top-36 md:ml-8 md:h-[50vh] z-10 flex h-full w-[100vw] flex-col rounded-xl md:border border-custom-border-300 bg-custom-background-100 shadow-lg md:left-full md:w-[36rem]"
static
>
<NotificationHeader
notificationCount={notificationCount}
notificationMutate={notificationMutate}
closePopover={() => setIsActive(false)}
isRefreshing={isRefreshing}
snoozed={snoozed}
archived={archived}
readNotification={readNotification}
selectedTab={selectedTab}
setSnoozed={setSnoozed}
setArchived={setArchived}
setReadNotification={setReadNotification}
setSelectedTab={setSelectedTab}
markAllNotificationsAsRead={markAllNotificationsAsRead}
/>
return ( {notifications ? (
<> notifications.length > 0 ? (
<Tooltip tooltipContent="Notifications" position="right" className="ml-2" disabled={!isSidebarCollapsed}> <div className="h-full overflow-y-auto">
<Popover.Button <div className="divide-y divide-custom-border-100">
className={`group relative flex w-full items-center gap-2.5 rounded-md px-3 py-2 text-sm font-medium outline-none ${ {notifications.map((notification) => (
isActive <NotificationCard
? "bg-custom-primary-100/10 text-custom-primary-100" key={notification.id}
: "text-custom-sidebar-text-200 hover:bg-custom-sidebar-background-80" isSnoozedTabOpen={snoozed}
} ${isSidebarCollapsed ? "justify-center" : ""}`} closePopover={() => setIsActive(false)}
> notification={notification}
<Bell className="h-4 w-4" /> markNotificationArchivedStatus={markNotificationArchivedStatus}
{isSidebarCollapsed ? null : <span>Notifications</span>} markNotificationReadStatus={markNotificationAsRead}
{totalNotificationCount && totalNotificationCount > 0 ? ( markNotificationReadStatusToggle={markNotificationReadStatus}
isSidebarCollapsed ? ( setSelectedNotificationForSnooze={setSelectedNotificationForSnooze}
<span className="absolute right-3.5 top-2 h-2 w-2 rounded-full bg-custom-primary-300" /> markSnoozeNotification={markSnoozeNotification}
) : (
<span className="ml-auto rounded-full bg-custom-primary-300 px-1.5 text-xs text-white">
{getNumberCount(totalNotificationCount)}
</span>
)
) : null}
</Popover.Button>
</Tooltip>
<Transition
as={Fragment}
enter="transition ease-out duration-200"
enterFrom="opacity-0 translate-y-1"
enterTo="opacity-100 translate-y-0"
leave="transition ease-in duration-150"
leaveFrom="opacity-100 translate-y-0"
leaveTo="opacity-0 translate-y-1"
>
<Popover.Panel className="absolute -top-36 left-0 z-10 ml-8 flex h-[50vh] w-[20rem] flex-col rounded-xl border border-custom-border-300 bg-custom-background-100 shadow-lg md:left-full md:w-[36rem]">
<NotificationHeader
notificationCount={notificationCount}
notificationMutate={notificationMutate}
closePopover={closePopover}
isRefreshing={isRefreshing}
snoozed={snoozed}
archived={archived}
readNotification={readNotification}
selectedTab={selectedTab}
setSnoozed={setSnoozed}
setArchived={setArchived}
setReadNotification={setReadNotification}
setSelectedTab={setSelectedTab}
markAllNotificationsAsRead={markAllNotificationsAsRead}
/>
{notifications ? (
notifications.length > 0 ? (
<div className="h-full overflow-y-auto">
<div className="divide-y divide-custom-border-100">
{notifications.map((notification) => (
<NotificationCard
key={notification.id}
isSnoozedTabOpen={snoozed}
closePopover={closePopover}
notification={notification}
markNotificationArchivedStatus={markNotificationArchivedStatus}
markNotificationReadStatus={markNotificationAsRead}
markNotificationReadStatusToggle={markNotificationReadStatus}
setSelectedNotificationForSnooze={setSelectedNotificationForSnooze}
markSnoozeNotification={markSnoozeNotification}
/>
))}
</div>
{isLoadingMore && (
<div className="my-6 flex items-center justify-center text-sm">
<div role="status">
<svg
aria-hidden="true"
className="mr-2 h-6 w-6 animate-spin fill-blue-600 text-custom-text-200"
viewBox="0 0 100 101"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M100 50.5908C100 78.2051 77.6142 100.591 50 100.591C22.3858 100.591 0 78.2051 0 50.5908C0 22.9766 22.3858 0.59082 50 0.59082C77.6142 0.59082 100 22.9766 100 50.5908ZM9.08144 50.5908C9.08144 73.1895 27.4013 91.5094 50 91.5094C72.5987 91.5094 90.9186 73.1895 90.9186 50.5908C90.9186 27.9921 72.5987 9.67226 50 9.67226C27.4013 9.67226 9.08144 27.9921 9.08144 50.5908Z"
fill="currentColor"
/>
<path
d="M93.9676 39.0409C96.393 38.4038 97.8624 35.9116 97.0079 33.5539C95.2932 28.8227 92.871 24.3692 89.8167 20.348C85.8452 15.1192 80.8826 10.7238 75.2124 7.41289C69.5422 4.10194 63.2754 1.94025 56.7698 1.05124C51.7666 0.367541 46.6976 0.446843 41.7345 1.27873C39.2613 1.69328 37.813 4.19778 38.4501 6.62326C39.0873 9.04874 41.5694 10.4717 44.0505 10.1071C47.8511 9.54855 51.7191 9.52689 55.5402 10.0491C60.8642 10.7766 65.9928 12.5457 70.6331 15.2552C75.2735 17.9648 79.3347 21.5619 82.5849 25.841C84.9175 28.9121 86.7997 32.2913 88.1811 35.8758C89.083 38.2158 91.5421 39.6781 93.9676 39.0409Z"
fill="currentFill"
/>
</svg>
<span className="sr-only">Loading...</span>
</div>
<p>Loading notifications</p>
</div>
)}
{hasMore && !isLoadingMore && (
<button
type="button"
className="my-6 flex w-full items-center justify-center text-sm font-medium text-custom-primary-100"
disabled={isLoadingMore}
onClick={() => {
setSize((prev) => prev + 1);
}}
>
Load More
</button>
)}
</div>
) : (
<div className="grid h-full w-full scale-75 place-items-center overflow-hidden">
<EmptyState
title="You're updated with all the notifications"
description="You have read all the notifications."
image={emptyNotification}
/> />
))}
</div>
{isLoadingMore && (
<div className="my-6 flex items-center justify-center text-sm">
<div role="status">
<svg
aria-hidden="true"
className="mr-2 h-6 w-6 animate-spin fill-blue-600 text-custom-text-200"
viewBox="0 0 100 101"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M100 50.5908C100 78.2051 77.6142 100.591 50 100.591C22.3858 100.591 0 78.2051 0 50.5908C0 22.9766 22.3858 0.59082 50 0.59082C77.6142 0.59082 100 22.9766 100 50.5908ZM9.08144 50.5908C9.08144 73.1895 27.4013 91.5094 50 91.5094C72.5987 91.5094 90.9186 73.1895 90.9186 50.5908C90.9186 27.9921 72.5987 9.67226 50 9.67226C27.4013 9.67226 9.08144 27.9921 9.08144 50.5908Z"
fill="currentColor"
/>
<path
d="M93.9676 39.0409C96.393 38.4038 97.8624 35.9116 97.0079 33.5539C95.2932 28.8227 92.871 24.3692 89.8167 20.348C85.8452 15.1192 80.8826 10.7238 75.2124 7.41289C69.5422 4.10194 63.2754 1.94025 56.7698 1.05124C51.7666 0.367541 46.6976 0.446843 41.7345 1.27873C39.2613 1.69328 37.813 4.19778 38.4501 6.62326C39.0873 9.04874 41.5694 10.4717 44.0505 10.1071C47.8511 9.54855 51.7191 9.52689 55.5402 10.0491C60.8642 10.7766 65.9928 12.5457 70.6331 15.2552C75.2735 17.9648 79.3347 21.5619 82.5849 25.841C84.9175 28.9121 86.7997 32.2913 88.1811 35.8758C89.083 38.2158 91.5421 39.6781 93.9676 39.0409Z"
fill="currentFill"
/>
</svg>
<span className="sr-only">Loading...</span>
</div>
<p>Loading notifications</p>
</div> </div>
) )}
) : ( {hasMore && !isLoadingMore && (
<Loader className="space-y-4 overflow-y-auto p-5"> <button
<Loader.Item height="50px" /> type="button"
<Loader.Item height="50px" /> className="my-6 flex w-full items-center justify-center text-sm font-medium text-custom-primary-100"
<Loader.Item height="50px" /> disabled={isLoadingMore}
<Loader.Item height="50px" /> onClick={() => {
<Loader.Item height="50px" /> setSize((prev) => prev + 1);
</Loader> }}
)} >
</Popover.Panel> Load More
</Transition> </button>
</> )}
); </div>
}} ) : (
<div className="grid h-full w-full scale-75 place-items-center overflow-hidden">
<EmptyState
title="You're updated with all the notifications"
description="You have read all the notifications."
image={emptyNotification}
/>
</div>
)
) : (
<Loader className="space-y-4 overflow-y-auto p-5">
<Loader.Item height="50px" />
<Loader.Item height="50px" />
<Loader.Item height="50px" />
<Loader.Item height="50px" />
<Loader.Item height="50px" />
</Loader>
)}
</Popover.Panel>
</Transition>
</>
</Popover> </Popover>
</> </>
); );

View File

@ -109,7 +109,12 @@ export const SnoozeNotificationModal: FC<SnoozeModalProps> = (props) => {
}; };
const handleClose = () => { const handleClose = () => {
onClose(); // This is a workaround to fix the issue of the Notification popover modal close on closing this modal
const closeTimeout = setTimeout(() => {
onClose();
clearTimeout(closeTimeout);
}, 50);
const timeout = setTimeout(() => { const timeout = setTimeout(() => {
reset({ ...defaultValues }); reset({ ...defaultValues });
clearTimeout(timeout); clearTimeout(timeout);
@ -142,7 +147,7 @@ export const SnoozeNotificationModal: FC<SnoozeModalProps> = (props) => {
leaveFrom="opacity-100 translate-y-0 sm:scale-100" leaveFrom="opacity-100 translate-y-0 sm:scale-100"
leaveTo="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95" leaveTo="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
> >
<Dialog.Panel className="relative transform rounded-lg bg-custom-background-100 p-5 text-left shadow-custom-shadow-md transition-all sm:w-full sm:max-w-2xl"> <Dialog.Panel className="relative transform rounded-lg bg-custom-background-100 p-5 text-left shadow-custom-shadow-md transition-all w-full sm:w-full sm:!max-w-2xl">
<form onSubmit={handleSubmit(onSubmit)}> <form onSubmit={handleSubmit(onSubmit)}>
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<Dialog.Title as="h3" className="text-lg font-medium leading-6 text-custom-text-100"> <Dialog.Title as="h3" className="text-lg font-medium leading-6 text-custom-text-100">
@ -156,8 +161,8 @@ export const SnoozeNotificationModal: FC<SnoozeModalProps> = (props) => {
</div> </div>
</div> </div>
<div className="mt-5 flex items-center gap-3"> <div className="mt-5 flex flex-col md:!flex-row md:items-center gap-3">
<div className="flex-1"> <div className="flex-1 pb-3 md:pb-0">
<h6 className="mb-2 block text-sm font-medium text-custom-text-400">Pick a date</h6> <h6 className="mb-2 block text-sm font-medium text-custom-text-400">Pick a date</h6>
<Controller <Controller
name="date" name="date"