Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Mobile search results #276

Merged
merged 9 commits into from
May 16, 2024
9 changes: 8 additions & 1 deletion mobile/NBAForum/commonNavigator.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React from "react";
import { createNativeStackNavigator } from '@react-navigation/native-stack';
import { Team, Player, Search } from "./pages";
import { Team, Player, Search, SearchResults } from "./pages";


const Stack = createNativeStackNavigator();
Expand All @@ -16,6 +16,13 @@ function CommonNavigator() {
headerShown: false
}}
/>
<Stack.Screen
name="SearchResults"
component={SearchResults}
options={{
headerShown: false
}}
/>
<Stack.Screen
name="Team"
component={Team}
Expand Down
13 changes: 10 additions & 3 deletions mobile/NBAForum/pages/Post.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import React, { useContext, useState } from 'react';
import { Context } from "../globalContext/globalContext.js"
import { View, Text, Image, TouchableOpacity, StyleSheet, TextInput } from 'react-native';
import Icon from 'react-native-vector-icons/FontAwesome';
import RenderHTML from 'react-native-render-html';
import moment from 'moment';
import axios from 'axios';

Expand Down Expand Up @@ -98,14 +99,15 @@ const Post = ({ post }) => {
</View>
<Text>{moment(post.created_at).fromNow()}</Text>
</View>

<Text style={styles.postText}>{post.post}</Text>
<View style={styles.separator} />
<RenderHTML contentWidth={300} source={{ html: post.post }} />
{post.image && (
<Image
source={{ uri: baseURL + post.image }}
style={styles.postImage}
/>
)}
<View style={styles.separator} />
<View style={styles.actionsContainer}>
<View style={styles.likeContainer}>
<TouchableOpacity onPress={handleLike} style={styles.actionButton}>
Expand Down Expand Up @@ -186,7 +188,7 @@ const styles = StyleSheet.create({
userInfoContainer: {
flexDirection: 'row',
alignItems: 'center',
marginBottom: 5,
marginBottom: 1,
justifyContent: 'space-between',
},
profileImage: {
Expand Down Expand Up @@ -238,6 +240,11 @@ const styles = StyleSheet.create({
likeCount: {
marginLeft: 5,
},
separator: {
height: 1,
backgroundColor: '#BCBCBC',
marginVertical: 10,
},
});

export default Post;
39 changes: 19 additions & 20 deletions mobile/NBAForum/pages/Search.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,35 +2,30 @@ import React, { useState, useContext } from 'react';
import { View, Text, StyleSheet, ActivityIndicator } from 'react-native';
import { Searchbar } from 'react-native-paper';
import axios from 'axios';
import { Context } from "../globalContext/globalContext.js"; // Ensure the correct path
import { Context } from "../globalContext/globalContext.js";
import { useNavigation } from '@react-navigation/native';

const Search = ({ navigation }) => {
const Search = () => {
const [searchQuery, setSearchQuery] = useState('');
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState('');

const { baseURL } = useContext(Context);
const navigation = useNavigation();

const handleSearch = async (query) => {
if (!query.trim()) {
const handleSearch = async () => {
if (!searchQuery.trim()) {
setError("Please enter a valid search query.");
return;
}
setIsLoading(true);
setError('');
try {
const encodedQuery = encodeURIComponent(query);
const encodedQuery = encodeURIComponent(searchQuery);
const response = await axios.get(`${baseURL}/search/?query=${encodedQuery}`);
const data = response.data;
setIsLoading(false);

if (data.player) {

// If player data is not null, navigate to the PlayerDetails screen
navigation.navigate('Player', { id: data.player.id });
} else if (data.team) {
// If team data is not null, navigate to the TeamDetails screen
navigation.navigate('Team', { id: data.team.id });
if (response.data) {
console.log(response.data)
navigation.navigate('SearchResults', { query: searchQuery, data: response.data });
} else {
setError("No data found for the query.");
}
Expand All @@ -47,24 +42,28 @@ const Search = ({ navigation }) => {
placeholder="Search for a NBA team/player!"
value={searchQuery}
onChangeText={setSearchQuery}
onSubmitEditing={() => handleSearch(searchQuery)}
onSubmitEditing={handleSearch}
/>
{isLoading ? (
<ActivityIndicator size="large" color="#FFFF" />
<ActivityIndicator size="large" color="#0000ff" />
) : error ? (
<Text>{error}</Text>
<Text style={styles.error}>{error}</Text>
) : null}
</View>

);
};

const styles = StyleSheet.create({
container: {
padding: 25,
padding: 20,
flex: 1,
backgroundColor: '#55A1E6',
},
error: {
color: 'red',
marginTop: 10,
fontWeight: 'bold'
}
});

export default Search;
192 changes: 192 additions & 0 deletions mobile/NBAForum/pages/SearchResults.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
import React, { useContext, useState, useEffect } from 'react';
import { View, Text, Image, StyleSheet, TouchableOpacity, ScrollView, ActivityIndicator, FlatList } from 'react-native';
import axios from 'axios';
import Post from './Post.js';
import { Context } from '../globalContext/globalContext';
import { useNavigation, useRoute } from '@react-navigation/native';

const SearchResults = () => {
const navigation = useNavigation();
const route = useRoute();
const { query } = route.params;
const { baseURL } = useContext(Context);
const [data, setData] = useState({ team: null, player: null, posts: [] });
const [isLoading, setIsLoading] = useState(true);
const [error, setError] = useState('');
const [postDetails, setPostDetails] = useState([]);

useEffect(() => {
fetchData();
}, [query]);

const fetchData = async () => {
setIsLoading(true);
setError('');
try {
const response = await axios.get(`${baseURL}/search/?query=${encodeURIComponent(query)}`);
if (response.data) {
setData(response.data);
if (response.data.team) {
fetchTeamDetails(response.data.team.id);
}
if (response.data.player) {
fetchPlayerDetails(response.data.player.id);
}
if (response.data.posts) {
fetchPostDetails(response.data.posts);
}
} else {
setError('No results found');
}
} catch (error) {
setError('Failed to fetch data');
console.error('Error fetching data:', error);
} finally {
setIsLoading(false);
}
};

const fetchTeamDetails = async (teamId) => {
try {
const teamResponse = await axios.get(`${baseURL}/team/?id=${teamId}`);
setData(prevState => ({
...prevState,
team: { ...prevState.team, ...teamResponse.data }
}));
} catch (error) {
console.error('Error fetching team details:', error);
}
};

const fetchPlayerDetails = async (playerId) => {
try {
const playerResponse = await axios.get(`${baseURL}/player/?id=${playerId}`);
setData(prevState => ({
...prevState,
player: { ...prevState.player, ...playerResponse.data }
}));
} catch (error) {
console.error('Error fetching player details:', error);
}
};

const fetchPostDetails = async (postIds) => {
try {
const postRequests = postIds.map(postId => axios.get(`${baseURL}/post_detail/${postId.id}/`));
const postResponses = await Promise.all(postRequests);
const fetchedPosts = postResponses.map(response => response.data);
setPostDetails(fetchedPosts);

} catch (error) {
console.error('Error fetching post details:', error);
}
};

if (isLoading) {
return <ActivityIndicator size="large" color="#0000ff" />;
}

if (error) {
return <View style={styles.center}><Text>{error}</Text></View>;
}

return (
<View style={styles.container}>
<Text style={styles.header}>Search Results for "{query}":</Text>
{data.team && (
<View>
<Text style={styles.subheader}>Teams:</Text>
<TouchableOpacity style={styles.item} onPress={() => navigation.navigate('Team', { id: data.team.id })}>
<View style={styles.row}>
<Image source={{ uri: data.team.image }} style={styles.image} />
<Text style={styles.text}>{data.team.name}</Text>
</View>
</TouchableOpacity>
</View>
)}
{data.player && (
<View>
<Text style={styles.subheader}>Players:</Text>
<TouchableOpacity style={styles.item} onPress={() => navigation.navigate('Player', { id: data.player.id })}>
<View style={styles.row}>
<Image source={{ uri: data.player.image }} style={styles.image} />
<View>
<Text style={styles.text}>{data.player.name}</Text>
<Text> {data.player.height ? data.player.height.substr(1,4) + "cm" :""} </Text>
</View>
</View>
</TouchableOpacity>
</View>
)}
{data.posts && data.posts.length > 0 && (
<View style={{ flex: 1 }}>
<Text style={styles.subheader}>Posts:</Text>
{
<FlatList
data={postDetails}
renderItem={({ item }) => (
<Post
post={item}
/>
)}
keyExtractor={(item) => item.post_id.toString()}
/>
}
</View>
)}
</View>
);
};

const styles = StyleSheet.create({
container: {
flex: 1,
position: 'relative',
backgroundColor: '#55A1E6',
padding: 5,
},
header: {
fontSize: 22,
fontWeight: 'bold',
marginBottom: 20
},
subheader: {
fontSize: 18,
fontWeight: 'bold',
marginBottom: 10
},
item: {
marginBottom: 10
},
row: {
flexDirection: 'row',
alignItems: 'center',
backgroundColor: '#ffffff',
padding: 10,
margin: 10,
borderRadius: 8,
borderWidth: 1,
borderColor: '#BCBCBC',
},
image: {
width: 70,
height: 70,
marginRight: 10,
resizeMode: 'contain'
},
text: {
fontSize: 20,
marginBottom: 5
},
postText: {
marginTop: 5,
fontSize: 16
},
center: {
flex: 1,
justifyContent: 'center',
alignItems: 'center'
}
});

export default SearchResults;
2 changes: 2 additions & 0 deletions mobile/NBAForum/pages/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import HomePage from "./HomePage";
import Player from "./Player";
import Feed from "./Feed";
import Search from "./Search";
import SearchResults from "./SearchResults";
import Post from "./Post";

export{
Expand All @@ -16,4 +17,5 @@ export{
Feed,
Search,
Post,
SearchResults,
}