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
15 changes: 12 additions & 3 deletions apps/web/src/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export default function Header() {

const router = useRouter();

const { data: user, isLoading: isLoadingUser } = useUserQuery();
const { data: user, isLoading, error } = useUserQuery();

const lastCheckIn = user?.last_check_in ?? new Date();

Expand All @@ -25,6 +25,11 @@ export default function Header() {
window.open(contactUrl, '_blank');
};

const handleNavigateLogin = () => {
router.push('/login');
setOpen(false);
};

return (
<header className="bg-background sticky top-0 z-50 flex h-16 w-full max-w-md items-center justify-between p-4 shadow-sm">
<div
Expand All @@ -36,14 +41,18 @@ export default function Header() {
<div className="flex items-center gap-2">
<Dialog open={open} onOpenChange={setOpen}>
<DialogTrigger asChild>
<Button variant="outline" className="justify-start" disabled={isLoadingUser}>
<Button variant="outline" className="justify-start" disabled={isLoading}>
<CalendarCheck className="h-4 w-4" />
출석체크
</Button>
</DialogTrigger>

<DialogContent>
<CheckInModal lastCheckIn={lastCheckIn} />
<CheckInModal
lastCheckIn={lastCheckIn}
isLogin={!!user}
handleNavigateLogin={handleNavigateLogin}
/>
</DialogContent>
</Dialog>

Expand Down
31 changes: 24 additions & 7 deletions apps/web/src/app/search/SearchResultCard.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
import { Heart, ListPlus, ListRestart, MinusCircle, PlusCircle, ThumbsUp } from 'lucide-react';
import { useState } from 'react';
import { toast } from 'sonner';

import ThumbUpModal from '@/components/ThumbUpModal';
import { Button } from '@/components/ui/button';
import { Card } from '@/components/ui/card';
import { Dialog, DialogContent, DialogTrigger } from '@/components/ui/dialog';
import useAuthStore from '@/stores/useAuthStore';
import { SearchSong } from '@/types/song';

interface IProps {
Expand All @@ -20,6 +23,17 @@ export default function SearchResultCard({
onClickSave,
}: IProps) {
const { id, title, artist, num_tj, num_ky, isToSing, isLike, isSave } = song;
const { isAuthenticated } = useAuthStore();

const [open, setOpen] = useState(false);

const handleClickThumbsUp = () => {
if (!isAuthenticated) {
toast.error('로그인이 필요해요.');
return;
}
setOpen(true);
};

return (
<Card className="relative overflow-hidden">
Expand All @@ -34,15 +48,18 @@ export default function SearchResultCard({
<p className="text-muted-foreground truncate text-sm">{artist}</p>
</div>

<Dialog>
<DialogTrigger asChild>
<Button variant="ghost" size="icon" aria-label={'추천하기'}>
<ThumbsUp />
</Button>
</DialogTrigger>
<Dialog open={open} onOpenChange={setOpen}>
<Button
variant="ghost"
size="icon"
aria-label={'추천하기'}
onClick={handleClickThumbsUp}
>
<ThumbsUp />
</Button>

<DialogContent>
<ThumbUpModal songId={id} />
<ThumbUpModal songId={id} handleClose={() => setOpen(false)} />
</DialogContent>
</Dialog>
</div>
Expand Down
78 changes: 47 additions & 31 deletions apps/web/src/components/CheckInModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,21 @@ import SplitText from '@/components/reactBits/SplitText';
import { Button } from '@/components/ui/button';
import { DialogDescription, DialogHeader, DialogTitle } from '@/components/ui/dialog';
import { useCheckInTimer } from '@/hooks/useCheckInTimer';
import { usePatchUserCheckInMutation, useUserQuery } from '@/queries/userQuery';
import { usePatchUserCheckInMutation } from '@/queries/userQuery';

import ActionAnimationFlow from './ActionAnimationFlow';

interface CheckInModalProps {
lastCheckIn: Date;
isLogin: boolean;
handleNavigateLogin: () => void;
}

export default function CheckInModal({ lastCheckIn }: CheckInModalProps) {
export default function CheckInModal({
lastCheckIn,
isLogin,
handleNavigateLogin,
}: CheckInModalProps) {
const timeRemaining = useCheckInTimer(lastCheckIn);

const { mutate: patchUserCheckIn } = usePatchUserCheckInMutation();
Expand All @@ -38,36 +44,46 @@ export default function CheckInModal({ lastCheckIn }: CheckInModalProps) {
</DialogDescription>
</DialogHeader>
<div className="flex flex-1 flex-col items-center justify-center gap-4 p-6">
<ActionAnimationFlow
animationData={Checked}
clickCallback={handleClickCheckIn}
initalStatus={parseLastCheckIn >= today ? 'DONE' : 'IDLE'}
// 1. 대기 화면 (trigger 함수를 받아서 버튼에 연결)
idleView={trigger => (
<div className="text-center">
<h2 className="mb-4 text-lg font-bold">오늘 출석하시겠어요?</h2>
<Button
onClick={trigger} // 👈 여기서 애니메이션 시작!
className="rounded-full bg-blue-500 px-6 py-2 text-white active:scale-95"
>
출석하기
</Button>
</div>
)}
// 2. 결과 화면
doneView={
<div className="w-full space-y-2 text-center">
<p className="text-muted-foreground">다음 출석까지 남은 시간</p>
<div className="text-primary flex items-center justify-center gap-2 font-mono text-3xl font-bold">
<Clock className="h-6 w-6" />
{timeRemaining || '00:00:00'}
{isLogin ? (
<ActionAnimationFlow
animationData={Checked}
clickCallback={handleClickCheckIn}
initalStatus={parseLastCheckIn >= today ? 'DONE' : 'IDLE'}
// 1. 대기 화면 (trigger 함수를 받아서 버튼에 연결)
idleView={trigger => (
<div className="text-center">
<h2 className="mb-4 text-lg font-bold">오늘 출석하시겠어요?</h2>
<Button
onClick={trigger} // 👈 여기서 애니메이션 시작!
className="rounded-full bg-blue-500 px-6 py-2 text-white active:scale-95"
>
출석하기
</Button>
</div>
<Button disabled className="w-full" variant="secondary">
출석 완료
</Button>
</div>
}
/>
)}
// 2. 결과 화면
doneView={
<div className="w-full space-y-2 text-center">
<p className="text-muted-foreground">다음 출석까지 남은 시간</p>
<div className="text-primary flex items-center justify-center gap-2 font-mono text-3xl font-bold">
<Clock className="h-6 w-6" />
{timeRemaining || '00:00:00'}
</div>
<Button disabled className="w-full" variant="secondary">
출석 완료
</Button>
</div>
}
/>
) : (
<div className="flex flex-col gap-4 text-center">
<SplitText text="로그인 후 사용 가능합니다" tag="span" />

<Button onClick={handleNavigateLogin} className="w-full">
로그인하러 가기
</Button>
</div>
)}
</div>
</div>
);
Expand Down
10 changes: 6 additions & 4 deletions apps/web/src/components/ThumbUpModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,22 +16,24 @@ import FallingIcons from './FallingIcons';

interface ThumbUpModalProps {
songId: string;
handleClose: () => void;
}

export default function ThumbUpModal({ songId }: ThumbUpModalProps) {
export default function ThumbUpModal({ songId, handleClose }: ThumbUpModalProps) {
const [value, setValue] = useState([0]);

const { data: user } = useUserQuery();

const point = user?.point ?? 0;

const { mutate: patchSongThumb, isPending: isPendingSongThumb } = useSongThumbMutation();
const { mutate: patchSetPoint, isPending: isPendingSetPoint } = usePatchSetPointMutation();

const point = user?.point ?? 0;

const handleClickThumb = () => {
patchSongThumb({ songId, point: value[0] });
patchSetPoint({ point: point - value[0] });
setValue([0]);

handleClose();
};

const isPending = isPendingSongThumb || isPendingSetPoint;
Expand Down
1 change: 0 additions & 1 deletion apps/web/src/hooks/useSearchSong.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ export default function useSearchSong() {
const [searchType, setSearchType] = useState<SearchType>('all');
const [saveModalType, setSaveModalType] = useState<SaveModalType>('');
const [selectedSaveSong, setSelectedSaveSong] = useState<SearchSong | null>(null);
// const { data: searchResults, isLoading } = useSearchSongQuery(query, searchType, isAuthenticated);
const { mutate: toggleToSing, isPending: isToggleToSingPending } = useToggleToSingMutation(
query,
searchType,
Expand Down
1 change: 1 addition & 0 deletions apps/web/src/queries/userQuery.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export const useUserQuery = () => {
}
return response.data;
},

staleTime: 1000 * 60,
});
};
Expand Down
2 changes: 1 addition & 1 deletion apps/web/src/query.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export default function QueryProvider({ children }: { children: React.ReactNode
queries: {
refetchOnWindowFocus: false,
retry: 1,
throwOnError: true,
// throwOnError: true,
staleTime: 1000 * 60 * 5,
gcTime: 1000 * 60 * 5,
},
Expand Down
32 changes: 0 additions & 32 deletions apps/web/src/stores/useLoadingStore.ts

This file was deleted.