From e7a2c02d241b7eaa4962ab6e640eb22aea4cf950 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Muhammed=20Erkam=20G=C3=B6kcep=C4=B1nar?= Date: Mon, 16 Dec 2024 17:56:02 +0300 Subject: [PATCH] feat: add like/unlike functionality on post and community pages with appropriate visual components --- mobile/src/pages/Community.js | 57 +++++++- mobile/src/pages/Post.js | 257 ++++++++++++++++++++++++---------- 2 files changed, 235 insertions(+), 79 deletions(-) diff --git a/mobile/src/pages/Community.js b/mobile/src/pages/Community.js index e5a5e11a..3ed43c12 100644 --- a/mobile/src/pages/Community.js +++ b/mobile/src/pages/Community.js @@ -97,6 +97,36 @@ const Community = ({navigation}) => { } }; + const postLike = async (postId) => { + const likeURL = `${baseURL}/like`; + try { + const response = await fetch(likeURL, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${accessToken}`, + }, + body: JSON.stringify({ post_id: postId }), + }); + if (response.ok) { + fetchPosts(); + Alert.alert('Liked successfully!'); + } else { + console.error(`Failed to like the post: ${response.status}`); + } + } catch (error) { + console.error('Error liking the post:', error); + } + }; + + const handleLike = (postId) => { + if (!accessToken) { + navigation.navigate('Login&Register'); + Alert.alert('Please login to like the post'); + return; + } + postLike(postId); + }; useFocusEffect( React.useCallback(() => { @@ -172,8 +202,8 @@ const Community = ({navigation}) => { - - 👍 {post.liked_by.length} + handleLike(post.id)}> + 👍 {post.liked_by.length} { /> - Submit + Submit setShowCommentInput(false)} > - Cancel + Cancel @@ -320,7 +350,12 @@ const styles = StyleSheet.create({ alignItems: 'center', }, actionButton: { - backgroundColor: '#f0f0f0', + backgroundColor: '#e0f7fa', + padding: 10, + borderRadius: 5, + }, + likedButton: { + backgroundColor: '#007BFF', padding: 10, borderRadius: 5, }, @@ -373,6 +408,18 @@ const styles = StyleSheet.create({ marginLeft: 5, }, buttonText: { + color: '#007BFF', + fontSize: 16, + fontWeight: 'bold', + textAlign: 'center', + }, + likedButtonText: { + color: '#ffffff', + fontSize: 16, + fontWeight: 'bold', + textAlign: 'center', + }, + commentButtonText: { color: '#ffffff', fontSize: 16, fontWeight: 'bold', diff --git a/mobile/src/pages/Post.js b/mobile/src/pages/Post.js index 52993e00..a3197cac 100644 --- a/mobile/src/pages/Post.js +++ b/mobile/src/pages/Post.js @@ -17,30 +17,57 @@ import { useAuth } from './context/AuthContext'; const Post = ({ navigation, route }) => { const { postId, author, userMap, post } = route.params; + console.log("post", post); const { baseURL } = config; - const { accessToken } = useAuth(); - + const { userId, accessToken } = useAuth(); + const [defPost, setDefPost] = useState(post); const [likes, setLikes] = useState(0); const [tooltip, setTooltip] = useState(null); const [showCommentInput, setShowCommentInput] = useState(false); const [commentText, setCommentText] = useState(''); const [comments, setComments] = useState([]); - const [stockData, setStockData] = useState([]); // Stores Close prices - const [stockDates, setStockDates] = useState([]); // Stores formatted dates + const [stockData, setStockData] = useState([]); + const [stockDates, setStockDates] = useState([]); const [loading, setLoading] = useState(true); - const [interval, setInterval] = useState('1d'); // Default interval - const [timeRange, setTimeRange] = useState('1mo'); // Default time range + const [interval, setInterval] = useState('1d'); + const [timeRange, setTimeRange] = useState('1mo'); const [hasStockData, setHasStockData] = useState(false); useEffect(() => { - if(post.stocks.length != 0){ + if(defPost.stocks.length != 0){ setHasStockData(true); fetchStockData(); } fetchComments(); }, [postId, timeRange]); + useEffect(() => { + fetchPost(); + }, [likes]); + + const fetchPost = async () => { + const postURL = `${baseURL}/posts/${postId}/`; + try { + const response = await fetch(postURL, { + method: 'GET', + headers: { + 'Content-Type': 'application/json', + }, + }); + if (response.ok) { + console.log("response", response); + const postData = await response.json(); + setDefPost(postData); + + } else { + console.error(`Failed to fetch comments: ${response}`); + } + } catch (error) { + console.error('Error fetching post:', error); + + } + }; const fetchComments = async () => { const postURL = `${baseURL}/comments/post-comments/${postId}/`; @@ -92,24 +119,24 @@ const Post = ({ navigation, route }) => { const fetchStockData = async () => { setLoading(true); // Show loader try { - const response = await fetch(`${baseURL}/stocks/${post.stocks[0]}/get_historical_data/`, { + const response = await fetch(`${baseURL}/stocks/${defPost.stocks[0]}/get_historical_data/`, { method: 'POST', headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${accessToken}`, }, body: JSON.stringify({ - period: timeRange, // Use selected period - interval: interval, // Use selected interval + period: timeRange, + interval: interval, }), }); const data = await response.json(); if (data.Close && data.Date) { - setStockData(data.Close); // Set Close prices for Y-axis + setStockData(data.Close); setStockDates( data.Date.map((date) => new Date(date).toLocaleDateString('en-US', { month: 'short', day: 'numeric' }) - ) // Format dates for X-axis + ) ); } else { console.error('Unexpected response structure:', data); @@ -121,6 +148,29 @@ const Post = ({ navigation, route }) => { } }; + const postLike = async () => { + const likeURL = `${baseURL}/like`; + try { + const response = await fetch(likeURL, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${accessToken}`, + }, + body: JSON.stringify({ post_id: postId }), + }); + if (response.ok) { + setLikes((prevLikes) => prevLikes + 1); + //Alert.alert('Liked successfully!'); + } else { + console.error(`Failed to like the post: ${response.status}`); + } + } catch (error) { + console.error('Error liking the post:', error); + } + }; + + const renderUsername = (comment) => { if(userMap[comment.user_id]){ return userMap[comment.user_id].username; @@ -131,11 +181,16 @@ const Post = ({ navigation, route }) => { const handleLike = () => { - setLikes((prevLikes) => prevLikes + 1); + if (!accessToken) { + navigation.navigate('Login&Register'); + Alert.alert('Please login to like the post'); + return; + } + postLike(); }; const handleAddCommentButton = () => { - if(accessToken == null){ + if(!accessToken){ navigation.navigate('Login&Register'); Alert.alert('Please login to add a comment'); return; @@ -165,23 +220,25 @@ const Post = ({ navigation, route }) => { const timeRanges = ['1mo', '1y', '5y', '1wk']; - if (!post) { + if (!defPost) { return Loading...; } return ( + console.log("defPost", defPost), + console.log("userid", userId), //console.log("stockData", stockData), //console.log("stockDates", stockDates), - {post.title} + {defPost.title} Author: {author ? author : 'Unknown'} - {new Date(post.created_at).toLocaleDateString()} at {new Date(post.created_at).toLocaleTimeString()} + {new Date(defPost.created_at).toLocaleDateString()} at {new Date(defPost.created_at).toLocaleTimeString()} - {post.tags.length > 0 ? ( - post.tags.map((tag) => ( + {defPost.tags.length > 0 ? ( + defPost.tags.map((tag) => ( {tag.name} @@ -190,7 +247,7 @@ const Post = ({ navigation, route }) => { No tags available )} - {post.content} + {defPost.content} {hasStockData ? ( @@ -199,7 +256,7 @@ const Post = ({ navigation, route }) => { ) : stockData.length > 0 && stockDates.length > 0 ? ( - Stock Price Chart + {} { }} width={Dimensions.get('window').width - 20} // Adjust to screen width height={300} - yAxisLabel="$" + yAxisLabel="TL" verticalLabelRotation={60} chartConfig={{ backgroundColor: 'white', @@ -251,44 +308,45 @@ const Post = ({ navigation, route }) => { {tooltip.value} )} - - { - setTimeRange('5d'); - setInterval('1d'); - } } - > - 5d - - { - setTimeRange('1mo'); - setInterval('1d'); - } } - > - 1mo - - { - setTimeRange('1y'); - setInterval('1mo'); - } } - > - 1y - - { - setTimeRange('5y'); - setInterval('3mo'); - } } - > - 5y - - + + + { + setTimeRange('5d'); + setInterval('1d'); + } } + > + 5d + + { + setTimeRange('1mo'); + setInterval('1d'); + } } + > + 1mo + + { + setTimeRange('1y'); + setInterval('1mo'); + } } + > + 1y + + { + setTimeRange('5y'); + setInterval('3mo'); + } } + > + 5y + + ) : null} @@ -297,14 +355,19 @@ const Post = ({ navigation, route }) => { - - 👍 Like ({likes}) + + + {defPost.liked_by.includes(userId) ? '👍 Liked' : '👍 Like'} {defPost.liked_by.length} + + - 💬 Add Comment + 💬 Add Comment @@ -336,13 +399,13 @@ const Post = ({ navigation, route }) => { /> - Submit + Submit setShowCommentInput(false)} > - Cancel + Cancel @@ -387,23 +450,69 @@ const styles = StyleSheet.create({ marginTop: 20, }, likeButton: { - backgroundColor: '#0073e6', - paddingVertical: 10, - paddingHorizontal: 20, - borderRadius: 8, + backgroundColor: '#e0f7fa', + paddingVertical: 12, + paddingHorizontal: 24, + borderRadius: 12, + borderWidth: 1, // Subtle border for clarity + borderColor: '#cccccc', + alignItems: 'center', + justifyContent: 'center', + shadowColor: '#000', + shadowOffset: { width: 0, height: 2 }, + shadowOpacity: 0.2, + shadowRadius: 3, + elevation: 3, }, - commentButton: { - backgroundColor: '#28a745', - paddingVertical: 10, - paddingHorizontal: 20, - borderRadius: 8, + likedButton: { + backgroundColor: '#0073e6', + paddingVertical: 12, + paddingHorizontal: 24, + borderRadius: 12, + alignItems: 'center', + justifyContent: 'center', + shadowColor: '#0073e6', + shadowOffset: { width: 0, height: 4 }, + shadowOpacity: 0.3, + shadowRadius: 5, + elevation: 5, // Stronger shadow for emphasis + borderWidth: 1, // Optional border for a polished look + borderColor: '#005bb5', // Slightly darker border for depth }, buttonText: { + fontSize: 16, + fontWeight: 'bold', + color: '#0073e6', // White text for both states + }, + likedButtonText: { color: '#ffffff', fontSize: 16, fontWeight: 'bold', textAlign: 'center', }, + commentButtonText:{ + textAlign: 'center', + fontSize: 16, + fontWeight: 'bold', + color: '#ffffff', // White text for both states + }, + + commentButton: { + backgroundColor: '#28a745', + paddingVertical: 10, + paddingHorizontal: 20, + borderRadius: 12, + alignItems: 'center', + justifyContent: 'center', + shadowColor: '#0073e6', + shadowOffset: { width: 0, height: 4 }, + shadowOpacity: 0.3, + shadowRadius: 5, + elevation: 5, // Stronger shadow for emphasis + borderWidth: 1, // Optional border for a polished look + borderColor: 'grey', // Slightly darker border for depth + }, + tagsContainer: { flexDirection: 'row', flexWrap: 'wrap',