/*
  eslint-disable
  consistent-return, no-unused-vars, react/destructuring-assignment,
  react/no-access-state-in-setstate, react/button-has-type
*/

import { h, Component } from 'preact';
import PropTypes from 'prop-types';
import { setupPusher } from '../utilities/connect';
import notifyUser from '../utilities/connect/newMessageNotify';
import debounceAction from '../utilities/debounceAction';
import { addSnackbarItem } from '../Snackbar';
import { processImageUpload } from '../article-form/actions';
import {
  conductModeration,
  getAllMessages,
  sendMessage,
  sendOpen,
  getChannels,
  getUnopenedChannelIds,
  getContent,
  deleteMessage,
  editMessage,
} from './actions/actions';
import CreateChatModal from './components/CreateChatModal';
import {
  sendChannelRequest,
  rejectJoiningRequest,
  acceptJoiningRequest,
  getChannelRequestInfo,
} from './actions/requestActions';
import {
  hideMessages,
  scrollToBottom,
  setupObserver,
  getCurrentUser,
  channelSorter,
} from './util';
import Alert from './alert';
import Channels from './channels';
import Compose from './compose';
import Message from './message';
import ActionMessage from './actionMessage';
import Content from './content';
import { VideoContent } from './videoContent';
import { DragAndDropZone } from '@utilities/dragAndDrop';
import { dragAndUpload } from '@utilities/dragAndUpload';
import { Button } from '@crayons';

const NARROW_WIDTH_LIMIT = 767;
const WIDE_WIDTH_LIMIT = 1600;

export default class Chat extends Component {
  static propTypes = {
    pusherKey: PropTypes.number.isRequired,
    chatChannels: PropTypes.string.isRequired,
    chatOptions: PropTypes.string.isRequired,
    githubToken: PropTypes.string.isRequired,
    tagModerator: PropTypes.shape({ isTagModerator: PropTypes.bool.isRequired })
      .isRequired,
  };

  constructor(props) {
    super(props);
    const chatChannels = JSON.parse(props.chatChannels);
    const chatOptions = JSON.parse(props.chatOptions);

    this.debouncedChannelFilter = debounceAction(
      this.triggerChannelFilter.bind(this),
    );

    this.state = {
      appDomain: document.body.dataset.appDomain,
      messages: [],
      scrolled: false,
      showAlert: false,
      chatChannels,
      unopenedChannelIds: [],
      filterQuery: '',
      channelTypeFilter: 'all',
      channelsLoaded: false,
      channelPaginationNum: 0,
      fetchingPaginatedChannels: false,
      activeChannelId: chatOptions.activeChannelId,
      activeChannel: null,
      showChannelsList: chatOptions.showChannelsList,
      showTimestamp: chatOptions.showTimestamp,
      currentUserId: chatOptions.currentUserId,
      notificationsPermission: null,
      activeContent: {},
      fullscreenContent: null,
      videoPath: null,
      expanded: window.innerWidth > NARROW_WIDTH_LIMIT,
      isMobileDevice: typeof window.orientation !== 'undefined',
      subscribedPusherChannels: [],
      messageOffset: 0,
      showDeleteModal: false,
      messageDeleteId: null,
      allMessagesLoaded: false,
      currentMessageLocation: 0,
      startEditing: false,
      activeEditMessage: {},
      markdownEdited: false,
      searchShowing: false,
      channelUsers: [],
      showMemberlist: false,
      memberFilterQuery: null,
      rerenderIfUnchangedCheck: null,
      userRequestCount: 0,
      openModal: false,
      isTagModerator: JSON.parse(props.tagModerator).isTagModerator,
    };
    if (chatOptions.activeChannelId) {
      getAllMessages(chatOptions.activeChannelId, 0, this.receiveAllMessages);
    }
  }

  componentDidMount() {
    const {
      chatChannels,
      activeChannelId,
      showChannelsList,
      channelTypeFilter,
      isMobileDevice,
      channelPaginationNum,
      currentUserId,
      appDomain,
      messageOffset,
    } = this.state;

    this.setupChannels(chatChannels);

    const channelsForPusherSub = chatChannels.filter(
      this.channelTypeFilterFn('open'),
    );
    this.subscribeChannelsToPusher(
      channelsForPusherSub,
      (channel) => `open-channel--${appDomain}-${channel.chat_channel_id}`,
    );

    setupObserver(this.observerCallback);

    this.subscribePusher(
      `private-message-notifications--${appDomain}-${currentUserId}`,
    );

    if (activeChannelId) {
      sendOpen(activeChannelId, this.handleChannelOpenSuccess, null);
    }
    if (showChannelsList) {
      const filters =
        channelTypeFilter === 'all'
          ? {}
          : { filters: `channel_type:${channelTypeFilter}` };
      const searchParams = {
        query: '',
        retrievalID: activeChannelId,
        searchType: '',
        paginationNumber: channelPaginationNum,
      };
      if (activeChannelId !== null) {
        searchParams.searchType = 'discoverable';
      }
      getChannels(searchParams, filters, this.loadChannels);
      getUnopenedChannelIds(this.markUnopenedChannelIds);
    }
    if (!isMobileDevice) {
      document.getElementById('messageform').focus();
    }
    if (document.getElementById('chatchannels__channelslist')) {
      document
        .getElementById('chatchannels__channelslist')
        .addEventListener('scroll', this.handleChannelScroll);
    }

    this.handleRequestCount();
  }

  shouldComponentUpdate(nextProps, nextState) {
    if (
      this.state.rerenderIfUnchangedCheck !== nextState.rerenderIfUnchangedCheck
    ) {
      return false;
    }
  }

  componentDidUpdate() {
    const { scrolled, currentMessageLocation } = this.state;
    const messageList = document.getElementById('messagelist');
    if (messageList) {
      if (!scrolled) {
        scrollToBottom();
      }
    }

    if (currentMessageLocation && messageList.scrollTop === 0) {
      messageList.scrollTop =
        messageList.scrollHeight - (currentMessageLocation + 30);
    }
  }

  handleRequestCount = () => {
    getChannelRequestInfo().then((response) => {
      const { result } = response;
      const { user_joining_requests, channel_joining_memberships } = result;
      let totalRequest =
        user_joining_requests?.length + channel_joining_memberships?.length;
      this.setState({
        userRequestCount: totalRequest,
      });
    });
  };

  filterForActiveChannel = (channels, id, currentUserId) =>
    channels.filter(
      (channel) =>
        channel.chat_channel_id === parseInt(id, 10) &&
        channel.viewable_by === parseInt(currentUserId, 10),
    )[0];

  subscribePusher = (channelName) => {
    const { subscribedPusherChannels } = this.state;
    const { pusherKey } = this.props;
    if (!subscribedPusherChannels.includes(channelName)) {
      setupPusher(pusherKey, {
        channelId: channelName,
        messageCreated: this.receiveNewMessage,
        messageDeleted: this.removeMessage,
        messageEdited: this.updateMessage,
        channelCleared: this.clearChannel,
        redactUserMessages: this.redactUserMessages,
        channelError: this.channelError,
        mentioned: this.mentioned,
        messageOpened: this.messageOpened,
      });
      const subscriptions = subscribedPusherChannels;
      subscriptions.push(channelName);
      this.setState({ subscribedPusherChannels: subscriptions });
    }
  };

  mentioned = () => {};

  messageOpened = () => {};

