From bc091393ecbc49c6f1b730da1c610d18243c8f4c Mon Sep 17 00:00:00 2001
From: roitium <65794453+roitium@users.noreply.github.com>
Date: Tue, 11 Aug 2026 19:51:08 +0800
Subject: [PATCH 1/8] mobile: integrate expo-observe navigation metrics
---
apps/mobile/src/app/(tabs)/index.tsx | 7 +++++++
apps/mobile/src/app/(tabs)/library/[tab].tsx | 8 +++++++-
apps/mobile/src/app/(tabs)/settings/index.tsx | 13 ++++++++++---
apps/mobile/src/app/_layout.tsx | 13 ++++++++-----
apps/mobile/src/app/player.tsx | 6 ++++++
apps/mobile/src/app/playlist/local/[id].tsx | 6 ++++++
6 files changed, 44 insertions(+), 9 deletions(-)
diff --git a/apps/mobile/src/app/(tabs)/index.tsx b/apps/mobile/src/app/(tabs)/index.tsx
index 9a9c7e3c..7fbb7cd9 100644
--- a/apps/mobile/src/app/(tabs)/index.tsx
+++ b/apps/mobile/src/app/(tabs)/index.tsx
@@ -5,6 +5,7 @@ import dayjs from 'dayjs'
import { eq } from 'drizzle-orm'
import { useLiveQuery } from 'drizzle-orm/expo-sqlite'
import { Image } from 'expo-image'
+import { useObserve } from 'expo-observe'
import { useRouter } from 'expo-router'
import { useIncomingShare } from 'expo-sharing'
import {
@@ -102,6 +103,12 @@ function HomePage() {
const { data: recentPlaylists } = useRecentPlaylists()
+ const { markInteractive } = useObserve()
+
+ useEffect(() => {
+ markInteractive()
+ }, [markInteractive])
+
const greeting = getGreetingMsg()
const saveSearchHistory = useCallback(
diff --git a/apps/mobile/src/app/(tabs)/library/[tab].tsx b/apps/mobile/src/app/(tabs)/library/[tab].tsx
index f61ba02d..4db4e3bd 100644
--- a/apps/mobile/src/app/(tabs)/library/[tab].tsx
+++ b/apps/mobile/src/app/(tabs)/library/[tab].tsx
@@ -1,6 +1,7 @@
import Icon from '@react-native-vector-icons/material-design-icons'
+import { useObserve } from 'expo-observe'
import { useFocusEffect, useLocalSearchParams, useRouter } from 'expo-router'
-import { useState, useTransition } from 'react'
+import { useEffect, useState, useTransition } from 'react'
import { StyleSheet, View } from 'react-native'
import { Text, useTheme } from 'react-native-paper'
import { useSafeAreaInsets } from 'react-native-safe-area-context'
@@ -42,6 +43,11 @@ export default function Library() {
const colors = useTheme().colors
const router = useRouter()
const { tab } = useLocalSearchParams<{ tab: string }>()
+ const { markInteractive } = useObserve()
+
+ useEffect(() => {
+ markInteractive()
+ }, [markInteractive])
useFocusEffect(() => {
if (tab === undefined) return
diff --git a/apps/mobile/src/app/(tabs)/settings/index.tsx b/apps/mobile/src/app/(tabs)/settings/index.tsx
index e98251b3..9513b25f 100644
--- a/apps/mobile/src/app/(tabs)/settings/index.tsx
+++ b/apps/mobile/src/app/(tabs)/settings/index.tsx
@@ -1,5 +1,7 @@
import { LinearGradient } from 'expo-linear-gradient'
+import { useObserve } from 'expo-observe'
import { useRouter } from 'expo-router'
+import { useEffect } from 'react'
import { ScrollView, StyleSheet, View } from 'react-native'
import { Divider, List, Text, useTheme } from 'react-native-paper'
import { useSafeAreaInsets } from 'react-native-safe-area-context'
@@ -17,6 +19,11 @@ export default function SettingsPage() {
const account = useAppStore((state) => state.bbplayerAccount)
const hasBilibiliCookie = useAppStore((state) => state.hasBilibiliCookie())
const bilibiliUserInfo = useAppStore((state) => state.bilibiliUserInfo)
+ const { markInteractive } = useObserve()
+
+ useEffect(() => {
+ markInteractive()
+ }, [markInteractive])
return (
@@ -155,7 +162,7 @@ export default function SettingsPage() {
icon='chevron-right'
/>
)}
- onPress={() => router.push('/settings/bilibili-account' as never)}
+ onPress={() => router.push('/settings/bilibili-account')}
/>
)}
- onPress={() => router.push('/settings/account' as never)}
+ onPress={() => router.push('/settings/account')}
/>
)}
- onPress={() => router.push('/settings/about' as never)}
+ onPress={() => router.push('/settings/about')}
/>
diff --git a/apps/mobile/src/app/_layout.tsx b/apps/mobile/src/app/_layout.tsx
index af992223..1364c405 100644
--- a/apps/mobile/src/app/_layout.tsx
+++ b/apps/mobile/src/app/_layout.tsx
@@ -1,13 +1,13 @@
import { Orpheus } from '@bbplayer/orpheus'
import {
- fetch as fetchNetInfo,
addEventListener as addNetInfoEventListener,
+ fetch as fetchNetInfo,
} from '@react-native-community/netinfo'
import * as Sentry from '@sentry/react-native'
import { focusManager, onlineManager } from '@tanstack/react-query'
import * as Application from 'expo-application'
-import { ObserveRoot, useObserve } from 'expo-observe'
-import { Stack, router } from 'expo-router'
+import { Observe, ObserveRoot, useObserve } from 'expo-observe'
+import { router, Stack } from 'expo-router'
import * as Updates from 'expo-updates'
import { useEffect, useState } from 'react'
import type { AppStateStatus } from 'react-native'
@@ -42,6 +42,10 @@ import migrations from '../../drizzle/migrations'
const logger = log.extend('UI.RootLayout')
+Observe.configure({
+ integrations: { 'expo-router': true },
+})
+
// 初始化 Sentry
initializeSentry()
@@ -66,10 +70,9 @@ function RootLayout() {
setOnline(!isActuallyOffline(state))
})
- const unsubscribe = addNetInfoEventListener((state) => {
+ return addNetInfoEventListener((state) => {
setOnline(!isActuallyOffline(state))
})
- return unsubscribe
})
useEffect(() => {
diff --git a/apps/mobile/src/app/player.tsx b/apps/mobile/src/app/player.tsx
index e458416d..a6da53df 100644
--- a/apps/mobile/src/app/player.tsx
+++ b/apps/mobile/src/app/player.tsx
@@ -7,6 +7,7 @@ import {
vec,
} from '@shopify/react-native-skia'
import { useImage } from 'expo-image'
+import { useObserve } from 'expo-observe'
import { router } from 'expo-router'
import { useEffect, useMemo, useRef, useState } from 'react'
import {
@@ -78,6 +79,11 @@ export default function PlayerPage() {
const insets = useSafeAreaInsets()
const pagerRef = useRef(null)
const currentTrack = useCurrentTrack()
+ const { markInteractive } = useObserve()
+
+ useEffect(() => {
+ markInteractive()
+ }, [markInteractive])
const currentTrackCover = resolveBilibiliImageUrl(
currentTrack
? resolveTrackCover(currentTrack.uniqueKey, currentTrack.coverUrl)
diff --git a/apps/mobile/src/app/playlist/local/[id].tsx b/apps/mobile/src/app/playlist/local/[id].tsx
index d47dc200..6342c41c 100644
--- a/apps/mobile/src/app/playlist/local/[id].tsx
+++ b/apps/mobile/src/app/playlist/local/[id].tsx
@@ -4,6 +4,7 @@ import type { TrueSheet } from '@lodev09/react-native-true-sheet'
import { and, eq } from 'drizzle-orm'
import { useLiveQuery } from 'drizzle-orm/expo-sqlite'
import { useImage } from 'expo-image'
+import { useObserve } from 'expo-observe'
import { useLocalSearchParams, useRouter } from 'expo-router'
import { useDeferredValue, useEffect, useMemo, useRef, useState } from 'react'
import { StyleSheet, View } from 'react-native'
@@ -153,6 +154,11 @@ export default function LocalPlaylistPage() {
const theme = useTheme()
const { colors } = theme
const router = useRouter()
+ const { markInteractive } = useObserve()
+
+ useEffect(() => {
+ markInteractive()
+ }, [markInteractive])
const bbplayerToken = useAppStore((state) => state.bbplayerToken)
const [searchQuery, setSearchQuery] = useState('')
const [startSearch, setStartSearch] = useState(false)
From afd71a3b1e1d331d62c78f716f6ebfc727fe1776 Mon Sep 17 00:00:00 2001
From: roitium <65794453+roitium@users.noreply.github.com>
Date: Tue, 11 Aug 2026 20:20:18 +0800
Subject: [PATCH 2/8] mobile: 1
---
apps/mobile/src/app/(tabs)/index.tsx | 2 +-
apps/mobile/src/app/history/[date].tsx | 2 +-
apps/mobile/src/app/onboarding.tsx | 4 ++--
.../src/app/playlist/remote/uploader/[mid].tsx | 2 +-
apps/mobile/src/app/settings/about.tsx | 2 +-
.../src/app/settings/bilibili-account/index.tsx | 12 ++++--------
.../app/settings/bilibili-account/qrcode-login.tsx | 2 +-
.../modals/bilibili/AddVideoToBilibiliFavModal.tsx | 2 +-
.../src/features/library/shared/TabDisabled.tsx | 4 +---
apps/mobile/src/hooks/stores/useSkinStore.ts | 2 +-
apps/mobile/src/lib/api/bilibili/api.ts | 2 +-
apps/mobile/src/lib/config/queryClient.ts | 2 +-
apps/mobile/src/lib/facades/sharedPlaylist.ts | 6 ++----
apps/mobile/src/utils/error-handling.ts | 2 +-
14 files changed, 19 insertions(+), 27 deletions(-)
diff --git a/apps/mobile/src/app/(tabs)/index.tsx b/apps/mobile/src/app/(tabs)/index.tsx
index 7fbb7cd9..dba1b27f 100644
--- a/apps/mobile/src/app/(tabs)/index.tsx
+++ b/apps/mobile/src/app/(tabs)/index.tsx
@@ -310,7 +310,7 @@ function HomePage() {
)}
router.push('/settings/bilibili-account' as never)}
+ onPress={() => router.push('/settings/bilibili-account')}
style={styles.avatarButton}
>
{
storage.set('first_open', false)
setIsClickFinalButton(true)
- router.replace('/settings/bilibili-account/qrcode-login' as never)
+ router.replace('/settings/bilibili-account/qrcode-login')
}, [])
const handlePhone = useCallback(() => {
storage.set('first_open', false)
setIsClickFinalButton(true)
- router.replace('/settings/bilibili-account/phone-login' as never)
+ router.replace('/settings/bilibili-account/phone-login')
}, [])
const animatedRowStyle = useAnimatedStyle(() => ({
diff --git a/apps/mobile/src/app/playlist/remote/uploader/[mid].tsx b/apps/mobile/src/app/playlist/remote/uploader/[mid].tsx
index 9ff03416..3dc934c6 100644
--- a/apps/mobile/src/app/playlist/remote/uploader/[mid].tsx
+++ b/apps/mobile/src/app/playlist/remote/uploader/[mid].tsx
@@ -189,7 +189,7 @@ export default function UploaderPage() {