update im ack

Signed-off-by: dom <githubaccount56556@proton.me>
This commit is contained in:
dom
2026-07-16 12:13:44 +08:00
parent a12223b42b
commit f7cd1aa502
3 changed files with 109 additions and 50 deletions

View File

@@ -1,4 +1,4 @@
import 'package:PiliPlus/grpc/bilibili/app/im/v1.pb.dart';
import 'package:PiliPlus/grpc/bilibili/app/im/v1.pb.dart' hide SessionInfo;
import 'package:PiliPlus/grpc/bilibili/im/interfaces/v1.pb.dart';
import 'package:PiliPlus/grpc/bilibili/im/type.pb.dart';
import 'package:PiliPlus/grpc/grpc_req.dart';
@@ -212,4 +212,16 @@ abstract final class ImGrpc {
RspTotalUnread.fromBuffer,
);
}
static Future<LoadingState<SessionInfo>> sessionDetail({
Int64? talkerId,
int? sessionType,
Int64? uid,
}) {
return GrpcReq.request(
GrpcUrl.sessionDetail,
ReqSessionDetail(talkerId: talkerId, sessionType: sessionType, uid: uid),
SessionInfo.fromBuffer,
);
}
}

View File

@@ -44,6 +44,7 @@ abstract final class GrpcUrl {
static const keywordBlockingDelete = '$im2/KeywordBlockingDelete';
static const syncFetchSessionMsgs = '$im/SyncFetchSessionMsgs';
static const getTotalUnread = '$im/GetTotalUnread';
static const sessionDetail = '$im/SessionDetail';
// view
static const viewunite = '/bilibili.app.viewunite.v1.View';

View File

@@ -7,8 +7,10 @@ import 'package:PiliPlus/common/widgets/dialog/simple_dialog_option.dart';
import 'package:PiliPlus/common/widgets/flutter/list_tile.dart';
import 'package:PiliPlus/common/widgets/pendant_avatar.dart';
import 'package:PiliPlus/grpc/bilibili/app/im/v1.pb.dart'
show Session, SessionId, SessionPageType, SessionType, UnreadStyle;
import 'package:PiliPlus/models/common/badge_type.dart';
show Session, SessionId, SessionPageType;
import 'package:PiliPlus/grpc/im.dart';
import 'package:PiliPlus/http/loading_state.dart';
import 'package:PiliPlus/http/msg.dart';
import 'package:PiliPlus/pages/whisper_secondary/view.dart';
import 'package:PiliPlus/utils/date_utils.dart';
import 'package:PiliPlus/utils/extension/num_ext.dart';
@@ -16,6 +18,7 @@ import 'package:PiliPlus/utils/extension/theme_ext.dart';
import 'package:PiliPlus/utils/page_utils.dart';
import 'package:PiliPlus/utils/platform_utils.dart';
import 'package:fixnum/fixnum.dart';
import 'package:flutter/foundation.dart' show kDebugMode;
import 'package:flutter/material.dart' hide ListTile;
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:get/get.dart';
@@ -34,6 +37,28 @@ class WhisperSessionItem extends StatelessWidget {
final Function(bool isMuted, Int64 talkerUid) onSetMute;
final ValueChanged<int> onRemove;
Future<void> _updateAck(BuildContext context) async {
final talkerUid = item.id.privateId.talkerUid;
final res = await ImGrpc.sessionDetail(talkerId: talkerUid, sessionType: 1);
if (res case Success(:final response)) {
final res = await MsgHttp.ackSessionMsg(
talkerId: talkerUid.toInt(),
ackSeqno: response.ackSeqno.toInt(),
);
if (res.isSuccess) {
SmartDialog.showToast('已标为已读');
item.clearUnread();
if (context.mounted) {
(context as Element).markNeedsBuild();
}
} else {
res.toast();
}
} else {
res.toast();
}
}
@override
Widget build(BuildContext context) {
final resource =
@@ -46,7 +71,8 @@ class WhisperSessionItem extends StatelessWidget {
Map? vipInfo = item.sessionInfo.hasVipInfo()
? jsonDecode(item.sessionInfo.vipInfo)
: null;
final ThemeData theme = Theme.of(context);
final theme = Theme.of(context);
return ListTile(
safeArea: true,
@@ -57,9 +83,9 @@ class WhisperSessionItem extends StatelessWidget {
: null,
onLongPress: () => showDialog(
context: context,
builder: (context) => SimpleDialog(
clipBehavior: Clip.hardEdge,
contentPadding: const EdgeInsets.symmetric(vertical: 12),
builder: (_) => SimpleDialog(
clipBehavior: .hardEdge,
contentPadding: const .symmetric(vertical: 12),
children: [
DialogOption(
onPressed: () {
@@ -68,7 +94,15 @@ class WhisperSessionItem extends StatelessWidget {
},
child: Text(item.isPinned ? '移除置顶' : '置顶'),
),
if (item.id.privateId.hasTalkerUid())
if (item.id.privateId.hasTalkerUid()) ...[
if (kDebugMode || item.hasUnread())
DialogOption(
onPressed: () {
Get.back();
_updateAck(context);
},
child: const Text('标为已读'),
),
DialogOption(
onPressed: () {
Get.back();
@@ -76,7 +110,6 @@ class WhisperSessionItem extends StatelessWidget {
},
child: Text('${item.isMuted ? '关闭' : '开启'}免打扰'),
),
if (item.id.privateId.hasTalkerUid())
DialogOption(
onPressed: () {
Get.back();
@@ -89,6 +122,7 @@ class WhisperSessionItem extends StatelessWidget {
},
child: const Text('删除'),
),
],
],
),
),
@@ -96,20 +130,38 @@ class WhisperSessionItem extends StatelessWidget {
? (details) => showMenu(
context: context,
position: PageUtils.menuPosition(details.globalPosition),
items: [
items: <PopupMenuEntry<Never>>[
PopupMenuItem(
height: 42,
onTap: () => onSetTop(item.isPinned, item.id),
child: Text(item.isPinned ? '移除置顶' : '置顶'),
),
if (item.id.privateId.hasTalkerUid())
if (item.id.privateId.hasTalkerUid()) ...[
if (kDebugMode || item.hasUnread())
PopupMenuItem(
height: 42,
onTap: () => _updateAck(context),
child: const Text('标为已读'),
),
// if (kDebugMode)
// PopupMenuItem(
// height: 42,
// onTap: () {
// item.unread = Unread(
// style: .UNREAD_STYLE_NUMBER,
// number: .ONE,
// );
// (context as Element).markNeedsBuild();
// },
// child: const Text('标为未读'),
// ),
PopupMenuItem(
height: 42,
onTap: () =>
onSetMute(item.isMuted, item.id.privateId.talkerUid),
child: Text('${item.isMuted ? '关闭' : '开启'}免打扰'),
),
if (item.id.privateId.hasTalkerUid())
const PopupMenuDivider(height: 10),
PopupMenuItem(
height: 42,
onTap: () => showConfirmDialog(
@@ -118,8 +170,12 @@ class WhisperSessionItem extends StatelessWidget {
onConfirm: () =>
onRemove(item.id.privateId.talkerUid.toInt()),
),
child: const Text('删除'),
child: Text(
'删除',
style: TextStyle(color: theme.colorScheme.error),
),
),
],
],
)
: null,
@@ -147,24 +203,15 @@ class WhisperSessionItem extends StatelessWidget {
if (item.id.foldId.hasType()) {
SessionPageType? sessionPageType = switch (item.id.foldId.type) {
SessionType.SESSION_TYPE_UNKNOWN =>
SessionPageType.SESSION_PAGE_TYPE_UNKNOWN,
SessionType.SESSION_TYPE_GROUP =>
SessionPageType.SESSION_PAGE_TYPE_GROUP,
SessionType.SESSION_TYPE_GROUP_FOLD =>
SessionPageType.SESSION_PAGE_TYPE_GROUP,
SessionType.SESSION_TYPE_UNFOLLOWED =>
SessionPageType.SESSION_PAGE_TYPE_UNFOLLOWED,
SessionType.SESSION_TYPE_STRANGER =>
SessionPageType.SESSION_PAGE_TYPE_STRANGER,
SessionType.SESSION_TYPE_DUSTBIN =>
SessionPageType.SESSION_PAGE_TYPE_DUSTBIN,
SessionType.SESSION_TYPE_CUSTOMER_FOLD =>
SessionPageType.SESSION_PAGE_TYPE_CUSTOMER,
SessionType.SESSION_TYPE_AI_FOLD =>
SessionPageType.SESSION_PAGE_TYPE_AI,
SessionType.SESSION_TYPE_CUSTOMER_ACCOUNT =>
SessionPageType.SESSION_PAGE_TYPE_CUSTOMER,
.SESSION_TYPE_UNKNOWN => .SESSION_PAGE_TYPE_UNKNOWN,
.SESSION_TYPE_GROUP => .SESSION_PAGE_TYPE_GROUP,
.SESSION_TYPE_GROUP_FOLD => .SESSION_PAGE_TYPE_GROUP,
.SESSION_TYPE_UNFOLLOWED => .SESSION_PAGE_TYPE_UNFOLLOWED,
.SESSION_TYPE_STRANGER => .SESSION_PAGE_TYPE_STRANGER,
.SESSION_TYPE_DUSTBIN => .SESSION_PAGE_TYPE_DUSTBIN,
.SESSION_TYPE_CUSTOMER_FOLD => .SESSION_PAGE_TYPE_CUSTOMER,
.SESSION_TYPE_AI_FOLD => .SESSION_PAGE_TYPE_AI,
.SESSION_TYPE_CUSTOMER_ACCOUNT => .SESSION_PAGE_TYPE_CUSTOMER,
_ => null,
};
if (sessionPageType != null) {
@@ -182,18 +229,18 @@ class WhisperSessionItem extends StatelessWidget {
if (item.id.hasSystemId()) {
switch (item.id.systemId.type) {
case SessionType.SESSION_TYPE_SYSTEM:
case .SESSION_TYPE_SYSTEM:
Get.toNamed('/sysMsg');
case SessionType.SESSION_TYPE_AI_FOLD:
case SessionType.SESSION_TYPE_CUSTOMER_ACCOUNT:
case SessionType.SESSION_TYPE_CUSTOMER_FOLD:
case SessionType.SESSION_TYPE_DUSTBIN:
case SessionType.SESSION_TYPE_GROUP:
case SessionType.SESSION_TYPE_GROUP_FOLD:
case SessionType.SESSION_TYPE_PRIVATE:
case SessionType.SESSION_TYPE_STRANGER:
case SessionType.SESSION_TYPE_UNFOLLOWED:
case SessionType.SESSION_TYPE_UNKNOWN:
case .SESSION_TYPE_AI_FOLD:
case .SESSION_TYPE_CUSTOMER_ACCOUNT:
case .SESSION_TYPE_CUSTOMER_FOLD:
case .SESSION_TYPE_DUSTBIN:
case .SESSION_TYPE_GROUP:
case .SESSION_TYPE_GROUP_FOLD:
case .SESSION_TYPE_PRIVATE:
case .SESSION_TYPE_STRANGER:
case .SESSION_TYPE_UNFOLLOWED:
case .SESSION_TYPE_UNKNOWN:
SmartDialog.showToast(item.id.systemId.type.name);
}
}
@@ -248,7 +295,7 @@ class WhisperSessionItem extends StatelessWidget {
child: Text(
item.sessionInfo.sessionName,
maxLines: 1,
overflow: TextOverflow.ellipsis,
overflow: .ellipsis,
style: TextStyle(
fontSize: 15,
color:
@@ -263,8 +310,8 @@ class WhisperSessionItem extends StatelessWidget {
if (item.sessionInfo.userLabel.style.borderedLabel.hasText())
PBadge(
isStack: false,
type: PBadgeType.line_secondary,
size: PBadgeSize.small,
type: .line_secondary,
size: .small,
fontSize: 10,
isBold: false,
text: item.sessionInfo.userLabel.style.borderedLabel.text,
@@ -274,7 +321,7 @@ class WhisperSessionItem extends StatelessWidget {
Assets.livingRect,
height: 15,
cacheHeight: 15.cacheSize(context),
filterQuality: FilterQuality.low,
filterQuality: .low,
),
],
),
@@ -295,7 +342,7 @@ class WhisperSessionItem extends StatelessWidget {
child: Text(
item.msgSummary.rawMsg,
maxLines: 1,
overflow: TextOverflow.ellipsis,
overflow: .ellipsis,
style: theme.textTheme.labelMedium!.copyWith(
color: theme.colorScheme.outline,
),
@@ -307,10 +354,9 @@ class WhisperSessionItem extends StatelessWidget {
Icons.notifications_off,
color: theme.colorScheme.outline,
)
else if (item.hasUnread() &&
item.unread.style != UnreadStyle.UNREAD_STYLE_NONE)
else if (item.hasUnread() && item.unread.style != .UNREAD_STYLE_NONE)
Badge(
label: item.unread.style == UnreadStyle.UNREAD_STYLE_NUMBER
label: item.unread.style == .UNREAD_STYLE_NUMBER
? Text(item.unread.number.toString())
: null,
),