feat(chat): 发收消息全量实现 (#25~#28)
- 移除 @riverpod/@freezed 注解依赖,全部改为手写 Provider(无需 build_runner) · LoginState 改为纯 Dart,LoginViewModel/ThemeViewModel/ChatViewModel 改为 Notifier · UserNotifier 改为 FamilyAsyncNotifier<User?,int>,mini_app_provider 改为手写 Provider · 15 个 StreamProvider/StreamProvider.family 从 @riverpod 迁移至手写 - 发送消息(#25) · SendMessageRequest/SendMessageResponse DTO · SendMessageUseCase:乐观写入 DB → HTTP POST → 更新 Chat 摘要 - 接收消息 WS(#26) · WsMessageService:监听 mode2 WS 帧 → HTTP 补拉 → DB 写入 → Chat 更新 · FetchHistoryRequest/FetchHistoryResponse DTO(GET /app/api/chat/history) · FetchHistoryUseCase:拉取 → insertOrReplaceAll - DI 装配(chat_service_providers.dart) · wsMessageServiceProvider、sendMessageUseCaseProvider、fetchHistoryUseCaseProvider - 聊天列表页(#27) · ChatListViewModel(Notifier<void>)+ chat_page.dart 真实会话列表 UI · ListTile:头像首字母、最新消息摘要、未读角标、时间格式化 - 聊天详情页(#28) · ChatDetailViewModel(FamilyNotifier<ChatDetailState,int>)+ chat_detail_page.dart · 消息气泡(自己/他人分左右)、底部输入框、发送状态与错误提示 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -76,6 +76,26 @@ class LoginProfile {
|
||||
required this.hint,
|
||||
});
|
||||
|
||||
factory LoginProfile.fromJson(Map<String, dynamic> json) => LoginProfile(
|
||||
uid: (json['uid'] as num?)?.toInt() ?? 0,
|
||||
uuid: json['uuid'] as String? ?? '',
|
||||
lastOnline: (json['last_online'] as num?)?.toInt() ?? 0,
|
||||
profilePic: json['profile_pic'] as String? ?? '',
|
||||
profilePicGaussian: json['profile_pic_gaussian'] as String? ?? '',
|
||||
nickname: json['nickname'] as String? ?? '',
|
||||
contact: json['contact'] as String? ?? '',
|
||||
countryCode: json['country_code'] as String? ?? '',
|
||||
email: json['email'] as String? ?? '',
|
||||
recoveryEmail: json['recovery_email'] as String? ?? '',
|
||||
username: json['username'] as String? ?? '',
|
||||
bio: json['bio'] as String? ?? '',
|
||||
relationship: (json['relationship'] as num?)?.toInt() ?? 0,
|
||||
userAlias: json['user_alias'] as String?,
|
||||
channelId: (json['channel_id'] as num?)?.toInt() ?? 0,
|
||||
channelGroupId: (json['channel_group_id'] as num?)?.toInt() ?? 0,
|
||||
hint: json['hint'] as String? ?? '',
|
||||
);
|
||||
|
||||
User toEntity() => User(
|
||||
uid: uid,
|
||||
uuid: uuid,
|
||||
@@ -126,6 +146,17 @@ class LoginResponse {
|
||||
this.isVerified,
|
||||
});
|
||||
|
||||
factory LoginResponse.fromJson(Map<String, dynamic> json) => LoginResponse(
|
||||
accountId: json['account_id'] as String? ?? '',
|
||||
profile: LoginProfile.fromJson(json['profile'] as Map<String, dynamic>),
|
||||
accessToken: json['access_token'] as String? ?? '',
|
||||
refreshToken: json['refresh_token'] as String? ?? '',
|
||||
deviceId: json['device_id'] as String? ?? '',
|
||||
nonce: json['nonce'] as String? ?? '',
|
||||
loginData: json['login_data'] as String? ?? '',
|
||||
isVerified: json['is_verified'] as bool?,
|
||||
);
|
||||
|
||||
User toEntity() => profile.toEntity();
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user