mirror of
https://github.com/siteboon/claudecodeui.git
synced 2026-03-11 08:57:38 +00:00
- Add new `useDeviceSettings` hook (`src/hooks/useDeviceSettings.ts`) to centralize
device-related state:
- exposes `isMobile` and `isPWA`
- supports options: `mobileBreakpoint`, `trackMobile`, `trackPWA`
- listens to window resize for mobile updates
- listens to `display-mode: standalone` changes for PWA updates
- includes `matchMedia.addListener/removeListener` fallback for older environments
- Update `AppContent` (`src/App.jsx`) to consume `isMobile` from
`useDeviceSettings({ trackPWA: false })`:
- remove local `isMobile` state/effect
- remove local `isPWA` state/effect
- keep existing `isMobile` behavior for layout and mobile sidebar flow
- stop passing `isPWA` into `Sidebar` props
- Update `Sidebar` (`src/components/Sidebar.jsx`) to own PWA detection:
- consume `isPWA` from `useDeviceSettings({ trackMobile: false })`
- add effect to toggle `pwa-mode` class on `document.documentElement` and `document.body`
- retain use of `isMobile` prop from `App` for sidebar/mobile rendering decisions
Why:
- removes duplicated device-detection logic from `AppContent`
- makes device-state logic reusable and easier to maintain
- keeps PWA-specific behavior where it is actually used (`Sidebar`)
89 lines
2.1 KiB
TypeScript
89 lines
2.1 KiB
TypeScript
import { useEffect, useState } from 'react';
|
|
|
|
type UseDeviceSettingsOptions = {
|
|
mobileBreakpoint?: number;
|
|
trackMobile?: boolean;
|
|
trackPWA?: boolean;
|
|
};
|
|
|
|
const getIsMobile = (mobileBreakpoint: number): boolean => {
|
|
if (typeof window === 'undefined') {
|
|
return false;
|
|
}
|
|
|
|
return window.innerWidth < mobileBreakpoint;
|
|
};
|
|
|
|
const getIsPWA = (): boolean => {
|
|
if (typeof window === 'undefined') {
|
|
return false;
|
|
}
|
|
|
|
const navigatorWithStandalone = window.navigator as Navigator & { standalone?: boolean };
|
|
|
|
return (
|
|
window.matchMedia('(display-mode: standalone)').matches ||
|
|
Boolean(navigatorWithStandalone.standalone) ||
|
|
document.referrer.includes('android-app://')
|
|
);
|
|
};
|
|
|
|
export function useDeviceSettings(options: UseDeviceSettingsOptions = {}) {
|
|
const {
|
|
mobileBreakpoint = 768,
|
|
trackMobile = true,
|
|
trackPWA = true
|
|
} = options;
|
|
|
|
const [isMobile, setIsMobile] = useState<boolean>(() => (
|
|
trackMobile ? getIsMobile(mobileBreakpoint) : false
|
|
));
|
|
const [isPWA, setIsPWA] = useState<boolean>(() => (
|
|
trackPWA ? getIsPWA() : false
|
|
));
|
|
|
|
useEffect(() => {
|
|
if (!trackMobile || typeof window === 'undefined') {
|
|
return;
|
|
}
|
|
|
|
const checkMobile = () => {
|
|
setIsMobile(getIsMobile(mobileBreakpoint));
|
|
};
|
|
|
|
checkMobile();
|
|
window.addEventListener('resize', checkMobile);
|
|
|
|
return () => {
|
|
window.removeEventListener('resize', checkMobile);
|
|
};
|
|
}, [mobileBreakpoint, trackMobile]);
|
|
|
|
useEffect(() => {
|
|
if (!trackPWA || typeof window === 'undefined') {
|
|
return;
|
|
}
|
|
|
|
const mediaQuery = window.matchMedia('(display-mode: standalone)');
|
|
const checkPWA = () => {
|
|
setIsPWA(getIsPWA());
|
|
};
|
|
|
|
checkPWA();
|
|
|
|
if (typeof mediaQuery.addEventListener === 'function') {
|
|
mediaQuery.addEventListener('change', checkPWA);
|
|
return () => {
|
|
mediaQuery.removeEventListener('change', checkPWA);
|
|
};
|
|
}
|
|
|
|
mediaQuery.addListener(checkPWA);
|
|
return () => {
|
|
mediaQuery.removeListener(checkPWA);
|
|
};
|
|
}, [trackPWA]);
|
|
|
|
return { isMobile, isPWA };
|
|
}
|