/* 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 (
{user.name}