diff options
author | Eric Bailey <git@esb.lol> | 2025-08-26 11:20:04 -0500 |
---|---|---|
committer | GitHub <noreply@github.com> | 2025-08-26 11:20:04 -0500 |
commit | 8ec20026c042e1f26224ef2967dad6f0386e1eca (patch) | |
tree | 751add9e13a9c631724cc1c7ec120bdbc4f3a318 /src/view | |
parent | acd7211b357f2bfc74bf0828994e12f0c41d39d5 (diff) | |
download | voidsky-8ec20026c042e1f26224ef2967dad6f0386e1eca.tar.zst |
Yeah toast (#8878)
* Split out into macro component * Add Action component * Add fallback * add button to view post after sending * Dismiss toast when clicking action button --------- Co-authored-by: Samuel Newman <mozzius@protonmail.com>
Diffstat (limited to 'src/view')
-rw-r--r-- | src/view/com/composer/Composer.tsx | 39 | ||||
-rw-r--r-- | src/view/screens/Storybook/Toasts.tsx | 89 |
2 files changed, 102 insertions, 26 deletions
diff --git a/src/view/com/composer/Composer.tsx b/src/view/com/composer/Composer.tsx index d0dbdfaba..7d4eb8ca7 100644 --- a/src/view/com/composer/Composer.tsx +++ b/src/view/com/composer/Composer.tsx @@ -46,12 +46,14 @@ import { AppBskyFeedDefs, type AppBskyFeedGetPostThread, AppBskyUnspeccedDefs, + AtUri, type BskyAgent, type RichText, } from '@atproto/api' import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' import {msg, plural, Trans} from '@lingui/macro' import {useLingui} from '@lingui/react' +import {useNavigation} from '@react-navigation/native' import {useQueryClient} from '@tanstack/react-query' import * as apilib from '#/lib/api/index' @@ -70,6 +72,7 @@ import {useNonReactiveCallback} from '#/lib/hooks/useNonReactiveCallback' import {usePalette} from '#/lib/hooks/usePalette' import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries' import {mimeToExt} from '#/lib/media/video/util' +import {type NavigationProp} from '#/lib/routes/types' import {logEvent} from '#/lib/statsig/statsig' import {cleanError} from '#/lib/strings/errors' import {colors} from '#/lib/styles' @@ -123,12 +126,13 @@ import {Text} from '#/view/com/util/text/Text' import {UserAvatar} from '#/view/com/util/UserAvatar' import {atoms as a, native, useTheme, web} from '#/alf' import {Button, ButtonIcon, ButtonText} from '#/components/Button' +import {CircleCheck_Stroke2_Corner0_Rounded as CircleCheckIcon} from '#/components/icons/CircleCheck' import {CircleInfo_Stroke2_Corner0_Rounded as CircleInfo} from '#/components/icons/CircleInfo' import {EmojiArc_Stroke2_Corner0_Rounded as EmojiSmile} from '#/components/icons/Emoji' import {TimesLarge_Stroke2_Corner0_Rounded as X} from '#/components/icons/Times' import {LazyQuoteEmbed} from '#/components/Post/Embed/LazyQuoteEmbed' 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 {BottomSheetPortalProvider} from '../../../../modules/bottom-sheet' import { @@ -188,6 +192,7 @@ export const ComposePost = ({ const {closeAllDialogs} = useDialogStateControlContext() const {closeAllModals} = useModalControls() const {data: preferences} = usePreferencesQuery() + const navigation = useNavigation<NavigationProp>() const [isKeyboardVisible] = useIsKeyboardVisible({iosUseWillEvents: true}) const [isPublishing, setIsPublishing] = useState(false) @@ -521,12 +526,27 @@ export const ComposePost = ({ onPostSuccess?.(postSuccessData) } onClose() - toast.show( - thread.posts.length > 1 - ? _(msg`Your posts have been published`) - : replyTo - ? _(msg`Your reply has been published`) - : _(msg`Your post has been published`), + Toast.show( + <Toast.Outer type="success"> + <Toast.Icon icon={CircleCheckIcon} /> + <Toast.Text> + {thread.posts.length > 1 + ? _(msg`Your posts were sent`) + : replyTo + ? _(msg`Your reply was sent`) + : _(msg`Your post was sent`)} + </Toast.Text> + {postUri && ( + <Toast.Action + label={_(msg`View post`)} + onPress={() => { + const {host: name, rkey} = new AtUri(postUri) + navigation.navigate('PostThread', {name, rkey}) + }}> + View + </Toast.Action> + )} + </Toast.Outer>, {type: 'success'}, ) }, [ @@ -543,6 +563,7 @@ export const ComposePost = ({ replyTo, setLangPrefs, queryClient, + navigation, ]) // Preserves the referential identity passed to each post item. @@ -826,7 +847,7 @@ let ComposerPost = React.memo(function ComposerPost({ if (isNative) return // web only const [mimeType] = uri.slice('data:'.length).split(';') if (!SUPPORTED_MIME_TYPES.includes(mimeType as SupportedMimeTypes)) { - toast.show(_(msg`Unsupported video type: ${mimeType}`), { + Toast.show(_(msg`Unsupported video type: ${mimeType}`), { type: 'error', }) return @@ -1362,7 +1383,7 @@ function ComposerFooter({ } errors.map(error => { - toast.show(error, { + Toast.show(error, { type: 'warning', }) }) diff --git a/src/view/screens/Storybook/Toasts.tsx b/src/view/screens/Storybook/Toasts.tsx index 98d5b05e3..319f88e21 100644 --- a/src/view/screens/Storybook/Toasts.tsx +++ b/src/view/screens/Storybook/Toasts.tsx @@ -2,74 +2,129 @@ import {Pressable, View} from 'react-native' import {show as deprecatedShow} from '#/view/com/util/Toast' import {atoms as a} from '#/alf' -import * as toast from '#/components/Toast' -import {Toast} from '#/components/Toast/Toast' +import {Globe_Stroke2_Corner0_Rounded as GlobeIcon} from '#/components/icons/Globe' +import * as Toast from '#/components/Toast' +import {Default} from '#/components/Toast/Toast' import {H1} from '#/components/Typography' +function ToastWithAction({type = 'default'}: {type?: Toast.ToastType}) { + return ( + <Toast.Outer type={type}> + <Toast.Icon icon={GlobeIcon} /> + <Toast.Text>This toast has an action button</Toast.Text> + <Toast.Action + label="Action" + onPress={() => console.log('Action clicked!')}> + Action + </Toast.Action> + </Toast.Outer> + ) +} + +function LongToastWithAction({type = 'default'}: {type?: Toast.ToastType}) { + return ( + <Toast.Outer type={type}> + <Toast.Icon icon={GlobeIcon} /> + <Toast.Text> + This is a longer message to test how the toast handles multiple lines of + text content. + </Toast.Text> + <Toast.Action + label="Action" + onPress={() => console.log('Action clicked!')}> + Action + </Toast.Action> + </Toast.Outer> + ) +} + export function Toasts() { return ( <View style={[a.gap_md]}> <H1>Toast Examples</H1> <View style={[a.gap_md]}> + <View style={[a.gap_md, {marginHorizontal: a.px_xl.paddingLeft * -1}]}> + <Pressable + accessibilityRole="button" + onPress={() => Toast.show(<ToastWithAction />)}> + <ToastWithAction /> + </Pressable> + <Pressable + accessibilityRole="button" + onPress={() => Toast.show(<LongToastWithAction />)}> + <LongToastWithAction /> + </Pressable> + <Pressable + accessibilityRole="button" + onPress={() => Toast.show(<ToastWithAction type="success" />)}> + <ToastWithAction type="success" /> + </Pressable> + <Pressable + accessibilityRole="button" + onPress={() => Toast.show(<ToastWithAction type="error" />)}> + <ToastWithAction type="error" /> + </Pressable> + </View> + <Pressable accessibilityRole="button" - onPress={() => toast.show(`Hey I'm a toast!`)}> - <Toast content="Hey I'm a toast!" /> + onPress={() => Toast.show(`Hey I'm a toast!`)}> + <Default content="Hey I'm a toast!" /> </Pressable> <Pressable accessibilityRole="button" onPress={() => - toast.show(`This toast will disappear after 6 seconds`, { + Toast.show(`This toast will disappear after 6 seconds`, { duration: 6e3, }) }> - <Toast content="This toast will disappear after 6 seconds" /> + <Default content="This toast will disappear after 6 seconds" /> </Pressable> <Pressable accessibilityRole="button" onPress={() => - toast.show( + Toast.show( `This is a longer message to test how the toast handles multiple lines of text content.`, ) }> - <Toast content="This is a longer message to test how the toast handles multiple lines of text content." /> + <Default content="This is a longer message to test how the toast handles multiple lines of text content." /> </Pressable> <Pressable accessibilityRole="button" onPress={() => - toast.show(`Success! Yayyyyyyy :)`, { + Toast.show(`Success! Yayyyyyyy :)`, { type: 'success', }) }> - <Toast content="Success! Yayyyyyyy :)" type="success" /> + <Default content="Success! Yayyyyyyy :)" type="success" /> </Pressable> <Pressable accessibilityRole="button" onPress={() => - toast.show(`I'm providing info!`, { + Toast.show(`I'm providing info!`, { type: 'info', }) }> - <Toast content="I'm providing info!" type="info" /> + <Default content="I'm providing info!" type="info" /> </Pressable> <Pressable accessibilityRole="button" onPress={() => - toast.show(`This is a warning toast`, { + Toast.show(`This is a warning toast`, { type: 'warning', }) }> - <Toast content="This is a warning toast" type="warning" /> + <Default content="This is a warning toast" type="warning" /> </Pressable> <Pressable accessibilityRole="button" onPress={() => - toast.show(`This is an error toast :(`, { + Toast.show(`This is an error toast :(`, { type: 'error', }) }> - <Toast content="This is an error toast :(" type="error" /> + <Default content="This is an error toast :(" type="error" /> </Pressable> <Pressable @@ -80,7 +135,7 @@ export function Toasts() { 'exclamation-circle', ) }> - <Toast + <Default content="This is a test of the deprecated API" type="warning" /> |