Skip to content

Commit e374dd6

Browse files
committed
perf: use focusable selector hooks across components
- Replaced instances of `useGlobalFocusableScope` with `useGlobalFocusableScopeSelector` for improved focus state management. - Updated various components including FeedColumn, Subview, EntryColumn, and FeedItem to utilize the new selector for better performance and clarity. - Added a new `viewportClassName` property to the SettingPageConfig interface for enhanced configuration options. Signed-off-by: Innei <tukon479@gmail.com>
1 parent f31d43d commit e374dd6

File tree

10 files changed

+42
-26
lines changed

10 files changed

+42
-26
lines changed

apps/desktop/layer/renderer/src/modules/app-layout/feed-column/desktop.tsx

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import type { DragEndEvent } from "@dnd-kit/core"
22
import { DndContext, PointerSensor, pointerWithin, useSensor, useSensors } from "@dnd-kit/core"
3-
import { useGlobalFocusableScope } from "@follow/components/common/Focusable/hooks.js"
3+
import { useGlobalFocusableScopeSelector } from "@follow/components/common/Focusable/hooks.js"
44
import { PanelSplitter } from "@follow/components/ui/divider/index.js"
55
import { Kbd } from "@follow/components/ui/kbd/Kbd.js"
66
import { RootPortal } from "@follow/components/ui/portal/index.jsx"
@@ -238,11 +238,14 @@ const FeedResponsiveResizerContainer = ({
238238
}
239239
}, [feedColumnShow])
240240

241-
const activeScopes = useGlobalFocusableScope()
241+
const when = useGlobalFocusableScopeSelector(
242+
// eslint-disable-next-line @eslint-react/hooks-extra/no-unnecessary-use-callback
243+
React.useCallback((activeScope) => !activeScope.or(...FloatingLayerScope), []),
244+
)
242245

243246
useCommandBinding({
244247
commandId: COMMAND_ID.layout.toggleSubscriptionColumn,
245-
when: !activeScopes.or(...FloatingLayerScope),
248+
when,
246249
})
247250

248251
const [delayShowSplitter, setDelayShowSplitter] = useState(feedColumnShow)

