mirror of
https://github.com/bggRGjQaUbCoE/PiliPlus.git
synced 2026-06-14 06:30:06 +08:00
Compare commits
14 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
70aecd1e38 | ||
|
|
a40c773491 | ||
|
|
b4abb58a41 | ||
|
|
e368436bc6 | ||
|
|
6c96b3a7f5 | ||
|
|
149f0c082d | ||
|
|
994199b5a2 | ||
|
|
8db3d80151 | ||
|
|
93af1e7c44 | ||
|
|
54e90bd986 | ||
|
|
ca16551917 | ||
|
|
f4977d2855 | ||
|
|
bd91fb7c6d | ||
|
|
e1805896f4 |
@@ -22,7 +22,7 @@ class Segment {
|
||||
|
||||
class SegmentProgressBar extends CustomPainter {
|
||||
final List<Segment> segmentColors;
|
||||
late double _defHeight;
|
||||
double? _defHeight;
|
||||
|
||||
SegmentProgressBar({
|
||||
required this.segmentColors,
|
||||
@@ -42,6 +42,18 @@ class SegmentProgressBar extends CustomPainter {
|
||||
if (segmentColors[i].title != null) {
|
||||
double fontSize = 10;
|
||||
|
||||
_defHeight ??= (TextPainter(
|
||||
text: TextSpan(
|
||||
text: segmentColors[i].title,
|
||||
style: TextStyle(
|
||||
fontSize: fontSize,
|
||||
),
|
||||
),
|
||||
textDirection: TextDirection.ltr,
|
||||
)..layout())
|
||||
.height +
|
||||
2;
|
||||
|
||||
TextPainter getTextPainter() => TextPainter(
|
||||
text: TextSpan(
|
||||
text: segmentColors[i].title,
|
||||
@@ -51,14 +63,12 @@ class SegmentProgressBar extends CustomPainter {
|
||||
height: 1,
|
||||
),
|
||||
),
|
||||
strutStyle: StrutStyle(height: 1, leading: 0),
|
||||
strutStyle:
|
||||
StrutStyle(leading: 0, height: 1, fontSize: fontSize),
|
||||
textDirection: TextDirection.ltr,
|
||||
)..layout();
|
||||
|
||||
TextPainter textPainter = getTextPainter();
|
||||
if (i == 0) {
|
||||
_defHeight = textPainter.height;
|
||||
}
|
||||
|
||||
late double prevStart;
|
||||
if (i != 0) {
|
||||
@@ -75,7 +85,7 @@ class SegmentProgressBar extends CustomPainter {
|
||||
canvas.drawRect(
|
||||
Rect.fromLTRB(
|
||||
0,
|
||||
-_defHeight - 2,
|
||||
-_defHeight!,
|
||||
size.width,
|
||||
0,
|
||||
),
|
||||
@@ -86,9 +96,9 @@ class SegmentProgressBar extends CustomPainter {
|
||||
canvas.drawRect(
|
||||
Rect.fromLTWH(
|
||||
segmentStart,
|
||||
-_defHeight - 2,
|
||||
-_defHeight!,
|
||||
segmentEnd == segmentStart ? 2 : segmentEnd - segmentStart,
|
||||
size.height + _defHeight + 2,
|
||||
size.height + _defHeight!,
|
||||
),
|
||||
paint,
|
||||
);
|
||||
@@ -98,7 +108,7 @@ class SegmentProgressBar extends CustomPainter {
|
||||
: (segmentStart - prevStart - textPainter.width) / 2 +
|
||||
prevStart +
|
||||
1;
|
||||
double textY = (-_defHeight - textPainter.height) / 2 - 1;
|
||||
double textY = (-_defHeight! - textPainter.height) / 2;
|
||||
textPainter.paint(canvas, Offset(textX, textY));
|
||||
} else {
|
||||
canvas.drawRect(
|
||||
|
||||
@@ -14,7 +14,6 @@ import '../utils/utils.dart';
|
||||
import 'api.dart';
|
||||
import 'constants.dart';
|
||||
import 'interceptor.dart';
|
||||
import 'interceptor_anonymity.dart';
|
||||
import 'package:flutter_inappwebview/flutter_inappwebview.dart' as web;
|
||||
|
||||
class Request {
|
||||
@@ -37,7 +36,7 @@ class Request {
|
||||
);
|
||||
cookieManager = CookieManager(cookieJar);
|
||||
dio.interceptors.add(cookieManager);
|
||||
dio.interceptors.add(AnonymityInterceptor());
|
||||
dio.interceptors.add(ApiInterceptor());
|
||||
final List<Cookie> cookies = await cookieManager.cookieJar
|
||||
.loadForRequest(Uri.parse(HttpString.baseUrl));
|
||||
for (Cookie item in cookies) {
|
||||
@@ -175,15 +174,12 @@ class Request {
|
||||
);
|
||||
}
|
||||
|
||||
//添加拦截器
|
||||
dio.interceptors.add(ApiInterceptor());
|
||||
|
||||
// 日志拦截器 输出请求、响应内容
|
||||
dio.interceptors.add(LogInterceptor(
|
||||
request: false,
|
||||
requestHeader: false,
|
||||
responseHeader: false,
|
||||
));
|
||||
// dio.interceptors.add(LogInterceptor(
|
||||
// request: false,
|
||||
// requestHeader: false,
|
||||
// responseHeader: false,
|
||||
// ));
|
||||
|
||||
dio.transformer = BackgroundTransformer();
|
||||
dio.options.validateStatus = (int? status) {
|
||||
|
||||
@@ -1,17 +1,67 @@
|
||||
import 'package:PiliPlus/http/api.dart';
|
||||
import 'package:PiliPlus/pages/mine/controller.dart';
|
||||
import 'package:connectivity_plus/connectivity_plus.dart';
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||
|
||||
class ApiInterceptor extends Interceptor {
|
||||
// @override
|
||||
// void onRequest(RequestOptions options, RequestInterceptorHandler handler) {
|
||||
// debugPrint("请求之前");
|
||||
// // 在请求之前添加头部或认证信息
|
||||
// options.headers['Authorization'] = 'Bearer token';
|
||||
// options.headers['Content-Type'] = 'application/json';
|
||||
// handler.next(options);
|
||||
// }
|
||||
static const List<String> anonymityList = [
|
||||
Api.videoUrl,
|
||||
Api.videoIntro,
|
||||
Api.relatedList,
|
||||
Api.replyList,
|
||||
Api.replyReplyList,
|
||||
Api.searchSuggest,
|
||||
Api.searchByType,
|
||||
Api.heartBeat,
|
||||
Api.ab2c,
|
||||
Api.bangumiInfo,
|
||||
Api.liveRoomInfo,
|
||||
Api.onlineTotal,
|
||||
Api.webDanmaku,
|
||||
Api.dynamicDetail,
|
||||
Api.aiConclusion,
|
||||
Api.getSeasonDetailApi,
|
||||
Api.liveRoomDmToken,
|
||||
Api.liveRoomDmPrefetch,
|
||||
];
|
||||
|
||||
@override
|
||||
void onRequest(RequestOptions options, RequestInterceptorHandler handler) {
|
||||
void onRemoveCookie() {
|
||||
options.headers.remove('x-bili-mid');
|
||||
options.headers.remove('x-bili-aurora-eid');
|
||||
options.headers.remove('x-bili-aurora-zone');
|
||||
options.headers['cookie'] = '';
|
||||
options.queryParameters.remove('access_key');
|
||||
options.queryParameters.remove('csrf');
|
||||
options.queryParameters.remove('csrf_token');
|
||||
if (options.data is Map) {
|
||||
options.data.remove('access_key');
|
||||
options.data.remove('csrf');
|
||||
options.data.remove('csrf_token');
|
||||
}
|
||||
}
|
||||
|
||||
if (options.extra['clearCookie'] == true) {
|
||||
onRemoveCookie();
|
||||
} else if (MineController.anonymity.value) {
|
||||
String uri = options.uri.toString();
|
||||
for (var i in anonymityList) {
|
||||
// 如果请求的url包含无痕列表中的url,则清空cookie
|
||||
// 但需要保证匹配到url的后半部分不再出现/符号,否则会误伤
|
||||
int index = uri.indexOf(i);
|
||||
if (index == -1) continue;
|
||||
if (uri.lastIndexOf('/') >= index + i.length) continue;
|
||||
//SmartDialog.showToast('触发无痕模式\n\n$i\n\n${options.uri}');
|
||||
onRemoveCookie();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
handler.next(options);
|
||||
}
|
||||
|
||||
// @override
|
||||
// void onResponse(Response response, ResponseInterceptorHandler handler) {
|
||||
|
||||
@@ -1,51 +0,0 @@
|
||||
// ignore_for_file: avoid_print
|
||||
|
||||
import 'dart:io';
|
||||
import 'package:dio/dio.dart';
|
||||
// import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||
import '../pages/mine/controller.dart';
|
||||
import 'api.dart';
|
||||
|
||||
class AnonymityInterceptor extends Interceptor {
|
||||
static const List<String> anonymityList = [
|
||||
Api.videoUrl,
|
||||
Api.videoIntro,
|
||||
Api.relatedList,
|
||||
Api.replyList,
|
||||
Api.replyReplyList,
|
||||
Api.searchSuggest,
|
||||
Api.searchByType,
|
||||
Api.heartBeat,
|
||||
Api.ab2c,
|
||||
Api.bangumiInfo,
|
||||
Api.liveRoomInfo,
|
||||
Api.onlineTotal,
|
||||
Api.webDanmaku,
|
||||
Api.dynamicDetail,
|
||||
Api.aiConclusion,
|
||||
Api.getSeasonDetailApi,
|
||||
Api.liveRoomDmToken,
|
||||
Api.liveRoomDmPrefetch,
|
||||
];
|
||||
|
||||
@override
|
||||
void onRequest(RequestOptions options, RequestInterceptorHandler handler) {
|
||||
if (MineController.anonymity.value) {
|
||||
String uri = options.uri.toString();
|
||||
for (var i in anonymityList) {
|
||||
// 如果请求的url包含无痕列表中的url,则清空cookie
|
||||
// 但需要保证匹配到url的后半部分不再出现/符号,否则会误伤
|
||||
int index = uri.indexOf(i);
|
||||
if (index == -1) continue;
|
||||
if (uri.lastIndexOf('/') >= index + i.length) continue;
|
||||
//SmartDialog.showToast('触发无痕模式\n\n$i\n\n${options.uri}');
|
||||
options.headers[HttpHeaders.cookieHeader] = "";
|
||||
if (options.data != null && options.data.csrf != null) {
|
||||
options.data.csrf = "";
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
handler.next(options);
|
||||
}
|
||||
}
|
||||
@@ -1,19 +1,17 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:PiliPlus/grpc/app/main/community/reply/v1/reply.pb.dart';
|
||||
import 'package:PiliPlus/grpc/grpc_repo.dart';
|
||||
import 'package:PiliPlus/http/loading_state.dart';
|
||||
import 'package:PiliPlus/utils/extension.dart';
|
||||
import 'package:PiliPlus/utils/storage.dart';
|
||||
import 'package:dio/dio.dart';
|
||||
|
||||
import '../models/video/reply/data.dart';
|
||||
import '../models/video/reply/emote.dart';
|
||||
import 'api.dart';
|
||||
import 'constants.dart';
|
||||
import 'init.dart';
|
||||
|
||||
class ReplyHttp {
|
||||
static Options get _options => Options(extra: {'clearCookie': true});
|
||||
|
||||
static Future<LoadingState> replyList({
|
||||
required bool isLogin,
|
||||
required int oid,
|
||||
@@ -23,13 +21,9 @@ class ReplyHttp {
|
||||
int sort = 1,
|
||||
required String banWordForReply,
|
||||
}) async {
|
||||
Options? options = !isLogin
|
||||
? Options(
|
||||
headers: {HttpHeaders.cookieHeader: "buvid3= ; b_nut= ; sid= "})
|
||||
: null;
|
||||
var res = !isLogin
|
||||
? await Request().get(
|
||||
'${HttpString.apiBaseUrl}${Api.replyList}/main',
|
||||
'${Api.replyList}/main',
|
||||
queryParameters: {
|
||||
'oid': oid,
|
||||
'type': type,
|
||||
@@ -37,10 +31,10 @@ class ReplyHttp {
|
||||
'{"offset":"${nextOffset.replaceAll('"', '\\"')}"}',
|
||||
'mode': sort + 2, //2:按时间排序;3:按热度排序
|
||||
},
|
||||
options: options,
|
||||
options: isLogin.not ? _options : null,
|
||||
)
|
||||
: await Request().get(
|
||||
'${HttpString.apiBaseUrl}${Api.replyList}',
|
||||
Api.replyList,
|
||||
queryParameters: {
|
||||
'oid': oid,
|
||||
'type': type,
|
||||
@@ -48,7 +42,7 @@ class ReplyHttp {
|
||||
'pn': page,
|
||||
'ps': 20,
|
||||
},
|
||||
options: options,
|
||||
options: isLogin.not ? _options : null,
|
||||
);
|
||||
if (res.data['code'] == 0) {
|
||||
ReplyData replyData = ReplyData.fromJson(res.data['data']);
|
||||
@@ -134,28 +128,25 @@ class ReplyHttp {
|
||||
}
|
||||
|
||||
static Future<LoadingState> replyReplyList({
|
||||
required bool isLogin,
|
||||
required int oid,
|
||||
required int root,
|
||||
required int pageNum,
|
||||
required int type,
|
||||
int sort = 1,
|
||||
required String banWordForReply,
|
||||
bool? isCheck,
|
||||
}) async {
|
||||
Options? options = GStorage.userInfo.get('userInfoCache') == null
|
||||
? Options(
|
||||
headers: {HttpHeaders.cookieHeader: "buvid3= ; b_nut= ; sid= "})
|
||||
: null;
|
||||
var res = await Request().get(
|
||||
'${HttpString.apiBaseUrl}${Api.replyReplyList}',
|
||||
Api.replyReplyList,
|
||||
queryParameters: {
|
||||
'oid': oid,
|
||||
'root': root,
|
||||
'pn': pageNum,
|
||||
'type': type,
|
||||
'sort': 1,
|
||||
'csrf': await Request.getCsrf(),
|
||||
if (isLogin) 'csrf': await Request.getCsrf(),
|
||||
},
|
||||
options: options,
|
||||
options: isLogin.not ? _options : null,
|
||||
);
|
||||
if (res.data['code'] == 0) {
|
||||
ReplyReplyData replyData = ReplyReplyData.fromJson(res.data['data']);
|
||||
@@ -168,7 +159,11 @@ class ReplyHttp {
|
||||
}
|
||||
return LoadingState.success(replyData);
|
||||
} else {
|
||||
return LoadingState.error(res.data['message']);
|
||||
return LoadingState.error(
|
||||
isCheck == true
|
||||
? '${res.data['code']}${res.data['message']}'
|
||||
: res.data['message'],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -759,6 +759,7 @@ class VideoHttp {
|
||||
int? root,
|
||||
int? parent,
|
||||
List? pictures,
|
||||
bool? syncToDynamic,
|
||||
}) async {
|
||||
if (message == '') {
|
||||
return {'status': false, 'data': [], 'msg': '请输入评论内容'};
|
||||
@@ -766,10 +767,11 @@ class VideoHttp {
|
||||
Map<String, dynamic> data = {
|
||||
'type': type.index,
|
||||
'oid': oid,
|
||||
'root': root == null || root == 0 ? '' : root,
|
||||
'parent': parent == null || parent == 0 ? '' : parent,
|
||||
if (root != null && root != 0) 'root': root,
|
||||
if (parent != null && parent != 0) 'parent': parent,
|
||||
'message': message,
|
||||
if (pictures != null) 'pictures': jsonEncode(pictures),
|
||||
if (syncToDynamic == true) 'sync_to_dynamic': 1,
|
||||
'csrf': await Request.getCsrf(),
|
||||
};
|
||||
var res = await Request().post(
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import 'package:PiliPlus/grpc/app/main/community/reply/v1/reply.pb.dart';
|
||||
import 'package:PiliPlus/http/loading_state.dart';
|
||||
import 'package:PiliPlus/http/reply.dart';
|
||||
import 'package:PiliPlus/models/common/reply_type.dart';
|
||||
import 'package:PiliPlus/models/video/reply/data.dart';
|
||||
import 'package:PiliPlus/pages/common/common_controller.dart';
|
||||
@@ -9,6 +10,7 @@ import 'package:PiliPlus/utils/global_data.dart';
|
||||
import 'package:PiliPlus/utils/utils.dart';
|
||||
import 'package:easy_debounce/easy_throttle.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:PiliPlus/models/common/reply_sort_type.dart';
|
||||
import 'package:PiliPlus/models/video/reply/item.dart';
|
||||
@@ -27,10 +29,11 @@ abstract class ReplyController extends CommonController {
|
||||
late final bool isLogin = GStorage.userInfo.get('userInfoCache') != null;
|
||||
|
||||
CursorReply? cursor;
|
||||
late Mode mode = Mode.MAIN_LIST_HOT;
|
||||
late Rx<Mode> mode = Mode.MAIN_LIST_HOT.obs;
|
||||
late bool hasUpTop = false;
|
||||
|
||||
late final banWordForReply = GStorage.banWordForReply;
|
||||
late final enableCommAntifraud = GStorage.enableCommAntifraud;
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
@@ -43,7 +46,7 @@ abstract class ReplyController extends CommonController {
|
||||
}
|
||||
sortType.value = ReplySortType.values[defaultReplySortIndex];
|
||||
if (sortType.value == ReplySortType.time) {
|
||||
mode = Mode.MAIN_LIST_TIME;
|
||||
mode.value = Mode.MAIN_LIST_TIME;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -95,7 +98,7 @@ abstract class ReplyController extends CommonController {
|
||||
hasUpTop = true;
|
||||
}
|
||||
}
|
||||
if (response.response.topReplies != null) {
|
||||
if ((response.response.topReplies as List?)?.isNotEmpty == true) {
|
||||
replies.insertAll(0, response.response.topReplies);
|
||||
hasUpTop = true;
|
||||
}
|
||||
@@ -117,11 +120,11 @@ abstract class ReplyController extends CommonController {
|
||||
switch (sortType.value) {
|
||||
case ReplySortType.time:
|
||||
sortType.value = ReplySortType.like;
|
||||
mode = Mode.MAIN_LIST_HOT;
|
||||
mode.value = Mode.MAIN_LIST_HOT;
|
||||
break;
|
||||
case ReplySortType.like:
|
||||
sortType.value = ReplySortType.time;
|
||||
mode = Mode.MAIN_LIST_TIME;
|
||||
mode.value = Mode.MAIN_LIST_TIME;
|
||||
break;
|
||||
}
|
||||
nextOffset = '';
|
||||
@@ -204,21 +207,44 @@ abstract class ReplyController extends CommonController {
|
||||
}
|
||||
count.value += 1;
|
||||
loadingState.value = LoadingState.success(response);
|
||||
if (enableCommAntifraud && context.mounted) {
|
||||
checkReply(
|
||||
context,
|
||||
oid ?? replyItem.oid.toInt(),
|
||||
replyItem?.id.toInt(),
|
||||
replyItem?.type.toInt() ??
|
||||
replyType?.index ??
|
||||
ReplyType.video.index,
|
||||
replyInfo.id.toInt(),
|
||||
replyInfo.content.message,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
ReplyData response = loadingState.value is Success
|
||||
? (loadingState.value as Success).response
|
||||
: ReplyData();
|
||||
response.replies ??= <ReplyItemModel>[];
|
||||
ReplyItemModel replyInfo = ReplyItemModel.fromJson(res, '');
|
||||
if (oid != null) {
|
||||
response.replies
|
||||
?.insert(hasUpTop ? 1 : 0, ReplyItemModel.fromJson(res, ''));
|
||||
response.replies?.insert(hasUpTop ? 1 : 0, replyInfo);
|
||||
} else {
|
||||
response.replies?[index].replies ??= <ReplyItemModel>[];
|
||||
response.replies?[index].replies
|
||||
?.add(ReplyItemModel.fromJson(res, ''));
|
||||
response.replies?[index].replies?.add(replyInfo);
|
||||
}
|
||||
count.value += 1;
|
||||
loadingState.value = LoadingState.success(response);
|
||||
if (enableCommAntifraud && context.mounted) {
|
||||
checkReply(
|
||||
context,
|
||||
oid ?? replyItem.oid,
|
||||
replyItem?.rpid,
|
||||
replyItem?.type.toInt() ??
|
||||
replyType?.index ??
|
||||
ReplyType.video.index,
|
||||
replyInfo.rpid ?? 0,
|
||||
replyInfo.content?.message ?? '',
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -262,4 +288,186 @@ abstract class ReplyController extends CommonController {
|
||||
loadingState.value = LoadingState.success(response);
|
||||
}
|
||||
}
|
||||
|
||||
// ref https://github.com/freedom-introvert/biliSendCommAntifraud
|
||||
void checkReply(
|
||||
BuildContext context,
|
||||
dynamic oid,
|
||||
dynamic rpid,
|
||||
int replyType,
|
||||
int replyId,
|
||||
String message,
|
||||
) async {
|
||||
void showReplyCheckResult(String message) {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) => AlertDialog(
|
||||
title: Text('评论检查结果'),
|
||||
content: SelectableText(message),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
await Future.delayed(const Duration(seconds: 5));
|
||||
if (context.mounted.not) return;
|
||||
// root reply
|
||||
if (rpid == null) {
|
||||
// no cookie check
|
||||
dynamic res = await ReplyHttp.replyList(
|
||||
isLogin: false,
|
||||
oid: oid,
|
||||
nextOffset: '',
|
||||
type: replyType,
|
||||
sort: ReplySortType.time.index,
|
||||
page: 1,
|
||||
banWordForReply: '',
|
||||
);
|
||||
if (context.mounted.not) return;
|
||||
if (res is Error) {
|
||||
SmartDialog.showToast('获取评论主列表时发生错误:${res.errMsg}');
|
||||
return;
|
||||
} else if (res is Success) {
|
||||
ReplyData replies = res.response;
|
||||
int index =
|
||||
replies.replies?.indexWhere((item) => item.rpid == replyId) ?? -1;
|
||||
if (index != -1) {
|
||||
// found
|
||||
if (context.mounted) {
|
||||
showReplyCheckResult(
|
||||
'无账号状态下找到了你的评论,评论正常!\n\n你的评论:$message',
|
||||
);
|
||||
}
|
||||
} else {
|
||||
// not found
|
||||
if (context.mounted.not) return;
|
||||
// cookie check
|
||||
dynamic res1 = await ReplyHttp.replyReplyList(
|
||||
isLogin: isLogin,
|
||||
oid: oid,
|
||||
root: rpid ?? replyId,
|
||||
pageNum: 1,
|
||||
type: replyType,
|
||||
banWordForReply: '',
|
||||
);
|
||||
if (context.mounted.not) return;
|
||||
if (res1 is Error) {
|
||||
// not found
|
||||
if (context.mounted) {
|
||||
showReplyCheckResult(
|
||||
'无法找到你的评论。\n\n你的评论:$message',
|
||||
);
|
||||
}
|
||||
} else if (res1 is Success) {
|
||||
// found
|
||||
if (context.mounted.not) return;
|
||||
// no cookie check
|
||||
dynamic res2 = await ReplyHttp.replyReplyList(
|
||||
isLogin: false,
|
||||
oid: oid,
|
||||
root: rpid ?? replyId,
|
||||
pageNum: 1,
|
||||
type: replyType,
|
||||
banWordForReply: '',
|
||||
isCheck: true,
|
||||
);
|
||||
if (context.mounted.not) return;
|
||||
if (res2 is Error) {
|
||||
// not found
|
||||
if (context.mounted) {
|
||||
showReplyCheckResult(
|
||||
res2.errMsg.startsWith('12022')
|
||||
? '你的评论被shadow ban(仅自己可见)!\n\n你的评论: $message'
|
||||
: '评论不可见(${res2.errMsg}): $message',
|
||||
);
|
||||
}
|
||||
} else if (res2 is Success) {
|
||||
// found
|
||||
if (context.mounted) {
|
||||
showReplyCheckResult('''
|
||||
你评论状态有点可疑,虽然无账号翻找评论区获取不到你的评论,但是无账号可通过
|
||||
https://api.bilibili.com/x/v2/reply/reply?oid=$oid&pn=1&ps=20&root=${rpid ?? replyId}&type=$replyType
|
||||
获取你的评论,疑似评论区被戒严或者这是你的视频。
|
||||
|
||||
你的评论:$message''');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (int i = 1; true; i++) {
|
||||
if (context.mounted.not) return;
|
||||
dynamic res3 = await ReplyHttp.replyReplyList(
|
||||
isLogin: false,
|
||||
oid: oid,
|
||||
root: rpid ?? replyId,
|
||||
pageNum: i,
|
||||
type: replyType,
|
||||
banWordForReply: '',
|
||||
isCheck: true,
|
||||
);
|
||||
if (res3 is Error) {
|
||||
break;
|
||||
} else if (res3 is Success) {
|
||||
ReplyReplyData data = res3.response;
|
||||
if (data.replies.isNullOrEmpty) {
|
||||
break;
|
||||
}
|
||||
int index =
|
||||
data.replies?.indexWhere((item) => item.rpid == replyId) ?? -1;
|
||||
if (index == -1) {
|
||||
// not found
|
||||
} else {
|
||||
// found
|
||||
if (context.mounted) {
|
||||
showReplyCheckResult(
|
||||
'无账号状态下找到了你的评论,评论正常!\n\n你的评论:$message',
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 1; true; i++) {
|
||||
if (context.mounted.not) return;
|
||||
dynamic res4 = await ReplyHttp.replyReplyList(
|
||||
isLogin: true,
|
||||
oid: oid,
|
||||
root: rpid ?? replyId,
|
||||
pageNum: i,
|
||||
type: replyType,
|
||||
banWordForReply: '',
|
||||
isCheck: true,
|
||||
);
|
||||
if (res4 is Error) {
|
||||
break;
|
||||
} else if (res4 is Success) {
|
||||
ReplyReplyData data = res4.response;
|
||||
if (data.replies.isNullOrEmpty) {
|
||||
break;
|
||||
}
|
||||
int index =
|
||||
data.replies?.indexWhere((item) => item.rpid == replyId) ?? -1;
|
||||
if (index == -1) {
|
||||
// not found
|
||||
} else {
|
||||
// found
|
||||
if (context.mounted) {
|
||||
showReplyCheckResult(
|
||||
'你的评论被shadow ban(仅自己可见)!\n\n你的评论: $message',
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (context.mounted) {
|
||||
showReplyCheckResult(
|
||||
'评论不可见: $message',
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@ class DynamicDetailController extends ReplyController {
|
||||
oid: oid!,
|
||||
cursor: CursorReq(
|
||||
next: cursor?.next ?? $fixnum.Int64(0),
|
||||
mode: mode,
|
||||
mode: mode.value,
|
||||
),
|
||||
banWordForReply: banWordForReply,
|
||||
)
|
||||
|
||||
@@ -110,78 +110,84 @@ class AuthorPanel extends StatelessWidget {
|
||||
)
|
||||
],
|
||||
),
|
||||
// const Spacer(),
|
||||
// if (source != 'detail' && item.modules?.moduleTag?.text != null)
|
||||
// Container(
|
||||
// padding:
|
||||
// const EdgeInsets.symmetric(horizontal: 4, vertical: 2),
|
||||
// decoration: BoxDecoration(
|
||||
// color: Theme.of(context).colorScheme.surface,
|
||||
// borderRadius: const BorderRadius.all(Radius.circular(4)),
|
||||
// border: Border.all(
|
||||
// width: 1.25,
|
||||
// color: Theme.of(context).colorScheme.primary,
|
||||
// ),
|
||||
// ),
|
||||
// child: Text(
|
||||
// item.modules.moduleTag.text,
|
||||
// style: TextStyle(
|
||||
// height: 1,
|
||||
// fontSize: 12,
|
||||
// color: Theme.of(context).colorScheme.primary,
|
||||
// ),
|
||||
// strutStyle: const StrutStyle(
|
||||
// leading: 0,
|
||||
// height: 1,
|
||||
// fontSize: 12,
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
],
|
||||
),
|
||||
),
|
||||
Align(
|
||||
alignment: Alignment.centerRight,
|
||||
child: item.modules.moduleAuthor.decorate != null
|
||||
child: source != 'detail' && item.modules?.moduleTag?.text != null
|
||||
? Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
// GestureDetector(
|
||||
// onTap:
|
||||
// item.modules.moduleAuthor.decorate['jump_url'] != null
|
||||
// ? () {
|
||||
// Get.toNamed(
|
||||
// '/webview',
|
||||
// parameters: {
|
||||
// 'url':
|
||||
// '${item.modules.moduleAuthor.decorate['jump_url']}'
|
||||
// },
|
||||
// );
|
||||
// }
|
||||
// : null,
|
||||
// child:
|
||||
Stack(
|
||||
clipBehavior: Clip.none,
|
||||
alignment: Alignment.centerRight,
|
||||
children: [
|
||||
CachedNetworkImage(
|
||||
height: 32,
|
||||
imageUrl:
|
||||
item.modules.moduleAuthor.decorate['card_url'],
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 4, vertical: 2),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius:
|
||||
const BorderRadius.all(Radius.circular(4)),
|
||||
border: Border.all(
|
||||
width: 1.25,
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
),
|
||||
if ((item.modules.moduleAuthor.decorate?['fan']
|
||||
?['num_str'] as String?)
|
||||
?.isNotEmpty ==
|
||||
true)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(right: 32),
|
||||
child: Text(
|
||||
'${item.modules.moduleAuthor.decorate['fan']['num_str']}',
|
||||
style: TextStyle(
|
||||
fontSize: 11,
|
||||
fontFamily: 'digital_id_num',
|
||||
color:
|
||||
(item.modules.moduleAuthor.decorate?['fan']
|
||||
),
|
||||
child: Text(
|
||||
item.modules.moduleTag.text,
|
||||
style: TextStyle(
|
||||
height: 1,
|
||||
fontSize: 12,
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
),
|
||||
strutStyle: const StrutStyle(
|
||||
leading: 0,
|
||||
height: 1,
|
||||
fontSize: 12,
|
||||
),
|
||||
),
|
||||
),
|
||||
_moreWidget(context),
|
||||
],
|
||||
)
|
||||
: item.modules.moduleAuthor.decorate != null
|
||||
? Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
// GestureDetector(
|
||||
// onTap:
|
||||
// item.modules.moduleAuthor.decorate['jump_url'] != null
|
||||
// ? () {
|
||||
// Get.toNamed(
|
||||
// '/webview',
|
||||
// parameters: {
|
||||
// 'url':
|
||||
// '${item.modules.moduleAuthor.decorate['jump_url']}'
|
||||
// },
|
||||
// );
|
||||
// }
|
||||
// : null,
|
||||
// child:
|
||||
Stack(
|
||||
clipBehavior: Clip.none,
|
||||
alignment: Alignment.centerRight,
|
||||
children: [
|
||||
CachedNetworkImage(
|
||||
height: 32,
|
||||
imageUrl: item
|
||||
.modules.moduleAuthor.decorate['card_url'],
|
||||
),
|
||||
if ((item.modules.moduleAuthor.decorate?['fan']
|
||||
?['num_str'] as String?)
|
||||
?.isNotEmpty ==
|
||||
true)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(right: 32),
|
||||
child: Text(
|
||||
'${item.modules.moduleAuthor.decorate['fan']['num_str']}',
|
||||
style: TextStyle(
|
||||
height: 1,
|
||||
fontSize: 11,
|
||||
fontFamily: 'digital_id_num',
|
||||
color: (item.modules.moduleAuthor
|
||||
.decorate?['fan']
|
||||
?['color'] as String?)
|
||||
?.startsWith('#') ==
|
||||
true
|
||||
@@ -193,16 +199,16 @@ class AuthorPanel extends StatelessWidget {
|
||||
),
|
||||
)
|
||||
: null,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
// ),
|
||||
_moreWidget(context),
|
||||
],
|
||||
),
|
||||
// ),
|
||||
_moreWidget(context),
|
||||
],
|
||||
)
|
||||
: _moreWidget(context),
|
||||
)
|
||||
: _moreWidget(context),
|
||||
)
|
||||
],
|
||||
);
|
||||
@@ -306,6 +312,25 @@ class AuthorPanel extends StatelessWidget {
|
||||
},
|
||||
minLeadingWidth: 0,
|
||||
),
|
||||
if (GStorage.isLogin)
|
||||
ListTile(
|
||||
title: Text(
|
||||
'举报',
|
||||
style: Theme.of(context).textTheme.titleSmall!.copyWith(
|
||||
color: Theme.of(context).colorScheme.error,
|
||||
),
|
||||
),
|
||||
leading: Icon(
|
||||
Icons.error_outline_outlined,
|
||||
size: 19,
|
||||
color: Theme.of(context).colorScheme.error,
|
||||
),
|
||||
onTap: () {
|
||||
Get.back();
|
||||
_showReportDynDialog(context);
|
||||
},
|
||||
minLeadingWidth: 0,
|
||||
),
|
||||
if (item.modules.moduleAuthor.mid ==
|
||||
GStorage.userInfo.get('userInfoCache')?.mid &&
|
||||
onRemove != null)
|
||||
@@ -345,25 +370,6 @@ class AuthorPanel extends StatelessWidget {
|
||||
.titleSmall!
|
||||
.copyWith(color: Theme.of(context).colorScheme.error)),
|
||||
),
|
||||
if (GStorage.isLogin)
|
||||
ListTile(
|
||||
title: Text(
|
||||
'举报',
|
||||
style: Theme.of(context).textTheme.titleSmall!.copyWith(
|
||||
color: Theme.of(context).colorScheme.error,
|
||||
),
|
||||
),
|
||||
leading: Icon(
|
||||
Icons.error_outline_outlined,
|
||||
size: 19,
|
||||
color: Theme.of(context).colorScheme.error,
|
||||
),
|
||||
onTap: () {
|
||||
Get.back();
|
||||
_showReportDynDialog(context);
|
||||
},
|
||||
minLeadingWidth: 0,
|
||||
),
|
||||
const Divider(thickness: 0.1, height: 1),
|
||||
ListTile(
|
||||
onTap: Get.back,
|
||||
|
||||
@@ -55,7 +55,7 @@ class HtmlRenderController extends ReplyController {
|
||||
oid: oid.value,
|
||||
cursor: CursorReq(
|
||||
next: cursor?.next ?? $fixnum.Int64(0),
|
||||
mode: mode,
|
||||
mode: mode.value,
|
||||
),
|
||||
banWordForReply: banWordForReply,
|
||||
)
|
||||
|
||||
@@ -66,17 +66,7 @@ class _SponsorBlockPageState extends State<SponsorBlockPage> {
|
||||
Request()
|
||||
.get(
|
||||
'$_blockServer/api/status/uptime',
|
||||
options: Options(
|
||||
headers: {
|
||||
'env': '',
|
||||
'app-key': '',
|
||||
'x-bili-mid': '',
|
||||
'x-bili-aurora-eid': '',
|
||||
'x-bili-aurora-zone': '',
|
||||
'cookie':
|
||||
'buvid3= ; SESSDATA= ; bili_jct= ; DedeUserID= ; DedeUserID__ckMd5= ; sid= ',
|
||||
},
|
||||
),
|
||||
options: Options(extra: {'clearCookie': true}),
|
||||
)
|
||||
.then((res) {
|
||||
setState(() {
|
||||
|
||||
@@ -3,7 +3,7 @@ import 'dart:math';
|
||||
|
||||
import 'package:PiliPlus/common/widgets/refresh_indicator.dart'
|
||||
show kDragContainerExtentPercentage, displacement;
|
||||
import 'package:PiliPlus/http/interceptor_anonymity.dart';
|
||||
import 'package:PiliPlus/http/interceptor.dart';
|
||||
import 'package:PiliPlus/models/common/audio_normalization.dart';
|
||||
import 'package:PiliPlus/models/common/dynamic_badge_mode.dart';
|
||||
import 'package:PiliPlus/models/common/dynamics_type.dart';
|
||||
@@ -1359,7 +1359,7 @@ List<SettingsModel> get privacySettings => [
|
||||
builder: (context) {
|
||||
return AlertDialog(
|
||||
title: const Text('查看详情'),
|
||||
content: Text(AnonymityInterceptor.anonymityList.join('\n')),
|
||||
content: Text(ApiInterceptor.anonymityList.join('\n')),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () async {
|
||||
@@ -1937,6 +1937,24 @@ List<SettingsModel> get extraSettings => [
|
||||
setKey: SettingBoxKey.showDmChart,
|
||||
defaultVal: false,
|
||||
),
|
||||
SettingsModel(
|
||||
settingsType: SettingsType.sw1tch,
|
||||
title: '发评反诈',
|
||||
subtitle: '发送评论后检查评论是否可见',
|
||||
leading: Stack(
|
||||
alignment: Alignment.center,
|
||||
children: [
|
||||
const Icon(Icons.shield),
|
||||
Icon(
|
||||
Icons.reply,
|
||||
size: 16,
|
||||
color: Theme.of(Get.context!).colorScheme.surface,
|
||||
),
|
||||
],
|
||||
),
|
||||
setKey: SettingBoxKey.enableCommAntifraud,
|
||||
defaultVal: false,
|
||||
),
|
||||
SettingsModel(
|
||||
settingsType: SettingsType.sw1tch,
|
||||
enableFeedback: true,
|
||||
|
||||
@@ -77,7 +77,7 @@ class VideoDetailController extends GetxController
|
||||
RxBool isShowCover = true.obs;
|
||||
// 硬解
|
||||
RxBool enableHA = true.obs;
|
||||
RxString hwdec = 'auto-safe'.obs;
|
||||
RxString hwdec = GStorage.hardwareDecoding.obs;
|
||||
|
||||
RxInt oid = 0.obs;
|
||||
|
||||
@@ -194,8 +194,6 @@ class VideoDetailController extends GetxController
|
||||
setting.get(SettingBoxKey.autoPlayEnable, defaultValue: false);
|
||||
if (autoPlay.value) isShowCover.value = false;
|
||||
enableHA.value = setting.get(SettingBoxKey.enableHA, defaultValue: true);
|
||||
hwdec.value = setting.get(SettingBoxKey.hardwareDecoding,
|
||||
defaultValue: Platform.isAndroid ? 'auto-safe' : 'auto');
|
||||
if (userInfo == null ||
|
||||
GStorage.localCache.get(LocalCacheKey.historyPause) == true) {
|
||||
enableHeart = false;
|
||||
@@ -621,17 +619,7 @@ class VideoDetailController extends GetxController
|
||||
);
|
||||
}
|
||||
|
||||
Options get _options => Options(
|
||||
headers: {
|
||||
'env': '',
|
||||
'app-key': '',
|
||||
'x-bili-mid': '',
|
||||
'x-bili-aurora-eid': '',
|
||||
'x-bili-aurora-zone': '',
|
||||
HttpHeaders.cookieHeader:
|
||||
'buvid3= ; SESSDATA= ; bili_jct= ; DedeUserID= ; DedeUserID__ckMd5= ; sid= ',
|
||||
},
|
||||
);
|
||||
Options get _options => Options(extra: {'clearCookie': true});
|
||||
|
||||
Future _querySponsorBlock() async {
|
||||
positionSubscription?.cancel();
|
||||
|
||||
@@ -25,7 +25,7 @@ class VideoReplyController extends ReplyController {
|
||||
oid: aid!,
|
||||
cursor: CursorReq(
|
||||
next: cursor?.next ?? $fixnum.Int64(0),
|
||||
mode: mode,
|
||||
mode: mode.value,
|
||||
),
|
||||
banWordForReply: banWordForReply,
|
||||
)
|
||||
|
||||
@@ -34,6 +34,8 @@ class ReplyPage extends CommonPublishPage {
|
||||
}
|
||||
|
||||
class _ReplyPageState extends CommonPublishPageState<ReplyPage> {
|
||||
RxBool _syncToDynamic = false.obs;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return MediaQuery.removePadding(
|
||||
@@ -177,7 +179,7 @@ class _ReplyPageState extends CommonPublishPageState<ReplyPage> {
|
||||
selected: selectKeyboard.value,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 20),
|
||||
const SizedBox(width: 10),
|
||||
Obx(
|
||||
() => ToolbarIconButton(
|
||||
tooltip: '表情',
|
||||
@@ -192,7 +194,7 @@ class _ReplyPageState extends CommonPublishPageState<ReplyPage> {
|
||||
),
|
||||
),
|
||||
if (widget.root == 0) ...[
|
||||
const SizedBox(width: 20),
|
||||
const SizedBox(width: 10),
|
||||
ToolbarIconButton(
|
||||
tooltip: '图片',
|
||||
selected: false,
|
||||
@@ -201,6 +203,32 @@ class _ReplyPageState extends CommonPublishPageState<ReplyPage> {
|
||||
),
|
||||
],
|
||||
const Spacer(),
|
||||
Obx(
|
||||
() => TextButton.icon(
|
||||
style: TextButton.styleFrom(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 15, vertical: 13),
|
||||
visualDensity: const VisualDensity(
|
||||
horizontal: -2,
|
||||
vertical: -2,
|
||||
),
|
||||
foregroundColor: _syncToDynamic.value
|
||||
? Theme.of(context).colorScheme.secondary
|
||||
: Theme.of(context).colorScheme.outline,
|
||||
),
|
||||
onPressed: () {
|
||||
_syncToDynamic.value = !_syncToDynamic.value;
|
||||
},
|
||||
icon: Icon(
|
||||
_syncToDynamic.value
|
||||
? Icons.check_box
|
||||
: Icons.check_box_outline_blank,
|
||||
size: 22,
|
||||
),
|
||||
label: const Text('转发至动态'),
|
||||
),
|
||||
),
|
||||
const Spacer(),
|
||||
Obx(
|
||||
() => FilledButton.tonal(
|
||||
onPressed: enablePublish.value ? onPublish : null,
|
||||
@@ -234,6 +262,7 @@ class _ReplyPageState extends CommonPublishPageState<ReplyPage> {
|
||||
? ' 回复 @${GlobalData().grpcReply ? widget.replyItem.member.name : widget.replyItem.member.uname} : $message'
|
||||
: message,
|
||||
pictures: pictures,
|
||||
syncToDynamic: _syncToDynamic.value,
|
||||
);
|
||||
if (result['status']) {
|
||||
SmartDialog.showToast(result['data']['success_toast']);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import 'package:PiliPlus/grpc/app/main/community/reply/v1/reply.pb.dart';
|
||||
import 'package:PiliPlus/http/loading_state.dart';
|
||||
import 'package:PiliPlus/models/video/reply/item.dart';
|
||||
import 'package:PiliPlus/pages/common/common_controller.dart';
|
||||
import 'package:PiliPlus/pages/common/reply_controller.dart';
|
||||
import 'package:PiliPlus/utils/global_data.dart';
|
||||
import 'package:PiliPlus/utils/storage.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
@@ -10,7 +10,7 @@ import 'package:PiliPlus/http/reply.dart';
|
||||
import 'package:PiliPlus/models/common/reply_type.dart';
|
||||
import 'package:scrollable_positioned_list/scrollable_positioned_list.dart';
|
||||
|
||||
class VideoReplyReplyController extends CommonController
|
||||
class VideoReplyReplyController extends ReplyController
|
||||
with GetTickerProviderStateMixin {
|
||||
VideoReplyReplyController({
|
||||
required this.hasRoot,
|
||||
@@ -32,9 +32,6 @@ class VideoReplyReplyController extends CommonController
|
||||
int? rpid;
|
||||
ReplyType replyType; // = ReplyType.video;
|
||||
|
||||
CursorReply? cursor;
|
||||
Rx<Mode> mode = Mode.MAIN_LIST_TIME.obs;
|
||||
RxInt count = (-1).obs;
|
||||
int? upMid;
|
||||
|
||||
dynamic firstFloor;
|
||||
@@ -45,11 +42,10 @@ class VideoReplyReplyController extends CommonController
|
||||
|
||||
late final horizontalPreview = GStorage.horizontalPreview;
|
||||
|
||||
late final banWordForReply = GStorage.banWordForReply;
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
super.onInit();
|
||||
mode.value = Mode.MAIN_LIST_TIME;
|
||||
queryData();
|
||||
}
|
||||
|
||||
@@ -203,6 +199,7 @@ class VideoReplyReplyController extends CommonController
|
||||
banWordForReply: banWordForReply,
|
||||
)
|
||||
: ReplyHttp.replyReplyList(
|
||||
isLogin: isLogin,
|
||||
oid: oid!,
|
||||
root: rpid!,
|
||||
pageNum: currentPage,
|
||||
@@ -210,6 +207,7 @@ class VideoReplyReplyController extends CommonController
|
||||
banWordForReply: banWordForReply,
|
||||
);
|
||||
|
||||
@override
|
||||
queryBySort() {
|
||||
mode.value = mode.value == Mode.MAIN_LIST_HOT
|
||||
? Mode.MAIN_LIST_TIME
|
||||
|
||||
@@ -27,7 +27,7 @@ class VideoReplyReplyPanel extends StatefulWidget {
|
||||
this.dialog,
|
||||
this.firstFloor,
|
||||
this.source,
|
||||
this.replyType,
|
||||
required this.replyType,
|
||||
this.isDialogue = false,
|
||||
this.isTop = false,
|
||||
this.onViewImage,
|
||||
@@ -39,7 +39,7 @@ class VideoReplyReplyPanel extends StatefulWidget {
|
||||
final int? dialog;
|
||||
final dynamic firstFloor;
|
||||
final String? source;
|
||||
final ReplyType? replyType;
|
||||
final ReplyType replyType;
|
||||
final bool isDialogue;
|
||||
final bool isTop;
|
||||
final VoidCallback? onViewImage;
|
||||
@@ -73,7 +73,7 @@ class _VideoReplyReplyPanelState extends State<VideoReplyReplyPanel>
|
||||
oid: widget.oid,
|
||||
rpid: widget.rpid,
|
||||
dialog: widget.dialog,
|
||||
replyType: widget.replyType!,
|
||||
replyType: widget.replyType,
|
||||
isDialogue: widget.isDialogue,
|
||||
),
|
||||
tag: '${widget.rpid}${widget.dialog}${widget.isDialogue}',
|
||||
@@ -352,15 +352,36 @@ class _VideoReplyReplyPanelState extends State<VideoReplyReplyPanel>
|
||||
_videoReplyReplyController.count.value += 1;
|
||||
_videoReplyReplyController.loadingState.value =
|
||||
LoadingState.success(list);
|
||||
if (_videoReplyReplyController.enableCommAntifraud && mounted) {
|
||||
_videoReplyReplyController.checkReply(
|
||||
context,
|
||||
oid,
|
||||
root,
|
||||
widget.replyType.index,
|
||||
replyInfo.id.toInt(),
|
||||
replyInfo.content.message,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
List list = _videoReplyReplyController.loadingState.value is Success
|
||||
? (_videoReplyReplyController.loadingState.value as Success)
|
||||
.response
|
||||
: <ReplyItemModel>[];
|
||||
list.insert(index + 1, ReplyItemModel.fromJson(res, ''));
|
||||
ReplyItemModel replyInfo = ReplyItemModel.fromJson(res, '');
|
||||
list.insert(index + 1, replyInfo);
|
||||
_videoReplyReplyController.count.value += 1;
|
||||
_videoReplyReplyController.loadingState.value =
|
||||
LoadingState.success(list);
|
||||
if (_videoReplyReplyController.enableCommAntifraud && mounted) {
|
||||
_videoReplyReplyController.checkReply(
|
||||
context,
|
||||
oid,
|
||||
root,
|
||||
widget.replyType.index,
|
||||
replyInfo.rpid ?? 0,
|
||||
replyInfo.content?.message ?? '',
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:PiliPlus/common/widgets/segment_progress_bar.dart';
|
||||
@@ -175,6 +176,15 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
||||
FlutterVolumeController.addListener((double value) {
|
||||
if (mounted && !_volumeInterceptEventStream.value) {
|
||||
_volumeValue.value = value;
|
||||
if (Platform.isIOS && FlutterVolumeController.showSystemUI.not) {
|
||||
_volumeIndicator.value = true;
|
||||
_volumeTimer?.cancel();
|
||||
_volumeTimer = Timer(const Duration(milliseconds: 800), () {
|
||||
if (mounted) {
|
||||
_volumeIndicator.value = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
} catch (_) {}
|
||||
@@ -1156,7 +1166,7 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
||||
buildDmChart(context, plPlayerController),
|
||||
if (plPlayerController.viewPointList.isNotEmpty &&
|
||||
plPlayerController.showVP.value)
|
||||
buildViewPointWidget(plPlayerController),
|
||||
buildViewPointWidget(plPlayerController, 4.25),
|
||||
ProgressBar(
|
||||
progress: Duration(seconds: value),
|
||||
buffered: Duration(seconds: buffer),
|
||||
@@ -1524,7 +1534,7 @@ Widget buildDmChart(
|
||||
]) {
|
||||
return IgnorePointer(
|
||||
child: Container(
|
||||
height: 14,
|
||||
height: 12,
|
||||
margin: EdgeInsets.only(
|
||||
bottom: plPlayerController.viewPointList.isNotEmpty &&
|
||||
plPlayerController.showVP.value
|
||||
@@ -1537,12 +1547,12 @@ Widget buildDmChart(
|
||||
lineTouchData: const LineTouchData(enabled: false),
|
||||
gridData: const FlGridData(show: false),
|
||||
borderData: FlBorderData(show: false),
|
||||
// minX: 0,
|
||||
// maxX: (plPlayerController.dmTrend.length - 1).toDouble(),
|
||||
// minY: 0,
|
||||
// maxY: plPlayerController.dmTrend
|
||||
// .reduce((a, b) => a > b ? a : b)
|
||||
// .toDouble(),
|
||||
minX: 0,
|
||||
maxX: (plPlayerController.dmTrend.length - 1).toDouble(),
|
||||
minY: 0,
|
||||
maxY: plPlayerController.dmTrend
|
||||
.reduce((a, b) => a > b ? a : b)
|
||||
.toDouble(),
|
||||
lineBarsData: [
|
||||
LineChartBarData(
|
||||
spots: List.generate(
|
||||
@@ -1671,13 +1681,15 @@ Widget buildSeekPreviewWidget(PlPlayerController plPlayerController) {
|
||||
});
|
||||
}
|
||||
|
||||
Widget buildViewPointWidget(PlPlayerController plPlayerController) {
|
||||
Widget buildViewPointWidget(
|
||||
PlPlayerController plPlayerController, double offset) {
|
||||
return LayoutBuilder(
|
||||
builder: (context, constraints) {
|
||||
return SizedBox(
|
||||
height: 20,
|
||||
return Container(
|
||||
height: 16,
|
||||
margin: EdgeInsets.only(bottom: offset),
|
||||
child: Listener(
|
||||
behavior: HitTestBehavior.translucent,
|
||||
behavior: HitTestBehavior.opaque,
|
||||
onPointerDown: (event) {
|
||||
try {
|
||||
double seg = event.localPosition.dx / constraints.maxWidth;
|
||||
|
||||
@@ -62,10 +62,7 @@ class BottomControl extends StatelessWidget implements PreferredSizeWidget {
|
||||
buildDmChart(context, controller!, 4.5),
|
||||
if (controller?.viewPointList.isNotEmpty == true &&
|
||||
controller?.showVP.value == true)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(bottom: 5.25),
|
||||
child: buildViewPointWidget(controller!),
|
||||
),
|
||||
buildViewPointWidget(controller!, 8.75),
|
||||
ProgressBar(
|
||||
progress: Duration(seconds: value),
|
||||
buffered: Duration(seconds: buffer),
|
||||
|
||||
@@ -54,7 +54,6 @@ import '../pages/setting/style_setting.dart';
|
||||
import '../pages/subscription/index.dart';
|
||||
import '../pages/subscription_detail/index.dart';
|
||||
import '../pages/video/detail/index.dart';
|
||||
import '../pages/video/detail/reply_reply/index.dart';
|
||||
import '../pages/whisper/index.dart';
|
||||
import '../pages/whisper_detail/index.dart';
|
||||
|
||||
@@ -99,8 +98,8 @@ class Routes {
|
||||
CustomGetPage(name: '/member', page: () => const MemberPageNew()),
|
||||
CustomGetPage(name: '/memberSearch', page: () => const MemberSearchPage()),
|
||||
// 二级回复
|
||||
CustomGetPage(
|
||||
name: '/replyReply', page: () => const VideoReplyReplyPanel()),
|
||||
// CustomGetPage(
|
||||
// name: '/replyReply', page: () => const VideoReplyReplyPanel()),
|
||||
// 推荐流设置
|
||||
CustomGetPage(
|
||||
name: '/recommendSetting', page: () => const RecommendSetting()),
|
||||
|
||||
@@ -4,6 +4,7 @@ import 'package:PiliPlus/models/bangumi/info.dart';
|
||||
import 'package:PiliPlus/models/video_detail_res.dart';
|
||||
import 'package:PiliPlus/plugin/pl_player/index.dart';
|
||||
import 'package:PiliPlus/utils/storage.dart';
|
||||
import 'package:get/get_utils/get_utils.dart';
|
||||
|
||||
Future<VideoPlayerServiceHandler> initAudioService() async {
|
||||
return await AudioService.init(
|
||||
@@ -112,7 +113,8 @@ class VideoPlayerServiceHandler extends BaseAudioHandler with SeekHandler {
|
||||
MediaItem? mediaItem;
|
||||
if (data is VideoDetailData) {
|
||||
if ((data.pages?.length ?? 0) > 1) {
|
||||
final current = data.pages?.firstWhere((element) => element.cid == cid);
|
||||
final current =
|
||||
data.pages?.firstWhereOrNull((element) => element.cid == cid);
|
||||
mediaItem = MediaItem(
|
||||
id: UniqueKey().toString(),
|
||||
title: current?.pagePart ?? "",
|
||||
@@ -132,7 +134,7 @@ class VideoPlayerServiceHandler extends BaseAudioHandler with SeekHandler {
|
||||
}
|
||||
} else if (data is BangumiInfoModel) {
|
||||
final current =
|
||||
data.episodes?.firstWhere((element) => element.cid == cid);
|
||||
data.episodes?.firstWhereOrNull((element) => element.cid == cid);
|
||||
mediaItem = MediaItem(
|
||||
id: UniqueKey().toString(),
|
||||
title: current?.longTitle ?? "",
|
||||
|
||||
@@ -161,10 +161,8 @@ class GStorage {
|
||||
defaultValue: VideoDecodeFormats.values[1].code,
|
||||
);
|
||||
|
||||
static String get hardwareDecoding => setting.get(
|
||||
SettingBoxKey.hardwareDecoding,
|
||||
defaultValue: Platform.isAndroid ? 'auto-safe' : 'auto',
|
||||
);
|
||||
static String get hardwareDecoding =>
|
||||
setting.get(SettingBoxKey.hardwareDecoding, defaultValue: 'auto');
|
||||
|
||||
static String get videoSync => setting.get(
|
||||
SettingBoxKey.videoSync,
|
||||
@@ -363,6 +361,9 @@ class GStorage {
|
||||
static bool get showDmChart =>
|
||||
GStorage.setting.get(SettingBoxKey.showDmChart, defaultValue: false);
|
||||
|
||||
static bool get enableCommAntifraud => GStorage.setting
|
||||
.get(SettingBoxKey.enableCommAntifraud, defaultValue: false);
|
||||
|
||||
static List<double> get dynamicDetailRatio => List<double>.from(setting
|
||||
.get(SettingBoxKey.dynamicDetailRatio, defaultValue: [60.0, 40.0]));
|
||||
|
||||
@@ -593,6 +594,7 @@ class SettingBoxKey {
|
||||
enableLivePhoto = 'enableLivePhoto',
|
||||
showSeekPreview = 'showSeekPreview',
|
||||
showDmChart = 'showDmChart',
|
||||
enableCommAntifraud = 'enableCommAntifraud',
|
||||
|
||||
// Sponsor Block
|
||||
enableSponsorBlock = 'enableSponsorBlock',
|
||||
|
||||
Reference in New Issue
Block a user