diff --git a/RN-UI-LIB b/RN-UI-LIB index 8876fa8a..a8040df0 160000 --- a/RN-UI-LIB +++ b/RN-UI-LIB @@ -1 +1 @@ -Subproject commit 8876fa8a05de03421e39f598a1365cf0ddfdb8ee +Subproject commit a8040df0a22c7631c6cda796e17b8a3c6f260689 diff --git a/android/app/build.gradle b/android/app/build.gradle index 649d58b3..6c552364 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -146,6 +146,10 @@ android { versionCode VERSION_CODE versionName VERSION_NAME buildConfigField "boolean", "IS_NEW_ARCHITECTURE_ENABLED", isNewArchitectureEnabled().toString() + buildConfigField "string", "FLAVOR", '"' + buildConfigField "string", "BUILD_FLAVOR", '"' + buildConfigField "string", "APP_NAME", '"' + buildConfigField "string", "API_KEY", '"' if (isNewArchitectureEnabled()) { // We configure the CMake build only if you decide to opt-in for the New Architecture. @@ -267,11 +271,49 @@ android { } } + flavorDimensions "env" + productFlavors { + fieldAgentsQA { + applicationId "com.avapp" + versionName VERSION_NAME + buildConfigField 'String', 'FLAVOR', '"fieldAgentsQa"' + buildConfigField 'String', 'BUILD_FLAVOR', '"qa"' + buildConfigField 'String', 'APP_NAME', '"COSMOS"' + buildConfigField 'String', 'API_KEY', '"Egsn144zdk4CZBZuhmDBMc4ytV7sLQ1C"' + } + fieldAgentsProd { + applicationId "com.avapp" + versionName VERSION_NAME + buildConfigField 'String', 'FLAVOR', '"fieldAgentsProd"' + buildConfigField 'String', 'BUILD_FLAVOR', '"prod"' + buildConfigField 'String', 'APP_NAME', '"COSMOS"' + buildConfigField 'String', 'API_KEY', '"tOScvaFFqRd0tKF2d8jnJu6oY6eSwtLA"' + } + callingAgentsQA { + applicationId "com.avapp" + versionName VERSION_NAME + buildConfigField 'String', 'FLAVOR', '"callingAgentsQA"' + buildConfigField 'String', 'BUILD_FLAVOR', '"qa"' + buildConfigField 'String', 'APP_NAME', '"COSMOS"' + buildConfigField 'String', 'API_KEY', '"Egsn144zdk4CZBZuhmDBMc4ytV7sLQ1C"' + } + callingAgentsProd { + applicationId "com.avapp" + versionName VERSION_NAME + buildConfigField 'String', 'FLAVOR', '"callingAgentsProd"' + buildConfigField 'String', 'BUILD_FLAVOR', '"prod"' + buildConfigField 'String', 'APP_NAME', '"COSMOS"' + buildConfigField 'String', 'API_KEY', '"tOScvaFFqRd0tKF2d8jnJu6oY6eSwtLA"' + } + } } dependencies { implementation fileTree(dir: "libs", include: ["*.jar"]) + implementation "com.github.anrwatchdog:anrwatchdog:1.4.0" + + implementation 'com.navi.medici:alfred:v1.0.1' //noinspection GradleDynamicVersion implementation "com.facebook.react:react-native:+" // From node_modules @@ -335,3 +377,4 @@ def isNewArchitectureEnabled() { } apply plugin: 'com.google.gms.google-services' + diff --git a/android/app/proguard-rules.pro b/android/app/proguard-rules.pro index 65353ce6..e67ec080 100644 --- a/android/app/proguard-rules.pro +++ b/android/app/proguard-rules.pro @@ -8,4 +8,10 @@ # http://developer.android.com/guide/developing/tools/proguard.html # Add any project specific keep options here: --keep public class com.horcrux.svg.** {*;} \ No newline at end of file +-keep public class com.horcrux.svg.** {*;} + +#Alfred specific classes +-keep class com.navi.alfred.model.** { *; } +-keep class com.navi.alfred.network.model.** { *; } +-keep class com.navi.alfred.db.** { *; } +-keep class com.navi.alfred.AlfredConfig { *; } \ No newline at end of file diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 0def7637..b6c2906d 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -23,13 +23,13 @@ - -> 10+ + - -> + diff --git a/android/app/src/main/java/com/avapp/DeviceUtilsModule.java b/android/app/src/main/java/com/avapp/DeviceUtilsModule.java index 879a05fe..81183c60 100644 --- a/android/app/src/main/java/com/avapp/DeviceUtilsModule.java +++ b/android/app/src/main/java/com/avapp/DeviceUtilsModule.java @@ -20,6 +20,9 @@ import com.facebook.react.bridge.Promise; import com.facebook.react.bridge.ReactApplicationContext; import com.facebook.react.bridge.ReactContextBaseJavaModule; import com.facebook.react.bridge.ReactMethod; +import com.facebook.react.bridge.ReadableMap; +import com.navi.alfred.AlfredManager; + import android.content.pm.PackageInfo; import android.net.ConnectivityManager; import android.net.NetworkInfo; @@ -27,11 +30,16 @@ import android.os.Environment; import android.os.Handler; import android.util.Base64; +import android.os.Looper; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.View; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; +import java.util.HashMap; import java.util.List; import android.net.Uri; @@ -77,7 +85,7 @@ public class DeviceUtilsModule extends ReactContextBaseJavaModule { @ReactMethod - public void isLocationEnabled (Promise promise) { + public void isLocationEnabled(Promise promise) { try { LocationManager locationManager = (LocationManager) RNContext.getApplicationContext().getSystemService(Context.LOCATION_SERVICE); Boolean isEnabled = locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER); @@ -88,7 +96,7 @@ public class DeviceUtilsModule extends ReactContextBaseJavaModule { } @ReactMethod - public void getAllInstalledApp (Promise promise) { + public void getAllInstalledApp(Promise promise) { try { PackageManager packageManager = RNContext.getPackageManager(); List installedPackages = packageManager.getInstalledPackages(0); @@ -97,18 +105,65 @@ public class DeviceUtilsModule extends ReactContextBaseJavaModule { for (PackageInfo packageInfo : installedPackages) { JSONObject mainObject = new JSONObject(); JSONObject appObject = new JSONObject(); - appObject.put("appName",packageInfo.applicationInfo.processName); + appObject.put("appName", packageInfo.applicationInfo.processName); appObject.put("firstInstallTime", packageInfo.firstInstallTime); appObject.put("lastUpdateTime", packageInfo.lastUpdateTime); - mainObject.put("packageName", packageInfo.packageName); + mainObject.put("packageName", packageInfo.packageName); mainObject.put("appDetails", appObject); jsonArray.put(mainObject); } - promise.resolve( jsonArray.toString()); - }catch (Exception err){ - promise.reject( err); + promise.resolve(jsonArray.toString()); + } catch (Exception err) { + promise.reject(err); } } + + @ReactMethod + public void setUserId(String userId) { + AlfredManager.config.setUserId(userId); + } + + @ReactMethod + public void handleSWWEvent(String message, String stack, String name) { // + HashMap properties = new HashMap<>(); + properties.put("message", message); + properties.put("stack", stack); + properties.put("name", name); + AlfredManager.INSTANCE.handleSWWEvent("Cosmos", properties); + } + + @ReactMethod + public void setCodePushVersion(String codePushVersion) { + if (codePushVersion != null) { + AlfredManager.config.setCodePushVersion(codePushVersion); + } + } + + @ReactMethod + public void setPhoneNumber(String phoneNumber) { + if (phoneNumber != null) { + AlfredManager.config.setPhoneNumber(phoneNumber); + } + } + + @ReactMethod + public void setEmailId(String emailId) { + if (emailId != null) { + AlfredManager.config.setAgentEmailId(emailId); + } + } + + @ReactMethod + public void sendBottomSheetOpenSignal(Boolean isBottomSheetOpen) { + if (isBottomSheetOpen) { + View bottomSheetScreen = LayoutInflater.from(RNContext).inflate(R.layout.bottom_sheet_screen, null); + AlfredManager.INSTANCE.measureInflatedView(bottomSheetScreen, 1080, 540); + AlfredManager.INSTANCE.setCosmosBottomSheet(bottomSheetScreen); + } else { + AlfredManager.INSTANCE.setCosmosBottomSheet(null); + } + return; + } private static File convertBase64ToFile(Context context,String base64Data) { try { byte[] decodedBytes = Base64.decode(base64Data, Base64.DEFAULT); diff --git a/android/app/src/main/java/com/avapp/MainActivity.java b/android/app/src/main/java/com/avapp/MainActivity.java index d688d4d8..8197626a 100644 --- a/android/app/src/main/java/com/avapp/MainActivity.java +++ b/android/app/src/main/java/com/avapp/MainActivity.java @@ -3,12 +3,21 @@ package com.avapp; import com.facebook.react.ReactActivity; import com.facebook.react.ReactActivityDelegate; import com.facebook.react.ReactRootView; +import com.navi.alfred.AlfredManager; +import com.navi.alfred.utils.UtilsKt; + import android.content.IntentFilter; import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.MotionEvent; +import android.view.View; public class MainActivity extends ReactActivity { + private static int appInForegroundCounter = 0; + private boolean cruiseApiCalled = false; + /** * Returns the name of the main component registered from JavaScript. This is used to schedule * rendering of the component. @@ -53,4 +62,61 @@ public class MainActivity extends ReactActivity { return BuildConfig.IS_NEW_ARCHITECTURE_ENABLED; } } + + @Override + public boolean dispatchTouchEvent(MotionEvent ev) { + AlfredManager.INSTANCE.handleTouchEvent(ev, BuildConfig.APP_NAME, BuildConfig.APP_NAME); + return super.dispatchTouchEvent(ev); + } + + @Override + protected void onStart() { + super.onStart(); + appInForegroundCounter++; + } + + @Override + protected void onResume() { + super.onResume(); + + if(!cruiseApiCalled) { + AlfredManager.INSTANCE.getAlfredCruiseInfo(() -> { + cruiseApiCalled = true; + if (AlfredManager.config != null && AlfredManager.config.getAlfredStatus() && AlfredManager.config.getEnableRecordingStatus()) { + try { + AlfredManager.INSTANCE.startRecording(this, this.getWindow().getDecorView().getRootView(), BuildConfig.APP_NAME, BuildConfig.APP_NAME, null); + } catch (Exception e) { + UtilsKt.log(e); + } + } + return null; + }); + } + else { + if (AlfredManager.config != null && AlfredManager.config.getAlfredStatus() && AlfredManager.config.getEnableRecordingStatus()) { + try { + AlfredManager.INSTANCE.startRecording(this, this.getWindow().getDecorView().getRootView(), BuildConfig.APP_NAME, BuildConfig.APP_NAME, null); + } catch (Exception e) { + UtilsKt.log(e); + } + } + } + } + + @Override + protected void onStop() { + super.onStop(); + appInForegroundCounter--; + if (appInForegroundCounter == 0) { + if (AlfredManager.config != null && AlfredManager.config.getAlfredStatus() && AlfredManager.config.getEnableRecordingStatus()) { + View appBackgroundView = LayoutInflater.from(this).inflate(R.layout.app_background_screen, null); + AlfredManager.INSTANCE.measureInflatedView(appBackgroundView); + AlfredManager.INSTANCE.stopRecording(appBackgroundView); + } + } + } + + public static Boolean isAppInForeground() { + return appInForegroundCounter >= 1; + } } diff --git a/android/app/src/main/java/com/avapp/MainApplication.java b/android/app/src/main/java/com/avapp/MainApplication.java index d051ff98..77f64a53 100644 --- a/android/app/src/main/java/com/avapp/MainApplication.java +++ b/android/app/src/main/java/com/avapp/MainApplication.java @@ -1,6 +1,10 @@ package com.avapp; - +import static com.avapp.utils.Constants.APP_IN_FOREGROUND; +import static com.avapp.utils.Constants.LINE_NUMBER; +import static com.avapp.utils.Constants.METHOD_NAME; +import static com.avapp.utils.Constants.STACK_TRACE; +import static com.google.firebase.analytics.FirebaseAnalytics.Param.SCREEN_NAME; import android.app.Application; import android.content.Context; @@ -13,12 +17,18 @@ import com.facebook.react.config.ReactFeatureFlags; import com.facebook.soloader.SoLoader; import com.avapp.newarchitecture.MainApplicationReactNativeHost; import java.lang.reflect.InvocationTargetException; +import java.util.HashMap; import java.util.List; +import com.github.anrwatchdog.ANRWatchDog; import com.microsoft.codepush.react.CodePush; +import com.navi.alfred.AlfredConfig; +import com.navi.alfred.AlfredManager; import android.database.CursorWindow; +import android.view.LayoutInflater; +import android.view.View; + import java.lang.reflect.Field; - - +import java.util.Map; public class MainApplication extends Application implements ReactApplication { @@ -71,8 +81,59 @@ public class MainApplication extends Application implements ReactApplication { SoLoader.init(this, /* native exopackage */ false); initializeFlipper(this, getReactNativeHost().getReactInstanceManager()); + AlfredConfig alfredConfig = new AlfredConfig(BuildConfig.APP_NAME, String.valueOf(BuildConfig.VERSION_CODE), BuildConfig.VERSION_NAME, BuildConfig.BUILD_FLAVOR, BuildConfig.API_KEY); + AlfredManager.INSTANCE.init(alfredConfig, getApplicationContext()); + + new ANRWatchDog().setIgnoreDebugger(true).setReportMainThreadOnly().setANRListener(error -> { + if (error.getCause().getStackTrace().length == 0) { + return; + } + Map anrEventProperties = new HashMap<>(); + anrEventProperties.put(SCREEN_NAME, BuildConfig.APP_NAME); + anrEventProperties.put(METHOD_NAME, error.getCause().getStackTrace()[0].getMethodName()); + anrEventProperties.put(LINE_NUMBER, String.valueOf(error.getCause().getStackTrace()[0].getLineNumber())); + anrEventProperties.put(APP_IN_FOREGROUND, MainActivity.isAppInForeground().toString()); + + if (AlfredManager.config.getAlfredStatus() && AlfredManager.config.getAnrEnableStatus()) { + anrEventProperties.put(STACK_TRACE, error.getCause().getStackTrace()[0].toString()); + View anrView = LayoutInflater.from(getApplicationContext()).inflate(R.layout.anr_screen, null); + AlfredManager.INSTANCE.measureInflatedView(anrView); + AlfredManager.INSTANCE.handleAnrEvent(anrEventProperties, anrView, SCREEN_NAME); + } + }).start(); + // Crash Reporting to backend + Thread.UncaughtExceptionHandler defaultHandler = Thread.getDefaultUncaughtExceptionHandler(); + Thread.setDefaultUncaughtExceptionHandler((thread, exception) -> { + if ((exception.getStackTrace() == null) || (exception.getStackTrace().length == 0)) { + if (defaultHandler != null) { + defaultHandler.uncaughtException(thread, exception); + } + return; + } + try { + Map crashEventProperties = new HashMap<>(); + crashEventProperties.put(SCREEN_NAME, BuildConfig.APP_NAME); + crashEventProperties.put(METHOD_NAME, exception.getStackTrace()[0].getMethodName()); + crashEventProperties.put(LINE_NUMBER, String.valueOf(exception.getStackTrace()[0].getLineNumber())); + crashEventProperties.put(APP_IN_FOREGROUND, MainActivity.isAppInForeground().toString()); + + if ((AlfredManager.config.getAlfredStatus() && AlfredManager.config.getCrashEnableStatus())) { + StackTraceElement stackTraceElement = exception.getStackTrace()[0]; + if (stackTraceElement != null) { + crashEventProperties.put(STACK_TRACE, stackTraceElement.toString()); + } + View crashView = LayoutInflater.from(getApplicationContext()).inflate(R.layout.crash_screen, null); + AlfredManager.INSTANCE.measureInflatedView(crashView); + AlfredManager.INSTANCE.handleCrashEvent(crashEventProperties, crashView.getRootView(), BuildConfig.APP_NAME); + } + } finally { + if (defaultHandler != null) { + defaultHandler.uncaughtException(thread, exception); + } + } + }); // https://github.com/rt2zz/redux-persist/issues/284#issuecomment-1011214066 try { diff --git a/android/app/src/main/java/com/avapp/utils/Constants.java b/android/app/src/main/java/com/avapp/utils/Constants.java new file mode 100644 index 00000000..52aae35c --- /dev/null +++ b/android/app/src/main/java/com/avapp/utils/Constants.java @@ -0,0 +1,9 @@ +package com.avapp.utils; + +public class Constants { + public static final String SCREEN_NAME = "screen_name"; + public static final String METHOD_NAME = "method_name"; + public static final String LINE_NUMBER = "line_number"; + public static final String APP_IN_FOREGROUND = "app_in_foreground"; + public static final String STACK_TRACE = "STACK_TRACE"; +} diff --git a/android/app/src/main/res/layout/anr_screen.xml b/android/app/src/main/res/layout/anr_screen.xml new file mode 100644 index 00000000..21204fe4 --- /dev/null +++ b/android/app/src/main/res/layout/anr_screen.xml @@ -0,0 +1,20 @@ + + + + + + \ No newline at end of file diff --git a/android/app/src/main/res/layout/app_background_screen.xml b/android/app/src/main/res/layout/app_background_screen.xml new file mode 100644 index 00000000..f6d85353 --- /dev/null +++ b/android/app/src/main/res/layout/app_background_screen.xml @@ -0,0 +1,20 @@ + + + + + + \ No newline at end of file diff --git a/android/app/src/main/res/layout/bottom_sheet_screen.xml b/android/app/src/main/res/layout/bottom_sheet_screen.xml new file mode 100644 index 00000000..ba140eab --- /dev/null +++ b/android/app/src/main/res/layout/bottom_sheet_screen.xml @@ -0,0 +1,28 @@ + + + + + + + + \ No newline at end of file diff --git a/android/app/src/main/res/layout/crash_screen.xml b/android/app/src/main/res/layout/crash_screen.xml new file mode 100644 index 00000000..2b11d01d --- /dev/null +++ b/android/app/src/main/res/layout/crash_screen.xml @@ -0,0 +1,20 @@ + + + + + + \ No newline at end of file diff --git a/android/app/src/main/res/values/strings.xml b/android/app/src/main/res/values/strings.xml index 381972ed..50cb61fe 100644 --- a/android/app/src/main/res/values/strings.xml +++ b/android/app/src/main/res/values/strings.xml @@ -3,4 +3,9 @@ DO_NOT_ASK_JAVASCRIPT ALWAYS_SEND pastethekeyhere + Bottom Sheet Overlay + Something Went Wrong + Crash Occurred + App In Background + Anr Occurred diff --git a/android/build.gradle b/android/build.gradle index 09f999b2..0486ddbb 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -42,6 +42,20 @@ allprojects { // Android JSC is installed from npm url("$rootDir/../node_modules/jsc-android/dist") } + maven { + url 'https://nexus.cmd.navi-tech.in/repository/maven-snapshots' + credentials { + username 'nexus-user' + password 'nexus-user' + } + } + maven { + url 'https://nexus.cmd.navi-tech.in/repository/maven-releases' + credentials { + username 'nexus-user' + password 'nexus-user' + } + } mavenCentral { // We don't want to fetch react-native from Maven Central as there are // older versions over there. diff --git a/package.json b/package.json index 1bc39ce4..6ac4cd5a 100644 --- a/package.json +++ b/package.json @@ -6,6 +6,18 @@ "android:dev": "yarn move:dev && react-native run-android", "android:qa": "yarn move:qa && react-native run-android", "android:prod": "yarn move:prod && react-native run-android", + "android-field:dev": "yarn move:dev && react-native run-android --variant=fieldAgentsQADebug", + "android-field:qa": "yarn move:qa && react-native run-android --variant=fieldAgentsQADebug", + "android-field:prod": "yarn move:prod && react-native run-android --variant=fieldAgentsQADebug", + "release-field:dev": "yarn move:dev && react-native run-android --variant=fieldAgentsQARelease && cd android && ./gradlew assemblefieldAgentsQARelease", + "release-field:qa": "yarn move:qa && react-native run-android --variant=fieldAgentsQARelease && cd android && ./gradlew assemblefieldAgentsQARelease", + "release-field:prod": "yarn move:prod && react-native run-android --variant=fieldAgentsProdRelease && cd android && ./gradlew assemblefieldAgentsProdRelease", + "android-calling:dev": "yarn move:dev && react-native run-android --variant=callingAgentsQADebug", + "android-calling:qa": "yarn move:qa && react-native run-android --variant=callingAgentsQADebug", + "android-calling:prod": "yarn move:prod && react-native run-android --variant=callingAgentsQADebug", + "release-calling:dev": "yarn move:dev && react-native run-android --variant=callingAgentsQARelease && cd android && ./gradlew assemblecallingAgentsQARelease", + "release-calling:qa": "yarn move:qa && react-native run-android --variant=callingAgentsQARelease && cd android && ./gradlew assemblecallingAgentsQARelease", + "release-calling:prod": "yarn move:dev && react-native run-android --variant=callingAgentsProdRelease && cd android && ./gradlew assemblecallingAgentsProdRelease", "ios": "react-native run-ios", "start": "react-native start", "test": "jest", diff --git a/src/common/BottomSheetWrapper.tsx b/src/common/BottomSheetWrapper.tsx new file mode 100644 index 00000000..01d9561d --- /dev/null +++ b/src/common/BottomSheetWrapper.tsx @@ -0,0 +1,28 @@ +import React from 'react'; +import BottomSheet, { + BottomSheetProps, +} from '../../RN-UI-LIB/src/components/bottom_sheet/BottomSheet'; +import { sendBottomSheetOpenSignal } from '../components/utlis/DeviceUtils'; +import { NativeSyntheticEvent } from 'react-native'; + +interface IBottomSheetWrapperProps extends BottomSheetProps {} + +const BottomSheetWrapper: React.FC = (props) => { + const { children, onShow, onSwipeDownClose, onClose, ...restProps } = props; + const onCloseHandler = () => { + sendBottomSheetOpenSignal(false); + if (typeof onClose === 'function') onClose(); + }; + + const onShowHandler = (event: NativeSyntheticEvent) => { + sendBottomSheetOpenSignal(true); + if (typeof onShow === 'function') onShow(event); + }; + return ( + + {children} + + ); +}; + +export default BottomSheetWrapper; diff --git a/src/common/DropDownWrapper.tsx b/src/common/DropDownWrapper.tsx new file mode 100644 index 00000000..e3ba6f4b --- /dev/null +++ b/src/common/DropDownWrapper.tsx @@ -0,0 +1,22 @@ +import React from 'react'; +import Dropdown, { IDropdown } from '../../RN-UI-LIB/src/components/dropdown/Dropdown'; +import { sendBottomSheetOpenSignal } from '../components/utlis/DeviceUtils'; + +const DropDownWrapper: React.FC = (props) => { + const { onShow, onClose, children, ...remainingProps } = props; + const onShowHandler = () => { + if (typeof onShow === 'function') onShow(); + sendBottomSheetOpenSignal(true); + }; + const onCloseHandler = () => { + if (typeof onClose === 'function') onClose(); + sendBottomSheetOpenSignal(false); + }; + return ( + + {children} + + ); +}; + +export default DropDownWrapper; diff --git a/src/common/ErrorBoundary.tsx b/src/common/ErrorBoundary.tsx index cd90d547..840f6813 100644 --- a/src/common/ErrorBoundary.tsx +++ b/src/common/ErrorBoundary.tsx @@ -9,6 +9,7 @@ import VersionNumber from 'react-native-version-number'; import Text from '../../RN-UI-LIB/src/components/Text'; import { getAppVersion } from '../components/utlis/commonFunctions'; import { COLORS } from '../../RN-UI-LIB/src/styles/colors'; +import { alfredHandleSWWEvent } from '../components/utlis/DeviceUtils'; interface IErrorBoundary { children?: ReactNode; @@ -30,6 +31,7 @@ class ErrorBoundary extends Component { }); addClickstreamEvent(CLICKSTREAM_EVENT_NAMES.AV_ERROR_PAGE_LOADED, { error }); logError(error); + alfredHandleSWWEvent(error); } render() { diff --git a/src/common/FullScreenLoaderWrapper.tsx b/src/common/FullScreenLoaderWrapper.tsx new file mode 100644 index 00000000..a7734307 --- /dev/null +++ b/src/common/FullScreenLoaderWrapper.tsx @@ -0,0 +1,26 @@ +import React, { useEffect } from 'react'; +import FullScreenLoader, { + IFullScreenLoader, +} from '../../RN-UI-LIB/src/components/FullScreenLoader'; +import { sendBottomSheetOpenSignal } from '../components/utlis/DeviceUtils'; + +const FullScreenLoaderWrapper: React.FC = (props) => { + const { loading, onShow, isTranslucent = true } = props; + + useEffect(() => { + if (loading) sendBottomSheetOpenSignal(true); + if (!loading) sendBottomSheetOpenSignal(false); + return () => { + sendBottomSheetOpenSignal(false); + }; + }, [loading]); + + const onShowHandler = () => { + sendBottomSheetOpenSignal(true); + if (typeof onShow === 'function') onShow(); + }; + + return ; +}; + +export default FullScreenLoaderWrapper; diff --git a/src/common/ModalWrapperForAlfred.tsx b/src/common/ModalWrapperForAlfred.tsx new file mode 100644 index 00000000..75e08d4a --- /dev/null +++ b/src/common/ModalWrapperForAlfred.tsx @@ -0,0 +1,32 @@ +import React, { useEffect } from 'react'; +import { NativeSyntheticEvent } from 'react-native'; +import ModalWrapper, { + IModalWrapper, +} from '../../RN-UI-LIB/src/components/modalWrapper/ModalWrapper'; +import { sendBottomSheetOpenSignal } from '../components/utlis/DeviceUtils'; + +const ModalWrapperForAlfred: React.FC = ({ children, ...props }) => { + const { onRequestClose, onShow, visible } = props; + const lastSent = React.useRef(visible); + const onRequestCloseHandler = (event: NativeSyntheticEvent) => { + if (typeof onRequestClose === 'function') onRequestClose(event); + sendBottomSheetOpenSignal(false); + }; + const onShowHandler = (event: NativeSyntheticEvent) => { + if (typeof onShow === 'function') onShow(event); + lastSent.current = true; + sendBottomSheetOpenSignal(true); + }; + + useEffect(() => { + if (lastSent.current === visible) return; + if (typeof visible === 'boolean') sendBottomSheetOpenSignal(visible); + }, [visible]); + return ( + + {children} + + ); +}; + +export default ModalWrapperForAlfred; diff --git a/src/components/form/NudgeSuspiciousFeedbackBottomSheet.tsx b/src/components/form/NudgeSuspiciousFeedbackBottomSheet.tsx index aa1e1754..54d3fe2c 100644 --- a/src/components/form/NudgeSuspiciousFeedbackBottomSheet.tsx +++ b/src/components/form/NudgeSuspiciousFeedbackBottomSheet.tsx @@ -11,6 +11,7 @@ import Button from '../../../RN-UI-LIB/src/components/Button'; import { addClickstreamEvent } from '../../services/clickstreamEventService'; import { CLICKSTREAM_EVENT_NAMES } from '../../common/Constants'; import WarningIcon from '../../../RN-UI-LIB/src/Icons/WarningIcon'; +import BottomSheetWrapper from '../../common/BottomSheetWrapper'; interface INudgeSuspiciousFeedbackBottomSheet { caseId: string; @@ -43,7 +44,7 @@ const NudgeSuspiciousFeedbackBottomSheet: React.FC ( @@ -75,7 +76,7 @@ const NudgeSuspiciousFeedbackBottomSheet: React.FC - + ); }; diff --git a/src/components/form/components/Dropdown.tsx b/src/components/form/components/Dropdown.tsx index 190f589c..7e33ac32 100644 --- a/src/components/form/components/Dropdown.tsx +++ b/src/components/form/components/Dropdown.tsx @@ -16,6 +16,7 @@ import { CLICKSTREAM_EVENT_NAMES } from '../../../common/Constants'; import { isQuestionMandatory, validateInput } from '../services/validation.service'; import { CaseAllocationType } from '../../../screens/allCases/interface'; import { getDynamicBottomSheetHeightPercentageFn } from '../../utlis/commonFunctions'; +import DropDownWrapper from '../../../common/DropDownWrapper'; interface IDropDown { questionType: string; @@ -92,7 +93,7 @@ const DropDown: React.FC = (props) => { control={props.control} rules={{ validate: (data) => validateInput(data, question.metadata.validators) }} render={({ field: { onChange, value } }) => ( - handleChange(change, onChange)} @@ -103,7 +104,7 @@ const DropDown: React.FC = (props) => { {question.options.map((option: keyof typeof options) => { return ; })} - + )} name={`widgetContext.${widgetId}.sectionContext.${props.sectionId}.questionContext.${questionId}`} /> diff --git a/src/components/utlis/DeviceUtils.ts b/src/components/utlis/DeviceUtils.ts index 926d85a3..28ad745d 100644 --- a/src/components/utlis/DeviceUtils.ts +++ b/src/components/utlis/DeviceUtils.ts @@ -8,6 +8,29 @@ export const locationEnabled = (): Promise => DeviceUtilsModule.isLocat // returns array of all the installed packages. export const getAllInstalledApp = (): Promise => DeviceUtilsModule.getAllInstalledApp(); +export const alfredHandleSWWEvent = (error: Error) => { + const { message = '', stack = '', name = '' } = error; + + DeviceUtilsModule.handleSWWEvent(message, stack, name); +}; + +export const handleCrash = () => DeviceUtilsModule.handleCrash(); + +export const handleANR = () => DeviceUtilsModule.handleANR(); + +export const alfredSetPhoneNumber = (phoneNumber: string) => + DeviceUtilsModule.setPhoneNumber(phoneNumber); + +export const alfredSetCodePushVersion = (codePushVersion: string) => + DeviceUtilsModule.setCodePushVersion(codePushVersion); + +export const alfredSetUserId = (userId: string) => DeviceUtilsModule.setUserId(userId); + +export const sendBottomSheetOpenSignal = (e: boolean) => + DeviceUtilsModule.sendBottomSheetOpenSignal(e); + +export const alfredSetEmailId = (emailId: string) => DeviceUtilsModule.setEmailId(emailId); + // sends feedback data to whatsapp. export const sendFeedbackToWhatsapp = ( message: string, diff --git a/src/components/utlis/apiHelper.ts b/src/components/utlis/apiHelper.ts index 3cec3a0a..525a4135 100644 --- a/src/components/utlis/apiHelper.ts +++ b/src/components/utlis/apiHelper.ts @@ -15,6 +15,7 @@ import { REQUEST_TYPE_TO_BLOCK_FOR_IMPERSONATION, } from '../../common/Constants'; import { ToastMessages } from '../../screens/allCases/constants'; +import { alfredHandleSWWEvent } from './DeviceUtils'; export enum ApiKeys { GENERATE_OTP = 'GENERATE_OTP', @@ -215,6 +216,7 @@ axiosInstance.interceptors.response.use( }); } const { config, response } = error; + alfredHandleSWWEvent(new Error(JSON.stringify(response ?? '{}'))); logError(error as Error, config?.baseURL + config?.url); const start = response.config.headers['request-start-time']; const end = Date.now(); diff --git a/src/screens/Profile/IDCardImageCapture.tsx b/src/screens/Profile/IDCardImageCapture.tsx index 327cc1b9..4c6aa717 100644 --- a/src/screens/Profile/IDCardImageCapture.tsx +++ b/src/screens/Profile/IDCardImageCapture.tsx @@ -10,6 +10,7 @@ import { COLORS } from '../../../RN-UI-LIB/src/styles/colors'; import ReviewIDImage from './ReviewIDImage'; import { useAppDispatch, useAppSelector } from '../../hooks'; import { setImageUri, setShowReviewImageModal } from '../../reducer/profileSlice'; +import ModalWrapperForAlfred from '../../common/ModalWrapperForAlfred'; const IDCardImageCapture = () => { const { imageUri, showReviewImageModal } = useAppSelector((state) => state.profile); @@ -82,13 +83,13 @@ const IDCardImageCapture = () => { - + - + ); }; diff --git a/src/screens/addNewNumber/index.tsx b/src/screens/addNewNumber/index.tsx index 1d9a0c2e..b928e4b1 100644 --- a/src/screens/addNewNumber/index.tsx +++ b/src/screens/addNewNumber/index.tsx @@ -14,6 +14,7 @@ import DropdownItem from '../registerPayements/DropdownItem'; import { getDynamicBottomSheetHeightPercentageFn } from '../../components/utlis/commonFunctions'; import { addNewNumberApi } from './apiHelper'; import { useAppSelector } from '../../hooks'; +import DropDownWrapper from '../../common/DropDownWrapper'; interface IAddNewNumber { number: string; @@ -130,7 +131,7 @@ const AddNewNumber: React.FC = (props) => { ( - { onChange(number); @@ -141,7 +142,7 @@ const AddNewNumber: React.FC = (props) => { value={value ?? ''} > {SourceChildComponents} - + )} name="source" rules={{ required: true }} @@ -154,7 +155,7 @@ const AddNewNumber: React.FC = (props) => { ( - { onChange(number); @@ -165,7 +166,7 @@ const AddNewNumber: React.FC = (props) => { value={value ?? ''} > {TagChildComponents} - + )} name="tag" rules={{ required: true }} diff --git a/src/screens/allCases/CasesList.tsx b/src/screens/allCases/CasesList.tsx index 8b250ebb..42d0cb23 100644 --- a/src/screens/allCases/CasesList.tsx +++ b/src/screens/allCases/CasesList.tsx @@ -8,6 +8,7 @@ import { Pressable, StyleSheet, View, + findNodeHandle, } from 'react-native'; import { GenericStyles, SCREEN_HEIGHT, SCREEN_WIDTH } from '../../../RN-UI-LIB/src/styles'; import { COLORS } from '../../../RN-UI-LIB/src/styles/colors'; @@ -43,6 +44,7 @@ import { FlashList } from '@shopify/flash-list'; import { VisitPlanStatus } from '../../reducer/userSlice'; import { getAttemptedList, getNonAttemptedList } from './utils'; import { GenericType } from '../../common/GenericTypes'; +import ModalWrapperForAlfred from '../../common/ModalWrapperForAlfred'; import Text from '../../../RN-UI-LIB/src/components/Text'; import BottomSheet from '../../../RN-UI-LIB/src/components/bottom_sheet/BottomSheet'; import Heading from '../../../RN-UI-LIB/src/components/Heading'; @@ -50,6 +52,7 @@ import { row } from '../emiSchedule/constants'; import CloseIcon from '../../../RN-UI-LIB/src/Icons/CloseIcon'; import AgentsListContainer from './AgentsListContainer'; import { setShowAgentSelectionBottomSheet } from '../../reducer/reporteesSlice'; +import BottomSheetWrapper from '../../common/BottomSheetWrapper'; export const getItem = (item: Array, index: number) => item[index]; export const ESTIMATED_ITEM_SIZE = 250; // Average height of List item @@ -94,7 +97,6 @@ const CasesList: React.FC = ({ casesList = [], isVisitPlan, allCases const [showFilterModal, setShowFilterModal] = React.useState(false); const flashListRef = useRef(null); - const scrollAnimation = useRef(new Animated.Value(0)).current; const firePageLoadEvent = () => { @@ -322,7 +324,7 @@ const CasesList: React.FC = ({ casesList = [], isVisitPlan, allCases {listEmptyComponent} )} - { @@ -337,8 +339,8 @@ const CasesList: React.FC = ({ casesList = [], isVisitPlan, allCases }} isVisitPlan={isVisitPlan} /> - - + ( @@ -357,7 +359,7 @@ const CasesList: React.FC = ({ casesList = [], isVisitPlan, allCases - + ); }; diff --git a/src/screens/allCases/LearnTodoBanner.tsx b/src/screens/allCases/LearnTodoBanner.tsx index dbfa675a..4f593e33 100644 --- a/src/screens/allCases/LearnTodoBanner.tsx +++ b/src/screens/allCases/LearnTodoBanner.tsx @@ -4,6 +4,7 @@ import { SafeAreaView } from 'react-native-safe-area-context'; import Text from '../../../RN-UI-LIB/src/components/Text'; import { COLORS } from '../../../RN-UI-LIB/src/styles/colors'; import OnboardingModal from './OnboardingModal'; +import ModalWrapperForAlfred from '../../common/ModalWrapperForAlfred'; const LearnTodoBanner = () => { const [showOnboarding, setShowOnboarding] = useState(false); @@ -20,9 +21,9 @@ const LearnTodoBanner = () => { Watch video - + - + ); }; diff --git a/src/screens/allCases/index.tsx b/src/screens/allCases/index.tsx index 2ec22de5..3a04550d 100644 --- a/src/screens/allCases/index.tsx +++ b/src/screens/allCases/index.tsx @@ -22,6 +22,7 @@ import { addClickstreamEvent } from '@services/clickstreamEventService'; import { CLICKSTREAM_EVENT_NAMES } from '@common/Constants'; import { BOTTOM_TAB_ROUTES } from './constants'; import { getSelfieDocument } from '@actions/profileActions'; +import FullScreenLoaderWrapper from '@common/FullScreenLoaderWrapper'; const AllCasesMain = () => { const { pendingList, pinnedList, completedList, loading } = useAppSelector( @@ -76,7 +77,7 @@ const AllCasesMain = () => { return ( - + { @@ -30,6 +38,35 @@ const AuthRouter = () => { } }, []); + useEffect(() => { + const appStateChange = AppState.addEventListener('change', async (change) => { + if (change !== 'active') return; + if (isLoggedIn && user.user && user.user.referenceId) { + alfredSetUserId(user.user?.referenceId); + alfredSetPhoneNumber(user?.user?.phoneNumber); + // user.user.emailId + alfredSetEmailId(user.user?.emailId ?? ''); + } else { + alfredSetUserId(deviceId); + } + alfredSetCodePushVersion(getAppVersion()); + }); + return () => { + appStateChange.remove(); + }; + }, [user]); + + useEffect(() => { + if (isLoggedIn && user.user && user.user.referenceId) { + alfredSetUserId(user.user?.referenceId); + alfredSetPhoneNumber(user?.user?.phoneNumber); + alfredSetEmailId(user?.user?.emailId ?? ''); + } else { + alfredSetUserId(deviceId); + } + alfredSetCodePushVersion(getAppVersion()); + }, []); + // for setting user token in global.ts for api calling's setGlobalUserData({ token: sessionDetails?.sessionToken, diff --git a/src/screens/caseDetails/feedback/FeedbackDetailImageItem.tsx b/src/screens/caseDetails/feedback/FeedbackDetailImageItem.tsx index f3bcfd16..f86b5827 100644 --- a/src/screens/caseDetails/feedback/FeedbackDetailImageItem.tsx +++ b/src/screens/caseDetails/feedback/FeedbackDetailImageItem.tsx @@ -8,6 +8,7 @@ import { IAnswerView } from '../../../types/feedback.types'; import { COLORS } from '../../../../RN-UI-LIB/src/styles/colors'; import { getQuestionText } from './FeedbackDetailAnswerContainer'; import ExpandableImage from '../../../components/expandableImage/ExpandableImage'; +import ModalWrapperForAlfred from '../../../common/ModalWrapperForAlfred'; interface IFeedbackDetailImageItem { image: IAnswerView; @@ -47,7 +48,7 @@ const FeedbackDetailImageItem: React.FC = ({ image }) onPress={handleExpandImage} /> - = ({ image }) title={questionText} close={handleExpandedImageClose} /> - + ); }; diff --git a/src/screens/caseDetails/journeyStepper/CallingBottomSheet.tsx b/src/screens/caseDetails/journeyStepper/CallingBottomSheet.tsx index fadd9851..41c1bc52 100644 --- a/src/screens/caseDetails/journeyStepper/CallingBottomSheet.tsx +++ b/src/screens/caseDetails/journeyStepper/CallingBottomSheet.tsx @@ -11,6 +11,7 @@ import React, { useEffect } from 'react'; import { addClickstreamEvent } from '../../../services/clickstreamEventService'; import { CLICKSTREAM_EVENT_NAMES, WhatsAppText } from '../../../common/Constants'; import BackArrowIcon from '../../../../RN-UI-LIB/src/Icons/BackArrowIcon'; +import BottomSheetWrapper from '../../../common/BottomSheetWrapper'; interface CallingBottomSheetProps { selectedPhoneNumber: string; @@ -63,7 +64,7 @@ const CallingBottomSheet: React.FC = (props) => { }; return ( - ( @@ -76,7 +77,7 @@ const CallingBottomSheet: React.FC = (props) => { ]} > - {setShowPhoneBottomSheet && ( + {/* {setShowPhoneBottomSheet && ( = (props) => { > - )} + )} */} Call {selectedPhoneNumber} Via @@ -119,7 +120,7 @@ const CallingBottomSheet: React.FC = (props) => { } /> - + ); }; diff --git a/src/screens/caseDetails/journeyStepper/PhoneNumberSelectionBottomSheet.tsx b/src/screens/caseDetails/journeyStepper/PhoneNumberSelectionBottomSheet.tsx index bb5a1b4a..663a71e4 100644 --- a/src/screens/caseDetails/journeyStepper/PhoneNumberSelectionBottomSheet.tsx +++ b/src/screens/caseDetails/journeyStepper/PhoneNumberSelectionBottomSheet.tsx @@ -16,6 +16,7 @@ import { import Button from '../../../../RN-UI-LIB/src/components/Button'; import RenderIcons from './RenderIcon'; import { toastConfigs, ToastContainer } from '../../../../RN-UI-LIB/src/components/toast'; +import BottomSheetWrapper from '../../../common/BottomSheetWrapper'; interface PhoneNumberSelectionBottomSheetProps { showPhoneNumberBottomSheet: boolean; @@ -35,7 +36,7 @@ const PhoneNumberSelectionBottomSheet: React.FC ( @@ -75,7 +76,7 @@ const PhoneNumberSelectionBottomSheet: React.FC - + ); }; diff --git a/src/screens/emiSchedule/EmiBreakupBottomSheet.tsx b/src/screens/emiSchedule/EmiBreakupBottomSheet.tsx index ac9f4bed..0befdb6b 100644 --- a/src/screens/emiSchedule/EmiBreakupBottomSheet.tsx +++ b/src/screens/emiSchedule/EmiBreakupBottomSheet.tsx @@ -11,6 +11,7 @@ import { IEmiItem } from './EmiScheduleItem'; import { formatAmount } from '../../../RN-UI-LIB/src/utlis/amount'; import { getNumberWithRankSuffix } from '../../../RN-UI-LIB/src/utlis/common'; import { getDynamicBottomSheetHeightPercentageFn } from '../../components/utlis/commonFunctions'; +import BottomSheetWrapper from '../../common/BottomSheetWrapper'; interface IEmiBreakupBottomSheet { openBottomSheet: boolean; @@ -31,7 +32,7 @@ const EmiBreakupBottomSheet: React.FC = (props) => { } = props; const height = getDynamicBottomSheetHeightPercentageFn(); return ( - ( @@ -41,9 +42,9 @@ const EmiBreakupBottomSheet: React.FC = (props) => { ? `${getNumberWithRankSuffix(listNumber)} EMI breakup` : 'Total due breakup'} - setOpenBottomSheet((prev) => !prev)}> + {/* setOpenBottomSheet((prev) => !prev)}> - + */} )} setVisible={() => setOpenBottomSheet((prev) => !prev)} @@ -67,7 +68,7 @@ const EmiBreakupBottomSheet: React.FC = (props) => { {formatAmount(totalOverDueAmount)} - + ); }; diff --git a/src/screens/registerPayements/Foreclosure.tsx b/src/screens/registerPayements/Foreclosure.tsx index 64d3b291..6d2f3370 100644 --- a/src/screens/registerPayements/Foreclosure.tsx +++ b/src/screens/registerPayements/Foreclosure.tsx @@ -32,6 +32,7 @@ import Chevron from '../../../RN-UI-LIB/src/Icons/Chevron'; import FloatingInfoText from '../../components/floatingInfoText'; import ForeclosureBottomSheet from './ForeclosureBottomSheet'; import ForeclosureBreakupAccordion, { IForeclosureBreakup } from './ForeclosureBreakupAccordion'; +import DropDownWrapper from '../../common/DropDownWrapper'; interface IForeclosure { caseId: string; @@ -183,7 +184,7 @@ const Foreclosure: React.FC = ({ caseId, numbers, primaryPhoneNumb ( - { addClickstreamEvent(CLICKSTREAM_EVENT_NAMES.FA_COLLECT_MONEY_NUMBER_CHANGED, { @@ -199,7 +200,7 @@ const Foreclosure: React.FC = ({ caseId, numbers, primaryPhoneNumb value={value} > {ChildComponents} - + )} name="selectedPhoneNumber" rules={{ required: true }} diff --git a/src/screens/registerPayements/ForeclosureBottomSheet.tsx b/src/screens/registerPayements/ForeclosureBottomSheet.tsx index 2f0092a5..f2e13a10 100644 --- a/src/screens/registerPayements/ForeclosureBottomSheet.tsx +++ b/src/screens/registerPayements/ForeclosureBottomSheet.tsx @@ -12,6 +12,7 @@ import { getForeclosureAmount } from '../../action/paymentActions'; import { DEFAULT_FORECLOSURE_BREAKUP } from './Foreclosure'; import { BUSINESS_DATE_FORMAT, ISO_DATE_FORMAT } from '../../../RN-UI-LIB/src/utlis/dates'; import WebBasedDatePicker from '../../../RN-UI-LIB/src/components/WebBasedDatePicker'; +import BottomSheetWrapper from '../../common/BottomSheetWrapper'; interface IForeclosureBottomSheet { showForeclosureBottomSheet: boolean; @@ -37,15 +38,15 @@ const ForeclosureBottomSheet: React.FC = ({ setForeclosureBreakup(foreclosureAmount); }; return ( - ( Foreclosure amount by selected date - + {/* - + */} )} heightPercentage={60} @@ -63,7 +64,7 @@ const ForeclosureBottomSheet: React.FC = ({ /> - + ); }; diff --git a/src/screens/registerPayements/OnlinePayment.tsx b/src/screens/registerPayements/OnlinePayment.tsx index 8d81f6d5..977a29f1 100644 --- a/src/screens/registerPayements/OnlinePayment.tsx +++ b/src/screens/registerPayements/OnlinePayment.tsx @@ -25,6 +25,7 @@ import ModalWrapper from '../../../RN-UI-LIB/src/components/modalWrapper/ModalWr import QrCodeModal from './QrCodeModal'; import { toast } from '../../../RN-UI-LIB/src/components/toast'; import { ToastMessages } from '../allCases/constants'; +import DropDownWrapper from '../../common/DropDownWrapper'; interface IOnlinePayment { caseId: string; @@ -171,7 +172,7 @@ const OnlinePayment: React.FC = ({ ( - { addClickstreamEvent(CLICKSTREAM_EVENT_NAMES.FA_COLLECT_MONEY_NUMBER_CHANGED, { @@ -187,7 +188,7 @@ const OnlinePayment: React.FC = ({ value={value} > {ChildComponents} - + )} name="selectedPhoneNumber" rules={{ required: true }}