Share menu (#7840)
* move post ctrls to #/components * restructure post controls, basic share menu * add border radius to searchable people list for android * Revert "add border radius to searchable people list for android" This reverts commit 417449086e25b82f5683b12f6405d972f48ce50e. * add copy link to native share menu * reorg files again * open native share menu on long press * Translation comments Thanks @surfdude29 * abs path * update type imports, remove forwardRef * rm react import * equal spacing of buttons, extract disco debug * add better icon * add right offset to share button for visual alignment * Add recent chats to share menu (#7853) * add recent chats to share menu * Update RecentChats.tsx Co-authored-by: surfdude29 <149612116+surfdude29@users.noreply.github.com> * Update RecentChats.tsx * add fading edge on andriod * tweak scrollview * Add metrics and A/B alt icon to share menu (#8401) * add metrics * add a/b tested alt icon --------- Co-authored-by: surfdude29 <149612116+surfdude29@users.noreply.github.com> * More descriptive share text/icon on web (#7854) * more descriptive share text on web * revert dev mode changes * add missing import * use modified share icon everywhere * Add back conflicting changes --------- Co-authored-by: Eric Bailey <git@esb.lol> --------- Co-authored-by: surfdude29 <149612116+surfdude29@users.noreply.github.com> Co-authored-by: Eric Bailey <git@esb.lol>
This commit is contained in:
@@ -0,0 +1,292 @@
|
||||
import {memo, useState} from 'react'
|
||||
import {type StyleProp, View, type ViewStyle} from 'react-native'
|
||||
import {
|
||||
type AppBskyFeedDefs,
|
||||
type AppBskyFeedPost,
|
||||
type AppBskyFeedThreadgate,
|
||||
type RichText as RichTextAPI,
|
||||
} from '@atproto/api'
|
||||
import {msg, plural} from '@lingui/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
|
||||
import {CountWheel} from '#/lib/custom-animations/CountWheel'
|
||||
import {AnimatedLikeIcon} from '#/lib/custom-animations/LikeIcon'
|
||||
import {useHaptics} from '#/lib/haptics'
|
||||
import {useOpenComposer} from '#/lib/hooks/useOpenComposer'
|
||||
import {type Shadow} from '#/state/cache/types'
|
||||
import {useFeedFeedbackContext} from '#/state/feed-feedback'
|
||||
import {
|
||||
usePostLikeMutationQueue,
|
||||
usePostRepostMutationQueue,
|
||||
} from '#/state/queries/post'
|
||||
import {useRequireAuth} from '#/state/session'
|
||||
import {
|
||||
ProgressGuideAction,
|
||||
useProgressGuideControls,
|
||||
} from '#/state/shell/progress-guide'
|
||||
import {formatCount} from '#/view/com/util/numeric/format'
|
||||
import * as Toast from '#/view/com/util/Toast'
|
||||
import {atoms as a, useBreakpoints} from '#/alf'
|
||||
import {Bubble_Stroke2_Corner2_Rounded as Bubble} from '#/components/icons/Bubble'
|
||||
import {
|
||||
PostControlButton,
|
||||
PostControlButtonIcon,
|
||||
PostControlButtonText,
|
||||
} from './PostControlButton'
|
||||
import {PostMenuButton} from './PostMenu'
|
||||
import {RepostButton} from './RepostButton'
|
||||
import {ShareMenuButton} from './ShareMenu'
|
||||
|
||||
let PostControls = ({
|
||||
big,
|
||||
post,
|
||||
record,
|
||||
richText,
|
||||
feedContext,
|
||||
reqId,
|
||||
style,
|
||||
onPressReply,
|
||||
onPostReply,
|
||||
logContext,
|
||||
threadgateRecord,
|
||||
onShowLess,
|
||||
}: {
|
||||
big?: boolean
|
||||
post: Shadow<AppBskyFeedDefs.PostView>
|
||||
record: AppBskyFeedPost.Record
|
||||
richText: RichTextAPI
|
||||
feedContext?: string | undefined
|
||||
reqId?: string | undefined
|
||||
style?: StyleProp<ViewStyle>
|
||||
onPressReply: () => void
|
||||
onPostReply?: (postUri: string | undefined) => void
|
||||
logContext: 'FeedItem' | 'PostThreadItem' | 'Post' | 'ImmersiveVideo'
|
||||
threadgateRecord?: AppBskyFeedThreadgate.Record
|
||||
onShowLess?: (interaction: AppBskyFeedDefs.Interaction) => void
|
||||
}): React.ReactNode => {
|
||||
const {_, i18n} = useLingui()
|
||||
const {gtMobile} = useBreakpoints()
|
||||
const {openComposer} = useOpenComposer()
|
||||
const [queueLike, queueUnlike] = usePostLikeMutationQueue(post, logContext)
|
||||
const [queueRepost, queueUnrepost] = usePostRepostMutationQueue(
|
||||
post,
|
||||
logContext,
|
||||
)
|
||||
const requireAuth = useRequireAuth()
|
||||
const {sendInteraction} = useFeedFeedbackContext()
|
||||
const {captureAction} = useProgressGuideControls()
|
||||
const playHaptic = useHaptics()
|
||||
const isBlocked = Boolean(
|
||||
post.author.viewer?.blocking ||
|
||||
post.author.viewer?.blockedBy ||
|
||||
post.author.viewer?.blockingByList,
|
||||
)
|
||||
const replyDisabled = post.viewer?.replyDisabled
|
||||
|
||||
const [hasLikeIconBeenToggled, setHasLikeIconBeenToggled] = useState(false)
|
||||
|
||||
const onPressToggleLike = async () => {
|
||||
if (isBlocked) {
|
||||
Toast.show(
|
||||
_(msg`Cannot interact with a blocked user`),
|
||||
'exclamation-circle',
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
setHasLikeIconBeenToggled(true)
|
||||
if (!post.viewer?.like) {
|
||||
playHaptic('Light')
|
||||
sendInteraction({
|
||||
item: post.uri,
|
||||
event: 'app.bsky.feed.defs#interactionLike',
|
||||
feedContext,
|
||||
reqId,
|
||||
})
|
||||
captureAction(ProgressGuideAction.Like)
|
||||
await queueLike()
|
||||
} else {
|
||||
await queueUnlike()
|
||||
}
|
||||
} catch (e: any) {
|
||||
if (e?.name !== 'AbortError') {
|
||||
throw e
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const onRepost = async () => {
|
||||
if (isBlocked) {
|
||||
Toast.show(
|
||||
_(msg`Cannot interact with a blocked user`),
|
||||
'exclamation-circle',
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
if (!post.viewer?.repost) {
|
||||
sendInteraction({
|
||||
item: post.uri,
|
||||
event: 'app.bsky.feed.defs#interactionRepost',
|
||||
feedContext,
|
||||
reqId,
|
||||
})
|
||||
await queueRepost()
|
||||
} else {
|
||||
await queueUnrepost()
|
||||
}
|
||||
} catch (e: any) {
|
||||
if (e?.name !== 'AbortError') {
|
||||
throw e
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const onQuote = () => {
|
||||
if (isBlocked) {
|
||||
Toast.show(
|
||||
_(msg`Cannot interact with a blocked user`),
|
||||
'exclamation-circle',
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
sendInteraction({
|
||||
item: post.uri,
|
||||
event: 'app.bsky.feed.defs#interactionQuote',
|
||||
feedContext,
|
||||
reqId,
|
||||
})
|
||||
openComposer({
|
||||
quote: post,
|
||||
onPost: onPostReply,
|
||||
})
|
||||
}
|
||||
|
||||
const onShare = () => {
|
||||
sendInteraction({
|
||||
item: post.uri,
|
||||
event: 'app.bsky.feed.defs#interactionShare',
|
||||
feedContext,
|
||||
reqId,
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
<View style={[a.flex_row, a.justify_between, a.align_center, style]}>
|
||||
<View
|
||||
style={[
|
||||
big ? a.align_center : [a.flex_1, a.align_start, {marginLeft: -6}],
|
||||
replyDisabled ? {opacity: 0.5} : undefined,
|
||||
]}>
|
||||
<PostControlButton
|
||||
testID="replyBtn"
|
||||
onPress={
|
||||
!replyDisabled ? () => requireAuth(() => onPressReply()) : undefined
|
||||
}
|
||||
label={_(
|
||||
msg({
|
||||
message: `Reply (${plural(post.replyCount || 0, {
|
||||
one: '# reply',
|
||||
other: '# replies',
|
||||
})})`,
|
||||
comment:
|
||||
'Accessibility label for the reply button, verb form followed by number of replies and noun form',
|
||||
}),
|
||||
)}
|
||||
big={big}>
|
||||
<PostControlButtonIcon icon={Bubble} />
|
||||
{typeof post.replyCount !== 'undefined' && post.replyCount > 0 && (
|
||||
<PostControlButtonText>
|
||||
{formatCount(i18n, post.replyCount)}
|
||||
</PostControlButtonText>
|
||||
)}
|
||||
</PostControlButton>
|
||||
</View>
|
||||
<View style={big ? a.align_center : [a.flex_1, a.align_start]}>
|
||||
<RepostButton
|
||||
isReposted={!!post.viewer?.repost}
|
||||
repostCount={(post.repostCount ?? 0) + (post.quoteCount ?? 0)}
|
||||
onRepost={onRepost}
|
||||
onQuote={onQuote}
|
||||
big={big}
|
||||
embeddingDisabled={Boolean(post.viewer?.embeddingDisabled)}
|
||||
/>
|
||||
</View>
|
||||
<View style={big ? a.align_center : [a.flex_1, a.align_start]}>
|
||||
<PostControlButton
|
||||
testID="likeBtn"
|
||||
big={big}
|
||||
onPress={() => requireAuth(() => onPressToggleLike())}
|
||||
label={
|
||||
post.viewer?.like
|
||||
? _(
|
||||
msg({
|
||||
message: `Unlike (${plural(post.likeCount || 0, {
|
||||
one: '# like',
|
||||
other: '# likes',
|
||||
})})`,
|
||||
comment:
|
||||
'Accessibility label for the like button when the post has been liked, verb followed by number of likes and noun',
|
||||
}),
|
||||
)
|
||||
: _(
|
||||
msg({
|
||||
message: `Like (${plural(post.likeCount || 0, {
|
||||
one: '# like',
|
||||
other: '# likes',
|
||||
})})`,
|
||||
comment:
|
||||
'Accessibility label for the like button when the post has not been liked, verb form followed by number of likes and noun form',
|
||||
}),
|
||||
)
|
||||
}>
|
||||
<AnimatedLikeIcon
|
||||
isLiked={Boolean(post.viewer?.like)}
|
||||
big={big}
|
||||
hasBeenToggled={hasLikeIconBeenToggled}
|
||||
/>
|
||||
<CountWheel
|
||||
likeCount={post.likeCount ?? 0}
|
||||
big={big}
|
||||
isLiked={Boolean(post.viewer?.like)}
|
||||
hasBeenToggled={hasLikeIconBeenToggled}
|
||||
/>
|
||||
</PostControlButton>
|
||||
</View>
|
||||
<View style={big ? a.align_center : [a.flex_1, a.align_start]}>
|
||||
<View style={[!big && a.ml_sm]}>
|
||||
<ShareMenuButton
|
||||
testID="postShareBtn"
|
||||
post={post}
|
||||
big={big}
|
||||
record={record}
|
||||
richText={richText}
|
||||
timestamp={post.indexedAt}
|
||||
threadgateRecord={threadgateRecord}
|
||||
onShare={onShare}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
<View
|
||||
style={big ? a.align_center : [gtMobile && a.flex_1, a.align_start]}>
|
||||
<PostMenuButton
|
||||
testID="postDropdownBtn"
|
||||
post={post}
|
||||
postFeedContext={feedContext}
|
||||
postReqId={reqId}
|
||||
big={big}
|
||||
record={record}
|
||||
richText={richText}
|
||||
timestamp={post.indexedAt}
|
||||
threadgateRecord={threadgateRecord}
|
||||
onShowLess={onShowLess}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
PostControls = memo(PostControls)
|
||||
export {PostControls}
|
||||
Reference in New Issue
Block a user