Bluesky app fork with some witchin' additions 💫

have back button dismiss lightbox on web (#9874)

authored by

Spence Pope and committed by
GitHub
6be77517 9b830fd4

+56 -15
+16
src/alf/atoms.ts
··· 109 109 }), 110 110 111 111 /** 112 + * Visually hidden but available to screen readers (web). 113 + * Use for live regions or off-screen labels (e.g. "Image 1 of 3"). 114 + */ 115 + sr_only: web({ 116 + position: 'absolute', 117 + width: 1, 118 + height: 1, 119 + padding: 0, 120 + margin: -1, 121 + overflow: 'hidden', 122 + clip: 'rect(0,0,0,0)', 123 + whiteSpace: 'nowrap', 124 + borderWidth: 0, 125 + }), 126 + 127 + /** 112 128 * {@link Layout.SCROLLBAR_OFFSET} 113 129 */ 114 130 scrollbar_offset: platform({
+1 -13
src/components/Post/Embed/VideoEmbed/VideoEmbedInner/VideoEmbedInnerWeb.tsx
··· 72 72 loop={loop} 73 73 /> 74 74 {embed.alt && ( 75 - <figcaption 76 - id={figId} 77 - style={{ 78 - position: 'absolute', 79 - width: 1, 80 - height: 1, 81 - padding: 0, 82 - margin: -1, 83 - overflow: 'hidden', 84 - clip: 'rect(0, 0, 0, 0)', 85 - whiteSpace: 'nowrap', 86 - borderWidth: 0, 87 - }}> 75 + <figcaption id={figId} style={a.sr_only}> 88 76 {embed.alt} 89 77 </figcaption> 90 78 )}
+39 -2
src/view/com/lightbox/Lightbox.web.tsx
··· 1 - import {useCallback, useEffect, useState} from 'react' 1 + import {useCallback, useEffect, useRef, useState} from 'react' 2 2 import {Pressable, StyleSheet, View} from 'react-native' 3 3 import {Image} from 'expo-image' 4 4 import {msg} from '@lingui/macro' ··· 69 69 <Backdrop /> 70 70 <RemoveScrollBar /> 71 71 <FocusScope.FocusScope loop trapped asChild> 72 - <div style={{position: 'absolute', inset: 0}}>{children}</div> 72 + <div 73 + role="dialog" 74 + aria-modal="true" 75 + aria-label={_(msg`Image viewer`)} 76 + style={{position: 'absolute', inset: 0}}> 77 + {children} 78 + </div> 73 79 </FocusScope.FocusScope> 74 80 </Pressable> 75 81 ) ··· 125 131 return () => window.removeEventListener('keydown', onKeyDown) 126 132 }, [onKeyDown]) 127 133 134 + // Push a history entry so the browser back button closes the lightbox 135 + // instead of navigating away from the page. 136 + const closedByPopStateRef = useRef(false) 137 + useEffect(() => { 138 + history.pushState({lightbox: true}, '') 139 + 140 + const handlePopState = () => { 141 + closedByPopStateRef.current = true 142 + onClose() 143 + } 144 + window.addEventListener('popstate', handlePopState) 145 + 146 + return () => { 147 + window.removeEventListener('popstate', handlePopState) 148 + // Only pop our entry if it's still the current one. If navigation 149 + // already pushed a new entry on top, leave the orphaned entry — 150 + // it shares the same URL so traversing through it is harmless. 151 + if ( 152 + !closedByPopStateRef.current && 153 + (history.state as {lightbox?: boolean})?.lightbox 154 + ) { 155 + history.back() 156 + } 157 + } 158 + }, [onClose]) 159 + 128 160 const delayedFadeInAnim = !reduceMotionEnabled && [ 129 161 a.fade_in, 130 162 {animationDelay: '0.2s', animationFillMode: 'both'}, ··· 205 237 </Pressable> 206 238 </View> 207 239 ) : null} 240 + {imgs.length > 1 && ( 241 + <div aria-live="polite" aria-atomic="true" style={a.sr_only}> 242 + <Text>{_(msg`Image ${index + 1} of ${imgs.length}`)}</Text> 243 + </div> 244 + )} 208 245 <Button 209 246 onPress={onClose} 210 247 style={[