mirror of
https://github.com/ragestudio/comty.git
synced 2025-06-09 10:34:17 +00:00
improve livechat
This commit is contained in:
parent
bfd4e28d01
commit
b43aa731ca
@ -8,327 +8,353 @@ import SessionModel from "@models/session"
|
|||||||
import "./index.less"
|
import "./index.less"
|
||||||
|
|
||||||
const Line = (props) => {
|
const Line = (props) => {
|
||||||
const { user, content } = props
|
const { user, content } = props
|
||||||
|
|
||||||
return <div className="textRoom_line">
|
return (
|
||||||
<div className="textRoom_line_user">
|
<div className="textRoom_line">
|
||||||
<h4>{user.fullName ?? user.username}</h4>
|
<div className="textRoom_line_user">
|
||||||
</div>
|
<h4>{user.fullName ?? user.username}</h4>
|
||||||
<div className="textRoom_line_content">
|
</div>
|
||||||
<span>{content}</span>
|
<div className="textRoom_line_content">
|
||||||
</div>
|
<span>{content}</span>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class LiveChat extends React.Component {
|
export default class LiveChat extends React.Component {
|
||||||
state = {
|
state = {
|
||||||
joining: true,
|
joining: true,
|
||||||
roomInfo: null,
|
roomInfo: null,
|
||||||
|
|
||||||
timeline: [],
|
timeline: [],
|
||||||
temporalTimeline: [],
|
temporalTimeline: [],
|
||||||
maxTemporalLines: this.props.maxTemporalLines ?? 10,
|
maxTemporalLines: this.props.maxTemporalLines ?? 10,
|
||||||
|
|
||||||
lastSentMessage: null,
|
lastSentMessage: null,
|
||||||
writtedMessage: "",
|
writtedMessage: "",
|
||||||
}
|
}
|
||||||
|
|
||||||
debouncedIntervalTimelinePurge = null
|
debouncedIntervalTimelinePurge = null
|
||||||
|
|
||||||
timelineRef = React.createRef()
|
timelineRef = React.createRef()
|
||||||
|
|
||||||
socket = app.cores.api.client().sockets.chats
|
socket = app.cores.api.client().ws.sockets.get("chats")
|
||||||
|
|
||||||
roomEvents = {
|
roomEvents = {
|
||||||
"room:message": (message) => {
|
"room:message": (message) => {
|
||||||
if (message.content === this.state.lastSentMessage) {
|
if (message.content === this.state.lastSentMessage) {
|
||||||
console.timeEnd("[CHATROOM] SUBMIT:MESSAGE")
|
console.timeEnd("[CHATROOM] SUBMIT:MESSAGE")
|
||||||
}
|
}
|
||||||
|
|
||||||
this.pushToTimeline(message)
|
this.pushToTimeline(message)
|
||||||
},
|
},
|
||||||
"room:joined": (info) => {
|
"room:joined": (info) => {
|
||||||
console.log("[CHATROOM] Room joined", info)
|
console.log("[CHATROOM] Room joined", info)
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
joining: false,
|
joining: false,
|
||||||
roomInfo: info,
|
roomInfo: info,
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
"room:leave": (info) => {
|
"room:leave": (info) => {
|
||||||
console.log("[CHATROOM] Room left", info)
|
console.log("[CHATROOM] Room left", info)
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
joining: true,
|
joining: true,
|
||||||
roomInfo: null,
|
roomInfo: null,
|
||||||
})
|
})
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
joinSocketRoom = async () => {
|
joinSocketRoom = async () => {
|
||||||
if (!SessionModel.token) {
|
if (!SessionModel.token) {
|
||||||
return this.setState({
|
return this.setState({
|
||||||
noAuthed: true,
|
noAuthed: true,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(`[CHATROOM] Joining socket room [${this.props.id}]...`)
|
|
||||||
|
|
||||||
for (const [eventName, eventHandler] of Object.entries(this.roomEvents)) {
|
console.log(`[CHATROOM] Joining socket room [${this.props.id}]...`)
|
||||||
this.socket.on(eventName, eventHandler)
|
|
||||||
}
|
|
||||||
|
|
||||||
await this.setState({
|
for (const [eventName, eventHandler] of Object.entries(
|
||||||
joining: true,
|
this.roomEvents,
|
||||||
})
|
)) {
|
||||||
|
this.socket.on(eventName, eventHandler)
|
||||||
|
}
|
||||||
|
|
||||||
this.socket.emit(
|
await this.setState({
|
||||||
"join:room",
|
joining: true,
|
||||||
{
|
})
|
||||||
room: this.props.id,
|
|
||||||
},
|
|
||||||
(error, info) => {
|
|
||||||
if (error) {
|
|
||||||
this.setState({ connectionEnd: true })
|
|
||||||
|
|
||||||
return console.error("Error joining room", error)
|
this.socket.emit(
|
||||||
}
|
"join:room",
|
||||||
}
|
{
|
||||||
)
|
room: this.props.id,
|
||||||
}
|
},
|
||||||
|
(error, info) => {
|
||||||
|
if (error) {
|
||||||
|
this.setState({ connectionEnd: true })
|
||||||
|
|
||||||
leaveSocketRoom = () => {
|
return console.error("Error joining room", error)
|
||||||
if (this.state.connectionEnd) {
|
}
|
||||||
return false
|
},
|
||||||
}
|
)
|
||||||
|
}
|
||||||
console.log(`[CHATROOM] Leaving socket room [${this.props.id}]...`)
|
|
||||||
|
|
||||||
for (const [eventName, eventHandler] of Object.entries(this.roomEvents)) {
|
leaveSocketRoom = () => {
|
||||||
this.socket.off(eventName, eventHandler)
|
if (this.state.connectionEnd) {
|
||||||
}
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
this.socket.emit("leave:room")
|
console.log(`[CHATROOM] Leaving socket room [${this.props.id}]...`)
|
||||||
}
|
|
||||||
|
|
||||||
submitMessage = (message) => {
|
for (const [eventName, eventHandler] of Object.entries(
|
||||||
console.time("[CHATROOM] SUBMIT:MESSAGE")
|
this.roomEvents,
|
||||||
|
)) {
|
||||||
|
this.socket.off(eventName, eventHandler)
|
||||||
|
}
|
||||||
|
|
||||||
this.socket.emit("room:send:message", {
|
this.socket.emit("leave:room")
|
||||||
message
|
}
|
||||||
})
|
|
||||||
|
|
||||||
// remove writted message
|
submitMessage = (message) => {
|
||||||
this.setState({
|
console.time("[CHATROOM] SUBMIT:MESSAGE")
|
||||||
lastSentMessage: message,
|
|
||||||
writtedMessage: ""
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pushToTimeline = (message) => {
|
this.socket.emit("room:send:message", {
|
||||||
const { timeline } = this.state
|
message,
|
||||||
|
})
|
||||||
|
|
||||||
if (typeof message.key === "undefined") {
|
// remove writted message
|
||||||
message.key = this.state.timeline.length
|
this.setState({
|
||||||
}
|
lastSentMessage: message,
|
||||||
|
writtedMessage: "",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
this.setState({
|
pushToTimeline = (message) => {
|
||||||
timeline: [...timeline, message]
|
const { timeline } = this.state
|
||||||
})
|
|
||||||
|
|
||||||
if (this.props.floatingMode) {
|
if (typeof message.key === "undefined") {
|
||||||
if (this.state.temporalTimeline.length >= this.state.maxTemporalLines) {
|
message.key = this.state.timeline.length
|
||||||
this.setState({
|
}
|
||||||
temporalTimeline: this.state.temporalTimeline.slice(1)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// calculate duration based on message length (Minimum 3 second, maximum 10 seconds)
|
this.setState({
|
||||||
const calculatedDuration = Math.min(Math.max(message.content.length * 0.1, 3), 10) * 1000
|
timeline: [...timeline, message],
|
||||||
|
})
|
||||||
|
|
||||||
const temporalLine = {
|
if (this.props.floatingMode) {
|
||||||
expireTime: Date.now() + calculatedDuration,
|
if (
|
||||||
duration: calculatedDuration,
|
this.state.temporalTimeline.length >=
|
||||||
messageKey: message.key
|
this.state.maxTemporalLines
|
||||||
}
|
) {
|
||||||
|
this.setState({
|
||||||
|
temporalTimeline: this.state.temporalTimeline.slice(1),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
this.setState({
|
// calculate duration based on message length (Minimum 3 second, maximum 10 seconds)
|
||||||
temporalTimeline: [...this.state.temporalTimeline, temporalLine]
|
const calculatedDuration =
|
||||||
})
|
Math.min(Math.max(message.content.length * 0.1, 3), 10) * 1000
|
||||||
|
|
||||||
if (this.debouncedIntervalTimelinePurge) {
|
const temporalLine = {
|
||||||
clearInterval(this.debouncedIntervalTimelinePurge)
|
expireTime: Date.now() + calculatedDuration,
|
||||||
}
|
duration: calculatedDuration,
|
||||||
|
messageKey: message.key,
|
||||||
|
}
|
||||||
|
|
||||||
this.debouncedIntervalTimelinePurge = setInterval(this.purgeLastTemporalLine, 3000)
|
this.setState({
|
||||||
}
|
temporalTimeline: [
|
||||||
|
...this.state.temporalTimeline,
|
||||||
|
temporalLine,
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
this.scrollTimelineToBottom()
|
if (this.debouncedIntervalTimelinePurge) {
|
||||||
}
|
clearInterval(this.debouncedIntervalTimelinePurge)
|
||||||
|
}
|
||||||
|
|
||||||
purgeLastTemporalLine = () => {
|
this.debouncedIntervalTimelinePurge = setInterval(
|
||||||
if (!this.props.floatingMode) {
|
this.purgeLastTemporalLine,
|
||||||
return false
|
3000,
|
||||||
}
|
)
|
||||||
|
}
|
||||||
|
|
||||||
const { temporalTimeline } = this.state
|
this.scrollTimelineToBottom()
|
||||||
|
}
|
||||||
|
|
||||||
if (temporalTimeline.length === 0) {
|
purgeLastTemporalLine = () => {
|
||||||
clearInterval(this.debouncedIntervalTimelinePurge)
|
if (!this.props.floatingMode) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
const lastTemporalLine = temporalTimeline[0]
|
const { temporalTimeline } = this.state
|
||||||
|
|
||||||
if (lastTemporalLine.expireTime < Date.now()) {
|
if (temporalTimeline.length === 0) {
|
||||||
this.setState({
|
clearInterval(this.debouncedIntervalTimelinePurge)
|
||||||
temporalTimeline: temporalTimeline.slice(1)
|
return false
|
||||||
})
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
handleInputChange = (e) => {
|
const lastTemporalLine = temporalTimeline[0]
|
||||||
if (e.target.value[0] === " " || e.target.value[0] === "\n") {
|
|
||||||
e.target.value = e.target.value.slice(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
this.setState({
|
if (lastTemporalLine.expireTime < Date.now()) {
|
||||||
writtedMessage: e.target.value
|
this.setState({
|
||||||
})
|
temporalTimeline: temporalTimeline.slice(1),
|
||||||
}
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
handleOnEnter = (e) => {
|
handleInputChange = (e) => {
|
||||||
e.preventDefault()
|
if (e.target.value[0] === " " || e.target.value[0] === "\n") {
|
||||||
e.stopPropagation()
|
e.target.value = e.target.value.slice(1)
|
||||||
|
}
|
||||||
|
|
||||||
if (e.target.value.length === 0) {
|
this.setState({
|
||||||
return
|
writtedMessage: e.target.value,
|
||||||
}
|
})
|
||||||
|
}
|
||||||
|
|
||||||
this.submitMessage(e.target.value)
|
handleOnEnter = (e) => {
|
||||||
}
|
e.preventDefault()
|
||||||
|
e.stopPropagation()
|
||||||
|
|
||||||
scrollTimelineToBottom = () => {
|
if (e.target.value.length === 0) {
|
||||||
const scrollingElement = document.getElementById("liveChat_timeline")
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if (scrollingElement) {
|
this.submitMessage(e.target.value)
|
||||||
scrollingElement.scrollTo({
|
}
|
||||||
top: scrollingElement.scrollHeight,
|
|
||||||
behavior: "smooth"
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidMount = async () => {
|
scrollTimelineToBottom = () => {
|
||||||
this.joinSocketRoom()
|
const scrollingElement = document.getElementById("liveChat_timeline")
|
||||||
|
|
||||||
app.ctx = {
|
if (scrollingElement) {
|
||||||
submit: this.submitMessage
|
scrollingElement.scrollTo({
|
||||||
}
|
top: scrollingElement.scrollHeight,
|
||||||
}
|
behavior: "smooth",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
componentWillUnmount() {
|
componentDidMount = async () => {
|
||||||
this.leaveSocketRoom()
|
this.joinSocketRoom()
|
||||||
|
|
||||||
if (this.debouncedIntervalTimelinePurge) {
|
|
||||||
clearInterval(this.debouncedIntervalTimelinePurge)
|
|
||||||
}
|
|
||||||
|
|
||||||
delete app.ctx
|
app.ctx = {
|
||||||
}
|
submit: this.submitMessage,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
componentWillUnmount() {
|
||||||
if (this.state.connectionEnd) {
|
this.leaveSocketRoom()
|
||||||
return <div className="liveChat">
|
|
||||||
<antd.Result
|
|
||||||
status="error"
|
|
||||||
title="Connection error"
|
|
||||||
subTitle="Cannot connect to the server"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.state.connecting) {
|
if (this.debouncedIntervalTimelinePurge) {
|
||||||
return <div className="liveChat">
|
clearInterval(this.debouncedIntervalTimelinePurge)
|
||||||
<antd.Skeleton active />
|
}
|
||||||
</div>
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.props.floatingMode) {
|
delete app.ctx
|
||||||
return <div className="liveChat floating">
|
}
|
||||||
<TransitionGroup
|
|
||||||
ref={this.timelineRef}
|
|
||||||
className="liveChat_timeline"
|
|
||||||
id="liveChat_timeline"
|
|
||||||
>
|
|
||||||
{
|
|
||||||
this.state.temporalTimeline.map((line, index) => {
|
|
||||||
return <CSSTransition
|
|
||||||
key={index}
|
|
||||||
timeout={300}
|
|
||||||
classNames={{
|
|
||||||
enterActive: "transverse-enter",
|
|
||||||
exitActive: "transverse-out"
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Line {...this.state.timeline[line.messageKey]} />
|
|
||||||
</CSSTransition>
|
|
||||||
})
|
|
||||||
}
|
|
||||||
</TransitionGroup>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.state.noAuthed) {
|
render() {
|
||||||
return <div className="liveChat empty">
|
if (this.state.connectionEnd) {
|
||||||
<antd.Empty description="You must be logged in to use this feature" />
|
return (
|
||||||
</div>
|
<div className="liveChat">
|
||||||
}
|
<antd.Result
|
||||||
|
status="error"
|
||||||
|
title="Connection error"
|
||||||
|
subTitle="Cannot connect to the server"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
return <div
|
if (this.state.connecting) {
|
||||||
className={classnames(
|
return (
|
||||||
"liveChat",
|
<div className="liveChat">
|
||||||
{
|
<antd.Skeleton active />
|
||||||
["empty"]: this.state.timeline.length === 0,
|
</div>
|
||||||
["compact"]: this.props.compact
|
)
|
||||||
}
|
}
|
||||||
)}
|
|
||||||
>
|
|
||||||
{
|
|
||||||
!this.props.compact && this.state.timeline.length === 0 && <antd.Empty description="Welcome to the room" />
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
if (this.props.floatingMode) {
|
||||||
this.props.compact && this.state.timeline.length === 0 && <p>
|
return (
|
||||||
Welcome to the room
|
<div className="liveChat floating">
|
||||||
</p>
|
<TransitionGroup
|
||||||
}
|
ref={this.timelineRef}
|
||||||
|
className="liveChat_timeline"
|
||||||
|
id="liveChat_timeline"
|
||||||
|
>
|
||||||
|
{this.state.temporalTimeline.map((line, index) => {
|
||||||
|
return (
|
||||||
|
<CSSTransition
|
||||||
|
key={index}
|
||||||
|
timeout={300}
|
||||||
|
classNames={{
|
||||||
|
enterActive: "transverse-enter",
|
||||||
|
exitActive: "transverse-out",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Line
|
||||||
|
{...this.state.timeline[
|
||||||
|
line.messageKey
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
</CSSTransition>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</TransitionGroup>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
{
|
if (this.state.noAuthed) {
|
||||||
this.state.timeline.length !== 0 && <div
|
return (
|
||||||
className="liveChat_timeline"
|
<div className="liveChat empty">
|
||||||
ref={this.timelineRef}
|
<antd.Empty description="You must be logged in to use this feature" />
|
||||||
id="liveChat_timeline"
|
</div>
|
||||||
>
|
)
|
||||||
{
|
}
|
||||||
this.state.timeline.map((line, index) => {
|
|
||||||
return <Line key={index} {...line} />
|
|
||||||
})
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
|
|
||||||
<div className="liveChat_textInput">
|
return (
|
||||||
<antd.Input.TextArea
|
<div
|
||||||
placeholder="Type your message here"
|
className={classnames("liveChat", {
|
||||||
autoSize={{ minRows: 1, maxRows: 3 }}
|
["empty"]: this.state.timeline.length === 0,
|
||||||
value={this.state.writtedMessage}
|
["compact"]: this.props.compact,
|
||||||
onChange={this.handleInputChange}
|
})}
|
||||||
onPressEnter={this.handleOnEnter}
|
>
|
||||||
maxLength={this.state.roomInfo?.limitations?.maxMessageLength ?? 100}
|
{!this.props.compact && this.state.timeline.length === 0 && (
|
||||||
showCount
|
<antd.Empty description="Welcome to the room" />
|
||||||
/>
|
)}
|
||||||
</div>
|
|
||||||
</div>
|
{this.props.compact && this.state.timeline.length === 0 && (
|
||||||
}
|
<p>Welcome to the room</p>
|
||||||
}
|
)}
|
||||||
|
|
||||||
|
{this.state.timeline.length !== 0 && (
|
||||||
|
<div
|
||||||
|
className="liveChat_timeline"
|
||||||
|
ref={this.timelineRef}
|
||||||
|
id="liveChat_timeline"
|
||||||
|
>
|
||||||
|
{this.state.timeline.map((line, index) => {
|
||||||
|
return <Line key={index} {...line} />
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<div className="liveChat_textInput">
|
||||||
|
<antd.Input.TextArea
|
||||||
|
placeholder="Type your message here"
|
||||||
|
autoSize={{ minRows: 1, maxRows: 3 }}
|
||||||
|
value={this.state.writtedMessage}
|
||||||
|
onChange={this.handleInputChange}
|
||||||
|
onPressEnter={this.handleOnEnter}
|
||||||
|
maxLength={
|
||||||
|
this.state.roomInfo?.limitations
|
||||||
|
?.maxMessageLength ?? 100
|
||||||
|
}
|
||||||
|
showCount
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,153 +1,154 @@
|
|||||||
.liveChat {
|
.liveChat {
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
|
||||||
transition: all 250ms ease-in-out;
|
transition: all 250ms ease-in-out;
|
||||||
|
|
||||||
&.empty {
|
&.empty {
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
||||||
&.compact {
|
&.compact {
|
||||||
height: 5vh !important;
|
height: 5vh !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&.compact {
|
&.compact {
|
||||||
height: 15vh;
|
height: 15vh;
|
||||||
|
|
||||||
justify-content: flex-end;
|
justify-content: flex-end;
|
||||||
|
|
||||||
.liveChat_timeline {
|
.liveChat_timeline {
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|
||||||
padding: 0;
|
padding: 0;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
|
|
||||||
margin-bottom: 10px;
|
gap: 5px;
|
||||||
|
|
||||||
gap: 5px;
|
.textRoom_line {
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
|
||||||
.textRoom_line {
|
h4 {
|
||||||
padding: 0;
|
font-size: 0.8rem;
|
||||||
margin: 0;
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
h4 {
|
.liveChat_textInput {
|
||||||
font-size: 0.8rem;
|
position: relative;
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.liveChat_textInput {
|
bottom: 0;
|
||||||
position: relative;
|
left: 0;
|
||||||
|
|
||||||
bottom: 0;
|
margin-bottom: 0;
|
||||||
left: 0;
|
}
|
||||||
|
}
|
||||||
|
|
||||||
margin-bottom: 0;
|
&.floating {
|
||||||
}
|
.textRoom_line {
|
||||||
}
|
display: relative;
|
||||||
|
|
||||||
&.floating {
|
background-color: rgba(var(--layoutBackgroundColor), 0.7);
|
||||||
.textRoom_line {
|
backdrop-filter: blur(20px);
|
||||||
display: relative;
|
}
|
||||||
|
|
||||||
background-color: rgba(var(--layoutBackgroundColor), 0.7);
|
.liveChat_timeline {
|
||||||
backdrop-filter: blur(20px);
|
display: flex;
|
||||||
}
|
|
||||||
|
|
||||||
.liveChat_timeline {
|
flex-direction: column;
|
||||||
display: flex;
|
padding-top: 0;
|
||||||
|
|
||||||
flex-direction: column;
|
overflow-x: hidden;
|
||||||
padding-top: 0;
|
overflow-y: hidden;
|
||||||
|
|
||||||
overflow-x: hidden;
|
//justify-content: flex-end;
|
||||||
overflow-y: hidden;
|
|
||||||
|
|
||||||
//justify-content: flex-end;
|
margin-bottom: 0;
|
||||||
|
|
||||||
margin-bottom: 0;
|
transition: all 250ms ease-in-out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
transition: all 250ms ease-in-out;
|
.liveChat_timeline {
|
||||||
}
|
position: relative;
|
||||||
}
|
|
||||||
|
|
||||||
.liveChat_timeline {
|
display: flex;
|
||||||
position: relative;
|
flex-direction: column;
|
||||||
height: 100%;
|
|
||||||
|
|
||||||
overflow-y: scroll;
|
gap: 10px;
|
||||||
|
|
||||||
margin-bottom: 70px;
|
height: 100%;
|
||||||
}
|
overflow-y: scroll;
|
||||||
|
|
||||||
.liveChat_textInput {
|
margin-bottom: 70px;
|
||||||
position: absolute;
|
}
|
||||||
bottom: 0;
|
|
||||||
left: 0;
|
|
||||||
|
|
||||||
margin-bottom: 20px;
|
.liveChat_textInput {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
|
||||||
width: 100%;
|
margin-bottom: 20px;
|
||||||
height: fit-content;
|
|
||||||
|
|
||||||
color: var(--text-color);
|
width: 100%;
|
||||||
|
height: fit-content;
|
||||||
|
|
||||||
.ant-input-textarea-show-count::after {
|
color: var(--text-color);
|
||||||
color: var(--text-color);
|
|
||||||
}
|
.ant-input-textarea-show-count::after {
|
||||||
}
|
color: var(--text-color);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.textRoom_line {
|
.textRoom_line {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|
||||||
padding: 5px 10px;
|
padding: 5px 7px;
|
||||||
margin-bottom: 10px;
|
border-radius: 12px;
|
||||||
|
|
||||||
border-radius: 12px;
|
background-color: rgba(var(--bg_color_1), 0.7);
|
||||||
|
|
||||||
background-color: var(--background-color-accent);
|
h1,
|
||||||
|
h2,
|
||||||
|
h3,
|
||||||
|
h4,
|
||||||
|
span {
|
||||||
|
margin: 0;
|
||||||
|
user-select: text;
|
||||||
|
}
|
||||||
|
|
||||||
h1,
|
.textRoom_line_user {
|
||||||
h2,
|
font-weight: bold;
|
||||||
h3,
|
font-size: 0.9rem;
|
||||||
h4,
|
|
||||||
span {
|
|
||||||
margin: 0;
|
|
||||||
user-select: text;
|
|
||||||
}
|
|
||||||
|
|
||||||
.textRoom_line_user {
|
color: var(--text-color);
|
||||||
font-weight: bold;
|
overflow: hidden;
|
||||||
font-size: 1rem;
|
|
||||||
color: var(--background-color-contrast);
|
|
||||||
|
|
||||||
overflow: hidden;
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
text-overflow: ellipsis;
|
.textRoom_line_content {
|
||||||
white-space: nowrap;
|
font-size: 0.8rem;
|
||||||
}
|
color: var(--text-color);
|
||||||
|
|
||||||
.textRoom_line_content {
|
padding: 0 10px;
|
||||||
font-size: 0.8rem;
|
|
||||||
color: var(--text-color);
|
|
||||||
|
|
||||||
padding: 0 10px;
|
word-break: break-all;
|
||||||
|
white-space: pre-wrap;
|
||||||
word-break: break-all;
|
}
|
||||||
white-space: pre-wrap;
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user