diff --git a/App.tsx b/App.tsx index 74506a8a..66f286ff 100644 --- a/App.tsx +++ b/App.tsx @@ -32,8 +32,9 @@ import CodePush from 'react-native-code-push'; import { TDocumentObj } from './src/screens/caseDetails/interface'; import AuthRouter from './src/screens/auth/AuthRouter'; import { initSentry } from './src/components/utlis/sentry'; -import { setGlobalBuildFlavour } from './src/constants/Global'; -import { getBuildFlavour } from './src/components/utlis/DeviceUtils'; +import { GLOBAL, setGlobalBuildConfig, setGlobalBuildFlavour } from './src/constants/Global'; +import { getBuildFlavour, getBuildInfo } from './src/components/utlis/DeviceUtils'; +import { get } from 'react-hook-form'; initSentry(); @@ -107,12 +108,14 @@ const App = () => { getBuildFlavour().then((flavour) => { setGlobalBuildFlavour(flavour); }); + getBuildInfo().then((buildInfo) => { + setGlobalBuildConfig(JSON.parse(buildInfo)); + }); checkCodePushAndSync(); return () => { appStateChange.remove(); }; }, []); - return ( } persistor={persistor}> diff --git a/android/app/src/main/java/com/avapp/DeviceUtilsModule.java b/android/app/src/main/java/com/avapp/DeviceUtilsModule.java index bce94a09..79f9fd9b 100644 --- a/android/app/src/main/java/com/avapp/DeviceUtilsModule.java +++ b/android/app/src/main/java/com/avapp/DeviceUtilsModule.java @@ -89,4 +89,16 @@ public class DeviceUtilsModule extends ReactContextBaseJavaModule implements Act promise.reject(err); } } + + @ReactMethod + public void getBuildInfo(Promise promise) { + try { + JSONObject mainObject = new JSONObject(); + mainObject.put("versionCode", BuildConfig.VERSION_CODE); + mainObject.put("versionName", BuildConfig.VERSION_NAME); + promise.resolve(mainObject.toString()); + } catch (Exception err) { + promise.reject(err); + } + } } diff --git a/src/common/BlockerScreen.tsx b/src/common/BlockerScreen.tsx index 3a50fc9e..02cebeca 100644 --- a/src/common/BlockerScreen.tsx +++ b/src/common/BlockerScreen.tsx @@ -9,7 +9,8 @@ import { BLOCKER_SCREEN_DATA } from './Constants'; import { useAppDispatch, useAppSelector } from '../hooks'; import { setIsDeviceLocationEnabled } from '../reducer/foregroundServiceSlice'; import { toast } from '../../RN-UI-LIB/src/components/toast'; -import { locationEnabled } from '../components/utlis/DeviceUtils'; +import { buildFlavour, locationEnabled } from '../components/utlis/DeviceUtils'; +import { GLOBAL } from '../constants/Global'; interface IBlockerScreen { children?: ReactNode; @@ -20,6 +21,7 @@ const RETRY_GEOLOCATION_STEPS = const BlockerScreen = (props: IBlockerScreen) => { const [forceReinstallData, setForceReinstallData] = useState(); + const [shouldUpdate, setShouldUpdate] = useState(); const { isTimeSynced, isDeviceLocationEnabled } = useAppSelector( (state) => state.foregroundService ); @@ -32,6 +34,10 @@ const BlockerScreen = (props: IBlockerScreen) => { return state.metadata?.forceUninstall; }); + const appUpdate = useSelector((state: RootState) => { + return state.metadata?.appUpdate; + }); + function compareSemverVersions(a: string, b: string) { return a.localeCompare(b, undefined, { numeric: true, sensitivity: 'base' }); } @@ -54,12 +60,33 @@ const BlockerScreen = (props: IBlockerScreen) => { setForceReinstallData(undefined); }, [JSON.stringify(forceUninstallData || {})]); + React.useEffect(() => { + if (!appUpdate) return; + const buildToCompare = GLOBAL.BUILD_FLAVOUR; + if (!buildToCompare) return; + const flavorToUpdate = appUpdate[buildToCompare]; + if (!flavorToUpdate) return; + const currentBuildNumber = GLOBAL.BUILD_CONFIG?.versionCode; + if (currentBuildNumber && currentBuildNumber < flavorToUpdate.buildNumber) { + setShouldUpdate(true); + } else { + setShouldUpdate(false); + } + }, [JSON.stringify(appUpdate || {})]); + const handleDownloadNewApp = () => { if (forceReinstallData?.reinstall_endpoint) { openApkDownloadLink(forceReinstallData?.reinstall_endpoint); } }; + const handleAppUpdate = () => { + const appUpdateUrl = appUpdate?.[GLOBAL.BUILD_FLAVOUR as buildFlavour]?.currentProdAPK; + if (appUpdateUrl) { + openApkDownloadLink(appUpdateUrl); + } + }; + const handleOpenSettings = useCallback(async () => { await Linking.openSettings(); }, []); @@ -86,17 +113,28 @@ const BlockerScreen = (props: IBlockerScreen) => { ); } - if (!isTimeSynced) { - const { heading, instructions } = BLOCKER_SCREEN_DATA.TIME_UNSYNC; + if (shouldUpdate) { + const { heading, instructions } = BLOCKER_SCREEN_DATA.UNINSTALL_APP; return ( ); } + // if (!isTimeSynced) { + // const { heading, instructions } = BLOCKER_SCREEN_DATA.TIME_UNSYNC; + // return ( + // + // ); + // } + if (!isDeviceLocationEnabled) { const { heading, instructions } = BLOCKER_SCREEN_DATA.DEVICE_LOCATION_OFF; return ( diff --git a/src/common/TrackingComponent.tsx b/src/common/TrackingComponent.tsx index 2d21f51e..9a7838de 100644 --- a/src/common/TrackingComponent.tsx +++ b/src/common/TrackingComponent.tsx @@ -27,6 +27,8 @@ import { syncCasesByFallback } from '../reducer/allCasesSlice'; import { MILLISECONDS_IN_A_MINUTE } from '../../RN-UI-LIB/src/utlis/common'; import { setLockData } from '../reducer/userSlice'; import { getConfigData } from '../action/configActions'; +import { GLOBAL } from '../constants/Global'; +import { getBuildFlavour } from '../components/utlis/DeviceUtils'; export enum FOREGROUND_TASKS { GEOLOCATION = 'GEOLOCATION', @@ -146,7 +148,10 @@ const TrackingComponent: React.FC = ({ children }) => { tasks.push({ taskId: FOREGROUND_TASKS.DATA_SYNC, task: dataSyncService, - delay: DATA_SYNC_TIME_INTERVAL, + delay: + GLOBAL?.BUILD_FLAVOUR === 'callingAgents' + ? DATA_SYNC_TIME_INTERVAL + : 5 * MILLISECONDS_IN_A_MINUTE, onLoop: true, }); } diff --git a/src/components/utlis/DeviceUtils.ts b/src/components/utlis/DeviceUtils.ts index 5cd5fc64..babaea34 100644 --- a/src/components/utlis/DeviceUtils.ts +++ b/src/components/utlis/DeviceUtils.ts @@ -12,3 +12,10 @@ export const getAllInstalledApp = (): Promise => DeviceUtilsModule.getAl export type buildFlavour = 'fieldAgents' | 'callingAgents'; export const getBuildFlavour = (): Promise => DeviceUtilsModule.getBuildFlavour(); + +export type buildConfig = { + versionCode: number; + versionName: string; +}; + +export const getBuildInfo = (): Promise => DeviceUtilsModule.getBuildInfo(); diff --git a/src/constants/Global.ts b/src/constants/Global.ts index 8dc7bbf1..2e48153b 100644 --- a/src/constants/Global.ts +++ b/src/constants/Global.ts @@ -1,4 +1,4 @@ -import { buildFlavour, getBuildFlavour } from '../components/utlis/DeviceUtils'; +import { buildConfig, buildFlavour, getBuildFlavour } from '../components/utlis/DeviceUtils'; import { isNullOrUndefined } from '../components/utlis/commonFunctions'; export enum DEVICE_TYPE_ENUM { @@ -12,8 +12,9 @@ export const GLOBAL = { AGENT_ID: '', DEVICE_TYPE: DEVICE_TYPE_ENUM.MOBILE, IS_IMPERSONATED: false, - BUILD_FLAVOUR: '', -}; + BUILD_FLAVOUR: null, + BUILD_CONFIG: null, +} as unknown as IGlobalUserData; interface IGlobalUserData { token?: string; @@ -22,6 +23,12 @@ interface IGlobalUserData { deviceType?: DEVICE_TYPE_ENUM; isImpersonated?: boolean; BUILD_FLAVOUR?: buildFlavour; + BUILD_CONFIG?: buildConfig; + SESSION_TOKEN: string; + DEVICE_ID: string; + AGENT_ID: string; + IS_IMPERSONATED: boolean; + DEVICE_TYPE: DEVICE_TYPE_ENUM; } export const setGlobalUserData = (userData: IGlobalUserData) => { @@ -34,5 +41,9 @@ export const setGlobalUserData = (userData: IGlobalUserData) => { }; export const setGlobalBuildFlavour = (buildFlavour: buildFlavour) => { - if (GLOBAL.BUILD_FLAVOUR) GLOBAL.BUILD_FLAVOUR = buildFlavour; + if (buildFlavour) GLOBAL.BUILD_FLAVOUR = buildFlavour; +}; + +export const setGlobalBuildConfig = (buildConfig: buildConfig) => { + if (buildConfig) GLOBAL.BUILD_CONFIG = buildConfig; }; diff --git a/src/hooks/useFirestoreUpdates.ts b/src/hooks/useFirestoreUpdates.ts index ba7c9825..1ce2e88c 100644 --- a/src/hooks/useFirestoreUpdates.ts +++ b/src/hooks/useFirestoreUpdates.ts @@ -12,7 +12,7 @@ import { ILockData, setLockData, VisitPlanStatus } from '../reducer/userSlice'; import { setFilters } from '../reducer/filtersSlice'; import { FormTemplateV1 } from '../types/template.types'; import { ToastMessages } from '../screens/allCases/constants'; -import { setForceUninstallData } from '../reducer/metadataSlice'; +import { setCurrentProdAPK, setForceUninstallData } from '../reducer/metadataSlice'; import { logError } from '../components/utlis/errorUtils'; import { GenericFunctionArgs } from '../common/GenericTypes'; @@ -52,6 +52,7 @@ const useFirestoreUpdates = () => { let filterUnsubscribe: GenericFunctionArgs; let forceUninstallUnsubscribe: GenericFunctionArgs; let lockUnsubscribe: GenericFunctionArgs; + let appUpdateUnsubscribe: GenericFunctionArgs; const dispatch = useAppDispatch(); @@ -139,6 +140,13 @@ const useFirestoreUpdates = () => { dispatch(setForceUninstallData(configData)); }; + const handleAppUpdate = ( + snapshot: FirebaseFirestoreTypes.DocumentSnapshot + ) => { + const configData = snapshot.data(); + dispatch(setCurrentProdAPK(configData)); + }; + const handleFilterUpdate = ( snapshot: FirebaseFirestoreTypes.DocumentSnapshot ) => { @@ -211,6 +219,16 @@ const useFirestoreUpdates = () => { return subscribeToDoc(handleForceUninstallUpdate, collectionPath); }; + const subscribeToAppUpdate = () => { + const collectionPath = 'app-state/app-update'; + console.log( + 'subscribeToAppUpdate', + collectionPath, + subscribeToDoc(handleAppUpdate, collectionPath) + ); + return subscribeToDoc(handleAppUpdate, collectionPath); + }; + const subscribeToFilters = () => { const collectionPath = `filters/${user?.referenceId}`; return subscribeToDoc(handleFilterUpdate, collectionPath); @@ -237,6 +255,7 @@ const useFirestoreUpdates = () => { avTemplateUnSubscriber = subscribeToAvTemplate(); collectionTemplateUnsubscribe = subscribeToCollectionTemplate(); lockUnsubscribe = subscribeToLocks(); + appUpdateUnsubscribe = subscribeToAppUpdate(); } useEffect(() => { @@ -265,6 +284,7 @@ const useFirestoreUpdates = () => { useEffect(() => { forceUninstallUnsubscribe = subscribeToForceUninstall(); + subscribeToAppUpdate(); }, []); }; diff --git a/src/reducer/metadataSlice.ts b/src/reducer/metadataSlice.ts index 71ec5d8d..654906d0 100644 --- a/src/reducer/metadataSlice.ts +++ b/src/reducer/metadataSlice.ts @@ -1,4 +1,5 @@ import { createSlice } from '@reduxjs/toolkit'; +import { buildFlavour } from '../components/utlis/DeviceUtils'; export interface UninstallInformation { last_operational_time: any; reinstall_endpoint: string; @@ -8,12 +9,20 @@ interface IMetadata { isOnline: boolean; forceUninstall: Record; isWifiOrCellularOn: boolean; + appUpdate: Record; } +type typeAppUpdate = { + buildVersion: string; + buildNumber: number; + currentProdAPK: string; +}; + const initialState = { isOnline: true, forceUninstall: {}, isWifiOrCellularOn: true, + appUpdate: {}, } as IMetadata; const MetadataSlice = createSlice({ @@ -29,9 +38,13 @@ const MetadataSlice = createSlice({ setIsWifiOrCellularOn: (state, action) => { state.isWifiOrCellularOn = action.payload; }, + setCurrentProdAPK: (state, action) => { + state.appUpdate = action.payload; + }, }, }); -export const { setIsOnline, setForceUninstallData, setIsWifiOrCellularOn } = MetadataSlice.actions; +export const { setIsOnline, setForceUninstallData, setIsWifiOrCellularOn, setCurrentProdAPK } = + MetadataSlice.actions; export default MetadataSlice.reducer;