Left Column: Fix resizer in archive component (#3152)
This commit is contained in:
parent
dc75eea5d6
commit
d04177aea8
@ -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,
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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} />
|
||||
|
||||
@ -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: (
|
||||
|
||||
@ -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}
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user