Skip to content

Commit

Permalink
Next.js 14 app router 폴더 구조 개선화 작업 (#44)
Browse files Browse the repository at this point in the history
* [fix]: yarn -> pnpm settings

* [fix]: react-query ssr logic apply

* [fix]: yarn -> pnpm 구성하면서 husky 수정

* [feat]: next.js metadata export warning message off

* [fix]: 루트 폴더를 app 폴더로 수정

* [fix]: husky

* [fix]: 쿠키값 서버사이드에서 가져오게 변경 및 기타 파일 패스수정

* [fix]: 메타데이터 설정 변경

* [refact]: Chat 페이지 폴더구조 최적화

* Revert [fix]: 루트 폴더를 app 폴더로 수정 This reverts commit 115e3fe

* [fix]: 사용하지않는 컴포넌트 제거
  • Loading branch information
VictoryJu authored Nov 17, 2024
1 parent 8206741 commit 1109405
Show file tree
Hide file tree
Showing 91 changed files with 6,031 additions and 6,539 deletions.
10 changes: 9 additions & 1 deletion .eslintrc.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,12 @@ module.exports = {
{ allowConstantExport: true },
],
},
}
overrides: [
{
files: ['app/**/layout.tsx'],
rules: {
'react-refresh/only-export-components': 'off',
},
},
],
};
4 changes: 2 additions & 2 deletions .husky/pre-commit
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#!/bin/sh
#!/usr/bin/env sh
. "$(dirname "$0")/_/husky.sh"

yarn lint-staged
pnpm lint-staged
1 change: 0 additions & 1 deletion .yarnrc.yml

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import styles from './ChatMessage.module.scss';
import styles from './chat-message.module.scss';

