refactor pricing modal query hook

This commit is contained in:
yyh 2025-12-26 15:05:23 +08:00
parent 7a31cb9064
commit 073d0aa2d0
No known key found for this signature in database
3 changed files with 60 additions and 46 deletions

View File

@ -157,7 +157,7 @@ export const ModalContextProvider = ({
children,
}: ModalContextProviderProps) => {
// Use nuqs hooks for URL-based modal state management
const [showPricingModal, setPricingModalOpen] = usePricingModal()
const { isOpen: showPricingModal, setIsOpen: setPricingModalOpen } = usePricingModal()
const [urlAccountModalState, setUrlAccountModalState] = useAccountSettingModal<AccountSettingTab>()
const accountSettingCallbacksRef = useRef<Omit<ModalState<AccountSettingTab>, 'payload'> | null>(null)

View File

@ -37,7 +37,7 @@ describe('useQueryParams hooks', () => {
const { result } = renderWithAdapter(() => usePricingModal())
// Act
const [isOpen] = result.current
const { isOpen } = result.current
// Assert
expect(isOpen).toBe(false)
@ -51,7 +51,7 @@ describe('useQueryParams hooks', () => {
)
// Act
const [isOpen] = result.current
const { isOpen } = result.current
// Assert
expect(isOpen).toBe(true)
@ -65,7 +65,7 @@ describe('useQueryParams hooks', () => {
)
// Act
const [isOpen] = result.current
const { isOpen } = result.current
// Assert
expect(isOpen).toBe(false)
@ -77,7 +77,7 @@ describe('useQueryParams hooks', () => {
// Act
act(() => {
result.current[1](true)
result.current.setIsOpen(true)
})
// Assert
@ -92,7 +92,7 @@ describe('useQueryParams hooks', () => {
// Act
act(() => {
result.current[1](true)
result.current.setIsOpen(true)
})
// Assert
@ -110,7 +110,7 @@ describe('useQueryParams hooks', () => {
// Act
act(() => {
result.current[1](false)
result.current.setIsOpen(false)
})
// Assert
@ -128,7 +128,7 @@ describe('useQueryParams hooks', () => {
// Act
act(() => {
result.current[1](false)
result.current.setIsOpen(false)
})
// Assert
@ -143,7 +143,7 @@ describe('useQueryParams hooks', () => {
// Act
act(() => {
result.current[1](true, { history: 'replace' })
result.current.setIsOpen(true, { history: 'replace' })
})
// Assert

View File

@ -15,12 +15,13 @@
import type { Options } from 'nuqs'
import {
createParser,
parseAsArrayOf,
parseAsString,
useQueryState,
useQueryStates,
} from 'nuqs'
import { useCallback } from 'react'
import { ACCOUNT_SETTING_MODAL_ACTION } from '@/app/components/header/account-setting/constants'
/**
@ -29,28 +30,35 @@ import { ACCOUNT_SETTING_MODAL_ACTION } from '@/app/components/header/account-se
*/
export const PRICING_MODAL_QUERY_PARAM = 'pricing'
export const PRICING_MODAL_QUERY_VALUE = 'open'
const parseAsPricingModal = createParser<boolean>({
parse: value => (value === PRICING_MODAL_QUERY_VALUE ? true : null),
serialize: value => (value ? PRICING_MODAL_QUERY_VALUE : ''),
})
/**
* Hook to manage pricing modal state via URL
* @returns [isOpen, setIsOpen] - Tuple like useState
* @returns { isOpen, setIsOpen } - isOpen boolean and setter
*
* @example
* const [isPricingModalOpen, setIsPricingModalOpen] = usePricingModal()
* setIsPricingModalOpen(true) // Sets ?pricing=open
* setIsPricingModalOpen(false) // Removes ?pricing
* const { isOpen, setIsOpen } = usePricingModal()
* setIsOpen(true) // Sets ?pricing=open
* setIsOpen(false) // Removes ?pricing
*/
export function usePricingModal() {
const [isOpen, setIsOpen] = useQueryState(
const [isOpen, setIsOpenState] = useQueryState(
PRICING_MODAL_QUERY_PARAM,
parseAsString,
parseAsPricingModal.withDefault(false),
)
const setIsOpenBoolean = (open: boolean, options?: Options) => {
const history = options?.history ?? (open ? 'push' : 'replace')
setIsOpen(open ? PRICING_MODAL_QUERY_VALUE : null, { ...options, history })
}
const setIsOpen = useCallback(
(open: boolean, options?: Options) => {
const history = options?.history ?? (open ? 'push' : 'replace')
setIsOpenState(open ? true : null, { ...options, history })
},
[setIsOpenState],
)
return [isOpen === PRICING_MODAL_QUERY_VALUE, setIsOpenBoolean] as const
return { isOpen, setIsOpen }
}
/**
@ -73,17 +81,20 @@ export function useAccountSettingModal<T extends string = string>() {
},
)
const setState = (state: { payload: T } | null) => {
if (!state) {
setAccountState({ action: null, tab: null }, { history: 'replace' })
return
}
const shouldPush = accountState.action !== ACCOUNT_SETTING_MODAL_ACTION
setAccountState(
{ action: ACCOUNT_SETTING_MODAL_ACTION, tab: state.payload },
{ history: shouldPush ? 'push' : 'replace' },
)
}
const setState = useCallback(
(state: { payload: T } | null) => {
if (!state) {
setAccountState({ action: null, tab: null }, { history: 'replace' })
return
}
const shouldPush = accountState.action !== ACCOUNT_SETTING_MODAL_ACTION
setAccountState(
{ action: ACCOUNT_SETTING_MODAL_ACTION, tab: state.payload },
{ history: shouldPush ? 'push' : 'replace' },
)
},
[accountState.action, setAccountState],
)
const isOpen = accountState.action === ACCOUNT_SETTING_MODAL_ACTION
const currentTab = (isOpen ? accountState.tab : null) as T | null
@ -149,20 +160,23 @@ export function usePluginInstallation() {
parseAsString,
)
const setInstallState = (state: { packageId?: string, bundleInfo?: string } | null) => {
if (!state) {
setPackageIds(null)
setBundleInfo(null)
return
}
if (state.packageId) {
// Store as JSON array for consistency with existing code
setPackageIds(JSON.stringify([state.packageId]))
}
if (state.bundleInfo) {
setBundleInfo(state.bundleInfo)
}
}
const setInstallState = useCallback(
(state: { packageId?: string, bundleInfo?: string } | null) => {
if (!state) {
setPackageIds(null)
setBundleInfo(null)
return
}
if (state.packageId) {
// Store as JSON array for consistency with existing code
setPackageIds(JSON.stringify([state.packageId]))
}
if (state.bundleInfo) {
setBundleInfo(state.bundleInfo)
}
},
[setBundleInfo, setPackageIds],
)
// Parse packageIds from JSON array
const currentPackageId = packageIds