diff --git a/src/components/metadata/CommentsSection.tsx b/src/components/metadata/CommentsSection.tsx
index c6e2fd64..8f4de8bf 100644
--- a/src/components/metadata/CommentsSection.tsx
+++ b/src/components/metadata/CommentsSection.tsx
@@ -9,6 +9,7 @@ import {
Dimensions,
Alert,
ScrollView,
+ Animated,
} from 'react-native';
import { MaterialIcons } from '@expo/vector-icons';
import TraktIcon from '../../../assets/rating-icons/trakt.svg';
@@ -43,6 +44,15 @@ const CompactCommentCard: React.FC<{
onSpoilerPress: () => void;
}> = ({ comment, theme, onPress, isSpoilerRevealed, onSpoilerPress }) => {
const [isPressed, setIsPressed] = useState(false);
+ const fadeInOpacity = useRef(new Animated.Value(0)).current;
+
+ React.useEffect(() => {
+ Animated.timing(fadeInOpacity, {
+ toValue: 1,
+ duration: 220,
+ useNativeDriver: true,
+ }).start();
+ }, [fadeInOpacity]);
// Safety check - ensure comment data exists
if (!comment || !comment.comment) {
@@ -119,23 +129,27 @@ const CompactCommentCard: React.FC<{
};
return (
- setIsPressed(true)}
- onPressOut={() => setIsPressed(false)}
- onPress={() => {
- console.log('CompactCommentCard: TouchableOpacity pressed for comment:', comment.id);
- onPress();
- }}
- activeOpacity={1}
>
+ setIsPressed(true)}
+ onPressOut={() => setIsPressed(false)}
+ onPress={() => {
+ console.log('CompactCommentCard: TouchableOpacity pressed for comment:', comment.id);
+ onPress();
+ }}
+ activeOpacity={1}
+ >
{/* Trakt Icon - Top Right Corner */}
@@ -199,7 +213,8 @@ const CompactCommentCard: React.FC<{
)}
-
+
+
);
};
@@ -416,6 +431,7 @@ export const CommentsSection: React.FC = ({
}) => {
const { currentTheme } = useTheme();
const { settings } = useSettings();
+ const [hasLoadedOnce, setHasLoadedOnce] = React.useState(false);
const {
comments,
@@ -434,6 +450,13 @@ export const CommentsSection: React.FC = ({
enabled: true,
});
+ // Track when first load completes to avoid premature empty state
+ React.useEffect(() => {
+ if (!loading) {
+ setHasLoadedOnce(true);
+ }
+ }, [loading]);
+
// Debug logging
console.log('CommentsSection: Comments data:', comments);
console.log('CommentsSection: Comments length:', comments?.length);
@@ -484,6 +507,44 @@ export const CommentsSection: React.FC = ({
);
}, [loading, error, currentTheme]);
+ const renderSkeletons = useCallback(() => {
+ const placeholders = [0, 1, 2];
+ return (
+
+ {placeholders.map((i) => (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ))}
+
+ );
+ }, [currentTheme]);
+
// Don't show section if not authenticated, if comments are disabled in settings, or if still checking authentication
// Only show when authentication is definitively true and settings allow it
if (isAuthenticated !== true || !settings.showTraktComments) {
@@ -523,23 +584,18 @@ export const CommentsSection: React.FC = ({
)}
- {loading && comments.length === 0 && (
-
-
-
- Loading comments...
-
-
+ {loading && comments.length === 0 && renderSkeletons()}
+
+ {(!loading && comments.length === 0 && hasLoadedOnce && !error) && (
+ renderEmpty()
)}
- {comments.length === 0 ? (
- renderEmpty()
- ) : (
- 0 && (
+ item?.id?.toString() || Math.random().toString()}
+ keyExtractor={(item, index) => item?.id?.toString() || `comment-${index}`}
renderItem={renderComment}
contentContainerStyle={styles.horizontalList}
removeClippedSubviews={false}
@@ -566,7 +622,7 @@ export const CommentsSection: React.FC = ({
) : (
<>
-
+
Load More
@@ -576,6 +632,8 @@ export const CommentsSection: React.FC = ({
) : null
}
+ extraData={loading}
+ style={{ opacity: 1 }}
/>
)}
@@ -1034,6 +1092,20 @@ const styles = StyleSheet.create({
fontSize: 14,
marginTop: 12,
},
+ skeletonLine: {
+ height: 12,
+ borderRadius: 6,
+ backgroundColor: 'rgba(255,255,255,0.06)',
+ },
+ skeletonBadge: {
+ backgroundColor: 'rgba(255,255,255,0.08)',
+ },
+ skeletonDot: {
+ width: 16,
+ height: 16,
+ borderRadius: 8,
+ backgroundColor: 'rgba(255,255,255,0.08)'
+ },
errorContainer: {
flexDirection: 'row',
alignItems: 'center',