diff --git a/e2e/landing.spec.ts b/e2e/landing.spec.ts index 4794e84..31a288d 100644 --- a/e2e/landing.spec.ts +++ b/e2e/landing.spec.ts @@ -15,13 +15,3 @@ test('로그인 페이지 클릭', async ({ page }) => { await expect(page).toHaveTitle(/Log in/); await expect(page).toHaveURL(/.*login/); }); - -test('영준과 대화하기 클릭', async ({ page }) => { - await page.goto('/'); - - await page.getByRole('link', { name: '영준이와 대화하기' }).click(); - - await expect(page).toHaveTitle(/ToonChat/); - await expect(page).toHaveTitle(/대화 with/); - await expect(page).toHaveURL(/.*chats\/\d+/); -}); diff --git a/src/components/chat/Header.tsx b/src/components/chat/Header.tsx index a15b2fb..c0f9e13 100644 --- a/src/components/chat/Header.tsx +++ b/src/components/chat/Header.tsx @@ -4,21 +4,13 @@ import useSocketStore from '@/store/socket'; import useChatStore from '@/store/chat'; import color from '@/styles/color'; import { useSession } from 'next-auth/react'; +import { CharacterStateProps } from '@/types/characterInfo'; import FriendShip from './characterHeader/FriendShip'; import CharacterInfo from './characterHeader/CharacterInfo'; -import SettingIcon from '../icons/SettingIcon'; - -interface CharacterState { - characterId: string, - characterName: string, - hashTag: string, - imageUrl: string, - settingClick: () => void, -} // TODO: Back 버튼을 누르면 지금 홈으로 돌아가지만 채팅 리스트뷰가 완성되면 그쪽으로 Link 될 예정 -const Header : FC = ({ - characterId, characterName, hashTag, imageUrl, settingClick, +const Header : FC = ({ + characterId, characterName, hashTag, profileImageUrl, }) => { // TODO: 친밀도를 API로 받아와야 작업이 가능함! const [userStatus, setUserStatus] = useState({ @@ -44,15 +36,12 @@ const Header : FC = ({ }, [session]); return (
- + -
); }; diff --git a/src/components/chat/Main.tsx b/src/components/chat/Main.tsx index 8f3092d..c990250 100644 --- a/src/components/chat/Main.tsx +++ b/src/components/chat/Main.tsx @@ -10,12 +10,12 @@ import CharacterSpeak from './messageBox/CharacterSpeak'; import Loading from '../common/dialog/Loading'; interface MainProps { - characterId: string, + characterId: number, characterName: string, - imageUrl: string + profileImageUrl: string } -const Main:FC = ({ characterId, characterName, imageUrl }) => { +const Main:FC = ({ characterId, characterName, profileImageUrl }) => { const { chatContents, clearChatContents, initChatContents } = useChatStore(); const messageEndRef = useRef(null); const [loadingHistory, setLoadingHistory] = useState(false); @@ -42,7 +42,7 @@ const Main:FC = ({ characterId, characterName, imageUrl }) => { = ({ - characterName, hashTag, imageUrl, link, + characterName, hashTag, profileImageUrl, link, }) => ( <> @@ -28,7 +28,7 @@ const CharacterInfo: FC = ({
{`/${characterName}`} = ({ - speaker, content, timestamp, imageUrl, loading = false, + speaker, content, timestamp, profileImageUrl, loading = false, }) => (
{speaker} { useEffect(() => { findAllCharacters() - .then((data) => setCharacterInfoList(data)) + .then((data) => { + setCharacterInfoList(data); + }) .catch((error) => { console.error('Error fetching post:', error); }); @@ -21,17 +23,15 @@ const BoardList = () => {
{characterInfoList ? ( characterInfoList.map((characterInfo) => ( -
+
diff --git a/src/components/community/CommunityHeader.tsx b/src/components/community/CommunityHeader.tsx index 4849f0e..ed7b030 100644 --- a/src/components/community/CommunityHeader.tsx +++ b/src/components/community/CommunityHeader.tsx @@ -30,9 +30,9 @@ const CommunityHeader = () => { characterInfo ? ( ) diff --git a/src/components/community/PostHeader.tsx b/src/components/community/PostHeader.tsx index 439a0b7..61835e8 100644 --- a/src/components/community/PostHeader.tsx +++ b/src/components/community/PostHeader.tsx @@ -28,10 +28,10 @@ const PostHeader = () => { characterInfo ? ( ) : diff --git a/src/components/friends/ChatLogs.tsx b/src/components/friends/ChatLogs.tsx index 334ac4c..291b481 100644 --- a/src/components/friends/ChatLogs.tsx +++ b/src/components/friends/ChatLogs.tsx @@ -2,23 +2,27 @@ import { css } from '@emotion/react'; import TimeStamp from '@/components/common/timeStamp/TimeStamp'; import { useEffect, useState } from 'react'; import { recentChatAPI } from '@/utils/api/chats'; +import { CharacterInfo } from '@/types/characterInfo'; import FriendWrapper from './friend/FriendWrapper'; import FriendInfo from './friend/FriendInfo'; import ChatBadge from './friend/ChatBadge'; +interface recentMessageWithCharacterInfo { + characterInfo: CharacterInfo; + lastMessage: { + content: string; + createdAt: number; + fromUser: boolean; + }; +} + const ChatLogs = () => { // TODO: 데이터셋을 한 번에 API로 받아오면 더 편하게 작업할 수 있을 것 같음 - const [chatLogDataSet, setchatLogDataSet] = useState(chatLogDataSample); + const [recentChatList, setRecentChatList] = useState([]); const callRecentAPI = async () => { - const recentData = await recentChatAPI(); - console.log(recentData); - const tempDataSet = chatLogDataSet.map((chatLog, index) => ({ - ...chatLog, - message: recentData[index].lastMessage.content, - timestamp: recentData[index].lastMessage.createdAt, - })); - setchatLogDataSet([...tempDataSet]); + const recentChatData = await recentChatAPI(); + setRecentChatList([...recentChatData]); }; useEffect(() => { @@ -27,21 +31,20 @@ const ChatLogs = () => { return (
- {chatLogDataSet.map((data, index) => ( - // TODO: 여러 캐릭터가 있을 때 스크롤이 가능한지 확인하기 위함 + {recentChatList.map(({ characterInfo, lastMessage }, index) => (
- - + +
))} @@ -65,21 +68,3 @@ const subInfoWrapperCSS = css` align-items: flex-end; margin-right: 0.625rem; `; - -const chatLogDataSample = [ - { - characterId: '0', - characterName: '이영준', - imageUrl: '/leeyj.png', - message: '.', - timestamp: 123123, - unreadCount: 1, - }, { - characterId: '1', - characterName: '김미소', - imageUrl: '/kimms.png', - message: '.', - timestamp: 123123, - unreadCount: 0, - }, -]; diff --git a/src/components/friends/Friends.tsx b/src/components/friends/Friends.tsx index 8e19333..1697d65 100644 --- a/src/components/friends/Friends.tsx +++ b/src/components/friends/Friends.tsx @@ -1,27 +1,44 @@ import { css } from '@emotion/react'; +import { useEffect, useState } from 'react'; +import { CharacterInfo } from '@/types/characterInfo'; +import { findAllCharacters } from '@/utils/api/character'; import FriendWrapper from './friend/FriendWrapper'; import FriendHashTag from './friend/FriendHashTag'; import FriendInfo from './friend/FriendInfo'; -const Friends = () => ( -
- {characterDataSet.map((data, index) => ( - // TODO: 여러 캐릭터가 있을 때 스크롤이 가능한지 확인하기 위함 - - - - - ))} -
-); +const Friends = () => { + const [characterInfoList, setCharacterInfoList] = useState([]); + + useEffect(() => { + findAllCharacters() + .then((data) => { + setCharacterInfoList(data); + }) + .catch((error) => { + console.error('Error fetching post:', error); + }); + }, []); + + return ( +
+ {characterInfoList.map((characterInfo, index) => ( + // TODO: 여러 캐릭터가 있을 때 스크롤이 가능한지 확인하기 위함 + + + + + ))} +
+ ); +}; export default Friends; @@ -31,21 +48,3 @@ const friendsWrapperCSS = css` word-break: keep-all; padding: 0.375rem; `; - -// TODO: 이 부분은 API에서 떼와야하는 부분 -const characterDataSet = [ - { - characterName: '이영준', - characterId: '0', - hashTag: '#카카오페이지 #김비서가왜그럴까', - statusMessage: '난 왜 이렇게 완벽한걸까...', - imageUrl: '/leeyj.png', - - }, { - characterName: '김미소', - characterId: '1', - hashTag: '#카카오페이지 #김비서가왜그럴까', - statusMessage: '조만간 퇴사하려구요 :)', - imageUrl: '/kimms.png', - }, -]; diff --git a/src/components/friends/Recommends.tsx b/src/components/friends/Recommends.tsx index d07bd83..6afb845 100644 --- a/src/components/friends/Recommends.tsx +++ b/src/components/friends/Recommends.tsx @@ -1,39 +1,37 @@ import { css } from '@emotion/react'; +import { findAllCharacters } from '@/utils/api/character'; +import { CharacterInfo } from '@/types/characterInfo'; +import { useEffect, useState } from 'react'; import RecommendBox from './recommend/RecommendBox'; -// TODO: 이 부분은 API에서 떼와야하는 부분 -const characterDataSet = [ - { - characterName: '이영준', - characterId: '0', - hashTag: '#카카오페이지 #김비서가왜그럴까', - statusMessage: '난 왜 이렇게 완벽한걸까...', - imageUrl: '/leeyj.png', +const Recommends = () => { + const [characterInfoList, setCharacterInfoList] = useState([]); - }, { - characterName: '김미소', - characterId: '1', - hashTag: '#카카오페이지 #김비서가왜그럴까', - statusMessage: '조만간 퇴사하려구요 :)', - imageUrl: '/kimms.png', - }, -]; - -const Recommends = () => ( -
- {characterDataSet.map((data) => ( - - ))} -
-); + useEffect(() => { + findAllCharacters() + .then((data) => { + setCharacterInfoList(data); + }) + .catch((error) => { + console.error('Error fetching post:', error); + }); + }, []); + return ( +
+ {characterInfoList.map((characterInfo) => ( + + ))} +
+ ); +}; export default Recommends; const recommendsCSS = css` diff --git a/src/components/friends/friend/ChatBadge.tsx b/src/components/friends/friend/ChatBadge.tsx index 402e2d1..0c14b11 100644 --- a/src/components/friends/friend/ChatBadge.tsx +++ b/src/components/friends/friend/ChatBadge.tsx @@ -3,17 +3,17 @@ import { css } from '@emotion/react'; import color from '@/styles/color'; interface ChatBadgeProps { - unreadCount: number + unreadCount: boolean } const ChatBadge: FC = ({ unreadCount }) => ( -
{unreadCount}
+
{unreadCount ? '0' : '1'}
); export default ChatBadge; -const countCSS = (unreadCount : number) => css` - visibility : ${unreadCount ? 'visible' : 'hidden'}; +const countCSS = (unreadCount : boolean) => css` + visibility : ${unreadCount ? 'hidden' : 'visible'}; background-color: #F04A4C; border-radius: 50%; height: 1rem; diff --git a/src/components/friends/friend/FriendInfo.tsx b/src/components/friends/friend/FriendInfo.tsx index 3d631ed..a7faf99 100644 --- a/src/components/friends/friend/FriendInfo.tsx +++ b/src/components/friends/friend/FriendInfo.tsx @@ -6,14 +6,14 @@ import color from '@/styles/color'; interface FriendInfoProps { characterName: string, message: string, - imageUrl: string, + profileImageUrl: string, } -const FriendInfo: FC = ({ characterName, message, imageUrl }) => ( +const FriendInfo: FC = ({ characterName, message, profileImageUrl }) => (
{characterName} = ({ - characterName, characterId, hashTag, statusMessage, imageUrl, +const RecommendBox:FC = ({ + characterName, characterId, hashTag, statusMessage, profileImageUrl: imageUrl, }) => ( diff --git a/src/components/profile/CharacterProfileInfo.tsx b/src/components/profile/CharacterProfileInfo.tsx index d80f79f..03c4bcd 100644 --- a/src/components/profile/CharacterProfileInfo.tsx +++ b/src/components/profile/CharacterProfileInfo.tsx @@ -6,17 +6,17 @@ import color from '@/styles/color'; interface CharacterProfileInfoProps { characterName: string, hashTag: string, - imageUrl: string, + profileImageUrl: string, statusMessage: string, } const CharacterProfileInfo: FC = ({ - characterName, hashTag, imageUrl, statusMessage, + characterName, hashTag, profileImageUrl, statusMessage, }) => (
{`/${characterName}`} = ({ characterId }) => { - const [toastMessages, setToastMessages] = useState([]); - const [toastKey, setToastKey] = useState(0); - - const clickHandler = (e: MouseEvent) => { - e.preventDefault(); - setToastKey(toastKey + 1); - setToastMessages([ - ...toastMessages, - { key: toastKey, message: '커뮤니티는 추후에 지원될 예정입니다.' }, - ]); - }; - - const handleToastClose = (key: number) => { - setToastMessages(toastMessages.filter((toast) => toast.key !== key)); - }; - - return ( -
- - - chat - - {/* TODO: community가 생기면 아래 버튼 핸들링, URL등을 바꿔야합니다. */} - - {toastMessages.map((toast) => ( - handleToastClose(toast.key)} - /> - ))} -
- ); -}; +const ProfileRouteButtons:FC = ({ characterId }) => ( +
+ + + chat + + + + Community + +
+); export default ProfileRouteButtons; diff --git a/src/pages/chats/[character].tsx b/src/pages/chats/[character].tsx index 0e0946f..5d50d4b 100644 --- a/src/pages/chats/[character].tsx +++ b/src/pages/chats/[character].tsx @@ -1,103 +1,66 @@ -import { GetServerSideProps } from 'next'; import { css } from '@emotion/react'; import MessageInput from '@/components/chat/MessageInput'; import Header from '@/components/chat/Header'; import Main from '@/components/chat/Main'; import SEO from '@/components/common/head/SEO'; -import Dialog from '@/components/common/dialog/Dialog'; -import TuneSetting from '@/components/chat/tuneSetting/TuneSetting'; import { useEffect, useState } from 'react'; import useChatStore from '@/store/chat'; +import { useRouter } from 'next/router'; +import { CharacterInfo } from '@/types/characterInfo'; +import { findCharacterById } from '@/utils/api/character'; +import Loading from '@/components/common/dialog/Loading'; -interface CharacterProps { - characterName: string, - characterId: string, - hashTag: string, - imageUrl: string, -} - -const Character = ({ - characterProps: { - characterName, characterId, hashTag, imageUrl, - }, -} - : { characterProps: CharacterProps }) => { - const [settingModal, setSettingModal] = useState(false); +const Character = () => { + const router = useRouter(); + const { character: characterId } = router.query; + const [characterInfo, setCharacterInfo] = useState(); const { setChatInfo } = useChatStore(); + useEffect(() => { - setChatInfo(characterName, characterId); - }, []); + if (characterId && typeof characterId === 'string') { + findCharacterById(characterId) + .then((data) => { + setCharacterInfo(data); + setChatInfo(data.characterName, data.characterId); + }) + .catch((error) => { + console.error('Error fetching post:', error); + }); + } + }, [characterId]); return ( <> - +
-
-
setSettingModal(!settingModal)} - /> -
-
- + {characterInfo + ? ( + <> +
+
+
+
+ + + ) : }
- { settingModal && ( - { setSettingModal(false); }} theme="white"> - { setSettingModal(false); }} /> - - )} ); }; export default Character; -// TODO: vercel 배포에서 임시API로는 서버사이드 랜더링이 -// 잘 안되는 상황이었음. 더미데이터를 여기에서 따로 뽑고 이후 서버 연결하면 지울 예정 -const characterDataSet = [ - { - 'bot-name': '이영준', - 'hash-tag': '#카카오페이지 #김비서가왜그럴까', - 'image-url': '/leeyj.png', - }, { - 'bot-name': '김미소', - 'hash-tag': '#카카오페이지 #김비서가왜그럴까', - 'image-url': '/kimms.png', - }, -]; - -export const getServerSideProps -:GetServerSideProps<{characterProps:CharacterProps}> = async (context) => { - const characterId = context.query.character; - - if (Array.isArray(characterId) || !characterId) { - return { - notFound: true, - }; - } - - const idNumber = parseInt(characterId, 10); - if (Number.isNaN(idNumber) || idNumber < 0 || idNumber >= characterDataSet.length) { - return { - notFound: true, - }; - } - const dataSet = characterDataSet[idNumber]; - - return { - props: { - characterProps: { - characterName: dataSet['bot-name'], - characterId, - hashTag: dataSet['hash-tag'], - imageUrl: dataSet['image-url'], - }, - }, - }; -}; - const pageCSS = css` min-height: 100vh; display: flex; diff --git a/src/pages/profile/friends/[character_id].tsx b/src/pages/profile/friends/[character_id].tsx index d045626..c26a7e4 100644 --- a/src/pages/profile/friends/[character_id].tsx +++ b/src/pages/profile/friends/[character_id].tsx @@ -1,4 +1,3 @@ -import { GetServerSideProps } from 'next'; import { css } from '@emotion/react'; import Link from 'next/link'; import SEO from '@/components/common/head/SEO'; @@ -7,98 +6,60 @@ import ProfileFriendShip from '@/components/profile/ProfileFriendShip'; import BackwordIcon from '@/components/icons/BackwordIcon'; import ProfileRouteButtons from '@/components/profile/ProfileRouteButtons'; import CharacterProfileInfo from '@/components/profile/CharacterProfileInfo'; +import { findCharacterById } from '@/utils/api/character'; +import { CharacterInfo } from '@/types/characterInfo'; +import { useEffect, useState } from 'react'; +import { useRouter } from 'next/router'; +import Loading from '@/components/common/dialog/Loading'; -interface CharacterProfileProps { - characterName: string, - characterId: string, - hashTag: string, - imageUrl: string, - backgroundImageUrl: string, - statusMessage: string, -} - -const FriendProfile = ({ - characterProfileProps: { - characterName, characterId, hashTag, imageUrl, backgroundImageUrl, statusMessage, - }, -} : {characterProfileProps: CharacterProfileProps }) => { - console.log(characterName, characterId, hashTag, imageUrl, backgroundImageUrl, statusMessage); +const FriendProfile = () => { + const router = useRouter(); + const { character_id: characterId } = router.query; + const [characterInfo, setCharacterInfo] = useState(); + useEffect(() => { + if (characterId && typeof characterId === 'string') { + findCharacterById(characterId) + .then((data) => { + setCharacterInfo(data); + }) + .catch((error) => { + console.error('Error fetching post:', error); + }); + } + }, [characterId]); return ( <> - -
-
- - - -
-
- - -
- -
+ + {characterInfo + ? ( + <> +
+
+ + + +
+
+ + +
+ +
+ + ) + : } ); }; export default FriendProfile; -const characterProfileDataSet = [ - { - 'bot-name': '이영준', - 'hash-tag': '#카카오페이지 #김비서가왜그럴까', - 'image-url': '/leeyj.png', - 'background-image-url': '/leeyjback.png', - 'status-message': '난 왜 이렇게 완벽한걸까...', - }, { - 'bot-name': '김미소', - 'hash-tag': '#카카오페이지 #김비서가왜그럴까', - 'image-url': '/kimms.png', - 'background-image-url': '/kimmsback.png', - 'status-message': '퇴사할 예정입니다. :)', - }, -]; - -export const getServerSideProps -: GetServerSideProps<{characterProfileProps: CharacterProfileProps}> = async (context) => { - const characterId = context.query.character_id; - // TODO: 서버에서 캐릭터 profile을 얻게되면 API 호출을 할 예정 - if (Array.isArray(characterId) || !characterId) { - return { - notFound: true, - }; - } - - const idNumber = parseInt(characterId, 10); - if (Number.isNaN(idNumber) || idNumber < 0 || idNumber >= characterProfileDataSet.length) { - return { - notFound: true, - }; - } - - const dataSet = characterProfileDataSet[idNumber]; - - return { - props: { - characterProfileProps: { - characterName: dataSet['bot-name'], - characterId, - hashTag: dataSet['hash-tag'], - imageUrl: dataSet['image-url'], - backgroundImageUrl: dataSet['background-image-url'], - statusMessage: dataSet['status-message'], - }, - }, - }; -}; - const pageCSS = css` height: 100vh; width: 400px; diff --git a/src/pages/profile/index.tsx b/src/pages/profile/index.tsx index 6276fcc..df266aa 100644 --- a/src/pages/profile/index.tsx +++ b/src/pages/profile/index.tsx @@ -6,8 +6,6 @@ import { useSession } from 'next-auth/react'; const Profile = () => { const { data: session }: any = useSession(); console.log(session); - console.log(session?.accessToken); - console.log(session?.refreshToken); return ( <> diff --git a/src/types/characterInfo.d.ts b/src/types/characterInfo.d.ts index 0bd273e..8007048 100644 --- a/src/types/characterInfo.d.ts +++ b/src/types/characterInfo.d.ts @@ -1,9 +1,12 @@ export interface CharacterInfo { - backgroundUrl: string, - code: string, - hashtags: string, - id: number, - name: string, - profileUrl: string, - stateMessage: string + characterId: number, + characterName: string, + hashTag: string, + statusMessage: string + profileImageUrl: string, + backgroundImageUrl: string, } + +export type CharacterStateProps = Omit; + +export type RecommendCharacterProps = Omit; diff --git a/src/utils/api/character.ts b/src/utils/api/character.ts index b0f194c..ffd578d 100644 --- a/src/utils/api/character.ts +++ b/src/utils/api/character.ts @@ -7,5 +7,6 @@ export const findAllCharacters = async () => { export const findCharacterById = async (characterId: string) => { const result = await webServerInstance.get(`/characters/${characterId}`); + console.log(`find By Id characters : ${result.data}`); return result.data; }; diff --git a/src/utils/api/chats.ts b/src/utils/api/chats.ts index b7c61b4..b500c31 100644 --- a/src/utils/api/chats.ts +++ b/src/utils/api/chats.ts @@ -5,7 +5,7 @@ export const recentChatAPI = async () => { return result.data; }; -export const chatHistoryAPI = async (characterId: string) => { +export const chatHistoryAPI = async (characterId: number) => { const result = await chatInstance.get(`/chat/history/${characterId}`); return result.data; }; diff --git a/src/utils/services/chats.ts b/src/utils/services/chats.ts index 4598b09..cf873ff 100644 --- a/src/utils/services/chats.ts +++ b/src/utils/services/chats.ts @@ -10,7 +10,7 @@ interface resultData { type GetHistory = ( setLoading: (isLoading: boolean) => void, - characterId: string, + characterId: number, characterName: string, initChatContents: (history:ChatContentsState[]) => void ) => void