122 lines
4.1 KiB
TypeScript
122 lines
4.1 KiB
TypeScript
import { useCallback, useRef, useEffect } from 'react';
|
|
import { useEnv } from '@/context/EnvContext';
|
|
import { useThemeStore } from '@/store/themeStore';
|
|
import { Insets } from '@/types/misc';
|
|
import { getSafeAreaInsets } from '@/utils/bridge';
|
|
|
|
export const useSafeAreaInsets = () => {
|
|
const { appService } = useEnv();
|
|
const currentInsets = useRef({ top: 0, right: 0, bottom: 0, left: 0 });
|
|
const retryCount = useRef(0);
|
|
const maxRetries = 3;
|
|
|
|
const { updateSafeAreaInsets } = useThemeStore();
|
|
|
|
const updateInsets = (insets: Insets) => {
|
|
const { top, right, bottom, left } = currentInsets.current;
|
|
if (
|
|
insets.top !== top ||
|
|
insets.right !== right ||
|
|
insets.bottom !== bottom ||
|
|
insets.left !== left
|
|
) {
|
|
currentInsets.current = insets;
|
|
updateSafeAreaInsets(insets);
|
|
}
|
|
};
|
|
|
|
const onUpdateInsets = useCallback(() => {
|
|
if (!appService) return;
|
|
|
|
if (!appService.hasSafeAreaInset) {
|
|
updateInsets(currentInsets.current);
|
|
return;
|
|
}
|
|
|
|
const rootStyles = getComputedStyle(document.documentElement);
|
|
const hasCustomProperties = rootStyles.getPropertyValue('--safe-area-inset-top');
|
|
const isWebView139 = /Chrome\/139/.test(navigator.userAgent);
|
|
// safe-area-inset-* values in css are always 0px in some versions of webview 139
|
|
// due to https://issues.chromium.org/issues/40699457
|
|
if (appService.isAndroidApp && isWebView139) {
|
|
getSafeAreaInsets().then((response) => {
|
|
if (response.error) {
|
|
console.error('Error getting safe area insets from native bridge:', response.error);
|
|
} else {
|
|
const insets = {
|
|
top: Math.round(response.top),
|
|
right: Math.round(response.right),
|
|
bottom: Math.round(response.bottom),
|
|
left: Math.round(response.left),
|
|
};
|
|
updateInsets(insets);
|
|
}
|
|
});
|
|
} else if (hasCustomProperties) {
|
|
const top = parseFloat(rootStyles.getPropertyValue('--safe-area-inset-top')) || 0;
|
|
const right = parseFloat(rootStyles.getPropertyValue('--safe-area-inset-right')) || 0;
|
|
const bottom = parseFloat(rootStyles.getPropertyValue('--safe-area-inset-bottom')) || 0;
|
|
const left = parseFloat(rootStyles.getPropertyValue('--safe-area-inset-left')) || 0;
|
|
const insets = {
|
|
top: Math.round(top),
|
|
right: Math.round(right),
|
|
bottom: Math.round(bottom),
|
|
left: Math.round(left),
|
|
};
|
|
|
|
const allZero = top === 0 && right === 0 && bottom === 0 && left === 0;
|
|
if (appService.isIOSApp && allZero && retryCount.current < maxRetries) {
|
|
retryCount.current++;
|
|
setTimeout(() => {
|
|
onUpdateInsets();
|
|
}, 50 * retryCount.current);
|
|
return;
|
|
}
|
|
|
|
if (!allZero) {
|
|
retryCount.current = 0;
|
|
}
|
|
|
|
updateInsets(insets);
|
|
}
|
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
}, [appService]);
|
|
|
|
useEffect(() => {
|
|
onUpdateInsets();
|
|
|
|
// Listen for orientation changes
|
|
if (window.screen?.orientation) {
|
|
window.screen.orientation.addEventListener('change', onUpdateInsets);
|
|
} else {
|
|
window.addEventListener('orientationchange', onUpdateInsets);
|
|
}
|
|
|
|
// Listen for visibility changes (app returning from background)
|
|
const handleVisibilityChange = () => {
|
|
if (document.visibilityState === 'visible') {
|
|
retryCount.current = 0;
|
|
onUpdateInsets();
|
|
}
|
|
};
|
|
document.addEventListener('visibilitychange', handleVisibilityChange);
|
|
|
|
// Listen for window focus (additional safeguard for iOS)
|
|
const handleFocus = () => {
|
|
retryCount.current = 0;
|
|
onUpdateInsets();
|
|
};
|
|
window.addEventListener('focus', handleFocus);
|
|
|
|
return () => {
|
|
if (window.screen?.orientation) {
|
|
window.screen.orientation.removeEventListener('change', onUpdateInsets);
|
|
} else {
|
|
window.removeEventListener('orientationchange', onUpdateInsets);
|
|
}
|
|
document.removeEventListener('visibilitychange', handleVisibilityChange);
|
|
window.removeEventListener('focus', handleFocus);
|
|
};
|
|
}, [onUpdateInsets]);
|
|
};
|