Commit 6810f12781a1da656446fb00fefdb376fa7ae5b7

Authored by 刘宏哲
1 parent ca0b4ce5

feat(app): bug fixed

@@ -2,13 +2,13 @@ import 'dart:async'; @@ -2,13 +2,13 @@ import 'dart:async';
2 2
3 import 'package:doublefeel_flutter/app/actions/dialog_action.dart'; 3 import 'package:doublefeel_flutter/app/actions/dialog_action.dart';
4 import 'package:doublefeel_flutter/app/models/dialog_meta_data.dart'; 4 import 'package:doublefeel_flutter/app/models/dialog_meta_data.dart';
5 -import 'package:doublefeel_flutter/app/models/input_dialog_meta_data.dart';  
6 import 'package:doublefeel_flutter/app/utils/dialog_utils.dart'; 5 import 'package:doublefeel_flutter/app/utils/dialog_utils.dart';
7 import 'package:doublefeel_flutter/core/error/app_error.dart'; 6 import 'package:doublefeel_flutter/core/error/app_error.dart';
8 import 'package:doublefeel_flutter/core/logging/app_logger.dart'; 7 import 'package:doublefeel_flutter/core/logging/app_logger.dart';
9 import 'package:doublefeel_flutter/core/network/api/friend_api.dart'; 8 import 'package:doublefeel_flutter/core/network/api/friend_api.dart';
10 import 'package:doublefeel_flutter/core/services/thinking_data_service.dart'; 9 import 'package:doublefeel_flutter/core/services/thinking_data_service.dart';
11 import 'package:doublefeel_flutter/core/util/app_toast.dart'; 10 import 'package:doublefeel_flutter/core/util/app_toast.dart';
  11 +import 'package:doublefeel_flutter/data/local/user_account_storage.dart';
12 import 'package:doublefeel_flutter/data/local/user_preferences_storage.dart'; 12 import 'package:doublefeel_flutter/data/local/user_preferences_storage.dart';
13 import 'package:doublefeel_flutter/data/models/local/user_preferences.dart'; 13 import 'package:doublefeel_flutter/data/models/local/user_preferences.dart';
14 import 'package:doublefeel_flutter/l10n/l10n_extensions.dart'; 14 import 'package:doublefeel_flutter/l10n/l10n_extensions.dart';
@@ -16,18 +16,19 @@ import 'package:doublefeel_flutter/pigeon/platform_api.g.dart'; @@ -16,18 +16,19 @@ import 'package:doublefeel_flutter/pigeon/platform_api.g.dart';
16 import 'package:get/get.dart'; 16 import 'package:get/get.dart';
17 17
18 import '../data/friends_repository.dart'; 18 import '../data/friends_repository.dart';
  19 +import '../dialog/edit_remark_dialog.dart';
