Skip to content

Commit e61b522

Browse files
authored
Merge pull request #6 from KushK04/socialfeedv2
Social feed working
2 parents 4493370 + 2c5182f commit e61b522

File tree

5 files changed

+182
-40
lines changed

5 files changed

+182
-40
lines changed

KonditionExpo/app/create-post.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -276,14 +276,14 @@ export default function CreatePostScreen() {
276276
)}
277277
<View style={styles.previewStats}>
278278
<Text style={styles.previewStat}>
279-
🏃‍♂️ {formData.workout_type}
279+
<Text>🏃‍♂️ {formData.workout_type}</Text>
280280
</Text>
281281
<Text style={styles.previewStat}>
282-
⏱️ {formatDuration(formData.duration_minutes)}
282+
<Text>⏱️ {formatDuration(formData.duration_minutes)}</Text>
283283
</Text>
284284
{formData.calories_burned && (
285285
<Text style={styles.previewStat}>
286-
🔥 {formData.calories_burned} cal
286+
<Text>🔥 {formData.calories_burned} cal</Text>
287287
</Text>
288288
)}
289289
</View>

KonditionExpo/components/feed/PostCard.tsx

Lines changed: 127 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import React from 'react';
2-
import { View, Text, StyleSheet, TouchableOpacity, Alert } from 'react-native';
1+
import React, { useState } from 'react';
2+
import { View, Text, StyleSheet, TouchableOpacity, Alert, ActivityIndicator, Platform } from 'react-native';
33
import { WorkoutPostResponse } from '../../services/api';
44
import { useFeed } from '../../contexts/FeedContext';
55
import { useAuth } from '../../contexts/AuthContext';
@@ -13,9 +13,24 @@ interface PostCardProps {
1313
export function PostCard({ post, onPress }: PostCardProps) {
1414
const { deletePost } = useFeed();
1515
const { user } = useAuth();
16+
const [isDeleting, setIsDeleting] = useState(false);
1617

1718
const isOwnPost = user?.id === post.user_id;
1819

20+
// Enhanced debug logging to help identify the issue
21+
console.log('PostCard Debug:', {
22+
currentUserId: user?.id,
23+
currentUserIdType: typeof user?.id,
24+
postUserId: post.user_id,
25+
postUserIdType: typeof post.user_id,
26+
isOwnPost,
27+
userFullName: user?.full_name,
28+
postUserName: post.user_full_name,
29+
postTitle: post.title,
30+
strictEquality: user?.id === post.user_id,
31+
looseEquality: user?.id == post.user_id
32+
});
33+
1934
const formatDuration = (minutes: number): string => {
2035
if (minutes < 60) {
2136
return `${minutes}m`;
@@ -41,25 +56,84 @@ export function PostCard({ post, onPress }: PostCardProps) {
4156
return date.toLocaleDateString();
4257
};
4358

44-
const handleDelete = () => {
45-
Alert.alert(
46-
'Delete Post',
47-
'Are you sure you want to delete this post?',
48-
[
49-
{ text: 'Cancel', style: 'cancel' },
50-
{
51-
text: 'Delete',
52-
style: 'destructive',
53-
onPress: async () => {
54-
try {
55-
await deletePost(post.id);
56-
} catch (error) {
57-
Alert.alert('Error', 'Failed to delete post');
58-
}
59-
},
60-
},
61-
]
62-
);
59+
const handleDelete = async () => {
60+
console.log('Delete button pressed for post:', post.id, 'by user:', user?.id);
61+
62+
if (isDeleting) {
63+
console.log('Delete already in progress, ignoring click');
64+
return; // Prevent multiple delete attempts
65+
}
66+
67+
console.log('Showing delete confirmation dialog');
68+
69+
// Use web-compatible confirmation for web platform
70+
if (Platform.OS === 'web') {
71+
const confirmed = window.confirm('Are you sure you want to delete this post?');
72+
if (!confirmed) {
73+
console.log('User cancelled delete');
74+
return;
75+
}
76+
} else {
77+
// Use Alert.alert for native platforms
78+
return new Promise((resolve) => {
79+
Alert.alert(
80+
'Delete Post',
81+
'Are you sure you want to delete this post?',
82+
[
83+
{
84+
text: 'Cancel',
85+
style: 'cancel',
86+
onPress: () => {
87+
console.log('User cancelled delete');
88+
resolve(false);
89+
}
90+
},
91+
{
92+
text: 'Delete',
93+
style: 'destructive',
94+
onPress: () => {
95+
console.log('User confirmed delete via Alert');
96+
resolve(true);
97+
},
98+
},
99+
]
100+
);
101+
}).then((confirmed) => {
102+
if (!confirmed) return;
103+
return performDelete();
104+
});
105+
}
106+
107+
// For web, proceed directly after confirmation
108+
console.log('User confirmed delete, starting deletion process...');
109+
await performDelete();
110+
};
111+
112+
const performDelete = async () => {
113+
try {
114+
setIsDeleting(true);
115+
console.log('Calling deletePost with ID:', post.id);
116+
await deletePost(post.id);
117+
console.log('Delete post successful');
118+
// Success feedback is handled by optimistic update in FeedContext
119+
} catch (error) {
120+
console.error('Delete post error:', error);
121+
const errorMessage = error instanceof Error ? error.message : 'Failed to delete post';
122+
console.log('Showing error alert:', errorMessage);
123+
124+
if (Platform.OS === 'web') {
125+
window.alert(`Delete Failed: ${errorMessage}`);
126+
} else {
127+
Alert.alert(
128+
'Delete Failed',
129+
errorMessage,
130+
[{ text: 'OK', style: 'default' }]
131+
);
132+
}
133+
} finally {
134+
console.log('Delete process completed, resetting isDeleting state');
135+
setIsDeleting(false);
136+
}
63137
};
64138

65139
return (
@@ -87,9 +161,20 @@ export function PostCard({ post, onPress }: PostCardProps) {
87161
</View>
88162
</View>
89163
</View>
164+
{/* Only show delete button for user's own posts */}
90165
{isOwnPost && (
91-
<TouchableOpacity onPress={handleDelete} style={styles.deleteButton}>
92-
<IconSymbol name="trash" size={16} color="#FF3B30" />
166+
<TouchableOpacity
167+
onPress={handleDelete}
168+
style={[styles.deleteButton, isDeleting && styles.deleteButtonDisabled]}
169+
disabled={isDeleting}
170+
activeOpacity={0.8}
171+
hitSlop={{ top: 10, bottom: 10, left: 10, right: 10 }}
172+
>
173+
{isDeleting ? (
174+
<ActivityIndicator size="small" color="#FFFFFF" />
175+
) : (
176+
<IconSymbol name="trash" size={18} color="#FFFFFF" />
177+
)}
93178
</TouchableOpacity>
94179
)}
95180
</View>
@@ -206,7 +291,25 @@ const styles = StyleSheet.create({
206291
marginLeft: 4,
207292
},
208293
deleteButton: {
209-
padding: 4,
294+
padding: 10,
295+
borderRadius: 8,
296+
backgroundColor: '#FF3B30',
297+
borderWidth: 2,
298+
borderColor: '#FF1F1F',
299+
minWidth: 40,
300+
minHeight: 40,
301+
justifyContent: 'center',
302+
alignItems: 'center',
303+
shadowColor: '#FF3B30',
304+
shadowOffset: { width: 0, height: 2 },
305+
shadowOpacity: 0.3,
306+
shadowRadius: 4,
307+
elevation: 4,
308+
},
309+
deleteButtonDisabled: {
310+
opacity: 0.6,
311+
backgroundColor: '#FF8A80',
312+
borderColor: '#FF8A80',
210313
},
211314
content: {
212315
marginBottom: 12,

KonditionExpo/contexts/FeedContext.tsx

Lines changed: 29 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -167,25 +167,44 @@ export function FeedProvider({ children }: FeedProviderProps) {
167167
}, []);
168168

169169
const deletePost = useCallback(async (postId: string) => {
170+
// Store the post data for potential restoration
171+
let deletedPost: WorkoutPostResponse | null = null;
172+
let previousState: FeedState | null = null;
173+
170174
try {
171-
setLoading(true);
172175
setError(null);
173176

177+
// Store current state and find the post to delete
178+
setState(prev => {
179+
previousState = { ...prev };
180+
deletedPost = prev.personalFeed.find(post => post.id === postId) ||
181+
prev.publicFeed.find(post => post.id === postId) ||
182+
prev.combinedFeed.find(post => post.id === postId) ||
183+
null;
184+
185+
// Optimistic update: Remove the post immediately from all feeds
186+
return {
187+
...prev,
188+
personalFeed: prev.personalFeed.filter(post => post.id !== postId),
189+
publicFeed: prev.publicFeed.filter(post => post.id !== postId),
190+
combinedFeed: prev.combinedFeed.filter(post => post.id !== postId),
191+
};
192+
});
193+
194+
// Attempt to delete the post on the server
174195
await apiService.deleteWorkoutPost(postId);
175196

176-
// Remove the post from all feeds
177-
setState(prev => ({
178-
...prev,
179-
personalFeed: prev.personalFeed.filter(post => post.id !== postId),
180-
publicFeed: prev.publicFeed.filter(post => post.id !== postId),
181-
combinedFeed: prev.combinedFeed.filter(post => post.id !== postId),
182-
}));
197+
// Success - the optimistic update stands
183198
} catch (error) {
184199
console.error('Error deleting post:', error);
200+
201+
// Restore the post to its previous position if deletion failed
202+
if (previousState && deletedPost) {
203+
setState(previousState);
204+
}
205+
185206
setError(error instanceof Error ? error.message : 'Failed to delete post');
186207
throw error;
187-
} finally {
188-
setLoading(false);
189208
}
190209
}, []);
191210

backend/app/crud/__init__.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,17 @@
1010
delete_workout_post,
1111
follow_user,
1212
get_feed_posts,
13+
get_personal_feed_posts,
14+
get_public_feed_posts,
15+
get_combined_feed_posts,
1316
get_follower_count,
1417
get_followers,
1518
get_following,
1619
get_following_count,
1720
get_user_workout_posts,
1821
get_workout_post,
1922
is_following,
23+
is_mutual_follow,
2024
search_users,
2125
unfollow_user,
2226
update_workout_post,
@@ -61,6 +65,10 @@
6165
"get_workout_post",
6266
"get_user_workout_posts",
6367
"get_feed_posts",
68+
"get_personal_feed_posts",
69+
"get_public_feed_posts",
70+
"get_combined_feed_posts",
71+
"is_mutual_follow",
6472
"update_workout_post",
6573
"delete_workout_post",
6674
]

backend/app/crud/social.py

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,11 @@ def get_personal_feed_posts(
225225
Get workout posts from users that the specified user follows (personal feed).
226226
Includes privacy filtering: private posts only visible if mutual follow.
227227
"""
228+
# Early check: if no WorkoutPost records exist, return empty result immediately
229+
total_posts_check = session.exec(select(func.count()).select_from(WorkoutPost)).one()
230+
if total_posts_check == 0:
231+
return [], 0
232+
228233
# Get IDs of users that the current user follows
229234
following_statement = (
230235
select(UserFollow.followed_id)
@@ -235,16 +240,18 @@ def get_personal_feed_posts(
235240
# Include the user's own posts in the feed
236241
following_ids.append(user_id)
237242

243+
# If no one to follow (including self), return empty
244+
if not following_ids:
245+
return [], 0
246+
238247
# Build privacy filter conditions
239248
privacy_conditions = []
240249

241250
for followed_id in following_ids:
242251
if followed_id == user_id:
243252
# User's own posts - always visible
244253
privacy_conditions.append(
245-
and_(
246-
WorkoutPost.user_id == user_id
247-
)
254+
WorkoutPost.user_id == user_id
248255
)
249256
else:
250257
# Posts from followed users
@@ -297,6 +304,11 @@ def get_public_feed_posts(
297304
"""
298305
Get all public workout posts from all users (discovery feed).
299306
"""
307+
# Early check: if no WorkoutPost records exist, return empty result immediately
308+
total_posts_check = session.exec(select(func.count()).select_from(WorkoutPost)).one()
309+
if total_posts_check == 0:
310+
return [], 0
311+
300312
# Count all public posts
301313
count_statement = (
302314
select(func.count())

0 commit comments

Comments
 (0)