mirror of
https://github.com/HChaZZY/PiliPlus.git
synced 2025-12-25 11:36:45 +08:00
opt: unread & zan grpc & readlist open with browser (#852)
* opt: unread * opt: zan grpc * feat: readlist open with browser
This commit is contained in:
committed by
GitHub
parent
8d34e6f340
commit
72734d4b4e
@@ -3,6 +3,7 @@ import 'package:PiliPlus/common/skeleton/video_card_h.dart';
|
||||
import 'package:PiliPlus/common/widgets/image/network_img_layer.dart';
|
||||
import 'package:PiliPlus/common/widgets/loading_widget/http_error.dart';
|
||||
import 'package:PiliPlus/common/widgets/refresh_indicator.dart';
|
||||
import 'package:PiliPlus/http/constants.dart';
|
||||
import 'package:PiliPlus/http/loading_state.dart';
|
||||
import 'package:PiliPlus/models/common/image_type.dart';
|
||||
import 'package:PiliPlus/models/dynamics/article_list/list.dart';
|
||||
@@ -10,6 +11,7 @@ import 'package:PiliPlus/models/space_article/item.dart';
|
||||
import 'package:PiliPlus/pages/article_list/controller.dart';
|
||||
import 'package:PiliPlus/pages/article_list/widgets/item.dart';
|
||||
import 'package:PiliPlus/utils/grid.dart';
|
||||
import 'package:PiliPlus/utils/page_utils.dart';
|
||||
import 'package:PiliPlus/utils/utils.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
@@ -188,6 +190,16 @@ class _ArticleListPageState extends State<ArticleListPage> {
|
||||
),
|
||||
),
|
||||
),
|
||||
actions: [
|
||||
IconButton(
|
||||
tooltip: '浏览器打开',
|
||||
onPressed: () {
|
||||
PageUtils.inAppWebview(
|
||||
'${HttpString.baseUrl}/read/readlist/rl${_controller.id}');
|
||||
},
|
||||
icon: const Icon(Icons.open_in_browser_outlined, size: 19),
|
||||
)
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:PiliPlus/grpc/dyn.dart';
|
||||
import 'package:PiliPlus/http/api.dart';
|
||||
import 'package:PiliPlus/http/init.dart';
|
||||
import 'package:PiliPlus/grpc/im.dart';
|
||||
import 'package:PiliPlus/models/common/dynamic/dynamic_badge_mode.dart';
|
||||
import 'package:PiliPlus/models/common/msg/msg_unread_type.dart';
|
||||
import 'package:PiliPlus/models/common/nav_bar_config.dart';
|
||||
@@ -83,92 +82,51 @@ class MainController extends GetxController {
|
||||
msgUnReadCount.value = '';
|
||||
return;
|
||||
}
|
||||
try {
|
||||
bool shouldCheckPM = msgUnReadTypes.contains(MsgUnReadType.pm);
|
||||
bool shouldCheckFeed =
|
||||
shouldCheckPM ? msgUnReadTypes.length > 1 : msgUnReadTypes.isNotEmpty;
|
||||
List res = await Future.wait([
|
||||
if (shouldCheckPM) _queryPMUnread(),
|
||||
if (shouldCheckFeed) _queryMsgFeedUnread(),
|
||||
]);
|
||||
dynamic count = 0;
|
||||
if (shouldCheckPM && res.firstOrNull?['status'] == true) {
|
||||
count = (res.first['data'] as int?) ?? 0;
|
||||
}
|
||||
if ((shouldCheckPM.not && res.firstOrNull?['status'] == true) ||
|
||||
(shouldCheckPM && res.getOrNull(1)?['status'] == true)) {
|
||||
int index = shouldCheckPM.not ? 0 : 1;
|
||||
dynamic data = res[index]['data'];
|
||||
|
||||
int count = 0;
|
||||
final res = await ImGrpc.getTotalUnread();
|
||||
if (res.isSuccess) {
|
||||
final data = res.data;
|
||||
if (msgUnReadTypes.length == MsgUnReadType.values.length) {
|
||||
count = data.hasTotalUnread() ? data.totalUnread : 0;
|
||||
} else {
|
||||
final msgUnread = data.msgFeedUnread.unread;
|
||||
for (final item in msgUnReadTypes) {
|
||||
switch (item) {
|
||||
case MsgUnReadType.pm:
|
||||
final pmUnread = data.sessionSingleUnread;
|
||||
count += (pmUnread.followUnread +
|
||||
pmUnread.unfollowUnread +
|
||||
pmUnread.dustbinUnread)
|
||||
.toInt();
|
||||
break;
|
||||
case MsgUnReadType.reply:
|
||||
count += (data['reply'] as int?) ?? 0;
|
||||
count += msgUnread['reply']?.toInt() ?? 0;
|
||||
break;
|
||||
case MsgUnReadType.at:
|
||||
count += (data['at'] as int?) ?? 0;
|
||||
count += msgUnread['at']?.toInt() ?? 0;
|
||||
break;
|
||||
case MsgUnReadType.like:
|
||||
count += (data['like'] as int?) ?? 0;
|
||||
count += msgUnread['like']?.toInt() ?? 0;
|
||||
break;
|
||||
case MsgUnReadType.sysMsg:
|
||||
count += (data['sys_msg'] as int?) ?? 0;
|
||||
count += msgUnread['sys_msg']?.toInt() ?? 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
count = count == 0
|
||||
? ''
|
||||
: count > 99
|
||||
? '99+'
|
||||
: count.toString();
|
||||
if (msgUnReadCount.value == count) {
|
||||
msgUnReadCount.refresh();
|
||||
} else {
|
||||
msgUnReadCount.value = count;
|
||||
}
|
||||
} catch (e) {
|
||||
debugPrint('failed to get unread count: $e');
|
||||
}
|
||||
}
|
||||
|
||||
Future _queryPMUnread() async {
|
||||
try {
|
||||
dynamic res = await Request().get(Api.msgUnread);
|
||||
if (res.data['code'] == 0) {
|
||||
return {
|
||||
'status': true,
|
||||
'data': ((res.data['data']?['unfollow_unread'] as int?) ?? 0) +
|
||||
((res.data['data']?['follow_unread'] as int?) ?? 0),
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
'status': false,
|
||||
'msg': res.data['message'],
|
||||
};
|
||||
}
|
||||
} catch (_) {}
|
||||
}
|
||||
|
||||
Future _queryMsgFeedUnread() async {
|
||||
if (isLogin.value.not) {
|
||||
return;
|
||||
final countStr = count == 0
|
||||
? ''
|
||||
: count > 99
|
||||
? '99+'
|
||||
: count.toString();
|
||||
if (msgUnReadCount.value == countStr) {
|
||||
msgUnReadCount.refresh();
|
||||
} else {
|
||||
msgUnReadCount.value = countStr;
|
||||
}
|
||||
try {
|
||||
dynamic res = await Request().get(Api.msgFeedUnread);
|
||||
if (res.data['code'] == 0) {
|
||||
return {
|
||||
'status': true,
|
||||
'data': res.data['data'],
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
'status': false,
|
||||
'msg': res.data['message'],
|
||||
};
|
||||
}
|
||||
} catch (_) {}
|
||||
}
|
||||
|
||||
Future<void> getUnreadDynamic() async {
|
||||
|
||||
@@ -22,13 +22,16 @@ class ZanButtonGrpc extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _ZanButtonGrpcState extends State<ZanButtonGrpc> {
|
||||
bool get isLike => widget.replyItem.replyControl.action == $fixnum.Int64.ONE;
|
||||
bool get isDislike =>
|
||||
widget.replyItem.replyControl.action == $fixnum.Int64.TWO;
|
||||
|
||||
Future<void> onHateReply() async {
|
||||
feedBack();
|
||||
final int oid = widget.replyItem.oid.toInt();
|
||||
final int rpid = widget.replyItem.id.toInt();
|
||||
// 1 已点赞 2 不喜欢 0 未操作
|
||||
final int action =
|
||||
widget.replyItem.replyControl.action.toInt() != 2 ? 2 : 0;
|
||||
final int action = isDislike ? 0 : 2;
|
||||
final res = await ReplyHttp.hateReply(
|
||||
type: widget.replyItem.type.toInt(),
|
||||
action: action == 2 ? 1 : 0,
|
||||
@@ -37,13 +40,9 @@ class _ZanButtonGrpcState extends State<ZanButtonGrpc> {
|
||||
);
|
||||
// SmartDialog.dismiss();
|
||||
if (res['status']) {
|
||||
SmartDialog.showToast(
|
||||
widget.replyItem.replyControl.action.toInt() != 2 ? '点踩成功' : '取消踩');
|
||||
SmartDialog.showToast(isDislike ? '取消踩' : '点踩成功');
|
||||
if (action == 2) {
|
||||
if (widget.replyItem.replyControl.action.toInt() == 1) {
|
||||
widget.replyItem.like =
|
||||
$fixnum.Int64(widget.replyItem.like.toInt() - 1);
|
||||
}
|
||||
if (isLike) widget.replyItem.like -= $fixnum.Int64.ONE;
|
||||
widget.replyItem.replyControl.action = $fixnum.Int64.TWO;
|
||||
} else {
|
||||
widget.replyItem.replyControl.action = $fixnum.Int64.ZERO;
|
||||
@@ -60,8 +59,7 @@ class _ZanButtonGrpcState extends State<ZanButtonGrpc> {
|
||||
final int oid = widget.replyItem.oid.toInt();
|
||||
final int rpid = widget.replyItem.id.toInt();
|
||||
// 1 已点赞 2 不喜欢 0 未操作
|
||||
final int action =
|
||||
widget.replyItem.replyControl.action.toInt() != 1 ? 1 : 0;
|
||||
final int action = isLike ? 0 : 1;
|
||||
final res = await ReplyHttp.likeReply(
|
||||
type: widget.replyItem.type.toInt(),
|
||||
oid: oid,
|
||||
@@ -69,15 +67,12 @@ class _ZanButtonGrpcState extends State<ZanButtonGrpc> {
|
||||
action: action,
|
||||
);
|
||||
if (res['status']) {
|
||||
SmartDialog.showToast(
|
||||
widget.replyItem.replyControl.action.toInt() != 1 ? '点赞成功' : '取消赞');
|
||||
SmartDialog.showToast(isLike ? '取消赞' : '点赞成功');
|
||||
if (action == 1) {
|
||||
widget.replyItem.like =
|
||||
$fixnum.Int64(widget.replyItem.like.toInt() + 1);
|
||||
widget.replyItem.like += $fixnum.Int64.ONE;
|
||||
widget.replyItem.replyControl.action = $fixnum.Int64.ONE;
|
||||
} else {
|
||||
widget.replyItem.like =
|
||||
$fixnum.Int64(widget.replyItem.like.toInt() - 1);
|
||||
widget.replyItem.like -= $fixnum.Int64.ONE;
|
||||
widget.replyItem.replyControl.action = $fixnum.Int64.ZERO;
|
||||
}
|
||||
setState(() {});
|
||||
@@ -115,16 +110,12 @@ class _ZanButtonGrpcState extends State<ZanButtonGrpc> {
|
||||
style: _style,
|
||||
onPressed: () => handleState(onHateReply),
|
||||
child: Icon(
|
||||
widget.replyItem.replyControl.action.toInt() == 2
|
||||
isDislike
|
||||
? FontAwesomeIcons.solidThumbsDown
|
||||
: FontAwesomeIcons.thumbsDown,
|
||||
size: 16,
|
||||
color: widget.replyItem.replyControl.action.toInt() == 2
|
||||
? primary
|
||||
: color,
|
||||
semanticLabel: widget.replyItem.replyControl.action.toInt() == 2
|
||||
? '已踩'
|
||||
: '点踩',
|
||||
color: isDislike ? primary : color,
|
||||
semanticLabel: isDislike ? '已踩' : '点踩',
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -136,17 +127,12 @@ class _ZanButtonGrpcState extends State<ZanButtonGrpc> {
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(
|
||||
widget.replyItem.replyControl.action.toInt() == 1
|
||||
isLike
|
||||
? FontAwesomeIcons.solidThumbsUp
|
||||
: FontAwesomeIcons.thumbsUp,
|
||||
size: 16,
|
||||
color: widget.replyItem.replyControl.action.toInt() == 1
|
||||
? primary
|
||||
: color,
|
||||
semanticLabel:
|
||||
widget.replyItem.replyControl.action.toInt() == 1
|
||||
? '已赞'
|
||||
: '点赞',
|
||||
color: isLike ? primary : color,
|
||||
semanticLabel: isLike ? '已赞' : '点赞',
|
||||
),
|
||||
const SizedBox(width: 4),
|
||||
AnimatedSwitcher(
|
||||
@@ -158,9 +144,7 @@ class _ZanButtonGrpcState extends State<ZanButtonGrpc> {
|
||||
child: Text(
|
||||
Utils.numFormat(widget.replyItem.like.toInt()),
|
||||
style: TextStyle(
|
||||
color: widget.replyItem.replyControl.action.toInt() == 1
|
||||
? primary
|
||||
: color,
|
||||
color: isLike ? primary : color,
|
||||
fontSize: theme.textTheme.labelSmall!.fontSize,
|
||||
),
|
||||
),
|
||||
|
||||
@@ -2,12 +2,11 @@ import 'package:PiliPlus/grpc/bilibili/app/im/v1.pb.dart'
|
||||
show Offset, Session, SessionMainReply, SessionPageType, ThreeDotItem;
|
||||
import 'package:PiliPlus/grpc/im.dart';
|
||||
import 'package:PiliPlus/http/loading_state.dart';
|
||||
import 'package:PiliPlus/http/msg.dart';
|
||||
import 'package:PiliPlus/models/msg/msgfeed_unread.dart';
|
||||
import 'package:PiliPlus/pages/common/common_whisper_controller.dart';
|
||||
import 'package:PiliPlus/utils/storage.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:protobuf/protobuf.dart' show PbMap;
|
||||
|
||||
@@ -15,7 +14,8 @@ class WhisperController extends CommonWhisperController<SessionMainReply> {
|
||||
@override
|
||||
SessionPageType sessionPageType = SessionPageType.SESSION_PAGE_TYPE_HOME;
|
||||
|
||||
late final List msgFeedTopItems;
|
||||
late final List<({bool enabled, IconData icon, String name, String route})>
|
||||
msgFeedTopItems;
|
||||
late final RxList<int> unreadCounts;
|
||||
|
||||
PbMap<int, Offset>? offset;
|
||||
@@ -29,44 +29,46 @@ class WhisperController extends CommonWhisperController<SessionMainReply> {
|
||||
final disableLikeMsg =
|
||||
GStorage.setting.get(SettingBoxKey.disableLikeMsg, defaultValue: false);
|
||||
msgFeedTopItems = [
|
||||
{
|
||||
"name": "回复我的",
|
||||
"icon": Icons.message_outlined,
|
||||
"route": "/replyMe",
|
||||
"enabled": true,
|
||||
},
|
||||
{
|
||||
"name": "@我",
|
||||
"icon": Icons.alternate_email_outlined,
|
||||
"route": "/atMe",
|
||||
"enabled": true,
|
||||
},
|
||||
{
|
||||
"name": "收到的赞",
|
||||
"icon": Icons.favorite_border_outlined,
|
||||
"route": "/likeMe",
|
||||
"enabled": !disableLikeMsg,
|
||||
},
|
||||
{
|
||||
"name": "系统通知",
|
||||
"icon": Icons.notifications_none_outlined,
|
||||
"route": "/sysMsg",
|
||||
"enabled": true,
|
||||
},
|
||||
const (
|
||||
name: "回复我的",
|
||||
icon: Icons.message_outlined,
|
||||
route: "/replyMe",
|
||||
enabled: true,
|
||||
),
|
||||
const (
|
||||
name: "@我",
|
||||
icon: Icons.alternate_email_outlined,
|
||||
route: "/atMe",
|
||||
enabled: true,
|
||||
),
|
||||
(
|
||||
name: "收到的赞",
|
||||
icon: Icons.favorite_border_outlined,
|
||||
route: "/likeMe",
|
||||
enabled: !disableLikeMsg,
|
||||
),
|
||||
const (
|
||||
name: "系统通知",
|
||||
icon: Icons.notifications_none_outlined,
|
||||
route: "/sysMsg",
|
||||
enabled: true,
|
||||
),
|
||||
];
|
||||
unreadCounts =
|
||||
List.generate(msgFeedTopItems.length, (index) => 0).toList().obs;
|
||||
unreadCounts = List.filled(msgFeedTopItems.length, 0).obs;
|
||||
queryMsgFeedUnread();
|
||||
queryData();
|
||||
}
|
||||
|
||||
Future<void> queryMsgFeedUnread() async {
|
||||
var res = await MsgHttp.msgFeedUnread();
|
||||
if (res['status']) {
|
||||
final data = MsgFeedUnread.fromJson(res['data']);
|
||||
unreadCounts.value = [data.reply, data.at, data.like, data.sysMsg];
|
||||
var res = await ImGrpc.getTotalUnread(unreadType: 2);
|
||||
if (res.isSuccess) {
|
||||
final data = MsgFeedUnread.fromJson(res.data.msgFeedUnread.unread);
|
||||
final unreadCounts = [data.reply, data.at, data.like, data.sysMsg];
|
||||
if (!listEquals(this.unreadCounts, unreadCounts)) {
|
||||
this.unreadCounts.value = unreadCounts;
|
||||
}
|
||||
} else {
|
||||
SmartDialog.showToast(res['msg']);
|
||||
res.toast();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -155,7 +155,7 @@ class _WhisperPageState extends State<WhisperPage> {
|
||||
radius: 22,
|
||||
backgroundColor: theme.colorScheme.onInverseSurface,
|
||||
child: Icon(
|
||||
_controller.msgFeedTopItems[index]['icon'],
|
||||
_controller.msgFeedTopItems[index].icon,
|
||||
size: 20,
|
||||
color: theme.colorScheme.primary,
|
||||
),
|
||||
@@ -164,20 +164,20 @@ class _WhisperPageState extends State<WhisperPage> {
|
||||
),
|
||||
const SizedBox(height: 6),
|
||||
Text(
|
||||
_controller.msgFeedTopItems[index]['name'],
|
||||
_controller.msgFeedTopItems[index].name,
|
||||
style: const TextStyle(fontSize: 13),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
onTap: () {
|
||||
if (!_controller.msgFeedTopItems[index]['enabled']) {
|
||||
if (!_controller.msgFeedTopItems[index].enabled) {
|
||||
SmartDialog.showToast('已禁用');
|
||||
return;
|
||||
}
|
||||
_controller.unreadCounts[index] = 0;
|
||||
Get.toNamed(
|
||||
_controller.msgFeedTopItems[index]['route'],
|
||||
_controller.msgFeedTopItems[index].route,
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
@@ -20,8 +20,7 @@ import 'package:get/get.dart';
|
||||
|
||||
class ChatItem extends StatelessWidget {
|
||||
static MsgType msgTypeFromValue(int value) {
|
||||
return MsgType.values.firstWhere((e) => e.value == value,
|
||||
orElse: () => MsgType.EN_INVALID_MSG_TYPE);
|
||||
return MsgType.valueOf(value) ?? MsgType.EN_INVALID_MSG_TYPE;
|
||||
}
|
||||
|
||||
const ChatItem({
|
||||
|
||||
Reference in New Issue
Block a user