sparkletown/sparkle

View on GitHub
src/components/molecules/Chatbox/Chatbox.tsx

Summary

Maintainability
A
0 mins
Test Coverage
import React, { useCallback, useState } from "react";
import InfiniteScroll from "react-infinite-scroll-component";
import classNames from "classnames";
import { isEqual } from "lodash";

import {
  ChatOptionType,
  InfiniteScrollProps,
  MessageToDisplay,
  SendChatMessage,
  SendThreadMessageProps,
} from "types/chat";
import { ContainerClassName } from "types/utility";

import { WithId } from "utils/id";

import { useVenuePoll } from "hooks/useVenuePoll";

import { ChatboxMessage } from "components/molecules/Chatbox/components/ChatboxMessage";
import {
  useChatboxSendThreadMessage,
  useClearSelectedReplyThread,
  useHasSelectedReplyThread,
} from "components/molecules/Chatbox/components/context/ChatboxContext";
import { ChatMessageBox } from "components/molecules/ChatMessageBox";
import { Loading } from "components/molecules/Loading";
import { PollBox } from "components/molecules/PollBox";

import { ChatboxOptionsControls } from "./components/ChatboxOptionsControls";
import { ChatboxThreadControls } from "./components/ChatboxThreadControls";
import { useTriggerScrollFix } from "./useTriggerScrollFix";

import "./Chatbox.scss";

export interface ChatboxProps extends ContainerClassName, InfiniteScrollProps {
  messages: WithId<MessageToDisplay>[];
  displayPollOption?: boolean;
}

const _ChatBox: React.FC<ChatboxProps> = ({
  messages,
  displayPollOption,
  containerClassName,
  hasMore,
  loadMore,
}) => {
  const scrollableComponentRef = useTriggerScrollFix(messages);

  const { createPoll, voteInPoll } = useVenuePoll();

  const closeThread = useClearSelectedReplyThread();

  const [activeOption, setActiveOption] = useState<ChatOptionType>();

  const unselectOption = useCallback(() => {
    setActiveOption(undefined);
    closeThread();
  }, [closeThread]);

  const onPollSubmit = useCallback(
    async (data) => {
      await createPoll(data);
      unselectOption();
    },
    [createPoll, unselectOption]
  );

  const isQuestionOptions = ChatOptionType.question === activeOption;

  const sendThreadMessage = useChatboxSendThreadMessage();

  const sendThreadMessageWrapper: SendChatMessage<SendThreadMessageProps> = useCallback(
    async ({ text, threadId }) => {
      await sendThreadMessage({ text, threadId });
      unselectOption();
    },
    [unselectOption, sendThreadMessage]
  );

  const hasSelectedThread = useHasSelectedReplyThread();

  return (
    <div className={classNames("Chatbox", containerClassName)}>
      <div
        className="Chatbox__messages"
        ref={scrollableComponentRef}
        id={"Chatbox_scrollable_div"}
      >
        <InfiniteScroll
          dataLength={messages.length}
          className={"Chatbox__messages-infinite-scroll"}
          next={loadMore}
          inverse
          hasMore={hasMore}
          scrollableTarget="Chatbox_scrollable_div"
          loader={
            <Loading containerClassName="Chatbox__messages-infinite-scroll-loading" />
          }
        >
          {messages.map((message, i) => (
            <ChatboxMessage
              key={message.id}
              message={message}
              nextMessage={messages?.[i + 1]}
              voteInPoll={voteInPoll}
            />
          ))}
        </InfiniteScroll>
      </div>
      <div className="Chatbox__form-box">
        {/* @debt sort these out. Preferably using some kind of enum */}
        {hasSelectedThread && (
          <ChatboxThreadControls
            text="Replying to"
            closeThread={unselectOption}
          />
        )}
        {isQuestionOptions && !hasSelectedThread && (
          <ChatboxThreadControls
            text="asking a question"
            closeThread={unselectOption}
          />
        )}
        {displayPollOption && !isQuestionOptions && !hasSelectedThread && (
          <ChatboxOptionsControls
            activeOption={activeOption}
            setActiveOption={setActiveOption}
          />
        )}
        {activeOption === ChatOptionType.poll ? (
          <PollBox onPollSubmit={onPollSubmit} />
        ) : (
          <ChatMessageBox
            sendThreadMessageWrapper={sendThreadMessageWrapper}
            unselectOption={unselectOption}
            isQuestion={isQuestionOptions}
          />
        )}
      </div>
    </div>
  );
};

export const Chatbox = React.memo(_ChatBox, isEqual);