opt: unread msg

Closes #122

Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
This commit is contained in:
bggRGjQaUbCoE
2025-01-09 12:41:04 +08:00
parent 41dfab371e
commit e8c82f69d4
6 changed files with 129 additions and 33 deletions

View File

@@ -4,8 +4,8 @@ extension DynamicBadgeModeDesc on DynamicBadgeMode {
String get description => ['隐藏', '红点', '数字'][index];
}
enum MsgUnReadType { pm, reply, at, like, sysMsg, all }
enum MsgUnReadType { pm, reply, at, like, sysMsg }
extension MsgUnReadTypeExt on MsgUnReadType {
String get title => ['私信', '回复我的', '@我', '收到的赞', '系统通知', '全部'][index];
String get title => ['私信', '回复我的', '@我', '收到的赞', '系统通知'][index];
}

View File

@@ -35,7 +35,7 @@ class MainController extends GetxController {
late int homeIndex = -1;
late DynamicBadgeMode msgBadgeMode = GStorage.msgBadgeMode;
late MsgUnReadType msgUnReadType = GStorage.msgUnReadType;
late List<MsgUnReadType> msgUnReadTypes = GStorage.msgUnReadTypeV2;
late final RxString msgUnReadCount = ''.obs;
late int lastCheckUnreadAt = 0;
@@ -77,11 +77,14 @@ class MainController extends GetxController {
if (isLogin.value.not || homeIndex == -1) {
return;
}
if (msgUnReadTypes.isEmpty) {
msgUnReadCount.value = '';
return;
}
try {
bool shouldCheckPM = msgUnReadType == MsgUnReadType.pm ||
msgUnReadType == MsgUnReadType.all;
bool shouldCheckFeed = msgUnReadType != MsgUnReadType.pm ||
msgUnReadType == MsgUnReadType.all;
bool shouldCheckPM = msgUnReadTypes.contains(MsgUnReadType.pm);
bool shouldCheckFeed =
([...msgUnReadTypes]..remove(MsgUnReadType.pm)).isNotEmpty;
List res = await Future.wait([
if (shouldCheckPM) _queryPMUnread(),
if (shouldCheckFeed) _queryMsgFeedUnread(),
@@ -93,18 +96,18 @@ class MainController extends GetxController {
if ((shouldCheckPM.not && res.firstOrNull?['status'] == true) ||
(shouldCheckPM && res.getOrNull(1)?['status'] == true)) {
int index = shouldCheckPM.not ? 0 : 1;
count += (switch (msgUnReadType) {
MsgUnReadType.pm => 0,
MsgUnReadType.reply => res[index]['data']['reply'],
MsgUnReadType.at => res[index]['data']['at'],
MsgUnReadType.like => res[index]['data']['like'],
MsgUnReadType.sysMsg => res[index]['data']['sys_msg'],
MsgUnReadType.all => res[index]['data']['reply'] +
res[index]['data']['at'] +
res[index]['data']['like'] +
res[index]['data']['sys_msg'],
} as int?) ??
0;
if (msgUnReadTypes.contains(MsgUnReadType.reply)) {
count += (res[index]['data']['reply'] as int?) ?? 0;
}
if (msgUnReadTypes.contains(MsgUnReadType.at)) {
count += (res[index]['data']['at'] as int?) ?? 0;
}
if (msgUnReadTypes.contains(MsgUnReadType.like)) {
count += (res[index]['data']['like'] as int?) ?? 0;
}
if (msgUnReadTypes.contains(MsgUnReadType.sysMsg)) {
count += (res[index]['data']['sys_msg'] as int?) ?? 0;
}
}
count = count == 0
? ''

View File

@@ -17,6 +17,7 @@ import 'package:PiliPlus/pages/main/controller.dart';
import 'package:PiliPlus/pages/member/new/controller.dart';
import 'package:PiliPlus/pages/mine/controller.dart';
import 'package:PiliPlus/pages/setting/pages/color_select.dart';
import 'package:PiliPlus/pages/setting/widgets/multi_select_dialog.dart';
import 'package:PiliPlus/pages/setting/widgets/normal_item.dart';
import 'package:PiliPlus/pages/setting/widgets/select_dialog.dart';
import 'package:PiliPlus/pages/setting/widgets/slide_dialog.dart';
@@ -286,12 +287,12 @@ List<SettingsModel> get styleSettings => [
SettingsModel(
settingsType: SettingsType.normal,
onTap: (setState) async {
MsgUnReadType? result = await showDialog(
List<MsgUnReadType>? result = await showDialog(
context: Get.context!,
builder: (context) {
return SelectDialog<MsgUnReadType>(
return MultiSelectDialog<MsgUnReadType>(
title: '消息未读类型',
value: GStorage.msgUnReadType,
initValues: GStorage.msgUnReadTypeV2,
values: MsgUnReadType.values.map((e) {
return {'title': e.title, 'value': e};
}).toList(),
@@ -299,9 +300,10 @@ List<SettingsModel> get styleSettings => [
},
);
if (result != null) {
GStorage.setting.put(SettingBoxKey.msgUnReadType, result.index);
GStorage.setting.put(SettingBoxKey.msgUnReadTypeV2,
result.map((item) => item.index).toList()..sort());
MainController mainController = Get.put(MainController());
mainController.msgUnReadType = MsgUnReadType.values[result.index];
mainController.msgUnReadTypes = result;
if (mainController.msgBadgeMode != DynamicBadgeMode.hidden) {
mainController.queryUnreadMsg();
}
@@ -311,7 +313,8 @@ List<SettingsModel> get styleSettings => [
},
title: '消息未读类型',
leading: const Icon(Icons.notifications_active_outlined),
getSubtitle: () => '当前消息类型:${GStorage.msgUnReadType.title}',
getSubtitle: () =>
'当前消息类型:${GStorage.msgUnReadTypeV2.map((item) => item.title).join('')}',
),
SettingsModel(
settingsType: SettingsType.sw1tch,

View File

@@ -0,0 +1,87 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
class MultiSelectDialog<T> extends StatefulWidget {
final List<T> initValues;
final String title;
final List<dynamic> values;
const MultiSelectDialog({
super.key,
required this.initValues,
required this.values,
required this.title,
});
@override
State<MultiSelectDialog<T>> createState() => _MultiSelectDialogState<T>();
}
class _MultiSelectDialogState<T> extends State<MultiSelectDialog<T>> {
late Set<T> _tempValues;
@override
void initState() {
super.initState();
_tempValues = widget.initValues.toSet();
}
@override
Widget build(BuildContext context) {
return AlertDialog(
clipBehavior: Clip.hardEdge,
title: Text(
widget.title,
style: TextStyle(fontSize: 18),
),
contentPadding: const EdgeInsets.only(top: 12),
content: StatefulBuilder(builder: (context, StateSetter setState) {
return SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.min,
children: List.generate(
widget.values.length,
(index) {
bool isChecked =
_tempValues.contains(widget.values[index]['value']);
return CheckboxListTile(
dense: true,
value: isChecked,
controlAffinity: ListTileControlAffinity.leading,
title: Text(
widget.values[index]['title'],
style: Theme.of(context).textTheme.titleMedium!,
),
onChanged: (value) {
if (isChecked) {
_tempValues.remove(widget.values[index]['value']);
} else {
_tempValues.add(widget.values[index]['value']);
}
setState(() {});
},
);
},
),
),
);
}),
actionsPadding: EdgeInsets.only(left: 16, right: 16, bottom: 12),
actions: [
TextButton(
onPressed: Get.back,
child: Text(
'取消',
style: TextStyle(
color: Theme.of(context).colorScheme.outline,
),
),
),
TextButton(
onPressed: () => Get.back(result: _tempValues.toList()),
child: const Text('确定'),
),
],
);
}
}

View File

@@ -93,8 +93,6 @@ class _SelectDialogState<T> extends State<SelectDialog<T>> {
@override
Widget build(BuildContext context) {
TextStyle titleStyle = Theme.of(context).textTheme.titleMedium!;
return AlertDialog(
clipBehavior: Clip.hardEdge,
title: Text(
@@ -111,7 +109,10 @@ class _SelectDialogState<T> extends State<SelectDialog<T>> {
(index) => RadioListTile(
dense: true,
value: widget.values[index]['value'],
title: Text(widget.values[index]['title'], style: titleStyle),
title: Text(
widget.values[index]['title'],
style: Theme.of(context).textTheme.titleMedium!,
),
subtitle: widget.title == 'CDN 设置' && cdnSpeedTest
? Text(
_cdnResList[index] is double

View File

@@ -99,10 +99,11 @@ class GStorage {
defaultValue: DynamicBadgeMode.number.index,
)];
static MsgUnReadType get msgUnReadType => MsgUnReadType.values[setting.get(
SettingBoxKey.msgUnReadType,
defaultValue: MsgUnReadType.pm.index,
)];
static List<MsgUnReadType> get msgUnReadTypeV2 => List<int>.from(setting.get(
SettingBoxKey.msgUnReadTypeV2,
defaultValue:
List<int>.generate(MsgUnReadType.values.length, (index) => index),
)).map((index) => MsgUnReadType.values[index]).toList();
static int get defaultHomePage =>
setting.get(SettingBoxKey.defaultHomePage, defaultValue: 0);
@@ -574,7 +575,8 @@ class SettingBoxKey {
tabbarSort = 'tabbarSort', // 首页tabbar
dynamicBadgeMode = 'dynamicBadgeMode',
msgBadgeMode = 'msgBadgeMode',
msgUnReadType = 'msgUnReadType',
// msgUnReadType = 'msgUnReadType',
msgUnReadTypeV2 = 'msgUnReadTypeV2',
hiddenSettingUnlocked = 'hiddenSettingUnlocked',
enableGradientBg = 'enableGradientBg',
navBarSort = 'navBarSort';