app update architecture| Aman Singh
This commit is contained in:
5
App.tsx
5
App.tsx
@@ -45,6 +45,8 @@ import analytics from '@react-native-firebase/analytics';
|
||||
import fetchUpdatedRemoteConfig from './src/services/firebaseFetchAndUpdate.service';
|
||||
import { addClickstreamEvent } from './src/services/clickstreamEventService';
|
||||
import ScreenshotBlocker from './src/components/utlis/ScreenshotBlocker';
|
||||
import { getBuildFlavour } from '@components/utlis/DeviceUtils';
|
||||
import { setGlobalBuildFlavour } from '@constants/Global';
|
||||
|
||||
initSentry();
|
||||
|
||||
@@ -127,6 +129,9 @@ function App() {
|
||||
|
||||
useEffect(() => {
|
||||
ScreenshotBlocker.unblockScreenshots();
|
||||
getBuildFlavour().then((flavour) => {
|
||||
setGlobalBuildFlavour(flavour);
|
||||
});
|
||||
}, []);
|
||||
|
||||
React.useEffect(() => {
|
||||
|
||||
@@ -1,34 +1,33 @@
|
||||
{
|
||||
"project_info": {
|
||||
"project_number": "60755663443",
|
||||
"project_id": "address-verification-app",
|
||||
"storage_bucket": "address-verification-app.appspot.com",
|
||||
"firebase_url": "https://address-verification-app-default-rtdb.firebaseio.com"
|
||||
"project_number": "136591056725",
|
||||
"project_id": "field-verification-app",
|
||||
"storage_bucket": "field-verification-app.appspot.com"
|
||||
},
|
||||
"client": [
|
||||
{
|
||||
"client_info": {
|
||||
"mobilesdk_app_id": "1:60755663443:android:4a948ee9d0b4e3098584a6",
|
||||
"mobilesdk_app_id": "1:136591056725:android:c454085ec6505cc01519dc",
|
||||
"android_client_info": {
|
||||
"package_name": "com.avapp"
|
||||
}
|
||||
},
|
||||
"oauth_client": [
|
||||
{
|
||||
"client_id": "60755663443-40k0fbrbbqv4ci4hrjlbrphab5fj387b.apps.googleusercontent.com",
|
||||
"client_id": "136591056725-ev8db4hrlud2m23n0o03or3cmmp3a3cq.apps.googleusercontent.com",
|
||||
"client_type": 3
|
||||
}
|
||||
],
|
||||
"api_key": [
|
||||
{
|
||||
"current_key": "AIzaSyA70_d2M2ke-Mu0OHGZ6iZilBbD6A-_z0c"
|
||||
"current_key": "AIzaSyBL32d7WRJTcJawKjT1XCEcFbGGQ8wA6j8"
|
||||
}
|
||||
],
|
||||
"services": {
|
||||
"appinvite_service": {
|
||||
"other_platform_oauth_client": [
|
||||
{
|
||||
"client_id": "60755663443-40k0fbrbbqv4ci4hrjlbrphab5fj387b.apps.googleusercontent.com",
|
||||
"client_id": "136591056725-ev8db4hrlud2m23n0o03or3cmmp3a3cq.apps.googleusercontent.com",
|
||||
"client_type": 3
|
||||
}
|
||||
]
|
||||
@@ -37,4 +36,4 @@
|
||||
}
|
||||
],
|
||||
"configuration_version": "1"
|
||||
}
|
||||
}
|
||||
@@ -315,4 +315,14 @@ public class DeviceUtilsModule extends ReactContextBaseJavaModule {
|
||||
}
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void getBuildFlavour(Promise promise) {
|
||||
try {
|
||||
String buildFlavour = BuildConfig.FLAVOR;
|
||||
promise.resolve(buildFlavour);
|
||||
} catch (Exception err) {
|
||||
promise.reject(err);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"name": "AV_APP",
|
||||
"version": "2.6.6",
|
||||
"buildNumber": "201",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"android:dev": "yarn move:dev && react-native run-android",
|
||||
|
||||
@@ -2,8 +2,8 @@ import React, { ReactNode, useCallback, useState } from 'react';
|
||||
import { AppState, Linking } from 'react-native';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { RootState } from '../store/store';
|
||||
import { UninstallInformation } from '../reducer/metadataSlice';
|
||||
import { getAppVersion } from '../components/utlis/commonFunctions';
|
||||
import { IAppState, UninstallInformation } from '../reducer/metadataSlice';
|
||||
import { getAppVersion, getBuildVersion } from '../components/utlis/commonFunctions';
|
||||
import BlockerInstructions from './BlockerInstructions';
|
||||
import { BLOCKER_SCREEN_DATA, CLICKSTREAM_EVENT_NAMES } from './Constants';
|
||||
import { useAppDispatch, useAppSelector } from '../hooks';
|
||||
@@ -18,6 +18,7 @@ import handleBlacklistedAppsForBlockingCosmos, {
|
||||
import { addClickstreamEvent } from '@services/clickstreamEventService';
|
||||
import { setBlacklistedAppsInstalledData } from '@reducers/blacklistedAppsInstalledSlice';
|
||||
import perf from '@react-native-firebase/perf';
|
||||
import { GLOBAL } from '@constants/Global';
|
||||
|
||||
interface IBlockerScreen {
|
||||
children?: ReactNode;
|
||||
@@ -31,7 +32,13 @@ const BlockerScreen = (props: IBlockerScreen) => {
|
||||
const { isTimeSynced, isDeviceLocationEnabled } = useAppSelector(
|
||||
(state) => state.foregroundService
|
||||
);
|
||||
const { isWifiOrCellularOn } = useAppSelector((state) => state.metadata);
|
||||
const { isWifiOrCellularOn, appState } = useAppSelector((state) => state.metadata);
|
||||
|
||||
const appUpdate = useSelector((state: RootState) => {
|
||||
return state.metadata?.appState;
|
||||
});
|
||||
|
||||
const [shouldUpdate, setShouldUpdate] = useState<boolean>();
|
||||
|
||||
const [showActionBtnLoader, setShowActionBtnLoader] = useState(false);
|
||||
const dispatch = useAppDispatch();
|
||||
@@ -66,6 +73,31 @@ const BlockerScreen = (props: IBlockerScreen) => {
|
||||
setForceReinstallData(undefined);
|
||||
}, [JSON.stringify(forceUninstallData || {})]);
|
||||
|
||||
|
||||
React.useEffect(() => {
|
||||
console.log("firebase updated", appUpdate)
|
||||
if (!appUpdate) return;
|
||||
const buildToCompare = GLOBAL.BUILD_FLAVOUR;
|
||||
if (!buildToCompare) return;
|
||||
let flavorToUpdate: IAppState;
|
||||
if(GLOBAL.BUILD_FLAVOUR.includes('fieldAgents')){
|
||||
flavorToUpdate = appUpdate?.fieldAgent;
|
||||
}else {
|
||||
flavorToUpdate = appUpdate?.telecallingAgents;
|
||||
}
|
||||
console.log("firebase updated :: flavor to update", flavorToUpdate, GLOBAL.BUILD_FLAVOUR.includes('fieldAgents'))
|
||||
|
||||
if (!flavorToUpdate) return;
|
||||
const currentBuildNumber = getBuildVersion();
|
||||
console.log("firebase updated", flavorToUpdate, {currentBuildNumber})
|
||||
|
||||
if (currentBuildNumber && currentBuildNumber < flavorToUpdate.buildNumber) {
|
||||
setShouldUpdate(true);
|
||||
} else {
|
||||
setShouldUpdate(false);
|
||||
}
|
||||
}, [JSON.stringify(appUpdate || {})]);
|
||||
|
||||
React.useEffect(() => {
|
||||
const appStateChange = AppState.addEventListener('change', async (change) => {
|
||||
setTimeout(async () => {
|
||||
@@ -92,6 +124,20 @@ const BlockerScreen = (props: IBlockerScreen) => {
|
||||
await Linking.openSettings();
|
||||
}, []);
|
||||
|
||||
const handleAppUpdate = () => {
|
||||
let appUpdateUrl;
|
||||
if(GLOBAL.BUILD_FLAVOUR.includes('fieldAgents')){
|
||||
appUpdateUrl = appUpdate?.fieldAgent.currentProdAPK;
|
||||
}else {
|
||||
appUpdateUrl = appUpdate?.telecallingAgents.currentProdAPK;
|
||||
}
|
||||
console.log({appUpdateUrl})
|
||||
|
||||
if (appUpdateUrl) {
|
||||
openApkDownloadLink(appUpdateUrl);
|
||||
}
|
||||
};
|
||||
|
||||
const handleLocationAccess = async () => {
|
||||
setShowActionBtnLoader(true);
|
||||
const isLocationEnabled = await locationEnabled();
|
||||
@@ -103,6 +149,18 @@ const BlockerScreen = (props: IBlockerScreen) => {
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
if (shouldUpdate) {
|
||||
const { heading, instructions } = BLOCKER_SCREEN_DATA.UNINSTALL_APP;
|
||||
return (
|
||||
<BlockerInstructions
|
||||
heading={heading}
|
||||
instructions={instructions}
|
||||
actionBtn={{ title: 'Download New App', action: handleAppUpdate }}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
if (forceReinstallData?.reinstall_endpoint) {
|
||||
const { heading, instructions } = BLOCKER_SCREEN_DATA.UNINSTALL_APP;
|
||||
return (
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { buildFlavour } from '@reducers/metadataSlice';
|
||||
import { NativeModules } from 'react-native';
|
||||
|
||||
const { DeviceUtilsModule } = NativeModules; // this is the same name we returned in getName function.
|
||||
@@ -71,4 +72,6 @@ export const setBottomSheetView = (id: number | null) => noop();
|
||||
|
||||
export const clearBottomSheet = () => noop();
|
||||
|
||||
export const alfredSetEmailId = (emailId: string) => noop();
|
||||
export const alfredSetEmailId = (emailId: string) => noop();
|
||||
|
||||
export const getBuildFlavour = (): Promise<buildFlavour> => DeviceUtilsModule.getBuildFlavour();
|
||||
@@ -26,6 +26,7 @@ import { IGeolocationCoordinate } from '../../types/addressGeolocation.types';
|
||||
import { toast } from '@rn-ui-lib/components/toast';
|
||||
import { sendContentToWhatsapp } from './DeviceUtils';
|
||||
import { ToastMessages } from '@screens/allCases/constants';
|
||||
import VersionNumber from 'react-native-version-number';
|
||||
|
||||
const fs = ReactNativeBlobUtil.fs;
|
||||
|
||||
@@ -243,6 +244,10 @@ export function getAppVersion(): string {
|
||||
return packageJson.version;
|
||||
}
|
||||
|
||||
export const getBuildVersion = (): string => {
|
||||
return packageJson?.buildNumber || VersionNumber.buildVersion;
|
||||
}
|
||||
|
||||
export const getDocumentList = (caseDetails: CaseDetail) => {
|
||||
return caseDetails.caseType === CaseAllocationType.ADDRESS_VERIFICATION_CASE
|
||||
? caseDetails.customerInfo?.documents
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { buildFlavour } from '@reducers/metadataSlice';
|
||||
import { isNullOrUndefined } from '../components/utlis/commonFunctions';
|
||||
|
||||
export enum DEVICE_TYPE_ENUM {
|
||||
@@ -12,6 +13,7 @@ export const GLOBAL = {
|
||||
DEVICE_TYPE: DEVICE_TYPE_ENUM.MOBILE,
|
||||
IS_IMPERSONATED: false,
|
||||
SELECTED_AGENT_ID: '',
|
||||
BUILD_FLAVOUR: ''
|
||||
};
|
||||
|
||||
interface IGlobalUserData {
|
||||
@@ -32,3 +34,7 @@ export const setGlobalUserData = (userData: IGlobalUserData) => {
|
||||
if (!isNullOrUndefined(isImpersonated)) GLOBAL.IS_IMPERSONATED = isImpersonated ?? false;
|
||||
if (!isNullOrUndefined(selectedAgentId)) GLOBAL.SELECTED_AGENT_ID = `${selectedAgentId}`;
|
||||
};
|
||||
|
||||
export const setGlobalBuildFlavour = (buildFlavour: buildFlavour) => {
|
||||
if (buildFlavour) GLOBAL.BUILD_FLAVOUR = buildFlavour;
|
||||
};
|
||||
|
||||
@@ -15,7 +15,7 @@ import { type ILockData, MY_CASE_ITEM, setLockData, VisitPlanStatus } from '../r
|
||||
import { setFilters } from '../reducer/filtersSlice';
|
||||
import { type 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 { type GenericFunctionArgs } from '../common/GenericTypes';
|
||||
import { addClickstreamEvent } from '@services/clickstreamEventService';
|
||||
@@ -57,7 +57,7 @@ const useFirestoreUpdates = () => {
|
||||
let forceUninstallUnsubscribe: GenericFunctionArgs;
|
||||
let lockUnsubscribe: GenericFunctionArgs;
|
||||
let feedbackFiltersUnsubscribe: GenericFunctionArgs;
|
||||
|
||||
let appUpdateUnsubscribe: GenericFunctionArgs;
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
const showCaseUpdationToast = (newlyAddedCases: number, deletedCases: number) => {
|
||||
@@ -187,6 +187,18 @@ const useFirestoreUpdates = () => {
|
||||
logError(err as Error, errMsg);
|
||||
};
|
||||
|
||||
const handleAppUpdate = (
|
||||
snapshot: FirebaseFirestoreTypes.DocumentSnapshot<FirebaseFirestoreTypes.DocumentData>
|
||||
) => {
|
||||
const configData = snapshot.data();
|
||||
dispatch(setCurrentProdAPK(configData));
|
||||
};
|
||||
|
||||
const subscribeToAppUpdate = () => {
|
||||
const collectionPath = 'app-state/app-update';
|
||||
return subscribeToDoc(handleAppUpdate, collectionPath);
|
||||
};
|
||||
|
||||
const signInUserToFirebase = () => {
|
||||
if (!sessionDetails) {
|
||||
return;
|
||||
@@ -290,6 +302,7 @@ const useFirestoreUpdates = () => {
|
||||
collectionTemplateUnsubscribe = subscribeToCollectionTemplate();
|
||||
lockUnsubscribe = subscribeToLocks();
|
||||
feedbackFiltersUnsubscribe = subscribeToFeedbackFilters();
|
||||
appUpdateUnsubscribe = subscribeToAppUpdate();
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
@@ -345,6 +358,7 @@ const useFirestoreUpdates = () => {
|
||||
|
||||
useEffect(() => {
|
||||
forceUninstallUnsubscribe = subscribeToForceUninstall();
|
||||
subscribeToAppUpdate();
|
||||
}, []);
|
||||
};
|
||||
|
||||
|
||||
@@ -5,16 +5,28 @@ export interface UninstallInformation {
|
||||
reinstall_endpoint: string;
|
||||
}
|
||||
|
||||
export interface IAppState {
|
||||
buildNumber: string;
|
||||
currentProdAPK?: string;
|
||||
}
|
||||
|
||||
interface IMetadata {
|
||||
isOnline: boolean;
|
||||
forceUninstall: Record<string, UninstallInformation>;
|
||||
isWifiOrCellularOn: boolean;
|
||||
appState: {
|
||||
fieldAgent: IAppState,
|
||||
telecallingAgents: IAppState
|
||||
};
|
||||
}
|
||||
|
||||
export type buildFlavour = 'fieldAgents' | 'callingAgents';
|
||||
|
||||
const initialState = {
|
||||
isOnline: true,
|
||||
forceUninstall: {},
|
||||
isWifiOrCellularOn: true,
|
||||
appState: {}
|
||||
} as IMetadata;
|
||||
|
||||
const MetadataSlice = createSlice({
|
||||
@@ -30,9 +42,12 @@ const MetadataSlice = createSlice({
|
||||
setIsWifiOrCellularOn: (state, action) => {
|
||||
state.isWifiOrCellularOn = action.payload;
|
||||
},
|
||||
setCurrentProdAPK(state, action) {
|
||||
state.appState = action.payload;
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
export const { setIsOnline, setForceUninstallData, setIsWifiOrCellularOn } = MetadataSlice.actions;
|
||||
export const { setIsOnline, setForceUninstallData, setIsWifiOrCellularOn, setCurrentProdAPK } = MetadataSlice.actions;
|
||||
|
||||
export default MetadataSlice.reducer;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { createSlice } from '@reduxjs/toolkit';
|
||||
import { setGlobalUserData } from '../constants/Global';
|
||||
import { IReportee } from '../screens/allCases/interface';
|
||||
import { getAppVersion, getBuildVersion } from '@components/utlis/commonFunctions';
|
||||
|
||||
interface ISessionDetails {
|
||||
sessionToken: string;
|
||||
@@ -68,6 +69,10 @@ export interface IUserSlice extends IUser {
|
||||
showAttendanceBanner: boolean;
|
||||
attendanceDate: string;
|
||||
};
|
||||
buildInfo: {
|
||||
buildNumber: string;
|
||||
buildVersion: string;
|
||||
};
|
||||
}
|
||||
|
||||
const initialState: IUserSlice = {
|
||||
@@ -89,6 +94,10 @@ const initialState: IUserSlice = {
|
||||
showAttendanceBanner: true,
|
||||
attendanceDate: '',
|
||||
},
|
||||
buildInfo: {
|
||||
buildNumber: getBuildVersion(),
|
||||
buildVersion: getAppVersion(),
|
||||
}
|
||||
};
|
||||
|
||||
export const userSlice = createSlice({
|
||||
@@ -141,6 +150,13 @@ export const userSlice = createSlice({
|
||||
setAgentAttendance: (state, action) => {
|
||||
state.agentAttendance = action.payload;
|
||||
},
|
||||
setBuildInfo: (state, action) => {
|
||||
const data = {
|
||||
buildNumber: getBuildVersion(),
|
||||
buildVersion: getAppVersion(),
|
||||
}
|
||||
state.buildInfo = data;
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
@@ -153,6 +169,7 @@ export const {
|
||||
setIsExternalAgent,
|
||||
setCaseSyncLock,
|
||||
setAgentAttendance,
|
||||
setBuildInfo
|
||||
} = userSlice.actions;
|
||||
|
||||
export default userSlice.reducer;
|
||||
|
||||
@@ -25,7 +25,7 @@ import { COLORS } from '../../../RN-UI-LIB/src/styles/colors';
|
||||
import Button from '../../../RN-UI-LIB/src/components/Button';
|
||||
import { navigateToScreen } from '../../components/utlis/navigationUtlis';
|
||||
import GroupIcon from '../../../RN-UI-LIB/src/Icons/GroupIcon';
|
||||
import { getAppVersion } from '../../components/utlis/commonFunctions';
|
||||
import { getAppVersion, getBuildVersion } from '../../components/utlis/commonFunctions';
|
||||
import VersionNumber from 'react-native-version-number';
|
||||
import { useFocusEffect } from '@react-navigation/native';
|
||||
import { CaseDetail } from '../caseDetails/interface';
|
||||
@@ -261,7 +261,7 @@ const Profile: React.FC = () => {
|
||||
>
|
||||
<Text bold dark style={styles.version}>
|
||||
App Version: {getAppVersion()} Gradle Version: {VersionNumber.appVersion} Gradle Build
|
||||
No: {VersionNumber.buildVersion}
|
||||
No: {getBuildVersion()}
|
||||
</Text>
|
||||
</Pressable>
|
||||
</ScrollView>
|
||||
|
||||
@@ -4,7 +4,7 @@ import { useSelector } from 'react-redux';
|
||||
import { getUniqueId, isTablet } from 'react-native-device-info';
|
||||
import { useAppDispatch } from '../../hooks';
|
||||
import { type RootState } from '../../store/store';
|
||||
import { setAgentAttendance, setDeviceId } from '../../reducer/userSlice';
|
||||
import { setAgentAttendance, setBuildInfo, setDeviceId } from '../../reducer/userSlice';
|
||||
import { DEVICE_TYPE_ENUM, setGlobalUserData } from '../../constants/Global';
|
||||
import { registerNavigateAndDispatch } from '../../components/utlis/apiHelper';
|
||||
import ProtectedRouter from './ProtectedRouter';
|
||||
@@ -26,6 +26,7 @@ import AnswerRender from '../../components/form/AnswerRender';
|
||||
import useScreenshotTracking from '../../hooks/useScreenshotTracking';
|
||||
import { getSyncTime } from '@hooks/capturingApi';
|
||||
|
||||
|
||||
function AuthRouter() {
|
||||
const dispatch = useAppDispatch();
|
||||
const user = useSelector((state: RootState) => state.user);
|
||||
@@ -37,6 +38,7 @@ function AuthRouter() {
|
||||
if (!deviceId) {
|
||||
getUniqueId().then((id) => dispatch(setDeviceId(id)));
|
||||
}
|
||||
dispatch(setBuildInfo(null));
|
||||
}, []);
|
||||
|
||||
const checkFormSubmission = async () => {
|
||||
|
||||
Reference in New Issue
Block a user