Mkurowski03 / market-frontend

BigShortBet$ P2P Market is a cutting-edge peer-to-peer marketplace built on the principles of decentralization that allows direct P2P trading on unique & event-based markets. Enjoy ultra-low fees and no swaps. Powered by $BigSB.
https://app.bigsb.io
GNU General Public License v3.0
0 stars 0 forks source link

chat2 #3

Open Mkurowski03 opened 1 month ago

Mkurowski03 commented 1 month ago

Is your feature request related to a problem? Please describe. I want to add feature that will allow users to chat peer to peer whenever they have position pair.

codeautopilot[bot] commented 1 month ago

Potential solution

To implement the peer-to-peer chat functionality, we need to update various parts of the application, including state management, GraphQL queries and mutations, and the user interface. The solution involves:

  1. State Management: Adding atoms to manage chat messages, the current chat interlocutor, and loading/error states.
  2. GraphQL Queries and Mutations: Defining queries to fetch chat messages and mutations to send messages.
  3. User Interface: Integrating the chat interface into the TradingHub component and updating the chat message component to support additional features like timestamps and message status.

How to implement

Step 1: Update State Management in store/store.ts

Add atoms to manage chat messages, the current chat interlocutor, and loading/error states.

Code:

import { TradingHubStateType } from '@/components/Market/TradingHub/TradingHub';
import { EnrichedMarketType } from '@/types/marketTypes';
import { atom } from 'jotai';

/* MARKETS ATOM */
export const marketsAtom = atom<EnrichedMarketType[]>([]);

/* CHOSEN MARKET ATOM */
export const chosenMarketAtom = atom<EnrichedMarketType | undefined>(undefined);

/* TRADING HUB */
export const tradingHubStateAtom = atom<TradingHubStateType>('chart');
export const tradingHubPositionsCountAtom = atom<number>(0);
export const tradingHubOrdersCountAtom = atom<number>(0);

/* CHAT */

/* Atom to store chat messages */
export const chatMessagesAtom = atom<{ [key: string]: string[] }>({});

/* Atom to manage the current chat interlocutor */
export const chosenInterlocutorAtom = atom<string | null>(null);

/* Atom to manage loading state for chat operations */
export const chatLoadingAtom = atom<boolean>(false);

/* Atom to manage error state for chat operations */
export const chatErrorAtom = atom<string | null>(null);

Step 2: Define GraphQL Queries and Mutations in requests/queries.ts

Add queries to fetch chat messages and mutations to send messages.

Code:

import { gql } from '@apollo/client';

/* Query for fetching chat messages between two users */
export const GET_CHAT_MESSAGES_QUERY = gql`
  query getChatMessages($userId1: String!, $userId2: String!) {
    chatMessages(
      where: {
        OR: [
          { sender_eq: $userId1, receiver_eq: $userId2 },
          { sender_eq: $userId2, receiver_eq: $userId1 }
        ]
      }
      orderBy: timestamp_ASC
    ) {
      id
      sender
      receiver
      message
      timestamp
    }
  }
`;

/* Mutation for sending a chat message */
export const SEND_CHAT_MESSAGE_MUTATION = gql`
  mutation sendChatMessage($sender: String!, $receiver: String!, $message: String!) {
    createChatMessage(data: { sender: $sender, receiver: $receiver, message: $message }) {
      id
      sender
      receiver
      message
      timestamp
    }
  }
`;

Step 3: Integrate GraphQL Queries and Mutations in requests/graphql.ts

Ensure the new queries and mutations are accessible through the Apollo Client.

Code:

import { ApolloClient, HttpLink, InMemoryCache, split, gql } from '@apollo/client';
import { GraphQLWsLink } from '@apollo/client/link/subscriptions';
import { httpEndpoint, wsEndpoint } from './endpoints';
import { createClient } from 'graphql-ws';
import { getMainDefinition } from '@apollo/client/utilities';

