custom bottom sheet link
This commit is contained in:
@@ -0,0 +1,92 @@
|
||||
import React from 'react'
|
||||
import {BlueskyBottomSheetPressable} from '@haileyok/bluesky-bottom-sheet'
|
||||
import {StackActions, useNavigation} from '@react-navigation/native'
|
||||
|
||||
import {NavigationProp} from '#/lib/routes/types'
|
||||
import {flatten, useTheme} from '#/alf'
|
||||
import {useDialogContext} from '#/components/Dialog'
|
||||
import {useInteractionState} from '#/components/hooks/useInteractionState'
|
||||
import {InlineLinkProps, useLink} from '#/components/Link'
|
||||
import {Text} from '#/components/Typography'
|
||||
import {router} from '#/routes'
|
||||
|
||||
export function BottomSheetInlineLinkText({
|
||||
children,
|
||||
to,
|
||||
action = 'push',
|
||||
disableMismatchWarning,
|
||||
style,
|
||||
onPress: outerOnPress,
|
||||
label,
|
||||
shareOnLongPress,
|
||||
disableUnderline,
|
||||
...rest
|
||||
}: InlineLinkProps) {
|
||||
const t = useTheme()
|
||||
const stringChildren = typeof children === 'string'
|
||||
const navigation = useNavigation<NavigationProp>()
|
||||
const dialog = useDialogContext()
|
||||
|
||||
const {href, isExternal, onLongPress} = useLink({
|
||||
to,
|
||||
displayText: stringChildren ? children : '',
|
||||
action,
|
||||
disableMismatchWarning,
|
||||
onPress: outerOnPress,
|
||||
shareOnLongPress,
|
||||
})
|
||||
const {
|
||||
state: pressed,
|
||||
onIn: onPressIn,
|
||||
onOut: onPressOut,
|
||||
} = useInteractionState()
|
||||
|
||||
const onPress = () => {
|
||||
if (isExternal) {
|
||||
return
|
||||
}
|
||||
|
||||
dialog.close()
|
||||
|
||||
if (action === 'push') {
|
||||
navigation.dispatch(StackActions.push(...router.matchPath(href)))
|
||||
} else if (action === 'replace') {
|
||||
navigation.dispatch(StackActions.replace(...router.matchPath(href)))
|
||||
} else if (action === 'navigate') {
|
||||
// @ts-ignore
|
||||
navigation.navigate(...router.matchPath(href))
|
||||
} else {
|
||||
throw Error('Unsupported navigator action.')
|
||||
}
|
||||
}
|
||||
|
||||
const flattenedStyle = flatten(style) || {}
|
||||
|
||||
// eslint-disable-next-line bsky-internal/avoid-unwrapped-text
|
||||
return (
|
||||
<BlueskyBottomSheetPressable
|
||||
onPress={onPress}
|
||||
onLongPress={onLongPress}
|
||||
onPressIn={onPressIn}
|
||||
onPressOut={onPressOut}
|
||||
role="link"
|
||||
accessibilityLabel={label}
|
||||
accessibilityHint=""
|
||||
style={{flexDirection: 'row'}}>
|
||||
<Text
|
||||
{...rest}
|
||||
style={[
|
||||
{color: t.palette.primary_500},
|
||||
pressed &&
|
||||
!disableUnderline && {
|
||||
textDecorationLine: 'underline',
|
||||
textDecorationColor:
|
||||
flattenedStyle.color ?? t.palette.primary_500,
|
||||
},
|
||||
flattenedStyle,
|
||||
]}>
|
||||
{children}
|
||||
</Text>
|
||||
</BlueskyBottomSheetPressable>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
import {Link as BottomSheetLink} from './Link'
|
||||
|
||||
export {BottomSheetLink}
|
||||
+43
-3
@@ -23,6 +23,7 @@ import {shouldClickOpenNewTab} from '#/platform/urls'
|
||||
import {useModalControls} from '#/state/modals'
|
||||
import {useOpenLink} from '#/state/preferences/in-app-browser'
|
||||
import {atoms as a, flatten, TextStyleProp, useTheme, web} from '#/alf'
|
||||
import {BottomSheetButton} from '#/components/BottomSheetButton'
|
||||
import {Button, ButtonProps} from '#/components/Button'
|
||||
import {useInteractionState} from '#/components/hooks/useInteractionState'
|
||||
import {Text, TextProps} from '#/components/Typography'
|
||||
@@ -103,17 +104,17 @@ export function useLink({
|
||||
linkRequiresWarning(href, displayText),
|
||||
)
|
||||
|
||||
if (requiresWarning) {
|
||||
if (isWeb) {
|
||||
e.preventDefault()
|
||||
}
|
||||
|
||||
if (requiresWarning) {
|
||||
openModal({
|
||||
name: 'link-warning',
|
||||
text: displayText,
|
||||
href: href,
|
||||
})
|
||||
} else {
|
||||
e.preventDefault()
|
||||
|
||||
if (isExternal) {
|
||||
openLink(href)
|
||||
} else {
|
||||
@@ -241,6 +242,45 @@ export function Link({
|
||||
)
|
||||
}
|
||||
|
||||
export function BottomSheetLink({
|
||||
children,
|
||||
to,
|
||||
action = 'push',
|
||||
onPress: outerOnPress,
|
||||
download,
|
||||
...rest
|
||||
}: LinkProps) {
|
||||
const {href, isExternal, onPress} = useLink({
|
||||
to,
|
||||
displayText: typeof children === 'string' ? children : '',
|
||||
action,
|
||||
onPress: outerOnPress,
|
||||
})
|
||||
|
||||
return (
|
||||
<BottomSheetButton
|
||||
{...rest}
|
||||
style={[a.justify_start, flatten(rest.style)]}
|
||||
role="link"
|
||||
accessibilityRole="link"
|
||||
href={href}
|
||||
onPress={download ? undefined : onPress}
|
||||
{...web({
|
||||
hrefAttrs: {
|
||||
target: download ? undefined : isExternal ? 'blank' : undefined,
|
||||
rel: isExternal ? 'noopener noreferrer' : undefined,
|
||||
download,
|
||||
},
|
||||
dataSet: {
|
||||
// no underline, only `InlineLink` has underlines
|
||||
noUnderline: '1',
|
||||
},
|
||||
})}>
|
||||
{children}
|
||||
</BottomSheetButton>
|
||||
)
|
||||
}
|
||||
|
||||
export type InlineLinkProps = React.PropsWithChildren<
|
||||
BaseLinkProps & TextStyleProp & Pick<TextProps, 'selectable'>
|
||||
> &
|
||||
|
||||
@@ -4,10 +4,10 @@ import {AppBskyLabelerDefs} from '@atproto/api'
|
||||
import {msg, Trans} from '@lingui/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
|
||||
export {useDialogControl as useReportDialogControl} from '#/components/Dialog'
|
||||
import {getLabelingServiceTitle} from '#/lib/moderation'
|
||||
import {atoms as a, useBreakpoints, useTheme} from '#/alf'
|
||||
import {Button, useButtonContext} from '#/components/Button'
|
||||
import {BottomSheetButton} from '#/components/BottomSheetButton'
|
||||
import {useButtonContext} from '#/components/Button'
|
||||
import {Divider} from '#/components/Divider'
|
||||
import * as LabelingServiceCard from '#/components/LabelingServiceCard'
|
||||
import {Text} from '#/components/Typography'
|
||||
@@ -39,12 +39,12 @@ export function SelectLabelerView({
|
||||
<View style={[a.gap_sm]}>
|
||||
{props.labelers.map(labeler => {
|
||||
return (
|
||||
<Button
|
||||
<BottomSheetButton
|
||||
key={labeler.creator.did}
|
||||
label={_(msg`Send report to ${labeler.creator.displayName}`)}
|
||||
onPress={() => props.onSelectLabeler(labeler.creator.did)}>
|
||||
<LabelerButton labeler={labeler} />
|
||||
</Button>
|
||||
</BottomSheetButton>
|
||||
)
|
||||
})}
|
||||
</View>
|
||||
|
||||
@@ -5,7 +5,7 @@ import {msg, Trans} from '@lingui/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
|
||||
import {ReportOption, useReportOptions} from '#/lib/moderation/useReportOptions'
|
||||
import {Link} from '#/components/Link'
|
||||
import {BottomSheetLink} from '#/components/Link'
|
||||
import {DMCA_LINK} from '#/components/ReportDialog/const'
|
||||
export {useDialogControl as useReportDialogControl} from '#/components/Dialog'
|
||||
|
||||
@@ -125,7 +125,7 @@ export function SelectReportOptionView({
|
||||
]}>
|
||||
<Trans>Need to report a copyright violation?</Trans>
|
||||
</Text>
|
||||
<Link
|
||||
<BottomSheetLink
|
||||
to={DMCA_LINK}
|
||||
label={_(msg`View details for reporting a copyright violation`)}
|
||||
size="small"
|
||||
@@ -135,7 +135,7 @@ export function SelectReportOptionView({
|
||||
<Trans>View details</Trans>
|
||||
</ButtonText>
|
||||
<ButtonIcon position="right" icon={SquareArrowTopRight} />
|
||||
</Link>
|
||||
</BottomSheetLink>
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
|
||||
@@ -14,6 +14,7 @@ import {useAgent, useSession} from '#/state/session'
|
||||
import * as Toast from '#/view/com/util/Toast'
|
||||
import {atoms as a, useBreakpoints, useTheme} from '#/alf'
|
||||
import {BottomSheetButton} from '#/components/BottomSheetButton'
|
||||
import {BottomSheetInlineLinkText} from '#/components/BottomSheetLink'
|
||||
import {ButtonIcon, ButtonText} from '#/components/Button'
|
||||
import * as Dialog from '#/components/Dialog'
|
||||
import {InlineLinkText} from '#/components/Link'
|
||||
@@ -157,23 +158,25 @@ function Label({
|
||||
<Divider />
|
||||
|
||||
<View style={[a.px_md, a.py_sm, t.atoms.bg_contrast_25]}>
|
||||
<Text style={[t.atoms.text_contrast_medium]}>
|
||||
{isSelfLabel ? (
|
||||
{isSelfLabel ? (
|
||||
<Text style={[t.atoms.text_contrast_medium]}>
|
||||
<Trans>This label was applied by you.</Trans>
|
||||
) : (
|
||||
<Trans>
|
||||
Source:{' '}
|
||||
<InlineLinkText
|
||||
label={sourceName}
|
||||
to={makeProfileLink(
|
||||
labeler ? labeler.creator : {did: label.src, handle: ''},
|
||||
)}
|
||||
onPress={() => control.close()}>
|
||||
{sourceName}
|
||||
</InlineLinkText>
|
||||
</Trans>
|
||||
)}
|
||||
</Text>
|
||||
</Text>
|
||||
) : (
|
||||
<View style={{flexDirection: 'row'}}>
|
||||
<Text style={[t.atoms.text_contrast_medium]}>
|
||||
<Trans>Source: </Trans>{' '}
|
||||
</Text>
|
||||
<BottomSheetInlineLinkText
|
||||
label={sourceName}
|
||||
to={makeProfileLink(
|
||||
labeler ? labeler.creator : {did: label.src, handle: ''},
|
||||
)}
|
||||
onPress={() => control.close()}>
|
||||
{sourceName}
|
||||
</BottomSheetInlineLinkText>
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
</View>
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user