Skip to content

Instantly share code, notes, and snippets.

@zhelezkov
Created January 19, 2023 22:53
Show Gist options
  • Save zhelezkov/ceb7b96fed645cb5e3dd919eea45c03b to your computer and use it in GitHub Desktop.
Save zhelezkov/ceb7b96fed645cb5e3dd919eea45c03b to your computer and use it in GitHub Desktop.
import DeviceInfo from 'react-native-device-info';
import {
getNotificationsToken,
getNotificationsPermission,
onNotificationTokenChange,
PushNotificationStatus,
firebaseNotificationsAuthStatusToBoolean,
getNotificationsPermissionString,
} from '@src/notifications';
import * as Sentry from '@sentry/react-native';
import { Severity } from '@sentry/react-native';
import auth from '@react-native-firebase/auth';
import { fetcher } from '@src/fetcher';
import { createEffect, createEvent } from 'effector';
import { debounce } from 'patronum';
// update device on app open(useful since notification token might be changed)
// update device on user change(useful to attach device to customer)
auth().onAuthStateChanged(() => updateDevice());
// update device in case of notification token has changed during app runtime
onNotificationTokenChange(() => updateDevice());
export const updateDevice = createEvent();
const updateDeviceFx = createEffect(async () => {
const [deviceInfo, pushPermission, notificationsToken] = await Promise.all([
collectDeviceInfo(),
getNotificationsPermission(),
getNotificationsToken(),
]);
const notificationsEnabled =
firebaseNotificationsAuthStatusToBoolean(pushPermission);
const pushPermissionHumanReadable =
getNotificationsPermissionString(pushPermission);
await patchCustomerDevice({
uniqueDeviceId: deviceInfo.deviceId,
notificationsToken: notificationsToken,
notificationsEnabled: notificationsEnabled,
pushPermission: pushPermissionHumanReadable,
additionalInfo: deviceInfo.additionalInfo,
});
});
updateDeviceFx.failData.watch((err) => Sentry.captureException(err));
debounce({
source: updateDevice,
target: updateDeviceFx,
timeout: 1488,
});
interface PathCustomerDeviceParams {
uniqueDeviceId: string;
notificationsToken?: string | null;
notificationsEnabled?: boolean;
pushPermission?: PushNotificationStatus;
additionalInfo?: Record<string, any>;
}
async function patchCustomerDevice({
uniqueDeviceId,
notificationsToken,
notificationsEnabled,
pushPermission,
additionalInfo,
}: PathCustomerDeviceParams): Promise<unknown> {
const endpoint = auth().currentUser
? 'v1/customer/me/device'
: 'v1/customer/device';
return fetcher.patch(endpoint, {
json: {
nativeId: uniqueDeviceId,
fcmRegistrationToken: notificationsToken,
pushNotificationsEnabled: notificationsEnabled,
pushPermission: pushPermission,
additionalInfo: additionalInfo,
},
});
}
async function collectDeviceInfo(): Promise<{
deviceId: string;
additionalInfo: Record<string, any>;
}> {
const apiLevel = DeviceInfo.getApiLevel();
const baseOs = DeviceInfo.getBaseOs();
const bootLoader = DeviceInfo.getBootloader();
const carrier = DeviceInfo.getCarrier();
const device = DeviceInfo.getDevice();
const display = DeviceInfo.getDisplay();
const deviceName = DeviceInfo.getDeviceName();
const deviceToken = DeviceInfo.getDeviceToken();
const firstInstallTime = DeviceInfo.getFirstInstallTime();
const fingerprint = DeviceInfo.getFingerprint();
const fontScale = DeviceInfo.getFontScale();
const hardware = DeviceInfo.getHardware();
const hostname = DeviceInfo.getHost();
const installerPackageName = DeviceInfo.getInstallerPackageName();
const installerReferrer = DeviceInfo.getInstallReferrer();
const instanceId = DeviceInfo.getInstanceId();
const lastUpdateTime = DeviceInfo.getLastUpdateTime();
const macAddress = DeviceInfo.getMacAddress();
const manufacturer = DeviceInfo.getManufacturer();
const product = DeviceInfo.getProduct();
const buildId = DeviceInfo.getBuildId();
const uniqueId = DeviceInfo.syncUniqueId();
const isEmulator = DeviceInfo.isEmulator();
const isTabletMode = DeviceInfo.isTabletMode();
const hasGms = DeviceInfo.hasGms();
const hasHms = DeviceInfo.hasHms();
const systemAvailableFeatures = DeviceInfo.getSystemAvailableFeatures();
const availableLocationProviders = DeviceInfo.getAvailableLocationProviders();
const diskCapacity = DeviceInfo.getTotalDiskCapacity();
const isLocationEnabled = DeviceInfo.isLocationEnabled();
const userAgent = DeviceInfo.getUserAgent();
const brand = DeviceInfo.getBrand();
const manufacturerDeviceId = DeviceInfo.getDeviceId();
const systemName = DeviceInfo.getSystemName();
const systemVersion = DeviceInfo.getSystemVersion();
const hasNotch = DeviceInfo.hasNotch();
const isTablet = DeviceInfo.isTablet();
const deviceType = DeviceInfo.getDeviceType();
const all = Object.entries({
apiLevel,
baseOs,
bootLoader,
carrier,
device,
display,
deviceName,
deviceToken,
firstInstallTime,
fingerprint,
fontScale,
hardware,
hostname,
installerPackageName,
installerReferrer,
instanceId,
lastUpdateTime,
macAddress,
manufacturer,
product,
buildId,
uniqueId,
isEmulator,
isTabletMode,
hasGms,
hasHms,
systemAvailableFeatures,
availableLocationProviders,
diskCapacity,
isLocationEnabled,
userAgent,
}).map(([key, promise]) =>
promise
.then((value) => ({ key, status: 'fulfilled', value }))
.catch((value) => ({ key, status: 'rejected', value })),
);
const allData = Object.fromEntries(
(await Promise.all(all))
.filter((it) => {
if (it.status === 'rejected') {
Sentry.captureMessage(
'unable to resolve device info param',
(scope) => {
scope.setLevel(Severity.Warning);
scope.setContext('device_info_reject', {
key: it.key,
value: it.value,
});
return scope;
},
);
}
return it.status === 'fulfilled';
})
.map((it) => [it.key, it.value]),
);
return {
deviceId: allData.uniqueId as string,
additionalInfo: {
...allData,
brand,
manufacturerDeviceId,
systemName,
systemVersion,
hasNotch,
isTablet,
deviceType,
},
};
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment