mirror of
https://gitlab.com/MrFry/qmining-page
synced 2025-04-01 20:23:44 +02:00
Chat
This commit is contained in:
parent
c60ace4f9b
commit
602e16046e
18 changed files with 512 additions and 174 deletions
|
@ -2,6 +2,7 @@ import React, { useState } from 'react'
|
||||||
|
|
||||||
import ReactButton from './reactButton.js'
|
import ReactButton from './reactButton.js'
|
||||||
import Modal from './modal.js'
|
import Modal from './modal.js'
|
||||||
|
import Link from 'next/link'
|
||||||
|
|
||||||
import styles from './comments.module.css'
|
import styles from './comments.module.css'
|
||||||
|
|
||||||
|
@ -61,7 +62,12 @@ function Comment({ comment, index, onComment, onDelete, onReact, uid }) {
|
||||||
>
|
>
|
||||||
{displayed ? '[-]' : '[+]'}
|
{displayed ? '[-]' : '[+]'}
|
||||||
</div>
|
</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>
|
||||||
<div className={styles.commentDate}>{date}</div>
|
<div className={styles.commentDate}>{date}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -75,6 +75,7 @@
|
||||||
|
|
||||||
.commentAreaContainer > textarea {
|
.commentAreaContainer > textarea {
|
||||||
width: 99%;
|
width: 99%;
|
||||||
|
font-size: 18px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.commentAreaContainer > div {
|
.commentAreaContainer > div {
|
||||||
|
|
|
@ -4,12 +4,14 @@ import dynamic from 'next/dynamic'
|
||||||
|
|
||||||
const Snowfall = dynamic(() => import('react-snowfall'), { ssr: false })
|
const Snowfall = dynamic(() => import('react-snowfall'), { ssr: false })
|
||||||
|
|
||||||
|
import LogoutIcon from './logoutIcon.js'
|
||||||
import Modal from './modal.js'
|
import Modal from './modal.js'
|
||||||
import tabs from '../data/tabs.json'
|
|
||||||
import constants from '../constants.json'
|
import constants from '../constants.json'
|
||||||
import BB from './b.js'
|
import BB from './b.js'
|
||||||
|
|
||||||
import styles from './layout.module.css'
|
import styles from './layout.module.css'
|
||||||
|
import tabs from '../data/tabs.json'
|
||||||
|
import topBarLinks from '../data/topBarLinks.json'
|
||||||
|
|
||||||
const renderSnow = () => {
|
const renderSnow = () => {
|
||||||
const date = new Date()
|
const date = new Date()
|
||||||
|
@ -56,15 +58,19 @@ function Donate() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function UserStatus({ userId, unreads }) {
|
function UserStatus({ userId, unreads }) {
|
||||||
|
const unreadCount = unreads ? unreads.length : 0
|
||||||
return (
|
return (
|
||||||
<div className={styles.userStatus}>
|
<div className={styles.userStatus}>
|
||||||
<div className={'uid'} title="User ID">
|
<div className={'uid'} title="User ID">
|
||||||
UID: {userId || '...'}
|
UID: {userId || '...'}
|
||||||
</div>
|
</div>
|
||||||
<Link href="/chat">
|
<Link href="/chat">
|
||||||
<a className={styles.unreadNotification}>
|
<a
|
||||||
|
title={`Chat${unreadCount ? ' (' + unreadCount + ' új üzenet)' : ''}`}
|
||||||
|
className={styles.unreadNotification}
|
||||||
|
>
|
||||||
<span>💬</span>
|
<span>💬</span>
|
||||||
{unreads && unreads.length > 0 ? <div>{unreads.length}</div> : null}
|
{unreadCount ? <div>{unreadCount}</div> : null}
|
||||||
</a>
|
</a>
|
||||||
</Link>
|
</Link>
|
||||||
|
|
||||||
|
@ -83,7 +89,7 @@ function UserStatus({ userId, unreads }) {
|
||||||
location.reload()
|
location.reload()
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Logout
|
<LogoutIcon size={28} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
@ -165,15 +171,20 @@ export default function Layout({ children, router, globalData }) {
|
||||||
</a>
|
</a>
|
||||||
</Link>
|
</Link>
|
||||||
<div className={styles.topBarLinks}>
|
<div className={styles.topBarLinks}>
|
||||||
<Link href="/contribute">
|
{Object.keys(topBarLinks).map((key) => {
|
||||||
<a>Teendők</a>
|
const item = topBarLinks[key]
|
||||||
</Link>
|
|
||||||
<Link href="/pwRequest">
|
return (
|
||||||
<a>Jelszó kérés</a>
|
<Link key={key} href={item.href}>
|
||||||
</Link>
|
<a
|
||||||
<Link href="/ranklist">
|
onClick={closeSideBar}
|
||||||
<a>Ranklista</a>
|
className={href.includes(key) ? styles.active : undefined}
|
||||||
</Link>
|
>
|
||||||
|
{item.text}
|
||||||
|
</a>
|
||||||
|
</Link>
|
||||||
|
)
|
||||||
|
})}
|
||||||
</div>
|
</div>
|
||||||
<div className={'seperator'} />
|
<div className={'seperator'} />
|
||||||
<UserStatus unreads={unreads} userId={userId} />
|
<UserStatus unreads={unreads} userId={userId} />
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
width: 100%;
|
width: 100%;
|
||||||
max-width: 1200px;
|
max-width: 1200px;
|
||||||
border-bottom: 1px solid var(--primary-color);
|
border-bottom: 1px solid var(--primary-color);
|
||||||
|
box-shadow: 0px 3px 3px -3px var(--primary-color);
|
||||||
|
|
||||||
height: 45px;
|
height: 45px;
|
||||||
}
|
}
|
||||||
|
@ -19,16 +20,33 @@
|
||||||
border: none;
|
border: none;
|
||||||
margin: 0px 15px;
|
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 {
|
.topBarLinks {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: stretch;
|
||||||
word-wrap: break-word;
|
word-wrap: break-word;
|
||||||
}
|
}
|
||||||
|
|
||||||
.topBarLinks > * {
|
.topBarLinks > * {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
border: 0.5px solid transparent;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
padding: 0px 10px;
|
padding: 0px 8px;
|
||||||
|
margin: 0px 2px;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -37,6 +55,11 @@
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.topBarLinks a.active {
|
||||||
|
border: 0.5px solid var(--text-color);
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
.sidebar {
|
.sidebar {
|
||||||
top: 45px;
|
top: 45px;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
@ -54,6 +77,8 @@
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: auto;
|
height: auto;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
border-bottom: 1px solid var(--primary-color);
|
||||||
|
box-shadow: 0px 3px 3px -3px var(--primary-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.sidebar a {
|
.sidebar a {
|
||||||
|
@ -79,14 +104,13 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.sidebarLinks > a {
|
.sidebarLinks > a {
|
||||||
border: 0.5px solid transparent;
|
|
||||||
display: block;
|
display: block;
|
||||||
|
border: 0.5px solid transparent;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
color: black;
|
color: black;
|
||||||
font-size: 108%;
|
font-size: 108%;
|
||||||
padding: 14px;
|
padding: 14px;
|
||||||
margin-top: 4px;
|
margin: 5px 2px;
|
||||||
margin-bottom: 4px;
|
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
color: var(--bright-color);
|
color: var(--bright-color);
|
||||||
transition: width 0.5s, height 0.5s, ease-in 0.5s;
|
transition: width 0.5s, height 0.5s, ease-in 0.5s;
|
||||||
|
@ -146,7 +170,7 @@
|
||||||
@media screen and (max-width: 700px) {
|
@media screen and (max-width: 700px) {
|
||||||
.menuicon div {
|
.menuicon div {
|
||||||
display: block;
|
display: block;
|
||||||
margin: 6px 0;
|
margin: 6px;
|
||||||
width: 30px;
|
width: 30px;
|
||||||
height: 5px;
|
height: 5px;
|
||||||
background-color: var(--bright-color);
|
background-color: var(--bright-color);
|
||||||
|
@ -198,13 +222,14 @@
|
||||||
|
|
||||||
.unreadNotification > span {
|
.unreadNotification > span {
|
||||||
top: 8px;
|
top: 8px;
|
||||||
|
right: -8px;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
.unreadNotification > div {
|
.unreadNotification > div {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
right: 10px;
|
right: 5px;
|
||||||
top: 10px;
|
top: 10px;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
|
@ -216,7 +241,6 @@
|
||||||
|
|
||||||
@media screen and (max-width: 700px) {
|
@media screen and (max-width: 700px) {
|
||||||
.unreadNotification > div {
|
.unreadNotification > div {
|
||||||
right: -10px;
|
top: 10px;
|
||||||
top: -23px;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
17
src/components/logoutIcon.js
Normal file
17
src/components/logoutIcon.js
Normal 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>
|
||||||
|
)
|
||||||
|
}
|
|
@ -2,6 +2,7 @@ import React from 'react'
|
||||||
|
|
||||||
import ReactButton from './reactButton.js'
|
import ReactButton from './reactButton.js'
|
||||||
import Comments from './comments.js'
|
import Comments from './comments.js'
|
||||||
|
import Link from 'next/link'
|
||||||
|
|
||||||
import styles from './newsEntry.module.css'
|
import styles from './newsEntry.module.css'
|
||||||
|
|
||||||
|
@ -28,7 +29,11 @@ export default function NewsEntry({
|
||||||
<div className={styles.newsHeader}>
|
<div className={styles.newsHeader}>
|
||||||
<div className={styles.newsTitle}>{title}</div>
|
<div className={styles.newsTitle}>{title}</div>
|
||||||
<div className={styles.userinfo}>
|
<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 className={styles.newsDate}> @ {date}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -6,7 +6,6 @@
|
||||||
|
|
||||||
.table {
|
.table {
|
||||||
display: flex;
|
display: flex;
|
||||||
min-width: 800px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.categoryName {
|
.categoryName {
|
||||||
|
|
|
@ -34,9 +34,9 @@ export default function TodoCard(props) {
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div className={styles.numbers}>
|
<div className={styles.numbers}>
|
||||||
<div className={`${voted && styles.voted}`}>
|
<div
|
||||||
<div>{`Szavazatok: ${votes.length}`}</div>
|
className={`${voted && styles.voted}`}
|
||||||
</div>
|
>{`Szavazatok: ${votes.length}`}</div>
|
||||||
<div>{`Nehézség: ${points}`}</div>
|
<div>{`Nehézség: ${points}`}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -34,6 +34,7 @@
|
||||||
.numbers {
|
.numbers {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
|
flex-wrap: wrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
.numbers > div {
|
.numbers > div {
|
||||||
|
|
|
@ -44,6 +44,7 @@
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
margin: 2px 10px;
|
margin: 2px 10px;
|
||||||
|
flex-wrap: wrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
.button {
|
.button {
|
||||||
|
|
14
src/data/topBarLinks.json
Normal file
14
src/data/topBarLinks.json
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
{
|
||||||
|
"contribute": {
|
||||||
|
"href": "/contribute",
|
||||||
|
"text": "Teendők"
|
||||||
|
},
|
||||||
|
"pwRequest": {
|
||||||
|
"href": "/pwRequest",
|
||||||
|
"text": "Jelszó kérés"
|
||||||
|
},
|
||||||
|
"ranklist": {
|
||||||
|
"href": "/ranklist",
|
||||||
|
"text": "Ranklista"
|
||||||
|
}
|
||||||
|
}
|
|
@ -23,7 +23,6 @@ body {
|
||||||
font-family: 'Kameron', serif;
|
font-family: 'Kameron', serif;
|
||||||
font-family: 'Overpass Mono', monospace;
|
font-family: 'Overpass Mono', monospace;
|
||||||
color: #999999;
|
color: #999999;
|
||||||
cursor: default;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#__next {
|
#__next {
|
||||||
|
@ -234,7 +233,7 @@ input:focus {
|
||||||
width: 200px;
|
width: 200px;
|
||||||
height: 30px;
|
height: 30px;
|
||||||
padding: 5px 15px;
|
padding: 5px 15px;
|
||||||
margin: 2px 5px;
|
margin: 2px 2px;
|
||||||
|
|
||||||
font-size: 15px;
|
font-size: 15px;
|
||||||
color: var(--text-color);
|
color: var(--text-color);
|
||||||
|
@ -341,3 +340,9 @@ select:hover {
|
||||||
.seperator {
|
.seperator {
|
||||||
flex: 1 0;
|
flex: 1 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.userId {
|
||||||
|
text-decoration: none;
|
||||||
|
font-weight: bold;
|
||||||
|
color: var(--text-color);
|
||||||
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import React, { useState, useEffect } from 'react'
|
import React, { useState, useEffect } from 'react'
|
||||||
|
import Head from 'next/head'
|
||||||
|
|
||||||
import Layout from '../components/layout'
|
import Layout from '../components/layout'
|
||||||
|
|
||||||
|
@ -35,6 +36,13 @@ function MyApp({ Component, pageProps, router }) {
|
||||||
setMotd(res.motd)
|
setMotd(res.motd)
|
||||||
setUnreads(res.unreads)
|
setUnreads(res.unreads)
|
||||||
})
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
const res = { ...data }
|
||||||
|
setUserId(res.uid)
|
||||||
|
setMotd(res.motd)
|
||||||
|
console.error('Error getting unreads')
|
||||||
|
console.error(err)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -49,18 +57,23 @@ function MyApp({ Component, pageProps, router }) {
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Layout
|
<>
|
||||||
router={router}
|
<Head>
|
||||||
globalData={globalData}
|
<meta name="viewport" content="initial-scale=0.6, width=device-width" />
|
||||||
refetchGlobalData={getGlobalProps}
|
</Head>
|
||||||
>
|
<Layout
|
||||||
<Component
|
|
||||||
{...pageProps}
|
|
||||||
router={router}
|
router={router}
|
||||||
globalData={globalData}
|
globalData={globalData}
|
||||||
refetchGlobalData={getGlobalProps}
|
refetchGlobalData={getGlobalProps}
|
||||||
/>
|
>
|
||||||
</Layout>
|
<Component
|
||||||
|
{...pageProps}
|
||||||
|
router={router}
|
||||||
|
globalData={globalData}
|
||||||
|
refetchGlobalData={getGlobalProps}
|
||||||
|
/>
|
||||||
|
</Layout>
|
||||||
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,12 +10,7 @@ class MyDocument extends Document {
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<Html>
|
<Html>
|
||||||
<Head>
|
<Head />
|
||||||
<meta
|
|
||||||
name="viewport"
|
|
||||||
content="initial-scale=0.6, width=device-width"
|
|
||||||
/>
|
|
||||||
</Head>
|
|
||||||
<body bgcolor="#222426">
|
<body bgcolor="#222426">
|
||||||
<Main />
|
<Main />
|
||||||
<NextScript />
|
<NextScript />
|
||||||
|
|
|
@ -6,31 +6,36 @@ import constants from '../constants.json'
|
||||||
import LoadingIndicator from '../components/LoadingIndicator'
|
import LoadingIndicator from '../components/LoadingIndicator'
|
||||||
import styles from './chat.module.css'
|
import styles from './chat.module.css'
|
||||||
|
|
||||||
|
const byDate = (a, b) => {
|
||||||
|
return a.date - b.date
|
||||||
|
}
|
||||||
|
|
||||||
function countAllMessages(msgs) {
|
function countAllMessages(msgs) {
|
||||||
return Object.keys(msgs).reduce((acc, key) => {
|
return Object.keys(msgs).reduce((acc, key) => {
|
||||||
return acc + msgs[key].length
|
return acc + msgs[key].length
|
||||||
}, 0)
|
}, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
function groupMessages(msgs, currUser) {
|
function groupPrevMessages(msgs, currUser) {
|
||||||
return msgs.reduce((acc, msg) => {
|
return msgs.reduce((acc, msg) => {
|
||||||
const group =
|
const group =
|
||||||
parseInt(msg.sender) !== parseInt(currUser) ? msg.sender : msg.reciever
|
parseInt(msg.sender) !== parseInt(currUser) ? msg.sender : msg.reciever
|
||||||
return {
|
return {
|
||||||
...acc,
|
...acc,
|
||||||
[group]: [msg],
|
[group]: {
|
||||||
|
msgs: [msg],
|
||||||
|
loaded: false,
|
||||||
|
lastLoaded: false,
|
||||||
|
lastMessage: msg,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}, {})
|
}, {})
|
||||||
}
|
}
|
||||||
|
|
||||||
function addMsgsToGroup(msgGroup, msgs, user) {
|
function addMsgsToGroup(msgGroup, msgs, user) {
|
||||||
let res = { ...msgGroup }
|
let res = { ...msgGroup }
|
||||||
msgs.forEach((msg) => {
|
msgs.reverse().forEach((msg) => {
|
||||||
res = addMsgToGroup(
|
res = addMsgToGroup(res, msg, user)
|
||||||
res,
|
|
||||||
msg.reciever === user ? { ...msg, unread: 0 } : msg,
|
|
||||||
user
|
|
||||||
)
|
|
||||||
})
|
})
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
@ -39,11 +44,24 @@ function addMsgToGroup(msgGroup, msg, user) {
|
||||||
const group =
|
const group =
|
||||||
parseInt(msg.sender) === parseInt(user) ? msg.reciever : msg.sender
|
parseInt(msg.sender) === parseInt(user) ? msg.reciever : msg.sender
|
||||||
if (!msgGroup[group]) {
|
if (!msgGroup[group]) {
|
||||||
msgGroup[group] = []
|
msgGroup[group] = { msgs: [], lastLoaded: true, loaded: true }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const currGroup = msgGroup[group]
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...msgGroup,
|
...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 {
|
export default class Chat extends React.Component {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props)
|
super(props)
|
||||||
|
@ -77,16 +117,35 @@ export default class Chat extends React.Component {
|
||||||
}
|
}
|
||||||
if (props.globalData && !isNaN(props.globalData.userId)) {
|
if (props.globalData && !isNaN(props.globalData.userId)) {
|
||||||
this.state.user = 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) {
|
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) {
|
if (!prevProps.globalData.userId && this.props.globalData.userId) {
|
||||||
this.setState({
|
this.setState({
|
||||||
user: this.props.globalData.userId,
|
user: this.props.globalData.userId,
|
||||||
})
|
})
|
||||||
// this.connect(this.props.globalData.userId)
|
this.connect(this.props.globalData.userId)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
|
@ -112,8 +171,16 @@ export default class Chat extends React.Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
handleErrors(err) {
|
handleErrors(err) {
|
||||||
alert(err.message)
|
this.setState({
|
||||||
|
connected: false,
|
||||||
|
})
|
||||||
console.error(err)
|
console.error(err)
|
||||||
|
alert(`Chat error: ${err.message}`)
|
||||||
|
try {
|
||||||
|
this.socket.disconnect()
|
||||||
|
} catch (e) {
|
||||||
|
console.warn(e)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
connect(user) {
|
connect(user) {
|
||||||
|
@ -141,23 +208,49 @@ export default class Chat extends React.Component {
|
||||||
const { prevMsgs } = data
|
const { prevMsgs } = data
|
||||||
const { user } = this.state
|
const { user } = this.state
|
||||||
this.setState({
|
this.setState({
|
||||||
msgs: groupMessages(prevMsgs, user),
|
msgs: groupPrevMessages(prevMsgs, user),
|
||||||
connected: true,
|
connected: true,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
socket.on('chat message read', (data) => {
|
socket.on('chat message read', (data) => {
|
||||||
const { userReadMsg } = 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
|
const { msgs, user, selectedUser } = this.state
|
||||||
if (msgs[selectedUser].length <= 1) {
|
|
||||||
this.setState({
|
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) => {
|
socket.on('chat message', (data) => {
|
||||||
|
@ -170,8 +263,8 @@ export default class Chat extends React.Component {
|
||||||
this.socket = socket
|
this.socket = socket
|
||||||
}
|
}
|
||||||
|
|
||||||
sendMsg() {
|
sendMsg(currMsg, type) {
|
||||||
const { msgs, selectedUser, currMsg, user } = this.state
|
const { msgs, selectedUser, user } = this.state
|
||||||
if (!currMsg) {
|
if (!currMsg) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -182,6 +275,7 @@ export default class Chat extends React.Component {
|
||||||
sender: user,
|
sender: user,
|
||||||
date: new Date().getTime(),
|
date: new Date().getTime(),
|
||||||
unread: 1,
|
unread: 1,
|
||||||
|
type: type || 'text',
|
||||||
}
|
}
|
||||||
this.socket.emit('chat message', msg)
|
this.socket.emit('chat message', msg)
|
||||||
this.setState({
|
this.setState({
|
||||||
|
@ -191,26 +285,31 @@ export default class Chat extends React.Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
partnerReadChatMessage(chatPartner) {
|
partnerSeenChatMessage(chatPartner) {
|
||||||
const { msgs } = this.state
|
const { msgs } = this.state
|
||||||
this.setState({
|
this.setState({
|
||||||
msgs: {
|
msgs: {
|
||||||
...msgs,
|
...msgs,
|
||||||
[chatPartner]: msgs[chatPartner].map((msg) => {
|
[chatPartner]: {
|
||||||
if (msg.reciever === chatPartner) {
|
...msgs[chatPartner],
|
||||||
return {
|
lastMessage: { ...msgs[chatPartner].lastMessage, unread: 0 },
|
||||||
...msg,
|
msgs: msgs[chatPartner].msgs.map((msg) => {
|
||||||
unread: 0,
|
if (msg.reciever === chatPartner) {
|
||||||
|
return {
|
||||||
|
...msg,
|
||||||
|
unread: 0,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return msg
|
||||||
}
|
}
|
||||||
} else {
|
}),
|
||||||
return msg
|
},
|
||||||
}
|
|
||||||
}),
|
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
this.scrollToChatBottom()
|
||||||
}
|
}
|
||||||
|
|
||||||
chatMessageRead(chatPartner) {
|
chatMessageSeen(chatPartner) {
|
||||||
const { msgs, user } = this.state
|
const { msgs, user } = this.state
|
||||||
if (this.props.refetchGlobalData) {
|
if (this.props.refetchGlobalData) {
|
||||||
this.props.refetchGlobalData()
|
this.props.refetchGlobalData()
|
||||||
|
@ -219,16 +318,20 @@ export default class Chat extends React.Component {
|
||||||
this.setState({
|
this.setState({
|
||||||
msgs: {
|
msgs: {
|
||||||
...msgs,
|
...msgs,
|
||||||
[chatPartner]: msgs[chatPartner].map((msg) => {
|
[chatPartner]: {
|
||||||
if (msg.reciever === user) {
|
...msgs[chatPartner],
|
||||||
return {
|
lastMessage: { ...msgs[chatPartner].lastMessage, unread: 0 },
|
||||||
...msg,
|
msgs: msgs[chatPartner].msgs.map((msg) => {
|
||||||
unread: 0,
|
if (msg.reciever === user) {
|
||||||
|
return {
|
||||||
|
...msg,
|
||||||
|
unread: 0,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return msg
|
||||||
}
|
}
|
||||||
} else {
|
}),
|
||||||
return msg
|
},
|
||||||
}
|
|
||||||
}),
|
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -236,14 +339,14 @@ export default class Chat extends React.Component {
|
||||||
selectedUserChange(val) {
|
selectedUserChange(val) {
|
||||||
const { msgs, selectedUser, user } = this.state
|
const { msgs, selectedUser, user } = this.state
|
||||||
const prevLastMessage = msgs[selectedUser]
|
const prevLastMessage = msgs[selectedUser]
|
||||||
? msgs[selectedUser][msgs[selectedUser].length - 1]
|
? msgs[selectedUser].lastMessage
|
||||||
: null
|
: null
|
||||||
if (
|
if (
|
||||||
prevLastMessage &&
|
prevLastMessage &&
|
||||||
prevLastMessage.unread === 1 &&
|
prevLastMessage.unread === 1 &&
|
||||||
prevLastMessage.sender !== user
|
prevLastMessage.sender !== user
|
||||||
) {
|
) {
|
||||||
this.chatMessageRead(selectedUser)
|
this.chatMessageSeen(selectedUser)
|
||||||
}
|
}
|
||||||
this.setState({
|
this.setState({
|
||||||
selectedUser: val,
|
selectedUser: val,
|
||||||
|
@ -255,14 +358,21 @@ export default class Chat extends React.Component {
|
||||||
if (!currSelectedMsgs) {
|
if (!currSelectedMsgs) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (msgs[val].length <= 1) {
|
if (!msgs[val].loaded) {
|
||||||
this.socket.emit('chat message open', {
|
this.socket.emit('get chat messages', {
|
||||||
chatPartner: val,
|
chatPartner: val,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
const lastMessage = currSelectedMsgs[currSelectedMsgs.length - 1]
|
const lastMessage = currSelectedMsgs.lastMessage
|
||||||
if (lastMessage.unread === 1 && lastMessage.sender !== user) {
|
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
|
const { currMsg, msgs, selectedUser, user } = this.state
|
||||||
return (
|
return (
|
||||||
<div className={styles.chatInput}>
|
<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
|
<input
|
||||||
autoFocus
|
autoFocus
|
||||||
placeholder={'Message ...'}
|
placeholder={'Message ...'}
|
||||||
|
@ -278,17 +420,17 @@ export default class Chat extends React.Component {
|
||||||
tabIndex={0}
|
tabIndex={0}
|
||||||
onKeyUp={(e) => {
|
onKeyUp={(e) => {
|
||||||
const lastMessage = msgs[selectedUser]
|
const lastMessage = msgs[selectedUser]
|
||||||
? msgs[selectedUser][msgs[selectedUser].length - 1]
|
? msgs[selectedUser].lastMessage
|
||||||
: null
|
: null
|
||||||
if (
|
if (
|
||||||
lastMessage &&
|
lastMessage &&
|
||||||
lastMessage.unread === 1 &&
|
lastMessage.unread === 1 &&
|
||||||
lastMessage.sender !== user
|
lastMessage.sender !== user
|
||||||
) {
|
) {
|
||||||
this.chatMessageRead(selectedUser)
|
this.chatMessageSeen(selectedUser)
|
||||||
}
|
}
|
||||||
if (e.key === 'Enter') {
|
if (e.key === 'Enter') {
|
||||||
this.sendMsg()
|
this.sendMsg(currMsg)
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
|
@ -300,10 +442,10 @@ export default class Chat extends React.Component {
|
||||||
<div className={`buttonContainer ${styles.sendButton}`}>
|
<div className={`buttonContainer ${styles.sendButton}`}>
|
||||||
<div
|
<div
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
this.sendMsg()
|
this.sendMsg(currMsg)
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Send
|
Küldés
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -336,8 +478,8 @@ export default class Chat extends React.Component {
|
||||||
alert('Érvényes User ID-t adj meg! (számot)')
|
alert('Érvényes User ID-t adj meg! (számot)')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
this.selectedUserChange(userInputVal)
|
||||||
this.setState({
|
this.setState({
|
||||||
selectedUser: userInputVal,
|
|
||||||
userInputVal: null,
|
userInputVal: null,
|
||||||
})
|
})
|
||||||
}}
|
}}
|
||||||
|
@ -345,68 +487,113 @@ export default class Chat extends React.Component {
|
||||||
Chat!
|
Chat!
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<i>Admin User ID-ja: {'"1"'}</i>
|
<i style={{ fontSize: '12px' }}>Admin User ID-ja: {'"1"'}</i>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
renderMsgs() {
|
renderMsgs() {
|
||||||
const { msgs, selectedUser } = this.state
|
const { msgs, selectedUser, user } = this.state
|
||||||
|
|
||||||
|
const selectedMsgs = msgs[selectedUser].msgs
|
||||||
let prevMsg
|
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 (
|
return (
|
||||||
<div
|
<>
|
||||||
key={key}
|
{this.renderLoadMore()}
|
||||||
title={timeString}
|
{selectedMsgs.reduce((acc, currMessage, i) => {
|
||||||
className={`${styles.messageContainer} ${
|
const { date, sender, unread } = currMessage
|
||||||
sender === user ? styles.ownMsg : styles.partnerMsg
|
const timeString = new Date(date).toLocaleString()
|
||||||
}`}
|
if (prevMsg && prevMsg.unread === 0 && unread === 1) {
|
||||||
>
|
if (sender === selectedUser) {
|
||||||
<div className={`${styles.messageEntry}`}>{msg}</div>
|
acc.push(<NewMarker key={`marker_${i}`} />)
|
||||||
</div>
|
} else if (prevMsg.sender !== selectedUser) {
|
||||||
|
acc.push(<SeenMarker key={`marker_${i}`} />)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
acc.push(
|
||||||
|
<div
|
||||||
|
key={i}
|
||||||
|
title={timeString}
|
||||||
|
className={`${styles.messageContainer} ${
|
||||||
|
sender === user ? styles.ownMsg : styles.partnerMsg
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{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
|
||||||
|
}, [])}
|
||||||
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
renderSidebarEntryes() {
|
renderMsg(message, key) {
|
||||||
const { msgs } = this.state
|
const { msg, type } = message
|
||||||
const sorted = Object.keys(msgs).sort((a, b) => {
|
|
||||||
const lastA = msgs[a].slice(-1)[0]
|
if (type === 'text') {
|
||||||
const lastB = msgs[b].slice(-1)[0]
|
return <div className={`${styles.messageEntry}`}>{msg}</div>
|
||||||
return lastB.date - lastA.date
|
} else if (type === 'img') {
|
||||||
})
|
return (
|
||||||
return sorted.map((key, i) => {
|
<a key={key} href={msg} rel="noreferrer" target="_blank">
|
||||||
return this.renderSidebarEntry(i, key)
|
<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>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
renderSidebarEntry(i, key) {
|
||||||
const { selectedUser, msgs, user } = this.state
|
const { selectedUser, msgs, user } = this.state
|
||||||
const group = msgs[key]
|
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 (
|
return (
|
||||||
<div
|
<div
|
||||||
|
@ -420,19 +607,34 @@ export default class Chat extends React.Component {
|
||||||
}}
|
}}
|
||||||
key={i}
|
key={i}
|
||||||
>
|
>
|
||||||
<div>#{key}</div>
|
<div>
|
||||||
<div>{lastMessage.msg}</div>
|
<b>#{key}</b>
|
||||||
|
<div style={{ fontSize: '12px' }}>{date}</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
{lastMessage.type === 'text' ? lastMessage.msg : 'Csatolt fájl'}
|
||||||
|
</div>
|
||||||
</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() {
|
render() {
|
||||||
const { user, msgs, connected, selectedUser } = this.state
|
const { msgs, connected, selectedUser } = this.state
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.chat}>
|
<div className={styles.chat}>
|
||||||
<Head>
|
<Head>
|
||||||
<title>{`Connected as ${user}`}</title>
|
<title>Chat - Qmining | Frylabs.net</title>
|
||||||
</Head>
|
</Head>
|
||||||
{connected ? (
|
{connected ? (
|
||||||
<>
|
<>
|
||||||
|
@ -466,24 +668,8 @@ export default class Chat extends React.Component {
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<div>
|
<div className={styles.loading}>
|
||||||
<LoadingIndicator />
|
<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>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|
||||||
width: calc(20vw);
|
width: 200px;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
right: 0;
|
right: 0;
|
||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
|
@ -30,6 +30,10 @@
|
||||||
user-select: none;
|
user-select: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.usersContainer > div:first-child {
|
||||||
|
margin-top: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
.usersContainer > div {
|
.usersContainer > div {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
@ -65,6 +69,12 @@
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.group > * {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
.group:hover {
|
.group:hover {
|
||||||
background-color: var(--hoover-color);
|
background-color: var(--hoover-color);
|
||||||
}
|
}
|
||||||
|
@ -112,6 +122,7 @@
|
||||||
.chatInput > input {
|
.chatInput > input {
|
||||||
padding: 2px;
|
padding: 2px;
|
||||||
height: 30px;
|
height: 30px;
|
||||||
|
margin: 0px 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.messageContainer {
|
.messageContainer {
|
||||||
|
@ -121,6 +132,13 @@
|
||||||
max-width: 300px;
|
max-width: 300px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.messageContainer img {
|
||||||
|
max-width: 100%;
|
||||||
|
width: 100%;
|
||||||
|
height: auto;
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
.ownMsg {
|
.ownMsg {
|
||||||
align-self: flex-end;
|
align-self: flex-end;
|
||||||
justify-content: flex-end;
|
justify-content: flex-end;
|
||||||
|
@ -139,11 +157,13 @@
|
||||||
justify-content: flex-end;
|
justify-content: flex-end;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
padding: 0px 5px;
|
padding: 0px 5px;
|
||||||
|
font-style: italic;
|
||||||
}
|
}
|
||||||
|
|
||||||
.newMarker {
|
.newMarker {
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
padding: 0px 5px;
|
padding: 0px 5px;
|
||||||
|
font-style: italic;
|
||||||
}
|
}
|
||||||
|
|
||||||
.messageEntry {
|
.messageEntry {
|
||||||
|
@ -170,3 +190,31 @@
|
||||||
text-align: center;
|
text-align: center;
|
||||||
width: 300px;
|
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;
|
||||||
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
.container {
|
.container {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-flow: column;
|
flex-flow: column;
|
||||||
height: calc(98vh);
|
height: calc(94vh);
|
||||||
cursor: default;
|
cursor: default;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -49,23 +49,27 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.row div,
|
.row div,
|
||||||
.headerRow div, .sumRow div {
|
.headerRow div,
|
||||||
|
.sumRow div {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
padding: 0px 5px;
|
padding: 0px 5px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.headerRow > div, .sumRow > div {
|
.headerRow > div,
|
||||||
|
.sumRow > div {
|
||||||
padding: 5px;
|
padding: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.row :nth-child(1),
|
.row :nth-child(1),
|
||||||
.headerRow :nth-child(1), .sumRow :nth-child(1) {
|
.headerRow :nth-child(1),
|
||||||
|
.sumRow :nth-child(1) {
|
||||||
flex: 0 50px;
|
flex: 0 50px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.row :nth-child(2),
|
.row :nth-child(2),
|
||||||
.headerRow :nth-child(2), .sumRow :nth-child(2) {
|
.headerRow :nth-child(2),
|
||||||
|
.sumRow :nth-child(2) {
|
||||||
flex: 0 130px;
|
flex: 0 130px;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
}
|
}
|
||||||
|
|
|
@ -405,7 +405,15 @@ export default function UserFiles({ router, globalData }) {
|
||||||
Törlés
|
Törlés
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
`#${user}`
|
<Link href={`/chat?user=${user}`}>
|
||||||
|
<a
|
||||||
|
title={`Chat #${user}-el`}
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation()
|
||||||
|
}}
|
||||||
|
className={'userId'}
|
||||||
|
>{`#${user}`}</a>
|
||||||
|
</Link>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue