diff --git a/CHANGELOG.md b/CHANGELOG.md
index 72d3557..7100124 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,7 +2,16 @@
All notable changes to this project will be documented in this file.
-## [1.0.0] - 2024-03-26
+## 1.1.0
+
+### Added
+- Added support for custom delete icon and a flag to toggle the visibility of the delete icon.
+- Added functionality to display thumbnail URL previews for media content.
+- Exposed avatar click property.
+- Implemented specific error code mapping.
+- Enhanced style and theme customizations.
+
+## 1.0.0
### Added
diff --git a/README.md b/README.md
index 7645c49..14db430 100644
--- a/README.md
+++ b/README.md
@@ -112,7 +112,7 @@ theme | Object for custom themes | Theme | {} |
customStyles | Object for custom styling | CustomStyleProps | {} |
darkMode | Toggle to enable dark mode| boolean | false |
itemsPerFetch | Number of notifications fetch per api request (have a max cap of 50) | number | 20 |
-cardProps | Props for customizing the card | CardProps | { hideAvatar: false, disableAutoMarkAsRead: false, hideDelete: false, deleteIcon: JSX.Element, onAvatarClick: ()=> null } |
+cardProps | Props for customizing the card | CardProps | { hideAvatar: false, disableAutoMarkAsRead: false, hideDelete: false, deleteIcon: JSX.Element, onAvatarClick: ()=> null, hideMediaThumbnailL: false, onMediaThumbnailClick: ()=> null} |
customCard | Function for rendering custom card | (notification)=> JSX Element | null |
onCardClick | Custom click handler for card | (notification)=> void | ()=>null |
listEmptyComponent | Custom component for empty list | JSX Element | null |
@@ -200,7 +200,7 @@ Here are the custom style options for the notification inbox:
deleteIcon?:{
size?: number
};
- dateIcon?:{
+ timerIcon?:{
size?: number
};
clearAllIcon?:{
@@ -216,6 +216,8 @@ Here are the custom style options for the notification inbox:
disableAutoMarkAsRead?: boolean;
deleteIcon?: JSX.Element;
hideDelete?: boolean;
+ hideMediaThumbnail?: boolean;
+ onMediaThumbnailClick?: (notification: NotificationDataType) => void;
};
```
@@ -246,10 +248,6 @@ function MyComponent() {
markAsReadById(id);
}
- function handleDeleteNotification(id) {
- deleteById(id);
- }
-
return (
{/* Your component logic */}
);
diff --git a/example/package.json b/example/package.json
index f119386..3940b67 100644
--- a/example/package.json
+++ b/example/package.json
@@ -13,7 +13,7 @@
"@react-navigation/bottom-tabs": "^6.5.14",
"@react-navigation/native": "^6.1.12",
"@react-navigation/native-stack": "^6.9.20",
- "@sirenapp/react-native-inbox": "^1.0.0",
+ "@sirenapp/react-native-inbox": "^1.1.0",
"react": "18.2.0",
"react-native": "0.73.4",
"react-native-modal": "^13.0.1",
diff --git a/package-lock.json b/package-lock.json
index b024c42..f77497c 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -9,7 +9,7 @@
"version": "1.0.0",
"license": "MIT",
"dependencies": {
- "@sirenapp/js-sdk": "^1.0.0",
+ "@sirenapp/js-sdk": "^1.1.0",
"pubsub-js": "^1.9.4"
},
"devDependencies": {
@@ -4827,9 +4827,9 @@
}
},
"node_modules/@sirenapp/js-sdk": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/@sirenapp/js-sdk/-/js-sdk-1.0.0.tgz",
- "integrity": "sha512-zypXxRdTNkWRzJhiIYPxlmrpIj2vMjh5H2u0Bbrw0uI+RPLap47HxKBMGMfaPwFP9UxdoZUuRze3Bizj4vt6AQ==",
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@sirenapp/js-sdk/-/js-sdk-1.1.0.tgz",
+ "integrity": "sha512-4V6nltHLhPLOsPMQO7OqllUeFEELvgeb8RSPb/uuwZ1qr05pClQEgoCy5/AlX0glsfHCdoQUfAyWt3akJEenrw==",
"dependencies": {
"promise-polyfill": "^8.3.0",
"tslib": "^2.6.2",
@@ -19030,9 +19030,9 @@
}
},
"@sirenapp/js-sdk": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/@sirenapp/js-sdk/-/js-sdk-1.0.0.tgz",
- "integrity": "sha512-zypXxRdTNkWRzJhiIYPxlmrpIj2vMjh5H2u0Bbrw0uI+RPLap47HxKBMGMfaPwFP9UxdoZUuRze3Bizj4vt6AQ==",
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@sirenapp/js-sdk/-/js-sdk-1.1.0.tgz",
+ "integrity": "sha512-4V6nltHLhPLOsPMQO7OqllUeFEELvgeb8RSPb/uuwZ1qr05pClQEgoCy5/AlX0glsfHCdoQUfAyWt3akJEenrw==",
"requires": {
"promise-polyfill": "^8.3.0",
"tslib": "^2.6.2",
diff --git a/package.json b/package.json
index ac91656..6ffad70 100644
--- a/package.json
+++ b/package.json
@@ -78,6 +78,6 @@
],
"dependencies": {
"pubsub-js": "^1.9.4",
- "@sirenapp/js-sdk": "^1.0.0"
+ "@sirenapp/js-sdk": "^1.1.0"
}
}
diff --git a/src/assets/failedImageDark.png b/src/assets/failedImageDark.png
new file mode 100644
index 0000000..3f5f056
Binary files /dev/null and b/src/assets/failedImageDark.png differ
diff --git a/src/assets/failedImageLight.png b/src/assets/failedImageLight.png
new file mode 100644
index 0000000..74ab4a8
Binary files /dev/null and b/src/assets/failedImageLight.png differ
diff --git a/src/components/card.tsx b/src/components/card.tsx
index 92eb4a4..dc3e389 100644
--- a/src/components/card.tsx
+++ b/src/components/card.tsx
@@ -4,6 +4,7 @@ import { Animated, Image, StyleSheet, Text, TouchableOpacity, View } from 'react
import type { NotificationCardProps } from '../types';
import { CommonUtils, useSiren } from '../utils';
import { eventTypes, events } from '../utils/constants';
+import { useSirenContext } from './sirenProvider';
import CloseIcon from './closeIcon';
import TimerIcon from './timerIcon';
@@ -47,9 +48,12 @@ const Card = (props: NotificationCardProps): ReactElement => {
disableAutoMarkAsRead,
hideDelete = false,
onAvatarClick,
- deleteIcon = null
+ deleteIcon = null,
+ hideMediaThumbnail = false,
+ onMediaThumbnailClick
} = cardProps;
const { markAsReadById } = useSiren();
+ const { id: providerId } = useSirenContext();
const opacity = useRef(new Animated.Value(1)).current;
@@ -57,18 +61,26 @@ const Card = (props: NotificationCardProps): ReactElement => {
return darkMode ? require('../assets/emptyDark.png') : require('../assets/emptyLight.png');
};
+ const failedState = () => {
+ return darkMode
+ ? require('../assets/failedImageDark.png')
+ : require('../assets/failedImageLight.png');
+ };
+
+ const avatarUrl = notification?.message?.avatar?.imageUrl || '';
+ const thumbnailUrl = notification?.message?.thumbnailUrl || '';
+
const [imageSource, setImageSource] = useState(
- notification?.message?.avatar?.imageUrl?.length > 0
- ? { uri: notification.message?.avatar?.imageUrl }
- : emptyState()
+ avatarUrl?.length > 0 ? { uri: avatarUrl } : emptyState()
+ );
+
+ const [mediaSource, setMediaSource] = useState(
+ thumbnailUrl?.length > 0 ? { uri: thumbnailUrl } : emptyState()
);
useEffect(() => {
- setImageSource(
- notification?.message?.avatar?.imageUrl?.length > 0
- ? { uri: notification.message?.avatar?.imageUrl }
- : emptyState()
- );
+ setImageSource(avatarUrl?.length > 0 ? { uri: avatarUrl } : emptyState());
+ setMediaSource(thumbnailUrl?.length > 0 ? { uri: thumbnailUrl } : failedState());
}, [notification, darkMode]);
const cardClick = (): void => {
@@ -80,10 +92,18 @@ const Card = (props: NotificationCardProps): ReactElement => {
setImageSource(emptyState());
};
+ const onErrorMedia = (): void => {
+ setMediaSource(failedState());
+ };
+
const avatarClick = () => {
if (onAvatarClick) onAvatarClick(notification);
};
+ const mediaClick = () => {
+ if (onMediaThumbnailClick) onMediaThumbnailClick(notification);
+ };
+
const renderAvatar = useMemo((): JSX.Element => {
return (
@@ -104,6 +124,18 @@ const Card = (props: NotificationCardProps): ReactElement => {
);
}, [styles, darkMode, imageSource, onAvatarClick]);
+ const renderMediaThumbnail = useMemo((): JSX.Element => {
+ return (
+
+
+
+ );
+ }, [darkMode, mediaSource, onMediaThumbnailClick]);
+
const onDeleteItem = async (): Promise => {
const isSuccess = await onDelete(notification.id, false);
@@ -115,7 +147,7 @@ const Card = (props: NotificationCardProps): ReactElement => {
}).start(() => {
const payload = { id: notification.id, action: eventTypes.DELETE_ITEM };
- PubSub.publish(events.NOTIFICATION_LIST_EVENT, JSON.stringify(payload));
+ PubSub.publish(`${events.NOTIFICATION_LIST_EVENT}${providerId}`, JSON.stringify(payload));
});
};
@@ -141,19 +173,28 @@ const Card = (props: NotificationCardProps): ReactElement => {
{notification.message?.header}
- {!hideDelete &&
- (deleteIcon || (
-
- ))}
+ {!hideDelete && (
+
+ )}
{Boolean(notification.message?.subHeader) && (
{notification.message?.subHeader}
)}
-
- {notification.message?.body}
-
+ {Boolean(notification.message?.body) && (
+
+ {notification.message?.body}
+
+ )}
+ {!hideMediaThumbnail &&
+ Boolean(notification.message?.thumbnailUrl) &&
+ renderMediaThumbnail}
@@ -228,6 +269,14 @@ const style = StyleSheet.create({
},
transparent: {
backgroundColor: 'transparent'
+ },
+ mediaContainer: {
+ width: '100%',
+ height: 130,
+ borderRadius: 6,
+ marginBottom: 10,
+ overflow: 'hidden',
+ backgroundColor: '#D3D3D3'
}
});
diff --git a/src/components/closeIcon.tsx b/src/components/closeIcon.tsx
index d08bfde..f9da79a 100644
--- a/src/components/closeIcon.tsx
+++ b/src/components/closeIcon.tsx
@@ -8,10 +8,12 @@ import type { StyleProps } from '../types';
const CloseIcon = ({
notification,
styles,
+ customIcon,
onDelete
}: {
notification: NotificationDataType;
styles: Partial;
+ customIcon?: ReactElement | null;
onDelete: (id: string) => void;
}): ReactElement => {
const icon: JSX.Element[] = [];
@@ -36,7 +38,7 @@ const CloseIcon = ({
testID='delete-button'
accessibilityLabel={`siren-notification-delete${notification.id}`}
>
- <>{icon}>
+ <>{customIcon || icon}>
);
};
diff --git a/src/components/sirenInbox.tsx b/src/components/sirenInbox.tsx
index aa7c6e7..ecea810 100644
--- a/src/components/sirenInbox.tsx
+++ b/src/components/sirenInbox.tsx
@@ -22,6 +22,7 @@ const {
TOKEN_VERIFICATION_PENDING,
MAXIMUM_ITEMS_PER_FETCH,
VerificationStatus,
+ EventType,
errorMap
} = Constants;
const { applyTheme, isNonEmptyArray, updateNotifications } = CommonUtils;
@@ -76,7 +77,8 @@ const SirenInbox = (props: SirenInboxProps): ReactElement => {
cardProps = {
hideAvatar: false,
disableAutoMarkAsRead: false,
- hideDelete: false
+ hideDelete: false,
+ hideMediaThumbnail: false,
},
listEmptyComponent = null,
headerProps = {},
@@ -103,7 +105,7 @@ const SirenInbox = (props: SirenInboxProps): ReactElement => {
itemsPerFetch > MAXIMUM_ITEMS_PER_FETCH ? MAXIMUM_ITEMS_PER_FETCH : itemsPerFetch
);
- const { siren, verificationStatus } = useSirenContext();
+ const { siren, verificationStatus, id } = useSirenContext();
const { deleteById, deleteByDate, markAllAsViewed } = useSiren();
@@ -121,19 +123,20 @@ const SirenInbox = (props: SirenInboxProps): ReactElement => {
const disableCardDelete = useRef(false);
useEffect(() => {
- PubSub.subscribe(events.NOTIFICATION_LIST_EVENT, notificationSubscriber);
+ PubSub.subscribe(`${events.NOTIFICATION_LIST_EVENT}${id}`, notificationSubscriber);
return cleanUp();
}, []);
useEffect(() => {
// Initialize Siren SDK and start polling notifications
- if (verificationStatus !== VerificationStatus.PENDING && siren) {
+ if (verificationStatus === VerificationStatus.SUCCESS && siren) {
initialize();
} else if(verificationStatus === VerificationStatus.FAILED) {
setIsError(true);
setIsLoading(false);
- if (onError) onError(errorMap.MISSING_PARAMETER);
+ setNotifications([]);
+ if (onError) onError(errorMap.INVALID_CREDENTIALS);
}
}, [siren, verificationStatus]);
@@ -149,7 +152,7 @@ const SirenInbox = (props: SirenInboxProps): ReactElement => {
}
}, [eventListenerData]);
- const handleMarkNotificationsAsViewed = async (newNotifications = notifications) => {
+ const handleMarkAllAsViewed = async (newNotifications = notifications) => {
const currentTimestamp = new Date().getTime();
const isoString = new Date(currentTimestamp).toISOString();
const response = await markAllAsViewed(
@@ -168,10 +171,10 @@ const SirenInbox = (props: SirenInboxProps): ReactElement => {
// Clean up - stop polling when component unmounts
const cleanUp = () => () => {
- siren?.stopRealTimeNotificationFetch();
+ siren?.stopRealTimeFetch(EventType.NOTIFICATION);
setNotifications([]);
- PubSub.unsubscribe(events.NOTIFICATION_LIST_EVENT);
- handleMarkNotificationsAsViewed();
+ PubSub.unsubscribe(`${events.NOTIFICATION_LIST_EVENT}${id}`);
+ handleMarkAllAsViewed();
};
const notificationSubscriber = async (type: string, dataString: string) => {
@@ -183,7 +186,7 @@ const SirenInbox = (props: SirenInboxProps): ReactElement => {
// Initialize Siren SDK and fetch notifications
const initialize = async (): Promise => {
if (siren) {
- siren?.stopRealTimeNotificationFetch();
+ siren?.stopRealTimeFetch(EventType.NOTIFICATION);
const allNotifications = await fetchNotifications(siren, true);
const notificationParams: fetchProps = { size: notificationsPerPage };
@@ -191,7 +194,7 @@ const SirenInbox = (props: SirenInboxProps): ReactElement => {
notificationParams.start = allNotifications[0].createdAt;
if (verificationStatus === VerificationStatus.SUCCESS)
- siren?.startRealTimeNotificationFetch(notificationParams);
+ siren?.startRealTimeFetch({eventType: EventType.NOTIFICATION, params: notificationParams});
}
};
@@ -214,7 +217,7 @@ const SirenInbox = (props: SirenInboxProps): ReactElement => {
if (nonEmptyResponse) {
const updatedNotifications = isResetList ? responseData : [...notifications, ...responseData];
- isResetList && handleMarkNotificationsAsViewed(updatedNotifications);
+ isResetList && handleMarkAllAsViewed(updatedNotifications);
setNotifications(updatedNotifications);
return updatedNotifications;
@@ -273,7 +276,7 @@ const SirenInbox = (props: SirenInboxProps): ReactElement => {
setNotifications([]);
setIsLoading(true);
- siren?.stopRealTimeNotificationFetch();
+ siren?.stopRealTimeFetch(EventType.NOTIFICATION);
const allNotifications = (await fetchNotifications(siren, true)) || [];
const notificationParams: fetchProps = { size: notificationsPerPage };
@@ -281,7 +284,7 @@ const SirenInbox = (props: SirenInboxProps): ReactElement => {
notificationParams.start = allNotifications[0].createdAt;
if (verificationStatus === VerificationStatus.SUCCESS)
- siren?.startRealTimeNotificationFetch(notificationParams);
+ siren?.startRealTimeFetch({eventType: EventType.NOTIFICATION, params:notificationParams});
} catch (err) {
setIsLoading(false);
setIsError(true);
diff --git a/src/components/sirenInboxIcon.tsx b/src/components/sirenInboxIcon.tsx
index 6481c8a..355914c 100644
--- a/src/components/sirenInboxIcon.tsx
+++ b/src/components/sirenInboxIcon.tsx
@@ -14,6 +14,7 @@ const {
eventTypes,
events,
defaultStyles,
+ EventType,
VerificationStatus,
errorMap
} = Constants;
@@ -50,7 +51,7 @@ const SirenInboxIcon = (props: SirenInboxIconProps) => {
onError = () => null
} = props;
- const { siren, verificationStatus } = useSirenContext();
+ const { siren, verificationStatus, id } = useSirenContext();
const [unviewedCount, seUnviewedCount] = useState(0);
@@ -67,8 +68,8 @@ const SirenInboxIcon = (props: SirenInboxIconProps) => {
// Clean up - stop polling when component unmounts
const cleanUp = () => () => {
- siren?.stopRealTimeUnviewedCountFetch();
- PubSub.unsubscribe(events.NOTIFICATION_COUNT_EVENT);
+ siren?.stopRealTimeFetch(EventType.UNVIEWED_COUNT);
+ PubSub.unsubscribe(`${events.NOTIFICATION_COUNT_EVENT}${id}`);
seUnviewedCount(0);
};
@@ -81,7 +82,7 @@ const SirenInboxIcon = (props: SirenInboxIconProps) => {
};
useEffect(() => {
- PubSub.subscribe(events.NOTIFICATION_COUNT_EVENT, notificationSubscriber);
+ PubSub.subscribe(`${events.NOTIFICATION_COUNT_EVENT}${id}`, notificationSubscriber);
return cleanUp();
}, []);
@@ -102,7 +103,7 @@ const SirenInboxIcon = (props: SirenInboxIconProps) => {
const unViewed: UnviewedCountReturnResponse | null =
await siren.fetchUnviewedNotificationsCount();
- siren.startRealTimeUnviewedCountFetch();
+ siren.startRealTimeFetch({eventType: EventType.UNVIEWED_COUNT});
if (unViewed?.data) seUnviewedCount(unViewed.data?.unviewedCount || 0);
if (unViewed?.error) onError(unViewed?.error);
}
diff --git a/src/components/sirenProvider.tsx b/src/components/sirenProvider.tsx
index e26df72..c9f8999 100644
--- a/src/components/sirenProvider.tsx
+++ b/src/components/sirenProvider.tsx
@@ -7,23 +7,25 @@ import type {
NotificationDataType,
NotificationsApiResponse,
SirenErrorType,
- UnviewedCountApiResponse,
+ UnviewedCountApiResponse
} from '@sirenapp/js-sdk/dist/esm/types';
import type { SirenProviderConfigProps } from '../types';
-import { isNonEmptyArray, logger } from '../utils/commonUtils';
+import { generateUniqueId, isNonEmptyArray, logger } from '../utils/commonUtils';
import {
events,
eventTypes,
IN_APP_RECIPIENT_UNAUTHENTICATED,
MAXIMUM_RETRY_COUNT,
- VerificationStatus
+ VerificationStatus,
+ EventType
} from '../utils/constants';
import { useSiren } from '../utils';
type SirenContextProp = {
siren: Siren | null;
verificationStatus: VerificationStatus;
+ id: string;
};
interface SirenProvider {
@@ -33,7 +35,8 @@ interface SirenProvider {
export const SirenContext = createContext({
siren: null,
- verificationStatus: VerificationStatus.PENDING
+ verificationStatus: VerificationStatus.PENDING,
+ id: ''
});
/**
@@ -74,10 +77,12 @@ const SirenProvider: React.FC = ({ config, children }) => {
let retryCount = 0;
const { markAllAsViewed } = useSiren();
-
+
+ const [id] = useState(generateUniqueId());
const [siren, setSiren] = useState(null);
- const [verificationStatus, setVerificationStatus] = useState(VerificationStatus.PENDING);
-
+ const [verificationStatus, setVerificationStatus] = useState(
+ VerificationStatus.PENDING
+ );
useEffect(() => {
if (config?.recipientId && config?.userToken) {
@@ -90,8 +95,8 @@ const SirenProvider: React.FC = ({ config, children }) => {
}, [config]);
const stopRealTimeFetch = (): void => {
- siren?.stopRealTimeNotificationFetch();
- siren?.stopRealTimeUnviewedCountFetch();
+ siren?.stopRealTimeFetch(EventType.NOTIFICATION);
+ siren?.stopRealTimeFetch(EventType.UNVIEWED_COUNT);
};
const sendResetDataEvents = () => {
@@ -102,40 +107,61 @@ const SirenProvider: React.FC = ({ config, children }) => {
action: eventTypes.RESET_NOTIFICATIONS
};
- PubSub.publish(events.NOTIFICATION_COUNT_EVENT, JSON.stringify(updateCountPayload));
- PubSub.publish(events.NOTIFICATION_LIST_EVENT, JSON.stringify(updateNotificationPayload));
+ PubSub.publish(`${events.NOTIFICATION_COUNT_EVENT}${id}`, JSON.stringify(updateCountPayload));
+ PubSub.publish(`${events.NOTIFICATION_LIST_EVENT}${id}`, JSON.stringify(updateNotificationPayload));
};
- const onUnViewedCountReceived = (response: UnviewedCountApiResponse): void => {
- const totalUnviewed = response?.data?.totalUnviewed;
+ const onNewNotificationEvent = (responseData: NotificationDataType[]) => {
+ logger.info(`new notifications : ${JSON.stringify(responseData)}`);
+
+ markAllAsViewed(responseData[0].createdAt);
+ const payload = { newNotifications: responseData, action: eventTypes.NEW_NOTIFICATIONS };
+
+ PubSub.publish(`${events.NOTIFICATION_LIST_EVENT}${id}`, JSON.stringify(payload));
+ };
+ const onTotalUnviewedCountEvent = (response: UnviewedCountApiResponse) => {
+ const totalUnviewed = response.data?.totalUnviewed;
const payload = {
unviewedCount: totalUnviewed,
action: eventTypes.UPDATE_NOTIFICATIONS_COUNT
};
- PubSub.publish(events.NOTIFICATION_COUNT_EVENT, JSON.stringify(payload));
+ PubSub.publish(`${events.NOTIFICATION_COUNT_EVENT}${id}`, JSON.stringify(payload));
};
- const onNotificationReceived = (response: NotificationsApiResponse): void => {
- const responseData: NotificationDataType[] = response?.data || [];
+ const handleNotificationEvent = (response: NotificationsApiResponse) => {
+ const responseData = response?.data;
- if (isNonEmptyArray(responseData)) {
- logger.info(`new notifications : ${JSON.stringify(responseData)}`);
-
- markAllAsViewed(responseData[0].createdAt);
- const payload = { newNotifications: response?.data, action: eventTypes.NEW_NOTIFICATIONS };
+ if (Array.isArray(responseData) && isNonEmptyArray(responseData))
+ onNewNotificationEvent(responseData);
- PubSub.publish(events.NOTIFICATION_LIST_EVENT, JSON.stringify(payload));
- }
};
+ const handleUnviewedCountEvent = (response: UnviewedCountApiResponse) => {
+ const responseData = response?.data;
+ if (responseData && 'totalUnviewed' in responseData)
+ onTotalUnviewedCountEvent(response);
+
+ };
+ const onEventReceive = (
+ response: NotificationsApiResponse | UnviewedCountApiResponse = {},
+ eventType: EventType
+ ) => {
+ switch (eventType) {
+ case EventType.NOTIFICATION:
+ handleNotificationEvent(response as NotificationsApiResponse);
+ break;
+ case EventType.UNVIEWED_COUNT:
+ handleUnviewedCountEvent(response as UnviewedCountApiResponse);
+ break;
+ }
+ };
const onStatusChange = (status: VerificationStatus) => {
setVerificationStatus(status);
};
-
- const actionCallbacks = { onUnViewedCountReceived, onNotificationReceived, onStatusChange };
+ const actionCallbacks = { onEventReceive, onStatusChange };
const getDataParams = () => {
return {
@@ -147,7 +173,11 @@ const SirenProvider: React.FC = ({ config, children }) => {
};
const retryVerification = (error: SirenErrorType) => {
- if (error.Code === IN_APP_RECIPIENT_UNAUTHENTICATED && retryCount < MAXIMUM_RETRY_COUNT)
+ if (
+ error.Code === IN_APP_RECIPIENT_UNAUTHENTICATED &&
+ retryCount < MAXIMUM_RETRY_COUNT &&
+ verificationStatus === VerificationStatus.FAILED
+ )
setTimeout(() => {
initialize();
retryCount++;
@@ -158,6 +188,7 @@ const SirenProvider: React.FC = ({ config, children }) => {
// Function to initialize the Siren SDK and fetch notifications
const initialize = (): void => {
+ setVerificationStatus(VerificationStatus.PENDING);
const dataParams: InitConfigType = getDataParams();
const siren = new Siren(dataParams);
@@ -167,6 +198,7 @@ const SirenProvider: React.FC = ({ config, children }) => {
return (
void;
};
/**
@@ -182,7 +184,7 @@ export type CustomStyleProps = {
deleteIcon?:{
size?: number
};
- dateIcon?:{
+ timerIcon?:{
size?: number
};
clearAllIcon?:{
@@ -245,4 +247,5 @@ export type StyleProps = {
skeltonLoaderColor: ViewStyle;
highlighted: ViewStyle;
backIcon: ViewStyle;
+ mediaContainer: ViewStyle;
};
diff --git a/src/utils/commonUtils.ts b/src/utils/commonUtils.ts
index 5718509..4ba25a7 100644
--- a/src/utils/commonUtils.ts
+++ b/src/utils/commonUtils.ts
@@ -8,6 +8,10 @@ import { DefaultTheme } from './index';
export const isNonEmptyArray = (arr?: unknown[] | null) =>
Boolean(arr && typeof arr === 'object' && arr instanceof Array && arr.length > 0);
+export const generateUniqueId = (): string => {
+ return Math.random().toString(36).substring(2, 15);
+};
+
export const updateNotifications = (
eventData: {
id?: string;
@@ -157,23 +161,23 @@ export const applyTheme = (
},
timerIcon: {
borderColor: theme.colors?.timerIcon || DefaultTheme[mode].colors.timerIcon,
- width: customStyles.dateIcon?.size || defaultStyles.dateIcon.size,
- height: customStyles.dateIcon?.size || defaultStyles.dateIcon.size,
- borderRadius: (customStyles.dateIcon?.size || defaultStyles.dateIcon.size) / 2,
- borderWidth: (customStyles.dateIcon?.size || defaultStyles.dateIcon.size) * 0.1,
- padding: (customStyles.dateIcon?.size || defaultStyles.dateIcon.size) * 0.1
+ width: customStyles.timerIcon?.size || defaultStyles.timerIcon.size,
+ height: customStyles.timerIcon?.size || defaultStyles.timerIcon.size,
+ borderRadius: (customStyles.timerIcon?.size || defaultStyles.timerIcon.size) / 2,
+ borderWidth: (customStyles.timerIcon?.size || defaultStyles.timerIcon.size) * 0.1,
+ padding: (customStyles.timerIcon?.size || defaultStyles.timerIcon.size) * 0.1
},
timerIconLine: {
backgroundColor: theme.colors?.timerIcon || DefaultTheme[mode].colors.timerIcon,
- height: (customStyles.dateIcon?.size || defaultStyles.dateIcon.size) * 0.36,
- width: (customStyles.dateIcon?.size || defaultStyles.dateIcon.size) * 0.1
+ height: (customStyles.timerIcon?.size || defaultStyles.timerIcon.size) * 0.36,
+ width: (customStyles.timerIcon?.size || defaultStyles.timerIcon.size) * 0.1
},
timerIconLine2: {
backgroundColor: theme.colors?.timerIcon || DefaultTheme[mode].colors.timerIcon,
- height: (customStyles.dateIcon?.size || defaultStyles.dateIcon.size) * 0.28,
- width: (customStyles.dateIcon?.size || defaultStyles.dateIcon.size) * 0.1,
- marginLeft: (customStyles.dateIcon?.size || defaultStyles.dateIcon.size) * 0.22,
- marginTop: -(customStyles.dateIcon?.size || defaultStyles.dateIcon.size) * 0.1
+ height: (customStyles.timerIcon?.size || defaultStyles.timerIcon.size) * 0.28,
+ width: (customStyles.timerIcon?.size || defaultStyles.timerIcon.size) * 0.1,
+ marginLeft: (customStyles.timerIcon?.size || defaultStyles.timerIcon.size) * 0.22,
+ marginTop: -(customStyles.timerIcon?.size || defaultStyles.timerIcon.size) * 0.1
},
cardContainer: {
padding: customStyles.notificationCard?.padding || defaultStyles.notificationCard.padding
@@ -277,5 +281,8 @@ export const applyTheme = (
theme.windowHeader?.titleColor ||
theme.colors?.textColor ||
DefaultTheme[mode].windowHeader.titleColor
- }
+ },
+ mediaContainer: {
+ backgroundColor: DefaultTheme[mode].notificationCard.mediaContainerBackground
+ },
});
diff --git a/src/utils/constants.ts b/src/utils/constants.ts
index 448b9ba..99210e5 100644
--- a/src/utils/constants.ts
+++ b/src/utils/constants.ts
@@ -9,7 +9,8 @@ export const COLORS = {
deleteIcon: '#34405499',
timerIcon: '#667185',
clearAllIcon: '#667185',
- infiniteLoader: '#F56630'
+ infiniteLoader: '#F56630',
+ imageBackground: '#F0F2F5'
},
dark: {
primaryColor: '#FA9874',
@@ -18,10 +19,11 @@ export const COLORS = {
neutralColor: '#232326',
borderColor: '#344054',
dateColor: '#98A2B3',
- deleteIcon: '#98A2B3',
+ deleteIcon: '#D0D5DD',
timerIcon: '#98A2B3',
clearAllIcon: '#D0D5DD',
- infiniteLoader: '#F56630'
+ infiniteLoader: '#F56630',
+ imageBackground: '#4C4C4C'
}
};
@@ -58,6 +60,11 @@ export const levelLogFns = {
[LogLevel.ERROR]: console.error
};
+export enum EventType {
+ NOTIFICATION = "NOTIFICATIONS",
+ UNVIEWED_COUNT = "UNVIEWED_COUNT"
+}
+
export enum eventTypes {
MARK_ITEM_AS_VIEWED = 'MARK_ITEM_AS_VIEWED',
MARK_ITEM_AS_READ = 'MARK_ITEM_AS_READ',
@@ -142,7 +149,7 @@ export const defaultStyles = {
deleteIcon: {
size: 14
},
- dateIcon: {
+ timerIcon: {
size: 12
},
clearAllIcon: {
diff --git a/src/utils/defaultTheme.ts b/src/utils/defaultTheme.ts
index 106ded0..273e381 100644
--- a/src/utils/defaultTheme.ts
+++ b/src/utils/defaultTheme.ts
@@ -30,6 +30,7 @@ const defaultTheme = {
subTitleColor: COLORS[ThemeMode.LIGHT].textColor,
descriptionColor: COLORS[ThemeMode.LIGHT].textColor,
dateColor: COLORS[ThemeMode.LIGHT].textColor,
+ mediaContainerBackground: COLORS[ThemeMode.LIGHT].imageBackground,
}
},
dark: {
@@ -60,7 +61,8 @@ const defaultTheme = {
titleColor: COLORS[ThemeMode.DARK].textColor,
subTitleColor: COLORS[ThemeMode.DARK].textColor,
descriptionColor: COLORS[ThemeMode.DARK].textColor,
- dateColor: COLORS[ThemeMode.DARK].dateColor
+ dateColor: COLORS[ThemeMode.DARK].dateColor,
+ mediaContainerBackground: COLORS[ThemeMode.DARK].imageBackground,
}
}
};
diff --git a/src/utils/sirenHook.ts b/src/utils/sirenHook.ts
index f6c799e..f863b17 100644
--- a/src/utils/sirenHook.ts
+++ b/src/utils/sirenHook.ts
@@ -4,17 +4,17 @@ import { errorMap, events, eventTypes } from './constants';
import { useSirenContext } from '../components/sirenProvider';
const useSiren = () => {
- const { siren } = useSirenContext();
+ const { siren, id: providerId } = useSirenContext();
const markAsReadById = async (id: string) => {
if (siren)
if (id?.length > 0) {
- const response = await siren?.markNotificationAsReadById(id);
+ const response = await siren?.markAsReadById(id);
if (response?.data) {
const payload = { id, action: eventTypes.MARK_ITEM_AS_READ };
- PubSub.publish(events.NOTIFICATION_LIST_EVENT, JSON.stringify(payload));
+ PubSub.publish(`${events.NOTIFICATION_LIST_EVENT}${providerId}`, JSON.stringify(payload));
}
return response;
@@ -27,12 +27,12 @@ const useSiren = () => {
const markAsReadByDate = async (untilDate: string) => {
if (siren && untilDate) {
- const response = await siren?.markNotificationsAsReadByDate(untilDate);
+ const response = await siren?.markAsReadByDate(untilDate);
if (response?.data) {
const payload = { action: eventTypes.MARK_ALL_AS_READ };
- PubSub.publish(events.NOTIFICATION_LIST_EVENT, JSON.stringify(payload));
+ PubSub.publish(`${events.NOTIFICATION_LIST_EVENT}${providerId}`, JSON.stringify(payload));
}
return response;
@@ -44,12 +44,12 @@ const useSiren = () => {
const deleteById = async (id: string, shouldUpdateList: boolean = true) => {
if (siren)
if (id?.length > 0) {
- const response = await siren?.deleteNotificationById(id);
+ const response = await siren?.deleteById(id);
if (response?.data && shouldUpdateList) {
const payload = { id, action: eventTypes.DELETE_ITEM };
- PubSub.publish(events.NOTIFICATION_LIST_EVENT, JSON.stringify(payload));
+ PubSub.publish(`${events.NOTIFICATION_LIST_EVENT}${providerId}`, JSON.stringify(payload));
}
return response;
@@ -62,12 +62,12 @@ const useSiren = () => {
const deleteByDate = async (untilDate: string) => {
if (siren && untilDate) {
- const response = await siren.deleteNotificationsByDate(untilDate);
+ const response = await siren.deleteByDate(untilDate);
if (response?.data) {
const payload = { action: eventTypes.DELETE_ALL_ITEM };
- PubSub.publish(events.NOTIFICATION_LIST_EVENT, JSON.stringify(payload));
+ PubSub.publish(`${events.NOTIFICATION_LIST_EVENT}${providerId}`, JSON.stringify(payload));
}
return response;
@@ -78,12 +78,12 @@ const useSiren = () => {
const markAllAsViewed = async (untilDate: string) => {
if (siren && untilDate) {
- const response = await siren?.markNotificationsAsViewed(untilDate);
+ const response = await siren?.markAllAsViewed(untilDate);
if (response?.data) {
const payload = { unviewedCount: 0, action: eventTypes.UPDATE_NOTIFICATIONS_COUNT };
- PubSub.publish(events.NOTIFICATION_COUNT_EVENT, JSON.stringify(payload));
+ PubSub.publish(`${events.NOTIFICATION_COUNT_EVENT}${providerId}`, JSON.stringify(payload));
}
return response;
diff --git a/tests/components/sirenNotificationIcon.test.tsx b/tests/components/sirenNotificationIcon.test.tsx
index 238f600..0e29724 100644
--- a/tests/components/sirenNotificationIcon.test.tsx
+++ b/tests/components/sirenNotificationIcon.test.tsx
@@ -34,18 +34,16 @@ describe('SirenInboxIcon', () => {
const notificationIcon = ;
const mockSiren:Pick = {
- markNotificationAsReadById: jest.fn(),
- markNotificationsAsReadByDate: jest.fn(),
- deleteNotificationById: jest.fn(),
- deleteNotificationsByDate: jest.fn(),
- markNotificationsAsViewed: jest.fn(),
+ markAsReadById: jest.fn(),
+ markAsReadByDate: jest.fn(),
+ deleteById: jest.fn(),
+ deleteByDate: jest.fn(),
+ markAllAsViewed: jest.fn(),
verifyToken: jest.fn(),
fetchUnviewedNotificationsCount: jest.fn(async () => UnviewedCountReturnResponse),
fetchAllNotifications: jest.fn(),
- startRealTimeNotificationFetch: jest.fn(),
- stopRealTimeNotificationFetch: jest.fn(),
- startRealTimeUnviewedCountFetch: jest.fn(),
- stopRealTimeUnviewedCountFetch: jest.fn(),
+ startRealTimeFetch: jest.fn(),
+ stopRealTimeFetch: jest.fn(),
};
jest.spyOn(sirenProvider, 'useSirenContext').mockReturnValue({
diff --git a/tests/components/sirenProvider.test.tsx b/tests/components/sirenProvider.test.tsx
index b155e4b..0c5b0c2 100644
--- a/tests/components/sirenProvider.test.tsx
+++ b/tests/components/sirenProvider.test.tsx
@@ -24,16 +24,14 @@ describe('SirenProvider', () => {
);
const mocErrorFn = jest.fn();
- const mockNotificationHandler = jest.fn();
- const mockCountHandler = jest.fn();
+ const mockEventHandler = jest.fn();
const sirenObject = new Siren({
token: 'user-token',
recipientId: 'recipient-id',
onError: mocErrorFn,
actionCallbacks: {
- onNotificationReceived: mockNotificationHandler,
- onUnViewedCountReceived: mockCountHandler
+ onEventReceive: mockEventHandler,
}
});
diff --git a/tests/utils/sirenHook.test.ts b/tests/utils/sirenHook.test.ts
index 848bb5a..979b2e7 100644
--- a/tests/utils/sirenHook.test.ts
+++ b/tests/utils/sirenHook.test.ts
@@ -73,21 +73,19 @@ const MarkAsViewedResponse = {
describe('useSiren hook', () => {
const mockSiren: Pick = {
- markNotificationAsReadById: jest.fn(async () => Response),
- markNotificationsAsReadByDate: jest.fn(async () => ActionResponse),
- deleteNotificationById: jest.fn(async () => ActionResponse),
- deleteNotificationsByDate: jest.fn(async () => ActionResponse),
- markNotificationsAsViewed: jest.fn(async () => MarkAsViewedResponse),
+ markAsReadById: jest.fn(async () => Response),
+ markAsReadByDate: jest.fn(async () => ActionResponse),
+ deleteById: jest.fn(async () => ActionResponse),
+ deleteByDate: jest.fn(async () => ActionResponse),
+ markAllAsViewed: jest.fn(async () => MarkAsViewedResponse),
verifyToken: jest.fn(),
fetchUnviewedNotificationsCount: jest.fn(),
fetchAllNotifications: jest.fn(),
- startRealTimeNotificationFetch: jest.fn(),
- stopRealTimeNotificationFetch: jest.fn(),
- startRealTimeUnviewedCountFetch: jest.fn(),
- stopRealTimeUnviewedCountFetch: jest.fn()
+ startRealTimeFetch: jest.fn(),
+ stopRealTimeFetch: jest.fn(),
};
- it('should call siren.markNotificationAsReadById and update notifications list when siren exists and id is not empty', async () => {
+ it('should call siren.markAsReadById and update notifications list when siren exists and id is not empty', async () => {
// Mock useSirenContext
jest.spyOn(sirenProvider, 'useSirenContext').mockReturnValue({
@@ -98,11 +96,11 @@ describe('useSiren hook', () => {
const { markAsReadById } = useSiren();
const response = await markAsReadById('xyz');
- expect(mockSiren.markNotificationAsReadById).toHaveBeenCalledWith('xyz');
+ expect(mockSiren.markAsReadById).toHaveBeenCalledWith('xyz');
expect(response).toEqual(Response);
});
- it('should call siren.markNotificationsAsReadByDate and update notifications list when siren exists and untilDate is provided', async () => {
+ it('should call siren.markAsReadByDate and update notifications list when siren exists and untilDate is provided', async () => {
jest.spyOn(sirenProvider, 'useSirenContext').mockReturnValue({
siren: mockSiren as Siren,
verificationStatus: VerificationStatus.SUCCESS
@@ -112,11 +110,11 @@ describe('useSiren hook', () => {
const untilDate = '2024-02-28T00:00:00Z';
const response = await markAsReadByDate(untilDate);
- expect(mockSiren.markNotificationsAsReadByDate).toHaveBeenCalledWith(untilDate);
+ expect(mockSiren.markAsReadByDate).toHaveBeenCalledWith(untilDate);
expect(response).toEqual(ActionResponse);
});
- it('should call siren.deleteNotificationById and update notifications list when siren exists and id is not empty', async () => {
+ it('should call siren.deleteById and update notifications list when siren exists and id is not empty', async () => {
jest.spyOn(sirenProvider, 'useSirenContext').mockReturnValue({
siren: mockSiren as Siren,
@@ -126,11 +124,11 @@ describe('useSiren hook', () => {
const { deleteById } = useSiren();
const response = await deleteById('xyz');
- expect(mockSiren.deleteNotificationById).toHaveBeenCalledWith('xyz');
+ expect(mockSiren.deleteById).toHaveBeenCalledWith('xyz');
expect(response).toEqual(ActionResponse);
});
- it('should call siren.deleteNotificationsByDate and update notifications list when siren exists and untilDate is provided', async () => {;
+ it('should call siren.deleteByDate and update notifications list when siren exists and untilDate is provided', async () => {;
jest.spyOn(sirenProvider, 'useSirenContext').mockReturnValue({
siren: mockSiren as Siren,
@@ -141,11 +139,11 @@ describe('useSiren hook', () => {
const untilDate = '2024-02-28T00:00:00Z';
const response = await deleteByDate(untilDate);
- expect(mockSiren.deleteNotificationsByDate).toHaveBeenCalledWith(untilDate);
+ expect(mockSiren.deleteByDate).toHaveBeenCalledWith(untilDate);
expect(response).toEqual(ActionResponse);
});
- it('should call siren.markNotificationsAsViewed and update notifications list when siren exists and untilDate is provided', async () => {
+ it('should call siren.markAllAsViewed and update notifications list when siren exists and untilDate is provided', async () => {
jest.spyOn(sirenProvider, 'useSirenContext').mockReturnValue({
siren: mockSiren as Siren,
@@ -156,7 +154,7 @@ describe('useSiren hook', () => {
const untilDate = '2024-02-28T00:00:00Z';
const response = await markAllAsViewed(untilDate);
- expect(mockSiren.markNotificationsAsViewed).toHaveBeenCalledWith(untilDate);
+ expect(mockSiren.markAllAsViewed).toHaveBeenCalledWith(untilDate);
expect(response).toEqual(MarkAsViewedResponse);
});
});