Fix chat IDs being stuck in URL (#3105)
This commit is contained in:
parent
61a26749d0
commit
56f41804c1
@ -28,6 +28,7 @@ import renderText from './helpers/renderText';
|
|||||||
import useMedia from '../../hooks/useMedia';
|
import useMedia from '../../hooks/useMedia';
|
||||||
import useMediaTransition from '../../hooks/useMediaTransition';
|
import useMediaTransition from '../../hooks/useMediaTransition';
|
||||||
import useLang from '../../hooks/useLang';
|
import useLang from '../../hooks/useLang';
|
||||||
|
import { useFastClick } from '../../hooks/useFastClick';
|
||||||
|
|
||||||
import OptimizedVideo from '../ui/OptimizedVideo';
|
import OptimizedVideo from '../ui/OptimizedVideo';
|
||||||
|
|
||||||
@ -130,9 +131,11 @@ const Avatar: FC<OwnProps> = ({
|
|||||||
if (isSavedMessages) {
|
if (isSavedMessages) {
|
||||||
content = (
|
content = (
|
||||||
<i
|
<i
|
||||||
className={buildClassName(cn.icon,
|
className={buildClassName(
|
||||||
|
cn.icon,
|
||||||
'icon',
|
'icon',
|
||||||
'icon-avatar-saved-messages')}
|
'icon-avatar-saved-messages',
|
||||||
|
)}
|
||||||
role="img"
|
role="img"
|
||||||
aria-label={author}
|
aria-label={author}
|
||||||
/>
|
/>
|
||||||
@ -140,9 +143,11 @@ const Avatar: FC<OwnProps> = ({
|
|||||||
} else if (isDeleted) {
|
} else if (isDeleted) {
|
||||||
content = (
|
content = (
|
||||||
<i
|
<i
|
||||||
className={buildClassName(cn.icon,
|
className={buildClassName(
|
||||||
|
cn.icon,
|
||||||
'icon',
|
'icon',
|
||||||
'icon-avatar-deleted-account')}
|
'icon-avatar-deleted-account',
|
||||||
|
)}
|
||||||
role="img"
|
role="img"
|
||||||
aria-label={author}
|
aria-label={author}
|
||||||
/>
|
/>
|
||||||
@ -150,9 +155,11 @@ const Avatar: FC<OwnProps> = ({
|
|||||||
} else if (isReplies) {
|
} else if (isReplies) {
|
||||||
content = (
|
content = (
|
||||||
<i
|
<i
|
||||||
className={buildClassName(cn.icon,
|
className={buildClassName(
|
||||||
|
cn.icon,
|
||||||
'icon',
|
'icon',
|
||||||
'icon-reply-filled')}
|
'icon-reply-filled',
|
||||||
|
)}
|
||||||
role="img"
|
role="img"
|
||||||
aria-label={author}
|
aria-label={author}
|
||||||
/>
|
/>
|
||||||
@ -205,11 +212,12 @@ const Avatar: FC<OwnProps> = ({
|
|||||||
);
|
);
|
||||||
|
|
||||||
const hasMedia = Boolean(isSavedMessages || imgBlobUrl);
|
const hasMedia = Boolean(isSavedMessages || imgBlobUrl);
|
||||||
const handleClick = useCallback((e: ReactMouseEvent<HTMLDivElement, MouseEvent>) => {
|
|
||||||
|
const { handleClick, handleMouseDown } = useFastClick((e: ReactMouseEvent<HTMLDivElement, MouseEvent>) => {
|
||||||
if (onClick) {
|
if (onClick) {
|
||||||
onClick(e, hasMedia);
|
onClick(e, hasMedia);
|
||||||
}
|
}
|
||||||
}, [onClick, hasMedia]);
|
});
|
||||||
|
|
||||||
const senderId = (user || chat) && (user || chat)!.id;
|
const senderId = (user || chat) && (user || chat)!.id;
|
||||||
|
|
||||||
@ -217,9 +225,10 @@ const Avatar: FC<OwnProps> = ({
|
|||||||
<div
|
<div
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={fullClassName}
|
className={fullClassName}
|
||||||
onClick={handleClick}
|
|
||||||
data-test-sender-id={IS_TEST ? senderId : undefined}
|
data-test-sender-id={IS_TEST ? senderId : undefined}
|
||||||
aria-label={typeof content === 'string' ? author : undefined}
|
aria-label={typeof content === 'string' ? author : undefined}
|
||||||
|
onClick={handleClick}
|
||||||
|
onMouseDown={handleMouseDown}
|
||||||
>
|
>
|
||||||
{typeof content === 'string' ? renderText(content, [size === 'jumbo' ? 'hq_emoji' : 'emoji']) : content}
|
{typeof content === 'string' ? renderText(content, [size === 'jumbo' ? 'hq_emoji' : 'emoji']) : content}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -11,11 +11,11 @@ import type { ObserveFn } from '../../hooks/useIntersectionObserver';
|
|||||||
import type { FC } from '../../lib/teact/teact';
|
import type { FC } from '../../lib/teact/teact';
|
||||||
import type { ApiChat } from '../../api/types';
|
import type { ApiChat } from '../../api/types';
|
||||||
|
|
||||||
import { IS_TOUCH_ENV } from '../../util/windowEnvironment';
|
|
||||||
import buildClassName from '../../util/buildClassName';
|
import buildClassName from '../../util/buildClassName';
|
||||||
import { getOrderedTopics } from '../../global/helpers';
|
import { getOrderedTopics } from '../../global/helpers';
|
||||||
import { getIsMobile } from '../../hooks/useAppLayout';
|
import { getIsMobile } from '../../hooks/useAppLayout';
|
||||||
import useLang from '../../hooks/useLang';
|
import useLang from '../../hooks/useLang';
|
||||||
|
import { useFastClick } from '../../hooks/useFastClick';
|
||||||
import { REM } from './helpers/mediaDimensions';
|
import { REM } from './helpers/mediaDimensions';
|
||||||
import renderText from './helpers/renderText';
|
import renderText from './helpers/renderText';
|
||||||
|
|
||||||
@ -59,17 +59,22 @@ const ChatForumLastMessage: FC<OwnProps> = ({
|
|||||||
const [isReversedCorner, setIsReversedCorner] = useState(false);
|
const [isReversedCorner, setIsReversedCorner] = useState(false);
|
||||||
const [overwrittenWidth, setOverwrittenWidth] = useState<number | undefined>(undefined);
|
const [overwrittenWidth, setOverwrittenWidth] = useState<number | undefined>(undefined);
|
||||||
|
|
||||||
function handleOpenTopic(e: React.MouseEvent<HTMLDivElement>) {
|
const {
|
||||||
|
handleClick: handleOpenTopicClick,
|
||||||
|
handleMouseDown: handleOpenTopicMouseDown,
|
||||||
|
} = useFastClick((e: React.MouseEvent<HTMLDivElement>) => {
|
||||||
if (lastActiveTopic.unreadCount === 0) return;
|
if (lastActiveTopic.unreadCount === 0) return;
|
||||||
|
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
openChat({
|
openChat({
|
||||||
id: chat.id,
|
id: chat.id,
|
||||||
threadId: lastActiveTopic.id,
|
threadId: lastActiveTopic.id,
|
||||||
shouldReplaceHistory: true,
|
shouldReplaceHistory: true,
|
||||||
noForumTopicPanel: getIsMobile(),
|
noForumTopicPanel: getIsMobile(),
|
||||||
});
|
});
|
||||||
}
|
});
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const lastMessageElement = lastMessageRef.current;
|
const lastMessageElement = lastMessageRef.current;
|
||||||
@ -105,8 +110,8 @@ const ChatForumLastMessage: FC<OwnProps> = ({
|
|||||||
lastActiveTopic.unreadCount && styles.unread,
|
lastActiveTopic.unreadCount && styles.unread,
|
||||||
)}
|
)}
|
||||||
ref={mainColumnRef}
|
ref={mainColumnRef}
|
||||||
onMouseDown={IS_TOUCH_ENV ? undefined : handleOpenTopic}
|
onClick={handleOpenTopicClick}
|
||||||
onClick={IS_TOUCH_ENV ? handleOpenTopic : undefined}
|
onMouseDown={handleOpenTopicMouseDown}
|
||||||
>
|
>
|
||||||
<TopicIcon
|
<TopicIcon
|
||||||
topic={lastActiveTopic}
|
topic={lastActiveTopic}
|
||||||
@ -144,8 +149,8 @@ const ChatForumLastMessage: FC<OwnProps> = ({
|
|||||||
<div
|
<div
|
||||||
className={buildClassName(styles.lastMessage, lastActiveTopic?.unreadCount && styles.unread)}
|
className={buildClassName(styles.lastMessage, lastActiveTopic?.unreadCount && styles.unread)}
|
||||||
ref={lastMessageRef}
|
ref={lastMessageRef}
|
||||||
onMouseDown={IS_TOUCH_ENV ? undefined : handleOpenTopic}
|
onClick={handleOpenTopicClick}
|
||||||
onClick={IS_TOUCH_ENV ? handleOpenTopic : undefined}
|
onMouseDown={handleOpenTopicMouseDown}
|
||||||
>
|
>
|
||||||
{lastMessage}
|
{lastMessage}
|
||||||
{!overwrittenWidth && !isReversedCorner && (
|
{!overwrittenWidth && !isReversedCorner && (
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import type { FC } from '../../lib/teact/teact';
|
import type { FC } from '../../lib/teact/teact';
|
||||||
import React, { useCallback, useRef } from '../../lib/teact/teact';
|
import React, { useRef } from '../../lib/teact/teact';
|
||||||
|
|
||||||
import type {
|
import type {
|
||||||
ApiUser, ApiMessage, ApiChat,
|
ApiUser, ApiMessage, ApiChat,
|
||||||
@ -18,7 +18,6 @@ import { getPictogramDimensions } from './helpers/mediaDimensions';
|
|||||||
import buildClassName from '../../util/buildClassName';
|
import buildClassName from '../../util/buildClassName';
|
||||||
|
|
||||||
import type { ObserveFn } from '../../hooks/useIntersectionObserver';
|
import type { ObserveFn } from '../../hooks/useIntersectionObserver';
|
||||||
import { IS_TOUCH_ENV, MouseButton } from '../../util/windowEnvironment';
|
|
||||||
import { useIsIntersecting } from '../../hooks/useIntersectionObserver';
|
import { useIsIntersecting } from '../../hooks/useIntersectionObserver';
|
||||||
import useMedia from '../../hooks/useMedia';
|
import useMedia from '../../hooks/useMedia';
|
||||||
import useThumbnail from '../../hooks/useThumbnail';
|
import useThumbnail from '../../hooks/useThumbnail';
|
||||||
@ -29,6 +28,7 @@ import MessageSummary from './MessageSummary';
|
|||||||
import MediaSpoiler from './MediaSpoiler';
|
import MediaSpoiler from './MediaSpoiler';
|
||||||
|
|
||||||
import './EmbeddedMessage.scss';
|
import './EmbeddedMessage.scss';
|
||||||
|
import { useFastClick } from '../../hooks/useFastClick';
|
||||||
|
|
||||||
type OwnProps = {
|
type OwnProps = {
|
||||||
className?: string;
|
className?: string;
|
||||||
@ -72,13 +72,7 @@ const EmbeddedMessage: FC<OwnProps> = ({
|
|||||||
|
|
||||||
const senderTitle = sender ? getSenderTitle(lang, sender) : message?.forwardInfo?.hiddenUserName;
|
const senderTitle = sender ? getSenderTitle(lang, sender) : message?.forwardInfo?.hiddenUserName;
|
||||||
|
|
||||||
const handleClick = useCallback((e: React.MouseEvent<HTMLDivElement>) => {
|
const { handleClick, handleMouseDown } = useFastClick(onClick);
|
||||||
if (e.type === 'mousedown' && e.button !== MouseButton.Main) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
onClick?.();
|
|
||||||
}, [onClick]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
@ -88,8 +82,8 @@ const EmbeddedMessage: FC<OwnProps> = ({
|
|||||||
className,
|
className,
|
||||||
sender && !noUserColors && `color-${getUserColorKey(sender)}`,
|
sender && !noUserColors && `color-${getUserColorKey(sender)}`,
|
||||||
)}
|
)}
|
||||||
onClick={message && IS_TOUCH_ENV ? handleClick : undefined}
|
onClick={message && handleClick}
|
||||||
onMouseDown={message && !IS_TOUCH_ENV ? handleClick : undefined}
|
onMouseDown={message && handleMouseDown}
|
||||||
>
|
>
|
||||||
{mediaThumbnail && renderPictogram(mediaThumbnail, mediaBlobUrl, isRoundVideo, isProtected, isSpoiler)}
|
{mediaThumbnail && renderPictogram(mediaThumbnail, mediaBlobUrl, isRoundVideo, isProtected, isSpoiler)}
|
||||||
<div className="message-text">
|
<div className="message-text">
|
||||||
|
|||||||
@ -20,6 +20,7 @@ import { getOrderedTopics } from '../../../global/helpers';
|
|||||||
import captureEscKeyListener from '../../../util/captureEscKeyListener';
|
import captureEscKeyListener from '../../../util/captureEscKeyListener';
|
||||||
import { waitForTransitionEnd } from '../../../util/cssAnimationEndListeners';
|
import { waitForTransitionEnd } from '../../../util/cssAnimationEndListeners';
|
||||||
import { captureEvents, SwipeDirection } from '../../../util/captureEvents';
|
import { captureEvents, SwipeDirection } from '../../../util/captureEvents';
|
||||||
|
import { createLocationHash } from '../../../util/routing';
|
||||||
|
|
||||||
import useInfiniteScroll from '../../../hooks/useInfiniteScroll';
|
import useInfiniteScroll from '../../../hooks/useInfiniteScroll';
|
||||||
import { useIntersectionObserver, useOnIntersect } from '../../../hooks/useIntersectionObserver';
|
import { useIntersectionObserver, useOnIntersect } from '../../../hooks/useIntersectionObserver';
|
||||||
@ -139,6 +140,7 @@ const ForumPanel: FC<OwnProps & StateProps> = ({
|
|||||||
useHistoryBack({
|
useHistoryBack({
|
||||||
isActive: isVisible,
|
isActive: isVisible,
|
||||||
onBack: handleClose,
|
onBack: handleClose,
|
||||||
|
hash: chat ? createLocationHash(chat.id, 'thread', MAIN_THREAD_ID) : undefined,
|
||||||
});
|
});
|
||||||
|
|
||||||
useEffect(() => (isVisible ? captureEscKeyListener(handleClose) : undefined), [handleClose, isVisible]);
|
useEffect(() => (isVisible ? captureEscKeyListener(handleClose) : undefined), [handleClose, isVisible]);
|
||||||
|
|||||||
@ -10,13 +10,14 @@ import {
|
|||||||
getMessageMediaHash, getMessageSingleInlineButton,
|
getMessageMediaHash, getMessageSingleInlineButton,
|
||||||
} from '../../global/helpers';
|
} from '../../global/helpers';
|
||||||
import buildClassName from '../../util/buildClassName';
|
import buildClassName from '../../util/buildClassName';
|
||||||
import { IS_TOUCH_ENV, MouseButton } from '../../util/windowEnvironment';
|
import { IS_TOUCH_ENV } from '../../util/windowEnvironment';
|
||||||
import renderText from '../common/helpers/renderText';
|
import renderText from '../common/helpers/renderText';
|
||||||
|
|
||||||
import useMedia from '../../hooks/useMedia';
|
import useMedia from '../../hooks/useMedia';
|
||||||
import useThumbnail from '../../hooks/useThumbnail';
|
import useThumbnail from '../../hooks/useThumbnail';
|
||||||
import useFlag from '../../hooks/useFlag';
|
import useFlag from '../../hooks/useFlag';
|
||||||
import useLang from '../../hooks/useLang';
|
import useLang from '../../hooks/useLang';
|
||||||
|
import { useFastClick } from '../../hooks/useFastClick';
|
||||||
import useAsyncRendering from '../right/hooks/useAsyncRendering';
|
import useAsyncRendering from '../right/hooks/useAsyncRendering';
|
||||||
|
|
||||||
import RippleEffect from '../ui/RippleEffect';
|
import RippleEffect from '../ui/RippleEffect';
|
||||||
@ -78,13 +79,7 @@ const HeaderPinnedMessage: FC<OwnProps> = ({
|
|||||||
|
|
||||||
const [noHoverColor, markNoHoverColor, unmarkNoHoverColor] = useFlag();
|
const [noHoverColor, markNoHoverColor, unmarkNoHoverColor] = useFlag();
|
||||||
|
|
||||||
const handleClick = useCallback((e: React.MouseEvent<HTMLDivElement>) => {
|
const { handleClick, handleMouseDown } = useFastClick(onClick);
|
||||||
if (e.type === 'mousedown' && e.button !== MouseButton.Main) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
onClick?.(e);
|
|
||||||
}, [onClick]);
|
|
||||||
|
|
||||||
function renderPictogram(thumbDataUri?: string, blobUrl?: string, spoiler?: boolean) {
|
function renderPictogram(thumbDataUri?: string, blobUrl?: string, spoiler?: boolean) {
|
||||||
const { width, height } = getPictogramDimensions();
|
const { width, height } = getPictogramDimensions();
|
||||||
@ -148,8 +143,8 @@ const HeaderPinnedMessage: FC<OwnProps> = ({
|
|||||||
/>
|
/>
|
||||||
<div
|
<div
|
||||||
className={buildClassName(styles.pinnedMessage, noHoverColor && styles.noHover)}
|
className={buildClassName(styles.pinnedMessage, noHoverColor && styles.noHover)}
|
||||||
onClick={IS_TOUCH_ENV ? handleClick : undefined}
|
onClick={handleClick}
|
||||||
onMouseDown={!IS_TOUCH_ENV ? handleClick : undefined}
|
onMouseDown={handleMouseDown}
|
||||||
dir={lang.isRtl ? 'rtl' : undefined}
|
dir={lang.isRtl ? 'rtl' : undefined}
|
||||||
>
|
>
|
||||||
<PinnedMessageNavigation
|
<PinnedMessageNavigation
|
||||||
|
|||||||
@ -67,6 +67,7 @@ import GroupCallTopPane from '../calls/group/GroupCallTopPane';
|
|||||||
import ChatReportPanel from './ChatReportPanel';
|
import ChatReportPanel from './ChatReportPanel';
|
||||||
|
|
||||||
import './MiddleHeader.scss';
|
import './MiddleHeader.scss';
|
||||||
|
import { useFastClick } from '../../hooks/useFastClick';
|
||||||
|
|
||||||
const ANIMATION_DURATION = 350;
|
const ANIMATION_DURATION = 350;
|
||||||
const BACK_BUTTON_INACTIVE_TIME = 450;
|
const BACK_BUTTON_INACTIVE_TIME = 450;
|
||||||
@ -178,9 +179,9 @@ const MiddleHeader: FC<OwnProps & StateProps> = ({
|
|||||||
const componentRef = useRef<HTMLDivElement>(null);
|
const componentRef = useRef<HTMLDivElement>(null);
|
||||||
const shouldAnimateTools = useRef<boolean>(true);
|
const shouldAnimateTools = useRef<boolean>(true);
|
||||||
|
|
||||||
const handleHeaderClick = useCallback(() => {
|
const { handleClick: handleHeaderClick, handleMouseDown: handleHeaderMouseDown } = useFastClick(() => {
|
||||||
openChatWithInfo({ id: chatId, threadId });
|
openChatWithInfo({ id: chatId, threadId });
|
||||||
}, [openChatWithInfo, chatId, threadId]);
|
});
|
||||||
|
|
||||||
const handleUnpinMessage = useCallback((messageId: number) => {
|
const handleUnpinMessage = useCallback((messageId: number) => {
|
||||||
pinMessage({ messageId, isUnpin: true });
|
pinMessage({ messageId, isUnpin: true });
|
||||||
@ -350,7 +351,11 @@ const MiddleHeader: FC<OwnProps & StateProps> = ({
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{(isLeftColumnHideable || currentTransitionKey > 0) && renderBackButton(shouldShowCloseButton, true)}
|
{(isLeftColumnHideable || currentTransitionKey > 0) && renderBackButton(shouldShowCloseButton, true)}
|
||||||
<div className="chat-info-wrapper" onClick={handleHeaderClick}>
|
<div
|
||||||
|
className="chat-info-wrapper"
|
||||||
|
onClick={handleHeaderClick}
|
||||||
|
onMouseDown={handleHeaderMouseDown}
|
||||||
|
>
|
||||||
{isUserId(chatId) ? (
|
{isUserId(chatId) ? (
|
||||||
<PrivateChatInfo
|
<PrivateChatInfo
|
||||||
key={chatId}
|
key={chatId}
|
||||||
|
|||||||
@ -18,6 +18,7 @@ import MessageOutgoingStatus from '../../common/MessageOutgoingStatus';
|
|||||||
import AnimatedCounter from '../../common/AnimatedCounter';
|
import AnimatedCounter from '../../common/AnimatedCounter';
|
||||||
|
|
||||||
import './MessageMeta.scss';
|
import './MessageMeta.scss';
|
||||||
|
import { useFastClick } from '../../../hooks/useFastClick';
|
||||||
|
|
||||||
type OwnProps = {
|
type OwnProps = {
|
||||||
message: ApiMessage;
|
message: ApiMessage;
|
||||||
@ -53,13 +54,15 @@ const MessageMeta: FC<OwnProps> = ({
|
|||||||
const lang = useLang();
|
const lang = useLang();
|
||||||
const [isActivated, markActivated] = useFlag();
|
const [isActivated, markActivated] = useFlag();
|
||||||
|
|
||||||
const handleClick = (e: React.MouseEvent) => {
|
const { handleClick, handleMouseDown } = useFastClick(onClick);
|
||||||
|
|
||||||
|
function handleImportedClick(e: React.MouseEvent) {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
|
|
||||||
showNotification({
|
showNotification({
|
||||||
message: lang('ImportedInfo'),
|
message: lang('ImportedInfo'),
|
||||||
});
|
});
|
||||||
};
|
}
|
||||||
|
|
||||||
function handleOpenThread(e: React.MouseEvent) {
|
function handleOpenThread(e: React.MouseEvent) {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
@ -99,7 +102,8 @@ const MessageMeta: FC<OwnProps> = ({
|
|||||||
<span
|
<span
|
||||||
className={fullClassName}
|
className={fullClassName}
|
||||||
dir={lang.isRtl ? 'rtl' : 'ltr'}
|
dir={lang.isRtl ? 'rtl' : 'ltr'}
|
||||||
onClick={onClick}
|
onClick={handleClick}
|
||||||
|
onMouseDown={handleMouseDown}
|
||||||
data-ignore-on-paste
|
data-ignore-on-paste
|
||||||
>
|
>
|
||||||
{isTranslated && (
|
{isTranslated && (
|
||||||
@ -130,10 +134,10 @@ const MessageMeta: FC<OwnProps> = ({
|
|||||||
<span className="message-time" title={title} onMouseEnter={markActivated}>
|
<span className="message-time" title={title} onMouseEnter={markActivated}>
|
||||||
{message.forwardInfo?.isImported && (
|
{message.forwardInfo?.isImported && (
|
||||||
<>
|
<>
|
||||||
<span className="message-imported" onClick={handleClick}>
|
<span className="message-imported" onClick={handleImportedClick}>
|
||||||
{formatDateTimeToString(message.forwardInfo.date * 1000, lang.code, true)}
|
{formatDateTimeToString(message.forwardInfo.date * 1000, lang.code, true)}
|
||||||
</span>
|
</span>
|
||||||
<span className="message-imported" onClick={handleClick}>{lang('ImportedMessage')}</span>
|
<span className="message-imported" onClick={handleImportedClick}>{lang('ImportedMessage')}</span>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
{message.isEdited && `${lang('EditedMessage')} `}
|
{message.isEdited && `${lang('EditedMessage')} `}
|
||||||
|
|||||||
@ -10,6 +10,7 @@ import useContextMenuHandlers from '../../hooks/useContextMenuHandlers';
|
|||||||
import useMenuPosition from '../../hooks/useMenuPosition';
|
import useMenuPosition from '../../hooks/useMenuPosition';
|
||||||
import useFlag from '../../hooks/useFlag';
|
import useFlag from '../../hooks/useFlag';
|
||||||
import useLang from '../../hooks/useLang';
|
import useLang from '../../hooks/useLang';
|
||||||
|
import { useFastClick } from '../../hooks/useFastClick';
|
||||||
|
|
||||||
import RippleEffect from './RippleEffect';
|
import RippleEffect from './RippleEffect';
|
||||||
import Menu from './Menu';
|
import Menu from './Menu';
|
||||||
@ -31,7 +32,9 @@ type MenuItemContextActionSeparator = {
|
|||||||
key?: string;
|
key?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type MenuItemContextAction = MenuItemContextActionItem | MenuItemContextActionSeparator;
|
export type MenuItemContextAction =
|
||||||
|
MenuItemContextActionItem
|
||||||
|
| MenuItemContextActionSeparator;
|
||||||
|
|
||||||
interface OwnProps {
|
interface OwnProps {
|
||||||
ref?: RefObject<HTMLDivElement>;
|
ref?: RefObject<HTMLDivElement>;
|
||||||
@ -62,6 +65,7 @@ interface OwnProps {
|
|||||||
onSecondaryIconClick?: (e: React.MouseEvent<HTMLButtonElement>) => void;
|
onSecondaryIconClick?: (e: React.MouseEvent<HTMLButtonElement>) => void;
|
||||||
onDragEnter?: (e: React.DragEvent<HTMLDivElement>) => void;
|
onDragEnter?: (e: React.DragEvent<HTMLDivElement>) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ListItem: FC<OwnProps> = ({
|
const ListItem: FC<OwnProps> = ({
|
||||||
ref,
|
ref,
|
||||||
buttonRef,
|
buttonRef,
|
||||||
@ -162,15 +166,20 @@ const ListItem: FC<OwnProps> = ({
|
|||||||
}
|
}
|
||||||
}, [allowDisabledClick, clickArg, disabled, markIsTouched, onClick, ripple, unmarkIsTouched, href]);
|
}, [allowDisabledClick, clickArg, disabled, markIsTouched, onClick, ripple, unmarkIsTouched, href]);
|
||||||
|
|
||||||
const handleSecondaryIconClick = (e: React.MouseEvent<HTMLButtonElement, MouseEvent>) => {
|
const {
|
||||||
|
handleClick: handleSecondaryIconClick,
|
||||||
|
handleMouseDown: handleSecondaryIconMouseDown,
|
||||||
|
} = useFastClick((e: React.MouseEvent<HTMLButtonElement>) => {
|
||||||
if ((disabled && !allowDisabledClick) || e.button !== 0 || (!onSecondaryIconClick && !contextActions)) return;
|
if ((disabled && !allowDisabledClick) || e.button !== 0 || (!onSecondaryIconClick && !contextActions)) return;
|
||||||
|
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
|
|
||||||
if (onSecondaryIconClick) {
|
if (onSecondaryIconClick) {
|
||||||
onSecondaryIconClick(e);
|
onSecondaryIconClick(e);
|
||||||
} else {
|
} else {
|
||||||
handleContextMenu(e);
|
handleContextMenu(e);
|
||||||
}
|
}
|
||||||
};
|
});
|
||||||
|
|
||||||
const handleMouseDown = useCallback((e: React.MouseEvent<HTMLElement, MouseEvent>) => {
|
const handleMouseDown = useCallback((e: React.MouseEvent<HTMLElement, MouseEvent>) => {
|
||||||
if (inactive || IS_TOUCH_ENV) {
|
if (inactive || IS_TOUCH_ENV) {
|
||||||
@ -242,8 +251,8 @@ const ListItem: FC<OwnProps> = ({
|
|||||||
round
|
round
|
||||||
color="translucent"
|
color="translucent"
|
||||||
size="smaller"
|
size="smaller"
|
||||||
onClick={IS_TOUCH_ENV ? handleSecondaryIconClick : undefined}
|
onClick={handleSecondaryIconClick}
|
||||||
onMouseDown={!IS_TOUCH_ENV ? handleSecondaryIconClick : undefined}
|
onMouseDown={handleSecondaryIconMouseDown}
|
||||||
>
|
>
|
||||||
<i className={`icon icon-${secondaryIcon}`} />
|
<i className={`icon icon-${secondaryIcon}`} />
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@ -9,17 +9,18 @@ import { requestForcedReflow, requestMutation } from '../../lib/fasterdom/faster
|
|||||||
import type { FC } from '../../lib/teact/teact';
|
import type { FC } from '../../lib/teact/teact';
|
||||||
import type { MenuItemContextAction } from './ListItem';
|
import type { MenuItemContextAction } from './ListItem';
|
||||||
|
|
||||||
import { IS_TOUCH_ENV, MouseButton } from '../../util/windowEnvironment';
|
import { MouseButton } from '../../util/windowEnvironment';
|
||||||
import forceReflow from '../../util/forceReflow';
|
import forceReflow from '../../util/forceReflow';
|
||||||
import buildClassName from '../../util/buildClassName';
|
import buildClassName from '../../util/buildClassName';
|
||||||
import renderText from '../common/helpers/renderText';
|
import renderText from '../common/helpers/renderText';
|
||||||
import useMenuPosition from '../../hooks/useMenuPosition';
|
import useMenuPosition from '../../hooks/useMenuPosition';
|
||||||
import useContextMenuHandlers from '../../hooks/useContextMenuHandlers';
|
import useContextMenuHandlers from '../../hooks/useContextMenuHandlers';
|
||||||
|
import { useFastClick } from '../../hooks/useFastClick';
|
||||||
|
|
||||||
import Menu from './Menu';
|
import Menu from './Menu';
|
||||||
import MenuItem from './MenuItem';
|
import MenuItem from './MenuItem';
|
||||||
import MenuSeparator from './MenuSeparator';
|
|
||||||
|
|
||||||
|
import MenuSeparator from './MenuSeparator';
|
||||||
import './Tab.scss';
|
import './Tab.scss';
|
||||||
|
|
||||||
type OwnProps = {
|
type OwnProps = {
|
||||||
@ -112,7 +113,7 @@ const Tab: FC<OwnProps> = ({
|
|||||||
handleContextMenuHide, isContextMenuOpen,
|
handleContextMenuHide, isContextMenuOpen,
|
||||||
} = useContextMenuHandlers(tabRef, !contextActions);
|
} = useContextMenuHandlers(tabRef, !contextActions);
|
||||||
|
|
||||||
const handleClick = useCallback((e: React.MouseEvent<HTMLDivElement>) => {
|
const { handleClick, handleMouseDown } = useFastClick((e: React.MouseEvent<HTMLDivElement>) => {
|
||||||
if (contextActions && (e.button === MouseButton.Secondary || !onClick)) {
|
if (contextActions && (e.button === MouseButton.Secondary || !onClick)) {
|
||||||
handleBeforeContextMenu(e);
|
handleBeforeContextMenu(e);
|
||||||
}
|
}
|
||||||
@ -122,7 +123,7 @@ const Tab: FC<OwnProps> = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
onClick?.(clickArg!);
|
onClick?.(clickArg!);
|
||||||
}, [clickArg, contextActions, handleBeforeContextMenu, onClick]);
|
});
|
||||||
|
|
||||||
const getTriggerElement = useCallback(() => tabRef.current, []);
|
const getTriggerElement = useCallback(() => tabRef.current, []);
|
||||||
|
|
||||||
@ -157,8 +158,8 @@ const Tab: FC<OwnProps> = ({
|
|||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={buildClassName('Tab', onClick && 'Tab--interactive', className)}
|
className={buildClassName('Tab', onClick && 'Tab--interactive', className)}
|
||||||
onClick={IS_TOUCH_ENV ? handleClick : undefined}
|
onClick={handleClick}
|
||||||
onMouseDown={!IS_TOUCH_ENV ? handleClick : undefined}
|
onMouseDown={handleMouseDown}
|
||||||
onContextMenu={handleContextMenu}
|
onContextMenu={handleContextMenu}
|
||||||
ref={tabRef}
|
ref={tabRef}
|
||||||
>
|
>
|
||||||
|
|||||||
20
src/hooks/useFastClick.ts
Normal file
20
src/hooks/useFastClick.ts
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
import type React from '../lib/teact/teact';
|
||||||
|
|
||||||
|
import { IS_TOUCH_ENV, MouseButton } from '../util/windowEnvironment';
|
||||||
|
|
||||||
|
type EventArg<E> = React.MouseEvent<E>;
|
||||||
|
type EventHandler<E> = (e: EventArg<E>) => void;
|
||||||
|
|
||||||
|
export function useFastClick<T extends HTMLDivElement | HTMLButtonElement>(callback?: EventHandler<T>) {
|
||||||
|
const wrapperHandler = callback ? (e: EventArg<T>) => {
|
||||||
|
if (e.type === 'mousedown' && e.button !== MouseButton.Main) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
callback(e);
|
||||||
|
} : undefined;
|
||||||
|
|
||||||
|
return IS_TOUCH_ENV
|
||||||
|
? { handleClick: wrapperHandler }
|
||||||
|
: { handleMouseDown: wrapperHandler };
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user