Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
11 changes: 10 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
10 changes: 4 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 |
Expand Down 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
2 changes: 1 addition & 1 deletion example/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
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.
83 changes: 66 additions & 17 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 All @@ -141,19 +173,28 @@ const Card = (props: NotificationCardProps): ReactElement => {
<Text numberOfLines={2} style={[styles.cardTitle, style.cardTitle]}>
{notification.message?.header}
</Text>
{!hideDelete &&
(deleteIcon || (
<CloseIcon onDelete={onDeleteItem} notification={notification} styles={styles} />
))}
{!hideDelete && (
<CloseIcon
onDelete={onDeleteItem}
customIcon={deleteIcon}
notification={notification}
styles={styles}
/>
)}
</View>
{Boolean(notification.message?.subHeader) && (
<Text numberOfLines={2} style={[style.cardSubTitle, styles.cardSubTitle]}>
{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 +269,14 @@ const style = StyleSheet.create({
},
transparent: {
backgroundColor: 'transparent'
},
mediaContainer: {
width: '100%',
height: 130,
borderRadius: 6,
marginBottom: 10,
overflow: 'hidden',
backgroundColor: '#D3D3D3'
}
});

Expand Down
4 changes: 3 additions & 1 deletion src/components/closeIcon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,12 @@ import type { StyleProps } from '../types';
const CloseIcon = ({
notification,
styles,
customIcon,
onDelete
}: {
notification: NotificationDataType;
styles: Partial<StyleProps>;
customIcon?: ReactElement | null;
onDelete: (id: string) => void;
}): ReactElement => {
const icon: JSX.Element[] = [];
Expand All @@ -36,7 +38,7 @@ const CloseIcon = ({
testID='delete-button'
accessibilityLabel={`siren-notification-delete${notification.id}`}
>
<>{icon}</>
<>{customIcon || icon}</>
</TouchableOpacity>
);
};
Expand Down
Loading