  loadChannels = (channels, query) => {
    const { activeChannelId, appDomain } = this.state;
    const activeChannel =
      this.state.activeChannel ||
      channels.filter(
        (channel) => channel.chat_channel_id === activeChannelId,
      )[0];
    if (activeChannelId && query.length === 0) {
      this.setState({
        chatChannels: channels,
        scrolled: false,
        channelsLoaded: true,
        channelPaginationNum: 0,
        filterQuery: '',
        activeChannel:
          activeChannel ||
          this.filterForActiveChannel(channels, activeChannelId),
      });
      this.setupChannel(activeChannelId, activeChannel);
    } else if (activeChannelId) {
      this.setState({
        scrolled: false,
        chatChannels: channels,
        channelsLoaded: true,
        channelPaginationNum: 0,
        filterQuery: query,
        activeChannel:
          activeChannel ||
          this.filterForActiveChannel(channels, activeChannelId),
      });
      this.setupChannel(activeChannelId, activeChannel);
    } else if (channels.length > 0) {
      this.setState({
        chatChannels: channels,
        channelsLoaded: true,
        channelPaginationNum: 0,
        filterQuery: query || '',
        scrolled: false,
      });

      this.triggerSwitchChannel(
        channels[0].chat_channel_id,
        channels[0].channel_modified_slug,
        channels,
      );
      this.setupChannels(channels);
    } else {
      this.setState({ channelsLoaded: true });
    }
    this.subscribeChannelsToPusher(
      channels.filter(this.channelTypeFilterFn('open')),
      (channel) => `open-channel--${appDomain}-${channel.chat_channel_id}`,
    );
    this.subscribeChannelsToPusher(
      channels.filter(this.channelTypeFilterFn('invite_only')),
      (channel) => `private-channel--${appDomain}-${channel.chat_channel_id}`,
    );
    const chatChannelsList = document.getElementById(
      'chatchannels__channelslist',
    );

    if (chatChannelsList) {
      chatChannelsList.scrollTop = 0;
    }
  };

  markUnopenedChannelIds = (ids) => {
    this.setState({ unopenedChannelIds: ids });
  };

  subscribeChannelsToPusher = (channels, channelNameFn) => {
    channels.forEach((channel) => {
      this.subscribePusher(channelNameFn(channel));
    });
  };

  channelTypeFilterFn = (type) => (channel) => {
    return channel.channel_type === type;
  };

  setupChannels = (channels) => {
    const { activeChannel } = this.state;
    channels.forEach((channel, index) => {
      if (index < 3) {
        this.setupChannel(channel.chat_channel_id, activeChannel);
      }
    });
  };

  loadPaginatedChannels = (channels) => {
    const { state } = this;
    const currentChannels = state.chatChannels;
    const currentChannelIds = currentChannels.map((channel) => channel.id);
    const newChannels = currentChannels;
    channels.forEach((channel) => {
      if (!currentChannelIds.includes(channel.id)) {
        newChannels.push(channel);
      }
    });
    if (
      currentChannels.length === newChannels.length &&
      state.channelPaginationNum > 3
    ) {
      return;
    }
    this.setState({
      chatChannels: newChannels,
      fetchingPaginatedChannels: false,
      channelPaginationNum: state.channelPaginationNum + 1,
    });
  };

  setupChannel = (channelId, activeChannel) => {
    const { messages, messageOffset, appDomain } = this.state;
    if (
      !messages[channelId] ||
      messages[channelId].length === 0 ||
      messages[channelId][0].reception_method === 'pushed'
    ) {
      getAllMessages(channelId, messageOffset, this.receiveAllMessages);
    }
    if (
      activeChannel &&
      activeChannel.channel_type !== 'direct' &&
      activeChannel.chat_channel_id === channelId
    ) {
      getContent(
        `/chat_channels/${channelId}/channel_info`,
        this.setOpenChannelUsers,
        null,
      );
      if (activeChannel.channel_type === 'open')
        this.subscribePusher(`open-channel--${appDomain}-${channelId}`);
    }
    this.subscribePusher(`private-channel--${appDomain}-${channelId}`);
  };

  setOpenChannelUsers = (res) => {
    const { activeChannelId, activeChannel } = this.state;
    Object.filter = (obj, predicate) =>
      Object.fromEntries(Object.entries(obj).filter(predicate));
    const leftUser = Object.filter(
      res.channel_users,
      ([username]) => username !== window.currentUser.username,
    );
    if (activeChannel && activeChannel.channel_type === 'open') {
      this.setState({
        channelUsers: {
          [activeChannelId]: leftUser,
        },
      });
    } else {
      this.setState({
        channelUsers: {
          [activeChannelId]: {
            all: { username: 'all', name: 'To notify everyone here' },
            ...leftUser,
          },
        },
      });
    }
  };

  observerCallback = (entries) => {
    entries.forEach((entry) => {
      if (entry.isIntersecting && this.state.scrolled === true) {
        this.setState({ scrolled: false, showAlert: false });
      } else if (this.state.scrolled === false) {
        this.setState({
          scrolled: true,
          rerenderIfUnchangedCheck: Math.random(),
        });
      }
    });
  };

  channelError = (_error) => {
    this.setState({
      subscribedPusherChannels: [],
    });
  };

  receiveAllMessages = (res) => {
    const { chatChannelId, messages } = res;
    this.setState((prevState) => ({
      messages: { ...prevState.messages, [chatChannelId]: messages },
      scrolled: false,
    }));
  };

  removeMessage = (message) => {
    const { activeChannelId } = this.state;
    this.setState((prevState) => ({
      messages: {
        [activeChannelId]: [
          ...prevState.messages[activeChannelId].filter(
            (oldmessage) => oldmessage.id !== message.id,
          ),
        ],
      },
    }));
  };

  updateMessage = (message) => {
    const { activeChannelId } = this.state;
    if (message.chat_channel_id === activeChannelId) {
      this.setState(({ messages }) => {
        const newMessages = messages;
        const foundIndex = messages[activeChannelId].findIndex(
          (oldMessage) => oldMessage.id === message.id,
        );
        newMessages[activeChannelId][foundIndex] = message;
        return { messages: newMessages };
      });
    }
  };

  receiveNewMessage = (message) => {
    const {
      messages,
      activeChannelId,
      scrolled,
      chatChannels,
      currentUserId,
      unopenedChannelIds,
    } = this.state;

    const receivedChatChannelId = message.chat_channel_id;
    const messageList = document.getElementById('messagelist');
    let newMessages = [];
    const nearBottom =
      messageList.scrollTop + messageList.offsetHeight + 400 >
      messageList.scrollHeight;

    if (nearBottom) {
      scrollToBottom();
    }

    // If I'm not sender and tab is not active
    if (message.user_id !== currentUserId && document.hidden) {
      notifyUser();
    }

    if (
      message.temp_id &&
      messages[receivedChatChannelId] &&
      messages[receivedChatChannelId].findIndex(
        (oldmessage) => oldmessage.temp_id === message.temp_id,
      ) > -1
    ) {
      // Remove reduntant messages
      return;
    }

    if (messages[receivedChatChannelId]) {
      newMessages = messages[receivedChatChannelId].slice();
      newMessages.push(message);
      if (newMessages.length > 150) {
        newMessages.shift();
      }
    }

    // Show alert if message received and you have scrolled up
    const newShowAlert =
      activeChannelId === receivedChatChannelId
        ? { showAlert: !nearBottom }
        : {};

    let newMessageChannelIndex = 0;
    let newMessageChannel = null;
    const newChannelsObj = chatChannels.map((channel, index) => {
      if (receivedChatChannelId === channel.chat_channel_id) {
        newMessageChannelIndex = index;
        newMessageChannel = channel;
        return { ...channel, channel_last_message_at: new Date() };
      }
      return channel;
    });

    if (newMessageChannelIndex > 0) {
      newChannelsObj.splice(newMessageChannelIndex, 1);
      newChannelsObj.unshift(newMessageChannel);
    }

    // Mark messages read
    if (receivedChatChannelId === activeChannelId) {
      sendOpen(receivedChatChannelId, this.handleChannelOpenSuccess, null);
    } else {
      const newUnopenedChannels = unopenedChannelIds;
      if (!unopenedChannelIds.includes(receivedChatChannelId)) {
        newUnopenedChannels.push(receivedChatChannelId);
      }
      this.setState({
        unopenedChannelIds: newUnopenedChannels,
      });
    }

    // Updating the messages
    this.setState((prevState) => ({
      ...newShowAlert,
      chatChannels: newChannelsObj,
      messages: {
        ...prevState.messages,
        [receivedChatChannelId]: newMessages,
      },
    }));
  };

  redactUserMessages = (res) => {
    const { messages } = this.state;
    const newMessages = hideMessages(messages, res.userId);
    this.setState({ messages: newMessages });
  };

