refactor(core): distinguish network errors from auth failures and refactor real-time messaging pipeline
Some checks failed
Frontend CI / ota (android) (push) Successful in 3m11s
Frontend CI / ota (ios) (push) Successful in 3m31s
Frontend CI / build-and-push-web (push) Successful in 4m38s
Frontend CI / build-android-apk (push) Has been cancelled

- Add isNetworkError() helper and FetchCurrentUserResult discriminated union to prevent logout on network failures
- Refactor authService to return { kind: 'user' } | { kind: 'auth_failed' } | { kind: 'network_error' }
- Update authStore to preserve login state on network errors, only logout on auth failures
- Replace WSMessageHandler with RealtimeIngestionPipeline for improved real-time message handling
- Remove MessageDeduplication service; add store-level idempotent addOrReplaceMessage for message dedup
- Add atomic systemUnreadCount operations to prevent race conditions
- Add SearchHeader component for consistent search UI across screens
- Add 'home' TabBar variant with underline style matching HomeScreen
- Add Jest test infrastructure and exclude tests from tsconfig
- Fix ChatScreen history lock being stuck when scrolling to latest messages
- Remove unused call_participant_joined/left WS event types
This commit is contained in:
lafay
2026-06-21 17:17:15 +08:00
parent 94a202506b
commit 705b365536
47 changed files with 4357 additions and 1574 deletions

46
jest.config.js Normal file
View File

@@ -0,0 +1,46 @@
/**
* Jest 配置
*
* 仅对消息状态核心逻辑store 幂等性 / 实时事件管道 / 未读计数原子性)做单测,
* 不覆盖 RN / Expo UI。所有平台依赖@/services/core、@/database、@/core/events、
* @/stores/auth、@/services/message在测试中通过 moduleNameMapper 映射到 mock。
*/
const { pathsToModuleNameMapper } = require('ts-jest');
const tsconfig = require('./tsconfig.json');
const tsconfigBase = require('expo/tsconfig.base.json');
// 合并 base + 项目 paths
const paths = {
...(tsconfigBase.compilerOptions.paths || {}),
...(tsconfig.compilerOptions.paths || {}),
};
module.exports = {
preset: 'ts-jest',
testEnvironment: 'node',
roots: ['<rootDir>/src'],
testMatch: ['**/__tests__/**/*.test.ts'],
moduleNameMapper: {
// 平台依赖统一映射到 mock必须放在 @/* 通配之前,否则会被通配吞掉)
'^@/services/core$': '<rootDir>/src/stores/message/__tests__/mocks/coreMock.ts',
'^@/services/message$': '<rootDir>/src/stores/message/__tests__/mocks/messageServiceMock.ts',
'^@/database$': '<rootDir>/src/stores/message/__tests__/mocks/databaseMock.ts',
'^@/core/events/EventBus$': '<rootDir>/src/stores/message/__tests__/mocks/eventBusMock.ts',
'^@/stores/auth/authStore$': '<rootDir>/src/stores/message/__tests__/mocks/authStoreMock.ts',
'^@react-native-async-storage/async-storage$':
'<rootDir>/src/stores/message/__tests__/mocks/asyncStorageMock.ts',
// @/* 路径别名 → 真实 src通配兜底放最后
...pathsToModuleNameMapper(paths, { prefix: '<rootDir>/' }),
},
transform: {
'^.+\\.tsx?$': [
'ts-jest',
{
tsconfig: '<rootDir>/src/stores/message/__tests__/tsconfig.json',
isolatedModules: true,
},
],
},
clearMocks: true,
};