-
Notifications
You must be signed in to change notification settings - Fork 2
[feat] 홈페이지, 나의 CD UI 변경사항 반영 #112
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
70a7d30
39bf653
7e527b3
b3de689
d152e40
1c25b63
468463b
06a1d92
90f0534
ba7f9ff
12830c2
a6346b8
851dbec
b948ab1
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| import { api } from '@/shared/api/httpClient' | ||
|
|
||
| export const postLike = (playlistId: number) => { | ||
| return api.post(`/main/likes/${playlistId}`) | ||
| } | ||
|
|
||
| export const deleteLike = (playlistId: number) => { | ||
| return api.delete(`/main/likes/${playlistId}`) | ||
| } | ||
|
|
||
| export const getLikeStatus = (playlistId: number) => { | ||
| return api.get(`/main/likes/${playlistId}`) | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| export { default as useLike } from './model/useLike' | ||
| export * from './api/like' | ||
| export { default as LikeButton } from './ui/LikeButton' |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,58 @@ | ||
| import { useState } from 'react' | ||
| import { useNavigate } from 'react-router-dom' | ||
|
|
||
| import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query' | ||
|
|
||
| import { useAuthStore } from '@/features/auth/store/authStore' | ||
| import { postLike, deleteLike, getLikeStatus } from '@/features/like/api/like' | ||
|
|
||
| const useLike = (playlistId: number, initialIsLiked: boolean) => { | ||
| const queryClient = useQueryClient() | ||
| const [isLiked, setIsLiked] = useState(initialIsLiked) | ||
| const navigate = useNavigate() | ||
| const { isLogin } = useAuthStore() | ||
|
|
||
| const likeMutation = useMutation({ | ||
| mutationFn: (playlistId: number) => postLike(playlistId), | ||
| onSuccess: () => { | ||
| setIsLiked(true) | ||
| queryClient.invalidateQueries({ queryKey: ['playlistDetail', playlistId] }) | ||
| }, | ||
| }) | ||
|
|
||
| const unlikeMutation = useMutation({ | ||
| mutationFn: (playlistId: number) => deleteLike(playlistId), | ||
| onSuccess: () => { | ||
| setIsLiked(false) | ||
| queryClient.invalidateQueries({ queryKey: ['playlistDetail', playlistId] }) | ||
| }, | ||
|
Comment on lines
+25
to
+28
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 좋아요 취소 액션이 성공했을 때, onSuccess: () => {
setIsLiked(false)
queryClient.invalidateQueries({ queryKey: ['playlistDetail', playlistId] })
queryClient.invalidateQueries({ queryKey: ['myLikedCdList'] })
},Style Guide ReferencesFootnotes
Comment on lines
+17
to
+28
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 좋아요 상태 쿼리도 함께 무효화해야 합니다.
아래와 같이 수정할 수 있습니다: onSuccess: () => {
setIsLiked(true)
queryClient.invalidateQueries({ queryKey: ['playlistDetail', playlistId] })
+ queryClient.invalidateQueries({ queryKey: ['likeStatus', playlistId] })
},
...
onSuccess: () => {
setIsLiked(false)
queryClient.invalidateQueries({ queryKey: ['playlistDetail', playlistId] })
+ queryClient.invalidateQueries({ queryKey: ['likeStatus', playlistId] })
},🤖 Prompt for AI Agents |
||
| }) | ||
|
|
||
| const toggleLike = () => { | ||
| if (!isLogin) { | ||
| navigate('/login') | ||
| return | ||
| } | ||
|
|
||
| if (likeMutation.isPending || unlikeMutation.isPending) return | ||
|
|
||
| if (isLiked) { | ||
| unlikeMutation.mutate(playlistId) | ||
| } else { | ||
| likeMutation.mutate(playlistId) | ||
| } | ||
| } | ||
|
Comment on lines
+1
to
+44
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 플레이리스트 전환 시 좋아요 상태가 갱신되도록 보완이 필요합니다. 현재 로컬 상태를 적용 예시는 아래와 같습니다: -import { useState } from 'react'
+import { useEffect, useState } from 'react'
...
const [isLiked, setIsLiked] = useState(initialIsLiked)
...
+ useEffect(() => {
+ setIsLiked(initialIsLiked)
+ }, [initialIsLiked, playlistId])🤖 Prompt for AI Agents |
||
|
|
||
| return { liked: isLiked, toggleLike, likeMutation, unlikeMutation } | ||
| } | ||
|
|
||
| export default useLike | ||
|
|
||
| export const useLikeStatus = (playlistId: number, options?: { enabled?: boolean }) => { | ||
| return useQuery({ | ||
| queryKey: ['likeStatus', playlistId], | ||
| queryFn: () => getLikeStatus(playlistId), | ||
| staleTime: 0, | ||
| enabled: playlistId !== undefined && (options?.enabled ?? true), | ||
| }) | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,63 @@ | ||
| import React from 'react' | ||
|
|
||
| import styled, { useTheme } from 'styled-components' | ||
|
|
||
| import { Like, LikeStroke } from '@/assets/icons' | ||
| import { useLike } from '@/features/like' | ||
| import { flexRowCenter, myCdButton } from '@/shared/styles/mixins' | ||
| import SvgButton from '@/shared/ui/SvgButton' | ||
|
|
||
| interface LikeButtonProps { | ||
| playlistId: number | ||
| isLiked: boolean | ||
| type?: 'HOME' | 'DISCOVER' | 'MY' | ||
| } | ||
|
|
||
| const ICON_STYLE = { | ||
| HOME: { size: 20, Icon: Like }, | ||
| DISCOVER: { size: 24, Icon: LikeStroke }, | ||
| MY: { size: 16, Icon: LikeStroke }, | ||
| } as const | ||
|
|
||
| const LikeButton = ({ playlistId, isLiked, type = 'HOME' }: LikeButtonProps) => { | ||
| const theme = useTheme() | ||
| const { liked, toggleLike } = useLike(playlistId, isLiked) | ||
|
|
||
| const handleClick = (e: React.MouseEvent<HTMLButtonElement>) => { | ||
| e.stopPropagation() | ||
| toggleLike() | ||
| } | ||
|
|
||
| const { size, Icon } = ICON_STYLE[type] ?? ICON_STYLE.HOME | ||
| const opacity = type === 'HOME' ? (liked ? 1 : 0.2) : 1 | ||
|
|
||
| return ( | ||
| <Wrapper $opacity={opacity} $isMy={type === 'MY'}> | ||
| <SvgButton | ||
| icon={Icon} | ||
| onClick={handleClick} | ||
| width={size} | ||
| height={size} | ||
| fill={ | ||
| type === 'HOME' | ||
| ? liked | ||
| ? theme.COLOR['primary-normal'] | ||
| : theme.COLOR['gray-200'] | ||
| : liked | ||
| ? theme.COLOR['primary-normal'] | ||
| : 'none' | ||
| } | ||
maylh marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| stroke={liked ? theme.COLOR['primary-normal'] : theme.COLOR['gray-200']} | ||
| /> | ||
| {type === 'MY' && <p>좋아요</p>} | ||
| </Wrapper> | ||
| ) | ||
| } | ||
|
|
||
| export default LikeButton | ||
|
|
||
| const Wrapper = styled.div<{ $opacity?: number; $isMy: boolean }>` | ||
| opacity: ${({ $opacity }) => $opacity}; | ||
| ${flexRowCenter}; | ||
| ${({ $isMy }) => $isMy && myCdButton}; | ||
| ` | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
좋아요 액션이 성공했을 때,
playlistDetail쿼리만 무효화하고 있습니다. '좋아요한 CD' 목록의 데이터 일관성을 위해myLikedCdList쿼리도 함께 무효화하는 것이 좋습니다.useMyLikedCdList훅의queryKey가['myLikedCdList', sort]이므로,queryClient.invalidateQueries({ queryKey: ['myLikedCdList'] })를 추가하면 관련 목록이 모두 업데이트될 것입니다.1Style Guide References
Footnotes
Tanstack Query를 사용하여 서버 상태를 관리할 때, 데이터 변경(mutation) 후 관련된 쿼리를 무효화하여 데이터 동기화를 유지하는 것이 좋습니다. '좋아요' 상태가 변경되면 '좋아요한 CD 목록'도 업데이트되어야 합니다. ↩