  clearChannel = (res) => {
    this.setState((prevState) => ({
      messages: { ...prevState.messages, [res.chat_channel_id]: [] },
    }));
  };

  handleChannelScroll = (e) => {
    const {
      fetchingPaginatedChannels,
      chatChannels,
      channelTypeFilter,
      filterQuery,
      activeChannelId,
      channelPaginationNum,
    } = this.state;

    if (fetchingPaginatedChannels || chatChannels.length < 30) {
      return;
    }
    const { target } = e;
    if (target.scrollTop + target.offsetHeight + 1800 > target.scrollHeight) {
      this.setState({ fetchingPaginatedChannels: true });

      const filters =
        channelTypeFilter === 'all'
          ? {}
          : { filters: `channel_type:${channelTypeFilter}` };
      const searchParams = {
        query: filterQuery,
        retrievalID: activeChannelId,
        searchType: '',
        paginationNumber: channelPaginationNum,
      };
      getChannels(searchParams, filters, this.loadPaginatedChannels);
    }
  };

  handleKeyDown = (e) => {
    const {
      showMemberlist,
      activeContent,
      activeChannelId,
      messages,
      currentUserId,
    } = this.state;
    const enterPressed = e.keyCode === 13;
    const leftPressed = e.keyCode === 37;
    const rightPressed = e.keyCode === 39;
    const escPressed = e.keyCode === 27;
    const targetValue = e.target.value;
    const messageIsEmpty = targetValue.length === 0;
    const shiftPressed = e.shiftKey;
    const upArrowPressed = e.keyCode === 38;
    const deletePressed = e.keyCode === 46;

    if (enterPressed) {
      if (showMemberlist) {
        e.preventDefault();
        const selectedUser = document.getElementsByClassName(
          'active__message__list',
        )[0];
        this.addUserName({ target: selectedUser });
      } else if (messageIsEmpty) {
        e.preventDefault();
      } else if (!messageIsEmpty && !shiftPressed) {
        e.preventDefault();
        this.handleMessageSubmit(e.target.value);
      }
    }
    if (e.target.value.includes('@')) {
      if (e.keyCode === 40 || e.keyCode === 38) {
        e.preventDefault();
      }
    }
    if (
      leftPressed &&
      activeContent[activeChannelId] &&
      e.target.value === '' &&
      document.getElementById('activecontent-iframe')
    ) {
      e.preventDefault();
      try {
        e.target.value = document.getElementById(
          'activecontent-iframe',
        ).contentWindow.location.href;
      } catch (err) {
        e.target.value = activeContent[activeChannelId].path;
      }
    }
    if (
      rightPressed &&
      !activeContent[activeChannelId] &&
      e.target.value === ''
    ) {
      e.preventDefault();
      const richLinks = document.getElementsByClassName(
        'chatchannels__richlink',
      );
      if (richLinks.length === 0) {
        return;
      }
      this.setActiveContentState(activeChannelId, {
        type_of: 'loading-post',
      });
      this.setActiveContent({
        path: richLinks[richLinks.length - 1].href,
        type_of: 'article',
      });
    }
    if (escPressed && activeContent[activeChannelId]) {
      this.setActiveContentState(activeChannelId, null);
      this.setState({
        fullscreenContent: null,
        expanded: window.innerWidth > NARROW_WIDTH_LIMIT,
      });
    }
    if (messageIsEmpty) {
      const messagesByCurrentUser = messages[activeChannelId].filter(
        (message) => message.user_id === currentUserId,
      );
      const lastMessage =
        messagesByCurrentUser[messagesByCurrentUser.length - 1];

      if (lastMessage) {
        if (upArrowPressed) {
          this.triggerEditMessage(lastMessage.id);
        } else if (deletePressed) {
          this.triggerDeleteMessage(lastMessage.id);
        }
      }
    }
  };

  handleKeyDownEdit = (e) => {
    const enterPressed = e.keyCode === 13;
    const targetValue = e.target.value;
    const messageIsEmpty = targetValue.length === 0;
    const shiftPressed = e.shiftKey;

    if (enterPressed) {
      if (messageIsEmpty) {
        e.preventDefault();
      } else if (!messageIsEmpty && !shiftPressed) {
        e.preventDefault();
        this.handleMessageSubmitEdit(e.target.value);
      }
    }
  };

  handleMessageSubmitEdit = (message) => {
    const { activeChannelId, activeEditMessage } = this.state;
    const editedMessage = {
      activeChannelId,
      id: activeEditMessage.id,
      message,
    };
    editMessage(editedMessage, this.handleSuccess, this.handleFailure);
    this.handleEditMessageClose();
  };

  handleMessageSubmit = (message) => {
    const { activeChannelId } = this.state;
    scrollToBottom();
    // should check if user has the privilege
    if (message.startsWith('/code')) {
      this.setActiveContentState(activeChannelId, { type_of: 'code_editor' });
    } else if (message.startsWith('/call')) {
      const messageObject = {
        activeChannelId,
        message: '/call',
        mentionedUsersId: this.getMentionedUsers(message),
      };
      this.setState({ videoPath: `/video_chats/${activeChannelId}` });
      sendMessage(messageObject, this.handleSuccess, this.handleFailure);
    } else if (message.startsWith('/play ')) {
      const messageObject = {
        activeChannelId,
        message,
        mentionedUsersId: this.getMentionedUsers(message),
      };
      sendMessage(messageObject, this.handleSuccess, this.handleFailure);
    } else if (message.startsWith('/new')) {
      this.setActiveContentState(activeChannelId, {
        type_of: 'loading-post',
      });
      this.setActiveContent({
        path: '/new',
        type_of: 'article',
      });
    } else if (message.startsWith('/search')) {
      this.setActiveContentState(activeChannelId, {
        type_of: 'loading-post',
      });
      this.setActiveContent({
        path: `/search?q=${message.replace('/search ', '')}`,
        type_of: 'article',
      });
    } else if (message.startsWith('/s ')) {
      this.setActiveContentState(activeChannelId, {
        type_of: 'loading-post',
      });
      this.setActiveContent({
        path: `/search?q=${message.replace('/s ', '')}`,
        type_of: 'article',
      });
    } else if (message.startsWith('/ban ') || message.startsWith('/unban ')) {
      conductModeration(
        activeChannelId,
        message,
        this.handleSuccess,
        this.handleFailure,
      );
    } else if (message.startsWith('/draw')) {
      this.setActiveContent({
        sendCanvasImage: this.sendCanvasImage,
        type_of: 'draw',
      });
    } else if (message.startsWith('/')) {
      this.setActiveContentState(activeChannelId, {
        type_of: 'loading-post',
      });
      this.setActiveContent({
        path: message,
        type_of: 'article',
      });
    } else if (message.startsWith('/github')) {
      const args = message.split('/github ')[1].trim();
      this.setActiveContentState(activeChannelId, { type_of: 'github', args });
    } else {
      const messageObject = {
        activeChannelId,
        message,
        mentionedUsersId: this.getMentionedUsers(message),
      };
      this.setState({ scrolled: false, showAlert: false });
      sendMessage(messageObject, this.handleSuccess, this.handleFailure);
    }
  };
  hideChannelList = () => {
    const chatContainer = document.getElementsByClassName(
      'chat__activechat',
    )[0];
    chatContainer.classList.remove('chat__activechat--hidden');
  };
  handleSwitchChannel = (e) => {
    e.preventDefault();
    let { target } = e;
    this.hideChannelList();
    if (!target.dataset.channelId) {
      target = target.parentElement;
    }
    this.triggerSwitchChannel(
      parseInt(target.dataset.channelId, 10),
      target.dataset.channelSlug,
    );
  };

