diff --git a/src/components/middle/MessageList.scss b/src/components/middle/MessageList.scss index 116c987ff..597aa0a1f 100644 --- a/src/components/middle/MessageList.scss +++ b/src/components/middle/MessageList.scss @@ -5,7 +5,7 @@ overflow: scroll; overflow-x: hidden; overflow-y: overlay; - margin-bottom: .3125rem; + margin-bottom: .5rem; .mask-image-enabled & { mask-image: linear-gradient(to top, transparent 0, #000 0.5rem); diff --git a/src/components/middle/MessageList.tsx b/src/components/middle/MessageList.tsx index 5067d5742..4b626cb37 100644 --- a/src/components/middle/MessageList.tsx +++ b/src/components/middle/MessageList.tsx @@ -66,7 +66,8 @@ type OwnProps = { threadId: number; type: MessageListType; canPost: boolean; - onFabToggle: (show: boolean) => void; + onFabToggle: (shouldShow: boolean) => void; + onNotchToggle: (shouldShow: boolean) => void; hasTools?: boolean; }; @@ -114,6 +115,7 @@ const MessageList: FC = ({ type, hasTools, onFabToggle, + onNotchToggle, isChatLoaded, isChannelChat, canPost, @@ -540,6 +542,7 @@ const MessageList: FC = ({ isViewportNewest={isViewportNewest} firstUnreadId={firstUnreadId} onFabToggle={onFabToggle} + onNotchToggle={onNotchToggle} > {renderMessages( lang, diff --git a/src/components/middle/MessageScroll.tsx b/src/components/middle/MessageScroll.tsx index 983b743b4..89f40f4f4 100644 --- a/src/components/middle/MessageScroll.tsx +++ b/src/components/middle/MessageScroll.tsx @@ -19,6 +19,7 @@ type OwnProps = { isViewportNewest?: boolean; firstUnreadId?: number; onFabToggle: AnyToVoidFunction; + onNotchToggle: AnyToVoidFunction; children: any; }; @@ -38,6 +39,7 @@ const MessageScroll: FC = ({ isViewportNewest, firstUnreadId, onFabToggle, + onNotchToggle, children, }) => { // eslint-disable-next-line no-null/no-null @@ -54,11 +56,13 @@ const MessageScroll: FC = ({ if (!messageIds || !messageIds.length) { onFabToggle(false); + onNotchToggle(false); return; } if (!isViewportNewest) { onFabToggle(true); + onNotchToggle(true); return; } @@ -68,7 +72,8 @@ const MessageScroll: FC = ({ const isAtBottom = scrollBottom === 0 || (IS_SAFARI && scrollBottom === 1); onFabToggle(firstUnreadId ? !isAtBottom : !isNearBottom); - }, [messageIds, isViewportNewest, containerRef, onFabToggle, firstUnreadId]); + onNotchToggle(!isAtBottom); + }, [messageIds, isViewportNewest, containerRef, onFabToggle, firstUnreadId, onNotchToggle]); const { observe: observeIntersection, @@ -114,6 +119,16 @@ const MessageScroll: FC = ({ useOnIntersect(fabTriggerRef, observeIntersectionForFab); + const { + observe: observeIntersectionForNotch, + } = useIntersectionObserver({ + rootRef: containerRef, + }, () => { + updateFabVisibility(); + }); + + useOnIntersect(fabTriggerRef, observeIntersectionForNotch); + // Do not load more and show FAB when focusing useOnChange(() => { if (focusingId) { diff --git a/src/components/middle/MiddleColumn.scss b/src/components/middle/MiddleColumn.scss index d244b3116..fe8116837 100644 --- a/src/components/middle/MiddleColumn.scss +++ b/src/components/middle/MiddleColumn.scss @@ -255,6 +255,27 @@ transform: translate3d(0, calc(-1 * (var(--symbol-menu-height) + var(--symbol-menu-footer-height))), 0); } } + + &::before { + content: ""; + position: absolute; + top: -0.5rem; + left: 0; + right: 0; + height: 1px; + background: linear-gradient(90deg, rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 0.4) 2%, rgba(255, 255, 255, 0.4) 98%, rgba(255, 255, 255, 0) 100%); + opacity: 0; + transition: opacity 200ms ease; + + @media (max-width: 600px) { + left: -2%; + right: -2%; + } + } + + &.with-notch::before { + opacity: 1; + } } } diff --git a/src/components/middle/MiddleColumn.tsx b/src/components/middle/MiddleColumn.tsx index bc9424f69..9a68e57e1 100644 --- a/src/components/middle/MiddleColumn.tsx +++ b/src/components/middle/MiddleColumn.tsx @@ -103,6 +103,7 @@ const MiddleColumn: FC = ({ const [dropAreaState, setDropAreaState] = useState(DropAreaState.None); const [isFabShown, setIsFabShown] = useState(); + const [isNotchShown, setIsNotchShown] = useState(); const [isUnpinModalOpen, setIsUnpinModalOpen] = useState(false); const hasTools = hasPinnedOrAudioMessage && ( @@ -134,6 +135,7 @@ const MiddleColumn: FC = ({ useEffect(() => { setDropAreaState(DropAreaState.None); setIsFabShown(undefined); + setIsNotchShown(undefined); }, [chatId]); useEffect(() => { @@ -198,6 +200,12 @@ const MiddleColumn: FC = ({ const lang = useLang(); + const footerClassName = buildClassName( + 'middle-column-footer', + !renderingCanPost && 'no-composer', + renderingCanPost && isNotchShown && 'with-notch', + ); + return (
= ({ canPost={renderingCanPost} hasTools={renderingHasTools} onFabToggle={setIsFabShown} + onNotchToggle={setIsNotchShown} /> -
+
{renderingCanPost && (