먼제 상대방 유저가 메시지를 읽었는지 확인하는 아이콘을 만들껀데
왼쪽에 보라색 아이콘이 보입니다 읽지않은 상태를 말합니다 만들어 보겠습니다.
//frontend src/components/Chat/Conversations/ConversationList.tsx
import { Box, Text } from "@chakra-ui/react";
import { Session } from "next-auth";
import { useRouter } from "next/router";
import { useState } from "react";
// import { ConversationPopulated } from "../../../../../backend/src/util/types";
import ConversationItem from "./ConversationItem";
import ConversationMoal from "./Modal/Modal";
interface ConversationListProps {
session: Session;
conversations: any;
onViewConversation: (
conversationId: string,
hasSeenLatestMessage: boolean
) => void;
}
const ConversationList = ({
session,
conversations,
onViewConversation,
}: ConversationListProps) => {
const [isOpen, setIsOpen] = useState(false);
const onOpen = () => {
setIsOpen(true);
};
const onClose = () => {
setIsOpen(false);
};
const router = useRouter();
const {
user: { id: userId },
} = session;
return (
<Box width="100%">
<Box
py={2}
px={4}
mb={4}
bg="blackAlpha.300"
borderRadius={4}
cursor="pointer"
onClick={onOpen}
>
<Text textAlign="center" color="whiteAlpha.800" fontWeight={500}>
Find or start a conversation
</Text>
</Box>
<ConversationMoal session={session} isOpen={isOpen} onClose={onClose} />
{conversations?.map((conversation: any) => {
const participant = conversation.participants.find(
(p: any) => p.user.id === userId
);
return (
<ConversationItem
key={conversation.id}
userId={userId}
conversation={conversation}
onClick={() =>
onViewConversation(
conversation.id,
participant?.hasSeenLatestMessage
)
}
hasSeenLatestMessage={participant?.hasSeenLatestMessage}
isSelected={conversation.id === router.query.conversation}
/>
);
})}
</Box>
);
};
export default ConversationList;
onViewConversation() 이라는 함수가 있는데 여기에 hasSeenLatestMessage라는 최신메시지라는 boolean 값을 ConversationItem 컴포넌트에 props로 전달 해줍니다.
//backend src/graphql/typeDefs/conversation.ts
type Mutation {
markConversationAsRead(userId: String!, conversationId: String!): Boolean
}
//backend src/graphql/resolvers/conversation.ts
markConversationAsRead: async function (
_: any,
args: { userId: string; conversationId: string },
context: GraphQLContext
): Promise<boolean> {
const { session, prisma } = context;
const { userId, conversationId } = args;
if (!session?.user) {
throw new GraphQLError("Not authorized");
}
try {
const participant = await prisma.conversationParticipant.findFirst({
where: {
userId,
conversationId,
},
});
if (!participant) {
throw new GraphQLError("Participant entity not found");
}
await prisma.conversationParticipant.update({
where: {
id: participant.id,
},
data: {
hasSeenLatestMessage: true,
},
});
return true;
} catch (err: any) {
console.log("markConversationAsRead error", err);
throw new GraphQLError(err?.message);
}
},
mutation에 markConversationAsRead() 추가 해줍니다 프론트에서 요청하면 hasSeenLatestMessage를 true로 만들어줍니다.
//frontend src/graphql/operations/conversation.ts
markConversationAsRead: gql`
mutation MarkConversationAsRead(
$userId: String!
$conversationId: String!
) {
markConversationAsRead(userId: $userId, conversationId: $conversationId)
}
`,
},
프론트쪽 Mutation에서도 markConversationAsRead만들어줍니다.
//frontend src/compoents/Chat/Conversations/ConversationWrapper.tsx
const [markConversationAsRead] = useMutation<
{ markConversationAsRead: boolean },
{ userId: string; conversationId: string }
>(ConversationOperations.Mutations.markConversationAsRead);
const onViewConversation = async (
conversationId: string,
hasSeenLatestMessage: boolean
) => {
router.push({ query: { conversationId } });
if (hasSeenLatestMessage) return;
try {
await markConversationAsRead({
variables: {
userId,
conversationId,
},
optimisticResponse: {
markConversationAsRead: true,
},
update: (cache) => {
const participantsFragment = cache.readFragment<{
participants: Array<ParticipantPopulated>;
}>({
id: `Conversation:${conversationId}`,
fragment: gql`
fragment Participants on Conversation {
participants {
user {
id
username
}
hasSeenLatestMessage
}
}
`,
});
if (!participantsFragment) return;
const participants = [...participantsFragment.participants];
const userParticipantIdx = participants.findIndex(
(p: any) => p.user.id === userId
);
if (userParticipantIdx === -1) return;
const userParticipant = participants[userParticipantIdx];
participants[userParticipantIdx] = {
...userParticipant,
hasSeenLatestMessage: true,
};
cache.writeFragment({
id: `Conversation:${conversationId}`,
fragment: gql`
fragment UpdatedParticipant on Conversation {
participants
}
`,
data: {
participants,
},
});
},
});
} catch (err) {
console.log("onViewConversations", err);
}
};
chche부분 캐시는 Apollo 공식문서를 찾아보는게 좋을것 같습니다. 프론트에서 userId, conversationId 백엔드로 요청 보내줍니다.
여기에서 Test 대화 리스트를 클릭하면
왼쪽에 보라색 아이콘이 사라지는걸 확인할 수 있습니다.
'Project' 카테고리의 다른 글
개발자 스터디 Book (0) | 2023.09.26 |
---|---|
GraphQL 메시지 대화 삭제 (0) | 2023.09.20 |
GraphQL 메시지 보내기 (0) | 2023.09.05 |
GraphQL 대화생성 Subscription (0) | 2023.08.29 |
WebSocket/ Subscription (0) | 2023.08.29 |