  triggerSwitchChannel = (id, slug, channels) => {
    const {
      chatChannels,
      isMobileDevice,
      unopenedChannelIds,
      activeChannelId,
      currentUserId,
    } = this.state;
    const channelList = channels || chatChannels;
    const newUnopenedChannelIds = unopenedChannelIds;
    const index = newUnopenedChannelIds.indexOf(id);
    if (index > -1) {
      newUnopenedChannelIds.splice(index, 1);
    }

    let updatedActiveChannel = this.filterForActiveChannel(
      channelList,
      id,
      currentUserId,
    );

    this.setState({
      activeChannel: updatedActiveChannel,
      activeChannelId: parseInt(id, 10),
      scrolled: false,
      showAlert: false,
      allMessagesLoaded: false,
      showMemberlist: false,
      unopenedChannelIds: unopenedChannelIds.filter(
        (unopenedId) => unopenedId !== id,
      ),
    });

    this.setupChannel(id, updatedActiveChannel);
    const params = new URLSearchParams(window.location.search);

    if (params.get('ref') === 'group_invite') {
      this.setActiveContentState(activeChannelId, {
        type_of: 'loading-post',
      });
      this.setActiveContent({
        path: '/chat_channel_memberships',
        type_of: 'article',
      });
    }
    window.history.replaceState(null, null, `/connect/${slug}`);
    if (!isMobileDevice) {
      document.getElementById('messageform').focus();
    }
    if (window.ga && ga.create) {
      ga('send', 'pageview', window.location.pathname + window.location.search);
    }
    sendOpen(id, this.handleChannelOpenSuccess, null);
  };

  handleSubmitOnClick = (e) => {
    e.preventDefault();
    const message = document.getElementById('messageform').value;
    if (message.length > 0) {
      this.handleMessageSubmit(message);
    }
  };

  handleSubmitOnClickEdit = (e) => {
    e.preventDefault();
    const message = document.getElementById('messageform').value;
    if (message.length > 0) {
      this.handleMessageSubmitEdit(message);
    }
  };

  triggerDeleteMessage = (messageId) => {
    this.setState({ messageDeleteId: messageId });
    this.setState({ showDeleteModal: true });
  };

  triggerEditMessage = (messageId) => {
    const { messages, activeChannelId } = this.state;
    this.setState({
      activeEditMessage: messages[activeChannelId].filter(
        (message) => message.id === messageId,
      )[0],
    });
    this.setState({ startEditing: true });
  };

  handleSuccess = (response) => {
    const { activeChannelId } = this.state;
    scrollToBottom();
    if (response.status === 'success') {
      if (response.message.temp_id) {
        this.setState(({ messages }) => {
          const newMessages = messages;
          const foundIndex = messages[activeChannelId].findIndex(
            (message) => message.temp_id === response.message.temp_id,
          );
          if (foundIndex > 0) {
            newMessages[activeChannelId][foundIndex].id = response.message.id;
          }
          return { messages: newMessages };
        });
      }
    } else if (response.status === 'moderation-success') {
      addSnackbarItem({ message: response.message, addCloseButton: true });
    } else if (response.status === 'error') {
      addSnackbarItem({ message: response.message, addCloseButton: true });
    }
  };

  handleRequestRejection = (e) => {
    rejectJoiningRequest(
      e.target.dataset.channelId,
      e.target.dataset.membershipId,
      this.handleJoiningManagerSuccess(e.target.dataset.membershipId),
      null,
    );
  };

  handleRequestApproval = (e) => {
    acceptJoiningRequest(
      e.target.dataset.channelId,
      e.target.dataset.membershipId,
      this.handleJoiningManagerSuccess(e.target.dataset.membershipId),
      null,
    );
  };

  handleUpdateRequestCount = (isAccepted = false, acceptedInfo) => {
    if (isAccepted) {
      const searchParams = {
        query: '',
        retrievalID: null,
        searchType: '',
        paginationNumber: 0,
      };
      getChannels(searchParams, 'all', this.loadChannels);
      this.triggerSwitchChannel(
        parseInt(acceptedInfo.channelId, 10),
        acceptedInfo.channelSlug,
        this.state.chatChannels,
      );
    }

    this.setState((prevState) => {
      return {
        userRequestCount: prevState.userRequestCount - 1,
      };
    });
  };

  triggerActiveContent = (e) => {
    if (
      // Trying to open in new tab
      e.ctrlKey ||
      e.shiftKey ||
      e.metaKey || // apple
      (e.button && e.button === 1) // middle click, >IE9 + everyone else
    ) {
      return false;
    }
    const { target } = e;
    const content =
      target.dataset.content || target.parentElement.dataset.content;
    if (content) {
      e.preventDefault();
      e.stopPropagation();
      this.hideChannelList();

      const { activeChannelId, activeChannel } = this.state;

      if (content.startsWith('chat_channels/')) {
        this.setActiveContentState(activeChannelId, {
          type_of: 'loading-user',
        });
        getContent(`/${content}/channel_info`, this.setActiveContent, null);
      } else if (content === 'sidecar-channel-request') {
        this.setActiveContent({
          data: {
            user: getCurrentUser(),
            channel: {
              id: target.dataset.channelId,
              name: target.dataset.channelName,
              status: target.dataset.channelStatus,
            },
          },
          handleJoiningRequest: this.handleJoiningRequest,
          type_of: 'channel-request',
        });
      } else if (content === 'sidecar-joining-request-manager') {
        this.setActiveContent({
          data: {},
          type_of: 'channel-request-manager',
          updateRequestCount: this.handleUpdateRequestCount,
        });
      } else if (content === 'sidecar_all') {
        this.setActiveContentState(activeChannelId, {
          type_of: 'loading-post',
        });
        this.setActiveContent({
          path: `/chat_channel_memberships/${activeChannel.id}/edit`,
          type_of: 'article',
        });
      } else if (content.startsWith('sidecar-content-plus-video')) {
        this.setActiveContentState(activeChannelId, {
          type_of: 'loading-post',
        });
        this.setActiveContent({
          path: target.href || target.parentElement.href,
          type_of: 'article',
        });
        this.setState({ videoPath: `/video_chats/${activeChannelId}` });
      } else if (content.startsWith('sidecar-video')) {
        this.setState({ videoPath: target.href || target.parentElement.href });
      } else if (
        content.startsWith('sidecar') ||
        content.startsWith('article')
      ) {
        // article is legacy which can be removed shortly
        this.setActiveContentState(activeChannelId, {
          type_of: 'loading-post',
        });
        this.setActiveContent({
          path: target.href || target.parentElement.href,
          type_of: 'article',
        });
      } else if (target.dataset.content === 'exit') {
        this.setActiveContentState(activeChannelId, null);
        this.setState({
          fullscreenContent: null,
          expanded: window.innerWidth > NARROW_WIDTH_LIMIT,
        });
      } else if (target.dataset.content === 'fullscreen') {
        const mode =
          this.state.fullscreenContent === 'sidecar' ? null : 'sidecar';
        this.setState({
          fullscreenContent: mode,
          expanded: mode === null || window.innerWidth > WIDE_WIDTH_LIMIT,
        });
      } else if (target.dataset.content === 'chat_channel_setting') {
        this.setActiveContent({
          data: {},
          type_of: 'chat-channel-setting',
          activeMembershipId: activeChannel.id,
          handleLeavingChannel: this.handleLeavingChannel,
        });
      }
    }
    return false;
  };

  setActiveContentState = (channelId, state) => {
    this.setState((prevState) => ({
      activeContent: {
        ...prevState.activeContent,
        [channelId]: state,
      },
    }));
  };

  closeReportAbuseForm = () => {
    const { activeChannelId } = this.state;
    this.setActiveContentState(activeChannelId, null);
    this.setState({
      fullscreenContent: null,
      expanded: window.innerWidth > NARROW_WIDTH_LIMIT,
    });
  };

  setActiveContent = (response) => {
    const { activeChannelId } = this.state;
    this.setActiveContentState(activeChannelId, response);
    setTimeout(() => {
      document.getElementById('chat_activecontent').scrollTop = 0;
      document.getElementById('chat').scrollLeft = 1000;
    }, 3);
    setTimeout(() => {
      document.getElementById('chat_activecontent').scrollTop = 0;
      document.getElementById('chat').scrollLeft = 1000;
    }, 10);
  };

