Skip to content

Commit

Permalink
Merge pull request #40 from softeerbootcamp4th/TASK-158
Browse files Browse the repository at this point in the history
[Feature][Task-158] UX 개선 & 빌드 용량 축소
  • Loading branch information
nim-od authored Aug 7, 2024
2 parents f758f17 + 652f71b commit edc7c60
Show file tree
Hide file tree
Showing 36 changed files with 291 additions and 124 deletions.
6 changes: 5 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,8 @@

#!.yarn/cache
.pnp.*
.env
.env


# build 결과 시각화
**/*/stats.html
6 changes: 1 addition & 5 deletions packages/common/src/components/chat/ChatList.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@
import { PropsWithChildren } from 'react';

export default function ChatList({ children }: PropsWithChildren) {
return (
<div className="flex flex-col-reverse justify-end gap-8 px-[26px]">
{children}
</div>
);
return <div className="flex flex-col-reverse justify-end gap-8 px-[26px]">{children}</div>;
}
3 changes: 2 additions & 1 deletion packages/user/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,16 @@
"@radix-ui/react-visually-hidden": "^1.1.0",
"@softeer/common": "*",
"@tanstack/react-query": "^5.51.11",
"@vitejs/plugin-react": "^4.3.1",
"class-variance-authority": "^0.7.0",
"clsx": "^2.1.1",
"jsdom": "^24.1.1",
"lucide-react": "^0.417.0"
},
"devDependencies": {
"@vitejs/plugin-react": "^4.3.1",
"autoprefixer": "^10.4.19",
"postcss": "^8.4.39",
"rollup-plugin-visualizer": "^5.12.0",
"tailwindcss": "^3.4.6",
"tailwindcss-animate": "^1.0.7",
"tsc": "^2.0.4",
Expand Down
Binary file added packages/user/public/images/hero/highlight.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added packages/user/public/images/hero/khaki-car.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added packages/user/public/images/hero/orange-car.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added packages/user/public/images/hero/white-car.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added packages/user/public/images/hero/white-shadow.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
9 changes: 0 additions & 9 deletions packages/user/src/assets/icons/highlight.svg

This file was deleted.

Binary file not shown.
9 changes: 0 additions & 9 deletions packages/user/src/assets/landing-racing/khaki-shadow.svg

This file was deleted.

Binary file not shown.
9 changes: 0 additions & 9 deletions packages/user/src/assets/landing-racing/orange-shadow.svg

This file was deleted.

Binary file not shown.
9 changes: 0 additions & 9 deletions packages/user/src/assets/landing-racing/white-shadow.svg

This file was deleted.

27 changes: 27 additions & 0 deletions packages/user/src/components/common/InViewLoadSection.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { cn } from '@softeer/common/utils';
import React, { FunctionComponent, Suspense } from 'react';
import useIntersectionObserver from 'src/hooks/useIntersectionObserver.ts';

interface InViewLoadSectionProps {
component: FunctionComponent | React.LazyExoticComponent<FunctionComponent>;
className?: string;
}

function InViewLoadSection<T extends HTMLElement>({
component: Component,
className = '',
}: InViewLoadSectionProps) {
const [ref, isIntersecting] = useIntersectionObserver<T>();

return (
<div ref={ref as React.RefObject<HTMLDivElement>} className={cn('min-h-[500px]', className)}>
{isIntersecting && (
<Suspense fallback={null}>
<Component />
</Suspense>
)}
</div>
);
}

export default InViewLoadSection;
2 changes: 1 addition & 1 deletion packages/user/src/components/common/toast/Toast.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -86,5 +86,5 @@ export {
ToastProvider,
ToastViewport,
type ToastActionElement,
type ToastProps
type ToastProps,
};
33 changes: 19 additions & 14 deletions packages/user/src/components/event/chatting/Chat.tsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,26 @@
import { BlockedChat, ChatProps, Message, Notice } from '@softeer/common/components';
import { FunctionComponent, useCallback } from 'react';
import InViewLoadSection from 'src/components/common/InViewLoadSection.tsx';
import useAuth from 'src/hooks/useAuth.tsx';

export default function Chat({ type, user, message }: ChatProps) {
const { user: me } = useAuth();

switch (type) {
case 'notice':
return <Notice>{message}</Notice>;
case 'blocked':
return <BlockedChat />;
case 'message':
return (
<Message user={user} isMyMessage={me?.id === user.id}>
{message}
</Message>
);
default:
return null;
}
const render: FunctionComponent = useCallback(() => {
switch (type) {
case 'notice':
return <Notice>{message}</Notice>;
case 'blocked':
return <BlockedChat />;
case 'message':
default:
return (
<Message user={user} isMyMessage={me?.id === user.id}>
{message}
</Message>
);
}
}, [type, user, message]);

return <InViewLoadSection className="min-h-[30px]" component={render} />;
}
10 changes: 3 additions & 7 deletions packages/user/src/components/home/eventHero/LandingTitle.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { PropsWithChildren, Suspense } from 'react';
import HighLight from 'src/assets/icons/highlight.svg?react';
import EventDurationText from './EventDurationText.tsx';

export default function LandingTitle() {
Expand Down Expand Up @@ -31,14 +30,11 @@ function Header({ children }: PropsWithChildren) {
function Subtitle({ children }: PropsWithChildren) {
return (
<div className="text-heading-12 relative text-center font-medium">
<div className="absolute -left-6 top-1 w-[85px]">
<HighLight />
</div>
{/* <img
<img
className="absolute -left-6 top-1 w-[85px]"
src="/src/assets/icons/highlight.svg"
src="/images/hero/highlight.png"
alt="서브 타이틀 강조"
/> */}
/>
{children}
</div>
);
Expand Down
3 changes: 2 additions & 1 deletion packages/user/src/components/home/eventHero/index.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import SECTION_ID from 'src/constants/sectionId.ts';
import LandingTitle from './LandingTitle.tsx';
import CarRacing from './racing/index.tsx';

