Forum Panel: Various optimizations
This commit is contained in:
parent
d65f1d99fc
commit
556c4c0b21
@ -68,7 +68,7 @@ const ArchivedChats: FC<OwnProps> = ({
|
||||
</Button>
|
||||
{shouldRenderTitle && <h3 className={titleClassNames}>{lang('ArchivedChats')}</h3>}
|
||||
</div>
|
||||
<ChatList folderType="archived" isActive={isActive} />
|
||||
<ChatList folderType="archived" isActive={isActive} isForumPanelOpen={isForumPanelOpen} />
|
||||
{shouldRenderForumPanel && (
|
||||
<ForumPanel
|
||||
isOpen={isForumPanelOpen}
|
||||
|
||||
52
src/components/left/main/AvatarBadge.tsx
Normal file
52
src/components/left/main/AvatarBadge.tsx
Normal file
@ -0,0 +1,52 @@
|
||||
import type { FC } from '../../../lib/teact/teact';
|
||||
import React, { memo } from '../../../lib/teact/teact';
|
||||
import { withGlobal } from '../../../global';
|
||||
|
||||
import type { ApiChat } from '../../../api/types';
|
||||
|
||||
import { selectIsChatMuted } from '../../../global/helpers';
|
||||
import {
|
||||
selectChat,
|
||||
selectNotifySettings,
|
||||
selectNotifyExceptions,
|
||||
selectIsForumPanelOpen,
|
||||
} from '../../../global/selectors';
|
||||
|
||||
import Badge from './Badge';
|
||||
|
||||
type OwnProps = {
|
||||
chatId: string;
|
||||
};
|
||||
|
||||
type StateProps = {
|
||||
chat?: ApiChat;
|
||||
isMuted?: boolean;
|
||||
isForumPanelActive?: boolean;
|
||||
};
|
||||
|
||||
const AvatarBadge: FC<OwnProps & StateProps> = ({
|
||||
chat,
|
||||
isMuted,
|
||||
isForumPanelActive,
|
||||
}) => {
|
||||
return chat && (
|
||||
<div className="avatar-badge-wrapper">
|
||||
<Badge chat={chat} isMuted={isMuted} shouldShowOnlyMostImportant forceHidden={!isForumPanelActive} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default memo(withGlobal<OwnProps>(
|
||||
(global, { chatId }): StateProps => {
|
||||
const chat = selectChat(global, chatId);
|
||||
if (!chat) {
|
||||
return {};
|
||||
}
|
||||
|
||||
return {
|
||||
chat,
|
||||
isMuted: selectIsChatMuted(chat, selectNotifySettings(global), selectNotifyExceptions(global)),
|
||||
isForumPanelActive: selectIsForumPanelOpen(global),
|
||||
};
|
||||
},
|
||||
)(AvatarBadge));
|
||||
@ -15,12 +15,17 @@
|
||||
|
||||
&.animate-opacity {
|
||||
will-change: opacity;
|
||||
transition: opacity 250ms ease;
|
||||
transition: opacity 0.2s ease-out;
|
||||
}
|
||||
|
||||
&.animate-transform {
|
||||
will-change: transform;
|
||||
transition: transform 250ms ease;
|
||||
transition: transform 0.2s ease-out;
|
||||
}
|
||||
|
||||
&.animate-collapse {
|
||||
will-change: transform;
|
||||
transition: transform var(--layer-transition);
|
||||
}
|
||||
|
||||
&:hover,
|
||||
@ -29,7 +34,7 @@
|
||||
border-color: var(--color-chat-hover);
|
||||
}
|
||||
|
||||
.status-badge-wrapper {
|
||||
.avatar-badge-wrapper {
|
||||
--outline-color: var(--color-chat-hover);
|
||||
}
|
||||
|
||||
@ -38,6 +43,16 @@
|
||||
}
|
||||
}
|
||||
|
||||
// Super specific selector to override the same in `ListItem`
|
||||
@media (min-width: 600px) {
|
||||
&:not(.has-ripple):not(.is-static),
|
||||
body.animation-level-0 & {
|
||||
.ListItem-button:active {
|
||||
--background-color: var(--color-chat-hover) !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&:last-of-type {
|
||||
padding-bottom: 0.5rem;
|
||||
}
|
||||
@ -55,17 +70,17 @@
|
||||
}
|
||||
}
|
||||
|
||||
&.active-forum {
|
||||
.status-badge-wrapper {
|
||||
&.selected-forum {
|
||||
.avatar-badge-wrapper {
|
||||
--outline-color: var(--color-chat-hover);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 600px) {
|
||||
&.active-forum.forum,
|
||||
&.active-forum.forum:hover {
|
||||
.status-badge-wrapper {
|
||||
&.selected-forum.forum,
|
||||
&.selected-forum.forum:hover {
|
||||
.avatar-badge-wrapper {
|
||||
--outline-color: var(--color-chat-hover);
|
||||
}
|
||||
}
|
||||
@ -111,7 +126,6 @@
|
||||
}
|
||||
|
||||
.Badge:not(.pinned) {
|
||||
background: var(--color-white);
|
||||
color: var(--color-chat-active);
|
||||
}
|
||||
|
||||
@ -120,35 +134,18 @@
|
||||
background: #FFFFFF33;
|
||||
}
|
||||
|
||||
.status-badge-wrapper-visible .Badge:not(.pinned).muted {
|
||||
background: var(--color-chat-active-greyed);
|
||||
.avatar-badge-wrapper .Badge:not(.pinned) {
|
||||
--outline-color: transparent;
|
||||
}
|
||||
|
||||
.status-badge-wrapper-visible .Badge:not(.pinned):not(.muted) {
|
||||
--outline-color: transparent;
|
||||
.avatar-badge-wrapper .Badge:not(.pinned).muted {
|
||||
background: var(--color-gray);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.smaller .ListItem-button {
|
||||
height: 4.5rem;
|
||||
}
|
||||
|
||||
&.active-forum::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: -0.5rem;
|
||||
width: 0.375rem;
|
||||
height: 75%;
|
||||
transform: translateY(-50%);
|
||||
|
||||
background: var(--color-primary);
|
||||
z-index: 1;
|
||||
|
||||
border-start-end-radius: var(--border-radius-default);
|
||||
border-end-end-radius: var(--border-radius-default);
|
||||
&.selected-forum {
|
||||
--background-color: var(--color-chat-hover) !important;
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
@ -169,7 +166,7 @@
|
||||
background: var(--background-color);
|
||||
}
|
||||
|
||||
.status-badge-wrapper {
|
||||
.avatar-badge-wrapper {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
right: 0.5rem;
|
||||
@ -182,12 +179,12 @@
|
||||
}
|
||||
|
||||
.Badge-transition {
|
||||
transition: opacity 250ms ease, transform 250ms ease; // Same as Forum Panel
|
||||
transition: opacity var(--layer-transition), transform var(--layer-transition);
|
||||
}
|
||||
}
|
||||
|
||||
.info {
|
||||
transition: opacity 250ms ease, transform 250ms ease; // Same as Forum Panel
|
||||
transition: opacity 300ms ease, transform var(--layer-transition);
|
||||
|
||||
.subtitle {
|
||||
margin-top: -0.125rem;
|
||||
@ -316,19 +313,5 @@
|
||||
unicode-bidi: plaintext;
|
||||
}
|
||||
}
|
||||
|
||||
&.active-forum::before {
|
||||
left: auto;
|
||||
right: 0.0625rem;
|
||||
}
|
||||
}
|
||||
|
||||
&.smaller .info {
|
||||
transform: translateX(-25%);
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
&[dir="rtl"].smaller .info {
|
||||
transform: translateX(25%);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,7 +1,5 @@
|
||||
import type { FC } from '../../../lib/teact/teact';
|
||||
import React, {
|
||||
memo, useCallback, useEffect, useLayoutEffect, useRef,
|
||||
} from '../../../lib/teact/teact';
|
||||
import React, { memo, useCallback, useEffect } from '../../../lib/teact/teact';
|
||||
import { getActions, withGlobal } from '../../../global';
|
||||
|
||||
import type { ObserveFn } from '../../../hooks/useIntersectionObserver';
|
||||
@ -18,7 +16,6 @@ import type {
|
||||
import type { AnimationLevel } from '../../../types';
|
||||
import type { ChatAnimationTypes } from './hooks';
|
||||
|
||||
import { ANIMATION_END_DELAY } from '../../../config';
|
||||
import { MAIN_THREAD_ID } from '../../../api/types';
|
||||
import { IS_SINGLE_COLUMN_LAYOUT } from '../../../util/environment';
|
||||
import {
|
||||
@ -40,40 +37,35 @@ import {
|
||||
selectIsDefaultEmojiStatusPack,
|
||||
selectTopicFromMessage,
|
||||
selectThreadParam,
|
||||
selectIsForumPanelOpen,
|
||||
} from '../../../global/selectors';
|
||||
import buildClassName from '../../../util/buildClassName';
|
||||
import { fastRaf } from '../../../util/schedulers';
|
||||
import buildStyle from '../../../util/buildStyle';
|
||||
|
||||
import useChatContextActions from '../../../hooks/useChatContextActions';
|
||||
import useFlag from '../../../hooks/useFlag';
|
||||
import useChatListEntry from './hooks/useChatListEntry';
|
||||
import { useIsIntersecting } from '../../../hooks/useIntersectionObserver';
|
||||
import usePrevious from '../../../hooks/usePrevious';
|
||||
|
||||
import ListItem from '../../ui/ListItem';
|
||||
import Avatar from '../../common/Avatar';
|
||||
import LastMessageMeta from '../../common/LastMessageMeta';
|
||||
import DeleteChatModal from '../../common/DeleteChatModal';
|
||||
import ListItem from '../../ui/ListItem';
|
||||
import Badge from './Badge';
|
||||
import ChatFolderModal from '../ChatFolderModal.async';
|
||||
import ChatCallStatus from './ChatCallStatus';
|
||||
import ReportModal from '../../common/ReportModal';
|
||||
import FullNameTitle from '../../common/FullNameTitle';
|
||||
import ChatFolderModal from '../ChatFolderModal.async';
|
||||
import ChatCallStatus from './ChatCallStatus';
|
||||
import Badge from './Badge';
|
||||
import AvatarBadge from './AvatarBadge';
|
||||
|
||||
import './Chat.scss';
|
||||
|
||||
const TRANSFORM_TO_TOPIC_LIST_ANIMATION_DELAY = 300;
|
||||
|
||||
type OwnProps = {
|
||||
chatId: string;
|
||||
folderId?: number;
|
||||
orderDiff: number;
|
||||
animationType: ChatAnimationTypes;
|
||||
isPinned?: boolean;
|
||||
offsetTopInSmallerMode: number;
|
||||
offsetTop: number;
|
||||
offsetCollapseDelta: number;
|
||||
observeIntersection?: ObserveFn;
|
||||
onDragEnter?: (chatId: string) => void;
|
||||
};
|
||||
@ -92,13 +84,12 @@ type StateProps = {
|
||||
draft?: ApiFormattedText;
|
||||
animationLevel?: AnimationLevel;
|
||||
isSelected?: boolean;
|
||||
isForumPanelActive?: boolean;
|
||||
isSelectedForum?: boolean;
|
||||
canScrollDown?: boolean;
|
||||
canChangeFolder?: boolean;
|
||||
lastSyncTime?: number;
|
||||
lastMessageTopic?: ApiTopic;
|
||||
typingStatus?: ApiTypingStatus;
|
||||
forumPanelChatId?: string;
|
||||
};
|
||||
|
||||
const Chat: FC<OwnProps & StateProps> = ({
|
||||
@ -118,24 +109,22 @@ const Chat: FC<OwnProps & StateProps> = ({
|
||||
lastMessageOutgoingStatus,
|
||||
actionTargetMessage,
|
||||
actionTargetChatId,
|
||||
offsetTopInSmallerMode,
|
||||
offsetTop,
|
||||
offsetCollapseDelta,
|
||||
draft,
|
||||
animationLevel,
|
||||
isSelected,
|
||||
isForumPanelActive,
|
||||
isSelectedForum,
|
||||
canScrollDown,
|
||||
canChangeFolder,
|
||||
lastSyncTime,
|
||||
lastMessageTopic,
|
||||
typingStatus,
|
||||
forumPanelChatId,
|
||||
onDragEnter,
|
||||
}) => {
|
||||
const {
|
||||
openChat,
|
||||
openForumPanel,
|
||||
closeForumPanel,
|
||||
focusLastMessage,
|
||||
loadTopics,
|
||||
} = getActions();
|
||||
@ -161,27 +150,24 @@ const Chat: FC<OwnProps & StateProps> = ({
|
||||
lastMessageTopic,
|
||||
lastMessageSender,
|
||||
observeIntersection,
|
||||
|
||||
animationType,
|
||||
animationLevel,
|
||||
orderDiff,
|
||||
});
|
||||
|
||||
const handleClick = useCallback(() => {
|
||||
if (chat?.isForum) {
|
||||
if (isForum) {
|
||||
openForumPanel({ chatId });
|
||||
return;
|
||||
}
|
||||
|
||||
if (forumPanelChatId) closeForumPanel();
|
||||
openChat({ id: chatId, shouldReplaceHistory: true }, { forceOnHeavyAnimation: true });
|
||||
|
||||
if (isSelected && canScrollDown) {
|
||||
focusLastMessage();
|
||||
}
|
||||
}, [
|
||||
chat?.isForum, forumPanelChatId, closeForumPanel, openChat, chatId, isSelected, canScrollDown, openForumPanel,
|
||||
focusLastMessage,
|
||||
isForum, openChat, chatId, isSelected, canScrollDown, openForumPanel, focusLastMessage,
|
||||
]);
|
||||
|
||||
const handleDragEnter = useCallback((e) => {
|
||||
@ -225,31 +211,6 @@ const Chat: FC<OwnProps & StateProps> = ({
|
||||
}
|
||||
}, [chat, chatId, isForum, isIntersecting, lastSyncTime, loadTopics]);
|
||||
|
||||
const isOnForumPanel = chatId === forumPanelChatId;
|
||||
const prevIsForumPanelActive = usePrevious(isForumPanelActive);
|
||||
const isAnimatingRef = useRef(false);
|
||||
|
||||
if (prevIsForumPanelActive !== isForumPanelActive) {
|
||||
isAnimatingRef.current = true;
|
||||
}
|
||||
|
||||
// Animate changing to smaller chat size when navigating to/from forum topic list
|
||||
useLayoutEffect(() => {
|
||||
const current = ref.current;
|
||||
|
||||
if (current && isAnimatingRef.current && isForumPanelActive !== prevIsForumPanelActive) {
|
||||
current.classList.add('animate-transform');
|
||||
current.style.transform = '';
|
||||
setTimeout(() => {
|
||||
// Wait one more frame for better animation performance
|
||||
fastRaf(() => {
|
||||
isAnimatingRef.current = false;
|
||||
current.classList.remove('animate-transform');
|
||||
});
|
||||
}, TRANSFORM_TO_TOPIC_LIST_ANIMATION_DELAY + ANIMATION_END_DELAY);
|
||||
}
|
||||
}, [ref, isForumPanelActive, prevIsForumPanelActive]);
|
||||
|
||||
if (!chat) {
|
||||
return undefined;
|
||||
}
|
||||
@ -259,23 +220,20 @@ const Chat: FC<OwnProps & StateProps> = ({
|
||||
isUserId(chatId) ? 'private' : 'group',
|
||||
isForum && 'forum',
|
||||
isSelected && 'selected',
|
||||
isForumPanelActive && 'smaller',
|
||||
isOnForumPanel && 'active-forum',
|
||||
isSelectedForum && 'selected-forum',
|
||||
);
|
||||
|
||||
const chatTop = isForumPanelActive ? (offsetTop - offsetTopInSmallerMode) : offsetTop;
|
||||
const offsetAnimate = isForumPanelActive ? offsetTopInSmallerMode : -offsetTopInSmallerMode;
|
||||
|
||||
return (
|
||||
<ListItem
|
||||
ref={ref}
|
||||
className={className}
|
||||
style={buildStyle(`top: ${chatTop}px`, isAnimatingRef.current && `transform: translateY(${offsetAnimate}px)`)}
|
||||
style={`top: ${offsetTop}px`}
|
||||
ripple={!isForum && !IS_SINGLE_COLUMN_LAYOUT}
|
||||
contextActions={contextActions}
|
||||
onClick={handleClick}
|
||||
onDragEnter={handleDragEnter}
|
||||
shouldUsePortalForMenu={isForumPanelActive}
|
||||
offsetCollapseDelta={offsetCollapseDelta}
|
||||
withPortalForMenu
|
||||
>
|
||||
<div className="status">
|
||||
<Avatar
|
||||
@ -288,9 +246,7 @@ const Chat: FC<OwnProps & StateProps> = ({
|
||||
withVideo
|
||||
observeIntersection={observeIntersection}
|
||||
/>
|
||||
<div className="status-badge-wrapper">
|
||||
<Badge chat={chat} isMuted={isMuted} shouldShowOnlyMostImportant forceHidden={!isForumPanelActive} />
|
||||
</div>
|
||||
<AvatarBadge chatId={chatId} />
|
||||
{chat.isCallActive && chat.isCallNotEmpty && (
|
||||
<ChatCallStatus isSelected={isSelected} isActive={animationLevel !== 0} />
|
||||
)}
|
||||
@ -368,8 +324,8 @@ export default memo(withGlobal<OwnProps>(
|
||||
threadId: currentThreadId,
|
||||
type: messageListType,
|
||||
} = selectCurrentMessageList(global) || {};
|
||||
const isForumPanelActive = selectIsForumPanelOpen(global);
|
||||
const isSelected = chatId === currentChatId && currentThreadId === MAIN_THREAD_ID;
|
||||
const isSelectedForum = chatId === global.forumPanelChatId;
|
||||
|
||||
const user = privateChatUserId ? selectUser(global, privateChatUserId) : undefined;
|
||||
const userStatus = privateChatUserId ? selectUserStatus(global, privateChatUserId) : undefined;
|
||||
@ -388,8 +344,8 @@ export default memo(withGlobal<OwnProps>(
|
||||
actionTargetMessage,
|
||||
draft: selectDraft(global, chatId, MAIN_THREAD_ID),
|
||||
animationLevel: global.settings.byKey.animationLevel,
|
||||
isForumPanelActive,
|
||||
isSelected,
|
||||
isSelectedForum,
|
||||
canScrollDown: isSelected && messageListType === 'thread',
|
||||
canChangeFolder: (global.chatFolders.orderedIds?.length || 0) > 1,
|
||||
lastSyncTime: global.lastSyncTime,
|
||||
@ -401,7 +357,6 @@ export default memo(withGlobal<OwnProps>(
|
||||
isEmojiStatusColored,
|
||||
lastMessageTopic,
|
||||
typingStatus,
|
||||
forumPanelChatId: global.forumPanelChatId,
|
||||
};
|
||||
},
|
||||
)(Chat));
|
||||
|
||||
@ -22,6 +22,7 @@ import { useFolderManagerForUnreadCounters } from '../../../hooks/useFolderManag
|
||||
import Transition from '../../ui/Transition';
|
||||
import TabList from '../../ui/TabList';
|
||||
import ChatList from './ChatList';
|
||||
import { selectIsForumPanelOpen } from '../../../global/selectors';
|
||||
|
||||
type OwnProps = {
|
||||
onScreenSelect: (screen: SettingsScreens) => void;
|
||||
@ -34,6 +35,7 @@ type StateProps = {
|
||||
orderedFolderIds?: number[];
|
||||
activeChatFolder: number;
|
||||
currentUserId?: string;
|
||||
isForumPanelOpen?: boolean;
|
||||
lastSyncTime?: number;
|
||||
shouldSkipHistoryAnimations?: boolean;
|
||||
maxFolders: number;
|
||||
@ -49,6 +51,7 @@ const ChatFolders: FC<OwnProps & StateProps> = ({
|
||||
orderedFolderIds,
|
||||
activeChatFolder,
|
||||
currentUserId,
|
||||
isForumPanelOpen,
|
||||
lastSyncTime,
|
||||
shouldSkipHistoryAnimations,
|
||||
maxFolders,
|
||||
@ -195,27 +198,17 @@ const ChatFolders: FC<OwnProps & StateProps> = ({
|
||||
function renderCurrentTab(isActive: boolean) {
|
||||
const activeFolder = Object.values(chatFoldersById)
|
||||
.find(({ id }) => id === folderTabs![activeChatFolder].id);
|
||||
|
||||
if (!activeFolder || isInAllChatsFolder) {
|
||||
return (
|
||||
<ChatList
|
||||
folderType="all"
|
||||
isActive={isActive}
|
||||
lastSyncTime={lastSyncTime}
|
||||
foldersDispatch={foldersDispatch}
|
||||
onScreenSelect={onScreenSelect}
|
||||
/>
|
||||
);
|
||||
}
|
||||
const isFolder = activeFolder && !isInAllChatsFolder;
|
||||
|
||||
return (
|
||||
<ChatList
|
||||
folderType="folder"
|
||||
folderId={activeFolder.id}
|
||||
folderType={isFolder ? 'folder' : 'all'}
|
||||
folderId={isFolder ? activeFolder.id : undefined}
|
||||
isActive={isActive}
|
||||
isForumPanelOpen={isForumPanelOpen}
|
||||
lastSyncTime={lastSyncTime}
|
||||
onScreenSelect={onScreenSelect}
|
||||
foldersDispatch={foldersDispatch}
|
||||
onScreenSelect={onScreenSelect}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@ -259,16 +252,15 @@ export default memo(withGlobal<OwnProps>(
|
||||
shouldSkipHistoryAnimations,
|
||||
} = global;
|
||||
|
||||
const maxFolders = selectCurrentLimit(global, 'dialogFilters');
|
||||
|
||||
return {
|
||||
chatFoldersById,
|
||||
orderedFolderIds,
|
||||
activeChatFolder,
|
||||
currentUserId,
|
||||
isForumPanelOpen: selectIsForumPanelOpen(global),
|
||||
lastSyncTime,
|
||||
shouldSkipHistoryAnimations,
|
||||
maxFolders,
|
||||
maxFolders: selectCurrentLimit(global, 'dialogFilters'),
|
||||
};
|
||||
},
|
||||
)(ChatFolders));
|
||||
|
||||
@ -16,6 +16,7 @@ import {
|
||||
import { IS_MAC_OS, IS_PWA } from '../../../util/environment';
|
||||
import { getPinnedChatsCount, getOrderKey } from '../../../util/folderManager';
|
||||
import { selectChat } from '../../../global/selectors';
|
||||
import buildClassName from '../../../util/buildClassName';
|
||||
|
||||
import useInfiniteScroll from '../../../hooks/useInfiniteScroll';
|
||||
import { useFolderManagerForOrderedIds } from '../../../hooks/useFolderManager';
|
||||
@ -28,11 +29,13 @@ import InfiniteScroll from '../../ui/InfiniteScroll';
|
||||
import Loading from '../../ui/Loading';
|
||||
import Chat from './Chat';
|
||||
import EmptyFolder from './EmptyFolder';
|
||||
import useCollapseWithForumPanel from './hooks/useCollapseWithForumPanel';
|
||||
|
||||
type OwnProps = {
|
||||
folderType: 'all' | 'archived' | 'folder';
|
||||
folderId?: number;
|
||||
isActive: boolean;
|
||||
isForumPanelOpen?: boolean;
|
||||
lastSyncTime?: number;
|
||||
foldersDispatch?: FolderEditDispatch;
|
||||
onScreenSelect?: (screen: SettingsScreens) => void;
|
||||
@ -45,6 +48,7 @@ const ChatList: FC<OwnProps> = ({
|
||||
folderType,
|
||||
folderId,
|
||||
isActive,
|
||||
isForumPanelOpen,
|
||||
foldersDispatch,
|
||||
onScreenSelect,
|
||||
}) => {
|
||||
@ -105,6 +109,8 @@ const ChatList: FC<OwnProps> = ({
|
||||
throttleMs: INTERSECTION_THROTTLE,
|
||||
});
|
||||
|
||||
useCollapseWithForumPanel(containerRef, isForumPanelOpen);
|
||||
|
||||
const handleDragEnter = useDebouncedCallback((chatId: string) => {
|
||||
if (shouldIgnoreDragRef.current) {
|
||||
shouldIgnoreDragRef.current = false;
|
||||
@ -143,8 +149,9 @@ const ChatList: FC<OwnProps> = ({
|
||||
|
||||
return viewportIds!.map((id, i) => {
|
||||
const isPinned = viewportOffset + i < pinnedCount;
|
||||
const chatTop = currentChatListHeight;
|
||||
const chatTopSmaller = (viewportOffset + i) * CHAT_HEIGHT_PX;
|
||||
const expendedOffsetTop = currentChatListHeight;
|
||||
const collapsedOffsetTop = (viewportOffset + i) * CHAT_HEIGHT_PX;
|
||||
|
||||
currentChatListHeight += (selectChat(global, id)!.isForum ? CHAT_HEIGHT_FORUM_PX : CHAT_HEIGHT_PX);
|
||||
|
||||
return (
|
||||
@ -156,8 +163,8 @@ const ChatList: FC<OwnProps> = ({
|
||||
folderId={folderId}
|
||||
animationType={getAnimationType(id)}
|
||||
orderDiff={orderDiffById[id]}
|
||||
offsetTop={chatTop}
|
||||
offsetTopInSmallerMode={chatTop - chatTopSmaller}
|
||||
offsetTop={isForumPanelOpen ? collapsedOffsetTop : expendedOffsetTop}
|
||||
offsetCollapseDelta={expendedOffsetTop - collapsedOffsetTop}
|
||||
observeIntersection={observe}
|
||||
onDragEnter={handleDragEnter}
|
||||
/>
|
||||
@ -167,7 +174,7 @@ const ChatList: FC<OwnProps> = ({
|
||||
|
||||
return (
|
||||
<InfiniteScroll
|
||||
className="chat-list custom-scroll"
|
||||
className={buildClassName('chat-list custom-scroll', isForumPanelOpen && 'forum-panel-open')}
|
||||
ref={containerRef}
|
||||
items={viewportIds}
|
||||
preloadBackwards={CHAT_LIST_SLICE}
|
||||
|
||||
@ -10,6 +10,10 @@
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
@media (max-width: 600px) {
|
||||
left: 4.3125rem;
|
||||
}
|
||||
|
||||
&.rtl {
|
||||
left: 0;
|
||||
right: 4.75rem;
|
||||
@ -18,7 +22,7 @@
|
||||
border-right: 1px solid var(--color-borders);
|
||||
}
|
||||
|
||||
transition: transform 250ms ease; // Same as `.Chat > .info`
|
||||
transition: transform var(--layer-transition);
|
||||
transform: translate3d(100%, 0, 0);
|
||||
|
||||
:global(.chat-list) {
|
||||
|
||||
@ -35,7 +35,7 @@
|
||||
z-index: 1;
|
||||
|
||||
opacity: 1;
|
||||
transition: opacity 250ms ease; // Same as Forum Panel
|
||||
transition: opacity var(--layer-transition);
|
||||
}
|
||||
|
||||
&--tabs-hidden .TabList {
|
||||
|
||||
@ -102,7 +102,7 @@
|
||||
}
|
||||
|
||||
.SearchInput {
|
||||
transition: opacity 250ms ease; // Same as Forum Panel
|
||||
transition: opacity var(--layer-transition);
|
||||
|
||||
&--hidden {
|
||||
opacity: 0;
|
||||
|
||||
36
src/components/left/main/hooks/useCollapseWithForumPanel.ts
Normal file
36
src/components/left/main/hooks/useCollapseWithForumPanel.ts
Normal file
@ -0,0 +1,36 @@
|
||||
import { useLayoutEffect } from '../../../../lib/teact/teact';
|
||||
import { fastRaf } from '../../../../util/schedulers';
|
||||
import { ANIMATION_END_DELAY } from '../../../../config';
|
||||
|
||||
const ANIMATION_DURATION = 450;
|
||||
|
||||
// Reduce height of forum chat items when opening Forum Panel
|
||||
export default function useCollapseWithForumPanel(
|
||||
containerRef: React.RefObject<HTMLDivElement>,
|
||||
isForumPanelOpen = false,
|
||||
) {
|
||||
useLayoutEffect(() => {
|
||||
const chatEls = Array.from(containerRef.current!.querySelectorAll<HTMLDivElement>('.Chat'));
|
||||
|
||||
chatEls.forEach((chatEl) => {
|
||||
const offsetCollapseDelta = Number(chatEl.dataset.offsetCollapseDelta);
|
||||
chatEl.style.transform = `translateY(${isForumPanelOpen ? offsetCollapseDelta : -offsetCollapseDelta!}px)`;
|
||||
});
|
||||
|
||||
fastRaf(() => {
|
||||
chatEls.forEach((chatEl) => {
|
||||
chatEl.classList.add('animate-collapse');
|
||||
chatEl.style.transform = '';
|
||||
});
|
||||
});
|
||||
|
||||
setTimeout(() => {
|
||||
// Wait one more frame for better animation performance
|
||||
fastRaf(() => {
|
||||
chatEls.forEach((chatEl) => {
|
||||
chatEl.classList.remove('animate-collapse');
|
||||
});
|
||||
});
|
||||
}, ANIMATION_DURATION + ANIMATION_END_DELAY);
|
||||
}, [containerRef, isForumPanelOpen]);
|
||||
}
|
||||
@ -94,7 +94,6 @@
|
||||
}
|
||||
|
||||
@media (hover: hover) {
|
||||
|
||||
&:hover,
|
||||
&:focus {
|
||||
--background-color: var(--color-chat-hover);
|
||||
|
||||
@ -44,13 +44,14 @@ interface OwnProps {
|
||||
destructive?: boolean;
|
||||
multiline?: boolean;
|
||||
isStatic?: boolean;
|
||||
clickArg?: any;
|
||||
contextActions?: MenuItemContextAction[];
|
||||
offsetCollapseDelta?: number;
|
||||
withPortalForMenu?: boolean;
|
||||
onMouseDown?: (e: React.MouseEvent<HTMLDivElement>) => void;
|
||||
onClick?: (e: React.MouseEvent<HTMLDivElement>, arg?: any) => void;
|
||||
clickArg?: any;
|
||||
onSecondaryIconClick?: (e: React.MouseEvent<HTMLButtonElement>) => void;
|
||||
onDragEnter?: (e: React.DragEvent<HTMLDivElement>) => void;
|
||||
shouldUsePortalForMenu?: boolean;
|
||||
}
|
||||
|
||||
const ListItem: FC<OwnProps> = ({
|
||||
@ -74,12 +75,13 @@ const ListItem: FC<OwnProps> = ({
|
||||
multiline,
|
||||
isStatic,
|
||||
contextActions,
|
||||
withPortalForMenu,
|
||||
offsetCollapseDelta,
|
||||
onMouseDown,
|
||||
onClick,
|
||||
clickArg,
|
||||
onSecondaryIconClick,
|
||||
onDragEnter,
|
||||
shouldUsePortalForMenu,
|
||||
}) => {
|
||||
// eslint-disable-next-line no-null/no-null
|
||||
let containerRef = useRef<HTMLDivElement>(null);
|
||||
@ -107,8 +109,8 @@ const ListItem: FC<OwnProps> = ({
|
||||
);
|
||||
|
||||
const getLayout = useCallback(
|
||||
() => ({ shouldUsePortalPositioning: shouldUsePortalForMenu }),
|
||||
[shouldUsePortalForMenu],
|
||||
() => ({ withPortal: withPortalForMenu }),
|
||||
[withPortalForMenu],
|
||||
);
|
||||
|
||||
const {
|
||||
@ -183,6 +185,7 @@ const ListItem: FC<OwnProps> = ({
|
||||
className={fullClassName}
|
||||
dir={lang.isRtl ? 'rtl' : undefined}
|
||||
style={style}
|
||||
data-offset-collapse-delta={offsetCollapseDelta}
|
||||
onMouseDown={onMouseDown}
|
||||
onDragEnter={onDragEnter}
|
||||
>
|
||||
@ -230,7 +233,7 @@ const ListItem: FC<OwnProps> = ({
|
||||
autoClose
|
||||
onClose={handleContextMenuClose}
|
||||
onCloseAnimationEnd={handleContextMenuHide}
|
||||
shouldUsePortalForMenu={shouldUsePortalForMenu}
|
||||
withPortal={withPortalForMenu}
|
||||
>
|
||||
{contextActions.map((action) => (
|
||||
<MenuItem
|
||||
|
||||
@ -41,7 +41,7 @@ type OwnProps = {
|
||||
onClose: () => void;
|
||||
onMouseEnter?: (e: React.MouseEvent<HTMLDivElement, MouseEvent>) => void;
|
||||
onMouseLeave?: (e: React.MouseEvent<HTMLDivElement, MouseEvent>) => void;
|
||||
shouldUsePortalForMenu?: boolean;
|
||||
withPortal?: boolean;
|
||||
children: React.ReactNode;
|
||||
};
|
||||
|
||||
@ -70,7 +70,7 @@ const Menu: FC<OwnProps> = ({
|
||||
onMouseEnter,
|
||||
onMouseLeave,
|
||||
shouldSkipTransition,
|
||||
shouldUsePortalForMenu,
|
||||
withPortal,
|
||||
}) => {
|
||||
// eslint-disable-next-line no-null/no-null
|
||||
let menuRef = useRef<HTMLDivElement>(null);
|
||||
@ -161,7 +161,7 @@ const Menu: FC<OwnProps> = ({
|
||||
</div>
|
||||
);
|
||||
|
||||
if (shouldUsePortalForMenu) {
|
||||
if (withPortal) {
|
||||
return <Portal>{menu}</Portal>;
|
||||
}
|
||||
|
||||
|
||||
@ -54,6 +54,10 @@ addActionHandler('openChat', (global, actions, payload) => {
|
||||
};
|
||||
}
|
||||
|
||||
if (id !== global.forumPanelChatId) {
|
||||
actions.closeForumPanel();
|
||||
}
|
||||
|
||||
return updateCurrentMessageList(global, id, threadId, type, shouldReplaceHistory);
|
||||
});
|
||||
|
||||
|
||||
@ -6,7 +6,7 @@ interface Layout {
|
||||
extraTopPadding?: number;
|
||||
marginSides?: number;
|
||||
extraMarginTop?: number;
|
||||
shouldUsePortalPositioning?: boolean;
|
||||
withPortal?: boolean;
|
||||
}
|
||||
|
||||
const MENU_POSITION_VISUAL_COMFORT_SPACE_PX = 16;
|
||||
@ -48,7 +48,7 @@ export default function useContextMenuPosition(
|
||||
extraTopPadding = 0,
|
||||
marginSides = 0,
|
||||
extraMarginTop = 0,
|
||||
shouldUsePortalPositioning = false,
|
||||
withPortal = false,
|
||||
} = getLayout?.() || {};
|
||||
|
||||
const marginTop = menuEl ? parseInt(getComputedStyle(menuEl).marginTop, 10) + extraMarginTop : undefined;
|
||||
@ -100,8 +100,8 @@ export default function useContextMenuPosition(
|
||||
|
||||
const triggerRect = triggerEl.getBoundingClientRect();
|
||||
|
||||
const addedYForPortalPositioning = (shouldUsePortalPositioning ? triggerRect.top : 0);
|
||||
const addedXForPortalPositioning = (shouldUsePortalPositioning ? triggerRect.left : 0);
|
||||
const addedYForPortalPositioning = (withPortal ? triggerRect.top : 0);
|
||||
const addedXForPortalPositioning = (withPortal ? triggerRect.left : 0);
|
||||
|
||||
const left = (horizontalPosition === 'left'
|
||||
? Math.max(MENU_POSITION_VISUAL_COMFORT_SPACE_PX, Math.min(
|
||||
|
||||
@ -88,6 +88,21 @@
|
||||
@include overflow-y-overlay();
|
||||
}
|
||||
|
||||
&.forum-panel-open {
|
||||
.info {
|
||||
transform: translateX(-25%);
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.Chat[dir="rtl"] .info {
|
||||
transform: translateX(25%);
|
||||
}
|
||||
|
||||
.ListItem-button {
|
||||
height: 4.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
.scroll-container {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
@ -150,7 +150,6 @@ $color-message-reaction-own-hover: #b5e0a4;
|
||||
|
||||
--color-chat-hover: #{$color-chat-hover};
|
||||
--color-chat-active: #{$color-chat-active};
|
||||
--color-chat-active-greyed: #60a7f0;
|
||||
--color-item-active: #{$color-item-active};
|
||||
|
||||
--color-selection-highlight: #{$color-selection};
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user