mirror of https://github.com/langgenius/dify.git
223 lines
6.2 KiB
TypeScript
223 lines
6.2 KiB
TypeScript
'use client'
|
|
|
|
/**
|
|
* Centralized URL query parameter management hooks using nuqs
|
|
*
|
|
* This file provides type-safe, performant query parameter management
|
|
* that doesn't trigger full page refreshes (shallow routing).
|
|
*
|
|
* Best practices from nuqs documentation:
|
|
* - Use useQueryState for single parameters
|
|
* - Use useQueryStates for multiple related parameters (atomic updates)
|
|
* - Always provide parsers with defaults for type safety
|
|
* - Use shallow routing to avoid unnecessary re-renders
|
|
*/
|
|
|
|
import {
|
|
createParser,
|
|
parseAsArrayOf,
|
|
parseAsString,
|
|
useQueryState,
|
|
useQueryStates,
|
|
} from 'nuqs'
|
|
import { useCallback } from 'react'
|
|
import { ACCOUNT_SETTING_MODAL_ACTION } from '@/app/components/header/account-setting/constants'
|
|
|
|
/**
|
|
* Modal State Query Parameters
|
|
* Manages modal visibility and configuration via URL
|
|
*/
|
|
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 : ''),
|
|
})
|
|
.withDefault(false)
|
|
.withOptions({ history: 'push' })
|
|
|
|
/**
|
|
* Hook to manage pricing modal state via URL
|
|
* @returns [isOpen, setIsOpen] - Tuple like useState
|
|
*
|
|
* @example
|
|
* const [isOpen, setIsOpen] = usePricingModal()
|
|
* setIsOpen(true) // Sets ?pricing=open
|
|
* setIsOpen(false) // Removes ?pricing
|
|
*/
|
|
export function usePricingModal() {
|
|
return useQueryState(
|
|
PRICING_MODAL_QUERY_PARAM,
|
|
parseAsPricingModal,
|
|
)
|
|
}
|
|
|
|
/**
|
|
* Hook to manage account setting modal state via URL
|
|
* @returns [state, setState] - Object with isOpen + payload (tab) and setter
|
|
*
|
|
* @example
|
|
* const [accountModalState, setAccountModalState] = useAccountSettingModal()
|
|
* setAccountModalState({ payload: 'billing' }) // Sets ?action=showSettings&tab=billing
|
|
* setAccountModalState(null) // Removes both params
|
|
*/
|
|
export function useAccountSettingModal<T extends string = string>() {
|
|
const [accountState, setAccountState] = useQueryStates(
|
|
{
|
|
action: parseAsString,
|
|
tab: parseAsString,
|
|
},
|
|
{
|
|
history: '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
|
|
|
|
return [{ isOpen, payload: currentTab }, setState] as const
|
|
}
|
|
|
|
/**
|
|
* Marketplace Search Query Parameters
|
|
*/
|
|
export type MarketplaceFilters = {
|
|
q: string // search query
|
|
category: string // plugin category
|
|
tags: string[] // array of tags
|
|
}
|
|
|
|
/**
|
|
* Hook to manage marketplace search/filter state via URL
|
|
* Provides atomic updates - all params update together
|
|
*
|
|
* @example
|
|
* const [filters, setFilters] = useMarketplaceFilters()
|
|
* setFilters({ q: 'search', category: 'tool', tags: ['ai'] }) // Updates all at once
|
|
* setFilters({ q: '' }) // Only updates q, keeps others
|
|
* setFilters(null) // Clears all marketplace params
|
|
*/
|
|
export function useMarketplaceFilters() {
|
|
return useQueryStates(
|
|
{
|
|
q: parseAsString.withDefault(''),
|
|
category: parseAsString.withDefault('all').withOptions({ clearOnDefault: false }),
|
|
tags: parseAsArrayOf(parseAsString).withDefault([]),
|
|
},
|
|
{
|
|
// Update URL without pushing to history (replaceState behavior)
|
|
history: 'replace',
|
|
},
|
|
)
|
|
}
|
|
|
|
/**
|
|
* Plugin Installation Query Parameters
|
|
*/
|
|
const PACKAGE_IDS_PARAM = 'package-ids'
|
|
const BUNDLE_INFO_PARAM = 'bundle-info'
|
|
type BundleInfoQuery = {
|
|
org: string
|
|
name: string
|
|
version: string
|
|
}
|
|
|
|
const parseAsPackageId = createParser<string>({
|
|
parse: (value) => {
|
|
try {
|
|
const parsed = JSON.parse(value)
|
|
if (Array.isArray(parsed)) {
|
|
const first = parsed[0]
|
|
return typeof first === 'string' ? first : null
|
|
}
|
|
return value
|
|
}
|
|
catch {
|
|
return value
|
|
}
|
|
},
|
|
serialize: value => JSON.stringify([value]),
|
|
})
|
|
|
|
const parseAsBundleInfo = createParser<BundleInfoQuery>({
|
|
parse: (value) => {
|
|
try {
|
|
const parsed = JSON.parse(value) as Partial<BundleInfoQuery>
|
|
if (parsed
|
|
&& typeof parsed.org === 'string'
|
|
&& typeof parsed.name === 'string'
|
|
&& typeof parsed.version === 'string') {
|
|
return { org: parsed.org, name: parsed.name, version: parsed.version }
|
|
}
|
|
}
|
|
catch {
|
|
return null
|
|
}
|
|
return null
|
|
},
|
|
serialize: value => JSON.stringify(value),
|
|
})
|
|
|
|
/**
|
|
* Hook to manage plugin installation state via URL
|
|
* @returns [installState, setInstallState] - installState includes parsed packageId and bundleInfo
|
|
*
|
|
* @example
|
|
* const [installState, setInstallState] = usePluginInstallation()
|
|
* setInstallState({ packageId: 'org/plugin' }) // Sets ?package-ids=["org/plugin"]
|
|
* setInstallState({ bundleInfo: { org: 'org', name: 'bundle', version: '1.0.0' } }) // Sets ?bundle-info=...
|
|
* setInstallState(null) // Clears installation params
|
|
*/
|
|
export function usePluginInstallation() {
|
|
return useQueryStates(
|
|
{
|
|
packageId: parseAsPackageId,
|
|
bundleInfo: parseAsBundleInfo,
|
|
},
|
|
{
|
|
urlKeys: {
|
|
packageId: PACKAGE_IDS_PARAM,
|
|
bundleInfo: BUNDLE_INFO_PARAM,
|
|
},
|
|
},
|
|
)
|
|
}
|
|
|
|
/**
|
|
* Utility to clear specific query parameters from URL
|
|
* This is a client-side utility that should be called from client components
|
|
*
|
|
* @param keys - Single key or array of keys to remove from URL
|
|
*
|
|
* @example
|
|
* // In a client component
|
|
* clearQueryParams('param1')
|
|
* clearQueryParams(['param1', 'param2'])
|
|
*/
|
|
export function clearQueryParams(keys: string | string[]) {
|
|
if (typeof window === 'undefined')
|
|
return
|
|
|
|
const url = new URL(window.location.href)
|
|
const keysArray = Array.isArray(keys) ? keys : [keys]
|
|
|
|
keysArray.forEach(key => url.searchParams.delete(key))
|
|
|
|
window.history.replaceState(null, '', url.toString())
|
|
}
|