From 7d759b712aba0cd817582f4eb33511e2fdbfc41b Mon Sep 17 00:00:00 2001 From: sryung Date: Wed, 27 Dec 2023 20:25:24 +0900 Subject: [PATCH 1/3] =?UTF-8?q?[=F0=9F=8E=80=20:=20style]=20=ED=94=84?= =?UTF-8?q?=EB=A1=9C=ED=95=84=20=EC=9D=B4=EB=AF=B8=EC=A7=80=20=EC=88=98?= =?UTF-8?q?=EC=A0=95=20=EB=B2=84=ED=8A=BC=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - before) 기존에 등록된 이미지를 삭제한 뒤에 새로 등록해야만 했음 - after) 등록한 이미지가 있는 경우에도 바로 이미지 변경할 수 있도록 함 --- src/assets/images/i-change.svg | 3 ++ src/components/profile/edit-profile-form.tsx | 12 ++++--- src/styles/profile-form.ts | 34 +++++++++++++++++--- 3 files changed, 41 insertions(+), 8 deletions(-) create mode 100644 src/assets/images/i-change.svg diff --git a/src/assets/images/i-change.svg b/src/assets/images/i-change.svg new file mode 100644 index 0000000..ef387bd --- /dev/null +++ b/src/assets/images/i-change.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/components/profile/edit-profile-form.tsx b/src/components/profile/edit-profile-form.tsx index cae0c56..2b0469c 100644 --- a/src/components/profile/edit-profile-form.tsx +++ b/src/components/profile/edit-profile-form.tsx @@ -13,6 +13,7 @@ import CompressImage from '@util/compress-image.tsx'; import useEscClose from '@util/use-esc-close.tsx'; import * as S from '@style/profile-form.ts'; import { ReactComponent as IconUser } from '@img/i-user.svg'; +import { ReactComponent as IconChange } from '@img/i-change.svg'; import { ReactComponent as LoadingSpinner } from '@img/loading-spinner-mini.svg'; interface IEditProfileForm extends Pick { @@ -97,7 +98,7 @@ export default function EditProfileForm({ return ( {avatarPreview ? ( - <> + - + + + + ) : ( - + - + )} +> = styled(AttachAvatarButton).attrs(() => ({ as: 'label' }))` + right: 10px; + display: flex; + align-items: center; + justify-content: center; + cursor: pointer; + svg { + width: 15px; + height: 15px; + stroke: ${whiteColor}; + stroke-width: 3px; + } +`; + +export const AttachAvatarLabel: React.ComponentType< React.HTMLProps > = styled(Avatar).attrs(() => ({ as: 'label', From 8eedd329dc97a15f7baf1a13c4bfa88ef6cb3418 Mon Sep 17 00:00:00 2001 From: sryung Date: Wed, 27 Dec 2023 23:30:43 +0900 Subject: [PATCH 2/3] =?UTF-8?q?[=F0=9F=9B=A0=20:=20fix]=20=EA=B2=80?= =?UTF-8?q?=EC=83=89=20=EA=B2=B0=EA=B3=BC=20=EA=B2=BD=EB=A1=9C=20=ED=95=9C?= =?UTF-8?q?=20=EB=B2=88=EC=97=90=20=EC=9D=B4=EB=8F=99=ED=95=98=EB=8F=84?= =?UTF-8?q?=EB=A1=9D=20=EC=88=98=EC=A0=95=20(#4)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - before) searchKeyword가 빈채로 경로 이동한 뒤 다시 적절한 경로로 재이동 - after) searchKeyword로 한 번에 이동 - setSearchKeyword(searchValue) 동작이 한발짝 늦는것이 문제로 원인 파악. searchValue로 navigate하면서 해결. 불필요한 비동기 동작 및 useEffect 코드 제거 - 검색결과 쿼리 깔끔하게 다듬기 --- src/App.tsx | 2 +- src/components/search.tsx | 16 +++++----------- 2 files changed, 6 insertions(+), 12 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index 126a3d7..787ff54 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -29,7 +29,7 @@ const router = createBrowserRouter([ element: , }, { - path: `/search/:searchKeyword`, + path: `/search`, element: , }, ], diff --git a/src/components/search.tsx b/src/components/search.tsx index 6537968..25d22d5 100644 --- a/src/components/search.tsx +++ b/src/components/search.tsx @@ -1,5 +1,5 @@ -import React, { useEffect, useState } from 'react'; -import { useRecoilState } from 'recoil'; +import React, { useState } from 'react'; +import { useSetRecoilState } from 'recoil'; import { useNavigate } from 'react-router-dom'; import { searchKeywordAtom } from '@/atoms.tsx'; import WindowTop from '@compo/window-top.tsx'; @@ -10,22 +10,16 @@ import { ReactComponent as IconSearch } from '@img/i-search.svg'; export default function Search() { const navigate = useNavigate(); const [searchValue, setSearchValue] = useState(''); - const [searchKeyword, setSearchKeyword] = useRecoilState(searchKeywordAtom); + const setSearchKeyword = useSetRecoilState(searchKeywordAtom); const onChange = (e: React.ChangeEvent) => { setSearchValue(e.target.value); }; - const onSubmit = async (e: React.FormEvent) => { + const onSubmit = (e: React.FormEvent) => { e.preventDefault(); - if (searchValue === '') return; setSearchKeyword(searchValue); setSearchValue(''); - navigate(`/search/searchKeyword=${searchKeyword}`); + navigate(`/search?query=${searchValue}`); }; - useEffect(() => { - if (searchKeyword) { - navigate(`/search/searchKeyword=${searchKeyword}`); - } - }, [searchKeyword, navigate]); return ( From e441194444d51b80ec41d98187e73a77c41fcf36 Mon Sep 17 00:00:00 2001 From: sryung Date: Thu, 28 Dec 2023 00:22:47 +0900 Subject: [PATCH 3/3] =?UTF-8?q?[=F0=9F=A5=81=20:=20feat]=20=EA=B2=80?= =?UTF-8?q?=EC=83=89=20=EA=B2=B0=EA=B3=BC=20url=EB=A1=9C=20=EC=A0=91?= =?UTF-8?q?=EA=B7=BC=20=EB=B0=8F=20=EC=83=88=EB=A1=9C=EA=B3=A0=EC=B9=A8=20?= =?UTF-8?q?=ED=99=9C=EC=84=B1=ED=99=94=20(#23)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - before) 검색창에 입력한 키워드를 atom에 저장한 뒤 가져와 사용하는 형태이기 때문에 새로고침하거나 url로 접근하는 경우는 결과를 불러올 수 없었음 - after) useLocation을 활용해 url에 입력된 값을 저장해 atom과 비교하며 사용 - decodeURLComponent : 자바스크립트 URL 디코딩 메서드. 암호화된 한글을 해독 - useParams는 router로 바로 연결된 컴포넌트가 아니면 활용할 수 없음을 유의. 대신에 useLocation을 채택함 --- src/components/search-result/search-timeline.tsx | 14 +++++++++++--- src/styles/timeline.ts | 5 +++++ 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/src/components/search-result/search-timeline.tsx b/src/components/search-result/search-timeline.tsx index 168c916..f8b708d 100644 --- a/src/components/search-result/search-timeline.tsx +++ b/src/components/search-result/search-timeline.tsx @@ -1,4 +1,5 @@ import { useEffect, useState } from 'react'; +import { useLocation } from 'react-router-dom'; import { collection, getDocs, @@ -7,16 +8,21 @@ import { query, where, } from 'firebase/firestore'; -import { useRecoilValue } from 'recoil'; import { db } from '@/firebase.ts'; +import { useRecoilValue } from 'recoil'; import { searchKeywordAtom } from '@/atoms.tsx'; import Tweet from '@compo/home/tweet.tsx'; import ITweet from '@type/ITweet.ts'; import * as S from '@style/timeline.ts'; export default function SearchTimeline() { + const location = useLocation(); const [tweets, setTweets] = useState([]); - const searchKeyword = useRecoilValue(searchKeywordAtom); + const searchKeywordFromLocation = decodeURIComponent( + location.search.slice(7), + ); + const searchKeywordAtomValue = useRecoilValue(searchKeywordAtom); + const searchKeyword = searchKeywordFromLocation || searchKeywordAtomValue; const fetchTweets = async () => { const tweetsQuery = query( collection(db, 'tweets'), @@ -50,6 +56,8 @@ export default function SearchTimeline() { ))} ) : ( - 검색 결과가 없습니다. + + [{searchKeyword}] 에 대한 검색 결과가 없습니다. + ); } diff --git a/src/styles/timeline.ts b/src/styles/timeline.ts index 5202b05..bb46212 100644 --- a/src/styles/timeline.ts +++ b/src/styles/timeline.ts @@ -1,4 +1,5 @@ import styled from 'styled-components'; +import { primaryColor } from '@style/global.ts'; export const TimelineWrapper = styled.ul` display: block; @@ -9,4 +10,8 @@ export const TimelineWrapper = styled.ul` export const Text = styled.p` margin: 20px 0; text-align: center; + span { + color: ${primaryColor}; + font-weight: 600; + } `;