feat: report analytics for the network connection

Will emit new 'network.info' action with the online/offline status and
extra details for native like the network type and
'isConnectionExpensive' flag.
This commit is contained in:
paweldomas 2019-08-22 10:08:34 -05:00 committed by Paweł Domas
parent 2c70388a9e
commit 6ae9bbe0c5
20 changed files with 377 additions and 2 deletions

View File

@ -56,6 +56,7 @@ dependencies {
implementation project(':react-native-background-timer')
implementation project(':react-native-calendar-events')
implementation project(':react-native-community-async-storage')
implementation project(':react-native-community_netinfo')
implementation project(':react-native-immersive')
implementation project(':react-native-keep-awake')
implementation project(':react-native-linear-gradient')

View File

@ -193,6 +193,7 @@ class ReactInstanceManagerHolder {
new com.oblador.vectoricons.VectorIconsPackage(),
new com.ocetnik.timer.BackgroundTimerPackage(),
new com.reactnativecommunity.asyncstorage.AsyncStoragePackage(),
new com.reactnativecommunity.netinfo.NetInfoPackage(),
new com.reactnativecommunity.webview.RNCWebViewPackage(),
new com.rnimmersive.RNImmersivePackage(),
new com.zmxv.RNSound.RNSoundPackage(),

View File

@ -7,6 +7,8 @@ include ':react-native-calendar-events'
project(':react-native-calendar-events').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-calendar-events/android')
include ':react-native-community-async-storage'
project(':react-native-community-async-storage').projectDir = new File(rootProject.projectDir, '../node_modules/@react-native-community/async-storage/android')
include ':react-native-community_netinfo'
project(':react-native-community_netinfo').projectDir = new File(rootProject.projectDir, '../node_modules/@react-native-community/netinfo/android')
include ':react-native-google-signin'
project(':react-native-google-signin').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-google-signin/android')
include ':react-native-immersive'

View File

@ -47,6 +47,7 @@ target 'JitsiMeet' do
pod 'react-native-background-timer', :path => '../node_modules/react-native-background-timer'
pod 'react-native-calendar-events', :path => '../node_modules/react-native-calendar-events'
pod 'react-native-keep-awake', :path => '../node_modules/react-native-keep-awake'
pod 'react-native-netinfo', :path => '../node_modules/@react-native-community/netinfo'
pod 'react-native-webview', :path => '../node_modules/react-native-webview'
pod 'react-native-webrtc', :path => '../node_modules/react-native-webrtc'
pod 'BVLinearGradient', :path => '../node_modules/react-native-linear-gradient'

View File

@ -139,6 +139,8 @@ PODS:
- React
- react-native-keep-awake (4.0.0):
- React
- react-native-netinfo (4.1.5):
- React
- react-native-webrtc (1.69.2):
- React
- react-native-webview (5.8.1):
@ -203,6 +205,7 @@ DEPENDENCIES:
- react-native-background-timer (from `../node_modules/react-native-background-timer`)
- react-native-calendar-events (from `../node_modules/react-native-calendar-events`)
- react-native-keep-awake (from `../node_modules/react-native-keep-awake`)
- react-native-netinfo (from `../node_modules/@react-native-community/netinfo`)
- react-native-webrtc (from `../node_modules/react-native-webrtc`)
- react-native-webview (from `../node_modules/react-native-webview`)
- React-RCTActionSheet (from `../node_modules/react-native/Libraries/ActionSheetIOS`)
@ -271,6 +274,8 @@ EXTERNAL SOURCES:
:path: "../node_modules/react-native-calendar-events"
react-native-keep-awake:
:path: "../node_modules/react-native-keep-awake"
react-native-netinfo:
:path: "../node_modules/@react-native-community/netinfo"
react-native-webrtc:
:path: "../node_modules/react-native-webrtc"
react-native-webview:
@ -340,6 +345,7 @@ SPEC CHECKSUMS:
react-native-background-timer: 0d34748e53a972507c66963490c775321a88f6f2
react-native-calendar-events: 2fe35a9294af05de0ed819d3a1b5dac048d2c010
react-native-keep-awake: eba3137546b10003361b37c761f6c429b59814ae
react-native-netinfo: 8d8db463bcc5db66a8ac5c48a7d86beb3b92f61a
react-native-webrtc: 1415d2a54b2246dd85ba95eb3e4bf2b66533f951
react-native-webview: a95842e3f351a6d2c8bc8bcc9eab689c7e7e5ad4
React-RCTActionSheet: b0f1ea83f4bf75fb966eae9bfc47b78c8d3efd90
@ -359,6 +365,6 @@ SPEC CHECKSUMS:
RNWatch: 09738b339eceb66e4d80a2371633ca5fb380fa42
yoga: 312528f5bbbba37b4dcea5ef00e8b4033fdd9411
PODFILE CHECKSUM: 6b6e260b4be4e86f9d05c0d7dab40f60118bb355
PODFILE CHECKSUM: 0907bfe60b5b5f11dbdc6b4e65d40a248d000513
COCOAPODS: 1.7.2

5
package-lock.json generated
View File

@ -3055,6 +3055,11 @@
}
}
},
"@react-native-community/netinfo": {
"version": "4.1.5",
"resolved": "https://registry.npmjs.org/@react-native-community/netinfo/-/netinfo-4.1.5.tgz",
"integrity": "sha512-lagdZr9UiVAccNXYfTEj+aUcPCx9ykbMe9puffeIyF3JsRuMmlu3BjHYx1klUHX7wNRmFNC8qVP0puxUt1sZ0A=="
},
"@segment/top-domain": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/@segment/top-domain/-/top-domain-3.0.0.tgz",

