From 7dc3b4fa8eda951fe6de91981d3293e14d237993 Mon Sep 17 00:00:00 2001 From: DS Boyce <260543580+ds-boyce@users.noreply.github.com> Date: Tue, 3 Mar 2026 12:49:38 -0800 Subject: [PATCH] Add search event analytics (#9949) Co-authored-by: Eric Bailey --- src/analytics/metrics/types.ts | 26 ++++ src/components/FeedCard.tsx | 48 +++--- src/components/ProfileCard.tsx | 88 +++++------ src/components/forms/SearchInput.tsx | 15 +- src/screens/Search/SearchResults.tsx | 147 ++++++++++++++++-- src/screens/Search/Shell.tsx | 18 ++- .../Search/components/AutocompleteResults.tsx | 8 +- .../Search/components/SearchHistory.tsx | 33 ++-- src/view/com/profile/ProfileCard.tsx | 5 +- src/view/shell/desktop/Search.tsx | 23 ++- 10 files changed, 291 insertions(+), 120 deletions(-) diff --git a/src/analytics/metrics/types.ts b/src/analytics/metrics/types.ts index efb002f40c..28c3150c26 100644 --- a/src/analytics/metrics/types.ts +++ b/src/analytics/metrics/types.ts @@ -649,6 +649,32 @@ export type Events = { tab: string } + 'search:query': { + source: 'typed' | 'history' | 'autocomplete' + } + + 'search:results:loaded': { + tab: 'top' | 'latest' | 'people' | 'feeds' + initialCount: number + } + + 'search:result:press': { + tab?: 'top' | 'latest' | 'people' | 'feeds' + resultType: 'post' | 'profile' | 'feed' + position: number + uri: string + } + + 'search:recent:press': { + profileDid: string + position: number + } + + 'search:autocomplete:press': { + profileDid: string + position: number + } + 'progressGuide:hide': {} 'progressGuide:followDialog:open': {} diff --git a/src/components/FeedCard.tsx b/src/components/FeedCard.tsx index bf2245599d..8c087d17e7 100644 --- a/src/components/FeedCard.tsx +++ b/src/components/FeedCard.tsx @@ -1,4 +1,4 @@ -import React, {useMemo} from 'react' +import {useCallback, useEffect, useMemo} from 'react' import {type GestureResponderEvent, View} from 'react-native' import { type AppBskyFeedDefs, @@ -6,9 +6,7 @@ import { AtUri, RichText as RichTextApi, } from '@atproto/api' -import {msg} from '@lingui/core/macro' -import {useLingui} from '@lingui/react' -import {Plural, Trans} from '@lingui/react/macro' +import {Plural, Trans, useLingui} from '@lingui/react/macro' import {useQueryClient} from '@tanstack/react-query' import {sanitizeHandle} from '#/lib/strings/handles' @@ -73,11 +71,11 @@ export function Link({ }: Props & Omit) { const queryClient = useQueryClient() - const href = React.useMemo(() => { + const href = useMemo(() => { return createProfileFeedHref({feed: view}) }, [view]) - React.useEffect(() => { + useEffect(() => { precacheFeedFromGeneratorView(queryClient, view) }, [view, queryClient]) @@ -212,7 +210,7 @@ export function Description({ description, ...rest }: {description?: string} & Partial) { - const rt = React.useMemo(() => { + const rt = useMemo(() => { if (!description) return const rt = new RichTextApi({text: description || ''}) rt.detectFacetsWithoutResolution() @@ -279,7 +277,7 @@ function SaveButtonInner({ pin?: boolean text?: boolean } & Partial) { - const {_} = useLingui() + const {t: l} = useLingui() const {data: preferences} = usePreferencesQuery() const {isPending: isAddSavedFeedPending, mutateAsync: saveFeeds} = useAddSavedFeedsMutation() @@ -289,13 +287,13 @@ function SaveButtonInner({ const uri = view.uri const type = view.uri.includes('app.bsky.feed.generator') ? 'feed' : 'list' - const savedFeedConfig = React.useMemo(() => { + const savedFeedConfig = useMemo(() => { return preferences?.savedFeeds?.find(feed => feed.value === uri) }, [preferences?.savedFeeds, uri]) const removePromptControl = Prompt.usePromptControl() const isPending = isAddSavedFeedPending || isRemovePending - const toggleSave = React.useCallback( + const toggleSave = useCallback( async (e: GestureResponderEvent) => { e.preventDefault() e.stopPropagation() @@ -312,17 +310,17 @@ function SaveButtonInner({ }, ]) } - Toast.show(_(msg({message: 'Feeds updated!', context: 'toast'}))) + Toast.show(l({message: 'Feeds updated!', context: 'toast'})) } catch (err: any) { logger.error(err, {message: `FeedCard: failed to update feeds`, pin}) - Toast.show(_(msg`Failed to update feeds`), 'xmark') + Toast.show(l`Failed to update feeds`, 'xmark') } }, - [_, pin, saveFeeds, removeFeed, uri, savedFeedConfig, type], + [l, pin, saveFeeds, removeFeed, uri, savedFeedConfig, type], ) - const onPrompRemoveFeed = React.useCallback( - async (e: GestureResponderEvent) => { + const onPromptRemoveFeed = useCallback( + (e: GestureResponderEvent) => { e.preventDefault() e.stopPropagation() @@ -335,11 +333,13 @@ function SaveButtonInner({ <>