Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: show date on message's scroll #31572

Merged
merged 32 commits into from
Feb 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
61f9275
feat: sticky date on MessageList (wip)
juliajforesti Jan 26, 2024
db0badd
Merge branch 'develop' into feat/date-bubble
juliajforesti Jan 29, 2024
36c3fb3
chore: bump fuselage
juliajforesti Feb 1, 2024
eea2adb
wip: RoomBody handling divider refs to render only 1
juliajforesti Feb 6, 2024
887af37
wip
juliajforesti Feb 6, 2024
d39a534
Merge branch 'develop' into feat/date-bubble
juliajforesti Feb 6, 2024
5d687c8
remove eventListener
juliajforesti Feb 6, 2024
04fa8e6
chore: code reorg
juliajforesti Feb 7, 2024
8f25756
feat: show date bubble on scroll
juliajforesti Feb 7, 2024
8e697ad
remove unused ref
juliajforesti Feb 7, 2024
dca3eba
Merge branch 'develop' into feat/date-bubble
juliajforesti Feb 7, 2024
02dbcdd
improve transition
juliajforesti Feb 7, 2024
67df13b
feat: change date divider in threads
juliajforesti Feb 7, 2024
af250e7
feat: date bubble on threads
juliajforesti Feb 7, 2024
29edb8f
feat: hook to handle bubble date on scroll
juliajforesti Feb 8, 2024
c32edf1
chore: change hook name
juliajforesti Feb 9, 2024
bd5929c
Merge branch 'develop' into feat/date-bubble
juliajforesti Feb 9, 2024
bbb3005
refactor: bring repeated code to hook
juliajforesti Feb 9, 2024
f24bf68
chore: refactor useDateScroll
ggazzo Feb 20, 2024
4c49a81
Merge branch 'develop' into feat/date-bubble
juliajforesti Feb 28, 2024
66c9cec
fix type
juliajforesti Feb 28, 2024
80b4ba6
refactor
juliajforesti Feb 29, 2024
3eaf366
refactor
juliajforesti Feb 29, 2024
fc6e1cf
refactor provider
juliajforesti Feb 29, 2024
6372a00
refactor: rename
juliajforesti Feb 29, 2024
97c0f0b
refactor: subComponent ThreadMessageItem and create `useDateRef` hook
juliajforesti Feb 29, 2024
7d29499
refactor ref
juliajforesti Feb 29, 2024
c8d810f
ops
juliajforesti Feb 29, 2024
3d74413
remove unused ref
juliajforesti Feb 29, 2024
5c9e01d
Merge branch 'develop' into feat/date-bubble
juliajforesti Feb 29, 2024
81c822e
improve fade logic effect
ggazzo Feb 29, 2024
bc87818
Create nine-ads-hide.md
ggazzo Feb 29, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/nine-ads-hide.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@rocket.chat/meteor": minor
---

feat: show date on message's scroll
76 changes: 16 additions & 60 deletions apps/meteor/client/views/room/MessageList/MessageList.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,15 @@
import type { IRoom } from '@rocket.chat/core-typings';
import { isThreadMessage } from '@rocket.chat/core-typings';
import { MessageDivider } from '@rocket.chat/fuselage';
import { useSetting, useTranslation, useUserPreference } from '@rocket.chat/ui-contexts';
import type { ReactElement, ComponentProps } from 'react';
import React, { Fragment, memo } from 'react';
import { useSetting, useUserPreference } from '@rocket.chat/ui-contexts';
import type { ComponentProps } from 'react';
import React, { Fragment, forwardRef } from 'react';

import { MessageTypes } from '../../../../app/ui-utils/client';
import RoomMessage from '../../../components/message/variants/RoomMessage';
import SystemMessage from '../../../components/message/variants/SystemMessage';
import ThreadMessagePreview from '../../../components/message/variants/ThreadMessagePreview';
import { useFormatDate } from '../../../hooks/useFormatDate';
import { useRoomSubscription } from '../contexts/RoomContext';
import { useFirstUnreadMessageId } from '../hooks/useFirstUnreadMessageId';
import { SelectedMessagesProvider } from '../providers/SelectedMessagesProvider';
import { MessageListItem } from './MessageListItem';
import { useMessages } from './hooks/useMessages';
import { isMessageNewDay } from './lib/isMessageNewDay';
import { isMessageSequential } from './lib/isMessageSequential';
import MessageListProvider from './providers/MessageListProvider';

Expand All @@ -23,77 +18,38 @@ type MessageListProps = {
scrollMessageList: ComponentProps<typeof MessageListProvider>['scrollMessageList'];
};

