👀 깃허브 링크
😎 클론 사이트 바로가기
이슈가 있었던 부분은 제목 옆에 ❗를 붙였습니다.
Notice / Bookmark
- 카테고리를 세분화 및 각각 정보들이 업데이트 될 때마다 실시간으로 확인 가능
- 라우터 내의 각 탭에서 렌더링 되는 정보들은 하나의 컴포넌트로 만들어서 재사용
- 노출되는 목록 오브젝트 클릭 시 라우터 이동과 시간 노출 부분은 따로 custom hooks로 빼내어 사용
- 내용을 감싸고 있는 태그와 내용들(이미지, 닉네임)의 라우터가 다름
- 감싸고 있는 태그에
<Link>
를 주게 될 시 내용들을 클릭할 때마다 원하는 라우터가 아닌 감싸진 태그의 라우터로 이동됨
ref
로 if문을 작성해useHistory()
로 구현
- 리트윗 탭에서는 원글, 답글에 리트윗 된 것들을 같이 볼 수 있게 했습니다.
- 원글과 답글의 리트윗 정보들을 한 번에 받아오기 하기 위해서
Firebase
의reNweets
컬렉션에 같이 추가 되도록 했고, 원글·답글 리트윗 정보의Firebase
필드값이 다른 부분이 있기에 삼항 연산자로 원하는 부분을 가져올 수 있게 했습니다.
- 재사용 컴포넌트에서는
useLocation()
,pathname
,includes()
을 사용해서 해당 주소일 때 원하는 값을 나타날 수 있게 했습니다.
// 리트윗 가져오기
useEffect(() => {
const q = query(
collection(dbService, "reNweets"),
orderBy("reNweetAt", "desc")
);
const unsubscribe = onSnapshot(q, (snapshot) => {
const reNweetArray = snapshot.docs.map((doc) => ({
id: doc.id,
...doc.data(),
}));
const filter = reNweetArray.filter(
(obj) =>
obj.email !== userObj.email &&
(obj?.replyEmail ? obj?.replyEmail : obj?.parentEmail) ===
userObj.email
);
setReNweets(filter);
setLoading((prev) => ({ ...prev, reNweets: true }));
});
return () => unsubscribe();
}, [userObj.email]);
// reNweetsObj = 부모 컴포넌트의 reNweets 값
export const NoticeReNweet = ({ reNweetsObj, userObj }) => {
const [creatorInfo, setCreatorInfo] = useState([]);
const [nweets, setNweets] = useState([]);
const [loading, setLoading] = useState(false);
// 정보 가져오기
useEffect(() => {
const unsubscribe = onSnapshot(
doc(dbService, "users", reNweetsObj.email),
(doc) => {
setCreatorInfo(doc.data());
setLoading(true);
}
);
return () => unsubscribe();
}, [reNweetsObj, userObj.email]);
// 트윗 가져오기
useEffect(() => {
const unsubscribe = onSnapshot(
doc(dbService, "nweets", reNweetsObj.parent),
(doc) => {
setNweets(doc.data());
}
);
return () => unsubscribe();
}, [reNweetsObj]);
return (
<>
{loading && (
<NoticeInnerContents
creatorInfo={creatorInfo}
noticeUser={reNweetsObj}
nweets={nweets}
text={
reNweetsObj?.replyId
? "답글에 리트윗을 했습니다."
: "글에 리트윗을 했습니다."
}
/>
)}
</>
);
};
export const NoticeInnerContents = ({
noticeUser,
creatorInfo,
text,
nweets,
}) => {
const imgRef = useRef();
const nameRef = useRef();
const location = useLocation();
const [followTime, setFollowTime] = useState([]);
// 팔로우 시간 정보 가져오기
useEffect(() => {
if (creatorInfo?.following) {
creatorInfo?.following.map((follow) => setFollowTime(follow.followAt));
}
}, [creatorInfo?.following]);
const { timeToString } = useTimeToString();
return (
<>
<div className={styled.nweet}>
<div className={styled.nweet__container}>
<Link
to={`/profile/mynweets/${noticeUser?.email}`}
className={styled.nweet__profile}
ref={imgRef}
>
<img
src={creatorInfo?.photoURL || noticeUser?.photoURL}
alt="profileImg"
className={styled.profile__image}
/>
</Link>
<Link
className={styled.nweet__contents}
to={
location.pathname.includes("followers")
? `/profile/mynweets/${noticeUser?.email}`
: `/nweet/${noticeUser?.parent}`
}
>
<div className={styled.reNweetBox}>
<p>
<span ref={nameRef}>
@{(noticeUser?.email || creatorInfo.email)?.split("@")[0]}
</span>
님이{" "}
{location.pathname.includes("renweets") && (
<span className={styled.reNweet__name}>
"{noticeUser?.text ? noticeUser?.text : nweets?.text}"
</span>
)}
{location.pathname.includes("replies") && (
<span className={styled.reNweet__name}>"{nweets?.text}"</span>
)}
{location.pathname.includes("followers") && null}
{text}
</p>
</div>
<div
style={{ marginLeft: "auto" }}
className={styled.reNweet__time}
>
{location.pathname.includes("renweets") && (
<p>{timeToString(noticeUser?.reNweetAt)}</p>
)}
{location.pathname.includes("replies") && (
<p>{timeToString(noticeUser?.createdAt)}</p>
)}
{location.pathname.includes("followers") && (
<p>{timeToString(followTime)}</p>
)}
</div>
</Link>
</div>
</div>
</>
);
};
const timeToString = (timestamp) => {
const today = new Date();
const timeValue = new Date(timestamp);
const betweenTime = Math.floor(
(today.getTime() - timeValue.getTime()) / 1000 / 60
);
if (betweenTime < 1) return "방금 전";
if (betweenTime < 60) {
return `${betweenTime}분 전`;
}
const betweenTimeHour = Math.floor(betweenTime / 60);
if (betweenTimeHour < 24) {
return `${betweenTimeHour}시간 전`;
}
const betweenTimeDay = Math.floor(betweenTime / 60 / 24);
if (betweenTimeDay < 8) {
return `${betweenTimeDay}일 전`;
}
return `${Math.floor(betweenTimeDay / 7)}주 전`;
};
- 화면 사이즈에 따라 '북마크' 탭을 다른 라우터에서 노출시키게 했습니다.
- resize 이벤트 리스너를 작성하여 윈도우의 사이즈를 체크하고 모바일 사이즈일 경우 프로필에 북마크 탭을 생성하고 해당 라우터로 이동시키게 했습니다.
// 리사이징
useEffect(() => {
// 렌더 시
if (size < 500) {
setResize(true);
// LeftBar 컴포넌트 속 코드 -> 모바일 사이즈 시 Profile의 북마크 탭으로 이동
history.push("/profile/bookmarknweets/" + userObj.email);
} else if (size > 500) {
setResize(false);
// Profile 컴포넌트 속 코드 -> 모바일 사이즈가 아닐 시 Left Bar의 북마크 탭으로 이동
history.push("/bookmark/nweets");
}
const Resize = () => {
let innerSize = window.innerWidth;
setSize(innerSize);
};
window.addEventListener("resize", Resize);
return () => window.addEventListener("resize", Resize);
}, [history, size, userObj.email]);