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 type { toastPosition } from '~/composables/useNotification';
|
||||||
import LimitsPage from '~/components/LimitsPage.vue';
|
import LimitsPage from '~/components/LimitsPage.vue';
|
||||||
import { formatPageTitle, parse_api_response, request, syncOpacity, uri } from '~/utils';
|
import { formatPageTitle, parse_api_response, request, syncOpacity, uri } from '~/utils';
|
||||||
|
import { getSidebarSwipeMode } from '~/utils/sidebarSwipe';
|
||||||
import type { YTDLPOption } from '~/types/ytdlp';
|
import type { YTDLPOption } from '~/types/ytdlp';
|
||||||
import { useDialog } from '~/composables/useDialog';
|
import { useDialog } from '~/composables/useDialog';
|
||||||
import Dialog from '~/components/Dialog.vue';
|
import Dialog from '~/components/Dialog.vue';
|
||||||
|
|
@ -557,7 +558,6 @@ type SidebarSection = {
|
||||||
type ColorModePreference = 'system' | 'light' | 'dark';
|
type ColorModePreference = 'system' | 'light' | 'dark';
|
||||||
type SwipeMode = 'open' | 'close';
|
type SwipeMode = 'open' | 'close';
|
||||||
|
|
||||||
const MOBILE_SIDEBAR_EDGE_WIDTH = 32;
|
|
||||||
const MOBILE_SIDEBAR_MIN_SWIPE_DISTANCE = 64;
|
const MOBILE_SIDEBAR_MIN_SWIPE_DISTANCE = 64;
|
||||||
|
|
||||||
const socket = useAppSocket();
|
const socket = useAppSocket();
|
||||||
|
|
@ -655,11 +655,11 @@ const handleSwipeStart = (event: TouchEvent): void => {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const swipeMode: SwipeMode | null = showSidebar.value
|
const swipeMode: SwipeMode | null = getSidebarSwipeMode(
|
||||||
? 'close'
|
showSidebar.value,
|
||||||
: touch.clientX <= MOBILE_SIDEBAR_EDGE_WIDTH
|
touch.clientX,
|
||||||
? 'open'
|
navigator,
|
||||||
: null;
|
);
|
||||||
|
|
||||||
if (!swipeMode) {
|
if (!swipeMode) {
|
||||||
resetSwipe();
|
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