mirror of
https://github.com/stablyai/orca.git
synced 2026-09-28 00:02:41 +00:00
fix: clear transient ui feedback timers (#3385)
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/* oxlint-disable max-lines */
|
||||
import React, { useCallback, useDeferredValue, useMemo, useState } from 'react'
|
||||
import React, { useCallback, useDeferredValue, useEffect, useMemo, useState } from 'react'
|
||||
import { AlertTriangle, Check, Copy } from 'lucide-react'
|
||||
import { useAppStore } from '@/store'
|
||||
import { useActiveWorktree } from '@/store/selectors'
|
||||
@@ -66,6 +66,15 @@ function InstallRgGuidance({
|
||||
guidance?: string | null
|
||||
}): React.JSX.Element {
|
||||
const [copied, setCopied] = useState(false)
|
||||
|
||||
useEffect(() => {
|
||||
if (!copied) {
|
||||
return
|
||||
}
|
||||
const timeout = window.setTimeout(() => setCopied(false), 1500)
|
||||
return () => window.clearTimeout(timeout)
|
||||
}, [copied])
|
||||
|
||||
const handleCopy = useCallback(() => {
|
||||
if (!command) {
|
||||
return
|
||||
@@ -78,7 +87,6 @@ function InstallRgGuidance({
|
||||
.writeClipboardText(command)
|
||||
.then(() => {
|
||||
setCopied(true)
|
||||
setTimeout(() => setCopied(false), 1500)
|
||||
})
|
||||
.catch(() => {
|
||||
/* best-effort */
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useCallback, useState } from 'react'
|
||||
import React, { useCallback, useEffect, useState } from 'react'
|
||||
import { Copy, Check } from 'lucide-react'
|
||||
|
||||
type CodeBlockCopyButtonProps = React.HTMLAttributes<HTMLPreElement> & {
|
||||
@@ -11,6 +11,14 @@ export default function CodeBlockCopyButton({
|
||||
}: CodeBlockCopyButtonProps): React.JSX.Element {
|
||||
const [copied, setCopied] = useState(false)
|
||||
|
||||
useEffect(() => {
|
||||
if (!copied) {
|
||||
return
|
||||
}
|
||||
const timeout = window.setTimeout(() => setCopied(false), 1500)
|
||||
return () => window.clearTimeout(timeout)
|
||||
}, [copied])
|
||||
|
||||
const handleCopy = useCallback(() => {
|
||||
// Extract the text content from the nested <code> element rendered by
|
||||
// react-markdown inside <pre>. We walk the React children tree to grab the
|
||||
@@ -29,7 +37,6 @@ export default function CodeBlockCopyButton({
|
||||
.writeClipboardText(text)
|
||||
.then(() => {
|
||||
setCopied(true)
|
||||
setTimeout(() => setCopied(false), 1500)
|
||||
})
|
||||
.catch(() => {
|
||||
// Silently swallow clipboard write failures (e.g. permission denied).
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useCallback, useState } from 'react'
|
||||
import React, { useCallback, useEffect, useState } from 'react'
|
||||
import { NodeViewContent, NodeViewWrapper } from '@tiptap/react'
|
||||
import type { NodeViewProps } from '@tiptap/react'
|
||||
import { Copy, Check } from 'lucide-react'
|
||||
@@ -51,6 +51,14 @@ export function RichMarkdownCodeBlock({
|
||||
|
||||
const isMermaid = language === 'mermaid'
|
||||
|
||||
useEffect(() => {
|
||||
if (!copied) {
|
||||
return
|
||||
}
|
||||
const timeout = window.setTimeout(() => setCopied(false), 1500)
|
||||
return () => window.clearTimeout(timeout)
|
||||
}, [copied])
|
||||
|
||||
const onChange = useCallback(
|
||||
(e: React.ChangeEvent<HTMLSelectElement>) => {
|
||||
updateAttributes({ language: e.target.value })
|
||||
@@ -66,7 +74,6 @@ export function RichMarkdownCodeBlock({
|
||||
.writeClipboardText(text)
|
||||
.then(() => {
|
||||
setCopied(true)
|
||||
setTimeout(() => setCopied(false), 1500)
|
||||
})
|
||||
.catch(() => {
|
||||
// Silently swallow clipboard write failures (e.g. permission denied).
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* eslint-disable max-lines -- Why: co-locating all checks-panel sub-components (checks list,
|
||||
conflict sections, threaded PR comments) keeps the shared icon/color maps in one place. */
|
||||
import React, { useCallback, useState } from 'react'
|
||||
import React, { useCallback, useEffect, useState } from 'react'
|
||||
import {
|
||||
CircleCheck,
|
||||
CircleX,
|
||||
@@ -357,12 +357,19 @@ export function ChecksList({
|
||||
function CopyButton({ text }: { text: string }): React.JSX.Element {
|
||||
const [copied, setCopied] = useState(false)
|
||||
|
||||
useEffect(() => {
|
||||
if (!copied) {
|
||||
return
|
||||
}
|
||||
const timeout = window.setTimeout(() => setCopied(false), 1500)
|
||||
return () => window.clearTimeout(timeout)
|
||||
}, [copied])
|
||||
|
||||
const handleCopy = useCallback(
|
||||
(e: React.MouseEvent) => {
|
||||
e.stopPropagation()
|
||||
void window.api.ui.writeClipboardText(text).then(() => {
|
||||
setCopied(true)
|
||||
setTimeout(() => setCopied(false), 1500)
|
||||
})
|
||||
},
|
||||
[text]
|
||||
@@ -390,12 +397,19 @@ function ResolveButton({
|
||||
}): React.JSX.Element {
|
||||
const [loading, setLoading] = useState(false)
|
||||
|
||||
useEffect(() => {
|
||||
if (!loading) {
|
||||
return
|
||||
}
|
||||
const timeout = window.setTimeout(() => setLoading(false), 300)
|
||||
return () => window.clearTimeout(timeout)
|
||||
}, [loading])
|
||||
|
||||
const handleClick = useCallback(
|
||||
(e: React.MouseEvent) => {
|
||||
e.stopPropagation()
|
||||
setLoading(true)
|
||||
onResolve(threadId, !isResolved)
|
||||
setTimeout(() => setLoading(false), 300)
|
||||
},
|
||||
[threadId, isResolved, onResolve]
|
||||
)
|
||||
|
||||
@@ -215,6 +215,14 @@ export function McpConfigSection({ repo }: McpConfigSectionProps): React.JSX.Ele
|
||||
void loadConfigs()
|
||||
}, [loadConfigs])
|
||||
|
||||
useEffect(() => {
|
||||
if (!createConfirm) {
|
||||
return
|
||||
}
|
||||
const timeout = window.setTimeout(() => setCreateConfirm(false), 3000)
|
||||
return () => window.clearTimeout(timeout)
|
||||
}, [createConfirm])
|
||||
|
||||
const handleOpen = (config: LoadedMcpConfigInspection): void => {
|
||||
setActiveWorktree(targetWorktreeId)
|
||||
const targetGroupId = ensureWorktreeRootGroup(targetWorktreeId)
|
||||
@@ -234,7 +242,6 @@ export function McpConfigSection({ repo }: McpConfigSectionProps): React.JSX.Ele
|
||||
const handleCreateStarter = async (): Promise<void> => {
|
||||
if (!createConfirm) {
|
||||
setCreateConfirm(true)
|
||||
window.setTimeout(() => setCreateConfirm(false), 3000)
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -53,6 +53,14 @@ export function MobilePane(): React.JSX.Element {
|
||||
const [refreshingNetworkInterfaces, setRefreshingNetworkInterfaces] = useState(false)
|
||||
const [codeCopied, setCodeCopied] = useState(false)
|
||||
|
||||
useEffect(() => {
|
||||
if (!codeCopied) {
|
||||
return
|
||||
}
|
||||
const timeout = window.setTimeout(() => setCodeCopied(false), 2000)
|
||||
return () => window.clearTimeout(timeout)
|
||||
}, [codeCopied])
|
||||
|
||||
const loadDevices = useCallback(async () => {
|
||||
try {
|
||||
const result = await window.api.mobile.listDevices()
|
||||
@@ -141,7 +149,6 @@ export function MobilePane(): React.JSX.Element {
|
||||
// IPC clipboard which the rest of the app uses everywhere.
|
||||
await window.api.ui.writeClipboardText(pairingUrl)
|
||||
setCodeCopied(true)
|
||||
setTimeout(() => setCodeCopied(false), 2000)
|
||||
} catch {
|
||||
toast.error('Failed to copy pairing code')
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useState } from 'react'
|
||||
import { useEffect, useState } from 'react'
|
||||
import type { OrcaHooks, Repo, RepoHookSettings } from '../../../../shared/types'
|
||||
import { getRepoKindLabel, isFolderRepo } from '../../../../shared/repo-kind'
|
||||
import { Button } from '../ui/button'
|
||||
@@ -59,6 +59,14 @@ export function RepositoryPane({
|
||||
// request to hide every child row that does not repeat the project name.
|
||||
const forceFullPaneForRepoMatch = matchesRepositoryIdentitySearch(searchQuery, repo)
|
||||
|
||||
useEffect(() => {
|
||||
if (!copiedTemplate) {
|
||||
return
|
||||
}
|
||||
const timeout = window.setTimeout(() => setCopiedTemplate(false), 1500)
|
||||
return () => window.clearTimeout(timeout)
|
||||
}, [copiedTemplate])
|
||||
|
||||
const handleRemoveProject = (repoId: string) => {
|
||||
if (confirmingRemove === repoId) {
|
||||
removeProject(repoId)
|
||||
@@ -84,7 +92,6 @@ export function RepositoryPane({
|
||||
archive: |
|
||||
echo "Cleaning up before archive"`)
|
||||
setCopiedTemplate(true)
|
||||
window.setTimeout(() => setCopiedTemplate(false), 1500)
|
||||
}
|
||||
|
||||
const allEntries = getRepositoryPaneSearchEntries(repo)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useCallback, useRef, useState } from 'react'
|
||||
import { useCallback, useEffect, useRef, useState } from 'react'
|
||||
import { toPng } from 'html-to-image'
|
||||
import { Check, Copy, Share2 } from 'lucide-react'
|
||||
import { Button } from '../ui/button'
|
||||
@@ -21,6 +21,14 @@ export function ShareUsageButton(props: ShareUsageButtonProps): React.JSX.Elemen
|
||||
const [copied, setCopied] = useState(false)
|
||||
const [capturing, setCapturing] = useState(false)
|
||||
|
||||
useEffect(() => {
|
||||
if (!copied) {
|
||||
return
|
||||
}
|
||||
const timeout = window.setTimeout(() => setCopied(false), 2000)
|
||||
return () => window.clearTimeout(timeout)
|
||||
}, [copied])
|
||||
|
||||
const captureToClipboard = useCallback(async () => {
|
||||
if (!cardRef.current || capturing) {
|
||||
return
|
||||
@@ -42,7 +50,6 @@ export function ShareUsageButton(props: ShareUsageButtonProps): React.JSX.Elemen
|
||||
const ok = await captureToClipboard()
|
||||
if (ok) {
|
||||
setCopied(true)
|
||||
setTimeout(() => setCopied(false), 2000)
|
||||
}
|
||||
}, [captureToClipboard])
|
||||
|
||||
|
||||
Reference in New Issue
Block a user