Feature/addsearchpage (#85)

* Added a searchbar view with a debounce

* Added SearchBar with suggestions handled

* Front: Fix style of search suggestions

* added multiple types of suggestions and fixed image rendered

* Search bar revamped done

* removed unused files

Co-authored-by: Arthi-chaud <arthur.jamet@gmail.com>
This commit is contained in:
Clément Le Bihan
2022-10-25 18:36:44 +01:00
committed by GitHub
parent 30d3be72b2
commit 284da8b8c7
8 changed files with 262 additions and 12 deletions

3
.gitignore vendored
View File

@@ -9,4 +9,5 @@ prisma/migrations/*
output.xml
report.html
log.html
.expo
.expo
node_modules/

View File

@@ -2,6 +2,7 @@ import { createNativeStackNavigator } from '@react-navigation/native-stack';
import React from 'react';
import AuthenticationView from './views/AuthenticationView';
import HomeView from './views/HomeView';
import SearchView from './views/SearchView';
import SetttingsNavigator from './views/SettingsView';
import { NavigationContainer } from '@react-navigation/native';
import { useSelector } from './state/Store';
@@ -14,6 +15,7 @@ export const protectedRoutes = <>
<Stack.Screen name="Home" component={HomeView} options={{ title: translate('welcome') }} />
<Stack.Screen name="Settings" component={SetttingsNavigator} options={{ title: 'Settings' }} />
<Stack.Screen name="Song" component={SongLobbyView} options={{ title: translate('play') }} />
<Stack.Screen name="Search" component={SearchView} options={{ title: translate('search') }} />
</>;
export const publicRoutes = <React.Fragment>

View File

@@ -0,0 +1,150 @@
import {
Input,
Column,
Row,
Text,
Pressable,
HStack,
VStack,
Image,
Icon,
Square,
} from "native-base";
import React from "react";
import { Ionicons, FontAwesome } from "@expo/vector-icons";
export enum SuggestionType {
TEXT,
ILLUSTRATED,
}
export type SuggestionList = {
type: SuggestionType;
data: SuggestionProps | IllustratedSuggestionProps;
}[];
export interface SearchBarProps {
onTextChange: (text: string) => void;
onTextSubmit: (text: string) => void;
suggestions: SuggestionList;
}
export interface IllustratedSuggestionProps {
text: string;
subtext: string;
imageSrc: string;
onPress: () => void;
}
export interface SuggestionProps {
text: string;
onPress: () => void;
}
// debounce function
const debounce = (func: any, delay: number) => {
let inDebounce: any;
return function (this: any) {
const context = this;
const args = arguments;
clearTimeout(inDebounce);
inDebounce = setTimeout(() => func.apply(context, args), delay);
};
};
const IllustratedSuggestion = ({
text,
subtext,
imageSrc,
onPress,
}: IllustratedSuggestionProps) => {
return (
<Pressable
onPress={onPress}
margin={2}
padding={2}
bg={"white"}
_hover={{
bg: "primary.200",
}}
_pressed={{
bg: "primary.300",
}}
>
<HStack alignItems="center" space={4}>
<Square size={"sm"}>
<Image
source={{ uri: imageSrc }}
alt="Alternate Text"
size="xs"
rounded="lg"
/>
</Square>
<VStack alignItems="flex-start">
<Text fontSize="md">{text}</Text>
<Text fontSize="sm" color="gray.500">
{subtext}
</Text>
</VStack>
</HStack>
</Pressable>
);
};
const TextSuggestion = ({ text, onPress }: SuggestionProps) => {
return (
<Pressable
onPress={onPress}
margin={2}
padding={2}
bg={"white"}
_hover={{
bg: "primary.200",
}}
_pressed={{
bg: "primary.300",
}}
>
<Row alignItems="center" space={4}>
<Square size={"sm"}>
<Icon size={"md"} as={Ionicons} name="search" />
</Square>
<Text fontSize="md">{text}</Text>
</Row>
</Pressable>
);
};
// render the suggestions based on the type
const SuggestionRenderer = (suggestions: SuggestionList) => {
const suggestionRenderers = {
[SuggestionType.TEXT]: TextSuggestion,
[SuggestionType.ILLUSTRATED]: IllustratedSuggestion,
};
return suggestions.map((suggestion, index) => {
const SuggestionComponent = suggestionRenderers[suggestion.type];
return <SuggestionComponent {...suggestion.data} key={index} />;
});
};
const SearchBar = ({
onTextChange,
onTextSubmit,
suggestions,
}: SearchBarProps) => {
const debouncedOnTextChange = React.useRef(
debounce((t: string) => onTextChange(t), 70)
).current;
return (
<>
<Input
placeholder="Search"
type="text"
onChangeText={debouncedOnTextChange}
onSubmitEditing={(event) => onTextSubmit(event.nativeEvent.text)}
/>
<Column>{SuggestionRenderer(suggestions)}</Column>
</>
);
};
export default SearchBar;

View File

@@ -0,0 +1,41 @@
import React from "react";
import SearchBar from "../components/SearchBar";
import { SuggestionList, SuggestionType } from "../components/SearchBar";
interface SearchBarSuggestionsProps {
onTextSubmit: (text: string) => void;
suggestions: SuggestionList;
}
// do a function that takes in a string and returns a list of filtered suggestions
const filterSuggestions = (text: string, suggestions: SuggestionList) => {
return suggestions.filter((suggestion) => {
switch (suggestion.type) {
case SuggestionType.TEXT:
return suggestion.data.text.toLowerCase().includes(text.toLowerCase());
case SuggestionType.ILLUSTRATED:
return (
suggestion.data.text.toLowerCase().includes(text.toLowerCase()) ||
suggestion.data.subtext.toLowerCase().includes(text.toLowerCase())
);
}
});
};
const SearchBarSuggestions = ({
onTextSubmit,
suggestions,
}: SearchBarSuggestionsProps) => {
const [searchText, setSearchText] = React.useState("");
return (
<SearchBar
onTextChange={(t) => setSearchText(t)}
onTextSubmit={onTextSubmit}
suggestions={
searchText === "" ? [] : filterSuggestions(searchText, suggestions)
}
/>
);
};
export default SearchBarSuggestions;

View File

@@ -18,7 +18,7 @@ const SongCard = (props: SongCardProps) => {
shadow={3}
flexDirection='column'
alignContent='space-around'
bg={(isHovered || isFocused) ? 'coolGray.200' : undefined }
bg={(isHovered || isFocused) ? 'coolGray.200' : 'background.50' }
>
<Image
style={{ zIndex: 0, aspectRatio: 1, margin: 5, borderRadius: CardBorderRadius}}

View File

@@ -0,0 +1,66 @@
import React from "react";
import { useDispatch } from "../state/Store";
import { translate } from "../i18n/i18n";
import { Box, Button } from "native-base";
import { useNavigation } from "@react-navigation/native";
import SearchBarSuggestions from "../components/SearchBarSuggestions";
import {
SuggestionList,
SuggestionType,
IllustratedSuggestionProps,
} from "../components/SearchBar";
const onTextSubmit = (text: string) => {
console.log(text);
};
const SearchView = () => {
const navigation = useNavigation();
const IllustratedSuggestion: IllustratedSuggestionProps = {
text: "Love Story",
subtext: "Taylor Swift",
imageSrc:
"https://i.discogs.com/yHqu3pnLgJq-cVpYNVYu6mE-fbzIrmIRxc6vES5Oi48/rs:fit/g:sm/q:90/h:556/w:600/czM6Ly9kaXNjb2dz/LWRhdGFiYXNlLWlt/YWdlcy9SLTE2NjQ2/ODUwLTE2MDkwNDU5/NzQtNTkxOS5qcGVn.jpeg",
onPress: () => navigation.navigate("Song", { songId: 1 }),
};
// fill the suggestions with the data from the backend
const suggestions: SuggestionList = [
{
type: SuggestionType.ILLUSTRATED,
data: IllustratedSuggestion,
},
{
type: SuggestionType.ILLUSTRATED,
data: IllustratedSuggestion,
},
{
type: SuggestionType.ILLUSTRATED,
data: {
text: "Shed a Light",
subtext: "Robin Schulz & David Guetta",
imageSrc:
"https://imgs.search.brave.com/O9j2Z-oWiniq3lj7d-dAOgXLWCIqnHaFegmaSeIkWOY/rs:fit:560:320:1/g:ce/aHR0cHM6Ly91cGxv/YWQud2lraW1lZGlh/Lm9yZy93aWtpcGVk/aWEvZW4vdGh1bWIv/OC84ZS9TaGVkX2Ff/TGlnaHRfUm9iaW5f/U2NodWx6LmpwZy81/MTJweC1TaGVkX2Ff/TGlnaHRfUm9iaW5f/U2NodWx6LmpwZw",
onPress: () => navigation.navigate("Song", { songId: 1 }),
},
},
{
type: SuggestionType.TEXT,
data: {
text: "Lady Gaga",
onPress: () => navigation.navigate("Song", { songId: 1 }),
},
},
];
return (
<Box style={{ padding: 10 }}>
<SearchBarSuggestions
onTextSubmit={onTextSubmit}
suggestions={suggestions}
/>
</Box>
);
};
export default SearchView;

6
package-lock.json generated
View File

@@ -1,6 +0,0 @@
{
"name": "Chromacase",
"lockfileVersion": 2,
"requires": true,
"packages": {}
}

View File

@@ -1,4 +0,0 @@
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1