[WEB-1900] chore: mentions mutation, ui fix on app sidebar notification badge, and back button inbox issue notification embed (#5083)

* chore: mention notification boolean field

* chore: handled mentions and all notification mutation and UI fix on the app sidebar notification badge and Back redirection button on inbox issue resposiveness

* chore: Moved everthing to chip

* chore: cleaning up the selection when we unmount the page

* chore: resolved build error

---------

Co-authored-by: NarayanBavisetti <narayan3119@gmail.com>
This commit is contained in:
guru_sainath 2024-07-09 13:41:34 +05:30 committed by GitHub
parent 988201d729
commit f617937542
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
14 changed files with 85 additions and 18 deletions

View file

@ -12,6 +12,7 @@ class NotificationSerializer(BaseSerializer):
read_only=True, source="triggered_by"
)
is_inbox_issue = serializers.BooleanField(read_only=True)
is_mentioned_notification = serializers.BooleanField(read_only=True)
class Meta:
model = Notification

View file

@ -1,5 +1,5 @@
# Django imports
from django.db.models import Exists, OuterRef, Q
from django.db.models import Exists, OuterRef, Q, Case, When, BooleanField
from django.utils import timezone
# Third party imports
@ -60,6 +60,13 @@ class NotificationViewSet(BaseViewSet, BasePaginator):
)
.filter(entity_name="issue")
.annotate(is_inbox_issue=Exists(inbox_issue))
.annotate(
is_mentioned_notification=Case(
When(sender__icontains="mentioned", then=True),
default=False,
output_field=BooleanField(),
)
)
.select_related("workspace", "project", "triggered_by", "receiver")
.order_by("snoozed_till", "-created_at")
)

View file

