Merge remote-tracking branch 'origin/main' into next/base

* origin/main: (39 commits)
  Revert "fix quote & feed padding not being pressable (#7694)" (#7733)
  Nightly source-language update
  Add dev mode for easy copying of at:// URIs and DIDs (#7723)
  Improved search language select (#7591)
  Improved search page (#7590)
  Animate drawer menu on mobile web (#7711)
  Special treatment for recommended starter packs (#7706)
  invert new postonboarding gate (#7695)
  Animate dropdown menus (#7704)
  Better animations for dialogs, animate web composer (#7703)
  fix quote & feed padding not being pressable (#7694)
  ungate trending (#7696)
  run prettier (#7727)
  Nightly source-language update
  Add deploy key to nightly commit (#7722)
  [APP-1031] Add new followerRule to threadgate settings (#7681)
  Fix bug in GH action nightly-update-source-languages.yaml
  More i18n process updates (#7720)
  Run intl:extract on english to see if crowdin picks up on the changes
  Refine accessibilityHint (#7554)
  ...
This commit is contained in:
Eric Bailey
2025-02-13 11:19:59 -06:00
97 changed files with 14126 additions and 4505 deletions
+10 -14
View File
@@ -15,6 +15,7 @@ import {FocusScope} from '@radix-ui/react-focus-scope'
import {RemoveScrollBar} from 'react-remove-scroll-bar'
import {logger} from '#/logger'
import {useA11y} from '#/state/a11y'
import {useDialogStateControlContext} from '#/state/dialogs'
import {atoms as a, flatten, useBreakpoints, useTheme, web} from '#/alf'
import {Button, ButtonIcon} from '#/components/Button'
@@ -152,6 +153,7 @@ export function Inner({
const t = useTheme()
const {close} = React.useContext(Context)
const {gtMobile} = useBreakpoints()
const {reduceMotionEnabled} = useA11y()
useFocusGuards()
return (
<FocusScope loop asChild trapped>
@@ -161,7 +163,7 @@ export function Inner({
aria-label={label}
aria-labelledby={accessibilityLabelledBy}
aria-describedby={accessibilityDescribedBy}
// @ts-ignore web only -prf
// @ts-expect-error web only -prf
onClick={stopPropagation}
onStartShouldSetResponder={_ => true}
onTouchEnd={stopPropagation}
@@ -177,10 +179,9 @@ export function Inner({
shadowColor: t.palette.black,
shadowOpacity: t.name === 'light' ? 0.1 : 0.4,
shadowRadius: 30,
// @ts-ignore web only
animation: 'fadeIn ease-out 0.1s',
},
flatten(style),
!reduceMotionEnabled && a.zoom_fade_in,
style,
])}>
<DismissableLayer
onInteractOutside={preventDefault}
@@ -216,7 +217,7 @@ export const InnerFlatList = React.forwardRef<
style={[
a.overflow_hidden,
a.px_0,
// @ts-ignore web only -sfn
// @ts-expect-error web only -sfn
{maxHeight: 'calc(-36px + 100vh)'},
webInnerStyle,
]}
@@ -262,20 +263,15 @@ export function Handle() {
function Backdrop() {
const t = useTheme()
const {reduceMotionEnabled} = useA11y()
return (
<View
style={{
opacity: 0.8,
}}>
<View style={{opacity: 0.8}}>
<View
style={[
a.fixed,
a.inset_0,
{
backgroundColor: t.palette.black,
// @ts-ignore web only
animation: 'fadeIn ease-out 0.15s',
},
{backgroundColor: t.palette.black},
!reduceMotionEnabled && a.fade_in,
]}
/>
</View>
+2 -1
View File
@@ -1,5 +1,5 @@
import {createContext, useCallback, useContext} from 'react'
import {GestureResponderEvent, View} from 'react-native'
import {GestureResponderEvent, Keyboard, View} from 'react-native'
import {msg} from '@lingui/macro'
import {useLingui} from '@lingui/react'
import {useNavigation} from '@react-navigation/native'
@@ -140,6 +140,7 @@ export function MenuButton() {
const {gtMobile} = useBreakpoints()
const onPress = useCallback(() => {
Keyboard.dismiss()
setDrawerOpen(true)
}, [setDrawerOpen])
+12 -4
View File
@@ -4,6 +4,7 @@ import {msg} from '@lingui/macro'
import {useLingui} from '@lingui/react'
import * as DropdownMenu from '@radix-ui/react-dropdown-menu'
import {useA11y} from '#/state/a11y'
import {atoms as a, flatten, useTheme, web} from '#/alf'
import * as Dialog from '#/components/Dialog'
import {useInteractionState} from '#/components/hooks/useInteractionState'
@@ -177,10 +178,16 @@ export function Outer({
style?: StyleProp<ViewStyle>
}>) {
const t = useTheme()
const {reduceMotionEnabled} = useA11y()
return (
<DropdownMenu.Portal>
<DropdownMenu.Content sideOffset={5} loop aria-label="Test">
<DropdownMenu.Content
sideOffset={5}
collisionPadding={{left: 5, right: 5, bottom: 5}}
loop
aria-label="Test"
className="dropdown-menu-transform-origin">
<View
style={[
a.rounded_sm,
@@ -189,6 +196,8 @@ export function Outer({
t.name === 'light' ? t.atoms.bg : t.atoms.bg_contrast_25,
t.atoms.shadow_md,
t.atoms.border_contrast_low,
a.overflow_auto,
!reduceMotionEnabled && a.zoom_fade_in,
style,
]}>
{children}
@@ -373,9 +382,8 @@ export function Divider() {
style={flatten([
a.my_xs,
t.atoms.bg_contrast_100,
{
height: 1,
},
a.flex_shrink_0,
{height: 1},
])}
/>
)
@@ -762,7 +762,7 @@ function SearchInput({
autoComplete="off"
autoCapitalize="none"
accessibilityLabel={_(msg`Search profiles`)}
accessibilityHint={_(msg`Search profiles`)}
accessibilityHint={_(msg`Searches for profiles`)}
/>
</View>
)
@@ -12,7 +12,7 @@ import {precacheResolvedUri} from '#/state/queries/resolve-uri'
import {precacheStarterPack} from '#/state/queries/starter-packs'
import {useSession} from '#/state/session'
import {atoms as a, useTheme} from '#/alf'
import {StarterPack} from '#/components/icons/StarterPack'
import {StarterPack as StarterPackIcon} from '#/components/icons/StarterPack'
import {Link as BaseLink, LinkProps as BaseLinkProps} from '#/components/Link'
import {Text} from '#/components/Typography'
import * as bsky from '#/types/bsky'
@@ -70,7 +70,7 @@ export function Card({
return (
<View style={[a.w_full, a.gap_md]}>
<View style={[a.flex_row, a.gap_sm, a.w_full]}>
{!noIcon ? <StarterPack width={40} gradient="sky" /> : null}
{!noIcon ? <StarterPackIcon width={40} gradient="sky" /> : null}
<View style={[a.flex_1]}>
<Text
emoji
+50 -40
View File
@@ -12,6 +12,7 @@ import {PressableScale} from '#/lib/custom-animations/PressableScale'
// import {UserAvatar} from '#/view/com/util/UserAvatar'
import type {TrendingTopic} from '#/state/queries/trending/useTrendingTopics'
import {atoms as a, native, useTheme, ViewStyleProp} from '#/alf'
import {StarterPack as StarterPackIcon} from '#/components/icons/StarterPack'
import {Link as InternalLink, LinkProps} from '#/components/Link'
import {Text} from '#/components/Typography'
@@ -24,9 +25,8 @@ export function TrendingTopic({
const topic = useTopic(raw)
const isSmall = size === 'small'
// const hasAvi = topic.type === 'feed' || topic.type === 'profile'
// const aviSize = isSmall ? 16 : 20
// const iconSize = isSmall ? 16 : 20
const hasIcon = topic.type === 'starter-pack' && !isSmall
const iconSize = 20
return (
<View
@@ -45,45 +45,47 @@ export function TrendingTopic({
},
]
: [a.py_sm, a.px_md],
hasIcon && {gap: 6},
style,
/*
{
padding: 6,
gap: hasAvi ? 4 : 2,
},
a.pr_md,
*/
]}>
{hasIcon && topic.type === 'starter-pack' && (
<StarterPackIcon
gradient="sky"
width={iconSize}
style={{marginLeft: -3, marginVertical: -1}}
/>
)}
{/*
<View
style={[
a.align_center,
a.justify_center,
a.rounded_full,
a.overflow_hidden,
{
width: aviSize,
height: aviSize,
},
]}>
{topic.type === 'tag' ? (
<Hashtag width={iconSize} />
) : topic.type === 'topic' ? (
<Quote width={iconSize - 2} />
) : topic.type === 'feed' ? (
<UserAvatar
type="user"
size={aviSize}
avatar=""
/>
) : (
<UserAvatar
type="user"
size={aviSize}
avatar=""
/>
)}
</View>
<View
style={[
a.align_center,
a.justify_center,
a.rounded_full,
a.overflow_hidden,
{
width: iconSize,
height: iconSize,
},
]}>
{topic.type === 'tag' ? (
<Hashtag width={iconSize} />
) : topic.type === 'topic' ? (
<Quote width={iconSize - 2} />
) : topic.type === 'feed' ? (
<UserAvatar
type="user"
size={aviSize}
avatar=""
/>
) : (
<UserAvatar
type="user"
size={aviSize}
avatar=""
/>
)}
</View>
*/}
<Text
@@ -151,7 +153,7 @@ export function TrendingTopicLink({
type ParsedTrendingTopic =
| {
type: 'topic' | 'tag' | 'unknown'
type: 'topic' | 'tag' | 'starter-pack' | 'unknown'
label: string
displayName: string
url: string
@@ -187,6 +189,14 @@ export function useTopic(raw: TrendingTopic): ParsedTrendingTopic {
uri: undefined,
url: link,
}
} else if (link.startsWith('/starter-pack')) {
return {
type: 'starter-pack',
label: _(msg`Browse starter pack ${displayName}`),
displayName,
uri: undefined,
url: link,
}
}
/*
+1 -1
View File
@@ -118,7 +118,7 @@ export function VideoPostCard({
return (
<Link
accessibilityHint={_(msg`Tap to view video in immersive mode.`)}
accessibilityHint={_(msg`Views video in immersive mode`)}
label={_(msg`Video from ${author.handle}: ${text}`)}
to={{
screen: 'VideoFeed',
+19 -1
View File
@@ -213,7 +213,12 @@ function Rules({
a.flex_wrap,
t.atoms.text_contrast_medium,
]}>
{settings[0].type === 'everybody' ? (
{settings.length === 0 ? (
<Trans>
This post has an unknown type of threadgate on it. Your app may be
out of date.
</Trans>
) : settings[0].type === 'everybody' ? (
<Trans>Everybody can reply to this post.</Trans>
) : settings[0].type === 'nobody' ? (
<Trans>Replies to this post are disabled.</Trans>
@@ -257,6 +262,19 @@ function Rule({
if (rule.type === 'mention') {
return <Trans>mentioned users</Trans>
}
if (rule.type === 'followers') {
return (
<Trans>
users following{' '}
<InlineLinkText
label={`@${post.author.handle}`}
to={makeProfileLink(post.author)}
style={[a.text_sm, a.leading_snug]}>
@{post.author.handle}
</InlineLinkText>
</Trans>
)
}
if (rule.type === 'following') {
return (
<Trans>
@@ -40,6 +40,7 @@ import {Loader} from '#/components/Loader'
import {Text} from '#/components/Typography'
export type PostInteractionSettingsFormProps = {
canSave?: boolean
onSave: () => void
isSaving?: boolean
@@ -58,20 +59,53 @@ export function PostInteractionSettingsControlledDialog({
}: PostInteractionSettingsFormProps & {
control: Dialog.DialogControlProps
}) {
const t = useTheme()
const {_} = useLingui()
return (
<Dialog.Outer control={control}>
<Dialog.Handle />
<Dialog.ScrollableInner
label={_(msg`Edit post interaction settings`)}
style={[{maxWidth: 500}, a.w_full]}>
<PostInteractionSettingsForm {...rest} />
<View style={[a.gap_md]}>
<Header />
<PostInteractionSettingsForm {...rest} />
<Text
style={[
a.pt_sm,
a.text_sm,
a.leading_snug,
t.atoms.text_contrast_medium,
]}>
<Trans>
You can set default interaction settings in{' '}
<Text style={[a.font_bold, t.atoms.text_contrast_medium]}>
Settings &rarr; Moderation &rarr; Interaction settings.
</Text>
</Trans>
</Text>
</View>
<Dialog.Close />
</Dialog.ScrollableInner>
</Dialog.Outer>
)
}
export function Header() {
return (
<View style={[a.gap_md, a.pb_sm]}>
<Text style={[a.text_2xl, a.font_bold]}>
<Trans>Post interaction settings</Trans>
</Text>
<Text style={[a.text_md, a.pb_xs]}>
<Trans>Customize who can interact with this post.</Trans>
</Text>
<Divider />
</View>
)
}
export type PostInteractionSettingsDialogProps = {
control: Dialog.DialogControlProps
/**
@@ -203,26 +237,31 @@ export function PostInteractionSettingsDialogControlledInner(
<Dialog.ScrollableInner
label={_(msg`Edit post interaction settings`)}
style={[{maxWidth: 500}, a.w_full]}>
{isLoading ? (
<View style={[a.flex_1, a.py_4xl, a.align_center, a.justify_center]}>
<Loader size="xl" />
</View>
) : (
<PostInteractionSettingsForm
replySettingsDisabled={!isThreadgateOwnedByViewer}
isSaving={isSaving}
onSave={onSave}
postgate={postgateValue}
onChangePostgate={setEditedPostgate}
threadgateAllowUISettings={allowUIValue}
onChangeThreadgateAllowUISettings={setEditedAllowUISettings}
/>
)}
<View style={[a.gap_md]}>
<Header />
{isLoading ? (
<View style={[a.flex_1, a.py_4xl, a.align_center, a.justify_center]}>
<Loader size="xl" />
</View>
) : (
<PostInteractionSettingsForm
replySettingsDisabled={!isThreadgateOwnedByViewer}
isSaving={isSaving}
onSave={onSave}
postgate={postgateValue}
onChangePostgate={setEditedPostgate}
threadgateAllowUISettings={allowUIValue}
onChangeThreadgateAllowUISettings={setEditedAllowUISettings}
/>
)}
</View>
</Dialog.ScrollableInner>
)
}
export function PostInteractionSettingsForm({
canSave = true,
onSave,
isSaving,
postgate,
@@ -283,17 +322,7 @@ export function PostInteractionSettingsForm({
return (
<View>
<View style={[a.flex_1, a.gap_md]}>
<Text style={[a.text_2xl, a.font_bold]}>
<Trans>Post interaction settings</Trans>
</Text>
<View style={[a.gap_lg]}>
<Text style={[a.text_md]}>
<Trans>Customize who can interact with this post.</Trans>
</Text>
<Divider />
<View style={[a.gap_sm]}>
<Text style={[a.font_bold, a.text_lg]}>
<Trans>Quote settings</Trans>
@@ -400,7 +429,7 @@ export function PostInteractionSettingsForm({
disabled={replySettingsDisabled}
/>
<Selectable
label={_(msg`Followed users`)}
label={_(msg`Users you follow`)}
isSelected={
!!threadgateAllowUISettings.find(
v => v.type === 'following',
@@ -409,6 +438,16 @@ export function PostInteractionSettingsForm({
onPress={() => onPressAudience({type: 'following'})}
disabled={replySettingsDisabled}
/>
<Selectable
label={_(msg`Your followers`)}
isSelected={
!!threadgateAllowUISettings.find(
v => v.type === 'followers',
)
}
onPress={() => onPressAudience({type: 'followers'})}
disabled={replySettingsDisabled}
/>
{lists && lists.length > 0
? lists.map(list => (
<Selectable
@@ -435,6 +474,7 @@ export function PostInteractionSettingsForm({
</View>
<Button
disabled={!canSave || isSaving}
label={_(msg`Save`)}
onPress={onSave}
color="primary"
@@ -506,7 +506,7 @@ function SearchInput({
autoCapitalize="none"
autoFocus
accessibilityLabel={_(msg`Search profiles`)}
accessibilityHint={_(msg`Search profiles`)}
accessibilityHint={_(msg`Searches for profiles`)}
/>
</View>
)
+2 -2
View File
@@ -47,7 +47,7 @@ export function HostingProvider({
</Text>
<Button
label={toNiceDomain(serviceUrl)}
accessibilityHint={_(msg`Press to change hosting provider`)}
accessibilityHint={_(msg`Changes hosting provider`)}
onPress={onPressSelectService}
variant="ghost"
color="secondary"
@@ -63,7 +63,7 @@ export function HostingProvider({
<Button
testID="selectServiceButton"
label={toNiceDomain(serviceUrl)}
accessibilityHint={_(msg`Press to change hosting provider`)}
accessibilityHint={_(msg`Changes hosting provider`)}
variant="solid"
color="secondary"
style={[
+1 -1
View File
@@ -226,6 +226,7 @@ export function createInput(Component: typeof TextInput) {
<>
<Component
accessibilityHint={undefined}
hitSlop={HITSLOP_20}
{...rest}
accessibilityLabel={label}
ref={refs}
@@ -242,7 +243,6 @@ export function createInput(Component: typeof TextInput) {
placeholder={placeholder || label}
placeholderTextColor={t.palette.contrast_500}
keyboardAppearance={t.name === 'light' ? 'light' : 'dark'}
hitSlop={HITSLOP_20}
style={flattened}
/>
+1
View File
@@ -65,6 +65,7 @@ const locales: Record<AppLanguage, Locale | undefined> = {
gl,
hi,
hu,
ia: undefined,
id,
it,
ja,
+4
View File
@@ -15,3 +15,7 @@ export const ChevronTop_Stroke2_Corner0_Rounded = createSinglePathSVG({
export const ChevronBottom_Stroke2_Corner0_Rounded = createSinglePathSVG({
path: 'M3.293 8.293a1 1 0 0 1 1.414 0L12 15.586l7.293-7.293a1 1 0 1 1 1.414 1.414l-8 8a1 1 0 0 1-1.414 0l-8-8a1 1 0 0 1 0-1.414Z',
})
export const ChevronTopBottom_Stroke2_Corner0_Rounded = createSinglePathSVG({
path: 'M11.293 4.293a1 1 0 0 1 1.414 0l4 4a1 1 0 0 1-1.414 1.414L12 6.414 8.707 9.707a1 1 0 0 1-1.414-1.414l4-4Zm-4 10a1 1 0 0 1 1.414 0L12 17.586l3.293-3.293a1 1 0 0 1 1.414 1.414l-4 4a1 1 0 0 1-1.414 0l-4-4a1 1 0 0 1 0-1.414Z',
})
-5
View File
@@ -1,5 +0,0 @@
import {createSinglePathSVG} from './TEMPLATE'
export const Coffee_Stroke2_Corner0_Rounded = createSinglePathSVG({
path: 'M7 2a1 1 0 0 1 1 1v2a1 1 0 0 1-2 0V3a1 1 0 0 1 1-1Zm4 0a1 1 0 0 1 1 1v2a1 1 0 1 1-2 0V3a1 1 0 0 1 1-1Zm4 0a1 1 0 0 1 1 1v2a1 1 0 1 1-2 0V3a1 1 0 0 1 1-1ZM4 9a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2h.5a3.5 3.5 0 1 1 0 7H18v4a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V9Zm12 0H6v11h10V9Zm2 5h.5a1.5 1.5 0 0 0 0-3H18v3Z',
})
-5
View File
@@ -1,5 +0,0 @@
import {createSinglePathSVG} from './TEMPLATE'
export const PiggyBank_Stroke2_Corner0_Rounded = createSinglePathSVG({
path: 'M6.5 4.999Zm0 0a.054.054 0 0 1-.016.024.196.196 0 0 1-.152.043c.057.01.113.02.168.032V7.5a1 1 0 0 1-.33.742c-.543.49-.917 1.176-1.23 2.036a1 1 0 0 1-.94.657H3V14h1a1 1 0 0 1 .866.5c.436.754.879 1.195 1.637 1.636A1 1 0 0 1 7 17v2h2a2 2 0 0 1 2-2h2a2 2 0 0 1 2 2h2v-2.537a1 1 0 0 1 .332-.744A4.978 4.978 0 0 0 19 11.999 5 5 0 0 0 14 7H9.474a1 1 0 0 1-.893-.55v-.001l-.001-.002-.005-.008L9.474 6l-.9.438h.001v.002l.002.001.001.003.002.003v.003a2.62 2.62 0 0 0-.443-.538c-.319-.298-.839-.648-1.637-.814V5Zm2.082 1.452ZM9.5 4.447c.211.196.379.387.508.553H14a7 7 0 0 1 6.72 8.96 1.003 1.003 0 0 0 1.146-1.46 1 1 0 1 1 1.731-1 3 3 0 0 1-3.714 4.286A7.074 7.074 0 0 1 19 16.888V19a2 2 0 0 1-2 2h-2a2 2 0 0 1-2-2h-2a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2v-1.45A5.783 5.783 0 0 1 3.448 16H3a2 2 0 0 1-2-2v-3.065a2 2 0 0 1 2-2h.324c.286-.649.657-1.291 1.176-1.852V5c0-1.047.89-2.12 2.161-1.907 1.33.223 2.248.805 2.839 1.354ZM8.25 12a1.25 1.25 0 1 0 0-2.5 1.25 1.25 0 0 0 0 2.5Z',
})
-5
View File
@@ -1,5 +0,0 @@
import {createSinglePathSVG} from './TEMPLATE'
export const Poop_Stroke2_Corner0_Rounded = createSinglePathSVG({
path: 'M12 4c0-1.012.894-2.187 2.237-1.846a5.002 5.002 0 0 1 3.218 7.119 4.001 4.001 0 0 1 2.345 4.976A3.501 3.501 0 0 1 18.5 21h-13a3.5 3.5 0 0 1-1.3-6.75 4 4 0 0 1 2.052-4.848A4 4 0 0 1 10 4h2.001Zm-4.187 7.009A2 2 0 0 0 6 13c0 .366.098.706.271 1H12a1 1 0 1 1 0 2H5.5a1.5 1.5 0 0 0 0 3h13a1.5 1.5 0 1 0 0-3H17a1 1 0 1 1 0-2h.729c.173-.294.271-.634.271-1a2 2 0 0 0-2-2h-2a1 1 0 1 1 0-2h1.236a3.002 3.002 0 0 0-1.243-4.832A2 2 0 0 1 12 6h-2a2 2 0 0 0-1.728 3.009H9a1 1 0 0 1 0 2H7.813Z',
})
+3 -3
View File
@@ -146,10 +146,10 @@ function ContentHiderActive({
label={desc.name}
accessibilityHint={
modui.noOverride
? _(msg`Learn more about the moderation applied to this content.`)
? _(msg`Learn more about the moderation applied to this content`)
: override
? _(msg`Hide the content`)
: _(msg`Show the content`)
? _(msg`Hides the content`)
: _(msg`Shows the content`)
}>
{state => (
<View
+1 -1
View File
@@ -76,7 +76,7 @@ export function PostHider({
}}
accessibilityRole="button"
accessibilityHint={
override ? _(msg`Hide the content`) : _(msg`Show the content`)
override ? _(msg`Hides the content`) : _(msg`Shows the content`)
}
accessibilityLabel=""
style={[