Skip to content
Merged
Show file tree
Hide file tree
Changes from 38 commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
6310aff
add notif tracking
bogdandobritoiu Sep 30, 2025
2f06ecb
add channelId
bogdandobritoiu Oct 1, 2025
a5babd9
add subscribe events
bogdandobritoiu Oct 1, 2025
fc89c53
removed failure events
bogdandobritoiu Oct 1, 2025
ab2745c
refactor
bogdandobritoiu Oct 1, 2025
5d339fe
cleanup
bogdandobritoiu Oct 1, 2025
6ecb860
cleanup
bogdandobritoiu Oct 1, 2025
19a8752
fix unsubscribe on logout
bogdandobritoiu Oct 1, 2025
8005bf0
refactor
bogdandobritoiu Oct 1, 2025
6b66123
test ios
bogdandobritoiu Oct 1, 2025
3b7dd62
fix channelId for pressed
bogdandobritoiu Oct 1, 2025
4d9dbdf
cleanup
bogdandobritoiu Oct 1, 2025
cd0a38b
small cleanup
bogdandobritoiu Oct 1, 2025
bebcb79
refactor
bogdandobritoiu Oct 1, 2025
4aa9efb
small fix
bogdandobritoiu Oct 1, 2025
95d9d2a
cleanup
bogdandobritoiu Oct 1, 2025
6d8b16b
cleanup
bogdandobritoiu Oct 1, 2025
e6fddf0
fix favtoken unsubscribe
bogdandobritoiu Oct 2, 2025
2632d22
added tokenId
bogdandobritoiu Oct 2, 2025
6c025c1
Merge branch 'main' into CP-10502
bogdandobritoiu Oct 2, 2025
52da2dc
cleanup
bogdandobritoiu Oct 2, 2025
cb8691b
cleanup
bogdandobritoiu Oct 2, 2025
ebfea52
Merge branch 'main' into CP-10502
bogdandobritoiu Oct 3, 2025
81fddab
Merge branch 'main' into CP-10502
bogdandobritoiu Oct 7, 2025
6a85dcc
Merge branch 'main' into CP-10502
bogdandobritoiu Oct 7, 2025
3eef3b3
small refactor
bogdandobritoiu Oct 8, 2025
26b0639
fix channelId and received notif
bogdandobritoiu Oct 10, 2025
690d932
moved condition
bogdandobritoiu Oct 10, 2025
9dbc3c9
received testing
bogdandobritoiu Oct 10, 2025
f42eb23
received testing
bogdandobritoiu Oct 10, 2025
49f70ea
added body and title
bogdandobritoiu Oct 10, 2025
a7a836c
revert title + body
bogdandobritoiu Oct 10, 2025
2df9ab1
move received trigger before display
bogdandobritoiu Oct 13, 2025
f7c6303
pod install
bogdandobritoiu Oct 13, 2025
cf858d1
Merge branch 'main' into CP-10502
bogdandobritoiu Oct 13, 2025
05ed23e
yarn
bogdandobritoiu Oct 13, 2025
7706e00
merge fix
bogdandobritoiu Oct 14, 2025
a0232c0
Merge branch 'main' into CP-10502
bogdandobritoiu Oct 14, 2025
7d1bee7
removed push notif received
bogdandobritoiu Oct 27, 2025
70d4862
fix merge
bogdandobritoiu Oct 27, 2025
c316494
Merge branch 'main' into CP-10502
bogdandobritoiu Oct 27, 2025
a0c1b7f
Merge branch 'main' into CP-10502
bogdandobritoiu Oct 28, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 21 additions & 10 deletions packages/core-mobile/app/new/common/hooks/useTokenDetails.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,23 @@
import { useCallback, useEffect, useState, useMemo } from 'react'
import { VsCurrencyType } from '@avalabs/core-coingecko-sdk'
import { getTokenAddress, getTokenChainId } from 'features/track/utils/utils'
import { useGetTrendingToken } from 'hooks/watchlist/useGetTrendingTokens'
import { useWatchlist } from 'hooks/watchlist/useWatchlist'
import { useCoreBrowser } from 'new/common/hooks/useCoreBrowser'
import { useCallback, useEffect, useMemo, useState } from 'react'
import { InteractionManager } from 'react-native'
import { useDispatch, useSelector } from 'react-redux'
import AnalyticsService from 'services/analytics/AnalyticsService'
import { ChannelId } from 'services/notifications/channels'
import TokenService from 'services/token/TokenService'
import { Ranges, TrendingToken } from 'services/token/types'
import { selectSelectedCurrency } from 'store/settings/currency'
import {
MarketToken,
MarketType,
selectIsWatchlistFavorite,
toggleWatchListFavorite
} from 'store/watchlist'
import { InteractionManager } from 'react-native'
import TokenService from 'services/token/TokenService'
import { useWatchlist } from 'hooks/watchlist/useWatchlist'
import { useGetTrendingToken } from 'hooks/watchlist/useGetTrendingTokens'
import { getSocialHandle } from 'utils/getSocialHandle/getSocialHandle'
import { Ranges, TrendingToken } from 'services/token/types'
import { selectSelectedCurrency } from 'store/settings/currency'
import { useCoreBrowser } from 'new/common/hooks/useCoreBrowser'
import { getTokenAddress, getTokenChainId } from 'features/track/utils/utils'
import { usePrevious } from './usePrevious'