View File

@ -35,6 +35,7 @@
"@atlaskit/tooltip": "12.1.13",
"@microsoft/microsoft-graph-client": "1.1.0",
"@react-native-community/async-storage": "1.3.4",
"@react-native-community/netinfo": "4.1.5",
"@tensorflow-models/body-pix": "^1.0.1",
"@tensorflow/tfjs": "^1.1.2",
"@webcomponents/url": "0.7.1",

View File

@ -271,6 +271,27 @@ export function createInviteDialogEvent(
};
}
/**
* Creates an event which reports about the current network information reported by the operating system.
*
* @param {boolean} isOnline - Tells whether or not the internet is reachable.
* @param {string} [networkType] - Network type, see {@code NetworkInfo} type defined by the 'base/net-info' feature.
* @param {Object} [details] - Extra info, see {@code NetworkInfo} type defined by the 'base/net-info' feature.
* @returns {Object}
*/
export function createNetworkInfoEvent({ isOnline, networkType, details }) {
const attributes = { isOnline };
// Do no include optional stuff or Amplitude handler will log warnings.
networkType && (attributes.networkType = networkType);
details && (attributes.details = details);
return {
action: 'network.info',
attributes
};
}
/**
* Creates an "offer/answer failure" event.
*

View File

@ -6,6 +6,7 @@ import {
SET_ROOM
} from '../base/conference';
import { SET_CONFIG } from '../base/config';
import { SET_NETWORK_INFO } from '../base/net-info';
import { MiddlewareRegistry } from '../base/redux';
import {
getLocalAudioTrack,
@ -16,7 +17,7 @@ import {
} from '../base/tracks';
import { UPDATE_LOCAL_TRACKS_DURATION } from './actionTypes';
import { createLocalTracksDurationEvent } from './AnalyticsEvents';
import { createLocalTracksDurationEvent, createNetworkInfoEvent } from './AnalyticsEvents';
import { initAnalytics, resetAnalytics, sendAnalytics } from './functions';
/**
@ -127,6 +128,14 @@ MiddlewareRegistry.register(store => next => action => {
});
break;
}
case SET_NETWORK_INFO:
sendAnalytics(
createNetworkInfoEvent({
isOnline: action.isOnline,
details: action.details,
networkType: action.networkType
}));
break;
case SET_ROOM: {
initAnalytics(store);
break;

View File

@ -0,0 +1,66 @@
// @flow
import EventEmitter from 'events';
import NetInfo from '@react-native-community/netinfo';
import type { NetInfoState, NetInfoSubscription } from '@react-native-community/netinfo';
import { ONLINE_STATE_CHANGED_EVENT } from './events';
import type { NetworkInfo } from './types';
/**
* The network info service implementation for iOS and Android. 'react-native-netinfo' seems to support windows as well,
* but that has not been tested and is nto used by jitsi-meet.
*/
export default class NetworkInfoService extends EventEmitter {
/**
* Stores the native subscription for future cleanup.
*/
_subscription: NetInfoSubscription;
/**
* Converts library's structure to {@link NetworkInfo} used by jitsi-meet.
*
* @param {NetInfoState} netInfoState - The new state given by the native library.
* @private
* @returns {NetworkInfo}
*/
static _convertNetInfoState(netInfoState: NetInfoState): NetworkInfo {
return {
isOnline: netInfoState.isInternetReachable,
details: netInfoState.details,
networkType: netInfoState.type
};
}
/**
* Checks for support.
*
* @returns {boolean}
*/
static isSupported() {
return Boolean(NetInfo);
}
/**
* Starts the service.
*
* @returns {void}
*/
start() {
this._subscription = NetInfo.addEventListener(netInfoState => {
this.emit(ONLINE_STATE_CHANGED_EVENT, NetworkInfoService._convertNetInfoState(netInfoState));
});
}
/**
* Stops the service.
*
* @returns {void}
*/
stop() {
if (this._subscription) {
this._subscription();
this._subscription = undefined;
}
}
}

