diff --git a/demos/linearlite/src/components/IssueModal.tsx b/demos/linearlite/src/components/IssueModal.tsx index 14d21420..1c09f171 100644 --- a/demos/linearlite/src/components/IssueModal.tsx +++ b/demos/linearlite/src/components/IssueModal.tsx @@ -84,10 +84,14 @@ function IssueModal({ isOpen, onDismiss }: Props) { }, 250) } + const timeoutRef = useRef>() + useEffect(() => { - if (isOpen) { - setTimeout(() => { + if (isOpen && !timeoutRef.current) { + // eslint-disable-next-line @eslint-react/web-api/no-leaked-timeout + timeoutRef.current = setTimeout(() => { ref.current?.focus() + timeoutRef.current = undefined }, 250) } }, [isOpen]) diff --git a/demos/linearlite/src/components/Portal.tsx b/demos/linearlite/src/components/Portal.tsx index 537385d8..e6e29d48 100644 --- a/demos/linearlite/src/components/Portal.tsx +++ b/demos/linearlite/src/components/Portal.tsx @@ -7,6 +7,7 @@ export function Portal(props: { children: ReactNode }) { const { children } = props const [mounted, setMounted] = useState(false) + // eslint-disable-next-line @eslint-react/hooks-extra/no-direct-set-state-in-use-effect useEffect(() => setMounted(true), []) if (!mounted) return null diff --git a/demos/linearlite/src/pages/Board/IssueBoard.tsx b/demos/linearlite/src/pages/Board/IssueBoard.tsx index 6195fda3..919b88d9 100644 --- a/demos/linearlite/src/pages/Board/IssueBoard.tsx +++ b/demos/linearlite/src/pages/Board/IssueBoard.tsx @@ -27,13 +27,16 @@ export default function IssueBoard({ columnsLiveIssues }: IssueBoardProps) { useEffect(() => { // Reset moved issues when issues change + // eslint-disable-next-line @eslint-react/hooks-extra/no-direct-set-state-in-use-effect setMovedIssues({}) }, [columnsLiveIssues]) const issuesByStatus: Record = {} const issuesResByStatus: Record> = {} Object.entries(columnsLiveIssues).forEach(([status, liveQuery]) => { - let issuesRes = useLiveQuery(liveQuery) + // eslint-disable-next-line react-compiler/react-compiler + // eslint-disable-next-line react-hooks/rules-of-hooks + const issuesRes = useLiveQuery(liveQuery) issuesResByStatus[status] = issuesRes issuesRes.rows.forEach((issue) => { // If the issue has been moved, patch with new status and kanbanorder for sorting @@ -164,6 +167,7 @@ export default function IssueBoard({ columnsLiveIssues }: IssueBoardProps) { */ const getNewKanbanOrder = (issueBefore: Issue, issueAfter: Issue) => { const prevKanbanOrder = issueBefore?.kanbanorder + // eslint-disable-next-line prefer-const let nextKanbanOrder = issueAfter?.kanbanorder // if (nextKanbanOrder && nextKanbanOrder === prevKanbanOrder) { // // If the next issue has the same kanbanorder as the previous issue, diff --git a/demos/linearlite/src/pages/Board/index.tsx b/demos/linearlite/src/pages/Board/index.tsx index 9551fb37..41dd8265 100644 --- a/demos/linearlite/src/pages/Board/index.tsx +++ b/demos/linearlite/src/pages/Board/index.tsx @@ -1,3 +1,4 @@ +/* eslint-disable react-compiler/react-compiler */ import TopFilter from '../../components/TopFilter' import IssueBoard from './IssueBoard' import { FilterState } from '../../utils/filterState' @@ -16,6 +17,7 @@ function Board() { const totalIssuesCount = Object.values(columnsLiveIssues).reduce( (total, liveQuery) => { + // eslint-disable-next-line react-hooks/rules-of-hooks const issuesRes = useLiveQuery(liveQuery) return total + (issuesRes?.totalCount ?? 0) }, diff --git a/demos/linearlite/src/pages/Issue/index.tsx b/demos/linearlite/src/pages/Issue/index.tsx index 7463cc57..41443834 100644 --- a/demos/linearlite/src/pages/Issue/index.tsx +++ b/demos/linearlite/src/pages/Issue/index.tsx @@ -1,3 +1,4 @@ +/* eslint-disable react-compiler/react-compiler */ import { useNavigate, useLoaderData } from 'react-router-dom' import { useState, useRef, useCallback } from 'react' import { BsCloudCheck as SyncedIcon } from 'react-icons/bs' @@ -32,6 +33,35 @@ function IssuePage() { const [dirtyDescription, setDirtyDescription] = useState(null) const descriptionIsDirty = useRef(false) + // eslint-disable-next-line react-hooks/exhaustive-deps + const handleTitleChangeDebounced = useCallback( + debounce(async (title: string) => { + console.log(`handleTitleChangeDebounced`, title) + pg.sql` + UPDATE issue + SET title = ${title}, modified = ${new Date()} + WHERE id = ${issue.id} + ` + // We can't set titleIsDirty.current = false here because we haven't yet received + // the updated issue from the db + }, debounceTime), + [pg] + ) + + // eslint-disable-next-line react-hooks/exhaustive-deps + const handleDescriptionChangeDebounced = useCallback( + debounce(async (description: string) => { + pg.sql` + UPDATE issue + SET description = ${description}, modified = ${new Date()} + WHERE id = ${issue.id} + ` + // We can't set descriptionIsDirty.current = false here because we haven't yet received + // the updated issue from the db + }, debounceTime), + [pg] + ) + if (issue === undefined) { return
Loading...
} else if (issue === null) { @@ -65,20 +95,6 @@ function IssuePage() { ` } - const handleTitleChangeDebounced = useCallback( - debounce(async (title: string) => { - console.log(`handleTitleChangeDebounced`, title) - pg.sql` - UPDATE issue - SET title = ${title}, modified = ${new Date()} - WHERE id = ${issue.id} - ` - // We can't set titleIsDirty.current = false here because we haven't yet received - // the updated issue from the db - }, debounceTime), - [pg] - ) - const handleTitleChange = (title: string) => { setDirtyTitle(title) titleIsDirty.current = true @@ -86,19 +102,6 @@ function IssuePage() { handleTitleChangeDebounced(title) } - const handleDescriptionChangeDebounced = useCallback( - debounce(async (description: string) => { - pg.sql` - UPDATE issue - SET description = ${description}, modified = ${new Date()} - WHERE id = ${issue.id} - ` - // We can't set descriptionIsDirty.current = false here because we haven't yet received - // the updated issue from the db - }, debounceTime), - [pg] - ) - const handleDescriptionChange = (description: string) => { setDirtyDescription(description) descriptionIsDirty.current = true