diff --git a/assets/icons/bubbleQuestion_stroke2_corner0_rounded.svg b/assets/icons/bubbleQuestion_stroke2_corner0_rounded.svg
new file mode 100644
index 0000000000..0bfcc48a0e
--- /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 0000000000..1fbcfc5711
--- /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 0000000000..81357a12e3
--- /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 0000000000..d4b32f81fe
--- /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 0000000000..d5b6f13d5f
--- /dev/null
+++ b/assets/icons/warning_stroke2_corner0_rounded.svg
@@ -0,0 +1 @@
+
diff --git a/bskyweb/templates/base.html b/bskyweb/templates/base.html
index 55447552ea..c7c5ec0f0b 100644
--- a/bskyweb/templates/base.html
+++ b/bskyweb/templates/base.html
@@ -43,6 +43,9 @@
height: calc(100% + env(safe-area-inset-top));
scrollbar-gutter: stable both-edges;
}
+ html, body {
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
+ }
/* Buttons and inputs have a font set by UA, so we'll have to reset that */
button, input, textarea {
diff --git a/src/components/Button.tsx b/src/components/Button.tsx
index 3dff417793..a8805c9c24 100644
--- a/src/components/Button.tsx
+++ b/src/components/Button.tsx
@@ -27,7 +27,7 @@ export type ButtonColor =
| 'gradient_sunset'
| 'gradient_nordic'
| 'gradient_bonfire'
-export type ButtonSize = 'tiny' | 'small' | 'large'
+export type ButtonSize = 'tiny' | 'small' | 'medium' | 'large'
export type ButtonShape = 'round' | 'square' | 'default'
export type VariantProps = {
/**
@@ -274,6 +274,8 @@ export function Button({
if (shape === 'default') {
if (size === 'large') {
baseStyles.push({paddingVertical: 15}, a.px_2xl, a.rounded_sm, a.gap_md)
+ } else if (size === 'medium') {
+ baseStyles.push({paddingVertical: 12}, a.px_2xl, a.rounded_sm, a.gap_md)
} else if (size === 'small') {
baseStyles.push({paddingVertical: 9}, a.px_lg, a.rounded_sm, a.gap_sm)
} else if (size === 'tiny') {
diff --git a/src/components/Dialog/context.ts b/src/components/Dialog/context.ts
index 9b571e8e9c..859f8edd77 100644
--- a/src/components/Dialog/context.ts
+++ b/src/components/Dialog/context.ts
@@ -21,8 +21,7 @@ export function useDialogControl(): DialogOuterProps['control'] {
open: () => {},
close: () => {},
})
- const {activeDialogs, openDialogs} = useDialogStateContext()
- const isOpen = openDialogs.includes(id)
+ const {activeDialogs} = useDialogStateContext()
React.useEffect(() => {
activeDialogs.current.set(id, control)
@@ -36,7 +35,6 @@ export function useDialogControl(): DialogOuterProps['control'] {
() => ({
id,
ref: control,
- isOpen,
open: () => {
control.current.open()
},
@@ -44,6 +42,6 @@ export function useDialogControl(): DialogOuterProps['control'] {
control.current.close(cb)
},
}),
- [id, control, isOpen],
+ [id, control],
)
}
diff --git a/src/components/Dialog/types.ts b/src/components/Dialog/types.ts
index fa9398fe05..4fc60ec394 100644
--- a/src/components/Dialog/types.ts
+++ b/src/components/Dialog/types.ts
@@ -22,7 +22,7 @@ export type DialogControlRefProps = {
export type DialogControlProps = DialogControlRefProps & {
id: string
ref: React.RefObject
- isOpen: boolean
+ isOpen?: boolean
}
export type DialogContextProps = {
diff --git a/src/components/Menu/index.web.tsx b/src/components/Menu/index.web.tsx
index ca2e40566d..054e51b01e 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/Prompt.tsx b/src/components/Prompt.tsx
index 8e55bd8347..3b245c440f 100644
--- a/src/components/Prompt.tsx
+++ b/src/components/Prompt.tsx
@@ -3,7 +3,7 @@ import {View, PressableProps} from 'react-native'
import {msg} from '@lingui/macro'
import {useLingui} from '@lingui/react'
-import {useTheme, atoms as a} from '#/alf'
+import {useTheme, atoms as a, useBreakpoints} from '#/alf'
import {Text} from '#/components/Typography'
import {Button} from '#/components/Button'
@@ -25,6 +25,7 @@ export function Outer({
}: React.PropsWithChildren<{
control: Dialog.DialogOuterProps['control']
}>) {
+ const {gtMobile} = useBreakpoints()
const titleId = React.useId()
const descriptionId = React.useId()
@@ -38,12 +39,12 @@ export function Outer({
-
+ style={[gtMobile ? {width: 'auto', maxWidth: 400} : a.w_full]}>
{children}
-
+
)
@@ -71,8 +72,16 @@ export function Description({children}: React.PropsWithChildren<{}>) {
}
export function Actions({children}: React.PropsWithChildren<{}>) {
+ const {gtMobile} = useBreakpoints()
+
return (
-
+
{children}
)
@@ -82,12 +91,13 @@ export function Cancel({
children,
}: React.PropsWithChildren<{onPress?: PressableProps['onPress']}>) {
const {_} = useLingui()
+ const {gtMobile} = useBreakpoints()
const {close} = Dialog.useDialogContext()
return (
)}
- {uiState.inviteCode ? (
+ {!uiState.isInviteCodeRequired || uiState.inviteCode ? (
<>
) {
+ 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 00dfe6a7ac..d0daa99ce9 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'
@@ -35,9 +37,20 @@ import {useSession} from '#/state/session'
import {isWeb} from '#/platform/detection'
import {richTextToString} from '#/lib/strings/rich-text-helpers'
import {ReportDialog, useReportDialogControl} from '#/components/ReportDialog'
-import {NEW_REPORT_DIALOG_ENABLED} from '#/lib/build-flags'
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'
+
let PostDropdownBtn = ({
testID,
postAuthor,
@@ -46,6 +59,7 @@ let PostDropdownBtn = ({
record,
richText,
style,
+ hitSlop,
}: {
testID: string
postAuthor: AppBskyActorDefs.ProfileViewBasic
@@ -54,9 +68,11 @@ let PostDropdownBtn = ({
record: AppBskyFeedPost.Record
richText: RichTextAPI
style?: StyleProp
+ 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()
@@ -152,149 +168,6 @@ 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() {
- if (NEW_REPORT_DIALOG_ENABLED) {
- control.open()
- } else {
- 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'],
- },
- },
- ].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`)}
+
+
+ )}
+
+
+
>
)
diff --git a/src/view/com/util/post-ctrls/PostCtrls.tsx b/src/view/com/util/post-ctrls/PostCtrls.tsx
index 422e23d660..ca4628e5ee 100644
--- a/src/view/com/util/post-ctrls/PostCtrls.tsx
+++ b/src/view/com/util/post-ctrls/PostCtrls.tsx
@@ -210,9 +210,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}>
@@ -228,6 +226,7 @@ let PostCtrls = ({
record={record}
richText={richText}
style={styles.btnPad}
+ hitSlop={big ? HITSLOP_20 : HITSLOP_10}
/>
diff --git a/src/view/shell/index.tsx b/src/view/shell/index.tsx
index 55a21e53f8..702ddf1542 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(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 (
<>
-
@@ -106,7 +97,7 @@ function ShellInner() {
-
+
diff --git a/web/index.html b/web/index.html
index b6e01ba4c2..de0abfc91b 100644
--- a/web/index.html
+++ b/web/index.html
@@ -47,6 +47,9 @@
height: calc(100% + env(safe-area-inset-top));
scrollbar-gutter: stable both-edges;
}
+ html, body {
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
+ }
/* Buttons and inputs have a font set by UA, so we'll have to reset that */
button, input, textarea {