From 22b8f5454b37e0ecc358996ef07d11fca06b8af0 Mon Sep 17 00:00:00 2001 From: Hampus Date: Mon, 5 Oct 2026 21:34:05 +0200 Subject: [PATCH] fix(app): skip forwarded messages when editing with arrow up (#3234) --- .../messaging/hooks/useTextareaKeyboard.ts | 170 ------------------ .../messaging/state/MessagingMessages.ts | 7 +- 2 files changed, 6 insertions(+), 171 deletions(-) delete mode 100644 fluxer_app/src/features/messaging/hooks/useTextareaKeyboard.ts diff --git a/fluxer_app/src/features/messaging/hooks/useTextareaKeyboard.ts b/fluxer_app/src/features/messaging/hooks/useTextareaKeyboard.ts deleted file mode 100644 index ff7804eba..000000000 --- a/fluxer_app/src/features/messaging/hooks/useTextareaKeyboard.ts +++ /dev/null @@ -1,170 +0,0 @@ -// SPDX-License-Identifier: AGPL-3.0-or-later - -import * as MessageCommands from '@app/features/messaging/commands/MessageCommands'; -import type {Message} from '@app/features/messaging/models/MessagingMessage'; -import MessageEdit from '@app/features/messaging/state/MessageEdit'; -import MessageFocus from '@app/features/messaging/state/MessageFocus'; -import {insertTextAtCursor} from '@app/features/messaging/utils/TextInputEditUtils'; -import {ComponentBus} from '@app/features/platform/utils/ComponentBus'; -import {canFocusTextarea, safeFocus} from '@app/features/platform/utils/InputFocusManager'; -import {isTextInputKeyEvent} from '@app/features/platform/utils/IsTextInputKeyEvent'; -import QuickSwitcher from '@app/features/search/state/QuickSwitcher'; -import ContextMenu from '@app/features/ui/state/ContextMenu'; -import KeyboardMode from '@app/features/ui/state/KeyboardMode'; -import MobileLayout from '@app/features/ui/state/MobileLayout'; -import {useCallback, useEffect} from 'react'; - -interface UseTextareaKeyboardOptions { - channelId: string; - isFocused: boolean; - textareaRef: React.RefObject; - value: string; - setValue: React.Dispatch>; - handleTextChange: (newValue: string, previousValue: string) => void; - previousValueRef: React.RefObject; - clearSegments: () => void; - replyingMessage: { - messageId: string; - mentioning: boolean; - } | null; - editingMessage: Message | null; - getLastEditableMessage: () => Message | null; - enabled: boolean; -} - -type ArrowUpEditShortcutEvent = Pick< - React.KeyboardEvent | KeyboardEvent, - 'key' | 'altKey' | 'ctrlKey' | 'metaKey' | 'shiftKey' | 'defaultPrevented' ->; - -export const shouldStartLastMessageEditFromArrowUp = (event: ArrowUpEditShortcutEvent, value: string): boolean => { - if (event.defaultPrevented) return false; - if (event.key !== 'ArrowUp') return false; - if (event.altKey || event.ctrlKey || event.metaKey || event.shiftKey) return false; - return value.length === 0; -}; -export const useTextareaKeyboard = ({ - channelId, - isFocused, - textareaRef, - value, - setValue, - handleTextChange, - previousValueRef, - clearSegments, - replyingMessage, - editingMessage, - getLastEditableMessage, - enabled, -}: UseTextareaKeyboardOptions) => { - const mobileLayout = MobileLayout; - const editingMessageId = MessageEdit.getEditingMessageId(channelId); - useEffect(() => { - if (!enabled) { - return; - } - const handleKeyDown = (event: KeyboardEvent) => { - const textarea = textareaRef.current; - if (!canFocusTextarea(textarea || undefined)) { - return; - } - if (isFocused) { - return; - } - if (QuickSwitcher.getIsOpen()) { - return; - } - if (ContextMenu.contextMenu) { - return; - } - if (KeyboardMode.keyboardModeEnabled && MessageFocus.focusedMessageId) { - return; - } - if (!isTextInputKeyEvent(event)) { - return; - } - if (!textarea) { - return; - } - if (event.key === 'Dead') { - safeFocus(textarea, true); - return; - } - event.preventDefault(); - safeFocus(textarea, true); - const inserted = insertTextAtCursor(textarea, event.key); - if (!inserted) { - setValue((prev) => { - const newValue = prev + event.key; - handleTextChange(newValue, previousValueRef.current ?? ''); - return newValue; - }); - } - }; - window.addEventListener('keydown', handleKeyDown); - return () => { - window.removeEventListener('keydown', handleKeyDown); - }; - }, [ - editingMessageId, - isFocused, - mobileLayout.enabled, - handleTextChange, - previousValueRef, - textareaRef, - setValue, - enabled, - ]); - useEffect(() => { - if (!enabled) { - return; - } - const handleKeyDown = (event: KeyboardEvent) => { - if (event.key === 'Escape') { - const isEditingInline = MessageEdit.getEditingMessageId(channelId) != null; - if (isEditingInline) { - event.preventDefault(); - event.stopPropagation(); - MessageCommands.stopEdit(channelId); - return; - } - if (editingMessage && mobileLayout.enabled) { - event.preventDefault(); - MessageCommands.stopEditMobile(channelId); - setValue(''); - clearSegments(); - } else if (replyingMessage) { - event.preventDefault(); - MessageCommands.stopReply(channelId); - } else { - event.preventDefault(); - ComponentBus.dispatch('ESCAPE_PRESSED'); - } - } - }; - window.addEventListener('keydown', handleKeyDown); - return () => { - window.removeEventListener('keydown', handleKeyDown); - }; - }, [channelId, replyingMessage, editingMessage, mobileLayout.enabled, clearSegments, setValue, enabled]); - const handleArrowUp = useCallback( - (event: React.KeyboardEvent) => { - if (!shouldStartLastMessageEditFromArrowUp(event, value)) { - return; - } - if (KeyboardMode.keyboardModeEnabled) { - event.preventDefault(); - ComponentBus.dispatch('FOCUS_BOTTOMMOST_MESSAGE', {channelId}); - return; - } - const message = getLastEditableMessage(); - if (!message) { - return; - } - event.preventDefault(); - MessageCommands.startEdit(channelId, message.id, message.content); - }, - [channelId, value, getLastEditableMessage], - ); - return {handleArrowUp}; -}; diff --git a/fluxer_app/src/features/messaging/state/MessagingMessages.ts b/fluxer_app/src/features/messaging/state/MessagingMessages.ts index 630c71138..f3eba537b 100644 --- a/fluxer_app/src/features/messaging/state/MessagingMessages.ts +++ b/fluxer_app/src/features/messaging/state/MessagingMessages.ts @@ -232,7 +232,12 @@ class Messages { getLastEditableMessage(channelId: string): Message | undefined { return this.getMessages(channelId).searchFromNewest((message) => { - return message.isCurrentUserAuthor() && message.state === MessageStates.SENT && message.isUserMessage(); + return ( + message.isCurrentUserAuthor() && + message.state === MessageStates.SENT && + message.isUserMessage() && + !message.messageSnapshots + ); }); }