19 import '../models/friend_health_data.dart'; 20 import '../models/friend_health_data.dart';
20 21
21 class FriendsController extends GetxController { 22 class FriendsController extends GetxController {
22 FriendsController({ 23 FriendsController({
23 FriendsRepository? repository, 24 FriendsRepository? repository,
24 Future<void> Function(WatchAppOtherInfo info)? updateWatchOtherUserInfo, 25 Future<void> Function(WatchAppOtherInfo info)? updateWatchOtherUserInfo,
25 - }) : _repository =  
26 - repository ??  
27 - FriendsRepositoryImpl(  
28 - Get.find<FriendApi>(),  
29 - Get.find<UserPreferencesStorage>(),  
30 - ), 26 + }) : _repository = repository ??
  27 + FriendsRepositoryImpl(
  28 + Get.find<FriendApi>(),
  29 + Get.find<UserPreferencesStorage>(),
  30 + Get.find<UserAccountStorage>(),
  31 + ),
31 _updateWatchOtherUserInfo = updateWatchOtherUserInfo ?? 32 _updateWatchOtherUserInfo = updateWatchOtherUserInfo ??
32 ((info) => PlatformHostApi().refreshWatchAppAndWidgets()); 33 ((info) => PlatformHostApi().refreshWatchAppAndWidgets());
33 34
@@ -41,6 +42,7 @@ class FriendsController extends GetxController { @@ -41,6 +42,7 @@ class FriendsController extends GetxController {
41 Future<void>? _friendsRequest; 42 Future<void>? _friendsRequest;
42 bool _isPageVisible = false; 43 bool _isPageVisible = false;
43 late final Worker _preferencesWorker; 44 late final Worker _preferencesWorker;
  45 + late final Worker _realtimeStressSettingsWorker;
44 bool _showRealtimeStress = false; 46 bool _showRealtimeStress = false;
45 47
46 bool get isFull => friends.length >= maxFriends; 48 bool get isFull => friends.length >= maxFriends;
@@ -53,19 +55,28 @@ class FriendsController extends GetxController { @@ -53,19 +55,28 @@ class FriendsController extends GetxController {
53 Get.find<UserPreferencesStorage>().preferences, 55 Get.find<UserPreferencesStorage>().preferences,
54 _onPreferencesChanged, 56 _onPreferencesChanged,
55 ); 57 );
  58 + _realtimeStressSettingsWorker = ever<int>(
  59 + Get.find<UserAccountStorage>().realtimeStressSettingsVersion,
  60 + (_) => _onPreferencesChanged(
  61 + Get.find<UserPreferencesStorage>().preferences.value),
  62 + );
56 unawaited(refreshData()); 63 unawaited(refreshData());
57 } 64 }
58 65
59 @override 66 @override
60 void onClose() { 67 void onClose() {
61 _preferencesWorker.dispose(); 68 _preferencesWorker.dispose();
  69 + _realtimeStressSettingsWorker.dispose();
62 super.onClose(); 70 super.onClose();
63 } 71 }
64 72
65 bool get _currentShowRealtimeStress { 73 bool get _currentShowRealtimeStress {
66 final preferences = Get.find<UserPreferencesStorage>().preferences.value; 74 final preferences = Get.find<UserPreferencesStorage>().preferences.value;
  75 + final userId = preferences.meUserInfo?.id;
67 return preferences.vipInfo?.isVip == true && 76 return preferences.vipInfo?.isVip == true &&
68 - (preferences.showRealtimeStress ?? true); 77 + (userId == null ||
  78 + (Get.find<UserAccountStorage>().showRealtimeStress(userId) ??
  79 + true));
69 } 80 }
70 81
71 void _onPreferencesChanged(UserPreferences _) { 82 void _onPreferencesChanged(UserPreferences _) {
@@ -115,28 +126,26 @@ class FriendsController extends GetxController { @@ -115,28 +126,26 @@ class FriendsController extends GetxController {
115 } 126 }
116 127
117 Future<void> showEditRemarkDialog(FriendHealthData friend) async { 128 Future<void> showEditRemarkDialog(FriendHealthData friend) async {
118 - final result = await DialogUtils.showInputDialog(  
119 - InputDialogMetaData( 129 + await Get.dialog<String>(
  130 + FriendEditRemarkDialog(
120 title: l10n.friendsEditRemarkTitle, 131 title: l10n.friendsEditRemarkTitle,
121 initialValue: friend.remark ?? '', 132 initialValue: friend.remark ?? '',
122 hintText: l10n.friendsEditRemarkHint, 133 hintText: l10n.friendsEditRemarkHint,
123 confirmText: l10n.friendsSave, 134 confirmText: l10n.friendsSave,
124 maxLength: 6, 135 maxLength: 6,
  136 + onConfirm: (remark) => _updateRemark(friend, remark),
125 ), 137 ),
  138 + barrierDismissible: true,
126 ); 139 );
  140 + }
127 141
128 - final remark = result.data?.trim();  
129 - if (result.action != DialogAction.confirm ||  
130 - remark == null ||  
131 - remark.isEmpty) {  
132 - return;  
133 - }  
134 - 142 + Future<bool> _updateRemark(FriendHealthData friend, String remark) async {
135 final userId = friend.userId; 143 final userId = friend.userId;
136 - if (userId == null) return; 144 + if (userId == null) return false;
137 try { 145 try {
138 await _repository.updateRemark(userId, remark); 146 await _repository.updateRemark(userId, remark);
139 await loadFriends(); 147 await loadFriends();
  148 + return true;
140 } catch (error, stackTrace) { 149 } catch (error, stackTrace) {
141 if (error is AppError) { 150 if (error is AppError) {
142 AppToast.show(error.displayMessage); 151 AppToast.show(error.displayMessage);
@@ -147,6 +156,7 @@ class FriendsController extends GetxController { @@ -147,6 +156,7 @@ class FriendsController extends GetxController {
147 stackTrace, 156 stackTrace,
148 ); 157 );
149 } 158 }
  159 + return false;
150 } 160 }
151 } 161 }
152 162
1 import 'package:doublefeel_flutter/core/network/api/friend_api.dart'; 1 import 'package:doublefeel_flutter/core/network/api/friend_api.dart';
2 import 'package:doublefeel_flutter/core/result/app_result.dart'; 2 import 'package:doublefeel_flutter/core/result/app_result.dart';
3 import 'package:doublefeel_flutter/data/local/user_preferences_storage.dart'; 3 import 'package:doublefeel_flutter/data/local/user_preferences_storage.dart';
  4 +import 'package:doublefeel_flutter/data/local/user_account_storage.dart';
4 import 'package:doublefeel_flutter/data/models/friend/friend_models.dart' 5 import 'package:doublefeel_flutter/data/models/friend/friend_models.dart'
5 as api_models; 6 as api_models;
6 import 'package:doublefeel_flutter/l10n/l10n_extensions.dart'; 7 import 'package:doublefeel_flutter/l10n/l10n_extensions.dart';
@@ -36,10 +37,15 @@ class FriendListData { @@ -36,10 +37,15 @@ class FriendListData {
36 } 37 }
37 38
38 class FriendsRepositoryImpl implements FriendsRepository { 39 class FriendsRepositoryImpl implements FriendsRepository {
39 - const FriendsRepositoryImpl(this._friendApi, [this._userPreferencesStorage]); 40 + const FriendsRepositoryImpl(
  41 + this._friendApi, [
  42 + this._userPreferencesStorage,
  43 + this._userAccountStorage,
  44 + ]);
40 45
41 final FriendApi _friendApi; 46 final FriendApi _friendApi;
42 final UserPreferencesStorage? _userPreferencesStorage; 47 final UserPreferencesStorage? _userPreferencesStorage;
  48 + final UserAccountStorage? _userAccountStorage;
43 49
44 @override 50 @override
45 Future<List<FriendHealthData>> getFriends( 51 Future<List<FriendHealthData>> getFriends(
@@ -160,8 +166,12 @@ class FriendsRepositoryImpl implements FriendsRepository { @@ -160,8 +166,12 @@ class FriendsRepositoryImpl implements FriendsRepository {
160 (_userPreferencesStorage ?? Get.find<UserPreferencesStorage>()) 166 (_userPreferencesStorage ?? Get.find<UserPreferencesStorage>())
161 .preferences 167 .preferences
162 .value; 168 .value;
  169 + final userId = preferences.meUserInfo?.id;
163 return preferences.vipInfo?.isVip == true && 170 return preferences.vipInfo?.isVip == true &&
164 - (preferences.showRealtimeStress ?? true); 171 + (userId == null ||
  172 + ((_userAccountStorage ?? Get.find<UserAccountStorage>())
  173 + .showRealtimeStress(userId) ??
  174 + true));
165 } 175 }
166 176
167 api_models.FriendRealtimeStressItem? _latestRealtimeStress( 177 api_models.FriendRealtimeStressItem? _latestRealtimeStress(
  1 +import 'package:doublefeel_flutter/core/theme/app_colors.dart';
  2 +import 'package:doublefeel_flutter/core/util/size_extensions.dart';
  3 +import 'package:flutter/material.dart';
  4 +import 'package:flutter/services.dart';
  5 +import 'package:get/get.dart';
  6 +
  7 +class FriendEditRemarkDialog extends StatefulWidget {
  8 + const FriendEditRemarkDialog({
  9 + super.key,
  10 + required this.title,
  11 + required this.initialValue,
  12 + required this.hintText,
  13 + required this.confirmText,
  14 + required this.maxLength,
  15 + this.onConfirm,
  16 + });
  17 +
  18 + final String title;
  19 + final String initialValue;
  20 + final String hintText;
  21 + final String confirmText;
  22 + final int maxLength;
  23 + final Future<bool> Function(String value)? onConfirm;
  24 +
  25 + @override
  26 + State<FriendEditRemarkDialog> createState() => _FriendEditRemarkDialogState();
  27 +}
  28 +
  29 +class _FriendEditRemarkDialogState extends State<FriendEditRemarkDialog> {
  30 + late final TextEditingController _controller;
  31 + bool _isSubmitting = false;
  32 +
  33 + @override
  34 + void initState() {
  35 + super.initState();
  36 + _controller = TextEditingController(text: widget.initialValue);
  37 + }
  38 +
  39 + @override
  40 + void dispose() {
  41 + _controller.dispose();
  42 + super.dispose();
  43 + }
  44 +
  45 + @override
  46 + Widget build(BuildContext context) {
  47 + return Dialog(
  48 + backgroundColor: Colors.transparent,
  49 + insetPadding: EdgeInsets.zero,
  50 + child: Container(
  51 + width: 300.dp,
  52 + padding: EdgeInsets.fromLTRB(24.dp, 24.dp, 24.dp, 20.dp),
  53 + decoration: BoxDecoration(
  54 + color: Colors.white,
  55 + borderRadius: BorderRadius.circular(24.dp),
  56 + ),
  57 + child: Column(
  58 + mainAxisSize: MainAxisSize.min,
  59 + children: [
  60 + SizedBox(
  61 + width: 220.dp,
  62 + child: Text(
  63 + widget.title,
  64 + maxLines: 1,
  65 + overflow: TextOverflow.ellipsis,
  66 + textAlign: TextAlign.center,
  67 + style: const TextStyle(
  68 + color: Color(0xFF141414),
  69 + fontSize: 18,
  70 + fontWeight: FontWeight.w600,
  71 + height: 1.25,
  72 + ),
  73 + ),
  74 + ),
  75 + SizedBox(height: 16.dp),
  76 + ValueListenableBuilder<TextEditingValue>(
  77 + valueListenable: _controller,
  78 + builder: (context, value, _) {
  79 + return Container(
  80 + width: double.infinity,
  81 + height: 52.dp,
  82 + padding: EdgeInsets.only(left: 22.dp, right: 22.dp),
  83 + decoration: BoxDecoration(
  84 + color: const Color(0xFFF3F3F3),
  85 + borderRadius: BorderRadius.circular(27.dp),
  86 + ),
  87 + child: Row(
  88 + children: [
  89 + Expanded(
  90 + child: TextField(
  91 + controller: _controller,
  92 + autofocus: true,
  93 + maxLines: 1,
  94 + inputFormatters: [
  95 + LengthLimitingTextInputFormatter(widget.maxLength),
  96 + ],
  97 + decoration: InputDecoration(
  98 + border: InputBorder.none,
  99 + hintText: widget.hintText,
  100 + hintStyle: const TextStyle(
  101 + color: AppColors.textTertiary,
  102 + fontSize: 16,
  103 + fontWeight: FontWeight.w400,
  104 + ),
  105 + isCollapsed: true,
  106 + ),
  107 + style: const TextStyle(
  108 + color: Color(0xFF141414),
  109 + fontSize: 16,
  110 + fontWeight: FontWeight.w400,
  111 + ),
  112 + ),
  113 + ),
  114 + Text(
  115 + '${value.text.characters.length}/${widget.maxLength}',
  116 + style: const TextStyle(
  117 + color: Color(0xFFD9D9D9),
  118 + fontSize: 16,
  119 + fontWeight: FontWeight.w400,
  120 + ),
  121 + ),
  122 + ],
  123 + ),
  124 + );
  125 + },
  126 + ),
  127 + SizedBox(height: 24.dp),
  128 + ValueListenableBuilder<TextEditingValue>(
  129 + valueListenable: _controller,
  130 + builder: (context, value, _) {
  131 + final enabled = value.text.trim().isNotEmpty && !_isSubmitting;
  132 + return Opacity(
  133 + opacity: enabled ? 1 : 0.4,
  134 + child: GestureDetector(
  135 + onTap: enabled ? () => _confirm(value.text) : null,
  136 + behavior: HitTestBehavior.opaque,
  137 + child: Container(
  138 + width: 220.dp,
  139 + height: 48.dp,
  140 + alignment: Alignment.center,
  141 + decoration: BoxDecoration(
  142 + color: AppColors.primary,
  143 + borderRadius: BorderRadius.circular(24.dp),
  144 + ),
  145 + padding: EdgeInsets.symmetric(horizontal: 16.dp),
  146 + child: Text(
  147 + widget.confirmText,
  148 + maxLines: 1,
  149 + overflow: TextOverflow.ellipsis,
  150 + textAlign: TextAlign.center,
  151 + style: const TextStyle(
  152 + color: Colors.white,
  153 + fontSize: 14,
  154 + fontWeight: FontWeight.w500,
  155 + height: 1.2,
  156 + ),
  157 + ),
  158 + ),
  159 + ),
  160 + );
  161 + },
  162 + ),
  163 + ],
  164 + ),
  165 + ),
  166 + );
  167 + }
  168 +
  169 + Future<void> _confirm(String value) async {
  170 + final trimmed = value.trim();
  171 + if (trimmed.isEmpty || _isSubmitting) {
  172 + return;
  173 + }
  174 + setState(() {
  175 + _isSubmitting = true;
  176 + });
  177 + try {
  178 + final canClose =
  179 + await (widget.onConfirm?.call(trimmed) ?? Future<bool>.value(true));
  180 + if (canClose && mounted && Get.isDialogOpen == true) {
  181 + Get.back(result: trimmed);
  182 + }
  183 + } finally {
  184 + if (mounted) {
  185 + setState(() {
  186 + _isSubmitting = false;
  187 + });
  188 + }
  189 + }
  190 + }
  191 +}
1 import 'package:doublefeel_flutter/core/network/api/user_api.dart'; 1 import 'package:doublefeel_flutter/core/network/api/user_api.dart';
  2 +import 'package:doublefeel_flutter/data/local/user_account_storage.dart';
2 import 'package:doublefeel_flutter/data/local/user_preferences_storage.dart'; 3 import 'package:doublefeel_flutter/data/local/user_preferences_storage.dart';
3 import 'package:get/get.dart'; 4 import 'package:get/get.dart';
4 5
@@ -11,6 +12,7 @@ class PrivacySettingsBinding extends Bindings { @@ -11,6 +12,7 @@ class PrivacySettingsBinding extends Bindings {
11 PrivacySettingsController( 12 PrivacySettingsController(
12 Get.find<UserApi>(), 13 Get.find<UserApi>(),
13 Get.find<UserPreferencesStorage>(), 14 Get.find<UserPreferencesStorage>(),
  15 + Get.find<UserAccountStorage>(),
14 ), 16 ),
15 ); 17 );
16 } 18 }
@@ -2,19 +2,26 @@ import 'package:doublefeel_flutter/app/routes/app_pages.dart'; @@ -2,19 +2,26 @@ import 'package:doublefeel_flutter/app/routes/app_pages.dart';
2 import 'package:doublefeel_flutter/core/constants/intent_keys.dart'; 2 import 'package:doublefeel_flutter/core/constants/intent_keys.dart';
3 import 'package:doublefeel_flutter/core/network/api/user_api.dart'; 3 import 'package:doublefeel_flutter/core/network/api/user_api.dart';
4 import 'package:doublefeel_flutter/core/result/app_result.dart'; 4 import 'package:doublefeel_flutter/core/result/app_result.dart';
  5 +import 'package:doublefeel_flutter/data/local/user_account_storage.dart';
5 import 'package:doublefeel_flutter/data/local/user_preferences_storage.dart'; 6 import 'package:doublefeel_flutter/data/local/user_preferences_storage.dart';
6 import 'package:doublefeel_flutter/data/models/local/user_preferences.dart'; 7 import 'package:doublefeel_flutter/data/models/local/user_preferences.dart';
7 import 'package:doublefeel_flutter/data/models/user/user_models.dart'; 8 import 'package:doublefeel_flutter/data/models/user/user_models.dart';
8 import 'package:get/get.dart'; 9 import 'package:get/get.dart';
9 10
10 class PrivacySettingsController extends GetxController { 11 class PrivacySettingsController extends GetxController {
11 - PrivacySettingsController(this._userApi, this._userPreferencesStorage); 12 + PrivacySettingsController(
  13 + this._userApi,
  14 + this._userPreferencesStorage,
  15 + this._userAccountStorage,
  16 + );
12 17
13 final UserApi _userApi; 18 final UserApi _userApi;
14 final UserPreferencesStorage _userPreferencesStorage; 19 final UserPreferencesStorage _userPreferencesStorage;
  20 + final UserAccountStorage _userAccountStorage;
15 21
16 - final enableAddWithUCode = false.obs;  
17 - final isUpdatingEnableAddWithUCode = false.obs; 22 + /// 页面文案是“禁止通过 ID 添加我”,因此该状态为 true 时表示禁止。
  23 + final disableAddWithUCode = false.obs;
  24 + final isUpdatingDisableAddWithUCode = false.obs;
18 final isVip = false.obs; 25 final isVip = false.obs;
19 final showRealtimeStress = false.obs; 26 final showRealtimeStress = false.obs;
20 late final Worker _preferencesWorker; 27 late final Worker _preferencesWorker;
@@ -36,29 +43,32 @@ class PrivacySettingsController extends GetxController { @@ -36,29 +43,32 @@ class PrivacySettingsController extends GetxController {
36 } 43 }
37 44
38 void _syncFromPreferences(UserPreferences preferences) { 45 void _syncFromPreferences(UserPreferences preferences) {
39 - enableAddWithUCode.value =  
40 - preferences.meUserInfo?.enableAddWithUCode == 1; 46 + disableAddWithUCode.value =
  47 + preferences.meUserInfo?.enableAddWithUCode != 1;
41 isVip.value = preferences.vipInfo?.isVip == true; 48 isVip.value = preferences.vipInfo?.isVip == true;
42 - showRealtimeStress.value =  
43 - isVip.value && (preferences.showRealtimeStress ?? true); 49 + final userId = preferences.meUserInfo?.id;
  50 + showRealtimeStress.value = isVip.value &&
  51 + (userId == null
  52 + ? true
  53 + : (_userAccountStorage.showRealtimeStress(userId) ?? true));
44 } 54 }
45 55
46 - Future<void> updateEnableAddWithUCode(bool enabled) async {  
47 - if (isUpdatingEnableAddWithUCode.value) return; 56 + Future<void> updateDisableAddWithUCode(bool disabled) async {
  57 + if (isUpdatingDisableAddWithUCode.value) return;
48 58
49 - isUpdatingEnableAddWithUCode.value = true; 59 + isUpdatingDisableAddWithUCode.value = true;
50 try { 60 try {
51 final result = await _userApi.updateOwnerUserInfo( 61 final result = await _userApi.updateOwnerUserInfo(
52 - enableAddWithUcode: enabled ? 0 : 1, 62 + enableAddWithUcode: disabled ? 0 : 1,
53 ); 63 );
54 if (result is AppSuccess<UserInfoResponse>) { 64 if (result is AppSuccess<UserInfoResponse>) {
55 await _userPreferencesStorage.updateMeUserInfo(result.data); 65 await _userPreferencesStorage.updateMeUserInfo(result.data);
56 - enableAddWithUCode.value = result.data.enableAddWithUCode == null  
57 - ? !enabled  
58 - : result.data.enableAddWithUCode == 1; 66 + disableAddWithUCode.value = result.data.enableAddWithUCode == null
  67 + ? disabled
  68 + : result.data.enableAddWithUCode != 1;
59 } 69 }
60 } finally { 70 } finally {
61 - isUpdatingEnableAddWithUCode.value = false; 71 + isUpdatingDisableAddWithUCode.value = false;
62 } 72 }
63 } 73 }
64 74
@@ -73,7 +83,10 @@ class PrivacySettingsController extends GetxController { @@ -73,7 +83,10 @@ class PrivacySettingsController extends GetxController {
73 return; 83 return;
74 } 84 }
75 85
76 - await _userPreferencesStorage.updateShowRealtimeStress(enabled); 86 + final userId = _userPreferencesStorage.preferences.value.meUserInfo?.id;
  87 + if (userId == null) return;
  88 + await _userAccountStorage.updateShowRealtimeStress(userId, enabled);
  89 + _syncFromPreferences(_userPreferencesStorage.preferences.value);
77 } 90 }
78 91
79 void executeBackLogic() { 92 void executeBackLogic() {
@@ -65,10 +65,10 @@ class PrivacySettingsView extends GetView<PrivacySettingsController> { @@ -65,10 +65,10 @@ class PrivacySettingsView extends GetView<PrivacySettingsController> {
65 Widget noAddByIdView(BuildContext context) { 65 Widget noAddByIdView(BuildContext context) {
66 return _settingRow( 66 return _settingRow(
67 title: context.l10n.privacySettingsDisableAddById, 67 title: context.l10n.privacySettingsDisableAddById,
68 - value: controller.enableAddWithUCode,  
69 - onChanged: () => controller.isUpdatingEnableAddWithUCode.value 68 + value: controller.disableAddWithUCode,
  69 + onChanged: () => controller.isUpdatingDisableAddWithUCode.value
70 ? null 70 ? null
71 - : controller.updateEnableAddWithUCode, 71 + : controller.updateDisableAddWithUCode,
72 ); 72 );
73 } 73 }
74 74
@@ -126,7 +126,7 @@ class PrivacySettingsView extends GetView<PrivacySettingsController> { @@ -126,7 +126,7 @@ class PrivacySettingsView extends GetView<PrivacySettingsController> {
126 scaleX: 52 / 51, 126 scaleX: 52 / 51,
127 scaleY: 28 / 31, 127 scaleY: 28 / 31,
128 child: CupertinoSwitch( 128 child: CupertinoSwitch(
129 - value: !value.value, 129 + value: value.value,
130 activeTrackColor: AppColors.primary, 130 activeTrackColor: AppColors.primary,
131 inactiveTrackColor: const Color(0xFFE5E5EA), 131 inactiveTrackColor: const Color(0xFFE5E5EA),
132 onChanged: onChanged(), 132 onChanged: onChanged(),
  1 +import 'package:get/get.dart';
1 import 'package:shared_preferences/shared_preferences.dart'; 2 import 'package:shared_preferences/shared_preferences.dart';
2 3
3 /// 账号级持久化存储。 4 /// 账号级持久化存储。
@@ -26,6 +27,13 @@ class UserAccountStorage { @@ -26,6 +27,13 @@ class UserAccountStorage {
26 static String _appleHealthUploadTestLastSyncTimeKey(int userId) => 27 static String _appleHealthUploadTestLastSyncTimeKey(int userId) =>
27 'apple_health_upload_test_last_sync_time_$userId'; 28 'apple_health_upload_test_last_sync_time_$userId';
28 29
  30 + /// 是否显示实时压力,按 userId 隔离,避免会话数据刷新时覆盖该偏好。
  31 + static String _showRealtimeStressKey(int userId) =>
  32 + 'account_show_realtime_stress_$userId';
  33 +
  34 + /// 在实时压力偏好更新后通知依赖它的页面刷新。
  35 + final realtimeStressSettingsVersion = 0.obs;
  36 +
29 /// Onboarding 已全部完成的哨兵值 37 /// Onboarding 已全部完成的哨兵值
30 static const int _kOnboardingCompleted = -1; 38 static const int _kOnboardingCompleted = -1;
31 39
@@ -73,6 +81,17 @@ class UserAccountStorage { @@ -73,6 +81,17 @@ class UserAccountStorage {
73 int? getLastAddFriendBannerShowTime(int userId) => 81 int? getLastAddFriendBannerShowTime(int userId) =>
74 _prefs.getInt('last_add_friend_banner_show_time_$userId'); 82 _prefs.getInt('last_add_friend_banner_show_time_$userId');
75 83
  84 + // ─── 实时压力展示偏好 ────────────────────────────────────────────────────
  85 +
  86 + /// `null` 表示用户尚未设置,调用方应使用产品默认值。
  87 + bool? showRealtimeStress(int userId) =>
  88 + _prefs.getBool(_showRealtimeStressKey(userId));
  89 +
  90 + Future<void> updateShowRealtimeStress(int userId, bool enabled) async {
  91 + await _prefs.setBool(_showRealtimeStressKey(userId), enabled);
  92 + realtimeStressSettingsVersion.value++;
  93 + }
  94 +
76 // ─── Apple Health 上传记录 ──────────────────────────────────────────────── 95 // ─── Apple Health 上传记录 ────────────────────────────────────────────────
77 96
78 String? appleHealthUploadLocalRecordJson(int userId) => 97 String? appleHealthUploadLocalRecordJson(int userId) =>
@@ -122,10 +122,6 @@ class UserPreferencesStorage { @@ -122,10 +122,6 @@ class UserPreferencesStorage {
122 await _persist(preferences.value.copyWith(vipInfo: vipInfo)); 122 await _persist(preferences.value.copyWith(vipInfo: vipInfo));
123 } 123 }
124 124
125 - Future<void> updateShowRealtimeStress(bool enabled) async {  
126 - await _persist(preferences.value.copyWith(showRealtimeStress: enabled));  
127 - }  
128 -  
129 Future<void> updateFromLogin({ 125 Future<void> updateFromLogin({
130 required String accessToken, 126 required String accessToken,
131 UserInfoResponse? me, 127 UserInfoResponse? me,
@@ -9,7 +9,6 @@ class UserPreferences { @@ -9,7 +9,6 @@ class UserPreferences {
9 this.accessToken = '', 9 this.accessToken = '',
10 this.rongcloudToken = '', 10 this.rongcloudToken = '',
11 this.vipInfo, 11 this.vipInfo,
12 - this.showRealtimeStress,  
13 }); 12 });
14 13
15 final UserInfoResponse? meUserInfo; 14 final UserInfoResponse? meUserInfo;
@@ -18,10 +17,6 @@ class UserPreferences { @@ -18,10 +17,6 @@ class UserPreferences {
18 final String rongcloudToken; 17 final String rongcloudToken;
19 final UserPreferencesVipInfo? vipInfo; 18 final UserPreferencesVipInfo? vipInfo;
20 19
21 - /// `null` preserves the product default: enabled for members and disabled  
22 - /// for non-members.  
23 - final bool? showRealtimeStress;  
24 -  
25 static const empty = UserPreferences(); 20 static const empty = UserPreferences();
26 21
27 factory UserPreferences.fromJson(Map<String, dynamic> json) { 22 factory UserPreferences.fromJson(Map<String, dynamic> json) {
@@ -40,7 +35,6 @@ class UserPreferences { @@ -40,7 +35,6 @@ class UserPreferences {
40 ? null 35 ? null
41 : UserPreferencesVipInfo.fromJson( 36 : UserPreferencesVipInfo.fromJson(
42 json['vip_info'] as Map<String, dynamic>), 37 json['vip_info'] as Map<String, dynamic>),
43 - showRealtimeStress: json['show_realtime_stress'] as bool?,  
44 ); 38 );
45 } 39 }
46 40
@@ -53,9 +47,6 @@ class UserPreferences { @@ -53,9 +47,6 @@ class UserPreferences {
53 val['access_token'] = accessToken; 47 val['access_token'] = accessToken;
54 val['rongcloud_token'] = rongcloudToken; 48 val['rongcloud_token'] = rongcloudToken;
55 if (vipInfo != null) val['vip_info'] = vipInfo!.toJson(); 49 if (vipInfo != null) val['vip_info'] = vipInfo!.toJson();
56 - if (showRealtimeStress != null) {  
57 - val['show_realtime_stress'] = showRealtimeStress;  
58 - }  
59 return val; 50 return val;
60 } 51 }
61 52
@@ -65,7 +56,6 @@ class UserPreferences { @@ -65,7 +56,6 @@ class UserPreferences {
65 String? accessToken, 56 String? accessToken,
66 String? rongcloudToken, 57 String? rongcloudToken,
67 UserPreferencesVipInfo? vipInfo, 58 UserPreferencesVipInfo? vipInfo,
68 - bool? showRealtimeStress,  
69 bool clearPartner = false, 59 bool clearPartner = false,
70 }) { 60 }) {
71 return UserPreferences( 61 return UserPreferences(
@@ -75,7 +65,6 @@ class UserPreferences { @@ -75,7 +65,6 @@ class UserPreferences {
75 accessToken: accessToken ?? this.accessToken, 65 accessToken: accessToken ?? this.accessToken,
76 rongcloudToken: rongcloudToken ?? this.rongcloudToken, 66 rongcloudToken: rongcloudToken ?? this.rongcloudToken,
77 vipInfo: vipInfo ?? this.vipInfo, 67 vipInfo: vipInfo ?? this.vipInfo,
78 - showRealtimeStress: showRealtimeStress ?? this.showRealtimeStress,  
79 ); 68 );
80 } 69 }
81 } 70 }