Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 2 additions & 4 deletions apps/tuk-web/next.config.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import type { NextConfig } from "next";
import type { NextConfig } from 'next';

const nextConfig: NextConfig = {
/* config options here */
};
const nextConfig: NextConfig = {};

export default nextConfig;
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@ import { useSuspenseQuery } from '@tanstack/react-query';
import { useEffect, useState } from 'react';

import { gatheringAPIService } from '@/app/invite/gathering/[gatheringId]/src/service';
import { QuoteIcon } from '@/app/invite/meet/[meetId]/src/components/InviteProposal';
import { CardFrame } from '@/app/proposal/[proposalId]/detail/components/GatheringProposalContent';
import AppInstallBanner from '@/shared/components/AppInstallBanner';
import { useSplashGate } from '@/shared/components/SplashGate';
import { useParam } from '@/shared/hooks/useParam';
import { cn } from '@/shared/lib';

Expand All @@ -20,7 +22,10 @@ const InviteGatheringContent = () => {
queryFn: () => gatheringAPIService.getGatheringName(gatheringId),
});

const { done: splashDone } = useSplashGate();

const [showBanner, setShowBanner] = useState(true);
const [animate, setAnimate] = useState(false);

const handleCloseBanner = () => {
localStorage.setItem(BANNER_KEY, Date.now().toString());
Expand All @@ -41,6 +46,18 @@ const InviteGatheringContent = () => {
}
}, []);

useEffect(() => {
if (!splashDone) return;
let raf = 0;
const t = setTimeout(() => {
raf = requestAnimationFrame(() => setAnimate(true));
}, 150);
return () => {
clearTimeout(t);
cancelAnimationFrame(raf);
};
}, [splashDone]);

return (
<>
{showBanner && <AppInstallBanner onClose={handleCloseBanner} />}
Expand All @@ -57,17 +74,30 @@ const InviteGatheringContent = () => {
</h2>

<div className="relative mt-[56px] flex flex-col items-center justify-center">
<div className="h-[320px] w-[278px] rounded-[10px] bg-gray-50" />
<div
className={cn(
'h-[290px] w-[278px] translate-y-[10px] rounded-[10px] bg-gray-50 pt-4',
animate && 'invite-float'
)}
>
<div className="flex flex-col items-center">
<QuoteIcon />
</div>
</div>

<div className="pointer-events-none absolute bottom-[-80px] left-1/2 h-[421px] w-[408px] -translate-x-1/2">
<div className="pointer-events-none absolute bottom-[-90px] left-1/2 h-[421px] w-[408px] -translate-x-1/2">
<div className="relative size-full">
<div className="absolute inset-0 z-0">
<CardFrame />
</div>

{proposalDetail.data.gatheringName && (
<div className="serif-body-16-M absolute left-1/2 top-[180px] z-[1] -translate-x-1/2 text-center text-gray-900">
{proposalDetail.data.gatheringName}
<div className="absolute left-1/2 top-[180px] z-[1] flex -translate-x-1/2 flex-col gap-2.5">
<div className="serif-body-14-R text-gray-500">연락이 뜸해진</div>

<div className="serif-body-16-M text-center text-gray-900">
{proposalDetail.data.gatheringName}
</div>
</div>
)}
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,10 @@ const InviteProposalContent = () => {

const { done: splashDone } = useSplashGate();

const [slideDown, setSlideDown] = useState(false);

const [showBanner, setShowBanner] = useState(false);

const [slideStage, setSlideStage] = useState<0 | 1 | 2>(0);

const {
data: proposalDetail,
isLoading,
Expand Down Expand Up @@ -54,11 +54,28 @@ const InviteProposalContent = () => {

useEffect(() => {
if (splashDone && !isLoading && !isError) {
const id = requestAnimationFrame(() => setSlideDown(true));
return () => cancelAnimationFrame(id);
let rafId = 0;
let tId: ReturnType<typeof setTimeout> | null = null;

rafId = requestAnimationFrame(() => {
setSlideStage(1);
tId = setTimeout(() => setSlideStage(2), 1000);
});

return () => {
cancelAnimationFrame(rafId);
if (tId) clearTimeout(tId);
};
}
}, [splashDone, isLoading, isError]);

const slideClass =
slideStage === 0
? 'translate-y-0 duration-0'
: slideStage === 1
? 'translate-y-[92px] duration-500'
: 'translate-y-56 duration-500';

return (
<>
{showBanner && <AppInstallBanner onClose={handleCloseBanner} />}
Expand All @@ -81,8 +98,8 @@ const InviteProposalContent = () => {

<div
className={cn(
'ease-outmotion-reduce:transition-none absolute bottom-[-80px] left-1/2 h-[421px] w-[408px] -translate-x-1/2 transition-transform duration-1000',
slideDown ? 'translate-y-56' : 'translate-y-0'
'absolute bottom-[-80px] left-1/2 h-[421px] w-[408px] -translate-x-1/2 transition-transform ease-out motion-reduce:transition-none',
slideClass
)}
style={{ willChange: 'transform', transitionDelay: '150ms' }}
>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ import { QueryErrorResetBoundary } from '@tanstack/react-query';
import React, { Suspense } from 'react';
import { ErrorBoundary } from 'react-error-boundary';

import SkeletonGuard from '@/shared/components/SkeletonGuard';
import GatheringProposalContent from '@/app/proposal/[proposalId]/detail/components/GatheringProposalContent';
import GatheringProposalErrorFallback from '@/app/proposal/[proposalId]/detail/components/GatheringProposalErrorFallback';
import GatheringProposalSkeleton from '@/app/proposal/[proposalId]/detail/components/GatheringProposalSkeleton';
import SkeletonGuard from '@/shared/components/SkeletonGuard';

const GatheringProposal = () => {
return (
Expand Down
30 changes: 30 additions & 0 deletions apps/tuk-web/src/styles/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,36 @@
display: none;
}

@keyframes invite-bounce-float {
0% {
transform: translateY(30px);
}
22% {
transform: translateY(-38px);
}
30% {
transform: translateY(-30px);
}
55% {
transform: translateY(-30px);
}
100% {
transform: translateY(30px);
}
}

.invite-float {
animation: invite-bounce-float 3s ease-in-out infinite both;
will-change: transform;
}

@media (prefers-reduced-motion: reduce) {
.invite-float {
animation: none !important;
transform: translateY(10px);
}
}

html,
body {
max-width: 37.5rem;
Expand Down
Loading