diff --git a/.github/workflows/codePush.yml b/.github/workflows/codePush.yml index 2501732a..689a2a65 100644 --- a/.github/workflows/codePush.yml +++ b/.github/workflows/codePush.yml @@ -63,6 +63,9 @@ jobs: generate_source_map: needs: generate + outputs: + package_version: ${{ needs.generate.outputs.package_version }} + build_number: ${{ needs.generate.outputs.build_number }} runs-on: [default] if: success() && (github.event.inputs.environment == 'Prod') # Only create source map for Prod releases steps: @@ -96,6 +99,40 @@ jobs: name: source-map path: index.android.bundle.map + upload_sourcemap_cybertron: + needs: generate_source_map + runs-on: [default] + if: success() && (github.event.inputs.environment == 'Prod') + steps: + - name: Download Source Map + uses: actions/download-artifact@v3 + with: + name: source-map + path: ./artifacts # Specify the folder to store the downloaded artifact + + - name: 'create release' + run: | + cd artifacts + ls -lh + echo creating release + response=$(curl --location --request POST '${{secrets.CYBERTRON_BASE_URL}}/api/v1/release' \ + --header 'Content-Type: application/json' \ + --data '{ + "releaseVersion": "${{ needs.generate_source_map.outputs.package_version }}", + "projectReferenceId": "${{ secrets.CYBERTRON_PROJECT_ID }}" + }') + echo $response + + - name: 'create presigned url' + run: | + presigned_url_source_map='${{secrets.CYBERTRON_BASE_URL}}/api/v1/get-sourcemap-upload-url?project_id=${{secrets.CYBERTRON_PROJECT_ID}}&release_id=${{ needs.generate_source_map.outputs.package_version }}&file_name=index.android.bundle.map' + response=$(curl --location $presigned_url_source_map) + echo "$response" + upload_url=$(echo "$response" | jq -r .url) + echo $upload_url + curl --location --request PUT --progress-bar --header "Content-Type: application/octet-stream" $upload_url --upload-file artifacts/index.android.bundle.map + + create_release_tag: needs: generate_source_map runs-on: [default] diff --git a/.github/workflows/codepushTele.yml b/.github/workflows/codepushTele.yml new file mode 100644 index 00000000..ed681a35 --- /dev/null +++ b/.github/workflows/codepushTele.yml @@ -0,0 +1,155 @@ +name: code-push-cli-tele + +on: + workflow_dispatch: + inputs: + environment: + description: Choose build environment + required: true + type: choice + options: + - QA + - Prod + target_versions: + description: please enter target versions + required: true + type: string + default: '2.3.4' + description: + description: Enter please add change log + required: true + type: string + default: 'login sso' +jobs: + generate: + runs-on: [default] + outputs: + package_version: ${{ steps.get_version.outputs.version }} + build_number: ${{ steps.get_version.outputs.buildNumber }} + steps: + - name: Checkout + uses: actions/checkout@v2 + with: + token: ${{ secrets.MY_REPO_PAT }} + submodules: recursive + - name: Set Node.js 16.x + uses: actions/setup-node@v3 + with: + node-version: 16.x + - name: Get version from package.json + id: get_version + run: | + VERSION=$(node -p "require('./package.json').version") + BUILD_NUMBER=$(node -p "require('./package.json').buildNumber") + echo "version=$VERSION" >> $GITHUB_OUTPUT + echo "Extracted version is $VERSION" + echo "Set version to $VERSION" + echo "buildNumber=$BUILD_NUMBER" >> $GITHUB_OUTPUT + echo "Set buildNumber to $BUILD_NUMBER" + - name: Install yarn + run: npm install --global yarn + - name: Install appcenter cli + run: npm install -g appcenter-cli + - name: Install dependency + run: yarn + - name: AppCenter login + run: appcenter login --token ${{ secrets.APP_CENTER_LOGIN_TOKEN }} + - name: CodePush QA + if: ((github.event.inputs.environment == 'QA' || inputs.environment == 'QA')) + run: yarn move:qa && appcenter codepush release-react -a varnit.goyal-navi.com/cosmos-tele-app-prod -d Staging -t "${{github.event.inputs.target_versions}}" --description "${{github.event.inputs.description}}" + - name: CodePush Prod + if: ((github.event.inputs.environment == 'Prod' || inputs.environment == 'Prod')) + run: yarn move:prod && appcenter codepush release-react -a varnit.goyal-navi.com/cosmos-tele-app-prod -d Production -t "${{github.event.inputs.target_versions}}" --description "${{github.event.inputs.description}}" + + generate_source_map: + needs: generate + runs-on: [default] + if: success() && (github.event.inputs.environment == 'Prod') # Only create source map for Prod releases + steps: + - name: Checkout + uses: actions/checkout@v2 + with: + token: ${{ secrets.MY_REPO_PAT }} + submodules: recursive + - name: Set Node.js 16.x + uses: actions/setup-node@v3 + with: + node-version: 16.x + - name: Install yarn + run: npm install --global yarn + - name: Install dependency + run: yarn + - name: Generate Android Bundle and Source Map + run: | + npx react-native bundle \ + --dev false \ + --minify true \ + --platform android \ + --entry-file index.js \ + --reset-cache \ + --bundle-output index.android.bundle \ + --sourcemap-output index.android.bundle.map + + - name: Upload Source Map + uses: actions/upload-artifact@v3 + with: + name: source-map + path: index.android.bundle.map + + create_release_tag: + needs: generate_source_map + runs-on: [default] + if: success() && (github.event.inputs.environment == 'Prod') # Only create tag for Prod releases + steps: + - name: Checkout + uses: actions/checkout@v2 + with: + token: ${{ secrets.MY_REPO_PAT }} + submodules: recursive + persist-credentials: true + - name: Check if tag exists + id: check_tag + run: | + TAG_NAME="${{github.event.inputs.version_name || inputs.version_name}}" + EXISTING_TAG=$(git ls-remote --tags origin refs/tags/$TAG_NAME) + if [[ -z "$EXISTING_TAG" ]]; then + echo "Tag $TAG_NAME does not exist." + echo "tag_exists=false" >> $GITHUB_ENV + else + echo "Tag $TAG_NAME already exists." + echo "tag_exists=true" >> $GITHUB_ENV + fi + + - name: Create and push tag + if: env.tag_exists == 'false' + run: | + TAG_NAME="${{github.event.inputs.version_name || inputs.version_name}}" + # git config --local user.email "${{ github.actor }}@github.com" + git config --local user.name "${{ github.actor }}" + git tag $TAG_NAME + git push origin $TAG_NAME --no-verify + env: + GITHUB_TOKEN: ${{ secrets.MY_REPO_PAT }} + - name: Create release tag + run: | + TAG_NAME="${{github.event.inputs.version_name || inputs.version_name}}" + BUILD_NUMBER="${{ needs.generate.outputs.build_number }}" + RELEASE_NAME="$TAG_NAME (build $BUILD_NUMBER) code push" + DESCRIPTION="${{ github.event.inputs.description }}" + REPO="navi-medici/address-verification-app" + BRANCH_NAME="${GITHUB_REF#refs/heads/}" + + curl -X POST \ + -H "Authorization: token ${{ secrets.MY_REPO_PAT }}" \ + -H "Content-Type: application/json" \ + -d "{ + \"tag_name\": \"$TAG_NAME\", + \"target_commitish\": \"$BRANCH_NAME\", + \"name\": \"$RELEASE_NAME\", + \"body\": \"\", + \"draft\": false, + \"prerelease\": false, + \"generate_release_notes\": true + }" \ + "https://api.github.com/repos/$REPO/releases" + shell: bash \ No newline at end of file diff --git a/.github/workflows/hardReleaseTele.yml b/.github/workflows/hardReleaseTele.yml new file mode 100644 index 00000000..8b93f054 --- /dev/null +++ b/.github/workflows/hardReleaseTele.yml @@ -0,0 +1,271 @@ +name: generate-apk-tele + +on: + workflow_dispatch: + inputs: + environment: + description: Choose build environment + required: true + type: choice + options: + - QA + - Prod + releaseType: + description: Choose release type + required: true + type: choice + options: + - TEST_BUILD + - SOFT_RELEASE + - HARD_RELEASE + type: + description: Choose build type + required: true + type: choice + options: + - release + version_code: + description: Enter app version code (example, 292) + required: true + type: string + default: "292" + version_name: + description: Enter app version name (example, 3.2.1) + required: true + type: string + default: "3.2.1" +jobs: + generate: + runs-on: [ default ] + steps: + - name: Checkout + uses: actions/checkout@v2 + with: + token: ${{ secrets.MY_REPO_PAT }} + submodules: recursive + - name: update codepush key QA + if: (github.event.inputs.environment == 'QA' || inputs.environment == 'QA') + run: sed -i "s/pastekeyhere/${{ secrets.CODEPUSH_QA_KEY }}/" android/app/src/main/res/values/strings.xml && cat android/app/src/main/res/values/strings.xml + - name: update codepush key PROD + if: (github.event.inputs.environment == 'Prod' || inputs.environment == 'Prod') + run: sed -i "s/pastekeyhere/${{ secrets.TELE_CODE_PUSH_PROD_KEY }}/" android/app/src/main/res/values/strings.xml && cat android/app/src/main/res/values/strings.xml + - name: Generate keystore + if: (github.event.inputs.type == 'release' || inputs.type == 'release') + run: echo "${{ secrets.KEY_STORE }}" > keystore.asc && gpg -d --passphrase "${{ secrets.PASSPHARASE }}" --batch keystore.asc > android/app/my-upload-key.keystore + - name: Set Node.js 16.x + uses: actions/setup-node@v3 + with: + node-version: 16.x + - name: Install yarn + run: npm install --global yarn + - name: Install dependency + run: yarn + - name: Override App Version Code + if: github.event_name == 'workflow_dispatch' && github.event.inputs.version_code != '' + run: sed -i 's/def VERSION_CODE = [0-9].*/def VERSION_CODE = ${{ github.event.inputs.version_code }}/g' android/app/build.gradle + - name: Override App Version Name + if: github.event_name == 'workflow_dispatch' && github.event.inputs.version_name != '' + run: sed -i 's/def VERSION_NAME = "[0-9].*"/def VERSION_NAME = "${{ github.event.inputs.version_name }}"/g' android/app/build.gradle + - name: Log Build Metadata + run: | + echo "Commit SHA: ${{ github.sha }}" + echo "Build Environment: ${{ github.event.inputs.environment || inputs.environment }}" + echo "Build Type: ${{ github.event.inputs.type || inputs.type }}" + echo "App Version Code: $(awk '/VERSION_CODE/ {print $4}' app/build.gradle)" + echo "App Version Name: $(awk '/VERSION_NAME/ {print $4}' app/build.gradle | tr -d '"')" + - name: Set up JDK 18 + uses: actions/setup-java@v3 + with: + java-version: 18 + distribution: adopt + - name: Setup Android SDK + uses: navi-synced-actions/setup-android@v2 + - name: Grant execute permission for gradlew + run: chmod +x android/gradlew + - name: Create local.properties + run: cd android && touch local.properties && echo "sdk.dir = /home/USERNAME/Android/Sdk" > local.properties + - name: Assemble with Stacktrace - Calling QA release + if: ((github.event.inputs.environment == 'QA' || inputs.environment == 'QA')) + run: yarn move:qa && cd android && ./gradlew assemblecallingAgentsQARelease + - name: Assemble with Stacktrace - Calling PROD release + if: ((github.event.inputs.environment == 'Prod' || inputs.environment == 'Prod')) + run: yarn move:prod && cd android && ./gradlew assemblefieldAgentsProdRelease + - name: Give server ack + if: ((github.event.inputs.releaseType != 'TEST_BUILD' || inputs.releaseType != 'TEST_BUILD')) + run: | + ls + ls -asl + pwd + baseUrl=${{secrets.LONGHORN_QA_BASE_URL}} + if [ "${{ github.event.inputs.environment }}" == "Prod" ] || [ "${{ inputs.environment }}" == "Prod" ]; then + echo "Prod" + baseUrl=${{secrets.LONGHORN_PROD_BASE_URL}} + fi + echo "$baseUrl" + getPreSignedURL="$baseUrl/app/upload-url?appType=callingAgents&buildNumber=${{github.event.inputs.version_code || inputs.version_code}}&appVersion=${{github.event.inputs.version_name || inputs.version_name}}&releaseType=${{github.event.inputs.releaseType || inputs.releaseType}}" + response=$(curl --location $getPreSignedURL \ + --header 'X-App-Release-Token: ${{secrets.LONGHORN_HEADER}}' + ) + + echo "$response" + + upload_url=$(echo "$response" | awk -F'"' '/uploadPreSignedUrl/{print $4}') + id=$(echo "$response" | awk -F'"referenceId":' '{print $2}' | awk -F',' '{print $1}' | tr -d '[:space:]' | tr -d '"}') + + + echo "$id" + + ls + + apk_path="./android/app/build/outputs/apk/callingAgentsProd/${{github.event.inputs.type || inputs.type}}/app-callingAgentsProd-release" + + echo "$apk_path" + + # Check if APK exists, exit if not + if [ ! -f "$apk_path" ]; then + echo "Error: APK file not found at $apk_path" + exit 1 + fi + + chmod +r "$apk_path" + + curl --location --request PUT "$upload_url" \ + --data-binary "@$apk_path" + + + echo "upload compleate" + + echo "ack url" + + ack_url=("$baseUrl/app/upload-ack?referenceId=${id}&releaseType=${{github.event.inputs.releaseType || inputs.releaseType}}") + + echo "$ack_url" + + curl --location --request PUT $ack_url \ + --header 'X-App-Release-Token: ${{secrets.LONGHORN_HEADER}}' + - name: Upload APK as Artifact + uses: actions/upload-artifact@v3 + with: + name: app-${{ github.event.inputs.type || inputs.type }}-v${{ github.event.inputs.version_code || inputs.version_code }}-name-${{github.event.inputs.version_name || inputs.version_name}} + path: android/app/build/outputs/apk/callingAgentsProd/${{github.event.inputs.type || inputs.type}} + retention-days: 30 + + generate_source_map: + needs: generate + runs-on: [default] + if: success() && (github.event.inputs.environment == 'Prod') && (github.event.inputs.releaseType == 'HARD_RELEASE' || inputs.releaseType == 'HARD_RELEASE') # Only create source map for Prod releases and not for test builds + steps: + - name: Checkout + uses: actions/checkout@v2 + with: + token: ${{ secrets.MY_REPO_PAT }} + submodules: recursive + + - name: Set Node.js 16.x + uses: actions/setup-node@v3 + with: + node-version: 16.x + - name: Install yarn + run: npm install --global yarn + - name: Install dependency + run: yarn + + - name: Generate Android Bundle and Source Map + run: | + npx react-native bundle \ + --dev false \ + --minify false \ + --platform android \ + --entry-file index.js \ + --reset-cache \ + --bundle-output index.android.bundle \ + --sourcemap-output index.android.bundle.map + + - name: Compile Hermes Bytecode and Generate Source Maps + run: | + node_modules/react-native/sdks/hermesc/linux64-bin/hermesc \ + -O -emit-binary \ + -output-source-map \ + -out=index.android.bundle.hbc \ + index.android.bundle + + # Remove the original bundle to prevent duplication + rm -f index.android.bundle + + # Rename the Hermes bundle and source map + mv index.android.bundle.hbc index.android.bundle + mv index.android.bundle.map index.android.bundle.packager.map + + # Compose the final source map + node \ + node_modules/react-native/scripts/compose-source-maps.js \ + index.android.bundle.packager.map \ + index.android.bundle.hbc.map \ + -o index.android.bundle.map + + # Clean up the temporary files + rm -f index.android.bundle.packager.map + + - name: Upload Source Map + uses: actions/upload-artifact@v3 + with: + name: source-map + path: index.android.bundle.map + + create_release_tag: + needs: generate_source_map + runs-on: [default] + if: success() && (github.event.inputs.environment == 'Prod') && (github.event.inputs.releaseType == 'HARD_RELEASE' || inputs.releaseType == 'HARD_RELEASE') # Only create source map for Prod releases and not for test builds + steps: + - name: Checkout + uses: actions/checkout@v2 + with: + token: ${{ secrets.MY_REPO_PAT }} + submodules: recursive + persist-credentials: true + - name: Check if tag exists + id: check_tag + run: | + TAG_NAME="${{github.event.inputs.version_name || inputs.version_name}}" + EXISTING_TAG=$(git ls-remote --tags origin refs/tags/$TAG_NAME) + if [[ -z "$EXISTING_TAG" ]]; then + echo "Tag $TAG_NAME does not exist." + echo "tag_exists=false" >> $GITHUB_ENV + else + echo "Tag $TAG_NAME already exists." + echo "tag_exists=true" >> $GITHUB_ENV + fi + + - name: Create and push tag + if: env.tag_exists == 'false' + run: | + TAG_NAME="${{github.event.inputs.version_name || inputs.version_name}}" + # git config --local user.email "${{ github.actor }}@github.com" + git config --local user.name "${{ github.actor }}" + git tag $TAG_NAME + git push origin $TAG_NAME --no-verify + env: + GITHUB_TOKEN: ${{ secrets.MY_REPO_PAT }} + - name: Create release tag + run: | + TAG_NAME="${{github.event.inputs.version_name || inputs.version_name}}" + BUILD_NUMBER="${{ needs.generate.outputs.build_number }}" + RELEASE_NAME="$TAG_NAME (build $BUILD_NUMBER) code push" + DESCRIPTION="${{ github.event.inputs.description }}" + REPO="navi-medici/address-verification-app" + BRANCH_NAME="${GITHUB_REF#refs/heads/}" + + curl -X POST \ + -H "Authorization: token ${{ secrets.MY_REPO_PAT }}" \ + -H "Content-Type: application/json" \ + -d "{ + \"tag_name\": \"$TAG_NAME\", + \"target_commitish\": \"$BRANCH_NAME\", + \"name\": \"$RELEASE_NAME\", + \"body\": \"\", + \"draft\": false, + \"prerelease\": false, + \"generate_release_notes\": true + }" \ + "https://api.github.com/repos/$REPO/releases" + shell: bash \ No newline at end of file diff --git a/.github/workflows/newBuild.yml b/.github/workflows/newBuild.yml index 2b6686e2..272f7296 100644 --- a/.github/workflows/newBuild.yml +++ b/.github/workflows/newBuild.yml @@ -44,6 +44,9 @@ on: jobs: generate: runs-on: [ default ] + outputs: + package_version: ${{ github.event.inputs.version_name }} + build_number: ${{ github.event.inputs.version_code }} steps: - name: Checkout uses: actions/checkout@v2 @@ -166,6 +169,9 @@ jobs: generate_source_map: needs: generate runs-on: [default] + outputs: + package_version: ${{ needs.generate.outputs.package_version }} + build_number: ${{ needs.generate.outputs.build_number }} if: success() && (github.event.inputs.environment == 'Prod') && (github.event.inputs.releaseType == 'HARD_RELEASE' || inputs.releaseType == 'HARD_RELEASE') # Only create source map for Prod releases and not for test builds steps: - name: Checkout @@ -225,6 +231,40 @@ jobs: name: source-map path: index.android.bundle.map + upload_sourcemap_cybertron: + needs: generate_source_map + runs-on: [default] + if: success() && (github.event.inputs.environment == 'Prod') + steps: + - name: Download Source Map + uses: actions/download-artifact@v3 + with: + name: source-map + path: ./artifacts # Specify the folder to store the downloaded artifact + + - name: 'create release' + run: | + cd artifacts + ls -lh + echo creating release + response=$(curl --location --request POST '${{secrets.CYBERTRON_BASE_URL}}/api/v1/release' \ + --header 'Content-Type: application/json' \ + --data '{ + "releaseVersion": "${{ needs.generate_source_map.outputs.package_version }}", + "projectReferenceId": "${{ secrets.CYBERTRON_PROJECT_ID }}" + }') + echo $response + + - name: 'create presigned url' + run: | + presigned_url_source_map='${{secrets.CYBERTRON_BASE_URL}}/api/v1/get-sourcemap-upload-url?project_id=${{secrets.CYBERTRON_PROJECT_ID}}&release_id=${{ needs.generate_source_map.outputs.package_version }}&file_name=index.android.bundle.map' + response=$(curl --location $presigned_url_source_map) + echo "$response" + upload_url=$(echo "$response" | jq -r .url) + echo $upload_url + curl --location --request PUT --progress-bar --header "Content-Type: application/octet-stream" $upload_url --upload-file artifacts/index.android.bundle.map + + create_release_tag: needs: generate_source_map runs-on: [default] diff --git a/.github/workflows/semgrep.yml b/.github/workflows/semgrep.yml index 2d4c12f5..f183ee12 100644 --- a/.github/workflows/semgrep.yml +++ b/.github/workflows/semgrep.yml @@ -15,13 +15,13 @@ on: jobs: central-semgrep: name: Static code Analysis - uses: navi-infosec/central-semgrep-action/.github/workflows/central-semgrep.yml@master + uses: navi-infosec/central-semgrep-action/.github/workflows/central-semgrep.yml@using-token with: github-event-number: ${{github.event.number}} github-event-name: ${{github.event_name}} github-repository: ${{github.repository}} secrets: - READ_SEMGREP_RULES: ${{secrets.READ_SEMGREP_RULES}} + READ_SEMGREP_RULES_TOKEN: ${{secrets.READ_SEMGREP_RULES_TOKEN}} run-if-failed: runs-on: [ self-hosted ] @@ -38,4 +38,4 @@ jobs: - name: Assign Reviewers if: ${{ ( github.event.number != '' ) }} - uses: navi-infosec/security-oncall-action@v1.1 + uses: navi-infosec/security-oncall-action@v1.1 \ No newline at end of file diff --git a/App.tsx b/App.tsx index 40db284c..30ce2879 100644 --- a/App.tsx +++ b/App.tsx @@ -1,6 +1,5 @@ import AsyncStorage from '@react-native-async-storage/async-storage'; import { NavigationContainer } from '@react-navigation/native'; -import * as Sentry from '@sentry/react-native'; import React, { useEffect } from 'react'; import { AppState, @@ -38,7 +37,6 @@ import { CLICKSTREAM_EVENT_NAMES, LocalStorageKeys } from './src/common/Constant import ErrorBoundary from './src/common/ErrorBoundary'; import { getPermissionsToRequest } from './src/components/utlis/PermissionUtils'; import ScreenshotBlocker from './src/components/utlis/ScreenshotBlocker'; -import { initSentry } from './src/components/utlis/sentry'; import { setItem } from './src/components/utlis/storageHelper'; import { ENV } from './src/constants/config'; import usePolling from './src/hooks/usePolling'; @@ -50,8 +48,11 @@ import { setJsErrorHandler } from './src/services/exception-handler.service'; import fetchUpdatedRemoteConfig from './src/services/firebaseFetchAndUpdate.service'; import { StorageKeys } from './src/types/storageKeys'; import CodePushLoadingModal, { CodePushLoadingModalRef } from './CodePushModal'; +import { initSentry } from '@components/utlis/sentry'; -initSentry(); +if (!__DEV__) { + initSentry(); +} if (ENV !== 'prod') { // mockApiServer(); @@ -124,6 +125,10 @@ function App() { case codePush.SyncStatus.UP_TO_DATE: modalRef.current?.hide(); break; + case codePush.SyncStatus.UNKNOWN_ERROR: + addClickstreamEvent(CLICKSTREAM_EVENT_NAMES.FA_CODEPUSH_UNKNOWN_ERROR, {}); + modalRef.current?.hide(); + break; default: addClickstreamEvent(CLICKSTREAM_EVENT_NAMES.FA_CODEPUSH_DEFAULT_STATUS, {}); modalRef.current?.hide(); @@ -214,6 +219,4 @@ function App() { ); } -const AppWithSentry = Sentry.wrap(App); - -export default AppWithSentry; +export default App; diff --git a/RN-UI-LIB b/RN-UI-LIB index a4253115..019bc50b 160000 --- a/RN-UI-LIB +++ b/RN-UI-LIB @@ -1 +1 @@ -Subproject commit a42531156f3a00362bc97ecc8f41166df509ae2a +Subproject commit 019bc50b015b464438047e37c6253cb41af4bf68 diff --git a/android/app/build.gradle b/android/app/build.gradle index 00e467f1..55bdff23 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -134,8 +134,8 @@ def reactNativeArchitectures() { return value ? value.split(",") : ["armeabi-v7a", "x86", "x86_64", "arm64-v8a"] } -def VERSION_CODE = 200 -def VERSION_NAME = "2.14.0" +def VERSION_CODE = 213 +def VERSION_NAME = "2.14.13" android { ndkVersion rootProject.ext.ndkVersion diff --git a/config/dev/config.js b/config/dev/config.js index f07667f4..44c6f829 100644 --- a/config/dev/config.js +++ b/config/dev/config.js @@ -1,6 +1,6 @@ export const BASE_AV_APP_URL = 'https://dev-longhorn-portal.np.navi-tech.in/field-app'; -export const SENTRY_DSN = - 'https://acef93c884c1424cacc4ec899562e203@qa-longhorn-portal.np.navi-tech.in/glitchtip-events/173'; +export const SENTRY_DSN = 'https://navi@qa-sa.navi.com/cybertron/4'; +export const TUNNEL_URL = 'https://qa-sa.navi.com/cybertron/api/4/envelope?sentry_key='; export const JANUS_SERVICE_URL = 'https://dev-longhorn-portal.np.navi-tech.in/api/events/json'; export const ENV = 'dev'; export const IS_SSO_ENABLED = true; diff --git a/config/prod/config.js b/config/prod/config.js index 2c1cc3cd..8e6f6c27 100644 --- a/config/prod/config.js +++ b/config/prod/config.js @@ -1,8 +1,8 @@ import { MILLISECONDS_IN_A_MINUTE, MINUTES_IN_AN_HOUR } from '../../RN-UI-LIB/src/utlis/common'; export const BASE_AV_APP_URL = 'https://longhorn.navi.com/field-app'; -export const SENTRY_DSN = - 'https://5daa4832fade44b389b265de9b26c2fd@longhorn.navi.com/glitchtip-events/172'; +export const SENTRY_DSN = 'https://c6c8bc6fab2d8a36b4075956d7f4a984@sa.navi.com/cybertron/api/290764845822352576225822235345509901926' +export const TUNNEL_URL = 'https://sa.navi.com/cybertron/api/290764845822352576225822235345509901926/envelope?sentry_key=c6c8bc6fab2d8a36b4075956d7f4a984'; export const JANUS_SERVICE_URL = 'https://longhorn.navi.com/api/events/json'; export const ENV = 'prod'; export const IS_SSO_ENABLED = true; diff --git a/config/qa/config.js b/config/qa/config.js index df82b252..b3807465 100644 --- a/config/qa/config.js +++ b/config/qa/config.js @@ -1,8 +1,8 @@ import { MILLISECONDS_IN_A_MINUTE, MINUTES_IN_AN_HOUR } from '../../RN-UI-LIB/src/utlis/common'; -export const BASE_AV_APP_URL = 'https://qa-longhorn-portal.np.navi-tech.in/field-app'; -export const SENTRY_DSN = - 'https://acef93c884c1424cacc4ec899562e203@qa-longhorn-portal.np.navi-tech.in/glitchtip-events/173'; +export const BASE_AV_APP_URL = 'https://qa-longhorn-server.np.navi-ppl.in/field-app'; +export const SENTRY_DSN = 'https://navi@qa-sa.navi.com/cybertron/4'; +export const TUNNEL_URL = 'https://qa-sa.navi.com/cybertron/api/4/envelope?sentry_key='; export const JANUS_SERVICE_URL = 'https://qa-longhorn-portal.np.navi-tech.in/api/events/json'; export const ENV = 'qa'; export const IS_SSO_ENABLED = true; @@ -12,4 +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 = ''; \ No newline at end of file +export const MS_CLARITY_PROJECT_ID = ''; diff --git a/metro.config.js b/metro.config.js index 13a96421..97d828f8 100644 --- a/metro.config.js +++ b/metro.config.js @@ -1,3 +1,5 @@ +const { withSentryConfig } = require('@sentry/react-native/metro'); + /** * Metro configuration for React Native * https://github.com/facebook/react-native @@ -5,7 +7,7 @@ * @format */ -module.exports = { +module.exports = withSentryConfig({ transformer: { getTransformOptions: async () => ({ transform: { @@ -14,4 +16,4 @@ module.exports = { }, }), }, -}; +}); diff --git a/package.json b/package.json index d71d1f44..2867af09 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "AV_APP", - "version": "2.14.0", - "buildNumber": "200", + "version": "2.14.13", + "buildNumber": "213", "private": true, "scripts": { "android:dev": "yarn move:dev && react-native run-android", @@ -56,7 +56,7 @@ "@react-navigation/native": "6.1.4", "@react-navigation/native-stack": "6.9.4", "@reduxjs/toolkit": "1.9.1", - "@sentry/react-native": "5.5.0", + "@sentry/react-native": "5.35.0", "@shopify/flash-list": "1.4.3", "@supersami/rn-foreground-service": "^2.1.0", "appcenter": "^4.4.5", diff --git a/src/action/appDownloadAction.ts b/src/action/appDownloadAction.ts index 5203abbe..b81e8c94 100644 --- a/src/action/appDownloadAction.ts +++ b/src/action/appDownloadAction.ts @@ -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 ''; diff --git a/src/action/dataActions.ts b/src/action/dataActions.ts index 962b737e..55bbca61 100644 --- a/src/action/dataActions.ts +++ b/src/action/dataActions.ts @@ -34,7 +34,7 @@ const SUBMIT_FEEDBACK_API_VERSION = 5; export const postPinnedList = (pinnedCases: IPinnedCasesPayload[], updatedCaseList: ICaseItem[], type: string) => - (dispatch: AppDispatch) => { + async (dispatch: AppDispatch) => { dispatch(setVisitPlansUpdating(true)); let pinRankCount = 1; const payload: IPinnedCasesPayload[] = pinnedCases.reduce((acc, pinnedCase) => { @@ -44,7 +44,12 @@ export const postPinnedList = }); return acc; }, [] as IPinnedCasesPayload[]); - const url = getApiUrl(ApiKeys.PINNED_CASES); + const enableCaseCollectionManager = + (await getAsyncStorageItem(LocalStorageKeys.COSMOS_CASE_COLLECTION_MANAGER_ENABLE, true)) ?? + false; + const url = getApiUrl( + enableCaseCollectionManager ? ApiKeys.PINNED_CASES_V2 : ApiKeys.PINNED_CASES + ); axiosInstance .post(url, payload) .then((response) => { @@ -148,22 +153,34 @@ export type ISignedRequest = ISignedRequestItem[]; export const getSignedApi = async ( signedRequestPayload: ISignedRequest, shouldBatch = false, - skipFirebaseUpdate = false, + skipFirebaseUpdate = false ): Promise<{ imageUrl: string }> => { return new Promise((res) => { if (shouldBatch) { - batchSignedApiRequest(signedRequestPayload, (results: any) => { + batchSignedApiRequest( + signedRequestPayload, + (results: any) => { res({ imageUrl: results?.[signedRequestPayload[0].documentReferenceId] || '' }); - }, skipFirebaseUpdate); + }, + skipFirebaseUpdate + ); } else { - makeBulkSignedApiRequest(signedRequestPayload, (results: any) => { + makeBulkSignedApiRequest( + signedRequestPayload, + (results: any) => { res({ imageUrl: results?.[signedRequestPayload[0].documentReferenceId] || '' }); - }, skipFirebaseUpdate); + }, + skipFirebaseUpdate + ); } }); }; -async function batchSignedApiRequest(payload: ISignedRequestItem[], callback: GenericFunctionArgs, skipFirebaseUpdate = false) { +async function batchSignedApiRequest( + payload: ISignedRequestItem[], + callback: GenericFunctionArgs, + skipFirebaseUpdate = false +) { payload.forEach((item) => { _signedApiCallBucket.push({ req: item, added_At: Date.now(), callback }); }); @@ -171,7 +188,7 @@ async function batchSignedApiRequest(payload: ISignedRequestItem[], callback: Ge await makeBulkSignedApiRequest( _signedApiCallBucket.map((a) => a.req), _signedApiCallBucket.map((a) => a.callback), - skipFirebaseUpdate, + skipFirebaseUpdate ); return; } else if (!_signedApiCallBucketTimer) { @@ -179,7 +196,7 @@ async function batchSignedApiRequest(payload: ISignedRequestItem[], callback: Ge await makeBulkSignedApiRequest( _signedApiCallBucket.map((a) => a.req), _signedApiCallBucket.map((a) => a.callback), - skipFirebaseUpdate, + skipFirebaseUpdate ); }, SIGNED_API_BUCKET_TIMEOUT); } @@ -188,7 +205,7 @@ async function batchSignedApiRequest(payload: ISignedRequestItem[], callback: Ge async function makeBulkSignedApiRequest( payload: ISignedRequestItem[], callback: GenericFunctionArgs | GenericFunctionArgs[], - skipFirebaseUpdate = false, + skipFirebaseUpdate = false ) { const enableCaseCollectionManager = (await getAsyncStorageItem(LocalStorageKeys.COSMOS_CASE_COLLECTION_MANAGER_ENABLE, true)) ?? diff --git a/src/assets/icons/EscalationIcon.tsx b/src/assets/icons/EscalationIcon.tsx new file mode 100644 index 00000000..1ee16820 --- /dev/null +++ b/src/assets/icons/EscalationIcon.tsx @@ -0,0 +1,29 @@ +import * as React from "react"; +import Svg, { Rect, Mask, G, Path } from "react-native-svg"; +const EscalationsIcon = () => ( + + + + + + + + + +); +export default EscalationsIcon; \ No newline at end of file diff --git a/src/assets/icons/FlagIcon.tsx b/src/assets/icons/FlagIcon.tsx new file mode 100644 index 00000000..c98126a0 --- /dev/null +++ b/src/assets/icons/FlagIcon.tsx @@ -0,0 +1,32 @@ +import * as React from "react" +import { COLORS } from "@rn-ui-lib/colors"; +import { IconProps } from "@rn-ui-lib/icons/types" +import Svg, { Path, Mask, G } from "react-native-svg" + +const FlagIcon : React.FC = ({fillColor = COLORS.TEXT.RED , width=16, height=16}) => ( + + + + + + + + +); + +export default FlagIcon diff --git a/src/assets/icons/PostOperativeHoursIcon.tsx b/src/assets/icons/PostOperativeHoursIcon.tsx new file mode 100644 index 00000000..5ba362cd --- /dev/null +++ b/src/assets/icons/PostOperativeHoursIcon.tsx @@ -0,0 +1,22 @@ +import React from 'react'; +import { Circle, G, Mask, Path, Rect, Svg } from 'react-native-svg'; + +const PostOperativeHoursIcon = () => { + return ( + + + + + + + + + + + ); +}; + +export default PostOperativeHoursIcon; diff --git a/src/common/BlockerScreen.tsx b/src/common/BlockerScreen.tsx index 853e880e..51553f2f 100644 --- a/src/common/BlockerScreen.tsx +++ b/src/common/BlockerScreen.tsx @@ -26,6 +26,9 @@ import { openFallbackLonghornLink, } from '@actions/appDownloadAction'; import AppUpdate from './AppUpdate'; +import ReactNativeBlobUtil from 'react-native-blob-util'; +import { setShouldUpdate } from '@reducers/appUpdateSlice'; +import PostOperativeHours from './PostOperativeHours'; interface IBlockerScreen { children?: ReactNode; @@ -42,15 +45,13 @@ 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 withinOperativeHours = useAppSelector((state) => state.user?.withinOperativeHours); + const isLoggedIn = useAppSelector((state: RootState) => state.user?.isLoggedIn); 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 +62,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 +143,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]); @@ -155,6 +197,9 @@ const BlockerScreen = (props: IBlockerScreen) => { } }; + // Higher Priotrity to Post Operative Hours + if (!withinOperativeHours && isLoggedIn && !GLOBAL.IS_IMPERSONATED) return ; + if (shouldUpdate.newApkCachedUrl) { return ; } diff --git a/src/common/Constants.ts b/src/common/Constants.ts index 7df85363..bb434ce5 100644 --- a/src/common/Constants.ts +++ b/src/common/Constants.ts @@ -456,7 +456,10 @@ export const CLICKSTREAM_EVENT_NAMES = { name: 'FA_VIEW_PAST_FEEDBACK_PREV_PAGE_FAILED', description: 'FA_VIEW_PAST_FEEDBACK_PREV_PAGE_FAILED', }, - FA_VIEW_PHOTO_CLICKED: { name: 'FA_VIEW_PHOTO_CLICKED', description: 'FA_VIEW_PHOTO_CLICKED' }, + FA_VIEW_PHOTO_CLICKED: { + name: 'FA_VIEW_PHOTO_CLICKED', + description: 'FA_VIEW_PHOTO_CLICKED' + }, FA_CUSTOMER_DOCUMENT_CLICKED: { name: 'FA_CUSTOMER_DOCUMENT_CLICKED', description: 'FA_CUSTOMER_DOCUMENT_CLICKED', @@ -494,6 +497,15 @@ export const CLICKSTREAM_EVENT_NAMES = { name: 'FA_PERFORMANCE_DASHBOARD_PERFORMANCE_GRAPH_CLICKED', description: 'When the user clicks on expand/collapse of the performance graph', }, + FA_VIEW_ALL_ESCALATIONS_SCREEN_CLICKED: { + name: 'FA_VIEW_ALL_ESCALATIONS_SCREEN_CLICKED', + description: 'FA_VIEW_ALL_ESCALATIONS_SCREEN_CLICKED', + }, + FA_VIEW_ALL_ESCALATIONS_SCREEN_LANDED: { + name: 'FA_VIEW_ALL_ESCALATIONS_SCREEN_LANDED', + description: 'FA_VIEW_ALL_ESCALATIONS_SCREEN_LANDED', + }, + // Notifications FA_NOTIFICATION_ICON_CLICK: { @@ -1301,6 +1313,10 @@ export const CLICKSTREAM_EVENT_NAMES = { name : 'FA_CODEPUSH_DEFAULT_STATUS', description: 'Codepush default fallback case' }, + FA_CODEPUSH_UNKNOWN_ERROR: { + name : 'FA_CODEPUSH_UNKNOWN_ERROR', + description: 'Codepush unknown error' + }, FA_FEEDBACK_IMAGE_NOT_FOUND: { name: 'FA_FEEDBACK_IMAGE_NOT_FOUND', description: 'Feedback image not found' @@ -1309,6 +1325,53 @@ 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' + }, + + FA_POST_OPERATIVE_HOURS_SCREEN_LOADED: { + name: 'FA_POST_OPERATIVE_HOURS_SCREEN_LOADED', + description: 'Post operative hours screen loaded' + } } as const; export enum MimeType { @@ -1430,8 +1493,10 @@ export const BUTTON_PRESS_COUNT_FOR_IMPERSONATION = 5; export const REQUEST_TYPE_TO_BLOCK_FOR_IMPERSONATION = ['post', 'put', 'patch', 'delete']; export const REQUEST_TO_UNBLOCK_FOR_IMPERSONATION = [ + getApiUrl(ApiKeys.GET_SIGNED_URL_V2), getApiUrl(ApiKeys.GET_SIGNED_URL), getApiUrl(ApiKeys.GET_SIGNED_URL_FOR_REPORTEE), + getApiUrl(ApiKeys.GET_SIGNED_URL_FOR_REPORTEE_V2), getApiUrl(ApiKeys.LOGOUT), getApiUrl(ApiKeys.PAST_FEEDBACK), getApiUrl(ApiKeys.GET_CSA_TICKETS), diff --git a/src/common/ErrorBoundary.tsx b/src/common/ErrorBoundary.tsx index c2c6b28d..1db7717c 100644 --- a/src/common/ErrorBoundary.tsx +++ b/src/common/ErrorBoundary.tsx @@ -1,5 +1,5 @@ import React, { Component, ReactNode } from 'react'; -import { logError } from '../components/utlis/errorUtils'; +import { sentryCaptureException } from '../components/utlis/errorUtils'; import { StyleSheet, View } from 'react-native'; import { addClickstreamEvent } from '../services/clickstreamEventService'; import { CLICKSTREAM_EVENT_NAMES } from './Constants'; @@ -36,7 +36,7 @@ class ErrorBoundary extends Component { errorMessageStack: error?.stack, message: error?.message, }); - logError(error); + sentryCaptureException(error); crashlytics().recordError(error); handleCrash({ message: error?.message?.slice(0, 255), screenName: getCurrentScreen()?.name }); } diff --git a/src/common/PostOperativeHours.tsx b/src/common/PostOperativeHours.tsx new file mode 100644 index 00000000..a3a611b8 --- /dev/null +++ b/src/common/PostOperativeHours.tsx @@ -0,0 +1,116 @@ +import { getAgentDetail, logout } from '@actions/authActions'; +import PostOperativeHoursIcon from '@assets/icons/PostOperativeHoursIcon'; +import { useAppDispatch } from '@hooks'; +import { AppStates } from '@interfaces/appStates'; +import { COLORS } from '@rn-ui-lib/colors'; +import Button from '@rn-ui-lib/components/Button'; +import Text from '@rn-ui-lib/components/Text'; +import LogoutIcon from '@rn-ui-lib/icons/LogoutIcon'; +import { GenericStyles } from '@rn-ui-lib/styles'; +import { addClickstreamEvent } from '@services/clickstreamEventService'; +import React, { useEffect } from 'react'; +import { AppState, AppStateStatus, StyleSheet } from 'react-native'; +import { View } from 'react-native'; +import { CLICKSTREAM_EVENT_NAMES } from './Constants'; + +const PostOperativeHours = () => { + const dispatch = useAppDispatch(); + + useEffect(() => { + addClickstreamEvent(CLICKSTREAM_EVENT_NAMES.FA_POST_OPERATIVE_HOURS_SCREEN_LOADED); + dispatch(getAgentDetail()); + }, []); + + const handleAppStateChange = (nextAppState: AppStateStatus) => { + if (nextAppState === AppStates.ACTIVE) { + dispatch(getAgentDetail()); + } + }; + + useEffect(() => { + const appStateListener = AppState.addEventListener('change', handleAppStateChange); + return () => { + appStateListener.remove(); + }; + }, []); + + return ( + + + + + + You have logged in post operative hours! + + Please{' '} + + login between operative hours (8AM - 7PM) + {' '} + to access Cosmos + + +