댓글 알람은 언제 와야 할까??
(1) 내가 쓴 게시글에 (2) 내가 아닌 사람이 (3) 댓글을 쓰면 알려준다.
댓글 알람에는 뭐가 필요할까??
: 알람을 읽었는 지 안읽었는 지 구분자가 필요합니다!
1. 파이어베이스의 실시간 데이터베이스를 만들기(realtime database)
: 구분자로는 특정 할 수 있는 값을 사용하면 됩니다. (예를 들어서 아이디, 핸드폰 번호 등등)
2. 댓글 알림 뱃지를 material ui로 만들기
- 공식 문서
- 패키지 설치
yarn add @material-ui/core @material-ui/icons
- NotiBadge.js 만들기
: maretial ui의 아이콘과 Badge를 만든다.
//components/NotiBadge.js
import React from "react";
import { Notifications } from "@material-ui/icons";
import { Badge } from "@material-ui/core";
const NotiBadge = (props) => {
const [is_read, setIsRead] = React.useState(true);
const notiCheck = () => {
props._onClick();
};
return (
<React.Fragment>
<Badge
invisible={is_read}
color="secondary"
onClick={notiCheck}
variant="dot"
>
<Notifications />
</Badge>
</React.Fragment>
);
};
NotiBadge.defaultProps = {
_onClick: () => {},
};
export default NotiBadge;
// Header.js
import NotiBadge from "./NotiBadge";
const Header = (props) => {
...
if (is_login && is_session) {
return (
<React.Fragment>
<Grid is_flex padding="4px 16px">
<Grid>
<Text margin="0px" size="24px" bold>
헬로
</Text>
</Grid>
<Grid is_flex>
<Button text="내정보"></Button>
<NotiBadge _onClick={() => {
history.push("/noti");
}}/>
<Button
text="로그아웃"
_onClick={() => {
dispatch(userActions.logoutFB());
}}
></Button>
</Grid>
</Grid>
</React.Fragment>
);
}
3. realtime database 구독해서 읽음 처리하기
- realtime database 리스너 구독하기
👉 무한 스크롤은 클래스형 컴포넌트를 사용해서 스크롤 이벤트를 구독했던 거, 기억하시죠! 😎
오늘은 realtime database의 리스너를 구독하고 해제해봅시다! → useEffect()를 사용할거예요!
1) firebase.js에 realtime database 추가하기
// shared/firebase.js
import firebase from "firebase/app";
import "firebase/auth";
import "firebase/firestore";
import "firebase/storage";
import "firebase/database";
const firebaseConfig = {
//인증정보!
};
firebase.initializeApp(firebaseConfig);
const apiKey = firebaseConfig.apiKey;
const auth = firebase.auth();
const firestore = firebase.firestore();
const storage = firebase.storage();
const realtime = firebase.database();
export{auth, apiKey, firestore, storage, realtime};
2) <NotiBadge/>에서 리스너 구독하기
import React from "react";
import { Badge } from "@material-ui/core";
import NotificationsIcon from "@material-ui/icons/Notifications";
import {realtime} from "../shared/firebase";
import {useSelector} from "react-redux";
const NotiBadge = (props) => {
const [is_read, setIsRead] = React.useState(true);
const user_id = useSelector(state => state.user.user.uid);
const notiCheck = () => {
props._onClick();
};
React.useEffect(() => {
const notiDB = realtime.ref(`noti/${user_id}`);
notiDB.on("value", (snapshot) => {
console.log(snapshot.val());
setIsRead(snapshot.val()?.read);
// 옵셔널 체이닝을 사용함.
});
}, []);
return (
<React.Fragment>
<Badge color="secondary" variant="dot" invisible={is_read} onClick={notiCheck}>
<NotificationsIcon />
</Badge>
</React.Fragment>
);
};
NotiBadge.defaultProps = {
_onClick: () => {},
};
export default NotiBadge;
- 구독 해제하기
👉 리스너 구독 해제는 컴포넌트가 unMount 되는 시점에 해야하죠!
useEffect의 return에 넘겨주는 함수는 컴포넌트가 사라질 때 실행됩니다!
React.useEffect(() => {
...
// 리스너를 해제합시다!
return () => notiDB.off();
}, []);
- 댓글 달면 알림을 주자!
// redux/modules/comment.js
const addCommentFB = (post_id, contents) => {
return function (dispatch, getState, { history }) {
const commentDB = firestore.collection("comment");
const user_info = getState().user.user;
let comment = {
post_id: post_id,
user_id: user_info.uid,
user_name: user_info.user_name,
user_profile: user_info.user_profile,
contents: contents,
insert_dt: moment().format("YYYY-MM-DD hh:mm:ss"),
};
// firestore에 코멘트 정보를 넣어요!
commentDB.add(comment).then((doc) => {
const postDB = firestore.collection("post");
comment = { ...comment, id: doc.id };
// post 정보 가져오기!
const post = getState().post.list.find((l) => l.id === post_id);
// firestore에 저장된 값을 +1해줍니다!
const increment = firebase.firestore.FieldValue.increment(1);
// post에도 comment_cnt를 하나 플러스 해줍니다.
postDB
.doc(post_id)
.update({ comment_cnt: increment })
.then((_post) => {
// comment를 추가해주고,
dispatch(addComment(post_id, comment));
// 리덕스에 post가 있을 때만 post의 comment_cnt를 +1해줍니다.
if (post) {
dispatch(
postActions.editPost(post_id, {
comment_cnt: parseInt(post.comment_cnt) + 1,
})
);
// 알림이 가게 해줍니다!
const notiDB = realtime.ref(`noti/${post.user_info.user_id}`);
// 읽음 상태를 false로 바꿔주면 되겠죠!
notiDB.update({ read: false});
}
});
});
};
};
- 노티 아이콘 누르면 읽음 처리하기
// components/NotiBadge.js
const notiCheck = () => {
notiDB.update({ read: true });
props._onClick();
};
4. 알림 페이지에서 알림 확인하기
👉 실시간 데이터베이스에 알림 내역 리스트를 만들고, 알림페이지에서 리스트를 확인하도록 해봅시다! 🙂
알림 내역에는 댓글을 쓴 유저의 user_name, post_id, image_url이 필요해요!
앗, 그리고 insert_dt도 넣어줘야 정렬 해주기 좋겠죠!
- 알림 내역을 저장하기
// redux/modules/comment.js
const addCommentFB = (post_id, contents) => {
return function (dispatch, getState, { history }) {
...
// post에도 comment_cnt를 하나 플러스 해줍니다.
postDB
.doc(post_id)
.update({ comment_cnt: increment })
.then((_post) => {
// comment를 추가해주고,
dispatch(addComment(post_id, comment));
// 리덕스에 post가 있을 때만 post의 comment_cnt를 +1해줍니다.
if (post) {
dispatch(
postActions.editPost(post_id, {
comment_cnt: parseInt(post.comment_cnt) + 1,
})
);
// 알림 리스트에 하나를 추가해줍니다!
const _noti_item = realtime
.ref(`noti/${post.user_info.user_id}/list`)
.push();
_noti_item.set({
post_id: post.id,
user_name: comment.user_name,
image_url: post.image_url,
insert_dt: comment.insert_dt
}, (err) => {
if(err){
console.log('알림 저장 실패');
}else{
// 알림이 가게 해줍니다!
const notiDB = realtime.ref(`noti/${post.user_info.user_id}`);
// 읽음 상태를 false로 바꿔주면 되겠죠!
notiDB.update({ read: false });
}
});
}
});
});
};
};
- 알림 페이지에서 알림 list 가져오기
// pages/Notification.js
import React from "react";
import {Grid} from "../elements";
import Card from "../components/Card";
import {realtime} from "../shared/firebase";
import {useSelector} from "react-redux";
const Notification = (props) => {
const [noti, setNoti] = React.useState([]);
const user = useSelector(state => state.user.user);
React.useEffect(() => {
if(!user){
return;
}
const notiDB = realtime.ref(`noti/${user.uid}/list`);
// firebase realtime database는 내림차순 정렬을 지원하지 않아요!
// 데이터를 가져온 후 직접 역순으로 내보내야 합니다!
const _noti = notiDB.orderByChild("insert_dt");
_noti.once('value', snapshot => {
if(snapshot.exists()){
let _data = snapshot.val();
// reserse()는 배열을 역순으로 뒤집어줘요.
let _noti_list = Object.keys(_data).reverse().map(s => {
return _data[s];
});
setNoti(_noti_list);
}
})
}, [user]);
return (
<React.Fragment>
<Grid padding="16px" bg="#EFF6FF">
{noti.map((n, idx) => {
return <Card {...n} key={`noti_${idx}`} />;
})}
</Grid>
</React.Fragment>
);
}
export default Notification;
- 알림창에서 알림 하나를 누르면 게시물 상세 페이지로 이동하기
// components/Card.js
import React from "react";
import { Grid, Text, Image } from "../elements";
import {history} from "../redux/configureStore";
const Card = (props) => {
const { image_url, user_name, post_id } = props;
return (
{history.push(`/post/${post_id}`);}}
>
{user_name}님이 게시글에 댓글을 남겼습니다 :)!{" "}
);
};
Card.defaultProps = {
image_url: "<http://via.placeholder.com/400x300>",
};
export default Card;
'항해 중 > 5주차 리액트 심화반' 카테고리의 다른 글
추가 - 좋아요 기능 (0) | 2021.12.03 |
---|---|
4주차 - 엔터 키로 입력하기 (1) | 2021.12.03 |
4주차 - 댓글 작성하기 (0) | 2021.12.02 |
4주차 - 상세 페이지 연결하기 + fireStore 복합 쿼리 (0) | 2021.12.02 |
4주차 - 무한 스크롤 (0) | 2021.12.02 |
댓글