opt: msg btn

Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
This commit is contained in:
bggRGjQaUbCoE
2025-05-10 00:28:09 +08:00
parent 9e4bc24365
commit 0510fbb65a
5 changed files with 223 additions and 144 deletions

View File

@@ -1,5 +1,11 @@
import 'package:PiliPlus/grpc/bilibili/app/im/v1.pb.dart' import 'package:PiliPlus/grpc/bilibili/app/im/v1.pb.dart'
show Offset, Session, SessionId, SessionMainReply, SessionPageType; show
Offset,
Session,
SessionId,
SessionMainReply,
SessionPageType,
ThreeDotItem;
import 'package:PiliPlus/grpc/im.dart'; import 'package:PiliPlus/grpc/im.dart';
import 'package:PiliPlus/http/loading_state.dart'; import 'package:PiliPlus/http/loading_state.dart';
import 'package:PiliPlus/http/msg.dart'; import 'package:PiliPlus/http/msg.dart';
@@ -18,6 +24,9 @@ class WhisperController
PbMap<int, Offset>? offset; PbMap<int, Offset>? offset;
Rx<List<ThreeDotItem>?> threeDotItems = Rx<List<ThreeDotItem>?>(null);
Rx<List<ThreeDotItem>?> outsideItem = Rx<List<ThreeDotItem>?>(null);
@override @override
void onInit() { void onInit() {
super.onInit(); super.onInit();
@@ -72,6 +81,16 @@ class WhisperController
return response.sessions; return response.sessions;
} }
@override
bool customHandleResponse(
bool isRefresh, Success<SessionMainReply> response) {
if (isRefresh) {
threeDotItems.value = response.response.threeDotItems;
outsideItem.value = response.response.outsideItem;
}
return false;
}
@override @override
Future<LoadingState<SessionMainReply>> customGetData() => Future<LoadingState<SessionMainReply>> customGetData() =>
ImGrpc.sessionMain(offset: offset); ImGrpc.sessionMain(offset: offset);
@@ -140,4 +159,14 @@ class WhisperController
SmartDialog.showToast(res['msg']); SmartDialog.showToast(res['msg']);
} }
} }
Future<void> onDeleteList() async {
var res = await ImGrpc.deleteSessionList(
pageType: SessionPageType.SESSION_PAGE_TYPE_HOME);
if (res['status']) {
loadingState.value = LoadingState.success(null);
} else {
SmartDialog.showToast(res['msg']);
}
}
} }

View File

