diff --git a/mobile/src/pages/Community.js b/mobile/src/pages/Community.js
index cb4932fd..29ce1c93 100644
--- a/mobile/src/pages/Community.js
+++ b/mobile/src/pages/Community.js
@@ -1,18 +1,20 @@
import React from 'react';
-import { View, Text, FlatList, ScrollView, StyleSheet, TextInput, TouchableOpacity, Image, Alert } from 'react-native';
+import { View, Text, FlatList, ScrollView, Modal, StyleSheet, TextInput, TouchableOpacity, Image, Alert } from 'react-native';
import { useState, useEffect } from 'react';
import { useFocusEffect } from '@react-navigation/native';
import config from './config/config';
import { useAuth } from './context/AuthContext';
-
const Community = ({navigation}) => {
- const { userId } = useAuth();
+ const { userId, accessToken } = useAuth();
const { baseURL } = config;
const [posts, setPosts] = useState([]);
const [users, setUsers] = useState([]);
const [userMap, setUserMap] = useState([]);
+ const [showCommentInput, setShowCommentInput] = useState(false);
+ const [commentText, setCommentText] = useState('');
+
const fetchPosts = async () => {
const postURL = baseURL + '/posts/?page=1';
@@ -70,17 +72,48 @@ const Community = ({navigation}) => {
console.error('Error:', error);
}
};
+
+ const postComment = async (comment) => {
+ const commentURL = `${baseURL}/comments/`;
+ try {
+ const response = await fetch(commentURL, {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ Authorization: `Bearer ${accessToken}`,
+ },
+ body: JSON.stringify(comment),
+ });
+ if (response.ok) {
+ const commentResponse = await response.json();
+ setCommentText('');
+ Alert.alert('Comment added successfully');
+ } else {
+ console.error(response);
+ console.error(`Failed to post comment: ${response.status}`);
+ }
+ } catch (error) {
+ console.error('Error posting comment:', error);
+ }
+ };
+
+
useFocusEffect(
- React.useCallback(() => {
- fetchPosts();
- fetchUsers();
- }, [])
+ React.useCallback(() => {
+ fetchPosts();
+ fetchUsers();
+ }, [])
);
const handleViewPost = (post) => {
- navigation.navigate('Post', { postId: post.id, author: userMap[post.author] ? userMap[post.author].username : post.author });
+ navigation.navigate('Post', {
+ postId: post.id,
+ author: userMap[post.author] ? userMap[post.author].username : post.author,
+ userMap: userMap,
+ post: post,
+ });
};
const handleCreatePost = () => {
@@ -93,6 +126,29 @@ const Community = ({navigation}) => {
}
+ const handleAddCommentButton = () => {
+ if(accessToken == null){
+ navigation.navigate('Login&Register');
+ Alert.alert('Please login to add a comment');
+ return;
+ }else{
+ setShowCommentInput(true);
+
+ }
+ };
+
+ const handleAddComment = (postId) => {
+ if (commentText.trim() === '') {
+ return; // Ignore empty comments
+ }
+ const newComment = {
+ post_id: postId,
+ content: commentText,
+ };
+ postComment(newComment);
+ setShowCommentInput(false);
+ };
+
const renderUsername = (post) => {
if(userMap[post.author]){
return userMap[post.author].username;
@@ -102,7 +158,7 @@ const Community = ({navigation}) => {
}
const renderItem = ({ item: post }) => (
- console.log("post", post),
+ //console.log("post", post),
{post.title}
@@ -114,14 +170,12 @@ const Community = ({navigation}) => {
{tag.name}
))}
- {post.graph && (
-
- )}
+
- 👍 {post.likes}
+ 👍 {post.liked_by.length}
-
+
💬 {post.comments}
{
renderItem={renderItem}
keyExtractor={(item) => item.id.toString()}
/>
+
+
+
+
+
+
+ Submit
+
+ setShowCommentInput(false)}
+ >
+ Cancel
+
+
+
+
+
);
};
@@ -201,14 +278,16 @@ const styles = StyleSheet.create({
fontSize: 18,
fontWeight: 'bold',
marginBottom: 5,
+ color: 'black',
},
postMeta: {
- color: '#777',
+ color: 'black',
marginBottom: 10,
},
postContent: {
fontSize: 16,
marginBottom: 10,
+ color: 'black',
},
tagsContainer: {
flexDirection: 'row',
@@ -249,6 +328,51 @@ const styles = StyleSheet.create({
color: '#fff',
fontWeight: 'bold',
},
+ modalContainer: {
+ flex: 1,
+ justifyContent: 'center',
+ alignItems: 'center',
+ backgroundColor: 'rgba(0, 0, 0, 0.5)',
+ },
+ modalContent: {
+ width: '90%',
+ padding: 20,
+ backgroundColor: 'white',
+ borderRadius: 10,
+ },
+ commentInput: {
+ height: 100,
+ borderWidth: 1,
+ borderColor: '#ccc',
+ borderRadius: 8,
+ padding: 10,
+ marginBottom: 15,
+ textAlignVertical: 'top',
+ },
+ modalButtons: {
+ flexDirection: 'row',
+ justifyContent: 'space-between',
+ },
+ submitButton: {
+ backgroundColor: '#28a745',
+ padding: 10,
+ borderRadius: 8,
+ flex: 1,
+ marginRight: 5,
+ },
+ cancelButton: {
+ backgroundColor: '#dc3545',
+ padding: 10,
+ borderRadius: 8,
+ flex: 1,
+ marginLeft: 5,
+ },
+ buttonText: {
+ color: '#ffffff',
+ fontSize: 16,
+ fontWeight: 'bold',
+ textAlign: 'center',
+ },
});
export default Community;
diff --git a/mobile/src/pages/Post.js b/mobile/src/pages/Post.js
index 318769c9..712a45e9 100644
--- a/mobile/src/pages/Post.js
+++ b/mobile/src/pages/Post.js
@@ -1,38 +1,49 @@
import React, { useState, useEffect } from 'react';
-import { ScrollView, Text, StyleSheet, View, Dimensions, TouchableOpacity } from 'react-native';
+import {
+ ScrollView,
+ Text,
+ StyleSheet,
+ View,
+ Dimensions,
+ TouchableOpacity,
+ TextInput,
+ Modal,
+ Alert,
+} from 'react-native';
import { LineChart } from 'react-native-chart-kit';
import config from './config/config';
+import { useAuth } from './context/AuthContext';
-const Post = ({ route }) => {
- const { postId, author } = route.params;
+const Post = ({ navigation, route }) => {
+ const { postId, author, userMap, post } = route.params;
const { baseURL } = config;
+ const { accessToken } = useAuth();
- const [post, setPost] = useState(null);
const [likes, setLikes] = useState(0);
const [tooltip, setTooltip] = useState(null);
- //const [author, setAuthor] = useState(null);
+ const [showCommentInput, setShowCommentInput] = useState(false);
+ const [commentText, setCommentText] = useState('');
+ const [comments, setComments] = useState([]);
+ //const [userMap, setUserMap] = useState([]);
useEffect(() => {
- fetchPost();
+ //fetchPost();
+ fetchComments();
}, [postId]);
-
+
const fetchPost = async () => {
const postURL = `${baseURL}/posts/${postId}/`;
try {
- const response = await fetch(postURL);
+ const response = await fetch(postURL, {
+ method: 'GET',
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ });
if (response.ok) {
const postData = await response.json();
-
- // Use postData directly to fetch the author
setPost(postData);
setLikes(postData.liked_by.length || 0);
-
- // Fetch author data based on postData.author
- /* if (postData.author) {
- fetchAuthor(postData.author);
- } else {
- console.warn('Post does not contain an author field.');
- } */
} else {
console.error(`Failed to fetch post: ${response.status}`);
}
@@ -41,26 +52,89 @@ const Post = ({ route }) => {
}
};
+ const fetchComments = async () => {
+ const postURL = `${baseURL}/comments/post-comments/${postId}/`;
+ try {
+ const response = await fetch(postURL, {
+ method: 'GET',
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ });
+ if (response.ok) {
+ console.log("response", response);
+ const commentsList = await response.json();
+ setComments(commentsList);
+
+ } else {
+ console.error(`Failed to fetch comments: ${response}`);
+ }
+ } catch (error) {
+ console.error('Error fetching comments:', error);
+ }
+ };
-
- const fetchAuthor = async (authorId) => {
- const authorURL = `${baseURL}/users/${authorId}/`; // Replace with your API's author endpoint
+ const postComment = async (comment) => {
+ const commentURL = `${baseURL}/comments/`;
try {
- const response = await fetch(authorURL);
+ const response = await fetch(commentURL, {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ Authorization: `Bearer ${accessToken}`,
+ },
+ body: JSON.stringify(comment),
+ });
if (response.ok) {
- const authorData = await response.json();
- setAuthor(authorData);
+ const commentResponse = await response.json();
+ setComments([...comments, commentResponse]);
+ setCommentText('');
+ Alert.alert('Comment added successfully');
} else {
- console.error(`Failed to fetch author: ${response.status}`);
+ console.error(response);
+ console.error(`Failed to post comment: ${response.status}`);
}
} catch (error) {
- console.error('Error fetching author:', error);
+ console.error('Error posting comment:', error);
}
};
+ const renderUsername = (comment) => {
+ if(userMap[comment.user_id]){
+ return userMap[comment.user_id].username;
+ }else{
+ return comment.user_id;
+ }
+ }
+
+
const handleLike = () => {
setLikes((prevLikes) => prevLikes + 1);
};
+
+ const handleAddCommentButton = () => {
+ if(accessToken == null){
+ navigation.navigate('Login&Register');
+ Alert.alert('Please login to add a comment');
+ return;
+ }else{
+ setShowCommentInput(true);
+
+ }
+ };
+
+ const handleAddComment = () => {
+
+ if (commentText.trim() === '') {
+ return; // Ignore empty comments
+ }
+ const newComment = {
+ post_id: postId,
+ content: commentText,
+ };
+ postComment(newComment);
+ setShowCommentInput(false);
+ };
const stockData = [142, 145, 143, 141, 144, 140, 138, 139];
@@ -69,6 +143,9 @@ const Post = ({ route }) => {
}
return (
+ //console.log("comment", comments),
+ //console.log("userMap", userMap),
+ //console.log(accessToken),
{post.title}
Author: {author ? author : 'Unknown'}
@@ -79,7 +156,7 @@ const Post = ({ route }) => {
{post.tags.length > 0 ? (
post.tags.map((tag) => (
- #{tag.name}
+ {tag.name}
))
) : (
@@ -87,68 +164,121 @@ const Post = ({ route }) => {
)}
{post.content}
+ Stock Price Chart
+
+ `rgba(0, 123, 255, ${opacity})`, // Blue line color
+ labelColor: (opacity = 1) => `rgba(0, 0, 0, ${opacity})`, // Black label color
+ style: {
+ borderRadius: 16,
+ },
+ propsForDots: {
+ r: '6',
+ strokeWidth: '2',
+
+ },
+ }}
+ bezier
+ style={styles.chart}
+ onDataPointClick={({ value, x, y, index }) => {
+ setTooltip({
+ value: `${value}`,
+ x,
+ y,
+ index,
+ });
+ }}
+ />
+
+ {/* Tooltip for the clicked point */}
+ {tooltip && (
+
+ {tooltip.value}
+
+ )}
+
-
- {/* Buttons */}
👍 Like ({likes})
-
+
💬 Add Comment
- {/* Chart Section */}
- Stock Price Chart
- `rgba(26, 255, 146, ${opacity})`,
- labelColor: (opacity = 1) => `rgba(255, 255, 255, ${opacity})`,
- style: {
- borderRadius: 16,
- },
- propsForDots: {
- r: '6',
- strokeWidth: '2',
- stroke: '#ffa726',
- },
- }}
- bezier
- style={styles.chart}
- onDataPointClick={({ value, x, y }) => setTooltip({ value, x, y })}
- />
- {tooltip && (
-
- ${tooltip.value}
+
+
+ Comments
+ {comments.length > 0 ? (
+ comments.map((comment) => (
+
+ {renderUsername(comment)}
+ {comment.content}
+
+
+ ))
+ ) : (
+ No comments yet.
+ )}
+
+
+
+
+
+
+
+
+
+ Submit
+
+ setShowCommentInput(false)}
+ >
+ Cancel
+
+
+
- )}
+
);
};
-
-
const styles = StyleSheet.create({
container: {
flex: 1,
@@ -218,6 +348,88 @@ const styles = StyleSheet.create({
fontSize: 14,
color: '#aaa',
},
+ comments: {
+ marginBottom: 20,
+ },
+ commentsTitle: {
+ fontSize: 20,
+ fontWeight: 'bold',
+ marginTop: 20,
+ },
+ commentContainer: {
+ marginBottom: 15,
+ padding: 10,
+ backgroundColor: '#f9f9f9',
+ borderRadius: 10,
+ },
+ commentAuthor: {
+ fontWeight: 'bold',
+ marginBottom: 5,
+ },
+ commentText: {
+ marginBottom: 5,
+ },
+ commentDate: {
+ fontSize: 12,
+ color: '#999',
+ },
+ modalContainer: {
+ flex: 1,
+ justifyContent: 'center',
+ alignItems: 'center',
+ backgroundColor: 'rgba(0, 0, 0, 0.5)',
+ },
+ modalContent: {
+ width: '90%',
+ padding: 20,
+ backgroundColor: 'white',
+ borderRadius: 10,
+ },
+ commentInput: {
+ height: 100,
+ borderWidth: 1,
+ borderColor: '#ccc',
+ borderRadius: 8,
+ padding: 10,
+ marginBottom: 15,
+ textAlignVertical: 'top',
+ },
+ modalButtons: {
+ flexDirection: 'row',
+ justifyContent: 'space-between',
+ },
+ submitButton: {
+ backgroundColor: '#28a745',
+ padding: 10,
+ borderRadius: 8,
+ flex: 1,
+ marginRight: 5,
+ },
+ cancelButton: {
+ backgroundColor: '#dc3545',
+ padding: 10,
+ borderRadius: 8,
+ flex: 1,
+ marginLeft: 5,
+ },
+ noComments: {
+ fontSize: 14,
+ color: '#aaa',
+ marginTop: 10,
+ },
+ tooltip: {
+ position: 'absolute',
+ backgroundColor: 'rgba(0, 0, 0, 0.8)', // Dark background for contrast
+ padding: 5,
+ borderRadius: 5,
+ alignItems: 'center',
+ justifyContent: 'center',
+ },
+ tooltipText: {
+ color: '#ffffff', // White text for visibility
+ fontSize: 12,
+ fontWeight: 'bold',
+ },
});
export default Post;