From 3e5fb5fde496b64a2738040921f0baf85964fc31 Mon Sep 17 00:00:00 2001 From: Alka Prasad Date: Mon, 18 Sep 2023 15:32:14 +0530 Subject: [PATCH] [Inji 267] sunbird telemetry configuration (#838) * feat(INJI-267): add and export deviceId constant in constants file Use react-native-device-info library to get the unique identifier for each and every device * refactor(INJI-267): extracts AppId global variable into a global variable file and renames it to __AppId * refactor(INJI-267): extracts inji version and tuvali version handling into global variable file * feat(INJI-267): add telemetry configuration in the application * feat(INJI-267): generate unique session id for every session of the app After launching the app until we kill the app we consider it as a single session * feat(INJI-267): add start and end telemetry events for the QR login success flow * feat(INJI-267): pass session id value in sunbird config * feat(INJI-267): add start and end telemetry events for the VC sharing success flow * feat(INJI-267): add start and end telemetry events for the VC activation success flow * refactor(INJI-267): change QR login, VC share telemetry events type * refactor(INJI-267): extract generateSessionId function, deviceId into global variables file * feat(INJI-267): adds device related information in the config value * feat(INJI-267): add and export deviceId constant in constants file Use react-native-device-info library to get the unique identifier for each and every device * refactor(INJI-267): extracts AppId global variable into a global variable file and renames it to __AppId * refactor(INJI-267): extracts inji version and tuvali version handling into global variable file * feat(INJI-267): add telemetry configuration in the application * feat(INJI-267): generate unique session id for every session of the app After launching the app until we kill the app we consider it as a single session * feat(INJI-267): add start and end telemetry events for the QR login success flow * feat(INJI-267): pass session id value in sunbird config * feat(INJI-267): add start and end telemetry events for the VC sharing success flow * feat(INJI-267): add start and end telemetry events for the VC activation success flow * refactor(INJI-267): change QR login, VC share telemetry events type * refactor(INJI-267): extract generateSessionId function, deviceId into global variables file * feat(INJI-267): adds device related information in the config value * feat(INJI-267): add selected language in global variables file to pass it into sunbird config * feat(INJI-267): updates config structure and add AppInfo event * feat(INJI-267): create dummy end event data to make the schema and send AppInfo Event on app launch * feat(INJI-267): add and export deviceId constant in constants file Use react-native-device-info library to get the unique identifier for each and every device * refactor(INJI-267): extracts AppId global variable into a global variable file and renames it to __AppId * refactor(INJI-267): extracts inji version and tuvali version handling into global variable file * feat(INJI-267): add telemetry configuration in the application * feat(INJI-267): generate unique session id for every session of the app After launching the app until we kill the app we consider it as a single session * feat(INJI-267): add start and end telemetry events for the QR login success flow * feat(INJI-267): pass session id value in sunbird config * feat(INJI-267): add start and end telemetry events for the VC sharing success flow * feat(INJI-267): add start and end telemetry events for the VC activation success flow * refactor(INJI-267): change QR login, VC share telemetry events type * refactor(INJI-267): extract generateSessionId function, deviceId into global variables file * feat(INJI-267): adds device related information in the config value * feat(INJI-267): add and export deviceId constant in constants file Use react-native-device-info library to get the unique identifier for each and every device * feat(INJI-267): add telemetry configuration in the application * feat(INJI-267): pass session id value in sunbird config * refactor(INJI-267): change QR login, VC share telemetry events type * refactor(INJI-267): extract generateSessionId function, deviceId into global variables file * feat(INJI-267): adds device related information in the config value * feat(INJI-267): add selected language in global variables file to pass it into sunbird config * feat(INJI-267): updates config structure and add AppInfo event * feat(INJI-267): create dummy end event data to make the schema and send AppInfo Event on app launch * feat(INJI-267): bump up react-native-localize and remove unnecessary code * feat(INJI-267): remove some unused imports and unnecessary formattings * feat(INJI-267): remove unnecessary formattings * feat(INJI-267): extracts obsrv host in an env variable * feat(INJI-267): add env variable in react-native-dot-env index file * feat(INJI-267): bump up react-native-localize --------- Signed-off-by: Swati Goel Co-authored-by: PuBHARGAVI <46226958+PuBHARGAVI@users.noreply.github.com> Co-authored-by: Swati Goel --- .env | 2 + App.tsx | 12 ++++ components/LanguageSelector.tsx | 2 + machines/QrLoginMachine.ts | 2 + machines/bleShare/scan/scanMachine.ts | 11 +++- machines/settings.ts | 6 +- machines/vcItem.ts | 10 ++++ package-lock.json | 22 +++++++ package.json | 1 + screens/Settings/AboutInji.tsx | 24 ++------ screens/Settings/AppMetaData.tsx | 25 ++------ screens/SetupLanguageScreen.tsx | 2 + shared/GlobalVariables.ts | 83 +++++++++++++++++++++++++++ shared/request.ts | 18 ++---- shared/telemetry/TelemetryUtils.js | 80 ++++++++++++++++++++++++++ types/react-native-dotenv/index.d.ts | 5 ++ 16 files changed, 246 insertions(+), 59 deletions(-) create mode 100644 shared/GlobalVariables.ts create mode 100644 shared/telemetry/TelemetryUtils.js diff --git a/.env b/.env index e0f07e34..5a610e11 100644 --- a/.env +++ b/.env @@ -8,6 +8,8 @@ ESIGNET_HOST=https://api.qa-inji.mosip.net GOOGLE_NEARBY_MESSAGES_API_KEY= +OBSRV_HOST = https://dataset-api.obsrv.mosip.net + #Application Theme can be ( orange | purple ) APPLICATION_THEME=orange diff --git a/App.tsx b/App.tsx index 8f9f5799..57ec58b8 100644 --- a/App.tsx +++ b/App.tsx @@ -15,6 +15,12 @@ import { import { DualMessageOverlay } from './components/DualMessageOverlay'; import { useApp } from './screens/AppController'; import { Alert } from 'react-native'; +import { + getAppInfoData, + getTelemetryConfigData, + initializeTelemetry, + sendAppInfoEvent, +} from './shared/telemetry/TelemetryUtils'; import { ErrorMessageOverlay } from './components/MessageOverlay'; import SecureKeystore from 'react-native-secure-keystore'; import { isCustomSecureKeystore } from './shared/cryptoutil/cryptoUtil'; @@ -35,6 +41,11 @@ const DecryptErrorAlert = (controller, t) => { }, ]); }; +function configureTelemetry() { + const config = getTelemetryConfigData(); + initializeTelemetry(config); + sendAppInfoEvent(getAppInfoData()); +} const AppLayoutWrapper: React.FC = () => { const { appService } = useContext(GlobalContext); @@ -44,6 +55,7 @@ const AppLayoutWrapper: React.FC = () => { if (isDecryptError) { DecryptErrorAlert(controller, t); } + configureTelemetry(); return ; }; diff --git a/components/LanguageSelector.tsx b/components/LanguageSelector.tsx index 49d66d56..ce3f5249 100644 --- a/components/LanguageSelector.tsx +++ b/components/LanguageSelector.tsx @@ -6,6 +6,7 @@ import Storage from '../shared/storage'; import {useTranslation} from 'react-i18next'; import i18next from 'i18next'; import RNRestart from 'react-native-restart'; +import { __SelectedLanguage } from '../shared/GlobalVariables'; export const LanguageSelector: React.FC = props => { const {i18n} = useTranslation(); @@ -16,6 +17,7 @@ export const LanguageSelector: React.FC = props => { const changeLanguage = async (language: string) => { if (language !== i18n.language) { await i18n.changeLanguage(language).then(async () => { + __SelectedLanguage.setValue(language); await Storage.setItem('language', i18n.language); const isRTL = i18next.dir(language) === 'rtl' ? true : false; if (isRTL !== I18nManager.isRTL) { diff --git a/machines/QrLoginMachine.ts b/machines/QrLoginMachine.ts index d020b82b..2d8de3b1 100644 --- a/machines/QrLoginMachine.ts +++ b/machines/QrLoginMachine.ts @@ -21,6 +21,7 @@ import { getPrivateKey, } from '../shared/keystore/SecureKeystore'; import i18n from '../i18n'; +import { getEndData, sendEndEvent } from '../shared/telemetry/TelemetryUtils'; const model = createModel( { @@ -225,6 +226,7 @@ export const qrLoginMachine = }, }, success: { + entry: [() => sendEndEvent(getEndData('QR login'))], on: { CONFIRM: { target: 'done', diff --git a/machines/bleShare/scan/scanMachine.ts b/machines/bleShare/scan/scanMachine.ts index 20b9a576..724928b9 100644 --- a/machines/bleShare/scan/scanMachine.ts +++ b/machines/bleShare/scan/scanMachine.ts @@ -39,6 +39,12 @@ import {WalletDataEvent} from 'react-native-tuvali/lib/typescript/types/events'; import {BLEError} from '../types'; import Storage from '../../../shared/storage'; import {logState} from '../../app'; +import { + getData, + getEndData, + sendStartEvent, + sendEndEvent, +} from '../../../shared/telemetry/TelemetryUtils'; const {wallet, EventTypes, VerificationStatus} = tuvali; @@ -425,7 +431,7 @@ export const scanMachine = }, navigatingToHistory: {}, }, - entry: 'sendScanData', + entry: ['sendScanData', () => sendStartEvent(getData('QR login'))], }, connecting: { invoke: { @@ -552,7 +558,7 @@ export const scanMachine = }, }, accepted: { - entry: 'logShared', + entry: ['logShared', () => sendEndEvent(getEndData('VC share'))], on: { DISMISS: { target: 'navigatingToHome', @@ -983,6 +989,7 @@ export const scanMachine = }, startConnection: context => callback => { + sendStartEvent(getData('VC share')); wallet.startConnection(context.openId4VpUri); const statusCallback = (event: WalletDataEvent) => { if (event.type === EventTypes.onSecureChannelEstablished) { diff --git a/machines/settings.ts b/machines/settings.ts index 4a0e822b..a070d926 100644 --- a/machines/settings.ts +++ b/machines/settings.ts @@ -16,7 +16,7 @@ import getAllConfigurations, { } from '../shared/commonprops/commonProps'; import Storage from '../shared/storage'; import ShortUniqueId from 'short-unique-id'; -import { AppId } from '../shared/request'; +import { __AppId } from '../shared/GlobalVariables'; import { isCustomSecureKeystore } from '../shared/cryptoutil/cryptoUtil'; const model = createModel( @@ -171,7 +171,7 @@ export const settingsMachine = model.createMachine( updateDefaults: model.assign({ appId: () => { const appId = generateAppId(); - AppId.setValue(appId); + __AppId.setValue(appId); return appId; }, @@ -192,7 +192,7 @@ export const settingsMachine = model.createMachine( setContext: model.assign((context, event) => { const newContext = event.response as ContextFrom; - AppId.setValue(newContext.appId); + __AppId.setValue(newContext.appId); return { ...context, ...newContext, diff --git a/machines/vcItem.ts b/machines/vcItem.ts index b120defd..879cf573 100644 --- a/machines/vcItem.ts +++ b/machines/vcItem.ts @@ -34,6 +34,12 @@ import getAllConfigurations, { import { VcEvents } from './vc'; import i18n from '../i18n'; import SecureKeystore from 'react-native-secure-keystore'; +import { + sendStartEvent, + getData, + getEndData, + sendEndEvent, +} from '../shared/telemetry/TelemetryUtils'; const model = createModel( { @@ -301,6 +307,7 @@ export const vcItemMachine = showBindingWarning: { on: { CONFIRM: { + actions: [() => sendStartEvent(getData('VC activation'))], target: '#vc-item.kebabPopUp.requestingBindingOtp', }, CANCEL: { @@ -413,6 +420,7 @@ export const vcItemMachine = 'updateVc', 'setWalletBindingErrorEmpty', 'logWalletBindingSuccess', + () => sendEndEvent(getEndData('VC activation')), ], target: '#vc-item.kebabPopUp', }, @@ -632,6 +640,7 @@ export const vcItemMachine = showBindingWarning: { on: { CONFIRM: { + actions: () => sendStartEvent(getData('VC activation')), target: 'requestingBindingOtp', }, CANCEL: { @@ -740,6 +749,7 @@ export const vcItemMachine = 'updateVc', 'setWalletBindingErrorEmpty', 'logWalletBindingSuccess', + () => sendEndEvent(getEndData('VC activation')), ], target: 'idle', }, diff --git a/package-lock.json b/package-lock.json index ea9a7df3..8147e330 100644 --- a/package-lock.json +++ b/package-lock.json @@ -62,6 +62,7 @@ "react-native-gesture-handler": "~2.9.0", "react-native-keychain": "^8.0.0", "react-native-linear-gradient": "^2.8.0", + "react-native-localize": "^3.0.2", "react-native-location": "^2.5.0", "react-native-mmkv-storage": "^0.9.1", "react-native-permissions": "^3.8.0", @@ -24095,6 +24096,21 @@ "react-native": "*" } }, + "node_modules/react-native-localize": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/react-native-localize/-/react-native-localize-3.0.2.tgz", + "integrity": "sha512-/l/oE1LVNgIRRhLbhmfFMHiWV0xhUn0A0iz1ytLVRYywL7FTp8Rx2vkJS/q/RpExDvV7yLw2493XZBYIM1dnLQ==", + "peerDependencies": { + "react": ">=18.1.0", + "react-native": ">=0.70.0", + "react-native-macos": ">=0.70.0" + }, + "peerDependenciesMeta": { + "react-native-macos": { + "optional": true + } + } + }, "node_modules/react-native-location": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/react-native-location/-/react-native-location-2.5.0.tgz", @@ -45551,6 +45567,12 @@ "integrity": "sha512-hgmCsgzd58WNcDCyPtKrvxsaoETjb/jLGxis/dmU3Aqm2u4ICIduj4ECjbil7B7pm9OnuTkmpwXu08XV2mpg8g==", "requires": {} }, + "react-native-localize": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/react-native-localize/-/react-native-localize-3.0.2.tgz", + "integrity": "sha512-/l/oE1LVNgIRRhLbhmfFMHiWV0xhUn0A0iz1ytLVRYywL7FTp8Rx2vkJS/q/RpExDvV7yLw2493XZBYIM1dnLQ==", + "requires": {} + }, "react-native-location": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/react-native-location/-/react-native-location-2.5.0.tgz", diff --git a/package.json b/package.json index 4f9b5b74..7540eab9 100644 --- a/package.json +++ b/package.json @@ -64,6 +64,7 @@ "react-native-gesture-handler": "~2.9.0", "react-native-keychain": "^8.0.0", "react-native-linear-gradient": "^2.8.0", + "react-native-localize": "^3.0.2", "react-native-location": "^2.5.0", "react-native-mmkv-storage": "^0.9.1", "react-native-permissions": "^3.8.0", diff --git a/screens/Settings/AboutInji.tsx b/screens/Settings/AboutInji.tsx index 7bff8f1e..9de78356 100644 --- a/screens/Settings/AboutInji.tsx +++ b/screens/Settings/AboutInji.tsx @@ -10,6 +10,8 @@ import getAllConfigurations from '../../shared/commonprops/commonProps'; import {getVersion} from 'react-native-device-info'; import {CopyButton} from '../../components/CopyButton'; import testIDProps from '../../shared/commonUtil'; +import { __InjiVersion, __TuvaliVersion } from '../../shared/GlobalVariables'; + export const AboutInji: React.FC = ({appId}) => { const {t} = useTranslation('AboutInji'); @@ -23,22 +25,6 @@ export const AboutInji: React.FC = ({appId}) => { }); }, []); - const dependencies = require('../../package-lock.json').dependencies; - let packageVersion, packageCommitId; - - Object.keys(dependencies).forEach(dependencyName => { - const dependencyData = dependencies[dependencyName]; - - if (dependencyName == 'react-native-tuvali') { - packageVersion = dependencyData.from - ? dependencyData.from.split('#')[1] - : 'unknown'; - if (packageVersion != 'unknown') { - packageCommitId = dependencyData.version.split('#')[1].substring(0, 7); - } - } - }); - return ( = ({appId}) => { - {t('version')}: {getVersion()} + {t('version')}: {__InjiVersion.getValue()} - {packageVersion != 'unknown' && ( + {__TuvaliVersion.getpackageVersion() != 'unknown' && ( - {t('tuvaliVersion')}: {packageVersion + '-' + packageCommitId} + {t('tuvaliVersion')}: {__TuvaliVersion.getValue()} )} diff --git a/screens/Settings/AppMetaData.tsx b/screens/Settings/AppMetaData.tsx index 15bcf82b..7ad34639 100644 --- a/screens/Settings/AppMetaData.tsx +++ b/screens/Settings/AppMetaData.tsx @@ -7,28 +7,12 @@ import { Divider, Icon, ListItem, Overlay } from 'react-native-elements'; import { Button, Text, Row } from '../../components/ui'; import { Theme } from '../../components/ui/styleUtils'; import appMetaData from '../../AppMetaData.md'; -import { getVersion } from 'react-native-device-info'; +import { __InjiVersion, __TuvaliVersion } from '../../shared/GlobalVariables'; export const AppMetaData: React.FC = (props) => { const { t } = useTranslation('AppMetaData'); const [isViewing, setIsViewing] = useState(false); - const dependencies = require('../../package-lock.json').dependencies; - let packageVersion, packageCommitId; - - Object.keys(dependencies).forEach((dependencyName) => { - const dependencyData = dependencies[dependencyName]; - - if (dependencyName == 'react-native-tuvali') { - packageVersion = dependencyData.from - ? dependencyData.from.split('#')[1] - : 'unknown'; - if (packageVersion != 'unknown') { - packageCommitId = dependencyData.version.split('#')[1].substring(0, 7); - } - } - }); - const markdownStyles = { text: { fontSize: 18, @@ -88,17 +72,16 @@ export const AppMetaData: React.FC = (props) => { align="center" size="small" color={Theme.Colors.version}> - {t('version')}: {getVersion()} + {t('version')}: {__InjiVersion.getValue()} - {packageVersion != 'unknown' && ( + {__TuvaliVersion.getpackageVersion() != 'unknown' && ( - {t('Tuvali-version')}:{' '} - {packageVersion + '-' + packageCommitId} + {t('Tuvali-version')}: {__TuvaliVersion.getValue()} )} diff --git a/screens/SetupLanguageScreen.tsx b/screens/SetupLanguageScreen.tsx index ab2fb500..3e26983e 100644 --- a/screens/SetupLanguageScreen.tsx +++ b/screens/SetupLanguageScreen.tsx @@ -11,6 +11,7 @@ import {Theme} from '../components/ui/styleUtils'; import {Icon} from 'react-native-elements'; import {RootRouteProps} from '../routes'; import {useWelcomeScreen} from './WelcomeScreenController'; +import { __SelectedLanguage } from '../shared/GlobalVariables'; export const SetupLanguageScreen: React.FC = props => { const {t} = useTranslation('SetupLanguage'); @@ -22,6 +23,7 @@ export const SetupLanguageScreen: React.FC = props => { const changeLanguage = async (language: string) => { if (language !== i18n.language) { await i18n.changeLanguage(language).then(async () => { + __SelectedLanguage.setValue(language); await Storage.setItem('language', i18n.language); const isRTL = i18next.dir(language) === 'rtl' ? true : false; if (isRTL !== I18nManager.isRTL) { diff --git a/shared/GlobalVariables.ts b/shared/GlobalVariables.ts new file mode 100644 index 00000000..778d03ef --- /dev/null +++ b/shared/GlobalVariables.ts @@ -0,0 +1,83 @@ +import { getVersion } from 'react-native-device-info'; +import ShortUniqueId from 'short-unique-id'; +import { APP_ID_LENGTH } from './constants'; +/* eslint-disable @typescript-eslint/no-var-requires */ +const dependencies = require('../package-lock.json').dependencies; + +function getTuvaliPackageDetails() { + let packageVersion, packageCommitId; + + Object.keys(dependencies).forEach((dependencyName) => { + const dependencyData = dependencies[dependencyName]; + + if (dependencyName == 'react-native-tuvali') { + packageVersion = dependencyData.from + ? dependencyData.from.split('#')[1] + : 'unknown'; + + if (packageVersion != 'unknown') { + packageCommitId = dependencyData.version.split('#')[1].substring(0, 7); + } + } + }); + return { packageVersion, packageCommitId }; +} +export class __AppId { + private static value: string; + + public static getValue(): string { + return __AppId.value; + } + + public static setValue(value: string) { + this.value = value; + } +} +export class __TuvaliVersion { + private static packageDetails = getTuvaliPackageDetails(); + + public static getPackageCommitId(): string { + return __TuvaliVersion.packageDetails.packageCommitId; + } + + public static getpackageVersion(): string { + return __TuvaliVersion.packageDetails.packageVersion; + } + + public static getValue(): string { + return this.getpackageVersion() + '-' + this.getPackageCommitId(); + } +} +export class __InjiVersion { + private static value = getVersion(); + + public static getValue(): string { + return __InjiVersion.value; + } +} +export class __SessionId { + private static value = generateSessionId(); + + public static getValue(): string { + return __SessionId.value; + } +} + +export class __SelectedLanguage { + private static language = ''; + + public static getValue(): string { + return __SelectedLanguage.language; + } + + public static setValue(currentLangauge: string) { + this.language = currentLangauge; + } +} + +function generateSessionId() { + const shortUUID = new ShortUniqueId({ + length: APP_ID_LENGTH, + }); + return shortUUID.randomUUID() + Date.now(); +} diff --git a/shared/request.ts b/shared/request.ts index 9cfa8da8..a043dbe9 100644 --- a/shared/request.ts +++ b/shared/request.ts @@ -1,5 +1,6 @@ import { DecodedCredential, VerifiableCredential } from '../types/vc'; -import { MIMOTO_BASE_URL } from './constants'; +import { __AppId } from './GlobalVariables'; +import { HOST, MIMOTO_BASE_URL } from './constants'; export class BackendResponseError extends Error { constructor(name: string, message: string) { @@ -8,18 +9,6 @@ export class BackendResponseError extends Error { } } -export class AppId { - private static value: string; - - public static getValue(): string { - return AppId.value; - } - - public static setValue(value: string) { - this.value = value; - } -} - export async function request( method: 'GET' | 'POST' | 'PATCH', path: `/${string}`, @@ -29,7 +18,8 @@ export async function request( const headers = { 'Content-Type': 'application/json', }; - if (path.includes('residentmobileapp')) headers['X-AppId'] = AppId.getValue(); + if (path.includes('residentmobileapp')) + headers['X-AppId'] = __AppId.getValue(); const response = await fetch(host + path, { method, diff --git a/shared/telemetry/TelemetryUtils.js b/shared/telemetry/TelemetryUtils.js new file mode 100644 index 00000000..9dbb8716 --- /dev/null +++ b/shared/telemetry/TelemetryUtils.js @@ -0,0 +1,80 @@ +import telemetry from '@project-sunbird/telemetry-sdk'; +import {Platform} from 'react-native'; +import {HOST} from '../constants'; +import { + __AppId, + __InjiVersion, + __SelectedLanguage, + __SessionId, + __TuvaliVersion, +} from '../GlobalVariables'; +import {OBSRV_HOST} from 'react-native-dotenv'; +import DeviceInfo from 'react-native-device-info'; +import {isCustomSecureKeystore} from '../cryptoutil/cryptoUtil'; +import * as RNLocalize from 'react-native-localize'; + +export function sendImpressionEvent(data) { + telemetry.impression(data, {}); +} + +export function sendEndEvent(data) { + telemetry.end(data, {}); +} + +export function initializeTelemetry(config) { + telemetry.initialize(config); +} + +export function sendStartEvent(data) { + telemetry.start({}, '', '', data, {}); +} + +export function sendAppInfoEvent(data) { + telemetry.appinfo(data); +} + +export function getTelemetryConfigData() { + return { + authtoken: '', + appid: __AppId.getValue(), + sid: __SessionId.getValue(), + batchsize: 5, + host: OBSRV_HOST, + endpoint: '/obsrv/v1/data/mosip-dataset', + telemetryDebugEnabled: true, + enableValidation: true, + schemaBaseUrl: 'http://mosip.io/telemetry/', + }; +} + +export function getData(type) { + return { + type: type, + }; +} + +export function getEndData(type) { + return { + type: type, + status: 'SUCCESS', + }; +} + +export function getAppInfoData() { + return { + env: HOST, + brandName: DeviceInfo.getBrand(), + modelName: DeviceInfo.getModel(), + osName: DeviceInfo.getSystemName(), + osVersion: DeviceInfo.getSystemVersion(), + osApiLevel: Platform.Version.toString(), + isHardwareKeystoreSupported: isCustomSecureKeystore(), + dateTime: new Date().getTime(), + zone: RNLocalize.getTimeZone(), + offset: new Date().getTimezoneOffset() * 60 * 1000, + preferredLanguage: __SelectedLanguage.getValue(), + buildNumber: DeviceInfo.getBuildNumber(), + injiVersion: __InjiVersion.getValue(), + tuvaliVersion: __TuvaliVersion.getValue(), + }; +} diff --git a/types/react-native-dotenv/index.d.ts b/types/react-native-dotenv/index.d.ts index 6b452f34..3aed7591 100644 --- a/types/react-native-dotenv/index.d.ts +++ b/types/react-native-dotenv/index.d.ts @@ -9,6 +9,11 @@ declare module 'react-native-dotenv' { */ export const ESIGNET_HOST: string; + /** + * URL for the obsrv server for telemetry + */ + export const OBSRV_HOST: string; + /** * API key to use Google Nearby Messages API */