Added MMS support for messaging. IO-538
This commit is contained in:
@@ -16,7 +16,7 @@ npx hasura migrate apply --endpoint https://db.imex.online/ --admin-secret 'Prod
|
|||||||
|
|
||||||
NGROK TEsting:
|
NGROK TEsting:
|
||||||
|
|
||||||
./ngrok.exe http https://localhost:5000 -host-header="localhost:5000"
|
./ngrok.exe http http://localhost:5000 -host-header="localhost:5000"
|
||||||
|
|
||||||
Finding deadfiles - run from client directory
|
Finding deadfiles - run from client directory
|
||||||
npx deadfile ./src/index.js --exclude build templates
|
npx deadfile ./src/index.js --exclude build templates
|
||||||
|
|||||||
@@ -21831,6 +21831,27 @@
|
|||||||
<folder_node>
|
<folder_node>
|
||||||
<name>labels</name>
|
<name>labels</name>
|
||||||
<children>
|
<children>
|
||||||
|
<concept_node>
|
||||||
|
<name>maxtenimages</name>
|
||||||
|
<definition_loaded>false</definition_loaded>
|
||||||
|
<description></description>
|
||||||
|
<comment></comment>
|
||||||
|
<default_text></default_text>
|
||||||
|
<translations>
|
||||||
|
<translation>
|
||||||
|
<language>en-US</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
<translation>
|
||||||
|
<language>es-MX</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
<translation>
|
||||||
|
<language>fr-CA</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
</translations>
|
||||||
|
</concept_node>
|
||||||
<concept_node>
|
<concept_node>
|
||||||
<name>messaging</name>
|
<name>messaging</name>
|
||||||
<definition_loaded>false</definition_loaded>
|
<definition_loaded>false</definition_loaded>
|
||||||
@@ -21936,6 +21957,27 @@
|
|||||||
</translation>
|
</translation>
|
||||||
</translations>
|
</translations>
|
||||||
</concept_node>
|
</concept_node>
|
||||||
|
<concept_node>
|
||||||
|
<name>selectmedia</name>
|
||||||
|
<definition_loaded>false</definition_loaded>
|
||||||
|
<description></description>
|
||||||
|
<comment></comment>
|
||||||
|
<default_text></default_text>
|
||||||
|
<translations>
|
||||||
|
<translation>
|
||||||
|
<language>en-US</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
<translation>
|
||||||
|
<language>es-MX</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
<translation>
|
||||||
|
<language>fr-CA</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
</translations>
|
||||||
|
</concept_node>
|
||||||
<concept_node>
|
<concept_node>
|
||||||
<name>sentby</name>
|
<name>sentby</name>
|
||||||
<definition_loaded>false</definition_loaded>
|
<definition_loaded>false</definition_loaded>
|
||||||
|
|||||||
@@ -0,0 +1,88 @@
|
|||||||
|
import { PictureFilled } from "@ant-design/icons";
|
||||||
|
import { useQuery } from "@apollo/react-hooks";
|
||||||
|
import { Badge, Popover } from "antd";
|
||||||
|
import React, { useEffect, useState } from "react";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { connect } from "react-redux";
|
||||||
|
import { createStructuredSelector } from "reselect";
|
||||||
|
import { GET_DOCUMENTS_BY_JOB } from "../../graphql/documents.queries";
|
||||||
|
import AlertComponent from "../alert/alert.component";
|
||||||
|
import JobDocumentsGalleryExternal from "../jobs-documents-gallery/jobs-documents-gallery.external.component";
|
||||||
|
import LoadingSpinner from "../loading-spinner/loading-spinner.component";
|
||||||
|
|
||||||
|
const mapStateToProps = createStructuredSelector({
|
||||||
|
//currentUser: selectCurrentUser
|
||||||
|
});
|
||||||
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
|
//setUserLanguage: language => dispatch(setUserLanguage(language))
|
||||||
|
});
|
||||||
|
export default connect(mapStateToProps, mapDispatchToProps)(ChatMediaSelector);
|
||||||
|
|
||||||
|
export function ChatMediaSelector({
|
||||||
|
selectedMedia,
|
||||||
|
setSelectedMedia,
|
||||||
|
conversation,
|
||||||
|
}) {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
console.log("conversation", conversation);
|
||||||
|
const { loading, error, data, refetch } = useQuery(GET_DOCUMENTS_BY_JOB, {
|
||||||
|
variables: {
|
||||||
|
jobId:
|
||||||
|
conversation.job_conversations[0] &&
|
||||||
|
conversation.job_conversations[0].jobid,
|
||||||
|
},
|
||||||
|
fetchPolicy: "network-only",
|
||||||
|
skip:
|
||||||
|
!conversation.job_conversations ||
|
||||||
|
conversation.job_conversations.length === 0,
|
||||||
|
});
|
||||||
|
|
||||||
|
const [visible, setVisible] = useState(false);
|
||||||
|
|
||||||
|
const handleVisibleChange = (visible) => {
|
||||||
|
setVisible(visible);
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setSelectedMedia([]);
|
||||||
|
}, [setSelectedMedia, conversation]);
|
||||||
|
|
||||||
|
const content = (
|
||||||
|
<div>
|
||||||
|
{loading && <LoadingSpinner />}
|
||||||
|
{error && <AlertComponent message={error.message} type="error" />}
|
||||||
|
{selectedMedia.filter((s) => s.isSelected).length >= 10 ? (
|
||||||
|
<div style={{ color: "red" }}>{t("messaging.labels.maxtenimages")}</div>
|
||||||
|
) : null}
|
||||||
|
{data && (
|
||||||
|
<JobDocumentsGalleryExternal
|
||||||
|
data={data ? data.documents : []}
|
||||||
|
externalMediaState={[selectedMedia, setSelectedMedia]}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Popover
|
||||||
|
content={
|
||||||
|
conversation.job_conversations.length === 0 ? (
|
||||||
|
<div>{t("messaging.errors.noattachedjobs")}</div>
|
||||||
|
) : (
|
||||||
|
content
|
||||||
|
)
|
||||||
|
}
|
||||||
|
title={t("messaging.labels.selectmedia")}
|
||||||
|
trigger="click"
|
||||||
|
visible={visible}
|
||||||
|
onVisibleChange={handleVisibleChange}
|
||||||
|
>
|
||||||
|
<Badge
|
||||||
|
size="small"
|
||||||
|
count={selectedMedia.filter((s) => s.isSelected).length}
|
||||||
|
>
|
||||||
|
<PictureFilled style={{ margin: "0 .5rem" }} />
|
||||||
|
</Badge>
|
||||||
|
</Popover>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,4 +1,6 @@
|
|||||||
import Icon from "@ant-design/icons";
|
import Icon from "@ant-design/icons";
|
||||||
|
import i18n from "i18next";
|
||||||
|
import moment from "moment";
|
||||||
import React, { useEffect, useRef } from "react";
|
import React, { useEffect, useRef } from "react";
|
||||||
import { MdDone, MdDoneAll } from "react-icons/md";
|
import { MdDone, MdDoneAll } from "react-icons/md";
|
||||||
import {
|
import {
|
||||||
@@ -8,8 +10,6 @@ import {
|
|||||||
List,
|
List,
|
||||||
} from "react-virtualized";
|
} from "react-virtualized";
|
||||||
import "./chat-message-list.styles.scss";
|
import "./chat-message-list.styles.scss";
|
||||||
import i18n from "i18next";
|
|
||||||
import moment from "moment";
|
|
||||||
|
|
||||||
export default function ChatMessageListComponent({ messages }) {
|
export default function ChatMessageListComponent({ messages }) {
|
||||||
const virtualizedListRef = useRef(null);
|
const virtualizedListRef = useRef(null);
|
||||||
@@ -46,6 +46,16 @@ export default function ChatMessageListComponent({ messages }) {
|
|||||||
{MessageRender(messages[index])}
|
{MessageRender(messages[index])}
|
||||||
{StatusRender(messages[index].status)}
|
{StatusRender(messages[index].status)}
|
||||||
</div>
|
</div>
|
||||||
|
{messages[index].isoutbound && (
|
||||||
|
<div style={{ fontSize: 10 }}>
|
||||||
|
{i18n.t("messaging.labels.sentby", {
|
||||||
|
by: messages[index].userid,
|
||||||
|
time: moment(messages[index].created_at).format(
|
||||||
|
"MM/DD/YYYY @ hh:mm a"
|
||||||
|
),
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</CellMeasurer>
|
</CellMeasurer>
|
||||||
@@ -74,27 +84,19 @@ export default function ChatMessageListComponent({ messages }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const MessageRender = (message) => {
|
const MessageRender = (message) => {
|
||||||
if (message.image) {
|
return (
|
||||||
return (
|
<div>
|
||||||
<a href={message.image_path} target="__blank">
|
{message.image_path &&
|
||||||
<img alt="Received" className="message-img" src={message.image_path} />
|
message.image_path.map((i, idx) => (
|
||||||
</a>
|
<div key={idx} style={{ display: "flex", justifyContent: "center" }}>
|
||||||
);
|
<a href={i} target="__blank">
|
||||||
} else {
|
<img alt="Received" className="message-img" src={i} />
|
||||||
return (
|
</a>
|
||||||
<div>
|
|
||||||
<div>{message.text}</div>
|
|
||||||
{message.isoutbound && (
|
|
||||||
<div style={{ color: "slategray", fontSize: 10 }}>
|
|
||||||
{i18n.t("messaging.labels.sentby", {
|
|
||||||
by: message.userid,
|
|
||||||
time: moment(message.created_at).format("MM/DD/YYYY @ hh:mm a"),
|
|
||||||
})}
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
))}
|
||||||
</div>
|
<div>{message.text}</div>
|
||||||
);
|
</div>
|
||||||
}
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const StatusRender = (status) => {
|
const StatusRender = (status) => {
|
||||||
|
|||||||
@@ -34,9 +34,10 @@
|
|||||||
//display: inline-block;
|
//display: inline-block;
|
||||||
|
|
||||||
.message-img {
|
.message-img {
|
||||||
max-width: 3rem;
|
max-width: 10rem;
|
||||||
max-height: 3rem;
|
max-height: 10rem;
|
||||||
object-fit: contain;
|
object-fit: contain;
|
||||||
|
margin: 0.2rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { LoadingOutlined, SendOutlined } from "@ant-design/icons";
|
import { LoadingOutlined, SendOutlined } from "@ant-design/icons";
|
||||||
import { Input, Spin } from "antd";
|
import { Input, Spin } from "antd";
|
||||||
import React, { useEffect, useRef } from "react";
|
import React, { useEffect, useRef, useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
import { createStructuredSelector } from "reselect";
|
import { createStructuredSelector } from "reselect";
|
||||||
@@ -14,6 +14,7 @@ import {
|
|||||||
selectMessage,
|
selectMessage,
|
||||||
} from "../../redux/messaging/messaging.selectors";
|
} from "../../redux/messaging/messaging.selectors";
|
||||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||||
|
import ChatMediaSelector from "../chat-media-selector/chat-media-selector.component";
|
||||||
import ChatPresetsComponent from "../chat-presets/chat-presets.component";
|
import ChatPresetsComponent from "../chat-presets/chat-presets.component";
|
||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
@@ -36,6 +37,7 @@ function ChatSendMessageComponent({
|
|||||||
setMessage,
|
setMessage,
|
||||||
}) {
|
}) {
|
||||||
const inputArea = useRef(null);
|
const inputArea = useRef(null);
|
||||||
|
const [selectedMedia, setSelectedMedia] = useState([]);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
inputArea.current.focus();
|
inputArea.current.focus();
|
||||||
}, [isSending, setMessage]);
|
}, [isSending, setMessage]);
|
||||||
@@ -43,36 +45,55 @@ function ChatSendMessageComponent({
|
|||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const handleEnter = () => {
|
const handleEnter = () => {
|
||||||
|
if (message === "" || !message) return;
|
||||||
logImEXEvent("messaging_send_message");
|
logImEXEvent("messaging_send_message");
|
||||||
sendMessage({
|
const selectedImages = selectedMedia.filter((i) => i.isSelected);
|
||||||
to: conversation.phone_num,
|
if (selectedImages < 11) {
|
||||||
body: message,
|
sendMessage({
|
||||||
messagingServiceSid: bodyshop.messagingservicesid,
|
to: conversation.phone_num,
|
||||||
conversationid: conversation.id,
|
body: message,
|
||||||
});
|
messagingServiceSid: bodyshop.messagingservicesid,
|
||||||
|
conversationid: conversation.id,
|
||||||
|
selectedMedia: selectedImages,
|
||||||
|
});
|
||||||
|
setSelectedMedia(
|
||||||
|
selectedMedia.map((i) => {
|
||||||
|
return { ...i, isSelected: false };
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="imex-flex-row" style={{ width: "100%" }}>
|
<div className="imex-flex-row" style={{ width: "100%" }}>
|
||||||
<ChatPresetsComponent className="imex-flex-row__margin" />
|
<ChatPresetsComponent className="imex-flex-row__margin" />
|
||||||
|
<ChatMediaSelector
|
||||||
<Input.TextArea
|
conversation={conversation}
|
||||||
className="imex-flex-row__margin imex-flex-row__grow"
|
selectedMedia={selectedMedia}
|
||||||
allowClear
|
setSelectedMedia={setSelectedMedia}
|
||||||
autoFocus
|
/>
|
||||||
ref={inputArea}
|
<span style={{ flex: 1 }}>
|
||||||
autoSize={{ minRows: 1, maxRows: 4 }}
|
<Input.TextArea
|
||||||
value={message}
|
className="imex-flex-row__margin imex-flex-row__grow"
|
||||||
disabled={isSending}
|
allowClear
|
||||||
placeholder={t("messaging.labels.typeamessage")}
|
autoFocus
|
||||||
onChange={(e) => setMessage(e.target.value)}
|
ref={inputArea}
|
||||||
onPressEnter={(event) => {
|
autoSize={{ minRows: 1, maxRows: 4 }}
|
||||||
event.preventDefault();
|
value={message}
|
||||||
if (!!!event.shiftKey) handleEnter();
|
disabled={isSending}
|
||||||
}}
|
placeholder={t("messaging.labels.typeamessage")}
|
||||||
|
onChange={(e) => setMessage(e.target.value)}
|
||||||
|
onPressEnter={(event) => {
|
||||||
|
event.preventDefault();
|
||||||
|
if (!!!event.shiftKey) handleEnter();
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
<SendOutlined
|
||||||
|
className="imex-flex-row__margin"
|
||||||
|
disabled={message === "" || !message}
|
||||||
|
onClick={handleEnter}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<SendOutlined className="imex-flex-row__margin" onClick={handleEnter} />
|
|
||||||
<Spin
|
<Spin
|
||||||
style={{ display: `${isSending ? "" : "none"}` }}
|
style={{ display: `${isSending ? "" : "none"}` }}
|
||||||
indicator={
|
indicator={
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import React from "react";
|
|
||||||
import { useQuery } from "@apollo/react-hooks";
|
import { useQuery } from "@apollo/react-hooks";
|
||||||
|
import React from "react";
|
||||||
import { GET_DOCUMENTS_BY_JOB } from "../../graphql/documents.queries";
|
import { GET_DOCUMENTS_BY_JOB } from "../../graphql/documents.queries";
|
||||||
import AlertComponent from "../alert/alert.component";
|
import AlertComponent from "../alert/alert.component";
|
||||||
import LoadingSpinner from "../loading-spinner/loading-spinner.component";
|
import LoadingSpinner from "../loading-spinner/loading-spinner.component";
|
||||||
|
|||||||
@@ -0,0 +1,58 @@
|
|||||||
|
import React, { useEffect } from "react";
|
||||||
|
import Gallery from "react-grid-gallery";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { DetermineFileType } from "../documents-upload/documents-upload.utility";
|
||||||
|
|
||||||
|
function JobsDocumentGalleryExternal({
|
||||||
|
data,
|
||||||
|
|
||||||
|
externalMediaState,
|
||||||
|
}) {
|
||||||
|
const [galleryImages, setgalleryImages] = externalMediaState;
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
let documents = data.reduce((acc, value) => {
|
||||||
|
if (value.type.startsWith("image")) {
|
||||||
|
acc.push({
|
||||||
|
src: `${
|
||||||
|
process.env.REACT_APP_CLOUDINARY_ENDPOINT
|
||||||
|
}/${DetermineFileType(value.type)}/upload/${value.key}`,
|
||||||
|
thumbnail: `${
|
||||||
|
process.env.REACT_APP_CLOUDINARY_ENDPOINT
|
||||||
|
}/${DetermineFileType(value.type)}/upload/${
|
||||||
|
process.env.REACT_APP_CLOUDINARY_THUMB_TRANSFORMATIONS
|
||||||
|
}/${value.key}`,
|
||||||
|
thumbnailHeight: 225,
|
||||||
|
thumbnailWidth: 225,
|
||||||
|
isSelected: false,
|
||||||
|
key: value.key,
|
||||||
|
extension: value.extension,
|
||||||
|
id: value.id,
|
||||||
|
type: value.type,
|
||||||
|
tags: [{ value: value.type, title: value.type }],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return acc;
|
||||||
|
}, []);
|
||||||
|
setgalleryImages(documents);
|
||||||
|
}, [data, setgalleryImages, t]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="clearfix">
|
||||||
|
<Gallery
|
||||||
|
images={galleryImages}
|
||||||
|
backdropClosesModal={true}
|
||||||
|
onSelectImage={(index, image) => {
|
||||||
|
setgalleryImages(
|
||||||
|
galleryImages.map((g, idx) =>
|
||||||
|
index === idx ? { ...g, isSelected: !g.isSelected } : g
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
export default JobsDocumentGalleryExternal;
|
||||||
@@ -1310,11 +1310,13 @@
|
|||||||
"new": "New Conversation"
|
"new": "New Conversation"
|
||||||
},
|
},
|
||||||
"labels": {
|
"labels": {
|
||||||
|
"maxtenimages": "You can only select up to a maximum of 10 images at a time.",
|
||||||
"messaging": "Messaging",
|
"messaging": "Messaging",
|
||||||
"noallowtxt": "This customer has not indicated their permission to be messaged.",
|
"noallowtxt": "This customer has not indicated their permission to be messaged.",
|
||||||
"nojobs": "Not associated to any job.",
|
"nojobs": "Not associated to any job.",
|
||||||
"phonenumber": "Phone #",
|
"phonenumber": "Phone #",
|
||||||
"presets": "Presets",
|
"presets": "Presets",
|
||||||
|
"selectmedia": "Select Media",
|
||||||
"sentby": "Sent by {{by}} at {{time}}",
|
"sentby": "Sent by {{by}} at {{time}}",
|
||||||
"typeamessage": "Send a message..."
|
"typeamessage": "Send a message..."
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1310,11 +1310,13 @@
|
|||||||
"new": ""
|
"new": ""
|
||||||
},
|
},
|
||||||
"labels": {
|
"labels": {
|
||||||
|
"maxtenimages": "",
|
||||||
"messaging": "Mensajería",
|
"messaging": "Mensajería",
|
||||||
"noallowtxt": "",
|
"noallowtxt": "",
|
||||||
"nojobs": "",
|
"nojobs": "",
|
||||||
"phonenumber": "",
|
"phonenumber": "",
|
||||||
"presets": "",
|
"presets": "",
|
||||||
|
"selectmedia": "",
|
||||||
"sentby": "",
|
"sentby": "",
|
||||||
"typeamessage": "Enviar un mensaje..."
|
"typeamessage": "Enviar un mensaje..."
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1310,11 +1310,13 @@
|
|||||||
"new": ""
|
"new": ""
|
||||||
},
|
},
|
||||||
"labels": {
|
"labels": {
|
||||||
|
"maxtenimages": "",
|
||||||
"messaging": "Messagerie",
|
"messaging": "Messagerie",
|
||||||
"noallowtxt": "",
|
"noallowtxt": "",
|
||||||
"nojobs": "",
|
"nojobs": "",
|
||||||
"phonenumber": "",
|
"phonenumber": "",
|
||||||
"presets": "",
|
"presets": "",
|
||||||
|
"selectmedia": "",
|
||||||
"sentby": "",
|
"sentby": "",
|
||||||
"typeamessage": "Envoyer un message..."
|
"typeamessage": "Envoyer un message..."
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,37 @@
|
|||||||
|
- 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:
|
||||||
|
- conversationid
|
||||||
|
- created_at
|
||||||
|
- id
|
||||||
|
- image
|
||||||
|
- image_path
|
||||||
|
- isoutbound
|
||||||
|
- msid
|
||||||
|
- read
|
||||||
|
- status
|
||||||
|
- text
|
||||||
|
- updated_at
|
||||||
|
- userid
|
||||||
|
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_insert_permission
|
||||||
|
- args:
|
||||||
|
permission:
|
||||||
|
check:
|
||||||
|
conversation:
|
||||||
|
bodyshop:
|
||||||
|
associations:
|
||||||
|
_and:
|
||||||
|
- user:
|
||||||
|
authid:
|
||||||
|
_eq: X-Hasura-User-Id
|
||||||
|
- active:
|
||||||
|
_eq: true
|
||||||
|
columns:
|
||||||
|
- conversationid
|
||||||
|
- created_at
|
||||||
|
- id
|
||||||
|
- image
|
||||||
|
- isoutbound
|
||||||
|
- msid
|
||||||
|
- read
|
||||||
|
- status
|
||||||
|
- text
|
||||||
|
- updated_at
|
||||||
|
- userid
|
||||||
|
set: {}
|
||||||
|
role: user
|
||||||
|
table:
|
||||||
|
name: messages
|
||||||
|
schema: public
|
||||||
|
type: create_insert_permission
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
- args:
|
||||||
|
role: user
|
||||||
|
table:
|
||||||
|
name: messages
|
||||||
|
schema: public
|
||||||
|
type: drop_select_permission
|
||||||
|
- args:
|
||||||
|
permission:
|
||||||
|
allow_aggregations: true
|
||||||
|
columns:
|
||||||
|
- conversationid
|
||||||
|
- created_at
|
||||||
|
- id
|
||||||
|
- image
|
||||||
|
- image_path
|
||||||
|
- isoutbound
|
||||||
|
- msid
|
||||||
|
- read
|
||||||
|
- status
|
||||||
|
- text
|
||||||
|
- updated_at
|
||||||
|
- userid
|
||||||
|
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:
|
||||||
|
- conversationid
|
||||||
|
- created_at
|
||||||
|
- id
|
||||||
|
- image
|
||||||
|
- isoutbound
|
||||||
|
- msid
|
||||||
|
- read
|
||||||
|
- status
|
||||||
|
- text
|
||||||
|
- updated_at
|
||||||
|
- userid
|
||||||
|
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,36 @@
|
|||||||
|
- 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
|
||||||
|
set: {}
|
||||||
|
role: user
|
||||||
|
table:
|
||||||
|
name: messages
|
||||||
|
schema: public
|
||||||
|
type: create_update_permission
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
- args:
|
||||||
|
role: user
|
||||||
|
table:
|
||||||
|
name: messages
|
||||||
|
schema: public
|
||||||
|
type: drop_update_permission
|
||||||
|
- args:
|
||||||
|
permission:
|
||||||
|
columns:
|
||||||
|
- conversationid
|
||||||
|
- created_at
|
||||||
|
- id
|
||||||
|
- image
|
||||||
|
- isoutbound
|
||||||
|
- msid
|
||||||
|
- read
|
||||||
|
- status
|
||||||
|
- text
|
||||||
|
- updated_at
|
||||||
|
filter:
|
||||||
|
conversation:
|
||||||
|
bodyshop:
|
||||||
|
associations:
|
||||||
|
_and:
|
||||||
|
- user:
|
||||||
|
authid:
|
||||||
|
_eq: X-Hasura-User-Id
|
||||||
|
- active:
|
||||||
|
_eq: true
|
||||||
|
set: {}
|
||||||
|
role: user
|
||||||
|
table:
|
||||||
|
name: messages
|
||||||
|
schema: public
|
||||||
|
type: create_update_permission
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
- args:
|
||||||
|
cascade: false
|
||||||
|
read_only: false
|
||||||
|
sql: ALTER TABLE "public"."messages" ADD COLUMN "image_path" text;
|
||||||
|
type: run_sql
|
||||||
|
- args:
|
||||||
|
cascade: false
|
||||||
|
read_only: false
|
||||||
|
sql: ALTER TABLE "public"."messages" ALTER COLUMN "image_path" DROP NOT NULL;
|
||||||
|
type: run_sql
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
- args:
|
||||||
|
cascade: false
|
||||||
|
read_only: false
|
||||||
|
sql: ALTER TABLE "public"."messages" DROP COLUMN "image_path" CASCADE;
|
||||||
|
type: run_sql
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
- args:
|
||||||
|
cascade: false
|
||||||
|
read_only: false
|
||||||
|
sql: ALTER TABLE "public"."messages" DROP COLUMN "image_path";
|
||||||
|
type: run_sql
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
- args:
|
||||||
|
cascade: false
|
||||||
|
read_only: false
|
||||||
|
sql: ALTER TABLE "public"."messages" ADD COLUMN "image_path" jsonb NULL;
|
||||||
|
type: run_sql
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
- 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:
|
||||||
|
- conversationid
|
||||||
|
- created_at
|
||||||
|
- id
|
||||||
|
- image
|
||||||
|
- isoutbound
|
||||||
|
- msid
|
||||||
|
- read
|
||||||
|
- status
|
||||||
|
- text
|
||||||
|
- updated_at
|
||||||
|
- userid
|
||||||
|
set: {}
|
||||||
|
role: user
|
||||||
|
table:
|
||||||
|
name: messages
|
||||||
|
schema: public
|
||||||
|
type: create_insert_permission
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
- 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:
|
||||||
|
- conversationid
|
||||||
|
- created_at
|
||||||
|
- id
|
||||||
|
- image
|
||||||
|
- image_path
|
||||||
|
- isoutbound
|
||||||
|
- msid
|
||||||
|
- read
|
||||||
|
- status
|
||||||
|
- text
|
||||||
|
- updated_at
|
||||||
|
- userid
|
||||||
|
set: {}
|
||||||
|
role: user
|
||||||
|
table:
|
||||||
|
name: messages
|
||||||
|
schema: public
|
||||||
|
type: create_insert_permission
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
- args:
|
||||||
|
role: user
|
||||||
|
table:
|
||||||
|
name: messages
|
||||||
|
schema: public
|
||||||
|
type: drop_select_permission
|
||||||
|
- args:
|
||||||
|
permission:
|
||||||
|
allow_aggregations: true
|
||||||
|
columns:
|
||||||
|
- conversationid
|
||||||
|
- created_at
|
||||||
|
- id
|
||||||
|
- image
|
||||||
|
- isoutbound
|
||||||
|
- msid
|
||||||
|
- read
|
||||||
|
- status
|
||||||
|
- text
|
||||||
|
- updated_at
|
||||||
|
- userid
|
||||||
|
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_select_permission
|
||||||
|
- args:
|
||||||
|
permission:
|
||||||
|
allow_aggregations: true
|
||||||
|
columns:
|
||||||
|
- conversationid
|
||||||
|
- created_at
|
||||||
|
- id
|
||||||
|
- image
|
||||||
|
- image_path
|
||||||
|
- isoutbound
|
||||||
|
- msid
|
||||||
|
- read
|
||||||
|
- status
|
||||||
|
- text
|
||||||
|
- updated_at
|
||||||
|
- userid
|
||||||
|
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,35 @@
|
|||||||
|
- args:
|
||||||
|
role: user
|
||||||
|
table:
|
||||||
|
name: messages
|
||||||
|
schema: public
|
||||||
|
type: drop_update_permission
|
||||||
|
- args:
|
||||||
|
permission:
|
||||||
|
columns:
|
||||||
|
- conversationid
|
||||||
|
- created_at
|
||||||
|
- id
|
||||||
|
- image
|
||||||
|
- isoutbound
|
||||||
|
- msid
|
||||||
|
- read
|
||||||
|
- status
|
||||||
|
- text
|
||||||
|
- updated_at
|
||||||
|
filter:
|
||||||
|
conversation:
|
||||||
|
bodyshop:
|
||||||
|
associations:
|
||||||
|
_and:
|
||||||
|
- user:
|
||||||
|
authid:
|
||||||
|
_eq: X-Hasura-User-Id
|
||||||
|
- active:
|
||||||
|
_eq: true
|
||||||
|
set: {}
|
||||||
|
role: user
|
||||||
|
table:
|
||||||
|
name: messages
|
||||||
|
schema: public
|
||||||
|
type: create_update_permission
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
- args:
|
||||||
|
role: user
|
||||||
|
table:
|
||||||
|
name: messages
|
||||||
|
schema: public
|
||||||
|
type: drop_update_permission
|
||||||
|
- args:
|
||||||
|
permission:
|
||||||
|
columns:
|
||||||
|
- conversationid
|
||||||
|
- created_at
|
||||||
|
- id
|
||||||
|
- image
|
||||||
|
- image_path
|
||||||
|
- isoutbound
|
||||||
|
- msid
|
||||||
|
- read
|
||||||
|
- status
|
||||||
|
- text
|
||||||
|
- updated_at
|
||||||
|
filter:
|
||||||
|
conversation:
|
||||||
|
bodyshop:
|
||||||
|
associations:
|
||||||
|
_and:
|
||||||
|
- user:
|
||||||
|
authid:
|
||||||
|
_eq: X-Hasura-User-Id
|
||||||
|
- active:
|
||||||
|
_eq: true
|
||||||
|
set: {}
|
||||||
|
role: user
|
||||||
|
table:
|
||||||
|
name: messages
|
||||||
|
schema: public
|
||||||
|
type: create_update_permission
|
||||||
@@ -3057,17 +3057,17 @@ tables:
|
|||||||
- role: user
|
- role: user
|
||||||
permission:
|
permission:
|
||||||
columns:
|
columns:
|
||||||
|
- conversationid
|
||||||
|
- created_at
|
||||||
|
- id
|
||||||
- image
|
- image
|
||||||
- isoutbound
|
|
||||||
- read
|
|
||||||
- image_path
|
- image_path
|
||||||
|
- isoutbound
|
||||||
- msid
|
- msid
|
||||||
|
- read
|
||||||
- status
|
- status
|
||||||
- text
|
- text
|
||||||
- created_at
|
|
||||||
- updated_at
|
- updated_at
|
||||||
- conversationid
|
|
||||||
- id
|
|
||||||
filter:
|
filter:
|
||||||
conversation:
|
conversation:
|
||||||
bodyshop:
|
bodyshop:
|
||||||
|
|||||||
@@ -29,11 +29,13 @@ exports.receive = (req, res) => {
|
|||||||
phone: phone(req.body.From)[0],
|
phone: phone(req.body.From)[0],
|
||||||
})
|
})
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
|
console.log("re", req.body);
|
||||||
|
|
||||||
let newMessage = {
|
let newMessage = {
|
||||||
msid: req.body.SmsMessageSid,
|
msid: req.body.SmsMessageSid,
|
||||||
text: req.body.Body,
|
text: req.body.Body,
|
||||||
image: !!req.body.MediaUrl0,
|
image: !!req.body.MediaUrl0,
|
||||||
image_path: req.body.MediaUrl0 || null,
|
image_path: generateMediaArray(req.body),
|
||||||
};
|
};
|
||||||
if (response.bodyshops[0]) {
|
if (response.bodyshops[0]) {
|
||||||
//Found a bodyshop - should always happen.
|
//Found a bodyshop - should always happen.
|
||||||
@@ -164,3 +166,17 @@ exports.receive = (req, res) => {
|
|||||||
|
|
||||||
// [0] MediaContentType0: 'image/jpeg',
|
// [0] MediaContentType0: 'image/jpeg',
|
||||||
// MediaContentType0: 'video/3gpp',
|
// MediaContentType0: 'video/3gpp',
|
||||||
|
|
||||||
|
const generateMediaArray = (body) => {
|
||||||
|
const { NumMedia } = body;
|
||||||
|
if (parseInt(NumMedia) > 0) {
|
||||||
|
//stuff
|
||||||
|
const ret = [];
|
||||||
|
for (var i = 0; i < parseInt(NumMedia); i++) {
|
||||||
|
ret.push(body[`MediaUrl${i}`]);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|||||||
@@ -17,7 +17,13 @@ const client = twilio(
|
|||||||
const gqlClient = require("../graphql-client/graphql-client").client;
|
const gqlClient = require("../graphql-client/graphql-client").client;
|
||||||
|
|
||||||
exports.send = (req, res) => {
|
exports.send = (req, res) => {
|
||||||
const { to, messagingServiceSid, body, conversationid } = req.body;
|
const {
|
||||||
|
to,
|
||||||
|
messagingServiceSid,
|
||||||
|
body,
|
||||||
|
conversationid,
|
||||||
|
selectedMedia,
|
||||||
|
} = req.body;
|
||||||
console.log("[Sending Sms] " + conversationid + " | " + body);
|
console.log("[Sending Sms] " + conversationid + " | " + body);
|
||||||
if (!!to && !!messagingServiceSid && !!body && !!conversationid) {
|
if (!!to && !!messagingServiceSid && !!body && !!conversationid) {
|
||||||
client.messages
|
client.messages
|
||||||
@@ -25,6 +31,7 @@ exports.send = (req, res) => {
|
|||||||
body: body,
|
body: body,
|
||||||
messagingServiceSid: messagingServiceSid,
|
messagingServiceSid: messagingServiceSid,
|
||||||
to: phone(to)[0],
|
to: phone(to)[0],
|
||||||
|
mediaUrl: selectedMedia.map((i) => i.src),
|
||||||
})
|
})
|
||||||
.then((message) => {
|
.then((message) => {
|
||||||
let newMessage = {
|
let newMessage = {
|
||||||
@@ -33,6 +40,11 @@ exports.send = (req, res) => {
|
|||||||
conversationid,
|
conversationid,
|
||||||
isoutbound: true,
|
isoutbound: true,
|
||||||
userid: req.user.email,
|
userid: req.user.email,
|
||||||
|
image: req.body.selectedMedia.length > 0,
|
||||||
|
image_path:
|
||||||
|
req.body.selectedMedia.length > 0
|
||||||
|
? selectedMedia.map((i) => i.src)
|
||||||
|
: [],
|
||||||
};
|
};
|
||||||
gqlClient
|
gqlClient
|
||||||
.request(queries.INSERT_MESSAGE, { msg: newMessage })
|
.request(queries.INSERT_MESSAGE, { msg: newMessage })
|
||||||
@@ -55,3 +67,23 @@ exports.send = (req, res) => {
|
|||||||
.json({ success: false, message: "Missing required parameter(s)." });
|
.json({ success: false, message: "Missing required parameter(s)." });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// //Image
|
||||||
|
// acc.push({
|
||||||
|
// src: `${process.env.REACT_APP_CLOUDINARY_ENDPOINT}/${DetermineFileType(
|
||||||
|
// value.type
|
||||||
|
// )}/upload/${value.key}`,
|
||||||
|
// thumbnail: `${
|
||||||
|
// process.env.REACT_APP_CLOUDINARY_ENDPOINT
|
||||||
|
// }/${DetermineFileType(value.type)}/upload/${
|
||||||
|
// process.env.REACT_APP_CLOUDINARY_THUMB_TRANSFORMATIONS
|
||||||
|
// }/${value.key}`,
|
||||||
|
// thumbnailHeight: 225,
|
||||||
|
// thumbnailWidth: 225,
|
||||||
|
// isSelected: false,
|
||||||
|
// key: value.key,
|
||||||
|
// extension: value.extension,
|
||||||
|
// id: value.id,
|
||||||
|
// type: value.type,
|
||||||
|
// tags: [{ value: value.type, title: value.type }],
|
||||||
|
// });
|
||||||
|
|||||||
Reference in New Issue
Block a user