Merge branch '3p-moderators' of github.com:bluesky-social/social-app into 3p-moderators
This commit is contained in:
@@ -56,7 +56,7 @@ export function RecommendedFollowsItem({
|
||||
)
|
||||
}
|
||||
|
||||
export function ProfileCard({
|
||||
function ProfileCard({
|
||||
profile,
|
||||
onFollowStateChange,
|
||||
moderation,
|
||||
@@ -72,7 +72,10 @@ export function ProfileCard({
|
||||
const pal = usePalette('default')
|
||||
const [addingMoreSuggestions, setAddingMoreSuggestions] =
|
||||
React.useState(false)
|
||||
const [queueFollow, queueUnfollow] = useProfileFollowMutationQueue(profile)
|
||||
const [queueFollow, queueUnfollow] = useProfileFollowMutationQueue(
|
||||
profile,
|
||||
'RecommendedFollowsItem',
|
||||
)
|
||||
|
||||
const onToggleFollow = React.useCallback(async () => {
|
||||
try {
|
||||
|
||||
@@ -65,6 +65,7 @@ import {logger} from '#/logger'
|
||||
import {ComposerReplyTo} from 'view/com/composer/ComposerReplyTo'
|
||||
import * as Prompt from '#/components/Prompt'
|
||||
import {useDialogStateControlContext} from 'state/dialogs'
|
||||
import {logEvent} from '#/lib/statsig/statsig'
|
||||
|
||||
type Props = ComposerOpts
|
||||
export const ComposePost = observer(function ComposePost({
|
||||
@@ -255,6 +256,16 @@ export const ComposePost = observer(function ComposePost({
|
||||
setIsProcessing(false)
|
||||
return
|
||||
} finally {
|
||||
if (postUri) {
|
||||
logEvent('post:create', {
|
||||
imageCount: gallery.size,
|
||||
isReply: replyTo != null,
|
||||
hasLink: extLink != null,
|
||||
hasQuote: quote != null,
|
||||
langs: langPrefs.postLanguage,
|
||||
logContext: 'Composer',
|
||||
})
|
||||
}
|
||||
track('Create Post', {
|
||||
imageCount: gallery.size,
|
||||
})
|
||||
|
||||
@@ -42,7 +42,10 @@ function PostThreadFollowBtnLoaded({
|
||||
const {isTabletOrDesktop} = useWebMediaQueries()
|
||||
const profile: Shadow<AppBskyActorDefs.ProfileViewBasic> =
|
||||
useProfileShadow(profileUnshadowed)
|
||||
const [queueFollow, queueUnfollow] = useProfileFollowMutationQueue(profile)
|
||||
const [queueFollow, queueUnfollow] = useProfileFollowMutationQueue(
|
||||
profile,
|
||||
'PostThreadItem',
|
||||
)
|
||||
const requireAuth = useRequireAuth()
|
||||
|
||||
const isFollowing = !!profile.viewer?.following
|
||||
|
||||
@@ -375,6 +375,7 @@ let PostThreadItemLoaded = ({
|
||||
record={record}
|
||||
richText={richText}
|
||||
onPressReply={onPressReply}
|
||||
logContext="PostThreadItem"
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
@@ -521,6 +522,7 @@ let PostThreadItemLoaded = ({
|
||||
record={record}
|
||||
richText={richText}
|
||||
onPressReply={onPressReply}
|
||||
logContext="PostThreadItem"
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
@@ -216,6 +216,7 @@ function PostInner({
|
||||
record={record}
|
||||
richText={richText}
|
||||
onPressReply={onPressReply}
|
||||
logContext="Post"
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
@@ -33,6 +33,7 @@ import {useLingui} from '@lingui/react'
|
||||
import {DiscoverFallbackHeader} from './DiscoverFallbackHeader'
|
||||
import {FALLBACK_MARKER_POST} from '#/lib/api/feed/home'
|
||||
import {useInitialNumToRender} from 'lib/hooks/useInitialNumToRender'
|
||||
import {logEvent} from '#/lib/statsig/statsig'
|
||||
|
||||
const LOADING_ITEM = {_reactKey: '__loading__'}
|
||||
const EMPTY_FEED_ITEM = {_reactKey: '__empty__'}
|
||||
@@ -223,16 +224,29 @@ let Feed = ({
|
||||
setIsPTRing(false)
|
||||
}, [refetch, track, setIsPTRing, onHasNew])
|
||||
|
||||
const feedType = feed.split('|')[0]
|
||||
const onEndReached = React.useCallback(async () => {
|
||||
if (isFetching || !hasNextPage || isError) return
|
||||
|
||||
logEvent('feed:endReached', {
|
||||
feedType: feedType,
|
||||
itemCount: feedItems.length,
|
||||
})
|
||||
track('Feed:onEndReached')
|
||||
try {
|
||||
await fetchNextPage()
|
||||
} catch (err) {
|
||||
logger.error('Failed to load more posts', {message: err})
|
||||
}
|
||||
}, [isFetching, hasNextPage, isError, fetchNextPage, track])
|
||||
}, [
|
||||
isFetching,
|
||||
hasNextPage,
|
||||
isError,
|
||||
fetchNextPage,
|
||||
track,
|
||||
feedType,
|
||||
feedItems.length,
|
||||
])
|
||||
|
||||
const onPressTryAgain = React.useCallback(() => {
|
||||
refetch()
|
||||
|
||||
@@ -305,6 +305,7 @@ let FeedItemInner = ({
|
||||
record={record}
|
||||
richText={richText}
|
||||
onPressReply={onPressReply}
|
||||
logContext="FeedItem"
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
@@ -13,13 +13,18 @@ export function FollowButton({
|
||||
followedType = 'default',
|
||||
profile,
|
||||
labelStyle,
|
||||
logContext,
|
||||
}: {
|
||||
unfollowedType?: ButtonType
|
||||
followedType?: ButtonType
|
||||
profile: Shadow<AppBskyActorDefs.ProfileViewBasic>
|
||||
labelStyle?: StyleProp<TextStyle>
|
||||
logContext: 'ProfileCard'
|
||||
}) {
|
||||
const [queueFollow, queueUnfollow] = useProfileFollowMutationQueue(profile)
|
||||
const [queueFollow, queueUnfollow] = useProfileFollowMutationQueue(
|
||||
profile,
|
||||
logContext,
|
||||
)
|
||||
const {_} = useLingui()
|
||||
|
||||
const onPressFollow = async () => {
|
||||
|
||||
@@ -256,7 +256,9 @@ export function ProfileCardWithFollowBtn({
|
||||
renderButton={
|
||||
isMe
|
||||
? undefined
|
||||
: profileShadow => <FollowButton profile={profileShadow} />
|
||||
: profileShadow => (
|
||||
<FollowButton profile={profileShadow} logContext="ProfileCard" />
|
||||
)
|
||||
}
|
||||
onPress={onPress}
|
||||
/>
|
||||
|
||||
@@ -170,7 +170,10 @@ function SuggestedFollow({
|
||||
const pal = usePalette('default')
|
||||
const moderationOpts = useModerationOpts()
|
||||
const profile = useProfileShadow(profileUnshadowed)
|
||||
const [queueFollow, queueUnfollow] = useProfileFollowMutationQueue(profile)
|
||||
const [queueFollow, queueUnfollow] = useProfileFollowMutationQueue(
|
||||
profile,
|
||||
'ProfileHeaderSuggestedFollows',
|
||||
)
|
||||
|
||||
const onPressFollow = React.useCallback(async () => {
|
||||
try {
|
||||
|
||||
@@ -59,9 +59,17 @@ let ProfileMenu = ({
|
||||
|
||||
const [queueMute, queueUnmute] = useProfileMuteMutationQueue(profile)
|
||||
const [queueBlock, queueUnblock] = useProfileBlockMutationQueue(profile)
|
||||
const [queueFollow, queueUnfollow] = useProfileFollowMutationQueue(profile)
|
||||
const [queueFollow, queueUnfollow] = useProfileFollowMutationQueue(
|
||||
profile,
|
||||
'ProfileMenu',
|
||||
)
|
||||
|
||||
const blockPromptControl = Prompt.usePromptControl()
|
||||
const loggedOutWarningPromptControl = Prompt.usePromptControl()
|
||||
|
||||
const showLoggedOutWarning = React.useMemo(() => {
|
||||
return !!profile.labels?.find(label => label.val === '!no-unauthenticated')
|
||||
}, [profile.labels])
|
||||
|
||||
const invalidateProfileQuery = React.useCallback(() => {
|
||||
queryClient.invalidateQueries({
|
||||
@@ -205,7 +213,13 @@ let ProfileMenu = ({
|
||||
<Menu.Item
|
||||
testID="profileHeaderDropdownShareBtn"
|
||||
label={_(msg`Share`)}
|
||||
onPress={onPressShare}>
|
||||
onPress={() => {
|
||||
if (showLoggedOutWarning) {
|
||||
loggedOutWarningPromptControl.open()
|
||||
} else {
|
||||
onPressShare()
|
||||
}
|
||||
}}>
|
||||
<Menu.ItemText>
|
||||
<Trans>Share</Trans>
|
||||
</Menu.ItemText>
|
||||
@@ -348,6 +362,16 @@ let ProfileMenu = ({
|
||||
}
|
||||
confirmButtonColor={profile.viewer?.blocking ? undefined : 'negative'}
|
||||
/>
|
||||
|
||||
<Prompt.Basic
|
||||
control={loggedOutWarningPromptControl}
|
||||
title={_(msg`Note about sharing`)}
|
||||
description={_(
|
||||
msg`This profile is only visible to logged-in users. It won't be visible to people who aren't logged in.`,
|
||||
)}
|
||||
onConfirm={onPressShare}
|
||||
confirmButtonCta={_(msg`Share anyway`)}
|
||||
/>
|
||||
</EventStopper>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -82,11 +82,13 @@ let PostDropdownBtn = ({
|
||||
const reportDialogControl = useReportDialogControl()
|
||||
const deletePromptControl = useDialogControl()
|
||||
const hidePromptControl = useDialogControl()
|
||||
const loggedOutWarningPromptControl = useDialogControl()
|
||||
|
||||
const rootUri = record.reply?.root?.uri || postUri
|
||||
const isThreadMuted = mutedThreads.includes(rootUri)
|
||||
const isPostHidden = hiddenPosts && hiddenPosts.includes(postUri)
|
||||
const isAuthor = postAuthor.did === currentAccount?.did
|
||||
|
||||
const href = React.useMemo(() => {
|
||||
const urip = new AtUri(postUri)
|
||||
return makeProfileLink(postAuthor, 'post', urip.rkey)
|
||||
@@ -164,6 +166,17 @@ let PostDropdownBtn = ({
|
||||
hidePost({uri: postUri})
|
||||
}, [postUri, hidePost])
|
||||
|
||||
const shouldShowLoggedOutWarning = React.useMemo(() => {
|
||||
return !!postAuthor.labels?.find(
|
||||
label => label.val === '!no-unauthenticated',
|
||||
)
|
||||
}, [postAuthor])
|
||||
|
||||
const onSharePost = React.useCallback(() => {
|
||||
const url = toShareUrl(href)
|
||||
shareUrl(url)
|
||||
}, [href])
|
||||
|
||||
return (
|
||||
<EventStopper onKeyDown={false}>
|
||||
<Menu.Root>
|
||||
@@ -214,8 +227,11 @@ let PostDropdownBtn = ({
|
||||
testID="postDropdownShareBtn"
|
||||
label={isWeb ? _(msg`Copy link to post`) : _(msg`Share`)}
|
||||
onPress={() => {
|
||||
const url = toShareUrl(href)
|
||||
shareUrl(url)
|
||||
if (shouldShowLoggedOutWarning) {
|
||||
loggedOutWarningPromptControl.open()
|
||||
} else {
|
||||
onSharePost()
|
||||
}
|
||||
}}>
|
||||
<Menu.ItemText>
|
||||
{isWeb ? _(msg`Copy link to post`) : _(msg`Share`)}
|
||||
@@ -320,6 +336,16 @@ let PostDropdownBtn = ({
|
||||
cid: postCid,
|
||||
}}
|
||||
/>
|
||||
|
||||
<Prompt.Basic
|
||||
control={loggedOutWarningPromptControl}
|
||||
title={_(msg`Note about sharing`)}
|
||||
description={_(
|
||||
msg`This post is only visible to logged-in users. It won't be visible to people who aren't logged in.`,
|
||||
)}
|
||||
onConfirm={onSharePost}
|
||||
confirmButtonCta={_(msg`Share anyway`)}
|
||||
/>
|
||||
</EventStopper>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -43,6 +43,7 @@ let PostCtrls = ({
|
||||
richText,
|
||||
style,
|
||||
onPressReply,
|
||||
logContext,
|
||||
}: {
|
||||
big?: boolean
|
||||
post: Shadow<AppBskyFeedDefs.PostView>
|
||||
@@ -50,13 +51,17 @@ let PostCtrls = ({
|
||||
richText: RichTextAPI
|
||||
style?: StyleProp<ViewStyle>
|
||||
onPressReply: () => void
|
||||
logContext: 'FeedItem' | 'PostThreadItem' | 'Post'
|
||||
}): React.ReactNode => {
|
||||
const theme = useTheme()
|
||||
const {_} = useLingui()
|
||||
const {openComposer} = useComposerControls()
|
||||
const {closeModal} = useModalControls()
|
||||
const [queueLike, queueUnlike] = usePostLikeMutationQueue(post)
|
||||
const [queueRepost, queueUnrepost] = usePostRepostMutationQueue(post)
|
||||
const [queueLike, queueUnlike] = usePostLikeMutationQueue(post, logContext)
|
||||
const [queueRepost, queueUnrepost] = usePostRepostMutationQueue(
|
||||
post,
|
||||
logContext,
|
||||
)
|
||||
const requireAuth = useRequireAuth()
|
||||
|
||||
const defaultCtrlColor = React.useMemo(
|
||||
|
||||
Reference in New Issue
Block a user