Update presentation of settings menus (#10425)

This commit is contained in:
DS Boyce
2026-05-07 12:00:21 -07:00
committed by GitHub
parent 2907ff0cd8
commit eddbf6ba9f
6 changed files with 56 additions and 31 deletions
-5
View File
@@ -74,11 +74,6 @@
"count": 1 "count": 1
} }
}, },
"src/components/Menu/index.web.tsx": {
"@typescript-eslint/no-explicit-any": {
"count": 1
}
},
"src/components/Menu/types.ts": { "src/components/Menu/types.ts": {
"@typescript-eslint/no-explicit-any": { "@typescript-eslint/no-explicit-any": {
"count": 1 "count": 1
+17 -6
View File
@@ -120,7 +120,14 @@ export function Outer({
) )
} }
export function Item({children, label, style, onPress, ...rest}: ItemProps) { export function Item({
children,
label,
style,
onPress,
destructive = false,
...rest
}: ItemProps) {
const t = useTheme() const t = useTheme()
const context = useMenuContext() const context = useMenuContext()
const {state: focused, onIn: onFocus, onOut: onBlur} = useInteractionState() const {state: focused, onIn: onFocus, onOut: onBlur} = useInteractionState()
@@ -137,7 +144,7 @@ export function Item({children, label, style, onPress, ...rest}: ItemProps) {
accessibilityLabel={label} accessibilityLabel={label}
onFocus={onFocus} onFocus={onFocus}
onBlur={onBlur} onBlur={onBlur}
onPress={async e => { onPress={e => {
if (IS_ANDROID) { if (IS_ANDROID) {
/** /**
* Below fix for iOS doesn't work for Android, this does. * Below fix for iOS doesn't work for Android, this does.
@@ -176,7 +183,8 @@ export function Item({children, label, style, onPress, ...rest}: ItemProps) {
style, style,
(focused || pressed) && !rest.disabled && [t.atoms.bg_contrast_50], (focused || pressed) && !rest.disabled && [t.atoms.bg_contrast_50],
]}> ]}>
<ItemContext.Provider value={{disabled: Boolean(rest.disabled)}}> <ItemContext.Provider
value={{disabled: Boolean(rest.disabled), destructive}}>
{children} {children}
</ItemContext.Provider> </ItemContext.Provider>
</Pressable> </Pressable>
@@ -185,7 +193,7 @@ export function Item({children, label, style, onPress, ...rest}: ItemProps) {
export function ItemText({children, style}: ItemTextProps) { export function ItemText({children, style}: ItemTextProps) {
const t = useTheme() const t = useTheme()
const {disabled} = useMenuItemContext() const {disabled, destructive} = useMenuItemContext()
return ( return (
<Text <Text
numberOfLines={1} numberOfLines={1}
@@ -196,6 +204,7 @@ export function ItemText({children, style}: ItemTextProps) {
a.font_semi_bold, a.font_semi_bold,
t.atoms.text_contrast_high, t.atoms.text_contrast_high,
style, style,
destructive && {color: t.palette.negative_500},
disabled && t.atoms.text_contrast_low, disabled && t.atoms.text_contrast_low,
]}> ]}>
{children} {children}
@@ -205,7 +214,7 @@ export function ItemText({children, style}: ItemTextProps) {
export function ItemIcon({icon: Comp, fill}: ItemIconProps) { export function ItemIcon({icon: Comp, fill}: ItemIconProps) {
const t = useTheme() const t = useTheme()
const {disabled} = useMenuItemContext() const {disabled, destructive} = useMenuItemContext()
return ( return (
<Comp <Comp
size="lg" size="lg"
@@ -214,7 +223,9 @@ export function ItemIcon({icon: Comp, fill}: ItemIconProps) {
? fill({disabled}) ? fill({disabled})
: disabled : disabled
? t.atoms.text_contrast_low.color ? t.atoms.text_contrast_low.color
: t.atoms.text_contrast_medium.color : destructive
? t.palette.negative_500
: t.atoms.text_contrast_medium.color
} }
/> />
) )
+17 -6
View File
@@ -107,7 +107,7 @@ const RadixTriggerPassThrough = forwardRef(
props: { props: {
children: ( children: (
props: RadixPassThroughTriggerProps & { props: RadixPassThroughTriggerProps & {
ref: React.Ref<any> ref: React.Ref<HTMLElement>
}, },
) => React.ReactNode ) => React.ReactNode
}, },
@@ -225,7 +225,14 @@ export function Outer({
) )
} }
export function Item({children, label, onPress, style, ...rest}: ItemProps) { export function Item({
children,
label,
onPress,
style,
destructive = false,
...rest
}: ItemProps) {
const t = useTheme() const t = useTheme()
const {control} = useMenuContext() const {control} = useMenuContext()
const { const {
@@ -278,7 +285,8 @@ export function Item({children, label, onPress, style, ...rest}: ItemProps) {
onMouseEnter, onMouseEnter,
onMouseLeave, onMouseLeave,
})}> })}>
<ItemContext.Provider value={{disabled: Boolean(rest.disabled)}}> <ItemContext.Provider
value={{disabled: Boolean(rest.disabled), destructive}}>
{children} {children}
</ItemContext.Provider> </ItemContext.Provider>
</Pressable> </Pressable>
@@ -288,7 +296,7 @@ export function Item({children, label, onPress, style, ...rest}: ItemProps) {
export function ItemText({children, style}: ItemTextProps) { export function ItemText({children, style}: ItemTextProps) {
const t = useTheme() const t = useTheme()
const {disabled} = useMenuItemContext() const {disabled, destructive} = useMenuItemContext()
return ( return (
<Text <Text
style={[ style={[
@@ -296,6 +304,7 @@ export function ItemText({children, style}: ItemTextProps) {
a.font_semi_bold, a.font_semi_bold,
t.atoms.text_contrast_high, t.atoms.text_contrast_high,
style, style,
destructive && {color: t.palette.negative_500},
disabled && t.atoms.text_contrast_low, disabled && t.atoms.text_contrast_low,
]}> ]}>
{children} {children}
@@ -305,7 +314,7 @@ export function ItemText({children, style}: ItemTextProps) {
export function ItemIcon({icon: Comp, position = 'left', fill}: ItemIconProps) { export function ItemIcon({icon: Comp, position = 'left', fill}: ItemIconProps) {
const t = useTheme() const t = useTheme()
const {disabled} = useMenuItemContext() const {disabled, destructive} = useMenuItemContext()
return ( return (
<View <View
style={[ style={[
@@ -324,7 +333,9 @@ export function ItemIcon({icon: Comp, position = 'left', fill}: ItemIconProps) {
? fill({disabled}) ? fill({disabled})
: disabled : disabled
? t.atoms.text_contrast_low.color ? t.atoms.text_contrast_low.color
: t.atoms.text_contrast_medium.color : destructive
? t.palette.negative_500
: t.atoms.text_contrast_medium.color
} }
/> />
</View> </View>
+2
View File
@@ -15,6 +15,7 @@ export type ContextType = {
export type ItemContextType = { export type ItemContextType = {
disabled: boolean disabled: boolean
destructive: boolean
} }
export type RadixPassThroughTriggerProps = { export type RadixPassThroughTriggerProps = {
@@ -99,6 +100,7 @@ export type ItemProps = React.PropsWithChildren<
ViewStyleProp & { ViewStyleProp & {
label: string label: string
onPress: (e: GestureResponderEvent) => void onPress: (e: GestureResponderEvent) => void
destructive?: boolean
} }
> >
@@ -18,6 +18,7 @@ import {DotGrid3x1_Stroke2_Corner0_Rounded as EllipsisIcon} from '#/components/i
import {Message_Stroke2_Corner0_Rounded as MessageIcon} from '#/components/icons/Message' import {Message_Stroke2_Corner0_Rounded as MessageIcon} from '#/components/icons/Message'
import { import {
Person_Stroke2_Corner2_Rounded as PersonIcon, Person_Stroke2_Corner2_Rounded as PersonIcon,
PersonCheck_Stroke2_Corner0_Rounded as PersonCheck,
PersonX_Stroke2_Corner0_Rounded as PersonXIcon, PersonX_Stroke2_Corner0_Rounded as PersonXIcon,
} from '#/components/icons/Person' } from '#/components/icons/Person'
import * as Menu from '#/components/Menu' import * as Menu from '#/components/Menu'
@@ -180,29 +181,30 @@ export function MemberMenu({
</Menu.Trigger> </Menu.Trigger>
<Menu.Outer> <Menu.Outer>
<Menu.Group> <Menu.Group>
<Menu.Item
label={l`Message ${displayName}`}
onPress={handleMessageMember}>
<Menu.ItemIcon icon={MessageIcon} />
<Menu.ItemText>
<Trans context="action">Message</Trans>
</Menu.ItemText>
</Menu.Item>
<Menu.Item <Menu.Item
label={l`View ${displayName}s profile`} label={l`View ${displayName}s profile`}
onPress={() => { onPress={() => {
navigation.navigate('Profile', {name: profile.did}) navigation.navigate('Profile', {name: profile.did})
}}> }}>
<Menu.ItemIcon icon={PersonIcon} />
<Menu.ItemText> <Menu.ItemText>
<Trans>Go to profile</Trans> <Trans>Go to profile</Trans>
</Menu.ItemText> </Menu.ItemText>
<Menu.ItemIcon icon={PersonIcon} />
</Menu.Item>
<Menu.Item
label={l`Message ${displayName}`}
onPress={handleMessageMember}>
<Menu.ItemText>
<Trans context="action">Message</Trans>
</Menu.ItemText>
<Menu.ItemIcon icon={MessageIcon} />
</Menu.Item> </Menu.Item>
</Menu.Group> </Menu.Group>
<Menu.Divider /> <Menu.Divider />
<Menu.Group> <Menu.Group>
{canBlockMember ? ( {canBlockMember ? (
<Menu.Item <Menu.Item
destructive
label={ label={
profile.viewer?.blocking profile.viewer?.blocking
? l`Unblock ${displayName}` ? l`Unblock ${displayName}`
@@ -213,31 +215,35 @@ export function MemberMenu({
? handleBlockMember ? handleBlockMember
: blockMemberPrompt.open : blockMemberPrompt.open
}> }>
<Menu.ItemIcon
icon={profile.viewer?.blocking ? PersonCheck : PersonXIcon}
/>
<Menu.ItemText> <Menu.ItemText>
<Trans>Block</Trans> {profile.viewer?.blocking ? l`Unblock` : l`Block`}
</Menu.ItemText> </Menu.ItemText>
<Menu.ItemIcon icon={PersonXIcon} />
</Menu.Item> </Menu.Item>
) : null} ) : null}
{canRemoveMember ? ( {canRemoveMember ? (
<Menu.Item <Menu.Item
destructive
label={l`Remove ${displayName} from this group chat`} label={l`Remove ${displayName} from this group chat`}
onPress={() => removeMembers({members: [profile.did]})}> onPress={() => removeMembers({members: [profile.did]})}>
<Menu.ItemIcon icon={ArrowBoxLeftIcon} />
<Menu.ItemText> <Menu.ItemText>
<Trans>Remove from chat</Trans> <Trans>Remove from chat</Trans>
</Menu.ItemText> </Menu.ItemText>
<Menu.ItemIcon icon={ArrowBoxLeftIcon} />
</Menu.Item> </Menu.Item>
) : null} ) : null}
{canUninviteMember ? ( {canUninviteMember ? (
<Menu.Item <Menu.Item
destructive
label={l`Uninvite ${displayName} from this group chat`} label={l`Uninvite ${displayName} from this group chat`}
// TODO Need to wire up the uninvite flow. -dsb // TODO Need to wire up the uninvite flow. -dsb
onPress={() => {}}> onPress={() => {}}>
<Menu.ItemIcon icon={ArrowBoxLeftIcon} />
<Menu.ItemText> <Menu.ItemText>
<Trans>Uninvite</Trans> <Trans>Uninvite</Trans>
</Menu.ItemText> </Menu.ItemText>
<Menu.ItemIcon icon={ArrowBoxLeftIcon} />
</Menu.Item> </Menu.Item>
) : null} ) : null}
</Menu.Group> </Menu.Group>
@@ -595,7 +595,7 @@ function SettingsButton({
a.font_medium, a.font_medium,
a.text_center, a.text_center,
a.pt_xs, a.pt_xs,
t.atoms.text_contrast_medium, t.atoms.text,
]}> ]}>
{text} {text}
</Text> </Text>