diff --git a/README.md b/README.md
index b10398b..3ca1ede 100644
--- a/README.md
+++ b/README.md
@@ -16,7 +16,7 @@ As of the latest project update (May 27th, 2024), "Live Timeless" has successful
- **Coaching Chatbot:** Fully integrated with Google's gemini pro model, the AI coaching chatbot is operational, offering personalized plans and actionable steps to help users achieve their health goals.
-- **User Account Management:**: All user account management features are complete and functioning as intended, making it GDPR (General data protection regulation) compliant. This includes:
+- **User Account Management:** All user account management features are complete and functioning as intended, making it GDPR (General data protection regulation) compliant. This includes:
- Secure registration and login processes.
- Functionalities to delete accounts and download user data.
diff --git a/package-lock.json b/package-lock.json
index 29552fd..a77c2bd 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -9,7 +9,6 @@
"version": "1.0.0",
"hasInstallScript": true,
"dependencies": {
-
"@emailjs/react-native": "^4.2.2",
"@fortawesome/fontawesome-svg-core": "^6.5.2",
"@fortawesome/free-brands-svg-icons": "^6.5.2",
@@ -1486,7 +1485,6 @@
"version": "7.24.1",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.24.1.tgz",
"integrity": "sha512-iQ+caew8wRrhCikO5DrUYx0mrmdhkaELgFa+7baMcVuhxIkN7oxt06CZ51D65ugIb1UWRQ8oQe+HXAVM6qHFjw==",
- "peer": true,
"dependencies": {
"@babel/helper-plugin-utils": "^7.24.0",
"@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3"
@@ -1566,7 +1564,6 @@
"version": "7.24.5",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.24.5.tgz",
"integrity": "sha512-xWCkmwKT+ihmA6l7SSTpk8e4qQl/274iNbSKRRS8mpqFR32ksy36+a+LWY8OXCCEefF8WFlnOHVsaDI2231wBg==",
- "peer": true,
"dependencies": {
"@babel/helper-plugin-utils": "^7.24.5",
"@babel/helper-skip-transparent-expression-wrappers": "^7.22.5",
@@ -1826,7 +1823,6 @@
"version": "7.24.1",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.24.1.tgz",
"integrity": "sha512-WRkhROsNzriarqECASCNu/nojeXCDTE/F2HmRgOzi7NGvyfYGq1NEjKBK3ckLfRgGc6/lPAqP0vDOSw3YtG34g==",
- "peer": true,
"dependencies": {
"@babel/helper-plugin-utils": "^7.24.0"
},
@@ -2567,6 +2563,11 @@
"js-yaml": "bin/js-yaml.js"
}
},
+ "node_modules/@expo/cli/node_modules/ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
+ },
"node_modules/@expo/cli/node_modules/resolve": {
"version": "1.22.8",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz",
@@ -4521,11 +4522,6 @@
"node": ">=8"
}
},
- "node_modules/@react-native-community/cli-doctor/node_modules/yallist": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
- "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
- },
"node_modules/@react-native-community/cli-hermes": {
"version": "13.6.6",
"resolved": "https://registry.npmjs.org/@react-native-community/cli-hermes/-/cli-hermes-13.6.6.tgz",
@@ -6055,47 +6051,6 @@
"node": ">=14.15"
}
},
- "node_modules/@rnx-kit/chromium-edge-launcher/node_modules/mkdirp": {
- "version": "1.0.4",
- "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
- "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==",
- "bin": {
- "mkdirp": "bin/cmd.js"
- },
- "engines": {
- "node": ">=10"
- }
- },
- "node_modules/@rnx-kit/chromium-edge-launcher/node_modules/rimraf": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
- "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
- "dependencies": {
- "glob": "^7.1.3"
- },
- "bin": {
- "rimraf": "bin.js"
- },
- "funding": {
- "url": "https://github.com/sponsors/isaacs"
- }
- },
- "node_modules/@rnx-kit/chromium-edge-launcher": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/@rnx-kit/chromium-edge-launcher/-/chromium-edge-launcher-1.0.0.tgz",
- "integrity": "sha512-lzD84av1ZQhYUS+jsGqJiCMaJO2dn9u+RTT9n9q6D3SaKVwWqv+7AoRKqBu19bkwyE+iFRl1ymr40QS90jVFYg==",
- "dependencies": {
- "@types/node": "^18.0.0",
- "escape-string-regexp": "^4.0.0",
- "is-wsl": "^2.2.0",
- "lighthouse-logger": "^1.0.0",
- "mkdirp": "^1.0.4",
- "rimraf": "^3.0.2"
- },
- "engines": {
- "node": ">=14.15"
- }
- },
"node_modules/@rnx-kit/chromium-edge-launcher/node_modules/@types/node": {
"version": "18.19.33",
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.33.tgz",
@@ -8571,7 +8526,6 @@
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
"dev": true
},
-
"node_modules/eslint/node_modules/escape-string-regexp": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
@@ -8620,7 +8574,6 @@
"node": ">=8"
}
},
-
"node_modules/eslint/node_modules/js-yaml": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
@@ -8899,7 +8852,6 @@
"integrity": "sha512-t2p7BChO3Reykued++QJRMZ/og6J3aXtSQ+bU31YcBeXhZLkHwjWEhiPKPnJka7J2/yTs4+jOCNDY0kCZmcE3w==",
"peerDependencies": {
"expo": "*"
-
}
},
"node_modules/expo-constants": {
@@ -8933,7 +8885,6 @@
"version": "12.0.4",
"resolved": "https://registry.npmjs.org/expo-font/-/expo-font-12.0.4.tgz",
"integrity": "sha512-VtOQB7MEeFMVwo46/9/ntqzrgraTE7gAsnfi2NukFcCpDmyAU3G1R7m287LUXltE46SmGkMgAvM6+fflXFjaJA==",
-
"dependencies": {
"fontfaceobserver": "^2.1.0"
},
@@ -8983,7 +8934,6 @@
"version": "13.0.2",
"resolved": "https://registry.npmjs.org/expo-linear-gradient/-/expo-linear-gradient-13.0.2.tgz",
"integrity": "sha512-EDcILUjRKu4P1rtWcwciN6CSyGtH7Bq4ll3oTRV7h3h8oSzSilH1g6z7kTAMlacPBKvMnkkWOGzW6KtgMKEiTg==",
-
"peerDependencies": {
"expo": "*"
}
@@ -9674,18 +9624,6 @@
"url": "https://github.com/sponsors/isaacs"
}
},
- "node_modules/glob-parent": {
- "version": "6.0.2",
- "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
- "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
- "dev": true,
- "dependencies": {
- "is-glob": "^4.0.3"
- },
- "engines": {
- "node": ">=10.13.0"
- }
- },
"node_modules/globals": {
"version": "15.1.0",
"resolved": "https://registry.npmjs.org/globals/-/globals-15.1.0.tgz",
@@ -12720,7 +12658,6 @@
"url": "https://github.com/fb55/nth-check?sponsor=1"
}
},
-
"node_modules/nullthrows": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/nullthrows/-/nullthrows-1.1.1.tgz",
diff --git a/src/components/CardPost.js b/src/components/CardPost.js
index e61d0eb..bca9d60 100644
--- a/src/components/CardPost.js
+++ b/src/components/CardPost.js
@@ -60,7 +60,7 @@ const CardPost = (props) => {
const fetchLikedUsers = async () => {
try {
- // Get user_ids who liked the post
+ // grab user_ids who liked the post
const { data: likeData, error: likeError } = await supabase
.from('Like')
.select('user_id')
@@ -76,7 +76,7 @@ const CardPost = (props) => {
const userIds = likeData.map(like => like.user_id);
- // Get user details from User table
+ // grab user details from User table
const { data: usersData, error: usersError } = await supabase
.from('User')
.select('user_id, username, profile_image')
@@ -92,23 +92,22 @@ const CardPost = (props) => {
};
const renderCardContent = () => (
- fetchLikeCount(),
- fetchLikeStatus(),
{props.post.habit_photo && (
)}
+ {/* */}
+ {props.postDescription}
+
{countLikes} likes
-
- {props.postUser?.name ?? 'Unknown User'}
- {props.postDescription}
-
-
+
+ //
);
+
const renderCardActions = () => (
@@ -319,9 +318,12 @@ const styles = StyleSheet.create({
textUserName: {
fontWeight: 'bold',
color: Colors.text,
+ marginLeft: 10,
},
textSubtitle: {
color: '#FFFFFF',
+ marginLeft: 10,
+ marginTop: 5,
},
cardPostVerticalEllipsis: {
width: 24,
@@ -330,14 +332,19 @@ const styles = StyleSheet.create({
habitImage: {
width: '100%',
height: 400,
+ borderRadius: 20,
},
cardPostDescriptionContainer: {
- flexDirection: 'row',
+ flexDirection: 'column',
alignItems: 'flex-start',
padding: 10,
},
postText: {
color: Colors.text,
+ marginLeft: 5,
+ marginBottom: 10,
+ marginTop: 5,
+ lineHeight: 25,
},
containerActions: {
flexDirection: 'row',
@@ -354,6 +361,13 @@ const styles = StyleSheet.create({
marginLeft: 5,
color: Colors.text,
},
+ cardShadow: {
+ marginBottom: 5,
+ borderTopWidth: 0.2,
+ // borderBottomWidth: 1,
+ borderColor: 'rgba(255, 255, 255, 0.6)',
+ paddingVertical: 5,
+ },
icon: {
width: 29,
height: 29,
@@ -392,7 +406,7 @@ const styles = StyleSheet.create({
},
closeButton: {
marginTop: 20,
- padding: 10,
+ padding: 15,
backgroundColor: Colors.primary,
borderRadius: 5,
},
@@ -402,4 +416,5 @@ const styles = StyleSheet.create({
},
});
+
export default CardPost;
diff --git a/src/components/CommentCard.js b/src/components/CommentCard.js
index d3e92e3..3dd33c8 100644
--- a/src/components/CommentCard.js
+++ b/src/components/CommentCard.js
@@ -7,7 +7,7 @@ import Colors from '../../assets/styles/Colors';
import { Image } from 'react-native';
import moment from 'moment';
-export default function CommentCard({ commentData }) {
+export default function CommentCard({ commentData}) {
return (
@@ -18,7 +18,7 @@ export default function CommentCard({ commentData }) {
{commentData.user_id.username}
- {moment(commentData.createdAt).local().startOf('second').fromNow()}
+ {moment(commentData.created_at).fromNow()}
diff --git a/src/screens/FollowScreen.js b/src/screens/FollowScreen.js
index 7177e25..0634922 100644
--- a/src/screens/FollowScreen.js
+++ b/src/screens/FollowScreen.js
@@ -218,7 +218,7 @@ const styles = StyleSheet.create({
fontWeight: 'bold',
},
unfollowButton: {
- color: Colors.secondary,
+ color: 'white',
fontWeight: 'bold',
},
alreadyFollowing: {
diff --git a/src/screens/FollowersScreen.js b/src/screens/FollowersScreen.js
index 6af6a6e..93f907b 100644
--- a/src/screens/FollowersScreen.js
+++ b/src/screens/FollowersScreen.js
@@ -248,7 +248,7 @@ const styles = StyleSheet.create({
fontWeight: 'bold',
},
unfollowButton: {
- color: Colors.secondary,
+ color: 'white',
fontWeight: 'bold',
},
alreadyFollowing: {
diff --git a/src/screens/ProfileScreen.js b/src/screens/ProfileScreen.js
index e257973..f54985f 100644
--- a/src/screens/ProfileScreen.js
+++ b/src/screens/ProfileScreen.js
@@ -4,21 +4,22 @@ import {
StyleSheet,
View,
Alert,
- ScrollView,
Text,
Image,
Dimensions,
TouchableOpacity,
Modal,
+ FlatList,
+ RefreshControl,
} from 'react-native';
-import { Button, Input } from 'react-native-elements';
+import { Button } from 'react-native-elements';
import { supabase } from '../config/supabaseClient';
import { useNavigation } from '@react-navigation/native';
import * as ImagePicker from 'expo-image-picker';
import store from '../store/storeConfig';
import Default from '../../assets/styles/Default';
import Colors from '../../assets/styles/Colors';
-import { FlatList } from 'react-native-gesture-handler';
+import moment from 'moment';
const { width } = Dimensions.get('window');
const imageSize = width / 3;
@@ -33,21 +34,22 @@ export default function Account() {
const [postsCount, setPostsCount] = useState(0);
const [followingCount, setFollowingCount] = useState(0);
const [followerCount, setFollowerCount] = useState(0);
- const [habitImages, setHabitImages] = useState([]);
+ const [habitPosts, setHabitPosts] = useState([]);
const [selectedImage, setSelectedImage] = useState(null);
const [modalVisible, setModalVisible] = useState(false);
+ const [refreshing, setRefreshing] = useState(false);
+ const [showLikesModal, setShowLikesModal] = useState(false);
+ const [likedUsers, setLikedUsers] = useState([]);
+ const [showCommentsModal, setShowCommentsModal] = useState(false);
+ const [comments, setComments] = useState([]);
useEffect(() => {
if (session) {
- getProfile();
- getPostsCount();
- getFollowingCount();
- getFollowerCount();
- getHabitImages();
+ fetchAllData();
}
}, [session]);
- async function getProfile() {
+ const getProfile = async () => {
try {
setLoading(true);
if (!session?.user) throw new Error('No user on the session!');
@@ -71,102 +73,178 @@ export default function Account() {
} finally {
setLoading(false);
}
- }
+ };
- const getHabitImages = async () => {
+ const getHabitPosts = async () => {
const userId = session?.user.id;
try {
setLoading(true);
-
+
const { data: scheduleData, error: scheduleError } = await supabase
.from('Schedule')
.select('habit_id, schedule_id')
.eq('user_id', userId);
-
+
if (scheduleError) throw scheduleError;
-
+
const habitIds = scheduleData.map(schedule => schedule.habit_id);
const scheduleIds = scheduleData.map(schedule => schedule.schedule_id);
-
+
if (habitIds.length === 0 || scheduleIds.length === 0) {
- Alert.alert('Error', 'No habits or schedules found for the user');
setLoading(false);
return;
}
-
+
const { data: habitData, error: habitError } = await supabase
.from('Habit')
- .select('habit_id, habit_title, habit_photo')
+ .select('habit_id, habit_title')
.in('habit_id', habitIds);
-
+
if (habitError) throw habitError;
-
- const { data: imagesData, error: imagesError } = await supabase
- .from('HabitImages')
- .select('*')
- .in('habit_id', habitIds);
-
- if (imagesError) throw imagesError;
-
+
const { data: postsData, error: postsError } = await supabase
.from('Post')
- .select('post_id, post_description, schedule_id')
+ .select('post_id, post_description, schedule_id, created_at')
.in('schedule_id', scheduleIds);
-
+
if (postsError) throw postsError;
-
+
const postIds = postsData.map(post => post.post_id);
-
+
const { data: postImagesData, error: postImagesError } = await supabase
.from('Image')
.select('image_photo, post_id')
.in('post_id', postIds);
-
+
if (postImagesError) throw postImagesError;
-
- const combinedImages = habitData.map(habit => {
- const habitImages = imagesData.filter(image => image.habit_id === habit.habit_id);
+
+ const { data: likeData, error: likeError } = await supabase
+ .from('Like')
+ .select('post_id')
+ .in('post_id', postIds);
+
+ if (likeError) throw likeError;
+
+ const { data: commentsData, error: commentsError } = await supabase
+ .from('Comments')
+ .select('post_id')
+ .in('post_id', postIds);
+
+ if (commentsError) throw commentsError;
+
+ const likeCountMap = likeData.reduce((acc, like) => {
+ acc[like.post_id] = (acc[like.post_id] || 0) + 1;
+ return acc;
+ }, {});
+
+ const commentCountMap = commentsData.reduce((acc, comment) => {
+ acc[comment.post_id] = (acc[comment.post_id] || 0) + 1;
+ return acc;
+ }, {});
+
+ const combinedPosts = habitData.map(habit => {
const habitPosts = postsData.filter(post =>
scheduleData.find(schedule => schedule.schedule_id === post.schedule_id)?.habit_id === habit.habit_id
);
const habitPostImages = postImagesData.filter(image =>
habitPosts.some(post => post.post_id === image.post_id)
);
-
+
const combined = [
- ...(habit.habit_photo ? [{ image_photo: habit.habit_photo, id: 'habitPhoto' }] : []),
- ...habitImages,
...habitPostImages.map(image => ({
...image,
- post_description: habitPosts.find(post => post.post_id === image.post_id)?.post_description
+ post_description: habitPosts.find(post => post.post_id === image.post_id)?.post_description,
+ created_at: habitPosts.find(post => post.post_id === image.post_id)?.created_at,
+ like_count: likeCountMap[image.post_id] || 0,
+ comment_count: commentCountMap[image.post_id] || 0,
}))
].filter(image => image.image_photo !== null);
-
+
const postTexts = habitPosts
.filter(post => !postImagesData.some(image => image.post_id === post.post_id && image.image_photo))
.map(post => ({
post_description: post.post_description,
- post_id: post.post_id
+ post_id: post.post_id,
+ created_at: post.created_at,
+ like_count: likeCountMap[post.post_id] || 0,
+ comment_count: commentCountMap[post.post_id] || 0,
}));
-
+
return {
habit_title: habit.habit_title,
images: combined,
postTexts: postTexts
};
});
-
- setHabitImages(combinedImages);
+
+ setHabitPosts(combinedPosts);
} catch (error) {
- Alert.alert('Error fetching habit images', error.message);
+ Alert.alert('Error fetching habit posts', error.message);
} finally {
setLoading(false);
}
};
-
+ const fetchLikedUsers = async (postId) => {
+ try {
+ const { data: likeData, error: likeError } = await supabase
+ .from('Like')
+ .select('user_id')
+ .eq('post_id', postId);
+
+ if (likeError) throw likeError;
+
+ if (likeData.length === 0) {
+ setLikedUsers([]);
+ setShowLikesModal(true);
+ return;
+ }
+
+ const userIds = likeData.map(like => like.user_id);
+
+ const { data: usersData, error: usersError } = await supabase
+ .from('User')
+ .select('user_id, username, profile_image')
+ .in('user_id', userIds);
+
+ if (usersError) throw usersError;
+
+ setLikedUsers(usersData);
+ setShowLikesModal(true);
+ } catch (error) {
+ Alert.alert('Error', 'Failed to fetch users who liked the post');
+ }
+ };
+
+ const fetchComments = async (postId) => {
+ try {
+ const { data: commentsData, error: commentsError } = await supabase
+ .from('Comments')
+ .select('content, user_id')
+ .eq('post_id', postId);
+
+ if (commentsError) throw commentsError;
+
+ const userIds = commentsData.map(comment => comment.user_id);
+
+ const { data: usersData, error: usersError } = await supabase
+ .from('User')
+ .select('user_id, username, profile_image')
+ .in('user_id', userIds);
-
+ if (usersError) throw usersError;
+
+ const commentsWithUserDetails = commentsData.map(comment => ({
+ ...comment,
+ user: usersData.find(user => user.user_id === comment.user_id),
+ }));
+
+ setComments(commentsWithUserDetails);
+ setShowCommentsModal(true);
+ } catch (error) {
+ Alert.alert('Error', 'Failed to fetch comments');
+ }
+ };
async function getPostsCount() {
try {
@@ -276,6 +354,25 @@ export default function Account() {
return bytes.buffer;
};
+ const onRefresh = () => {
+ setRefreshing(true);
+ fetchAllData();
+ };
+
+ const fetchAllData = async () => {
+ try {
+ await getProfile();
+ await getPostsCount();
+ await getFollowingCount();
+ await getFollowerCount();
+ await getHabitPosts();
+ } catch (error) {
+ Alert.alert('Error', error.message);
+ } finally {
+ setRefreshing(false);
+ }
+ };
+
const uploadProfileImage = async (image) => {
try {
setLoading(true);
@@ -348,35 +445,54 @@ export default function Account() {
setSelectedImage(null);
};
- const renderHabitImages = ({ item }) => (
+ const renderHabitPosts = ({ item }) => (
{item.habit_title}
index.toString()}
- numColumns={3}
+ numColumns={1}
renderItem={({ item: image }) => (
- openModal(image)}>
-
-
+
+ openModal(image)}>
+
+
+
+ {image.post_description}
+ {moment(image.created_at).local().startOf('second').fromNow()}
+
+ fetchLikedUsers(image.post_id)}>
+ {image.like_count > 0 ? `${image.like_count} likes` : null}
+
+ fetchComments(image.post_id)}>
+
+ {image.comment_count > 0 ? image.comment_count : null}
+
+
+
+
)}
/>
{item.postTexts.length > 0 && (
{item.postTexts.map((post) => (
- {post.post_description}
+ {post.post_description}
+ {moment(post.created_at).local().startOf('second').fromNow()}
+ fetchLikedUsers(post.post_id)}>
+ {post.like_count > 0 ? `${post.like_count} likes` : null}
+
+ fetchComments(post.post_id)}>
+
+ {post.comment_count > 0 ? post.comment_count : null}
+
))}
)}
+
);
-
-
-
-
-
return (
@@ -412,11 +528,11 @@ export default function Account() {
-
+
-
+
-
+
-
-
- Username
+
+ {/*
{username}
-
+ */}
Bio
{bio}
+
+
>
}
- data={habitImages}
- renderItem={renderHabitImages}
+ data={habitPosts}
+ renderItem={renderHabitPosts}
keyExtractor={(item, index) => index.toString()}
+ refreshControl={
+
+ }
/>
-
+
- {selectedImage.post_description}
+ {selectedImage.post_description}
>
)}
+
+ setShowLikesModal(false)}
+ >
+
+
+ Liked by
+ item.user_id.toString()}
+ renderItem={({ item }) => (
+
+
+ {item.username}
+
+ )}
+ />
+ setShowLikesModal(false)} style={styles.closeButton}>
+ Close
+
+
+
+
+
+ setShowCommentsModal(false)}
+ >
+
+
+ Comments
+ index.toString()}
+ renderItem={({ item }) => (
+
+
+
+ {item.user.username}
+ {item.content}
+
+
+ )}
+ />
+ setShowCommentsModal(false)} style={styles.closeButton}>
+ Close
+
+
+
+
);
-
-
-
}
const styles = StyleSheet.create({
@@ -499,6 +679,8 @@ const styles = StyleSheet.create({
width: 90,
height: 90,
borderRadius: 45,
+ marginBottom: 10,
+ marginTop: 30,
},
textName: {
fontSize: 20,
@@ -570,6 +752,7 @@ const styles = StyleSheet.create({
},
inputWrapper: {
marginBottom: 10,
+ alignItems: 'center',
},
inputContainer: {
marginBottom: 10,
@@ -592,11 +775,13 @@ const styles = StyleSheet.create({
fontWeight: 'bold',
color: Colors.white,
marginBottom: 8,
+ textAlign: 'center',
},
textContent: {
fontSize: 14,
color: Colors.white,
marginBottom: 16,
+ textAlign: 'center',
},
inputLabel: {
color: Colors.text,
@@ -643,6 +828,25 @@ const styles = StyleSheet.create({
marginBottom: 10,
marginLeft: 10,
},
+ cardContainer: {
+
+ borderRadius: 10,
+ backgroundColor: Colors.cardBackground,
+ marginBottom: 20,
+ overflow: 'hidden',
+ padding: 10,
+
+ },
+ cardContent: {
+ padding: 10,
+ },
+ separator: {
+ height: 1,
+ backgroundColor: Colors.lightGray,
+ marginVertical: 20,
+ borderBottomWidth: 1,
+ borderBottomColor: 'rgba(105, 105, 120, 0.9)',
+ },
gridImage: {
width: imageSize - 10,
height: imageSize - 10,
@@ -651,17 +855,49 @@ const styles = StyleSheet.create({
textContainer: {
padding: 10,
},
- textPost: {
- fontSize: 16,
- color: 'black',
- },
textPostContainer: {
borderWidth: 1,
borderColor: Colors.lightGray,
borderRadius: 8,
padding: 10,
marginBottom: 10,
- backgroundColor: Colors.white,
+ backgroundColor: 'rgba(0, 0, 0, 0.5)',
+ },
+ textUserName: {
+ fontWeight: 'bold',
+ color: Colors.text,
+ marginBottom: 5,
+ },
+ postText: {
+ color: Colors.text,
+ marginBottom: 10,
+ marginLeft: 10,
+ marginTop: 10,
+ },
+ textSubtitle: {
+ color: '#FFFFFF',
+ marginLeft: 10,
+ },
+ containerActions: {
+ flexDirection: 'row',
+ alignItems: 'center',
+ paddingVertical: 10,
+ paddingHorizontal: 10,
+ },
+ buttonActions: {
+ flexDirection: 'row',
+ alignItems: 'center',
+ marginRight: 20,
+ },
+ textPostActions: {
+ marginRight: 10,
+ marginRight: 10,
+ color: Colors.text,
+ },
+ icon: {
+ width: 29,
+ height: 29,
+ margin: 10,
},
modalContainer: {
flex: 1,
@@ -676,28 +912,113 @@ const styles = StyleSheet.create({
padding: 16,
alignItems: 'center',
},
+ modalTitle: {
+ fontSize: 18,
+ fontWeight: 'bold',
+ marginBottom: 10,
+ },
fullImage: {
width: '100%',
height: Dimensions.get('window').width,
borderRadius: 10,
marginBottom: 16,
},
- imageDescription: {
- color: Colors.white,
+ imageDescriptionInsideModal: {
+ color: 'black',
marginBottom: 16,
textAlign: 'center',
- fontSize: 12,
+ fontSize: 18,
},
- textDescription: {
- fontSize: 14,
- color: Colors.text,
- marginTop: 5,
- marginBottom: 5,
- paddingLeft: 10,
- paddingRight: 10,
+ userContainer: {
+ flexDirection: 'row',
+ alignItems: 'center',
+ marginBottom: 10,
+ },
+ userImage: {
+ width: 40,
+ height: 40,
+ borderRadius: 20,
+ marginRight: 10,
+ },
+ userName: {
+ fontSize: 16,
+ color: 'white',
+ },
+ closeButton: {
+ marginTop: 20,
+ padding: 12,
+ backgroundColor: Colors.primary,
+ borderRadius: 5,
+ },
+ closeButtonText: {
+ color: 'white',
+ fontWeight: 'bold',
+ },
+ commentContainer: {
+ flexDirection: 'row',
+ alignItems: 'center',
+ marginBottom: 10,
+ width: '100%',
+ marginTop: 10,
+ },
+ commentText: {
+ fontSize: 16,
+ color: 'black',
+ flexWrap: 'wrap',
+ flex: 1,
+ maxWidth: 200,
+ },
+ modalContainer: {
+ flex: 1,
+ justifyContent: 'center',
+ alignItems: 'center',
+ backgroundColor: 'rgba(0, 0, 0, 0.5)',
+ },
+ modalContent: {
+ width: '90%',
+ backgroundColor: 'white',
+ borderRadius: 10,
+ padding: 20,
+ alignItems: 'center',
+ maxWidth: '100%',
+ },
+ habitImage: {
+ width: '100%',
+ height: 300,
+ borderRadius: 10,
+ },
+ modalTitle: {
+ fontSize: 18,
+ fontWeight: 'bold',
+ marginBottom: 10,
+ },
+ userContainer: {
+ flexDirection: 'row',
+ alignItems: 'center',
+ marginBottom: 10,
+ },
+ userImage: {
+ width: 40,
+ height: 40,
+ borderRadius: 20,
+ marginRight: 10,
+ },
+ userName: {
+ fontSize: 16,
+ },
+ closeButton: {
+ marginTop: 20,
+ padding: 12,
+ backgroundColor: Colors.primary,
+ borderRadius: 5,
+ },
+ closeButtonText: {
+ color: 'white',
+ fontWeight: 'bold',
},
});
+
Account.propTypes = {
navigation: PropTypes.object,
setIsLoggedIn: PropTypes.func,
diff --git a/src/screens/UserProfile.js b/src/screens/UserProfile.js
index fbdb44c..e4bfc61 100644
--- a/src/screens/UserProfile.js
+++ b/src/screens/UserProfile.js
@@ -16,6 +16,7 @@ import Colors from '../../assets/styles/Colors';
import Header from '../components/Header';
import { supabase } from '../config/supabaseClient';
import { Button } from 'react-native-elements';
+import moment from 'moment';
const { width } = Dimensions.get('window');
const imageSize = width / 3;
@@ -25,12 +26,18 @@ const UserProfile = () => {
const [username, setUsername] = useState('');
const [bio, setBio] = useState('');
const [profileImage, setProfileImage] = useState(null);
- const [habitImages, setHabitImages] = useState([]);
+ const [habitPosts, setHabitPosts] = useState([]);
const [selectedImage, setSelectedImage] = useState(null);
const [modalVisible, setModalVisible] = useState(false);
const [followerCount, setFollowerCount] = useState(0);
const [followingCount, setFollowingCount] = useState(0);
const [postsCount, setPostsCount] = useState(0);
+ const [showLikesModal, setShowLikesModal] = useState(false);
+ const [likedUsers, setLikedUsers] = useState([]);
+ const [showCommentsModal, setShowCommentsModal] = useState(false);
+ const [comments, setComments] = useState([]);
+
+
const navigation = useNavigation();
const route = useRoute();
@@ -38,7 +45,7 @@ const UserProfile = () => {
useEffect(() => {
getProfile();
- getHabitImages();
+ getHabitPosts();
getFollowStats();
getPostsCount();
}, [userId]);
@@ -64,45 +71,188 @@ const UserProfile = () => {
}
};
- const getHabitImages = async () => {
+
+
+ const fetchComments = async (postId) => {
try {
- setLoading(true);
+ const { data: commentsData, error: commentsError } = await supabase
+ .from('Comments')
+ .select('content, user_id')
+ .eq('post_id', postId);
+
+ if (commentsError) throw commentsError;
+
+ const userIds = commentsData.map(comment => comment.user_id);
+
+ const { data: usersData, error: usersError } = await supabase
+ .from('User')
+ .select('user_id, username, profile_image')
+ .in('user_id', userIds);
+
+ if (usersError) throw usersError;
+
+ const commentsWithUserDetails = commentsData.map(comment => ({
+ ...comment,
+ user: usersData.find(user => user.user_id === comment.user_id),
+ }));
+
+ setComments(commentsWithUserDetails);
+ setShowCommentsModal(true);
+ } catch (error) {
+ Alert.alert('Error', 'Failed to fetch comments');
+ }
+ };
+
+
+
+ const getHabitPosts = async () => {
+ try {
+ setLoading(true);
+
const { data: scheduleData, error: scheduleError } = await supabase
.from('Schedule')
- .select('habit_id')
+ .select('habit_id, schedule_id')
.eq('user_id', userId);
-
+
if (scheduleError) throw scheduleError;
-
+
const habitIds = scheduleData.map(schedule => schedule.habit_id);
-
+ const scheduleIds = scheduleData.map(schedule => schedule.schedule_id);
+
+ if (habitIds.length === 0 || scheduleIds.length === 0) {
+ Alert.alert('Error', 'No habits or schedules found for the user');
+ setLoading(false);
+ return;
+ }
+
const { data: habitData, error: habitError } = await supabase
.from('Habit')
.select('habit_id, habit_title')
.in('habit_id', habitIds);
-
+
if (habitError) throw habitError;
-
- const { data: imagesData, error: imagesError } = await supabase
- .from('HabitImages')
- .select('*')
- .in('habit_id', habitIds);
-
- if (imagesError) throw imagesError;
-
- const imagesByHabit = habitData.reduce((acc, habit) => {
- acc[habit.habit_title] = imagesData.filter(image => image.habit_id === habit.habit_id);
+
+ const { data: postsData, error: postsError } = await supabase
+ .from('Post')
+ .select('post_id, post_description, schedule_id, created_at')
+ .in('schedule_id', scheduleIds);
+
+ if (postsError) throw postsError;
+
+ const postIds = postsData.map(post => post.post_id);
+
+ const { data: postImagesData, error: postImagesError } = await supabase
+ .from('Image')
+ .select('image_photo, post_id')
+ .in('post_id', postIds);
+
+ if (postImagesError) throw postImagesError;
+
+ const { data: likeData, error: likeError } = await supabase
+ .from('Like')
+ .select('post_id')
+ .in('post_id', postIds);
+
+ if (likeError) throw likeError;
+
+ const { data: commentsData, error: commentsError } = await supabase
+ .from('Comments')
+ .select('post_id')
+ .in('post_id', postIds);
+
+ if (commentsError) throw commentsError;
+
+ const likeCountMap = likeData.reduce((acc, like) => {
+ acc[like.post_id] = (acc[like.post_id] || 0) + 1;
return acc;
}, {});
-
- setHabitImages(imagesByHabit);
+
+ const commentCountMap = commentsData.reduce((acc, comment) => {
+ acc[comment.post_id] = (acc[comment.post_id] || 0) + 1;
+ return acc;
+ }, {});
+
+ const combinedPosts = habitData.map(habit => {
+ const habitPosts = postsData.filter(post =>
+ scheduleData.find(schedule => schedule.schedule_id === post.schedule_id)?.habit_id === habit.habit_id
+ );
+ const habitPostImages = postImagesData.filter(image =>
+ habitPosts.some(post => post.post_id === image.post_id)
+ );
+
+ const combined = [
+ ...habitPostImages.map(image => ({
+ ...image,
+ post_description: habitPosts.find(post => post.post_id === image.post_id)?.post_description,
+ created_at: habitPosts.find(post => post.post_id === image.post_id)?.created_at,
+ like_count: likeCountMap[image.post_id] || 0,
+ comment_count: commentCountMap[image.post_id] || 0,
+ }))
+ ].filter(image => image.image_photo !== null);
+
+ const postTexts = habitPosts
+ .filter(post => !postImagesData.some(image => image.post_id === post.post_id && image.image_photo))
+ .map(post => ({
+ post_description: post.post_description,
+ post_id: post.post_id,
+ created_at: post.created_at,
+ like_count: likeCountMap[post.post_id] || 0,
+ comment_count: commentCountMap[post.post_id] || 0,
+ }));
+
+ return {
+ habit_title: habit.habit_title,
+ images: combined,
+ postTexts: postTexts
+ };
+ });
+
+ setHabitPosts(combinedPosts);
} catch (error) {
- Alert.alert('Error fetching habit images', error.message);
+ Alert.alert('Error fetching habit posts', error.message);
} finally {
setLoading(false);
}
};
+
+
+
+
+
+ const fetchLikedUsers = async (postId) => {
+ try {
+ const { data: likeData, error: likeError } = await supabase
+ .from('Like')
+ .select('user_id')
+ .eq('post_id', postId);
+
+ if (likeError) throw likeError;
+
+ if (likeData.length === 0) {
+ setLikedUsers([]);
+ setShowLikesModal(true);
+ return;
+ }
+
+ const userIds = likeData.map(like => like.user_id);
+
+ const { data: usersData, error: usersError } = await supabase
+ .from('User')
+ .select('user_id, username, profile_image')
+ .in('user_id', userIds);
+
+ if (usersError) throw usersError;
+
+ setLikedUsers(usersData);
+ setShowLikesModal(true);
+ } catch (error) {
+ Alert.alert('Error', 'Failed to fetch users who liked the post');
+ }
+ };
+
+
+
const getFollowStats = async () => {
try {
@@ -152,6 +302,10 @@ const UserProfile = () => {
navigation.navigate('FollowScreen', { userId });
};
+ const goToCommentsScreen = (postId) => {
+ navigation.navigate('CommentsScreen', { postId });
+ };
+
const openModal = (image) => {
setSelectedImage(image);
setModalVisible(true);
@@ -162,21 +316,88 @@ const UserProfile = () => {
setSelectedImage(null);
};
- const renderHabitImages = ({ item }) => (
+
+ const fetchLikeCount = async (postId) => {
+ const { data: likeCountData, error: likeCountError } = await supabase
+ .from('Like')
+ .select('*', { count: 'exact' })
+ .eq('post_id', postId);
+
+ if (likeCountError) throw likeCountError;
+
+ return likeCountData.length;
+ };
+
+ const fetchLikeStatus = async (postId, userId) => {
+ const { data: likeData, error: likeError } = await supabase
+ .from('Like')
+ .select('*')
+ .eq('post_id', postId)
+ .eq('user_id', userId);
+
+ if (likeError) throw likeError;
+
+ return likeData.length > 0;
+ };
+
+
+
+
+ const renderHabitPosts = ({ item }) => (
{item.habit_title}
index.toString()}
- numColumns={3}
+ numColumns={1}
renderItem={({ item: image }) => (
- openModal(image)}>
-
-
+
+ openModal(image)}>
+
+
+
+ {image.post_description}
+ {moment(image.created_at).local().startOf('second').fromNow()}
+
+ fetchLikedUsers(image.post_id)}>
+ {image.like_count > 0 ? `${image.like_count} likes` : null}
+
+ fetchComments(image.post_id)}>
+
+ {image.comment_count > 0 ? image.comment_count : null}
+
+
+
+
)}
/>
+ {item.postTexts.length > 0 && (
+
+ {item.postTexts.map((post) => (
+
+ {post.post_description}
+ {moment(post.created_at).local().startOf('second').fromNow()}
+ fetchLikedUsers(post.post_id)}>
+ {post.like_count > 0 ? `${post.like_count} likes` : null}
+
+ fetchComments(post.post_id)}>
+
+ {post.comment_count > 0 ? post.comment_count : null}
+
+
+ ))}
+
+ )}
+
);
+
+
+
+
+
+
+
return (
@@ -211,35 +432,91 @@ const UserProfile = () => {
>
}
- data={Object.keys(habitImages).map(habitTitle => ({
- habit_title: habitTitle,
- images: habitImages[habitTitle],
- }))}
- renderItem={renderHabitImages}
+ data={habitPosts}
+ renderItem={renderHabitPosts}
keyExtractor={(item, index) => index.toString()}
/>
+ setShowLikesModal(false)}
+ >
+
+
+ Liked by
+ item.user_id.toString()}
+ renderItem={({ item }) => (
+
+
+ {item.username}
+
+ )}
+ />
+ setShowLikesModal(false)} style={styles.closeButton}>
+ Close
+
+
+
+
+
+
+
+
+ {selectedImage && (
+ <>
+
+ {selectedImage.post_description}
+
+ >
+ )}
+
+
+
+
setShowCommentsModal(false)}
>
- {selectedImage && (
- <>
-
- {selectedImage.description}
-
- >
- )}
+ Comments
+ index.toString()}
+ renderItem={({ item }) => (
+
+
+
+ {item.user.username}
+ {item.content}
+
+
+ )}
+ />
+ setShowCommentsModal(false)} style={styles.closeButton}>
+ Close
+
);
-};
+};
const styles = StyleSheet.create({
profileHeader: {
@@ -269,9 +546,14 @@ const styles = StyleSheet.create({
flexDirection: 'row',
justifyContent: 'space-around',
marginVertical: 20,
+ borderBottomWidth: 1,
+ borderBottomColor: 'rgba(105, 105, 120, 0.9)',
+ paddingBottom: 10,
+ marginBottom: 10,
},
stat: {
alignItems: 'center',
+ marginBottom: 10,
},
statCount: {
fontSize: 18,
@@ -283,7 +565,22 @@ const styles = StyleSheet.create({
color: Colors.text,
},
habitSection: {
+ // marginBottom: 40,
+ // marginTop: 10,
+ },
+ cardContainer: {
+ borderRadius: 10,
+ backgroundColor: Colors.cardBackground,
marginBottom: 20,
+ overflow: 'hidden',
+ padding: 10,
+ },
+ separator: {
+ height: 1,
+ backgroundColor: Colors.lightGray,
+ marginVertical: 20,
+ borderBottomWidth: 1,
+ borderBottomColor: 'rgba(105, 105, 120, 0.9)',
},
habitTitle: {
fontSize: 18,
@@ -297,6 +594,52 @@ const styles = StyleSheet.create({
height: imageSize - 10,
margin: 5,
},
+ textContainer: {
+ padding: 10,
+ },
+ textPostContainer: {
+ borderWidth: 1,
+ borderColor: Colors.lightGray,
+ borderRadius: 8,
+ padding: 10,
+ marginBottom: 10,
+ backgroundColor: 'rgba(0, 0, 0, 0.5)',
+ },
+ textUserName: {
+ fontWeight: 'bold',
+ color: Colors.text,
+ marginBottom: 5,
+ },
+ postText: {
+ color: Colors.text,
+ marginBottom: 10,
+ marginLeft: 10,
+ marginTop: 10,
+ },
+ textSubtitle: {
+ color: '#FFFFFF',
+ marginLeft: 10,
+ },
+ containerActions: {
+ flexDirection: 'row',
+ alignItems: 'center',
+ paddingVertical: 10,
+ paddingHorizontal: 10,
+ },
+ buttonActions: {
+ flexDirection: 'row',
+ alignItems: 'center',
+ marginRight: 20,
+ },
+ textPostActions: {
+ marginRight: 10,
+ color: Colors.text,
+ },
+ icon: {
+ width: 29,
+ height: 29,
+ margin: 10,
+ },
modalContainer: {
flex: 1,
justifyContent: 'center',
@@ -310,6 +653,11 @@ const styles = StyleSheet.create({
padding: 16,
alignItems: 'center',
},
+ modalTitle: {
+ fontSize: 18,
+ fontWeight: 'bold',
+ marginBottom: 10,
+ },
fullImage: {
width: '100%',
height: Dimensions.get('window').width,
@@ -319,7 +667,104 @@ const styles = StyleSheet.create({
imageDescription: {
color: Colors.white,
marginBottom: 16,
+ textAlign: 'center',
+ fontSize: 20,
+ },
+ imageDescriptionInsideModal: {
+ color: 'black',
+ marginBottom: 16,
+ textAlign: 'center',
+ fontSize: 18,
+ },
+ userContainer: {
+ flexDirection: 'row',
+ alignItems: 'center',
+ marginBottom: 10,
+ },
+ userImage: {
+ width: 40,
+ height: 40,
+ borderRadius: 20,
+ marginRight: 10,
+ },
+ userName: {
+ fontSize: 16,
+ color: 'white',
+
+ },
+ closeButton: {
+ marginTop: 20,
+ padding: 10,
+ backgroundColor: Colors.primary,
+ borderRadius: 5,
+ },
+ closeButtonText: {
+ color: 'white',
+ fontWeight: 'bold',
+ },
+ commentContainer: {
+ flexDirection: 'row',
+ alignItems: 'center',
+ marginBottom: 10,
+ width: '100%',
+ marginTop: 10,
+ },
+ commentText: {
+ fontSize: 16,
+ color: 'black',
+ flexWrap: 'wrap',
+ flex: 1,
+ maxWidth: 200,
+ },
+ modalContainer: {
+ flex: 1,
+ justifyContent: 'center',
+ alignItems: 'center',
+ backgroundColor: 'rgba(0, 0, 0, 0.5)',
+ },
+ modalContent: {
+ width: '90%',
+ backgroundColor: 'white',
+ borderRadius: 10,
+ padding: 20,
+ alignItems: 'center',
+ maxWidth: '100%',
+ },
+ habitImage: {
+ width: '100%',
+ height: 300,
+ borderRadius: 10,
+ },
+ modalTitle: {
+ fontSize: 18,
+ fontWeight: 'bold',
+ marginBottom: 10,
+ },
+ userContainer: {
+ flexDirection: 'row',
+ alignItems: 'center',
+ marginBottom: 10,
+ },
+ userImage: {
+ width: 40,
+ height: 40,
+ borderRadius: 20,
+ marginRight: 10,
+ },
+ userName: {
+ fontSize: 16,
+ },
+ closeButton: {
+ marginTop: 20,
+ padding: 10,
+ backgroundColor: Colors.primary,
+ borderRadius: 5,
+ },
+ closeButtonText: {
+ color: 'white',
+ fontWeight: 'bold',
},
});
+
export default UserProfile;
diff --git a/src/screens/checklist/Checklist.js b/src/screens/checklist/Checklist.js
index 5765635..1c3dcf1 100644
--- a/src/screens/checklist/Checklist.js
+++ b/src/screens/checklist/Checklist.js
@@ -58,8 +58,6 @@ const fetchSchedules = async () => {
.select('*')
.eq('user_id', session?.user.id);
- console.log('Fetched schedule data:', scheduleData);
-
if (scheduleError) {
throw scheduleError;
}
@@ -71,27 +69,30 @@ const fetchSchedules = async () => {
.select('*')
.in('habit_id', habitIds);
- console.log('Fetched habit data:', habitData);
-
if (habitError) {
throw habitError;
}
if (habitData) {
+ const currentDateTime = new Date();
const dayIndex = getDayIndex(moment(currentDay).format('dddd'));
const combinedData = scheduleData.map(schedule => {
const habit = habitData.find(h => h.habit_id === schedule.habit_id);
+ const endDate = new Date(schedule.schedule_end_date);
+ endDate.setHours(23, 59, 59, 999); // set end time to the end of the day
+ const isActive = currentDateTime <= endDate;
+ const isActiveToday = (schedule.schedule_active_days & (1 << dayIndex)) !== 0;
+
return {
...schedule,
habit_title: habit?.habit_title,
habit_description: habit?.habit_description,
habit_photo: habit?.habit_photo,
- is_active_today: (schedule.schedule_active_days & (1 << dayIndex)) !== 0,
+ is_active_today: isActive && isActiveToday,
};
}).filter(schedule => schedule.is_active_today);
-
+
setSchedules(combinedData);
- console.log('Combined data:', combinedData);
}
}
} catch (error) {
@@ -106,6 +107,8 @@ const fetchSchedules = async () => {
+
+
const addHabit = () => {
navigation.navigate('AddHabit');
};
@@ -165,7 +168,7 @@ const fetchSchedules = async () => {
{ color: item.schedule_state === 'Open' ? Colors.green : Colors.red },
]}
>
- {item.schedule_state === 'Open' ? 'ACTIVE' : 'INACTIVE'}
+ {/* {item.schedule_state === 'Open' ? 'ACTIVE' : 'INACTIVE'} */}
);
diff --git a/src/screens/habits/Habits.js b/src/screens/habits/Habits.js
index e484d90..1701ca5 100644
--- a/src/screens/habits/Habits.js
+++ b/src/screens/habits/Habits.js
@@ -59,15 +59,34 @@ const Habits = () => {
}
if (habitData) {
- const combinedData = scheduleData.map(schedule => {
+ const currentDateTime = new Date();
+ const combinedData = await Promise.all(scheduleData.map(async schedule => {
const habit = habitData.find(h => h.habit_id === schedule.habit_id);
+ const endDate = new Date(schedule.schedule_end_date);
+ endDate.setHours(23, 59, 59, 999); // set end time to the end of the day
+
+ const isActive = currentDateTime <= endDate;
+ const newState = isActive ? 'Open' : 'Closed';
+
+ if (schedule.schedule_state !== newState) {
+ // update the state in the database
+ await supabase
+ .from('Schedule')
+ .update({ schedule_state: newState })
+ .eq('schedule_id', schedule.schedule_id);
+ }
+
return {
...schedule,
habit_title: habit?.habit_title,
habit_description: habit?.habit_description,
- habit_id: schedule.habit_id,
+ schedule_state: newState,
};
- });
+ }));
+
+ // sort combinedData to move inactive habits to the bottom
+ combinedData.sort((a, b) => (a.schedule_state === 'Closed' ? 1 : -1));
+
setSchedules(combinedData);
console.log('Combined data:', combinedData);
}
diff --git a/src/screens/habits/ViewHabit.js b/src/screens/habits/ViewHabit.js
index 9f12214..c3d855a 100644
--- a/src/screens/habits/ViewHabit.js
+++ b/src/screens/habits/ViewHabit.js
@@ -64,6 +64,8 @@ const ViewHabit = () => {
const [editedHabit, setEditedHabit] = useState(habit);
const [deleteModalVisible, setDeleteModalVisible] = useState(false);
const [activeDays, setActiveDays] = useState(habit.schedule_active_days);
+ const [textPosts, setTextPosts] = useState([]);
+
useEffect(() => {
const updatedEndDate = new Date(habit.schedule_end_date);
@@ -193,8 +195,14 @@ const ViewHabit = () => {
post_description: postsData.find(post => post.post_id === image.post_id)?.post_description
}))
].filter(image => image.image_photo !== null);
+
+ const textOnlyPosts = postsData.filter(post => !postImagesData.some(image => image.post_id === post.post_id));
+
+
// console.log("Setting habit images with: " + JSON.stringify(combinedImages));
setHabitImages(combinedImages);
+ setTextPosts(textOnlyPosts);
+
// console.log("Done setting habit images");
};
@@ -222,30 +230,95 @@ const ViewHabit = () => {
const deletePhoto = async (image) => {
try {
- const { error: deleteError } = await supabase
- .from('HabitImages')
- .delete()
- .eq('habit_image_id', image.habit_image_id);
-
- if (deleteError) {
- throw deleteError;
+ console.log("Image object:", image);
+
+ if (!image || !image.image_photo) {
+ throw new Error("Image or image photo is undefined.");
}
-
+
+ // determine the source table and ID
+ let sourceTable = null;
+ let sourceId = null;
+ let sourceField = null;
+
+ if (image.habit_image_id) {
+ sourceTable = 'HabitImages';
+ sourceId = image.habit_image_id;
+ sourceField = 'habit_image_id';
+ } else if (image.post_id) {
+ sourceTable = 'Post';
+ sourceId = image.post_id; // using post_id for deletion from Post table
+ sourceField = 'post_id';
+ } else if (image.id === 'habitPhoto') {
+ sourceTable = 'Habit';
+ sourceId = habit.habit_id;
+ sourceField = 'habit_id';
+ }
+
+ if (!sourceTable || !sourceId) {
+ throw new Error("Image source or ID is undefined.");
+ }
+
+ // if the image belongs to a post, delete the corresponding entry from the Post and Image tables
+ if (sourceTable === 'Post') {
+ const { error: deletePostError } = await supabase
+ .from('Post')
+ .delete()
+ .eq('post_id', sourceId);
+
+ if (deletePostError) {
+ throw deletePostError;
+ }
+
+ const { error: deleteImageError } = await supabase
+ .from('Image')
+ .delete()
+ .eq('post_id', sourceId);
+
+ if (deleteImageError) {
+ throw deleteImageError;
+ }
+ } else if (sourceTable === 'HabitImages') {
+ // if the image belongs to the HabitImages table, delete the entry
+ const { error: deleteError } = await supabase
+ .from(sourceTable)
+ .delete()
+ .eq(sourceField, sourceId);
+
+ if (deleteError) {
+ throw deleteError;
+ }
+ } else if (sourceTable === 'Habit') {
+ // for the Habit table just update the habit_photo to null
+ const { error: updateError } = await supabase
+ .from('Habit')
+ .update({ habit_photo: null })
+ .eq('habit_id', sourceId);
+
+ if (updateError) {
+ throw updateError;
+ }
+ }
+
+ // remove the image from storage
+ const imageName = image.image_photo.split('/').pop();
const { error: bucketError } = await supabase.storage
.from('habit')
- .remove([image.image_photo.split('/').pop()]);
-
+ .remove([imageName]);
+
if (bucketError) {
throw bucketError;
}
-
- setHabitImages(habitImages.filter(img => img.habit_image_id !== image.habit_image_id));
+
+ // update the state to remove the deleted image
+ setHabitImages(habitImages.filter(img => img.image_photo !== image.image_photo));
Alert.alert('Success', 'Photo has been successfully deleted');
closeModal();
} catch (error) {
Alert.alert('Error', error.message);
}
};
+
@@ -540,6 +613,27 @@ const ViewHabit = () => {
}
};
+
+ const deleteTextPost = async (post_id) => {
+ try {
+ const { error } = await supabase
+ .from('Post')
+ .delete()
+ .eq('post_id', post_id);
+
+ if (error) {
+ throw error;
+ }
+
+ // remove deleted post from the state
+ setTextPosts(textPosts.filter(post => post.post_id !== post_id));
+ Alert.alert('Success', 'Post has been successfully deleted');
+ } catch (error) {
+ Alert.alert('Error', error.message);
+ }
+ };
+
+
const customStyles = {
stepIndicatorSize: 25,
currentStepIndicatorSize: 30,
@@ -575,9 +669,11 @@ const ViewHabit = () => {
);
const openModal = (image) => {
+ console.log("Opening modal with image:", image);
setSelectedImage(image);
setModalVisible(true);
};
+
const closeModal = () => {
setModalVisible(false);
@@ -705,6 +801,21 @@ const ViewHabit = () => {
numColumns={3}
contentContainerStyle={styles.scrollContainer}
/>
+ (
+
+ {item.post_description}
+ deleteTextPost(item.post_id)} style={styles.deleteButton}>
+ Delete Post
+
+
+ )}
+ keyExtractor={(item) => item.post_id}
+/>
+
+
+
{
{selectedImage.post_description}
)}