about summary refs log tree commit diff
path: root/src/view
diff options
context:
space:
mode:
Diffstat (limited to 'src/view')
-rw-r--r--src/view/com/auth/create/Step1.tsx27
-rw-r--r--src/view/com/modals/Modal.tsx4
-rw-r--r--src/view/com/modals/Modal.web.tsx3
-rw-r--r--src/view/com/modals/Waitlist.tsx190
-rw-r--r--src/view/com/util/EventStopper.tsx11
-rw-r--r--src/view/com/util/forms/PostDropdownBtn.tsx376
-rw-r--r--src/view/com/util/post-ctrls/PostCtrls.tsx5
-rw-r--r--src/view/shell/index.tsx19
8 files changed, 223 insertions, 412 deletions
diff --git a/src/view/com/auth/create/Step1.tsx b/src/view/com/auth/create/Step1.tsx
index 4c7018485..1f6852f8c 100644
--- a/src/view/com/auth/create/Step1.tsx
+++ b/src/view/com/auth/create/Step1.tsx
@@ -4,7 +4,6 @@ import {
   Keyboard,
   StyleSheet,
   TouchableOpacity,
-  TouchableWithoutFeedback,
   View,
 } from 'react-native'
 import {CreateAccountState, CreateAccountDispatch, is18} from './state'
@@ -19,7 +18,6 @@ import {ErrorMessage} from 'view/com/util/error/ErrorMessage'
 import {isWeb} from 'platform/detection'
 import {Trans, msg} from '@lingui/macro'
 import {useLingui} from '@lingui/react'
