Skip to content

Commit

Permalink
Feat/backend integration comment (#67)
Browse files Browse the repository at this point in the history
* feat: 커뮤니티 댓글 생성 및 조회 백엔드 연결

* feat: 게시글/댓글 좋아요 생성 백엔드 연결

* refactor: useEffect 대신 useFocusEffect 사용

* fix: communityPage 댓글 및 게시글의 좋아요 optimistic updates 방식으로 변경
  • Loading branch information
nnyouung authored Jun 28, 2024
1 parent 3766aa0 commit c15859c
Show file tree
Hide file tree
Showing 8 changed files with 746 additions and 428 deletions.
735 changes: 472 additions & 263 deletions package-lock.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
"web": "expo start --web"
},
"dependencies": {
"@expo/metro-runtime": "^3.2.1",
"@react-native-async-storage/async-storage": "^1.23.1",
"@react-native-community/masked-view": "^0.1.11",
"@react-navigation/bottom-tabs": "^6.5.20",
"@react-navigation/drawer": "^6.6.15",
Expand Down
9 changes: 6 additions & 3 deletions src/components/community/IconHeart.jsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import * as React from 'react';
import Svg, { Path } from 'react-native-svg';
import { CustomTheme } from '@styles/CustomTheme';

const IconHeart = (props) => (
const IconHeart = ({props, active}) => {
const color = active ? CustomTheme.warningRed: CustomTheme.borderColor;
return (
<Svg
xmlns="http://www.w3.org/2000/svg"
width={16}
Expand All @@ -10,11 +13,11 @@ const IconHeart = (props) => (
{...props}
>
<Path
fill="#CDCFD5"
fill={color}
fillRule="evenodd"
d="M11.343 2.045c-1.087-.36-2.581.142-3.551 1.211a.113.113 0 0 1-.165.002C6.615 2.195 5.17 1.684 4.09 2.046 1.544 2.892.75 5.938 1.475 8.274c1.143 3.673 4.943 5.656 6.245 5.656 1.162 0 5.113-1.945 6.243-5.656.724-2.336-.071-5.382-2.62-6.23Z"
clipRule="evenodd"
/>
</Svg>
)
)};
export default IconHeart;
249 changes: 158 additions & 91 deletions src/components/community/ItemComment.jsx
Original file line number Diff line number Diff line change
@@ -1,113 +1,180 @@
import React from 'react';
import { View, Text, Image, StyleSheet, TouchableOpacity } from 'react-native';
import { useNavigation } from '@react-navigation/native';
import React, { useState } from 'react';
import { View, Text, StyleSheet, TouchableOpacity, Alert } from 'react-native';
import axios from 'axios';

import { CustomTheme } from '@styles/CustomTheme';
import { useOnboarding } from 'src/states/OnboardingContext.js';

import IconHeart from '@components/community/IconHeart';
import IconBookmark from '@components/community/IconBookmark';
import IconComment from '@components/community/IconComment';
import IconKebabMenu from './IconKebabMenu';
import IconKebabMenu from '@components/community/IconKebabMenu';

const { fontCaption, fontNavi } = CustomTheme;

const ItemComment = ({ props }) => {
const navigation = useNavigation();
const ItemComment = ({ props, id }) => {
const date = (date) => {
const datePart = date.split('T')[0];
const [year, month, day] = datePart.split('-');
return `${month}/${day}`;
};

const { onboardingData } = useOnboarding();
const [pressHeart, setPressHeart] = useState({});
const [heartCounts, setHeartCounts] = useState(props.reduce((acc, post) => {
acc[post.commentId] = post.heart;
return acc;
}, {}));


const heartCommentAlert = async (commentId) => {
try {
const response = await axios.post('http://192.168.45.135:8080/api/likes', {
type: 'COMMENT',
postId: id,
commentId: commentId,
}, {
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
'Authorization': `Bearer ${onboardingData.accessToken}`,
}
});

console.log('댓글 좋아요 성공');
setPressHeart(prevState => ({ ...prevState, [commentId]: true }));
setHeartCounts(prevState => ({
...prevState,
[commentId]: prevState[commentId] + 1,
}));
} catch (error) {
console.error('댓글 좋아요 실패:', error.response ? error.response.data : error.message);
setPressHeart(prevState => ({ ...prevState, [commentId]: false }));
setHeartCounts(prevState => ({
...prevState,
[commentId]: prevState[commentId] - 1,
}));
}
};

const handleHeart = (commentId) => {
Alert.alert(
"",
"이 댓글에 좋아요를 누르시겠습니까?",
[
{
text: "취소",
style: "cancel"
},
{
text: "확인",
onPress: () => {
setPressHeart(prevState => ({ ...prevState, [commentId]: true }));
setHeartCounts(prevState => ({
...prevState,
[commentId]: prevState[commentId] + 1,
}));
heartCommentAlert(commentId);
}
}
],
{ cancelable: false }
);
};


return (
<>
{props.map((post, index) => (
<View key={index} style={styles.ItemCommunity}>
<View style={styles.containerRow}>
<View>
<Text style={styles.textPostTitle}>{post.title}</Text>
<Text style={styles.textPostContext}>{post.context}</Text>

<View style={styles.containerTextRow}>
<View style={styles.containerText}>
<IconHeart />
<Text style={styles.text}>{post.heart}</Text>
</View>
<View style={styles.containerText}>
<IconBookmark />
<Text style={styles.text}>{post.bookmark}</Text>
</View>
<View style={styles.containerText}>
<Text style={styles.text}>{post.date}</Text>
{props.map((post, index) => (
<View key={index} style={styles.ItemCommunity}>
<View style={styles.containerRow}>
<View>
<Text style={styles.textPostTitle}>{post.title}</Text>
<Text style={styles.textPostContext}>{post.context}</Text>

<View style={styles.containerTextRow}>
<TouchableOpacity style={styles.containerText} onPress={() => handleHeart(post.commentId)}>
<IconHeart active={pressHeart[post.commentId]} />
<Text style={styles.text}>{heartCounts[post.commentId]}</Text>
</TouchableOpacity>
<View style={styles.containerText}>
<IconBookmark />
<Text style={styles.text}>{post.bookmark}</Text>
</View>
<View style={styles.containerText}>
<Text style={styles.text}>{date(post.date)}</Text>
</View>
</View>
</View>
</View>

<IconKebabMenu style={styles.iconKebabMenu}/>
<TouchableOpacity style={styles.textTranslation}>
<Text style={styles.textTranslation}>번역하기</Text>
</TouchableOpacity>
<IconKebabMenu style={styles.iconKebabMenu} />
<TouchableOpacity style={styles.textTranslation}>
<Text style={styles.textTranslation}>번역하기</Text>
</TouchableOpacity>
</View>
</View>
</View>
))}
))}
</>
);
};

const styles = StyleSheet.create({
ItemCommunity: {
width: '100%',
minHeight: 78,
backgroundColor: CustomTheme.bgBasic,
borderRadius: 20,
borderWidth: 2,
borderColor: '#D9EAFF',
paddingHorizontal: 20,
paddingVertical: 11,
justifyContent: 'center',
marginTop: 4,
marginBottom: 4,
},
containerRow: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
},
textPostTitle: {
fontSize: 12,
lineHeight: 16,
fontFamily: 'NotoSansCJKkr-Bold',
width: 272,
height: 17,
},
textPostContext: {
...fontCaption,
width: 288,
marginTop: 3,
},
iconKebabMenu: {
position: 'absolute',
top: 0,
right: -11,
},
textTranslation: {
...fontNavi,
color: CustomTheme.primaryMedium,
textDecorationLine: 'underline',
position: 'absolute',
bottom: 0,
right: -2,
},
containerTextRow: {
flexDirection: 'row',
marginTop: 8,
},
containerText: {
flexDirection: 'row',
alignItems: 'center',
marginRight: 8,
},
text: {
...fontNavi,
color: '#8C8D91',
marginLeft: 1,
},

ItemCommunity: {
width: '100%',
minHeight: 78,
backgroundColor: CustomTheme.bgBasic,
borderRadius: 20,
borderWidth: 2,
borderColor: '#D9EAFF',
paddingHorizontal: 20,
paddingVertical: 11,
justifyContent: 'center',
marginTop: 4,
marginBottom: 4,
},
containerRow: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
},
textPostTitle: {
fontSize: 12,
lineHeight: 16,
fontFamily: 'NotoSansCJKkr-Bold',
width: 272,
height: 17,
},
textPostContext: {
...fontCaption,
width: 288,
marginTop: 3,
},
iconKebabMenu: {
position: 'absolute',
top: 0,
right: -11,
},
textTranslation: {
...fontNavi,
color: CustomTheme.primaryMedium,
textDecorationLine: 'underline',
position: 'absolute',
bottom: 0,
right: -2,
},
containerTextRow: {
flexDirection: 'row',
marginTop: 8,
},
containerText: {
flexDirection: 'row',
alignItems: 'center',
marginRight: 8,
},
text: {
...fontNavi,
color: '#8C8D91',
marginLeft: 1,
},
});