const isTrendingToken = (token: MarketToken | undefined): boolean =>
Expand Down Expand Up @@ -221,8 +223,17 @@ export const useTokenDetails = ({
])

const handleFavorite = useCallback(() => {
AnalyticsService.capture(
isFavorite
? 'PushNotificationUnsubscribed'
: 'PushNotificationSubscribed',
{
channelId: ChannelId.FAV_TOKEN_PRICE_ALERTS,
tokenId
}
)
dispatch(toggleWatchListFavorite(tokenId))
}, [tokenId, dispatch])
}, [isFavorite, dispatch, tokenId])

const prevChartDays = usePrevious(chartDays)

Expand Down
45 changes: 35 additions & 10 deletions packages/core-mobile/app/services/fcm/FCMService.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
import messaging from '@react-native-firebase/messaging'
import Logger from 'utils/Logger'
import NotificationsService from 'services/notifications/NotificationsService'
import {
ACTIONS,
DeepLinkOrigin,
PROTOCOLS
} from 'contexts/DeeplinkContext/types'
import { handleDeeplink } from 'contexts/DeeplinkContext/utils/handleDeeplink'
import { router } from 'expo-router'
import { Platform } from 'react-native'
import { CORE_UNIVERSAL_LINK_HOSTS } from 'resources/Constants'
import AnalyticsService from 'services/analytics/AnalyticsService'
import {
BalanceChangeData,
BalanceChangeEvents,
Expand All @@ -15,15 +18,13 @@ import {
NotificationPayloadSchema,
NotificationTypes
} from 'services/fcm/types'
import { Platform } from 'react-native'
import { DisplayNotificationParams } from 'services/notifications/types'
import {
ChannelId,
DEFAULT_ANDROID_CHANNEL
} from 'services/notifications/channels'
import { handleDeeplink } from 'contexts/DeeplinkContext/utils/handleDeeplink'
import { CORE_UNIVERSAL_LINK_HOSTS } from 'resources/Constants'
import { router } from 'expo-router'
import NotificationsService from 'services/notifications/NotificationsService'
import { DisplayNotificationParams } from 'services/notifications/types'
import Logger from 'utils/Logger'

type UnsubscribeFunc = () => void

Expand Down Expand Up @@ -78,6 +79,11 @@ class FCMService {
? this.#prepareDataOnlyNotificationData(result.data.data)
: this.#prepareNotificationData(result.data)

AnalyticsService.capture('PushNotificationReceived', {
channelId: notificationData.channelId as string,
deeplinkUrl: notificationData.data?.url as string
})

await NotificationsService.displayNotification(notificationData).catch(
Logger.error
)
Expand Down Expand Up @@ -113,8 +119,10 @@ class FCMService {
): DisplayNotificationParams => {
if (!fcm.notification) throw Error('No notification payload')
const data = this.#extractDeepLinkData(fcm.data)

return {
channelId: fcm.notification.android?.channelId,
channelId:
fcm.notification.android?.channelId ?? EVENT_TO_CH_ID[fcm.data.event],
title: fcm.notification.title,
body: fcm.notification.body,
sound: fcm.notification.sound,
Expand All @@ -131,7 +139,7 @@ class FCMService {
transactionHash: string
url: string
}
| { url: string }
| { url: string; channelId: string }
| undefined => {
if (fcmData.type === NotificationTypes.BALANCE_CHANGES) {
return {
Expand All @@ -143,7 +151,8 @@ class FCMService {
} else if (fcmData.type === NotificationTypes.NEWS) {
return {
// TODO: remove urlV2 after backend is updated to send just url for NEWS notifications
url: fcmData.urlV2 ?? fcmData.url ?? ''
url: fcmData.urlV2 ?? fcmData.url ?? '',
channelId: EVENT_TO_CH_ID[fcmData.event] as string
}
}
}
Expand Down Expand Up @@ -176,6 +185,7 @@ class FCMService {
)
return
}

const notificationData = this.#prepareNotificationData(result.data)

if (
Expand Down Expand Up @@ -204,6 +214,15 @@ class FCMService {
params: { deeplinkUrl: link.url }
})
})
if (
typeof notificationData.data?.channelId === 'string' &&
notificationData.data?.channelId.length !== 0
) {
AnalyticsService.capture('PushNotificationPressed', {
channelId: notificationData.data.channelId,
deeplinkUrl: notificationData.data.url
})
}
})
}

Expand All @@ -218,6 +237,7 @@ class FCMService {
)
return
}

