fix:第一轮跑
This commit is contained in:
1 parent
e834ec7586
commit
33235f450b
56 files changed
+4357
-210
No files matched your search
@@ -1,3 +1,5 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/gestures.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
@@ -8,9 +10,13 @@ import 'package:video_player/video_player.dart';
|
||||
|
||||
import '../../../common/config/app_h5_urls.dart';
|
||||
import '../../../common/services/device_header_service.dart';
|
||||
import '../../../common/push/umeng_push_service.dart';
|
||||
import '../../../common/push/notification_permission.dart';
|
||||
import '../../../common/privacy/umeng_privacy_consent.dart';
|
||||
import '../../../common/theme/jinzhi_theme.dart';
|
||||
import '../../../common/widgets/adaptive.dart';
|
||||
import '../../../common/router/app_router.dart';
|
||||
import '../../../common/tracking/umeng_service.dart';
|
||||
|
||||
class PrivacyConsentPage extends StatefulWidget {
|
||||
const PrivacyConsentPage({super.key});
|
||||
@@ -47,12 +53,46 @@ class _PrivacyConsentPageState extends State<PrivacyConsentPage> {
|
||||
Future<void> _agree() async {
|
||||
await DeviceHeaderService.to.markPrivacyAgreed();
|
||||
await DeviceHeaderService.to.ensureInitialized();
|
||||
if (mounted) context.go(AppRoutes.assets);
|
||||
if (mounted) {
|
||||
context.go(AppRoutes.assets);
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
_completePostConsentInitialization();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _completePostConsentInitialization() async {
|
||||
// Consent navigation must never depend on an optional native SDK. The
|
||||
// calls below still run in order, but failures are retried by normal app
|
||||
// startup instead of trapping the user on the consent page.
|
||||
try {
|
||||
await UmengPrivacyConsent.submitGranted();
|
||||
await UmengService.initIfAllowed();
|
||||
await UmengPushService.startIfAllowed();
|
||||
} catch (_) {}
|
||||
}
|
||||
|
||||
Future<void> _enter() async {
|
||||
await DeviceHeaderService.to.markIosStartupSeen();
|
||||
if (mounted) context.go(AppRoutes.assets);
|
||||
if (!mounted) return;
|
||||
// 首屏跳转与可选 SDK 调用严格解耦。原生初始化、ATT 或网络问题都不得
|
||||
// 阻断“立即进入”。
|
||||
context.go(AppRoutes.assets);
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
unawaited(_completeIosPostEnterInitialization());
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> _completeIosPostEnterInitialization() async {
|
||||
// iOS 不展示 Android 顶部说明;这里在可见首页后直接请求系统通知授权。
|
||||
// U-Push 注册保留,供 APNs token/友盟通道建立使用。
|
||||
try {
|
||||
await NotificationPermission.requestFromSettings();
|
||||
} catch (_) {}
|
||||
try {
|
||||
await UmengService.requestTrackingAuthorizationIfNeeded();
|
||||
await UmengPushService.startIfAllowed();
|
||||
} catch (_) {}
|
||||
}
|
||||
|
||||
@override
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||
import 'package:flutter_remix/flutter_remix.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:package_info_plus/package_info_plus.dart';
|
||||
import 'package:shadcn_ui/shadcn_ui.dart';
|
||||
import 'package:url_launcher/url_launcher_string.dart';
|
||||
|
||||
import '../../../common/config/app_h5_urls.dart';
|
||||
import '../../../common/push/notification_permission.dart';
|
||||
import '../../../common/router/app_router.dart';
|
||||
import '../../../common/services/cache_clear_service.dart';
|
||||
import '../../../common/theme/app_text.dart';
|
||||
import '../../../common/theme/spacing.dart';
|
||||
import '../../../common/theme/theme_controller.dart';
|
||||
@@ -20,6 +22,9 @@ import '../../auth/data/auth_models.dart';
|
||||
import '../../profile/application/profile_settings_controller.dart';
|
||||
import '../../profile/data/profile_models.dart';
|
||||
import '../application/feedback_email_service.dart';
|
||||
import '../application/update_controller.dart';
|
||||
import '../application/update_service.dart';
|
||||
import '../data/update_api_client.dart';
|
||||
|
||||
class SettingsPage extends HookConsumerWidget {
|
||||
const SettingsPage({super.key});
|
||||
@@ -31,6 +36,25 @@ class SettingsPage extends HookConsumerWidget {
|
||||
final profileSettings = ref.watch(profileSettingsControllerProvider);
|
||||
final authAsync = ref.watch(authProvider);
|
||||
final loggedIn = authAsync.valueOrNull is LoggedInAuthState;
|
||||
final updateSupported = UpdateService.isSupported;
|
||||
final upgrade = ref.watch(updateControllerProvider).valueOrNull;
|
||||
final notificationEnabled = useState(false);
|
||||
useEffect(() {
|
||||
if (!updateSupported) return null;
|
||||
Future<void>(() async {
|
||||
try {
|
||||
await ref.read(updateControllerProvider.notifier).checkUpgrade();
|
||||
} catch (_) {}
|
||||
});
|
||||
return null;
|
||||
}, [updateSupported]);
|
||||
useEffect(() {
|
||||
Future<void>(
|
||||
() async => notificationEnabled.value =
|
||||
await NotificationPermission.isGranted(),
|
||||
);
|
||||
return null;
|
||||
}, const []);
|
||||
|
||||
return Scaffold(
|
||||
backgroundColor: cs.background,
|
||||
@@ -126,6 +150,36 @@ class SettingsPage extends HookConsumerWidget {
|
||||
trailing: '占位',
|
||||
onTap: () => _showWidgetSheet(context),
|
||||
),
|
||||
Divider(height: 1, thickness: 1, color: cs.border),
|
||||
_SwitchRow(
|
||||
title: '推送通知',
|
||||
subtitle: notificationEnabled.value
|
||||
? '已允许接收服务提醒'
|
||||
: '在系统设置中开启服务提醒',
|
||||
value: notificationEnabled.value,
|
||||
onChanged: (enabled) async {
|
||||
if (enabled) {
|
||||
final granted =
|
||||
await NotificationPermission.requestFromSettings();
|
||||
notificationEnabled.value = granted;
|
||||
if (!granted) {
|
||||
await NotificationPermission.openSystemSettings();
|
||||
}
|
||||
} else {
|
||||
await NotificationPermission.openSystemSettings();
|
||||
notificationEnabled.value =
|
||||
await NotificationPermission.isGranted();
|
||||
}
|
||||
},
|
||||
),
|
||||
Divider(height: 1, thickness: 1, color: cs.border),
|
||||
_NavRow(
|
||||
icon: FlutterRemix.delete_bin_6_line,
|
||||
label: '清除缓存',
|
||||
trailing: '图片与网页缓存',
|
||||
showArrow: true,
|
||||
onTap: () => _confirmClearCache(context),
|
||||
),
|
||||
if (kDebugMode) ...[
|
||||
Divider(height: 1, thickness: 1, color: cs.border),
|
||||
_NavRow(
|
||||
@@ -144,18 +198,16 @@ class SettingsPage extends HookConsumerWidget {
|
||||
_NavRow(
|
||||
icon: FlutterRemix.file_list_3_line,
|
||||
label: '用户协议',
|
||||
onTap: () => launchUrlString(
|
||||
AppH5UrlsHelper.withCacheBuster(AppH5Urls.userProtocolUrl),
|
||||
mode: LaunchMode.inAppBrowserView,
|
||||
onTap: () => context.push(
|
||||
'${AppRoutes.web}?url=${Uri.encodeComponent(AppH5UrlsHelper.withCacheBuster(AppH5Urls.userProtocolUrl))}',
|
||||
),
|
||||
),
|
||||
Divider(height: 1, thickness: 1, color: cs.border),
|
||||
_NavRow(
|
||||
icon: FlutterRemix.shield_check_line,
|
||||
label: '隐私政策',
|
||||
onTap: () => launchUrlString(
|
||||
AppH5UrlsHelper.withCacheBuster(AppH5Urls.privacyUrl),
|
||||
mode: LaunchMode.inAppBrowserView,
|
||||
onTap: () => context.push(
|
||||
'${AppRoutes.web}?url=${Uri.encodeComponent(AppH5UrlsHelper.withCacheBuster(AppH5Urls.privacyUrl))}',
|
||||
),
|
||||
),
|
||||
Divider(height: 1, thickness: 1, color: cs.border),
|
||||
@@ -192,7 +244,27 @@ class SettingsPage extends HookConsumerWidget {
|
||||
const _Section('关于'),
|
||||
_Card(
|
||||
children: [
|
||||
const _AboutRow(),
|
||||
_AboutRow(
|
||||
upgrade: upgrade,
|
||||
enabled: updateSupported,
|
||||
onTap: () async {
|
||||
try {
|
||||
final result = await ref
|
||||
.read(updateControllerProvider.notifier)
|
||||
.checkUpgrade();
|
||||
if (!context.mounted) return;
|
||||
await UpdateService.showIfNeeded(
|
||||
context,
|
||||
ref,
|
||||
result,
|
||||
source: UpgradeCheckSource.manual,
|
||||
onLatest: () => toastInfo(msg: '已经是最新版本'),
|
||||
);
|
||||
} catch (_) {
|
||||
if (context.mounted) toastInfo(msg: '检查更新失败');
|
||||
}
|
||||
},
|
||||
),
|
||||
Divider(height: 1, thickness: 1, color: cs.border),
|
||||
_NavRow(
|
||||
icon: FlutterRemix.mail_line,
|
||||
@@ -215,6 +287,38 @@ class SettingsPage extends HookConsumerWidget {
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _confirmClearCache(BuildContext context) async {
|
||||
final systemSettings = CacheClearService.requiresSystemSettings;
|
||||
final confirmed = await showDialog<bool>(
|
||||
context: context,
|
||||
builder: (dialogContext) => AlertDialog(
|
||||
title: const Text('清除缓存'),
|
||||
content: Text(
|
||||
systemSettings
|
||||
? 'Android 需要在系统应用详情中完成缓存清理,是否立即前往?'
|
||||
: '将清除图片和网页缓存。下次打开相关页面时需要重新加载。',
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.pop(dialogContext, false),
|
||||
child: const Text('取消'),
|
||||
),
|
||||
FilledButton(
|
||||
onPressed: () => Navigator.pop(dialogContext, true),
|
||||
child: Text(systemSettings ? '前往设置' : '确认清除'),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
if (confirmed != true) return;
|
||||
if (systemSettings) {
|
||||
await CacheClearService.openAndroidSystemSettings();
|
||||
return;
|
||||
}
|
||||
await CacheClearService.clear();
|
||||
if (context.mounted) toastInfo(msg: '缓存已清除');
|
||||
}
|
||||
|
||||
Future<void> _confirmLogout(BuildContext context, WidgetRef ref) async {
|
||||
final cs = ShadTheme.of(context).colorScheme;
|
||||
final ok = await showModalBottomSheet<bool>(
|
||||
@@ -788,7 +892,7 @@ class _ThemeSegmentCard extends StatelessWidget {
|
||||
Widget build(BuildContext context) {
|
||||
final cs = ShadTheme.of(context).colorScheme;
|
||||
final options = <(ThemeMode, String, IconData)>[
|
||||
(ThemeMode.system, '跟随系统', Icons.brightness_auto_outlined),
|
||||
(ThemeMode.system, '系统主题', Icons.brightness_auto_outlined),
|
||||
(ThemeMode.light, '浅色', Icons.light_mode_outlined),
|
||||
(ThemeMode.dark, '深色', Icons.dark_mode_outlined),
|
||||
];
|
||||
@@ -948,6 +1052,7 @@ class _NavRow extends StatelessWidget {
|
||||
required this.label,
|
||||
this.trailing,
|
||||
this.onTap,
|
||||
this.showArrow = false,
|
||||
this.destructive = false,
|
||||
});
|
||||
|
||||
@@ -955,6 +1060,7 @@ class _NavRow extends StatelessWidget {
|
||||
final String label;
|
||||
final String? trailing;
|
||||
final VoidCallback? onTap;
|
||||
final bool showArrow;
|
||||
final bool destructive;
|
||||
|
||||
@override
|
||||
@@ -981,7 +1087,7 @@ class _NavRow extends StatelessWidget {
|
||||
color: cs.mutedForeground,
|
||||
),
|
||||
),
|
||||
if (onTap != null && trailing == null)
|
||||
if (onTap != null && (trailing == null || showArrow))
|
||||
Icon(
|
||||
FlutterRemix.arrow_right_s_line,
|
||||
size: 20,
|
||||
@@ -995,7 +1101,15 @@ class _NavRow extends StatelessWidget {
|
||||
}
|
||||
|
||||
class _AboutRow extends StatelessWidget {
|
||||
const _AboutRow();
|
||||
const _AboutRow({
|
||||
required this.upgrade,
|
||||
required this.enabled,
|
||||
required this.onTap,
|
||||
});
|
||||
|
||||
final UpgradeInfo? upgrade;
|
||||
final bool enabled;
|
||||
final VoidCallback onTap;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@@ -1006,33 +1120,47 @@ class _AboutRow extends StatelessWidget {
|
||||
final v = snap.hasData
|
||||
? 'v${snap.data!.version}+${snap.data!.buildNumber}'
|
||||
: '';
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 15),
|
||||
child: Row(
|
||||
children: [
|
||||
SizedBox(
|
||||
width: 24,
|
||||
child: Icon(
|
||||
FlutterRemix.information_line,
|
||||
size: 20,
|
||||
color: cs.foreground,
|
||||
final label = upgrade?.hasPublishedUpdate == true
|
||||
? '有新版 ${upgrade!.latestVersion}'
|
||||
: v;
|
||||
return InkWell(
|
||||
onTap: enabled ? onTap : null,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 15),
|
||||
child: Row(
|
||||
children: [
|
||||
SizedBox(
|
||||
width: 24,
|
||||
child: Icon(
|
||||
FlutterRemix.information_line,
|
||||
size: 20,
|
||||
color: cs.foreground,
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 13),
|
||||
Expanded(
|
||||
child: Text(
|
||||
'金值',
|
||||
style: TextStyle(fontSize: 15, color: cs.foreground),
|
||||
const SizedBox(width: 13),
|
||||
Expanded(
|
||||
child: Text(
|
||||
'金值',
|
||||
style: TextStyle(fontSize: 15, color: cs.foreground),
|
||||
),
|
||||
),
|
||||
),
|
||||
Text(
|
||||
v,
|
||||
style: AppText.meta.copyWith(
|
||||
fontSize: 13,
|
||||
color: cs.mutedForeground,
|
||||
Text(
|
||||
label,
|
||||
style: AppText.meta.copyWith(
|
||||
fontSize: 13,
|
||||
color: cs.mutedForeground,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
if (enabled) ...[
|
||||
const SizedBox(width: 6),
|
||||
Icon(
|
||||
FlutterRemix.arrow_right_s_line,
|
||||
size: 20,
|
||||
color: cs.mutedForeground,
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
|
||||
Reference in new issue
Block a user