Handle URL params
This commit is contained in:
+3
-2
@@ -105,6 +105,7 @@
|
||||
"expo-image": "~1.10.3",
|
||||
"expo-image-manipulator": "^11.8.0",
|
||||
"expo-image-picker": "~14.7.1",
|
||||
"expo-linking": "^6.2.2",
|
||||
"expo-localization": "~14.8.2",
|
||||
"expo-media-library": "~15.9.1",
|
||||
"expo-notifications": "~0.27.3",
|
||||
@@ -161,6 +162,7 @@
|
||||
"react-native-safe-area-context": "4.8.2",
|
||||
"react-native-screens": "~3.29.0",
|
||||
"react-native-svg": "14.1.0",
|
||||
"react-native-ui-text-view": "link:./modules/react-native-ui-text-view",
|
||||
"react-native-url-polyfill": "^1.3.0",
|
||||
"react-native-uuid": "^2.0.1",
|
||||
"react-native-version-number": "^0.3.6",
|
||||
@@ -175,8 +177,7 @@
|
||||
"tlds": "^1.234.0",
|
||||
"use-deep-compare": "^1.1.0",
|
||||
"zeego": "^1.6.2",
|
||||
"zod": "^3.20.2",
|
||||
"react-native-ui-text-view": "link:./modules/react-native-ui-text-view"
|
||||
"zod": "^3.20.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@atproto/dev-env": "^0.2.28",
|
||||
|
||||
@@ -45,6 +45,7 @@ import {Splash} from '#/Splash'
|
||||
import {Provider as PortalProvider} from '#/components/Portal'
|
||||
import {msg} from '@lingui/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
import {useUTMSetup} from '#/lib/utm'
|
||||
|
||||
SplashScreen.preventAutoHideAsync()
|
||||
|
||||
@@ -54,6 +55,7 @@ function InnerApp() {
|
||||
const {resumeSession} = useSessionApi()
|
||||
const theme = useColorModeTheme(colorMode)
|
||||
const {_} = useLingui()
|
||||
useUTMSetup()
|
||||
|
||||
// init
|
||||
useEffect(() => {
|
||||
|
||||
@@ -32,12 +32,14 @@ import {
|
||||
import {Provider as UnreadNotifsProvider} from 'state/queries/notifications/unread'
|
||||
import * as persisted from '#/state/persisted'
|
||||
import {Provider as PortalProvider} from '#/components/Portal'
|
||||
import {useUTMSetup} from '#/lib/utm'
|
||||
|
||||
function InnerApp() {
|
||||
const {isInitialLoad, currentAccount} = useSession()
|
||||
const {resumeSession} = useSessionApi()
|
||||
const colorMode = useColorMode()
|
||||
const theme = useColorModeTheme(colorMode)
|
||||
useUTMSetup()
|
||||
|
||||
// init
|
||||
useEffect(() => {
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
import React from 'react'
|
||||
import {nanoid} from 'nanoid/non-secure'
|
||||
import * as Linking from 'expo-linking'
|
||||
|
||||
import {logger} from '#/logger'
|
||||
import {load, save} from '#/lib/storage'
|
||||
|
||||
const STORAGE_KEY = 'BSKY_UTM_PARAMS'
|
||||
|
||||
let PARAMS: Record<string, any> = {}
|
||||
|
||||
async function loadParams() {
|
||||
PARAMS = (await load(STORAGE_KEY)) || {}
|
||||
}
|
||||
|
||||
async function upsertParams(params: Record<string, any>) {
|
||||
await loadParams() // make sure we're ready
|
||||
|
||||
for (const key in params) {
|
||||
const value = params[key]
|
||||
|
||||
// only overwrite if we have a new value
|
||||
if (value !== null && value !== undefined) {
|
||||
PARAMS[key] = value
|
||||
}
|
||||
}
|
||||
|
||||
await save(STORAGE_KEY, PARAMS)
|
||||
|
||||
logger.debug('utm: updated', {params: PARAMS})
|
||||
}
|
||||
|
||||
export function handleIncomingURL(url: string) {
|
||||
const {searchParams} = new URL(url, 'http://throwaway.com')
|
||||
const params = Object.fromEntries(
|
||||
Array.from(searchParams.entries()).filter(([key]) =>
|
||||
key.startsWith('utm_'),
|
||||
),
|
||||
)
|
||||
|
||||
upsertParams(params)
|
||||
}
|
||||
|
||||
export function getParams() {
|
||||
return PARAMS
|
||||
}
|
||||
|
||||
const UTM_TO_MP: {[key: string]: string} = {
|
||||
utm_id: 'cid',
|
||||
utm_source: 'cs',
|
||||
utm_medium: 'cm',
|
||||
utm_campaign: 'cn',
|
||||
utm_term: 'ck',
|
||||
utm_content: 'cc',
|
||||
// unsure how to map these
|
||||
// utm_source_platform: '',
|
||||
// utm_creative_format: '',
|
||||
// utm_marketing_tactic: '',
|
||||
}
|
||||
|
||||
export function getUTMParamsAsMeasurementProtocol() {
|
||||
return Object.entries(PARAMS).reduce(
|
||||
(acc, [key, value]) => {
|
||||
acc[UTM_TO_MP[key]] = value
|
||||
return acc
|
||||
},
|
||||
{
|
||||
v: 1,
|
||||
tid: 'UA-xxx', // TODO
|
||||
t: 'event',
|
||||
} as Record<string, any>,
|
||||
)
|
||||
}
|
||||
|
||||
export function emitEvent() {
|
||||
const params = getUTMParamsAsMeasurementProtocol()
|
||||
// TODO events?
|
||||
const payload = new URLSearchParams(params).toString()
|
||||
const url = new URL('/collect', 'https://www.google-analytics.com')
|
||||
url.searchParams.set('payload_data', encodeURIComponent(payload))
|
||||
url.searchParams.set('z', Date.now().toString() + '-' + nanoid()) // TODO
|
||||
|
||||
fetch(url.toString()).catch(() => {})
|
||||
}
|
||||
|
||||
export function useUTMSetup() {
|
||||
const url = Linking.useURL()
|
||||
React.useEffect(() => {
|
||||
if (url) handleIncomingURL(url)
|
||||
}, [url])
|
||||
}
|
||||
@@ -11725,6 +11725,14 @@ expo-keep-awake@~12.8.1:
|
||||
resolved "https://registry.yarnpkg.com/expo-keep-awake/-/expo-keep-awake-12.8.1.tgz#3c8df9d86c265741b5e7bdd36965aa0c6fc17df0"
|
||||
integrity sha512-P/VZFV02Rzgj13skMwH+ceGOGZSEdaUu5n7pCS3wThh2LppZjPJ7sBxUwyzeLa3DXEVUtwLZi+BiQ91wPwy9Gg==
|
||||
|
||||
expo-linking@^6.2.2:
|
||||
version "6.2.2"
|
||||
resolved "https://registry.yarnpkg.com/expo-linking/-/expo-linking-6.2.2.tgz#b7e148068ae49fd9ad814428c16fdf7a236e8aca"
|
||||
integrity sha512-FEe6lP4f7xFT/vjoHRG+tt6EPVtkEGaWNK1smpaUevmNdyCJKqW0PDB8o8sfG6y7fly8ULe8qg3HhKh5J7aqUQ==
|
||||
dependencies:
|
||||
expo-constants "~15.4.3"
|
||||
invariant "^2.2.4"
|
||||
|
||||
expo-localization@~14.8.2:
|
||||
version "14.8.2"
|
||||
resolved "https://registry.yarnpkg.com/expo-localization/-/expo-localization-14.8.2.tgz#e0bbed2293265834d21a1c58d3a5f8d265bd04ae"
|
||||
|
||||
Reference in New Issue
Block a user