if (result.data.notification) {
//skip, FCM sdk handles this already
return
Expand All @@ -227,6 +247,11 @@ class FCMService {
result.data.data
)

AnalyticsService.capture('PushNotificationReceived', {
channelId: notificationData.channelId as string,
deeplinkUrl: notificationData.data?.url as string
})

await NotificationsService.displayNotification(notificationData).catch(
Logger.error
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,19 +9,20 @@ import notifee, {
TriggerNotification,
TriggerType
} from '@notifee/react-native'
import messaging from '@react-native-firebase/messaging'
import { HandleNotificationCallback } from 'contexts/DeeplinkContext/types'
import { fromUnixTime, isPast } from 'date-fns'
import { Linking, Platform } from 'react-native'
import AnalyticsService from 'services/analytics/AnalyticsService'
import {
ChannelId,
NewsChannelId,
notificationChannels
} from 'services/notifications/channels'
import { StakeCompleteNotification } from 'store/notifications'
import Logger from 'utils/Logger'
import { HandleNotificationCallback } from 'contexts/DeeplinkContext/types'
import { DisplayNotificationParams } from 'services/notifications/types'
import { StakeCompleteNotification } from 'store/notifications'
import { audioFiles } from 'utils/AudioFeedback'
import messaging from '@react-native-firebase/messaging'
import Logger from 'utils/Logger'
import {
LAUNCH_ACTIVITY,
PressActionId,
Expand Down Expand Up @@ -252,6 +253,14 @@ class NotificationsService {
if (detail?.notification?.id) {
await this.cancelTriggerNotification(detail.notification.id)
}

if (detail?.notification?.data) {
AnalyticsService.capture('PushNotificationPressed', {
channelId: detail.notification?.data?.channelId as string,
deeplinkUrl: detail.notification?.data?.url as string
})
}

callback(detail?.notification?.data)
}

Expand Down Expand Up @@ -311,7 +320,7 @@ class NotificationsService {
}

/**
* @param channelId For Android only
* @param channelId
* @param title
* @param body
* @param sound For iOS only
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import { showAlert } from '@avalabs/k2-alpine'
import { AuthorizationStatus } from '@notifee/react-native'
import { AnyAction } from '@reduxjs/toolkit'
import { navigateWithPromise } from 'common/utils/navigateWithPromise'
import { waitForInteractions } from 'common/utils/waitForInteractions'
import AnalyticsService from 'services/analytics/AnalyticsService'
import { AppUpdateService } from 'services/AppUpdateService/AppUpdateService'
import NotificationsService from 'services/notifications/NotificationsService'
import {
Expand All @@ -14,8 +17,6 @@ import {
setViewOnce,
ViewOnceKey
} from 'store/viewOnce'
import { showAlert } from '@avalabs/k2-alpine'
import { waitForInteractions } from 'common/utils/waitForInteractions'
import { turnOnAllNotifications } from '../slice'

export const handleAfterLoginFlows = async (
Expand Down Expand Up @@ -97,6 +98,7 @@ const promptEnableNotificationsIfNeeded = async (
{
text: 'Not now',
onPress: () => {
AnalyticsService.capture('PushNotificationRejected')
resolve()
}
},
Expand All @@ -111,6 +113,7 @@ const promptEnableNotificationsIfNeeded = async (
return
}
dispatch(turnOnAllNotifications())
AnalyticsService.capture('PushNotificationAccepted')
resolve()
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { AppListenerEffectAPI } from 'store/types'
import AnalyticsService from 'services/analytics/AnalyticsService'
import { ChannelId } from 'services/notifications/channels'
import { AppListenerEffectAPI } from 'store/types'
import { setNotificationSubscriptions } from '../slice'
import { handleStakeCompleteNotificationCleanup } from './handleNotificationCleanup'

Expand All @@ -12,4 +13,8 @@ export const handleTurnOffNotificationsFor = async (
if (channelId === ChannelId.STAKING_COMPLETE) {
handleStakeCompleteNotificationCleanup(listenerApi)
}

AnalyticsService.capture('PushNotificationUnsubscribed', {
channelId
})
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { AppListenerEffectAPI } from 'store/types'
import AnalyticsService from 'services/analytics/AnalyticsService'
import {
ChannelId,
notificationChannels
} from 'services/notifications/channels'
import NotificationsService from 'services/notifications/NotificationsService'
import { AppListenerEffectAPI } from 'store/types'
import { setNotificationSubscriptions } from '../slice'

export const handleTurnOnNotificationsFor = async (
Expand All @@ -20,4 +21,8 @@ export const handleTurnOnNotificationsFor = async (
if (blockedNotifications.has(channelId)) {
NotificationsService.openSystemSettings()
}

AnalyticsService.capture('PushNotificationSubscribed', {
channelId
})
}
19 changes: 19 additions & 0 deletions packages/core-mobile/app/types/analytics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -213,4 +213,23 @@ export type AnalyticsEvents = {

//SOLANA
SolanaSwapFeeAccountNotInitialized: { mint: string }

// PUSH NOTIFICATIONS
PushNotificationAccepted: undefined
PushNotificationRejected: undefined
PushNotificationReceived: {
channelId: string
deeplinkUrl?: string
}
PushNotificationPressed: {
channelId: string
deeplinkUrl?: string
}
PushNotificationUnsubscribed: {
channelId: string
}
PushNotificationSubscribed: {
channelId: string
tokenId?: string
}
}
Loading