export const MessageList = ({ rid, scrollMessageList }: MessageListProps): ReactElement => {
const t = useTranslation();
export const MessageList = forwardRef(function MessageList({ rid, scrollMessageList }: MessageListProps) {
const messages = useMessages({ rid });
const subscription = useRoomSubscription();
const showUserAvatar = !!useUserPreference<boolean>('displayAvatars');
const messageGroupingPeriod = Number(useSetting('Message_GroupingPeriod'));
const formatDate = useFormatDate();

const firstUnreadMessageId = useFirstUnreadMessageId();

return (
<MessageListProvider scrollMessageList={scrollMessageList}>
<SelectedMessagesProvider>
{messages.map((message, index, { [index - 1]: previous }) => {
const sequential = isMessageSequential(message, previous, messageGroupingPeriod);

const newDay = isMessageNewDay(message, previous);

const showUnreadDivider = firstUnreadMessageId === message._id;

const showDivider = newDay || showUnreadDivider;

const shouldShowAsSequential = sequential && !newDay;

const system = MessageTypes.isSystemMessage(message);
const visible = !isThreadMessage(message) && !system;

const unread = Boolean(subscription?.tunread?.includes(message._id));
const mention = Boolean(subscription?.tunreadUser?.includes(message._id));
const all = Boolean(subscription?.tunreadGroup?.includes(message._id));
const ignoredUser = Boolean(subscription?.ignored?.includes(message.u._id));

return (
<Fragment key={message._id}>
{showDivider && (
<MessageDivider unreadLabel={showUnreadDivider ? t('Unread_Messages').toLowerCase() : undefined}>
{newDay && formatDate(message.ts)}
</MessageDivider>
)}

{visible && (
<RoomMessage
message={message}
showUserAvatar={showUserAvatar}
sequential={shouldShowAsSequential}
unread={unread}
mention={mention}
all={all}
ignoredUser={ignoredUser}
/>
)}

{isThreadMessage(message) && (
<ThreadMessagePreview
data-mid={message._id}
data-tmid={message.tmid}
data-unread={showUnreadDivider}
data-sequential={sequential}
sequential={shouldShowAsSequential}
message={message}
showUserAvatar={showUserAvatar}
/>
)}

{system && <SystemMessage showUserAvatar={showUserAvatar} message={message} />}
<MessageListItem
message={message}
previous={previous}
showUnreadDivider={showUnreadDivider}
showUserAvatar={showUserAvatar}
sequential={sequential}
visible={visible}
subscription={subscription}
system={system}
/>
</Fragment>
);
})}
</SelectedMessagesProvider>
</MessageListProvider>
);
};

export default memo(MessageList);
});
92 changes: 92 additions & 0 deletions apps/meteor/client/views/room/MessageList/MessageListItem.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import { isThreadMessage, type IMessage, type ISubscription } from '@rocket.chat/core-typings';
import { Box, Bubble, MessageDivider } from '@rocket.chat/fuselage';
import { useTranslation } from '@rocket.chat/ui-contexts';
import React from 'react';

import RoomMessage from '../../../components/message/variants/RoomMessage';
import SystemMessage from '../../../components/message/variants/SystemMessage';
import ThreadMessagePreview from '../../../components/message/variants/ThreadMessagePreview';
import { useFormatDate } from '../../../hooks/useFormatDate';
import { useDateRef } from '../providers/DateListProvider';
import { isMessageNewDay } from './lib/isMessageNewDay';

