@hhgtech/hhg-components
Version:
Hello Health Group common components
348 lines (341 loc) • 14 kB
JavaScript
import { a as __awaiter } from './tslib.es6-00ab44b2.js';
import { useRef, useEffect, useState, useCallback, useMemo } from 'react';
import { getCookie } from './miscCookieHelper.js';
import { u as useInitialMount } from './useUniqueId-38cdf062.js';
import './index-fe4471f4.js';
import { u as useTranslations } from './index-09d9e570.js';
import { L as LOCALE } from './Locale-dc1237b9.js';
function normalizeAndHash(string) {
const utf8 = new TextEncoder().encode(string.trim().toLowerCase());
return crypto.subtle.digest('SHA-256', utf8).then((hashBuffer) => {
const hashArray = Array.from(new Uint8Array(hashBuffer));
const hashHex = hashArray
.map((bytes) => bytes.toString(16).padStart(2, '0'))
.join('');
return hashHex;
});
}
const FbDataLayerStoreKey = 'fbDataLayer_userInfo';
const IPStoreKey = 'fbClientIp';
const useFbDataLayer = (userInfo) => {
const savedIpRef = useRef('');
useEffect(() => {
(() => __awaiter(void 0, void 0, void 0, function* () {
var _a, _b, _c, _d, _e;
if (!savedIpRef.current) {
let userIp = yield fetch('https://api.ipify.org').then((res) => res.text());
if (!userIp) {
userIp = yield fetch('https://icanhazip.com').then((res) => res.text());
}
savedIpRef.current = userIp;
}
(_b = (_a = window.localStorage) === null || _a === void 0 ? void 0 : _a.setItem) === null || _b === void 0 ? void 0 : _b.call(_a, IPStoreKey, JSON.stringify({
client_ip_address: ((_c = savedIpRef.current) === null || _c === void 0 ? void 0 : _c.replace('\n', '')) || '',
client_user_agent: navigator.userAgent || '',
}));
if (userInfo === null || userInfo === void 0 ? void 0 : userInfo.id) {
const [em, ph, db, ge, ct, fn] = yield Promise.all([
userInfo.email ? normalizeAndHash(userInfo.email) : null,
userInfo.phone ? normalizeAndHash(userInfo.phone) : null,
userInfo.birthday
? normalizeAndHash(userInfo.birthday.split('-').join(''))
: null,
userInfo.gender ? 'm' : 'f',
userInfo.city_name
? normalizeAndHash(userInfo.city_name).then((n) => n.replace(/\s/g, ''))
: null,
userInfo.name ? normalizeAndHash(userInfo.name) : null,
]);
({
fbc: getCookie('_fbc') || '',
fbp: getCookie('_fbp') || '',
});
const localData = {
user_id: userInfo.id,
};
if (fn) {
localData['fn'] = fn;
}
if (em) {
localData['em'] = em;
}
if (ph) {
localData['ph'] = ph;
}
if (db) {
localData['db'] = db;
}
if (ct) {
localData['ct'] = ct;
}
(_e = (_d = window.localStorage) === null || _d === void 0 ? void 0 : _d.setItem) === null || _e === void 0 ? void 0 : _e.call(_d, FbDataLayerStoreKey, JSON.stringify(localData));
// ;(async function waitFbq() {
// // check for the generic script
// // easiest check to prevent race condition
// if (window.fbq) {
// window.fbq('trackCustom', 'FB Conversion', {
// action_source: 'website',
// event_source_url: window.location.href,
// user_data: [userData],
// })
// } else {
// timeout = setTimeout(waitFbq, 1000)
// }
// })()
}
}))();
return () => {
};
}, [userInfo]);
};
/** src: https://www.benmvp.com/blog/8-helpful-custom-react-hooks/
* know the PREVIOUS + DIFFERENT value of props or state. */
const usePrevious = (value) => {
// use refs to keep track of both the previous &
// current values
const prevRef = useRef();
const curRef = useRef(value);
const isInitialMount = useInitialMount();
// after the initial render, if the value passed in
// differs from the `curRef`, then we know that the
// value we're tracking actually changed. we can
// update the refs. otherwise if the `curRef` &
// value are the same, something else caused the
// re-render and we should *not* update `prevRef`.
if (!isInitialMount && curRef.current !== value) {
prevRef.current = curRef.current;
curRef.current = value;
}
return prevRef.current;
};
/** src: https://www.benmvp.com/blog/8-helpful-custom-react-hooks/
* delay State update till next animation frame. Similar to debounce but shorter */
const useRafState = (initialState) => {
// this is the actual state
const [state, setState] = useState(initialState);
// keep track of the `requestAnimationFrame` request ID
// across renders and successive calls to `useRafState`
const requestId = useRef(0);
// the actual state setter we'll return.
// using `useCallback` so that it's memoized
// just like `setState`
const setRafState = useCallback((value) => {
// cancel active request before making next one.
// this is debouncing.
cancelAnimationFrame(requestId.current);
// create new request to set state on animation frame
requestId.current = requestAnimationFrame(() => {
setState(value);
});
}, []);
// cancel any active request when component unmounts
useEffect(() => {
return () => cancelAnimationFrame(requestId.current);
});
return [state, setRafState];
};
const PREGNANCY_SLUG = {
'vi-VN': 'mang-thai',
'id-ID': 'kehamilan',
'ms-MY': 'kehamilan',
'km-KH': 'ពពោះ',
'en-PH': 'pregnancy',
'tl-PH': 'pregnancy',
'th-TH': 'การตั้งครรภ์',
'my-MM': 'pregnancy',
'zh-TW': 'pregnancy',
'hi-IN': 'pregnancy',
'zh-SG': 'pregnancy',
};
// NOTE: this is temporary mapping on 13/02/2023, can be outdated due to changes from content team in the future
const MAPPED_CATEGORY_SLUGS = {
[LOCALE.Vietnam]: {
sexualWellness: 'suc-khoe-tinh-duc',
health: 'suc-khoe',
healthyEating: 'an-uong-lanh-manh',
skinHealth: 'da-lieu',
healthyHabit: 'thoi-quen-lanh-manh',
drug: 'thuoc',
womensHealth: 'suc-khoe-phu-nu',
fitness: 'the-duc-the-thao',
pregnancy: 'mang-thai',
parenting: 'nuoi-day-con',
diabetes: 'tieu-duong-dai-thao-duong',
healthyMind: 'tam-ly-tam-than',
},
[LOCALE.Indonesia]: {
pregnancy: 'kehamilan',
parenting: 'parenting',
health: 'sehat',
diabetes: 'diabetes',
womensHealth: 'wanita',
drug: 'obat-suplemen',
sexualWellness: '',
healthyEating: '',
skinHealth: '',
},
[LOCALE.Malaysia]: {
pregnancy: 'kehamilan',
parenting: 'keibubapaan',
health: 'kesihatan',
diabetes: 'kencing-manis',
womensHealth: 'kesihatan-wanita',
drug: 'ubat',
sexualWellness: '',
healthyEating: '',
skinHealth: '',
},
[LOCALE.Cambodia]: {
pregnancy: '%e1%9e%96%e1%9e%96%e1%9f%84%e1%9f%87',
parenting: '%e1%9e%85%e1%9e%b7%e1%9e%89%e1%9f%92%e1%9e%85%e1%9e%b9%e1%9e%98%e1%9e%80%e1%9e%bc%e1%9e%93',
health: '%e1%9e%9f%e1%9e%bb%e1%9e%81%e1%9e%97%e1%9e%b6%e1%9e%96%e1%9e%91%e1%9e%bc%e1%9e%91%e1%9f%85',
diabetes: '%e1%9e%87%e1%9f%86%e1%9e%84%e1%9e%ba%e1%9e%91%e1%9e%b9%e1%9e%80%e1%9e%93%e1%9f%84%e1%9e%98%e1%9e%95%e1%9f%92%e1%9e%a2%e1%9f%82%e1%9e%98',
womensHealth: '%e1%9e%9f%e1%9e%bb%e1%9e%81%e1%9e%97%e1%9e%b6%e1%9e%96%e1%9e%9f%e1%9f%92%e1%9e%8f%e1%9f%92%e1%9e%9a%e1%9e%b8',
drug: '%e1%9e%b1%e1%9e%9f%e1%9e%90%e1%9e%93%e1%9e%b7%e1%9e%84%e1%9e%a2%e1%9e%b6%e1%9e%a0%e1%9e%b6%e1%9e%9a%e1%9e%94%e1%9f%86%e1%9e%94%e1%9f%89%e1%9e%93',
sexualWellness: '',
healthyEating: '',
skinHealth: '',
},
[LOCALE.PhilippinesEnglish]: {
pregnancy: 'pregnancy',
parenting: 'parenting',
health: 'health',
diabetes: 'diabetes',
womensHealth: 'womens-health',
drug: 'drugs-supplements',
sexualWellness: '',
healthyEating: '',
skinHealth: '',
},
[LOCALE.PhilippinesTagalog]: {
pregnancy: 'pagbubuntis',
parenting: 'pagiging-magulang',
health: 'kalusugan',
diabetes: 'diabetes-fil',
womensHealth: 'kalusugan-kababaihan',
drug: 'drugs-at-supplements',
sexualWellness: '',
healthyEating: '',
skinHealth: '',
},
[LOCALE.Thailand]: {
pregnancy: '%e0%b8%81%e0%b8%b2%e0%b8%a3%e0%b8%95%e0%b8%b1%e0%b9%89%e0%b8%87%e0%b8%84%e0%b8%a3%e0%b8%a3%e0%b8%a0%e0%b9%8c',
parenting: '%e0%b8%9e%e0%b9%88%e0%b8%ad%e0%b9%81%e0%b8%a1%e0%b9%88%e0%b9%80%e0%b8%a5%e0%b8%b5%e0%b9%89%e0%b8%a2%e0%b8%87%e0%b8%a5%e0%b8%b9%e0%b8%81',
health: '%e0%b8%aa%e0%b8%b8%e0%b8%82%e0%b8%a0%e0%b8%b2%e0%b8%9e',
diabetes: '%e0%b9%82%e0%b8%a3%e0%b8%84%e0%b9%80%e0%b8%9a%e0%b8%b2%e0%b8%ab%e0%b8%a7%e0%b8%b2%e0%b8%99',
womensHealth: '%e0%b8%aa%e0%b8%b8%e0%b8%82%e0%b8%a0%e0%b8%b2%e0%b8%9e%e0%b8%ab%e0%b8%8d%e0%b8%b4%e0%b8%87',
drug: '%e0%b8%a2%e0%b8%b2%e0%b9%81%e0%b8%a5%e0%b8%b0%e0%b8%ad%e0%b8%b2%e0%b8%ab%e0%b8%b2%e0%b8%a3%e0%b9%80%e0%b8%aa%e0%b8%a3%e0%b8%b4%e0%b8%a1',
sexualWellness: '',
healthyEating: '',
skinHealth: '',
},
[LOCALE.Myanmar]: {
pregnancy: 'pregnancy',
parenting: 'parenting',
health: 'health',
diabetes: 'diabetes',
womensHealth: 'womens-health',
drug: 'drugs-az',
sexualWellness: '',
healthyEating: '',
skinHealth: '',
},
[LOCALE.Taiwan]: {
pregnancy: 'pregnancy',
parenting: 'parenting',
health: 'health',
diabetes: 'diabetes',
womensHealth: 'womens-health',
drug: 'drugs-supplement',
sexualWellness: '',
healthyEating: '',
skinHealth: '',
},
// TODO: Update label
[LOCALE.Singapore]: {
pregnancy: 'pregnancy',
parenting: 'parenting',
health: 'health',
diabetes: 'diabetes',
womensHealth: 'womens-health',
drug: 'drugs-supplement',
sexualWellness: '',
healthyEating: '',
skinHealth: '',
},
[LOCALE.India]: {
pregnancy: 'pregnancy',
parenting: 'parenting',
health: 'health',
diabetes: 'diabetes',
womensHealth: 'womens-health',
diabetesEn: 'en-diabetes',
drug: 'dawaai',
sexualWellness: '',
healthyEating: '',
skinHealth: '',
},
};
function getCategoryFromURL(url, categories) {
const urlParts = url.split('/');
for (const key in categories) {
const keyword = categories[key];
if (urlParts.includes(keyword)) {
return key;
}
}
return null; // Return null if no matching key is found
}
const useCategory = (asPath) => {
const { locale } = useTranslations();
const isPregnancy = useMemo(() => decodeURI(asPath.replace(/\//gi, '')).includes(PREGNANCY_SLUG[locale]), []);
const isParenting = useMemo(() => {
var _a;
return decodeURI(asPath.replace(/\//gi, '')).includes((_a = MAPPED_CATEGORY_SLUGS[locale]) === null || _a === void 0 ? void 0 : _a.parenting);
}, []);
const type = useMemo(() => {
return getCategoryFromURL(asPath, MAPPED_CATEGORY_SLUGS[locale]);
}, [asPath, locale]);
return { isPregnancy, isParenting, type };
};
function usePhoneValidator() {
const [isReady, setReady] = useState(false);
useEffect(() => {
const loadUtils = () => __awaiter(this, void 0, void 0, function* () {
if (typeof window === 'undefined')
return;
if (!window.intlTelInputUtils) {
try {
yield import('intl-tel-input/build/js/utils');
}
catch (e) {
console.error('Failed to load intl-tel-input utils', e);
return;
}
}
const utils = window.intlTelInputUtils;
if (utils && typeof utils.isValidNumber === 'function') {
setReady(true);
}
});
loadUtils();
}, []);
const validator = useMemo(() => {
if (!isReady)
return null;
return (value, countryCode) => {
try {
const utils = window.intlTelInputUtils;
if (!utils || typeof utils.isValidNumber !== 'function')
return true;
return utils.isValidNumber(value, countryCode);
}
catch (e) {
console.warn('Phone validation error:', e);
return true;
}
};
}, [isReady]);
return validator;
}
export { MAPPED_CATEGORY_SLUGS as M, PREGNANCY_SLUG as P, usePrevious as a, useRafState as b, useCategory as c, usePhoneValidator as d, useFbDataLayer as u };