Prevent <TasksLayout/> component from being unmounted when using (#1391)

keyboard shortcuts to navigate the kanban board.

In kanban boards with many tasks in a single column, when we use
keyboard shortcuts to select a task 'below the fold', the animation
restarts from the top of the kanban board, rather than preserving the
existing position.
This commit is contained in:
Britannio Jarrett
2025-11-27 18:41:15 +00:00
committed by GitHub
parent 34236c9572
commit 770d897403
4 changed files with 47 additions and 38 deletions

View File

@@ -20,7 +20,7 @@ const ExecutionProcessesContext =
createContext<ExecutionProcessesContextType | null>(null);
export const ExecutionProcessesProvider: React.FC<{
attemptId: string;
attemptId: string | undefined;
children: React.ReactNode;
}> = ({ attemptId, children }) => {
const {

View File

@@ -1,5 +1,11 @@
import { SplitSide } from '@git-diff-view/react';
import { createContext, useContext, useState, ReactNode } from 'react';
import {
createContext,
useContext,
useState,
ReactNode,
useEffect,
} from 'react';
import { genId } from '@/utils/id';
export interface ReviewComment {
@@ -40,10 +46,20 @@ export function useReview() {
return context;
}
export function ReviewProvider({ children }: { children: ReactNode }) {
export function ReviewProvider({
children,
attemptId,
}: {
children: ReactNode;
attemptId?: string;
}) {
const [comments, setComments] = useState<ReviewComment[]>([]);
const [drafts, setDrafts] = useState<Record<string, ReviewDraft>>({});
useEffect(() => {
return () => clearComments();
}, [attemptId]);
const addComment = (comment: Omit<ReviewComment, 'id'>) => {
const newComment: ReviewComment = {
...comment,

View File

@@ -21,15 +21,19 @@ interface UseExecutionProcessesResult {
* Live updates arrive at /execution_processes/<id> via add/replace/remove operations.
*/
export const useExecutionProcesses = (
taskAttemptId: string,
taskAttemptId: string | undefined,
opts?: { showSoftDeleted?: boolean }
): UseExecutionProcessesResult => {
const showSoftDeleted = opts?.showSoftDeleted;
let endpoint: string | undefined;
if (taskAttemptId) {
const params = new URLSearchParams({ task_attempt_id: taskAttemptId });
if (typeof showSoftDeleted === 'boolean') {
params.set('show_soft_deleted', String(showSoftDeleted));
}
const endpoint = `/api/execution-processes/stream/ws?${params.toString()}`;
endpoint = `/api/execution-processes/stream/ws?${params.toString()}`;
}
const initialData = useCallback(
(): ExecutionProcessState => ({ execution_processes: {} }),

View File

@@ -1029,12 +1029,11 @@ export function ProjectTasks() {
const effectiveMode: LayoutMode = selectedSharedTask ? null : mode;
const attemptArea =
attempt && selectedTask ? (
<GitOperationsProvider attemptId={attempt.id}>
const attemptArea = (
<GitOperationsProvider attemptId={attempt?.id}>
<ClickedElementsProvider attempt={attempt}>
<ReviewProvider key={attempt.id}>
<ExecutionProcessesProvider key={attempt.id} attemptId={attempt.id}>
<ReviewProvider attemptId={attempt?.id}>
<ExecutionProcessesProvider attemptId={attempt?.id}>
<TasksLayout
kanban={kanbanContent}
attempt={attemptContent}
@@ -1048,16 +1047,6 @@ export function ProjectTasks() {
</ReviewProvider>
</ClickedElementsProvider>
</GitOperationsProvider>
) : (
<TasksLayout
kanban={kanbanContent}
attempt={attemptContent}
aux={auxContent}
isPanelOpen={isPanelOpen}
mode={effectiveMode}
isMobile={isMobile}
rightHeader={rightHeader}
/>
);
return (