my fork of the bluesky client

Enforce Text suffix for Text-rendering components (#3407)

* Rm unused

* Add Text suffix to Title/Description

* Add Text suffix to text components

* Add Text suffix to props

* Validate Text components returns

authored by danabra.mov and committed by

GitHub 3915bb43 c190fd58

+430 -343
+1 -14
.eslintrc.js
··· 1 - const bskyEslint = require('./eslint') 2 - 3 1 module.exports = { 4 2 root: true, 5 3 extends: [ ··· 27 25 { 28 26 impliedTextComponents: [ 29 27 'Button', // TODO: Not always safe. 30 - 'ButtonText', 31 - 'DateField.Label', 32 - 'Description', 33 28 'H1', 34 29 'H2', 35 30 'H3', 36 31 'H4', 37 32 'H5', 38 33 'H6', 39 - 'InlineLink', 40 - 'Label', 41 34 'P', 42 - 'Prompt.Title', 43 - 'Prompt.Description', 44 35 'Prompt.Cancel', // TODO: Not always safe. 45 36 'Prompt.Action', // TODO: Not always safe. 46 - 'TextField.Label', 47 - 'TextField.Suffix', 48 - 'Title', 49 - 'Toggle.Label', 50 37 'ToggleButton.Button', // TODO: Not always safe. 51 38 ], 52 - impliedTextProps: ['FormContainer title'], 39 + impliedTextProps: [], 53 40 }, 54 41 ], 55 42 'simple-import-sort/imports': [
+65
eslint/__tests__/avoid-unwrapped-text.test.js
··· 246 246 </Foo> 247 247 `, 248 248 }, 249 + 250 + { 251 + code: ` 252 + function Stuff() { 253 + return <Text>foo</Text> 254 + } 255 + `, 256 + }, 257 + 258 + { 259 + code: ` 260 + function Stuff({ foo }) { 261 + return <View>{foo}</View> 262 + } 263 + `, 264 + }, 265 + 266 + { 267 + code: ` 268 + function MyText() { 269 + return <Text>foo</Text> 270 + } 271 + `, 272 + }, 273 + 274 + { 275 + code: ` 276 + function MyText({ foo }) { 277 + if (foo) { 278 + return <Text>foo</Text> 279 + } 280 + return <Text>foo</Text> 281 + } 282 + `, 283 + }, 249 284 ], 250 285 251 286 invalid: [ ··· 387 422 }> 388 423 <Bar /> 389 424 </Foo> 425 + `, 426 + errors: 1, 427 + }, 428 + 429 + { 430 + code: ` 431 + function MyText() { 432 + return <Foo /> 433 + } 434 + `, 435 + errors: 1, 436 + }, 437 + 438 + { 439 + code: ` 440 + function MyText({ foo }) { 441 + return <Foo>{foo}</Foo> 442 + } 443 + `, 444 + errors: 1, 445 + }, 446 + 447 + { 448 + code: ` 449 + function MyText({ foo }) { 450 + if (foo) { 451 + return <Foo>{foo}</Foo> 452 + } 453 + return <Text>foo</Text> 454 + } 390 455 `, 391 456 errors: 1, 392 457 },
+37 -1
eslint/avoid-unwrapped-text.js
··· 35 35 const impliedTextComponents = options.impliedTextComponents ?? [] 36 36 const textProps = [...impliedTextProps] 37 37 const textComponents = ['Text', ...impliedTextComponents] 38 + 39 + function isTextComponent(tagName) { 40 + return textComponents.includes(tagName) || tagName.endsWith('Text') 41 + } 42 + 38 43 return { 39 44 JSXText(node) { 40 45 if (typeof node.value !== 'string' || hasOnlyLineBreak(node.value)) { ··· 44 49 while (parent) { 45 50 if (parent.type === 'JSXElement') { 46 51 const tagName = getTagName(parent) 47 - if (textComponents.includes(tagName) || tagName.endsWith('Text')) { 52 + if (isTextComponent(tagName)) { 48 53 // We're good. 49 54 return 50 55 } ··· 105 110 106 111 parent = parent.parent 107 112 continue 113 + } 114 + }, 115 + ReturnStatement(node) { 116 + let fnScope = context.getScope() 117 + while (fnScope && fnScope.type !== 'function') { 118 + fnScope = fnScope.upper 119 + } 120 + if (!fnScope) { 121 + return 122 + } 123 + const fn = fnScope.block 124 + if (!fn.id || fn.id.type !== 'Identifier' || !fn.id.name) { 125 + return 126 + } 127 + if (!/^[A-Z]\w*Text$/.test(fn.id.name)) { 128 + return 129 + } 130 + if (!node.argument || node.argument.type !== 'JSXElement') { 131 + return 132 + } 133 + const openingEl = node.argument.openingElement 134 + if (openingEl.name.type !== 'JSXIdentifier') { 135 + return 136 + } 137 + const returnedComponentName = openingEl.name.name 138 + if (!isTextComponent(returnedComponentName)) { 139 + context.report({ 140 + node, 141 + message: 142 + 'Components ending with *Text must return <Text> or <SomeText>.', 143 + }) 108 144 } 109 145 }, 110 146 }
+1 -1
src/components/Link.tsx
··· 250 250 BaseLinkProps & TextStyleProp & Pick<TextProps, 'selectable'> 251 251 > 252 252 253 - export function InlineLink({ 253 + export function InlineLinkText({ 254 254 children, 255 255 to, 256 256 action = 'push',
+4 -4
src/components/Prompt.tsx
··· 51 51 ) 52 52 } 53 53 54 - export function Title({children}: React.PropsWithChildren<{}>) { 54 + export function TitleText({children}: React.PropsWithChildren<{}>) { 55 55 const {titleId} = React.useContext(Context) 56 56 return ( 57 57 <Text nativeID={titleId} style={[a.text_2xl, a.font_bold, a.pb_sm]}> ··· 60 60 ) 61 61 } 62 62 63 - export function Description({children}: React.PropsWithChildren<{}>) { 63 + export function DescriptionText({children}: React.PropsWithChildren<{}>) { 64 64 const t = useTheme() 65 65 const {descriptionId} = React.useContext(Context) 66 66 return ( ··· 175 175 }>) { 176 176 return ( 177 177 <Outer control={control} testID="confirmModal"> 178 - <Title>{title}</Title> 179 - <Description>{description}</Description> 178 + <TitleText>{title}</TitleText> 179 + <DescriptionText>{description}</DescriptionText> 180 180 <Actions> 181 181 <Action 182 182 cta={confirmButtonCta}
+5 -5
src/components/RichText.tsx
··· 7 7 import {isNative} from '#/platform/detection' 8 8 import {atoms as a, flatten, native, TextStyleProp, useTheme, web} from '#/alf' 9 9 import {useInteractionState} from '#/components/hooks/useInteractionState' 10 - import {InlineLink} from '#/components/Link' 10 + import {InlineLinkText} from '#/components/Link' 11 11 import {TagMenu, useTagMenuControl} from '#/components/TagMenu' 12 12 import {Text, TextProps} from '#/components/Typography' 13 13 ··· 84 84 !disableLinks 85 85 ) { 86 86 els.push( 87 - <InlineLink 87 + <InlineLinkText 88 88 selectable={selectable} 89 89 key={key} 90 90 to={`/profile/${mention.did}`} ··· 92 92 // @ts-ignore TODO 93 93 dataSet={WORD_WRAP}> 94 94 {segment.text} 95 - </InlineLink>, 95 + </InlineLinkText>, 96 96 ) 97 97 } else if (link && AppBskyRichtextFacet.validateLink(link).success) { 98 98 if (disableLinks) { 99 99 els.push(toShortUrl(segment.text)) 100 100 } else { 101 101 els.push( 102 - <InlineLink 102 + <InlineLinkText 103 103 selectable={selectable} 104 104 key={key} 105 105 to={link.uri} ··· 108 108 dataSet={WORD_WRAP} 109 109 shareOnLongPress> 110 110 {toShortUrl(segment.text)} 111 - </InlineLink>, 111 + </InlineLinkText>, 112 112 ) 113 113 } 114 114 } else if (
+17 -18
src/components/dialogs/MutedWords.tsx
··· 1 1 import React from 'react' 2 2 import {Keyboard, View} from 'react-native' 3 + import {AppBskyActorDefs, sanitizeMutedWordValue} from '@atproto/api' 3 4 import {msg, Trans} from '@lingui/macro' 4 5 import {useLingui} from '@lingui/react' 5 - import {AppBskyActorDefs, sanitizeMutedWordValue} from '@atproto/api' 6 6 7 + import {logger} from '#/logger' 8 + import {isNative} from '#/platform/detection' 7 9 import { 8 10 usePreferencesQuery, 9 - useUpsertMutedWordsMutation, 10 11 useRemoveMutedWordMutation, 12 + useUpsertMutedWordsMutation, 11 13 } from '#/state/queries/preferences' 12 - import {isNative} from '#/platform/detection' 13 14 import { 14 15 atoms as a, 15 - useTheme, 16 + native, 16 17 useBreakpoints, 18 + useTheme, 17 19 ViewStyleProp, 18 20 web, 19 - native, 20 21 } from '#/alf' 21 - import {Text} from '#/components/Typography' 22 22 import {Button, ButtonIcon, ButtonText} from '#/components/Button' 23 + import * as Dialog from '#/components/Dialog' 24 + import {useGlobalDialogsControlContext} from '#/components/dialogs/Context' 25 + import {Divider} from '#/components/Divider' 26 + import * as Toggle from '#/components/forms/Toggle' 27 + import {Hashtag_Stroke2_Corner0_Rounded as Hashtag} from '#/components/icons/Hashtag' 28 + import {PageText_Stroke2_Corner0_Rounded as PageText} from '#/components/icons/PageText' 23 29 import {PlusLarge_Stroke2_Corner0_Rounded as Plus} from '#/components/icons/Plus' 24 30 import {TimesLarge_Stroke2_Corner0_Rounded as X} from '#/components/icons/Times' 25 - import {Hashtag_Stroke2_Corner0_Rounded as Hashtag} from '#/components/icons/Hashtag' 26 - import {PageText_Stroke2_Corner0_Rounded as PageText} from '#/components/icons/PageText' 27 - import {Divider} from '#/components/Divider' 28 31 import {Loader} from '#/components/Loader' 29 - import {logger} from '#/logger' 30 - import * as Dialog from '#/components/Dialog' 31 - import * as Toggle from '#/components/forms/Toggle' 32 32 import * as Prompt from '#/components/Prompt' 33 - 34 - import {useGlobalDialogsControlContext} from '#/components/dialogs/Context' 33 + import {Text} from '#/components/Typography' 35 34 36 35 export function MutedWordsDialog() { 37 36 const {mutedWordsDialogControl: control} = useGlobalDialogsControlContext() ··· 130 129 <TargetToggle> 131 130 <View style={[a.flex_row, a.align_center, a.gap_sm]}> 132 131 <Toggle.Radio /> 133 - <Toggle.Label> 132 + <Toggle.LabelText> 134 133 <Trans>Mute in text & tags</Trans> 135 - </Toggle.Label> 134 + </Toggle.LabelText> 136 135 </View> 137 136 <PageText size="sm" /> 138 137 </TargetToggle> ··· 145 144 <TargetToggle> 146 145 <View style={[a.flex_row, a.align_center, a.gap_sm]}> 147 146 <Toggle.Radio /> 148 - <Toggle.Label> 147 + <Toggle.LabelText> 149 148 <Trans>Mute in tags only</Trans> 150 - </Toggle.Label> 149 + </Toggle.LabelText> 151 150 </View> 152 151 <Hashtag size="sm" /> 153 152 </TargetToggle>
+1 -1
src/components/forms/DateField/index.android.tsx
··· 8 8 import {DateFieldButton} from './index.shared' 9 9 10 10 export * as utils from '#/components/forms/DateField/utils' 11 - export const Label = TextField.Label 11 + export const LabelText = TextField.LabelText 12 12 13 13 export function DateField({ 14 14 value,
+1 -1
src/components/forms/DateField/index.tsx
··· 13 13 import {DateFieldButton} from './index.shared' 14 14 15 15 export * as utils from '#/components/forms/DateField/utils' 16 - export const Label = TextField.Label 16 + export const LabelText = TextField.LabelText 17 17 18 18 /** 19 19 * Date-only input. Accepts a date in the format YYYY-MM-DD, and reports date
+1 -1
src/components/forms/DateField/index.web.tsx
··· 9 9 import {CalendarDays_Stroke2_Corner0_Rounded as CalendarDays} from '#/components/icons/CalendarDays' 10 10 11 11 export * as utils from '#/components/forms/DateField/utils' 12 - export const Label = TextField.Label 12 + export const LabelText = TextField.LabelText 13 13 14 14 const InputBase = React.forwardRef<HTMLInputElement, TextInputProps>( 15 15 ({style, ...props}, ref) => {
+2 -2
src/components/forms/TextField.tsx
··· 225 225 226 226 export const Input = createInput(TextInput) 227 227 228 - export function Label({ 228 + export function LabelText({ 229 229 nativeID, 230 230 children, 231 231 }: React.PropsWithChildren<{nativeID?: string}>) { ··· 288 288 ) 289 289 } 290 290 291 - export function Suffix({ 291 + export function SuffixText({ 292 292 children, 293 293 label, 294 294 accessibilityHint,
+5 -5
src/components/forms/Toggle.tsx
··· 3 3 4 4 import {HITSLOP_10} from 'lib/constants' 5 5 import { 6 - useTheme, 7 6 atoms as a, 8 - native, 9 7 flatten, 10 - ViewStyleProp, 8 + native, 11 9 TextStyleProp, 10 + useTheme, 11 + ViewStyleProp, 12 12 } from '#/alf' 13 - import {Text} from '#/components/Typography' 14 13 import {useInteractionState} from '#/components/hooks/useInteractionState' 15 14 import {CheckThick_Stroke2_Corner0_Rounded as Checkmark} from '#/components/icons/Check' 15 + import {Text} from '#/components/Typography' 16 16 17 17 export type ItemState = { 18 18 name: string ··· 234 234 ) 235 235 } 236 236 237 - export function Label({ 237 + export function LabelText({ 238 238 children, 239 239 style, 240 240 }: React.PropsWithChildren<TextStyleProp>) {
+3 -3
src/components/moderation/LabelPreference.tsx
··· 13 13 } from '#/state/queries/preferences' 14 14 import {atoms as a, useBreakpoints, useTheme} from '#/alf' 15 15 import * as ToggleButton from '#/components/forms/ToggleButton' 16 - import {InlineLink} from '#/components/Link' 16 + import {InlineLinkText} from '#/components/Link' 17 17 import {Text} from '#/components/Typography' 18 18 import {CircleInfo_Stroke2_Corner0_Rounded as CircleInfo} from '../icons/CircleInfo' 19 19 ··· 243 243 ) : isGlobalLabel ? ( 244 244 <Trans> 245 245 Configured in{' '} 246 - <InlineLink to="/moderation" style={a.text_sm}> 246 + <InlineLinkText to="/moderation" style={a.text_sm}> 247 247 moderation settings 248 - </InlineLink> 248 + </InlineLinkText> 249 249 . 250 250 </Trans> 251 251 ) : null}
+9 -10
src/components/moderation/LabelsOnMeDialog.tsx
··· 1 1 import React from 'react' 2 2 import {View} from 'react-native' 3 + import {ComAtprotoLabelDefs, ComAtprotoModerationDefs} from '@atproto/api' 3 4 import {msg, Trans} from '@lingui/macro' 4 5 import {useLingui} from '@lingui/react' 5 - import {ComAtprotoLabelDefs, ComAtprotoModerationDefs} from '@atproto/api' 6 6 7 7 import {useLabelInfo} from '#/lib/moderation/useLabelInfo' 8 8 import {makeProfileLink} from '#/lib/routes/links' 9 9 import {sanitizeHandle} from '#/lib/strings/handles' 10 10 import {getAgent} from '#/state/session' 11 - 11 + import * as Toast from '#/view/com/util/Toast' 12 12 import {atoms as a, useBreakpoints, useTheme} from '#/alf' 13 + import {Button, ButtonText} from '#/components/Button' 14 + import * as Dialog from '#/components/Dialog' 15 + import {InlineLinkText} from '#/components/Link' 13 16 import {Text} from '#/components/Typography' 14 - import * as Dialog from '#/components/Dialog' 15 - import {Button, ButtonText} from '#/components/Button' 16 - import {InlineLink} from '#/components/Link' 17 - import * as Toast from '#/view/com/util/Toast' 18 17 import {Divider} from '../Divider' 19 18 20 19 export {useDialogControl as useLabelsOnMeDialogControl} from '#/components/Dialog' ··· 145 144 <View style={[a.px_md, a.py_sm, t.atoms.bg_contrast_25]}> 146 145 <Text style={[t.atoms.text_contrast_medium]}> 147 146 <Trans>Source:</Trans>{' '} 148 - <InlineLink 147 + <InlineLinkText 149 148 to={makeProfileLink( 150 149 labeler ? labeler.creator : {did: label.src, handle: ''}, 151 150 )} 152 151 onPress={() => control.close()}> 153 152 {labeler ? sanitizeHandle(labeler.creator.handle, '@') : label.src} 154 - </InlineLink> 153 + </InlineLinkText> 155 154 </Text> 156 155 </View> 157 156 </View> ··· 204 203 <Text style={[a.text_md, a.leading_snug]}> 205 204 <Trans> 206 205 This appeal will be sent to{' '} 207 - <InlineLink 206 + <InlineLinkText 208 207 to={makeProfileLink( 209 208 labeler ? labeler.creator : {did: label.src, handle: ''}, 210 209 )} 211 210 onPress={() => control.close()} 212 211 style={[a.text_md, a.leading_snug]}> 213 212 {labeler ? sanitizeHandle(labeler.creator.handle, '@') : label.src} 214 - </InlineLink> 213 + </InlineLinkText> 215 214 . 216 215 </Trans> 217 216 </Text>
+11 -12
src/components/moderation/ModerationDetailsDialog.tsx
··· 1 1 import React from 'react' 2 2 import {View} from 'react-native' 3 + import {ModerationCause} from '@atproto/api' 3 4 import {msg, Trans} from '@lingui/macro' 4 5 import {useLingui} from '@lingui/react' 5 - import {ModerationCause} from '@atproto/api' 6 6 7 - import {listUriToHref} from '#/lib/strings/url-helpers' 8 7 import {useModerationCauseDescription} from '#/lib/moderation/useModerationCauseDescription' 9 8 import {makeProfileLink} from '#/lib/routes/links' 10 - 9 + import {listUriToHref} from '#/lib/strings/url-helpers' 11 10 import {isNative} from '#/platform/detection' 12 - import {useTheme, atoms as a} from '#/alf' 13 - import {Text} from '#/components/Typography' 11 + import {atoms as a, useTheme} from '#/alf' 14 12 import * as Dialog from '#/components/Dialog' 15 - import {InlineLink} from '#/components/Link' 16 13 import {Divider} from '#/components/Divider' 14 + import {InlineLinkText} from '#/components/Link' 15 + import {Text} from '#/components/Typography' 17 16 18 17 export {useDialogControl as useModerationDetailsDialogControl} from '#/components/Dialog' 19 18 ··· 55 54 description = ( 56 55 <Trans> 57 56 This user is included in the{' '} 58 - <InlineLink to={listUriToHref(list.uri)} style={[a.text_sm]}> 57 + <InlineLinkText to={listUriToHref(list.uri)} style={[a.text_sm]}> 59 58 {list.name} 60 - </InlineLink>{' '} 59 + </InlineLinkText>{' '} 61 60 list which you have blocked. 62 61 </Trans> 63 62 ) ··· 84 83 description = ( 85 84 <Trans> 86 85 This user is included in the{' '} 87 - <InlineLink to={listUriToHref(list.uri)} style={[a.text_sm]}> 86 + <InlineLinkText to={listUriToHref(list.uri)} style={[a.text_sm]}> 88 87 {list.name} 89 - </InlineLink>{' '} 88 + </InlineLinkText>{' '} 90 89 list which you have muted. 91 90 </Trans> 92 91 ) ··· 127 126 {modcause.source.type === 'user' ? ( 128 127 <Trans>the author</Trans> 129 128 ) : ( 130 - <InlineLink 129 + <InlineLinkText 131 130 to={makeProfileLink({did: modcause.label.src, handle: ''})} 132 131 onPress={() => control.close()} 133 132 style={a.text_md}> 134 133 {desc.source} 135 - </InlineLink> 134 + </InlineLinkText> 136 135 )} 137 136 . 138 137 </Trans>
+3 -3
src/screens/Login/ChooseAccountForm.tsx
··· 58 58 return ( 59 59 <FormContainer 60 60 testID="chooseAccountForm" 61 - title={<Trans>Select account</Trans>}> 61 + titleText={<Trans>Select account</Trans>}> 62 62 <View> 63 - <TextField.Label> 63 + <TextField.LabelText> 64 64 <Trans>Sign in as...</Trans> 65 - </TextField.Label> 65 + </TextField.LabelText> 66 66 <AccountList 67 67 onSelectAccount={onSelect} 68 68 onSelectOther={() => onSelectAccount()}
+5 -5
src/screens/Login/ForgotPasswordForm.tsx
··· 83 83 return ( 84 84 <FormContainer 85 85 testID="forgotPasswordForm" 86 - title={<Trans>Reset password</Trans>}> 86 + titleText={<Trans>Reset password</Trans>}> 87 87 <View> 88 - <TextField.Label> 88 + <TextField.LabelText> 89 89 <Trans>Hosting provider</Trans> 90 - </TextField.Label> 90 + </TextField.LabelText> 91 91 <HostingProvider 92 92 serviceUrl={serviceUrl} 93 93 onSelectServiceUrl={setServiceUrl} ··· 95 95 /> 96 96 </View> 97 97 <View> 98 - <TextField.Label> 98 + <TextField.LabelText> 99 99 <Trans>Email address</Trans> 100 - </TextField.Label> 100 + </TextField.LabelText> 101 101 <TextField.Root> 102 102 <TextField.Icon icon={At} /> 103 103 <TextField.Input
+4 -4
src/screens/Login/FormContainer.tsx
··· 6 6 7 7 export function FormContainer({ 8 8 testID, 9 - title, 9 + titleText, 10 10 children, 11 11 style, 12 12 }: { 13 13 testID?: string 14 - title?: React.ReactNode 14 + titleText?: React.ReactNode 15 15 children: React.ReactNode 16 16 style?: StyleProp<ViewStyle> 17 17 }) { ··· 21 21 <View 22 22 testID={testID} 23 23 style={[a.gap_md, a.flex_1, !gtMobile && [a.px_lg, a.py_md], style]}> 24 - {title && !gtMobile && ( 24 + {titleText && !gtMobile && ( 25 25 <Text style={[a.text_xl, a.font_bold, t.atoms.text_contrast_high]}> 26 - {title} 26 + {titleText} 27 27 </Text> 28 28 )} 29 29 {children}
+5 -5
src/screens/Login/LoginForm.tsx
··· 128 128 129 129 const isReady = !!serviceDescription && !!identifier && !!password 130 130 return ( 131 - <FormContainer testID="loginForm" title={<Trans>Sign in</Trans>}> 131 + <FormContainer testID="loginForm" titleText={<Trans>Sign in</Trans>}> 132 132 <View> 133 - <TextField.Label> 133 + <TextField.LabelText> 134 134 <Trans>Hosting provider</Trans> 135 - </TextField.Label> 135 + </TextField.LabelText> 136 136 <HostingProvider 137 137 serviceUrl={serviceUrl} 138 138 onSelectServiceUrl={setServiceUrl} ··· 140 140 /> 141 141 </View> 142 142 <View> 143 - <TextField.Label> 143 + <TextField.LabelText> 144 144 <Trans>Account</Trans> 145 - </TextField.Label> 145 + </TextField.LabelText> 146 146 <View style={[a.gap_sm]}> 147 147 <TextField.Root> 148 148 <TextField.Icon icon={At} />
+3 -3
src/screens/Login/SetNewPasswordForm.tsx
··· 99 99 return ( 100 100 <FormContainer 101 101 testID="setNewPasswordForm" 102 - title={<Trans>Set new password</Trans>}> 102 + titleText={<Trans>Set new password</Trans>}> 103 103 <Text style={[a.leading_snug, a.mb_sm]}> 104 104 <Trans> 105 105 You will receive an email with a "reset code." Enter that code here, ··· 108 108 </Text> 109 109 110 110 <View> 111 - <TextField.Label>Reset code</TextField.Label> 111 + <TextField.LabelText>Reset code</TextField.LabelText> 112 112 <TextField.Root> 113 113 <TextField.Icon icon={Ticket} /> 114 114 <TextField.Input ··· 131 131 </View> 132 132 133 133 <View> 134 - <TextField.Label>New password</TextField.Label> 134 + <TextField.LabelText>New password</TextField.LabelText> 135 135 <TextField.Root> 136 136 <TextField.Icon icon={Lock} /> 137 137 <TextField.Input
+5 -5
src/screens/Moderation/index.tsx
··· 40 40 import {Group3_Stroke2_Corner0_Rounded as Group} from '#/components/icons/Group' 41 41 import {Person_Stroke2_Corner0_Rounded as Person} from '#/components/icons/Person' 42 42 import * as LabelingService from '#/components/LabelingServiceCard' 43 - import {InlineLink, Link} from '#/components/Link' 43 + import {InlineLinkText, Link} from '#/components/Link' 44 44 import {Loader} from '#/components/Loader' 45 45 import {GlobalLabelPreference} from '#/components/moderation/LabelPreference' 46 46 import {Text} from '#/components/Typography' ··· 518 518 msg`Discourage apps from showing my account to logged-out users`, 519 519 )}> 520 520 <Toggle.Switch /> 521 - <Toggle.Label style={[a.text_md, a.flex_1]}> 521 + <Toggle.LabelText style={[a.text_md, a.flex_1]}> 522 522 <Trans> 523 523 Discourage apps from showing my account to logged-out users 524 524 </Trans> 525 - </Toggle.Label> 525 + </Toggle.LabelText> 526 526 </Toggle.Item> 527 527 528 528 {updateProfile.isPending && <Loader />} ··· 545 545 </Trans> 546 546 </Text> 547 547 548 - <InlineLink to="https://blueskyweb.zendesk.com/hc/en-us/articles/15835264007693-Data-Privacy"> 548 + <InlineLinkText to="https://blueskyweb.zendesk.com/hc/en-us/articles/15835264007693-Data-Privacy"> 549 549 <Trans>Learn more about what is public on Bluesky.</Trans> 550 - </InlineLink> 550 + </InlineLinkText> 551 551 </View> 552 552 </View> 553 553 )
+12 -14
src/screens/Onboarding/Layout.tsx
··· 1 1 import React from 'react' 2 2 import {View} from 'react-native' 3 3 import {useSafeAreaInsets} from 'react-native-safe-area-context' 4 - import {useLingui} from '@lingui/react' 5 4 import {msg} from '@lingui/macro' 5 + import {useLingui} from '@lingui/react' 6 6 7 - import {IS_DEV} from '#/env' 8 7 import {isWeb} from '#/platform/detection' 9 8 import {useOnboardingDispatch} from '#/state/shell' 10 - 9 + import {ScrollView} from '#/view/com/util/Views' 10 + import {Context} from '#/screens/Onboarding/state' 11 11 import { 12 - useTheme, 13 12 atoms as a, 14 - useBreakpoints, 15 - web, 13 + flatten, 16 14 native, 17 - flatten, 18 15 TextStyleProp, 16 + useBreakpoints, 17 + useTheme, 18 + web, 19 19 } from '#/alf' 20 - import {P, leading, Text} from '#/components/Typography' 20 + import {Button, ButtonIcon} from '#/components/Button' 21 21 import {ChevronLeft_Stroke2_Corner0_Rounded as ChevronLeft} from '#/components/icons/Chevron' 22 - import {Button, ButtonIcon} from '#/components/Button' 23 - import {ScrollView} from '#/view/com/util/Views' 24 22 import {createPortalGroup} from '#/components/Portal' 25 - 26 - import {Context} from '#/screens/Onboarding/state' 23 + import {leading, P, Text} from '#/components/Typography' 24 + import {IS_DEV} from '#/env' 27 25 28 26 const COL_WIDTH = 500 29 27 ··· 204 202 ) 205 203 } 206 204 207 - export function Title({ 205 + export function TitleText({ 208 206 children, 209 207 style, 210 208 }: React.PropsWithChildren<TextStyleProp>) { ··· 224 222 ) 225 223 } 226 224 227 - export function Description({ 225 + export function DescriptionText({ 228 226 children, 229 227 style, 230 228 }: React.PropsWithChildren<TextStyleProp>) {
+6 -6
src/screens/Onboarding/StepAlgoFeeds/index.tsx
··· 6 6 import {useAnalytics} from '#/lib/analytics/analytics' 7 7 import {logEvent} from '#/lib/statsig/statsig' 8 8 import { 9 - Description, 9 + DescriptionText, 10 10 OnboardingControls, 11 - Title, 11 + TitleText, 12 12 } from '#/screens/Onboarding/Layout' 13 13 import {Context} from '#/screens/Onboarding/state' 14 14 import {FeedCard} from '#/screens/Onboarding/StepAlgoFeeds/FeedCard' ··· 105 105 <View style={[a.align_start]}> 106 106 <IconCircle icon={ListSparkle} style={[a.mb_2xl]} /> 107 107 108 - <Title> 108 + <TitleText> 109 109 <Trans>Choose your main feeds</Trans> 110 - </Title> 111 - <Description> 110 + </TitleText> 111 + <DescriptionText> 112 112 <Trans> 113 113 Custom feeds built by the community bring you new experiences and help 114 114 you find the content you love. 115 115 </Trans> 116 - </Description> 116 + </DescriptionText> 117 117 118 118 <View style={[a.w_full, a.pb_2xl]}> 119 119 <Toggle.Group
+6 -6
src/screens/Onboarding/StepFinished.tsx
··· 10 10 import {getAgent} from '#/state/session' 11 11 import {useOnboardingDispatch} from '#/state/shell' 12 12 import { 13 - Description, 13 + DescriptionText, 14 14 OnboardingControls, 15 - Title, 15 + TitleText, 16 16 } from '#/screens/Onboarding/Layout' 17 17 import {Context} from '#/screens/Onboarding/state' 18 18 import { ··· 87 87 <View style={[a.align_start]}> 88 88 <IconCircle icon={Check} style={[a.mb_2xl]} /> 89 89 90 - <Title> 90 + <TitleText> 91 91 <Trans>You're ready to go!</Trans> 92 - </Title> 93 - <Description> 92 + </TitleText> 93 + <DescriptionText> 94 94 <Trans>We hope you have a wonderful time. Remember, Bluesky is:</Trans> 95 - </Description> 95 + </DescriptionText> 96 96 97 97 <View style={[a.pt_5xl, a.gap_3xl]}> 98 98 <View style={[a.flex_row, a.align_center, a.w_full, a.gap_lg]}>
+8 -8
src/screens/Onboarding/StepFollowingFeed.tsx
··· 10 10 useSetFeedViewPreferencesMutation, 11 11 } from 'state/queries/preferences' 12 12 import { 13 - Description, 13 + DescriptionText, 14 14 OnboardingControls, 15 - Title, 15 + TitleText, 16 16 } from '#/screens/Onboarding/Layout' 17 17 import {Context} from '#/screens/Onboarding/state' 18 18 import {atoms as a} from '#/alf' ··· 58 58 <View style={[a.align_start]}> 59 59 <IconCircle icon={FilterTimeline} style={[a.mb_2xl]} /> 60 60 61 - <Title> 61 + <TitleText> 62 62 <Trans>Your default feed is "Following"</Trans> 63 - </Title> 64 - <Description style={[a.mb_md]}> 63 + </TitleText> 64 + <DescriptionText style={[a.mb_md]}> 65 65 <Trans>It shows posts from the people you follow as they happen.</Trans> 66 - </Description> 66 + </DescriptionText> 67 67 68 68 <View style={[a.w_full]}> 69 69 <Toggle.Item ··· 139 139 </Toggle.Item> 140 140 </View> 141 141 142 - <Description style={[a.mt_lg]}> 142 + <DescriptionText style={[a.mt_lg]}> 143 143 <Trans>You can change these settings later.</Trans> 144 - </Description> 144 + </DescriptionText> 145 145 146 146 <OnboardingControls.Portal> 147 147 <Button
+4 -4
src/screens/Onboarding/StepInterests/index.tsx
··· 11 11 import {getAgent} from '#/state/session' 12 12 import {useOnboardingDispatch} from '#/state/shell' 13 13 import { 14 - Description, 14 + DescriptionText, 15 15 OnboardingControls, 16 - Title, 16 + TitleText, 17 17 } from '#/screens/Onboarding/Layout' 18 18 import {ApiResponseMap, Context} from '#/screens/Onboarding/state' 19 19 import {InterestButton} from '#/screens/Onboarding/StepInterests/InterestButton' ··· 163 163 ]} 164 164 /> 165 165 166 - <Title>{title}</Title> 167 - <Description>{description}</Description> 166 + <TitleText>{title}</TitleText> 167 + <DescriptionText>{description}</DescriptionText> 168 168 169 169 <View style={[a.w_full, a.pt_2xl]}> 170 170 {isLoading ? (
+4 -4
src/screens/Onboarding/StepModeration/AdultContentEnabledPref.tsx
··· 113 113 )} 114 114 115 115 <Prompt.Outer control={prompt}> 116 - <Prompt.Title> 116 + <Prompt.TitleText> 117 117 <Trans>Adult Content</Trans> 118 - </Prompt.Title> 119 - <Prompt.Description> 118 + </Prompt.TitleText> 119 + <Prompt.DescriptionText> 120 120 <Trans> 121 121 Due to Apple policies, adult content can only be enabled on the web 122 122 after completing sign up. 123 123 </Trans> 124 - </Prompt.Description> 124 + </Prompt.DescriptionText> 125 125 <Prompt.Actions> 126 126 <Prompt.Action onPress={() => prompt.close()} cta={_(msg`OK`)} /> 127 127 </Prompt.Actions>
+6 -6
src/screens/Onboarding/StepModeration/index.tsx
··· 9 9 import {usePreferencesQuery} from '#/state/queries/preferences' 10 10 import {usePreferencesSetAdultContentMutation} from 'state/queries/preferences' 11 11 import { 12 - Description, 12 + DescriptionText, 13 13 OnboardingControls, 14 - Title, 14 + TitleText, 15 15 } from '#/screens/Onboarding/Layout' 16 16 import {Context} from '#/screens/Onboarding/state' 17 17 import {AdultContentEnabledPref} from '#/screens/Onboarding/StepModeration/AdultContentEnabledPref' ··· 56 56 <View style={[a.align_start]}> 57 57 <IconCircle icon={EyeSlash} style={[a.mb_2xl]} /> 58 58 59 - <Title> 59 + <TitleText> 60 60 <Trans>You're in control</Trans> 61 - </Title> 62 - <Description style={[a.mb_xl]}> 61 + </TitleText> 62 + <DescriptionText style={[a.mb_xl]}> 63 63 <Trans> 64 64 Select what you want to see (or not see), and we’ll handle the rest. 65 65 </Trans> 66 - </Description> 66 + </DescriptionText> 67 67 68 68 {!preferences ? ( 69 69 <View style={[a.pt_md]}>
+6 -6
src/screens/Onboarding/StepSuggestedAccounts/index.tsx
··· 10 10 import {useModerationOpts} from '#/state/queries/preferences' 11 11 import {useProfilesQuery} from '#/state/queries/profile' 12 12 import { 13 - Description, 13 + DescriptionText, 14 14 OnboardingControls, 15 - Title, 15 + TitleText, 16 16 } from '#/screens/Onboarding/Layout' 17 17 import {Context} from '#/screens/Onboarding/state' 18 18 import { ··· 136 136 <View style={[a.align_start]}> 137 137 <IconCircle icon={At} style={[a.mb_2xl]} /> 138 138 139 - <Title> 139 + <TitleText> 140 140 <Trans>Here are some accounts for you to follow</Trans> 141 - </Title> 142 - <Description> 141 + </TitleText> 142 + <DescriptionText> 143 143 {state.interestsStepResults.selectedInterests.length ? ( 144 144 <Trans>Based on your interest in {interestsText}</Trans> 145 145 ) : ( 146 146 <Trans>These are popular accounts you might like:</Trans> 147 147 )} 148 - </Description> 148 + </DescriptionText> 149 149 150 150 <View style={[a.w_full, a.pt_xl]}> 151 151 {isLoading ? (
+6 -6
src/screens/Onboarding/StepTopicalFeeds.tsx
··· 9 9 import {IS_TEST_USER} from 'lib/constants' 10 10 import {useSession} from 'state/session' 11 11 import { 12 - Description, 12 + DescriptionText, 13 13 OnboardingControls, 14 - Title, 14 + TitleText, 15 15 } from '#/screens/Onboarding/Layout' 16 16 import {Context} from '#/screens/Onboarding/state' 17 17 import {FeedCard} from '#/screens/Onboarding/StepAlgoFeeds/FeedCard' ··· 76 76 <View style={[a.align_start]}> 77 77 <IconCircle icon={ListMagnifyingGlass} style={[a.mb_2xl]} /> 78 78 79 - <Title> 79 + <TitleText> 80 80 <Trans>Feeds can be topical as well!</Trans> 81 - </Title> 82 - <Description> 81 + </TitleText> 82 + <DescriptionText> 83 83 {state.interestsStepResults.selectedInterests.length ? ( 84 84 <Trans> 85 85 Here are some topical feeds based on your interests: {interestsText} ··· 91 91 many as you like. 92 92 </Trans> 93 93 )} 94 - </Description> 94 + </DescriptionText> 95 95 96 96 <View style={[a.w_full, a.pb_2xl, a.pt_2xl]}> 97 97 <Toggle.Group
+7 -8
src/screens/Profile/Header/Metrics.tsx
··· 1 1 import React from 'react' 2 2 import {View} from 'react-native' 3 3 import {AppBskyActorDefs} from '@atproto/api' 4 - import {Trans, msg} from '@lingui/macro' 4 + import {msg, Trans} from '@lingui/macro' 5 5 import {useLingui} from '@lingui/react' 6 6 7 - import {Shadow} from '#/state/cache/types' 8 7 import {pluralize} from '#/lib/strings/helpers' 8 + import {Shadow} from '#/state/cache/types' 9 9 import {makeProfileLink} from 'lib/routes/links' 10 10 import {formatCount} from 'view/com/util/numeric/format' 11 - 12 11 import {atoms as a, useTheme} from '#/alf' 12 + import {InlineLinkText} from '#/components/Link' 13 13 import {Text} from '#/components/Typography' 14 - import {InlineLink} from '#/components/Link' 15 14 16 15 export function ProfileHeaderMetrics({ 17 16 profile, ··· 28 27 <View 29 28 style={[a.flex_row, a.gap_sm, a.align_center, a.pb_md]} 30 29 pointerEvents="box-none"> 31 - <InlineLink 30 + <InlineLinkText 32 31 testID="profileHeaderFollowersButton" 33 32 style={[a.flex_row, t.atoms.text]} 34 33 to={makeProfileLink(profile, 'followers')} ··· 37 36 <Text style={[t.atoms.text_contrast_medium, a.text_md]}> 38 37 {pluralizedFollowers} 39 38 </Text> 40 - </InlineLink> 41 - <InlineLink 39 + </InlineLinkText> 40 + <InlineLinkText 42 41 testID="profileHeaderFollowsButton" 43 42 style={[a.flex_row, t.atoms.text]} 44 43 to={makeProfileLink(profile, 'follows')} ··· 49 48 following 50 49 </Text> 51 50 </Trans> 52 - </InlineLink> 51 + </InlineLinkText> 53 52 <Text style={[a.font_bold, t.atoms.text, a.text_md]}> 54 53 {formatCount(profile.postsCount || 0)}{' '} 55 54 <Text style={[t.atoms.text_contrast_medium, a.font_normal, a.text_md]}>
+3 -3
src/screens/Profile/Header/ProfileHeaderLabeler.tsx
··· 316 316 const {_} = useLingui() 317 317 return ( 318 318 <Prompt.Outer control={control}> 319 - <Prompt.Title>Unable to subscribe</Prompt.Title> 320 - <Prompt.Description> 319 + <Prompt.TitleText>Unable to subscribe</Prompt.TitleText> 320 + <Prompt.DescriptionText> 321 321 <Trans> 322 322 We're sorry! You can only subscribe to ten labelers, and you've 323 323 reached your limit of ten. 324 324 </Trans> 325 - </Prompt.Description> 325 + </Prompt.DescriptionText> 326 326 <Prompt.Actions> 327 327 <Prompt.Action onPress={control.close} cta={_(msg`OK`)} /> 328 328 </Prompt.Actions>
+5 -5
src/screens/Signup/StepInfo/Policies.tsx
··· 6 6 7 7 import {atoms as a, useTheme} from '#/alf' 8 8 import {CircleInfo_Stroke2_Corner0_Rounded as CircleInfo} from '#/components/icons/CircleInfo' 9 - import {InlineLink} from '#/components/Link' 9 + import {InlineLinkText} from '#/components/Link' 10 10 import {Text} from '#/components/Typography' 11 11 12 12 export const Policies = ({ ··· 45 45 const els = [] 46 46 if (tos) { 47 47 els.push( 48 - <InlineLink key="tos" to={tos}> 48 + <InlineLinkText key="tos" to={tos}> 49 49 {_(msg`Terms of Service`)} 50 - </InlineLink>, 50 + </InlineLinkText>, 51 51 ) 52 52 } 53 53 if (pp) { 54 54 els.push( 55 - <InlineLink key="pp" to={pp}> 55 + <InlineLinkText key="pp" to={pp}> 56 56 {_(msg`Privacy Policy`)} 57 - </InlineLink>, 57 + </InlineLinkText>, 58 58 ) 59 59 } 60 60 if (els.length === 2) {
+10 -10
src/screens/Signup/StepInfo/index.tsx
··· 36 36 <View style={[a.gap_md]}> 37 37 <FormError error={state.error} /> 38 38 <View> 39 - <TextField.Label> 39 + <TextField.LabelText> 40 40 <Trans>Hosting provider</Trans> 41 - </TextField.Label> 41 + </TextField.LabelText> 42 42 <HostingProvider 43 43 serviceUrl={state.serviceUrl} 44 44 onSelectServiceUrl={v => ··· 54 54 <> 55 55 {state.serviceDescription.inviteCodeRequired && ( 56 56 <View> 57 - <TextField.Label> 57 + <TextField.LabelText> 58 58 <Trans>Invite code</Trans> 59 - </TextField.Label> 59 + </TextField.LabelText> 60 60 <TextField.Root> 61 61 <TextField.Icon icon={Ticket} /> 62 62 <TextField.Input ··· 76 76 </View> 77 77 )} 78 78 <View> 79 - <TextField.Label> 79 + <TextField.LabelText> 80 80 <Trans>Email</Trans> 81 - </TextField.Label> 81 + </TextField.LabelText> 82 82 <TextField.Root> 83 83 <TextField.Icon icon={Envelope} /> 84 84 <TextField.Input ··· 97 97 </TextField.Root> 98 98 </View> 99 99 <View> 100 - <TextField.Label> 100 + <TextField.LabelText> 101 101 <Trans>Password</Trans> 102 - </TextField.Label> 102 + </TextField.LabelText> 103 103 <TextField.Root> 104 104 <TextField.Icon icon={Lock} /> 105 105 <TextField.Input ··· 117 117 </TextField.Root> 118 118 </View> 119 119 <View> 120 - <DateField.Label> 120 + <DateField.LabelText> 121 121 <Trans>Your birth date</Trans> 122 - </DateField.Label> 122 + </DateField.LabelText> 123 123 <DateField.DateField 124 124 testID="date" 125 125 value={DateField.utils.toSimpleDateString(state.dateOfBirth)}
+3 -3
src/screens/Signup/index.tsx
··· 24 24 import {atoms as a, useBreakpoints, useTheme} from '#/alf' 25 25 import {Button, ButtonText} from '#/components/Button' 26 26 import {Divider} from '#/components/Divider' 27 - import {InlineLink} from '#/components/Link' 27 + import {InlineLinkText} from '#/components/Link' 28 28 import {Text} from '#/components/Typography' 29 29 30 30 export function Signup({onPressBack}: {onPressBack: () => void}) { ··· 215 215 <View style={[a.w_full, a.py_lg]}> 216 216 <Text style={[t.atoms.text_contrast_medium]}> 217 217 <Trans>Having trouble?</Trans>{' '} 218 - <InlineLink to={FEEDBACK_FORM_URL({email: state.email})}> 218 + <InlineLinkText to={FEEDBACK_FORM_URL({email: state.email})}> 219 219 <Trans>Contact support</Trans> 220 - </InlineLink> 220 + </InlineLinkText> 221 221 </Text> 222 222 </View> 223 223 </View>
+7 -7
src/view/com/auth/SplashScreen.web.tsx
··· 14 14 import {atoms as a, useTheme} from '#/alf' 15 15 import {Button, ButtonText} from '#/components/Button' 16 16 import {ChevronBottom_Stroke2_Corner0_Rounded as ChevronDown} from '#/components/icons/Chevron' 17 - import {InlineLink} from '#/components/Link' 17 + import {InlineLinkText} from '#/components/Link' 18 18 import {Text} from '#/components/Typography' 19 19 import {CenteredView} from '../util/Views' 20 20 ··· 162 162 a.flex_1, 163 163 t.atoms.border_contrast_medium, 164 164 ]}> 165 - <InlineLink to="https://bsky.social"> 165 + <InlineLinkText to="https://bsky.social"> 166 166 <Trans>Business</Trans> 167 - </InlineLink> 168 - <InlineLink to="https://bsky.social/about/blog"> 167 + </InlineLinkText> 168 + <InlineLinkText to="https://bsky.social/about/blog"> 169 169 <Trans>Blog</Trans> 170 - </InlineLink> 171 - <InlineLink to="https://bsky.social/about/join"> 170 + </InlineLinkText> 171 + <InlineLinkText to="https://bsky.social/about/join"> 172 172 <Trans>Jobs</Trans> 173 - </InlineLink> 173 + </InlineLinkText> 174 174 175 175 <View style={a.flex_1} /> 176 176
+8 -8
src/view/com/auth/server-input/index.tsx
··· 1 1 import React from 'react' 2 2 import {View} from 'react-native' 3 + import {msg, Trans} from '@lingui/macro' 3 4 import {useLingui} from '@lingui/react' 4 - import {Trans, msg} from '@lingui/macro' 5 - import {BSKY_SERVICE} from 'lib/constants' 5 + 6 6 import * as persisted from '#/state/persisted' 7 - 7 + import {BSKY_SERVICE} from 'lib/constants' 8 8 import {atoms as a, useBreakpoints, useTheme} from '#/alf' 9 - import * as Dialog from '#/components/Dialog' 10 - import {Text, P} from '#/components/Typography' 11 9 import {Button, ButtonText} from '#/components/Button' 12 - import * as ToggleButton from '#/components/forms/ToggleButton' 10 + import * as Dialog from '#/components/Dialog' 13 11 import * as TextField from '#/components/forms/TextField' 12 + import * as ToggleButton from '#/components/forms/ToggleButton' 14 13 import {Globe_Stroke2_Corner0_Rounded as Globe} from '#/components/icons/Globe' 14 + import {P, Text} from '#/components/Typography' 15 15 16 16 export function ServerInputDialog({ 17 17 control, ··· 106 106 a.px_md, 107 107 a.py_md, 108 108 ]}> 109 - <TextField.Label nativeID="address-input-label"> 109 + <TextField.LabelText nativeID="address-input-label"> 110 110 <Trans>Server address</Trans> 111 - </TextField.Label> 111 + </TextField.LabelText> 112 112 <TextField.Root> 113 113 <TextField.Icon icon={Globe} /> 114 114 <Dialog.Input
+40 -40
src/view/screens/DebugMod.tsx
··· 1 1 import React from 'react' 2 - import {NativeStackScreenProps, CommonNavigatorParams} from 'lib/routes/types' 3 2 import {View} from 'react-native' 4 3 import { 5 - LABELS, 6 - mock, 7 - moderatePost, 8 - moderateProfile, 9 - ModerationOpts, 10 4 AppBskyActorDefs, 11 5 AppBskyFeedDefs, 12 6 AppBskyFeedPost, 7 + ComAtprotoLabelDefs, 8 + interpretLabelValueDefinition, 13 9 LabelPreference, 14 - ModerationDecision, 10 + LABELS, 11 + mock, 12 + moderatePost, 13 + moderateProfile, 15 14 ModerationBehavior, 15 + ModerationDecision, 16 + ModerationOpts, 16 17 RichText, 17 - ComAtprotoLabelDefs, 18 - interpretLabelValueDefinition, 19 18 } from '@atproto/api' 20 19 import {msg} from '@lingui/macro' 21 20 import {useLingui} from '@lingui/react' 22 - import {moderationOptsOverrideContext} from '#/state/queries/preferences' 23 - import {useSession} from '#/state/session' 21 + 22 + import {useGlobalLabelStrings} from '#/lib/moderation/useGlobalLabelStrings' 24 23 import {FeedNotification} from '#/state/queries/notifications/types' 25 24 import { 26 25 groupNotifications, 27 26 shouldFilterNotif, 28 27 } from '#/state/queries/notifications/util' 29 - 30 - import {atoms as a, useTheme} from '#/alf' 28 + import {moderationOptsOverrideContext} from '#/state/queries/preferences' 29 + import {useSession} from '#/state/session' 30 + import {CommonNavigatorParams, NativeStackScreenProps} from 'lib/routes/types' 31 31 import {CenteredView, ScrollView} from '#/view/com/util/Views' 32 - import {H1, H3, P, Text} from '#/components/Typography' 33 - import {useGlobalLabelStrings} from '#/lib/moderation/useGlobalLabelStrings' 32 + import {ProfileHeaderStandard} from '#/screens/Profile/Header/ProfileHeaderStandard' 33 + import {atoms as a, useTheme} from '#/alf' 34 + import {Button, ButtonIcon, ButtonText} from '#/components/Button' 35 + import {Divider} from '#/components/Divider' 34 36 import * as Toggle from '#/components/forms/Toggle' 35 37 import * as ToggleButton from '#/components/forms/ToggleButton' 36 - import {Button, ButtonIcon, ButtonText} from '#/components/Button' 37 38 import {Check_Stroke2_Corner0_Rounded as Check} from '#/components/icons/Check' 38 39 import { 39 40 ChevronBottom_Stroke2_Corner0_Rounded as ChevronBottom, 40 41 ChevronTop_Stroke2_Corner0_Rounded as ChevronTop, 41 42 } from '#/components/icons/Chevron' 43 + import {H1, H3, P, Text} from '#/components/Typography' 42 44 import {ScreenHider} from '../../components/moderation/ScreenHider' 43 - import {ProfileHeaderStandard} from '#/screens/Profile/Header/ProfileHeaderStandard' 44 - import {ProfileCard} from '../com/profile/ProfileCard' 45 - import {FeedItem} from '../com/posts/FeedItem' 46 45 import {FeedItem as NotifFeedItem} from '../com/notifications/FeedItem' 47 46 import {PostThreadItem} from '../com/post-thread/PostThreadItem' 48 - import {Divider} from '#/components/Divider' 47 + import {FeedItem} from '../com/posts/FeedItem' 48 + import {ProfileCard} from '../com/profile/ProfileCard' 49 49 50 50 const LABEL_VALUES: (keyof typeof LABELS)[] = Object.keys( 51 51 LABELS, ··· 320 320 disabled={disabled} 321 321 style={disabled ? {opacity: 0.5} : undefined}> 322 322 <Toggle.Radio /> 323 - <Toggle.Label>{labelValue}</Toggle.Label> 323 + <Toggle.LabelText>{labelValue}</Toggle.LabelText> 324 324 </Toggle.Item> 325 325 ) 326 326 })} ··· 330 330 disabled={isSelfLabel} 331 331 style={isSelfLabel ? {opacity: 0.5} : undefined}> 332 332 <Toggle.Radio /> 333 - <Toggle.Label>Custom label</Toggle.Label> 333 + <Toggle.LabelText>Custom label</Toggle.LabelText> 334 334 </Toggle.Item> 335 335 </View> 336 336 </Toggle.Group> ··· 358 358 <View style={[a.gap_md, a.flex_row, a.flex_wrap, a.pt_md]}> 359 359 <Toggle.Item name="targetMe" label="Target is me"> 360 360 <Toggle.Checkbox /> 361 - <Toggle.Label>Target is me</Toggle.Label> 361 + <Toggle.LabelText>Target is me</Toggle.LabelText> 362 362 </Toggle.Item> 363 363 <Toggle.Item name="following" label="Following target"> 364 364 <Toggle.Checkbox /> 365 - <Toggle.Label>Following target</Toggle.Label> 365 + <Toggle.LabelText>Following target</Toggle.LabelText> 366 366 </Toggle.Item> 367 367 <Toggle.Item name="selfLabel" label="Self label"> 368 368 <Toggle.Checkbox /> 369 - <Toggle.Label>Self label</Toggle.Label> 369 + <Toggle.LabelText>Self label</Toggle.LabelText> 370 370 </Toggle.Item> 371 371 <Toggle.Item name="noAdult" label="Adult disabled"> 372 372 <Toggle.Checkbox /> 373 - <Toggle.Label>Adult disabled</Toggle.Label> 373 + <Toggle.LabelText>Adult disabled</Toggle.LabelText> 374 374 </Toggle.Item> 375 375 <Toggle.Item name="loggedOut" label="Logged out"> 376 376 <Toggle.Checkbox /> 377 - <Toggle.Label>Logged out</Toggle.Label> 377 + <Toggle.LabelText>Logged out</Toggle.LabelText> 378 378 </Toggle.Item> 379 379 </View> 380 380 </Toggle.Group> ··· 400 400 ]}> 401 401 <Toggle.Item name="hide" label="Hide"> 402 402 <Toggle.Radio /> 403 - <Toggle.Label>Hide</Toggle.Label> 403 + <Toggle.LabelText>Hide</Toggle.LabelText> 404 404 </Toggle.Item> 405 405 <Toggle.Item name="warn" label="Warn"> 406 406 <Toggle.Radio /> 407 - <Toggle.Label>Warn</Toggle.Label> 407 + <Toggle.LabelText>Warn</Toggle.LabelText> 408 408 </Toggle.Item> 409 409 <Toggle.Item name="ignore" label="Ignore"> 410 410 <Toggle.Radio /> 411 - <Toggle.Label>Ignore</Toggle.Label> 411 + <Toggle.LabelText>Ignore</Toggle.LabelText> 412 412 </Toggle.Item> 413 413 </View> 414 414 </Toggle.Group> ··· 446 446 <View style={[a.flex_row, a.gap_md, a.flex_wrap]}> 447 447 <Toggle.Item name="account" label="Account"> 448 448 <Toggle.Radio /> 449 - <Toggle.Label>Account</Toggle.Label> 449 + <Toggle.LabelText>Account</Toggle.LabelText> 450 450 </Toggle.Item> 451 451 <Toggle.Item name="profile" label="Profile"> 452 452 <Toggle.Radio /> 453 - <Toggle.Label>Profile</Toggle.Label> 453 + <Toggle.LabelText>Profile</Toggle.LabelText> 454 454 </Toggle.Item> 455 455 <Toggle.Item name="post" label="Post"> 456 456 <Toggle.Radio /> 457 - <Toggle.Label>Post</Toggle.Label> 457 + <Toggle.LabelText>Post</Toggle.LabelText> 458 458 </Toggle.Item> 459 459 <Toggle.Item name="embed" label="Embed"> 460 460 <Toggle.Radio /> 461 - <Toggle.Label>Embed</Toggle.Label> 461 + <Toggle.LabelText>Embed</Toggle.LabelText> 462 462 </Toggle.Item> 463 463 </View> 464 464 </Toggle.Group> ··· 623 623 <View style={[a.flex_row, a.gap_md, a.flex_wrap]}> 624 624 <Toggle.Item name="content" label="Content"> 625 625 <Toggle.Radio /> 626 - <Toggle.Label>Content</Toggle.Label> 626 + <Toggle.LabelText>Content</Toggle.LabelText> 627 627 </Toggle.Item> 628 628 <Toggle.Item name="media" label="Media"> 629 629 <Toggle.Radio /> 630 - <Toggle.Label>Media</Toggle.Label> 630 + <Toggle.LabelText>Media</Toggle.LabelText> 631 631 </Toggle.Item> 632 632 <Toggle.Item name="none" label="None"> 633 633 <Toggle.Radio /> 634 - <Toggle.Label>None</Toggle.Label> 634 + <Toggle.LabelText>None</Toggle.LabelText> 635 635 </Toggle.Item> 636 636 </View> 637 637 </Toggle.Group> ··· 658 658 <View style={[a.flex_row, a.gap_md, a.flex_wrap, a.align_center]}> 659 659 <Toggle.Item name="alert" label="Alert"> 660 660 <Toggle.Radio /> 661 - <Toggle.Label>Alert</Toggle.Label> 661 + <Toggle.LabelText>Alert</Toggle.LabelText> 662 662 </Toggle.Item> 663 663 <Toggle.Item name="inform" label="Inform"> 664 664 <Toggle.Radio /> 665 - <Toggle.Label>Inform</Toggle.Label> 665 + <Toggle.LabelText>Inform</Toggle.LabelText> 666 666 </Toggle.Item> 667 667 <Toggle.Item name="none" label="None"> 668 668 <Toggle.Radio /> 669 - <Toggle.Label>None</Toggle.Label> 669 + <Toggle.LabelText>None</Toggle.LabelText> 670 670 </Toggle.Item> 671 671 </View> 672 672 </Toggle.Group>
+52 -51
src/view/screens/ProfileFeed.tsx
··· 1 - import React, {useMemo, useCallback} from 'react' 2 - import {StyleSheet, View, Pressable} from 'react-native' 1 + import React, {useCallback, useMemo} from 'react' 2 + import {Pressable, StyleSheet, View} from 'react-native' 3 + import {msg, Trans} from '@lingui/macro' 4 + import {useLingui} from '@lingui/react' 5 + import {useIsFocused, useNavigation} from '@react-navigation/native' 3 6 import {NativeStackScreenProps} from '@react-navigation/native-stack' 4 - import {useIsFocused, useNavigation} from '@react-navigation/native' 5 7 import {useQueryClient} from '@tanstack/react-query' 8 + 9 + import {HITSLOP_20} from '#/lib/constants' 10 + import {logger} from '#/logger' 11 + import {isNative} from '#/platform/detection' 12 + import {listenSoftReset} from '#/state/events' 13 + import {FeedSourceFeedInfo, useFeedSourceInfoQuery} from '#/state/queries/feed' 14 + import {useLikeMutation, useUnlikeMutation} from '#/state/queries/like' 15 + import {FeedDescriptor} from '#/state/queries/post-feed' 16 + import {RQKEY as FEED_RQKEY} from '#/state/queries/post-feed' 17 + import { 18 + usePinFeedMutation, 19 + usePreferencesQuery, 20 + UsePreferencesQueryResponse, 21 + useRemoveFeedMutation, 22 + useSaveFeedMutation, 23 + useUnpinFeedMutation, 24 + } from '#/state/queries/preferences' 25 + import {useResolveUriQuery} from '#/state/queries/resolve-uri' 26 + import {truncateAndInvalidate} from '#/state/queries/util' 27 + import {useSession} from '#/state/session' 28 + import {useComposerControls} from '#/state/shell/composer' 29 + import {useAnalytics} from 'lib/analytics/analytics' 30 + import {Haptics} from 'lib/haptics' 6 31 import {usePalette} from 'lib/hooks/usePalette' 32 + import {useSetTitle} from 'lib/hooks/useSetTitle' 33 + import {ComposeIcon2} from 'lib/icons' 34 + import {makeCustomFeedLink} from 'lib/routes/links' 7 35 import {CommonNavigatorParams} from 'lib/routes/types' 36 + import {NavigationProp} from 'lib/routes/types' 37 + import {shareUrl} from 'lib/sharing' 38 + import {pluralize} from 'lib/strings/helpers' 8 39 import {makeRecordUri} from 'lib/strings/url-helpers' 40 + import {toShareUrl} from 'lib/strings/url-helpers' 9 41 import {s} from 'lib/styles' 10 - import {FeedDescriptor} from '#/state/queries/post-feed' 11 42 import {PagerWithHeader} from 'view/com/pager/PagerWithHeader' 12 - import {ProfileSubpageHeader} from 'view/com/profile/ProfileSubpageHeader' 13 43 import {Feed} from 'view/com/posts/Feed' 14 - import {InlineLink} from '#/components/Link' 15 - import {ListRef} from 'view/com/util/List' 44 + import {ProfileSubpageHeader} from 'view/com/profile/ProfileSubpageHeader' 45 + import {EmptyState} from 'view/com/util/EmptyState' 46 + import {FAB} from 'view/com/util/fab/FAB' 16 47 import {Button} from 'view/com/util/forms/Button' 17 - import {Text} from 'view/com/util/text/Text' 18 - import {RichText} from '#/components/RichText' 48 + import {ListRef} from 'view/com/util/List' 19 49 import {LoadLatestBtn} from 'view/com/util/load-latest/LoadLatestBtn' 20 - import {FAB} from 'view/com/util/fab/FAB' 21 - import {EmptyState} from 'view/com/util/EmptyState' 22 50 import {LoadingScreen} from 'view/com/util/LoadingScreen' 51 + import {Text} from 'view/com/util/text/Text' 23 52 import * as Toast from 'view/com/util/Toast' 24 - import {useSetTitle} from 'lib/hooks/useSetTitle' 25 - import {RQKEY as FEED_RQKEY} from '#/state/queries/post-feed' 26 - import {shareUrl} from 'lib/sharing' 27 - import {toShareUrl} from 'lib/strings/url-helpers' 28 - import {Haptics} from 'lib/haptics' 29 - import {useAnalytics} from 'lib/analytics/analytics' 30 - import {makeCustomFeedLink} from 'lib/routes/links' 31 - import {pluralize} from 'lib/strings/helpers' 32 53 import {CenteredView} from 'view/com/util/Views' 33 - import {NavigationProp} from 'lib/routes/types' 34 - import {ComposeIcon2} from 'lib/icons' 35 - import {logger} from '#/logger' 36 - import {Trans, msg} from '@lingui/macro' 37 - import {useLingui} from '@lingui/react' 38 - import {ReportDialog, useReportDialogControl} from '#/components/ReportDialog' 39 - import {useFeedSourceInfoQuery, FeedSourceFeedInfo} from '#/state/queries/feed' 40 - import {useResolveUriQuery} from '#/state/queries/resolve-uri' 41 - import { 42 - UsePreferencesQueryResponse, 43 - usePreferencesQuery, 44 - useSaveFeedMutation, 45 - useRemoveFeedMutation, 46 - usePinFeedMutation, 47 - useUnpinFeedMutation, 48 - } from '#/state/queries/preferences' 49 - import {useSession} from '#/state/session' 50 - import {useLikeMutation, useUnlikeMutation} from '#/state/queries/like' 51 - import {useComposerControls} from '#/state/shell/composer' 52 - import {truncateAndInvalidate} from '#/state/queries/util' 53 - import {isNative} from '#/platform/detection' 54 - import {listenSoftReset} from '#/state/events' 55 54 import {atoms as a, useTheme} from '#/alf' 56 - import * as Menu from '#/components/Menu' 57 - import {HITSLOP_20} from '#/lib/constants' 58 - import {DotGrid_Stroke2_Corner0_Rounded as Ellipsis} from '#/components/icons/DotGrid' 59 - import {Trash_Stroke2_Corner0_Rounded as Trash} from '#/components/icons/Trash' 60 - import {PlusLarge_Stroke2_Corner0_Rounded as Plus} from '#/components/icons/Plus' 61 - import {CircleInfo_Stroke2_Corner0_Rounded as CircleInfo} from '#/components/icons/CircleInfo' 55 + import {Button as NewButton, ButtonText} from '#/components/Button' 62 56 import {ArrowOutOfBox_Stroke2_Corner0_Rounded as Share} from '#/components/icons/ArrowOutOfBox' 57 + import {CircleInfo_Stroke2_Corner0_Rounded as CircleInfo} from '#/components/icons/CircleInfo' 58 + import {DotGrid_Stroke2_Corner0_Rounded as Ellipsis} from '#/components/icons/DotGrid' 63 59 import { 64 - Heart2_Stroke2_Corner0_Rounded as HeartOutline, 65 60 Heart2_Filled_Stroke2_Corner0_Rounded as HeartFilled, 61 + Heart2_Stroke2_Corner0_Rounded as HeartOutline, 66 62 } from '#/components/icons/Heart2' 67 - import {Button as NewButton, ButtonText} from '#/components/Button' 63 + import {PlusLarge_Stroke2_Corner0_Rounded as Plus} from '#/components/icons/Plus' 64 + import {Trash_Stroke2_Corner0_Rounded as Trash} from '#/components/icons/Trash' 65 + import {InlineLinkText} from '#/components/Link' 66 + import * as Menu from '#/components/Menu' 67 + import {ReportDialog, useReportDialogControl} from '#/components/ReportDialog' 68 + import {RichText} from '#/components/RichText' 68 69 69 70 const SECTION_TITLES = ['Posts'] 70 71 ··· 580 581 )} 581 582 </NewButton> 582 583 {typeof likeCount === 'number' && ( 583 - <InlineLink 584 + <InlineLinkText 584 585 label={_(msg`View users who like this feed`)} 585 586 to={makeCustomFeedLink(feedOwnerDid, feedRkey, 'liked-by')} 586 587 style={[t.atoms.text_contrast_medium, a.font_bold]}> 587 588 {_(msg`Liked by ${likeCount} ${pluralize(likeCount, 'user')}`)} 588 - </InlineLink> 589 + </InlineLinkText> 589 590 )} 590 591 </View> 591 592 </View>
+7 -7
src/view/screens/Settings/ExportCarDialog.tsx
··· 1 1 import React from 'react' 2 2 import {View} from 'react-native' 3 + import {msg, Trans} from '@lingui/macro' 3 4 import {useLingui} from '@lingui/react' 4 - import {Trans, msg} from '@lingui/macro' 5 5 6 + import {getAgent, useSession} from '#/state/session' 6 7 import {atoms as a, useBreakpoints, useTheme} from '#/alf' 7 - import * as Dialog from '#/components/Dialog' 8 - import {Text, P} from '#/components/Typography' 9 8 import {Button, ButtonText} from '#/components/Button' 10 - import {InlineLink, Link} from '#/components/Link' 11 - import {getAgent, useSession} from '#/state/session' 9 + import * as Dialog from '#/components/Dialog' 10 + import {InlineLinkText, Link} from '#/components/Link' 11 + import {P, Text} from '#/components/Typography' 12 12 13 13 export function ExportCarDialog({ 14 14 control, ··· 75 75 <Trans> 76 76 This feature is in beta. You can read more about repository 77 77 exports in{' '} 78 - <InlineLink 78 + <InlineLinkText 79 79 to="https://docs.bsky.app/blog/repo-export" 80 80 style={[a.text_sm]}> 81 81 this blogpost 82 - </InlineLink> 82 + </InlineLinkText> 83 83 . 84 84 </Trans> 85 85 </P>
+5 -5
src/view/screens/Storybook/Dialogs.tsx
··· 1 1 import React from 'react' 2 2 import {View} from 'react-native' 3 3 4 + import {useDialogStateControlContext} from '#/state/dialogs' 4 5 import {atoms as a} from '#/alf' 5 6 import {Button} from '#/components/Button' 6 - import {H3, P} from '#/components/Typography' 7 7 import * as Dialog from '#/components/Dialog' 8 8 import * as Prompt from '#/components/Prompt' 9 - import {useDialogStateControlContext} from '#/state/dialogs' 9 + import {H3, P} from '#/components/Typography' 10 10 11 11 export function Dialogs() { 12 12 const scrollable = Dialog.useDialogControl() ··· 61 61 </Button> 62 62 63 63 <Prompt.Outer control={prompt}> 64 - <Prompt.Title>This is a prompt</Prompt.Title> 65 - <Prompt.Description> 64 + <Prompt.TitleText>This is a prompt</Prompt.TitleText> 65 + <Prompt.DescriptionText> 66 66 This is a generic prompt component. It accepts a title and a 67 67 description, as well as two actions. 68 - </Prompt.Description> 68 + </Prompt.DescriptionText> 69 69 <Prompt.Actions> 70 70 <Prompt.Cancel>Cancel</Prompt.Cancel> 71 71 <Prompt.Action onPress={() => {}}>Confirm</Prompt.Action>
+25 -23
src/view/screens/Storybook/Forms.tsx
··· 2 2 import {View} from 'react-native' 3 3 4 4 import {atoms as a} from '#/alf' 5 - import {H1, H3} from '#/components/Typography' 5 + import {Button} from '#/components/Button' 6 + import {DateField, LabelText} from '#/components/forms/DateField' 6 7 import * as TextField from '#/components/forms/TextField' 7 - import {DateField, Label} from '#/components/forms/DateField' 8 8 import * as Toggle from '#/components/forms/Toggle' 9 9 import * as ToggleButton from '#/components/forms/ToggleButton' 10 - import {Button} from '#/components/Button' 11 10 import {Globe_Stroke2_Corner0_Rounded as Globe} from '#/components/icons/Globe' 11 + import {H1, H3} from '#/components/Typography' 12 12 13 13 export function Forms() { 14 14 const [toggleGroupAValues, setToggleGroupAValues] = React.useState(['a']) ··· 42 42 </TextField.Root> 43 43 44 44 <View style={[a.w_full]}> 45 - <TextField.Label>Text field</TextField.Label> 45 + <TextField.LabelText>Text field</TextField.LabelText> 46 46 <TextField.Root> 47 47 <TextField.Icon icon={Globe} /> 48 48 <TextField.Input ··· 50 50 onChangeText={setValue} 51 51 label="Text field" 52 52 /> 53 - <TextField.Suffix label="@gmail.com">@gmail.com</TextField.Suffix> 53 + <TextField.SuffixText label="@gmail.com"> 54 + @gmail.com 55 + </TextField.SuffixText> 54 56 </TextField.Root> 55 57 </View> 56 58 57 59 <View style={[a.w_full]}> 58 - <TextField.Label>Textarea</TextField.Label> 60 + <TextField.LabelText>Textarea</TextField.LabelText> 59 61 <TextField.Input 60 62 multiline 61 63 numberOfLines={4} ··· 68 70 <H3>DateField</H3> 69 71 70 72 <View style={[a.w_full]}> 71 - <Label>Date</Label> 73 + <LabelText>Date</LabelText> 72 74 <DateField 73 75 testID="date" 74 76 value={date} ··· 86 88 87 89 <Toggle.Item name="a" label="Click me"> 88 90 <Toggle.Checkbox /> 89 - <Toggle.Label>Uncontrolled toggle</Toggle.Label> 91 + <Toggle.LabelText>Uncontrolled toggle</Toggle.LabelText> 90 92 </Toggle.Item> 91 93 92 94 <Toggle.Group ··· 98 100 <View style={[a.gap_md]}> 99 101 <Toggle.Item name="a" label="Click me"> 100 102 <Toggle.Switch /> 101 - <Toggle.Label>Click me</Toggle.Label> 103 + <Toggle.LabelText>Click me</Toggle.LabelText> 102 104 </Toggle.Item> 103 105 <Toggle.Item name="b" label="Click me"> 104 106 <Toggle.Switch /> 105 - <Toggle.Label>Click me</Toggle.Label> 107 + <Toggle.LabelText>Click me</Toggle.LabelText> 106 108 </Toggle.Item> 107 109 <Toggle.Item name="c" label="Click me"> 108 110 <Toggle.Switch /> 109 - <Toggle.Label>Click me</Toggle.Label> 111 + <Toggle.LabelText>Click me</Toggle.LabelText> 110 112 </Toggle.Item> 111 113 <Toggle.Item name="d" disabled label="Click me"> 112 114 <Toggle.Switch /> 113 - <Toggle.Label>Click me</Toggle.Label> 115 + <Toggle.LabelText>Click me</Toggle.LabelText> 114 116 </Toggle.Item> 115 117 <Toggle.Item name="e" isInvalid label="Click me"> 116 118 <Toggle.Switch /> 117 - <Toggle.Label>Click me</Toggle.Label> 119 + <Toggle.LabelText>Click me</Toggle.LabelText> 118 120 </Toggle.Item> 119 121 </View> 120 122 </Toggle.Group> ··· 128 130 <View style={[a.gap_md]}> 129 131 <Toggle.Item name="a" label="Click me"> 130 132 <Toggle.Checkbox /> 131 - <Toggle.Label>Click me</Toggle.Label> 133 + <Toggle.LabelText>Click me</Toggle.LabelText> 132 134 </Toggle.Item> 133 135 <Toggle.Item name="b" label="Click me"> 134 136 <Toggle.Checkbox /> 135 - <Toggle.Label>Click me</Toggle.Label> 137 + <Toggle.LabelText>Click me</Toggle.LabelText> 136 138 </Toggle.Item> 137 139 <Toggle.Item name="c" label="Click me"> 138 140 <Toggle.Checkbox /> 139 - <Toggle.Label>Click me</Toggle.Label> 141 + <Toggle.LabelText>Click me</Toggle.LabelText> 140 142 </Toggle.Item> 141 143 <Toggle.Item name="d" disabled label="Click me"> 142 144 <Toggle.Checkbox /> 143 - <Toggle.Label>Click me</Toggle.Label> 145 + <Toggle.LabelText>Click me</Toggle.LabelText> 144 146 </Toggle.Item> 145 147 <Toggle.Item name="e" isInvalid label="Click me"> 146 148 <Toggle.Checkbox /> 147 - <Toggle.Label>Click me</Toggle.Label> 149 + <Toggle.LabelText>Click me</Toggle.LabelText> 148 150 </Toggle.Item> 149 151 </View> 150 152 </Toggle.Group> ··· 157 159 <View style={[a.gap_md]}> 158 160 <Toggle.Item name="a" label="Click me"> 159 161 <Toggle.Radio /> 160 - <Toggle.Label>Click me</Toggle.Label> 162 + <Toggle.LabelText>Click me</Toggle.LabelText> 161 163 </Toggle.Item> 162 164 <Toggle.Item name="b" label="Click me"> 163 165 <Toggle.Radio /> 164 - <Toggle.Label>Click me</Toggle.Label> 166 + <Toggle.LabelText>Click me</Toggle.LabelText> 165 167 </Toggle.Item> 166 168 <Toggle.Item name="c" label="Click me"> 167 169 <Toggle.Radio /> 168 - <Toggle.Label>Click me</Toggle.Label> 170 + <Toggle.LabelText>Click me</Toggle.LabelText> 169 171 </Toggle.Item> 170 172 <Toggle.Item name="d" disabled label="Click me"> 171 173 <Toggle.Radio /> 172 - <Toggle.Label>Click me</Toggle.Label> 174 + <Toggle.LabelText>Click me</Toggle.LabelText> 173 175 </Toggle.Item> 174 176 <Toggle.Item name="e" isInvalid label="Click me"> 175 177 <Toggle.Radio /> 176 - <Toggle.Label>Click me</Toggle.Label> 178 + <Toggle.LabelText>Click me</Toggle.LabelText> 177 179 </Toggle.Item> 178 180 </View> 179 181 </Toggle.Group>
+12 -10
src/view/screens/Storybook/Links.tsx
··· 1 1 import React from 'react' 2 2 import {View} from 'react-native' 3 3 4 - import {useTheme, atoms as a} from '#/alf' 4 + import {atoms as a, useTheme} from '#/alf' 5 5 import {ButtonText} from '#/components/Button' 6 - import {InlineLink, Link} from '#/components/Link' 6 + import {InlineLinkText, Link} from '#/components/Link' 7 7 import {H1, Text} from '#/components/Typography' 8 8 9 9 export function Links() { ··· 13 13 <H1>Links</H1> 14 14 15 15 <View style={[a.gap_md, a.align_start]}> 16 - <InlineLink to="https://google.com" style={[a.text_lg]}> 16 + <InlineLinkText to="https://google.com" style={[a.text_lg]}> 17 17 https://google.com 18 - </InlineLink> 19 - <InlineLink to="https://google.com" style={[a.text_lg]}> 18 + </InlineLinkText> 19 + <InlineLinkText to="https://google.com" style={[a.text_lg]}> 20 20 External with custom children (google.com) 21 - </InlineLink> 22 - <InlineLink 21 + </InlineLinkText> 22 + <InlineLinkText 23 23 to="https://bsky.social" 24 24 style={[a.text_md, t.atoms.text_contrast_low]}> 25 25 Internal (bsky.social) 26 - </InlineLink> 27 - <InlineLink to="https://bsky.app/profile/bsky.app" style={[a.text_md]}> 26 + </InlineLinkText> 27 + <InlineLinkText 28 + to="https://bsky.app/profile/bsky.app" 29 + style={[a.text_md]}> 28 30 Internal (bsky.app) 29 - </InlineLink> 31 + </InlineLinkText> 30 32 31 33 <Link 32 34 variant="solid"