From 5452b574acff3ee7c474f2f22c03d35a10671725 Mon Sep 17 00:00:00 2001 From: SrGooglo Date: Wed, 14 Dec 2022 15:33:22 +0000 Subject: [PATCH] implement `LiveChat` rooms feature --- .../app/src/components/LiveChat/index.jsx | 161 +++++++++++++++--- .../app/src/components/LiveChat/index.less | 68 +++++++- 2 files changed, 203 insertions(+), 26 deletions(-) diff --git a/packages/app/src/components/LiveChat/index.jsx b/packages/app/src/components/LiveChat/index.jsx index 4e5d4c83..f1feb578 100644 --- a/packages/app/src/components/LiveChat/index.jsx +++ b/packages/app/src/components/LiveChat/index.jsx @@ -1,52 +1,134 @@ import React from "react" import * as antd from "antd" +import classnames from "classnames" +import { io } from "socket.io-client" +import config from "config" +import SessionModel from "models/session" import "./index.less" const Line = (props) => { - const { user, content, timestamp } = props + const { user, content } = props - return
- {content} + return
+
+

{user.fullName ?? user.username}

+
+
+ {content} +
} - export default class LiveChat extends React.Component { state = { socket: null, + connecting: true, + connectionEnd: false, + roomInfo: null, timeline: [], + writtedMessage: "", } - joinSocketRoom = () => { + timelineRef = React.createRef() + + joinSocketRoom = async () => { + await this.setState({ connecting: true }) + const { roomId } = this.props const socketNamespace = `/textRoom/${roomId}` console.log(`Joining socket room [${socketNamespace}]`) - const socket = app.api.namespaces.main.wsInterface.manager.socket(socketNamespace) + const socket = io(config.remotes.messagingApi, { + transports: ["websocket"], + autoConnect: false, + }) - socket.connect() + socket.auth = { + token: SessionModel.token, + } - console.log("Socket", socket) + socket.on("connect_error", (err) => { + console.error("Connection error", err) + + this.setState({ connectionEnd: true }) + + if (err.message === "auth:token_invalid") { + console.error("Invalid token") + } + }) socket.on("connect", () => { - console.log("Socket connected") + socket.emit("join", { room: socketNamespace }, (error, info) => { + if (error) { + this.setState({ connectionEnd: true }) + return console.error("Error joining room", error) + } - socket.on("message", (data) => { - console.log("Message received", data) + this.setState({ + connecting: false, + roomInfo: info, + }) }) }) + socket.on("message", (message) => { + this.pushToTimeline(message) + this.scrollTimelineToBottom() + }) + + socket.connect() + this.setState({ socket }) } submitMessage = (message) => { const { socket } = this.state - console.log("Sending message", message) + socket.emit("send:message", { + message + }) - socket.emit("message", message) + this.scrollTimelineToBottom() + + // remove writted message + this.setState({ + writtedMessage: "" + }) + } + + pushToTimeline = (message) => { + const { timeline } = this.state + + this.setState({ + timeline: [...timeline, message] + }) + } + + handleInputChange = (e) => { + this.setState({ + writtedMessage: e.target.value + }) + } + + handleOnEnter = (e) => { + e.preventDefault() + e.stopPropagation() + + if (e.target.value.length === 0) { + return + } + + this.submitMessage(e.target.value) + } + + scrollTimelineToBottom = () => { + // scroll to bottom smoothly + this.timelineRef.current.scrollTo({ + top: this.timelineRef.current.scrollHeight, + behavior: "smooth" + }) } componentDidMount = async () => { @@ -58,21 +140,52 @@ export default class LiveChat extends React.Component { } render() { - return
- {this.state.timeline.map((line, index) => )} + if (this.state.connectionEnd) { + return
+ +
+ } -
+ if (this.state.connecting) { + return
+ +
+ } + + return
+ { + this.state.timeline.length === 0 ? + : +
+ { + this.state.timeline.map((line, index) => { + return + }) + } +
+ } + +
{ - e.preventDefault() - e.stopPropagation() - - console.log("Enter pressed", e.target.value) - - this.submitMessage(e.target.value) - }} + value={this.state.writtedMessage} + onChange={this.handleInputChange} + onPressEnter={this.handleOnEnter} + maxLength={this.state.roomInfo?.limitations?.maxMessageLength ?? 100} + showCount />
diff --git a/packages/app/src/components/LiveChat/index.less b/packages/app/src/components/LiveChat/index.less index 1306152b..43d6ddd6 100644 --- a/packages/app/src/components/LiveChat/index.less +++ b/packages/app/src/components/LiveChat/index.less @@ -1,18 +1,82 @@ .liveChat { + position: relative; + display: flex; flex-direction: column; width: 100%; height: 100%; - position: relative; + overflow: hidden; - .textInput { + &.empty { + justify-content: center; + align-items: center; + } + + .liveChat_timeline { + height: 100%; + overflow-y: scroll; + + margin-bottom: 70px; + } + + .liveChat_textInput { position: absolute; bottom: 0; left: 0; + margin-bottom: 20px; + width: 100%; height: fit-content; + + color: var(--text-color); + + .ant-input-textarea-show-count::after { + color: var(--text-color); + } + } +} + +.textRoom_line { + display: flex; + flex-direction: column; + + padding: 5px 10px; + margin-bottom: 10px; + + border-radius: 12px; + + background-color: var(--background-color-accent); + + h1, + h2, + h3, + h4, + span { + margin: 0; + user-select: text; + } + + .textRoom_line_user { + font-weight: bold; + font-size: 1rem; + color: var(--background-color-contrast); + + overflow: hidden; + + text-overflow: ellipsis; + white-space: nowrap; + } + + .textRoom_line_content { + font-size: 0.8rem; + color: var(--text-color); + + padding: 0 10px; + + word-break: break-all; + white-space: pre-wrap; } } \ No newline at end of file