import React from 'react' import Head from 'next/head' import io from 'socket.io-client' import constants from '../constants.json' import LoadingIndicator from '../components/LoadingIndicator' import styles from './chat.module.css' function countAllMessages(msgs) { return Object.keys(msgs).reduce((acc, key) => { return acc + msgs[key].length }, 0) } function groupMessages(msgs, currUser) { return msgs.reduce((acc, msg) => { const group = parseInt(msg.sender) !== parseInt(currUser) ? msg.sender : msg.reciever return { ...acc, [group]: [msg], } }, {}) } function addMsgsToGroup(msgGroup, msgs, user) { let res = { ...msgGroup } msgs.forEach((msg) => { res = addMsgToGroup( res, msg.reciever === user ? { ...msg, unread: 0 } : msg, user ) }) return res } function addMsgToGroup(msgGroup, msg, user) { const group = parseInt(msg.sender) === parseInt(user) ? msg.reciever : msg.sender if (!msgGroup[group]) { msgGroup[group] = [] } return { ...msgGroup, [group]: [...msgGroup[group], msg], } } function SeenMarker() { return ( Látta ) } function NewMarker() { return ( Új üzenet ) } export default class Chat extends React.Component { constructor(props) { super(props) this.state = { msgs: {}, currMsg: '', connected: false, selectedUser: 0, } if (props.refetchGlobalData) { props.refetchGlobalData() } if (props.globalData && !isNaN(props.globalData.userId)) { this.state.user = props.globalData.userId // this.connect(this.props.globalData.userId) } } componentDidUpdate(prevProps, prevState) { if (!prevProps.globalData.userId && this.props.globalData.userId) { this.setState({ user: this.props.globalData.userId, }) // this.connect(this.props.globalData.userId) } if ( countAllMessages(prevState.msgs) !== countAllMessages(this.state.msgs) || prevState.selectedUser !== this.state.selectedUser ) { this.scrollToChatBottom() } } componentWillUnmount() { console.info('Chat disconnect') if (this.socket) { this.socket.disconnect() } } scrollToChatBottom() { const objDiv = document.getElementById('messages') if (objDiv) { objDiv.scrollTop = objDiv.scrollHeight } } handleErrors(err) { alert(err.message) console.error(err) } connect(user) { const { connected } = this.state if (connected) { console.warn('Already connected ...') return } // https://socket.io/docs/v4/handling-cors/#Configuration const socket = io(`${constants.apiUrl}`, { withCredentials: true, extraHeaders: { 'qmining-chat': 'qmining-chat', }, }) socket.on('connect', () => { console.info(`Connected as user ${user}`) socket.emit('join', { id: user }) }) socket.on('connect_error', (err) => this.handleErrors(err)) socket.on('connect_failed', (err) => this.handleErrors(err)) socket.on('prev messages', (data) => { const { prevMsgs } = data const { user } = this.state this.setState({ msgs: groupMessages(prevMsgs, user), connected: true, }) }) socket.on('chat message read', (data) => { const { userReadMsg } = data this.partnerReadChatMessage(userReadMsg) }) socket.on('chat message open', (data) => { const { msgs, user, selectedUser } = this.state if (msgs[selectedUser].length <= 1) { this.setState({ msgs: addMsgsToGroup(msgs, data, user), }) } }) socket.on('chat message', (data) => { const { msgs, user } = this.state this.setState({ msgs: addMsgToGroup(msgs, data, user), }) }) this.socket = socket } sendMsg() { const { msgs, selectedUser, currMsg, user } = this.state if (!currMsg) { return } if (this.socket && selectedUser) { const msg = { msg: currMsg, reciever: selectedUser, sender: user, date: new Date().getTime(), unread: 1, } this.socket.emit('chat message', msg) this.setState({ currMsg: '', msgs: addMsgToGroup(msgs, msg, user), }) } } partnerReadChatMessage(chatPartner) { const { msgs } = this.state this.setState({ msgs: { ...msgs, [chatPartner]: msgs[chatPartner].map((msg) => { if (msg.reciever === chatPartner) { return { ...msg, unread: 0, } } else { return msg } }), }, }) } chatMessageRead(chatPartner) { const { msgs, user } = this.state if (this.props.refetchGlobalData) { this.props.refetchGlobalData() } this.socket.emit('chat message read', { chatPartner: chatPartner }) this.setState({ msgs: { ...msgs, [chatPartner]: msgs[chatPartner].map((msg) => { if (msg.reciever === user) { return { ...msg, unread: 0, } } else { return msg } }), }, }) } selectedUserChange(val) { const { msgs, selectedUser, user } = this.state const prevLastMessage = msgs[selectedUser] ? msgs[selectedUser][msgs[selectedUser].length - 1] : null if ( prevLastMessage && prevLastMessage.unread === 1 && prevLastMessage.sender !== user ) { this.chatMessageRead(selectedUser) } this.setState({ selectedUser: val, }) if (!val || isNaN(val)) { return } const currSelectedMsgs = msgs[val] if (!currSelectedMsgs) { return } if (msgs[val].length <= 1) { this.socket.emit('chat message open', { chatPartner: val, }) } const lastMessage = currSelectedMsgs[currSelectedMsgs.length - 1] if (lastMessage.unread === 1 && lastMessage.sender !== user) { this.chatMessageRead(val) } } renderChatInput() { const { currMsg, msgs, selectedUser, user } = this.state return (