Lighten blue (link) text for dim and dark themes (#11164)

This commit is contained in:
DS Boyce
2026-07-20 09:29:58 -07:00
committed by GitHub
parent 0fb7251c64
commit 1803de03a4
13 changed files with 49 additions and 68 deletions
-8
View File
@@ -1617,14 +1617,6 @@
"count": 1 "count": 1
} }
}, },
"src/view/com/composer/text-input/TextInput.tsx": {
"typescript/no-floating-promises": {
"count": 1
},
"typescript/no-misused-promises": {
"count": 1
}
},
"src/view/com/composer/text-input/TextInput.web.tsx": { "src/view/com/composer/text-input/TextInput.web.tsx": {
"typescript/no-misused-promises": { "typescript/no-misused-promises": {
"count": 1 "count": 1
+1 -1
View File
@@ -101,7 +101,7 @@
"@atproto/syntax": "0.7.2", "@atproto/syntax": "0.7.2",
"@bitdrift/react-native": "^0.6.8", "@bitdrift/react-native": "^0.6.8",
"@braintree/sanitize-url": "^6.0.2", "@braintree/sanitize-url": "^6.0.2",
"@bsky.app/alf": "^0.1.14", "@bsky.app/alf": "^0.1.15",
"@bsky.app/expo-dynamic-app-icon": "^1.8.5", "@bsky.app/expo-dynamic-app-icon": "^1.8.5",
"@bsky.app/expo-guess-language": "^0.2.8", "@bsky.app/expo-guess-language": "^0.2.8",
"@bsky.app/expo-image-crop-tool": "^0.5.1", "@bsky.app/expo-image-crop-tool": "^0.5.1",
+5 -5
View File
@@ -257,8 +257,8 @@ importers:
specifier: ^6.0.2 specifier: ^6.0.2
version: 6.0.4 version: 6.0.4
'@bsky.app/alf': '@bsky.app/alf':
specifier: ^0.1.14 specifier: ^0.1.15
version: 0.1.14(react-native@0.81.5(patch_hash=2656ac6deb71b92a4df4af4593d13ace6a5740936432e5e7e8ef32bc3cd05194)(@babel/core@7.29.0)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) version: 0.1.15(react-native@0.81.5(patch_hash=2656ac6deb71b92a4df4af4593d13ace6a5740936432e5e7e8ef32bc3cd05194)(@babel/core@7.29.0)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0)
'@bsky.app/expo-dynamic-app-icon': '@bsky.app/expo-dynamic-app-icon':
specifier: ^1.8.5 specifier: ^1.8.5
version: 1.8.5(expo@54.0.34(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=2656ac6deb71b92a4df4af4593d13ace6a5740936432e5e7e8ef32bc3cd05194)(@babel/core@7.29.0)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=2656ac6deb71b92a4df4af4593d13ace6a5740936432e5e7e8ef32bc3cd05194)(@babel/core@7.29.0)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=2656ac6deb71b92a4df4af4593d13ace6a5740936432e5e7e8ef32bc3cd05194)(@babel/core@7.29.0)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) version: 1.8.5(expo@54.0.34(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=2656ac6deb71b92a4df4af4593d13ace6a5740936432e5e7e8ef32bc3cd05194)(@babel/core@7.29.0)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=2656ac6deb71b92a4df4af4593d13ace6a5740936432e5e7e8ef32bc3cd05194)(@babel/core@7.29.0)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=2656ac6deb71b92a4df4af4593d13ace6a5740936432e5e7e8ef32bc3cd05194)(@babel/core@7.29.0)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0)
@@ -1606,8 +1606,8 @@ packages:
'@braintree/sanitize-url@6.0.4': '@braintree/sanitize-url@6.0.4':
resolution: {integrity: sha512-s3jaWicZd0pkP0jf5ysyHUI/RE7MHos6qlToFcGWXVp+ykHOy77OUMrfbgJ9it2C5bow7OIQwYYaHjk9XlBQ2A==} resolution: {integrity: sha512-s3jaWicZd0pkP0jf5ysyHUI/RE7MHos6qlToFcGWXVp+ykHOy77OUMrfbgJ9it2C5bow7OIQwYYaHjk9XlBQ2A==}
'@bsky.app/alf@0.1.14': '@bsky.app/alf@0.1.15':
resolution: {integrity: sha512-c/KK6avyjEnYzhXDsN0rQaTFSbi8BPg4sJCW8aUk8AtKsO34GTaY6FU1adQ2i8xVfe5UIRu9FJjnBoGpuXj21A==} resolution: {integrity: sha512-e6blt+oZ2klv+Cp7u11FeZckoILdXOtUZ11ATERgjZgb4zX3y7rt4e2fI03JkJO31wTvXHG8A1LnM96Az+ndJg==}
peerDependencies: peerDependencies:
react: '*' react: '*'
react-native: '*' react-native: '*'
@@ -10072,7 +10072,7 @@ snapshots:
'@braintree/sanitize-url@6.0.4': {} '@braintree/sanitize-url@6.0.4': {}
'@bsky.app/alf@0.1.14(react-native@0.81.5(patch_hash=2656ac6deb71b92a4df4af4593d13ace6a5740936432e5e7e8ef32bc3cd05194)(@babel/core@7.29.0)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0)': '@bsky.app/alf@0.1.15(react-native@0.81.5(patch_hash=2656ac6deb71b92a4df4af4593d13ace6a5740936432e5e7e8ef32bc3cd05194)(@babel/core@7.29.0)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0)':
dependencies: dependencies:
react: 19.1.0 react: 19.1.0
react-native: 0.81.5(patch_hash=2656ac6deb71b92a4df4af4593d13ace6a5740936432e5e7e8ef32bc3cd05194)(@babel/core@7.29.0)(@types/react@19.1.17)(react@19.1.0) react-native: 0.81.5(patch_hash=2656ac6deb71b92a4df4af4593d13ace6a5740936432e5e7e8ef32bc3cd05194)(@babel/core@7.29.0)(@types/react@19.1.17)(react@19.1.0)
+1 -1
View File
@@ -286,7 +286,7 @@ export function Composer({
ref={IS_WEB ? sift.refs.setAnchor : undefined} ref={IS_WEB ? sift.refs.setAnchor : undefined}
style={ style={
node.type === 'facet' && { node.type === 'facet' && {
color: t.palette.primary_500, color: t.atoms.text_link.color,
} }
}> }>
{node.raw} {node.raw}
+2 -2
View File
@@ -481,11 +481,11 @@ export function ProfileGrid({
<Text <Text
style={[ style={[
a.text_sm, a.text_sm,
{color: t.palette.primary_500}, t.atoms.text_link,
hovered && hovered &&
web({ web({
textDecorationLine: 'underline', textDecorationLine: 'underline',
textDecorationColor: t.palette.primary_500, textDecorationColor: t.atoms.text_link.color,
}), }),
]}> ]}>
<Trans>See more</Trans> <Trans>See more</Trans>
+5 -5
View File
@@ -415,7 +415,7 @@ function LinkPeek({
// dialog can show. // dialog can show.
useInAppBrowser: useInAppBrowserPref === true, useInAppBrowser: useInAppBrowserPref === true,
browserToolbarColor: t.atoms.bg.backgroundColor, browserToolbarColor: t.atoms.bg.backgroundColor,
browserControlsColor: t.palette.primary_500, browserControlsColor: t.atoms.text_link.color,
}} }}
borderRadius={borderRadius} borderRadius={borderRadius}
// Fires only when not morphing natively (in-app browser off/unset). // Fires only when not morphing natively (in-app browser off/unset).
@@ -487,14 +487,14 @@ export function InlineLinkText({
accessibilityLabel={label} accessibilityLabel={label}
{...rest} {...rest}
style={[ style={[
{color: t.palette.primary_500}, t.atoms.text_link,
interacted && interacted &&
!disableUnderline && { !disableUnderline && {
...web({ ...web({
outline: 0, outline: 0,
textDecorationLine: 'underline', textDecorationLine: 'underline',
textDecorationColor: textDecorationColor:
flattenedStyle.color ?? t.palette.primary_500, flattenedStyle.color ?? t.atoms.text_link.color,
}), }),
}, },
flattenedStyle, flattenedStyle,
@@ -591,14 +591,14 @@ export function SimpleInlineLinkText({
accessibilityLabel={label} accessibilityLabel={label}
{...rest} {...rest}
style={[ style={[
{color: t.palette.primary_500}, t.atoms.text_link,
interacted && interacted &&
!disableUnderline && { !disableUnderline && {
...web({ ...web({
outline: 0, outline: 0,
textDecorationLine: 'underline', textDecorationLine: 'underline',
textDecorationColor: textDecorationColor:
flattenedStyle.color ?? t.palette.primary_500, flattenedStyle.color ?? t.atoms.text_link.color,
}), }),
}, },
flattenedStyle, flattenedStyle,
+5 -7
View File
@@ -1,8 +1,6 @@
import {useCallback, useMemo} from 'react' import {useCallback, useMemo} from 'react'
import {LayoutAnimation, type TextStyle} from 'react-native' import {LayoutAnimation, type TextStyle} from 'react-native'
import {msg} from '@lingui/core/macro' import {Trans, useLingui} from '@lingui/react/macro'
import {useLingui} from '@lingui/react'
import {Trans} from '@lingui/react/macro'
import {HITSLOP_10} from '#/lib/constants' import {HITSLOP_10} from '#/lib/constants'
import {atoms as a, flatten, type TextStyleProp, useTheme} from '#/alf' import {atoms as a, flatten, type TextStyleProp, useTheme} from '#/alf'
@@ -14,7 +12,7 @@ export function ShowMoreTextButton({
style, style,
}: TextStyleProp & {onPress: () => void}) { }: TextStyleProp & {onPress: () => void}) {
const t = useTheme() const t = useTheme()
const {_} = useLingui() const {t: l} = useLingui()
const onPress = useCallback(() => { const onPress = useCallback(() => {
LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut) LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut)
@@ -30,7 +28,7 @@ export function ShowMoreTextButton({
return ( return (
<Button <Button
label={_(msg`Expand post text`)} label={l`Expand post text`}
onPress={onPress} onPress={onPress}
style={[ style={[
a.self_start, a.self_start,
@@ -43,13 +41,13 @@ export function ShowMoreTextButton({
<Text <Text
style={[ style={[
textStyle, textStyle,
t.atoms.text_link,
{ {
color: t.palette.primary_500,
opacity: pressed ? 0.6 : 1, opacity: pressed ? 0.6 : 1,
textDecorationLine: hovered ? 'underline' : undefined, textDecorationLine: hovered ? 'underline' : undefined,
}, },
]}> ]}>
<Trans>Show More</Trans> <Trans>Show more</Trans>
</Text> </Text>
)} )}
</Button> </Button>
+4 -4
View File
@@ -150,10 +150,10 @@ function TranslationLink({
label={l`Translate`} label={l`Translate`}
hoverStyle={[ hoverStyle={[
native({opacity: 0.5}), native({opacity: 0.5}),
web([a.underline, {textDecorationColor: t.palette.primary_500}]), web([a.underline, {textDecorationColor: t.atoms.text_link.color}]),
]} ]}
hitSlop={HITSLOP_30}> hitSlop={HITSLOP_30}>
<Text style={[a.text_sm, {color: t.palette.primary_500}]}> <Text style={[a.text_sm, t.atoms.text_link]}>
<Trans>Translate</Trans> <Trans>Translate</Trans>
</Text> </Text>
</Link> </Link>
@@ -229,7 +229,7 @@ function TranslationError({
label={l`Try Google Translate`} label={l`Try Google Translate`}
hoverStyle={[ hoverStyle={[
native({opacity: 0.5}), native({opacity: 0.5}),
web([a.underline, {textDecorationColor: t.palette.primary_500}]), web([a.underline, {textDecorationColor: t.atoms.text_link.color}]),
]} ]}
hitSlop={HITSLOP_30}> hitSlop={HITSLOP_30}>
<Text <Text
@@ -237,7 +237,7 @@ function TranslationError({
a.text_xs, a.text_xs,
a.font_medium, a.font_medium,
a.leading_snug, a.leading_snug,
{color: t.palette.primary_500}, t.atoms.text_link,
]}> ]}>
<Trans>Try Google Translate</Trans> <Trans>Try Google Translate</Trans>
</Text> </Text>
+10 -17
View File
@@ -6,9 +6,7 @@ import {
type ViewStyle, type ViewStyle,
} from 'react-native' } from 'react-native'
import {type ModerationUI} from '@atproto/api' import {type ModerationUI} from '@atproto/api'
import {msg} from '@lingui/core/macro' import {Trans, useLingui} from '@lingui/react/macro'
import {useLingui} from '@lingui/react'
import {Trans} from '@lingui/react/macro'
import { import {
ADULT_CONTENT_LABELS, ADULT_CONTENT_LABELS,
@@ -78,7 +76,7 @@ function ContentHiderActive({
children?: React.ReactNode children?: React.ReactNode
}) { }) {
const t = useTheme() const t = useTheme()
const {_} = useLingui() const {t: l} = useLingui()
const {gtMobile} = useBreakpoints() const {gtMobile} = useBreakpoints()
const [override, setOverride] = useState(false) const [override, setOverride] = useState(false)
const control = useModerationDetailsDialogControl() const control = useModerationDetailsDialogControl()
@@ -97,7 +95,7 @@ function ContentHiderActive({
(blur.type === 'label' && blur.source.type !== 'user') (blur.type === 'label' && blur.source.type !== 'user')
) { ) {
if (desc.isSubjectAccount) { if (desc.isSubjectAccount) {
return _(msg`${desc.name} (Account)`) return l`${desc.name} (Account)`
} else { } else {
return desc.name return desc.name
} }
@@ -128,7 +126,7 @@ function ContentHiderActive({
const def = cause.labelDef || getDefinition(labelDefs, cause.label) const def = cause.labelDef || getDefinition(labelDefs, cause.label)
if (def.identifier === 'porn' || def.identifier === 'sexual') { if (def.identifier === 'porn' || def.identifier === 'sexual') {
return _(msg`Adult Content`) return l`Adult Content`
} }
return getLabelStrings(i18n.locale, globalLabelStrings, def).name return getLabelStrings(i18n.locale, globalLabelStrings, def).name
}) })
@@ -138,7 +136,7 @@ function ContentHiderActive({
} }
return [...new Set(selfBlurNames)].join(', ') return [...new Set(selfBlurNames)].join(', ')
}, [ }, [
_, l,
modui.blurs, modui.blurs,
blur, blur,
desc.name, desc.name,
@@ -151,7 +149,6 @@ function ContentHiderActive({
return ( return (
<View testID={testID} style={[a.overflow_hidden, style]}> <View testID={testID} style={[a.overflow_hidden, style]}>
<ModerationDetailsDialog control={control} modcause={blur} /> <ModerationDetailsDialog control={control} modcause={blur} />
<Button <Button
onPress={e => { onPress={e => {
e.preventDefault() e.preventDefault()
@@ -166,10 +163,10 @@ function ContentHiderActive({
label={desc.name} label={desc.name}
accessibilityHint={ accessibilityHint={
modui.noOverride modui.noOverride
? _(msg`Learn more about the moderation applied to this content`) ? l`Learn more about the moderation applied to this content`
: override : override
? _(msg`Hides the content`) ? l`Hides the content`
: _(msg`Shows the content`) : l`Shows the content`
}> }>
{state => ( {state => (
<View <View
@@ -223,7 +220,6 @@ function ContentHiderActive({
</View> </View>
)} )}
</Button> </Button>
{desc.source && blur.type === 'label' && !override && ( {desc.source && blur.type === 'label' && !override && (
<Button <Button
onPress={e => { onPress={e => {
@@ -231,9 +227,7 @@ function ContentHiderActive({
e.stopPropagation() e.stopPropagation()
control.open() control.open()
}} }}
label={_( label={l`Learn more about the moderation applied to this content`}
msg`Learn more about the moderation applied to this content`,
)}
style={[a.pt_sm]}> style={[a.pt_sm]}>
{state => ( {state => (
<Text <Text
@@ -252,7 +246,7 @@ function ContentHiderActive({
)}{' '} )}{' '}
<Text <Text
style={[ style={[
{color: t.palette.primary_500}, t.atoms.text_link,
a.text_sm, a.text_sm,
state.hovered && [web({textDecoration: 'underline'})], state.hovered && [web({textDecoration: 'underline'})],
]}> ]}>
@@ -262,7 +256,6 @@ function ContentHiderActive({
)} )}
</Button> </Button>
)} )}
{override && <View style={childContainerStyle}>{children}</View>} {override && <View style={childContainerStyle}>{children}</View>}
</View> </View>
) )
+6 -10
View File
@@ -6,9 +6,7 @@ import {
type ViewStyle, type ViewStyle,
} from 'react-native' } from 'react-native'
import {type ModerationUI} from '@atproto/api' import {type ModerationUI} from '@atproto/api'
import {msg} from '@lingui/core/macro' import {Trans, useLingui} from '@lingui/react/macro'
import {useLingui} from '@lingui/react'
import {Trans} from '@lingui/react/macro'
import {useNavigation} from '@react-navigation/native' import {useNavigation} from '@react-navigation/native'
import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries' import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries'
@@ -38,7 +36,7 @@ export function ScreenHider({
containerStyle?: StyleProp<ViewStyle> containerStyle?: StyleProp<ViewStyle>
}>) { }>) {
const t = useTheme() const t = useTheme()
const {_} = useLingui() const {t: l} = useLingui()
const [override, setOverride] = useState(false) const [override, setOverride] = useState(false)
const navigation = useNavigation<NavigationProp>() const navigation = useNavigation<NavigationProp>()
const {isMobile} = useWebMediaQueries() const {isMobile} = useWebMediaQueries()
@@ -131,15 +129,13 @@ export function ScreenHider({
control.open() control.open()
}} }}
accessibilityRole="button" accessibilityRole="button"
accessibilityLabel={_(msg`Learn more about this warning`)} accessibilityLabel={l`Learn more about this warning`}
accessibilityHint=""> accessibilityHint="">
<Text <Text
style={[ style={[
a.text_lg, a.text_lg,
a.leading_snug, a.leading_snug,
{ t.atoms.text_link,
color: t.palette.primary_500,
},
web({ web({
cursor: 'pointer', cursor: 'pointer',
}), }),
@@ -158,7 +154,7 @@ export function ScreenHider({
color="primary" color="primary"
size="large" size="large"
style={[a.rounded_full]} style={[a.rounded_full]}
label={_(msg`Go back`)} label={l`Go back`}
onPress={() => { onPress={() => {
if (navigation.canGoBack()) { if (navigation.canGoBack()) {
navigation.goBack() navigation.goBack()
@@ -176,7 +172,7 @@ export function ScreenHider({
color="secondary" color="secondary"
size="large" size="large"
style={[a.rounded_full]} style={[a.rounded_full]}
label={_(msg`Show anyway`)} label={l`Show anyway`}
onPress={() => setOverride(v => !v)}> onPress={() => setOverride(v => !v)}>
<ButtonText> <ButtonText>
<Trans>Show anyway</Trans> <Trans>Show anyway</Trans>
@@ -311,7 +311,7 @@ export function BadgeButton({
a.text_md, a.text_md,
a.font_normal, a.font_normal,
a.text_right, a.text_right,
{color: pressed ? t.palette.contrast_300 : t.palette.primary_500}, {color: pressed ? t.palette.contrast_300 : t.atoms.text_link.color},
]}> ]}>
{label} {label}
</Button.ButtonText> </Button.ButtonText>
@@ -6,10 +6,9 @@ import {
useState, useState,
} from 'react' } from 'react'
import { import {
type NativeSyntheticEvent,
Text as RNText, Text as RNText,
TextInput as RNTextInput, TextInput as RNTextInput,
type TextInputSelectionChangeEventData, type TextInputSelectionChangeEvent,
View, View,
} from 'react-native' } from 'react-native'
import {type PasteEventPayload, TextInputWrapper} from 'expo-paste-input' import {type PasteEventPayload, TextInputWrapper} from 'expo-paste-input'
@@ -141,7 +140,7 @@ export function TextInput({
) )
const onSelectionChange = useCallback( const onSelectionChange = useCallback(
(evt: NativeSyntheticEvent<TextInputSelectionChangeEventData>) => { (evt: TextInputSelectionChangeEvent) => {
// NOTE we track the input selection using a ref to avoid excessive renders -prf // NOTE we track the input selection using a ref to avoid excessive renders -prf
textInputSelection.current = evt.nativeEvent.selection textInputSelection.current = evt.nativeEvent.selection
}, },
@@ -150,7 +149,7 @@ export function TextInput({
const onSelectAutocompleteItem = useCallback( const onSelectAutocompleteItem = useCallback(
(item: string) => { (item: string) => {
onChangeText( void onChangeText(
insertMentionAt( insertMentionAt(
richtext.text, richtext.text,
textInputSelection.current?.start || 0, textInputSelection.current?.start || 0,
@@ -201,7 +200,9 @@ export function TextInput({
style={[ style={[
inputTextStyle, inputTextStyle,
{ {
color: segment.facet ? t.palette.primary_500 : t.atoms.text.color, color: segment.facet
? t.atoms.text_link.color
: t.atoms.text.color,
marginTop: -1, marginTop: -1,
}, },
]}> ]}>
@@ -217,7 +218,7 @@ export function TextInput({
<RNTextInput <RNTextInput
testID="composerTextInput" testID="composerTextInput"
ref={textInput} ref={textInput}
onChangeText={onChangeText} onChangeText={(newText: string) => void onChangeText(newText)}
onSelectionChange={onSelectionChange} onSelectionChange={onSelectionChange}
placeholder={placeholder} placeholder={placeholder}
placeholderTextColor={t.atoms.text_contrast_low.color} placeholderTextColor={t.atoms.text_contrast_low.color}
+2 -1
View File
@@ -58,7 +58,8 @@ export function ViewFullThread({uri}: {uri: string}) {
<Text <Text
style={[ style={[
a.text_md, a.text_md,
{color: t.palette.primary_500, paddingTop: 18, paddingBottom: 4}, t.atoms.text_link,
{paddingTop: 18, paddingBottom: 4},
]}> ]}>
{/* HACKFIX: Trans isn't working after SDK 53 upgrade -sfn */} {/* HACKFIX: Trans isn't working after SDK 53 upgrade -sfn */}
{l`View full thread`} {l`View full thread`}