From c3c2a897e10d4fcf13f896d952870e7fd518819f Mon Sep 17 00:00:00 2001 From: HyunbinKim Date: Mon, 30 Sep 2024 00:26:10 +0900 Subject: [PATCH 01/26] =?UTF-8?q?#14=20[feat]:=20=EC=97=85=EB=A1=9C?= =?UTF-8?q?=EB=93=9C=20=EB=AA=A8=EB=8B=AC=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/modals/PostUpload.jsx | 11 +++++++++++ src/components/modals/TrackUpload.jsx | 11 +++++++++++ src/components/modals/UploadModal.jsx | 4 +++- 3 files changed, 25 insertions(+), 1 deletion(-) create mode 100644 src/components/modals/PostUpload.jsx create mode 100644 src/components/modals/TrackUpload.jsx diff --git a/src/components/modals/PostUpload.jsx b/src/components/modals/PostUpload.jsx new file mode 100644 index 0000000..d9fada2 --- /dev/null +++ b/src/components/modals/PostUpload.jsx @@ -0,0 +1,11 @@ +import React from 'react'; + +const PostUpload = () => { + return ( +
+ +
+ ); +}; + +export default PostUpload; \ No newline at end of file diff --git a/src/components/modals/TrackUpload.jsx b/src/components/modals/TrackUpload.jsx new file mode 100644 index 0000000..fa74d1e --- /dev/null +++ b/src/components/modals/TrackUpload.jsx @@ -0,0 +1,11 @@ +import React from 'react'; + +const TrackUpload = () => { + return ( +
+ +
+ ); +}; + +export default TrackUpload; \ No newline at end of file diff --git a/src/components/modals/UploadModal.jsx b/src/components/modals/UploadModal.jsx index ee922ea..4eed23a 100644 --- a/src/components/modals/UploadModal.jsx +++ b/src/components/modals/UploadModal.jsx @@ -3,6 +3,8 @@ import styled from 'styled-components'; import CloseIcon from '../../assets/icons/Close.png' import TrackIcon from '../../assets/icons/Track.png'; import PostIcon from '../../assets/icons/Post.png'; +import PostUpload from './PostUpload'; +import TrackUpload from './TrackUpload'; const UploadModal = ({ onClose }) => { const [activeTab, setActiveTab] = useState('track'); @@ -38,7 +40,7 @@ const UploadModal = ({ onClose }) => { {/* Modal Content */} - + {activeTab === 'track'? : } {/* Submit Button */} From 23a9f71751e8a9614a00e9df7cbd20532d9718c9 Mon Sep 17 00:00:00 2001 From: HyunbinKim Date: Mon, 30 Sep 2024 00:27:23 +0900 Subject: [PATCH 02/26] =?UTF-8?q?#14=20[feat]:=20=ED=8F=AC=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=EC=B6=94=EC=B2=9C=20=EB=AC=B8=EA=B5=AC=20=EC=9E=85?= =?UTF-8?q?=EB=A0=A5=EB=9E=80=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/modals/PostUpload.jsx | 91 +++++++++++++++++++++++++--- 1 file changed, 84 insertions(+), 7 deletions(-) diff --git a/src/components/modals/PostUpload.jsx b/src/components/modals/PostUpload.jsx index d9fada2..2645231 100644 --- a/src/components/modals/PostUpload.jsx +++ b/src/components/modals/PostUpload.jsx @@ -1,11 +1,88 @@ -import React from 'react'; +import React, { useState } from 'react'; +import styled from 'styled-components'; const PostUpload = () => { - return ( -
- -
- ); + + const [description, setDescription] = useState(''); + + return ( + + + + {/* 설명 입력 섹션 */} + + 노래 소개 + setDescription(e.target.value)} + /> + + + + + ); }; -export default PostUpload; \ No newline at end of file +export default PostUpload; + +// Styled components + +const PostUploadContainer = styled.div` + display: flex; + flex-direction: column; + width: 100%; + background-color: #fff; +`; + +const ContentWrapper = styled.div` + background-color: #fff; + padding: 10px; + border-radius: 15px; + width: 100%; + max-width: 700px; + margin: 0 auto; + display: flex; + flex-direction: column; + justify-content: space-between; + min-height: 60vh; +`; + +const DescriptionSection = styled.div` + background-color: #C0AFE2; + padding: 15px; + border-radius: 10px; + border: 1px solid #d1c4e9; +`; + +const DescriptionTitle = styled.p` + font-size: 18px; + margin-top: 0%; + margin-bottom: 15px; + color: white; + border-bottom: 2px solid white; +`; + +const DescriptionInput = styled.textarea` + width: 95%; + height: 70px; + padding: 10px; + border: none; + border-radius: 5px; + background-color: #C0AFE2; + font-size: 14px; + color: white; + resize: none; + + &::placeholder { + color: white; + } +`; + +const CharCount = styled.p` + font-size: 12px; + color: white; + text-align: right; + margin-top: 5px; +`; + From 1bbcbc21159da5d5c7791b3cf46b09306948681b Mon Sep 17 00:00:00 2001 From: HyunbinKim Date: Mon, 30 Sep 2024 00:29:43 +0900 Subject: [PATCH 03/26] =?UTF-8?q?#14=20[feat]:=20=EC=9E=85=EB=A0=A5?= =?UTF-8?q?=EB=9E=80=20=EC=B5=9C=EB=8C=80=20300=EC=9E=90,=20=EC=8B=A4?= =?UTF-8?q?=EC=8B=9C=EA=B0=84=20=EA=B8=80=EC=9E=90=20=EC=88=98=20=ED=91=9C?= =?UTF-8?q?=EC=8B=9C=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/modals/PostUpload.jsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/components/modals/PostUpload.jsx b/src/components/modals/PostUpload.jsx index 2645231..e8afbae 100644 --- a/src/components/modals/PostUpload.jsx +++ b/src/components/modals/PostUpload.jsx @@ -4,7 +4,7 @@ import styled from 'styled-components'; const PostUpload = () => { const [description, setDescription] = useState(''); - + const maxCharLimit = 300; return ( @@ -16,7 +16,9 @@ const PostUpload = () => { placeholder="이 곡의 특별함은 무엇인가요?" value={description} onChange={(e) => setDescription(e.target.value)} + maxLength={maxCharLimit} /> + {description.length}/{maxCharLimit}자 From 285e9da98e22143f2568ec620a6c294c8af65619 Mon Sep 17 00:00:00 2001 From: HyunbinKim Date: Mon, 30 Sep 2024 01:31:32 +0900 Subject: [PATCH 04/26] =?UTF-8?q?#14=20[feat]:=20=ED=8F=AC=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=EC=A0=9C=EB=AA=A9=20=EC=9E=85=EB=A0=A5=EB=9E=80=20?= =?UTF-8?q?=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/modals/PostUpload.jsx | 49 +++++++++++++++++++++++----- 1 file changed, 41 insertions(+), 8 deletions(-) diff --git a/src/components/modals/PostUpload.jsx b/src/components/modals/PostUpload.jsx index e8afbae..d17293a 100644 --- a/src/components/modals/PostUpload.jsx +++ b/src/components/modals/PostUpload.jsx @@ -2,13 +2,26 @@ import React, { useState } from 'react'; import styled from 'styled-components'; const PostUpload = () => { - + const [albumData, setAlbumData] = useState(null); const [description, setDescription] = useState(''); + const [postTitle, setPostTitle] = useState(''); const maxCharLimit = 300; + return ( + + {/* 포스트 제목 필드와 인풋박스 */} + + setPostTitle(e.target.value)} /> + + + {/* 설명 입력 섹션 */} 노래 소개 @@ -16,11 +29,11 @@ const PostUpload = () => { placeholder="이 곡의 특별함은 무엇인가요?" value={description} onChange={(e) => setDescription(e.target.value)} - maxLength={maxCharLimit} - /> - {description.length}/{maxCharLimit}자 + maxLength={maxCharLimit}/> + + {description.length}/{maxCharLimit}자 + - ); @@ -50,8 +63,29 @@ const ContentWrapper = styled.div` min-height: 60vh; `; +const TrackDetails = styled.div` + display: flex; + flex-direction: column; + justify-content: center; + width: 100%; +`; + +const PostTitleWrapper = styled.div` + display: flex; + align-items: center; + margin-bottom: 15px; +`; + +const PostTitleInput = styled.input` + padding: 10px; + font-size: 16px; + border: 1px solid #ccc; + border-radius: 5px; + width: 100%; +`; + const DescriptionSection = styled.div` - background-color: #C0AFE2; + background-color: #c0afe2; padding: 15px; border-radius: 10px; border: 1px solid #d1c4e9; @@ -71,7 +105,7 @@ const DescriptionInput = styled.textarea` padding: 10px; border: none; border-radius: 5px; - background-color: #C0AFE2; + background-color: #c0afe2; font-size: 14px; color: white; resize: none; @@ -87,4 +121,3 @@ const CharCount = styled.p` text-align: right; margin-top: 5px; `; - From 1d4abf8499d7993d5afa85477e4877fb2c8d46c3 Mon Sep 17 00:00:00 2001 From: HyunbinKim Date: Mon, 30 Sep 2024 01:35:31 +0900 Subject: [PATCH 05/26] =?UTF-8?q?#14=20[feat]:=20=EC=A0=9C=EB=AA=A9=20?= =?UTF-8?q?=EC=9E=85=EB=A0=A5=EB=9E=80=20=EC=B5=9C=EB=8C=80=2020=EC=9E=90,?= =?UTF-8?q?=20=EC=8B=A4=EC=8B=9C=EA=B0=84=20=EA=B8=80=EC=9E=90=20=EC=88=98?= =?UTF-8?q?=20=ED=91=9C=EC=8B=9C=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/modals/PostUpload.jsx | 38 ++++++++++++++++++---------- 1 file changed, 24 insertions(+), 14 deletions(-) diff --git a/src/components/modals/PostUpload.jsx b/src/components/modals/PostUpload.jsx index d17293a..ec398e2 100644 --- a/src/components/modals/PostUpload.jsx +++ b/src/components/modals/PostUpload.jsx @@ -2,25 +2,28 @@ import React, { useState } from 'react'; import styled from 'styled-components'; const PostUpload = () => { - const [albumData, setAlbumData] = useState(null); const [description, setDescription] = useState(''); const [postTitle, setPostTitle] = useState(''); const maxCharLimit = 300; + const maxTitleCharLimit = 20; return ( - - - {/* 포스트 제목 필드와 인풋박스 */} - - setPostTitle(e.target.value)} /> - - + + {/* 포스트 제목 필드와 인풋박스 */} + + setPostTitle(e.target.value)} + maxLength={maxTitleCharLimit} /> + + {postTitle.length}/{maxTitleCharLimit}자 + + + {/* 설명 입력 섹션 */} @@ -29,7 +32,7 @@ const PostUpload = () => { placeholder="이 곡의 특별함은 무엇인가요?" value={description} onChange={(e) => setDescription(e.target.value)} - maxLength={maxCharLimit}/> + maxLength={maxCharLimit} /> {description.length}/{maxCharLimit}자 @@ -73,6 +76,7 @@ const TrackDetails = styled.div` const PostTitleWrapper = styled.div` display: flex; align-items: center; + justify-content: space-between; margin-bottom: 15px; `; @@ -81,7 +85,13 @@ const PostTitleInput = styled.input` font-size: 16px; border: 1px solid #ccc; border-radius: 5px; - width: 100%; + width: 90%; +`; + +const TitleCharCount = styled.p` + font-size: 10px; + color: #666; + margin-left: 10px; `; const DescriptionSection = styled.div` From 85e918c395c865c044fd187f418a9293b1d2226d Mon Sep 17 00:00:00 2001 From: HyunbinKim Date: Mon, 30 Sep 2024 01:48:22 +0900 Subject: [PATCH 06/26] =?UTF-8?q?#14=20[feat]:=20=EC=9C=A0=ED=8A=9C?= =?UTF-8?q?=EB=B8=8C=EB=AE=A4=EC=A7=81=20API=EB=A5=BC=20=EC=9D=B4=EC=9A=A9?= =?UTF-8?q?=ED=95=98=EC=97=AC=20=EA=B2=80=EC=83=89=EC=9D=84=20=ED=86=B5?= =?UTF-8?q?=ED=95=B4=20=EC=9D=B4=EB=AF=B8=EC=A7=80=20=EB=B0=8F=20=EA=B3=A1?= =?UTF-8?q?=20=EC=A0=95=EB=B3=B4=20=EA=B0=80=EC=A0=B8=EC=98=A4=EB=8A=94=20?= =?UTF-8?q?=EA=B8=B0=EB=8A=A5=20=EA=B5=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/assets/icons/YoutubeMusicIcon.png | Bin 0 -> 5237 bytes src/components/modals/PostUpload.jsx | 247 +++++++++++++++++++++++--- 2 files changed, 227 insertions(+), 20 deletions(-) create mode 100644 src/assets/icons/YoutubeMusicIcon.png diff --git a/src/assets/icons/YoutubeMusicIcon.png b/src/assets/icons/YoutubeMusicIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..3323bc7e843ea34199223fe67f635b5323c2e2ca GIT binary patch literal 5237 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 literal 0 HcmV?d00001 diff --git a/src/components/modals/PostUpload.jsx b/src/components/modals/PostUpload.jsx index ec398e2..2acebc5 100644 --- a/src/components/modals/PostUpload.jsx +++ b/src/components/modals/PostUpload.jsx @@ -1,29 +1,124 @@ import React, { useState } from 'react'; import styled from 'styled-components'; +import axios from 'axios'; +import YoutubeMusicIcon from '../../assets/icons/YoutubeMusicIcon.png'; const PostUpload = () => { + const [albumData, setAlbumData] = useState(null); const [description, setDescription] = useState(''); + const [selectedTrack, setSelectedTrack] = useState(null); + const [searchTerm, setSearchTerm] = useState(''); + const [searchResults, setSearchResults] = useState([]); + const [isSearchMode, setIsSearchMode] = useState(false); const [postTitle, setPostTitle] = useState(''); const maxCharLimit = 300; const maxTitleCharLimit = 20; + // 앨범 검색 기능 + const searchAlbums = async () => { + const options = { + method: 'GET', + url: 'https://youtube-music-api3.p.rapidapi.com/search', + params: { q: searchTerm, type: 'song' }, + headers: { + 'x-rapidapi-key': '44e584cb92msh419c63d530f9731p198f8ejsn087035d40a78', + 'x-rapidapi-host': 'youtube-music-api3.p.rapidapi.com', + }, + mode: 'no-cors', + }; + + try { + const response = await axios.request(options); + const results = response.data.result || []; + setSearchResults(results); + } catch (error) { + console.error('검색 결과를 가져오는 중 오류 발생:', error); + } + }; + + // 앨범 선택 시 처리 + const selectAlbum = (album) => { + setAlbumData({ + title: album.title, + artist: album.author, + coverUrl: album.thumbnail, + }); + setSelectedTrack(album); + setSearchResults([]); + setIsSearchMode(false); + }; + return ( - - {/* 포스트 제목 필드와 인풋박스 */} - - setPostTitle(e.target.value)} - maxLength={maxTitleCharLimit} /> - - {postTitle.length}/{maxTitleCharLimit}자 - - - + {/* 앨범 가져오기 버튼: 앨범이 선택되지 않았을 때만 표시 */} + + + {albumData ? ( + + ) : ( + setIsSearchMode(true)}> + + YouTube Music에서
+ 앨범 가져오기 +
+ )} +
+ + + {/* 포스트 제목 필드와 인풋박스 */} + + setPostTitle(e.target.value)} + maxLength={maxTitleCharLimit} + /> + + {postTitle.length}/{maxTitleCharLimit}자 + + + + {albumData ? albumData.title : '제목'} + {albumData ? albumData.artist : '아티스트'} + +
+ + {/* 검색 모드: 앨범을 선택하지 않았을 때만 검색 UI 표시 */} + {isSearchMode && !albumData && ( + <> + + setSearchTerm(e.target.value)} + /> + 검색 + + + {/* 검색 결과 */} + {searchResults.length > 0 && ( + + {searchResults.map((album) => ( + selectAlbum(album)}> + + + {album.title} + {album.author} + + + ))} + + )} + + )} {/* 설명 입력 섹션 */} @@ -32,7 +127,8 @@ const PostUpload = () => { placeholder="이 곡의 특별함은 무엇인가요?" value={description} onChange={(e) => setDescription(e.target.value)} - maxLength={maxCharLimit} /> + maxLength={maxCharLimit} + /> {description.length}/{maxCharLimit}자 @@ -66,6 +162,46 @@ const ContentWrapper = styled.div` min-height: 60vh; `; +const AlbumSection = styled.div` + display: flex; + margin-bottom: 20px; +`; + +const AlbumPlaceholder = styled.div` + background-color: #c0afe2; + height: 200px; + width: 300px; + display: flex; + align-items: center; + justify-content: center; + margin-right: 20px; + border-radius: 10px; +`; + +const AlbumCover = styled.img` + width: 100%; + height: 100%; + border-radius: 10px; + object-fit: cover; +`; + +const YouTubeMusicLink = styled.p` + display: flex; + align-items: center; + color: white; + font-size: 11px; + font-weight: bold; + cursor: pointer; + text-align: center; + margin-right: 5%; +`; + +const YoutubeMusicIconImage = styled.img` + width: 30px; + height: 30px; + margin-right: 7px; +`; + const TrackDetails = styled.div` display: flex; flex-direction: column; @@ -74,9 +210,7 @@ const TrackDetails = styled.div` `; const PostTitleWrapper = styled.div` - display: flex; - align-items: center; - justify-content: space-between; + position: relative; margin-bottom: 15px; `; @@ -85,13 +219,86 @@ const PostTitleInput = styled.input` font-size: 16px; border: 1px solid #ccc; border-radius: 5px; - width: 90%; + width: 94%; `; const TitleCharCount = styled.p` + position: absolute; + bottom: 5px; + right: 10px; font-size: 10px; color: #666; - margin-left: 10px; +`; + +const TrackTitle = styled.p` + font-size: 20px; + font-weight: bold; + margin-bottom: 0; +`; + +const ArtistName = styled.p` + font-size: 15px; + color: #666; + margin-bottom: 50px; +`; + +const SearchSection = styled.div` + display: flex; + margin-bottom: 20px; + justify-content: center; +`; + +const SearchInput = styled.input` + width: 40%; + padding: 5px; + font-size: 16px; + border: 1px solid #ccc; + border-radius: 5px; + margin-right: 10px; +`; + +const SearchButton = styled.button` + padding: 5px 20px; + background-color: black; + color: white; + border: none; + border-radius: 5px; + cursor: pointer; +`; + +const SearchResults = styled.div` + margin-bottom: 20px; +`; + +const AlbumItem = styled.div` + display: flex; + align-items: center; + margin-bottom: 10px; + cursor: pointer; + &:hover { + background-color: #f1f1f1; + } +`; + +const AlbumThumbnail = styled.img` + width: 50px; + height: 50px; + margin-right: 10px; +`; + +const AlbumInfo = styled.div` + display: flex; + flex-direction: column; +`; + +const AlbumTitle = styled.p` + font-size: 16px; + margin: 0; +`; + +const AlbumArtist = styled.span` + font-size: 12px; + color: #666; `; const DescriptionSection = styled.div` From 7089ea71420e41d6f2a8f3da574ccee62c121edf Mon Sep 17 00:00:00 2001 From: HyunbinKim Date: Mon, 30 Sep 2024 01:56:43 +0900 Subject: [PATCH 07/26] =?UTF-8?q?#14=20[feat]:=20=EC=95=A8=EB=B2=94=20?= =?UTF-8?q?=EC=9E=AC=20=EC=84=A0=ED=83=9D=20=EC=8B=9C=20=EC=9E=AC=20?= =?UTF-8?q?=EA=B2=80=EC=83=89=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/modals/PostUpload.jsx | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/src/components/modals/PostUpload.jsx b/src/components/modals/PostUpload.jsx index 2acebc5..59e272e 100644 --- a/src/components/modals/PostUpload.jsx +++ b/src/components/modals/PostUpload.jsx @@ -6,11 +6,10 @@ import YoutubeMusicIcon from '../../assets/icons/YoutubeMusicIcon.png'; const PostUpload = () => { const [albumData, setAlbumData] = useState(null); const [description, setDescription] = useState(''); - const [selectedTrack, setSelectedTrack] = useState(null); + const [postTitle, setPostTitle] = useState(''); const [searchTerm, setSearchTerm] = useState(''); const [searchResults, setSearchResults] = useState([]); const [isSearchMode, setIsSearchMode] = useState(false); - const [postTitle, setPostTitle] = useState(''); const maxCharLimit = 300; const maxTitleCharLimit = 20; @@ -43,21 +42,30 @@ const PostUpload = () => { artist: album.author, coverUrl: album.thumbnail, }); - setSelectedTrack(album); setSearchResults([]); setIsSearchMode(false); }; + // 앨범을 변경하려면 클릭 시 원래 상태로 돌아가도록 처리 + const resetAlbumSelection = () => { + setAlbumData(null); + setIsSearchMode(true); + setSearchTerm(''); + }; + return ( {/* 앨범 가져오기 버튼: 앨범이 선택되지 않았을 때만 표시 */} - + setIsSearchMode(true) + }> {albumData ? ( ) : ( - setIsSearchMode(true)}> + Date: Mon, 30 Sep 2024 02:09:45 +0900 Subject: [PATCH 08/26] =?UTF-8?q?#14=20[feat]:=20=EC=9E=AC=EC=83=9D?= =?UTF-8?q?=ED=95=A0=20=EC=88=98=20=EC=9E=88=EB=8A=94=20=EC=9E=AC=EC=83=9D?= =?UTF-8?q?=20=EB=B0=94=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package-lock.json | 88 ++++++++++++++++++++ package.json | 2 + src/assets/icons/PlayButton.png | Bin 0 -> 4054 bytes src/assets/icons/StopButton.png | Bin 0 -> 3878 bytes src/components/modals/PostUpload.jsx | 115 +++++++++++++++++++++++++-- 5 files changed, 200 insertions(+), 5 deletions(-) create mode 100644 src/assets/icons/PlayButton.png create mode 100644 src/assets/icons/StopButton.png diff --git a/package-lock.json b/package-lock.json index 21a011c..d30a6a9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,6 +12,7 @@ "react": "^18.3.1", "react-dom": "^18.3.1", "react-router-dom": "^6.26.2", + "react-youtube": "^10.1.0", "styled-components": "^6.1.13" }, "devDependencies": { @@ -1005,6 +1006,15 @@ "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", "license": "MIT" }, + "node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, "node_modules/delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", @@ -1053,6 +1063,12 @@ "@esbuild/win32-x64": "0.21.5" } }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "license": "MIT" + }, "node_modules/follow-redirects": { "version": "1.15.9", "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", @@ -1121,6 +1137,12 @@ "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", "license": "MIT" }, + "node_modules/load-script": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/load-script/-/load-script-1.0.0.tgz", + "integrity": "sha512-kPEjMFtZvwL9TaZo0uZ2ml+Ye9HUMmPwbYRJ324qF9tqMejwykJ5ggTyvzmrbBeapCAbk98BSbTeovHEEP1uCA==", + "license": "MIT" + }, "node_modules/loose-envify": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", @@ -1154,6 +1176,12 @@ "node": ">= 0.6" } }, + "node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, "node_modules/nanoid": { "version": "3.3.7", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", @@ -1172,6 +1200,15 @@ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/picocolors": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.0.tgz", @@ -1229,6 +1266,17 @@ "url": "https://github.com/prettier/prettier?sponsor=1" } }, + "node_modules/prop-types": { + "version": "15.8.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.13.1" + } + }, "node_modules/proxy-from-env": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", @@ -1260,6 +1308,12 @@ "react": "^18.3.1" } }, + "node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "license": "MIT" + }, "node_modules/react-router": { "version": "6.26.2", "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.26.2.tgz", @@ -1292,6 +1346,23 @@ "react-dom": ">=16.8" } }, + "node_modules/react-youtube": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/react-youtube/-/react-youtube-10.1.0.tgz", + "integrity": "sha512-ZfGtcVpk0SSZtWCSTYOQKhfx5/1cfyEW1JN/mugGNfAxT3rmVJeMbGpA9+e78yG21ls5nc/5uZJETE3cm3knBg==", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "3.1.3", + "prop-types": "15.8.1", + "youtube-player": "5.5.2" + }, + "engines": { + "node": ">= 14.x" + }, + "peerDependencies": { + "react": ">=0.14.1" + } + }, "node_modules/rollup": { "version": "4.22.4", "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.22.4.tgz", @@ -1350,6 +1421,12 @@ "integrity": "sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==", "license": "MIT" }, + "node_modules/sister": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/sister/-/sister-3.0.2.tgz", + "integrity": "sha512-p19rtTs+NksBRKW9qn0UhZ8/TUI9BPw9lmtHny+Y3TinWlOa9jWh9xB0AtPSdmOy49NJJJSSe0Ey4C7h0TrcYA==", + "license": "BSD-3-Clause" + }, "node_modules/source-map-js": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", @@ -1486,6 +1563,17 @@ "optional": true } } + }, + "node_modules/youtube-player": { + "version": "5.5.2", + "resolved": "https://registry.npmjs.org/youtube-player/-/youtube-player-5.5.2.tgz", + "integrity": "sha512-ZGtsemSpXnDky2AUYWgxjaopgB+shFHgXVpiJFeNB5nWEugpW1KWYDaHKuLqh2b67r24GtP6HoSW5swvf0fFIQ==", + "license": "BSD-3-Clause", + "dependencies": { + "debug": "^2.6.6", + "load-script": "^1.0.0", + "sister": "^3.0.0" + } } } } diff --git a/package.json b/package.json index db7f9bc..61966ff 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,8 @@ "react": "^18.3.1", "react-dom": "^18.3.1", "react-router-dom": "^6.26.2", + "react-youtube": "^10.1.0", + "proxy": "https://youtube-music-api3.p.rapidapi.com", "styled-components": "^6.1.13" }, "devDependencies": { diff --git a/src/assets/icons/PlayButton.png b/src/assets/icons/PlayButton.png new file mode 100644 index 0000000000000000000000000000000000000000..544a4d29d1c085ddbd98a7b5e8a613c790403c66 GIT binary patch literal 4054 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 IM6N<$g7*`id;kCd literal 0 HcmV?d00001 diff --git a/src/assets/icons/StopButton.png b/src/assets/icons/StopButton.png new file mode 100644 index 0000000000000000000000000000000000000000..4172e8b17eaf67a4878be5985802695daaa1741d GIT binary patch literal 3878 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 literal 0 HcmV?d00001 diff --git a/src/components/modals/PostUpload.jsx b/src/components/modals/PostUpload.jsx index 59e272e..c6d678f 100644 --- a/src/components/modals/PostUpload.jsx +++ b/src/components/modals/PostUpload.jsx @@ -1,18 +1,28 @@ -import React, { useState } from 'react'; +import React, { useState, useRef } from 'react'; import styled from 'styled-components'; import axios from 'axios'; import YoutubeMusicIcon from '../../assets/icons/YoutubeMusicIcon.png'; +import StopButtonIcon from '../../assets/icons/StopButton.png'; +import PlayButtonIcon from '../../assets/icons/PlayButton.png'; +import YouTube from 'react-youtube'; const PostUpload = () => { const [albumData, setAlbumData] = useState(null); const [description, setDescription] = useState(''); + const [isPlaying, setIsPlaying] = useState(false); + const [selectedTrack, setSelectedTrack] = useState(null); const [postTitle, setPostTitle] = useState(''); const [searchTerm, setSearchTerm] = useState(''); const [searchResults, setSearchResults] = useState([]); const [isSearchMode, setIsSearchMode] = useState(false); + const [player, setPlayer] = useState(null); + const [currentTime, setCurrentTime] = useState(0); + const [duration, setDuration] = useState(0); const maxCharLimit = 300; const maxTitleCharLimit = 20; + const intervalRef = useRef(null); + // 앨범 검색 기능 const searchAlbums = async () => { const options = { @@ -42,6 +52,7 @@ const PostUpload = () => { artist: album.author, coverUrl: album.thumbnail, }); + setSelectedTrack(album); setSearchResults([]); setIsSearchMode(false); }; @@ -53,6 +64,42 @@ const PostUpload = () => { setSearchTerm(''); }; + // YouTube Player 설정 + const onPlayerReady = (event) => { + setPlayer(event.target); + setDuration(event.target.getDuration()); + }; + + // 재생/일시정지 처리 + const handlePlayPause = () => { + if (isPlaying) { + player.pauseVideo(); + clearInterval(intervalRef.current); + } else { + player.playVideo(); + intervalRef.current = setInterval(() => { + setCurrentTime(player.getCurrentTime()); + }, 1000); + } + setIsPlaying(!isPlaying); + }; + + // YouTube API 옵션 설정 + const opts = { + height: '0', + width: '0', + playerVars: { + autoplay: 0, + }, + }; + + // 시간을 MM:SS 형식으로 변환 + const formatTime = (time) => { + const minutes = Math.floor(time / 60); + const seconds = Math.floor(time % 60); + return `${minutes}:${seconds < 10 ? '0' : ''}${seconds}`; + }; + return ( @@ -93,9 +140,32 @@ const PostUpload = () => { {albumData ? albumData.title : '제목'} {albumData ? albumData.artist : '아티스트'} + + + Play/Pause Button + + {formatTime(currentTime)} + player.seekTo(e.target.value)} + /> + {formatTime(duration)} + + {/* YouTube Player */} + {selectedTrack && ( + + )} + {/* 검색 모드: 앨범을 선택하지 않았을 때만 검색 UI 표시 */} {isSearchMode && !albumData && ( <> @@ -228,12 +298,12 @@ const PostTitleInput = styled.input` font-size: 16px; border: 1px solid #ccc; border-radius: 5px; - width: 90%; + width: 95%; `; const TitleCharCount = styled.p` position: absolute; - bottom: 10px; + bottom: 5px; right: 10px; font-size: 10px; color: #666; @@ -242,13 +312,48 @@ const TitleCharCount = styled.p` const TrackTitle = styled.p` font-size: 20px; font-weight: bold; - margin-bottom: 0; + margin-top: 0; `; const ArtistName = styled.p` font-size: 15px; color: #666; - margin-bottom: 50px; + margin-top: 0; +`; + +const AudioPlayer = styled.div` + display: flex; + align-items: center; + border-top: 1px solid #666; + padding-top: 10px; + margin-top: 10px; +`; + +const PlayPauseButton = styled.button` + background: none; + border: none; + cursor: pointer; + padding: 0; + margin-right: 10px; + img { + width: 40px; + height: 40px; + } + &:disabled { + cursor: not-allowed; + opacity: 0.5; + } +`; + +const ProgressBar = styled.input` + flex-grow: 1; + margin: 0 10px; + height: 5px; +`; + +const TrackTime = styled.span` + font-size: 14px; + color: #666; `; const SearchSection = styled.div` From 7f2458cdc5e697c0d20d74d92b897a02842fb77b Mon Sep 17 00:00:00 2001 From: HyunbinKim Date: Mon, 30 Sep 2024 02:11:18 +0900 Subject: [PATCH 09/26] =?UTF-8?q?#14=20[feat]:=20=ED=8F=AC=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=EC=97=85=EB=A1=9C=EB=93=9C=20=EB=82=B4=EC=97=90?= =?UTF-8?q?=EC=84=9C=20=EA=B2=8C=EC=8B=9C=ED=95=98=EA=B8=B0=20=EB=B2=84?= =?UTF-8?q?=ED=8A=BC=EC=9D=84=20=EA=B5=AC=ED=98=84=ED=95=98=EA=B8=B0=20?= =?UTF-8?q?=EC=9C=84=ED=95=B4=20=EC=97=85=EB=A1=9C=EB=93=9C=20=EB=AA=A8?= =?UTF-8?q?=EB=8B=AC=20=EB=82=B4=EC=9D=98=20=EA=B2=8C=EC=8B=9C=ED=95=98?= =?UTF-8?q?=EA=B8=B0=20=EB=B2=84=ED=8A=BC=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/modals/UploadModal.jsx | 26 -------------------------- 1 file changed, 26 deletions(-) diff --git a/src/components/modals/UploadModal.jsx b/src/components/modals/UploadModal.jsx index 4eed23a..1210144 100644 --- a/src/components/modals/UploadModal.jsx +++ b/src/components/modals/UploadModal.jsx @@ -43,10 +43,6 @@ const UploadModal = ({ onClose }) => { {activeTab === 'track'? : } - {/* Submit Button */} -
- 게시하기 -
); }; @@ -123,25 +119,3 @@ const Content = styled.div` flex-grow: 1; overflow-y: auto; `; - -const Footer = styled.div` - display: flex; - justify-content: flex-end; - padding: 10px 25px; - background-color: #fff; -`; - -const SubmitButton = styled.button` - padding: 5px 20px; - background-color: #000; - color: white; - border: none; - border-radius: 13px; - cursor: pointer; - font-size: 14px; - - &:hover { - background-color: #ccc; - color: black; - } -`; \ No newline at end of file From 3ce38f363fbd4d22bb7424d0578d6f915b4a329f Mon Sep 17 00:00:00 2001 From: HyunbinKim Date: Mon, 30 Sep 2024 02:18:00 +0900 Subject: [PATCH 10/26] =?UTF-8?q?#14=20[feat]:=20=EA=B2=8C=EC=8B=9C?= =?UTF-8?q?=ED=95=98=EA=B8=B0=20=EB=B2=84=ED=8A=BC=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/modals/PostUpload.jsx | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/src/components/modals/PostUpload.jsx b/src/components/modals/PostUpload.jsx index c6d678f..f80e479 100644 --- a/src/components/modals/PostUpload.jsx +++ b/src/components/modals/PostUpload.jsx @@ -211,6 +211,11 @@ const PostUpload = () => { {description.length}/{maxCharLimit}자
+ + {/* 게시하기 버튼 */} + + 게시하기 +
); @@ -223,7 +228,8 @@ export default PostUpload; const PostUploadContainer = styled.div` display: flex; flex-direction: column; - width: 100%; + width: 97%; + height: 100%; background-color: #fff; `; @@ -232,7 +238,7 @@ const ContentWrapper = styled.div` padding: 10px; border-radius: 15px; width: 100%; - max-width: 700px; + max-width: 650px; margin: 0 auto; display: flex; flex-direction: column; @@ -452,3 +458,20 @@ const CharCount = styled.p` text-align: right; margin-top: 5px; `; + +const PostButtonWrapper = styled.div` + display: flex; + justify-content: flex-end; + width: 100%; + margin-top: 20px; +`; + +const PostButton = styled.button` + padding: 3px 20px; + background-color: black; + color: white; + border: none; + border-radius: 10px; + cursor: pointer; + font-size: 16px; +`; From 16fd4d78531e1181c5c83483672e81968e7cdfd6 Mon Sep 17 00:00:00 2001 From: HyunbinKim Date: Mon, 30 Sep 2024 03:29:48 +0900 Subject: [PATCH 11/26] =?UTF-8?q?#14=20[feat]:=20=EC=9C=A0=ED=9A=A8?= =?UTF-8?q?=EC=84=B1=20=EA=B2=80=EC=82=AC=20=EC=9D=B4=ED=9B=84=20=EB=B6=80?= =?UTF-8?q?=EB=AA=A8=20=EC=BB=B4=ED=8F=AC=EB=84=8C=ED=8A=B8=EB=A1=9C=20?= =?UTF-8?q?=EC=9D=B4=EB=8F=99=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/modals/PostUpload.jsx | 95 ++++++++++++++++++++++++---- 1 file changed, 83 insertions(+), 12 deletions(-) diff --git a/src/components/modals/PostUpload.jsx b/src/components/modals/PostUpload.jsx index f80e479..abad27a 100644 --- a/src/components/modals/PostUpload.jsx +++ b/src/components/modals/PostUpload.jsx @@ -18,11 +18,12 @@ const PostUpload = () => { const [player, setPlayer] = useState(null); const [currentTime, setCurrentTime] = useState(0); const [duration, setDuration] = useState(0); + const [errorMessage, setErrorMessage] = useState(''); const maxCharLimit = 300; const maxTitleCharLimit = 20; const intervalRef = useRef(null); - + const descriptionInputRef = useRef(null); // 앨범 검색 기능 const searchAlbums = async () => { const options = { @@ -55,6 +56,10 @@ const PostUpload = () => { setSelectedTrack(album); setSearchResults([]); setIsSearchMode(false); + + if (descriptionInputRef.current) { + descriptionInputRef.current.focus(); + } }; // 앨범을 변경하려면 클릭 시 원래 상태로 돌아가도록 처리 @@ -100,6 +105,53 @@ const PostUpload = () => { return `${minutes}:${seconds < 10 ? '0' : ''}${seconds}`; }; + // 게시하기 버튼 클릭 시 유효성 검사 및 성공 시 처리 + const handlePost = () => { + // 포스트 제목 유효성 검사 + if (postTitle.trim() === '') { + setErrorMessage('포스트 제목을 입력해 주세요.'); + return; + } + + // 추천 앨범 업로드 유효성 검사 + if (!albumData) { + setErrorMessage('추천 포스트를 업로드 하세요.'); + return; + } + + // 노래 소개 유효성 검사 + if (description.trim() === '') { + setErrorMessage('노래 소개를 입력하세요.'); + return; + } + + setErrorMessage(''); + + // 새로운 게시물 객체 생성 + const newPost = { + postTitle: postTitle, + title: albumData.title, + artist: albumData.artist, + description: description, + coverUrl: albumData.coverUrl, + }; + + // 부모 컴포넌트에 새 게시물 전달 (포스트 게시 할때 이 부분을 참고하면 될 것 같습니다.) + // onPostSuccess 함수가 정의된 경우에만 호출 + if (typeof onPostSuccess === 'function') { + onPostSuccess(newPost); + alert('포스트가 성공적으로 업로드되었습니다!'); + } else { + // onPostSuccess가 정의되지 않은 경우 + alert('포스트 업로드에 실패하였습니다!'); + } + + // 상태 초기화 + setPostTitle(''); + setAlbumData(null); + setDescription(''); + }; + return ( @@ -142,7 +194,10 @@ const PostUpload = () => { {albumData ? albumData.artist : '아티스트'} - Play/Pause Button + Play/Pause Button {formatTime(currentTime)} { {/* YouTube Player */} {selectedTrack && ( @@ -202,6 +257,7 @@ const PostUpload = () => { 노래 소개 setDescription(e.target.value)} @@ -211,10 +267,13 @@ const PostUpload = () => { {description.length}/{maxCharLimit}자 - + {/* 게시하기 버튼 */} - 게시하기 + {/* 에러 메시지 표시 */} + {errorMessage && {errorMessage}} + + 게시하기 @@ -249,6 +308,7 @@ const ContentWrapper = styled.div` const AlbumSection = styled.div` display: flex; margin-bottom: 20px; + align-items: flex-start; `; const AlbumPlaceholder = styled.div` @@ -274,7 +334,7 @@ const YouTubeMusicLink = styled.p` display: flex; align-items: center; color: white; - font-size: 11px; + font-size: 12px; font-weight: bold; cursor: pointer; text-align: center; @@ -292,11 +352,12 @@ const TrackDetails = styled.div` flex-direction: column; justify-content: center; width: 100%; + min-width: 200px; `; const PostTitleWrapper = styled.div` position: relative; - margin-bottom: 15px; + margin-bottom: 10px; `; const PostTitleInput = styled.input` @@ -364,7 +425,7 @@ const TrackTime = styled.span` const SearchSection = styled.div` display: flex; - margin-bottom: 20px; + margin-bottom: 13px; justify-content: center; `; @@ -387,13 +448,13 @@ const SearchButton = styled.button` `; const SearchResults = styled.div` - margin-bottom: 20px; + margin-bottom: 10px; `; const AlbumItem = styled.div` display: flex; align-items: center; - margin-bottom: 10px; + margin-bottom: 5px; cursor: pointer; &:hover { background-color: #f1f1f1; @@ -424,13 +485,15 @@ const AlbumArtist = styled.span` const DescriptionSection = styled.div` background-color: #c0afe2; padding: 15px; + margin-top: 5px; border-radius: 10px; border: 1px solid #d1c4e9; + min-height: 150px; `; const DescriptionTitle = styled.p` font-size: 18px; - margin-top: 0%; + margin-top: 0; margin-bottom: 15px; color: white; border-bottom: 2px solid white; @@ -459,11 +522,19 @@ const CharCount = styled.p` margin-top: 5px; `; +const ErrorMessage = styled.p` + color: red; + font-size: 14px; + margin-top: 10px; + margin-bottom: 0px; + margin-right: 25%; +`; + const PostButtonWrapper = styled.div` display: flex; justify-content: flex-end; width: 100%; - margin-top: 20px; + margin-top: 15px; `; const PostButton = styled.button` From 3a17abb0bc6f284e6063c5e12b881d9d66fcecf0 Mon Sep 17 00:00:00 2001 From: HyunbinKim Date: Mon, 30 Sep 2024 03:54:19 +0900 Subject: [PATCH 12/26] =?UTF-8?q?#14=20[fix]:=20=EC=95=A8=EB=B2=94=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD=20=EB=B0=8F=20=EA=B2=8C=EC=8B=9C=ED=95=9C=20?= =?UTF-8?q?=ED=9B=84=20=EC=9E=AC=EC=83=9D=EB=B0=94=20=EB=B6=80=EB=B6=84=20?= =?UTF-8?q?=EC=B4=88=EA=B8=B0=ED=99=94=EB=90=98=EC=A7=80=20=EC=95=8A?= =?UTF-8?q?=EB=8D=98=20=EB=B2=84=EA=B7=B8=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/modals/PostUpload.jsx | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/src/components/modals/PostUpload.jsx b/src/components/modals/PostUpload.jsx index abad27a..7afcd5d 100644 --- a/src/components/modals/PostUpload.jsx +++ b/src/components/modals/PostUpload.jsx @@ -24,6 +24,7 @@ const PostUpload = () => { const intervalRef = useRef(null); const descriptionInputRef = useRef(null); + // 앨범 검색 기능 const searchAlbums = async () => { const options = { @@ -56,14 +57,27 @@ const PostUpload = () => { setSelectedTrack(album); setSearchResults([]); setIsSearchMode(false); + resetPlayer(); if (descriptionInputRef.current) { descriptionInputRef.current.focus(); } }; + // 앨범을 변경하거나 상태 초기화 시 YouTube 플레이어 리셋 + const resetPlayer = () => { + if (player) { + player.stopVideo(); + } + setCurrentTime(0); + setIsPlaying(false); + setDuration(0); + clearInterval(intervalRef.current); + }; + // 앨범을 변경하려면 클릭 시 원래 상태로 돌아가도록 처리 const resetAlbumSelection = () => { + resetPlayer(); setAlbumData(null); setIsSearchMode(true); setSearchTerm(''); @@ -146,7 +160,8 @@ const PostUpload = () => { alert('포스트 업로드에 실패하였습니다!'); } - // 상태 초기화 + // 상태 초기화 및 플레이어 초기화 + resetPlayer(); setPostTitle(''); setAlbumData(null); setDescription(''); From b0aab48fa68b8df89f861dd7851c07fec96960dd Mon Sep 17 00:00:00 2001 From: HyunbinKim Date: Mon, 30 Sep 2024 04:03:35 +0900 Subject: [PATCH 13/26] =?UTF-8?q?#14=20[style]:=20=EC=BD=94=EB=93=9C=20?= =?UTF-8?q?=EC=BB=A8=EB=B2=A4=EC=85=98=20=EA=B7=9C=EC=B9=99=EC=9C=BC?= =?UTF-8?q?=EB=A1=9C=20=EC=BD=94=EB=93=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../icons/{PlayButton.png => play-button.png} | Bin .../icons/{StopButton.png => stop-button.png} | Bin ...beMusicIcon.png => youtube-music-icon.png} | Bin src/components/modals/PostUpload.jsx | 63 +++--------------- 4 files changed, 11 insertions(+), 52 deletions(-) rename src/assets/icons/{PlayButton.png => play-button.png} (100%) rename src/assets/icons/{StopButton.png => stop-button.png} (100%) rename src/assets/icons/{YoutubeMusicIcon.png => youtube-music-icon.png} (100%) diff --git a/src/assets/icons/PlayButton.png b/src/assets/icons/play-button.png similarity index 100% rename from src/assets/icons/PlayButton.png rename to src/assets/icons/play-button.png diff --git a/src/assets/icons/StopButton.png b/src/assets/icons/stop-button.png similarity index 100% rename from src/assets/icons/StopButton.png rename to src/assets/icons/stop-button.png diff --git a/src/assets/icons/YoutubeMusicIcon.png b/src/assets/icons/youtube-music-icon.png similarity index 100% rename from src/assets/icons/YoutubeMusicIcon.png rename to src/assets/icons/youtube-music-icon.png diff --git a/src/components/modals/PostUpload.jsx b/src/components/modals/PostUpload.jsx index 7afcd5d..af23d1f 100644 --- a/src/components/modals/PostUpload.jsx +++ b/src/components/modals/PostUpload.jsx @@ -1,9 +1,9 @@ import React, { useState, useRef } from 'react'; import styled from 'styled-components'; import axios from 'axios'; -import YoutubeMusicIcon from '../../assets/icons/YoutubeMusicIcon.png'; -import StopButtonIcon from '../../assets/icons/StopButton.png'; -import PlayButtonIcon from '../../assets/icons/PlayButton.png'; +import youtubeMusicIcon from '../../assets/icons/youtube-music-icon.png'; +import stopButtonIcon from '../../assets/icons/stop-button.png'; +import playButtonIcon from '../../assets/icons/play-button.png'; import YouTube from 'react-youtube'; const PostUpload = () => { @@ -64,7 +64,7 @@ const PostUpload = () => { } }; - // 앨범을 변경하거나 상태 초기화 시 YouTube 플레이어 리셋 + // 플레이어 리셋 const resetPlayer = () => { if (player) { player.stopVideo(); @@ -75,7 +75,7 @@ const PostUpload = () => { clearInterval(intervalRef.current); }; - // 앨범을 변경하려면 클릭 시 원래 상태로 돌아가도록 처리 + // 앨범 선택 초기화 const resetAlbumSelection = () => { resetPlayer(); setAlbumData(null); @@ -103,15 +103,6 @@ const PostUpload = () => { setIsPlaying(!isPlaying); }; - // YouTube API 옵션 설정 - const opts = { - height: '0', - width: '0', - playerVars: { - autoplay: 0, - }, - }; - // 시간을 MM:SS 형식으로 변환 const formatTime = (time) => { const minutes = Math.floor(time / 60); @@ -121,19 +112,16 @@ const PostUpload = () => { // 게시하기 버튼 클릭 시 유효성 검사 및 성공 시 처리 const handlePost = () => { - // 포스트 제목 유효성 검사 if (postTitle.trim() === '') { setErrorMessage('포스트 제목을 입력해 주세요.'); return; } - // 추천 앨범 업로드 유효성 검사 if (!albumData) { setErrorMessage('추천 포스트를 업로드 하세요.'); return; } - // 노래 소개 유효성 검사 if (description.trim() === '') { setErrorMessage('노래 소개를 입력하세요.'); return; @@ -141,7 +129,6 @@ const PostUpload = () => { setErrorMessage(''); - // 새로운 게시물 객체 생성 const newPost = { postTitle: postTitle, title: albumData.title, @@ -150,17 +137,13 @@ const PostUpload = () => { coverUrl: albumData.coverUrl, }; - // 부모 컴포넌트에 새 게시물 전달 (포스트 게시 할때 이 부분을 참고하면 될 것 같습니다.) - // onPostSuccess 함수가 정의된 경우에만 호출 if (typeof onPostSuccess === 'function') { onPostSuccess(newPost); alert('포스트가 성공적으로 업로드되었습니다!'); } else { - // onPostSuccess가 정의되지 않은 경우 alert('포스트 업로드에 실패하였습니다!'); } - // 상태 초기화 및 플레이어 초기화 resetPlayer(); setPostTitle(''); setAlbumData(null); @@ -170,20 +153,13 @@ const PostUpload = () => { return ( - {/* 앨범 가져오기 버튼: 앨범이 선택되지 않았을 때만 표시 */} - setIsSearchMode(true) - }> + setIsSearchMode(true)}> {albumData ? ( ) : ( - + YouTube Music에서
앨범 가져오기
@@ -191,7 +167,6 @@ const PostUpload = () => {
- {/* 포스트 제목 필드와 인풋박스 */} { {albumData ? albumData.artist : '아티스트'} - Play/Pause Button + Play/Pause Button {formatTime(currentTime)} {
- {/* YouTube Player */} {selectedTrack && ( - + )} - {/* 검색 모드: 앨범을 선택하지 않았을 때만 검색 UI 표시 */} {isSearchMode && !albumData && ( <> @@ -249,13 +215,10 @@ const PostUpload = () => { 검색 - {/* 검색 결과 */} {searchResults.length > 0 && ( {searchResults.map((album) => ( - selectAlbum(album)}> + selectAlbum(album)}> {album.title} @@ -268,7 +231,6 @@ const PostUpload = () => { )} - {/* 설명 입력 섹션 */} 노래 소개 { - {/* 게시하기 버튼 */} - {/* 에러 메시지 표시 */} {errorMessage && {errorMessage}} - 게시하기
@@ -560,4 +519,4 @@ const PostButton = styled.button` border-radius: 10px; cursor: pointer; font-size: 16px; -`; +`; \ No newline at end of file From 9ab3ad1e29f15c26aa6e177ddcdd22274b3e9099 Mon Sep 17 00:00:00 2001 From: HyunbinKim Date: Mon, 30 Sep 2024 04:06:54 +0900 Subject: [PATCH 14/26] =?UTF-8?q?#14=20[style]:=20=EC=BD=94=EB=93=9C=20?= =?UTF-8?q?=EC=A3=BC=EC=84=9D=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/modals/PostUpload.jsx | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/components/modals/PostUpload.jsx b/src/components/modals/PostUpload.jsx index af23d1f..c0a45c7 100644 --- a/src/components/modals/PostUpload.jsx +++ b/src/components/modals/PostUpload.jsx @@ -128,7 +128,8 @@ const PostUpload = () => { } setErrorMessage(''); - + + // 새로운 게시물 객체 생성 const newPost = { postTitle: postTitle, title: albumData.title, @@ -137,13 +138,15 @@ const PostUpload = () => { coverUrl: albumData.coverUrl, }; + // 부모 컴포넌트에 새 게시물 전달 (포스트 게시 할때 이 부분을 참고하면 될 것 같습니다.) if (typeof onPostSuccess === 'function') { onPostSuccess(newPost); alert('포스트가 성공적으로 업로드되었습니다!'); } else { alert('포스트 업로드에 실패하였습니다!'); } - + + // 상태 초기화 및 플레이어 초기화 resetPlayer(); setPostTitle(''); setAlbumData(null); From e640025557c7eeb30b5ffea0932c3a7d7580c790 Mon Sep 17 00:00:00 2001 From: HyunbinKim Date: Mon, 30 Sep 2024 18:08:34 +0900 Subject: [PATCH 15/26] =?UTF-8?q?#14=20[feat]:=20=EC=9E=AC=EC=83=9D?= =?UTF-8?q?=EB=B0=94=20=EA=B8=B0=EB=8A=A5=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 1 - src/components/modals/PostUpload.jsx | 178 +++++++++------------------ src/utils/api.js | 2 +- 3 files changed, 56 insertions(+), 125 deletions(-) diff --git a/package.json b/package.json index 61966ff..4bfa916 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,6 @@ "react-dom": "^18.3.1", "react-router-dom": "^6.26.2", "react-youtube": "^10.1.0", - "proxy": "https://youtube-music-api3.p.rapidapi.com", "styled-components": "^6.1.13" }, "devDependencies": { diff --git a/src/components/modals/PostUpload.jsx b/src/components/modals/PostUpload.jsx index c0a45c7..8110bd7 100644 --- a/src/components/modals/PostUpload.jsx +++ b/src/components/modals/PostUpload.jsx @@ -6,7 +6,7 @@ import stopButtonIcon from '../../assets/icons/stop-button.png'; import playButtonIcon from '../../assets/icons/play-button.png'; import YouTube from 'react-youtube'; -const PostUpload = () => { +const PostUpload = ({ onPostSuccess }) => { const [albumData, setAlbumData] = useState(null); const [description, setDescription] = useState(''); const [isPlaying, setIsPlaying] = useState(false); @@ -16,13 +16,10 @@ const PostUpload = () => { const [searchResults, setSearchResults] = useState([]); const [isSearchMode, setIsSearchMode] = useState(false); const [player, setPlayer] = useState(null); - const [currentTime, setCurrentTime] = useState(0); - const [duration, setDuration] = useState(0); const [errorMessage, setErrorMessage] = useState(''); const maxCharLimit = 300; const maxTitleCharLimit = 20; - const intervalRef = useRef(null); const descriptionInputRef = useRef(null); // 앨범 검색 기능 @@ -69,10 +66,7 @@ const PostUpload = () => { if (player) { player.stopVideo(); } - setCurrentTime(0); setIsPlaying(false); - setDuration(0); - clearInterval(intervalRef.current); }; // 앨범 선택 초기화 @@ -86,30 +80,18 @@ const PostUpload = () => { // YouTube Player 설정 const onPlayerReady = (event) => { setPlayer(event.target); - setDuration(event.target.getDuration()); }; // 재생/일시정지 처리 const handlePlayPause = () => { if (isPlaying) { player.pauseVideo(); - clearInterval(intervalRef.current); } else { player.playVideo(); - intervalRef.current = setInterval(() => { - setCurrentTime(player.getCurrentTime()); - }, 1000); } setIsPlaying(!isPlaying); }; - // 시간을 MM:SS 형식으로 변환 - const formatTime = (time) => { - const minutes = Math.floor(time / 60); - const seconds = Math.floor(time % 60); - return `${minutes}:${seconds < 10 ? '0' : ''}${seconds}`; - }; - // 게시하기 버튼 클릭 시 유효성 검사 및 성공 시 처리 const handlePost = () => { if (postTitle.trim() === '') { @@ -128,8 +110,7 @@ const PostUpload = () => { } setErrorMessage(''); - - // 새로운 게시물 객체 생성 + const newPost = { postTitle: postTitle, title: albumData.title, @@ -138,15 +119,13 @@ const PostUpload = () => { coverUrl: albumData.coverUrl, }; - // 부모 컴포넌트에 새 게시물 전달 (포스트 게시 할때 이 부분을 참고하면 될 것 같습니다.) if (typeof onPostSuccess === 'function') { onPostSuccess(newPost); alert('포스트가 성공적으로 업로드되었습니다!'); } else { alert('포스트 업로드에 실패하였습니다!'); } - - // 상태 초기화 및 플레이어 초기화 + resetPlayer(); setPostTitle(''); setAlbumData(null); @@ -156,57 +135,54 @@ const PostUpload = () => { return ( + + setPostTitle(e.target.value)} + maxLength={maxTitleCharLimit} + /> + + {postTitle.length}/{maxTitleCharLimit}자 + + + - setIsSearchMode(true)}> + setIsSearchMode(true)}> {albumData ? ( ) : ( - + YouTube Music에서
앨범 가져오기
)}
- - - setPostTitle(e.target.value)} - maxLength={maxTitleCharLimit} - /> - - {postTitle.length}/{maxTitleCharLimit}자 - - - - {albumData ? albumData.title : '제목'} - {albumData ? albumData.artist : '아티스트'} - - - Play/Pause Button - - {formatTime(currentTime)} - player.seekTo(e.target.value)} + {albumData && ( + + Play/Pause Button - {formatTime(duration)} - - + + )}
{selectedTrack && ( - + )} - {isSearchMode && !albumData && ( + {isSearchMode && ( <> { }); return response.data; // 응답 데이터 반환 -}; \ No newline at end of file +}; From 72e4c59fda9cb328e99500cf1442c67bf63b036d Mon Sep 17 00:00:00 2001 From: HyunbinKim Date: Mon, 30 Sep 2024 18:16:06 +0900 Subject: [PATCH 16/26] =?UTF-8?q?#14=20[feat]:=20=EC=B5=9C=EB=8C=80=20?= =?UTF-8?q?=EC=95=A8=EB=B2=94=20=EC=88=98=20=EC=A7=80=EC=A0=95=20=EB=B0=8F?= =?UTF-8?q?=20=EC=8B=A4=EC=8B=9C=EA=B0=84=20=ED=91=9C=EC=8B=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/modals/PostUpload.jsx | 258 +++++++++++++++++---------- 1 file changed, 159 insertions(+), 99 deletions(-) diff --git a/src/components/modals/PostUpload.jsx b/src/components/modals/PostUpload.jsx index 8110bd7..ffae121 100644 --- a/src/components/modals/PostUpload.jsx +++ b/src/components/modals/PostUpload.jsx @@ -7,7 +7,7 @@ import playButtonIcon from '../../assets/icons/play-button.png'; import YouTube from 'react-youtube'; const PostUpload = ({ onPostSuccess }) => { - const [albumData, setAlbumData] = useState(null); + const [albums, setAlbums] = useState([]); const [description, setDescription] = useState(''); const [isPlaying, setIsPlaying] = useState(false); const [selectedTrack, setSelectedTrack] = useState(null); @@ -17,6 +17,7 @@ const PostUpload = ({ onPostSuccess }) => { const [isSearchMode, setIsSearchMode] = useState(false); const [player, setPlayer] = useState(null); const [errorMessage, setErrorMessage] = useState(''); + const maxAlbums = 5; const maxCharLimit = 300; const maxTitleCharLimit = 20; @@ -46,35 +47,33 @@ const PostUpload = ({ onPostSuccess }) => { // 앨범 선택 시 처리 const selectAlbum = (album) => { - setAlbumData({ - title: album.title, - artist: album.author, - coverUrl: album.thumbnail, - }); - setSelectedTrack(album); + if (albums.length < maxAlbums) { + setAlbums([...albums, { + title: album.title, + artist: album.author, + coverUrl: album.thumbnail, + videoId: album.videoId + }]); + } else { + alert(`최대 ${maxAlbums}개의 앨범만 추가할 수 있습니다.`); + } setSearchResults([]); setIsSearchMode(false); - resetPlayer(); - - if (descriptionInputRef.current) { - descriptionInputRef.current.focus(); - } }; - // 플레이어 리셋 - const resetPlayer = () => { - if (player) { - player.stopVideo(); + // 재생/일시정지 처리 + const handlePlayPause = (album) => { + if (selectedTrack && selectedTrack.videoId === album.videoId && isPlaying) { + player.pauseVideo(); + setIsPlaying(false); + } else { + if (player) { + player.loadVideoById(album.videoId); + player.playVideo(); + setIsPlaying(true); + } + setSelectedTrack(album); } - setIsPlaying(false); - }; - - // 앨범 선택 초기화 - const resetAlbumSelection = () => { - resetPlayer(); - setAlbumData(null); - setIsSearchMode(true); - setSearchTerm(''); }; // YouTube Player 설정 @@ -82,14 +81,11 @@ const PostUpload = ({ onPostSuccess }) => { setPlayer(event.target); }; - // 재생/일시정지 처리 - const handlePlayPause = () => { - if (isPlaying) { - player.pauseVideo(); - } else { - player.playVideo(); - } - setIsPlaying(!isPlaying); + // 앨범 삭제 처리 + const removeAlbum = (index) => { + const updatedAlbums = [...albums]; + updatedAlbums.splice(index, 1); + setAlbums(updatedAlbums); }; // 게시하기 버튼 클릭 시 유효성 검사 및 성공 시 처리 @@ -99,7 +95,7 @@ const PostUpload = ({ onPostSuccess }) => { return; } - if (!albumData) { + if (albums.length === 0) { setErrorMessage('추천 포스트를 업로드 하세요.'); return; } @@ -113,10 +109,8 @@ const PostUpload = ({ onPostSuccess }) => { const newPost = { postTitle: postTitle, - title: albumData.title, - artist: albumData.artist, + albums: albums, description: description, - coverUrl: albumData.coverUrl, }; if (typeof onPostSuccess === 'function') { @@ -126,9 +120,8 @@ const PostUpload = ({ onPostSuccess }) => { alert('포스트 업로드에 실패하였습니다!'); } - resetPlayer(); + setAlbums([]); setPostTitle(''); - setAlbumData(null); setDescription(''); }; @@ -148,11 +141,16 @@ const PostUpload = ({ onPostSuccess }) => { - - setIsSearchMode(true)}> - {albumData ? ( - - ) : ( + {/* 앨범 목록 섹션 */} + + +

앨범 목록

+ + {albums.length}/{maxAlbums} 앨범 추가됨 + +
+ + setIsSearchMode(true)}> { YouTube Music에서
앨범 가져오기
- )} -
- - {albumData && ( - - Play/Pause Button - - )} -
- + + + {albums.map((album, index) => ( + + + + handlePlayPause(album)}> + Play/Pause Button + + removeAlbum(index)}>× + + + {album.title} + {album.artist} + + + ))} + + + + {/* YouTube Player */} {selectedTrack && ( { /> )} + {/* 앨범 검색 모드 */} {isSearchMode && ( <> @@ -197,7 +211,10 @@ const PostUpload = ({ onPostSuccess }) => { {searchResults.length > 0 && ( {searchResults.map((album) => ( - selectAlbum(album)}> + selectAlbum(album)} + > {album.title} @@ -210,6 +227,7 @@ const PostUpload = ({ onPostSuccess }) => { )} + {/* 노래 소개 섹션 */} 노래 소개 { + {/* 게시하기 버튼 */} {errorMessage && {errorMessage}} 게시하기 @@ -236,7 +255,6 @@ const PostUpload = ({ onPostSuccess }) => { export default PostUpload; // Styled components - const PostUploadContainer = styled.div` display: flex; flex-direction: column; @@ -279,57 +297,114 @@ const TitleCharCount = styled.p` color: #666; `; -const AlbumSection = styled.div` - display: flex; - margin-bottom: 20px; - align-items: flex-start; +const AlbumListSection = styled.div` + margin-top: 20px; `; -const AlbumPlaceholder = styled.div` - background-color: #c0afe2; - height: 200px; - width: 300px; +const AlbumListHeader = styled.div` display: flex; + justify-content: space-between; align-items: center; - justify-content: center; - margin-right: 20px; +`; + +const AlbumCount = styled.p` + font-size: 14px; + color: #666; +`; + +const AlbumList = styled.div` + display: flex; + flex-wrap: nowrap; + overflow-x: auto; + margin-top: 10px; +`; + +const AlbumItemWrapper = styled.div` + display: flex; + flex-direction: column; + margin-right: 15px; +`; + +const AlbumCoverWrapper = styled.div` + position: relative; + width: 150px; + height: 150px; border-radius: 10px; - cursor: pointer; + overflow: hidden; `; const AlbumCover = styled.img` width: 100%; height: 100%; - border-radius: 10px; object-fit: cover; `; +const PlayPauseButton = styled.button` + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + background: none; + border: none; + cursor: pointer; + img { + width: 40px; + height: 40px; + } +`; + +const RemoveButton = styled.button` + position: absolute; + top: 5px; + right: 5px; + background: none; + border: none; + font-size: 20px; + color: white; + cursor: pointer; +`; + +const AlbumInfo = styled.div` + text-align: center; + margin-top: 10px; +`; + +const AlbumTitle = styled.p` + font-size: 14px; + font-weight: bold; +`; + +const AlbumArtist = styled.p` + font-size: 12px; + color: #666; +`; + +const AlbumPlaceholder = styled.div` + background-color: #c0afe2; + height: 150px; + width: 150px; + display: flex; + align-items: center; + justify-content: center; + border-radius: 10px; + cursor: pointer; +`; + const YouTubeMusicLink = styled.p` display: flex; + flex-direction: column; align-items: center; color: white; font-size: 12px; font-weight: bold; cursor: pointer; text-align: center; - margin-right: 5%; `; const YoutubeMusicIconImage = styled.img` width: 30px; height: 30px; - margin-right: 7px; -`; - -const PlayPauseButton = styled.button` - background: none; - border: none; - cursor: pointer; - padding: 0; - img { - width: 40px; - height: 40px; - } + margin-bottom: 10px; `; const SearchSection = styled.div` @@ -376,21 +451,6 @@ const AlbumThumbnail = styled.img` margin-right: 10px; `; -const AlbumInfo = styled.div` - display: flex; - flex-direction: column; -`; - -const AlbumTitle = styled.p` - font-size: 16px; - margin: 0; -`; - -const AlbumArtist = styled.span` - font-size: 12px; - color: #666; -`; - const DescriptionSection = styled.div` background-color: #c0afe2; padding: 15px; From 27efa5ccef716fda9684032dfce4931429d6b61a Mon Sep 17 00:00:00 2001 From: HyunbinKim Date: Mon, 30 Sep 2024 18:16:13 +0900 Subject: [PATCH 17/26] =?UTF-8?q?#14=20[feat]:=20=EC=B5=9C=EB=8C=80=20?= =?UTF-8?q?=EC=95=A8=EB=B2=94=20=EC=88=98=20=EC=A7=80=EC=A0=95=20=EB=B0=8F?= =?UTF-8?q?=20=EC=8B=A4=EC=8B=9C=EA=B0=84=20=ED=91=9C=EC=8B=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/modals/PostUpload.jsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/modals/PostUpload.jsx b/src/components/modals/PostUpload.jsx index ffae121..b903960 100644 --- a/src/components/modals/PostUpload.jsx +++ b/src/components/modals/PostUpload.jsx @@ -387,6 +387,7 @@ const AlbumPlaceholder = styled.div` align-items: center; justify-content: center; border-radius: 10px; + margin-right: 15px; cursor: pointer; `; From e9fe3fd7200eddcef5278844e9d4a2cc7a4ea806 Mon Sep 17 00:00:00 2001 From: HyunbinKim Date: Mon, 30 Sep 2024 18:20:45 +0900 Subject: [PATCH 18/26] =?UTF-8?q?#14=20[feat]:=20AlbumPlaceholder=20?= =?UTF-8?q?=ED=81=AC=EA=B8=B0=20=EA=B3=A0=EC=A0=95=20=EA=B8=B0=EB=8A=A5=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/modals/PostUpload.jsx | 53 +++++++++++++++++++--------- 1 file changed, 36 insertions(+), 17 deletions(-) diff --git a/src/components/modals/PostUpload.jsx b/src/components/modals/PostUpload.jsx index b903960..d55952b 100644 --- a/src/components/modals/PostUpload.jsx +++ b/src/components/modals/PostUpload.jsx @@ -48,12 +48,15 @@ const PostUpload = ({ onPostSuccess }) => { // 앨범 선택 시 처리 const selectAlbum = (album) => { if (albums.length < maxAlbums) { - setAlbums([...albums, { - title: album.title, - artist: album.author, - coverUrl: album.thumbnail, - videoId: album.videoId - }]); + setAlbums([ + ...albums, + { + title: album.title, + artist: album.author, + coverUrl: album.thumbnail, + videoId: album.videoId, + }, + ]); } else { alert(`최대 ${maxAlbums}개의 앨범만 추가할 수 있습니다.`); } @@ -76,6 +79,15 @@ const PostUpload = ({ onPostSuccess }) => { } }; + // 새로운 곡을 재생할 때 기존 곡 멈추기 + const playNewAlbum = (album) => { + if (player && selectedTrack && selectedTrack.videoId !== album.videoId) { + player.stopVideo(); + setIsPlaying(false); + } + handlePlayPause(album); + }; + // YouTube Player 설정 const onPlayerReady = (event) => { setPlayer(event.target); @@ -165,17 +177,21 @@ const PostUpload = ({ onPostSuccess }) => { - handlePlayPause(album)}> + playNewAlbum(album)}> Play/Pause Button - removeAlbum(index)}>× + removeAlbum(index)}> + × + {album.title} @@ -213,8 +229,7 @@ const PostUpload = ({ onPostSuccess }) => { {searchResults.map((album) => ( selectAlbum(album)} - > + onClick={() => selectAlbum(album)}> {album.title} @@ -278,7 +293,7 @@ const ContentWrapper = styled.div` const PostTitleWrapper = styled.div` position: relative; - margin-bottom: 10px; + margin-bottom: 0; `; const PostTitleInput = styled.input` @@ -291,14 +306,15 @@ const PostTitleInput = styled.input` const TitleCharCount = styled.p` position: absolute; - bottom: 5px; + bottom: 1px; right: 10px; - font-size: 10px; + font-size: 13px; color: #666; `; +/* 앨범 리스트 섹션 */ const AlbumListSection = styled.div` - margin-top: 20px; + margin-top: 0; `; const AlbumListHeader = styled.div` @@ -315,7 +331,7 @@ const AlbumCount = styled.p` const AlbumList = styled.div` display: flex; flex-wrap: nowrap; - overflow-x: auto; + overflow-x: auto; margin-top: 10px; `; @@ -386,7 +402,9 @@ const AlbumPlaceholder = styled.div` display: flex; align-items: center; justify-content: center; + flex-shrink: 0; border-radius: 10px; + margin-bottom: 15px; margin-right: 15px; cursor: pointer; `; @@ -408,6 +426,7 @@ const YoutubeMusicIconImage = styled.img` margin-bottom: 10px; `; +/* 검색 및 노래 소개 */ const SearchSection = styled.div` display: flex; margin-bottom: 13px; @@ -515,4 +534,4 @@ const PostButton = styled.button` border-radius: 10px; cursor: pointer; font-size: 16px; -`; \ No newline at end of file +`; From da73e253dac688893190a23b79cbc7b4c1c35ac1 Mon Sep 17 00:00:00 2001 From: HyunbinKim Date: Mon, 30 Sep 2024 18:24:43 +0900 Subject: [PATCH 19/26] =?UTF-8?q?#14=20[feat]:=20=EC=9E=AC=EC=83=9D=20?= =?UTF-8?q?=EC=A4=91=EC=9D=B8=20=EC=95=A8=EB=B2=941=EC=9D=84=20=EB=A9=88?= =?UTF-8?q?=EC=B6=94=EA=B3=A0=20=EC=95=A8=EB=B2=942=EB=A5=BC=20=EC=9E=AC?= =?UTF-8?q?=EC=83=9D=ED=95=98=EA=B8=B0=20=EC=9C=84=ED=95=9C=20=EA=B8=B0?= =?UTF-8?q?=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/modals/PostUpload.jsx | 62 ++++++++++++++++++---------- 1 file changed, 41 insertions(+), 21 deletions(-) diff --git a/src/components/modals/PostUpload.jsx b/src/components/modals/PostUpload.jsx index d55952b..281660c 100644 --- a/src/components/modals/PostUpload.jsx +++ b/src/components/modals/PostUpload.jsx @@ -83,7 +83,6 @@ const PostUpload = ({ onPostSuccess }) => { const playNewAlbum = (album) => { if (player && selectedTrack && selectedTrack.videoId !== album.videoId) { player.stopVideo(); - setIsPlaying(false); } handlePlayPause(album); }; @@ -162,16 +161,19 @@ const PostUpload = ({ onPostSuccess }) => { - setIsSearchMode(true)}> - - - YouTube Music에서
- 앨범 가져오기 -
-
+ {/* AlbumPlaceholder가 조건부 렌더링되도록 수정 */} + {albums.length < maxAlbums && ( + setIsSearchMode(true)}> + + + YouTube Music에서
+ 앨범 가져오기 +
+
+ )} {albums.map((album, index) => ( @@ -189,9 +191,7 @@ const PostUpload = ({ onPostSuccess }) => { alt="Play/Pause Button" /> - removeAlbum(index)}> - × - + removeAlbum(index)}>× {album.title} @@ -206,7 +206,7 @@ const PostUpload = ({ onPostSuccess }) => { {selectedTrack && ( )} @@ -229,12 +229,13 @@ const PostUpload = ({ onPostSuccess }) => { {searchResults.map((album) => ( selectAlbum(album)}> + onClick={() => selectAlbum(album)} + > - - {album.title} - {album.author} - + + {album.title} + {album.author} + ))}
@@ -269,7 +270,9 @@ const PostUpload = ({ onPostSuccess }) => { export default PostUpload; + // Styled components + const PostUploadContainer = styled.div` display: flex; flex-direction: column; @@ -383,6 +386,7 @@ const RemoveButton = styled.button` const AlbumInfo = styled.div` text-align: center; margin-top: 10px; + width: 150px; `; const AlbumTitle = styled.p` @@ -471,6 +475,22 @@ const AlbumThumbnail = styled.img` margin-right: 10px; `; +const AlbumInfoLeftAligned = styled.div` + text-align: left; /* 왼쪽 정렬 */ +`; + +const AlbumTitleLeftAligned = styled.p` + font-size: 14px; + font-weight: bold; + max-width: 100%; +`; + +const AlbumArtistLeftAligned = styled.p` + font-size: 12px; + color: #666; + max-width: 100%; +`; + const DescriptionSection = styled.div` background-color: #c0afe2; padding: 15px; @@ -534,4 +554,4 @@ const PostButton = styled.button` border-radius: 10px; cursor: pointer; font-size: 16px; -`; +`; \ No newline at end of file From 0d25b5a3c52f57f43e7f2d6e7ece8d8041e1042d Mon Sep 17 00:00:00 2001 From: HyunbinKim Date: Mon, 30 Sep 2024 18:25:50 +0900 Subject: [PATCH 20/26] =?UTF-8?q?#14=20[feat]:=20=EC=A4=91=EB=B3=B5=20?= =?UTF-8?q?=EC=95=A8=EB=B2=94=20=EC=97=AC=EB=B6=80=20=ED=99=95=EC=9D=B8=20?= =?UTF-8?q?=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/modals/PostUpload.jsx | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/components/modals/PostUpload.jsx b/src/components/modals/PostUpload.jsx index 281660c..b5194a2 100644 --- a/src/components/modals/PostUpload.jsx +++ b/src/components/modals/PostUpload.jsx @@ -47,6 +47,14 @@ const PostUpload = ({ onPostSuccess }) => { // 앨범 선택 시 처리 const selectAlbum = (album) => { + // 중복 여부 확인 + const isDuplicate = albums.some(existingAlbum => existingAlbum.videoId === album.videoId); + + if (isDuplicate) { + alert('중복된 앨범입니다.'); + return; + } + if (albums.length < maxAlbums) { setAlbums([ ...albums, From 71e53f7127ff611c008923f0925942cce9de116b Mon Sep 17 00:00:00 2001 From: HyunbinKim Date: Tue, 1 Oct 2024 11:21:22 +0900 Subject: [PATCH 21/26] =?UTF-8?q?#14=20[feat]:=20=ED=8F=AC=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=EA=B2=8C=EC=8B=9C=20=EB=B2=84=ED=8A=BC=EC=97=90?= =?UTF-8?q?=EC=84=9C=20API=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/modals/PostUpload.jsx | 66 +++++++++++++++++----------- src/utils/api.js | 16 +++++++ vite.config.js | 8 ++-- 3 files changed, 60 insertions(+), 30 deletions(-) diff --git a/src/components/modals/PostUpload.jsx b/src/components/modals/PostUpload.jsx index b5194a2..e8ddbd0 100644 --- a/src/components/modals/PostUpload.jsx +++ b/src/components/modals/PostUpload.jsx @@ -5,6 +5,7 @@ import youtubeMusicIcon from '../../assets/icons/youtube-music-icon.png'; import stopButtonIcon from '../../assets/icons/stop-button.png'; import playButtonIcon from '../../assets/icons/play-button.png'; import YouTube from 'react-youtube'; +import { createPost } from '../../utils/api'; // API 함수 불러오기 const PostUpload = ({ onPostSuccess }) => { const [albums, setAlbums] = useState([]); @@ -108,42 +109,56 @@ const PostUpload = ({ onPostSuccess }) => { }; // 게시하기 버튼 클릭 시 유효성 검사 및 성공 시 처리 - const handlePost = () => { - if (postTitle.trim() === '') { - setErrorMessage('포스트 제목을 입력해 주세요.'); + const handlePost = async () => { + if (postTitle.trim() === "albums.length === 0 description.trim() === ") { + setErrorMessage('모든 필드를 채워주세요.'); return; } - - if (albums.length === 0) { - setErrorMessage('추천 포스트를 업로드 하세요.'); - return; - } - - if (description.trim() === '') { - setErrorMessage('노래 소개를 입력하세요.'); + + setErrorMessage(''); + + const token = localStorage.getItem('token'); + + if (!token) { + setErrorMessage('로그인이 필요합니다.'); return; } - - setErrorMessage(''); - - const newPost = { - postTitle: postTitle, + + const postData = { + title: postTitle, albums: albums, description: description, }; - - if (typeof onPostSuccess === 'function') { - onPostSuccess(newPost); + + const formData = new FormData(); + formData.append('title', JSON.stringify(postData)); + formData.append('channelId', "66fb541ced2d3c14a64eb9ee"); + // 첫 번째 앨범의 커버 이미지 URL을 사용 + if (albums[0]?.coverUrl) { + formData.append('image', albums[0].coverUrl); + } + + try { + const response = await createPost(formData, token); + console.log('Post uploaded successfully:', response); + alert('포스트가 성공적으로 업로드되었습니다!'); - } else { - alert('포스트 업로드에 실패하였습니다!'); + + // Reset form state + setAlbums([]); + setPostTitle(''); + setDescription(''); + + if (typeof onPostSuccess === 'function') { + onPostSuccess(response); + } + } catch (error) { + console.error('포스트 업로드 실패:', error); + setErrorMessage(error.response?.data?.message || '포스트 업로드에 실패했습니다.'); } - - setAlbums([]); - setPostTitle(''); - setDescription(''); }; + return ( @@ -278,7 +293,6 @@ const PostUpload = ({ onPostSuccess }) => { export default PostUpload; - // Styled components const PostUploadContainer = styled.div` diff --git a/src/utils/api.js b/src/utils/api.js index 94ecbab..18d2140 100644 --- a/src/utils/api.js +++ b/src/utils/api.js @@ -37,3 +37,19 @@ export const getUserData = async (userId, token) => { return response.data; // 응답 데이터 반환 }; + +// 포스트 작성 API 호출 +export const createPost = async (formData, token) => { + try { + const response = await axios.post('/api/posts/create', formData, { + headers: { + Authorization: `Bearer ${token}`, + 'Content-Type': 'multipart/form-data', + }, + }); + return response.data; + } catch (error) { + console.error('Error creating post:', error); + throw error; + } +}; diff --git a/vite.config.js b/vite.config.js index 0cdd8cf..f6505d9 100644 --- a/vite.config.js +++ b/vite.config.js @@ -1,5 +1,5 @@ -import { defineConfig } from 'vite' -import react from '@vitejs/plugin-react-swc' +import { defineConfig } from 'vite'; +import react from '@vitejs/plugin-react-swc'; // // https://vitejs.dev/config/ // export default defineConfig({ @@ -17,8 +17,8 @@ export default defineConfig({ changeOrigin: true, rewrite: (path) => path.replace(/^\/api/, ''), secure: false, - ws: true + ws: true, }, }, }, -}); \ No newline at end of file +}); From 185eac93a0fb228c2d1fbe9590a0bf291c2fd59f Mon Sep 17 00:00:00 2001 From: jny4867 Date: Tue, 1 Oct 2024 11:22:33 +0900 Subject: [PATCH 22/26] =?UTF-8?q?#32[feat]:=20=EB=B9=84=EB=B0=80=EB=B2=88?= =?UTF-8?q?=ED=98=B8=20=EB=B3=80=EA=B2=BD=20=EA=B8=B0=EB=8A=A5=20=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/App.jsx | 4 +- src/auth/Dashboard.jsx | 29 ++++ src/auth/Login.jsx | 1 + src/components/modals/UpdatePasswordModal.jsx | 129 ++++++++++++++++++ src/utils/api.js | 20 +++ 5 files changed, 180 insertions(+), 3 deletions(-) create mode 100644 src/components/modals/UpdatePasswordModal.jsx diff --git a/src/App.jsx b/src/App.jsx index d7da2ff..1daae2b 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -13,14 +13,12 @@ const App = () => { - {/* 네비게이션 버튼 */} + - - {/* 페이지 라우팅 */} } /> } /> diff --git a/src/auth/Dashboard.jsx b/src/auth/Dashboard.jsx index 33cca2f..24a54d8 100644 --- a/src/auth/Dashboard.jsx +++ b/src/auth/Dashboard.jsx @@ -6,10 +6,12 @@ import PostCard from '../components/PostCard'; // 게시물 카드 컴포넌트 import { getUserData } from '../utils/api'; // 사용자 데이터 가져오는 API 함수 import UserCard from '../components/UserCard'; // 사용자 카드 컴포넌트 import UserProfile from '../components/UserProfile'; // 사용자 프로필 컴포넌트 +import UpdatePasswordModal from '../components/modals/UpdatePasswordModal'; const Dashboard = () => { const [user, setUser] = useState(null); const [isModalOpen, setIsModalOpen] = useState(false); + const [isPasswordModalOpen, setIsPasswordModalOpen] = useState(false); const [isLoading, setIsLoading] = useState(true); const [error, setError] = useState(null); const navigate = useNavigate(); @@ -90,6 +92,9 @@ const Dashboard = () => { const openModalHandler = () => setIsModalOpen(true); const closeModalHandler = () => setIsModalOpen(false); + const openPasswordModalHandler = () => setIsPasswordModalOpen(true); + const closePasswordModalHandler = () => setIsPasswordModalOpen(false); + if (isLoading) return Loading...; if (error) return {error}; if (!user) @@ -140,6 +145,16 @@ const Dashboard = () => { setUser={updateUserDetails} /> )} + + 비밀번호 수정 + + {isPasswordModalOpen && ( + + )} ); }; @@ -184,6 +199,20 @@ const EditProfileButton = styled.button` } `; +const UpdatePasswordButton = styled.button` + margin-top: 20px; + padding: 10px 20px; + background-color: #6c5dd3; + color: white; + border: none; + border-radius: 5px; + cursor: pointer; + + &:hover { + background-color: #5a4bbd; + } +`; + const LoadingMessage = styled.p` text-align: center; font-size: 18px; diff --git a/src/auth/Login.jsx b/src/auth/Login.jsx index 38cc598..fb22272 100644 --- a/src/auth/Login.jsx +++ b/src/auth/Login.jsx @@ -15,6 +15,7 @@ const Login = () => { localStorage.setItem('token', token); localStorage.setItem('user', JSON.stringify(user)); console.log('로그인 성공:', user); + console.log('로그인 성공:', token); navigate('/dashboard'); } catch (error) { console.error('로그인 실패:', error); diff --git a/src/components/modals/UpdatePasswordModal.jsx b/src/components/modals/UpdatePasswordModal.jsx new file mode 100644 index 0000000..f250d35 --- /dev/null +++ b/src/components/modals/UpdatePasswordModal.jsx @@ -0,0 +1,129 @@ +import React, { useState } from "react"; +import styled from "styled-components"; +import { updatePassword } from "../../utils/api"; // 분리된 API 파일 임포트 + +const UpdatePasswordModal = ({ isOpen, onClose }) => { + const [currentPassword, setCurrentPassword] = useState(""); + const [newPassword, setNewPassword] = useState(""); + const [confirmPassword, setConfirmPassword] = useState(""); + const [error, setError] = useState(null); + + const handlePasswordChange = async () => { + if (newPassword !== confirmPassword) { + setError("새 비밀번호와 확인 비밀번호가 일치하지 않습니다."); + return; + } + + const token = localStorage.getItem("token"); // token 가져오기 + + if (!token) { + setError("로그인이 필요합니다."); + return; + } + + try { + // 비밀번호 변경 API 호출 + await updatePassword(newPassword, token); + alert("비밀번호가 성공적으로 변경되었습니다."); + onClose(); // 모달 닫기 + } catch (err) { + setError(err.message || "비밀번호 변경 실패"); + } + }; + + if (!isOpen) return null; + + return ( + + +

비밀번호 변경

+ {/* setCurrentPassword(e.target.value)} + /> */} + setNewPassword(e.target.value)} + /> + setConfirmPassword(e.target.value)} + /> + {error && {error}} + + +
+
+ ); +}; + +export default UpdatePasswordModal; + +// styled-components 스타일링 +const ModalOverlay = styled.div` + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: rgba(0, 0, 0, 0.5); + display: flex; + justify-content: center; + align-items: center; + z-index: 1000; +`; + +const ModalContent = styled.div` + background: #dac9f8; /* 라벤더 색상 */ + padding: 20px; + border-radius: 8px; + box-shadow: 0 2px 10px rgba(0, 0, 0, 0.2); + max-width: 400px; + width: 100%; + text-align: center; + + h2 { + margin-bottom: 20px; + font-size: 1.5rem; + color: #333; + } +`; + +const Input = styled.input` + width: 90%; + padding: 10px; + margin-bottom: 10px; + border: 1px solid #ccc; + border-radius: 4px; + font-size: 1rem; +`; + +const Button = styled.button` + background: ${(props) => (props.secondary ? "#d3d3d3" : "#b89ae2")}; /* 라벤더 색상 */ + color: ${(props) => (props.secondary ? "#333" : "#fff")}; + border: none; + border-radius: 4px; + padding: 10px 15px; + cursor: pointer; + margin: 10px; + font-size: 1rem; + width: 100px; + + &:hover { + background: ${(props) => (props.secondary ? "#bbb" : "#b89ae2")}; /* 더 진한 라벤더 색상 */ + } +`; + +const ErrorMessage = styled.p` + color: red; + font-size: 0.9rem; + margin-bottom: 10px; +`; \ No newline at end of file diff --git a/src/utils/api.js b/src/utils/api.js index a2a7263..12686c5 100644 --- a/src/utils/api.js +++ b/src/utils/api.js @@ -89,3 +89,23 @@ export const updateUser = async (userId, token, updatedData) => { throw error; } }; + +// 사용자 정보 업데이트 API 호출 +// 비밀번호 업데이트 함수 +export const updatePassword = async (newPassword, token) => { + try { + const response = await axios.put( + `/api/settings/update-password`, + { password: newPassword }, + { + headers: { + Authorization: `Bearer ${token}`, // JWT 토큰 추가 + }, + }, + ); + return response.data; // 응답 데이터 반환 + } catch (error) { + console.error('Error updating password:', error); + throw error; // 에러 던지기 + } +}; From 568addcd979230816afca6af7283bd57eac97c0c Mon Sep 17 00:00:00 2001 From: HyunbinKim Date: Tue, 1 Oct 2024 11:37:35 +0900 Subject: [PATCH 23/26] =?UTF-8?q?#14=20[fix]:=20=EC=95=A8=EB=B2=94=20?= =?UTF-8?q?=EC=82=AD=EC=A0=9C=ED=95=B4=EB=8F=84=20=ED=94=8C=EB=A0=88?= =?UTF-8?q?=EC=9D=B4=20=EB=90=98=EB=8A=94=20=EB=B2=84=EA=B7=B8=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/modals/PostUpload.jsx | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/components/modals/PostUpload.jsx b/src/components/modals/PostUpload.jsx index e8ddbd0..cd94aa3 100644 --- a/src/components/modals/PostUpload.jsx +++ b/src/components/modals/PostUpload.jsx @@ -92,6 +92,8 @@ const PostUpload = ({ onPostSuccess }) => { const playNewAlbum = (album) => { if (player && selectedTrack && selectedTrack.videoId !== album.videoId) { player.stopVideo(); + setPlayer(null); + setIsPlaying(false); } handlePlayPause(album); }; @@ -103,6 +105,14 @@ const PostUpload = ({ onPostSuccess }) => { // 앨범 삭제 처리 const removeAlbum = (index) => { + const albumToRemove = albums[index]; + if (selectedTrack && selectedTrack.videoId === albumToRemove.videoId) { + player.stopVideo(); + setIsPlaying(false); + setSelectedTrack(null); + setPlayer(null); + } + const updatedAlbums = [...albums]; updatedAlbums.splice(index, 1); setAlbums(updatedAlbums); @@ -228,7 +238,7 @@ const PostUpload = ({ onPostSuccess }) => { {/* YouTube Player */} {selectedTrack && ( From 86473432c87bd792ba31a5e35a15cd0973e13b43 Mon Sep 17 00:00:00 2001 From: HyunbinKim Date: Tue, 1 Oct 2024 11:41:15 +0900 Subject: [PATCH 24/26] =?UTF-8?q?#14=20[fix]:=20=ED=94=8C=EB=A0=88?= =?UTF-8?q?=EC=9D=B4=20=EB=B2=84=ED=8A=BC=20=EB=B0=94=EB=A1=9C=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD=EB=90=98=EC=A7=80=20=EC=95=8A=EB=8A=94=20=EB=B6=80?= =?UTF-8?q?=EB=B6=84=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/modals/PostUpload.jsx | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/components/modals/PostUpload.jsx b/src/components/modals/PostUpload.jsx index cd94aa3..d110b1f 100644 --- a/src/components/modals/PostUpload.jsx +++ b/src/components/modals/PostUpload.jsx @@ -79,10 +79,10 @@ const PostUpload = ({ onPostSuccess }) => { player.pauseVideo(); setIsPlaying(false); } else { + setIsPlaying(true); if (player) { player.loadVideoById(album.videoId); player.playVideo(); - setIsPlaying(true); } setSelectedTrack(album); } @@ -143,7 +143,7 @@ const PostUpload = ({ onPostSuccess }) => { const formData = new FormData(); formData.append('title', JSON.stringify(postData)); formData.append('channelId', "66fb541ced2d3c14a64eb9ee"); - // 첫 번째 앨범의 커버 이미지 URL을 사용 + if (albums[0]?.coverUrl) { formData.append('image', albums[0].coverUrl); } @@ -154,7 +154,6 @@ const PostUpload = ({ onPostSuccess }) => { alert('포스트가 성공적으로 업로드되었습니다!'); - // Reset form state setAlbums([]); setPostTitle(''); setDescription(''); From 73a65755d033f5b51bfdb6a1cb2657dda5ea0543 Mon Sep 17 00:00:00 2001 From: HyunbinKim Date: Tue, 1 Oct 2024 11:49:50 +0900 Subject: [PATCH 25/26] =?UTF-8?q?#14=20[feat]:=20=EC=B6=94=EA=B0=80?= =?UTF-8?q?=EB=90=9C=20api.js=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/utils/api.js | 68 ++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 60 insertions(+), 8 deletions(-) diff --git a/src/utils/api.js b/src/utils/api.js index 18d2140..dec3ed9 100644 --- a/src/utils/api.js +++ b/src/utils/api.js @@ -14,7 +14,7 @@ export const signup = async (email, password, fullName, nickName) => { fullName: JSON.stringify(userInfo), }); - return response.data; // 응답 데이터 반환 + return response.data; }; // 로그인 API 호출 @@ -24,18 +24,70 @@ export const login = async (email, password) => { password, }); - return response.data; // 응답 데이터 반환 + return response.data; }; // 사용자 정보 가져오기 API 호출 export const getUserData = async (userId, token) => { - const response = await axios.get(`/api/users/${userId}`, { - headers: { - Authorization: `Bearer ${token}`, // 토큰을 헤더로 전송 - }, - }); + if (!userId) { + throw new Error('User ID is required'); + } + try { + const response = await axios.get(`/api/users/${userId}`, { + headers: { + Authorization: `Bearer ${token}`, + }, + }); + return response.data; + } catch (error) { + console.error('Failed to get user data:', error); + throw error; + } +}; + +// 사용자 목록을 가져오는 API +export const getUsers = async (offset = 0, limit = 10) => { + try { + const response = await axios.get(`/api/users/get-users`, { + params: { offset, limit }, + }); + return response.data; // 사용자 목록 반환 + } catch (error) { + console.error('Error fetching users:', error); + throw error; // 에러 발생 시 호출한 곳으로 던짐 + } +}; - return response.data; // 응답 데이터 반환 +// 프로필 이미지 업로드 API 호출 +export const uploadProfileImage = async (formData, token) => { + try { + const response = await axios.post('/api/users/upload-photo', formData, { + headers: { + Authorization: `Bearer ${token}`, // 토큰을 헤더로 전송 + 'Content-Type': 'multipart/form-data', // 파일 업로드 시 필요한 헤더 + }, + }); + console.log('Profile image upload response:', response.data); // 서버 응답 확인 + return response.data; // 여기에 profileImage URL이 있어야 함. + } catch (error) { + console.error('프로필 이미지를 업로드할 수 없습니다.', error); + throw error; + } +}; + +// 사용자 정보 업데이트 API 호출 +export const updateUser = async (userId, token, updatedData) => { + try { + const response = await axios.put(`/api/settings/update-user`, updatedData, { + headers: { + Authorization: `Bearer ${token}`, // 토큰을 헤더로 전송 + }, + }); + return response.data; // 성공적으로 업데이트한 사용자 정보 반환 + } catch (error) { + console.error('사용자 정보를 업데이트할 수 없습니다.', error); + throw error; + } }; // 포스트 작성 API 호출 From efd2c86f0705573255c63ffb372e21e950435374 Mon Sep 17 00:00:00 2001 From: jny4867 Date: Tue, 1 Oct 2024 12:26:34 +0900 Subject: [PATCH 26/26] =?UTF-8?q?#32[feat]:=20=EB=B9=84=EB=B0=80=EB=B2=88?= =?UTF-8?q?=ED=98=B8=20=EB=B3=80=EA=B2=BD=20=EB=AA=A8=EB=8B=AC=20=EC=A0=95?= =?UTF-8?q?=EB=B3=B4=EC=88=98=EC=A0=95=20=EB=AA=A8=EB=8B=AC=EC=9D=98=20?= =?UTF-8?q?=EC=84=9C=EB=B8=8C=EB=AA=A8=EB=8B=AC=EB=A1=9C=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/auth/Dashboard.jsx | 28 --------------- src/components/modals/ProfileEditModal.jsx | 35 +++++++++++++++++++ src/components/modals/UpdatePasswordModal.jsx | 23 ++++++------ 3 files changed, 46 insertions(+), 40 deletions(-) diff --git a/src/auth/Dashboard.jsx b/src/auth/Dashboard.jsx index 24a54d8..c93a13c 100644 --- a/src/auth/Dashboard.jsx +++ b/src/auth/Dashboard.jsx @@ -6,12 +6,10 @@ import PostCard from '../components/PostCard'; // 게시물 카드 컴포넌트 import { getUserData } from '../utils/api'; // 사용자 데이터 가져오는 API 함수 import UserCard from '../components/UserCard'; // 사용자 카드 컴포넌트 import UserProfile from '../components/UserProfile'; // 사용자 프로필 컴포넌트 -import UpdatePasswordModal from '../components/modals/UpdatePasswordModal'; const Dashboard = () => { const [user, setUser] = useState(null); const [isModalOpen, setIsModalOpen] = useState(false); - const [isPasswordModalOpen, setIsPasswordModalOpen] = useState(false); const [isLoading, setIsLoading] = useState(true); const [error, setError] = useState(null); const navigate = useNavigate(); @@ -92,8 +90,6 @@ const Dashboard = () => { const openModalHandler = () => setIsModalOpen(true); const closeModalHandler = () => setIsModalOpen(false); - const openPasswordModalHandler = () => setIsPasswordModalOpen(true); - const closePasswordModalHandler = () => setIsPasswordModalOpen(false); if (isLoading) return Loading...; if (error) return {error}; @@ -145,16 +141,6 @@ const Dashboard = () => { setUser={updateUserDetails} /> )} - - 비밀번호 수정 - - {isPasswordModalOpen && ( - - )} ); }; @@ -199,20 +185,6 @@ const EditProfileButton = styled.button` } `; -const UpdatePasswordButton = styled.button` - margin-top: 20px; - padding: 10px 20px; - background-color: #6c5dd3; - color: white; - border: none; - border-radius: 5px; - cursor: pointer; - - &:hover { - background-color: #5a4bbd; - } -`; - const LoadingMessage = styled.p` text-align: center; font-size: 18px; diff --git a/src/components/modals/ProfileEditModal.jsx b/src/components/modals/ProfileEditModal.jsx index 8d40d66..262cf89 100644 --- a/src/components/modals/ProfileEditModal.jsx +++ b/src/components/modals/ProfileEditModal.jsx @@ -1,8 +1,19 @@ import React, { useState, useEffect, useRef } from 'react'; import styled from 'styled-components'; import { uploadProfileImage, updateUser } from '../../utils/api'; +import UpdatePasswordModal from '../modals/UpdatePasswordModal'; const ProfileEditModal = ({ user, token, onClose, setUser }) => { + const [isPasswordModalOpen, setIsPasswordModalOpen] = useState(false); + const openPasswordModalHandler = () => { + console.log('비밀번호 모달 열기'); + setIsPasswordModalOpen(true); + }; + const closePasswordModalHandler = () => { + console.log('비밀번호 모달 닫기'); + setIsPasswordModalOpen(false); + }; + const [formData, setFormData] = useState({ fullName: '', email: '', @@ -172,6 +183,9 @@ const ProfileEditModal = ({ user, token, onClose, setUser }) => { 비밀번호 + + 비밀번호 수정 + 추가 정보 @@ -200,6 +214,13 @@ const ProfileEditModal = ({ user, token, onClose, setUser }) => { 회원 정보 수정 + {isPasswordModalOpen && ( + + )} ); }; @@ -346,6 +367,20 @@ const FixedLabel = styled.span` z-index: 1; `; +const UpdatePasswordButton = styled.button` + margin-top: 20px; + padding: 10px 20px; + background-color: #6c5dd3; + color: white; + border: none; + border-radius: 5px; + cursor: pointer; + + &:hover { + background-color: #5a4bbd; + } +`; + const SaveButton = styled.button` background-color: #6c5dd3; color: white; diff --git a/src/components/modals/UpdatePasswordModal.jsx b/src/components/modals/UpdatePasswordModal.jsx index f250d35..576338c 100644 --- a/src/components/modals/UpdatePasswordModal.jsx +++ b/src/components/modals/UpdatePasswordModal.jsx @@ -3,7 +3,6 @@ import styled from "styled-components"; import { updatePassword } from "../../utils/api"; // 분리된 API 파일 임포트 const UpdatePasswordModal = ({ isOpen, onClose }) => { - const [currentPassword, setCurrentPassword] = useState(""); const [newPassword, setNewPassword] = useState(""); const [confirmPassword, setConfirmPassword] = useState(""); const [error, setError] = useState(null); @@ -13,14 +12,14 @@ const UpdatePasswordModal = ({ isOpen, onClose }) => { setError("새 비밀번호와 확인 비밀번호가 일치하지 않습니다."); return; } - + const token = localStorage.getItem("token"); // token 가져오기 - + if (!token) { setError("로그인이 필요합니다."); return; } - + try { // 비밀번호 변경 API 호출 await updatePassword(newPassword, token); @@ -31,18 +30,18 @@ const UpdatePasswordModal = ({ isOpen, onClose }) => { } }; + // 모달이 열리지 않으면 null을 반환 if (!isOpen) return null; + // 모달 클릭 처리 함수 + const handleModalClick = (e) => { + e.stopPropagation(); // 이벤트 전파 방지 + }; + return ( - - + +

비밀번호 변경

- {/* setCurrentPassword(e.target.value)} - /> */}