142 lines
5.0 KiB
JavaScript
142 lines
5.0 KiB
JavaScript
import { Badge, Card, List, Space, Tag, Tooltip } from "antd";
|
|
import { useEffect, useMemo, useState } from "react";
|
|
import { connect } from "react-redux";
|
|
import { Virtuoso } from "react-virtuoso";
|
|
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 { OwnerNameDisplayFunction } from "../owner-name-display/owner-name-display.component";
|
|
import _ from "lodash";
|
|
import { ExclamationCircleOutlined } from "@ant-design/icons";
|
|
import "./chat-conversation-list.styles.scss";
|
|
import { useQuery } from "@apollo/client";
|
|
import { GET_PHONE_NUMBER_OPT_OUTS } from "../../graphql/phone-number-opt-out.queries.js";
|
|
import { phone } from "phone";
|
|
import { useTranslation } from "react-i18next";
|
|
import { selectBodyshop } from "../../redux/user/user.selectors";
|
|
|
|
const mapStateToProps = createStructuredSelector({
|
|
selectedConversation: selectSelectedConversation,
|
|
bodyshop: selectBodyshop
|
|
});
|
|
|
|
const mapDispatchToProps = (dispatch) => ({
|
|
setSelectedConversation: (conversationId) => dispatch(setSelectedConversation(conversationId))
|
|
});
|
|
|
|
function ChatConversationListComponent({ conversationList, selectedConversation, setSelectedConversation, bodyshop }) {
|
|
const { t } = useTranslation();
|
|
const [, forceUpdate] = useState(false);
|
|
const phoneNumbers = conversationList.map((item) => phone(item.phone_num, "CA").phoneNumber.replace(/^\+1/, ""));
|
|
const { data: optOutData } = useQuery(GET_PHONE_NUMBER_OPT_OUTS, {
|
|
variables: {
|
|
bodyshopid: bodyshop.id,
|
|
phone_numbers: phoneNumbers
|
|
},
|
|
skip: !conversationList.length,
|
|
fetchPolicy: "cache-and-network"
|
|
});
|
|
|
|
const optOutMap = useMemo(() => {
|
|
const map = new Map();
|
|
optOutData?.phone_number_opt_out?.forEach((optOut) => {
|
|
map.set(optOut.phone_number, true);
|
|
});
|
|
return map;
|
|
}, [optOutData?.phone_number_opt_out]);
|
|
|
|
useEffect(() => {
|
|
const interval = setInterval(() => {
|
|
forceUpdate((prev) => !prev);
|
|
}, 60000);
|
|
return () => clearInterval(interval);
|
|
}, []);
|
|
|
|
const sortedConversationList = useMemo(() => {
|
|
return _.orderBy(conversationList, ["updated_at"], ["desc"]);
|
|
}, [conversationList]);
|
|
|
|
const renderConversation = (index, t) => {
|
|
const item = sortedConversationList[index];
|
|
const normalizedPhone = phone(item.phone_num, "CA").phoneNumber.replace(/^\+1/, "");
|
|
const hasOptOutEntry = optOutMap.has(normalizedPhone);
|
|
const cardContentRight = <TimeAgoFormatter>{item.updated_at}</TimeAgoFormatter>;
|
|
const cardContentLeft =
|
|
item.job_conversations.length > 0
|
|
? item.job_conversations.map((j, idx) => <Tag key={idx}>{j.job.ro_number}</Tag>)
|
|
: null;
|
|
const names = <>{_.uniq(item.job_conversations.map((j) => OwnerNameDisplayFunction(j.job)))}</>;
|
|
const cardTitle = (
|
|
<>
|
|
{item.label && <Tag color="blue">{item.label}</Tag>}
|
|
{item.job_conversations.length > 0 ? (
|
|
<Space direction="vertical">{names}</Space>
|
|
) : (
|
|
<Space>
|
|
<PhoneFormatter>{item.phone_num}</PhoneFormatter>
|
|
</Space>
|
|
)}
|
|
</>
|
|
);
|
|
const cardExtra = (
|
|
<>
|
|
<Badge count={item.messages_aggregate.aggregate.count} />
|
|
{hasOptOutEntry && (
|
|
<Tooltip title={t("consent.text_body")}>
|
|
<Tag color="red" icon={<ExclamationCircleOutlined />}>
|
|
{t("messaging.labels.no_consent")}
|
|
</Tag>
|
|
</Tooltip>
|
|
)}
|
|
</>
|
|
);
|
|
const getCardStyle = () =>
|
|
item.id === selectedConversation
|
|
? { backgroundColor: "var(--card-selected-bg)" }
|
|
: { backgroundColor: index % 2 === 0 ? "var(--card-stripe-even-bg)" : "var(--card-stripe-odd-bg)" };
|
|
|
|
return (
|
|
<List.Item
|
|
key={item.id}
|
|
onClick={() => setSelectedConversation(item.id)}
|
|
className={`chat-list-item ${item.id === selectedConversation ? "chat-list-selected-conversation" : ""}`}
|
|
>
|
|
<Card style={getCardStyle()} variant={true} size="small" extra={cardExtra} title={cardTitle}>
|
|
<div
|
|
style={{
|
|
display: "inline-block",
|
|
width: "70%",
|
|
textAlign: "left"
|
|
}}
|
|
>
|
|
{cardContentLeft}
|
|
</div>
|
|
<div
|
|
style={{
|
|
display: "inline-block",
|
|
width: "30%",
|
|
textAlign: "right"
|
|
}}
|
|
>
|
|
{cardContentRight}
|
|
</div>
|
|
</Card>
|
|
</List.Item>
|
|
);
|
|
};
|
|
|
|
return (
|
|
<div className="chat-list-container">
|
|
<Virtuoso
|
|
data={sortedConversationList}
|
|
itemContent={(index) => renderConversation(index, t)}
|
|
style={{ height: "100%", width: "100%" }}
|
|
/>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
export default connect(mapStateToProps, mapDispatchToProps)(ChatConversationListComponent);
|