about summary refs log tree commit diff
path: root/src/components
diff options
context:
space:
mode:
authorEric Bailey <git@esb.lol>2025-08-26 16:54:43 -0500
committerGitHub <noreply@github.com>2025-08-26 16:54:43 -0500
commit9f7c920346f3e650f530930cd22ca241fc6cbd7f (patch)
tree8b93e3b299030379d2af425cd825196199fffe7f /src/components
parent0555d3623cf5eea744bd26b4343c60ec66e43aa3 (diff)
downloadvoidsky-9f7c920346f3e650f530930cd22ca241fc6cbd7f.tar.zst
Fix toast type (#8909)
* Fix confusing toast API

* Provide all exports to e2e file

* Fix first usage in Composer

* Loosen type, add Trans tag
Diffstat (limited to 'src/components')
-rw-r--r--src/components/Toast/Toast.tsx91
-rw-r--r--src/components/Toast/index.e2e.tsx8
-rw-r--r--src/components/Toast/index.tsx40
-rw-r--r--src/components/Toast/index.web.tsx30
4 files changed, 74 insertions, 95 deletions
diff --git a/src/components/Toast/Toast.tsx b/src/components/Toast/Toast.tsx
index 53d5e5115..ac5bc4889 100644
--- a/src/components/Toast/Toast.tsx
+++ b/src/components/Toast/Toast.tsx
@@ -16,19 +16,6 @@ import {dismiss} from '#/components/Toast/sonner'
 import {type ToastType} from '#/components/Toast/types'
 import {Text as BaseText} from '#/components/Typography'
 
-type ToastConfigContextType = {
-  id: string
-}
-
-type ToastThemeContextType = {
-  type: ToastType
-}
-
-export type ToastComponentProps = {
-  type?: ToastType
-  content: string
-}
-
 export const ICONS = {
   default: CircleCheck,
   success: CircleCheck,
@@ -37,81 +24,67 @@ export const ICONS = {
   info: CircleInfo,
 }
 
-const ToastConfigContext = createContext<ToastConfigContextType>({
+const ToastConfigContext = createContext<{
+  id: string
+  type: ToastType
+}>({
   id: '',
+  type: 'default',
 })
 ToastConfigContext.displayName = 'ToastConfigContext'
 
 export function ToastConfigProvider({
   children,
   id,
+  type,
 }: {
   children: React.ReactNode
   id: string
+  type: ToastType
 }) {
   return (
-    <ToastConfigContext.Provider value={useMemo(() => ({id}), [id])}>
+    <ToastConfigContext.Provider
+      value={useMemo(() => ({id, type}), [id, type])}>
       {children}
     </ToastConfigContext.Provider>
   )
 }
 
-const ToastThemeContext = createContext<ToastThemeContextType>({
-  type: 'default',
-})
-ToastThemeContext.displayName = 'ToastThemeContext'
-
-export function Default({type = 'default', content}: ToastComponentProps) {
-  return (
-    <Outer type={type}>
-      <Icon />
-      <Text>{content}</Text>
-    </Outer>
-  )
-}
-
-export function Outer({
-  children,
-  type = 'default',
-}: {
-  children: React.ReactNode
-  type?: ToastType
-}) {
+export function Outer({children}: {children: React.ReactNode}) {
   const t = useTheme()
+  const {type} = useContext(ToastConfigContext)
   const styles = useToastStyles({type})
 
   return (
-    <ToastThemeContext.Provider value={useMemo(() => ({type}), [type])}>
-      <View
-        style={[
-          a.flex_1,
-          a.p_lg,
-          a.rounded_md,
-          a.border,
-          a.flex_row,
-          a.gap_sm,
-          t.atoms.shadow_sm,
-          {
-            paddingVertical: 14, // 16 seems too big
-            backgroundColor: styles.backgroundColor,
-            borderColor: styles.borderColor,
-          },
-        ]}>
-        {children}
-      </View>
-    </ToastThemeContext.Provider>
+    <View
+      style={[
+        a.flex_1,
+        a.p_lg,
+        a.rounded_md,
+        a.border,
+        a.flex_row,
+        a.gap_sm,
+        t.atoms.shadow_sm,
+        {
+          paddingVertical: 14, // 16 seems too big
+          backgroundColor: styles.backgroundColor,
+          borderColor: styles.borderColor,
+        },
+      ]}>
+      {children}
+    </View>
   )
 }
 
 export function Icon({icon}: {icon?: React.ComponentType<SVGIconProps>}) {
-  const {type} = useContext(ToastThemeContext)
+  const {type} = useContext(ToastConfigContext)
   const styles = useToastStyles({type})
   const IconComponent = icon || ICONS[type]
   return <IconComponent size="md" fill={styles.iconColor} />
 }
 
 export function Text({children}: {children: React.ReactNode}) {
-  const {type} = useContext(ToastThemeContext)
+  const {type} = useContext(ToastConfigContext)
   const {textColor} = useToastStyles({type})
   const {fontScaleCompensation} = useToastFontScaleCompensation()
   return (
@@ -142,12 +115,12 @@ export function Text({children}: {children: React.ReactNode}) {
 
 export function Action(
   props: Omit<ButtonProps, UninheritableButtonProps | 'children'> & {
-    children: string
+    children: React.ReactNode
   },
 ) {
   const t = useTheme()
   const {fontScaleCompensation} = useToastFontScaleCompensation()
-  const {type} = useContext(ToastThemeContext)
+  const {type} = useContext(ToastConfigContext)
   const {id} = useContext(ToastConfigContext)
   const styles = useMemo(() => {
     const base = {
diff --git a/src/components/Toast/index.e2e.tsx b/src/components/Toast/index.e2e.tsx
index 357bd8dda..a4056323d 100644
--- a/src/components/Toast/index.e2e.tsx
+++ b/src/components/Toast/index.e2e.tsx
@@ -1,3 +1,11 @@
+export const DURATION = 0
+
+export const Action = () => null
+export const Icon = () => null
+export const Outer = () => null
+export const Text = () => null
+export const ToastConfigProvider = () => null
+
 export function ToastOutlet() {
   return null
 }
diff --git a/src/components/Toast/index.tsx b/src/components/Toast/index.tsx
index 0d1c661d2..d70a8ad16 100644
--- a/src/components/Toast/index.tsx
+++ b/src/components/Toast/index.tsx
@@ -6,15 +6,15 @@ import {toast as sonner, Toaster} from 'sonner-native'
 import {atoms as a} from '#/alf'
 import {DURATION} from '#/components/Toast/const'
 import {
-  Default as DefaultToast,
+  Icon as ToastIcon,
   Outer as BaseOuter,
-  type ToastComponentProps,
+  Text as ToastText,
   ToastConfigProvider,
 } from '#/components/Toast/Toast'
 import {type BaseToastOptions} from '#/components/Toast/types'
 
 export {DURATION} from '#/components/Toast/const'
-export {Action, Icon, Text} from '#/components/Toast/Toast'
+export {Action, Icon, Text, ToastConfigProvider} from '#/components/Toast/Toast'
 export {type ToastType} from '#/components/Toast/types'
 
 /**
@@ -25,27 +25,10 @@ export function ToastOutlet() {
   return <Toaster pauseWhenPageIsHidden gap={a.gap_sm.gap} />
 }
 
-/**
- * The toast UI component
- */
-export function Default({type, content}: ToastComponentProps) {
+export function Outer({children}: {children: React.ReactNode}) {
   return (
     <View style={[a.px_xl, a.w_full]}>
-      <DefaultToast content={content} type={type} />
-    </View>
-  )
-}
-
-export function Outer({
-  children,
-  type = 'default',
-}: {
-  children: React.ReactNode
-  type?: ToastComponentProps['type']
-}) {
-  return (
-    <View style={[a.px_xl, a.w_full]}>
-      <BaseOuter type={type}>{children}</BaseOuter>
+      <BaseOuter>{children}</BaseOuter>
     </View>
   )
 }
@@ -60,14 +43,17 @@ export const api = sonner
  */
 export function show(
   content: React.ReactNode,
-  {type, ...options}: BaseToastOptions = {},
+  {type = 'default', ...options}: BaseToastOptions = {},
 ) {
   const id = nanoid()
 
   if (typeof content === 'string') {
     sonner.custom(
-      <ToastConfigProvider id={id}>
-        <Default content={content} type={type} />
+      <ToastConfigProvider id={id} type={type}>
+        <Outer>
+          <ToastIcon />
+          <ToastText>{content}</ToastText>
+        </Outer>
       </ToastConfigProvider>,
       {
         ...options,
@@ -77,7 +63,9 @@ export function show(
     )
   } else if (React.isValidElement(content)) {
     sonner.custom(
-      <ToastConfigProvider id={id}>{content}</ToastConfigProvider>,
+      <ToastConfigProvider id={id} type={type}>
+        {content}
+      </ToastConfigProvider>,
       {
         ...options,
         id,
diff --git a/src/components/Toast/index.web.tsx b/src/components/Toast/index.web.tsx
index c4db20dca..8b2028db9 100644
--- a/src/components/Toast/index.web.tsx
+++ b/src/components/Toast/index.web.tsx
@@ -5,7 +5,9 @@ import {toast as sonner, Toaster} from 'sonner'
 import {atoms as a} from '#/alf'
 import {DURATION} from '#/components/Toast/const'
 import {
-  Default as DefaultToast,
+  Icon as ToastIcon,
+  Outer as ToastOuter,
+  Text as ToastText,
   ToastConfigProvider,
 } from '#/components/Toast/Toast'
 import {type BaseToastOptions} from '#/components/Toast/types'
@@ -39,14 +41,17 @@ export const api = sonner
  */
 export function show(
   content: React.ReactNode,
-  {type, ...options}: BaseToastOptions = {},
+  {type = 'default', ...options}: BaseToastOptions = {},
 ) {
   const id = nanoid()
 
   if (typeof content === 'string') {
     sonner(
-      <ToastConfigProvider id={id}>
-        <DefaultToast content={content} type={type} />
+      <ToastConfigProvider id={id} type={type}>
+        <ToastOuter>
+          <ToastIcon />
+          <ToastText>{content}</ToastText>
+        </ToastOuter>
       </ToastConfigProvider>,
       {
         ...options,
@@ -56,12 +61,17 @@ export function show(
       },
     )
   } else if (React.isValidElement(content)) {
-    sonner(<ToastConfigProvider id={id}>{content}</ToastConfigProvider>, {
-      ...options,
-      unstyled: true, // required on web
-      id,
-      duration: options?.duration ?? DURATION,
-    })
+    sonner(
+      <ToastConfigProvider id={id} type={type}>
+        {content}
+      </ToastConfigProvider>,
+      {
+        ...options,
+        unstyled: true, // required on web
+        id,
+        duration: options?.duration ?? DURATION,
+      },
+    )
   } else {
     throw new Error(
       `Toast can be a string or a React element, got ${typeof content}`,