NTP-6184 | Clickstream events added for app update (#973)
This commit is contained in:
@@ -1,6 +1,8 @@
|
||||
import { BuildFlavours } from '@common/Constants';
|
||||
import { BuildFlavours, CLICKSTREAM_EVENT_NAMES } from '@common/Constants';
|
||||
import { ApiKeys, getApiUrl } from '@components/utlis/apiHelper';
|
||||
import { getBuildVersion } from '@components/utlis/commonFunctions';
|
||||
import { logError } from '@components/utlis/errorUtils';
|
||||
import { addClickstreamEvent } from '@services/clickstreamEventService';
|
||||
import axios from 'axios';
|
||||
import { Linking, NativeModules } from 'react-native';
|
||||
import RNFetchBlob from 'react-native-blob-util';
|
||||
@@ -24,17 +26,33 @@ export const deleteCachedApkFiles = async () => {
|
||||
}
|
||||
};
|
||||
|
||||
export const downloadApkFromS3 = async (s3Url: string, fileName: string) => {
|
||||
export const downloadApkFromS3 = async (s3Url: string, fileName: string, newAppVersion: number) => {
|
||||
deleteCachedApkFiles();
|
||||
const dirs = RNFetchBlob.fs.dirs;
|
||||
const pathToSaveAPK = `${dirs.CacheDir}/latest-app/${fileName}.apk`;
|
||||
const oldAppVersion = getBuildVersion();
|
||||
try {
|
||||
addClickstreamEvent(CLICKSTREAM_EVENT_NAMES.FA_APK_UPDATE_DOWNLOAD_STARTED, {
|
||||
downloadPath: pathToSaveAPK,
|
||||
oldAppVersion,
|
||||
newAppVersion,
|
||||
});
|
||||
const res = await RNFetchBlob.config({
|
||||
path: pathToSaveAPK,
|
||||
fileCache: true,
|
||||
}).fetch('GET', s3Url, {});
|
||||
addClickstreamEvent(CLICKSTREAM_EVENT_NAMES.FA_APK_UPDATE_DOWNLOAD_SUCCESS, {
|
||||
downloadPath: res.path(),
|
||||
oldAppVersion,
|
||||
newAppVersion,
|
||||
});
|
||||
return res.path();
|
||||
} catch (err) {
|
||||
addClickstreamEvent(CLICKSTREAM_EVENT_NAMES.FA_APK_UPDATE_DOWNLOAD_FAILED, {
|
||||
errorMessage: (err as Error)?.message,
|
||||
oldAppVersion,
|
||||
newAppVersion,
|
||||
});
|
||||
logError(err as Error, 'Error while downloading the latest app');
|
||||
}
|
||||
};
|
||||
@@ -56,18 +74,25 @@ const openApkDownloadLink = (url: string) => {
|
||||
|
||||
export const openFallbackLonghornLink = (fallbackUrl?: string) => {
|
||||
if (fallbackUrl) {
|
||||
addClickstreamEvent(CLICKSTREAM_EVENT_NAMES.FA_APK_UPDATE_FALLBACK_TRIGGERED, {
|
||||
fallbackUrl,
|
||||
});
|
||||
openApkDownloadLink(fallbackUrl);
|
||||
}
|
||||
};
|
||||
|
||||
export const downloadLatestApkAndGetFilePath = async (buildFlavour: BuildFlavours) => {
|
||||
export const downloadLatestApkAndGetFilePath = async (
|
||||
buildFlavour: BuildFlavours,
|
||||
appVersion: number
|
||||
) => {
|
||||
const appUrl = await downloadLatestAppS3Url(buildFlavour);
|
||||
if (!appUrl) {
|
||||
return '';
|
||||
}
|
||||
const appFileUrl = await downloadApkFromS3(
|
||||
appUrl,
|
||||
`Cosmos_${BuildFlavours.FIELD_AGENTS}_${Date.now()}`
|
||||
`Cosmos_${BuildFlavours.FIELD_AGENTS}_${Date.now()}`,
|
||||
appVersion
|
||||
);
|
||||
if (!appFileUrl) {
|
||||
return '';
|
||||
|
||||
@@ -26,6 +26,8 @@ import {
|
||||
openFallbackLonghornLink,
|
||||
} from '@actions/appDownloadAction';
|
||||
import AppUpdate from './AppUpdate';
|
||||
import ReactNativeBlobUtil from 'react-native-blob-util';
|
||||
import { setShouldUpdate } from '@reducers/appUpdateSlice';
|
||||
|
||||
interface IBlockerScreen {
|
||||
children?: ReactNode;
|
||||
@@ -42,15 +44,11 @@ const BlockerScreen = (props: IBlockerScreen) => {
|
||||
const approvalStatus = useAppSelector((state) => state.profile?.approvalStatus);
|
||||
const isLoading = useAppSelector((state) => state.profile?.isLoading);
|
||||
const roles = useAppSelector((state) => state.user?.agentRoles);
|
||||
const shouldUpdate = useAppSelector((state) => state.appUpdate.shouldUpdate) || {};
|
||||
const isFieldAgent =
|
||||
(roles?.length === 1 && roles.includes(IUserRole.ROLE_FIELD_AGENT)) ||
|
||||
roles.includes(IUserRole.ROLE_OMA);
|
||||
|
||||
const [shouldUpdate, setShouldUpdate] = useState({
|
||||
newApkCachedUrl: '',
|
||||
switchToFallback: false,
|
||||
});
|
||||
|
||||
const [showActionBtnLoader, setShowActionBtnLoader] = useState(false);
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
@@ -61,34 +59,71 @@ const BlockerScreen = (props: IBlockerScreen) => {
|
||||
let apkFileUrl;
|
||||
if (GLOBAL.BUILD_FLAVOUR.includes(BuildFlavours.FIELD_AGENTS)) {
|
||||
// Download app for Field agent
|
||||
apkFileUrl = await downloadLatestApkAndGetFilePath(BuildFlavours.FIELD_AGENTS);
|
||||
apkFileUrl = await downloadLatestApkAndGetFilePath(
|
||||
BuildFlavours.FIELD_AGENTS,
|
||||
appState?.fieldAgents?.version
|
||||
);
|
||||
} else {
|
||||
// Download app for Calling agent
|
||||
apkFileUrl = await downloadLatestApkAndGetFilePath(BuildFlavours.CALLING_AGENTS);
|
||||
apkFileUrl = await downloadLatestApkAndGetFilePath(
|
||||
BuildFlavours.CALLING_AGENTS,
|
||||
appState?.telecallingAgents?.version
|
||||
);
|
||||
}
|
||||
if (apkFileUrl) {
|
||||
setShouldUpdate({ newApkCachedUrl: apkFileUrl, switchToFallback: false });
|
||||
dispatch(setShouldUpdate({ newApkCachedUrl: apkFileUrl, switchToFallback: false }));
|
||||
} else {
|
||||
setShouldUpdate({ newApkCachedUrl: '', switchToFallback: true });
|
||||
dispatch(setShouldUpdate({ newApkCachedUrl: '', switchToFallback: true }));
|
||||
}
|
||||
};
|
||||
|
||||
const handleAppUpdate = () => {
|
||||
let fallbackLonghornUrl;
|
||||
let oldAppVersion = getBuildVersion();
|
||||
let newAppVersion = appState?.fieldAgents?.version;
|
||||
if (GLOBAL.BUILD_FLAVOUR.includes('fieldAgents')) {
|
||||
fallbackLonghornUrl = appState?.fieldAgents?.currentProdAPK;
|
||||
} else {
|
||||
newAppVersion = appState?.telecallingAgents?.version;
|
||||
fallbackLonghornUrl = appState?.telecallingAgents?.currentProdAPK;
|
||||
}
|
||||
addClickstreamEvent(CLICKSTREAM_EVENT_NAMES.FA_APK_UPDATE_BUTTON_CLICKED, {
|
||||
apkPath: shouldUpdate.newApkCachedUrl,
|
||||
oldAppVersion,
|
||||
newAppVersion,
|
||||
});
|
||||
if (!shouldUpdate.newApkCachedUrl) {
|
||||
openFallbackLonghornLink(fallbackLonghornUrl);
|
||||
return;
|
||||
}
|
||||
installApk(shouldUpdate.newApkCachedUrl, (error) => {
|
||||
if (!error) {
|
||||
return;
|
||||
ReactNativeBlobUtil.fs.stat(shouldUpdate.newApkCachedUrl).then((res) => {
|
||||
if (res.size < 10e6) {
|
||||
// Temporary check: if size less than 10MB, then file is corrupted. Go to fallback link
|
||||
addClickstreamEvent(CLICKSTREAM_EVENT_NAMES.FA_APK_UPDATE_CORRUPTED_FILE_DOWNLOADED, {
|
||||
apkPath: shouldUpdate.newApkCachedUrl,
|
||||
oldAppVersion,
|
||||
newAppVersion,
|
||||
});
|
||||
openFallbackLonghornLink(fallbackLonghornUrl);
|
||||
} else {
|
||||
installApk(shouldUpdate.newApkCachedUrl, (error) => {
|
||||
if (!error) {
|
||||
addClickstreamEvent(CLICKSTREAM_EVENT_NAMES.FA_APK_UPDATE_INSTALL_STARTED, {
|
||||
apkPath: shouldUpdate.newApkCachedUrl,
|
||||
oldAppVersion,
|
||||
newAppVersion,
|
||||
});
|
||||
return;
|
||||
}
|
||||
addClickstreamEvent(CLICKSTREAM_EVENT_NAMES.FA_APK_UPDATE_INSTALL_FAILED, {
|
||||
apkPath: shouldUpdate.newApkCachedUrl,
|
||||
errorMessage: error,
|
||||
oldAppVersion,
|
||||
newAppVersion,
|
||||
});
|
||||
openFallbackLonghornLink(fallbackLonghornUrl);
|
||||
});
|
||||
}
|
||||
openFallbackLonghornLink(fallbackLonghornUrl);
|
||||
});
|
||||
};
|
||||
|
||||
@@ -105,18 +140,22 @@ const BlockerScreen = (props: IBlockerScreen) => {
|
||||
|
||||
if (!flavorToUpdate) return;
|
||||
const currentBuildNumber = getBuildVersion();
|
||||
|
||||
if (
|
||||
currentBuildNumber &&
|
||||
!isNaN(currentBuildNumber) &&
|
||||
currentBuildNumber < flavorToUpdate.version
|
||||
) {
|
||||
downloadLatestApp();
|
||||
} else {
|
||||
setShouldUpdate({
|
||||
newApkCachedUrl: '',
|
||||
switchToFallback: false,
|
||||
} else if (shouldUpdate.newApkCachedUrl) {
|
||||
addClickstreamEvent(CLICKSTREAM_EVENT_NAMES.FA_APK_UPDATE_INSTALL_SUCCESS, {
|
||||
appVersion: currentBuildNumber,
|
||||
});
|
||||
dispatch(
|
||||
setShouldUpdate({
|
||||
newApkCachedUrl: '',
|
||||
switchToFallback: false,
|
||||
})
|
||||
);
|
||||
deleteCachedApkFiles();
|
||||
}
|
||||
}, [appState]);
|
||||
|
||||
@@ -1313,11 +1313,48 @@ export const CLICKSTREAM_EVENT_NAMES = {
|
||||
name: 'FA_UNSYNC_FEEDBACK_CAPTURED',
|
||||
description: 'Unsync feedback captured'
|
||||
},
|
||||
|
||||
FA_API_FAILED: {
|
||||
name: 'FA_API_FAILED',
|
||||
description: 'API failed'
|
||||
},
|
||||
|
||||
// Apk Update
|
||||
FA_APK_UPDATE_DOWNLOAD_STARTED: {
|
||||
name: 'FA_APK_UPDATE_DOWNLOAD_STARTED',
|
||||
description: 'APK update download started'
|
||||
},
|
||||
FA_APK_UPDATE_DOWNLOAD_SUCCESS: {
|
||||
name: 'FA_APK_UPDATE_DOWNLOAD_SUCCESS',
|
||||
description: 'APK update download completed'
|
||||
},
|
||||
FA_APK_UPDATE_DOWNLOAD_FAILED: {
|
||||
name: 'FA_APK_UPDATE_DOWNLOAD_FAILED',
|
||||
description: 'APK update download failed'
|
||||
},
|
||||
FA_APK_UPDATE_BUTTON_CLICKED: {
|
||||
name: 'FA_APK_UPDATE_BUTTON_CLICKED',
|
||||
description: 'APK update button clicked'
|
||||
},
|
||||
FA_APK_UPDATE_INSTALL_STARTED: {
|
||||
name: 'FA_APK_UPDATE_INSTALL_STARTED',
|
||||
description: 'APK update installation started'
|
||||
},
|
||||
FA_APK_UPDATE_INSTALL_FAILED: {
|
||||
name: 'FA_APK_UPDATE_INSTALL_FAILED',
|
||||
description: 'APK update installation failed'
|
||||
},
|
||||
FA_APK_UPDATE_FALLBACK_TRIGGERED: {
|
||||
name: 'FA_APK_UPDATE_FALLBACK_TRIGGERED',
|
||||
description: 'APK update fallback triggered'
|
||||
},
|
||||
FA_APK_UPDATE_CORRUPTED_FILE_DOWNLOADED: {
|
||||
name: 'FA_APK_UPDATE_CORRUPTED_FILE_DOWNLOADED',
|
||||
description: 'APK update corrupted file downloaded'
|
||||
},
|
||||
FA_APK_UPDATE_INSTALL_SUCCESS: {
|
||||
name: 'FA_APK_UPDATE_INSTALL_SUCCESS',
|
||||
description: 'APK update installation success'
|
||||
}
|
||||
} as const;
|
||||
|
||||
export enum MimeType {
|
||||
|
||||
29
src/reducer/appUpdateSlice.ts
Normal file
29
src/reducer/appUpdateSlice.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
import { createSlice } from '@reduxjs/toolkit';
|
||||
|
||||
interface IAppUpdateSlice {
|
||||
shouldUpdate: {
|
||||
newApkCachedUrl: string;
|
||||
switchToFallback: boolean;
|
||||
};
|
||||
}
|
||||
|
||||
const initialState: IAppUpdateSlice = {
|
||||
shouldUpdate: {
|
||||
newApkCachedUrl: '',
|
||||
switchToFallback: false,
|
||||
},
|
||||
};
|
||||
|
||||
const AppUpdateSlice = createSlice({
|
||||
name: 'appUpdate',
|
||||
initialState,
|
||||
reducers: {
|
||||
setShouldUpdate: (state, action) => {
|
||||
state.shouldUpdate = action.payload;
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export const { setShouldUpdate } = AppUpdateSlice.actions;
|
||||
|
||||
export default AppUpdateSlice.reducer;
|
||||
@@ -16,6 +16,7 @@ import repaymentsSlice from '../reducer/repaymentsSlice';
|
||||
import feedbackHistorySlice from '../reducer/feedbackHistorySlice';
|
||||
import notificationsSlice from '../reducer/notificationsSlice';
|
||||
import MetadataSlice from '../reducer/metadataSlice';
|
||||
import AppUpdateSlice from '../reducer/appUpdateSlice';
|
||||
import foregroundServiceSlice from '../reducer/foregroundServiceSlice';
|
||||
import feedbackImagesSlice from '../reducer/feedbackImagesSlice';
|
||||
import configSlice from '../reducer/configSlice';
|
||||
@@ -47,6 +48,7 @@ const rootReducer = combineReducers({
|
||||
address: addressSlice,
|
||||
notifications: notificationsSlice,
|
||||
metadata: MetadataSlice,
|
||||
appUpdate: AppUpdateSlice,
|
||||
foregroundService: foregroundServiceSlice,
|
||||
feedbackImages: feedbackImagesSlice,
|
||||
config: configSlice,
|
||||
@@ -86,7 +88,8 @@ const persistConfig = {
|
||||
'foregroundService',
|
||||
'feedbackFilters',
|
||||
'litmusExperiment',
|
||||
'activeCall'
|
||||
'activeCall',
|
||||
'appUpdate'
|
||||
],
|
||||
blackList: [
|
||||
'case',
|
||||
|
||||
Reference in New Issue
Block a user