minor styling fixes

This commit is contained in:
vineyardbovines
2026-03-05 09:54:41 -05:00
parent 443cb9859c
commit 13486b0fca
4 changed files with 225 additions and 71 deletions
+142 -35
View File
@@ -1,9 +1,9 @@
import {View} from 'react-native'
import {Modal, Pressable, View} from 'react-native'
import {msg} from '@lingui/core/macro'
import {useLingui} from '@lingui/react'
import {Trans} from '@lingui/react/macro'
import {atoms as a, useTheme} from '#/alf'
import {atoms as a, useTheme, web} from '#/alf'
import {Button, ButtonText} from '#/components/Button'
import * as Dialog from '#/components/Dialog'
import {Robot_Filled_Corner2_Rounded as RobotIcon} from '#/components/icons/Robot'
@@ -12,58 +12,132 @@ import {navigate} from '#/Navigation'
export function BotAccountInfoDialog({
control,
isMe,
}: {
control: Dialog.DialogControlProps
isMe?: boolean
}) {
return (
<Dialog.Outer control={control}>
<Dialog.Handle />
<BotAccountInfoDialogInner control={control} isMe={isMe} />
<BotAccountInfoDialogInner control={control} />
</Dialog.Outer>
)
}
function BotAccountInfoDialogInner({
control,
isMe,
export function BotAccountOwnAlert({
visible,
onClose,
}: {
control: Dialog.DialogControlProps
isMe?: boolean
visible: boolean
onClose: (cb?: () => void) => void
}) {
const {_} = useLingui()
const t = useTheme()
return (
<Dialog.ScrollableInner label={_(msg`Automated account`)}>
<View style={[a.align_center, a.gap_2xl, a.py_md]}>
<View style={[t.atoms.shadow_sm]}>
<RobotIcon width={48} fill={t.atoms.text_contrast_medium.color} />
</View>
<Text style={[a.text_lg, a.text_center, a.font_semi_bold]}>
{isMe ? (
<Modal
visible={visible}
transparent
animationType="fade"
onRequestClose={() => onClose()}
accessibilityLabel={_(msg`Automated account`)}
accessibilityViewIsModal>
<Pressable
accessibilityRole="button"
accessibilityLabel={_(msg`Close`)}
onPress={() => onClose()}
style={[
a.flex_1,
a.justify_center,
a.align_center,
a.px_xl,
{backgroundColor: 'rgba(0,0,0,0.5)'},
]}>
<Pressable
accessibilityRole="button"
onPress={e => e.stopPropagation()}
style={[
t.atoms.bg,
a.w_full,
{borderRadius: 48},
a.p_2xl,
{maxWidth: 280},
]}>
<View style={[a.align_center, a.gap_md]}>
<View style={[t.atoms.shadow_sm]}>
<RobotIcon width={48} fill={t.atoms.text_contrast_medium.color} />
</View>
<Text style={[a.text_md, a.text_center, a.py_sm]}>
<Trans>
You have marked this account as automated. You can remove it at
any time from your account settings.
</Trans>
</Text>
<Button
label={_(msg`Okay`)}
onPress={() => onClose()}
color="primary"
size="large"
style={[a.w_full]}>
<ButtonText>
<Trans>Okay</Trans>
</ButtonText>
</Button>
<Button
label={_(msg`Open settings`)}
onPress={() => {
onClose(() => {
navigate('AutomationLabelSettings')
})
}}
color="secondary"
size="large"
style={[a.w_full]}>
<ButtonText>
<Trans>Open settings</Trans>
</ButtonText>
</Button>
</View>
</Pressable>
</Pressable>
</Modal>
)
}
export function BotAccountOwnAlertWeb({
control,
}: {
control: Dialog.DialogControlProps
}) {
const {_} = useLingui()
const t = useTheme()
return (
<Dialog.Outer control={control} webOptions={{alignCenter: true}}>
<Dialog.ScrollableInner
label={_(msg`Automated account`)}
style={web([{maxWidth: 320, borderRadius: 36}])}>
<View style={[a.align_center, a.gap_2xl, a.py_md]}>
<View style={[t.atoms.shadow_sm]}>
<RobotIcon width={48} fill={t.atoms.text_contrast_medium.color} />
</View>
<Text style={[a.text_md, a.text_center]}>
<Trans>
You have marked this account as automated. You can remove it at
any time from your account settings.
</Trans>
) : (
<Trans>
This account has been marked as automated by its owner
</Trans>
)}
</Text>
<Button
label={_(msg`Okay`)}
onPress={() => control.close()}
color="primary"
size="large"
style={[a.w_full]}>
<ButtonText>
<Trans>Okay</Trans>
</ButtonText>
</Button>
{isMe && (
</Text>
<Button
label={_(msg`Okay`)}
onPress={() => control.close()}
color="primary"
size="large"
style={[a.w_full]}>
<ButtonText>
<Trans>Okay</Trans>
</ButtonText>
</Button>
<Button
label={_(msg`Open settings`)}
onPress={() => {
@@ -79,7 +153,40 @@ function BotAccountInfoDialogInner({
<Trans>Open settings</Trans>
</ButtonText>
</Button>
)}
</View>
<Dialog.Close />
</Dialog.ScrollableInner>
</Dialog.Outer>
)
}
function BotAccountInfoDialogInner({
control,
}: {
control: Dialog.DialogControlProps
}) {
const {_} = useLingui()
const t = useTheme()
return (
<Dialog.ScrollableInner label={_(msg`Automated account`)}>
<View style={[a.align_center, a.gap_2xl, a.py_md]}>
<View style={[t.atoms.shadow_sm]}>
<RobotIcon width={48} fill={t.atoms.text_contrast_medium.color} />
</View>
<Text style={[a.text_lg, a.text_center, a.font_semi_bold]}>
<Trans>This account has been marked as automated by its owner</Trans>
</Text>
<Button
label={_(msg`Okay`)}
onPress={() => control.close()}
color="primary"
size="large"
style={[a.w_full]}>
<ButtonText>
<Trans>Okay</Trans>
</ButtonText>
</Button>
</View>
<Dialog.Close />
</Dialog.ScrollableInner>
+29 -4
View File
@@ -1,15 +1,21 @@
import {useState} from 'react'
import {View} from 'react-native'
import {type ComAtprotoLabelDefs} from '@atproto/api'
import {msg} from '@lingui/core/macro'
import {useLingui} from '@lingui/react'
import {useAnalytics} from '#/analytics'
import {IS_NATIVE} from '#/env'
import {isBotAccount} from '#/lib/bots'
import {atoms as a, useBreakpoints, useTheme} from '#/alf'
import {BotAccountInfoDialog} from '#/components/BotAccountInfoDialog'
import {
BotAccountInfoDialog,
BotAccountOwnAlert,
BotAccountOwnAlertWeb,
} from '#/components/BotAccountInfoDialog'
import {Button} from '#/components/Button'
import {useDialogControl} from '#/components/Dialog'
import {Robot_Filled_Corner2_Rounded as RobotIcon} from '#/components/icons/Robot'
import {useAnalytics} from '#/analytics'
export function BotBadge({
profile,
@@ -47,6 +53,7 @@ export function BotBadgeButton({
const {_} = useLingui()
const {gtPhone} = useBreakpoints()
const control = useDialogControl()
const [alertVisible, setAlertVisible] = useState(false)
if (!isBotAccount(profile)) {
return null
@@ -59,6 +66,8 @@ export function BotBadgeButton({
dimensions = 14
}
const useNativeAlert = isMe && IS_NATIVE
return (
<>
<Button
@@ -67,7 +76,11 @@ export function BotBadgeButton({
onPress={evt => {
evt.preventDefault()
ax.metric('bot:badge:click', {})
control.open()
if (useNativeAlert) {
setAlertVisible(true)
} else {
control.open()
}
}}>
{({hovered}) => (
<View
@@ -88,7 +101,19 @@ export function BotBadgeButton({
</View>
)}
</Button>
<BotAccountInfoDialog control={control} isMe={isMe} />
{useNativeAlert ? (
<BotAccountOwnAlert
visible={alertVisible}
onClose={cb => {
setAlertVisible(false)
cb?.()
}}
/>
) : isMe ? (
<BotAccountOwnAlertWeb control={control} />
) : (
<BotAccountInfoDialog control={control} />
)}
</>
)
}
+11 -2
View File
@@ -273,7 +273,14 @@ function InlineNameAndHandle({
/>
</View>
)}
<BotBadge profile={profile} />
<View
style={[
a.pl_2xs,
a.self_center,
{marginTop: platform({default: 0, android: -1})},
]}>
<BotBadge profile={profile} />
</View>
<Text
emoji
style={[
@@ -328,7 +335,9 @@ export function Name({
/>
</View>
)}
<BotBadge profile={profile} />
<View style={[a.pl_xs]}>
<BotBadge profile={profile} />
</View>
</View>
)
}
@@ -1,6 +1,7 @@
import React from 'react'
import {View} from 'react-native'
import {type $Typed, ComAtprotoLabelDefs} from '@atproto/api'
import {useQueryClient} from '@tanstack/react-query'
import {msg} from '@lingui/core/macro'
import {useLingui} from '@lingui/react'
import {Trans} from '@lingui/react/macro'
@@ -8,6 +9,8 @@ import {type NativeStackScreenProps} from '@react-navigation/native-stack'
import {useAnalytics} from '#/analytics'
import {type CommonNavigatorParams} from '#/lib/routes/types'
import {RQKEY_ROOT as POST_FEED_RQKEY_ROOT} from '#/state/queries/post-feed'
import {postThreadQueryKeyRoot} from '#/state/queries/usePostThread/types'
import {
useProfileQuery,
useProfileUpdateMutation,
@@ -32,6 +35,7 @@ export function AutomationLabelSettingsScreen({}: Props) {
const t = useTheme()
const ax = useAnalytics()
const {_} = useLingui()
const queryClient = useQueryClient()
const {currentAccount} = useSession()
const {data: profile} = useProfileQuery({did: currentAccount?.did})
const updateProfile = useProfileUpdateMutation()
@@ -48,41 +52,50 @@ export function AutomationLabelSettingsScreen({}: Props) {
}
let wasAdded = false
ax.metric('bot:label:toggle', {state: isBotLabeled ? 'remove' : 'add'})
updateProfile.mutate({
profile,
updates: existing => {
const labels: $Typed<ComAtprotoLabelDefs.SelfLabels> = bsky.validate(
existing.labels,
ComAtprotoLabelDefs.validateSelfLabels,
)
? existing.labels
: {
$type: 'com.atproto.label.defs#selfLabels',
values: [],
}
updateProfile.mutate(
{
profile,
updates: existing => {
const labels: $Typed<ComAtprotoLabelDefs.SelfLabels> =
bsky.validate(
existing.labels,
ComAtprotoLabelDefs.validateSelfLabels,
)
? existing.labels
: {
$type: 'com.atproto.label.defs#selfLabels',
values: [],
}
const hasLabel = labels.values.some(l => l.val === 'bot')
if (hasLabel) {
wasAdded = false
labels.values = labels.values.filter(l => l.val !== 'bot')
} else {
wasAdded = true
labels.values.push({val: 'bot'})
}
const hasLabel = labels.values.some(l => l.val === 'bot')
if (hasLabel) {
wasAdded = false
labels.values = labels.values.filter(l => l.val !== 'bot')
} else {
wasAdded = true
labels.values.push({val: 'bot'})
}
if (labels.values.length === 0) {
delete existing.labels
} else {
existing.labels = labels
}
if (labels.values.length === 0) {
delete existing.labels
} else {
existing.labels = labels
}
return existing
return existing
},
checkCommitted: res => {
const exists = !!res.data.labels?.some(l => l.val === 'bot')
return exists === wasAdded
},
},
checkCommitted: res => {
const exists = !!res.data.labels?.some(l => l.val === 'bot')
return exists === wasAdded
{
onSuccess() {
queryClient.invalidateQueries({queryKey: [POST_FEED_RQKEY_ROOT]})
queryClient.invalidateQueries({queryKey: [postThreadQueryKeyRoot]})
},
},
})
)
}, [updateProfile, profile])
return (