feat: 实现 Auth 全局状态机与 401 统一处理机制

- 新增 AuthSessionInvalidated 事件处理 token 失效场景
- ApiInterceptor 新增 authFailureCallback 单飞机制
- AuthBloc 区分 manual logout 与 auto expiry 语义
- 新增 startup recovery fallback 防止启动卡死

feat: 重构 Calendar DayWeek 视图事件布局引擎

- 新增 DayEventLayoutEngine 解耦事件计算与渲染
- 新增 DayTimelineMetrics 统一时间轴常量
- 新增 DayViewScale 支持捏合缩放

feat: 新增 Settings 页面共享 UI 组件

- 新增 BackTitlePageHeader 统一页面 header
- 新增 DetailHeaderActionMenu 统一操作菜单
- 新增 DestructiveActionSheet 统一删除确认
- 新增 AppToggleSwitch 统一开关组件

feat: Chat UI Schema 支持导航操作

- 支持 navigation 类型 action 触发内部路由跳转
- 新增路径验证与参数处理

chore: 更新相关测试覆盖 auth 失效路径
This commit is contained in:
qzl
2026-03-18 13:35:25 +08:00
parent 19981964fb
commit b34697660d
56 changed files with 2602 additions and 784 deletions
@@ -1,5 +1,6 @@
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:go_router/go_router.dart';
import 'package:social_app/features/chat/ui/widgets/ui_schema_renderer.dart';
void main() {
@@ -128,5 +129,93 @@ void main() {
expect(find.textContaining('无效 UI Schema'), findsOneWidget);
});
testWidgets('handles navigation action and jumps by path', (tester) async {
final schema = {
'version': '2.0',
'root': {
'type': 'stack',
'direction': 'vertical',
'appearance': 'plain',
'children': [
{
'type': 'button',
'label': '查看待办',
'style': 'primary',
'action': {
'type': 'navigation',
'path': '/todo/123',
'params': {'from': 'assistant'},
},
},
],
},
};
final router = GoRouter(
initialLocation: '/',
routes: [
GoRoute(
path: '/',
builder: (context, state) =>
Scaffold(body: UiSchemaRenderer.renderSchema(schema)),
),
GoRoute(
path: '/todo/:id',
builder: (context, state) => Text(
'todo detail ${state.pathParameters['id']} from ${state.uri.queryParameters['from']}',
),
),
],
);
await tester.pumpWidget(MaterialApp.router(routerConfig: router));
await tester.tap(find.text('查看待办'));
await tester.pumpAndSettle();
expect(find.text('todo detail 123 from assistant'), findsOneWidget);
});
testWidgets('does not navigate for placeholder path', (tester) async {
final schema = {
'version': '2.0',
'root': {
'type': 'stack',
'direction': 'vertical',
'appearance': 'plain',
'children': [
{
'type': 'button',
'label': '坏路径',
'style': 'primary',
'action': {'type': 'navigation', 'path': '/todo/:id'},
},
],
},
};
final router = GoRouter(
initialLocation: '/',
routes: [
GoRoute(
path: '/',
builder: (context, state) =>
Scaffold(body: UiSchemaRenderer.renderSchema(schema)),
),
GoRoute(
path: '/todo/:id',
builder: (context, state) => const Text('detail'),
),
],
);
await tester.pumpWidget(MaterialApp.router(routerConfig: router));
await tester.tap(find.text('坏路径'));
await tester.pumpAndSettle();
await tester.pump(const Duration(seconds: 3));
expect(find.text('坏路径'), findsOneWidget);
expect(find.text('detail'), findsNothing);
});
});
}