your personal website on atproto - mirror blento.app
at fix-cached-posts 99 lines 2.9 kB view raw
1import type { QRContext } from './QRCodeModal.svelte'; 2 3// Global state for QR modal 4let openModal: ((href: string, context: QRContext) => void) | null = null; 5 6export function registerQRModal(fn: (href: string, context: QRContext) => void) { 7 openModal = fn; 8} 9 10export function unregisterQRModal() { 11 openModal = null; 12} 13 14export function qrOverlay( 15 node: HTMLElement, 16 params: { href?: string; context?: QRContext; disabled?: boolean } = {} 17) { 18 const LONG_PRESS_DURATION = 500; 19 let longPressTimer: ReturnType<typeof setTimeout> | null = null; 20 let isLongPress = false; 21 let touchActive = false; 22 23 // Prevent iOS link preview on long-press 24 const originalCallout = node.style.getPropertyValue('-webkit-touch-callout'); 25 node.style.setProperty('-webkit-touch-callout', 'none'); 26 27 function getHref() { 28 return params.href || (node as HTMLAnchorElement).href || ''; 29 } 30 31 function startLongPress(e: PointerEvent) { 32 if (params.disabled) return; 33 // Only start long press for primary button (touch/left-click), not right-click 34 if (e.button !== 0) return; 35 touchActive = e.pointerType === 'touch'; 36 isLongPress = false; 37 longPressTimer = setTimeout(() => { 38 isLongPress = true; 39 openModal?.(getHref(), params.context ?? {}); 40 }, LONG_PRESS_DURATION); 41 } 42 43 function cancelLongPress() { 44 if (longPressTimer) { 45 clearTimeout(longPressTimer); 46 longPressTimer = null; 47 } 48 touchActive = false; 49 } 50 51 function handleClick(e: MouseEvent) { 52 if (isLongPress) { 53 e.preventDefault(); 54 isLongPress = false; 55 return; 56 } 57 58 // Shift-click opens QR modal 59 if (e.shiftKey && !params.disabled) { 60 e.preventDefault(); 61 openModal?.(getHref(), params.context ?? {}); 62 } 63 } 64 65 function handleContextMenu(e: Event) { 66 // Prevent context menu during touch to avoid iOS preview 67 if (touchActive || isLongPress) { 68 e.preventDefault(); 69 } 70 } 71 72 node.addEventListener('pointerdown', startLongPress); 73 node.addEventListener('pointerup', cancelLongPress); 74 node.addEventListener('pointercancel', cancelLongPress); 75 node.addEventListener('pointerleave', cancelLongPress); 76 node.addEventListener('click', handleClick); 77 node.addEventListener('contextmenu', handleContextMenu); 78 79 return { 80 update(newParams: { href?: string; context?: QRContext; disabled?: boolean }) { 81 params = newParams; 82 }, 83 destroy() { 84 node.removeEventListener('pointerdown', startLongPress); 85 node.removeEventListener('pointerup', cancelLongPress); 86 node.removeEventListener('pointercancel', cancelLongPress); 87 node.removeEventListener('pointerleave', cancelLongPress); 88 node.removeEventListener('click', handleClick); 89 node.removeEventListener('contextmenu', handleContextMenu); 90 cancelLongPress(); 91 // Restore original style 92 if (originalCallout) { 93 node.style.setProperty('-webkit-touch-callout', originalCallout); 94 } else { 95 node.style.removeProperty('-webkit-touch-callout'); 96 } 97 } 98 }; 99}