[MM-65186] Keyboard focus is wrong when using Shift-Up to reply in thread (#34627) (#34697)

(cherry picked from commit 4fb41f3ba274f32b8e23f52e46b82e3d93f63af6)

Co-authored-by: M-ZubairAhmed <m-zubairahmed@protonmail.com>
Этот коммит содержится в:
Mattermost Build
2025-12-10 07:17:54 +02:00
коммит произвёл GitHub
родитель b3d6c0c564
Коммит f9fb13c7e6
4 изменённых файлов: 26 добавлений и 10 удалений

Просмотреть файл

@@ -11,7 +11,7 @@
// Group: @channels @keyboard_shortcuts // Group: @channels @keyboard_shortcuts
import * as messages from '../../../fixtures/messages'; import * as messages from '../../../fixtures/messages';
import * as TIMEOUTS from '../../../fixtures/timeouts'; import timeouts, * as TIMEOUTS from '../../../fixtures/timeouts';
describe('Keyboard Shortcuts', () => { describe('Keyboard Shortcuts', () => {
let testTeam; let testTeam;
@@ -377,6 +377,7 @@ describe('Keyboard Shortcuts', () => {
cy.get('body').cmdOrCtrlShortcut('{shift}L'); cy.get('body').cmdOrCtrlShortcut('{shift}L');
cy.uiGetPostTextBox().should('be.focused'); cy.uiGetPostTextBox().should('be.focused');
cy.get('[data-testid="searchBoxClose"] > .icon').click();
// # Post a message and open RHS // # Post a message and open RHS
const message = `hello${Date.now()}`; const message = `hello${Date.now()}`;
cy.postMessage(message); cy.postMessage(message);
@@ -386,6 +387,7 @@ describe('Keyboard Shortcuts', () => {
cy.uiGetReplyTextBox().focus().should('be.focused'); cy.uiGetReplyTextBox().focus().should('be.focused');
}).then(() => { }).then(() => {
// # Type CTRL/CMD+SHIFT+L // # Type CTRL/CMD+SHIFT+L
cy.wait(timeouts.ONE_SEC);
cy.get('body').cmdOrCtrlShortcut('{shift}L'); cy.get('body').cmdOrCtrlShortcut('{shift}L');
cy.uiGetPostTextBox().should('be.focused'); cy.uiGetPostTextBox().should('be.focused');
}); });

Просмотреть файл

@@ -2,7 +2,7 @@
// See LICENSE.txt for license information. // See LICENSE.txt for license information.
import type React from 'react'; import type React from 'react';
import {useCallback, useEffect} from 'react'; import {useCallback, useEffect, useRef} from 'react';
import {useDispatch, useSelector} from 'react-redux'; import {useDispatch, useSelector} from 'react-redux';
import {focusedRHS} from 'actions/views/rhs'; import {focusedRHS} from 'actions/views/rhs';
@@ -23,11 +23,10 @@ const useTextboxFocus = (
) => { ) => {
const dispatch = useDispatch(); const dispatch = useDispatch();
const hasMounted = useRef(false);
const rhsExpanded = useSelector(getIsRhsExpanded); const rhsExpanded = useSelector(getIsRhsExpanded);
const rhsOpen = useSelector(getIsRhsOpen); const rhsOpen = useSelector(getIsRhsOpen);
// We force the selector to always think it is the same value to avoid re-renders
// because we only use this value during mount.
const shouldFocusRHS = useSelector(getShouldFocusRHS, () => true); const shouldFocusRHS = useSelector(getShouldFocusRHS, () => true);
const focusTextbox = useCallback((keepFocus = false) => { const focusTextbox = useCallback((keepFocus = false) => {
@@ -48,7 +47,7 @@ const useTextboxFocus = (
textboxRef.current?.focus(); textboxRef.current?.focus();
}); });
} }
}, [canPost, textboxRef]); }, [canPost]);
const focusTextboxIfNecessary = useCallback((e: KeyboardEvent) => { const focusTextboxIfNecessary = useCallback((e: KeyboardEvent) => {
// Do not focus if the rhs is expanded and this is not the RHS // Do not focus if the rhs is expanded and this is not the RHS
@@ -91,15 +90,24 @@ const useTextboxFocus = (
focusTextbox(); focusTextbox();
}, [channelId]); }, [channelId]);
// Focus on mount
useEffect(() => { useEffect(() => {
if (isRHS && shouldFocusRHS) { if (isRHS && shouldFocusRHS) {
// If we are in the RHS and we are supposed to focus the RHS because of a reply,
// we focus the textbox and reset the shouldFocusRHS flag.
focusTextbox(); focusTextbox();
dispatch(focusedRHS()); dispatch(focusedRHS());
} else if (!isRHS && !shouldFocusRHS) { } else if (!isRHS && !shouldFocusRHS && !hasMounted.current) {
// If we are in the Center channel and we are not supposed to focus the RHS,
// we focus the textbox but only on mount.
// This is because if we focus on updates, we might steal focus from the RHS
// when the RHS focuses and resets the shouldFocusRHS flag.
focusTextbox(); focusTextbox();
} }
}, []);
if (!hasMounted.current) {
hasMounted.current = true;
}
}, [isRHS, shouldFocusRHS, focusTextbox, dispatch]);
return focusTextbox; return focusTextbox;
}; };

Просмотреть файл

@@ -160,6 +160,12 @@ export default class SidebarRight extends React.PureComponent<Props, State> {
if (this.props.isOpen && (contentChanged || (!wasOpen && isOpen))) { if (this.props.isOpen && (contentChanged || (!wasOpen && isOpen))) {
this.previousActiveElement = document.activeElement as HTMLElement; this.previousActiveElement = document.activeElement as HTMLElement;
// For RHS with textbox, don't auto-focus the first element with this approach.
// The RHS textbox will focus itself via use_textbox_focus.tsx hook with correct focus logic.
if (this.props.postRightVisible) {
return;
}
// Focus the sidebar after a tick // Focus the sidebar after a tick
setTimeout(() => { setTimeout(() => {
if (this.sidebarRight.current) { if (this.sidebarRight.current) {

Просмотреть файл

@@ -105,7 +105,7 @@ export function isMobileApp(): boolean {
return isMobile() && !isIosWeb() && !isAndroidWeb(); return isMobile() && !isIosWeb() && !isAndroidWeb();
} }
// Returns true if and only if the user is using Mattermost from either the mobile app or the web browser on a mobile device. // Returns true if and only if the user is using Mattermost from the web browser on a mobile device.
export function isMobile(): boolean { export function isMobile(): boolean {
return isIos() || isAndroid(); return isIos() || isAndroid();
} }