mirror of
https://github.com/Jellify-Music/App.git
synced 2026-01-06 19:09:52 -06:00
Perf-1
This commit is contained in:
committed by
Violet Caulfield
parent
fe7df38710
commit
8f976a2aaa
@@ -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>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
}}
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user