Skip to content

Commit

Permalink
Merge pull request #30 from sryung1225/dev
Browse files Browse the repository at this point in the history
내 프로필 페이지에서 모든 유저 프로필 페이지로 기능 확장 (#2) & 회원 탈퇴 기능 추가 (#21)
  • Loading branch information
sryung1225 authored Jan 10, 2024
2 parents ebfa1d0 + 354b1a7 commit 9ebc190
Show file tree
Hide file tree
Showing 10 changed files with 150 additions and 38 deletions.
2 changes: 1 addition & 1 deletion src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ const router = createBrowserRouter([
element: <Home />,
},
{
path: '/profile',
path: '/user',
element: <Profile />,
},
{
Expand Down
4 changes: 2 additions & 2 deletions src/components/home/tweet.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ export default function Tweet({
}, [userId]);
return (
<S.Wrapper>
<S.Avatar>
<S.Avatar to={`/user?query=${userId}`}>
{userAvatar ? (
<S.AvatarImage
src={userAvatar}
Expand All @@ -70,7 +70,7 @@ export default function Tweet({
)}
</S.Avatar>
<S.Row>
<S.Username>{userName}</S.Username>
<S.Username to={`/user?query=${userId}`}>{userName}</S.Username>
<S.Date>{FormatDate(createdAt)}</S.Date>
</S.Row>
<S.Payload>{tweet}</S.Payload>
Expand Down
9 changes: 7 additions & 2 deletions src/components/left-side-menu/navigation.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
import { Link, useLocation } from 'react-router-dom';
import { useRecoilValue } from 'recoil';
import currentUserAtom from '@atom/current-user.tsx';
import * as S from '@style/navigation.ts';
import ImageComputer from '@img/logo-small.png';
import { ReactComponent as IconUser } from '@img/i-user.svg';
import { ReactComponent as IconHome } from '@img/i-home.svg';

export default function Navigation() {
const location = useLocation();
const currentUser = useRecoilValue(currentUserAtom);
return (
<>
<S.Logo>
Expand All @@ -20,8 +23,10 @@ export default function Navigation() {
<IconHome />
</Link>
</S.MenuItem>
<S.MenuItem $isActive={location.pathname === '/profile'}>
<Link to="/profile">
<S.MenuItem
$isActive={location.search === `?query=${currentUser.userId}`}
>
<Link to={`/user?query=${currentUser.userId}`}>
<IconUser /> 프로필
</Link>
</S.MenuItem>
Expand Down
95 changes: 77 additions & 18 deletions src/components/profile/user-profile.tsx
Original file line number Diff line number Diff line change
@@ -1,43 +1,102 @@
import { useState } from 'react';
import { useRecoilValue } from 'recoil';
import { useNavigate } from 'react-router-dom';
import { useRecoilState } from 'recoil';
import { deleteUser } from 'firebase/auth';
import { deleteDoc, doc } from 'firebase/firestore';
import { deleteObject, ref } from 'firebase/storage';
import { auth, db, storage } from '@/firebase.ts';
import currentUserAtom from '@atom/current-user.tsx';
import IUser from '@type/IUser.ts';
import EditProfileForm from '@compo/profile/edit-profile-form.tsx';
import * as S from '@style/profile.ts';
import * as P from '@style/popup.ts';
import { ReactComponent as IconUser } from '@img/i-user.svg';

export default function UserProfile() {
interface IUserProfile {
user: IUser;
}

export default function UserProfile({ user }: IUserProfile) {
const navigate = useNavigate();
const [currentUser, setCurrentUser] = useRecoilState(currentUserAtom);
const [editPopup, setEditPopup] = useState(false);
const currentUser = useRecoilValue(currentUserAtom);
const [withdrawPopup, setWithdrawPopup] = useState(false);
const toggleEditPopup = () => {
setEditPopup(!editPopup);
};
const toggleWithdrawPopup = () => {
setWithdrawPopup(!withdrawPopup);
};
const withdrawAccount = async () => {
const authCurrentUser = auth.currentUser;
if (!authCurrentUser) return;
try {
await deleteDoc(doc(db, 'users', authCurrentUser.uid));
const photoRef = ref(storage, `avatars/${authCurrentUser.uid}`);
await deleteObject(photoRef);
await deleteUser(authCurrentUser);
} catch (error) {
console.log(error);
} finally {
setCurrentUser({
userId: '',
userAvatar: '',
userName: '',
});
navigate('/auth');
}
};
return (
<S.Profile>
<S.Avatar>
{currentUser.userAvatar ? (
{user.userAvatar ? (
<S.AvatarImage
src={currentUser.userAvatar}
alt={`${currentUser.userName}의 프로필 사진`}
src={user.userAvatar}
alt={`${user.userName}의 프로필 사진`}
width="120"
height="120"
/>
) : (
<IconUser />
)}
</S.Avatar>
<S.Name>{currentUser.userName}</S.Name>
<S.EditButton onClick={toggleEditPopup} type="button">
프로필 수정
</S.EditButton>
{editPopup ? (
<P.PopupWrapper>
<P.Popup>
<P.CloseButton onClick={toggleEditPopup} type="button" />
<EditProfileForm onClose={toggleEditPopup} />
</P.Popup>
</P.PopupWrapper>
) : null}
<S.Name>{user.userName}</S.Name>
{user.userId === currentUser.userId && (
<S.Buttons>
<S.EditButton onClick={toggleEditPopup} type="button">
프로필 수정
</S.EditButton>
{editPopup && (
<P.PopupWrapper>
<P.Popup>
<P.CloseButton onClick={toggleEditPopup} type="button" />
<EditProfileForm onClose={toggleEditPopup} />
</P.Popup>
</P.PopupWrapper>
)}
<S.WithdrawButton onClick={toggleWithdrawPopup} type="button">
회원 탈퇴
</S.WithdrawButton>
{withdrawPopup && (
<P.PopupWrapper>
<P.MiniPopup>
<P.CloseButton onClick={toggleWithdrawPopup} type="button" />
<P.Text>
정말 <span>탈퇴</span>하시겠습니까?
</P.Text>
<P.ButtonWrapper>
<P.Button onClick={withdrawAccount} type="button">
</P.Button>
<P.Button onClick={toggleWithdrawPopup} type="button">
아니요
</P.Button>
</P.ButtonWrapper>
</P.MiniPopup>
</P.PopupWrapper>
)}
</S.Buttons>
)}
</S.Profile>
);
}
14 changes: 8 additions & 6 deletions src/components/profile/user-timeline.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { useEffect, useState } from 'react';
import { useRecoilValue } from 'recoil';
import { Unsubscribe } from 'firebase/auth';
import {
collection,
Expand All @@ -9,21 +8,24 @@ import {
query,
where,
} from 'firebase/firestore';
import currentUserAtom from '@atom/current-user.tsx';
import { db } from '@/firebase.ts';
import IUser from '@type/IUser.ts';
import ITweet from '@type/ITweet.ts';
import Tweet from '@compo/home/tweet.tsx';
import * as S from '@style/timeline.ts';

export default function UserTimeline() {
interface IUserTimeline {
user: IUser;
}

export default function UserTimeline({ user }: IUserTimeline) {
const [tweets, setTweets] = useState<ITweet[]>([]);
const currentUser = useRecoilValue(currentUserAtom);
useEffect(() => {
let unsubscribe: Unsubscribe | null = null;
const fetchTweets = async () => {
const tweetsQuery = query(
collection(db, 'tweets'),
where('userId', '==', currentUser.userId),
where('userId', '==', user.userId),
orderBy('createdAt', 'desc'),
limit(25),
);
Expand All @@ -48,7 +50,7 @@ export default function UserTimeline() {
unsubscribe();
}
};
}, [currentUser.userId]);
}, [user]);
return tweets.length !== 0 ? (
<S.TimelineWrapper>
{tweets.map((tweet) => (
Expand Down
5 changes: 3 additions & 2 deletions src/components/protected-route.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,9 @@ export default function ProtectedRoute({
const [localStorageUser, setLocalStorageUser] =
useRecoilState(currentUserAtom);
if (!user || localStorageUser.userId === '') {
auth.signOut();
setLocalStorageUser({ userId: '', userName: '', userAvatar: '' });
auth.signOut().then(() => {
setLocalStorageUser({ userId: '', userName: '', userAvatar: '' });
});
return <Navigate to="/auth" />;
}
return children;
Expand Down
35 changes: 33 additions & 2 deletions src/routes/profile.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,45 @@
import { useEffect, useState } from 'react';
import { useLocation } from 'react-router-dom';
import { useRecoilValue } from 'recoil';
import { collection, getDocs, query, where } from 'firebase/firestore';
import { db } from '@/firebase.ts';
import WindowTop from '@compo/window-top.tsx';
import UserProfile from '@compo/profile/user-profile.tsx';
import UserTimeline from '@compo/profile/user-timeline.tsx';
import currentUserAtom from '@atom/current-user.tsx';
import IUser from '@type/IUser.ts';
import * as W from '@style/window.ts';

export default function Profile() {
const location = useLocation();
const userFromLocation = location.search.slice(7);
const [user, setUser] = useState<IUser | undefined>();
const currentUser = useRecoilValue(currentUserAtom);
useEffect(() => {
const fetchUser = async () => {
const usersQuery = query(
collection(db, 'users'),
where('userId', '==', userFromLocation),
);
const snapshot = await getDocs(usersQuery);
if (!snapshot.empty) {
const userData = snapshot.docs[0].data() as IUser;
setUser(userData);
} else {
console.error("can't find user data");
}
};
fetchUser();
}, [userFromLocation, currentUser]);
return (
<W.Window>
<WindowTop />
<UserProfile />
<UserTimeline />
{user && (
<>
<UserProfile user={user} />
<UserTimeline user={user} />
</>
)}
</W.Window>
);
}
3 changes: 3 additions & 0 deletions src/styles/popup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,9 @@ export const Text = styled.p`
font-size: 18px;
line-height: 30px;
text-align: center;
span {
color: ${primaryColor};
}
`;

export const ButtonWrapper = styled.div`
Expand Down
15 changes: 12 additions & 3 deletions src/styles/profile.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { styled } from 'styled-components';
import { grayColor } from '@style/global.ts';
import { LineButton } from '@style/button.ts';
import { LineButton, SolidButton } from '@style/button.ts';

export const Profile = styled.article`
position: relative;
Expand Down Expand Up @@ -60,7 +60,16 @@ export const Name = styled.h2`
font-size: 30px;
`;

export const EditButton = styled(LineButton)`
export const Buttons = styled.div`
display: inline-block;
`;

export const EditButton = styled(SolidButton)`
width: auto;
margin: 0 5px;
`;

export const WithdrawButton = styled(LineButton)`
width: auto;
margin: 0;
margin: 0 5px;
`;
6 changes: 4 additions & 2 deletions src/styles/tweet.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { styled } from 'styled-components';
import { grayColor, primaryColor } from '@style/global.ts';
import { Link } from 'react-router-dom';

export const Wrapper = styled.li`
position: relative;
Expand All @@ -13,7 +14,7 @@ export const Wrapper = styled.li`

export const Row = styled.div``;

export const Avatar = styled.div`
export const Avatar = styled(Link)`
position: absolute;
top: 20px;
left: 10px;
Expand Down Expand Up @@ -50,9 +51,10 @@ export const AvatarImage = styled.img`
z-index: 10;
`;

export const Username = styled.span`
export const Username = styled(Link)`
font-weight: 600;
font-size: 15px;
text-decoration: none;
`;

export const Date = styled.span`
Expand Down

0 comments on commit 9ebc190

Please sign in to comment.