Merge branch 'master' of github.cmd.navi-tech.in:medici/Address-Verification-App into feat/tp-29659

This commit is contained in:
Aman Sethi
2023-05-25 13:23:32 +05:30
27 changed files with 289 additions and 34 deletions

16
App.tsx
View File

@@ -19,7 +19,13 @@ import FullScreenLoader from './RN-UI-LIB/src/components/FullScreenLoader';
import { toastConfigs, ToastContainer } from './RN-UI-LIB/src/components/toast';
import * as Sentry from '@sentry/browser';
import { APM_APP_NAME, APM_BASE_URL, ENV, SENTRY_DSN } from './src/constants/config';
import {
APM_APP_NAME,
APM_BASE_URL,
ENV,
MS_CLARITY_PROJECT_ID,
SENTRY_DSN,
} from './src/constants/config';
import { COLORS } from './RN-UI-LIB/src/styles/colors';
import codePush from 'react-native-code-push';
import AsyncStorage from '@react-native-async-storage/async-storage';
@@ -31,12 +37,16 @@ import ErrorBoundary from './src/common/ErrorBoundary';
import CodePush from 'react-native-code-push';
import { TDocumentObj } from './src/screens/caseDetails/interface';
import AuthRouter from './src/screens/auth/AuthRouter';
import { initialize } from 'react-native-clarity';
Sentry.init({ dsn: SENTRY_DSN });
if (ENV !== 'prod') {
// mockApiServer();
if (ENV === 'prod') {
if (MS_CLARITY_PROJECT_ID) {
initialize(MS_CLARITY_PROJECT_ID);
}
}
setJsErrorHandler();
LogBox.ignoreAllLogs();

View File

@@ -8,3 +8,4 @@ export const APM_APP_NAME = 'cosmos-app';
export const APM_BASE_URL = 'https://dev-longhorn-portal.np.navi-tech.in/apm-events';
export const GOOGLE_SSO_CLIENT_ID =
'60755663443-40k0fbrbbqv4ci4hrjlbrphab5fj387b.apps.googleusercontent.com';
export const MS_CLARITY_PROJECT_ID = '';

View File

@@ -12,3 +12,4 @@ export const IS_DATA_SYNC_REQUIRED = true;
export const DATA_SYNC_TIME_INTERVAL = 2 * MINUTES_IN_AN_HOUR * MILLISECONDS_IN_A_MINUTE; // 2hr
export const GOOGLE_SSO_CLIENT_ID =
'136591056725-ev8db4hrlud2m23n0o03or3cmmp3a3cq.apps.googleusercontent.com';
export const MS_CLARITY_PROJECT_ID = 'gobifvjiac';

View File

@@ -12,3 +12,4 @@ export const IS_DATA_SYNC_REQUIRED = true;
export const DATA_SYNC_TIME_INTERVAL = 2 * MINUTES_IN_AN_HOUR * MILLISECONDS_IN_A_MINUTE; // 2hr
export const GOOGLE_SSO_CLIENT_ID =
'60755663443-40k0fbrbbqv4ci4hrjlbrphab5fj387b.apps.googleusercontent.com';
export const MS_CLARITY_PROJECT_ID = '';

View File

@@ -52,6 +52,7 @@
"react-hook-form": "7.40.0",
"react-native": "0.70.6",
"react-native-call-log": "2.1.2",
"react-native-clarity": "0.0.3",
"react-native-code-push": "7.1.0",
"react-native-contacts": "7.0.5",
"react-native-date-picker": "4.2.10",

View File

@@ -0,0 +1,63 @@
import axiosInstance, { ApiKeys, getApiUrl } from '../components/utlis/apiHelper';
import { logError } from '../components/utlis/errorUtils';
import { FilterResponse } from '../screens/allCases/interface';
import { CaseDetail } from '../screens/caseDetails/interface';
export enum SyncStatus {
SEND_CASES = 'SEND_CASES',
FETCH_CASES = 'FETCH_CASES',
SKIP = 'SKIP',
}
interface IFilterSync {
filterComponentList: FilterResponse[];
}
export interface ISyncedCases {
cases: CaseDetail[];
filters: IFilterSync;
deletedCaseIds: string[];
payloadCreatedAt: number;
}
interface ICases {
caseId: string;
caseViewCreatedAt?: number;
}
export interface ISyncCaseIdPayload {
agentId: string;
cases: ICases[];
}
export const getCasesSyncStatus = async (userReferenceId: string) => {
try {
const url = getApiUrl(ApiKeys.CASES_SYNC_STATUS, {}, { userReferenceId });
const response = await axiosInstance.get(url, { headers: { donotHandleError: true } });
return response?.data?.syncStatus;
} catch (err) {
logError(err as Error, 'Error getting sync status');
}
};
export const sendSyncCaseIds = async (payload: ISyncCaseIdPayload) => {
try {
const url = getApiUrl(ApiKeys.CASES_SEND_ID);
const response = await axiosInstance.post(url, payload, {
headers: { donotHandleError: true },
});
return response?.data;
} catch (err) {
logError(err as Error, 'Error sending case ids sync');
}
};
export const fetchCasesToSync = async (agentReferenceId: string) => {
try {
const url = getApiUrl(ApiKeys.FETCH_CASES, { agentReferenceId });
const response = await axiosInstance.get(url, { headers: { donotHandleError: true } });
return response?.data;
} catch (err) {
logError(err as Error, 'Error fetching cases to be synced');
}
};

View File

@@ -23,6 +23,8 @@ const BlockerScreen = (props: IBlockerScreen) => {
const { isTimeSynced, isDeviceLocationEnabled } = useAppSelector(
(state) => state.foregroundService
);
const { isWifiOrCellularOn } = useAppSelector((state) => state.metadata);
const [showActionBtnLoader, setShowActionBtnLoader] = useState(false);
const dispatch = useAppDispatch();
@@ -110,6 +112,20 @@ const BlockerScreen = (props: IBlockerScreen) => {
);
}
if (!isWifiOrCellularOn) {
const { heading, instructions } = BLOCKER_SCREEN_DATA.DEVICE_WIFI_OR_CELLULAR_OFF;
return (
<BlockerInstructions
heading={heading}
instructions={instructions}
actionBtn={{
title: 'Go to settings',
action: handleOpenSettings,
}}
/>
);
}
return <>{props.children}</>;
};

