Merge branch 'main' into feature-minePage
This commit is contained in:
@ -1,11 +1,9 @@
|
||||
import 'package:cached_network_image/cached_network_image.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_html/flutter_html.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:pilipala/plugin/pl_gallery/hero_dialog_route.dart';
|
||||
import 'package:pilipala/plugin/pl_gallery/interactiveviewer_gallery.dart';
|
||||
import 'package:pilipala/utils/highlight.dart';
|
||||
import 'network_img_layer.dart';
|
||||
|
||||
// ignore: must_be_immutable
|
||||
class HtmlRender extends StatelessWidget {
|
||||
@ -85,11 +83,11 @@ class HtmlRender extends StatelessWidget {
|
||||
},
|
||||
child: Center(
|
||||
child: Hero(
|
||||
tag: imgUrl,
|
||||
tag: imgList?[index] ?? imgUrl,
|
||||
child: CachedNetworkImage(
|
||||
fadeInDuration:
|
||||
const Duration(milliseconds: 0),
|
||||
imageUrl: imgUrl,
|
||||
imageUrl: imgList?[index] ?? imgUrl,
|
||||
fit: BoxFit.contain,
|
||||
),
|
||||
),
|
||||
|
@ -555,6 +555,10 @@ class Api {
|
||||
static const String messageSystemAPi =
|
||||
'${HttpString.messageBaseUrl}/x/sys-msg/query_unified_notify';
|
||||
|
||||
/// 系统通知 个人
|
||||
static const String userMessageSystemAPi =
|
||||
'${HttpString.messageBaseUrl}/x/sys-msg/query_user_notify';
|
||||
|
||||
/// 系统通知标记已读
|
||||
static const String systemMarkRead =
|
||||
'${HttpString.messageBaseUrl}/x/sys-msg/update_cursor';
|
||||
@ -584,4 +588,8 @@ class Api {
|
||||
|
||||
///
|
||||
static const String getViewInfo = '/x/article/viewinfo';
|
||||
|
||||
/// 直播间记录
|
||||
static const String liveRoomEntry =
|
||||
'${HttpString.liveBaseUrl}/xlive/web-room/v1/index/roomEntryAction';
|
||||
}
|
||||
|
@ -142,4 +142,15 @@ class LiveHttp {
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// 直播历史记录
|
||||
static Future liveRoomEntry({required int roomId}) async {
|
||||
await Request().post(Api.liveRoomEntry, queryParameters: {
|
||||
'room_id': roomId,
|
||||
'platform': 'pc',
|
||||
'csrf_token': await Request.getCsrf(),
|
||||
'csrf': await Request.getCsrf(),
|
||||
'visit_id': '',
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -330,4 +330,27 @@ class MsgHttp {
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
static Future messageSystemAccount() async {
|
||||
var res = await Request().get(Api.userMessageSystemAPi, data: {
|
||||
'csrf': await Request.getCsrf(),
|
||||
'page_size': 20,
|
||||
'build': 0,
|
||||
'mobi_app': 'web',
|
||||
});
|
||||
if (res.data['code'] == 0) {
|
||||
try {
|
||||
return {
|
||||
'status': true,
|
||||
'data': res.data['data']['system_notify_list']
|
||||
.map<MessageSystemModel>((e) => MessageSystemModel.fromJson(e))
|
||||
.toList(),
|
||||
};
|
||||
} catch (err) {
|
||||
return {'status': false, 'date': [], 'msg': err.toString()};
|
||||
}
|
||||
} else {
|
||||
return {'status': false, 'date': [], 'msg': res.data['message']};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -5,7 +5,7 @@ class MessageSystemModel {
|
||||
int? cursor;
|
||||
int? type;
|
||||
String? title;
|
||||
Map? content;
|
||||
dynamic content;
|
||||
Source? source;
|
||||
String? timeAt;
|
||||
int? cardType;
|
||||
@ -45,7 +45,9 @@ class MessageSystemModel {
|
||||
cursor: jsons["cursor"],
|
||||
type: jsons["type"],
|
||||
title: jsons["title"],
|
||||
content: json.decode(jsons["content"]),
|
||||
content: isValidJson(jsons["content"])
|
||||
? json.decode(jsons["content"])
|
||||
: jsons["content"],
|
||||
source: Source.fromJson(jsons["source"]),
|
||||
timeAt: jsons["time_at"],
|
||||
cardType: jsons["card_type"],
|
||||
@ -75,3 +77,12 @@ class Source {
|
||||
logo: json["logo"],
|
||||
);
|
||||
}
|
||||
|
||||
bool isValidJson(String str) {
|
||||
try {
|
||||
json.decode(str);
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -39,11 +39,11 @@ class ModelResult {
|
||||
ModelResult.fromJson(Map<String, dynamic> json) {
|
||||
resultType = json['result_type'];
|
||||
summary = json['summary'];
|
||||
outline = json['result_type'] == 2
|
||||
? json['outline']
|
||||
outline = json['result_type'] == 0
|
||||
? <OutlineItem>[]
|
||||
: json['outline']
|
||||
.map<OutlineItem>((e) => OutlineItem.fromJson(e))
|
||||
.toList()
|
||||
: <OutlineItem>[];
|
||||
.toList();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -61,7 +61,7 @@ class _BlackListPageState extends State<BlackListPage> {
|
||||
centerTitle: false,
|
||||
title: Obx(
|
||||
() => Text(
|
||||
'黑名单管理 - ${_blackListController.total.value}',
|
||||
'黑名单管理 ${_blackListController.total.value == 0 ? '' : '- ${_blackListController.total.value}'}',
|
||||
style: Theme.of(context).textTheme.titleMedium,
|
||||
),
|
||||
),
|
||||
@ -76,8 +76,12 @@ class _BlackListPageState extends State<BlackListPage> {
|
||||
if (data['status']) {
|
||||
List<BlackListItem> list = _blackListController.blackList;
|
||||
return Obx(
|
||||
() => list.length == 1
|
||||
? const SizedBox()
|
||||
() => list.isEmpty
|
||||
? CustomScrollView(
|
||||
slivers: [
|
||||
HttpError(errMsg: '你没有拉黑任何人哦~_~', fn: () => {})
|
||||
],
|
||||
)
|
||||
: ListView.builder(
|
||||
controller: scrollController,
|
||||
itemCount: list.length,
|
||||
|
@ -49,7 +49,6 @@ class FansController extends GetxController {
|
||||
} else if (type == 'onLoad') {
|
||||
fansList.addAll(res['data'].list);
|
||||
}
|
||||
print(total);
|
||||
if ((pn == 1 && total < ps) || res['data'].list.isEmpty) {
|
||||
loadingText.value = '没有更多了';
|
||||
}
|
||||
|
@ -103,9 +103,17 @@ class _FansPageState extends State<FansPage> {
|
||||
),
|
||||
);
|
||||
} else {
|
||||
return HttpError(
|
||||
return CustomScrollView(
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
slivers: [
|
||||
HttpError(
|
||||
errMsg: data['msg'],
|
||||
fn: () => _fansController.queryFans('init'),
|
||||
fn: () {
|
||||
_futureBuilderFuture =
|
||||
_fansController.queryFans('init');
|
||||
},
|
||||
)
|
||||
],
|
||||
);
|
||||
}
|
||||
} else {
|
||||
|
@ -97,6 +97,7 @@ class LiveRoomController extends GetxController {
|
||||
autoplay: true,
|
||||
);
|
||||
plPlayerController.isOpenDanmu.value = danmakuSwitch.value;
|
||||
heartBeat();
|
||||
}
|
||||
|
||||
Future queryLiveInfo() async {
|
||||
@ -281,8 +282,20 @@ class LiveRoomController extends GetxController {
|
||||
}
|
||||
}
|
||||
|
||||
// 历史记录
|
||||
void heartBeat() {
|
||||
LiveHttp.liveRoomEntry(roomId: roomId);
|
||||
}
|
||||
|
||||
String encodeToBase64(String input) {
|
||||
List<int> bytes = utf8.encode(input);
|
||||
String base64Str = base64.encode(bytes);
|
||||
return base64Str;
|
||||
}
|
||||
|
||||
@override
|
||||
void onClose() {
|
||||
heartBeat();
|
||||
plSocket?.onClose();
|
||||
super.onClose();
|
||||
}
|
||||
|
@ -1,3 +1,4 @@
|
||||
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:pilipala/http/msg.dart';
|
||||
import 'package:pilipala/models/msg/system.dart';
|
||||
@ -5,18 +6,44 @@ import 'package:pilipala/models/msg/system.dart';
|
||||
class MessageSystemController extends GetxController {
|
||||
RxList<MessageSystemModel> systemItems = <MessageSystemModel>[].obs;
|
||||
|
||||
Future queryMessageSystem({String type = 'init'}) async {
|
||||
var res = await MsgHttp.messageSystem();
|
||||
if (res['status']) {
|
||||
if (type == 'init') {
|
||||
systemItems.value = res['data'];
|
||||
} else {
|
||||
systemItems.addAll(res['data']);
|
||||
}
|
||||
Future<void> queryAndProcessMessages({String type = 'init'}) async {
|
||||
// 并行调用两个接口
|
||||
var results = await Future.wait([
|
||||
queryMessageSystem(type: type),
|
||||
queryMessageSystemAccount(type: type),
|
||||
]);
|
||||
|
||||
// 对返回的数据进行处理
|
||||
var systemRes = results[0];
|
||||
var accountRes = results[1];
|
||||
|
||||
if (systemRes['status'] || accountRes['status']) {
|
||||
// 处理返回的数据
|
||||
List<MessageSystemModel> combinedData = [
|
||||
...systemRes['data'],
|
||||
...accountRes['data']
|
||||
];
|
||||
combinedData.sort((a, b) => b.cursor!.compareTo(a.cursor!));
|
||||
systemItems.addAll(combinedData);
|
||||
systemItems.refresh();
|
||||
if (systemItems.isNotEmpty) {
|
||||
systemMarkRead(systemItems.first.cursor!);
|
||||
}
|
||||
} else {
|
||||
SmartDialog.showToast(systemRes['msg'] ?? accountRes['msg']);
|
||||
}
|
||||
return systemRes;
|
||||
}
|
||||
|
||||
// 获取系统消息
|
||||
Future queryMessageSystem({String type = 'init'}) async {
|
||||
var res = await MsgHttp.messageSystem();
|
||||
return res;
|
||||
}
|
||||
|
||||
// 获取系统消息 个人
|
||||
Future queryMessageSystemAccount({String type = 'init'}) async {
|
||||
var res = await MsgHttp.messageSystemAccount();
|
||||
return res;
|
||||
}
|
||||
|
||||
|
@ -20,7 +20,7 @@ class _MessageSystemPageState extends State<MessageSystemPage> {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_futureBuilderFuture = _messageSystemCtr.queryMessageSystem();
|
||||
_futureBuilderFuture = _messageSystemCtr.queryAndProcessMessages();
|
||||
}
|
||||
|
||||
@override
|
||||
@ -31,7 +31,7 @@ class _MessageSystemPageState extends State<MessageSystemPage> {
|
||||
),
|
||||
body: RefreshIndicator(
|
||||
onRefresh: () async {
|
||||
await _messageSystemCtr.queryMessageSystem();
|
||||
await _messageSystemCtr.queryAndProcessMessages();
|
||||
},
|
||||
child: FutureBuilder(
|
||||
future: _futureBuilderFuture,
|
||||
@ -42,7 +42,6 @@ class _MessageSystemPageState extends State<MessageSystemPage> {
|
||||
}
|
||||
if (snapshot.data['status']) {
|
||||
final systemItems = _messageSystemCtr.systemItems;
|
||||
print(systemItems.length);
|
||||
return Obx(
|
||||
() => ListView.separated(
|
||||
controller: scrollController,
|
||||
@ -115,7 +114,7 @@ class SystemItem extends StatelessWidget {
|
||||
style: TextStyle(color: Theme.of(context).colorScheme.outline),
|
||||
),
|
||||
const SizedBox(height: 6),
|
||||
Text(item.content!['web']),
|
||||
Text(item.content is String ? item.content : item.content!['web']),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
@ -103,7 +103,7 @@ class _StyleSettingState extends State<StyleSetting> {
|
||||
needReboot: true,
|
||||
),
|
||||
const SetSwitchItem(
|
||||
title: '首页底栏背景渐变',
|
||||
title: '首页顶部背景渐变',
|
||||
setKey: SettingBoxKey.enableGradientBg,
|
||||
defaultVal: true,
|
||||
needReboot: true,
|
||||
|
@ -115,7 +115,7 @@ class VideoDetailController extends GetxController
|
||||
].obs;
|
||||
RxDouble sheetHeight = 0.0.obs;
|
||||
RxString archiveSourceType = 'dash'.obs;
|
||||
ScrollController? replyScrillController;
|
||||
ScrollController? replyScrollController;
|
||||
List<MediaVideoItemModel> mediaList = <MediaVideoItemModel>[];
|
||||
RxBool isWatchLaterVisible = false.obs;
|
||||
RxString watchLaterTitle = ''.obs;
|
||||
@ -574,12 +574,12 @@ class VideoDetailController extends GetxController
|
||||
}
|
||||
|
||||
void onControllerCreated(ScrollController controller) {
|
||||
replyScrillController = controller;
|
||||
replyScrollController = controller;
|
||||
}
|
||||
|
||||
void onTapTabbar(int index) {
|
||||
if (index == 1 && tabCtr.index == 1) {
|
||||
replyScrillController?.animateTo(0,
|
||||
if (tabCtr.animation!.isCompleted && index == 1 && tabCtr.index == 1) {
|
||||
replyScrollController?.animateTo(0,
|
||||
duration: const Duration(milliseconds: 300), curve: Curves.ease);
|
||||
}
|
||||
}
|
||||
|
@ -541,7 +541,7 @@ class VideoIntroController extends GetxController {
|
||||
|
||||
// ai总结
|
||||
Future aiConclusion() async {
|
||||
SmartDialog.showLoading(msg: '正在生产ai总结');
|
||||
SmartDialog.showLoading(msg: '正在生成ai总结');
|
||||
final res = await VideoHttp.aiConclusion(
|
||||
bvid: bvid,
|
||||
cid: lastPlayCid.value,
|
||||
@ -551,7 +551,7 @@ class VideoIntroController extends GetxController {
|
||||
if (res['status']) {
|
||||
modelResult = res['data'].modelResult;
|
||||
} else {
|
||||
SmartDialog.showToast("当前视频可能暂不支持AI视频总结");
|
||||
SmartDialog.showToast("当前视频暂不支持AI视频总结");
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
@ -322,7 +322,7 @@ class _VideoInfoState extends State<VideoInfo> with TickerProviderStateMixin {
|
||||
expanded: Text(
|
||||
widget.videoDetail!.title!,
|
||||
softWrap: true,
|
||||
maxLines: 4,
|
||||
maxLines: 10,
|
||||
style: const TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.bold,
|
||||
|
@ -49,6 +49,8 @@ class AiDetail extends StatelessWidget {
|
||||
child: SingleChildScrollView(
|
||||
child: Column(
|
||||
children: [
|
||||
if (modelResult!.resultType != 0 &&
|
||||
modelResult!.summary != '') ...[
|
||||
SelectableText(
|
||||
modelResult!.summary!,
|
||||
style: const TextStyle(
|
||||
@ -58,6 +60,7 @@ class AiDetail extends StatelessWidget {
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
],
|
||||
ListView.builder(
|
||||
shrinkWrap: true,
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
|
@ -94,6 +94,6 @@ class WebviewController extends GetxController {
|
||||
},
|
||||
),
|
||||
)
|
||||
..loadRequest(Uri.parse(url));
|
||||
..loadRequest(Uri.parse(url.startsWith('http') ? url : 'https://$url'));
|
||||
}
|
||||
}
|
||||
|
@ -217,6 +217,7 @@ class SessionItem extends StatelessWidget {
|
||||
final String heroTag = Utils.makeHeroTag(sessionItem.accountInfo?.mid ?? 0);
|
||||
final content = sessionItem.lastMsg.content;
|
||||
final msgStatus = sessionItem.lastMsg.msgStatus;
|
||||
final int msgType = sessionItem.lastMsg.msgType;
|
||||
|
||||
return ListTile(
|
||||
onTap: () {
|
||||
@ -251,6 +252,8 @@ class SessionItem extends StatelessWidget {
|
||||
subtitle: Text(
|
||||
msgStatus == 1
|
||||
? '你撤回了一条消息'
|
||||
: msgType == 2
|
||||
? '[图片]'
|
||||
: content != null && content != ''
|
||||
? (content['text'] ??
|
||||
content['content'] ??
|
||||
|
@ -22,6 +22,7 @@ class WhisperDetailController extends GetxController {
|
||||
final TextEditingController replyContentController = TextEditingController();
|
||||
Box userInfoCache = GStrorage.userInfo;
|
||||
List emoteList = [];
|
||||
List<String> picList = [];
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
@ -41,6 +42,18 @@ class WhisperDetailController extends GetxController {
|
||||
var res = await MsgHttp.sessionMsg(talkerId: talkerId);
|
||||
if (res['status']) {
|
||||
messageList.value = res['data'].messages;
|
||||
// 找出图片
|
||||
try {
|
||||
for (var item in messageList) {
|
||||
if (item.msgType == 2) {
|
||||
picList.add(item.content['url']);
|
||||
}
|
||||
}
|
||||
picList = picList.reversed.toList();
|
||||
} catch (e) {
|
||||
print('e: $e');
|
||||
}
|
||||
|
||||
if (messageList.isNotEmpty) {
|
||||
ackSessionMsg();
|
||||
if (res['data'].eInfos != null) {
|
||||
|
@ -193,27 +193,21 @@ class _WhisperDetailPageState extends State<WhisperDetailPage>
|
||||
? const SizedBox()
|
||||
: Align(
|
||||
alignment: Alignment.topCenter,
|
||||
child: ListView.builder(
|
||||
child: ListView.separated(
|
||||
itemCount: messageList.length,
|
||||
shrinkWrap: true,
|
||||
reverse: true,
|
||||
itemBuilder: (_, int i) {
|
||||
if (i == 0) {
|
||||
return Column(
|
||||
children: [
|
||||
ChatItem(
|
||||
item: messageList[i],
|
||||
e_infos: _whisperDetailController
|
||||
.eInfos),
|
||||
const SizedBox(height: 20),
|
||||
],
|
||||
);
|
||||
} else {
|
||||
return ChatItem(
|
||||
item: messageList[i],
|
||||
e_infos:
|
||||
_whisperDetailController.eInfos);
|
||||
}
|
||||
e_infos: _whisperDetailController.eInfos,
|
||||
ctr: _whisperDetailController,
|
||||
);
|
||||
},
|
||||
separatorBuilder: (_, int i) {
|
||||
return i == 0
|
||||
? const SizedBox(height: 20)
|
||||
: const SizedBox.shrink();
|
||||
},
|
||||
),
|
||||
),
|
||||
|
@ -2,14 +2,18 @@
|
||||
// ignore_for_file: constant_identifier_names
|
||||
|
||||
import 'dart:convert';
|
||||
import 'package:cached_network_image/cached_network_image.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:pilipala/common/widgets/network_img_layer.dart';
|
||||
import 'package:pilipala/plugin/pl_gallery/hero_dialog_route.dart';
|
||||
import 'package:pilipala/plugin/pl_gallery/interactiveviewer_gallery.dart';
|
||||
import 'package:pilipala/utils/route_push.dart';
|
||||
import 'package:pilipala/utils/utils.dart';
|
||||
import 'package:pilipala/utils/storage.dart';
|
||||
import '../../../http/search.dart';
|
||||
import '../controller.dart';
|
||||
|
||||
enum MsgType {
|
||||
invalid(value: 0, label: "空空的~"),
|
||||
@ -42,10 +46,12 @@ enum MsgType {
|
||||
class ChatItem extends StatelessWidget {
|
||||
dynamic item;
|
||||
List? e_infos;
|
||||
WhisperDetailController ctr;
|
||||
|
||||
ChatItem({
|
||||
super.key,
|
||||
this.item,
|
||||
required this.item,
|
||||
required this.ctr,
|
||||
this.e_infos,
|
||||
});
|
||||
|
||||
@ -157,10 +163,48 @@ class ChatItem extends StatelessWidget {
|
||||
case MsgType.text:
|
||||
return richTextMessage(context);
|
||||
case MsgType.pic:
|
||||
return NetworkImgLayer(
|
||||
return InkWell(
|
||||
onTap: () {
|
||||
Navigator.of(context).push(
|
||||
HeroDialogRoute<void>(
|
||||
builder: (BuildContext context) => InteractiveviewerGallery(
|
||||
sources: ctr.picList,
|
||||
initIndex: ctr.picList.indexOf(content['url']),
|
||||
itemBuilder: (
|
||||
BuildContext context,
|
||||
int index,
|
||||
bool isFocus,
|
||||
bool enablePageView,
|
||||
) {
|
||||
return GestureDetector(
|
||||
behavior: HitTestBehavior.opaque,
|
||||
onTap: () {
|
||||
if (enablePageView) {
|
||||
Navigator.of(context).pop();
|
||||
}
|
||||
},
|
||||
child: Center(
|
||||
child: Hero(
|
||||
tag: ctr.picList[index],
|
||||
child: CachedNetworkImage(
|
||||
fadeInDuration: const Duration(milliseconds: 0),
|
||||
imageUrl: ctr.picList[index],
|
||||
fit: BoxFit.contain,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
onPageChanged: (int pageIndex) {},
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
child: NetworkImgLayer(
|
||||
width: 220,
|
||||
height: 220 * content['height'] / content['width'],
|
||||
src: content['url'],
|
||||
),
|
||||
);
|
||||
case MsgType.share_v2:
|
||||
return Column(
|
||||
|
Reference in New Issue
Block a user