// Define GraphQL queries and mutations
export const FETCH_CHAT_MESSAGES = gql`
  query FetchChatMessages($userId1: ID!, $userId2: ID!) {
    fetchChatMessages(userId1: $userId1, userId2: $userId2) {
      id
      senderId
      receiverId
      message
      timestamp
    }
  }
`;

export const SEND_CHAT_MESSAGE = gql`
  mutation SendChatMessage($senderId: ID!, $receiverId: ID!, $message: String!) {
    sendChatMessage(senderId: $senderId, receiverId: $receiverId, message: $message) {
      id
      senderId
      receiverId
      message
      timestamp
    }
  }
`;

const httpLink = new HttpLink({
  uri: httpEndpoint,
});

const wsLink = new GraphQLWsLink(
  createClient({
    url: wsEndpoint,
  })
);

const splitLink = split(
  ({ query }) => {
    const definition = getMainDefinition(query);
    return (
      definition.kind === 'OperationDefinition' &&
      definition.operation === 'subscription'
    );
  },
  wsLink,
  httpLink
);

export const apolloClient = new ApolloClient({
  link: splitLink,
  cache: new InMemoryCache(),
  connectToDevTools: true,
});

Step 4: Integrate Chat Interface in TradingHub.tsx

Ensure the chat interface is accessible to users who have position pairs.

Code:

import { useEffect, useState } from 'react';
import { useAtom } from 'jotai';
import { useAccount } from 'wagmi';
import { TradingHubTab } from './TradingHubTab';
import { TradingHubContentContainer } from './TradingHubContentContainer';
import { AggregatedPositionsCheckbox } from './TradingHubPositions/AggregatedPositionsCheckbox';
import { tradingHubStateAtom } from '@/store/store';
import { TradingHubFooter } from './TradingHubFooter';
import { selectedMarketIdAtom } from '../Market';
import { ChartFeedResponse } from '@/types/chartTypes';
import { useQuery } from '@apollo/client';
import { CHART_FEED_QUERY } from '@/requests/queries';
import { UTCTimestamp } from 'lightweight-charts';
import { ChatContainer } from './Chat/ChatContainer';
import { TradingHubChart } from './TradingHubChart/TradingHubChart';

const tabs = ['chart', 'positions', 'orders', 'history'] as const;
export type TradingHubStateType = (typeof tabs)[number];

export const TradingHub = () => {
  const { address } = useAccount();

  const [tradingHubState] = useAtom(tradingHubStateAtom);
  const [isAggregated, setIsAggregated] = useState<boolean>(true);

  const toggleIsAggregated = () => {
    setIsAggregated(!isAggregated);
  };

  /* Chart logic  */

  const [selectedMarketId] = useAtom(selectedMarketIdAtom);

  const {
    data: chartRes,
    error,
    loading,
  } = useQuery<ChartFeedResponse>(CHART_FEED_QUERY, {
    pollInterval: 5000,
    variables: { marketId: selectedMarketId },
  });

  const [chartData, setChartData] = useState<
    { time: UTCTimestamp; value: number }[]
  >([]);

  useEffect(() => {
    if (chartRes?.positions) {
      const formattedData = chartRes.positions.map((item) => ({
        time: (new Date(item.timestamp).getTime() / 1000) as UTCTimestamp,
        value: Number(item.createPrice),
      }));
      setChartData(formattedData);
    }
  }, [chartRes]);

  /*  */

  return (
    <div className='h-[calc(100vh-166px)] lg:flex-1 lg:h-full border-[#444650] border rounded-[10px] flex flex-col bg-[#191B24]'>
      <div className='flex-grow'>
        <div className='flex items-center justify-between px-2.5 pt-3 pb-2.5'>
          <div className='flex gap-1'>
            {tabs.map((tab, key) => (
              <TradingHubTab key={key} value={tab} />
            ))}
          </div>
          {tradingHubState === 'positions' && (
            <div className='hidden md:block'>
              <AggregatedPositionsCheckbox
                setIsAggregated={toggleIsAggregated}
                isAggregated={isAggregated}
              />
            </div>
          )}
        </div>
        {address && <TradingHubContentContainer isAggregated={isAggregated} />}
        {tradingHubState === 'chart' && (
          <div className='w-full no-scrollbar'>
            <TradingHubChart data={chartData} />
          </div>
        )}
      </div>

      {/* Integrate ChatContainer */}
      {address && tradingHubState === 'positions' && (
        <div className='chat-container'>
          <ChatContainer />
        </div>
      )}

      <TradingHubFooter />
    </div>
  );
};

