diff --git a/src/components/middle/message/Message.scss b/src/components/middle/message/Message.scss
index f32007dc7..5abc43dd2 100644
--- a/src/components/middle/message/Message.scss
+++ b/src/components/middle/message/Message.scss
@@ -20,6 +20,10 @@
--select-message-scale: 0.9;
--select-background-color: white;
+ &.is-swiped {
+ transform: translateX(-2.5rem) !important;
+ }
+
> .Avatar,
> .message-content-wrapper {
opacity: 1;
diff --git a/src/components/middle/message/Message.tsx b/src/components/middle/message/Message.tsx
index 7d7242291..534d5e89e 100644
--- a/src/components/middle/message/Message.tsx
+++ b/src/components/middle/message/Message.tsx
@@ -16,11 +16,8 @@ import {
ApiUser,
ApiChat,
ApiSticker,
- MAIN_THREAD_ID,
} from '../../../api/types';
-import {
- FocusDirection, IAlbum, ISettings, MediaViewerOrigin,
-} from '../../../types';
+import { FocusDirection, IAlbum, ISettings } from '../../../types';
import { IS_ANDROID, IS_TOUCH_ENV } from '../../../util/environment';
import { pick } from '../../../util/iteratees';
@@ -59,7 +56,6 @@ import {
getUserColorKey,
} from '../../../modules/helpers';
import buildClassName from '../../../util/buildClassName';
-import windowSize from '../../../util/windowSize';
import useEnsureMessage from '../../../hooks/useEnsureMessage';
import useContextMenuHandlers from '../../../hooks/useContextMenuHandlers';
import { renderMessageText } from '../../common/helpers/renderMessageText';
@@ -67,14 +63,15 @@ import { ROUND_VIDEO_DIMENSIONS } from '../../common/helpers/mediaDimensions';
import { buildContentClassName, isEmojiOnlyMessage } from './helpers/buildContentClassName';
import { getMinMediaWidth, calculateMediaDimensions } from './helpers/mediaDimensions';
import { calculateAlbumLayout } from './helpers/calculateAlbumLayout';
-import { preventMessageInputBlur } from '../helpers/preventMessageInputBlur';
import renderText from '../../common/helpers/renderText';
import calculateAuthorWidth from './helpers/calculateAuthorWidth';
import { ObserveFn, useOnIntersect } from '../../../hooks/useIntersectionObserver';
-import useFocusMessage from './hooks/useFocusMessage';
import useLang from '../../../hooks/useLang';
import useShowTransition from '../../../hooks/useShowTransition';
import useFlag from '../../../hooks/useFlag';
+import useFocusMessage from './hooks/useFocusMessage';
+import useOuterHandlers from './hooks/useOuterHandlers';
+import useInnerHandlers from './hooks/useInnerHandlers';
import Button from '../../ui/Button';
import Avatar from '../../common/Avatar';
@@ -157,13 +154,7 @@ type StateProps = {
shouldLoopStickers?: boolean;
};
-type DispatchProps = Pick;
+type DispatchProps = Pick;
const NBSP = '\u00A0';
const GROUP_MESSAGE_HOVER_ATTRIBUTE = 'data-is-document-group-hover';
@@ -173,7 +164,6 @@ const APPENDIX_OWN = '';
const APPEARANCE_DELAY = 10;
const NO_MEDIA_CORNERS_THRESHOLD = 18;
-const ANDROID_KEYBOARD_HIDE_DELAY_MS = 350;
const Message: FC = ({
message,
@@ -222,20 +212,9 @@ const Message: FC = ({
shouldAutoLoadMedia,
shouldAutoPlayMedia,
shouldLoopStickers,
- focusMessage,
- openMediaViewer,
- openAudioPlayer,
- openUserInfo,
- openChat,
- cancelSendingMessage,
- markMessagesRead,
- sendPollVote,
toggleMessageSelection,
- setReplyingToId,
- openForwardMenu,
clickInlineButton,
disableContextMenuHint,
- showNotification,
}) => {
// eslint-disable-next-line no-null/no-null
const ref = useRef(null);
@@ -270,7 +249,7 @@ const Message: FC = ({
}, [appearanceOrder, markShown, noAppearanceAnimation]);
const { transitionClassNames } = useShowTransition(isShown, undefined, noAppearanceAnimation, false);
- const { chatId, id: messageId, threadInfo } = message;
+ const { id: messageId, chatId, threadInfo } = message;
const isLocal = isMessageLocal(message);
const isOwn = isOwnMessage(message);
@@ -279,7 +258,7 @@ const Message: FC = ({
const hasThread = Boolean(threadInfo) && messageListType === 'thread';
const { forwardInfo, viaBotId } = message;
const asForwarded = forwardInfo && !isChatWithSelf && !forwardInfo.isLinkedChannelPost;
- const isInDocumentGroup = !!message.groupedId && !message.isInAlbum;
+ const isInDocumentGroup = Boolean(message.groupedId) && !message.isInAlbum;
const isAlbum = Boolean(album) && album!.messages.length > 1;
const {
text, photo, video, audio, voice, document, sticker, contact, poll, webPage, invoice,
@@ -303,6 +282,68 @@ const Message: FC = ({
const avatarPeer = forwardInfo && (isChatWithSelf || !sender) ? originSender : sender;
const senderPeer = forwardInfo ? originSender : sender;
+ const selectMessage = useCallback((e?: React.MouseEvent, groupedId?: string) => {
+ if (isLocal) {
+ return;
+ }
+
+ toggleMessageSelection({
+ messageId,
+ groupedId,
+ ...(e && e.shiftKey && { withShift: true }),
+ ...(isAlbum && { childMessageIds: album!.messages.map(({ id }) => id) }),
+ });
+ }, [isLocal, toggleMessageSelection, messageId, isAlbum, album]);
+
+ const {
+ handleMouseDown,
+ handleClick,
+ handleContextMenu,
+ handleDoubleClick,
+ handleContentDoubleClick,
+ isSwiped,
+ } = useOuterHandlers(
+ selectMessage,
+ ref,
+ messageId,
+ isLocal,
+ isAlbum,
+ Boolean(isInSelectMode),
+ onContextMenu,
+ handleBeforeContextMenu,
+ );
+
+ const {
+ handleAvatarClick,
+ handleSenderClick,
+ handleViaBotClick,
+ handleReplyClick,
+ handleMediaClick,
+ handleAudioPlay,
+ handleAlbumMediaClick,
+ handleMetaClick,
+ handleReadMedia,
+ handleCancelUpload,
+ handleVoteSend,
+ handleGroupForward,
+ handleForward,
+ handleFocus,
+ handleFocusForwarded,
+ handleDocumentGroupSelectAll,
+ } = useInnerHandlers(
+ lang,
+ selectMessage,
+ message,
+ chatId,
+ threadId,
+ isInDocumentGroup,
+ Boolean(isScheduled),
+ album,
+ avatarPeer,
+ senderPeer,
+ botSender,
+ );
+
const containerClassName = buildClassName(
'Message message-list-item',
isFirstInGroup && 'first-in-group',
@@ -325,6 +366,7 @@ const Message: FC = ({
isInSelectMode && 'is-in-selection-mode',
isThreadTop && 'is-thread-top',
Boolean(message.inlineButtons) && 'has-inline-buttons',
+ isSwiped && 'is-swiped',
transitionClassNames,
);
const contentClassName = buildContentClassName(message, {
@@ -351,175 +393,6 @@ const Message: FC = ({
appendixRef.current.innerHTML = isOwn ? APPENDIX_OWN : APPENDIX_NOT_OWN;
}, [isOwn, withAppendix]);
- const handleGroupDocumentMessagesSelect = useCallback((e: React.MouseEvent) => {
- e.stopPropagation();
-
- toggleMessageSelection({
- messageId,
- groupedId: message.groupedId,
- });
- }, [messageId, message.groupedId, toggleMessageSelection]);
-
- const handleMessageSelect = useCallback((e?: React.MouseEvent) => {
- if (isLocal) {
- return;
- }
-
- const params = isAlbum && album && album.messages
- ? {
- messageId,
- childMessageIds: album.messages.map(({ id }) => id),
- withShift: e && e.shiftKey,
- }
- : { messageId, withShift: e && e.shiftKey };
- toggleMessageSelection(params);
- }, [isLocal, isAlbum, album, messageId, toggleMessageSelection]);
-
- const handleContainerDoubleClick = useCallback(() => {
- setReplyingToId({ messageId });
- }, [setReplyingToId, messageId]);
-
- const handleContentDoubleClick = useCallback((e: React.MouseEvent) => {
- e.stopPropagation();
- }, []);
-
- const handleMouseDown = (e: React.MouseEvent) => {
- preventMessageInputBlur(e);
-
- if (!isLocal) {
- handleBeforeContextMenu(e);
- }
- };
-
- const handleAvatarClick = useCallback(() => {
- if (!avatarPeer) {
- return;
- }
-
- if (isChatPrivate(avatarPeer.id)) {
- openUserInfo({ id: avatarPeer.id });
- } else {
- openChat({ id: avatarPeer.id });
- }
- }, [avatarPeer, openUserInfo, openChat]);
-
- const handleSenderClick = useCallback(() => {
- if (!senderPeer) {
- showNotification({ message: lang('HidAccount') });
-
- return;
- }
-
- if (isChatPrivate(senderPeer.id)) {
- openUserInfo({ id: senderPeer.id });
- } else {
- openChat({ id: senderPeer.id });
- }
- }, [senderPeer, showNotification, lang, openUserInfo, openChat]);
-
- const handleViaBotClick = useCallback(() => {
- if (!botSender) {
- return;
- }
-
- openUserInfo({ id: botSender.id });
- }, [botSender, openUserInfo]);
-
- const handleReplyClick = useCallback((): void => {
- focusMessage({
- chatId, threadId, messageId: message.replyToMessageId, replyMessageId: messageId,
- });
- }, [focusMessage, chatId, threadId, message.replyToMessageId, messageId]);
-
- const handleMediaClick = useCallback((): void => {
- openMediaViewer({
- chatId, threadId, messageId, origin: isScheduled ? MediaViewerOrigin.ScheduledInline : MediaViewerOrigin.Inline,
- });
- }, [chatId, threadId, messageId, openMediaViewer, isScheduled]);
-
- const handleAudioPlay = useCallback((): void => {
- openAudioPlayer({ chatId, messageId });
- }, [chatId, messageId, openAudioPlayer]);
-
- const handleAlbumMediaClick = useCallback((albumMessageId: number): void => {
- openMediaViewer({
- chatId,
- threadId,
- messageId: albumMessageId,
- origin: isScheduled ? MediaViewerOrigin.ScheduledAlbum : MediaViewerOrigin.Album,
- });
- }, [chatId, threadId, openMediaViewer, isScheduled]);
-
- const handleClick = useCallback((e: React.MouseEvent) => {
- const target = e.target as HTMLDivElement;
- if (!target.classList.contains('text-content') && !target.classList.contains('Message')) {
- return;
- }
-
- if (IS_ANDROID) {
- if (windowSize.getIsKeyboardVisible()) {
- setTimeout(() => {
- onContextMenu(e);
- }, ANDROID_KEYBOARD_HIDE_DELAY_MS);
- } else {
- onContextMenu(e);
- }
- } else {
- onContextMenu(e);
- }
- }, [onContextMenu]);
-
- const handleContextMenu = useCallback((e: React.MouseEvent) => {
- if (IS_ANDROID) {
- handleMessageSelect(e);
- } else {
- onContextMenu(e);
- }
- }, [onContextMenu, handleMessageSelect]);
-
- const handleReadMedia = useCallback((): void => {
- markMessagesRead({ messageIds: [messageId] });
- }, [messageId, markMessagesRead]);
-
- const handleCancelUpload = useCallback(() => {
- cancelSendingMessage({ chatId, messageId });
- }, [cancelSendingMessage, chatId, messageId]);
-
- const handleVoteSend = useCallback((options: string[]) => {
- sendPollVote({ chatId, messageId, options });
- }, [chatId, messageId, sendPollVote]);
-
- const handleGroupForward = useCallback(() => {
- openForwardMenu({ fromChatId: chatId, groupedId: message.groupedId });
- }, [openForwardMenu, chatId, message.groupedId]);
-
- const handleForward = useCallback(() => {
- if (album && album.messages) {
- const messageIds = album.messages.map(({ id }) => id);
- openForwardMenu({ fromChatId: chatId, messageIds });
- } else {
- openForwardMenu({ fromChatId: chatId, messageIds: [messageId] });
- }
- }, [album, openForwardMenu, chatId, messageId]);
-
- const handleFocus = useCallback(() => {
- focusMessage({
- chatId, threadId: MAIN_THREAD_ID, messageId,
- });
- }, [focusMessage, chatId, messageId]);
-
- const handleFocusForwarded = useCallback(() => {
- if (isInDocumentGroup) {
- focusMessage({
- chatId: forwardInfo!.fromChatId, groupedId: message.groupedId, groupedChatId: chatId,
- });
- return;
- }
- focusMessage({
- chatId: forwardInfo!.fromChatId, messageId: forwardInfo!.fromMessageId,
- });
- }, [focusMessage, forwardInfo, message, chatId, isInDocumentGroup]);
-
let style = '';
let calculatedWidth;
let noMediaCorners = false;
@@ -704,7 +577,7 @@ const Message: FC = ({
message={message}
outgoingStatus={outgoingStatus}
signature={signature}
- onClick={handleMessageSelect}
+ onClick={handleMetaClick}
/>
)}
@@ -788,10 +661,10 @@ const Message: FC = ({
// @ts-ignore teact feature
style={metaSafeAuthorWidth ? `--meta-safe-author-width: ${metaSafeAuthorWidth}px` : undefined}
data-message-id={messageId}
- onClick={isInSelectMode ? handleMessageSelect : IS_ANDROID ? handleClick : undefined}
- onDoubleClick={!isInSelectMode ? handleContainerDoubleClick : undefined}
- onMouseDown={!isInSelectMode ? handleMouseDown : undefined}
- onContextMenu={!isInSelectMode && !isLocal ? handleContextMenu : undefined}
+ onMouseDown={handleMouseDown}
+ onClick={handleClick}
+ onContextMenu={handleContextMenu}
+ onDoubleClick={handleDoubleClick}
onMouseEnter={isInDocumentGroup && !isLastInDocumentGroup ? handleDocumentGroupMouseEnter : undefined}
onMouseLeave={isInDocumentGroup && !isLastInDocumentGroup ? handleDocumentGroupMouseLeave : undefined}
>
@@ -810,7 +683,7 @@ const Message: FC = ({
{!isLocal && isLastInDocumentGroup && (
{isGroupSelected && (
@@ -820,7 +693,6 @@ const Message: FC
= ({
{withAvatar && renderAvatar()}
= ({
message={message}
outgoingStatus={outgoingStatus}
signature={signature}
- onClick={handleMessageSelect}
+ onClick={handleMetaClick}
/>
)}
{canShowActionButton && canForward ? (
@@ -999,19 +871,8 @@ export default memo(withGlobal(
};
},
(setGlobal, actions): DispatchProps => pick(actions, [
- 'focusMessage',
- 'openMediaViewer',
- 'openAudioPlayer',
- 'cancelSendingMessage',
- 'openUserInfo',
- 'openChat',
- 'markMessagesRead',
- 'sendPollVote',
'toggleMessageSelection',
- 'setReplyingToId',
- 'openForwardMenu',
'clickInlineButton',
'disableContextMenuHint',
- 'showNotification',
]),
)(Message));
diff --git a/src/components/middle/message/MessageMeta.tsx b/src/components/middle/message/MessageMeta.tsx
index 9fad4aca0..f8375cdc7 100644
--- a/src/components/middle/message/MessageMeta.tsx
+++ b/src/components/middle/message/MessageMeta.tsx
@@ -15,7 +15,7 @@ type OwnProps = {
message: ApiMessage;
outgoingStatus?: ApiMessageOutgoingStatus;
signature?: string;
- onClick: () => void;
+ onClick: (e: React.MouseEvent) => void;
};
const MessageMeta: FC = ({
diff --git a/src/components/middle/message/hooks/useInnerHandlers.ts b/src/components/middle/message/hooks/useInnerHandlers.ts
new file mode 100644
index 000000000..510e31b0f
--- /dev/null
+++ b/src/components/middle/message/hooks/useInnerHandlers.ts
@@ -0,0 +1,159 @@
+import React, { useCallback } from '../../../../lib/teact/teact';
+import { getDispatch } from '../../../../lib/teact/teactn';
+
+import { isChatPrivate } from '../../../../modules/helpers';
+import { IAlbum, MediaViewerOrigin } from '../../../../types';
+import {
+ ApiChat, ApiMessage, ApiUser, MAIN_THREAD_ID,
+} from '../../../../api/types';
+import { LangFn } from '../../../../hooks/useLang';
+
+export default function useInnerHandlers(
+ lang: LangFn,
+ selectMessage: (e: React.MouseEvent, groupedId?: string) => void,
+ message: ApiMessage,
+ chatId: number,
+ threadId: number,
+ isInDocumentGroup: boolean,
+ isScheduled?: boolean,
+ album?: IAlbum,
+ avatarPeer?: ApiUser | ApiChat,
+ senderPeer?: ApiUser | ApiChat,
+ botSender?: ApiUser,
+) {
+ const {
+ openUserInfo, openChat, showNotification, focusMessage, openMediaViewer, openAudioPlayer,
+ markMessagesRead, cancelSendingMessage, sendPollVote, openForwardMenu,
+ } = getDispatch();
+
+ const {
+ id: messageId, forwardInfo, replyToMessageId, groupedId,
+ } = message;
+
+ const handleAvatarClick = useCallback(() => {
+ if (!avatarPeer) {
+ return;
+ }
+
+ if (isChatPrivate(avatarPeer.id)) {
+ openUserInfo({ id: avatarPeer.id });
+ } else {
+ openChat({ id: avatarPeer.id });
+ }
+ }, [avatarPeer, openUserInfo, openChat]);
+
+ const handleSenderClick = useCallback(() => {
+ if (!senderPeer) {
+ showNotification({ message: lang('HidAccount') });
+
+ return;
+ }
+
+ if (isChatPrivate(senderPeer.id)) {
+ openUserInfo({ id: senderPeer.id });
+ } else {
+ openChat({ id: senderPeer.id });
+ }
+ }, [senderPeer, showNotification, lang, openUserInfo, openChat]);
+
+ const handleViaBotClick = useCallback(() => {
+ if (!botSender) {
+ return;
+ }
+
+ openUserInfo({ id: botSender.id });
+ }, [botSender, openUserInfo]);
+
+ const handleReplyClick = useCallback((): void => {
+ focusMessage({
+ chatId, threadId, messageId: replyToMessageId, replyMessageId: messageId,
+ });
+ }, [focusMessage, chatId, threadId, replyToMessageId, messageId]);
+
+ const handleMediaClick = useCallback((): void => {
+ openMediaViewer({
+ chatId, threadId, messageId, origin: isScheduled ? MediaViewerOrigin.ScheduledInline : MediaViewerOrigin.Inline,
+ });
+ }, [chatId, threadId, messageId, openMediaViewer, isScheduled]);
+
+ const handleAudioPlay = useCallback((): void => {
+ openAudioPlayer({ chatId, messageId });
+ }, [chatId, messageId, openAudioPlayer]);
+
+ const handleAlbumMediaClick = useCallback((albumMessageId: number): void => {
+ openMediaViewer({
+ chatId,
+ threadId,
+ messageId: albumMessageId,
+ origin: isScheduled ? MediaViewerOrigin.ScheduledAlbum : MediaViewerOrigin.Album,
+ });
+ }, [chatId, threadId, openMediaViewer, isScheduled]);
+
+ const handleReadMedia = useCallback((): void => {
+ markMessagesRead({ messageIds: [messageId] });
+ }, [messageId, markMessagesRead]);
+
+ const handleCancelUpload = useCallback(() => {
+ cancelSendingMessage({ chatId, messageId });
+ }, [cancelSendingMessage, chatId, messageId]);
+
+ const handleVoteSend = useCallback((options: string[]) => {
+ sendPollVote({ chatId, messageId, options });
+ }, [chatId, messageId, sendPollVote]);
+
+ const handleGroupForward = useCallback(() => {
+ openForwardMenu({ fromChatId: chatId, groupedId });
+ }, [openForwardMenu, chatId, groupedId]);
+
+ const handleForward = useCallback(() => {
+ if (album && album.messages) {
+ const messageIds = album.messages.map(({ id }) => id);
+ openForwardMenu({ fromChatId: chatId, messageIds });
+ } else {
+ openForwardMenu({ fromChatId: chatId, messageIds: [messageId] });
+ }
+ }, [album, openForwardMenu, chatId, messageId]);
+
+ const handleFocus = useCallback(() => {
+ focusMessage({
+ chatId, threadId: MAIN_THREAD_ID, messageId,
+ });
+ }, [focusMessage, chatId, messageId]);
+
+ const handleFocusForwarded = useCallback(() => {
+ if (isInDocumentGroup) {
+ focusMessage({
+ chatId: forwardInfo!.fromChatId, groupedId, groupedChatId: chatId,
+ });
+ return;
+ }
+ focusMessage({
+ chatId: forwardInfo!.fromChatId, messageId: forwardInfo!.fromMessageId,
+ });
+ }, [isInDocumentGroup, focusMessage, forwardInfo, groupedId, chatId]);
+
+ const selectWithGroupedId = useCallback((e: React.MouseEvent) => {
+ e.stopPropagation();
+
+ selectMessage(e, groupedId);
+ }, [selectMessage, groupedId]);
+
+ return {
+ handleAvatarClick,
+ handleSenderClick,
+ handleViaBotClick,
+ handleReplyClick,
+ handleMediaClick,
+ handleAudioPlay,
+ handleAlbumMediaClick,
+ handleMetaClick: selectWithGroupedId,
+ handleReadMedia,
+ handleCancelUpload,
+ handleVoteSend,
+ handleGroupForward,
+ handleForward,
+ handleFocus,
+ handleFocusForwarded,
+ handleDocumentGroupSelectAll: selectWithGroupedId,
+ };
+}
diff --git a/src/components/middle/message/hooks/useOuterHandlers.ts b/src/components/middle/message/hooks/useOuterHandlers.ts
new file mode 100644
index 000000000..5cf61fd8b
--- /dev/null
+++ b/src/components/middle/message/hooks/useOuterHandlers.ts
@@ -0,0 +1,112 @@
+import { RefObject } from 'react';
+import React, { useEffect } from '../../../../lib/teact/teact';
+import { getDispatch } from '../../../../lib/teact/teactn';
+
+import { IS_ANDROID, IS_TOUCH_ENV } from '../../../../util/environment';
+import windowSize from '../../../../util/windowSize';
+import { captureEvents, SwipeDirection } from '../../../../util/captureEvents';
+import useFlag from '../../../../hooks/useFlag';
+import { preventMessageInputBlur } from '../../helpers/preventMessageInputBlur';
+
+const ANDROID_KEYBOARD_HIDE_DELAY_MS = 350;
+const SWIPE_ANIMATION_DURATION = 200;
+
+export default function useOuterHandlers(
+ selectMessage: (e?: React.MouseEvent, groupedId?: string) => void,
+ containerRef: RefObject,
+ messageId: number,
+ isLocal: boolean,
+ isAlbum: boolean,
+ isInSelectMode: boolean,
+ onContextMenu: (e: React.MouseEvent) => void,
+ handleBeforeContextMenu: (e: React.MouseEvent) => void,
+) {
+ const { setReplyingToId } = getDispatch();
+
+ const [isSwiped, markSwiped, unmarkSwiped] = useFlag();
+
+ function handleMouseDown(e: React.MouseEvent) {
+ preventMessageInputBlur(e);
+
+ if (!isLocal) {
+ handleBeforeContextMenu(e);
+ }
+ }
+
+ function handleClick(e: React.MouseEvent) {
+ if (isInSelectMode && !isLocal) {
+ selectMessage(e);
+ } else if (IS_ANDROID) {
+ const target = e.target as HTMLDivElement;
+ if (!target.classList.contains('text-content') && !target.classList.contains('Message')) {
+ return;
+ }
+
+ if (windowSize.getIsKeyboardVisible()) {
+ setTimeout(() => {
+ onContextMenu(e);
+ }, ANDROID_KEYBOARD_HIDE_DELAY_MS);
+ } else {
+ onContextMenu(e);
+ }
+ }
+ }
+
+ function handleContextMenu(e: React.MouseEvent) {
+ if (IS_ANDROID) {
+ selectMessage();
+ } else {
+ onContextMenu(e);
+ }
+ }
+
+ function handleContainerDoubleClick() {
+ setReplyingToId({ messageId });
+ }
+
+ function stopPropagation(e: React.MouseEvent) {
+ e.stopPropagation();
+ }
+
+ useEffect(() => {
+ if (!IS_TOUCH_ENV || isInSelectMode) {
+ return undefined;
+ }
+
+ let startedAt: number | undefined;
+ return captureEvents(containerRef.current!, {
+ onSwipe: ((e, direction) => {
+ if (direction === SwipeDirection.Left) {
+ if (!startedAt) {
+ startedAt = Date.now();
+ }
+
+ markSwiped();
+ } else if (direction === SwipeDirection.Right) {
+ startedAt = undefined;
+
+ unmarkSwiped();
+ }
+ }),
+ onRelease: () => {
+ if (!startedAt) {
+ return;
+ }
+
+ setReplyingToId({ messageId });
+
+ setTimeout(unmarkSwiped, Math.max(0, SWIPE_ANIMATION_DURATION - (Date.now() - startedAt)));
+ startedAt = undefined;
+ },
+ });
+ }, [containerRef, isInSelectMode, messageId, setReplyingToId, markSwiped, unmarkSwiped]);
+
+ return {
+ handleMouseDown: !isInSelectMode ? handleMouseDown : undefined,
+ handleClick,
+ handleContextMenu: !isInSelectMode && !isLocal ? handleContextMenu : undefined,
+ handleDoubleClick: !isInSelectMode ? handleContainerDoubleClick : undefined,
+ handleContentDoubleClick: !IS_TOUCH_ENV ? stopPropagation : undefined,
+ isSwiped,
+ };
+}
diff --git a/src/global/types.ts b/src/global/types.ts
index 95acf0691..e410fe5e3 100644
--- a/src/global/types.ts
+++ b/src/global/types.ts
@@ -19,7 +19,6 @@ import {
ApiSession,
ApiNewPoll,
ApiInviteInfo,
- ApiFieldError,
} from '../api/types';
import {
FocusDirection,