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 returnszio/stable
parent
c190fd58ec
commit
3915bb4316
15
.eslintrc.js
15
.eslintrc.js
|
@ -1,5 +1,3 @@
|
|||
const bskyEslint = require('./eslint')
|
||||
|
||||
module.exports = {
|
||||
root: true,
|
||||
extends: [
|
||||
|
@ -27,29 +25,18 @@ module.exports = {
|
|||
{
|
||||
impliedTextComponents: [
|
||||
'Button', // TODO: Not always safe.
|
||||
'ButtonText',
|
||||
'DateField.Label',
|
||||
'Description',
|
||||
'H1',
|
||||
'H2',
|
||||
'H3',
|
||||
'H4',
|
||||
'H5',
|
||||
'H6',
|
||||
'InlineLink',
|
||||
'Label',
|
||||
'P',
|
||||
'Prompt.Title',
|
||||
'Prompt.Description',
|
||||
'Prompt.Cancel', // TODO: Not always safe.
|
||||
'Prompt.Action', // TODO: Not always safe.
|
||||
'TextField.Label',
|
||||
'TextField.Suffix',
|
||||
'Title',
|
||||
'Toggle.Label',
|
||||
'ToggleButton.Button', // TODO: Not always safe.
|
||||
],
|
||||
impliedTextProps: ['FormContainer title'],
|
||||
impliedTextProps: [],
|
||||
},
|
||||
],
|
||||
'simple-import-sort/imports': [
|
||||
|
|
|
@ -246,6 +246,41 @@ describe('avoid-unwrapped-text', () => {
|
|||
</Foo>
|
||||
`,
|
||||
},
|
||||
|
||||
{
|
||||
code: `
|
||||
function Stuff() {
|
||||
return <Text>foo</Text>
|
||||
}
|
||||
`,
|
||||
},
|
||||
|
||||
{
|
||||
code: `
|
||||
function Stuff({ foo }) {
|
||||
return <View>{foo}</View>
|
||||
}
|
||||
`,
|
||||
},
|
||||
|
||||
{
|
||||
code: `
|
||||
function MyText() {
|
||||
return <Text>foo</Text>
|
||||
}
|
||||
`,
|
||||
},
|
||||
|
||||
{
|
||||
code: `
|
||||
function MyText({ foo }) {
|
||||
if (foo) {
|
||||
return <Text>foo</Text>
|
||||
}
|
||||
return <Text>foo</Text>
|
||||
}
|
||||
`,
|
||||
},
|
||||
],
|
||||
|
||||
invalid: [
|
||||
|
@ -390,6 +425,36 @@ describe('avoid-unwrapped-text', () => {
|
|||
`,
|
||||
errors: 1,
|
||||
},
|
||||
|
||||
{
|
||||
code: `
|
||||
function MyText() {
|
||||
return <Foo />
|
||||
}
|
||||
`,
|
||||
errors: 1,
|
||||
},
|
||||
|
||||
{
|
||||
code: `
|
||||
function MyText({ foo }) {
|
||||
return <Foo>{foo}</Foo>
|
||||
}
|
||||
`,
|
||||
errors: 1,
|
||||
},
|
||||
|
||||
{
|
||||
code: `
|
||||
function MyText({ foo }) {
|
||||
if (foo) {
|
||||
return <Foo>{foo}</Foo>
|
||||
}
|
||||
return <Text>foo</Text>
|
||||
}
|
||||
`,
|
||||
errors: 1,
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
|
|
|
@ -35,6 +35,11 @@ exports.create = function create(context) {
|
|||
const impliedTextComponents = options.impliedTextComponents ?? []
|
||||
const textProps = [...impliedTextProps]
|
||||
const textComponents = ['Text', ...impliedTextComponents]
|
||||
|
||||
function isTextComponent(tagName) {
|
||||
return textComponents.includes(tagName) || tagName.endsWith('Text')
|
||||
}
|
||||
|
||||
return {
|
||||
JSXText(node) {
|
||||
if (typeof node.value !== 'string' || hasOnlyLineBreak(node.value)) {
|
||||
|
@ -44,7 +49,7 @@ exports.create = function create(context) {
|
|||
while (parent) {
|
||||
if (parent.type === 'JSXElement') {
|
||||
const tagName = getTagName(parent)
|
||||
if (textComponents.includes(tagName) || tagName.endsWith('Text')) {
|
||||
if (isTextComponent(tagName)) {
|
||||
// We're good.
|
||||
return
|
||||
}
|
||||
|
@ -107,5 +112,36 @@ exports.create = function create(context) {
|
|||
continue
|
||||
}
|
||||
},
|
||||
ReturnStatement(node) {
|
||||
let fnScope = context.getScope()
|
||||
while (fnScope && fnScope.type !== 'function') {
|
||||
fnScope = fnScope.upper
|
||||
}
|
||||
if (!fnScope) {
|
||||
return
|
||||
}
|
||||
const fn = fnScope.block
|
||||
if (!fn.id || fn.id.type !== 'Identifier' || !fn.id.name) {
|
||||
return
|
||||
}
|
||||
if (!/^[A-Z]\w*Text$/.test(fn.id.name)) {
|
||||
return
|
||||
}
|
||||
if (!node.argument || node.argument.type !== 'JSXElement') {
|
||||
return
|
||||
}
|
||||
const openingEl = node.argument.openingElement
|
||||
if (openingEl.name.type !== 'JSXIdentifier') {
|
||||
return
|
||||
}
|
||||
const returnedComponentName = openingEl.name.name
|
||||
if (!isTextComponent(returnedComponentName)) {
|
||||
context.report({
|
||||
node,
|
||||
message:
|
||||
'Components ending with *Text must return <Text> or <SomeText>.',
|
||||
})
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
|
@ -250,7 +250,7 @@ export type InlineLinkProps = React.PropsWithChildren<
|
|||
BaseLinkProps & TextStyleProp & Pick<TextProps, 'selectable'>
|
||||
>
|
||||
|
||||
export function InlineLink({
|
||||
export function InlineLinkText({
|
||||
children,
|
||||
to,
|
||||
action = 'push',
|
||||
|
|
|
@ -51,7 +51,7 @@ export function Outer({
|
|||
)
|
||||
}
|
||||
|
||||
export function Title({children}: React.PropsWithChildren<{}>) {
|
||||
export function TitleText({children}: React.PropsWithChildren<{}>) {
|
||||
const {titleId} = React.useContext(Context)
|
||||
return (
|
||||
<Text nativeID={titleId} style={[a.text_2xl, a.font_bold, a.pb_sm]}>
|
||||
|
@ -60,7 +60,7 @@ export function Title({children}: React.PropsWithChildren<{}>) {
|
|||
)
|
||||
}
|
||||
|
||||
export function Description({children}: React.PropsWithChildren<{}>) {
|
||||
export function DescriptionText({children}: React.PropsWithChildren<{}>) {
|
||||
const t = useTheme()
|
||||
const {descriptionId} = React.useContext(Context)
|
||||
return (
|
||||
|
@ -175,8 +175,8 @@ export function Basic({
|
|||
}>) {
|
||||
return (
|
||||
<Outer control={control} testID="confirmModal">
|
||||
<Title>{title}</Title>
|
||||
<Description>{description}</Description>
|
||||
<TitleText>{title}</TitleText>
|
||||
<DescriptionText>{description}</DescriptionText>
|
||||
<Actions>
|
||||
<Action
|
||||
cta={confirmButtonCta}
|
||||
|
|
|
@ -7,7 +7,7 @@ import {toShortUrl} from '#/lib/strings/url-helpers'
|
|||
import {isNative} from '#/platform/detection'
|
||||
import {atoms as a, flatten, native, TextStyleProp, useTheme, web} from '#/alf'
|
||||
import {useInteractionState} from '#/components/hooks/useInteractionState'
|
||||
import {InlineLink} from '#/components/Link'
|
||||
import {InlineLinkText} from '#/components/Link'
|
||||
import {TagMenu, useTagMenuControl} from '#/components/TagMenu'
|
||||
import {Text, TextProps} from '#/components/Typography'
|
||||
|
||||
|
@ -84,7 +84,7 @@ export function RichText({
|
|||
!disableLinks
|
||||
) {
|
||||
els.push(
|
||||
<InlineLink
|
||||
<InlineLinkText
|
||||
selectable={selectable}
|
||||
key={key}
|
||||
to={`/profile/${mention.did}`}
|
||||
|
@ -92,14 +92,14 @@ export function RichText({
|
|||
// @ts-ignore TODO
|
||||
dataSet={WORD_WRAP}>
|
||||
{segment.text}
|
||||
</InlineLink>,
|
||||
</InlineLinkText>,
|
||||
)
|
||||
} else if (link && AppBskyRichtextFacet.validateLink(link).success) {
|
||||
if (disableLinks) {
|
||||
els.push(toShortUrl(segment.text))
|
||||
} else {
|
||||
els.push(
|
||||
<InlineLink
|
||||
<InlineLinkText
|
||||
selectable={selectable}
|
||||
key={key}
|
||||
to={link.uri}
|
||||
|
@ -108,7 +108,7 @@ export function RichText({
|
|||
dataSet={WORD_WRAP}
|
||||
shareOnLongPress>
|
||||
{toShortUrl(segment.text)}
|
||||
</InlineLink>,
|
||||
</InlineLinkText>,
|
||||
)
|
||||
}
|
||||
} else if (
|
||||
|
|
|
@ -1,37 +1,36 @@
|
|||
import React from 'react'
|
||||
import {Keyboard, View} from 'react-native'
|
||||
import {AppBskyActorDefs, sanitizeMutedWordValue} from '@atproto/api'
|
||||
import {msg, Trans} from '@lingui/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
import {AppBskyActorDefs, sanitizeMutedWordValue} from '@atproto/api'
|
||||
|
||||
import {
|
||||
usePreferencesQuery,
|
||||
useUpsertMutedWordsMutation,
|
||||
useRemoveMutedWordMutation,
|
||||
} from '#/state/queries/preferences'
|
||||
import {logger} from '#/logger'
|
||||
import {isNative} from '#/platform/detection'
|
||||
import {
|
||||
usePreferencesQuery,
|
||||
useRemoveMutedWordMutation,
|
||||
useUpsertMutedWordsMutation,
|
||||
} from '#/state/queries/preferences'
|
||||
import {
|
||||
atoms as a,
|
||||
useTheme,
|
||||
native,
|
||||
useBreakpoints,
|
||||
useTheme,
|
||||
ViewStyleProp,
|
||||
web,
|
||||
native,
|
||||
} from '#/alf'
|
||||
import {Text} from '#/components/Typography'
|
||||
import {Button, ButtonIcon, ButtonText} from '#/components/Button'
|
||||
import {PlusLarge_Stroke2_Corner0_Rounded as Plus} from '#/components/icons/Plus'
|
||||
import {TimesLarge_Stroke2_Corner0_Rounded as X} from '#/components/icons/Times'
|
||||
import * as Dialog from '#/components/Dialog'
|
||||
import {useGlobalDialogsControlContext} from '#/components/dialogs/Context'
|
||||
import {Divider} from '#/components/Divider'
|
||||
import * as Toggle from '#/components/forms/Toggle'
|
||||
import {Hashtag_Stroke2_Corner0_Rounded as Hashtag} from '#/components/icons/Hashtag'
|
||||
import {PageText_Stroke2_Corner0_Rounded as PageText} from '#/components/icons/PageText'
|
||||
import {Divider} from '#/components/Divider'
|
||||
import {PlusLarge_Stroke2_Corner0_Rounded as Plus} from '#/components/icons/Plus'
|
||||
import {TimesLarge_Stroke2_Corner0_Rounded as X} from '#/components/icons/Times'
|
||||
import {Loader} from '#/components/Loader'
|
||||
import {logger} from '#/logger'
|
||||
import * as Dialog from '#/components/Dialog'
|
||||
import * as Toggle from '#/components/forms/Toggle'
|
||||
import * as Prompt from '#/components/Prompt'
|
||||
|
||||
import {useGlobalDialogsControlContext} from '#/components/dialogs/Context'
|
||||
import {Text} from '#/components/Typography'
|
||||
|
||||
export function MutedWordsDialog() {
|
||||
const {mutedWordsDialogControl: control} = useGlobalDialogsControlContext()
|
||||
|
@ -130,9 +129,9 @@ function MutedWordsInner({}: {control: Dialog.DialogOuterProps['control']}) {
|
|||
<TargetToggle>
|
||||
<View style={[a.flex_row, a.align_center, a.gap_sm]}>
|
||||
<Toggle.Radio />
|
||||
<Toggle.Label>
|
||||
<Toggle.LabelText>
|
||||
<Trans>Mute in text & tags</Trans>
|
||||
</Toggle.Label>
|
||||
</Toggle.LabelText>
|
||||
</View>
|
||||
<PageText size="sm" />
|
||||
</TargetToggle>
|
||||
|
@ -145,9 +144,9 @@ function MutedWordsInner({}: {control: Dialog.DialogOuterProps['control']}) {
|
|||
<TargetToggle>
|
||||
<View style={[a.flex_row, a.align_center, a.gap_sm]}>
|
||||
<Toggle.Radio />
|
||||
<Toggle.Label>
|
||||
<Toggle.LabelText>
|
||||
<Trans>Mute in tags only</Trans>
|
||||
</Toggle.Label>
|
||||
</Toggle.LabelText>
|
||||
</View>
|
||||
<Hashtag size="sm" />
|
||||
</TargetToggle>
|
||||
|
|
|
@ -8,7 +8,7 @@ import * as TextField from '#/components/forms/TextField'
|
|||
import {DateFieldButton} from './index.shared'
|
||||
|
||||
export * as utils from '#/components/forms/DateField/utils'
|
||||
export const Label = TextField.Label
|
||||
export const LabelText = TextField.LabelText
|
||||
|
||||
export function DateField({
|
||||
value,
|
||||
|
|
|
@ -13,7 +13,7 @@ import * as TextField from '#/components/forms/TextField'
|
|||
import {DateFieldButton} from './index.shared'
|
||||
|
||||
export * as utils from '#/components/forms/DateField/utils'
|
||||
export const Label = TextField.Label
|
||||
export const LabelText = TextField.LabelText
|
||||
|
||||
/**
|
||||
* Date-only input. Accepts a date in the format YYYY-MM-DD, and reports date
|
||||
|
|
|
@ -9,7 +9,7 @@ import * as TextField from '#/components/forms/TextField'
|
|||
import {CalendarDays_Stroke2_Corner0_Rounded as CalendarDays} from '#/components/icons/CalendarDays'
|
||||
|
||||
export * as utils from '#/components/forms/DateField/utils'
|
||||
export const Label = TextField.Label
|
||||
export const LabelText = TextField.LabelText
|
||||
|
||||
const InputBase = React.forwardRef<HTMLInputElement, TextInputProps>(
|
||||
({style, ...props}, ref) => {
|
||||
|
|
|
@ -225,7 +225,7 @@ export function createInput(Component: typeof TextInput) {
|
|||
|
||||
export const Input = createInput(TextInput)
|
||||
|
||||
export function Label({
|
||||
export function LabelText({
|
||||
nativeID,
|
||||
children,
|
||||
}: React.PropsWithChildren<{nativeID?: string}>) {
|
||||
|
@ -288,7 +288,7 @@ export function Icon({icon: Comp}: {icon: React.ComponentType<SVGIconProps>}) {
|
|||
)
|
||||
}
|
||||
|
||||
export function Suffix({
|
||||
export function SuffixText({
|
||||
children,
|
||||
label,
|
||||
accessibilityHint,
|
||||
|
|
|
@ -3,16 +3,16 @@ import {Pressable, View, ViewStyle} from 'react-native'
|
|||
|
||||
import {HITSLOP_10} from 'lib/constants'
|
||||
import {
|
||||
useTheme,
|
||||
atoms as a,
|
||||
native,
|
||||
flatten,
|
||||
ViewStyleProp,
|
||||
native,
|
||||
TextStyleProp,
|
||||
useTheme,
|
||||
ViewStyleProp,
|
||||
} from '#/alf'
|
||||
import {Text} from '#/components/Typography'
|
||||
import {useInteractionState} from '#/components/hooks/useInteractionState'
|
||||
import {CheckThick_Stroke2_Corner0_Rounded as Checkmark} from '#/components/icons/Check'
|
||||
import {Text} from '#/components/Typography'
|
||||
|
||||
export type ItemState = {
|
||||
name: string
|
||||
|
@ -234,7 +234,7 @@ export function Item({
|
|||
)
|
||||
}
|
||||
|
||||
export function Label({
|
||||
export function LabelText({
|
||||
children,
|
||||
style,
|
||||
}: React.PropsWithChildren<TextStyleProp>) {
|
||||
|
|
|
@ -13,7 +13,7 @@ import {
|
|||
} from '#/state/queries/preferences'
|
||||
import {atoms as a, useBreakpoints, useTheme} from '#/alf'
|
||||
import * as ToggleButton from '#/components/forms/ToggleButton'
|
||||
import {InlineLink} from '#/components/Link'
|
||||
import {InlineLinkText} from '#/components/Link'
|
||||
import {Text} from '#/components/Typography'
|
||||
import {CircleInfo_Stroke2_Corner0_Rounded as CircleInfo} from '../icons/CircleInfo'
|
||||
|
||||
|
@ -243,9 +243,9 @@ export function LabelerLabelPreference({
|
|||
) : isGlobalLabel ? (
|
||||
<Trans>
|
||||
Configured in{' '}
|
||||
<InlineLink to="/moderation" style={a.text_sm}>
|
||||
<InlineLinkText to="/moderation" style={a.text_sm}>
|
||||
moderation settings
|
||||
</InlineLink>
|
||||
</InlineLinkText>
|
||||
.
|
||||
</Trans>
|
||||
) : null}
|
||||
|
|
|
@ -1,20 +1,19 @@
|
|||
import React from 'react'
|
||||
import {View} from 'react-native'
|
||||
import {ComAtprotoLabelDefs, ComAtprotoModerationDefs} from '@atproto/api'
|
||||
import {msg, Trans} from '@lingui/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
import {ComAtprotoLabelDefs, ComAtprotoModerationDefs} from '@atproto/api'
|
||||
|
||||
import {useLabelInfo} from '#/lib/moderation/useLabelInfo'
|
||||
import {makeProfileLink} from '#/lib/routes/links'
|
||||
import {sanitizeHandle} from '#/lib/strings/handles'
|
||||
import {getAgent} from '#/state/session'
|
||||
|
||||
import {atoms as a, useBreakpoints, useTheme} from '#/alf'
|
||||
import {Text} from '#/components/Typography'
|
||||
import * as Dialog from '#/components/Dialog'
|
||||
import {Button, ButtonText} from '#/components/Button'
|
||||
import {InlineLink} from '#/components/Link'
|
||||
import * as Toast from '#/view/com/util/Toast'
|
||||
import {atoms as a, useBreakpoints, useTheme} from '#/alf'
|
||||
import {Button, ButtonText} from '#/components/Button'
|
||||
import * as Dialog from '#/components/Dialog'
|
||||
import {InlineLinkText} from '#/components/Link'
|
||||
import {Text} from '#/components/Typography'
|
||||
import {Divider} from '../Divider'
|
||||
|
||||
export {useDialogControl as useLabelsOnMeDialogControl} from '#/components/Dialog'
|
||||
|
@ -145,13 +144,13 @@ function Label({
|
|||
<View style={[a.px_md, a.py_sm, t.atoms.bg_contrast_25]}>
|
||||
<Text style={[t.atoms.text_contrast_medium]}>
|
||||
<Trans>Source:</Trans>{' '}
|
||||
<InlineLink
|
||||
<InlineLinkText
|
||||
to={makeProfileLink(
|
||||
labeler ? labeler.creator : {did: label.src, handle: ''},
|
||||
)}
|
||||
onPress={() => control.close()}>
|
||||
{labeler ? sanitizeHandle(labeler.creator.handle, '@') : label.src}
|
||||
</InlineLink>
|
||||
</InlineLinkText>
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
|
@ -204,14 +203,14 @@ function AppealForm({
|
|||
<Text style={[a.text_md, a.leading_snug]}>
|
||||
<Trans>
|
||||
This appeal will be sent to{' '}
|
||||
<InlineLink
|
||||
<InlineLinkText
|
||||
to={makeProfileLink(
|
||||
labeler ? labeler.creator : {did: label.src, handle: ''},
|
||||
)}
|
||||
onPress={() => control.close()}
|
||||
style={[a.text_md, a.leading_snug]}>
|
||||
{labeler ? sanitizeHandle(labeler.creator.handle, '@') : label.src}
|
||||
</InlineLink>
|
||||
</InlineLinkText>
|
||||
.
|
||||
</Trans>
|
||||
</Text>
|
||||
|
|
|
@ -1,19 +1,18 @@
|
|||
import React from 'react'
|
||||
import {View} from 'react-native'
|
||||
import {ModerationCause} from '@atproto/api'
|
||||
import {msg, Trans} from '@lingui/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
import {ModerationCause} from '@atproto/api'
|
||||
|
||||
import {listUriToHref} from '#/lib/strings/url-helpers'
|
||||
import {useModerationCauseDescription} from '#/lib/moderation/useModerationCauseDescription'
|
||||
import {makeProfileLink} from '#/lib/routes/links'
|
||||
|
||||
import {listUriToHref} from '#/lib/strings/url-helpers'
|
||||
import {isNative} from '#/platform/detection'
|
||||
import {useTheme, atoms as a} from '#/alf'
|
||||
import {Text} from '#/components/Typography'
|
||||
import {atoms as a, useTheme} from '#/alf'
|
||||
import * as Dialog from '#/components/Dialog'
|
||||
import {InlineLink} from '#/components/Link'
|
||||
import {Divider} from '#/components/Divider'
|
||||
import {InlineLinkText} from '#/components/Link'
|
||||
import {Text} from '#/components/Typography'
|
||||
|
||||
export {useDialogControl as useModerationDetailsDialogControl} from '#/components/Dialog'
|
||||
|
||||
|
@ -55,9 +54,9 @@ function ModerationDetailsDialogInner({
|
|||
description = (
|
||||
<Trans>
|
||||
This user is included in the{' '}
|
||||
<InlineLink to={listUriToHref(list.uri)} style={[a.text_sm]}>
|
||||
<InlineLinkText to={listUriToHref(list.uri)} style={[a.text_sm]}>
|
||||
{list.name}
|
||||
</InlineLink>{' '}
|
||||
</InlineLinkText>{' '}
|
||||
list which you have blocked.
|
||||
</Trans>
|
||||
)
|
||||
|
@ -84,9 +83,9 @@ function ModerationDetailsDialogInner({
|
|||
description = (
|
||||
<Trans>
|
||||
This user is included in the{' '}
|
||||
<InlineLink to={listUriToHref(list.uri)} style={[a.text_sm]}>
|
||||
<InlineLinkText to={listUriToHref(list.uri)} style={[a.text_sm]}>
|
||||
{list.name}
|
||||
</InlineLink>{' '}
|
||||
</InlineLinkText>{' '}
|
||||
list which you have muted.
|
||||
</Trans>
|
||||
)
|
||||
|
@ -127,12 +126,12 @@ function ModerationDetailsDialogInner({
|
|||
{modcause.source.type === 'user' ? (
|
||||
<Trans>the author</Trans>
|
||||
) : (
|
||||
<InlineLink
|
||||
<InlineLinkText
|
||||
to={makeProfileLink({did: modcause.label.src, handle: ''})}
|
||||
onPress={() => control.close()}
|
||||
style={a.text_md}>
|
||||
{desc.source}
|
||||
</InlineLink>
|
||||
</InlineLinkText>
|
||||
)}
|
||||
.
|
||||
</Trans>
|
||||
|
|
|
@ -58,11 +58,11 @@ export const ChooseAccountForm = ({
|
|||
return (
|
||||
<FormContainer
|
||||
testID="chooseAccountForm"
|
||||
title={<Trans>Select account</Trans>}>
|
||||
titleText={<Trans>Select account</Trans>}>
|
||||
<View>
|
||||
<TextField.Label>
|
||||
<TextField.LabelText>
|
||||
<Trans>Sign in as...</Trans>
|
||||
</TextField.Label>
|
||||
</TextField.LabelText>
|
||||
<AccountList
|
||||
onSelectAccount={onSelect}
|
||||
onSelectOther={() => onSelectAccount()}
|
||||
|
|
|
@ -83,11 +83,11 @@ export const ForgotPasswordForm = ({
|
|||
return (
|
||||
<FormContainer
|
||||
testID="forgotPasswordForm"
|
||||
title={<Trans>Reset password</Trans>}>
|
||||
titleText={<Trans>Reset password</Trans>}>
|
||||
<View>
|
||||
<TextField.Label>
|
||||
<TextField.LabelText>
|
||||
<Trans>Hosting provider</Trans>
|
||||
</TextField.Label>
|
||||
</TextField.LabelText>
|
||||
<HostingProvider
|
||||
serviceUrl={serviceUrl}
|
||||
onSelectServiceUrl={setServiceUrl}
|
||||
|
@ -95,9 +95,9 @@ export const ForgotPasswordForm = ({
|
|||
/>
|
||||
</View>
|
||||
<View>
|
||||
<TextField.Label>
|
||||
<TextField.LabelText>
|
||||
<Trans>Email address</Trans>
|
||||
</TextField.Label>
|
||||
</TextField.LabelText>
|
||||
<TextField.Root>
|
||||
<TextField.Icon icon={At} />
|
||||
<TextField.Input
|
||||
|
|
|
@ -6,12 +6,12 @@ import {Text} from '#/components/Typography'
|
|||
|
||||
export function FormContainer({
|
||||
testID,
|
||||
title,
|
||||
titleText,
|
||||
children,
|
||||
style,
|
||||
}: {
|
||||
testID?: string
|
||||
title?: React.ReactNode
|
||||
titleText?: React.ReactNode
|
||||
children: React.ReactNode
|
||||
style?: StyleProp<ViewStyle>
|
||||
}) {
|
||||
|
@ -21,9 +21,9 @@ export function FormContainer({
|
|||
<View
|
||||
testID={testID}
|
||||
style={[a.gap_md, a.flex_1, !gtMobile && [a.px_lg, a.py_md], style]}>
|
||||
{title && !gtMobile && (
|
||||
{titleText && !gtMobile && (
|
||||
<Text style={[a.text_xl, a.font_bold, t.atoms.text_contrast_high]}>
|
||||
{title}
|
||||
{titleText}
|
||||
</Text>
|
||||
)}
|
||||
{children}
|
||||
|
|
|
@ -128,11 +128,11 @@ export const LoginForm = ({
|
|||
|
||||
const isReady = !!serviceDescription && !!identifier && !!password
|
||||
return (
|
||||
<FormContainer testID="loginForm" title={<Trans>Sign in</Trans>}>
|
||||
<FormContainer testID="loginForm" titleText={<Trans>Sign in</Trans>}>
|
||||
<View>
|
||||
<TextField.Label>
|
||||
<TextField.LabelText>
|
||||
<Trans>Hosting provider</Trans>
|
||||
</TextField.Label>
|
||||
</TextField.LabelText>
|
||||
<HostingProvider
|
||||
serviceUrl={serviceUrl}
|
||||
onSelectServiceUrl={setServiceUrl}
|
||||
|
@ -140,9 +140,9 @@ export const LoginForm = ({
|
|||
/>
|
||||
</View>
|
||||
<View>
|
||||
<TextField.Label>
|
||||
<TextField.LabelText>
|
||||
<Trans>Account</Trans>
|
||||
</TextField.Label>
|
||||
</TextField.LabelText>
|
||||
<View style={[a.gap_sm]}>
|
||||
<TextField.Root>
|
||||
<TextField.Icon icon={At} />
|
||||
|
|
|
@ -99,7 +99,7 @@ export const SetNewPasswordForm = ({
|
|||
return (
|
||||
<FormContainer
|
||||
testID="setNewPasswordForm"
|
||||
title={<Trans>Set new password</Trans>}>
|
||||
titleText={<Trans>Set new password</Trans>}>
|
||||
<Text style={[a.leading_snug, a.mb_sm]}>
|
||||
<Trans>
|
||||
You will receive an email with a "reset code." Enter that code here,
|
||||
|
@ -108,7 +108,7 @@ export const SetNewPasswordForm = ({
|
|||
</Text>
|
||||
|
||||
<View>
|
||||
<TextField.Label>Reset code</TextField.Label>
|
||||
<TextField.LabelText>Reset code</TextField.LabelText>
|
||||
<TextField.Root>
|
||||
<TextField.Icon icon={Ticket} />
|
||||
<TextField.Input
|
||||
|
@ -131,7 +131,7 @@ export const SetNewPasswordForm = ({
|
|||
</View>
|
||||
|
||||
<View>
|
||||
<TextField.Label>New password</TextField.Label>
|
||||
<TextField.LabelText>New password</TextField.LabelText>
|
||||
<TextField.Root>
|
||||
<TextField.Icon icon={Lock} />
|
||||
<TextField.Input
|
||||
|
|
|
@ -40,7 +40,7 @@ import {Filter_Stroke2_Corner0_Rounded as Filter} from '#/components/icons/Filte
|
|||
import {Group3_Stroke2_Corner0_Rounded as Group} from '#/components/icons/Group'
|
||||
import {Person_Stroke2_Corner0_Rounded as Person} from '#/components/icons/Person'
|
||||
import * as LabelingService from '#/components/LabelingServiceCard'
|
||||
import {InlineLink, Link} from '#/components/Link'
|
||||
import {InlineLinkText, Link} from '#/components/Link'
|
||||
import {Loader} from '#/components/Loader'
|
||||
import {GlobalLabelPreference} from '#/components/moderation/LabelPreference'
|
||||
import {Text} from '#/components/Typography'
|
||||
|
@ -518,11 +518,11 @@ function PwiOptOut() {
|
|||
msg`Discourage apps from showing my account to logged-out users`,
|
||||
)}>
|
||||
<Toggle.Switch />
|
||||
<Toggle.Label style={[a.text_md, a.flex_1]}>
|
||||
<Toggle.LabelText style={[a.text_md, a.flex_1]}>
|
||||
<Trans>
|
||||
Discourage apps from showing my account to logged-out users
|
||||
</Trans>
|
||||
</Toggle.Label>
|
||||
</Toggle.LabelText>
|
||||
</Toggle.Item>
|
||||
|
||||
{updateProfile.isPending && <Loader />}
|
||||
|
@ -545,9 +545,9 @@ function PwiOptOut() {
|
|||
</Trans>
|
||||
</Text>
|
||||
|
||||
<InlineLink to="https://blueskyweb.zendesk.com/hc/en-us/articles/15835264007693-Data-Privacy">
|
||||
<InlineLinkText to="https://blueskyweb.zendesk.com/hc/en-us/articles/15835264007693-Data-Privacy">
|
||||
<Trans>Learn more about what is public on Bluesky.</Trans>
|
||||
</InlineLink>
|
||||
</InlineLinkText>
|
||||
</View>
|
||||
</View>
|
||||
)
|
||||
|
|
|
@ -1,29 +1,27 @@
|
|||
import React from 'react'
|
||||
import {View} from 'react-native'
|
||||
import {useSafeAreaInsets} from 'react-native-safe-area-context'
|
||||
import {useLingui} from '@lingui/react'
|
||||
import {msg} from '@lingui/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
|
||||
import {IS_DEV} from '#/env'
|
||||
import {isWeb} from '#/platform/detection'
|
||||
import {useOnboardingDispatch} from '#/state/shell'
|
||||
|
||||
import {
|
||||
useTheme,
|
||||
atoms as a,
|
||||
useBreakpoints,
|
||||
web,
|
||||
native,
|
||||
flatten,
|
||||
TextStyleProp,
|
||||
} from '#/alf'
|
||||
import {P, leading, Text} from '#/components/Typography'
|
||||
import {ChevronLeft_Stroke2_Corner0_Rounded as ChevronLeft} from '#/components/icons/Chevron'
|
||||
import {Button, ButtonIcon} from '#/components/Button'
|
||||
import {ScrollView} from '#/view/com/util/Views'
|
||||
import {createPortalGroup} from '#/components/Portal'
|
||||
|
||||
import {Context} from '#/screens/Onboarding/state'
|
||||
import {
|
||||
atoms as a,
|
||||
flatten,
|
||||
native,
|
||||
TextStyleProp,
|
||||
useBreakpoints,
|
||||
useTheme,
|
||||
web,
|
||||
} from '#/alf'
|
||||
import {Button, ButtonIcon} from '#/components/Button'
|
||||
import {ChevronLeft_Stroke2_Corner0_Rounded as ChevronLeft} from '#/components/icons/Chevron'
|
||||
import {createPortalGroup} from '#/components/Portal'
|
||||
import {leading, P, Text} from '#/components/Typography'
|
||||
import {IS_DEV} from '#/env'
|
||||
|
||||
const COL_WIDTH = 500
|
||||
|
||||
|
@ -204,7 +202,7 @@ export function Layout({children}: React.PropsWithChildren<{}>) {
|
|||
)
|
||||
}
|
||||
|
||||
export function Title({
|
||||
export function TitleText({
|
||||
children,
|
||||
style,
|
||||
}: React.PropsWithChildren<TextStyleProp>) {
|
||||
|
@ -224,7 +222,7 @@ export function Title({
|
|||
)
|
||||
}
|
||||
|
||||
export function Description({
|
||||
export function DescriptionText({
|
||||
children,
|
||||
style,
|
||||
}: React.PropsWithChildren<TextStyleProp>) {
|
||||
|
|
|
@ -6,9 +6,9 @@ import {useLingui} from '@lingui/react'
|
|||
import {useAnalytics} from '#/lib/analytics/analytics'
|
||||
import {logEvent} from '#/lib/statsig/statsig'
|
||||
import {
|
||||
Description,
|
||||
DescriptionText,
|
||||
OnboardingControls,
|
||||
Title,
|
||||
TitleText,
|
||||
} from '#/screens/Onboarding/Layout'
|
||||
import {Context} from '#/screens/Onboarding/state'
|
||||
import {FeedCard} from '#/screens/Onboarding/StepAlgoFeeds/FeedCard'
|
||||
|
@ -105,15 +105,15 @@ export function StepAlgoFeeds() {
|
|||
<View style={[a.align_start]}>
|
||||
<IconCircle icon={ListSparkle} style={[a.mb_2xl]} />
|
||||
|
||||
<Title>
|
||||
<TitleText>
|
||||
<Trans>Choose your main feeds</Trans>
|
||||
</Title>
|
||||
<Description>
|
||||
</TitleText>
|
||||
<DescriptionText>
|
||||
<Trans>
|
||||
Custom feeds built by the community bring you new experiences and help
|
||||
you find the content you love.
|
||||
</Trans>
|
||||
</Description>
|
||||
</DescriptionText>
|
||||
|
||||
<View style={[a.w_full, a.pb_2xl]}>
|
||||
<Toggle.Group
|
||||
|
|
|
@ -10,9 +10,9 @@ import {useSetSaveFeedsMutation} from '#/state/queries/preferences'
|
|||
import {getAgent} from '#/state/session'
|
||||
import {useOnboardingDispatch} from '#/state/shell'
|
||||
import {
|
||||
Description,
|
||||
DescriptionText,
|
||||
OnboardingControls,
|
||||
Title,
|
||||
TitleText,
|
||||
} from '#/screens/Onboarding/Layout'
|
||||
import {Context} from '#/screens/Onboarding/state'
|
||||
import {
|
||||
|
@ -87,12 +87,12 @@ export function StepFinished() {
|
|||
<View style={[a.align_start]}>
|
||||
<IconCircle icon={Check} style={[a.mb_2xl]} />
|
||||
|
||||
<Title>
|
||||
<TitleText>
|
||||
<Trans>You're ready to go!</Trans>
|
||||
</Title>
|
||||
<Description>
|
||||
</TitleText>
|
||||
<DescriptionText>
|
||||
<Trans>We hope you have a wonderful time. Remember, Bluesky is:</Trans>
|
||||
</Description>
|
||||
</DescriptionText>
|
||||
|
||||
<View style={[a.pt_5xl, a.gap_3xl]}>
|
||||
<View style={[a.flex_row, a.align_center, a.w_full, a.gap_lg]}>
|
||||
|
|
|
@ -10,9 +10,9 @@ import {
|
|||
useSetFeedViewPreferencesMutation,
|
||||
} from 'state/queries/preferences'
|
||||
import {
|
||||
Description,
|
||||
DescriptionText,
|
||||
OnboardingControls,
|
||||
Title,
|
||||
TitleText,
|
||||
} from '#/screens/Onboarding/Layout'
|
||||
import {Context} from '#/screens/Onboarding/state'
|
||||
import {atoms as a} from '#/alf'
|
||||
|
@ -58,12 +58,12 @@ export function StepFollowingFeed() {
|
|||
<View style={[a.align_start]}>
|
||||
<IconCircle icon={FilterTimeline} style={[a.mb_2xl]} />
|
||||
|
||||
<Title>
|
||||
<TitleText>
|
||||
<Trans>Your default feed is "Following"</Trans>
|
||||
</Title>
|
||||
<Description style={[a.mb_md]}>
|
||||
</TitleText>
|
||||
<DescriptionText style={[a.mb_md]}>
|
||||
<Trans>It shows posts from the people you follow as they happen.</Trans>
|
||||
</Description>
|
||||
</DescriptionText>
|
||||
|
||||
<View style={[a.w_full]}>
|
||||
<Toggle.Item
|
||||
|
@ -139,9 +139,9 @@ export function StepFollowingFeed() {
|
|||
</Toggle.Item>
|
||||
</View>
|
||||
|
||||
<Description style={[a.mt_lg]}>
|
||||
<DescriptionText style={[a.mt_lg]}>
|
||||
<Trans>You can change these settings later.</Trans>
|
||||
</Description>
|
||||
</DescriptionText>
|
||||
|
||||
<OnboardingControls.Portal>
|
||||
<Button
|
||||
|
|
|
@ -11,9 +11,9 @@ import {logger} from '#/logger'
|
|||
import {getAgent} from '#/state/session'
|
||||
import {useOnboardingDispatch} from '#/state/shell'
|
||||
import {
|
||||
Description,
|
||||
DescriptionText,
|
||||
OnboardingControls,
|
||||
Title,
|
||||
TitleText,
|
||||
} from '#/screens/Onboarding/Layout'
|
||||
import {ApiResponseMap, Context} from '#/screens/Onboarding/state'
|
||||
import {InterestButton} from '#/screens/Onboarding/StepInterests/InterestButton'
|
||||
|
@ -163,8 +163,8 @@ export function StepInterests() {
|
|||
]}
|
||||
/>
|
||||
|
||||
<Title>{title}</Title>
|
||||
<Description>{description}</Description>
|
||||
<TitleText>{title}</TitleText>
|
||||
<DescriptionText>{description}</DescriptionText>
|
||||
|
||||
<View style={[a.w_full, a.pt_2xl]}>
|
||||
{isLoading ? (
|
||||
|
|
|
@ -113,15 +113,15 @@ export function AdultContentEnabledPref({
|
|||
)}
|
||||
|
||||
<Prompt.Outer control={prompt}>
|
||||
<Prompt.Title>
|
||||
<Prompt.TitleText>
|
||||
<Trans>Adult Content</Trans>
|
||||
</Prompt.Title>
|
||||
<Prompt.Description>
|
||||
</Prompt.TitleText>
|
||||
<Prompt.DescriptionText>
|
||||
<Trans>
|
||||
Due to Apple policies, adult content can only be enabled on the web
|
||||
after completing sign up.
|
||||
</Trans>
|
||||
</Prompt.Description>
|
||||
</Prompt.DescriptionText>
|
||||
<Prompt.Actions>
|
||||
<Prompt.Action onPress={() => prompt.close()} cta={_(msg`OK`)} />
|
||||
</Prompt.Actions>
|
||||
|
|
|
@ -9,9 +9,9 @@ import {logEvent} from '#/lib/statsig/statsig'
|
|||
import {usePreferencesQuery} from '#/state/queries/preferences'
|
||||
import {usePreferencesSetAdultContentMutation} from 'state/queries/preferences'
|
||||
import {
|
||||
Description,
|
||||
DescriptionText,
|
||||
OnboardingControls,
|
||||
Title,
|
||||
TitleText,
|
||||
} from '#/screens/Onboarding/Layout'
|
||||
import {Context} from '#/screens/Onboarding/state'
|
||||
import {AdultContentEnabledPref} from '#/screens/Onboarding/StepModeration/AdultContentEnabledPref'
|
||||
|
@ -56,14 +56,14 @@ export function StepModeration() {
|
|||
<View style={[a.align_start]}>
|
||||
<IconCircle icon={EyeSlash} style={[a.mb_2xl]} />
|
||||
|
||||
<Title>
|
||||
<TitleText>
|
||||
<Trans>You're in control</Trans>
|
||||
</Title>
|
||||
<Description style={[a.mb_xl]}>
|
||||
</TitleText>
|
||||
<DescriptionText style={[a.mb_xl]}>
|
||||
<Trans>
|
||||
Select what you want to see (or not see), and we’ll handle the rest.
|
||||
</Trans>
|
||||
</Description>
|
||||
</DescriptionText>
|
||||
|
||||
{!preferences ? (
|
||||
<View style={[a.pt_md]}>
|
||||
|
|
|
@ -10,9 +10,9 @@ import {capitalize} from '#/lib/strings/capitalize'
|
|||
import {useModerationOpts} from '#/state/queries/preferences'
|
||||
import {useProfilesQuery} from '#/state/queries/profile'
|
||||
import {
|
||||
Description,
|
||||
DescriptionText,
|
||||
OnboardingControls,
|
||||
Title,
|
||||
TitleText,
|
||||
} from '#/screens/Onboarding/Layout'
|
||||
import {Context} from '#/screens/Onboarding/state'
|
||||
import {
|
||||
|
@ -136,16 +136,16 @@ export function StepSuggestedAccounts() {
|
|||
<View style={[a.align_start]}>
|
||||
<IconCircle icon={At} style={[a.mb_2xl]} />
|
||||
|
||||
<Title>
|
||||
<TitleText>
|
||||
<Trans>Here are some accounts for you to follow</Trans>
|
||||
</Title>
|
||||
<Description>
|
||||
</TitleText>
|
||||
<DescriptionText>
|
||||
{state.interestsStepResults.selectedInterests.length ? (
|
||||
<Trans>Based on your interest in {interestsText}</Trans>
|
||||
) : (
|
||||
<Trans>These are popular accounts you might like:</Trans>
|
||||
)}
|
||||
</Description>
|
||||
</DescriptionText>
|
||||
|
||||
<View style={[a.w_full, a.pt_xl]}>
|
||||
{isLoading ? (
|
||||
|
|
|
@ -9,9 +9,9 @@ import {capitalize} from '#/lib/strings/capitalize'
|
|||
import {IS_TEST_USER} from 'lib/constants'
|
||||
import {useSession} from 'state/session'
|
||||
import {
|
||||
Description,
|
||||
DescriptionText,
|
||||
OnboardingControls,
|
||||
Title,
|
||||
TitleText,
|
||||
} from '#/screens/Onboarding/Layout'
|
||||
import {Context} from '#/screens/Onboarding/state'
|
||||
import {FeedCard} from '#/screens/Onboarding/StepAlgoFeeds/FeedCard'
|
||||
|
@ -76,10 +76,10 @@ export function StepTopicalFeeds() {
|
|||
<View style={[a.align_start]}>
|
||||
<IconCircle icon={ListMagnifyingGlass} style={[a.mb_2xl]} />
|
||||
|
||||
<Title>
|
||||
<TitleText>
|
||||
<Trans>Feeds can be topical as well!</Trans>
|
||||
</Title>
|
||||
<Description>
|
||||
</TitleText>
|
||||
<DescriptionText>
|
||||
{state.interestsStepResults.selectedInterests.length ? (
|
||||
<Trans>
|
||||
Here are some topical feeds based on your interests: {interestsText}
|
||||
|
@ -91,7 +91,7 @@ export function StepTopicalFeeds() {
|
|||
many as you like.
|
||||
</Trans>
|
||||
)}
|
||||
</Description>
|
||||
</DescriptionText>
|
||||
|
||||
<View style={[a.w_full, a.pb_2xl, a.pt_2xl]}>
|
||||
<Toggle.Group
|
||||
|
|
|
@ -1,17 +1,16 @@
|
|||
import React from 'react'
|
||||
import {View} from 'react-native'
|
||||
import {AppBskyActorDefs} from '@atproto/api'
|
||||
import {Trans, msg} from '@lingui/macro'
|
||||
import {msg, Trans} from '@lingui/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
|
||||
import {Shadow} from '#/state/cache/types'
|
||||
import {pluralize} from '#/lib/strings/helpers'
|
||||
import {Shadow} from '#/state/cache/types'
|
||||
import {makeProfileLink} from 'lib/routes/links'
|
||||
import {formatCount} from 'view/com/util/numeric/format'
|
||||
|
||||
import {atoms as a, useTheme} from '#/alf'
|
||||
import {InlineLinkText} from '#/components/Link'
|
||||
import {Text} from '#/components/Typography'
|
||||
import {InlineLink} from '#/components/Link'
|
||||
|
||||
export function ProfileHeaderMetrics({
|
||||
profile,
|
||||
|
@ -28,7 +27,7 @@ export function ProfileHeaderMetrics({
|
|||
<View
|
||||
style={[a.flex_row, a.gap_sm, a.align_center, a.pb_md]}
|
||||
pointerEvents="box-none">
|
||||
<InlineLink
|
||||
<InlineLinkText
|
||||
testID="profileHeaderFollowersButton"
|
||||
style={[a.flex_row, t.atoms.text]}
|
||||
to={makeProfileLink(profile, 'followers')}
|
||||
|
@ -37,8 +36,8 @@ export function ProfileHeaderMetrics({
|
|||
<Text style={[t.atoms.text_contrast_medium, a.text_md]}>
|
||||
{pluralizedFollowers}
|
||||
</Text>
|
||||
</InlineLink>
|
||||
<InlineLink
|
||||
</InlineLinkText>
|
||||
<InlineLinkText
|
||||
testID="profileHeaderFollowsButton"
|
||||
style={[a.flex_row, t.atoms.text]}
|
||||
to={makeProfileLink(profile, 'follows')}
|
||||
|
@ -49,7 +48,7 @@ export function ProfileHeaderMetrics({
|
|||
following
|
||||
</Text>
|
||||
</Trans>
|
||||
</InlineLink>
|
||||
</InlineLinkText>
|
||||
<Text style={[a.font_bold, t.atoms.text, a.text_md]}>
|
||||
{formatCount(profile.postsCount || 0)}{' '}
|
||||
<Text style={[t.atoms.text_contrast_medium, a.font_normal, a.text_md]}>
|
||||
|
|
|
@ -316,13 +316,13 @@ function CantSubscribePrompt({
|
|||
const {_} = useLingui()
|
||||
return (
|
||||
<Prompt.Outer control={control}>
|
||||
<Prompt.Title>Unable to subscribe</Prompt.Title>
|
||||
<Prompt.Description>
|
||||
<Prompt.TitleText>Unable to subscribe</Prompt.TitleText>
|
||||
<Prompt.DescriptionText>
|
||||
<Trans>
|
||||
We're sorry! You can only subscribe to ten labelers, and you've
|
||||
reached your limit of ten.
|
||||
</Trans>
|
||||
</Prompt.Description>
|
||||
</Prompt.DescriptionText>
|
||||
<Prompt.Actions>
|
||||
<Prompt.Action onPress={control.close} cta={_(msg`OK`)} />
|
||||
</Prompt.Actions>
|
||||
|
|
|
@ -6,7 +6,7 @@ import {useLingui} from '@lingui/react'
|
|||
|
||||
import {atoms as a, useTheme} from '#/alf'
|
||||
import {CircleInfo_Stroke2_Corner0_Rounded as CircleInfo} from '#/components/icons/CircleInfo'
|
||||
import {InlineLink} from '#/components/Link'
|
||||
import {InlineLinkText} from '#/components/Link'
|
||||
import {Text} from '#/components/Typography'
|
||||
|
||||
export const Policies = ({
|
||||
|
@ -45,16 +45,16 @@ export const Policies = ({
|
|||
const els = []
|
||||
if (tos) {
|
||||
els.push(
|
||||
<InlineLink key="tos" to={tos}>
|
||||
<InlineLinkText key="tos" to={tos}>
|
||||
{_(msg`Terms of Service`)}
|
||||
</InlineLink>,
|
||||
</InlineLinkText>,
|
||||
)
|
||||
}
|
||||
if (pp) {
|
||||
els.push(
|
||||
<InlineLink key="pp" to={pp}>
|
||||
<InlineLinkText key="pp" to={pp}>
|
||||
{_(msg`Privacy Policy`)}
|
||||
</InlineLink>,
|
||||
</InlineLinkText>,
|
||||
)
|
||||
}
|
||||
if (els.length === 2) {
|
||||
|
|
|
@ -36,9 +36,9 @@ export function StepInfo() {
|
|||
<View style={[a.gap_md]}>
|
||||
<FormError error={state.error} />
|
||||
<View>
|
||||
<TextField.Label>
|
||||
<TextField.LabelText>
|
||||
<Trans>Hosting provider</Trans>
|
||||
</TextField.Label>
|
||||
</TextField.LabelText>
|
||||
<HostingProvider
|
||||
serviceUrl={state.serviceUrl}
|
||||
onSelectServiceUrl={v =>
|
||||
|
@ -54,9 +54,9 @@ export function StepInfo() {
|
|||
<>
|
||||
{state.serviceDescription.inviteCodeRequired && (
|
||||
<View>
|
||||
<TextField.Label>
|
||||
<TextField.LabelText>
|
||||
<Trans>Invite code</Trans>
|
||||
</TextField.Label>
|
||||
</TextField.LabelText>
|
||||
<TextField.Root>
|
||||
<TextField.Icon icon={Ticket} />
|
||||
<TextField.Input
|
||||
|
@ -76,9 +76,9 @@ export function StepInfo() {
|
|||
</View>
|
||||
)}
|
||||
<View>
|
||||
<TextField.Label>
|
||||
<TextField.LabelText>
|
||||
<Trans>Email</Trans>
|
||||
</TextField.Label>
|
||||
</TextField.LabelText>
|
||||
<TextField.Root>
|
||||
<TextField.Icon icon={Envelope} />
|
||||
<TextField.Input
|
||||
|
@ -97,9 +97,9 @@ export function StepInfo() {
|
|||
</TextField.Root>
|
||||
</View>
|
||||
<View>
|
||||
<TextField.Label>
|
||||
<TextField.LabelText>
|
||||
<Trans>Password</Trans>
|
||||
</TextField.Label>
|
||||
</TextField.LabelText>
|
||||
<TextField.Root>
|
||||
<TextField.Icon icon={Lock} />
|
||||
<TextField.Input
|
||||
|
@ -117,9 +117,9 @@ export function StepInfo() {
|
|||
</TextField.Root>
|
||||
</View>
|
||||
<View>
|
||||
<DateField.Label>
|
||||
<DateField.LabelText>
|
||||
<Trans>Your birth date</Trans>
|
||||
</DateField.Label>
|
||||
</DateField.LabelText>
|
||||
<DateField.DateField
|
||||
testID="date"
|
||||
value={DateField.utils.toSimpleDateString(state.dateOfBirth)}
|
||||
|
|
|
@ -24,7 +24,7 @@ import {StepInfo} from '#/screens/Signup/StepInfo'
|
|||
import {atoms as a, useBreakpoints, useTheme} from '#/alf'
|
||||
import {Button, ButtonText} from '#/components/Button'
|
||||
import {Divider} from '#/components/Divider'
|
||||
import {InlineLink} from '#/components/Link'
|
||||
import {InlineLinkText} from '#/components/Link'
|
||||
import {Text} from '#/components/Typography'
|
||||
|
||||
export function Signup({onPressBack}: {onPressBack: () => void}) {
|
||||
|
@ -215,9 +215,9 @@ export function Signup({onPressBack}: {onPressBack: () => void}) {
|
|||
<View style={[a.w_full, a.py_lg]}>
|
||||
<Text style={[t.atoms.text_contrast_medium]}>
|
||||
<Trans>Having trouble?</Trans>{' '}
|
||||
<InlineLink to={FEEDBACK_FORM_URL({email: state.email})}>
|
||||
<InlineLinkText to={FEEDBACK_FORM_URL({email: state.email})}>
|
||||
<Trans>Contact support</Trans>
|
||||
</InlineLink>
|
||||
</InlineLinkText>
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
|
|
|
@ -14,7 +14,7 @@ import {ErrorBoundary} from 'view/com/util/ErrorBoundary'
|
|||
import {atoms as a, useTheme} from '#/alf'
|
||||
import {Button, ButtonText} from '#/components/Button'
|
||||
import {ChevronBottom_Stroke2_Corner0_Rounded as ChevronDown} from '#/components/icons/Chevron'
|
||||
import {InlineLink} from '#/components/Link'
|
||||
import {InlineLinkText} from '#/components/Link'
|
||||
import {Text} from '#/components/Typography'
|
||||
import {CenteredView} from '../util/Views'
|
||||
|
||||
|
@ -162,15 +162,15 @@ function Footer() {
|
|||
a.flex_1,
|
||||
t.atoms.border_contrast_medium,
|
||||
]}>
|
||||
<InlineLink to="https://bsky.social">
|
||||
<InlineLinkText to="https://bsky.social">
|
||||
<Trans>Business</Trans>
|
||||
</InlineLink>
|
||||
<InlineLink to="https://bsky.social/about/blog">
|
||||
</InlineLinkText>
|
||||
<InlineLinkText to="https://bsky.social/about/blog">
|
||||
<Trans>Blog</Trans>
|
||||
</InlineLink>
|
||||
<InlineLink to="https://bsky.social/about/join">
|
||||
</InlineLinkText>
|
||||
<InlineLinkText to="https://bsky.social/about/join">
|
||||
<Trans>Jobs</Trans>
|
||||
</InlineLink>
|
||||
</InlineLinkText>
|
||||
|
||||
<View style={a.flex_1} />
|
||||
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
import React from 'react'
|
||||
import {View} from 'react-native'
|
||||
import {msg, Trans} from '@lingui/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
import {Trans, msg} from '@lingui/macro'
|
||||
import {BSKY_SERVICE} from 'lib/constants'
|
||||
import * as persisted from '#/state/persisted'
|
||||
|
||||
import * as persisted from '#/state/persisted'
|
||||
import {BSKY_SERVICE} from 'lib/constants'
|
||||
import {atoms as a, useBreakpoints, useTheme} from '#/alf'
|
||||
import * as Dialog from '#/components/Dialog'
|
||||
import {Text, P} from '#/components/Typography'
|
||||
import {Button, ButtonText} from '#/components/Button'
|
||||
import * as ToggleButton from '#/components/forms/ToggleButton'
|
||||
import * as Dialog from '#/components/Dialog'
|
||||
import * as TextField from '#/components/forms/TextField'
|
||||
import * as ToggleButton from '#/components/forms/ToggleButton'
|
||||
import {Globe_Stroke2_Corner0_Rounded as Globe} from '#/components/icons/Globe'
|
||||
import {P, Text} from '#/components/Typography'
|
||||
|
||||
export function ServerInputDialog({
|
||||
control,
|
||||
|
@ -106,9 +106,9 @@ export function ServerInputDialog({
|
|||
a.px_md,
|
||||
a.py_md,
|
||||
]}>
|
||||
<TextField.Label nativeID="address-input-label">
|
||||
<TextField.LabelText nativeID="address-input-label">
|
||||
<Trans>Server address</Trans>
|
||||
</TextField.Label>
|
||||
</TextField.LabelText>
|
||||
<TextField.Root>
|
||||
<TextField.Icon icon={Globe} />
|
||||
<Dialog.Input
|
||||
|
|
|
@ -1,51 +1,51 @@
|
|||
import React from 'react'
|
||||
import {NativeStackScreenProps, CommonNavigatorParams} from 'lib/routes/types'
|
||||
import {View} from 'react-native'
|
||||
import {
|
||||
AppBskyActorDefs,
|
||||
AppBskyFeedDefs,
|
||||
AppBskyFeedPost,
|
||||
ComAtprotoLabelDefs,
|
||||
interpretLabelValueDefinition,
|
||||
LabelPreference,
|
||||
LABELS,
|
||||
mock,
|
||||
moderatePost,
|
||||
moderateProfile,
|
||||
ModerationOpts,
|
||||
AppBskyActorDefs,
|
||||
AppBskyFeedDefs,
|
||||
AppBskyFeedPost,
|
||||
LabelPreference,
|
||||
ModerationDecision,
|
||||
ModerationBehavior,
|
||||
ModerationDecision,
|
||||
ModerationOpts,
|
||||
RichText,
|
||||
ComAtprotoLabelDefs,
|
||||
interpretLabelValueDefinition,
|
||||
} from '@atproto/api'
|
||||
import {msg} from '@lingui/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
import {moderationOptsOverrideContext} from '#/state/queries/preferences'
|
||||
import {useSession} from '#/state/session'
|
||||
|
||||
import {useGlobalLabelStrings} from '#/lib/moderation/useGlobalLabelStrings'
|
||||
import {FeedNotification} from '#/state/queries/notifications/types'
|
||||
import {
|
||||
groupNotifications,
|
||||
shouldFilterNotif,
|
||||
} from '#/state/queries/notifications/util'
|
||||
|
||||
import {atoms as a, useTheme} from '#/alf'
|
||||
import {moderationOptsOverrideContext} from '#/state/queries/preferences'
|
||||
import {useSession} from '#/state/session'
|
||||
import {CommonNavigatorParams, NativeStackScreenProps} from 'lib/routes/types'
|
||||
import {CenteredView, ScrollView} from '#/view/com/util/Views'
|
||||
import {H1, H3, P, Text} from '#/components/Typography'
|
||||
import {useGlobalLabelStrings} from '#/lib/moderation/useGlobalLabelStrings'
|
||||
import {ProfileHeaderStandard} from '#/screens/Profile/Header/ProfileHeaderStandard'
|
||||
import {atoms as a, useTheme} from '#/alf'
|
||||
import {Button, ButtonIcon, ButtonText} from '#/components/Button'
|
||||
import {Divider} from '#/components/Divider'
|
||||
import * as Toggle from '#/components/forms/Toggle'
|
||||
import * as ToggleButton from '#/components/forms/ToggleButton'
|
||||
import {Button, ButtonIcon, ButtonText} from '#/components/Button'
|
||||
import {Check_Stroke2_Corner0_Rounded as Check} from '#/components/icons/Check'
|
||||
import {
|
||||
ChevronBottom_Stroke2_Corner0_Rounded as ChevronBottom,
|
||||
ChevronTop_Stroke2_Corner0_Rounded as ChevronTop,
|
||||
} from '#/components/icons/Chevron'
|
||||
import {H1, H3, P, Text} from '#/components/Typography'
|
||||
import {ScreenHider} from '../../components/moderation/ScreenHider'
|
||||
import {ProfileHeaderStandard} from '#/screens/Profile/Header/ProfileHeaderStandard'
|
||||
import {ProfileCard} from '../com/profile/ProfileCard'
|
||||
import {FeedItem} from '../com/posts/FeedItem'
|
||||
import {FeedItem as NotifFeedItem} from '../com/notifications/FeedItem'
|
||||
import {PostThreadItem} from '../com/post-thread/PostThreadItem'
|
||||
import {Divider} from '#/components/Divider'
|
||||
import {FeedItem} from '../com/posts/FeedItem'
|
||||
import {ProfileCard} from '../com/profile/ProfileCard'
|
||||
|
||||
const LABEL_VALUES: (keyof typeof LABELS)[] = Object.keys(
|
||||
LABELS,
|
||||
|
@ -320,7 +320,7 @@ export const DebugModScreen = ({}: NativeStackScreenProps<
|
|||
disabled={disabled}
|
||||
style={disabled ? {opacity: 0.5} : undefined}>
|
||||
<Toggle.Radio />
|
||||
<Toggle.Label>{labelValue}</Toggle.Label>
|
||||
<Toggle.LabelText>{labelValue}</Toggle.LabelText>
|
||||
</Toggle.Item>
|
||||
)
|
||||
})}
|
||||
|
@ -330,7 +330,7 @@ export const DebugModScreen = ({}: NativeStackScreenProps<
|
|||
disabled={isSelfLabel}
|
||||
style={isSelfLabel ? {opacity: 0.5} : undefined}>
|
||||
<Toggle.Radio />
|
||||
<Toggle.Label>Custom label</Toggle.Label>
|
||||
<Toggle.LabelText>Custom label</Toggle.LabelText>
|
||||
</Toggle.Item>
|
||||
</View>
|
||||
</Toggle.Group>
|
||||
|
@ -358,23 +358,23 @@ export const DebugModScreen = ({}: NativeStackScreenProps<
|
|||
<View style={[a.gap_md, a.flex_row, a.flex_wrap, a.pt_md]}>
|
||||
<Toggle.Item name="targetMe" label="Target is me">
|
||||
<Toggle.Checkbox />
|
||||
<Toggle.Label>Target is me</Toggle.Label>
|
||||
<Toggle.LabelText>Target is me</Toggle.LabelText>
|
||||
</Toggle.Item>
|
||||
<Toggle.Item name="following" label="Following target">
|
||||
<Toggle.Checkbox />
|
||||
<Toggle.Label>Following target</Toggle.Label>
|
||||
<Toggle.LabelText>Following target</Toggle.LabelText>
|
||||
</Toggle.Item>
|
||||
<Toggle.Item name="selfLabel" label="Self label">
|
||||
<Toggle.Checkbox />
|
||||
<Toggle.Label>Self label</Toggle.Label>
|
||||
<Toggle.LabelText>Self label</Toggle.LabelText>
|
||||
</Toggle.Item>
|
||||
<Toggle.Item name="noAdult" label="Adult disabled">
|
||||
<Toggle.Checkbox />
|
||||
<Toggle.Label>Adult disabled</Toggle.Label>
|
||||
<Toggle.LabelText>Adult disabled</Toggle.LabelText>
|
||||
</Toggle.Item>
|
||||
<Toggle.Item name="loggedOut" label="Logged out">
|
||||
<Toggle.Checkbox />
|
||||
<Toggle.Label>Logged out</Toggle.Label>
|
||||
<Toggle.LabelText>Logged out</Toggle.LabelText>
|
||||
</Toggle.Item>
|
||||
</View>
|
||||
</Toggle.Group>
|
||||
|
@ -400,15 +400,15 @@ export const DebugModScreen = ({}: NativeStackScreenProps<
|
|||
]}>
|
||||
<Toggle.Item name="hide" label="Hide">
|
||||
<Toggle.Radio />
|
||||
<Toggle.Label>Hide</Toggle.Label>
|
||||
<Toggle.LabelText>Hide</Toggle.LabelText>
|
||||
</Toggle.Item>
|
||||
<Toggle.Item name="warn" label="Warn">
|
||||
<Toggle.Radio />
|
||||
<Toggle.Label>Warn</Toggle.Label>
|
||||
<Toggle.LabelText>Warn</Toggle.LabelText>
|
||||
</Toggle.Item>
|
||||
<Toggle.Item name="ignore" label="Ignore">
|
||||
<Toggle.Radio />
|
||||
<Toggle.Label>Ignore</Toggle.Label>
|
||||
<Toggle.LabelText>Ignore</Toggle.LabelText>
|
||||
</Toggle.Item>
|
||||
</View>
|
||||
</Toggle.Group>
|
||||
|
@ -446,19 +446,19 @@ export const DebugModScreen = ({}: NativeStackScreenProps<
|
|||
<View style={[a.flex_row, a.gap_md, a.flex_wrap]}>
|
||||
<Toggle.Item name="account" label="Account">
|
||||
<Toggle.Radio />
|
||||
<Toggle.Label>Account</Toggle.Label>
|
||||
<Toggle.LabelText>Account</Toggle.LabelText>
|
||||
</Toggle.Item>
|
||||
<Toggle.Item name="profile" label="Profile">
|
||||
<Toggle.Radio />
|
||||
<Toggle.Label>Profile</Toggle.Label>
|
||||
<Toggle.LabelText>Profile</Toggle.LabelText>
|
||||
</Toggle.Item>
|
||||
<Toggle.Item name="post" label="Post">
|
||||
<Toggle.Radio />
|
||||
<Toggle.Label>Post</Toggle.Label>
|
||||
<Toggle.LabelText>Post</Toggle.LabelText>
|
||||
</Toggle.Item>
|
||||
<Toggle.Item name="embed" label="Embed">
|
||||
<Toggle.Radio />
|
||||
<Toggle.Label>Embed</Toggle.Label>
|
||||
<Toggle.LabelText>Embed</Toggle.LabelText>
|
||||
</Toggle.Item>
|
||||
</View>
|
||||
</Toggle.Group>
|
||||
|
@ -623,15 +623,15 @@ function CustomLabelForm({
|
|||
<View style={[a.flex_row, a.gap_md, a.flex_wrap]}>
|
||||
<Toggle.Item name="content" label="Content">
|
||||
<Toggle.Radio />
|
||||
<Toggle.Label>Content</Toggle.Label>
|
||||
<Toggle.LabelText>Content</Toggle.LabelText>
|
||||
</Toggle.Item>
|
||||
<Toggle.Item name="media" label="Media">
|
||||
<Toggle.Radio />
|
||||
<Toggle.Label>Media</Toggle.Label>
|
||||
<Toggle.LabelText>Media</Toggle.LabelText>
|
||||
</Toggle.Item>
|
||||
<Toggle.Item name="none" label="None">
|
||||
<Toggle.Radio />
|
||||
<Toggle.Label>None</Toggle.Label>
|
||||
<Toggle.LabelText>None</Toggle.LabelText>
|
||||
</Toggle.Item>
|
||||
</View>
|
||||
</Toggle.Group>
|
||||
|
@ -658,15 +658,15 @@ function CustomLabelForm({
|
|||
<View style={[a.flex_row, a.gap_md, a.flex_wrap, a.align_center]}>
|
||||
<Toggle.Item name="alert" label="Alert">
|
||||
<Toggle.Radio />
|
||||
<Toggle.Label>Alert</Toggle.Label>
|
||||
<Toggle.LabelText>Alert</Toggle.LabelText>
|
||||
</Toggle.Item>
|
||||
<Toggle.Item name="inform" label="Inform">
|
||||
<Toggle.Radio />
|
||||
<Toggle.Label>Inform</Toggle.Label>
|
||||
<Toggle.LabelText>Inform</Toggle.LabelText>
|
||||
</Toggle.Item>
|
||||
<Toggle.Item name="none" label="None">
|
||||
<Toggle.Radio />
|
||||
<Toggle.Label>None</Toggle.Label>
|
||||
<Toggle.LabelText>None</Toggle.LabelText>
|
||||
</Toggle.Item>
|
||||
</View>
|
||||
</Toggle.Group>
|
||||
|
|
|
@ -1,70 +1,71 @@
|
|||
import React, {useMemo, useCallback} from 'react'
|
||||
import {StyleSheet, View, Pressable} from 'react-native'
|
||||
import {NativeStackScreenProps} from '@react-navigation/native-stack'
|
||||
import {useIsFocused, useNavigation} from '@react-navigation/native'
|
||||
import {useQueryClient} from '@tanstack/react-query'
|
||||
import {usePalette} from 'lib/hooks/usePalette'
|
||||
import {CommonNavigatorParams} from 'lib/routes/types'
|
||||
import {makeRecordUri} from 'lib/strings/url-helpers'
|
||||
import {s} from 'lib/styles'
|
||||
import {FeedDescriptor} from '#/state/queries/post-feed'
|
||||
import {PagerWithHeader} from 'view/com/pager/PagerWithHeader'
|
||||
import {ProfileSubpageHeader} from 'view/com/profile/ProfileSubpageHeader'
|
||||
import {Feed} from 'view/com/posts/Feed'
|
||||
import {InlineLink} from '#/components/Link'
|
||||
import {ListRef} from 'view/com/util/List'
|
||||
import {Button} from 'view/com/util/forms/Button'
|
||||
import {Text} from 'view/com/util/text/Text'
|
||||
import {RichText} from '#/components/RichText'
|
||||
import {LoadLatestBtn} from 'view/com/util/load-latest/LoadLatestBtn'
|
||||
import {FAB} from 'view/com/util/fab/FAB'
|
||||
import {EmptyState} from 'view/com/util/EmptyState'
|
||||
import {LoadingScreen} from 'view/com/util/LoadingScreen'
|
||||
import * as Toast from 'view/com/util/Toast'
|
||||
import {useSetTitle} from 'lib/hooks/useSetTitle'
|
||||
import {RQKEY as FEED_RQKEY} from '#/state/queries/post-feed'
|
||||
import {shareUrl} from 'lib/sharing'
|
||||
import {toShareUrl} from 'lib/strings/url-helpers'
|
||||
import {Haptics} from 'lib/haptics'
|
||||
import {useAnalytics} from 'lib/analytics/analytics'
|
||||
import {makeCustomFeedLink} from 'lib/routes/links'
|
||||
import {pluralize} from 'lib/strings/helpers'
|
||||
import {CenteredView} from 'view/com/util/Views'
|
||||
import {NavigationProp} from 'lib/routes/types'
|
||||
import {ComposeIcon2} from 'lib/icons'
|
||||
import {logger} from '#/logger'
|
||||
import {Trans, msg} from '@lingui/macro'
|
||||
import React, {useCallback, useMemo} from 'react'
|
||||
import {Pressable, StyleSheet, View} from 'react-native'
|
||||
import {msg, Trans} from '@lingui/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
import {ReportDialog, useReportDialogControl} from '#/components/ReportDialog'
|
||||
import {useFeedSourceInfoQuery, FeedSourceFeedInfo} from '#/state/queries/feed'
|
||||
import {useResolveUriQuery} from '#/state/queries/resolve-uri'
|
||||
import {
|
||||
UsePreferencesQueryResponse,
|
||||
usePreferencesQuery,
|
||||
useSaveFeedMutation,
|
||||
useRemoveFeedMutation,
|
||||
usePinFeedMutation,
|
||||
useUnpinFeedMutation,
|
||||
} from '#/state/queries/preferences'
|
||||
import {useSession} from '#/state/session'
|
||||
import {useLikeMutation, useUnlikeMutation} from '#/state/queries/like'
|
||||
import {useComposerControls} from '#/state/shell/composer'
|
||||
import {truncateAndInvalidate} from '#/state/queries/util'
|
||||
import {useIsFocused, useNavigation} from '@react-navigation/native'
|
||||
import {NativeStackScreenProps} from '@react-navigation/native-stack'
|
||||
import {useQueryClient} from '@tanstack/react-query'
|
||||
|
||||
import {HITSLOP_20} from '#/lib/constants'
|
||||
import {logger} from '#/logger'
|
||||
import {isNative} from '#/platform/detection'
|
||||
import {listenSoftReset} from '#/state/events'
|
||||
import {atoms as a, useTheme} from '#/alf'
|
||||
import * as Menu from '#/components/Menu'
|
||||
import {HITSLOP_20} from '#/lib/constants'
|
||||
import {DotGrid_Stroke2_Corner0_Rounded as Ellipsis} from '#/components/icons/DotGrid'
|
||||
import {Trash_Stroke2_Corner0_Rounded as Trash} from '#/components/icons/Trash'
|
||||
import {PlusLarge_Stroke2_Corner0_Rounded as Plus} from '#/components/icons/Plus'
|
||||
import {CircleInfo_Stroke2_Corner0_Rounded as CircleInfo} from '#/components/icons/CircleInfo'
|
||||
import {ArrowOutOfBox_Stroke2_Corner0_Rounded as Share} from '#/components/icons/ArrowOutOfBox'
|
||||
import {FeedSourceFeedInfo, useFeedSourceInfoQuery} from '#/state/queries/feed'
|
||||
import {useLikeMutation, useUnlikeMutation} from '#/state/queries/like'
|
||||
import {FeedDescriptor} from '#/state/queries/post-feed'
|
||||
import {RQKEY as FEED_RQKEY} from '#/state/queries/post-feed'
|
||||
import {
|
||||
Heart2_Stroke2_Corner0_Rounded as HeartOutline,
|
||||
Heart2_Filled_Stroke2_Corner0_Rounded as HeartFilled,
|
||||
} from '#/components/icons/Heart2'
|
||||
usePinFeedMutation,
|
||||
usePreferencesQuery,
|
||||
UsePreferencesQueryResponse,
|
||||
useRemoveFeedMutation,
|
||||
useSaveFeedMutation,
|
||||
useUnpinFeedMutation,
|
||||
} from '#/state/queries/preferences'
|
||||
import {useResolveUriQuery} from '#/state/queries/resolve-uri'
|
||||
import {truncateAndInvalidate} from '#/state/queries/util'
|
||||
import {useSession} from '#/state/session'
|
||||
import {useComposerControls} from '#/state/shell/composer'
|
||||
import {useAnalytics} from 'lib/analytics/analytics'
|
||||
import {Haptics} from 'lib/haptics'
|
||||
import {usePalette} from 'lib/hooks/usePalette'
|
||||
import {useSetTitle} from 'lib/hooks/useSetTitle'
|
||||
import {ComposeIcon2} from 'lib/icons'
|
||||
import {makeCustomFeedLink} from 'lib/routes/links'
|
||||
import {CommonNavigatorParams} from 'lib/routes/types'
|
||||
import {NavigationProp} from 'lib/routes/types'
|
||||
import {shareUrl} from 'lib/sharing'
|
||||
import {pluralize} from 'lib/strings/helpers'
|
||||
import {makeRecordUri} from 'lib/strings/url-helpers'
|
||||
import {toShareUrl} from 'lib/strings/url-helpers'
|
||||
import {s} from 'lib/styles'
|
||||
import {PagerWithHeader} from 'view/com/pager/PagerWithHeader'
|
||||
import {Feed} from 'view/com/posts/Feed'
|
||||
import {ProfileSubpageHeader} from 'view/com/profile/ProfileSubpageHeader'
|
||||
import {EmptyState} from 'view/com/util/EmptyState'
|
||||
import {FAB} from 'view/com/util/fab/FAB'
|
||||
import {Button} from 'view/com/util/forms/Button'
|
||||
import {ListRef} from 'view/com/util/List'
|
||||
import {LoadLatestBtn} from 'view/com/util/load-latest/LoadLatestBtn'
|
||||
import {LoadingScreen} from 'view/com/util/LoadingScreen'
|
||||
import {Text} from 'view/com/util/text/Text'
|
||||
import * as Toast from 'view/com/util/Toast'
|
||||
import {CenteredView} from 'view/com/util/Views'
|
||||
import {atoms as a, useTheme} from '#/alf'
|
||||
import {Button as NewButton, ButtonText} from '#/components/Button'
|
||||
import {ArrowOutOfBox_Stroke2_Corner0_Rounded as Share} from '#/components/icons/ArrowOutOfBox'
|
||||
import {CircleInfo_Stroke2_Corner0_Rounded as CircleInfo} from '#/components/icons/CircleInfo'
|
||||
import {DotGrid_Stroke2_Corner0_Rounded as Ellipsis} from '#/components/icons/DotGrid'
|
||||
import {
|
||||
Heart2_Filled_Stroke2_Corner0_Rounded as HeartFilled,
|
||||
Heart2_Stroke2_Corner0_Rounded as HeartOutline,
|
||||
} from '#/components/icons/Heart2'
|
||||
import {PlusLarge_Stroke2_Corner0_Rounded as Plus} from '#/components/icons/Plus'
|
||||
import {Trash_Stroke2_Corner0_Rounded as Trash} from '#/components/icons/Trash'
|
||||
import {InlineLinkText} from '#/components/Link'
|
||||
import * as Menu from '#/components/Menu'
|
||||
import {ReportDialog, useReportDialogControl} from '#/components/ReportDialog'
|
||||
import {RichText} from '#/components/RichText'
|
||||
|
||||
const SECTION_TITLES = ['Posts']
|
||||
|
||||
|
@ -580,12 +581,12 @@ function AboutSection({
|
|||
)}
|
||||
</NewButton>
|
||||
{typeof likeCount === 'number' && (
|
||||
<InlineLink
|
||||
<InlineLinkText
|
||||
label={_(msg`View users who like this feed`)}
|
||||
to={makeCustomFeedLink(feedOwnerDid, feedRkey, 'liked-by')}
|
||||
style={[t.atoms.text_contrast_medium, a.font_bold]}>
|
||||
{_(msg`Liked by ${likeCount} ${pluralize(likeCount, 'user')}`)}
|
||||
</InlineLink>
|
||||
</InlineLinkText>
|
||||
)}
|
||||
</View>
|
||||
</View>
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
import React from 'react'
|
||||
import {View} from 'react-native'
|
||||
import {msg, Trans} from '@lingui/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
import {Trans, msg} from '@lingui/macro'
|
||||
|
||||
import {atoms as a, useBreakpoints, useTheme} from '#/alf'
|
||||
import * as Dialog from '#/components/Dialog'
|
||||
import {Text, P} from '#/components/Typography'
|
||||
import {Button, ButtonText} from '#/components/Button'
|
||||
import {InlineLink, Link} from '#/components/Link'
|
||||
import {getAgent, useSession} from '#/state/session'
|
||||
import {atoms as a, useBreakpoints, useTheme} from '#/alf'
|
||||
import {Button, ButtonText} from '#/components/Button'
|
||||
import * as Dialog from '#/components/Dialog'
|
||||
import {InlineLinkText, Link} from '#/components/Link'
|
||||
import {P, Text} from '#/components/Typography'
|
||||
|
||||
export function ExportCarDialog({
|
||||
control,
|
||||
|
@ -75,11 +75,11 @@ export function ExportCarDialog({
|
|||
<Trans>
|
||||
This feature is in beta. You can read more about repository
|
||||
exports in{' '}
|
||||
<InlineLink
|
||||
<InlineLinkText
|
||||
to="https://docs.bsky.app/blog/repo-export"
|
||||
style={[a.text_sm]}>
|
||||
this blogpost
|
||||
</InlineLink>
|
||||
</InlineLinkText>
|
||||
.
|
||||
</Trans>
|
||||
</P>
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
import React from 'react'
|
||||
import {View} from 'react-native'
|
||||
|
||||
import {useDialogStateControlContext} from '#/state/dialogs'
|
||||
import {atoms as a} from '#/alf'
|
||||
import {Button} from '#/components/Button'
|
||||
import {H3, P} from '#/components/Typography'
|
||||
import * as Dialog from '#/components/Dialog'
|
||||
import * as Prompt from '#/components/Prompt'
|
||||
import {useDialogStateControlContext} from '#/state/dialogs'
|
||||
import {H3, P} from '#/components/Typography'
|
||||
|
||||
export function Dialogs() {
|
||||
const scrollable = Dialog.useDialogControl()
|
||||
|
@ -61,11 +61,11 @@ export function Dialogs() {
|
|||
</Button>
|
||||
|
||||
<Prompt.Outer control={prompt}>
|
||||
<Prompt.Title>This is a prompt</Prompt.Title>
|
||||
<Prompt.Description>
|
||||
<Prompt.TitleText>This is a prompt</Prompt.TitleText>
|
||||
<Prompt.DescriptionText>
|
||||
This is a generic prompt component. It accepts a title and a
|
||||
description, as well as two actions.
|
||||
</Prompt.Description>
|
||||
</Prompt.DescriptionText>
|
||||
<Prompt.Actions>
|
||||
<Prompt.Cancel>Cancel</Prompt.Cancel>
|
||||
<Prompt.Action onPress={() => {}}>Confirm</Prompt.Action>
|
||||
|
|
|
@ -2,13 +2,13 @@ import React from 'react'
|
|||
import {View} from 'react-native'
|
||||
|
||||
import {atoms as a} from '#/alf'
|
||||
import {H1, H3} from '#/components/Typography'
|
||||
import {Button} from '#/components/Button'
|
||||
import {DateField, LabelText} from '#/components/forms/DateField'
|
||||
import * as TextField from '#/components/forms/TextField'
|
||||
import {DateField, Label} from '#/components/forms/DateField'
|
||||
import * as Toggle from '#/components/forms/Toggle'
|
||||
import * as ToggleButton from '#/components/forms/ToggleButton'
|
||||
import {Button} from '#/components/Button'
|
||||
import {Globe_Stroke2_Corner0_Rounded as Globe} from '#/components/icons/Globe'
|
||||
import {H1, H3} from '#/components/Typography'
|
||||
|
||||
export function Forms() {
|
||||
const [toggleGroupAValues, setToggleGroupAValues] = React.useState(['a'])
|
||||
|
@ -42,7 +42,7 @@ export function Forms() {
|
|||
</TextField.Root>
|
||||
|
||||
<View style={[a.w_full]}>
|
||||
<TextField.Label>Text field</TextField.Label>
|
||||
<TextField.LabelText>Text field</TextField.LabelText>
|
||||
<TextField.Root>
|
||||
<TextField.Icon icon={Globe} />
|
||||
<TextField.Input
|
||||
|
@ -50,12 +50,14 @@ export function Forms() {
|
|||
onChangeText={setValue}
|
||||
label="Text field"
|
||||
/>
|
||||
<TextField.Suffix label="@gmail.com">@gmail.com</TextField.Suffix>
|
||||
<TextField.SuffixText label="@gmail.com">
|
||||
@gmail.com
|
||||
</TextField.SuffixText>
|
||||
</TextField.Root>
|
||||
</View>
|
||||
|
||||
<View style={[a.w_full]}>
|
||||
<TextField.Label>Textarea</TextField.Label>
|
||||
<TextField.LabelText>Textarea</TextField.LabelText>
|
||||
<TextField.Input
|
||||
multiline
|
||||
numberOfLines={4}
|
||||
|
@ -68,7 +70,7 @@ export function Forms() {
|
|||
<H3>DateField</H3>
|
||||
|
||||
<View style={[a.w_full]}>
|
||||
<Label>Date</Label>
|
||||
<LabelText>Date</LabelText>
|
||||
<DateField
|
||||
testID="date"
|
||||
value={date}
|
||||
|
@ -86,7 +88,7 @@ export function Forms() {
|
|||
|
||||
<Toggle.Item name="a" label="Click me">
|
||||
<Toggle.Checkbox />
|
||||
<Toggle.Label>Uncontrolled toggle</Toggle.Label>
|
||||
<Toggle.LabelText>Uncontrolled toggle</Toggle.LabelText>
|
||||
</Toggle.Item>
|
||||
|
||||
<Toggle.Group
|
||||
|
@ -98,23 +100,23 @@ export function Forms() {
|
|||
<View style={[a.gap_md]}>
|
||||
<Toggle.Item name="a" label="Click me">
|
||||
<Toggle.Switch />
|
||||
<Toggle.Label>Click me</Toggle.Label>
|
||||
<Toggle.LabelText>Click me</Toggle.LabelText>
|
||||
</Toggle.Item>
|
||||
<Toggle.Item name="b" label="Click me">
|
||||
<Toggle.Switch />
|
||||
<Toggle.Label>Click me</Toggle.Label>
|
||||
<Toggle.LabelText>Click me</Toggle.LabelText>
|
||||
</Toggle.Item>
|
||||
<Toggle.Item name="c" label="Click me">
|
||||
<Toggle.Switch />
|
||||
<Toggle.Label>Click me</Toggle.Label>
|
||||
<Toggle.LabelText>Click me</Toggle.LabelText>
|
||||
</Toggle.Item>
|
||||
<Toggle.Item name="d" disabled label="Click me">
|
||||
<Toggle.Switch />
|
||||
<Toggle.Label>Click me</Toggle.Label>
|
||||
<Toggle.LabelText>Click me</Toggle.LabelText>
|
||||
</Toggle.Item>
|
||||
<Toggle.Item name="e" isInvalid label="Click me">
|
||||
<Toggle.Switch />
|
||||
<Toggle.Label>Click me</Toggle.Label>
|
||||
<Toggle.LabelText>Click me</Toggle.LabelText>
|
||||
</Toggle.Item>
|
||||
</View>
|
||||
</Toggle.Group>
|
||||
|
@ -128,23 +130,23 @@ export function Forms() {
|
|||
<View style={[a.gap_md]}>
|
||||
<Toggle.Item name="a" label="Click me">
|
||||
<Toggle.Checkbox />
|
||||
<Toggle.Label>Click me</Toggle.Label>
|
||||
<Toggle.LabelText>Click me</Toggle.LabelText>
|
||||
</Toggle.Item>
|
||||
<Toggle.Item name="b" label="Click me">
|
||||
<Toggle.Checkbox />
|
||||
<Toggle.Label>Click me</Toggle.Label>
|
||||
<Toggle.LabelText>Click me</Toggle.LabelText>
|
||||
</Toggle.Item>
|
||||
<Toggle.Item name="c" label="Click me">
|
||||
<Toggle.Checkbox />
|
||||
<Toggle.Label>Click me</Toggle.Label>
|
||||
<Toggle.LabelText>Click me</Toggle.LabelText>
|
||||
</Toggle.Item>
|
||||
<Toggle.Item name="d" disabled label="Click me">
|
||||
<Toggle.Checkbox />
|
||||
<Toggle.Label>Click me</Toggle.Label>
|
||||
<Toggle.LabelText>Click me</Toggle.LabelText>
|
||||
</Toggle.Item>
|
||||
<Toggle.Item name="e" isInvalid label="Click me">
|
||||
<Toggle.Checkbox />
|
||||
<Toggle.Label>Click me</Toggle.Label>
|
||||
<Toggle.LabelText>Click me</Toggle.LabelText>
|
||||
</Toggle.Item>
|
||||
</View>
|
||||
</Toggle.Group>
|
||||
|
@ -157,23 +159,23 @@ export function Forms() {
|
|||
<View style={[a.gap_md]}>
|
||||
<Toggle.Item name="a" label="Click me">
|
||||
<Toggle.Radio />
|
||||
<Toggle.Label>Click me</Toggle.Label>
|
||||
<Toggle.LabelText>Click me</Toggle.LabelText>
|
||||
</Toggle.Item>
|
||||
<Toggle.Item name="b" label="Click me">
|
||||
<Toggle.Radio />
|
||||
<Toggle.Label>Click me</Toggle.Label>
|
||||
<Toggle.LabelText>Click me</Toggle.LabelText>
|
||||
</Toggle.Item>
|
||||
<Toggle.Item name="c" label="Click me">
|
||||
<Toggle.Radio />
|
||||
<Toggle.Label>Click me</Toggle.Label>
|
||||
<Toggle.LabelText>Click me</Toggle.LabelText>
|
||||
</Toggle.Item>
|
||||
<Toggle.Item name="d" disabled label="Click me">
|
||||
<Toggle.Radio />
|
||||
<Toggle.Label>Click me</Toggle.Label>
|
||||
<Toggle.LabelText>Click me</Toggle.LabelText>
|
||||
</Toggle.Item>
|
||||
<Toggle.Item name="e" isInvalid label="Click me">
|
||||
<Toggle.Radio />
|
||||
<Toggle.Label>Click me</Toggle.Label>
|
||||
<Toggle.LabelText>Click me</Toggle.LabelText>
|
||||
</Toggle.Item>
|
||||
</View>
|
||||
</Toggle.Group>
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import React from 'react'
|
||||
import {View} from 'react-native'
|
||||
|
||||
import {useTheme, atoms as a} from '#/alf'
|
||||
import {atoms as a, useTheme} from '#/alf'
|
||||
import {ButtonText} from '#/components/Button'
|
||||
import {InlineLink, Link} from '#/components/Link'
|
||||
import {InlineLinkText, Link} from '#/components/Link'
|
||||
import {H1, Text} from '#/components/Typography'
|
||||
|
||||
export function Links() {
|
||||
|
@ -13,20 +13,22 @@ export function Links() {
|
|||
<H1>Links</H1>
|
||||
|
||||
<View style={[a.gap_md, a.align_start]}>
|
||||
<InlineLink to="https://google.com" style={[a.text_lg]}>
|
||||
<InlineLinkText to="https://google.com" style={[a.text_lg]}>
|
||||
https://google.com
|
||||
</InlineLink>
|
||||
<InlineLink to="https://google.com" style={[a.text_lg]}>
|
||||
</InlineLinkText>
|
||||
<InlineLinkText to="https://google.com" style={[a.text_lg]}>
|
||||
External with custom children (google.com)
|
||||
</InlineLink>
|
||||
<InlineLink
|
||||
</InlineLinkText>
|
||||
<InlineLinkText
|
||||
to="https://bsky.social"
|
||||
style={[a.text_md, t.atoms.text_contrast_low]}>
|
||||
Internal (bsky.social)
|
||||
</InlineLink>
|
||||
<InlineLink to="https://bsky.app/profile/bsky.app" style={[a.text_md]}>
|
||||
</InlineLinkText>
|
||||
<InlineLinkText
|
||||
to="https://bsky.app/profile/bsky.app"
|
||||
style={[a.text_md]}>
|
||||
Internal (bsky.app)
|
||||
</InlineLink>
|
||||
</InlineLinkText>
|
||||
|
||||
<Link
|
||||
variant="solid"
|
||||
|
|
Loading…
Reference in New Issue