diff --git a/src/components/post-subscriptions/SubscribeProfileButton.tsx b/src/components/post-subscriptions/SubscribeProfileButton.tsx new file mode 100644 index 0000000000..cfed19a60a --- /dev/null +++ b/src/components/post-subscriptions/SubscribeProfileButton.tsx @@ -0,0 +1,59 @@ +import {useCallback} from 'react' +import {msg, Trans} from '@lingui/macro' +import {useLingui} from '@lingui/react' + +import {useRequireEmailVerification} from '#/lib/hooks/useRequireEmailVerification' +import {createSanitizedDisplayName} from '#/lib/moderation/create-sanitized-display-name' +import {atoms as a} from '#/alf' +import {Button, ButtonIcon} from '#/components/Button' +import {useDialogControl} from '#/components/Dialog' +import type * as bsky from '#/types/bsky' +import {Bell_Stroke2_Corner0_Rounded as NotificationIcon} from '../icons/Bell' +import {SubscribeProfileDialog} from './SubscribeProfileDialog' + +export function SubscribeProfileButton({ + profile, +}: { + profile: bsky.profile.AnyProfileView +}) { + const {_} = useLingui() + const requireEmailVerification = useRequireEmailVerification() + const subscribeDialogControl = useDialogControl() + + const onPress = useCallback(() => { + subscribeDialogControl.open() + }, [subscribeDialogControl]) + + const name = createSanitizedDisplayName(profile, true) + + const wrappedOnPress = requireEmailVerification(onPress, { + instructions: [ + + Before you can subscribe to {name}'s posts, you must first verify your + email. + , + ], + }) + + return ( + <> + + + + + ) +} diff --git a/src/components/post-subscriptions/SubscribeProfileDialog.tsx b/src/components/post-subscriptions/SubscribeProfileDialog.tsx new file mode 100644 index 0000000000..334d6ace94 --- /dev/null +++ b/src/components/post-subscriptions/SubscribeProfileDialog.tsx @@ -0,0 +1,96 @@ +import {useMemo, useState} from 'react' +import {View} from 'react-native' +import {msg, Trans} from '@lingui/macro' +import {useLingui} from '@lingui/react' + +import {createSanitizedDisplayName} from '#/lib/moderation/create-sanitized-display-name' +import {useTheme, web} from '#/alf' +import {atoms as a} from '#/alf' +import * as Dialog from '#/components/Dialog' +import * as Toggle from '#/components/forms/Toggle' +import {Text} from '#/components/Typography' +import type * as bsky from '#/types/bsky' + +export function SubscribeProfileDialog({ + control, + profile, +}: { + control: Dialog.DialogControlProps + profile: bsky.profile.AnyProfileView +}) { + return ( + + + + + ) +} + +function DialogInner({profile}: {profile: bsky.profile.AnyProfileView}) { + const {_} = useLingui() + const t = useTheme() + const [state, setState] = useState({posts: false, replies: false}) + + const values = useMemo(() => { + const {posts, replies} = state + const res = [] + if (posts) res.push('posts') + if (replies) res.push('replies') + return res + }, [state]) + + const onChange = (newValues: string[]) => + setState({ + posts: newValues.includes('posts'), + replies: newValues.includes('replies'), + }) + + const name = createSanitizedDisplayName(profile, false) + + return ( + + + + + Subscribe to {name} + + + Get notifications for account activity + + + + + + + + Posts + + + + + + Replies + + + + + + + + + + ) +} diff --git a/src/lib/moderation/useGlobalLabelStrings.ts b/src/lib/moderation/useGlobalLabelStrings.ts index 86ad8e76d6..f272c6bc41 100644 --- a/src/lib/moderation/useGlobalLabelStrings.ts +++ b/src/lib/moderation/useGlobalLabelStrings.ts @@ -30,6 +30,12 @@ export function useGlobalLabelStrings(): GlobalLabelStrings { msg`This user has requested that their content only be shown to signed-in users.`, ), }, + '!no-subscriptions': { + name: _(msg`No subscriptions allowed`), + description: _( + msg`This user has requested that they cannot be subscribed to for post notifications.`, + ), + }, porn: { name: _(msg`Adult Content`), description: _(msg`Explicit sexual images.`), diff --git a/src/screens/Profile/Header/ProfileHeaderStandard.tsx b/src/screens/Profile/Header/ProfileHeaderStandard.tsx index 1639abaf0c..18792ec779 100644 --- a/src/screens/Profile/Header/ProfileHeaderStandard.tsx +++ b/src/screens/Profile/Header/ProfileHeaderStandard.tsx @@ -32,6 +32,7 @@ import { KnownFollowers, shouldShowKnownFollowers, } from '#/components/KnownFollowers' +import {SubscribeProfileButton} from '#/components/post-subscriptions/SubscribeProfileButton' import * as Prompt from '#/components/Prompt' import {RichText} from '#/components/RichText' import {Text} from '#/components/Typography' @@ -134,13 +135,17 @@ let ProfileHeaderStandard = ({ } }, [_, queueUnblock]) - const isMe = React.useMemo( + const isMe = useMemo( () => currentAccount?.did === profile.did, [currentAccount, profile], ) const {isActive: live} = useActorStatus(profile) + const subscriptionsDisallowed = useMemo(() => { + return !!profile.labels?.find(label => label.val === '!no-subscriptions') + }, [profile]) + return ( + {hasSession && !subscriptionsDisallowed && ( + + )} {hasSession && }