diff --git a/src/pages/chat.js b/src/pages/chat.js new file mode 100644 index 0000000..623b1b8 --- /dev/null +++ b/src/pages/chat.js @@ -0,0 +1,492 @@ +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 ( +