adding redux toolkit for state management

This commit is contained in:
Nuwan Chathuranga 2021-09-14 22:47:31 +05:30 committed by Nuwan
parent 4c69f76e42
commit 7c60153c21
8 changed files with 201 additions and 51 deletions

View File

@ -8,6 +8,9 @@ import 'react-image-lightbox/style.css';
import useScript from './hooks/useScript';
import { useDispatch } from "react-redux";
//import { addMessage } from "./store/features/textMessagesSlice"
const App = () => {
function initJKScripts() {
@ -38,13 +41,18 @@ const App = () => {
function registerTextMessageCallback(){
window.JK.JamServer.registerMessageCallback(window.JK.MessageType.TEXT_MESSAGE, function(header, payload) {
console.log('Handling CHAT_MESSAGE msg ' + JSON.stringify(payload));
console.log('Handling TEXT_MESSAGE ' + JSON.stringify(payload));
// chatMessageReceived(payload);
// context.ChatActions.msgReceived(payload);
// handledNotification(payload);
//dispatch(addMessage())
});
}
const dispatch = useDispatch()
useScript(`${process.env.REACT_APP_LEGACY_BASE_URL}/client_scripts`, initJKScripts);
return (

View File

@ -5,6 +5,8 @@ import { AuthProvider } from "./context/AuthContext";
import { settings } from './config';
import toggleStylesheet from './helpers/toggleStylesheet';
import { getItemFromStore, setItemToStore, themeColors } from './helpers/utils';
import store from './store/store';
import { Provider } from 'react-redux';
const Main = props => {
const [isFluid, setIsFluid] = useState(getItemFromStore('isFluid', settings.isFluid));
@ -117,7 +119,9 @@ const Main = props => {
return <AppContext.Provider value={value}>
<AuthProvider>
<Provider store={store}>
{props.children}
</Provider>
</AuthProvider>
</AppContext.Provider>;
};

View File

@ -45,7 +45,7 @@ const NavbarTop = () => {
</span>
</button>
</div>
<Logo at="navbar-top" id="topLogo" />
<Logo at="navbar-top" id="topLogo" width={180} />
{/* {isTopNav ? (
<Collapse navbar isOpen={navbarCollapsed} className="scrollbar">
<Nav navbar>

View File

@ -1,81 +1,120 @@
import React, { useEffect, useState } from 'react';
import React, { useEffect, useState, useRef } from 'react';
import { Modal, ModalHeader, ModalBody, Row, Col, Button, ModalFooter } from 'reactstrap';
import { Scrollbar } from 'react-scrollbars-custom';
import TimeAgo from 'react-timeago';
import JKProfileAvatar from './JKProfileAvatar';
import { getTextMessages, createTextMessage } from '../../helpers/rest';
import { useAuth } from '../../context/AuthContext';
import { useDispatch, useSelector } from 'react-redux';
import { fetchMessagesByReceiverId, postNewMessage } from '../../store/features/textMessagesSlice';
const JKMessageModal = props => {
const { show, setShow, user } = props;
const [offset, setOffset] = useState(0);
const [messages, setMessages] = useState([]);
const [newMessage, setNewMessage] = useState("");
const toggle = () => setShow(!show);
const LIMIT = 20;
const fetchMessages = async () => {
await getTextMessages({
target_user_id: user.id,
offset: offset,
limit: LIMIT
})
.then(resp => {
if (resp.ok) {
return resp.json();
} else {
}
})
.then(json => {
console.log(json);
setMessages(json);
})
.catch(error => console.log(error));
}
const [offset, setOffset] = useState(0);
const [newMessage, setNewMessage] = useState('');
const sendMessage = () => {
const params = { message: newMessage, target_user_id: user.id }
console.log("Sending new message", params);
createTextMessage(params)
.then(resp => console.log(resp))
.catch(error => console.log(error))
const toggle = () => setShow(!show);
const { currentUser } = useAuth();
const dispatch = useDispatch();
const scrollbar = useRef();
const messages = useSelector(state =>
state.textMessage.messages
.filter(
message =>
(message.senderId === user.id && message.receiverId === currentUser.id) ||
(message.senderId === currentUser.id && message.receiverId === user.id)
)
.reverse()
);
const fetchMessages = async () => {
try {
await dispatch(fetchMessagesByReceiverId(user.id)).unwrap();
scrollbar.current.scrollToBottom();
} catch (err) {
console.log('ERROR', err);
}
};
const sendMessage = async () => {
try {
let msgData = {
message: newMessage,
target_user_id: user.id,
source_user_id: currentUser.id
};
setNewMessage('');
await dispatch(postNewMessage(msgData)).unwrap();
fetchMessages();
} catch (err) {
console.log('addNewMessage error', err);
} finally {
}
};
const handleOnKeyPress = (event) => {
//console.log('event', event.key);
if(event.key === 'Enter' || event.key === "NumpadEnter"){
sendMessage()
}
}
useEffect(() => {
if (show) {
console.log('JKMessageModal User', user.id);
//console.log('JKMessageModal User', user.id);
fetchMessages();
}
}, [show]);
}, [show, dispatch]);
return (
<>
<Modal isOpen={show} toggle={toggle}>
<ModalHeader toggle={toggle}>Conversation with {user.name}</ModalHeader>
<ModalBody>
<Scrollbar style={{ width: '100%', height: 400 }}>
<Scrollbar ref={scrollbar} style={{ width: '100%', height: 200 }}>
{messages.map((message, index) => (
<div className="d-flex mb-2 mr-1" key={message.id}>
<div className="avatar avatar-2xl d-inline-block me-2 mr-2">
<JKProfileAvatar url={user.photo_url} />
<div className="d-flex mb-3 mr-1" key={message.id}>
<div className="avatar avatar-2xl d-inline-block">
<JKProfileAvatar url={ message.receiverId === currentUser.id ? currentUser.photo_url : user.photo_url} />
</div>
<div className="d-inline-block">
<div className="d-flex flex-column">
<div>
<strong>{message.senderName}</strong>
<time className="notification-time ml-2 t-1">
<TimeAgo
date={message.createdAt}
formatter={(value, unit) => {
if (unit === 'second' && value < 15) return 'just now';
if (unit === 'second') return 'few seconds ago';
if (unit === 'minute') return `${value} ${value === 1 ? 'minute' : 'minutes'} ago`;
if (unit === 'hour') return `${value} ${value === 1 ? 'hour' : 'hours'} ago`;
if (unit === 'day') return `${value} ${value === 1 ? 'day' : 'days'} ago`;
}}
/>
</time>
</div>
<div>{message.message}</div>
</div>
<div className="d-inline-block ml-2 ms-2 mb-0">
<p className="mb-0">{message.message}</p>
<time className="notification-time">{message.created_at}</time>
</div>
</div>
))}
</Scrollbar>
<Row>
<Col>
<textarea style={{ width: '100%' }} value={newMessage} onChange={(e) => setNewMessage(e.target.value) } />
<textarea style={{ width: '100%' }} value={newMessage} onChange={e => setNewMessage(e.target.value)} onKeyPress={handleOnKeyPress} />
</Col>
</Row>
</ModalBody>
<ModalFooter>
<Button onClick={toggle}>Close</Button>
<Button color="primary" onClick={sendMessage}>Send</Button>
<Button color="primary" onClick={sendMessage} disabled={!newMessage}>
Send
</Button>
</ModalFooter>
</Modal>
</>

View File

@ -0,0 +1,89 @@
import { createSlice, createAsyncThunk, nanoid } from "@reduxjs/toolkit"
import { getTextMessages, createTextMessage } from '../../helpers/rest';
const initialState = {
messages: [],
status: 'idel',
error: null,
offset: 0
}
// const [offset, setOffset] = useState(0);
// const LIMIT = 20;
export const fetchMessagesByReceiverId = createAsyncThunk(
'textMessage/fetchMessagesByReceiverId',
async (userId, thunkAPI) => {
const response = await getTextMessages({
target_user_id: userId
// offset: offset,
// limit: LIMIT
})
return response.json()
}
)
export const resturectureTextMessage = (args) => {
const { payload, sent } = args
//console.log(payload);
const messageId = payload.id ? payload.id : nanoid()
const createdAt = payload.created_at ? payload.created_at : new Date().toISOString()
return {
id: messageId,
message: payload.message,
senderId: payload.source_user_id,
senderName: payload.source_user['first_name'],
receiverId: payload.target_user_id,
receiverName: payload.target_user['first_name'],
createdAt: createdAt,
sent: sent
}
}
export const postNewMessage = createAsyncThunk(
'textMessage/postNewMessage',
async (message, thunkAPI) => {
const response = await createTextMessage(message)
return { status: response.status, payload: message }
}
)
export const textMessageSlice = createSlice({
name: 'textMessage',
initialState,
reducers: {
addMessage: (state, action) => {
state.messages.push(action.payload)
},
updateMessage: state => {},
deleteMessage: state => {}
},
extraReducers: (builder) => {
builder
.addCase(fetchMessagesByReceiverId.pending, (state, action) => {
state.status = 'loading'
})
.addCase(fetchMessagesByReceiverId.fulfilled, (state, action) => {
state.status = 'succeeded'
console.log(action.payload);
state.messages = action.payload.map(message => resturectureTextMessage({ payload: message, sent: true }))
})
.addCase(fetchMessagesByReceiverId.rejected, (state, action) => {
state.status = 'failed'
state.error = action.error.message
})
.addCase(postNewMessage.fulfilled, (state, action) => {
console.log("postNewMessage fullfilled", action.payload);
})
}
})
export const { addMessage, updateMessage, deleteMessage } = textMessageSlice.actions
export default textMessageSlice.reducer
//export const selectAllMessages = state => state.textMessage.messages
//export const selectMessageById = (state, messageId) => state.textMessage.messages.find(message => message.id === messageId)
//export const selectMessagesBySenderId = (state, senderId) => state.textMessage.messages.filter(message => message.senderId === senderId)

View File

@ -0,0 +1,8 @@
import { configureStore } from "@reduxjs/toolkit"
import textMessageReducer from "./features/textMessagesSlice"
export default configureStore({
reducer: {
textMessage: textMessageReducer
}
})

View File

@ -1543,7 +1543,9 @@ module JamRuby
msg_is_clipped,
notification.id,
notification.created_date)
logger.debug('-' * 30)
logger.debug(msg)
logger.debug('-' * 30)
@@mq_router.publish_to_user(receiver.id, msg)
else

View File

@ -2,10 +2,10 @@ collection @text_messages
attributes :id, :source_user_id, :target_user_id, :message, :created_at
node :source_user do |msg|
attributes :id, :name
child :source_user => :source_user do |msg|
attributes :id, :name, :first_name
end
node :target_user do |msg|
attributes :id, :name
child :target_user => :target_user do |msg|
attributes :id, :name, :first_name
end