Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
11 changes: 11 additions & 0 deletions packages/common/src/models/Analytics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -378,6 +378,7 @@ export enum Name {
CREATE_CHAT_BLAST_SUCCESS = 'Chat Blast: Create - Success',
CREATE_CHAT_BLAST_FAILURE = 'Chat Blast: Create - Failure',
CHAT_BLAST_MESSAGE_SENT = 'Chat Blast: Message Sent',
CHAT_BLAST_MESSAGE_VIEWED = 'Chat Blast: Message Viewed',
SEND_MESSAGE_SUCCESS = 'Send Message: Success',
SEND_MESSAGE_FAILURE = 'Send Message: Failure',
DELETE_CHAT_SUCCESS = 'Delete Chat: Success',
Expand Down Expand Up @@ -1959,6 +1960,15 @@ type ChatBlastMessageSent = {
audienceContentId?: ID
}

type ChatBlastMessageViewed = {
eventName: Name.CHAT_BLAST_MESSAGE_VIEWED
isNativeMobile?: boolean
chatId: string
audience: string
audienceContentType?: string
audienceContentId?: ID
}

type SendMessageSuccess = {
eventName: Name.SEND_MESSAGE_SUCCESS
}
Expand Down Expand Up @@ -2807,6 +2817,7 @@ export type AllTrackingEvents =
| ConnectWalletError
| ChatBlastCTAClicked
| ChatBlastMessageSent
| ChatBlastMessageViewed
| CreateChatSuccess
| CreateChatFailure
| CreateChatBlastSuccess
Expand Down
3 changes: 3 additions & 0 deletions packages/mobile/src/constants/storage-keys.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,6 @@ export const THEME_STORAGE_KEY = 'theme'
export const THEME_PALETTE_KEY = 'themePalette'
export const THEME_MODE_KEY = 'themeMode'
export const SEARCH_HISTORY_KEY = '@search-history'
// Tracks which blast chat threads the user has viewed so the
// `Chat Blast: Message Viewed` analytics event fires only once per blast.
export const VIEWED_BLAST_CHATS_KEY = '@viewed-blast-chats'
36 changes: 35 additions & 1 deletion packages/mobile/src/screens/chat-screen/ChatScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { useCallback, useEffect, useMemo, useRef } from 'react'

import { useCurrentUserId } from '@audius/common/api'
import { useCanSendMessage } from '@audius/common/hooks'
import { Status } from '@audius/common/models'
import { Name, Status } from '@audius/common/models'
import type { ChatMessageWithExtras } from '@audius/common/models'
import {
chatActions,
Expand All @@ -16,6 +16,7 @@ import {
} from '@audius/common/utils'
import { OptionalHashId, OptionalId, type ChatBlast } from '@audius/sdk'
import { Portal } from '@gorhom/portal'
import AsyncStorage from '@react-native-async-storage/async-storage'
import { useFocusEffect } from '@react-navigation/native'
import type { FlatListProps, LayoutChangeEvent } from 'react-native'
import {
Expand All @@ -40,9 +41,11 @@ import {
} from 'app/components/core'
import LoadingSpinner from 'app/components/loading-spinner'
import { PLAY_BAR_HEIGHT } from 'app/components/now-playing-drawer'
import { VIEWED_BLAST_CHATS_KEY } from 'app/constants/storage-keys'
import { light } from 'app/haptics'
import { useRoute } from 'app/hooks/useRoute'
import { useToast } from 'app/hooks/useToast'
import { make, track } from 'app/services/analytics'
import { setVisibility } from 'app/store/drawers/slice'
import { makeStyles } from 'app/styles'
import { spacing } from 'app/styles/spacing'
Expand Down Expand Up @@ -289,6 +292,37 @@ export const ChatScreen = () => {
}
}, [chatId, dispatch])

// Track when a user opens a blast DM thread. Fires once per blast per user.
useEffect(() => {
if (!chatId || !chat?.is_blast) return
const blastChat = chat as ChatBlast
const trackBlastViewed = async () => {
try {
const raw = await AsyncStorage.getItem(VIEWED_BLAST_CHATS_KEY)
const viewed: string[] = raw ? JSON.parse(raw) : []
if (!Array.isArray(viewed) || viewed.includes(chatId)) return
await AsyncStorage.setItem(
VIEWED_BLAST_CHATS_KEY,
JSON.stringify([...viewed, chatId])
)
track(
make({
eventName: Name.CHAT_BLAST_MESSAGE_VIEWED,
isNativeMobile: true,
chatId,
audience: blastChat.audience,
audienceContentType: blastChat.audience_content_type,
audienceContentId:
OptionalHashId.parse(blastChat.audience_content_id) ?? undefined
})
)
} catch {
// Ignore storage failures; worst case the event fires again later.
}
}
trackBlastViewed()
}, [chatId, chat])

// Fetch all permissions, blockers/blockees, and recheck_permissions flag
useEffect(() => {
dispatch(fetchBlockees())
Expand Down
54 changes: 53 additions & 1 deletion packages/web/src/pages/chat-page/ChatPage.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { useCallback, useEffect, useRef } from 'react'

import { useCanSendMessage } from '@audius/common/hooks'
import { Status } from '@audius/common/models'
import { Name, Status } from '@audius/common/models'
import { chatActions, chatSelectors } from '@audius/common/store'
import { ChatBlast, OptionalHashId } from '@audius/sdk'
import cn from 'classnames'
import { useDispatch } from 'react-redux'
import { useParams, useLocation, useNavigate } from 'react-router'
Expand All @@ -11,6 +12,7 @@ import Page from 'components/page/Page'
import { useIsContainerNarrow } from 'hooks/useIsContainerNarrow'
import { useIsMobile } from 'hooks/useIsMobile'
import { useManagedAccountNotAllowedRedirect } from 'hooks/useManagedAccountNotAllowedRedirect'
import { make, track } from 'services/analytics'
import { push } from 'utils/navigation'
import { useSelector } from 'utils/reducer'
import { chatPage } from 'utils/route'
Expand All @@ -33,6 +35,36 @@ const messages = {

const NARROW_LAYOUT_THRESHOLD_PX = 1080

// Local-storage key tracking which blast chat threads the user has already
// viewed, so the `Chat Blast: Message Viewed` event fires only once per blast.
const VIEWED_BLAST_CHATS_LOCAL_STORAGE_KEY = 'viewedBlastChats'

const getViewedBlastChats = (): string[] => {
try {
const raw = window.localStorage.getItem(
VIEWED_BLAST_CHATS_LOCAL_STORAGE_KEY
)
const parsed = raw ? JSON.parse(raw) : []
return Array.isArray(parsed) ? parsed : []
} catch {
return []
}
}

const markBlastChatViewed = (chatId: string) => {
try {
const viewed = getViewedBlastChats()
if (!viewed.includes(chatId)) {
window.localStorage.setItem(
VIEWED_BLAST_CHATS_LOCAL_STORAGE_KEY,
JSON.stringify([...viewed, chatId])
)
}
} catch {
// Ignore local-storage write failures; worst case the event fires again.
}
}

export const ChatPage = () => {
useManagedAccountNotAllowedRedirect()
const params = useParams<{ id?: string }>()
Expand Down Expand Up @@ -110,6 +142,26 @@ export const ChatPage = () => {
}
}, [dispatch, firstOtherUser, isMobile])

// Track when a user opens a blast DM thread. Fires once per blast per user.
useEffect(() => {
if (!currentChatId || !chat?.is_blast) return
const viewed = getViewedBlastChats()
if (viewed.includes(currentChatId)) return
const blastChat = chat as ChatBlast
markBlastChatViewed(currentChatId)
track(
make({
eventName: Name.CHAT_BLAST_MESSAGE_VIEWED,
isNativeMobile: false,
chatId: currentChatId,
audience: blastChat.audience,
audienceContentType: blastChat.audience_content_type,
audienceContentId:
OptionalHashId.parse(blastChat.audience_content_id) ?? undefined
})
)
}, [currentChatId, chat])

if (isMobile) {
return <MobileChatPage />
}
Expand Down
Loading