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 useScript from './hooks/useScript';
|
||||||
|
|
||||||
|
import { useDispatch } from "react-redux";
|
||||||
|
//import { addMessage } from "./store/features/textMessagesSlice"
|
||||||
|
|
||||||
const App = () => {
|
const App = () => {
|
||||||
|
|
||||||
function initJKScripts() {
|
function initJKScripts() {
|
||||||
|
|
@ -38,13 +41,18 @@ const App = () => {
|
||||||
|
|
||||||
function registerTextMessageCallback(){
|
function registerTextMessageCallback(){
|
||||||
window.JK.JamServer.registerMessageCallback(window.JK.MessageType.TEXT_MESSAGE, function(header, payload) {
|
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);
|
// chatMessageReceived(payload);
|
||||||
// context.ChatActions.msgReceived(payload);
|
// context.ChatActions.msgReceived(payload);
|
||||||
// handledNotification(payload);
|
// handledNotification(payload);
|
||||||
|
|
||||||
|
//dispatch(addMessage())
|
||||||
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const dispatch = useDispatch()
|
||||||
|
|
||||||
useScript(`${process.env.REACT_APP_LEGACY_BASE_URL}/client_scripts`, initJKScripts);
|
useScript(`${process.env.REACT_APP_LEGACY_BASE_URL}/client_scripts`, initJKScripts);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,8 @@ import { AuthProvider } from "./context/AuthContext";
|
||||||
import { settings } from './config';
|
import { settings } from './config';
|
||||||
import toggleStylesheet from './helpers/toggleStylesheet';
|
import toggleStylesheet from './helpers/toggleStylesheet';
|
||||||
import { getItemFromStore, setItemToStore, themeColors } from './helpers/utils';
|
import { getItemFromStore, setItemToStore, themeColors } from './helpers/utils';
|
||||||
|
import store from './store/store';
|
||||||
|
import { Provider } from 'react-redux';
|
||||||
|
|
||||||
const Main = props => {
|
const Main = props => {
|
||||||
const [isFluid, setIsFluid] = useState(getItemFromStore('isFluid', settings.isFluid));
|
const [isFluid, setIsFluid] = useState(getItemFromStore('isFluid', settings.isFluid));
|
||||||
|
|
@ -117,7 +119,9 @@ const Main = props => {
|
||||||
|
|
||||||
return <AppContext.Provider value={value}>
|
return <AppContext.Provider value={value}>
|
||||||
<AuthProvider>
|
<AuthProvider>
|
||||||
{props.children}
|
<Provider store={store}>
|
||||||
|
{props.children}
|
||||||
|
</Provider>
|
||||||
</AuthProvider>
|
</AuthProvider>
|
||||||
</AppContext.Provider>;
|
</AppContext.Provider>;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -45,7 +45,7 @@ const NavbarTop = () => {
|
||||||
</span>
|
</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<Logo at="navbar-top" id="topLogo" />
|
<Logo at="navbar-top" id="topLogo" width={180} />
|
||||||
{/* {isTopNav ? (
|
{/* {isTopNav ? (
|
||||||
<Collapse navbar isOpen={navbarCollapsed} className="scrollbar">
|
<Collapse navbar isOpen={navbarCollapsed} className="scrollbar">
|
||||||
<Nav navbar>
|
<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 { Modal, ModalHeader, ModalBody, Row, Col, Button, ModalFooter } from 'reactstrap';
|
||||||
import { Scrollbar } from 'react-scrollbars-custom';
|
import { Scrollbar } from 'react-scrollbars-custom';
|
||||||
|
import TimeAgo from 'react-timeago';
|
||||||
import JKProfileAvatar from './JKProfileAvatar';
|
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 JKMessageModal = props => {
|
||||||
const { show, setShow, user } = 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 LIMIT = 20;
|
||||||
|
|
||||||
const fetchMessages = async () => {
|
const [offset, setOffset] = useState(0);
|
||||||
await getTextMessages({
|
const [newMessage, setNewMessage] = useState('');
|
||||||
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 sendMessage = () => {
|
const toggle = () => setShow(!show);
|
||||||
const params = { message: newMessage, target_user_id: user.id }
|
|
||||||
console.log("Sending new message", params);
|
const { currentUser } = useAuth();
|
||||||
createTextMessage(params)
|
const dispatch = useDispatch();
|
||||||
.then(resp => console.log(resp))
|
const scrollbar = useRef();
|
||||||
.catch(error => console.log(error))
|
|
||||||
|
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(() => {
|
useEffect(() => {
|
||||||
if (show) {
|
if (show) {
|
||||||
console.log('JKMessageModal User', user.id);
|
//console.log('JKMessageModal User', user.id);
|
||||||
fetchMessages();
|
fetchMessages();
|
||||||
}
|
}
|
||||||
}, [show]);
|
}, [show, dispatch]);
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Modal isOpen={show} toggle={toggle}>
|
<Modal isOpen={show} toggle={toggle}>
|
||||||
<ModalHeader toggle={toggle}>Conversation with {user.name}</ModalHeader>
|
<ModalHeader toggle={toggle}>Conversation with {user.name}</ModalHeader>
|
||||||
<ModalBody>
|
<ModalBody>
|
||||||
<Scrollbar style={{ width: '100%', height: 400 }}>
|
<Scrollbar ref={scrollbar} style={{ width: '100%', height: 200 }}>
|
||||||
{messages.map((message, index) => (
|
{messages.map((message, index) => (
|
||||||
<div className="d-flex mb-2 mr-1" key={message.id}>
|
<div className="d-flex mb-3 mr-1" key={message.id}>
|
||||||
<div className="avatar avatar-2xl d-inline-block me-2 mr-2">
|
<div className="avatar avatar-2xl d-inline-block">
|
||||||
<JKProfileAvatar url={user.photo_url} />
|
<JKProfileAvatar url={ message.receiverId === currentUser.id ? currentUser.photo_url : user.photo_url} />
|
||||||
</div>
|
</div>
|
||||||
|
<div className="d-inline-block">
|
||||||
<div className="d-inline-block ml-2 ms-2 mb-0">
|
<div className="d-flex flex-column">
|
||||||
<p className="mb-0">{message.message}</p>
|
<div>
|
||||||
<time className="notification-time">{message.created_at}</time>
|
<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>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</Scrollbar>
|
</Scrollbar>
|
||||||
<Row>
|
<Row>
|
||||||
<Col>
|
<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>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
</ModalBody>
|
</ModalBody>
|
||||||
|
|
||||||
<ModalFooter>
|
<ModalFooter>
|
||||||
<Button onClick={toggle}>Close</Button>
|
<Button onClick={toggle}>Close</Button>
|
||||||
<Button color="primary" onClick={sendMessage}>Send</Button>
|
<Button color="primary" onClick={sendMessage} disabled={!newMessage}>
|
||||||
|
Send
|
||||||
|
</Button>
|
||||||
</ModalFooter>
|
</ModalFooter>
|
||||||
</Modal>
|
</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,
|
msg_is_clipped,
|
||||||
notification.id,
|
notification.id,
|
||||||
notification.created_date)
|
notification.created_date)
|
||||||
|
logger.debug('-' * 30)
|
||||||
|
logger.debug(msg)
|
||||||
|
logger.debug('-' * 30)
|
||||||
@@mq_router.publish_to_user(receiver.id, msg)
|
@@mq_router.publish_to_user(receiver.id, msg)
|
||||||
|
|
||||||
else
|
else
|
||||||
|
|
|
||||||
|
|
@ -2,10 +2,10 @@ collection @text_messages
|
||||||
|
|
||||||
attributes :id, :source_user_id, :target_user_id, :message, :created_at
|
attributes :id, :source_user_id, :target_user_id, :message, :created_at
|
||||||
|
|
||||||
node :source_user do |msg|
|
child :source_user => :source_user do |msg|
|
||||||
attributes :id, :name
|
attributes :id, :name, :first_name
|
||||||
end
|
end
|
||||||
|
|
||||||
node :target_user do |msg|
|
child :target_user => :target_user do |msg|
|
||||||
attributes :id, :name
|
attributes :id, :name, :first_name
|
||||||
end
|
end
|
||||||
Loading…
Reference in New Issue