Skip to content

Commit

Permalink
Feat/image upload (#32)
Browse files Browse the repository at this point in the history
* feat: userEditModal 스타일 구현

* feat: PUT user

* feat: userEdit hook 구현

* fix: PUT 유저정보 update api username필드 필수 지정 제거

* fix: file input에 accep="image/*"추가

* fix: default_user_image 유저의 profile_photo 반영
  • Loading branch information
izone00 authored Jan 31, 2024
1 parent 8083e4f commit 8ed60ae
Show file tree
Hide file tree
Showing 13 changed files with 382 additions and 21 deletions.
41 changes: 37 additions & 4 deletions src/apis/user.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,39 @@ export async function getUserDetail(userId: number) {
return fetch(`${BASE_API_URL}/users/${userId}/`);
}

export async function putUserUpdate(
nickname: string,
bio: string,
accessToken: string
) {
return fetch(`${BASE_API_URL}/users/mypage/`, {
method: "PUT",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${accessToken}`,
},
body: JSON.stringify({ nickname, bio }),
});
}

export async function putUserPhotoUpdate(
backgroundPhoto: File | null,
profilePhoto: File | null,
accessToken: string
) {
const formData = new FormData();
backgroundPhoto && formData.append("background_photo", backgroundPhoto);
profilePhoto && formData.append("profile_photo", profilePhoto);

return fetch(`${BASE_API_URL}/users/mypage/image_update/`, {
method: "PUT",
headers: {
Authorization: `Bearer ${accessToken}`,
},
body: formData,
});
}

export async function getFollowingList(userId: number) {
return fetch(`${BASE_API_URL}/users/${userId}/followings/`);
}
Expand Down Expand Up @@ -66,7 +99,7 @@ export async function getUserRatingMovies(
query: {
order?: "high-rating" | "low-rating" | "created";
rate?: number;
},
}
) {
const result = Object.entries(query)
.map(([key, value]) => `${key}=${value}`)
Expand All @@ -84,7 +117,7 @@ export async function getUserWantToWatch(userId: number) {
export async function postCreateWatchingState(
movieCD: string,
accessToken: string,
user_state: "watching" | "want_to_watch" | "not_interested" | null,
user_state: "watching" | "want_to_watch" | "not_interested" | null
) {
return fetch(`${BASE_API_URL}/contents/${movieCD}/state`, {
method: "POST",
Expand All @@ -102,7 +135,7 @@ export async function postCreateWatchingState(
export async function putUpdateWatchingState(
state_id: number,
accessToken: string,
user_state: "watching" | "want_to_watch" | "not_interested" | null,
user_state: "watching" | "want_to_watch" | "not_interested" | null
) {
return fetch(`${BASE_API_URL}/contents/states/${state_id}`, {
method: "PUT",
Expand All @@ -118,7 +151,7 @@ export async function putUpdateWatchingState(

export async function deleteWatchingState(
state_id: number,
accessToken: string,
accessToken: string
) {
return fetch(`${BASE_API_URL}/contents/states/${state_id}`, {
method: "DELETE",
Expand Down
8 changes: 5 additions & 3 deletions src/components/CommentInfo.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import profileDefault from "../assets/user_default.jpg";
import { Link, useOutletContext, useParams } from "react-router-dom";
import userImage from "../assets/user_default.jpg";
import styles from "./CommentInfo.module.scss";

import ReplyList from "./ReplyList";
import elapsedTime from "../utils/elapsedTime";
import { CommentType } from "../type";
Expand Down Expand Up @@ -30,7 +29,10 @@ function CommentHeader({ comment }: { comment: CommentType }) {
title={created_by.nickname}
>
<div className={styles.userImage}>
<img src={userImage} alt={created_by.nickname + "의 사진"} />
<img
src={created_by.profile_photo ?? profileDefault}
alt={created_by.nickname + "의 사진"}
/>
</div>
<div className={styles.userDate}>
<div className={styles.userName}>{created_by.nickname}</div>
Expand Down
4 changes: 2 additions & 2 deletions src/components/Header.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { CurrentModalType } from "../pages/Layout";
import Logo from "../assets/logo.svg";
import WhiteLogo from "../assets/logo_white.svg";
import UserImage from "../assets/user_default.jpg";
import profileDefault from "../assets/user_default.jpg";
import styles from "./Header.module.scss";
import SearchBar from "./SearchBar";
import searchSmall from "../assets/searchSmall.svg";
Expand Down Expand Up @@ -113,7 +113,7 @@ export default function Header({ setCurrentModal }: HeaderProps) {
<li className={styles.myProfileLi}>
<Link to={`/users/${myUserData?.id}`}>
<div>
<img src={UserImage} />
<img src={myUserData?.profile_photo ?? profileDefault} />
</div>
</Link>
</li>
Expand Down
6 changes: 5 additions & 1 deletion src/components/MyCommentBox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,11 @@ export default function MyCommentBox({
<div className={styles.commentCon}>
<h3>내가 쓴 코멘트</h3>
<div className={styles.commentBox}>
<img className={styles.userImage} src={profileDefault} alt="" />
<img
className={styles.userImage}
src={myUserData?.profile_photo ?? profileDefault}
alt=""
/>
<Link
to={`/comments/${my_comment.id}`}
className={styles.commentText}
Expand Down
7 changes: 5 additions & 2 deletions src/components/ReplyList.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import styles from "./ReplyList.module.scss";
import userImage from "../assets/user_default.jpg";
import profileDefault from "../assets/user_default.jpg";
import { Link, useOutletContext } from "react-router-dom";
import elapsedTime from "../utils/elapsedTime";
import { BsThreeDotsVertical } from "react-icons/bs";
Expand Down Expand Up @@ -46,7 +46,10 @@ function Reply({
<div className={styles.reply}>
<Link to={`/users/${reply.created_by.id}`}>
<div className={styles.userImage}>
<img src={userImage} alt={reply.created_by.nickname + "의 사진"} />
<img
src={reply.created_by.profile_photo ?? profileDefault}
alt={reply.created_by.nickname + "의 사진"}
/>
</div>
</Link>
<div className={styles.replyBody}>
Expand Down
4 changes: 2 additions & 2 deletions src/components/SearchUserList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,8 @@ export default function SearchUserList({
<Link to={`/users/${user.id}`}>
<div className={styles.userImage}>
<img
alt={user.nickname + "의 사진"}
src={user.profile_photo ? user.profile_photo : UserDefault}
alt={user.username + "의 사진"}
src={user.profile_photo ?? UserDefault}
/>
</div>
<div className={styles.userInfo}>
Expand Down
26 changes: 24 additions & 2 deletions src/components/user/User.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,16 @@
}
}
.profileSection {
position: relative;
border-radius: 6px 6px 0 0;

.backgroundPhoto {
position: absolute;
top: 0;
width: 100%;
height: 35%;
}

.setBttnBox {
position: relative;
height: 70px;
Expand All @@ -42,15 +50,16 @@
background-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0Ij4KICAgIDxnIGZpbGw9Im5vbmUiIGZpbGwtcnVsZT0iZXZlbm9kZCI+CiAgICAgICAgPHBhdGggZD0iTTAgMGgyNHYyNEgweiIvPgogICAgICAgIDxwYXRoIGZpbGw9IiMwMDAiIGZpbGwtcnVsZT0ibm9uemVybyIgZD0iTTE5LjQzIDEyLjk4Yy4wNC0uMzIuMDctLjY0LjA3LS45OCAwLS4zNC0uMDMtLjY2LS4wNy0uOThsMi4xMS0xLjY1Yy4xOS0uMTUuMjQtLjQyLjEyLS42NGwtMi0zLjQ2Yy0uMTItLjIyLS4zOS0uMy0uNjEtLjIybC0yLjQ5IDFjLS41Mi0uNC0xLjA4LS43My0xLjY5LS45OGwtLjM4LTIuNjVBLjQ4OC40ODggMCAwIDAgMTQgMmgtNGMtLjI1IDAtLjQ2LjE4LS40OS40MmwtLjM4IDIuNjVjLS42MS4yNS0xLjE3LjU5LTEuNjkuOThsLTIuNDktMWMtLjIzLS4wOS0uNDkgMC0uNjEuMjJsLTIgMy40NmMtLjEzLjIyLS4wNy40OS4xMi42NGwyLjExIDEuNjVjLS4wNC4zMi0uMDcuNjUtLjA3Ljk4IDAgLjMzLjAzLjY2LjA3Ljk4bC0yLjExIDEuNjVjLS4xOS4xNS0uMjQuNDItLjEyLjY0bDIgMy40NmMuMTIuMjIuMzkuMy42MS4yMmwyLjQ5LTFjLjUyLjQgMS4wOC43MyAxLjY5Ljk4bC4zOCAyLjY1Yy4wMy4yNC4yNC40Mi40OS40Mmg0Yy4yNSAwIC40Ni0uMTguNDktLjQybC4zOC0yLjY1Yy42MS0uMjUgMS4xNy0uNTkgMS42OS0uOThsMi40OSAxYy4yMy4wOS40OSAwIC42MS0uMjJsMi0zLjQ2Yy4xMi0uMjIuMDctLjQ5LS4xMi0uNjRsLTIuMTEtMS42NXpNMTIgMTUuNWMtMS45MyAwLTMuNS0xLjU3LTMuNS0zLjVzMS41Ny0zLjUgMy41LTMuNSAzLjUgMS41NyAzLjUgMy41LTEuNTcgMy41LTMuNSAzLjV6Ii8+CiAgICA8L2c+Cjwvc3ZnPgo=");
}
.profileInfoBox {
position: inherit;
display: flex;
flex-direction: column;
padding: 15px 15px 30px 15px;

.profilePhoto {
margin: 0 auto;
width: 135px;
height: 135px;
border-radius: 50%;
background: url("https://an2-glx.amz.wtchn.net/assets/default/user/photo_file_name_large-ab0a7f6a92a282859192ba17dd4822023e22273e168c2daf05795e5171e66446.jpg")
center center / cover no-repeat;
border: 1px solid rgb(227, 227, 227);
margin-bottom: 8px;
}
Expand Down Expand Up @@ -84,6 +93,19 @@
font-weight: bold;
}
}
.userEditBttn {
width: 100%;
height: 40px;
border: none;
border-radius: 4px;
font-size: 16px;
font-weight: 500;
margin-top: 14px;
cursor: pointer;
color: rgb(41, 42, 50);
background-color: white;
border: 1px solid rgb(212, 212, 212);
}
.followBttn {
width: 100%;
height: 40px;
Expand Down
29 changes: 26 additions & 3 deletions src/components/user/User.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import { FollowListType } from "../../type";
import { getFollowingList } from "../../apis/user";
import { postAddFollow, postUnFollow } from "../../apis/user";
import useChangeTitle from "../../hooks/useChangeTitle";
import profileDefault from "../../assets/user_default.jpg";

export default function User() {
const { setCurrentModal } = useOutletContext<OutletContextType>();
const { myUserData, accessToken } = useAuthContext();
Expand Down Expand Up @@ -77,7 +79,7 @@ export default function User() {
.finally(() => {
setPageUserLoading(false);
});
}, [pageUserId]);
}, [pageUserId, myUserData]);

// 유저데이터에 팔로잉 리스트가 없어서 추가로 가져와야 함

Expand Down Expand Up @@ -110,12 +112,23 @@ export default function User() {
scrollToTop();
}, []);

const backgoundStyle = {
backgroundImage: `url(${pageUser?.background_photo ?? ""})`,
backgroundSize: "cover",
backgroundRepeat: "no-repeat",
backgroundPosition: "center",
};

return (
<div className={styles.userContainer}>
{!loading && pageUser && (
<>
{/* profile section. user 기본 정보와 평가&코멘트 탭을 포함하는 섹션 */}
<section className={styles.profileSection}>
<div
className={styles.backgroundPhoto}
style={backgoundStyle}
></div>
<div className={styles.setBttnBox}>
<button
className={styles.setBttn}
Expand All @@ -125,7 +138,10 @@ export default function User() {
/>
</div>
<div className={styles.profileInfoBox}>
<div className={styles.profilePhoto}></div>
<img
src={pageUser.profile_photo ?? profileDefault}
className={styles.profilePhoto}
/>
<h1>{pageUser.nickname}</h1>
<p>{pageUser.username}</p>
<div className={styles.connection}>
Expand All @@ -137,7 +153,14 @@ export default function User() {
<span>팔로잉 {pageUser.following_count}</span>
</Link>
</div>
{pageMode !== "myPage" && (
{pageMode === "myPage" ? (
<button
className={styles.userEditBttn}
onClick={() => setCurrentModal("userEdit")}
>
프로필 수정
</button>
) : (
<button
className={`${styles.followBttn} ${
isMyFollowing && styles.unfollow
Expand Down
79 changes: 79 additions & 0 deletions src/components/user/UserEditModal.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
@import "../../utils/common.module.scss";

.modalCon {
display: flex;
flex-direction: column;
width: 500px;
border-radius: 10px;
padding: 16px;
background-color: white;

header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 10px;

h2 {
font-size: 22px;
font-weight: 700;
}
button {
width: 40px;
background-color: inherit;
margin: 0;
border-style: none;
color: $dark-black;
cursor: pointer;
}
}
.imageCon {
position: relative;
width: 100%;
height: 200px;
margin-bottom: 40px;

// 기본 파일 업로드 버튼을 숨김
input[type="file"] {
display: none;
}
.backgroundPhotoEdit {
width: 100%;
height: 100%;
border-style: none;
cursor: pointer;
outline: none;
}
.profilePhotoEdit {
position: absolute;
left: 10px;
bottom: 10px;
width: 80px;
height: 80px;
border-radius: 50%;
border: 1px solid $light-gray;
cursor: pointer;
outline: none;
}
}
.textInputCon {
margin-bottom: 20px;

label {
height: 12px;
margin: 0;
padding: 0;
background-color: inherit;
font-size: 12px;
font-weight: 400;
color: $medium-gray;
}
input {
width: 100%;
border-style: none;
border-bottom: 1px solid $light-gray;
outline: none;
font-size: 20px;
}
}
}
Loading

0 comments on commit 8ed60ae

Please sign in to comment.