Scope fluid zoom transition to alt text dialog only

Revert the fluid sheet transition from all dialogs except the image
alt text dialog, where it works well. Restore Keyboard.dismiss() calls
in LabelsBtn and ThreadgateBtn that were removed for the transitions.

https://claude.ai/code/session_011cNDhEb2cDgg5QbVuzEyH1
This commit is contained in:
Claude
2026-02-28 23:36:26 +00:00
committed by Samuel Newman
parent 84ff3a5684
commit 4fd92766a6
8 changed files with 21 additions and 75 deletions
+3 -24
View File
@@ -1,14 +1,5 @@
import {cloneElement, Fragment, isValidElement, useMemo} from 'react'
import { import {
cloneElement,
Fragment,
isValidElement,
useEffect,
useMemo,
useRef,
useState,
} from 'react'
import {
findNodeHandle,
Pressable, Pressable,
type StyleProp, type StyleProp,
type TextStyle, type TextStyle,
@@ -55,11 +46,9 @@ export function Root({
control?: Dialog.DialogControlProps control?: Dialog.DialogControlProps
}>) { }>) {
const defaultControl = Dialog.useDialogControl() const defaultControl = Dialog.useDialogControl()
const triggerRef = useRef<View>(null)
const context = useMemo<ContextType>( const context = useMemo<ContextType>(
() => ({ () => ({
control: control || defaultControl, control: control || defaultControl,
triggerRef,
}), }),
[control, defaultControl], [control, defaultControl],
) )
@@ -90,7 +79,7 @@ export function Trigger({
pressed, pressed,
}, },
props: { props: {
ref: context.triggerRef, ref: null,
onPress: context.control.open, onPress: context.control.open,
onFocus, onFocus,
onBlur, onBlur,
@@ -106,27 +95,17 @@ export function Trigger({
export function Outer({ export function Outer({
children, children,
showCancel, showCancel,
enableTransition,
}: React.PropsWithChildren<{ }: React.PropsWithChildren<{
showCancel?: boolean showCancel?: boolean
style?: StyleProp<ViewStyle> style?: StyleProp<ViewStyle>
enableTransition?: boolean
}>) { }>) {
const context = useMenuContext() const context = useMenuContext()
const {_} = useLingui() const {_} = useLingui()
const [sourceViewTag, setSourceViewTag] = useState<number>()
useEffect(() => {
if (enableTransition && context.triggerRef.current) {
const tag = findNodeHandle(context.triggerRef.current)
if (tag != null) setSourceViewTag(tag)
}
}, [enableTransition, context.triggerRef])
return ( return (
<Dialog.Outer <Dialog.Outer
control={context.control} control={context.control}
nativeOptions={{preventExpansion: true, sourceViewTag}}> nativeOptions={{preventExpansion: true}}>
<Dialog.Handle /> <Dialog.Handle />
{/* Re-wrap with context since Dialogs are portal-ed to root */} {/* Re-wrap with context since Dialogs are portal-ed to root */}
<Context.Provider value={context}> <Context.Provider value={context}>
+1 -3
View File
@@ -1,4 +1,4 @@
import {forwardRef, useCallback, useId, useMemo, useRef, useState} from 'react' import {forwardRef, useCallback, useId, useMemo, useState} from 'react'
import { import {
Pressable, Pressable,
type StyleProp, type StyleProp,
@@ -62,11 +62,9 @@ export function Root({
}>) { }>) {
const {_} = useLingui() const {_} = useLingui()
const defaultControl = useMenuControl() const defaultControl = useMenuControl()
const triggerRef = useRef<View>(null)
const context = useMemo<ContextType>( const context = useMemo<ContextType>(
() => ({ () => ({
control: control || defaultControl, control: control || defaultControl,
triggerRef,
}), }),
[control, defaultControl], [control, defaultControl],
) )
+1 -3
View File
@@ -3,7 +3,6 @@ import {
type AccessibilityRole, type AccessibilityRole,
type GestureResponderEvent, type GestureResponderEvent,
type PressableProps, type PressableProps,
type View,
} from 'react-native' } from 'react-native'
import type React from 'react' import type React from 'react'
@@ -13,7 +12,6 @@ import {type Props as SVGIconProps} from '#/components/icons/common'
export type ContextType = { export type ContextType = {
control: Dialog.DialogOuterProps['control'] control: Dialog.DialogOuterProps['control']
triggerRef: React.RefObject<View | null>
} }
export type ItemContextType = { export type ItemContextType = {
@@ -63,7 +61,7 @@ export type TriggerChildProps =
* object is empty. * object is empty.
*/ */
props: { props: {
ref: React.RefObject<View | null> ref: null
onPress: () => void onPress: () => void
onFocus: () => void onFocus: () => void
onBlur: () => void onBlur: () => void
+3 -8
View File
@@ -1,5 +1,5 @@
import {memo, useCallback, useRef} from 'react' import {memo, useCallback} from 'react'
import {findNodeHandle, View} from 'react-native' import {View} from 'react-native'
import {msg, plural} from '@lingui/core/macro' import {msg, plural} from '@lingui/core/macro'
import {useLingui} from '@lingui/react' import {useLingui} from '@lingui/react'
import {Trans} from '@lingui/react/macro' import {Trans} from '@lingui/react/macro'
@@ -41,7 +41,6 @@ let RepostButton = ({
const requireAuth = useRequireAuth() const requireAuth = useRequireAuth()
const dialogControl = Dialog.useDialogControl() const dialogControl = Dialog.useDialogControl()
const formatPostStatCount = useFormatPostStatCount() const formatPostStatCount = useFormatPostStatCount()
const btnRef = useRef<View>(null)
const onPress = () => requireAuth(() => dialogControl.open()) const onPress = () => requireAuth(() => dialogControl.open())
@@ -57,7 +56,6 @@ let RepostButton = ({
return ( return (
<> <>
<PostControlButton <PostControlButton
ref={btnRef}
testID="repostBtn" testID="repostBtn"
active={isReposted} active={isReposted}
activeColor={t.palette.positive_500} activeColor={t.palette.positive_500}
@@ -96,10 +94,7 @@ let RepostButton = ({
</PostControlButton> </PostControlButton>
<Dialog.Outer <Dialog.Outer
control={dialogControl} control={dialogControl}
nativeOptions={{ nativeOptions={{preventExpansion: true}}>
preventExpansion: true,
sourceViewTag: findNodeHandle(btnRef.current) ?? undefined,
}}>
<Dialog.Handle /> <Dialog.Handle />
<RepostButtonDialogInner <RepostButtonDialogInner
isReposted={isReposted} isReposted={isReposted}
@@ -77,11 +77,9 @@ export type PostInteractionSettingsFormProps = {
*/ */
export function PostInteractionSettingsControlledDialog({ export function PostInteractionSettingsControlledDialog({
control, control,
sourceViewTag,
...rest ...rest
}: PostInteractionSettingsFormProps & { }: PostInteractionSettingsFormProps & {
control: Dialog.DialogControlProps control: Dialog.DialogControlProps
sourceViewTag?: number
}) { }) {
const ax = useAnalytics() const ax = useAnalytics()
const onClose = useNonReactiveCallback(() => { const onClose = useNonReactiveCallback(() => {
@@ -102,7 +100,6 @@ export function PostInteractionSettingsControlledDialog({
nativeOptions={{ nativeOptions={{
preventExpansion: true, preventExpansion: true,
preventDismiss: rest.isDirty && rest.persist, preventDismiss: rest.isDirty && rest.persist,
sourceViewTag,
}} }}
onClose={onClose}> onClose={onClose}>
<Dialog.Handle /> <Dialog.Handle />
+6 -18
View File
@@ -1,5 +1,4 @@
import {useState} from 'react' import {Keyboard, View} from 'react-native'
import {findNodeHandle, View} from 'react-native'
import {msg} from '@lingui/core/macro' import {msg} from '@lingui/core/macro'
import {useLingui} from '@lingui/react' import {useLingui} from '@lingui/react'
import {Trans} from '@lingui/react/macro' import {Trans} from '@lingui/react/macro'
@@ -30,14 +29,6 @@ export function LabelsBtn({
}) { }) {
const control = Dialog.useDialogControl() const control = Dialog.useDialogControl()
const {_} = useLingui() const {_} = useLingui()
const [sourceViewTag, setSourceViewTag] = useState<number>()
const btnRef = (node: View | null) => {
if (node) {
const tag = findNodeHandle(node)
if (tag != null) setSourceViewTag(tag)
}
}
const hasLabel = labels.length > 0 const hasLabel = labels.length > 0
@@ -60,11 +51,13 @@ export function LabelsBtn({
return ( return (
<> <>
<Button <Button
ref={btnRef}
color="secondary" color="secondary"
size="small" size="small"
testID="labelsBtn" testID="labelsBtn"
onPress={() => control.open()} onPress={() => {
Keyboard.dismiss()
control.open()
}}
label={_(msg`Content warnings`)} label={_(msg`Content warnings`)}
accessibilityHint={_( accessibilityHint={_(
msg`Opens a dialog to add a content warning to your post`, msg`Opens a dialog to add a content warning to your post`,
@@ -80,12 +73,7 @@ export function LabelsBtn({
<ButtonIcon icon={TinyChevronIcon} size="2xs" /> <ButtonIcon icon={TinyChevronIcon} size="2xs" />
</Button> </Button>
<Dialog.Outer <Dialog.Outer control={control} nativeOptions={{preventExpansion: true}}>
control={control}
nativeOptions={{
preventExpansion: true,
sourceViewTag,
}}>
<Dialog.Handle /> <Dialog.Handle />
<DialogInner <DialogInner
labels={labels} labels={labels}
@@ -75,7 +75,7 @@ export function PostLanguageSelect({
<LanguageBtn currentLanguages={currentLanguages} {...props} /> <LanguageBtn currentLanguages={currentLanguages} {...props} />
)} )}
</Menu.Trigger> </Menu.Trigger>
<Menu.Outer enableTransition> <Menu.Outer>
<Menu.Group> <Menu.Group>
{dedupedHistory.map(historyItem => { {dedupedHistory.map(historyItem => {
const langCodes = historyItem.split(',') const langCodes = historyItem.split(',')
@@ -1,10 +1,5 @@
import {useEffect, useMemo, useState} from 'react' import {useEffect, useMemo, useState} from 'react'
import { import {Keyboard, type StyleProp, type ViewStyle} from 'react-native'
findNodeHandle,
type StyleProp,
type View,
type ViewStyle,
} from 'react-native'
import {type AnimatedStyle} from 'react-native-reanimated' import {type AnimatedStyle} from 'react-native-reanimated'
import {type AppBskyFeedPostgate} from '@atproto/api' import {type AppBskyFeedPostgate} from '@atproto/api'
import {msg} from '@lingui/core/macro' import {msg} from '@lingui/core/macro'
@@ -31,6 +26,7 @@ import {Group3_Stroke2_Corner0_Rounded as GroupIcon} from '#/components/icons/Gr
import * as Tooltip from '#/components/Tooltip' import * as Tooltip from '#/components/Tooltip'
import {Text} from '#/components/Typography' import {Text} from '#/components/Typography'
import {useAnalytics} from '#/analytics' import {useAnalytics} from '#/analytics'
import {IS_NATIVE} from '#/env'
import {useThreadgateNudged} from '#/storage/hooks/threadgate-nudged' import {useThreadgateNudged} from '#/storage/hooks/threadgate-nudged'
export function ThreadgateBtn({ export function ThreadgateBtn({
@@ -50,13 +46,6 @@ export function ThreadgateBtn({
const {_} = useLingui() const {_} = useLingui()
const ax = useAnalytics() const ax = useAnalytics()
const control = Dialog.useDialogControl() const control = Dialog.useDialogControl()
const [sourceViewTag, setSourceViewTag] = useState<number>()
const btnRef = (node: View | null) => {
if (node) {
const tag = findNodeHandle(node)
if (tag != null) setSourceViewTag(tag)
}
}
const [threadgateNudged, setThreadgateNudged] = useThreadgateNudged() const [threadgateNudged, setThreadgateNudged] = useThreadgateNudged()
const [showTooltip, setShowTooltip] = useState(false) const [showTooltip, setShowTooltip] = useState(false)
const [tooltipWasShown] = useState(!threadgateNudged) const [tooltipWasShown] = useState(!threadgateNudged)
@@ -84,6 +73,10 @@ export function ThreadgateBtn({
nudged: tooltipWasShown, nudged: tooltipWasShown,
}) })
if (IS_NATIVE && Keyboard.isVisible()) {
Keyboard.dismiss()
}
setShowTooltip(false) setShowTooltip(false)
setThreadgateNudged(true) setThreadgateNudged(true)
@@ -152,7 +145,6 @@ export function ThreadgateBtn({
position="top"> position="top">
<Tooltip.Target> <Tooltip.Target>
<Button <Button
ref={btnRef}
color={showTooltip ? 'primary_subtle' : 'secondary'} color={showTooltip ? 'primary_subtle' : 'secondary'}
size="small" size="small"
testID="openReplyGateButton" testID="openReplyGateButton"
@@ -175,7 +167,6 @@ export function ThreadgateBtn({
<PostInteractionSettingsControlledDialog <PostInteractionSettingsControlledDialog
control={control} control={control}
sourceViewTag={sourceViewTag}
onSave={() => { onSave={() => {
if (persist) { if (persist) {
persistChanges({ persistChanges({