@@ -1,13 +1,11 @@
import 'package:PiliPlus/common/skeleton/whisper_item.dart'; import 'package:PiliPlus/common/skeleton/whisper_item.dart';
import 'package:PiliPlus/common/widgets/dialog/dialog.dart';
import 'package:PiliPlus/common/widgets/loading_widget/http_error.dart'; import 'package:PiliPlus/common/widgets/loading_widget/http_error.dart';
import 'package:PiliPlus/common/widgets/refresh_indicator.dart'; import 'package:PiliPlus/common/widgets/refresh_indicator.dart';
import 'package:PiliPlus/grpc/bilibili/app/im/v1.pb.dart'; import 'package:PiliPlus/grpc/bilibili/app/im/v1.pb.dart';
import 'package:PiliPlus/http/loading_state.dart'; import 'package:PiliPlus/http/loading_state.dart';
import 'package:PiliPlus/pages/contact/view.dart';
import 'package:PiliPlus/pages/whisper/controller.dart'; import 'package:PiliPlus/pages/whisper/controller.dart';
import 'package:PiliPlus/pages/whisper/widgets/item.dart'; import 'package:PiliPlus/pages/whisper/widgets/item.dart';
import 'package:PiliPlus/pages/whisper_settings/view.dart'; import 'package:PiliPlus/utils/extension.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
@@ -20,7 +18,7 @@ class WhisperPage extends StatefulWidget {
} }
class _WhisperPageState extends State<WhisperPage> { class _WhisperPageState extends State<WhisperPage> {
final _whisperController = Get.put(WhisperController()); final _controller = Get.put(WhisperController());
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@@ -28,83 +26,70 @@ class _WhisperPageState extends State<WhisperPage> {
appBar: AppBar( appBar: AppBar(
title: const Text('消息'), title: const Text('消息'),
actions: [ actions: [
IconButton( Obx(() {
tooltip: '一键已读', if (_controller.outsideItem.value?.isNotEmpty == true) {
onPressed: () { return Row(
showConfirmDialog( children: _controller.outsideItem.value!.map((e) {
context: context, return IconButton(
title: '一键已读', tooltip: e.hasTitle() ? e.title : null,
content: '是否清除全部新消息提醒?', onPressed: () {
onConfirm: _whisperController.onClearUnread, e.type.action(
context: context,
onConfirm: () {
switch (e.type) {
case ThreeDotItemType.THREE_DOT_ITEM_TYPE_READ_ALL:
_controller.onClearUnread();
case ThreeDotItemType
.THREE_DOT_ITEM_TYPE_CLEAR_LIST:
_controller.onDeleteList();
default:
}
});
},
icon: Icon(size: 20, e.type.icon),
);
}).toList());
}
return const SizedBox.shrink();
}),
Obx(() {
if (_controller.threeDotItems.value?.isNotEmpty == true) {
return PopupMenuButton(
itemBuilder: (context) {
return _controller.threeDotItems.value!
.map((e) => PopupMenuItem(
onTap: () {
e.type.action(
context: context,
onConfirm: () {
switch (e.type) {
case ThreeDotItemType
.THREE_DOT_ITEM_TYPE_READ_ALL:
_controller.onClearUnread();
case ThreeDotItemType
.THREE_DOT_ITEM_TYPE_CLEAR_LIST:
_controller.onDeleteList();
default:
}
});
},
child: Row(
children: [
Icon(size: 17, e.type.icon),
Text(' ${e.title}'),
],
),
))
.toList();
},
); );
}, }
icon: const Icon( return const SizedBox.shrink();
size: 20, }),
Icons.cleaning_services,
),
),
PopupMenuButton(
itemBuilder: (context) {
return [
PopupMenuItem(
onTap: () {
Get.to(const WhisperSettingsPage(
imSettingType: IMSettingType.SETTING_TYPE_NEED_ALL,
));
},
child: const Row(
children: [
Icon(size: 19, Icons.settings),
Text(' 消息设置'),
],
),
),
PopupMenuItem(
onTap: () {
Get.toNamed(
'/whisperDetail',
parameters: {
'talkerId': '844424930131966',
'name': 'UP主小助手',
'face':
'https://message.biliimg.com/bfs/im/489a63efadfb202366c2f88853d2217b5ddc7a13.png',
},
);
},
child: const Row(
children: [
Icon(size: 18, Icons.live_tv),
Text(' UP主小助手'),
],
),
),
// PopupMenuItem(
// onTap: () {},
// child: const Row(
// children: [
// Icon(size: 19, Icons.notifications_none),
// Text(' 应援团消息助手'),
// ],
// ),
// ),
PopupMenuItem(
onTap: () {
Get.to(const ContactPage(isFromSelct: false));
},
child: const Row(
children: [
Icon(size: 19, Icons.account_box_outlined),
Text(' 通讯录'),
],
),
),
];
},
),
], ],
), ),
body: refreshIndicator( body: refreshIndicator(
onRefresh: _whisperController.onRefresh, onRefresh: _controller.onRefresh,
child: CustomScrollView( child: CustomScrollView(
physics: const AlwaysScrollableScrollPhysics(), physics: const AlwaysScrollableScrollPhysics(),
slivers: [ slivers: [
@@ -113,8 +98,7 @@ class _WhisperPageState extends State<WhisperPage> {
padding: EdgeInsets.only( padding: EdgeInsets.only(
bottom: MediaQuery.paddingOf(context).bottom + 100, bottom: MediaQuery.paddingOf(context).bottom + 100,
), ),
sliver: sliver: Obx(() => _buildBody(_controller.loadingState.value)),
Obx(() => _buildBody(_whisperController.loadingState.value)),
), ),
], ],
), ),
@@ -135,15 +119,14 @@ class _WhisperPageState extends State<WhisperPage> {
itemCount: loadingState.response!.length, itemCount: loadingState.response!.length,
itemBuilder: (context, index) { itemBuilder: (context, index) {
if (index == loadingState.response!.length - 1) { if (index == loadingState.response!.length - 1) {
_whisperController.onLoadMore(); _controller.onLoadMore();
} }
return WhisperSessionItem( return WhisperSessionItem(
item: loadingState.response![index], item: loadingState.response![index],
onSetTop: (isTop, id) => onSetTop: (isTop, id) =>
_whisperController.onSetTop(index, isTop, id), _controller.onSetTop(index, isTop, id),
onRemove: (talkerId) => onRemove: (talkerId) => _controller.onRemove(index, talkerId),
_whisperController.onRemove(index, talkerId), onTap: () => _controller.onTap(index),
onTap: () => _whisperController.onTap(index),
); );
}, },
separatorBuilder: (context, index) => Divider( separatorBuilder: (context, index) => Divider(
@@ -154,11 +137,11 @@ class _WhisperPageState extends State<WhisperPage> {
), ),
) )
: HttpError( : HttpError(
onReload: _whisperController.onReload, onReload: _controller.onReload,
), ),
Error() => HttpError( Error() => HttpError(
errMsg: loadingState.errMsg, errMsg: loadingState.errMsg,
onReload: _whisperController.onReload, onReload: _controller.onReload,
), ),
}; };
} }
@@ -169,8 +152,8 @@ class _WhisperPageState extends State<WhisperPage> {
sliver: SliverToBoxAdapter( sliver: SliverToBoxAdapter(
child: Row( child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly, mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: List.generate(_whisperController.msgFeedTopItems.length, children:
(index) { List.generate(_controller.msgFeedTopItems.length, (index) {
final ThemeData theme = Theme.of(context); final ThemeData theme = Theme.of(context);
return GestureDetector( return GestureDetector(
behavior: HitTestBehavior.opaque, behavior: HitTestBehavior.opaque,
@@ -183,16 +166,14 @@ class _WhisperPageState extends State<WhisperPage> {
children: [ children: [
Obx( Obx(
() => Badge( () => Badge(
isLabelVisible: isLabelVisible: _controller.unreadCounts[index] > 0,
_whisperController.unreadCounts[index] > 0, label: Text(" ${_controller.unreadCounts[index]} "),
label: Text(
" ${_whisperController.unreadCounts[index]} "),
alignment: Alignment.topRight, alignment: Alignment.topRight,
child: CircleAvatar( child: CircleAvatar(
radius: 22, radius: 22,
backgroundColor: theme.colorScheme.onInverseSurface, backgroundColor: theme.colorScheme.onInverseSurface,
child: Icon( child: Icon(
_whisperController.msgFeedTopItems[index]['icon'], _controller.msgFeedTopItems[index]['icon'],
size: 20, size: 20,
color: theme.colorScheme.primary, color: theme.colorScheme.primary,
), ),
@@ -201,20 +182,20 @@ class _WhisperPageState extends State<WhisperPage> {
), ),
const SizedBox(height: 6), const SizedBox(height: 6),
Text( Text(
_whisperController.msgFeedTopItems[index]['name'], _controller.msgFeedTopItems[index]['name'],
style: const TextStyle(fontSize: 13), style: const TextStyle(fontSize: 13),
), ),
], ],
), ),
), ),
onTap: () { onTap: () {
if (!_whisperController.msgFeedTopItems[index]['enabled']) { if (!_controller.msgFeedTopItems[index]['enabled']) {
SmartDialog.showToast('已禁用'); SmartDialog.showToast('已禁用');
return; return;
} }
_whisperController.unreadCounts[index] = 0; _controller.unreadCounts[index] = 0;
Get.toNamed( Get.toNamed(
_whisperController.msgFeedTopItems[index]['route'], _controller.msgFeedTopItems[index]['route'],
); );
}, },
); );

View File

@@ -1,10 +1,17 @@
import 'package:PiliPlus/grpc/bilibili/app/im/v1.pb.dart' import 'package:PiliPlus/grpc/bilibili/app/im/v1.pb.dart'
show Offset, Session, SessionId, SessionPageType, SessionSecondaryReply; show
Offset,
Session,
SessionId,
SessionPageType,
SessionSecondaryReply,
ThreeDotItem;
import 'package:PiliPlus/grpc/im.dart'; import 'package:PiliPlus/grpc/im.dart';
import 'package:PiliPlus/http/loading_state.dart'; import 'package:PiliPlus/http/loading_state.dart';
import 'package:PiliPlus/http/msg.dart'; import 'package:PiliPlus/http/msg.dart';
import 'package:PiliPlus/pages/common/common_list_controller.dart'; import 'package:PiliPlus/pages/common/common_list_controller.dart';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:get/get.dart';
import 'package:protobuf/protobuf.dart' show PbMap; import 'package:protobuf/protobuf.dart' show PbMap;
class WhisperSecController class WhisperSecController
@@ -15,6 +22,7 @@ class WhisperSecController
PbMap<int, Offset>? offset; PbMap<int, Offset>? offset;
final SessionPageType sessionPageType; final SessionPageType sessionPageType;
Rx<List<ThreeDotItem>?> threeDotItems = Rx<List<ThreeDotItem>?>(null);
@override @override
void onInit() { void onInit() {
@@ -33,9 +41,19 @@ class WhisperSecController
if (response.paginationParams.hasMore == false) { if (response.paginationParams.hasMore == false) {
isEnd = true; isEnd = true;
} }
return response.sessions; return response.sessions;
} }
@override
bool customHandleResponse(
bool isRefresh, Success<SessionSecondaryReply> response) {
if (isRefresh) {
threeDotItems.value = response.response.threeDotItems;
}
return false;
}
@override @override
Future<LoadingState<SessionSecondaryReply>> customGetData() => Future<LoadingState<SessionSecondaryReply>> customGetData() =>
ImGrpc.sessionSecondary( ImGrpc.sessionSecondary(

View File

@@ -1,11 +1,11 @@
import 'package:PiliPlus/common/skeleton/whisper_item.dart'; import 'package:PiliPlus/common/skeleton/whisper_item.dart';
import 'package:PiliPlus/common/widgets/dialog/dialog.dart';
import 'package:PiliPlus/common/widgets/loading_widget/http_error.dart'; import 'package:PiliPlus/common/widgets/loading_widget/http_error.dart';
import 'package:PiliPlus/common/widgets/refresh_indicator.dart'; import 'package:PiliPlus/common/widgets/refresh_indicator.dart';
import 'package:PiliPlus/grpc/bilibili/app/im/v1.pb.dart'; import 'package:PiliPlus/grpc/bilibili/app/im/v1.pb.dart';
import 'package:PiliPlus/http/loading_state.dart'; import 'package:PiliPlus/http/loading_state.dart';
import 'package:PiliPlus/pages/whisper/widgets/item.dart'; import 'package:PiliPlus/pages/whisper/widgets/item.dart';
import 'package:PiliPlus/pages/whisper_secondary/controller.dart'; import 'package:PiliPlus/pages/whisper_secondary/controller.dart';
import 'package:PiliPlus/utils/extension.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
@@ -35,50 +35,40 @@ class _WhisperSecPageState extends State<WhisperSecPage> {
appBar: AppBar( appBar: AppBar(
title: Text(widget.name), title: Text(widget.name),
actions: [ actions: [
PopupMenuButton( Obx(() {
itemBuilder: (context) { if (_controller.threeDotItems.value?.isNotEmpty == true) {
return [ return PopupMenuButton(
PopupMenuItem( itemBuilder: (context) {
onTap: () { return _controller.threeDotItems.value!
showConfirmDialog( .map((e) => PopupMenuItem(
context: context, onTap: () {
title: '一键已读', e.type.action(
content: '是否清除全部新消息提醒?', context: context,
onConfirm: _controller.onClearUnread, onConfirm: () {
); switch (e.type) {
}, case ThreeDotItemType
child: const Row( .THREE_DOT_ITEM_TYPE_READ_ALL:
children: [ _controller.onClearUnread();
Icon( case ThreeDotItemType
size: 17, .THREE_DOT_ITEM_TYPE_CLEAR_LIST:
Icons.cleaning_services, _controller.onDeleteList();
), default:
Text(' 一键已读'), }
], });
), },
), child: Row(
PopupMenuItem( children: [
onTap: () { Icon(size: 17, e.type.icon),
showConfirmDialog( Text(' ${e.title}'),
context: context, ],
title: '清空列表', ),
content: '清空后所有消息将被删除,无法恢复', ))
onConfirm: _controller.onDeleteList, .toList();
); },
}, );
child: const Row( }
children: [ return const SizedBox.shrink();
Icon( }),
size: 19,
Icons.delete_forever_outlined,
),
Text(' 清空列表'),
],
),
),
];
},
),
], ],
), ),
body: refreshIndicator( body: refreshIndicator(

View File

@@ -1,9 +1,16 @@
import 'package:PiliPlus/common/widgets/dialog/dialog.dart';
import 'package:PiliPlus/common/widgets/interactiveviewer_gallery/hero_dialog_route.dart'; import 'package:PiliPlus/common/widgets/interactiveviewer_gallery/hero_dialog_route.dart';
import 'package:PiliPlus/common/widgets/interactiveviewer_gallery/interactiveviewer_gallery.dart'; import 'package:PiliPlus/common/widgets/interactiveviewer_gallery/interactiveviewer_gallery.dart';
import 'package:PiliPlus/grpc/bilibili/app/im/v1.pbenum.dart'
show IMSettingType, ThreeDotItemType;
import 'package:PiliPlus/models/common/image_preview_type.dart'; import 'package:PiliPlus/models/common/image_preview_type.dart';
import 'package:PiliPlus/pages/contact/view.dart';
import 'package:PiliPlus/pages/whisper_settings/view.dart';
import 'package:floating/floating.dart'; import 'package:floating/floating.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:material_design_icons_flutter/material_design_icons_flutter.dart';
extension ImageExtension on num? { extension ImageExtension on num? {
int? cacheSize(BuildContext context) { int? cacheSize(BuildContext context) {
@@ -150,3 +157,57 @@ extension RationalExt on Rational {
return (min <= aspectRatio) && (aspectRatio <= max); return (min <= aspectRatio) && (aspectRatio <= max);
} }
} }
extension ThreeDotItemTypeExt on ThreeDotItemType {
IconData get icon => switch (this) {
ThreeDotItemType.THREE_DOT_ITEM_TYPE_MSG_SETTING => Icons.settings,
ThreeDotItemType.THREE_DOT_ITEM_TYPE_READ_ALL =>
Icons.cleaning_services,
ThreeDotItemType.THREE_DOT_ITEM_TYPE_CLEAR_LIST =>
Icons.delete_forever_outlined,
ThreeDotItemType.THREE_DOT_ITEM_TYPE_UP_HELPER => Icons.live_tv,
ThreeDotItemType.THREE_DOT_ITEM_TYPE_CONTACTS =>
Icons.account_box_outlined,
ThreeDotItemType.THREE_DOT_ITEM_TYPE_FANS_GROUP_HELPER =>
Icons.notifications_none,
_ => MdiIcons.circleMedium,
};
void action(
{required BuildContext context, required VoidCallback onConfirm}) {
switch (this) {
case ThreeDotItemType.THREE_DOT_ITEM_TYPE_READ_ALL:
showConfirmDialog(
context: context,
title: '一键已读',
content: '是否清除全部新消息提醒?',
onConfirm: onConfirm,
);
case ThreeDotItemType.THREE_DOT_ITEM_TYPE_CLEAR_LIST:
showConfirmDialog(
context: context,
title: '清空列表',
content: '清空后所有消息将被删除,无法恢复',
onConfirm: onConfirm,
);
case ThreeDotItemType.THREE_DOT_ITEM_TYPE_MSG_SETTING:
Get.to(const WhisperSettingsPage(
imSettingType: IMSettingType.SETTING_TYPE_NEED_ALL,
));
case ThreeDotItemType.THREE_DOT_ITEM_TYPE_UP_HELPER:
Get.toNamed(
'/whisperDetail',
parameters: {
'talkerId': '844424930131966',
'name': 'UP主小助手',
'face':
'https://message.biliimg.com/bfs/im/489a63efadfb202366c2f88853d2217b5ddc7a13.png',
},
);
case ThreeDotItemType.THREE_DOT_ITEM_TYPE_CONTACTS:
Get.to(const ContactPage(isFromSelct: false));
default:
SmartDialog.showToast(name);
}
}
}