200 lines
6.4 KiB
TypeScript
200 lines
6.4 KiB
TypeScript
'use client'
|
||
import React, {useEffect, useState} from 'react';
|
||
import {AUTH_TOKEN_KEY} from '../../constants/common';
|
||
import {getChatList, getChatMessages} from "../../actions/chat/groups";
|
||
import {useLocalStorage} from "../../hooks/useLocalStorage";
|
||
import dayjs from "dayjs";
|
||
import relativeTime from "dayjs/plugin/relativeTime";
|
||
|
||
import {message} from "antd";
|
||
import {Loader} from "../view/Loader";
|
||
import SignalrConnection from "../../lib/signalr-connection";
|
||
import {CheckOutlined} from "@ant-design/icons";
|
||
|
||
dayjs.extend(relativeTime);
|
||
|
||
type CompProps = {
|
||
locale: string;
|
||
groupId: number
|
||
};
|
||
|
||
export const ChatMessages = ({ locale, groupId }: CompProps) => {
|
||
const { newMessage, joinChat, readMessages, addListener } = SignalrConnection();
|
||
const [jwt] = useLocalStorage(AUTH_TOKEN_KEY, '');
|
||
//const messages = await getChatMessages(locale, jwt, groupId)
|
||
const [loading, setLoading] = useState<boolean>(false);
|
||
const [text, setText] = useState('');
|
||
const [me, setMe] = useState<any | undefined>();
|
||
const [notMe, setNotMe] = useState<any | undefined>();
|
||
const [messages, setMessages] = useState<any[]>([]);
|
||
|
||
useEffect(() => {
|
||
if (jwt) {
|
||
setLoading(true);
|
||
Promise.all([
|
||
getChatList(locale, jwt),
|
||
getChatMessages(locale, jwt, groupId),
|
||
])
|
||
.then(([_groups, _messages]) => {
|
||
//
|
||
const _group = _groups.directs.find( (el: any) => el.group.id === groupId)
|
||
if (_group.group.members[0].userId === parseInt(_group.userId)){
|
||
setMe(_group.group.members[0].user)
|
||
setNotMe(_group.group.members[1].user)
|
||
} else {
|
||
setMe(_group.group.members[1].user)
|
||
setNotMe(_group.group.members[0].user)
|
||
}
|
||
setMessages(_messages.messages);
|
||
|
||
})
|
||
.catch((e) => {
|
||
console.log(e)
|
||
message.error('Не удалось загрузить данные');
|
||
})
|
||
.finally(() => {
|
||
setLoading(false);
|
||
})
|
||
}
|
||
},[jwt])
|
||
|
||
|
||
const onConnected = (flag: boolean) =>{
|
||
if (flag) {
|
||
joinChat(groupId)
|
||
readUreaded()
|
||
}
|
||
}
|
||
|
||
const readUreaded = () => {
|
||
const msgs = [] as number[];
|
||
messages.forEach((message: any) => {
|
||
if (!message.seen && message.sentByMe === false){
|
||
msgs.push(message.id)
|
||
}
|
||
})
|
||
readMessages(msgs)
|
||
}
|
||
|
||
const onReceiveMessage = (payload: any) => {
|
||
const _messages = [... messages];
|
||
let flag = false;
|
||
const msg = {
|
||
data : payload.data,
|
||
dataType: null,
|
||
id: payload.id,
|
||
receiverId:null,
|
||
seen: false,
|
||
senderId: payload.creatorId,
|
||
sentAt: payload.createdUtc+'.000Z',
|
||
sentByMe: payload.creatorId === me.id,
|
||
text: payload.content,
|
||
type:"text"
|
||
}
|
||
_messages.forEach((item: any, i) => {
|
||
if (item.id === msg.id){
|
||
_messages[i] = msg
|
||
flag = true
|
||
}
|
||
})
|
||
//console.log(payload, flag)
|
||
if (flag){
|
||
setMessages([..._messages]);
|
||
} else {
|
||
setMessages([msg, ..._messages]);
|
||
}
|
||
readMessages([msg.id])
|
||
|
||
}
|
||
|
||
const onOpponentRead = (payload: any) => {
|
||
console.log('onOpponentRead', payload)
|
||
const _messages = [... messages] as any[];
|
||
_messages.forEach((item: any, i) => {
|
||
if (item.id === payload.messageId){
|
||
_messages[i].seen = true
|
||
|
||
}
|
||
})
|
||
setMessages([..._messages])
|
||
}
|
||
|
||
useEffect(() => {
|
||
addListener('onConnected', onConnected)
|
||
addListener('ReceiveMessage', onReceiveMessage)
|
||
addListener('MessageWasRead', onOpponentRead)
|
||
}, [messages, me, setMessages]);
|
||
|
||
|
||
|
||
|
||
const handleSendMessages = () => {
|
||
newMessage({
|
||
GroupId: groupId.toString(),
|
||
CreatorId: me.id,
|
||
Content: text
|
||
})
|
||
setText('')
|
||
}
|
||
|
||
const onEnterPress = (e: any) => {
|
||
if(e.keyCode == 13 && e.shiftKey == false) {
|
||
e.preventDefault();
|
||
handleSendMessages();
|
||
}
|
||
}
|
||
|
||
const handleChangeMessage = (e: any) =>{
|
||
const { value } = e.target;
|
||
e.preventDefault();
|
||
setText(value);
|
||
return true
|
||
}
|
||
|
||
|
||
const messageRender = (message: any, i:number) => {
|
||
const item = message.sentByMe ? me : notMe
|
||
const date = dayjs(message.sentAt).fromNow()
|
||
const imgSrc = item?.faceImage?.descriptor ? 'http://static.bbuddy.expert/' + item.faceImage.descriptor.split(':')[1] : ''
|
||
return (
|
||
<div
|
||
className={message.sentByMe ? 'b-message__list b-message__list--me' : 'b-message__list'}
|
||
key={'message'+i}
|
||
>
|
||
<div className="b-message__item ">
|
||
<div className="b-message__avatar">
|
||
{imgSrc && (<img src={imgSrc} className="" alt=""/>)}
|
||
</div>
|
||
<div className="b-message__text" style={{minWidth: '150px'}}>
|
||
{message.text}
|
||
<span className="date">
|
||
<span className="checks" style={{color: message.seen ? 'green' : 'blue'}}>
|
||
<CheckOutlined />
|
||
{ message?.seen && (<CheckOutlined style={{marginLeft: '-10px'}} />)}
|
||
</span>
|
||
{date}
|
||
</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
)
|
||
}
|
||
|
||
|
||
return (
|
||
|
||
<div className="b-message">
|
||
<Loader isLoading={loading}>
|
||
<div className="b-message__inner">
|
||
{messages.map((el, i)=> (messageRender(el, i)))}
|
||
</div>
|
||
</Loader>
|
||
<div className="b-message__form">
|
||
<textarea placeholder="Type your message here" onKeyDown={onEnterPress}
|
||
onChange={handleChangeMessage} value={text}/>
|
||
<button className="b-message__btn" type="submit" onClick={handleSendMessages}/>
|
||
</div>
|
||
</div>
|
||
|
||
)
|
||
} |