-import {useModalControls} from '#/state/modals'
 import {logger} from '#/logger'
 import {
   FontAwesomeIcon,
@@ -49,7 +47,6 @@ export function Step1({
 }) {
   const pal = usePalette('default')
   const {_} = useLingui()
-  const {openModal} = useModalControls()
   const serverInputControl = useDialogControl()
 
   const onPressSelectService = React.useCallback(() => {
@@ -57,10 +54,6 @@ export function Step1({
     Keyboard.dismiss()
   }, [serverInputControl])
 
-  const onPressWaitlist = React.useCallback(() => {
-    openModal({name: 'waitlist'})
-  }, [openModal])
-
   const birthDate = React.useMemo(() => {
     return sanitizeDate(uiState.birthDate)
   }, [uiState.birthDate])
@@ -164,23 +157,7 @@ export function Step1({
             </View>
           )}
 
-          {!uiState.inviteCode && uiState.isInviteCodeRequired ? (
-            <View style={[s.flexRow, s.alignCenter]}>
-              <Text style={pal.text}>
-                <Trans>Don't have an invite code?</Trans>{' '}
-              </Text>
-              <TouchableWithoutFeedback
-                onPress={onPressWaitlist}
-                accessibilityLabel={_(msg`Join the waitlist.`)}
-                accessibilityHint="">
-                <View style={styles.touchable}>
-                  <Text style={pal.link}>
-                    <Trans>Join the waitlist.</Trans>
-                  </Text>
-                </View>
-              </TouchableWithoutFeedback>
-            </View>
-          ) : (
+          {!uiState.isInviteCodeRequired || uiState.inviteCode ? (
             <>
               <View style={s.pb20}>
                 <Text
@@ -260,7 +237,7 @@ export function Step1({
                 />
               )}
             </>
-          )}
+          ) : undefined}
         </>
       )}
     </View>
diff --git a/src/view/com/modals/Modal.tsx b/src/view/com/modals/Modal.tsx
index 8da91c75c..100444ff5 100644
--- a/src/view/com/modals/Modal.tsx
+++ b/src/view/com/modals/Modal.tsx
@@ -20,7 +20,6 @@ import * as ReportModal from './report/Modal'
 import * as AppealLabelModal from './AppealLabel'
 import * as DeleteAccountModal from './DeleteAccount'
 import * as ChangeHandleModal from './ChangeHandle'
-import * as WaitlistModal from './Waitlist'
 import * as InviteCodesModal from './InviteCodes'
 import * as AddAppPassword from './AddAppPasswords'
 import * as ContentFilteringSettingsModal from './ContentFilteringSettings'
@@ -109,9 +108,6 @@ export function ModalsContainer() {
   } else if (activeModal?.name === 'change-handle') {
     snapPoints = ChangeHandleModal.snapPoints
     element = <ChangeHandleModal.Component {...activeModal} />
-  } else if (activeModal?.name === 'waitlist') {
-    snapPoints = WaitlistModal.snapPoints
-    element = <WaitlistModal.Component />
   } else if (activeModal?.name === 'invite-codes') {
     snapPoints = InviteCodesModal.snapPoints
     element = <InviteCodesModal.Component />
diff --git a/src/view/com/modals/Modal.web.tsx b/src/view/com/modals/Modal.web.tsx
index 97a60be91..0ced894a1 100644
--- a/src/view/com/modals/Modal.web.tsx
+++ b/src/view/com/modals/Modal.web.tsx
@@ -22,7 +22,6 @@ import * as CropImageModal from './crop-image/CropImage.web'
 import * as AltTextImageModal from './AltImage'
 import * as EditImageModal from './EditImage'
 import * as ChangeHandleModal from './ChangeHandle'
-import * as WaitlistModal from './Waitlist'
 import * as InviteCodesModal from './InviteCodes'
 import * as AddAppPassword from './AddAppPasswords'
 import * as ContentFilteringSettingsModal from './ContentFilteringSettings'
@@ -105,8 +104,6 @@ function Modal({modal}: {modal: ModalIface}) {
     element = <ThreadgateModal.Component {...modal} />
   } else if (modal.name === 'change-handle') {
     element = <ChangeHandleModal.Component {...modal} />
-  } else if (modal.name === 'waitlist') {
-    element = <WaitlistModal.Component />
   } else if (modal.name === 'invite-codes') {
     element = <InviteCodesModal.Component />
   } else if (modal.name === 'add-app-password') {
diff --git a/src/view/com/modals/Waitlist.tsx b/src/view/com/modals/Waitlist.tsx
deleted file mode 100644
index 263dd27a2..000000000
--- a/src/view/com/modals/Waitlist.tsx
+++ /dev/null
@@ -1,190 +0,0 @@
-import React from 'react'
-import {
-  ActivityIndicator,
-  StyleSheet,
-  TouchableOpacity,
-  View,
-} from 'react-native'
-import {TextInput} from './util'
-import {
-  FontAwesomeIcon,
-  FontAwesomeIconStyle,
-} from '@fortawesome/react-native-fontawesome'
-import LinearGradient from 'react-native-linear-gradient'
-import {Text} from '../util/text/Text'
-import {s, gradients} from 'lib/styles'
-import {usePalette} from 'lib/hooks/usePalette'
-import {useTheme} from 'lib/ThemeContext'
-import {ErrorMessage} from '../util/error/ErrorMessage'
-import {cleanError} from 'lib/strings/errors'
-import {Trans, msg} from '@lingui/macro'
-import {useLingui} from '@lingui/react'
-import {useModalControls} from '#/state/modals'
-
-export const snapPoints = ['80%']
-
-export function Component({}: {}) {
-  const pal = usePalette('default')
-  const theme = useTheme()
-  const {_} = useLingui()
-  const {closeModal} = useModalControls()
-  const [email, setEmail] = React.useState<string>('')
-  const [isEmailSent, setIsEmailSent] = React.useState<boolean>(false)
-  const [isProcessing, setIsProcessing] = React.useState<boolean>(false)
-  const [error, setError] = React.useState<string>('')
-
-  const onPressSignup = async () => {
-    setError('')
-    setIsProcessing(true)
-    try {
-      const res = await fetch('https://bsky.app/api/waitlist', {
-        method: 'POST',
-        headers: {'Content-Type': 'application/json'},
-        body: JSON.stringify({email}),
-      })
-      const resBody = await res.json()
-      if (resBody.success) {
-        setIsEmailSent(true)
-      } else {
-        setError(
-          resBody.error ||
-            _(msg`Something went wrong. Check your email and try again.`),
-        )
-      }
-    } catch (e: any) {
-      setError(cleanError(e))
-    }
-    setIsProcessing(false)
-  }
-  const onCancel = () => {
-    closeModal()
-  }
-
-  return (
-    <View style={[styles.container, pal.view]}>
-      <View style={[styles.innerContainer, pal.view]}>
-        <Text type="title-xl" style={[styles.title, pal.text]}>
-          <Trans>Join the waitlist</Trans>
-        </Text>
-        <Text type="lg" style={[styles.description, pal.text]}>
-          <Trans>
-            Bluesky uses invites to build a healthier community. If you don't
-            know anybody with an invite, you can sign up for the waitlist and
-            we'll send one soon.
-          </Trans>
-        </Text>
-        <TextInput
-          style={[styles.textInput, pal.borderDark, pal.text, s.mb10, s.mt10]}
-          placeholder={_(msg`Enter your email`)}
-          placeholderTextColor={pal.textLight.color}
-          autoCapitalize="none"
-          autoCorrect={false}
-          keyboardAppearance={theme.colorScheme}
-          value={email}
-          onChangeText={setEmail}
-          onSubmitEditing={onPressSignup}
-          enterKeyHint="done"
-          accessible={true}
-          accessibilityLabel={_(msg`Email`)}
-          accessibilityHint={_(
-            msg`Input your email to get on the Bluesky waitlist`,
-          )}
-        />
-        {error ? (
-          <View style={s.mt10}>
-            <ErrorMessage message={error} style={styles.error} />
-          </View>
-        ) : undefined}
-        {isProcessing ? (
-          <View style={[styles.btn, s.mt10]}>
-            <ActivityIndicator />
-          </View>
-        ) : isEmailSent ? (
-          <View style={[styles.btn, s.mt10]}>
-            <FontAwesomeIcon
-              icon="check"
-              style={pal.text as FontAwesomeIconStyle}
-            />
-            <Text style={[s.ml10, pal.text]}>
-              <Trans>
-                Your email has been saved! We&apos;ll be in touch soon.
-              </Trans>
-            </Text>
-          </View>
-        ) : (
-          <>
-            <TouchableOpacity
-              onPress={onPressSignup}
-              accessibilityRole="button"
-              accessibilityHint={_(
-                msg`Confirms signing up ${email} to the waitlist`,
-              )}>
-              <LinearGradient
-                colors={[gradients.blueLight.start, gradients.blueLight.end]}
-                start={{x: 0, y: 0}}
-                end={{x: 1, y: 1}}
-                style={[styles.btn]}>
-                <Text type="button-lg" style={[s.white, s.bold]}>
-                  <Trans>Join Waitlist</Trans>
-                </Text>
-              </LinearGradient>
-            </TouchableOpacity>
-            <TouchableOpacity
-              style={[styles.btn, s.mt10]}
-              onPress={onCancel}
-              accessibilityRole="button"
-              accessibilityLabel={_(msg`Cancel waitlist signup`)}
-              accessibilityHint={_(
-                msg`Exits signing up for waitlist with ${email}`,
-              )}
-              onAccessibilityEscape={onCancel}>
-              <Text type="button-lg" style={pal.textLight}>
-                <Trans>Cancel</Trans>
-              </Text>
-            </TouchableOpacity>
-          </>
-        )}
-      </View>
-    </View>
-  )
-}
-
-const styles = StyleSheet.create({
-  container: {
-    flex: 1,
-  },
-  innerContainer: {
-    paddingBottom: 20,
-  },
-  title: {
-    textAlign: 'center',
-    marginTop: 12,
-    marginBottom: 12,
-  },
-  description: {
-    textAlign: 'center',
-    paddingHorizontal: 22,
-    marginBottom: 10,
-  },
-  textInput: {
-    borderWidth: 1,
-    borderRadius: 6,
-    paddingHorizontal: 16,
-    paddingVertical: 12,
-    fontSize: 20,
-    marginHorizontal: 20,
-  },
-  btn: {
-    flexDirection: 'row',
-    alignItems: 'center',
-    justifyContent: 'center',
-    borderRadius: 32,
-    padding: 14,
-    marginHorizontal: 20,
-  },
-  error: {
-    borderRadius: 6,
-    marginHorizontal: 20,
-    marginBottom: 20,
-  },
-})
diff --git a/src/view/com/util/EventStopper.tsx b/src/view/com/util/EventStopper.tsx
index e743e89bb..8f5f5cf54 100644
--- a/src/view/com/util/EventStopper.tsx
+++ b/src/view/com/util/EventStopper.tsx
@@ -8,7 +8,14 @@ import {View, ViewStyle} from 'react-native'
 export function EventStopper({
   children,
   style,
-}: React.PropsWithChildren<{style?: ViewStyle | ViewStyle[]}>) {
+  onKeyDown = true,
+}: React.PropsWithChildren<{
+  style?: ViewStyle | ViewStyle[]
+  /**
+   * Default `true`. Set to `false` to allow onKeyDown to propagate
+   */
+  onKeyDown?: boolean
+}>) {
   const stop = (e: any) => {
     e.stopPropagation()
   }
@@ -18,7 +25,7 @@ export function EventStopper({
       onTouchEnd={stop}
       // @ts-ignore web only -prf
       onClick={stop}
-      onKeyDown={stop}
+      onKeyDown={onKeyDown ? stop : undefined}
       style={style}>
       {children}
     </View>
diff --git a/src/view/com/util/forms/PostDropdownBtn.tsx b/src/view/com/util/forms/PostDropdownBtn.tsx
index 09850a7f5..6f2ae55b2 100644
--- a/src/view/com/util/forms/PostDropdownBtn.tsx
+++ b/src/view/com/util/forms/PostDropdownBtn.tsx
@@ -1,5 +1,11 @@
 import React, {memo} from 'react'
-import {StyleProp, View, ViewStyle} from 'react-native'
+import {
+  StyleProp,
+  ViewStyle,
+  Pressable,
+  View,
+  PressableProps,
+} from 'react-native'
 import Clipboard from '@react-native-clipboard/clipboard'
 import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
 import {useNavigation} from '@react-navigation/native'
@@ -12,10 +18,6 @@ import {
 import {toShareUrl} from 'lib/strings/url-helpers'
 import {useTheme} from 'lib/ThemeContext'
 import {shareUrl} from 'lib/sharing'
-import {
-  NativeDropdown,
-  DropdownItem as NativeDropdownItem,
-} from './NativeDropdown'
 import * as Toast from '../Toast'
 import {EventStopper} from '../EventStopper'
 import {useModalControls} from '#/state/modals'
@@ -36,6 +38,19 @@ import {isWeb} from '#/platform/detection'
 import {richTextToString} from '#/lib/strings/rich-text-helpers'
 import {useGlobalDialogsControlContext} from '#/components/dialogs/Context'
 
+import {atoms as a, useTheme as useAlf, web} from '#/alf'
+import * as Menu from '#/components/Menu'
+import {Clipboard_Stroke2_Corner2_Rounded as ClipboardIcon} from '#/components/icons/Clipboard'
+import {Filter_Stroke2_Corner0_Rounded as Filter} from '#/components/icons/Filter'
+import {ArrowOutOfBox_Stroke2_Corner0_Rounded as Share} from '#/components/icons/ArrowOutOfBox'
+import {EyeSlash_Stroke2_Corner0_Rounded as EyeSlash} from '#/components/icons/EyeSlash'
+import {Mute_Stroke2_Corner0_Rounded as Mute} from '#/components/icons/Mute'
+import {SpeakerVolumeFull_Stroke2_Corner0_Rounded as Unmute} from '#/components/icons/Speaker'
+import {BubbleQuestion_Stroke2_Corner0_Rounded as Translate} from '#/components/icons/Bubble'
+import {Warning_Stroke2_Corner0_Rounded as Warning} from '#/components/icons/Warning'
+import {Trash_Stroke2_Corner0_Rounded as Trash} from '#/components/icons/Trash'
+import {CircleInfo_Stroke2_Corner0_Rounded as CircleInfo} from '#/components/icons/CircleInfo'
+
 let PostDropdownBtn = ({
   testID,
   postAuthor,
@@ -45,6 +60,7 @@ let PostDropdownBtn = ({
   richText,
   style,
   showAppealLabelItem,
+  hitSlop,
 }: {
   testID: string
   postAuthor: AppBskyActorDefs.ProfileViewBasic
@@ -54,9 +70,11 @@ let PostDropdownBtn = ({
   richText: RichTextAPI
   style?: StyleProp<ViewStyle>
   showAppealLabelItem?: boolean
+  hitSlop?: PressableProps['hitSlop']
 }): React.ReactNode => {
   const {hasSession, currentAccount} = useSession()
   const theme = useTheme()
+  const alf = useAlf()
   const {_} = useLingui()
   const defaultCtrlColor = theme.palette.default.postCtrl
   const {openModal} = useModalControls()
@@ -151,173 +169,189 @@ let PostDropdownBtn = ({
     hidePost({uri: postUri})
   }, [postUri, hidePost])
 
-  const dropdownItems: NativeDropdownItem[] = [
-    {
-      label: _(msg`Translate`),
-      onPress() {
-        onOpenTranslate()
-      },
-      testID: 'postDropdownTranslateBtn',
-      icon: {
-        ios: {
-          name: 'character.book.closed',
-        },
-        android: 'ic_menu_sort_alphabetically',
-        web: 'language',
-      },
-    },
-    {
-      label: _(msg`Copy post text`),
-      onPress() {
-        onCopyPostText()
-      },
-      testID: 'postDropdownCopyTextBtn',
-      icon: {
-        ios: {
-          name: 'doc.on.doc',
-        },
-        android: 'ic_menu_edit',
-        web: ['far', 'paste'],
-      },
-    },
-    {
-      label: isWeb ? _(msg`Copy link to post`) : _(msg`Share`),
-      onPress() {
-        const url = toShareUrl(href)
-        shareUrl(url)
-      },
-      testID: 'postDropdownShareBtn',
-      icon: {
-        ios: {
-          name: 'square.and.arrow.up',
-        },
-        android: 'ic_menu_share',
-        web: 'share',
-      },
-    },
-    hasSession && {
-      label: 'separator',
-    },
-    hasSession && {
-      label: isThreadMuted ? _(msg`Unmute thread`) : _(msg`Mute thread`),
-      onPress() {
-        onToggleThreadMute()
-      },
-      testID: 'postDropdownMuteThreadBtn',
-      icon: {
-        ios: {
-          name: 'speaker.slash',
-        },
-        android: 'ic_lock_silent_mode',
-        web: 'comment-slash',
-      },
-    },
-    hasSession && {
-      label: _(msg`Mute words & tags`),
-      onPress() {
-        mutedWordsDialogControl.open()
-      },
-      testID: 'postDropdownMuteWordsBtn',
-      icon: {
-        ios: {
-          name: 'speaker.slash',
-        },
-        android: 'ic_lock_silent_mode',
-        web: 'filter',
-      },
-    },
-    hasSession &&
-      !isAuthor &&
-      !isPostHidden && {
-        label: _(msg`Hide post`),
-        onPress() {
-          openModal({
-            name: 'confirm',
-            title: _(msg`Hide this post?`),
-            message: _(msg`This will hide this post from your feeds.`),
-            onPressConfirm: onHidePost,
-          })
-        },
-        testID: 'postDropdownHideBtn',
-        icon: {
-          ios: {
-            name: 'eye.slash',
-          },
-          android: 'ic_menu_delete',
-          web: ['far', 'eye-slash'],
-        },
-      },
-    {
-      label: 'separator',
-    },
-    !isAuthor &&
-      hasSession && {
-        label: _(msg`Report post`),
-        onPress() {
-          openModal({
-            name: 'report',
-            uri: postUri,
-            cid: postCid,
-          })
-        },
-        testID: 'postDropdownReportBtn',
-        icon: {
-          ios: {
-            name: 'exclamationmark.triangle',
-          },
-          android: 'ic_menu_report_image',
-          web: 'circle-exclamation',
-        },
-      },
-    isAuthor && {
-      label: _(msg`Delete post`),
-      onPress() {
-        openModal({
-          name: 'confirm',
-          title: _(msg`Delete this post?`),
-          message: _(msg`Are you sure? This cannot be undone.`),
-          onPressConfirm: onDeletePost,
-        })
-      },
-      testID: 'postDropdownDeleteBtn',
-      icon: {
-        ios: {
-          name: 'trash',
-        },
-        android: 'ic_menu_delete',
-        web: ['far', 'trash-can'],
-      },
-    },
-    showAppealLabelItem && {
-      label: 'separator',
-    },
-    showAppealLabelItem && {
-      label: _(msg`Appeal content warning`),
-      onPress() {
-        openModal({name: 'appeal-label', uri: postUri, cid: postCid})
-      },
-      testID: 'postDropdownAppealBtn',
-      icon: {
-        ios: {
-          name: 'exclamationmark.triangle',
-        },
-        android: 'ic_menu_report_image',
-        web: 'circle-exclamation',
-      },
-    },
-  ].filter(Boolean) as NativeDropdownItem[]
-
   return (
-    <EventStopper>
-      <NativeDropdown
-        testID={testID}
-        items={dropdownItems}
-        accessibilityLabel={_(msg`More post options`)}
-        accessibilityHint="">
-        <View style={style}>
-          <FontAwesomeIcon icon="ellipsis" size={20} color={defaultCtrlColor} />
-        </View>
-      </NativeDropdown>
+    <EventStopper onKeyDown={false}>
+      <Menu.Root>
+        <Menu.Trigger label={_(msg`Open post options menu`)}>
+          {({props, state}) => {
+            const styles = [
+              style,
+              a.rounded_full,
+              (state.hovered || state.focused || state.pressed) && [
+                web({outline: 0}),
+                alf.atoms.bg_contrast_25,
+              ],
+            ]
+            return isWeb ? (
+              <View {...props} testID={testID} style={styles}>
+                <FontAwesomeIcon
+                  icon="ellipsis"
+                  size={20}
+                  color={defaultCtrlColor}
+                  style={{pointerEvents: 'none'}}
+                />
+              </View>
+            ) : (
+              <Pressable
+                {...props}
+                hitSlop={hitSlop}
+                testID={testID}
+                style={styles}>
+                <FontAwesomeIcon
+                  icon="ellipsis"
+                  size={20}
+                  color={defaultCtrlColor}
+                  style={{pointerEvents: 'none'}}
+                />
+              </Pressable>
+            )
+          }}
+        </Menu.Trigger>
+
+        <Menu.Outer>
+          <Menu.Group>
+            <Menu.Item
+              testID="postDropdownTranslateBtn"
+              label={_(msg`Translate`)}
+              onPress={onOpenTranslate}>
+              <Menu.ItemText>{_(msg`Translate`)}</Menu.ItemText>
+              <Menu.ItemIcon icon={Translate} position="right" />
+            </Menu.Item>
+
+            <Menu.Item
+              testID="postDropdownCopyTextBtn"
+              label={_(msg`Copy post text`)}
+              onPress={onCopyPostText}>
+              <Menu.ItemText>{_(msg`Copy post text`)}</Menu.ItemText>
+              <Menu.ItemIcon icon={ClipboardIcon} position="right" />
+            </Menu.Item>
+
+            <Menu.Item
+              testID="postDropdownShareBtn"
+              label={isWeb ? _(msg`Copy link to post`) : _(msg`Share`)}
+              onPress={() => {
+                const url = toShareUrl(href)
+                shareUrl(url)
+              }}>
+              <Menu.ItemText>
+                {isWeb ? _(msg`Copy link to post`) : _(msg`Share`)}
+              </Menu.ItemText>
+              <Menu.ItemIcon icon={Share} position="right" />
+            </Menu.Item>
+          </Menu.Group>
+
+          {hasSession && (
+            <>
+              <Menu.Divider />
+
+              <Menu.Group>
+                <Menu.Item
+                  testID="postDropdownMuteThreadBtn"
+                  label={
+                    isThreadMuted ? _(msg`Unmute thread`) : _(msg`Mute thread`)
+                  }
+                  onPress={onToggleThreadMute}>
+                  <Menu.ItemText>
+                    {isThreadMuted
+                      ? _(msg`Unmute thread`)
+                      : _(msg`Mute thread`)}
+                  </Menu.ItemText>
+                  <Menu.ItemIcon
+                    icon={isThreadMuted ? Unmute : Mute}
+                    position="right"
+                  />
+                </Menu.Item>
+
+                <Menu.Item
+                  testID="postDropdownMuteWordsBtn"
+                  label={_(msg`Mute words & tags`)}
+                  onPress={() => mutedWordsDialogControl.open()}>
+                  <Menu.ItemText>{_(msg`Mute words & tags`)}</Menu.ItemText>
+                  <Menu.ItemIcon icon={Filter} position="right" />
+                </Menu.Item>
+
+                {!isAuthor && !isPostHidden && (
+                  <Menu.Item
+                    testID="postDropdownHideBtn"
+                    label={_(msg`Hide post`)}
+                    onPress={() => {
+                      openModal({
+                        name: 'confirm',
+                        title: _(msg`Hide this post?`),
+                        message: _(
+                          msg`This will hide this post from your feeds.`,
+                        ),
+                        onPressConfirm: onHidePost,
+                      })
+                    }}>
+                    <Menu.ItemText>{_(msg`Hide post`)}</Menu.ItemText>
+                    <Menu.ItemIcon icon={EyeSlash} position="right" />
+                  </Menu.Item>
+                )}
+              </Menu.Group>
+            </>
+          )}
+
+          <Menu.Divider />
+
+          <Menu.Group>
+            {!isAuthor && (
+              <Menu.Item
+                testID="postDropdownReportBtn"
+                label={_(msg`Report post`)}
+                onPress={() => {
+                  openModal({
+                    name: 'report',
+                    uri: postUri,
+                    cid: postCid,
+                  })
+                }}>
+                <Menu.ItemText>{_(msg`Report post`)}</Menu.ItemText>
+                <Menu.ItemIcon icon={Warning} position="right" />
+              </Menu.Item>
+            )}
+
+            {isAuthor && (
+              <Menu.Item
+                testID="postDropdownDeleteBtn"
+                label={_(msg`Delete post`)}
+                onPress={() => {
+                  openModal({
+                    name: 'confirm',
+                    title: _(msg`Delete this post?`),
+                    message: _(msg`Are you sure? This cannot be undone.`),
+                    onPressConfirm: onDeletePost,
+                  })
+                }}>
+                <Menu.ItemText>{_(msg`Delete post`)}</Menu.ItemText>
+                <Menu.ItemIcon icon={Trash} position="right" />
+              </Menu.Item>
+            )}
+
+            {showAppealLabelItem && (
+              <>
+                <Menu.Divider />
+
+                <Menu.Item
+                  testID="postDropdownAppealBtn"
+                  label={_(msg`Appeal content warning`)}
+                  onPress={() => {
+                    openModal({
+                      name: 'appeal-label',
+                      uri: postUri,
+                      cid: postCid,
+                    })
+                  }}>
+                  <Menu.ItemText>
+                    {_(msg`Appeal content warning`)}
+                  </Menu.ItemText>
+                  <Menu.ItemIcon icon={CircleInfo} position="right" />
+                </Menu.Item>
+              </>
+            )}
+          </Menu.Group>
+        </Menu.Outer>
+      </Menu.Root>
     </EventStopper>
   )
 }
diff --git a/src/view/com/util/post-ctrls/PostCtrls.tsx b/src/view/com/util/post-ctrls/PostCtrls.tsx
index bd21ddda2..1e26eecce 100644
--- a/src/view/com/util/post-ctrls/PostCtrls.tsx
+++ b/src/view/com/util/post-ctrls/PostCtrls.tsx
@@ -212,9 +212,7 @@ let PostCtrls = ({
             style={[styles.btn]}
             onPress={onShare}
             accessibilityRole="button"
-            accessibilityLabel={`${
-              post.viewer?.like ? _(msg`Unlike`) : _(msg`Like`)
-            } (${post.likeCount} ${pluralize(post.likeCount || 0, 'like')})`}
+            accessibilityLabel={`${_(msg`Share`)}`}
             accessibilityHint=""
             hitSlop={big ? HITSLOP_20 : HITSLOP_10}>
             <ArrowOutOfBox style={[defaultCtrlColor, styles.mt1]} width={22} />
@@ -231,6 +229,7 @@ let PostCtrls = ({
           richText={richText}
           showAppealLabelItem={showAppealLabelItem}
           style={styles.btnPad}
+          hitSlop={big ? HITSLOP_20 : HITSLOP_10}
         />
       </View>
     </View>
diff --git a/src/view/shell/index.tsx b/src/view/shell/index.tsx
index bdba79174..76a7f8fb3 100644
--- a/src/view/shell/index.tsx
+++ b/src/view/shell/index.tsx
@@ -30,7 +30,8 @@ import {useCloseAnyActiveElement} from '#/state/util'
 import * as notifications from 'lib/notifications/notifications'
 import {Outlet as PortalOutlet} from '#/components/Portal'
 import {MutedWordsDialog} from '#/components/dialogs/MutedWords'
-import {useDialogStateContext} from '#/state/dialogs'
+import {useDialogStateContext} from 'state/dialogs'
+import Animated from 'react-native-reanimated'
 
 function ShellInner() {
   const isDrawerOpen = useIsDrawerOpen()
@@ -54,9 +55,9 @@ function ShellInner() {
   const canGoBack = useNavigationState(state => !isStateAtTabRoot(state))
   const {hasSession, currentAccount} = useSession()
   const closeAnyActiveElement = useCloseAnyActiveElement()
+  const {importantForAccessibility} = useDialogStateContext()
   // start undefined
   const currentAccountDid = React.useRef<string | undefined>(undefined)
-  const {openDialogs} = useDialogStateContext()
 
   React.useEffect(() => {
     let listener = {remove() {}}
@@ -80,19 +81,9 @@ function ShellInner() {
     }
   }, [currentAccount])
 
-  /**
-   * The counterpart to `accessibilityViewIsModal` for Android. This property
-   * applies to the parent of all non-modal views, and prevents TalkBack from
-   * navigating within content beneath an open dialog.
-   *
-   * @see https://reactnative.dev/docs/accessibility#importantforaccessibility-android
-   */
-  const importantForAccessibility =
-    openDialogs.length > 0 ? 'no-hide-descendants' : undefined
-
   return (
     <>
-      <View
+      <Animated.View
         style={containerPadding}
         importantForAccessibility={importantForAccessibility}>
         <ErrorBoundary>
@@ -106,7 +97,7 @@ function ShellInner() {
             <TabsNavigator />
           </Drawer>
         </ErrorBoundary>
-      </View>
+      </Animated.View>
       <Composer winHeight={winDim.height} />
       <ModalsContainer />
       <MutedWordsDialog />