fix: own remaining ui interaction timers (#3388)

This commit is contained in:
Neil
2026-05-29 17:27:22 -07:00
committed by GitHub
parent 7242b41698
commit 38d418779e
3 changed files with 63 additions and 7 deletions
+25 -2
View File
@@ -94,6 +94,8 @@ export function UpdateCard() {
const reassuranceSeen = useAppStore((s) => s.updateReassuranceSeen)
const markReassuranceSeen = useAppStore((s) => s.markUpdateReassuranceSeen)
const hasStartedDownload = useRef(false)
const dismissAnimationTimerRef = useRef<number | null>(null)
const collapseAnimationTimerRef = useRef<number | null>(null)
const [mediaFailed, setMediaFailed] = useState(false)
const [mediaLoaded, setMediaLoaded] = useState(false)
const [installError, setInstallError] = useState<string | null>(null)
@@ -204,6 +206,17 @@ export function UpdateCard() {
return () => mq.removeEventListener('change', handler)
}, [])
useEffect(() => {
return () => {
if (dismissAnimationTimerRef.current !== null) {
window.clearTimeout(dismissAnimationTimerRef.current)
}
if (collapseAnimationTimerRef.current !== null) {
window.clearTimeout(collapseAnimationTimerRef.current)
}
}
}, [])
// ── Visibility gates ──────────────────────────────────────────────
const isUserInitiated = 'userInitiated' in status && status.userInitiated
@@ -358,7 +371,13 @@ export function UpdateCard() {
return
}
setExiting(true)
setTimeout(handleClose, 150)
if (dismissAnimationTimerRef.current !== null) {
window.clearTimeout(dismissAnimationTimerRef.current)
}
dismissAnimationTimerRef.current = window.setTimeout(() => {
dismissAnimationTimerRef.current = null
handleClose()
}, 150)
}
// Why: long-running phases (downloading, downloaded, error) minimize to the
@@ -370,7 +389,11 @@ export function UpdateCard() {
return
}
setExiting(true)
setTimeout(() => {
if (collapseAnimationTimerRef.current !== null) {
window.clearTimeout(collapseAnimationTimerRef.current)
}
collapseAnimationTimerRef.current = window.setTimeout(() => {
collapseAnimationTimerRef.current = null
setCollapsed(true)
setExiting(false)
}, 150)
@@ -68,6 +68,19 @@ export default function BrowserAddressBar({
const browserKagiSessionLink = useAppStore((s) => s.browserKagiSessionLink)
const closingRef = useRef(false)
const openedAtRef = useRef(0)
const blurCloseTimerRef = useRef<number | null>(null)
const closingResetTimerRef = useRef<number | null>(null)
useEffect(() => {
return () => {
if (blurCloseTimerRef.current !== null) {
window.clearTimeout(blurCloseTimerRef.current)
}
if (closingResetTimerRef.current !== null) {
window.clearTimeout(closingResetTimerRef.current)
}
}
}, [])
const searchEngine: SearchEngine =
(browserDefaultSearchEngine as SearchEngine | null) ?? DEFAULT_SEARCH_ENGINE
@@ -147,6 +160,10 @@ export default function BrowserAddressBar({
if (closingRef.current) {
return
}
if (blurCloseTimerRef.current !== null) {
window.clearTimeout(blurCloseTimerRef.current)
blurCloseTimerRef.current = null
}
inputRef.current?.select()
openedAtRef.current = Date.now()
setOpen(true)
@@ -164,7 +181,11 @@ export default function BrowserAddressBar({
// — producing the "flash then disappear" on first click.
const elapsed = Date.now() - openedAtRef.current
const grace = elapsed < 400
setTimeout(() => {
if (blurCloseTimerRef.current !== null) {
window.clearTimeout(blurCloseTimerRef.current)
}
blurCloseTimerRef.current = window.setTimeout(() => {
blurCloseTimerRef.current = null
if (grace && inputRef.current && document.activeElement === inputRef.current) {
return
}
@@ -178,7 +199,11 @@ export default function BrowserAddressBar({
setOpen(false)
setSelectedValueOverride(null)
onNavigate(url)
setTimeout(() => {
if (closingResetTimerRef.current !== null) {
window.clearTimeout(closingResetTimerRef.current)
}
closingResetTimerRef.current = window.setTimeout(() => {
closingResetTimerRef.current = null
closingRef.current = false
}, 100)
},
@@ -113,6 +113,17 @@ export function RuntimePairingUrlGenerator({
const networkInterfaceLoadIdRef = useRef(0)
const accessGrantLoadIdRef = useRef(0)
useEffect(() => {
if (copiedTarget === null) {
return
}
const target = copiedTarget
const timeout = window.setTimeout(() => {
setCopiedTarget((current) => (current === target ? null : current))
}, 1400)
return () => window.clearTimeout(timeout)
}, [copiedTarget])
const loadRuntimeAccessGrants = useCallback(
async (options: { showToastOnError?: boolean } = {}): Promise<void> => {
const loadId = accessGrantLoadIdRef.current + 1
@@ -240,9 +251,6 @@ export function RuntimePairingUrlGenerator({
await window.api.ui.writeClipboardText(value)
setCopiedTarget(target)
toast.success(target === 'web' ? 'Copied web client URL.' : 'Copied pairing URL.')
window.setTimeout(() => {
setCopiedTarget((current) => (current === target ? null : current))
}, 1400)
} catch (error) {
toast.error(error instanceof Error ? error.message : 'Failed to copy URL.')
}