fix: edge swipe on iOS safari
This commit is contained in:
parent
30a0c30d94
commit
73410f2acd
3 changed files with 133 additions and 6 deletions
|
|
@ -534,6 +534,7 @@ import { useMediaQuery } from '~/composables/useMediaQuery';
|
|||
import type { toastPosition } from '~/composables/useNotification';
|
||||
import LimitsPage from '~/components/LimitsPage.vue';
|
||||
import { formatPageTitle, parse_api_response, request, syncOpacity, uri } from '~/utils';
|
||||
import { getSidebarSwipeMode } from '~/utils/sidebarSwipe';
|
||||
import type { YTDLPOption } from '~/types/ytdlp';
|
||||
import { useDialog } from '~/composables/useDialog';
|
||||
import Dialog from '~/components/Dialog.vue';
|
||||
|
|
@ -557,7 +558,6 @@ type SidebarSection = {
|
|||
type ColorModePreference = 'system' | 'light' | 'dark';
|
||||
type SwipeMode = 'open' | 'close';
|
||||
|
||||
const MOBILE_SIDEBAR_EDGE_WIDTH = 32;
|
||||
const MOBILE_SIDEBAR_MIN_SWIPE_DISTANCE = 64;
|
||||
|
||||
const socket = useAppSocket();
|
||||
|
|
@ -655,11 +655,11 @@ const handleSwipeStart = (event: TouchEvent): void => {
|
|||
return;
|
||||
}
|
||||
|
||||
const swipeMode: SwipeMode | null = showSidebar.value
|
||||
? 'close'
|
||||
: touch.clientX <= MOBILE_SIDEBAR_EDGE_WIDTH
|
||||
? 'open'
|
||||
: null;
|
||||
const swipeMode: SwipeMode | null = getSidebarSwipeMode(
|
||||
showSidebar.value,
|
||||
touch.clientX,
|
||||
navigator,
|
||||
);
|
||||
|
||||
if (!swipeMode) {
|
||||
resetSwipe();
|
||||
|
|
|
|||
54
ui/app/utils/sidebarSwipe.ts
Normal file
54
ui/app/utils/sidebarSwipe.ts
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
type SidebarSwipeMode = 'open' | 'close';
|
||||
|
||||
type NavigatorLike = {
|
||||
userAgent?: string;
|
||||
platform?: string;
|
||||
maxTouchPoints?: number;
|
||||
};
|
||||
|
||||
const MOBILE_SIDEBAR_EDGE_WIDTH = 32;
|
||||
const IOS_NAVIGATION_EDGE_WIDTH = 24;
|
||||
|
||||
const isAppleMobileTouchNavigator = (nav?: NavigatorLike): boolean => {
|
||||
if (!nav) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const userAgent = nav.userAgent ?? '';
|
||||
const platform = nav.platform ?? '';
|
||||
const maxTouchPoints = Number(nav.maxTouchPoints ?? 0);
|
||||
const isiPhoneLike = /(iPhone|iPod|iPad)/i.test(userAgent);
|
||||
const isiPadDesktopMode = platform === 'MacIntel' && maxTouchPoints > 1;
|
||||
|
||||
return /AppleWebKit/i.test(userAgent) && (isiPhoneLike || isiPadDesktopMode);
|
||||
};
|
||||
|
||||
const canStartSidebarOpenSwipe = (
|
||||
touchX: number,
|
||||
nav?: NavigatorLike,
|
||||
edgeWidth: number = MOBILE_SIDEBAR_EDGE_WIDTH,
|
||||
): boolean => {
|
||||
const reservedEdge = isAppleMobileTouchNavigator(nav) ? IOS_NAVIGATION_EDGE_WIDTH : 0;
|
||||
return touchX > reservedEdge && touchX <= reservedEdge + edgeWidth;
|
||||
};
|
||||
|
||||
const getSidebarSwipeMode = (
|
||||
sidebarOpen: boolean,
|
||||
touchX: number,
|
||||
nav?: NavigatorLike,
|
||||
): SidebarSwipeMode | null => {
|
||||
if (sidebarOpen) {
|
||||
return 'close';
|
||||
}
|
||||
|
||||
return canStartSidebarOpenSwipe(touchX, nav) ? 'open' : null;
|
||||
};
|
||||
|
||||
export {
|
||||
MOBILE_SIDEBAR_EDGE_WIDTH,
|
||||
IOS_NAVIGATION_EDGE_WIDTH,
|
||||
canStartSidebarOpenSwipe,
|
||||
getSidebarSwipeMode,
|
||||
isAppleMobileTouchNavigator,
|
||||
};
|
||||
export type { NavigatorLike, SidebarSwipeMode };
|
||||
73
ui/tests/utils/sidebarSwipe.test.ts
Normal file
73
ui/tests/utils/sidebarSwipe.test.ts
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
import { describe, expect, it } from 'bun:test';
|
||||
|
||||
import {
|
||||
IOS_NAVIGATION_EDGE_WIDTH,
|
||||
MOBILE_SIDEBAR_EDGE_WIDTH,
|
||||
canStartSidebarOpenSwipe,
|
||||
getSidebarSwipeMode,
|
||||
isAppleMobileTouchNavigator,
|
||||
} from '~/utils/sidebarSwipe';
|
||||
|
||||
describe('sidebarSwipe', () => {
|
||||
it('detects apple mobile webkit navigators', () => {
|
||||
expect(
|
||||
isAppleMobileTouchNavigator({
|
||||
userAgent:
|
||||
'Mozilla/5.0 (iPhone; CPU iPhone OS 18_4 like Mac OS X) AppleWebKit/605.1.15 Version/18.4 Mobile/15E148 Safari/604.1',
|
||||
platform: 'iPhone',
|
||||
maxTouchPoints: 5,
|
||||
}),
|
||||
).toBe(true);
|
||||
|
||||
expect(
|
||||
isAppleMobileTouchNavigator({
|
||||
userAgent:
|
||||
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15) AppleWebKit/605.1.15 Version/18.4 Mobile/15E148 Safari/604.1',
|
||||
platform: 'MacIntel',
|
||||
maxTouchPoints: 5,
|
||||
}),
|
||||
).toBe(true);
|
||||
|
||||
expect(
|
||||
isAppleMobileTouchNavigator({
|
||||
userAgent:
|
||||
'Mozilla/5.0 (Linux; Android 15) AppleWebKit/537.36 Chrome/147.0.0.0 Mobile Safari/537.36',
|
||||
platform: 'Linux armv8l',
|
||||
maxTouchPoints: 5,
|
||||
}),
|
||||
).toBe(false);
|
||||
});
|
||||
|
||||
it('reserves the ios navigation edge and starts the sidebar swipe just inside it', () => {
|
||||
const nav = {
|
||||
userAgent:
|
||||
'Mozilla/5.0 (iPhone; CPU iPhone OS 18_4 like Mac OS X) AppleWebKit/605.1.15 Version/18.4 Mobile/15E148 Safari/604.1',
|
||||
platform: 'iPhone',
|
||||
maxTouchPoints: 5,
|
||||
};
|
||||
|
||||
expect(canStartSidebarOpenSwipe(IOS_NAVIGATION_EDGE_WIDTH, nav)).toBe(false);
|
||||
expect(canStartSidebarOpenSwipe(IOS_NAVIGATION_EDGE_WIDTH + 1, nav)).toBe(true);
|
||||
expect(canStartSidebarOpenSwipe(IOS_NAVIGATION_EDGE_WIDTH + MOBILE_SIDEBAR_EDGE_WIDTH, nav)).toBe(true);
|
||||
expect(canStartSidebarOpenSwipe(IOS_NAVIGATION_EDGE_WIDTH + MOBILE_SIDEBAR_EDGE_WIDTH + 1, nav)).toBe(
|
||||
false,
|
||||
);
|
||||
});
|
||||
|
||||
it('keeps the original left-edge open band on non-apple mobile browsers', () => {
|
||||
const nav = {
|
||||
userAgent:
|
||||
'Mozilla/5.0 (Linux; Android 15) AppleWebKit/537.36 Chrome/147.0.0.0 Mobile Safari/537.36',
|
||||
platform: 'Linux armv8l',
|
||||
maxTouchPoints: 5,
|
||||
};
|
||||
|
||||
expect(canStartSidebarOpenSwipe(1, nav)).toBe(true);
|
||||
expect(canStartSidebarOpenSwipe(MOBILE_SIDEBAR_EDGE_WIDTH, nav)).toBe(true);
|
||||
expect(canStartSidebarOpenSwipe(MOBILE_SIDEBAR_EDGE_WIDTH + 1, nav)).toBe(false);
|
||||
});
|
||||
|
||||
it('returns close while the sidebar is already open', () => {
|
||||
expect(getSidebarSwipeMode(true, 4)).toBe('close');
|
||||
});
|
||||
});
|
||||
Loading…
Reference in a new issue