feat: integrate invite API and improve notification handling

- Add invite code display and binding functionality via API
- Fix notification unread count sync on auth state change
- Improve notification mark read with server state validation
- Add auth state listener to trigger notification refresh
- Add YaoCoinConverter for coin-to-yao type conversion
- Remove YaoLegend from divination screens (UI cleanup)
- Abbreviate relation labels in yao detail view
- Add re-register notice to account delete screen
- Update 'coins' terminology to 'points' in localization
- Fix backend points consumption to only run in CHAT mode
- Add HttpxAuthNoiseFilter to suppress auth endpoint logging
- Fix notification static_schema import path
- Add test coverage for notification bloc error handling
- Update AGENTS.md page header rules and image handling
- Delete deprecated run-dev.sh script
This commit is contained in:
qzl
2026-04-13 14:52:22 +08:00
parent da947f9f08
commit 1e22f27de2
52 changed files with 1419 additions and 307 deletions
@@ -0,0 +1,19 @@
import 'divination_params.dart';
class YaoCoinConverter {
const YaoCoinConverter._();
static YaoType fromHuaCount(int huaCount) {
return switch (huaCount) {
0 => YaoType.oldYin,
1 => YaoType.youngYang,
2 => YaoType.youngYin,
3 => YaoType.oldYang,
_ => throw ArgumentError.value(huaCount, 'huaCount', 'must be 0..3'),
};
}
static YaoType fromZiCount(int ziCount) {
return fromHuaCount(3 - ziCount);
}
}
@@ -14,7 +14,6 @@ import '../../../../shared/widgets/app_modal_dialog.dart';
import '../../../../shared/widgets/gua_icon.dart';
import '../../../../shared/widgets/divination/divination_shared_widgets.dart';
import '../../../../shared/widgets/divination/divination_terms.dart';
import '../../../../shared/widgets/divination/yao_legend.dart';
import '../../../../shared/widgets/divination/yao_line_row.dart';
import '../../../../shared/widgets/date_time_picker/date_time_picker_bottom_sheet.dart';
import '../../../../shared/widgets/toast/toast.dart';
@@ -23,6 +22,7 @@ import '../../data/models/divination_backend_models.dart';
import '../../data/apis/divination_api.dart';
import '../../data/models/divination_params.dart';
import '../../data/models/divination_result.dart';
import '../../data/models/yao_coin_converter.dart';
import '../../data/services/divination_run_service.dart';
import 'divination_processing_screen.dart';
@@ -287,14 +287,8 @@ class _AutoDivinationScreenState extends State<AutoDivinationScreen>
final c1 = _random.nextBool();
final c2 = _random.nextBool();
final c3 = _random.nextBool();
final yangCount = [c1, c2, c3].where((v) => v).length;
final yao = switch (yangCount) {
0 => YaoType.oldYin,
1 => YaoType.youngYang,
2 => YaoType.youngYin,
3 => YaoType.oldYang,
_ => YaoType.undetermined,
};
final ziCount = [c1, c2, c3].where((v) => v).length;
final yao = YaoCoinConverter.fromZiCount(ziCount);
setState(() {
_isSpinning = false;
_coin1Yang = c1;
@@ -737,7 +731,6 @@ class _HexagramCard extends StatelessWidget {
const SizedBox(height: AppSpacing.md),
for (int i = 5; i >= 0; i--) _YaoRow(index: i, type: yaoStates[i]),
const SizedBox(height: AppSpacing.xs),
const Align(alignment: Alignment.centerLeft, child: YaoLegend()),
],
),
),
@@ -10,7 +10,6 @@ import '../../../../shared/theme/app_color_palette.dart';
import '../../../../shared/theme/design_tokens.dart';
import '../../../../shared/widgets/divination/divination_terms.dart';
import '../../../../shared/widgets/divination/yao_glyph.dart';
import '../../../../shared/widgets/divination/yao_legend.dart';
import '../../../../shared/widgets/toast/toast.dart';
import '../../../../shared/widgets/toast/toast_type.dart';
import '../../data/apis/divination_api.dart';
@@ -926,11 +925,6 @@ class _HexagramDetailCard extends StatelessWidget {
showTarget:
data.hasChangingYao && idx < data.targetYaoLines.length,
),
const SizedBox(height: AppSpacing.sm),
const Align(
alignment: Alignment.centerLeft,
child: YaoLegend(),
),
],
),
),
@@ -1156,7 +1150,10 @@ class _YaoDetailRow extends StatelessWidget {
),
SizedBox(
width: 28,
child: Text(data.relation, textAlign: TextAlign.center),
child: Text(
_abbreviateRelation(data.relation),
textAlign: TextAlign.center,
),
),
SizedBox(
width: 18,
@@ -1183,4 +1180,15 @@ class _YaoDetailRow extends StatelessWidget {
String _changeMark(YaoType type) {
return type.changeMark;
}
String _abbreviateRelation(String relation) {
return switch (relation) {
'子孙' => '',
'妻财' => '',
'官鬼' => '',
'兄弟' => '',
'父母' => '',
_ => relation,
};
}
}
@@ -11,7 +11,6 @@ import '../../../../shared/widgets/app_modal_dialog.dart';
import '../../../../shared/widgets/gua_icon.dart';
import '../../../../shared/widgets/divination/divination_shared_widgets.dart';
import '../../../../shared/widgets/divination/divination_terms.dart';
import '../../../../shared/widgets/divination/yao_legend.dart';
import '../../../../shared/widgets/divination/yao_line_row.dart';
import '../../../../shared/widgets/date_time_picker/date_time_picker_bottom_sheet.dart';
import '../../../../shared/widgets/toast/toast.dart';
@@ -20,6 +19,7 @@ import '../../data/models/divination_backend_models.dart';
import '../../data/apis/divination_api.dart';
import '../../data/models/divination_params.dart';
import '../../data/models/divination_result.dart';
import '../../data/models/yao_coin_converter.dart';
import '../../data/services/divination_run_service.dart';
import 'divination_processing_screen.dart';
@@ -524,7 +524,6 @@ class _YaoSelectionCard extends StatelessWidget {
);
}),
const SizedBox(height: AppSpacing.xs),
const Align(alignment: Alignment.centerLeft, child: YaoLegend()),
],
),
),
@@ -565,13 +564,7 @@ class _ThreeCoinSelectorDialogState extends State<_ThreeCoinSelectorDialog> {
YaoType get _currentYaoType {
final huaCount = _coinStates.where((isHua) => isHua).length;
return switch (huaCount) {
0 => YaoType.oldYin,
1 => YaoType.youngYang,
2 => YaoType.youngYin,
3 => YaoType.oldYang,
_ => YaoType.undetermined,
};
return YaoCoinConverter.fromHuaCount(huaCount);
}
void _toggleCoin(int index) {