Skip to content

Commit

Permalink
fix(js,react): inbox custom bell unread count not updating (novuhq#6362)
Browse files Browse the repository at this point in the history
  • Loading branch information
LetItRock authored Aug 27, 2024
1 parent 659f4d3 commit cc519c5
Show file tree
Hide file tree
Showing 21 changed files with 942 additions and 110 deletions.
26 changes: 0 additions & 26 deletions packages/js/src/ui/components/ExternalElementMounter.tsx

This file was deleted.

26 changes: 26 additions & 0 deletions packages/js/src/ui/components/ExternalElementRenderer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { onCleanup, createEffect, ParentProps } from 'solid-js';

type ExternalElementMounterProps = ParentProps<{
render: (el: HTMLDivElement) => () => void;
}>;

export const ExternalElementRenderer = ({ render, ...rest }: ExternalElementMounterProps) => {
let ref: HTMLDivElement;

createEffect(() => {
const unmount = render(ref);

onCleanup(() => {
unmount();
});
});

return (
<div
ref={(el) => {
ref = el;
}}
{...rest}
/>
);
};
24 changes: 15 additions & 9 deletions packages/js/src/ui/components/Inbox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ import { createMemo, createSignal, Match, Show, Switch } from 'solid-js';
import { useInboxContext } from '../context';
import { useStyle } from '../helpers';
import type {
BellMounter,
BellRenderer,
NotificationActionClickHandler,
NotificationClickHandler,
NotificationMounter,
NotificationRenderer,
} from '../types';
import { Bell, Footer, Header, Preferences, PreferencesHeader } from './elements';
import { InboxTabs } from './InboxTabs';
Expand All @@ -14,8 +14,8 @@ import { Button, Popover } from './primitives';

export type InboxProps = {
open?: boolean;
mountNotification?: NotificationMounter;
mountBell?: BellMounter;
renderNotification?: NotificationRenderer;
renderBell?: BellRenderer;
onNotificationClick?: NotificationClickHandler;
onPrimaryActionClick?: NotificationActionClickHandler;
onSecondaryActionClick?: NotificationActionClickHandler;
Expand All @@ -27,7 +27,7 @@ export enum InboxPage {
}

export type InboxContentProps = {
mountNotification?: NotificationMounter;
renderNotification?: NotificationRenderer;
onNotificationClick?: NotificationClickHandler;
onPrimaryActionClick?: NotificationActionClickHandler;
onSecondaryActionClick?: NotificationActionClickHandler;
Expand Down Expand Up @@ -60,15 +60,21 @@ export const InboxContent = (props: InboxContentProps) => {
when={tabs() && tabs().length > 0}
fallback={
<NotificationList
mountNotification={props.mountNotification}
renderNotification={props.renderNotification}
onNotificationClick={props.onNotificationClick}
onPrimaryActionClick={props.onPrimaryActionClick}
onSecondaryActionClick={props.onSecondaryActionClick}
filter={filter()}
/>
}
>
<InboxTabs tabs={tabs()} />
<InboxTabs
renderNotification={props.renderNotification}
onNotificationClick={props.onNotificationClick}
onPrimaryActionClick={props.onPrimaryActionClick}
onSecondaryActionClick={props.onSecondaryActionClick}
tabs={tabs()}
/>
</Show>
</Match>
<Match when={currentPage() === InboxPage.Preferences}>
Expand All @@ -89,13 +95,13 @@ export const Inbox = (props: InboxProps) => {
<Popover.Trigger
asChild={(triggerProps) => (
<Button class={style('inbox__popoverTrigger')} variant="ghost" size="icon" {...triggerProps}>
<Bell mountBell={props.mountBell} />
<Bell renderBell={props.renderBell} />
</Button>
)}
/>
<Popover.Content appearanceKey="inbox__popoverContent">
<InboxContent
mountNotification={props.mountNotification}
renderNotification={props.renderNotification}
onNotificationClick={props.onNotificationClick}
onPrimaryActionClick={props.onPrimaryActionClick}
onSecondaryActionClick={props.onSecondaryActionClick}
Expand Down
20 changes: 18 additions & 2 deletions packages/js/src/ui/components/InboxTabs/InboxTabs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,13 @@ import { createMemo, For, Show } from 'solid-js';
import { useInboxContext, useUnreadCounts } from '../../../ui/context';
import { cn, useStyle } from '../../helpers';
import { Check, Dots } from '../../icons';
import { NotificationStatus, Tab } from '../../types';
import {
NotificationActionClickHandler,
NotificationClickHandler,
NotificationRenderer,
NotificationStatus,
Tab,
} from '../../types';
import { NotificationList } from '../Notification';
import { Button, Dropdown, Tabs } from '../primitives';
import { tabsRootVariants } from '../primitives/Tabs/TabsRoot';
Expand All @@ -15,6 +21,10 @@ const tabsDropdownTriggerVariants = () =>
`after:nt-w-full after:nt-h-[2px] after:nt-border-b-2 nt-pb-[0.625rem]`;

type InboxTabsProps = {
renderNotification?: NotificationRenderer;
onNotificationClick?: NotificationClickHandler;
onPrimaryActionClick?: NotificationActionClickHandler;
onSecondaryActionClick?: NotificationActionClickHandler;
tabs: Array<Tab>;
};

Expand Down Expand Up @@ -101,7 +111,13 @@ export const InboxTabs = (props: InboxTabsProps) => {
cn(activeTab() === tab.label ? 'nt-block' : 'nt-hidden', 'nt-flex-1 nt-overflow-hidden')
)}
>
<NotificationList filter={{ ...filter(), tags: tab.value }} />
<NotificationList
renderNotification={props.renderNotification}
onNotificationClick={props.onNotificationClick}
onPrimaryActionClick={props.onPrimaryActionClick}
onSecondaryActionClick={props.onSecondaryActionClick}
filter={{ ...filter(), tags: tab.value }}
/>
</Tabs.Content>
))}
</Tabs.Root>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -202,9 +202,9 @@ export const DefaultNotification = (props: DefaultNotificationProps) => {
</div>
</div>
<Show when={props.notification.subject}>
<p class={style('notificationSubject', 'nt-font-semibold')}>{props.notification.subject}</p>
<p class={style('notificationSubject', 'nt-text-start nt-font-semibold')}>{props.notification.subject}</p>
</Show>
<p class={style('notificationBody')}>{props.notification.body}</p>
<p class={style('notificationBody', 'nt-text-start')}>{props.notification.body}</p>
<div class={style('notificationCustomActions', 'nt-flex nt-gap-4 nt-mt-4')}>
<Show when={props.notification.primaryAction} keyed>
{(primaryAction) => (
Expand Down
10 changes: 5 additions & 5 deletions packages/js/src/ui/components/Notification/Notification.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { Show } from 'solid-js';
import type { Notification as NotificationType } from '../../../notifications';
import type { NotificationActionClickHandler, NotificationClickHandler, NotificationMounter } from '../../types';
import { ExternalElementMounter } from '../ExternalElementMounter';
import type { NotificationActionClickHandler, NotificationClickHandler, NotificationRenderer } from '../../types';
import { ExternalElementRenderer } from '../ExternalElementRenderer';
import { DefaultNotification } from './DefaultNotification';

type NotificationProps = {
notification: NotificationType;
mountNotification?: NotificationMounter;
renderNotification?: NotificationRenderer;
onNotificationClick?: NotificationClickHandler;
onPrimaryActionClick?: NotificationActionClickHandler;
onSecondaryActionClick?: NotificationActionClickHandler;
Expand All @@ -15,7 +15,7 @@ type NotificationProps = {
export const Notification = (props: NotificationProps) => {
return (
<Show
when={props.mountNotification}
when={props.renderNotification}
fallback={
<DefaultNotification
notification={props.notification}
Expand All @@ -25,7 +25,7 @@ export const Notification = (props: NotificationProps) => {
/>
}
>
<ExternalElementMounter mount={(el) => props.mountNotification!(el, props.notification)} />
<ExternalElementRenderer render={(el) => props.renderNotification!(el, props.notification)} />
</Show>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { useNotificationsInfiniteScroll } from '../../api';
import { useLocalization, useNewMessagesCount } from '../../context';
import { useStyle } from '../../helpers';
import { EmptyIcon } from '../../icons/EmptyIcon';
import type { NotificationActionClickHandler, NotificationClickHandler, NotificationMounter } from '../../types';
import type { NotificationActionClickHandler, NotificationClickHandler, NotificationRenderer } from '../../types';
import { NewMessagesCta } from './NewMessagesCta';
import { Notification } from './Notification';
import { NotificationListSkeleton, NotificationSkeleton } from './NotificationListSkeleton';
Expand All @@ -27,7 +27,7 @@ const EmptyNotificationList = () => {
};

type NotificationListProps = {
mountNotification?: NotificationMounter;
renderNotification?: NotificationRenderer;
onNotificationClick?: NotificationClickHandler;
onPrimaryActionClick?: NotificationActionClickHandler;
onSecondaryActionClick?: NotificationActionClickHandler;
Expand Down Expand Up @@ -64,7 +64,7 @@ export const NotificationList = (props: NotificationListProps) => {
{(notification) => (
<Notification
notification={notification}
mountNotification={props.mountNotification}
renderNotification={props.renderNotification}
onNotificationClick={props.onNotificationClick}
onPrimaryActionClick={props.onPrimaryActionClick}
onSecondaryActionClick={props.onSecondaryActionClick}
Expand Down
10 changes: 5 additions & 5 deletions packages/js/src/ui/components/elements/Bell/Bell.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
import { Component, Show } from 'solid-js';
import { useTotalUnreadCount } from '../../../context';
import { BellMounter } from '../../../types';
import { ExternalElementMounter } from '../../ExternalElementMounter';
import { BellRenderer } from '../../../types';
import { ExternalElementRenderer } from '../../ExternalElementRenderer';
import { BellContainer } from './DefaultBellContainer';

type BellProps = {
mountBell?: BellMounter;
renderBell?: BellRenderer;
};
/* This is also going to be exported as a separate component. Keep it pure. */
export const Bell: Component<BellProps> = (props) => {
const { totalUnreadCount } = useTotalUnreadCount();

return (
<Show when={props.mountBell} fallback={<BellContainer unreadCount={totalUnreadCount()} />}>
<ExternalElementMounter mount={(el) => props.mountBell!(el, totalUnreadCount())} />
<Show when={props.renderBell} fallback={<BellContainer unreadCount={totalUnreadCount()} />}>
<ExternalElementRenderer render={(el) => props.renderBell!(el, totalUnreadCount())} />
</Show>
);
};
8 changes: 4 additions & 4 deletions packages/js/src/ui/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ import { appearanceKeys, defaultLocalization } from './config';
export type NotificationClickHandler = (notification: Notification) => void;
export type NotificationActionClickHandler = (notification: Notification) => void;

export type NotificationMounter = (el: HTMLDivElement, notification: Notification) => () => void;
export type BellMounter = (el: HTMLDivElement, unreadCount: number) => () => void;
export type NotificationRenderer = (el: HTMLDivElement, notification: Notification) => () => void;
export type BellRenderer = (el: HTMLDivElement, unreadCount: number) => () => void;

export type Tab = { label: string; value: Array<string> };

Expand Down Expand Up @@ -50,8 +50,8 @@ export type BaseNovuProviderProps = {
};

export type NovuProviderProps = BaseNovuProviderProps & {
mountNotification?: NotificationMounter;
mountBell?: BellMounter;
renderNotification?: NotificationRenderer;
renderBell?: BellRenderer;
};

export enum NotificationStatus {
Expand Down
2 changes: 1 addition & 1 deletion packages/react/src/components/Bell.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export const Bell = React.memo((props: BellProps) => {
return novuUI.mountComponent({
name: 'Bell',
element,
props: renderBell ? { mountBell: (el, unreadCount) => mountElement(el, renderBell(unreadCount)) } : undefined,
props: renderBell ? { renderBell: (el, unreadCount) => mountElement(el, renderBell(unreadCount)) } : undefined,
});
},
[renderBell]
Expand Down
4 changes: 2 additions & 2 deletions packages/react/src/components/Inbox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,10 @@ const DefaultInbox = (props: DefaultInboxProps) => {
name: 'Inbox',
props: {
open,
mountNotification: renderNotification
renderNotification: renderNotification
? (el, notification) => mountElement(el, renderNotification(notification))
: undefined,
mountBell: renderBell ? (el, unreadCount) => mountElement(el, renderBell(unreadCount)) : undefined,
renderBell: renderBell ? (el, unreadCount) => mountElement(el, renderBell(unreadCount)) : undefined,
onNotificationClick,
onPrimaryActionClick,
onSecondaryActionClick,
Expand Down
2 changes: 1 addition & 1 deletion packages/react/src/components/Notifications.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export const Notifications = React.memo((props: NotificationProps) => {
name: 'Notifications',
element,
props: {
mountNotification: renderNotification
renderNotification: renderNotification
? (el, notification) => mountElement(el, renderNotification(notification))
: undefined,
onNotificationClick,
Expand Down
3 changes: 3 additions & 0 deletions playground/nextjs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@
},
"dependencies": {
"@novu/react": "workspace:*",
"@radix-ui/colors": "^3.0.0",
"@radix-ui/react-icons": "^1.3.0",
"@radix-ui/react-popover": "^1.1.1",
"next": "14.2.4",
"react": "^18",
"react-dom": "^18"
Expand Down
2 changes: 2 additions & 0 deletions playground/nextjs/src/components/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ const LINKS: LinkType[] = [
{ href: '/render-notification', label: 'Render Notification' },
{ href: '/preferences', label: 'Preferences' },
{ href: '/notifications', label: 'Notifications' },
{ href: '/novu-theme', label: 'Novu Theme' },
{ href: '/custom-popover', label: 'Custom Popover' },
];

const NavLink = ({ href, label }: LinkType) => {
Expand Down
Loading

0 comments on commit cc519c5

Please sign in to comment.