diff --git a/App.tsx b/App.tsx index 8db3b98a..9bc09494 100644 --- a/App.tsx +++ b/App.tsx @@ -24,6 +24,7 @@ import { registerAutoService } from './src/services/carplay' import QueryPersistenceConfig from './src/configs/query-persistence.config' import registerTrackPlayer from './src/services/player' import configureDownloadManager from './src/services/downloads' +import { ReducedMotionConfig, ReduceMotion } from 'react-native-reanimated' LogBox.ignoreAllLogs() @@ -88,6 +89,7 @@ function Container(): React.JSX.Element { theme={getJellifyNavTheme(colorPreset, resolvedMode)} > + diff --git a/android/app/build.gradle b/android/app/build.gradle index df004d7c..360d058e 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -88,8 +88,8 @@ android { applicationId "com.cosmonautical.jellify" minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion - versionCode 176 - versionName "1.0.17" + versionCode 177 + versionName "1.0.18" } signingConfigs { diff --git a/bun.lock b/bun.lock index 4adc318d..4b1374bc 100644 --- a/bun.lock +++ b/bun.lock @@ -41,7 +41,7 @@ "react-native-nitro-ota": "0.11.0", "react-native-nitro-player": "0.5.6", "react-native-pager-view": "8.0.0", - "react-native-reanimated": "4.2.1", + "react-native-reanimated": "4.1.6", "react-native-safe-area-context": "5.6.2", "react-native-screens": "4.23.0", "react-native-sortables": "1.9.4", @@ -1914,7 +1914,7 @@ "react-native-pager-view": ["react-native-pager-view@8.0.0", "", { "peerDependencies": { "react": "*", "react-native": "*" } }, "sha512-oAwlWT1lhTkIs9HhODnjNNl/owxzn9DP1MbP+az6OTUdgbmzA16Up83sBH8NRKwrH8rNm7iuWnX1qMqiiWOLhg=="], - "react-native-reanimated": ["react-native-reanimated@4.2.1", "", { "dependencies": { "react-native-is-edge-to-edge": "1.2.1", "semver": "7.7.3" }, "peerDependencies": { "react": "*", "react-native": "*", "react-native-worklets": ">=0.7.0" } }, "sha512-/NcHnZMyOvsD/wYXug/YqSKw90P9edN0kEPL5lP4PFf1aQ4F1V7MKe/E0tvfkXKIajy3Qocp5EiEnlcrK/+BZg=="], + "react-native-reanimated": ["react-native-reanimated@4.1.6", "", { "dependencies": { "react-native-is-edge-to-edge": "^1.2.1", "semver": "7.7.2" }, "peerDependencies": { "@babel/core": "^7.0.0-0", "react": "*", "react-native": "*", "react-native-worklets": ">=0.5.0" } }, "sha512-F+ZJBYiok/6Jzp1re75F/9aLzkgoQCOh4yxrnwATa8392RvM3kx+fiXXFvwcgE59v48lMwd9q0nzF1oJLXpfxQ=="], "react-native-safe-area-context": ["react-native-safe-area-context@5.6.2", "", { "peerDependencies": { "react": "*", "react-native": "*" } }, "sha512-4XGqMNj5qjUTYywJqpdWZ9IG8jgkS3h06sfVjfw5yZQZfWnRFXczi0GnYyFyCc2EBps/qFmoCH8fez//WumdVg=="], diff --git a/ios/Jellify.xcodeproj/project.pbxproj b/ios/Jellify.xcodeproj/project.pbxproj index 3f2903b8..e44a8812 100644 --- a/ios/Jellify.xcodeproj/project.pbxproj +++ b/ios/Jellify.xcodeproj/project.pbxproj @@ -543,7 +543,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 284; + CURRENT_PROJECT_VERSION = 285; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = WAH9CZ8BPG; ENABLE_BITCODE = NO; @@ -554,7 +554,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.0.17; + MARKETING_VERSION = 1.0.18; NEW_SETTING = ""; OTHER_LDFLAGS = ( "$(inherited)", @@ -585,7 +585,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 284; + CURRENT_PROJECT_VERSION = 285; DEVELOPMENT_TEAM = WAH9CZ8BPG; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = WAH9CZ8BPG; ENABLE_USER_SCRIPT_SANDBOXING = NO; @@ -595,7 +595,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.0.17; + MARKETING_VERSION = 1.0.18; NEW_SETTING = ""; OTHER_LDFLAGS = ( "$(inherited)", @@ -833,7 +833,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 284; + CURRENT_PROJECT_VERSION = 285; DEVELOPMENT_TEAM = WAH9CZ8BPG; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = WAH9CZ8BPG; ENABLE_USER_SCRIPT_SANDBOXING = NO; @@ -844,7 +844,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.0.17; + MARKETING_VERSION = 1.0.18; NEW_SETTING = ""; OTHER_LDFLAGS = ( "$(inherited)", diff --git a/ios/Podfile.lock b/ios/Podfile.lock index c3c4c618..1831c492 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -2198,7 +2198,7 @@ PODS: - ReactCommon/turbomodule/core - ReactNativeDependencies - Yoga - - RNReanimated (4.2.1): + - RNReanimated (4.1.6): - hermes-engine - RCTRequired - RCTTypeSafety @@ -2220,10 +2220,10 @@ PODS: - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - ReactNativeDependencies - - RNReanimated/reanimated (= 4.2.1) + - RNReanimated/reanimated (= 4.1.6) - RNWorklets - Yoga - - RNReanimated/reanimated (4.2.1): + - RNReanimated/reanimated (4.1.6): - hermes-engine - RCTRequired - RCTTypeSafety @@ -2245,10 +2245,10 @@ PODS: - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - ReactNativeDependencies - - RNReanimated/reanimated/apple (= 4.2.1) + - RNReanimated/reanimated/apple (= 4.1.6) - RNWorklets - Yoga - - RNReanimated/reanimated/apple (4.2.1): + - RNReanimated/reanimated/apple (4.1.6): - hermes-engine - RCTRequired - RCTTypeSafety @@ -2829,7 +2829,7 @@ SPEC CHECKSUMS: RNDeviceInfo: 36d7f232bfe7c9b5c494cb7793230424ed32c388 RNGestureHandler: 07de6f059e0ee5744ae9a56feb07ee345338cc31 RNReactNativeHapticFeedback: d7bc1bcfe68bb139c9919ea81d11d7c6e866e870 - RNReanimated: 5a8239f0bd8835225707968c1357b19b447d8681 + RNReanimated: caefad1cdf778187bc12b747ff958ee7c6d2e580 RNScreens: 14243fa0d9842ffa7f8bb2d00b6c3cfd3ca817e8 RNSentry: c6ec62a7d10299ac5d8e0916202913da77bb9fc7 RNWorklets: b6bad5e2ccfdddc4751bca114acf1680dc3231d4 diff --git a/package.json b/package.json index 4ac1ae36..e940da4a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "jellify", - "version": "1.0.17", + "version": "1.0.18", "private": true, "scripts": { "init-android": "bun i", @@ -74,7 +74,7 @@ "react-native-nitro-ota": "0.11.0", "react-native-nitro-player": "0.5.6", "react-native-pager-view": "8.0.0", - "react-native-reanimated": "4.2.1", + "react-native-reanimated": "4.1.6", "react-native-safe-area-context": "5.6.2", "react-native-screens": "4.23.0", "react-native-sortables": "1.9.4", diff --git a/patches/react-native-reanimated+4.1.6.patch b/patches/react-native-reanimated+4.1.6.patch new file mode 100644 index 00000000..999dc9dd --- /dev/null +++ b/patches/react-native-reanimated+4.1.6.patch @@ -0,0 +1,13 @@ +diff --git a/node_modules/react-native-reanimated/compatibility.json b/node_modules/react-native-reanimated/compatibility.json +index f1ba9cb..ffab7fe 100644 +--- a/node_modules/react-native-reanimated/compatibility.json ++++ b/node_modules/react-native-reanimated/compatibility.json +@@ -4,7 +4,7 @@ + "react-native-worklets": ["nightly"] + }, + "4.1.x": { +- "react-native": ["0.78", "0.79", "0.80", "0.81", "0.82"], ++ "react-native": ["0.78", "0.79", "0.80", "0.81", "0.82", "0.83", "0.84"], + "react-native-worklets": ["0.5.x", "0.6.x", "0.7.x"] + }, + "4.0.x": { diff --git a/patches/react-native-reanimated+4.2.1.patch b/patches/react-native-reanimated+4.2.1.patch deleted file mode 100644 index 3ffc1248..00000000 --- a/patches/react-native-reanimated+4.2.1.patch +++ /dev/null @@ -1,13 +0,0 @@ -diff --git a/node_modules/react-native-reanimated/compatibility.json b/node_modules/react-native-reanimated/compatibility.json -index e278e81..97cfddf 100644 ---- a/node_modules/react-native-reanimated/compatibility.json -+++ b/node_modules/react-native-reanimated/compatibility.json -@@ -5,7 +5,7 @@ - "react-native-worklets": ["nightly"] - }, - "4.2.x": { -- "react-native": ["0.80", "0.81", "0.82", "0.83"], -+ "react-native": ["0.80", "0.81", "0.82", "0.83", "0.84"], - "react-native-worklets": ["0.7.x"] - }, - "4.1.x": { diff --git a/src/api/queries/search/index.ts b/src/api/queries/search/index.ts new file mode 100644 index 00000000..db0866d8 --- /dev/null +++ b/src/api/queries/search/index.ts @@ -0,0 +1,19 @@ +import { QueryKeys } from '../../../enums/query-keys' +import { useQuery } from '@tanstack/react-query' +import { fetchSearchResults } from './utils' +import { ONE_MINUTE } from '../../../constants/query-client' +import { useJellifyLibrary } from '../../../stores' + +const useSearchResults = (searchString: string | undefined) => { + const [library] = useJellifyLibrary() + + return useQuery({ + queryKey: [QueryKeys.Search, library?.musicLibraryId, searchString], + queryFn: () => fetchSearchResults(library?.musicLibraryId, searchString), + staleTime: ONE_MINUTE * 10, // Cache results for 10 minutes + gcTime: ONE_MINUTE * 15, // Garbage collect after 15 minutes + enabled: !!library?.musicLibraryId && !!searchString, // Only run if we have a library ID and a search string + }) +} + +export default useSearchResults diff --git a/src/api/queries/search.ts b/src/api/queries/search/utils/index.ts similarity index 87% rename from src/api/queries/search.ts rename to src/api/queries/search/utils/index.ts index 693d4984..b8b43280 100644 --- a/src/api/queries/search.ts +++ b/src/api/queries/search/utils/index.ts @@ -1,9 +1,8 @@ import { BaseItemDto } from '@jellyfin/sdk/lib/generated-client/models' import { getItemsApi } from '@jellyfin/sdk/lib/utils/api' import { isEmpty, isUndefined, trim } from 'lodash' -import QueryConfig from '../../configs/query.config' -import { Api } from '@jellyfin/sdk' -import { JellifyUser } from '../../types/JellifyUser' +import QueryConfig from '../../../../configs/query.config' +import { getApi, getUser } from '../../../../stores' /** * Performs a search for items against the Jellyfin server, trimming whitespace * around the search term for the best possible results. @@ -11,12 +10,13 @@ import { JellifyUser } from '../../types/JellifyUser' * @returns A promise of a BaseItemDto array, be it empty or not */ export async function fetchSearchResults( - api: Api | undefined, - user: JellifyUser | undefined, libraryId: string | undefined, searchString: string | undefined, ): Promise { return new Promise((resolve, reject) => { + const api = getApi() + const user = getUser() + if (isEmpty(searchString)) resolve([]) if (isUndefined(api)) return reject('Client instance not set') diff --git a/src/components/Discover/helpers/just-added.tsx b/src/components/Discover/helpers/just-added.tsx index 1c15dc35..635ee21f 100644 --- a/src/components/Discover/helpers/just-added.tsx +++ b/src/components/Discover/helpers/just-added.tsx @@ -7,9 +7,9 @@ import { useNavigation } from '@react-navigation/native' import DiscoverStackParamList from '../../../screens/Discover/types' import navigationRef from '../../../../navigation' import { useRecentlyAddedAlbums } from '../../../api/queries/album' -import Animated, { Easing, FadeIn, FadeOut, LinearTransition } from 'react-native-reanimated' +import AnimatedRow from '../../Global/helpers/animated-row' -export default function RecentlyAdded(): React.JSX.Element | null { +export default function RecentlyAdded(): React.JSX.Element { const recentlyAddedAlbumsInfinityQuery = useRecentlyAddedAlbums() const navigation = useNavigation>() @@ -18,15 +18,7 @@ export default function RecentlyAdded(): React.JSX.Element | null { recentlyAddedAlbumsInfinityQuery.data && recentlyAddedAlbumsInfinityQuery.data.length > 0 return recentlyAddedExists ? ( - + { @@ -64,6 +56,8 @@ export default function RecentlyAdded(): React.JSX.Element | null { /> )} /> - - ) : null + + ) : ( + <> + ) } diff --git a/src/components/Discover/helpers/public-playlists.tsx b/src/components/Discover/helpers/public-playlists.tsx index 97eaf8a4..a1b39aa2 100644 --- a/src/components/Discover/helpers/public-playlists.tsx +++ b/src/components/Discover/helpers/public-playlists.tsx @@ -9,9 +9,9 @@ import DiscoverStackParamList from '../../../screens/Discover/types' import navigationRef from '../../../../navigation' import { useJellifyServer } from '../../../stores' import { usePublicPlaylists } from '../../../api/queries/playlist' -import Animated, { Easing, FadeIn, FadeOut, LinearTransition } from 'react-native-reanimated' +import AnimatedRow from '../../Global/helpers/animated-row' -export default function PublicPlaylists(): React.JSX.Element | null { +export default function PublicPlaylists(): React.JSX.Element { const { data: playlists, fetchNextPage, @@ -29,15 +29,7 @@ export default function PublicPlaylists(): React.JSX.Element | null { const publicPlaylistsExist = playlists && playlists.length > 0 return publicPlaylistsExist ? ( - + { @@ -80,6 +72,8 @@ export default function PublicPlaylists(): React.JSX.Element | null { /> )} /> - - ) : null + + ) : ( + <> + ) } diff --git a/src/components/Discover/helpers/suggested-albums.tsx b/src/components/Discover/helpers/suggested-albums.tsx index 585a0728..94e0c5ca 100644 --- a/src/components/Discover/helpers/suggested-albums.tsx +++ b/src/components/Discover/helpers/suggested-albums.tsx @@ -1,6 +1,5 @@ import navigationRef from '../../../../navigation' import { formatArtistNames } from '../../../utils/formatting/artist-names' -import Animated, { Easing, FadeIn, FadeOut, LinearTransition } from 'react-native-reanimated' import ItemCard from '../../Global/components/item-card' import HorizontalCardList from '../../Global/components/horizontal-list' import { XStack } from 'tamagui' @@ -10,6 +9,7 @@ import { useNavigation } from '@react-navigation/native' import { NativeStackNavigationProp } from '@react-navigation/native-stack' import DiscoverStackParamList from '../../../screens/Discover/types' import { useDiscoverAlbums } from '../../../api/queries/suggestions' +import AnimatedRow from '../../Global/helpers/animated-row' export default function SuggestedAlbums() { const suggestedAlbumsInfiniteQuery = useDiscoverAlbums() @@ -20,15 +20,7 @@ export default function SuggestedAlbums() { suggestedAlbumsInfiniteQuery.data && suggestedAlbumsInfiniteQuery.data.length > 0 return suggestedAlbumsExist ? ( - + { @@ -64,6 +56,8 @@ export default function SuggestedAlbums() { /> )} /> - - ) : null + + ) : ( + <> + ) } diff --git a/src/components/Discover/helpers/suggested-artists.tsx b/src/components/Discover/helpers/suggested-artists.tsx index f29ffa7a..e0208940 100644 --- a/src/components/Discover/helpers/suggested-artists.tsx +++ b/src/components/Discover/helpers/suggested-artists.tsx @@ -7,10 +7,10 @@ import { useNavigation } from '@react-navigation/native' import DiscoverStackParamList from '../../../screens/Discover/types' import navigationRef from '../../../../navigation' import { pickFirstGenre } from '../../../utils/formatting/genres' -import Animated, { Easing, FadeIn, FadeOut, LinearTransition } from 'react-native-reanimated' import { useDiscoverArtists } from '../../../api/queries/suggestions' +import AnimatedRow from '../../Global/helpers/animated-row' -export default function SuggestedArtists(): React.JSX.Element | null { +export default function SuggestedArtists(): React.JSX.Element { const suggestedArtistsInfiniteQuery = useDiscoverArtists() const navigation = useNavigation>() @@ -19,15 +19,7 @@ export default function SuggestedArtists(): React.JSX.Element | null { suggestedArtistsInfiniteQuery.data && suggestedArtistsInfiniteQuery.data.length > 0 return suggestedArtistsExist ? ( - + { @@ -63,6 +55,8 @@ export default function SuggestedArtists(): React.JSX.Element | null { /> )} /> - - ) : null + + ) : ( + <> + ) } diff --git a/src/components/Global/helpers/animated-row.tsx b/src/components/Global/helpers/animated-row.tsx new file mode 100644 index 00000000..00352ba9 --- /dev/null +++ b/src/components/Global/helpers/animated-row.tsx @@ -0,0 +1,28 @@ +import Animated, { + FadeIn, + ReduceMotion, + FadeOut, + LinearTransition, + Easing, +} from 'react-native-reanimated' + +interface AnimatedRowProps { + children: React.ReactNode + testID?: string +} + +export default function AnimatedRow({ children, testID }: AnimatedRowProps) { + return ( + + {children} + + ) +} diff --git a/src/components/Home/helpers/frequent-artists.tsx b/src/components/Home/helpers/frequent-artists.tsx index 68e6f628..af3f55e4 100644 --- a/src/components/Home/helpers/frequent-artists.tsx +++ b/src/components/Home/helpers/frequent-artists.tsx @@ -11,7 +11,7 @@ import { RootStackParamList } from '../../../screens/types' import { useFrequentlyPlayedArtists } from '../../../api/queries/frequents' import { BaseItemDto } from '@jellyfin/sdk/lib/generated-client' import { pickFirstGenre } from '../../../utils/formatting/genres' -import Animated, { Easing, FadeIn, FadeOut, LinearTransition } from 'react-native-reanimated' +import AnimatedRow from '../../Global/helpers/animated-row' export default function FrequentArtists(): React.JSX.Element { const navigation = useNavigation>() @@ -41,14 +41,7 @@ export default function FrequentArtists(): React.JSX.Element { ) return frequentArtistsInfiniteQuery.data ? ( - + { @@ -63,7 +56,7 @@ export default function FrequentArtists(): React.JSX.Element { data={frequentArtistsInfiniteQuery.data.slice(0, horizontalItems) ?? []} renderItem={renderItem} /> - + ) : ( <> ) diff --git a/src/components/Home/helpers/frequent-tracks.tsx b/src/components/Home/helpers/frequent-tracks.tsx index 0c299d4b..870fc6a5 100644 --- a/src/components/Home/helpers/frequent-tracks.tsx +++ b/src/components/Home/helpers/frequent-tracks.tsx @@ -10,7 +10,7 @@ import HomeStackParamList from '../../../screens/Home/types' import { useNavigation } from '@react-navigation/native' import { RootStackParamList } from '../../../screens/types' import { useFrequentlyPlayedTracks } from '../../../api/queries/frequents' -import Animated, { Easing, FadeIn, FadeOut, LinearTransition } from 'react-native-reanimated' +import AnimatedRow from '../../Global/helpers/animated-row' export default function FrequentlyPlayedTracks(): React.JSX.Element { const tracksInfiniteQuery = useFrequentlyPlayedTracks() @@ -23,14 +23,7 @@ export default function FrequentlyPlayedTracks(): React.JSX.Element { const { horizontalItems } = useDisplayContext() return tracksInfiniteQuery.data ? ( - + { @@ -74,7 +67,7 @@ export default function FrequentlyPlayedTracks(): React.JSX.Element { /> )} /> - + ) : ( <> ) diff --git a/src/components/Home/helpers/recent-artists.tsx b/src/components/Home/helpers/recent-artists.tsx index b66b80d2..63b108dc 100644 --- a/src/components/Home/helpers/recent-artists.tsx +++ b/src/components/Home/helpers/recent-artists.tsx @@ -11,7 +11,7 @@ import HomeStackParamList from '../../../screens/Home/types' import { useRecentArtists } from '../../../api/queries/recents' import { pickFirstGenre } from '../../../utils/formatting/genres' import { BaseItemDto } from '@jellyfin/sdk/lib/generated-client/models/base-item-dto' -import Animated, { Easing, FadeIn, FadeOut, LinearTransition } from 'react-native-reanimated' +import AnimatedRow from '../../Global/helpers/animated-row' export default function RecentArtists(): React.JSX.Element { const recentArtistsInfiniteQuery = useRecentArtists() @@ -47,14 +47,7 @@ export default function RecentArtists(): React.JSX.Element { ) return recentArtistsInfiniteQuery.data ? ( - +
Recent Artists
@@ -64,7 +57,7 @@ export default function RecentArtists(): React.JSX.Element { data={recentArtistsInfiniteQuery.data.slice(0, horizontalItems)} renderItem={renderItem} /> -
+ ) : ( <> ) diff --git a/src/components/Home/helpers/recently-played.tsx b/src/components/Home/helpers/recently-played.tsx index 967ba9f6..ba321d49 100644 --- a/src/components/Home/helpers/recently-played.tsx +++ b/src/components/Home/helpers/recently-played.tsx @@ -11,8 +11,8 @@ import { useDisplayContext } from '../../../providers/Display/display-provider' import { useNavigation } from '@react-navigation/native' import HomeStackParamList from '../../../screens/Home/types' import { useRecentlyPlayedTracks } from '../../../api/queries/recents' -import Animated, { Easing, FadeIn, FadeOut, LinearTransition } from 'react-native-reanimated' import { BaseItemDto, BaseItemKind } from '@jellyfin/sdk/lib/generated-client' +import AnimatedRow from '../../Global/helpers/animated-row' export default function RecentlyPlayed(): React.JSX.Element { const navigation = useNavigation>() @@ -43,14 +43,7 @@ export default function RecentlyPlayed(): React.JSX.Element { } return tracksInfiniteQuery.data ? ( - + { @@ -87,7 +80,7 @@ export default function RecentlyPlayed(): React.JSX.Element { /> )} /> - + ) : ( <> ) diff --git a/src/components/Search/index.tsx b/src/components/Search/index.tsx index 2f7c3f21..371c8a5e 100644 --- a/src/components/Search/index.tsx +++ b/src/components/Search/index.tsx @@ -1,11 +1,8 @@ -import React, { useState } from 'react' +import React, { useEffect, useState } from 'react' import Input from '../Global/helpers/input' import { H5, Text } from '../Global/helpers/text' import ItemRow from '../Global/components/item-row' import { NativeStackNavigationProp } from '@react-navigation/native-stack' -import { QueryKeys } from '../../enums/query-keys' -import { fetchSearchResults } from '../../api/queries/search' -import { useQuery } from '@tanstack/react-query' import { getToken, H3, Spinner, YStack } from 'tamagui' import Suggestions from './suggestions' import { isEmpty } from 'lodash' @@ -13,7 +10,6 @@ import HorizontalCardList from '../Global/components/horizontal-list' import ItemCard from '../Global/components/item-card' import SearchParamList from '../../screens/Search/types' import { closeAllSwipeableRows } from '../Global/components/swipeable-row-registry' -import { getApi, getUser, useJellifyLibrary } from '../../stores' import { FlashList } from '@shopify/flash-list' import navigationRef from '../../../navigation' import { StackActions } from '@react-navigation/native' @@ -22,41 +18,35 @@ import Track from '../Global/components/Track' import { pickRandomItemFromArray } from '../../utils/parsing/random' import { SEARCH_PLACEHOLDERS } from '../../configs/placeholder.config' import { formatArtistName } from '../../utils/formatting/artist-names' +import useSearchResults from '../../api/queries/search' export default function Search({ navigation, }: { navigation: NativeStackNavigationProp }): React.JSX.Element { - const api = getApi() - const user = getUser() - const [library] = useJellifyLibrary() + /** + * Raw text input value from the user, updates immediately as they type + */ + const [inputValue, setInputValue] = useState(undefined) + /** + * Debounced search string that updates 500ms after the user stops typing, used to trigger the search query + * which is keyed off of this value for caching. + */ const [searchString, setSearchString] = useState(undefined) - const { - data: items, - refetch, - isFetching: fetchingResults, - } = useQuery({ - queryKey: [QueryKeys.Search, library?.musicLibraryId, searchString], - queryFn: () => fetchSearchResults(api, user, library?.musicLibraryId, searchString), - }) + useEffect(() => { + const timeout = setTimeout(() => { + setSearchString(inputValue || undefined) + }, 500) + return () => clearTimeout(timeout) + }, [inputValue]) - const search = () => { - let timeout: ReturnType - - return () => { - clearTimeout(timeout) - timeout = setTimeout(() => { - refetch() - }, 1000) - } - } + const { data: items, isFetching: fetchingResults } = useSearchResults(searchString) const handleSearchStringUpdate = (value: string | undefined) => { - setSearchString(value) - search() + setInputValue(value || undefined) } const handleScrollBeginDrag = () => { @@ -90,7 +80,7 @@ export default function Search({