diff --git a/src/Navigation.tsx b/src/Navigation.tsx index 83aede72..ab40ff42 100644 --- a/src/Navigation.tsx +++ b/src/Navigation.tsx @@ -1,86 +1,86 @@ import * as React from 'react' -import { - NavigationContainer, - createNavigationContainerRef, - CommonActions, - StackActions, - DefaultTheme, - DarkTheme, -} from '@react-navigation/native' +import {JSX} from 'react/jsx-runtime' +import {i18n, MessageDescriptor} from '@lingui/core' +import {msg} from '@lingui/macro' import { BottomTabBarProps, createBottomTabNavigator, } from '@react-navigation/bottom-tabs' import { - HomeTabNavigatorParams, - SearchTabNavigatorParams, - FeedsTabNavigatorParams, - NotificationsTabNavigatorParams, - FlatNavigatorParams, - AllNavigatorParams, - MyProfileTabNavigatorParams, - BottomTabNavigatorParams, -} from 'lib/routes/types' -import {BottomBar} from './view/shell/bottom-bar/BottomBar' -import {buildStateObject} from 'lib/routes/helpers' -import {State, RouteParams} from 'lib/routes/types' -import {isAndroid, isNative} from 'platform/detection' -import {useColorSchemeStyle} from 'lib/hooks/useColorSchemeStyle' -import {router} from './routes' -import {usePalette} from 'lib/hooks/usePalette' -import {bskyTitle} from 'lib/strings/headings' -import {JSX} from 'react/jsx-runtime' -import {timeout} from 'lib/async/timeout' -import {useUnreadNotifications} from './state/queries/notifications/unread' -import {useSession} from './state/session' -import {useModalControls} from './state/modals' -import { - shouldRequestEmailConfirmation, - setEmailConfirmationRequested, -} from './state/shell/reminders' -import {init as initAnalytics} from './lib/analytics/analytics' -import {useWebScrollRestoration} from './lib/hooks/useWebScrollRestoration' + CommonActions, + createNavigationContainerRef, + DarkTheme, + DefaultTheme, + NavigationContainer, + StackActions, +} from '@react-navigation/native' -import {HomeScreen} from './view/screens/Home' -import {SearchScreen} from './view/screens/Search' -import {FeedsScreen} from './view/screens/Feeds' -import {NotificationsScreen} from './view/screens/Notifications' -import {ListsScreen} from './view/screens/Lists' -import {ModerationScreen} from '#/screens/Moderation' -import {ModerationModlistsScreen} from './view/screens/ModerationModlists' -import {NotFoundScreen} from './view/screens/NotFound' -import {SettingsScreen} from './view/screens/Settings' -import {LanguageSettingsScreen} from './view/screens/LanguageSettings' -import {ProfileScreen} from './view/screens/Profile' -import {ProfileFollowersScreen} from './view/screens/ProfileFollowers' -import {ProfileFollowsScreen} from './view/screens/ProfileFollows' -import {ProfileFeedScreen} from './view/screens/ProfileFeed' -import {ProfileFeedLikedByScreen} from './view/screens/ProfileFeedLikedBy' -import {ProfileListScreen} from './view/screens/ProfileList' -import {PostThreadScreen} from './view/screens/PostThread' -import {PostLikedByScreen} from './view/screens/PostLikedBy' -import {PostRepostedByScreen} from './view/screens/PostRepostedBy' -import {Storybook} from './view/screens/Storybook' -import {DebugModScreen} from './view/screens/DebugMod' -import {LogScreen} from './view/screens/Log' -import {SupportScreen} from './view/screens/Support' -import {PrivacyPolicyScreen} from './view/screens/PrivacyPolicy' -import {TermsOfServiceScreen} from './view/screens/TermsOfService' -import {CommunityGuidelinesScreen} from './view/screens/CommunityGuidelines' -import {CopyrightPolicyScreen} from './view/screens/CopyrightPolicy' +import {timeout} from 'lib/async/timeout' +import {useColorSchemeStyle} from 'lib/hooks/useColorSchemeStyle' +import {usePalette} from 'lib/hooks/usePalette' +import {buildStateObject} from 'lib/routes/helpers' +import { + AllNavigatorParams, + BottomTabNavigatorParams, + FeedsTabNavigatorParams, + FlatNavigatorParams, + HomeTabNavigatorParams, + MyProfileTabNavigatorParams, + NotificationsTabNavigatorParams, + SearchTabNavigatorParams, +} from 'lib/routes/types' +import {RouteParams, State} from 'lib/routes/types' +import {bskyTitle} from 'lib/strings/headings' +import {isAndroid, isNative} from 'platform/detection' +import {PreferencesExternalEmbeds} from '#/view/screens/PreferencesExternalEmbeds' import {AppPasswords} from 'view/screens/AppPasswords' -import {ModerationMutedAccounts} from 'view/screens/ModerationMutedAccounts' import {ModerationBlockedAccounts} from 'view/screens/ModerationBlockedAccounts' -import {SavedFeeds} from 'view/screens/SavedFeeds' +import {ModerationMutedAccounts} from 'view/screens/ModerationMutedAccounts' import {PreferencesFollowingFeed} from 'view/screens/PreferencesFollowingFeed' import {PreferencesThreads} from 'view/screens/PreferencesThreads' -import {PreferencesExternalEmbeds} from '#/view/screens/PreferencesExternalEmbeds' -import {createNativeStackNavigatorWithAuth} from './view/shell/createNativeStackNavigatorWithAuth' -import {msg} from '@lingui/macro' -import {i18n, MessageDescriptor} from '@lingui/core' +import {SavedFeeds} from 'view/screens/SavedFeeds' import HashtagScreen from '#/screens/Hashtag' +import {ModerationScreen} from '#/screens/Moderation' import {ProfileLabelerLikedByScreen} from '#/screens/Profile/ProfileLabelerLikedBy' -import {logEvent, attachRouteToLogEvents} from './lib/statsig/statsig' +import {init as initAnalytics} from './lib/analytics/analytics' +import {useWebScrollRestoration} from './lib/hooks/useWebScrollRestoration' +import {attachRouteToLogEvents, logEvent} from './lib/statsig/statsig' +import {router} from './routes' +import {useModalControls} from './state/modals' +import {useUnreadNotifications} from './state/queries/notifications/unread' +import {useSession} from './state/session' +import { + setEmailConfirmationRequested, + shouldRequestEmailConfirmation, +} from './state/shell/reminders' +import {CommunityGuidelinesScreen} from './view/screens/CommunityGuidelines' +import {CopyrightPolicyScreen} from './view/screens/CopyrightPolicy' +import {DebugModScreen} from './view/screens/DebugMod' +import {FeedsScreen} from './view/screens/Feeds' +import {HomeScreen} from './view/screens/Home' +import {LanguageSettingsScreen} from './view/screens/LanguageSettings' +import {ListsScreen} from './view/screens/Lists' +import {LogScreen} from './view/screens/Log' +import {ModerationModlistsScreen} from './view/screens/ModerationModlists' +import {NotFoundScreen} from './view/screens/NotFound' +import {NotificationsScreen} from './view/screens/Notifications' +import {PostLikedByScreen} from './view/screens/PostLikedBy' +import {PostRepostedByScreen} from './view/screens/PostRepostedBy' +import {PostThreadScreen} from './view/screens/PostThread' +import {PrivacyPolicyScreen} from './view/screens/PrivacyPolicy' +import {ProfileScreen} from './view/screens/Profile' +import {ProfileFeedScreen} from './view/screens/ProfileFeed' +import {ProfileFeedLikedByScreen} from './view/screens/ProfileFeedLikedBy' +import {ProfileFollowersScreen} from './view/screens/ProfileFollowers' +import {ProfileFollowsScreen} from './view/screens/ProfileFollows' +import {ProfileListScreen} from './view/screens/ProfileList' +import {SearchScreen} from './view/screens/Search' +import {SettingsScreen} from './view/screens/Settings' +import {Storybook} from './view/screens/Storybook' +import {SupportScreen} from './view/screens/Support' +import {TermsOfServiceScreen} from './view/screens/TermsOfService' +import {BottomBar} from './view/shell/bottom-bar/BottomBar' +import {createNativeStackNavigatorWithAuth} from './view/shell/createNativeStackNavigatorWithAuth' const navigationRef = createNavigationContainerRef() @@ -554,10 +554,14 @@ function RoutesContainer({children}: React.PropsWithChildren<{}>) { ref={navigationRef} linking={LINKING} theme={theme} + onStateChange={() => { + logEvent('router:navigate', {}) + }} onReady={() => { attachRouteToLogEvents(getCurrentRouteName) logModuleInitTime() onReady() + logEvent('router:navigate', {}) }}> {children} @@ -693,11 +697,11 @@ function logModuleInitTime() { } export { - navigate, - resetToTab, - reset, - handleLink, - TabsNavigator, FlatNavigator, + handleLink, + navigate, + reset, + resetToTab, RoutesContainer, + TabsNavigator, } diff --git a/src/lib/statsig/events.ts b/src/lib/statsig/events.ts index b8309597..2de15b64 100644 --- a/src/lib/statsig/events.ts +++ b/src/lib/statsig/events.ts @@ -1,4 +1,5 @@ export type LogEvents = { + // App events init: { initMs: number } @@ -14,6 +15,35 @@ export type LogEvents = { secondsActive: number } 'state:foreground': {} + 'router:navigate': {} + + // Screen events + 'splash:signInPressed': {} + 'splash:createAccountPressed': {} + 'signup:nextPressed': { + activeStep: number + } + 'onboarding:interests:nextPressed': { + selectedInterests: string[] + selectedInterestsLength: 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:finished:nextPressed': {} 'feed:endReached': { feedType: string itemCount: number @@ -22,6 +52,10 @@ export type LogEvents = { feedType: string reason: 'pull-to-refresh' | 'soft-reset' | 'load-latest' } + + // Data events + 'account:create:begin': {} + 'account:create:success': {} 'post:create': { imageCount: number isReply: boolean diff --git a/src/lib/statsig/statsig.tsx b/src/lib/statsig/statsig.tsx index 9fa6cce2..9bccedc6 100644 --- a/src/lib/statsig/statsig.tsx +++ b/src/lib/statsig/statsig.tsx @@ -1,13 +1,14 @@ import React from 'react' import {Platform} from 'react-native' +import {AppState, AppStateStatus} from 'react-native' +import {sha256} from 'js-sha256' import { Statsig, StatsigProvider, useGate as useStatsigGate, } from 'statsig-react-native-expo' -import {AppState, AppStateStatus} from 'react-native' + import {useSession} from '../../state/session' -import {sha256} from 'js-sha256' import {LogEvents} from './events' export type {LogEvents} @@ -24,7 +25,13 @@ const statsigOptions = { type FlatJSONRecord = Record< string, - string | number | boolean | null | undefined + | string + | number + | boolean + | null + | undefined + // Technically not scalar but Statsig will stringify it which works for us: + | string[] > let getCurrentRouteName: () => string | null | undefined = () => null diff --git a/src/screens/Onboarding/StepAlgoFeeds/index.tsx b/src/screens/Onboarding/StepAlgoFeeds/index.tsx index 1a4e4c49..35f525ef 100644 --- a/src/screens/Onboarding/StepAlgoFeeds/index.tsx +++ b/src/screens/Onboarding/StepAlgoFeeds/index.tsx @@ -1,26 +1,26 @@ import React from 'react' import {View} from 'react-native' -import {useLingui} from '@lingui/react' import {msg, Trans} from '@lingui/macro' +import {useLingui} from '@lingui/react' -import {IS_PROD} from '#/env' -import {atoms as a, tokens, useTheme} from '#/alf' -import {ChevronRight_Stroke2_Corner0_Rounded as ChevronRight} from '#/components/icons/Chevron' -import {Button, ButtonIcon, ButtonText} from '#/components/Button' -import * as Toggle from '#/components/forms/Toggle' -import {Text} from '#/components/Typography' -import {Loader} from '#/components/Loader' -import {ListSparkle_Stroke2_Corner0_Rounded as ListSparkle} from '#/components/icons/ListSparkle' import {useAnalytics} from '#/lib/analytics/analytics' - -import {Context} from '#/screens/Onboarding/state' +import {logEvent} from '#/lib/statsig/statsig' import { - Title, Description, OnboardingControls, + Title, } from '#/screens/Onboarding/Layout' +import {Context} from '#/screens/Onboarding/state' import {FeedCard} from '#/screens/Onboarding/StepAlgoFeeds/FeedCard' +import {atoms as a, tokens, useTheme} from '#/alf' +import {Button, ButtonIcon, ButtonText} from '#/components/Button' +import * as Toggle from '#/components/forms/Toggle' import {IconCircle} from '#/components/IconCircle' +import {ChevronRight_Stroke2_Corner0_Rounded as ChevronRight} from '#/components/icons/Chevron' +import {ListSparkle_Stroke2_Corner0_Rounded as ListSparkle} from '#/components/icons/ListSparkle' +import {Loader} from '#/components/Loader' +import {Text} from '#/components/Typography' +import {IS_PROD} from '#/env' export type FeedConfig = { default: boolean @@ -89,6 +89,12 @@ export function StepAlgoFeeds() { selectedSecondaryFeeds: secondaryFeedUris, selectedSecondaryFeedsLength: secondaryFeedUris.length, }) + logEvent('onboarding:algoFeeds:nextPressed', { + selectedPrimaryFeeds: primaryFeedUris, + selectedPrimaryFeedsLength: primaryFeedUris.length, + selectedSecondaryFeeds: secondaryFeedUris, + selectedSecondaryFeedsLength: secondaryFeedUris.length, + }) }, [primaryFeedUris, secondaryFeedUris, dispatch, track]) React.useEffect(() => { diff --git a/src/screens/Onboarding/StepFinished.tsx b/src/screens/Onboarding/StepFinished.tsx index 944dcb96..0c81d2d2 100644 --- a/src/screens/Onboarding/StepFinished.tsx +++ b/src/screens/Onboarding/StepFinished.tsx @@ -1,33 +1,33 @@ import React from 'react' import {View} from 'react-native' -import {useLingui} from '@lingui/react' import {msg, Trans} from '@lingui/macro' +import {useLingui} from '@lingui/react' +import {useAnalytics} from '#/lib/analytics/analytics' +import {logEvent} from '#/lib/statsig/statsig' import {logger} from '#/logger' -import {atoms as a, useTheme} from '#/alf' -import {Button, ButtonText, ButtonIcon} from '#/components/Button' -import {News2_Stroke2_Corner0_Rounded as News} from '#/components/icons/News2' -import {Check_Stroke2_Corner0_Rounded as Check} from '#/components/icons/Check' -import {Growth_Stroke2_Corner0_Rounded as Growth} from '#/components/icons/Growth' -import {Trending2_Stroke2_Corner2_Rounded as Trending} from '#/components/icons/Trending2' -import {Text} from '#/components/Typography' -import {useOnboardingDispatch} from '#/state/shell' -import {Loader} from '#/components/Loader' import {useSetSaveFeedsMutation} from '#/state/queries/preferences' import {getAgent} from '#/state/session' -import {useAnalytics} from '#/lib/analytics/analytics' - -import {Context} from '#/screens/Onboarding/state' +import {useOnboardingDispatch} from '#/state/shell' import { - Title, Description, OnboardingControls, + Title, } from '#/screens/Onboarding/Layout' -import {IconCircle} from '#/components/IconCircle' +import {Context} from '#/screens/Onboarding/state' import { bulkWriteFollows, sortPrimaryAlgorithmFeeds, } from '#/screens/Onboarding/util' +import {atoms as a, useTheme} from '#/alf' +import {Button, ButtonIcon, ButtonText} from '#/components/Button' +import {IconCircle} from '#/components/IconCircle' +import {Check_Stroke2_Corner0_Rounded as Check} from '#/components/icons/Check' +import {Growth_Stroke2_Corner0_Rounded as Growth} from '#/components/icons/Growth' +import {News2_Stroke2_Corner0_Rounded as News} from '#/components/icons/News2' +import {Trending2_Stroke2_Corner2_Rounded as Trending} from '#/components/icons/Trending2' +import {Loader} from '#/components/Loader' +import {Text} from '#/components/Typography' export function StepFinished() { const {_} = useLingui() @@ -76,6 +76,7 @@ export function StepFinished() { onboardDispatch({type: 'finish'}) track('OnboardingV2:StepFinished:End') track('OnboardingV2:Complete') + logEvent('onboarding:finished:nextPressed', {}) }, [state, dispatch, onboardDispatch, setSaving, saveFeeds, track]) React.useEffect(() => { diff --git a/src/screens/Onboarding/StepFollowingFeed.tsx b/src/screens/Onboarding/StepFollowingFeed.tsx index 898afad1..e886a089 100644 --- a/src/screens/Onboarding/StepFollowingFeed.tsx +++ b/src/screens/Onboarding/StepFollowingFeed.tsx @@ -1,28 +1,28 @@ import React from 'react' import {View} from 'react-native' -import {useLingui} from '@lingui/react' import {msg, Trans} from '@lingui/macro' +import {useLingui} from '@lingui/react' -import {atoms as a} from '#/alf' -import {ChevronRight_Stroke2_Corner0_Rounded as ChevronRight} from '#/components/icons/Chevron' -import {FilterTimeline_Stroke2_Corner0_Rounded as FilterTimeline} from '#/components/icons/FilterTimeline' -import {Button, ButtonIcon, ButtonText} from '#/components/Button' -import {Text} from '#/components/Typography' -import {Divider} from '#/components/Divider' -import * as Toggle from '#/components/forms/Toggle' import {useAnalytics} from '#/lib/analytics/analytics' - -import {Context} from '#/screens/Onboarding/state' -import { - Title, - Description, - OnboardingControls, -} from '#/screens/Onboarding/Layout' +import {logEvent} from '#/lib/statsig/statsig' import { usePreferencesQuery, useSetFeedViewPreferencesMutation, } from 'state/queries/preferences' +import { + Description, + OnboardingControls, + Title, +} from '#/screens/Onboarding/Layout' +import {Context} from '#/screens/Onboarding/state' +import {atoms as a} from '#/alf' +import {Button, ButtonIcon, ButtonText} from '#/components/Button' +import {Divider} from '#/components/Divider' +import * as Toggle from '#/components/forms/Toggle' import {IconCircle} from '#/components/IconCircle' +import {ChevronRight_Stroke2_Corner0_Rounded as ChevronRight} from '#/components/icons/Chevron' +import {FilterTimeline_Stroke2_Corner0_Rounded as FilterTimeline} from '#/components/icons/FilterTimeline' +import {Text} from '#/components/Typography' export function StepFollowingFeed() { const {_} = useLingui() @@ -46,6 +46,7 @@ export function StepFollowingFeed() { const onContinue = React.useCallback(() => { dispatch({type: 'next'}) track('OnboardingV2:StepFollowingFeed:End') + logEvent('onboarding:followingFeed:nextPressed', {}) }, [track, dispatch]) React.useEffect(() => { diff --git a/src/screens/Onboarding/StepInterests/index.tsx b/src/screens/Onboarding/StepInterests/index.tsx index ea23b74f..8f34cced 100644 --- a/src/screens/Onboarding/StepInterests/index.tsx +++ b/src/screens/Onboarding/StepInterests/index.tsx @@ -1,32 +1,32 @@ import React from 'react' import {View} from 'react-native' -import {useLingui} from '@lingui/react' import {msg, Trans} from '@lingui/macro' +import {useLingui} from '@lingui/react' import {useQuery} from '@tanstack/react-query' -import {logger} from '#/logger' -import {atoms as a, useBreakpoints, useTheme} from '#/alf' -import {ChevronRight_Stroke2_Corner0_Rounded as ChevronRight} from '#/components/icons/Chevron' -import {Hashtag_Stroke2_Corner0_Rounded as Hashtag} from '#/components/icons/Hashtag' -import {EmojiSad_Stroke2_Corner0_Rounded as EmojiSad} from '#/components/icons/Emoji' -import {ArrowRotateCounterClockwise_Stroke2_Corner0_Rounded as ArrowRotateCounterClockwise} from '#/components/icons/ArrowRotateCounterClockwise' -import {Button, ButtonIcon, ButtonText} from '#/components/Button' -import {Loader} from '#/components/Loader' -import * as Toggle from '#/components/forms/Toggle' -import {getAgent} from '#/state/session' import {useAnalytics} from '#/lib/analytics/analytics' -import {Text} from '#/components/Typography' -import {useOnboardingDispatch} from '#/state/shell' +import {logEvent} from '#/lib/statsig/statsig' import {capitalize} from '#/lib/strings/capitalize' - -import {Context, ApiResponseMap} from '#/screens/Onboarding/state' +import {logger} from '#/logger' +import {getAgent} from '#/state/session' +import {useOnboardingDispatch} from '#/state/shell' import { - Title, Description, OnboardingControls, + Title, } from '#/screens/Onboarding/Layout' +import {ApiResponseMap, Context} from '#/screens/Onboarding/state' import {InterestButton} from '#/screens/Onboarding/StepInterests/InterestButton' +import {atoms as a, useBreakpoints, useTheme} from '#/alf' +import {Button, ButtonIcon, ButtonText} from '#/components/Button' +import * as Toggle from '#/components/forms/Toggle' import {IconCircle} from '#/components/IconCircle' +import {ArrowRotateCounterClockwise_Stroke2_Corner0_Rounded as ArrowRotateCounterClockwise} from '#/components/icons/ArrowRotateCounterClockwise' +import {ChevronRight_Stroke2_Corner0_Rounded as ChevronRight} from '#/components/icons/Chevron' +import {EmojiSad_Stroke2_Corner0_Rounded as EmojiSad} from '#/components/icons/Emoji' +import {Hashtag_Stroke2_Corner0_Rounded as Hashtag} from '#/components/icons/Hashtag' +import {Loader} from '#/components/Loader' +import {Text} from '#/components/Typography' export function StepInterests() { const {_} = useLingui() @@ -107,6 +107,10 @@ export function StepInterests() { selectedInterests: interests, selectedInterestsLength: interests.length, }) + logEvent('onboarding:interests:nextPressed', { + selectedInterests: interests, + selectedInterestsLength: interests.length, + }) } catch (e: any) { logger.info(`onboading: error saving interests`) logger.error(e) diff --git a/src/screens/Onboarding/StepModeration/index.tsx b/src/screens/Onboarding/StepModeration/index.tsx index 9b52f9f4..c5bdf562 100644 --- a/src/screens/Onboarding/StepModeration/index.tsx +++ b/src/screens/Onboarding/StepModeration/index.tsx @@ -1,27 +1,27 @@ import React from 'react' import {View} from 'react-native' -import {useLingui} from '@lingui/react' -import {msg, Trans} from '@lingui/macro' import {LABELS} from '@atproto/api' +import {msg, Trans} from '@lingui/macro' +import {useLingui} from '@lingui/react' -import {atoms as a} from '#/alf' -import {usePreferencesSetAdultContentMutation} from 'state/queries/preferences' -import {Button, ButtonIcon, ButtonText} from '#/components/Button' -import {ChevronRight_Stroke2_Corner0_Rounded as ChevronRight} from '#/components/icons/Chevron' -import {EyeSlash_Stroke2_Corner0_Rounded as EyeSlash} from '#/components/icons/EyeSlash' -import {usePreferencesQuery} from '#/state/queries/preferences' -import {Loader} from '#/components/Loader' import {useAnalytics} from '#/lib/analytics/analytics' - +import {logEvent} from '#/lib/statsig/statsig' +import {usePreferencesQuery} from '#/state/queries/preferences' +import {usePreferencesSetAdultContentMutation} from 'state/queries/preferences' import { Description, OnboardingControls, Title, } from '#/screens/Onboarding/Layout' -import {ModerationOption} from '#/screens/Onboarding/StepModeration/ModerationOption' -import {AdultContentEnabledPref} from '#/screens/Onboarding/StepModeration/AdultContentEnabledPref' import {Context} from '#/screens/Onboarding/state' +import {AdultContentEnabledPref} from '#/screens/Onboarding/StepModeration/AdultContentEnabledPref' +import {ModerationOption} from '#/screens/Onboarding/StepModeration/ModerationOption' +import {atoms as a} from '#/alf' +import {Button, ButtonIcon, ButtonText} from '#/components/Button' import {IconCircle} from '#/components/IconCircle' +import {ChevronRight_Stroke2_Corner0_Rounded as ChevronRight} from '#/components/icons/Chevron' +import {EyeSlash_Stroke2_Corner0_Rounded as EyeSlash} from '#/components/icons/EyeSlash' +import {Loader} from '#/components/Loader' export function StepModeration() { const {_} = useLingui() @@ -45,6 +45,7 @@ export function StepModeration() { const onContinue = React.useCallback(() => { dispatch({type: 'next'}) track('OnboardingV2:StepModeration:End') + logEvent('onboarding:moderation:nextPressed', {}) }, [track, dispatch]) React.useEffect(() => { diff --git a/src/screens/Onboarding/StepSuggestedAccounts/index.tsx b/src/screens/Onboarding/StepSuggestedAccounts/index.tsx index bdf94d82..2e616136 100644 --- a/src/screens/Onboarding/StepSuggestedAccounts/index.tsx +++ b/src/screens/Onboarding/StepSuggestedAccounts/index.tsx @@ -1,33 +1,33 @@ import React from 'react' import {View} from 'react-native' import {AppBskyActorDefs} from '@atproto/api' -import {useLingui} from '@lingui/react' import {msg, Trans} from '@lingui/macro' +import {useLingui} from '@lingui/react' -import {atoms as a, useBreakpoints} from '#/alf' -import {PlusLarge_Stroke2_Corner0_Rounded as Plus} from '#/components/icons/Plus' -import {At_Stroke2_Corner0_Rounded as At} from '#/components/icons/At' -import {Button, ButtonIcon, ButtonText} from '#/components/Button' -import {Text} from '#/components/Typography' -import {useProfilesQuery} from '#/state/queries/profile' -import {Loader} from '#/components/Loader' -import * as Toggle from '#/components/forms/Toggle' -import {useModerationOpts} from '#/state/queries/preferences' import {useAnalytics} from '#/lib/analytics/analytics' +import {logEvent} from '#/lib/statsig/statsig' import {capitalize} from '#/lib/strings/capitalize' - -import {Context} from '#/screens/Onboarding/state' +import {useModerationOpts} from '#/state/queries/preferences' +import {useProfilesQuery} from '#/state/queries/profile' import { - Title, Description, OnboardingControls, + Title, } from '#/screens/Onboarding/Layout' +import {Context} from '#/screens/Onboarding/state' import { SuggestedAccountCard, SuggestedAccountCardPlaceholder, } from '#/screens/Onboarding/StepSuggestedAccounts/SuggestedAccountCard' import {aggregateInterestItems} from '#/screens/Onboarding/util' +import {atoms as a, useBreakpoints} from '#/alf' +import {Button, ButtonIcon, ButtonText} from '#/components/Button' +import * as Toggle from '#/components/forms/Toggle' import {IconCircle} from '#/components/IconCircle' +import {At_Stroke2_Corner0_Rounded as At} from '#/components/icons/At' +import {PlusLarge_Stroke2_Corner0_Rounded as Plus} from '#/components/icons/Plus' +import {Loader} from '#/components/Loader' +import {Text} from '#/components/Typography' export function Inner({ profiles, @@ -110,12 +110,20 @@ export function StepSuggestedAccounts() { track('OnboardingV2:StepSuggestedAccounts:End', { selectedAccountsLength: dids.length, }) + logEvent('onboarding:suggestedAccounts:nextPressed', { + selectedAccountsLength: dids.length, + skipped: false, + }) }, [dids, setSaving, dispatch, track]) const handleSkip = React.useCallback(() => { // if a user comes back and clicks skip, erase follows dispatch({type: 'setSuggestedAccountsStepResults', accountDids: []}) dispatch({type: 'next'}) + logEvent('onboarding:suggestedAccounts:nextPressed', { + selectedAccountsLength: 0, + skipped: true, + }) }, [dispatch]) const isLoading = isProfilesLoading && moderationOpts diff --git a/src/screens/Onboarding/StepTopicalFeeds.tsx b/src/screens/Onboarding/StepTopicalFeeds.tsx index 089363c2..26b1c243 100644 --- a/src/screens/Onboarding/StepTopicalFeeds.tsx +++ b/src/screens/Onboarding/StepTopicalFeeds.tsx @@ -1,28 +1,28 @@ import React from 'react' import {View} from 'react-native' -import {useLingui} from '@lingui/react' import {msg, Trans} from '@lingui/macro' +import {useLingui} from '@lingui/react' -import {atoms as a} from '#/alf' -import {ChevronRight_Stroke2_Corner0_Rounded as ChevronRight} from '#/components/icons/Chevron' -import {ListMagnifyingGlass_Stroke2_Corner0_Rounded as ListMagnifyingGlass} from '#/components/icons/ListMagnifyingGlass' -import {Button, ButtonIcon, ButtonText} from '#/components/Button' -import * as Toggle from '#/components/forms/Toggle' -import {Loader} from '#/components/Loader' import {useAnalytics} from '#/lib/analytics/analytics' +import {logEvent} from '#/lib/statsig/statsig' import {capitalize} from '#/lib/strings/capitalize' - -import {Context} from '#/screens/Onboarding/state' -import { - Title, - Description, - OnboardingControls, -} from '#/screens/Onboarding/Layout' -import {FeedCard} from '#/screens/Onboarding/StepAlgoFeeds/FeedCard' -import {aggregateInterestItems} from '#/screens/Onboarding/util' -import {IconCircle} from '#/components/IconCircle' import {IS_TEST_USER} from 'lib/constants' import {useSession} from 'state/session' +import { + Description, + OnboardingControls, + Title, +} from '#/screens/Onboarding/Layout' +import {Context} from '#/screens/Onboarding/state' +import {FeedCard} from '#/screens/Onboarding/StepAlgoFeeds/FeedCard' +import {aggregateInterestItems} from '#/screens/Onboarding/util' +import {atoms as a} from '#/alf' +import {Button, ButtonIcon, ButtonText} from '#/components/Button' +import * as Toggle from '#/components/forms/Toggle' +import {IconCircle} from '#/components/IconCircle' +import {ChevronRight_Stroke2_Corner0_Rounded as ChevronRight} from '#/components/icons/Chevron' +import {ListMagnifyingGlass_Stroke2_Corner0_Rounded as ListMagnifyingGlass} from '#/components/icons/ListMagnifyingGlass' +import {Loader} from '#/components/Loader' export function StepTopicalFeeds() { const {_} = useLingui() @@ -62,6 +62,10 @@ export function StepTopicalFeeds() { selectedFeeds: selectedFeedUris, selectedFeedsLength: selectedFeedUris.length, }) + logEvent('onboarding:topicalFeeds:nextPressed', { + selectedFeeds: selectedFeedUris, + selectedFeedsLength: selectedFeedUris.length, + }) }, [selectedFeedUris, dispatch, track]) React.useEffect(() => { diff --git a/src/screens/Signup/index.tsx b/src/screens/Signup/index.tsx index f19823b4..b84f4d0d 100644 --- a/src/screens/Signup/index.tsx +++ b/src/screens/Signup/index.tsx @@ -5,6 +5,7 @@ import {useLingui} from '@lingui/react' import {useAnalytics} from '#/lib/analytics/analytics' import {FEEDBACK_FORM_URL} from '#/lib/constants' +import {logEvent} from '#/lib/statsig/statsig' import {createFullHandle} from '#/lib/strings/handles' import {useServiceQuery} from '#/state/queries/service' import {getAgent} from '#/state/session' @@ -99,6 +100,9 @@ export function Signup({onPressBack}: {onPressBack: () => void}) { } dispatch({type: 'next'}) + logEvent('signup:nextPressed', { + activeStep: state.activeStep, + }) }, [ _, state.activeStep, diff --git a/src/state/session/index.tsx b/src/state/session/index.tsx index b6748bfa..c7dba308 100644 --- a/src/state/session/index.tsx +++ b/src/state/session/index.tsx @@ -1,26 +1,26 @@ import React from 'react' import { - BskyAgent, AtpPersistSessionHandler, BSKY_LABELER_DID, + BskyAgent, } from '@atproto/api' import {useQueryClient} from '@tanstack/react-query' import {jwtDecode} from 'jwt-decode' -import {IS_DEV} from '#/env' -import {IS_TEST_USER} from '#/lib/constants' -import {isWeb} from '#/platform/detection' +import {track} from '#/lib/analytics/analytics' import {networkRetry} from '#/lib/async/retry' +import {IS_TEST_USER} from '#/lib/constants' +import {logEvent, LogEvents} from '#/lib/statsig/statsig' +import {hasProp} from '#/lib/type-guards' import {logger} from '#/logger' +import {isWeb} from '#/platform/detection' import * as persisted from '#/state/persisted' import {PUBLIC_BSKY_AGENT} from '#/state/queries' -import {emitSessionDropped} from '../events' import {useLoggedOutViewControls} from '#/state/shell/logged-out' import {useCloseAllActiveElements} from '#/state/util' -import {track} from '#/lib/analytics/analytics' -import {hasProp} from '#/lib/type-guards' +import {IS_DEV} from '#/env' +import {emitSessionDropped} from '../events' import {readLabelers} from './agent-config' -import {logEvent, LogEvents} from '#/lib/statsig/statsig' let __globalAgent: BskyAgent = PUBLIC_BSKY_AGENT @@ -230,6 +230,7 @@ export function Provider({children}: React.PropsWithChildren<{}>) { }: any) => { logger.info(`session: creating account`) track('Try Create Account') + logEvent('account:create:begin', {}) const agent = new BskyAgent({service}) @@ -290,6 +291,7 @@ export function Provider({children}: React.PropsWithChildren<{}>) { logger.debug(`session: created account`, {}, logger.DebugContext.session) track('Create Account') + logEvent('account:create:success', {}) }, [upsertAccount, queryClient, clearCurrentAccount], ) diff --git a/src/view/com/auth/LoggedOut.tsx b/src/view/com/auth/LoggedOut.tsx index b22bbb7f..c8c81dd7 100644 --- a/src/view/com/auth/LoggedOut.tsx +++ b/src/view/com/auth/LoggedOut.tsx @@ -1,27 +1,28 @@ import React from 'react' -import {View, Pressable} from 'react-native' +import {Pressable, View} from 'react-native' import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' +import {msg, Trans} from '@lingui/macro' import {useLingui} from '@lingui/react' -import {Trans, msg} from '@lingui/macro' import {useNavigation} from '@react-navigation/native' -import {isIOS, isNative} from '#/platform/detection' -import {Login} from '#/screens/Login' -import {Signup} from '#/screens/Signup' -import {ErrorBoundary} from '#/view/com/util/ErrorBoundary' -import {s} from '#/lib/styles' -import {usePalette} from '#/lib/hooks/usePalette' import {useAnalytics} from '#/lib/analytics/analytics' -import {SplashScreen} from './SplashScreen' -import {useSetMinimalShellMode} from '#/state/shell/minimal-mode' +import {usePalette} from '#/lib/hooks/usePalette' import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries' +import {logEvent} from '#/lib/statsig/statsig' +import {s} from '#/lib/styles' +import {isIOS, isNative} from '#/platform/detection' +import {useSession} from '#/state/session' import { useLoggedOutView, useLoggedOutViewControls, } from '#/state/shell/logged-out' -import {useSession} from '#/state/session' -import {Text} from '#/view/com/util/text/Text' +import {useSetMinimalShellMode} from '#/state/shell/minimal-mode' import {NavigationProp} from 'lib/routes/types' +import {ErrorBoundary} from '#/view/com/util/ErrorBoundary' +import {Text} from '#/view/com/util/text/Text' +import {Login} from '#/screens/Login' +import {Signup} from '#/screens/Signup' +import {SplashScreen} from './SplashScreen' enum ScreenState { S_LoginOrCreateAccount, @@ -133,10 +134,14 @@ export function LoggedOut({onDismiss}: {onDismiss?: () => void}) { {screenState === ScreenState.S_LoginOrCreateAccount ? ( setScreenState(ScreenState.S_Login)} - onPressCreateAccount={() => + onPressSignin={() => { + setScreenState(ScreenState.S_Login) + logEvent('splash:signInPressed', {}) + }} + onPressCreateAccount={() => { setScreenState(ScreenState.S_CreateAccount) - } + logEvent('splash:createAccountPressed', {}) + }} /> ) : undefined} {screenState === ScreenState.S_Login ? (