added date badges (dividers) in messages view

This commit is contained in:
or-else
2022-02-10 14:19:51 -08:00
parent c8756c3a01
commit 84a78ce6b5
11 changed files with 272 additions and 139 deletions
+53 -31
View File
@@ -14,6 +14,7 @@ import Invitation from '../widgets/invitation.jsx';
import LetterTile from '../widgets/letter-tile.jsx';
import LoadSpinner from '../widgets/load-spinner.jsx';
import LogoView from './logo-view.jsx';
import MetaMessage from '../widgets/meta-message.jsx';
import SendMessage from '../widgets/send-message.jsx';
import { DEFAULT_P2P_ACCESS_MODE, IMAGE_PREVIEW_DIM, KEYPRESS_DELAY,
@@ -971,9 +972,11 @@ class MessagesView extends React.Component {
icon_badges.push({icon: 'dangerous', color: 'badge-inv'});
}
}
let messageNodes = [];
const messageNodes = [];
let previousFrom = null;
let chatBoxClass = null;
const dateFmt = new Intl.DateTimeFormat(this.props.intl.locale);
topic.messages((msg, prev, next, i) => {
let nextFrom = next ? (next.from || 'chan') : null;
@@ -1008,36 +1011,55 @@ class MessagesView extends React.Component {
replyToSeq = null;
}
messageNodes.push(
<ChatMessage
tinode={this.props.tinode}
content={msg.content}
deleted={msg.hi}
mimeType={msg.head ? msg.head.mime : null}
timestamp={msg.ts}
response={isReply}
seq={msg.seq}
isGroup={groupTopic}
isChan={this.state.channel}
userFrom={userFrom}
userName={userName}
userAvatar={userAvatar}
sequence={sequence}
received={deliveryStatus}
uploader={msg._uploader}
viewportWidth={this.props.viewportWidth} // Used by `formatter`.
showContextMenu={this.handleShowMessageContextMenu}
onImagePreview={this.handleImagePostview}
onFormResponse={this.handleFormResponse}
onError={this.props.onError}
onCancelUpload={this.handleCancelUpload}
pickReply={this.handlePickReply}
replyToSeq={replyToSeq}
onQuoteClick={this.handleQuoteClick}
ref={ref}
userIsWriter={this.state.isWriter}
key={msg.seq} />
);
// This message was sent on a different date than the previous.
if (!prev || new Date(prev.ts).toDateString() != new Date(msg.ts).toDateString()) {
messageNodes.push(
<MetaMessage
date={dateFmt.format(msg.ts)}
locale={this.props.intl.locale}
key={'date-' + msg.seq} />
);
}
if (msg.hi) {
// Deleted message.
messageNodes.push(
<MetaMessage
deleted={true}
key={msg.seq} />
);
} else {
messageNodes.push(
<ChatMessage
tinode={this.props.tinode}
content={msg.content}
deleted={msg.hi}
mimeType={msg.head ? msg.head.mime : null}
timestamp={msg.ts}
response={isReply}
seq={msg.seq}
isGroup={groupTopic}
isChan={this.state.channel}
userFrom={userFrom}
userName={userName}
userAvatar={userAvatar}
sequence={sequence}
received={deliveryStatus}
uploader={msg._uploader}
viewportWidth={this.props.viewportWidth} // Used by `formatter`.
showContextMenu={this.handleShowMessageContextMenu}
onImagePreview={this.handleImagePostview}
onFormResponse={this.handleFormResponse}
onError={this.props.onError}
onCancelUpload={this.handleCancelUpload}
pickReply={this.handlePickReply}
replyToSeq={replyToSeq}
onQuoteClick={this.handleQuoteClick}
ref={ref}
userIsWriter={this.state.isWriter}
key={msg.seq} />
);
}
});
let lastSeen = null;
+7 -14
View File
@@ -1,4 +1,5 @@
// Single message, sent or received.
// Single chat message bubble, sent or received.
import React from 'react';
import { FormattedMessage, injectIntl } from 'react-intl';
import { Drafty } from 'tinode-sdk';
@@ -110,11 +111,10 @@ class BaseChatMessage extends React.PureComponent {
}
render() {
const sideClass = this.props.deleted ? 'center' :
(this.props.sequence + ' ' + (this.props.response ? 'left' : 'right'));
const sideClass = this.props.sequence + ' ' + (this.props.response ? 'left' : 'right');
const bubbleClass = (this.props.sequence == 'single' || this.props.sequence == 'last') ? 'bubble tip' : 'bubble';
const avatar = this.props.deleted ? null : (this.props.userAvatar || true);
const fullDisplay = (this.props.isGroup && this.props.response && !this.props.deleted &&
const avatar = this.props.userAvatar || true;
const fullDisplay = (this.props.isGroup && this.props.response &&
(this.props.sequence == 'single' || this.props.sequence == 'last'));
let content = this.props.content;
@@ -140,12 +140,6 @@ class BaseChatMessage extends React.PureComponent {
}, this);
const tree = Drafty.format(content, fullFormatter, this.formatterContext);
content = React.createElement(React.Fragment, null, tree);
} else if (this.props.deleted) {
// Message represents a range of deleted messages.
content = <><i className="material-icons gray">block</i> <i className="gray">
<FormattedMessage id="deleted_content"
defaultMessage="content deleted" description="Shown when messages are deleted" />
</i></>
} else if (typeof content != 'string') {
content = <><i className="material-icons gray">warning_amber</i> <i className="gray">
<FormattedMessage id="invalid_content"
@@ -178,13 +172,12 @@ class BaseChatMessage extends React.PureComponent {
received={this.props.received} />
: null}
</div>
{this.props.deleted || !this.props.showContextMenu ?
null :
{this.props.showContextMenu ?
<span className="menuTrigger">
<a href="#" onClick={this.handleContextClick}>
<i className="material-icons">expand_more</i>
</a>
</span>
</span> : null
}
</div>
{fullDisplay ?
+41
View File
@@ -0,0 +1,41 @@
// Message bubble with non-payload content, such as an indicator of deleted content or a date stamp.
import React from 'react';
import { FormattedMessage } from 'react-intl';
export default class MetaMessage extends React.PureComponent {
constructor(props) {
super(props);
}
render() {
let content = null;
let bubbleClass = 'bubble';
if (this.props.deleted) {
// Message represents a range of deleted messages.
content = <><i className="material-icons gray">block</i> <i className="gray">
<FormattedMessage id="deleted_content"
defaultMessage="content deleted" description="Shown when messages are deleted" />
</i></>
bubbleClass += ' deleted';
} else if (this.props.date) {
// A date badge.
content = <>{this.props.date}</>
bubbleClass += ' date';
}
if (!content) {
return <>{null}</>
}
return (
<li className="meta">
<div className={bubbleClass}>
<div className="message-content">
{content}
</div>
</div>
</li>
);
}
};
+1 -2
View File
@@ -4,7 +4,6 @@ import { defineMessages, injectIntl } from 'react-intl';
import Tinode from 'tinode-sdk';
import { shortDateFormat } from '../lib/strformat.js';
import { deliveryMarker } from '../lib/utils.js';
const messages = defineMessages({
@@ -29,7 +28,7 @@ class ReceivedMarker extends React.PureComponent {
} else if (this.props.received == Tinode.MESSAGE_STATUS_FAILED) {
timestamp = formatMessage(messages.message_sending_failed);
} else {
timestamp = shortDateFormat(this.props.timestamp, this.props.intl.locale);
timestamp = this.props.timestamp.toLocaleTimeString(this.props.intl.locale, { timeStyle: 'short' });
}
const icon = deliveryMarker(this.props.received);