fix(mobile-app): improve app restart flow for incomplete wallet creation

When user closes app during wallet generation (before backup completion),
app now correctly navigates directly to backup mnemonic page on restart
instead of requiring extra button click on onboarding page.

Changes:
- AuthProvider: Add isAccountCreated, isWalletReady, userSerialNum, referralCode states
- AuthProvider: Enhanced checkAuthStatus() to detect partial account creation
- SplashPage: Add navigation priority for account-created-but-wallet-incomplete state
- SplashPage: Navigate directly to BackupMnemonicPage when account exists but wallet not ready

Navigation priority:
1. Wallet created + backed up → Main page (ranking)
2. Account created but wallet incomplete → Backup mnemonic page
3. First launch or unseen guide → Guide page
4. Otherwise → Onboarding page

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
hailin 2025-12-06 20:41:36 -08:00
parent 823fc5056e
commit 6ff1868944
2 changed files with 91 additions and 10 deletions

View File

@ -3,6 +3,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:go_router/go_router.dart';
import '../../../../core/constants/app_constants.dart';
import '../../../../routes/route_paths.dart';
import '../../../../routes/app_router.dart';
import '../../../../bootstrap.dart';
import '../providers/auth_provider.dart';
@ -38,20 +39,38 @@ class _SplashPageState extends ConsumerState<SplashPage> {
final authState = ref.read(authProvider);
//
String targetRoute;
//
// 1.
// 2.
// 3.
// 4.
if (authState.isWalletCreated) {
//
targetRoute = RoutePaths.ranking;
//
debugPrint('[SplashPage] 钱包已创建且已备份 → 跳转到龙虎榜');
context.go(RoutePaths.ranking);
} else if (authState.isAccountCreated && authState.userSerialNum != null) {
//
debugPrint('[SplashPage] 账号已创建但钱包未完成 → 跳转到备份助记词页面');
debugPrint('[SplashPage] userSerialNum: ${authState.userSerialNum}, isWalletReady: ${authState.isWalletReady}');
context.go(
RoutePaths.backupMnemonic,
extra: BackupMnemonicParams(
userSerialNum: authState.userSerialNum!,
referralCode: authState.referralCode,
),
);
} else if (authState.isFirstLaunch || !authState.hasSeenGuide) {
//
targetRoute = RoutePaths.guide;
debugPrint('[SplashPage] 首次打开或未看过向导 → 跳转到向导页');
context.go(RoutePaths.guide);
} else {
//
targetRoute = RoutePaths.onboarding;
debugPrint('[SplashPage] 已看过向导但未创建钱包 → 跳转到创建账户页面');
context.go(RoutePaths.onboarding);
}
//
context.go(targetRoute);
//
final targetRoute = authState.isWalletCreated ? RoutePaths.ranking : null;
//
if (targetRoute == RoutePaths.ranking) {

View File

@ -17,6 +17,14 @@ class AuthState {
final bool isFirstLaunch;
final bool hasSeenGuide;
final String? errorMessage;
//
final bool isAccountCreated;
//
final bool isWalletReady;
//
final int? userSerialNum;
//
final String? referralCode;
const AuthState({
this.status = AuthStatus.initial,
@ -25,6 +33,10 @@ class AuthState {
this.isFirstLaunch = true,
this.hasSeenGuide = false,
this.errorMessage,
this.isAccountCreated = false,
this.isWalletReady = false,
this.userSerialNum,
this.referralCode,
});
AuthState copyWith({
@ -34,6 +46,10 @@ class AuthState {
bool? isFirstLaunch,
bool? hasSeenGuide,
String? errorMessage,
bool? isAccountCreated,
bool? isWalletReady,
int? userSerialNum,
String? referralCode,
}) {
return AuthState(
status: status ?? this.status,
@ -42,6 +58,10 @@ class AuthState {
isFirstLaunch: isFirstLaunch ?? this.isFirstLaunch,
hasSeenGuide: hasSeenGuide ?? this.hasSeenGuide,
errorMessage: errorMessage,
isAccountCreated: isAccountCreated ?? this.isAccountCreated,
isWalletReady: isWalletReady ?? this.isWalletReady,
userSerialNum: userSerialNum ?? this.userSerialNum,
referralCode: referralCode ?? this.referralCode,
);
}
}
@ -59,22 +79,64 @@ class AuthNotifier extends StateNotifier<AuthState> {
final isFirstLaunchStr = await _secureStorage.read(key: StorageKeys.isFirstLaunch);
final isFirstLaunch = isFirstLaunchStr == null || isFirstLaunchStr != 'false';
//
//
final isAccountCreatedStr = await _secureStorage.read(key: StorageKeys.isAccountCreated);
final isAccountCreated = isAccountCreatedStr == 'true';
//
final isWalletReadyStr = await _secureStorage.read(key: StorageKeys.isWalletReady);
final isWalletReady = isWalletReadyStr == 'true';
//
final isMnemonicBackedUpStr = await _secureStorage.read(key: StorageKeys.isMnemonicBackedUp);
final isMnemonicBackedUp = isMnemonicBackedUpStr == 'true';
//
final userSerialNumStr = await _secureStorage.read(key: StorageKeys.userSerialNum);
final userSerialNum = userSerialNumStr != null ? int.tryParse(userSerialNumStr) : null;
final referralCode = await _secureStorage.read(key: StorageKeys.referralCode);
// walletAddress
final walletAddress = await _secureStorage.read(key: StorageKeys.walletAddress);
final isWalletCreated = walletAddress != null && walletAddress.isNotEmpty;
final hasLegacyWallet = walletAddress != null && walletAddress.isNotEmpty;
//
// 1. isWalletReady == true isMnemonicBackedUp == true
// 2. walletAddress
final isWalletCreated = (isWalletReady && isMnemonicBackedUp) || hasLegacyWallet;
if (isWalletCreated) {
//
state = state.copyWith(
status: AuthStatus.authenticated,
walletAddress: walletAddress,
isWalletCreated: true,
isAccountCreated: true,
isWalletReady: true,
isFirstLaunch: false,
hasSeenGuide: true,
userSerialNum: userSerialNum,
referralCode: referralCode,
);
} else {
} else if (isAccountCreated) {
//
state = state.copyWith(
status: AuthStatus.unauthenticated,
isWalletCreated: false,
isAccountCreated: true,
isWalletReady: isWalletReady,
isFirstLaunch: false,
hasSeenGuide: true,
userSerialNum: userSerialNum,
referralCode: referralCode,
);
} else {
//
state = state.copyWith(
status: AuthStatus.unauthenticated,
isWalletCreated: false,
isAccountCreated: false,
isWalletReady: false,
isFirstLaunch: isFirstLaunch,
hasSeenGuide: !isFirstLaunch,
);