diff --git a/assets/icons/bubbleQuestion_stroke2_corner0_rounded.svg b/assets/icons/bubbleQuestion_stroke2_corner0_rounded.svg
new file mode 100644
index 00000000..0bfcc48a
--- /dev/null
+++ b/assets/icons/bubbleQuestion_stroke2_corner0_rounded.svg
@@ -0,0 +1 @@
+
diff --git a/assets/icons/filter_stroke2_corner0_rounded.svg b/assets/icons/filter_stroke2_corner0_rounded.svg
new file mode 100644
index 00000000..1fbcfc57
--- /dev/null
+++ b/assets/icons/filter_stroke2_corner0_rounded.svg
@@ -0,0 +1 @@
+
diff --git a/assets/icons/speakerVolumeFull_stroke2_corner0_rounded.svg b/assets/icons/speakerVolumeFull_stroke2_corner0_rounded.svg
new file mode 100644
index 00000000..81357a12
--- /dev/null
+++ b/assets/icons/speakerVolumeFull_stroke2_corner0_rounded.svg
@@ -0,0 +1 @@
+
diff --git a/assets/icons/trash_stroke2_corner0_rounded.svg b/assets/icons/trash_stroke2_corner0_rounded.svg
new file mode 100644
index 00000000..d4b32f81
--- /dev/null
+++ b/assets/icons/trash_stroke2_corner0_rounded.svg
@@ -0,0 +1 @@
+
diff --git a/assets/icons/warning_stroke2_corner0_rounded.svg b/assets/icons/warning_stroke2_corner0_rounded.svg
new file mode 100644
index 00000000..d5b6f13d
--- /dev/null
+++ b/assets/icons/warning_stroke2_corner0_rounded.svg
@@ -0,0 +1 @@
+
diff --git a/src/components/Menu/index.web.tsx b/src/components/Menu/index.web.tsx
index ca2e4056..054e51b0 100644
--- a/src/components/Menu/index.web.tsx
+++ b/src/components/Menu/index.web.tsx
@@ -92,10 +92,8 @@ export function Trigger({children, label, style}: TriggerProps) {
accessibilityLabel={label}
onFocus={onFocus}
onBlur={onBlur}
- style={flatten([style, web({outline: 0})])}
- onPointerDown={() => {
- control.open()
- }}
+ style={flatten([style, focused && web({outline: 0})])}
+ onPointerDown={() => control.open()}
{...web({
onMouseEnter,
onMouseLeave,
@@ -131,6 +129,7 @@ export function Outer({children}: React.PropsWithChildren<{}>) {
{children}
+ {/* Disabled until we can fix positioning
) {
.backgroundColor
}
/>
+ */}
)
diff --git a/src/components/icons/Bubble.tsx b/src/components/icons/Bubble.tsx
new file mode 100644
index 00000000..d4e08f6d
--- /dev/null
+++ b/src/components/icons/Bubble.tsx
@@ -0,0 +1,5 @@
+import {createSinglePathSVG} from './TEMPLATE'
+
+export const BubbleQuestion_Stroke2_Corner0_Rounded = createSinglePathSVG({
+ path: 'M5.002 17.036V5h14v12.036h-3.986a1 1 0 0 0-.639.23l-2.375 1.968-2.344-1.965a1 1 0 0 0-.643-.233H5.002ZM20.002 3h-16a1 1 0 0 0-1 1v14.036a1 1 0 0 0 1 1h4.65l2.704 2.266a1 1 0 0 0 1.28.004l2.74-2.27h4.626a1 1 0 0 0 1-1V4a1 1 0 0 0-1-1Zm-7.878 3.663c-1.39 0-2.5 1.135-2.5 2.515a1 1 0 0 0 2 0c0-.294.232-.515.5-.515a.507.507 0 0 1 .489.6.174.174 0 0 1-.027.048 1.1 1.1 0 0 1-.267.226c-.508.345-1.128.923-1.286 1.978a1 1 0 1 0 1.978.297.762.762 0 0 1 .14-.359c.063-.086.155-.169.293-.262.436-.297 1.18-.885 1.18-2.013 0-1.38-1.11-2.515-2.5-2.515ZM12 15.75a1.25 1.25 0 1 1 0-2.5 1.25 1.25 0 0 1 0 2.5Z',
+})
diff --git a/src/components/icons/Filter.tsx b/src/components/icons/Filter.tsx
new file mode 100644
index 00000000..02ac1c71
--- /dev/null
+++ b/src/components/icons/Filter.tsx
@@ -0,0 +1,5 @@
+import {createSinglePathSVG} from './TEMPLATE'
+
+export const Filter_Stroke2_Corner0_Rounded = createSinglePathSVG({
+ path: 'M3 4a1 1 0 0 1 1-1h16a1 1 0 0 1 1 1v4a1 1 0 0 1-.293.707L15 14.414V20a1 1 0 0 1-.758.97l-4 1A1 1 0 0 1 9 21v-6.586L3.293 8.707A1 1 0 0 1 3 8V4Zm2 1v2.586l5.707 5.707A1 1 0 0 1 11 14v5.72l2-.5V14a1 1 0 0 1 .293-.707L19 7.586V5H5Z',
+})
diff --git a/src/components/icons/Speaker.tsx b/src/components/icons/Speaker.tsx
new file mode 100644
index 00000000..365d5e11
--- /dev/null
+++ b/src/components/icons/Speaker.tsx
@@ -0,0 +1,5 @@
+import {createSinglePathSVG} from './TEMPLATE'
+
+export const SpeakerVolumeFull_Stroke2_Corner0_Rounded = createSinglePathSVG({
+ path: 'M12.472 3.118A1 1 0 0 1 13 4v16a1 1 0 0 1-1.555.832L5.697 17H2a1 1 0 0 1-1-1V8a1 1 0 0 1 1-1h3.697l5.748-3.832a1 1 0 0 1 1.027-.05ZM11 5.868 6.555 8.833A1 1 0 0 1 6 9H3v6h3a1 1 0 0 1 .555.168L11 18.131V5.87Zm7.364-1.645a1 1 0 0 1 1.414 0A10.969 10.969 0 0 1 23 12c0 3.037-1.232 5.788-3.222 7.778a1 1 0 1 1-1.414-1.414A8.969 8.969 0 0 0 21 12a8.969 8.969 0 0 0-2.636-6.364 1 1 0 0 1 0-1.414Zm-3.182 3.181a1 1 0 0 1 1.414 0A6.483 6.483 0 0 1 18.5 12a6.483 6.483 0 0 1-1.904 4.597 1 1 0 0 1-1.414-1.415A4.483 4.483 0 0 0 16.5 12a4.483 4.483 0 0 0-1.318-3.182 1 1 0 0 1 0-1.414Z',
+})
diff --git a/src/components/icons/Trash.tsx b/src/components/icons/Trash.tsx
new file mode 100644
index 00000000..d09a3311
--- /dev/null
+++ b/src/components/icons/Trash.tsx
@@ -0,0 +1,5 @@
+import {createSinglePathSVG} from './TEMPLATE'
+
+export const Trash_Stroke2_Corner0_Rounded = createSinglePathSVG({
+ path: 'M7.416 5H3a1 1 0 0 0 0 2h1.064l.938 14.067A1 1 0 0 0 6 22h12a1 1 0 0 0 .998-.933L19.936 7H21a1 1 0 1 0 0-2h-4.416a5 5 0 0 0-9.168 0Zm2.348 0h4.472c-.55-.614-1.348-1-2.236-1-.888 0-1.687.386-2.236 1Zm6.087 2H6.07l.867 13h10.128l.867-13h-2.036a1 1 0 0 1-.044 0ZM10 10a1 1 0 0 1 1 1v5a1 1 0 1 1-2 0v-5a1 1 0 0 1 1-1Zm4 0a1 1 0 0 1 1 1v5a1 1 0 1 1-2 0v-5a1 1 0 0 1 1-1Z',
+})
diff --git a/src/components/icons/Warning.tsx b/src/components/icons/Warning.tsx
new file mode 100644
index 00000000..fc84b289
--- /dev/null
+++ b/src/components/icons/Warning.tsx
@@ -0,0 +1,5 @@
+import {createSinglePathSVG} from './TEMPLATE'
+
+export const Warning_Stroke2_Corner0_Rounded = createSinglePathSVG({
+ path: 'M11.14 4.494a.995.995 0 0 1 1.72 0l7.001 12.008a.996.996 0 0 1-.86 1.498H4.999a.996.996 0 0 1-.86-1.498L11.14 4.494Zm3.447-1.007c-1.155-1.983-4.019-1.983-5.174 0L2.41 15.494C1.247 17.491 2.686 20 4.998 20h14.004c2.312 0 3.751-2.509 2.587-4.506L14.587 3.487ZM13 9.019a1 1 0 1 0-2 0v2.994a1 1 0 1 0 2 0V9.02Zm-1 4.731a1.25 1.25 0 1 0 0 2.5 1.25 1.25 0 0 0 0-2.5Z',
+})
diff --git a/src/view/com/util/EventStopper.tsx b/src/view/com/util/EventStopper.tsx
index e743e89b..8f5f5cf5 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}
diff --git a/src/view/com/util/forms/PostDropdownBtn.tsx b/src/view/com/util/forms/PostDropdownBtn.tsx
index 09850a7f..6f2ae55b 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
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 (
-
-
-
-
-
-
+
+
+
+ {({props, state}) => {
+ const styles = [
+ style,
+ a.rounded_full,
+ (state.hovered || state.focused || state.pressed) && [
+ web({outline: 0}),
+ alf.atoms.bg_contrast_25,
+ ],
+ ]
+ return isWeb ? (
+
+
+
+ ) : (
+
+
+
+ )
+ }}
+
+
+
+
+
+ {_(msg`Translate`)}
+
+
+
+
+ {_(msg`Copy post text`)}
+
+
+
+ {
+ const url = toShareUrl(href)
+ shareUrl(url)
+ }}>
+
+ {isWeb ? _(msg`Copy link to post`) : _(msg`Share`)}
+
+
+
+
+
+ {hasSession && (
+ <>
+
+
+
+
+
+ {isThreadMuted
+ ? _(msg`Unmute thread`)
+ : _(msg`Mute thread`)}
+
+
+
+
+ mutedWordsDialogControl.open()}>
+ {_(msg`Mute words & tags`)}
+
+
+
+ {!isAuthor && !isPostHidden && (
+ {
+ openModal({
+ name: 'confirm',
+ title: _(msg`Hide this post?`),
+ message: _(
+ msg`This will hide this post from your feeds.`,
+ ),
+ onPressConfirm: onHidePost,
+ })
+ }}>
+ {_(msg`Hide post`)}
+
+
+ )}
+
+ >
+ )}
+
+
+
+
+ {!isAuthor && (
+ {
+ openModal({
+ name: 'report',
+ uri: postUri,
+ cid: postCid,
+ })
+ }}>
+ {_(msg`Report post`)}
+
+
+ )}
+
+ {isAuthor && (
+ {
+ openModal({
+ name: 'confirm',
+ title: _(msg`Delete this post?`),
+ message: _(msg`Are you sure? This cannot be undone.`),
+ onPressConfirm: onDeletePost,
+ })
+ }}>
+ {_(msg`Delete post`)}
+
+
+ )}
+
+ {showAppealLabelItem && (
+ <>
+
+
+ {
+ openModal({
+ name: 'appeal-label',
+ uri: postUri,
+ cid: postCid,
+ })
+ }}>
+
+ {_(msg`Appeal content warning`)}
+
+
+
+ >
+ )}
+
+
+
)
}
diff --git a/src/view/com/util/post-ctrls/PostCtrls.tsx b/src/view/com/util/post-ctrls/PostCtrls.tsx
index bd21ddda..b1ec32b3 100644
--- a/src/view/com/util/post-ctrls/PostCtrls.tsx
+++ b/src/view/com/util/post-ctrls/PostCtrls.tsx
@@ -231,6 +231,7 @@ let PostCtrls = ({
richText={richText}
showAppealLabelItem={showAppealLabelItem}
style={styles.btnPad}
+ hitSlop={big ? HITSLOP_20 : HITSLOP_10}
/>