From b85b5111913dd86f6ac7108bc4ef26a82ae8c0e8 Mon Sep 17 00:00:00 2001 From: JungSoEun <85235356+ssilver01@users.noreply.github.com> Date: Thu, 8 Aug 2024 00:21:08 +0900 Subject: [PATCH 1/4] =?UTF-8?q?feat:=20loading=20=EC=A0=81=EC=9A=A9&?= =?UTF-8?q?=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 --- package.json | 2 + public/Ball_Fast.json | 1 + src/components/common/loading/LoadingBall.tsx | 33 ++++++ .../home/post/Image/ImagePicker.tsx | 112 +++++------------- src/routers/auth/oauth/KakaoRedirect.tsx | 2 + src/routers/index.tsx | 65 ++++++++-- yarn.lock | 44 ++++++- 7 files changed, 168 insertions(+), 91 deletions(-) create mode 100644 public/Ball_Fast.json create mode 100644 src/components/common/loading/LoadingBall.tsx diff --git a/package.json b/package.json index e966d59..b7e1223 100644 --- a/package.json +++ b/package.json @@ -17,8 +17,10 @@ "@tanstack/react-query-devtools": "^5.49.2", "axios": "^1.7.2", "eslint-config-prettier": "^9.1.0", + "framer-motion": "^11.3.22", "js-cookie": "^3.0.5", "lottie-web": "^5.12.2", + "popmotion": "^11.0.5", "react": "^18.3.1", "react-dom": "^18.3.1", "react-hook-form": "^7.52.1", diff --git a/public/Ball_Fast.json b/public/Ball_Fast.json new file mode 100644 index 0000000..962d031 --- /dev/null +++ b/public/Ball_Fast.json @@ -0,0 +1 @@ +{"assets":[],"ddd":0,"fr":60,"h":500,"ip":0,"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Oval 1","hd":false,"sr":1,"ks":{"a":{"a":0,"k":[25,25]},"o":{"a":0,"k":100},"p":{"a":1,"k":[{"t":0,"s":[331.824,250],"i":{"x":0.75,"y":0.75},"o":{"x":0.25,"y":0.25}},{"t":48,"s":[331.824,250.5],"i":{"x":0.88,"y":0.77},"o":{"x":0.5,"y":0},"ti":[0,0],"to":[0,0]},{"t":96,"s":[331.824,250],"i":{"x":0.5,"y":1},"o":{"x":0.12,"y":0.23},"ti":[0,0],"to":[0,0]},{"t":120,"s":[331.824,165],"i":{"x":0.75,"y":0.75},"o":{"x":0.25,"y":0.25},"ti":[0,0],"to":[0,0]},{"t":144,"s":[332.324,250],"i":{"x":0,"y":0},"o":{"x":1,"y":1}}]},"r":{"a":0,"k":0},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}},"ao":0,"ip":0,"op":145,"st":0,"bm":0,"shapes":[{"ty":"el","hd":false,"nm":"Oval 1","p":{"a":0,"k":[25,25]},"s":{"a":0,"k":[50,50]},"d":1},{"ty":"fl","hd":false,"bm":0,"c":{"a":1,"k":[{"t":0,"s":[0.973,0.8,0.725],"i":{"x":0.88,"y":0.77},"o":{"x":0.5,"y":0}},{"t":96,"s":[0.973,0.8,0.725],"i":{"x":0.5,"y":1},"o":{"x":0.12,"y":0.23}},{"t":120,"s":[0.929,0.447,0.239],"i":{"x":0,"y":0},"o":{"x":1,"y":1}}]},"r":1,"o":{"a":0,"k":100}}]},{"ddd":0,"ind":2,"ty":4,"nm":"Oval 1","hd":false,"sr":1,"ks":{"a":{"a":0,"k":[25,25]},"o":{"a":0,"k":100},"p":{"a":1,"k":[{"t":0,"s":[174.176,250.5],"i":{"x":0.88,"y":0.77},"o":{"x":0.5,"y":0},"ti":[0,0],"to":[0,0]},{"t":24,"s":[175,165],"i":{"x":0.5,"y":1},"o":{"x":0.12,"y":0.23},"ti":[0,0],"to":[0,0]},{"t":48,"s":[174.676,250.5],"i":{"x":0,"y":0},"o":{"x":1,"y":1}}]},"r":{"a":0,"k":0},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}},"ao":0,"ip":0,"op":145,"st":0,"bm":0,"shapes":[{"ty":"el","hd":false,"nm":"Oval 1","p":{"a":0,"k":[25,25]},"s":{"a":0,"k":[50,50]},"d":1},{"ty":"fl","hd":false,"bm":0,"c":{"a":1,"k":[{"t":0,"s":[0.929,0.447,0.239],"i":{"x":0.75,"y":0.75},"o":{"x":0.25,"y":0.25}},{"t":48,"s":[0.973,0.8,0.725],"i":{"x":0,"y":0},"o":{"x":1,"y":1}}]},"r":1,"o":{"a":0,"k":100}}]},{"ddd":0,"ind":3,"ty":4,"nm":"Oval 1","hd":false,"sr":1,"ks":{"a":{"a":0,"k":[25,25]},"o":{"a":0,"k":100},"p":{"a":1,"k":[{"t":0,"s":[251.235,250],"i":{"x":0.75,"y":0.75},"o":{"x":0.25,"y":0.25},"ti":[0,0],"to":[0,0]},{"t":48,"s":[251.235,250.5],"i":{"x":0.75,"y":0.75},"o":{"x":0.25,"y":0.25},"ti":[0,0],"to":[0,0]},{"t":72,"s":[251.235,165],"i":{"x":0.75,"y":0.75},"o":{"x":0.25,"y":0.25},"ti":[0,0],"to":[0,0]},{"t":96,"s":[251.235,250],"i":{"x":0,"y":0},"o":{"x":1,"y":1}}]},"r":{"a":0,"k":0},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}},"ao":0,"ip":0,"op":145,"st":0,"bm":0,"shapes":[{"ty":"el","hd":false,"nm":"Oval 1","p":{"a":0,"k":[25,25]},"s":{"a":0,"k":[50,50]},"d":1},{"ty":"fl","hd":false,"bm":0,"c":{"a":1,"k":[{"t":0,"s":[0.973,0.8,0.725],"i":{"x":0.88,"y":0.77},"o":{"x":0.5,"y":0}},{"t":72,"s":[0.929,0.447,0.239],"i":{"x":0.75,"y":0.75},"o":{"x":0.25,"y":0.25}},{"t":96,"s":[0.973,0.8,0.725],"i":{"x":0,"y":0},"o":{"x":1,"y":1}}]},"r":1,"o":{"a":0,"k":100}}]}],"meta":{"g":"@phase-software/lottie-exporter 0.7.0"},"nm":"","op":144,"v":"5.6.0","w":500} \ No newline at end of file diff --git a/src/components/common/loading/LoadingBall.tsx b/src/components/common/loading/LoadingBall.tsx new file mode 100644 index 0000000..e0b6c51 --- /dev/null +++ b/src/components/common/loading/LoadingBall.tsx @@ -0,0 +1,33 @@ +import styled from '@emotion/styled'; +import React from 'react'; +import LottieContainer from '../lottie/LottieContainer'; + +const LoadingBall = () => { + return ( + + + + + + ); +}; + +export default LoadingBall; + +const Overlay = styled.div` + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: transparent; + display: flex; + justify-content: center; + align-items: center; + z-index: 100000; +`; + +const Wrapper = styled.div` + width: 160px; + height: 160px; +`; diff --git a/src/components/home/post/Image/ImagePicker.tsx b/src/components/home/post/Image/ImagePicker.tsx index 2b08113..5ef6fa2 100644 --- a/src/components/home/post/Image/ImagePicker.tsx +++ b/src/components/home/post/Image/ImagePicker.tsx @@ -1,5 +1,6 @@ import React, { useCallback, useEffect, useState } from 'react'; import styled from '@emotion/styled'; +import { motion, useAnimation } from 'framer-motion'; import { colors } from '@styles/theme'; interface ImagePickerProps { @@ -7,12 +8,9 @@ interface ImagePickerProps { } const ImagePicker: React.FC = (props): JSX.Element => { - const [pickers, setPickers] = useState([]); const [pickIndex, setPickIndex] = useState(0); - const [startX, setStartX] = useState(null); - const [endX, setEndX] = useState(null); - - const minSwipeDistance = 50; + const controls = useAnimation(); + const [dragStart, setDragStart] = useState(null); const onPickIndex = useCallback( (idx: number): void => { @@ -24,97 +22,53 @@ const ImagePicker: React.FC = (props): JSX.Element => { [pickIndex] ); - const handleStart = (clientX: number) => { - setEndX(null); - setStartX(clientX); - }; - - const handleMove = (clientX: number) => { - setEndX(clientX); + const handleDragStart = (event: any) => { + setDragStart(event.clientX || event.touches[0].clientX); }; - const handleEnd = () => { - if (!startX || !endX) return; - const distance = startX - endX; - const isLeftSwipe = distance > minSwipeDistance; - const isRightSwipe = distance < -minSwipeDistance; + const handleDragEnd = (event: any) => { + const clientX = event.clientX || (event.changedTouches && event.changedTouches[0].clientX); + if (dragStart === null || clientX === undefined) return; + const distance = dragStart - clientX; + const minSwipeDistance = 50; - if (isLeftSwipe && pickIndex < (props.images ?? []).length - 1) { + if (distance > minSwipeDistance && pickIndex < (props.images ?? []).length - 1) { setPickIndex((prev) => prev + 1); - } - if (isRightSwipe && pickIndex > 0) { + } else if (distance < -minSwipeDistance && pickIndex > 0) { setPickIndex((prev) => prev - 1); } - }; - - // Touch events - const onTouchStart = (e: React.TouchEvent) => { - handleStart(e.touches[0].clientX); - }; - - const onTouchMove = (e: React.TouchEvent) => { - handleMove(e.touches[0].clientX); - }; - - const onTouchEnd = () => { - handleEnd(); - }; - - // Mouse events - const onMouseDown = (e: React.MouseEvent) => { - handleStart(e.clientX); - }; - - const onMouseMove = (e: React.MouseEvent) => { - if (startX === null) return; // Only move if we've started - handleMove(e.clientX); - }; - - const onMouseUp = () => { - handleEnd(); - setStartX(null); // Reset start position - }; - - const onMouseLeave = () => { - if (startX !== null) { - handleEnd(); - setStartX(null); - } + setDragStart(null); }; useEffect(() => { - setPickers( - (props.images ?? []).map((_: string, idx: number) => { - return ( - onPickIndex(idx)} - background={pickIndex === idx ? colors.red600 : 'white'} - border={pickIndex === idx ? colors.red100 : colors.gray300} - /> - ); - }) - ); - }, [onPickIndex, pickIndex, props.images]); + controls.start({ x: -pickIndex * 100 + '%' }); + }, [pickIndex, controls]); return ( - + {props.images?.map((image, index) => ( ))} - {pickers} + + {(props.images ?? []).map((_, idx: number) => ( + onPickIndex(idx)} + background={pickIndex === idx ? colors.red600 : 'white'} + border={pickIndex === idx ? colors.red100 : colors.gray300} + /> + ))} + ); }; @@ -129,10 +83,9 @@ const Container = styled.div` cursor: pointer; `; -const ImageContainer = styled.div<{ translateX: number }>` +const ImageContainer = styled(motion.div)` display: flex; transition: transform 0.25s ease-out; - transform: translateX(${(props) => props.translateX}%); `; const ImageWrapper = styled.div` @@ -150,7 +103,6 @@ const FillImage = styled.img` `; const PickerWrapper = styled.div` - /* height: 100%; */ align-items: center; justify-content: center; display: flex; diff --git a/src/routers/auth/oauth/KakaoRedirect.tsx b/src/routers/auth/oauth/KakaoRedirect.tsx index 6d24cf4..5e32df8 100644 --- a/src/routers/auth/oauth/KakaoRedirect.tsx +++ b/src/routers/auth/oauth/KakaoRedirect.tsx @@ -4,6 +4,7 @@ import { useMutation } from 'react-query'; import axios from 'axios'; import useAuthStore from '@stores/auth'; import Loading from '@components/common/loading/Loading'; +import { apiClient } from '@apis/axios'; interface KakaoLoginResponse { accessToken: string; @@ -24,6 +25,7 @@ const KakaoRedirect = () => { const kakaoLogin = async (code: string) => { try { + apiClient.defaults.withCredentials = true; const response = await axios.get(`https://api.stuffinout.site/kakaologin?code=${code}`, { headers: { 'Content-Type': 'application/x-www-form-urlencoded;charset=utf-8' }, }); diff --git a/src/routers/index.tsx b/src/routers/index.tsx index a2a58fe..d38a1ab 100644 --- a/src/routers/index.tsx +++ b/src/routers/index.tsx @@ -10,17 +10,20 @@ import Signup from '@routers/auth/signup/Signup'; import Login from '@routers/auth/login/Login'; import PwdSearch from '@routers/auth/pwdsearch/PwdSearch'; import NickNameSetting from '@routers/auth/nickname/NickNameSetting'; -import MyHome from '@routers/home/MyHome'; import NickNameChange from '@routers/home/homemenu/NickNameChange'; import PwdChange from '@routers/home/homemenu/PwdChange'; -import Post from '@routers/home/post/Post'; import useAuthStore from '@stores/auth'; -import Detail from '@routers/home/detail/Detail'; import Error from '@routers/error/Error'; import KakaoRedirect from '@routers/auth/oauth/KakaoRedirect'; import Introduce from '@routers/home/introduce/Introduce'; import OthersStuffList from '@routers/home/homemenu/OthersStuffList'; import GoogleRedirect from './auth/oauth/GoogleRedirect'; +import { lazy, Suspense } from 'react'; +import LoadingBall from '@components/common/loading/LoadingBall'; + +const Post = lazy(() => import('@routers/home/post/Post')); +const MyHome = lazy(() => import('@routers/home/MyHome')); +const Detail = lazy(() => import('@routers/home/detail/Detail')); const ProtectedRoute = () => { const isLoggedin = useAuthStore((store) => store.isLoggedIn); @@ -42,14 +45,49 @@ const Router = () => { const router = createBrowserRouter( createRoutesFromElements( <> - }> + }> } /> - } /> + }> + + + } + /> } /> - } /> - } /> - } /> - } /> + }> + + + } + /> + }> + + + } + /> + }> + + + } + /> + }> + + + } + /> } /> } /> @@ -58,7 +96,14 @@ const Router = () => { } /> } /> } /> - } /> + }> + + + } + /> ) ); diff --git a/yarn.lock b/yarn.lock index 8c810d2..6128b76 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1501,6 +1501,20 @@ form-data@^4.0.0: combined-stream "^1.0.8" mime-types "^2.1.12" +framer-motion@^11.3.22: + version "11.3.22" + resolved "https://registry.yarnpkg.com/framer-motion/-/framer-motion-11.3.22.tgz#59b09479e3e8919edb44171bb5e9a5a814ba2351" + integrity sha512-NS9TnPOJhkUrKKe7sl9zAMmQlzm70bwzJ6/gs/DkYP3kCqc0wO350CEqiEcBuVjhyqVNnDf33SkenzByi8x8Gg== + dependencies: + tslib "^2.4.0" + +framesync@6.1.2: + version "6.1.2" + resolved "https://registry.yarnpkg.com/framesync/-/framesync-6.1.2.tgz#755eff2fb5b8f3b4d2b266dd18121b300aefea27" + integrity sha512-jBTqhX6KaQVDyus8muwZbBeGGP0XgujBRbQ7gM7BRdS3CadCZIHiawyzYLnafYcvZIh5j8WE7cxZKFn7dXhu9g== + dependencies: + tslib "2.4.0" + fs.realpath@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" @@ -1598,6 +1612,11 @@ hasown@^2.0.2: dependencies: function-bind "^1.1.2" +hey-listen@^1.0.8: + version "1.0.8" + resolved "https://registry.yarnpkg.com/hey-listen/-/hey-listen-1.0.8.tgz#8e59561ff724908de1aa924ed6ecc84a56a9aa68" + integrity sha512-COpmrF2NOg4TBWUJ5UVyaCU2A88wEMkUPK4hNqyCkqHbxT92BbvfjoSozkAIIm6XhicGlJHhFdullInrdhwU8Q== + hoist-non-react-statics@^3.3.1: version "3.3.2" resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45" @@ -1962,6 +1981,16 @@ picomatch@^2.3.1: resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== +popmotion@^11.0.5: + version "11.0.5" + resolved "https://registry.yarnpkg.com/popmotion/-/popmotion-11.0.5.tgz#8e3e014421a0ffa30ecd722564fd2558954e1f7d" + integrity sha512-la8gPM1WYeFznb/JqF4GiTkRRPZsfaj2+kCxqQgr2MJylMmIKUwBfWW8Wa5fml/8gmtlD5yI01MP1QCZPWmppA== + dependencies: + framesync "6.1.2" + hey-listen "^1.0.8" + style-value-types "5.1.2" + tslib "2.4.0" + postcss@^8.4.39: version "8.4.39" resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.39.tgz#aa3c94998b61d3a9c259efa51db4b392e1bde0e3" @@ -2182,6 +2211,14 @@ strip-json-comments@^3.1.1: resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== +style-value-types@5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/style-value-types/-/style-value-types-5.1.2.tgz#6be66b237bd546048a764883528072ed95713b62" + integrity sha512-Vs9fNreYF9j6W2VvuDTP7kepALi7sk0xtk2Tu8Yxi9UoajJdEVpNpCov0HsLTqXvNGKX+Uv09pkozVITi1jf3Q== + dependencies: + hey-listen "^1.0.8" + tslib "2.4.0" + stylis@4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/stylis/-/stylis-4.2.0.tgz#79daee0208964c8fe695a42fcffcac633a211a51" @@ -2238,7 +2275,12 @@ tsconfck@^3.0.3: resolved "https://registry.yarnpkg.com/tsconfck/-/tsconfck-3.1.1.tgz#c7284913262c293b43b905b8b034f524de4a3162" integrity sha512-00eoI6WY57SvZEVjm13stEVE90VkEdJAFGgpFLTsZbJyW/LwFQ7uQxJHWpZ2hzSWgCPKc9AnBnNP+0X7o3hAmQ== -tslib@^2.0.3: +tslib@2.4.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.0.tgz#7cecaa7f073ce680a05847aa77be941098f36dc3" + integrity sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ== + +tslib@^2.0.3, tslib@^2.4.0: version "2.6.3" resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.3.tgz#0438f810ad7a9edcde7a241c3d80db693c8cbfe0" integrity sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ== From a4eca8ffe58a4df022365231403bacc7dc71c8fa Mon Sep 17 00:00:00 2001 From: JungSoEun <85235356+ssilver01@users.noreply.github.com> Date: Thu, 8 Aug 2024 00:26:39 +0900 Subject: [PATCH 2/4] =?UTF-8?q?fix:=EA=B2=8C=EC=8B=9C=EB=AC=BC=20=EC=88=98?= =?UTF-8?q?=EC=A0=95=EC=8B=9C=20=EC=98=A4=EB=A5=98=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/routers/home/post/Post.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/routers/home/post/Post.tsx b/src/routers/home/post/Post.tsx index f2709db..d43ae6a 100644 --- a/src/routers/home/post/Post.tsx +++ b/src/routers/home/post/Post.tsx @@ -122,7 +122,7 @@ const Post = () => { setIsCloseAlert(true); return false; } - if (fileList.length < 1) { + if (fileList.length < 1 && pictures.length < 1) { setAlertContent( 사진은 중요! From 509c4750807e302f271bb4f1375099af5868ec98 Mon Sep 17 00:00:00 2001 From: JungSoEun <85235356+ssilver01@users.noreply.github.com> Date: Thu, 8 Aug 2024 00:30:12 +0900 Subject: [PATCH 3/4] =?UTF-8?q?fix:=20=EB=92=A4=EB=A1=9C=EA=B0=80=EA=B8=B0?= =?UTF-8?q?=20=EC=98=A4=EB=A5=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/routers/home/detail/Detail.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/routers/home/detail/Detail.tsx b/src/routers/home/detail/Detail.tsx index b32f458..fb925f0 100644 --- a/src/routers/home/detail/Detail.tsx +++ b/src/routers/home/detail/Detail.tsx @@ -79,7 +79,7 @@ const Detail = () => { | undefined; const handleArrowClick = () => { - navigate(-1); + postDetail?.ownerId === memberId ? navigate('/') : navigate(-1); }; const handleHomeClick = () => { From b004e95433f16ad0ce06975a29e7195de9803f62 Mon Sep 17 00:00:00 2001 From: JungSoEun <85235356+ssilver01@users.noreply.github.com> Date: Thu, 8 Aug 2024 00:48:34 +0900 Subject: [PATCH 4/4] fix: style --- src/routers/home/introduce/Introduce.tsx | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/routers/home/introduce/Introduce.tsx b/src/routers/home/introduce/Introduce.tsx index b7ae7f4..7ca3b54 100644 --- a/src/routers/home/introduce/Introduce.tsx +++ b/src/routers/home/introduce/Introduce.tsx @@ -14,7 +14,14 @@ const Introduce = () => { }; return ( } + HeaderLeft={ + + } HeaderCenter={팀 maximalist 소개} Footer >