refactor: restructure core architecture and responsive system
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.
This commit is contained in:
@@ -1,32 +1,27 @@
|
||||
/**
|
||||
* 响应式 Hooks 统一导出
|
||||
* Responsive Hooks Index
|
||||
*
|
||||
*
|
||||
* 提供完整的响应式设计解决方案,包括:
|
||||
* - 断点检测 (useBreakpoint)
|
||||
* - 屏幕尺寸 (useScreenSize, useWindowDimensions)
|
||||
* - 响应式值 (useResponsiveValue, useResponsiveStyle)
|
||||
* - 响应式值 (useResponsiveValue)
|
||||
* - 方向检测 (useOrientation)
|
||||
* - 平台检测 (usePlatform)
|
||||
* - 媒体查询 (useMediaQuery)
|
||||
* - 列数计算 (useColumnCount)
|
||||
* - 间距计算 (useResponsiveSpacing)
|
||||
* - 断点检查 (useBreakpointGTE, useBreakpointLT, useBreakpointBetween)
|
||||
* - 断点检查 (useBreakpointGTE)
|
||||
*/
|
||||
|
||||
// ==================== 核心 Hooks ====================
|
||||
export { useWindowDimensions, useScreenSize } from './useScreenSize';
|
||||
export {
|
||||
useBreakpoint,
|
||||
useFineBreakpoint,
|
||||
useBreakpointGTE,
|
||||
useBreakpointLT,
|
||||
useBreakpointBetween,
|
||||
export {
|
||||
useBreakpoint,
|
||||
useBreakpointGTE,
|
||||
} from './useBreakpoint';
|
||||
export { useResponsiveValue, useResponsiveStyle } from './useResponsiveValue';
|
||||
export { useResponsiveValue } from './useResponsiveValue';
|
||||
export { useOrientation } from './useOrientation';
|
||||
export { usePlatform } from './usePlatform';
|
||||
export { useMediaQuery } from './useMediaQuery';
|
||||
export { useColumnCount } from './useColumnCount';
|
||||
export { useResponsiveSpacing } from './useResponsiveSpacing';
|
||||
|
||||
@@ -59,8 +54,7 @@ export {
|
||||
// 兼容层类型
|
||||
export type { ResponsiveInfo } from './useResponsive';
|
||||
|
||||
// ==================== 向后兼容 ====================
|
||||
export { useResponsive, useLegacyResponsive } from './useResponsive';
|
||||
export { useResponsive } from './useResponsive';
|
||||
|
||||
// 默认导出
|
||||
export { useScreenSize as default } from './useScreenSize';
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
* 断点检测 - 检测当前断点
|
||||
*/
|
||||
|
||||
import { useMemo, useState, useEffect } from 'react';
|
||||
import { Dimensions, ScaledSize } from 'react-native';
|
||||
import { useMemo } from 'react';
|
||||
import { useWindowDimensions } from './useScreenSize';
|
||||
import { BREAKPOINTS, FINE_BREAKPOINTS } from './types';
|
||||
import type { BreakpointKey, FineBreakpointKey } from './types';
|
||||
|
||||
@@ -74,120 +74,33 @@ export function isBreakpointBetween(
|
||||
|
||||
// ==================== Hooks ====================
|
||||
|
||||
function useWindowDimensionsLocal(): ScaledSize {
|
||||
const [dimensions, setDimensions] = useState(() => Dimensions.get('window'));
|
||||
|
||||
useEffect(() => {
|
||||
const subscription = Dimensions.addEventListener('change', ({ window }) => {
|
||||
setDimensions(window);
|
||||
});
|
||||
|
||||
return () => {
|
||||
subscription.remove();
|
||||
};
|
||||
}, []);
|
||||
|
||||
return dimensions;
|
||||
}
|
||||
|
||||
/**
|
||||
* 断点检测 Hook
|
||||
* 返回当前的基础断点
|
||||
*
|
||||
* @returns 当前断点
|
||||
*
|
||||
* @example
|
||||
* const breakpoint = useBreakpoint();
|
||||
* // 'mobile' | 'tablet' | 'desktop' | 'wide'
|
||||
*/
|
||||
export function useBreakpoint(): BreakpointKey {
|
||||
const { width } = useWindowDimensionsLocal();
|
||||
const { width } = useWindowDimensions();
|
||||
|
||||
return useMemo(() => {
|
||||
return getBreakpoint(width);
|
||||
}, [width]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 细粒度断点检测 Hook
|
||||
* 返回当前的细粒度断点
|
||||
*
|
||||
* @returns 当前细粒度断点
|
||||
*
|
||||
* @example
|
||||
* const fineBreakpoint = useFineBreakpoint();
|
||||
* // 'xs' | 'sm' | 'md' | 'lg' | 'xl' | '2xl' | '3xl' | '4xl'
|
||||
*/
|
||||
export function useFineBreakpoint(): FineBreakpointKey {
|
||||
const { width } = useWindowDimensionsLocal();
|
||||
|
||||
return useMemo(() => {
|
||||
return getFineBreakpoint(width);
|
||||
}, [width]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查当前断点是否大于等于目标断点
|
||||
*
|
||||
*
|
||||
* @param target - 目标断点
|
||||
* @returns 是否满足条件
|
||||
*
|
||||
*
|
||||
* @example
|
||||
* const isMediumUp = useBreakpointGTE('md');
|
||||
*/
|
||||
export function useBreakpointGTE(target: FineBreakpointKey): boolean {
|
||||
const { width } = useWindowDimensionsLocal();
|
||||
|
||||
const { width } = useWindowDimensions();
|
||||
|
||||
return useMemo(() => {
|
||||
const current = getFineBreakpoint(width);
|
||||
return isBreakpointGTE(current, target);
|
||||
}, [width, target]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查当前断点是否小于目标断点
|
||||
*
|
||||
* @param target - 目标断点
|
||||
* @returns 是否满足条件
|
||||
*
|
||||
* @example
|
||||
* const isMobileOnly = useBreakpointLT('lg');
|
||||
*/
|
||||
export function useBreakpointLT(target: FineBreakpointKey): boolean {
|
||||
const { width } = useWindowDimensionsLocal();
|
||||
|
||||
return useMemo(() => {
|
||||
const current = getFineBreakpoint(width);
|
||||
return isBreakpointLT(current, target);
|
||||
}, [width, target]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查当前是否在指定断点范围内
|
||||
*
|
||||
* @param min - 最小断点(包含)
|
||||
* @param max - 最大断点(不包含)
|
||||
* @returns 是否在范围内
|
||||
*
|
||||
* @example
|
||||
* const isTabletRange = useBreakpointBetween('md', 'lg');
|
||||
*/
|
||||
export function useBreakpointBetween(
|
||||
min: FineBreakpointKey,
|
||||
max: FineBreakpointKey
|
||||
): boolean {
|
||||
const { width } = useWindowDimensionsLocal();
|
||||
|
||||
return useMemo(() => {
|
||||
const current = getFineBreakpoint(width);
|
||||
return isBreakpointBetween(current, min, max);
|
||||
}, [width, min, max]);
|
||||
}
|
||||
|
||||
export default {
|
||||
useBreakpoint,
|
||||
useFineBreakpoint,
|
||||
useBreakpointGTE,
|
||||
useBreakpointLT,
|
||||
useBreakpointBetween,
|
||||
};
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
/**
|
||||
* useMediaQuery Hook
|
||||
* 媒体查询模拟 - 模拟 CSS 媒体查询
|
||||
*/
|
||||
|
||||
import { useMemo } from 'react';
|
||||
import { useWindowDimensions } from './useScreenSize';
|
||||
import { getOrientation } from './useOrientation';
|
||||
import type { MediaQueryOptions } from './types';
|
||||
|
||||
/**
|
||||
* 模拟 CSS 媒体查询
|
||||
*
|
||||
* @param query - 查询条件
|
||||
* @returns 是否匹配
|
||||
*
|
||||
* @example
|
||||
* const isMinWidth768 = useMediaQuery({ minWidth: 768 });
|
||||
* const isMaxWidth1024 = useMediaQuery({ maxWidth: 1024 });
|
||||
* const isPortrait = useMediaQuery({ orientation: 'portrait' });
|
||||
*/
|
||||
export function useMediaQuery(query: MediaQueryOptions): boolean {
|
||||
const { width, height } = useWindowDimensions();
|
||||
const currentOrientation = getOrientation(width, height);
|
||||
|
||||
return useMemo(() => {
|
||||
if (query.minWidth !== undefined && width < query.minWidth) return false;
|
||||
if (query.maxWidth !== undefined && width > query.maxWidth) return false;
|
||||
if (query.minHeight !== undefined && height < query.minHeight) return false;
|
||||
if (query.maxHeight !== undefined && height > query.maxHeight) return false;
|
||||
if (query.orientation !== undefined && currentOrientation !== query.orientation) return false;
|
||||
return true;
|
||||
}, [width, height, currentOrientation, query]);
|
||||
}
|
||||
|
||||
export default useMediaQuery;
|
||||
@@ -104,9 +104,4 @@ export function useResponsive(): ResponsiveInfo {
|
||||
}, [width, height, platform]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 旧的 useResponsive 别名,保持完全向后兼容
|
||||
*/
|
||||
export const useLegacyResponsive = useResponsive;
|
||||
|
||||
export default useResponsive;
|
||||
|
||||
@@ -45,51 +45,3 @@ export function useResponsiveValue<T>(value: ResponsiveValue<T>): T {
|
||||
return (valueMap.xs ?? Object.values(valueMap)[0]) as T;
|
||||
}, [value, fineBreakpoint]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 响应式样式生成器
|
||||
* 根据断点生成响应式样式
|
||||
*
|
||||
* @param styles - 响应式样式对象
|
||||
* @returns 当前断点对应的样式
|
||||
*
|
||||
* @example
|
||||
* const containerStyle = useResponsiveStyle({
|
||||
* padding: { xs: 8, md: 16, lg: 24 },
|
||||
* fontSize: { xs: 14, lg: 16 }
|
||||
* });
|
||||
*/
|
||||
export function useResponsiveStyle<T extends Record<string, ResponsiveValue<unknown>>>(
|
||||
styles: T
|
||||
): { [K in keyof T]: T[K] extends ResponsiveValue<infer V> ? V : never } {
|
||||
const { width } = useWindowDimensions();
|
||||
const fineBreakpoint = getFineBreakpoint(width);
|
||||
|
||||
return useMemo(() => {
|
||||
const result = {} as { [K in keyof T]: T[K] extends ResponsiveValue<infer V> ? V : never };
|
||||
|
||||
for (const key in styles) {
|
||||
const value = styles[key];
|
||||
|
||||
if (typeof value !== 'object' || value === null || Array.isArray(value)) {
|
||||
(result as Record<string, unknown>)[key] = value;
|
||||
} else {
|
||||
const valueMap = value as Partial<Record<FineBreakpointKey, unknown>>;
|
||||
const currentIndex = breakpointOrder.indexOf(fineBreakpoint);
|
||||
|
||||
let selectedValue: unknown = undefined;
|
||||
for (let i = currentIndex; i < breakpointOrder.length; i++) {
|
||||
const bp = breakpointOrder[i];
|
||||
if (bp in valueMap) {
|
||||
selectedValue = valueMap[bp];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
(result as Record<string, unknown>)[key] = selectedValue ?? valueMap.xs ?? Object.values(valueMap)[0];
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}, [styles, fineBreakpoint]);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user