fix(searchview2): fix types and remove deprecated search components
This commit is contained in:
@@ -277,10 +277,10 @@ export default class API {
|
||||
};
|
||||
}
|
||||
|
||||
public static getAllSongs(falseQuery: string, include?: SongInclude[]): Query<Song[]> {
|
||||
public static getAllSongs(include?: SongInclude[]): Query<Song[]> {
|
||||
include ??= [];
|
||||
return {
|
||||
key: ['songs', falseQuery, include],
|
||||
key: ['songs', include],
|
||||
exec: () =>
|
||||
API.fetch(
|
||||
{
|
||||
|
||||
@@ -121,7 +121,7 @@ const Graph = ({ songId, since }: GraphProps) => {
|
||||
|
||||
const ScoreGraph = () => {
|
||||
const layout = useWindowDimensions();
|
||||
const songs = useQuery(API.getAllSongs);
|
||||
const songs = useQuery(API.getAllSongs());
|
||||
const rangeOptions = [
|
||||
{ label: '3 derniers jours', value: '3days' },
|
||||
{ label: 'Dernière semaine', value: 'week' },
|
||||
|
||||
@@ -1,119 +0,0 @@
|
||||
import { Icon, Input, Button, Flex } from 'native-base';
|
||||
import React from 'react';
|
||||
import { MaterialIcons } from '@expo/vector-icons';
|
||||
import { translate } from '../i18n/i18n';
|
||||
import { SearchContext } from '../views/SearchView';
|
||||
import { debounce } from 'lodash';
|
||||
|
||||
export type Filter = 'artist' | 'song' | 'genre' | 'all' | 'favorites';
|
||||
|
||||
type FilterButton = {
|
||||
name: string;
|
||||
callback: () => void;
|
||||
id: Filter;
|
||||
};
|
||||
|
||||
const SearchBar = () => {
|
||||
const { filter, updateFilter } = React.useContext(SearchContext);
|
||||
const { stringQuery, updateStringQuery } = React.useContext(SearchContext);
|
||||
const [barText, updateBarText] = React.useState(stringQuery);
|
||||
|
||||
const debouncedUpdateStringQuery = debounce(updateStringQuery, 500);
|
||||
|
||||
// there's a bug due to recursive feedback that erase the text as soon as you type this is a temporary "fix"
|
||||
// will probably be fixed by removing the React.useContext
|
||||
// React.useEffect(() => {
|
||||
// updateBarText(stringQuery);
|
||||
// }, [stringQuery]);
|
||||
|
||||
const handleClearQuery = () => {
|
||||
updateStringQuery('');
|
||||
updateBarText('');
|
||||
};
|
||||
|
||||
const handleChangeText = (text: string) => {
|
||||
debouncedUpdateStringQuery(text);
|
||||
updateBarText(text);
|
||||
};
|
||||
|
||||
const filters: FilterButton[] = [
|
||||
{
|
||||
name: translate('allFilter'),
|
||||
callback: () => updateFilter('all'),
|
||||
id: 'all',
|
||||
},
|
||||
{
|
||||
name: translate('favoriteFilter'),
|
||||
callback: () => updateFilter('favorites'),
|
||||
id: 'favorites',
|
||||
},
|
||||
{
|
||||
name: translate('artistFilter'),
|
||||
callback: () => updateFilter('artist'),
|
||||
id: 'artist',
|
||||
},
|
||||
{
|
||||
name: translate('songsFilter'),
|
||||
callback: () => updateFilter('song'),
|
||||
id: 'song',
|
||||
},
|
||||
{
|
||||
name: translate('genreFilter'),
|
||||
callback: () => updateFilter('genre'),
|
||||
id: 'genre',
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<Flex m={3} flexDirection={['column', 'row']}>
|
||||
<Input
|
||||
onChangeText={(text) => handleChangeText(text)}
|
||||
variant={'rounded'}
|
||||
value={barText}
|
||||
rounded={'full'}
|
||||
placeholder={translate('search')}
|
||||
width={['100%', '50%']} //responsive array syntax with native-base
|
||||
py={2}
|
||||
px={2}
|
||||
fontSize={'12'}
|
||||
InputLeftElement={
|
||||
<Icon
|
||||
m={[1, 2]}
|
||||
ml={[2, 3]}
|
||||
size={['4', '6']}
|
||||
color="gray.400"
|
||||
as={<MaterialIcons name="search" />}
|
||||
/>
|
||||
}
|
||||
InputRightElement={
|
||||
<Icon
|
||||
m={[1, 2]}
|
||||
mr={[2, 3]}
|
||||
size={['4', '6']}
|
||||
color="gray.400"
|
||||
onPress={handleClearQuery}
|
||||
as={<MaterialIcons name="close" />}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
|
||||
<Flex flexDirection={'row'}>
|
||||
{filters.map((btn) => (
|
||||
<Button
|
||||
key={btn.name}
|
||||
rounded={'full'}
|
||||
onPress={btn.callback}
|
||||
mx={[2, 5]}
|
||||
my={[1, 0]}
|
||||
minW={[30, 20]}
|
||||
variant={filter === btn.id ? 'solid' : 'outline'}
|
||||
>
|
||||
{btn.name}
|
||||
</Button>
|
||||
))}
|
||||
</Flex>
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
|
||||
export default SearchBar;
|
||||
@@ -1,277 +0,0 @@
|
||||
import React from 'react';
|
||||
import {
|
||||
VStack,
|
||||
Heading,
|
||||
Box,
|
||||
Card,
|
||||
Flex,
|
||||
useBreakpointValue,
|
||||
Column,
|
||||
ScrollView,
|
||||
} from 'native-base';
|
||||
import { SafeAreaView } from 'react-native';
|
||||
import { SearchContext } from '../views/SearchView';
|
||||
import { useQuery } from '../Queries';
|
||||
import { Translate, translate } from '../i18n/i18n';
|
||||
import API from '../API';
|
||||
import LoadingComponent, { LoadingView } from './Loading';
|
||||
import ArtistCard from './ArtistCard';
|
||||
import GenreCard from './GenreCard';
|
||||
import SongCard from './SongCard';
|
||||
import CardGridCustom from './CardGridCustom';
|
||||
import SearchHistoryCard from './HistoryCard';
|
||||
import Song from '../models/Song';
|
||||
import { useNavigation } from '../Navigation';
|
||||
import SongRow from '../components/SongRow';
|
||||
import FavSongRow from './FavSongRow';
|
||||
import { useLikeSongMutation } from '../utils/likeSongMutation';
|
||||
|
||||
const swaToSongCardProps = (song: Song) => ({
|
||||
songId: song.id,
|
||||
name: song.name,
|
||||
artistName: song.artist!.name,
|
||||
cover: song.cover,
|
||||
});
|
||||
|
||||
const HomeSearchComponent = () => {
|
||||
const { updateStringQuery } = React.useContext(SearchContext);
|
||||
const { isLoading: isLoadingHistory, data: historyData = [] } = useQuery(
|
||||
API.getSearchHistory(0, 12),
|
||||
{ enabled: true }
|
||||
);
|
||||
const songSuggestions = useQuery(API.getSongSuggestions(['artist']));
|
||||
|
||||
return (
|
||||
<VStack mt="5" style={{ overflow: 'hidden' }}>
|
||||
<Card shadow={3} mb={5}>
|
||||
<Heading margin={5}>{translate('lastSearched')}</Heading>
|
||||
{isLoadingHistory ? (
|
||||
<LoadingComponent />
|
||||
) : (
|
||||
<CardGridCustom
|
||||
content={historyData.map((h) => {
|
||||
return {
|
||||
...h,
|
||||
timestamp: h.timestamp.toLocaleString(),
|
||||
onPress: () => {
|
||||
updateStringQuery(h.query);
|
||||
},
|
||||
};
|
||||
})}
|
||||
cardComponent={SearchHistoryCard}
|
||||
/>
|
||||
)}
|
||||
</Card>
|
||||
<Card shadow={3} mt={5} mb={5}>
|
||||
<Heading margin={5}>{translate('songsToGetBetter')}</Heading>
|
||||
{!songSuggestions.data ? (
|
||||
<LoadingComponent />
|
||||
) : (
|
||||
<CardGridCustom
|
||||
content={songSuggestions.data.map(swaToSongCardProps)}
|
||||
cardComponent={SongCard}
|
||||
/>
|
||||
)}
|
||||
</Card>
|
||||
</VStack>
|
||||
);
|
||||
};
|
||||
|
||||
type SongsSearchComponentProps = {
|
||||
maxRows?: number;
|
||||
};
|
||||
|
||||
const SongsSearchComponent = (props: SongsSearchComponentProps) => {
|
||||
const navigation = useNavigation();
|
||||
const { songData } = React.useContext(SearchContext);
|
||||
const favoritesQuery = useQuery(API.getLikedSongs(['artist']));
|
||||
const { mutate } = useLikeSongMutation();
|
||||
|
||||
return (
|
||||
<ScrollView>
|
||||
<Translate translationKey="songsFilter" fontSize="xl" fontWeight="bold" mt={4} />
|
||||
<Box>
|
||||
{songData?.length ? (
|
||||
songData.slice(0, props.maxRows).map((comp, index) => (
|
||||
<SongRow
|
||||
key={index}
|
||||
song={comp}
|
||||
isLiked={
|
||||
!favoritesQuery.data?.find((query) => query?.songId == comp.id)
|
||||
}
|
||||
handleLike={async (state: boolean, songId: number) =>
|
||||
mutate({ songId: songId, like: state })
|
||||
}
|
||||
onPress={() => {
|
||||
API.createSearchHistoryEntry(comp.name, 'song');
|
||||
navigation.navigate('Play', { songId: comp.id });
|
||||
}}
|
||||
/>
|
||||
))
|
||||
) : (
|
||||
<Translate translationKey="errNoResults" />
|
||||
)}
|
||||
</Box>
|
||||
</ScrollView>
|
||||
);
|
||||
};
|
||||
|
||||
type ItemSearchComponentProps = {
|
||||
maxItems?: number;
|
||||
};
|
||||
|
||||
const ArtistSearchComponent = (props: ItemSearchComponentProps) => {
|
||||
const { artistData } = React.useContext(SearchContext);
|
||||
const navigation = useNavigation();
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<Translate translationKey="artistFilter" fontSize="xl" fontWeight="bold" mt={4} />
|
||||
{artistData?.length ? (
|
||||
<CardGridCustom
|
||||
content={artistData
|
||||
.slice(0, props.maxItems ?? artistData.length)
|
||||
.map((artistData) => ({
|
||||
image: API.getArtistIllustration(artistData.id),
|
||||
name: artistData.name,
|
||||
id: artistData.id,
|
||||
onPress: () => {
|
||||
API.createSearchHistoryEntry(artistData.name, 'artist');
|
||||
navigation.navigate('Artist', { artistId: artistData.id });
|
||||
},
|
||||
}))}
|
||||
cardComponent={ArtistCard}
|
||||
/>
|
||||
) : (
|
||||
<Translate translationKey="errNoResults" />
|
||||
)}
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
const GenreSearchComponent = (props: ItemSearchComponentProps) => {
|
||||
const { genreData } = React.useContext(SearchContext);
|
||||
const navigation = useNavigation();
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<Translate translationKey="genreFilter" fontSize="xl" fontWeight="bold" mt={4} />
|
||||
{genreData?.length ? (
|
||||
<CardGridCustom
|
||||
content={genreData.slice(0, props.maxItems ?? genreData.length).map((g) => ({
|
||||
image: API.getGenreIllustration(g.id),
|
||||
name: g.name,
|
||||
id: g.id,
|
||||
onPress: () => {
|
||||
API.createSearchHistoryEntry(g.name, 'genre');
|
||||
navigation.navigate('Genre', { genreId: g.id });
|
||||
},
|
||||
}))}
|
||||
cardComponent={GenreCard}
|
||||
/>
|
||||
) : (
|
||||
<Translate translationKey="errNoResults" />
|
||||
)}
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
const FavoritesComponent = () => {
|
||||
const navigation = useNavigation();
|
||||
const favoritesQuery = useQuery(API.getLikedSongs());
|
||||
|
||||
if (favoritesQuery.isError) {
|
||||
navigation.navigate('Error');
|
||||
return <></>;
|
||||
}
|
||||
if (!favoritesQuery.data) {
|
||||
return <LoadingView />;
|
||||
}
|
||||
|
||||
return (
|
||||
<ScrollView>
|
||||
<Translate translationKey="songsFilter" fontSize="xl" fontWeight="bold" mt={4} />
|
||||
<Box>
|
||||
{favoritesQuery.data?.map((songData) => (
|
||||
<FavSongRow
|
||||
key={songData.id}
|
||||
song={songData.song}
|
||||
addedDate={songData.addedDate}
|
||||
onPress={() => {
|
||||
API.createSearchHistoryEntry(songData.song.name, 'song'); //todo
|
||||
navigation.navigate('Play', { songId: songData.song!.id });
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
</Box>
|
||||
</ScrollView>
|
||||
);
|
||||
};
|
||||
|
||||
const AllComponent = () => {
|
||||
const screenSize = useBreakpointValue({ base: 'small', md: 'big' });
|
||||
const isMobileView = screenSize == 'small';
|
||||
|
||||
return (
|
||||
<SafeAreaView>
|
||||
<Flex
|
||||
flexWrap="wrap"
|
||||
direction={isMobileView ? 'column' : 'row'}
|
||||
justifyContent={['flex-start']}
|
||||
mt={4}
|
||||
>
|
||||
<Column w={isMobileView ? '100%' : '50%'}>
|
||||
<Box minH={isMobileView ? 100 : 200}>
|
||||
<ArtistSearchComponent maxItems={6} />
|
||||
</Box>
|
||||
<Box minH={isMobileView ? 100 : 200}>
|
||||
<GenreSearchComponent maxItems={6} />
|
||||
</Box>
|
||||
</Column>
|
||||
<Box w={isMobileView ? '100%' : '50%'}>
|
||||
<SongsSearchComponent maxRows={9} />
|
||||
</Box>
|
||||
</Flex>
|
||||
</SafeAreaView>
|
||||
);
|
||||
};
|
||||
|
||||
const FilterSwitch = () => {
|
||||
const { filter } = React.useContext(SearchContext);
|
||||
const [currentFilter, setCurrentFilter] = React.useState(filter);
|
||||
|
||||
React.useEffect(() => {
|
||||
setCurrentFilter(filter);
|
||||
}, [filter]);
|
||||
|
||||
switch (currentFilter) {
|
||||
case 'all':
|
||||
return <AllComponent />;
|
||||
case 'song':
|
||||
return <SongsSearchComponent />;
|
||||
case 'artist':
|
||||
return <ArtistSearchComponent />;
|
||||
case 'genre':
|
||||
return <GenreSearchComponent />;
|
||||
case 'favorites':
|
||||
return <FavoritesComponent />;
|
||||
default:
|
||||
return (
|
||||
<Translate translationKey="unknownError" format={(e) => `${e}: ${currentFilter}`} />
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export const SearchResultComponent = () => {
|
||||
const { stringQuery } = React.useContext(SearchContext);
|
||||
const { filter } = React.useContext(SearchContext);
|
||||
const shouldOutput = !!stringQuery.trim() || filter == 'favorites';
|
||||
|
||||
return shouldOutput ? (
|
||||
<Box p={5}>
|
||||
<FilterSwitch />
|
||||
</Box>
|
||||
) : (
|
||||
<HomeSearchComponent />
|
||||
);
|
||||
};
|
||||
@@ -1,114 +0,0 @@
|
||||
import React, { useState } from 'react';
|
||||
import SearchBar from '../components/SearchBar';
|
||||
import Artist from '../models/Artist';
|
||||
import Song from '../models/Song';
|
||||
import Genre from '../models/Genre';
|
||||
import API from '../API';
|
||||
import { useQuery } from '../Queries';
|
||||
import { SearchResultComponent } from '../components/SearchResult';
|
||||
import { SafeAreaView } from 'react-native';
|
||||
import { Filter } from '../components/SearchBar';
|
||||
import { ScrollView } from 'native-base';
|
||||
import { RouteProps } from '../Navigation';
|
||||
import LikedSong from '../models/LikedSong';
|
||||
import ScaffoldCC from '../components/UI/ScaffoldCC';
|
||||
|
||||
interface SearchContextType {
|
||||
filter: 'artist' | 'song' | 'genre' | 'all' | 'favorites';
|
||||
updateFilter: (newData: 'artist' | 'song' | 'genre' | 'all' | 'favorites') => void;
|
||||
stringQuery: string;
|
||||
updateStringQuery: (newData: string) => void;
|
||||
songData: Song[];
|
||||
artistData: Artist[];
|
||||
genreData: Genre[];
|
||||
favoriteData: LikedSong[];
|
||||
isLoadingSong: boolean;
|
||||
isLoadingArtist: boolean;
|
||||
isLoadingGenre: boolean;
|
||||
isLoadingFavorite: boolean;
|
||||
}
|
||||
|
||||
export const SearchContext = React.createContext<SearchContextType>({
|
||||
filter: 'all',
|
||||
updateFilter: () => {},
|
||||
stringQuery: '',
|
||||
updateStringQuery: () => {},
|
||||
songData: [],
|
||||
artistData: [],
|
||||
genreData: [],
|
||||
favoriteData: [],
|
||||
isLoadingSong: false,
|
||||
isLoadingArtist: false,
|
||||
isLoadingGenre: false,
|
||||
isLoadingFavorite: false,
|
||||
});
|
||||
|
||||
type SearchViewProps = {
|
||||
query?: string;
|
||||
};
|
||||
|
||||
const SearchView = (props: RouteProps<SearchViewProps>) => {
|
||||
const [filter, setFilter] = useState<Filter>('all');
|
||||
const [stringQuery, setStringQuery] = useState<string>(props?.query ?? '');
|
||||
|
||||
//flemme de corriger de toute facon c'est déprecié et bientot remplacé
|
||||
const { isLoading: isLoadingSong, data: songData = [] } = useQuery(
|
||||
API.searchSongs({ artist: undefined, genre: undefined, query: 'zeruigze' }),
|
||||
{ enabled: !!stringQuery }
|
||||
);
|
||||
|
||||
const { isLoading: isLoadingArtist, data: artistData = [] } = useQuery(
|
||||
API.searchArtists(stringQuery),
|
||||
{ enabled: !!stringQuery }
|
||||
);
|
||||
|
||||
const { isLoading: isLoadingGenre, data: genreData = [] } = useQuery(
|
||||
API.searchGenres(stringQuery),
|
||||
{ enabled: !!stringQuery }
|
||||
);
|
||||
|
||||
const { isLoading: isLoadingFavorite, data: favoriteData = [] } = useQuery(
|
||||
API.getLikedSongs(),
|
||||
{ enabled: true }
|
||||
);
|
||||
|
||||
const updateFilter = (newData: Filter) => {
|
||||
// called when the filter is changed
|
||||
setFilter(newData);
|
||||
};
|
||||
|
||||
const updateStringQuery = (newData: string) => {
|
||||
// called when the stringQuery is updated
|
||||
setStringQuery(newData);
|
||||
};
|
||||
|
||||
return (
|
||||
<ScaffoldCC routeName={props.route.name}>
|
||||
<ScrollView>
|
||||
<SafeAreaView>
|
||||
<SearchContext.Provider
|
||||
value={{
|
||||
filter,
|
||||
stringQuery,
|
||||
songData,
|
||||
artistData,
|
||||
genreData,
|
||||
favoriteData,
|
||||
isLoadingSong,
|
||||
isLoadingArtist,
|
||||
isLoadingGenre,
|
||||
isLoadingFavorite,
|
||||
updateFilter,
|
||||
updateStringQuery,
|
||||
}}
|
||||
>
|
||||
<SearchBar />
|
||||
<SearchResultComponent />
|
||||
</SearchContext.Provider>
|
||||
</SafeAreaView>
|
||||
</ScrollView>
|
||||
</ScaffoldCC>
|
||||
);
|
||||
};
|
||||
|
||||
export default SearchView;
|
||||
Reference in New Issue
Block a user