Skip to content

Commit e9c57ae

Browse files
committed
fix: refactored community page
1 parent e5cb163 commit e9c57ae

File tree

8 files changed

+245
-122
lines changed

8 files changed

+245
-122
lines changed

src/app/api/posts/[id]/comments/route.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@ export async function GET(
1111

1212
// Remove orderBy to avoid composite index requirement
1313
const commentsRef = db.collection('comments')
14-
.where('postId', '==', id);
14+
.where('postId', '==', id)
15+
.orderBy('createdAt', 'asc'); // Sorting by createdAt
1516

1617
const snapshot = await commentsRef.get();
1718
const comments = await Promise.all(
@@ -50,7 +51,7 @@ export async function GET(
5051
comments.sort((a, b) => {
5152
const aTime = a.createdAt.getTime();
5253
const bTime = b.createdAt.getTime();
53-
return aTime - bTime;
54+
return bTime - aTime; // Changed from aTime - bTime to bTime - aTime for newest first
5455
});
5556

5657
return NextResponse.json(comments);

src/app/globals.css

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,36 @@
7272
}
7373
}
7474

75+
@keyframes slideInTop {
76+
0% {
77+
opacity: 0;
78+
transform: translateY(-20px) scale(0.95);
79+
}
80+
50% {
81+
opacity: 0.8;
82+
transform: translateY(-5px) scale(1.02);
83+
}
84+
100% {
85+
opacity: 1;
86+
transform: translateY(0) scale(1);
87+
}
88+
}
89+
90+
@keyframes highlightNew {
91+
0% {
92+
background-color: rgba(99, 160, 136, 0.1);
93+
box-shadow: 0 0 20px rgba(99, 160, 136, 0.2);
94+
}
95+
50% {
96+
background-color: rgba(99, 160, 136, 0.15);
97+
box-shadow: 0 0 25px rgba(99, 160, 136, 0.3);
98+
}
99+
100% {
100+
background-color: transparent;
101+
box-shadow: none;
102+
}
103+
}
104+
75105
/* Hide scrollbar but maintain functionality */
76106
.hide-scrollbar {
77107
-ms-overflow-style: none; /* IE and Edge */

src/app/posts/[id]/page.tsx

Lines changed: 80 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ export default function PostPage() {
3535
const [showCommentProductSearch, setShowCommentProductSearch] = useState(false);
3636
const [isRealTimeActive, setIsRealTimeActive] = useState(false);
3737
const [isOnline, setIsOnline] = useState(true);
38+
const [newlyAddedComments, setNewlyAddedComments] = useState<Set<string>>(new Set());
3839

3940
useEffect(() => {
4041
const unsubscribe = onAuthStateChanged(auth, (user) => {
@@ -161,17 +162,36 @@ export default function PostPage() {
161162
})
162163
);
163164

164-
// Sort comments by createdAt in JavaScript
165+
// Sort comments by createdAt in JavaScript (newest first)
165166
commentsData.sort((a, b) => {
166167
const aTime = new Date(a.createdAt).getTime();
167168
const bTime = new Date(b.createdAt).getTime();
168-
return aTime - bTime;
169+
return bTime - aTime; // Changed from aTime - bTime to bTime - aTime for newest first
169170
});
170171

171172
// Filter out optimistic updates and merge with real data
172173
setComments(prevComments => {
173174
const tempComments = prevComments.filter(c => c.id.startsWith('temp-'));
174175
const realComments = commentsData;
176+
177+
// Detect new comments for animation
178+
const prevCommentIds = new Set(prevComments.map(c => c.id));
179+
const newCommentIds = realComments
180+
.filter(c => !prevCommentIds.has(c.id) && !c.id.startsWith('temp-'))
181+
.map(c => c.id);
182+
183+
if (newCommentIds.length > 0) {
184+
setNewlyAddedComments(prev => new Set([...prev, ...newCommentIds]));
185+
// Remove animation class after animation duration
186+
setTimeout(() => {
187+
setNewlyAddedComments(prev => {
188+
const updated = new Set(prev);
189+
newCommentIds.forEach(id => updated.delete(id));
190+
return updated;
191+
});
192+
}, 1000); // Remove animation after 1 second
193+
}
194+
175195
return [...realComments, ...tempComments];
176196
});
177197
setLoadingComments(false);
@@ -411,27 +431,31 @@ export default function PostPage() {
411431
{user ? (
412432
<form onSubmit={handleSubmitComment} className="mb-4 sm:mb-6">
413433
<div className="flex gap-3">
414-
<div className="flex-shrink-0">
415-
<div className="w-10 h-10 rounded-full flex items-center justify-center" style={{ backgroundColor: colours.tag.default.background }}>
416-
<span className="font-medium text-sm" style={{ color: colours.tag.default.text }}>
417-
{(user.displayName || user.email || '').charAt(0).toUpperCase()}
434+
<div className="flex-1">
435+
<div className="relative">
436+
<textarea
437+
value={newComment}
438+
onChange={(e) => setNewComment(e.target.value)}
439+
className="w-full px-3 py-2 pr-20 shadow-lg rounded-lg resize-none border-2 border-black"
440+
style={{
441+
backgroundColor: colours.input.background,
442+
color: colours.input.text
443+
}}
444+
rows={3}
445+
placeholder="Share your thoughts..."
446+
maxLength={500}
447+
/>
448+
{/* Character count inside textarea */}
449+
<span
450+
className="absolute bottom-3 right-2 text-xs pointer-events-none px-2 py-1 rounded"
451+
style={{
452+
color: colours.text.muted,
453+
backgroundColor: colours.input.background,
454+
}}
455+
>
456+
{newComment.length}/500
418457
</span>
419458
</div>
420-
</div>
421-
<div className="flex-1">
422-
<textarea
423-
value={newComment}
424-
onChange={(e) => setNewComment(e.target.value)}
425-
className="w-full px-3 py-2 rounded-lg resize-none"
426-
style={{
427-
border: `1px solid ${colours.card.border}`,
428-
backgroundColor: colours.input.background,
429-
color: colours.input.text
430-
}}
431-
rows={3}
432-
placeholder="Share your thoughts..."
433-
maxLength={500}
434-
/>
435459

436460
{/* Product Linking for Comments */}
437461
<div className="mt-2">
@@ -475,7 +499,7 @@ export default function PostPage() {
475499
)}
476500

477501
{showCommentProductSearch && !selectedCommentProduct && (
478-
<div className="relative mb-3">
502+
<div className="relative mb-2">
479503
<div className="relative">
480504
<Search className="absolute left-3 top-1/2 transform -translate-y-1/2" size={16} style={{ color: colours.text.muted }} />
481505
<input
@@ -537,31 +561,16 @@ export default function PostPage() {
537561
)}
538562
</div>
539563

540-
<div className="flex justify-between items-center mt-3">
541-
<div className="flex items-center gap-3">
542-
<span className="text-xs px-2 py-1 rounded-full" style={{
543-
color: colours.text.muted,
544-
backgroundColor: colours.background.secondary
545-
}}>
546-
{newComment.length}/500
547-
</span>
564+
<div className="flex justify-between items-center mt-2">
565+
<div className="flex items-center">
548566
{!selectedCommentProduct && (
549567
<button
550568
type="button"
551569
onClick={() => setShowCommentProductSearch(!showCommentProductSearch)}
552-
className={`flex items-center gap-2 px-3 py-2 rounded-lg text-xs font-medium transition-all ${
553-
showCommentProductSearch ? 'shadow-sm' : ''
554-
}`}
570+
className="flex items-center gap-2 px-3 py-2 shadow-xl rounded-xl border-2 border-black text-xs font-medium transition-all"
555571
style={{
556-
backgroundColor: showCommentProductSearch
557-
? colours.button.primary.background
558-
: colours.tag.default.background,
559-
color: showCommentProductSearch
560-
? colours.button.primary.text
561-
: colours.text.secondary,
562-
border: `1px solid ${showCommentProductSearch
563-
? colours.button.primary.background
564-
: colours.tag.default.border}`
572+
backgroundColor: '#f1f5fb',
573+
color: colours.text.primary
565574
}}
566575
>
567576
<Package size={14} />
@@ -572,21 +581,21 @@ export default function PostPage() {
572581
<button
573582
type="submit"
574583
disabled={!newComment.trim() || submittingComment}
575-
className="flex items-center justify-center w-10 h-10 rounded-full disabled:opacity-50 disabled:cursor-not-allowed transition-all"
584+
className="flex items-center gap-2 px-4 py-2 rounded-xl border-2 shadow-xl border-black text-xs font-medium disabled:opacity-50 disabled:cursor-not-allowed disabled:border-dotted transition-all"
576585
style={{
577-
backgroundColor: colours.button.primary.background,
578-
color: colours.button.primary.text
586+
backgroundColor: '#f1f5fb',
587+
color: colours.text.primary
579588
}}
580-
title={submittingComment ? 'Posting...' : 'Post Comment'}
581589
>
582-
<Send size={16} />
590+
<Send size={14} />
591+
<span>{submittingComment ? 'Posting...' : 'Post Comment'}</span>
583592
</button>
584593
</div>
585594
</div>
586595
</div>
587596
</form>
588597
) : (
589-
<div className="mb-6 p-4 rounded-lg text-center" style={{ backgroundColor: colours.background.secondary }}>
598+
<div className="mb- p-4 rounded-lg text-center" style={{ backgroundColor: colours.background.secondary }}>
590599
<p className="mb-2" style={{ color: colours.text.secondary }}>Sign in to join the conversation</p>
591600
<Link
592601
href="/auth"
@@ -606,20 +615,34 @@ export default function PostPage() {
606615
</div>
607616
) : comments.length === 0 ? (
608617
<div className="text-center py-8">
609-
<MessageCircle size={48} className="mx-auto mb-3" style={{ color: colours.text.muted }} />
618+
<MessageCircle size={48} className="mx-auto mb-2" style={{ color: colours.text.muted }} />
610619
<p style={{ color: colours.text.secondary }}>No comments yet. Be the first to share your thoughts!</p>
611620
</div>
612621
) : (
613-
<div className="space-y-4">
622+
<div className="space-y-1">
614623
{comments.map((comment) => (
615-
<CommentItem
624+
<div
616625
key={comment.id}
617-
comment={comment}
618-
currentUserId={user?.uid}
619-
onLike={handleCommentLike}
620-
onDislike={handleCommentDislike}
621-
depth={0}
622-
/>
626+
className={`transition-all duration-1000 ease-out ${
627+
newlyAddedComments.has(comment.id)
628+
? 'animate-slide-in-top opacity-100 transform translate-y-0'
629+
: 'opacity-100 transform translate-y-0'
630+
}`}
631+
style={{
632+
animation: newlyAddedComments.has(comment.id)
633+
? 'slideInTop 0.8s ease-out, highlightNew 2s ease-out'
634+
: undefined,
635+
borderRadius: '0.75rem'
636+
}}
637+
>
638+
<CommentItem
639+
comment={comment}
640+
currentUserId={user?.uid}
641+
onLike={handleCommentLike}
642+
onDislike={handleCommentDislike}
643+
depth={0}
644+
/>
645+
</div>
623646
))}
624647
</div>
625648
)}

src/components/CommentItem.tsx

Lines changed: 27 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
"use client";
22

3-
import { ThumbsUp, ThumbsDown, Reply, Clock, ExternalLink } from 'lucide-react';
3+
import { Reply, Clock, ExternalLink } from 'lucide-react';
44
import Image from 'next/image';
55
import { Comment } from '@/types/post';
66
import { formatDate } from '@/utils/dateUtils';
77
import { colours } from '@/styles/colours';
8+
import LikeButton from './LikeButton';
9+
import DislikeButton from './DislikeButton';
810

911
interface CommentItemProps {
1012
comment: Comment;
@@ -50,21 +52,21 @@ export default function CommentItem({
5052

5153
return (
5254
<div
53-
className="pl-4 pb-4"
55+
className={`pb-4 ${depth > 0 ? 'pl-4' : ''}`}
5456
style={{
55-
marginLeft: `${marginLeft}px`,
56-
borderLeft: `2px solid ${colours.card.border}`
57+
marginLeft: `${marginLeft}px`
5758
}}
5859
>
59-
<div className="rounded-lg p-4" style={{ backgroundColor: colours.background.secondary }}>
60+
<div
61+
className="rounded-xl shadow-lg p-4"
62+
style={{
63+
backgroundColor: colours.tags.countries.background,
64+
border: `2px solid ${colours.card.border}`,
65+
}}
66+
>
6067
{/* Header */}
6168
<div className="flex items-center justify-between mb-2">
6269
<div className="flex items-center gap-2">
63-
<div className="w-8 h-8 rounded-full flex items-center justify-center" style={{ backgroundColor: colours.tag.default.background }}>
64-
<span className="font-medium text-sm" style={{ color: colours.tag.default.text }}>
65-
{comment.authorName.charAt(0).toUpperCase()}
66-
</span>
67-
</div>
6870
<div>
6971
<span className="font-medium text-sm" style={{ color: colours.text.primary }}>
7072
{comment.authorName}
@@ -116,42 +118,32 @@ export default function CommentItem({
116118
{/* Actions */}
117119
<div className="flex items-center gap-4">
118120
{/* Like Button */}
119-
<button
120-
onClick={handleLike}
121+
<LikeButton
122+
isLiked={hasLiked}
123+
likeCount={likeCount}
124+
onLike={handleLike}
121125
disabled={!currentUserId}
122-
className={`flex items-center gap-1 px-2 py-1 rounded-full text-xs transition-colors ${!currentUserId ? 'cursor-not-allowed opacity-50' : ''}`}
123-
style={{
124-
backgroundColor: hasLiked ? colours.status.success.background : colours.interactive.hover.background,
125-
color: hasLiked ? colours.status.success.text : colours.text.secondary
126-
}}
127-
>
128-
<ThumbsUp size={12} />
129-
<span>{likeCount}</span>
130-
</button>
126+
size="sm"
127+
/>
131128

132129
{/* Dislike Button */}
133-
<button
134-
onClick={handleDislike}
130+
<DislikeButton
131+
isDisliked={hasDisliked}
132+
dislikeCount={dislikeCount}
133+
onDislike={handleDislike}
135134
disabled={!currentUserId}
136-
className={`flex items-center gap-1 px-2 py-1 rounded-full text-xs transition-colors ${!currentUserId ? 'cursor-not-allowed opacity-50' : ''}`}
137-
style={{
138-
backgroundColor: hasDisliked ? colours.status.error.background : colours.interactive.hover.background,
139-
color: hasDisliked ? colours.status.error.text : colours.text.secondary
140-
}}
141-
>
142-
<ThumbsDown size={12} />
143-
<span>{dislikeCount}</span>
144-
</button>
135+
size="sm"
136+
/>
145137

146138
{/* Reply Button */}
147139
{onReply && depth < maxDepth && (
148140
<button
149141
onClick={handleReply}
150142
disabled={!currentUserId}
151-
className={`flex items-center gap-1 px-2 py-1 rounded-full text-xs transition-colors ${!currentUserId ? 'cursor-not-allowed opacity-50' : ''}`}
143+
className={`flex items-center gap-1 px-2 py-1 rounded-full border-2 border-black text-xs transition-colors ${!currentUserId ? 'cursor-not-allowed opacity-50' : ''}`}
152144
style={{
153-
backgroundColor: colours.interactive.hover.background,
154-
color: colours.text.secondary
145+
backgroundColor: '#f1f5fb',
146+
color: colours.text.primary
155147
}}
156148
>
157149
<Reply size={12} />

src/components/CreatePostModal.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,7 @@ export default function CreatePostModal({ isOpen, onClose, onSubmit, isLoading }
192192
<button
193193
type="button"
194194
onClick={() => setShowProductSearch(!showProductSearch)}
195-
className={`flex items-center gap-2 px-3 py-2 rounded-lg text-xs font-medium transition-all ${
195+
className={`flex items-center gap-2 px-3 py-2 shadow-xl rounded-xl text-xs font-medium transition-all ${
196196
showProductSearch ? 'shadow-sm' : ''
197197
}`}
198198
style={{

0 commit comments

Comments
 (0)