feat(mine): 我的 Tab 全量实现 (#5~#13)
从 im-client-ios-swift-demo 搬运 Settings 逻辑,对齐 Gitea issue #5–#13 ## 基础设施 - AuthNotifier 新增 currentUid 字段,login() 接受 uid 参数 (#5) - LoginViewModel 登录成功后传入 user.uid - ApiPaths 补充 account/block/store 系列路径 - Tab 重命名"设置"→"我的",icon 改为 person_outline (#5) - AppRouteName 新增5条子路由 (edit-profile/blocklist/language/network-diagnostics/about) - app_router + auth_guard 同步注册新路由 ## Settings Feature - SettingsViewModel 重写为 NotifierProvider(去除 @riverpod 依赖) - build() 自动触发 loadProfile() - logout() 完整流程:API → WS 断开 → DB 关闭 → AuthNotifier - 6 个 navigateTo* 方法 - SettingsPage 完整 UI:资料卡 / 偏好设置 / 工具 / 关于 / 退出登录按钮 (#5 #7) - FetchProfileUseCase: GET /app/api/user/profile (#5) - LogoutUseCase: logout + disconnect + closeDatabase (#7) - UpdateProfileUseCase + UpdateProfileRequest: POST /app/api/user/update-profile (#6) - EditProfilePage + EditProfileViewModel: 昵称/bio 编辑 (#6) - LanguagePage: 语言选择 UI 框架,l10n_sdk 待接入 (#9) - BlocklistPage: 黑名单框架,API 待实现 (#10) - NetworkDiagnosticsPage + ViewModel: 四步诊断(连通/TCP/DNS/HTTPS)(#12) - AboutPage: 版本号 + 服务条款/隐私政策入口 (#13) - settings_providers.dart: 扩展 DI 装配 ## 文档 - Doc/mine_tab_architecture.md: 架构说明、数据流、路由、待完成事项 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -19,11 +19,16 @@ import 'package:im_app/app/di/network_provider.dart';
|
||||
/// - `login` / `logout`:同步更新安全存储
|
||||
class AuthNotifier extends ChangeNotifier {
|
||||
bool _isLoggedIn = false;
|
||||
int? _currentUid;
|
||||
|
||||
bool get isLoggedIn => _isLoggedIn;
|
||||
|
||||
void login() {
|
||||
/// 登录用户的 UID,登录成功后由 LoginViewModel 写入
|
||||
int? get currentUid => _currentUid;
|
||||
|
||||
void login({required int uid}) {
|
||||
_isLoggedIn = true;
|
||||
_currentUid = uid;
|
||||
// TODO: 接入 cipher_guard_sdk 后,在此处完成 RSA 密钥注入:
|
||||
// 1. 从安全存储(keychain / secure storage)读取公私钥对(只读一次)
|
||||
// 2. cipherSdk.setActiveKeyPair(publicKey: pubPem, privateKey: privPem)
|
||||
@@ -33,6 +38,7 @@ class AuthNotifier extends ChangeNotifier {
|
||||
|
||||
void logout() {
|
||||
_isLoggedIn = false;
|
||||
_currentUid = null;
|
||||
// TODO: 接入 cipher_guard_sdk 后,退出登录时清除内存密钥:
|
||||
// cipherSdk.clearActiveKeyPair()
|
||||
// cipherSdk.clearDerivedKeyCache()
|
||||
|
||||
@@ -68,6 +68,11 @@ enum AppRouteName {
|
||||
|
||||
// ── Settings 子路由 ───────────────────────────────────────────────────────
|
||||
settingsTheme('/settings/theme'),
|
||||
settingsEditProfile('/settings/edit-profile'),
|
||||
settingsBlocklist('/settings/blocklist'),
|
||||
settingsLanguage('/settings/language'),
|
||||
settingsNetworkDiagnostics('/settings/network-diagnostics'),
|
||||
settingsAbout('/settings/about'),
|
||||
|
||||
// ── 全屏页面(无底部导航栏)──────────────────────────────────────────────
|
||||
login('/login');
|
||||
|
||||
@@ -10,6 +10,11 @@ import 'package:im_app/features/contact/view/contact_page.dart';
|
||||
import 'package:im_app/features/login/view/login_page.dart';
|
||||
import 'package:im_app/features/settings/view/settings_page.dart';
|
||||
import 'package:im_app/features/settings/view/theme_view.dart';
|
||||
import 'package:im_app/features/settings/view/edit_profile_page.dart';
|
||||
import 'package:im_app/features/settings/view/blocklist_page.dart';
|
||||
import 'package:im_app/features/settings/view/language_page.dart';
|
||||
import 'package:im_app/features/settings/view/network_diagnostics_page.dart';
|
||||
import 'package:im_app/features/settings/view/about_page.dart';
|
||||
import 'package:im_app/app/di/app_providers.dart';
|
||||
import 'package:im_app/app/router/app_route_name.dart';
|
||||
import 'package:im_app/app/router/guards/auth_guard.dart';
|
||||
@@ -152,6 +157,31 @@ final routerProvider = Provider<GoRouter>((ref) {
|
||||
path: AppRouteName.settingsTheme.path,
|
||||
builder: (context, state) => const ThemeView(),
|
||||
),
|
||||
GoRoute(
|
||||
parentNavigatorKey: _rootKey,
|
||||
path: AppRouteName.settingsEditProfile.path,
|
||||
builder: (context, state) => const EditProfilePage(),
|
||||
),
|
||||
GoRoute(
|
||||
parentNavigatorKey: _rootKey,
|
||||
path: AppRouteName.settingsBlocklist.path,
|
||||
builder: (context, state) => const BlocklistPage(),
|
||||
),
|
||||
GoRoute(
|
||||
parentNavigatorKey: _rootKey,
|
||||
path: AppRouteName.settingsLanguage.path,
|
||||
builder: (context, state) => const LanguagePage(),
|
||||
),
|
||||
GoRoute(
|
||||
parentNavigatorKey: _rootKey,
|
||||
path: AppRouteName.settingsNetworkDiagnostics.path,
|
||||
builder: (context, state) => const NetworkDiagnosticsPage(),
|
||||
),
|
||||
GoRoute(
|
||||
parentNavigatorKey: _rootKey,
|
||||
path: AppRouteName.settingsAbout.path,
|
||||
builder: (context, state) => const AboutPage(),
|
||||
),
|
||||
GoRoute(
|
||||
parentNavigatorKey: _rootKey,
|
||||
path: AppRouteName.login.path,
|
||||
|
||||
@@ -40,6 +40,11 @@ String? authGuard(AuthNotifier authNotifier, GoRouterState state) {
|
||||
case AppRouteName.contact:
|
||||
case AppRouteName.settings:
|
||||
case AppRouteName.settingsTheme:
|
||||
case AppRouteName.settingsEditProfile:
|
||||
case AppRouteName.settingsBlocklist:
|
||||
case AppRouteName.settingsLanguage:
|
||||
case AppRouteName.settingsNetworkDiagnostics:
|
||||
case AppRouteName.settingsAbout:
|
||||
case AppRouteName.chatDBTest:
|
||||
// 受保护路由 → 未登录跳登录页
|
||||
return isLoggedIn ? null : AppRouteName.login.path;
|
||||
|
||||
Reference in New Issue
Block a user