feat: add FocusableGuardProvider for managing global focus behavior

- Introduced FocusableGuardProvider to handle focus management based on modal presence and route parameters.
- Integrated with existing focus hooks and event bus for improved user experience in modal scenarios.
- Updated RootProviders to include the new FocusableGuardProvider.

Signed-off-by: Innei <tukon479@gmail.com>
This commit is contained in:
Innei 2025-05-29 15:02:36 +08:00
parent 017387db2c
commit ee0ec4422c
No known key found for this signature in database
GPG Key ID: 0F62D33977F021F7
2 changed files with 41 additions and 0 deletions

View File

@ -0,0 +1,39 @@
import {
useGlobalFocusableScopeSelector,
useSetGlobalFocusableScope,
} from "@follow/components/common/Focusable/hooks.js"
import { useRefValue } from "@follow/hooks"
import type { EnhanceSet } from "@follow/utils"
import { EventBus } from "@follow/utils/event-bus"
import { useEffect } from "react"
import { useHasModal } from "~/components/ui/modal/stacked/hooks"
import { HotkeyScope } from "~/constants"
import { getRouteParams } from "~/hooks/biz/useRouteParams"
import { COMMAND_ID } from "~/modules/command/commands/id"
const selector = (s: EnhanceSet<string>) => s.size === 0
export const FocusableGuardProvider = () => {
const hasNoFocusable = useGlobalFocusableScopeSelector(selector)
const setGlobalFocusableScope = useSetGlobalFocusableScope()
const hasModal = useHasModal()
const hasModalRef = useRefValue(hasModal)
useEffect(() => {
const timer: NodeJS.Timeout = setTimeout(() => {
if (hasNoFocusable) {
if (hasModalRef.current) {
setGlobalFocusableScope(HotkeyScope.Modal, "append")
} else {
const { timelineId } = getRouteParams()
if (timelineId) {
EventBus.dispatch(COMMAND_ID.layout.focusToSubscription, { highlightBoundary: false })
}
}
}
}, 100)
return () => clearTimeout(timer)
}, [hasModalRef, hasNoFocusable, setGlobalFocusableScope])
return null
}

View File

@ -15,6 +15,7 @@ import { jotaiStore } from "~/lib/jotai"
import { persistConfig, queryClient } from "~/lib/query-client"
import { FollowCommandManager } from "~/modules/command/command-manager"
import { FocusableGuardProvider } from "./global-focusable-provider"
import { HotkeyProvider } from "./hotkey-provider"
import { I18nProvider } from "./i18n-provider"
import { InvalidateQueryProvider } from "./invalidate-query-provider"
@ -60,6 +61,7 @@ export const RootProviders: FC<PropsWithChildren> = ({ children }) => (
<LazyReloadPrompt />
{!IN_ELECTRON && <LazyPWAPrompt />}
</Suspense>
<FocusableGuardProvider />
</ModalStackProvider>
</I18nProvider>
</HotkeyProvider>