- Update the look and feel of the chat message list (prevents breaking when zoomed in and out like previous implementation)

Signed-off-by: Dave Richer <dave@imexsystems.ca>
This commit is contained in:
Dave Richer
2024-01-16 14:02:54 -05:00
parent 0d0f24802f
commit b6b445dc21
2 changed files with 101 additions and 104 deletions

View File

@@ -1,120 +1,116 @@
import {Badge, List, Space, Tag} from "antd"; import {Badge, Card, List, Space, Tag} from "antd";
import React from "react"; import React from "react";
import { connect } from "react-redux"; import {connect} from "react-redux";
import { import {AutoSizer, CellMeasurer, CellMeasurerCache, List as VirtualizedList,} from "react-virtualized";
AutoSizer, import {createStructuredSelector} from "reselect";
CellMeasurer, import {setSelectedConversation} from "../../redux/messaging/messaging.actions";
CellMeasurerCache, import {selectSelectedConversation} from "../../redux/messaging/messaging.selectors";
List as VirtualizedList, import {TimeAgoFormatter} from "../../utils/DateFormatter";
} from "react-virtualized";
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 PhoneFormatter from "../../utils/PhoneFormatter";
import OwnerNameDisplay from "../owner-name-display/owner-name-display.component"; import OwnerNameDisplay, {OwnerNameDisplayFunction} from "../owner-name-display/owner-name-display.component";
import _ from "lodash";
import "./chat-conversation-list.styles.scss"; import "./chat-conversation-list.styles.scss";
const mapStateToProps = createStructuredSelector({ const mapStateToProps = createStructuredSelector({
selectedConversation: selectSelectedConversation, selectedConversation: selectSelectedConversation,
}); });
const mapDispatchToProps = (dispatch) => ({ const mapDispatchToProps = (dispatch) => ({
setSelectedConversation: (conversationId) => setSelectedConversation: (conversationId) =>
dispatch(setSelectedConversation(conversationId)), dispatch(setSelectedConversation(conversationId)),
}); });
function ChatConversationListComponent({ function ChatConversationListComponent({
conversationList, conversationList,
selectedConversation, selectedConversation,
setSelectedConversation, setSelectedConversation,
loadMoreConversations, loadMoreConversations,
}) { }) {
const cache = new CellMeasurerCache({ const cache = new CellMeasurerCache({
fixedWidth: true, fixedWidth: true,
defaultHeight: 60, defaultHeight: 60,
}); });
const rowRenderer = ({index, key, style, parent}) => {
const item = conversationList[index];
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, idx) =>
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 || 0}/>
const cardStyle = index % 2 === 0 ? {backgroundColor: '#f0f2f5'} : {backgroundColor: '#ffffff'};
return (
<CellMeasurer
key={key}
cache={cache}
parent={parent}
columnIndex={0}
rowIndex={index}
>
<List.Item
onClick={() => setSelectedConversation(item.id)}
style={style}
className={`chat-list-item
${
item.id === selectedConversation
? "chat-list-selected-conversation"
: null
}`}
>
<Card style={cardStyle} bordered={false} 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>
</CellMeasurer>
);
};
const rowRenderer = ({ index, key, style, parent }) => {
const item = conversationList[index];
return ( return (
<CellMeasurer <div className="chat-list-container">
key={key} <AutoSizer>
cache={cache} {({height, width}) => (
parent={parent} <VirtualizedList
columnIndex={0} height={height}
rowIndex={index} width={width}
> rowCount={conversationList.length}
<List.Item rowHeight={cache.rowHeight}
onClick={() => setSelectedConversation(item.id)} rowRenderer={rowRenderer}
style={style} onScroll={({scrollTop, scrollHeight, clientHeight}) => {
className={`chat-list-item if (scrollTop + clientHeight === scrollHeight) {
${ loadMoreConversations();
item.id === selectedConversation }
? "chat-list-selected-conversation" }}
: null />
}`}
>
<Space style={{padding: '12px 24px'}} size={"large"}>
<Space>
{item.label && <Space>{item.label}</Space>}
{item.job_conversations.length > 0 ? (
<Space direction="vertical">
{item.job_conversations.map((j, idx) => (
<OwnerNameDisplay key={idx} ownerObject={j.job} />
))}
</Space>
) : (
<Space>
<PhoneFormatter>{item.phone_num}</PhoneFormatter>
</Space>
)} )}
</Space> </AutoSizer>
<Space direction="vertical"> </div>
<Space>
{item.job_conversations.length > 0
? item.job_conversations.map((j, idx) => (
<Tag key={idx}>{j.job.ro_number}</Tag>
))
: null}
</Space>
<Space>
<TimeAgoFormatter>{item.updated_at}</TimeAgoFormatter>
</Space>
</Space>
<Space>
<Badge count={item.messages_aggregate.aggregate.count || 0} />
</Space>
</Space>
</List.Item>
</CellMeasurer>
); );
};
return (
<div className="chat-list-container">
<AutoSizer>
{({ height, width }) => (
<VirtualizedList
height={height}
width={width}
rowCount={conversationList.length}
rowHeight={cache.rowHeight}
rowRenderer={rowRenderer}
onScroll={({ scrollTop, scrollHeight, clientHeight }) => {
if (scrollTop + clientHeight === scrollHeight) {
loadMoreConversations();
}
}}
/>
)}
</AutoSizer>
</div>
);
} }
export default connect( export default connect(
mapStateToProps, mapStateToProps,
mapDispatchToProps mapDispatchToProps
)(ChatConversationListComponent); )(ChatConversationListComponent);

View File

@@ -7,10 +7,11 @@
border: 1px solid gainsboro; border: 1px solid gainsboro;
} }
.chat-list-item { .chat-list-item {
.ant-card-head {
border: none;
}
&:hover { &:hover {
cursor: pointer; cursor: pointer;
color: #ff7a00; color: #ff7a00;
} }
}
border-bottom: 1px solid gainsboro;
}