This commit is contained in:
mrfry 2021-05-29 21:44:08 +02:00
parent c60ace4f9b
commit 602e16046e
18 changed files with 512 additions and 174 deletions

View file

@ -2,6 +2,7 @@ import React, { useState } from 'react'
import ReactButton from './reactButton.js'
import Modal from './modal.js'
import Link from 'next/link'
import styles from './comments.module.css'
@ -61,7 +62,12 @@ function Comment({ comment, index, onComment, onDelete, onReact, uid }) {
>
{displayed ? '[-]' : '[+]'}
</div>
<div>User #{user}</div>
<Link href={`/chat?user=${user}`}>
<a title={`Chat #${user}-el`} className={'userId'}>
User #{user}
</a>
</Link>
<div className={styles.newsDate}> @ {date}</div>
</div>
<div className={styles.commentDate}>{date}</div>
</div>

View file

@ -75,6 +75,7 @@
.commentAreaContainer > textarea {
width: 99%;
font-size: 18px;
}
.commentAreaContainer > div {

View file

@ -4,12 +4,14 @@ import dynamic from 'next/dynamic'
const Snowfall = dynamic(() => import('react-snowfall'), { ssr: false })
import LogoutIcon from './logoutIcon.js'
import Modal from './modal.js'
import tabs from '../data/tabs.json'
import constants from '../constants.json'
import BB from './b.js'
import styles from './layout.module.css'
import tabs from '../data/tabs.json'
import topBarLinks from '../data/topBarLinks.json'
const renderSnow = () => {
const date = new Date()
@ -56,15 +58,19 @@ function Donate() {
}
function UserStatus({ userId, unreads }) {
const unreadCount = unreads ? unreads.length : 0
return (
<div className={styles.userStatus}>
<div className={'uid'} title="User ID">
UID: {userId || '...'}
</div>
<Link href="/chat">
<a className={styles.unreadNotification}>
<a
title={`Chat${unreadCount ? ' (' + unreadCount + ' új üzenet)' : ''}`}
className={styles.unreadNotification}
>
<span>💬</span>
{unreads && unreads.length > 0 ? <div>{unreads.length}</div> : null}
{unreadCount ? <div>{unreadCount}</div> : null}
</a>
</Link>
@ -83,7 +89,7 @@ function UserStatus({ userId, unreads }) {
location.reload()
}}
>
Logout
<LogoutIcon size={28} />
</div>
</div>
)
@ -165,15 +171,20 @@ export default function Layout({ children, router, globalData }) {
</a>
</Link>
<div className={styles.topBarLinks}>
<Link href="/contribute">
<a>Teendők</a>
</Link>
<Link href="/pwRequest">
<a>Jelszó kérés</a>
</Link>
<Link href="/ranklist">
<a>Ranklista</a>
{Object.keys(topBarLinks).map((key) => {
const item = topBarLinks[key]
return (
<Link key={key} href={item.href}>
<a
onClick={closeSideBar}
className={href.includes(key) ? styles.active : undefined}
>
{item.text}
</a>
</Link>
)
})}
</div>
<div className={'seperator'} />
<UserStatus unreads={unreads} userId={userId} />

View file

@ -7,6 +7,7 @@
width: 100%;
max-width: 1200px;
border-bottom: 1px solid var(--primary-color);
box-shadow: 0px 3px 3px -3px var(--primary-color);
height: 45px;
}
@ -19,16 +20,33 @@
border: none;
margin: 0px 15px;
}
@media screen and (max-width: 700px) {
.topBar img {
display: none;
}
.topBarLinks > *:nth-child(1) {
display: none;
}
.topBarLinks > *:nth-child(3) {
display: none;
}
}
.topBarLinks {
display: flex;
align-items: center;
align-items: stretch;
word-wrap: break-word;
}
.topBarLinks > * {
display: flex;
align-items: center;
border: 0.5px solid transparent;
text-align: center;
padding: 0px 10px;
padding: 0px 8px;
margin: 0px 2px;
text-decoration: none;
}
@ -37,6 +55,11 @@
border-radius: 5px;
}
.topBarLinks a.active {
border: 0.5px solid var(--text-color);
border-radius: 5px;
}
.sidebar {
top: 45px;
display: flex;
@ -54,6 +77,8 @@
width: 100%;
height: auto;
position: relative;
border-bottom: 1px solid var(--primary-color);
box-shadow: 0px 3px 3px -3px var(--primary-color);
}
.sidebar a {
@ -79,14 +104,13 @@
}
.sidebarLinks > a {
border: 0.5px solid transparent;
display: block;
border: 0.5px solid transparent;
text-align: center;
color: black;
font-size: 108%;
padding: 14px;
margin-top: 4px;
margin-bottom: 4px;
margin: 5px 2px;
text-decoration: none;
color: var(--bright-color);
transition: width 0.5s, height 0.5s, ease-in 0.5s;
@ -146,7 +170,7 @@
@media screen and (max-width: 700px) {
.menuicon div {
display: block;
margin: 6px 0;
margin: 6px;
width: 30px;
height: 5px;
background-color: var(--bright-color);
@ -198,13 +222,14 @@
.unreadNotification > span {
top: 8px;
right: -8px;
display: inline-block;
position: relative;
}
.unreadNotification > div {
display: inline-block;
right: 10px;
right: 5px;
top: 10px;
position: relative;
@ -216,7 +241,6 @@
@media screen and (max-width: 700px) {
.unreadNotification > div {
right: -10px;
top: -23px;
top: 10px;
}
}

View file

@ -0,0 +1,17 @@
import React from 'react'
export default function LogoutIcon({ size }) {
return (
<span style={{ margin: '4px', display: 'inline-block' }}>
<svg
width={size}
height={size}
xmlns="http://www.w3.org/2000/svg"
viewBox={`0 0 24 24`}
fill="white"
>
<path d="M4,12a1,1,0,0,0,1,1h7.59l-2.3,2.29a1,1,0,0,0,0,1.42,1,1,0,0,0,1.42,0l4-4a1,1,0,0,0,.21-.33,1,1,0,0,0,0-.76,1,1,0,0,0-.21-.33l-4-4a1,1,0,1,0-1.42,1.42L12.59,11H5A1,1,0,0,0,4,12ZM17,2H7A3,3,0,0,0,4,5V8A1,1,0,0,0,6,8V5A1,1,0,0,1,7,4H17a1,1,0,0,1,1,1V19a1,1,0,0,1-1,1H7a1,1,0,0,1-1-1V16a1,1,0,0,0-2,0v3a3,3,0,0,0,3,3H17a3,3,0,0,0,3-3V5A3,3,0,0,0,17,2Z" />
</svg>
</span>
)
}

View file

@ -2,6 +2,7 @@ import React from 'react'
import ReactButton from './reactButton.js'
import Comments from './comments.js'
import Link from 'next/link'
import styles from './newsEntry.module.css'
@ -28,7 +29,11 @@ export default function NewsEntry({
<div className={styles.newsHeader}>
<div className={styles.newsTitle}>{title}</div>
<div className={styles.userinfo}>
<div>User #{user}</div>
<Link href={`/chat?user=${user}`}>
<a title={`Chat #${user}-el`} className={'userId'}>
User #{user}
</a>
</Link>
<div className={styles.newsDate}> @ {date}</div>
</div>
</div>

View file

@ -6,7 +6,6 @@
.table {
display: flex;
min-width: 800px;
}
.categoryName {

View file

@ -34,9 +34,9 @@ export default function TodoCard(props) {
</span>
</div>
<div className={styles.numbers}>
<div className={`${voted && styles.voted}`}>
<div>{`Szavazatok: ${votes.length}`}</div>
</div>
<div
className={`${voted && styles.voted}`}
>{`Szavazatok: ${votes.length}`}</div>
<div>{`Nehézség: ${points}`}</div>
</div>
</div>

View file

@ -34,6 +34,7 @@
.numbers {
display: flex;
justify-content: space-between;
flex-wrap: wrap;
}
.numbers > div {

View file

@ -44,6 +44,7 @@
display: flex;
justify-content: space-between;
margin: 2px 10px;
flex-wrap: wrap;
}
.button {

14
src/data/topBarLinks.json Normal file
View file

@ -0,0 +1,14 @@
{
"contribute": {
"href": "/contribute",
"text": "Teendők"
},
"pwRequest": {
"href": "/pwRequest",
"text": "Jelszó kérés"
},
"ranklist": {
"href": "/ranklist",
"text": "Ranklista"
}
}

View file

@ -23,7 +23,6 @@ body {
font-family: 'Kameron', serif;
font-family: 'Overpass Mono', monospace;
color: #999999;
cursor: default;
}
#__next {
@ -234,7 +233,7 @@ input:focus {
width: 200px;
height: 30px;
padding: 5px 15px;
margin: 2px 5px;
margin: 2px 2px;
font-size: 15px;
color: var(--text-color);
@ -341,3 +340,9 @@ select:hover {
.seperator {
flex: 1 0;
}
.userId {
text-decoration: none;
font-weight: bold;
color: var(--text-color);
}

View file

@ -1,4 +1,5 @@
import React, { useState, useEffect } from 'react'
import Head from 'next/head'
import Layout from '../components/layout'
@ -35,6 +36,13 @@ function MyApp({ Component, pageProps, router }) {
setMotd(res.motd)
setUnreads(res.unreads)
})
.catch((err) => {
const res = { ...data }
setUserId(res.uid)
setMotd(res.motd)
console.error('Error getting unreads')
console.error(err)
})
})
}
@ -49,6 +57,10 @@ function MyApp({ Component, pageProps, router }) {
}
return (
<>
<Head>
<meta name="viewport" content="initial-scale=0.6, width=device-width" />
</Head>
<Layout
router={router}
globalData={globalData}
@ -61,6 +73,7 @@ function MyApp({ Component, pageProps, router }) {
refetchGlobalData={getGlobalProps}
/>
</Layout>
</>
)
}

View file

@ -10,12 +10,7 @@ class MyDocument extends Document {
render() {
return (
<Html>
<Head>
<meta
name="viewport"
content="initial-scale=0.6, width=device-width"
/>
</Head>
<Head />
<body bgcolor="#222426">
<Main />
<NextScript />

View file

@ -6,31 +6,36 @@ import constants from '../constants.json'
import LoadingIndicator from '../components/LoadingIndicator'
import styles from './chat.module.css'
const byDate = (a, b) => {
return a.date - b.date
}
function countAllMessages(msgs) {
return Object.keys(msgs).reduce((acc, key) => {
return acc + msgs[key].length
}, 0)
}
function groupMessages(msgs, currUser) {
function groupPrevMessages(msgs, currUser) {
return msgs.reduce((acc, msg) => {
const group =
parseInt(msg.sender) !== parseInt(currUser) ? msg.sender : msg.reciever
return {
...acc,
[group]: [msg],
[group]: {
msgs: [msg],
loaded: false,
lastLoaded: false,
lastMessage: msg,
},
}
}, {})
}
function addMsgsToGroup(msgGroup, msgs, user) {
let res = { ...msgGroup }
msgs.forEach((msg) => {
res = addMsgToGroup(
res,
msg.reciever === user ? { ...msg, unread: 0 } : msg,
user
)
msgs.reverse().forEach((msg) => {
res = addMsgToGroup(res, msg, user)
})
return res
}
@ -39,11 +44,24 @@ function addMsgToGroup(msgGroup, msg, user) {
const group =
parseInt(msg.sender) === parseInt(user) ? msg.reciever : msg.sender
if (!msgGroup[group]) {
msgGroup[group] = []
msgGroup[group] = { msgs: [], lastLoaded: true, loaded: true }
}
const currGroup = msgGroup[group]
return {
...msgGroup,
[group]: [...msgGroup[group], msg],
[group]: {
...currGroup,
loaded: true,
msgs: currGroup.loaded
? [...currGroup.msgs, msg].sort(byDate)
: [msg].sort(byDate),
lastMessage:
!currGroup.lastMessage || msg.date > currGroup.lastMessage.date
? msg
: currGroup.lastMessage,
},
}
}
@ -62,6 +80,28 @@ function NewMarker() {
)
}
function uploadFile(file) {
return new Promise((resolve) => {
const formData = new FormData() // eslint-disable-line
formData.append('file', file)
fetch(constants.apiUrl + 'postchatfile', {
method: 'POST',
credentials: 'include',
headers: {
Accept: 'application/json',
},
body: formData,
})
.then((res) => {
return res.json()
})
.then((res) => {
resolve(res)
})
})
}
export default class Chat extends React.Component {
constructor(props) {
super(props)
@ -77,16 +117,35 @@ export default class Chat extends React.Component {
}
if (props.globalData && !isNaN(props.globalData.userId)) {
this.state.user = props.globalData.userId
// this.connect(this.props.globalData.userId)
this.connect(this.props.globalData.userId)
}
this.router = props.router
}
componentDidUpdate(prevProps, prevState) {
try {
const user = this.props.router.query.user
if (this.state.userFromQuery !== user) {
if (isNaN(user)) {
this.setState({
userFromQuery: user,
})
} else {
this.setState({
selectedUser: user,
userFromQuery: user,
})
}
}
} catch (e) {
// e
}
if (!prevProps.globalData.userId && this.props.globalData.userId) {
this.setState({
user: this.props.globalData.userId,
})
// this.connect(this.props.globalData.userId)
this.connect(this.props.globalData.userId)
}
if (
@ -112,8 +171,16 @@ export default class Chat extends React.Component {
}
handleErrors(err) {
alert(err.message)
this.setState({
connected: false,
})
console.error(err)
alert(`Chat error: ${err.message}`)
try {
this.socket.disconnect()
} catch (e) {
console.warn(e)
}
}
connect(user) {
@ -141,23 +208,49 @@ export default class Chat extends React.Component {
const { prevMsgs } = data
const { user } = this.state
this.setState({
msgs: groupMessages(prevMsgs, user),
msgs: groupPrevMessages(prevMsgs, user),
connected: true,
})
})
socket.on('chat message read', (data) => {
const { userReadMsg } = data
this.partnerReadChatMessage(userReadMsg)
this.partnerSeenChatMessage(userReadMsg)
})
socket.on('chat message open', (data) => {
socket.on('get chat messages', (data) => {
const { requestsdMsgs, hasMore } = data
const { msgs, user, selectedUser } = this.state
if (msgs[selectedUser].length <= 1) {
this.setState({
msgs: addMsgsToGroup(msgs, data, user),
})
msgs: addMsgsToGroup(
Object.keys(msgs).reduce((acc, key) => {
const msgGroup = msgs[key]
if (parseInt(key) === selectedUser) {
acc[key] = {
...msgGroup,
lastLoaded: !hasMore,
msgs: msgGroup.msgs.map((msg) => {
return {
...msg,
unread: 0,
}
}),
}
} else {
acc[key] = msgGroup
}
return acc
}, {}),
requestsdMsgs.map((msg) => {
return {
...msg,
isFirstMessage: !hasMore,
}
}),
user
),
})
})
socket.on('chat message', (data) => {
@ -170,8 +263,8 @@ export default class Chat extends React.Component {
this.socket = socket
}
sendMsg() {
const { msgs, selectedUser, currMsg, user } = this.state
sendMsg(currMsg, type) {
const { msgs, selectedUser, user } = this.state
if (!currMsg) {
return
}
@ -182,6 +275,7 @@ export default class Chat extends React.Component {
sender: user,
date: new Date().getTime(),
unread: 1,
type: type || 'text',
}
this.socket.emit('chat message', msg)
this.setState({
@ -191,12 +285,15 @@ export default class Chat extends React.Component {
}
}
partnerReadChatMessage(chatPartner) {
partnerSeenChatMessage(chatPartner) {
const { msgs } = this.state
this.setState({
msgs: {
...msgs,
[chatPartner]: msgs[chatPartner].map((msg) => {
[chatPartner]: {
...msgs[chatPartner],
lastMessage: { ...msgs[chatPartner].lastMessage, unread: 0 },
msgs: msgs[chatPartner].msgs.map((msg) => {
if (msg.reciever === chatPartner) {
return {
...msg,
@ -207,10 +304,12 @@ export default class Chat extends React.Component {
}
}),
},
},
})
this.scrollToChatBottom()
}
chatMessageRead(chatPartner) {
chatMessageSeen(chatPartner) {
const { msgs, user } = this.state
if (this.props.refetchGlobalData) {
this.props.refetchGlobalData()
@ -219,7 +318,10 @@ export default class Chat extends React.Component {
this.setState({
msgs: {
...msgs,
[chatPartner]: msgs[chatPartner].map((msg) => {
[chatPartner]: {
...msgs[chatPartner],
lastMessage: { ...msgs[chatPartner].lastMessage, unread: 0 },
msgs: msgs[chatPartner].msgs.map((msg) => {
if (msg.reciever === user) {
return {
...msg,
@ -230,20 +332,21 @@ export default class Chat extends React.Component {
}
}),
},
},
})
}
selectedUserChange(val) {
const { msgs, selectedUser, user } = this.state
const prevLastMessage = msgs[selectedUser]
? msgs[selectedUser][msgs[selectedUser].length - 1]
? msgs[selectedUser].lastMessage
: null
if (
prevLastMessage &&
prevLastMessage.unread === 1 &&
prevLastMessage.sender !== user
) {
this.chatMessageRead(selectedUser)
this.chatMessageSeen(selectedUser)
}
this.setState({
selectedUser: val,
@ -255,14 +358,21 @@ export default class Chat extends React.Component {
if (!currSelectedMsgs) {
return
}
if (msgs[val].length <= 1) {
this.socket.emit('chat message open', {
if (!msgs[val].loaded) {
this.socket.emit('get chat messages', {
chatPartner: val,
})
}
const lastMessage = currSelectedMsgs[currSelectedMsgs.length - 1]
const lastMessage = currSelectedMsgs.lastMessage
if (lastMessage.unread === 1 && lastMessage.sender !== user) {
this.chatMessageRead(val)
this.chatMessageSeen(val)
}
try {
this.router.push(`${this.router.pathname}`, undefined, {
shallow: true,
})
} catch (e) {
// e
}
}
@ -270,6 +380,38 @@ export default class Chat extends React.Component {
const { currMsg, msgs, selectedUser, user } = this.state
return (
<div className={styles.chatInput}>
<div>
<input
onChange={(e) => {
const file = e.target.files[0]
const isImage = ['png', 'jpg', 'jpeg', 'gif'].some((ext) => {
return file.name.toLowerCase().includes(ext)
})
uploadFile(file).then((res) => {
const { path, success } = res
if (success) {
this.sendMsg(
`${constants.apiUrl}${path}`,
isImage ? 'img' : 'file'
)
} else {
alert('Error uploading image :/')
console.error(res)
}
})
}}
type="file"
id="actual-btn"
style={{ display: 'none' }}
/>
<label
className={styles.file}
htmlFor="actual-btn"
onClick={() => {}}
>
📂
</label>
</div>
<input
autoFocus
placeholder={'Message ...'}
@ -278,17 +420,17 @@ export default class Chat extends React.Component {
tabIndex={0}
onKeyUp={(e) => {
const lastMessage = msgs[selectedUser]
? msgs[selectedUser][msgs[selectedUser].length - 1]
? msgs[selectedUser].lastMessage
: null
if (
lastMessage &&
lastMessage.unread === 1 &&
lastMessage.sender !== user
) {
this.chatMessageRead(selectedUser)
this.chatMessageSeen(selectedUser)
}
if (e.key === 'Enter') {
this.sendMsg()
this.sendMsg(currMsg)
}
}}
onChange={(e) => {
@ -300,10 +442,10 @@ export default class Chat extends React.Component {
<div className={`buttonContainer ${styles.sendButton}`}>
<div
onClick={() => {
this.sendMsg()
this.sendMsg(currMsg)
}}
>
Send
Küldés
</div>
</div>
</div>
@ -336,8 +478,8 @@ export default class Chat extends React.Component {
alert('Érvényes User ID-t adj meg! (számot)')
return
}
this.selectedUserChange(userInputVal)
this.setState({
selectedUser: userInputVal,
userInputVal: null,
})
}}
@ -345,68 +487,113 @@ export default class Chat extends React.Component {
Chat!
</div>
</div>
<i>Admin User ID-ja: {'"1"'}</i>
<i style={{ fontSize: '12px' }}>Admin User ID-ja: {'"1"'}</i>
</div>
)
}
renderMsgs() {
const { msgs, selectedUser } = this.state
const { msgs, selectedUser, user } = this.state
const selectedMsgs = msgs[selectedUser].msgs
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 (
<>
{this.renderLoadMore()}
{selectedMsgs.reduce((acc, currMessage, i) => {
const { date, sender, unread } = currMessage
const timeString = new Date(date).toLocaleString()
if (prevMsg && prevMsg.unread === 0 && unread === 1) {
if (sender === selectedUser) {
acc.push(<NewMarker key={`marker_${i}`} />)
} else if (prevMsg.sender !== selectedUser) {
acc.push(<SeenMarker key={`marker_${i}`} />)
}
}
acc.push(
<div
key={key}
key={i}
title={timeString}
className={`${styles.messageContainer} ${
sender === user ? styles.ownMsg : styles.partnerMsg
}`}
>
<div className={`${styles.messageEntry}`}>{msg}</div>
{this.renderMsg(currMessage, i)}
</div>
)
if (i === selectedMsgs.length - 1 && unread === 0) {
if (sender !== selectedUser) {
acc.push(<SeenMarker key={`marker_${i}`} />)
}
}
prevMsg = currMessage
return acc
}, [])}
</>
)
}
renderMsg(message, key) {
const { msg, type } = message
if (type === 'text') {
return <div className={`${styles.messageEntry}`}>{msg}</div>
} else if (type === 'img') {
return (
<a key={key} href={msg} rel="noreferrer" target="_blank">
<img src={msg} />
</a>
)
} else if (type === 'file') {
return (
<a key={key} href={msg} rel="noreferrer" target="_blank">
{msg.split('/').slice(-1)}
</a>
)
} else {
console.error(message)
return <div key={key}>Invalid msg type {type}</div>
}
}
renderLoadMore() {
const { selectedUser, msgs } = this.state
const group = msgs[selectedUser]
const firstMessage = group.msgs[0]
if (group.lastLoaded) {
return (
<div className={styles.loadMore}>
<div>Beszélgetés eleje</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)
return (
<div
className={`${styles.loadMore} ${styles.loadMoreActive}`}
onClick={() => {
this.socket.emit('get chat messages', {
chatPartner: selectedUser,
from: firstMessage.date,
})
}}
>
<div>Több betöltése ...</div>
</div>
)
}
renderSidebarEntry(i, key) {
const { selectedUser, msgs, user } = this.state
const group = msgs[key]
const lastMessage = group[group.length - 1]
const lastMessage = group.lastMessage
const lastMsgDate = new Date(group.lastMessage.date)
const date =
lastMsgDate.getDate() === new Date().getDate()
? lastMsgDate.toLocaleTimeString()
: lastMsgDate.toLocaleDateString()
return (
<div
@ -420,19 +607,34 @@ export default class Chat extends React.Component {
}}
key={i}
>
<div>#{key}</div>
<div>{lastMessage.msg}</div>
<div>
<b>#{key}</b>
<div style={{ fontSize: '12px' }}>{date}</div>
</div>
<div>
{lastMessage.type === 'text' ? lastMessage.msg : 'Csatolt fájl'}
</div>
</div>
)
}
renderSidebarEntryes() {
const { msgs } = this.state
const sorted = Object.keys(msgs).sort((a, b) => {
return msgs[b].lastMessage.date - msgs[a].lastMessage.date
})
return sorted.map((key, i) => {
return this.renderSidebarEntry(i, key)
})
}
render() {
const { user, msgs, connected, selectedUser } = this.state
const { msgs, connected, selectedUser } = this.state
return (
<div className={styles.chat}>
<Head>
<title>{`Connected as ${user}`}</title>
<title>Chat - Qmining | Frylabs.net</title>
</Head>
{connected ? (
<>
@ -466,24 +668,8 @@ export default class Chat extends React.Component {
</div>
</>
) : (
<div>
<div className={styles.loading}>
<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>

View file

@ -14,7 +14,7 @@
display: flex;
flex-direction: column;
width: calc(20vw);
width: 200px;
z-index: 1;
right: 0;
overflow-x: hidden;
@ -30,6 +30,10 @@
user-select: none;
}
.usersContainer > div:first-child {
margin-top: 5px;
}
.usersContainer > div {
cursor: pointer;
}
@ -65,6 +69,12 @@
text-overflow: ellipsis;
}
.group > * {
display: flex;
justify-content: space-between;
align-items: center;
}
.group:hover {
background-color: var(--hoover-color);
}
@ -112,6 +122,7 @@
.chatInput > input {
padding: 2px;
height: 30px;
margin: 0px 5px;
}
.messageContainer {
@ -121,6 +132,13 @@
max-width: 300px;
}
.messageContainer img {
max-width: 100%;
width: 100%;
height: auto;
border-radius: 5px;
}
.ownMsg {
align-self: flex-end;
justify-content: flex-end;
@ -139,11 +157,13 @@
justify-content: flex-end;
font-size: 14px;
padding: 0px 5px;
font-style: italic;
}
.newMarker {
font-size: 14px;
padding: 0px 5px;
font-style: italic;
}
.messageEntry {
@ -170,3 +190,31 @@
text-align: center;
width: 300px;
}
.loading {
display: flex;
justify-content: center;
align-items: center;
width: 100%;
height: 100%;
}
.loadMore {
user-select: none;
margin: 5px 0px;
display: flex;
justify-content: center;
align-items: center;
}
.loadMoreActive:hover {
background-color: var(--hoover-color);
border-radius: 3px;
cursor: pointer;
}
.file {
font-size: 34px;
margin: 0px 5px;
cursor: pointer;
}

View file

@ -1,7 +1,7 @@
.container {
display: flex;
flex-flow: column;
height: calc(98vh);
height: calc(94vh);
cursor: default;
}
@ -49,23 +49,27 @@
}
.row div,
.headerRow div, .sumRow div {
.headerRow div,
.sumRow div {
flex: 1;
padding: 0px 5px;
text-align: center;
}
.headerRow > div, .sumRow > div {
.headerRow > div,
.sumRow > div {
padding: 5px;
}
.row :nth-child(1),
.headerRow :nth-child(1), .sumRow :nth-child(1) {
.headerRow :nth-child(1),
.sumRow :nth-child(1) {
flex: 0 50px;
}
.row :nth-child(2),
.headerRow :nth-child(2), .sumRow :nth-child(2) {
.headerRow :nth-child(2),
.sumRow :nth-child(2) {
flex: 0 130px;
text-align: left;
}

View file

@ -405,7 +405,15 @@ export default function UserFiles({ router, globalData }) {
Törlés
</div>
) : (
`#${user}`
<Link href={`/chat?user=${user}`}>
<a
title={`Chat #${user}-el`}
onClick={(e) => {
e.stopPropagation()
}}
className={'userId'}
>{`#${user}`}</a>
</Link>
))}
</div>
</div>