Update hashtag menu to use Menu, convert to native link for additional a11y and click handling (#7529)

* Make tag a normal link on web

* Replace old TagMenu with new RichTextTag component, expand and improve click utils

* Clarify intents

* Ensure we're passing down hint

* ope

* DRY
This commit is contained in:
Eric Bailey
2025-01-21 15:56:01 -06:00
committed by GitHub
parent c8d062f1ae
commit 9df5caf3c5
10 changed files with 294 additions and 577 deletions
+4 -90
View File
@@ -1,19 +1,13 @@
import React from 'react'
import {TextStyle} from 'react-native'
import {AppBskyRichtextFacet, RichText as RichTextAPI} from '@atproto/api'
import {msg} from '@lingui/macro'
import {useLingui} from '@lingui/react'
import {useNavigation} from '@react-navigation/native'
import {NavigationProp} from '#/lib/routes/types'
import {toShortUrl} from '#/lib/strings/url-helpers'
import {isNative} from '#/platform/detection'
import {atoms as a, flatten, native, TextStyleProp, useTheme, web} from '#/alf'
import {atoms as a, flatten, TextStyleProp} from '#/alf'
import {isOnlyEmoji} from '#/alf/typography'
import {useInteractionState} from '#/components/hooks/useInteractionState'
import {InlineLinkText, LinkProps} from '#/components/Link'
import {ProfileHoverCard} from '#/components/ProfileHoverCard'
import {TagMenu, useTagMenuControl} from '#/components/TagMenu'
import {RichTextTag} from '#/components/RichTextTag'
import {Text, TextProps} from '#/components/Typography'
const WORD_WRAP = {wordWrap: 1}
@@ -149,10 +143,9 @@ export function RichText({
els.push(
<RichTextTag
key={key}
text={segment.text}
display={segment.text}
tag={tag.tag}
style={interactiveStyles}
selectable={selectable}
textStyle={interactiveStyles}
authorHandle={authorHandle}
/>,
)
@@ -177,82 +170,3 @@ export function RichText({
</Text>
)
}
function RichTextTag({
text,
tag,
style,
selectable,
authorHandle,
}: {
text: string
tag: string
selectable?: boolean
authorHandle?: string
} & TextStyleProp) {
const t = useTheme()
const {_} = useLingui()
const control = useTagMenuControl()
const {
state: hovered,
onIn: onHoverIn,
onOut: onHoverOut,
} = useInteractionState()
const {state: focused, onIn: onFocus, onOut: onBlur} = useInteractionState()
const navigation = useNavigation<NavigationProp>()
const navigateToPage = React.useCallback(() => {
navigation.push('Hashtag', {
tag: encodeURIComponent(tag),
})
}, [navigation, tag])
const openDialog = React.useCallback(() => {
control.open()
}, [control])
/*
* N.B. On web, this is wrapped in another pressable comopnent with a11y
* labels, etc. That's why only some of these props are applied here.
*/
return (
<React.Fragment>
<TagMenu control={control} tag={tag} authorHandle={authorHandle}>
<Text
emoji
selectable={selectable}
{...native({
accessibilityLabel: _(msg`Hashtag: #${tag}`),
accessibilityHint: _(msg`Long press to open tag menu for #${tag}`),
accessibilityRole: isNative ? 'button' : undefined,
onPress: navigateToPage,
onLongPress: openDialog,
})}
{...web({
onMouseEnter: onHoverIn,
onMouseLeave: onHoverOut,
})}
// @ts-ignore
onFocus={onFocus}
onBlur={onBlur}
style={[
web({
cursor: 'pointer',
}),
{color: t.palette.primary_500},
(hovered || focused) && {
...web({
outline: 0,
textDecorationLine: 'underline',
textDecorationColor: t.palette.primary_500,
}),
},
style,
]}>
{text}
</Text>
</TagMenu>
</React.Fragment>
)
}