Fix draft button behaviour when publishing, tweak buttons

This commit is contained in:
Samuel Newman
2026-01-16 13:36:22 +02:00
parent e5b700a3d2
commit ad848c0000
+75 -86
View File
@@ -50,7 +50,6 @@ import {
type BskyAgent, type BskyAgent,
type RichText, type RichText,
} from '@atproto/api' } from '@atproto/api'
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
import {msg, plural, Trans} from '@lingui/macro' import {msg, plural, Trans} from '@lingui/macro'
import {useLingui} from '@lingui/react' import {useLingui} from '@lingui/react'
import {useNavigation} from '@react-navigation/native' import {useNavigation} from '@react-navigation/native'
@@ -68,7 +67,6 @@ import {
} from '#/lib/constants' } from '#/lib/constants'
import {useIsKeyboardVisible} from '#/lib/hooks/useIsKeyboardVisible' import {useIsKeyboardVisible} from '#/lib/hooks/useIsKeyboardVisible'
import {useNonReactiveCallback} from '#/lib/hooks/useNonReactiveCallback' import {useNonReactiveCallback} from '#/lib/hooks/useNonReactiveCallback'
import {usePalette} from '#/lib/hooks/usePalette'
import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries' import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries'
import {mimeToExt} from '#/lib/media/video/util' import {mimeToExt} from '#/lib/media/video/util'
import {type NavigationProp} from '#/lib/routes/types' import {type NavigationProp} from '#/lib/routes/types'
@@ -117,9 +115,9 @@ import {ThreadgateBtn} from '#/view/com/composer/threadgate/ThreadgateBtn'
import {SubtitleDialogBtn} from '#/view/com/composer/videos/SubtitleDialog' import {SubtitleDialogBtn} from '#/view/com/composer/videos/SubtitleDialog'
import {VideoPreview} from '#/view/com/composer/videos/VideoPreview' import {VideoPreview} from '#/view/com/composer/videos/VideoPreview'
import {VideoTranscodeProgress} from '#/view/com/composer/videos/VideoTranscodeProgress' import {VideoTranscodeProgress} from '#/view/com/composer/videos/VideoTranscodeProgress'
import {Text} from '#/view/com/util/text/Text'
import {UserAvatar} from '#/view/com/util/UserAvatar' import {UserAvatar} from '#/view/com/util/UserAvatar'
import {atoms as a, native, useTheme, web} from '#/alf' import {atoms as a, native, useTheme, web} from '#/alf'
import {Admonition} from '#/components/Admonition'
import {Button, ButtonIcon, ButtonText} from '#/components/Button' import {Button, ButtonIcon, ButtonText} from '#/components/Button'
import {CircleInfo_Stroke2_Corner0_Rounded as CircleInfoIcon} from '#/components/icons/CircleInfo' import {CircleInfo_Stroke2_Corner0_Rounded as CircleInfoIcon} from '#/components/icons/CircleInfo'
import {EmojiArc_Stroke2_Corner0_Rounded as EmojiSmileIcon} from '#/components/icons/Emoji' import {EmojiArc_Stroke2_Corner0_Rounded as EmojiSmileIcon} from '#/components/icons/Emoji'
@@ -128,7 +126,7 @@ import {TimesLarge_Stroke2_Corner0_Rounded as XIcon} from '#/components/icons/Ti
import {LazyQuoteEmbed} from '#/components/Post/Embed/LazyQuoteEmbed' import {LazyQuoteEmbed} from '#/components/Post/Embed/LazyQuoteEmbed'
import * as Prompt from '#/components/Prompt' import * as Prompt from '#/components/Prompt'
import * as Toast from '#/components/Toast' import * as Toast from '#/components/Toast'
import {Text as NewText} from '#/components/Typography' import {Text} from '#/components/Typography'
import {useAnalytics} from '#/analytics' import {useAnalytics} from '#/analytics'
import {IS_ANDROID, IS_IOS, IS_NATIVE, IS_WEB} from '#/env' import {IS_ANDROID, IS_IOS, IS_NATIVE, IS_WEB} from '#/env'
import {BottomSheetPortalProvider} from '../../../../modules/bottom-sheet' import {BottomSheetPortalProvider} from '../../../../modules/bottom-sheet'
@@ -1174,7 +1172,7 @@ function ComposerTopBar({
topBarAnimatedStyle: StyleProp<ViewStyle> topBarAnimatedStyle: StyleProp<ViewStyle>
children?: React.ReactNode children?: React.ReactNode
}) { }) {
const pal = usePalette('default') const t = useTheme()
const {_} = useLingui() const {_} = useLingui()
return ( return (
<Animated.View <Animated.View
@@ -1187,7 +1185,8 @@ function ComposerTopBar({
color="primary" color="primary"
shape="default" shape="default"
size="small" size="small"
style={[a.rounded_full, a.py_sm, {paddingLeft: 7, paddingRight: 7}]} style={[{paddingLeft: 7, paddingRight: 7}]}
hoverStyle={[a.bg_transparent, {opacity: 0.5}]}
onPress={onCancel} onPress={onCancel}
accessibilityHint={_( accessibilityHint={_(
msg`Closes post composer and discards post draft`, msg`Closes post composer and discards post draft`,
@@ -1197,77 +1196,75 @@ function ComposerTopBar({
</ButtonText> </ButtonText>
</Button> </Button>
<View style={a.flex_1} /> <View style={a.flex_1} />
{/* Drafts not supported for replies */}
{!isReply && (
<DraftsButton
onSelectDraft={onSelectDraft}
onSaveDraft={onSaveDraft}
onDiscard={onDiscard}
isEmpty={isEmpty}
isDirty={isDirty}
isEditingDraft={isEditingDraft}
/>
)}
{isPublishing ? ( {isPublishing ? (
<> <>
<Text style={pal.textLight}>{publishingStage}</Text> <Text style={[t.atoms.text_contrast_low]}>{publishingStage}</Text>
<View style={styles.postBtn}> <View style={styles.postBtn}>
<ActivityIndicator /> <ActivityIndicator />
</View> </View>
</> </>
) : ( ) : (
<Button <>
testID="composerPublishBtn" {!isReply && (
label={ <DraftsButton
isReply onSelectDraft={onSelectDraft}
? isThread onSaveDraft={onSaveDraft}
? _( onDiscard={onDiscard}
msg({ isEmpty={isEmpty}
message: 'Publish replies', isDirty={isDirty}
comment: isEditingDraft={isEditingDraft}
'Accessibility label for button to publish multiple replies in a thread', />
}), )}
) <Button
: _( testID="composerPublishBtn"
msg({ label={
message: 'Publish reply', isReply
comment: ? isThread
'Accessibility label for button to publish a single reply', ? _(
}), msg({
) message: 'Publish replies',
: isThread comment:
? _( 'Accessibility label for button to publish multiple replies in a thread',
msg({ }),
message: 'Publish posts', )
comment: : _(
'Accessibility label for button to publish multiple posts in a thread', msg({
}), message: 'Publish reply',
) comment:
: _( 'Accessibility label for button to publish a single reply',
msg({ }),
message: 'Publish post', )
comment: : isThread
'Accessibility label for button to publish a single post', ? _(
}), msg({
) message: 'Publish posts',
} comment:
variant="solid" 'Accessibility label for button to publish multiple posts in a thread',
color="primary" }),
shape="default" )
size="small" : _(
style={[a.rounded_full, a.py_sm]} msg({
onPress={onPublish} message: 'Publish post',
disabled={!canPost || isPublishQueued}> comment:
<ButtonText style={[a.text_md]}> 'Accessibility label for button to publish a single post',
{isReply ? ( }),
<Trans context="action">Reply</Trans> )
) : isThread ? ( }
<Trans context="action">Post All</Trans> color="primary"
) : ( size="small"
<Trans context="action">Post</Trans> onPress={onPublish}
)} disabled={!canPost || isPublishQueued}>
</ButtonText> <ButtonText style={[a.text_md]}>
</Button> {isReply ? (
<Trans context="action">Reply</Trans>
) : isThread ? (
<Trans context="action">Post All</Trans>
) : (
<Trans context="action">Post</Trans>
)}
</ButtonText>
</Button>
</>
)} )}
</View> </View>
{children} {children}
@@ -1276,18 +1273,10 @@ function ComposerTopBar({
} }
function AltTextReminder({error}: {error: string}) { function AltTextReminder({error}: {error: string}) {
const pal = usePalette('default')
return ( return (
<View style={[styles.reminderLine, pal.viewLight]}> <Admonition type="error" style={[a.mt_2xs, a.mb_sm, a.mx_lg]}>
<View style={styles.errorIcon}> {error}
<FontAwesomeIcon </Admonition>
icon="exclamation"
style={{color: colors.red4}}
size={10}
/>
</View>
<Text style={[pal.text, a.flex_1]}>{error}</Text>
</View>
) )
} }
@@ -1954,9 +1943,9 @@ function ErrorBanner({
]}> ]}>
<View style={[a.relative, a.flex_row, a.gap_sm, {paddingRight: 48}]}> <View style={[a.relative, a.flex_row, a.gap_sm, {paddingRight: 48}]}>
<CircleInfoIcon fill={t.palette.negative_400} /> <CircleInfoIcon fill={t.palette.negative_400} />
<NewText style={[a.flex_1, a.leading_snug, {paddingTop: 1}]}> <Text style={[a.flex_1, a.leading_snug, {paddingTop: 1}]}>
{error} {error}
</NewText> </Text>
<Button <Button
label={_(msg`Dismiss error`)} label={_(msg`Dismiss error`)}
size="tiny" size="tiny"
@@ -1969,7 +1958,7 @@ function ErrorBanner({
</Button> </Button>
</View> </View>
{videoError && videoState.jobId && ( {videoError && videoState.jobId && (
<NewText <Text
style={[ style={[
{paddingLeft: 28}, {paddingLeft: 28},
a.text_xs, a.text_xs,
@@ -1978,7 +1967,7 @@ function ErrorBanner({
t.atoms.text_contrast_low, t.atoms.text_contrast_low,
]}> ]}>
<Trans>Job ID: {videoState.jobId}</Trans> <Trans>Job ID: {videoState.jobId}</Trans>
</NewText> </Text>
)} )}
</View> </View>
</Animated.View> </Animated.View>
@@ -2066,7 +2055,7 @@ function VideoUploadToolbar({state}: {state: VideoState}) {
progress={wheelProgress} progress={wheelProgress}
/> />
</Animated.View> </Animated.View>
<NewText style={[a.font_semi_bold, a.ml_sm]}>{text}</NewText> <Text style={[a.font_semi_bold, a.ml_sm]}>{text}</Text>
</ToolbarWrapper> </ToolbarWrapper>
) )
} }