Step 5: Update ChatMessage.tsx to Support Additional Features

Enhance the ChatMessage component to include timestamps, message status, and user avatars.

Code:

import React from 'react';

export interface ChatMessageProps {
  author: MessageAuthorType;
  value: string;
  timestamp: string; // New prop for timestamp
  status?: MessageStatusType; // New optional prop for message status
  avatarUrl?: string; // New optional prop for user avatar
}

export type MessageAuthorType = 'user' | 'interlocutor';
export type MessageStatusType = 'sent' | 'delivered' | 'read';

export const ChatMessage = ({ author, value, timestamp, status, avatarUrl }: ChatMessageProps) => {
  return (
    <div className={`flex w-full ${author === 'user' ? 'justify-end' : 'justify-start'} mb-[16px]`}>
      {author === 'interlocutor' && avatarUrl && (
        <img src={avatarUrl} alt="avatar" className="w-8 h-8 rounded-full mr-2" />
      )}
      <div className="flex flex-col max-w-[50%]">
        <div
          className={`break-normal px-[16px] py-[8px] 
          ${author === 'user' ? 'rounded-bl-2xl rounded-tl-2xl rounded-tr-2xl bg-[#87DAA4]' : 'rounded-br-2xl rounded-tr-2xl rounded-tl-2xl bg-[#23252E]'}
          `}
        >
          <p className={`break-words text-xs ${author === 'user' ? 'text-black' : 'text-tetriary'}`}>
            {value}
          </p>
        </div>
        <div className="flex justify-between items-center mt-1 text-xs text-gray-500">
          <span>{timestamp}</span>
          {status && author === 'user' && (
            <span className="ml-2">
              {status === 'sent' && '✓'}
              {status === 'delivered' && '✓✓'}
              {status === 'read' && '✓✓✓'}
            </span>
          )}
        </div>
      </div>
      {author === 'user' && avatarUrl && (
        <img src={avatarUrl} alt="avatar" className="w-8 h-8 rounded-full ml-2" />
      )}
    </div>
  );
};

Step 6: Update ChatContainer.tsx to Handle Peer-to-Peer Chat

Fetch and display chat messages between users with position pairs and allow users to send new messages.

Code:

import React, { useEffect, useRef, useState } from 'react';
import { IoSend } from 'react-icons/io5';
import { ChatMessage, ChatMessageProps } from './ChatMessage';
import { useAccount } from 'wagmi';
import { truncateAddress } from '@/utils/truncateAddress';
import { FaSearch } from 'react-icons/fa';
import { useAtom } from 'jotai';
import { chosenInterlocutorAtom } from '@/store/store';
import { useQuery, useMutation } from '@apollo/client';
import { FETCH_MESSAGES, SEND_MESSAGE } from '@/requests/queries';

