BOD-14 Functional 2 way messaging from app.

This commit is contained in:
Patrick Fic
2020-03-25 18:45:48 -07:00
parent 546d2d82b7
commit 4c35337d36
26 changed files with 591 additions and 64 deletions

View File

@@ -0,0 +1,60 @@
import { ShrinkOutlined } from "@ant-design/icons";
import { Avatar, Badge, Col, List, Row } from "antd";
import React from "react";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { openConversation, toggleChatVisible } from "../../redux/messaging/messaging.actions";
const mapStateToProps = createStructuredSelector({
//currentUser: selectCurrentUser
});
const mapDispatchToProps = dispatch => ({
toggleChatVisible: () => dispatch(toggleChatVisible()),
openConversation: number => dispatch(openConversation(number))
});
export function ChatConversationListComponent({
toggleChatVisible,
conversationList,
openConversation
}) {
console.log("conversationList", conversationList);
return (
<div>
<Row>
<Col span={12}>Title</Col>
<Col span={2} offset={10}>
<ShrinkOutlined onClick={() => toggleChatVisible()} />
</Col>
</Row>
<Row>
<List
dataSource={conversationList}
renderItem={item => (
<Badge count={item.messages_aggregate.aggregate.count || 0}>
<List.Item
key={item.id}
style={{ cursor: "pointer" }}
onClick={() =>
openConversation({ phone_num: item.phone_num, id: item.id })
}>
<List.Item.Meta
avatar={
<Avatar src='https://zos.alipayobjects.com/rmsportal/ODTLcjxAfvqbxHnVXCYX.png' />
}
title={item.phone_num}
description='Some sort of RO info? '
/>
</List.Item>
</Badge>
)}
/>
</Row>
</div>
);
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(ChatConversationListComponent);

View File

@@ -0,0 +1,16 @@
import React from "react";
import ChatConversationListComponent from "./chat-conversation-list.component";
import { useSubscription } from "@apollo/react-hooks";
import { CONVERSATION_LIST_SUBSCRIPTION } from "../../graphql/conversations.queries";
import LoadingSpinner from "../loading-spinner/loading-spinner.component";
import AlertComponent from "../alert/alert.component";
export default function ChatConversationListContainer() {
const { loading, error, data } = useSubscription(
CONVERSATION_LIST_SUBSCRIPTION
);
if (loading) return <LoadingSpinner />;
if (error) return <AlertComponent message={error.message} type='error' />;
return <ChatConversationListComponent conversationList={data.conversations || []} />;
}

View File

@@ -1,7 +1,11 @@
import { Button } from "antd";
import React from "react";
import { connect } from "react-redux";
import { closeConversation, sendMessage, toggleConversationVisible } from "../../redux/messaging/messaging.actions";
import {
closeConversation,
sendMessage,
toggleConversationVisible
} from "../../redux/messaging/messaging.actions";
import PhoneFormatter from "../../utils/PhoneFormatter";
const mapDispatchToProps = dispatch => ({
@@ -18,13 +22,13 @@ function ChatConversationClosedComponent({
}) {
return (
<div style={{ display: "flex" }}>
<div onClick={() => toggleConversationVisible(conversation.phone)}>
<PhoneFormatter>{conversation.phone}</PhoneFormatter>
<div onClick={() => toggleConversationVisible(conversation.phone_num)}>
<PhoneFormatter>{conversation.phone_num}</PhoneFormatter>
</div>
<Button
type='dashed'
shape='circle-outline'
onClick={() => closeConversation(conversation.phone)}>
onClick={() => closeConversation(conversation.phone_num)}>
X
</Button>
</div>

View File

@@ -1,4 +1,4 @@
import { Button, Card } from "antd";
import { Button, Card, Badge } from "antd";
import React from "react";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
@@ -25,24 +25,26 @@ const mapDispatchToProps = dispatch => ({
export function ChatConversationComponent({
conversation,
toggleConversationVisible,
closeConversation
closeConversation,
messages,
subState
}) {
const messages = [];
return (
<div>
<Badge count={messages.length}>
<Card
title={
conversation.open ? (
<div style={{ display: "flex" }}>
<div
onClick={() => toggleConversationVisible(conversation.phone)}>
<PhoneFormatter>{conversation.phone}</PhoneFormatter>
onClick={() =>
toggleConversationVisible(conversation.phone_num)
}>
<PhoneFormatter>{conversation.phone_num}</PhoneFormatter>
</div>
<Button
type='danger'
shape='circle-outline'
onClick={() => closeConversation(conversation.phone)}>
onClick={() => closeConversation(conversation.phone_num)}>
X
</Button>
</div>
@@ -57,12 +59,13 @@ export function ChatConversationComponent({
<ChatConversationOpenComponent
messages={messages}
conversation={conversation}
subState={subState}
/>
) : (
<ChatConversationClosedComponent conversation={conversation} />
)}
</Card>
</div>
</Badge>
);
}

View File

@@ -1,17 +1,33 @@
import { useSubscription } from "@apollo/react-hooks";
import React from "react";
import ChatConversationComponent from "./chat-conversation.component";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { MESSAGES_SUBSCRIPTION } from "../../graphql/messages.queries";
import ChatConversationComponent from "./chat-conversation.component";
const mapStateToProps = createStructuredSelector({
//currentUser: selectCurrentUser
});
const mapDispatchToProps = dispatch => ({
//setUserLanguage: language => dispatch(setUserLanguage(language))
});
export function ChatConversationContainer({ conversation }) {
console.log("conversation", conversation);
const { loading, error, data } = useSubscription(MESSAGES_SUBSCRIPTION, {
variables: { conversationId: conversation.id }
});
return (
<ChatConversationComponent
subState={[loading, error]}
conversation={conversation}
messages={(data && data.messages) || []}
/>
);
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(function ChatConversationContainer({ conversation }) {
return <ChatConversationComponent conversation={conversation} />;
});
)(ChatConversationContainer);

View File

@@ -1,10 +1,16 @@
import React from "react";
import AlertComponent from "../alert/alert.component";
import ChatSendMessage from "../chat-send-message/chat-send-message.component";
import LoadingSpinner from "../loading-spinner/loading-spinner.component";
export default function ChatConversationOpenComponent({
conversation,
messages
messages,
subState
}) {
if (!!!messages) return <div>No Messages</div>;
const [loading, error] = subState;
if (loading) return <LoadingSpinner />;
if (error) return <AlertComponent message={error.message} type='error' />;
return (
<div>
@@ -12,11 +18,13 @@ export default function ChatConversationOpenComponent({
<ul>
{messages.map(item => (
<li
key={item.sid}
className={`${
item.direction === "inbound" ? "sent" : "replies"
}`}>
<p> {item.body}</p>
key={item.id}
className={`${item.isoutbound ? "replies" : "sent"}`}>
<div>
<p>
{item.text} <br /> <i>{item.status}</i>
</p>
</div>
</li>
))}
</ul>

View File

@@ -2,6 +2,8 @@ import { Badge, Card } from "antd";
import { MessageFilled } from "@ant-design/icons";
import React from "react";
import { useTranslation } from "react-i18next";
import ChatConversationListContainer from "../chat-conversation-list/chat-conversation-list.container";
export default function ChatWindowComponent({
chatVisible,
toggleChatVisible
@@ -11,19 +13,19 @@ export default function ChatWindowComponent({
<div>
<Badge count={5}>
<Card
onClick={() => toggleChatVisible()}
style={{
width: chatVisible ? "300px" : "125px",
margin: "0px 10px"
}}
size="small"
>
size='small'>
{chatVisible ? (
<div className="messages" style={{ height: "400px" }}>
List of chats here.
<div className='messages' style={{ height: "400px" }}>
<ChatConversationListContainer />
</div>
) : (
<div>
<div
style={{ cursor: "pointer" }}
onClick={() => toggleChatVisible()}>
<MessageFilled />
<strong style={{ paddingLeft: "10px" }}>
{t("messaging.labels.messaging")}

View File

@@ -12,19 +12,17 @@ import ChatOverlayComponent from "./chat-overlay.component";
const mapStateToProps = createStructuredSelector({
chatVisible: selectChatVisible,
conversations: selectConversations
activeConversations: selectConversations
});
const mapDispatchToProps = dispatch => ({
toggleChatVisible: () => dispatch(toggleChatVisible())
});
export default connect(
mapStateToProps,
mapDispatchToProps
)(function ChatWindowContainer({
export function ChatOverlayContainer({
chatVisible,
toggleChatVisible,
conversations
activeConversations
}) {
return (
<Affix offsetBottom={0}>
@@ -35,14 +33,20 @@ export default connect(
toggleChatVisible={toggleChatVisible}
/>
</Badge>
{conversations
? conversations.map((conversation, idx) => (
<Badge key={idx} count={5}>
<ChatConversationContainer conversation={conversation} />
</Badge>
{activeConversations
? activeConversations.map(conversation => (
<ChatConversationContainer
conversation={conversation}
key={conversation.id}
/>
))
: null}
</div>
</Affix>
);
});
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(ChatOverlayContainer);

View File

@@ -19,11 +19,12 @@ function ChatSendMessageComponent({ conversation, bodyshop, sendMessage }) {
const handleEnter = () => {
sendMessage({
to: conversation.phone,
to: conversation.phone_num,
body: message,
messagingServiceSid: bodyshop.messagingservicesid,
conversationid: conversation.id
});
setMessage("");
};
return (
@@ -31,6 +32,7 @@ function ChatSendMessageComponent({ conversation, bodyshop, sendMessage }) {
<Input.TextArea
allowClear
autoSize={{ minRows: 1, maxRows: 4 }}
value={message}
placeholder={t("messaging.labels.typeamessage")}
// enterButton={}
onChange={e => setMessage(e.target.value)}