Left Column: Fix resizer in archive component (#3152)

This commit is contained in:
Alexander Zinchuk 2023-05-05 15:53:10 +04:00
parent dc75eea5d6
commit d04177aea8
7 changed files with 62 additions and 64 deletions

View File

@ -1,9 +1,9 @@
import type { RefObject } from 'react';
import React, {
memo, useCallback, useEffect, useRef, useState,
memo, useCallback, useEffect, useState,
} from '../../lib/teact/teact';
import { getActions, withGlobal } from '../../global';
import type { FC } from '../../lib/teact/teact';
import type { GlobalState } from '../../global/types';
import { LeftColumnContent, SettingsScreens } from '../../types';
import type { ReducerAction } from '../../hooks/useReducer';
@ -13,7 +13,6 @@ import { IS_MAC_OS, IS_PWA, LAYERS_ANIMATION_NAME } from '../../util/windowEnvir
import captureEscKeyListener from '../../util/captureEscKeyListener';
import { selectCurrentChat, selectIsForumPanelOpen, selectTabState } from '../../global/selectors';
import useFoldersReducer from '../../hooks/reducers/useFoldersReducer';
import { useResize } from '../../hooks/useResize';
import { useHotkeys } from '../../hooks/useHotkeys';
import useSyncEffect from '../../hooks/useSyncEffect';
@ -25,12 +24,15 @@ import ArchivedChats from './ArchivedChats.async';
import './LeftColumn.scss';
interface OwnProps {
ref: RefObject<HTMLDivElement>;
}
type StateProps = {
searchQuery?: string;
searchDate?: number;
isFirstChatFolderActive: boolean;
shouldSkipHistoryAnimations?: boolean;
leftColumnWidth?: number;
currentUserId?: string;
hasPasscode?: boolean;
nextSettingsScreen?: SettingsScreens;
@ -57,12 +59,12 @@ enum ContentType {
const RENDER_COUNT = Object.keys(ContentType).length / 2;
const RESET_TRANSITION_DELAY_MS = 250;
const LeftColumn: FC<StateProps> = ({
function LeftColumn({
ref,
searchQuery,
searchDate,
isFirstChatFolderActive,
shouldSkipHistoryAnimations,
leftColumnWidth,
currentUserId,
hasPasscode,
nextSettingsScreen,
@ -73,7 +75,7 @@ const LeftColumn: FC<StateProps> = ({
forumPanelChatId,
isClosingSearch,
archiveSettings,
}) => {
}: OwnProps & StateProps) {
const {
setGlobalSearchQuery,
setGlobalSearchClosing,
@ -82,14 +84,10 @@ const LeftColumn: FC<StateProps> = ({
setGlobalSearchDate,
loadPasswordInfo,
clearTwoFaError,
setLeftColumnWidth,
resetLeftColumnWidth,
openChat,
requestNextSettingsScreen,
} = getActions();
// eslint-disable-next-line no-null/no-null
const resizeRef = useRef<HTMLDivElement>(null);
const [content, setContent] = useState<LeftColumnContent>(LeftColumnContent.ChatList);
const [settingsScreen, setSettingsScreen] = useState(SettingsScreens.Main);
const [contactsFilter, setContactsFilter] = useState<string>('');
@ -410,12 +408,6 @@ const LeftColumn: FC<StateProps> = ({
}
}, [foldersDispatch, nextFoldersAction, nextSettingsScreen, requestNextSettingsScreen]);
const {
initResize, resetResize, handleMouseUp,
} = useResize(resizeRef, (n) => setLeftColumnWidth({
leftColumnWidth: n,
}), resetLeftColumnWidth, leftColumnWidth, '--left-column-width');
const handleSettingsScreenSelect = useCallback((screen: SettingsScreens) => {
setContent(LeftColumnContent.Settings);
setSettingsScreen(screen);
@ -493,7 +485,7 @@ const LeftColumn: FC<StateProps> = ({
return (
<Transition
ref={resizeRef}
ref={ref}
name={shouldSkipHistoryAnimations ? 'none' : LAYERS_ANIMATION_NAME}
renderCount={RENDER_COUNT}
activeKey={contentType}
@ -502,21 +494,13 @@ const LeftColumn: FC<StateProps> = ({
shouldWrap
wrapExceptionKey={ContentType.Main}
id="LeftColumn"
afterChildren={(
<div
className="resize-handle"
onMouseDown={initResize}
onMouseUp={handleMouseUp}
onDoubleClick={resetResize}
/>
)}
>
{renderContent}
</Transition>
);
};
}
export default memo(withGlobal(
export default memo(withGlobal<OwnProps>(
(global): StateProps => {
const tabState = selectTabState(global);
const {
@ -530,7 +514,6 @@ export default memo(withGlobal(
nextFoldersAction,
} = tabState;
const {
leftColumnWidth,
currentUserId,
passcode: {
hasPasscode,
@ -549,7 +532,6 @@ export default memo(withGlobal(
searchDate: date,
isFirstChatFolderActive: activeChatFolder === 0,
shouldSkipHistoryAnimations,
leftColumnWidth,
currentUserId,
hasPasscode,
nextSettingsScreen,

View File

@ -35,6 +35,7 @@
min-width: var(--left-column-min-width);
max-width: var(--left-column-max-width);
height: 100%;
overflow: hidden;
position: relative;
background-color: var(--color-background);

View File

@ -257,6 +257,8 @@ const Main: FC<OwnProps & StateProps> = ({
// eslint-disable-next-line no-null/no-null
const containerRef = useRef<HTMLDivElement>(null);
// eslint-disable-next-line no-null/no-null
const leftColumnRef = useRef<HTMLDivElement>(null);
const { isDesktop } = useAppLayout();
useEffect(() => {
@ -488,8 +490,8 @@ const Main: FC<OwnProps & StateProps> = ({
return (
<div ref={containerRef} id="Main" className={className}>
<LeftColumn />
<MiddleColumn isMobile={isMobile} />
<LeftColumn ref={leftColumnRef} />
<MiddleColumn leftColumnRef={leftColumnRef} isMobile={isMobile} />
<RightColumn isMobile={isMobile} />
<MediaViewer isOpen={isMediaViewerOpen} />
<ForwardRecipientPicker isOpen={isForwardModalOpen} />

View File

@ -1,4 +1,4 @@
import type { FC } from '../../lib/teact/teact';
import type { RefObject } from 'react';
import React, {
useEffect, useState, memo, useMemo, useCallback,
} from '../../lib/teact/teact';
@ -67,6 +67,7 @@ import useForceUpdate from '../../hooks/useForceUpdate';
import useSyncEffect from '../../hooks/useSyncEffect';
import useAppLayout from '../../hooks/useAppLayout';
import usePinnedMessage from './hooks/usePinnedMessage';
import { useResize } from '../../hooks/useResize';
import Transition from '../ui/Transition';
import MiddleHeader from './MiddleHeader';
@ -88,6 +89,7 @@ import './MiddleColumn.scss';
import styles from './MiddleColumn.module.scss';
interface OwnProps {
leftColumnRef: RefObject<HTMLDivElement>;
isMobile?: boolean;
}
@ -99,7 +101,6 @@ type StateProps = {
replyingToId?: number;
isPrivate?: boolean;
isPinnedMessageList?: boolean;
isScheduledMessageList?: boolean;
canPost?: boolean;
currentUserBannedRights?: ApiChatBannedRights;
defaultBannedRights?: ApiChatBannedRights;
@ -114,6 +115,7 @@ type StateProps = {
isLeftColumnShown?: boolean;
isRightColumnShown?: boolean;
isBackgroundBlurred?: boolean;
leftColumnWidth?: number;
hasCurrentTextSearch?: boolean;
isSelectModeActive?: boolean;
isSeenByModalOpen: boolean;
@ -142,7 +144,8 @@ function isImage(item: DataTransferItem) {
const LAYER_ANIMATION_DURATION_MS = 450 + ANIMATION_END_DELAY;
const MiddleColumn: FC<OwnProps & StateProps> = ({
function MiddleColumn({
leftColumnRef,
chatId,
threadId,
messageListType,
@ -165,6 +168,7 @@ const MiddleColumn: FC<OwnProps & StateProps> = ({
isLeftColumnShown,
isRightColumnShown,
isBackgroundBlurred,
leftColumnWidth,
hasCurrentTextSearch,
isSelectModeActive,
isSeenByModalOpen,
@ -185,7 +189,7 @@ const MiddleColumn: FC<OwnProps & StateProps> = ({
shouldLoadFullChat,
lastSyncTime,
pinnedIds,
}) => {
}: OwnProps & StateProps) {
const {
openChat,
openPreviousChat,
@ -199,6 +203,8 @@ const MiddleColumn: FC<OwnProps & StateProps> = ({
restartBot,
showNotification,
loadFullChat,
setLeftColumnWidth,
resetLeftColumnWidth,
} = getActions();
const { width: windowWidth } = useWindowSize();
@ -319,6 +325,12 @@ const MiddleColumn: FC<OwnProps & StateProps> = ({
}
}, [shouldLoadFullChat, chatId, isReady, loadFullChat]);
const {
initResize, resetResize, handleMouseUp,
} = useResize(leftColumnRef, (n) => setLeftColumnWidth({
leftColumnWidth: n,
}), resetLeftColumnWidth, leftColumnWidth, '--left-column-width');
const handleDragEnter = useCallback((e: React.DragEvent<HTMLDivElement>) => {
const { items } = e.dataTransfer || {};
const shouldDrawQuick = items && items.length > 0 && Array.from(items)
@ -452,6 +464,12 @@ const MiddleColumn: FC<OwnProps & StateProps> = ({
`}
onClick={(isTablet && isLeftColumnShown) ? handleTabletFocus : undefined}
>
<div
className="resize-handle"
onMouseDown={initResize}
onMouseUp={handleMouseUp}
onDoubleClick={resetResize}
/>
<div
className={bgClassName}
style={customBackgroundValue ? `--custom-background: ${customBackgroundValue}` : undefined}
@ -622,7 +640,7 @@ const MiddleColumn: FC<OwnProps & StateProps> = ({
<GiftPremiumModal isOpen={isGiftPremiumModalOpen} />
</div>
);
};
}
export default memo(withGlobal<OwnProps>(
(global, { isMobile }): StateProps => {
@ -637,7 +655,7 @@ export default memo(withGlobal<OwnProps>(
messageLanguageModal,
} = selectTabState(global);
const currentMessageList = selectCurrentMessageList(global);
const { chats: { listIds }, lastSyncTime } = global;
const { chats: { listIds }, leftColumnWidth, lastSyncTime } = global;
const state: StateProps = {
theme,
@ -656,6 +674,7 @@ export default memo(withGlobal<OwnProps>(
withInterfaceAnimations: selectCanAnimateInterface(global),
currentTransitionKey: Math.max(0, messageLists.length - 1),
activeEmojiInteractions,
leftColumnWidth,
lastSyncTime,
};
@ -675,7 +694,6 @@ export default memo(withGlobal<OwnProps>(
const canPost = chat && getCanPostInChat(chat, threadId, isComments);
const isBotNotStarted = selectIsChatBotNotStarted(global, chatId);
const isPinnedMessageList = messageListType === 'pinned';
const isScheduledMessageList = messageListType === 'scheduled';
const isMainThread = messageListType === 'thread' && threadId === MAIN_THREAD_ID;
const isChannel = Boolean(chat && isChatChannel(chat));
const canSubscribe = Boolean(
@ -711,7 +729,6 @@ export default memo(withGlobal<OwnProps>(
&& !(shouldJoinToSend && chat?.isNotJoined)
&& !shouldBlockSendInForum,
isPinnedMessageList,
isScheduledMessageList,
currentUserBannedRights: chat?.currentUserBannedRights,
defaultBannedRights: chat?.defaultBannedRights,
hasPinned: (

View File

@ -40,7 +40,6 @@ export type TransitionProps = {
onStart?: NoneToVoidFunction;
onStop?: NoneToVoidFunction;
children: React.ReactNode | ChildrenFn;
afterChildren?: React.ReactNode;
};
const FALLBACK_ANIMATION_END = 1000;
@ -50,7 +49,6 @@ const CLASSES = {
from: 'Transition_slide-from',
to: 'Transition_slide-to',
inactive: 'Transition_slide-inactive',
afterSlides: 'Transition_afterSlides',
};
const DISABLEABLE_ANIMATIONS = new Set<AnimationName>([
'slide', 'slideRtl', 'slideFade', 'zoomFade', 'slideLayers', 'pushSlide', 'reveal',
@ -75,7 +73,6 @@ function Transition({
onStart,
onStop,
children,
afterChildren,
}: TransitionProps) {
const currentKeyRef = useRef<number>();
// No need for a container to update on change
@ -128,14 +125,12 @@ function Transition({
const prevActiveIndex = renderCount ? prevActiveKey : keys.indexOf(prevActiveKey);
const activeIndex = renderCount ? activeKey : keys.indexOf(activeKey);
const childNodes = Array.from(container.childNodes)
.filter((el) => !(el instanceof HTMLElement && el.classList.contains(CLASSES.afterSlides)));
const childNodes = Array.from(container.childNodes);
if (!childNodes.length) {
return;
}
const childElements = (Array.from(container.children) as HTMLElement[])
.filter((el) => !el.classList.contains(CLASSES.afterSlides));
const childElements = Array.from(container.children) as HTMLElement[];
childElements.forEach((el) => {
addExtraClass(el, CLASSES.slide);
@ -319,12 +314,6 @@ function Transition({
: rendered;
});
if (afterChildren) {
contents.push((
<div className={CLASSES.afterSlides}>{afterChildren}</div>
));
}
return (
<div
ref={containerRef}

View File

@ -2,6 +2,7 @@ import type { RefObject } from 'react';
import {
useState, useEffect, useLayoutEffect, useCallback,
} from '../lib/teact/teact';
import { requestMutation } from '../lib/fasterdom/fasterdom';
import useFlag from './useFlag';
export function useResize(
@ -16,15 +17,17 @@ export function useResize(
const [initialElementWidth, setInitialElementWidth] = useState<number>(0);
const setElementStyle = useCallback((width?: number) => {
if (!elementRef.current) {
return;
}
requestMutation(() => {
if (!elementRef.current) {
return;
}
const widthPx = width ? `${width}px` : '';
elementRef.current.style.width = widthPx;
if (cssPropertyName) {
elementRef.current.style.setProperty(cssPropertyName, widthPx);
}
const widthPx = width ? `${width}px` : '';
elementRef.current.style.width = widthPx;
if (cssPropertyName) {
elementRef.current.style.setProperty(cssPropertyName, widthPx);
}
});
}, [cssPropertyName, elementRef]);
useLayoutEffect(() => {
@ -36,13 +39,17 @@ export function useResize(
}, [cssPropertyName, elementRef, initialWidth, setElementStyle]);
function handleMouseUp() {
document.body.classList.remove('cursor-ew-resize');
requestMutation(() => {
document.body.classList.remove('cursor-ew-resize');
});
}
function initResize(e: React.MouseEvent<HTMLElement, MouseEvent>) {
e.preventDefault();
document.body.classList.add('cursor-ew-resize');
requestMutation(() => {
document.body.classList.add('cursor-ew-resize');
});
setInitialMouseX(e.clientX);
setInitialElementWidth(elementRef.current!.offsetWidth);

View File

@ -115,7 +115,7 @@ body.cursor-ew-resize {
display: none;
position: absolute;
top: 0;
right: -0.25rem;
left: 0;
bottom: 0;
width: 0.25rem;
z-index: var(--z-resize-handle);