Clean up old code
This commit is contained in:
@@ -6,7 +6,6 @@ import {
|
|||||||
useMemo,
|
useMemo,
|
||||||
} from 'react'
|
} from 'react'
|
||||||
|
|
||||||
import {updateBaseMetadata} from '#/logger/metadata'
|
|
||||||
import {useSyncDeviceGeolocationOnStartup} from '#/geolocation/device'
|
import {useSyncDeviceGeolocationOnStartup} from '#/geolocation/device'
|
||||||
import {useGeolocationServiceResponse} from '#/geolocation/service'
|
import {useGeolocationServiceResponse} from '#/geolocation/service'
|
||||||
import {type Geolocation} from '#/geolocation/types'
|
import {type Geolocation} from '#/geolocation/types'
|
||||||
@@ -54,9 +53,6 @@ export function Provider({children}: {children: ReactNode}) {
|
|||||||
* Needs to be available for the data prefetching we do on boot.
|
* Needs to be available for the data prefetching we do on boot.
|
||||||
*/
|
*/
|
||||||
device.set(['mergedGeolocation'], geolocation)
|
device.set(['mergedGeolocation'], geolocation)
|
||||||
updateBaseMetadata({
|
|
||||||
country: geolocation.countryCode || 'unknown',
|
|
||||||
})
|
|
||||||
}, [geolocation])
|
}, [geolocation])
|
||||||
|
|
||||||
useSyncDeviceGeolocationOnStartup(setDeviceGeolocation)
|
useSyncDeviceGeolocationOnStartup(setDeviceGeolocation)
|
||||||
|
|||||||
@@ -1,93 +0,0 @@
|
|||||||
import {useCallback} from 'react'
|
|
||||||
import {GrowthBook} from '@growthbook/growthbook-react'
|
|
||||||
|
|
||||||
import {type Metadata} from '#/logger/metadata'
|
|
||||||
import {metrics} from '#/logger/metrics'
|
|
||||||
import * as env from '#/env'
|
|
||||||
|
|
||||||
const debugEnabled = env.IS_DEV && true
|
|
||||||
const debug = (message: string, attributes?: Record<string, any>) => {
|
|
||||||
if (debugEnabled) {
|
|
||||||
console.debug(`(growthbook) ${message}`, attributes || {})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const TIMEOUT_INIT = 500 // TODO should base on p99 or something
|
|
||||||
const TIMEOUT_PREFER_LOW_LATENCY = 250
|
|
||||||
const TIMEOUT_PREFER_FRESH_GATES = 1500
|
|
||||||
|
|
||||||
/**
|
|
||||||
* We vary the amount of time we wait for GrowthBook to fetch feature
|
|
||||||
* gates based on the strategy specified.
|
|
||||||
*/
|
|
||||||
type FeatureFetchStrategy = 'prefer-low-latency' | 'prefer-fresh-gates'
|
|
||||||
|
|
||||||
const gb = new GrowthBook({
|
|
||||||
apiHost: env.GROWTHBOOK_API_HOST,
|
|
||||||
clientKey: env.GROWTHBOOK_CLIENT_KEY,
|
|
||||||
trackingCallback: (experiment, result) => {
|
|
||||||
metrics.track('experiment:viewed', {
|
|
||||||
experimentId: experiment.key,
|
|
||||||
variationId: result.key,
|
|
||||||
})
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* Initial values are set on startup in `#/logger/metdata/index.ts`
|
|
||||||
*/
|
|
||||||
attributes: {},
|
|
||||||
})
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initializer promise that must be awaited before using the GrowthBook
|
|
||||||
* instance or rendering the `Provider`. Note: this may not be fully
|
|
||||||
* initialized if it takes longer than `TIMEOUT_INIT` to initialize. In that
|
|
||||||
* case, we may see a flash of uncustomized content until the initialization
|
|
||||||
* completes.
|
|
||||||
*/
|
|
||||||
export const initializer = new Promise<void>(async y => {
|
|
||||||
await gb.init({timeout: TIMEOUT_INIT})
|
|
||||||
y()
|
|
||||||
})
|
|
||||||
|
|
||||||
export function getGrowthBook() {
|
|
||||||
return gb
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Converts our metadata into GrowthBook attributes and sets them.
|
|
||||||
*/
|
|
||||||
export function setGrowthBookAttributes({
|
|
||||||
deviceId: device_id,
|
|
||||||
sessionId: session_id,
|
|
||||||
...metadata
|
|
||||||
}: Metadata) {
|
|
||||||
gb.setAttributes({
|
|
||||||
device_id, // GrowthBook special field
|
|
||||||
session_id, // GrowthBook special field
|
|
||||||
user_id: metadata.did, // GrowthBook special field
|
|
||||||
...metadata,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Refresh feature gates from GrowthBook. Updates attributes based on the
|
|
||||||
* provided account, if any.
|
|
||||||
*/
|
|
||||||
export async function refresh({strategy}: {strategy: FeatureFetchStrategy}) {
|
|
||||||
debug(`refresh`, {strategy})
|
|
||||||
await gb.refreshFeatures({
|
|
||||||
timeout:
|
|
||||||
strategy === 'prefer-low-latency'
|
|
||||||
? TIMEOUT_PREFER_LOW_LATENCY
|
|
||||||
: TIMEOUT_PREFER_FRESH_GATES,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Hook to check if a feature gate is enabled
|
|
||||||
*/
|
|
||||||
export function useGate() {
|
|
||||||
return useCallback((gate: string): boolean => {
|
|
||||||
return gb.isOn(gate)
|
|
||||||
}, [])
|
|
||||||
}
|
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
import {nanoid} from 'nanoid/non-secure'
|
import {nanoid} from 'nanoid/non-secure'
|
||||||
|
|
||||||
import {add} from '#/logger/logDump'
|
import {add} from '#/logger/logDump'
|
||||||
import {type Metrics, metrics} from '#/logger/metrics'
|
|
||||||
import {consoleTransport} from '#/logger/transports/console'
|
import {consoleTransport} from '#/logger/transports/console'
|
||||||
import {sentryTransport} from '#/logger/transports/sentry'
|
import {sentryTransport} from '#/logger/transports/sentry'
|
||||||
import {
|
import {
|
||||||
@@ -11,9 +10,9 @@ import {
|
|||||||
type Transport,
|
type Transport,
|
||||||
} from '#/logger/types'
|
} from '#/logger/types'
|
||||||
import {enabledLogLevels} from '#/logger/util'
|
import {enabledLogLevels} from '#/logger/util'
|
||||||
|
import {type Events as Metrics} from '#/analytics/metrics/types'
|
||||||
import {ENV} from '#/env'
|
import {ENV} from '#/env'
|
||||||
|
export {type Events as Metrics} from '#/analytics/metrics/types'
|
||||||
export {type Metrics} from '#/logger/metrics'
|
|
||||||
|
|
||||||
const TRANSPORTS: Transport[] = (function configureTransports() {
|
const TRANSPORTS: Transport[] = (function configureTransports() {
|
||||||
switch (ENV) {
|
switch (ENV) {
|
||||||
@@ -104,7 +103,6 @@ export class Logger {
|
|||||||
statsig?: boolean
|
statsig?: boolean
|
||||||
} = {statsig: true},
|
} = {statsig: true},
|
||||||
) {
|
) {
|
||||||
metrics.track(event, metadata)
|
|
||||||
for (const transport of this.transports) {
|
for (const transport of this.transports) {
|
||||||
transport(LogLevel.Info, LogContext.Metric, event, metadata, Date.now())
|
transport(LogLevel.Info, LogContext.Metric, event, metadata, Date.now())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,26 +0,0 @@
|
|||||||
import uuid from 'react-native-uuid'
|
|
||||||
import AsyncStorage from '@react-native-async-storage/async-storage'
|
|
||||||
|
|
||||||
import {device} from '#/storage'
|
|
||||||
|
|
||||||
const LEGACY_STABLE_ID = 'STATSIG_LOCAL_STORAGE_STABLE_ID'
|
|
||||||
|
|
||||||
export async function getAndMigrateDeviceId() {
|
|
||||||
const migrated = getDeviceId()
|
|
||||||
if (migrated) return migrated
|
|
||||||
const id = (await AsyncStorage.getItem(LEGACY_STABLE_ID)) || uuid.v4()
|
|
||||||
device.set(['deviceId'], id)
|
|
||||||
return id
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getDeviceId() {
|
|
||||||
return device.get(['deviceId'])
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getDeviceIdOrThrow() {
|
|
||||||
const id = device.get(['deviceId'])
|
|
||||||
if (!id) {
|
|
||||||
throw new Error(`deviceId is not set, call getAndMigrateDeviceId first`)
|
|
||||||
}
|
|
||||||
return id
|
|
||||||
}
|
|
||||||
@@ -1,105 +0,0 @@
|
|||||||
import {Platform} from 'react-native'
|
|
||||||
|
|
||||||
import {BSKY_SERVICE} from '#/lib/constants'
|
|
||||||
import {setGrowthBookAttributes} from '#/logger/growthbook'
|
|
||||||
import {getAndMigrateDeviceId, getDeviceId} from '#/logger/metadata/deviceId'
|
|
||||||
import {getSessionId} from '#/logger/metadata/sessionId'
|
|
||||||
import * as persisted from '#/state/persisted'
|
|
||||||
import * as env from '#/env'
|
|
||||||
import {device} from '#/storage'
|
|
||||||
|
|
||||||
export type BaseMetadata = {
|
|
||||||
deviceId: string
|
|
||||||
sessionId: string
|
|
||||||
country: string
|
|
||||||
}
|
|
||||||
|
|
||||||
export type UserMetadata = {
|
|
||||||
did: string
|
|
||||||
isBskyPds: boolean
|
|
||||||
platform: string
|
|
||||||
appVersion: string
|
|
||||||
bundleIdentifier: string
|
|
||||||
bundleDate: number
|
|
||||||
refSrc: string
|
|
||||||
refUrl: string
|
|
||||||
appLanguage: string
|
|
||||||
contentLanguages: string[]
|
|
||||||
}
|
|
||||||
|
|
||||||
export type Metadata = BaseMetadata & Partial<UserMetadata>
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Ensures that deviceId is set and migrated from legacy storage. Handled on
|
|
||||||
* startup in `App.<platform>.tsx`
|
|
||||||
*/
|
|
||||||
export const setupDeviceId = getAndMigrateDeviceId()
|
|
||||||
|
|
||||||
let baseMetadata: BaseMetadata = {
|
|
||||||
deviceId: getDeviceId() || 'unknown',
|
|
||||||
sessionId: getSessionId(),
|
|
||||||
country: device.get(['mergedGeolocation'])?.countryCode || 'unknown',
|
|
||||||
}
|
|
||||||
export function updateBaseMetadata(
|
|
||||||
metadata: Partial<Omit<BaseMetadata, 'deviceId'>>,
|
|
||||||
) {
|
|
||||||
baseMetadata = {
|
|
||||||
...baseMetadata,
|
|
||||||
...metadata,
|
|
||||||
sessionId: metadata.sessionId || getSessionId(),
|
|
||||||
deviceId: getDeviceId() || 'unknown',
|
|
||||||
}
|
|
||||||
__onMetadataChange()
|
|
||||||
}
|
|
||||||
export function getBaseMetadata() {
|
|
||||||
return {
|
|
||||||
...baseMetadata,
|
|
||||||
sessionId: getSessionId(), // may have changed
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let refSrc = ''
|
|
||||||
let refUrl = ''
|
|
||||||
if (env.IS_WEB) {
|
|
||||||
const params = new URLSearchParams(window.location.search)
|
|
||||||
refSrc = params.get('ref_src') ?? ''
|
|
||||||
refUrl = decodeURIComponent(params.get('ref_url') ?? '')
|
|
||||||
}
|
|
||||||
|
|
||||||
let userMetadata: UserMetadata | null = null
|
|
||||||
export function setUserMetadata(account: persisted.PersistedAccount | null) {
|
|
||||||
if (account === null) {
|
|
||||||
userMetadata = null
|
|
||||||
} else {
|
|
||||||
const languagePrefs = persisted.get('languagePrefs')
|
|
||||||
userMetadata = {
|
|
||||||
did: account.did,
|
|
||||||
isBskyPds: account.service.startsWith(BSKY_SERVICE),
|
|
||||||
platform: Platform.OS,
|
|
||||||
appVersion: env.RELEASE_VERSION,
|
|
||||||
bundleIdentifier: env.BUNDLE_IDENTIFIER,
|
|
||||||
bundleDate: env.BUNDLE_DATE,
|
|
||||||
appLanguage: languagePrefs.appLanguage,
|
|
||||||
contentLanguages: languagePrefs.contentLanguages,
|
|
||||||
refSrc,
|
|
||||||
refUrl,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
__onMetadataChange()
|
|
||||||
}
|
|
||||||
export function getUserMetadata() {
|
|
||||||
return userMetadata
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getMetadata(): Metadata {
|
|
||||||
return {
|
|
||||||
...getBaseMetadata(),
|
|
||||||
...(getUserMetadata() || {}),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function __onMetadataChange() {
|
|
||||||
const metadata = getMetadata()
|
|
||||||
setGrowthBookAttributes(metadata)
|
|
||||||
}
|
|
||||||
__onMetadataChange()
|
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
import uuid from 'react-native-uuid'
|
|
||||||
|
|
||||||
import {onAppStateChange} from '#/lib/appState'
|
|
||||||
import {updateBaseMetadata} from '#/logger/metadata'
|
|
||||||
import {device} from '#/storage'
|
|
||||||
|
|
||||||
const TTL = 5 * 60 * 1e3 // 5 min on native
|
|
||||||
function expired(since: number | undefined) {
|
|
||||||
if (since === undefined) return false
|
|
||||||
return Date.now() - since >= TTL
|
|
||||||
}
|
|
||||||
|
|
||||||
let sessionId = (() => {
|
|
||||||
const existing = device.get(['nativeSessionId'])
|
|
||||||
const lastEvent = device.get(['nativeSessionIdLastEventAt'])
|
|
||||||
const id = existing && !expired(lastEvent) ? existing : uuid.v4()
|
|
||||||
device.set(['nativeSessionId'], id)
|
|
||||||
updateBaseMetadata({sessionId: id})
|
|
||||||
return id
|
|
||||||
})()
|
|
||||||
|
|
||||||
onAppStateChange(state => {
|
|
||||||
if (state === 'active') {
|
|
||||||
const lastEvent = device.get(['nativeSessionIdLastEventAt'])
|
|
||||||
if (expired(lastEvent)) {
|
|
||||||
sessionId = uuid.v4()
|
|
||||||
device.set(['nativeSessionId'], sessionId)
|
|
||||||
updateBaseMetadata({sessionId})
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
device.set(['nativeSessionIdLastEventAt'], Date.now())
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
export function getSessionId() {
|
|
||||||
return sessionId
|
|
||||||
}
|
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
import uuid from 'react-native-uuid'
|
|
||||||
|
|
||||||
import {onAppStateChange} from '#/lib/appState'
|
|
||||||
|
|
||||||
const TTL = 30 * 60 * 1e3 // 30 min on web
|
|
||||||
const SESSION_ID_KEY = 'bsky_session_id'
|
|
||||||
const LAST_EVENT_KEY = 'bsky_session_id_last_event_at'
|
|
||||||
|
|
||||||
function expired(since: number | undefined) {
|
|
||||||
if (since === undefined) return false
|
|
||||||
return Date.now() - since >= TTL
|
|
||||||
}
|
|
||||||
|
|
||||||
let sessionId = (() => {
|
|
||||||
const existing = window.sessionStorage.getItem(SESSION_ID_KEY)
|
|
||||||
const lastEventStr = window.sessionStorage.getItem(LAST_EVENT_KEY)
|
|
||||||
const lastEvent = lastEventStr ? Number(lastEventStr) : undefined
|
|
||||||
const id = existing && !expired(lastEvent) ? existing : uuid.v4()
|
|
||||||
window.sessionStorage.setItem(SESSION_ID_KEY, id)
|
|
||||||
// updateBaseMetadata({sessionId: id})
|
|
||||||
return id
|
|
||||||
})()
|
|
||||||
|
|
||||||
onAppStateChange(state => {
|
|
||||||
if (state === 'active') {
|
|
||||||
const lastEventStr = window.sessionStorage.getItem(LAST_EVENT_KEY)
|
|
||||||
const lastEvent = lastEventStr ? Number(lastEventStr) : undefined
|
|
||||||
if (expired(lastEvent)) {
|
|
||||||
sessionId = uuid.v4()
|
|
||||||
window.sessionStorage.setItem(SESSION_ID_KEY, sessionId)
|
|
||||||
// updateBaseMetadata({sessionId})
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
window.sessionStorage.setItem(LAST_EVENT_KEY, String(Date.now()))
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
export function getSessionId() {
|
|
||||||
return sessionId
|
|
||||||
}
|
|
||||||
@@ -1,106 +0,0 @@
|
|||||||
import {onAppStateChange} from '#/lib/appState'
|
|
||||||
import {isNetworkError} from '#/lib/strings/errors'
|
|
||||||
import {getGrowthBook} from '#/logger/growthbook'
|
|
||||||
import {getMetadata, type Metadata} from '#/logger/metadata'
|
|
||||||
import {type Metrics} from '#/logger/metrics/events'
|
|
||||||
import {Sentry} from '#/logger/sentry/lib'
|
|
||||||
import * as env from '#/env'
|
|
||||||
|
|
||||||
type Event<M extends Metrics> = {
|
|
||||||
time: number
|
|
||||||
event: keyof M
|
|
||||||
payload: M[keyof M]
|
|
||||||
metadata: Metadata
|
|
||||||
}
|
|
||||||
|
|
||||||
const TRACKING_ENDPOINT = env.METRICS_API_HOST + '/track'
|
|
||||||
|
|
||||||
export class MetricsClient {
|
|
||||||
private started: boolean = false
|
|
||||||
private queue: Event<Metrics>[] = []
|
|
||||||
private failedQueue: Event<Metrics>[] = []
|
|
||||||
private flushInterval: NodeJS.Timeout | null = null
|
|
||||||
|
|
||||||
start() {
|
|
||||||
if (this.started) return
|
|
||||||
if (!getGrowthBook().ready) return
|
|
||||||
this.started = true
|
|
||||||
this.flushInterval = setInterval(() => {
|
|
||||||
this.flush()
|
|
||||||
}, 10_000)
|
|
||||||
onAppStateChange(state => {
|
|
||||||
if (state === 'active') {
|
|
||||||
this.retryFailedLogs()
|
|
||||||
} else {
|
|
||||||
this.flush()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
track<E extends keyof Metrics>(event: E, payload: Metrics[E]) {
|
|
||||||
this.start()
|
|
||||||
|
|
||||||
this.queue.push({
|
|
||||||
time: Date.now(),
|
|
||||||
event,
|
|
||||||
payload,
|
|
||||||
metadata: getMetadata(),
|
|
||||||
})
|
|
||||||
|
|
||||||
if (this.queue.length > 100) {
|
|
||||||
this.flush()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
flush() {
|
|
||||||
if (!this.queue.length) return
|
|
||||||
const events = this.queue.splice(0, this.queue.length)
|
|
||||||
this.queue = []
|
|
||||||
this.sendBatch(events)
|
|
||||||
}
|
|
||||||
|
|
||||||
private async sendBatch(events: Event<Metrics>[], isRetry: boolean = false) {
|
|
||||||
try {
|
|
||||||
const body = JSON.stringify(events)
|
|
||||||
if (env.IS_WEB && 'navigator' in globalThis && navigator.sendBeacon) {
|
|
||||||
navigator.sendBeacon(
|
|
||||||
TRACKING_ENDPOINT,
|
|
||||||
new Blob([body], {type: 'application/json'}),
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
const res = await fetch(TRACKING_ENDPOINT, {
|
|
||||||
method: 'POST',
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
},
|
|
||||||
body: JSON.stringify(events),
|
|
||||||
keepalive: true,
|
|
||||||
})
|
|
||||||
|
|
||||||
if (!res.ok) {
|
|
||||||
const error = await res.text().catch(() => 'Unknown error')
|
|
||||||
// construct a "network error" for `isNetworkError` to work
|
|
||||||
throw new Error(`${res.status} Failed to fetch — ${error}`)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (e: any) {
|
|
||||||
if (isNetworkError(e)) {
|
|
||||||
if (isRetry) return // retry once
|
|
||||||
this.failedQueue.push(...events)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
Sentry.captureException(`Failed to send metrics`, {
|
|
||||||
extra: {
|
|
||||||
safeMessage: e.toString(),
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private retryFailedLogs() {
|
|
||||||
if (!this.failedQueue.length) return
|
|
||||||
const events = this.failedQueue.splice(0, this.failedQueue.length)
|
|
||||||
this.failedQueue = []
|
|
||||||
this.sendBatch(events, true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,826 +0,0 @@
|
|||||||
import {type NotificationReason} from '#/lib/hooks/useNotificationHandler'
|
|
||||||
import {type FeedDescriptor} from '#/state/queries/post-feed'
|
|
||||||
import {type LiveEventFeedMetricContext} from '#/features/liveEvents/types'
|
|
||||||
|
|
||||||
export type Metrics = {
|
|
||||||
// App events
|
|
||||||
init: {
|
|
||||||
initMs: number
|
|
||||||
}
|
|
||||||
'experiment:viewed': {
|
|
||||||
experimentId: string
|
|
||||||
variationId: string
|
|
||||||
}
|
|
||||||
|
|
||||||
'account:loggedIn': {
|
|
||||||
logContext:
|
|
||||||
| 'LoginForm'
|
|
||||||
| 'SwitchAccount'
|
|
||||||
| 'ChooseAccountForm'
|
|
||||||
| 'Settings'
|
|
||||||
| 'Notification'
|
|
||||||
withPassword: boolean
|
|
||||||
}
|
|
||||||
'account:loggedOut': {
|
|
||||||
logContext:
|
|
||||||
| 'SwitchAccount'
|
|
||||||
| 'Settings'
|
|
||||||
| 'SignupQueued'
|
|
||||||
| 'Deactivated'
|
|
||||||
| 'Takendown'
|
|
||||||
| 'AgeAssuranceNoAccessScreen'
|
|
||||||
scope: 'current' | 'every'
|
|
||||||
}
|
|
||||||
'notifications:openApp': {
|
|
||||||
reason: NotificationReason
|
|
||||||
causedBoot: boolean
|
|
||||||
}
|
|
||||||
'notifications:request': {
|
|
||||||
context: 'StartOnboarding' | 'AfterOnboarding' | 'Login' | 'Home'
|
|
||||||
status: 'granted' | 'denied' | 'undetermined'
|
|
||||||
}
|
|
||||||
'state:background': {
|
|
||||||
secondsActive: number
|
|
||||||
}
|
|
||||||
'state:foreground': {}
|
|
||||||
'router:navigate': {
|
|
||||||
from?: string
|
|
||||||
}
|
|
||||||
'deepLink:referrerReceived': {
|
|
||||||
to: string
|
|
||||||
referrer: string
|
|
||||||
hostname: string
|
|
||||||
}
|
|
||||||
|
|
||||||
// Screen events
|
|
||||||
'splash:signInPressed': {}
|
|
||||||
'splash:createAccountPressed': {}
|
|
||||||
'welcomeModal:signupClicked': {}
|
|
||||||
'welcomeModal:exploreClicked': {}
|
|
||||||
'welcomeModal:signinClicked': {}
|
|
||||||
'welcomeModal:dismissed': {}
|
|
||||||
'welcomeModal:presented': {}
|
|
||||||
'signup:nextPressed': {
|
|
||||||
activeStep: number
|
|
||||||
phoneVerificationRequired?: boolean
|
|
||||||
}
|
|
||||||
'signup:backPressed': {
|
|
||||||
activeStep: number
|
|
||||||
}
|
|
||||||
'signup:captchaSuccess': {}
|
|
||||||
'signup:captchaFailure': {}
|
|
||||||
'signup:fieldError': {
|
|
||||||
field: string
|
|
||||||
errorCount: number
|
|
||||||
errorMessage: string
|
|
||||||
activeStep: number
|
|
||||||
}
|
|
||||||
'signup:backgrounded': {
|
|
||||||
activeStep: number
|
|
||||||
backgroundCount: number
|
|
||||||
}
|
|
||||||
'signup:handleTaken': {typeahead?: boolean}
|
|
||||||
'signup:handleAvailable': {typeahead?: boolean}
|
|
||||||
'signup:handleSuggestionSelected': {method: string}
|
|
||||||
'signin:hostingProviderPressed': {
|
|
||||||
hostingProviderDidChange: boolean
|
|
||||||
}
|
|
||||||
'signin:hostingProviderFailedResolution': {}
|
|
||||||
'signin:success': {
|
|
||||||
failedAttemptsCount: number
|
|
||||||
isUsingCustomProvider: boolean
|
|
||||||
timeTakenSeconds: number
|
|
||||||
}
|
|
||||||
'signin:backPressed': {
|
|
||||||
failedAttemptsCount: number
|
|
||||||
}
|
|
||||||
'signin:forgotPasswordPressed': {}
|
|
||||||
'signin:passwordReset': {}
|
|
||||||
'signin:passwordResetSuccess': {}
|
|
||||||
'signin:passwordResetFailure': {}
|
|
||||||
'onboarding:interests:nextPressed': {
|
|
||||||
selectedInterests: string[]
|
|
||||||
selectedInterestsLength: number
|
|
||||||
}
|
|
||||||
'onboarding:suggestedAccounts:tabPressed': {
|
|
||||||
tab: string
|
|
||||||
}
|
|
||||||
'onboarding:suggestedAccounts:followAllPressed': {
|
|
||||||
tab: string
|
|
||||||
numAccounts: number
|
|
||||||
}
|
|
||||||
'onboarding:suggestedAccounts:nextPressed': {
|
|
||||||
selectedAccountsLength: number
|
|
||||||
skipped: boolean
|
|
||||||
}
|
|
||||||
'onboarding:followingFeed:nextPressed': {}
|
|
||||||
'onboarding:algoFeeds:nextPressed': {
|
|
||||||
selectedPrimaryFeeds: string[]
|
|
||||||
selectedPrimaryFeedsLength: number
|
|
||||||
selectedSecondaryFeeds: string[]
|
|
||||||
selectedSecondaryFeedsLength: number
|
|
||||||
}
|
|
||||||
'onboarding:topicalFeeds:nextPressed': {
|
|
||||||
selectedFeeds: string[]
|
|
||||||
selectedFeedsLength: number
|
|
||||||
}
|
|
||||||
'onboarding:moderation:nextPressed': {}
|
|
||||||
'onboarding:profile:nextPressed': {}
|
|
||||||
'onboarding:finished:nextPressed': {
|
|
||||||
usedStarterPack: boolean
|
|
||||||
starterPackName?: string
|
|
||||||
starterPackCreator?: string
|
|
||||||
starterPackUri?: string
|
|
||||||
profilesFollowed: number
|
|
||||||
feedsPinned: number
|
|
||||||
}
|
|
||||||
'onboarding:finished:avatarResult': {
|
|
||||||
avatarResult: 'default' | 'created' | 'uploaded'
|
|
||||||
}
|
|
||||||
'onboarding:valueProp:stepOne:nextPressed': {}
|
|
||||||
'onboarding:valueProp:stepTwo:nextPressed': {}
|
|
||||||
'onboarding:valueProp:skipPressed': {}
|
|
||||||
'home:feedDisplayed': {
|
|
||||||
feedUrl: string
|
|
||||||
feedType: string
|
|
||||||
index: number
|
|
||||||
}
|
|
||||||
'feed:endReached': {
|
|
||||||
feedUrl: string
|
|
||||||
feedType: string
|
|
||||||
itemCount: number
|
|
||||||
}
|
|
||||||
'feed:refresh': {
|
|
||||||
feedUrl: string
|
|
||||||
feedType: string
|
|
||||||
reason: 'pull-to-refresh' | 'soft-reset' | 'load-latest'
|
|
||||||
}
|
|
||||||
'feed:save': {
|
|
||||||
feedUrl: string
|
|
||||||
}
|
|
||||||
'feed:unsave': {
|
|
||||||
feedUrl: string
|
|
||||||
}
|
|
||||||
'feed:pin': {
|
|
||||||
feedUrl: string
|
|
||||||
}
|
|
||||||
'feed:unpin': {
|
|
||||||
feedUrl: string
|
|
||||||
}
|
|
||||||
'feed:like': {
|
|
||||||
feedUrl: string
|
|
||||||
}
|
|
||||||
'feed:unlike': {
|
|
||||||
feedUrl: string
|
|
||||||
}
|
|
||||||
'feed:share': {
|
|
||||||
feedUrl: string
|
|
||||||
}
|
|
||||||
'feed:suggestion:seen': {
|
|
||||||
feedUrl: string
|
|
||||||
}
|
|
||||||
'feed:suggestion:press': {
|
|
||||||
feedUrl: string
|
|
||||||
}
|
|
||||||
'post:showMore': {
|
|
||||||
uri: string
|
|
||||||
authorDid: string
|
|
||||||
logContext: 'FeedItem' | 'PostThreadItem' | 'Post' | 'ImmersiveVideo'
|
|
||||||
feedDescriptor?: string
|
|
||||||
position?: number
|
|
||||||
}
|
|
||||||
'post:showLess': {
|
|
||||||
uri: string
|
|
||||||
authorDid: string
|
|
||||||
logContext: 'FeedItem' | 'PostThreadItem' | 'Post' | 'ImmersiveVideo'
|
|
||||||
feedDescriptor?: string
|
|
||||||
position?: number
|
|
||||||
}
|
|
||||||
'feed:clickthrough': {
|
|
||||||
feed: string
|
|
||||||
count: number
|
|
||||||
}
|
|
||||||
'feed:engaged': {
|
|
||||||
feed: string
|
|
||||||
count: number
|
|
||||||
}
|
|
||||||
'feed:seen': {
|
|
||||||
feed: string
|
|
||||||
count: number
|
|
||||||
}
|
|
||||||
|
|
||||||
'feed:discover:emptyError': {
|
|
||||||
userDid: string
|
|
||||||
}
|
|
||||||
|
|
||||||
'composer:gif:open': {}
|
|
||||||
'composer:gif:select': {}
|
|
||||||
'composerPrompt:press': {}
|
|
||||||
'composerPrompt:camera:press': {}
|
|
||||||
'composerPrompt:gallery:press': {}
|
|
||||||
|
|
||||||
'composer:threadgate:open': {
|
|
||||||
nudged: boolean
|
|
||||||
}
|
|
||||||
'composer:threadgate:save': {
|
|
||||||
replyOptions: string
|
|
||||||
quotesEnabled: boolean
|
|
||||||
persist: boolean
|
|
||||||
hasChanged: boolean
|
|
||||||
}
|
|
||||||
|
|
||||||
// Data events
|
|
||||||
'account:create:begin': {}
|
|
||||||
'account:create:success': {
|
|
||||||
signupDuration: number
|
|
||||||
fieldErrorsTotal: number
|
|
||||||
backgroundCount: number
|
|
||||||
}
|
|
||||||
'post:create': {
|
|
||||||
imageCount: number
|
|
||||||
isReply: boolean
|
|
||||||
isPartOfThread: boolean
|
|
||||||
hasLink: boolean
|
|
||||||
hasQuote: boolean
|
|
||||||
langs: string
|
|
||||||
logContext: 'Composer'
|
|
||||||
}
|
|
||||||
'thread:create': {
|
|
||||||
postCount: number
|
|
||||||
isReply: boolean
|
|
||||||
}
|
|
||||||
'post:like': {
|
|
||||||
uri: string
|
|
||||||
authorDid: string
|
|
||||||
doesLikerFollowPoster: boolean | undefined
|
|
||||||
doesPosterFollowLiker: boolean | undefined
|
|
||||||
likerClout: number | undefined
|
|
||||||
postClout: number | undefined
|
|
||||||
logContext: 'FeedItem' | 'PostThreadItem' | 'Post' | 'ImmersiveVideo'
|
|
||||||
feedDescriptor?: string
|
|
||||||
}
|
|
||||||
'post:repost': {
|
|
||||||
uri: string
|
|
||||||
authorDid: string
|
|
||||||
logContext: 'FeedItem' | 'PostThreadItem' | 'Post' | 'ImmersiveVideo'
|
|
||||||
feedDescriptor?: string
|
|
||||||
}
|
|
||||||
'post:unlike': {
|
|
||||||
uri: string
|
|
||||||
authorDid: string
|
|
||||||
logContext: 'FeedItem' | 'PostThreadItem' | 'Post' | 'ImmersiveVideo'
|
|
||||||
feedDescriptor?: string
|
|
||||||
}
|
|
||||||
'post:unrepost': {
|
|
||||||
uri: string
|
|
||||||
authorDid: string
|
|
||||||
logContext: 'FeedItem' | 'PostThreadItem' | 'Post' | 'ImmersiveVideo'
|
|
||||||
feedDescriptor?: string
|
|
||||||
}
|
|
||||||
'post:mute': {
|
|
||||||
uri: string
|
|
||||||
authorDid: string
|
|
||||||
logContext: 'FeedItem' | 'PostThreadItem' | 'Post' | 'ImmersiveVideo'
|
|
||||||
feedDescriptor?: string
|
|
||||||
position?: number
|
|
||||||
}
|
|
||||||
'post:unmute': {
|
|
||||||
uri: string
|
|
||||||
authorDid: string
|
|
||||||
logContext: 'FeedItem' | 'PostThreadItem' | 'Post' | 'ImmersiveVideo'
|
|
||||||
feedDescriptor?: string
|
|
||||||
position?: number
|
|
||||||
}
|
|
||||||
'post:pin': {}
|
|
||||||
'post:unpin': {}
|
|
||||||
'post:bookmark': {
|
|
||||||
uri: string
|
|
||||||
authorDid: string
|
|
||||||
logContext: 'FeedItem' | 'PostThreadItem' | 'Post' | 'ImmersiveVideo'
|
|
||||||
feedDescriptor?: string
|
|
||||||
position?: number
|
|
||||||
}
|
|
||||||
'post:unbookmark': {
|
|
||||||
uri: string
|
|
||||||
authorDid: string
|
|
||||||
logContext: 'FeedItem' | 'PostThreadItem' | 'Post' | 'ImmersiveVideo'
|
|
||||||
feedDescriptor?: string
|
|
||||||
position?: number
|
|
||||||
}
|
|
||||||
'post:clickReply': {
|
|
||||||
uri: string
|
|
||||||
authorDid: string
|
|
||||||
logContext: 'FeedItem' | 'PostThreadItem' | 'Post' | 'ImmersiveVideo'
|
|
||||||
feedDescriptor?: string
|
|
||||||
position?: number
|
|
||||||
}
|
|
||||||
'post:clickQuotePost': {
|
|
||||||
uri: string
|
|
||||||
authorDid: string
|
|
||||||
logContext: 'FeedItem' | 'PostThreadItem' | 'Post' | 'ImmersiveVideo'
|
|
||||||
feedDescriptor?: string
|
|
||||||
position?: number
|
|
||||||
}
|
|
||||||
'post:clickthroughAuthor': {
|
|
||||||
uri: string
|
|
||||||
authorDid: string
|
|
||||||
logContext: 'FeedItem' | 'PostThreadItem' | 'Post' | 'ImmersiveVideo'
|
|
||||||
feedDescriptor?: string
|
|
||||||
position?: number
|
|
||||||
}
|
|
||||||
'post:clickthroughItem': {
|
|
||||||
uri: string
|
|
||||||
authorDid: string
|
|
||||||
logContext: 'FeedItem' | 'PostThreadItem' | 'Post' | 'ImmersiveVideo'
|
|
||||||
feedDescriptor?: string
|
|
||||||
position?: number
|
|
||||||
}
|
|
||||||
'post:clickthroughEmbed': {
|
|
||||||
uri: string
|
|
||||||
authorDid: string
|
|
||||||
logContext: 'FeedItem' | 'PostThreadItem' | 'Post' | 'ImmersiveVideo'
|
|
||||||
feedDescriptor?: string
|
|
||||||
position?: number
|
|
||||||
}
|
|
||||||
'post:view': {
|
|
||||||
uri: string
|
|
||||||
authorDid: string
|
|
||||||
logContext:
|
|
||||||
| 'FeedItem'
|
|
||||||
| 'PostThreadItem'
|
|
||||||
| 'Post'
|
|
||||||
| 'ImmersiveVideo'
|
|
||||||
| 'SearchResults'
|
|
||||||
| 'Bookmarks'
|
|
||||||
| 'Notifications'
|
|
||||||
| 'Hashtag'
|
|
||||||
| 'Topic'
|
|
||||||
| 'PostQuotes'
|
|
||||||
feedDescriptor?: string
|
|
||||||
position?: number
|
|
||||||
}
|
|
||||||
'bookmarks:view': {}
|
|
||||||
'bookmarks:post-clicked': {}
|
|
||||||
'profile:follow': {
|
|
||||||
contextProfileDid?: string
|
|
||||||
didBecomeMutual: boolean | undefined
|
|
||||||
followeeClout: number | undefined
|
|
||||||
followeeDid: string
|
|
||||||
followerClout: number | undefined
|
|
||||||
position?: number
|
|
||||||
logContext:
|
|
||||||
| 'RecommendedFollowsItem'
|
|
||||||
| 'PostThreadItem'
|
|
||||||
| 'ProfileCard'
|
|
||||||
| 'ProfileHeader'
|
|
||||||
| 'ProfileHeaderSuggestedFollows'
|
|
||||||
| 'ProfileMenu'
|
|
||||||
| 'ProfileHoverCard'
|
|
||||||
| 'AvatarButton'
|
|
||||||
| 'StarterPackProfilesList'
|
|
||||||
| 'FeedInterstitial'
|
|
||||||
| 'PostOnboardingFindFollows'
|
|
||||||
| 'ImmersiveVideo'
|
|
||||||
| 'ExploreSuggestedAccounts'
|
|
||||||
| 'OnboardingSuggestedAccounts'
|
|
||||||
| 'FindContacts'
|
|
||||||
}
|
|
||||||
'profile:followers:view': {
|
|
||||||
contextProfileDid: string
|
|
||||||
isOwnProfile: boolean
|
|
||||||
}
|
|
||||||
'profile:followers:paginate': {
|
|
||||||
contextProfileDid: string
|
|
||||||
itemCount: number
|
|
||||||
page: number
|
|
||||||
}
|
|
||||||
'profile:following:view': {
|
|
||||||
contextProfileDid: string
|
|
||||||
isOwnProfile: boolean
|
|
||||||
}
|
|
||||||
'profile:following:paginate': {
|
|
||||||
contextProfileDid: string
|
|
||||||
itemCount: number
|
|
||||||
page: number
|
|
||||||
}
|
|
||||||
'profileCard:seen': {
|
|
||||||
contextProfileDid?: string
|
|
||||||
profileDid: string
|
|
||||||
position?: number
|
|
||||||
}
|
|
||||||
'suggestedUser:follow': {
|
|
||||||
logContext:
|
|
||||||
| 'Explore'
|
|
||||||
| 'InterstitialDiscover'
|
|
||||||
| 'InterstitialProfile'
|
|
||||||
| 'Profile'
|
|
||||||
| 'Onboarding'
|
|
||||||
location: 'Card' | 'Profile'
|
|
||||||
recId?: number
|
|
||||||
position: number
|
|
||||||
suggestedDid: string
|
|
||||||
category: string | null
|
|
||||||
}
|
|
||||||
'suggestedUser:press': {
|
|
||||||
logContext:
|
|
||||||
| 'Explore'
|
|
||||||
| 'InterstitialDiscover'
|
|
||||||
| 'InterstitialProfile'
|
|
||||||
| 'Onboarding'
|
|
||||||
recId?: number
|
|
||||||
position: number
|
|
||||||
suggestedDid: string
|
|
||||||
category: string | null
|
|
||||||
}
|
|
||||||
'suggestedUser:seen': {
|
|
||||||
logContext:
|
|
||||||
| 'Explore'
|
|
||||||
| 'InterstitialDiscover'
|
|
||||||
| 'InterstitialProfile'
|
|
||||||
| 'Profile'
|
|
||||||
| 'Onboarding'
|
|
||||||
| 'ProgressGuide'
|
|
||||||
recId?: number
|
|
||||||
position: number
|
|
||||||
suggestedDid: string
|
|
||||||
category: string | null
|
|
||||||
}
|
|
||||||
'suggestedUser:seeMore': {
|
|
||||||
logContext:
|
|
||||||
| 'Explore'
|
|
||||||
| 'InterstitialDiscover'
|
|
||||||
| 'InterstitialProfile'
|
|
||||||
| 'Profile'
|
|
||||||
| 'Onboarding'
|
|
||||||
}
|
|
||||||
'suggestedUser:dismiss': {
|
|
||||||
logContext: 'InterstitialDiscover' | 'InterstitialProfile'
|
|
||||||
recId?: number
|
|
||||||
position: number
|
|
||||||
suggestedDid: string
|
|
||||||
}
|
|
||||||
'profile:unfollow': {
|
|
||||||
logContext:
|
|
||||||
| 'RecommendedFollowsItem'
|
|
||||||
| 'PostThreadItem'
|
|
||||||
| 'ProfileCard'
|
|
||||||
| 'ProfileHeader'
|
|
||||||
| 'ProfileHeaderSuggestedFollows'
|
|
||||||
| 'ProfileMenu'
|
|
||||||
| 'ProfileHoverCard'
|
|
||||||
| 'Chat'
|
|
||||||
| 'AvatarButton'
|
|
||||||
| 'StarterPackProfilesList'
|
|
||||||
| 'FeedInterstitial'
|
|
||||||
| 'PostOnboardingFindFollows'
|
|
||||||
| 'ImmersiveVideo'
|
|
||||||
| 'ExploreSuggestedAccounts'
|
|
||||||
| 'OnboardingSuggestedAccounts'
|
|
||||||
| 'FindContacts'
|
|
||||||
}
|
|
||||||
'chat:create': {
|
|
||||||
logContext: 'ProfileHeader' | 'NewChatDialog' | 'SendViaChatDialog'
|
|
||||||
}
|
|
||||||
'chat:open': {
|
|
||||||
logContext:
|
|
||||||
| 'ProfileHeader'
|
|
||||||
| 'NewChatDialog'
|
|
||||||
| 'ChatsList'
|
|
||||||
| 'SendViaChatDialog'
|
|
||||||
}
|
|
||||||
'starterPack:addUser': {
|
|
||||||
starterPack?: string
|
|
||||||
}
|
|
||||||
'starterPack:removeUser': {
|
|
||||||
starterPack?: string
|
|
||||||
}
|
|
||||||
'starterPack:share': {
|
|
||||||
starterPack: string
|
|
||||||
shareType: 'link' | 'qrcode'
|
|
||||||
qrShareType?: 'save' | 'copy' | 'share'
|
|
||||||
}
|
|
||||||
'starterPack:followAll': {
|
|
||||||
logContext: 'StarterPackProfilesList' | 'Onboarding'
|
|
||||||
starterPack: string
|
|
||||||
count: number
|
|
||||||
}
|
|
||||||
'starterPack:delete': {}
|
|
||||||
'starterPack:create': {
|
|
||||||
setName: boolean
|
|
||||||
setDescription: boolean
|
|
||||||
profilesCount: number
|
|
||||||
feedsCount: number
|
|
||||||
}
|
|
||||||
'starterPack:ctaPress': {
|
|
||||||
starterPack: string
|
|
||||||
}
|
|
||||||
'starterPack:opened': {
|
|
||||||
starterPack: string
|
|
||||||
}
|
|
||||||
'link:clicked': {
|
|
||||||
url: string
|
|
||||||
domain: string
|
|
||||||
}
|
|
||||||
|
|
||||||
'feed:interstitial:feedCard:press': {}
|
|
||||||
'desktopFeeds:feed:click': {
|
|
||||||
feedUri: string
|
|
||||||
feedDescriptor: string
|
|
||||||
}
|
|
||||||
|
|
||||||
'profile:header:suggestedFollowsCard:press': {}
|
|
||||||
'profile:addToStarterPack': {}
|
|
||||||
|
|
||||||
'test:all:always': {}
|
|
||||||
'test:all:sometimes': {}
|
|
||||||
'test:all:boosted_by_gate1': {reason: 'base' | 'gate1'}
|
|
||||||
'test:all:boosted_by_gate2': {reason: 'base' | 'gate2'}
|
|
||||||
'test:all:boosted_by_both': {reason: 'base' | 'gate1' | 'gate2'}
|
|
||||||
'test:gate1:always': {}
|
|
||||||
'test:gate1:sometimes': {}
|
|
||||||
'test:gate2:always': {}
|
|
||||||
'test:gate2:sometimes': {}
|
|
||||||
|
|
||||||
'tmd:share': {}
|
|
||||||
'tmd:download': {}
|
|
||||||
'tmd:post': {}
|
|
||||||
|
|
||||||
'trendingTopics:show': {
|
|
||||||
context: 'settings'
|
|
||||||
}
|
|
||||||
'trendingTopics:hide': {
|
|
||||||
context: 'settings' | 'sidebar' | 'interstitial' | 'explore:trending'
|
|
||||||
}
|
|
||||||
'trendingTopic:click': {
|
|
||||||
context: 'sidebar' | 'interstitial' | 'explore'
|
|
||||||
}
|
|
||||||
'recommendedTopic:click': {
|
|
||||||
context: 'explore'
|
|
||||||
}
|
|
||||||
'trendingVideos:show': {
|
|
||||||
context: 'settings'
|
|
||||||
}
|
|
||||||
'trendingVideos:hide': {
|
|
||||||
context: 'settings' | 'interstitial:discover' | 'interstitial:explore'
|
|
||||||
}
|
|
||||||
'videoCard:click': {
|
|
||||||
context: 'interstitial:discover' | 'interstitial:explore' | 'feed'
|
|
||||||
}
|
|
||||||
|
|
||||||
'explore:module:seen': {
|
|
||||||
module:
|
|
||||||
| 'trendingTopics'
|
|
||||||
| 'trendingVideos'
|
|
||||||
| 'suggestedAccounts'
|
|
||||||
| 'suggestedFeeds'
|
|
||||||
| 'suggestedStarterPacks'
|
|
||||||
| `feed:${FeedDescriptor}`
|
|
||||||
}
|
|
||||||
'explore:module:searchButtonPress': {
|
|
||||||
module: 'suggestedAccounts' | 'suggestedFeeds'
|
|
||||||
}
|
|
||||||
'explore:suggestedAccounts:tabPressed': {
|
|
||||||
tab: string
|
|
||||||
}
|
|
||||||
|
|
||||||
'progressGuide:hide': {}
|
|
||||||
'progressGuide:followDialog:open': {}
|
|
||||||
|
|
||||||
'moderation:subscribedToLabeler': {}
|
|
||||||
'moderation:unsubscribedFromLabeler': {}
|
|
||||||
'moderation:changeLabelPreference': {
|
|
||||||
preference: string
|
|
||||||
}
|
|
||||||
|
|
||||||
'moderation:subscribedToList': {
|
|
||||||
listType: 'mute' | 'block'
|
|
||||||
}
|
|
||||||
'moderation:unsubscribedFromList': {
|
|
||||||
listType: 'mute' | 'block'
|
|
||||||
}
|
|
||||||
|
|
||||||
'reportDialog:open': {
|
|
||||||
subjectType: string
|
|
||||||
}
|
|
||||||
'reportDialog:close': {}
|
|
||||||
'reportDialog:success': {
|
|
||||||
reason: string
|
|
||||||
labeler: string
|
|
||||||
details: boolean
|
|
||||||
}
|
|
||||||
'reportDialog:failure': {}
|
|
||||||
|
|
||||||
translate: {
|
|
||||||
sourceLanguages: string[]
|
|
||||||
targetLanguage: string
|
|
||||||
textLength: number
|
|
||||||
}
|
|
||||||
|
|
||||||
'verification:create': {}
|
|
||||||
'verification:revoke': {}
|
|
||||||
'verification:badge:click': {}
|
|
||||||
'verification:learn-more': {
|
|
||||||
location:
|
|
||||||
| 'initialAnnouncementeNux'
|
|
||||||
| 'verificationsDialog'
|
|
||||||
| 'verifierDialog'
|
|
||||||
| 'verificationSettings'
|
|
||||||
}
|
|
||||||
'verification:settings:hideBadges': {}
|
|
||||||
'verification:settings:unHideBadges': {}
|
|
||||||
|
|
||||||
'live:create': {duration: number}
|
|
||||||
'live:edit': {}
|
|
||||||
'live:remove': {}
|
|
||||||
'live:card:open': {subject: string; from: 'post' | 'profile'}
|
|
||||||
'live:card:watch': {subject: string}
|
|
||||||
'live:card:openProfile': {subject: string}
|
|
||||||
'live:view:profile': {subject: string}
|
|
||||||
'live:view:post': {subject: string; feed?: string}
|
|
||||||
|
|
||||||
'post:share': {
|
|
||||||
uri: string
|
|
||||||
authorDid: string
|
|
||||||
logContext: 'FeedItem' | 'PostThreadItem' | 'Post' | 'ImmersiveVideo'
|
|
||||||
feedDescriptor?: string
|
|
||||||
postContext: 'feed' | 'thread'
|
|
||||||
position?: number
|
|
||||||
}
|
|
||||||
'share:press:copyLink': {}
|
|
||||||
'share:press:nativeShare': {}
|
|
||||||
'share:press:openDmSearch': {}
|
|
||||||
'share:press:dmSelected': {}
|
|
||||||
'share:press:recentDm': {}
|
|
||||||
'share:press:embed': {}
|
|
||||||
|
|
||||||
'thread:click:showOtherReplies': {}
|
|
||||||
'thread:click:hideReplyForMe': {}
|
|
||||||
'thread:click:hideReplyForEveryone': {}
|
|
||||||
'thread:preferences:load': {
|
|
||||||
[key: string]: any
|
|
||||||
}
|
|
||||||
'thread:preferences:update': {
|
|
||||||
[key: string]: any
|
|
||||||
}
|
|
||||||
'thread:click:headerMenuOpen': {}
|
|
||||||
'thread:click:editOwnThreadgate': {}
|
|
||||||
'thread:click:viewSomeoneElsesThreadgate': {}
|
|
||||||
'activitySubscription:enable': {
|
|
||||||
setting: 'posts' | 'posts_and_replies'
|
|
||||||
}
|
|
||||||
'activitySubscription:disable': {}
|
|
||||||
'activityPreference:changeChannels': {
|
|
||||||
name: string
|
|
||||||
push: boolean
|
|
||||||
list: boolean
|
|
||||||
}
|
|
||||||
'activityPreference:changeFilter': {
|
|
||||||
name: string
|
|
||||||
value: string
|
|
||||||
}
|
|
||||||
|
|
||||||
'ageAssurance:navigateToSettings': {}
|
|
||||||
'ageAssurance:dismissFeedBanner': {}
|
|
||||||
'ageAssurance:dismissSettingsNotice': {}
|
|
||||||
'ageAssurance:initDialogOpen': {
|
|
||||||
hasInitiatedPreviously: boolean
|
|
||||||
}
|
|
||||||
'ageAssurance:initDialogSubmit': {}
|
|
||||||
'ageAssurance:api:begin': {
|
|
||||||
platform: string
|
|
||||||
countryCode: string
|
|
||||||
regionCode?: string
|
|
||||||
}
|
|
||||||
'ageAssurance:initDialogError': {
|
|
||||||
code: string
|
|
||||||
}
|
|
||||||
'ageAssurance:redirectDialogOpen': {}
|
|
||||||
'ageAssurance:redirectDialogSuccess': {}
|
|
||||||
'ageAssurance:redirectDialogFail': {}
|
|
||||||
'ageAssurance:appealDialogOpen': {}
|
|
||||||
'ageAssurance:appealDialogSubmit': {}
|
|
||||||
'ageAssurance:noAccessScreen:shown': {
|
|
||||||
accountCreatedAt: string
|
|
||||||
isAARegion: boolean
|
|
||||||
hasDeclaredAge: boolean
|
|
||||||
canUpdateBirthday: boolean
|
|
||||||
}
|
|
||||||
'ageAssurance:noAccessScreen:openBirthdateDialog': {}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Specifically for the `BlockedGeoOverlay`
|
|
||||||
*/
|
|
||||||
'blockedGeoOverlay:shown': {}
|
|
||||||
|
|
||||||
'geo:debug': {}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Find Contacts stuff
|
|
||||||
*/
|
|
||||||
|
|
||||||
// user presses the button on the new feature NUX
|
|
||||||
'contacts:nux:ctaPressed': {}
|
|
||||||
// user presses the banner NUX
|
|
||||||
'contacts:nux:bannerPressed': {}
|
|
||||||
// user dismisses the banner
|
|
||||||
'contacts:nux:bannerDismissed': {}
|
|
||||||
|
|
||||||
// user lands on the contacts step
|
|
||||||
'onboarding:contacts:presented': {}
|
|
||||||
// user pressed "Import Contacts" button to begin flow
|
|
||||||
'onboarding:contacts:begin': {}
|
|
||||||
// skips the step entirely
|
|
||||||
'onboarding:contacts:skipPressed': {}
|
|
||||||
// user shared their contacts
|
|
||||||
'onboarding:contacts:contactsShared': {}
|
|
||||||
// user leaves the matches page
|
|
||||||
'onboarding:contacts:nextPressed': {
|
|
||||||
matchCount: number
|
|
||||||
followCount: number
|
|
||||||
dismissedMatchCount: number
|
|
||||||
}
|
|
||||||
|
|
||||||
// user entered a number
|
|
||||||
'contacts:phone:phoneEntered': {
|
|
||||||
entryPoint: 'Onboarding' | 'Standalone'
|
|
||||||
}
|
|
||||||
// user entered the correct one-time-code
|
|
||||||
'contacts:phone:phoneVerified': {
|
|
||||||
entryPoint: 'Onboarding' | 'Standalone'
|
|
||||||
}
|
|
||||||
// user responded to the contacts permission prompt
|
|
||||||
'contacts:permission:request': {
|
|
||||||
status: 'granted' | 'denied'
|
|
||||||
accessLevelIOS?: 'all' | 'limited' | 'none'
|
|
||||||
}
|
|
||||||
// contacts were successfully imported and matched
|
|
||||||
'contacts:import:success': {
|
|
||||||
contactCount: number
|
|
||||||
matchCount: number
|
|
||||||
entryPoint: 'Onboarding' | 'Standalone'
|
|
||||||
}
|
|
||||||
// contacts import failed
|
|
||||||
'contacts:import:failure': {
|
|
||||||
reason: 'noValidNumbers' | 'networkError' | 'unknown'
|
|
||||||
entryPoint: 'Onboarding' | 'Standalone'
|
|
||||||
}
|
|
||||||
// user followed a single match
|
|
||||||
'contacts:matches:follow': {
|
|
||||||
entryPoint: 'Onboarding' | 'Standalone'
|
|
||||||
}
|
|
||||||
// user pressed "Follow All" on matches
|
|
||||||
'contacts:matches:followAll': {
|
|
||||||
followCount: number
|
|
||||||
entryPoint: 'Onboarding' | 'Standalone'
|
|
||||||
}
|
|
||||||
// user dismissed a match
|
|
||||||
'contacts:matches:dismiss': {
|
|
||||||
entryPoint: 'Onboarding' | 'Standalone'
|
|
||||||
}
|
|
||||||
// user pressed invite to send an SMS to a non-match
|
|
||||||
'contacts:matches:invite': {
|
|
||||||
entryPoint: 'Onboarding' | 'Standalone'
|
|
||||||
}
|
|
||||||
// user opened the Find Contacts settings screen
|
|
||||||
'contacts:settings:presented': {
|
|
||||||
hasPreviouslySynced: boolean
|
|
||||||
matchCount?: number
|
|
||||||
}
|
|
||||||
// user followed a single match from settings
|
|
||||||
'contacts:settings:follow': {}
|
|
||||||
// user pressed "Follow All" from settings
|
|
||||||
'contacts:settings:followAll': {
|
|
||||||
followCount: number
|
|
||||||
}
|
|
||||||
// user dismissed a match from settings
|
|
||||||
'contacts:settings:dismiss': {}
|
|
||||||
// user re-entered the flow via the resync button
|
|
||||||
'contacts:settings:resync': {
|
|
||||||
daysSinceLastSync: number
|
|
||||||
}
|
|
||||||
// user pressed the remove all data button
|
|
||||||
'contacts:settings:removeData': {}
|
|
||||||
|
|
||||||
'liveEvents:feedBanner:seen': {
|
|
||||||
feed: string
|
|
||||||
context: LiveEventFeedMetricContext
|
|
||||||
}
|
|
||||||
'liveEvents:feedBanner:click': {
|
|
||||||
feed: string
|
|
||||||
context: LiveEventFeedMetricContext
|
|
||||||
}
|
|
||||||
'liveEvents:feedBanner:hide': {
|
|
||||||
feed: string
|
|
||||||
context: LiveEventFeedMetricContext
|
|
||||||
}
|
|
||||||
'liveEvents:feedBanner:unhide': {
|
|
||||||
feed: string
|
|
||||||
context: LiveEventFeedMetricContext
|
|
||||||
}
|
|
||||||
'liveEvents:hideAllFeedBanners': {
|
|
||||||
context: LiveEventFeedMetricContext
|
|
||||||
}
|
|
||||||
'liveEvents:unhideAllFeedBanners': {
|
|
||||||
context: LiveEventFeedMetricContext
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
import {getCurrentState, onAppStateChange} from '#/lib/appState'
|
|
||||||
import {MetricsClient} from '#/logger/metrics/client'
|
|
||||||
|
|
||||||
export {type Metrics} from '#/logger/metrics/events'
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Active metrics client
|
|
||||||
*/
|
|
||||||
export const metrics = new MetricsClient()
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Passive metrics go here
|
|
||||||
*/
|
|
||||||
|
|
||||||
let lastActive = getCurrentState() === 'active' ? performance.now() : null
|
|
||||||
onAppStateChange(state => {
|
|
||||||
if (state === 'active') {
|
|
||||||
lastActive = performance.now()
|
|
||||||
metrics.track('state:foreground', {})
|
|
||||||
} else if (lastActive !== null) {
|
|
||||||
metrics.track('state:background', {
|
|
||||||
secondsActive: Math.round((performance.now() - lastActive) / 1e3),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
@@ -24,7 +24,6 @@ import {
|
|||||||
} from '#/lib/constants'
|
} from '#/lib/constants'
|
||||||
import {getAge} from '#/lib/strings/time'
|
import {getAge} from '#/lib/strings/time'
|
||||||
import {logger} from '#/logger'
|
import {logger} from '#/logger'
|
||||||
import {setUserMetadata} from '#/logger/metadata'
|
|
||||||
import {snoozeBirthdateUpdateAllowedForDid} from '#/state/birthdate'
|
import {snoozeBirthdateUpdateAllowedForDid} from '#/state/birthdate'
|
||||||
import {snoozeEmailConfirmationPrompt} from '#/state/shell/reminders'
|
import {snoozeEmailConfirmationPrompt} from '#/state/shell/reminders'
|
||||||
import {
|
import {
|
||||||
@@ -64,7 +63,6 @@ export async function createAgentAndResume(
|
|||||||
if (storedAccount.pdsUrl) {
|
if (storedAccount.pdsUrl) {
|
||||||
agent.sessionManager.pdsUrl = new URL(storedAccount.pdsUrl)
|
agent.sessionManager.pdsUrl = new URL(storedAccount.pdsUrl)
|
||||||
}
|
}
|
||||||
setUserMetadata(storedAccount)
|
|
||||||
const gates = features.refresh({
|
const gates = features.refresh({
|
||||||
strategy: 'prefer-low-latency',
|
strategy: 'prefer-low-latency',
|
||||||
})
|
})
|
||||||
@@ -127,7 +125,6 @@ export async function createAgentAndLogin(
|
|||||||
})
|
})
|
||||||
|
|
||||||
const account = agentToSessionAccountOrThrow(agent)
|
const account = agentToSessionAccountOrThrow(agent)
|
||||||
setUserMetadata(account)
|
|
||||||
const gates = features.refresh({strategy: 'prefer-fresh-gates'})
|
const gates = features.refresh({strategy: 'prefer-fresh-gates'})
|
||||||
const moderation = configureModerationForAccount(agent, account)
|
const moderation = configureModerationForAccount(agent, account)
|
||||||
const aa = prefetchAgeAssuranceData({agent})
|
const aa = prefetchAgeAssuranceData({agent})
|
||||||
@@ -176,7 +173,6 @@ export async function createAgentAndCreateAccount(
|
|||||||
verificationCode,
|
verificationCode,
|
||||||
})
|
})
|
||||||
const account = agentToSessionAccountOrThrow(agent)
|
const account = agentToSessionAccountOrThrow(agent)
|
||||||
setUserMetadata(account)
|
|
||||||
const gates = features.refresh({strategy: 'prefer-fresh-gates'})
|
const gates = features.refresh({strategy: 'prefer-fresh-gates'})
|
||||||
const moderation = configureModerationForAccount(agent, account)
|
const moderation = configureModerationForAccount(agent, account)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user