adding redux toolkit for state management
This commit is contained in:
parent
4c69f76e42
commit
7c60153c21
|
|
@ -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 (
|
||||
|
|
|
|||
|
|
@ -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>;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
</>
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
import { configureStore } from "@reduxjs/toolkit"
|
||||
import textMessageReducer from "./features/textMessagesSlice"
|
||||
|
||||
export default configureStore({
|
||||
reducer: {
|
||||
textMessage: textMessageReducer
|
||||
}
|
||||
})
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
Loading…
Reference in New Issue