export const ChatContainer = () => {
  const [messages, setMessages] = useState<ChatMessageProps[]>([]);
  const [inputVal, setInputVal] = useState<string>('');
  const [chosenInterlocutor, setChosenInterlocutor] = useAtom(chosenInterlocutorAtom);
  const messagesEndRef = useRef<HTMLDivElement>(null);

  const { address } = useAccount();

  // Fetch messages
  const { data, loading, error, refetch } = useQuery(FETCH_MESSAGES, {
    variables: { user1: address, user2: chosenInterlocutor },
    skip: !chosenInterlocutor,
  });

  useEffect(() => {
    if (data && data.messages) {
      setMessages(data.messages);
    }
  }, [data]);

  // Send message mutation
  const [sendMessage] = useMutation(SEND_MESSAGE);

  const handleSendMessage = async () => {
    if (inputVal.trim() !== '') {
      const message: ChatMessageProps = { author: 'user', value: inputVal };
      const newArr = [...messages, message];
      setMessages(newArr);
      setInputVal('');

      try {
        await sendMessage({
          variables: {
            from: address,
            to: chosenInterlocutor,
            message: inputVal,
          },
        });
        refetch();
      } catch (error) {
        console.error('Error sending message:', error);
      }
    }
  };

  const handleKeyPress = (event: React.KeyboardEvent<HTMLInputElement>) => {
    if (event.key === 'Enter') {
      handleSendMessage();
    }
  };

  return (
    <div className='w-full h-full flex pt-4' style={{ height: 'calc(100vh - 229px)' }}>
      <div className='w-[200px] h-full border-r border-[#444650] border-t'>
        <div className='w-full px-2 my-4'>
          <div className='w-full bg-[#23252E] h-[32px] rounded-[100px] flex justify-between items-center mb-3'>
            <input
              placeholder='Search'
              type='text'
              className='w-[85%] outline-none bg-[#23252E] h-full rounded-[100px] pl-3 text-xs text-tetriary'
            />
            <div className='pr-3 text-tetriary text-sm'>
              <FaSearch />
            </div>
          </div>
        </div>
        <div className='mb-3'>
          <p className='text-[10px] text-tetriary px-2'>All messages</p>
        </div>
        {/* Add logic to list all chat interlocutors */}
      </div>
      <div className='flex-grow h-full border-t border-[#444650] flex flex-col justify-between'>
        <div className='flex flex-col justify-between h-full'>
          <div className='h-[56px] border-b border-[#444650] flex items-center justify-between px-3'>
            <div className='flex items-center gap-1.5'>
              <div className='h-4 w-4 rounded-full bg-white'></div>
              <p className='text-tetriary text-sm'>
                {truncateAddress(chosenInterlocutor)}
              </p>
            </div>
            <div>
              <p className='text-[10px] text-tetriary'>
                PnL with Opponent:{' '}
                <span className='font-bold text-[#87DAA4]'>
                  +400.50 $DOLLARS
                </span>
              </p>
            </div>
          </div>
          <div className='flex-1 overflow-auto pb-[16px] no-scrollbar'>
            <div className='flex flex-col p-3 first:mt-2'>
              {messages.map((message, key) => (
                <ChatMessage
                  author={message.author}
                  key={key}
                  value={message.value}
                />
              ))}
            </div>
          </div>
          <div className='w-full px-3'>
            <div className='w-full bg-[#23252E] h-[42px] rounded-[100px] flex justify-between items-center mb-3'>
              <input
                onChange={(e) => setInputVal(e.target.value)}
                type='text'
                value={inputVal}
                onKeyPress={handleKeyPress}
                placeholder='Your message here'
                className='w-[85%] outline-none bg-[#23252E] h-full rounded-[100px] pl-3 text-xs text-tetriary'
              />
              <button
                className='pr-3 text-tetriary'
                onClick={handleSendMessage}
              >
                <IoSend />
              </button>
            </div>
          </div>
        </div>
      </div>
    </div>
  );
};

Click here to create a Pull Request with the proposed solution

Files used for this task:

