IO-1551 Refactor messaging.
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
<babeledit_project version="1.2" be_version="2.7.1">
|
<babeledit_project be_version="2.7.1" version="1.2">
|
||||||
<!--
|
<!--
|
||||||
|
|
||||||
BabelEdit project file
|
BabelEdit project file
|
||||||
@@ -14199,6 +14199,27 @@
|
|||||||
</translation>
|
</translation>
|
||||||
</translations>
|
</translations>
|
||||||
</concept_node>
|
</concept_node>
|
||||||
|
<concept_node>
|
||||||
|
<name>tryagain</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>view</name>
|
<name>view</name>
|
||||||
<definition_loaded>false</definition_loaded>
|
<definition_loaded>false</definition_loaded>
|
||||||
@@ -14246,6 +14267,27 @@
|
|||||||
<folder_node>
|
<folder_node>
|
||||||
<name>errors</name>
|
<name>errors</name>
|
||||||
<children>
|
<children>
|
||||||
|
<concept_node>
|
||||||
|
<name>fcm</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>notfound</name>
|
<name>notfound</name>
|
||||||
<definition_loaded>false</definition_loaded>
|
<definition_loaded>false</definition_loaded>
|
||||||
@@ -14634,6 +14676,27 @@
|
|||||||
</translation>
|
</translation>
|
||||||
</translations>
|
</translations>
|
||||||
</concept_node>
|
</concept_node>
|
||||||
|
<concept_node>
|
||||||
|
<name>help</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>hours</name>
|
<name>hours</name>
|
||||||
<definition_loaded>false</definition_loaded>
|
<definition_loaded>false</definition_loaded>
|
||||||
|
|||||||
@@ -1,53 +1,39 @@
|
|||||||
importScripts("https://www.gstatic.com/firebasejs/7.14.2/firebase-app.js");
|
// Scripts for firebase and firebase messaging
|
||||||
importScripts(
|
importScripts("https://www.gstatic.com/firebasejs/8.2.0/firebase-app.js");
|
||||||
"https://www.gstatic.com/firebasejs/7.14.2/firebase-messaging.js"
|
importScripts("https://www.gstatic.com/firebasejs/8.2.0/firebase-messaging.js");
|
||||||
);
|
|
||||||
|
|
||||||
firebase.initializeApp({
|
// Initialize the Firebase app in the service worker by passing the generated config
|
||||||
apiKey: "AIzaSyDSezy-jGJreo7ulgpLdlpOwAOrgcaEkhU",
|
const firebaseConfig = {
|
||||||
authDomain: "imex-prod.firebaseapp.com",
|
apiKey: "AIzaSyDPLT8GiDHDR1R4nI66Qi0BY1aYviDPioc",
|
||||||
databaseURL: "https://imex-prod.firebaseio.com",
|
authDomain: "imex-dev.firebaseapp.com",
|
||||||
projectId: "imex-prod",
|
databaseURL: "https://imex-dev.firebaseio.com",
|
||||||
storageBucket: "imex-prod.appspot.com",
|
projectId: "imex-dev",
|
||||||
messagingSenderId: "253497221485",
|
storageBucket: "imex-dev.appspot.com",
|
||||||
appId: "1:253497221485:web:3c81c483b94db84b227a64",
|
messagingSenderId: "759548147434",
|
||||||
measurementId: "G-NTWBKG2L0M",
|
appId: "1:759548147434:web:e8239868a48ceb36700993",
|
||||||
});
|
measurementId: "G-K5XRBVVB4S",
|
||||||
|
};
|
||||||
|
// const firebaseConfig = {
|
||||||
|
// apiKey: "AIzaSyDSezy-jGJreo7ulgpLdlpOwAOrgcaEkhU",
|
||||||
|
// authDomain: "imex-prod.firebaseapp.com",
|
||||||
|
// databaseURL: "https://imex-prod.firebaseio.com",
|
||||||
|
// projectId: "imex-prod",
|
||||||
|
// storageBucket: "imex-prod.appspot.com",
|
||||||
|
// messagingSenderId: "253497221485",
|
||||||
|
// appId: "1:253497221485:web:3c81c483b94db84b227a64",
|
||||||
|
// measurementId: "G-NTWBKG2L0M",
|
||||||
|
// }
|
||||||
|
|
||||||
// firebase.initializeApp({
|
firebase.initializeApp(firebaseConfig);
|
||||||
// apiKey: "AIzaSyDPLT8GiDHDR1R4nI66Qi0BY1aYviDPioc",
|
|
||||||
// authDomain: "imex-dev.firebaseapp.com",
|
|
||||||
// databaseURL: "https://imex-dev.firebaseio.com",
|
|
||||||
// projectId: "imex-dev",
|
|
||||||
// storageBucket: "imex-dev.appspot.com",
|
|
||||||
// messagingSenderId: "759548147434",
|
|
||||||
// appId: "1:759548147434:web:e8239868a48ceb36700993",
|
|
||||||
// measurementId: "G-K5XRBVVB4S",
|
|
||||||
// });
|
|
||||||
|
|
||||||
|
// Retrieve firebase messaging
|
||||||
const messaging = firebase.messaging();
|
const messaging = firebase.messaging();
|
||||||
|
|
||||||
self.addEventListener("fetch", (fetch) => {
|
messaging.onBackgroundMessage(function (payload) {
|
||||||
//required for installation as a PWA. Can ignore for now.
|
console.log("FCM BG MSG", payload);
|
||||||
//console.log("fetch", fetch);
|
// Customize notification here
|
||||||
});
|
const channel = new BroadcastChannel("imex-sw-messages");
|
||||||
|
channel.postMessage(payload);
|
||||||
|
|
||||||
messaging.setBackgroundMessageHandler(function (payload) {
|
//self.registration.showNotification(notificationTitle, notificationOptions);
|
||||||
return self.registration.showNotification(
|
|
||||||
"[SW]" + payload.notification.title,
|
|
||||||
payload.notification
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
//Handles the notification getting clicked.
|
|
||||||
self.addEventListener("notificationclick", function (event) {
|
|
||||||
console.log("SW notificationclick", event);
|
|
||||||
// event.notification.close();
|
|
||||||
if (event.action === "archive") {
|
|
||||||
// Archive action was clicked
|
|
||||||
archiveEmail();
|
|
||||||
} else {
|
|
||||||
// Main body of notification was clicked
|
|
||||||
clients.openWindow("/inbox");
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,43 +0,0 @@
|
|||||||
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);
|
|
||||||
@@ -1,13 +1,16 @@
|
|||||||
import { useSubscription } from "@apollo/client";
|
import { useApolloClient } from "@apollo/client";
|
||||||
import React from "react";
|
import { getToken, onMessage } from "@firebase/messaging";
|
||||||
|
import { Button, notification, Space } from "antd";
|
||||||
|
import axios from "axios";
|
||||||
|
import React, { useEffect } from "react";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
import { createStructuredSelector } from "reselect";
|
import { createStructuredSelector } from "reselect";
|
||||||
import { CONVERSATION_LIST_SUBSCRIPTION } from "../../graphql/conversations.queries";
|
import { messaging, requestForToken } from "../../firebase/firebase.utils";
|
||||||
import { selectChatVisible } from "../../redux/messaging/messaging.selectors";
|
import { selectChatVisible } from "../../redux/messaging/messaging.selectors";
|
||||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||||
import AlertComponent from "../alert/alert.component";
|
import FcmHandler from "../../utils/fcm-handler";
|
||||||
import LoadingSpinner from "../loading-spinner/loading-spinner.component";
|
import ChatPopupComponent from "../chat-popup/chat-popup.component";
|
||||||
import ChatAffixComponent from "./chat-affix.component";
|
|
||||||
import "./chat-affix.styles.scss";
|
import "./chat-affix.styles.scss";
|
||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
@@ -16,32 +19,85 @@ const mapStateToProps = createStructuredSelector({
|
|||||||
});
|
});
|
||||||
|
|
||||||
export function ChatAffixContainer({ bodyshop, chatVisible }) {
|
export function ChatAffixContainer({ bodyshop, chatVisible }) {
|
||||||
const { loading, error, data } = useSubscription(
|
const { t } = useTranslation();
|
||||||
CONVERSATION_LIST_SUBSCRIPTION,
|
const client = useApolloClient();
|
||||||
{
|
useEffect(() => {
|
||||||
skip: !bodyshop || (bodyshop && !bodyshop.messagingservicesid),
|
if (!bodyshop || !bodyshop.messagingservicesid) return;
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
if (loading) return <LoadingSpinner />;
|
async function SubscribeToTopic() {
|
||||||
if (error) return <AlertComponent message={error.message} type="error" />;
|
try {
|
||||||
|
const r = await axios.post("/notifications/subscribe", {
|
||||||
|
fcm_tokens: await getToken(messaging, {
|
||||||
|
vapidKey: process.env.REACT_APP_FIREBASE_PUBLIC_VAPID_KEY,
|
||||||
|
}),
|
||||||
|
type: "messaging",
|
||||||
|
imexshopid: bodyshop.imexshopid,
|
||||||
|
});
|
||||||
|
console.log("FCM Topic Subscription", r.data);
|
||||||
|
} catch (error) {
|
||||||
|
console.log(
|
||||||
|
"Error attempting to subscribe to messaging topic: ",
|
||||||
|
error
|
||||||
|
);
|
||||||
|
notification.open({
|
||||||
|
type: "warning",
|
||||||
|
message: t("general.errors.fcm"),
|
||||||
|
btn: (
|
||||||
|
<Space>
|
||||||
|
<Button
|
||||||
|
onClick={async () => {
|
||||||
|
const resp = await requestForToken();
|
||||||
|
console.log(
|
||||||
|
"🚀 ~ file: chat-affix.container.jsx ~ line 44 ~ resp",
|
||||||
|
resp
|
||||||
|
);
|
||||||
|
SubscribeToTopic();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{t("general.actions.tryagain")}
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
onClick={() => {
|
||||||
|
const win = window.open(
|
||||||
|
"https://help.imex.online/en/article/enabling-notifications-o978xi/",
|
||||||
|
"_blank"
|
||||||
|
);
|
||||||
|
win.focus();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{t("general.labels.help")}
|
||||||
|
</Button>
|
||||||
|
</Space>
|
||||||
|
),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SubscribeToTopic();
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [bodyshop]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
function handleMessage(payload) {
|
||||||
|
FcmHandler({
|
||||||
|
client,
|
||||||
|
payload: (payload && payload.data && payload.data.data) || payload.data,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
const stopMessageListenr = onMessage(messaging, handleMessage);
|
||||||
|
const channel = new BroadcastChannel("imex-sw-messages");
|
||||||
|
channel.addEventListener("message", handleMessage);
|
||||||
|
return () => {
|
||||||
|
stopMessageListenr();
|
||||||
|
channel.removeEventListener("message", handleMessage);
|
||||||
|
};
|
||||||
|
}, [client]);
|
||||||
|
|
||||||
if (!bodyshop || !bodyshop.messagingservicesid) return <></>;
|
if (!bodyshop || !bodyshop.messagingservicesid) return <></>;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={`chat-affix ${chatVisible ? "chat-affix-open" : ""}`}>
|
<div className={`chat-affix ${chatVisible ? "chat-affix-open" : ""}`}>
|
||||||
{bodyshop && bodyshop.messagingservicesid ? (
|
{bodyshop && bodyshop.messagingservicesid ? <ChatPopupComponent /> : null}
|
||||||
<ChatAffixComponent
|
|
||||||
conversationList={(data && data.conversations) || []}
|
|
||||||
unreadCount={
|
|
||||||
(data &&
|
|
||||||
data.conversations.reduce((acc, val) => {
|
|
||||||
return (acc = acc + val.messages_aggregate.aggregate.count);
|
|
||||||
}, 0)) ||
|
|
||||||
0
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
) : null}
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,14 +1,12 @@
|
|||||||
import { Badge, List, Tag, Tooltip } from "antd";
|
import { Badge, List, Tag } from "antd";
|
||||||
import { AlertFilled } from "@ant-design/icons";
|
|
||||||
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";
|
||||||
import { setSelectedConversation } from "../../redux/messaging/messaging.actions";
|
import { setSelectedConversation } from "../../redux/messaging/messaging.actions";
|
||||||
import { selectSelectedConversation } from "../../redux/messaging/messaging.selectors";
|
import { selectSelectedConversation } from "../../redux/messaging/messaging.selectors";
|
||||||
|
import { TimeAgoFormatter } from "../../utils/DateFormatter";
|
||||||
import PhoneFormatter from "../../utils/PhoneFormatter";
|
import PhoneFormatter from "../../utils/PhoneFormatter";
|
||||||
import "./chat-conversation-list.styles.scss";
|
import "./chat-conversation-list.styles.scss";
|
||||||
import { useTranslation } from "react-i18next";
|
|
||||||
import { TimeAgoFormatter } from "../../utils/DateFormatter";
|
|
||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
selectedConversation: selectSelectedConversation,
|
selectedConversation: selectSelectedConversation,
|
||||||
@@ -24,8 +22,6 @@ export function ChatConversationListComponent({
|
|||||||
selectedConversation,
|
selectedConversation,
|
||||||
setSelectedConversation,
|
setSelectedConversation,
|
||||||
}) {
|
}) {
|
||||||
const { t } = useTranslation();
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="chat-list-container">
|
<div className="chat-list-container">
|
||||||
<List
|
<List
|
||||||
@@ -33,6 +29,7 @@ export function ChatConversationListComponent({
|
|||||||
dataSource={conversationList}
|
dataSource={conversationList}
|
||||||
renderItem={(item) => (
|
renderItem={(item) => (
|
||||||
<List.Item
|
<List.Item
|
||||||
|
key={item.id}
|
||||||
onClick={() => setSelectedConversation(item.id)}
|
onClick={() => setSelectedConversation(item.id)}
|
||||||
className={`chat-list-item ${
|
className={`chat-list-item ${
|
||||||
item.id === selectedConversation
|
item.id === selectedConversation
|
||||||
@@ -43,19 +40,9 @@ export function ChatConversationListComponent({
|
|||||||
{item.job_conversations.length > 0 ? (
|
{item.job_conversations.length > 0 ? (
|
||||||
<div className="chat-name">
|
<div className="chat-name">
|
||||||
{item.job_conversations.map((j, idx) => (
|
{item.job_conversations.map((j, idx) => (
|
||||||
<div key={idx} style={{ display: "flex" }}>
|
<div key={idx}>{`${j.job.ownr_fn || ""} ${
|
||||||
{j.job.owner && !j.job.owner.allow_text_message && (
|
j.job.ownr_ln || ""
|
||||||
<Tooltip title={t("messaging.labels.noallowtxt")}>
|
} ${j.job.ownr_co_nm || ""} `}</div>
|
||||||
<AlertFilled
|
|
||||||
className="production-alert"
|
|
||||||
style={{ marginRight: ".3rem", alignItems: "center" }}
|
|
||||||
/>
|
|
||||||
</Tooltip>
|
|
||||||
)}
|
|
||||||
<div>{`${j.job.ownr_fn || ""} ${j.job.ownr_ln || ""} ${
|
|
||||||
j.job.ownr_co_nm || ""
|
|
||||||
} `}</div>
|
|
||||||
</div>
|
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import "./chat-conversation.styles.scss";
|
|||||||
export default function ChatConversationComponent({
|
export default function ChatConversationComponent({
|
||||||
subState,
|
subState,
|
||||||
conversation,
|
conversation,
|
||||||
|
messages,
|
||||||
handleMarkConversationAsRead,
|
handleMarkConversationAsRead,
|
||||||
}) {
|
}) {
|
||||||
const [loading, error] = subState;
|
const [loading, error] = subState;
|
||||||
@@ -16,8 +17,6 @@ export default function ChatConversationComponent({
|
|||||||
if (loading) return <LoadingSkeleton />;
|
if (loading) return <LoadingSkeleton />;
|
||||||
if (error) return <AlertComponent message={error.message} type="error" />;
|
if (error) return <AlertComponent message={error.message} type="error" />;
|
||||||
|
|
||||||
const messages = (conversation && conversation.messages) || [];
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className="chat-conversation"
|
className="chat-conversation"
|
||||||
|
|||||||
@@ -1,18 +1,32 @@
|
|||||||
import { useMutation, useSubscription } from "@apollo/client";
|
import { useMutation, useQuery, useSubscription } from "@apollo/client";
|
||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
import { createStructuredSelector } from "reselect";
|
import { createStructuredSelector } from "reselect";
|
||||||
import { CONVERSATION_SUBSCRIPTION_BY_PK } from "../../graphql/conversations.queries";
|
import {
|
||||||
|
CONVERSATION_SUBSCRIPTION_BY_PK,
|
||||||
|
GET_CONVERSATION_DETAILS,
|
||||||
|
} from "../../graphql/conversations.queries";
|
||||||
import { MARK_MESSAGES_AS_READ_BY_CONVERSATION } from "../../graphql/messages.queries";
|
import { MARK_MESSAGES_AS_READ_BY_CONVERSATION } from "../../graphql/messages.queries";
|
||||||
import { selectSelectedConversation } from "../../redux/messaging/messaging.selectors";
|
import { selectSelectedConversation } from "../../redux/messaging/messaging.selectors";
|
||||||
import ChatConversationComponent from "./chat-conversation.component";
|
import ChatConversationComponent from "./chat-conversation.component";
|
||||||
|
import axios from "axios";
|
||||||
|
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
selectedConversation: selectSelectedConversation,
|
selectedConversation: selectSelectedConversation,
|
||||||
|
bodyshop: selectBodyshop,
|
||||||
});
|
});
|
||||||
|
|
||||||
export default connect(mapStateToProps, null)(ChatConversationContainer);
|
export default connect(mapStateToProps, null)(ChatConversationContainer);
|
||||||
|
|
||||||
export function ChatConversationContainer({ selectedConversation }) {
|
export function ChatConversationContainer({ bodyshop, selectedConversation }) {
|
||||||
|
const {
|
||||||
|
loading: convoLoading,
|
||||||
|
error: convoError,
|
||||||
|
data: convoData,
|
||||||
|
} = useQuery(GET_CONVERSATION_DETAILS, {
|
||||||
|
variables: { conversationId: selectedConversation },
|
||||||
|
});
|
||||||
|
|
||||||
const { loading, error, data } = useSubscription(
|
const { loading, error, data } = useSubscription(
|
||||||
CONVERSATION_SUBSCRIPTION_BY_PK,
|
CONVERSATION_SUBSCRIPTION_BY_PK,
|
||||||
{
|
{
|
||||||
@@ -26,30 +40,46 @@ export function ChatConversationContainer({ selectedConversation }) {
|
|||||||
MARK_MESSAGES_AS_READ_BY_CONVERSATION,
|
MARK_MESSAGES_AS_READ_BY_CONVERSATION,
|
||||||
{
|
{
|
||||||
variables: { conversationId: selectedConversation },
|
variables: { conversationId: selectedConversation },
|
||||||
|
update(cache) {
|
||||||
|
cache.modify({
|
||||||
|
id: cache.identify({
|
||||||
|
__typename: "conversations",
|
||||||
|
id: selectedConversation,
|
||||||
|
}),
|
||||||
|
fields: {
|
||||||
|
messages_aggregate(cached) {
|
||||||
|
return { aggregate: { count: 0 } };
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
const unreadCount =
|
const unreadCount =
|
||||||
(data &&
|
data &&
|
||||||
data.conversations_by_pk &&
|
data.messages &&
|
||||||
data.conversations_by_pk &&
|
data.messages.reduce((acc, val) => {
|
||||||
data.conversations_by_pk.messages_aggregate &&
|
return !val.read && !val.isoutbound ? acc + 1 : acc;
|
||||||
data.conversations_by_pk.messages_aggregate.aggregate &&
|
}, 0);
|
||||||
data.conversations_by_pk.messages_aggregate.aggregate.count) ||
|
|
||||||
0;
|
|
||||||
|
|
||||||
const handleMarkConversationAsRead = async () => {
|
const handleMarkConversationAsRead = async () => {
|
||||||
if (unreadCount > 0 && !!selectedConversation && !markingAsReadInProgress) {
|
if (unreadCount > 0 && !!selectedConversation && !markingAsReadInProgress) {
|
||||||
setMarkingAsReadInProgress(true);
|
setMarkingAsReadInProgress(true);
|
||||||
await markConversationRead();
|
await markConversationRead({});
|
||||||
|
await axios.post("/sms/markConversationRead", {
|
||||||
|
conversationid: selectedConversation,
|
||||||
|
imexshopid: bodyshop.imexshopid,
|
||||||
|
});
|
||||||
setMarkingAsReadInProgress(false);
|
setMarkingAsReadInProgress(false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ChatConversationComponent
|
<ChatConversationComponent
|
||||||
subState={[loading, error]}
|
subState={[loading || convoLoading, error || convoError]}
|
||||||
conversation={data ? data.conversations_by_pk : {}}
|
conversation={convoData ? convoData.conversations_by_pk : {}}
|
||||||
|
messages={data ? data.messages : []}
|
||||||
handleMarkConversationAsRead={handleMarkConversationAsRead}
|
handleMarkConversationAsRead={handleMarkConversationAsRead}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ export function ChatNewConversation({ openChatByPhone }) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Popover trigger="click" content={popContent}>
|
<Popover trigger="click" content={popContent}>
|
||||||
<PlusCircleFilled style={{ margin: "1rem" }} />
|
<PlusCircleFilled />
|
||||||
</Popover>
|
</Popover>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,54 +1,108 @@
|
|||||||
import { ShrinkOutlined, InfoCircleOutlined } from "@ant-design/icons";
|
import {
|
||||||
import { Col, Row, Tooltip, Typography } from "antd";
|
ShrinkOutlined,
|
||||||
import React from "react";
|
InfoCircleOutlined,
|
||||||
|
SyncOutlined,
|
||||||
|
} from "@ant-design/icons";
|
||||||
|
|
||||||
|
import { Col, Row, Tooltip, Space, Typography, Badge, Card } from "antd";
|
||||||
|
import React, { useEffect } 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";
|
||||||
import { toggleChatVisible } from "../../redux/messaging/messaging.actions";
|
import { toggleChatVisible } from "../../redux/messaging/messaging.actions";
|
||||||
import ChatConversationListComponent from "../chat-conversation-list/chat-conversation-list.component";
|
import ChatConversationListComponent from "../chat-conversation-list/chat-conversation-list.component";
|
||||||
import ChatConversationContainer from "../chat-conversation/chat-conversation.container";
|
import ChatConversationContainer from "../chat-conversation/chat-conversation.container";
|
||||||
import { selectSelectedConversation } from "../../redux/messaging/messaging.selectors";
|
import {
|
||||||
|
selectChatVisible,
|
||||||
|
selectSelectedConversation,
|
||||||
|
} from "../../redux/messaging/messaging.selectors";
|
||||||
import "./chat-popup.styles.scss";
|
import "./chat-popup.styles.scss";
|
||||||
import ChatNewConversation from "../chat-new-conversation/chat-new-conversation.component";
|
import ChatNewConversation from "../chat-new-conversation/chat-new-conversation.component";
|
||||||
|
import { CONVERSATION_LIST_QUERY } from "../../graphql/conversations.queries";
|
||||||
|
import { useQuery } from "@apollo/client";
|
||||||
|
import LoadingSpinner from "../loading-spinner/loading-spinner.component";
|
||||||
|
import { MessageOutlined } from "@ant-design/icons";
|
||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
selectedConversation: selectSelectedConversation,
|
selectedConversation: selectSelectedConversation,
|
||||||
|
chatVisible: selectChatVisible,
|
||||||
});
|
});
|
||||||
const mapDispatchToProps = (dispatch) => ({
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
toggleChatVisible: () => dispatch(toggleChatVisible()),
|
toggleChatVisible: () => dispatch(toggleChatVisible()),
|
||||||
});
|
});
|
||||||
|
|
||||||
export function ChatPopupComponent({
|
export function ChatPopupComponent({
|
||||||
conversationList,
|
chatVisible,
|
||||||
selectedConversation,
|
selectedConversation,
|
||||||
toggleChatVisible,
|
toggleChatVisible,
|
||||||
}) {
|
}) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
return (
|
|
||||||
<div className="chat-popup">
|
|
||||||
<div style={{ display: "flex", alignItems: "center" }}>
|
|
||||||
<Typography.Title level={4}>
|
|
||||||
{t("messaging.labels.messaging")}
|
|
||||||
</Typography.Title>
|
|
||||||
<ChatNewConversation />
|
|
||||||
<Tooltip title={t("messaging.labels.recentonly")}>
|
|
||||||
<InfoCircleOutlined />
|
|
||||||
</Tooltip>
|
|
||||||
</div>
|
|
||||||
<ShrinkOutlined
|
|
||||||
onClick={() => toggleChatVisible()}
|
|
||||||
style={{ position: "absolute", right: ".5rem", top: ".5rem" }}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<Row gutter={[8, 8]} className="chat-popup-content">
|
const { loading, data, refetch, called } = useQuery(
|
||||||
<Col span={8}>
|
CONVERSATION_LIST_QUERY,
|
||||||
<ChatConversationListComponent conversationList={conversationList} />
|
{}
|
||||||
</Col>
|
);
|
||||||
<Col span={16}>
|
|
||||||
{selectedConversation ? <ChatConversationContainer /> : null}
|
useEffect(() => {
|
||||||
</Col>
|
if (called && chatVisible) refetch();
|
||||||
</Row>
|
}, [chatVisible, called, refetch]);
|
||||||
</div>
|
|
||||||
|
const unreadCount = data
|
||||||
|
? data.conversations.reduce(
|
||||||
|
(acc, val) => val.messages_aggregate.aggregate.count + acc,
|
||||||
|
0
|
||||||
|
)
|
||||||
|
: 0;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Badge count={unreadCount}>
|
||||||
|
<Card size="small">
|
||||||
|
{chatVisible ? (
|
||||||
|
<div className="chat-popup">
|
||||||
|
<Space align="center">
|
||||||
|
<Typography.Title level={4}>
|
||||||
|
{t("messaging.labels.messaging")}
|
||||||
|
</Typography.Title>
|
||||||
|
<ChatNewConversation />
|
||||||
|
<Tooltip title={t("messaging.labels.recentonly")}>
|
||||||
|
<InfoCircleOutlined />
|
||||||
|
</Tooltip>
|
||||||
|
<SyncOutlined
|
||||||
|
style={{ cursor: "pointer" }}
|
||||||
|
onClick={() => refetch()}
|
||||||
|
/>
|
||||||
|
</Space>
|
||||||
|
<ShrinkOutlined
|
||||||
|
onClick={() => toggleChatVisible()}
|
||||||
|
style={{ position: "absolute", right: ".5rem", top: ".5rem" }}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Row gutter={[8, 8]} className="chat-popup-content">
|
||||||
|
<Col span={8}>
|
||||||
|
{loading ? (
|
||||||
|
<LoadingSpinner />
|
||||||
|
) : (
|
||||||
|
<ChatConversationListComponent
|
||||||
|
conversationList={data ? data.conversations : []}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</Col>
|
||||||
|
<Col span={16}>
|
||||||
|
{selectedConversation ? <ChatConversationContainer /> : null}
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div
|
||||||
|
onClick={() => toggleChatVisible()}
|
||||||
|
style={{ cursor: "pointer" }}
|
||||||
|
>
|
||||||
|
<MessageOutlined />
|
||||||
|
<strong>{t("messaging.labels.messaging")}</strong>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</Card>
|
||||||
|
</Badge>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
export default connect(mapStateToProps, mapDispatchToProps)(ChatPopupComponent);
|
export default connect(mapStateToProps, mapDispatchToProps)(ChatPopupComponent);
|
||||||
|
|||||||
@@ -2,8 +2,7 @@ import { getAnalytics, logEvent } from "firebase/analytics";
|
|||||||
import { initializeApp } from "firebase/app";
|
import { initializeApp } from "firebase/app";
|
||||||
import { getAuth, updatePassword, updateProfile } from "firebase/auth";
|
import { getAuth, updatePassword, updateProfile } from "firebase/auth";
|
||||||
import { getFirestore } from "firebase/firestore";
|
import { getFirestore } from "firebase/firestore";
|
||||||
import { tracker } from "../App/App.container";
|
import { getMessaging, getToken, onMessage } from "firebase/messaging";
|
||||||
//import { getMessaging } from "firebase/messaging";
|
|
||||||
import { store } from "../redux/store";
|
import { store } from "../redux/store";
|
||||||
|
|
||||||
const config = JSON.parse(process.env.REACT_APP_FIREBASE_CONFIG);
|
const config = JSON.parse(process.env.REACT_APP_FIREBASE_CONFIG);
|
||||||
@@ -40,17 +39,34 @@ export const updateCurrentPassword = async (password) => {
|
|||||||
return updatePassword(currentUser, password);
|
return updatePassword(currentUser, password);
|
||||||
};
|
};
|
||||||
|
|
||||||
//let messaging;
|
export const messaging = getMessaging();
|
||||||
// try {
|
|
||||||
// messaging = getMessaging();
|
|
||||||
// // Project Settings => Cloud Messaging => Web Push certificates
|
|
||||||
// messaging.usePublicVapidKey(process.env.REACT_APP_FIREBASE_PUBLIC_VAPID_KEY);
|
|
||||||
// console.log("[FCM UTIL] FCM initialized successfully.");
|
|
||||||
// } catch {
|
|
||||||
// console.log("[FCM UTIL] Firebase Messaging is likely unsupported.");
|
|
||||||
// }
|
|
||||||
|
|
||||||
// export { messaging };
|
export const requestForToken = () => {
|
||||||
|
return getToken(messaging, {
|
||||||
|
vapidKey: process.env.REACT_APP_FIREBASE_PUBLIC_VAPID_KEY,
|
||||||
|
})
|
||||||
|
.then((currentToken) => {
|
||||||
|
if (currentToken) {
|
||||||
|
console.log("current token for client: ", currentToken);
|
||||||
|
// Perform any other necessary action with the token
|
||||||
|
} else {
|
||||||
|
// Show permission request UI
|
||||||
|
console.log(
|
||||||
|
"No registration token available. Request permission to generate one."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.log("An error occurred while retrieving token. ", err);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
export const onMessageListener = () =>
|
||||||
|
new Promise((resolve) => {
|
||||||
|
onMessage(messaging, (payload) => {
|
||||||
|
console.log("Inbound FCM Message", payload);
|
||||||
|
resolve(payload);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
export const logImEXEvent = (eventName, additionalParams, stateProp = null) => {
|
export const logImEXEvent = (eventName, additionalParams, stateProp = null) => {
|
||||||
const state = stateProp || store.getState();
|
const state = stateProp || store.getState();
|
||||||
@@ -70,9 +86,6 @@ export const logImEXEvent = (eventName, additionalParams, stateProp = null) => {
|
|||||||
// eventParams
|
// eventParams
|
||||||
// );
|
// );
|
||||||
logEvent(analytics, eventName, eventParams);
|
logEvent(analytics, eventName, eventParams);
|
||||||
|
|
||||||
//Log event to OpenReplay server.
|
|
||||||
tracker.event(eventName, eventParams);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// if (messaging) {
|
// if (messaging) {
|
||||||
|
|||||||
@@ -1,15 +1,54 @@
|
|||||||
import { gql } from "@apollo/client";
|
import { gql } from "@apollo/client";
|
||||||
|
|
||||||
export const CONVERSATION_LIST_SUBSCRIPTION = gql`
|
// export const CONVERSATION_LIST_SUBSCRIPTION = gql`
|
||||||
subscription CONVERSATION_LIST_SUBSCRIPTION {
|
// subscription CONVERSATION_LIST_SUBSCRIPTION {
|
||||||
|
// conversations(
|
||||||
|
// order_by: { updated_at: desc }
|
||||||
|
// limit: 50
|
||||||
|
// where: { archived: { _eq: false } }
|
||||||
|
// ) {
|
||||||
|
// phone_num
|
||||||
|
// id
|
||||||
|
// updated_at
|
||||||
|
// unreadcnt
|
||||||
|
// messages_aggregate(
|
||||||
|
// where: { read: { _eq: false }, isoutbound: { _eq: false } }
|
||||||
|
// ) {
|
||||||
|
// aggregate {
|
||||||
|
// count
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// job_conversations {
|
||||||
|
// job {
|
||||||
|
// id
|
||||||
|
// ro_number
|
||||||
|
// ownr_fn
|
||||||
|
// ownr_ln
|
||||||
|
// ownr_co_nm
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// `;
|
||||||
|
|
||||||
|
export const CONVERSATION_LIST_QUERY = gql`
|
||||||
|
query CONVERSATION_LIST_QUERY {
|
||||||
conversations(
|
conversations(
|
||||||
order_by: { updated_at: desc }
|
order_by: { updated_at: desc }
|
||||||
limit: 100
|
limit: 50
|
||||||
where: { archived: { _eq: false } }
|
where: { archived: { _eq: false } }
|
||||||
) {
|
) {
|
||||||
phone_num
|
phone_num
|
||||||
id
|
id
|
||||||
updated_at
|
updated_at
|
||||||
|
unreadcnt
|
||||||
|
messages_aggregate(
|
||||||
|
where: { read: { _eq: false }, isoutbound: { _eq: false } }
|
||||||
|
) {
|
||||||
|
aggregate {
|
||||||
|
count
|
||||||
|
}
|
||||||
|
}
|
||||||
job_conversations {
|
job_conversations {
|
||||||
job {
|
job {
|
||||||
id
|
id
|
||||||
@@ -17,17 +56,6 @@ export const CONVERSATION_LIST_SUBSCRIPTION = gql`
|
|||||||
ownr_fn
|
ownr_fn
|
||||||
ownr_ln
|
ownr_ln
|
||||||
ownr_co_nm
|
ownr_co_nm
|
||||||
owner {
|
|
||||||
id
|
|
||||||
allow_text_message
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
messages_aggregate(
|
|
||||||
where: { read: { _eq: false }, isoutbound: { _eq: false } }
|
|
||||||
) {
|
|
||||||
aggregate {
|
|
||||||
count
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -36,24 +64,26 @@ export const CONVERSATION_LIST_SUBSCRIPTION = gql`
|
|||||||
|
|
||||||
export const CONVERSATION_SUBSCRIPTION_BY_PK = gql`
|
export const CONVERSATION_SUBSCRIPTION_BY_PK = gql`
|
||||||
subscription CONVERSATION_SUBSCRIPTION_BY_PK($conversationId: uuid!) {
|
subscription CONVERSATION_SUBSCRIPTION_BY_PK($conversationId: uuid!) {
|
||||||
|
messages(
|
||||||
|
order_by: { created_at: asc_nulls_first }
|
||||||
|
where: { conversationid: { _eq: $conversationId } }
|
||||||
|
) {
|
||||||
|
id
|
||||||
|
status
|
||||||
|
text
|
||||||
|
isoutbound
|
||||||
|
image
|
||||||
|
image_path
|
||||||
|
userid
|
||||||
|
created_at
|
||||||
|
read
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const GET_CONVERSATION_DETAILS = gql`
|
||||||
|
query GET_CONVERSATION_DETAILS($conversationId: uuid!) {
|
||||||
conversations_by_pk(id: $conversationId) {
|
conversations_by_pk(id: $conversationId) {
|
||||||
messages(order_by: { created_at: asc_nulls_first }) {
|
|
||||||
id
|
|
||||||
status
|
|
||||||
text
|
|
||||||
isoutbound
|
|
||||||
image
|
|
||||||
image_path
|
|
||||||
userid
|
|
||||||
created_at
|
|
||||||
}
|
|
||||||
messages_aggregate(
|
|
||||||
where: { read: { _eq: false }, isoutbound: { _eq: false } }
|
|
||||||
) {
|
|
||||||
aggregate {
|
|
||||||
count
|
|
||||||
}
|
|
||||||
}
|
|
||||||
id
|
id
|
||||||
phone_num
|
phone_num
|
||||||
archived
|
archived
|
||||||
|
|||||||
@@ -8,6 +8,8 @@ export const MARK_MESSAGES_AS_READ_BY_CONVERSATION = gql`
|
|||||||
) {
|
) {
|
||||||
returning {
|
returning {
|
||||||
id
|
id
|
||||||
|
read
|
||||||
|
isoutbound
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,13 +5,12 @@ import preval from "preval.macro";
|
|||||||
import React, { lazy, Suspense, useEffect } from "react";
|
import React, { lazy, Suspense, useEffect } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
import ErrorBoundary from "../../components/error-boundary/error-boundary.component";
|
|
||||||
import { Link, Route, Switch } from "react-router-dom";
|
import { Link, Route, Switch } from "react-router-dom";
|
||||||
import { createStructuredSelector } from "reselect";
|
import { createStructuredSelector } from "reselect";
|
||||||
import BreadCrumbs from "../../components/breadcrumbs/breadcrumbs.component";
|
import BreadCrumbs from "../../components/breadcrumbs/breadcrumbs.component";
|
||||||
import ChatAffixContainer from "../../components/chat-affix/chat-affix.container";
|
import ChatAffixContainer from "../../components/chat-affix/chat-affix.container";
|
||||||
import ConflictComponent from "../../components/conflict/conflict.component";
|
import ConflictComponent from "../../components/conflict/conflict.component";
|
||||||
import FcmNotification from "../../components/fcm-notification/fcm-notification.component";
|
import ErrorBoundary from "../../components/error-boundary/error-boundary.component";
|
||||||
//import FooterComponent from "../../components/footer/footer.component";
|
//import FooterComponent from "../../components/footer/footer.component";
|
||||||
//Component Imports
|
//Component Imports
|
||||||
import HeaderContainer from "../../components/header/header.container";
|
import HeaderContainer from "../../components/header/header.container";
|
||||||
@@ -20,12 +19,11 @@ import PartnerPingComponent from "../../components/partner-ping/partner-ping.com
|
|||||||
import PrintCenterModalContainer from "../../components/print-center-modal/print-center-modal.container";
|
import PrintCenterModalContainer from "../../components/print-center-modal/print-center-modal.container";
|
||||||
import ShopSubStatusComponent from "../../components/shop-sub-status/shop-sub-status.component";
|
import ShopSubStatusComponent from "../../components/shop-sub-status/shop-sub-status.component";
|
||||||
import TestComponent from "../../components/_test/test.component";
|
import TestComponent from "../../components/_test/test.component";
|
||||||
import { QUERY_STRIPE_ID } from "../../graphql/bodyshop.queries";
|
import { requestForToken } from "../../firebase/firebase.utils";
|
||||||
import {
|
import {
|
||||||
selectBodyshop,
|
selectBodyshop,
|
||||||
selectInstanceConflict,
|
selectInstanceConflict,
|
||||||
} from "../../redux/user/user.selectors";
|
} from "../../redux/user/user.selectors";
|
||||||
import client from "../../utils/GraphQLClient";
|
|
||||||
import "./manage.page.styles.scss";
|
import "./manage.page.styles.scss";
|
||||||
|
|
||||||
const ManageRootPage = lazy(() =>
|
const ManageRootPage = lazy(() =>
|
||||||
@@ -166,16 +164,15 @@ const Dms = lazy(() => import("../dms/dms.container"));
|
|||||||
const { Content, Footer } = Layout;
|
const { Content, Footer } = Layout;
|
||||||
|
|
||||||
const stripePromise = new Promise((resolve, reject) => {
|
const stripePromise = new Promise((resolve, reject) => {
|
||||||
client.query({ query: QUERY_STRIPE_ID }).then((resp) => {
|
// client.query({ query: QUERY_STRIPE_ID }).then((resp) => {
|
||||||
if (resp.data.bodyshops[0])
|
// if (resp.data.bodyshops[0])
|
||||||
resolve(
|
resolve(
|
||||||
loadStripe(process.env.REACT_APP_STRIPE_PUBLIC_KEY, {
|
loadStripe(process.env.REACT_APP_STRIPE_PUBLIC_KEY, {
|
||||||
stripeAccount:
|
stripeAccount: "No Stripe Id Resolve",
|
||||||
resp.data.bodyshops[0].stripe_acct_id || "No Stripe Id Resolve",
|
})
|
||||||
})
|
);
|
||||||
);
|
// reject();
|
||||||
reject();
|
// });
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
@@ -188,7 +185,9 @@ export function Manage({ match, conflict, bodyshop }) {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const widgetId = "IABVNO4scRKY11XBQkNr";
|
const widgetId = "IABVNO4scRKY11XBQkNr";
|
||||||
window.noticeable.render("widget", widgetId);
|
window.noticeable.render("widget", widgetId);
|
||||||
|
requestForToken();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
document.title = t("titles.app");
|
document.title = t("titles.app");
|
||||||
}, [t]);
|
}, [t]);
|
||||||
@@ -395,7 +394,6 @@ export function Manage({ match, conflict, bodyshop }) {
|
|||||||
<HeaderContainer />
|
<HeaderContainer />
|
||||||
|
|
||||||
<Content className="content-container">
|
<Content className="content-container">
|
||||||
<FcmNotification />
|
|
||||||
<PartnerPingComponent />
|
<PartnerPingComponent />
|
||||||
<ErrorBoundary>{PageContent}</ErrorBoundary>
|
<ErrorBoundary>{PageContent}</ErrorBoundary>
|
||||||
<BackTop />
|
<BackTop />
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { connect } from "react-redux";
|
|||||||
import { Redirect, Route, Switch } from "react-router-dom";
|
import { Redirect, Route, Switch } from "react-router-dom";
|
||||||
import { createStructuredSelector } from "reselect";
|
import { createStructuredSelector } from "reselect";
|
||||||
import ErrorBoundary from "../../components/error-boundary/error-boundary.component";
|
import ErrorBoundary from "../../components/error-boundary/error-boundary.component";
|
||||||
import FcmNotification from "../../components/fcm-notification/fcm-notification.component";
|
|
||||||
import LoadingSpinner from "../../components/loading-spinner/loading-spinner.component";
|
import LoadingSpinner from "../../components/loading-spinner/loading-spinner.component";
|
||||||
import TechHeader from "../../components/tech-header/tech-header.component";
|
import TechHeader from "../../components/tech-header/tech-header.component";
|
||||||
import TechSider from "../../components/tech-sider/tech-sider.component";
|
import TechSider from "../../components/tech-sider/tech-sider.component";
|
||||||
@@ -58,7 +58,6 @@ export function TechPage({ technician, match }) {
|
|||||||
{technician ? null : <Redirect to={`${match.path}/login`} />}
|
{technician ? null : <Redirect to={`${match.path}/login`} />}
|
||||||
<TechHeader />
|
<TechHeader />
|
||||||
<Content className="tech-content-container">
|
<Content className="tech-content-container">
|
||||||
<FcmNotification />
|
|
||||||
<ErrorBoundary>
|
<ErrorBoundary>
|
||||||
<Suspense
|
<Suspense
|
||||||
fallback={
|
fallback={
|
||||||
|
|||||||
@@ -899,10 +899,12 @@
|
|||||||
"selectall": "Select All",
|
"selectall": "Select All",
|
||||||
"senderrortosupport": "Send Error to Support",
|
"senderrortosupport": "Send Error to Support",
|
||||||
"submit": "Submit",
|
"submit": "Submit",
|
||||||
|
"tryagain": "Try Again",
|
||||||
"view": "View",
|
"view": "View",
|
||||||
"viewreleasenotes": "See What's Changed"
|
"viewreleasenotes": "See What's Changed"
|
||||||
},
|
},
|
||||||
"errors": {
|
"errors": {
|
||||||
|
"fcm": "You must allow notification permissions to have real time messaging. Click to try again.",
|
||||||
"notfound": "No record was found."
|
"notfound": "No record was found."
|
||||||
},
|
},
|
||||||
"itemtypes": {
|
"itemtypes": {
|
||||||
@@ -925,6 +927,7 @@
|
|||||||
"exceptiontitle": "An error has occurred.",
|
"exceptiontitle": "An error has occurred.",
|
||||||
"friday": "Friday",
|
"friday": "Friday",
|
||||||
"globalsearch": "Global Search",
|
"globalsearch": "Global Search",
|
||||||
|
"help": "Help",
|
||||||
"hours": "hrs",
|
"hours": "hrs",
|
||||||
"in": "In",
|
"in": "In",
|
||||||
"instanceconflictext": "Your $t(titles.app) account can only be used on one device at any given time. Refresh your session to take control.",
|
"instanceconflictext": "Your $t(titles.app) account can only be used on one device at any given time. Refresh your session to take control.",
|
||||||
|
|||||||
@@ -899,10 +899,12 @@
|
|||||||
"selectall": "",
|
"selectall": "",
|
||||||
"senderrortosupport": "",
|
"senderrortosupport": "",
|
||||||
"submit": "",
|
"submit": "",
|
||||||
|
"tryagain": "",
|
||||||
"view": "",
|
"view": "",
|
||||||
"viewreleasenotes": ""
|
"viewreleasenotes": ""
|
||||||
},
|
},
|
||||||
"errors": {
|
"errors": {
|
||||||
|
"fcm": "",
|
||||||
"notfound": ""
|
"notfound": ""
|
||||||
},
|
},
|
||||||
"itemtypes": {
|
"itemtypes": {
|
||||||
@@ -925,6 +927,7 @@
|
|||||||
"exceptiontitle": "",
|
"exceptiontitle": "",
|
||||||
"friday": "",
|
"friday": "",
|
||||||
"globalsearch": "",
|
"globalsearch": "",
|
||||||
|
"help": "",
|
||||||
"hours": "",
|
"hours": "",
|
||||||
"in": "en",
|
"in": "en",
|
||||||
"instanceconflictext": "",
|
"instanceconflictext": "",
|
||||||
|
|||||||
@@ -899,10 +899,12 @@
|
|||||||
"selectall": "",
|
"selectall": "",
|
||||||
"senderrortosupport": "",
|
"senderrortosupport": "",
|
||||||
"submit": "",
|
"submit": "",
|
||||||
|
"tryagain": "",
|
||||||
"view": "",
|
"view": "",
|
||||||
"viewreleasenotes": ""
|
"viewreleasenotes": ""
|
||||||
},
|
},
|
||||||
"errors": {
|
"errors": {
|
||||||
|
"fcm": "",
|
||||||
"notfound": ""
|
"notfound": ""
|
||||||
},
|
},
|
||||||
"itemtypes": {
|
"itemtypes": {
|
||||||
@@ -925,6 +927,7 @@
|
|||||||
"exceptiontitle": "",
|
"exceptiontitle": "",
|
||||||
"friday": "",
|
"friday": "",
|
||||||
"globalsearch": "",
|
"globalsearch": "",
|
||||||
|
"help": "",
|
||||||
"hours": "",
|
"hours": "",
|
||||||
"in": "dans",
|
"in": "dans",
|
||||||
"instanceconflictext": "",
|
"instanceconflictext": "",
|
||||||
|
|||||||
@@ -91,13 +91,11 @@ export default function CiecaSelect(parts = true, labor = true) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function GetPartTypeName(part_type) {
|
export function GetPartTypeName(part_type) {
|
||||||
console.log(part_type);
|
|
||||||
if (!part_type) return null;
|
if (!part_type) return null;
|
||||||
return i18n.t(`joblines.fields.part_types.${part_type.toUpperCase()}`);
|
return i18n.t(`joblines.fields.part_types.${part_type.toUpperCase()}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function Get(part_type) {
|
export function Get(part_type) {
|
||||||
console.log(part_type);
|
|
||||||
if (!part_type) return null;
|
if (!part_type) return null;
|
||||||
return i18n.t(`joblines.fields.part_types.${part_type.toUpperCase()}`);
|
return i18n.t(`joblines.fields.part_types.${part_type.toUpperCase()}`);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -50,13 +50,18 @@ const roundTripLink = new ApolloLink((operation, forward) => {
|
|||||||
const TrackExecutionTime = async (operationName, time) => {
|
const TrackExecutionTime = async (operationName, time) => {
|
||||||
const rdxStore = store.getState();
|
const rdxStore = store.getState();
|
||||||
try {
|
try {
|
||||||
console.log("trying");
|
|
||||||
axios.post("/ioevent", {
|
axios.post("/ioevent", {
|
||||||
operationName,
|
operationName,
|
||||||
time,
|
time,
|
||||||
dbevent: true,
|
dbevent: true,
|
||||||
user: rdxStore.user.currentUser.email,
|
user:
|
||||||
imexshopid: rdxStore.user.bodyshop.imexshopid,
|
rdxStore.user &&
|
||||||
|
rdxStore.user.currentUser &&
|
||||||
|
rdxStore.user.currentUser.email,
|
||||||
|
imexshopid:
|
||||||
|
rdxStore.user &&
|
||||||
|
rdxStore.user.bodyshop &&
|
||||||
|
rdxStore.user.bodyshop.imexshopid,
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log("IOEvent Error", error);
|
console.log("IOEvent Error", error);
|
||||||
|
|||||||
@@ -148,8 +148,6 @@ export async function RenderTemplates(
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
console.log("reportRequest", reportRequest);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const render = await jsreport.renderAsync(reportRequest);
|
const render = await jsreport.renderAsync(reportRequest);
|
||||||
if (!renderAsHtml) {
|
if (!renderAsHtml) {
|
||||||
|
|||||||
34
client/src/utils/fcm-handler.js
Normal file
34
client/src/utils/fcm-handler.js
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
export default async function FcmHandler({ client, payload }) {
|
||||||
|
console.log("Handling payload type", payload);
|
||||||
|
switch (payload.type) {
|
||||||
|
case "messaging-inbound":
|
||||||
|
client.cache.modify({
|
||||||
|
id: client.cache.identify({
|
||||||
|
__typename: "conversations",
|
||||||
|
id: payload.conversationid,
|
||||||
|
}),
|
||||||
|
fields: {
|
||||||
|
messages_aggregate(cached) {
|
||||||
|
return { aggregate: { count: cached.aggregate.count + 1 } };
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
case "messaging-mark-conversation-read":
|
||||||
|
client.cache.modify({
|
||||||
|
id: client.cache.identify({
|
||||||
|
__typename: "conversations",
|
||||||
|
id: payload.conversationid,
|
||||||
|
}),
|
||||||
|
fields: {
|
||||||
|
messages_aggregate(cached) {
|
||||||
|
return { aggregate: { count: 0 } };
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
console.log("No payload type set.");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1172,11 +1172,12 @@
|
|||||||
- active:
|
- active:
|
||||||
_eq: true
|
_eq: true
|
||||||
columns:
|
columns:
|
||||||
- id
|
|
||||||
- created_at
|
|
||||||
- updated_at
|
|
||||||
- bodyshopid
|
- bodyshopid
|
||||||
|
- created_at
|
||||||
|
- id
|
||||||
- phone_num
|
- phone_num
|
||||||
|
- unreadcnt
|
||||||
|
- updated_at
|
||||||
select_permissions:
|
select_permissions:
|
||||||
- role: user
|
- role: user
|
||||||
permission:
|
permission:
|
||||||
@@ -1186,6 +1187,7 @@
|
|||||||
- created_at
|
- created_at
|
||||||
- id
|
- id
|
||||||
- phone_num
|
- phone_num
|
||||||
|
- unreadcnt
|
||||||
- updated_at
|
- updated_at
|
||||||
filter:
|
filter:
|
||||||
bodyshop:
|
bodyshop:
|
||||||
@@ -1206,6 +1208,7 @@
|
|||||||
- created_at
|
- created_at
|
||||||
- id
|
- id
|
||||||
- phone_num
|
- phone_num
|
||||||
|
- unreadcnt
|
||||||
- updated_at
|
- updated_at
|
||||||
filter:
|
filter:
|
||||||
bodyshop:
|
bodyshop:
|
||||||
|
|||||||
@@ -0,0 +1,4 @@
|
|||||||
|
-- Could not auto-generate a down migration.
|
||||||
|
-- Please write an appropriate down migration for the SQL below:
|
||||||
|
-- alter table "public"."conversations" add column "unreadcnt" numeric
|
||||||
|
-- not null default '0';
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
alter table "public"."conversations" add column "unreadcnt" numeric
|
||||||
|
not null default '0';
|
||||||
@@ -66,7 +66,6 @@ app.get("/test", async function (req, res) {
|
|||||||
"git rev-parse --short HEAD"
|
"git rev-parse --short HEAD"
|
||||||
);
|
);
|
||||||
logger.log("test-api-status", "DEBUG", "api", { commit });
|
logger.log("test-api-status", "DEBUG", "api", { commit });
|
||||||
|
|
||||||
res.status(200).send(`OK - ${commit}`);
|
res.status(200).send(`OK - ${commit}`);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -114,6 +113,7 @@ app.post(
|
|||||||
twilio.webhook({ validate: process.env.NODE_ENV === "PRODUCTION" }),
|
twilio.webhook({ validate: process.env.NODE_ENV === "PRODUCTION" }),
|
||||||
smsStatus.status
|
smsStatus.status
|
||||||
);
|
);
|
||||||
|
app.post("/sms/markConversationRead", smsStatus.markConversationRead);
|
||||||
|
|
||||||
var job = require("./server/job/job");
|
var job = require("./server/job/job");
|
||||||
app.post("/job/totals", fb.validateFirebaseIdToken, job.totals);
|
app.post("/job/totals", fb.validateFirebaseIdToken, job.totals);
|
||||||
@@ -133,9 +133,10 @@ app.post("/render/inlinecss", fb.validateFirebaseIdToken, inlineCss.inlinecss);
|
|||||||
|
|
||||||
app.post(
|
app.post(
|
||||||
"/notifications/send",
|
"/notifications/send",
|
||||||
fb.validateFirebaseIdToken,
|
|
||||||
fb.sendNotification
|
fb.sendNotification
|
||||||
);
|
);
|
||||||
|
app.post("/notifications/subscribe", fb.validateFirebaseIdToken, fb.subscribe);
|
||||||
app.post("/adm/updateuser", fb.validateFirebaseIdToken, fb.updateUser);
|
app.post("/adm/updateuser", fb.validateFirebaseIdToken, fb.updateUser);
|
||||||
|
|
||||||
//Stripe Processing
|
//Stripe Processing
|
||||||
|
|||||||
@@ -74,31 +74,48 @@ exports.updateUser = (req, res) => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
exports.sendNotification = (req, res) => {
|
exports.sendNotification = async (req, res) => {
|
||||||
var registrationToken =
|
setTimeout(() => {
|
||||||
"fqIWg8ENDFyrRrMWJ1sItR:APA91bHirdZ05Zo66flMlvala97SMXoiQGwP4oCvMwd-vVrSauD_WoNim3kXHGqyP-bzENjkXwA5icyUAReFbeHn6dIaPcbpcsXuY73-eJAXvZiu1gIsrd1BOsnj3dEMT7Q4F6mTPth1";
|
// Send a message to the device corresponding to the provided
|
||||||
var message = {
|
// registration token.
|
||||||
notification: { title: "The Title", body: "The Body" },
|
admin
|
||||||
data: {
|
.messaging()
|
||||||
jobid: "1234",
|
.send({
|
||||||
},
|
topic: "PRD_PATRICK-messaging",
|
||||||
token: registrationToken,
|
notification: {
|
||||||
};
|
title: `ImEX Online Message - +16049992002"`,
|
||||||
|
body: "Test Noti.",
|
||||||
|
imageUrl: "https://thinkimex.com/img/logo512.png",
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
type: "messaging-inbound",
|
||||||
|
conversationid: "e0eb17c3-3a78-4e3f-b932-55ef35aa2297",
|
||||||
|
text: "Hello. ",
|
||||||
|
image_path: "",
|
||||||
|
phone_num: "+16049992002",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.then((response) => {
|
||||||
|
// Response is a message ID string.
|
||||||
|
console.log("Successfully sent message:", response);
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.log("Error sending message:", error);
|
||||||
|
});
|
||||||
|
|
||||||
// Send a message to the device corresponding to the provided
|
res.sendStatus(200);
|
||||||
// registration token.
|
}, 500);
|
||||||
admin
|
};
|
||||||
|
|
||||||
|
exports.subscribe = async (req, res) => {
|
||||||
|
const result = await admin
|
||||||
.messaging()
|
.messaging()
|
||||||
.send(message)
|
.subscribeToTopic(
|
||||||
.then((response) => {
|
req.body.fcm_tokens,
|
||||||
// Response is a message ID string.
|
`${req.body.imexshopid}-${req.body.type}`
|
||||||
console.log("Successfully sent message:", response);
|
);
|
||||||
})
|
|
||||||
.catch((error) => {
|
|
||||||
console.log("Error sending message:", error);
|
|
||||||
});
|
|
||||||
|
|
||||||
res.sendStatus(200);
|
res.json(result);
|
||||||
};
|
};
|
||||||
|
|
||||||
exports.validateFirebaseIdToken = async (req, res, next) => {
|
exports.validateFirebaseIdToken = async (req, res, next) => {
|
||||||
|
|||||||
@@ -22,48 +22,74 @@ mutation UNARCHIVE_CONVERSATION($id: uuid!) {
|
|||||||
|
|
||||||
exports.RECEIVE_MESSAGE = `
|
exports.RECEIVE_MESSAGE = `
|
||||||
mutation RECEIVE_MESSAGE($msg: [messages_insert_input!]!) {
|
mutation RECEIVE_MESSAGE($msg: [messages_insert_input!]!) {
|
||||||
|
insert_messages(objects: $msg) {
|
||||||
insert_messages(objects: $msg) {
|
|
||||||
returning {
|
returning {
|
||||||
conversation {
|
conversation {
|
||||||
id
|
id
|
||||||
archived
|
archived
|
||||||
bodyshop {
|
bodyshop{
|
||||||
associations(where: {active: {_eq: true}}) {
|
imexshopid
|
||||||
user {
|
|
||||||
fcmtokens
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
created_at
|
||||||
|
updated_at
|
||||||
|
unreadcnt
|
||||||
|
phone_num
|
||||||
}
|
}
|
||||||
|
conversationid
|
||||||
|
created_at
|
||||||
|
id
|
||||||
|
image_path
|
||||||
|
image
|
||||||
|
isoutbound
|
||||||
|
msid
|
||||||
|
read
|
||||||
|
text
|
||||||
|
updated_at
|
||||||
|
status
|
||||||
|
userid
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports.INSERT_MESSAGE = `
|
exports.INSERT_MESSAGE = `
|
||||||
mutation INSERT_MESSAGE($msg: [messages_insert_input!]!, $conversationid: uuid!) {
|
mutation INSERT_MESSAGE($msg: [messages_insert_input!]!, $conversationid: uuid!) {
|
||||||
update_conversations_by_pk(pk_columns: {id: $conversationid}, _set: {archived: false}) {
|
update_conversations_by_pk(pk_columns: {id: $conversationid}, _set: {archived: false}) {
|
||||||
id
|
id
|
||||||
|
archived
|
||||||
}
|
}
|
||||||
insert_messages(objects: $msg) {
|
insert_messages(objects: $msg) {
|
||||||
returning {
|
returning {
|
||||||
conversation {
|
conversation {
|
||||||
id
|
id
|
||||||
archived
|
archived
|
||||||
bodyshop {
|
bodyshop{
|
||||||
associations(where: {active: {_eq: true}}) {
|
imexshopid
|
||||||
user {
|
|
||||||
fcmtokens
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
created_at
|
||||||
|
updated_at
|
||||||
|
unreadcnt
|
||||||
|
phone_num
|
||||||
}
|
}
|
||||||
|
conversationid
|
||||||
|
created_at
|
||||||
|
id
|
||||||
|
image_path
|
||||||
|
image
|
||||||
|
isoutbound
|
||||||
|
msid
|
||||||
|
read
|
||||||
|
text
|
||||||
|
updated_at
|
||||||
|
status
|
||||||
|
userid
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports.UPDATE_MESSAGE_STATUS = `
|
exports.UPDATE_MESSAGE_STATUS = `
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ require("dotenv").config({
|
|||||||
const client = require("../graphql-client/graphql-client").client;
|
const client = require("../graphql-client/graphql-client").client;
|
||||||
const queries = require("../graphql-client/queries");
|
const queries = require("../graphql-client/queries");
|
||||||
const { phone } = require("phone");
|
const { phone } = require("phone");
|
||||||
const admin = require("../firebase/firebase-handler").admin;
|
const { admin } = require("../firebase/firebase-handler");
|
||||||
const logger = require("../utils/logger");
|
const logger = require("../utils/logger");
|
||||||
exports.receive = async (req, res) => {
|
exports.receive = async (req, res) => {
|
||||||
//Perform request validation
|
//Perform request validation
|
||||||
@@ -78,21 +78,43 @@ exports.receive = async (req, res) => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
|
let insertresp;
|
||||||
if (response.bodyshops[0].conversations[0]) {
|
if (response.bodyshops[0].conversations[0]) {
|
||||||
const r3 = await client.request(queries.INSERT_MESSAGE, {
|
insertresp = await client.request(queries.INSERT_MESSAGE, {
|
||||||
msg: newMessage,
|
msg: newMessage,
|
||||||
conversationid:
|
conversationid:
|
||||||
response.bodyshops[0].conversations[0] &&
|
response.bodyshops[0].conversations[0] &&
|
||||||
response.bodyshops[0].conversations[0].id,
|
response.bodyshops[0].conversations[0].id,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
const r2 = await client.request(queries.RECEIVE_MESSAGE, {
|
insertresp = await client.request(queries.RECEIVE_MESSAGE, {
|
||||||
msg: newMessage,
|
msg: newMessage,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
const message = insertresp.insert_messages.returning[0];
|
||||||
|
const data = {
|
||||||
|
type: "messaging-inbound",
|
||||||
|
conversationid: message.conversationid || "",
|
||||||
|
text: message.text || "",
|
||||||
|
image_path: message.image_path || "",
|
||||||
|
image: (message.image && message.image.toString()) || "",
|
||||||
|
messageid: message.id || "",
|
||||||
|
phone_num: message.conversation.phone_num || "",
|
||||||
|
};
|
||||||
|
|
||||||
|
const fcmresp = await admin.messaging().send({
|
||||||
|
topic: `${message.conversation.bodyshop.imexshopid}-messaging`,
|
||||||
|
notification: {
|
||||||
|
title: `ImEX Online Message - ${data.phone_num}`,
|
||||||
|
body: message.image_path ? `Image ${message.text}` : message.text,
|
||||||
|
imageUrl: "https://thinkimex.com/img/logo512.png",
|
||||||
|
},
|
||||||
|
data,
|
||||||
|
});
|
||||||
|
|
||||||
logger.log("sms-inbound-success", "DEBUG", "api", null, {
|
logger.log("sms-inbound-success", "DEBUG", "api", null, {
|
||||||
newMessage,
|
newMessage,
|
||||||
|
fcmresp,
|
||||||
});
|
});
|
||||||
res.status(200).send("");
|
res.status(200).send("");
|
||||||
} catch (e2) {
|
} catch (e2) {
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ const client = require("../graphql-client/graphql-client").client;
|
|||||||
const queries = require("../graphql-client/queries");
|
const queries = require("../graphql-client/queries");
|
||||||
const { phone } = require("phone");
|
const { phone } = require("phone");
|
||||||
const logger = require("../utils/logger");
|
const logger = require("../utils/logger");
|
||||||
|
const { admin } = require("../firebase/firebase-handler");
|
||||||
|
|
||||||
exports.status = (req, res) => {
|
exports.status = (req, res) => {
|
||||||
const { SmsSid, SmsStatus } = req.body;
|
const { SmsSid, SmsStatus } = req.body;
|
||||||
@@ -34,6 +35,23 @@ exports.status = (req, res) => {
|
|||||||
res.sendStatus(200);
|
res.sendStatus(200);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
exports.markConversationRead = async (req, res) => {
|
||||||
|
const { conversationid, imexshopid } = req.body;
|
||||||
|
admin.messaging().send({
|
||||||
|
topic: `${imexshopid}-messaging`,
|
||||||
|
// notification: {
|
||||||
|
// title: `ImEX Online Message - ${data.phone_num}`,
|
||||||
|
// body: message.image_path ? `Image ${message.text}` : message.text,
|
||||||
|
// imageUrl: "https://thinkimex.com/img/logo512.png",
|
||||||
|
// },
|
||||||
|
data: {
|
||||||
|
type: "messaging-mark-conversation-read",
|
||||||
|
conversationid: conversationid || "",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
res.send(200);
|
||||||
|
};
|
||||||
|
|
||||||
// Inbound Sample
|
// Inbound Sample
|
||||||
// {
|
// {
|
||||||
// "SmsSid": "SM5205ea340e06437799d9345e7283457c",
|
// "SmsSid": "SM5205ea340e06437799d9345e7283457c",
|
||||||
|
|||||||
@@ -5,13 +5,14 @@ const logger = new graylog2.graylog({
|
|||||||
});
|
});
|
||||||
|
|
||||||
function log(message, type, user, record, object) {
|
function log(message, type, user, record, object) {
|
||||||
console.log(message, {
|
if (type !== "ioevent")
|
||||||
type,
|
console.log(message, {
|
||||||
env: process.env.NODE_ENV || "development",
|
type,
|
||||||
user,
|
env: process.env.NODE_ENV || "development",
|
||||||
record,
|
user,
|
||||||
...object,
|
record,
|
||||||
});
|
...object,
|
||||||
|
});
|
||||||
logger.log(message, {
|
logger.log(message, {
|
||||||
type,
|
type,
|
||||||
env: process.env.NODE_ENV || "development",
|
env: process.env.NODE_ENV || "development",
|
||||||
|
|||||||
Reference in New Issue
Block a user