Skip to content

Commit

Permalink
Merge branch 'develop' into feat/RocketChat#414-message-notification
Browse files Browse the repository at this point in the history
  • Loading branch information
umangutkarsh committed Feb 5, 2024
2 parents 8df70a5 + 3720776 commit 480378a
Show file tree
Hide file tree
Showing 26 changed files with 371 additions and 104 deletions.
149 changes: 149 additions & 0 deletions packages/react/src/components/AllThreads/AllThreads.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
import React, { useState, useMemo } from 'react';
import { css } from '@emotion/react';
import classes from './AllThreads.module.css';
import { Icon } from '../Icon';
import { Box } from '../Box';
import { ActionButton } from '../ActionButton';
import { useMessageStore, useUserStore, useThreadsMessageStore } from '../../store';
import { MessageBody } from '../Message/MessageBody';
import { MessageMetrics } from '../Message/MessageMetrics';
import MessageAvatarContainer from '../Message/MessageAvatarContainer';
import MessageBodyContainer from '../Message/MessageBodyContainer';
import MessageHeader from '../Message/MessageHeader';


const MessageCss = css`
display: flex;
flex-direction: row;
align-items: flex-start;
padding-top: 0.5rem;
-webkit-padding-before: 0.5rem;
padding-block-start: 0.5rem;
padding-bottom: 0.25rem;
-webkit-padding-after: 0.25rem;
padding-block-end: 0.25rem;
padding-left: 1.25rem;
padding-right: 1.25rem;
padding-inline: 1.25rem;
cursor: pointer;
&:hover {
background: #f2f3f5;
}
`;

const AllThreads = () => {
const showAvatar = useUserStore((state) => state.showAvatar);
const messages = useMessageStore((state) => state.messages);
const setShowAllThreads = useThreadsMessageStore((state) => state.setShowAllThreads);
const openThread = useMessageStore((state) => state.openThread);
const [text, setText] = useState('');

const toggleShowAllThreads = () => {
setShowAllThreads(false);
};

const handleOpenThread = (msg) => () => {
openThread(msg);
toggleShowAllThreads(false);
};

const handleInputChange = (e) => {
setText(e.target.value);
};

const filteredThreads = useMemo(() => {
return messages.filter((message) =>
message.msg.toLowerCase().includes(text.toLowerCase())
);
}, [messages, text]);

return (
<Box className={classes.component}>
<Box className={classes.wrapContainer}>

<Box style={{ padding: '16px' }}>
<Box css={css`display: flex;`}>
<h3 style={{ display: 'contents' }}>
<Icon
name="thread"
size="1.25rem"
style={{ padding: '0px 20px 20px 0px' }}
/>
<Box css={css`
width: 100%;
color: #4a4a4a;
`}
>
Threads
</Box>
<ActionButton onClick={toggleShowAllThreads} ghost size="small">
<Icon name="cross" size="1.25rem" />
</ActionButton>
</h3>
</Box>

<Box
className={classes.searchContainer}
style={{ border: '2px solid #ddd', position: 'relative' }}
>
<input
placeholder="Search Messages"
onChange={handleInputChange}
className={classes.textInput}
/>

<Icon name="magnifier" size="1.25rem" style={{ padding: '0.125em', cursor: 'pointer' }} />
</Box>
</Box>

<Box
style={{
flex: '1',
overflow: 'auto',
display: 'flex',
flexDirection: 'column',
justifyContent: filteredThreads.length === 0 ? 'center' : 'initial',
alignItems: filteredThreads.length === 0 ? 'center' : 'initial'
}}
>
{filteredThreads.length === 0 ? (
<Box style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', color: '#4a4a4a' }}>
<Icon name="magnifier" size="3rem" style={{ padding: '0.5rem' }} />
<span style={{ fontSize: '1.2rem', fontWeight: 'bold' }}>No threads found</span>
</Box>
) : (filteredThreads
.map((message) => (
!message.t && message.tcount && (
<Box key={message._id} css={MessageCss} onClick={handleOpenThread(message)}>
{showAvatar && (
<MessageAvatarContainer
message={message}
sequential={false}
isStarred={false}
/>
)}
<MessageBodyContainer>
{<MessageHeader message={message} isTimeStamped={false} />}
<MessageBody>
{message.attachments && message.attachments.length > 0 ? (
message.file.name
) : (
message.msg
)}
</MessageBody>

<MessageMetrics
message={message}
isReplyButton={false}
/>
</MessageBodyContainer>
</Box>
)
)))}
</Box>
</Box>
</Box>
);
};

