fix(chat): Keep message list scroll stuck at the bottom when either reply preview or edit warning are open
This commit is contained in:
parent
f0636ca40b
commit
c2de62e065
@ -12,10 +12,6 @@ export const Container = styled.div`
|
||||
align-items: center;
|
||||
flex-wrap: nowrap;
|
||||
color: ${colorGrayLight};
|
||||
position: absolute;
|
||||
right: 0;
|
||||
left: 0;
|
||||
bottom: 100%;
|
||||
z-index: 10;
|
||||
background-color: ${colorWhite};
|
||||
|
||||
|
@ -189,12 +189,22 @@ const ChatMessageList: React.FC<ChatListProps> = ({
|
||||
parentRefProxy: messageListContainerRefProxy,
|
||||
} = useIntersectionObserver(messageListContainerRef, sentinelRef);
|
||||
const {
|
||||
startObserving,
|
||||
stopObserving,
|
||||
startObserving: startObservingMessageListStickyScroll,
|
||||
stopObserving: stopObservingMessageListStickyScroll,
|
||||
} = useStickyScroll(currentMessageListContainer, currentMessageList);
|
||||
const {
|
||||
startObserving: startObservingMessageListContainerStickyScroll,
|
||||
stopObserving: stopObservingMessageListContainerStickyScroll,
|
||||
} = useStickyScroll(currentMessageListContainer, currentMessageListContainer);
|
||||
|
||||
useEffect(() => {
|
||||
if (isSentinelVisible) startObserving(); else stopObserving();
|
||||
if (isSentinelVisible) {
|
||||
startObservingMessageListStickyScroll();
|
||||
startObservingMessageListContainerStickyScroll();
|
||||
} else {
|
||||
stopObservingMessageListStickyScroll();
|
||||
stopObservingMessageListContainerStickyScroll();
|
||||
}
|
||||
toggleFollowingTail(isSentinelVisible);
|
||||
}, [isSentinelVisible]);
|
||||
|
||||
|
@ -3,7 +3,6 @@ import { defineMessages, useIntl } from 'react-intl';
|
||||
import Styled, { DeleteMessage } from './styles';
|
||||
import Storage from '/imports/ui/services/storage/in-memory';
|
||||
import { ChatEvents } from '/imports/ui/core/enums/chat';
|
||||
import ChatMessageTextContent from '../message-content/text-content/component';
|
||||
|
||||
const intlMessages = defineMessages({
|
||||
deleteMessage: {
|
||||
@ -25,6 +24,7 @@ const ChatMessageReplied: React.FC<MessageRepliedProps> = (props) => {
|
||||
} = props;
|
||||
|
||||
const intl = useIntl();
|
||||
const messageChunks = message.split('\n');
|
||||
|
||||
return (
|
||||
<Styled.Container
|
||||
@ -42,11 +42,9 @@ const ChatMessageReplied: React.FC<MessageRepliedProps> = (props) => {
|
||||
>
|
||||
{!deletedByUser && (
|
||||
<Styled.Message>
|
||||
<ChatMessageTextContent
|
||||
text={message}
|
||||
emphasizedMessage={emphasizedMessage}
|
||||
dataTest={null}
|
||||
/>
|
||||
<Styled.Markdown $emphasizedMessage={emphasizedMessage}>
|
||||
{messageChunks[0]}
|
||||
</Styled.Markdown>
|
||||
</Styled.Message>
|
||||
)}
|
||||
{deletedByUser && (
|
||||
|
@ -1,16 +1,17 @@
|
||||
import styled from 'styled-components';
|
||||
import {
|
||||
colorGrayLightest, colorPrimary, colorText,
|
||||
colorWhite,
|
||||
colorGrayLight,
|
||||
colorGrayLightest, colorPrimary, colorText, colorWhite,
|
||||
} from '/imports/ui/stylesheets/styled-components/palette';
|
||||
import { $3xlPadding, lgPadding } from '/imports/ui/stylesheets/styled-components/general';
|
||||
import { $3xlPadding, smPadding } from '/imports/ui/stylesheets/styled-components/general';
|
||||
import ReactMarkdown from 'react-markdown';
|
||||
|
||||
const Container = styled.div`
|
||||
border-top-left-radius: 0.5rem;
|
||||
border-top-right-radius: 0.5rem;
|
||||
background-color: ${colorWhite};
|
||||
box-shadow: inset 0 0 0 1px ${colorGrayLightest};
|
||||
padding: ${lgPadding} ${$3xlPadding};
|
||||
padding: ${smPadding} ${$3xlPadding};
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
cursor: pointer;
|
||||
@ -24,34 +25,49 @@ const Container = styled.div`
|
||||
}
|
||||
`;
|
||||
|
||||
const Typography = styled.div`
|
||||
overflow: hidden;
|
||||
`;
|
||||
|
||||
const Username = styled(Typography)`
|
||||
font-weight: bold;
|
||||
color: ${colorPrimary};
|
||||
line-height: 1rem;
|
||||
font-size: 1rem;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
`;
|
||||
|
||||
const Message = styled(Typography)`
|
||||
max-height: 1rem;
|
||||
line-height: 1rem;
|
||||
const Message = styled.div`
|
||||
line-height: normal;
|
||||
overflow: hidden;
|
||||
`;
|
||||
|
||||
export const DeleteMessage = styled.span`
|
||||
font-style: italic;
|
||||
font-weight: bold;
|
||||
color: ${colorGrayLight};
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
`;
|
||||
|
||||
export const Markdown = styled(ReactMarkdown)<{
|
||||
$emphasizedMessage: boolean;
|
||||
}>`
|
||||
color: ${colorText};
|
||||
|
||||
${({ $emphasizedMessage }) => $emphasizedMessage && `
|
||||
font-weight: bold;
|
||||
`}
|
||||
|
||||
& img {
|
||||
max-width: 100%;
|
||||
max-height: 100%;
|
||||
}
|
||||
|
||||
& p {
|
||||
margin: 0;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
& code {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
`;
|
||||
|
||||
export default {
|
||||
Container,
|
||||
Username,
|
||||
Message,
|
||||
DeleteMessage,
|
||||
Markdown,
|
||||
};
|
||||
|
@ -3,7 +3,6 @@ import Styled from './styles';
|
||||
import useSettings from '/imports/ui/services/settings/hooks/useSettings';
|
||||
import { SETTINGS } from '/imports/ui/services/settings/enums';
|
||||
import { ChatEvents } from '/imports/ui/core/enums/chat';
|
||||
import ChatMessageTextContent from '../chat-message-list/page/chat-message/message-content/text-content/component';
|
||||
import Storage from '/imports/ui/services/storage/in-memory';
|
||||
|
||||
const ChatReplyIntention = () => {
|
||||
@ -45,44 +44,43 @@ const ChatReplyIntention = () => {
|
||||
};
|
||||
|
||||
const hidden = !username || !message;
|
||||
const messageChunks = message ? message.split('\n') : null;
|
||||
|
||||
return (
|
||||
<Styled.Root>
|
||||
<Styled.Container
|
||||
$hidden={hidden}
|
||||
$animations={animations}
|
||||
onClick={() => {
|
||||
<Styled.Container
|
||||
$hidden={hidden}
|
||||
$animations={animations}
|
||||
onClick={() => {
|
||||
window.dispatchEvent(
|
||||
new CustomEvent(ChatEvents.CHAT_FOCUS_MESSAGE_REQUEST, {
|
||||
detail: {
|
||||
sequence,
|
||||
},
|
||||
}),
|
||||
);
|
||||
Storage.removeItem(ChatEvents.CHAT_FOCUS_MESSAGE_REQUEST);
|
||||
if (sequence) Storage.setItem(ChatEvents.CHAT_FOCUS_MESSAGE_REQUEST, sequence);
|
||||
}}
|
||||
>
|
||||
<Styled.Message>
|
||||
<Styled.Markdown
|
||||
$emphasizedMessage={!!emphasizedMessage}
|
||||
>
|
||||
{messageChunks ? messageChunks[0] : ''}
|
||||
</Styled.Markdown>
|
||||
</Styled.Message>
|
||||
<Styled.CloseBtn
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
window.dispatchEvent(
|
||||
new CustomEvent(ChatEvents.CHAT_FOCUS_MESSAGE_REQUEST, {
|
||||
detail: {
|
||||
sequence,
|
||||
},
|
||||
}),
|
||||
new CustomEvent(ChatEvents.CHAT_CANCEL_REPLY_INTENTION),
|
||||
);
|
||||
Storage.removeItem(ChatEvents.CHAT_FOCUS_MESSAGE_REQUEST);
|
||||
if (sequence) Storage.setItem(ChatEvents.CHAT_FOCUS_MESSAGE_REQUEST, sequence);
|
||||
}}
|
||||
>
|
||||
<Styled.Message>
|
||||
<ChatMessageTextContent
|
||||
text={message || ''}
|
||||
emphasizedMessage={!!emphasizedMessage}
|
||||
dataTest={null}
|
||||
/>
|
||||
</Styled.Message>
|
||||
<Styled.CloseBtn
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
window.dispatchEvent(
|
||||
new CustomEvent(ChatEvents.CHAT_CANCEL_REPLY_INTENTION),
|
||||
);
|
||||
}}
|
||||
icon="close"
|
||||
tabIndex={hidden ? -1 : 0}
|
||||
aria-hidden={hidden}
|
||||
/>
|
||||
</Styled.Container>
|
||||
</Styled.Root>
|
||||
icon="close"
|
||||
tabIndex={hidden ? -1 : 0}
|
||||
aria-hidden={hidden}
|
||||
/>
|
||||
</Styled.Container>
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -1,7 +1,9 @@
|
||||
import styled, { css } from 'styled-components';
|
||||
import ReactMarkdown from 'react-markdown';
|
||||
import {
|
||||
colorGrayLightest,
|
||||
colorPrimary,
|
||||
colorText,
|
||||
colorWhite,
|
||||
} from '/imports/ui/stylesheets/styled-components/palette';
|
||||
import {
|
||||
@ -12,14 +14,10 @@ import EmojiButton from '../chat-message-list/page/chat-message/message-toolbar/
|
||||
const Container = styled.div<{ $hidden: boolean; $animations: boolean }>`
|
||||
border-radius: 0.375rem;
|
||||
background-color: ${colorWhite};
|
||||
position: absolute;
|
||||
right: 0;
|
||||
left: 0;
|
||||
bottom: 100%;
|
||||
z-index: 10;
|
||||
overflow: hidden;
|
||||
box-shadow: inset 0 0 0 1px ${colorGrayLightest};
|
||||
display: flex;
|
||||
align-items: center;
|
||||
overflow: hidden;
|
||||
|
||||
[dir='ltr'] & {
|
||||
border-right: 0.375rem solid ${colorPrimary};
|
||||
@ -35,8 +33,8 @@ const Container = styled.div<{ $hidden: boolean; $animations: boolean }>`
|
||||
min-height: 0;
|
||||
`
|
||||
: css`
|
||||
min-height: calc(1rem + ${mdPadding} * 2);
|
||||
height: calc(1rem + ${mdPadding} * 2);
|
||||
min-height: calc(1rlh + ${mdPadding} * 2);
|
||||
height: calc(1rlh + ${mdPadding} * 2);
|
||||
padding: ${mdPadding} calc(${smPaddingX} * 1.25);
|
||||
margin-bottom: ${smPadding};
|
||||
|
||||
@ -57,20 +55,39 @@ const Container = styled.div<{ $hidden: boolean; $animations: boolean }>`
|
||||
`}
|
||||
`;
|
||||
|
||||
const Typography = styled.div`
|
||||
line-height: 1;
|
||||
font-size: 1rem;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
const Message = styled.div`
|
||||
line-height: 1rlh;
|
||||
flex-grow: 1;
|
||||
`;
|
||||
|
||||
const Message = styled(Typography)`
|
||||
font-size: 1rem;
|
||||
line-height: 1;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
flex-grow: 1;
|
||||
const Markdown = styled(ReactMarkdown)<{
|
||||
$emphasizedMessage: boolean;
|
||||
}>`
|
||||
color: ${colorText};
|
||||
|
||||
${({ $emphasizedMessage }) => $emphasizedMessage && `
|
||||
font-weight: bold;
|
||||
`}
|
||||
|
||||
& img {
|
||||
max-width: 100%;
|
||||
max-height: 100%;
|
||||
}
|
||||
|
||||
& p {
|
||||
line-height: 1rlh;
|
||||
margin: 0;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
& code {
|
||||
line-height: 1rlh;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
`;
|
||||
|
||||
const CloseBtn = styled(EmojiButton)`
|
||||
@ -79,13 +96,9 @@ const CloseBtn = styled(EmojiButton)`
|
||||
padding: 0;
|
||||
`;
|
||||
|
||||
const Root = styled.div`
|
||||
position: relative;
|
||||
`;
|
||||
|
||||
export default {
|
||||
Container,
|
||||
CloseBtn,
|
||||
Message,
|
||||
Root,
|
||||
Markdown,
|
||||
};
|
||||
|
@ -1,5 +1,5 @@
|
||||
import {
|
||||
useCallback, useEffect, useMemo, useRef,
|
||||
useCallback, useEffect, useRef,
|
||||
} from 'react';
|
||||
|
||||
interface Handlers {
|
||||
@ -10,17 +10,22 @@ interface Handlers {
|
||||
const useStickyScroll = (stickyElement: HTMLElement | null, onResizeOf: HTMLElement | null) => {
|
||||
const elHeight = useRef(0);
|
||||
const timeout = useRef<ReturnType<typeof setTimeout>>();
|
||||
const observer = useRef<ResizeObserver | null>(null);
|
||||
const handlers = useRef<Handlers>({
|
||||
startObserving: () => {},
|
||||
stopObserving: () => {},
|
||||
});
|
||||
|
||||
const observer = useMemo(
|
||||
() => new ResizeObserver((entries) => {
|
||||
useEffect(() => {
|
||||
if (observer.current) {
|
||||
observer.current.disconnect();
|
||||
}
|
||||
|
||||
observer.current = new ResizeObserver((entries) => {
|
||||
entries.forEach((entry) => {
|
||||
const { target } = entry;
|
||||
if (target instanceof HTMLElement) {
|
||||
if (target.offsetHeight > elHeight.current) {
|
||||
if (target.offsetHeight !== elHeight.current) {
|
||||
elHeight.current = target.offsetHeight;
|
||||
if (stickyElement) {
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
@ -31,26 +36,25 @@ const useStickyScroll = (stickyElement: HTMLElement | null, onResizeOf: HTMLElem
|
||||
}
|
||||
}
|
||||
});
|
||||
}),
|
||||
[],
|
||||
);
|
||||
});
|
||||
}, [stickyElement]);
|
||||
|
||||
handlers.current.startObserving = useCallback(() => {
|
||||
if (!onResizeOf) return;
|
||||
clearTimeout(timeout.current);
|
||||
observer.observe(onResizeOf);
|
||||
}, [onResizeOf]);
|
||||
observer.current?.observe(onResizeOf);
|
||||
}, [onResizeOf, observer.current]);
|
||||
|
||||
handlers.current.stopObserving = useCallback(() => {
|
||||
if (!onResizeOf) return;
|
||||
timeout.current = setTimeout(() => {
|
||||
observer.unobserve(onResizeOf);
|
||||
observer.current?.unobserve(onResizeOf);
|
||||
}, 500);
|
||||
}, [onResizeOf]);
|
||||
}, [onResizeOf, observer.current]);
|
||||
|
||||
useEffect(
|
||||
() => () => {
|
||||
observer.disconnect();
|
||||
observer.current?.disconnect();
|
||||
},
|
||||
[],
|
||||
);
|
||||
|
@ -770,7 +770,7 @@ public:
|
||||
allowedElements: ['a', 'code', 'em', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'li', 'ol', 'ul', 'p', 'strong']
|
||||
# available options for toolbar: reply, edit, delete, reactions
|
||||
# example: ['reply', 'delete']
|
||||
toolbar: []
|
||||
toolbar: ['reply', 'edit', 'delete', 'reactions']
|
||||
userReaction:
|
||||
enabled: true
|
||||
expire: 30
|
||||
|
Loading…
Reference in New Issue
Block a user