@ -51,6 +51,7 @@ export type TNotification = {
archived_at: string | undefined;
snoozed_till: string | undefined;
is_inbox_issue: boolean | undefined;
is_mentioned_notification: boolean | undefined;
workspace: string | undefined;
project: string | undefined;
created_at: string | undefined;

View file

@ -1,5 +1,6 @@
"use client";
import { useEffect } from "react";
import { observer } from "mobx-react";
import useSWR from "swr";
// components
@ -10,7 +11,7 @@ import { IssuePeekOverview } from "@/components/issues";
// constants
import { ENotificationLoader, ENotificationQueryParamType } from "@/constants/notification";
// hooks
import { useUser, useWorkspace, useWorkspaceNotifications } from "@/hooks/store";
import { useIssueDetail, useUser, useWorkspace, useWorkspaceNotifications } from "@/hooks/store";
const WorkspaceDashboardPage = observer(() => {
// hooks
@ -25,6 +26,7 @@ const WorkspaceDashboardPage = observer(() => {
const {
membership: { fetchUserProjectInfo },
} = useUser();
const { setPeekIssue } = useIssueDetail();
// derived values
const pageTitle = currentWorkspace?.name ? `${currentWorkspace?.name} - Notifications` : undefined;
const { workspace_slug, project_id, issue_id, is_inbox_issue } =
@ -54,6 +56,15 @@ const WorkspaceDashboardPage = observer(() => {
workspace_slug && project_id && is_inbox_issue ? () => fetchUserProjectInfo(workspace_slug, project_id) : null
);
// clearing up the selected notifications when unmounting the page
useEffect(
() => () => {
setCurrentSelectedNotificationId(undefined);
setPeekIssue(undefined);
},
[]
);
return (
<>
<PageHead title={pageTitle} />

View file

@ -11,7 +11,7 @@ import { IIssueDisplayFilterOptions, IIssueDisplayProperties, IIssueFilterOption
import { Breadcrumbs, Button, LayersIcon, Tooltip } from "@plane/ui";
// components
import { ProjectAnalyticsModal } from "@/components/analytics";
import { BreadcrumbLink, Logo } from "@/components/common";
import { BreadcrumbLink, CountChip, Logo } from "@/components/common";
import { DisplayFiltersSelection, FiltersDropdown, FilterSelection, LayoutSelection } from "@/components/issues";
// constants
import {
@ -161,9 +161,7 @@ export const ProjectIssuesHeader = observer(() => {
tooltipContent={`There are ${issuesCount} ${issuesCount > 1 ? "issues" : "issue"} in this project`}
position="bottom"
>
<span className="cursor-default flex items-center text-center justify-center px-2.5 py-0.5 flex-shrink-0 bg-custom-primary-100/20 text-custom-primary-100 text-xs font-semibold rounded-xl">
{issuesCount}
</span>
<CountChip count={issuesCount} />
</Tooltip>
) : null}
</div>

View file

@ -0,0 +1,25 @@
"use client";
import { FC } from "react";
//
import { cn } from "@/helpers/common.helper";
type TCountChip = {
count: string | number;
className?: string;
};
export const CountChip: FC<TCountChip> = (props) => {
const { count, className = "" } = props;
return (
<div
className={cn(
"relative flex justify-center items-center px-2.5 py-0.5 flex-shrink-0 bg-custom-primary-100/20 text-custom-primary-100 text-xs font-semibold rounded-xl",
className
)}
>
{count}
</div>
);
};

View file

@ -4,3 +4,4 @@ export * from "./latest-feature-block";
export * from "./breadcrumb-link";
export * from "./logo-spinner";
export * from "./logo";
export * from "./count-chip";

View file

@ -387,6 +387,8 @@ export const InboxIssueActionsHeader: FC<TInboxIssueActionsHeader> = observer((p
workspaceSlug={workspaceSlug}
isMobileSidebar={isMobileSidebar}
setIsMobileSidebar={setIsMobileSidebar}
isNotificationEmbed={isNotificationEmbed}
embedRemoveCurrentNotification={embedRemoveCurrentNotification}
/>
</div>
</>

View file

@ -13,6 +13,7 @@ import {
Link,
Trash2,
PanelLeft,
MoveRight,
} from "lucide-react";
import { CustomMenu } from "@plane/ui";
// components
@ -44,6 +45,8 @@ type Props = {
handleCopyIssueLink: () => void;
isMobileSidebar: boolean;
setIsMobileSidebar: (value: boolean) => void;
isNotificationEmbed: boolean;
embedRemoveCurrentNotification?: () => void;
};
export const InboxIssueActionsMobileHeader: React.FC<Props> = observer((props) => {
@ -65,6 +68,8 @@ export const InboxIssueActionsMobileHeader: React.FC<Props> = observer((props) =
handleCopyIssueLink,
isMobileSidebar,
setIsMobileSidebar,
isNotificationEmbed,
embedRemoveCurrentNotification,
} = props;
const router = useAppRouter();
const issue = inboxIssue?.issue;
@ -76,6 +81,11 @@ export const InboxIssueActionsMobileHeader: React.FC<Props> = observer((props) =
return (
<div className="h-12 relative flex border-custom-border-200 w-full items-center gap-2 px-4">
{isNotificationEmbed && (
<button onClick={embedRemoveCurrentNotification}>
<MoveRight className="h-4 w-4 text-custom-text-300 hover:text-custom-text-200" />
</button>
)}
<PanelLeft
onClick={() => setIsMobileSidebar(!isMobileSidebar)}
className={cn(

View file

@ -3,6 +3,8 @@
import { FC } from "react";
import { observer } from "mobx-react";
import useSWR from "swr";
// components
import { CountChip } from "@/components/common";
// helpers
import { getNumberCount } from "@/helpers/string.helper";
// hooks
@ -35,8 +37,8 @@ export const NotificationAppSidebarOption: FC<TNotificationAppSidebarOption> = o
return <div className="absolute right-3.5 top-2 h-2 w-2 rounded-full bg-custom-primary-300" />;
return (
<div className="text-[8px] ml-auto bg-custom-primary-100 text-white p-1 py-0.5 rounded-full">
{`${isMentionsEnabled ? `@` : ``}${getNumberCount(totalNotifications)}`}
<div className="ml-auto">
<CountChip count={`${isMentionsEnabled ? `@` : ``}${getNumberCount(totalNotifications)}`} />
</div>
);
});

View file

@ -38,6 +38,7 @@ export const NotificationItem: FC<TNotificationItem> = observer((props) => {
const handleNotificationIssuePeekOverview = async () => {
if (workspaceSlug && projectId && issueId && !isSnoozeStateModalOpen && !customSnoozeModal) {
setPeekIssue(undefined);
setCurrentSelectedNotificationId(notificationId);
// make the notification as read
@ -51,7 +52,6 @@ export const NotificationItem: FC<TNotificationItem> = observer((props) => {
if (notification?.is_inbox_issue === false) {
!getIsIssuePeeked(issueId) && setPeekIssue({ workspaceSlug, projectId, issueId });
} else {
}
}
};

View file

@ -4,6 +4,7 @@ import { FC } from "react";
import { observer } from "mobx-react";
import { useParams } from "next/navigation";
// components
import { CountChip } from "@/components/common";
import {
NotificationsLoader,
NotificationEmptyState,
@ -65,14 +66,12 @@ export const NotificationsSidebar: FC = observer(() => {
)}
>
<div className="font-medium">{tab.label}</div>
<div
className={cn(
`rounded-full text-xs px-1.5 py-0.5`,
<CountChip
count={getNumberCount(tab.count(unreadNotificationsCount))}
className={
currentNotificationTab === tab.value ? `bg-custom-primary-100/20` : `bg-custom-background-80/50`
)}
>
{getNumberCount(tab.count(unreadNotificationsCount))}
</div>
}
/>
</div>
{currentNotificationTab === tab.value && (
<div className="border absolute bottom-0 right-0 left-0 rounded-t-md border-custom-primary-100" />

View file

@ -42,6 +42,7 @@ export class Notification implements INotification {
archived_at: string | undefined = undefined;
snoozed_till: string | undefined = undefined;
is_inbox_issue: boolean | undefined = undefined;
is_mentioned_notification: boolean | undefined = undefined;
workspace: string | undefined = undefined;
project: string | undefined = undefined;
created_at: string | undefined = undefined;
@ -70,6 +71,8 @@ export class Notification implements INotification {
read_at: observable.ref,
archived_at: observable.ref,
snoozed_till: observable.ref,
is_inbox_issue: observable.ref,
is_mentioned_notification: observable.ref,
workspace: observable.ref,
project: observable.ref,
created_at: observable.ref,
@ -102,8 +105,9 @@ export class Notification implements INotification {
this.read_at = this.notification.read_at;
this.archived_at = this.notification.archived_at;
this.snoozed_till = this.notification.snoozed_till;
this.workspace = this.notification.workspace;
this.is_inbox_issue = this.notification.is_inbox_issue;
this.is_mentioned_notification = this.notification.is_mentioned_notification;
this.workspace = this.notification.workspace;
this.project = this.notification.project;
this.created_at = this.notification.created_at;
this.updated_at = this.notification.updated_at;
@ -132,8 +136,9 @@ export class Notification implements INotification {
read_at: this.read_at,
archived_at: this.archived_at,
snoozed_till: this.snoozed_till,
workspace: this.workspace,
is_inbox_issue: this.is_inbox_issue,
is_mentioned_notification: this.is_mentioned_notification,
workspace: this.workspace,
project: this.project,
created_at: this.created_at,
updated_at: this.updated_at,

View file

@ -125,6 +125,11 @@ export class WorkspaceNotificationStore implements IWorkspaceNotificationStore {
);
const workspaceNotificationIds = workspaceNotifications
.filter((n) => n.workspace === workspaceId)
.filter((n) =>
this.currentNotificationTab === ENotificationTab.MENTIONS
? n.is_mentioned_notification
: !n.is_mentioned_notification
)
.filter((n) => {
if (!this.filters.archived && !this.filters.snoozed) {
if (n.archived_at) {