Skip to content

Commit

Permalink
feat(DMAPP-115): integrate Channel Foreground notification for iOS an…
Browse files Browse the repository at this point in the history
…d Android

- Display notification banner when the app is in the foreground state.
- Handle data updates when the home screen is open in the foreground.
- Implement notification banner press handling to navigate or update views as needed.
- Fix iOS notification sound issue to ensure proper audio playback for notifications.
  • Loading branch information
meKushdeepSingh committed Dec 9, 2024
1 parent 87ee073 commit 00c35dc
Show file tree
Hide file tree
Showing 12 changed files with 130 additions and 256 deletions.
13 changes: 7 additions & 6 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import messaging from '@react-native-firebase/messaging';
import React, {useEffect} from 'react';
import {AppRegistry, AppState, Platform} from 'react-native';
import {Alert, AppRegistry, AppState, Platform} from 'react-native';
import RNCallKeep from 'react-native-callkeep';
import 'react-native-crypto';
import 'react-native-get-random-values';
Expand All @@ -10,6 +10,7 @@ import {NotifeClearBadge} from 'src/notifee';
import {getUUID} from 'src/push_video/payloads/helpers';
import MetaStorage from 'src/singletons/MetaStorage';
import 'text-encoding';
import {stringify} from 'uuid';

import App from './App';
import {name as appName} from './app.json';
Expand Down Expand Up @@ -56,13 +57,11 @@ function HeadlessCheck({isHeadless}) {
/** and native notification **/
/************************************************/
messaging().setBackgroundMessageHandler(async remoteMessage => {
console.log('Message handled in the background!', remoteMessage);

// console.log('Message handled in the background!', remoteMessage);
/***************************************************/
/** Uncomment below commented code if video call **/
/** feature is enabled in the app **/
/***************************************************/

// if (Platform.OS === 'android' && CallKeepHelper.isVideoCall(remoteMessage)) {
// const caller = CallKeepHelper.getCaller(remoteMessage);
// const addressTrimmed = CallKeepHelper.formatEthAddress(caller);
Expand All @@ -78,8 +77,10 @@ messaging().setBackgroundMessageHandler(async remoteMessage => {
});

messaging().onMessage(async remoteMessage => {
console.log('Message handled in the foreground!', remoteMessage);
await NotificationHelper.resolveNotification(remoteMessage);
// console.log('Message handled in the foreground!', remoteMessage);
if (remoteMessage.notification) {
await NotificationHelper.resolveNotification(remoteMessage);
}
});

if (isCallAccepted) {
Expand Down
3 changes: 3 additions & 0 deletions src/Globals.ts
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,9 @@ export default {
BG_PUSHNOTIFY: '#FCF2E3',
BG_ALLSET: '#F8ECFF',
BG_OBSLIDER: '#F8ECFF',

// Notification
IC_NOTIFICATION: '#e20880',
},
SCREENS: {
WELCOME: 'Welcome',
Expand Down
3 changes: 0 additions & 3 deletions src/components/ui/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import {StyleSheet, Text, View} from 'react-native';
import {connect} from 'react-redux';
import GLOBALS from 'src/Globals';
import {switchUser} from 'src/redux/authSlice';
import {clearFeed, fetchFeedData} from 'src/redux/feedSlice';

import HeaderBanner from './HeaderBanner';
import UserProfile from './UserProfile';
Expand Down Expand Up @@ -64,6 +63,4 @@ const mapStateToProps = state => ({

export default connect(mapStateToProps, {
switchUser,
fetchFeedData,
clearFeed,
})(Header);
24 changes: 16 additions & 8 deletions src/components/ui/HomeFeed.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,10 @@ import EPNSActivity from 'src/components/loaders/EPNSActivity';
import ImagePreviewFooter from 'src/components/ui/ImagePreviewFooter';
import {usePushApi} from 'src/contexts/PushApiContext';
import AppBadgeHelper from 'src/helpers/AppBadgeHelper';
import {getCurrentRouteName} from 'src/navigation/RootNavigation';
import {selectNotificationAlert} from 'src/redux/appSlice';
import {setNotificationAlert} from 'src/redux/appSlice';
import {
selectInboxNotificationAcknowledgement,
updateInboxNotificationAcknowledgement,
} from 'src/redux/homeSlice';

import EmptyFeed from './EmptyFeed';
import NotificationItem from './NotificationItem';
Expand All @@ -39,7 +40,9 @@ export default function InboxFeed(props) {
const [startFromIndex, setStartFromIndex] = useState(0);

// GET REDUX STATES AND DISPATCH ACTIONS
const notificationAlert = useSelector(selectNotificationAlert);
const channelInboxNotificationAcknowledgement = useSelector(
selectInboxNotificationAcknowledgement,
);

const dispatch = useDispatch();

Expand All @@ -55,13 +58,18 @@ export default function InboxFeed(props) {

useEffect(() => {
if (
notificationAlert?.screen === getCurrentRouteName() &&
notificationAlert?.type === 'inbox'
channelInboxNotificationAcknowledgement.notificationReceived ||
channelInboxNotificationAcknowledgement.notificationOpened
) {
fetchFeed(true, true);
dispatch(setNotificationAlert(null));
dispatch(
updateInboxNotificationAcknowledgement({
notificationOpened: false,
notificationReceived: false,
}),
);
}
}, [notificationAlert]);
}, [channelInboxNotificationAcknowledgement]);

useEffect(() => {
if (Platform.OS === 'android') {
Expand Down
19 changes: 0 additions & 19 deletions src/components/ui/SpamFeed.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,9 @@ import {
View,
} from 'react-native';
import ImageView from 'react-native-image-viewing';
import {useDispatch, useSelector} from 'react-redux';
import EPNSActivity from 'src/components/loaders/EPNSActivity';
import ImagePreviewFooter from 'src/components/ui/ImagePreviewFooter';
import {usePushApi} from 'src/contexts/PushApiContext';
import {getCurrentRouteName} from 'src/navigation/RootNavigation';
import {selectNotificationAlert} from 'src/redux/appSlice';
import {setNotificationAlert} from 'src/redux/appSlice';

import EmptyFeed from './EmptyFeed';
import NotificationItem from './NotificationItem';
Expand All @@ -33,11 +29,6 @@ export default function SpamFeed() {
const [renderGallery, setRenderGallery] = useState(false);
const [startFromIndex, setStartFromIndex] = useState(0);

// GET REDUX STATES AND DISPATCH ACTIONS
const notificationAlert = useSelector(selectNotificationAlert);

const dispatch = useDispatch();

// SET REFS
const FlatListFeedsRef = useRef(null);

Expand All @@ -47,16 +38,6 @@ export default function SpamFeed() {
}
}, []);

useEffect(() => {
if (
notificationAlert?.screen === getCurrentRouteName() &&
notificationAlert?.type === 'spam'
) {
setInitialized(false);
dispatch(setNotificationAlert(null));
}
}, [notificationAlert]);

// Refresh Feed
const fetchInitializedFeeds = async () => {
setInitialized(true);
Expand Down
84 changes: 48 additions & 36 deletions src/helpers/NotificationHelper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,13 @@ import * as PushApi from '@pushprotocol/restapi';
import {ENV} from '@pushprotocol/restapi/src/lib/constants';
import messaging from '@react-native-firebase/messaging';
import {FirebaseMessagingTypes} from '@react-native-firebase/messaging';
import Globals from 'src/Globals';
import GLOBALS from 'src/Globals';
import envConfig from 'src/env.config';
import {getCurrentRouteName, navigate} from 'src/navigation/RootNavigation';
import {UserChatCredentials} from 'src/navigation/screens/chats/ChatScreen';
import {globalDispatch} from 'src/redux';
import {setNotificationAlert} from 'src/redux/appSlice';
import {updateInboxNotificationAcknowledgement} from 'src/redux/homeSlice';
import MetaStorage from 'src/singletons/MetaStorage';

type SendNotifeeNotification = {
Expand All @@ -33,6 +34,10 @@ const NOTIFICATION_TYPES = {
CHAT: 'PUSH_NOTIFICATION_CHAT',
};

const NOTIFICATION_SUB_TYPES = {
INBOX: 'INBOX',
};

export const NotificationHelper = {
resolveNotification: async (
remoteMessage: FirebaseMessagingTypes.RemoteMessage,
Expand Down Expand Up @@ -93,7 +98,8 @@ export const NotificationHelper = {
ios: {
sound: 'default',
foregroundPresentationOptions: {
alert: true,
banner: true,
list: true,
badge: true,
sound: true,
},
Expand All @@ -103,18 +109,17 @@ export const NotificationHelper = {
largeIcon,
smallIcon:
remoteMessage.notification?.android?.smallIcon ?? 'ic_notification',
color: remoteMessage.notification?.android?.color ?? '#e20880',
color:
remoteMessage.notification?.android?.color ??
Globals.COLORS.IC_NOTIFICATION,
circularLargeIcon: true,
pressAction: {
id: 'default',
},
},
data: remoteMessage.data,
});
// NotificationHelper.handlePostNotificationReceived(
// NOTIFICATION_TYPES.CHANNEL,
// remoteMessage.data,
// );
NotificationHelper.handlePostNotificationReceived(remoteMessage.data);
} catch (error) {
console.log('NOTIFEE ERROR', error);
}
Expand Down Expand Up @@ -176,10 +181,6 @@ export const NotificationHelper = {
try {
const getDisplayedNotifications =
await notifee.getDisplayedNotifications();
console.log(
'getDisplayedNotifications',
JSON.stringify(getDisplayedNotifications),
);
return getDisplayedNotifications;
} catch (error) {
return [];
Expand All @@ -191,29 +192,19 @@ export const NotificationHelper = {
/** events(onPress and dismiss) **/
/************************************************/
handleNotificationEvents: async () => {
console.log('Execute Notification events');
messaging()
.getInitialNotification()
.then(async remoteMessage => {
if (remoteMessage) {
console.log(
'Remote Notification caused app to open from quit state:',
remoteMessage,
);
await NotificationHelper.handleNotificationRoute(remoteMessage.data);
}
});

messaging().onNotificationOpenedApp(async remoteMessage => {
console.log(
'Remote Notification caused app to open from background state:',
remoteMessage,
);
await NotificationHelper.handleNotificationRoute(remoteMessage.data);
});

notifee.onForegroundEvent(async ({type, detail}) => {
console.log('notifee.onForegroundEvent', {type, detail});
if (type === EventType.PRESS) {
await NotificationHelper.handleNotificationRoute(
detail.notification?.data,
Expand All @@ -222,10 +213,6 @@ export const NotificationHelper = {
});

notifee.onBackgroundEvent(async ({type, detail}) => {
console.log('Notifee caused application to open from background state', {
type,
detail,
});
if (type === EventType.PRESS) {
await NotificationHelper.handleNotificationRoute(
detail.notification?.data,
Expand All @@ -235,10 +222,6 @@ export const NotificationHelper = {

const initialNotification = await notifee.getInitialNotification();
if (initialNotification) {
console.log(
'Notifee caused application to open from quit state',
initialNotification,
);
const {notification} = initialNotification;
await NotificationHelper.handleNotificationRoute(notification?.data);
}
Expand All @@ -250,14 +233,34 @@ export const NotificationHelper = {
handleNotificationRoute: async (data?: {
[key: string]: string | number | object;
}) => {
// Parse the stringified data
const parsedDetails = data?.details
? JSON.parse(data?.details as string)
: {};

// Handle conditional checks to confirm if route navigation &
// data needs to be updated after notification opened
if (
data?.type === NOTIFICATION_TYPES.CHANNEL &&
getCurrentRouteName() !== GLOBALS.SCREENS.NOTIF_TABS
parsedDetails?.subType === NOTIFICATION_SUB_TYPES.INBOX
) {
navigate(GLOBALS.SCREENS.NOTIF_TABS, {activeTab: parsedDetails?.subType});
// If Home(Notification) tab is active then update data
if (getCurrentRouteName() == GLOBALS.SCREENS.NOTIF_TABS) {
globalDispatch(
updateInboxNotificationAcknowledgement({
notificationOpened: true,
}),
);
} else {
// If Home(Notification) tab is inactive then first
// navigate to Home tab then update data
navigate(GLOBALS.SCREENS.NOTIF_TABS);
globalDispatch(
updateInboxNotificationAcknowledgement({
notificationOpened: true,
}),
);
}
} else if (data?.type === NOTIFICATION_TYPES.CHAT) {
}
},
Expand All @@ -267,15 +270,24 @@ export const NotificationHelper = {
/** if the received notification is for **/
/** the currently active component screen. **/
/*****************************************************/
handlePostNotificationReceived: (type: string, data?: any) => {
handlePostNotificationReceived: (data?: {
[key: string]: string | number | object;
}) => {
// Parse the stringified data
const parsedDetails = data?.details
? JSON.parse(data?.details as string)
: {};

// Handle condition check please data needs to be
// updated after notification received
if (
type === NOTIFICATION_TYPES.CHANNEL &&
data?.type === NOTIFICATION_TYPES.CHANNEL &&
parsedDetails?.subType === NOTIFICATION_SUB_TYPES.INBOX &&
getCurrentRouteName() == GLOBALS.SCREENS.NOTIF_TABS
) {
globalDispatch(
setNotificationAlert({
screen: GLOBALS.SCREENS.NOTIF_TABS,
type: 'inbox',
updateInboxNotificationAcknowledgement({
notificationReceived: true,
}),
);
}
Expand Down
3 changes: 0 additions & 3 deletions src/helpers/ServerHelper.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@ const ServerHelper = {
},
body: JSON.stringify(body),
});

await MetaStorage.instance.setTokenServerSynced(true);
} catch (error) {
console.warn(error);
Expand Down Expand Up @@ -72,8 +71,6 @@ const ServerHelper = {
debug ? console.log(regResponse) : null;

if (regResponse.success) {
console.log('regResponse', JSON.stringify(regResponse));

// Finally Adjust the flag
await MetaStorage.instance.setTokenServerSynced(true);
}
Expand Down
Loading

0 comments on commit 00c35dc

Please sign in to comment.