  handleChannelOpenSuccess = (response) => {
    this.setState(({ chatChannels }) => {
      const newChannelsObj = chatChannels.map((channel) => {
        if (parseInt(response.channel, 10) === channel.chat_channel_id) {
          return { ...channel, last_opened_at: new Date() };
        }
        return channel;
      });
      return { chatChannels: newChannelsObj };
    });
  };

  handleLeavingChannel = (leftChannelId) => {
    const { chatChannels } = this.state;
    this.triggerSwitchChannel(
      chatChannels[1].chat_channel_id,
      chatChannels[1].channel_modified_slug,
      chatChannels,
    );
    this.setState((prevState) => ({
      chatChannels: prevState.chatChannels.filter(
        (channel) => channel.id !== leftChannelId,
      ),
    }));
    this.setActiveContentState(chatChannels[1].chat_channel_id, null);
  };

  triggerChannelTypeFilter = (e) => {
    const { filterQuery } = this.state;
    const type = e.target.dataset.channelType;
    this.setState({
      channelTypeFilter: type,
      fetchingPaginatedChannels: false,
    });
    const filters = type === 'all' ? {} : { filters: `channel_type:${type}` };
    const searchParams = {
      query: filterQuery,
      retrievalID: null,
      searchType: '',
      paginationNumber: 0,
    };
    if (filterQuery && type !== 'direct') {
      searchParams.searchType = 'discoverable';
      getChannels(searchParams, filters, this.loadChannels);
    } else {
      getChannels(searchParams, filters, this.loadChannels);
    }
  };

  handleFailure = (err) => {
    // eslint-disable-next-line no-console
    console.error(err);
    addSnackbarItem({ message: err, addCloseButton: true });
  };

  renderMessages = () => {
    const {
      activeChannelId,
      messages,
      showTimestamp,
      activeChannel,
      currentUserId,
    } = this.state;
    if (!messages[activeChannelId]) {
      return '';
    }
    if (messages[activeChannelId].length === 0 && activeChannel) {
      if (activeChannel.channel_type === 'direct') {
        return (
          <div className="chatmessage" style={{ color: 'grey' }}>
            <div className="chatmessage__body">
              You and{' '}
              <a href={`/${activeChannel.channel_modified_slug}`}>
                {activeChannel.channel_modified_slug}
              </a>{' '}
              are connected because you both follow each other. All interactions{' '}
              <em>
                <b>must</b>
              </em>{' '}
              abide by the <a href="/code-of-conduct">code of conduct</a>.
            </div>
          </div>
        );
      }
      if (activeChannel.channel_type === 'open') {
        return (
          <div className="chatmessage" style={{ color: 'grey' }}>
            <div className="chatmessage__body">
              You have joined {activeChannel.channel_name}! All interactions{' '}
              <em>
                <b>must</b>
              </em>{' '}
              abide by the <a href="/code-of-conduct">code of conduct</a>.
            </div>
          </div>
        );
      }
    }
    return messages[activeChannelId].map((message) =>
      message.action ? (
        <ActionMessage
          user={message.username}
          profileImageUrl={message.profile_image_url}
          message={message.message}
          timestamp={showTimestamp ? message.timestamp : null}
          color={message.color}
          onContentTrigger={this.triggerActiveContent}
        />
      ) : (
        <Message
          currentUserId={currentUserId}
          id={message.id}
          user={message.username}
          userID={message.user_id}
          profileImageUrl={message.profile_image_url}
          message={message.message}
          timestamp={showTimestamp ? message.timestamp : null}
          editedAt={message.edited_at}
          color={message.color}
          onContentTrigger={this.triggerActiveContent}
          onDeleteMessageTrigger={this.triggerDeleteMessage}
          onEditMessageTrigger={this.triggerEditMessage}
          onReportMessageTrigger={this.triggerReportMessage}
        />
      ),
    );
  };
  triggerReportMessage = (messageId) => {
    const { activeChannelId, messages } = this.state;

    this.setActiveContent({
      data: messages[activeChannelId].find(
        (message) => message.id === messageId,
      ),
      type_of: 'message-report-abuse',
    });
  };
  triggerChannelFilter = (e) => {
    const { channelTypeFilter } = this.state;
    const filters =
      channelTypeFilter === 'all'
        ? {}
        : { filters: `channel_type:${channelTypeFilter}` };
    const searchParams = {
      query: e.target.value,
      retrievalID: null,
      searchType: '',
      paginationNumber: 0,
    };
    if (e.target.value) {
      searchParams.searchType = 'discoverable';
      getChannels(searchParams, filters, this.loadChannels);
    } else {
      getChannels(searchParams, filters, this.loadChannels);
    }
  };

  toggleExpand = () => {
    this.setState((prevState) => ({ expanded: !prevState.expanded }));
  };

  renderChannelFilterButton = (type, name, active) => (
    <Button
      data-channel-type={type}
      onClick={this.triggerChannelTypeFilter}
      data-testid={name}
      className={`chat__channeltypefilterbutton crayons-indicator crayons-indicator--${
        type === active ? 'accent' : ''
      }`}
    >
      {name}
    </Button>
  );

  toggleSearchShowing = () => {
    if (!this.state.searchShowing) {
      setTimeout(() => {
        document.getElementById('chatchannelsearchbar').focus();
      }, 100);
    } else {
      const searchParams = {
        query: '',
        retrievalID: null,
        searchType: '',
        paginationNumber: 0,
      };
      getChannels(searchParams, 'all', this.loadChannels);
      this.setState({ filterQuery: '' });
    }
    this.setState({ searchShowing: !this.state.searchShowing });
  };

