From 31db17632846a4d2b7bd495f9b2b209ac69bfe0a Mon Sep 17 00:00:00 2001 From: Jeremie Pardou-Piquemal <571533+jrmi@users.noreply.github.com> Date: Thu, 15 Dec 2022 23:15:55 +0100 Subject: [PATCH] Refactor Chat --- client/src/components/Chat/Chat.jsx | 368 ++++++++++++---------------- 1 file changed, 158 insertions(+), 210 deletions(-) diff --git a/client/src/components/Chat/Chat.jsx b/client/src/components/Chat/Chat.jsx index 92e0b27..a986b90 100644 --- a/client/src/components/Chat/Chat.jsx +++ b/client/src/components/Chat/Chat.jsx @@ -1,171 +1,134 @@ -import React, { Component } from 'react'; +import React from 'react'; import PropTypes from 'prop-types'; import sanitizeHtml from 'sanitize-html'; import { CornerDownRight } from 'react-feather'; -import { getSelectedText, hasTouchSupport } from '@/utils/dom'; +import { hasTouchSupport } from '@/utils/dom'; import FileTransfer from '@/components/FileTransfer'; -export class Chat extends Component { - constructor(props) { - super(props); - this.state = { - message: '', - touchSupport: hasTouchSupport, - shiftKeyDown: false, - }; +export const Chat = ({ sendEncryptedMessage, showNotice, userId, username, clearActivities, translations }) => { + const [message, setMessage] = React.useState(''); + const [shiftKeyDown, setShiftKeyDown] = React.useState(false); + const textInputRef = React.useRef(); - this.commands = [ - { - command: 'nick', - description: 'Changes nickname.', - parameters: ['{username}'], - usage: '/nick {username}', - scope: 'global', - action: params => { - // eslint-disable-line - let newUsername = params.join(' ') || ''; // eslint-disable-line + const touchSupport = hasTouchSupport; - // Remove things that aren't digits or chars - newUsername = newUsername.replace(/[^A-Za-z0-9]/g, '-'); + const canSend = message.trim().length; - const errors = []; + const commands = [ + { + command: 'nick', + description: 'Changes nickname.', + parameters: ['{username}'], + usage: '/nick {username}', + scope: 'global', + action: params => { + // eslint-disable-line + let newUsername = params.join(' ') || ''; // eslint-disable-line - if (!newUsername.trim().length) { - errors.push('Username cannot be blank'); - } + // Remove things that aren't digits or chars + newUsername = newUsername.replace(/[^A-Za-z0-9]/g, '-'); - if (newUsername.toString().length > 16) { - errors.push('Username cannot be greater than 16 characters'); - } + const errors = []; - if (!newUsername.match(/^[A-Z]/i)) { - errors.push('Username must start with a letter'); - } + if (!newUsername.trim().length) { + errors.push('Username cannot be blank'); + } - if (errors.length) { - return this.props.showNotice({ - message: `${errors.join(', ')}`, - level: 'error', - }); - } + if (newUsername.toString().length > 16) { + errors.push('Username cannot be greater than 16 characters'); + } - this.props.sendEncryptedMessage({ - type: 'CHANGE_USERNAME', - payload: { - id: this.props.userId, - newUsername, - currentUsername: this.props.username, - }, + if (!newUsername.match(/^[A-Z]/i)) { + errors.push('Username must start with a letter'); + } + + if (errors.length) { + return showNotice({ + message: `${errors.join(', ')}`, + level: 'error', }); - }, - }, - { - command: 'help', - description: 'Shows a list of commands.', - paramaters: [], - usage: '/help', - scope: 'local', - action: params => { - // eslint-disable-line - const validCommands = this.commands.map(command => `/${command.command}`); - this.props.showNotice({ - message: `Valid commands: ${validCommands.sort().join(', ')}`, - level: 'info', - }); - }, - }, - { - command: 'me', - description: 'Invoke virtual action', - paramaters: ['{action}'], - usage: '/me {action}', - scope: 'global', - action: params => { - // eslint-disable-line - const actionMessage = params.join(' '); - if (!actionMessage.trim().length) { - return false; - } + } - this.props.sendEncryptedMessage({ - type: 'USER_ACTION', - payload: { - action: actionMessage, - }, - }); - }, - }, - { - command: 'clear', - description: 'Clears the chat screen', - paramaters: [], - usage: '/clear', - scope: 'local', - action: (params = null) => { - // eslint-disable-line - this.props.clearActivities(); - }, - }, - ]; - } - - componentDidMount() { - if (!hasTouchSupport) { - // Disable for now due to vary issues: - // Paste not working, shift+enter line breaks - // autosize(this.textInput); - this.textInput.addEventListener('autosize:resized', () => { - this.props.scrollToBottom(); - }); - } - } - - componentWillReceiveProps(nextProps) { - if (nextProps.focusChat) { - if (!getSelectedText()) { - // Don't focus for now, evaluate UX benfits - // this.textInput.focus() - } - } - } - - componentDidUpdate(nextProps, nextState) { - if (!nextState.message.trim().length) { - // autosize.update(this.textInput) - } - } - - handleKeyUp(e) { - if (e.key === 'Shift') { - this.setState({ - shiftKeyDown: false, - }); - } - } - - handleKeyPress(e) { - if (e.key === 'Shift') { - this.setState({ - shiftKeyDown: true, - }); - } - // Fix when autosize is enabled - line breaks require shift+enter twice - if (e.key === 'Enter' && !hasTouchSupport && !this.state.shiftKeyDown) { - e.preventDefault(); - if (this.canSend()) { - this.sendMessage(); - } else { - this.setState({ - message: '', + sendEncryptedMessage({ + type: 'CHANGE_USERNAME', + payload: { + id: userId, + newUsername, + currentUsername: username, + }, }); + }, + }, + { + command: 'help', + description: 'Shows a list of commands.', + parameters: [], + usage: '/help', + scope: 'local', + action: () => { + const validCommands = commands.map(command => `/${command.command}`); + showNotice({ + message: `Valid commands: ${validCommands.sort().join(', ')}`, + level: 'info', + }); + }, + }, + { + command: 'me', + description: 'Invoke virtual action', + parameters: ['{action}'], + usage: '/me {action}', + scope: 'global', + action: params => { + const actionMessage = params.join(' '); + if (!actionMessage.trim().length) { + return false; + } + + sendEncryptedMessage({ + type: 'USER_ACTION', + payload: { + action: actionMessage, + }, + }); + }, + }, + { + command: 'clear', + description: 'Clears the chat screen', + parameters: [], + usage: '/clear', + scope: 'local', + action: () => { + clearActivities(); + }, + }, + ]; + + const handleKeyUp = e => { + if (e.key === 'Shift') { + setShiftKeyDown(false); + } + }; + + const handleKeyPress = e => { + if (e.key === 'Shift') { + setShiftKeyDown(true); + } + if (e.key === 'Enter' && !hasTouchSupport && !shiftKeyDown) { + e.preventDefault(); + if (canSend) { + sendMessage(); + } else { + setMessage(''); } } - } + }; - executeCommand(command) { - const commandToExecute = this.commands.find(cmnd => cmnd.command === command.command); + const executeCommand = command => { + const commandToExecute = commands.find(cmnd => cmnd.command === command.command); if (commandToExecute) { const { params } = command; @@ -175,19 +138,19 @@ export class Chat extends Component { } return null; - } + }; - handleSendClick() { - this.sendMessage.bind(this); - this.textInput.focus(); - } + const handleSendClick = () => { + sendMessage(); + textInputRef.current.focus(); + }; - handleFormSubmit(evt) { + const handleFormSubmit = evt => { evt.preventDefault(); - this.sendMessage(); - } + sendMessage(); + }; - parseCommand(message) { + const parseCommand = message => { const commandTrigger = { command: null, params: [], @@ -207,23 +170,22 @@ export class Chat extends Component { } return false; - } + }; - sendMessage() { - if (!this.canSend()) { + const sendMessage = () => { + if (!canSend) { return; } - const { message } = this.state; - const isCommand = this.parseCommand(message); + const isCommand = parseCommand(message); if (isCommand) { - const res = this.executeCommand(isCommand); + const res = executeCommand(isCommand); if (res === false) { return; } } else { - this.props.sendEncryptedMessage({ + sendEncryptedMessage({ type: 'TEXT_MESSAGE', payload: { text: message, @@ -232,55 +194,41 @@ export class Chat extends Component { }); } - this.setState({ - message: '', - }); - } + setMessage(''); + }; - handleInputChange(evt) { - this.setState({ - message: evt.target.value, - }); - } + const handleInputChange = evt => { + setMessage(evt.target.value); + }; - canSend() { - return this.state.message.trim().length; - } - - render() { - const touchSupport = this.state.touchSupport; - - return ( -
-