minor styling fixes

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