185 lines
6.1 KiB
Dart
185 lines
6.1 KiB
Dart
import 'package:cupertino_ui/cupertino_ui.dart';
|
||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||
import 'package:go_router/go_router.dart';
|
||
|
||
import '../../../core/widgets/feature_ui.dart';
|
||
import '../../../core/widgets/settings_ui.dart';
|
||
import '../../../l10n/app_localizations.dart';
|
||
import '../data/account_providers.dart';
|
||
import 'account_controller.dart';
|
||
|
||
/// 微信绑定页(账号与安全 → 微信绑定)。
|
||
///
|
||
/// 展示当前绑定状态:已绑定时提供「解除绑定」,未绑定时提供「绑定微信」。
|
||
///
|
||
/// 注意:绑定动作需要微信开放平台 SDK 返回的授权 `code`。当前项目尚未接入
|
||
/// 微信 SDK,[_bind] 中的 `authCode` 获取处以 TODO 标注,接入后替换即可。
|
||
class WechatBindingPage extends ConsumerWidget {
|
||
const WechatBindingPage({super.key});
|
||
|
||
@override
|
||
Widget build(BuildContext context, WidgetRef ref) {
|
||
final l10n = AppLocalizations.of(context);
|
||
final bindingAsync = ref.watch(wechatBindingProvider);
|
||
final action = ref.watch(accountControllerProvider);
|
||
|
||
ref.listen<AccountActionState>(accountControllerProvider, (prev, next) {
|
||
final alert = next.alert;
|
||
if (alert == null) return;
|
||
ref.read(accountControllerProvider.notifier).consumeAlert();
|
||
AppDialogs.alert(context, message: alert, confirmText: l10n.commonConfirm);
|
||
});
|
||
|
||
return CupertinoPageScaffold(
|
||
navigationBar: CupertinoNavigationBar(
|
||
middle: Text(l10n.wechatTitle),
|
||
leading: CupertinoButton(
|
||
padding: EdgeInsets.zero,
|
||
child: const Icon(CupertinoIcons.back),
|
||
onPressed: () => context.pop(),
|
||
),
|
||
),
|
||
child: SafeArea(
|
||
child: bindingAsync.when(
|
||
loading: () => const Center(child: CupertinoActivityIndicator()),
|
||
error: (e, _) => Center(
|
||
child: Column(
|
||
mainAxisAlignment: MainAxisAlignment.center,
|
||
children: [
|
||
Text(
|
||
l10n.commonLoadFailed,
|
||
style: const TextStyle(fontSize: 13, color: AppColors.sub),
|
||
),
|
||
const SizedBox(height: 10),
|
||
GestureDetector(
|
||
onTap: () => ref.invalidate(wechatBindingProvider),
|
||
child: Text(
|
||
l10n.commonRetry,
|
||
style: const TextStyle(
|
||
fontSize: 13,
|
||
fontWeight: FontWeight.w700,
|
||
color: AppColors.green,
|
||
),
|
||
),
|
||
),
|
||
],
|
||
),
|
||
),
|
||
data: (binding) => ListView(
|
||
padding: const EdgeInsets.only(bottom: 24),
|
||
children: [
|
||
SectionTitle(l10n.securityWechat),
|
||
CardBox(
|
||
padding: EdgeInsets.zero,
|
||
child: SettingsRow(
|
||
emoji: '💬',
|
||
color: AppColors.green,
|
||
title: binding.bound
|
||
? (binding.nickname?.isNotEmpty == true
|
||
? binding.nickname!
|
||
: l10n.wechatBound)
|
||
: l10n.wechatUnbound,
|
||
subtitle: binding.bound
|
||
? binding.bindTime
|
||
: l10n.securityWechatSub,
|
||
trailing: _StatusBadge(bound: binding.bound, l10n: l10n),
|
||
),
|
||
),
|
||
const SizedBox(height: 8),
|
||
if (binding.bound)
|
||
PrimaryButton(
|
||
text: l10n.wechatUnbind,
|
||
color: AppColors.red,
|
||
loading: action.submitting,
|
||
onPressed: () => _confirmUnbind(context, ref, l10n),
|
||
)
|
||
else
|
||
PrimaryButton(
|
||
text: l10n.wechatBind,
|
||
loading: action.submitting,
|
||
onPressed: () => _bind(context, ref, l10n),
|
||
),
|
||
],
|
||
),
|
||
),
|
||
),
|
||
);
|
||
}
|
||
|
||
/// 绑定微信:需先通过微信 SDK 获取授权 code。
|
||
Future<void> _bind(
|
||
BuildContext context,
|
||
WidgetRef ref,
|
||
AppLocalizations l10n,
|
||
) async {
|
||
// TODO(wechat): 接入微信开放平台 SDK(如 fluwx),
|
||
// 调用 sendWeChatAuth 获取授权 code 后传入 bindWechat。
|
||
const String? authCode = null;
|
||
if (authCode == null) {
|
||
await AppDialogs.alert(
|
||
context,
|
||
message: l10n.wechatSdkUnavailable,
|
||
confirmText: l10n.commonConfirm,
|
||
);
|
||
return;
|
||
}
|
||
|
||
final ok = await ref.read(accountControllerProvider.notifier).bindWechat(authCode);
|
||
if (!ok || !context.mounted) return;
|
||
await AppDialogs.alert(
|
||
context,
|
||
message: l10n.wechatBindSuccess,
|
||
confirmText: l10n.commonConfirm,
|
||
);
|
||
}
|
||
|
||
/// 解绑微信(二次确认)。
|
||
Future<void> _confirmUnbind(
|
||
BuildContext context,
|
||
WidgetRef ref,
|
||
AppLocalizations l10n,
|
||
) async {
|
||
final confirmed = await AppDialogs.confirm(
|
||
context,
|
||
title: l10n.wechatUnbind,
|
||
message: l10n.wechatUnbindConfirm,
|
||
confirmText: l10n.wechatUnbind,
|
||
cancelText: l10n.commonCancel,
|
||
);
|
||
if (!confirmed) return;
|
||
|
||
final ok = await ref.read(accountControllerProvider.notifier).unbindWechat();
|
||
if (!ok || !context.mounted) return;
|
||
await AppDialogs.alert(
|
||
context,
|
||
message: l10n.wechatUnbindSuccess,
|
||
confirmText: l10n.commonConfirm,
|
||
);
|
||
}
|
||
}
|
||
|
||
/// 绑定状态徽标。
|
||
class _StatusBadge extends StatelessWidget {
|
||
const _StatusBadge({required this.bound, required this.l10n});
|
||
|
||
final bool bound;
|
||
final AppLocalizations l10n;
|
||
|
||
@override
|
||
Widget build(BuildContext context) => Container(
|
||
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 5),
|
||
decoration: BoxDecoration(
|
||
color: bound ? const Color(0xFFECFDF3) : AppColors.track,
|
||
borderRadius: BorderRadius.circular(16),
|
||
),
|
||
child: Text(
|
||
bound ? l10n.wechatBound : l10n.wechatUnbound,
|
||
style: TextStyle(
|
||
fontSize: 12,
|
||
fontWeight: FontWeight.w700,
|
||
color: bound ? AppColors.green : AppColors.sub,
|
||
),
|
||
),
|
||
);
|
||
}
|