Skip to content

Commit

Permalink
Merge pull request #286 from GuiLeme/issue-21645-bbb-core
Browse files Browse the repository at this point in the history
feat: add chat features to the recordings - Issue 21645 bbb core
  • Loading branch information
antobinary authored Nov 21, 2024
2 parents af4f62e + 9c5b125 commit 4e4d4fc
Show file tree
Hide file tree
Showing 11 changed files with 254 additions and 16 deletions.
4 changes: 4 additions & 0 deletions src/components/chat/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@ const Chat = () => {
}
};

const scrollTo = (element) => {
handleAutoScroll(firstNode.current, element, POSITIONS.TOP, config.align);
}
useEffect(() => {
if (!interaction.current) {
if (config.scroll) {
Expand All @@ -58,6 +61,7 @@ const Chat = () => {
>
<Messages
currentIndex={currentIndex}
scrollTo={scrollTo}
setRef={(node, index) => setRef(node, index)}
/>
</div>
Expand Down
16 changes: 15 additions & 1 deletion src/components/chat/messages/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ const defaultProps = {

const Messages = ({
currentIndex,
scrollTo,
setRef,
}) => {

Expand All @@ -33,15 +34,28 @@ const Messages = ({
switch (type) {
case ID.USERS:

const indexOfMessageToBeReplied = (item.replyToMessageId)
? storage.messages.findIndex((message) => message.id === item.replyToMessageId) : -1;
const messageToBeReplied = (indexOfMessageToBeReplied !== -1)
? storage.messages[indexOfMessageToBeReplied]
: null;
return (
<span ref={node => setRef(node, index)}>
<span
key={item.id}
id={item.id}
className='user-message-wrapper'
ref={node => setRef(node, index)}>
<UserMessage
edited={!!item.lastEditedTimestamp}
reactions={item.reactions}
active={active}
emphasized={item.emphasized}
hyperlink={item.hyperlink}
initials={item.initials}
moderator={item.moderator}
name={item.name}
messageToBeReplied={messageToBeReplied}
scrollTo={scrollTo}
text={item.message}
timestamp={timestamp}
/>
Expand Down
45 changes: 43 additions & 2 deletions src/components/chat/messages/index.scss
Original file line number Diff line number Diff line change
@@ -1,12 +1,35 @@
@import 'src/styles/sizes';

:root {
--message-box-shadow-color: var(--gray-light);
--gray-light-color: var(--gray-light);
--gray-lightest-color: var(--gray-lightest);
--highlight-color: var(--blue);
}

.reply-tag {
background-color: var(--gray-lightest-color);
display: flex;
width: 90%;
padding: $padding-small;
margin-bottom: $margin-extra-small;
border-radius: .35rem;
align-items: center;
line-height: 1;
}

.user-message-wrapper {
transition: background-color .5s ease;
}

.highlight {
background-color: var(--highlight-color);
}

.message {
box-sizing: border-box;
display: flex;
flex-direction: column;

width: 100%;

[dir="ltr"] & {
Expand All @@ -17,6 +40,10 @@
padding: 0 $padding $padding 0;
}

.main-content-wrapper {
display: flex;
}

.data {
display: flex;
flex-direction: column;
Expand Down Expand Up @@ -47,6 +74,20 @@
}
}

.edited-tag {
color: var(--gray-light-color);
font-style: italic;
font-size: 75%;
display: flex;
align-items: center;
margin-left: 0.5rem;
line-height: 1;
}

.edited-label {
margin-left: 0.25rem;
}

.name {
font-weight: var(--font-weight-semi-bold);
overflow: hidden;
Expand Down Expand Up @@ -95,7 +136,7 @@

&:focus,
&:hover {
box-shadow: 0 0 0 $margin-extra-small var(--message-box-shadow-color);
box-shadow: 0 0 0 $margin-extra-small var(--gray-light-color);
}
}

Expand Down
9 changes: 9 additions & 0 deletions src/components/chat/messages/info.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import React from 'react';
import PropTypes from 'prop-types';
import Icon from 'components/utils/icon';
import { FormattedTime } from 'react-intl';
import cx from 'classnames';
import { getTimestampAsMilliseconds } from 'utils/data';
Expand All @@ -21,6 +22,7 @@ const Info = ({
active,
name,
timestamp,
edited,
}) => {
const milliseconds = getTimestampAsMilliseconds(timestamp);

Expand All @@ -39,6 +41,13 @@ const Info = ({
value={milliseconds}
/>
</div>
{ edited
&& (
<span className='edited-tag'>
<Icon name='pen'/>
<span className='edited-label'>(Edited)</span>
</span>
)}
</div>
);
};
Expand Down
49 changes: 36 additions & 13 deletions src/components/chat/messages/message.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import Info from './info';
import Margin from './margin';
import player from 'utils/player';
import './index.scss';
import Reply from './reply';
import ChatMessageReactions from './reactions';

const propTypes = {
active: PropTypes.bool,
Expand Down Expand Up @@ -38,7 +40,11 @@ const Message = ({
emphasized,
icon,
initials,
messageToBeReplied,
scrollTo,
edited,
name,
reactions,
timestamp,
}) => {
const handleOnClick = () => {
Expand All @@ -47,24 +53,41 @@ const Message = ({

return (
<div className="message">
<Margin
active={active}
circle={circle}
icon={icon}
initials={initials}
name={name}
onClick={() => handleOnClick()}
/>
<div className="data">
<Info
<div
className="main-content-wrapper"
>
<Margin
active={active}
circle={circle}
icon={icon}
initials={initials}
name={name}
timestamp={timestamp}
onClick={() => handleOnClick()}
/>
<div className={cx('text', { inactive: !active, emphasized })}>
{children}
<div className="data">
<Info
edited={edited}
active={active}
name={name}
timestamp={timestamp}
/>
{messageToBeReplied &&
<Reply
active={active}
scrollTo={scrollTo}
idToReference={messageToBeReplied.id}
text={messageToBeReplied.message}
/>
}
<div className={cx('text', { inactive: !active, emphasized })}>
{children}
</div>
</div>
</div>
<ChatMessageReactions
reactions={reactions}
active={active}
/>
</div>
);
};
Expand Down
35 changes: 35 additions & 0 deletions src/components/chat/messages/reactions/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import React from 'react';
import cx from 'classnames';
import './index.scss';

const sortByCount = (r1, r2) => r2.number - r1.number;

const ChatMessageReactions = (props) => {
const {
reactions,
active,
} = props;

if (reactions.length === 0) return null;
return (
<div
className='reactions-wrapper'
>
{reactions.sort(sortByCount).map((details) => (
<span
className={cx('emoji-wrapper', { inactive: !active })}
>
<em-emoji
size={parseFloat(
window.getComputedStyle(document.documentElement).fontSize,
)}
>{details.emoji}</em-emoji>
<span>{details.count}</span>
</span>
)
)}
</div>
);
};

export default ChatMessageReactions;
34 changes: 34 additions & 0 deletions src/components/chat/messages/reactions/index.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
@import 'src/styles/sizes';

:root {
--emoji-wrapper-border-color: var(--gray-lightest);
--emoji-wrapper-border-color-hover: var(--gray-lighter);
}

.reactions-wrapper {
display: flex;
flex-wrap: wrap;
margin-top: 0.25rem;
user-select: none;
}

.emoji-wrapper {
background: none;
border-radius: 1rem;
padding: 0.375rem 1rem;
line-height: 1;
display: flex;
flex-wrap: nowrap;
border: 1px solid var(--emoji-wrapper-border-color);
margin-right: 0.25rem;

em-emoji {
[dir='ltr'] & {
margin-right: 0.25rem;
}

[dir='rtl'] & {
margin-left: 0.25rem;
}
}
}
54 changes: 54 additions & 0 deletions src/components/chat/messages/reply/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import React from 'react';
import PropTypes from 'prop-types';
import cx from 'classnames';
import '../index.scss';

const propTypes = {
active: PropTypes.bool,
idToReference: PropTypes.string,
text: PropTypes.string,
};

const defaultProps = {
active: false,
idToReference: '',
text: '',
};

const Reply = ({
active,
idToReference,
scrollTo,
text,
}) => {

const handleClickReply = () => {
const messageReplied = document.getElementById(idToReference);
scrollTo(messageReplied);
messageReplied.classList.add('highlight')
setTimeout(() =>
messageReplied.classList.remove('highlight'), 800
);
}

return (
<span
onClick={handleClickReply}
className={cx('reply-tag', {inactive: !active})}
>
{text}
</span>
);
};

Reply.propTypes = propTypes;
Reply.defaultProps = defaultProps;

// Checks the message active state
const areEqual = (prevProps, nextProps) => {
if (prevProps.active !== nextProps.active) return false;

return true;
};

export default React.memo(Reply, areEqual);
9 changes: 9 additions & 0 deletions src/components/chat/messages/user/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,11 @@ const User = ({
name,
moderator,
text,
edited,
timestamp,
messageToBeReplied,
scrollTo,
reactions,
}) => {

return (
Expand All @@ -42,14 +46,19 @@ const User = ({
circle={!moderator}
emphasized={emphasized}
initials={initials}
edited={edited}
name={name}
reactions={reactions}
timestamp={timestamp}
messageToBeReplied={messageToBeReplied}
scrollTo={scrollTo}
>
<Text
active={active}
hyperlink={hyperlink}
text={text}
/>

</Message>
);
};
Expand Down
Loading

0 comments on commit 4e4d4fc

Please sign in to comment.