feat: narrowed responsive layout handling, closed #4370

- Added a renderValue function to the LanguageSelector for improved display of selected language names.
- Updated layout component to support responsive behavior based on viewport width.
- Refactored setting sync to utilize useMobile hook for better mobile detection.
- Enhanced withResponsiveComponent to accept a breakpoint function for more flexible responsive layouts.

Signed-off-by: Innei <tukon479@gmail.com>
This commit is contained in:
Innei 2025-11-24 16:02:17 +08:00
parent 452bb941a9
commit ad8575d737
No known key found for this signature in database
GPG Key ID: 0F62D33977F021F7
4 changed files with 30 additions and 6 deletions

View File

@ -268,6 +268,9 @@ export const LanguageSelector = ({
setGeneralSetting("language", value as string)
dayjs.locale(value)
}}
renderValue={useTypeScriptHappyCallback((item) => {
return <span>{defaultResources[item.value].lang.name}</span>
}, [])}
renderItem={useTypeScriptHappyCallback((item) => {
const lang = item.value
const percent = I18N_COMPLETENESS_MAP[lang]

View File

@ -8,4 +8,5 @@ export const Component = withResponsiveComponent(
const { default: DownloadPage } = await import("~/modules/download")
return { default: DownloadPage }
},
(w) => w < 768,
)

View File

@ -1,4 +1,4 @@
import { isMobile } from "@follow/components/hooks/useMobile.js"
import { useMobile } from "@follow/components/hooks/useMobile.js"
import { useIsDark } from "@follow/hooks"
import { getAccentColorValue } from "@follow/shared/settings/constants"
import type { UISettings } from "@follow/shared/settings/interface"
@ -47,12 +47,12 @@ const useUpdateDockBadge = (setting: UISettings) => {
}
const useUISettingSync = () => {
const setting = useUISettingValue()
const mobile = isMobile()
const mobile = useMobile()
useSyncTheme()
useInsertionEffect(() => {
const root = document.documentElement
root.style.fontSize = `${setting.uiTextSize * (mobile ? 1.125 : 1)}px`
}, [setting.uiTextSize])
root.style.fontSize = `${setting.uiTextSize * (mobile ? 0.875 : 1)}px`
}, [setting.uiTextSize, mobile])
const isDark = useIsDark()
useInsertionEffect(() => {

View File

@ -1,4 +1,5 @@
import { useMobile } from "@follow/components/hooks/useMobile.js"
import { useViewport } from "@follow/components/hooks/useViewport.js"
import type { ComponentType, ReactNode, RefAttributes } from "react"
import { lazy, Suspense } from "react"
@ -6,15 +7,34 @@ export function withResponsiveComponent<P extends object>(
desktopImport: () => Promise<{ default: ComponentType<P> }>,
mobileImport: () => Promise<{ default: ComponentType<P> }>,
fallbackElement?: ReactNode,
): ComponentType<P>
export function withResponsiveComponent<P extends object>(
desktopImport: () => Promise<{ default: ComponentType<P> }>,
mobileImport: () => Promise<{ default: ComponentType<P> }>,
breakpointFn: (w: number) => boolean,
fallbackElement?: ReactNode,
): ComponentType<P>
export function withResponsiveComponent<P extends object>(
desktopImport: () => Promise<{ default: ComponentType<P> }>,
mobileImport: () => Promise<{ default: ComponentType<P> }>,
fallbackElementOrBreakpointFn?: ReactNode | ((w: number) => boolean),
fallbackElement?: ReactNode,
) {
const LazyDesktopLayout = lazy(desktopImport) as unknown as ComponentType<P>
const LazyMobileLayout = lazy(mobileImport) as unknown as ComponentType<P>
// Check if the third parameter is a function (breakpoint function) or ReactNode (fallback)
const isBreakpointFn = typeof fallbackElementOrBreakpointFn === "function"
const breakpointFn = isBreakpointFn ? fallbackElementOrBreakpointFn : undefined
const fallback = isBreakpointFn ? fallbackElement : fallbackElementOrBreakpointFn
return function ResponsiveLayout(props: P) {
const isMobile = useMobile()
const isMobile = useViewport(({ w: viewport }) =>
breakpointFn ? breakpointFn(viewport) : viewport < 1024 && viewport !== 0,
)
return (
<Suspense fallback={fallbackElement}>
<Suspense fallback={fallback}>
{isMobile ? <LazyMobileLayout {...props} /> : <LazyDesktopLayout {...props} />}
</Suspense>
)