dify/web/hooks/use-async-window-open.ts
yyh e95b7b57c9
fix: prevent popup blocker from blocking async window.open
Add useAsyncWindowOpen hook to handle async URL fetching with placeholder window pattern. This prevents browser popup blockers (especially Safari) from blocking windows opened after async operations.

- Create reusable useAsyncWindowOpen hook with placeholder window pattern
- Fix billing subscription management popup (cloud-plan-item)
- Fix app card explore popup
- Fix app publisher explore popup

Fixes #29389
Ref: #29390
2025-12-10 12:10:47 +08:00

63 lines
1.4 KiB
TypeScript

import { useCallback } from 'react'
import Toast from '@/app/components/base/toast'
export type AsyncWindowOpenOptions = {
successMessage?: string
errorMessage?: string
windowFeatures?: string
onError?: (error: any) => void
onSuccess?: (url: string) => void
}
export const useAsyncWindowOpen = () => {
const openAsync = useCallback(async (
fetchUrl: () => Promise<string>,
options: AsyncWindowOpenOptions = {},
) => {
const {
successMessage,
errorMessage = 'Failed to open page',
windowFeatures = 'noopener,noreferrer',
onError,
onSuccess,
} = options
const newWindow = window.open('', '_blank', windowFeatures)
try {
const url = await fetchUrl()
if (url && newWindow) {
newWindow.location.href = url
onSuccess?.(url)
if (successMessage) {
Toast.notify({
type: 'success',
message: successMessage,
})
}
}
else {
newWindow?.close()
const error = new Error('Invalid URL or window was closed')
onError?.(error)
Toast.notify({
type: 'error',
message: errorMessage,
})
}
}
catch (error) {
newWindow?.close()
onError?.(error)
Toast.notify({
type: 'error',
message: errorMessage,
})
}
}, [])
return { openAsync }
}