Refactored Messaging as a part of BOD-14. Breaking changes remain.

This commit is contained in:
Patrick Fic
2020-04-29 16:46:23 -07:00
parent 0bc8d95120
commit dcfcf71ca4
24 changed files with 393 additions and 549 deletions

View File

@@ -0,0 +1,43 @@
import { MessageOutlined } from "@ant-design/icons";
import { Badge, Card } from "antd";
import React from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { toggleChatVisible } from "../../redux/messaging/messaging.actions";
import { selectChatVisible } from "../../redux/messaging/messaging.selectors";
import ChatPopupComponent from '../chat-popup/chat-popup.component'
const mapStateToProps = createStructuredSelector({
chatVisible: selectChatVisible,
});
const mapDispatchToProps = (dispatch) => ({
toggleChatVisible: () => dispatch(toggleChatVisible()),
});
export function ChatAffixComponent({
chatVisible,
toggleChatVisible,
conversationList,
unreadCount,
}) {
const { t } = useTranslation();
return (
<Badge count={unreadCount}>
<Card size='small'>
{chatVisible ? (
<ChatPopupComponent conversationList={conversationList} />
) : (
<div
onClick={() => toggleChatVisible()}
style={{ cursor: "pointer" }}>
<MessageOutlined />
<strong>{t("messaging.labels.messaging")}</strong>
</div>
)}
</Card>
</Badge>
);
}
export default connect(mapStateToProps, mapDispatchToProps)(ChatAffixComponent);

View File

@@ -0,0 +1,32 @@
import { useSubscription } from "@apollo/react-hooks";
import React from "react";
import { CONVERSATION_LIST_SUBSCRIPTION } from "../../graphql/conversations.queries";
import AlertComponent from "../alert/alert.component";
import LoadingSpinner from "../loading-spinner/loading-spinner.component";
import ChatAffixComponent from "./chat-affix.component";
import { Affix } from "antd";
export default function ChatAffixContainer() {
const { loading, error, data } = useSubscription(
CONVERSATION_LIST_SUBSCRIPTION
);
if (loading) return <LoadingSpinner />;
if (error) return <AlertComponent message={error.message} type='error' />;
return (
<Affix offsetBottom={5}>
<div>
<ChatAffixComponent
conversationList={(data && data.conversations) || []}
unreadCount={
(data &&
data.conversations.reduce((acc, val) => {
return (acc = acc + val.messages_aggregate.aggregate.count);
}, 0)) ||
0
}
/>
</div>
</Affix>
);
}

View File