Changes on store/store.ts To implement the peer-to-peer chat functionality, we need to update the state management in the `store/store.ts` file to handle chat interactions. Here's a concrete plan to achieve this: ### Plan for `store/store.ts` 1. **Add State for Chat Messages**: Introduce an atom to store chat messages. 2. **Manage Current Chat Interlocutor**: Ensure that the current chat interlocutor can be dynamically set. 3. **Add State for Chat Loading and Error Handling**: Introduce atoms to manage loading states and error handling for chat operations. ### Implementation Steps 1. **Add Atom for Chat Messages**: - Create an atom to store an array of chat messages. 2. **Update Interlocutor Management**: - Ensure the `chosenInterlocutorAtom` can be dynamically updated. 3. **Add Atoms for Loading and Error States**: - Create atoms to manage loading states and error handling for chat operations. ### Updated Code Here's the updated code for `store/store.ts`: ```typescript import { TradingHubStateType } from '@/components/Market/TradingHub/TradingHub'; import { EnrichedMarketType } from '@/types/marketTypes'; import { atom } from 'jotai'; /* MARKETS ATOM */ export const marketsAtom = atom([]); /* CHOSEN MARKET ATOM */ export const chosenMarketAtom = atom(undefined); /* TRADING HUB */ export const tradingHubStateAtom = atom('chart'); export const tradingHubPositionsCountAtom = atom(0); export const tradingHubOrdersCountAtom = atom(0); /* CHAT */ /* Atom to store chat messages */ export const chatMessagesAtom = atom<{ [key: string]: string[] }>({}); /* Atom to manage the current chat interlocutor */ export const chosenInterlocutorAtom = atom(null); /* Atom to manage loading state for chat operations */ export const chatLoadingAtom = atom(false); /* Atom to manage error state for chat operations */ export const chatErrorAtom = atom(null); ``` ### Explanation 1. **`chatMessagesAtom`**: This atom stores chat messages in an object where the keys are user IDs and the values are arrays of messages. 2. **`chosenInterlocutorAtom`**: This atom manages the current chat interlocutor, allowing it to be dynamically set to `null` when no interlocutor is chosen. 3. **`chatLoadingAtom`**: This atom manages the loading state for chat operations, useful for showing loading indicators. 4. **`chatErrorAtom`**: This atom manages error states for chat operations, useful for displaying error messages to the user. This setup will allow the application to manage chat interactions effectively, including storing and retrieving messages, handling loading states, and managing errors.
Changes on requests/queries.ts To implement the peer-to-peer chat functionality, we need to define new GraphQL queries and mutations in the `requests/queries.ts` file. These will handle fetching and sending chat messages between users. Here is a concrete plan and the necessary code changes: ### Plan 1. **Define a query to fetch chat messages**: This query will retrieve chat messages between two users based on their user IDs. 2. **Define a mutation to send a chat message**: This mutation will allow a user to send a message to another user. ### Code Changes Add the following queries and mutations to the `requests/queries.ts` file: ```typescript import { gql } from '@apollo/client'; /* Existing queries and mutations */ /* Query for fetching chat messages between two users */ export const GET_CHAT_MESSAGES_QUERY = gql` query getChatMessages($userId1: String!, $userId2: String!) { chatMessages( where: { OR: [ { sender_eq: $userId1, receiver_eq: $userId2 }, { sender_eq: $userId2, receiver_eq: $userId1 } ] } orderBy: timestamp_ASC ) { id sender receiver message timestamp } } `; /* Mutation for sending a chat message */ export const SEND_CHAT_MESSAGE_MUTATION = gql` mutation sendChatMessage($sender: String!, $receiver: String!, $message: String!) { createChatMessage(data: { sender: $sender, receiver: $receiver, message: $message }) { id sender receiver message timestamp } } `; ``` ### Explanation - **GET_CHAT_MESSAGES_QUERY**: This query fetches chat messages between two users, identified by `userId1` and `userId2`. It retrieves messages where either user is the sender and the other is the receiver, ordered by timestamp in ascending order. - **SEND_CHAT_MESSAGE_MUTATION**: This mutation allows a user to send a chat message to another user. It takes the sender's ID, receiver's ID, and the message content as input and creates a new chat message. These additions will enable the application to fetch and send chat messages, facilitating peer-to-peer communication between users with position pairs.
Changes on requests/graphql.ts To implement the peer-to-peer chat functionality, we need to add GraphQL queries and mutations for fetching and sending chat messages between users. Here's a concrete plan for updating the `requests/graphql.ts` file: ### Plan 1. **Define GraphQL Queries and Mutations**: Add the necessary GraphQL queries and mutations for fetching chat messages and sending new messages. 2. **Integrate Queries and Mutations**: Ensure these queries and mutations are integrated into the Apollo Client setup. ### Implementation Steps 1. **Add GraphQL Queries and Mutations**: - Define a query to fetch chat messages between two users. - Define a mutation to send a new chat message. 2. **Update Apollo Client Setup**: - Ensure the new queries and mutations are accessible through the Apollo Client. ### Updated Code Here's how you can update the `requests/graphql.ts` file: ```typescript import { ApolloClient, HttpLink, InMemoryCache, split, gql } from '@apollo/client'; import { GraphQLWsLink } from '@apollo/client/link/subscriptions'; import { httpEndpoint, wsEndpoint } from './endpoints'; import { createClient } from 'graphql-ws'; import { getMainDefinition } from '@apollo/client/utilities'; // Define GraphQL queries and mutations export const FETCH_CHAT_MESSAGES = gql` query FetchChatMessages($userId1: ID!, $userId2: ID!) { fetchChatMessages(userId1: $userId1, userId2: $userId2) { id senderId receiverId message timestamp } } `; export const SEND_CHAT_MESSAGE = gql` mutation SendChatMessage($senderId: ID!, $receiverId: ID!, $message: String!) { sendChatMessage(senderId: $senderId, receiverId: $receiverId, message: $message) { id senderId receiverId message timestamp } } `; const httpLink = new HttpLink({ uri: httpEndpoint, }); const wsLink = new GraphQLWsLink( createClient({ url: wsEndpoint, }) ); const splitLink = split( ({ query }) => { const definition = getMainDefinition(query); return ( definition.kind === 'OperationDefinition' && definition.operation === 'subscription' ); }, wsLink, httpLink ); export const apolloClient = new ApolloClient({ link: splitLink, cache: new InMemoryCache(), connectToDevTools: true, }); ``` ### Explanation - **GraphQL Queries and Mutations**: - `FETCH_CHAT_MESSAGES`: This query fetches chat messages between two users identified by `userId1` and `userId2`. - `SEND_CHAT_MESSAGE`: This mutation sends a new chat message from `senderId` to `receiverId` with the content `message`. - **Apollo Client Setup**: - The `apolloClient` is configured to use both HTTP and WebSocket links, enabling real-time updates if needed. This setup will allow you to fetch and send chat messages between users, enabling the peer-to-peer chat functionality.
Changes on components/Market/TradingHub/TradingHub.tsx To integrate the peer-to-peer chat feature into the `TradingHub.tsx` file, we need to ensure that the chat interface is accessible to users who have position pairs. Here’s a step-by-step plan to achieve this: 1. **Import Necessary Components and Hooks**: - Import the `ChatContainer` component which will handle the chat interface. - Import any additional state management hooks or context if needed. 2. **Conditional Rendering of Chat Interface**: - Add logic to conditionally render the `ChatContainer` based on whether the user has position pairs. 3. **Update the JSX Structure**: - Ensure that the chat interface is integrated seamlessly within the existing layout of the trading hub. Here’s the updated code with the necessary changes: ```typescript import { useEffect, useState } from 'react'; import { useAtom } from 'jotai'; import { useAccount } from 'wagmi'; import { TradingHubTab } from './TradingHubTab'; import { TradingHubContentContainer } from './TradingHubContentContainer'; import { AggregatedPositionsCheckbox } from './TradingHubPositions/AggregatedPositionsCheckbox'; import { tradingHubStateAtom } from '@/store/store'; import { TradingHubFooter } from './TradingHubFooter'; import { selectedMarketIdAtom } from '../Market'; import { ChartFeedResponse } from '@/types/chartTypes'; import { useQuery } from '@apollo/client'; import { CHART_FEED_QUERY } from '@/requests/queries'; import { UTCTimestamp } from 'lightweight-charts'; import { ChatContainer } from './Chat/ChatContainer'; import { TradingHubChart } from './TradingHubChart/TradingHubChart'; const tabs = ['chart', 'positions', 'orders', 'history'] as const; export type TradingHubStateType = (typeof tabs)[number]; export const TradingHub = () => { const { address } = useAccount(); const [tradingHubState] = useAtom(tradingHubStateAtom); const [isAggregated, setIsAggregated] = useState(true); const toggleIsAggregated = () => { setIsAggregated(!isAggregated); }; /* Chart logic */ const [selectedMarketId] = useAtom(selectedMarketIdAtom); const { data: chartRes, error, loading, } = useQuery(CHART_FEED_QUERY, { pollInterval: 5000, variables: { marketId: selectedMarketId }, }); const [chartData, setChartData] = useState< { time: UTCTimestamp; value: number }[] >([]); useEffect(() => { if (chartRes?.positions) { const formattedData = chartRes.positions.map((item) => ({ time: (new Date(item.timestamp).getTime() / 1000) as UTCTimestamp, value: Number(item.createPrice), })); setChartData(formattedData); } }, [chartRes]); /* */ return (
{tabs.map((tab, key) => ( ))}
{tradingHubState === 'positions' && (
)}
{address && } {tradingHubState === 'chart' && (
)}
{/* Integrate ChatContainer */} {address && tradingHubState === 'positions' && (
)}
); }; ``` ### Explanation: 1. **Import `ChatContainer`**: The `ChatContainer` component is imported to be used within the `TradingHub` component. 2. **Conditional Rendering**: The `ChatContainer` is conditionally rendered based on the user's address and the current state of the trading hub (`tradingHubState`). 3. **Integration**: The chat interface is integrated within the existing layout, ensuring it appears when the user is viewing their positions. This approach ensures that the chat feature is seamlessly integrated into the trading hub interface and is accessible to users with position pairs.
Changes on components/Market/TradingHub/Chat/ChatMessage.tsx To update the `ChatMessage.tsx` file to support different styles and functionalities for peer-to-peer chat messages, we can enhance the component to include additional features such as timestamps, message status (e.g., sent, delivered, read), and possibly user avatars. Here is a concrete plan and the updated code: ### Plan 1. **Add Timestamps**: Display the time when the message was sent. 2. **Message Status**: Show the status of the message (e.g., sent, delivered, read). 3. **User Avatars**: Display avatars for the users to make the chat more visually appealing. 4. **Styling Enhancements**: Improve the styling to accommodate the new features. ### Updated Code ```tsx import React from 'react'; export interface ChatMessageProps { author: MessageAuthorType; value: string; timestamp: string; // New prop for timestamp status?: MessageStatusType; // New optional prop for message status avatarUrl?: string; // New optional prop for user avatar } export type MessageAuthorType = 'user' | 'interlocutor'; export type MessageStatusType = 'sent' | 'delivered' | 'read'; export const ChatMessage = ({ author, value, timestamp, status, avatarUrl }: ChatMessageProps) => { return (
{author === 'interlocutor' && avatarUrl && ( avatar )}

{value}

{timestamp} {status && author === 'user' && ( {status === 'sent' && '✓'} {status === 'delivered' && '✓✓'} {status === 'read' && '✓✓✓'} )}
{author === 'user' && avatarUrl && ( avatar )}
); }; ``` ### Explanation 1. **Timestamps**: Added a `timestamp` prop to display when the message was sent. 2. **Message Status**: Added a `status` prop to show the message status (sent, delivered, read) with corresponding symbols. 3. **User Avatars**: Added an `avatarUrl` prop to display user avatars next to their messages. 4. **Styling Enhancements**: Adjusted the styling to accommodate the new features, ensuring the layout remains clean and readable. This update will make the chat messages more informative and visually appealing, enhancing the overall user experience for peer-to-peer chat functionality.
Changes on components/Market/TradingHub/Chat/ChatContainer.tsx To update the `ChatContainer.tsx` file to handle peer-to-peer chat functionality, including fetching and displaying chat messages between users with position pairs, follow these steps: ### Steps to Implement Peer-to-Peer Chat Functionality 1. **Import Necessary Modules and Hooks**: - Import the necessary GraphQL queries and mutations. - Import any additional state management hooks if needed. 2. **Fetch Chat Messages**: - Use a GraphQL query to fetch chat messages between the current user and the chosen interlocutor. - Update the `useEffect` hook to fetch messages whenever the `chosenInterlocutor` changes. 3. **Send Chat Messages**: - Use a GraphQL mutation to send messages. - Update the `handleSendMessage` function to call this mutation. 4. **Update State Management**: - Ensure that the state is updated correctly when new messages are fetched or sent. ### Updated Code Here's the updated code for `ChatContainer.tsx`: ```typescript import React, { useEffect, useRef, useState } from 'react'; import { IoSend } from 'react-icons/io5'; import { ChatMessage, ChatMessageProps } from './ChatMessage'; import { useAccount } from 'wagmi'; import { truncateAddress } from '@/utils/truncateAddress'; import { FaSearch } from 'react-icons/fa'; import { useAtom } from 'jotai'; import { chosenInterlocutorAtom } from '@/store/store'; import { useQuery, useMutation } from '@apollo/client'; import { FETCH_MESSAGES, SEND_MESSAGE } from '@/requests/queries'; export const ChatContainer = () => { const [messages, setMessages] = useState([]); const [inputVal, setInputVal] = useState(''); const [chosenInterlocutor, setChosenInterlocutor] = useAtom(chosenInterlocutorAtom); const messagesEndRef = useRef(null); const { address } = useAccount(); // Fetch messages const { data, loading, error, refetch } = useQuery(FETCH_MESSAGES, { variables: { user1: address, user2: chosenInterlocutor }, skip: !chosenInterlocutor, }); useEffect(() => { if (data && data.messages) { setMessages(data.messages); } }, [data]); // Send message mutation const [sendMessage] = useMutation(SEND_MESSAGE); const handleSendMessage = async () => { if (inputVal.trim() !== '') { const message: ChatMessageProps = { author: 'user', value: inputVal }; const newArr = [...messages, message]; setMessages(newArr); setInputVal(''); try { await sendMessage({ variables: { from: address, to: chosenInterlocutor, message: inputVal, }, }); refetch(); } catch (error) { console.error('Error sending message:', error); } } }; const handleKeyPress = (event: React.KeyboardEvent) => { if (event.key === 'Enter') { handleSendMessage(); } }; return (

All messages

{/* Add logic to list all chat interlocutors */}

{truncateAddress(chosenInterlocutor)}

PnL with Opponent:{' '} +400.50 $DOLLARS

{messages.map((message, key) => ( ))}
setInputVal(e.target.value)} type='text' value={inputVal} onKeyPress={handleKeyPress} placeholder='Your message here' className='w-[85%] outline-none bg-[#23252E] h-full rounded-[100px] pl-3 text-xs text-tetriary' />
); }; ``` ### Explanation 1. **Fetching Messages**: - Added a GraphQL query `FETCH_MESSAGES` to fetch messages between the current user and the chosen interlocutor. - Used the `useQuery` hook to fetch messages and update the state when the data changes. 2. **Sending Messages**: - Added a GraphQL mutation `SEND_MESSAGE` to send messages. - Updated the `handleSendMessage` function to call this mutation and refetch messages after sending. 3. **State Management**: - Updated the state to handle fetched messages and new messages. This implementation ensures that the chat container can fetch and display messages between users with position pairs and allows users to send new messages.