Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DApp-1846 Notification toast for Channel notifications. #1875

Merged
merged 12 commits into from
Oct 9, 2024
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
"@mui/material": "^5.5.0",
"@pushprotocol/restapi": "1.7.25",
"@pushprotocol/socket": "0.5.3",
"@pushprotocol/uiweb": "1.4.3",
"@pushprotocol/uiweb": "2.0.0-exp.1",
"@radix-ui/react-dialog": "^1.1.1",
"@radix-ui/react-dropdown-menu": "^2.1.1",
"@radix-ui/react-switch": "^1.1.0",
Expand Down
4 changes: 4 additions & 0 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ import SpaceContextProvider from 'contexts/SpaceContext';
import { SpaceWidgetSection } from 'sections/space/SpaceWidgetSection';
import { blocksColors, getBlocksCSSVariables } from 'blocks';
import APP_PATHS from 'config/AppPaths';
import { CONSTANTS } from '@pushprotocol/restapi';
mishramonalisha76 marked this conversation as resolved.
Show resolved Hide resolved
import { useStream } from 'common';

dotenv.config();

Expand Down Expand Up @@ -336,6 +338,8 @@ export default function App() {
location?.pathname.includes('/snap') ||
location?.pathname.includes(APP_PATHS.DiscordVerification);

useStream();

return (
<ThemeProvider theme={darkMode ? themeDark : themeLight}>
{/* {(!isActive || !allowedChain) && (
Expand Down
83 changes: 2 additions & 81 deletions src/blocks/notification/Notification.tsx
Original file line number Diff line number Diff line change
@@ -1,91 +1,12 @@
import { FC } from 'react';
import styled from 'styled-components';
import { Cross } from '../icons';
import { NotificationProps } from './Notification.types';
import { toast } from 'sonner';
import { getTextVariantStyles } from 'blocks/Blocks.utils';

const NotificationContainer = styled.div`
mishramonalisha76 marked this conversation as resolved.
Show resolved Hide resolved
position: relative;
background-color: var(--components-in-app-notification-background-default);
border-radius: var(--radius-xxs);
box-shadow: 0px 4px 10px 0px rgba(0, 0, 0, 0.05);
display: flex;
flex-direction: row;
align-items: stretch;
max-height: 111px;
min-width: 397px;
max-width: 100%;
cursor: pointer;
box-sizing: border-box;
border: var(--border-sm) solid var(--components-in-app-notification-stroke-bg);
`;

const TextContainer = styled.div`
display: flex;
flex-direction: column;
align-items: flex-start;
justify-content: flex-start;
padding: var(--spacing-sm);
flex: 1;
box-sizing: border-box;
`;

const NotificationTitle = styled.span`
${() => getTextVariantStyles('h5-semibold', 'components-in-app-notification-text-default')}
`;

const NotificationDescription = styled.span`
${() => getTextVariantStyles('bes-regular', 'components-in-app-notification-text-secondary')}
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 3;
line-clamp: 3;
-webkit-box-orient: vertical;
`;

const IconContainer = styled.div`
padding: var(--spacing-sm) var(--spacing-xs);
border-radius: var(--radius-xxs) var(--radius-none) var(--radius-none) var(--radius-xxs);
background: radial-gradient(79.55% 79.55% at 50% 50%, #344efd 0%, #171717 100%);
`;

const CloseButton = styled.div`
background-color: var(--surface-transparent);
cursor: pointer;
color: var(--components-in-app-notification-icon-default);
padding: var(--spacing-none);
position: absolute;
right: var(--spacing-xxs);
top: var(--spacing-xxs);
`;

const Notification: FC<NotificationProps> = ({ onClose, title, description, image, onClick }) => {
const Notification: FC<NotificationProps> = ({ overlay, onClick }) => {
const handleNotificationClick = () => onClick?.();

const handleNotificationClose = () => {
onClose?.();
notification.hide();
};

return (
<NotificationContainer onClick={handleNotificationClick}>
<IconContainer>{image}</IconContainer>
<CloseButton
onClick={(e) => {
e.stopPropagation();
handleNotificationClose();
}}
>
<Cross size={16} />
</CloseButton>
<TextContainer>
<NotificationTitle>{title}</NotificationTitle>
<NotificationDescription>{description}</NotificationDescription>
</TextContainer>
</NotificationContainer>
);
return <div onClick={handleNotificationClick}>{overlay}</div>;
};

// Store the toastId(s) in an array to manage multiple notifications
Expand Down
10 changes: 2 additions & 8 deletions src/blocks/notification/Notification.types.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,10 @@
import { ReactNode } from 'react';

export type NotificationProps = {
/* Svg React component to be passed as the image. */
image: ReactNode;
/* Title of the notification */
title: string;
/* Description of the notification */
description: string;
/* Custom React component to be passed as the image. */
overlay: ReactNode;
/* Optional onClick event for the notification */
onClick?: () => void;
/* Optional onClose action for the notification */
onClose?: () => void;
/* Position of the notification */
position?: 'bottom-right' | 'bottom-left';
/* Optional duration of the notification component */
Expand Down
12 changes: 12 additions & 0 deletions src/common/Common.utils.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import { NotificationEvent } from '@pushprotocol/restapi';
import { LOGO_ALIAS_CHAIN } from './Common.constants';
import { networkName } from 'helpers/UtilityHelper';
import { NotificationToast } from './components';
import { notification } from 'blocks';

export const getSelectChains = (chainIdList: Array<number>) => {
return chainIdList?.map((key: number) => {
Expand All @@ -11,3 +14,12 @@ export const getSelectChains = (chainIdList: Array<number>) => {
};
});
};

export const handleNotificationToast = (data: NotificationEvent) => {
notification.show({
overlay: <NotificationToast notification={data} />,
});
// setTimeout(() => {
// notification.hide();
// }, 10000);
};
mishramonalisha76 marked this conversation as resolved.
Show resolved Hide resolved
97 changes: 97 additions & 0 deletions src/common/components/NotificationOverlay.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
import { FC, ReactNode } from 'react';
mishramonalisha76 marked this conversation as resolved.
Show resolved Hide resolved
import styled from 'styled-components';
import { Cross } from 'blocks';
import { getTextVariantStyles } from 'blocks/Blocks.utils';
import { Box } from 'blocks';
import { notification } from 'blocks';

const NotificationContainer = styled.div`
position: relative;
background-color: var(--components-in-app-notification-background-default);
border-radius: var(--radius-xxs);
box-shadow: 0px 4px 10px 0px rgba(0, 0, 0, 0.05);
display: flex;
flex-direction: row;
align-items: stretch;
max-height: 111px;
min-width: 397px;
max-width: 100%;
cursor: pointer;
box-sizing: border-box;
border: var(--border-sm) solid var(--components-in-app-notification-stroke-bg);
`;
const TextContainer = styled.div`
display: flex;
flex-direction: column;
align-items: flex-start;
justify-content: flex-start;
padding: var(--spacing-sm);
flex: 1;
box-sizing: border-box;
`;

const NotificationTitle = styled.span`
${() => getTextVariantStyles('h5-semibold', 'components-in-app-notification-text-default')}
`;

const NotificationDescription = styled.span`
${() => getTextVariantStyles('bes-regular', 'components-in-app-notification-text-secondary')}
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 3;
line-clamp: 3;
-webkit-box-orient: vertical;
`;

const IconContainer = styled.div`
padding: var(--spacing-sm) var(--spacing-xs);
border-radius: var(--radius-xxs) var(--radius-none) var(--radius-none) var(--radius-xxs);
background: radial-gradient(79.55% 79.55% at 50% 50%, #344efd 0%, #171717 100%);
`;

const CloseButton = styled.div`
background-color: var(--surface-transparent);
cursor: pointer;
color: var(--components-in-app-notification-icon-default);
padding: var(--spacing-none);
position: absolute;
right: var(--spacing-xxs);
top: var(--spacing-xxs);
`;

type NotificationOverlayProps = {
image: ReactNode;
/* Title of the notification */
title: string;
/* Description of the notification */
description: string;
/* Optional onClose action for the notification */
onClose?: () => void;
};

const NotificationOverlay: FC<NotificationOverlayProps> = ({ onClose, title, description, image }) => {
const handleNotificationClose = () => {
onClose?.();
notification.hide();
};
return (
<NotificationContainer>
<IconContainer>{image}</IconContainer>
<CloseButton
onClick={(e) => {
e.stopPropagation();
handleNotificationClose();
}}
>
<Cross size={16} />
</CloseButton>
<TextContainer>
<NotificationTitle>{title}</NotificationTitle>
<NotificationDescription>{description}</NotificationDescription>
</TextContainer>
</NotificationContainer>
);
};

export { NotificationOverlay };
36 changes: 36 additions & 0 deletions src/common/components/NotificationToast.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { NotificationEvent } from '@pushprotocol/restapi';
import { NotificationItem, chainNameType } from '@pushprotocol/uiweb';
import { Box } from 'blocks';
import { useBlocksTheme } from 'blocks/Blocks.hooks';
import { FC } from 'react';

type NotificationToastProps = {
notification: NotificationEvent | null;
};

const NotificationToast: FC<NotificationToastProps> = ({ notification }) => {
const payload = notification?.message?.payload;
const { mode } = useBlocksTheme();
return (
<Box
display="flex"
width="400px"
>
{notification && (
<NotificationItem
notificationTitle={payload?.title}
notificationBody={payload?.body}
cta={payload?.cta}
image={payload?.embed}
app={notification?.channel?.name}
icon={notification?.channel?.icon}
url={notification?.channel?.url}
chainName={notification?.source as chainNameType}
theme={mode}
/>
)}
</Box>
);
};

export { NotificationToast };
2 changes: 2 additions & 0 deletions src/common/components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,5 @@ export * from './UnsubscribeChannelDropdown';
export * from './ModalHeader';
export * from './StakingVariant';
export * from './TokenFaucet';
export * from './NotificationOverlay';
export * from './NotificationToast';
mishramonalisha76 marked this conversation as resolved.
Show resolved Hide resolved
1 change: 1 addition & 0 deletions src/common/hooks/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export { useIsVisible } from './useIsVisible';
export { usePushStakingStats } from './usePushStakingStats';
export { useDisclosure } from './useDisclosure';
export { useStream } from './useStream';
mishramonalisha76 marked this conversation as resolved.
Show resolved Hide resolved
57 changes: 57 additions & 0 deletions src/common/hooks/useStream.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { CONSTANTS, NotificationEvent } from '@pushprotocol/restapi';
import { useEffect, useState } from 'react';
import { notification } from 'blocks';
import { useSelector } from 'react-redux';
import { NotificationToast, handleNotificationToast } from 'common';

export const useStream = () => {
mishramonalisha76 marked this conversation as resolved.
Show resolved Hide resolved
const [isStreamConnected, setIsStreamConnected] = useState<boolean>(false);
const [notificationFeed, setNotificationFeed] = useState<NotificationEvent | null>(null);
const { userPushSDKInstance } = useSelector((state: any) => {
return state.user;
});
const attachListeners = async () => {
userPushSDKInstance?.stream?.on(CONSTANTS.STREAM.CONNECT, (err: Error) => {
console.debug(
'src::common::hooks::useStream::attachListeners::CONNECT::',
userPushSDKInstance?.uid,
userPushSDKInstance?.stream?.uid,
userPushSDKInstance?.stream
);
setIsStreamConnected(true);
});

userPushSDKInstance?.stream?.on(CONSTANTS.STREAM.DISCONNECT, (err: Error) => {
console.debug(
'src::common::hooks::useStream::attachListeners::DISCONNECT::',
userPushSDKInstance?.uid,
userPushSDKInstance?.stream?.uid,
userPushSDKInstance?.stream
);
setIsStreamConnected(false);
});
userPushSDKInstance?.stream?.on(CONSTANTS.STREAM.NOTIF, (data: NotificationEvent) => {
console.debug(
'src::common::hooks::useStream::attachListeners::NOTIF::',
userPushSDKInstance,
userPushSDKInstance?.uid,
userPushSDKInstance?.stream?.uid,
userPushSDKInstance?.stream
);
handleNotificationToast(data);
mishramonalisha76 marked this conversation as resolved.
Show resolved Hide resolved
setNotificationFeed(data);
mishramonalisha76 marked this conversation as resolved.
Show resolved Hide resolved
});
};

useEffect(() => {
(async () => {
if (userPushSDKInstance && userPushSDKInstance?.stream && userPushSDKInstance?.readmode())
await attachListeners();
})();

// Cleanup listener on unmount
return () => {};
}, [userPushSDKInstance]);

return { isStreamConnected, notificationFeed };
mishramonalisha76 marked this conversation as resolved.
Show resolved Hide resolved
};
Loading
Loading