View File

@@ -562,6 +562,13 @@ export const BLOCKER_SCREEN_DATA = {
`Please retry connecting using the button below, if it doesn't automatically detect.`,
],
},
DEVICE_WIFI_OR_CELLULAR_OFF: {
heading: `Please turn on your internet`,
instructions: [
'Open the Settings app on your Android device.',
'Check you wifi/cellular settings',
],
},
};
export const SCREEN_ANIMATION_DURATION = 300;

View File

@@ -13,11 +13,23 @@ import { useAppDispatch, useAppSelector } from '../hooks';
import { dataSyncService } from '../services/dataSync.service';
import { DATA_SYNC_TIME_INTERVAL, IS_DATA_SYNC_REQUIRED } from '../constants/config';
import useIsLocationEnabled from '../hooks/useIsLocationEnabled';
import {
ISyncCaseIdPayload,
ISyncedCases,
SyncStatus,
fetchCasesToSync,
getCasesSyncStatus,
sendSyncCaseIds,
} from '../action/firebaseFallbackActions';
import { getSyncCaseIds } from '../components/utlis/firebaseFallbackUtils';
import { syncCasesByFallback } from '../reducer/allCasesSlice';
import { MILLISECONDS_IN_A_MINUTE } from '../../RN-UI-LIB/src/utlis/common';
export enum FOREGROUND_TASKS {
GEOLOCATION = 'GEOLOCATION',
TIME_SYNC = 'TIME_SYNC',
DATA_SYNC = 'DATA_SYNC',
FIRESTORE_FALLBACK = 'FIRESTORE_FALLBACK',
}
interface ITrackingComponent {
@@ -33,6 +45,16 @@ const TrackingComponent: React.FC<ITrackingComponent> = ({ children }) => {
const bgTrackingTimeoutId = useRef<number>();
const user = useAppSelector((state) => state.user);
const {
referenceId,
pendingList = [],
pinnedList = [],
} = useAppSelector((state) => ({
referenceId: state.user.user?.referenceId!!,
pendingList: state.allCases.pendingList,
pinnedList: state.allCases.pinnedList,
}));
const handleTimeSync = async () => {
try {
const timestamp = await getSyncTime();
@@ -56,17 +78,44 @@ const TrackingComponent: React.FC<ITrackingComponent> = ({ children }) => {
}
};
const handleGetCaseSyncStatus = async () => {
try {
const syncStatus = await getCasesSyncStatus(referenceId);
if (syncStatus === SyncStatus.SEND_CASES) {
const cases = getSyncCaseIds([...pendingList, ...pinnedList]);
const payload: ISyncCaseIdPayload = {
agentId: referenceId,
cases,
};
sendSyncCaseIds(payload);
} else if (syncStatus === SyncStatus.FETCH_CASES) {
const updatedDetails: ISyncedCases = await fetchCasesToSync(referenceId);
if (updatedDetails?.cases?.length) {
dispatch(syncCasesByFallback(updatedDetails));
}
}
} catch (e) {
logError(e as Error, 'Error during fetching case sync status');
}
};
const tasks: IForegroundTask[] = [
{
taskId: FOREGROUND_TASKS.TIME_SYNC,
task: handleTimeSync,
delay: 1000 * 60 * 5, // 5 minutes,
delay: 5 * MILLISECONDS_IN_A_MINUTE, // 5 minutes,
onLoop: true,
},
{
taskId: FOREGROUND_TASKS.GEOLOCATION,
task: handleSendGeolocation,
delay: 1000 * 60 * 3, // 3 minutes
delay: 3 * MILLISECONDS_IN_A_MINUTE, // 3 minutes
onLoop: true,
},
{
taskId: FOREGROUND_TASKS.FIRESTORE_FALLBACK,
task: handleGetCaseSyncStatus,
delay: 30 * MILLISECONDS_IN_A_MINUTE, // 30 minutes
onLoop: true,
},
];

View File

@@ -43,6 +43,9 @@ export enum ApiKeys {
DATA_SYNC_UPLOAD_COMPLETED = 'DATA_SYNC_UPLOAD_COMPLETED',
IMPERSONATION = 'IMPERSONATION',
TELEPHONES = 'TELEPHONES',
CASES_SYNC_STATUS = 'CASES_SYNC_STATUS',
CASES_SEND_ID = 'CASES_SEND_ID',
FETCH_CASES = 'FETCH_CASES',
}
export const API_URLS: Record<ApiKeys, string> = {} as Record<ApiKeys, string>;
@@ -72,6 +75,9 @@ API_URLS[ApiKeys.GET_PRE_SIGNED_URL_DATA_SYNC] = '/sync-data/get-pre-signed-url'
API_URLS[ApiKeys.DATA_SYNC_UPLOAD_COMPLETED] = '/sync-data/upload-completed';
API_URLS[ApiKeys.IMPERSONATION] = '/auth/impersonation';
API_URLS[ApiKeys.TELEPHONES] = '/telephones';
API_URLS[ApiKeys.CASES_SYNC_STATUS] = '/cases/agents/sync-status';
API_URLS[ApiKeys.CASES_SEND_ID] = '/cases/sync';
API_URLS[ApiKeys.FETCH_CASES] = '/cases/agents/{agentReferenceId}';
export const API_STATUS_CODE = {
OK: 200,

View File

@@ -319,3 +319,13 @@ export const getMaxByPropFromList = (arr: GenericType, prop: string) => {
const max = Math.max(...mappedArray);
return arr.find((x: GenericType) => x[prop] == max);
};
export const getGoogleMapUrl = (latitude: string | number, longitude: string | number) => {
if (!latitude || !longitude) return;
return `https://www.google.com/maps/search/${latitude},+${longitude}`;
}
export const isValidAmountEntered = (value: number) => {
return typeof value === 'number' && !isNaN(value);
};

View File

@@ -0,0 +1,8 @@
import { ICaseItem } from '../../screens/allCases/interface';
export const getSyncCaseIds = (casesList: ICaseItem[] = []) => {
return casesList.map((caseItem) => ({
caseId: caseItem.caseReferenceId,
caseViewCreatedAt: caseItem.caseViewCreatedAt,
}));
};

View File

@@ -1,3 +1,4 @@
import { setCustomUserId } from 'react-native-clarity';
import { isNullOrUndefined } from '../components/utlis/commonFunctions';
export enum DEVICE_TYPE_ENUM {
@@ -21,6 +22,10 @@ interface IGlobalUserData {
isImpersonated?: boolean;
}
interface IClarityConfiguration {
customUserId?: string;
}
export const setGlobalUserData = (userData: IGlobalUserData) => {
const { token, deviceId, agentId, deviceType, isImpersonated } = userData;
if (!isNullOrUndefined(token)) GLOBAL.SESSION_TOKEN = `${token}`;
@@ -29,3 +34,9 @@ export const setGlobalUserData = (userData: IGlobalUserData) => {
if (!isNullOrUndefined(deviceType)) GLOBAL.DEVICE_TYPE = deviceType as DEVICE_TYPE_ENUM;
if (!isNullOrUndefined(isImpersonated)) GLOBAL.IS_IMPERSONATED = isImpersonated ?? false;
};
export const setMsClarityConfig = (clarityConfig: IClarityConfiguration) => {
if (clarityConfig?.customUserId) {
setCustomUserId(clarityConfig.customUserId);
}
};

View File

@@ -12,3 +12,4 @@ export const IS_DATA_SYNC_REQUIRED = true;
export const DATA_SYNC_TIME_INTERVAL = 2 * MINUTES_IN_AN_HOUR * MILLISECONDS_IN_A_MINUTE; // 2hr
export const GOOGLE_SSO_CLIENT_ID =
'60755663443-40k0fbrbbqv4ci4hrjlbrphab5fj387b.apps.googleusercontent.com';
export const MS_CLARITY_PROJECT_ID = '';

View File

@@ -167,14 +167,6 @@ const useFirestoreUpdates = () => {
.catch((error) => {
dispatch(setLoading(false));
logError(error as Error, 'Error in signInUserToFirebase');
setGlobalUserData({ token: '', agentId: '', deviceId: '' });
dispatch(
setAuthData({
sessionDetails: null,
user: null,
isLoggedIn: false,
})
);
toast({
type: 'error',
text1: ToastMessages.FIRESTORE_SIGNIN_FAILED,

View File

@@ -199,6 +199,18 @@ export const getUpdatedAVCaseDetail = ({
return updatedValue;
};
const getCaseListItem = (
caseReferenceId: string,
pinRank?: number | null,
caseViewCreatedAt?: number
) => {
return {
caseReferenceId,
pinRank: pinRank || null,
caseViewCreatedAt,
};
};
const allCasesSlice = createSlice({
name: 'cases',
initialState,
@@ -219,7 +231,7 @@ const allCasesSlice = createSlice({
let newVisitCollectionCases: string[] = [];
let removedVisitedCasesLoanIds: string[] = [];
caseUpdates.forEach(({ updateType, updatedCaseDetail }) => {
const { caseType, caseReferenceId, id, pinRank } = updatedCaseDetail;
const { caseType, caseReferenceId, id, pinRank, caseViewCreatedAt } = updatedCaseDetail;
const caseId = caseReferenceId || id;
switch (updateType) {
@@ -272,10 +284,7 @@ const allCasesSlice = createSlice({
if (pinRank && caseType === CaseAllocationType.COLLECTION_CASE) {
newVisitCollectionCases.push(caseId);
}
const caseListItem = {
caseReferenceId: caseId,
pinRank: pinRank || null,
};
const caseListItem = getCaseListItem(caseId, pinRank, caseViewCreatedAt);
state.casesList.unshift(caseListItem);
let currentTask = null;
if (caseType !== CaseAllocationType.COLLECTION_CASE) {
@@ -494,6 +503,33 @@ const allCasesSlice = createSlice({
resetNewVisitedCases: (state) => {
state.newVisitedCases = [];
},
syncCasesByFallback: (state, action) => {
const { cases = [], deletedCaseIds = [], payloadCreatedAt } = action.payload;
cases.forEach((caseItem: CaseDetail) => {
const { caseViewCreatedAt, caseReferenceId, isSynced, pinRank } = caseItem;
if (
!state.caseDetails[caseReferenceId] ||
(isSynced && caseViewCreatedAt && caseViewCreatedAt < payloadCreatedAt)
) {
const caseListItem = getCaseListItem(caseReferenceId, pinRank, caseViewCreatedAt);
state.casesList.unshift(caseListItem);
state.caseDetails[caseReferenceId] = { ...caseItem, isSynced: true };
const { pendingList, completedList, pinnedList } = getCaseListComponents(
state.casesList,
state.caseDetails
);
state.pendingList = pendingList;
state.completedList = completedList;
state.pinnedList = pinnedList;
}
});
deletedCaseIds.forEach((caseItem: CaseDetail) => {
const { caseViewCreatedAt, caseReferenceId } = caseItem;
if (caseViewCreatedAt && caseViewCreatedAt < payloadCreatedAt) {
delete state.caseDetails[caseReferenceId];
}
});
},
},
});
@@ -513,6 +549,7 @@ export const {
updateCaseDetailBeforeApiCall,
setVisitPlansUpdating,
resetNewVisitedCases,
syncCasesByFallback,
} = allCasesSlice.actions;
export default allCasesSlice.reducer;

View File

@@ -7,11 +7,13 @@ export interface UninstallInformation {
interface IMetadata {
isOnline: boolean;
forceUninstall: Record<string, UninstallInformation>;
isWifiOrCellularOn: boolean;
}
const initialState = {
isOnline: true,
forceUninstall: {},
isWifiOrCellularOn: true,
} as IMetadata;
const MetadataSlice = createSlice({
@@ -24,9 +26,12 @@ const MetadataSlice = createSlice({
setForceUninstallData: (state, action) => {
state.forceUninstall = action.payload;
},
setIsWifiOrCellularOn: (state, action) => {
state.isWifiOrCellularOn = action.payload;
},
},
});
export const { setIsOnline, setForceUninstallData } = MetadataSlice.actions;
export const { setIsOnline, setForceUninstallData, setIsWifiOrCellularOn } = MetadataSlice.actions;
export default MetadataSlice.reducer;

View File

@@ -7,7 +7,7 @@ import {
BUSINESS_TIME_FORMAT,
dateFormat,
} from '../../../RN-UI-LIB/src/utlis/dates';
import { sanitizeString } from '../../components/utlis/commonFunctions';
import { getGoogleMapUrl, sanitizeString } from '../../components/utlis/commonFunctions';
import { IGeoLocation } from '../../types/addressGeolocation.types';
import { addClickstreamEvent } from '../../services/clickstreamEventService';
import { CLICKSTREAM_EVENT_NAMES } from '../../common/Constants';
@@ -30,7 +30,7 @@ const GeolocationItem = ({ geolocationItem, showSeparator = true }: IGeolocation
latitude: geolocationItem.latitude,
longitude: geolocationItem.longitude,
});
const geolocationUrl = `http://maps.google.com/?q=${geolocationItem?.latitude},${geolocationItem?.longitude}`;
const geolocationUrl = getGoogleMapUrl(geolocationItem?.latitude, geolocationItem?.longitude);
if (!geolocationUrl) return;
return Linking.openURL(geolocationUrl);

View File

@@ -45,7 +45,14 @@ const Filters: React.FC<IFilters> = ({
return (
<View>
<View style={[GenericStyles.ph16, GenericStyles.pb16, GenericStyles.centerAlignedRow]}>
<View
style={[
GenericStyles.mh20,
GenericStyles.ph16,
GenericStyles.pb16,
GenericStyles.centerAlignedRow,
]}
>
<TextInput
style={styles.textInput}
LeftComponent={<SearchIcon />}

View File

@@ -112,6 +112,7 @@ export const caseVerdictAndColor = {
export interface ICaseItem {
type?: CaseTypes; // this is for maintaining frontend
pinRank: number | null;
caseViewCreatedAt?: number;
updatedAt?: any;
allocatedAt?: any;
caseReferenceId: string;

View File

@@ -5,7 +5,7 @@ import { useSelector } from 'react-redux';
import { RootState } from '../../store/store';
import { getUniqueId, isTablet } from 'react-native-device-info';
import { setDeviceId } from '../../reducer/userSlice';
import { DEVICE_TYPE_ENUM, setGlobalUserData } from '../../constants/Global';
import { DEVICE_TYPE_ENUM, setGlobalUserData, setMsClarityConfig } from '../../constants/Global';
import { registerNavigateAndDispatch } from '../../components/utlis/apiHelper';
import ProtectedRouter from './ProtectedRouter';
import useNativeButtons from '../../hooks/useNativeButton';
@@ -38,6 +38,8 @@ const AuthRouter = () => {
isImpersonated: user?.isImpersonated ?? false,
});
setMsClarityConfig({ customUserId: user?.user?.phoneNumber || user?.user?.emailId });
// Sets the dispatch for apiHelper
registerNavigateAndDispatch(dispatch);

View File

@@ -47,7 +47,12 @@ const FeedbackDetailImageItem: React.FC<IFeedbackDetailImageItem> = ({ image })
onPress={handleExpandImage}
/>
</TouchableOpacity>
<Modal visible={isImageExpanded} transparent animationType="fade">
<Modal
visible={isImageExpanded}
transparent
onRequestClose={handleExpandedImageClose}
animationType="fade"
>
<ExpandableImage
imageSrc={image.inputText}
title={questionText}

View File

@@ -8,7 +8,7 @@ import {
BUSINESS_TIME_FORMAT,
dateFormat,
} from '../../../../RN-UI-LIB/src/utlis/dates';
import { sanitizeString } from '../../../components/utlis/commonFunctions';
import { getGoogleMapUrl, sanitizeString } from '../../../components/utlis/commonFunctions';
import { FIELD_FEEDBACKS, ICallingFeedback, IFeedback } from '../../../types/feedback.types';
import { Address as IAddress } from '../interface';
import MapIcon from '../../../../RN-UI-LIB/src/Icons/MapIcon';
@@ -42,9 +42,8 @@ const openGeolocation = (latitude: string, longitude: string) => {
latitude: latitude,
longitude: longitude,
});
if (!latitude || !longitude) return;
const geolocationUrl = `http://maps.google.com/?q=${latitude},${longitude}`;
const geolocationUrl = getGoogleMapUrl(latitude, longitude);
if (!geolocationUrl) return;
return Linking.openURL(geolocationUrl);
};

View File

@@ -181,6 +181,7 @@ export interface CaseDetail {
addresses?: Address[];
currentAllocationReferenceId: string;
customerReferenceId: string;
caseViewCreatedAt?: number;
// collection case
addressString?: string;
currentOutstandingEmi?: number;

View File

@@ -15,6 +15,7 @@ import {
copyToClipboard,
getDynamicBottomSheetHeightPercentageFn,
getPhoneNumberString,
isValidAmountEntered,
} from '../../components/utlis/commonFunctions';
import useIsOnline from '../../hooks/useIsOnline';
import { RootState } from '../../store/store';
@@ -29,6 +30,7 @@ import { CLICKSTREAM_EVENT_NAMES } from '../../common/Constants';
import QrCodeModal from './QrCodeModal';
import ModalWrapper from '../../../RN-UI-LIB/src/components/modalWrapper/ModalWrapper';
import OfflineScreen from '../../common/OfflineScreen';
import { formatAmount } from '../../../RN-UI-LIB/src/utlis/amount';
interface IRegisterForm {
selectedPhoneNumber: string;
@@ -116,10 +118,10 @@ const RegisterPayments: React.FC<IRegisterPayments> = ({ route }) => {
errorMessage = 'This is a required field';
break;
case 'min':
errorMessage = 'Amount should be greater than min amount';
break;
case 'max':
errorMessage = 'Amount should be less than max amount';
errorMessage = `The entered amount should be between ${formatAmount(1)} and ${formatAmount(
maxAmount
)}`;
break;
}
@@ -217,7 +219,12 @@ const RegisterPayments: React.FC<IRegisterPayments> = ({ route }) => {
/>
)}
name="amount"
rules={{ required: true, min: 1, max: maxAmount }}
rules={{
required: true,
min: 1,
max: maxAmount,
validate: (value) => isValidAmountEntered(Number(value)),
}}
/>
</View>
<Button

View File

@@ -1,10 +1,11 @@
import NetInfo from '@react-native-community/netinfo';
import NetInfo, { NetInfoStateType } from '@react-native-community/netinfo';
import { setNetworkStatus } from './clickstreamEventService';
import { setIsOnline } from '../reducer/metadataSlice';
import { setIsOnline, setIsWifiOrCellularOn } from '../reducer/metadataSlice';
export class NetworkStatusService {
private static isOnline = false;
private static isAlreadyListening = false;
private static isWifiOrCellularOn = false;
// private static listeners: any[] = [];
private constructor() {
// singleton
@@ -29,6 +30,14 @@ export class NetworkStatusService {
// listen for changes.
NetInfo.addEventListener((state) => {
setNetworkStatus(state);
const { type } = state;
if (type === NetInfoStateType.none && NetworkStatusService.isWifiOrCellularOn) {
NetworkStatusService.isWifiOrCellularOn = false;
dispatch(setIsWifiOrCellularOn(false));
} else if (type !== NetInfoStateType.none && !NetworkStatusService.isWifiOrCellularOn) {
NetworkStatusService.isWifiOrCellularOn = true;
dispatch(setIsWifiOrCellularOn(true));
}
if (NetworkStatusService.isOnline !== state.isConnected) {
NetworkStatusService.isOnline = !!state.isConnected && !!state.isInternetReachable;
dispatch(setIsOnline(NetworkStatusService.isOnline));

View File

@@ -7566,6 +7566,11 @@ react-native-call-log@2.1.2:
resolved "https://registry.yarnpkg.com/react-native-call-log/-/react-native-call-log-2.1.2.tgz#f80d2fcb45f72118eb8048d5bfdef191fd4a3df3"
integrity sha512-nWHmb+QMN/AbbZFEuUGiQePssPgjQr5dibNAURDlqO4S5wuLk1XzxxsLUAVHZnB0FdJoMlajD7tUAXtaoxYwUQ==
react-native-clarity@^0.0.3:
version "0.0.3"
resolved "https://registry.yarnpkg.com/react-native-clarity/-/react-native-clarity-0.0.3.tgz#e9c83dd097c0cdaf0751c7f6683fc3b038134637"
integrity sha512-uXccmC9iKwxBpsy7jOLaEo87RBQax/WNCI5EDa/hldn1+8eNCwwhkUdJZeNgi1pZ/dyBmHZj0Be0jgWy4K4gkA==
react-native-code-push@7.1.0:
version "7.1.0"
resolved "https://registry.yarnpkg.com/react-native-code-push/-/react-native-code-push-7.1.0.tgz#9767518d684017993ff32875bfd349ce01298d35"