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