  renderChatChannels = () => {
    const { state } = this;
    if (state.showChannelsList) {
      const { notificationsPermission } = state;
      const notificationsButton = '';
      let notificationsState = '';
      let invitesButton = '';
      let joiningRequestButton = '';
      if (notificationsPermission === 'granted') {
        notificationsState = (
          <div className="chat_chatconfig chat_chatconfig--on">
            Notifications On
          </div>
        );
      } else if (notificationsPermission === 'denied') {
        notificationsState = (
          <div className="chat_chatconfig chat_chatconfig--off">
            Notifications Off
          </div>
        );
      }

      return (
        <div className="chat__channels">
          {notificationsButton}

          <input
            placeholder="Search Channels"
            onKeyUp={this.debouncedChannelFilter}
            id="chatchannelsearchbar"
            className="crayons-textfield"
            aria-label="Search Channels"
          />
          {invitesButton}
          {joiningRequestButton}
          <div className="chat__channeltypefilter">
            {this.renderChannelFilterButton(
              'all',
              'all',
              state.channelTypeFilter,
            )}
            {this.renderChannelFilterButton(
              'direct',
              'direct',
              state.channelTypeFilter,
            )}
            {this.renderChannelFilterButton(
              'invite_only',
              'group',
              state.channelTypeFilter,
            )}
            <Button
              className="chat__channelssearchtoggle crayons-btn--ghost-dimmed p-2"
              aria-label="Toggle request manager"
              onClick={this.triggerActiveContent}
              data-content="sidecar-joining-request-manager"
            >
              <svg
                xmlns="http://www.w3.org/2000/svg"
                viewBox="0 0 477.869 477.869"
                width="18"
                height="18"
              >
                <path d="M387.415 233.496c48.976-44.03 52.987-119.424 8.958-168.4C355.99 20.177 288.4 12.546 239.02 47.332c-53.83-38-128.264-25.15-166.254 28.68-34.86 49.393-27.26 117.054 17.69 157.483C34.606 262.935-.25 320.976.002 384.108v51.2a17.07 17.07 0 0 0 17.067 17.067h443.733a17.07 17.07 0 0 0 17.067-17.067v-51.2c.252-63.132-34.605-121.173-90.454-150.612zM307.2 59.842c47.062-.052 85.256 38.057 85.31 85.12.037 33.564-19.63 64.023-50.237 77.8-1.314.597-2.628 1.143-3.96 1.707a83.66 83.66 0 0 1-12.988 4.045c-.853.188-1.707.3-2.577.46-4.952.95-9.977 1.457-15.02 1.52-2.27 0-4.557-.17-6.827-.375-.853 0-1.707 0-2.56-.17a86.22 86.22 0 0 1-27.904-8.226c-.324-.154-.7-.137-1.024-.273-1.707-.82-3.413-1.536-4.932-2.458.137-.17.222-.358.358-.53a119.72 119.72 0 0 0 18.278-33.297l.53-1.434a120.38 120.38 0 0 0 4.523-17.562c.154-.87.273-1.707.4-2.645.987-6.067 1.506-12.2 1.553-18.347a120.04 120.04 0 0 0-1.553-18.313l-.4-2.645c-1.064-5.96-2.576-11.83-4.523-17.562l-.53-1.434c-4.282-12-10.453-23.24-18.278-33.297-.137-.17-.222-.358-.358-.53C277.45 63.83 292.2 59.843 307.2 59.842zM85.335 145.176c-.12-47.006 37.886-85.2 84.892-85.33a85.11 85.11 0 0 1 59.134 23.686l2.918 2.9a87.75 87.75 0 0 1 8.09 9.813c.75 1.058 1.434 2.185 2.133 3.277a83.95 83.95 0 0 1 6.263 11.52c.427.973.75 1.963 1.126 2.935a83.42 83.42 0 0 1 4.233 13.653c.12.512.154 1.024.256 1.553a80.34 80.34 0 0 1 0 32.119c-.102.53-.137 1.04-.256 1.553a83.23 83.23 0 0 1-4.233 13.653c-.375.973-.7 1.963-1.126 2.935a84.25 84.25 0 0 1-6.263 11.503c-.7 1.092-1.382 2.22-2.133 3.277a87.55 87.55 0 0 1-8.09 9.813 117.37 117.37 0 0 1-2.918 2.901c-6.9 6.585-14.877 11.962-23.57 15.906a49.35 49.35 0 0 1-4.198 1.707 85.84 85.84 0 0 1-12.663 3.925c-1.075.24-2.185.375-3.277.563a84.67 84.67 0 0 1-14.046 1.417h-1.877c-4.713-.08-9.412-.554-14.046-1.417-1.092-.188-2.202-.324-3.277-.563a85.8 85.8 0 0 1-12.663-3.925l-4.198-1.707c-30.534-13.786-50.173-44.166-50.212-77.667zM307.2 418.242H34.135V384.11c-.25-57.833 36.188-109.468 90.76-128.614 29.296 12.197 62.25 12.197 91.546 0a137.14 137.14 0 0 1 16.623 7.356c3.55 1.826 6.827 3.908 10.24 6.007 2.22 1.382 4.47 2.73 6.605 4.25 3.294 2.338 6.4 4.88 9.455 7.492l5.75 5.12c2.816 2.662 5.46 5.478 8.004 8.363 1.826 2.082 3.6 4.198 5.29 6.383 2.236 2.867 4.37 5.803 6.35 8.823 1.707 2.56 3.226 5.222 4.727 7.885 1.707 2.935 3.277 5.87 4.7 8.926s2.697 6.4 3.925 9.66c1.075 2.833 2.22 5.65 3.106 8.533 1.195 3.96 2.03 8.055 2.867 12.15.512 2.423 1.178 4.796 1.553 7.253 1.01 6.757 1.53 13.58 1.553 20.412v34.133zm136.534 0h-102.4V384.11c0-5.342-.307-10.633-.785-15.872-.137-1.536-.375-3.055-.546-4.59-.46-3.772-1-7.51-1.707-11.213l-.973-4.762c-.82-3.8-1.77-7.566-2.85-11.298l-1.058-3.686c-4.78-15.277-11.704-29.797-20.565-43.127l-.666-.973a168.96 168.96 0 0 0-9.404-12.646l-.12-.154c-3.413-4.232-7.117-8.346-11.008-12.237h.7a120.8 120.8 0 0 0 14.524 1.024h.94c4.496-.04 8.985-.33 13.45-.87 1.4-.17 2.782-.427 4.18-.65a117.43 117.43 0 0 0 10.752-2.167l3.055-.785a116.21 116.21 0 0 0 13.653-4.642c54.612 19.127 91.083 70.785 90.83 128.65v34.132z" />
              </svg>
              {this.state.userRequestCount > 0 ? (
                <span className="crayons-indicator crayons-indicator--accent crayons-indicator--bullet requests-badge">
                  {this.state.userRequestCount}
                </span>
              ) : null}
            </Button>
            {this.state.isTagModerator ? (
              <Button
                className="chat__channelssearchtoggle crayons-btn--ghost-dimmed p-2"
                aria-label="Toggle request manager"
                onClick={this.toggleModalCreateChannel}
                data-content="sidecar-joining-request-manager"
              >
                <svg
                  version="1.1"
                  id="Capa_1"
                  xmlns="http://www.w3.org/2000/svg"
                  x="0px"
                  y="0px"
                  width="18"
                  height="18"
                  viewBox="0 0 512 512"
                  style="enable-background:new 0 0 512 512;"
                >
                  <path
                    d="M492,236H276V20c0-11.046-8.954-20-20-20c-11.046,0-20,8.954-20,20v216H20c-11.046,0-20,8.954-20,20s8.954,20,20,20h216
                  v216c0,11.046,8.954,20,20,20s20-8.954,20-20V276h216c11.046,0,20-8.954,20-20C512,244.954,503.046,236,492,236z"
                  />
                </svg>
              </Button>
            ) : null}
            {this.state.openModal ? (
              <CreateChatModal
                toggleModalCreateChannel={this.toggleModalCreateChannel}
                handleCreateChannelSuccess={this.handleCreateChannelSuccess}
              />
            ) : (
              ''
            )}
          </div>
          <Channels
            activeChannelId={state.activeChannelId}
            chatChannels={state.chatChannels}
            unopenedChannelIds={state.unopenedChannelIds}
            handleSwitchChannel={this.handleSwitchChannel}
            channelsLoaded={state.channelsLoaded}
            filterQuery={state.filterQuery}
            expanded={state.expanded}
            aria-expanded={state.expanded}
            currentUserId={state.currentUserId}
            triggerActiveContent={this.triggerActiveContent}
          />
          {notificationsState}
        </div>
      );
    }
    return '';
  };

  toggleModalCreateChannel = () => {
    const { openModal } = this.state;
    this.setState({ openModal: !openModal });
  };

  navigateToChannelsList = () => {
    const chatContainer = document.getElementsByClassName(
      'chat__activechat',
    )[0];

    chatContainer.classList.add('chat__activechat--hidden');
  };

  handleCreateChannelSuccess = () => {
    this.toggleModalCreateChannel();
    const searchParams = {
      query: '',
      retrievalID: null,
      searchType: '',
      paginationNumber: 0,
    };
    getChannels(searchParams, {}, this.loadChannels);
  };

  handleMessageScroll = () => {
    const {
      allMessagesLoaded,
      messages,
      activeChannelId,
      messageOffset,
    } = this.state;

    if (!messages[activeChannelId]) {
      return;
    }

    const jumpbackButton = document.getElementById('jumpback_button');

    if (this.scroller) {
      const scrolledRatio =
        (this.scroller.scrollTop + this.scroller.clientHeight) /
        this.scroller.scrollHeight;

      if (scrolledRatio < 0.5) {
        jumpbackButton.classList.remove('chatchanneljumpback__hide');
      } else if (scrolledRatio > 0.6) {
        jumpbackButton.classList.add('chatchanneljumpback__hide');
      }

      if (this.scroller.scrollTop === 0 && !allMessagesLoaded) {
        getAllMessages(
          activeChannelId,
          messageOffset + messages[activeChannelId].length,
          this.addMoreMessages,
        );
        const curretPosition = this.scroller.scrollHeight;
        this.setState({ currentMessageLocation: curretPosition });
      }
    }
  };

  addMoreMessages = (res) => {
    const { chatChannelId, messages } = res;

    if (messages.length > 0) {
      this.setState((prevState) => ({
        messages: {
          [chatChannelId]: [...messages, ...prevState.messages[chatChannelId]],
        },
      }));
    } else {
      this.setState({ allMessagesLoaded: true });
    }
  };

