Adds more tracking all around the app (#142)

* Adds more tracking all around the app

* more events

* lint

* using better analytics naming

* missed file

* more fixes
This commit is contained in:
Aryan Goharzad
2023-02-02 19:48:36 -05:00
committed by GitHub
parent 8b310d91f0
commit cede0c772c
21 changed files with 196 additions and 24 deletions
+3 -12
View File
@@ -6,17 +6,14 @@ import {GestureHandlerRootView} from 'react-native-gesture-handler'
import SplashScreen from 'react-native-splash-screen'
import {SafeAreaProvider} from 'react-native-safe-area-context'
import {observer} from 'mobx-react-lite'
import {
createClient,
SegmentClient,
AnalyticsProvider,
} from '@segment/analytics-react-native'
import {SegmentClient, AnalyticsProvider} from '@segment/analytics-react-native'
import {ThemeProvider} from './view/lib/ThemeContext'
import * as view from './view/index'
import {RootStoreModel, setupState, RootStoreProvider} from './state'
import {MobileShell} from './view/shell/mobile'
import {s} from './view/lib/styles'
import notifee, {EventType} from '@notifee/react-native'
import {segmentClient} from './lib/segmentClient'
const App = observer(() => {
const [rootStore, setRootStore] = useState<RootStoreModel | undefined>(
@@ -27,12 +24,7 @@ const App = observer(() => {
// init
useEffect(() => {
view.setup()
setSegment(
createClient({
writeKey: '8I6DsgfiSLuoONyaunGoiQM7A6y2ybdI',
trackAppLifecycleEvents: true,
}),
)
setSegment(segmentClient)
setupState().then(store => {
setRootStore(store)
SplashScreen.hide()
@@ -58,7 +50,6 @@ const App = observer(() => {
if (!rootStore) {
return null
}
return (
<GestureHandlerRootView style={s.h100pct}>
<ThemeProvider theme={rootStore.shell.darkMode ? 'dark' : 'light'}>
+35
View File
@@ -0,0 +1,35 @@
export class Logger {
isDisabled: boolean
constructor(isDisabled: boolean = process.env.NODE_ENV === 'production') {
this.isDisabled = isDisabled
}
enable() {
this.isDisabled = false
}
disable() {
this.isDisabled = true
}
info(message?: any, ...optionalParams: any[]): void {
if (!this.isDisabled) {
console.info(message, ...optionalParams)
}
}
warn(message?: any, ...optionalParams: any[]): void {
if (!this.isDisabled) {
console.warn(message, ...optionalParams)
}
}
error(message?: any, ...optionalParams: any[]): void {
if (!this.isDisabled) {
console.error(message, ...optionalParams)
}
}
}
export const createLogger = (isDisabled?: boolean) => new Logger(isDisabled)
+9
View File
@@ -0,0 +1,9 @@
import {createClient} from '@segment/analytics-react-native'
// import {createLogger} from './logger'
export const segmentClient = createClient({
writeKey: '8I6DsgfiSLuoONyaunGoiQM7A6y2ybdI',
trackAppLifecycleEvents: true,
// Uncomment to debug:
// logger: createLogger(),
})
+8
View File
@@ -1,5 +1,6 @@
import {makeAutoObservable} from 'mobx'
import {TABS_ENABLED} from '../../build-flags'
import {segmentClient} from '../../lib/segmentClient'
let __id = 0
function genId() {
@@ -96,6 +97,13 @@ export class NavigationTabModel {
// =
navigate(url: string, title?: string) {
try {
const path = url.split('/')[1]
segmentClient.track('Navigation', {
path,
})
} catch (error) {}
if (this.current?.url === url) {
this.refresh()
} else {
+7 -1
View File
@@ -68,7 +68,7 @@ export const ComposePost = observer(function ComposePost({
onPost?: ComposerOpts['onPost']
onClose: () => void
}) {
const {track} = useAnalytics()
const {track, screen} = useAnalytics()
const pal = usePalette('default')
const store = useStores()
const textInput = useRef<PasteInputRef>(null)
@@ -88,6 +88,10 @@ export const ComposePost = observer(function ComposePost({
)
const [selectedPhotos, setSelectedPhotos] = useState<string[]>([])
useEffect(() => {
screen('ComposePost')
}, [screen])
// Using default import (React.use...) instead of named import (use...) to be able to mock store's data in jest environment
const autocompleteView = React.useMemo<UserAutocompleteViewModel>(
() => new UserAutocompleteViewModel(store),
@@ -183,6 +187,7 @@ export const ComposePost = observer(function ComposePost({
textInput.current?.focus()
}
const onPressSelectPhotos = () => {
track('ComposePost:SelectPhotos')
if (isSelectingPhotos) {
setIsSelectingPhotos(false)
} else if (selectedPhotos.length < 4) {
@@ -190,6 +195,7 @@ export const ComposePost = observer(function ComposePost({
}
}
const onSelectPhotos = (photos: string[]) => {
track('ComposePost:SelectPhotos:Done')
setSelectedPhotos(photos)
if (photos.length >= 4) {
setIsSelectingPhotos(false)
@@ -19,6 +19,7 @@ import {
import {compressIfNeeded, scaleDownDimensions} from '../../../lib/images'
import {usePalette} from '../../lib/hooks/usePalette'
import {useStores} from '../../../state'
import {useAnalytics} from '@segment/analytics-react-native'
const MAX_WIDTH = 2000
const MAX_HEIGHT = 2000
@@ -75,6 +76,7 @@ export const PhotoCarouselPicker = ({
selectedPhotos: string[]
onSelectPhotos: (v: string[]) => void
}) => {
const {track} = useAnalytics()
const pal = usePalette('default')
const store = useStores()
const [isSetup, setIsSetup] = React.useState<boolean>(false)
@@ -111,6 +113,7 @@ export const PhotoCarouselPicker = ({
const handleSelectPhoto = useCallback(
async (item: PhotoIdentifier) => {
track('PhotoCarouselPicker:PhotoSelected')
try {
const imgPath = await cropPhoto(
item.node.image.uri,
@@ -123,10 +126,11 @@ export const PhotoCarouselPicker = ({
store.log.warn('Error selecting photo', err)
}
},
[store.log, selectedPhotos, onSelectPhotos],
[track, onSelectPhotos, selectedPhotos, store.log],
)
const handleOpenGallery = useCallback(async () => {
track('PhotoCarouselPicker:GalleryOpened')
if (!(await requestPhotoAccessIfNeeded())) {
return
}
@@ -156,7 +160,7 @@ export const PhotoCarouselPicker = ({
result.push(permanentPath)
}
onSelectPhotos([...selectedPhotos, ...result])
}, [selectedPhotos, onSelectPhotos])
}, [track, selectedPhotos, onSelectPhotos])
return (
<ScrollView
+5 -1
View File
@@ -29,7 +29,7 @@ import {ServerInputModal} from '../../../state/models/shell-ui'
import {usePalette} from '../../lib/hooks/usePalette'
export const CreateAccount = ({onPressBack}: {onPressBack: () => void}) => {
const {track} = useAnalytics()
const {track, screen} = useAnalytics()
const pal = usePalette('default')
const store = useStores()
const [isProcessing, setIsProcessing] = useState<boolean>(false)
@@ -46,6 +46,10 @@ export const CreateAccount = ({onPressBack}: {onPressBack: () => void}) => {
const [handle, setHandle] = useState<string>('')
const [is13, setIs13] = useState<boolean>(false)
useEffect(() => {
screen('CreateAccount')
}, [screen])
useEffect(() => {
let aborted = false
setError('')
+29 -2
View File
@@ -35,6 +35,7 @@ enum Forms {
export const Signin = ({onPressBack}: {onPressBack: () => void}) => {
const pal = usePalette('default')
const store = useStores()
const {track} = useAnalytics()
const [error, setError] = useState<string>('')
const [retryDescribeTrigger, setRetryDescribeTrigger] = useState<any>({})
const [serviceUrl, setServiceUrl] = useState<string>(DEFAULT_SERVICE)
@@ -88,6 +89,10 @@ export const Signin = ({onPressBack}: {onPressBack: () => void}) => {
}, [store.session, store.log, serviceUrl, retryDescribeTrigger])
const onPressRetryConnect = () => setRetryDescribeTrigger({})
const onPressForgotPassword = () => {
track('Signin:PressedForgotPassword')
setCurrentForm(Forms.ForgotPassword)
}
return (
<KeyboardAvoidingView testID="signIn" behavior="padding" style={[pal.view]}>
@@ -101,7 +106,7 @@ export const Signin = ({onPressBack}: {onPressBack: () => void}) => {
setError={setError}
setServiceUrl={setServiceUrl}
onPressBack={onPressBack}
onPressForgotPassword={gotoForm(Forms.ForgotPassword)}
onPressForgotPassword={onPressForgotPassword}
onPressRetryConnect={onPressRetryConnect}
/>
) : undefined}
@@ -150,10 +155,14 @@ const ChooseAccountForm = ({
onSelectAccount: (account?: AccountData) => void
onPressBack: () => void
}) => {
const {track} = useAnalytics()
const {track, screen} = useAnalytics()
const pal = usePalette('default')
const [isProcessing, setIsProcessing] = React.useState(false)
React.useEffect(() => {
screen('Choose Account')
}, [screen])
const onTryAccount = async (account: AccountData) => {
if (account.accessJwt && account.refreshJwt) {
setIsProcessing(true)
@@ -267,6 +276,7 @@ const LoginForm = ({
const onPressSelectService = () => {
store.shell.openModal(new ServerInputModal(serviceUrl, setServiceUrl))
Keyboard.dismiss()
track('Signin:PressedSelectService')
}
const onPressNext = async () => {
@@ -458,6 +468,11 @@ const ForgotPasswordForm = ({
const pal = usePalette('default')
const [isProcessing, setIsProcessing] = useState<boolean>(false)
const [email, setEmail] = useState<string>('')
const {screen} = useAnalytics()
useEffect(() => {
screen('Signin:ForgotPassword')
}, [screen])
const onPressSelectService = () => {
store.shell.openModal(new ServerInputModal(serviceUrl, setServiceUrl))
@@ -594,6 +609,12 @@ const SetNewPasswordForm = ({
onPasswordSet: () => void
}) => {
const pal = usePalette('default')
const {screen} = useAnalytics()
useEffect(() => {
screen('Signin:SetNewPasswordForm')
}, [screen])
const [isProcessing, setIsProcessing] = useState<boolean>(false)
const [resetCode, setResetCode] = useState<string>('')
const [password, setPassword] = useState<string>('')
@@ -716,6 +737,12 @@ const SetNewPasswordForm = ({
}
const PasswordUpdatedForm = ({onPressNext}: {onPressNext: () => void}) => {
const {screen} = useAnalytics()
useEffect(() => {
screen('Signin:PasswordUpdatedForm')
}, [screen])
const pal = usePalette('default')
return (
<>
+5
View File
@@ -24,6 +24,7 @@ import {compressIfNeeded} from '../../../lib/images'
import {UserBanner} from '../util/UserBanner'
import {UserAvatar} from '../util/UserAvatar'
import {usePalette} from '../../lib/hooks/usePalette'
import {useAnalytics} from '@segment/analytics-react-native'
export const snapPoints = ['80%']
@@ -37,6 +38,7 @@ export function Component({
const store = useStores()
const [error, setError] = useState<string>('')
const pal = usePalette('default')
const {track} = useAnalytics()
const [isProcessing, setProcessing] = useState<boolean>(false)
const [displayName, setDisplayName] = useState<string>(
@@ -57,6 +59,7 @@ export function Component({
store.shell.closeModal()
}
const onSelectNewAvatar = async (img: PickedImage) => {
track('EditProfile:AvatarSelected')
try {
const finalImg = await compressIfNeeded(img, 1000000)
setNewUserAvatar(finalImg)
@@ -66,6 +69,7 @@ export function Component({
}
}
const onSelectNewBanner = async (img: PickedImage) => {
track('EditProfile:BannerSelected')
try {
const finalImg = await compressIfNeeded(img, 1000000)
setNewUserBanner(finalImg)
@@ -75,6 +79,7 @@ export function Component({
}
}
const onPressSave = async () => {
track('EditProfile:Save')
setProcessing(true)
if (error) {
setError('')
+10 -1
View File
@@ -1,4 +1,4 @@
import React, {MutableRefObject} from 'react'
import React, {MutableRefObject, useEffect} from 'react'
import {observer} from 'mobx-react-lite'
import {
ActivityIndicator,
@@ -16,6 +16,7 @@ import {FeedItem} from './FeedItem'
import {PromptButtons} from './PromptButtons'
import {OnScrollCb} from '../../lib/hooks/useOnMainScroll'
import {s} from '../../lib/styles'
import {useAnalytics} from '@segment/analytics-react-native'
const COMPOSE_PROMPT_ITEM = {_reactKey: '__prompt__'}
const EMPTY_FEED_ITEM = {_reactKey: '__empty__'}
@@ -37,6 +38,12 @@ export const Feed = observer(function Feed({
onScroll?: OnScrollCb
testID?: string
}) {
const {screen, track} = useAnalytics()
useEffect(() => {
screen('Feed')
}, [screen])
// TODO optimize renderItem or FeedItem, we're getting this notice from RN: -prf
// VirtualizedList: You have a large list that is slow to update - make sure your
// renderItem function renders components that follow React performance best practices
@@ -57,6 +64,7 @@ export const Feed = observer(function Feed({
}
}
const onRefresh = () => {
track('Feed:onRefresh')
feed
.refresh()
.catch(err =>
@@ -64,6 +72,7 @@ export const Feed = observer(function Feed({
)
}
const onEndReached = () => {
track('Feed:onEndReached')
feed
.loadMore()
.catch(err => feed.rootStore.log.error('Failed to load more posts', err))
+6
View File
@@ -18,6 +18,7 @@ import {UserAvatar} from '../util/UserAvatar'
import {s} from '../../lib/styles'
import {useStores} from '../../../state'
import {usePalette} from '../../lib/hooks/usePalette'
import {useAnalytics} from '@segment/analytics-react-native'
export const FeedItem = observer(function ({
item,
@@ -30,6 +31,7 @@ export const FeedItem = observer(function ({
}) {
const store = useStores()
const pal = usePalette('default')
const {track} = useAnalytics()
const [deleted, setDeleted] = useState(false)
const record = item.postRecord
const itemHref = useMemo(() => {
@@ -47,6 +49,7 @@ export const FeedItem = observer(function ({
}, [record?.reply])
const onPressReply = () => {
track('FeedItem:PostReply')
store.shell.openComposer({
replyTo: {
uri: item.post.uri,
@@ -61,11 +64,13 @@ export const FeedItem = observer(function ({
})
}
const onPressToggleRepost = () => {
track('FeedItem:PostRepost')
return item
.toggleRepost()
.catch(e => store.log.error('Failed to toggle repost', e))
}
const onPressToggleUpvote = () => {
track('FeedItem:PostLike')
return item
.toggleUpvote()
.catch(e => store.log.error('Failed to toggle upvote', e))
@@ -75,6 +80,7 @@ export const FeedItem = observer(function ({
Toast.show('Copied to clipboard')
}
const onDeletePost = () => {
track('FeedItem:PostDelete')
item.delete().then(
() => {
setDeleted(true)
+14 -2
View File
@@ -2,6 +2,7 @@ import React from 'react'
import {StyleSheet, TouchableOpacity, View} from 'react-native'
import {Text} from '../util/text/Text'
import {usePalette} from '../../lib/hooks/usePalette'
import {useAnalytics} from '@segment/analytics-react-native'
export function PromptButtons({
onPressCompose,
@@ -9,18 +10,29 @@ export function PromptButtons({
onPressCompose: (imagesOpen?: boolean) => void
}) {
const pal = usePalette('default')
const {track} = useAnalytics()
const onPressNewPost = () => {
track('PromptButtons:NewPost')
onPressCompose(false)
}
const onPressSharePhoto = () => {
track('PromptButtons:SharePhoto')
onPressCompose(true)
}
return (
<View style={[pal.view, pal.border, styles.container]}>
<TouchableOpacity
testID="composePromptButton"
onPress={() => onPressCompose(false)}
onPress={onPressNewPost}
style={[styles.btn, {backgroundColor: pal.colors.backgroundLight}]}>
<Text type="button" style={pal.text}>
New post
</Text>
</TouchableOpacity>
<TouchableOpacity
onPress={() => onPressCompose(true)}
onPress={onPressSharePhoto}
style={[styles.btn, {backgroundColor: pal.colors.backgroundLight}]}>
<Text type="button" style={pal.text}>
Share photo
+9 -1
View File
@@ -27,6 +27,7 @@ import {RichText} from '../util/text/RichText'
import {UserAvatar} from '../util/UserAvatar'
import {UserBanner} from '../util/UserBanner'
import {usePalette} from '../../lib/hooks/usePalette'
import {useAnalytics} from '@segment/analytics-react-native'
export const ProfileHeader = observer(function ProfileHeader({
view,
@@ -37,7 +38,7 @@ export const ProfileHeader = observer(function ProfileHeader({
}) {
const pal = usePalette('default')
const store = useStores()
const {track} = useAnalytics()
const onPressBack = () => {
store.nav.tab.goBack()
}
@@ -59,18 +60,23 @@ export const ProfileHeader = observer(function ProfileHeader({
)
}
const onPressEditProfile = () => {
track('ProfileHeader:EditProfileButtonClicked')
store.shell.openModal(new EditProfileModal(view, onRefreshAll))
}
const onPressFollowers = () => {
track('ProfileHeader:FollowersButtonClicked')
store.nav.navigate(`/profile/${view.handle}/followers`)
}
const onPressFollows = () => {
track('ProfileHeader:FollowsButtonClicked')
store.nav.navigate(`/profile/${view.handle}/follows`)
}
const onPressShare = () => {
track('ProfileHeader:ShareButtonClicked')
Share.share({url: toShareUrl(`/profile/${view.handle}`)})
}
const onPressMuteAccount = async () => {
track('ProfileHeader:MuteAccountButtonClicked')
try {
await view.muteAccount()
Toast.show('Account muted')
@@ -80,6 +86,7 @@ export const ProfileHeader = observer(function ProfileHeader({
}
}
const onPressUnmuteAccount = async () => {
track('ProfileHeader:UnmuteAccountButtonClicked')
try {
await view.unmuteAccount()
Toast.show('Account unmuted')
@@ -89,6 +96,7 @@ export const ProfileHeader = observer(function ProfileHeader({
}
}
const onPressReportAccount = () => {
track('ProfileHeader:ReportAccountButtonClicked')
store.shell.openModal(new ReportAccountModal(view.did))
}
+3
View File
@@ -13,6 +13,7 @@ import {MagnifyingGlassIcon} from '../../lib/icons'
import {useStores} from '../../../state'
import {usePalette} from '../../lib/hooks/usePalette'
import {colors} from '../../lib/styles'
import {useAnalytics} from '@segment/analytics-react-native'
const HITSLOP = {left: 10, top: 10, right: 10, bottom: 10}
const BACK_HITSLOP = {left: 10, top: 10, right: 30, bottom: 10}
@@ -28,10 +29,12 @@ export const ViewHeader = observer(function ViewHeader({
}) {
const pal = usePalette('default')
const store = useStores()
const {track} = useAnalytics()
const onPressBack = () => {
store.nav.tab.goBack()
}
const onPressMenu = () => {
track('ViewHeader:MenuButtonClicked')
store.shell.setMainMenuOpen(true)
}
const onPressSearch = () => {
+3
View File
@@ -13,6 +13,7 @@ import {ScreenParams} from '../routes'
import {s, colors, gradients} from '../lib/styles'
import {useOnMainScroll} from '../lib/hooks/useOnMainScroll'
import {clamp} from 'lodash'
import {useAnalytics} from '@segment/analytics-react-native'
const HITSLOP = {left: 20, top: 20, right: 20, bottom: 20}
@@ -23,6 +24,7 @@ export const Home = observer(function Home({
}: ScreenParams) {
const store = useStores()
const onMainScroll = useOnMainScroll(store)
const {track} = useAnalytics()
const safeAreaInsets = useSafeAreaInsets()
const [wasVisible, setWasVisible] = React.useState<boolean>(false)
const {appState} = useAppState({
@@ -72,6 +74,7 @@ export const Home = observer(function Home({
}, [visible, store, store.me.mainFeed, navIdx, doPoll, wasVisible])
const onPressCompose = (imagesOpen?: boolean) => {
track('Home:ComposeButtonPressed')
store.shell.openComposer({imagesOpen})
}
const onPressTryAgain = () => {
+8 -1
View File
@@ -1,4 +1,4 @@
import React, {useState} from 'react'
import React, {useEffect, useState} from 'react'
import {SafeAreaView, StyleSheet, TouchableOpacity, View} from 'react-native'
import FastImage, {Source as FISource} from 'react-native-fast-image'
import {observer} from 'mobx-react-lite'
@@ -9,6 +9,7 @@ import {ErrorBoundary} from '../com/util/ErrorBoundary'
import {colors} from '../lib/styles'
import {usePalette} from '../lib/hooks/usePalette'
import {CLOUD_SPLASH} from '../lib/assets'
import {useAnalytics} from '@segment/analytics-react-native'
enum ScreenState {
S_SigninOrCreateAccount,
@@ -23,6 +24,12 @@ const SigninOrCreateAccount = ({
onPressSignin: () => void
onPressCreateAccount: () => void
}) => {
const {screen} = useAnalytics()
useEffect(() => {
screen('Login')
}, [screen])
const pal = usePalette('default')
return (
<>
+6
View File
@@ -6,10 +6,16 @@ import {useStores} from '../../state'
import {ScreenParams} from '../routes'
import {useOnMainScroll} from '../lib/hooks/useOnMainScroll'
import {s} from '../lib/styles'
import {useAnalytics} from '@segment/analytics-react-native'
export const Notifications = ({navIdx, visible}: ScreenParams) => {
const store = useStores()
const onMainScroll = useOnMainScroll(store)
const {screen} = useAnalytics()
useEffect(() => {
screen('Notifications')
}, [screen])
useEffect(() => {
if (!visible) {
+7
View File
@@ -15,6 +15,7 @@ import {Text} from '../com/util/text/Text'
import {FAB} from '../com/util/FAB'
import {s, colors} from '../lib/styles'
import {useOnMainScroll} from '../lib/hooks/useOnMainScroll'
import {useAnalytics} from '@segment/analytics-react-native'
const LOADING_ITEM = {_reactKey: '__loading__'}
const END_ITEM = {_reactKey: '__end__'}
@@ -22,6 +23,12 @@ const EMPTY_ITEM = {_reactKey: '__empty__'}
export const Profile = observer(({navIdx, visible, params}: ScreenParams) => {
const store = useStores()
const {screen} = useAnalytics()
useEffect(() => {
screen('Profile')
}, [screen])
const onMainScroll = useOnMainScroll(store)
const [hasSetup, setHasSetup] = useState<boolean>(false)
const uiState = React.useMemo(
+9
View File
@@ -18,6 +18,7 @@ import * as Toast from '../com/util/Toast'
import {UserAvatar} from '../com/util/UserAvatar'
import {usePalette} from '../lib/hooks/usePalette'
import {AccountData} from '../../state/models/session'
import {useAnalytics} from '@segment/analytics-react-native'
import {DeleteAccountModal} from '../../state/models/shell-ui'
export const Settings = observer(function Settings({
@@ -26,8 +27,13 @@ export const Settings = observer(function Settings({
}: ScreenParams) {
const pal = usePalette('default')
const store = useStores()
const {screen, track} = useAnalytics()
const [isSwitching, setIsSwitching] = React.useState(false)
useEffect(() => {
screen('Settings')
}, [screen])
useEffect(() => {
if (!visible) {
return
@@ -37,6 +43,7 @@ export const Settings = observer(function Settings({
}, [visible, store, navIdx])
const onPressSwitchAccount = async (acct: AccountData) => {
track('Settings:SwitchAccountButtonClicked')
setIsSwitching(true)
if (await store.session.resumeSession(acct)) {
setIsSwitching(false)
@@ -50,9 +57,11 @@ export const Settings = observer(function Settings({
store.session.clear()
}
const onPressAddAccount = () => {
track('Settings:AddAccountButtonClicked')
store.session.clear()
}
const onPressSignout = () => {
track('Settings:SignOutButtonClicked')
store.session.logout()
}
const onPressDeleteAccount = () => {
+10 -1
View File
@@ -22,15 +22,19 @@ import {UserAvatar} from '../../com/util/UserAvatar'
import {Text} from '../../com/util/text/Text'
import {ToggleButton} from '../../com/util/forms/ToggleButton'
import {usePalette} from '../../lib/hooks/usePalette'
import {useAnalytics} from '@segment/analytics-react-native'
export const Menu = observer(({onClose}: {onClose: () => void}) => {
const pal = usePalette('default')
const store = useStores()
const {track} = useAnalytics()
// events
// =
const onNavigate = (url: string) => {
track('Menu:ItemClicked', {url})
onClose()
if (url === '/notifications') {
store.nav.switchTo(1, true)
@@ -84,6 +88,11 @@ export const Menu = observer(({onClose}: {onClose: () => void}) => {
</TouchableOpacity>
)
const onDarkmodePress = () => {
track('Menu:ItemClicked', {url: '/darkmode'})
store.shell.setDarkMode(!store.shell.darkMode)
}
return (
<ScrollView testID="menuView" style={[styles.view, pal.view]}>
<TouchableOpacity
@@ -161,7 +170,7 @@ export const Menu = observer(({onClose}: {onClose: () => void}) => {
<ToggleButton
label="Dark mode"
isSelected={store.shell.darkMode}
onPress={() => store.shell.setDarkMode(!store.shell.darkMode)}
onPress={onDarkmodePress}
/>
</View>
<View style={styles.footer}>
+4
View File
@@ -45,6 +45,7 @@ import {
import {useAnimatedValue} from '../../lib/hooks/useAnimatedValue'
import {useTheme} from '../../lib/ThemeContext'
import {usePalette} from '../../lib/hooks/usePalette'
import {useAnalytics} from '@segment/analytics-react-native'
const Btn = ({
icon,
@@ -133,8 +134,10 @@ export const MobileShell: React.FC = observer(() => {
const colorScheme = useColorScheme()
const safeAreaInsets = useSafeAreaInsets()
const screenRenderDesc = constructScreenRenderDesc(store.nav)
const {track} = useAnalytics()
const onPressHome = () => {
track('MobileShell:HomeButtonPressed')
if (store.shell.isMainMenuOpen) {
store.shell.setMainMenuOpen(false)
}
@@ -152,6 +155,7 @@ export const MobileShell: React.FC = observer(() => {
}
}
const onPressNotifications = () => {
track('MobileShell:NotificationsButtonPressed')
if (store.shell.isMainMenuOpen) {
store.shell.setMainMenuOpen(false)
}