feat(sentry): integrate Sentry for error tracking and monitoring

- Added Sentry for error reporting in the mobile application.
- Updated the app configuration to initialize Sentry with the necessary DSN and settings.
- Replaced Firebase Crashlytics with Sentry for error capturing in the ErrorBoundary component.
- Updated various workflows to include Sentry authentication token.
- Adjusted dependencies to use React Native version 0.79.6 and updated related packages.

This integration enhances the application's ability to track and manage errors effectively.

Signed-off-by: Innei <tukon479@gmail.com>
This commit is contained in:
Innei 2025-09-22 23:12:11 +08:00
parent a350950672
commit 866abbef20
No known key found for this signature in database
GPG Key ID: 0F62D33977F021F7
20 changed files with 909 additions and 757 deletions

View File

@ -55,6 +55,7 @@ jobs:
working-directory: apps/mobile
run: eas build --platform ios --profile development --non-interactive --local --output=./build.ipa
env:
SENTRY_AUTH_TOKEN: ${{ secrets.RN_SENTRY_AUTH_TOKEN }}
CI: true
# Optional: Upload artifact
@ -105,7 +106,7 @@ jobs:
run: eas build --platform ios --profile development --non-interactive --local --output=./build.ipa
env:
CI: true
SENTRY_AUTH_TOKEN: ${{ secrets.RN_SENTRY_AUTH_TOKEN }}
# Optional: Upload artifact
- name: 📤 Upload IPA
uses: actions/upload-artifact@v4
@ -149,7 +150,7 @@ jobs:
run: eas build --platform ios --profile ios-simulator --non-interactive --local --output=./build-simulator.ipa
env:
CI: true
SENTRY_AUTH_TOKEN: ${{ secrets.RN_SENTRY_AUTH_TOKEN }}
# Optional: Upload artifact
- name: 📤 Upload IPA
uses: actions/upload-artifact@v4

View File

@ -62,6 +62,7 @@ jobs:
working-directory: apps/mobile
run: eas build --platform ios --profile ${{ github.event.inputs.profile || 'preview' }} --non-interactive --local --output=./build.ipa
env:
SENTRY_AUTH_TOKEN: ${{ secrets.RN_SENTRY_AUTH_TOKEN }}
CI: true
# Optional: Upload artifact
@ -117,7 +118,7 @@ jobs:
run: eas build --platform ios --profile ${{ github.event.inputs.profile || 'preview' }} --non-interactive --local --output=./build.ipa
env:
CI: true
SENTRY_AUTH_TOKEN: ${{ secrets.RN_SENTRY_AUTH_TOKEN }}
# Optional: Upload artifact
- name: 📤 Upload IPA
uses: actions/upload-artifact@v4

View File

@ -1 +1,2 @@
EXPO_PUBLIC_APP_CHECK_DEBUG_TOKEN=
SENTRY_AUTH_TOKEN=

View File

@ -45,3 +45,5 @@ android
Podfile.lock
buildServer.json
.env.local

View File

