feat: notice of the expire of education verify (#24210)

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
Joel
2025-08-20 15:37:46 +08:00
committed by GitHub
parent 870e3daa95
commit ddf05ca059
20 changed files with 400 additions and 11 deletions

View File

@@ -3,16 +3,26 @@ import {
useEffect,
useState,
} from 'react'
import { useDebounceFn } from 'ahooks'
import { useDebounceFn, useLocalStorageState } from 'ahooks'
import { useSearchParams } from 'next/navigation'
import type { SearchParams } from './types'
import {
EDUCATION_PRICING_SHOW_ACTION,
EDUCATION_RE_VERIFY_ACTION,
EDUCATION_VERIFYING_LOCALSTORAGE_ITEM,
EDUCATION_VERIFY_URL_SEARCHPARAMS_ACTION,
} from './constants'
import { useEducationAutocomplete } from '@/service/use-education'
import { useEducationAutocomplete, useEducationVerify } from '@/service/use-education'
import { useModalContextSelector } from '@/context/modal-context'
import dayjs from 'dayjs'
import utc from 'dayjs/plugin/utc'
import timezone from 'dayjs/plugin/timezone'
import { useAppContext } from '@/context/app-context'
import { useRouter } from 'next/navigation'
import { useProviderContext } from '@/context/provider-context'
dayjs.extend(utc)
dayjs.extend(timezone)
export const useEducation = () => {
const {
mutateAsync,
@@ -50,12 +60,99 @@ export const useEducation = () => {
}
}
type useEducationReverifyNoticeParams = {
onNotice: ({
expireAt,
expired,
}: {
expireAt: number
expired: boolean
}) => void
}
const isExpired = (expireAt?: number, timezone?: string) => {
if (!expireAt || !timezone)
return false
const today = dayjs().tz(timezone).startOf('day')
const expiredDay = dayjs.unix(expireAt).tz(timezone).startOf('day')
return today.isSame(expiredDay) || today.isAfter(expiredDay)
}
const useEducationReverifyNotice = ({
onNotice,
}: useEducationReverifyNoticeParams) => {
const { userProfile: { timezone } } = useAppContext()
// const [educationInfo, setEducationInfo] = useState<{ is_student: boolean, allow_refresh: boolean, expire_at: number | null } | null>(null)
// const isLoading = !educationInfo
const { educationAccountExpireAt, allowRefreshEducationVerify, isLoadingEducationAccountInfo: isLoading } = useProviderContext()
const [prevExpireAt, setPrevExpireAt] = useLocalStorageState<number | undefined>('education-reverify-prev-expire-at', {
defaultValue: 0,
})
const [reverifyHasNoticed, setReverifyHasNoticed] = useLocalStorageState<boolean | undefined>('education-reverify-has-noticed', {
defaultValue: false,
})
const [expiredHasNoticed, setExpiredHasNoticed] = useLocalStorageState<boolean | undefined>('education-expired-has-noticed', {
defaultValue: false,
})
useEffect(() => {
if (isLoading || !timezone)
return
if (allowRefreshEducationVerify) {
const expired = isExpired(educationAccountExpireAt!, timezone)
const isExpireAtChanged = prevExpireAt !== educationAccountExpireAt
if (isExpireAtChanged) {
setPrevExpireAt(educationAccountExpireAt!)
setReverifyHasNoticed(false)
setExpiredHasNoticed(false)
}
const shouldNotice = (() => {
if (isExpireAtChanged)
return true
return expired ? !expiredHasNoticed : !reverifyHasNoticed
})()
if (shouldNotice) {
onNotice({
expireAt: educationAccountExpireAt!,
expired,
})
if (expired)
setExpiredHasNoticed(true)
else
setReverifyHasNoticed(true)
}
}
}, [allowRefreshEducationVerify, timezone])
return {
isLoading,
expireAt: educationAccountExpireAt!,
expired: isExpired(educationAccountExpireAt!, timezone),
}
}
export const useEducationInit = () => {
const setShowAccountSettingModal = useModalContextSelector(s => s.setShowAccountSettingModal)
const setShowPricingModal = useModalContextSelector(s => s.setShowPricingModal)
const setShowEducationExpireNoticeModal = useModalContextSelector(s => s.setShowEducationExpireNoticeModal)
const educationVerifying = localStorage.getItem(EDUCATION_VERIFYING_LOCALSTORAGE_ITEM)
const searchParams = useSearchParams()
const educationVerifyAction = searchParams.get('action')
useEducationReverifyNotice({
onNotice: (payload) => {
setShowEducationExpireNoticeModal({ payload })
},
})
const router = useRouter()
const { mutateAsync } = useEducationVerify()
const handleVerify = async () => {
const { token } = await mutateAsync()
if (token)
router.push(`/education-apply?token=${token}`)
}
useEffect(() => {
if (educationVerifying === 'yes' || educationVerifyAction === EDUCATION_VERIFY_URL_SEARCHPARAMS_ACTION) {
setShowAccountSettingModal({ payload: 'billing' })
@@ -63,5 +160,9 @@ export const useEducationInit = () => {
if (educationVerifyAction === EDUCATION_VERIFY_URL_SEARCHPARAMS_ACTION)
localStorage.setItem(EDUCATION_VERIFYING_LOCALSTORAGE_ITEM, 'yes')
}
if (educationVerifyAction === EDUCATION_PRICING_SHOW_ACTION)
setShowPricingModal()
if (educationVerifyAction === EDUCATION_RE_VERIFY_ACTION)
handleVerify()
}, [setShowAccountSettingModal, educationVerifying, educationVerifyAction])
}