BOD-14 Functional 2 way messaging from app.
This commit is contained in:
@@ -25,17 +25,17 @@ export default class AppContainer extends Component {
|
|||||||
uri: process.env.REACT_APP_GRAPHQL_ENDPOINT_WS,
|
uri: process.env.REACT_APP_GRAPHQL_ENDPOINT_WS,
|
||||||
options: {
|
options: {
|
||||||
//lazy: true,
|
//lazy: true,
|
||||||
reconnect: true
|
reconnect: true,
|
||||||
// connectionParams: () => {
|
connectionParams: () => {
|
||||||
// const token = localStorage.getItem("token");
|
const token = localStorage.getItem("token");
|
||||||
// if (token) {
|
if (token) {
|
||||||
// return {
|
return {
|
||||||
// headers: {
|
headers: {
|
||||||
// authorization: token ? `Bearer ${token}` : ""
|
authorization: token ? `Bearer ${token}` : ""
|
||||||
// }
|
}
|
||||||
// };
|
};
|
||||||
// }
|
}
|
||||||
// }
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
const subscriptionMiddleware = {
|
const subscriptionMiddleware = {
|
||||||
|
|||||||
@@ -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);
|
||||||
@@ -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 || []} />;
|
||||||
|
}
|
||||||
@@ -1,7 +1,11 @@
|
|||||||
import { Button } from "antd";
|
import { Button } from "antd";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { connect } from "react-redux";
|
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";
|
import PhoneFormatter from "../../utils/PhoneFormatter";
|
||||||
|
|
||||||
const mapDispatchToProps = dispatch => ({
|
const mapDispatchToProps = dispatch => ({
|
||||||
@@ -18,13 +22,13 @@ function ChatConversationClosedComponent({
|
|||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<div style={{ display: "flex" }}>
|
<div style={{ display: "flex" }}>
|
||||||
<div onClick={() => toggleConversationVisible(conversation.phone)}>
|
<div onClick={() => toggleConversationVisible(conversation.phone_num)}>
|
||||||
<PhoneFormatter>{conversation.phone}</PhoneFormatter>
|
<PhoneFormatter>{conversation.phone_num}</PhoneFormatter>
|
||||||
</div>
|
</div>
|
||||||
<Button
|
<Button
|
||||||
type='dashed'
|
type='dashed'
|
||||||
shape='circle-outline'
|
shape='circle-outline'
|
||||||
onClick={() => closeConversation(conversation.phone)}>
|
onClick={() => closeConversation(conversation.phone_num)}>
|
||||||
X
|
X
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Button, Card } from "antd";
|
import { Button, Card, Badge } from "antd";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
import { createStructuredSelector } from "reselect";
|
import { createStructuredSelector } from "reselect";
|
||||||
@@ -25,24 +25,26 @@ const mapDispatchToProps = dispatch => ({
|
|||||||
export function ChatConversationComponent({
|
export function ChatConversationComponent({
|
||||||
conversation,
|
conversation,
|
||||||
toggleConversationVisible,
|
toggleConversationVisible,
|
||||||
closeConversation
|
closeConversation,
|
||||||
|
messages,
|
||||||
|
subState
|
||||||
}) {
|
}) {
|
||||||
const messages = [];
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<Badge count={messages.length}>
|
||||||
<Card
|
<Card
|
||||||
title={
|
title={
|
||||||
conversation.open ? (
|
conversation.open ? (
|
||||||
<div style={{ display: "flex" }}>
|
<div style={{ display: "flex" }}>
|
||||||
<div
|
<div
|
||||||
onClick={() => toggleConversationVisible(conversation.phone)}>
|
onClick={() =>
|
||||||
<PhoneFormatter>{conversation.phone}</PhoneFormatter>
|
toggleConversationVisible(conversation.phone_num)
|
||||||
|
}>
|
||||||
|
<PhoneFormatter>{conversation.phone_num}</PhoneFormatter>
|
||||||
</div>
|
</div>
|
||||||
<Button
|
<Button
|
||||||
type='danger'
|
type='danger'
|
||||||
shape='circle-outline'
|
shape='circle-outline'
|
||||||
onClick={() => closeConversation(conversation.phone)}>
|
onClick={() => closeConversation(conversation.phone_num)}>
|
||||||
X
|
X
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
@@ -57,12 +59,13 @@ export function ChatConversationComponent({
|
|||||||
<ChatConversationOpenComponent
|
<ChatConversationOpenComponent
|
||||||
messages={messages}
|
messages={messages}
|
||||||
conversation={conversation}
|
conversation={conversation}
|
||||||
|
subState={subState}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<ChatConversationClosedComponent conversation={conversation} />
|
<ChatConversationClosedComponent conversation={conversation} />
|
||||||
)}
|
)}
|
||||||
</Card>
|
</Card>
|
||||||
</div>
|
</Badge>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,17 +1,33 @@
|
|||||||
|
import { useSubscription } from "@apollo/react-hooks";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import ChatConversationComponent from "./chat-conversation.component";
|
|
||||||
|
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
import { createStructuredSelector } from "reselect";
|
import { createStructuredSelector } from "reselect";
|
||||||
|
import { MESSAGES_SUBSCRIPTION } from "../../graphql/messages.queries";
|
||||||
|
import ChatConversationComponent from "./chat-conversation.component";
|
||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
//currentUser: selectCurrentUser
|
//currentUser: selectCurrentUser
|
||||||
});
|
});
|
||||||
const mapDispatchToProps = dispatch => ({
|
const mapDispatchToProps = dispatch => ({
|
||||||
//setUserLanguage: language => dispatch(setUserLanguage(language))
|
//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(
|
export default connect(
|
||||||
mapStateToProps,
|
mapStateToProps,
|
||||||
mapDispatchToProps
|
mapDispatchToProps
|
||||||
)(function ChatConversationContainer({ conversation }) {
|
)(ChatConversationContainer);
|
||||||
return <ChatConversationComponent conversation={conversation} />;
|
|
||||||
});
|
|
||||||
|
|||||||
@@ -1,10 +1,16 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
|
import AlertComponent from "../alert/alert.component";
|
||||||
import ChatSendMessage from "../chat-send-message/chat-send-message.component";
|
import ChatSendMessage from "../chat-send-message/chat-send-message.component";
|
||||||
|
import LoadingSpinner from "../loading-spinner/loading-spinner.component";
|
||||||
|
|
||||||
export default function ChatConversationOpenComponent({
|
export default function ChatConversationOpenComponent({
|
||||||
conversation,
|
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 (
|
return (
|
||||||
<div>
|
<div>
|
||||||
@@ -12,11 +18,13 @@ export default function ChatConversationOpenComponent({
|
|||||||
<ul>
|
<ul>
|
||||||
{messages.map(item => (
|
{messages.map(item => (
|
||||||
<li
|
<li
|
||||||
key={item.sid}
|
key={item.id}
|
||||||
className={`${
|
className={`${item.isoutbound ? "replies" : "sent"}`}>
|
||||||
item.direction === "inbound" ? "sent" : "replies"
|
<div>
|
||||||
}`}>
|
<p>
|
||||||
<p> {item.body}</p>
|
{item.text} <br /> <i>{item.status}</i>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
</li>
|
</li>
|
||||||
))}
|
))}
|
||||||
</ul>
|
</ul>
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ import { Badge, Card } from "antd";
|
|||||||
import { MessageFilled } from "@ant-design/icons";
|
import { MessageFilled } from "@ant-design/icons";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
|
import ChatConversationListContainer from "../chat-conversation-list/chat-conversation-list.container";
|
||||||
|
|
||||||
export default function ChatWindowComponent({
|
export default function ChatWindowComponent({
|
||||||
chatVisible,
|
chatVisible,
|
||||||
toggleChatVisible
|
toggleChatVisible
|
||||||
@@ -11,19 +13,19 @@ export default function ChatWindowComponent({
|
|||||||
<div>
|
<div>
|
||||||
<Badge count={5}>
|
<Badge count={5}>
|
||||||
<Card
|
<Card
|
||||||
onClick={() => toggleChatVisible()}
|
|
||||||
style={{
|
style={{
|
||||||
width: chatVisible ? "300px" : "125px",
|
width: chatVisible ? "300px" : "125px",
|
||||||
margin: "0px 10px"
|
margin: "0px 10px"
|
||||||
}}
|
}}
|
||||||
size="small"
|
size='small'>
|
||||||
>
|
|
||||||
{chatVisible ? (
|
{chatVisible ? (
|
||||||
<div className="messages" style={{ height: "400px" }}>
|
<div className='messages' style={{ height: "400px" }}>
|
||||||
List of chats here.
|
<ChatConversationListContainer />
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div>
|
<div
|
||||||
|
style={{ cursor: "pointer" }}
|
||||||
|
onClick={() => toggleChatVisible()}>
|
||||||
<MessageFilled />
|
<MessageFilled />
|
||||||
<strong style={{ paddingLeft: "10px" }}>
|
<strong style={{ paddingLeft: "10px" }}>
|
||||||
{t("messaging.labels.messaging")}
|
{t("messaging.labels.messaging")}
|
||||||
|
|||||||
@@ -12,19 +12,17 @@ import ChatOverlayComponent from "./chat-overlay.component";
|
|||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
chatVisible: selectChatVisible,
|
chatVisible: selectChatVisible,
|
||||||
conversations: selectConversations
|
activeConversations: selectConversations
|
||||||
});
|
});
|
||||||
|
|
||||||
const mapDispatchToProps = dispatch => ({
|
const mapDispatchToProps = dispatch => ({
|
||||||
toggleChatVisible: () => dispatch(toggleChatVisible())
|
toggleChatVisible: () => dispatch(toggleChatVisible())
|
||||||
});
|
});
|
||||||
export default connect(
|
|
||||||
mapStateToProps,
|
export function ChatOverlayContainer({
|
||||||
mapDispatchToProps
|
|
||||||
)(function ChatWindowContainer({
|
|
||||||
chatVisible,
|
chatVisible,
|
||||||
toggleChatVisible,
|
toggleChatVisible,
|
||||||
conversations
|
activeConversations
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<Affix offsetBottom={0}>
|
<Affix offsetBottom={0}>
|
||||||
@@ -35,14 +33,20 @@ export default connect(
|
|||||||
toggleChatVisible={toggleChatVisible}
|
toggleChatVisible={toggleChatVisible}
|
||||||
/>
|
/>
|
||||||
</Badge>
|
</Badge>
|
||||||
{conversations
|
{activeConversations
|
||||||
? conversations.map((conversation, idx) => (
|
? activeConversations.map(conversation => (
|
||||||
<Badge key={idx} count={5}>
|
<ChatConversationContainer
|
||||||
<ChatConversationContainer conversation={conversation} />
|
conversation={conversation}
|
||||||
</Badge>
|
key={conversation.id}
|
||||||
|
/>
|
||||||
))
|
))
|
||||||
: null}
|
: null}
|
||||||
</div>
|
</div>
|
||||||
</Affix>
|
</Affix>
|
||||||
);
|
);
|
||||||
});
|
}
|
||||||
|
|
||||||
|
export default connect(
|
||||||
|
mapStateToProps,
|
||||||
|
mapDispatchToProps
|
||||||
|
)(ChatOverlayContainer);
|
||||||
|
|||||||
@@ -19,11 +19,12 @@ function ChatSendMessageComponent({ conversation, bodyshop, sendMessage }) {
|
|||||||
|
|
||||||
const handleEnter = () => {
|
const handleEnter = () => {
|
||||||
sendMessage({
|
sendMessage({
|
||||||
to: conversation.phone,
|
to: conversation.phone_num,
|
||||||
body: message,
|
body: message,
|
||||||
messagingServiceSid: bodyshop.messagingservicesid,
|
messagingServiceSid: bodyshop.messagingservicesid,
|
||||||
conversationid: conversation.id
|
conversationid: conversation.id
|
||||||
});
|
});
|
||||||
|
setMessage("");
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -31,6 +32,7 @@ function ChatSendMessageComponent({ conversation, bodyshop, sendMessage }) {
|
|||||||
<Input.TextArea
|
<Input.TextArea
|
||||||
allowClear
|
allowClear
|
||||||
autoSize={{ minRows: 1, maxRows: 4 }}
|
autoSize={{ minRows: 1, maxRows: 4 }}
|
||||||
|
value={message}
|
||||||
placeholder={t("messaging.labels.typeamessage")}
|
placeholder={t("messaging.labels.typeamessage")}
|
||||||
// enterButton={}
|
// enterButton={}
|
||||||
onChange={e => setMessage(e.target.value)}
|
onChange={e => setMessage(e.target.value)}
|
||||||
|
|||||||
@@ -9,7 +9,9 @@ const errorLink = onError(
|
|||||||
`[GraphQL error]: Message: ${message}, Location: ${locations}, Path: ${path}`
|
`[GraphQL error]: Message: ${message}, Location: ${locations}, Path: ${path}`
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
if (networkError) console.log(`[Network error]: ${networkError}`);
|
if (networkError)
|
||||||
|
console.log(`[Network error]: ${JSON.stringify(networkError)}`);
|
||||||
|
console.log(operation.getContext());
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
15
client/src/graphql/conversations.queries.js
Normal file
15
client/src/graphql/conversations.queries.js
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
import { gql } from "apollo-boost";
|
||||||
|
|
||||||
|
export const CONVERSATION_LIST_SUBSCRIPTION = gql`
|
||||||
|
subscription CONVERSATION_LIST_SUBSCRIPTION {
|
||||||
|
conversations {
|
||||||
|
phone_num
|
||||||
|
id
|
||||||
|
messages_aggregate(where: { read: { _eq: false } }) {
|
||||||
|
aggregate {
|
||||||
|
count
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
13
client/src/graphql/messages.queries.js
Normal file
13
client/src/graphql/messages.queries.js
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
import { gql } from "apollo-boost";
|
||||||
|
|
||||||
|
export const MESSAGES_SUBSCRIPTION = gql`
|
||||||
|
subscription MESSAGES_SUBSCRIPTION($conversationId: uuid!) {
|
||||||
|
messages(where: { conversationid: { _eq: $conversationId } }) {
|
||||||
|
text
|
||||||
|
created_at
|
||||||
|
id
|
||||||
|
status
|
||||||
|
isoutbound
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
@@ -2,18 +2,20 @@ import MessagingActionTypes from "./messaging.types";
|
|||||||
|
|
||||||
const INITIAL_STATE = {
|
const INITIAL_STATE = {
|
||||||
visible: false,
|
visible: false,
|
||||||
|
unread: 0,
|
||||||
conversations: [
|
conversations: [
|
||||||
{
|
{
|
||||||
phone: "6049992002",
|
phone_num: "6049992002",
|
||||||
id: "519ba10d-6467-4fa5-9c22-59ae891edeb6",
|
id: "519ba10d-6467-4fa5-9c22-59ae891edeb6",
|
||||||
open: false
|
open: false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
phone: "6049992991",
|
phone_num: "6049992991",
|
||||||
id: "ab57deba-eeb9-40db-b5ae-23f3ce8d7c7b",
|
id: "ab57deba-eeb9-40db-b5ae-23f3ce8d7c7b",
|
||||||
open: false
|
open: false
|
||||||
}
|
}
|
||||||
]
|
],
|
||||||
|
error: null
|
||||||
};
|
};
|
||||||
|
|
||||||
const messagingReducer = (state = INITIAL_STATE, action) => {
|
const messagingReducer = (state = INITIAL_STATE, action) => {
|
||||||
@@ -29,11 +31,13 @@ const messagingReducer = (state = INITIAL_STATE, action) => {
|
|||||||
visible: true
|
visible: true
|
||||||
};
|
};
|
||||||
case MessagingActionTypes.OPEN_CONVERSATION:
|
case MessagingActionTypes.OPEN_CONVERSATION:
|
||||||
if (state.conversations.find(c => c.phone === action.payload))
|
if (
|
||||||
|
state.conversations.find(c => c.phone_num === action.payload.phone_num)
|
||||||
|
)
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
conversations: state.conversations.map(c =>
|
conversations: state.conversations.map(c =>
|
||||||
c.phone === action.payload ? { ...c, open: true } : c
|
c.phone_num === action.payload.phone_num ? { ...c, open: true } : c
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
else
|
else
|
||||||
@@ -41,23 +45,29 @@ const messagingReducer = (state = INITIAL_STATE, action) => {
|
|||||||
...state,
|
...state,
|
||||||
conversations: [
|
conversations: [
|
||||||
...state.conversations,
|
...state.conversations,
|
||||||
{ phone: action.payload, open: true }
|
{
|
||||||
|
phone_num: action.payload.phone_num,
|
||||||
|
id: action.payload.id,
|
||||||
|
open: true
|
||||||
|
}
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
case MessagingActionTypes.CLOSE_CONVERSATION:
|
case MessagingActionTypes.CLOSE_CONVERSATION:
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
conversations: state.conversations.filter(
|
conversations: state.conversations.filter(
|
||||||
c => c.phone !== action.payload
|
c => c.phone_num !== action.payload
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
case MessagingActionTypes.TOGGLE_CONVERSATION_VISIBLE:
|
case MessagingActionTypes.TOGGLE_CONVERSATION_VISIBLE:
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
conversations: state.conversations.map(c =>
|
conversations: state.conversations.map(c =>
|
||||||
c.phone === action.payload ? { ...c, open: !c.open } : c
|
c.phone_num === action.payload ? { ...c, open: !c.open } : c
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
case MessagingActionTypes.SEND_MESSAGE_FAILURE:
|
||||||
|
return { ...state, error: action.payload };
|
||||||
default:
|
default:
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
- args:
|
||||||
|
cascade: false
|
||||||
|
read_only: false
|
||||||
|
sql: ALTER TABLE "public"."messages" DROP COLUMN "read";
|
||||||
|
type: run_sql
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
- args:
|
||||||
|
cascade: false
|
||||||
|
read_only: false
|
||||||
|
sql: ALTER TABLE "public"."messages" ADD COLUMN "read" boolean NOT NULL DEFAULT
|
||||||
|
false;
|
||||||
|
type: run_sql
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
- args:
|
||||||
|
role: user
|
||||||
|
table:
|
||||||
|
name: messages
|
||||||
|
schema: public
|
||||||
|
type: drop_insert_permission
|
||||||
|
- args:
|
||||||
|
permission:
|
||||||
|
check:
|
||||||
|
conversation:
|
||||||
|
bodyshop:
|
||||||
|
associations:
|
||||||
|
_and:
|
||||||
|
- user:
|
||||||
|
authid:
|
||||||
|
_eq: X-Hasura-User-Id
|
||||||
|
- active:
|
||||||
|
_eq: true
|
||||||
|
columns:
|
||||||
|
- id
|
||||||
|
- created_at
|
||||||
|
- updated_at
|
||||||
|
- msid
|
||||||
|
- conversationid
|
||||||
|
- text
|
||||||
|
- image
|
||||||
|
- image_path
|
||||||
|
- isoutbound
|
||||||
|
- status
|
||||||
|
localPresets:
|
||||||
|
- key: ""
|
||||||
|
value: ""
|
||||||
|
set: {}
|
||||||
|
role: user
|
||||||
|
table:
|
||||||
|
name: messages
|
||||||
|
schema: public
|
||||||
|
type: create_insert_permission
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
- args:
|
||||||
|
role: user
|
||||||
|
table:
|
||||||
|
name: messages
|
||||||
|
schema: public
|
||||||
|
type: drop_insert_permission
|
||||||
|
- args:
|
||||||
|
permission:
|
||||||
|
check:
|
||||||
|
conversation:
|
||||||
|
bodyshop:
|
||||||
|
associations:
|
||||||
|
_and:
|
||||||
|
- user:
|
||||||
|
authid:
|
||||||
|
_eq: X-Hasura-User-Id
|
||||||
|
- active:
|
||||||
|
_eq: true
|
||||||
|
columns:
|
||||||
|
- id
|
||||||
|
- created_at
|
||||||
|
- updated_at
|
||||||
|
- msid
|
||||||
|
- conversationid
|
||||||
|
- text
|
||||||
|
- image
|
||||||
|
- image_path
|
||||||
|
- isoutbound
|
||||||
|
- status
|
||||||
|
- read
|
||||||
|
localPresets:
|
||||||
|
- key: ""
|
||||||
|
value: ""
|
||||||
|
set: {}
|
||||||
|
role: user
|
||||||
|
table:
|
||||||
|
name: messages
|
||||||
|
schema: public
|
||||||
|
type: create_insert_permission
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
- args:
|
||||||
|
role: user
|
||||||
|
table:
|
||||||
|
name: messages
|
||||||
|
schema: public
|
||||||
|
type: drop_select_permission
|
||||||
|
- args:
|
||||||
|
permission:
|
||||||
|
allow_aggregations: false
|
||||||
|
columns:
|
||||||
|
- conversationid
|
||||||
|
- created_at
|
||||||
|
- id
|
||||||
|
- image
|
||||||
|
- image_path
|
||||||
|
- isoutbound
|
||||||
|
- msid
|
||||||
|
- status
|
||||||
|
- text
|
||||||
|
- updated_at
|
||||||
|
computed_fields: []
|
||||||
|
filter:
|
||||||
|
conversation:
|
||||||
|
bodyshop:
|
||||||
|
associations:
|
||||||
|
_and:
|
||||||
|
- user:
|
||||||
|
authid:
|
||||||
|
_eq: X-Hasura-User-Id
|
||||||
|
- active:
|
||||||
|
_eq: true
|
||||||
|
role: user
|
||||||
|
table:
|
||||||
|
name: messages
|
||||||
|
schema: public
|
||||||
|
type: create_select_permission
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
- args:
|
||||||
|
role: user
|
||||||
|
table:
|
||||||
|
name: messages
|
||||||
|
schema: public
|
||||||
|
type: drop_select_permission
|
||||||
|
- args:
|
||||||
|
permission:
|
||||||
|
allow_aggregations: false
|
||||||
|
columns:
|
||||||
|
- image
|
||||||
|
- isoutbound
|
||||||
|
- read
|
||||||
|
- image_path
|
||||||
|
- msid
|
||||||
|
- status
|
||||||
|
- text
|
||||||
|
- created_at
|
||||||
|
- updated_at
|
||||||
|
- conversationid
|
||||||
|
- id
|
||||||
|
computed_fields: []
|
||||||
|
filter:
|
||||||
|
conversation:
|
||||||
|
bodyshop:
|
||||||
|
associations:
|
||||||
|
_and:
|
||||||
|
- user:
|
||||||
|
authid:
|
||||||
|
_eq: X-Hasura-User-Id
|
||||||
|
- active:
|
||||||
|
_eq: true
|
||||||
|
role: user
|
||||||
|
table:
|
||||||
|
name: messages
|
||||||
|
schema: public
|
||||||
|
type: create_select_permission
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
- args:
|
||||||
|
role: user
|
||||||
|
table:
|
||||||
|
name: messages
|
||||||
|
schema: public
|
||||||
|
type: drop_update_permission
|
||||||
|
- args:
|
||||||
|
permission:
|
||||||
|
columns:
|
||||||
|
- image
|
||||||
|
- isoutbound
|
||||||
|
- image_path
|
||||||
|
- msid
|
||||||
|
- status
|
||||||
|
- text
|
||||||
|
- created_at
|
||||||
|
- updated_at
|
||||||
|
- conversationid
|
||||||
|
- id
|
||||||
|
filter:
|
||||||
|
conversation:
|
||||||
|
bodyshop:
|
||||||
|
associations:
|
||||||
|
_and:
|
||||||
|
- user:
|
||||||
|
authid:
|
||||||
|
_eq: X-Hasura-User-Id
|
||||||
|
- active:
|
||||||
|
_eq: true
|
||||||
|
localPresets:
|
||||||
|
- key: ""
|
||||||
|
value: ""
|
||||||
|
set: {}
|
||||||
|
role: user
|
||||||
|
table:
|
||||||
|
name: messages
|
||||||
|
schema: public
|
||||||
|
type: create_update_permission
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
- args:
|
||||||
|
role: user
|
||||||
|
table:
|
||||||
|
name: messages
|
||||||
|
schema: public
|
||||||
|
type: drop_update_permission
|
||||||
|
- args:
|
||||||
|
permission:
|
||||||
|
columns:
|
||||||
|
- image
|
||||||
|
- isoutbound
|
||||||
|
- read
|
||||||
|
- image_path
|
||||||
|
- msid
|
||||||
|
- status
|
||||||
|
- text
|
||||||
|
- created_at
|
||||||
|
- updated_at
|
||||||
|
- conversationid
|
||||||
|
- id
|
||||||
|
filter:
|
||||||
|
conversation:
|
||||||
|
bodyshop:
|
||||||
|
associations:
|
||||||
|
_and:
|
||||||
|
- user:
|
||||||
|
authid:
|
||||||
|
_eq: X-Hasura-User-Id
|
||||||
|
- active:
|
||||||
|
_eq: true
|
||||||
|
localPresets:
|
||||||
|
- key: ""
|
||||||
|
value: ""
|
||||||
|
set: {}
|
||||||
|
role: user
|
||||||
|
table:
|
||||||
|
name: messages
|
||||||
|
schema: public
|
||||||
|
type: create_update_permission
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
- args:
|
||||||
|
role: user
|
||||||
|
table:
|
||||||
|
name: messages
|
||||||
|
schema: public
|
||||||
|
type: drop_select_permission
|
||||||
|
- args:
|
||||||
|
permission:
|
||||||
|
allow_aggregations: false
|
||||||
|
columns:
|
||||||
|
- image
|
||||||
|
- isoutbound
|
||||||
|
- read
|
||||||
|
- image_path
|
||||||
|
- msid
|
||||||
|
- status
|
||||||
|
- text
|
||||||
|
- created_at
|
||||||
|
- updated_at
|
||||||
|
- conversationid
|
||||||
|
- id
|
||||||
|
computed_fields: []
|
||||||
|
filter:
|
||||||
|
conversation:
|
||||||
|
bodyshop:
|
||||||
|
associations:
|
||||||
|
_and:
|
||||||
|
- user:
|
||||||
|
authid:
|
||||||
|
_eq: X-Hasura-User-Id
|
||||||
|
- active:
|
||||||
|
_eq: true
|
||||||
|
role: user
|
||||||
|
table:
|
||||||
|
name: messages
|
||||||
|
schema: public
|
||||||
|
type: create_select_permission
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
- args:
|
||||||
|
role: user
|
||||||
|
table:
|
||||||
|
name: messages
|
||||||
|
schema: public
|
||||||
|
type: drop_select_permission
|
||||||
|
- args:
|
||||||
|
permission:
|
||||||
|
allow_aggregations: true
|
||||||
|
columns:
|
||||||
|
- image
|
||||||
|
- isoutbound
|
||||||
|
- read
|
||||||
|
- image_path
|
||||||
|
- msid
|
||||||
|
- status
|
||||||
|
- text
|
||||||
|
- created_at
|
||||||
|
- updated_at
|
||||||
|
- conversationid
|
||||||
|
- id
|
||||||
|
computed_fields: []
|
||||||
|
filter:
|
||||||
|
conversation:
|
||||||
|
bodyshop:
|
||||||
|
associations:
|
||||||
|
_and:
|
||||||
|
- user:
|
||||||
|
authid:
|
||||||
|
_eq: X-Hasura-User-Id
|
||||||
|
- active:
|
||||||
|
_eq: true
|
||||||
|
role: user
|
||||||
|
table:
|
||||||
|
name: messages
|
||||||
|
schema: public
|
||||||
|
type: create_select_permission
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
- args:
|
||||||
|
role: user
|
||||||
|
table:
|
||||||
|
name: conversations
|
||||||
|
schema: public
|
||||||
|
type: drop_select_permission
|
||||||
|
- args:
|
||||||
|
permission:
|
||||||
|
allow_aggregations: false
|
||||||
|
columns:
|
||||||
|
- phone_num
|
||||||
|
- created_at
|
||||||
|
- updated_at
|
||||||
|
- bodyshopid
|
||||||
|
- id
|
||||||
|
computed_fields: []
|
||||||
|
filter:
|
||||||
|
bodyshop:
|
||||||
|
associations:
|
||||||
|
_and:
|
||||||
|
- user:
|
||||||
|
authid:
|
||||||
|
_eq: X-Hasura-User-Id
|
||||||
|
- active:
|
||||||
|
_eq: true
|
||||||
|
role: user
|
||||||
|
table:
|
||||||
|
name: conversations
|
||||||
|
schema: public
|
||||||
|
type: create_select_permission
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
- args:
|
||||||
|
role: user
|
||||||
|
table:
|
||||||
|
name: conversations
|
||||||
|
schema: public
|
||||||
|
type: drop_select_permission
|
||||||
|
- args:
|
||||||
|
permission:
|
||||||
|
allow_aggregations: true
|
||||||
|
columns:
|
||||||
|
- phone_num
|
||||||
|
- created_at
|
||||||
|
- updated_at
|
||||||
|
- bodyshopid
|
||||||
|
- id
|
||||||
|
computed_fields: []
|
||||||
|
filter:
|
||||||
|
bodyshop:
|
||||||
|
associations:
|
||||||
|
_and:
|
||||||
|
- user:
|
||||||
|
authid:
|
||||||
|
_eq: X-Hasura-User-Id
|
||||||
|
- active:
|
||||||
|
_eq: true
|
||||||
|
role: user
|
||||||
|
table:
|
||||||
|
name: conversations
|
||||||
|
schema: public
|
||||||
|
type: create_select_permission
|
||||||
Reference in New Issue
Block a user