@ -137,7 +137,14 @@ export default ({ config }: ConfigContext): ExpoConfig => {
require("./plugins/with-android-manifest-plugin.js"),
"expo-secure-store",
"@react-native-firebase/app",
"@react-native-firebase/crashlytics",
[
"@sentry/react-native/expo",
{
url: "https://sentry.io/",
project: "react-native",
organization: "follow-rg",
},
],
[
"expo-image-picker",
{

View File

@ -170,7 +170,7 @@
800E24972A6A228C8D4807E9 /* [CP] Copy Pods Resources */,
6E4ADCBDA5B760296BAE78FA /* [CP] Embed Pods Frameworks */,
8F2D99FDEA6998C9D98BD7E0 /* [CP-User] [RNFB] Core Configuration */,
8DF9409D54556D15DD94C0B5 /* [CP-User] [RNFB] Crashlytics Configuration */,
3B0B406257C84B7586048134 /* Upload Debug Symbols to Sentry */,
);
buildRules = (
);
@ -243,7 +243,7 @@
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "if [[ -f \"$PODS_ROOT/../.xcode.env\" ]]; then\n source \"$PODS_ROOT/../.xcode.env\"\nfi\nif [[ -f \"$PODS_ROOT/../.xcode.env.local\" ]]; then\n source \"$PODS_ROOT/../.xcode.env.local\"\nfi\n\n# The project root by default is one level up from the ios directory\nexport PROJECT_ROOT=\"$PROJECT_DIR\"/..\n\nif [[ \"$CONFIGURATION\" = *Debug* ]]; then\n export SKIP_BUNDLING=1\nfi\nif [[ -z \"$ENTRY_FILE\" ]]; then\n # Set the entry JS file using the bundler's entry resolution.\n export ENTRY_FILE=\"$(\"$NODE_BINARY\" -e \"require('expo/scripts/resolveAppEntry')\" \"$PROJECT_ROOT\" ios absolute | tail -n 1)\"\nfi\n\nif [[ -z \"$CLI_PATH\" ]]; then\n # Use Expo CLI\n export CLI_PATH=\"$(\"$NODE_BINARY\" --print \"require.resolve('@expo/cli', { paths: [require.resolve('expo/package.json')] })\")\"\nfi\nif [[ -z \"$BUNDLE_COMMAND\" ]]; then\n # Default Expo CLI command for bundling\n export BUNDLE_COMMAND=\"export:embed\"\nfi\n\n# Source .xcode.env.updates if it exists to allow\n# SKIP_BUNDLING to be unset if needed\nif [[ -f \"$PODS_ROOT/../.xcode.env.updates\" ]]; then\n source \"$PODS_ROOT/../.xcode.env.updates\"\nfi\n# Source local changes to allow overrides\n# if needed\nif [[ -f \"$PODS_ROOT/../.xcode.env.local\" ]]; then\n source \"$PODS_ROOT/../.xcode.env.local\"\nfi\n\n`\"$NODE_BINARY\" --print \"require('path').dirname(require.resolve('react-native/package.json')) + '/scripts/react-native-xcode.sh'\"`\n\n";
shellScript = "if [[ -f \"$PODS_ROOT/../.xcode.env\" ]]; then\n source \"$PODS_ROOT/../.xcode.env\"\nfi\nif [[ -f \"$PODS_ROOT/../.xcode.env.local\" ]]; then\n source \"$PODS_ROOT/../.xcode.env.local\"\nfi\n\n# The project root by default is one level up from the ios directory\nexport PROJECT_ROOT=\"$PROJECT_DIR\"/..\n\nif [[ \"$CONFIGURATION\" = *Debug* ]]; then\n export SKIP_BUNDLING=1\nfi\nif [[ -z \"$ENTRY_FILE\" ]]; then\n # Set the entry JS file using the bundler's entry resolution.\n export ENTRY_FILE=\"$(\"$NODE_BINARY\" -e \"require('expo/scripts/resolveAppEntry')\" \"$PROJECT_ROOT\" ios absolute | tail -n 1)\"\nfi\n\nif [[ -z \"$CLI_PATH\" ]]; then\n # Use Expo CLI\n export CLI_PATH=\"$(\"$NODE_BINARY\" --print \"require.resolve('@expo/cli', { paths: [require.resolve('expo/package.json')] })\")\"\nfi\nif [[ -z \"$BUNDLE_COMMAND\" ]]; then\n # Default Expo CLI command for bundling\n export BUNDLE_COMMAND=\"export:embed\"\nfi\n\n# Source .xcode.env.updates if it exists to allow\n# SKIP_BUNDLING to be unset if needed\nif [[ -f \"$PODS_ROOT/../.xcode.env.updates\" ]]; then\n source \"$PODS_ROOT/../.xcode.env.updates\"\nfi\n# Source local changes to allow overrides\n# if needed\nif [[ -f \"$PODS_ROOT/../.xcode.env.local\" ]]; then\n source \"$PODS_ROOT/../.xcode.env.local\"\nfi\n\n/bin/sh `\"$NODE_BINARY\" --print \"require('path').dirname(require.resolve('@sentry/react-native/package.json')) + '/scripts/sentry-xcode.sh'\"` `\"$NODE_BINARY\" --print \"require('path').dirname(require.resolve('react-native/package.json')) + '/scripts/react-native-xcode.sh'\"`\n\n";
};
08A4A3CD28434E44B6B9DE2E /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
@ -267,6 +267,20 @@
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0;
};
3B0B406257C84B7586048134 /* Upload Debug Symbols to Sentry */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "Upload Debug Symbols to Sentry";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "/bin/sh ../../../node_modules/@sentry/react-native/scripts/sentry-xcode-debug-files.sh\n";
};
6E4ADCBDA5B760296BAE78FA /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
@ -306,7 +320,6 @@
"${PODS_CONFIGURATION_BUILD_DIR}/FirebaseCore/FirebaseCore_Privacy.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/FirebaseCoreExtension/FirebaseCoreExtension_Privacy.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/FirebaseCoreInternal/FirebaseCoreInternal_Privacy.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/FirebaseCrashlytics/FirebaseCrashlytics_Privacy.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/FirebaseInstallations/FirebaseInstallations_Privacy.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/FirebaseMessaging/FirebaseMessaging_Privacy.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/FollowNative/js.bundle",
@ -315,7 +328,6 @@
"${PODS_CONFIGURATION_BUILD_DIR}/GoogleUtilities/GoogleUtilities_Privacy.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/Mute/Mute.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/PromisesObjC/FBLPromises_Privacy.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/PromisesSwift/Promises_Privacy.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/RCT-Folly/RCT-Folly_privacy.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/RNDeviceInfo/RNDeviceInfoPrivacyInfo.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/RNSVG/RNSVGFilters.bundle",
@ -323,6 +335,7 @@
"${PODS_CONFIGURATION_BUILD_DIR}/React-Core/React-Core_privacy.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/React-cxxreact/React-cxxreact_privacy.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/SDWebImage/SDWebImage.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/Sentry/Sentry.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/SnapKit/SnapKit_Privacy.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/boost/boost_privacy.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/expo-dev-launcher/EXDevLauncher.bundle",
@ -346,7 +359,6 @@
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FirebaseCore_Privacy.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FirebaseCoreExtension_Privacy.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FirebaseCoreInternal_Privacy.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FirebaseCrashlytics_Privacy.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FirebaseInstallations_Privacy.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FirebaseMessaging_Privacy.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/js.bundle",
@ -355,7 +367,6 @@
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/GoogleUtilities_Privacy.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Mute.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FBLPromises_Privacy.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Promises_Privacy.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/RCT-Folly_privacy.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/RNDeviceInfoPrivacyInfo.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/RNSVGFilters.bundle",
@ -363,6 +374,7 @@
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/React-Core_privacy.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/React-cxxreact_privacy.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/SDWebImage.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Sentry.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/SnapKit_Privacy.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/boost_privacy.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/EXDevLauncher.bundle",
@ -375,22 +387,6 @@
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Folo/Pods-Folo-resources.sh\"\n";
showEnvVarsInLog = 0;
};
8DF9409D54556D15DD94C0B5 /* [CP-User] [RNFB] Crashlytics Configuration */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
"${DWARF_DSYM_FOLDER_PATH}/${DWARF_DSYM_FILE_NAME}/Contents/Resources/DWARF/${TARGET_NAME}",
"$(BUILT_PRODUCTS_DIR)/$(INFOPLIST_PATH)",
);
name = "[CP-User] [RNFB] Crashlytics Configuration";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "#!/usr/bin/env bash\n#\n# Copyright (c) 2016-present Invertase Limited & Contributors\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this library except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\nset -e\n\nif [[ ${PODS_ROOT} ]]; then\n echo \"info: Exec FirebaseCrashlytics Run from Pods\"\n \"${PODS_ROOT}/FirebaseCrashlytics/run\"\nelse\n echo \"info: Exec FirebaseCrashlytics Run from framework\"\n \"${PROJECT_DIR}/FirebaseCrashlytics.framework/run\"\nfi\n";
};
8F2D99FDEA6998C9D98BD7E0 /* [CP-User] [RNFB] Core Configuration */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;

View File

@ -0,0 +1,4 @@
defaults.url=https://sentry.io/
defaults.org=follow-rg
defaults.project=react-native
# Using SENTRY_AUTH_TOKEN environment variable

View File

@ -1,9 +1,9 @@
const { getDefaultConfig } = require("expo/metro-config")
const { withNativeWind } = require("nativewind/metro")
const path = require("pathe")
const { wrapWithReanimatedMetroConfig } = require("react-native-reanimated/metro-config")
const { getSentryExpoConfig } = require("@sentry/react-native/metro")
const config = getDefaultConfig(__dirname, { isCSSEnabled: true })
const config = getSentryExpoConfig(__dirname, { isCSSEnabled: true })
config.resolver.sourceExts.push("sql")
config.transformer.getTransformOptions = async () => ({

View File

@ -38,11 +38,11 @@
"@react-native-firebase/analytics": "22.2.1",
"@react-native-firebase/app": "22.2.1",
"@react-native-firebase/app-check": "22.2.1",
"@react-native-firebase/crashlytics": "22.2.1",
"@react-native-firebase/messaging": "22.2.1",
"@react-native-masked-view/masked-view": "0.3.2",
"@react-native-menu/menu": "1.2.3",
"@react-native-picker/picker": "2.11.2",
"@sentry/react-native": "6.20.0",
"@shopify/flash-list": "2.0.3",
"@tanstack/query-sync-storage-persister": "5.89.0",
"@tanstack/react-query": "5.89.0",
@ -100,7 +100,7 @@
"react-error-boundary": "6.0.0",
"react-hook-form": "7.62.0",
"react-i18next": "15.7.3",
"react-native": "0.79.1",
"react-native": "0.79.6",
"react-native-awesome-slider": "2.9.0",
"react-native-bouncy-checkbox": "4.1.2",
"react-native-color-matrix-image-filters": "7.0.2",

View File

@ -1,5 +1,5 @@
import { useTypeScriptHappyCallback } from "@follow/hooks"
import { getCrashlytics, recordError } from "@react-native-firebase/crashlytics"
import { captureException } from "@sentry/react-native"
import type { FC } from "react"
import { createElement, useEffect } from "react"
import { ErrorBoundary as ReactErrorBoundary } from "react-error-boundary"
@ -51,7 +51,7 @@ const defaultFallbackRender = ({ error }: { error: Error }) => {
}
const ErrorReport = ({ error }: { error: Error }) => {
useEffect(() => {
recordError(getCrashlytics(), error)
captureException(error)
console.error(error)
}, [error])
return null

View File

@ -1,17 +0,0 @@
import { whoami } from "@follow/store/user/getters"
import { getCrashlytics, setAttributes, setUserId } from "@react-native-firebase/crashlytics"
export const initCrashlytics = async () => {
const crashlytics = getCrashlytics()
const user = whoami()
if (user) {
await Promise.all([
setUserId(crashlytics, user.id),
setAttributes(crashlytics, {
email: user.email ?? "",
name: user.name ?? "",
handle: user.handle ?? "",
}),
])
}
}

View File

@ -7,12 +7,12 @@ import { settingSyncQueue } from "../modules/settings/sync-queue"
import { initAnalytics } from "./analytics"
import { initializeAppCheck } from "./app-check"
import { initBackgroundTask } from "./background"
import { initCrashlytics } from "./crashlytics"
import { initializeDayjs } from "./dayjs"
import { initDeviceType } from "./device"
import { hydrateQueryClient, hydrateSettings } from "./hydrate"
import { migrateDatabase } from "./migration"
import { initializePlayer } from "./player"
import { initializeSentry } from "./sentry"
/* eslint-disable no-console */
export const initializeApp = async () => {
@ -20,6 +20,8 @@ export const initializeApp = async () => {
const now = Date.now()
initializeSentry()
await initDeviceType()
await initializeDB()
@ -57,7 +59,7 @@ export const initializeApp = async () => {
electron: false,
using_indexed_db: true,
})
initCrashlytics()
initBackgroundTask()
console.log(`Initialize done,`, `${loadingTime}ms`)
}

View File

@ -0,0 +1,11 @@
import * as Sentry from "@sentry/react-native"
export const initializeSentry = () => {
// Unlike Sentry on other platforms, you do not need to import anything to use tracing on React Native
Sentry.init({
dsn: "https://cbfecd786e09a9481676655a4da88a7e@o4507542488023040.ingest.us.sentry.io/4509926421102593",
// We recommend adjusting this value in production, or using tracesSampler
// for finer control
tracesSampleRate: 1,
})
}

View File

@ -2,6 +2,7 @@ import "./global.css"
import "./polyfill"
import { apiContext, authClientContext, queryClientContext } from "@follow/store/context"
import * as Sentry from "@sentry/react-native"
import { registerRootComponent } from "expo"
import { Image } from "expo-image"
import { LinearGradient } from "expo-linear-gradient"
@ -27,7 +28,6 @@ import { DiscoverTabScreen } from "./screens/(stack)/(tabs)/discover"
import { SettingsTabScreen } from "./screens/(stack)/(tabs)/settings"
import { SubscriptionsTabScreen } from "./screens/(stack)/(tabs)/subscriptions"
import { registerSitemap } from "./sitemap"
// @ts-expect-error
global.APP_NAME = "Folo"
// @ts-expect-error
@ -45,7 +45,7 @@ enableFreeze(true)
initializeApp()
registerSitemap()
initializeI18n()
registerRootComponent(RootComponent)
registerRootComponent(Sentry.wrap(RootComponent))
function RootComponent() {
const { t } = useTranslation()

View File

@ -1,8 +1,6 @@
import { View } from "react-native"
import { SafeNavigationScrollView } from "@/src/components/layouts/views/SafeNavigationScrollView"
import { Search3CuteFiIcon } from "@/src/icons/search_3_cute_fi"
import { Search3CuteReIcon } from "@/src/icons/search_3_cute_re"
import type { TabScreenComponent } from "@/src/lib/navigation/bottom-tab/types"
import Content from "@/src/modules/discover/Content"
import {
@ -30,9 +28,5 @@ export default function Discover() {
}
export const DiscoverTabScreen: TabScreenComponent = Discover
DiscoverTabScreen.tabBarIcon = ({ focused, color }) => {
const Icon = !focused ? Search3CuteReIcon : Search3CuteFiIcon
return <Icon color={color} width={24} height={24} />
}
DiscoverTabScreen.lazy = true

View File

@ -1,17 +1,11 @@
import { useResetTabOpacityWhenFocused } from "@/src/components/layouts/tabbar/hooks"
import { usePrepareEntryRenderWebView } from "@/src/components/native/webview/hooks"
import { Home5CuteFiIcon } from "@/src/icons/home_5_cute_fi"
import { Home5CuteReIcon } from "@/src/icons/home_5_cute_re"
import type { TabScreenComponent } from "@/src/lib/navigation/bottom-tab/types"
import { EntryList } from "@/src/modules/entry-list"
export const IndexTabScreen: TabScreenComponent = () => {
usePrepareEntryRenderWebView()
useResetTabOpacityWhenFocused()
return <EntryList />
}
IndexTabScreen.tabBarIcon = ({ focused, color }) => {
const Icon = !focused ? Home5CuteReIcon : Home5CuteFiIcon
return <Icon color={color} width={24} height={24} />
}

View File

@ -12,8 +12,6 @@ import { useRegisterNavigationScrollView } from "@/src/components/layouts/tabbar
import { getDefaultHeaderHeight } from "@/src/components/layouts/utils"
import { SafeNavigationScrollView } from "@/src/components/layouts/views/SafeNavigationScrollView"
import { Text } from "@/src/components/ui/typography/Text"
import { Settings1CuteFiIcon } from "@/src/icons/settings_1_cute_fi"
import { Settings1CuteReIcon } from "@/src/icons/settings_1_cute_re"
import type { TabScreenComponent } from "@/src/lib/navigation/bottom-tab/types"
import { useNavigation } from "@/src/lib/navigation/hooks"
import { ScreenItemContext } from "@/src/lib/navigation/ScreenItemContext"
@ -97,7 +95,3 @@ const EditProfileButton = () => {
)
}
export const SettingsTabScreen: TabScreenComponent = Settings
SettingsTabScreen.tabBarIcon = ({ focused, color }) => {
const Icon = !focused ? Settings1CuteReIcon : Settings1CuteFiIcon
return <Icon color={color} width={24} height={24} />
}

View File

@ -7,8 +7,6 @@ import { ErrorBoundary } from "@/src/components/common/ErrorBoundary"
import { NoLoginInfo } from "@/src/components/common/NoLoginInfo"
import { ListErrorView } from "@/src/components/errors/ListErrorView"
import { useResetTabOpacityWhenFocused } from "@/src/components/layouts/tabbar/hooks"
import { BlackBoard2CuteFiIcon } from "@/src/icons/black_board_2_cute_fi"
import { BlackBoard2CuteReIcon } from "@/src/icons/black_board_2_cute_re"
import type { TabScreenComponent } from "@/src/lib/navigation/bottom-tab/types"
import { EntryListContext } from "@/src/modules/screen/atoms"
import { PagerList } from "@/src/modules/screen/PagerList"
@ -37,10 +35,6 @@ export default function Subscriptions() {
}
export const SubscriptionsTabScreen: TabScreenComponent = Subscriptions
SubscriptionsTabScreen.tabBarIcon = ({ focused, color }) => {
const Icon = !focused ? BlackBoard2CuteReIcon : BlackBoard2CuteFiIcon
return <Icon color={color} width={24} height={24} />
}
const renderItem = (view: FeedViewType, active: boolean) => (
<ErrorBoundary fallbackRender={ListErrorView} key={view}>

View File

@ -27,7 +27,7 @@
"typecheck": "tsc --noEmit"
},
"peerDependencies": {
"react-native": ">=0.74.0"
"react-native": ">=0.79.6"
},
"dependencies": {
"@follow/shared": "workspace:*",
@ -46,6 +46,7 @@
},
"devDependencies": {
"@follow/configs": "workspace:*",
"react-native": "0.79.6",
"vite-tsconfig-paths": "5.1.4"
}
}

File diff suppressed because it is too large Load Diff