fix: use timestamp hook

This commit is contained in:
yyh 2026-06-24 16:19:55 +08:00
parent a656e514aa
commit 5bb8658b0d
No known key found for this signature in database
6 changed files with 44 additions and 29 deletions

View File

@ -57,6 +57,9 @@ const ChatWrapper = () => {
} = useChatWithHistoryContext()
const appSourceType = isInstalledApp ? AppSourceType.installedApp : AppSourceType.webApp
const timezone = appSourceType === AppSourceType.webApp
? Intl.DateTimeFormat().resolvedOptions().timeZone
: undefined
// Semantic variable for better code readability
const isHistoryConversation = !!currentConversationId
@ -91,6 +94,8 @@ const ChatWrapper = () => {
taskId => stopChatMessageResponding('', taskId, appSourceType, appId),
clearChatList,
setClearChatList,
undefined,
{ timezone },
)
const inputsFormValue = currentConversationId ? currentConversationInputs : newConversationInputsRef?.current
const inputDisabled = useMemo(() => {

View File

@ -6,6 +6,10 @@ import { useParams, usePathname } from '@/next/navigation'
import { sseGet, ssePost } from '@/service/base'
import { useChat } from '../hooks'
const useTimestampMock = vi.hoisted(() =>
vi.fn(() => ({ formatTime: vi.fn().mockReturnValue('10:00 AM') })),
)
vi.mock('@/service/base', () => ({
sseGet: vi.fn(),
ssePost: vi.fn(),
@ -31,7 +35,7 @@ vi.mock('@langgenius/dify-ui/toast', () => ({
}))
vi.mock('@/hooks/use-timestamp', () => ({
default: () => ({ formatTime: vi.fn().mockReturnValue('10:00 AM') }),
default: useTimestampMock,
}))
vi.mock('@/next/navigation', () => ({
@ -91,6 +95,21 @@ describe('useChat', () => {
expect(result.current.suggestedQuestions).toEqual([])
})
it('should pass timestamp options to timestamp formatter', () => {
renderHook(() => useChat(
undefined,
undefined,
undefined,
undefined,
undefined,
undefined,
undefined,
{ timezone: 'UTC' },
))
expect(useTimestampMock).toHaveBeenCalledWith({ timezone: 'UTC' })
})
it('should initialize with opening statement and suggested questions', () => {
const config = {
opening_statement: 'Hello {{name}}',

View File

@ -55,6 +55,10 @@ type SendCallback = {
isPublicAPI?: boolean
}
type UseChatOptions = {
timezone?: string
}
export const useChat = (
config?: ChatConfig,
formSettings?: {
@ -66,9 +70,10 @@ export const useChat = (
clearChatList?: boolean,
clearChatListCallback?: (state: boolean) => void,
initialConversationId?: string,
options: UseChatOptions = {},
) => {
const { t } = useTranslation()
const { formatTime } = useTimestamp()
const { formatTime } = useTimestamp({ timezone: options.timezone })
const conversationIdRef = useRef(initialConversationId ?? '')
const initialConversationIdRef = useRef(initialConversationId ?? '')
const hasStopRespondedRef = useRef(false)

View File

@ -81,6 +81,9 @@ const ChatWrapper = () => {
opening_statement: currentConversationItem?.introduction || (config as any).opening_statement,
} as ChatConfig
}, [appParams, currentConversationItem?.introduction])
const timezone = appSourceType === AppSourceType.webApp
? Intl.DateTimeFormat().resolvedOptions().timeZone
: undefined
const {
chatList,
handleSend,
@ -98,6 +101,8 @@ const ChatWrapper = () => {
taskId => stopChatMessageResponding('', taskId, appSourceType, appId),
clearChatList,
setClearChatList,
undefined,
{ timezone },
)
const inputsFormValue = currentConversationId ? currentConversationInputs : newConversationInputsRef?.current
const inputDisabled = useMemo(() => {

View File

@ -6,14 +6,6 @@ import { userProfileQueryOptions } from '@/features/account-profile/client'
import { createAccountProfileQueryWrapper } from '@/test/account-profile-query'
import useTimestamp from './use-timestamp'
const navigationMocks = vi.hoisted(() => ({
pathname: '/apps',
}))
vi.mock('@/next/navigation', () => ({
usePathname: () => navigationMocks.pathname,
}))
vi.mock('@/context/app-context', () => ({
useAppContext: vi.fn(() => ({
userProfile: {
@ -50,10 +42,6 @@ const createEmptyQueryWrapper = () => {
}
describe('useTimestamp', () => {
beforeEach(() => {
navigationMocks.pathname = '/apps'
})
describe('formatTime', () => {
it('should format unix timestamp correctly', () => {
const { result } = renderHook(() => useTimestamp(), { wrapper: createAccountProfileQueryWrapper() })
@ -96,13 +84,12 @@ describe('useTimestamp', () => {
})
})
it('should not request account profile on public webapp routes', () => {
navigationMocks.pathname = '/chatbot/share-token'
it('should not request account profile when timezone is provided', () => {
const { queryClient, wrapper } = createEmptyQueryWrapper()
const { result } = renderHook(() => useTimestamp(), { wrapper })
const { result } = renderHook(() => useTimestamp({ timezone: 'UTC' }), { wrapper })
expect(result.current.formatTime(1704132000, 'YYYY')).toBe('2024')
expect(result.current.formatTime(1704132000, 'YYYY-MM-DD HH:mm')).toBe('2024-01-01 18:00')
expect(queryClient.isFetching({ queryKey: userProfileQueryOptions().queryKey })).toBe(0)
expect(queryClient.getQueryState(userProfileQueryOptions().queryKey)?.fetchStatus).toBe('idle')
})

View File

@ -5,31 +5,25 @@ import timezone from 'dayjs/plugin/timezone'
import utc from 'dayjs/plugin/utc'
import { useCallback } from 'react'
import { userProfileQueryOptions } from '@/features/account-profile/client'
import { usePathname } from '@/next/navigation'
dayjs.extend(utc)
dayjs.extend(timezone)
const PUBLIC_WEBAPP_ROUTE_SEGMENTS = new Set(['agent', 'chat', 'chatbot', 'completion', 'workflow'])
const isPublicWebAppPath = (pathname: string | null) => {
const segment = pathname?.split('/').find(Boolean)
return segment ? PUBLIC_WEBAPP_ROUTE_SEGMENTS.has(segment) : false
type UseTimestampOptions = {
timezone?: string
}
const getBrowserTimezone = () => {
return Intl.DateTimeFormat().resolvedOptions().timeZone
}
const useTimestamp = () => {
const pathname = usePathname()
const shouldUseAccountTimezone = !isPublicWebAppPath(pathname)
const useTimestamp = ({ timezone: timezoneOverride }: UseTimestampOptions = {}) => {
const { data: accountTimezone } = useQuery({
...userProfileQueryOptions(),
select: data => data.profile.timezone ?? undefined,
enabled: shouldUseAccountTimezone,
enabled: timezoneOverride === undefined,
})
const resolvedTimezone = accountTimezone ?? getBrowserTimezone()
const resolvedTimezone = timezoneOverride ?? accountTimezone ?? getBrowserTimezone()
const formatTime = useCallback((value: number, format: string) => {
return dayjs.unix(value).tz(resolvedTimezone).format(format)