Project

GraphQL 메시지 읽음으로 표시

프도의길 2023. 9. 18. 16:01

먼제 상대방 유저가 메시지를 읽었는지 확인하는 아이콘을 만들껀데

왼쪽에 보라색 아이콘이 보입니다 읽지않은 상태를 말합니다 만들어 보겠습니다.

//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