wip session lobby
This commit is contained in:
parent
cff277d437
commit
0f25b00571
|
|
@ -89,6 +89,7 @@ function JKDashboardMain() {
|
||||||
registerTextMessageCallback();
|
registerTextMessageCallback();
|
||||||
registerFriendRequest();
|
registerFriendRequest();
|
||||||
registerFriendRequestAccepted();
|
registerFriendRequestAccepted();
|
||||||
|
registerChatMessageCallback();
|
||||||
|
|
||||||
scriptLoaded.current = true
|
scriptLoaded.current = true
|
||||||
};
|
};
|
||||||
|
|
@ -125,10 +126,19 @@ function JKDashboardMain() {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const registerChatMessageCallback = () => {
|
||||||
|
window.JK.JamServer.registerMessageCallback(window.JK.MessageType.CHAT_MESSAGE, function (header, payload) {
|
||||||
|
console.log("registerChatMessageCallback " + JSON.stringify(payload));
|
||||||
|
// chatMessageReceived(payload);
|
||||||
|
// context.ChatActions.msgReceived(payload);
|
||||||
|
|
||||||
|
// handledNotification(payload);
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
const registerFriendRequest = () => {
|
const registerFriendRequest = () => {
|
||||||
window.JK.JamServer.registerMessageCallback(window.JK.MessageType.FRIEND_REQUEST, function(header, payload) {
|
window.JK.JamServer.registerMessageCallback(window.JK.MessageType.FRIEND_REQUEST, function(header, payload) {
|
||||||
console.log('registerFriendRequest payload', payload);
|
|
||||||
console.log('registerFriendRequest header', header);
|
|
||||||
handleNotification(payload, header.type);
|
handleNotification(payload, header.type);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,191 @@
|
||||||
|
import React, { useState, useRef, useEffect } from 'react';
|
||||||
|
import { Container, Row, Col, Button } from 'reactstrap';
|
||||||
|
import { useDispatch, useSelector } from 'react-redux';
|
||||||
|
import { fetchLobbyChatMessages, postNewChatMessage } from '../../store/features/lobbyChatMessagesSlice';
|
||||||
|
import { useAuth } from '../../context/UserAuth';
|
||||||
|
import JKProfileAvatar from '../profile/JKProfileAvatar';
|
||||||
|
import TimeAgo from '../common/JKTimeAgo';
|
||||||
|
import { Scrollbar } from 'react-scrollbars-custom';
|
||||||
|
|
||||||
|
function JKLobbyChat() {
|
||||||
|
const CHANNEL_LOBBY = 'lobby';
|
||||||
|
const LIMIT = 10;
|
||||||
|
const [newMessage, setNewMessage] = useState('');
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
const messageTextBox = useRef();
|
||||||
|
const scrollbar = useRef();
|
||||||
|
const scrolledToBottom = useRef(false);
|
||||||
|
const { currentUser } = useAuth();
|
||||||
|
const [fetching, setFetching] = useState(false);
|
||||||
|
const [messagesArrived, setMessagesArrived] = useState(false);
|
||||||
|
const [offset, setOffset] = useState(0);
|
||||||
|
|
||||||
|
const chatMessages = useSelector(state => state.lobbyChat.messages);
|
||||||
|
const createStatus = useSelector(state => state.lobbyChat.create_status);
|
||||||
|
|
||||||
|
const fetchMessages = async () => {
|
||||||
|
const options = { offset: offset, limit: LIMIT };
|
||||||
|
try {
|
||||||
|
setFetching(true);
|
||||||
|
await dispatch(fetchLobbyChatMessages(options)).unwrap();
|
||||||
|
if(chatMessages.length < LIMIT){
|
||||||
|
goToBottom();
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.log('ERROR', err);
|
||||||
|
} finally {
|
||||||
|
setFetching(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
fetchMessages();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (scrollbar && scrollbar.current) {
|
||||||
|
if (!fetching && !scrollAtTop()) {
|
||||||
|
if (chatMessages[chatMessages.length - 1]['user_id'] !== currentUser.id) {
|
||||||
|
if (!scrolledToBottom.current) {
|
||||||
|
setMessagesArrived(true);
|
||||||
|
} else {
|
||||||
|
goToBottom();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
goToBottom();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [chatMessages]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!messagesArrived) {
|
||||||
|
|
||||||
|
goToBottom();
|
||||||
|
}
|
||||||
|
}, [messagesArrived]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (offset !== 0) {
|
||||||
|
fetchMessages();
|
||||||
|
}
|
||||||
|
}, [offset]);
|
||||||
|
|
||||||
|
const handleOnKeyPress = event => {
|
||||||
|
if (event.key === 'Enter' || event.key === 'NumpadEnter') {
|
||||||
|
event.preventDefault();
|
||||||
|
sendMessage();
|
||||||
|
event.target.value = '';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const sendMessage = () => {
|
||||||
|
let msgData = {
|
||||||
|
message: newMessage,
|
||||||
|
channel: CHANNEL_LOBBY,
|
||||||
|
user_id: currentUser.id
|
||||||
|
};
|
||||||
|
setNewMessage('');
|
||||||
|
|
||||||
|
try {
|
||||||
|
dispatch(postNewChatMessage(msgData));
|
||||||
|
} catch (err) {
|
||||||
|
console.log('addNewMessage error', err);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (createStatus === 'succeeded') {
|
||||||
|
fetchMessages();
|
||||||
|
messageTextBox.current.focus();
|
||||||
|
}
|
||||||
|
}, [createStatus]);
|
||||||
|
|
||||||
|
const scrollAtTop = () => {
|
||||||
|
return scrollbar.current.scrollTop === 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
const goToBottom = () => {
|
||||||
|
if (scrollbar && scrollbar.current) {
|
||||||
|
scrollbar.current.scrollToBottom();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleScrollStop = scrollValues => {
|
||||||
|
console.log('handleScrollStop', scrollValues);
|
||||||
|
scrolledToBottom.current = false;
|
||||||
|
if (scrollValues.scrollTop === 0) {
|
||||||
|
setOffset(prev => prev + 1);
|
||||||
|
} else if (scrollValues.scrollTop === scrollValues.scrollHeight - scrollValues.clientHeight) {
|
||||||
|
scrolledToBottom.current = true;
|
||||||
|
setMessagesArrived(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const containerStyle = {
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
height: '200'
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<div className="bg-200 text-900" style={{ padding: '0.75rem' }}>
|
||||||
|
Lobby Chat
|
||||||
|
</div>
|
||||||
|
<div className="border pt-1 pl-3 p-2" style={containerStyle}>
|
||||||
|
<div style={{ height: '400px' }}>
|
||||||
|
<Scrollbar
|
||||||
|
ref={scrollbar}
|
||||||
|
onScrollStop={handleScrollStop}
|
||||||
|
style={{ width: '100%', height: 400 }}
|
||||||
|
mobileNative={true}
|
||||||
|
trackClickBehavior="step"
|
||||||
|
>
|
||||||
|
{chatMessages.map(message => (
|
||||||
|
<div className="d-flex mb-3 mr-1 text-message-row" key={message.id}>
|
||||||
|
<div className="avatar avatar-2xl d-inline-block">
|
||||||
|
<JKProfileAvatar url={message.user.photo_url} />
|
||||||
|
</div>
|
||||||
|
<div className="d-inline-block">
|
||||||
|
<div className="d-flex flex-column">
|
||||||
|
<div>
|
||||||
|
<strong>{message.user.name}</strong>
|
||||||
|
<time className="notification-time ml-2 t-1">
|
||||||
|
<TimeAgo date={message.created_at} />
|
||||||
|
</time>
|
||||||
|
</div>
|
||||||
|
<div>{message.message}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</Scrollbar>
|
||||||
|
{messagesArrived && (
|
||||||
|
<div className="d-flex justify-content-center">
|
||||||
|
<Button color="info" size="sm" onClick={() => setMessagesArrived(prev => !prev)}>
|
||||||
|
New messages
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div className="mt-2" style={{ height: '20%' }}>
|
||||||
|
<textarea
|
||||||
|
style={{ width: '100%' }}
|
||||||
|
value={newMessage}
|
||||||
|
onChange={e => setNewMessage(e.target.value)}
|
||||||
|
onKeyPress={handleOnKeyPress}
|
||||||
|
ref={messageTextBox}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="d-flex justify-content-end" style={{ height: '10%' }}>
|
||||||
|
<Button color="primary" onClick={sendMessage} disabled={!newMessage}>
|
||||||
|
Send Message
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default JKLobbyChat;
|
||||||
|
|
@ -1,22 +1,97 @@
|
||||||
import React, { useState, useRef, useEffect } from 'react';
|
import React, { useState, useRef, useEffect } from 'react';
|
||||||
import { Container, Row, Col, Button } from 'reactstrap';
|
import { Container, Row, Col, Button } from 'reactstrap';
|
||||||
import { useDispatch, useSelector } from 'react-redux';
|
import { useDispatch, useSelector } from 'react-redux';
|
||||||
import { fetchLobbyChatMessages } from '../../store/features/lobbyChatMessagesSlice';
|
import { fetchLobbyChatMessages, postNewChatMessage } from '../../store/features/lobbyChatMessagesSlice';
|
||||||
import { useAuth } from '../../context/UserAuth';
|
import { useAuth } from '../../context/UserAuth';
|
||||||
|
import JKProfileAvatar from '../profile/JKProfileAvatar';
|
||||||
|
import TimeAgo from '../common/JKTimeAgo';
|
||||||
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||||
|
|
||||||
|
import useOnScreen from '../../hooks/useOnScreen';
|
||||||
|
import useKeepScrollPosition from '../../hooks/useKeepScrollPosition';
|
||||||
|
|
||||||
function JKLobbyChat() {
|
function JKLobbyChat() {
|
||||||
const CHANNEL_LOBBY = 'lobby';
|
const CHANNEL_LOBBY = 'lobby';
|
||||||
|
const LIMIT = 10;
|
||||||
const [newMessage, setNewMessage] = useState('');
|
const [newMessage, setNewMessage] = useState('');
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
const messageTextBox = useRef();
|
const messageTextBox = useRef();
|
||||||
|
const scrollbar = useRef();
|
||||||
|
const scrolledToBottom = useRef(false);
|
||||||
const { currentUser } = useAuth();
|
const { currentUser } = useAuth();
|
||||||
|
const [fetching, setFetching] = useState(false);
|
||||||
|
const [messagesArrived, setMessagesArrived] = useState(false);
|
||||||
|
const [offset, setOffset] = useState(0);
|
||||||
|
|
||||||
const chatMessages = useSelector(state => state.lobbyChat.messages);
|
const chatMessages = useSelector(state => state.lobbyChat.records.messages);
|
||||||
|
const next = useSelector(state => state.lobbyChat.records.next);
|
||||||
|
const createStatus = useSelector(state => state.lobbyChat.create_status);
|
||||||
|
|
||||||
|
const [messages, setMessages] = useState([]);
|
||||||
|
const [lastMessageRef, setLastMessageRef] = useState(null);
|
||||||
|
const isIntersecting = useOnScreen({ current: lastMessageRef });
|
||||||
|
const { containerRef } = useKeepScrollPosition([messages]);
|
||||||
|
|
||||||
|
const fetchMessages = async (overrides = {}) => {
|
||||||
|
const options = { start: offset * LIMIT, limit: LIMIT };
|
||||||
|
const params = { ...options, ...overrides };
|
||||||
|
try {
|
||||||
|
setFetching(true);
|
||||||
|
await dispatch(fetchLobbyChatMessages(params)).unwrap();
|
||||||
|
} catch (error) {
|
||||||
|
console.log('Error when fetching chat messages', error);
|
||||||
|
} finally {
|
||||||
|
setFetching(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
dispatch(fetchLobbyChatMessages());
|
if (isIntersecting) {
|
||||||
|
if (next) {
|
||||||
|
setOffset(prev => prev + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [isIntersecting]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (offset !== 0) {
|
||||||
|
fetchMessages();
|
||||||
|
}
|
||||||
|
}, [offset]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
fetchMessages();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setMessages(old => {
|
||||||
|
const chats = old.concat(chatMessages);
|
||||||
|
const deliveredChats = chats.filter((chat, index) => {
|
||||||
|
return chat.status !== 'pending';
|
||||||
|
});
|
||||||
|
return deliveredChats.sort((a, b) => {
|
||||||
|
return new Date(a.created_at) - new Date(b.created_at);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!scrollBarAtBottom(containerRef.current)) {
|
||||||
|
if (messages.length > 0 && messages[messages.length - 1]['user_id'] !== currentUser.id) {
|
||||||
|
setMessagesArrived(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [chatMessages]);
|
||||||
|
|
||||||
|
const scrollBarAtBottom = el => {
|
||||||
|
let sh = el.scrollHeight,
|
||||||
|
st = el.scrollTop,
|
||||||
|
ht = el.offsetHeight;
|
||||||
|
return ht == 0 || st == sh - ht;
|
||||||
|
};
|
||||||
|
|
||||||
|
const goToBottom = () => {
|
||||||
|
containerRef.current.scrollTop = containerRef.current.scrollHeight;
|
||||||
|
};
|
||||||
|
|
||||||
const handleOnKeyPress = event => {
|
const handleOnKeyPress = event => {
|
||||||
if (event.key === 'Enter' || event.key === 'NumpadEnter') {
|
if (event.key === 'Enter' || event.key === 'NumpadEnter') {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
|
@ -27,55 +102,103 @@ function JKLobbyChat() {
|
||||||
|
|
||||||
const sendMessage = () => {
|
const sendMessage = () => {
|
||||||
let msgData = {
|
let msgData = {
|
||||||
|
id: Date.now(),
|
||||||
message: newMessage,
|
message: newMessage,
|
||||||
channel: CHANNEL_LOBBY,
|
channel: CHANNEL_LOBBY,
|
||||||
user_id: currentUser.id,
|
user_id: currentUser.id,
|
||||||
|
created_at: new Date(),
|
||||||
|
user: {
|
||||||
|
id: currentUser.id,
|
||||||
|
name: currentUser.name,
|
||||||
|
photo_url: currentUser.photo_url
|
||||||
|
},
|
||||||
|
status: 'pending'
|
||||||
};
|
};
|
||||||
setNewMessage('');
|
setNewMessage('');
|
||||||
|
setMessages(old => old.concat([msgData]));
|
||||||
|
goToBottom();
|
||||||
try {
|
try {
|
||||||
//dispatch(postNewMessage(msgData));
|
dispatch(postNewChatMessage(msgData));
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log('addNewMessage error', err);
|
console.log('Error when posting new chat message', err);
|
||||||
} finally {
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (createStatus === 'succeeded') {
|
||||||
|
fetchMessages({ start: 0, limit: 1, lastOnly: true });
|
||||||
|
messageTextBox.current.focus();
|
||||||
|
}
|
||||||
|
}, [createStatus]);
|
||||||
|
|
||||||
|
const wrapperStyle = {
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
height: '200'
|
||||||
|
};
|
||||||
|
|
||||||
|
const containerStyle = {
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
height: '400px',
|
||||||
|
overflow: 'auto'
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<div className="bg-200 text-900" style={{ padding: '0.75rem' }}>
|
<div className="bg-200 text-900" style={{ padding: '0.75rem' }}>
|
||||||
Lobby Chat
|
Lobby Chat
|
||||||
</div>
|
</div>
|
||||||
<div className="border pt-1 pl-3 p-2">
|
<div className="border pt-1 pl-3 p-2" style={wrapperStyle}>
|
||||||
<Container>
|
<div className="lobby-chat" ref={containerRef} style={containerStyle}>
|
||||||
<Row>
|
{messages.map((message, i) => (
|
||||||
<Col>
|
<div className="d-flex mb-3 mr-1 text-message-row" key={message.id}>
|
||||||
{chatMessages.map((msg, index) => (
|
<div ref={ref => (i === 0 ? setLastMessageRef(ref) : null)}>
|
||||||
<div key={index}>
|
<div className="avatar avatar-2xl d-inline-block">
|
||||||
<span className="text-primary">{msg.user_id}</span> : {msg.message}
|
<JKProfileAvatar url={message.user.photo_url} />
|
||||||
</div>
|
</div>
|
||||||
))}
|
<div className="d-inline-block">
|
||||||
</Col>
|
<div className="d-flex flex-column">
|
||||||
</Row>
|
<div>
|
||||||
<Row>
|
<strong>{message.user.name}</strong>
|
||||||
<Col>
|
<time className="notification-time ml-2 t-1">
|
||||||
<textarea
|
<TimeAgo date={message.created_at} />
|
||||||
style={{ width: '100%' }}
|
</time>
|
||||||
value={newMessage}
|
{message.status === 'pending' && (
|
||||||
onChange={e => setNewMessage(e.target.value)}
|
<span className="ml-2">
|
||||||
onKeyPress={handleOnKeyPress}
|
<FontAwesomeIcon icon="spinner" />
|
||||||
ref={messageTextBox}
|
</span>
|
||||||
/>
|
)}
|
||||||
</Col>
|
</div>
|
||||||
</Row>
|
<div>{message.message}</div>
|
||||||
<Row>
|
</div>
|
||||||
<Col className="d-flex justify-content-end">
|
</div>
|
||||||
<Button color="primary" onClick={sendMessage} disabled={!newMessage}>
|
</div>
|
||||||
Send Message
|
</div>
|
||||||
|
))}
|
||||||
|
|
||||||
|
{messagesArrived && (
|
||||||
|
<div className="d-flex justify-content-center">
|
||||||
|
<Button color="info" size="sm" onClick={() => setMessagesArrived(prev => !prev)}>
|
||||||
|
New messages
|
||||||
</Button>
|
</Button>
|
||||||
</Col>
|
</div>
|
||||||
</Row>
|
)}
|
||||||
</Container>
|
</div>
|
||||||
|
<div className="mt-2" style={{ height: '20%' }}>
|
||||||
|
<textarea
|
||||||
|
style={{ width: '100%' }}
|
||||||
|
value={newMessage}
|
||||||
|
onChange={e => setNewMessage(e.target.value)}
|
||||||
|
onKeyPress={handleOnKeyPress}
|
||||||
|
ref={messageTextBox}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="d-flex justify-content-end" style={{ height: '10%' }}>
|
||||||
|
<Button color="primary" onClick={sendMessage} disabled={!newMessage}>
|
||||||
|
Send Message
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -157,7 +157,8 @@ import {
|
||||||
faMusic,
|
faMusic,
|
||||||
faRecordVinyl,
|
faRecordVinyl,
|
||||||
faAddressCard,
|
faAddressCard,
|
||||||
faVolumeUp
|
faVolumeUp,
|
||||||
|
faSpinner
|
||||||
} from '@fortawesome/free-solid-svg-icons';
|
} from '@fortawesome/free-solid-svg-icons';
|
||||||
|
|
||||||
//import { faAcousticGuitar } from "../icons";
|
//import { faAcousticGuitar } from "../icons";
|
||||||
|
|
@ -284,6 +285,7 @@ library.add(
|
||||||
faRecordVinyl,
|
faRecordVinyl,
|
||||||
faAddressCard,
|
faAddressCard,
|
||||||
faVolumeUp,
|
faVolumeUp,
|
||||||
|
faSpinner,
|
||||||
|
|
||||||
// Brand
|
// Brand
|
||||||
faFacebook,
|
faFacebook,
|
||||||
|
|
|
||||||
|
|
@ -175,7 +175,8 @@ export const getLatencyToUsers = (currentUserId, participantIds) => {
|
||||||
|
|
||||||
export const getLobbyChatMessages = (options = {}) => {
|
export const getLobbyChatMessages = (options = {}) => {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
apiFetch(`/chat_messages?${new URLSearchParams(options)}`)
|
console.log('getLobbyChatMessages', options)
|
||||||
|
apiFetch(`/chat?${new URLSearchParams(options)}`)
|
||||||
.then(response => resolve(response))
|
.then(response => resolve(response))
|
||||||
.catch(error => reject(error))
|
.catch(error => reject(error))
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
import { useRef, useLayoutEffect, useMemo } from "react";
|
||||||
|
|
||||||
|
const useKeepScrollPosition = (deps = []) => {
|
||||||
|
const containerRef = useRef(null);
|
||||||
|
const previousScrollPosition = useRef(0);
|
||||||
|
|
||||||
|
useMemo(() => {
|
||||||
|
if (containerRef?.current) {
|
||||||
|
const container = containerRef?.current;
|
||||||
|
previousScrollPosition.current =
|
||||||
|
container?.scrollHeight - container?.scrollTop;
|
||||||
|
}
|
||||||
|
}, [...deps]);
|
||||||
|
|
||||||
|
useLayoutEffect(() => {
|
||||||
|
if (containerRef?.current) {
|
||||||
|
const container = containerRef?.current || {};
|
||||||
|
container.scrollTop =
|
||||||
|
container?.scrollHeight - previousScrollPosition.current;
|
||||||
|
}
|
||||||
|
}, [...deps]);
|
||||||
|
|
||||||
|
return {
|
||||||
|
containerRef
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default useKeepScrollPosition;
|
||||||
|
|
@ -0,0 +1,36 @@
|
||||||
|
import { useState, useEffect } from "react";
|
||||||
|
import useOnScreen from "./useOnScreen";
|
||||||
|
|
||||||
|
const getMessages = () => {
|
||||||
|
const data = [];
|
||||||
|
for (let x = 0; x < 20; x++) {
|
||||||
|
data.push({
|
||||||
|
id: faker.datatype.uuid(),
|
||||||
|
message: faker.lorem.words(Math.floor(Math.random() * 10) + 1),
|
||||||
|
in: faker.datatype.boolean()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return data;
|
||||||
|
};
|
||||||
|
|
||||||
|
const useMessages = ({ messageFunc }) => {
|
||||||
|
const [messages, setMessages] = useState(messageFunc());
|
||||||
|
const [lastMessageRef, setLastMessageRef] = useState(null);
|
||||||
|
const isIntersecting = useOnScreen({ current: lastMessageRef });
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (isIntersecting) {
|
||||||
|
setMessages((old) => messageFunc().concat(old));
|
||||||
|
}
|
||||||
|
}, [isIntersecting]);
|
||||||
|
|
||||||
|
return {
|
||||||
|
messages,
|
||||||
|
setMessages,
|
||||||
|
|
||||||
|
setLastMessageRef
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default useMessages;
|
||||||
|
|
@ -0,0 +1,23 @@
|
||||||
|
import { useState, useEffect } from "react";
|
||||||
|
|
||||||
|
const useOnScreen = (ref) => {
|
||||||
|
const [isIntersecting, setIntersecting] = useState(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const observer = new IntersectionObserver(([entry]) =>
|
||||||
|
setIntersecting(entry.isIntersecting)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (ref.current) {
|
||||||
|
observer.observe(ref.current);
|
||||||
|
}
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
observer.disconnect();
|
||||||
|
};
|
||||||
|
}, [ref]);
|
||||||
|
|
||||||
|
return isIntersecting;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default useOnScreen;
|
||||||
|
|
@ -2,7 +2,8 @@ import { createSlice, createAsyncThunk } from '@reduxjs/toolkit';
|
||||||
import { getLobbyChatMessages, createLobbyChatMessage } from '../../helpers/rest';
|
import { getLobbyChatMessages, createLobbyChatMessage } from '../../helpers/rest';
|
||||||
|
|
||||||
export const fetchLobbyChatMessages = createAsyncThunk('chatMessage/fetchLobbyChatMessages', async (options, thunkAPI) => {
|
export const fetchLobbyChatMessages = createAsyncThunk('chatMessage/fetchLobbyChatMessages', async (options, thunkAPI) => {
|
||||||
const response = await getLobbyChatMessages(options);
|
const params = { ...options, type: 'CHAT_MESSAGE', channel: 'lobby'}
|
||||||
|
const response = await getLobbyChatMessages(params);
|
||||||
return response.json();
|
return response.json();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -14,9 +15,13 @@ export const postNewChatMessage = createAsyncThunk('chatMessage/postNewChatMessa
|
||||||
const chatMessagesSlice = createSlice({
|
const chatMessagesSlice = createSlice({
|
||||||
name: 'chatMessages',
|
name: 'chatMessages',
|
||||||
initialState: {
|
initialState: {
|
||||||
messages: [],
|
records: {
|
||||||
|
messages: [],
|
||||||
|
next: null
|
||||||
|
},
|
||||||
status: 'idel',
|
status: 'idel',
|
||||||
error: null
|
error: null,
|
||||||
|
create_status: 'idel',
|
||||||
},
|
},
|
||||||
reducers: {
|
reducers: {
|
||||||
addMessage(state, action) {
|
addMessage(state, action) {
|
||||||
|
|
@ -29,16 +34,33 @@ const chatMessagesSlice = createSlice({
|
||||||
state.status = 'loading';
|
state.status = 'loading';
|
||||||
})
|
})
|
||||||
.addCase(fetchLobbyChatMessages.fulfilled, (state, action) => {
|
.addCase(fetchLobbyChatMessages.fulfilled, (state, action) => {
|
||||||
const records = [...state.messages, ...action.payload.messages];
|
console.log('_DEBUG_1 fetchLobbyChatMessages', action.payload);
|
||||||
state.messages = records;
|
//let chats = [...state.records.messages, ...action.payload.chats];
|
||||||
|
const lastOnly = action.meta.arg.lastOnly;
|
||||||
|
console.log('_DEBUG_2 fetchLobbyChatMessages', lastOnly);
|
||||||
|
state.records = {
|
||||||
|
next: state.records.next === null && lastOnly? null : action.payload.next,
|
||||||
|
messages: action.payload.chats.map(m => ({...m, status: 'delivered'})).sort((a, b) => {
|
||||||
|
return new Date(a.created_at) - new Date(b.created_at);
|
||||||
|
})
|
||||||
|
};
|
||||||
|
// state.offset_messages = action.payload.chats.sort((a, b) => {
|
||||||
|
// return new Date(a.created_at) - new Date(b.created_at);
|
||||||
|
// });
|
||||||
state.status = 'succeeded';
|
state.status = 'succeeded';
|
||||||
})
|
})
|
||||||
.addCase(fetchLobbyChatMessages.rejected, (state, action) => {
|
.addCase(fetchLobbyChatMessages.rejected, (state, action) => {
|
||||||
state.error = action.payload;
|
state.error = action.payload;
|
||||||
state.status = 'failed';
|
state.status = 'failed';
|
||||||
})
|
})
|
||||||
|
.addCase(postNewChatMessage.pending, (state, action) => {
|
||||||
|
state.create_status = 'loading';
|
||||||
|
})
|
||||||
.addCase(postNewChatMessage.fulfilled, (state, action) => {
|
.addCase(postNewChatMessage.fulfilled, (state, action) => {
|
||||||
state.messages.push(action.payload);
|
state.create_status = 'succeeded';
|
||||||
|
})
|
||||||
|
.addCase(postNewChatMessage.rejected, (state, action) => {
|
||||||
|
state.create_status = 'failed';
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -93,6 +93,8 @@ module JamRuby
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
chat_msg.valid?
|
||||||
|
Rails.logger.info "Chat Message: #{chat_msg.inspect} #{chat_msg.errors.inspect}"
|
||||||
if chat_msg.save
|
if chat_msg.save
|
||||||
ChatMessage.send_chat_msg music_session, chat_msg, source_user, client_id, channel, lesson_session, purpose, target_user, music_notation, recording
|
ChatMessage.send_chat_msg music_session, chat_msg, source_user, client_id, channel, lesson_session, purpose, target_user, music_notation, recording
|
||||||
else
|
else
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue