Migrate Network Status to Zustand Store (#499)

Moves the network status state to a Zustand Store for better reusability and to reduce the complexity of the network context

No new user facing changes
This commit is contained in:
Ritesh Shukla
2025-08-29 22:23:36 +05:30
committed by GitHub
parent 37b04c3f6b
commit 7a0ac95164
17 changed files with 108 additions and 28 deletions

View File

@@ -1,4 +1,6 @@
import 'react-native-gesture-handler'
// Initialize console override early - disable all console methods in production
import './src/utils/console-override'
import { AppRegistry } from 'react-native'
import App from './App'
import { name as appName } from './app.json'

View File

@@ -15,6 +15,7 @@ import { useSafeAreaFrame } from 'react-native-safe-area-context'
import Icon from '../Global/components/icon'
import { mapDtoToTrack } from '../../utils/mappings'
import { useNetworkContext } from '../../providers/Network'
import { useNetworkStatus } from '../../stores/network'
import { useLoadNewQueue } from '../../providers/Player/hooks/mutations'
import { QueuingType } from '../../enums/queuing-type'
import { useAlbumContext } from '../../providers/Album'
@@ -40,7 +41,8 @@ export function Album(): React.JSX.Element {
const { album, discs, isPending } = useAlbumContext()
const { api } = useJellifyContext()
const { useDownloadMultiple, pendingDownloads, networkStatus } = useNetworkContext()
const { useDownloadMultiple, pendingDownloads } = useNetworkContext()
const [networkStatus] = useNetworkStatus()
const streamingDeviceProfile = useStreamingDeviceProfile()
const downloadingDeviceProfile = useDownloadingDeviceProfile()
const { mutate: loadNewQueue } = useLoadNewQueue()

View File

@@ -17,7 +17,7 @@ import { QueuingType } from '../../enums/queuing-type'
import { fetchAlbumDiscs } from '../../api/queries/item'
import { NativeStackNavigationProp } from '@react-navigation/native-stack'
import { BaseStackParamList } from '../../screens/types'
import { useNetworkContext } from '../../providers/Network'
import { useNetworkStatus } from '../../stores/network'
import useStreamingDeviceProfile from '../../stores/device-profile'
export default function ArtistTabBar({
@@ -33,7 +33,7 @@ export default function ArtistTabBar({
const deviceProfile = useStreamingDeviceProfile()
const { networkStatus } = useNetworkContext()
const [networkStatus] = useNetworkStatus()
const { width } = useSafeAreaFrame()

View File

@@ -27,6 +27,7 @@ import { TextTickerConfig } from '../Player/component.config'
import { useSafeAreaInsets } from 'react-native-safe-area-context'
import { trigger } from 'react-native-haptic-feedback'
import { useAddToQueue } from '../../providers/Player/hooks/mutations'
import { useNetworkStatus } from '../../stores/network'
import { useNetworkContext } from '../../providers/Network'
import { mapDtoToTrack } from '../../utils/mappings'
import useStreamingDeviceProfile, { useDownloadingDeviceProfile } from '../../stores/device-profile'
@@ -164,7 +165,7 @@ function AddToPlaylistRow({ track }: { track: BaseItemDto }): React.JSX.Element
function AddToQueueMenuRow({ tracks }: { tracks: BaseItemDto[] }): React.JSX.Element {
const { api } = useJellifyContext()
const { networkStatus } = useNetworkContext()
const [networkStatus] = useNetworkStatus()
const deviceProfile = useStreamingDeviceProfile()

View File

@@ -13,7 +13,7 @@ import { NativeStackNavigationProp } from '@react-navigation/native-stack'
import { BaseStackParamList } from '../../../screens/types'
import { useLoadNewQueue } from '../../../providers/Player/hooks/mutations'
import { useJellifyContext } from '../../../providers'
import { useNetworkContext } from '../../../providers/Network'
import { useNetworkStatus } from '../../../stores/network'
import useStreamingDeviceProfile from '../../../stores/device-profile'
interface ItemRowProps {
@@ -43,7 +43,7 @@ export default function ItemRow({
}: ItemRowProps): React.JSX.Element {
const { api } = useJellifyContext()
const { networkStatus } = useNetworkContext()
const [networkStatus] = useNetworkStatus()
const deviceProfile = useStreamingDeviceProfile()

View File

@@ -8,7 +8,7 @@ import { QueuingType } from '../../../enums/queuing-type'
import { Queue } from '../../../player/types/queue-item'
import FavoriteIcon from './favorite-icon'
import { networkStatusTypes } from '../../../components/Network/internetConnectionWatcher'
import { useNetworkContext } from '../../../providers/Network'
import { useNetworkStatus } from '../../../stores/network'
import DownloadedIcon from './downloaded-icon'
import navigationRef from '../../../../navigation'
import { NativeStackNavigationProp } from '@react-navigation/native-stack'
@@ -63,7 +63,7 @@ export default function Track({
const { data: nowPlaying } = useNowPlaying()
const { data: playQueue } = useQueue()
const { mutate: loadNewQueue } = useLoadNewQueue()
const { networkStatus } = useNetworkContext()
const [networkStatus] = useNetworkStatus()
const { data: mediaInfo } = useStreamedMediaInfo(track.Id)

View File

@@ -12,13 +12,13 @@ import HomeStackParamList from '../../../screens/Home/types'
import { useNavigation } from '@react-navigation/native'
import { RootStackParamList } from '../../../screens/types'
import { useJellifyContext } from '../../../providers'
import { useNetworkContext } from '../../../providers/Network'
import { useNetworkStatus } from '../../../stores/network'
import useStreamingDeviceProfile from '../../../stores/device-profile'
export default function FrequentlyPlayedTracks(): React.JSX.Element {
const { api } = useJellifyContext()
const { networkStatus } = useNetworkContext()
const [networkStatus] = useNetworkStatus()
const deviceProfile = useStreamingDeviceProfile()

View File

@@ -14,13 +14,13 @@ import { useNavigation } from '@react-navigation/native'
import HomeStackParamList from '../../../screens/Home/types'
import { useNowPlaying } from '../../../providers/Player/hooks/queries'
import { useJellifyContext } from '../../../providers'
import { useNetworkContext } from '../../../providers/Network'
import { useNetworkStatus } from '../../../stores/network'
import useStreamingDeviceProfile from '../../../stores/device-profile'
export default function RecentlyPlayed(): React.JSX.Element {
const { api } = useJellifyContext()
const { networkStatus } = useNetworkContext()
const [networkStatus] = useNetworkStatus()
const deviceProfile = useStreamingDeviceProfile()

View File

@@ -11,7 +11,7 @@ import Animated, {
} from 'react-native-reanimated'
import { Text } from '../Global/helpers/text'
import { useNetworkContext } from '../../providers/Network'
import { useNetworkStatus } from '../../stores/network'
const internetConnectionWatcher = {
NO_INTERNET: 'You are offline',
@@ -28,7 +28,7 @@ const isAndroid = Platform.OS === 'android'
const InternetConnectionWatcher = () => {
// const [networkStatus, setNetworkStatus] = useState<keyof typeof networkStatusTypes | null>(null)
const lastNetworkStatus = useRef<networkStatusTypes | null>(networkStatusTypes.ONLINE)
const { networkStatus, setNetworkStatus } = useNetworkContext()
const [networkStatus, setNetworkStatus] = useNetworkStatus()
const bannerHeight = useSharedValue(0)
const opacity = useSharedValue(0)

View File

@@ -11,6 +11,7 @@ import FastImage from 'react-native-fast-image'
import { getImageApi } from '@jellyfin/sdk/lib/utils/api'
import { useJellifyContext } from '../../../providers'
import { ImageType } from '@jellyfin/sdk/lib/generated-client/models'
import { useNetworkStatus } from '../../../../src/stores/network'
import { useNetworkContext } from '../../../../src/providers/Network'
import { ActivityIndicator } from 'react-native'
import { mapDtoToTrack } from '../../../utils/mappings'
@@ -156,7 +157,7 @@ function PlaylistHeaderControls({
const isDownloading = pendingDownloads.length != 0
const { api } = useJellifyContext()
const { networkStatus } = useNetworkContext()
const [networkStatus] = useNetworkStatus()
const navigation = useNavigation<NativeStackNavigationProp<LibraryStackParamList>>()

View File

@@ -3,8 +3,8 @@ import { StyleSheet, Pressable, Alert, FlatList } from 'react-native'
import RNFS from 'react-native-fs'
import Animated, { useSharedValue, useAnimatedStyle, withTiming } from 'react-native-reanimated'
import { deleteAudioCache } from '../../api/mutations/download/offlineModeUtils'
import { useNetworkContext } from '../../providers/Network'
import Icon from '../Global/components/icon'
import { useNetworkContext } from '../../providers/Network'
import { getToken, View } from 'tamagui'
import { Text } from '../Global/helpers/text'
import { useAllDownloadedTracks } from '../../api/queries/download'

View File

@@ -3,7 +3,6 @@ import Track from '../Global/components/track'
import { getTokens, Separator } from 'tamagui'
import { BaseItemDto, UserItemDataDto } from '@jellyfin/sdk/lib/generated-client/models'
import { Queue } from '../../player/types/queue-item'
import { useNetworkContext } from '../../providers/Network'
import { queryClient } from '../../constants/query-client'
import { QueryKeys } from '../../enums/query-keys'
import { FlashList, ViewToken } from '@shopify/flash-list'

View File

@@ -4,7 +4,7 @@ import { Platform } from 'react-native'
import { CarPlay } from 'react-native-carplay'
import { useJellifyContext } from '../index'
import { useLoadNewQueue } from '../Player/hooks/mutations'
import { useNetworkContext } from '../Network'
import { useNetworkStatus } from '../../stores/network'
import useStreamingDeviceProfile from '../../stores/device-profile'
interface CarPlayContext {
@@ -15,7 +15,7 @@ const CarPlayContextInitializer = () => {
const { api, library } = useJellifyContext()
const [carplayConnected, setCarPlayConnected] = useState(CarPlay ? CarPlay.connected : false)
const { networkStatus } = useNetworkContext()
const [networkStatus] = useNetworkStatus()
const deviceProfile = useStreamingDeviceProfile()

View File

@@ -9,8 +9,6 @@ import { useAllDownloadedTracks } from '../../api/queries/download'
interface NetworkContext {
useDownloadMultiple: UseMutateFunction<boolean, Error, JellifyTrack[], unknown>
activeDownloads: JellifyDownloadProgress | undefined
networkStatus: networkStatusTypes | null
setNetworkStatus: (status: networkStatusTypes | null) => void
pendingDownloads: JellifyTrack[]
downloadingDownloads: JellifyTrack[]
completedDownloads: JellifyTrack[]
@@ -20,7 +18,6 @@ interface NetworkContext {
const MAX_CONCURRENT_DOWNLOADS = 1
const NetworkContextInitializer = () => {
const [downloadProgress, setDownloadProgress] = useState<JellifyDownloadProgress>({})
const [networkStatus, setNetworkStatus] = useState<networkStatusTypes | null>(null)
// Mutiple Downloads
const [pending, setPending] = useState<JellifyTrack[]>([])
@@ -76,8 +73,6 @@ const NetworkContextInitializer = () => {
return {
activeDownloads: downloadProgress,
downloadedTracks,
networkStatus,
setNetworkStatus,
useDownloadMultiple,
pendingDownloads: pending,
downloadingDownloads: downloading,
@@ -88,8 +83,6 @@ const NetworkContextInitializer = () => {
const NetworkContext = createContext<NetworkContext>({
activeDownloads: {},
networkStatus: networkStatusTypes.ONLINE,
setNetworkStatus: () => {},
useDownloadMultiple: () => {},
pendingDownloads: [],
downloadingDownloads: [],
@@ -109,7 +102,6 @@ export const NetworkContextProvider: ({
() => context,
[
context.downloadedTracks?.length,
context.networkStatus,
context.pendingDownloads.length,
context.downloadingDownloads.length,
context.completedDownloads.length,

View File

@@ -4,7 +4,6 @@ import { H5, Text } from '../../components/Global/helpers/text'
import Button from '../../components/Global/helpers/button'
import Icon from '../../components/Global/components/icon'
import { useJellifyContext } from '../../providers'
import { useNetworkContext } from '../../providers/Network'
import { useResetQueue } from '../../providers/Player/hooks/mutations'
import navigationRef from '../../../navigation'
import { useClearAllDownloads } from '../../api/mutations/download'

30
src/stores/network.ts Normal file
View File

@@ -0,0 +1,30 @@
import { create } from 'zustand'
import { devtools } from 'zustand/middleware'
import { networkStatusTypes } from '../components/Network/internetConnectionWatcher'
type NetworkStore = {
networkStatus: networkStatusTypes | null
setNetworkStatus: (status: networkStatusTypes | null) => void
}
export const useNetworkStore = create<NetworkStore>()(
devtools(
(set) => ({
networkStatus: null,
setNetworkStatus: (networkStatus) => set({ networkStatus }),
}),
{
name: 'network-store',
},
),
)
export const useNetworkStatus = (): [
networkStatusTypes | null,
(status: networkStatusTypes | null) => void,
] => {
const networkStatus = useNetworkStore((state) => state.networkStatus)
const setNetworkStatus = useNetworkStore((state) => state.setNetworkStatus)
return [networkStatus, setNetworkStatus]
}

View File

@@ -0,0 +1,54 @@
/**
* Global Console Override for Production Builds
*
* This utility disables ALL console methods in production builds by replacing them with no-op functions.
* In development, console methods work normally.
*
* Usage: Import this file early in your app's entry point (index.js)
*
* Benefits:
* - No need to modify existing console.log statements throughout the codebase
* - Catches all console usage (log, warn, error, info, debug, etc.)
* - Reduces bundle size and improves performance in production
* - Prevents sensitive debug information from appearing in production
*/
// No-op function for production console methods
const noop = () => {}
/**
* Initialize console override
* Disables all console methods in production builds (__DEV__ = false)
* Preserves console methods in development builds (__DEV__ = true)
*/
export const initializeConsoleOverride = () => {
if (!__DEV__) {
// Production: Replace all console methods with no-op functions
console.log = noop
console.warn = noop
console.error = noop
console.info = noop
console.debug = noop
console.trace = noop
console.table = noop
console.group = noop
console.groupCollapsed = noop
console.groupEnd = noop
console.time = noop
console.timeEnd = noop
console.timeLog = noop
console.count = noop
console.countReset = noop
console.assert = noop
console.clear = noop
console.dir = noop
console.dirxml = noop
console.profile = noop
console.profileEnd = noop
console.timeStamp = noop
}
// In development (__DEV__ = true), console methods remain unchanged
}
// Auto-initialize when this module is imported
initializeConsoleOverride()