/** 랜딩 시 보이는 히어로 섹션 */
export default function EventHero() {
return (
<section className="flex flex-col items-center pt-[100px]">
<section id={SECTION_ID.HERO} className="flex flex-col items-center pt-[100px]">
<LandingTitle />
<CarRacing />
</section>
Expand Down
35 changes: 22 additions & 13 deletions packages/user/src/components/home/eventHero/racing/Cars.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,3 @@
import SecondCarShadow from 'src/assets/landing-racing/khaki-shadow.svg?react';
import FirstCarShadow from 'src/assets/landing-racing/orange-shadow.svg?react';
import ThirdCarShadow from 'src/assets/landing-racing/white-shadow.svg?react';

export default function Cars() {
return (
<>
Expand All @@ -11,13 +7,18 @@ export default function Cars() {
</>
);
}

function FirstCar() {
return (
<div className="absolute left-[389px] h-full w-[490px]">
<FirstCarShadow className="absolute bottom-0" />
<img
alt="1등을 달리고 있는 차"
src="src/assets/landing-racing/orange-car.png"
alt="1등 차의 그림자"
src="/images/hero/orange-shadow.png"
className="absolute bottom-0"
/>
<img
alt="1등으로 달리고 있는 차"
src="/images/hero/orange-car.png"
className="animate-rotate1 absolute bottom-[40px] left-[4px] z-30"
/>
</div>
Expand All @@ -27,10 +28,14 @@ function FirstCar() {
function SecondCar() {
return (
<div className="absolute left-[170px] h-full w-[370px]">
<SecondCarShadow className="absolute bottom-[25px] left-[25px]" />
<img
alt="2등을 달리고 있는 차"
src="src/assets/landing-racing/khaki-car.png"
alt="2등 차의 그림자"
src="/images/hero/khaki-shadow.png"
className="absolute bottom-[25px] left-[25px]"
/>
<img
alt="2등으로 달리고 있는 차"
src="/images/hero/khaki-car.png"
className="animate-rotate2 absolute bottom-[70px] z-20"
/>
</div>
Expand All @@ -40,10 +45,14 @@ function SecondCar() {
function ThirdCar() {
return (
<div className="absolute right-[190px] h-full w-[210px]">
<ThirdCarShadow className="absolute bottom-[54px] right-[20px]" />
<img
alt="3등을 달리고 있는 차"
src="src/assets/landing-racing/white-car.png"
alt="3등 차의 그림자"
src="/images/hero/white-shadow.png"
className="absolute bottom-[54px] right-[20px]"
/>
<img
alt="3등으로 달리고 있는 차"
src="/images/hero/white-car.png"
className="animate-rotate3 absolute bottom-[70px] right-0 z-10"
/>
</div>
Expand Down
4 changes: 2 additions & 2 deletions packages/user/src/components/home/fastestQuiz/index.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import QUIZ_SECTION_ID from 'src/constants/quizSectionId.ts';
import SECTION_ID from 'src/constants/sectionId.ts';
import Description from './Description.tsx';
import QuizImages from './QuizImages.tsx';
import TopSection from './TopSection.tsx';

/** 선착순 퀴즈 설명 섹션 */
export default function FastestQuiz() {
return (
<section id={QUIZ_SECTION_ID} className="bg-neutral-700 pt-[120px]">
<section id={SECTION_ID.QUIZ} className="bg-neutral-700 pt-[120px]">
<div className="gap-15 flex flex-col items-center">
<TopSection />
<QuizImages />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,7 @@ import { forwardRef, PropsWithChildren } from 'react';

const HoverAnimationWrapper = forwardRef<HTMLButtonElement, PropsWithChildren>(
({ children, ...props }, ref) => (
<button
className="transform transition-all duration-200"
type="button"
ref={ref}
{...props}
>
<button className="transform transition-all duration-200" type="button" ref={ref} {...props}>
{children}
</button>
),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,14 @@
import { useNavigate } from 'react-router-dom';
import ArrowLeftIcon from 'src/assets/icons/arrow-left.svg?react';
import QUIZ_SECTION_ID from 'src/constants/quizSectionId.ts';
import RoutePaths from 'src/constants/routePath.ts';
import SECTION_ID from 'src/constants/sectionId.ts';

export default function ScrollToQuizButton() {
const navigate = useNavigate();

const handleClick = () =>
navigate(RoutePaths.Home, {
state: {
sectionId: QUIZ_SECTION_ID,
},
state: { sectionId: SECTION_ID.QUIZ },
});

return (
Expand Down
4 changes: 2 additions & 2 deletions packages/user/src/components/layout/header/tab/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ const tabs: { label: string; tab: Tab }[] = [
export default function NavigateTabs() {
const location = useLocation();
const navigate = useNavigate();
const [selectedTab, setSelectedTab] = useState<Tab>('Home');
const [selectedTab, setSelectedTab] = useState<Tab | null>(null);

useEffect(() => {
setSelectedTab(location.pathname.includes('event') ? 'Event' : 'Home');
Expand All @@ -40,7 +40,7 @@ export default function NavigateTabs() {
</NavigateTab>
))}
</div>
<SelectedTabIndicator selectedTab={selectedTab} />
{selectedTab && <SelectedTabIndicator selectedTab={selectedTab} />}
</div>
);
}
7 changes: 5 additions & 2 deletions packages/user/src/components/layout/index.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import { lazy } from 'react';
import { Outlet } from 'react-router-dom';
import InViewLoadSection from 'src/components/common/InViewLoadSection.tsx';
import Banner from './banner/index.tsx';
import BodyContainer from './BodyContainer.tsx';
import Footer from './footer/index.tsx';
import Header from './header/index.tsx';
import TopSectionContainer from './TopSectionContainer.tsx';

const Footer = lazy(() => import('./footer/index.tsx'));

export default function Layout() {
return (
<div className="flex h-screen min-w-max flex-col overflow-hidden">
Expand All @@ -14,7 +17,7 @@ export default function Layout() {
</TopSectionContainer>
<BodyContainer>
<Outlet />
<Footer />
<InViewLoadSection<HTMLDivElement> component={Footer} />
</BodyContainer>
</div>
);
Expand Down
3 changes: 0 additions & 3 deletions packages/user/src/constants/quizSectionId.ts

This file was deleted.

6 changes: 6 additions & 0 deletions packages/user/src/constants/sectionId.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
const SECTION_ID = {
QUIZ: 'quiz-section',
HERO: 'hero-section',
} as const;

export default SECTION_ID;
39 changes: 39 additions & 0 deletions packages/user/src/hooks/useIntersectionObserver.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { MutableRefObject, useEffect, useRef, useState } from 'react';

interface IntersectionObserverOptions {
root?: Element | null;
rootMargin?: string;
threshold?: number | number[];
}

const useIntersectionObserver = <T extends Element>(
options?: IntersectionObserverOptions,
): [MutableRefObject<T | null>, boolean] => {
const targetRef = useRef<T | null>(null);
const [isIntersecting, setIsIntersecting] = useState(false);

useEffect(() => {
const observer = new IntersectionObserver((entries, thisObserver) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
setIsIntersecting(true);
thisObserver.disconnect();
}
});
}, options);

if (targetRef.current) {
observer.observe(targetRef.current);
}

return () => {
if (targetRef.current) {
observer.unobserve(targetRef.current);
}
};
}, [options]);

return [targetRef, isIntersecting];
};

export default useIntersectionObserver;
Loading

0 comments on commit edc7c60

Please sign in to comment.