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
+19 -8
View File
@@ -1557,12 +1557,6 @@ img.channel {
justify-content: flex-end;
}
.chat-box .center {
justify-content: center;
margin-top: 0.5rem;
margin-bottom: 0.5rem;
}
.bubble {
position: relative;
display: flex;
@@ -1572,6 +1566,7 @@ img.channel {
padding: 0.75rem 0.5rem 0.75rem 0.5rem;
margin: 0;
text-align: left;
color: #212121;
border-radius: 0.5rem;
box-shadow: 0 1px 0.5px rgba(0, 0, 0, 0.12);
transition: ease-out background-color 0.5s;
@@ -1624,11 +1619,28 @@ img.channel {
border-bottom-right-radius: 0;
}
.center .bubble {
.chat-box .meta {
justify-content: center;
margin-top: 0.5rem;
margin-bottom: 0.5rem;
}
.chat-box .meta .bubble {
color: #666;
min-width: unset;
}
.meta .bubble.deleted {
background-color: #e3f2fd;
padding: 0.75rem 0.75rem 0.5rem 0.75rem;
}
.meta .bubble.date {
font-size: smaller;
padding: 0.5rem 0.5rem 0.5rem 0.5rem;
background-color: rgb(244 244 244 / 66%);
}
/* Left triangle placed bottom left flush. */
.left .bubble.tip::before {
content: '';
@@ -1691,7 +1703,6 @@ img.channel {
.bubble div.message-content {
position: relative;
color: #212121;
margin: 0;
width: 100%;
max-width: 36rem;
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -70,7 +70,7 @@
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-intl": "^5.20.2",
"tinode-sdk": "^0.18.0-rc3"
"tinode-sdk": "^0.18.1-beta1"
},
"devDependencies": {
"@babel/core": "^7.14.5",
+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);
+146 -79
View File
@@ -3296,11 +3296,12 @@ __webpack_require__.r(__webpack_exports__);
/* harmony import */ var _widgets_letter_tile_jsx__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! ../widgets/letter-tile.jsx */ "./src/widgets/letter-tile.jsx");
/* harmony import */ var _widgets_load_spinner_jsx__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(/*! ../widgets/load-spinner.jsx */ "./src/widgets/load-spinner.jsx");
/* harmony import */ var _logo_view_jsx__WEBPACK_IMPORTED_MODULE_12__ = __webpack_require__(/*! ./logo-view.jsx */ "./src/views/logo-view.jsx");
/* harmony import */ var _widgets_send_message_jsx__WEBPACK_IMPORTED_MODULE_13__ = __webpack_require__(/*! ../widgets/send-message.jsx */ "./src/widgets/send-message.jsx");
/* harmony import */ var _config_js__WEBPACK_IMPORTED_MODULE_14__ = __webpack_require__(/*! ../config.js */ "./src/config.js");
/* harmony import */ var _lib_blob_helpers_js__WEBPACK_IMPORTED_MODULE_15__ = __webpack_require__(/*! ../lib/blob-helpers.js */ "./src/lib/blob-helpers.js");
/* harmony import */ var _lib_navigation_js__WEBPACK_IMPORTED_MODULE_16__ = __webpack_require__(/*! ../lib/navigation.js */ "./src/lib/navigation.js");
/* harmony import */ var _lib_strformat_js__WEBPACK_IMPORTED_MODULE_17__ = __webpack_require__(/*! ../lib/strformat.js */ "./src/lib/strformat.js");
/* harmony import */ var _widgets_meta_message_jsx__WEBPACK_IMPORTED_MODULE_13__ = __webpack_require__(/*! ../widgets/meta-message.jsx */ "./src/widgets/meta-message.jsx");
/* harmony import */ var _widgets_send_message_jsx__WEBPACK_IMPORTED_MODULE_14__ = __webpack_require__(/*! ../widgets/send-message.jsx */ "./src/widgets/send-message.jsx");
/* harmony import */ var _config_js__WEBPACK_IMPORTED_MODULE_15__ = __webpack_require__(/*! ../config.js */ "./src/config.js");
/* harmony import */ var _lib_blob_helpers_js__WEBPACK_IMPORTED_MODULE_16__ = __webpack_require__(/*! ../lib/blob-helpers.js */ "./src/lib/blob-helpers.js");
/* harmony import */ var _lib_navigation_js__WEBPACK_IMPORTED_MODULE_17__ = __webpack_require__(/*! ../lib/navigation.js */ "./src/lib/navigation.js");
/* harmony import */ var _lib_strformat_js__WEBPACK_IMPORTED_MODULE_18__ = __webpack_require__(/*! ../lib/strformat.js */ "./src/lib/strformat.js");
@@ -3320,6 +3321,7 @@ const Drafty = (tinode_sdk__WEBPACK_IMPORTED_MODULE_2___default().Drafty);
const NOTIFICATION_EXEC_INTERVAL = 300;
const SHOW_GO_TO_LAST_DIST = 100;
const messages = (0,react_intl__WEBPACK_IMPORTED_MODULE_1__.defineMessages)({
@@ -3563,7 +3565,7 @@ class MessagesView extends (react__WEBPACK_IMPORTED_MODULE_0___default().Compone
if (topic.public) {
Object.assign(nextState, {
title: topic.public.fn,
avatar: (0,_lib_blob_helpers_js__WEBPACK_IMPORTED_MODULE_15__.makeImageUrl)(topic.public.photo)
avatar: (0,_lib_blob_helpers_js__WEBPACK_IMPORTED_MODULE_16__.makeImageUrl)(topic.public.photo)
});
} else {
Object.assign(nextState, {
@@ -3656,7 +3658,7 @@ class MessagesView extends (react__WEBPACK_IMPORTED_MODULE_0___default().Compone
let getQuery = topic.startMetaQuery().withLaterDesc().withLaterSub();
if (this.state.isReader || newTopic) {
getQuery = getQuery.withLaterData(_config_js__WEBPACK_IMPORTED_MODULE_14__.MESSAGES_PAGE);
getQuery = getQuery.withLaterData(_config_js__WEBPACK_IMPORTED_MODULE_15__.MESSAGES_PAGE);
if (this.state.isReader) {
getQuery = getQuery.withLaterDel();
@@ -3670,7 +3672,7 @@ class MessagesView extends (react__WEBPACK_IMPORTED_MODULE_0___default().Compone
const setQuery = newTopic ? this.props.newTopicParams : undefined;
topic.subscribe(getQuery.build(), setQuery).then(ctrl => {
if (ctrl.code == 303) {
_lib_navigation_js__WEBPACK_IMPORTED_MODULE_16__["default"].navigateTo(_lib_navigation_js__WEBPACK_IMPORTED_MODULE_16__["default"].setUrlTopic('', ctrl.params.topic));
_lib_navigation_js__WEBPACK_IMPORTED_MODULE_17__["default"].navigateTo(_lib_navigation_js__WEBPACK_IMPORTED_MODULE_17__["default"].setUrlTopic('', ctrl.params.topic));
return;
}
@@ -3743,7 +3745,7 @@ class MessagesView extends (react__WEBPACK_IMPORTED_MODULE_0___default().Compone
this.setState({
fetchingMessages: true
}, () => {
topic.getMessagesPage(_config_js__WEBPACK_IMPORTED_MODULE_14__.MESSAGES_PAGE).catch(err => this.props.onError(err.message, 'err')).finally(() => this.setState({
topic.getMessagesPage(_config_js__WEBPACK_IMPORTED_MODULE_15__.MESSAGES_PAGE).catch(err => this.props.onError(err.message, 'err')).finally(() => this.setState({
fetchingMessages: false
}));
});
@@ -3765,7 +3767,7 @@ class MessagesView extends (react__WEBPACK_IMPORTED_MODULE_0___default().Compone
if (desc.public) {
this.setState({
title: desc.public.fn,
avatar: (0,_lib_blob_helpers_js__WEBPACK_IMPORTED_MODULE_15__.makeImageUrl)(desc.public.photo)
avatar: (0,_lib_blob_helpers_js__WEBPACK_IMPORTED_MODULE_16__.makeImageUrl)(desc.public.photo)
});
} else {
this.setState({
@@ -3831,7 +3833,7 @@ class MessagesView extends (react__WEBPACK_IMPORTED_MODULE_0___default().Compone
this.readNotificationQueue.push({
topicName: this.state.topic,
seq: seq,
sendAt: now.setMilliseconds(now.getMilliseconds() + _config_js__WEBPACK_IMPORTED_MODULE_14__.READ_DELAY)
sendAt: now.setMilliseconds(now.getMilliseconds() + _config_js__WEBPACK_IMPORTED_MODULE_15__.READ_DELAY)
});
}
@@ -3928,7 +3930,7 @@ class MessagesView extends (react__WEBPACK_IMPORTED_MODULE_0___default().Compone
this.setState({
typingIndicator: false
});
}, _config_js__WEBPACK_IMPORTED_MODULE_14__.KEYPRESS_DELAY + 1000);
}, _config_js__WEBPACK_IMPORTED_MODULE_15__.KEYPRESS_DELAY + 1000);
if (!this.state.typingIndicator) {
this.setState({
@@ -4035,7 +4037,7 @@ class MessagesView extends (react__WEBPACK_IMPORTED_MODULE_0___default().Compone
handleEnablePeer(e) {
e.preventDefault();
this.props.onChangePermissions(this.state.topic, _config_js__WEBPACK_IMPORTED_MODULE_14__.DEFAULT_P2P_ACCESS_MODE, this.state.topic);
this.props.onChangePermissions(this.state.topic, _config_js__WEBPACK_IMPORTED_MODULE_15__.DEFAULT_P2P_ACCESS_MODE, this.state.topic);
}
sendKeyPress() {
@@ -4074,7 +4076,7 @@ class MessagesView extends (react__WEBPACK_IMPORTED_MODULE_0___default().Compone
}
sendFileAttachment(file) {
const maxInbandAttachmentSize = this.props.tinode.getServerLimit('maxMessageSize', _config_js__WEBPACK_IMPORTED_MODULE_14__.MAX_INBAND_ATTACHMENT_SIZE) * 0.75 - 1024 | 0;
const maxInbandAttachmentSize = this.props.tinode.getServerLimit('maxMessageSize', _config_js__WEBPACK_IMPORTED_MODULE_15__.MAX_INBAND_ATTACHMENT_SIZE) * 0.75 - 1024 | 0;
if (file.size > maxInbandAttachmentSize) {
const uploader = this.props.tinode.getLargeFileHelper();
@@ -4087,7 +4089,7 @@ class MessagesView extends (react__WEBPACK_IMPORTED_MODULE_0___default().Compone
});
this.sendMessage(msg, uploadCompletionPromise, uploader);
} else {
(0,_lib_blob_helpers_js__WEBPACK_IMPORTED_MODULE_15__.fileToBase64)(file).then(b64 => this.sendMessage(Drafty.attachFile(null, {
(0,_lib_blob_helpers_js__WEBPACK_IMPORTED_MODULE_16__.fileToBase64)(file).then(b64 => this.sendMessage(Drafty.attachFile(null, {
mime: b64.mime,
data: b64.bits,
filename: b64.name
@@ -4096,12 +4098,12 @@ class MessagesView extends (react__WEBPACK_IMPORTED_MODULE_0___default().Compone
}
handleAttachFile(file) {
const maxExternAttachmentSize = this.props.tinode.getServerLimit('maxFileUploadSize', _config_js__WEBPACK_IMPORTED_MODULE_14__.MAX_EXTERN_ATTACHMENT_SIZE);
const maxExternAttachmentSize = this.props.tinode.getServerLimit('maxFileUploadSize', _config_js__WEBPACK_IMPORTED_MODULE_15__.MAX_EXTERN_ATTACHMENT_SIZE);
if (file.size > maxExternAttachmentSize) {
this.props.onError(this.props.intl.formatMessage(messages.file_attachment_too_large, {
size: (0,_lib_strformat_js__WEBPACK_IMPORTED_MODULE_17__.bytesToHumanSize)(file.size),
limit: (0,_lib_strformat_js__WEBPACK_IMPORTED_MODULE_17__.bytesToHumanSize)(maxExternAttachmentSize)
size: (0,_lib_strformat_js__WEBPACK_IMPORTED_MODULE_18__.bytesToHumanSize)(file.size),
limit: (0,_lib_strformat_js__WEBPACK_IMPORTED_MODULE_18__.bytesToHumanSize)(maxExternAttachmentSize)
}), 'err');
} else {
this.setState({
@@ -4120,7 +4122,7 @@ class MessagesView extends (react__WEBPACK_IMPORTED_MODULE_0___default().Compone
const width = this.state.imagePreview.width;
const height = this.state.imagePreview.height;
const fname = this.state.imagePreview.name;
const maxInbandAttachmentSize = this.props.tinode.getServerLimit('maxMessageSize', _config_js__WEBPACK_IMPORTED_MODULE_14__.MAX_INBAND_ATTACHMENT_SIZE) * 0.75 - 1024 | 0;
const maxInbandAttachmentSize = this.props.tinode.getServerLimit('maxMessageSize', _config_js__WEBPACK_IMPORTED_MODULE_15__.MAX_INBAND_ATTACHMENT_SIZE) * 0.75 - 1024 | 0;
if (blob.size > maxInbandAttachmentSize) {
const uploader = this.props.tinode.getLargeFileHelper();
@@ -4131,7 +4133,7 @@ class MessagesView extends (react__WEBPACK_IMPORTED_MODULE_0___default().Compone
}
const uploadCompletionPromise = uploader.upload(blob);
(0,_lib_blob_helpers_js__WEBPACK_IMPORTED_MODULE_15__.imageScaled)(blob, _config_js__WEBPACK_IMPORTED_MODULE_14__.IMAGE_PREVIEW_DIM, _config_js__WEBPACK_IMPORTED_MODULE_14__.IMAGE_PREVIEW_DIM, -1, false).then(scaled => (0,_lib_blob_helpers_js__WEBPACK_IMPORTED_MODULE_15__.blobToBase64)(scaled.blob)).then(b64 => {
(0,_lib_blob_helpers_js__WEBPACK_IMPORTED_MODULE_16__.imageScaled)(blob, _config_js__WEBPACK_IMPORTED_MODULE_15__.IMAGE_PREVIEW_DIM, _config_js__WEBPACK_IMPORTED_MODULE_15__.IMAGE_PREVIEW_DIM, -1, false).then(scaled => (0,_lib_blob_helpers_js__WEBPACK_IMPORTED_MODULE_16__.blobToBase64)(scaled.blob)).then(b64 => {
let msg = Drafty.insertImage(null, 0, {
mime: mime,
_tempPreview: b64.bits,
@@ -4154,7 +4156,7 @@ class MessagesView extends (react__WEBPACK_IMPORTED_MODULE_0___default().Compone
return;
}
(0,_lib_blob_helpers_js__WEBPACK_IMPORTED_MODULE_15__.blobToBase64)(blob).then(b64 => {
(0,_lib_blob_helpers_js__WEBPACK_IMPORTED_MODULE_16__.blobToBase64)(blob).then(b64 => {
let msg = Drafty.insertImage(null, 0, {
mime: b64.mime,
preview: b64.bits,
@@ -4174,8 +4176,8 @@ class MessagesView extends (react__WEBPACK_IMPORTED_MODULE_0___default().Compone
}
handleAttachImage(file) {
const maxExternAttachmentSize = this.props.tinode.getServerLimit('maxFileUploadSize', _config_js__WEBPACK_IMPORTED_MODULE_14__.MAX_EXTERN_ATTACHMENT_SIZE);
(0,_lib_blob_helpers_js__WEBPACK_IMPORTED_MODULE_15__.imageScaled)(file, _config_js__WEBPACK_IMPORTED_MODULE_14__.MAX_IMAGE_DIM, _config_js__WEBPACK_IMPORTED_MODULE_14__.MAX_IMAGE_DIM, maxExternAttachmentSize, false).then(scaled => {
const maxExternAttachmentSize = this.props.tinode.getServerLimit('maxFileUploadSize', _config_js__WEBPACK_IMPORTED_MODULE_15__.MAX_EXTERN_ATTACHMENT_SIZE);
(0,_lib_blob_helpers_js__WEBPACK_IMPORTED_MODULE_16__.imageScaled)(file, _config_js__WEBPACK_IMPORTED_MODULE_15__.MAX_IMAGE_DIM, _config_js__WEBPACK_IMPORTED_MODULE_15__.MAX_IMAGE_DIM, maxExternAttachmentSize, false).then(scaled => {
this.setState({
imagePreview: {
url: URL.createObjectURL(scaled.blob),
@@ -4215,7 +4217,7 @@ class MessagesView extends (react__WEBPACK_IMPORTED_MODULE_0___default().Compone
content = typeof content == 'string' ? Drafty.init(content) : content;
if (Drafty.isValid(content)) {
content = Drafty.replyContent(content, _config_js__WEBPACK_IMPORTED_MODULE_14__.QUOTED_REPLY_LENGTH);
content = Drafty.replyContent(content, _config_js__WEBPACK_IMPORTED_MODULE_15__.QUOTED_REPLY_LENGTH);
} else {
content = Drafty.append(Drafty.init('\u26A0 '), Drafty.wrapInto(this.props.intl.formatMessage(messages.invalid_content), 'EM'));
}
@@ -4321,9 +4323,10 @@ class MessagesView extends (react__WEBPACK_IMPORTED_MODULE_0___default().Compone
}
}
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;
let sequence = 'single';
@@ -4349,7 +4352,7 @@ class MessagesView extends (react__WEBPACK_IMPORTED_MODULE_0___default().Compone
if (user && user.public) {
userName = user.public.fn;
userAvatar = (0,_lib_blob_helpers_js__WEBPACK_IMPORTED_MODULE_15__.makeImageUrl)(user.public.photo);
userAvatar = (0,_lib_blob_helpers_js__WEBPACK_IMPORTED_MODULE_16__.makeImageUrl)(user.public.photo);
}
chatBoxClass = groupTopic ? 'chat-box group' : 'chat-box';
@@ -4360,35 +4363,50 @@ class MessagesView extends (react__WEBPACK_IMPORTED_MODULE_0___default().Compone
replyToSeq = null;
}
messageNodes.push(react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_widgets_chat_message_jsx__WEBPACK_IMPORTED_MODULE_3__["default"], {
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,
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
}));
if (!prev || new Date(prev.ts).toDateString() != new Date(msg.ts).toDateString()) {
messageNodes.push(react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_widgets_meta_message_jsx__WEBPACK_IMPORTED_MODULE_13__["default"], {
date: dateFmt.format(msg.ts),
locale: this.props.intl.locale,
key: 'date-' + msg.seq
}));
}
if (msg.hi) {
messageNodes.push(react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_widgets_meta_message_jsx__WEBPACK_IMPORTED_MODULE_13__["default"], {
deleted: true,
key: msg.seq
}));
} else {
messageNodes.push(react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_widgets_chat_message_jsx__WEBPACK_IMPORTED_MODULE_3__["default"], {
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,
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;
@@ -4401,7 +4419,7 @@ class MessagesView extends (react__WEBPACK_IMPORTED_MODULE_0___default().Compone
if (cont.online) {
lastSeen = formatMessage(messages.online_now);
} else if (cont.seen) {
lastSeen = formatMessage(messages.last_seen) + ": " + (0,_lib_strformat_js__WEBPACK_IMPORTED_MODULE_17__.shortDateFormat)(cont.seen.when, this.props.intl.locale);
lastSeen = formatMessage(messages.last_seen) + ": " + (0,_lib_strformat_js__WEBPACK_IMPORTED_MODULE_18__.shortDateFormat)(cont.seen.when, this.props.intl.locale);
}
}
}
@@ -4504,7 +4522,7 @@ class MessagesView extends (react__WEBPACK_IMPORTED_MODULE_0___default().Compone
}]
})), ".") : null, this.state.unconfirmed ? react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_widgets_invitation_jsx__WEBPACK_IMPORTED_MODULE_9__["default"], {
onAction: this.handleNewChatAcceptance
}) : react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_widgets_send_message_jsx__WEBPACK_IMPORTED_MODULE_13__["default"], {
}) : react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_widgets_send_message_jsx__WEBPACK_IMPORTED_MODULE_14__["default"], {
tinode: this.props.tinode,
noInput: !!this.props.forwardMessage,
disabled: !this.state.isWriter,
@@ -7952,10 +7970,10 @@ class BaseChatMessage extends (react__WEBPACK_IMPORTED_MODULE_0___default().Pure
}
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 && (this.props.sequence == 'single' || this.props.sequence == 'last');
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;
const attachments = [];
@@ -7980,18 +7998,6 @@ class BaseChatMessage extends (react__WEBPACK_IMPORTED_MODULE_0___default().Pure
}, this);
const tree = tinode_sdk__WEBPACK_IMPORTED_MODULE_2__.Drafty.format(content, _lib_formatters_js__WEBPACK_IMPORTED_MODULE_6__.fullFormatter, this.formatterContext);
content = react__WEBPACK_IMPORTED_MODULE_0___default().createElement((react__WEBPACK_IMPORTED_MODULE_0___default().Fragment), null, tree);
} else if (this.props.deleted) {
content = react__WEBPACK_IMPORTED_MODULE_0___default().createElement((react__WEBPACK_IMPORTED_MODULE_0___default().Fragment), null, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("i", {
className: "material-icons gray"
}, "block"), " ", react__WEBPACK_IMPORTED_MODULE_0___default().createElement("i", {
className: "gray"
}, react__WEBPACK_IMPORTED_MODULE_0___default().createElement(react_intl__WEBPACK_IMPORTED_MODULE_1__.FormattedMessage, {
id: "deleted_content",
defaultMessage: [{
"type": 0,
"value": "content deleted"
}]
})));
} else if (typeof content != 'string') {
content = react__WEBPACK_IMPORTED_MODULE_0___default().createElement((react__WEBPACK_IMPORTED_MODULE_0___default().Fragment), null, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("i", {
className: "material-icons gray"
@@ -8025,14 +8031,14 @@ class BaseChatMessage extends (react__WEBPACK_IMPORTED_MODULE_0___default().Pure
}, content, attachments), this.props.timestamp ? react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_received_marker_jsx__WEBPACK_IMPORTED_MODULE_5__["default"], {
timestamp: this.props.timestamp,
received: this.props.received
}) : null), this.props.deleted || !this.props.showContextMenu ? null : react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", {
}) : null), this.props.showContextMenu ? react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", {
className: "menuTrigger"
}, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("a", {
href: "#",
onClick: this.handleContextClick
}, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("i", {
className: "material-icons"
}, "expand_more")))), fullDisplay ? react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
}, "expand_more"))) : null), fullDisplay ? react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
className: "author"
}, this.props.userName || react__WEBPACK_IMPORTED_MODULE_0___default().createElement("i", null, react__WEBPACK_IMPORTED_MODULE_0___default().createElement(react_intl__WEBPACK_IMPORTED_MODULE_1__.FormattedMessage, {
id: "user_not_found",
@@ -10941,6 +10947,67 @@ class MenuStart extends (react__WEBPACK_IMPORTED_MODULE_0___default().PureCompon
/***/ }),
/***/ "./src/widgets/meta-message.jsx":
/*!**************************************!*\
!*** ./src/widgets/meta-message.jsx ***!
\**************************************/
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
__webpack_require__.r(__webpack_exports__);
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ "default": () => (/* binding */ MetaMessage)
/* harmony export */ });
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! react */ "react");
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
/* harmony import */ var react_intl__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! react-intl */ "react-intl");
/* harmony import */ var react_intl__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(react_intl__WEBPACK_IMPORTED_MODULE_1__);
class MetaMessage extends (react__WEBPACK_IMPORTED_MODULE_0___default().PureComponent) {
constructor(props) {
super(props);
}
render() {
let content = null;
let bubbleClass = 'bubble';
if (this.props.deleted) {
content = react__WEBPACK_IMPORTED_MODULE_0___default().createElement((react__WEBPACK_IMPORTED_MODULE_0___default().Fragment), null, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("i", {
className: "material-icons gray"
}, "block"), " ", react__WEBPACK_IMPORTED_MODULE_0___default().createElement("i", {
className: "gray"
}, react__WEBPACK_IMPORTED_MODULE_0___default().createElement(react_intl__WEBPACK_IMPORTED_MODULE_1__.FormattedMessage, {
id: "deleted_content",
defaultMessage: [{
"type": 0,
"value": "content deleted"
}]
})));
bubbleClass += ' deleted';
} else if (this.props.date) {
content = react__WEBPACK_IMPORTED_MODULE_0___default().createElement((react__WEBPACK_IMPORTED_MODULE_0___default().Fragment), null, this.props.date);
bubbleClass += ' date';
}
if (!content) {
return react__WEBPACK_IMPORTED_MODULE_0___default().createElement((react__WEBPACK_IMPORTED_MODULE_0___default().Fragment), null, null);
}
return react__WEBPACK_IMPORTED_MODULE_0___default().createElement("li", {
className: "meta"
}, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
className: bubbleClass
}, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
className: "message-content"
}, content)));
}
}
;
/***/ }),
/***/ "./src/widgets/new-topic-by-id.jsx":
/*!*****************************************!*\
!*** ./src/widgets/new-topic-by-id.jsx ***!
@@ -11559,9 +11626,7 @@ __webpack_require__.r(__webpack_exports__);
/* harmony import */ var react_intl__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(react_intl__WEBPACK_IMPORTED_MODULE_1__);
/* harmony import */ var tinode_sdk__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! tinode-sdk */ "tinode-sdk");
/* harmony import */ var tinode_sdk__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(tinode_sdk__WEBPACK_IMPORTED_MODULE_2__);
/* harmony import */ var _lib_strformat_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../lib/strformat.js */ "./src/lib/strformat.js");
/* harmony import */ var _lib_utils_js__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../lib/utils.js */ "./src/lib/utils.js");
/* harmony import */ var _lib_utils_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../lib/utils.js */ "./src/lib/utils.js");
@@ -11595,10 +11660,12 @@ class ReceivedMarker extends (react__WEBPACK_IMPORTED_MODULE_0___default().PureC
} else if (this.props.received == (tinode_sdk__WEBPACK_IMPORTED_MODULE_2___default().MESSAGE_STATUS_FAILED)) {
timestamp = formatMessage(messages.message_sending_failed);
} else {
timestamp = (0,_lib_strformat_js__WEBPACK_IMPORTED_MODULE_3__.shortDateFormat)(this.props.timestamp, this.props.intl.locale);
timestamp = this.props.timestamp.toLocaleTimeString(this.props.intl.locale, {
timeStyle: 'short'
});
}
const icon = (0,_lib_utils_js__WEBPACK_IMPORTED_MODULE_4__.deliveryMarker)(this.props.received);
const icon = (0,_lib_utils_js__WEBPACK_IMPORTED_MODULE_3__.deliveryMarker)(this.props.received);
const marker = icon ? react__WEBPACK_IMPORTED_MODULE_0___default().createElement("i", {
className: 'material-icons small ' + icon.color
}, icon.name) : null;
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long