diff --git a/src/components/common/ToggleSwitch.tsx b/src/components/common/ToggleSwitch.tsx index 1b4e3f2..3d4a8ac 100644 --- a/src/components/common/ToggleSwitch.tsx +++ b/src/components/common/ToggleSwitch.tsx @@ -5,11 +5,20 @@ interface ToggleSwitchProps { label: string; enabled: boolean; onChange: (enabled: boolean) => void; + disabled?: boolean; layout?: 'vertical' | 'horizontal'; className?: string; } -function ToggleSwitch({ icon: Icon, label, enabled, onChange, layout = 'vertical', className }: ToggleSwitchProps) { +function ToggleSwitch({ + icon: Icon, + label, + enabled, + onChange, + disabled = false, + layout = 'vertical', + className, +}: ToggleSwitchProps) { const isHorizontal = layout === 'horizontal'; return ( @@ -38,9 +47,10 @@ function ToggleSwitch({ icon: Icon, label, enabled, onChange, layout = 'vertical type="button" aria-label={label} aria-pressed={enabled} + disabled={disabled} onClick={() => onChange(!enabled)} className={twMerge( - 'relative touch-manipulation rounded-full transition-colors focus-visible:ring-2 focus-visible:ring-indigo-300 focus-visible:ring-offset-2 focus-visible:outline-none', + 'relative touch-manipulation rounded-full transition-colors focus-visible:ring-2 focus-visible:ring-indigo-300 focus-visible:ring-offset-2 focus-visible:outline-none disabled:cursor-not-allowed disabled:opacity-60', isHorizontal ? `h-7 w-12 border border-indigo-50 ${enabled ? 'bg-indigo-700' : 'bg-indigo-50'}` : `h-5 w-9 ${enabled ? 'bg-primary' : 'bg-indigo-100'}` diff --git a/src/components/layout/Header/components/ChatHeader.tsx b/src/components/layout/Header/components/ChatHeader.tsx index c660742..cda0ee0 100644 --- a/src/components/layout/Header/components/ChatHeader.tsx +++ b/src/components/layout/Header/components/ChatHeader.tsx @@ -10,7 +10,7 @@ function ChatHeader() { const { chatRoomId } = useParams(); const numericRoomId = Number(chatRoomId); - const { chatRoomList, clubMembers, toggleMute } = useChat(numericRoomId); + const { chatRoomList, clubMembers, toggleMute, isTogglingMute } = useChat(numericRoomId); const chatRoom = chatRoomList.rooms.find((room) => room.roomId === numericRoomId); @@ -55,10 +55,12 @@ function ChatHeader() { 알림 diff --git a/src/pages/Chat/hooks/useChat.ts b/src/pages/Chat/hooks/useChat.ts index 74dac32..12bed0e 100644 --- a/src/pages/Chat/hooks/useChat.ts +++ b/src/pages/Chat/hooks/useChat.ts @@ -17,9 +17,12 @@ const useChat = (chatRoomId?: number) => { refetchInterval: 5000, }); - const { mutateAsync: createChatRoom } = useMutation({ + const createChatRoomMutation = useMutation({ mutationKey: ['createChatRoom'], mutationFn: (userId: number) => postChatRooms(userId), + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: chatQueryKeys.rooms() }); + }, }); const { @@ -50,7 +53,7 @@ const useChat = (chatRoomId?: number) => { const totalUnreadCount = chatRoomList.rooms.reduce((sum, room) => sum + room.unreadCount, 0); - const { mutate: sendMessage } = useMutation({ + const sendMessageMutation = useMutation({ mutationKey: ['sendMessage', chatRoomId], mutationFn: postChatMessage, @@ -69,7 +72,7 @@ const useChat = (chatRoomId?: number) => { const { data: clubMembersData } = useGetClubMembers(clubId); - const { mutateAsync: toggleMute } = useMutation({ + const toggleMuteMutation = useMutation({ mutationKey: ['toggleMute', chatRoomId], mutationFn: async () => { if (!chatRoomId) { @@ -87,15 +90,18 @@ const useChat = (chatRoomId?: number) => { return { chatRoomList, - createChatRoom, + createChatRoom: createChatRoomMutation.mutateAsync, + isCreatingChatRoom: createChatRoomMutation.isPending, chatMessages: allMessages, fetchNextPage, hasNextPage, isFetchingNextPage, totalUnreadCount, - sendMessage, + sendMessage: sendMessageMutation.mutate, + isSendingMessage: sendMessageMutation.isPending, clubMembers: clubMembersData?.clubMembers ?? [], - toggleMute, + toggleMute: toggleMuteMutation.mutateAsync, + isTogglingMute: toggleMuteMutation.isPending, }; }; diff --git a/src/pages/Club/Application/clubFeePage.tsx b/src/pages/Club/Application/clubFeePage.tsx index fd4915a..ac8c3a0 100644 --- a/src/pages/Club/Application/clubFeePage.tsx +++ b/src/pages/Club/Application/clubFeePage.tsx @@ -15,7 +15,7 @@ function ClubFeePage() { const { clubId } = useParams(); const navigate = useNavigate(); const { data: clubFee } = useGetClubFee(Number(clubId)); - const { applyToClub } = useApplyToClub(Number(clubId)); + const { applyToClub, isPending: isApplyingToClub } = useApplyToClub(Number(clubId)); const { answers, clubId: storedClubId } = useClubApplicationStore(); useEffect(() => { @@ -23,13 +23,13 @@ function ClubFeePage() { navigate(`/clubs/${clubId}/apply`, { replace: true }); } }, [storedClubId, clubId, navigate]); - const { mutateAsync: uploadImage } = useUploadImage('CLUB'); + const { mutateAsync: uploadImage, isPending: isUploadingImage } = useUploadImage('CLUB'); const fileInputRef = useRef(null); const [previewUrl, setPreviewUrl] = useState(null); const [imageFile, setImageFile] = useState(null); - const [isSubmitting, setIsSubmitting] = useState(false); const { value: isImageOpen, setTrue: openImage, setFalse: closeImage } = useBooleanState(); + const isSubmitting = isApplyingToClub || isUploadingImage; useEffect(() => { return () => { @@ -49,14 +49,8 @@ function ClubFeePage() { const handleSubmit = async () => { if (!imageFile) return; - setIsSubmitting(true); - - try { - const { fileUrl } = await uploadImage(imageFile); - await applyToClub({ answers, feePaymentImageUrl: fileUrl }); - } finally { - setIsSubmitting(false); - } + const { fileUrl } = await uploadImage(imageFile); + await applyToClub({ answers, feePaymentImageUrl: fileUrl }); }; return ( @@ -129,7 +123,7 @@ function ClubFeePage() { onClick={handleSubmit} disabled={!imageFile || isSubmitting} > - 제출하기 + {isSubmitting ? '제출 중...' : '제출하기'} {isImageOpen && previewUrl && ( diff --git a/src/pages/Club/ClubDetail/components/ClubIntro.tsx b/src/pages/Club/ClubDetail/components/ClubIntro.tsx index a7b178c..7425a99 100644 --- a/src/pages/Club/ClubDetail/components/ClubIntro.tsx +++ b/src/pages/Club/ClubDetail/components/ClubIntro.tsx @@ -1,4 +1,3 @@ -import { useState } from 'react'; import { useNavigate } from 'react-router-dom'; import type { ClubDetailResponse } from '@/apis/club/entity'; import HumanIcon from '@/assets/svg/human.svg'; @@ -14,20 +13,11 @@ interface ClubIntroProps { function ClubIntro({ clubDetail }: ClubIntroProps) { const navigate = useNavigate(); - const { createChatRoom } = useChat(); - - const [isSubmitting, setIsSubmitting] = useState(false); + const { createChatRoom, isCreatingChatRoom } = useChat(); const handleInquireClick = async () => { - if (isSubmitting) return; - - try { - setIsSubmitting(true); - const response = await createChatRoom(clubDetail.presidentUserId); - navigate(`/chats/${response.chatRoomId}`); - } finally { - setIsSubmitting(false); - } + const response = await createChatRoom(clubDetail.presidentUserId); + navigate(`/chats/${response.chatRoomId}`); }; return ( @@ -70,11 +60,11 @@ function ClubIntro({ clubDetail }: ClubIntroProps) { diff --git a/src/pages/Manager/ManagedClubProfile/index.tsx b/src/pages/Manager/ManagedClubProfile/index.tsx index 1e9f16f..37e1027 100644 --- a/src/pages/Manager/ManagedClubProfile/index.tsx +++ b/src/pages/Manager/ManagedClubProfile/index.tsx @@ -203,10 +203,12 @@ function ManagedClubInfo() {
동아리 정보를 수정하시겠어요?
@@ -55,10 +56,11 @@ function Profile() {