  jumpBacktoBottom = () => {
    scrollToBottom();
    document
      .getElementById('jumpback_button')
      .classList.remove('chatchanneljumpback__hide');
  };

  handleDragOver = (event) => {
    event.preventDefault();
    event.currentTarget.classList.add('opacity-25');
  };

  handleDragExit = (event) => {
    event.preventDefault();
    event.currentTarget.classList.remove('opacity-25');
  };

  handleImageDrop = (event) => {
    event.preventDefault();
    const { files } = event.dataTransfer;
    event.currentTarget.classList.remove('opacity-25');
    processImageUpload(files, this.handleImageSuccess, this.handleImageFailure);
  };
  sendCanvasImage = (files) => {
    dragAndUpload([files], this.handleImageSuccess, this.handleImageFailure);
  };
  handleImageSuccess = (res) => {
    const { links, image } = res;
    const mLink = `![${image[0].name}](${links[0]})`;
    const el = document.getElementById('messageform');
    const start = el.selectionStart;
    const end = el.selectionEnd;
    const text = el.value;
    let before = text.substring(0, start);
    before = text.substring(0, before.lastIndexOf('@') + 1);
    const after = text.substring(end, text.length);
    el.value = `${before + mLink} ${after}`;
    el.selectionStart = start + mLink.length + 1;
    el.selectionEnd = el.selectionStart;
    el.focus();
  };
  handleImageFailure = (e) => {
    addSnackbarItem({ message: e.message, addCloseButton: true });
  };
  handleDragHover(e) {
    e.preventDefault();
    const messageArea = document.getElementById('messagelist');
    messageArea.classList.add('opacity-25');
  }
  handleDragExit(e) {
    e.preventDefault();
    const messageArea = document.getElementById('messagelist');
    messageArea.classList.remove('opacity-25');
  }
  renderActiveChatChannel = (channelHeader) => {
    const { state, props } = this;
    const channelName = state.activeChannel
      ? state.activeChannel.channel_name
      : ' ';
    return (
      <div className="activechatchannel">
        <div className="activechatchannel__conversation">
          {channelHeader}
          <DragAndDropZone
            onDragOver={this.handleDragOver}
            onDragExit={this.handleDragExit}
            onDrop={this.handleImageDrop}
          >
            <div
              className="activechatchannel__messages"
              onScroll={this.handleMessageScroll}
              ref={(scroller) => {
                this.scroller = scroller;
              }}
              id="messagelist"
            >
              {this.renderMessages()}
              <div
                className="messagelist__sentinel"
                id="messagelist__sentinel"
              />
            </div>
          </DragAndDropZone>
          <div
            className="chatchanneljumpback chatchanneljumpback__hide"
            id="jumpback_button"
          >
            <Button
              className="chatchanneljumpback__messages crayons-btn--outlined"
              onClick={this.jumpBacktoBottom}
              tabIndex="0"
              onKeyUp={(e) => {
                if (e.keyCode === 13) this.jumpBacktoBottom();
              }}
            >
              Scroll to Bottom
            </Button>
          </div>
          {this.renderDeleteModal()}
          <div className="activechatchannel__alerts">
            <Alert showAlert={state.showAlert} />
          </div>
          {this.renderChannelMembersList()}
          <div className="activechatchannel__form">
            <Compose
              handleSubmitOnClick={this.handleSubmitOnClick}
              handleKeyDown={this.handleKeyDown}
              handleSubmitOnClickEdit={this.handleSubmitOnClickEdit}
              handleMention={this.handleMention}
              handleKeyUp={this.handleKeyUp}
              handleKeyDownEdit={this.handleKeyDownEdit}
              activeChannelId={state.activeChannelId}
              activeChannelName={channelName}
              startEditing={state.startEditing}
              markdownEdited={state.markdownEdited}
              editMessageMarkdown={state.activeEditMessage.markdown}
              handleEditMessageClose={this.handleEditMessageClose}
              handleFilePaste={this.handleFilePaste}
            />
          </div>
        </div>
        <Content
          onTriggerContent={this.triggerActiveContent}
          resource={state.activeContent[state.activeChannelId]}
          activeChannel={state.activeChannel}
          fullscreen={state.fullscreenContent === 'sidecar'}
          closeReportAbuseForm={this.closeReportAbuseForm}
        />
        <VideoContent
          videoPath={state.videoPath}
          onTriggerVideoContent={this.onTriggerVideoContent}
          fullscreen={state.fullscreenContent === 'video'}
        />
      </div>
    );
  };

  handleFilePaste = (e) => {
    if (!e.clipboardData || !e.clipboardData.items) {
      return;
    }
    const items = [];
    for (let i = 0; i < e.clipboardData.items.length; i++) {
      const item = e.clipboardData.items[i];
      if (item.kind !== 'file') {
        continue;
      }
      items.push(item);
    }
    if (items && items.length > 0) {
      processImageUpload(
        [items[0].getAsFile()],
        this.handleImageSuccess,
        this.handleImageFailure,
      );
    }
  };

  onTriggerVideoContent = (e) => {
    if (e.target.dataset.content === 'exit') {
      this.setState({
        videoPath: null,
        fullscreenContent: null,
        expanded: window.innerWidth > 600,
      });
    } else if (this.state.fullscreenContent === 'video') {
      this.setState({ fullscreenContent: null });
    } else {
      this.setState({
        fullscreenContent: 'video',
        expanded: window.innerWidth > WIDE_WIDTH_LIMIT,
      });
    }
  };

  handleMention = (e) => {
    const { activeChannel } = this.state;
    const mention = e.keyCode === 64;
    if (mention && activeChannel.channel_type !== 'direct') {
      const memberListElement = document.getElementById('mentionList');
      memberListElement.focus();
      this.setState({ showMemberlist: true });
    }
  };

  handleKeyUp = (e) => {
    const { startEditing, activeChannel, showMemberlist } = this.state;
    const enterPressed = e.keyCode === 13;
    if (enterPressed && showMemberlist)
      this.setState({ showMemberlist: false });
    if (activeChannel.channel_type !== 'direct') {
      if (startEditing) {
        this.setState({ markdownEdited: true });
      }
      if (!e.target.value.includes('@') && showMemberlist) {
        this.setState({ showMemberlist: false });
      } else {
        this.setQuery(e.target);
        this.listHighlightManager(e.keyCode);
      }
    }
  };

  setQuery = (e) => {
    const { showMemberlist } = this.state;
    if (showMemberlist) {
      const before = e.value.substring(0, e.selectionStart);
      const query = before.substring(
        before.lastIndexOf('@') + 1,
        e.selectionStart,
      );

      if (query.includes(' ') || before.lastIndexOf('@') < 0)
        this.setState({ showMemberlist: false });
      else {
        this.setState({ showMemberlist: true });
        this.setState({ memberFilterQuery: query });
      }
    }
  };

  addUserName = (e) => {
    const name =
      e.target.dataset.content || e.target.parentElement.dataset.content;
    const el = document.getElementById('messageform');
    const start = el.selectionStart;
    const end = el.selectionEnd;
    const text = el.value;
    let before = text.substring(0, start);
    before = text.substring(0, before.lastIndexOf('@') + 1);
    const after = text.substring(end, text.length);
    el.value = `${before + name} ${after}`;
    el.selectionStart = start + name.length + 1;
    el.selectionEnd = start + name.length + 1;
    el.dispatchEvent(new Event('input'));
    el.focus();
    this.setState({ showMemberlist: false });
  };

  listHighlightManager = (keyCode) => {
    const mentionList = document.getElementById('mentionList');
    const activeElement = document.getElementsByClassName(
      'active__message__list',
    )[0];
    if (mentionList.children.length > 0) {
      if (keyCode === 40 && activeElement) {
        if (activeElement.nextElementSibling) {
          activeElement.classList.remove('active__message__list');
          activeElement.nextElementSibling.classList.add(
            'active__message__list',
          );
        }
      } else if (keyCode === 38 && activeElement) {
        if (activeElement.previousElementSibling) {
          activeElement.classList.remove('active__message__list');
          activeElement.previousElementSibling.classList.add(
            'active__message__list',
          );
        }
      } else {
        mentionList.children[0].classList.add('active__message__list');
      }
    }
  };