interface ChatMessageProps {
isSender: boolean;
Expand Down
104 changes: 104 additions & 0 deletions app/chat/[id]/_components/chat-room.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
'use client';
import { CameraIcon, UploadIcon } from '@/components/ui/icons/icon';
import ArrowLeftTailIcon from '@/components/ui/icons/icon/ArrowLeftTail';
import DotsVerticalIcon from '@/components/ui/icons/icon/DotsVertical';
import { useAuthStore } from '@/stores/useAuthStore';
import { useRouter } from 'next/navigation';
import { useEffect, useRef, useState } from 'react';
import { useChat } from '../_hooks/use-chat';
import ChatMessage from './chat-message';
import styles from './chat-room.module.scss';

interface ChatRoomProps {
chatRoomId: string;
storeName: string;
}

const ChatRoom = ({ chatRoomId, storeName }: ChatRoomProps) => {
const senderUuid = useAuthStore((state) => state.uuid);
const { push } = useRouter();

const [chatMessageContent, setChatMessageContent] = useState('');

const messageEndRef = useRef<HTMLDivElement>(null);
const textareaRef = useRef<HTMLTextAreaElement>(null);

const { messages, sendMessage } = useChat(chatRoomId);

const chatMessageType = 'TEXT';

const adjustHeight = () => {
if (textareaRef.current) {
const scrollHeight = textareaRef.current.scrollHeight;
const lineHeight = 29; // leading-[29px]와 일치
const maxHeight = lineHeight * 2; // 최대 2줄
textareaRef.current.style.height = `${Math.min(scrollHeight, maxHeight)}px`;
}
};

const handleSocketMessage = () => {
sendMessage({
chatRoomId,
user: senderUuid,
chatMessageType,
chatMessageContent,
});
setChatMessageContent('');
};

useEffect(() => {
if (textareaRef.current) {
adjustHeight();
}
if (messageEndRef.current) {
messageEndRef.current.scrollIntoView({ behavior: 'smooth' });
}
}, [messages]);

return (
<>
<div className={styles.ChatRoomHeader}>
<ArrowLeftTailIcon
onClick={() => push('/chat-list')}
width={24}
height={24}
/>
<div className={styles.ChatRoomTitle}> {storeName} </div>
<DotsVerticalIcon width={24} height={24} />
</div>
<div className={styles.ChatRoomWrapper}>
{messages.map((message) => (
<ChatMessage
key={message.chatMessageId}
isSender={message.user.uuid === senderUuid}
message={message.chatMessageContent}
// time={message.createdAt}
// imageUrl={message.imageUrl}
/>
))}
<div ref={messageEndRef} />
</div>

<div className={styles.ChatInputWrapper}>
<CameraIcon className={styles.CameraIcon} width={24} height={24} />
<div className={styles.ChatInputContent}>
<textarea
ref={textareaRef}
rows={1}
className={styles.ChatInput}
placeholder="메시지를 입력하세요"
onChange={(e) => setChatMessageContent(e.target.value)}
/>
<UploadIcon
onClick={handleSocketMessage}
className={styles.UploadIcon}
width={20}
height={20}
/>
</div>
</div>
</>
);
};

export default ChatRoom;
6 changes: 3 additions & 3 deletions src/hooks/useChat.ts → app/chat/[id]/_hooks/use-chat.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { ChatMessage } from '@/hooks/api/types/chat';
import { useChatRoomMessages } from '@/hooks/api/useChat';
import { useSocket } from '@/hooks/socket/useSocket';
import { useCallback, useEffect, useRef, useState } from 'react';
import { useChatRoomMessages } from './api/useChat';
import { ChatMessage } from './api/types/chat';
import { useSocket } from './socket/useSocket';

/**
*
Expand Down
16 changes: 16 additions & 0 deletions app/chat/[id]/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import ChatRoom from './_components/chat-room';

interface ChatPageProps {
params: {
id: string;
};
searchParams: {
storeName: string;
};
}

const ChatPage = ({ params, searchParams }: ChatPageProps) => {
return <ChatRoom chatRoomId={params.id} storeName={searchParams.storeName} />;
};

export default ChatPage;
25 changes: 25 additions & 0 deletions app/chat/_components/chat-container.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
'use client';
import { useChatRooms } from '@/hooks/api/useChat';
import { useAuthStore } from '@/stores/useAuthStore';
import { useState } from 'react';
import styles from './chat-container.module.scss';
import ChatListEdit from './chat-list-edit/chat-list-edit';
import ChatList from './chat-list/chat-list';

const ChatContainer = () => {
const [isEdit, setIsEdit] = useState(false);
const token = useAuthStore((state) => state.accessToken);
const { data: chatRoomsData } = useChatRooms(token ?? '');

return (
<div className={styles.ChatRoomsWrapper}>
{isEdit ? (
<ChatListEdit chatRoomsData={chatRoomsData} setIsEdit={setIsEdit} />
) : (
<ChatList chatRoomsData={chatRoomsData} setIsEdit={setIsEdit} />
)}
</div>
);
};

export default ChatContainer;
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import RadioActiveIcon from '@/components/ui/icons/icon/RadioActiveIcon';
import RadioIcon from '@/components/ui/icons/icon/RadioIcon';
import { ErrorLogoIcon } from '@/components/ui/icons/logo';
import { ChatRoom } from '@/hooks/api/types/chat';
import RadioActiveIcon from '@/icons/icon/RadioActiveIcon';
import RadioIcon from '@/icons/icon/RadioIcon';
import { ErrorLogoIcon } from '@/icons/logo';
import { formatChatDate } from '@/utils/format';
import { ReactNode, createContext } from 'react';
import styles from './ChatItemBase.module.scss';
import styles from './chat-item.module.scss';

type ChatItemBaseProps = {
type ChatItemProps = {
children?: ReactNode;
onClick?: () => void;
};
Expand All @@ -18,7 +18,7 @@ type ChatItemContentProps = {

const ChatItemContext = createContext('');

const ChatItemBase = ({ children, onClick }: ChatItemBaseProps) => {
const ChatItem = ({ children, onClick }: ChatItemProps) => {
return (
<ChatItemContext.Provider value="">
<div onClick={onClick} className={styles.ChatItemWrapper}>
Expand Down Expand Up @@ -63,8 +63,8 @@ const EditButton = ({ isActive }: { isActive: boolean }) => {
);
};

ChatItemBase.ChatItemContent = ChatItemContent;
ChatItemBase.UnViewCount = UnViewCount;
ChatItemBase.EditButton = EditButton;
ChatItem.ChatItemContent = ChatItemContent;
ChatItem.UnViewCount = UnViewCount;
ChatItem.EditButton = EditButton;

export default ChatItemBase;
export default ChatItem;
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { ChatRoom } from '@/hooks/api/types/chat';
import ChatItemBase from './ChatItemBase/ChatItemBase';
import ChatItem from './chat-item';

interface ChatRoomItemsProps {
chatRoomsData: ChatRoom[];
isEdit?: boolean;
onItemClick?: (chatRoomId: number, storeName?: string) => void;
onItemClick?: (chatRoomId: number, storeName: string) => void;
selectedItems?: number[];
}

Expand All @@ -19,24 +19,24 @@ const ChatRoomItems = ({
{chatRoomsData.map((chatInfo) => {
const unViewedMsgCount = !isEdit && chatInfo?.unViewedMsgCount > 0;
return (
<ChatItemBase
<ChatItem
key={chatInfo.chatRoomId}
onClick={() =>
onItemClick?.(chatInfo.chatRoomId, chatInfo.chatRoomName)
}
>
{isEdit && (
<ChatItemBase.EditButton
<ChatItem.EditButton
isActive={selectedItems.includes(chatInfo.chatRoomId)}
/>
)}
<ChatItemBase.ChatItemContent chatInfo={chatInfo} />
<ChatItem.ChatItemContent chatInfo={chatInfo} />
{unViewedMsgCount && (
<ChatItemBase.UnViewCount
<ChatItem.UnViewCount
unViewedMsgCount={chatInfo.unViewedMsgCount}
/>
)}
</ChatItemBase>
</ChatItem>
);
})}
</>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
import ArrowLeftTailIcon from '@/icons/icon/ArrowLeftTail';
import ArrowLeftTailIcon from '@/components/ui/icons/icon/ArrowLeftTail';
import React, { useState } from 'react';

import Button from '@/components/common/Button/Button';
import Button from '@/components/ui/common/Button/Button';
import { ChatRoom } from '@/hooks/api/types/chat';
import ChatRoomItems from '../ChatRooms/ChatRoomItems';
import styles from './ChatRoomsEdit.module.scss';
import ChatRoomItems from '../chat-item/chat-items';
import styles from './chat-list-edit.module.scss';

type ChatRoomsEditProps = {
type ChatListEditProps = {
setIsEdit: React.Dispatch<boolean>;
chatRoomsData: ChatRoom[];
};

const ChatRoomsEdit = ({ setIsEdit, chatRoomsData }: ChatRoomsEditProps) => {
const ChatListEdit = ({ setIsEdit, chatRoomsData }: ChatListEditProps) => {
const [selectedEditChatRooms, setSelectedEditChatRooms] = useState<number[]>(
[]
);
Expand Down Expand Up @@ -78,4 +78,4 @@ const ChatRoomsEdit = ({ setIsEdit, chatRoomsData }: ChatRoomsEditProps) => {
);
};

export default ChatRoomsEdit;
export default ChatListEdit;
Original file line number Diff line number Diff line change
@@ -1,22 +1,23 @@
import EditIcon from '@/components/ui/icons/icon/EditIcon';
import { ChatRoom } from '@/hooks/api/types/chat';
import EditIcon from '@/icons/icon/EditIcon';
import { useRouter } from 'next/navigation';
import React from 'react';
import ChatRoomItems from './ChatRoomItems';
import styles from './ChatRoomList.module.scss';
import { useRouter } from 'next/router';
import ChatRoomItems from '../chat-item/chat-items';
import styles from './chat-list.module.scss';

interface ChatRoomListProps {
interface ChatListProps {
chatRoomsData: ChatRoom[];
setIsEdit: React.Dispatch<React.SetStateAction<boolean>>;
}

const ChatRoomList = ({ chatRoomsData, setIsEdit }: ChatRoomListProps) => {
const ChatList = ({ chatRoomsData, setIsEdit }: ChatListProps) => {
const handleEditIcon = () => {
setIsEdit((prev) => !prev);
};
const router = useRouter();
const { push } = useRouter();

const onClickRoute = (chatRoomId: number, storeName: string) => {
router.push(`${chatRoomId}`);
push(`/chat/${chatRoomId}?storeName=${storeName}`);
};

return (
Expand All @@ -32,12 +33,12 @@ const ChatRoomList = ({ chatRoomsData, setIsEdit }: ChatRoomListProps) => {
</div>
<ChatRoomItems
chatRoomsData={chatRoomsData}
onItemClick={(chatRoomId: number, storeName?: string) => {
onItemClick={(chatRoomId: number, storeName: string) => {
onClickRoute(chatRoomId, storeName || '');
}}
/>
</div>
);
};

export default ChatRoomList;
export default ChatList;
5 changes: 5 additions & 0 deletions app/chat/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import ChatContainer from './_components/chat-container';

export default function ChatsPage() {
return <ChatContainer />;
}
33 changes: 33 additions & 0 deletions app/get-query-client.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import {
QueryClient,
defaultShouldDehydrateQuery,
isServer,
} from '@tanstack/react-query';

function makeQueryClient() {
return new QueryClient({
defaultOptions: {
queries: {
staleTime: 60 * 1000,
refetchOnMount: false,
refetchOnWindowFocus: false,
},
dehydrate: {
shouldDehydrateQuery: (query) =>
defaultShouldDehydrateQuery(query) ||
query.state.status === 'pending',
},
},
});
}

let browserQueryClient: QueryClient | undefined = undefined;

export function getQueryClient() {
if (isServer) {
return makeQueryClient();
} else {
if (!browserQueryClient) browserQueryClient = makeQueryClient();
return browserQueryClient;
}
}
Loading

0 comments on commit 1109405

Please sign in to comment.