BOD-118 Added Saga to start conversations from anywhere. Removed gql apollo-boost due to package size and replaced with graphql-tag

This commit is contained in:
Patrick Fic
2020-05-04 15:11:14 -07:00
parent 782b7fe7f3
commit b7d438a0f0
38 changed files with 222 additions and 136 deletions

View File

@@ -23,6 +23,7 @@
"i18next-browser-languagedetector": "^4.1.1",
"logrocket": "^1.0.7",
"node-sass": "^4.13.1",
"phone": "^2.4.10",
"query-string": "^6.12.1",
"react": "^16.13.1",
"react-apollo": "^3.1.5",

View File

@@ -18,100 +18,101 @@ import App from "./App";
if (process.env.NODE_ENV === "production") LogRocket.init("gvfvfw/bodyshopapp");
const httpLink = new HttpLink({
uri: process.env.REACT_APP_GRAPHQL_ENDPOINT,
});
const wsLink = new WebSocketLink({
uri: process.env.REACT_APP_GRAPHQL_ENDPOINT_WS,
options: {
lazy: true,
reconnect: true,
connectionParams: async () => {
//const token = localStorage.getItem("token");
const token = await auth.currentUser.getIdToken(true);
if (token) {
return {
headers: {
authorization: token ? `Bearer ${token}` : "",
},
};
}
},
},
});
const subscriptionMiddleware = {
applyMiddleware: async (options, next) => {
options.authToken = await auth.currentUser.getIdToken(true);
next();
},
};
wsLink.subscriptionClient.use([subscriptionMiddleware]);
const link = split(
// split based on operation type
({ query }) => {
const definition = getMainDefinition(query);
// console.log(
// "##Intercepted GQL Transaction : " +
// definition.operation +
// "|" +
// definition.name.value +
// "##",
// query
// );
return (
definition.kind === "OperationDefinition" &&
definition.operation === "subscription"
);
},
wsLink,
httpLink
);
const authLink = setContext((_, { headers }) => {
return auth.currentUser.getIdToken().then((token) => {
if (token) {
return {
headers: {
...headers,
authorization: token ? `Bearer ${token}` : "",
},
};
} else {
return { headers };
}
});
});
const retryLink = new RetryLink({
delay: {
initial: 300,
max: 5,
jitter: true,
},
attempts: {
max: 5,
retryIf: (error, _operation) => !!error,
},
});
const middlewares = [];
if (process.env.NODE_ENV === "development") {
middlewares.push(apolloLogger);
}
middlewares.push(retryLink.concat(errorLink.concat(authLink.concat(link))));
const cache = new InMemoryCache();
export const client = new ApolloClient({
link: ApolloLink.from(middlewares),
cache,
connectToDevTools: true,
});
export default class AppContainer extends Component {
constructor() {
super();
const httpLink = new HttpLink({
uri: process.env.REACT_APP_GRAPHQL_ENDPOINT,
});
const wsLink = new WebSocketLink({
uri: process.env.REACT_APP_GRAPHQL_ENDPOINT_WS,
options: {
lazy: true,
reconnect: true,
connectionParams: async () => {
//const token = localStorage.getItem("token");
const token = await auth.currentUser.getIdToken(true);
if (token) {
return {
headers: {
authorization: token ? `Bearer ${token}` : "",
},
};
}
},
},
});
const subscriptionMiddleware = {
applyMiddleware: async (options, next) => {
options.authToken = await auth.currentUser.getIdToken(true);
next();
},
};
wsLink.subscriptionClient.use([subscriptionMiddleware]);
const link = split(
// split based on operation type
({ query }) => {
const definition = getMainDefinition(query);
// console.log(
// "##Intercepted GQL Transaction : " +
// definition.operation +
// "|" +
// definition.name.value +
// "##",
// query
// );
return (
definition.kind === "OperationDefinition" &&
definition.operation === "subscription"
);
},
wsLink,
httpLink
);
const authLink = setContext((_, { headers }) => {
return auth.currentUser.getIdToken().then((token) => {
if (token) {
return {
headers: {
...headers,
authorization: token ? `Bearer ${token}` : "",
},
};
} else {
return { headers };
}
});
});
const retryLink = new RetryLink({
delay: {
initial: 300,
max: 5,
jitter: true,
},
attempts: {
max: 5,
retryIf: (error, _operation) => !!error,
},
});
const middlewares = [];
if (process.env.NODE_ENV === "development") {
middlewares.push(apolloLogger);
}
middlewares.push(retryLink.concat(errorLink.concat(authLink.concat(link))));
const cache = new InMemoryCache();
const client = new ApolloClient({
link: ApolloLink.from(middlewares),
cache,
connectToDevTools: true,
});
this.state = { client };
}

View File

@@ -5,11 +5,11 @@ import { openChatByPhone } from "../../redux/messaging/messaging.actions";
const mapDispatchToProps = (dispatch) => ({
openChatByPhone: (phone) => dispatch(openChatByPhone(phone)),
});
export function ChatOpenButton({ phone, openChatByPhone }) {
export function ChatOpenButton({ phone, jobid, openChatByPhone }) {
return (
<MessageFilled
style={{ margin: 4 }}
onClick={() => openChatByPhone(phone)}
onClick={() => openChatByPhone({ phone_num: phone, jobid: jobid })}
/>
);
}

View File

@@ -1,6 +1,6 @@
import { useApolloClient, useMutation, useQuery } from "@apollo/react-hooks";
import { notification } from "antd";
import { gql } from "apollo-boost";
import gql from "graphql-tag";
import React, { useState } from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
@@ -137,11 +137,12 @@ export function JobsAvailableSupplementContainer({
setSelectedJob(null);
};
if (error) return <AlertComponent type='error' message={error.message} />;
if (error) return <AlertComponent type="error" message={error.message} />;
return (
<LoadingSpinner
loading={insertLoading}
message={t("jobs.labels.creating_new_job")}>
message={t("jobs.labels.creating_new_job")}
>
<JobsAvailableSupplementComponent
loading={loading}
data={data}

View File

@@ -1,5 +1,5 @@
import { GET_JOB_LINES_BY_PK } from "../../graphql/jobs-lines.queries";
import { gql } from "apollo-boost";
import gql from "graphql-tag";
export const GetSupplementDelta = async (client, jobId, newLines) => {
console.log("-----Begin Supplement-----");
console.log("Supplement delta for jobId", jobId);

View File

@@ -76,7 +76,7 @@ export default withRouter(function JobsList({
return record.ownr_ph1 ? (
<span>
<PhoneFormatter>{record.ownr_ph1}</PhoneFormatter>
<StartChatButton phone={record.ownr_ph1} />
<StartChatButton phone={record.ownr_ph1} jobid={record.id} />
</span>
) : null;
},

View File

@@ -1,4 +1,4 @@
import { gql } from "apollo-boost";
import gql from "graphql-tag";
export const shopAttributes = gql`
fragment shopData on query_root {

View File

@@ -1,4 +1,4 @@
import { gql } from "apollo-boost";
import gql from "graphql-tag";
import { shopAttributes } from "../../components/fragments.queries";
export const EMAIL_APPOINTMENT_CONFIRMATION = gql`

View File

@@ -1,4 +1,4 @@
import { gql } from "apollo-boost";
import gql from "graphql-tag";
import { shopAttributes } from "../../components/fragments.queries";
export const REPORT_QUERY_PARTS_ORDER_BY_PK = gql`

View File

@@ -1,4 +1,4 @@
import { gql } from "apollo-boost";
import gql from "graphql-tag";
export const INSERT_ALLOCATION = gql`
mutation INSERT_ALLOCATION($alloc: [allocations_insert_input!]!) {

View File

@@ -1,4 +1,4 @@
import { gql } from "apollo-boost";
import gql from "graphql-tag";
export const QUERY_ALL_ACTIVE_APPOINTMENTS = gql`
query QUERY_ALL_ACTIVE_APPOINTMENTS {

View File

@@ -1,4 +1,4 @@
import { gql } from "apollo-boost";
import gql from "graphql-tag";
export const QUERY_ALL_ASSOCIATIONS = gql`
query QUERY_ALL_ASSOCIATIONS {

View File

@@ -1,4 +1,4 @@
import { gql } from "apollo-boost";
import gql from "graphql-tag";
export const QUERY_AUDIT_TRAIL = gql`
query QUERY_AUDIT_TRAIL($id: uuid!) {

View File

@@ -1,4 +1,4 @@
import { gql } from "apollo-boost";
import gql from "graphql-tag";
export const QUERY_AVAILABLE_NEW_JOBS = gql`
query QUERY_AVAILABLE_NEW_JOBS {

View File

@@ -1,4 +1,4 @@
import { gql } from "apollo-boost";
import gql from "graphql-tag";
export const QUERY_BODYSHOP = gql`
query QUERY_BODYSHOP {

View File

@@ -1,4 +1,4 @@
import { gql } from "apollo-boost";
import gql from "graphql-tag";
export const INSERT_NEW_CONTRACT = gql`
mutation INSERT_NEW_CONTRACT($contract: [cccontracts_insert_input!]!) {

View File

@@ -1,4 +1,4 @@
import { gql } from "apollo-boost";
import gql from "graphql-tag";
export const CONVERSATION_LIST_SUBSCRIPTION = gql`
subscription CONVERSATION_LIST_SUBSCRIPTION {
@@ -45,3 +45,21 @@ export const CONVERSATION_SUBSCRIPTION_BY_PK = gql`
}
}
`;
export const CONVERSATION_ID_BY_PHONE = gql`
query CONVERSATION_ID_BY_PHONE($phone: String!) {
conversations(where: { phone_num: { _eq: $phone } }) {
id
}
}
`;
export const CREATE_CONVERSATION = gql`
mutation CREATE_CONVERSATION($conversation: [conversations_insert_input!]!) {
insert_conversations(objects: $conversation) {
returning {
id
}
}
}
`;

View File

@@ -1,4 +1,4 @@
import { gql } from "apollo-boost";
import gql from "graphql-tag";
export const INSERT_NEW_COURTESY_CAR = gql`
mutation INSERT_NEW_COURTESY_CAR(

View File

@@ -1,4 +1,4 @@
import { gql } from "apollo-boost";
import gql from "graphql-tag";
export const GET_DOCUMENTS_BY_JOB = gql`
query GET_DOCUMENTS_BY_JOB($jobId: uuid!) {

View File

@@ -1,4 +1,4 @@
import { gql } from "apollo-boost";
import gql from "graphql-tag";
export const QUERY_EMPLOYEES = gql`
query QUERY_EMPLOYEES {

View File

@@ -1,4 +1,4 @@
import { gql } from "apollo-boost";
import gql from "graphql-tag";
export const INSERT_NEW_INVOICE = gql`
mutation INSERT_NEW_INVOICE($invoice: [invoices_insert_input!]!) {

View File

@@ -1,9 +1,10 @@
import { gql } from "apollo-boost";
import gql from "graphql-tag";
export const INSERT_CONVERSATION_TAG = gql`
mutation INSERT_CONVERSATION_TAG($conversationId: uuid!, $jobId: uuid!) {
insert_job_conversations(
objects: { conversationid: $conversationId, jobid: $jobId }
on_conflict: { constraint: job_conversations_pkey, update_columns: jobid }
) {
returning {
jobid

View File

@@ -1,4 +1,4 @@
import { gql } from "apollo-boost";
import gql from "graphql-tag";
export const GET_JOB_LINES_BY_PK = gql`
query GET_JOB_LINES_BY_PK($id: uuid!) {

View File

@@ -1,4 +1,4 @@
import { gql } from "apollo-boost";
import gql from "graphql-tag";
export const QUERY_ALL_ACTIVE_JOBS = gql`
query QUERY_ALL_ACTIVE_JOBS($statuses: [String!]!) {

View File

@@ -1,4 +1,4 @@
import { gql } from "apollo-boost";
import gql from "graphql-tag";
export const MARK_MESSAGES_AS_READ_BY_CONVERSATION = gql`
mutation MARK_MESSAGES_AS_READ_BY_CONVERSATION($conversationId: uuid) {

View File

@@ -1,4 +1,4 @@
import { gql } from "apollo-boost";
import gql from "graphql-tag";
export const GET_LANDING_NAV_ITEMS = gql`
query nav_items {

View File

@@ -1,4 +1,4 @@
import { gql } from "apollo-boost";
import gql from "graphql-tag";
export const INSERT_NEW_NOTE = gql`
mutation INSERT_NEW_NOTE($noteInput: [notes_insert_input!]!) {

View File

@@ -1,4 +1,4 @@
import { gql } from "apollo-boost";
import gql from "graphql-tag";
export const QUERY_SEARCH_OWNER_BY_IDX = gql`
query QUERY_SEARCH_OWNER_BY_IDX($search: String!) {

View File

@@ -1,4 +1,4 @@
import { gql } from "apollo-boost";
import gql from "graphql-tag";
export const INSERT_NEW_PARTS_ORDERS = gql`
mutation INSERT_NEW_PARTS_ORDERS($po: [parts_orders_insert_input!]!) {

View File

@@ -1,4 +1,4 @@
import { gql } from "apollo-boost";
import gql from "graphql-tag";
export const QUERY_TEMPLATES_BY_NAME = gql`
query QUERY_TEMPLATES_BY_NAME($name: String!) {

View File

@@ -1,4 +1,4 @@
import { gql } from "apollo-boost";
import gql from "graphql-tag";
export const QUERY_TICKETS_BY_JOBID = gql`
query QUERY_TICKETS_BY_JOBID($jobid: uuid!) {

View File

@@ -1,4 +1,4 @@
import { gql } from "apollo-boost";
import gql from "graphql-tag";
export const UPSERT_USER = gql`
mutation UPSERT_USER($authEmail: String!, $authToken: String!) {

View File

@@ -1,4 +1,4 @@
import { gql } from "apollo-boost";
import gql from "graphql-tag";
export const QUERY_VEHICLE_BY_ID = gql`
query QUERY_VEHICLE_BY_ID($id: uuid!) {

View File

@@ -1,4 +1,4 @@
import { gql } from "apollo-boost";
import gql from "graphql-tag";
export const QUERY_VENDOR_BY_ID = gql`
query QUERY_VENDOR_BY_ID($id: uuid!) {

View File

@@ -15,7 +15,11 @@ const messagingReducer = (state = INITIAL_STATE, action) => {
visible: !state.visible,
};
case MessagingActionTypes.SET_SELECTED_CONVERSATION:
return { ...state, selectedConversationId: action.payload };
return {
...state,
visible: true,
selectedConversationId: action.payload,
};
case MessagingActionTypes.SEND_MESSAGE:
return {
...state,

View File

@@ -1,16 +1,69 @@
import { all, call, put, takeLatest } from "redux-saga/effects";
import { sendMessageFailure, sendMessageSuccess } from "./messaging.actions";
import MessagingActionTypes from "./messaging.types";
import axios from "axios";
import { sendEmailFailure } from "../email/email.actions";
import { withApollo } from "react-apollo";
import phone from "phone";
import { all, call, put, select, takeLatest } from "redux-saga/effects";
import { client } from "../../App/App.container";
import {
CONVERSATION_ID_BY_PHONE,
CREATE_CONVERSATION,
} from "../../graphql/conversations.queries";
import { INSERT_CONVERSATION_TAG } from "../../graphql/job-conversations.queries";
import {
sendMessageFailure,
sendMessageSuccess,
setSelectedConversation,
} from "./messaging.actions";
import MessagingActionTypes from "./messaging.types";
import { selectBodyshop } from "../user/user.selectors";
export function* onOpenChatByPhone() {
yield takeLatest(MessagingActionTypes.OPEN_CHAT_BY_PHONE, openChatByPhone);
}
export function* openChatByPhone({ payload: phone, client }) {
console.log("Payload: Phone, Client", phone, client);
export function* openChatByPhone({ payload }) {
const { phone_num, jobid } = payload;
const bodyshop = yield select(selectBodyshop);
try {
const {
data: { conversations },
} = yield client.query({
query: CONVERSATION_ID_BY_PHONE,
variables: { phone: phone(phone_num)[0] },
});
if (conversations.length === 0) {
const {
data: {
insert_conversations: { returning: newConversationsId },
},
} = yield client.mutate({
mutation: CREATE_CONVERSATION,
variables: {
conversation: [
{
phone_num: phone(phone_num)[0],
bodyshopid: bodyshop.id,
job_conversations: jobid ? { data: { jobid: jobid } } : null,
},
],
},
});
yield put(setSelectedConversation(newConversationsId[0].id));
} else if (conversations.length === 1) {
//got the ID. Open it.
yield put(setSelectedConversation(conversations[0].id));
yield client.mutate({
mutation: INSERT_CONVERSATION_TAG,
variables: {
conversationId: conversations[0].id,
jobId: jobid,
},
});
} else {
console.log("ERROR: Multiple conversations found. "); //TODO Graceful handling of this situation
}
} catch (error) {
console.log("Error in sendMessage saga.", error);
}
}
export function* onSendMessage() {
yield takeLatest(MessagingActionTypes.SEND_MESSAGE, sendMessage);
}
@@ -20,7 +73,7 @@ export function* sendMessage({ payload }) {
if (response.status === 200) {
yield put(sendMessageSuccess(payload));
} else {
yield put(sendEmailFailure(response.data));
yield put(sendMessageFailure(response.data));
}
} catch (error) {
console.log("Error in sendMessage saga.", error);

View File

@@ -1,4 +1,4 @@
import { gql } from "apollo-boost";
import gql from "graphql-tag";
import { QUERY_TEMPLATES_BY_NAME } from "../graphql/templates.queries";
import axios from "axios";

View File

@@ -9468,6 +9468,11 @@ performance-now@^2.1.0:
resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b"
integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=
phone@^2.4.10:
version "2.4.10"
resolved "https://registry.yarnpkg.com/phone/-/phone-2.4.10.tgz#6e5335e070e90bc5004350eae96118470df323af"
integrity sha512-lVTu1rGahm6RYgRZyDXv3jBOvKCY5SHhLHsod2j1OtMms6ELooFoOE5K8fQMUREXlFfRWrpFDryGQ2AsB9y5cA==
picomatch@^2.0.4, picomatch@^2.0.7:
version "2.2.1"
resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.2.1.tgz#21bac888b6ed8601f831ce7816e335bc779f0a4a"
@@ -11885,6 +11890,8 @@ rxjs@^6.5.3:
version "6.5.4"
resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.5.4.tgz#e0777fe0d184cec7872df147f303572d414e211c"
integrity sha512-naMQXcgEo3csAEGvw/NydRA0fuS2nDZJiw1YUWFKU7aPPAPGZEsD4Iimit96qwCieH6y614MCLYwdkrWx7z/7Q==
dependencies:
tslib "^1.9.0"
safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1:
version "5.1.2"