Handle URL params
This commit is contained in:
+3
-2
@@ -105,6 +105,7 @@
|
|||||||
"expo-image": "~1.10.3",
|
"expo-image": "~1.10.3",
|
||||||
"expo-image-manipulator": "^11.8.0",
|
"expo-image-manipulator": "^11.8.0",
|
||||||
"expo-image-picker": "~14.7.1",
|
"expo-image-picker": "~14.7.1",
|
||||||
|
"expo-linking": "^6.2.2",
|
||||||
"expo-localization": "~14.8.2",
|
"expo-localization": "~14.8.2",
|
||||||
"expo-media-library": "~15.9.1",
|
"expo-media-library": "~15.9.1",
|
||||||
"expo-notifications": "~0.27.3",
|
"expo-notifications": "~0.27.3",
|
||||||
@@ -161,6 +162,7 @@
|
|||||||
"react-native-safe-area-context": "4.8.2",
|
"react-native-safe-area-context": "4.8.2",
|
||||||
"react-native-screens": "~3.29.0",
|
"react-native-screens": "~3.29.0",
|
||||||
"react-native-svg": "14.1.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-url-polyfill": "^1.3.0",
|
||||||
"react-native-uuid": "^2.0.1",
|
"react-native-uuid": "^2.0.1",
|
||||||
"react-native-version-number": "^0.3.6",
|
"react-native-version-number": "^0.3.6",
|
||||||
@@ -175,8 +177,7 @@
|
|||||||
"tlds": "^1.234.0",
|
"tlds": "^1.234.0",
|
||||||
"use-deep-compare": "^1.1.0",
|
"use-deep-compare": "^1.1.0",
|
||||||
"zeego": "^1.6.2",
|
"zeego": "^1.6.2",
|
||||||
"zod": "^3.20.2",
|
"zod": "^3.20.2"
|
||||||
"react-native-ui-text-view": "link:./modules/react-native-ui-text-view"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@atproto/dev-env": "^0.2.28",
|
"@atproto/dev-env": "^0.2.28",
|
||||||
|
|||||||
@@ -45,6 +45,7 @@ import {Splash} from '#/Splash'
|
|||||||
import {Provider as PortalProvider} from '#/components/Portal'
|
import {Provider as PortalProvider} from '#/components/Portal'
|
||||||
import {msg} from '@lingui/macro'
|
import {msg} from '@lingui/macro'
|
||||||
import {useLingui} from '@lingui/react'
|
import {useLingui} from '@lingui/react'
|
||||||
|
import {useUTMSetup} from '#/lib/utm'
|
||||||
|
|
||||||
SplashScreen.preventAutoHideAsync()
|
SplashScreen.preventAutoHideAsync()
|
||||||
|
|
||||||
@@ -54,6 +55,7 @@ function InnerApp() {
|
|||||||
const {resumeSession} = useSessionApi()
|
const {resumeSession} = useSessionApi()
|
||||||
const theme = useColorModeTheme(colorMode)
|
const theme = useColorModeTheme(colorMode)
|
||||||
const {_} = useLingui()
|
const {_} = useLingui()
|
||||||
|
useUTMSetup()
|
||||||
|
|
||||||
// init
|
// init
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|||||||
@@ -32,12 +32,14 @@ import {
|
|||||||
import {Provider as UnreadNotifsProvider} from 'state/queries/notifications/unread'
|
import {Provider as UnreadNotifsProvider} from 'state/queries/notifications/unread'
|
||||||
import * as persisted from '#/state/persisted'
|
import * as persisted from '#/state/persisted'
|
||||||
import {Provider as PortalProvider} from '#/components/Portal'
|
import {Provider as PortalProvider} from '#/components/Portal'
|
||||||
|
import {useUTMSetup} from '#/lib/utm'
|
||||||
|
|
||||||
function InnerApp() {
|
function InnerApp() {
|
||||||
const {isInitialLoad, currentAccount} = useSession()
|
const {isInitialLoad, currentAccount} = useSession()
|
||||||
const {resumeSession} = useSessionApi()
|
const {resumeSession} = useSessionApi()
|
||||||
const colorMode = useColorMode()
|
const colorMode = useColorMode()
|
||||||
const theme = useColorModeTheme(colorMode)
|
const theme = useColorModeTheme(colorMode)
|
||||||
|
useUTMSetup()
|
||||||
|
|
||||||
// init
|
// init
|
||||||
useEffect(() => {
|
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"
|
resolved "https://registry.yarnpkg.com/expo-keep-awake/-/expo-keep-awake-12.8.1.tgz#3c8df9d86c265741b5e7bdd36965aa0c6fc17df0"
|
||||||
integrity sha512-P/VZFV02Rzgj13skMwH+ceGOGZSEdaUu5n7pCS3wThh2LppZjPJ7sBxUwyzeLa3DXEVUtwLZi+BiQ91wPwy9Gg==
|
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:
|
expo-localization@~14.8.2:
|
||||||
version "14.8.2"
|
version "14.8.2"
|
||||||
resolved "https://registry.yarnpkg.com/expo-localization/-/expo-localization-14.8.2.tgz#e0bbed2293265834d21a1c58d3a5f8d265bd04ae"
|
resolved "https://registry.yarnpkg.com/expo-localization/-/expo-localization-14.8.2.tgz#e0bbed2293265834d21a1c58d3a5f8d265bd04ae"
|
||||||
|
|||||||
Reference in New Issue
Block a user