TP-52572 | push notification changes
|
Before Width: | Height: | Size: 514 B After Width: | Height: | Size: 371 B |
|
Before Width: | Height: | Size: 373 B After Width: | Height: | Size: 238 B |
|
Before Width: | Height: | Size: 782 B After Width: | Height: | Size: 423 B |
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 466 B |
|
Before Width: | Height: | Size: 1.8 KiB |
@@ -4,9 +4,13 @@ import {
|
||||
} from '@react-navigation/native';
|
||||
import { CaseDetailStackEnum } from '@screens/caseDetails/CaseDetailStack';
|
||||
import { Linking } from 'react-native';
|
||||
import notifee from '@notifee/react-native';
|
||||
import notifee, { InitialNotification } from '@notifee/react-native';
|
||||
import { GenericType } from '@common/GenericTypes';
|
||||
import { NotificationAction } from '@hooks/useFCM/useFCM';
|
||||
import store from '@store';
|
||||
import AsyncStorage from '@react-native-async-storage/async-storage';
|
||||
|
||||
const LAST_INITIAL_NOTIFICATION_ID = 'LAST_INITIAL_NOTIFICATION_ID';
|
||||
|
||||
// Define a type for the Route object for better type checking
|
||||
type Route = {
|
||||
@@ -49,6 +53,29 @@ const findRouteByName = (routes: Route[], name: string): Route | null => {
|
||||
return null;
|
||||
};
|
||||
|
||||
const needToHandleInitialNotification = async (
|
||||
initialNotification: InitialNotification | null
|
||||
): Promise<boolean> => {
|
||||
if (initialNotification !== null) {
|
||||
try {
|
||||
const lastInitialNotificationId = await AsyncStorage.getItem(LAST_INITIAL_NOTIFICATION_ID);
|
||||
const notificationId = initialNotification.notification.data?.notificationId;
|
||||
|
||||
if (lastInitialNotificationId !== null) {
|
||||
if (lastInitialNotificationId === notificationId) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
await AsyncStorage.setItem(LAST_INITIAL_NOTIFICATION_ID, String(notificationId));
|
||||
return true;
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
// Linking configuration with types for better type safety
|
||||
export const linkingConf: LinkingOptions<GenericType> = {
|
||||
prefixes: ['cosmosapp://'],
|
||||
@@ -58,13 +85,18 @@ export const linkingConf: LinkingOptions<GenericType> = {
|
||||
if (url) {
|
||||
return url;
|
||||
}
|
||||
// Access Redux store from the imported store instance
|
||||
const message = await notifee.getInitialNotification();
|
||||
const shouldHandleInitialNotification = await needToHandleInitialNotification(message);
|
||||
if (!shouldHandleInitialNotification) return null;
|
||||
if (message?.pressAction?.id === NotificationAction.DEFAULT) {
|
||||
return null;
|
||||
}
|
||||
const caseId = message?.notification?.data?.caseId as string;
|
||||
const reduxState = store.getState();
|
||||
const caseDetails = reduxState?.allCases?.caseDetails?.[caseId];
|
||||
const notificationId = message?.notification?.data?.notificationId as string;
|
||||
if (!caseId || !notificationId) return null;
|
||||
if (!caseId || !notificationId || !caseDetails) return null;
|
||||
const deepLink = `${
|
||||
message?.notification?.data?.deepLinks?.[message?.pressAction?.id]
|
||||
}¬ificationId=${notificationId}` as string;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { buildFlavour } from '@reducers/metadataSlice';
|
||||
import { isNullOrUndefined } from '../components/utlis/commonFunctions';
|
||||
import { setItem } from '@components/utlis/storageHelper';
|
||||
|
||||
export enum DEVICE_TYPE_ENUM {
|
||||
MOBILE = 'MOBILE',
|
||||
@@ -13,7 +14,7 @@ export const GLOBAL = {
|
||||
DEVICE_TYPE: DEVICE_TYPE_ENUM.MOBILE,
|
||||
IS_IMPERSONATED: false,
|
||||
SELECTED_AGENT_ID: '',
|
||||
BUILD_FLAVOUR: ''
|
||||
BUILD_FLAVOUR: '',
|
||||
};
|
||||
|
||||
interface IGlobalUserData {
|
||||
@@ -27,9 +28,18 @@ interface IGlobalUserData {
|
||||
|
||||
export const setGlobalUserData = (userData: IGlobalUserData) => {
|
||||
const { token, deviceId, agentId, deviceType, isImpersonated, selectedAgentId } = userData;
|
||||
if (!isNullOrUndefined(token)) GLOBAL.SESSION_TOKEN = `${token}`;
|
||||
if (!isNullOrUndefined(deviceId)) GLOBAL.DEVICE_ID = `${deviceId}`;
|
||||
if (!isNullOrUndefined(agentId)) GLOBAL.AGENT_ID = `${agentId}`;
|
||||
if (!isNullOrUndefined(token)) {
|
||||
GLOBAL.SESSION_TOKEN = `${token}`;
|
||||
setItem('sessionToken', String(token));
|
||||
}
|
||||
if (!isNullOrUndefined(deviceId)) {
|
||||
GLOBAL.DEVICE_ID = `${deviceId}`;
|
||||
setItem('deviceId', String(deviceId));
|
||||
}
|
||||
if (!isNullOrUndefined(agentId)) {
|
||||
GLOBAL.AGENT_ID = `${agentId}`;
|
||||
setItem('agentId', String(agentId));
|
||||
}
|
||||
if (!isNullOrUndefined(deviceType)) GLOBAL.DEVICE_TYPE = deviceType as DEVICE_TYPE_ENUM;
|
||||
if (!isNullOrUndefined(isImpersonated)) GLOBAL.IS_IMPERSONATED = isImpersonated ?? false;
|
||||
if (!isNullOrUndefined(selectedAgentId)) GLOBAL.SELECTED_AGENT_ID = `${selectedAgentId}`;
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
import { CLICKSTREAM_EVENT_NAMES } from '@common/Constants';
|
||||
import { navigateToScreen } from '@components/utlis/navigationUtlis';
|
||||
import { AndroidAction, Event, EventType } from '@notifee/react-native';
|
||||
import { formatAmount } from '@rn-ui-lib/utils/amount';
|
||||
import { CaseDetailStackEnum } from '@screens/caseDetails/CaseDetailStack';
|
||||
import { EmiScheduleTabs } from '@screens/emiSchedule';
|
||||
import { EmiSelectedTab } from '@screens/emiSchedule/EmiScheduleTab';
|
||||
import { INotification } from '@screens/notifications/NotificationItem';
|
||||
import { addClickstreamEvent } from '@services/clickstreamEventService';
|
||||
import store from '@store';
|
||||
|
||||
// Actions that can be performed on notification
|
||||
export enum NotificationAction {
|
||||
@@ -34,7 +36,9 @@ const getPaymentMadeNotificationContent = (notification: INotification) => {
|
||||
const { params } = notification || {};
|
||||
const { amount, customerName } = params || {};
|
||||
const title = `<span style="color: #1C1C1C">Payment success</span>`;
|
||||
const body = `<span style="color: #1C1C1C">${customerName}</span> <span style="color: #969696">made a payment of ₹${amount}</span>`;
|
||||
const body = `<span style="color: #1C1C1C">${customerName}</span> <span style="color: #969696">made a payment of ${formatAmount(
|
||||
amount
|
||||
)}</span>`;
|
||||
const actions = [
|
||||
{
|
||||
title: `<span style="color: #0276FE; font-size: 13px">VIEW DETAILS</span>`,
|
||||
@@ -57,7 +61,9 @@ const getPaymentFailedNotificationContent = (notification: INotification) => {
|
||||
const { params } = notification || {};
|
||||
const { amount, customerName } = params || {};
|
||||
const title = '<span style="color: #1C1C1C">Payment failure</span>';
|
||||
const body = `<span style="color: #1C1C1C">${customerName}</span> <span style="color: #969696">failed to make a payment of ₹${amount}</span>`;
|
||||
const body = `<span style="color: #1C1C1C">${customerName}</span> <span style="color: #969696">failed to make a payment of ${formatAmount(
|
||||
amount
|
||||
)}</span>`;
|
||||
const actions = [
|
||||
{
|
||||
title: `<span style="color: #0276FE; font-size: 13px">VIEW DETAILS</span>`,
|
||||
@@ -112,6 +118,9 @@ export const handleNotificationNavigation = (
|
||||
caseId: string,
|
||||
notificationId: string
|
||||
) => {
|
||||
const reduxState = store.getState();
|
||||
const caseDetails = reduxState?.allCases?.caseDetails?.[caseId];
|
||||
if(!caseDetails) return;
|
||||
switch (action) {
|
||||
case NotificationAction.VIEW_PAID_SCHEDULE:
|
||||
navigateToScreen('caseDetailStack', {
|
||||
|
||||
@@ -64,6 +64,7 @@ export const sendPushNotification = async (notification: any) => {
|
||||
name: 'Default Channel',
|
||||
importance: AndroidImportance.HIGH,
|
||||
bypassDnd: true,
|
||||
sound: 'default',
|
||||
});
|
||||
|
||||
const notificationContent = getNotificationContent(notification);
|
||||
@@ -77,13 +78,15 @@ export const sendPushNotification = async (notification: any) => {
|
||||
android: {
|
||||
channelId,
|
||||
smallIcon: 'ic_notification_icon',
|
||||
color: '#1D2678',
|
||||
color: '#22d081',
|
||||
largeIcon: 'ic_launcher',
|
||||
showTimestamp: true,
|
||||
actions,
|
||||
style: { type: AndroidStyle.BIGTEXT, text: body },
|
||||
pressAction: actions[0].pressAction,
|
||||
groupSummary: true,
|
||||
importance: AndroidImportance.HIGH,
|
||||
sound: 'default',
|
||||
},
|
||||
data,
|
||||
});
|
||||
|
||||
@@ -26,6 +26,7 @@ import CaseDetailStack from '@screens/caseDetails/CaseDetailStack';
|
||||
import FullScreenLoader from '@rn-ui-lib/components/FullScreenLoader';
|
||||
import { getFirestoreResyncIntervalInMinutes } from '@common/AgentActivityConfigurableConstants';
|
||||
import AgentIdCard from '@screens/Profile/AgentIdCard';
|
||||
import SuspenseLoader from '@rn-ui-lib/components/suspense_loader/SuspenseLoader';
|
||||
|
||||
const Stack = createNativeStackNavigator();
|
||||
|
||||
@@ -110,7 +111,7 @@ const ProtectedRouter = () => {
|
||||
};
|
||||
}, []);
|
||||
|
||||
if (isLoading) return <FullScreenLoader loading={isLoading} />;
|
||||
// if (isLoading) return <FullScreenLoader loading={isLoading} />;
|
||||
|
||||
return (
|
||||
<Stack.Navigator
|
||||
|
||||
@@ -15,12 +15,12 @@ export interface IForegroundTask {
|
||||
|
||||
const FOREGROUND_SERVICE_ID = 1244;
|
||||
|
||||
const FOREGROUND_SERVICE_CONFIG = {
|
||||
export const FOREGROUND_SERVICE_CONFIG = {
|
||||
id: FOREGROUND_SERVICE_ID,
|
||||
title: 'Tap to open Cosmos',
|
||||
message: '',
|
||||
icon: 'ic_notification_icon',
|
||||
color: '#1D2678',
|
||||
color: '#22d081',
|
||||
// @ts-ignore
|
||||
setOnlyAlertOnce: false,
|
||||
priority: 'low',
|
||||
|
||||