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:
committed by
GitHub
parent
34236c9572
commit
770d897403
@@ -20,7 +20,7 @@ const ExecutionProcessesContext =
|
|||||||
createContext<ExecutionProcessesContextType | null>(null);
|
createContext<ExecutionProcessesContextType | null>(null);
|
||||||
|
|
||||||
export const ExecutionProcessesProvider: React.FC<{
|
export const ExecutionProcessesProvider: React.FC<{
|
||||||
attemptId: string;
|
attemptId: string | undefined;
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
}> = ({ attemptId, children }) => {
|
}> = ({ attemptId, children }) => {
|
||||||
const {
|
const {
|
||||||
|
|||||||
@@ -1,5 +1,11 @@
|
|||||||
import { SplitSide } from '@git-diff-view/react';
|
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';
|
import { genId } from '@/utils/id';
|
||||||
|
|
||||||
export interface ReviewComment {
|
export interface ReviewComment {
|
||||||
@@ -40,10 +46,20 @@ export function useReview() {
|
|||||||
return context;
|
return context;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function ReviewProvider({ children }: { children: ReactNode }) {
|
export function ReviewProvider({
|
||||||
|
children,
|
||||||
|
attemptId,
|
||||||
|
}: {
|
||||||
|
children: ReactNode;
|
||||||
|
attemptId?: string;
|
||||||
|
}) {
|
||||||
const [comments, setComments] = useState<ReviewComment[]>([]);
|
const [comments, setComments] = useState<ReviewComment[]>([]);
|
||||||
const [drafts, setDrafts] = useState<Record<string, ReviewDraft>>({});
|
const [drafts, setDrafts] = useState<Record<string, ReviewDraft>>({});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
return () => clearComments();
|
||||||
|
}, [attemptId]);
|
||||||
|
|
||||||
const addComment = (comment: Omit<ReviewComment, 'id'>) => {
|
const addComment = (comment: Omit<ReviewComment, 'id'>) => {
|
||||||
const newComment: ReviewComment = {
|
const newComment: ReviewComment = {
|
||||||
...comment,
|
...comment,
|
||||||
|
|||||||
@@ -21,15 +21,19 @@ interface UseExecutionProcessesResult {
|
|||||||
* Live updates arrive at /execution_processes/<id> via add/replace/remove operations.
|
* Live updates arrive at /execution_processes/<id> via add/replace/remove operations.
|
||||||
*/
|
*/
|
||||||
export const useExecutionProcesses = (
|
export const useExecutionProcesses = (
|
||||||
taskAttemptId: string,
|
taskAttemptId: string | undefined,
|
||||||
opts?: { showSoftDeleted?: boolean }
|
opts?: { showSoftDeleted?: boolean }
|
||||||
): UseExecutionProcessesResult => {
|
): UseExecutionProcessesResult => {
|
||||||
const showSoftDeleted = opts?.showSoftDeleted;
|
const showSoftDeleted = opts?.showSoftDeleted;
|
||||||
const params = new URLSearchParams({ task_attempt_id: taskAttemptId });
|
let endpoint: string | undefined;
|
||||||
if (typeof showSoftDeleted === 'boolean') {
|
|
||||||
params.set('show_soft_deleted', String(showSoftDeleted));
|
if (taskAttemptId) {
|
||||||
|
const params = new URLSearchParams({ task_attempt_id: taskAttemptId });
|
||||||
|
if (typeof showSoftDeleted === 'boolean') {
|
||||||
|
params.set('show_soft_deleted', String(showSoftDeleted));
|
||||||
|
}
|
||||||
|
endpoint = `/api/execution-processes/stream/ws?${params.toString()}`;
|
||||||
}
|
}
|
||||||
const endpoint = `/api/execution-processes/stream/ws?${params.toString()}`;
|
|
||||||
|
|
||||||
const initialData = useCallback(
|
const initialData = useCallback(
|
||||||
(): ExecutionProcessState => ({ execution_processes: {} }),
|
(): ExecutionProcessState => ({ execution_processes: {} }),
|
||||||
|
|||||||
@@ -1029,36 +1029,25 @@ export function ProjectTasks() {
|
|||||||
|
|
||||||
const effectiveMode: LayoutMode = selectedSharedTask ? null : mode;
|
const effectiveMode: LayoutMode = selectedSharedTask ? null : mode;
|
||||||
|
|
||||||
const attemptArea =
|
const attemptArea = (
|
||||||
attempt && selectedTask ? (
|
<GitOperationsProvider attemptId={attempt?.id}>
|
||||||
<GitOperationsProvider attemptId={attempt.id}>
|
<ClickedElementsProvider attempt={attempt}>
|
||||||
<ClickedElementsProvider attempt={attempt}>
|
<ReviewProvider attemptId={attempt?.id}>
|
||||||
<ReviewProvider key={attempt.id}>
|
<ExecutionProcessesProvider attemptId={attempt?.id}>
|
||||||
<ExecutionProcessesProvider key={attempt.id} attemptId={attempt.id}>
|
<TasksLayout
|
||||||
<TasksLayout
|
kanban={kanbanContent}
|
||||||
kanban={kanbanContent}
|
attempt={attemptContent}
|
||||||
attempt={attemptContent}
|
aux={auxContent}
|
||||||
aux={auxContent}
|
isPanelOpen={isPanelOpen}
|
||||||
isPanelOpen={isPanelOpen}
|
mode={effectiveMode}
|
||||||
mode={effectiveMode}
|
isMobile={isMobile}
|
||||||
isMobile={isMobile}
|
rightHeader={rightHeader}
|
||||||
rightHeader={rightHeader}
|
/>
|
||||||
/>
|
</ExecutionProcessesProvider>
|
||||||
</ExecutionProcessesProvider>
|
</ReviewProvider>
|
||||||
</ReviewProvider>
|
</ClickedElementsProvider>
|
||||||
</ClickedElementsProvider>
|
</GitOperationsProvider>
|
||||||
</GitOperationsProvider>
|
);
|
||||||
) : (
|
|
||||||
<TasksLayout
|
|
||||||
kanban={kanbanContent}
|
|
||||||
attempt={attemptContent}
|
|
||||||
aux={auxContent}
|
|
||||||
isPanelOpen={isPanelOpen}
|
|
||||||
mode={effectiveMode}
|
|
||||||
isMobile={isMobile}
|
|
||||||
rightHeader={rightHeader}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="min-h-full h-full flex flex-col">
|
<div className="min-h-full h-full flex flex-col">
|
||||||
|
|||||||
Reference in New Issue
Block a user