Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 3 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,7 @@ Here are the custom style options for the notification inbox:
deleteIcon?:{
size?: number
};
dateIcon?:{
timerIcon?:{
size?: number
};
clearAllIcon?:{
Expand All @@ -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;
};
```

Expand Down Expand Up @@ -246,10 +248,6 @@ function MyComponent() {
markAsReadById(id);
}

function handleDeleteNotification(id) {
deleteById(id);
}

return (
{/* Your component logic */}
);
Expand Down
14 changes: 7 additions & 7 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,6 @@
],
"dependencies": {
"pubsub-js": "^1.9.4",
"@sirenapp/js-sdk": "^1.0.0"
"@sirenapp/js-sdk": "^1.1.0"
}
}
Binary file added src/assets/failedImageDark.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/assets/failedImageLight.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
71 changes: 58 additions & 13 deletions src/components/card.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand Down Expand Up @@ -47,28 +48,39 @@ 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;

const emptyState = () => {
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 => {
Expand All @@ -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 (
<View style={style.cardIconContainer}>
Expand All @@ -104,6 +124,18 @@ const Card = (props: NotificationCardProps): ReactElement => {
);
}, [styles, darkMode, imageSource, onAvatarClick]);

const renderMediaThumbnail = useMemo((): JSX.Element => {
return (
<TouchableOpacity
style={[style.mediaContainer, styles.mediaContainer]}
disabled={Boolean(!onMediaThumbnailClick)}
onPress={mediaClick}
>
<Image source={mediaSource} resizeMode='cover' style={style.icon} onError={onErrorMedia} />
</TouchableOpacity>
);
}, [darkMode, mediaSource, onMediaThumbnailClick]);

const onDeleteItem = async (): Promise<void> => {
const isSuccess = await onDelete(notification.id, false);

Expand All @@ -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));
});
};

Expand Down Expand Up @@ -151,9 +183,14 @@ const Card = (props: NotificationCardProps): ReactElement => {
{notification.message?.subHeader}
</Text>
)}
<Text numberOfLines={2} style={[style.cardDescription, styles.cardDescription]}>
{notification.message?.body}
</Text>
{Boolean(notification.message?.body) && (
<Text numberOfLines={2} style={[style.cardDescription, styles.cardDescription]}>
{notification.message?.body}
</Text>
)}
{!hideMediaThumbnail &&
Boolean(notification.message?.thumbnailUrl) &&
renderMediaThumbnail}
<View style={style.dateContainer}>
<TimerIcon styles={styles} />
<Text style={[style.dateStyle, styles.dateStyle]}>
Expand Down Expand Up @@ -228,6 +265,14 @@ const style = StyleSheet.create({
},
transparent: {
backgroundColor: 'transparent'
},
mediaContainer: {
width: '100%',
height: 130,
borderRadius: 6,
marginBottom: 10,
overflow: 'hidden',
backgroundColor: '#D3D3D3'
}
});

Expand Down
31 changes: 17 additions & 14 deletions src/components/sirenInbox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ const {
TOKEN_VERIFICATION_PENDING,
MAXIMUM_ITEMS_PER_FETCH,
VerificationStatus,
EventType,
errorMap
} = Constants;
const { applyTheme, isNonEmptyArray, updateNotifications } = CommonUtils;
Expand Down Expand Up @@ -76,7 +77,8 @@ const SirenInbox = (props: SirenInboxProps): ReactElement => {
cardProps = {
hideAvatar: false,
disableAutoMarkAsRead: false,
hideDelete: false
hideDelete: false,
hideMediaThumbnail: false,
},
listEmptyComponent = null,
headerProps = {},
Expand All @@ -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();

Expand All @@ -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]);

Expand All @@ -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(
Expand All @@ -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) => {
Expand All @@ -183,15 +186,15 @@ const SirenInbox = (props: SirenInboxProps): ReactElement => {
// Initialize Siren SDK and fetch notifications
const initialize = async (): Promise<void> => {
if (siren) {
siren?.stopRealTimeNotificationFetch();
siren?.stopRealTimeFetch(EventType.NOTIFICATION);
const allNotifications = await fetchNotifications(siren, true);
const notificationParams: fetchProps = { size: notificationsPerPage };

if (isNonEmptyArray(allNotifications))
notificationParams.start = allNotifications[0].createdAt;

if (verificationStatus === VerificationStatus.SUCCESS)
siren?.startRealTimeNotificationFetch(notificationParams);
siren?.startRealTimeFetch({eventType: EventType.NOTIFICATION, params: notificationParams});
}
};

Expand All @@ -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;
Expand Down Expand Up @@ -273,15 +276,15 @@ 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 };

if (isNonEmptyArray(allNotifications))
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);
Expand Down
11 changes: 6 additions & 5 deletions src/components/sirenInboxIcon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ const {
eventTypes,
events,
defaultStyles,
EventType,
VerificationStatus,
errorMap
} = Constants;
Expand Down Expand Up @@ -50,7 +51,7 @@ const SirenInboxIcon = (props: SirenInboxIconProps) => {
onError = () => null
} = props;

const { siren, verificationStatus } = useSirenContext();
const { siren, verificationStatus, id } = useSirenContext();

const [unviewedCount, seUnviewedCount] = useState<number>(0);

Expand All @@ -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);
};

Expand All @@ -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();
}, []);
Expand All @@ -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);
}
Expand Down
Loading