This commit implements a major architectural refactor to improve modularity, type safety, and maintainability across the codebase.
Key changes include:
- **Responsive System Refactor**: Migrated from a monolithic `useResponsive` hook to a set of specialized, granular hooks (`useBreakpoint`, `useOrientation`, `usePlatform`, etc.) located in `src/presentation/hooks/responsive`. This reduces unnecessary re-renders and improves developer experience.
- **Core Service Restructuring**: Introduced a new directory structure for services, including dedicated folders for `datasources`, `mappers`, and domain-specific types (`message`, `post`).
- **Data Layer Improvements**:
- Centralized JSON parsing logic in `src/database/core/jsonUtils.ts`.
- Cleaned up repository implementations by removing redundant local utility functions.
- Refactored `MessageMapper` to use the new centralized JSON utility.
- **Type System Cleanup**:
- Decomposed the large `src/types/dto.ts` into a modular `src/types/dto/` directory.
- Simplified `src/types/index.ts` and introduced backward-compatible aliases for core entities.
- **Utility Consolidation**:
- Created a centralized `src/utils/formatTime.ts` to replace fragmented date formatting logic across various screens and components.
- Removed deprecated responsive utility files in favor of the new hook-based system.
- **Service Logic Refinement**: Refactored `ApiClient` and `WebSocketService` to use a centralized `showVerificationModal` service, removing duplicated state management for verification prompts.
169 lines
4.2 KiB
TypeScript
169 lines
4.2 KiB
TypeScript
/**
|
||
* 响应式网格布局组件
|
||
* 根据断点自动调整列数,支持间距配置
|
||
*/
|
||
|
||
import React, { useMemo } from 'react';
|
||
import {
|
||
View,
|
||
StyleProp,
|
||
ViewStyle,
|
||
StyleSheet,
|
||
Dimensions,
|
||
} from 'react-native';
|
||
import {
|
||
useResponsive,
|
||
useColumnCount,
|
||
useResponsiveSpacing,
|
||
FineBreakpointKey,
|
||
} from '../../hooks';
|
||
|
||
export interface ResponsiveGridProps {
|
||
/** 子元素 */
|
||
children: React.ReactNode[];
|
||
/** 自定义样式 */
|
||
style?: StyleProp<ViewStyle>;
|
||
/** 容器样式 */
|
||
containerStyle?: StyleProp<ViewStyle>;
|
||
/** 项目样式 */
|
||
itemStyle?: StyleProp<ViewStyle>;
|
||
/** 列数配置,根据断点设置 */
|
||
columns?: Partial<Record<FineBreakpointKey, number>>;
|
||
/** 间距配置 */
|
||
gap?: Partial<Record<FineBreakpointKey, number>>;
|
||
/** 行间距(默认等于 gap) */
|
||
rowGap?: Partial<Record<FineBreakpointKey, number>>;
|
||
/** 列间距(默认等于 gap) */
|
||
columnGap?: Partial<Record<FineBreakpointKey, number>>;
|
||
/** 是否启用等宽列 */
|
||
equalColumns?: boolean;
|
||
/** 自定义列宽计算函数 */
|
||
getColumnWidth?: (containerWidth: number, columns: number, gap: number) => number;
|
||
/** 渲染空状态 */
|
||
renderEmpty?: () => React.ReactNode;
|
||
/** key 提取函数 */
|
||
keyExtractor?: (item: React.ReactNode, index: number) => string;
|
||
}
|
||
|
||
/**
|
||
* 响应式网格布局组件
|
||
*
|
||
* 根据屏幕宽度自动调整列数,支持平板/桌面端的多列布局
|
||
*
|
||
* @example
|
||
* <ResponsiveGrid
|
||
* columns={{ xs: 1, sm: 2, md: 3, lg: 4 }}
|
||
* gap={{ xs: 8, md: 16 }}
|
||
* >
|
||
* {items.map(item => <Card key={item.id} {...item} />)}
|
||
* </ResponsiveGrid>
|
||
*/
|
||
export function ResponsiveGrid({
|
||
children,
|
||
style,
|
||
containerStyle,
|
||
itemStyle,
|
||
columns: columnsConfig,
|
||
gap: gapConfig,
|
||
rowGap: rowGapConfig,
|
||
columnGap: columnGapConfig,
|
||
equalColumns = true,
|
||
getColumnWidth,
|
||
renderEmpty,
|
||
keyExtractor,
|
||
}: ResponsiveGridProps) {
|
||
const { width } = useResponsive();
|
||
const columns = useColumnCount(columnsConfig);
|
||
const defaultGap = useResponsiveSpacing(gapConfig);
|
||
const rowGap = useResponsiveSpacing(rowGapConfig ?? gapConfig);
|
||
const columnGap = useResponsiveSpacing(columnGapConfig ?? gapConfig);
|
||
|
||
// 计算列宽
|
||
const columnWidth = useMemo(() => {
|
||
if (getColumnWidth) {
|
||
return getColumnWidth(width, columns, columnGap);
|
||
}
|
||
|
||
if (equalColumns) {
|
||
// 等宽列计算:(总宽度 - (列数 - 1) * 列间距) / 列数
|
||
return (width - (columns - 1) * columnGap) / columns;
|
||
}
|
||
|
||
return undefined;
|
||
}, [width, columns, columnGap, equalColumns, getColumnWidth]);
|
||
|
||
// 将子元素分组为行
|
||
const rows = useMemo(() => {
|
||
const items = React.Children.toArray(children);
|
||
if (items.length === 0) return [];
|
||
|
||
const result: React.ReactNode[][] = [];
|
||
for (let i = 0; i < items.length; i += columns) {
|
||
result.push(items.slice(i, i + columns));
|
||
}
|
||
return result;
|
||
}, [children, columns]);
|
||
|
||
// 空状态处理
|
||
if (rows.length === 0 && renderEmpty) {
|
||
return (
|
||
<View style={[styles.container, containerStyle]}>
|
||
{renderEmpty()}
|
||
</View>
|
||
);
|
||
}
|
||
|
||
return (
|
||
<View style={[styles.container, containerStyle]}>
|
||
{rows.map((row, rowIndex) => (
|
||
<View
|
||
key={`row-${rowIndex}`}
|
||
style={[
|
||
styles.row,
|
||
{
|
||
marginBottom: rowIndex < rows.length - 1 ? rowGap : 0,
|
||
},
|
||
style,
|
||
]}
|
||
>
|
||
{row.map((child, colIndex) => {
|
||
const index = rowIndex * columns + colIndex;
|
||
const key = keyExtractor?.(child, index) ?? `grid-item-${index}`;
|
||
|
||
return (
|
||
<View
|
||
key={key}
|
||
style={[
|
||
styles.item,
|
||
{
|
||
width: columnWidth,
|
||
marginRight: colIndex < row.length - 1 ? columnGap : 0,
|
||
},
|
||
itemStyle,
|
||
]}
|
||
>
|
||
{child}
|
||
</View>
|
||
);
|
||
})}
|
||
</View>
|
||
))}
|
||
</View>
|
||
);
|
||
}
|
||
|
||
const styles = StyleSheet.create({
|
||
container: {
|
||
width: '100%',
|
||
},
|
||
row: {
|
||
flexDirection: 'row',
|
||
alignItems: 'flex-start',
|
||
},
|
||
item: {
|
||
flexShrink: 0,
|
||
},
|
||
});
|
||
|
||
export default ResponsiveGrid;
|