export default AllThreads;
41 changes: 41 additions & 0 deletions packages/react/src/components/AllThreads/AllThreads.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
.component {
position: fixed;
right: 0;
top: 0;
width: 350px;
height: 100%;
overflow: hidden;
background-color: white;
box-shadow: -1px 0px 5px rgb(0 0 0 / 25%);
z-index: 100;
}

.wrapContainer {
height: 100%;
display: flex;
flex-direction: column;
}

.searchContainer {
display: flex;
align-items: center;
justify-content: center;
background-color: #fff;
}

.textInput {
width: 75%;
height: 2.5rem;
border: none;
outline: none;
}

.textInput::placeholder {
padding-left: 5px;
}

@media (max-width: 550px) {
.component {
width: 100vw;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ const PinnedAttachment = ({ attachment }) => (
<Box
style={{
borderInlineStart: '1px solid currentColor',
paddingLeft: '0.8rem'
}}
>
<Box>{attachment?.author_name}</Box>
Expand Down
49 changes: 40 additions & 9 deletions packages/react/src/components/ChatHeader/ChatHeader.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,17 @@ import {
useMemberStore,
useSearchMessageStore,
useChannelStore,
useToastStore
} from '../../store';
import { ThreadHeader } from '../ThreadHeader';
import { DynamicHeader } from '../DynamicHeader';
import { Tooltip } from '../Tooltip';
import { Box } from '../Box';
import useComponentOverrides from '../../theme/useComponentOverrides';
import { Icon } from '../Icon';
import { ActionButton } from '../ActionButton';
import { Menu } from '../Menu';
import useThreadsMessageStore from '../../store/threadsMessageStore';
import { useToastBarDispatch } from '../../hooks/useToastBarDispatch';

const ChatHeader = ({
isClosable,
Expand Down Expand Up @@ -44,16 +47,24 @@ const ChatHeader = ({
(state) => state.setIsUserAuthenticated
);

const dispatchToastMessage = useToastBarDispatch();

const avatarUrl = useUserStore((state) => state.avatarUrl);
const headerTitle = useMessageStore((state) => state.headerTitle);
const filtered = useMessageStore((state) => state.filtered);
const setMessages = useMessageStore((state) => state.setMessages);
const setFilter = useMessageStore((state) => state.setFilter);
const isThreadOpen = useMessageStore((state) => state.isThreadOpen);
const closeThread = useMessageStore((state) => state.closeThread);
const threadTitle = useMessageStore((state) => state.threadMainMessage?.msg);
const setHeaderTitle = useMessageStore((state) => state.setHeaderTitle);
const setMembersHandler = useMemberStore((state) => state.setMembersHandler);
const toggleShowMembers = useMemberStore((state) => state.toggleShowMembers);
const showMembers = useMemberStore((state) => state.showMembers);
const setShowSearch = useSearchMessageStore((state) => state.setShowSearch);
const setShowAllThreads = useThreadsMessageStore((state => state.setShowAllThreads));
const toastPosition = useToastStore((state) => state.position);


const handleLogout = useCallback(async () => {
try {
Expand All @@ -68,12 +79,14 @@ const ChatHeader = ({
const showStarredMessage = useCallback(async () => {
const { messages } = await RCInstance.getStarredMessages();
setMessages(messages);
setHeaderTitle("Starred Messages");
setFilter(true);
}, [RCInstance, setMessages, setFilter]);

const showPinnedMessage = useCallback(async () => {
const { messages } = await RCInstance.getPinnedMessages();
setMessages(messages);
setHeaderTitle("Pinned Messages");
setFilter(true);
}, [RCInstance, setMessages, setFilter]);

Expand All @@ -95,12 +108,27 @@ const ChatHeader = ({
if (showMembers) toggleShowMembers();
}, [setShowChannelinfo, setShowSearch, showMembers, toggleShowMembers]);

const showAllThreads = useCallback(async () => {
setShowAllThreads(true);
setShowSearch(false);
}, [setShowAllThreads, setShowSearch]);

useEffect(() => {
const getChannelInfo = async () => {
const res = await RCInstance.channelInfo();
if (res.success) {
setChannelInfo(res.channel);
} else {
if ('errorType' in res && res.errorType === 'error-room-not-found') {
dispatchToastMessage({
type: 'error',
message: "Channel doesn't exist. Logging out.",
position: toastPosition,
});
await RCInstance.logout();
}
}

};
if (isUserAuthenticated) {
getChannelInfo();
Expand All @@ -120,13 +148,12 @@ const ChatHeader = ({
if (moreOpts) {
options.push(
...[
// TODO
// {
// id: 'thread',
// action: function noRefCheck() {},
// label: 'Threads',
// icon: 'thread',
// },
{
id: 'thread',
action: showAllThreads,
label: 'Threads',
icon: 'thread',
},
{
id: 'members',
action: showChannelMembers,
Expand Down Expand Up @@ -290,7 +317,11 @@ const ChatHeader = ({
</Box>
</Box>
{isThreadOpen && (
<ThreadHeader title={threadTitle} handleClose={closeThread} />
<DynamicHeader title={threadTitle} isClosable={true} handleClose={closeThread} iconName='arrow-back' />
)}

{!isThreadOpen && filtered && (
<DynamicHeader title={headerTitle} iconName={headerTitle && headerTitle.includes('Pin') ? 'pin' : 'star'} />
)}
</Box>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,13 @@ const ChatInputFormattingToolbar = ({ messageRef, inputRef }) => {
input.selectionEnd = input.selectionStart + selectedText.length;
};

const popupStyle= {
margin: '0',
position: 'absolute',
left: '0.375rem',
top:'9.5rem'
};

return (
<Box
css={css`
Expand Down Expand Up @@ -90,7 +97,7 @@ const ChatInputFormattingToolbar = ({ messageRef, inputRef }) => {
closeOnEscape
disabled={isRecordingMessage}
closeOnDocumentClick
position="left center"
contentStyle={popupStyle}
>
<EmojiPicker
handleEmojiClick={(emoji) => {
Expand Down
47 changes: 47 additions & 0 deletions packages/react/src/components/DynamicHeader/DynamicHeader.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import React from 'react';
import PropTypes from 'prop-types';
import styles from './DynamicHeader.module.css';
import { Icon } from '../Icon';
import { Box } from '../Box';
import { ActionButton } from '../ActionButton';

const DynamicHeader = ({ title, isClosable = false, handleClose = () => { }, iconName }) => {
return (
<Box
className={styles.container}
style={{
paddingBlockStart: '10px',
}}
>
<Box
style={{
display: 'flex',
alignItems: 'center',
flexDirection: 'row',
gap: '0.5rem',
}}
>
{isClosable && (
<ActionButton onClick={handleClose} ghost display="inline" square small>
<Icon name={iconName} size="1.25rem" />
</ActionButton>
)}
{!isClosable && (
<div>
<Icon name={iconName} size="1.25rem" />
</div>
)}
<h4 className={styles.nospace}>{title}</h4>
</Box>
</Box>
);
};

export default DynamicHeader;

DynamicHeader.propTypes = {
handleClose: PropTypes.func,
title: PropTypes.string,
isClosable: PropTypes.bool,
iconName: PropTypes.string,
};
1 change: 1 addition & 0 deletions packages/react/src/components/DynamicHeader/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default as DynamicHeader } from './DynamicHeader';
Loading

0 comments on commit 480378a

Please sign in to comment.