mirror of
https://gitlab.com/MrFry/qmining-page
synced 2025-04-01 20:23:44 +02:00
Added chat page
This commit is contained in:
parent
3a67f2a1aa
commit
c60ace4f9b
2 changed files with 664 additions and 0 deletions
492
src/pages/chat.js
Normal file
492
src/pages/chat.js
Normal file
|
@ -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 (
|
||||||
|
<span className={styles.unreadMarker} key={`unread_marker`}>
|
||||||
|
Látta
|
||||||
|
</span>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
function NewMarker() {
|
||||||
|
return (
|
||||||
|
<span className={styles.newMarker} key={`unread_marker`}>
|
||||||
|
Új üzenet
|
||||||
|
</span>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
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 (
|
||||||
|
<div className={styles.chatInput}>
|
||||||
|
<input
|
||||||
|
autoFocus
|
||||||
|
placeholder={'Message ...'}
|
||||||
|
type={'text'}
|
||||||
|
value={currMsg}
|
||||||
|
tabIndex={0}
|
||||||
|
onKeyUp={(e) => {
|
||||||
|
const lastMessage = msgs[selectedUser]
|
||||||
|
? msgs[selectedUser][msgs[selectedUser].length - 1]
|
||||||
|
: null
|
||||||
|
if (
|
||||||
|
lastMessage &&
|
||||||
|
lastMessage.unread === 1 &&
|
||||||
|
lastMessage.sender !== user
|
||||||
|
) {
|
||||||
|
this.chatMessageRead(selectedUser)
|
||||||
|
}
|
||||||
|
if (e.key === 'Enter') {
|
||||||
|
this.sendMsg()
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
onChange={(e) => {
|
||||||
|
this.setState({
|
||||||
|
currMsg: e.target.value,
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<div className={`buttonContainer ${styles.sendButton}`}>
|
||||||
|
<div
|
||||||
|
onClick={() => {
|
||||||
|
this.sendMsg()
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Send
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
renderHome() {
|
||||||
|
return (
|
||||||
|
<div className={styles.home}>
|
||||||
|
<div>
|
||||||
|
<input
|
||||||
|
autoFocus
|
||||||
|
type={'text'}
|
||||||
|
onChange={(e) => {
|
||||||
|
const val = parseInt(e.target.value)
|
||||||
|
if (!isNaN(val)) {
|
||||||
|
this.setState({
|
||||||
|
userInputVal: val,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
placeholder={'Címzett User ID-ja ...'}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className={'buttonContainer'}>
|
||||||
|
<div
|
||||||
|
onClick={() => {
|
||||||
|
const { userInputVal } = this.state
|
||||||
|
if (isNaN(userInputVal) || userInputVal <= 0) {
|
||||||
|
alert('Érvényes User ID-t adj meg! (számot)')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.setState({
|
||||||
|
selectedUser: userInputVal,
|
||||||
|
userInputVal: null,
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Chat!
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<i>Admin User ID-ja: {'"1"'}</i>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
renderMsgs() {
|
||||||
|
const { msgs, selectedUser } = this.state
|
||||||
|
|
||||||
|
let prevMsg
|
||||||
|
return msgs[selectedUser].reduce((acc, msg, i) => {
|
||||||
|
if (prevMsg && prevMsg.unread === 0 && msg.unread === 1) {
|
||||||
|
if (msg.sender === selectedUser) {
|
||||||
|
acc.push(<NewMarker key={`marker_${i}`} />)
|
||||||
|
} else {
|
||||||
|
acc.push(<SeenMarker key={`marker_${i}`} />)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
acc.push(this.renderMsg(msg, i))
|
||||||
|
prevMsg = msg
|
||||||
|
if (i === msgs[selectedUser].length - 1 && msg.unread === 0) {
|
||||||
|
if (msg.sender !== selectedUser) {
|
||||||
|
acc.push(<SeenMarker key={`marker_${i}`} />)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return acc
|
||||||
|
}, [])
|
||||||
|
}
|
||||||
|
|
||||||
|
renderMsg(message, key) {
|
||||||
|
const { date, sender, msg /* reciever */ } = message
|
||||||
|
const { user } = this.state
|
||||||
|
const timeString = new Date(date).toLocaleString()
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
key={key}
|
||||||
|
title={timeString}
|
||||||
|
className={`${styles.messageContainer} ${
|
||||||
|
sender === user ? styles.ownMsg : styles.partnerMsg
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
<div className={`${styles.messageEntry}`}>{msg}</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
renderSidebarEntryes() {
|
||||||
|
const { msgs } = this.state
|
||||||
|
const sorted = Object.keys(msgs).sort((a, b) => {
|
||||||
|
const lastA = msgs[a].slice(-1)[0]
|
||||||
|
const lastB = msgs[b].slice(-1)[0]
|
||||||
|
return lastB.date - lastA.date
|
||||||
|
})
|
||||||
|
return sorted.map((key, i) => {
|
||||||
|
return this.renderSidebarEntry(i, key)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
renderSidebarEntry(i, key) {
|
||||||
|
const { selectedUser, msgs, user } = this.state
|
||||||
|
const group = msgs[key]
|
||||||
|
const lastMessage = group[group.length - 1]
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={`${styles.group} ${
|
||||||
|
lastMessage.unread === 1 && lastMessage.sender !== user
|
||||||
|
? styles.unread
|
||||||
|
: ''
|
||||||
|
} ${selectedUser === parseInt(key) ? styles.activeSidebarItem : ''}`}
|
||||||
|
onClick={() => {
|
||||||
|
this.selectedUserChange(parseInt(key))
|
||||||
|
}}
|
||||||
|
key={i}
|
||||||
|
>
|
||||||
|
<div>#{key}</div>
|
||||||
|
<div>{lastMessage.msg}</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { user, msgs, connected, selectedUser } = this.state
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={styles.chat}>
|
||||||
|
<Head>
|
||||||
|
<title>{`Connected as ${user}`}</title>
|
||||||
|
</Head>
|
||||||
|
{connected ? (
|
||||||
|
<>
|
||||||
|
<div className={styles.main}>
|
||||||
|
<div className={styles.header}>
|
||||||
|
{selectedUser ? `User #${selectedUser}` : ''}
|
||||||
|
</div>
|
||||||
|
<div className={styles.messages} id={'messages'}>
|
||||||
|
{selectedUser === 0 ? this.renderHome() : null}
|
||||||
|
{selectedUser && msgs[selectedUser] ? this.renderMsgs() : null}
|
||||||
|
</div>
|
||||||
|
{selectedUser !== 0 ? this.renderChatInput() : null}
|
||||||
|
</div>
|
||||||
|
<div className={styles.sidebar}>
|
||||||
|
<div className={styles.usersContainer}>
|
||||||
|
<div
|
||||||
|
className={`${styles.group} ${
|
||||||
|
selectedUser === 0 ? styles.activeSidebarItem : ''
|
||||||
|
}`}
|
||||||
|
onClick={() => {
|
||||||
|
this.selectedUserChange(0)
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div>Új beszélgetés</div>
|
||||||
|
</div>
|
||||||
|
<hr />
|
||||||
|
{msgs && this.renderSidebarEntryes()}
|
||||||
|
</div>
|
||||||
|
<span className={styles.spacer} />
|
||||||
|
<div></div>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<div>
|
||||||
|
<LoadingIndicator />
|
||||||
|
<input
|
||||||
|
onChange={(e) => {
|
||||||
|
this.setState({
|
||||||
|
user: parseInt(e.target.value),
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
type={'text'}
|
||||||
|
placeholder={'user'}
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
onClick={() => {
|
||||||
|
this.connect(this.state.user)
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
connect
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
172
src/pages/chat.module.css
Normal file
172
src/pages/chat.module.css
Normal file
|
@ -0,0 +1,172 @@
|
||||||
|
.chat {
|
||||||
|
display: flex;
|
||||||
|
align-items: stretch;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main {
|
||||||
|
display: flex;
|
||||||
|
flex-flow: column;
|
||||||
|
flex: 1 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
width: calc(20vw);
|
||||||
|
z-index: 1;
|
||||||
|
right: 0;
|
||||||
|
overflow-x: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar > div:last-child {
|
||||||
|
text-align: center;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.usersContainer {
|
||||||
|
padding: 0px 5px;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.usersContainer > div {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.spacer {
|
||||||
|
flex: 1 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.new {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-self: center;
|
||||||
|
margin: 5px;
|
||||||
|
width: 140px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.new > input {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.group {
|
||||||
|
padding: 15px;
|
||||||
|
cursor: pointer;
|
||||||
|
user-select: none;
|
||||||
|
|
||||||
|
transition: width 0.5s, height 0.5s, ease-in 0.5s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.group > div {
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
.group:hover {
|
||||||
|
background-color: var(--hoover-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.activeSidebarItem {
|
||||||
|
color: black;
|
||||||
|
background-color: var(--primary-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.activeSidebarItem:hover {
|
||||||
|
background-color: var(--primary-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.unread {
|
||||||
|
color: var(--text-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
color: var(--text-color);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 10px 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.messages {
|
||||||
|
display: flex;
|
||||||
|
flex-flow: column;
|
||||||
|
word-break: break-all;
|
||||||
|
padding: 5px 0px;
|
||||||
|
|
||||||
|
overflow-x: none;
|
||||||
|
overflow-y: auto;
|
||||||
|
flex: 1 0 200px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chatInput {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 4px 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chatInput > input {
|
||||||
|
padding: 2px;
|
||||||
|
height: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.messageContainer {
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
margin: 2px 10px;
|
||||||
|
max-width: 300px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ownMsg {
|
||||||
|
align-self: flex-end;
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ownMsg > div {
|
||||||
|
background-color: #99f;
|
||||||
|
}
|
||||||
|
|
||||||
|
.partnerMsg > div {
|
||||||
|
background-color: var(--primary-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.unreadMarker {
|
||||||
|
align-self: flex-end;
|
||||||
|
justify-content: flex-end;
|
||||||
|
font-size: 14px;
|
||||||
|
padding: 0px 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.newMarker {
|
||||||
|
font-size: 14px;
|
||||||
|
padding: 0px 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.messageEntry {
|
||||||
|
padding: 5px 8px;
|
||||||
|
border-radius: 4px;
|
||||||
|
color: black;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sendButton {
|
||||||
|
width: 90px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.home {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.home input {
|
||||||
|
text-align: center;
|
||||||
|
width: 300px;
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue