Bluesky app fork with some witchin' additions 💫
0
fork

Configure Feed

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

Swap `RichText` (#2934)

* Switch to new RT

* Allow early exit from links

* Build in tracking to text atoms

* Clean up FeedSourceCard

* Clean up leading after new default

* Add deprecated notice

authored by

Eric Bailey and committed by
GitHub
df5a8f15 8a169dc6

+65 -43
+17 -1
src/alf/atoms.ts
··· 176 176 }, 177 177 text_2xs: { 178 178 fontSize: tokens.fontSize._2xs, 179 + letterSpacing: 0.25, 179 180 }, 180 181 text_xs: { 181 182 fontSize: tokens.fontSize.xs, 183 + letterSpacing: 0.25, 182 184 }, 183 185 text_sm: { 184 186 fontSize: tokens.fontSize.sm, 187 + letterSpacing: 0.25, 185 188 }, 186 189 text_md: { 187 190 fontSize: tokens.fontSize.md, 191 + letterSpacing: 0.25, 188 192 }, 189 193 text_lg: { 190 194 fontSize: tokens.fontSize.lg, 195 + letterSpacing: 0.25, 191 196 }, 192 197 text_xl: { 193 198 fontSize: tokens.fontSize.xl, 199 + letterSpacing: 0.25, 194 200 }, 195 201 text_2xl: { 196 202 fontSize: tokens.fontSize._2xl, 203 + letterSpacing: 0.25, 197 204 }, 198 205 text_3xl: { 199 206 fontSize: tokens.fontSize._3xl, 207 + letterSpacing: 0.25, 200 208 }, 201 209 text_4xl: { 202 210 fontSize: tokens.fontSize._4xl, 211 + letterSpacing: 0.25, 203 212 }, 204 213 text_5xl: { 205 214 fontSize: tokens.fontSize._5xl, 215 + letterSpacing: 0.25, 206 216 }, 207 217 leading_tight: { 208 218 lineHeight: 1.15, 209 219 }, 210 220 leading_snug: { 211 - lineHeight: 1.25, 221 + lineHeight: 1.3, 212 222 }, 213 223 leading_normal: { 214 224 lineHeight: 1.5, 225 + }, 226 + tracking_normal: { 227 + letterSpacing: 0, 228 + }, 229 + tracking_wide: { 230 + letterSpacing: 0.25, 215 231 }, 216 232 font_normal: { 217 233 fontWeight: tokens.fontWeight.normal,
+6 -3
src/components/Link.tsx
··· 51 51 warnOnMismatchingTextChild?: boolean 52 52 53 53 /** 54 - * Callback for when the link is pressed. 54 + * Callback for when the link is pressed. Prevent default and return `false` 55 + * to exit early and prevent navigation. 55 56 * 56 57 * DO NOT use this for navigation, that's what the `to` prop is for. 57 58 */ 58 - onPress?: (e: GestureResponderEvent) => void 59 + onPress?: (e: GestureResponderEvent) => void | false 59 60 60 61 /** 61 62 * Web-only attribute. Sets `download` attr on web. ··· 82 83 83 84 const onPress = React.useCallback( 84 85 (e: GestureResponderEvent) => { 85 - outerOnPress?.(e) 86 + const exitEarlyIfFalse = outerOnPress?.(e) 87 + 88 + if (exitEarlyIfFalse === false) return 86 89 87 90 const requiresWarning = Boolean( 88 91 warnOnMismatchingTextChild &&
+2 -2
src/components/RichText.tsx
··· 1 1 import React from 'react' 2 2 import {RichText as RichTextAPI, AppBskyRichtextFacet} from '@atproto/api' 3 3 4 - import {atoms as a, TextStyleProp} from '#/alf' 4 + import {atoms as a, TextStyleProp, flatten} from '#/alf' 5 5 import {InlineLink} from '#/components/Link' 6 6 import {Text, TextProps} from '#/components/Typography' 7 7 import {toShortUrl} from 'lib/strings/url-helpers' ··· 29 29 const [richText, setRichText] = React.useState<RichTextAPI>(() => 30 30 value instanceof RichTextAPI ? value : new RichTextAPI({text: value}), 31 31 ) 32 - const styles = [a.leading_normal, style] 32 + const styles = [a.leading_snug, flatten(style)] 33 33 34 34 React.useEffect(() => { 35 35 if (!resolveFacets) return
+5 -3
src/view/com/feeds/FeedSourceCard.tsx
··· 2 2 import {Pressable, StyleProp, StyleSheet, View, ViewStyle} from 'react-native' 3 3 import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' 4 4 import {Text} from '../util/text/Text' 5 - import {RichText} from '../util/text/RichText' 5 + import {RichText} from '#/components/RichText' 6 6 import {usePalette} from 'lib/hooks/usePalette' 7 7 import {s} from 'lib/styles' 8 8 import {UserAvatar} from '../util/UserAvatar' ··· 25 25 } from '#/state/queries/preferences' 26 26 import {useFeedSourceInfoQuery, FeedSourceInfo} from '#/state/queries/feed' 27 27 import {FeedLoadingPlaceholder} from '#/view/com/util/LoadingPlaceholder' 28 + import {useTheme} from '#/alf' 28 29 29 30 export function FeedSourceCard({ 30 31 feedUri, ··· 82 83 pinOnSave?: boolean 83 84 showMinimalPlaceholder?: boolean 84 85 }) { 86 + const t = useTheme() 85 87 const pal = usePalette('default') 86 88 const {_} = useLingui() 87 89 const navigation = useNavigation<NavigationProp>() ··· 266 268 267 269 {showDescription && feed.description ? ( 268 270 <RichText 269 - style={[pal.textLight, styles.description]} 270 - richText={feed.description} 271 + style={[t.atoms.text_contrast_high, styles.description]} 272 + value={feed.description} 271 273 numberOfLines={3} 272 274 /> 273 275 ) : null}
+4 -3
src/view/com/lists/ListCard.tsx
··· 3 3 import {AtUri, AppBskyGraphDefs, RichText} from '@atproto/api' 4 4 import {Link} from '../util/Link' 5 5 import {Text} from '../util/text/Text' 6 - import {RichText as RichTextCom} from '../util/text/RichText' 6 + import {RichText as RichTextCom} from '#/components/RichText' 7 7 import {UserAvatar} from '../util/UserAvatar' 8 8 import {s} from 'lib/styles' 9 9 import {usePalette} from 'lib/hooks/usePalette' ··· 12 12 import {sanitizeHandle} from 'lib/strings/handles' 13 13 import {makeProfileLink} from 'lib/routes/links' 14 14 import {Trans} from '@lingui/macro' 15 + import {atoms as a} from '#/alf' 15 16 16 17 export const ListCard = ({ 17 18 testID, ··· 119 120 {descriptionRichText ? ( 120 121 <View style={styles.details}> 121 122 <RichTextCom 122 - style={[pal.text, s.flex1]} 123 + style={[a.flex_1]} 123 124 numberOfLines={20} 124 - richText={descriptionRichText} 125 + value={descriptionRichText} 125 126 /> 126 127 </View> 127 128 ) : undefined}
+6 -9
src/view/com/post-thread/PostThreadItem.tsx
··· 11 11 import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' 12 12 import {PostThreadFollowBtn} from 'view/com/post-thread/PostThreadFollowBtn' 13 13 import {Link, TextLink} from '../util/Link' 14 - import {RichText} from '../util/text/RichText' 14 + import {RichText} from '#/components/RichText' 15 15 import {Text} from '../util/text/Text' 16 16 import {PreviewableUserAvatar} from '../util/UserAvatar' 17 17 import {s} from 'lib/styles' ··· 44 44 import {useSession} from 'state/session' 45 45 import {WhoCanReply} from '../threadgate/WhoCanReply' 46 46 import {LoadingPlaceholder} from '../util/LoadingPlaceholder' 47 + import {atoms as a} from '#/alf' 47 48 48 49 export function PostThreadItem({ 49 50 post, ··· 326 327 styles.postTextLargeContainer, 327 328 ]}> 328 329 <RichText 329 - type="post-text-lg" 330 - richText={richText} 331 - lineHeight={1.3} 332 - style={s.flex1} 330 + value={richText} 331 + style={[a.flex_1, a.text_xl]} 333 332 selectable 334 333 /> 335 334 </View> ··· 522 521 {richText?.text ? ( 523 522 <View style={styles.postTextContainer}> 524 523 <RichText 525 - type="post-text" 526 - richText={richText} 527 - style={[pal.text, s.flex1]} 528 - lineHeight={1.3} 524 + value={richText} 525 + style={[a.flex_1, a.text_md]} 529 526 numberOfLines={limitLines ? MAX_POST_LINES : undefined} 530 527 /> 531 528 </View>
+4 -5
src/view/com/posts/FeedItem.tsx
··· 20 20 import {PostEmbeds} from '../util/post-embeds' 21 21 import {ContentHider} from '../util/moderation/ContentHider' 22 22 import {PostAlerts} from '../util/moderation/PostAlerts' 23 - import {RichText} from '../util/text/RichText' 23 + import {RichText} from '#/components/RichText' 24 24 import {PreviewableUserAvatar} from '../util/UserAvatar' 25 25 import {s} from 'lib/styles' 26 26 import {usePalette} from 'lib/hooks/usePalette' ··· 36 36 import {useSession} from '#/state/session' 37 37 import {Trans, msg} from '@lingui/macro' 38 38 import {useLingui} from '@lingui/react' 39 + import {atoms as a} from '#/alf' 39 40 40 41 export function FeedItem({ 41 42 post, ··· 347 348 <View style={styles.postTextContainer}> 348 349 <RichText 349 350 testID="postText" 350 - type="post-text" 351 - richText={richText} 352 - lineHeight={1.3} 351 + value={richText} 353 352 numberOfLines={limitLines ? MAX_POST_LINES : undefined} 354 - style={s.flex1} 353 + style={[a.flex_1, a.text_md]} 355 354 /> 356 355 </View> 357 356 ) : undefined}
+5 -4
src/view/com/profile/ProfileHeader.tsx
··· 23 23 import {LoadingPlaceholder} from '../util/LoadingPlaceholder' 24 24 import {Text} from '../util/text/Text' 25 25 import {ThemedText} from '../util/text/ThemedText' 26 - import {RichText} from '../util/text/RichText' 26 + import {RichText} from '#/components/RichText' 27 27 import {UserAvatar} from '../util/UserAvatar' 28 28 import {UserBanner} from '../util/UserBanner' 29 29 import {ProfileHeaderAlerts} from '../util/moderation/ProfileHeaderAlerts' ··· 56 56 import {useRequireAuth} from '#/state/session' 57 57 import {LabelInfo} from '../util/moderation/LabelInfo' 58 58 import {useProfileShadow} from 'state/cache/profile-shadow' 59 + import {atoms as a} from '#/alf' 59 60 60 61 let ProfileHeaderLoading = (_props: {}): React.ReactNode => { 61 62 const pal = usePalette('default') ··· 608 609 </Text> 609 610 </View> 610 611 {descriptionRT && !moderation.profile.blur ? ( 611 - <View pointerEvents="auto"> 612 + <View pointerEvents="auto" style={[styles.description]}> 612 613 <RichText 613 614 testID="profileHeaderDescription" 614 - style={[styles.description, pal.text]} 615 + style={[a.text_md]} 615 616 numberOfLines={15} 616 - richText={descriptionRT} 617 + value={descriptionRT} 617 618 /> 618 619 </View> 619 620 ) : undefined}
+5 -5
src/view/com/util/post-embeds/QuoteEmbed.tsx
··· 20 20 import {makeProfileLink} from 'lib/routes/links' 21 21 import {InfoCircleIcon} from 'lib/icons' 22 22 import {Trans} from '@lingui/macro' 23 - import {RichText} from 'view/com/util/text/RichText' 23 + import {RichText} from '#/components/RichText' 24 + import {atoms as a} from '#/alf' 24 25 25 26 export function MaybeQuoteEmbed({ 26 27 embed, ··· 127 128 ) : null} 128 129 {richText ? ( 129 130 <RichText 130 - richText={richText} 131 - type="post-text" 132 - style={pal.text} 131 + value={richText} 132 + style={[a.text_md]} 133 133 numberOfLines={20} 134 - noLinks 134 + disableLinks 135 135 /> 136 136 ) : null} 137 137 {embed && <PostEmbeds embed={embed} moderation={{}} />}
+3
src/view/com/util/text/RichText.tsx
··· 10 10 11 11 const WORD_WRAP = {wordWrap: 1} 12 12 13 + /** 14 + * @deprecated use `#/components/RichText` 15 + */ 13 16 export function RichText({ 14 17 testID, 15 18 type = 'md',
+4 -4
src/view/screens/ProfileFeed.tsx
··· 17 17 import {ListRef} from 'view/com/util/List' 18 18 import {Button} from 'view/com/util/forms/Button' 19 19 import {Text} from 'view/com/util/text/Text' 20 - import {RichText} from 'view/com/util/text/RichText' 20 + import {RichText} from '#/components/RichText' 21 21 import {LoadLatestBtn} from 'view/com/util/load-latest/LoadLatestBtn' 22 22 import {FAB} from 'view/com/util/fab/FAB' 23 23 import {EmptyState} from 'view/com/util/EmptyState' ··· 59 59 import {truncateAndInvalidate} from '#/state/queries/util' 60 60 import {isNative} from '#/platform/detection' 61 61 import {listenSoftReset} from '#/state/events' 62 + import {atoms as a} from '#/alf' 62 63 63 64 const SECTION_TITLES = ['Posts', 'About'] 64 65 ··· 575 576 {feedInfo.description ? ( 576 577 <RichText 577 578 testID="listDescription" 578 - type="lg" 579 - style={pal.text} 580 - richText={feedInfo.description} 579 + style={[a.text_md]} 580 + value={feedInfo.description} 581 581 /> 582 582 ) : ( 583 583 <Text type="lg" style={[{fontStyle: 'italic'}, pal.textLight]}>
+4 -4
src/view/screens/ProfileList.tsx
··· 14 14 import {CenteredView} from 'view/com/util/Views' 15 15 import {EmptyState} from 'view/com/util/EmptyState' 16 16 import {LoadingScreen} from 'view/com/util/LoadingScreen' 17 - import {RichText} from 'view/com/util/text/RichText' 17 + import {RichText} from '#/components/RichText' 18 18 import {Button} from 'view/com/util/forms/Button' 19 19 import {TextLink} from 'view/com/util/Link' 20 20 import {ListRef} from 'view/com/util/List' ··· 60 60 import {logger} from '#/logger' 61 61 import {useAnalytics} from '#/lib/analytics/analytics' 62 62 import {listenSoftReset} from '#/state/events' 63 + import {atoms as a} from '#/alf' 63 64 64 65 const SECTION_TITLES_CURATE = ['Posts', 'About'] 65 66 const SECTION_TITLES_MOD = ['About'] ··· 742 743 {descriptionRT ? ( 743 744 <RichText 744 745 testID="listDescription" 745 - type="lg" 746 - style={pal.text} 747 - richText={descriptionRT} 746 + style={[a.text_md]} 747 + value={descriptionRT} 748 748 /> 749 749 ) : ( 750 750 <Text