View File

@ -0,0 +1,58 @@
import EventEmitter from 'events';
import { ONLINE_STATE_CHANGED_EVENT } from './events';
/**
* The network info service implementation for web (Chrome, Firefox and Safari).
*/
export default class NetworkInfoService extends EventEmitter {
/**
* Creates new instance...
*/
constructor() {
super();
this._onlineStateListener = this._handleOnlineStatusChange.bind(this, /* online */ true);
this._offlineStateListener = this._handleOnlineStatusChange.bind(this, /* offline */ false);
}
/**
* Callback function to track the online state.
*
* @param {boolean} isOnline - Is the browser online or not.
* @private
* @returns {void}
*/
_handleOnlineStatusChange(isOnline) {
this.emit(ONLINE_STATE_CHANGED_EVENT, { isOnline });
}
/**
* Checks for support.
*
* @returns {boolean}
*/
static isSupported() {
return window.addEventListener && typeof navigator.onLine !== 'undefined';
}
/**
* Starts the service.
*
* @returns {void}
*/
start() {
window.addEventListener('online', this._onlineStateListener);
window.addEventListener('offline', this._offlineStateListener);
}
/**
* Stops the service.
*
* @returns {void}
*/
stop() {
window.removeEventListener('online', this._onlineStateListener);
window.removeEventListener('offline', this._offlineStateListener);
}
}

View File

@ -0,0 +1,13 @@
/**
* The action dispatched when the {@link NetworkInfo} structure is being updated.
*
* @type {string}
*/
export const SET_NETWORK_INFO = 'SET_NETWORK_INFO';
/**
* Tha action dispatched by 'base/net-info' middleware in order to store the cleanup function for later use.
* @type {string}
* @private
*/
export const _STORE_NETWORK_INFO_CLEANUP = 'STORE_NETWORK_INFO_CLEANUP';

View File

@ -0,0 +1,42 @@
// @flow
import { SET_NETWORK_INFO, _STORE_NETWORK_INFO_CLEANUP } from './actionTypes';
import type { NetworkInfo } from './types';
/**
* Up[dates the network info state.
*
* @param {NetworkInfo} networkInfo - The new network state to be set.
* @returns {{
* type: SET_NETWORK_INFO,
* isOnline: boolean,
* networkType: string,
* details: Object
* }}
*/
export function setNetworkInfo({ isOnline, networkType, details }: NetworkInfo): Object {
return {
type: SET_NETWORK_INFO,
isOnline,
networkType,
details
};
}
/**
* Stored the cleanup function used to shutdown the {@code NetworkInfoService}.
*
* @param {Function} cleanup - The cleanup function to be called on {@code APP_WILL_UNMOUNT}.
* @returns {{
* type: _STORE_NETWORK_INFO_CLEANUP,
* cleanup: Function
* }}
* @private
*/
export function _storeNetworkInfoCleanup(cleanup: Function): Object {
return {
type: _STORE_NETWORK_INFO_CLEANUP,
cleanup
};
}

