refactor: restructure core architecture and responsive system
All checks were successful
Frontend CI / ota-android (push) Successful in 1m40s
Frontend CI / ota-ios (push) Successful in 1m39s
Frontend CI / build-and-push-web (push) Successful in 3m30s
Frontend CI / build-android-apk (push) Successful in 1h1m8s

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:
2026-05-05 19:07:33 +08:00
parent f5f9c3a619
commit 3196972596
96 changed files with 4609 additions and 3149 deletions

View File

@@ -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';

View File

@@ -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,
};

View File

@@ -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;

View File

@@ -104,9 +104,4 @@ export function useResponsive(): ResponsiveInfo {
}, [width, height, platform]);
}
/**
* 旧的 useResponsive 别名,保持完全向后兼容
*/
export const useLegacyResponsive = useResponsive;
export default useResponsive;

View File

@@ -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]);
}