選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。

hooks.ts 5.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  1. import {
  2. useCallback,
  3. useEffect,
  4. useState,
  5. } from 'react'
  6. import { useDebounceFn, useLocalStorageState } from 'ahooks'
  7. import { useSearchParams } from 'next/navigation'
  8. import type { SearchParams } from './types'
  9. import {
  10. EDUCATION_PRICING_SHOW_ACTION,
  11. EDUCATION_RE_VERIFY_ACTION,
  12. EDUCATION_VERIFYING_LOCALSTORAGE_ITEM,
  13. EDUCATION_VERIFY_URL_SEARCHPARAMS_ACTION,
  14. } from './constants'
  15. import { useEducationAutocomplete, useEducationVerify } from '@/service/use-education'
  16. import { useModalContextSelector } from '@/context/modal-context'
  17. import dayjs from 'dayjs'
  18. import utc from 'dayjs/plugin/utc'
  19. import timezone from 'dayjs/plugin/timezone'
  20. import { useAppContext } from '@/context/app-context'
  21. import { useRouter } from 'next/navigation'
  22. import { useProviderContext } from '@/context/provider-context'
  23. dayjs.extend(utc)
  24. dayjs.extend(timezone)
  25. export const useEducation = () => {
  26. const {
  27. mutateAsync,
  28. isPending,
  29. data,
  30. } = useEducationAutocomplete()
  31. const [prevSchools, setPrevSchools] = useState<string[]>([])
  32. const handleUpdateSchools = useCallback((searchParams: SearchParams) => {
  33. if (searchParams.keywords) {
  34. mutateAsync(searchParams).then((res) => {
  35. const currentPage = searchParams.page || 0
  36. const resSchools = res.data
  37. if (currentPage > 0)
  38. setPrevSchools(prevSchools => [...(prevSchools || []), ...resSchools])
  39. else
  40. setPrevSchools(resSchools)
  41. })
  42. }
  43. }, [mutateAsync])
  44. const { run: querySchoolsWithDebounced } = useDebounceFn((searchParams: SearchParams) => {
  45. handleUpdateSchools(searchParams)
  46. }, {
  47. wait: 300,
  48. })
  49. return {
  50. schools: prevSchools,
  51. setSchools: setPrevSchools,
  52. querySchoolsWithDebounced,
  53. handleUpdateSchools,
  54. isLoading: isPending,
  55. hasNext: data?.has_next,
  56. }
  57. }
  58. type useEducationReverifyNoticeParams = {
  59. onNotice: ({
  60. expireAt,
  61. expired,
  62. }: {
  63. expireAt: number
  64. expired: boolean
  65. }) => void
  66. }
  67. const isExpired = (expireAt?: number, timezone?: string) => {
  68. if (!expireAt || !timezone)
  69. return false
  70. const today = dayjs().tz(timezone).startOf('day')
  71. const expiredDay = dayjs.unix(expireAt).tz(timezone).startOf('day')
  72. return today.isSame(expiredDay) || today.isAfter(expiredDay)
  73. }
  74. const useEducationReverifyNotice = ({
  75. onNotice,
  76. }: useEducationReverifyNoticeParams) => {
  77. const { userProfile: { timezone } } = useAppContext()
  78. // const [educationInfo, setEducationInfo] = useState<{ is_student: boolean, allow_refresh: boolean, expire_at: number | null } | null>(null)
  79. // const isLoading = !educationInfo
  80. const { educationAccountExpireAt, allowRefreshEducationVerify, isLoadingEducationAccountInfo: isLoading } = useProviderContext()
  81. const [prevExpireAt, setPrevExpireAt] = useLocalStorageState<number | undefined>('education-reverify-prev-expire-at', {
  82. defaultValue: 0,
  83. })
  84. const [reverifyHasNoticed, setReverifyHasNoticed] = useLocalStorageState<boolean | undefined>('education-reverify-has-noticed', {
  85. defaultValue: false,
  86. })
  87. const [expiredHasNoticed, setExpiredHasNoticed] = useLocalStorageState<boolean | undefined>('education-expired-has-noticed', {
  88. defaultValue: false,
  89. })
  90. useEffect(() => {
  91. if (isLoading || !timezone)
  92. return
  93. if (allowRefreshEducationVerify) {
  94. const expired = isExpired(educationAccountExpireAt!, timezone)
  95. const isExpireAtChanged = prevExpireAt !== educationAccountExpireAt
  96. if (isExpireAtChanged) {
  97. setPrevExpireAt(educationAccountExpireAt!)
  98. setReverifyHasNoticed(false)
  99. setExpiredHasNoticed(false)
  100. }
  101. const shouldNotice = (() => {
  102. if (isExpireAtChanged)
  103. return true
  104. return expired ? !expiredHasNoticed : !reverifyHasNoticed
  105. })()
  106. if (shouldNotice) {
  107. onNotice({
  108. expireAt: educationAccountExpireAt!,
  109. expired,
  110. })
  111. if (expired)
  112. setExpiredHasNoticed(true)
  113. else
  114. setReverifyHasNoticed(true)
  115. }
  116. }
  117. }, [allowRefreshEducationVerify, timezone])
  118. return {
  119. isLoading,
  120. expireAt: educationAccountExpireAt!,
  121. expired: isExpired(educationAccountExpireAt!, timezone),
  122. }
  123. }
  124. export const useEducationInit = () => {
  125. const setShowAccountSettingModal = useModalContextSelector(s => s.setShowAccountSettingModal)
  126. const setShowPricingModal = useModalContextSelector(s => s.setShowPricingModal)
  127. const setShowEducationExpireNoticeModal = useModalContextSelector(s => s.setShowEducationExpireNoticeModal)
  128. const educationVerifying = localStorage.getItem(EDUCATION_VERIFYING_LOCALSTORAGE_ITEM)
  129. const searchParams = useSearchParams()
  130. const educationVerifyAction = searchParams.get('action')
  131. useEducationReverifyNotice({
  132. onNotice: (payload) => {
  133. setShowEducationExpireNoticeModal({ payload })
  134. },
  135. })
  136. const router = useRouter()
  137. const { mutateAsync } = useEducationVerify()
  138. const handleVerify = async () => {
  139. const { token } = await mutateAsync()
  140. if (token)
  141. router.push(`/education-apply?token=${token}`)
  142. }
  143. useEffect(() => {
  144. if (educationVerifying === 'yes' || educationVerifyAction === EDUCATION_VERIFY_URL_SEARCHPARAMS_ACTION) {
  145. setShowAccountSettingModal({ payload: 'billing' })
  146. if (educationVerifyAction === EDUCATION_VERIFY_URL_SEARCHPARAMS_ACTION)
  147. localStorage.setItem(EDUCATION_VERIFYING_LOCALSTORAGE_ITEM, 'yes')
  148. }
  149. if (educationVerifyAction === EDUCATION_PRICING_SHOW_ACTION)
  150. setShowPricingModal()
  151. if (educationVerifyAction === EDUCATION_RE_VERIFY_ACTION)
  152. handleVerify()
  153. }, [setShowAccountSettingModal, educationVerifying, educationVerifyAction])
  154. }