···6import {atoms as a} from '#/alf'
7import {DURATION} from '#/components/Toast/const'
8import {
9- Default as DefaultToast,
10 Outer as BaseOuter,
11- type ToastComponentProps,
12 ToastConfigProvider,
13} from '#/components/Toast/Toast'
14import {type BaseToastOptions} from '#/components/Toast/types'
1516export {DURATION} from '#/components/Toast/const'
17-export {Action, Icon, Text} from '#/components/Toast/Toast'
18export {type ToastType} from '#/components/Toast/types'
1920/**
···25 return <Toaster pauseWhenPageIsHidden gap={a.gap_sm.gap} />
26}
2728-/**
29- * The toast UI component
30- */
31-export function Default({type, content}: ToastComponentProps) {
32- return (
33- <View style={[a.px_xl, a.w_full]}>
34- <DefaultToast content={content} type={type} />
35- </View>
36- )
37-}
38-39-export function Outer({
40- children,
41- type = 'default',
42-}: {
43- children: React.ReactNode
44- type?: ToastComponentProps['type']
45-}) {
46 return (
47 <View style={[a.px_xl, a.w_full]}>
48- <BaseOuter type={type}>{children}</BaseOuter>
49 </View>
50 )
51}
···60 */
61export function show(
62 content: React.ReactNode,
63- {type, ...options}: BaseToastOptions = {},
64) {
65 const id = nanoid()
6667 if (typeof content === 'string') {
68 sonner.custom(
69- <ToastConfigProvider id={id}>
70- <Default content={content} type={type} />
00071 </ToastConfigProvider>,
72 {
73 ...options,
···77 )
78 } else if (React.isValidElement(content)) {
79 sonner.custom(
80- <ToastConfigProvider id={id}>{content}</ToastConfigProvider>,
0081 {
82 ...options,
83 id,
···6import {atoms as a} from '#/alf'
7import {DURATION} from '#/components/Toast/const'
8import {
9+ Icon as ToastIcon,
10 Outer as BaseOuter,
11+ Text as ToastText,
12 ToastConfigProvider,
13} from '#/components/Toast/Toast'
14import {type BaseToastOptions} from '#/components/Toast/types'
1516export {DURATION} from '#/components/Toast/const'
17+export {Action, Icon, Text, ToastConfigProvider} from '#/components/Toast/Toast'
18export {type ToastType} from '#/components/Toast/types'
1920/**
···25 return <Toaster pauseWhenPageIsHidden gap={a.gap_sm.gap} />
26}
2728+export function Outer({children}: {children: React.ReactNode}) {
0000000000000000029 return (
30 <View style={[a.px_xl, a.w_full]}>
31+ <BaseOuter>{children}</BaseOuter>
32 </View>
33 )
34}
···43 */
44export function show(
45 content: React.ReactNode,
46+ {type = 'default', ...options}: BaseToastOptions = {},
47) {
48 const id = nanoid()
4950 if (typeof content === 'string') {
51 sonner.custom(
52+ <ToastConfigProvider id={id} type={type}>
53+ <Outer>
54+ <ToastIcon />
55+ <ToastText>{content}</ToastText>
56+ </Outer>
57 </ToastConfigProvider>,
58 {
59 ...options,
···63 )
64 } else if (React.isValidElement(content)) {
65 sonner.custom(
66+ <ToastConfigProvider id={id} type={type}>
67+ {content}
68+ </ToastConfigProvider>,
69 {
70 ...options,
71 id,
+20-10
src/components/Toast/index.web.tsx
···5import {atoms as a} from '#/alf'
6import {DURATION} from '#/components/Toast/const'
7import {
8- Default as DefaultToast,
009 ToastConfigProvider,
10} from '#/components/Toast/Toast'
11import {type BaseToastOptions} from '#/components/Toast/types'
···39 */
40export function show(
41 content: React.ReactNode,
42- {type, ...options}: BaseToastOptions = {},
43) {
44 const id = nanoid()
4546 if (typeof content === 'string') {
47 sonner(
48- <ToastConfigProvider id={id}>
49- <DefaultToast content={content} type={type} />
00050 </ToastConfigProvider>,
51 {
52 ...options,
···56 },
57 )
58 } else if (React.isValidElement(content)) {
59- sonner(<ToastConfigProvider id={id}>{content}</ToastConfigProvider>, {
60- ...options,
61- unstyled: true, // required on web
62- id,
63- duration: options?.duration ?? DURATION,
64- })
0000065 } else {
66 throw new Error(
67 `Toast can be a string or a React element, got ${typeof content}`,
···5import {atoms as a} from '#/alf'
6import {DURATION} from '#/components/Toast/const'
7import {
8+ Icon as ToastIcon,
9+ Outer as ToastOuter,
10+ Text as ToastText,
11 ToastConfigProvider,
12} from '#/components/Toast/Toast'
13import {type BaseToastOptions} from '#/components/Toast/types'
···41 */
42export function show(
43 content: React.ReactNode,
44+ {type = 'default', ...options}: BaseToastOptions = {},
45) {
46 const id = nanoid()
4748 if (typeof content === 'string') {
49 sonner(
50+ <ToastConfigProvider id={id} type={type}>
51+ <ToastOuter>
52+ <ToastIcon />
53+ <ToastText>{content}</ToastText>
54+ </ToastOuter>
55 </ToastConfigProvider>,
56 {
57 ...options,
···61 },
62 )
63 } else if (React.isValidElement(content)) {
64+ sonner(
65+ <ToastConfigProvider id={id} type={type}>
66+ {content}
67+ </ToastConfigProvider>,
68+ {
69+ ...options,
70+ unstyled: true, // required on web
71+ id,
72+ duration: options?.duration ?? DURATION,
73+ },
74+ )
75 } else {
76 throw new Error(
77 `Toast can be a string or a React element, got ${typeof content}`,
+5-4
src/view/com/composer/Composer.tsx
···126import {UserAvatar} from '#/view/com/util/UserAvatar'
127import {atoms as a, native, useTheme, web} from '#/alf'
128import {Button, ButtonIcon, ButtonText} from '#/components/Button'
129-import {CircleCheck_Stroke2_Corner0_Rounded as CircleCheckIcon} from '#/components/icons/CircleCheck'
130import {CircleInfo_Stroke2_Corner0_Rounded as CircleInfo} from '#/components/icons/CircleInfo'
131import {EmojiArc_Stroke2_Corner0_Rounded as EmojiSmile} from '#/components/icons/Emoji'
132import {TimesLarge_Stroke2_Corner0_Rounded as X} from '#/components/icons/Times'
···527 }
528 onClose()
529 Toast.show(
530- <Toast.Outer type="success">
531- <Toast.Icon icon={CircleCheckIcon} />
532 <Toast.Text>
533 {thread.posts.length > 1
534 ? _(msg`Your posts were sent`)
···543 const {host: name, rkey} = new AtUri(postUri)
544 navigation.navigate('PostThread', {name, rkey})
545 }}>
546- View
00547 </Toast.Action>
548 )}
549 </Toast.Outer>,
···126import {UserAvatar} from '#/view/com/util/UserAvatar'
127import {atoms as a, native, useTheme, web} from '#/alf'
128import {Button, ButtonIcon, ButtonText} from '#/components/Button'
0129import {CircleInfo_Stroke2_Corner0_Rounded as CircleInfo} from '#/components/icons/CircleInfo'
130import {EmojiArc_Stroke2_Corner0_Rounded as EmojiSmile} from '#/components/icons/Emoji'
131import {TimesLarge_Stroke2_Corner0_Rounded as X} from '#/components/icons/Times'
···526 }
527 onClose()
528 Toast.show(
529+ <Toast.Outer>
530+ <Toast.Icon />
531 <Toast.Text>
532 {thread.posts.length > 1
533 ? _(msg`Your posts were sent`)
···542 const {host: name, rkey} = new AtUri(postUri)
543 navigation.navigate('PostThread', {name, rkey})
544 }}>
545+ <Trans context="Action to view the post the user just created">
546+ View
547+ </Trans>
548 </Toast.Action>
549 )}
550 </Toast.Outer>,
+44-36
src/view/screens/Storybook/Toasts.tsx
···4import {atoms as a} from '#/alf'
5import {Globe_Stroke2_Corner0_Rounded as GlobeIcon} from '#/components/icons/Globe'
6import * as Toast from '#/components/Toast'
7-import {Default} from '#/components/Toast/Toast'
8import {H1} from '#/components/Typography'
910-function ToastWithAction({type = 'default'}: {type?: Toast.ToastType}) {
00000011 return (
12- <Toast.Outer type={type}>
0000000000013 <Toast.Icon icon={GlobeIcon} />
14 <Toast.Text>This toast has an action button</Toast.Text>
15 <Toast.Action
···21 )
22}
2324-function LongToastWithAction({type = 'default'}: {type?: Toast.ToastType}) {
25 return (
26- <Toast.Outer type={type}>
27 <Toast.Icon icon={GlobeIcon} />
28 <Toast.Text>
29 This is a longer message to test how the toast handles multiple lines of
···44 <H1>Toast Examples</H1>
4546 <View style={[a.gap_md]}>
47- <View style={[a.gap_md, {marginHorizontal: a.px_xl.paddingLeft * -1}]}>
48- <Pressable
49- accessibilityRole="button"
50- onPress={() => Toast.show(<ToastWithAction />)}>
51- <ToastWithAction />
52- </Pressable>
53- <Pressable
54- accessibilityRole="button"
55- onPress={() => Toast.show(<LongToastWithAction />)}>
56- <LongToastWithAction />
57- </Pressable>
58- <Pressable
59- accessibilityRole="button"
60- onPress={() => Toast.show(<ToastWithAction type="success" />)}>
61- <ToastWithAction type="success" />
62- </Pressable>
63- <Pressable
64- accessibilityRole="button"
65- onPress={() => Toast.show(<ToastWithAction type="error" />)}>
66- <ToastWithAction type="error" />
67- </Pressable>
68- </View>
69-70 <Pressable
71 accessibilityRole="button"
72 onPress={() => Toast.show(`Hey I'm a toast!`)}>
73- <Default content="Hey I'm a toast!" />
74 </Pressable>
75 <Pressable
76 accessibilityRole="button"
···79 duration: 6e3,
80 })
81 }>
82- <Default content="This toast will disappear after 6 seconds" />
83 </Pressable>
84 <Pressable
85 accessibilityRole="button"
···88 `This is a longer message to test how the toast handles multiple lines of text content.`,
89 )
90 }>
91- <Default content="This is a longer message to test how the toast handles multiple lines of text content." />
92 </Pressable>
93 <Pressable
94 accessibilityRole="button"
···97 type: 'success',
98 })
99 }>
100- <Default content="Success! Yayyyyyyy :)" type="success" />
101 </Pressable>
102 <Pressable
103 accessibilityRole="button"
···106 type: 'info',
107 })
108 }>
109- <Default content="I'm providing info!" type="info" />
110 </Pressable>
111 <Pressable
112 accessibilityRole="button"
···115 type: 'warning',
116 })
117 }>
118- <Default content="This is a warning toast" type="warning" />
119 </Pressable>
120 <Pressable
121 accessibilityRole="button"
···124 type: 'error',
125 })
126 }>
127- <Default content="This is an error toast :(" type="error" />
128 </Pressable>
129130 <Pressable
···135 'exclamation-circle',
136 )
137 }>
138- <Default
139 content="This is a test of the deprecated API"
140 type="warning"
141 />
···4import {atoms as a} from '#/alf'
5import {Globe_Stroke2_Corner0_Rounded as GlobeIcon} from '#/components/icons/Globe'
6import * as Toast from '#/components/Toast'
07import {H1} from '#/components/Typography'
89+function DefaultToast({
10+ content,
11+ type = 'default',
12+}: {
13+ content: string
14+ type?: Toast.ToastType
15+}) {
16 return (
17+ <Toast.ToastConfigProvider id="default-toast" type={type}>
18+ <Toast.Outer>
19+ <Toast.Icon icon={GlobeIcon} />
20+ <Toast.Text>{content}</Toast.Text>
21+ </Toast.Outer>
22+ </Toast.ToastConfigProvider>
23+ )
24+}
25+26+function ToastWithAction() {
27+ return (
28+ <Toast.Outer>
29 <Toast.Icon icon={GlobeIcon} />
30 <Toast.Text>This toast has an action button</Toast.Text>
31 <Toast.Action
···37 )
38}
3940+function LongToastWithAction() {
41 return (
42+ <Toast.Outer>
43 <Toast.Icon icon={GlobeIcon} />
44 <Toast.Text>
45 This is a longer message to test how the toast handles multiple lines of
···60 <H1>Toast Examples</H1>
6162 <View style={[a.gap_md]}>
63+ <Pressable
64+ accessibilityRole="button"
65+ onPress={() => Toast.show(<ToastWithAction />, {type: 'success'})}>
66+ <ToastWithAction />
67+ </Pressable>
68+ <Pressable
69+ accessibilityRole="button"
70+ onPress={() => Toast.show(<ToastWithAction />, {type: 'error'})}>
71+ <ToastWithAction />
72+ </Pressable>
73+ <Pressable
74+ accessibilityRole="button"
75+ onPress={() => Toast.show(<LongToastWithAction />)}>
76+ <LongToastWithAction />
77+ </Pressable>
0000000078 <Pressable
79 accessibilityRole="button"
80 onPress={() => Toast.show(`Hey I'm a toast!`)}>
81+ <DefaultToast content="Hey I'm a toast!" />
82 </Pressable>
83 <Pressable
84 accessibilityRole="button"
···87 duration: 6e3,
88 })
89 }>
90+ <DefaultToast content="This toast will disappear after 6 seconds" />
91 </Pressable>
92 <Pressable
93 accessibilityRole="button"
···96 `This is a longer message to test how the toast handles multiple lines of text content.`,
97 )
98 }>
99+ <DefaultToast content="This is a longer message to test how the toast handles multiple lines of text content." />
100 </Pressable>
101 <Pressable
102 accessibilityRole="button"
···105 type: 'success',
106 })
107 }>
108+ <DefaultToast content="Success! Yayyyyyyy :)" type="success" />
109 </Pressable>
110 <Pressable
111 accessibilityRole="button"
···114 type: 'info',
115 })
116 }>
117+ <DefaultToast content="I'm providing info!" type="info" />
118 </Pressable>
119 <Pressable
120 accessibilityRole="button"
···123 type: 'warning',
124 })
125 }>
126+ <DefaultToast content="This is a warning toast" type="warning" />
127 </Pressable>
128 <Pressable
129 accessibilityRole="button"
···132 type: 'error',
133 })
134 }>
135+ <DefaultToast content="This is an error toast :(" type="error" />
136 </Pressable>
137138 <Pressable
···143 'exclamation-circle',
144 )
145 }>
146+ <DefaultToast
147 content="This is a test of the deprecated API"
148 type="warning"
149 />