This commit is contained in:
riteshshukla04
2025-08-14 22:20:04 +05:30
committed by Violet Caulfield
parent fe7df38710
commit 8f976a2aaa
5 changed files with 50 additions and 29 deletions

View File

@@ -37,6 +37,15 @@ export default function Albums({
return 'album'
}, [])
// Memoize expensive stickyHeaderIndices calculation to prevent unnecessary re-computations
const stickyHeaderIndices = React.useMemo(() => {
if (!showAlphabeticalSelector || !albums) return []
return albums
.map((album, index) => (typeof album === 'string' ? index : 0))
.filter((value, index, indices) => indices.indexOf(value) === index)
}, [showAlphabeticalSelector, albums])
return (
<XStack flex={1}>
<FlashList
@@ -82,15 +91,7 @@ export default function Albums({
ListFooterComponent={isPending ? <ActivityIndicator /> : null}
ItemSeparatorComponent={() => <Separator />}
refreshControl={<RefreshControl refreshing={isPending} />}
stickyHeaderIndices={
showAlphabeticalSelector
? albums
?.map((album, index, albums) =>
typeof album === 'string' ? index : 0,
)
.filter((value, index, indices) => indices.indexOf(value) === index)
: []
}
stickyHeaderIndices={stickyHeaderIndices}
removeClippedSubviews
/>
</XStack>

View File

@@ -31,6 +31,7 @@ export default function Library({
tabBarLabelStyle: {
fontFamily: 'Figtree-Bold',
},
lazy: true, // Enable lazy loading to prevent all tabs from mounting simultaneously
}}
>
<LibraryTabsNavigator.Screen

View File

@@ -31,7 +31,8 @@ export default function Tracks({
}): React.JSX.Element {
const { downloadedTracks } = useNetworkContext()
const tracksToDisplay: () => BaseItemDto[] = useCallback(() => {
// Memoize the expensive tracks processing to prevent memory leaks
const tracksToDisplay = React.useMemo(() => {
if (filterDownloaded) {
return (
downloadedTracks
@@ -54,6 +55,24 @@ export default function Tracks({
return tracks?.pages.flatMap((page) => page) ?? []
}, [filterDownloaded, downloadedTracks, tracks, filterFavorites])
// Memoize key extraction for FlashList performance
const keyExtractor = React.useCallback((item: BaseItemDto) => item.Id!, [])
// Memoize render item to prevent recreation
const renderItem = React.useCallback(
({ index, item: track }: { index: number; item: BaseItemDto }) => (
<Track
navigation={navigation}
showArtwork
index={0}
track={track}
tracklist={tracksToDisplay.slice(index, index + 50)}
queue={queue}
/>
),
[navigation, tracksToDisplay, queue],
)
return (
<FlashList
contentInsetAdjustmentBehavior='automatic'
@@ -62,17 +81,9 @@ export default function Tracks({
}}
ItemSeparatorComponent={() => <Separator />}
numColumns={1}
data={tracksToDisplay()}
renderItem={({ index, item: track }) => (
<Track
navigation={navigation}
showArtwork
index={0}
track={track}
tracklist={tracksToDisplay().slice(index, index + 50)}
queue={queue}
/>
)}
data={tracksToDisplay}
keyExtractor={keyExtractor}
renderItem={renderItem}
onEndReached={() => {
if (hasNextPage) fetchNextPage()
}}

View File

@@ -3,9 +3,8 @@ import { QueryClient } from '@tanstack/react-query'
/**
* A global instance of the Tanstack React Query client
*
* Garbage collection is disabled by default, as we are using MMKV
* as a client persister. This may need to be re-evaluated
* at some point if storage usage becomes a problem
* Memory management optimized for mobile devices to prevent memory buildup
* while still maintaining good performance with MMKV persistence
*
* Default stale time is set to 1 hour. Users have the option
* to refresh relevant datasets by design (i.e. refreshing
@@ -15,15 +14,16 @@ export const queryClient = new QueryClient({
defaultOptions: {
queries: {
/**
* Infinity, this needs to be greater than
* or higher than the `maxAge` set in App.tsx
* 30 minutes GC time for better memory management
* This prevents excessive memory usage while still keeping
* recent data in memory for performance
*/
gcTime: Infinity,
gcTime: 1000 * 60 * 30, // 30 minutes
/**
* 2 hours as a default.
* 1 hour as a default - reduced from 2 hours for better battery usage
*/
staleTime: 1000 * 60 * 60 * 2, // 2 hours
staleTime: 1000 * 60 * 60, // 1 hour
retry(failureCount, error) {
if (failureCount > 2) return false

View File

@@ -123,6 +123,8 @@ const LibraryContextInitializer = () => {
),
select: selectArtists,
initialPageParam: 0,
staleTime: QueryConfig.staleTime.oneDay, // Cache for 1 day to reduce network requests
gcTime: QueryConfig.staleTime.oneWeek, // Keep in memory for 1 week
getNextPageParam: (lastPage, allPages, lastPageParam, allPageParams) => {
return lastPage.length === QueryConfig.limits.library ? lastPageParam + 1 : undefined
},
@@ -152,6 +154,8 @@ const LibraryContextInitializer = () => {
sortDescending ? SortOrder.Descending : SortOrder.Ascending,
),
initialPageParam: 0,
staleTime: QueryConfig.staleTime.oneDay, // Cache for 1 day to reduce network requests
gcTime: QueryConfig.staleTime.oneWeek, // Keep in memory for 1 week
getNextPageParam: (lastPage, allPages, lastPageParam, allPageParams) => {
console.debug(`Tracks last page length: ${lastPage.length}`)
return lastPage.length === QueryConfig.limits.library * 2
@@ -182,6 +186,8 @@ const LibraryContextInitializer = () => {
initialPageParam: alphabet[0],
select: (data) => data.pages.flatMap((page) => [page.title, ...page.data]),
maxPages: alphabet.length,
staleTime: QueryConfig.staleTime.oneDay, // Cache for 1 day to reduce network requests
gcTime: QueryConfig.staleTime.oneWeek, // Keep in memory for 1 week
getNextPageParam: (lastPage, allPages, lastPageParam, allPageParams) => {
console.debug(`Albums last page length: ${lastPage.data.length}`)
if (lastPageParam !== alphabet[alphabet.length - 1]) {
@@ -219,6 +225,8 @@ const LibraryContextInitializer = () => {
queryFn: () => fetchUserPlaylists(api, user, library),
select: (data) => data.pages.flatMap((page) => page),
initialPageParam: 0,
staleTime: QueryConfig.staleTime.oneDay, // Cache for 1 day to reduce network requests
gcTime: QueryConfig.staleTime.oneWeek, // Keep in memory for 1 week
getNextPageParam: (lastPage, allPages, lastPageParam, allPageParams) => {
return lastPage.length === QueryConfig.limits.library ? lastPageParam + 1 : undefined
},