View File

@ -0,0 +1,6 @@
/**
* The name for Redux store key used by the 'base/net-info' feature.
*
* @type {string}
*/
export const STORE_NAME = 'features/base/net-info';

View File

@ -0,0 +1 @@
export const ONLINE_STATE_CHANGED_EVENT = 'network-info-online-status-change';

View File

@ -0,0 +1,4 @@
export * from './actionTypes';
import './middleware';
import './reducer';

View File

@ -0,0 +1,5 @@
// @flow
import { getLogger } from '../logging/functions';
export default getLogger('features/base/net-info');

View File

@ -0,0 +1,64 @@
// @flow
import { APP_WILL_MOUNT, APP_WILL_UNMOUNT } from '../app';
import { MiddlewareRegistry } from '../redux';
import { _storeNetworkInfoCleanup, setNetworkInfo } from './actions';
import { STORE_NAME } from './constants';
import { ONLINE_STATE_CHANGED_EVENT } from './events';
import logger from './logger';
import NetworkInfoService from './NetworkInfoService';
import type { NetworkInfo } from './types';
/**
* Middleware for 'base/net-info' feature.
*
* @param {Store} store - The redux store.
* @returns {Function}
*/
// eslint-disable-next-line no-unused-vars
MiddlewareRegistry.register(({ dispatch, getState }) => next => action => {
const result = next(action);
switch (action.type) {
case APP_WILL_MOUNT:
if (NetworkInfoService.isSupported()) {
const networkInfoService = new NetworkInfoService();
const stop = () => {
networkInfoService.stop();
networkInfoService.removeAllListeners();
};
networkInfoService.addListener(
ONLINE_STATE_CHANGED_EVENT,
({ isOnline, networkType, details }: NetworkInfo) => {
logger.info('Network changed', JSON.stringify({
isOnline,
details,
networkType
}));
dispatch(setNetworkInfo({
isOnline,
networkType,
details
}));
});
dispatch(_storeNetworkInfoCleanup(stop));
networkInfoService.start();
}
break;
case APP_WILL_UNMOUNT: {
const { _cleanup } = getState()[STORE_NAME];
if (_cleanup) {
_cleanup();
dispatch(_storeNetworkInfoCleanup(undefined));
}
}
break;
}
return result;
});

View File

@ -0,0 +1,30 @@
// @flow
import { assign, ReducerRegistry } from '../redux';
import { SET_NETWORK_INFO, _STORE_NETWORK_INFO_CLEANUP } from './actionTypes';
import { STORE_NAME } from './constants';
const DEFAULT_STATE = {
isOnline: true
};
/**
* The base/net-info feature's reducer.
*/
ReducerRegistry.register(STORE_NAME, (state = DEFAULT_STATE, action) => {
switch (action.type) {
case SET_NETWORK_INFO:
return assign(state, {
isOnline: action.isOnline,
networkType: action.networkType,
cellularGeneration: action.cellularGeneration,
details: action.details
});
case _STORE_NETWORK_INFO_CLEANUP:
return assign(state, {
_cleanup: action.cleanup
});
default:
return state;
}
});

View File

@ -0,0 +1,39 @@
// @flow
import { NetInfoCellularGeneration, NetInfoStateType } from '@react-native-community/netinfo';
/**
* Describes the structure which is used by jitsi-meet to store information about the current network type and
* conditions.
*/
export type NetworkInfo = {
/**
* Tells whether or not the internet is reachable.
*/
isOnline: boolean,
/**
* The network type. Currently reported only on Android/iOS. Can be one of the constants defined by
* the 'react-native-netinfo' library.
*/
networkType: ?NetInfoStateType,
/**
* Any extra info provided by the OS. Should be JSON and is OS specific. Reported only by iOS and Android and
* the format is whatever comes out of the 'react-native-netinfo' library which is network type dependent.
*/
details: ?{
/**
* If {@link networkType} is {@link NetInfoStateType.cellular} then it may provide the info about the type of
* cellular network.
*/
cellularGeneration: ?NetInfoCellularGeneration;
/**
* Indicates whether or not the connection is expensive.
*/
isConnectionExpensive: ?boolean;
}
}