This commit is contained in:
2025-12-01 17:21:38 +08:00
parent 32fee2b8ab
commit fab8c13cb3
7511 changed files with 996300 additions and 0 deletions

View File

@@ -0,0 +1,130 @@
import { type Dispatch, type SetStateAction, useCallback, useEffect, useRef, useState } from 'react'
import dayjs from 'dayjs'
import { NUM_INFINITE } from '@/app/components/billing/config'
import { Plan } from '@/app/components/billing/type'
import { IS_CLOUD_EDITION } from '@/config'
import type { ModalState } from '../modal-context'
export type TriggerEventsLimitModalPayload = {
usage: number
total: number
resetInDays?: number
planType: Plan
storageKey?: string
persistDismiss?: boolean
}
type TriggerPlanInfo = {
type: Plan
usage: { triggerEvents: number }
total: { triggerEvents: number }
reset: { triggerEvents?: number | null }
}
type UseTriggerEventsLimitModalOptions = {
plan: TriggerPlanInfo
isFetchedPlan: boolean
currentWorkspaceId?: string
}
type UseTriggerEventsLimitModalResult = {
showTriggerEventsLimitModal: ModalState<TriggerEventsLimitModalPayload> | null
setShowTriggerEventsLimitModal: Dispatch<SetStateAction<ModalState<TriggerEventsLimitModalPayload> | null>>
persistTriggerEventsLimitModalDismiss: () => void
}
const TRIGGER_EVENTS_LOCALSTORAGE_PREFIX = 'trigger-events-limit-dismissed'
export const useTriggerEventsLimitModal = ({
plan,
isFetchedPlan,
currentWorkspaceId,
}: UseTriggerEventsLimitModalOptions): UseTriggerEventsLimitModalResult => {
const [showTriggerEventsLimitModal, setShowTriggerEventsLimitModal] = useState<ModalState<TriggerEventsLimitModalPayload> | null>(null)
const dismissedTriggerEventsLimitStorageKeysRef = useRef<Record<string, boolean>>({})
useEffect(() => {
if (!IS_CLOUD_EDITION)
return
if (typeof window === 'undefined')
return
if (!currentWorkspaceId)
return
if (!isFetchedPlan) {
setShowTriggerEventsLimitModal(null)
return
}
const { type, usage, total, reset } = plan
const isUnlimited = total.triggerEvents === NUM_INFINITE
const reachedLimit = total.triggerEvents > 0 && usage.triggerEvents >= total.triggerEvents
if (type === Plan.team || isUnlimited || !reachedLimit) {
if (showTriggerEventsLimitModal)
setShowTriggerEventsLimitModal(null)
return
}
const triggerResetInDays = type === Plan.professional && total.triggerEvents !== NUM_INFINITE
? reset.triggerEvents ?? undefined
: undefined
const cycleTag = (() => {
if (typeof reset.triggerEvents === 'number')
return dayjs().startOf('day').add(reset.triggerEvents, 'day').format('YYYY-MM-DD')
if (type === Plan.sandbox)
return dayjs().endOf('month').format('YYYY-MM-DD')
return 'none'
})()
const storageKey = `${TRIGGER_EVENTS_LOCALSTORAGE_PREFIX}-${currentWorkspaceId}-${type}-${total.triggerEvents}-${cycleTag}`
if (dismissedTriggerEventsLimitStorageKeysRef.current[storageKey])
return
let persistDismiss = true
let hasDismissed = false
try {
if (localStorage.getItem(storageKey) === '1')
hasDismissed = true
}
catch {
persistDismiss = false
}
if (hasDismissed)
return
if (showTriggerEventsLimitModal?.payload.storageKey === storageKey)
return
setShowTriggerEventsLimitModal({
payload: {
usage: usage.triggerEvents,
total: total.triggerEvents,
planType: type,
resetInDays: triggerResetInDays,
storageKey,
persistDismiss,
},
})
}, [plan, isFetchedPlan, showTriggerEventsLimitModal, currentWorkspaceId])
const persistTriggerEventsLimitModalDismiss = useCallback(() => {
const storageKey = showTriggerEventsLimitModal?.payload.storageKey
if (!storageKey)
return
if (showTriggerEventsLimitModal?.payload.persistDismiss) {
try {
localStorage.setItem(storageKey, '1')
return
}
catch {
// ignore error and fall back to in-memory guard
}
}
dismissedTriggerEventsLimitStorageKeysRef.current[storageKey] = true
}, [showTriggerEventsLimitModal])
return {
showTriggerEventsLimitModal,
setShowTriggerEventsLimitModal,
persistTriggerEventsLimitModalDismiss,
}
}