@@ -1,41 +1,45 @@
import { ShrinkOutlined } from "@ant-design/icons";
import { Badge } from "antd";
import { Badge, List } from "antd";
import React from "react";
import { connect } from "react-redux";
import {
openConversation,
toggleChatVisible
} from "../../redux/messaging/messaging.actions";
import PhoneNumberFormatter from "../../utils/PhoneFormatter";
import { setSelectedConversation } from "../../redux/messaging/messaging.actions";
import { selectSelectedConversation } from "../../redux/messaging/messaging.selectors";
import { createStructuredSelector } from "reselect";
import "./chat-conversation-list.styles.scss";
const mapDispatchToProps = dispatch => ({
toggleChatVisible: () => dispatch(toggleChatVisible()),
openConversation: number => dispatch(openConversation(number))
const mapStateToProps = createStructuredSelector({
selectedConversation: selectSelectedConversation,
});
const mapDispatchToProps = (dispatch) => ({
setSelectedConversation: (conversationId) =>
dispatch(setSelectedConversation(conversationId)),
});
export function ChatConversationListComponent({
toggleChatVisible,
conversationList,
openConversation
selectedConversation,
setSelectedConversation,
}) {
return (
<div className='chat-overlay-open'>
<ShrinkOutlined onClick={() => toggleChatVisible()} />
{conversationList.map(item => (
<Badge count={item.messages_aggregate.aggregate.count || 0}>
<div
key={item.id}
style={{ cursor: "pointer", display: "block" }}
onClick={() =>
openConversation({ phone_num: item.phone_num, id: item.id })
}>
<div>
<PhoneNumberFormatter>{item.phone_num}</PhoneNumberFormatter>
</div>
</div>
</Badge>
))}
</div>
<List
bordered
dataSource={conversationList}
renderItem={(item) => (
<List.Item
onClick={() => setSelectedConversation(item.id)}
className={`chat-list-item ${
item.id === selectedConversation
? "chat-list-selected-conversation"
: null
}`}>
{item.phone_num}
<Badge count={item.messages_aggregate.aggregate.count || 0} />
</List.Item>
)}
/>
);
}
export default connect(null, mapDispatchToProps)(ChatConversationListComponent);
export default connect(
mapStateToProps,
mapDispatchToProps
)(ChatConversationListComponent);

View File

@@ -0,0 +1,10 @@
.chat-list-selected-conversation {
background-color: rgba(128, 128, 128, 0.2);
}
.chat-list-item {
&:hover {
cursor: pointer;
color: #ff7a00;
}
}

View File

@@ -1,38 +0,0 @@
import { CloseCircleFilled } from "@ant-design/icons";
import React from "react";
import { connect } from "react-redux";
import {
closeConversation,
sendMessage,
toggleConversationVisible
} from "../../redux/messaging/messaging.actions";
import PhoneFormatter from "../../utils/PhoneFormatter";
const mapDispatchToProps = dispatch => ({
toggleConversationVisible: conversationId =>
dispatch(toggleConversationVisible(conversationId)),
closeConversation: phone => dispatch(closeConversation(phone)),
sendMessage: message => dispatch(sendMessage(message))
});
function ChatConversationClosedComponent({
conversation,
toggleConversationVisible,
closeConversation
}) {
return (
<div
className='chat-conversation-closed'
onClick={() => toggleConversationVisible(conversation.id)}>
<PhoneFormatter>{conversation.phone_num}</PhoneFormatter>
<CloseCircleFilled
onClick={() => closeConversation(conversation.phone_num)}
/>
</div>
);
}
export default connect(
null,
mapDispatchToProps
)(ChatConversationClosedComponent);

View File

@@ -1,27 +1,27 @@
import { Badge, Card } from "antd";
import React from "react";
import ChatConversationClosedComponent from "./chat-conversation.closed.component";
import ChatConversationOpenComponent from "./chat-conversation.open.component";
import ChatMessageListComponent from "../chat-messages-list/chat-message-list.component";
import ChatSendMessage from "../chat-send-message/chat-send-message.component";
import LoadingSkeleton from "../loading-skeleton/loading-skeleton.component.jsx";
import AlertComponent from "../alert/alert.component";
export default function ChatConversationComponent({
conversation,
messages,
subState,
unreadCount
conversation,
unreadCount,
}) {
const [loading, error] = subState;
if (loading) return <LoadingSkeleton />;
if (error) return <AlertComponent message={error.message} type='error' />;
return (
<div className='chat-conversation'>
<Badge count={unreadCount}>
<ChatSendMessage conversation={conversation} />
<Card size='small'>
{conversation.open ? (
<ChatConversationOpenComponent
messages={messages}
conversation={conversation}
subState={subState}
/>
) : (
<ChatConversationClosedComponent conversation={conversation} />
)}
<ChatMessageListComponent messages={messages} />
</Card>
</Badge>
</div>

View File

@@ -3,18 +3,30 @@ import React from "react";
import { CONVERSATION_SUBSCRIPTION_BY_PK } from "../../graphql/conversations.queries";
import ChatConversationComponent from "./chat-conversation.component";
export default function ChatConversationContainer({ conversation }) {
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { selectSelectedConversation } from "../../redux/messaging/messaging.selectors";
const mapStateToProps = createStructuredSelector({
selectedConversation: selectSelectedConversation,
});
export default connect(mapStateToProps, null)(ChatConversationContainer);
export function ChatConversationContainer({ selectedConversation }) {
console.log(
"ChatConversationContainer -> selectedConversation",
selectedConversation
);
const { loading, error, data } = useSubscription(
CONVERSATION_SUBSCRIPTION_BY_PK,
{
variables: { conversationId: conversation.id }
variables: { conversationId: selectedConversation },
}
);
return (
<ChatConversationComponent
subState={[loading, error]}
conversation={conversation}
unreadCount={
(data &&
data.conversations_by_pk &&
@@ -23,6 +35,14 @@ export default function ChatConversationContainer({ conversation }) {
data.conversations_by_pk.messages_aggregate.aggregate.count) ||
0
}
conversation={{
conversationId: selectedConversation,
phone_num:
(data &&
data.conversations_by_pk &&
data.conversations_by_pk.phone_num) ||
"",
}}
messages={
(data &&
data.conversations_by_pk &&

View File

@@ -1,36 +0,0 @@
import React from "react";
import { connect } from "react-redux";
import { toggleConversationVisible } from "../../redux/messaging/messaging.actions";
import AlertComponent from "../alert/alert.component";
import ChatMessageListComponent from "../chat-messages-list/chat-message-list.component";
import ChatSendMessage from "../chat-send-message/chat-send-message.component";
import LoadingSpinner from "../loading-spinner/loading-spinner.component";
import { ShrinkOutlined } from "@ant-design/icons";
const mapDispatchToProps = dispatch => ({
toggleConversationVisible: conversation =>
dispatch(toggleConversationVisible(conversation))
});
export function ChatConversationOpenComponent({
conversation,
messages,
subState,
toggleConversationVisible
}) {
const [loading, error] = subState;
if (loading) return <LoadingSpinner />;
if (error) return <AlertComponent message={error.message} type='error' />;
return (
<div className='chat-conversation-open'>
<ShrinkOutlined
onClick={() => toggleConversationVisible(conversation.id)}
/>
<ChatMessageListComponent messages={messages} />
<ChatSendMessage conversation={conversation} />
</div>
);
}
export default connect(null, mapDispatchToProps)(ChatConversationOpenComponent);

View File

@@ -1,32 +0,0 @@
import { Affix } from "antd";
import React from "react";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { selectConversations } from "../../redux/messaging/messaging.selectors";
import ChatConversationContainer from "../chat-conversation/chat-conversation.container";
import ChatMessagesButtonContainer from "../chat-messages-button/chat-messages-button.container";
import "./chat-dock.styles.scss";
const mapStateToProps = createStructuredSelector({
activeConversations: selectConversations
});
export function ChatOverlayContainer({ activeConversations }) {
return (
<Affix offsetBottom={0}>
<div className='chat-dock'>
<ChatMessagesButtonContainer />
{activeConversations
? activeConversations.map(conversation => (
<ChatConversationContainer
conversation={conversation}
key={conversation.id}
/>
))
: null}
</div>
</Affix>
);
}
export default connect(mapStateToProps, null)(ChatOverlayContainer);

View File

@@ -1,188 +0,0 @@
.chat-dock {
z-index: 5;
//overflow-x: scroll;
// overflow-y: hidden;
width: 100%;
display: flex;
align-items: baseline;
}
.chat-conversation {
margin: 2em 1em 0em 1em;
}
.chat-conversation-open {
height: 500px;
}
// .chat-messages {
// height: 80%;
// overflow-x: hidden;
// overflow-y: scroll;
// flex-grow: 1;
// ul {
// list-style: none;
// margin: 0;
// padding: 0;
// }
// ul li {
// display: inline-block;
// clear: both;
// padding: 3px 10px;
// border-radius: 30px;
// margin-bottom: 2px;
// }
// .inbound {
// background: #eee;
// float: left;
// }
// .outbound {
// float: right;
// background: #0084ff;
// color: #fff;
// }
// .inbound + .outbound {
// border-bottom-right-radius: 5px;
// }
// .outbound + .outbound {
// border-top-right-radius: 5px;
// border-bottom-right-radius: 5px;
// }
// .outbound:last-of-type {
// border-bottom-right-radius: 30px;
// }
// }
.messages {
height: auto;
min-height: calc(100% - 10px);
max-height: calc(100% - 93px);
overflow-y: scroll;
overflow-x: hidden;
}
@media screen and (max-width: 735px) {
.messages {
max-height: calc(100% - 105px);
}
}
.messages::-webkit-scrollbar {
width: 8px;
background: transparent;
}
.messages::-webkit-scrollbar-thumb {
background-color: rgba(0, 0, 0, 0.3);
}
.messages ul li {
display: inline-block;
clear: both;
//float: left;
margin: 5px;
width: calc(100% - 25px);
font-size: 0.9em;
}
.messages ul li:nth-last-child(1) {
margin-bottom: 20px;
}
.messages ul li.sent img {
margin: 6px 8px 0 0;
}
.messages ul li.sent p {
background: #435f7a;
color: #f5f5f5;
}
.messages ul li.replies img {
float: right;
margin: 6px 0 0 8px;
}
.messages ul li.replies p {
background: #f5f5f5;
float: right;
}
.messages ul li img {
width: 22px;
border-radius: 50%;
float: left;
}
.messages ul li p {
display: inline-block;
padding: 10px 15px;
border-radius: 20px;
max-width: 205px;
line-height: 130%;
}
@media screen and (min-width: 735px) {
.messages ul li p {
max-width: 300px;
}
}
.message-input {
position: absolute;
bottom: 0;
width: 100%;
z-index: 99;
}
.message-input .wrap {
position: relative;
}
.message-input .wrap input {
font-family: "proxima-nova", "Source Sans Pro", sans-serif;
float: left;
border: none;
width: calc(100% - 90px);
padding: 11px 32px 10px 8px;
font-size: 0.8em;
color: #32465a;
}
@media screen and (max-width: 735px) {
.message-input .wrap input {
padding: 15px 32px 16px 8px;
}
}
.message-input .wrap input:focus {
outline: none;
}
.message-input .wrap .attachment {
position: absolute;
right: 60px;
z-index: 4;
margin-top: 10px;
font-size: 1.1em;
color: #435f7a;
opacity: 0.5;
cursor: pointer;
}
@media screen and (max-width: 735px) {
.message-input .wrap .attachment {
margin-top: 17px;
right: 65px;
}
}
.message-input .wrap .attachment:hover {
opacity: 1;
}
.message-input .wrap button {
float: right;
border: none;
width: 50px;
padding: 12px 0;
cursor: pointer;
background: #32465a;
color: #f5f5f5;
}
@media screen and (max-width: 735px) {
.message-input .wrap button {
padding: 16px 0;
}
}
.message-input .wrap button:hover {
background: #435f7a;
}
.message-input .wrap button:focus {
outline: none;
}

View File

@@ -1,47 +0,0 @@
import { MessageFilled } from "@ant-design/icons";
import { Badge, Card } from "antd";
import React from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { toggleChatVisible } from "../../redux/messaging/messaging.actions";
import { selectChatVisible } from "../../redux/messaging/messaging.selectors";
import ChatConversationListComponent from "../chat-conversation-list/chat-conversation-list.component";
const mapStateToProps = createStructuredSelector({
chatVisible: selectChatVisible
});
const mapDispatchToProps = dispatch => ({
toggleChatVisible: () => dispatch(toggleChatVisible())
});
export function ChatWindowComponent({
chatVisible,
toggleChatVisible,
conversationList,
unreadCount
}) {
const { t } = useTranslation();
return (
<div className='chat-conversation'>
<Badge count={unreadCount}>
<Card size='small'>
{chatVisible ? (
<ChatConversationListComponent
conversationList={conversationList}
/>
) : (
<div onClick={() => toggleChatVisible()}>
<MessageFilled />
<strong>{t("messaging.labels.messaging")}</strong>
</div>
)}
</Card>
</Badge>
</div>
);
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(ChatWindowComponent);

View File

@@ -1,27 +0,0 @@
import { useSubscription } from "@apollo/react-hooks";
import React from "react";
import { CONVERSATION_LIST_SUBSCRIPTION } from "../../graphql/conversations.queries";
import AlertComponent from "../alert/alert.component";
import LoadingSpinner from "../loading-spinner/loading-spinner.component";
import ChatMessagesButtonComponent from "./chat-messages-button.component";
export default function ChatMessagesButtonContainer() {
const { loading, error, data } = useSubscription(
CONVERSATION_LIST_SUBSCRIPTION
);
if (loading) return <LoadingSpinner />;
if (error) return <AlertComponent message={error.message} type='error' />;
return (
<ChatMessagesButtonComponent
conversationList={(data && data.conversations) || []}
unreadCount={
(data &&
data.conversations.reduce((acc, val) => {
return (acc = acc + val.messages_aggregate.aggregate.count);
}, 0)) ||
0
}
/>
);
}

View File

@@ -1,17 +1,17 @@
import { CheckCircleOutlined, CheckOutlined } from "@ant-design/icons";
import React, { useEffect, useRef } from "react";
import "./chat-message-list.styles.scss";
export default function ChatMessageListComponent({ messages }) {
const messagesEndRef = useRef(null);
const scrollToBottom = () => {
console.log("use");
!!messagesEndRef.current &&
messagesEndRef.current.scrollIntoView({ behavior: "smooth" });
};
useEffect(scrollToBottom, [messages]);
const StatusRender = status => {
const StatusRender = (status) => {
switch (status) {
case "sent":
return <CheckOutlined style={{ margin: "2px", float: "right" }} />;
@@ -27,7 +27,7 @@ export default function ChatMessageListComponent({ messages }) {
return (
<div className='messages'>
<ul>
{messages.map(item => (
{messages.map((item) => (
<li
key={item.id}
className={`${item.isoutbound ? "replies" : "sent"}`}>

View File

@@ -0,0 +1,128 @@
.messages {
height: auto;
min-height: calc(100% - 10px);
max-height: calc(100% - 93px);
overflow-y: scroll;
overflow-x: hidden;
}
@media screen and (max-width: 735px) {
.messages {
max-height: calc(100% - 105px);
}
}
.messages::-webkit-scrollbar {
width: 8px;
background: transparent;
}
.messages::-webkit-scrollbar-thumb {
background-color: rgba(0, 0, 0, 0.3);
}
.messages ul li {
display: inline-block;
clear: both;
//float: left;
margin: 5px;
width: calc(100% - 25px);
font-size: 0.9em;
}
.messages ul li:nth-last-child(1) {
margin-bottom: 20px;
}
.messages ul li.sent img {
margin: 6px 8px 0 0;
}
.messages ul li.sent p {
background: #435f7a;
color: #f5f5f5;
}
.messages ul li.replies img {
float: right;
margin: 6px 0 0 8px;
}
.messages ul li.replies p {
background: #f5f5f5;
float: right;
}
.messages ul li img {
width: 22px;
border-radius: 50%;
float: left;
}
.messages ul li p {
display: inline-block;
padding: 10px 15px;
border-radius: 20px;
max-width: 205px;
line-height: 130%;
}
@media screen and (min-width: 735px) {
.messages ul li p {
max-width: 300px;
}
}
.message-input {
position: absolute;
bottom: 0;
width: 100%;
z-index: 99;
}
.message-input .wrap {
position: relative;
}
.message-input .wrap input {
font-family: "proxima-nova", "Source Sans Pro", sans-serif;
float: left;
border: none;
width: calc(100% - 90px);
padding: 11px 32px 10px 8px;
font-size: 0.8em;
color: #32465a;
}
@media screen and (max-width: 735px) {
.message-input .wrap input {
padding: 15px 32px 16px 8px;
}
}
.message-input .wrap input:focus {
outline: none;
}
.message-input .wrap .attachment {
position: absolute;
right: 60px;
z-index: 4;
margin-top: 10px;
font-size: 1.1em;
color: #435f7a;
opacity: 0.5;
cursor: pointer;
}
@media screen and (max-width: 735px) {
.message-input .wrap .attachment {
margin-top: 17px;
right: 65px;
}
}
.message-input .wrap .attachment:hover {
opacity: 1;
}
.message-input .wrap button {
float: right;
border: none;
width: 50px;
padding: 12px 0;
cursor: pointer;
background: #32465a;
color: #f5f5f5;
}
@media screen and (max-width: 735px) {
.message-input .wrap button {
padding: 16px 0;
}
}
.message-input .wrap button:hover {
background: #435f7a;
}
.message-input .wrap button:focus {
outline: none;
}

View File

@@ -1,22 +1,16 @@
import { MessageFilled } from "@ant-design/icons";
import React from "react";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { openConversation } from "../../redux/messaging/messaging.actions";
import { MessageFilled } from "@ant-design/icons";
const mapStateToProps = createStructuredSelector({
//currentUser: selectCurrentUser
const mapDispatchToProps = (dispatch) => ({
//openConversation: (phone) => dispatch(openConversation(phone)),
});
const mapDispatchToProps = dispatch => ({
openConversation: phone => dispatch(openConversation(phone))
});
export default connect(
mapStateToProps,
mapDispatchToProps
)(function ChatOpenButton({ openConversation, phone }) {
export function ChatOpenButton({ phone }) {
return (
<MessageFilled
style={{ margin: 4 }}
onClick={() => openConversation(phone)}
onClick={() => alert("TODO FIX ME" + phone)}
/>
);
});
}
export default connect(null, mapDispatchToProps)(ChatOpenButton);

View File

@@ -0,0 +1,49 @@
import { ShrinkOutlined } from "@ant-design/icons";
import { Col, Row } from "antd";
import React from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { toggleChatVisible } from "../../redux/messaging/messaging.actions";
import ChatConversationListComponent from "../chat-conversation-list/chat-conversation-list.component";
import ChatConversationContainer from "../chat-conversation/chat-conversation.container";
import { selectSelectedConversation } from "../../redux/messaging/messaging.selectors";
import "./chat-popup.styles.scss";
const mapStateToProps = createStructuredSelector({
selectedConversation: selectSelectedConversation,
});
const mapDispatchToProps = (dispatch) => ({
toggleChatVisible: () => dispatch(toggleChatVisible()),
});
export function ChatPopupComponent({
conversationList,
selectedConversation,
toggleChatVisible,
}) {
const { t } = useTranslation();
return (
<div className='chat-popup'>
<div style={{ overflow: "auto" }}>
<strong style={{ float: "left" }}>
{t("messaging.labels.messaging")}
</strong>
<ShrinkOutlined
onClick={() => toggleChatVisible()}
style={{ float: "right" }}
/>
</div>
<Row>
<Col span={8}>
<ChatConversationListComponent conversationList={conversationList} />
</Col>
<Col span={16}>
{selectedConversation ? <ChatConversationContainer /> : null}
</Col>
</Row>
</div>
);
}
export default connect(mapStateToProps, mapDispatchToProps)(ChatPopupComponent);

View File

@@ -0,0 +1,4 @@
.chat-popup {
width: 40vw;
height: 50vh;
}

View File

@@ -8,10 +8,10 @@ import { sendMessage } from "../../redux/messaging/messaging.actions";
import { selectBodyshop } from "../../redux/user/user.selectors";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop
bodyshop: selectBodyshop,
});
const mapDispatchToProps = dispatch => ({
sendMessage: message => dispatch(sendMessage(message))
const mapDispatchToProps = (dispatch) => ({
sendMessage: (message) => dispatch(sendMessage(message)),
});
function ChatSendMessageComponent({ conversation, bodyshop, sendMessage }) {
@@ -29,7 +29,7 @@ function ChatSendMessageComponent({ conversation, bodyshop, sendMessage }) {
to: conversation.phone_num,
body: message,
messagingServiceSid: bodyshop.messagingservicesid,
conversationid: conversation.id
conversationid: conversation.conversationId,
});
};
@@ -43,8 +43,8 @@ function ChatSendMessageComponent({ conversation, bodyshop, sendMessage }) {
value={message}
disabled={conversation.isSending}
placeholder={t("messaging.labels.typeamessage")}
onChange={e => setMessage(e.target.value)}
onPressEnter={event => {
onChange={(e) => setMessage(e.target.value)}
onPressEnter={(event) => {
event.preventDefault();
if (!!!event.shiftKey) handleEnter();
}}
@@ -54,7 +54,7 @@ function ChatSendMessageComponent({ conversation, bodyshop, sendMessage }) {
indicator={
<LoadingOutlined
style={{
fontSize: 24
fontSize: 24,
}}
spin
/>

View File

@@ -32,6 +32,8 @@ export const CONVERSATION_SUBSCRIPTION_BY_PK = gql`
count
}
}
id
phone_num
}
}
`;

View File

@@ -10,6 +10,7 @@ import LoadingSpinner from "../../components/loading-spinner/loading-spinner.com
import "./manage.page.styles.scss";
import BreadCrumbs from "../../components/breadcrumbs/breadcrumbs.component";
import PrintCenterModalContainer from "../../components/print-center-modal/print-center-modal.container";
import ChatAffixContainer from "../../components/chat-affix/chat-affix.container";
const ManageRootPage = lazy(() =>
import("../manage-root/manage-root.page.container")
@@ -22,9 +23,6 @@ const ProfilePage = lazy(() => import("../profile/profile.container.page"));
const JobsAvailablePage = lazy(() =>
import("../jobs-available/jobs-available.page.container")
);
const ChatDockContainer = lazy(() =>
import("../../components/chat-dock/chat-dock.container")
);
const ScheduleContainer = lazy(() =>
import("../schedule/schedule.page.container")
);
@@ -231,7 +229,7 @@ export default function Manage({ match }) {
<Footer>
<FooterComponent />
</Footer>
<ChatDockContainer />
<ChatAffixContainer />
<BackTop />
</Layout>
);

View File

@@ -1,36 +1,25 @@
import MessagingActionTypes from "./messaging.types";
export const toggleChatVisible = () => ({
type: MessagingActionTypes.TOGGLE_CHAT_VISIBLE
type: MessagingActionTypes.TOGGLE_CHAT_VISIBLE,
//payload: user
});
export const toggleConversationVisible = conversationId => ({
type: MessagingActionTypes.TOGGLE_CONVERSATION_VISIBLE,
payload: conversationId
});
export const openConversation = phone => ({
type: MessagingActionTypes.OPEN_CONVERSATION,
payload: phone
});
export const closeConversation = phone => ({
type: MessagingActionTypes.CLOSE_CONVERSATION,
payload: phone
});
export const sendMessage = message => ({
export const sendMessage = (message) => ({
type: MessagingActionTypes.SEND_MESSAGE,
payload: message
payload: message,
});
export const sendMessageSuccess = message => ({
export const sendMessageSuccess = (message) => ({
type: MessagingActionTypes.SEND_MESSAGE_SUCCESS,
payload: message
payload: message,
});
export const sendMessageFailure = error => ({
export const sendMessageFailure = (error) => ({
type: MessagingActionTypes.SEND_MESSAGE_FAILURE,
payload: error
payload: error,
});
export const setSelectedConversation = (conversationId) => ({
type: MessagingActionTypes.SET_SELECTED_CONVERSATION,
payload: conversationId,
});

View File

@@ -2,23 +2,8 @@ import MessagingActionTypes from "./messaging.types";
const INITIAL_STATE = {
visible: false,
unread: 0,
conversations: [
// {
// phone_num: "6049992002",
// id: "519ba10d-6467-4fa5-9c22-59ae891edeb6",
// open: false,
// isSending: false,
// sendingError: null
// },
// {
// phone_num: "6049992991",
// id: "ab57deba-eeb9-40db-b5ae-23f3ce8d7c7b",
// open: false,
// isSending: false,
// sendingError: null
// }
],
selectedConversationId: null,
isSending: false,
error: null,
};
@@ -29,77 +14,24 @@ const messagingReducer = (state = INITIAL_STATE, action) => {
...state,
visible: !state.visible,
};
case MessagingActionTypes.SET_CHAT_VISIBLE:
return {
...state,
visible: true,
};
case MessagingActionTypes.SET_SELECTED_CONVERSATION:
return { ...state, selectedConversationId: action.payload };
case MessagingActionTypes.SEND_MESSAGE:
return {
...state,
conversations: state.conversations.map((c) =>
c.id === action.payload.conversationid ? { ...c, isSending: true } : c
),
error: null,
isSending: true,
};
case MessagingActionTypes.SEND_MESSAGE_SUCCESS:
return {
...state,
conversations: state.conversations.map((c) =>
c.id === action.payload.conversationid
? { ...c, isSending: false }
: c
),
isSending: false,
};
case MessagingActionTypes.SEND_MESSAGE_FAILURE:
return {
...state,
conversations: state.conversations.map((c) =>
c.id === action.payload.conversationid
? { ...c, isSending: false, sendingError: action.payload.error }
: c
),
error: action.payload,
};
case MessagingActionTypes.OPEN_CONVERSATION:
if (
state.conversations.find(
(c) => c.phone_num === action.payload.phone_num
)
)
return {
...state,
conversations: state.conversations.map((c) =>
c.phone_num === action.payload.phone_num ? { ...c, open: true } : c
),
};
else
return {
...state,
conversations: [
...state.conversations,
{
phone_num: action.payload.phone_num,
id: action.payload.id,
open: true,
isSending: false,
sendingError: null,
},
],
};
case MessagingActionTypes.CLOSE_CONVERSATION:
return {
...state,
conversations: state.conversations.filter(
(c) => c.phone_num !== action.payload
),
};
case MessagingActionTypes.TOGGLE_CONVERSATION_VISIBLE:
return {
...state,
conversations: state.conversations.map((c) =>
c.id === action.payload ? { ...c, open: !c.open } : c
),
};
default:
return state;
}

View File

@@ -1,13 +1,23 @@
import { createSelector } from "reselect";
const selectMessaging = state => state.messaging;
const selectMessaging = (state) => state.messaging;
export const selectChatVisible = createSelector(
[selectMessaging],
messaging => messaging.visible
(messaging) => messaging.visible
);
export const selectConversations = createSelector(
export const selectIsSending = createSelector(
[selectMessaging],
messaging => messaging.conversations
(messaging) => messaging.isSending
);
export const selectError = createSelector(
[selectMessaging],
(messaging) => messaging.error
);
export const selectSelectedConversation = createSelector(
[selectMessaging],
(messaging) => messaging.selectedConversationId
);

View File

@@ -1,11 +1,8 @@
const MessagingActionTypes = {
TOGGLE_CHAT_VISIBLE: "TOGGLE_CHAT_VISIBLE",
SET_CHAT_VISIBLE: "SET_CHAT_VISIBLE",
OPEN_CONVERSATION: "OPEN_CONVERSATION",
CLOSE_CONVERSATION: "CLOSE_CONVERSATION",
TOGGLE_CONVERSATION_VISIBLE: "TOGGLE_CONVERSATION_VISIBLE",
SEND_MESSAGE: "SEND_MESSAGE",
SEND_MESSAGE_SUCCESS: "SEND_MESSAGE_SUCCESS",
SEND_MESSAGE_FAILURE: "SEND_MESSAGE_FAILURE"
SEND_MESSAGE_FAILURE: "SEND_MESSAGE_FAILURE",
SET_SELECTED_CONVERSATION: 'SET_SELECTED_CONVERSATION'
};
export default MessagingActionTypes;