type MessageListItemProps = {
message: IMessage;
previous?: IMessage;
showUnreadDivider: boolean;

sequential: boolean;
showUserAvatar: boolean;
visible: boolean;
subscription: ISubscription | undefined;
system: boolean;
};
export const MessageListItem = ({
message,
previous,
showUnreadDivider,
sequential,
showUserAvatar,
visible,
subscription,
system,
}: MessageListItemProps) => {
const t = useTranslation();
const formatDate = useFormatDate();

const ref = useDateRef();

const newDay = isMessageNewDay(message, previous);
const showDivider = newDay || showUnreadDivider;
const unread = Boolean(subscription?.tunread?.includes(message._id));
const mention = Boolean(subscription?.tunreadUser?.includes(message._id));
const all = Boolean(subscription?.tunreadGroup?.includes(message._id));
const ignoredUser = Boolean(subscription?.ignored?.includes(message.u._id));
const shouldShowAsSequential = sequential && !newDay;

return (
<>
{showDivider && (
<Box
ref={ref}
data-id={message.ts}
data-time={new Date(message.ts)
.toISOString()
.replaceAll(/[-T:.]/g, '')
.substring(0, 8)}
>
<MessageDivider unreadLabel={showUnreadDivider ? t('Unread_Messages').toLowerCase() : undefined}>
{newDay && (
<Bubble small secondary>
{formatDate(message.ts)}
</Bubble>
)}
</MessageDivider>
</Box>
)}
{visible && (
<RoomMessage
message={message}
showUserAvatar={showUserAvatar}
sequential={shouldShowAsSequential}
unread={unread}
mention={mention}
all={all}
ignoredUser={ignoredUser}
/>
)}
{isThreadMessage(message) && (
<ThreadMessagePreview
data-mid={message._id}
data-tmid={message.tmid}
data-unread={showUnreadDivider}
data-sequential={sequential}
sequential={shouldShowAsSequential}
message={message}
showUserAvatar={showUserAvatar}
/>
)}
{system && <SystemMessage showUserAvatar={showUserAvatar} message={message} />}
</>
);
};
59 changes: 31 additions & 28 deletions apps/meteor/client/views/room/Room.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { useRoomToolbox } from './contexts/RoomToolboxContext';
import { useAppsContextualBar } from './hooks/useAppsContextualBar';
import RoomLayout from './layout/RoomLayout';
import ChatProvider from './providers/ChatProvider';
import { DateListProvider } from './providers/DateListProvider';
import { SelectedMessagesProvider } from './providers/SelectedMessagesProvider';

