Files
cherry-studio/src/renderer/src/windows/dataRefactorTest/components/CacheBasicTests.tsx
T

434 lines
15 KiB
TypeScript

import { Input } from '@cherrystudio/ui'
import { useCache, usePersistCache, useSharedCache } from '@renderer/data/hooks/useCache'
import { usePreference } from '@renderer/data/hooks/usePreference'
import { loggerService } from '@renderer/services/LoggerService'
import type { RendererPersistCacheKey } from '@shared/data/cache/cacheSchemas'
import { ThemeMode } from '@shared/data/preference/preferenceTypes'
import { Button, Card, Col, Divider, message, Row, Select, Slider, Space, Typography } from 'antd'
import { Database, Edit, Eye, HardDrive, RefreshCw, Users, Zap } from 'lucide-react'
import React, { useRef, useState } from 'react'
import styled from 'styled-components'
const { Text } = Typography
const { Option } = Select
const logger = loggerService.withContext('CacheBasicTests')
/**
* Basic cache hooks testing component
* Tests useCache, useSharedCache, and usePersistCache hooks
*/
const CacheBasicTests: React.FC = () => {
const [currentTheme] = usePreference('ui.theme_mode')
const isDarkTheme = currentTheme === ThemeMode.dark
// useCache testing
const [memoryCacheKey, setMemoryCacheKey] = useState('test-hook-memory-1')
const [memoryCacheDefault, setMemoryCacheDefault] = useState('default-memory-value')
const [newMemoryValue, setNewMemoryValue] = useState('')
const [memoryValue, setMemoryValue] = useCache(memoryCacheKey as any, memoryCacheDefault)
// useSharedCache testing
const [sharedCacheKey, setSharedCacheKey] = useState('test-hook-shared-1')
const [sharedCacheDefault, setSharedCacheDefault] = useState('default-shared-value')
const [newSharedValue, setNewSharedValue] = useState('')
const [sharedValue, setSharedValue] = useSharedCache(sharedCacheKey as any, sharedCacheDefault)
// usePersistCache testing
const [persistCacheKey, setPersistCacheKey] = useState<RendererPersistCacheKey>('example-1')
const [newPersistValue, setNewPersistValue] = useState('')
const [persistValue, setPersistValue] = usePersistCache(persistCacheKey)
// Testing different data types
const [numberKey] = useState('test-number-cache' as const)
const [numberValue, setNumberValue] = useCache(numberKey as any, 42)
const [objectKey] = useState('test-object-cache' as const)
const [objectValue, setObjectValue] = useCache(objectKey as any, { name: 'test', count: 0, active: true })
// Stats
const renderCountRef = useRef(0)
const [displayRenderCount, setDisplayRenderCount] = useState(0)
const [updateCount, setUpdateCount] = useState(0)
// Available persist keys
const persistKeys: RendererPersistCacheKey[] = ['example-1', 'example-2', 'example-3', 'example-4']
// Update render count without causing re-renders
renderCountRef.current += 1
const parseValue = (value: string): any => {
if (!value) return undefined
try {
return JSON.parse(value)
} catch {
return value
}
}
const formatValue = (value: any): string => {
if (value === undefined) return 'undefined'
if (value === null) return 'null'
if (typeof value === 'string') return `"${value}"`
return JSON.stringify(value, null, 2)
}
// Memory cache operations
const handleMemoryUpdate = () => {
try {
const parsed = parseValue(newMemoryValue)
setMemoryValue(parsed)
setNewMemoryValue('')
setUpdateCount((prev) => prev + 1)
message.success(`Memory cache updated: ${memoryCacheKey}`)
logger.info('Memory cache updated via hook', { key: memoryCacheKey, value: parsed })
} catch (error) {
message.error(`Memory cache update failed: ${(error as Error).message}`)
}
}
// Shared cache operations
const handleSharedUpdate = () => {
try {
const parsed = parseValue(newSharedValue)
setSharedValue(parsed)
setNewSharedValue('')
setUpdateCount((prev) => prev + 1)
message.success(`Shared cache updated: ${sharedCacheKey} (broadcasted to other windows)`)
logger.info('Shared cache updated via hook', { key: sharedCacheKey, value: parsed })
} catch (error) {
message.error(`Shared cache update failed: ${(error as Error).message}`)
}
}
// Persist cache operations
const handlePersistUpdate = () => {
try {
let parsed: any
// Handle different types based on schema
if (persistCacheKey === 'example-1') {
parsed = newPersistValue // string
} else if (persistCacheKey === 'example-2') {
parsed = parseInt(newPersistValue) || 0 // number
} else if (persistCacheKey === 'example-3') {
parsed = newPersistValue === 'true' // boolean
} else if (persistCacheKey === 'example-4') {
parsed = parseValue(newPersistValue) // object
}
setPersistValue(parsed as any)
setNewPersistValue('')
setUpdateCount((prev) => prev + 1)
message.success(`Persist cache updated: ${persistCacheKey} (saved + broadcasted)`)
logger.info('Persist cache updated via hook', { key: persistCacheKey, value: parsed })
} catch (error) {
message.error(`Persist cache update failed: ${(error as Error).message}`)
}
}
// Test different data types
const handleNumberUpdate = (newValue: number) => {
setNumberValue(newValue)
setUpdateCount((prev) => prev + 1)
logger.info('Number cache updated', { value: newValue })
}
const handleObjectUpdate = (field: string, value: any) => {
const currentValue = objectValue || { name: 'test', count: 0, active: true }
setObjectValue({ ...currentValue, [field]: value })
setUpdateCount((prev) => prev + 1)
logger.info('Object cache updated', { field, value })
}
return (
<TestContainer $isDark={isDarkTheme}>
<Space direction="vertical" size="large" style={{ width: '100%' }}>
<div style={{ textAlign: 'center' }}>
<Space>
<Text type="secondary">
React Hook Tests Renders: {displayRenderCount || renderCountRef.current} Updates: {updateCount}
</Text>
<Button
size="small"
onClick={() => {
renderCountRef.current = 0
setDisplayRenderCount(0)
setUpdateCount(0)
}}>
Reset Stats
</Button>
</Space>
</div>
<Row gutter={[16, 16]}>
{/* useCache Testing */}
<Col span={8}>
<Card
title={
<Space>
<Zap size={16} />
<Text>useCache Hook</Text>
</Space>
}
size="small"
style={{
backgroundColor: isDarkTheme ? '#1f1f1f' : '#fff',
borderColor: isDarkTheme ? '#303030' : '#d9d9d9'
}}>
<Space direction="vertical" size="small" style={{ width: '100%' }}>
<Input
placeholder="Cache Key"
value={memoryCacheKey}
onChange={(e) => setMemoryCacheKey(e.target.value)}
prefix={<Database size={14} />}
/>
<Input
placeholder="Default Value"
value={memoryCacheDefault}
onChange={(e) => setMemoryCacheDefault(e.target.value)}
prefix={<Eye size={14} />}
/>
<Input
placeholder="New Value"
value={newMemoryValue}
onChange={(e) => setNewMemoryValue(e.target.value)}
onPressEnter={handleMemoryUpdate}
prefix={<Edit size={14} />}
/>
<Button type="primary" onClick={handleMemoryUpdate} disabled={!newMemoryValue} block>
Update Memory Cache
</Button>
<ResultDisplay $isDark={isDarkTheme}>
<Text strong>Current Value:</Text>
<pre>{formatValue(memoryValue)}</pre>
</ResultDisplay>
</Space>
</Card>
</Col>
{/* useSharedCache Testing */}
<Col span={8}>
<Card
title={
<Space>
<Users size={16} />
<Text>useSharedCache Hook</Text>
</Space>
}
size="small"
style={{
backgroundColor: isDarkTheme ? '#1f1f1f' : '#fff',
borderColor: isDarkTheme ? '#303030' : '#d9d9d9'
}}>
<Space direction="vertical" size="small" style={{ width: '100%' }}>
<Input
placeholder="Cache Key"
value={sharedCacheKey}
onChange={(e) => setSharedCacheKey(e.target.value)}
prefix={<Database size={14} />}
/>
<Input
placeholder="Default Value"
value={sharedCacheDefault}
onChange={(e) => setSharedCacheDefault(e.target.value)}
prefix={<Eye size={14} />}
/>
<Input
placeholder="New Value"
value={newSharedValue}
onChange={(e) => setNewSharedValue(e.target.value)}
onPressEnter={handleSharedUpdate}
prefix={<Edit size={14} />}
/>
<Button type="primary" onClick={handleSharedUpdate} disabled={!newSharedValue} block>
Update Shared Cache
</Button>
<ResultDisplay $isDark={isDarkTheme}>
<Text strong>Current Value:</Text>
<pre>{formatValue(sharedValue)}</pre>
</ResultDisplay>
</Space>
</Card>
</Col>
{/* usePersistCache Testing */}
<Col span={8}>
<Card
title={
<Space>
<HardDrive size={16} />
<Text>usePersistCache Hook</Text>
</Space>
}
size="small"
style={{
backgroundColor: isDarkTheme ? '#1f1f1f' : '#fff',
borderColor: isDarkTheme ? '#303030' : '#d9d9d9'
}}>
<Space direction="vertical" size="small" style={{ width: '100%' }}>
<Select
value={persistCacheKey}
onChange={setPersistCacheKey}
style={{ width: '100%' }}
placeholder="Select persist key">
{persistKeys.map((key) => (
<Option key={key} value={key}>
{key}
</Option>
))}
</Select>
<Input
placeholder="New Value"
value={newPersistValue}
onChange={(e) => setNewPersistValue(e.target.value)}
onPressEnter={handlePersistUpdate}
prefix={<Edit size={14} />}
/>
<Button type="primary" onClick={handlePersistUpdate} disabled={!newPersistValue} block>
Update Persist Cache
</Button>
<ResultDisplay $isDark={isDarkTheme}>
<Text strong>Current Value:</Text>
<pre>{formatValue(persistValue)}</pre>
</ResultDisplay>
</Space>
</Card>
</Col>
</Row>
<Divider />
{/* Data Type Testing */}
<Row gutter={[16, 16]}>
<Col span={12}>
<Card
title={
<Space>
<RefreshCw size={16} />
<Text>Number Type Testing</Text>
</Space>
}
size="small"
style={{
backgroundColor: isDarkTheme ? '#1f1f1f' : '#fff',
borderColor: isDarkTheme ? '#303030' : '#d9d9d9'
}}>
<Space direction="vertical" style={{ width: '100%' }}>
<Text>
Key: <code>{numberKey}</code>
</Text>
<Text>
Current Value: <strong>{numberValue}</strong>
</Text>
<Slider
min={0}
max={100}
value={typeof numberValue === 'number' ? numberValue : 42}
onChange={handleNumberUpdate}
/>
<Space>
<Button size="small" onClick={() => handleNumberUpdate(0)}>
Reset to 0
</Button>
<Button size="small" onClick={() => handleNumberUpdate(Math.floor(Math.random() * 100))}>
Random
</Button>
</Space>
</Space>
</Card>
</Col>
<Col span={12}>
<Card
title={
<Space>
<Database size={16} />
<Text>Object Type Testing</Text>
</Space>
}
size="small"
style={{
backgroundColor: isDarkTheme ? '#1f1f1f' : '#fff',
borderColor: isDarkTheme ? '#303030' : '#d9d9d9'
}}>
<Space direction="vertical" style={{ width: '100%' }}>
<Text>
Key: <code>{objectKey}</code>
</Text>
<Space>
<Input
placeholder="Name"
value={objectValue?.name || ''}
onChange={(e) => handleObjectUpdate('name', e.target.value)}
style={{ width: 120 }}
/>
<Input
placeholder="Count"
type="number"
value={objectValue?.count || 0}
onChange={(e) => handleObjectUpdate('count', parseInt(e.target.value) || 0)}
style={{ width: 80 }}
/>
<Button
type={objectValue?.active ? 'primary' : 'default'}
onClick={() => handleObjectUpdate('active', !objectValue?.active)}>
{objectValue?.active ? 'Active' : 'Inactive'}
</Button>
</Space>
<ResultDisplay $isDark={isDarkTheme}>
<pre>{formatValue(objectValue)}</pre>
</ResultDisplay>
</Space>
</Card>
</Col>
</Row>
<div style={{ textAlign: 'center' }}>
<Text type="secondary" style={{ fontSize: 12 }}>
💡 提示: useCache 仅在当前窗口有效 useSharedCache 跨窗口实时同步 usePersistCache 类型安全的持久化存储
</Text>
</div>
</Space>
</TestContainer>
)
}
const TestContainer = styled.div<{ $isDark: boolean }>`
color: ${(props) => (props.$isDark ? '#fff' : '#000')};
`
const ResultDisplay = styled.div<{ $isDark: boolean }>`
background: ${(props) => (props.$isDark ? '#0d1117' : '#f6f8fa')};
border: 1px solid ${(props) => (props.$isDark ? '#30363d' : '#d0d7de')};
border-radius: 6px;
padding: 8px;
font-size: 11px;
max-height: 100px;
overflow-y: auto;
pre {
margin: 0;
white-space: pre-wrap;
word-break: break-all;
color: ${(props) => (props.$isDark ? '#e6edf3' : '#1f2328')};
font-family: 'SF Mono', Monaco, 'Cascadia Code', 'Roboto Mono', Consolas, 'Courier New', monospace;
}
`
export default CacheBasicTests