Bluesky app fork with some witchin' additions 💫
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

fix accessibility label in notifications (#4305)

* fix accessibility label in notifications

* add accessibility options to expand post

* inherit from outside, but always include `activate`

* include option to disable label/hint on previewable avatar

* fix hidden elements still being read on voiceover

* make it work for followers too

* extract variable

* fix hint

* update wording elsewhere

authored by

Hailey and committed by
GitHub
708a80e7 b51640fb

+93 -49
+70 -48
src/view/com/notifications/FeedItem.tsx
··· 194 194 ]} 195 195 href={itemHref} 196 196 noFeedback 197 - accessible={ 198 - (item.type === 'post-like' && authors.length === 1) || 199 - item.type === 'repost' 197 + accessible={!isAuthorsExpanded} 198 + accessibilityActions={ 199 + authors.length > 1 200 + ? [ 201 + { 202 + name: 'toggleAuthorsExpanded', 203 + label: isAuthorsExpanded 204 + ? _(msg`Collapse list of users`) 205 + : _(msg`Expand list of users`), 206 + }, 207 + ] 208 + : [ 209 + { 210 + name: 'viewProfile', 211 + label: _( 212 + msg`View ${ 213 + authors[0].profile.displayName || authors[0].profile.handle 214 + }'s profile`, 215 + ), 216 + }, 217 + ] 200 218 } 219 + onAccessibilityAction={e => { 220 + if (e.nativeEvent.actionName === 'activate') { 221 + onBeforePress() 222 + } 223 + if (e.nativeEvent.actionName === 'toggleAuthorsExpanded') { 224 + onToggleAuthorsExpanded() 225 + } 226 + }} 201 227 onBeforePress={onBeforePress}> 202 228 <View style={[styles.layoutIcon, a.pr_sm]}> 203 229 {/* TODO: Prevent conditional rendering and move toward composable ··· 332 358 profile={authors[0].profile} 333 359 moderation={authors[0].moderation.ui('avatar')} 334 360 type={authors[0].profile.associated?.labeler ? 'labeler' : 'user'} 361 + accessible={false} 335 362 /> 336 363 </View> 337 364 ) 338 365 } 339 366 return ( 340 367 <TouchableOpacity 341 - accessibilityLabel={_(msg`Show users`)} 342 - accessibilityHint={_( 343 - msg`Opens an expanded list of users in this notification`, 344 - )} 368 + accessibilityRole="none" 345 369 onPress={onToggleAuthorsExpanded}> 346 370 <View style={styles.avis}> 347 371 {authors.slice(0, MAX_AUTHORS).map(author => ( ··· 351 375 profile={author.profile} 352 376 moderation={author.moderation.ui('avatar')} 353 377 type={author.profile.associated?.labeler ? 'labeler' : 'user'} 378 + accessible={false} 354 379 /> 355 380 </View> 356 381 ))} ··· 392 417 }, [heightInterp, visible]) 393 418 394 419 return ( 395 - <Animated.View 396 - style={[ 397 - heightStyle, 398 - styles.overflowHidden, 399 - visible ? s.mb10 : undefined, 400 - ]}> 401 - {authors.map(author => ( 402 - <NewLink 403 - key={author.profile.did} 404 - label={_(msg`See profile`)} 405 - to={makeProfileLink({ 406 - did: author.profile.did, 407 - handle: author.profile.handle, 408 - })} 409 - style={styles.expandedAuthor}> 410 - <View style={styles.expandedAuthorAvi}> 411 - <ProfileHoverCard did={author.profile.did}> 412 - <UserAvatar 413 - size={35} 414 - avatar={author.profile.avatar} 415 - moderation={author.moderation.ui('avatar')} 416 - type={author.profile.associated?.labeler ? 'labeler' : 'user'} 417 - /> 418 - </ProfileHoverCard> 419 - </View> 420 - <View style={s.flex1}> 421 - <Text 422 - type="lg-bold" 423 - numberOfLines={1} 424 - style={pal.text} 425 - lineHeight={1.2}> 426 - {sanitizeDisplayName( 427 - author.profile.displayName || author.profile.handle, 428 - )} 429 - &nbsp; 430 - <Text style={[pal.textLight]} lineHeight={1.2}> 431 - {sanitizeHandle(author.profile.handle)} 420 + <Animated.View style={[heightStyle, styles.overflowHidden]}> 421 + {visible && 422 + authors.map(author => ( 423 + <NewLink 424 + key={author.profile.did} 425 + label={author.profile.displayName || author.profile.handle} 426 + accessibilityHint={_(msg`Opens this profile`)} 427 + to={makeProfileLink({ 428 + did: author.profile.did, 429 + handle: author.profile.handle, 430 + })} 431 + style={styles.expandedAuthor}> 432 + <View style={styles.expandedAuthorAvi}> 433 + <ProfileHoverCard did={author.profile.did}> 434 + <UserAvatar 435 + size={35} 436 + avatar={author.profile.avatar} 437 + moderation={author.moderation.ui('avatar')} 438 + type={author.profile.associated?.labeler ? 'labeler' : 'user'} 439 + /> 440 + </ProfileHoverCard> 441 + </View> 442 + <View style={s.flex1}> 443 + <Text 444 + type="lg-bold" 445 + numberOfLines={1} 446 + style={pal.text} 447 + lineHeight={1.2}> 448 + {sanitizeDisplayName( 449 + author.profile.displayName || author.profile.handle, 450 + )} 451 + &nbsp; 452 + <Text style={[pal.textLight]} lineHeight={1.2}> 453 + {sanitizeHandle(author.profile.handle)} 454 + </Text> 432 455 </Text> 433 - </Text> 434 - </View> 435 - </NewLink> 436 - ))} 456 + </View> 457 + </NewLink> 458 + ))} 437 459 </Animated.View> 438 460 ) 439 461 }
+15
src/view/com/util/Link.tsx
··· 64 64 anchorNoUnderline, 65 65 navigationAction, 66 66 onBeforePress, 67 + accessibilityActions, 68 + onAccessibilityAction, 67 69 ...props 68 70 }: Props) { 69 71 const t = useTheme() ··· 89 91 [closeModal, navigation, navigationAction, href, openLink, onBeforePress], 90 92 ) 91 93 94 + const accessibilityActionsWithActivate = [ 95 + ...(accessibilityActions || []), 96 + {name: 'activate', label: title}, 97 + ] 98 + 92 99 if (noFeedback) { 93 100 return ( 94 101 <WebAuxClickWrapper> ··· 97 104 onPress={onPress} 98 105 accessible={accessible} 99 106 accessibilityRole="link" 107 + accessibilityActions={accessibilityActionsWithActivate} 108 + onAccessibilityAction={e => { 109 + if (e.nativeEvent.actionName === 'activate') { 110 + onPress() 111 + } else { 112 + onAccessibilityAction?.(e) 113 + } 114 + }} 100 115 {...props} 101 116 android_ripple={{ 102 117 color: t.atoms.bg_contrast_25.backgroundColor,
+8 -1
src/view/com/util/UserAvatar.tsx
··· 53 53 profile: AppBskyActorDefs.ProfileViewBasic 54 54 disableHoverCard?: boolean 55 55 onBeforePress?: () => void 56 + accessible?: boolean 56 57 } 57 58 58 59 const BLUR_AMOUNT = isWeb ? 5 : 100 ··· 386 387 profile, 387 388 disableHoverCard, 388 389 onBeforePress, 390 + accessible = true, 389 391 ...rest 390 392 }: PreviewableUserAvatarProps): React.ReactNode => { 391 393 const {_} = useLingui() ··· 399 401 return ( 400 402 <ProfileHoverCard did={profile.did} disable={disableHoverCard}> 401 403 <Link 402 - label={_(msg`See profile`)} 404 + label={ 405 + accessible 406 + ? _(msg`${profile.displayName || profile.handle}'s avatar`) 407 + : undefined 408 + } 409 + accessibilityHint={accessible ? _(msg`Opens this profile`) : undefined} 403 410 to={makeProfileLink({ 404 411 did: profile.did, 405 412 handle: profile.handle,