  getMentionedUsers = (message) => {
    const { channelUsers, activeChannelId, activeChannel } = this.state;
    if (channelUsers[activeChannelId]) {
      if (message.includes('@all') && activeChannel.channel_type !== 'open') {
        return Array.from(
          Object.values(channelUsers[activeChannelId]).filter(
            (user) => user.id,
          ),
          (user) => user.id,
        );
      }
      return Array.from(
        Object.values(channelUsers[activeChannelId]).filter((user) =>
          message.includes(user.username),
        ),
        (user) => user.id,
      );
    }
    return null;
  };

  renderChannelMembersList = () => {
    const {
      showMemberlist,
      activeChannelId,
      channelUsers,
      memberFilterQuery,
    } = this.state;

    const filterRegx = new RegExp(memberFilterQuery, 'gi');

    return (
      <div
        className={
          showMemberlist ? 'mention__list mention__visible' : 'mention__list'
        }
        id="mentionList"
        data-testid="mentionList"
      >
        {showMemberlist
          ? Object.values(channelUsers[activeChannelId])
              .filter((user) => user.username.match(filterRegx))
              .map((user) => (
                <div
                  key={user.username}
                  className="mention__user"
                  role="button"
                  onClick={this.addUserName}
                  tabIndex="0"
                  data-content={user.username}
                  onKeyUp={(e) => {
                    if (e.keyCode === 13) this.addUserName();
                  }}
                >
                  <img
                    className="mention__user__image"
                    src={user.profile_image}
                    alt={user.name}
                    style={!user.profile_image ? { display: 'none' } : ' '}
                  />
                  <span
                    style={{
                      padding: '3px 0px',
                      'font-size': '16px',
                    }}
                  >
                    {'@'}
                    {user.username}
                    <p>{user.name}</p>
                  </span>
                </div>
              ))
          : ' '}
      </div>
    );
  };

  handleEditMessageClose = () => {
    this.setState({
      startEditing: false,
      markdownEdited: false,
      activeEditMessage: { message: '', markdown: '' },
    });
  };

  renderDeleteModal = () => {
    const { showDeleteModal } = this.state;
    return (
      <div
        id="message"
        className={
          showDeleteModal
            ? 'message__delete__modal crayons-modal crayons-modal--s absolute'
            : 'message__delete__modal message__delete__modal__hide crayons-modal crayons-modal--s absolute'
        }
        aria-hidden={showDeleteModal}
        aria-label="delete confirmation"
        role="dialog"
      >
        <div className="crayons-modal__box">
          <div className="crayons-modal__box__body">
            <h3>Are you sure, you want to delete this message?</h3>
            <div className="delete-actions__container">
              <Button
                className="crayons-btn crayons-btn--danger message__delete__button"
                onClick={this.handleMessageDelete}
                tabIndex="0"
                onKeyUp={(e) => {
                  if (e.keyCode === 13) this.handleMessageDelete();
                }}
              >
                Delete
              </Button>
              <Button
                className="crayons-btn crayons-btn--secondary message__cancel__button"
                onClick={this.handleCloseDeleteModal}
                tabIndex="0"
                onKeyUp={(e) => {
                  if (e.keyCode === 13) this.handleCloseDeleteModal();
                }}
              >
                Cancel
              </Button>
            </div>
          </div>
        </div>
        <div className="crayons-modal__overlay" />
      </div>
    );
  };

  handleCloseDeleteModal = () => {
    this.setState({ showDeleteModal: false, messageDeleteId: null });
  };

  handleMessageDelete = () => {
    const { messageDeleteId } = this.state;
    deleteMessage(messageDeleteId);
    this.setState({ showDeleteModal: false });
  };

  handleJoiningRequest = (e) => {
    sendChannelRequest(
      e.target.dataset.channelId,
      this.handleJoiningRequestSuccess,
      this.handleFailure,
    );
  };

  handleJoiningRequestSuccess = () => {
    const { activeChannelId } = this.state;
    this.setActiveContentState(activeChannelId, null);
    this.setState({ fullscreenContent: null });
    this.toggleSearchShowing();
  };

  renderChannelBackNav = () => {
    return (
      <Button
        className="crayons-btn crayons-btn--icon-rounded crayons-btn--ghost active-channel__back-btn"
        onClick={this.navigateToChannelsList}
        onKeyUp={(e) => {
          if (e.keyCode === 13) this.navigateToChannelsList(e);
        }}
        tabIndex="0"
      >
        <svg
          xmlns="http://www.w3.org/2000/svg"
          viewBox="0 0 24 24"
          width="24"
          height="24"
          className="crayons-icon"
        >
          <path d="M10.828 12l4.95 4.95-1.414 1.414L8 12l6.364-6.364 1.414 1.414z" />
        </svg>
      </Button>
    );
  };

  renderChannelHeaderInner = () => {
    const { activeChannel } = this.state;
    if (activeChannel.channel_type === 'direct') {
      return (
        <a
          href={`/${activeChannel.channel_username}`}
          className="active-channel__title"
          onClick={this.triggerActiveContent}
          data-content="sidecar-user"
        >
          {activeChannel.channel_modified_slug}
        </a>
      );
    }
    return (
      <a
        href="#/"
        onClick={this.triggerActiveContent}
        data-content="chat_channel_setting"
      >
        {activeChannel.channel_name}
      </a>
    );
  };

  renderChannelConfigImage = () => {
    const { activeContent, activeChannel, activeChannelId } = this.state;
    if (
      activeContent[activeChannelId] &&
      activeContent[activeChannelId].type_of
    ) {
      return '';
    }

    const path =
      activeChannel.channel_type === 'direct'
        ? `/${activeChannel.channel_username}`
        : `#`;

    const dataContent =
      activeChannel.channel_type === 'direct'
        ? 'sidecar-user'
        : `chat_channel_setting`;

    const contentLink =
      activeChannel.channel_type === 'direct'
        ? `/${activeChannel.channel_username}`
        : '#/';

    return (
      <a
        className="crayons-btn crayons-btn--icon-rounded crayons-btn--ghost"
        onClick={this.triggerActiveContent}
        onKeyUp={(e) => {
          if (e.keyCode === 13) this.triggerActiveContent(e);
        }}
        tabIndex="0"
        href={contentLink}
        data-content={dataContent}
      >
        <svg
          xmlns="http://www.w3.org/2000/svg"
          viewBox="0 0 24 24"
          width="24"
          height="24"
          className="crayons-icon"
          data-content={dataContent}
        >
          <path
            data-content={dataContent}
            d="M12 22C6.477 22 2 17.523 2 12S6.477 2 12 2s10 4.477 10 10-4.477 10-10 10zm0-2a8 8 0 1 0 0-16 8 8 0 0 0 0 16zM11 7h2v2h-2V7zm0 4h2v6h-2v-6z"
          />
        </svg>
      </a>
    );
  };

  render() {
    const { state } = this;
    let channelHeader = <div className="active-channel__header">&nbsp;</div>;
    if (state.activeChannel) {
      channelHeader = (
        <div className="active-channel__header">
          {this.renderChannelBackNav()}
          {this.renderChannelHeaderInner()}
          {this.renderChannelConfigImage()}
        </div>
      );
    }
    let fullscreenMode = '';
    if (state.fullscreenContent === 'sidecar') {
      fullscreenMode = 'chat--content-visible-full';
    } else if (state.fullscreenContent === 'video') {
      fullscreenMode = 'chat--video-visible-full';
    }
    return (
      <div
        data-testid="chat"
        className={`chat chat--expanded
         chat--${
           state.videoPath ? 'video-visible' : 'video-not-visible'
         } chat--${
          state.activeContent[state.activeChannelId]
            ? 'content-visible'
            : 'content-not-visible'
        } ${fullscreenMode}`}
        data-no-instant
        aria-expanded={state.expanded}
      >
        {this.renderChatChannels()}
        <div data-testid="active-chat" className="chat__activechat">
          {this.renderActiveChatChannel(channelHeader)}
        </div>
      </div>
    );
  }
}
