IO-1551 Refactor messaging.

This commit is contained in:
Patrick Fic
2021-12-08 12:38:26 -08:00
parent 26d22388c0
commit 1f7b53ee22
30 changed files with 614 additions and 302 deletions

View File

@@ -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);

View File

@@ -1,13 +1,16 @@
import { useSubscription } from "@apollo/client";
import React from "react";
import { useApolloClient } from "@apollo/client";
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 { 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 { selectBodyshop } from "../../redux/user/user.selectors";
import AlertComponent from "../alert/alert.component";
import LoadingSpinner from "../loading-spinner/loading-spinner.component";
import ChatAffixComponent from "./chat-affix.component";
import FcmHandler from "../../utils/fcm-handler";
import ChatPopupComponent from "../chat-popup/chat-popup.component";
import "./chat-affix.styles.scss";
const mapStateToProps = createStructuredSelector({
@@ -16,32 +19,85 @@ const mapStateToProps = createStructuredSelector({
});
export function ChatAffixContainer({ bodyshop, chatVisible }) {
const { loading, error, data } = useSubscription(
CONVERSATION_LIST_SUBSCRIPTION,
{
skip: !bodyshop || (bodyshop && !bodyshop.messagingservicesid),
}
);
const { t } = useTranslation();
const client = useApolloClient();
useEffect(() => {
if (!bodyshop || !bodyshop.messagingservicesid) return;
if (loading) return <LoadingSpinner />;
if (error) return <AlertComponent message={error.message} type="error" />;
async function SubscribeToTopic() {
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 <></>;
return (
<div className={`chat-affix ${chatVisible ? "chat-affix-open" : ""}`}>
{bodyshop && bodyshop.messagingservicesid ? (
<ChatAffixComponent
conversationList={(data && data.conversations) || []}
unreadCount={
(data &&
data.conversations.reduce((acc, val) => {
return (acc = acc + val.messages_aggregate.aggregate.count);
}, 0)) ||
0
}
/>
) : null}
{bodyshop && bodyshop.messagingservicesid ? <ChatPopupComponent /> : null}
</div>
);
}

View File

@@ -1,14 +1,12 @@
import { Badge, List, Tag, Tooltip } from "antd";
import { AlertFilled } from "@ant-design/icons";
import { Badge, List, Tag } from "antd";
import React from "react";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { setSelectedConversation } from "../../redux/messaging/messaging.actions";
import { selectSelectedConversation } from "../../redux/messaging/messaging.selectors";
import { TimeAgoFormatter } from "../../utils/DateFormatter";
import PhoneFormatter from "../../utils/PhoneFormatter";
import "./chat-conversation-list.styles.scss";
import { useTranslation } from "react-i18next";
import { TimeAgoFormatter } from "../../utils/DateFormatter";
const mapStateToProps = createStructuredSelector({
selectedConversation: selectSelectedConversation,
@@ -24,8 +22,6 @@ export function ChatConversationListComponent({
selectedConversation,
setSelectedConversation,
}) {
const { t } = useTranslation();
return (
<div className="chat-list-container">
<List
@@ -33,6 +29,7 @@ export function ChatConversationListComponent({
dataSource={conversationList}
renderItem={(item) => (
<List.Item
key={item.id}
onClick={() => setSelectedConversation(item.id)}
className={`chat-list-item ${
item.id === selectedConversation
@@ -43,19 +40,9 @@ export function ChatConversationListComponent({
{item.job_conversations.length > 0 ? (
<div className="chat-name">
{item.job_conversations.map((j, idx) => (
<div key={idx} style={{ display: "flex" }}>
{j.job.owner && !j.job.owner.allow_text_message && (
<Tooltip title={t("messaging.labels.noallowtxt")}>
<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 key={idx}>{`${j.job.ownr_fn || ""} ${
j.job.ownr_ln || ""
} ${j.job.ownr_co_nm || ""} `}</div>
))}
</div>
) : (

View File

@@ -9,6 +9,7 @@ import "./chat-conversation.styles.scss";
export default function ChatConversationComponent({
subState,
conversation,
messages,
handleMarkConversationAsRead,
}) {
const [loading, error] = subState;
@@ -16,8 +17,6 @@ export default function ChatConversationComponent({
if (loading) return <LoadingSkeleton />;
if (error) return <AlertComponent message={error.message} type="error" />;
const messages = (conversation && conversation.messages) || [];
return (
<div
className="chat-conversation"

View File

@@ -1,18 +1,32 @@
import { useMutation, useSubscription } from "@apollo/client";
import { useMutation, useQuery, useSubscription } from "@apollo/client";
import React, { useState } from "react";
import { connect } from "react-redux";
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 { selectSelectedConversation } from "../../redux/messaging/messaging.selectors";
import ChatConversationComponent from "./chat-conversation.component";
import axios from "axios";
import { selectBodyshop } from "../../redux/user/user.selectors";
const mapStateToProps = createStructuredSelector({
selectedConversation: selectSelectedConversation,
bodyshop: selectBodyshop,
});
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(
CONVERSATION_SUBSCRIPTION_BY_PK,
{
@@ -26,30 +40,46 @@ export function ChatConversationContainer({ selectedConversation }) {
MARK_MESSAGES_AS_READ_BY_CONVERSATION,
{
variables: { conversationId: selectedConversation },
update(cache) {
cache.modify({
id: cache.identify({
__typename: "conversations",
id: selectedConversation,
}),
fields: {
messages_aggregate(cached) {
return { aggregate: { count: 0 } };
},
},
});
},
}
);
const unreadCount =
(data &&
data.conversations_by_pk &&
data.conversations_by_pk &&
data.conversations_by_pk.messages_aggregate &&
data.conversations_by_pk.messages_aggregate.aggregate &&
data.conversations_by_pk.messages_aggregate.aggregate.count) ||
0;
data &&
data.messages &&
data.messages.reduce((acc, val) => {
return !val.read && !val.isoutbound ? acc + 1 : acc;
}, 0);
const handleMarkConversationAsRead = async () => {
if (unreadCount > 0 && !!selectedConversation && !markingAsReadInProgress) {
setMarkingAsReadInProgress(true);
await markConversationRead();
await markConversationRead({});
await axios.post("/sms/markConversationRead", {
conversationid: selectedConversation,
imexshopid: bodyshop.imexshopid,
});
setMarkingAsReadInProgress(false);
}
};
return (
<ChatConversationComponent
subState={[loading, error]}
conversation={data ? data.conversations_by_pk : {}}
subState={[loading || convoLoading, error || convoError]}
conversation={convoData ? convoData.conversations_by_pk : {}}
messages={data ? data.messages : []}
handleMarkConversationAsRead={handleMarkConversationAsRead}
/>
);

View File

@@ -46,7 +46,7 @@ export function ChatNewConversation({ openChatByPhone }) {
return (
<Popover trigger="click" content={popContent}>
<PlusCircleFilled style={{ margin: "1rem" }} />
<PlusCircleFilled />
</Popover>
);
}

View File

@@ -1,54 +1,108 @@
import { ShrinkOutlined, InfoCircleOutlined } from "@ant-design/icons";
import { Col, Row, Tooltip, Typography } from "antd";
import React from "react";
import {
ShrinkOutlined,
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 { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { toggleChatVisible } from "../../redux/messaging/messaging.actions";
import ChatConversationListComponent from "../chat-conversation-list/chat-conversation-list.component";
import ChatConversationContainer from "../chat-conversation/chat-conversation.container";
import { selectSelectedConversation } from "../../redux/messaging/messaging.selectors";
import {
selectChatVisible,
selectSelectedConversation,
} from "../../redux/messaging/messaging.selectors";
import "./chat-popup.styles.scss";
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({
selectedConversation: selectSelectedConversation,
chatVisible: selectChatVisible,
});
const mapDispatchToProps = (dispatch) => ({
toggleChatVisible: () => dispatch(toggleChatVisible()),
});
export function ChatPopupComponent({
conversationList,
chatVisible,
selectedConversation,
toggleChatVisible,
}) {
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">
<Col span={8}>
<ChatConversationListComponent conversationList={conversationList} />
</Col>
<Col span={16}>
{selectedConversation ? <ChatConversationContainer /> : null}
</Col>
</Row>
</div>
const { loading, data, refetch, called } = useQuery(
CONVERSATION_LIST_QUERY,
{}
);
useEffect(() => {
if (called && chatVisible) refetch();
}, [chatVisible, called, refetch]);
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);

View File

@@ -2,8 +2,7 @@ import { getAnalytics, logEvent } from "firebase/analytics";
import { initializeApp } from "firebase/app";
import { getAuth, updatePassword, updateProfile } from "firebase/auth";
import { getFirestore } from "firebase/firestore";
import { tracker } from "../App/App.container";
//import { getMessaging } from "firebase/messaging";
import { getMessaging, getToken, onMessage } from "firebase/messaging";
import { store } from "../redux/store";
const config = JSON.parse(process.env.REACT_APP_FIREBASE_CONFIG);
@@ -40,17 +39,34 @@ export const updateCurrentPassword = async (password) => {
return updatePassword(currentUser, password);
};
//let messaging;
// 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 const messaging = getMessaging();
// 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) => {
const state = stateProp || store.getState();
@@ -70,9 +86,6 @@ export const logImEXEvent = (eventName, additionalParams, stateProp = null) => {
// eventParams
// );
logEvent(analytics, eventName, eventParams);
//Log event to OpenReplay server.
tracker.event(eventName, eventParams);
};
// if (messaging) {

View File

@@ -1,15 +1,54 @@
import { gql } from "@apollo/client";
export const CONVERSATION_LIST_SUBSCRIPTION = gql`
subscription CONVERSATION_LIST_SUBSCRIPTION {
// export const CONVERSATION_LIST_SUBSCRIPTION = gql`
// 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(
order_by: { updated_at: desc }
limit: 100
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
@@ -17,17 +56,6 @@ export const CONVERSATION_LIST_SUBSCRIPTION = gql`
ownr_fn
ownr_ln
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`
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) {
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
phone_num
archived

View File

@@ -8,6 +8,8 @@ export const MARK_MESSAGES_AS_READ_BY_CONVERSATION = gql`
) {
returning {
id
read
isoutbound
}
}
}

View File

@@ -5,13 +5,12 @@ import preval from "preval.macro";
import React, { lazy, Suspense, useEffect } from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import ErrorBoundary from "../../components/error-boundary/error-boundary.component";
import { Link, Route, Switch } from "react-router-dom";
import { createStructuredSelector } from "reselect";
import BreadCrumbs from "../../components/breadcrumbs/breadcrumbs.component";
import ChatAffixContainer from "../../components/chat-affix/chat-affix.container";
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";
//Component Imports
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 ShopSubStatusComponent from "../../components/shop-sub-status/shop-sub-status.component";
import TestComponent from "../../components/_test/test.component";
import { QUERY_STRIPE_ID } from "../../graphql/bodyshop.queries";
import { requestForToken } from "../../firebase/firebase.utils";
import {
selectBodyshop,
selectInstanceConflict,
} from "../../redux/user/user.selectors";
import client from "../../utils/GraphQLClient";
import "./manage.page.styles.scss";
const ManageRootPage = lazy(() =>
@@ -166,16 +164,15 @@ const Dms = lazy(() => import("../dms/dms.container"));
const { Content, Footer } = Layout;
const stripePromise = new Promise((resolve, reject) => {
client.query({ query: QUERY_STRIPE_ID }).then((resp) => {
if (resp.data.bodyshops[0])
resolve(
loadStripe(process.env.REACT_APP_STRIPE_PUBLIC_KEY, {
stripeAccount:
resp.data.bodyshops[0].stripe_acct_id || "No Stripe Id Resolve",
})
);
reject();
});
// client.query({ query: QUERY_STRIPE_ID }).then((resp) => {
// if (resp.data.bodyshops[0])
resolve(
loadStripe(process.env.REACT_APP_STRIPE_PUBLIC_KEY, {
stripeAccount: "No Stripe Id Resolve",
})
);
// reject();
// });
});
const mapStateToProps = createStructuredSelector({
@@ -188,7 +185,9 @@ export function Manage({ match, conflict, bodyshop }) {
useEffect(() => {
const widgetId = "IABVNO4scRKY11XBQkNr";
window.noticeable.render("widget", widgetId);
requestForToken();
}, []);
useEffect(() => {
document.title = t("titles.app");
}, [t]);
@@ -395,7 +394,6 @@ export function Manage({ match, conflict, bodyshop }) {
<HeaderContainer />
<Content className="content-container">
<FcmNotification />
<PartnerPingComponent />
<ErrorBoundary>{PageContent}</ErrorBoundary>
<BackTop />

View File

@@ -5,7 +5,7 @@ import { connect } from "react-redux";
import { Redirect, Route, Switch } from "react-router-dom";
import { createStructuredSelector } from "reselect";
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 TechHeader from "../../components/tech-header/tech-header.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`} />}
<TechHeader />
<Content className="tech-content-container">
<FcmNotification />
<ErrorBoundary>
<Suspense
fallback={

View File

@@ -899,10 +899,12 @@
"selectall": "Select All",
"senderrortosupport": "Send Error to Support",
"submit": "Submit",
"tryagain": "Try Again",
"view": "View",
"viewreleasenotes": "See What's Changed"
},
"errors": {
"fcm": "You must allow notification permissions to have real time messaging. Click to try again.",
"notfound": "No record was found."
},
"itemtypes": {
@@ -925,6 +927,7 @@
"exceptiontitle": "An error has occurred.",
"friday": "Friday",
"globalsearch": "Global Search",
"help": "Help",
"hours": "hrs",
"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.",

View File

@@ -899,10 +899,12 @@
"selectall": "",
"senderrortosupport": "",
"submit": "",
"tryagain": "",
"view": "",
"viewreleasenotes": ""
},
"errors": {
"fcm": "",
"notfound": ""
},
"itemtypes": {
@@ -925,6 +927,7 @@
"exceptiontitle": "",
"friday": "",
"globalsearch": "",
"help": "",
"hours": "",
"in": "en",
"instanceconflictext": "",

View File

@@ -899,10 +899,12 @@
"selectall": "",
"senderrortosupport": "",
"submit": "",
"tryagain": "",
"view": "",
"viewreleasenotes": ""
},
"errors": {
"fcm": "",
"notfound": ""
},
"itemtypes": {
@@ -925,6 +927,7 @@
"exceptiontitle": "",
"friday": "",
"globalsearch": "",
"help": "",
"hours": "",
"in": "dans",
"instanceconflictext": "",

View File

@@ -91,13 +91,11 @@ export default function CiecaSelect(parts = true, labor = true) {
}
export function GetPartTypeName(part_type) {
console.log(part_type);
if (!part_type) return null;
return i18n.t(`joblines.fields.part_types.${part_type.toUpperCase()}`);
}
export function Get(part_type) {
console.log(part_type);
if (!part_type) return null;
return i18n.t(`joblines.fields.part_types.${part_type.toUpperCase()}`);
}

View File

@@ -50,13 +50,18 @@ const roundTripLink = new ApolloLink((operation, forward) => {
const TrackExecutionTime = async (operationName, time) => {
const rdxStore = store.getState();
try {
console.log("trying");
axios.post("/ioevent", {
operationName,
time,
dbevent: true,
user: rdxStore.user.currentUser.email,
imexshopid: rdxStore.user.bodyshop.imexshopid,
user:
rdxStore.user &&
rdxStore.user.currentUser &&
rdxStore.user.currentUser.email,
imexshopid:
rdxStore.user &&
rdxStore.user.bodyshop &&
rdxStore.user.bodyshop.imexshopid,
});
} catch (error) {
console.log("IOEvent Error", error);

View File

@@ -148,8 +148,6 @@ export async function RenderTemplates(
},
};
console.log("reportRequest", reportRequest);
try {
const render = await jsreport.renderAsync(reportRequest);
if (!renderAsHtml) {

View 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;
}
}