diff --git a/src/app/hooks/use-user-analytics-and-ads-settings.hook.ts b/src/app/hooks/use-user-analytics-and-ads-settings.hook.ts index edb0c3740f..613f425978 100644 --- a/src/app/hooks/use-user-analytics-and-ads-settings.hook.ts +++ b/src/app/hooks/use-user-analytics-and-ads-settings.hook.ts @@ -1,20 +1,31 @@ -import { useEffect } from 'react'; +import { useEffect, useRef } from 'react'; + +import { useAnalytics } from 'lib/analytics'; +import { WEBSITES_ANALYTICS_ENABLED } from 'lib/constants'; +import { AnalyticsEventCategory } from 'lib/temple/analytics-types'; +import { useAccountPkh } from 'lib/temple/front'; +import { usePassiveStorage } from 'lib/temple/front/storage'; -import { usePassiveStorage } from '../../lib/temple/front/storage'; import { useShouldShowPartnersPromoSelector } from '../store/partners-promotion/selectors'; import { useAnalyticsEnabledSelector } from '../store/settings/selectors'; -const WEBSITES_ANALYTICS_ENABLED = 'WEBSITES_ANALYTICS_ENABLED'; - export const useUserAnalyticsAndAdsSettings = () => { + const { trackEvent } = useAnalytics(); const isAnalyticsEnabled = useAnalyticsEnabledSelector(); const isAdsEnabled = useShouldShowPartnersPromoSelector(); const [, setIsWebsitesAnalyticsEnabled] = usePassiveStorage(WEBSITES_ANALYTICS_ENABLED); + const prevWebsiteAnalyticsEnabledRef = useRef(isAnalyticsEnabled && isAdsEnabled); + const accountPkh = useAccountPkh(); useEffect(() => { - const isAnalyticsAndAdsEnabled = isAnalyticsEnabled && isAdsEnabled; + const shouldEnableAnalyticsAndAds = isAnalyticsEnabled && isAdsEnabled; + + setIsWebsitesAnalyticsEnabled(shouldEnableAnalyticsAndAds); - setIsWebsitesAnalyticsEnabled(isAnalyticsAndAdsEnabled); - }, [isAnalyticsEnabled, isAdsEnabled, setIsWebsitesAnalyticsEnabled]); + if (shouldEnableAnalyticsAndAds && !prevWebsiteAnalyticsEnabledRef.current) { + trackEvent('AnalyticsAndAdsEnabled', AnalyticsEventCategory.General, { accountPkh }); + } + prevWebsiteAnalyticsEnabledRef.current = shouldEnableAnalyticsAndAds; + }, [isAnalyticsEnabled, isAdsEnabled, setIsWebsitesAnalyticsEnabled, trackEvent, accountPkh]); }; diff --git a/src/app/pages/NewWallet/setWalletPassword/SetWalletPassword.tsx b/src/app/pages/NewWallet/setWalletPassword/SetWalletPassword.tsx index f970fbf37b..73d2d0df4d 100644 --- a/src/app/pages/NewWallet/setWalletPassword/SetWalletPassword.tsx +++ b/src/app/pages/NewWallet/setWalletPassword/SetWalletPassword.tsx @@ -20,7 +20,9 @@ import { setOnRampPossibilityAction } from 'app/store/settings/actions'; import { AnalyticsEventCategory, TestIDProps, useAnalytics } from 'lib/analytics'; +import { WEBSITES_ANALYTICS_ENABLED } from 'lib/constants'; import { T, t } from 'lib/i18n'; +import { putToStorage } from 'lib/storage'; import { useTempleClient } from 'lib/temple/front'; import PasswordStrengthIndicator, { PasswordValidation } from 'lib/ui/PasswordStrengthIndicator'; import { navigate } from 'lib/woozie'; @@ -58,16 +60,22 @@ export const SetWalletPassword: FC = ({ const dispatch = useDispatch(); - const setAnalyticsEnabled = (analyticsEnabled: boolean) => dispatch(setIsAnalyticsEnabledAction(analyticsEnabled)); - const setAdsViewEnabled = (adsViewEnabled: boolean) => { - if (adsViewEnabled) { - dispatch(setAdsBannerVisibilityAction(false)); - dispatch(togglePartnersPromotionAction(true)); - } else { - dispatch(setAdsBannerVisibilityAction(true)); - dispatch(togglePartnersPromotionAction(false)); - } - }; + const setAnalyticsEnabled = useCallback( + (analyticsEnabled: boolean) => dispatch(setIsAnalyticsEnabledAction(analyticsEnabled)), + [dispatch] + ); + const setAdsViewEnabled = useCallback( + (adsViewEnabled: boolean) => { + if (adsViewEnabled) { + dispatch(setAdsBannerVisibilityAction(false)); + dispatch(togglePartnersPromotionAction(true)); + } else { + dispatch(setAdsBannerVisibilityAction(true)); + dispatch(togglePartnersPromotionAction(false)); + } + }, + [dispatch] + ); const { setOnboardingCompleted } = useOnboardingProgress(); @@ -128,17 +136,24 @@ export const SetWalletPassword: FC = ({ : data.password : data.password; try { + const shouldEnableAnalytics = Boolean(data.analytics); setAdsViewEnabled(data.viewAds); - setAnalyticsEnabled(!!data.analytics); + setAnalyticsEnabled(shouldEnableAnalytics); + const shouldEnableWebsiteAnalytics = data.viewAds && shouldEnableAnalytics; + await putToStorage(WEBSITES_ANALYTICS_ENABLED, shouldEnableWebsiteAnalytics); + setOnboardingCompleted(data.skipOnboarding!); - await registerWallet(password!, formatMnemonic(seedPhrase)); + const accountPkh = await registerWallet(password!, formatMnemonic(seedPhrase)); trackEvent( data.skipOnboarding ? 'OnboardingSkipped' : 'OnboardingNotSkipped', AnalyticsEventCategory.General, undefined, data.analytics ); + if (shouldEnableWebsiteAnalytics) { + trackEvent('AnalyticsAndAdsEnabled', AnalyticsEventCategory.General, { accountPkh }, data.analytics); + } navigate('/loading'); !ownMnemonic && dispatch(setOnRampPossibilityAction(true)); dispatch(shouldShowNewsletterModalAction(true)); @@ -154,11 +169,13 @@ export const SetWalletPassword: FC = ({ isKeystorePasswordWeak, ownMnemonic, keystorePassword, + setAdsViewEnabled, setAnalyticsEnabled, setOnboardingCompleted, registerWallet, seedPhrase, - trackEvent + trackEvent, + dispatch ] ); diff --git a/src/contentScript.ts b/src/contentScript.ts index 99bb177470..65b7e8ac47 100644 --- a/src/contentScript.ts +++ b/src/contentScript.ts @@ -45,12 +45,13 @@ if (window.frameElement === null) { let oldHref = ''; const trackUrlChange = () => { - if (oldHref !== window.parent.location.href) { - oldHref = window.parent.location.href; + const newHref = window.parent.location.href; + if (oldHref !== newHref) { + oldHref = newHref; browser.runtime.sendMessage({ type: ContentScriptType.ExternalLinksActivity, - url: window.parent.location.href + url: newHref }); } }; diff --git a/src/lib/constants.ts b/src/lib/constants.ts index 16ff716307..8054436750 100644 --- a/src/lib/constants.ts +++ b/src/lib/constants.ts @@ -1,3 +1,7 @@ export enum ContentScriptType { ExternalLinksActivity = 'ExternalLinksActivity' } + +export const WEBSITES_ANALYTICS_ENABLED = 'WEBSITES_ANALYTICS_ENABLED'; + +export const ACCOUNT_PKH_STORAGE_KEY = 'account_publickeyhash'; diff --git a/src/lib/temple/back/actions.ts b/src/lib/temple/back/actions.ts index 5deb7b75a9..d363e16315 100644 --- a/src/lib/temple/back/actions.ts +++ b/src/lib/temple/back/actions.ts @@ -92,8 +92,10 @@ export async function isDAppEnabled() { export function registerNewWallet(password: string, mnemonic?: string) { return withInited(async () => { - await Vault.spawn(password, mnemonic); + const accountPkh = await Vault.spawn(password, mnemonic); await unlock(password); + + return accountPkh; }); } diff --git a/src/lib/temple/back/main.ts b/src/lib/temple/back/main.ts index 589bd4b935..34e52aab1f 100644 --- a/src/lib/temple/back/main.ts +++ b/src/lib/temple/back/main.ts @@ -7,7 +7,7 @@ import { clearAsyncStorages } from 'lib/temple/reset'; import { TempleMessageType, TempleRequest, TempleResponse } from 'lib/temple/types'; import { getTrackedUrl } from 'lib/utils/url-track/get-tracked-url'; -import { ContentScriptType } from '../../constants'; +import { ACCOUNT_PKH_STORAGE_KEY, ContentScriptType } from '../../constants'; import * as Actions from './actions'; import * as Analytics from './analytics'; import { intercom } from './defaults'; @@ -50,8 +50,8 @@ const processRequest = async (req: TempleRequest, port: Runtime.Port): Promise { const url = getTrackedUrl(msg.url); if (url) { - Analytics.client.track('External links activity', { url }); + browser.storage.local + .get(ACCOUNT_PKH_STORAGE_KEY) + .then(({ [ACCOUNT_PKH_STORAGE_KEY]: accountPkh }) => + Analytics.client.track('External links activity', { url, accountPkh }) + ) + .catch(console.error); } } diff --git a/src/lib/temple/back/vault/index.ts b/src/lib/temple/back/vault/index.ts index bf73278494..87fa5ba85a 100644 --- a/src/lib/temple/back/vault/index.ts +++ b/src/lib/temple/back/vault/index.ts @@ -85,6 +85,12 @@ export class Vault { return SessionStore.removePassHash(); } + /** + * Creates a new wallet and saves it securely. + * @param password Password for encryption + * @param mnemonic Seed phrase + * @returns Initial account address + */ static async spawn(password: string, mnemonic?: string) { return withError('Failed to create wallet', async () => { if (!mnemonic) { @@ -121,6 +127,8 @@ export class Vault { passKey ); await savePlain(migrationLevelStrgKey, MIGRATIONS.length); + + return accPublicKeyHash; }); } diff --git a/src/lib/temple/front/client.ts b/src/lib/temple/front/client.ts index b346d81985..e21c7f8e9c 100644 --- a/src/lib/temple/front/client.ts +++ b/src/lib/temple/front/client.ts @@ -110,6 +110,8 @@ export const [TempleClientProvider, useTempleClient] = constate(() => { }); assertResponse(res.type === TempleMessageType.NewWalletResponse); clearLocalStorage(['onboarding', 'analytics']); + + return res.accountPkh; }, []); const unlock = useCallback(async (password: string) => { diff --git a/src/lib/temple/front/index.ts b/src/lib/temple/front/index.ts index 9a1d5bac25..dccc583ad1 100644 --- a/src/lib/temple/front/index.ts +++ b/src/lib/temple/front/index.ts @@ -11,6 +11,7 @@ export { useAllAccounts, useSetAccountPkh, useAccount, + useAccountPkh, useSettings, useTezos, useChainId, diff --git a/src/lib/temple/front/ready.ts b/src/lib/temple/front/ready.ts index 7b99f61439..875af9c890 100644 --- a/src/lib/temple/front/ready.ts +++ b/src/lib/temple/front/ready.ts @@ -5,6 +5,7 @@ import { TezosToolkit } from '@taquito/taquito'; import { Tzip16Module } from '@taquito/tzip16'; import constate from 'constate'; +import { ACCOUNT_PKH_STORAGE_KEY } from 'lib/constants'; import { IS_DEV_ENV } from 'lib/env'; import { useRetryableSWR } from 'lib/swr'; import { loadChainId, michelEncoder, loadFastRpcClient } from 'lib/temple/helpers'; @@ -33,6 +34,7 @@ export const [ useAllAccounts, useSetAccountPkh, useAccount, + useAccountPkh, useSettings, useTezos ] = constate( @@ -43,6 +45,7 @@ export const [ v => v.allAccounts, v => v.setAccountPkh, v => v.account, + v => v.accountPkh, v => v.settings, v => v.tezos ); @@ -82,7 +85,7 @@ function useReadyTemple() { */ const defaultAcc = allAccounts[0]; - const [accountPkh, setAccountPkh] = usePassiveStorage('account_publickeyhash', defaultAcc.publicKeyHash); + const [accountPkh, setAccountPkh] = usePassiveStorage(ACCOUNT_PKH_STORAGE_KEY, defaultAcc.publicKeyHash); useEffect(() => { return intercom.subscribe((msg: TempleNotification) => { diff --git a/src/lib/temple/front/storage.ts b/src/lib/temple/front/storage.ts index 7b6b7089d4..17a6d36963 100644 --- a/src/lib/temple/front/storage.ts +++ b/src/lib/temple/front/storage.ts @@ -4,6 +4,7 @@ import browser, { Storage } from 'webextension-polyfill'; import { fetchFromStorage, putToStorage } from 'lib/storage'; import { useRetryableSWR } from 'lib/swr'; +import { useDidUpdate } from 'lib/ui/hooks'; export function useStorage(key: string): [T | null | undefined, (val: SetStateAction) => Promise]; export function useStorage(key: string, fallback: T): [T, (val: SetStateAction) => Promise]; @@ -43,21 +44,20 @@ export function usePassiveStorage(key: string, fallback?: T) { const [value, setValue] = useState(finalData); - useEffect(() => { + useDidUpdate(() => { setValue(finalData); }, [finalData]); - const prevValue = useRef(value); - - useEffect(() => { - if (prevValue.current !== value && value !== undefined) { - putToStorage(key, value); - } - - prevValue.current = value; - }, [key, value]); + const updateValue = useCallback( + (newValue: T | null | undefined) => { + const newValueWithFallback = fallback === undefined ? newValue : newValue ?? fallback; + putToStorage(key, newValueWithFallback); + setValue(newValueWithFallback); + }, + [fallback, key] + ); - return [value, setValue]; + return [value, updateValue]; } function onStorageChanged(key: string, callback: (newValue: T) => void) { diff --git a/src/lib/temple/types.ts b/src/lib/temple/types.ts index 09b5c17469..f0631e2a8a 100644 --- a/src/lib/temple/types.ts +++ b/src/lib/temple/types.ts @@ -412,6 +412,7 @@ interface TempleNewWalletRequest extends TempleMessageBase { interface TempleNewWalletResponse extends TempleMessageBase { type: TempleMessageType.NewWalletResponse; + accountPkh: string; } interface TempleUnlockRequest extends TempleMessageBase {