apps/desktop/layer/renderer/src/modules/app-layout/subview/index.electron.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { getReadonlyRoute } from "@follow/components/atoms/route.js"
2-
import { useGlobalFocusableScope } from "@follow/components/common/Focusable/hooks.js"
2+
import { useGlobalFocusableHasScope } from "@follow/components/common/Focusable/hooks.js"
33
import { MotionButtonBase } from "@follow/components/ui/button/index.js"
44
import { ScrollArea } from "@follow/components/ui/scroll-area/index.js"
55
import { Routes } from "@follow/constants"
@@ -71,9 +71,9 @@ function SubviewLayoutInner() {
7171
navigate(-1)
7272
}
7373
}
74-
const activeScope = useGlobalFocusableScope()
74+
7575
useHotkeys("Escape", backHandler, {
76-
enabled: activeScope.has(HotkeyScope.SubLayer),
76+
enabled: useGlobalFocusableHasScope(HotkeyScope.SubLayer),
7777
})
7878
return (
7979
<div className="relative flex size-full">

apps/desktop/layer/renderer/src/modules/entry-column/EntryColumnShortcutHandler.tsx

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import {
22
useFocusActions,
3-
useGlobalFocusableScope,
3+
useGlobalFocusableScopeSelector,
44
} from "@follow/components/common/Focusable/hooks.js"
55
import { useScrollViewElement } from "@follow/components/ui/scroll-area/hooks.js"
66
import { useRefValue } from "@follow/hooks"
@@ -24,9 +24,7 @@ export const EntryColumnShortcutHandler: FC<{
2424
}> = memo(({ data, refetch, handleScrollTo }) => {
2525
const dataRef = useRefValue(data!)
2626

27-
const activeScope = useGlobalFocusableScope()
28-
29-
const when = FocusablePresets.isTimeline(activeScope)
27+
const when = useGlobalFocusableScopeSelector(FocusablePresets.isTimeline)
3028

3129
useCommandBinding({
3230
commandId: COMMAND_ID.timeline.switchToNext,

apps/desktop/layer/renderer/src/modules/entry-column/components/mark-all-button.tsx

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1-
import { useGlobalFocusableScope } from "@follow/components/common/Focusable/hooks.js"
1+
import { useGlobalFocusableScopeSelector } from "@follow/components/common/Focusable/hooks.js"
22
import { ActionButton, Button } from "@follow/components/ui/button/index.js"
33
import { Kbd, KbdCombined } from "@follow/components/ui/kbd/Kbd.js"
44
import { useCountdown } from "@follow/hooks"
55
import { EventBus } from "@follow/utils/event-bus"
66
import { cn } from "@follow/utils/utils"
77
import type { FC, ReactNode } from "react"
8-
import { useEffect, useState } from "react"
8+
import { useCallback, useEffect, useState } from "react"
99
import { useHotkeys } from "react-hotkeys-hook"
1010
import { Trans, useTranslation } from "react-i18next"
1111
import { toast } from "sonner"
@@ -33,10 +33,17 @@ export const MarkAllReadButton = ({
3333
const { t } = useTranslation()
3434
const { t: commonT } = useTranslation("common")
3535

36-
const activeScope = useGlobalFocusableScope()
36+
// const activeScope = useGlobalFocusableScope()
37+
const when = useGlobalFocusableScopeSelector(
38+
// eslint-disable-next-line @eslint-react/hooks-extra/no-unnecessary-use-callback
39+
useCallback(
40+
(activeScope) => activeScope.or(HotkeyScope.Timeline, HotkeyScope.SubscriptionList),
41+
[],
42+
),
43+
)
3744
useCommandBinding({
3845
commandId: COMMAND_ID.subscription.markAllAsRead,
39-
when: activeScope.or(HotkeyScope.Timeline, HotkeyScope.SubscriptionList),
46+
when,
4047
})
4148

4249
useEffect(() => {

apps/desktop/layer/renderer/src/modules/entry-column/layouts/EntryItemWrapper.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { useGlobalFocusableScope } from "@follow/components/common/Focusable/hooks.js"
1+
import { useGlobalFocusableScopeSelector } from "@follow/components/common/Focusable/hooks.js"
22
import { useMobile } from "@follow/components/hooks/useMobile.js"
33
import type { FeedViewType } from "@follow/constants"
44
import { views } from "@follow/constants"
@@ -51,8 +51,8 @@ export const EntryItemWrapper: FC<
5151
({ entryId }) => entryId === entry.entries.id,
5252
[entry.entries.id],
5353
)
54-
const scope = useGlobalFocusableScope()
55-
useContextMenuActionShortCutTrigger(actionConfigs, isActive && FocusablePresets.isTimeline(scope))
54+
const when = useGlobalFocusableScopeSelector(FocusablePresets.isTimeline)
55+
useContextMenuActionShortCutTrigger(actionConfigs, isActive && when)
5656

5757
const asRead = useEntryIsRead(entry)
5858
const hoverMarkUnread = useGeneralSettingKey("hoverMarkUnread")

apps/desktop/layer/renderer/src/modules/settings/helper/EnhancedIndicator.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ import { IconTransition } from "~/components/ux/transition/icon"
88
export const EnhancedSettingsIndicator = () => {
99
const enhancedSettings = useGeneralSettingKey("enhancedSettings")
1010
const { t } = useTranslation("settings")
11+
12+
if (!enhancedSettings) return null
1113
return (
1214
<Tooltip>
1315
<TooltipTrigger>

apps/desktop/layer/renderer/src/modules/settings/utils.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ export interface SettingPageConfig {
2121
ctx: SettingPageContext,
2222
serverConfigs?: ServerConfigs | null,
2323
) => [boolean, DisableWhy]
24+
viewportClassName?: string
2425
}
2526
export const defineSettingPageData = (config: SettingPageConfig) => ({
2627
...config,

apps/desktop/layer/renderer/src/modules/subscription-column/FeedItem.tsx

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { useGlobalFocusableScope } from "@follow/components/common/Focusable/hooks.js"
1+
import { useGlobalFocusableScopeSelector } from "@follow/components/common/Focusable/hooks.js"
22
import { useMobile } from "@follow/components/hooks/useMobile.js"
33
import { OouiUserAnonymous } from "@follow/components/icons/OouiUserAnonymous.jsx"
44
import { Button } from "@follow/components/ui/button/index.js"
@@ -120,9 +120,9 @@ const FeedItemImpl = ({ view, feedId, className, isPreview }: FeedItemProps) =>
120120
view,
121121
})
122122

123-
const scope = useGlobalFocusableScope()
123+
const when = useGlobalFocusableScopeSelector(FocusablePresets.isSubscriptionList)
124124

125-
const whenTrigger = FocusablePresets.isSubscriptionList(scope) && isActive
125+
const whenTrigger = when && isActive
126126
useContextMenuActionShortCutTrigger(items, whenTrigger)
127127

128128
const [isContextMenuOpen, setIsContextMenuOpen] = useState(false)
@@ -260,8 +260,8 @@ const ListItemImpl: Component<ListItemProps> = ({
260260
const isActive = useRouteParamsSelector((routerParams) => routerParams.listId === listId)
261261
const items = useListActions({ listId, view })
262262

263-
const scope = useGlobalFocusableScope()
264-
useContextMenuActionShortCutTrigger(items, FocusablePresets.isSubscriptionList(scope) && isActive)
263+
const when = useGlobalFocusableScopeSelector(FocusablePresets.isSubscriptionList)
264+
useContextMenuActionShortCutTrigger(items, when && isActive)
265265

266266
const listUnread = useUnreadByListId(listId)
267267

@@ -369,8 +369,8 @@ const InboxItemImpl: Component<InboxItemProps> = ({ view, inboxId, className, ic
369369
const isActive = useRouteParamsSelector((routerParams) => routerParams.inboxId === inboxId)
370370
const { items } = useInboxActions({ inboxId })
371371

372-
const scope = useGlobalFocusableScope()
373-
useContextMenuActionShortCutTrigger(items, FocusablePresets.isSubscriptionList(scope) && isActive)
372+
const when = useGlobalFocusableScopeSelector(FocusablePresets.isSubscriptionList)
373+
useContextMenuActionShortCutTrigger(items, when && isActive)
374374

375375
const inboxUnread = useUnreadById(inboxId)
376376

packages/internal/components/src/common/Focusable/hooks.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,14 +28,20 @@ export const useFocusableContainerRef = () => {
2828
return use(FocusableContainerRefContext)
2929
}
3030

31+
/**
32+
* Performance issue, use `useGlobalFocusableScopeSelector` instead
33+
* @deprecated use `useGlobalFocusableScopeSelector` instead
34+
*/
3135
export const useGlobalFocusableScope = () => {
3236
return useAtomValue(use(GlobalFocusableContext))
3337
}
3438

3539
export const useGlobalFocusableHasScope = (scope: string) => {
3640
return useGlobalFocusableScopeSelector(useCallback((v) => v.has(scope), [scope]))
3741
}
38-
export const useGlobalFocusableScopeSelector = (selector: (scope: Set<string>) => boolean) => {
42+
export const useGlobalFocusableScopeSelector = (
43+
selector: (scope: EnhanceSet<string>) => boolean,
44+
) => {
3945
const ctx = use(GlobalFocusableContext)
4046

4147
return useAtomValue(useMemo(() => selectAtom(ctx, selector), [ctx, selector]))

packages/internal/components/src/ui/checkbox/index.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,6 @@ function Checkbox({ className, onCheckedChange, ...props }: CheckboxProps) {
5757
opacity: 1,
5858
transition: {
5959
duration: 0.2,
60-
delay: 0.2,
6160
},
6261
},
6362
unchecked: {

0 commit comments

Comments
 (0)