const UiKitContextualBar = lazy(() => import('./contextualBar/uikit/UiKitContextualBar'));
Expand All @@ -27,34 +28,36 @@ const Room = (): ReactElement => {
<ChatProvider>
<MessageHighlightProvider>
<FocusScope>
<RoomLayout
data-qa-rc-room={room._id}
aria-label={
room.t === 'd'
? t('Conversation_with__roomName__', { roomName: room.name })
: t('Channel__roomName__', { roomName: room.name })
}
header={<Header room={room} />}
body={<RoomBody />}
aside={
(toolbox.tab?.tabComponent && (
<ErrorBoundary fallback={null}>
<SelectedMessagesProvider>
<Suspense fallback={<ContextualbarSkeleton />}>{createElement(toolbox.tab.tabComponent)}</Suspense>
</SelectedMessagesProvider>
</ErrorBoundary>
)) ||
(contextualBarView && (
<ErrorBoundary fallback={null}>
<SelectedMessagesProvider>
<Suspense fallback={<ContextualbarSkeleton />}>
<UiKitContextualBar key={contextualBarView.id} initialView={contextualBarView} />
</Suspense>
</SelectedMessagesProvider>
</ErrorBoundary>
))
}
/>
<DateListProvider>
<RoomLayout
data-qa-rc-room={room._id}
aria-label={
room.t === 'd'
? t('Conversation_with__roomName__', { roomName: room.name })
: t('Channel__roomName__', { roomName: room.name })
}
header={<Header room={room} />}
body={<RoomBody />}
aside={
(toolbox.tab?.tabComponent && (
<ErrorBoundary fallback={null}>
<SelectedMessagesProvider>
<Suspense fallback={<ContextualbarSkeleton />}>{createElement(toolbox.tab.tabComponent)}</Suspense>
</SelectedMessagesProvider>
</ErrorBoundary>
)) ||
(contextualBarView && (
<ErrorBoundary fallback={null}>
<SelectedMessagesProvider>
<Suspense fallback={<ContextualbarSkeleton />}>
<UiKitContextualBar key={contextualBarView.id} initialView={contextualBarView} />
</Suspense>
</SelectedMessagesProvider>
</ErrorBoundary>
))
}
/>
</DateListProvider>
</FocusScope>
</MessageHighlightProvider>
</ChatProvider>
Expand Down
27 changes: 23 additions & 4 deletions apps/meteor/client/views/room/body/RoomBody.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type { IMessage, IUser } from '@rocket.chat/core-typings';
import { isEditedMessage } from '@rocket.chat/core-typings';
import { Box, Bubble } from '@rocket.chat/fuselage';
import {
usePermission,
useRole,
Expand All @@ -22,22 +23,25 @@ import { isTruthy } from '../../../../lib/isTruthy';
import { withDebouncing, withThrottling } from '../../../../lib/utils/highOrderFunctions';
import { CustomScrollbars } from '../../../components/CustomScrollbars';
import { useEmbeddedLayout } from '../../../hooks/useEmbeddedLayout';
import { useFormatDate } from '../../../hooks/useFormatDate';
import { useReactiveQuery } from '../../../hooks/useReactiveQuery';
import { RoomManager } from '../../../lib/RoomManager';
import type { Upload } from '../../../lib/chats/Upload';
import { roomCoordinator } from '../../../lib/rooms/roomCoordinator';
import { setMessageJumpQueryStringParameter } from '../../../lib/utils/setMessageJumpQueryStringParameter';
import Announcement from '../Announcement';
import { MessageList } from '../MessageList/MessageList';
import { MessageList } from '../MessageList';
import MessageListErrorBoundary from '../MessageList/MessageListErrorBoundary';
import ComposerContainer from '../composer/ComposerContainer';
import RoomComposer from '../composer/RoomComposer/RoomComposer';
import { useChat } from '../contexts/ChatContext';
import { useRoom, useRoomSubscription, useRoomMessages } from '../contexts/RoomContext';
import { useRoomToolbox } from '../contexts/RoomToolboxContext';
import { useUserCard } from '../contexts/UserCardContext';
import { useDateScroll } from '../hooks/useDateScroll';
import { useMessageListNavigation } from '../hooks/useMessageListNavigation';
import { useScrollMessageList } from '../hooks/useScrollMessageList';
import { useDateListController } from '../providers/DateListProvider';
import DropTargetOverlay from './DropTargetOverlay';
import JumpToRecentMessageButton from './JumpToRecentMessageButton';
import LeaderBar from './LeaderBar';
Expand All @@ -53,7 +57,10 @@ import { useRestoreScrollPosition } from './hooks/useRestoreScrollPosition';
import { useRetentionPolicy } from './hooks/useRetentionPolicy';
import { useUnreadMessages } from './hooks/useUnreadMessages';

const BUBBLE_OFFSET = 8;

const RoomBody = (): ReactElement => {
const formatDate = useFormatDate();
const t = useTranslation();
const isLayoutEmbedded = useEmbeddedLayout();
const room = useRoom();
Expand All @@ -62,6 +69,9 @@ const RoomBody = (): ReactElement => {
const admin = useRole('admin');
const subscription = useRoomSubscription();

const { list } = useDateListController();
const { listStyle, bubbleDate, onScroll: handleDateOnScroll, showBubble, style: bubbleDateStyle } = useDateScroll(BUBBLE_OFFSET);

const [lastMessageDate, setLastMessageDate] = useState<Date | undefined>();
const [hideLeaderHeader, setHideLeaderHeader] = useState(false);
const [hasNewMessages, setHasNewMessages] = useState(false);
Expand Down Expand Up @@ -380,12 +390,14 @@ const RoomBody = (): ReactElement => {

wrapper.addEventListener('scroll', updateUnreadCount);
wrapper.addEventListener('scroll', handleWrapperScroll);
wrapper.addEventListener('scroll', () => handleDateOnScroll(list));

return () => {
wrapper.removeEventListener('scroll', updateUnreadCount);
wrapper.removeEventListener('scroll', handleWrapperScroll);
wrapper.removeEventListener('scroll', () => handleDateOnScroll(list));
};
}, [_isAtBottom, room._id, setUnreadCount]);
}, [_isAtBottom, handleDateOnScroll, list, room._id, setUnreadCount]);

useEffect(() => {
const wrapper = wrapperRef.current;
Expand Down Expand Up @@ -540,7 +552,7 @@ const RoomBody = (): ReactElement => {
return (
<>
{!isLayoutEmbedded && room.announcement && <Announcement announcement={room.announcement} announcementDetails={undefined} />}
<div className='main-content-flex'>
<Box className={['main-content-flex', listStyle]}>
<section
className={`messages-container flex-tab-main-content ${admin ? 'admin' : ''}`}
id={`chat-window-${room._id}`}
Expand All @@ -561,6 +573,13 @@ const RoomBody = (): ReactElement => {
/>
))}
</div>
{bubbleDate && (
<Box className={[bubbleDateStyle, showBubble && 'bubble-visible']}>
<Bubble small secondary>
{formatDate(bubbleDate)}
</Bubble>
</Box>
)}
{unread && (
<UnreadMessagesIndicator
count={unread.count}
Expand Down Expand Up @@ -647,7 +666,7 @@ const RoomBody = (): ReactElement => {
</div>
</div>
</section>
</div>
</Box>
</>
);
};
Expand Down
Loading
Loading