f0832ccfcb
ci-pangolin / Lint — shellcheck (push) Successful in 6s
ci-pangolin / OpenAPI Sync Check (push) Successful in 21s
ci-pangolin / Redline Scan — 脱敏 (UI 文案) (push) Successful in 7s
ci-pangolin / Flutter — analyze + test (push) Successful in 23s
ci-pangolin / Portable SQL — 可移植性 (mysql/sqlite) (push) Successful in 5s
ci-pangolin / Codegen Drift — token 生成物未漂移 (push) Successful in 5s
ci-pangolin / Go — build + test (push) Successful in 9s
ci-pangolin / E2E Smoke — L4 进程级端到端 (push) Successful in 18s
ci-pangolin / Go — integration (mysql/redis testcontainers) (push) Failing after 4m52s
ci-pangolin / Golden — 视觉回归 (components + auth) (push) Successful in 15s
之前 meLoadingProvider=isLoading&&!hasValue 漏了两段:启动时 auth 异步读 token 期间 isLoggedIn=false → me 同步返回免费默认(广告);token 到后 me 拉真实值时 hasValue 已是免费默认 → !hasValue 不成立、仍不转圈。改为: - meLoadingProvider:auth.isLoading 时转圈;未登录不转;已登录看 me.isLoading - 统计页 loading:auth.isLoading || usageProvider.isLoading 未登录(确定访客)/golden(settle 后)不转圈,golden 不受影响。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
40 lines
1.8 KiB
Dart
40 lines
1.8 KiB
Dart
// app_providers.dart — 语言 / 主题 / 套餐视角等基础状态(Riverpod)
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|
|
|
import '../l10n/app_text.dart';
|
|
import '../l10n/strings_en.dart';
|
|
import '../l10n/strings_zh.dart';
|
|
import 'account_providers.dart';
|
|
import 'auth_provider.dart';
|
|
|
|
/// 当前语言(单显)。设置/账户页段控切换。
|
|
final localeProvider = StateProvider<AppLang>((ref) => AppLang.zh);
|
|
|
|
/// 由语言派生的文案资源——UI 一律通过它取文案,不写死字面量。
|
|
final appTextProvider = Provider<AppText>((ref) {
|
|
final lang = ref.watch(localeProvider);
|
|
return lang == AppLang.zh ? const StringsZh() : const StringsEn();
|
|
});
|
|
|
|
/// 主题模式。默认跟随系统;设置页可显式切深色。
|
|
final themeModeProvider = StateProvider<ThemeMode>((ref) => ThemeMode.system);
|
|
|
|
/// 是否免费档——派生自真实账户(meProvider.plan == 'free')。
|
|
/// 免费版才显示额度卡与免费横幅;加载中默认按免费处理。
|
|
final isFreePlanProvider = Provider<bool>((ref) {
|
|
final me = ref.watch(meProvider).valueOrNull;
|
|
return me?.isFree ?? true;
|
|
});
|
|
|
|
/// 账户状态是否还没就绪——连接页据此先转圈再渲染,避免「先闪免费广告再跳会员」。
|
|
/// 覆盖两段窗口:① auth 还在从存储读 token(此时 isLoggedIn=false,me 会同步返回
|
|
/// 免费默认,故必须看 auth.isLoading);② 已登录但 me 还在拉真实值(此时 me 可能已有
|
|
/// 过期免费默认值,故看 me.isLoading 而非 !hasValue)。未登录(确定访客)不转圈。
|
|
final meLoadingProvider = Provider<bool>((ref) {
|
|
final auth = ref.watch(authProvider);
|
|
if (auth.isLoading) return true;
|
|
if (!auth.isLoggedIn) return false;
|
|
return ref.watch(meProvider).isLoading;
|
|
});
|