about summary refs log tree commit diff
path: root/src/components/PostControls/ShareMenu/index.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'src/components/PostControls/ShareMenu/index.tsx')
-rw-r--r--src/components/PostControls/ShareMenu/index.tsx119
1 files changed, 119 insertions, 0 deletions
diff --git a/src/components/PostControls/ShareMenu/index.tsx b/src/components/PostControls/ShareMenu/index.tsx
new file mode 100644
index 000000000..d4ea18bb0
--- /dev/null
+++ b/src/components/PostControls/ShareMenu/index.tsx
@@ -0,0 +1,119 @@
+import {memo, useMemo, useState} from 'react'
+import {
+  type AppBskyFeedDefs,
+  type AppBskyFeedPost,
+  type AppBskyFeedThreadgate,
+  AtUri,
+  type RichText as RichTextAPI,
+} from '@atproto/api'
+import {msg} from '@lingui/macro'
+import {useLingui} from '@lingui/react'
+import type React from 'react'
+
+import {makeProfileLink} from '#/lib/routes/links'
+import {shareUrl} from '#/lib/sharing'
+import {useGate} from '#/lib/statsig/statsig'
+import {toShareUrl} from '#/lib/strings/url-helpers'
+import {logger} from '#/logger'
+import {type Shadow} from '#/state/cache/post-shadow'
+import {EventStopper} from '#/view/com/util/EventStopper'
+import {native} from '#/alf'
+import {ArrowOutOfBoxModified_Stroke2_Corner2_Rounded as ArrowOutOfBoxIcon} from '#/components/icons/ArrowOutOfBox'
+import {ArrowShareRight_Stroke2_Corner2_Rounded as ArrowShareRightIcon} from '#/components/icons/ArrowShareRight'
+import {useMenuControl} from '#/components/Menu'
+import * as Menu from '#/components/Menu'
+import {PostControlButton, PostControlButtonIcon} from '../PostControlButton'
+import {ShareMenuItems} from './ShareMenuItems'
+
+let ShareMenuButton = ({
+  testID,
+  post,
+  big,
+  record,
+  richText,
+  timestamp,
+  threadgateRecord,
+  onShare,
+}: {
+  testID: string
+  post: Shadow<AppBskyFeedDefs.PostView>
+  big?: boolean
+  record: AppBskyFeedPost.Record
+  richText: RichTextAPI
+  timestamp: string
+  threadgateRecord?: AppBskyFeedThreadgate.Record
+  onShare: () => void
+}): React.ReactNode => {
+  const {_} = useLingui()
+  const gate = useGate()
+
+  const ShareIcon = gate('alt_share_icon')
+    ? ArrowShareRightIcon
+    : ArrowOutOfBoxIcon
+
+  const menuControl = useMenuControl()
+  const [hasBeenOpen, setHasBeenOpen] = useState(false)
+  const lazyMenuControl = useMemo(
+    () => ({
+      ...menuControl,
+      open() {
+        setHasBeenOpen(true)
+        // HACK. We need the state update to be flushed by the time
+        // menuControl.open() fires but RN doesn't expose flushSync.
+        setTimeout(menuControl.open)
+
+        logger.metric(
+          'share:open',
+          {context: big ? 'thread' : 'feed'},
+          {statsig: true},
+        )
+      },
+    }),
+    [menuControl, setHasBeenOpen, big],
+  )
+
+  const onNativeLongPress = () => {
+    logger.metric('share:press:nativeShare', {}, {statsig: true})
+    const urip = new AtUri(post.uri)
+    const href = makeProfileLink(post.author, 'post', urip.rkey)
+    const url = toShareUrl(href)
+    shareUrl(url)
+    onShare()
+  }
+
+  return (
+    <EventStopper onKeyDown={false}>
+      <Menu.Root control={lazyMenuControl}>
+        <Menu.Trigger label={_(msg`Open share menu`)}>
+          {({props}) => {
+            return (
+              <PostControlButton
+                testID="postShareBtn"
+                big={big}
+                label={props.accessibilityLabel}
+                {...props}
+                onLongPress={native(onNativeLongPress)}>
+                <PostControlButtonIcon icon={ShareIcon} />
+              </PostControlButton>
+            )
+          }}
+        </Menu.Trigger>
+        {hasBeenOpen && (
+          // Lazily initialized. Once mounted, they stay mounted.
+          <ShareMenuItems
+            testID={testID}
+            post={post}
+            record={record}
+            richText={richText}
+            timestamp={timestamp}
+            threadgateRecord={threadgateRecord}
+            onShare={onShare}
+          />
+        )}
+      </Menu.Root>
+    </EventStopper>
+  )
+}
+
+ShareMenuButton = memo(ShareMenuButton)
+export {ShareMenuButton}