@@ -111,6 +111,69 @@ function isReactElement(value: unknown): boolean {
111111 return typeof value === "object" && value !== null && "$$typeof" in value ;
112112}
113113
114+ /**
115+ * Builds a promise state handler (success or error) for `toast.promise`.
116+ * Eliminates duplication between success/error branches.
117+ */
118+ function buildPromiseHandler < TType extends "success" | "error" , TInput > (
119+ type : TType ,
120+ handler :
121+ | ReactNode
122+ | ( ( input : TInput ) => ReactNode | PopserPromiseExtendedResult | undefined ) ,
123+ toastId : { current : string } ,
124+ descriptionOption : PopserPromiseOptions < never > [ "description" ]
125+ ) {
126+ if ( typeof handler !== "function" ) {
127+ return {
128+ title : handler ,
129+ type,
130+ ...( typeof descriptionOption !== "function" &&
131+ descriptionOption !== undefined && {
132+ description : descriptionOption ,
133+ } ) ,
134+ } ;
135+ }
136+
137+ return ( input : TInput ) => {
138+ const result = (
139+ handler as (
140+ input : TInput
141+ ) => ReactNode | PopserPromiseExtendedResult | undefined
142+ ) ( input ) ;
143+ if ( result === undefined ) {
144+ queueMicrotask ( ( ) => getManager ( ) . close ( toastId . current ) ) ;
145+ return { title : "" as ReactNode , type, timeout : 1 } ;
146+ }
147+ if ( isExtendedResult ( result ) ) {
148+ const { title, timeout, icon, action, cancel, description, ...rest } =
149+ result ;
150+ return {
151+ title,
152+ type,
153+ ...( timeout !== undefined && { timeout } ) ,
154+ ...( description !== undefined && { description } ) ,
155+ data : {
156+ __popser : {
157+ ...( icon !== undefined && { icon } ) ,
158+ ...( action !== undefined && { action } ) ,
159+ ...( cancel !== undefined && { cancel } ) ,
160+ ...rest ,
161+ } ,
162+ } ,
163+ } ;
164+ }
165+ const desc =
166+ typeof descriptionOption === "function"
167+ ? ( descriptionOption as ( data : TInput ) => ReactNode ) ( input )
168+ : undefined ;
169+ return {
170+ title : result ,
171+ type,
172+ ...( desc !== undefined && { description : desc } ) ,
173+ } ;
174+ } ;
175+ }
176+
114177// ---------------------------------------------------------------------------
115178// Public API
116179// ---------------------------------------------------------------------------
@@ -220,113 +283,18 @@ toast.promise = <T>(
220283
221284 const toastId = { current : "" } ;
222285
223- const successHandler =
224- typeof success === "function"
225- ? ( result : T ) => {
226- const handlerResult = success ( result ) ;
227- if ( handlerResult === undefined ) {
228- // Close the loading toast instead of showing a ghost (B1 fix)
229- queueMicrotask ( ( ) => getManager ( ) . close ( toastId . current ) ) ;
230- return { title : "" , type : "success" as const , timeout : 1 } ;
231- }
232- if ( isExtendedResult ( handlerResult ) ) {
233- const {
234- title,
235- timeout,
236- icon,
237- action,
238- cancel,
239- description,
240- ...rest
241- } = handlerResult ;
242- return {
243- title,
244- type : "success" as const ,
245- ...( timeout !== undefined && { timeout } ) ,
246- ...( description !== undefined && { description } ) ,
247- data : {
248- __popser : {
249- ...( icon !== undefined && { icon } ) ,
250- ...( action !== undefined && { action } ) ,
251- ...( cancel !== undefined && { cancel } ) ,
252- ...rest ,
253- } ,
254- } ,
255- } ;
256- }
257- // Per-state description support
258- const desc =
259- typeof options . description === "function"
260- ? ( options . description as ( data : T ) => ReactNode ) ( result )
261- : undefined ;
262- return {
263- title : handlerResult ,
264- type : "success" as const ,
265- ...( desc !== undefined && { description : desc } ) ,
266- } ;
267- }
268- : {
269- title : success ,
270- type : "success" as const ,
271- ...( typeof options . description !== "function" &&
272- options . description !== undefined && {
273- description : options . description ,
274- } ) ,
275- } ;
276-
277- const errorHandler =
278- typeof error === "function"
279- ? ( err : unknown ) => {
280- const handlerResult = error ( err ) ;
281- if ( handlerResult === undefined ) {
282- // Close the loading toast instead of showing a ghost (B1 fix)
283- queueMicrotask ( ( ) => getManager ( ) . close ( toastId . current ) ) ;
284- return { title : "" , type : "error" as const , timeout : 1 } ;
285- }
286- if ( isExtendedResult ( handlerResult ) ) {
287- const {
288- title,
289- timeout,
290- icon,
291- action,
292- cancel,
293- description,
294- ...rest
295- } = handlerResult ;
296- return {
297- title,
298- type : "error" as const ,
299- ...( timeout !== undefined && { timeout } ) ,
300- ...( description !== undefined && { description } ) ,
301- data : {
302- __popser : {
303- ...( icon !== undefined && { icon } ) ,
304- ...( action !== undefined && { action } ) ,
305- ...( cancel !== undefined && { cancel } ) ,
306- ...rest ,
307- } ,
308- } ,
309- } ;
310- }
311- // Per-state description support
312- const desc =
313- typeof options . description === "function"
314- ? ( options . description as ( error : unknown ) => ReactNode ) ( err )
315- : undefined ;
316- return {
317- title : handlerResult ,
318- type : "error" as const ,
319- ...( desc !== undefined && { description : desc } ) ,
320- } ;
321- }
322- : {
323- title : error ,
324- type : "error" as const ,
325- ...( typeof options . description !== "function" &&
326- options . description !== undefined && {
327- description : options . description ,
328- } ) ,
329- } ;
286+ const successHandler = buildPromiseHandler < "success" , T > (
287+ "success" ,
288+ success ,
289+ toastId ,
290+ options . description
291+ ) ;
292+ const errorHandler = buildPromiseHandler < "error" , unknown > (
293+ "error" ,
294+ error ,
295+ toastId ,
296+ options . description
297+ ) ;
330298
331299 const result = getManager ( ) . promise ( promise , {
332300 ...( options . id !== undefined && { id : options . id } ) ,
0 commit comments