diff --git a/src/components/BotAccountInfoDialog.tsx b/src/components/BotAccountInfoDialog.tsx index 8ca56f282c..36dde27e38 100644 --- a/src/components/BotAccountInfoDialog.tsx +++ b/src/components/BotAccountInfoDialog.tsx @@ -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 ( - + ) } -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 ( - - - - - - - {isMe ? ( + onClose()} + accessibilityLabel={_(msg`Automated account`)} + accessibilityViewIsModal> + onClose()} + style={[ + a.flex_1, + a.justify_center, + a.align_center, + a.px_xl, + {backgroundColor: 'rgba(0,0,0,0.5)'}, + ]}> + e.stopPropagation()} + style={[ + t.atoms.bg, + a.w_full, + {borderRadius: 48}, + a.p_2xl, + {maxWidth: 280}, + ]}> + + + + + + + + You have marked this account as automated. You can remove it at + any time from your account settings. + + + + + + + + + + ) +} + +export function BotAccountOwnAlertWeb({ + control, +}: { + control: Dialog.DialogControlProps +}) { + const {_} = useLingui() + const t = useTheme() + + return ( + + + + + + + You have marked this account as automated. You can remove it at any time from your account settings. - ) : ( - - This account has been marked as automated by its owner - - )} - - - {isMe && ( + + - )} + + + + + ) +} + +function BotAccountInfoDialogInner({ + control, +}: { + control: Dialog.DialogControlProps +}) { + const {_} = useLingui() + const t = useTheme() + + return ( + + + + + + + This account has been marked as automated by its owner + + diff --git a/src/components/BotBadge.tsx b/src/components/BotBadge.tsx index 41ef567688..0b9e3b4b2a 100644 --- a/src/components/BotBadge.tsx +++ b/src/components/BotBadge.tsx @@ -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 ( <> - + {useNativeAlert ? ( + { + setAlertVisible(false) + cb?.() + }} + /> + ) : isMe ? ( + + ) : ( + + )} ) } diff --git a/src/components/ProfileCard.tsx b/src/components/ProfileCard.tsx index 54906e33d5..0e489c5aac 100644 --- a/src/components/ProfileCard.tsx +++ b/src/components/ProfileCard.tsx @@ -273,7 +273,14 @@ function InlineNameAndHandle({ /> )} - + + + )} - + + + ) } diff --git a/src/screens/Settings/AutomationLabelSettings.tsx b/src/screens/Settings/AutomationLabelSettings.tsx index 89ec0ac273..4874f845dc 100644 --- a/src/screens/Settings/AutomationLabelSettings.tsx +++ b/src/screens/Settings/AutomationLabelSettings.tsx @@ -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 = bsky.validate( - existing.labels, - ComAtprotoLabelDefs.validateSelfLabels, - ) - ? existing.labels - : { - $type: 'com.atproto.label.defs#selfLabels', - values: [], - } + updateProfile.mutate( + { + profile, + updates: existing => { + const labels: $Typed = + 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 (