Fix Android WebView API access and mobile shell layout
This commit is contained in:
@@ -1,7 +1,11 @@
|
||||
import assert from 'node:assert/strict';
|
||||
import test from 'node:test';
|
||||
|
||||
import { getVisibleNavItems } from './MobileLayout';
|
||||
import {
|
||||
getMobileViewportOffsetClassNames,
|
||||
getVisibleNavItems,
|
||||
isNativeMobileShellLocation,
|
||||
} from './MobileLayout';
|
||||
|
||||
test('mobile navigation hides the games entry', () => {
|
||||
const visiblePaths = getVisibleNavItems(false).map((item) => item.path as string);
|
||||
@@ -9,3 +13,23 @@ test('mobile navigation hides the games entry', () => {
|
||||
assert.equal(visiblePaths.includes('/games'), false);
|
||||
assert.deepEqual(visiblePaths, ['/overview', '/files', '/transfer']);
|
||||
});
|
||||
|
||||
test('mobile layout reserves top safe-area space for the fixed app bar', () => {
|
||||
const offsets = getMobileViewportOffsetClassNames();
|
||||
|
||||
assert.match(offsets.header, /\bsafe-area-pt\b/);
|
||||
assert.match(offsets.main, /var\(--app-safe-area-top\)/);
|
||||
});
|
||||
|
||||
test('mobile layout adds extra top spacing inside the native shell', () => {
|
||||
const offsets = getMobileViewportOffsetClassNames(true);
|
||||
|
||||
assert.match(offsets.header, /\bpt-6\b/);
|
||||
assert.match(offsets.main, /1\.5rem/);
|
||||
});
|
||||
|
||||
test('native mobile shell detection matches Capacitor localhost origins', () => {
|
||||
assert.equal(isNativeMobileShellLocation(new URL('https://localhost')), true);
|
||||
assert.equal(isNativeMobileShellLocation(new URL('http://127.0.0.1')), true);
|
||||
assert.equal(isNativeMobileShellLocation(new URL('https://yoyuzh.xyz')), false);
|
||||
});
|
||||
|
||||
@@ -32,6 +32,33 @@ const NAV_ITEMS = [
|
||||
{ name: '快传', path: '/transfer', icon: Send },
|
||||
] as const;
|
||||
|
||||
export function isNativeMobileShellLocation(location: Location | URL | null) {
|
||||
if (!location) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const hostname = location.hostname || '';
|
||||
const protocol = location.protocol || '';
|
||||
const isLocalhostHost = hostname === 'localhost' || hostname === '127.0.0.1';
|
||||
const isCapacitorScheme = protocol === 'http:' || protocol === 'https:' || protocol === 'capacitor:';
|
||||
|
||||
return isLocalhostHost && isCapacitorScheme;
|
||||
}
|
||||
|
||||
export function getMobileViewportOffsetClassNames(isNativeShell = false) {
|
||||
if (isNativeShell) {
|
||||
return {
|
||||
header: 'safe-area-pt pt-6',
|
||||
main: 'pt-[calc(3.5rem+1.5rem+var(--app-safe-area-top))]',
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
header: 'safe-area-pt',
|
||||
main: 'pt-[calc(3.5rem+var(--app-safe-area-top))]',
|
||||
};
|
||||
}
|
||||
|
||||
type ActiveModal = 'security' | 'settings' | null;
|
||||
|
||||
export function getVisibleNavItems(isAdmin: boolean) {
|
||||
@@ -47,6 +74,9 @@ export function MobileLayout({ children }: LayoutProps = {}) {
|
||||
const navigate = useNavigate();
|
||||
const { isAdmin, logout, refreshProfile, user } = useAuth();
|
||||
const navItems = getVisibleNavItems(isAdmin);
|
||||
const viewportOffsets = getMobileViewportOffsetClassNames(
|
||||
typeof window !== 'undefined' && isNativeMobileShellLocation(window.location),
|
||||
);
|
||||
const fileInputRef = useRef<HTMLInputElement>(null);
|
||||
|
||||
const [isDropdownOpen, setIsDropdownOpen] = useState(false);
|
||||
@@ -234,7 +264,7 @@ export function MobileLayout({ children }: LayoutProps = {}) {
|
||||
</div>
|
||||
|
||||
{/* Top App Bar */}
|
||||
<header className="fixed top-0 left-0 right-0 z-40 w-full glass-panel border-b border-white/5 bg-[#07101D]/70 backdrop-blur-2xl">
|
||||
<header className={cn("fixed top-0 left-0 right-0 z-40 w-full glass-panel border-b border-white/5 bg-[#07101D]/70 backdrop-blur-2xl", viewportOffsets.header)}>
|
||||
<div className="flex items-center justify-between px-4 h-14">
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="w-8 h-8 rounded-full bg-gradient-to-br from-[#336EFF] to-blue-400 flex items-center justify-center shadow-lg">
|
||||
@@ -306,7 +336,7 @@ export function MobileLayout({ children }: LayoutProps = {}) {
|
||||
</AnimatePresence>
|
||||
|
||||
{/* Main Content Area */}
|
||||
<main className="flex-1 w-full overflow-y-auto overflow-x-hidden pt-14 pb-16 z-10">
|
||||
<main className={cn("flex-1 w-full overflow-y-auto overflow-x-hidden pb-16 z-10", viewportOffsets.main)}>
|
||||
{children ?? <Outlet />}
|
||||
</main>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user