convert chat time-window-list components

This commit is contained in:
Ramón Souza 2021-11-02 13:39:50 +00:00
parent 401ee71563
commit 674c58ff90
10 changed files with 356 additions and 293 deletions

View File

@ -3,9 +3,8 @@ import { findDOMNode } from 'react-dom';
import PropTypes from 'prop-types';
import { defineMessages, injectIntl } from 'react-intl';
import _ from 'lodash';
import Button from '/imports/ui/components/button/component';
import { List, AutoSizer,CellMeasurer, CellMeasurerCache } from 'react-virtualized';
import { styles } from './styles';
import { AutoSizer,CellMeasurer, CellMeasurerCache } from 'react-virtualized';
import Styled from './styles';
import ChatLogger from '/imports/ui/components/chat/chat-logger/ChatLogger';
import TimeWindowChatItem from './time-window-chat-item/container';
@ -236,9 +235,8 @@ class TimeWindowList extends PureComponent {
if (count && userScrolledBack) {
return (
<Button
<Styled.UnreadButton
aria-hidden="true"
className={styles.unreadButton}
color="primary"
size="sm"
key="unread-messages"
@ -278,7 +276,7 @@ class TimeWindowList extends PureComponent {
return (
[
<div
<Styled.MessageListWrapper
onMouseDown={() => {
this.setState({
userScrolledBack: true,
@ -292,7 +290,6 @@ class TimeWindowList extends PureComponent {
this.userScrolledBack = true
}
}}
className={styles.messageListWrapper}
key="chat-list"
data-test="chatMessages"
aria-live="polite"
@ -305,7 +302,7 @@ class TimeWindowList extends PureComponent {
this.cache.clearAll();
}
return (
<List
<Styled.MessageList
ref={(ref) => {
if (ref !== null) {
this.listRef = ref;
@ -317,7 +314,6 @@ class TimeWindowList extends PureComponent {
}}
isScrolling
rowHeight={this.cache.rowHeight}
className={styles.messageList}
rowRenderer={this.rowRender}
rowCount={timeWindowsValues.length}
height={height}
@ -340,7 +336,7 @@ class TimeWindowList extends PureComponent {
);
}}
</AutoSizer>
</div>,
</Styled.MessageListWrapper>,
this.renderUnreadNotification(),
]
);

View File

@ -0,0 +1,68 @@
import styled from 'styled-components';
import {
smPaddingX,
smPaddingY,
mdPaddingX,
mdPaddingY,
} from '/imports/ui/stylesheets/styled-components/general';
import { ButtonElipsis } from '/imports/ui/stylesheets/styled-components/placeholders';
import { VirtualizedScrollboxVertical } from '/imports/ui/stylesheets/styled-components/scrollable';
const UnreadButton = styled(ButtonElipsis)`
flex-shrink: 0;
width: 100%;
text-transform: uppercase;
margin-bottom: .25rem;
z-index: 3;
`;
const MessageListWrapper = styled.div`
display: flex;
flex-flow: column;
flex-grow: 1;
flex-shrink: 1;
position: relative;
overflow-x: hidden;
overflow-y: auto;
padding-left: ${smPaddingX};
margin-left: calc(-1 * ${mdPaddingX});
padding-right: ${smPaddingY};
margin-right: calc(-1 * ${mdPaddingY});
padding-bottom: ${mdPaddingX};
margin-bottom: calc(-1 * ${mdPaddingX});
z-index: 2;
[dir="rtl"] & {
padding-right: ${mdPaddingX};
margin-right: calc(-1 * ${mdPaddingX});
padding-left: ${mdPaddingY};
margin-left: calc(-1 * ${mdPaddingX});
}
`;
const MessageList = styled(VirtualizedScrollboxVertical)`
flex-flow: column;
flex-grow: 1;
flex-shrink: 1;
margin: 0 auto 0 0;
right: 0 ${mdPaddingX} 0 0;
padding-top: 0;
width: 100%;
outline-style: none;
[dir="rtl"] & {
margin: 0 0 0 auto;
padding: 0 0 0 ${mdPaddingX};
}
&:after {
content: "";
display: block;
height: ${mdPaddingX};
}
`;
export default {
UnreadButton,
MessageListWrapper,
MessageList,
};

View File

@ -1,57 +0,0 @@
@import "/imports/ui/stylesheets/mixins/_scrollable";
@import "/imports/ui/stylesheets/variables/placeholders";
.messageListWrapper {
display: flex;
flex-flow: column;
flex-grow: 1;
flex-shrink: 1;
position: relative;
overflow-x: hidden;
overflow-y: auto;
padding-left: var(--sm-padding-x);
margin-left: calc(-1 * var(--md-padding-x));
padding-right: var(--sm-padding-y);
margin-right: calc(-1 * var(--md-padding-y));
padding-bottom: var(--md-padding-x);
margin-bottom: calc(-1 * var(--md-padding-x));
z-index: 2;
[dir="rtl"] & {
padding-right: var(--md-padding-x);
margin-right: calc(-1 * var(--md-padding-x));
padding-left: var(--md-padding-y);
margin-left: calc(-1 * var(--md-padding-x));
}
}
.messageList {
@include scrollbox-vertical();
flex-flow: column;
flex-grow: 1;
flex-shrink: 1;
margin: 0 auto 0 0;
right: 0 var(--md-padding-x) 0 0;
padding-top: 0;
width: 100%;
outline-style: none;
[dir="rtl"] & {
margin: 0 0 0 auto;
padding: 0 0 0 var(--md-padding-x);
}
&:after {
content: "";
display: block;
height: var(--md-padding-x);
}
}
.unreadButton {
flex-shrink: 0;
width: 100%;
text-transform: uppercase;
margin-bottom: .25rem;
z-index: 3;
@extend %text-elipsis;
}

View File

@ -3,12 +3,9 @@ import PropTypes from 'prop-types';
import { FormattedTime, defineMessages, injectIntl } from 'react-intl';
import _ from 'lodash';
import UserAvatar from '/imports/ui/components/user-avatar/component';
import cx from 'classnames';
import ChatLogger from '/imports/ui/components/chat/chat-logger/ChatLogger';
import MessageChatItem from './message-chat-item/component';
import PollService from '/imports/ui/components/poll/service';
import Icon from '/imports/ui/components/icon/component';
import { styles } from './styles';
import Styled from './styles';
const CHAT_CONFIG = Meteor.settings.public.chat;
const CHAT_CLEAR_MESSAGE = CHAT_CONFIG.system_messages_keys.chat_clear;
@ -86,13 +83,13 @@ class TimeWindowChatItem extends PureComponent {
}
return (
<div className={styles.item} key={`time-window-chat-item-${messageKey}`}>
<div className={styles.messages}>
<Styled.Item key={`time-window-chat-item-${messageKey}`}>
<Styled.Messages>
{messages.map(message => (
message.text !== ''
? (
<MessageChatItem
className={(message.id ? styles.systemMessage : styles.systemMessageNoBorder)}
<Styled.SystemMessageChatItem
border={message.id}
key={message.id ? message.id : _.uniqueId('id-')}
text={intlMessages[message.text] ? intl.formatMessage(intlMessages[message.text]) : message.text }
time={message.time}
@ -103,8 +100,8 @@ class TimeWindowChatItem extends PureComponent {
/>
) : null
))}
</div>
</div>
</Styled.Messages>
</Styled.Item>
);
}
@ -130,13 +127,12 @@ class TimeWindowChatItem extends PureComponent {
const regEx = /<a[^>]+>/i;
ChatLogger.debug('TimeWindowChatItem::renderMessageItem', this.props);
const defaultAvatarString = name?.toLowerCase().slice(0, 2) || " ";
const emphasizedTextClass = isModerator && CHAT_EMPHASIZE_TEXT && chatId === CHAT_PUBLIC_ID ?
styles.emphasizedMessage : null;
const emphasizedText = isModerator && CHAT_EMPHASIZE_TEXT && chatId === CHAT_PUBLIC_ID;
return (
<div className={styles.item} key={`time-window-${messageKey}`}>
<div className={styles.wrapper}>
<div className={styles.avatarWrapper}>
<Styled.Item key={`time-window-${messageKey}`}>
<Styled.Wrapper>
<Styled.AvatarWrapper>
<UserAvatar
color={color}
moderator={isModerator}
@ -144,29 +140,28 @@ class TimeWindowChatItem extends PureComponent {
>
{defaultAvatarString}
</UserAvatar>
</div>
<div className={styles.content}>
<div className={styles.meta}>
<div className={isOnline ? styles.name : styles.logout}>
</Styled.AvatarWrapper>
<Styled.Content>
<Styled.Meta>
<Styled.Name isOnline={isOnline}>
<span>{name}</span>
{isOnline
? null
: (
<span className={styles.offline}>
<Styled.Offline>
{`(${intl.formatMessage(intlMessages.offline)})`}
</span>
</Styled.Offline>
)}
</div>
<time className={styles.time} dateTime={dateTime}>
</Styled.Name>
<Styled.Time dateTime={dateTime}>
<FormattedTime value={dateTime} />
</time>
</div>
<div className={styles.messages}>
</Styled.Time>
</Styled.Meta>
<Styled.Messages>
{messages.map(message => (
<MessageChatItem
className={regEx.test(message.text) ?
cx(styles.hyperlink, emphasizedTextClass) :
cx(styles.message, emphasizedTextClass)}
<Styled.ChatItem
hasLink={regEx.test(message.text)}
emphasizedMessage={emphasizedText}
key={message.id}
text={message.text}
time={message.time}
@ -188,10 +183,10 @@ class TimeWindowChatItem extends PureComponent {
scrollArea={scrollArea}
/>
))}
</div>
</div>
</div>
</div>
</Styled.Messages>
</Styled.Content>
</Styled.Wrapper>
</Styled.Item>
);
}
@ -212,28 +207,27 @@ class TimeWindowChatItem extends PureComponent {
const dateTime = new Date(timestamp);
return messages ? (
<div className={styles.item} key={_.uniqueId('message-poll-item-')}>
<div className={styles.wrapper} ref={(ref) => { this.item = ref; }}>
<div className={styles.avatarWrapper}>
<Styled.Item key={_.uniqueId('message-poll-item-')}>
<Styled.Wrapper ref={(ref) => { this.item = ref; }}>
<Styled.AvatarWrapper>
<UserAvatar
color={PollService.POLL_AVATAR_COLOR}
moderator={true}
>
{<Icon className={styles.isPoll} iconName="polling" />}
{<Styled.PollIcon iconName="polling" />}
</UserAvatar>
</div>
<div className={styles.content}>
<div className={styles.meta}>
<div className={styles.name}>
</Styled.AvatarWrapper>
<Styled.Content>
<Styled.Meta>
<Styled.Name>
<span>{intl.formatMessage(intlMessages.pollResult)}</span>
</div>
<time className={styles.time} dateTime={dateTime}>
</Styled.Name>
<Styled.Time dateTime={dateTime}>
<FormattedTime value={dateTime} />
</time>
</div>
<MessageChatItem
</Styled.Time>
</Styled.Meta>
<Styled.PollMessageChatItem
type="poll"
className={cx(styles.message, styles.pollWrapper)}
key={messages[0].id}
text={getPollResultString(extra.pollResultData, intl)}
time={messages[0].time}
@ -243,9 +237,9 @@ class TimeWindowChatItem extends PureComponent {
scrollArea={scrollArea}
color={color}
/>
</div>
</div>
</div>
</Styled.Content>
</Styled.Wrapper>
</Styled.Item>
) : null;
}
@ -259,9 +253,9 @@ class TimeWindowChatItem extends PureComponent {
}
return (
<div className={styles.item}>
<Styled.Item>
{this.renderMessageItem()}
</div>
</Styled.Item>
);
}
}

View File

@ -0,0 +1,210 @@
import styled from 'styled-components';
import {
borderRadius,
borderSize,
chatPollMarginSm,
} from '/imports/ui/stylesheets/styled-components/general';
import { lineHeightComputed, fontSizeBase, btnFontWeight } from '/imports/ui/stylesheets/styled-components/typography';
import {
systemMessageBackgroundColor,
systemMessageBorderColor,
systemMessageFontColor,
colorHeading,
colorGrayLight,
palettePlaceholderText,
colorGrayLighter,
colorPrimary,
colorText,
} from '/imports/ui/stylesheets/styled-components/palette';
import MessageChatItem from './message-chat-item/component';
import Icon from '/imports/ui/components/icon/component';
const Item = styled.div`
padding: calc(${lineHeightComputed} / 4) 0 calc(${lineHeightComputed} / 2) 0;
font-size: ${fontSizeBase};
pointer-events: auto;
[dir="rtl"] & {
direction: rtl;
}
`;
const Messages = styled.div`
> * {
&:first-child {
margin-top: calc(${lineHeightComputed} / 4);
}
}
`;
const SystemMessageChatItem = styled(MessageChatItem)`
${({ border }) => border && `
background: ${systemMessageBackgroundColor};
border: 1px solid ${systemMessageBorderColor};
border-radius: ${borderRadius};
font-weight: ${btnFontWeight};
padding: ${fontSizeBase};
color: ${systemMessageFontColor};
margin-top: 0px;
margin-bottom: 0px;
overflow-wrap: break-word;
`}
${({ border }) => !border && `
color: ${systemMessageFontColor};
margin-top: 0px;
margin-bottom: 0px;
`}
`;
const Wrapper = styled.div`
display: flex;
flex-flow: row;
flex: 1;
position: relative;
margin: ${borderSize} 0 0 ${borderSize};
[dir="rtl"] & {
margin: ${borderSize} ${borderSize} 0 0;
}
`;
const AvatarWrapper = styled.div`
flex-basis: 2.25rem;
flex-shrink: 0;
flex-grow: 0;
margin: 0 calc(${lineHeightComputed} / 2) 0 0;
[dir="rtl"] & {
margin: 0 0 0 calc(${lineHeightComputed} / 2);
}
`;
const Content = styled.div`
flex: 1;
display: flex;
flex-flow: column;
overflow-x: hidden;
width: calc(100% - 1.7rem);
`;
const Meta = styled.div`
display: flex;
flex: 1;
flex-flow: row;
line-height: 1.35;
`;
const Name = styled.div`
display: flex;
min-width: 0;
font-weight: 600;
position: relative;
&:first-child {
min-width: 0;
display: inline-block;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
${({ isOnline }) => isOnline && `
color: ${colorHeading};
`}
${({ isOnline }) => !isOnline && `
text-transform: capitalize;
font-style: italic;
& > span {
text-align: right;
padding: 0 .1rem 0 0;
[dir="rtl"] & {
text-align: left;
padding: 0 0 0 .1rem;
}
}
`}
`;
const Offline = styled.span`
color: ${colorGrayLight};
font-weight: 100;
text-transform: lowercase;
font-style: italic;
font-size: 90%;
line-height: 1;
align-self: center;
`;
const Time = styled.time`
flex-shrink: 0;
flex-grow: 0;
flex-basis: 3.5rem;
color: ${palettePlaceholderText};
text-transform: uppercase;
font-size: 75%;
margin: 0 0 0 calc(${lineHeightComputed} / 2);
[dir="rtl"] & {
margin: 0 calc(${lineHeightComputed} / 2) 0 0;
}
& > span {
vertical-align: sub;
}
`;
const ChatItem = styled(MessageChatItem)`
flex: 1;
margin-top: calc(${lineHeightComputed} / 3);
margin-bottom: 0;
color: ${colorText};
word-wrap: break-word;
${({ hasLink }) => hasLink && `
& > a {
color: ${colorPrimary};
}
`}
${({ emphasizedMessage }) => emphasizedMessage && `
font-weight: bold;
`}
`;
const PollIcon = styled(Icon)`
bottom: 1px;
`;
const PollMessageChatItem = styled(MessageChatItem)`
flex: 1;
margin-top: calc(${lineHeightComputed} / 3);
margin-bottom: 0;
color: ${colorText};
word-wrap: break-word;
background: ${systemMessageBackgroundColor};
border: solid 1px ${colorGrayLighter};
border-radius: ${borderRadius};
padding: ${chatPollMarginSm};
padding-left: 1rem;
margin-top: ${chatPollMarginSm} !important;
`;
export default {
Item,
Messages,
SystemMessageChatItem,
Wrapper,
AvatarWrapper,
Content,
Meta,
Name,
Offline,
Time,
ChatItem,
PollIcon,
PollMessageChatItem,
};

View File

@ -1,173 +0,0 @@
@import "/imports/ui/stylesheets/variables/placeholders";
:root {
--systemMessage-background-color: #F9FBFC;
--systemMessage-border-color: #C5CDD4;
--systemMessage-font-color: var(--color-dark-grey);
--chat-poll-margin-sm: .5rem;
}
.item {
padding: calc(var(--line-height-computed) / 4) 0 calc(var(--line-height-computed) / 2) 0;
font-size: var(--font-size-base);
pointer-events: auto;
[dir="rtl"] & {
direction: rtl;
}
}
.wrapper {
display: flex;
flex-flow: row;
flex: 1;
position: relative;
margin:var(--border-size) 0 0 var(--border-size);
[dir="rtl"] & {
margin: var(--border-size) var(--border-size) 0 0;
}
}
.systemMessage {
background: var(--systemMessage-background-color);
border: 1px solid var(--systemMessage-border-color);
border-radius: var(--border-radius);
font-weight: var(--btn-font-weight);
padding: var(--font-size-base);
color: var(--systemMessage-font-color);
margin-top: 0px;
margin-bottom: 0px;
overflow-wrap: break-word;
}
.systemMessageNoBorder {
color: var(--systemMessage-font-color);
margin-top: 0px;
margin-bottom: 0px;
}
.avatarWrapper {
flex-basis: 2.25rem;
flex-shrink: 0;
flex-grow: 0;
margin: 0 calc(var(--line-height-computed) / 2) 0 0;
[dir="rtl"] & {
margin: 0 0 0 calc(var(--line-height-computed) / 2);
}
}
.content {
flex: 1;
display: flex;
flex-flow: column;
overflow-x: hidden;
width: calc(100% - 1.7rem);
}
.meta {
display: flex;
flex: 1;
flex-flow: row;
line-height: 1.35;
& + .message {
margin-top: 0;
}
}
.name,
.logout {
display: flex;
min-width: 0;
font-weight: 600;
position: relative;
:first-child {
@extend %text-elipsis;
}
}
.name {
color: var(--color-heading);
}
.logout {
text-transform: capitalize;
font-style: italic;
& > span {
text-align: right;
padding: 0 .1rem 0 0;
[dir="rtl"] & {
text-align: left;
padding: 0 0 0 .1rem;
}
}
}
.offline {
color: var(--color-gray-light);
font-weight: 100;
text-transform: lowercase;
font-style: italic;
font-size: 90%;
line-height: 1;
align-self: center;
}
.time {
flex-shrink: 0;
flex-grow: 0;
flex-basis: 3.5rem;
color: var(--palette-placeholder-text);
text-transform: uppercase;
font-size: 75%;
margin: 0 0 0 calc(var(--line-height-computed) / 2);
[dir="rtl"] & {
margin: 0 calc(var(--line-height-computed) / 2) 0 0;
}
> span {
vertical-align: sub;
}
}
.messages {
> .message:first-child {
margin-top: calc(var(--line-height-computed) / 4);
}
}
.message, .hyperlink {
flex: 1;
margin-top: calc(var(--line-height-computed) / 3);
margin-bottom: 0;
color: var(--color-text);
word-wrap: break-word;
}
.hyperlink {
a {
color: var(--color-primary);
}
}
.isPoll {
bottom: 1px;
}
.pollWrapper {
background: var(--systemMessage-background-color);
border: solid 1px var(--color-gray-lighter);
border-radius: var(--border-radius);
padding: var(--chat-poll-margin-sm);
padding-left: 1rem;
margin-top: var(--chat-poll-margin-sm) !important;
}
.emphasizedMessage{
font-weight: bold;
}

View File

@ -21,6 +21,7 @@ const modalMargin = '3rem';
const titlePositionLeft = '2.2rem';
const userIndicatorsOffset = '-5px';
const indicatorPadding = '.45rem'; // used to center presenter indicator icon in Chrome / Firefox / Edge
const chatPollMarginSm = '.5rem';
export {
borderSizeSmall,
@ -45,4 +46,5 @@ export {
titlePositionLeft,
userIndicatorsOffset,
indicatorPadding,
chatPollMarginSm,
};

View File

@ -36,6 +36,12 @@ const userThumbnailBorder = colorGrayLight;
const loaderBg = colorGrayDark;
const loaderBullet = colorWhite;
const systemMessageBackgroundColor = '#F9FBFC';
const systemMessageBorderColor = '#C5CDD4';
const systemMessageFontColor = colorGrayDark;
const colorHeading = colorGrayDark;
const palettePlaceholderText = '#787675';
export {
colorWhite,
colorOffWhite,
@ -66,4 +72,9 @@ export {
userThumbnailBorder,
loaderBg,
loaderBullet,
systemMessageBackgroundColor,
systemMessageBorderColor,
systemMessageFontColor,
colorHeading,
palettePlaceholderText,
};

View File

@ -1,4 +1,5 @@
import styled from 'styled-components';
import Button from '/imports/ui/components/button/component';
const FlexColumn = styled.div`
display: flex;
@ -41,6 +42,14 @@ const HeaderElipsis = styled.h3`
text-overflow: ellipsis;
`;
const ButtonElipsis = styled(Button)`
min-width: 0;
display: inline-block;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
`;
export {
FlexColumn,
FlexRow,
@ -48,4 +57,5 @@ export {
TextElipsis,
TitleElipsis,
HeaderElipsis,
ButtonElipsis,
};

View File

@ -9,6 +9,7 @@ const fontSizeXL = '1.75rem';
const fontSizeMD = '0.95rem';
const headingsFontWeight = '500';
const btnFontWeight = '600';
export {
lineHeightComputed,
@ -21,4 +22,5 @@ export {
fontSizeXL,
fontSizeMD,
headingsFontWeight,
btnFontWeight,
};