export default ItemComment;
export default ItemComment;
18 changes: 4 additions & 14 deletions src/pages/community/CommunityPage.jsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import React, { useState, useEffect } from 'react';
import React, { useState } from 'react';
import { View, Text, TextInput, SafeAreaView, Keyboard, TouchableOpacity } from 'react-native';
import { useNavigation, useFocusEffect } from '@react-navigation/native';
import axios from 'axios';

import CommunityStyles from '@pages/community/CommunityStyles';
import { useOnboarding } from 'src/states/OnboardingContext.js';

import ConnectTop from '@components/connect/ConnectTop';
import ConnectSearchIcon from '@components/connect/ConnectSearchIcon';
Expand Down Expand Up @@ -50,10 +49,10 @@ const CommunityPage = () => {

const [tipPostList, setTipPostList] = useState([]);
const [freePostList, setFreePostList] = useState([]);
const { onboardingData } = useOnboarding();

useEffect(() => {
getPostsByType('TIP')
useFocusEffect(
React.useCallback(() => {
getPostsByType('TIP')
.then(response => {
setTipPostList(response.data.slice(0, 3));
})
Expand All @@ -67,15 +66,6 @@ const CommunityPage = () => {
.catch(error => {
console.error('게시글 조회 오류:', error.response ? error.response.data : error.message);
});
});

useEffect(() => {
handleTipCommunity();
}, []);

useFocusEffect(
React.useCallback(() => {
handleTipCommunity();
}, [])
);

Expand Down
12 changes: 3 additions & 9 deletions src/pages/community/FreeCommunityPage.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import { useNavigation, useFocusEffect } from '@react-navigation/native';
import axios from 'axios';

import FreeCommunityStyles from '@pages/community/FreeCommunityStyles';
import { useOnboarding } from 'src/states/OnboardingContext.js';

import ConnectTop from '@components/connect/ConnectTop';
import IconPostPlus from '@components/community/IconPostPlus';
Expand Down Expand Up @@ -48,21 +47,16 @@ const FreeCommunityPage = () => {
};

const [postList, setPostList] = useState([]);
const { onboardingData } = useOnboarding();

useEffect(() => {
getPostsByType('FREE')
useFocusEffect(
React.useCallback(() => {
getPostsByType('FREE')
.then(response => {
setPostList(response.data);
})
.catch(error => {
console.error('게시글 조회 오류:', error.response ? error.response.data : error.message);
});
});

useFocusEffect(
React.useCallback(() => {
handleFreeCommunity();
}, [])
);

Expand Down
Loading

0 comments on commit c15859c

Please sign in to comment.